refactor: factor frequently-used run environment variables into getters

A number of variables are defined based on the value of run environment
variables (variables that the test configuration configures and sets in
the `artefacts/run/env` file). There are no helpers to help us keep
track of these and their default values across disjoint scripts, so
let's introduce some!

This introduces getters for the `num_uarts`, `primary_uart`,
`payload_uart` and `ports_script` run environment variables. These were
chosen because we need them in the next patch.

Signed-off-by: Chris Kay <chris.kay@arm.com>
Change-Id: I9ddf6f2fc769e023910652b3a3507f1896f97226
diff --git a/fvp_utils.sh b/fvp_utils.sh
index 2f9a93c..2d9604f 100644
--- a/fvp_utils.sh
+++ b/fvp_utils.sh
@@ -537,16 +537,6 @@
         source "${run_env}"
     fi
 
-    # The following defaults are based on those used by `run_package.sh`!
-
-    local num_uarts="${num_uarts:-4}"
-
-    # The payload UART for most FVPs is the primary UART, and the primary UART
-    # for most FVPs is UART0, but there are some tests and platforms where that
-    # isn't the case.
-    local primary_uart="${primary_uart:-0}"
-    local payload_uart="${payload_uart:-${primary_uart}}"
-
     # TODO: Introduce a mechanism for mapping terminal names to UART indices.
     #
     # In the on-premises CI, the scripts figure out the FVP terminal names by
@@ -564,32 +554,27 @@
     # update the template file with the proper terminal names.
     declare -a ports
 
-    if [ "${ports_script}" = "" ]; then
-        for ((i=0; i<${num_uarts}; i++)); do
-            ports[$i]="terminal_${i}"
-        done
-    else
-        local ports_input=$(mktempfile)
-        local ports_output=$(mktempfile)
+    local ports_input=$(mktempfile)
+    local ports_output=$(mktempfile)
 
-        cat > "${ports_input}" <<-EOF
-			terminal_0
-			terminal_1
-			terminal_2
-			terminal_3
-			terminal_s0
-			terminal_s1
-			terminal_uart_aon
-			terminal_uart_ap
-			terminal_uart0
-			terminal_uart1_ap
-		EOF
+    cat > "${ports_input}" <<-EOF
+		terminal_0
+		terminal_1
+		terminal_2
+		terminal_3
+		terminal_s0
+		terminal_s1
+		terminal_uart_aon
+		terminal_uart_ap
+		terminal_uart0
+		terminal_uart1_ap
+	EOF
 
-        awk -v "num_uarts=${num_uarts}" -f "${ports_script:?}" \
+    awk -v "num_uarts=$(get_num_uarts "${archive}")" \
+        -f "$(get_ports_script "${archive}")" \
             "${ports_input}" > "${ports_output}"
 
-        source "${ports_output}"
-    fi
+    source "${ports_output}"
 
     # Generate the LAVA job definition, minus the test expectations
     expand_template "${yaml_template_file}" > "${yaml_file}"
@@ -612,7 +597,7 @@
 
         # Only handle the primary UART through LAVA. The remaining UARTs are
         # validated after LAVA returns by the post-expect script.
-        if [ "${uart_number:?}" != "uart${primary_uart}" ]; then
+        if [ "${uart_number:?}" != "uart$(get_primary_uart "${archive}")" ]; then
             continue
         fi
 
diff --git a/script/default-ports-script.awk b/script/default-ports-script.awk
new file mode 100644
index 0000000..77fe17b
--- /dev/null
+++ b/script/default-ports-script.awk
@@ -0,0 +1,11 @@
+/terminal_0/ { ports[0] = $NF }
+/terminal_1/ { ports[1] = $NF }
+/terminal_2/ { ports[2] = $NF }
+/terminal_3/ { ports[3] = $NF }
+
+END {
+	for (i = 0; i < num_uarts; i++) {
+		if (ports[i] != "")
+			print "ports[" i "]=" ports[i]
+	}
+}
diff --git a/script/lava-templates/fvp-linux.yaml b/script/lava-templates/fvp-linux.yaml
index 2203d1b..f512690 100644
--- a/script/lava-templates/fvp-linux.yaml
+++ b/script/lava-templates/fvp-linux.yaml
@@ -62,11 +62,11 @@
       local: true
     image: ${model_dir}/${model_bin}
     version_string: ${version_string}
