diff --git a/Makefile b/Makefile index 65211622..5f30dcfd 100644 --- a/Makefile +++ b/Makefile @@ -71,9 +71,6 @@ COMMON_INCLUDES += -I$(PFMLIB_INCLUDE) endif COMMON_FLAGS := $(COMMON_INCLUDES) -pipe -MP -MMD -m64 -D__x86_64__ -ifneq ($(UNITTEST),) -COMMON_FLAGS += -DKTF_UNIT_TEST -endif ifeq ($(CONFIG_LIBPFM),y) COMMON_FLAGS += -DKTF_PMU diff --git a/README.md b/README.md index dfb32778..33affe77 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,12 @@ vcpus=1 You need to generate a bootable ISO for this. +### Adding new tests + +New tests can be added by adding a new function in a file in the `tests` folder. Each test signature must +be the same as `test_fn` provided in `test.h`. Tests can be enabled in `grub.cfg` by adding the option with key `tests` and values +the comma-separated list of function names, such as `tests=test1,test2,unit_tests`. + ## Style The style for this project is defined in `.clang-format` file in the main directory of this repository. diff --git a/grub/boot/grub/grub-test.cfg b/grub/boot/grub/grub-test.cfg index 703120b4..2d774b13 100644 --- a/grub/boot/grub/grub-test.cfg +++ b/grub/boot/grub/grub-test.cfg @@ -5,6 +5,6 @@ terminal_input --append serial terminal_output --append serial menuentry "kernel64" { - multiboot /boot/kernel64.bin integer=42 boolean=1 string=foo badstring=toolong booleantwo + multiboot /boot/kernel64.bin integer=42 boolean=1 string=foo badstring=toolong booleantwo tests=unit_tests boot } diff --git a/include/test.h b/include/test.h new file mode 100644 index 00000000..5af7f101 --- /dev/null +++ b/include/test.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef KTF_TEST_H +#define KTF_TEST_H + +#define MAX_OPT_TESTS_LEN 128 + +typedef int(test_fn)(void *arg); + +#endif /* KTF_TEST_H */ diff --git a/tests/test.c b/tests/test.c index c6b24d91..0e969242 100644 --- a/tests/test.c +++ b/tests/test.c @@ -24,134 +24,58 @@ */ #include #include -#include #include - -#ifdef KTF_UNIT_TEST -#include -#include #include +#include +#include -extern char *kernel_cmdline; - -static char opt_string[4]; -string_cmd("string", opt_string); - -static char opt_badstring[5]; -string_cmd("badstring", opt_badstring); - -static unsigned long opt_ulong; -ulong_cmd("integer", opt_ulong); - -static bool opt_bool = 0; -bool_cmd("boolean", opt_bool); - -static bool opt_booltwo = 0; -bool_cmd("booleantwo", opt_booltwo); - -static char memmove_string[4]; -static char range_string[] = "123456"; -static char *src, *dst; +static const char opt_test_delims[] = ","; +#include -static void cpu_freq_expect(const char *cpu_str, uint64_t expectation) { - uint64_t result = get_cpu_freq(cpu_str); - if (result != expectation) { - printk("Could not parse cpu frequency from string '%s'\n", cpu_str); - printk("Expectation vs. result: %llu vs. %llu\n", expectation, result); - BUG(); +enum get_next_test_result { + TESTS_DONE, + TESTS_FOUND, + TESTS_ERROR, +}; +typedef enum get_next_test_result get_next_test_result_t; + +static char opt_tests[MAX_OPT_TESTS_LEN]; +string_cmd("tests", opt_tests); + +static get_next_test_result_t get_next_test(test_fn **out_test_fn, char **out_name) { + static char *opt = opt_tests; + + *out_name = strtok(opt, opt_test_delims); + + opt = NULL; + if (*out_name) { + *out_test_fn = symbol_address(*out_name); + if (!*out_test_fn) { + printk("Symbol for test %s not found\n", *out_name); + return TESTS_ERROR; + } + return TESTS_FOUND; } - - printk("Got CPU string '%s' and frequency '%llu'\n", cpu_str, result); - return; + return TESTS_DONE; } -static int __user_text func(void *arg) { return 0; } - -static int ktf_unit_tests(void) { - usermode_call(func, NULL); - - printk("\nLet the UNITTESTs begin\n"); - printk("Commandline parsing: %s\n", kernel_cmdline); - - if (strcmp(opt_string, "foo")) { - printk("String parameter opt_string != foo: %s\n", opt_string); - BUG(); - } - else { - printk("String parameter parsing works!\n"); - } - - if (strcmp(opt_badstring, "tool")) { - printk("String parameter opt_badstring != tool: %s\n", opt_badstring); - BUG(); - } - else { - printk("String parameter parsing works!\n"); - } - - if (opt_ulong != 42) { - printk("Integer parameter opt_ulong != 42: %d\n", opt_ulong); - BUG(); - } - else { - printk("Integer parameter parsing works!\n"); - } +void test_main(void) { + char *name; + test_fn *fn = NULL; + unsigned n = 0; - if (!opt_bool || !opt_booltwo) { - printk("Boolean parameter opt_bool != true: %d\n", opt_bool); - printk("Boolean parameter opt_booltwo != true: %d\n", opt_booltwo); - BUG(); - } - else { - printk("Boolean parameter parsing works!\n"); - } + printk("\nRunning tests\n"); - printk("\nMemmove testing:\n"); - (void) memmove(memmove_string, opt_string, sizeof(opt_string)); - if (!strcmp(memmove_string, opt_string)) { - printk("Moving around memory works!\n"); - } - else { - printk("Memmove'ing did not work: %s (%p) != %s (%p)\n", memmove_string, - memmove_string, opt_string, opt_string); - } + while (get_next_test(&fn, &name) == TESTS_FOUND) { + int rc; - src = (char *) range_string; - dst = (char *) range_string + 2; - (void) memmove(dst, src, 4); - if (!strcmp(range_string, "121234")) { - printk("Moving around memory with overlaping ranges works!\n"); + printk("Running test: %s\n", name); + rc = fn(NULL); + printk("Test %s returned: 0x%x\n", name, rc); + n++; } - else { - printk("Overlaping memmove'ing did not work: %s != %s\n", range_string, "121234"); - } - - cpu_freq_expect("Intel(R) Xeon(R) CPU E3-1270 V2 @ 3.50GHz", 3500000000); - cpu_freq_expect("Intel(R) Celeron(R) CPU J1900 @ 1.99GHz", 1990000000); - cpu_freq_expect("AMD G-T48L Processor", 0); - cpu_freq_expect("Intel(R) Atom(TM) CPU E3815 @ 1.46GHz", 1460000000); - cpu_freq_expect("Intel(R) Atom(TM) CPU E620 @ 600MHz", 600000000); - cpu_freq_expect("VIA C7 Processor 1000MHz", 1000000000); - cpu_freq_expect("Intel(R) Core(TM) i7 CPU 950 @ 3.07GHz", 3070000000); - cpu_freq_expect("AMD Ryzen Threadripper 1950X 16-Core Processor", 0); - cpu_freq_expect("Prototyp Amazing Foo One @ 1GHz", 1000000000); - cpu_freq_expect("Prototyp Amazing Foo Two @ 1.00GHz", 1000000000); - - printk("Long mode to real mode transition:\n"); - long_to_real(); - - return 0; -} -#else -static int ktf_unit_tests(void) { return 0; } -#endif - -void test_main(void) { - printk("\nTest:\n"); - - ktf_unit_tests(); wait_for_all_tasks(); - printk("Test done\n"); + printk("Tests completed: %u\n", n); } diff --git a/tests/unittests.c b/tests/unittests.c new file mode 100644 index 00000000..cc4a3c1a --- /dev/null +++ b/tests/unittests.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2021 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +extern char *kernel_cmdline; +#include + +static char opt_string[4]; +string_cmd("string", opt_string); + +static char opt_badstring[5]; +string_cmd("badstring", opt_badstring); + +static unsigned long opt_ulong; +ulong_cmd("integer", opt_ulong); + +static bool opt_bool = 0; +bool_cmd("boolean", opt_bool); + +static bool opt_booltwo = 0; +bool_cmd("booleantwo", opt_booltwo); + +static char memmove_string[4]; +static char range_string[] = "123456"; +static char *src, *dst; + +static void cpu_freq_expect(const char *cpu_str, uint64_t expectation) { + uint64_t result = get_cpu_freq(cpu_str); + if (result != expectation) { + printk("Could not parse cpu frequency from string '%s'\n", cpu_str); + printk("Expectation vs. result: %llu vs. %llu\n", expectation, result); + BUG(); + } + + printk("Got CPU string '%s' and frequency '%llu'\n", cpu_str, result); + return; +} + +static int __user_text func(void *arg) { return 0; } + +int unit_tests(void *_unused) { + usermode_call(func, NULL); + + printk("\nLet the UNITTESTs begin\n"); + printk("Commandline parsing: %s\n", kernel_cmdline); + + if (strcmp(opt_string, "foo")) { + printk("String parameter opt_string != foo: %s\n", opt_string); + BUG(); + } + else { + printk("String parameter parsing works!\n"); + } + + if (strcmp(opt_badstring, "tool")) { + printk("String parameter opt_badstring != tool: %s\n", opt_badstring); + BUG(); + } + else { + printk("String parameter parsing works!\n"); + } + + if (opt_ulong != 42) { + printk("Integer parameter opt_ulong != 42: %d\n", opt_ulong); + BUG(); + } + else { + printk("Integer parameter parsing works!\n"); + } + + if (!opt_bool || !opt_booltwo) { + printk("Boolean parameter opt_bool != true: %d\n", opt_bool); + printk("Boolean parameter opt_booltwo != true: %d\n", opt_booltwo); + BUG(); + } + else { + printk("Boolean parameter parsing works!\n"); + } + + printk("\nMemmove testing:\n"); + (void) memmove(memmove_string, opt_string, sizeof(opt_string)); + if (!strcmp(memmove_string, opt_string)) { + printk("Moving around memory works!\n"); + } + else { + printk("Memmove'ing did not work: %s (%p) != %s (%p)\n", memmove_string, + memmove_string, opt_string, opt_string); + } + + src = (char *) range_string; + dst = (char *) range_string + 2; + (void) memmove(dst, src, 4); + if (!strcmp(range_string, "121234")) { + printk("Moving around memory with overlaping ranges works!\n"); + } + else { + printk("Overlaping memmove'ing did not work: %s != %s\n", range_string, "121234"); + } + + cpu_freq_expect("Intel(R) Xeon(R) CPU E3-1270 V2 @ 3.50GHz", 3500000000); + cpu_freq_expect("Intel(R) Celeron(R) CPU J1900 @ 1.99GHz", 1990000000); + cpu_freq_expect("AMD G-T48L Processor", 0); + cpu_freq_expect("Intel(R) Atom(TM) CPU E3815 @ 1.46GHz", 1460000000); + cpu_freq_expect("Intel(R) Atom(TM) CPU E620 @ 600MHz", 600000000); + cpu_freq_expect("VIA C7 Processor 1000MHz", 1000000000); + cpu_freq_expect("Intel(R) Core(TM) i7 CPU 950 @ 3.07GHz", 3070000000); + cpu_freq_expect("AMD Ryzen Threadripper 1950X 16-Core Processor", 0); + cpu_freq_expect("Prototyp Amazing Foo One @ 1GHz", 1000000000); + cpu_freq_expect("Prototyp Amazing Foo Two @ 1.00GHz", 1000000000); + + printk("Long mode to real mode transition:\n"); + long_to_real(); + + return 0; +}