284 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			284 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
| #!/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
 | 
