diff --git a/tests/default.nix b/tests/default.nix index 42cbe9d..ae456bf 100644 --- a/tests/default.nix +++ b/tests/default.nix @@ -32,10 +32,11 @@ let | dhall text \ > $out ''; - }; in { linux = tests (import ./driver-linux.nix { inherit testPkgs hostPkgs lib; }).callTest; + nova = tests + (import ./driver-nova.nix { inherit testPkgs hostPkgs lib; }).callTest; } diff --git a/tests/driver-nova.nix b/tests/driver-nova.nix new file mode 100644 index 0000000..0750fa6 --- /dev/null +++ b/tests/driver-nova.nix @@ -0,0 +1,191 @@ +# SPDX-FileCopyrightText: Emery Hemingway +# +# SPDX-License-Identifier: LicenseRef-Hippocratic-1.1 + +{ testPkgs, hostPkgs, lib }: + +let + testDriver = with hostPkgs; + stdenv.mkDerivation { + name = "nova-genode-test-driver"; + + buildInputs = [ makeWrapper expect ]; + + dontUnpack = true; + + preferLocalBuild = true; + + installPhase = '' + install -Dm555 ${./nova-test-driver.exp} $out/bin/genode-test-driver + wrapProgram $out/bin/genode-test-driver \ + --prefix PATH : "${lib.makeBinPath [ expect coreutils ]}" + ''; + }; + + runTests = driver: + hostPkgs.stdenv.mkDerivation { + name = "test-run-${driver.testName}"; + + buildCommand = '' + mkdir -p $out/nix-support + + ${driver}/bin/genode-test-driver | tee $out/log + + touch $out/nix-support + echo "report testlog $out log" >> $out/nix-support/hydra-build-products + ''; + }; + + makeTest = { testScript, testConfig, name ? "unamed", ... }@t: + let + + baseSetup = with testPkgs; + + '' + global modules + set HOSTLD ${buildPackages.binutils}/bin/x86_64-unknown-genode-elf-ld + + file link -s timer ${genode.base-nova}/bin/nova_timer_drv + file link -s ld.lib.so ${depot.base-nova}/lib/ld.lib.so + file link -s core-nova.o ${depot.base-nova}/lib/core-nova.o + file link -s init ${genode.os}/bin/init + file link -s config ${./driver-config.xml} + file link -s test.config $env(testConfig) + + set modules { + timer + ld.lib.so + init + config + test.config + } + + set qemu_mem 64 + + ## + # Link core image containing given modules + # + proc build_core {lib modules target} { + global env + + # generate assembly code aggregating the modules data + set asm_src [generate_boot_modules_asm $modules] + + # compile the boot modules into one object file + exec $env(CC) -c -x assembler -o boot_modules.o - << $asm_src + + # link final image + global HOSTLD + exec $HOSTLD -nostdlib \ + -T${./genode.ld} \ + -T${./nova-bss.ld} \ + -z max-page-size=0x1000 \ + -Ttext=0x100000 -gc-sections \ + --whole-archive \ + $lib boot_modules.o --no-whole-archive \ + -o $target + } + + proc build_iso {target} { + # TODO: take our own build of NOVA + file mkdir boot/syslinux + file copy ${nova}/hypervisor-x86_64 boot/hypervisor + file copy ${./nova-isolinux.cfg} boot/syslinux/isolinux.cfg + file copy ${hostPkgs.syslinux}/share/syslinux/isolinux.bin boot/syslinux/isolinux.bin + file copy ${hostPkgs.syslinux}/share/syslinux/ldlinux.c32 boot/syslinux/ldlinux.c32 + file copy ${hostPkgs.syslinux}/share/syslinux/libcom32.c32 boot/syslinux/libcom32.c32 + file copy ${hostPkgs.syslinux}/share/syslinux/mboot.c32 boot/syslinux/mboot.c32 + + exec chmod +w boot/syslinux/isolinux.bin + catch { exec ${hostPkgs.cdrkit}/bin/mkisofs -o $target \ + -b syslinux/isolinux.bin -c syslinux/boot.cat \ + -no-emul-boot -boot-load-size 4 -boot-info-table \ + -iso-level 2 \ + boot + } + puts "built ISO?" + } + + ## + # Wait for a specific output of a already running spawned process + # + proc wait_for_output { wait_for_re timeout_value running_spawn_id } { + global output + + if {$wait_for_re == "forever"} { + set timeout -1 + interact { + \003 { + send_user "Expect: 'interact' received 'strg+c' and was cancelled\n"; + exit + } + -i $running_spawn_id + } + } else { + set timeout $timeout_value + } + + expect { + -i $running_spawn_id -re $wait_for_re { } + eof { puts stderr "Error: Spawned process died unexpectedly"; exit -1 } + timeout { puts stderr "Error: Test execution timed out"; exit -1 } + } + set output $expect_out(buffer) + } + + + proc run_genode_until {{wait_for_re forever} {timeout_value 0} {running_spawn_id -1}} { + # + # If a running_spawn_id is specified, wait for the expected output + # + if {$running_spawn_id != -1} { + wait_for_output $wait_for_re $timeout_value $running_spawn_id + return + } + + global env modules qemu_mem + file mkdir boot + build_core core-nova.o $modules boot/image.elf + + set out $env(out) + build_iso $out/test.iso + + global spawn_id + spawn ${hostPkgs.qemu_test}/bin/qemu-system-x86_64 -cdrom $out/test.iso -nographic -m $qemu_mem + wait_for_output $wait_for_re $timeout_value $spawn_id + } + ''; + + driver = with hostPkgs; + buildPackages.runCommand "genode-test-driver-${name}" { + buildInputs = [ makeWrapper expect ]; + inherit baseSetup testConfig testScript; + preferLocalBuild = true; + testName = name; + } '' + mkdir -p $out/bin + echo "$testConfig" > $out/test.config + echo "$testScript" > $out/test-script + echo "$baseSetup" > $out/base-setup + ln -s ${testDriver}/bin/genode-test-driver $out/bin/ + wrapProgram $out/bin/genode-test-driver \ + --run "export testConfig=\"$testConfig\"" \ + --run "export testScript=\"\$(cat $out/test-script)\"" \ + --run "export baseSetup=\"\$(cat $out/base-setup)\"" \ + ''; + + passMeta = drv: + drv + // lib.optionalAttrs (t ? meta) { meta = (drv.meta or { }) // t.meta; }; + + test = passMeta (runTests driver); + + in test // { inherit driver test; }; + +in { + callTest = path: args: + makeTest (import path ({ + pkgs = testPkgs; + inherit lib; + } // args)); +} diff --git a/tests/fs_report.nix b/tests/fs_report.nix index 6e686b5..f209576 100644 --- a/tests/fs_report.nix +++ b/tests/fs_report.nix @@ -13,6 +13,7 @@ with pkgs; file link -s ram_fs ${depot.ram_fs}/bin/ram_fs file link -s vfs.lib.so ${depot.vfs}/lib/vfs.lib.so file link -s test-fs_report ${depot.test-fs_report}/bin/test-fs_report + append modules { fs_report fs_rom ram_fs vfs.lib.so test-fs_report } run_genode_until {child "test-fs_report" exited with exit value 0} 15 ''; } diff --git a/tests/genode.ld b/tests/genode.ld new file mode 100644 index 0000000..c5f8989 --- /dev/null +++ b/tests/genode.ld @@ -0,0 +1,137 @@ +/* + * \brief Linker script for Genode programs + * \author Christian Helmuth + * \date 2006-04-12 + */ + +/* + * Copyright (C) 2006-2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +ENTRY(_start) + +PHDRS +{ + ro PT_LOAD; + rw PT_LOAD; + boot PT_LOAD FLAGS(4); +} + +SECTIONS +{ + .text : { + /* begin of program image (link address) */ + _prog_img_beg = .; + + /* put entry code at the start of the text segment / raw binary */ + KEEP (*(.text.crt0)) + + *(.init) + *(.text .text.* .gnu.linkonce.t.*) + *(.fini) + *(.rodata .rodata.* .gnu.linkonce.r.*) + + . = ALIGN(0x08); + + _ctors_start = .; + KEEP (*(.ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.init_array)) /* list of constructors specific for ARM eabi */ + _ctors_end = .; + _dtors_start = .; + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + _dtors_end = .; + } : ro = 0x0 + + /* Linux: exception section for uaccess mechanism */ + __ex_table : { *(__ex_table) } + + .eh_frame_hdr : { *(.eh_frame_hdr) } + __eh_frame_hdr_start = SIZEOF(.eh_frame_hdr) > 0 ? ADDR(.eh_frame_hdr) : 0; + __eh_frame_hdr_end = SIZEOF(.eh_frame_hdr) > 0 ? . : 0; + + . = ALIGN(0x1000); + + .data : { + /* + * Leave space for parent capability parameters at start of data + * section. The protection domain creator is reponsible for storing + * sane values here. + */ + _parent_cap = .; + _parent_cap_thread_id = .; + LONG(0xffffffff); + _parent_cap_local_name = .; + LONG(0xffffffff); + LONG(0xffffffff); + LONG(0xffffffff); + LONG(0xffffffff); + LONG(0xffffffff); + LONG(0xffffffff); + LONG(0xffffffff); + + /* + * Platform-specific entry for Fiasco.OC. + * + * PIC-code compiled for Fiasco.OC, needs some PIC-compatible + * way to enter the kernel, the fixed address of the kernel + * entry code address needs to be found here. + */ + __l4sys_invoke_indirect = .; + LONG(0xeacff000); + + *(.data .gnu.linkonce.d.*) + + /* include all data subsections except those of the boot modules */ + *(EXCLUDE_FILE (*boot_modules.o) .data.*) + } : rw + + /* exception frames for C++ */ + .eh_frame : { + __eh_frame_start__ = .; + __eh_frame_start = .; + KEEP (*(.eh_frame)) + __eh_frame_end = .; + LONG(0) + } : rw + + .gcc_except_table : { + KEEP(*(.gcc_except_table)) + KEEP(*(.gcc_except_table.*)) + } + + .dynamic : { *(.dynamic) } + + /* .ARM.exidx is sorted, so has to go in its own output section */ + __exidx_start = .; + .ARM.exidx : { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } + __exidx_end = .; + + .ARM.extab : { + *(.ARM.extab*) + } : rw + + .bss : { + _bss_start = ALIGN(4); + *(.bss .bss.* .gnu.linkonce.b.* COMMON) + } + _bss_end = ALIGN(4); + + /* separate location for the binaries of the boot modules */ + .data.boot_modules_binaries : { *(.data.boot_modules_binaries) } : boot + + /* end of program image -- must be after last section */ + _prog_img_end = .; + + /DISCARD/ : { + *(.note) + *(.note.ABI-tag) + *(.comment) + } +} diff --git a/tests/libc.nix b/tests/libc.nix index e77912f..f52f75e 100644 --- a/tests/libc.nix +++ b/tests/libc.nix @@ -8,11 +8,14 @@ rec { testConfig = lib.renderDhallInit ./libc.dhall "{=}"; testScript = '' + global qemu_mem file link -s libc.lib.so ${depot.libc}/lib/libc.lib.so file link -s libm.lib.so ${depot.libc}/lib/libm.lib.so file link -s posix.lib.so ${depot.posix}/lib/posix.lib.so file link -s vfs.lib.so ${depot.vfs}/lib/vfs.lib.so file link -s test-libc ${depot.test-libc}/bin/test-libc + append modules { libc.lib.so libm.lib.so posix.lib.so vfs.lib.so test-libc } + set qemu_mem 384 run_genode_until "child .* exited with exit value 0.*\n" 30 ''; } diff --git a/tests/log.nix b/tests/log.nix index fae60f8..868176e 100644 --- a/tests/log.nix +++ b/tests/log.nix @@ -9,6 +9,7 @@ rec { testScript = '' file link -s test-log ${genode.base}/bin/test-log + append modules { test-log } run_genode_until {Test done.} 10 ''; } diff --git a/tests/nova-bss.ld b/tests/nova-bss.ld new file mode 100644 index 0000000..994c3a2 --- /dev/null +++ b/tests/nova-bss.ld @@ -0,0 +1,6 @@ +SECTIONS +{ + .data : { + *(.bss .bss.* .gnu.linkonce.b.* COMMON) + } : rw +} diff --git a/tests/nova-isolinux.cfg b/tests/nova-isolinux.cfg new file mode 100644 index 0000000..93cdac1 --- /dev/null +++ b/tests/nova-isolinux.cfg @@ -0,0 +1,5 @@ +SERIAL +DEFAULT 0 +LABEL 0 + KERNEL mboot.c32 + APPEND /hypervisor iommu novpid serial --- /image.elf diff --git a/tests/nova-test-driver.exp b/tests/nova-test-driver.exp new file mode 100644 index 0000000..2acfbe2 --- /dev/null +++ b/tests/nova-test-driver.exp @@ -0,0 +1,75 @@ +#!/usr/bin/env expect + +## +# Generate assembly code aggregating boot-module data from specified files. +# +proc generate_boot_modules_asm {modules} { + + # architecture dependent definitions + set address_type ".quad" + + # header + set asm_src {} + append asm_src ".set MIN_PAGE_SIZE_LOG2, 12\n" + append asm_src ".set DATA_ACCESS_ALIGNM_LOG2, 3\n" + append asm_src "\n" + append asm_src ".section .data\n" + append asm_src "\n" + append asm_src ".p2align DATA_ACCESS_ALIGNM_LOG2\n" + append asm_src ".global _boot_modules_headers_begin\n" + append asm_src "_boot_modules_headers_begin:\n" + append asm_src "\n" + + # module list + set i 0 + foreach module $modules { + incr i + append asm_src "${address_type} _boot_module_${i}_name\n" + append asm_src "${address_type} _boot_module_${i}_begin\n" + append asm_src "${address_type} _boot_module_${i}_end -" + append asm_src " _boot_module_${i}_begin\n" + append asm_src "\n" + } + append asm_src ".global _boot_modules_headers_end\n" + append asm_src "_boot_modules_headers_end:\n" + append asm_src "\n" + + # module names + set i 0 + foreach module $modules { + incr i + append asm_src ".p2align DATA_ACCESS_ALIGNM_LOG2\n" + append asm_src "_boot_module_${i}_name:\n" + append asm_src ".string \"${module}\"\n" + append asm_src ".byte 0\n" + append asm_src "\n" + } + + # header end + append asm_src ".section .data.boot_modules_binaries\n" + append asm_src "\n" + append asm_src ".global _boot_modules_binaries_begin\n" + append asm_src "_boot_modules_binaries_begin:\n" + append asm_src "\n" + + # module data + set i 0 + foreach module $modules { + incr i + append asm_src ".p2align MIN_PAGE_SIZE_LOG2\n" + append asm_src "_boot_module_${i}_begin:\n" + append asm_src ".incbin \"${module}\"\n" + append asm_src "_boot_module_${i}_end:\n" + append asm_src "\n" + } + + append asm_src ".p2align MIN_PAGE_SIZE_LOG2\n" + append asm_src ".global _boot_modules_binaries_end\n" + append asm_src "_boot_modules_binaries_end:\n" + + return $asm_src +} + +eval $env(baseSetup) + +eval $env(testScript) diff --git a/tests/signal.nix b/tests/signal.nix index 98d00da..aec1b9c 100644 --- a/tests/signal.nix +++ b/tests/signal.nix @@ -9,6 +9,7 @@ rec { testScript = '' file link -s test-signal ${depot.test-signal}/bin/test-signal - run_genode_until {--- Signalling test finished ---} 60 + append modules { test-signal } + run_genode_until {--- Signalling test finished ---} 80 ''; } diff --git a/tests/solo5.nix b/tests/solo5.nix index 2c9df5b..feaec26 100644 --- a/tests/solo5.nix +++ b/tests/solo5.nix @@ -8,8 +8,11 @@ rec { testConfig = lib.renderDhallInit ./solo5.dhall "{=}"; testScript = '' + global modules file link -s solo5.lib.so ${solo5}/lib/solo5-bindings-genode/solo5.lib.so file link -s solo5-test_hello ${solo5.tests}/bin/solo5-test_hello + append modules { solo5.lib.so solo5-test_hello } + run_genode_until "child .* exited with exit value 0.*\n" 30 ''; }