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