* tests/Makefrag.am: add rules to build simple user-space test modules, including generating mig stubs. The tests reuse some kernel code (the printf(), mach_atoi(), mem*(), str*() functions) so we can use the freestanding environment and not depend on glibc. * tests/README: add basic info on how to run and debug tests * tests/configfrag.ac: add MIGUSER, required for user-space tests as it might be different from the one used to ubild the kernel. * 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: reuse 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: reuse util/atoi.h * tests/run-qemu.sh.template: add a simple qemu test runner * tests/syscalls.S: generate syscalls entry points * tests/test-hello.c: add basic smoke test * tests/testlib.c: add the imnimal funcitonality to run a user-space executable and reboot the system, and some test helpers. --- tests/Makefrag.am | 125 +++++++++++++++++++++++++++++++ tests/README | 37 +++++++++ tests/configfrag.ac | 10 +++ tests/grub.cfg.single.template | 3 + tests/include/device/cons.h | 10 +++ tests/include/kern/printf.h | 1 + tests/include/mach/mig_support.h | 71 ++++++++++++++++++ tests/include/syscalls.h | 83 ++++++++++++++++++++ tests/include/testlib.h | 46 ++++++++++++ tests/include/util/atoi.h | 1 + tests/run-qemu.sh.template | 22 ++++++ tests/syscalls.S | 4 + tests/test-hello.c | 9 +++ tests/testlib.c | 125 +++++++++++++++++++++++++++++++ 14 files changed, 547 insertions(+) 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/syscalls.S create mode 100644 tests/test-hello.c create mode 100644 tests/testlib.c
diff --git a/tests/Makefrag.am b/tests/Makefrag.am index 2723f64a..c16326e8 100644 --- a/tests/Makefrag.am +++ b/tests/Makefrag.am @@ -23,4 +23,129 @@ if !PLATFORM_xen TESTS += \ tests/test-multiboot + +# 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 $@ + +MACH_TESTINSTALL=$(builddir)/tests/include-mach +MACH_TESTINCLUDE=$(MACH_TESTINSTALL)/$(prefix)/include + +MIGCOMUSER=$(MIGUSER) -n -cc cat - /dev/null +MIG_OUTDIR=$(builddir)/tests/mig-out +MIG_CPPFLAGS=-nostdinc -I$(MACH_TESTINCLUDE) -ggdb + +define generate_mig_client +$(MIG_OUTDIR)/$(2).user.c: prepare-test $(MIG_OUTDIR) $(srcdir)/include/$(1)/$(2).defs + $(CPP) $(MIG_CPPFLAGS) -o $(MIG_OUTDIR)/$(2).user.defs $(srcdir)/include/$(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 + $(CPP) $(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 + +MIG_GEN_CC= \ + $(MIG_OUTDIR)/mach.user.c \ + $(MIG_OUTDIR)/mach_host.user.c \ + $(MIG_OUTDIR)/mach_port.user.c \ + $(MIG_OUTDIR)/gnumach.user.c \ + $(MIG_OUTDIR)/device_reply.user.c \ + $(MIG_OUTDIR)/device.user.c \ + $(MIG_OUTDIR)/exc.server.c + +# eval->info for debug +$(eval $(call generate_mig_client,mach,mach)) +$(eval $(call generate_mig_client,mach,mach_host)) +$(eval $(call generate_mig_client,mach,mach_port)) +$(eval $(call generate_mig_client,mach,gnumach)) +$(eval $(call generate_mig_client,device,device_reply)) +$(eval $(call generate_mig_client,device,device)) +$(eval $(call generate_mig_server,mach,exc)) + + +TESTCFLAGS=-static -nostartfiles -nolibc \ + -ffreestanding \ + -I$(srcdir)/tests/include \ + -I$(MACH_TESTINCLUDE) \ + -I$(MIG_OUTDIR) \ + -ggdb3 \ + -DMIG_EOPNOTSUPP + +if HOST_ix86 +QEMU=qemu-system-i386 +QEMU_GDB_PORT ?= 11234 +endif +if HOST_x86_64 +QEMU=qemu-system-x86_64 +QEMU_GDB_PORT ?= 1234 endif + +if enable_user32 +TESTCFLAGS += -m32 +MIG_CPPFLAGS += -m32 +endif + +# graphical console +#GNUMACH_ARGS = +#QEMU_OPTS = -m 512 -no-reboot +GNUMACH_ARGS = console=com0 +QEMU_OPTS = -m 2048 -nographic -no-reboot +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/testlib.c \ + $(MIG_GEN_CC) + +module-%: $(srcdir)/tests/test-%.c $(SRC_TESTLIB) $(MACH_TESTINSTALL) + gcc $(TESTCFLAGS) $< $(SRC_TESTLIB) -o $@ + +test-%.iso: module-% gnumach $(srcdir)/tests/grub.cfg.single.template + rm -rf $(builddir)/tests/isofiles + mkdir -p $(builddir)/tests/isofiles/boot/grub/ + cat $(srcdir)/tests/grub.cfg.single.template | \ + sed -e s/BOOTMODULE/$</g | \ + sed -e "s/GNUMACHARGS/$(GNUMACH_ARGS)/g" | \ + cat >$(builddir)/tests/isofiles/boot/grub/grub.cfg + cp gnumach $< $(builddir)/tests/isofiles/boot/ + grub-mkrescue -o $@ $(builddir)/tests/isofiles + +tests/test-%: module-% test-%.iso $(srcdir)/tests/run-qemu.sh.template + cat $(srcdir)/tests/run-qemu.sh.template | \ + sed -e "s/TESTNAME/$(subst module-,,$<)/g" | \ + sed -e "s/QEMU_OPTS/$(QEMU_OPTS)/g" | \ + sed -e "s/QEMU/$(QEMU)/g" | \ + cat >$@ + chmod +x $@ + +check-%: tests/test-% + $^ + +run-%: test-%.iso + $(QEMU) $(QEMU_OPTS) -cdrom $< | tail -n +18 # skip terminal reconfiguration + +debug-%: test-%.iso + $(QEMU) $(QEMU_OPTS) -cdrom $< -gdb tcp::$(QEMU_GDB_PORT) -S | tail -n +18 + + +TESTS += \ + tests/test-hello + +endif # !PLATFORM_xen diff --git a/tests/README b/tests/README new file mode 100644 index 00000000..4cf25891 --- /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 gnuamch + ... + (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..6fe1c3b9 100644 --- a/tests/configfrag.ac +++ b/tests/configfrag.ac @@ -22,6 +22,16 @@ dnl 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. AC_CONFIG_FILES([tests/test-multiboot], [chmod +x tests/test-multiboot]) + +AC_CHECK_TOOL([MIGUSER], [miguser], [no]) +AC_ARG_VAR([MIGUSER], [Path to the mig tool for user-space tests]) + +if test x$MIGUSER = xno +then + AC_MSG_WARN([mig user was not found, we will not be able to run the user-space tests. You can also specify the path with MIGUSER=]) + MIGUSER=mig-user-not-found +fi + 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..0aed377e --- /dev/null +++ b/tests/grub.cfg.single.template @@ -0,0 +1,3 @@ +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..6d097527 --- /dev/null +++ b/tests/include/device/cons.h @@ -0,0 +1,10 @@ + +#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..1fddcd95 --- /dev/null +++ b/tests/include/testlib.h @@ -0,0 +1,46 @@ + +#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) do { \ + if (!(cond)) \ + { \ + printf("error: " #cond " failed\n"); \ + halt(); \ + } \ + } while (0) + +#define ASSERT_RET(ret, msg) do { \ + if ((ret) != KERN_SUCCESS) \ + { \ + printf("error %s (0x%x): %s\n", e2s((ret)), (ret), (msg)); \ + halt(); \ + } \ + } while (0) + +const char* e2s(int err); +void halt(); +int msleep(uint32_t timeout); +void perror(int err, const char *s); + +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..1f116354 --- /dev/null +++ b/tests/run-qemu.sh.template @@ -0,0 +1,22 @@ +#!/bin/sh + +set -e + +cmd="QEMU QEMU_OPTS -cdrom test-TESTNAME.iso" +out=out-TESTNAME +if which QEMU >/dev/null ; then + if ! timeout -v --foreground --kill-after=3 15s $cmd > $out; then + tail -n +18 $out # skip terminal reconfiguration + exit 10 + fi + if grep -qi error $out; then # TODO: check test failure or success + tail -n +18 $out # skip terminal reconfiguration + exit 11 + fi + if ! grep -q reboot $out; then # check that we reached the end + tail -n +18 $out # skip terminal reconfiguration + exit 12 + fi +else + exit 77 # skip, qemu nut available +fi 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..68ae45de --- /dev/null +++ b/tests/test-hello.c @@ -0,0 +1,9 @@ + +#include <testlib.h> + +int main(int argc, char *argv[], int envc, char *envp[]) +{ + int ret = printf("hello!!\n"); + ASSERT(ret == 0); + return 0; +} diff --git a/tests/testlib.c b/tests/testlib.c new file mode 100644 index 00000000..13aa2614 --- /dev/null +++ b/tests/testlib.c @@ -0,0 +1,125 @@ + +#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() +{ + printf("reboot ...\n"); + int ret = host_reboot(host_priv_port, 0); + perror(ret, "host_reboot() failed!"); + while (1) + ; +} + +// from glibc's sysdeps/mach/usleep.c +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) +{ + switch (err) + { + case MACH_SEND_INVALID_DATA: return "MACH_SEND_INVALID_DATA"; + case MACH_SEND_INVALID_DEST: return "MACH_SEND_INVALID_DEST"; + case MACH_SEND_TIMED_OUT: return "MACH_SEND_TIMED_OUT"; + case MACH_SEND_INVALID_RIGHT: return "MACH_SEND_INVALID_RIGHT"; + case MACH_SEND_INVALID_HEADER: return "MACH_SEND_INVALID_HEADER"; + case MACH_SEND_INVALID_TYPE: return "MACH_SEND_INVALID_TYPE"; + case MACH_SEND_INVALID_REPLY: return "MACH_SEND_INVALID_REPLY"; + case MACH_SEND_MSG_TOO_SMALL: return "MACH_SEND_MSG_TOO_SMALL"; + case MACH_RCV_INVALID_NAME: return "MACH_RCV_INVALID_NAME"; + case MACH_RCV_TOO_LARGE: return "MACH_RCV_TOO_LARGE"; + case MIG_TYPE_ERROR: return "MIG_TYPE_ERROR"; + case MIG_REPLY_MISMATCH: return "MIG_REPLY_MISMATCH"; + case MIG_SERVER_DIED: return "MIG_SERVER_DIED"; + case KERN_NO_SPACE: return "KERN_NO_SPACE"; + case KERN_INVALID_ARGUMENT: return "KERN_INVALID_ARGUMENT"; + case KERN_FAILURE: return "KERN_FAILURE"; + case KERN_TIMEDOUT: return "KERN_TIMEDOUT"; + case KERN_INVALID_TASK: return "KERN_INVALID_TASK"; + case KERN_INVALID_HOST: return "KERN_INVALID_HOST"; + case KERN_INVALID_RIGHT: return "KERN_INVALID_RIGHT"; + case KERN_INVALID_VALUE: return "KERN_INVALID_VALUE"; + case KERN_NAME_EXISTS: return "KERN_NAME_EXISTS"; + default: return "unknown"; + } +} + +/* + * Report a test error, translating the error code. + */ +void perror(int err, const char *s) +{ + printf("error %s (0x%x): %s\n", e2s(err), err, s); +} + +/* + * Minimal _start() for test modules, we just take the arguments from + * the kernel, call main() and reboot. + */ +void _start() +{ + // magic from _hurd_startup, but here somehow we need +1 instead of +2 + void **argptr = (void **) __builtin_frame_address (0) + 1; + 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("module %s exit code %x\n", argv[0], ret); + halt(); +} -- 2.39.2