Commit a37ddddd authored by Russ Weight's avatar Russ Weight Committed by Greg Kroah-Hartman
Browse files

selftests: firmware: Add firmware upload selftests



Add selftests to verify the firmware upload mechanism. These test
include simple firmware uploads as well as upload cancellation and
error injection. The test creates three firmware devices and verifies
that they all work correctly and independently.

Tested-by: default avatarMatthew Gerlach <matthew.gerlach@linux.intel.com>
Reviewed-by: default avatarLuis Chamberlain <mcgrof@kernel.org>
Reviewed-by: default avatarTianfei zhang <tianfei.zhang@intel.com>
Signed-off-by: default avatarRuss Weight <russell.h.weight@intel.com>
Link: https://lore.kernel.org/r/20220426163532.114961-1-russell.h.weight@intel.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent cebdc534
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@ CFLAGS = -Wall \
         -O2

TEST_PROGS := fw_run_tests.sh
TEST_FILES := fw_fallback.sh fw_filesystem.sh fw_lib.sh
TEST_FILES := fw_fallback.sh fw_filesystem.sh fw_upload.sh fw_lib.sh
TEST_GEN_FILES := fw_namespace

include ../lib.mk
+1 −0
Original line number Diff line number Diff line
@@ -3,3 +3,4 @@ CONFIG_FW_LOADER=y
CONFIG_FW_LOADER_USER_HELPER=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_FW_UPLOAD=y
+7 −0
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ check_setup()
	HAS_FW_LOADER_USER_HELPER_FALLBACK="$(kconfig_has CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y)"
	HAS_FW_LOADER_COMPRESS_XZ="$(kconfig_has CONFIG_FW_LOADER_COMPRESS_XZ=y)"
	HAS_FW_LOADER_COMPRESS_ZSTD="$(kconfig_has CONFIG_FW_LOADER_COMPRESS_ZSTD=y)"
	HAS_FW_UPLOAD="$(kconfig_has CONFIG_FW_UPLOAD=y)"
	PROC_FW_IGNORE_SYSFS_FALLBACK="0"
	PROC_FW_FORCE_SYSFS_FALLBACK="0"

@@ -119,6 +120,12 @@ verify_reqs()
			exit 0
		fi
	fi
	if [ "$TEST_REQS_FW_UPLOAD" = "yes" ]; then
		if [ ! "$HAS_FW_UPLOAD" = "yes" ]; then
			echo "firmware upload disabled so ignoring test"
			exit 0
		fi
	fi
}

setup_tmp_file()
+4 −0
Original line number Diff line number Diff line
@@ -22,6 +22,10 @@ run_tests()
	proc_set_force_sysfs_fallback $1
	proc_set_ignore_sysfs_fallback $2
	$TEST_DIR/fw_fallback.sh

	proc_set_force_sysfs_fallback $1
	proc_set_ignore_sysfs_fallback $2
	$TEST_DIR/fw_upload.sh
}

run_test_config_0001()
+214 −0
Original line number Diff line number Diff line
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# This validates the user-initiated fw upload mechanism of the firmware
# loader. It verifies that one or more firmware devices can be created
# for a device driver. It also verifies the data transfer, the
# cancellation support, and the error flows.
set -e

TEST_REQS_FW_UPLOAD="yes"
TEST_DIR=$(dirname $0)

progress_states="preparing transferring  programming"
errors="hw-error
	timeout
	device-busy
	invalid-file-size
	read-write-error
	flash-wearout"
error_abort="user-abort"
fwname1=fw1
fwname2=fw2
fwname3=fw3

source $TEST_DIR/fw_lib.sh

check_mods
check_setup
verify_reqs

trap "upload_finish" EXIT

upload_finish() {
	local fwdevs="$fwname1 $fwname2 $fwname3"

	for name in $fwdevs; do
		if [ -e "$DIR/$name" ]; then
			echo -n "$name" > "$DIR"/upload_unregister
		fi
	done
}

