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