diff --git a/README.adoc b/README.adoc index 80e1cbb..be674ba 100644 --- a/README.adoc +++ b/README.adoc @@ -9,7 +9,7 @@ :toclevels: 6 :toc-title: -The perfect emulation setup to study and modify the <>, kernel modules, <> and <>. Highly automated. Thoroughly documented. <> and <> just work. Automated <>. Powered by <>. "Tested" in Ubuntu 18.04 host, x86_64, ARMv7 and ARMv8 guests with kernel v5.0. +The perfect emulation setup to study and develop the <>, kernel modules, <> and <> and <> assembly. Highly automated. Thoroughly documented. <> and <> just work. Automated <>. Powered by <> and <>. "Tested" in Ubuntu 18.04 host, x86_64, ARMv7 and ARMv8 guests with kernel v5.0. TL;DR: <> @@ -3923,37 +3923,6 @@ Error while loading /path/to/linux-kernel-module-cheat/out/userland/default/x86_ Tested in de77c62c091f6418e73b64e8a0a19639c587a103 + 1. -=== User mode simulation path_properties - -In order to build and run each example properly, we need some file metadata which is stored in link:path_properties.py[] at `path_properties_tuples`. - -Maybe we should embed it magically into source files directories? But this was easier to implement so we started like this. - -The format is as follows: - -.... -'path_component': ( - {'property': value}, - { - 'child_path_component': - { - {'child_property': }, - {} - } - } -) -.... - -and as a shortcut, paths that don't have any children can be written directly as: - -..... -'path_component': {'property': value} -..... - -Properties of parent directories apply to all children. - -Lists coming from parent directories are extended instead of overwritten by children, this is especially useful for C compiler flags. - == Kernel module utilities === insmod @@ -13957,6 +13926,10 @@ You can then see which tests failed on the test summary report at the end. ===== Test userland in full system +TODO: we really need a mechanism to automatically generate the test list automatically e.g. based on <>, currently there are many tests missing, and we have to add everything manually which is very annoying. + +We could just generate it on the fly on the host, and forward it to guest through CLI arguments. + Run all userland tests from inside full system simulation (i.e. not <>): .... @@ -13971,8 +13944,6 @@ Failure is detected by looking for the <> Most userland programs that don't rely on kernel modules can also be tested in user mode simulation as explained at: <>. -TODO: we really need a mechanism to automatically generate the test list much like user-mode-tests, currently there are many tests missing, and we have to add everything manually which is very annoying. - ===== Test GDB We have some link:https://github.com/pexpect/pexpect[pexpect] automated tests for the baremetal programs! @@ -14115,6 +14086,40 @@ git submodule update TODO broken, fix: An example of Linux kernel commit bisection on gem5 boots can be found at: link:bisect-linux-boot-gem5[]. +[[path-properties]] +=== path_properties + +In order to build and run each userland and <> example properly, we need per-file metadata such as compiler flags and required number of cores. + +This data is stored is stored in link:path_properties.py[] at `path_properties_tuples`. + +Maybe we should embed it magically into source files directories to make it easier to see? But one big Python dict was easier to implement so we started like this. And it allows factoring chunks out easily. + +The format is as follows: + +.... +'path_component': ( + {'property': value}, + { + 'child_path_component': + { + {'child_property': }, + {} + } + } +) +.... + +and as a shortcut, paths that don't have any children can be written directly as: + +..... +'path_component': {'property': value} +..... + +Properties of parent directories apply to all children. + +Lists coming from parent directories are extended instead of overwritten by children, this is especially useful for C compiler flags. + === Update a forked submodule This is a template update procedure for submodules for which we have some patches on on top of mainline. diff --git a/baremetal/hello.c b/baremetal/hello.c deleted file mode 100644 index 20d437d..0000000 --- a/baremetal/hello.c +++ /dev/null @@ -1,6 +0,0 @@ -#include - -int main(void) { - puts("hello"); - return 0; -} diff --git a/baremetal/hello.c b/baremetal/hello.c new file mode 120000 index 0000000..d00921e --- /dev/null +++ b/baremetal/hello.c @@ -0,0 +1 @@ +../lkmc/hello.c \ No newline at end of file diff --git a/baremetal/lib/syscalls.c b/baremetal/lib/syscalls.c index 3203832..626176d 100644 --- a/baremetal/lib/syscalls.c +++ b/baremetal/lib/syscalls.c @@ -63,10 +63,6 @@ int _write(int file, char *ptr, int len) { } void _exit(int status) { - if (status != 0) { - /* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */ - printf("lkmc_exit_status_%d\n", status); - } #if defined(GEM5) LKMC_M5OPS_EXIT; #else diff --git a/build-baremetal b/build-baremetal index b32325a..879e43c 100755 --- a/build-baremetal +++ b/build-baremetal @@ -136,9 +136,14 @@ Build the baremetal examples with crosstool-NG. in_ext in (self.env['c_ext'], self.env['asm_ext']) ): out = os.path.join(out_dir, in_name + self.env['baremetal_build_ext']) - print(out) + src = os.path.join(self.env['baremetal_source_dir'], in_path) if self.need_rebuild( - common_objs_bootloader + [self.env['baremetal_link_script'] + self.env['common_h']], + common_objs_bootloader + + [ + src, + self.env['baremetal_link_script'], + self.env['common_h'] + ], out ): self.sh.run_cmd( @@ -150,7 +155,7 @@ Build the baremetal examples with crosstool-NG. '-T', self.env['baremetal_link_script'], LF, ] + [ - os.path.join(self.env['baremetal_source_dir'], in_path), LF, + src, LF, ] + self.sh.add_newlines(common_objs_bootloader) + cflags_after diff --git a/lkmc.c b/lkmc.c index 2ceac96..e25c72d 100644 --- a/lkmc.c +++ b/lkmc.c @@ -12,7 +12,8 @@ void lkmc_assert(bool condition) { } void lkmc_assert_fail(void) { - puts("lkmc_test_fail"); + printf("%s\n", __func__); + puts("lkmc_exit_status_1"); exit(1); } diff --git a/lkmc/assert_fail.c b/lkmc/assert_fail.c index 68fbefa..e2a3593 100644 --- a/lkmc/assert_fail.c +++ b/lkmc/assert_fail.c @@ -1,5 +1,21 @@ #include +#include + +void atexit_cb(void) { + puts("atexit"); +} + +void onexit_cb(int status, void *arg) { + printf("status %d\n", status); +} + +void __attribute__ ((constructor)) premain() { + printf("premain2()\n"); +} + int main(void) { + atexit(atexit_cb); + on_exit(onexit_cb, NULL); lkmc_assert_fail(); } diff --git a/lkmc/hello.c b/lkmc/hello.c new file mode 100644 index 0000000..42cb55d --- /dev/null +++ b/lkmc/hello.c @@ -0,0 +1,9 @@ +/* Print hello to stdout ;-) */ + +#include +#include + +int main(void) { + puts("hello"); + return EXIT_SUCCESS; +} diff --git a/path_properties.py b/path_properties.py index f77313f..80078e5 100644 --- a/path_properties.py +++ b/path_properties.py @@ -9,7 +9,6 @@ class PathProperties: default_cxx_std = 'c++17' default_properties = { 'allowed_archs': None, - 'allowed_emulators': None, 'c_std': default_c_std, 'cc_flags': [ '-Wall', LF, @@ -42,6 +41,11 @@ class PathProperties: 'receives_signal': False, # The script requires a non-trivial argument to be passed to run properly. 'requires_argument': False, + 'requires_dynamic_library': False, + 'requires_m5ops': False, + # gem5 fatal: syscall getcpu (#168) unimplemented. + 'requires_syscall_getcpu': False, + 'requires_semihosting': False, # Requires certain of our custom kernel modules to be inserted to run. 'requires_kernel_modules': False, # The example requires sudo, which usually implies that it can do something @@ -59,7 +63,6 @@ class PathProperties: 'show_time': False, 'background': True, }, - 'uses_dynamic_library': False, } ''' @@ -97,27 +100,37 @@ class PathProperties: self['allowed_archs'] is None or env['arch'] in self['allowed_archs'] ) and \ - ( - self['allowed_emulators'] is None or - env['emulator'] in self['allowed_emulators'] - ) and \ not ( link and self['no_executable'] ) def should_be_tested(self, env): - return \ - self.should_be_built(env) and \ - not self['interactive'] and \ - not self['more_than_1s'] and \ - not self['no_executable'] and \ - not self['receives_signal'] and \ - not self['requires_argument'] and \ - not self['requires_kernel_modules'] and \ - not self['requires_sudo'] and \ - not self['skip_run_unclassified'] and \ - not (self['uses_dynamic_library'] and env['emulator'] == 'gem5') + return ( + self.should_be_built(env) and + not self['interactive'] and + not self['more_than_1s'] and + not self['no_executable'] and + not self['receives_signal'] and + not self['requires_argument'] and + not self['requires_kernel_modules'] and + not self['requires_sudo'] and + not self['skip_run_unclassified'] and + not ( + env['emulator'] == 'gem5' and + ( + self['requires_dynamic_library'] or + self['requires_semihosting'] or + self['requires_syscall_getcpu'] + ) + ) and + not ( + env['emulator'] == 'qemu' and + ( + self['requires_m5ops'] + ) + ) + ) def update(self, other): other_tmp_properties = other.properties.copy() @@ -185,7 +198,7 @@ freestanding_properties = { ], 'extra_objs_userland_asm': False, } -# See: https://github.com/cirosantilli/linux-kernel-module-cheat#user-mode-simulation-path_properties +# See: https://github.com/cirosantilli/linux-kernel-module-cheat#path-properties path_properties_tuples = ( PathProperties.default_properties, { @@ -198,16 +211,16 @@ path_properties_tuples = ( 'arm': ( {'allowed_archs': {'arm'}}, { - 'gem5_assert.S': {'allowed_emulators': {'gem5'}}, + 'gem5_assert.S': {'requires_m5ops': True}, 'multicore.S': {'test_run_args': {'cpus': 2}}, 'no_bootloader': ( {'extra_objs_baremetal_bootloader': False}, { - 'gem5_exit.S': {'allowed_emulators': {'gem5'}}, - 'semihost_exit.S': {'allowed_emulators': {'qemu'}}, + 'gem5_exit.S': {'requires_m5ops': True}, + 'semihost_exit.S': {'requires_semihosting': True}, } ), - 'semihost_exit.S': {'allowed_emulators': {'qemu'}}, + 'semihost_exit.S': {'requires_semihosting': True}, }, ), @@ -218,11 +231,11 @@ path_properties_tuples = ( 'no_bootloader': ( {'extra_objs_baremetal_bootloader': False}, { - 'gem5_exit.S': {'allowed_emulators': {'gem5'}}, - 'semihost_exit.S': {'allowed_emulators': {'qemu'}}, + 'gem5_exit.S': {'requires_m5ops': True}, + 'semihost_exit.S': {'requires_semihosting': True}, } ), - 'semihost_exit.S': {'allowed_emulators': {'qemu'}}, + 'semihost_exit.S': {'requires_semihosting': True}, }, ) } @@ -343,7 +356,7 @@ path_properties_tuples = ( } ), 'libs': ( - {'uses_dynamic_library': True}, + {'requires_dynamic_library': True}, { 'libdrm': {'requires_sudo': True}, } @@ -359,7 +372,11 @@ path_properties_tuples = ( 'poweroff.c': {'requires_sudo': True}, 'proc_events.c': {'requires_sudo': True}, 'proc_events.c': {'requires_sudo': True}, - 'sched_getaffinity_threads.c': {'more_than_1s': True}, + 'sched_getaffinity.c': {'requires_syscall_getcpu': True}, + 'sched_getaffinity_threads.c': { + 'requires_syscall_getcpu': True, + 'more_than_1s': True, + }, 'time_boot.c': {'requires_sudo': True}, 'virt_to_phys_user.c': {'requires_argument': True}, } diff --git a/test-baremetal b/test-baremetal index 18fcea7..bed5c65 100755 --- a/test-baremetal +++ b/test-baremetal @@ -49,12 +49,16 @@ If given, run only the given tests. Otherwise, run all tests. 'baremetal': os.path.relpath(os.path.join(path_abs, in_filename), os.getcwd()), }) cur_run_args.update(my_path_properties['test_run_args']) - error = thread_pool.submit({ + test_args = { 'expected_exit_status': my_path_properties['exit_status'], 'run_args': cur_run_args, 'run_obj': self.import_path_main('run'), 'test_id': path_relative_root, - }) + } + if path_relative_root != os.path.join('baremetal', 'assert_fail.c'): + # Workaround fro: https://github.com/cirosantilli/linux-kernel-module-cheat/issues/59 + test_args['expected_exit_status'] = 0 + error = thread_pool.submit(test_args) if error is not None: if self.env['quit_on_fail']: raise common.ExitLoop() diff --git a/test-userland-full-system b/test-userland-full-system index 9178d2d..cc69376 100755 --- a/test-userland-full-system +++ b/test-userland-full-system @@ -15,7 +15,7 @@ https://github.com/cirosantilli/linux-kernel-module-cheat#test-userland-in-full- def timed_main(self): run = self.import_path_main('run') run_args = self.get_common_args() - run_args['eval_after'] = '/test_all.sh;{};'.format(self.env['userland_quit_cmd']) + run_args['eval_after'] = './test_all.sh;{};'.format(self.env['userland_quit_cmd']) self.run_test(run, run_args) if __name__ == '__main__': diff --git a/userland/c/hello.c b/userland/c/hello.c deleted file mode 100644 index 3b0fee6..0000000 --- a/userland/c/hello.c +++ /dev/null @@ -1,9 +0,0 @@ -/* https://github.com/cirosantilli/linux-kernel-module-cheat#sanity-checks */ - -#include -#include - -int main(void) { - puts("hello"); - return EXIT_SUCCESS; -} diff --git a/userland/c/hello.c b/userland/c/hello.c new file mode 120000 index 0000000..1a462a6 --- /dev/null +++ b/userland/c/hello.c @@ -0,0 +1 @@ +../../lkmc/hello.c \ No newline at end of file