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