-    console_string: '${ports[${payload_uart:?}]:?}: Listening for serial connection on port (?P<PORT>\d+)'
+    console_string: '${ports[$(get_payload_uart "${archive}")]:?}: Listening for serial connection on port (?P<PORT>\d+)'
     feedbacks:
 
 $(for port in "${ports[@]}"; do
-	if [ "${port}" = "${ports[${payload_uart}]}" ]; then
+	if [ "${port}" = "${ports[$(get_payload_uart "${archive}")]}" ]; then
 		continue
 	fi
 
diff --git a/script/lava-templates/fvp-tftf.yaml b/script/lava-templates/fvp-tftf.yaml
index 8f41e9e..73d0a2e 100644
--- a/script/lava-templates/fvp-tftf.yaml
+++ b/script/lava-templates/fvp-tftf.yaml
@@ -75,11 +75,11 @@
       local: true
     image: ${model_dir}/${model_bin}
     version_string: ${version_string}
-    console_string: '${ports[${payload_uart:?}]:?}: Listening for serial connection on port (?P<PORT>\d+)'
+    console_string: '${ports[$(get_payload_uart "${archive}")]:?}: Listening for serial connection on port (?P<PORT>\d+)'
     feedbacks:
 
 $(for port in "${ports[@]}"; do
-	if [ "${port}" = "${ports[${payload_uart}]}" ]; then
+	if [ "${port}" = "${ports[$(get_payload_uart "${archive}")]}" ]; then
 		continue
 	fi
 
diff --git a/script/run_package.sh b/script/run_package.sh
index 980f19f..26cb745 100755
--- a/script/run_package.sh
+++ b/script/run_package.sh
@@ -136,12 +136,6 @@
 pkg_bin_mode="${BIN_MODE:-release}"
 bin_mode="${bin_mode:-$pkg_bin_mode}"
 
-# Assume 0 is the primary UART to track
-primary_uart=0
-
-# Assume 4 UARTs by default
-num_uarts="${num_uarts:-4}"
-
 # Whether to display primary UART progress live on the console
 primary_live="${primary_live-$PRIMARY_LIVE}"
 
@@ -164,9 +158,6 @@
 	die "No model path set by package!"
 fi
 
-# Assume primary UART is used by test payload as well
-payload_uart="${payload_uart:-$primary_uart}"
-
 # Launch model with parameters
 model_out="$run_root/model_log.txt"
 run_sh="$run_root/run.sh"
@@ -253,23 +244,6 @@
 done
 
 model_pid="$(cat $pid_dir/model.pid)"
-ports_output="$(mktempfile)"
-if not_upon "$ports_script"; then
-	# Default AWK script to parse model ports
-	ports_script="$(mktempfile)"
-	cat <<'EOF' >"$ports_script"
-/terminal_0/ { ports[0] = $NF }
-/terminal_1/ { ports[1] = $NF }
-/terminal_2/ { ports[2] = $NF }
-/terminal_3/ { ports[3] = $NF }
-END {
-	for (i = 0; i < num_uarts; i++) {
-		if (ports[i] != "")
-			print "ports[" i "]=" ports[i]
-	}
-}
-EOF
-fi
 
 # Start a watchdog to kill ourselves if we wait too long for the model
 # response. Note that this is not the timeout for the whole test, but only for
@@ -288,15 +262,17 @@
 ) &
 watchdog="$!"
 
+ports_output="$(mktempfile)"
+
 # Parse UARTs ports from early model output. Send a SIGSTOP to the model
 # as soon as it outputs all UART ports. This is to prevent the model
 # executing before the expect scripts get a chance to connect to the
 # UART thereby losing messages.
 model_fail=1
 while :; do
-	awk -v "num_uarts=$num_uarts" -f "$ports_script" "$model_out" \
-		> "$ports_output"
-	if [ $(wc -l < "$ports_output") -eq "$num_uarts" ]; then
+	awk -v "num_uarts=$(get_num_uarts "${run_cwd}")" \
+		-f "$(get_ports_script "${run_cwd}")" "$model_out" > "$ports_output"
+	if [ $(wc -l < "$ports_output") -eq "$(get_num_uarts "${run_cwd}")" ]; then
 		kill -SIGSTOP "$model_pid"
 		model_fail=0
 		break
@@ -327,7 +303,7 @@
 declare -a ports
 source "$ports_output"
 rm -f "$ports_output"
-if [ "${#ports[@]}" -ne "$num_uarts" ]; then
+if [ "${#ports[@]}" -ne "$(get_num_uarts "${run_cwd}")" ]; then
 	echo "Failed to get UART port numbers"
 	kill_and_reap "$model_pid"
 	unset model_pid
@@ -335,7 +311,7 @@
 
 # Launch expect scripts for all UARTs
 uarts=0
-for u in $(seq 0 $(( $num_uarts - 1 )) | tac); do
+for u in $(seq 0 $(( "$(get_num_uarts "${run_cwd}")" - 1 )) | tac); do
 	script="run/uart$u/expect"
 	if [ -f "$script" ]; then
 		script="$(cat "$script")"
@@ -345,7 +321,7 @@
 
 	# Primary UART must have a script
 	if [ -z "$script" ]; then
-		if [ "$u" = "$primary_uart" ]; then
+		if [ "$u" = "$(get_primary_uart "${run_cwd}")" ]; then
 			die "No primary UART script!"
 		else
 			echo "Ignoring UART$u (no expect script provided)."
@@ -363,7 +339,7 @@
 
 	full_log="$run_root/uart${u}_full.txt"
 
-	if [ "$u" = "$primary_uart" ]; then
+	if [ "$u" = "$(get_primary_uart "${run_cwd}")" ]; then
 		star="*"
 	else
 		star=" "
@@ -379,7 +355,7 @@
 		set +a
 	fi
 
-	if [ "$u" = "$primary_uart" ] && upon "$primary_live"; then
+	if [ "$u" = "$(get_primary_uart "${run_cwd}")" ] && upon "$primary_live"; then
 		uart_port="${ports[$u]}" timeout="$timeout" \
 			name="$uart_name" launch expect -f "$ci_root/expect/$script" | \
 				tee "$full_log"
@@ -438,8 +414,8 @@
 
 	echo "Waiting ${timeout}s for ${#remaining[@]} UART(s): ${remaining[@]}"
 
-	if [[ " ${completed[@]} " =~ " ${payload_uart} " ]]; then
-		echo "- Payload (UART ${payload_uart}) completed!"
+	if [[ " ${completed[@]} " =~ " $(get_payload_uart "${run_cwd}") " ]]; then
+		echo "- Payload (UART $(get_payload_uart "${run_cwd}")) completed!"
 
 		for uart in "${remaining[@]}"; do
 			pid=$(cat "${pid_dir}/uart${uart}.pid")
diff --git a/script/test_fpga_payload.sh b/script/test_fpga_payload.sh
index ee878e2..55a1991 100644
--- a/script/test_fpga_payload.sh
+++ b/script/test_fpga_payload.sh
@@ -135,14 +135,11 @@
 # 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}"
+num_uarts="$(get_num_uarts "${archive}" 1)"
 
 # Generate the environment configuration file for the FPGA host.
-for u in $(seq 0 $(( $num_uarts - 1 )) | tac); do
+for u in $(seq 0 $(( $(get_num_uarts "${archive}") - 1 )) | tac); do
 	descriptor="run/uart$u/descriptor"
 	if [ -f "$descriptor" ]; then
 		uart_descriptor="$(cat "$descriptor")"
@@ -230,12 +227,12 @@
 
 # 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")"
+	telnet "$remote_host" "$(cat "run/uart$(get_primary_uart "${archive}")/port")"
 	exit 0
 fi
 
 # Launch expect scripts for all UARTs
-for u in $(seq 0 $(( $num_uarts - 1 )) | tac); do
+for u in $(seq 0 $(( $(get_num_uarts "${archive}") - 1 )) | tac); do
 	script="run/uart$u/expect"
 	if [ -f "$script" ]; then
 		script="$(cat "$script")"
@@ -245,7 +242,7 @@
 
 	# Primary UART must have a script
 	if [ -z "$script" ]; then
-		if [ "$u" = "$primary_uart" ]; then
+		if [ "$u" = "$(get_primary_uart "${archive}")" ]; then
 			die "No primary UART script!"
 		else
 			echo "Ignoring UART$u (no expect script provided)."
@@ -267,7 +264,7 @@
 
 	full_log="$run_root/uart${u}_full.txt"
 
-	if [ "$u" = "$primary_uart" ]; then
+	if [ "$u" = "$(get_primary_uart "${archive}")" ]; then
 		star="*"
 		uart_name="primary_uart"
 	else
@@ -283,7 +280,7 @@
 		set +a
 	fi
 
-	if [ "$u" = "$primary_uart" ] && upon "$primary_live"; then
+	if [ "$u" = "$(get_primary_uart "${archive}")" ] && 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"
diff --git a/utils.sh b/utils.sh
index 0f7d82a..8a7c350 100644
--- a/utils.sh
+++ b/utils.sh
@@ -258,6 +258,112 @@
 	url="${url:?}" filename="kernel.bin" fetch_and_archive
 }
 
