From 08fafac4c9f289a9d9a22d838921e4b3eb22c664 Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Fri, 24 Jan 2025 11:01:42 +0000 Subject: [PATCH 1/7] kunit: tool: Use qboot on QEMU x86_64 As noted in [0], SeaBIOS (QEMU default) makes a mess of the terminal, qboot does not. It turns out this is actually useful with kunit.py, since the user is exposed to this issue if they set --raw_output=all. qboot is also faster than SeaBIOS, but it's is marginal for this usecase. [0] https://lore.kernel.org/all/CA+i-1C0wYb-gZ8Mwh3WSVpbk-LF-Uo+njVbASJPe1WXDURoV7A@mail.gmail.com/ Both SeaBIOS and qboot are x86-specific. Link: https://lore.kernel.org/r/20250124-kunit-qboot-v1-1-815e4d4c6f7c@google.com Signed-off-by: Brendan Jackman Reviewed-by: David Gow Signed-off-by: Shuah Khan --- tools/testing/kunit/qemu_configs/x86_64.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/testing/kunit/qemu_configs/x86_64.py b/tools/testing/kunit/qemu_configs/x86_64.py index dc79490768630..4a6bf4e048f5b 100644 --- a/tools/testing/kunit/qemu_configs/x86_64.py +++ b/tools/testing/kunit/qemu_configs/x86_64.py @@ -7,4 +7,6 @@ qemu_arch='x86_64', kernel_path='arch/x86/boot/bzImage', kernel_command_line='console=ttyS0', - extra_qemu_params=[]) + # qboot is faster than SeaBIOS and doesn't mess up + # the terminal. + extra_qemu_params=['-bios', 'qboot.rom']) From e275f44e0a187b9d76830847976072f1c17b4b7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Fri, 14 Feb 2025 14:27:05 +0100 Subject: [PATCH 2/7] kunit: qemu_configs: sparc: use Zilog console MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver for the 8250 console is not used, as no port is found. Instead the prom0 bootconsole is used the whole time. The prom driver translates '\n' to '\r\n' before handing of the message off to the firmware. The firmware performs the same translation again. In the final output produced by QEMU each line ends with '\r\r\n'. This breaks the kunit parser, which can only handle '\r\n' and '\n'. Use the Zilog console instead. It works correctly, is the one documented by the QEMU manual and also saves a bit of codesize: Before=4051011, After=4023326, chg -0.68% Observed on QEMU 9.2.0. Link: https://lore.kernel.org/r/20250214-kunit-qemu-sparc-console-v1-1-ba1dfdf8f0b1@linutronix.de Fixes: 87c9c1631788 ("kunit: tool: add support for QEMU") Signed-off-by: Thomas Weißschuh Reviewed-by: David Gow Signed-off-by: Shuah Khan --- tools/testing/kunit/qemu_configs/sparc.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/testing/kunit/qemu_configs/sparc.py b/tools/testing/kunit/qemu_configs/sparc.py index e975c4331a7c2..256d9573b4464 100644 --- a/tools/testing/kunit/qemu_configs/sparc.py +++ b/tools/testing/kunit/qemu_configs/sparc.py @@ -2,8 +2,9 @@ QEMU_ARCH = QemuArchParams(linux_arch='sparc', kconfig=''' -CONFIG_SERIAL_8250=y -CONFIG_SERIAL_8250_CONSOLE=y''', +CONFIG_SERIAL_SUNZILOG=y +CONFIG_SERIAL_SUNZILOG_CONSOLE=y +''', qemu_arch='sparc', kernel_path='arch/sparc/boot/zImage', kernel_command_line='console=ttyS0 mem=256M', From 43ebec94e1e8f6187c67bbe395b304a03ee63de5 Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Tue, 21 Jan 2025 13:01:40 +0000 Subject: [PATCH 3/7] kunit: tool: Build GDB scripts Following a similar rationale as commit e4835f1da425f ("kunit: tool: Build compile_commands.json"), make a common developer tool available by default for KUnit users. Compared to compile_commands.json, there is a little more work to be done to build the GDB scripts. Is it enough to affect development cycle duration? Unscientific evaluation: rm -rf .kunit; time tools/testing/kunit/kunit.py build --kunitconfig ./lib/kunit/.kunitconfig --jobs 96 Without this patch it took 14.77s, with this patch it took 14.83. So, although `make scripts_gdb` is pretty slow, presumably most of that is just the overhead of running Kbuild at all, actually building the scripts is approximately free. Note also, to actually get the GDB scripts the user needs to enable CONFIG_SCRIPTS_GDB, but building the scripts_gdb target without that is still harmless. Link: https://lore.kernel.org/r/20250121-kunit-gdb-v1-1-faedfd0653ef@google.com Signed-off-by: Brendan Jackman Reviewed-by: David Gow Signed-off-by: Shuah Khan --- tools/testing/kunit/kunit_kernel.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py index d30f90eae9a42..d3f39bc1ceec7 100644 --- a/tools/testing/kunit/kunit_kernel.py +++ b/tools/testing/kunit/kunit_kernel.py @@ -72,8 +72,8 @@ def make_olddefconfig(self, build_dir: str, make_options: Optional[List[str]]) - raise ConfigError(e.output.decode()) def make(self, jobs: int, build_dir: str, make_options: Optional[List[str]]) -> None: - command = ['make', 'all', 'compile_commands.json', 'ARCH=' + self._linux_arch, - 'O=' + build_dir, '--jobs=' + str(jobs)] + command = ['make', 'all', 'compile_commands.json', 'scripts_gdb', + 'ARCH=' + self._linux_arch, 'O=' + build_dir, '--jobs=' + str(jobs)] if make_options: command.extend(make_options) if self._cross_compile: From 0619a4868fc1b32b07fb9ed6c69adc5e5cf4e4b2 Mon Sep 17 00:00:00 2001 From: Kevin Brodsky Date: Mon, 17 Feb 2025 14:00:08 +0000 Subject: [PATCH 4/7] kunit: Clarify kunit_skip() argument name kunit_skip() and kunit_mark_skipped() can only be passed a pointer to a struct kunit, not struct kunit_suite (only kunit_log() actually supports both). Rename their first argument accordingly. Link: https://lore.kernel.org/r/20250217140008.1941287-1-kevin.brodsky@arm.com Signed-off-by: Kevin Brodsky Reviewed-by: David Gow Signed-off-by: Shuah Khan --- include/kunit/test.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/include/kunit/test.h b/include/kunit/test.h index 58dbab60f8530..0ffb97c785663 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -553,9 +553,9 @@ void kunit_cleanup(struct kunit *test); void __printf(2, 3) kunit_log_append(struct string_stream *log, const char *fmt, ...); /** - * kunit_mark_skipped() - Marks @test_or_suite as skipped + * kunit_mark_skipped() - Marks @test as skipped * - * @test_or_suite: The test context object. + * @test: The test context object. * @fmt: A printk() style format string. * * Marks the test as skipped. @fmt is given output as the test status @@ -563,18 +563,18 @@ void __printf(2, 3) kunit_log_append(struct string_stream *log, const char *fmt, * * Test execution continues after kunit_mark_skipped() is called. */ -#define kunit_mark_skipped(test_or_suite, fmt, ...) \ +#define kunit_mark_skipped(test, fmt, ...) \ do { \ - WRITE_ONCE((test_or_suite)->status, KUNIT_SKIPPED); \ - scnprintf((test_or_suite)->status_comment, \ + WRITE_ONCE((test)->status, KUNIT_SKIPPED); \ + scnprintf((test)->status_comment, \ KUNIT_STATUS_COMMENT_SIZE, \ fmt, ##__VA_ARGS__); \ } while (0) /** - * kunit_skip() - Marks @test_or_suite as skipped + * kunit_skip() - Marks @test as skipped * - * @test_or_suite: The test context object. + * @test: The test context object. * @fmt: A printk() style format string. * * Skips the test. @fmt is given output as the test status @@ -582,10 +582,10 @@ void __printf(2, 3) kunit_log_append(struct string_stream *log, const char *fmt, * * Test execution is halted after kunit_skip() is called. */ -#define kunit_skip(test_or_suite, fmt, ...) \ +#define kunit_skip(test, fmt, ...) \ do { \ - kunit_mark_skipped((test_or_suite), fmt, ##__VA_ARGS__);\ - kunit_try_catch_throw(&((test_or_suite)->try_catch)); \ + kunit_mark_skipped((test), fmt, ##__VA_ARGS__); \ + kunit_try_catch_throw(&((test)->try_catch)); \ } while (0) /* From c104c16073b7fdb3e4eae18f66f4009f6b073d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Gonz=C3=A1lez=20Collado?= Date: Sun, 2 Mar 2025 23:15:18 +0100 Subject: [PATCH 5/7] Kunit to check the longest symbol length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The longest length of a symbol (KSYM_NAME_LEN) was increased to 512 in the reference [1]. This patch adds kunit test suite to check the longest symbol length. These tests verify that the longest symbol length defined is supported. This test can also help other efforts for longer symbol length, like [2]. The test suite defines one symbol with the longest possible length. The first test verify that functions with names of the created symbol, can be called or not. The second test, verify that the symbols are created (or not) in the kernel symbol table. [1] https://lore.kernel.org/lkml/20220802015052.10452-6-ojeda@kernel.org/ [2] https://lore.kernel.org/lkml/20240605032120.3179157-1-song@kernel.org/ Link: https://lore.kernel.org/r/20250302221518.76874-1-sergio.collado@gmail.com Tested-by: Martin Rodriguez Reboredo Reviewed-by: Shuah Khan Reviewed-by: Rae Moar Signed-off-by: Sergio González Collado Link: https://github.com/Rust-for-Linux/linux/issues/504 Reviewed-by: Rae Moar Acked-by: David Gow Signed-off-by: Shuah Khan --- arch/x86/tools/insn_decoder_test.c | 3 +- lib/Kconfig.debug | 9 ++++ lib/Makefile | 2 + lib/longest_symbol_kunit.c | 82 ++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 lib/longest_symbol_kunit.c diff --git a/arch/x86/tools/insn_decoder_test.c b/arch/x86/tools/insn_decoder_test.c index 472540aeabc23..6c2986d2ad117 100644 --- a/arch/x86/tools/insn_decoder_test.c +++ b/arch/x86/tools/insn_decoder_test.c @@ -10,6 +10,7 @@ #include #include #include +#include #define unlikely(cond) (cond) @@ -106,7 +107,7 @@ static void parse_args(int argc, char **argv) } } -#define BUFSIZE 256 +#define BUFSIZE (256 + KSYM_NAME_LEN) int main(int argc, char **argv) { diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 1af972a92d06f..62d43aa9e8f08 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2838,6 +2838,15 @@ config FORTIFY_KUNIT_TEST by the str*() and mem*() family of functions. For testing runtime traps of FORTIFY_SOURCE, see LKDTM's "FORTIFY_*" tests. +config LONGEST_SYM_KUNIT_TEST + tristate "Test the longest symbol possible" if !KUNIT_ALL_TESTS + depends on KUNIT && KPROBES + default KUNIT_ALL_TESTS + help + Tests the longest symbol possible + + If unsure, say N. + config HW_BREAKPOINT_KUNIT_TEST bool "Test hw_breakpoint constraints accounting" if !KUNIT_ALL_TESTS depends on HAVE_HW_BREAKPOINT diff --git a/lib/Makefile b/lib/Makefile index d5cfc7afbbb82..e8fec9defec2c 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -393,6 +393,8 @@ obj-$(CONFIG_FORTIFY_KUNIT_TEST) += fortify_kunit.o obj-$(CONFIG_CRC_KUNIT_TEST) += crc_kunit.o obj-$(CONFIG_SIPHASH_KUNIT_TEST) += siphash_kunit.o obj-$(CONFIG_USERCOPY_KUNIT_TEST) += usercopy_kunit.o +obj-$(CONFIG_LONGEST_SYM_KUNIT_TEST) += longest_symbol_kunit.o +CFLAGS_longest_symbol_kunit.o += $(call cc-disable-warning, missing-prototypes) obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o diff --git a/lib/longest_symbol_kunit.c b/lib/longest_symbol_kunit.c new file mode 100644 index 0000000000000..e3c28ff1807f0 --- /dev/null +++ b/lib/longest_symbol_kunit.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test the longest symbol length. Execute with: + * ./tools/testing/kunit/kunit.py run longest-symbol + * --arch=x86_64 --kconfig_add CONFIG_KPROBES=y --kconfig_add CONFIG_MODULES=y + * --kconfig_add CONFIG_RETPOLINE=n --kconfig_add CONFIG_CFI_CLANG=n + * --kconfig_add CONFIG_MITIGATION_RETPOLINE=n + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include + +#define DI(name) s##name##name +#define DDI(name) DI(n##name##name) +#define DDDI(name) DDI(n##name##name) +#define DDDDI(name) DDDI(n##name##name) +#define DDDDDI(name) DDDDI(n##name##name) + +/*Generate a symbol whose name length is 511 */ +#define LONGEST_SYM_NAME DDDDDI(g1h2i3j4k5l6m7n) + +#define RETURN_LONGEST_SYM 0xAAAAA + +noinline int LONGEST_SYM_NAME(void); +noinline int LONGEST_SYM_NAME(void) +{ + return RETURN_LONGEST_SYM; +} + +_Static_assert(sizeof(__stringify(LONGEST_SYM_NAME)) == KSYM_NAME_LEN, +"Incorrect symbol length found. Expected KSYM_NAME_LEN: " +__stringify(KSYM_NAME_LEN) ", but found: " +__stringify(sizeof(LONGEST_SYM_NAME))); + +static void test_longest_symbol(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, RETURN_LONGEST_SYM, LONGEST_SYM_NAME()); +}; + +static void test_longest_symbol_kallsyms(struct kunit *test) +{ + unsigned long (*kallsyms_lookup_name)(const char *name); + static int (*longest_sym)(void); + + struct kprobe kp = { + .symbol_name = "kallsyms_lookup_name", + }; + + if (register_kprobe(&kp) < 0) { + pr_info("%s: kprobe not registered", __func__); + KUNIT_FAIL(test, "test_longest_symbol kallsyms: kprobe not registered\n"); + return; + } + + kunit_warn(test, "test_longest_symbol kallsyms: kprobe registered\n"); + kallsyms_lookup_name = (unsigned long (*)(const char *name))kp.addr; + unregister_kprobe(&kp); + + longest_sym = + (void *) kallsyms_lookup_name(__stringify(LONGEST_SYM_NAME)); + KUNIT_EXPECT_EQ(test, RETURN_LONGEST_SYM, longest_sym()); +}; + +static struct kunit_case longest_symbol_test_cases[] = { + KUNIT_CASE(test_longest_symbol), + KUNIT_CASE(test_longest_symbol_kallsyms), + {} +}; + +static struct kunit_suite longest_symbol_test_suite = { + .name = "longest-symbol", + .test_cases = longest_symbol_test_cases, +}; +kunit_test_suite(longest_symbol_test_suite); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Test the longest symbol length"); +MODULE_AUTHOR("Sergio González Collado"); From 1d4c06d51963f89f67c7b75d5c0c34e9d1bb2ae6 Mon Sep 17 00:00:00 2001 From: Rae Moar Date: Thu, 13 Mar 2025 19:27:13 +0000 Subject: [PATCH 6/7] kunit: tool: Fix bug in parsing test plan A bug was identified where the KTAP below caused an infinite loop: TAP version 13 ok 4 test_case 1..4 The infinite loop was caused by the parser not parsing a test plan if following a test result line. Fix this bug by parsing test plan line to avoid the infinite loop. Link: https://lore.kernel.org/r/20250313192714.1380005-1-rmoar@google.com Signed-off-by: Rae Moar Reviewed-by: David Gow Signed-off-by: Shuah Khan --- tools/testing/kunit/kunit_parser.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py index 29fc27e8949bd..da53a709773a2 100644 --- a/tools/testing/kunit/kunit_parser.py +++ b/tools/testing/kunit/kunit_parser.py @@ -759,7 +759,7 @@ def parse_test(lines: LineStream, expected_num: int, log: List[str], is_subtest: # If parsing the main/top-level test, parse KTAP version line and # test plan test.name = "main" - ktap_line = parse_ktap_header(lines, test, printer) + parse_ktap_header(lines, test, printer) test.log.extend(parse_diagnostic(lines)) parse_test_plan(lines, test) parent_test = True @@ -768,13 +768,12 @@ def parse_test(lines: LineStream, expected_num: int, log: List[str], is_subtest: # the KTAP version line and/or subtest header line ktap_line = parse_ktap_header(lines, test, printer) subtest_line = parse_test_header(lines, test) + test.log.extend(parse_diagnostic(lines)) + parse_test_plan(lines, test) parent_test = (ktap_line or subtest_line) if parent_test: - # If KTAP version line and/or subtest header is found, attempt - # to parse test plan and print test header - test.log.extend(parse_diagnostic(lines)) - parse_test_plan(lines, test) print_test_header(test, printer) + expected_count = test.expected_count subtests = [] test_num = 1 From 2e0cf2b32f72b20b0db5cc665cd8465d0f257278 Mon Sep 17 00:00:00 2001 From: Rae Moar Date: Thu, 13 Mar 2025 19:27:14 +0000 Subject: [PATCH 7/7] kunit: tool: add test to check parsing late test plan Add test to check for the infinite loop caused by the inability to parse a late test plan. The test parses the following output: TAP version 13 ok 4 test4 1..4 Link: https://lore.kernel.org/r/20250313192714.1380005-1-rmoar@google.com Signed-off-by: Rae Moar Reviewed-by: David Gow Signed-off-by: Shuah Khan --- tools/testing/kunit/kunit_tool_test.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tools/testing/kunit/kunit_tool_test.py b/tools/testing/kunit/kunit_tool_test.py index 0bcb0cc002f8a..5ff4f6ffd8736 100755 --- a/tools/testing/kunit/kunit_tool_test.py +++ b/tools/testing/kunit/kunit_tool_test.py @@ -363,6 +363,17 @@ def test_show_test_output_on_failure(self): self.print_mock.assert_any_call(StrContains(' Indented more.')) self.noPrintCallContains('not ok 1 test1') + def test_parse_late_test_plan(self): + output = """ + TAP version 13 + ok 4 test4 + 1..4 + """ + result = kunit_parser.parse_run_tests(output.splitlines(), stdout) + # Missing test results after test plan should alert a suspected test crash. + self.assertEqual(kunit_parser.TestStatus.TEST_CRASHED, result.status) + self.assertEqual(result.counts, kunit_parser.TestCounts(passed=1, crashed=1, errors=1)) + def line_stream_from_strs(strs: Iterable[str]) -> kunit_parser.LineStream: return kunit_parser.LineStream(enumerate(strs, start=1))