123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- #!/bin/bash
- # SPDX-License-Identifier: GPL-2.0
- # This validates that the kernel will fall back to using the fallback mechanism
- # to load firmware it can't find on disk itself. We must request a firmware
- # that the kernel won't find, and any installed helper (e.g. udev) also
- # won't find so that we can do the load ourself manually.
- set -e
- TEST_REQS_FW_SYSFS_FALLBACK="yes"
- TEST_REQS_FW_SET_CUSTOM_PATH="no"
- TEST_DIR=$(dirname $0)
- source $TEST_DIR/fw_lib.sh
- check_mods
- check_setup
- verify_reqs
- setup_tmp_file
- trap "test_finish" EXIT
- load_fw()
- {
- local name="$1"
- local file="$2"
- # This will block until our load (below) has finished.
- echo -n "$name" >"$DIR"/trigger_request &
- # Give kernel a chance to react.
- local timeout=10
- while [ ! -e "$DIR"/"$name"/loading ]; do
- sleep 0.1
- timeout=$(( $timeout - 1 ))
- if [ "$timeout" -eq 0 ]; then
- echo "$0: firmware interface never appeared" >&2
- exit 1
- fi
- done
- echo 1 >"$DIR"/"$name"/loading
- cat "$file" >"$DIR"/"$name"/data
- echo 0 >"$DIR"/"$name"/loading
- # Wait for request to finish.
- wait
- }
- load_fw_cancel()
- {
- local name="$1"
- local file="$2"
- # This will block until our load (below) has finished.
- echo -n "$name" >"$DIR"/trigger_request 2>/dev/null &
- # Give kernel a chance to react.
- local timeout=10
- while [ ! -e "$DIR"/"$name"/loading ]; do
- sleep 0.1
- timeout=$(( $timeout - 1 ))
- if [ "$timeout" -eq 0 ]; then
- echo "$0: firmware interface never appeared" >&2
- exit 1
- fi
- done
- echo -1 >"$DIR"/"$name"/loading
- # Wait for request to finish.
- wait
- }
- load_fw_custom()
- {
- if [ ! -e "$DIR"/trigger_custom_fallback ]; then
- echo "$0: custom fallback trigger not present, ignoring test" >&2
- exit $ksft_skip
- fi
- local name="$1"
- local file="$2"
- echo -n "$name" >"$DIR"/trigger_custom_fallback 2>/dev/null &
- # Give kernel a chance to react.
- local timeout=10
- while [ ! -e "$DIR"/"$name"/loading ]; do
- sleep 0.1
- timeout=$(( $timeout - 1 ))
- if [ "$timeout" -eq 0 ]; then
- echo "$0: firmware interface never appeared" >&2
- exit 1
- fi
- done
- echo 1 >"$DIR"/"$name"/loading
- cat "$file" >"$DIR"/"$name"/data
- echo 0 >"$DIR"/"$name"/loading
- # Wait for request to finish.
- wait
- return 0
- }
- load_fw_custom_cancel()
- {
- if [ ! -e "$DIR"/trigger_custom_fallback ]; then
- echo "$0: canceling custom fallback trigger not present, ignoring test" >&2
- exit $ksft_skip
- fi
- local name="$1"
- local file="$2"
- echo -n "$name" >"$DIR"/trigger_custom_fallback 2>/dev/null &
- # Give kernel a chance to react.
- local timeout=10
- while [ ! -e "$DIR"/"$name"/loading ]; do
- sleep 0.1
- timeout=$(( $timeout - 1 ))
- if [ "$timeout" -eq 0 ]; then
- echo "$0: firmware interface never appeared" >&2
- exit 1
- fi
- done
- echo -1 >"$DIR"/"$name"/loading
- # Wait for request to finish.
- wait
- return 0
- }
- load_fw_fallback_with_child()
- {
- local name="$1"
- local file="$2"
- # This is the value already set but we want to be explicit
- echo 4 >/sys/class/firmware/timeout
- sleep 1 &
- SECONDS_BEFORE=$(date +%s)
- echo -n "$name" >"$DIR"/trigger_request 2>/dev/null
- SECONDS_AFTER=$(date +%s)
- SECONDS_DELTA=$(($SECONDS_AFTER - $SECONDS_BEFORE))
- if [ "$SECONDS_DELTA" -lt 4 ]; then
- RET=1
- else
- RET=0
- fi
- wait
- return $RET
- }
- test_syfs_timeout()
- {
- DEVPATH="$DIR"/"nope-$NAME"/loading
- # Test failure when doing nothing (timeout works).
- echo -n 2 >/sys/class/firmware/timeout
- echo -n "nope-$NAME" >"$DIR"/trigger_request 2>/dev/null &
- # Give the kernel some time to load the loading file, must be less
- # than the timeout above.
- sleep 1
- if [ ! -f $DEVPATH ]; then
- echo "$0: fallback mechanism immediately cancelled"
- echo ""
- echo "The file never appeared: $DEVPATH"
- echo ""
- echo "This might be a distribution udev rule setup by your distribution"
- echo "to immediately cancel all fallback requests, this must be"
- echo "removed before running these tests. To confirm look for"
- echo "a firmware rule like /lib/udev/rules.d/50-firmware.rules"
- echo "and see if you have something like this:"
- echo ""
- echo "SUBSYSTEM==\"firmware\", ACTION==\"add\", ATTR{loading}=\"-1\""
- echo ""
- echo "If you do remove this file or comment out this line before"
- echo "proceeding with these tests."
- exit 1
- fi
- if diff -q "$FW" /dev/test_firmware >/dev/null ; then
- echo "$0: firmware was not expected to match" >&2
- exit 1
- else
- echo "$0: timeout works"
- fi
- }
- run_sysfs_main_tests()
- {
- test_syfs_timeout
- # Put timeout high enough for us to do work but not so long that failures
- # slow down this test too much.
- echo 4 >/sys/class/firmware/timeout
- # Load this script instead of the desired firmware.
- load_fw "$NAME" "$0"
- if diff -q "$FW" /dev/test_firmware >/dev/null ; then
- echo "$0: firmware was not expected to match" >&2
- exit 1
- else
- echo "$0: firmware comparison works"
- fi
- # Do a proper load, which should work correctly.
- load_fw "$NAME" "$FW"
- if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then
- echo "$0: firmware was not loaded" >&2
- exit 1
- else
- echo "$0: fallback mechanism works"
- fi
- load_fw_cancel "nope-$NAME" "$FW"
- if diff -q "$FW" /dev/test_firmware >/dev/null ; then
- echo "$0: firmware was expected to be cancelled" >&2
- exit 1
- else
- echo "$0: cancelling fallback mechanism works"
- fi
- set +e
- load_fw_fallback_with_child "nope-signal-$NAME" "$FW"
- if [ "$?" -eq 0 ]; then
- echo "$0: SIGCHLD on sync ignored as expected" >&2
- else
- echo "$0: error - sync firmware request cancelled due to SIGCHLD" >&2
- exit 1
- fi
- set -e
- }
- run_sysfs_custom_load_tests()
- {
- RANDOM_FILE_PATH=$(setup_random_file)
- RANDOM_FILE="$(basename $RANDOM_FILE_PATH)"
- if load_fw_custom "$RANDOM_FILE" "$RANDOM_FILE_PATH" ; then
- if ! diff -q "$RANDOM_FILE_PATH" /dev/test_firmware >/dev/null ; then
- echo "$0: firmware was not loaded" >&2
- exit 1
- else
- echo "$0: custom fallback loading mechanism works"
- fi
- fi
- RANDOM_FILE_PATH=$(setup_random_file)
- RANDOM_FILE="$(basename $RANDOM_FILE_PATH)"
- if load_fw_custom "$RANDOM_FILE" "$RANDOM_FILE_PATH" ; then
- if ! diff -q "$RANDOM_FILE_PATH" /dev/test_firmware >/dev/null ; then
- echo "$0: firmware was not loaded" >&2
- exit 1
- else
- echo "$0: custom fallback loading mechanism works"
- fi
- fi
- RANDOM_FILE_REAL="$RANDOM_FILE_PATH"
- FAKE_RANDOM_FILE_PATH=$(setup_random_file_fake)
- FAKE_RANDOM_FILE="$(basename $FAKE_RANDOM_FILE_PATH)"
- if load_fw_custom_cancel "$FAKE_RANDOM_FILE" "$RANDOM_FILE_REAL" ; then
- if diff -q "$RANDOM_FILE_PATH" /dev/test_firmware >/dev/null ; then
- echo "$0: firmware was expected to be cancelled" >&2
- exit 1
- else
- echo "$0: cancelling custom fallback mechanism works"
- fi
- fi
- }
- if [ "$HAS_FW_LOADER_USER_HELPER_FALLBACK" = "yes" ]; then
- run_sysfs_main_tests
- fi
- run_sysfs_custom_load_tests
- exit 0
|