Hello, I have applied the whole series, thanks!
It however seems that the i386 case is still not running for me, I don't know why. Using qemu-system-x86_64 instead of qemu-system-i386 works fine, however. I'm using debian's qemu-system-x86 1:8.1.2+ds-1 Samuel Luca Dariz, le jeu. 11 janv. 2024 22:08:57 +0100, a ecrit: > * configure.ac: move test fragment to have USER32 > * tests/Makefrag.am: add user tests > * tests/README: add basic info on how to run and debug user tests > * tests/configfrag.ac: allow the test compiler/flags to be > autoconfigured or customized > * tests/grub.cfg.single.template: add minimal grub config to boot a > module > * tests/include/device/cons.h: add a simplified version of > device/cons.h usable for tests > * tests/include/kern/printf.h: symlink to kern/printf.h > * tests/include/mach/mig_support.h: add basic version for user-space > tests > * tests/include/syscalls.h: add prototypes for syscalls used in tests. > * tests/include/testlib.h: add definitions for common test > functionalities > * tests/include/util/atoi.h: symlink to util/atoi.h > * tests/run-qemu.sh.template: add a simple qemu test runner > * tests/start.S: add arch-specific entry point > * tests/syscalls.S: generate syscalls entry points > * tests/test-hello.c: add basic smoke test > * tests/testlib.c: add the minimal functionality to run a user-space > executable and reboot the system, and some test helpers. > * tests/user-qemu.mk: add rules to build simple user-space test > modules, including generating mig stubs. The tests reuse some kernel > code (like printf(), mach_atoi(), mem*(), str*() functions) so we can > use the freestanding environment and not depend on glibc. > --- > configure.ac | 6 +- > tests/Makefrag.am | 11 +- > tests/README | 37 ++++++ > tests/configfrag.ac | 16 +++ > tests/grub.cfg.single.template | 4 + > tests/include/device/cons.h | 27 ++++ > tests/include/kern/printf.h | 1 + > tests/include/mach/mig_support.h | 71 +++++++++++ > tests/include/syscalls.h | 83 ++++++++++++ > tests/include/testlib.h | 74 +++++++++++ > tests/include/util/atoi.h | 1 + > tests/run-qemu.sh.template | 38 ++++++ > tests/start.S | 28 ++++ > tests/syscalls.S | 4 + > tests/test-hello.c | 26 ++++ > tests/testlib.c | 114 +++++++++++++++++ > tests/user-qemu.mk | 212 +++++++++++++++++++++++++++++++ > 17 files changed, 748 insertions(+), 5 deletions(-) > create mode 100644 tests/README > create mode 100644 tests/grub.cfg.single.template > create mode 100644 tests/include/device/cons.h > create mode 120000 tests/include/kern/printf.h > create mode 100644 tests/include/mach/mig_support.h > create mode 100644 tests/include/syscalls.h > create mode 100644 tests/include/testlib.h > create mode 120000 tests/include/util/atoi.h > create mode 100644 tests/run-qemu.sh.template > create mode 100644 tests/start.S > create mode 100644 tests/syscalls.S > create mode 100644 tests/test-hello.c > create mode 100644 tests/testlib.c > create mode 100644 tests/user-qemu.mk > > diff --git a/configure.ac b/configure.ac > index cadc33b6..69f75cf2 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -123,9 +123,6 @@ AC_CHECK_PROG([PATCH], [patch], [patch], > [patch-not-found]) > # configure fragments. > # > > -# The test suite. > -m4_include([tests/configfrag.ac]) > - > # Default set of device drivers. > AC_ARG_ENABLE([device-drivers], > AS_HELP_STRING([--enable-device-drivers=WHICH], [specify WHICH (on > `ix86-at' > @@ -181,6 +178,9 @@ m4_include([configfrag.ac]) > > # Linux code snarfed into GNU Mach. > m4_include([linux/configfrag.ac]) > + > +# The test suite. > +m4_include([tests/configfrag.ac]) > > # > # Compiler features. > diff --git a/tests/Makefrag.am b/tests/Makefrag.am > index 2723f64a..88c7fe8c 100644 > --- a/tests/Makefrag.am > +++ b/tests/Makefrag.am > @@ -21,6 +21,13 @@ > # > > if !PLATFORM_xen > + > +include tests/user-qemu.mk > + > TESTS += \ > - tests/test-multiboot > -endif > + tests/test-multiboot \ > + $(USER_TESTS) > + > +clean-local: $(USER_TESTS_CLEAN) > + > +endif # !PLATFORM_xen > diff --git a/tests/README b/tests/README > new file mode 100644 > index 00000000..3dacc184 > --- /dev/null > +++ b/tests/README > @@ -0,0 +1,37 @@ > + > +There are some basic tests that can be run qith qemu. You can run all the > tests with > + > + $ make check > + > +or selectively with: > + > + $ make run-hello > + > +Also, you can debug the existing tests, or a new one, by starting on one > shell > + > + $ make debug-hello > + > +and on another shell you can attach with gdb, load the symbols of the > +bootstrap module and break on its _start(): > + > + $ gdb gnumach > + ... > + (gdb) target remote :1234 > + ... > + (gdb) b setup_main > + Breakpoint 11 at 0xffffffff81019d60: file ../kern/startup.c, line 98. > + (gdb) c > + Continuing. > + > + Breakpoint 11, setup_main () at ../kern/startup.c:98 > + 98 cninit(); > + (gdb) add-symbol-file ../gnumach/build-64/module-task > + Reading symbols from ../gnumach/build-64/module-task... > + (gdb) b _start > + Breakpoint 12 at 0x40324a: _start. (2 locations) > + (gdb) c > + Continuing. > + > + Breakpoint 12, _start () at ../tests/testlib.c:96 > + 96 { > + (gdb) > diff --git a/tests/configfrag.ac b/tests/configfrag.ac > index 55c6da62..de87cbad 100644 > --- a/tests/configfrag.ac > +++ b/tests/configfrag.ac > @@ -22,6 +22,22 @@ dnl 51 Franklin Street, Fifth Floor, Boston, MA > 02110-1301, USA. > > AC_CONFIG_FILES([tests/test-multiboot], [chmod +x tests/test-multiboot]) > > + > +[if test x"$enable_user32" = xyes ; then > + ac_miguser=$user32_cpu-gnu-mig > +else > + ac_miguser=$host_cpu-gnu-mig > +fi] > + > +AC_CHECK_PROG([USER_MIG], [$ac_miguser], [$ac_miguser]) > +AC_ARG_VAR([USER_MIG], [Path to the mig tool for user-space tests]) > +AC_CHECK_PROG([USER_CC], [$CC], [$CC], [none]) > +AC_ARG_VAR([USER_CC], [C compiler command for user-space tests]) > +AC_CHECK_PROG([USER_CPP], [$CPP], [$CPP], [none]) > +AC_ARG_VAR([USER_CPP], [C preprocessor for user-space tests]) > +AC_ARG_VAR([USER_CFLAGS], [C compiler flags for user-space tests]) > +AC_ARG_VAR([USER_CPPFLAGS], [C preprocessor flags for user-space tests]) > + > dnl Local Variables: > dnl mode: autoconf > dnl End: > diff --git a/tests/grub.cfg.single.template b/tests/grub.cfg.single.template > new file mode 100644 > index 00000000..4432be3e > --- /dev/null > +++ b/tests/grub.cfg.single.template > @@ -0,0 +1,4 @@ > +echo TEST_START_MARKER > +multiboot /boot/gnumach GNUMACHARGS > +module /boot/BOOTMODULE BOOTMODULE '${host-port}' '${device-port}' > '$(task-create)' '$(task-resume)' > +boot > diff --git a/tests/include/device/cons.h b/tests/include/device/cons.h > new file mode 100644 > index 00000000..f4d8fe16 > --- /dev/null > +++ b/tests/include/device/cons.h > @@ -0,0 +1,27 @@ > +/* > + * Copyright (C) 2024 Free Software Foundation > + * > + * This program is free software ; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation ; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY ; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with the program ; if not, write to the Free Software > + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. > + */ > + > +#ifndef CONS_H > +#define CONS_H > + > +#include <mach/machine/vm_types.h> > + > +void cnputc(char c, vm_offset_t cookie); > +static inline int cngetc() { return 0; } > + > +#endif /* CONS_H */ > diff --git a/tests/include/kern/printf.h b/tests/include/kern/printf.h > new file mode 120000 > index 00000000..c61f3e0e > --- /dev/null > +++ b/tests/include/kern/printf.h > @@ -0,0 +1 @@ > +../../../kern/printf.h > \ No newline at end of file > diff --git a/tests/include/mach/mig_support.h > b/tests/include/mach/mig_support.h > new file mode 100644 > index 00000000..7006ae16 > --- /dev/null > +++ b/tests/include/mach/mig_support.h > @@ -0,0 +1,71 @@ > +/* > + * Mach Operating System > + * Copyright (c) 1992 Carnegie Mellon University > + * All Rights Reserved. > + * > + * Permission to use, copy, modify and distribute this software and its > + * documentation is hereby granted, provided that both the copyright > + * notice and this permission notice appear in all copies of the > + * software, derivative works or modified versions, and any portions > + * thereof, and that both notices appear in supporting documentation. > + * > + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" > + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR > + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. > + * > + * Carnegie Mellon requests users of this software to return to > + * > + * Software Distribution Coordinator or software.distribut...@cs.cmu.edu > + * School of Computer Science > + * Carnegie Mellon University > + * Pittsburgh PA 15213-3890 > + * > + * any improvements or extensions that they make and grant Carnegie Mellon > + * the rights to redistribute these changes. > + */ > +/* > + * Abstract: > + * MIG helpers for gnumach tests, mainly copied from glibc > + * > + */ > + > +#ifndef _MACH_MIG_SUPPORT_H_ > +#define _MACH_MIG_SUPPORT_H_ > + > +#include <string.h> > + > +#include <mach/message.h> > +#include <mach/mach_types.h> > + > +#include <syscalls.h> > + > +static inline void mig_init(void *_first) > +{} > + > +static inline void mig_allocate(vm_address_t *addr, vm_size_t size) > +{ > + if (syscall_vm_allocate(mach_task_self(), addr, size, 1) != KERN_SUCCESS) > + *addr = 0; > +} > +static inline void mig_deallocate(vm_address_t addr, vm_size_t size) > +{ > + syscall_vm_deallocate (mach_task_self(), addr, size); > +} > +static inline void mig_dealloc_reply_port(mach_port_t port) > +{} > +static inline void mig_put_reply_port(mach_port_t port) > +{} > +static inline mach_port_t mig_get_reply_port(void) > +{ > + return mach_reply_port(); > +} > +static inline void mig_reply_setup(const mach_msg_header_t *_request, > + mach_msg_header_t *reply) > +{} > + > +static inline vm_size_t mig_strncpy (char *dst, const char *src, vm_size_t > len) > +{ > + return dst - strncpy(dst, src, len); > +} > + > +#endif /* not defined(_MACH_MIG_SUPPORT_H_) */ > diff --git a/tests/include/syscalls.h b/tests/include/syscalls.h > new file mode 100644 > index 00000000..053af79d > --- /dev/null > +++ b/tests/include/syscalls.h > @@ -0,0 +1,83 @@ > +/* > + * Mach Operating System > + * Copyright (c) 1992 Carnegie Mellon University > + * All Rights Reserved. > + * > + * Permission to use, copy, modify and distribute this software and its > + * documentation is hereby granted, provided that both the copyright > + * notice and this permission notice appear in all copies of the > + * software, derivative works or modified versions, and any portions > + * thereof, and that both notices appear in supporting documentation. > + * > + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" > + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR > + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. > + * > + * Carnegie Mellon requests users of this software to return to > + * > + * Software Distribution Coordinator or software.distribut...@cs.cmu.edu > + * School of Computer Science > + * Carnegie Mellon University > + * Pittsburgh PA 15213-3890 > + * > + * any improvements or extensions that they make and grant Carnegie Mellon > + * the rights to redistribute these changes. > + */ > +/* > + * Abstract: > + * Syscall functions > + * > + */ > + > +#ifndef _SYSCALLS_ > +#define _SYSCALLS_ > + > +#include <device/device_types.h> > +#include <mach/message.h> > + > +// TODO: there is probably a better way to define these > + > +#define MACH_SYSCALL0(syscallid, retval, name) \ > + retval name(void) __attribute__((naked)); > + > +#define MACH_SYSCALL1(syscallid, retval, name, arg1) \ > + retval name(arg1 a1) __attribute__((naked)); > + > +#define MACH_SYSCALL2(syscallid, retval, name, arg1, arg2) \ > + retval name(arg1 a1, arg2 a2) __attribute__((naked)); > + > +#define MACH_SYSCALL3(syscallid, retval, name, arg1, arg2, arg3) \ > + retval name(arg1 a1, arg2 a2, arg3 a3) __attribute__((naked)); > + > +#define MACH_SYSCALL4(syscallid, retval, name, arg1, arg2, arg3, arg4) \ > + retval name(arg1 a1, arg2 a2, arg3 a3, arg4 a4) __attribute__((naked)); > + > +#define MACH_SYSCALL6(syscallid, retval, name, arg1, arg2, arg3, arg4, arg5, > arg6) \ > + retval name(arg1 a1, arg2 a2, arg3 a3, arg4 a4, arg5 a5, arg6 a6) > __attribute__((naked)); > + > +#define MACH_SYSCALL7(syscallid, retval, name, arg1, arg2, arg3, arg4, arg5, > arg6, arg7) \ > + retval name(arg1 a1, arg2 a2, arg3 a3, arg4 a4, arg5 a5, arg6 a6, arg7 a7) > __attribute__((naked)); > + > +#define mach_msg mach_msg_trap > + > +MACH_SYSCALL0(26, mach_port_name_t, mach_reply_port) > +MACH_SYSCALL0(27, mach_port_name_t, mach_thread_self) > +MACH_SYSCALL0(28, mach_port_name_t, mach_task_self) > +MACH_SYSCALL0(29, mach_port_name_t, mach_host_self) > +MACH_SYSCALL1(30, void, mach_print, const char*) > +MACH_SYSCALL0(31, kern_return_t, invalid_syscall) > +MACH_SYSCALL4(65, kern_return_t, syscall_vm_allocate, mach_port_t, > vm_offset_t*, vm_size_t, boolean_t) > +MACH_SYSCALL3(66, kern_return_t, syscall_vm_deallocate, mach_port_t, > vm_offset_t, vm_size_t) > +MACH_SYSCALL3(72, kern_return_t, syscall_mach_port_allocate, mach_port_t, > mach_port_right_t, mach_port_t*) > +MACH_SYSCALL2(73, kern_return_t, syscall_mach_port_deallocate, mach_port_t, > mach_port_t) > + > +/* > + todo: swtch_pri swtch ... > + these seem obsolete: evc_wait > + evc_wait_clear syscall_device_writev_request > + syscall_device_write_request ... > + */ > +MACH_SYSCALL6(40, io_return_t, syscall_device_write_request, > mach_port_name_t, > + mach_port_name_t, dev_mode_t, recnum_t, vm_offset_t, vm_size_t) > + > +#endif /* SYSCALLS */ > diff --git a/tests/include/testlib.h b/tests/include/testlib.h > new file mode 100644 > index 00000000..e492f2f6 > --- /dev/null > +++ b/tests/include/testlib.h > @@ -0,0 +1,74 @@ > +/* > + * Copyright (C) 2024 Free Software Foundation > + * > + * This program is free software ; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation ; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY ; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with the program ; if not, write to the Free Software > + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. > + */ > + > +#ifndef TESTLIB_H > +#define TESTLIB_H > + > +// in freestanding we can only use a few standard headers > +// float.h iso646.h limits.h stdarg.h stdbool.h stddef.h stdint.h > + > +#include <stddef.h> > +#include <stdint.h> > +#include <stdarg.h> > +#include <stdbool.h> > + > +#include <string.h> // we shouldn't include this from gcc, but it seems to > be ok > + > +#include <mach/mach_types.h> > +#include <kern/printf.h> > +#include <util/atoi.h> > +#include <syscalls.h> > + > +#define ASSERT(cond, msg) do { \ > + if (!(cond)) \ > + { \ > + printf("%s: " #cond " failed: %s\n", \ > + TEST_FAILURE_MARKER, (msg)); \ > + halt(); \ > + } \ > + } while (0) > + > +#define ASSERT_RET(ret, msg) do { \ > + if ((ret) != KERN_SUCCESS) \ > + { \ > + printf("%s %s (0x%x): %s\n", \ > + TEST_FAILURE_MARKER, e2s((ret)), (ret), (msg)); \ > + halt(); \ > + } \ > + } while (0) > + > +#define FAILURE(msg) do { \ > + printf("%s: %s\n", TEST_FAILURE_MARKER, (msg)); \ > + halt(); \ > + } while (0) > + > + > +extern const char* TEST_SUCCESS_MARKER; > +extern const char* TEST_FAILURE_MARKER; > + > +const char* e2s(int err); > +const char* e2s_gnumach(int err); > +void halt(); > +int msleep(uint32_t timeout); > + > +mach_port_t host_priv(void); > +mach_port_t device_priv(void); > + > +int main(int argc, char *argv[], int envc, char *envp[]); > + > +#endif /* TESTLIB_H */ > diff --git a/tests/include/util/atoi.h b/tests/include/util/atoi.h > new file mode 120000 > index 00000000..c32c2582 > --- /dev/null > +++ b/tests/include/util/atoi.h > @@ -0,0 +1 @@ > +../../../util/atoi.h > \ No newline at end of file > diff --git a/tests/run-qemu.sh.template b/tests/run-qemu.sh.template > new file mode 100644 > index 00000000..aba8d68a > --- /dev/null > +++ b/tests/run-qemu.sh.template > @@ -0,0 +1,38 @@ > +#!/bin/sh > +# Copyright (C) 2024 Free Software Foundation > +# > +# This program is free software ; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation ; either version 2 of the License, or > +# (at your option) any later version. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY ; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with the program ; if not, write to the Free Software > +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. > + > +set -e > + > +cmd="QEMU_BIN QEMU_OPTS -cdrom tests/test-TESTNAME.iso" > +log="tests/test-TESTNAME.raw" > + > +echo "temp log $log" > +if which QEMU_BIN >/dev/null ; then > + if ! timeout -v --foreground --kill-after=3 15s $cmd \ > + | tee $log | sed -n "/TEST_START_MARKER/"',$p' ; then > + exit 10 # timeout > + fi > + if grep -qi 'TEST_FAILURE_MARKER' $log; then > + exit 99 # error marker found, test explicitely failed > + fi > + if ! grep -q 'TEST_SUCCESS_MARKER' $log; then > + exit 12 # missing reboot marker, maybe the kernel crashed > + fi > +else > + echo "skipping, QEMU_BIN not found" > + exit 77 > +fi > diff --git a/tests/start.S b/tests/start.S > new file mode 100644 > index 00000000..b795bfbd > --- /dev/null > +++ b/tests/start.S > @@ -0,0 +1,28 @@ > +/* > + * Copyright (C) 2024 Free Software Foundation > + * > + * This program is free software ; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation ; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY ; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with the program ; if not, write to the Free Software > + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. > + */ > + > + .global _start > +_start: > +#ifdef __i386__ > + pushl %esp > + call c_start > +#endif /* __i386__ */ > +#ifdef __x86_64__ > + movq %rsp,%rdi > + callq c_start > +#endif /* __x86_64__ */ > diff --git a/tests/syscalls.S b/tests/syscalls.S > new file mode 100644 > index 00000000..df9c9bc0 > --- /dev/null > +++ b/tests/syscalls.S > @@ -0,0 +1,4 @@ > + > + #include <mach/syscall_sw.h> > + > + kernel_trap(invalid_syscall,-31,0) > diff --git a/tests/test-hello.c b/tests/test-hello.c > new file mode 100644 > index 00000000..0d739c61 > --- /dev/null > +++ b/tests/test-hello.c > @@ -0,0 +1,26 @@ > +/* > + * Copyright (C) 2024 Free Software Foundation > + * > + * This program is free software ; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation ; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY ; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with the program ; if not, write to the Free Software > + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. > + */ > + > +#include <testlib.h> > + > +int main(int argc, char *argv[], int envc, char *envp[]) > +{ > + int ret = printf("hello!!\n"); > + ASSERT_RET(ret, "printf() should return 0 here"); > + return 0; > +} > diff --git a/tests/testlib.c b/tests/testlib.c > new file mode 100644 > index 00000000..2eaeb591 > --- /dev/null > +++ b/tests/testlib.c > @@ -0,0 +1,114 @@ > +/* > + * Copyright (C) 2024 Free Software Foundation > + * > + * This program is free software ; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation ; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY ; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with the program ; if not, write to the Free Software > + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. > + */ > + > +#include <testlib.h> > + > +#include <device/cons.h> > +#include <mach/kern_return.h> > +#include <mach/message.h> > +#include <mach/mig_errors.h> > +#include <mach/vm_param.h> > + > +#include <mach.user.h> > +#include <mach_host.user.h> > + > + > +static int argc = 0; > +static char *argv_unknown[] = {"unknown", "m1", "123", "456"}; > +static char **argv = argv_unknown; > +static char **envp = NULL; > +static int envc = 0; > + > +static mach_port_t host_priv_port = 1; > +static mach_port_t device_master_port = 2; > + > +void cnputc(char c, vm_offset_t cookie) > +{ > + char buf[2] = {c, 0}; > + mach_print(buf); > +} > + > +mach_port_t host_priv(void) > +{ > + return host_priv_port; > +} > + > +mach_port_t device_priv(void) > +{ > + return device_master_port; > +} > + > +void halt() > +{ > + int ret = host_reboot(host_priv_port, 0); > + ASSERT_RET(ret, "host_reboot() failed!"); > + while (1) > + ; > +} > + > +int msleep(uint32_t timeout) > +{ > + mach_port_t recv = mach_reply_port(); > + return mach_msg(NULL, MACH_RCV_MSG|MACH_RCV_TIMEOUT|MACH_RCV_INTERRUPT, > + 0, 0, recv, timeout, MACH_PORT_NULL); > +} > + > +const char* e2s(int err) > +{ > + const char* s = e2s_gnumach(err); > + if (s != NULL) > + return s; > + else > + switch (err) > + { > + default: return "unknown"; > + } > +} > + > +/* > + * Minimal _start() for test modules, we just take the arguments from the > + * kernel, call main() and reboot. As in glibc, we expect the argument > pointer > + * as a first asrgument. > + */ > +void __attribute__((used, retain)) > +c_start(void **argptr) > +{ > + intptr_t* argcptr = (intptr_t*)argptr; > + argc = argcptr[0]; > + argv = (char **) &argcptr[1]; > + envp = &argv[argc + 1]; > + envc = 0; > + > + while (envp[envc]) > + ++envc; > + > + mach_atoi(argv[1], &host_priv_port); > + mach_atoi(argv[2], &device_master_port); > + > + printf("started %s", argv[0]); > + for (int i=1; i<argc; i++) > + { > + printf(" %s", argv[i]); > + } > + printf("\n"); > + > + int ret = main(argc, argv, envc, envp); > + > + printf("%s: test %s exit code %x\n", TEST_SUCCESS_MARKER, argv[0], ret); > + halt(); > +} > diff --git a/tests/user-qemu.mk b/tests/user-qemu.mk > new file mode 100644 > index 00000000..78775938 > --- /dev/null > +++ b/tests/user-qemu.mk > @@ -0,0 +1,212 @@ > +# Copyright (C) 2024 Free Software Foundation > + > +# This program is free software ; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation ; either version 2 of the License, or > +# (at your option) any later version. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY ; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with the program ; if not, write to the Free Software > +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. > + > +# > +# MIG stubs generation for user-space tests > +# > + > +MACH_TESTINSTALL = $(builddir)/tests/include-mach > +MACH_TESTINCLUDE = $(MACH_TESTINSTALL)/$(prefix)/include > + > +MIGCOMUSER = $(USER_MIG) -n -cc cat - /dev/null > +MIG_OUTDIR = $(builddir)/tests/mig-out > +MIG_CPPFLAGS = -x c -nostdinc -I$(MACH_TESTINCLUDE) > + > +# FIXME: how can we reliably detect a change on any header and reinstall > them? > +$(MACH_TESTINSTALL): > + mkdir -p $@ > + $(MAKE) install-data DESTDIR=$@ > + > +prepare-test: $(MACH_TESTINSTALL) > + > +$(MIG_OUTDIR): > + mkdir -p $@ > + > +define generate_mig_client > +$(MIG_OUTDIR)/$(2).user.c: prepare-test $(MIG_OUTDIR) > $(MACH_TESTINCLUDE)/$(1)/$(2).defs > + $(USER_CPP) $(USER_CPPFLAGS) $(MIG_CPPFLAGS) \ > + -o $(MIG_OUTDIR)/$(2).user.defs \ > + $(MACH_TESTINCLUDE)/$(1)/$(2).defs > + $(MIGCOMUSER) $(MIGCOMFLAGS) $(MIGCOMUFLAGS) \ > + -user $(MIG_OUTDIR)/$(2).user.c \ > + -header $(MIG_OUTDIR)/$(2).user.h \ > + -list $(MIG_OUTDIR)/$(2).user.msgids \ > + < $(MIG_OUTDIR)/$(2).user.defs > +endef > + > +define generate_mig_server > +$(MIG_OUTDIR)/$(2).server.c: prepare-test $(MIG_OUTDIR) > $(srcdir)/include/$(1)/$(2).defs > + $(USER_CPP) $(USER_CPPFLAGS) $(MIG_CPPFLAGS) \ > + -o $(MIG_OUTDIR)/$(2).server.defs \ > + $(srcdir)/include/$(1)/$(2).defs > + $(MIGCOMUSER) $(MIGCOMFLAGS) $(MIGCOMUFLAGS) \ > + -server $(MIG_OUTDIR)/$(2).server.c \ > + -header $(MIG_OUTDIR)/$(2).server.h \ > + -list $(MIG_OUTDIR)/$(2).server.msgids \ > + < $(MIG_OUTDIR)/$(2).server.defs > +endef > + > +# These are all the IPC implemented in the kernel, both as a server or as a > client. > +# Files are sorted as in > +# find builddir/tests/include-mach/ -name *.defs | grep -v types | sort > +# eval->info for debug of generated rules > +$(eval $(call generate_mig_client,device,device)) > +$(eval $(call generate_mig_client,device,device_reply)) > +$(eval $(call generate_mig_client,device,device_request)) > +$(eval $(call generate_mig_client,mach_debug,mach_debug)) > +# default_pager.defs? > +$(eval $(call generate_mig_server,mach,exc)) > +# experimental.defs? > +$(eval $(call generate_mig_client,mach,gnumach)) > +$(eval $(call generate_mig_client,mach,mach4)) > +$(eval $(call generate_mig_client,mach,mach)) > +$(eval $(call generate_mig_client,mach,mach_host)) > +$(eval $(call generate_mig_client,mach,mach_port)) > +# memory_object{_default}.defs? > +# notify.defs? > +$(eval $(call generate_mig_server,mach,task_notify)) > +if HOST_ix86 > +$(eval $(call generate_mig_client,mach/i386,mach_i386)) > +endif > +if HOST_x86_64 > +$(eval $(call generate_mig_client,mach/x86_64,mach_i386)) > +endif > + > +# NOTE: keep in sync with the rules above > +MIG_GEN_CC = \ > + $(MIG_OUTDIR)/device.user.c \ > + $(MIG_OUTDIR)/device_reply.user.c \ > + $(MIG_OUTDIR)/device_request.user.c \ > + $(MIG_OUTDIR)/mach_debug.user.c \ > + $(MIG_OUTDIR)/exc.server.c \ > + $(MIG_OUTDIR)/gnumach.user.c \ > + $(MIG_OUTDIR)/mach4.user.c \ > + $(MIG_OUTDIR)/mach.user.c \ > + $(MIG_OUTDIR)/mach_host.user.c \ > + $(MIG_OUTDIR)/mach_port.user.c \ > + $(MIG_OUTDIR)/task_notify.server.c \ > + $(MIG_OUTDIR)/mach_i386.user.c > + > +# > +# compilation of user space tests and utilities > +# > + > +TEST_START_MARKER = booting-start-of-test > +TEST_SUCCESS_MARKER = gnumach-test-success-and-reboot > +TEST_FAILURE_MARKER = gnumach-test-failure > + > +TESTCFLAGS = -static -nostartfiles -nolibc \ > + -ffreestanding \ > + -ftrivial-auto-var-init=pattern \ > + -I$(srcdir)/tests/include \ > + -I$(MACH_TESTINCLUDE) \ > + -I$(MIG_OUTDIR) \ > + -ggdb3 \ > + -DMIG_EOPNOTSUPP > + > +SRC_TESTLIB= \ > + $(srcdir)/i386/i386/strings.c \ > + $(srcdir)/kern/printf.c \ > + $(srcdir)/kern/strings.c \ > + $(srcdir)/util/atoi.c \ > + $(srcdir)/tests/syscalls.S \ > + $(srcdir)/tests/start.S \ > + $(srcdir)/tests/testlib.c \ > + $(builddir)/tests/errlist.c \ > + $(MIG_GEN_CC) > + > +tests/errlist.c: $(addprefix $(srcdir)/include/mach/,message.h kern_return.h > mig_errors.h) > + echo "/* autogenerated file */" >$@ > + echo "#include <mach/message.h>" >>$@ > + echo "#include <mach/kern_return.h>" >>$@ > + echo "#include <mach/mig_errors.h>" >>$@ > + echo "#include <testlib.h>" >>$@ > + echo "#include <stddef.h>" >>$@ > + echo "const char* TEST_SUCCESS_MARKER = \"$(TEST_SUCCESS_MARKER)\";" > >>$@ > + echo "const char* TEST_FAILURE_MARKER = \"$(TEST_FAILURE_MARKER)\";" > >>$@ > + echo "const char* e2s_gnumach(int err) { switch (err) {" >>$@ > + grep "define[[:space:]]MIG" $(srcdir)/include/mach/mig_errors.h | \ > + awk '{printf " case %s: return \"%s\";\n", $$2, $$2}' >>$@ > + grep "define[[:space:]]KERN" $(srcdir)/include/mach/kern_return.h | \ > + awk '{printf " case %s: return \"%s\";\n", $$2, $$2}' >>$@ > + awk 'f;/MACH_MSG_SUCCESS/{f=1}' $(srcdir)/include/mach/message.h | \ > + grep "define[[:space:]]MACH" | \ > + awk '{printf " case %s: return \"%s\";\n", $$2, $$2}' >>$@ > + echo " default: return NULL;" >>$@ > + echo "}}" >>$@ > + > +tests/module-%: $(srcdir)/tests/test-%.c $(SRC_TESTLIB) $(MACH_TESTINSTALL) > + $(USER_CC) $(USER_CFLAGS) $(TESTCFLAGS) $< $(SRC_TESTLIB) -o $@ > + > +# > +# packaging of qemu bootable image and test runner > +# > + > +GNUMACH_ARGS = console=com0 > +QEMU_OPTS = -m 2048 -nographic -no-reboot -boot d > +QEMU_GDB_PORT ?= 1234 > + > +if HOST_ix86 > +QEMU_BIN = qemu-system-i386 > +QEMU_OPTS += -cpu pentium3-v1 > +endif > +if HOST_x86_64 > +QEMU_BIN = qemu-system-x86_64 > +QEMU_OPTS += -cpu core2duo-v1 > +endif > + > +tests/test-%.iso: tests/module-% gnumach > $(srcdir)/tests/grub.cfg.single.template > + rm -rf $(builddir)/tests/isofiles > + mkdir -p $(builddir)/tests/isofiles/boot/grub/ > + < $(srcdir)/tests/grub.cfg.single.template \ > + sed -e "s|BOOTMODULE|$(notdir $<)|g" \ > + -e "s/GNUMACHARGS/$(GNUMACH_ARGS)/g" \ > + -e "s/TEST_START_MARKER/$(TEST_START_MARKER)/g" \ > + >$(builddir)/tests/isofiles/boot/grub/grub.cfg > + cp gnumach $< $(builddir)/tests/isofiles/boot/ > + grub-mkrescue -o $@ $(builddir)/tests/isofiles > + > +tests/test-%: tests/test-%.iso $(srcdir)/tests/run-qemu.sh.template > + < $(srcdir)/tests/run-qemu.sh.template \ > + sed -e "s|TESTNAME|$(subst tests/test-,,$@)|g" \ > + -e "s/QEMU_OPTS/$(QEMU_OPTS)/g" \ > + -e "s/QEMU_BIN/$(QEMU_BIN)/g" \ > + -e "s/TEST_START_MARKER/$(TEST_START_MARKER)/g" \ > + -e "s/TEST_SUCCESS_MARKER/$(TEST_SUCCESS_MARKER)/g" \ > + -e "s/TEST_FAILURE_MARKER/$(TEST_FAILURE_MARKER)/g" \ > + >$@ > + chmod +x $@ > + > +clean-test-%: > + rm -f tests/test-$*.iso tests/module-$* tests/test-$** > + > + > +USER_TESTS := \ > + tests/test-hello > + > +USER_TESTS_CLEAN = $(subst tests/,clean-,$(USER_TESTS)) > + > +# > +# helpers for interactive test run and debug > +# > + > +run-%: tests/test-% > + $^ > + > +# don't reuse the launcher script as the timeout would kill the debug session > +debug-%: tests/test-%.iso > + $(QEMU_BIN) $(QEMU_OPTS) -cdrom $< -gdb tcp::$(QEMU_GDB_PORT) -S \ > + | sed -n "/$(TEST_START_MARKER)/"',$$p' > -- > 2.39.2 > > -- Samuel --- Pour une évaluation indépendante, transparente et rigoureuse ! Je soutiens la Commission d'Évaluation de l'Inria.