mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-22 15:02:25 +00:00
dc2961338d
This provides bootable disk images for x86 platforms via ! RUN_OPT="--target disk" The resulting disk image contains one ext2 partition with binaries from the GRUB2 boot loader and the run scenario. The default disk size fits all binaries, but is configurable via ! --disk-size <size in MiB> in RUN_OPT. The feature depends on an grub2-head.img, which is part of the commit, but may also be generated by executing tool/create_grub2. The script generates a disk image prepared for one partition, which contains files for GRUB2. All image preparation steps that need superuser privileges are conducted by this script. The final step of writing the entire image to a disk must be executed later by sudo dd if=<image file> of=<device> bs=8M conv=fsync Fixes #1203.
951 lines
23 KiB
Plaintext
Executable File
951 lines
23 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} {
|
|
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
|
|
# '<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
|
|
# \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 <n> ... 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
|
|
|
|
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] &&
|
|
[have_installed amttool]} {
|
|
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
|
|
}
|
|
|
|
|
|
##
|
|
# Execute scenario using Intel's AMT
|
|
#
|
|
proc spawn_amt { wait_for_re timeout_value } {
|
|
global spawn_id
|
|
|
|
if {![is_amt_available]} { return 0 }
|
|
|
|
#
|
|
# amttool expects in the environment variable AMT_PASSWORD the password
|
|
#
|
|
set ::env(AMT_PASSWORD) $::env(AMT_TEST_MACHINE_PWD)
|
|
|
|
#
|
|
# reset the box
|
|
#
|
|
set timeout 20
|
|
set exit_result 1
|
|
|
|
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]
|
|
}
|
|
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.3"
|
|
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 210
|
|
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
|
|
}
|
|
|
|
eval spawn $serial_cmd
|
|
set serial_spawn_id $spawn_id
|
|
|
|
set timeout 210
|
|
expect {
|
|
$kernel_msg { break; }
|
|
eof { puts stderr "Serial command process died unexpectedly"; incr retry -1; }
|
|
timeout { puts stderr "Boot process timed out"; close; incr retry -1; }
|
|
}
|
|
}
|
|
if { $retry == 0 } {
|
|
puts stderr "Boot process failed 3 times in series. I give up!";
|
|
exit -1;
|
|
}
|
|
|
|
wait_for_output $wait_for_re $timeout_value $serial_spawn_id
|
|
}
|
|
|
|
|
|
##
|
|
# 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
|
|
}
|