upload_fw() {
	local name="$1"
	local file="$2"

	echo 1 > "$DIR"/"$name"/loading
	cat "$file" > "$DIR"/"$name"/data
	echo 0 > "$DIR"/"$name"/loading
}

verify_fw() {
	local name="$1"
	local file="$2"

	echo -n "$name" > "$DIR"/config_upload_name
	if ! cmp "$file" "$DIR"/upload_read > /dev/null 2>&1; then
		echo "$0: firmware compare for $name did not match" >&2
		exit 1
	fi

	echo "$0: firmware upload for $name works" >&2
	return 0
}

inject_error() {
	local name="$1"
	local status="$2"
	local error="$3"

	echo 1 > "$DIR"/"$name"/loading
	echo -n "inject":"$status":"$error" > "$DIR"/"$name"/data
	echo 0 > "$DIR"/"$name"/loading
}

await_status() {
	local name="$1"
	local expected="$2"
	local status
	local i

	let i=0
	while [ $i -lt 50 ]; do
		status=$(cat "$DIR"/"$name"/status)
		if [ "$status" = "$expected" ]; then
			return 0;
		fi
		sleep 1e-03
		let i=$i+1
	done

	echo "$0: Invalid status: Expected $expected, Actual $status" >&2
	return 1;
}

await_idle() {
	local name="$1"

	await_status "$name" "idle"
	return $?
}

expect_error() {
	local name="$1"
	local expected="$2"
	local error=$(cat "$DIR"/"$name"/error)

	if [ "$error" != "$expected" ]; then
		echo "Invalid error: Expected $expected, Actual $error" >&2
		return 1
	fi

	return 0
}

random_firmware() {
	local bs="$1"
	local count="$2"
	local file=$(mktemp -p /tmp uploadfwXXX.bin)

	dd if=/dev/urandom of="$file" bs="$bs" count="$count" > /dev/null 2>&1
	echo "$file"
}

test_upload_cancel() {
	local name="$1"
	local status

	for status in $progress_states; do
		inject_error $name $status $error_abort
		if ! await_status $name $status; then
			exit 1
		fi

		echo 1 > "$DIR"/"$name"/cancel

		if ! await_idle $name; then
			exit 1
		fi

		if ! expect_error $name "$status":"$error_abort"; then
			exit 1
		fi
	done

	echo "$0: firmware upload cancellation works"
	return 0
}

test_error_handling() {
	local name=$1
	local status
	local error

	for status in $progress_states; do
		for error in $errors; do
			inject_error $name $status $error

			if ! await_idle $name; then
				exit 1
			fi

			if ! expect_error $name "$status":"$error"; then
				exit 1
			fi

		done
	done
	echo "$0: firmware upload error handling works"
}

test_fw_too_big() {
	local name=$1
	local fw_too_big=`random_firmware 512 5`
	local expected="preparing:invalid-file-size"

	upload_fw $name $fw_too_big
	rm -f $fw_too_big

	if ! await_idle $name; then
		exit 1
	fi

	if ! expect_error $name $expected; then
		exit 1
	fi

	echo "$0: oversized firmware error handling works"
}

echo -n "$fwname1" > "$DIR"/upload_register
echo -n "$fwname2" > "$DIR"/upload_register
echo -n "$fwname3" > "$DIR"/upload_register

test_upload_cancel $fwname1
test_error_handling $fwname1
test_fw_too_big $fwname1

fw_file1=`random_firmware 512 4`
fw_file2=`random_firmware 512 3`
fw_file3=`random_firmware 512 2`

upload_fw $fwname1 $fw_file1
upload_fw $fwname2 $fw_file2
upload_fw $fwname3 $fw_file3

verify_fw ${fwname1} ${fw_file1}
verify_fw ${fwname2} ${fw_file2}
verify_fw ${fwname3} ${fw_file3}

echo -n "$fwname1" > "$DIR"/upload_unregister
echo -n "$fwname2" > "$DIR"/upload_unregister
echo -n "$fwname3" > "$DIR"/upload_unregister

exit 0