+# Get the path to the run environment variables file.
+#
+# Run environment variables are the test-specific environment variables
+# configured by the CI's test configuration.
+#
+# Usage: get_run_env_path <archive>
+get_run_env_path() {
+	echo "${1:?}/run/env"
+}
+
+# Get a run environment variable.
+#
+# Run environment variables are the test-specific environment variables
+# configured by the CI's test configuration.
+#
+# Usage: get_run_env <archive> <variable> [default]
+get_run_env() {
+	if [ -f "$(get_run_env_path "${1:?}")" ] && [ ! -v "${2:?}" ]; then
+		. "$(get_run_env_path "${1:?}")"
+	fi
+
+	echo "${!2:-${3}}"
+}
+
+# Get the number of UARTs configured by the current test configuration. This
+# defaults to `4`.
+#
+# Usage: get_num_uarts <archive> [default]
+get_num_uarts() {
+	local default=4
+
+	get_run_env "${1:?}" num_uarts "${2-${default}}"
+}
+
+# Get the ports script configured by the current test configuration. This
+# defaults to `script/default-ports-script.awk`.
+#
+# Usage: get_ports_script <archive> [default]
+get_ports_script() {
+	local default="${ci_root}/script/default-ports-script.awk"
+
+	get_run_env "${1:?}" ports_script "${2-${default}}"
+}
+
+# Get the primary UART configured by the current test configuration. This
+# defaults to `0`.
+#
+# Usage: get_primary_uart <archive> [default]
+get_primary_uart() {
+	local default=0
+
+	get_run_env "${1:?}" primary_uart "${2-${default}}"
+}
+
+# Get the payload UART configured by the current test configuration. This
+# defaults to the primary UART.
+#
+# Usage: get_payload_uart <archive> [default]
+get_payload_uart() {
+	local default="$(get_primary_uart "${1:?}")"
+
+	get_run_env "${1:?}" payload_uart "${2-${default}}"
+}
+
+# Get the path to a UART's environment variable directory.
+#
+# UART environment variables are the UART-specific environment variables
+# configured by the CI's test configuration.
+#
+# Usage: get_uart_env_path <archive> <uart>
+get_uart_env_path() {
+	echo "${1:?}/run/uart${2:?}"
+}
+
+# Get a UART environment variable.
+#
+# UART environment variables are the UART-specific environment variables
+# configured by the CI's test configuration.
+#
+# Usage: get_uart_env <archive> <uart> <variable> [default]
+get_uart_env() {
+	if [ ! -v "${3:?}" ] && [ -f "$(get_uart_env_path "${1:?}" "${2:?}")/${3:?}" ]; then
+		cat "$(get_uart_env_path "${1:?}" "${2:?}")/${3:?}"
+	else
+		echo "${!3:?-${4}}"
+	fi
+}
+
+# Get the path to the Expect script for a given UART. This defaults to nothing.
+#
+# Usage: get_uart_expect_script <archive> <uart> [default]
+get_uart_expect_script() {
+	local default=
+
+	get_uart_env "${1:?}" "${2:?}" expect "${3-${default}}"
+}
+
+# Get the FVP port for a given UART. This defaults to `5000 + ${uart}`.
+#
+# Usage: get_uart_port <archive> <uart> [default]
+get_uart_port() {
+	local default="$(( 5000 + "${2:?}" ))"
+
+	get_uart_env "${1:?}" "${2:?}" port "${3-${default}}"
+}
+
 # Make a temporary directory/file insdie workspace, so that it doesn't need to
 # be cleaned up. Jenkins is setup to clean up workspace before a job runs.
 mktempdir() {