Home | History | Annotate | Download | only in firmware
      1 #!/bin/bash
      2 # SPDX-License-Identifier: GPL-2.0
      3 # This validates that the kernel will fall back to using the fallback mechanism
      4 # to load firmware it can't find on disk itself. We must request a firmware
      5 # that the kernel won't find, and any installed helper (e.g. udev) also
      6 # won't find so that we can do the load ourself manually.
      7 set -e
      8 
      9 TEST_REQS_FW_SYSFS_FALLBACK="yes"
     10 TEST_REQS_FW_SET_CUSTOM_PATH="no"
     11 TEST_DIR=$(dirname $0)
     12 source $TEST_DIR/fw_lib.sh
     13 
     14 check_mods
     15 check_setup
     16 verify_reqs
     17 setup_tmp_file
     18 
     19 trap "test_finish" EXIT
     20 
     21 load_fw()
     22 {
     23 	local name="$1"
     24 	local file="$2"
     25 
     26 	# This will block until our load (below) has finished.
     27 	echo -n "$name" >"$DIR"/trigger_request &
     28 
     29 	# Give kernel a chance to react.
     30 	local timeout=10
     31 	while [ ! -e "$DIR"/"$name"/loading ]; do
     32 		sleep 0.1
     33 		timeout=$(( $timeout - 1 ))
     34 		if [ "$timeout" -eq 0 ]; then
     35 			echo "$0: firmware interface never appeared" >&2
     36 			exit 1
     37 		fi
     38 	done
     39 
     40 	echo 1 >"$DIR"/"$name"/loading
     41 	cat "$file" >"$DIR"/"$name"/data
     42 	echo 0 >"$DIR"/"$name"/loading
     43 
     44 	# Wait for request to finish.
     45 	wait
     46 }
     47 
     48 load_fw_cancel()
     49 {
     50 	local name="$1"
     51 	local file="$2"
     52 
     53 	# This will block until our load (below) has finished.
     54 	echo -n "$name" >"$DIR"/trigger_request 2>/dev/null &
     55 
     56 	# Give kernel a chance to react.
     57 	local timeout=10
     58 	while [ ! -e "$DIR"/"$name"/loading ]; do
     59 		sleep 0.1
     60 		timeout=$(( $timeout - 1 ))
     61 		if [ "$timeout" -eq 0 ]; then
     62 			echo "$0: firmware interface never appeared" >&2
     63 			exit 1
     64 		fi
     65 	done
     66 
     67 	echo -1 >"$DIR"/"$name"/loading
     68 
     69 	# Wait for request to finish.
     70 	wait
     71 }
     72 
     73 load_fw_custom()
     74 {
     75 	if [ ! -e "$DIR"/trigger_custom_fallback ]; then
     76 		echo "$0: custom fallback trigger not present, ignoring test" >&2
     77 		exit $ksft_skip
     78 	fi
     79 
     80 	local name="$1"
     81 	local file="$2"
     82 
     83 	echo -n "$name" >"$DIR"/trigger_custom_fallback 2>/dev/null &
     84 
     85 	# Give kernel a chance to react.
     86 	local timeout=10
     87 	while [ ! -e "$DIR"/"$name"/loading ]; do
     88 		sleep 0.1
     89 		timeout=$(( $timeout - 1 ))
     90 		if [ "$timeout" -eq 0 ]; then
     91 			echo "$0: firmware interface never appeared" >&2
     92 			exit 1
     93 		fi
     94 	done
     95 
     96 	echo 1 >"$DIR"/"$name"/loading
     97 	cat "$file" >"$DIR"/"$name"/data
     98 	echo 0 >"$DIR"/"$name"/loading
     99 
    100 	# Wait for request to finish.
    101 	wait
    102 	return 0
    103 }
    104 
    105 
    106 load_fw_custom_cancel()
    107 {
    108 	if [ ! -e "$DIR"/trigger_custom_fallback ]; then
    109 		echo "$0: canceling custom fallback trigger not present, ignoring test" >&2
    110 		exit $ksft_skip
    111 	fi
    112 
    113 	local name="$1"
    114 	local file="$2"
    115 
    116 	echo -n "$name" >"$DIR"/trigger_custom_fallback 2>/dev/null &
    117 
    118 	# Give kernel a chance to react.
    119 	local timeout=10
    120 	while [ ! -e "$DIR"/"$name"/loading ]; do
    121 		sleep 0.1
    122 		timeout=$(( $timeout - 1 ))
    123 		if [ "$timeout" -eq 0 ]; then
    124 			echo "$0: firmware interface never appeared" >&2
    125 			exit 1
    126 		fi
    127 	done
    128 
    129 	echo -1 >"$DIR"/"$name"/loading
    130 
    131 	# Wait for request to finish.
    132 	wait
    133 	return 0
    134 }
    135 
    136 load_fw_fallback_with_child()
    137 {
    138 	local name="$1"
    139 	local file="$2"
    140 
    141 	# This is the value already set but we want to be explicit
    142 	echo 4 >/sys/class/firmware/timeout
    143 
    144 	sleep 1 &
    145 	SECONDS_BEFORE=$(date +%s)
    146 	echo -n "$name" >"$DIR"/trigger_request 2>/dev/null
    147 	SECONDS_AFTER=$(date +%s)
    148 	SECONDS_DELTA=$(($SECONDS_AFTER - $SECONDS_BEFORE))
    149 	if [ "$SECONDS_DELTA" -lt 4 ]; then
    150 		RET=1
    151 	else
    152 		RET=0
    153 	fi
    154 	wait
    155 	return $RET
    156 }
    157 
    158 test_syfs_timeout()
    159 {
    160 	DEVPATH="$DIR"/"nope-$NAME"/loading
    161 
    162 	# Test failure when doing nothing (timeout works).
    163 	echo -n 2 >/sys/class/firmware/timeout
    164 	echo -n "nope-$NAME" >"$DIR"/trigger_request 2>/dev/null &
    165 
    166 	# Give the kernel some time to load the loading file, must be less
    167 	# than the timeout above.
    168 	sleep 1
    169 	if [ ! -f $DEVPATH ]; then
    170 		echo "$0: fallback mechanism immediately cancelled"
    171 		echo ""
    172 		echo "The file never appeared: $DEVPATH"
    173 		echo ""
    174 		echo "This might be a distribution udev rule setup by your distribution"
    175 		echo "to immediately cancel all fallback requests, this must be"
    176 		echo "removed before running these tests. To confirm look for"
    177 		echo "a firmware rule like /lib/udev/rules.d/50-firmware.rules"
    178 		echo "and see if you have something like this:"
    179 		echo ""
    180 		echo "SUBSYSTEM==\"firmware\", ACTION==\"add\", ATTR{loading}=\"-1\""
    181 		echo ""
    182 		echo "If you do remove this file or comment out this line before"
    183 		echo "proceeding with these tests."
    184 		exit 1
    185 	fi
    186 
    187 	if diff -q "$FW" /dev/test_firmware >/dev/null ; then
    188 		echo "$0: firmware was not expected to match" >&2
    189 		exit 1
    190 	else
    191 		echo "$0: timeout works"
    192 	fi
    193 }
    194 
    195 run_sysfs_main_tests()
    196 {
    197 	test_syfs_timeout
    198 	# Put timeout high enough for us to do work but not so long that failures
    199 	# slow down this test too much.
    200 	echo 4 >/sys/class/firmware/timeout
    201 
    202 	# Load this script instead of the desired firmware.
    203 	load_fw "$NAME" "$0"
    204 	if diff -q "$FW" /dev/test_firmware >/dev/null ; then
    205 		echo "$0: firmware was not expected to match" >&2
    206 		exit 1
    207 	else
    208 		echo "$0: firmware comparison works"
    209 	fi
    210 
    211 	# Do a proper load, which should work correctly.
    212 	load_fw "$NAME" "$FW"
    213 	if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then
    214 		echo "$0: firmware was not loaded" >&2
    215 		exit 1
    216 	else
    217 		echo "$0: fallback mechanism works"
    218 	fi
    219 
    220 	load_fw_cancel "nope-$NAME" "$FW"
    221 	if diff -q "$FW" /dev/test_firmware >/dev/null ; then
    222 		echo "$0: firmware was expected to be cancelled" >&2
    223 		exit 1
    224 	else
    225 		echo "$0: cancelling fallback mechanism works"
    226 	fi
    227 
    228 	set +e
    229 	load_fw_fallback_with_child "nope-signal-$NAME" "$FW"
    230 	if [ "$?" -eq 0 ]; then
    231 		echo "$0: SIGCHLD on sync ignored as expected" >&2
    232 	else
    233 		echo "$0: error - sync firmware request cancelled due to SIGCHLD" >&2
    234 		exit 1
    235 	fi
    236 	set -e
    237 }
    238 
    239 run_sysfs_custom_load_tests()
    240 {
    241 	RANDOM_FILE_PATH=$(setup_random_file)
    242 	RANDOM_FILE="$(basename $RANDOM_FILE_PATH)"
    243 	if load_fw_custom "$RANDOM_FILE" "$RANDOM_FILE_PATH" ; then
    244 		if ! diff -q "$RANDOM_FILE_PATH" /dev/test_firmware >/dev/null ; then
    245 			echo "$0: firmware was not loaded" >&2
    246 			exit 1
    247 		else
    248 			echo "$0: custom fallback loading mechanism works"
    249 		fi
    250 	fi
    251 
    252 	RANDOM_FILE_PATH=$(setup_random_file)
    253 	RANDOM_FILE="$(basename $RANDOM_FILE_PATH)"
    254 	if load_fw_custom "$RANDOM_FILE" "$RANDOM_FILE_PATH" ; then
    255 		if ! diff -q "$RANDOM_FILE_PATH" /dev/test_firmware >/dev/null ; then
    256 			echo "$0: firmware was not loaded" >&2
    257 			exit 1
    258 		else
    259 			echo "$0: custom fallback loading mechanism works"
    260 		fi
    261 	fi
    262 
    263 	RANDOM_FILE_REAL="$RANDOM_FILE_PATH"
    264 	FAKE_RANDOM_FILE_PATH=$(setup_random_file_fake)
    265 	FAKE_RANDOM_FILE="$(basename $FAKE_RANDOM_FILE_PATH)"
    266 
    267 	if load_fw_custom_cancel "$FAKE_RANDOM_FILE" "$RANDOM_FILE_REAL" ; then
    268 		if diff -q "$RANDOM_FILE_PATH" /dev/test_firmware >/dev/null ; then
    269 			echo "$0: firmware was expected to be cancelled" >&2
    270 			exit 1
    271 		else
    272 			echo "$0: cancelling custom fallback mechanism works"
    273 		fi
    274 	fi
    275 }
    276 
    277 if [ "$HAS_FW_LOADER_USER_HELPER_FALLBACK" = "yes" ]; then
    278 	run_sysfs_main_tests
    279 fi
    280 
    281 run_sysfs_custom_load_tests
    282 
    283 exit 0
    284