genode/tool/run
Alexander Boettcher ae6257dce1 Use NOVA microkernel from github, add 64bit
Use git to get recent kernels from github. Adjust NOVA patch to compile
with recent github version. Patch and use makefile of NOVA microkernel
to avoid duplicated (and outdated) makefile in Genode

Furthermore, this patch adds support for using NOVA on x86_64. The
generic part of the syscall bindings has been moved to
'base-nova/include/nova/syscall-generic.h'. The 32/64-bit specific
parts are located at 'base-nova/include/32bit/nova/syscalls.h' and
'base-nova/include/64bit/nova/syscalls.h' respectively.

On x86_64, the run environment boots qemu using the Pulsar boot loader
because GRUB legacy does not support booting 64bit ELF executables.

In addition to the NOVA-specific changes in base-nova, this patch
rectifies compile-time warnings or build errors in the 'ports' and
'libports' repositories that are related to NOVA x86_64 (i.e., Vancouver
builds for 32bit only and needed an adaptation to NOVAs changed
bindings)

Fixes #233, fixes #234
2012-06-20 19:44:07 +02:00

504 lines
12 KiB
Plaintext
Executable File

#!/usr/bin/expect
#
# \brief Framework for running automated tests
# \author Norman Feske
# \date 2010-03-16
#
# Usage: run --name <run_name> --include <run_script> ...
#
# 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} {
global $var
if {$condition} { append $var $string }
}
##
# Append element to list only if 'condition' is satisfied
#
proc lappend_if {condition var string} {
global $var
if {$condition} { lappend $var $string }
}
##
# 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
}
##
# Integrate specified binaries into boot image
#
# \param binaries space-separated list of file names located within the
# '<build-dir>/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
# \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}} {
puts stderr "Error: 'run_genode_until' is not implemented for this platform"
exit -3
}
##
# Filter output based on the specified pattern
#
# Only those lines that match the pattern are preserved.
#
proc grep_output {pattern} {
global output
regsub -all {[\r\n]+} $output "\n" output
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 ""]
#
# 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' tool returns a non-zero return code even if
# successful. So we ignore the return code here.
#
catch { exec [genode_dir]/tool/create_iso iso ISO=[run_dir] }
if {![file exists "[run_dir].iso"]} {
puts stderr "Error: ISO image creation failed"
exit -5
}
}
##
# Execute scenario using Qemu
#
proc spawn_qemu { wait_for_re timeout_value } {
global output
global qemu_args
global qemu
global spawn_id
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]} { append qemu_args " -M realview-pbx-a9 -m 256 " }
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 supply the boot image as ISO image
if {[have_spec x86_64] && [have_spec nova]} {
append qemu_args " -boot n -tftp [run_dir] -bootp boot/pulsar -no-reboot -no-shutdown "
} else {
# on x86, we supply the boot image as ISO image
if {[have_spec x86]} { 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 " }
set timeout $timeout_value
set pid [eval "spawn $qemu $qemu_args"]
if {$wait_for_re == "forever"} { interact $pid }
expect {
-re $wait_for_re { }
timeout { puts stderr "Error: Test execution timed out"; exit -2 }
}
set output $expect_out(buffer)
}
##
# 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
}
##
## 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
}