selftests: firmware: Add firmware upload selftests
authorRuss Weight <russell.h.weight@intel.com>
Tue, 26 Apr 2022 16:35:32 +0000 (09:35 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 29 Apr 2022 14:49:36 +0000 (16:49 +0200)
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: Matthew Gerlach <matthew.gerlach@linux.intel.com>
Reviewed-by: Luis Chamberlain <mcgrof@kernel.org>
Reviewed-by: Tianfei zhang <tianfei.zhang@intel.com>
Signed-off-by: Russ Weight <russell.h.weight@intel.com>
Link: https://lore.kernel.org/r/20220426163532.114961-1-russell.h.weight@intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
tools/testing/selftests/firmware/Makefile
tools/testing/selftests/firmware/config
tools/testing/selftests/firmware/fw_lib.sh
tools/testing/selftests/firmware/fw_run_tests.sh
tools/testing/selftests/firmware/fw_upload.sh [new file with mode: 0755]

index 40211cd..7992969 100644 (file)
@@ -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
index bf634dd..6e40251 100644 (file)
@@ -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
index 3fa8282..7bffd67 100755 (executable)
@@ -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()
index 7773770..f6d95a2 100755 (executable)
@@ -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()
diff --git a/tools/testing/selftests/firmware/fw_upload.sh b/tools/testing/selftests/firmware/fw_upload.sh
new file mode 100755 (executable)
index 0000000..c7a6f06
--- /dev/null
@@ -0,0 +1,214 @@
+#!/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