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_REQS_FW_SYSFS_FALLBACK="no" 10TEST_REQS_FW_SET_CUSTOM_PATH="yes" 11TEST_DIR=$(dirname $0) 12source $TEST_DIR/fw_lib.sh 13 14RUN_XZ="xz -C crc32 --lzma2=dict=2MiB" 15 16check_mods 17check_setup 18verify_reqs 19setup_tmp_file 20 21trap "test_finish" EXIT 22 23if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then 24 # Turn down the timeout so failures don't take so long. 25 echo 1 >/sys/class/firmware/timeout 26fi 27 28if printf '\000' >"$DIR"/trigger_request 2> /dev/null; then 29 echo "$0: empty filename should not succeed" >&2 30 exit 1 31fi 32 33if [ ! -e "$DIR"/trigger_async_request ]; then 34 echo "$0: empty filename: async trigger not present, ignoring test" >&2 35 exit $ksft_skip 36else 37 if printf '\000' >"$DIR"/trigger_async_request 2> /dev/null; then 38 echo "$0: empty filename should not succeed (async)" >&2 39 exit 1 40 fi 41fi 42 43# Request a firmware that doesn't exist, it should fail. 44if echo -n "nope-$NAME" >"$DIR"/trigger_request 2> /dev/null; then 45 echo "$0: firmware shouldn't have loaded" >&2 46 exit 1 47fi 48if diff -q "$FW" /dev/test_firmware >/dev/null ; then 49 echo "$0: firmware was not expected to match" >&2 50 exit 1 51else 52 if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then 53 echo "$0: timeout works" 54 fi 55fi 56 57# This should succeed via kernel load or will fail after 1 second after 58# being handed over to the user helper, which won't find the fw either. 59if ! echo -n "$NAME" >"$DIR"/trigger_request ; then 60 echo "$0: could not trigger request" >&2 61 exit 1 62fi 63 64# Verify the contents are what we expect. 65if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then 66 echo "$0: firmware was not loaded" >&2 67 exit 1 68else 69 echo "$0: filesystem loading works" 70fi 71 72# Try the asynchronous version too 73if [ ! -e "$DIR"/trigger_async_request ]; then 74 echo "$0: firmware loading: async trigger not present, ignoring test" >&2 75 exit $ksft_skip 76else 77 if ! echo -n "$NAME" >"$DIR"/trigger_async_request ; then 78 echo "$0: could not trigger async request" >&2 79 exit 1 80 fi 81 82 # Verify the contents are what we expect. 83 if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then 84 echo "$0: firmware was not loaded (async)" >&2 85 exit 1 86 else 87 echo "$0: async filesystem loading works" 88 fi 89fi 90 91# Try platform (EFI embedded fw) loading too 92if [ ! -e "$DIR"/trigger_request_platform ]; then 93 echo "$0: firmware loading: platform trigger not present, ignoring test" >&2 94else 95 if printf '\000' >"$DIR"/trigger_request_platform 2> /dev/null; then 96 echo "$0: empty filename should not succeed (platform)" >&2 97 exit 1 98 fi 99 100 # Note we echo a non-existing name, since files on the file-system 101 # are preferred over firmware embedded inside the platform's firmware 102 # The test adds a fake entry with the requested name to the platform's 103 # fw list, so the name does not matter as long as it does not exist 104 if ! echo -n "nope-$NAME" >"$DIR"/trigger_request_platform ; then 105 echo "$0: could not trigger request platform" >&2 106 exit 1 107 fi 108 109 # The test verifies itself that the loaded firmware contents matches 110 # the contents for the fake platform fw entry it added. 111 echo "$0: platform loading works" 112fi 113 114### Batched requests tests 115test_config_present() 116{ 117 if [ ! -f $DIR/reset ]; then 118 echo "Configuration triggers not present, ignoring test" 119 exit $ksft_skip 120 fi 121} 122 123# Defaults : 124# 125# send_uevent: 1 126# sync_direct: 0 127# name: test-firmware.bin 128# num_requests: 4 129config_reset() 130{ 131 echo 1 > $DIR/reset 132} 133 134release_all_firmware() 135{ 136 echo 1 > $DIR/release_all_firmware 137} 138 139config_set_name() 140{ 141 echo -n $1 > $DIR/config_name 142} 143 144config_set_into_buf() 145{ 146 echo 1 > $DIR/config_into_buf 147} 148 149config_unset_into_buf() 150{ 151 echo 0 > $DIR/config_into_buf 152} 153 154config_set_buf_size() 155{ 156 echo $1 > $DIR/config_buf_size 157} 158 159config_set_file_offset() 160{ 161 echo $1 > $DIR/config_file_offset 162} 163 164config_set_partial() 165{ 166 echo 1 > $DIR/config_partial 167} 168 169config_unset_partial() 170{ 171 echo 0 > $DIR/config_partial 172} 173 174config_set_sync_direct() 175{ 176 echo 1 > $DIR/config_sync_direct 177} 178 179config_unset_sync_direct() 180{ 181 echo 0 > $DIR/config_sync_direct 182} 183 184config_set_uevent() 185{ 186 echo 1 > $DIR/config_send_uevent 187} 188 189config_unset_uevent() 190{ 191 echo 0 > $DIR/config_send_uevent 192} 193 194config_trigger_sync() 195{ 196 echo -n 1 > $DIR/trigger_batched_requests 2>/dev/null 197} 198 199config_trigger_async() 200{ 201 echo -n 1 > $DIR/trigger_batched_requests_async 2> /dev/null 202} 203 204config_set_read_fw_idx() 205{ 206 echo -n $1 > $DIR/config_read_fw_idx 2> /dev/null 207} 208 209read_firmwares() 210{ 211 if [ "$(cat $DIR/config_into_buf)" == "1" ]; then 212 fwfile="$FW_INTO_BUF" 213 else 214 fwfile="$FW" 215 fi 216 if [ "$1" = "xzonly" ]; then 217 fwfile="${fwfile}-orig" 218 fi 219 for i in $(seq 0 3); do 220 config_set_read_fw_idx $i 221 # Verify the contents are what we expect. 222 # -Z required for now -- check for yourself, md5sum 223 # on $FW and DIR/read_firmware will yield the same. Even 224 # cmp agrees, so something is off. 225 if ! diff -q -Z "$fwfile" $DIR/read_firmware 2>/dev/null ; then 226 echo "request #$i: firmware was not loaded" >&2 227 exit 1 228 fi 229 done 230} 231 232read_partial_firmwares() 233{ 234 if [ "$(cat $DIR/config_into_buf)" == "1" ]; then 235 fwfile="${FW_INTO_BUF}" 236 else 237 fwfile="${FW}" 238 fi 239 240 if [ "$1" = "xzonly" ]; then 241 fwfile="${fwfile}-orig" 242 fi 243 244 # Strip fwfile down to match partial offset and length 245 partial_data="$(cat $fwfile)" 246 partial_data="${partial_data:$2:$3}" 247 248 for i in $(seq 0 3); do 249 config_set_read_fw_idx $i 250 251 read_firmware="$(cat $DIR/read_firmware)" 252 253 # Verify the contents are what we expect. 254 if [ $read_firmware != $partial_data ]; then 255 echo "request #$i: partial firmware was not loaded" >&2 256 exit 1 257 fi 258 done 259} 260 261read_firmwares_expect_nofile() 262{ 263 for i in $(seq 0 3); do 264 config_set_read_fw_idx $i 265 # Ensures contents differ 266 if diff -q -Z "$FW" $DIR/read_firmware 2>/dev/null ; then 267 echo "request $i: file was not expected to match" >&2 268 exit 1 269 fi 270 done 271} 272 273test_batched_request_firmware_nofile() 274{ 275 echo -n "Batched request_firmware() nofile try #$1: " 276 config_reset 277 config_set_name nope-test-firmware.bin 278 config_trigger_sync 279 read_firmwares_expect_nofile 280 release_all_firmware 281 echo "OK" 282} 283 284test_batched_request_firmware_into_buf_nofile() 285{ 286 echo -n "Batched request_firmware_into_buf() nofile try #$1: " 287 config_reset 288 config_set_name nope-test-firmware.bin 289 config_set_into_buf 290 config_trigger_sync 291 read_firmwares_expect_nofile 292 release_all_firmware 293 echo "OK" 294} 295 296test_request_partial_firmware_into_buf_nofile() 297{ 298 echo -n "Test request_partial_firmware_into_buf() off=$1 size=$2 nofile: " 299 config_reset 300 config_set_name nope-test-firmware.bin 301 config_set_into_buf 302 config_set_partial 303 config_set_buf_size $2 304 config_set_file_offset $1 305 config_trigger_sync 306 read_firmwares_expect_nofile 307 release_all_firmware 308 echo "OK" 309} 310 311test_batched_request_firmware_direct_nofile() 312{ 313 echo -n "Batched request_firmware_direct() nofile try #$1: " 314 config_reset 315 config_set_name nope-test-firmware.bin 316 config_set_sync_direct 317 config_trigger_sync 318 release_all_firmware 319 echo "OK" 320} 321 322test_request_firmware_nowait_uevent_nofile() 323{ 324 echo -n "Batched request_firmware_nowait(uevent=true) nofile try #$1: " 325 config_reset 326 config_set_name nope-test-firmware.bin 327 config_trigger_async 328 release_all_firmware 329 echo "OK" 330} 331 332test_wait_and_cancel_custom_load() 333{ 334 if [ "$HAS_FW_LOADER_USER_HELPER" != "yes" ]; then 335 return 336 fi 337 local timeout=10 338 name=$1 339 while [ ! -e "$DIR"/"$name"/loading ]; do 340 sleep 0.1 341 timeout=$(( $timeout - 1 )) 342 if [ "$timeout" -eq 0 ]; then 343 echo "firmware interface never appeared:" >&2 344 echo "$DIR/$name/loading" >&2 345 exit 1 346 fi 347 done 348 echo -1 >"$DIR"/"$name"/loading 349} 350 351test_request_firmware_nowait_custom_nofile() 352{ 353 echo -n "Batched request_firmware_nowait(uevent=false) nofile try #$1: " 354 config_reset 355 config_unset_uevent 356 RANDOM_FILE_PATH=$(setup_random_file_fake) 357 RANDOM_FILE="$(basename $RANDOM_FILE_PATH)" 358 config_set_name $RANDOM_FILE 359 config_trigger_async & 360 test_wait_and_cancel_custom_load $RANDOM_FILE 361 wait 362 release_all_firmware 363 echo "OK" 364} 365 366test_batched_request_firmware() 367{ 368 echo -n "Batched request_firmware() $2 try #$1: " 369 config_reset 370 config_trigger_sync 371 read_firmwares $2 372 release_all_firmware 373 echo "OK" 374} 375 376test_batched_request_firmware_into_buf() 377{ 378 echo -n "Batched request_firmware_into_buf() $2 try #$1: " 379 config_reset 380 config_set_name $TEST_FIRMWARE_INTO_BUF_FILENAME 381 config_set_into_buf 382 config_trigger_sync 383 read_firmwares $2 384 release_all_firmware 385 echo "OK" 386} 387 388test_batched_request_firmware_direct() 389{ 390 echo -n "Batched request_firmware_direct() $2 try #$1: " 391 config_reset 392 config_set_sync_direct 393 config_trigger_sync 394 release_all_firmware 395 echo "OK" 396} 397 398test_request_firmware_nowait_uevent() 399{ 400 echo -n "Batched request_firmware_nowait(uevent=true) $2 try #$1: " 401 config_reset 402 config_trigger_async 403 release_all_firmware 404 echo "OK" 405} 406 407test_request_firmware_nowait_custom() 408{ 409 echo -n "Batched request_firmware_nowait(uevent=false) $2 try #$1: " 410 config_reset 411 config_unset_uevent 412 RANDOM_FILE_PATH=$(setup_random_file) 413 RANDOM_FILE="$(basename $RANDOM_FILE_PATH)" 414 if [ "$2" = "both" ]; then 415 $RUN_XZ -k $RANDOM_FILE_PATH 416 elif [ "$2" = "xzonly" ]; then 417 $RUN_XZ $RANDOM_FILE_PATH 418 fi 419 config_set_name $RANDOM_FILE 420 config_trigger_async 421 release_all_firmware 422 echo "OK" 423} 424 425test_request_partial_firmware_into_buf() 426{ 427 echo -n "Test request_partial_firmware_into_buf() off=$1 size=$2: " 428 config_reset 429 config_set_name $TEST_FIRMWARE_INTO_BUF_FILENAME 430 config_set_into_buf 431 config_set_partial 432 config_set_buf_size $2 433 config_set_file_offset $1 434 config_trigger_sync 435 read_partial_firmwares normal $1 $2 436 release_all_firmware 437 echo "OK" 438} 439 440# Only continue if batched request triggers are present on the 441# test-firmware driver 442test_config_present 443 444# test with the file present 445echo 446echo "Testing with the file present..." 447for i in $(seq 1 5); do 448 test_batched_request_firmware $i normal 449done 450 451for i in $(seq 1 5); do 452 test_batched_request_firmware_into_buf $i normal 453done 454 455for i in $(seq 1 5); do 456 test_batched_request_firmware_direct $i normal 457done 458 459for i in $(seq 1 5); do 460 test_request_firmware_nowait_uevent $i normal 461done 462 463for i in $(seq 1 5); do 464 test_request_firmware_nowait_custom $i normal 465done 466 467# Partial loads cannot use fallback, so do not repeat tests. 468test_request_partial_firmware_into_buf 0 10 469test_request_partial_firmware_into_buf 0 5 470test_request_partial_firmware_into_buf 1 6 471test_request_partial_firmware_into_buf 2 10 472 473# Test for file not found, errors are expected, the failure would be 474# a hung task, which would require a hard reset. 475echo 476echo "Testing with the file missing..." 477for i in $(seq 1 5); do 478 test_batched_request_firmware_nofile $i 479done 480 481for i in $(seq 1 5); do 482 test_batched_request_firmware_into_buf_nofile $i 483done 484 485for i in $(seq 1 5); do 486 test_batched_request_firmware_direct_nofile $i 487done 488 489for i in $(seq 1 5); do 490 test_request_firmware_nowait_uevent_nofile $i 491done 492 493for i in $(seq 1 5); do 494 test_request_firmware_nowait_custom_nofile $i 495done 496 497# Partial loads cannot use fallback, so do not repeat tests. 498test_request_partial_firmware_into_buf_nofile 0 10 499test_request_partial_firmware_into_buf_nofile 0 5 500test_request_partial_firmware_into_buf_nofile 1 6 501test_request_partial_firmware_into_buf_nofile 2 10 502 503test "$HAS_FW_LOADER_COMPRESS" != "yes" && exit 0 504 505# test with both files present 506$RUN_XZ -k $FW 507config_set_name $NAME 508echo 509echo "Testing with both plain and xz files present..." 510for i in $(seq 1 5); do 511 test_batched_request_firmware $i both 512done 513 514for i in $(seq 1 5); do 515 test_batched_request_firmware_into_buf $i both 516done 517 518for i in $(seq 1 5); do 519 test_batched_request_firmware_direct $i both 520done 521 522for i in $(seq 1 5); do 523 test_request_firmware_nowait_uevent $i both 524done 525 526for i in $(seq 1 5); do 527 test_request_firmware_nowait_custom $i both 528done 529 530# test with only xz file present 531mv "$FW" "${FW}-orig" 532echo 533echo "Testing with only xz file present..." 534for i in $(seq 1 5); do 535 test_batched_request_firmware $i xzonly 536done 537 538for i in $(seq 1 5); do 539 test_batched_request_firmware_into_buf $i xzonly 540done 541 542for i in $(seq 1 5); do 543 test_batched_request_firmware_direct $i xzonly 544done 545 546for i in $(seq 1 5); do 547 test_request_firmware_nowait_uevent $i xzonly 548done 549 550for i in $(seq 1 5); do 551 test_request_firmware_nowait_custom $i xzonly 552done 553 554exit 0 555