diff --git a/repos/base-fiasco/run/env b/repos/base-fiasco/run/env deleted file mode 100644 index ebf324d47..000000000 --- a/repos/base-fiasco/run/env +++ /dev/null @@ -1,201 +0,0 @@ -# -# \brief Fiasco-specific test-environment supplements -# \author Norman Feske -# \author Christian Helmuth -# \date 2010-08-26 -# -# This file is meant to be used as '--include' argument for 'tool/run'. -# - - -## -# Install files needed to boot via PXE -# -proc install_pxe_bootloader_to_run_dir { } { - exec cp [genode_dir]/tool/boot/pulsar [run_dir]/boot/pulsar - exec cp [genode_dir]/tool/boot/bender [run_dir]/boot/bender -} - - -## -# Read the location of the Fiasco user directory from 'etc/fiasco.conf' -# -proc l4_dir { } { - global _l4_dir - - if {![info exists _l4_dir]} { - if {[file exists etc/fiasco.conf]} { - set _l4_dir [exec sed -n "/^L4_BUILD_DIR/s/^.*=\\s*//p" etc/fiasco.conf] - if {[file exists $_l4_dir]} { return $_l4_dir } - } - - set _l4_dir "[pwd]/l4" - if {![file exists $_l4_dir]} { - puts -nonewline stderr "Error: Could neither find the L4 build directory " - puts -nonewline stderr "within '/l4' nor at a location " - puts -nonewline stderr "specified via 'L4_BUILD_DIR = ' " - puts stderr "in /etc/fiasco.conf'." - exit 1 - } - } - return $_l4_dir -} - - -## -# Return whether the l4-buid-directory is provided from the outside -# -proc l4_dir_external { } { - if {[l4_dir] == "[pwd]/l4"} { return 0 } - return 1 -} - - -## -# Return the location of the Fiasco kernel -# -proc fiasco { } { - return [kernel_location_from_config_file etc/fiasco.conf [pwd]/kernel/fiasco/fiasco] -} - - -## -# Return whether fiasco kernel is provided from the outside -# -proc fiasco_external { } { - if {[fiasco] == "[pwd]/kernel/fiasco/fiasco"} { return 0 } - return 1 -} - - -################################## -## Test framework API functions ## -################################## - -proc create_boot_directory { } { - exec rm -rf [run_dir] - exec mkdir -p [run_dir]/genode - exec mkdir -p [run_dir]/fiasco -} - -proc bin_dir { } { - if {[have_spec x86_32]} { return "[l4_dir]/bin/x86_586" } - - puts stderr "Error: Cannot determine bin directory" - exit 1 -} - -set fiasco_serial_esc_arg "-serial_esc " - -proc build_boot_image {binaries} { - - global fiasco_serial_esc_arg - - # - # Collect contents of the ISO image - # - copy_and_strip_genode_binaries_to_run_dir $binaries - - if {![fiasco_external]} { build { kernel } } - if {![l4_dir_external]} { build { bootstrap sigma0 } } - - # assert existence of the L4 build directory - l4_dir - - puts "using fiasco kernel [fiasco]" - exec cp [fiasco] [run_dir]/fiasco/fiasco - puts "using sigma0/bootstrap at [l4_dir]" - exec cp [bin_dir]/l4v2/sigma0 [run_dir]/fiasco - exec cp [bin_dir]/bootstrap [run_dir]/fiasco - - install_iso_bootloader_to_run_dir - - # - # Generate grub config file - # - # The core binary is part of the 'binaries' list but it must - # appear right after 'sigma0' as boot module. Hence the special case. - # - set fh [open "[run_dir]/boot/grub/menu.lst" "WRONLY CREAT TRUNC"] - puts $fh "timeout 0" - puts $fh "default 0" - puts $fh "\ntitle Genode on L4/Fiasco" - puts $fh " kernel /boot/bender" - puts $fh " module /fiasco/bootstrap -serial -modaddr=0x02000000" - puts $fh " module /fiasco/fiasco -serial -jdb_cmd=JH $fiasco_serial_esc_arg" - puts $fh " module /fiasco/sigma0" - puts $fh " module /genode/core" - puts $fh " module /genode/config" - foreach binary $binaries { - if {$binary != "core"} { - puts $fh " module /genode/$binary" } } - puts $fh " vbeset 0x117 506070" - close $fh - - # - # Install PXE bootloader pulsar - # - install_pxe_bootloader_to_run_dir - - create_iso_image_from_run_dir - create_disk_image_from_run_dir - - # - # Generate pulsar config file - # - set fh [open "[run_dir]/config-52-54-00-12-34-56" "WRONLY CREAT TRUNC"] - puts $fh " exec /boot/bender" - puts $fh " load /fiasco/bootstrap -serial -modaddr=0x02000000" - puts $fh " load /fiasco/fiasco -serial -serial_esc -jdb_cmd=JH" - puts $fh " load /fiasco/sigma0" - puts $fh " load /genode/core" - puts $fh " load /genode/config" - foreach binary $binaries { - if {$binary != "core"} { - puts $fh " load /genode/$binary" } } - close $fh - - # - # Generate pulsar config file pointing to the config file above. - # - if {[info exists ::env(PXE_TFTP_DIR_BASE)] && [info exists ::env(PXE_TFTP_DIR_OFFSET)]} { - exec ln -nfs "[pwd]" "$::env(PXE_TFTP_DIR_BASE)$::env(PXE_TFTP_DIR_OFFSET)" - - set tftp_base "" - if {[get_cmd_switch --tftp-absolute]} { - set tftp_base $::env(PXE_TFTP_DIR_BASE) - } - - set fh [open "$::env(PXE_TFTP_DIR_BASE)$::env(PXE_TFTP_DIR_OFFSET)/config-00-00-00-00-00-00" "WRONLY CREAT TRUNC"] - puts $fh " root $tftp_base$::env(PXE_TFTP_DIR_OFFSET)/[run_dir]" - puts $fh " config config-52-54-00-12-34-56" - close $fh - } -} - - -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 - } - - # - # Try to use one of the supported backends for running the scripts - # - if {[is_amt_available]} { - spawn_amt $wait_for_re $timeout_value - return - } - if {[is_qemu_available]} { - spawn_qemu $wait_for_re $timeout_value - return - } - - global run_target - puts stderr "Error: Can't execute automatically on target '$run_target'" - exit -1 -} diff --git a/repos/base-linux/run/env b/repos/base-linux/run/env deleted file mode 100644 index c34edb7ef..000000000 --- a/repos/base-linux/run/env +++ /dev/null @@ -1,72 +0,0 @@ -# -# \brief Environment for executing Genode on Linux -# \author Norman Feske -# \date 2010-08-16 -# -# For the documentation of the implemented API functions, -# please refer to the comments in 'tool/run'. -# - -proc create_boot_directory { } { - exec rm -rf [run_dir] - exec mkdir -p [run_dir] -} - - -proc install_config {config} { - set fh [open "[run_dir]/config" "WRONLY CREAT TRUNC"] - puts $fh $config - close $fh - - check_xml_syntax [run_dir]/config -} - - -proc build_boot_image {binaries} { - foreach binary $binaries { - exec ln -sf ../../../bin/$binary [run_dir] } -} - - -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 spawn_id - - set orig_pwd [pwd] - cd [run_dir] - spawn ./core - wait_for_output $wait_for_re $timeout_value $spawn_id - cd $orig_pwd -} - - -## -# Umount a directory that was bind-mounted beforehand -# -# This function is used by chroot-related tests, e.g., 'os/run/chroot.run', -# 'os/run/chroot_loader.run'. -# -proc umount_and_rmdir { path } { - - puts "umounting $path" - - # - # Invoke umount until it returns an error. Apparently, the unmounting - # of bind-mounted mount points does not always take immediate effect - # (regardless of the -l option). - # - while {1} { - if {[catch { exec sudo umount -l $path }]} { break; } - sleep 0.25 - } - - catch { exec rmdir -p $path } -} - diff --git a/repos/base-nova/run/env b/repos/base-nova/run/env deleted file mode 100644 index 6b4cc1846..000000000 --- a/repos/base-nova/run/env +++ /dev/null @@ -1,144 +0,0 @@ -# -# \brief NOVA-specific test-environment supplements -# \author Norman Feske -# \date 2010-08-31 -# -# This file is meant to be used as '--include' argument for 'tool/run'. -# - -## -# Install files needed to boot via PXE -# -proc install_pxe_bootloader_to_run_dir { } { - exec cp [genode_dir]/tool/boot/pulsar [run_dir]/boot/pulsar - exec cp [genode_dir]/tool/boot/bender [run_dir]/boot/bender -} - -## -# Read the location of the NOVA kernel directory from 'etc/nova.conf' -# -proc nova_kernel { } { - global _nova_kernel - - if {![info exists _nova_kernel]} { - if {[file exists etc/nova.conf]} { - set _nova_kernel [exec sed -n "/^NOVA_KERNEL/s/^.*=\\s*//p" etc/nova.conf] - } else { - set _nova_kernel "[pwd]/kernel/hypervisor" - } - } - return $_nova_kernel -} - -## -# Return whether nova is provided from the outside -# -proc nova_external { } { - if {[nova_kernel] == "[pwd]/kernel/hypervisor"} { return 0 } - return 1 -} - -################################## -## Test framework API functions ## -################################## - -proc create_boot_directory { } { - exec rm -rf [run_dir] - exec mkdir -p [run_dir]/genode -} - - -proc build_boot_image {binaries} { - - # - # Collect contents of the ISO image - # - copy_and_strip_genode_binaries_to_run_dir $binaries - - if {![nova_external] && ![file exists [nova_kernel]]} { build { kernel } } - - puts "using NOVA kernel at [nova_kernel]" - exec [cross_dev_prefix]objcopy -O elf32-i386 [nova_kernel] [run_dir]/hypervisor - - install_iso_bootloader_to_run_dir - - # - # The core binary is part of the 'binaries' list but it must - # appear right after 'sigma0' as boot module. Hence the special case. - # - # Generate grub config file - # - set fh [open "[run_dir]/boot/grub/menu.lst" "WRONLY CREAT TRUNC"] - puts $fh "timeout 0" - puts $fh "default 0" - puts $fh "\ntitle Genode on NOVA" - puts $fh " kernel /boot/bender" - puts $fh " module /hypervisor iommu serial novpid" - puts $fh " module /genode/core" - puts $fh " module /genode/config" - foreach binary $binaries { - if {$binary != "core"} { - puts $fh " module /genode/$binary" } } - close $fh - - install_pxe_bootloader_to_run_dir - create_iso_image_from_run_dir - create_disk_image_from_run_dir - - # - # Generate pulsar config file - # - set fh [open "[run_dir]/config-52-54-00-12-34-56" "WRONLY CREAT TRUNC"] - puts $fh " exec /boot/bender" - puts $fh " load /hypervisor iommu serial novpid" - puts $fh " load /genode/core" - puts $fh " load /genode/config" - foreach binary $binaries { - if {$binary != "core"} { - puts $fh " load /genode/$binary" } } - close $fh - - # - # Generate pulsar config file pointing to the config file above. - # - if {[info exists ::env(PXE_TFTP_DIR_BASE)] && [info exists ::env(PXE_TFTP_DIR_OFFSET)]} { - exec ln -nfs "[pwd]" "$::env(PXE_TFTP_DIR_BASE)$::env(PXE_TFTP_DIR_OFFSET)" - - set tftp_base "" - if {[get_cmd_switch --tftp-absolute]} { - set tftp_base $::env(PXE_TFTP_DIR_BASE) - } - - set fh [open "$::env(PXE_TFTP_DIR_BASE)$::env(PXE_TFTP_DIR_OFFSET)/config-00-00-00-00-00-00" "WRONLY CREAT TRUNC"] - puts $fh " root $tftp_base$::env(PXE_TFTP_DIR_OFFSET)/[run_dir]" - puts $fh " config config-52-54-00-12-34-56" - close $fh - } -} - - -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 - } - - # - # Try to use one of the supported backends for running the scripts - # - if {[is_amt_available]} { - spawn_amt $wait_for_re $timeout_value - return - } - if {[is_serial_available]} { - spawn_serial $wait_for_re $timeout_value "NOVA Microhypervisor" - return - } - if {[is_qemu_available]} { - spawn_qemu $wait_for_re $timeout_value - return - } -} diff --git a/repos/base-pistachio/run/env b/repos/base-pistachio/run/env deleted file mode 100644 index 2400a99fb..000000000 --- a/repos/base-pistachio/run/env +++ /dev/null @@ -1,179 +0,0 @@ -# -# \brief Pistachio-specific test-environment supplements -# \author Norman Feske -# \date 2010-08-25 -# -# This file is meant to be used as '--include' argument for 'tool/run'. -# - - -## -# Install files needed to boot via PXE -# -proc install_pxe_bootloader_to_run_dir { } { - exec cp [genode_dir]/tool/boot/pulsar [run_dir]/boot/pulsar - exec cp [genode_dir]/tool/boot/bender [run_dir]/boot/bender -} - - -## -# Read the location of the Pistachio user directory from 'etc/pistachio.conf' -# -proc pistachio_user_dir { } { - global _pistachio_user_dir - - if {![info exists _pistachio_user_dir]} { - if {[file exists etc/pistachio.conf]} { - set _pistachio_user_dir [exec sed -n "/^PISTACHIO_USER_BUILD_DIR/s/^.*=\\s*//p" etc/pistachio.conf] - } else { - set _pistachio_user_dir "[pwd]/l4" - } - } - return $_pistachio_user_dir -} - - -## -# Read the location of the Pistachio kernel directory from 'etc/pistachio.conf' -# or return a good heuristic -# -proc pistachio_kernel { } { - global _pistachio_kernel - - if {![info exists _pistachio_kernel]} { - if {[file exists etc/pistachio.conf]} { - set _pistachio_kernel [exec sed -n "/^PISTACHIO_KERNEL/s/^.*=\\s*//p" etc/pistachio.conf] - if {$_pistachio_kernel == ""} { - set _pistachio_kernel [file dirname [file dirname [pistachio_user_dir]]]/kernel/build/x86-kernel - } - } else { - set _pistachio_kernel "[pwd]/bin/kernel" - } - } - return $_pistachio_kernel -} - - -## -# Return whether the kernel is provided from the outside -# -proc kernel_external { } { - if {[pistachio_kernel] == "[pwd]/bin/kernel"} { return 0 } - return 1 -} - - -################################## -## Test framework API functions ## -################################## - -proc create_boot_directory { } { - exec rm -rf [run_dir] - exec mkdir -p [run_dir]/genode - exec mkdir -p [run_dir]/pistachio -} - - -proc build_boot_image {binaries} { - - # - # Collect contents of the ISO image - # - copy_and_strip_genode_binaries_to_run_dir $binaries - - if {![kernel_external] && ![file exists [pistachio_kernel]]} { build { kernel } } - - exec cp [pistachio_kernel] [run_dir]/pistachio/kernel - exec cp [pistachio_user_dir]/serv/sigma0/sigma0 [run_dir]/pistachio - exec cp [pistachio_user_dir]/util/kickstart/kickstart [run_dir]/pistachio - - install_iso_bootloader_to_run_dir - - # - # Generate grub config file - # - # The core binary is part of the 'binaries' list but it must - # appear right after 'sigma0' as boot module. Hence the special case. - # - set fh [open "[run_dir]/boot/grub/menu.lst" "WRONLY CREAT TRUNC"] - puts $fh "timeout 0" - puts $fh "default 0" - puts $fh "\ntitle Genode on L4ka::Pistachio" - puts $fh " kernel /pistachio/kickstart" - puts $fh " module /pistachio/kernel" - puts $fh " module /pistachio/sigma0" - puts $fh " module /genode/core" - puts $fh " module /genode/config" - foreach binary $binaries { - if {$binary != "core"} { - puts $fh " module /genode/$binary" } } - close $fh - - # - # Install PXE bootloader pulsar - # - install_pxe_bootloader_to_run_dir - - create_iso_image_from_run_dir - create_disk_image_from_run_dir - - # - # Generate pulsar config file - # - set fh [open "[run_dir]/config-52-54-00-12-34-56" "WRONLY CREAT TRUNC"] - puts $fh " exec /boot/bender" - puts $fh " load /pistachio/kickstart" - puts $fh " load /pistachio/kernel" - puts $fh " load /pistachio/sigma0" - puts $fh " load /genode/core" - puts $fh " load /genode/config" - puts $fh " load /genode/config" - foreach binary $binaries { - if {$binary != "core"} { - puts $fh " load /genode/$binary" } } - close $fh - - # - # Generate pulsar config file pointing to the config file above. - # - if {[info exists ::env(PXE_TFTP_DIR_BASE)] && [info exists ::env(PXE_TFTP_DIR_OFFSET)]} { - exec ln -nfs "[pwd]" "$::env(PXE_TFTP_DIR_BASE)$::env(PXE_TFTP_DIR_OFFSET)" - - set tftp_base "" - if {[get_cmd_switch --tftp-absolute]} { - set tftp_base $::env(PXE_TFTP_DIR_BASE) - } - - set fh [open "$::env(PXE_TFTP_DIR_BASE)$::env(PXE_TFTP_DIR_OFFSET)/config-00-00-00-00-00-00" "WRONLY CREAT TRUNC"] - puts $fh " root $tftp_base$::env(PXE_TFTP_DIR_OFFSET)/[run_dir]" - puts $fh " config config-52-54-00-12-34-56" - close $fh - } -} - - -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 - } - - # - # Try to use one of the supported backends for running the scripts - # - if {[is_amt_available]} { - spawn_amt $wait_for_re $timeout_value - return - } - if {[is_qemu_available]} { - spawn_qemu $wait_for_re $timeout_value - return - } - - global run_target - puts stderr "Error: Can't execute automatically on target '$run_target'" - exit -1 -} diff --git a/tool/builddir/build.mk b/tool/builddir/build.mk index 07416a782..b944c4151 100644 --- a/tool/builddir/build.mk +++ b/tool/builddir/build.mk @@ -238,14 +238,15 @@ RUN_SCRIPT = $(call select_from_repositories,run/$*.run) run/%: $(call select_from_repositories,run/%.run) $(RUN_ENV) $(VERBOSE)test -f "$(RUN_SCRIPT)" || (echo "Error: No run script for $*"; exit -1) - $(VERBOSE)$(GENODE_DIR)/tool/run --genode-dir $(GENODE_DIR) \ - --name $* \ - --specs "$(SPECS)" \ - --repositories "$(REPOSITORIES)" \ - --cross-dev-prefix "$(CROSS_DEV_PREFIX)" \ - --qemu-args "$(QEMU_OPT)" \ - --include $(RUN_ENV) $(RUN_OPT) \ - --include $(RUN_SCRIPT) + $(VERBOSE)$(GENODE_DIR)/tool/run/run --genode-dir $(GENODE_DIR) \ + --name $* \ + --specs "$(SPECS)" \ + --repositories "$(REPOSITORIES)" \ + --cross-dev-prefix "$(CROSS_DEV_PREFIX)" \ + --qemu-args "$(QEMU_OPT)" \ + $(RUN_OPT) \ + --include $(RUN_ENV) \ + --include $(RUN_SCRIPT) ## ## Clean rules diff --git a/tool/run b/tool/run deleted file mode 100755 index 67fa35bf5..000000000 --- a/tool/run +++ /dev/null @@ -1,1011 +0,0 @@ -#!/usr/bin/expect - -# -# \brief Framework for running automated tests -# \author Norman Feske -# \date 2010-03-16 -# -# Usage: run --name --include ... -# -# The '--name' argument is used for as name for the boot-image and -# temporary directories. The files includes via the '--include' -# argument provide platform-specific additions/refinements to the -# test framework as well as the actual test steps. -# - - -## -# Remove leading and trailing whitespace from string -# -proc strip_whitespace {string} { - regsub -all {^\s+} $string "" string - regsub -all {\s+$} $string "" string - return $string -} - - -## -# Check if the specified spec requirement is satisfied -# -proc assert_spec {spec} { - global specs - if {[lsearch $specs $spec] == -1} { - puts stderr "Test requires '$spec'" - exit 0 - } -} - - -## -# Build genode targets specified as space-separated strings -# -# If the build process fails, this procedure will exit the program with -# the error code -4. -# -proc build {targets} { - - if {[get_cmd_switch --skip-build]} return - - regsub -all {\s\s+} $targets " " targets - puts "building targets: $targets" - set timeout 10000 - set pid [eval "spawn make $targets"] - expect { eof { } } - if {[lindex [wait $pid] end] != 0} { - puts "Error: Genode build failed" - exit -4 - } - puts "genode build completed" -} - - -## -# Create a fresh boot directory -# -proc create_boot_directory { } { } - - -## -# Append string to variable only if 'condition' is satisfied -# -proc append_if {condition var string} { - upvar $var up_var - if {$condition} { append up_var $string } -} - - -## -# Append element to list only if 'condition' is satisfied -# -proc lappend_if {condition var string} { - upvar $var up_var - if {$condition} { lappend up_var $string } -} - - -## -# Check syntax of specified XML file using xmllint -# -proc check_xml_syntax {xml_file} { - - if {![have_installed xmllint]} { - puts "Warning: Cannot validate config syntax (please install xmllint)" - return; - } - - if {[catch {exec xmllint --noout $xml_file} result]} { - puts stderr $result - puts stderr "Error: Invalid XML syntax in file [run_dir]/config" - exit 1 - } -} - - -## -# Install content of specfied variable as init config file -# -proc install_config {config} { - set fh [open "[run_dir]/genode/config" "WRONLY CREAT TRUNC"] - puts $fh $config - close $fh - - check_xml_syntax [run_dir]/genode/config -} - - -## -# Integrate specified binaries into boot image -# -# \param binaries space-separated list of file names located within the -# '/bin/' directory -# -# This function should be implemented by a platform-specific file -# included via the '--include' argument. -# -proc build_boot_image {binaries} { } - - -## -# Execute Genode -# -# \param wait_for_re regular expression that matches the test completion -# \param timeout_value timeout in seconds -# \param spawn_id spawn_id of a already running and spawned process -# spawn_id may be a list of spawned processes if needed -# \global output contains the core output (modified) -# -# If the function is called without any argument, Genode is executed in -# interactive mode. -# -# If the test execution times out, this procedure will exit the program with -# the error code -2. -# -# This function must be implemented by the platform-specific test environment. -# If not implemented, the program exits with the error code -3. -# -proc run_genode_until {{wait_for_re forever} {timeout_value 0} {running_spawn_id -1}} { - puts stderr "Error: 'run_genode_until' is not implemented for this platform" - exit -3 -} - - -## -# Remove color information from output -# -proc filter_out_color_escape_sequences { } { - global output - regsub -all {\e\[.*?m} $output "" output -} - - -## -# Remove superfluous empty lines and unify line endings from output -# -proc trim_lines { } { - global output - regsub -all {[\r\n]+} $output "\n" output -} - - -## -# Filter output based on the specified pattern -# -# Only those lines that match the pattern are preserved. -# -proc grep_output {pattern} { - global output - - filter_out_color_escape_sequences - - trim_lines - - set output_list [split $output "\n"] - set filtered "" - foreach line $output_list { - if {[regexp $pattern $line]} { - append filtered "$line\n" - } - } - set output $filtered -} - - -## -# Unify known variations that appear in the test output -# -# \global output test output (modified) -# -proc unify_output {pattern replacement} { - global output - regsub -all $pattern $output $replacement output -} - - -## -# Compare output against expected output line by line -# -# \param good expected test output -# \global output test output -# -# This procedure will exit the program with the error code -1 if the -# comparison fails. -# -proc compare_output_to { good } { - global output - set output_list [split [strip_whitespace $output] "\n"] - set good_list [split [strip_whitespace $good] "\n"] - - set i 0 - set mismatch_cnt 0 - foreach good_line $good_list { - set output_line [strip_whitespace [lindex $output_list $i]] - set good_line [strip_whitespace $good_line] - - if {$output_line != $good_line} { - puts "" - puts stderr "Line $i of output is unexpected" - puts stderr " expected: '$good_line'" - puts stderr " got: '$output_line'" - incr mismatch_cnt - } - incr i - } - - # - # if $good is empty the foreach-loop isn't entered - # so we've to check for it separately - # - if {![llength $good_list] && [llength $output_list]} { - foreach output_line $output_list { - set output_line [strip_whitespace $output_line] - puts "" - puts stderr "Line $i of output is unexpected" - puts stderr " got: '$output_line'" - incr mismatch_cnt - incr i - } - } - - if {$mismatch_cnt > 0} { - puts "Error: Test failed, $mismatch_cnt unexpected lines of output" - exit -1 - } else { - puts "Test succeeded" - } -} - - -## -# Return true if command-line switch was specified -# -proc get_cmd_switch { arg_name } { - global argv - return [expr [lsearch $argv $arg_name] >= 0] -} - - -## -# Return command-line argument value -# -# If a argument name is specified multiple times, a -# list of argument values is returned. -# -proc get_cmd_arg { arg_name default_value } { - global argv - - # find argument name in argv list - set arg_idx_list [lsearch -all $argv $arg_name] - - if {[llength $arg_idx_list] == 0} { return $default_value } - - set result {} - foreach arg_idx $arg_idx_list { - set next_idx [expr $arg_idx + 1] - - # stop if argv ends with the argument name - if {$next_idx >= [llength $argv]} continue - - # return list element following the argument name - lappend result [lindex $argv $next_idx] - } - - # if argument occurred only once, return its value - if {[llength $result] == 1} { return [lindex $result 0] } - - # if argument occurred multiple times, contain list of arguments - return $result -} - - -# -# Read command-line arguments -# - -set run_name [get_cmd_arg --name "noname"] -set genode_dir [get_cmd_arg --genode-dir ""] -set cross_dev_prefix [get_cmd_arg --cross-dev-prefix ""] -set specs [get_cmd_arg --specs ""] -set repositories [get_cmd_arg --repositories ""] -set qemu_args [get_cmd_arg --qemu-args ""] -set run_target [get_cmd_arg --target "qemu"] -set serial_cmd [get_cmd_arg --serial-cmd "picocom -b 115200 /dev/ttyUSB0"] - - - -# -# Enable run scripts to extend 'qemu_arg' via 'append' without bothering -# about the required whitespace in front of the custom arguments. -# -append qemu_args " " - -# accessor functions for command-line arguments -proc run_name { } { global run_name; return $run_name } -proc run_dir { } { global run_name; return var/run/$run_name } -proc genode_dir { } { global genode_dir; return $genode_dir } -proc cross_dev_prefix { } { global cross_dev_prefix; return $cross_dev_prefix } - -# set expect match-buffer size -match_max -d 20000 - -## -# Return true if spec value is set for the build -# -proc have_spec {spec} { global specs; return [expr [lsearch $specs $spec] != -1] } - - -## -# Return true if specified program is installed -# -proc have_installed {program} { - if {[catch { exec which $program }]} { return false; } - return true -} - - -## -# Return true if specified program is installed on the host platform -# -proc requires_installation_of {program} { - if {![have_installed $program]} { - puts "Run script aborted because $program is not installed"; exit - } -} - - -## -# Return first repository containing the given path -# -proc repository_contains {path} { - global repositories; - foreach i $repositories { - if {[file exists $i/$path]} { return $i } - } -} - - -## -## Utilities for performing steps that are the same on several platforms -## - -## -# Read kernel location from build-directory configuration -# -# If config file does not exist or if there is no 'KERNEL' declaration in the -# config file, the function returns 'default_location'. If the config file -# points to a non-existing kernel image, the function aborts with the exit -# value -6. -# -proc kernel_location_from_config_file { config_file default_location } { - global _kernel - - if {![info exists _kernel]} { - if {[file exists $config_file]} { - set _kernel [exec sed -n "/^KERNEL/s/^.*=\\s*//p" $config_file] - - # check if the regular expression matched - if {$_kernel != ""} { - if {[file exists $_kernel]} { - return $_kernel - } else { - puts stderr "Error: kernel specified in '$config_file' does not exist" - exit -6 - } - } - } - - # try to fall back to version hosted with the Genode build directory - set _kernel $default_location - } - return $_kernel -} - - -## -# Install files needed to create a bootable ISO image -# -# The ISO boot concept uses isolinux to load GRUB, which in turn loads Genode. -# This way we can make use of isolinux' support for booting ISO images from a -# USB stick. -# -proc install_iso_bootloader_to_run_dir { } { - - exec mkdir -p [run_dir]/boot/isolinux - exec cp [genode_dir]/tool/boot/chain.c32 [run_dir]/boot/isolinux - exec cp [genode_dir]/tool/boot/isolinux.bin [run_dir]/boot/isolinux - exec cp [genode_dir]/tool/boot/isolinux.cfg [run_dir]/boot/isolinux - - exec mkdir -p [run_dir]/boot/grub - exec cp [genode_dir]/tool/boot/stage2_eltorito [run_dir]/boot/grub -} - - -## -# Copy the specified binaries from the 'bin/' directory to the run -# directory and try to strip executables. -# -proc copy_and_strip_genode_binaries_to_run_dir { binaries } { - - foreach binary $binaries { - exec cp bin/$binary [run_dir]/genode - catch { - exec [cross_dev_prefix]strip [run_dir]/genode/$binary || true } - } -} - - -## -# Create ISO image with the content of the run directory -# -proc create_iso_image_from_run_dir { } { - - puts "creating ISO image..." - exec rm -f "[run_dir].iso" - - # - # The 'create_iso' write diagnostics to stderr, which are interpreted as - # execution failure by expect unless '-ignorestderr' is set on 'exec'. - # - if {[catch {exec -ignorestderr [genode_dir]/tool/create_iso iso ISO=[run_dir]} ]} { - puts stderr "Error: ISO image creation failed" - exit -5 - } -} - - -## -# Create disk image with the content of the run directory -# -# optional parameter: --disk-size ... disk size in MiB -# -proc create_disk_image_from_run_dir { } { - - global run_target - - if {![regexp "disk" $run_target]} { return } - - requires_installation_of parted - requires_installation_of resize2fs - requires_installation_of fallocate - - set grub_img "[genode_dir]/tool/grub2-head.img" - set disk_img "[run_dir].img" - set part1_img "[run_dir]-part1.img" - set run_size [expr [regsub {\s.*} [exec du -sm [run_dir]] {}] + 4] - set disk_size [get_cmd_arg --disk-size $run_size] - set part1_size [expr $disk_size - 1]MiB - - # extract and resize partition image - exec dd if=$grub_img of=$part1_img bs=1M skip=1 2>/dev/null - exec fallocate -l $part1_size $part1_img - exec resize2fs $part1_img 2>/dev/null - - # populate partition with binaries - exec [genode_dir]/tool/rump -F ext2fs -p [run_dir] $part1_img - - # merge final image from GRUB2 head and partition - exec dd if=$grub_img of=$disk_img status=noxfer bs=1M count=1 2>/dev/null - exec dd if=$part1_img of=$disk_img status=noxfer bs=1M seek=1 2>/dev/null - exec parted -s $disk_img -- rm 1 mkpart primary 2048s -1s set 1 boot on - - exec rm -f $part1_img - - puts "Created image file $disk_img ($disk_size MiB)" -} - - -## -# 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 -3 } - timeout { puts stderr "Error: Test execution timed out"; exit -2 } - } - set output $expect_out(buffer) -} - -## -# Execute scenario using Qemu -# -proc spawn_qemu { wait_for_re timeout_value } { - global qemu_args - global qemu - global spawn_id - global run_target - - # - # Back out on platforms w/o Qemu support - # - if {![is_qemu_available]} { return 0 } - - if {[have_spec x86_32]} { set qemu "qemu-system-i386" } - if {[have_spec x86_64]} { set qemu "qemu-system-x86_64" } - if {[have_spec arm]} { set qemu "qemu-system-arm" } - - # - # Only the x86_64 variant of Qemu provides the emulation of hardware - # virtualization features used by NOVA. So let's always stick to this - # varient of Qemu when working with NOVA even when operating in 32bit. - # - if {[have_spec nova]} { set qemu "qemu-system-x86_64" } - - # - # Redirect serial output to stdio, but only in graphics mode and no - # explicit configuration of serial interfaces is specified in the run - # script. The 'mon' prefix enables the access to the qemu console. - # - if {![regexp -- {-nographic} $qemu_args dummy] && - ![regexp -- {-serial} $qemu_args dummy]} { - append qemu_args " -serial mon:stdio " } - - # tweak emulated platform for specific platforms - if {[have_spec platform_pbxa9]} { - # - # For PBXA9 qemu adjusts provided RAM chips to the -m arg. Thus we - # filter user values and force value that enables all chips that Genode - # expects to be available. Not doing so leads to inexplicable errors. - # - regsub -all {\-m ([0-9])+} $qemu_args "" qemu_args - append qemu_args " -m 768" - append qemu_args " -M realview-pbx-a9" - } - if {[have_spec platform_vpb926]} { append qemu_args " -M versatilepb -m 128 " } - if {[have_spec platform_vea9x4]} { append qemu_args " -M vexpress-a9 -cpu cortex-a9 -m 256 " } - - # on x86, we support booting via pxe or iso image [default] - if {[have_spec x86]} { - if {[regexp "qemu" $run_target] && [regexp "pxe" $run_target]} { - append qemu_args " -boot n -tftp [run_dir] -bootp boot/pulsar -no-reboot -no-shutdown " - } else { - append qemu_args " -cdrom [run_dir].iso " - } - } - - # on ARM, we supply the boot image as kernel - if {[have_spec arm]} { append qemu_args " -kernel [run_dir]/image.elf " } - - eval spawn $qemu $qemu_args - set qemu_spawn_id $spawn_id - wait_for_output $wait_for_re $timeout_value $qemu_spawn_id -} - - -## -# Check whether Qemu support is available -# -proc is_qemu_available { } { - global run_target - - if {![regexp "qemu" $run_target]} { return false } - - if {[have_spec linux]} { return false } - - if {[have_spec platform_panda] - || [have_spec platform_arndale] - || [have_spec platform_rpi]} { - puts stderr "skipping execution because platform is not supported by qemu" - return false - } - return true -} - - -## -# Check whether AMT support is available -# -proc is_amt_available { } { - global run_target - - if {![have_spec x86] || ![regexp "amt" $run_target]} { return false } - - if {[info exists ::env(AMT_TEST_MACHINE_IP)] && - [info exists ::env(AMT_TEST_MACHINE_PWD)] && - [have_installed amtterm] && - [expr [have_installed amttool] || [have_installed wsman] ] } { - return true - } - puts "No support for Intel's AMT detected." - return false -} - - -## -# Check whether output is expected via a local attached serial device -# -proc is_serial_available { } { - global run_target - - if {![regexp "serial" $run_target]} { return false } - - return true -} - - -## -# Reset via Intel AMT (works up to version smaller Intel AMT 9) -# -proc amt_reset_soap_eoi { } { - set timeout 20 - set exit_result 1 - - # - # amttool expects in the environment variable AMT_PASSWORD the password - # - set ::env(AMT_PASSWORD) $::env(AMT_TEST_MACHINE_PWD) - - while { $exit_result != 0 } { - set try_again 0 - set time_start [ clock seconds ] - spawn amttool $::env(AMT_TEST_MACHINE_IP) reset - expect { - "host" { send "y\r"; } - eof { puts "Error: amttool died unexpectedly"; exit -4 } - timeout { puts "Error: amttool timed out"; exit -5 } - } - expect { - "result: pt_status: success" { break } - eof { set try_again 1 } - timeout { puts "Error: amttool timed out"; exit -6 } - } - catch wait result - set time_end [ clock seconds ] - if {[expr $time_end - $time_start] <= 1} { - incr timeout -1 - } else { - incr timeout [expr -1 * ($time_end - $time_start)] - } - if {$timeout < 0} { - set timeout 0 - } - if {$try_again != 0 } { - continue - } - - set exit_result [lindex $result 3] - } -} - - -## -# Reset via Intel AMT wsman protocol -# -proc amt_reset_wsman { } { - set xml_request "amt-reset-wsman.xml" - - set fh [open $xml_request "WRONLY CREAT TRUNC"] - - puts $fh { - - - 5 - - http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous - - http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ComputerSystem - - CIM_ComputerSystem - ManagedSystem - - - - - } - - close $fh - - exec wsman invoke -a RequestPowerStateChange -J $xml_request "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService?SystemCreationClassName=\"CIM_ComputerSystem\",SystemName=\"Intel(r) AMT\",CreationClassName=\"CIM_PowerManagementService\",Name=\"Intel(r) AMT Power Management Service\"" --port 16992 -h $::env(AMT_TEST_MACHINE_IP) --username admin -p $::env(AMT_TEST_MACHINE_PWD) -V -v -} - - -## -# Execute scenario using Intel's AMT -# -proc spawn_amt { wait_for_re timeout_value } { - global spawn_id - - if {![is_amt_available]} { return 0 } - - # - # amttool and wsman are supported for reset - # - set amt_tool [get_cmd_arg --amt-tool "default"] - - # - # reset the box - # - if {[have_installed wsman] && ( $amt_tool == "wsman" || $amt_tool == "default") } { - amt_reset_wsman - } else { - if {[have_installed amttool] && ($amt_tool == "amttool" || $amt_tool == "default") } { - amt_reset_soap_eoi - } else { - puts stderr "specified tool \"$amt_tool\" for using Intel AMT is unknown or is not installed" - exit -1 - } - } - - sleep 5 - - # - # grab output - # - set amtterm "amtterm -u admin -p $::env(AMT_TEST_MACHINE_PWD) -v $::env(AMT_TEST_MACHINE_IP)" - if {$wait_for_re == "forever"} { - set timeout -1 - } else { - set timeout [expr $timeout_value + 30] - } - set exit_result 1 - - while { $exit_result != 0 } { - set time_start [ clock seconds ] - set pid [eval "spawn $amtterm"] - expect { - -re $wait_for_re { break } - eof { continue } - timeout { puts "Error: Test execution timed out"; exit -2 } - } - catch wait result - set time_end [ clock seconds ] - if {[expr $time_end - $time_start] <= 1} { - incr timeout -1 - } else { - incr timeout [expr -1 * ($time_end - $time_start)] - } - if {$timeout < 0} { - set timeout 0 - } - set exit_result [lindex $result 3] - } - global output - set output $expect_out(buffer) -} - - -## -# Reset test machine via IP power plug NETIO-230B from Koukaam -# -proc power_plug_connect {} { - set server_ip [get_cmd_arg --reset-ip 1] - set user_name [get_cmd_arg --reset-user 1] - set password [get_cmd_arg --reset-passwd 1] - - spawn telnet $server_ip 1234 - set connection_id $spawn_id - expect -i $connection_id "KSHELL V1.*" - send -i $connection_id "login $user_name $password\n" - expect -i $connection_id "250 OK" - - return $connection_id -} - - -proc power_plug_reset {} { - set power_port [get_cmd_arg --reset-port 1] - - set connection_id [power_plug_connect] - - send -i $connection_id "port $power_port\n" - expect -i $connection_id -re {250 [0-9]+.*\n} - regexp -all {[0-9]+} $expect_out(0,string) power_status - if {!$power_status} { - puts "port $power_port is off - switching it on" - send -i $connection_id "port $power_port 1\n" - expect -i $connection_id "250 OK" - } else { - puts "port $power_port is on - reset port" - send -i $connection_id "port $power_port int\n" - expect -i $connection_id "250 OK" - } -} - - -## -# Overwrite exit handler to switch off power plug adapter at script exit -# -rename exit power_plug_off_exit -proc exit {{status 0}} { - global run_target - if {[regexp "reset" $run_target]} { - set power_port [get_cmd_arg --reset-port 1] - set connection_id [power_plug_connect] - - puts "switch port $power_port off" - send -i $connection_id "port $power_port 0\n" - expect -i $connection_id "250 OK" - } - power_plug_off_exit $status -} - - -## -# Load image to target hardware via JTAG -# -proc jtag_load { } { - if {![have_spec arm] || ![have_installed openocd]} { - puts "No support for JTAG detected." - exit -1 - } - - set debugger [get_cmd_arg --jtag-debugger 1] - set board [get_cmd_arg --jtag-board 1] - set elf_img "[run_dir]/image.elf" - - # sleep a bit, board might need some time to come up - sleep 8 - - # parse ELF entrypoint - set entrypoint [exec [cross_dev_prefix]readelf -h $elf_img | \ - grep "Entry point address: " | \ - sed -e "s/.*Entry point address: *//"] - - eval spawn openocd -f $debugger -f $board -c init -c halt -c \"load_image $elf_img\" -c \"resume $entrypoint\" - set jtag_spawn_id $spawn_id - set timeout 360 - expect { - "downloaded" { } - eof { puts stderr "openocd command process died unexpectedly" } - timeout { puts stderr "Loading timed out" } - } -} - -## -# Execute scenario expecting output via serial device -# -proc spawn_serial { wait_for_re timeout_value kernel_msg } { - global spawn_id - global serial_cmd - global run_target - - set retry 3 - while { $retry != 0 } { - - if {[regexp "reset" $run_target]} { - power_plug_reset - } - - if {[regexp "jtag" $run_target]} { - jtag_load - } - - set timeout 210 - while {true} { - eval spawn $serial_cmd - set serial_spawn_id $spawn_id - expect { - $kernel_msg { - wait_for_output $wait_for_re $timeout_value $serial_spawn_id; - return; - } - eof { continue; } - timeout { - puts stderr "Boot process timed out"; - close; - incr retry -1; - break; - } - } - } - } - - puts stderr "Boot process failed 3 times in series. I give up!"; - exit -1; -} - - -## -# Determine terminal program -# -proc terminal { } { - global env - if {[info exists env(COLORTERM)]} { - return $env(COLORTERM) - } - return $env(TERM) -} - - -## -# Determine GDB executable installed at the host -# -proc gdb { } { - if {[have_installed "[cross_dev_prefix]gdb"]} { - return "[cross_dev_prefix]gdb" } - - if {[have_installed gdb]} { - return "gdb" } - - requires_installation_of gdb -} - - -## -# Check if a shell command is installed -# -# \param command name of the command to search -# -# \return absolute path of command if cound, or exists if not -# -proc check_installed {command} { - if { [catch {set path [exec which $command]}] == 0} { - return $path - } - - set dir { /sbin /usr/sbin /usr/local/bin } - - foreach location $dir { - append location / $command - - if { [file exists $location] == 1} { - return $location - } - } - - puts stderr "Error: '$command' command could be not found. Please make sure to install the" - puts stderr " packet containing '$command', or make it avaiable in your PATH variable.\n" - - exit 1 -} - - -## -# U-boot bootloader specific uImage -# -# \param elf_img ELF binary to build uImage from -# -proc build_uboot_image {elf_img} { - global run_target - - if {[regexp "uboot" $run_target]} { - - # parse ELF entrypoint and load address - set entrypoint [exec [cross_dev_prefix]readelf -h $elf_img | \ - grep "Entry point address: " | \ - sed -e "s/.*Entry point address: *//"] - set load_addr [exec [cross_dev_prefix]readelf -l $elf_img | \ - grep -m 1 "LOAD"] - set load_addr [lindex [regexp -inline -all -- {\S+} $load_addr] 3] - - # compress ELF - set bin_img "[run_dir]/image.bin" - exec [cross_dev_prefix]objcopy -O binary $elf_img $bin_img - exec gzip --best --force $bin_img - - # create compressed uImage - set uboot_img [run_dir]/uImage - exec mkimage -A arm -O linux -T kernel -C gzip -a $load_addr \ - -e $entrypoint -d $bin_img.gz $uboot_img - exec rm -rf $bin_img.gz - } -} - - -## -## Execution of run scripts -## - -# -# Read and execute files specified as '--include' arguments -# - -foreach include_name [get_cmd_arg --include ""] { - puts "using run script $include_name" - source $include_name -} diff --git a/tool/run/README b/tool/run/README new file mode 100644 index 000000000..9375b7476 --- /dev/null +++ b/tool/run/README @@ -0,0 +1,141 @@ + + + =================== + The Genode run tool + =================== + +Introduction +############ + +The run tool is used to configure, build, and execute so-called run scripts. +These run scripts include a scenario or test-case of Genode components and +subsystems. Its core functionality is split into various modules to accommodate +a variety of different execution environments. These modules provide the +implementation of a given step in the sequence of execution of a run script. +These steps are: + +* Building the corresponding components +* Wrapping the components in a format suitable for execution +* Preparing the target systems +* Executing the scenario +* Collecting any output + +After the run script was executed successfully, the run tool will print the +string 'Run script execution successful.". This message can be used to check +for the successful completion of the run script when doing automated testing. + +The categories of modules are formed by existing requirements such as automated +testing on a variety of different hardware platforms and are based +on the above-named steps: + +:boot_dir: + These modules contain the functionality to populate the boot directory + and are specific to each kernel. It is mandatory to always include the + module corresponding to the used kernel. + +:image modules: + These modules are used to wrap up all components used by the run script + in a specific format and thereby to prepare them for execution. + Depending on the used kernel, there are different formats. With these + modules, the creation of ISO and disk images is also handled. + +:load modules: + These modules handle the way the components are transfered to the + target system. Depending on the used kernel there are various options + to pass on the components. Loading from TFTP or via JTAG is handled + by the modules of this category. + +:log modules: + These modules handle how the output of a currently executed run script + is captured. + +:power_on modules: + These modules are used for bringing the target system into an defined + state, e.g., by starting or rebooting the system. + +:power_off modules: + These modules are used for turning the target system off after the + execution of a run script. + +When executing a run script, only one module of each category must be used. + + +Usage +##### + +To execute a run script a combination of modules may be used. The combination +is controlled via the RUN_OPT variable used by the build framework. Here are a +few common exemplary combinations: + +Executing NOVA in Qemu: + +!RUN_OPT = --include boot_dir/nova \ +! --include exec/qemu --include log/qemu --include image/iso + +Executing NOVA on a real x86 machine using AMT for resetting the target system +and for capturing the serial output while loading the files via TFTP: + +!RUN_OPT = --include boot_dir/nova \ +! --include power_on/amt --power-on-amt-host 10.23.42.13 \ +! --power-on-amt-password 'foo!' \ +! --include load/tftp --load-tftp-base-dir /var/lib/tftpboot \ +! --load-tftp-offset-dir /x86 \ +! --include log/amt --log-amt-host 10.23.42.13 \ +! --log-amt-password 'foo!' + +Executing fiasco.OC on a real x86 machine using AMT for resetting, USB serial +for output while loading the files via TFTP: + +!RUN_OPT = --include boot_dir/foc \ +! --include power_on/amt --amt-host 10.23.42.13 --amt-password 'foo!' \ +! --include load/tftp --tftp-base-dir /var/lib/tftpboot \ +! --tftp-offset-dir /x86 \ +! --include log/serial --serial-cmd 'picocom -b 115200 /dev/ttyUSB0' + +Executing hw on a rpi using powerplug to reset the hardware, JTAG to load the +image and USB serial to capture the output: + +!RUN_OPT = --include boot_dir/hw \ +! --include power_on/powerplug --power-on-powerplug-ip 10.23.42.5 \ +! --power-on-powerplug-user admin \ +! --power-on-powerplug-password secret \ +! --power-on-powerplug-port 1 +! --include power_off/powerplug --power-off-powerplug-ip 10.23.42.5 \ +! --power-off-powerplug-user admin \ +! --power-off-powerplug-password secret \ +! --power-off-powerplug-port 1 +! --include load/jtag \ +! --load-jtag-debugger /usr/share/openocd/scripts/interface/flyswatter2.cfg \ +! --load-jtag-board /usr/share/openocd/scripts/interface/raspberrypi.cfg \ +! --include log/serial --log-serial-cmd 'picocom -b 115200 /dev/ttyUSB0' + + +Module overview +############### + +A module consist of a expect/TCL source file located in one of the existing +directories of a category. It is named implicitly by its location and the +name of the source file, e.g. 'image/iso' is the name of the image module +that creates an ISO image. + +The source file contains one mandatory procedure: + +* run_ { } + The procedure is called if the step at hand is executed by the + run tool. If its execution was successful, it returns true and + otherwise false. Certain modules may also call exit on failure. + +A module may have arguments, which are by convention prefixed with the name +of the source file, e.g. 'power_on/amt' may have an argument called +'--power-on-amt-host'. If the argument passes on a value the value must be +made accessible by calling an equally named procedure, e.g. +'--power-on-amt-host' becomes 'proc amt_host { }'. +Thereby a run script or a run environment can access the value of the argument +in a defined way without the use of a global variable by using +'[power_on_amt_host]'. Also arguments without a value may be queried in this +way. '--image-uboot-gzip' becomes '[image_uboot_use_gzip]'. +In addition to these procedures, a module may have any number of public +procedures. They may be used after the presence of the particular module that +contains them is verified. For this reason the run tool provides a procedure +called 'have_include', that performs this check. For example the presence of +the 'load/tftp' module is checked by calling '[have_include "load/tftp"]'. diff --git a/tool/run/amt.inc b/tool/run/amt.inc new file mode 100644 index 000000000..62d887cae --- /dev/null +++ b/tool/run/amt.inc @@ -0,0 +1,28 @@ +## +# Check whether AMT support is available +# +proc is_amt_available { {host "" } {password "" } } { + if {![have_spec x86]} { return false } + + # + # Exit execution if parameter are not set rather returning + # false because we cannot recover anyway. + # + if {[string compare $host ""] == 0} { + puts "Aborting, AMT host not specified." + exit -1 + } + + if {[string compare $password ""] == 0} { + puts "Aborting, AMT password not set." + exit -1 + } + + if {[have_installed amtterm] && + [expr [have_installed amttool] || [have_installed wsman] ] } { + return true + } + + puts "No support for Intel's AMT detected." + return false +} diff --git a/repos/base-codezero/run/env b/tool/run/boot_dir/codezero similarity index 61% rename from repos/base-codezero/run/env rename to tool/run/boot_dir/codezero index 7c9a0eb01..c196ae70e 100644 --- a/repos/base-codezero/run/env +++ b/tool/run/boot_dir/codezero @@ -1,10 +1,3 @@ -# -# \brief Codezero-specific test-environment supplements -# \author Norman Feske -# \date 2011-08-05 -# -# This file is meant to be used as '--include' argument for 'tool/run'. -# ## @@ -34,21 +27,10 @@ proc exec_sh { command } { } -################################## -## Test framework API functions ## -################################## - -proc create_boot_directory { } { - - # create only intermediate directries hosting the run directory - exec mkdir -p [run_dir] - exec rm -rf [run_dir] - - exec mkdir -p [run_dir]/genode -} - - -proc build_boot_image {binaries} { +## +# Populate boot directory with binaries on codezero +# +proc run_boot_dir {binaries} { if {![file exists kernel]} { build kernel } @@ -81,26 +63,3 @@ proc build_boot_image {binaries} { # copy result to [run_dir]/image.elf (to be picked up by spawn_qemu) exec_sh "cp [kernel_dir]/build/final.elf [run_dir]/image.elf" } - - -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 - } - - # - # Try to use one of the supported backends for running the scripts - # - if {[is_qemu_available]} { - spawn_qemu $wait_for_re $timeout_value - return - } - - global run_target - puts stderr "Error: Can't execute automatically on target '$run_target'" - exit -1 -} diff --git a/tool/run/boot_dir/fiasco b/tool/run/boot_dir/fiasco new file mode 100644 index 000000000..3e9e518c5 --- /dev/null +++ b/tool/run/boot_dir/fiasco @@ -0,0 +1,142 @@ +## +# Read the location of the Fiasco user directory from 'etc/fiasco.conf' +# +proc l4_dir { } { + global _l4_dir + + if {![info exists _l4_dir]} { + if {[file exists etc/fiasco.conf]} { + set _l4_dir [exec sed -n "/^L4_BUILD_DIR/s/^.*=\\s*//p" etc/fiasco.conf] + if {[file exists $_l4_dir]} { return $_l4_dir } + } + + set _l4_dir "[pwd]/l4" + if {![file exists $_l4_dir]} { + puts -nonewline stderr "Error: Could neither find the L4 build directory " + puts -nonewline stderr "within '/l4' nor at a location " + puts -nonewline stderr "specified via 'L4_BUILD_DIR = ' " + puts stderr "in /etc/fiasco.conf'." + exit 1 + } + } + return $_l4_dir +} + + +## +# Return whether the l4-buid-directory is provided from the outside +# +proc l4_dir_external { } { + if {[l4_dir] == "[pwd]/l4"} { return 0 } + return 1 +} + + +## +# Return the location of the Fiasco kernel +# +proc fiasco { } { + return [kernel_location_from_config_file etc/fiasco.conf [pwd]/kernel/fiasco/fiasco] +} + + +## +# Return whether fiasco kernel is provided from the outside +# +proc fiasco_external { } { + if {[fiasco] == "[pwd]/kernel/fiasco/fiasco"} { return 0 } + return 1 +} + + +proc bin_dir { } { + if {[have_spec x86_32]} { return "[l4_dir]/bin/x86_586" } + + puts stderr "Error: Cannot determine bin directory" + exit 1 +} + +set fiasco_serial_esc_arg "-serial_esc " + + +## +# Populate boot directory with binaries on fiasco +# +proc run_boot_dir {binaries} { + + global fiasco_serial_esc_arg + + exec mkdir -p [run_dir]/fiasco + + # + # Collect contents of the ISO image + # + copy_and_strip_genode_binaries_to_run_dir $binaries + + if {![fiasco_external]} { build { kernel } } + if {![l4_dir_external]} { build { bootstrap sigma0 } } + + # assert existence of the L4 build directory + l4_dir + + puts "using fiasco kernel [fiasco]" + exec cp [fiasco] [run_dir]/fiasco/fiasco + puts "using sigma0/bootstrap at [l4_dir]" + exec cp [bin_dir]/l4v2/sigma0 [run_dir]/fiasco + exec cp [bin_dir]/bootstrap [run_dir]/fiasco + + if {[have_include "image/iso"] || [have_include "image/disk"]} { + # + # Install isolinux/GRUB files and bender + # + install_iso_bootloader_to_run_dir + + # + # Generate GRUB config file + # + # The core binary is part of the 'binaries' list but it must + # appear right after 'sigma0' as boot module. Hence the special case. + # + set fh [open "[run_dir]/boot/grub/menu.lst" "WRONLY CREAT TRUNC"] + puts $fh "timeout 0" + puts $fh "default 0" + puts $fh "\ntitle Genode on L4/Fiasco" + puts $fh " kernel /boot/bender" + puts $fh " module /fiasco/bootstrap -serial -modaddr=0x02000000" + puts $fh " module /fiasco/fiasco -serial -jdb_cmd=JH $fiasco_serial_esc_arg" + puts $fh " module /fiasco/sigma0" + puts $fh " module /genode/core" + puts $fh " module /genode/config" + foreach binary $binaries { + if {$binary != "core"} { + puts $fh " module /genode/$binary" } } + puts $fh " vbeset 0x117 506070" + close $fh + } + + run_image + + if {[have_include "load/tftp"]} { + # + # Install PXE bootloader pulsar + # + install_pxe_bootloader_to_run_dir + + # + # Generate pulsar config file + # + set fh [open "[run_dir]/config-52-54-00-12-34-56" "WRONLY CREAT TRUNC"] + puts $fh " exec /boot/bender" + puts $fh " load /fiasco/bootstrap -serial -modaddr=0x02000000" + puts $fh " load /fiasco/fiasco -serial -serial_esc -jdb_cmd=JH" + puts $fh " load /fiasco/sigma0" + puts $fh " load /genode/core" + puts $fh " load /genode/config" + foreach binary $binaries { + if {$binary != "core"} { + puts $fh " load /genode/$binary" } } + close $fh + + generate_tftp_config + } +} diff --git a/repos/base-foc/run/env b/tool/run/boot_dir/foc similarity index 52% rename from repos/base-foc/run/env rename to tool/run/boot_dir/foc index 3418e2f73..62b2b6be1 100644 --- a/repos/base-foc/run/env +++ b/tool/run/boot_dir/foc @@ -6,14 +6,6 @@ # This file is meant to be used as '--include' argument for 'tool/run'. # -## -# Install files needed to boot via PXE -# -proc install_pxe_bootloader_to_run_dir { } { - exec cp [genode_dir]/tool/boot/pulsar [run_dir]/boot/pulsar - exec cp [genode_dir]/tool/boot/bender [run_dir]/boot/bender -} - ## # Return the location of the Fiasco.OC user directory # @@ -83,20 +75,6 @@ proc reset_target { {spawn_id_arg -1} } { send -i $spawn_id_arg "\033^^" } -################################## -## Test framework API functions ## -################################## - -proc create_boot_directory { } { - exec rm -rf [run_dir] - exec mkdir -p [run_dir]/genode - - if {[have_spec x86]} { - exec mkdir -p [run_dir]/fiasco - exec mkdir -p [run_dir]/boot/grub - } -} - proc copy_and_strip_binaries {binaries} { @@ -108,10 +86,6 @@ proc copy_and_strip_binaries {binaries} { catch { exec [cross_dev_prefix]strip [run_dir]/genode/$binary } } - - # - # Generate config file for bootstrap - # } @@ -126,10 +100,12 @@ proc bin_dir { } { set fiasco_serial_esc_arg "-serial_esc " -proc build_boot_image_x86 {binaries} { +proc run_boot_dir_x86 {binaries} { global fiasco_serial_esc_arg + exec mkdir -p [run_dir]/fiasco + copy_and_strip_binaries $binaries set foc_targets { } @@ -149,69 +125,67 @@ proc build_boot_image_x86 {binaries} { exec cp [bin_dir]/l4f/sigma0 [run_dir]/fiasco exec cp [bin_dir]/bootstrap [run_dir]/fiasco - install_iso_bootloader_to_run_dir + if {[have_include "image/iso"] || [have_include "image/disk"]} { + # + # Install isolinux/GRUB files and bender + # + install_iso_bootloader_to_run_dir - # - # Generate grub config file - # - # The core binary is part of the 'binaries' list but it must - # appear right after 'sigma0' as boot module. Hence the special case. - # - set fh [open "[run_dir]/boot/grub/menu.lst" "WRONLY CREAT TRUNC"] - puts $fh "timeout 0" - puts $fh "default 0" - puts $fh "\ntitle Genode on Fiasco.OC" - puts $fh " kernel /boot/bender" - puts $fh " module /fiasco/bootstrap -modaddr=0x01100000" - puts $fh " module /fiasco/fiasco $fiasco_serial_esc_arg" - puts $fh " module /fiasco/sigma0" - puts $fh " module /genode/core" - puts $fh " module /genode/config" - foreach binary $binaries { - if {$binary != "core"} { - puts $fh " module /genode/$binary" } } - puts $fh " vbeset 0x117 506070" - close $fh - - install_pxe_bootloader_to_run_dir - create_iso_image_from_run_dir - create_disk_image_from_run_dir - - # - # Generate pulsar config file - # - set fh [open "[run_dir]/config-52-54-00-12-34-56" "WRONLY CREAT TRUNC"] - puts $fh " exec /boot/bender" - puts $fh " load /fiasco/bootstrap -modaddr=0x01100000" - puts $fh " load /fiasco/fiasco -serial_esc" - puts $fh " load /fiasco/sigma0" - puts $fh " load /genode/core" - puts $fh " load /genode/config" - foreach binary $binaries { - if {$binary != "core"} { - puts $fh " load /genode/$binary" } } - close $fh - - # - # Generate pulsar config file pointing to the config file above. - # - if {[info exists ::env(PXE_TFTP_DIR_BASE)] && [info exists ::env(PXE_TFTP_DIR_OFFSET)]} { - exec ln -nfs "[pwd]" "$::env(PXE_TFTP_DIR_BASE)$::env(PXE_TFTP_DIR_OFFSET)" - - set tftp_base "" - if {[get_cmd_switch --tftp-absolute]} { - set tftp_base $::env(PXE_TFTP_DIR_BASE) - } - - set fh [open "$::env(PXE_TFTP_DIR_BASE)$::env(PXE_TFTP_DIR_OFFSET)/config-00-00-00-00-00-00" "WRONLY CREAT TRUNC"] - puts $fh " root $tftp_base$::env(PXE_TFTP_DIR_OFFSET)/[run_dir]" - puts $fh " config config-52-54-00-12-34-56" + # + # Generate grub config file + # + # The core binary is part of the 'binaries' list but it must + # appear right after 'sigma0' as boot module. Hence the special case. + # + set fh [open "[run_dir]/boot/grub/menu.lst" "WRONLY CREAT TRUNC"] + puts $fh "timeout 0" + puts $fh "default 0" + puts $fh "\ntitle Genode on Fiasco.OC" + puts $fh " kernel /boot/bender" + puts $fh " module /fiasco/bootstrap -modaddr=0x01100000" + puts $fh " module /fiasco/fiasco $fiasco_serial_esc_arg" + puts $fh " module /fiasco/sigma0" + puts $fh " module /genode/core" + puts $fh " module /genode/config" + foreach binary $binaries { + if {$binary != "core"} { + puts $fh " module /genode/$binary" } } + puts $fh " vbeset 0x117 506070" close $fh } + + # + # Build image + # + run_image + + if {[have_include "load/tftp"]} { + # + # Install PXE bootloader pulsar + # + install_pxe_bootloader_to_run_dir + + # + # Generate pulsar config file + # + set fh [open "[run_dir]/config-52-54-00-12-34-56" "WRONLY CREAT TRUNC"] + puts $fh " exec /boot/bender" + puts $fh " load /fiasco/bootstrap -modaddr=0x01100000" + puts $fh " load /fiasco/fiasco -serial_esc" + puts $fh " load /fiasco/sigma0" + puts $fh " load /genode/core" + puts $fh " load /genode/config" + foreach binary $binaries { + if {$binary != "core"} { + puts $fh " load /genode/$binary" } } + close $fh + + generate_tftp_config + } } -proc build_boot_image_arm {binaries} { +proc run_boot_dir_arm {binaries} { global run_target global fiasco_serial_esc_arg @@ -249,53 +223,27 @@ proc build_boot_image_arm {binaries} { } exec cp [bin_dir]/bootstrap.elf [run_dir]/image.elf - build_uboot_image [run_dir]/image.elf + run_image [run_dir]/image.elf puts "\nboot image: [run_dir]/image.elf\n" # set symbolic link to image.elf file in TFTP directory for PXE boot - if {[info exists ::env(PXE_TFTP_DIR_BASE)] && - [info exists ::env(PXE_TFTP_DIR_OFFSET)]} { - exec ln -sf "[pwd]/[run_dir]/image.elf" "$::env(PXE_TFTP_DIR_BASE)$::env(PXE_TFTP_DIR_OFFSET)" - if {[regexp "uboot" $run_target]} { - exec ln -sf "[pwd]/[run_dir]/uImage" "$::env(PXE_TFTP_DIR_BASE)$::env(PXE_TFTP_DIR_OFFSET)/uImage" + if {[have_include "load/tftp"]} { + set tftp_base_dir [load_tftp_base_dir] + set tftp_offset_dir [load_tftp_offset_dir] + + exec ln -sf [pwd]/[run_dir]/image.elf $tftp_base_dir$tftp_offset_dir + if {[have_include "image/uboot"]} { + exec ln -sf [pwd]/[run_dir]/uImage $tftp_base_dir$tftp_offset_dir/uImage } } } -proc build_boot_image {binaries} { - if {[have_spec x86]} { return [build_boot_image_x86 $binaries] } - if {[have_spec arm]} { return [build_boot_image_arm $binaries] } -} - - -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 - } - - # - # Try to use one of the supported backends for running the scripts - # - if {[is_amt_available]} { - spawn_amt $wait_for_re $timeout_value - return - } - if {[is_qemu_available]} { - spawn_qemu $wait_for_re $timeout_value - return - } - if {[is_serial_available]} { - spawn_serial $wait_for_re $timeout_value "L4 Bootstrapper" - return - } - - global run_target - puts stderr "Error: Can't execute automatically on target '$run_target'" - exit -1 +## +# Populate boot directory with binaries on fiasco.OC +# +proc run_boot_dir {binaries} { + if {[have_spec x86]} { return [run_boot_dir_x86 $binaries] } + if {[have_spec arm]} { return [run_boot_dir_arm $binaries] } } diff --git a/repos/base-hw/run/env b/tool/run/boot_dir/hw similarity index 67% rename from repos/base-hw/run/env rename to tool/run/boot_dir/hw index b3ab59262..f6d9b96c6 100644 --- a/repos/base-hw/run/env +++ b/tool/run/boot_dir/hw @@ -1,58 +1,19 @@ -#!/usr/bin/expect - -# -# \brief Implementation of the 'tool/run' interface -# \author Martin Stein -# \date 2011-12-16 -# - - -############### -## Utilities ## -############### - -# +## # Ensure that the next Genode build includes no target specific boot modules # proc clean_boot_modules { } { exec rm -rf boot_modules.s var/libcache/boot_modules/boot_modules.o } -########################## -## 'tool/run' interface ## -########################## - -proc build {targets} { - - # skip targets that shall not be build - if {[get_cmd_switch --skip-build]} return - - # handle false remnants of previous builds +proc run_boot_dir_hook { } { clean_boot_modules - - # - # Build all remaining targets. - # Core is build with a dummy boot-modules file first. - # - regsub -all {\s\s+} $targets " " targets - set timeout 10000 - set pid [eval "spawn make $targets"] - expect { eof { } } - if {[lindex [wait $pid] end] != 0} { - puts stderr "Error: Genode build failed" - exit -1 - } } -proc create_boot_directory { } { - - exec rm -rf [run_dir] - exec mkdir -p [run_dir]/genode -} - - -proc build_boot_image {binaries {core_type core}} { +## +# Populate boot directory with binaries on hw +# +proc run_boot_dir {binaries {core_type core}} { if {$core_type == "test"} { set core_bin "test-[run_name]" set core_target "test/[run_name]" @@ -90,7 +51,7 @@ proc build_boot_image {binaries {core_type core}} { exec echo -e \ "/**" \ "\n * This file was automatically generated by the procedure" \ - "\n * 'build_boot_image' in 'base-hw/run/env'." \ + "\n * 'run_boot_dir' in 'base-hw/run/env'." \ "\n */" \ "\n" \ "\n /* core includes */" \ @@ -176,15 +137,14 @@ proc build_boot_image {binaries {core_type core}} { exec cp -L bin/$core_bin $elf_img exec [cross_dev_prefix]strip $elf_img - build_uboot_image $elf_img + run_image $elf_img # set symbolic link to image.elf file in TFTP directory for PXE boot - if {[info exists ::env(PXE_TFTP_DIR_BASE)] && - [info exists ::env(PXE_TFTP_DIR_OFFSET)]} { - exec ln -sf "[pwd]/$elf_img" "$::env(PXE_TFTP_DIR_BASE)$::env(PXE_TFTP_DIR_OFFSET)" + if {[have_include "load/tftp"]} { + exec ln -sf [pwd]/$elf_img [load_tftp_base_dir][load_tftp_offset_dir] - if {[regexp "uboot" $run_target]} { - exec ln -sf "[pwd]/[run_dir]/uImage" "$::env(PXE_TFTP_DIR_BASE)$::env(PXE_TFTP_DIR_OFFSET)" + if {[have_include "image/uboot"]} { + exec ln -sf [pwd]/[run_dir]/uImage [load_tftp_base_dir][load_tftp_offset_dir] } } @@ -192,31 +152,3 @@ proc build_boot_image {binaries {core_type core}} { exec cp $core_target/$core_bin.standalone bin/$core_bin exec rm $core_target/$core_bin.standalone } - - -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 - } - - # - # Try to use one of the supported backends for running the scripts - # - if {[is_qemu_available]} { - spawn_qemu $wait_for_re $timeout_value - return - } - if {[is_serial_available]} { - spawn_serial $wait_for_re $timeout_value "kernel initialized" - return - } - - global run_target - puts stderr "Error: Can't execute automatically on target '$run_target'" - exit -1 -} - diff --git a/tool/run/boot_dir/linux b/tool/run/boot_dir/linux new file mode 100644 index 000000000..4b283eabd --- /dev/null +++ b/tool/run/boot_dir/linux @@ -0,0 +1,7 @@ +## +# Populate boot directory with binaries on Linux +# +proc run_boot_dir {binaries} { + foreach binary $binaries { + exec ln -sf ../../../../bin/$binary [run_dir]/genode } +} diff --git a/tool/run/boot_dir/nova b/tool/run/boot_dir/nova new file mode 100644 index 000000000..7057df455 --- /dev/null +++ b/tool/run/boot_dir/nova @@ -0,0 +1,90 @@ +## +# Read the location of the NOVA kernel directory from 'etc/nova.conf' +# +proc nova_kernel { } { + global _nova_kernel + + if {![info exists _nova_kernel]} { + if {[file exists etc/nova.conf]} { + set _nova_kernel [exec sed -n "/^NOVA_KERNEL/s/^.*=\\s*//p" etc/nova.conf] + } else { + set _nova_kernel "[pwd]/kernel/hypervisor" + } + } + return $_nova_kernel +} + + +## +# Return whether nova is provided from the outside +# +proc nova_external { } { + if {[nova_kernel] == "[pwd]/kernel/hypervisor"} { return 0 } + return 1 +} + + +## +# Populate directory with binaries on NOVA +# +proc run_boot_dir {binaries} { + # + # Collect contents of the ISO image + # + copy_and_strip_genode_binaries_to_run_dir $binaries + + if {![nova_external] && ![file exists [nova_kernel]]} { build { kernel } } + + puts "using NOVA kernel at [nova_kernel]" + exec [cross_dev_prefix]objcopy -O elf32-i386 [nova_kernel] [run_dir]/hypervisor + + if {[have_include "image/iso"] || [have_include "image/disk"]} { + # + # Install isolinux/GRUB files and bender + # + install_iso_bootloader_to_run_dir + + # + # Generate GRUB config file + # + set fh [open "[run_dir]/boot/grub/menu.lst" "WRONLY CREAT TRUNC"] + puts $fh "timeout 0" + puts $fh "default 0" + puts $fh "\ntitle Genode on NOVA" + puts $fh " kernel /boot/bender" + puts $fh " module /hypervisor iommu serial novpid" + puts $fh " module /genode/core" + puts $fh " module /genode/config" + foreach binary $binaries { + if {$binary != "core"} { + puts $fh " module /genode/$binary" } } + close $fh + } + + # + # Build image + # + run_image + + if {[have_include "load/tftp"]} { + # + # Install PXE bootloader pulsar + # + install_pxe_bootloader_to_run_dir + + # + # Generate pulsar config file + # + set fh [open "[run_dir]/config-52-54-00-12-34-56" "WRONLY CREAT TRUNC"] + puts $fh " exec /boot/bender" + puts $fh " load /hypervisor iommu serial novpid" + puts $fh " load /genode/core" + puts $fh " load /genode/config" + foreach binary $binaries { + if {$binary != "core"} { + puts $fh " load /genode/$binary" } } + close $fh + + generate_tftp_config + } +} diff --git a/repos/base-okl4/run/env b/tool/run/boot_dir/okl4 similarity index 61% rename from repos/base-okl4/run/env rename to tool/run/boot_dir/okl4 index 29e38a308..6036eebb1 100644 --- a/repos/base-okl4/run/env +++ b/tool/run/boot_dir/okl4 @@ -1,24 +1,9 @@ -# -# \brief OKL4-specific test-environment supplements -# \author Norman Feske -# \date 2010-08-16 -# -# This file is meant to be used as '--include' argument for 'tool/run'. -# - -## -# Install files needed to boot via PXE -# -proc install_pxe_bootloader_to_run_dir { } { - exec cp [genode_dir]/tool/boot/pulsar [run_dir]/boot/pulsar - exec cp [genode_dir]/tool/boot/bender [run_dir]/boot/bender -} - ## # Get the base-okl4 repository # proc base_okl4_dir {} { return [repository_contains mk/spec-okl4.mk] } + ## # Read the location of the OKL4 directory from 'etc/okl4.conf' # @@ -37,6 +22,7 @@ proc okl4_dir { } { return $_okl4_dir } + ## # Return the location of the OKL4 kernel # @@ -45,6 +31,7 @@ proc okl4 { } { return bin/kernel } + ## # Return whether okl4 kernel is provided from the outside # @@ -53,15 +40,6 @@ proc okl4_external { } { return 1 } -################################## -## Test framework API functions ## -################################## - -proc create_boot_directory { } { - exec rm -rf [run_dir] - exec mkdir -p [run_dir]/genode -} - set weaver_xml_template { @@ -122,7 +100,11 @@ set weaver_xml_template { } -proc build_boot_image {binaries} { + +## +# Populate directory with binaries on OKL4 +# +proc run_boot_dir {binaries} { global weaver_xml_template # @@ -179,86 +161,50 @@ proc build_boot_image {binaries} { # exec rm -r [run_dir]/genode - # - # Install GRUB - # - install_iso_bootloader_to_run_dir + if {[have_include "image/iso"] || [have_include "image/disk"]} { + # + # Install GRUB + # + install_iso_bootloader_to_run_dir - # - # Install PXE bootloader pulsar - # - install_pxe_bootloader_to_run_dir - - # - # Generate grub config file - # - # The core binary is part of the 'binaries' list but it must - # appear right after 'sigma0' as boot module. Hence the special case. - # - set fh [open "[run_dir]/boot/grub/menu.lst" "WRONLY CREAT TRUNC"] - puts $fh "timeout 0" - puts $fh "default 0" - puts $fh "hiddenmenu" - puts $fh "\ntitle Genode on OKL4" - puts $fh " kernel /boot/bender" - puts $fh " module /image.elf" - puts $fh " vbeset 0x117" - close $fh - - create_iso_image_from_run_dir - create_disk_image_from_run_dir - - # - # Generate pulsar config file - # - set fh [open "[run_dir]/config-52-54-00-12-34-56" "WRONLY CREAT TRUNC"] - # load okl4 at 256M to avoid overwritting binary, adjust by need - puts $fh " addr 0x10000000" - puts $fh " exec /boot/bender" - puts $fh " load /image.elf" - close $fh - - # - # Generate pulsar config file pointing to the config file above. - # - if {[info exists ::env(PXE_TFTP_DIR_BASE)] && [info exists ::env(PXE_TFTP_DIR_OFFSET)]} { - exec ln -nfs "[pwd]" "$::env(PXE_TFTP_DIR_BASE)$::env(PXE_TFTP_DIR_OFFSET)" - - set tftp_base "" - if {[get_cmd_switch --tftp-absolute]} { - set tftp_base $::env(PXE_TFTP_DIR_BASE) - } - - set fh [open "$::env(PXE_TFTP_DIR_BASE)$::env(PXE_TFTP_DIR_OFFSET)/config-00-00-00-00-00-00" "WRONLY CREAT TRUNC"] - puts $fh " root $tftp_base$::env(PXE_TFTP_DIR_OFFSET)/[run_dir]" - puts $fh " config config-52-54-00-12-34-56" + # + # Generate grub config file + # + # The core binary is part of the 'binaries' list but it must + # appear right after 'sigma0' as boot module. Hence the special case. + # + set fh [open "[run_dir]/boot/grub/menu.lst" "WRONLY CREAT TRUNC"] + puts $fh "timeout 0" + puts $fh "default 0" + puts $fh "hiddenmenu" + puts $fh "\ntitle Genode on OKL4" + puts $fh " kernel /boot/bender" + puts $fh " module /image.elf" + puts $fh " vbeset 0x117" close $fh } -} - - -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 - } - - # - # Try to use one of the supported backends for running the scripts - # - if {[is_amt_available]} { - spawn_amt $wait_for_re $timeout_value - return - } - if {[is_qemu_available]} { - spawn_qemu $wait_for_re $timeout_value - return - } - - global run_target - puts stderr "Error: Can't execute automatically on target '$run_target'" - exit -1 + + # + # Build image + # + run_image + + if {[have_include "load/tftp"]} { + # + # Install PXE bootloader pulsar + # + install_pxe_bootloader_to_run_dir + + # + # Generate pulsar config file + # + set fh [open "[run_dir]/config-52-54-00-12-34-56" "WRONLY CREAT TRUNC"] + # load okl4 at 256M to avoid overwritting binary, adjust by need + puts $fh " addr 0x10000000" + puts $fh " exec /boot/bender" + puts $fh " load /image.elf" + close $fh + + generate_tftp_config + } } diff --git a/tool/run/boot_dir/pistachio b/tool/run/boot_dir/pistachio new file mode 100644 index 000000000..8b6ae51c1 --- /dev/null +++ b/tool/run/boot_dir/pistachio @@ -0,0 +1,140 @@ +# +# \brief Pistachio-specific test-environment supplements +# \author Norman Feske +# \date 2010-08-25 +# +# This file is meant to be used as '--include' argument for 'tool/run'. +# + + +## +# Install files needed to boot via PXE +# +proc install_pxe_bootloader_to_run_dir { } { + exec cp [genode_dir]/tool/boot/pulsar [run_dir]/boot/pulsar + exec cp [genode_dir]/tool/boot/bender [run_dir]/boot/bender +} + + +## +# Read the location of the Pistachio user directory from 'etc/pistachio.conf' +# +proc pistachio_user_dir { } { + global _pistachio_user_dir + + if {![info exists _pistachio_user_dir]} { + if {[file exists etc/pistachio.conf]} { + set _pistachio_user_dir [exec sed -n "/^PISTACHIO_USER_BUILD_DIR/s/^.*=\\s*//p" etc/pistachio.conf] + } else { + set _pistachio_user_dir "[pwd]/l4" + } + } + return $_pistachio_user_dir +} + + +## +# Read the location of the Pistachio kernel directory from 'etc/pistachio.conf' +# or return a good heuristic +# +proc pistachio_kernel { } { + global _pistachio_kernel + + if {![info exists _pistachio_kernel]} { + if {[file exists etc/pistachio.conf]} { + set _pistachio_kernel [exec sed -n "/^PISTACHIO_KERNEL/s/^.*=\\s*//p" etc/pistachio.conf] + if {$_pistachio_kernel == ""} { + set _pistachio_kernel [file dirname [file dirname [pistachio_user_dir]]]/kernel/build/x86-kernel + } + } else { + set _pistachio_kernel "[pwd]/bin/kernel" + } + } + return $_pistachio_kernel +} + + +## +# Return whether the kernel is provided from the outside +# +proc kernel_external { } { + if {[pistachio_kernel] == "[pwd]/bin/kernel"} { return 0 } + return 1 +} + + +## +# Populdate boot directory with binaries on pistachio +# +proc run_boot_dir {binaries} { + + exec mkdir -p [run_dir]/pistachio + + # + # Collect contents of the ISO image + # + copy_and_strip_genode_binaries_to_run_dir $binaries + + if {![kernel_external] && ![file exists [pistachio_kernel]]} { build { kernel } } + + exec cp [pistachio_kernel] [run_dir]/pistachio/kernel + exec cp [pistachio_user_dir]/serv/sigma0/sigma0 [run_dir]/pistachio + exec cp [pistachio_user_dir]/util/kickstart/kickstart [run_dir]/pistachio + + if {[have_include "image/iso"] || [have_include "image/disk"]} { + # + # Install isolinux/GRUB files and bender + # + install_iso_bootloader_to_run_dir + + # + # Generate grub config file + # + # The core binary is part of the 'binaries' list but it must + # appear right after 'sigma0' as boot module. Hence the special case. + # + set fh [open "[run_dir]/boot/grub/menu.lst" "WRONLY CREAT TRUNC"] + puts $fh "timeout 0" + puts $fh "default 0" + puts $fh "\ntitle Genode on L4ka::Pistachio" + puts $fh " kernel /pistachio/kickstart" + puts $fh " module /pistachio/kernel" + puts $fh " module /pistachio/sigma0" + puts $fh " module /genode/core" + puts $fh " module /genode/config" + foreach binary $binaries { + if {$binary != "core"} { + puts $fh " module /genode/$binary" } } + close $fh + } + + # + # Build image + # + run_image + + if {[have_include "load/tftp"]} { + # + # Install PXE bootloader pulsar + # + install_pxe_bootloader_to_run_dir + + # + # Generate pulsar config file + # + set fh [open "[run_dir]/config-52-54-00-12-34-56" "WRONLY CREAT TRUNC"] + puts $fh " exec /boot/bender" + puts $fh " load /pistachio/kickstart" + puts $fh " load /pistachio/kernel" + puts $fh " load /pistachio/sigma0" + puts $fh " load /genode/core" + puts $fh " load /genode/config" + puts $fh " load /genode/config" + foreach binary $binaries { + if {$binary != "core"} { + puts $fh " load /genode/$binary" } } + close $fh + + generate_tftp_config + } +} diff --git a/tool/run/image/disk b/tool/run/image/disk new file mode 100644 index 000000000..497ddbbe6 --- /dev/null +++ b/tool/run/image/disk @@ -0,0 +1,49 @@ +## +# Create disk image with contents of the run directory +# +# \param --image-disk-size disk size in MiB +# + +source [genode_dir]/tool/run/iso.inc + + +proc image_disk_size { } { return [get_cmd_arg --image-disk-size 0] } + + +## +# Create disk image with the content of the run directory +# +proc run_image { {unused ""} } { + + requires_installation_of parted + requires_installation_of resize2fs + requires_installation_of fallocate + + set grub_img "[genode_dir]/tool/grub2-head.img" + set disk_img "[run_dir].img" + set part1_img "[run_dir]-part1.img" + set run_size [expr [regsub {\s.*} [exec du -sm [run_dir]] {}] + 4] + if {[image_disk_size] > 0} { + set disk_size [image_disk_size] + } else { + set disk_size $run_size + } + set part1_size [expr $disk_size - 1]MiB + + # extract and resize partition image + exec dd if=$grub_img of=$part1_img bs=1M skip=1 2>/dev/null + exec fallocate -l $part1_size $part1_img + exec resize2fs $part1_img 2>/dev/null + + # populate partition with binaries + exec [genode_dir]/tool/rump -F ext2fs -p [run_dir] $part1_img + + # merge final image from GRUB2 head and partition + exec dd if=$grub_img of=$disk_img status=noxfer bs=1M count=1 2>/dev/null + exec dd if=$part1_img of=$disk_img status=noxfer bs=1M seek=1 2>/dev/null + exec parted -s $disk_img -- rm 1 mkpart primary 2048s -1s set 1 boot on + + exec rm -f $part1_img + + puts "Created image file $disk_img ($disk_size MiB)" +} diff --git a/tool/run/image/iso b/tool/run/image/iso new file mode 100644 index 000000000..37cbb7225 --- /dev/null +++ b/tool/run/image/iso @@ -0,0 +1,20 @@ +source [genode_dir]/tool/run/iso.inc + + +## +# Create ISO image with the content of the run directory +# +proc run_image { {unused ""} } { + + puts "creating ISO image..." + exec rm -f "[run_dir].iso" + + # + # The 'create_iso' writes diagnostics to stderr, which are interpreted as + # execution failure by expect unless '-ignorestderr' is set on 'exec'. + # + if {[catch {exec -ignorestderr [genode_dir]/tool/create_iso iso ISO=[run_dir]} ]} { + puts stderr "Error: ISO image creation failed" + exit -5 + } +} diff --git a/tool/run/image/uboot b/tool/run/image/uboot new file mode 100644 index 000000000..bffe44381 --- /dev/null +++ b/tool/run/image/uboot @@ -0,0 +1,47 @@ +## +# Build U-boot bootloader specific uImage +# +# \param --image-uboot-no-gzip do not gzip the uImage +# + + +## +# Check if the uImage should be gzipped +# +proc image_uboot_use_no_gzip { } { return [get_cmd_switch --image-uboot-no-gzip] } + + +## +# Build U-boot bootloader specific uImage +# +# \param elf_img ELF binary to build uImage from +# +proc run_image {elf_img} { + # parse ELF entrypoint and load address + set entrypoint [exec [cross_dev_prefix]readelf -h $elf_img | \ + grep "Entry point address: " | \ + sed -e "s/.*Entry point address: *//"] + set load_addr [exec [cross_dev_prefix]readelf -l $elf_img | \ + grep -m 1 "LOAD"] + set load_addr [lindex [regexp -inline -all -- {\S+} $load_addr] 3] + + set bin_img "[run_dir]/image.bin" + exec [cross_dev_prefix]objcopy -O binary $elf_img $bin_img + + set use_gzip [expr ![image_uboot_use_no_gzip]] + set compress_type none + set bin_ext "" + + # compress ELF + if $use_gzip { + exec gzip --best --force $bin_img + set bin_ext .gz + set compress_type gzip + } + + # create uImage + set uboot_img [run_dir]/uImage + exec mkimage -A arm -O linux -T kernel -C $compress_type -a $load_addr \ + -e $entrypoint -d $bin_img$bin_ext $uboot_img + exec rm -rf $bin_img$bin_ext +} diff --git a/tool/run/iso.inc b/tool/run/iso.inc new file mode 100644 index 000000000..9225dbaef --- /dev/null +++ b/tool/run/iso.inc @@ -0,0 +1,20 @@ +## +# Install files needed to create a bootable ISO image +# +# The ISO boot concept uses isolinux to load GRUB, which in turn loads Genode. +# This way we can make use of isolinux' support for booting ISO images from a +# USB stick. +# +proc install_iso_bootloader_to_run_dir { } { + puts "install bootloader" + + exec mkdir -p [run_dir]/boot/isolinux + exec cp [genode_dir]/tool/boot/chain.c32 [run_dir]/boot/isolinux + exec cp [genode_dir]/tool/boot/isolinux.bin [run_dir]/boot/isolinux + exec cp [genode_dir]/tool/boot/isolinux.cfg [run_dir]/boot/isolinux + + exec mkdir -p [run_dir]/boot/grub + exec cp [genode_dir]/tool/boot/stage2_eltorito [run_dir]/boot/grub + + exec cp [genode_dir]/tool/boot/bender [run_dir]/boot/bender +} diff --git a/tool/run/load.inc b/tool/run/load.inc new file mode 100644 index 000000000..4d540248e --- /dev/null +++ b/tool/run/load.inc @@ -0,0 +1,7 @@ +## +# Get the spawn_id of the load process +# +proc load_spawn_id { } { + global load_spawn_id + return $load_spawn_id +} diff --git a/tool/run/load/jtag b/tool/run/load/jtag new file mode 100644 index 000000000..ef4baaeec --- /dev/null +++ b/tool/run/load/jtag @@ -0,0 +1,52 @@ +## +# Load image to target hardware via JTAG +# +# \param --load-jtag-debugger Debugger used +# \param --load-jtag-board Set the used board +# + +source [genode_dir]/tool/run/load.inc + + +proc load_jtag_debugger { } { return [get_cmd_arg --load-jtag-debugger 1] } + + +proc load_jtag_board { } { return [get_cmd_arg --load-jtag-board 1] } + + +proc run_load { } { + global load_spawn_id + + if {![have_spec arm] || ![have_installed openocd]} { + puts "No support for JTAG detected." + exit -1 + } + + set debugger [load_jtag_debugger] + set board [load_jtag_board] + set elf_img "[run_dir]/image.elf" + + # sleep a bit, board might need some time to come up + sleep 8 + + # parse ELF entrypoint + set entrypoint [exec [cross_dev_prefix]readelf -h $elf_img | \ + grep "Entry point address: " | \ + sed -e "s/.*Entry point address: *//"] + + eval spawn openocd -f $debugger -f $board -c init -c halt \ + -c \"load_image $elf_img\" -c \"resume $entrypoint\" + set load_spawn_id $spawn_id + set timeout 360 + expect { + "downloaded" { return true; } + eof { + puts stderr "openocd command process died unexpectedly"; + return false; + } + timeout { + puts stderr "Loading timed out"; + return false; + } + } +} diff --git a/tool/run/load/tftp b/tool/run/load/tftp new file mode 100644 index 000000000..677ebb41b --- /dev/null +++ b/tool/run/load/tftp @@ -0,0 +1,64 @@ +## +# Load files needed by the scenario via TFTP +# +# \param --load-tftp-base-dir base directory of TFTP +# \param --load-tftp-offset-dir offset directory within TFTP +# \param --load-tftp-absolute path is absolute, i.e. /base_dir/offset_dir +# instead of only /offset_dir is used +# + +source [genode_dir]/tool/run/load.inc + + +## +# The files are loaded implicitly via TFTP to the target machine +# +proc run_load { } { + global load_spawn_id + set load_spawn_id -1 + return true +} + + +proc load_tftp_base_dir { } { return [get_cmd_arg --load-tftp-base-dir ""] } + + +proc load_tftp_offset_dir { } { return [get_cmd_arg --load-tftp-offset-dir ""] } + + +proc load_tftp_use_absolute { } { return [get_cmd_switch --load-tftp-absolute] } + + +## +# Install files needed to boot via PXE +# +proc install_pxe_bootloader_to_run_dir { } { + exec mkdir -p [run_dir]/boot + exec cp [genode_dir]/tool/boot/pulsar [run_dir]/boot/pulsar + exec cp [genode_dir]/tool/boot/bender [run_dir]/boot/bender +} + + +## +# Generate pulsar config file used for loading files from TFTP +# +proc generate_tftp_config { } { + set tftp_base_dir [load_tftp_base_dir] + set tftp_offset_dir [load_tftp_offset_dir] + + if {[string length $tftp_base_dir] > 0 && [string length $tftp_offset_dir] > 0} { + exec ln -nfs "[pwd]" "$tftp_base_dir$tftp_offset_dir" + + set tftp_base "" + if {[load_tftp_use_absolute]} { + set tftp_base $tftp_base_dir + } + + set fh [open "$tftp_base_dir$tftp_offset_dir/config-00-00-00-00-00-00" "WRONLY CREAT TRUNC"] + puts $fh " root $tftp_base$tftp_offset_dir/[run_dir]" + puts $fh " config config-52-54-00-12-34-56" + close $fh + } else { + puts "Warning, TFTP base directory or TFTP offset directory not set." + } +} diff --git a/tool/run/log.inc b/tool/run/log.inc new file mode 100644 index 000000000..7c8b10648 --- /dev/null +++ b/tool/run/log.inc @@ -0,0 +1,7 @@ +## +# Get the spawn_id of the output process +# +proc output_spawn_id { } { + global output_spawn_id + return $output_spawn_id +} diff --git a/tool/run/log/amt b/tool/run/log/amt new file mode 100644 index 000000000..0efe79b63 --- /dev/null +++ b/tool/run/log/amt @@ -0,0 +1,70 @@ +## +# Get output of the target machine via Intel AMT's SoL feature +# +# \param --amt-host IP address of target machine +# \param --amt-password AMT password for target machine +# + +source [genode_dir]/tool/run/log.inc +source [genode_dir]/tool/run/amt.inc + + +proc log_amt_host { } { + return [get_cmd_arg_first --log-amt-host ""] +} + + +proc log_amt_password { } { + return [get_cmd_arg_first --log-amt-password ""] +} + + +## +# Log output of the test machine using Intel's AMT +# +proc run_log { wait_for_re timeout_value } { + global output_spawn_id + + if {![is_amt_available [log_amt_host] [log_amt_password]]} { + set exit_result 1 + return false + } + + # + # grab output + # + set amtterm "amtterm -u admin -p [log_amt_password] -v [log_amt_host]" + if {$wait_for_re == "forever"} { + set timeout -1 + } else { + set timeout [expr $timeout_value + 30] + } + set exit_result 1 + + while { $exit_result != 0 } { + set time_start [ clock seconds ] + eval spawn $amtterm + set output_spawn_id $spawn_id + expect { + -i $output_spawn_id -re $wait_for_re { break } + eof { continue } + timeout { puts "Error: Test execution timed out"; exit -2 } + } + catch wait result + set time_end [ clock seconds ] + if {[expr $time_end - $time_start] <= 1} { + incr timeout -1 + } else { + incr timeout [expr -1 * ($time_end - $time_start)] + } + if {$timeout < 0} { + set timeout 0 + } + set exit_result [lindex $result 3] + } + + global output + set output $expect_out(buffer) + + return true +} diff --git a/tool/run/log/linux b/tool/run/log/linux new file mode 100644 index 000000000..73de6f2b8 --- /dev/null +++ b/tool/run/log/linux @@ -0,0 +1,11 @@ +source [genode_dir]/tool/run/log.inc + +proc run_log { wait_for_re timeout_value } { + global linux_spawn_id + global output_spawn_id + + set output_spawn_id $linux_spawn_id + + wait_for_output $wait_for_re $timeout_value $linux_spawn_id + return true +} diff --git a/tool/run/log/qemu b/tool/run/log/qemu new file mode 100644 index 000000000..2d4f69e8f --- /dev/null +++ b/tool/run/log/qemu @@ -0,0 +1,17 @@ +## +# Capture the output of a scenario executed via Qemu +# + +source [genode_dir]/tool/run/log.inc +source [genode_dir]/tool/run/qemu.inc + + +proc run_log { wait_for_re timeout_value } { + global qemu_spawn_id + global output_spawn_id + + set output_spawn_id $qemu_spawn_id + + wait_for_output $wait_for_re $timeout_value $qemu_spawn_id + return true; +} diff --git a/tool/run/log/serial b/tool/run/log/serial new file mode 100644 index 000000000..d0cb532f2 --- /dev/null +++ b/tool/run/log/serial @@ -0,0 +1,42 @@ +## +# Get the output of the target machine via serial connection +# +# \param --log-serial-cmd Cmd that is executed to capture the output +# + +source [genode_dir]/tool/run/log.inc + + +set default_serial_cmd "picocom -b 115200 /dev/ttyUSB0" + + +proc log_serial_cmd { } { + global default_serial_cmd + return [get_cmd_arg --log-serial-cmd $default_serial_cmd] +} + + +## +# Log output of the test machine via serial device +# +proc run_log { wait_for_re timeout_value } { + global output_spawn_id + + set timeout 210 + while {true} { + eval spawn [log_serial_cmd] + set output_spawn_id $spawn_id + expect { + "Genode \[0-9]\[0-9]\.\[0-9]\[0-9]" { + wait_for_output $wait_for_re $timeout_value $output_spawn_id; + return true; + } + eof { continue; } + timeout { + puts stderr "Boot process timed out"; + close; + return false; + } + } + } +} diff --git a/tool/run/power_off/powerplug b/tool/run/power_off/powerplug new file mode 100644 index 000000000..b28d3b97f --- /dev/null +++ b/tool/run/power_off/powerplug @@ -0,0 +1,45 @@ +## +# Reset the target machine via powerplug +# +# \param --power-off-powerplug-ip IP address of powerplug device +# \param --power-off-powerplug-user user for powerplug device +# \param --power-off-powerplug-password password for powerplug device +# \param --power-off-powerplug-port target port of powerplug device +# + + +source [genode_dir]/tool/run/powerplug.inc + + +proc power_off_powerplug_ip { } { + return [get_cmd_arg_first --power-off-powerplug-ip 1] +} + + +proc power_off_powerplug_user { } { + return [get_cmd_arg_first --power-off-powerplug-user 1] +} + + +proc power_off_powerplug_password { } { + return [get_cmd_arg_first --power-off-powerplug-password 1] +} + + +proc power_off_powerplug_port { } { + return [get_cmd_arg_first --power-off-powerplug-port 1] +} + + +proc run_power_off { } { + set server_ip [power_off_powerplug_ip] + set user_name [power_off_powerplug_user] + set password [power_off_powerplug_password] + set power_port [power_off_powerplug_port] + + set connection_id [power_plug_connect $server_ip $user_name $password] + + puts "switch port $power_port off" + send -i $connection_id "port $power_port 0\n" + expect -i $connection_id "250 OK" +} diff --git a/tool/run/power_on/amt b/tool/run/power_on/amt new file mode 100644 index 000000000..2dea5494b --- /dev/null +++ b/tool/run/power_on/amt @@ -0,0 +1,131 @@ +## +# Reset the target machine via Intel AMT +# +# \param --power-on-amt-host IP address of target machine +# \param --power-on-amt-password AMT password for target machine +# + +source [genode_dir]/tool/run/amt.inc + + +proc power_on_amt_host { } { + return [get_cmd_arg_first --power-on-amt-host ""] +} + + +proc power_on_amt_password { } { + return [get_cmd_arg_first --power-on-amt-password ""] +} + + +## +# Reset via Intel AMT (works up to version smaller Intel AMT 9) +# +proc amt_reset_soap_eoi { } { + set timeout 20 + set exit_result 1 + + # + # amttool expects in the environment variable AMT_PASSWORD the password + # + set ::env(AMT_PASSWORD) [power_on_amt_password] + + while { $exit_result != 0 } { + set try_again 0 + set time_start [ clock seconds ] + spawn amttool [power_on_amt_host] reset + expect { + "host" { send "y\r"; } + eof { puts "Error: amttool died unexpectedly"; exit -4 } + timeout { puts "Error: amttool timed out"; exit -5 } + } + expect { + "result: pt_status: success" { break } + eof { set try_again 1 } + timeout { puts "Error: amttool timed out"; exit -6 } + } + catch wait result + set time_end [ clock seconds ] + if {[expr $time_end - $time_start] <= 1} { + incr timeout -1 + } else { + incr timeout [expr -1 * ($time_end - $time_start)] + } + if {$timeout < 0} { + set timeout 0 + } + if {$try_again != 0 } { + continue + } + + set exit_result [lindex $result 3] + } +} + + +## +# Reset via Intel AMT wsman protocol +# +proc amt_reset_wsman { } { + set xml_request "amt-reset-wsman.xml" + + set fh [open $xml_request "WRONLY CREAT TRUNC"] + + puts $fh { + + + 5 + + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + + http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ComputerSystem + + CIM_ComputerSystem + ManagedSystem + + + + + } + + close $fh + + exec wsman invoke -a RequestPowerStateChange -J $xml_request \ + "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService?SystemCreationClassName=\"CIM_ComputerSystem\",SystemName=\"Intel(r) AMT\",CreationClassName=\"CIM_PowerManagementService\",Name=\"Intel(r) AMT Power Management Service\"" \ + --port 16992 -h [power_on_amt_host] --username admin -p [power_on_amt_password] -V -v +} + + +## +# Reset the test machine using Intel's AMT +# +proc run_power_on { } { + if {![is_amt_available [power_on_amt_host] [power_on_amt_password]]} { + return false + } + + # + # amttool and wsman are supported for reset + # + set amt_tool [get_cmd_arg --amt-tool "default"] + + # + # reset the box + # + if {[have_installed wsman] && + ( $amt_tool == "wsman" || $amt_tool == "default") } { + amt_reset_wsman + } else { + if {[have_installed amttool] && + ($amt_tool == "amttool" || $amt_tool == "default") } { + amt_reset_soap_eoi + } else { + puts stderr "specified tool \"$amt_tool\" for using Intel AMT is unknown or is not installed" + exit -1 + } + } + + sleep 5 + return true +} diff --git a/tool/run/power_on/linux b/tool/run/power_on/linux new file mode 100644 index 000000000..2b343f9f1 --- /dev/null +++ b/tool/run/power_on/linux @@ -0,0 +1,37 @@ +## +# Execute scenario on Linux +# +proc run_power_on { } { + global linux_spawn_id + global linux_orig_pwd + + set linux_orig_pwd [pwd] + cd [run_dir]/genode + eval spawn ./core + set linux_spawn_id $spawn_id + cd $linux_orig_pwd +} + + +## +# Umount a directory that was bind-mounted beforehand +# +# This function is used by chroot-related tests, e.g., 'os/run/chroot.run', +# 'os/run/chroot_loader.run'. +# +proc umount_and_rmdir { path } { + + puts "umounting $path" + + # + # Invoke umount until it returns an error. Apparently, the unmounting + # of bind-mounted mount points does not always take immediate effect + # (regardless of the -l option). + # + while {1} { + if {[catch { exec sudo umount -l $path }]} { break; } + sleep 0.25 + } + + catch { exec rmdir -p $path } +} diff --git a/tool/run/power_on/powerplug b/tool/run/power_on/powerplug new file mode 100644 index 000000000..88caaebe0 --- /dev/null +++ b/tool/run/power_on/powerplug @@ -0,0 +1,54 @@ +## +# Reset the target machine via powerplug +# +# \param --power-on-powerplug-ip IP address of powerplug device +# \param --power-on-powerplug-user user for powerplug device +# \param --power-on-powerplug-password password for powerplug device +# \param --power-on-powerplug-port target port of powerplug device +# + +source [genode_dir]/tool/run/powerplug.inc + + +proc power_on_powerplug_ip { } { + return [get_cmd_arg_first --power-on-powerplug-ip 1] +} + + +proc power_on_powerplug_user { } { + return [get_cmd_arg_first --power-on-powerplug-user 1] +} + + +proc power_on_powerplug_password { } { + return [get_cmd_arg_first --power-on-powerplug-password 1] +} + + +proc power_on_powerplug_port { } { + return [get_cmd_arg_first --power-on-powerplug-port 1] +} + + + +proc run_power_on { } { + set server_ip [power_on_powerplug_ip] + set user_name [power_on_powerplug_user] + set password [power_on_powerplug_password] + set power_port [power_on_powerplug_port] + + set connection_id [power_plug_connect $server_ip $user_name $password] + + send -i $connection_id "port $power_port\n" + expect -i $connection_id -re {250 [0-9]+.*\n} + regexp -all {[0-9]+} $expect_out(0,string) power_status + if {!$power_status} { + puts "port $power_port is off - switching it on" + send -i $connection_id "port $power_port 1\n" + expect -i $connection_id "250 OK" + } else { + puts "port $power_port is on - reset port" + send -i $connection_id "port $power_port int\n" + expect -i $connection_id "250 OK" + } +} diff --git a/tool/run/power_on/qemu b/tool/run/power_on/qemu new file mode 100644 index 000000000..77d052366 --- /dev/null +++ b/tool/run/power_on/qemu @@ -0,0 +1,78 @@ +## +# Reset the target machine or rather run the scenario with Qemu +# + +source [genode_dir]/tool/run/qemu.inc + + +## +# Execute scenario using Qemu +# +proc run_power_on { } { + global qemu_args + global qemu + global qemu_spawn_id + + # + # Back out on platforms w/o Qemu support + # + if {![is_qemu_available]} { return 0 } + + if {[have_spec x86_32]} { set qemu "qemu-system-i386" } + if {[have_spec x86_64]} { set qemu "qemu-system-x86_64" } + if {[have_spec arm]} { set qemu "qemu-system-arm" } + + # + # Only the x86_64 variant of Qemu provides the emulation of hardware + # virtualization features used by NOVA. So let's always stick to this + # variant of Qemu when working with NOVA even when operating in 32bit. + # + if {[have_spec nova]} { set qemu "qemu-system-x86_64" } + + # + # Redirect serial output to stdio, but only in graphics mode and no + # explicit configuration of serial interfaces is specified in the run + # script. The 'mon' prefix enables the access to the qemu console. + # + if {![regexp -- {-nographic} $qemu_args dummy] && + ![regexp -- {-serial} $qemu_args dummy]} { + append qemu_args " -serial mon:stdio " } + + # tweak emulated platform for specific platforms + if {[have_spec platform_pbxa9]} { + # + # For PBXA9 qemu adjusts provided RAM chips to the -m arg. Thus we + # filter user values and force value that enables all chips that Genode + # expects to be available. Not doing so leads to inexplicable errors. + # + regsub -all {\-m ([0-9])+} $qemu_args "" qemu_args + append qemu_args " -m 768" + append qemu_args " -M realview-pbx-a9" + } + if {[have_spec platform_vpb926]} { append qemu_args " -M versatilepb -m 128 " } + if {[have_spec platform_vea9x4]} { append qemu_args " -M vexpress-a9 -cpu cortex-a9 -m 256 " } + + # on x86, we support booting via pxe or iso/disk image + if {[have_spec x86]} { + if {[have_include "load/tftp"]} { + append qemu_args " -boot n -tftp [run_dir] -bootp boot/pulsar -no-reboot -no-shutdown " + } else { + if {[have_include "image/iso"]} { + append qemu_args " -cdrom [run_dir].iso " + } else { + if {[have_include "image/disk"]} { + append qemu_args " -hda [run_dir].img " + } else { + puts "Aborting, cannot execute Qemu without a ISO or disk image" + exit -4 + } + } + } + } + + # on ARM, we supply the boot image as kernel + if {[have_spec arm]} { append qemu_args " -kernel [run_dir]/image.elf " } + + eval spawn $qemu $qemu_args + set qemu_spawn_id $spawn_id +} diff --git a/tool/run/powerplug.inc b/tool/run/powerplug.inc new file mode 100644 index 000000000..b648c5471 --- /dev/null +++ b/tool/run/powerplug.inc @@ -0,0 +1,9 @@ +proc power_plug_connect { server_ip user_name password } { + spawn telnet $server_ip 1234 + set connection_id $spawn_id + expect -i $connection_id "KSHELL V1.*" + send -i $connection_id "login $user_name $password\n" + expect -i $connection_id "250 OK" + + return $connection_id +} diff --git a/tool/run/qemu.inc b/tool/run/qemu.inc new file mode 100644 index 000000000..798a251fc --- /dev/null +++ b/tool/run/qemu.inc @@ -0,0 +1,33 @@ +set qemu_spawn_id "" +set qemu_args [get_cmd_arg --qemu-args ""] + + +# +# Enable run scripts to extend 'qemu_arg' via 'append' without bothering +# about the required whitespace in front of the custom arguments. +# +append qemu_args " " + + +proc qemu_args { } { + global qemu_args + return $qemu_args +} + + +## +# Check whether Qemu support is available +# +# XXX should by removed in favor of [have_include "exec/qemu"] +# +proc is_qemu_available { } { + if {[have_spec linux]} { return false } + + if {[have_spec platform_panda] + || [have_spec platform_arndale] + || [have_spec platform_rpi]} { + puts stderr "skipping execution because platform is not supported by qemu" + return false + } + return true +} diff --git a/tool/run/run b/tool/run/run new file mode 100755 index 000000000..1a346769b --- /dev/null +++ b/tool/run/run @@ -0,0 +1,663 @@ +#!/usr/bin/expect + +# +# \brief Framework for running automated tests +# \author Norman Feske +# \date 2010-03-16 +# +# Usage: run --name --include ... +# +# The '--name' argument is used for as name for the boot-image and +# temporary directories. The files includes via the '--include' +# argument provide platform-specific additions/refinements to the +# test framework as well as the actual test steps. +# + + +## +# Remove leading and trailing whitespace from string +# +proc strip_whitespace {string} { + regsub -all {^\s+} $string "" string + regsub -all {\s+$} $string "" string + return $string +} + + +## +# Check if the specified spec requirement is satisfied +# +proc assert_spec {spec} { + global specs + if {[lsearch $specs $spec] == -1} { + puts stderr "Test requires '$spec'" + exit 0 + } +} + + +## +# Build genode targets specified as space-separated strings +# +# If the build process fails, this procedure will exit the program with +# the error code -4. +# +proc build {targets} { + + if {[get_cmd_switch --skip-build]} return + + if {[info exists run_boot_dir_hook]} { + run_boot_dir_hook + } + + regsub -all {\s\s+} $targets " " targets + puts "building targets: $targets" + set timeout 10000 + set pid [eval "spawn make $targets"] + expect { eof { } } + if {[lindex [wait $pid] end] != 0} { + puts "Error: Genode build failed" + exit -4 + } + puts "genode build completed" +} + + +## +# Create a fresh boot directory +# +proc create_boot_directory { } { + exec rm -rf [run_dir] + exec mkdir -p [run_dir] + exec mkdir -p [run_dir]/genode +} + + +## +# Append string to variable only if 'condition' is satisfied +# +proc append_if {condition var string} { + upvar $var up_var + if {$condition} { append up_var $string } +} + + +## +# Append element to list only if 'condition' is satisfied +# +proc lappend_if {condition var string} { + upvar $var up_var + if {$condition} { lappend up_var $string } +} + + +## +# Check syntax of specified XML file using xmllint +# +proc check_xml_syntax {xml_file} { + + if {![have_installed xmllint]} { + puts "Warning: Cannot validate config syntax (please install xmllint)" + return; + } + + if {[catch {exec xmllint --noout $xml_file} result]} { + puts stderr $result + puts stderr "Error: Invalid XML syntax in $xml_file" + exit 1 + } +} + + +## +# Install content of specfied variable as init config file +# +proc install_config {config} { + set fh [open "[run_dir]/genode/config" "WRONLY CREAT TRUNC"] + puts $fh $config + close $fh + + check_xml_syntax [run_dir]/genode/config +} + + +## +# Integrate specified binaries into boot image +# +# \param binaries space-separated list of file names located within the +# '/bin/' directory +# +proc build_boot_image {binaries} { + run_boot_dir $binaries +} + + +## +# Execute Genode +# +# \param wait_for_re regular expression that matches the test completion +# \param timeout_value timeout in seconds +# \param spawn_id spawn_id of a already running and spawned process +# spawn_id may be a list of spawned processes if needed +# \global output contains the core output (modified) +# +# If the function is called without any argument, Genode is executed in +# interactive mode. +# +# If the test execution times out, this procedure will exit the program with +# the error code -2. +# +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; + } + + set retry 3 + while { $retry != 0 } { + + run_power_on + + if {![run_load]} { + puts "Load step failed, retry." + + # kill the spawned load process if there is one + if {[load_spawn_id] != -1} { + set pid [exp_pid -i [load_spawn_id]] + exec kill -9 $pid + } + + incr retry -1; + continue; + } + + if {![run_log $wait_for_re $timeout_value]} { + puts "Log step failed, retry." + incr retry -1; + continue; + } + + return; + } + + puts stderr "Boot process failed 3 times in series. I give up!"; + exit -1; +} + + +## +# Remove color information from output +# +proc filter_out_color_escape_sequences { } { + global output + regsub -all {\e\[.*?m} $output "" output +} + + +## +# Remove superfluous empty lines and unify line endings from output +# +proc trim_lines { } { + global output + regsub -all {[\r\n]+} $output "\n" output +} + + +## +# Filter output based on the specified pattern +# +# Only those lines that match the pattern are preserved. +# +proc grep_output {pattern} { + global output + + filter_out_color_escape_sequences + + trim_lines + + set output_list [split $output "\n"] + set filtered "" + foreach line $output_list { + if {[regexp $pattern $line]} { + append filtered "$line\n" + } + } + set output $filtered +} + + +## +# Unify known variations that appear in the test output +# +# \global output test output (modified) +# +proc unify_output {pattern replacement} { + global output + regsub -all $pattern $output $replacement output +} + + +## +# Compare output against expected output line by line +# +# \param good expected test output +# \global output test output +# +# This procedure will exit the program with the error code -1 if the +# comparison fails. +# +proc compare_output_to { good } { + global output + set output_list [split [strip_whitespace $output] "\n"] + set good_list [split [strip_whitespace $good] "\n"] + + set i 0 + set mismatch_cnt 0 + foreach good_line $good_list { + set output_line [strip_whitespace [lindex $output_list $i]] + set good_line [strip_whitespace $good_line] + + if {$output_line != $good_line} { + puts "" + puts stderr "Line $i of output is unexpected" + puts stderr " expected: '$good_line'" + puts stderr " got: '$output_line'" + incr mismatch_cnt + } + incr i + } + + # + # if $good is empty the foreach-loop isn't entered + # so we've to check for it separately + # + if {![llength $good_list] && [llength $output_list]} { + foreach output_line $output_list { + set output_line [strip_whitespace $output_line] + puts "" + puts stderr "Line $i of output is unexpected" + puts stderr " got: '$output_line'" + incr mismatch_cnt + incr i + } + } + + if {$mismatch_cnt > 0} { + puts "Error: Test failed, $mismatch_cnt unexpected lines of output" + exit -1 + } +} + + +## +# Return true if command-line switch was specified +# +proc get_cmd_switch { arg_name } { + global argv + return [expr [lsearch $argv $arg_name] >= 0] +} + + +## +# Return command-line argument value +# +# If a argument name is specified multiple times, a +# list of argument values is returned. +# +proc get_cmd_arg { arg_name default_value } { + global argv + + # find argument name in argv list + set arg_idx_list [lsearch -all $argv $arg_name] + + if {[llength $arg_idx_list] == 0} { return $default_value } + + set result {} + foreach arg_idx $arg_idx_list { + set next_idx [expr $arg_idx + 1] + + # stop if argv ends with the argument name + if {$next_idx >= [llength $argv]} continue + + # return list element following the argument name + lappend result [lindex $argv $next_idx] + } + + # if argument occurred only once, return its value + if {[llength $result] == 1} { return [lindex $result 0] } + + # if argument occurred multiple times, contain list of arguments + return $result +} + +## +# Return command-line argument value +# +# If a argument name is specified multiple times, return only the +# first match. +# +proc get_cmd_arg_first { arg_name default_value } { + global argv + + set arg_idx [lsearch $argv $arg_name] + + if {$arg_idx == -1} { return $default_value } + + return [lindex $argv [expr $arg_idx + 1]] +} + + +# +# Read command-line arguments +# + +set run_name [get_cmd_arg --name "noname"] +set genode_dir [get_cmd_arg --genode-dir ""] +set cross_dev_prefix [get_cmd_arg --cross-dev-prefix ""] +set specs [get_cmd_arg --specs ""] +set repositories [get_cmd_arg --repositories ""] + + +# accessor functions for command-line arguments +proc run_name { } { global run_name; return $run_name } +proc run_dir { } { global run_name; return var/run/$run_name } +proc genode_dir { } { global genode_dir; return $genode_dir } +proc cross_dev_prefix { } { global cross_dev_prefix; return $cross_dev_prefix } + +# set expect match-buffer size +match_max -d 20000 + +## +# Return true if spec value is set for the build +# +proc have_spec {spec} { global specs; return [expr [lsearch $specs $spec] != -1] } + + +## +# Return true if specified program is installed +# +proc have_installed {program} { + if {[catch { exec which $program }]} { return false; } + return true +} + + +## +# Return true if specified program is installed on the host platform +# +proc requires_installation_of {program} { + if {![have_installed $program]} { + puts "Run script aborted because $program is not installed"; exit + } +} + + +## +# Return first repository containing the given path +# +proc repository_contains {path} { + global repositories; + foreach i $repositories { + if {[file exists $i/$path]} { return $i } + } +} + + +## +## Utilities for performing steps that are the same on several platforms +## + +## +# Read kernel location from build-directory configuration +# +# If config file does not exist or if there is no 'KERNEL' declaration in the +# config file, the function returns 'default_location'. If the config file +# points to a non-existing kernel image, the function aborts with the exit +# value -6. +# +proc kernel_location_from_config_file { config_file default_location } { + global _kernel + + if {![info exists _kernel]} { + if {[file exists $config_file]} { + set _kernel [exec sed -n "/^KERNEL/s/^.*=\\s*//p" $config_file] + + # check if the regular expression matched + if {$_kernel != ""} { + if {[file exists $_kernel]} { + return $_kernel + } else { + puts stderr "Error: kernel specified in '$config_file' does not exist" + exit -6 + } + } + } + + # try to fall back to version hosted with the Genode build directory + set _kernel $default_location + } + return $_kernel +} + + +## +# Copy the specified binaries from the 'bin/' directory to the run +# directory and try to strip executables. +# +proc copy_and_strip_genode_binaries_to_run_dir { binaries } { + + foreach binary $binaries { + exec cp bin/$binary [run_dir]/genode + catch { + exec [cross_dev_prefix]strip [run_dir]/genode/$binary || true } + } +} + + +## +# 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 -3 } + timeout { puts stderr "Error: Test execution timed out"; exit -2 } + } + set output $expect_out(buffer) +} + + +## +## Fall-back implementations of all run module procedures +## + +## +# Dummy boot_dir maodule +proc run_boot_dir { } { return true; } + + +## +# Dummy image build module +# +proc run_image { {image "" } } { return true; } + + +## +# Dummy load module +# +proc run_load { } { return true; } + + +## +# Dummy output module +# +# XXX explain why exit 0 is appropiate +# +proc run_log { wait_for_re timeout_value } { exit 0 } + + +## +# Dummy power_on module +# +proc run_power_on { } { return true; } + + +## +# Dummy power_off module +# +proc run_power_off { } { return true; } + + +## +# Check if a specific file is included +# +proc have_include { name } { + global include_list + foreach element $include_list { + if {[string equal $element $name]} { + return true + } + } + + return false +} + + +## +# Override the exit procedure +# +# We have to override the exit procedure to make sure that a loaded +# run_power_off procedure is in any case execute when stopping the +# execution of the run tool. +# +rename exit real_exit +proc exit {{status 0}} { + run_power_off + + real_exit $status +} + + +## +# Determine terminal program +# +proc terminal { } { + global env + if {[info exists env(COLORTERM)]} { + return $env(COLORTERM) + } + return $env(TERM) +} + + +## +# Determine GDB executable installed at the host +# +proc gdb { } { + if {[have_installed "[cross_dev_prefix]gdb"]} { + return "[cross_dev_prefix]gdb" } + + if {[have_installed gdb]} { + return "gdb" } + + requires_installation_of gdb +} + + +## +# Check if a shell command is installed +# +# \param command name of the command to search +# +# \return absolute path of command if found, or exists if not +# +proc check_installed {command} { + if { [catch {set path [exec which $command]}] == 0} { + return $path + } + + set dir { /sbin /usr/sbin /usr/local/bin } + + foreach location $dir { + append location / $command + + if { [file exists $location] == 1} { + return $location + } + } + + puts stderr "Error: '$command' command could be not found. Please make sure to install the" + puts stderr " packet containing '$command', or make it available in your PATH variable.\n" + + exit 1 +} + + +## +## Execution of run scripts +## + + +set include_list { } + + +## +# Read and execute files specified as '--include' arguments +# +# Out of convenience run modules may be included by using a relative path. +# +foreach include_name [get_cmd_arg --include ""] { + # first check if the include name is absolute + if {[string first "/" $include_name] == 0} { + puts "including $include_name" + lappend include_list $include_name + source $include_name + + continue + } + + # if it is relative, check run modules + set run_path [genode_dir]/tool/run + set dir { etc } + lappend dir $run_path + + set found 0 + foreach location $dir { + set include_file $location/$include_name + if {[file exists $include_file] == 1} { + puts "including $include_file" + lappend include_list $include_name + source $include_file + + set found 1 + break + } else { + } + } + + if {!$found} { + puts stderr "Aborting, could not load '$include_file'" + exit -1; + } +} + +puts "\nRun script execution successful." +exit 0