mirror of
https://github.com/cirosantilli/linux-kernel-module-cheat.git
synced 2026-01-29 04:54:27 +01:00
baremetal: get exit status working with on_exit :-)
This commit is contained in:
34
README.adoc
34
README.adoc
@@ -11916,7 +11916,7 @@ svc 0x00123456
|
|||||||
|
|
||||||
and we can see from the docs that `0x18` stands for the `SYS_EXIT` command.
|
and we can see from the docs that `0x18` stands for the `SYS_EXIT` command.
|
||||||
|
|
||||||
This is also how we implement the `exit(0)` system call in C for QEMU for link:baremetal/exit.c[] through the Newlib via the function `_exit` at link:baremetal/lib/kwargs['c'][].
|
This is also how we implement the `exit(0)` system call in C for QEMU for link:baremetal/exit0.c[] through the Newlib via the function `_exit` at link:baremetal/lib/kwargs['c'][].
|
||||||
|
|
||||||
Other magic operations we can do with semihosting besides exiting the on the host include:
|
Other magic operations we can do with semihosting besides exiting the on the host include:
|
||||||
|
|
||||||
@@ -11990,7 +11990,7 @@ sudo apt-get install gcc-arm-none-eabi qemu-system-arm
|
|||||||
|
|
||||||
However, there are as usual limitations to using prebuilts:
|
However, there are as usual limitations to using prebuilts:
|
||||||
|
|
||||||
* certain examples fail to build with the Ubuntu packaged toolchain. E.g.: link:baremetal/exit.c[] fails with:
|
* certain examples fail to build with the Ubuntu packaged toolchain. E.g.: link:baremetal/exit0.c[] fails with:
|
||||||
+
|
+
|
||||||
....
|
....
|
||||||
/usr/lib/gcc/arm-none-eabi/6.3.1/../../../arm-none-eabi/lib/libg.a(lib_a-fini.o): In function `__libc_fini_array':
|
/usr/lib/gcc/arm-none-eabi/6.3.1/../../../arm-none-eabi/lib/libg.a(lib_a-fini.o): In function `__libc_fini_array':
|
||||||
@@ -14001,20 +14001,42 @@ For other arch / emulator combinations, we know how to do it:
|
|||||||
* gem5: <<m5-fail>> works on all archs
|
* gem5: <<m5-fail>> works on all archs
|
||||||
* user mode: QEMU forwards exit status, gem5 we do some log parsing: <<gem5-syscall-emulation-exit-status>>
|
* user mode: QEMU forwards exit status, gem5 we do some log parsing: <<gem5-syscall-emulation-exit-status>>
|
||||||
|
|
||||||
For this reason, we just parse the terminal output for a magic failure string to check if tests failed.
|
Since we can't do it for QEMU arm, the only reliable solution is to just parse the guest serial output for a magic failure string to check if tests failed.
|
||||||
|
|
||||||
In order to cover all archs, our run scripts parse the serial output looking for a line line containing only exactly the magic regular expression:
|
Our run scripts parse the serial output looking for a line line containing only exactly the magic regular expression:
|
||||||
|
|
||||||
....
|
....
|
||||||
lkmc_exit_status_(\d+)
|
lkmc_exit_status_(\d+)
|
||||||
....
|
....
|
||||||
|
|
||||||
and then exit with the given regular expression.
|
and then exit with the given regular expression, e.g.:
|
||||||
|
|
||||||
|
....
|
||||||
|
./run --arch aarch64 baremetal
|
||||||
|
....
|
||||||
|
|
||||||
This magic output string is notably generated by:
|
This magic output string is notably generated by:
|
||||||
|
|
||||||
* the `exit()` baremetal function when `status != 1`. This is in turn called by failed assertions for example from `lkmc_assert_fail`, which is used by <<baremetal-tests>>
|
|
||||||
* link:rootfs_overlay/lkmc/test_fail.sh[], which is used by <<test-userland-in-full-system>>
|
* link:rootfs_overlay/lkmc/test_fail.sh[], which is used by <<test-userland-in-full-system>>
|
||||||
|
* the `exit()` baremetal function when `status != 1`.
|
||||||
|
+
|
||||||
|
Unfortunately the only way we found to set this up was with `on_exit`: link:https://github.com/cirosantilli/linux-kernel-module-cheat/issues/59[].
|
||||||
|
+
|
||||||
|
Trying to patch `_exit` directly fails since at that point some de-initialization has already happened which prevents the print.
|
||||||
|
+
|
||||||
|
So setup this `on_exit` automatically from all our <<baremetal-bootloaders>>, so it just works automatically for the examples that use the bootloaders: https://stackoverflow.com/questions/44097610/pass-parameter-to-atexit/49659697#49659697
|
||||||
|
+
|
||||||
|
The following examples end up testing that our setup is working:
|
||||||
|
+
|
||||||
|
* link:baremetal/lkmc_assert_fail.c[]
|
||||||
|
* link:baremetal/return1.c[]
|
||||||
|
* link:baremetal/return2.c[]
|
||||||
|
* link:baremetal/exit0.c[]
|
||||||
|
* link:baremetal/exit1.c[]
|
||||||
|
* link:baremetal/arch/arm/return1.S[]
|
||||||
|
* link:baremetal/arch/aarch64/return1.S[]
|
||||||
|
|
||||||
|
Beware that on Linux kernel simulations, you cannot even echo that string from userland, since userland stdout shows up on the serial.
|
||||||
|
|
||||||
==== Non-automated tests
|
==== Non-automated tests
|
||||||
|
|
||||||
|
|||||||
5
baremetal/arch/aarch64/return1.S
Normal file
5
baremetal/arch/aarch64/return1.S
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
/* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */
|
||||||
|
.global main
|
||||||
|
main:
|
||||||
|
mov x0, 1
|
||||||
|
ret
|
||||||
5
baremetal/arch/arm/return1.S
Normal file
5
baremetal/arch/arm/return1.S
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
/* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */
|
||||||
|
.global main
|
||||||
|
main:
|
||||||
|
mov r0, #1
|
||||||
|
bx lr
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
7
baremetal/exit0.c
Normal file
7
baremetal/exit0.c
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#include <stdio.h>
|
/* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
|
|||||||
@@ -14,6 +14,12 @@ mystart:
|
|||||||
/* Prepare the stack for main, mandatory for C code. */
|
/* Prepare the stack for main, mandatory for C code. */
|
||||||
ldr x0, =stack_top
|
ldr x0, =stack_top
|
||||||
mov sp, x0
|
mov sp, x0
|
||||||
|
|
||||||
|
/* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */
|
||||||
|
adr x0, lkmc_baremetal_on_exit_callback
|
||||||
|
bl on_exit
|
||||||
|
|
||||||
|
/* Run main. */
|
||||||
bl main
|
bl main
|
||||||
|
|
||||||
/* If main returns, exit. */
|
/* If main returns, exit. */
|
||||||
|
|||||||
@@ -1,5 +1,16 @@
|
|||||||
|
#include <lkmc.h>
|
||||||
|
|
||||||
.global mystart
|
.global mystart
|
||||||
mystart:
|
mystart:
|
||||||
|
/* Prepare the stack for main, mandatory for C code. */
|
||||||
ldr sp, =stack_top
|
ldr sp, =stack_top
|
||||||
|
|
||||||
|
/* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */
|
||||||
|
ldr r0, =lkmc_baremetal_on_exit_callback
|
||||||
|
bl on_exit
|
||||||
|
|
||||||
|
/* Run main. */
|
||||||
bl main
|
bl main
|
||||||
|
|
||||||
|
/* If main returns, exit. */
|
||||||
bl exit
|
bl exit
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
/* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */
|
||||||
int main(void) { return 1; }
|
int main(void) { return 1; }
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
/* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */
|
||||||
int main(void) { return 2; }
|
int main(void) { return 2; }
|
||||||
|
|||||||
9
lkmc.c
9
lkmc.c
@@ -12,11 +12,16 @@ void lkmc_assert(bool condition) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void lkmc_assert_fail(void) {
|
void lkmc_assert_fail(void) {
|
||||||
printf("%s\n", __func__);
|
|
||||||
puts("lkmc_exit_status_1");
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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__)
|
#if defined(__aarch64__)
|
||||||
#define LKMC_SYSREG_READ_WRITE(type, name) \
|
#define LKMC_SYSREG_READ_WRITE(type, name) \
|
||||||
type LKMC_CONCAT(LKMC_CONCAT(LKMC_SYSREG_SYMBOL_PREFIX, name), _read(void)) { \
|
type LKMC_CONCAT(LKMC_CONCAT(LKMC_SYSREG_SYMBOL_PREFIX, name), _read(void)) { \
|
||||||
|
|||||||
1
lkmc.h
1
lkmc.h
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
void lkmc_assert(bool);
|
void lkmc_assert(bool);
|
||||||
void lkmc_assert_fail();
|
void lkmc_assert_fail();
|
||||||
|
void lkmc_baremetal_on_exit_callback(int status, void *arg);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* https://stackoverflow.com/questions/1489932/how-to-concatenate-twice-with-the-c-preprocessor-and-expand-a-macro-as-in-arg */
|
/* https://stackoverflow.com/questions/1489932/how-to-concatenate-twice-with-the-c-preprocessor-and-expand-a-macro-as-in-arg */
|
||||||
|
|||||||
@@ -1,21 +1,5 @@
|
|||||||
#include <lkmc.h>
|
#include <lkmc.h>
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
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) {
|
int main(void) {
|
||||||
atexit(atexit_cb);
|
|
||||||
on_exit(onexit_cb, NULL);
|
|
||||||
lkmc_assert_fail();
|
lkmc_assert_fail();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,6 +220,7 @@ path_properties_tuples = (
|
|||||||
'semihost_exit.S': {'requires_semihosting': True},
|
'semihost_exit.S': {'requires_semihosting': True},
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
'return1.S': {'exit_status': 1},
|
||||||
'semihost_exit.S': {'requires_semihosting': True},
|
'semihost_exit.S': {'requires_semihosting': True},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -235,12 +236,13 @@ path_properties_tuples = (
|
|||||||
'semihost_exit.S': {'requires_semihosting': True},
|
'semihost_exit.S': {'requires_semihosting': True},
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
'return1.S': {'exit_status': 1},
|
||||||
'semihost_exit.S': {'requires_semihosting': True},
|
'semihost_exit.S': {'requires_semihosting': True},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
'assert_fail.c': {'exit_status': 1},
|
'lkmc_assert_fail.c': {'exit_status': 1},
|
||||||
'exit1.c': {'exit_status': 1},
|
'exit1.c': {'exit_status': 1},
|
||||||
'infinite_loop.c': {'more_than_1s': True},
|
'infinite_loop.c': {'more_than_1s': True},
|
||||||
'lib': (
|
'lib': (
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ If given, run only the given tests. Otherwise, run all tests.
|
|||||||
'run_obj': self.import_path_main('run'),
|
'run_obj': self.import_path_main('run'),
|
||||||
'test_id': path_relative_root,
|
'test_id': path_relative_root,
|
||||||
}
|
}
|
||||||
if path_relative_root != os.path.join('baremetal', 'assert_fail.c'):
|
# if path_relative_root != os.path.join('baremetal', 'lkmc_assert_fail.c'):
|
||||||
# Workaround fro: https://github.com/cirosantilli/linux-kernel-module-cheat/issues/59
|
# Workaround fro: https://github.com/cirosantilli/linux-kernel-module-cheat/issues/59
|
||||||
test_args['expected_exit_status'] = 0
|
# test_args['expected_exit_status'] = 0
|
||||||
error = thread_pool.submit(test_args)
|
error = thread_pool.submit(test_args)
|
||||||
if error is not None:
|
if error is not None:
|
||||||
if self.env['quit_on_fail']:
|
if self.env['quit_on_fail']:
|
||||||
|
|||||||
Reference in New Issue
Block a user