From 05aa5c7c79c8d7962e9359e1c27f1d53daac8936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ciro=20Santilli=20=E5=85=AD=E5=9B=9B=E4=BA=8B=E4=BB=B6=20?= =?UTF-8?q?=E6=B3=95=E8=BD=AE=E5=8A=9F?= Date: Fri, 24 May 2019 00:00:00 +0000 Subject: [PATCH] baremetal: build userland/ programs using baremetal path property instead of symlinks Otherwise I'll go crazy with symlink action. --- README.adoc | 73 +++++------ .../arch/aarch64/no_bootloader/gem5_exit.S | 4 +- .../aarch64/no_bootloader/semihost_exit.S | 4 +- baremetal/arch/arm/no_bootloader/gem5_exit.S | 4 +- .../arch/arm/no_bootloader/semihost_exit.S | 4 +- baremetal/c/add.c | 1 - baremetal/c/add.py | 1 - baremetal/c/assert_fail.c | 1 - baremetal/c/hello.c | 1 - baremetal/c/infinite_loop.c | 1 - baremetal/c/stderr.c | 1 - baremetal/lib/aarch64.S | 5 +- baremetal/lib/arm.S | 5 +- baremetal/lib/syscalls.c | 7 ++ baremetal/link.ld | 2 +- baremetal/return.c | 1 - build-baremetal | 6 +- build-userland | 4 +- common.py | 90 ++++++++----- lkmc.c | 9 +- lkmc.h | 1 - lkmc/c/add.c | 13 -- lkmc/c/add.py | 9 -- lkmc/c/assert_fail.c | 18 --- lkmc/c/getchar.c | 21 ---- lkmc/c/hello.c | 9 -- lkmc/c/infinite_loop.c | 29 ----- lkmc/c/stderr.c | 7 -- path_properties.py | 118 +++++++++++------- test-baremetal | 8 +- test-user-mode | 9 +- userland/arch/aarch64/fadd_scalar.S | 6 + userland/arch/aarch64/pc.S | 1 + userland/arch/{fail.S => lkmc_assert_fail.S} | 0 userland/c/abort.c | 34 +++++ userland/c/add.c | 14 ++- userland/c/add.py | 10 +- userland/c/assert_fail.c | 19 ++- {baremetal => userland/c}/exit0.c | 0 {baremetal => userland/c}/exit1.c | 0 userland/c/exit2.c | 7 ++ userland/c/getchar.c | 22 +++- userland/c/hello.c | 10 +- userland/c/infinite_loop.c | 30 ++++- {baremetal => userland/c}/out_of_memory.c | 10 +- userland/c/return0.c | 6 + {baremetal => userland/c}/return1.c | 0 {baremetal => userland/c}/return2.c | 0 userland/c/stderr.c | 8 +- 49 files changed, 372 insertions(+), 271 deletions(-) delete mode 120000 baremetal/c/add.c delete mode 120000 baremetal/c/add.py delete mode 120000 baremetal/c/assert_fail.c delete mode 120000 baremetal/c/hello.c delete mode 120000 baremetal/c/infinite_loop.c delete mode 120000 baremetal/c/stderr.c delete mode 100644 baremetal/return.c delete mode 100644 lkmc/c/add.c delete mode 100644 lkmc/c/add.py delete mode 100644 lkmc/c/assert_fail.c delete mode 100644 lkmc/c/getchar.c delete mode 100644 lkmc/c/hello.c delete mode 100644 lkmc/c/infinite_loop.c delete mode 100644 lkmc/c/stderr.c rename userland/arch/{fail.S => lkmc_assert_fail.S} (100%) create mode 100644 userland/c/abort.c mode change 120000 => 100644 userland/c/add.c mode change 120000 => 100644 userland/c/add.py mode change 120000 => 100644 userland/c/assert_fail.c rename {baremetal => userland/c}/exit0.c (100%) rename {baremetal => userland/c}/exit1.c (100%) create mode 100644 userland/c/exit2.c mode change 120000 => 100644 userland/c/getchar.c mode change 120000 => 100644 userland/c/hello.c mode change 120000 => 100644 userland/c/infinite_loop.c rename {baremetal => userland/c}/out_of_memory.c (51%) create mode 100644 userland/c/return0.c rename {baremetal => userland/c}/return1.c (100%) rename {baremetal => userland/c}/return2.c (100%) mode change 120000 => 100644 userland/c/stderr.c diff --git a/README.adoc b/README.adoc index 41a5759..88d63d2 100644 --- a/README.adoc +++ b/README.adoc @@ -1206,35 +1206,6 @@ The terminal prints: hello .... -Now let's run link:baremetal/arch/aarch64/add.S[]: - -.... -./run --arch aarch64 --baremetal baremetal/arch/aarch64/add.S -.... - -This time, the terminal does not print anything, which indicates success. - -If you look into the source, you will see that we just have an assertion there. - -You can see a sample assertion fail in link:baremetal/interactive/assert_fail.c[]: - -.... -./run --arch aarch64 --baremetal baremetal/interactive/assert_fail.c -.... - -and the terminal contains: - -.... -lkmc_test_fail -error: simulation error detected by parsing logs -.... - -and the exit status of our script is 1: - -.... -echo $? -.... - To modify a baremetal program, simply edit the file, e.g. .... @@ -1252,17 +1223,44 @@ and rebuild: `./build-baremetal` uses crosstool-NG, and so it must be preceded by link:build-crosstool-ng[], which `./build qemu-baremetal` also calls. -Alternatively, for the sake of tab completion, we also accept relative paths inside `baremetal/`, for example the following also work: + +Now let's run link:baremetal/arch/aarch64/add.S[]: .... -./run --arch aarch64 --baremetal baremetal/c/hello.c ./run --arch aarch64 --baremetal baremetal/arch/aarch64/add.S .... -Absolute paths however are used as is and must point to the actual executable: +This time, the terminal does not print anything, which indicates success: if you look into the source, you will see that we just have an assertion there. + +You can see a sample assertion fail in link:baremetal/interactive/assert_fail.c[]: .... -./run --arch aarch64 --baremetal "$(./getvar --arch aarch64 baremetal_build_dir)/hello.elf" +./run --arch aarch64 --baremetal userland/c/assert_fail.c +.... + +and the terminal contains: + +.... +lkmc_test_fail +error: simulation error detected by parsing logs +.... + +and the exit status of our script is 1: + +.... +echo $? +.... + +As suggested by the above example, several of the <> can also be run in baremetal! This is largely due to the <>. + +The examples that work include most <> that don't rely on complicated syscalls such as threads, and almost all the <> examples. + +The exact list of userland programs that work in baremetal is specified in <> with the `baremetal` property. + +You can run all the baremetal examples in one go and check that all assertions passed with: + +.... +./test-baremetal --arch aarch64 .... To use gem5 instead of QEMU do: @@ -11554,6 +11552,9 @@ Programs under link:userland/c/[] are examples of link:https://en.wikipedia.org/ * Standard library ** assert.h *** link:userland/c/assert_fail.c[] +** `stdlib.h` +*** Exit related +**** link:userland/c/abort.c[] Userland assembly content is located at: <>. It was split from this section basically becase we were hitting the HTML `h6` limit, stupid web :-) @@ -11677,7 +11678,7 @@ Other infrastructure sanity checks that you might want to look into include: * link:userland/arch/empty.S[] * `LKMC_FAIL` tests -** link:userland/arch/fail.S[] +** link:userland/arch/lkmc_assert_fail.S[] * `LKMC_ASSERT_EQ` tests ** link:userland/arch/x86_64/lkmc_assert_eq_fail.S[] ** link:userland/arch/arm/lkmc_assert_eq_fail.S[] @@ -13609,8 +13610,8 @@ info threads shows something like: .... -* 1 Thread 1 (CPU#0 [running]) mystart - 2 Thread 2 (CPU#1 [halted ]) mystart +* 1 Thread 1 (CPU#0 [running]) lkmc_start + 2 Thread 2 (CPU#1 [halted ]) lkmc_start .... To wake up CPU 1 on QEMU, we must use the Power State Coordination Interface (PSCI) which is documented at: link:https://developer.arm.com/docs/den0022/latest/arm-power-state-coordination-interface-platform-design-document[]. diff --git a/baremetal/arch/aarch64/no_bootloader/gem5_exit.S b/baremetal/arch/aarch64/no_bootloader/gem5_exit.S index 0c9ba89..06045c0 100644 --- a/baremetal/arch/aarch64/no_bootloader/gem5_exit.S +++ b/baremetal/arch/aarch64/no_bootloader/gem5_exit.S @@ -1,5 +1,5 @@ #include -.global mystart -mystart: +.global lkmc_start +lkmc_start: LKMC_M5OPS_EXIT_ASM diff --git a/baremetal/arch/aarch64/no_bootloader/semihost_exit.S b/baremetal/arch/aarch64/no_bootloader/semihost_exit.S index 634880a..62fa06f 100644 --- a/baremetal/arch/aarch64/no_bootloader/semihost_exit.S +++ b/baremetal/arch/aarch64/no_bootloader/semihost_exit.S @@ -1,7 +1,7 @@ /* https://github.com/cirosantilli/linux-kernel-module-cheat#semihosting */ -.global mystart -mystart: +.global lkmc_start +lkmc_start: mov x1, 0x26 movk x1, 2, lsl 16 ldr x2, =semihost_args diff --git a/baremetal/arch/arm/no_bootloader/gem5_exit.S b/baremetal/arch/arm/no_bootloader/gem5_exit.S index 4122d2a..deed641 100644 --- a/baremetal/arch/arm/no_bootloader/gem5_exit.S +++ b/baremetal/arch/arm/no_bootloader/gem5_exit.S @@ -1,3 +1,3 @@ -.global mystart -mystart: +.global lkmc_start +lkmc_start: mov r0, #0; mov r1, #0; .inst 0xEE000110 | (0x21 << 16); diff --git a/baremetal/arch/arm/no_bootloader/semihost_exit.S b/baremetal/arch/arm/no_bootloader/semihost_exit.S index c9c6467..bfe85c2 100644 --- a/baremetal/arch/arm/no_bootloader/semihost_exit.S +++ b/baremetal/arch/arm/no_bootloader/semihost_exit.S @@ -1,5 +1,5 @@ -.global mystart -mystart: +.global lkmc_start +lkmc_start: mov r0, #0x18 ldr r1, =#0x20026 svc 0x00123456 diff --git a/baremetal/c/add.c b/baremetal/c/add.c deleted file mode 120000 index 46def32..0000000 --- a/baremetal/c/add.c +++ /dev/null @@ -1 +0,0 @@ -../../lkmc/c/add.c \ No newline at end of file diff --git a/baremetal/c/add.py b/baremetal/c/add.py deleted file mode 120000 index fb5788a..0000000 --- a/baremetal/c/add.py +++ /dev/null @@ -1 +0,0 @@ -../../lkmc/c/add.py \ No newline at end of file diff --git a/baremetal/c/assert_fail.c b/baremetal/c/assert_fail.c deleted file mode 120000 index 904602c..0000000 --- a/baremetal/c/assert_fail.c +++ /dev/null @@ -1 +0,0 @@ -../../lkmc/c/assert_fail.c \ No newline at end of file diff --git a/baremetal/c/hello.c b/baremetal/c/hello.c deleted file mode 120000 index 3f0a531..0000000 --- a/baremetal/c/hello.c +++ /dev/null @@ -1 +0,0 @@ -../../lkmc/c/hello.c \ No newline at end of file diff --git a/baremetal/c/infinite_loop.c b/baremetal/c/infinite_loop.c deleted file mode 120000 index faa4cdd..0000000 --- a/baremetal/c/infinite_loop.c +++ /dev/null @@ -1 +0,0 @@ -../../lkmc/c/infinite_loop.c \ No newline at end of file diff --git a/baremetal/c/stderr.c b/baremetal/c/stderr.c deleted file mode 120000 index 1344887..0000000 --- a/baremetal/c/stderr.c +++ /dev/null @@ -1 +0,0 @@ -../../lkmc/c/stderr.c \ No newline at end of file diff --git a/baremetal/lib/aarch64.S b/baremetal/lib/aarch64.S index 35e9c0d..1a6aee4 100644 --- a/baremetal/lib/aarch64.S +++ b/baremetal/lib/aarch64.S @@ -1,7 +1,7 @@ #include -.global mystart -mystart: +.global lkmc_start +lkmc_start: /* = NEON setup */ mov x1, #(0x3 << 20) msr cpacr_el1, x1 @@ -20,6 +20,7 @@ mystart: bl on_exit /* Run main. */ + mov x0, 0 bl main /* If main returns, exit. */ diff --git a/baremetal/lib/arm.S b/baremetal/lib/arm.S index d0fc20b..340ca8b 100644 --- a/baremetal/lib/arm.S +++ b/baremetal/lib/arm.S @@ -1,7 +1,7 @@ #include -.global mystart -mystart: +.global lkmc_start +lkmc_start: /* Prepare the stack for main, mandatory for C code. */ ldr sp, =stack_top @@ -10,6 +10,7 @@ mystart: bl on_exit /* Run main. */ + mov r0, 0 bl main /* If main returns, exit. */ diff --git a/baremetal/lib/syscalls.c b/baremetal/lib/syscalls.c index 1d631bd..647d1cd 100644 --- a/baremetal/lib/syscalls.c +++ b/baremetal/lib/syscalls.c @@ -5,6 +5,13 @@ #include #include +void lkmc_baremetal_on_exit_callback(int status, void *arg) { + (void)arg; + if (status != 0) { + printf("lkmc_exit_status_%d\n", status); + } +} + enum { UART_FR_RXFE = 0x10, }; diff --git a/baremetal/link.ld b/baremetal/link.ld index 2a3ef8c..91a9119 100644 --- a/baremetal/link.ld +++ b/baremetal/link.ld @@ -1,4 +1,4 @@ -ENTRY(mystart) +ENTRY(lkmc_start) SECTIONS { .text : { diff --git a/baremetal/return.c b/baremetal/return.c deleted file mode 100644 index 78f2de1..0000000 --- a/baremetal/return.c +++ /dev/null @@ -1 +0,0 @@ -int main(void) { return 0; } diff --git a/build-baremetal b/build-baremetal index cb08c91..8297c53 100755 --- a/build-baremetal +++ b/build-baremetal @@ -14,6 +14,7 @@ class Main(common.BuildCliFunction): description='''\ Build the baremetal examples with crosstool-NG. ''', + is_baremetal=True, supported_archs=common.consts['crosstool_ng_supported_archs'] ) self._add_argument('--ccflags') @@ -137,7 +138,10 @@ Build the baremetal examples with crosstool-NG. def setup_one(self): self.env['targets'] = self.resolve_targets( - self.env['baremetal_source_dir'], + [ + self.env['baremetal_source_dir'], + self.env['userland_source_dir'] + ], self.env['targets'] ) diff --git a/build-userland b/build-userland index 4d9a390..a389fa6 100755 --- a/build-userland +++ b/build-userland @@ -15,7 +15,7 @@ class Main(common.BuildCliFunction): kwargs['description'] = '''\ Build our compiled userland examples. ''' - super().__init__(*args, **kwargs) + super().__init__(*args, is_userland=True, **kwargs) self._add_argument('--ccflags') self._add_argument('--force-rebuild') self._add_argument('--optimization-level') @@ -109,7 +109,7 @@ Default: build all examples that have their package dependencies met, e.g.: def setup_one(self): self.env['targets'] = self.resolve_targets( - self.env['userland_source_dir'], + [self.env['userland_source_dir']], self.env['targets'] ) diff --git a/common.py b/common.py index e2c00f6..0c38a88 100644 --- a/common.py +++ b/common.py @@ -162,6 +162,7 @@ class LkmcCliFunction(cli_function.CliFunction): def __init__( self, *args, + is_baremetal=False, is_userland=False, defaults=None, supported_archs=None, @@ -175,7 +176,8 @@ class LkmcCliFunction(cli_function.CliFunction): kwargs['extra_config_params'] = os.path.basename(inspect.getfile(self.__class__)) if defaults is None: defaults = {} - self._is_userland = is_userland + self.is_baremetal = is_baremetal + self.is_userland = is_userland self._defaults = defaults self._is_common = True self._common_args = set() @@ -1035,8 +1037,12 @@ lunch aosp_{}-eng self._common_args.add(key) super().add_argument(*args, **kwargs) - def assert_is_subpath(self, subpath, parent): - if not self.is_subpath(subpath, parent): + def assert_is_subpath(self, subpath, parents): + is_subpath = False + for parent in parents: + if self.is_subpath(subpath, parent): + is_subpath = True + if not is_subpath: raise Exception( 'Can only accept targets inside {}, given: {}'.format( parent, @@ -1204,7 +1210,7 @@ lunch aosp_{}-eng continue else: raise Exception('native emulator only supported in if target arch == host arch') - if env['userland'] is None and not self._is_userland: + if env['userland'] is None and not self.is_userland: if real_all_emulators: continue else: @@ -1314,7 +1320,7 @@ lunch aosp_{}-eng def resolve_executable( self, in_path, - magic_in_dir, + magic_in_dirs, magic_out_dir, executable_ext ): @@ -1331,35 +1337,47 @@ lunch aosp_{}-eng ''' if not self.env['dry_run'] and not os.path.exists(in_path): raise Exception('Input path does not exist: ' + in_path) - if self.is_subpath(in_path, magic_in_dir): - # Abspath needed to remove the trailing `/.` which makes e.g. rmrf fail. - out = os.path.abspath(os.path.join( - magic_out_dir, - os.path.relpath( - os.path.splitext(in_path)[0], - magic_in_dir - ) - )) - if os.path.isfile(in_path): - out += executable_ext - return out + if len(magic_in_dirs) > 1: + relative_subpath = self.env['root_dir'] else: - return in_path + relative_subpath = magic_in_dirs[0] + for magic_in_dir in magic_in_dirs: + if self.is_subpath(in_path, magic_in_dir): + # Abspath needed to remove the trailing `/.` which makes e.g. rmrf fail. + out = os.path.abspath(os.path.join( + magic_out_dir, + os.path.relpath( + os.path.splitext(in_path)[0], + relative_subpath + ) + )) + if os.path.isfile(in_path): + out += executable_ext + return out + return in_path - def resolve_targets(self, source_dir, targets): + def resolve_targets(self, source_dirs, targets): + ''' + Resolve userland or baremetal CLI provided targets to final paths. + + Notably converts the toplevel directory into all source directories needed. + ''' if not targets: - targets = [source_dir] + targets = source_dirs.copy() new_targets = [] for target in targets: - target = self.toplevel_to_source_dir(target, source_dir) - self.assert_is_subpath(target, source_dir) - new_targets.append(target) + for resolved_target in self.toplevel_to_source_dirs(target, source_dirs): + self.assert_is_subpath(resolved_target, source_dirs) + new_targets.append(resolved_target) return new_targets def resolve_baremetal_executable(self, path): return self.resolve_executable( path, - self.env['baremetal_source_dir'], + [ + self.env['baremetal_source_dir'], + self.env['userland_source_dir'] + ], self.env['baremetal_build_dir'], self.env['baremetal_executable_ext'], ) @@ -1367,7 +1385,7 @@ lunch aosp_{}-eng def resolve_userland_executable(self, path): return self.resolve_executable( path, - self.env['userland_source_dir'], + [self.env['userland_source_dir']], self.env['userland_build_dir'], self.env['userland_executable_ext'], ) @@ -1386,12 +1404,12 @@ lunch aosp_{}-eng ''' pass - def toplevel_to_source_dir(self, path, source_dir): + def toplevel_to_source_dirs(self, path, source_dirs): path = os.path.abspath(path) if path == self.env['root_dir']: - return source_dir + return source_dirs else: - return path + return [path] def timed_main(self): ''' @@ -1491,13 +1509,21 @@ https://github.com/cirosantilli/linux-kernel-module-cheat#gem5-debug-build dirpath_relative_root, in_basename )) - if my_path_properties.should_be_built(self.env, link): + if my_path_properties.should_be_built( + self.env, + link, + is_baremetal=self.is_baremetal, + is_userland=self.is_userland + ): if extra_objs is None: extra_objs= [] if link: - if my_path_properties['extra_objs_lkmc_common']: + if self.is_baremetal or my_path_properties['extra_objs_lkmc_common']: extra_objs.extend(extra_objs_lkmc_common) - if my_path_properties['extra_objs_baremetal_bootloader']: + if ( + self.is_baremetal and + not my_path_properties['extra_objs_disable_baremetal_bootloader'] + ): extra_objs.extend(extra_objs_baremetal_bootloader) if self.need_rebuild([in_path] + extra_objs + extra_deps, out_path): cc_flags.extend(my_path_properties['cc_flags']) @@ -1542,7 +1568,7 @@ https://github.com/cirosantilli/linux-kernel-module-cheat#gem5-debug-build 'cc_flags_after': [], }, } - package_key = dirpath_relative_root_components[1] + package_key = dirpath_relative_root_components[2] if package_key in packages: package = packages[package_key] else: diff --git a/lkmc.c b/lkmc.c index a82414f..26f8e2b 100644 --- a/lkmc.c +++ b/lkmc.c @@ -23,7 +23,7 @@ LKMC_ASSERT_EQ_DEFINE(64) void lkmc_assert_fail(uint32_t line) { printf("error: assertion failed at line: %" PRIu32 "\n", line); - exit(1); + abort(); } void lkmc_assert_memcmp( @@ -56,13 +56,6 @@ void lkmc_assert_memcmp( } } -void lkmc_baremetal_on_exit_callback(int status, void *arg) { - (void)arg; - if (status != 0) { - printf("lkmc_exit_status_%d\n", status); - } -} - #if defined(__aarch64__) #define LKMC_SYSREG_READ_WRITE(type, name) \ type LKMC_CONCAT(LKMC_CONCAT(LKMC_SYSREG_SYMBOL_PREFIX, name), _read(void)) { \ diff --git a/lkmc.h b/lkmc.h index 7fb5be6..f1664b3 100644 --- a/lkmc.h +++ b/lkmc.h @@ -20,7 +20,6 @@ LKMC_ASSERT_EQ_DECLARE(32); LKMC_ASSERT_EQ_DECLARE(64); void lkmc_assert_fail(uint32_t line); void lkmc_assert_memcmp(const void *s1, const void *s2, size_t n, uint32_t line); -void lkmc_baremetal_on_exit_callback(int status, void *arg); #endif /* Assert that the given branch instruction is taken. */ diff --git a/lkmc/c/add.c b/lkmc/c/add.c deleted file mode 100644 index 6b3606c..0000000 --- a/lkmc/c/add.c +++ /dev/null @@ -1,13 +0,0 @@ -#include - -int main(void) { - int i, j, k; - i = 1; - /* test-gdb-op1 */ - j = 2; - /* test-gdb-op2 */ - k = i + j; - /* test-gdb-result */ - if (k != 3) - assert(0); -} diff --git a/lkmc/c/add.py b/lkmc/c/add.py deleted file mode 100644 index 0f42d2b..0000000 --- a/lkmc/c/add.py +++ /dev/null @@ -1,9 +0,0 @@ -def test(self): - self.sendline('tbreak main') - self.sendline('continue') - self.continue_to('op1') - assert self.get_int('i') == 1 - self.continue_to('op2') - assert self.get_int('j') == 2 - self.continue_to('result') - assert self.get_int('k') == 3 diff --git a/lkmc/c/assert_fail.c b/lkmc/c/assert_fail.c deleted file mode 100644 index cc881b6..0000000 --- a/lkmc/c/assert_fail.c +++ /dev/null @@ -1,18 +0,0 @@ -/* Let's see what happens when an assert fails. - * - * Outcome on Ubuntu 19.04 shows the failure line: - * - * assert_fail.out: /path/to/linux-kernel-module-cheat/userland/c/assert_fail.c:15: main: Assertion `0' failed. - * - * and exit status 134 == 128 + 6, which corresponds to SIGABORT (6). - */ - -#include -#include -#include - -int main(void) { - assert(0); - puts("here"); - return EXIT_SUCCESS; -} diff --git a/lkmc/c/getchar.c b/lkmc/c/getchar.c deleted file mode 100644 index 2430e29..0000000 --- a/lkmc/c/getchar.c +++ /dev/null @@ -1,21 +0,0 @@ -/* Get on character from stdin, and then print it back out. - * - * Same as getc(stdin). - * - * You have to press enter for the character to go through: - * https://stackoverflow.com/questions/1798511/how-to-avoid-pressing-enter-with-getchar - * - * Used at: - * https://stackoverflow.com/questions/556405/what-do-real-user-and-sys-mean-in-the-output-of-time1/53937376#53937376 - */ - -#include -#include - -int main(void) { - char c; - printf("enter a character: "); - c = getchar(); - printf("you entered: %c\n", c); - return EXIT_SUCCESS; -} diff --git a/lkmc/c/hello.c b/lkmc/c/hello.c deleted file mode 100644 index 42cb55d..0000000 --- a/lkmc/c/hello.c +++ /dev/null @@ -1,9 +0,0 @@ -/* Print hello to stdout ;-) */ - -#include -#include - -int main(void) { - puts("hello"); - return EXIT_SUCCESS; -} diff --git a/lkmc/c/infinite_loop.c b/lkmc/c/infinite_loop.c deleted file mode 100644 index 2ae905f..0000000 --- a/lkmc/c/infinite_loop.c +++ /dev/null @@ -1,29 +0,0 @@ -/* Loop infinitely. Print an integer whenever a period is reached: - * - * .... - * ./infinite_loop [period] - * .... - */ - -#include -#include -#include -#include - -int main(int argc, char **argv) { - uintmax_t i, j, period; - if (argc > 1) { - period = strtoumax(argv[1], NULL, 10); - } else { - period = 100000000; - } - i = 0; - j = 0; - while (1) { - i++; - if (i % period == 0) { - printf("%ju\n", j); - j++; - } - } -} diff --git a/lkmc/c/stderr.c b/lkmc/c/stderr.c deleted file mode 100644 index b43d067..0000000 --- a/lkmc/c/stderr.c +++ /dev/null @@ -1,7 +0,0 @@ -/* Print hello to stderr. */ - -#include - -int main(void) { - fputs("hello\n", stderr); -} diff --git a/path_properties.py b/path_properties.py index aa665de..7360786 100644 --- a/path_properties.py +++ b/path_properties.py @@ -1,14 +1,18 @@ #!/usr/bin/env python3 import os +import signal from shell_helpers import LF class PathProperties: default_c_std = 'c11' default_cxx_std = 'c++17' + # All new properties must be listed here or else you get an error. default_properties = { 'allowed_archs': None, + # Examples that can be built in baremetal. + 'baremetal': False, 'c_std': default_c_std, 'cc_flags': [ '-Wall', LF, @@ -29,11 +33,14 @@ class PathProperties: 'cc_flags_after': ['-lm', LF], 'cc_pedantic': True, 'cxx_std': default_cxx_std, + 'disrupts_system': False, # Expected program exit status. When signals are raised, this refers # to the native exit status. as reported by Bash #?. 'exit_status': 0, 'extra_objs': [], - 'extra_objs_baremetal_bootloader': False, + # Explicitly don't add the baremetal bootloader object which normally gets automatically + # added to baremetal examples. + 'extra_objs_disable_baremetal_bootloader': False, # We should get rid of this if we ever properly implement dependency graphs. 'extra_objs_lkmc_common': False, 'interactive': False, @@ -45,10 +52,7 @@ class PathProperties: # it only generates intermediate object files. Therefore it # should not be run while testing. 'no_executable': False, - # the test receives a signal. We skip those tests for now, - # on userland because we are lazy to figure out the exact semantics - # of how Python + QEMU + gem5 determine the exit status of signals. - 'receives_signal': False, + 'receives_signal': None, # The script requires a non-trivial argument to be passed to run properly. 'requires_argument': False, 'requires_dynamic_library': False, @@ -68,6 +72,8 @@ class PathProperties: # Aruments added automatically to run when running tests, # but not on manual running. 'test_run_args': {}, + # Examples that can be built in userland. + 'userland': False, } ''' @@ -93,7 +99,13 @@ class PathProperties: def set_path_components(self, path_components): self.path_components = path_components - def should_be_built(self, env, link=False): + def should_be_built( + self, + env, + link=False, + is_baremetal=False, + is_userland=False, + ): if len(self.path_components) > 1 and \ self.path_components[1] == 'libs' and \ not env['package_all'] and \ @@ -105,14 +117,22 @@ class PathProperties: self['allowed_archs'] is None or env['arch'] in self['allowed_archs'] ) and \ + ( + (is_userland and self['userland'] ) or + (is_baremetal and self['baremetal']) + ) and \ not ( link and self['no_executable'] ) - def should_be_tested(self, env): + def should_be_tested(self, env, is_baremetal=False, is_userland=False): return ( - self.should_be_built(env) and + self.should_be_built( + env, + is_baremetal=is_baremetal, + is_userland=is_userland + ) and not self['interactive'] and not self['more_than_1s'] and not self['no_executable'] and @@ -203,6 +223,7 @@ gnu_extension_properties = { 'cxx_std': 'gnu++17' } freestanding_properties = { + 'baremetal': False, 'cc_flags': [ '-ffreestanding', LF, '-nostdlib', LF, @@ -216,8 +237,7 @@ path_properties_tuples = ( { 'baremetal': ( { - 'extra_objs_baremetal_bootloader': True, - 'extra_objs_lkmc_common': True, + 'baremetal': True, }, { 'arch': ( @@ -229,7 +249,7 @@ path_properties_tuples = ( 'gem5_assert.S': {'requires_m5ops': True}, 'multicore.S': {'test_run_args': {'cpus': 2}}, 'no_bootloader': ( - {'extra_objs_baremetal_bootloader': False}, + {'extra_objs_disable_baremetal_bootloader': True}, { 'gem5_exit.S': {'requires_m5ops': True}, 'semihost_exit.S': {'requires_semihosting': True}, @@ -245,7 +265,7 @@ path_properties_tuples = ( { 'multicore.S': {'test_run_args': {'cpus': 2}}, 'no_bootloader': ( - {'extra_objs_baremetal_bootloader': False}, + {'extra_objs_disable_baremetal_bootloader': True}, { 'gem5_exit.S': {'requires_m5ops': True}, 'semihost_exit.S': {'requires_semihosting': True}, @@ -257,32 +277,22 @@ path_properties_tuples = ( ) } ), - 'c': ( - {}, - { - 'assert_fail.c': {'exit_status': 134}, - 'infinite_loop.c': {'more_than_1s': True}, - } - ), - 'exit1.c': {'exit_status': 1}, 'lib': {'no_executable': True}, 'getchar.c': {'interactive': True}, - 'return1.c': {'exit_status': 1}, - 'return2.c': {'exit_status': 2}, } ), + 'lkmc.c': { + 'baremetal': True, + 'userland': True, + }, 'userland': ( { - 'cc_flags': [ - '-fopenmp', LF, - ], - 'cc_flags_after': [ - '-pthread', LF, - ], + 'userland': True, }, { 'arch': ( { + 'baremetal': True, 'extra_objs_lkmc_common': True, }, { @@ -316,11 +326,10 @@ path_properties_tuples = ( }, ), 'freestanding': freestanding_properties, - 'lkmc_assert_eq_fail.S': {'exit_status': 1}, - 'lkmc_assert_memcmp_fail.S': {'exit_status': 1}, + 'lkmc_assert_eq_fail.S': {'receives_signal': signal.Signals.SIGABRT}, + 'lkmc_assert_memcmp_fail.S': {'receives_signal': signal.Signals.SIGABRT}, 'udf.S': { - 'exit_status': 132, - 'receives_signal': True + 'receives_signal': signal.Signals.SIGILL }, } ), @@ -335,15 +344,16 @@ path_properties_tuples = ( }, ), 'freestanding': freestanding_properties, - 'lkmc_assert_eq_fail.S': {'exit_status': 1}, - 'lkmc_assert_memcmp_fail.S': {'exit_status': 1}, + 'lkmc_assert_eq_fail.S': {'receives_signal': signal.Signals.SIGABRT}, + 'lkmc_assert_memcmp_fail.S': {'receives_signal': signal.Signals.SIGABRT}, 'udf.S': { - 'exit_status': 132, - 'receives_signal': True + 'receives_signal': signal.Signals.SIGILL }, } ), - 'fail.S': {'exit_status': 1}, + 'lkmc_assert_fail.S': { + 'receives_signal': signal.Signals.SIGABRT, + }, 'x86_64': ( {'allowed_archs': {'x86_64'}}, { @@ -353,31 +363,44 @@ path_properties_tuples = ( { 'freestanding': freestanding_properties, 'ring0.c': { - 'exit_status': 139, - 'receives_signal': True + 'receives_signal': signal.Signals.SIGSEGV } } ), 'freestanding': freestanding_properties, - 'lkmc_assert_eq_fail.S': {'exit_status': 1}, - 'lkmc_assert_memcmp_fail.S': {'exit_status': 1}, + 'lkmc_assert_eq_fail.S': {'receives_signal': signal.Signals.SIGABRT}, + 'lkmc_assert_memcmp_fail.S': {'receives_signal': signal.Signals.SIGABRT}, } ), } ), 'c': ( - {}, { - 'assert_fail.c': { - 'exit_status': 134, - 'receives_signal': True, + 'baremetal': True, + }, + { + 'abort.c': { + 'receives_signal': signal.Signals.SIGABRT, }, + 'assert_fail.c': { + 'receives_signal': signal.Signals.SIGABRT, + }, + 'exit1.c': {'exit_status': 1}, + 'exit2.c': {'exit_status': 2}, 'false.c': {'exit_status': 1}, 'getchar.c': {'interactive': True}, 'infinite_loop.c': {'more_than_1s': True}, + 'out_of_memory.c': {'disrupts_system': True}, + 'return1.c': {'exit_status': 1}, + 'return2.c': {'exit_status': 2}, + } + ), + 'gcc': ( + gnu_extension_properties, + { + 'openmp.c': {'cc_flags': ['-fopenmp', LF]}, } ), - 'gcc': gnu_extension_properties, 'kernel_modules': {**gnu_extension_properties, **{'requires_kernel_modules': True}}, 'libs': ( {'requires_dynamic_library': True}, @@ -398,8 +421,9 @@ path_properties_tuples = ( 'proc_events.c': {'requires_sudo': True}, 'sched_getaffinity.c': {'requires_syscall_getcpu': True}, 'sched_getaffinity_threads.c': { - 'requires_syscall_getcpu': True, + 'cc_flags_after': ['-pthread', LF], 'more_than_1s': True, + 'requires_syscall_getcpu': True, }, 'time_boot.c': {'requires_sudo': True}, 'virt_to_phys_user.c': {'requires_argument': True}, diff --git a/test-baremetal b/test-baremetal index f5224b7..49c801b 100755 --- a/test-baremetal +++ b/test-baremetal @@ -11,6 +11,7 @@ import thread_pool class Main(common.TestCliFunction): def __init__(self): super().__init__( + is_baremetal=True, supported_archs=common.consts['crosstool_ng_supported_archs'], ) self.add_argument( @@ -23,7 +24,10 @@ If given, run only the given tests. Otherwise, run all tests. def setup_one(self): self.env['tests'] = self.resolve_targets( - self.env['baremetal_source_dir'], + [ + self.env['baremetal_source_dir'], + self.env['userland_source_dir'] + ], self.env['tests'] ) @@ -45,7 +49,7 @@ If given, run only the given tests. Otherwise, run all tests. if os.path.splitext(in_filename)[1] in (self.env['c_ext'], self.env['asm_ext']): path_relative_root = os.path.join(dirpath_relative_root, in_filename) my_path_properties = path_properties.get(path_relative_root) - if my_path_properties.should_be_tested(self.env): + if my_path_properties.should_be_tested(self.env, is_baremetal=True): cur_run_args = run_args.copy() cur_run_args.update({ 'baremetal': os.path.relpath(os.path.join(path_abs, in_filename), os.getcwd()), diff --git a/test-user-mode b/test-user-mode index 912b5fa..e5ff755 100755 --- a/test-user-mode +++ b/test-user-mode @@ -28,7 +28,7 @@ If given, run only the given tests. Otherwise, run all tests. def setup_one(self): self.env['tests'] = self.resolve_targets( - self.env['userland_source_dir'], + [self.env['userland_source_dir']], self.env['tests'] ) @@ -53,7 +53,7 @@ If given, run only the given tests. Otherwise, run all tests. if os.path.splitext(in_filename)[1] in self.env['build_in_exts']: path_relative_root = os.path.join(dirpath_relative_root, in_filename) my_path_properties = path_properties.get(path_relative_root) - if my_path_properties.should_be_tested(self.env): + if my_path_properties.should_be_tested(self.env, is_userland=True): cur_run_args = run_args.copy() cur_run_args.update({ 'userland': os.path.relpath(os.path.join(path_abs, in_filename), os.getcwd()), @@ -65,8 +65,9 @@ If given, run only the given tests. Otherwise, run all tests. 'run_obj': lkmc.import_path.import_path_main('run'), 'test_id': path_relative_root, } - if my_path_properties['receives_signal']: - run_test_args['expected_exit_status'] = 128 - my_path_properties['exit_status'] + signal = my_path_properties['receives_signal'] + if signal is not None: + run_test_args['expected_exit_status'] = -signal.value my_thread_pool.submit(run_test_args) return self._handle_thread_pool_errors(my_thread_pool) diff --git a/userland/arch/aarch64/fadd_scalar.S b/userland/arch/aarch64/fadd_scalar.S index 31345b4..a5bb610 100644 --- a/userland/arch/aarch64/fadd_scalar.S +++ b/userland/arch/aarch64/fadd_scalar.S @@ -18,6 +18,12 @@ LKMC_PROLOGUE /* Now with a memory stored value. */ .data + /* TODO why is this align required, and why only the in the baremetal toolchain? + * Otherwise at edfbe9f0d7e9cc40cffd1c68c7c7c30a47fda2a8 + 1 was failing with: + * /path/to/linux-kernel-module-cheat/userland/arch/aarch64/fadd_scalar.S:28:(.text+0x3c): relocation truncated to fit: R_AARCH64_LD_PREL_LO19 against `.data' + * /path/to/linux-kernel-module-cheat/userland/arch/aarch64/fadd_scalar.S:28: warning: One possible cause of this error is that the symbol is being referenced in the indicated code as if it had a larger alignment than was declared where it was defined. + */ +.align 4 my_double_0: .double 1.5 my_double_1: diff --git a/userland/arch/aarch64/pc.S b/userland/arch/aarch64/pc.S index bcd308e..ffd5397 100644 --- a/userland/arch/aarch64/pc.S +++ b/userland/arch/aarch64/pc.S @@ -74,5 +74,6 @@ pc_relative_ldr: LKMC_ASSERT_EQ(x0, =0x123456789ABCDEF0) LKMC_EPILOGUE .data +.align 4 pc_relative_str: .quad 0x0000000000000000 diff --git a/userland/arch/fail.S b/userland/arch/lkmc_assert_fail.S similarity index 100% rename from userland/arch/fail.S rename to userland/arch/lkmc_assert_fail.S diff --git a/userland/c/abort.c b/userland/c/abort.c new file mode 100644 index 0000000..25128ea --- /dev/null +++ b/userland/c/abort.c @@ -0,0 +1,34 @@ +/* # abort + * + * Raise a SIGABRT, an ANSI C signal which by default kills the program. + * + * .... + * man abort + * .... + * + * Bibliography: + * + * * http://stackoverflow.com/questions/397075/what-is-the-difference-between-exit-and-abort + * * http://stackoverflow.com/questions/3676221/when-abort-is-preferred-over-exit + * + * Differences from exit: does not run regular program teardown: + * + * * does not call `atexit` function. + * * does not call C++ destructors + * + * `assert()` exits the program with abort. + */ + +#include +#include + +void atexit_func() { + puts("atexit"); +} + +int main(void) { + /* Will not get called. */ + atexit(atexit_func); + abort(); + return EXIT_SUCCESS; +} diff --git a/userland/c/add.c b/userland/c/add.c deleted file mode 120000 index 46def32..0000000 --- a/userland/c/add.c +++ /dev/null @@ -1 +0,0 @@ -../../lkmc/c/add.c \ No newline at end of file diff --git a/userland/c/add.c b/userland/c/add.c new file mode 100644 index 0000000..6b3606c --- /dev/null +++ b/userland/c/add.c @@ -0,0 +1,13 @@ +#include + +int main(void) { + int i, j, k; + i = 1; + /* test-gdb-op1 */ + j = 2; + /* test-gdb-op2 */ + k = i + j; + /* test-gdb-result */ + if (k != 3) + assert(0); +} diff --git a/userland/c/add.py b/userland/c/add.py deleted file mode 120000 index fb5788a..0000000 --- a/userland/c/add.py +++ /dev/null @@ -1 +0,0 @@ -../../lkmc/c/add.py \ No newline at end of file diff --git a/userland/c/add.py b/userland/c/add.py new file mode 100644 index 0000000..0f42d2b --- /dev/null +++ b/userland/c/add.py @@ -0,0 +1,9 @@ +def test(self): + self.sendline('tbreak main') + self.sendline('continue') + self.continue_to('op1') + assert self.get_int('i') == 1 + self.continue_to('op2') + assert self.get_int('j') == 2 + self.continue_to('result') + assert self.get_int('k') == 3 diff --git a/userland/c/assert_fail.c b/userland/c/assert_fail.c deleted file mode 120000 index 904602c..0000000 --- a/userland/c/assert_fail.c +++ /dev/null @@ -1 +0,0 @@ -../../lkmc/c/assert_fail.c \ No newline at end of file diff --git a/userland/c/assert_fail.c b/userland/c/assert_fail.c new file mode 100644 index 0000000..cc881b6 --- /dev/null +++ b/userland/c/assert_fail.c @@ -0,0 +1,18 @@ +/* Let's see what happens when an assert fails. + * + * Outcome on Ubuntu 19.04 shows the failure line: + * + * assert_fail.out: /path/to/linux-kernel-module-cheat/userland/c/assert_fail.c:15: main: Assertion `0' failed. + * + * and exit status 134 == 128 + 6, which corresponds to SIGABORT (6). + */ + +#include +#include +#include + +int main(void) { + assert(0); + puts("here"); + return EXIT_SUCCESS; +} diff --git a/baremetal/exit0.c b/userland/c/exit0.c similarity index 100% rename from baremetal/exit0.c rename to userland/c/exit0.c diff --git a/baremetal/exit1.c b/userland/c/exit1.c similarity index 100% rename from baremetal/exit1.c rename to userland/c/exit1.c diff --git a/userland/c/exit2.c b/userland/c/exit2.c new file mode 100644 index 0000000..04348fc --- /dev/null +++ b/userland/c/exit2.c @@ -0,0 +1,7 @@ +/* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */ + +#include + +int main(void) { + exit(2); +} diff --git a/userland/c/getchar.c b/userland/c/getchar.c deleted file mode 120000 index 8216384..0000000 --- a/userland/c/getchar.c +++ /dev/null @@ -1 +0,0 @@ -../../lkmc/c/getchar.c \ No newline at end of file diff --git a/userland/c/getchar.c b/userland/c/getchar.c new file mode 100644 index 0000000..2430e29 --- /dev/null +++ b/userland/c/getchar.c @@ -0,0 +1,21 @@ +/* Get on character from stdin, and then print it back out. + * + * Same as getc(stdin). + * + * You have to press enter for the character to go through: + * https://stackoverflow.com/questions/1798511/how-to-avoid-pressing-enter-with-getchar + * + * Used at: + * https://stackoverflow.com/questions/556405/what-do-real-user-and-sys-mean-in-the-output-of-time1/53937376#53937376 + */ + +#include +#include + +int main(void) { + char c; + printf("enter a character: "); + c = getchar(); + printf("you entered: %c\n", c); + return EXIT_SUCCESS; +} diff --git a/userland/c/hello.c b/userland/c/hello.c deleted file mode 120000 index 3f0a531..0000000 --- a/userland/c/hello.c +++ /dev/null @@ -1 +0,0 @@ -../../lkmc/c/hello.c \ No newline at end of file diff --git a/userland/c/hello.c b/userland/c/hello.c new file mode 100644 index 0000000..42cb55d --- /dev/null +++ b/userland/c/hello.c @@ -0,0 +1,9 @@ +/* Print hello to stdout ;-) */ + +#include +#include + +int main(void) { + puts("hello"); + return EXIT_SUCCESS; +} diff --git a/userland/c/infinite_loop.c b/userland/c/infinite_loop.c deleted file mode 120000 index faa4cdd..0000000 --- a/userland/c/infinite_loop.c +++ /dev/null @@ -1 +0,0 @@ -../../lkmc/c/infinite_loop.c \ No newline at end of file diff --git a/userland/c/infinite_loop.c b/userland/c/infinite_loop.c new file mode 100644 index 0000000..2ae905f --- /dev/null +++ b/userland/c/infinite_loop.c @@ -0,0 +1,29 @@ +/* Loop infinitely. Print an integer whenever a period is reached: + * + * .... + * ./infinite_loop [period] + * .... + */ + +#include +#include +#include +#include + +int main(int argc, char **argv) { + uintmax_t i, j, period; + if (argc > 1) { + period = strtoumax(argv[1], NULL, 10); + } else { + period = 100000000; + } + i = 0; + j = 0; + while (1) { + i++; + if (i % period == 0) { + printf("%ju\n", j); + j++; + } + } +} diff --git a/baremetal/out_of_memory.c b/userland/c/out_of_memory.c similarity index 51% rename from baremetal/out_of_memory.c rename to userland/c/out_of_memory.c index b7737a3..3bbc362 100644 --- a/baremetal/out_of_memory.c +++ b/userland/c/out_of_memory.c @@ -1,18 +1,18 @@ -/* Test out of memory. */ +/* Let's see how much memory Linux lets us allocate. */ #include #include int main(void) { char *ptr = NULL; - size_t alloc_size = 1; + size_t size = 1; while (1) { - ptr = realloc(ptr, alloc_size); + printf("0x%zx\n", size); + ptr = realloc(ptr, size); if (ptr == NULL) { - puts("out of memory"); break; } else { - alloc_size <<= 1; + size <<= 1; } } } diff --git a/userland/c/return0.c b/userland/c/return0.c new file mode 100644 index 0000000..9062ca1 --- /dev/null +++ b/userland/c/return0.c @@ -0,0 +1,6 @@ +/* false.c is a superset of this, this is mainly a sanity check for + * baremetal where we don't have CLI arguments yet: + * https://github.com/cirosantilli/linux-kernel-module-cheat/issues/67 + */ + +int main(void) { return 0; } diff --git a/baremetal/return1.c b/userland/c/return1.c similarity index 100% rename from baremetal/return1.c rename to userland/c/return1.c diff --git a/baremetal/return2.c b/userland/c/return2.c similarity index 100% rename from baremetal/return2.c rename to userland/c/return2.c diff --git a/userland/c/stderr.c b/userland/c/stderr.c deleted file mode 120000 index 1344887..0000000 --- a/userland/c/stderr.c +++ /dev/null @@ -1 +0,0 @@ -../../lkmc/c/stderr.c \ No newline at end of file diff --git a/userland/c/stderr.c b/userland/c/stderr.c new file mode 100644 index 0000000..b43d067 --- /dev/null +++ b/userland/c/stderr.c @@ -0,0 +1,7 @@ +/* Print hello to stderr. */ + +#include + +int main(void) { + fputs("hello\n", stderr); +}