From fbfc4905ec23170e0af227807512b183ab288a91 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: Thu, 23 May 2019 00:00:01 +0000 Subject: [PATCH] baremetal: build userland programs using userland_and_baremetal instead of symlinks Otherwise I'll go crazy with symlink action. --- README.adoc | 64 ++++++++++++++++++------------------- 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 | 1 + baremetal/lib/arm.S | 1 + build-baremetal | 6 +++- build-userland | 4 +-- common.py | 33 ++++++++++++------- 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 | 35 ++++++++++++++------ test-baremetal | 6 +++- test-user-mode | 2 +- userland/c/add.c | 14 +++++++- userland/c/add.py | 10 +++++- userland/c/assert_fail.c | 19 ++++++++++- userland/c/getchar.c | 22 ++++++++++++- userland/c/hello.c | 10 +++++- userland/c/infinite_loop.c | 30 ++++++++++++++++- userland/c/stderr.c | 8 ++++- 29 files changed, 200 insertions(+), 177 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 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 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 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 mode change 120000 => 100644 userland/c/stderr.c diff --git a/README.adoc b/README.adoc index 41a5759..f025125 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: 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..02686c9 100644 --- a/baremetal/lib/aarch64.S +++ b/baremetal/lib/aarch64.S @@ -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..1504812 100644 --- a/baremetal/lib/arm.S +++ b/baremetal/lib/arm.S @@ -10,6 +10,7 @@ mystart: bl on_exit /* Run main. */ + mov r0, 0 bl main /* If main returns, exit. */ 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..77ed78e 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: @@ -1346,13 +1352,13 @@ lunch aosp_{}-eng else: return in_path - def resolve_targets(self, source_dir, targets): + def resolve_targets(self, source_dirs, targets): 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) + target = self.toplevel_to_source_dir(target, source_dirs) + self.assert_is_subpath(target, source_dirs) new_targets.append(target) return new_targets @@ -1386,10 +1392,10 @@ lunch aosp_{}-eng ''' pass - def toplevel_to_source_dir(self, path, source_dir): + def toplevel_to_source_dir(self, path, source_dirs): path = os.path.abspath(path) if path == self.env['root_dir']: - return source_dir + return source_dirs else: return path @@ -1491,7 +1497,12 @@ 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: 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..04db838 100644 --- a/path_properties.py +++ b/path_properties.py @@ -9,6 +9,8 @@ class PathProperties: default_cxx_std = 'c++17' default_properties = { 'allowed_archs': None, + # Examples that can be built in baremetal. + 'baremetal': False, 'c_std': default_c_std, 'cc_flags': [ '-Wall', LF, @@ -68,6 +70,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 +97,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,6 +115,10 @@ class PathProperties: self['allowed_archs'] is None or env['arch'] in self['allowed_archs'] ) and \ + ( + ( is_userland and self['userland'] ) or + (not is_baremetal and self['baremetal']) + ) and \ not ( link and self['no_executable'] @@ -203,6 +217,7 @@ gnu_extension_properties = { 'cxx_std': 'gnu++17' } freestanding_properties = { + 'baremetal': False, 'cc_flags': [ '-ffreestanding', LF, '-nostdlib', LF, @@ -216,6 +231,7 @@ path_properties_tuples = ( { 'baremetal': ( { + 'baremetal': True, 'extra_objs_baremetal_bootloader': True, 'extra_objs_lkmc_common': True, }, @@ -257,13 +273,6 @@ 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}, @@ -271,6 +280,10 @@ path_properties_tuples = ( 'return2.c': {'exit_status': 2}, } ), + 'lkmc.c': { + 'baremetal': True, + 'userland': True, + } 'userland': ( { 'cc_flags': [ @@ -279,10 +292,12 @@ path_properties_tuples = ( 'cc_flags_after': [ '-pthread', LF, ], + 'userland': True, }, { 'arch': ( { + 'baremetal': True, 'extra_objs_lkmc_common': True, }, { @@ -366,7 +381,9 @@ path_properties_tuples = ( } ), 'c': ( - {}, + { + 'baremetal': True, + }, { 'assert_fail.c': { 'exit_status': 134, diff --git a/test-baremetal b/test-baremetal index f5224b7..c6dab04 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'] ) diff --git a/test-user-mode b/test-user-mode index 912b5fa..799c348 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'] ) 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/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/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); +}