From fb3b99ab8231c2f37fba427b3343bba48b97bae4 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Tue, 16 Jan 2018 13:42:15 +0100 Subject: Initial scaffolding --- Makefile | 20 ++++++++++++ README.md | 26 +++++++++++++++ kernel.c | 7 ++++ main.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ rdrand.c | 12 +++++++ rdseed.c | 12 +++++++ run.sh | 11 +++++++ 7 files changed, 196 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 kernel.c create mode 100644 main.c create mode 100644 rdrand.c create mode 100644 rdseed.c create mode 100755 run.sh diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3677112 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +ifneq ($(KERNELRELEASE),) +kbench9000-y := $(sort $(patsubst %.c,%.o,$(filter-out %.mod.c,$(subst $(M)/,,$(wildcard $(M)/*.c))))) +obj-m := kbench9000.o +ccflags-y += -O3 +ccflags-y += -D'pr_fmt(fmt)=KBUILD_MODNAME ": " fmt' +else +KERNELDIR ?= /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +default: build + +run: build + sudo ./run.sh +build: + $(MAKE) -C $(KERNELDIR) M=$(PWD) +clean: + $(MAKE) -C $(KERNELDIR) M=$(PWD) clean +.PHONY: default run build clean +endif + diff --git a/README.md b/README.md new file mode 100644 index 0000000..3fd3180 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# kBench9000 – simple kernel land cycle counter +### by [Jason A. Donenfeld](mailto:jason@zx2c4.com) + +This is a very simple kernel land cycle counter. To use, simply edit `function.h`, +add any other `.c` files and mention them in the `kbench9000-y +=` line of the +`Makefile`, and then type: + +``` +$ make run +``` + +![Expected kBench9000 output](https://data.zx2c4.com/kbench9000-screenshot.png) + +### Kernel Toolchain + +You'll need to have a working kernel toolchain, usually achievable by: + +``` +$ sudo apt install linux-headers-$(uname -r) build-essential +``` + +or + +``` +$ sudo dnf install kernel-devel @development-tools +``` diff --git a/kernel.c b/kernel.c new file mode 100644 index 0000000..8dbaf0d --- /dev/null +++ b/kernel.c @@ -0,0 +1,7 @@ +#include + +bool generate_1k_kernel(u8 out[1024]) +{ + get_random_bytes(out, 1024); + return true; +} diff --git a/main.c b/main.c new file mode 100644 index 0000000..a18d744 --- /dev/null +++ b/main.c @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018-2022 Jason A. Donenfeld . All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned long stamp = 0; +module_param(stamp, ulong, 0); + +#define declare_it(name) bool generate_1k_ ##name(u8 out[1024]) + +#define local_it(name) cycles_t median_ ##name = 0 + +#define do_it(name) do { \ + u32 eax = 0, ebx = 0, ecx = 0, edx = 0; \ + for (i = 0; i < TRIALS; ++i) \ + asm volatile("cpuid" : "+a" (eax), "=b" (ebx), "=d" (edx), "+c" (ecx)); \ + for (i = 0; i < TRIALS; ++i) \ + ret |= generate_1k_ ##name(out); \ + for (i = 0; i <= TRIALS; ++i) { \ + trial_times[i] = native_read_pmc(pmc_index); \ + ret |= generate_1k_ ##name(out); \ + } \ + for (i = 0; i < TRIALS; ++i) \ + trial_times[i] = trial_times[i + 1] - trial_times[i]; \ + sort(trial_times, TRIALS + 1, sizeof(cycles_t), compare_cycles, NULL); \ + median_ ## name = trial_times[TRIALS / 2]; \ +} while (0) + +#define report_it(name) do { \ + pr_err("%lu: %12s: %6llu cycles per call\n", stamp, #name, median_ ## name); \ +} while (0) + +#define forall(f) \ + f(rdrand); \ + f(rdseed); \ + f(kernel); + +forall(declare_it) + +static int compare_cycles(const void *a, const void *b) +{ + return *((cycles_t *)a) - *((cycles_t *)b); +} + +static int __init mod_init(void) +{ + enum { TRIALS = 30000 }; + int ret = 0, i; + cycles_t *trial_times; + forall(local_it) + unsigned long flags; + struct perf_event *cycles_event; + int pmc_index; + static struct perf_event_attr perf_cycles_attr = { + .type = PERF_TYPE_HARDWARE, + .config = PERF_COUNT_HW_CPU_CYCLES, + .size = sizeof(struct perf_event_attr), + .pinned = 1, + .disabled = 0, + .exclude_user = 1 + }; + DEFINE_SPINLOCK(lock); + u8 out[1024]; + + trial_times = kcalloc(TRIALS + 1, sizeof(cycles_t), GFP_KERNEL); + if (!trial_times) + return -ENOMEM; + + msleep(1000); + spin_lock_irqsave(&lock, flags); + cycles_event = perf_event_create_kernel_counter(&perf_cycles_attr, raw_smp_processor_id(), NULL, NULL, NULL); + if (IS_ERR(cycles_event)) { + pr_err("unable to create perf counter: %ld\n", PTR_ERR(cycles_event)); + goto skip; + } + pmc_index = cycles_event->hw.event_base_rdpmc; + + forall(do_it) + + perf_event_release_kernel(cycles_event); +skip: + spin_unlock_irqrestore(&lock, flags); + forall(report_it) + kfree(trial_times); + + /* We should never actually agree to insert the module. Choosing + * -0x1000 here is an amazing hack. It causes the kernel to not + * actually load the module, while the standard userspace tools + * don't return an error, because it's too big. */ + return -0x1000; +} + +module_init(mod_init); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("kBench9000 Cycle Counter"); +MODULE_AUTHOR("Jason A. Donenfeld "); diff --git a/rdrand.c b/rdrand.c new file mode 100644 index 0000000..c38484f --- /dev/null +++ b/rdrand.c @@ -0,0 +1,12 @@ +#include + +bool generate_1k_rdrand(u8 out[1024]) +{ + unsigned int i; + unsigned long *lout = (unsigned long *)out; + bool ret = true; + + for (i = 0; i < 1024 / sizeof(long); ++i) + ret &= arch_get_random_long(lout++); + return ret; +} diff --git a/rdseed.c b/rdseed.c new file mode 100644 index 0000000..6cd9cb6 --- /dev/null +++ b/rdseed.c @@ -0,0 +1,12 @@ +#include + +bool generate_1k_rdseed(u8 out[1024]) +{ + unsigned int i; + unsigned long *lout = (unsigned long *)out; + bool ret = true; + + for (i = 0; i < 1024 / sizeof(long); ++i) + ret &= arch_get_random_seed_long(lout++); + return ret; +} diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..7e94e4c --- /dev/null +++ b/run.sh @@ -0,0 +1,11 @@ +#!/bin/bash +set -e + +[[ -e kbench9000.ko ]] + +echo "[+] Inserting module to run tests" +stamp="$(date +%s)" +insmod kbench9000.ko stamp="$stamp" + +echo "[+] Gathering results" +dmesg | sed -n "s/.*kbench9000: $stamp: \\(.*\\)/\\x1b[37m\\x1b[44m\\x1b[1m\\1\\x1b[0m/p" -- cgit v1.2.3-59-g8ed1b