#!/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
	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} {
	global run_target

	# strip binaries
	copy_and_strip_genode_binaries_to_run_dir $binaries

	# append init config
	if {[file exists "[run_dir]/genode/config"] == 1} {
		append binaries " config"
	}

	#
	# Compose a platform specific assembly file 'boot_modules.s', that
	# enables the creation of a single boot image. The file rawly includes
	# all binaries given in 'binaries', minus 'core', if given, plus 'config',
	# if available. It also provides a simple file system, that enables Genode
	# to access these BLOBs. To build a single image this file is simply
	# linked against core. To build core stand-alone this file is substituted
	# by a dummy version. 'boot_modules.s' must be composed on demand, because
	# it depends on the individual run scenario.
	#
	set boot_modules "[run_dir]/boot_modules.s"
	if { [have_spec {arm}] } {

		set load_store_align_l2 2
		set min_page_align_l2  12

		# introduce boot module headers
		exec echo -e \
			  "/**" \
			"\n * This file was automatically generated by the procedure" \
			"\n * 'build_boot_image' in 'base-hw/run/env'." \
			"\n */" \
			"\n" \
			"\n.section .data" \
			"\n" \
			"\n.p2align ${load_store_align_l2}" \
			"\n.global _boot_modules_begin" \
			"\n_boot_modules_begin:" \
			"\n.string \"GROM\"" \
			"\n" \
			"\n.p2align ${load_store_align_l2}" \
			"\n.global _boot_module_headers_begin" \
			"\n_boot_module_headers_begin:" > $boot_modules

		# generate header for each boot module except core
		set i 1
		foreach binary $binaries {
			if {$binary == "core"} { continue }
			exec echo -e \
				"\n.long mod${i}_name" \
				"\n.long mod${i}_start" \
				"\n.long mod${i}_end - mod${i}_start" >> $boot_modules
			incr i
		}

		# end boot module headers
		exec echo -e \
			"\n.global _boot_module_headers_end" \
			"\n_boot_module_headers_end:" \
			"\n" >> $boot_modules

		# generate name string for each module except core
		set i 1
		foreach binary $binaries {
			if {$binary == "core"} { continue }
			exec echo -e \
				".p2align ${load_store_align_l2}" \
				"\nmod${i}_name:" \
				"\n.string \"${binary}\"" \
				"\n.byte 0" \
				"\n" >> $boot_modules
			incr i
		}

		# include raw data of modules consecutively but page aligned
		set i 1
		foreach binary $binaries {
			if {$binary == "core"} { continue }
			exec echo -e \
				".p2align ${min_page_align_l2}" \
				"\nmod${i}_start:" \
				"\n.incbin \"[run_dir]/genode/${binary}\"" \
				"\nmod${i}_end:" \
				"\n" >> $boot_modules
			incr i
		}

		# end boot-modules file
		exec echo -e \
			".global _boot_modules_end" \
			"\n_boot_modules_end:" >> $boot_modules

	} else {
		puts stderr "Error: Unknown architecture"
		puts stderr "       SPEC must contain one of: 'arm'"
		exit -1
	}
	clean_boot_modules
	exec ln -s $boot_modules boot_modules.s

	# recompile core with boot modules
	exec cp -L bin/core core/core.standalone
	exec find . -type f -name "core" -delete
	set timeout 10000
	set pid [eval "spawn make core"]
	expect { eof { } }
	if {[lindex [wait $pid] end] != 0} {
		clean_boot_modules
		puts stderr "Error: Genode build failed"
		exit -1
	}
	clean_boot_modules
	exec rm -rf "[run_dir]/genode"

	# offer ELF image
	set elf_img "[run_dir]/image.elf"
	exec cp -L bin/core $elf_img
	exec [cross_dev_prefix]strip $elf_img

	build_uboot_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 {[regexp "uboot" $run_target]} {
			exec ln -sf "[pwd]/[run_dir]/uImage" "$::env(PXE_TFTP_DIR_BASE)$::env(PXE_TFTP_DIR_OFFSET)"
		}
	}

	# retrieve stand-alone core
	exec cp core/core.standalone bin/core
	exec rm core/core.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 "create local services"
		return
	}

	global run_target
	puts stderr "Error: Can't execute automatically on target '$run_target'"
	exit -1
}

