1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3# This validates that the kernel will load firmware out of its list of 4# firmware locations on disk. Since the user helper does similar work, 5# we reset the custom load directory to a location the user helper doesn't 6# know so we can be sure we're not accidentally testing the user helper. 7set -e 8 9TEST_DIR=$(dirname $0) 10source $TEST_DIR/fw_lib.sh 11 12check_mods 13 14# CONFIG_FW_LOADER_USER_HELPER has a sysfs class under /sys/class/firmware/ 15# These days most distros enable CONFIG_FW_LOADER_USER_HELPER but disable 16# CONFIG_FW_LOADER_USER_HELPER_FALLBACK. We use /sys/class/firmware/ as an 17# indicator for CONFIG_FW_LOADER_USER_HELPER. 18HAS_FW_LOADER_USER_HELPER=$(if [ -d /sys/class/firmware/ ]; then echo yes; else echo no; fi) 19 20if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then 21 OLD_TIMEOUT=$(cat /sys/class/firmware/timeout) 22fi 23 24OLD_FWPATH=$(cat /sys/module/firmware_class/parameters/path) 25 26FWPATH=$(mktemp -d) 27FW="$FWPATH/test-firmware.bin" 28 29test_finish() 30{ 31 if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then 32 echo "$OLD_TIMEOUT" >/sys/class/firmware/timeout 33 fi 34 if [ "$OLD_FWPATH" = "" ]; then 35 OLD_FWPATH=" " 36 fi 37 echo -n "$OLD_FWPATH" >/sys/module/firmware_class/parameters/path 38 rm -f "$FW" 39 rmdir "$FWPATH" 40} 41 42trap "test_finish" EXIT 43 44if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then 45 # Turn down the timeout so failures don't take so long. 46 echo 1 >/sys/class/firmware/timeout 47fi 48 49# Set the kernel search path. 50echo -n "$FWPATH" >/sys/module/firmware_class/parameters/path 51 52# This is an unlikely real-world firmware content. :) 53echo "ABCD0123" >"$FW" 54 55NAME=$(basename "$FW") 56 57if printf '\000' >"$DIR"/trigger_request 2> /dev/null; then 58 echo "$0: empty filename should not succeed" >&2 59 exit 1 60fi 61 62if [ ! -e "$DIR"/trigger_async_request ]; then 63 echo "$0: empty filename: async trigger not present, ignoring test" >&2 64else 65 if printf '\000' >"$DIR"/trigger_async_request 2> /dev/null; then 66 echo "$0: empty filename should not succeed (async)" >&2 67 exit 1 68 fi 69fi 70 71# Request a firmware that doesn't exist, it should fail. 72if echo -n "nope-$NAME" >"$DIR"/trigger_request 2> /dev/null; then 73 echo "$0: firmware shouldn't have loaded" >&2 74 exit 1 75fi 76if diff -q "$FW" /dev/test_firmware >/dev/null ; then 77 echo "$0: firmware was not expected to match" >&2 78 exit 1 79else 80 if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then 81 echo "$0: timeout works" 82 fi 83fi 84 85# This should succeed via kernel load or will fail after 1 second after 86# being handed over to the user helper, which won't find the fw either. 87if ! echo -n "$NAME" >"$DIR"/trigger_request ; then 88 echo "$0: could not trigger request" >&2 89 exit 1 90fi 91 92# Verify the contents are what we expect. 93if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then 94 echo "$0: firmware was not loaded" >&2 95 exit 1 96else 97 echo "$0: filesystem loading works" 98fi 99 100# Try the asynchronous version too 101if [ ! -e "$DIR"/trigger_async_request ]; then 102 echo "$0: firmware loading: async trigger not present, ignoring test" >&2 103else 104 if ! echo -n "$NAME" >"$DIR"/trigger_async_request ; then 105 echo "$0: could not trigger async request" >&2 106 exit 1 107 fi 108 109 # Verify the contents are what we expect. 110 if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then 111 echo "$0: firmware was not loaded (async)" >&2 112 exit 1 113 else 114 echo "$0: async filesystem loading works" 115 fi 116fi 117 118### Batched requests tests 119test_config_present() 120{ 121 if [ ! -f $DIR/reset ]; then 122 echo "Configuration triggers not present, ignoring test" 123 exit 0 124 fi 125} 126 127# Defaults : 128# 129# send_uevent: 1 130# sync_direct: 0 131# name: test-firmware.bin 132# num_requests: 4 133config_reset() 134{ 135 echo 1 > $DIR/reset 136} 137 138release_all_firmware() 139{ 140 echo 1 > $DIR/release_all_firmware 141} 142 143config_set_name() 144{ 145 echo -n $1 > $DIR/config_name 146} 147 148config_set_sync_direct() 149{ 150 echo 1 > $DIR/config_sync_direct 151} 152 153config_unset_sync_direct() 154{ 155 echo 0 > $DIR/config_sync_direct 156} 157 158config_set_uevent() 159{ 160 echo 1 > $DIR/config_send_uevent 161} 162 163config_unset_uevent() 164{ 165 echo 0 > $DIR/config_send_uevent 166} 167 168config_trigger_sync() 169{ 170 echo -n 1 > $DIR/trigger_batched_requests 2>/dev/null 171} 172 173config_trigger_async() 174{ 175 echo -n 1 > $DIR/trigger_batched_requests_async 2> /dev/null 176} 177 178config_set_read_fw_idx() 179{ 180 echo -n $1 > $DIR/config_read_fw_idx 2> /dev/null 181} 182 183read_firmwares() 184{ 185 for i in $(seq 0 3); do 186 config_set_read_fw_idx $i 187 # Verify the contents are what we expect. 188 # -Z required for now -- check for yourself, md5sum 189 # on $FW and DIR/read_firmware will yield the same. Even 190 # cmp agrees, so something is off. 191 if ! diff -q -Z "$FW" $DIR/read_firmware 2>/dev/null ; then 192 echo "request #$i: firmware was not loaded" >&2 193 exit 1 194 fi 195 done 196} 197 198read_firmwares_expect_nofile() 199{ 200 for i in $(seq 0 3); do 201 config_set_read_fw_idx $i 202 # Ensures contents differ 203 if diff -q -Z "$FW" $DIR/read_firmware 2>/dev/null ; then 204 echo "request $i: file was not expected to match" >&2 205 exit 1 206 fi 207 done 208} 209 210test_batched_request_firmware_nofile() 211{ 212 echo -n "Batched request_firmware() nofile try #$1: " 213 config_reset 214 config_set_name nope-test-firmware.bin 215 config_trigger_sync 216 read_firmwares_expect_nofile 217 release_all_firmware 218 echo "OK" 219} 220 221test_batched_request_firmware_direct_nofile() 222{ 223 echo -n "Batched request_firmware_direct() nofile try #$1: " 224 config_reset 225 config_set_name nope-test-firmware.bin 226 config_set_sync_direct 227 config_trigger_sync 228 release_all_firmware 229 echo "OK" 230} 231 232test_request_firmware_nowait_uevent_nofile() 233{ 234 echo -n "Batched request_firmware_nowait(uevent=true) nofile try #$1: " 235 config_reset 236 config_set_name nope-test-firmware.bin 237 config_trigger_async 238 release_all_firmware 239 echo "OK" 240} 241 242test_wait_and_cancel_custom_load() 243{ 244 if [ "$HAS_FW_LOADER_USER_HELPER" != "yes" ]; then 245 return 246 fi 247 local timeout=10 248 name=$1 249 while [ ! -e "$DIR"/"$name"/loading ]; do 250 sleep 0.1 251 timeout=$(( $timeout - 1 )) 252 if [ "$timeout" -eq 0 ]; then 253 echo "firmware interface never appeared:" >&2 254 echo "$DIR/$name/loading" >&2 255 exit 1 256 fi 257 done 258 echo -1 >"$DIR"/"$name"/loading 259} 260 261test_request_firmware_nowait_custom_nofile() 262{ 263 echo -n "Batched request_firmware_nowait(uevent=false) nofile try #$1: " 264 config_unset_uevent 265 config_set_name nope-test-firmware.bin 266 config_trigger_async & 267 test_wait_and_cancel_custom_load nope-test-firmware.bin 268 wait 269 release_all_firmware 270 echo "OK" 271} 272 273test_batched_request_firmware() 274{ 275 echo -n "Batched request_firmware() try #$1: " 276 config_reset 277 config_trigger_sync 278 read_firmwares 279 release_all_firmware 280 echo "OK" 281} 282 283test_batched_request_firmware_direct() 284{ 285 echo -n "Batched request_firmware_direct() try #$1: " 286 config_reset 287 config_set_sync_direct 288 config_trigger_sync 289 release_all_firmware 290 echo "OK" 291} 292 293test_request_firmware_nowait_uevent() 294{ 295 echo -n "Batched request_firmware_nowait(uevent=true) try #$1: " 296 config_reset 297 config_trigger_async 298 release_all_firmware 299 echo "OK" 300} 301 302test_request_firmware_nowait_custom() 303{ 304 echo -n "Batched request_firmware_nowait(uevent=false) try #$1: " 305 config_unset_uevent 306 config_trigger_async 307 release_all_firmware 308 echo "OK" 309} 310 311# Only continue if batched request triggers are present on the 312# test-firmware driver 313test_config_present 314 315# test with the file present 316echo 317echo "Testing with the file present..." 318for i in $(seq 1 5); do 319 test_batched_request_firmware $i 320done 321 322for i in $(seq 1 5); do 323 test_batched_request_firmware_direct $i 324done 325 326for i in $(seq 1 5); do 327 test_request_firmware_nowait_uevent $i 328done 329 330for i in $(seq 1 5); do 331 test_request_firmware_nowait_custom $i 332done 333 334# Test for file not found, errors are expected, the failure would be 335# a hung task, which would require a hard reset. 336echo 337echo "Testing with the file missing..." 338for i in $(seq 1 5); do 339 test_batched_request_firmware_nofile $i 340done 341 342for i in $(seq 1 5); do 343 test_batched_request_firmware_direct_nofile $i 344done 345 346for i in $(seq 1 5); do 347 test_request_firmware_nowait_uevent_nofile $i 348done 349 350for i in $(seq 1 5); do 351 test_request_firmware_nowait_custom_nofile $i 352done 353 354exit 0 355