mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-18 21:27:56 +00:00
16b863fc6e
With libxml2 >= 2.13, the `-path` argument can no longer be used for setting search paths for xsd files. Instead, we use an XML catalog to replace genode:// URIs with absolute paths. Fixes #5248
1246 lines
30 KiB
Plaintext
Executable File
1246 lines
30 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} {
|
|
|
|
global _collected_build_artifacts
|
|
|
|
if {[llength $targets] == 0} return
|
|
|
|
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
|
|
}
|
|
|
|
# record the build artifacts created during the build process
|
|
set artifacts_log [exec sed -n "/BUILD_ARTIFACTS/s/^.*= //p" progress.log]
|
|
lappend _collected_build_artifacts {*}[split $artifacts_log "\n"]
|
|
|
|
puts "genode build completed"
|
|
}
|
|
|
|
|
|
##
|
|
# Return list of build artifacts created during 'build'
|
|
#
|
|
# The returned artifacts can be used as boot-modules arguments for
|
|
# the 'build_boot_image' step.
|
|
#
|
|
proc build_artifacts { } {
|
|
|
|
global _collected_build_artifacts
|
|
|
|
if {![info exists _collected_build_artifacts]} {
|
|
return { } }
|
|
|
|
# use lsort to remove duplicates
|
|
return [lsort -unique $_collected_build_artifacts]
|
|
}
|
|
|
|
|
|
##
|
|
# 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 args} {
|
|
upvar $var up_var
|
|
if {$condition} { append up_var [join $args ""] }
|
|
}
|
|
|
|
|
|
##
|
|
# 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
|
|
}
|
|
}
|
|
|
|
|
|
##
|
|
# Validate configuration file to match an XML schema using xmllint
|
|
#
|
|
# \param bin binary name of the component behind the config
|
|
# \param xml_file configuration file
|
|
# \param xsd_file configuration schema file
|
|
# \param avail_xsd_files list of xsd files that might be used for
|
|
# configurations of children of the component
|
|
# \param nesting_level level of recursive calls of this procedure
|
|
#
|
|
proc check_config {bin xml_file xsd_file label avail_xsd_files nesting_level} {
|
|
|
|
# check prerequisites if this is not a recursive call
|
|
if {$nesting_level == 0} {
|
|
if {![have_installed xmllint]} {
|
|
puts ""
|
|
puts "Warning: cannot validate config syntax\
|
|
(please install xmllint)"
|
|
puts ""
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
# check the given component configuration itself
|
|
puts " CHECK $label"
|
|
if {[catch {exec xmllint --noout -schema $xsd_file $xml_file} result]} {
|
|
|
|
if {$result != "$xml_file validates"} {
|
|
|
|
puts stderr "$result"
|
|
puts stderr "Error: Invalid XML syntax"
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
#
|
|
# If this is no instance of Genodes Init component, we can skip checking
|
|
# for potential child configurations.
|
|
#
|
|
if {$bin != "init"} {
|
|
return
|
|
}
|
|
|
|
#
|
|
# The names of the available XSD files tell us for which binaries we
|
|
# can do a check. So iterate through the XSD files and try to find
|
|
# corresponding child instances.
|
|
#
|
|
foreach child_xsd_file $avail_xsd_files {
|
|
|
|
set child_bin [file tail [file rootname $child_xsd_file]]
|
|
for {set child_instance 1} {1} {incr child_instance} {
|
|
|
|
#
|
|
# Try to find a child instance that uses this binary. We start
|
|
# with selecting the first matching start node, than the
|
|
# second, and so on. As soon as xmlscarlet returns an empty
|
|
# string, we know that there is no further matching start node.
|
|
#
|
|
set xpath_start_cond "child::binary\[@name=\'$child_bin\'\] or \
|
|
not(child::binary) and @name=\'$child_bin\'"
|
|
|
|
set xpath "string(/config/start\[$xpath_start_cond\]\[$child_instance\]/@name)"
|
|
set select_xpath "xmllint --xpath \"$xpath\" $xml_file"
|
|
if {[catch {exec {*}$select_xpath} child_name]} {
|
|
|
|
#
|
|
# No further child instance that uses this binary.
|
|
# Proceed with next binary.
|
|
#
|
|
break
|
|
}
|
|
|
|
#
|
|
# If the child has a config, check it with a recursive
|
|
# call to this procedure.
|
|
#
|
|
set xpath "/config/start\[$xpath_start_cond\]\[$child_instance\]/config"
|
|
set select_xpath "xmllint --xpath \"$xpath\" $xml_file"
|
|
if {[catch {exec {*}$select_xpath} child_xml]} {
|
|
|
|
# the child has no config
|
|
break
|
|
}
|
|
|
|
# write child config to temporary file
|
|
set child_xml_file ".config_$nesting_level.xml.tmp"
|
|
set child_xml_fd [open $child_xml_file w]
|
|
puts $child_xml_fd $child_xml
|
|
close $child_xml_fd
|
|
|
|
# call this procedure again on the child config file
|
|
set child_label "$label -> $child_name"
|
|
check_config $child_bin $child_xml_file $child_xsd_file \
|
|
$child_label $avail_xsd_files \
|
|
[expr $nesting_level+1]
|
|
|
|
# clean up
|
|
exec rm -f $child_xml_file
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
##
|
|
# Install boot module with the given 'name' and content
|
|
#
|
|
proc install_boot_module { name args } {
|
|
|
|
set fh [open [file join [run_dir] genode $name] "WRONLY CREAT TRUNC"]
|
|
puts $fh [join $args {}]
|
|
close $fh
|
|
}
|
|
|
|
|
|
##
|
|
# Install content of specified variable as init config file
|
|
#
|
|
proc install_config { args } {
|
|
|
|
install_boot_module "config" [join $args {}]
|
|
}
|
|
|
|
|
|
##
|
|
# Check consistency between [build_artifacts] and build_boot_image arguments
|
|
#
|
|
proc check_for_missing_lib_build { binaries } {
|
|
|
|
set artifacts [build_artifacts]
|
|
set missing_lib_builds { }
|
|
set proposed_build_targets { }
|
|
foreach binary $binaries {
|
|
if {[lsearch $artifacts $binary] == -1} {
|
|
if {[regexp {(.*)\.lib\.so$} $binary dummy libname]} {
|
|
lappend missing_lib_builds $binary
|
|
lappend proposed_build_targets "lib/$libname" } } }
|
|
|
|
if {[llength $missing_lib_builds] == 0} {
|
|
return }
|
|
|
|
set plural ""
|
|
if {[llength $missing_lib_builds] > 1} { set plural "s" }
|
|
|
|
puts stderr ""
|
|
puts stderr "Error: missing build argument$plural for: $missing_lib_builds\n"
|
|
puts stderr " The build_boot_image argument$plural may be superfluous or"
|
|
puts stderr " the build step lacks the argument$plural: $proposed_build_targets\n"
|
|
puts stderr " Consider using \[build_artifacts\] as build_boot_image argument to"
|
|
puts stderr " maintain consistency beween the build and build_boot_image steps."
|
|
puts stderr ""
|
|
exit 1
|
|
}
|
|
|
|
|
|
##
|
|
# Integrate specified binaries into boot image
|
|
#
|
|
# \param binaries space-separated list of file names located within the
|
|
# '<build-dir>/bin/' directory
|
|
#
|
|
proc build_boot_image { binaries } {
|
|
|
|
check_for_missing_depot_archives
|
|
check_for_missing_lib_build $binaries
|
|
|
|
# lookup XSD files in the run-script-specific folder
|
|
set xsd_files ""
|
|
if {[catch {exec find [run_dir]/genode -name *.xsd} find_stdout] == 0} {
|
|
set xsd_files [split "$find_stdout" "\n"]
|
|
}
|
|
# lookup XSD files in the binaries folder
|
|
foreach binary $binaries {
|
|
if {[file exists "bin/$binary.xsd"]} {
|
|
lappend xsd_files "bin/$binary.xsd"
|
|
}
|
|
}
|
|
# determine which XSD file to use for the init config
|
|
set init_xsd_file ""
|
|
foreach xsd_file $xsd_files {
|
|
set filename [file tail $xsd_file]
|
|
if {$filename == "init.xsd"} {
|
|
set init_xsd_file $xsd_file
|
|
}
|
|
}
|
|
# create catalog file
|
|
set catalog [file join [run_dir] genode catalog.xml]
|
|
catch { exec xmlcatalog --noout --create $catalog }
|
|
|
|
# determine paths of additional XSD files and add to catalog
|
|
global repositories;
|
|
foreach repo $repositories {
|
|
foreach xsd_path [glob -nocomplain -type f $repo/xsd/*.xsd] {
|
|
set xsd_name [file tail $xsd_path]
|
|
catch { exec xmlcatalog --noout --add "uri" "genode://$xsd_name" "$xsd_path" $catalog }
|
|
}
|
|
}
|
|
set ::env(XML_CATALOG_FILES) $catalog
|
|
|
|
# check configurations of init and its children
|
|
puts "checking configuration syntax"
|
|
check_config init [run_dir]/genode/config $init_xsd_file init $xsd_files 0
|
|
|
|
run_boot_dir $binaries
|
|
}
|
|
|
|
|
|
# set expect match-buffer size
|
|
match_max -d 40000
|
|
|
|
|
|
##
|
|
# 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 } {
|
|
|
|
#
|
|
# Depending an the used run module, a reset can include
|
|
# shutting the power off and turning it on again.
|
|
#
|
|
if (![run_power_cycle]) {
|
|
puts "Power cycle step failed, retry."
|
|
sleep 3
|
|
incr retry -1;
|
|
continue
|
|
}
|
|
|
|
if {![run_load]} {
|
|
puts "Load step failed, retry."
|
|
|
|
# kill the spawned load process if there is one
|
|
if {[load_spawn_id] != -1} {
|
|
kill_spawned [load_spawn_id]
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
##
|
|
# "Safely" kill spawned process
|
|
#
|
|
proc kill_spawned {spawn_id} {
|
|
set pid [exp_pid -i $spawn_id]
|
|
close -i $spawn_id
|
|
exec kill -9 $pid
|
|
wait -i $spawn_id
|
|
}
|
|
|
|
|
|
##
|
|
# Exeute command line with retry and optional timeout per try
|
|
#
|
|
# \param retry number of attempts before giving up
|
|
# \param cmd commane line to execute
|
|
# \param success_re output denoting successful attempt
|
|
# \param to timeout per attempt (optional)
|
|
#
|
|
# \return list { output of last attempt, spawn id }
|
|
#
|
|
# The output of each attempt is checked with the regular expression
|
|
# 'success_re' and presumed successful on a match. The proc can also be used to
|
|
# just execute one attempt with empty 'success_re' but a timeout.
|
|
#
|
|
proc retry { retry cmd success_re {to -1} } {
|
|
set success false
|
|
set output ""
|
|
|
|
while {$retry > 0} {
|
|
spawn {*}$cmd
|
|
sleep $to
|
|
|
|
expect {
|
|
-timeout 0
|
|
default { close; wait }
|
|
|
|
-re $success_re {
|
|
set success true
|
|
set output $expect_out(buffer)
|
|
}
|
|
}
|
|
|
|
if {$success} { break }
|
|
incr retry -1
|
|
}
|
|
|
|
return [list $output $spawn_id]
|
|
}
|
|
|
|
|
|
##
|
|
# 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 board_var [get_cmd_arg --board ""]
|
|
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 }
|
|
|
|
##
|
|
# 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 {[auto_execok "$program"] != ""} { return true; }
|
|
return false;
|
|
}
|
|
|
|
|
|
##
|
|
# 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 installed_command {command} {
|
|
|
|
set path [auto_execok $command]
|
|
if {$path != ""} {
|
|
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
|
|
}
|
|
|
|
|
|
##
|
|
# Return first repository containing the given path
|
|
#
|
|
proc repository_contains { rep_rel_path } {
|
|
|
|
global repositories;
|
|
|
|
foreach rep $repositories {
|
|
if {[file exists [file join $rep $rep_rel_path]]} {
|
|
return $rep }
|
|
}
|
|
}
|
|
|
|
|
|
##
|
|
# Return path to first file found in the available repositories
|
|
#
|
|
proc select_from_repositories { rep_rel_path } {
|
|
|
|
set rep_dir [repository_contains $rep_rel_path]
|
|
|
|
if {[llength $rep_dir]} {
|
|
return [file join $rep_dir $rep_rel_path] }
|
|
|
|
puts stderr "Error: $rep_rel_path not present in any repository"
|
|
exit -8
|
|
}
|
|
|
|
|
|
##
|
|
## 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
|
|
}
|
|
|
|
|
|
##
|
|
# Return name of kernel-specific binary for a given generic name
|
|
#
|
|
# The boot_dir plugin may provide functions named 'binary_name_<binary>'
|
|
# where '<binary>' stands for a generic name like "timer" or "nic". The
|
|
# function returns the name of the binary to integrate into the boot
|
|
# directory under the name '<binary>'.
|
|
#
|
|
# If no such function exists, it returns the argument as is. This is the
|
|
# case for regular binaries that appear in the boot directory under their
|
|
# original name.
|
|
#
|
|
proc kernel_specific_binary { binary {silent ""} } {
|
|
|
|
regsub -all {\.} $binary "_" function_suffix
|
|
set function_name "binary_name_$function_suffix"
|
|
|
|
if {[info procs $function_name] == $function_name} {
|
|
set binary_name [$function_name]
|
|
if {$silent != "silent"} {
|
|
puts "using '$binary_name' as '$binary'"
|
|
}
|
|
return [$function_name]
|
|
}
|
|
|
|
return $binary
|
|
}
|
|
|
|
|
|
proc copy_file {src dst} {
|
|
file copy -force $src $dst
|
|
}
|
|
|
|
|
|
proc copy_genode_binaries_to_run_dir { binaries } {
|
|
|
|
foreach binary $binaries {
|
|
|
|
#
|
|
# Normalize kernel-specific file names, needed when a run script passes
|
|
# [build_artifacts] to the build_boot_image command. The build artfact
|
|
# fetched from the progress.log has the kernel-specific name.
|
|
#
|
|
foreach name { "timer" "core" "ld.lib.so" } {
|
|
if {$binary == [kernel_specific_binary $name silent]} {
|
|
set binary $name } }
|
|
|
|
copy_file bin/[kernel_specific_binary $binary] [run_dir]/genode/$binary
|
|
}
|
|
}
|
|
|
|
|
|
##
|
|
# 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
|
|
}
|
|
|
|
set platform_msg [run_boot_string]
|
|
if {$platform_msg eq ""} {
|
|
set platform_msg "undefined platform command startup string sequence"
|
|
}
|
|
|
|
expect {
|
|
-i $running_spawn_id $platform_msg { puts stderr "Error: platform rebooted unexpectedly"; exit -4 }
|
|
-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 }
|
|
}
|
|
append output $expect_out(buffer)
|
|
}
|
|
|
|
|
|
##
|
|
# Remove 'genode' directory in 'run_dir' unless --preserve-genode-dir is present
|
|
# in RUN_OPT
|
|
#
|
|
proc remove_genode_dir { } {
|
|
global env
|
|
|
|
if {![get_cmd_switch --preserve-genode-dir]} {
|
|
exec rm -rf [run_dir]/genode
|
|
}
|
|
}
|
|
|
|
|
|
##
|
|
## Fall-back implementations of all run module procedures
|
|
##
|
|
|
|
##
|
|
# Dummy boot_string procedure
|
|
#
|
|
proc run_boot_string { } { return ""; }
|
|
|
|
|
|
##
|
|
# Fall-back boot_dir module
|
|
#
|
|
# If this function is called someone forgot to include an appropriate boot_dir
|
|
# module. So, we exit with an error.
|
|
#
|
|
proc run_boot_dir { binaries } {
|
|
puts stderr "Error: boot_dir module missing, e.g., '--include boot_dir/hw'"
|
|
exit 1
|
|
}
|
|
|
|
|
|
##
|
|
# Bring the boot-directory content into a form that can actually be booted
|
|
#
|
|
# The form suitable for booting depends on the platform and the used boot
|
|
# loader. By convention, this function can expect the presence of the boot
|
|
# image in the form of an [run_dir]/boot/image.elf file.
|
|
#
|
|
proc run_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; }
|
|
|
|
|
|
##
|
|
# Default power cycle fallback procedure
|
|
#
|
|
proc run_power_cycle { } {
|
|
#
|
|
# On targets that are directly connected to a socket,
|
|
# turn the socket off and on again. On targets that
|
|
# use a module were that is not required, e.g. softreset,
|
|
# power_off is a NOP. Note, we give the target some time
|
|
# to effectively drain energy before switching it on again.
|
|
#
|
|
run_power_off
|
|
sleep 1
|
|
return [run_power_on]
|
|
}
|
|
|
|
|
|
##
|
|
# Default core linker options
|
|
#
|
|
proc core_ld_opts { } {
|
|
set ret { -Wl,-T }
|
|
lappend ret "-Wl,[genode_dir]/repos/base/src/ld/genode.ld"
|
|
return $ret
|
|
}
|
|
|
|
|
|
##
|
|
# Default core link address
|
|
#
|
|
proc core_link_address { } { return "0x01000000" }
|
|
|
|
|
|
##
|
|
# 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}} {
|
|
catch {run_power_off}
|
|
|
|
real_exit $status
|
|
}
|
|
|
|
|
|
##
|
|
# Determine terminal program
|
|
#
|
|
proc terminal { } {
|
|
global env
|
|
return x-terminal-emulator
|
|
}
|
|
|
|
|
|
##
|
|
# Return the board to build for
|
|
#
|
|
proc board { } {
|
|
global board_var
|
|
if {$board_var eq ""} {
|
|
puts "Unknown platform no BOARD variable set"
|
|
exit 1
|
|
}
|
|
return $board_var
|
|
}
|
|
|
|
##
|
|
# Return whether the board equals to the given string
|
|
#
|
|
proc have_board {board} { global board_var; return [expr {$board_var} eq {$board}] }
|
|
|
|
|
|
##
|
|
# Determine GDB executable installed at the host
|
|
#
|
|
proc gdb { } {
|
|
if {[have_installed "[cross_dev_prefix]gdb"]} {
|
|
return "[cross_dev_prefix]gdb" }
|
|
|
|
return [installed_command gdb]
|
|
}
|
|
|
|
|
|
##
|
|
# Generate assembly code aggregating boot-module data from specified files.
|
|
#
|
|
proc generate_boot_modules_asm {path modules} {
|
|
|
|
# architecture dependent definitions
|
|
if {[have_spec "64bit"]} { set address_type ".quad"
|
|
} else { set address_type ".long" }
|
|
|
|
# header
|
|
set asm_src {}
|
|
append asm_src ".set MIN_PAGE_SIZE_LOG2, 12\n"
|
|
append asm_src ".set DATA_ACCESS_ALIGNM_LOG2, 3\n"
|
|
append asm_src "\n"
|
|
append asm_src ".section .data\n"
|
|
append asm_src "\n"
|
|
append asm_src ".p2align DATA_ACCESS_ALIGNM_LOG2\n"
|
|
append asm_src ".global _boot_modules_headers_begin\n"
|
|
append asm_src "_boot_modules_headers_begin:\n"
|
|
append asm_src "\n"
|
|
|
|
# module list
|
|
set i 0
|
|
foreach module $modules {
|
|
incr i
|
|
append asm_src "${address_type} _boot_module_${i}_name\n"
|
|
append asm_src "${address_type} _boot_module_${i}_begin\n"
|
|
append asm_src "${address_type} _boot_module_${i}_end -"
|
|
append asm_src " _boot_module_${i}_begin\n"
|
|
append asm_src "\n"
|
|
}
|
|
append asm_src ".global _boot_modules_headers_end\n"
|
|
append asm_src "_boot_modules_headers_end:\n"
|
|
append asm_src "\n"
|
|
|
|
# module names
|
|
set i 0
|
|
foreach module $modules {
|
|
incr i
|
|
append asm_src ".p2align DATA_ACCESS_ALIGNM_LOG2\n"
|
|
append asm_src "_boot_module_${i}_name:\n"
|
|
append asm_src ".string \"${module}\"\n"
|
|
append asm_src ".byte 0\n"
|
|
append asm_src "\n"
|
|
}
|
|
|
|
# header end
|
|
append asm_src ".section .data.boot_modules_binaries\n"
|
|
append asm_src "\n"
|
|
append asm_src ".global _boot_modules_binaries_begin\n"
|
|
append asm_src "_boot_modules_binaries_begin:\n"
|
|
append asm_src "\n"
|
|
|
|
# module data
|
|
set i 0
|
|
foreach module $modules {
|
|
incr i
|
|
append asm_src ".p2align MIN_PAGE_SIZE_LOG2\n"
|
|
append asm_src "_boot_module_${i}_begin:\n"
|
|
append asm_src ".incbin \"${path}/${module}\"\n"
|
|
append asm_src "_boot_module_${i}_end:\n"
|
|
append asm_src "\n"
|
|
}
|
|
|
|
append asm_src ".p2align MIN_PAGE_SIZE_LOG2\n"
|
|
append asm_src ".global _boot_modules_binaries_end\n"
|
|
append asm_src "_boot_modules_binaries_end:\n"
|
|
|
|
return $asm_src
|
|
}
|
|
|
|
|
|
##
|
|
# Link core image containing given modules
|
|
#
|
|
proc build_core {lib modules target link_address} {
|
|
|
|
# generate assembly code aggregating the modules data
|
|
set asm_src [generate_boot_modules_asm [run_dir]/genode $modules]
|
|
|
|
# architecture dependent definitions
|
|
set arch {}
|
|
if {[have_spec "x86_64"]} { set arch { -m64 -mcmodel=large } }
|
|
if {[have_spec "x86_32"]} { set arch -m32 }
|
|
|
|
# determine the libgcc
|
|
set libgcc [exec [cross_dev_prefix]gcc -print-libgcc-file-name {*}$arch]
|
|
|
|
# compile the boot modules into one object file
|
|
exec [cross_dev_prefix]gcc {*}$arch -c -x assembler -o [run_dir].boot_modules.o - << $asm_src
|
|
|
|
# link final image
|
|
exec [cross_dev_prefix]g++ {*}$arch -nostdlib {*}[core_ld_opts] \
|
|
-Wl,-z -Wl,max-page-size=0x1000 \
|
|
-Wl,-Ttext=$link_address -Wl,-gc-sections \
|
|
-Wl,-nostdlib -Wl,--whole-archive -Wl,--start-group \
|
|
$lib [run_dir].boot_modules.o -Wl,--no-whole-archive \
|
|
-Wl,--end-group $libgcc -o $target
|
|
}
|
|
|
|
|
|
##
|
|
# Return kernel-specific files to be excluded from the core image
|
|
#
|
|
proc kernel_files { } { return { } }
|
|
|
|
|
|
##
|
|
# Generate bootable core image containing all boot-modules
|
|
#
|
|
proc build_core_image { modules } {
|
|
|
|
set core_obj [kernel_specific_binary core.a]
|
|
|
|
# replace 'core' with actual core-object name in 'modules' list
|
|
if {[lsearch $modules "core"] != -1} {
|
|
set idx [lsearch $modules "core"]
|
|
set modules [lreplace $modules $idx $idx]
|
|
lappend modules $core_obj
|
|
}
|
|
|
|
copy_genode_binaries_to_run_dir $modules
|
|
|
|
# create core binary without modules for debugging
|
|
if {[file exists debug/$core_obj]} {
|
|
build_core debug/$core_obj {} [run_dir].core [core_link_address]
|
|
}
|
|
|
|
# determine modules to be incorporated into the core image
|
|
set modules [glob -nocomplain -tails -directory [run_dir]/genode/ *]
|
|
set excluded_modules [kernel_files]
|
|
lappend excluded_modules $core_obj
|
|
foreach excluded $excluded_modules {
|
|
set modules [lsearch -inline -not -all $modules $excluded] }
|
|
|
|
# check syntax of all boot modules named *.config
|
|
foreach file [glob -nocomplain [run_dir]/genode/*.config] {
|
|
check_xml_syntax $file }
|
|
|
|
# create core binary containing the boot modules
|
|
set image_elf [run_dir]/[kernel_specific_binary image.elf]
|
|
build_core [run_dir]/genode/$core_obj $modules $image_elf [core_link_address]
|
|
exec [cross_dev_prefix]strip $image_elf
|
|
|
|
# Save config part of the image.elf for easy inspection
|
|
exec cp -f [run_dir]/genode/config [run_dir].config
|
|
}
|
|
|
|
|
|
proc build_initrd { modules } {
|
|
|
|
copy_genode_binaries_to_run_dir $modules
|
|
|
|
set modules [glob -nocomplain -tails -directory [run_dir]/genode/ *]
|
|
set excluded_modules [kernel_files]
|
|
|
|
foreach file [glob -nocomplain [run_dir]/genode/*.config] {
|
|
check_xml_syntax $file }
|
|
|
|
exec cp -f [run_dir]/genode/config [run_dir].config
|
|
|
|
set here [pwd]
|
|
cd [run_dir]
|
|
puts "generating initrd"
|
|
exec cp genode/initramfs init
|
|
exec mkdir tmp
|
|
exec mkdir dev
|
|
set files "init\ntmp\ndev\ngenode\n"
|
|
append files [exec find genode -type f,l -printf "genode/%f\n"]
|
|
exec -ignorestderr echo $files | [installed_command cpio] -o -L -H newc > initrd
|
|
|
|
#workaround because cpio fails to compress broken links sometimes
|
|
exec touch dev/platform_info
|
|
cd genode
|
|
exec ln -s ../dev/platform_info platform_info
|
|
cd ..
|
|
exec -ignorestderr echo "genode/platform_info" | [installed_command cpio] -o -A -H newc -O initrd
|
|
cd ${here}
|
|
}
|
|
|
|
source [genode_dir]/tool/run/depot.inc
|
|
source [genode_dir]/tool/run/qemu.inc
|
|
|
|
|
|
##
|
|
## 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 repositories and tool directory for run modules
|
|
set search_dirs {}
|
|
foreach rep_dir $repositories {
|
|
lappend search_dirs [file join $rep_dir tool run] }
|
|
lappend search_dirs [genode_dir]/tool/run
|
|
|
|
set found 0
|
|
foreach dir $search_dirs {
|
|
set include_file $dir/$include_name
|
|
if {[file exists $include_file] == 1} {
|
|
puts "including $include_file"
|
|
lappend include_list $include_name
|
|
source $include_file
|
|
|
|
set found 1
|
|
break
|
|
}
|
|
}
|
|
|
|
if {!$found} {
|
|
puts stderr "Aborting, could not load '$include_file'"
|
|
exit -1;
|
|
}
|
|
}
|
|
|
|
puts "\nRun script execution successful."
|
|
exit 0
|