Add infrastructure for arm_fpga platform into CI.

This patch adds all the scripts needed for CI to be able to
configure and run tests on the arm_fpga platform.

No run configurations are provided in this patch.

Change-Id: Id371794d1691b9f10d6bbf28bcfd11f9f5ea54e2
Signed-off-by: Javier Almansa Sobrino <javier.almansasobrino@arm.com>
diff --git a/script/test_fpga_payload.sh b/script/test_fpga_payload.sh
new file mode 100644
index 0000000..be27918
--- /dev/null
+++ b/script/test_fpga_payload.sh
@@ -0,0 +1,321 @@
+#!/bin/bash
+#
+# Copyright (c) 2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+set -e
+
+# Enable job control to have background processes run in their own process
+# group. That way, we can kill a background process group in one go.
+set -m
+
+ci_root="$(readlink -f "$(dirname "$0")/..")"
+source "$ci_root/utils.sh"
+
+artefacts="${artefacts-$workspace/artefacts}"
+
+run_root="$workspace/run"
+pid_dir="$workspace/pids"
+
+mkdir -p "$pid_dir"
+mkdir -p "$run_root"
+
+archive="$artefacts"
+
+gen_fpga_params() {
+	local fpga_param_file="fpga_env.sh"
+
+	echo "Generating parameters for FPGA $fpga..."
+	echo
+
+	echo "baudrate=$uart_baudrate" > $fpga_param_file
+	echo "fpga=$fpga" >> $fpga_param_file
+	echo "fpga_bitfile=$fpga_bitfile" >> $fpga_param_file
+	echo "fpga_payload=$fpga_payload" >> $fpga_param_file
+	echo "project_name=$project_name" >> $fpga_param_file
+	echo "port=$uart_port" >> $fpga_param_file
+	echo "uart=$uart_descriptor" >> $fpga_param_file
+
+	archive_file "$fpga_param_file"
+}
+
+kill_and_reap() {
+	local gid
+	# Kill an active process. Ignore errors
+	[ "$1" ] || return 0
+	kill -0 "$1" &>/dev/null || return 0
+
+	# Kill the children
+	kill -- "-$1"  &>/dev/null || true
+	# Kill the group
+	{ gid="$(awk '{print $5}' < /proc/$1/stat)";} 2>/dev/null || return
+	kill -SIGKILL -- "-$gid" &>/dev/null || true
+
+	wait "$gid" &>/dev/null || true
+}
+
+# Perform clean up and ignore errors
+cleanup() {
+	local pid
+
+	# Test success. Kill all background processes so far and wait for them
+	pushd "$pid_dir"
+	set +e
+	while read pid; do
+		pid="$(cat $pid)"
+		kill_and_reap "$pid"
+	done < <(find -name '*.pid')
+	popd
+}
+
+# Launch a program. Have its PID saved in a file with given name with .pid
+# suffix. When the program exits, create a file with .success suffix, or one
+# with .fail if it fails. This function blocks, so the caller must '&' this if
+# they want to continue. Call must wait for $pid_dir/$name.pid to be created
+# should it want to read it.
+launch() {
+	local pid
+
+	"$@" &
+	pid="$!"
+	echo "$pid" > "$pid_dir/${name:?}.pid"
+	if wait "$pid"; then
+		touch "$pid_dir/$name.success"
+	else
+		touch "$pid_dir/$name.fail"
+	fi
+}
+
+# Cleanup actions
+trap cleanup SIGINT SIGHUP SIGTERM EXIT
+
+# Source variables required for run
+source "$artefacts/env"
+
+echo
+echo "RUNNING: $TEST_CONFIG"
+echo
+
+# Accept BIN_MODE from environment, or default to release. If bin_mode is set
+# and non-empty (intended to be set from command line), that takes precedence.
+pkg_bin_mode="${BIN_MODE:-release}"
+bin_mode="${bin_mode:-$pkg_bin_mode}"
+
+artefacts_wd="$artefacts/$bin_mode"
+
+# Change directory so that all binaries can be accessed relative to where they
+# lie
+run_cwd="$artefacts/$bin_mode"
+cd "$run_cwd"
+
+# Source environment for run
+if [ -f "run/env" ]; then
+	source "run/env"
+fi
+
+# Whether to display primary UART progress live on the console
+primary_live="${primary_live-$PRIMARY_LIVE}"
+
+# Assume 0 is the primary UART to track
+primary_uart="${primary_uart:-0}"
+
+# Assume 1 UARTs by default
+num_uarts="${num_uarts:-1}"
+
+# Generate the environment configuration file for the FPGA host.
+for u in $(seq 0 $(( $num_uarts - 1 )) | tac); do
+	descriptor="run/uart$u/descriptor"
+	if [ -f "$descriptor" ]; then
+		uart_descriptor="$(cat "$descriptor")"
+	else
+		echo "Error: No descriptor specified for UART$u"
+		exit 1
+	fi
+
+	baudrate="run/uart$u/baudrate"
+	if [ -f "$baudrate" ]; then
+		uart_baudrate="$(cat "$baudrate")"
+	else
+		echo "Error: No baudrate specified for UART$u"
+		exit 1
+	fi
+
+	port="run/uart$u/port"
+	if [ -f "$port" ]; then
+		uart_port="$(cat "$port")"
+	else
+		echo "Error: No port specified for UART$u"
+		exit 1
+	fi
+
+	fpga="$fpga_cluster" gen_fpga_params
+done
+
+if [ -z "$fpga_user" ]; then
+	echo "FPGA user not configured!"
+	exit 1
+fi
+if [ -z "$fpga_host" ]; then
+	echo "FPGA host not configured!"
+	exit 1
+fi
+remote_user="$fpga_user"
+remote_host="$fpga_host"
+
+echo
+echo "Copying artefacts to $remote_host as user $remote_user"
+echo
+
+# Copy the image to the remote host.
+scp "$artefacts_wd/$fpga_payload" "$remote_user@$remote_host:./$fpga_payload" > \
+							/dev/null
+
+# Copy the env and run scripts to the remote host.
+scp "$artefacts_wd/fpga_env.sh" "$remote_user@$remote_host:." > /dev/null
+scp "$ci_root/script/$fpga_run_script" "$remote_user@$remote_host:." > /dev/null
+
+echo "FPGA configuration options:"
+echo
+cat "$artefacts_wd/fpga_env.sh"
+
+# For an automated run, export a known variable so that we can identify stale
+# processes spawned by Trusted Firmware CI by inspecting its environment.
+export TRUSTED_FIRMWARE_CI="1"
+
+echo
+echo "Executing on $remote_host as user $remote_user"
+echo
+
+# Run the FPGA from the remote host.
+name="fpga_run" launch ssh "$remote_user@$remote_host" "bash ./$fpga_run_script" > \
+							/dev/null 2>&1 &
+
+# Wait enough time for the UART to show up on the FPGA host so the connection
+# can be stablished.
+sleep 35
+
+# If it's a test run, skip all the hoops and start a telnet connection to the FPGA.
+if upon "$test_run"; then
+	telnet "$remote_host" "$(cat "run/uart$primary_uart/port")"
+	exit 0
+fi
+
+# Launch expect scripts for all UARTs
+for u in $(seq 0 $(( $num_uarts - 1 )) | tac); do
+	script="run/uart$u/expect"
+	if [ -f "$script" ]; then
+		script="$(cat "$script")"
+	else
+		script=
+	fi
+
+	# Primary UART must have a script
+	if [ -z "$script" ]; then
+		if [ "$u" = "$primary_uart" ]; then
+			die "No primary UART script!"
+		else
+			echo "Ignoring UART$u (no expect script provided)."
+			continue
+		fi
+	fi
+
+	uart_descriptor="$(cat "run/uart$u/descriptor")"
+
+	timeout="run/uart$u/timeout"
+	uart_port="$(cat "run/uart$u/port")"
+
+	if [ -f "$timeout" ]; then
+		timeout="$(cat "$timeout")"
+	else
+		timeout=
+	fi
+	timeout="${timeout-600}"
+
+	full_log="$run_root/uart${u}_full.txt"
+
+	if [ "$u" = "$primary_uart" ]; then
+		star="*"
+		uart_name="primary_uart"
+	else
+		star=" "
+		uart_name="uart$u"
+	fi
+
+	# Launch expect after exporting required variables
+	(
+	if [ -f "run/uart$u/env" ]; then
+		set -a
+		source "run/uart$u/env"
+		set +a
+	fi
+
+	if [ "$u" = "$primary_uart" ] && upon "$primary_live"; then
+		uart_port="$uart_port" remote_host="$remote_host" timeout="$timeout" \
+			name="$uart_name" launch expect -f "$ci_root/expect/$script" | \
+				tee "$full_log"
+		echo
+	else
+		uart_port="$uart_port" remote_host="$remote_host" timeout="$timeout" \
+			name="$uart_name" launch expect -f "$ci_root/expect/$script" \
+				&>"$full_log"
+	fi
+
+	) &
+
+	echo "Tracking UART$u$star ($uart_descriptor) with $script and timeout $timeout."
+done
+echo
+
+result=0
+
+set +e
+
+# Wait for all the children. Note that the wait below is *not* a timed wait.
+wait -n
+
+pushd "$pid_dir"
+# Wait for fpga_run to finish on the remote server.
+while :; do
+	if [ "$(wc -l < <(ls -l fpga_run.* 2> /dev/null))" -eq 2 ]; then
+		break
+	else
+		sleep 1
+	fi
+done
+
+# Check if there is any failure.
+while :; do
+	# Exit failure if we've any failures
+	if [ "$(wc -l < <(find -name '*.fail'))" -ne 0 ]; then
+		result=1
+		break
+	fi
+
+	# We're done if the primary UART exits success
+	if [ -f "$pid_dir/primary_uart.success" ]; then
+		break
+	fi
+done
+
+cleanup
+
+if [ "$result" -eq 0 ]; then
+	echo "Test success!"
+else
+	echo "Test failed!"
+fi
+
+if upon "$jenkins_run"; then
+	echo
+	echo "Artefacts location: $BUILD_URL."
+	echo
+fi
+
+if upon "$jenkins_run" && upon "$artefacts_receiver" && [ -d "$workspace/run" ]; then
+	source "$CI_ROOT/script/send_artefacts.sh" "run"
+fi
+
+exit "$result"
+# vim: set tw=80 sw=8 noet: