b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame^] | 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. |
| 7 | set -e |
| 8 | |
| 9 | TEST_REQS_FW_SYSFS_FALLBACK="no" |
| 10 | TEST_REQS_FW_SET_CUSTOM_PATH="yes" |
| 11 | TEST_DIR=$(dirname $0) |
| 12 | source $TEST_DIR/fw_lib.sh |
| 13 | |
| 14 | check_mods |
| 15 | check_setup |
| 16 | verify_reqs |
| 17 | setup_tmp_file |
| 18 | |
| 19 | trap "test_finish" EXIT |
| 20 | |
| 21 | if [ "$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 |
| 24 | fi |
| 25 | |
| 26 | if printf '\000' >"$DIR"/trigger_request 2> /dev/null; then |
| 27 | echo "$0: empty filename should not succeed" >&2 |
| 28 | exit 1 |
| 29 | fi |
| 30 | |
| 31 | if [ ! -e "$DIR"/trigger_async_request ]; then |
| 32 | echo "$0: empty filename: async trigger not present, ignoring test" >&2 |
| 33 | exit $ksft_skip |
| 34 | else |
| 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 |
| 39 | fi |
| 40 | |
| 41 | # Request a firmware that doesn't exist, it should fail. |
| 42 | if echo -n "nope-$NAME" >"$DIR"/trigger_request 2> /dev/null; then |
| 43 | echo "$0: firmware shouldn't have loaded" >&2 |
| 44 | exit 1 |
| 45 | fi |
| 46 | if diff -q "$FW" /dev/test_firmware >/dev/null ; then |
| 47 | echo "$0: firmware was not expected to match" >&2 |
| 48 | exit 1 |
| 49 | else |
| 50 | if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then |
| 51 | echo "$0: timeout works" |
| 52 | fi |
| 53 | fi |
| 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. |
| 57 | if ! echo -n "$NAME" >"$DIR"/trigger_request ; then |
| 58 | echo "$0: could not trigger request" >&2 |
| 59 | exit 1 |
| 60 | fi |
| 61 | |
| 62 | # Verify the contents are what we expect. |
| 63 | if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then |
| 64 | echo "$0: firmware was not loaded" >&2 |
| 65 | exit 1 |
| 66 | else |
| 67 | echo "$0: filesystem loading works" |
| 68 | fi |
| 69 | |
| 70 | # Try the asynchronous version too |
| 71 | if [ ! -e "$DIR"/trigger_async_request ]; then |
| 72 | echo "$0: firmware loading: async trigger not present, ignoring test" >&2 |
| 73 | exit $ksft_skip |
| 74 | else |
| 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 |
| 87 | fi |
| 88 | |
| 89 | ### Batched requests tests |
| 90 | test_config_present() |
| 91 | { |
| 92 | if [ ! -f $DIR/reset ]; then |
| 93 | echo "Configuration triggers not present, ignoring test" |
| 94 | exit $ksft_skip |
| 95 | fi |
| 96 | } |
| 97 | |
| 98 | # Defaults : |
| 99 | # |
| 100 | # send_uevent: 1 |
| 101 | # sync_direct: 0 |
| 102 | # name: test-firmware.bin |
| 103 | # num_requests: 4 |
| 104 | config_reset() |
| 105 | { |
| 106 | echo 1 > $DIR/reset |
| 107 | } |
| 108 | |
| 109 | release_all_firmware() |
| 110 | { |
| 111 | echo 1 > $DIR/release_all_firmware |
| 112 | } |
| 113 | |
| 114 | config_set_name() |
| 115 | { |
| 116 | echo -n $1 > $DIR/config_name |
| 117 | } |
| 118 | |
| 119 | config_set_into_buf() |
| 120 | { |
| 121 | echo 1 > $DIR/config_into_buf |
| 122 | } |
| 123 | |
| 124 | config_unset_into_buf() |
| 125 | { |
| 126 | echo 0 > $DIR/config_into_buf |
| 127 | } |
| 128 | |
| 129 | config_set_sync_direct() |
| 130 | { |
| 131 | echo 1 > $DIR/config_sync_direct |
| 132 | } |
| 133 | |
| 134 | config_unset_sync_direct() |
| 135 | { |
| 136 | echo 0 > $DIR/config_sync_direct |
| 137 | } |
| 138 | |
| 139 | config_set_uevent() |
| 140 | { |
| 141 | echo 1 > $DIR/config_send_uevent |
| 142 | } |
| 143 | |
| 144 | config_unset_uevent() |
| 145 | { |
| 146 | echo 0 > $DIR/config_send_uevent |
| 147 | } |
| 148 | |
| 149 | config_trigger_sync() |
| 150 | { |
| 151 | echo -n 1 > $DIR/trigger_batched_requests 2>/dev/null |
| 152 | } |
| 153 | |
| 154 | config_trigger_async() |
| 155 | { |
| 156 | echo -n 1 > $DIR/trigger_batched_requests_async 2> /dev/null |
| 157 | } |
| 158 | |
| 159 | config_set_read_fw_idx() |
| 160 | { |
| 161 | echo -n $1 > $DIR/config_read_fw_idx 2> /dev/null |
| 162 | } |
| 163 | |
| 164 | read_firmwares() |
| 165 | { |
| 166 | if [ "$(cat $DIR/config_into_buf)" == "1" ]; then |
| 167 | fwfile="$FW_INTO_BUF" |
| 168 | else |
| 169 | fwfile="$FW" |
| 170 | fi |
| 171 | if [ "$1" = "xzonly" ]; then |
| 172 | fwfile="${fwfile}-orig" |
| 173 | fi |
| 174 | for i in $(seq 0 3); do |
| 175 | config_set_read_fw_idx $i |
| 176 | # Verify the contents are what we expect. |
| 177 | # -Z required for now -- check for yourself, md5sum |
| 178 | # on $FW and DIR/read_firmware will yield the same. Even |
| 179 | # cmp agrees, so something is off. |
| 180 | if ! diff -q -Z "$fwfile" $DIR/read_firmware 2>/dev/null ; then |
| 181 | echo "request #$i: firmware was not loaded" >&2 |
| 182 | exit 1 |
| 183 | fi |
| 184 | done |
| 185 | } |
| 186 | |
| 187 | read_firmwares_expect_nofile() |
| 188 | { |
| 189 | for i in $(seq 0 3); do |
| 190 | config_set_read_fw_idx $i |
| 191 | # Ensures contents differ |
| 192 | if diff -q -Z "$FW" $DIR/read_firmware 2>/dev/null ; then |
| 193 | echo "request $i: file was not expected to match" >&2 |
| 194 | exit 1 |
| 195 | fi |
| 196 | done |
| 197 | } |
| 198 | |
| 199 | test_batched_request_firmware_nofile() |
| 200 | { |
| 201 | echo -n "Batched request_firmware() nofile try #$1: " |
| 202 | config_reset |
| 203 | config_set_name nope-test-firmware.bin |
| 204 | config_trigger_sync |
| 205 | read_firmwares_expect_nofile |
| 206 | release_all_firmware |
| 207 | echo "OK" |
| 208 | } |
| 209 | |
| 210 | test_batched_request_firmware_into_buf_nofile() |
| 211 | { |
| 212 | echo -n "Batched request_firmware_into_buf() nofile try #$1: " |
| 213 | config_reset |
| 214 | config_set_name nope-test-firmware.bin |
| 215 | config_set_into_buf |
| 216 | config_trigger_sync |
| 217 | read_firmwares_expect_nofile |
| 218 | release_all_firmware |
| 219 | echo "OK" |
| 220 | } |
| 221 | |
| 222 | test_batched_request_firmware_direct_nofile() |
| 223 | { |
| 224 | echo -n "Batched request_firmware_direct() nofile try #$1: " |
| 225 | config_reset |
| 226 | config_set_name nope-test-firmware.bin |
| 227 | config_set_sync_direct |
| 228 | config_trigger_sync |
| 229 | release_all_firmware |
| 230 | echo "OK" |
| 231 | } |
| 232 | |
| 233 | test_request_firmware_nowait_uevent_nofile() |
| 234 | { |
| 235 | echo -n "Batched request_firmware_nowait(uevent=true) nofile try #$1: " |
| 236 | config_reset |
| 237 | config_set_name nope-test-firmware.bin |
| 238 | config_trigger_async |
| 239 | release_all_firmware |
| 240 | echo "OK" |
| 241 | } |
| 242 | |
| 243 | test_wait_and_cancel_custom_load() |
| 244 | { |
| 245 | if [ "$HAS_FW_LOADER_USER_HELPER" != "yes" ]; then |
| 246 | return |
| 247 | fi |
| 248 | local timeout=10 |
| 249 | name=$1 |
| 250 | while [ ! -e "$DIR"/"$name"/loading ]; do |
| 251 | sleep 0.1 |
| 252 | timeout=$(( $timeout - 1 )) |
| 253 | if [ "$timeout" -eq 0 ]; then |
| 254 | echo "firmware interface never appeared:" >&2 |
| 255 | echo "$DIR/$name/loading" >&2 |
| 256 | exit 1 |
| 257 | fi |
| 258 | done |
| 259 | echo -1 >"$DIR"/"$name"/loading |
| 260 | } |
| 261 | |
| 262 | test_request_firmware_nowait_custom_nofile() |
| 263 | { |
| 264 | echo -n "Batched request_firmware_nowait(uevent=false) nofile try #$1: " |
| 265 | config_reset |
| 266 | config_unset_uevent |
| 267 | RANDOM_FILE_PATH=$(setup_random_file_fake) |
| 268 | RANDOM_FILE="$(basename $RANDOM_FILE_PATH)" |
| 269 | config_set_name $RANDOM_FILE |
| 270 | config_trigger_async & |
| 271 | test_wait_and_cancel_custom_load $RANDOM_FILE |
| 272 | wait |
| 273 | release_all_firmware |
| 274 | echo "OK" |
| 275 | } |
| 276 | |
| 277 | test_batched_request_firmware() |
| 278 | { |
| 279 | echo -n "Batched request_firmware() $2 try #$1: " |
| 280 | config_reset |
| 281 | config_trigger_sync |
| 282 | read_firmwares $2 |
| 283 | release_all_firmware |
| 284 | echo "OK" |
| 285 | } |
| 286 | |
| 287 | test_batched_request_firmware_into_buf() |
| 288 | { |
| 289 | echo -n "Batched request_firmware_into_buf() $2 try #$1: " |
| 290 | config_reset |
| 291 | config_set_name $TEST_FIRMWARE_INTO_BUF_FILENAME |
| 292 | config_set_into_buf |
| 293 | config_trigger_sync |
| 294 | read_firmwares $2 |
| 295 | release_all_firmware |
| 296 | echo "OK" |
| 297 | } |
| 298 | |
| 299 | test_batched_request_firmware_direct() |
| 300 | { |
| 301 | echo -n "Batched request_firmware_direct() $2 try #$1: " |
| 302 | config_reset |
| 303 | config_set_sync_direct |
| 304 | config_trigger_sync |
| 305 | release_all_firmware |
| 306 | echo "OK" |
| 307 | } |
| 308 | |
| 309 | test_request_firmware_nowait_uevent() |
| 310 | { |
| 311 | echo -n "Batched request_firmware_nowait(uevent=true) $2 try #$1: " |
| 312 | config_reset |
| 313 | config_trigger_async |
| 314 | release_all_firmware |
| 315 | echo "OK" |
| 316 | } |
| 317 | |
| 318 | test_request_firmware_nowait_custom() |
| 319 | { |
| 320 | echo -n "Batched request_firmware_nowait(uevent=false) $2 try #$1: " |
| 321 | config_reset |
| 322 | config_unset_uevent |
| 323 | RANDOM_FILE_PATH=$(setup_random_file) |
| 324 | RANDOM_FILE="$(basename $RANDOM_FILE_PATH)" |
| 325 | if [ "$2" = "both" ]; then |
| 326 | xz -9 -C crc32 -k $RANDOM_FILE_PATH |
| 327 | elif [ "$2" = "xzonly" ]; then |
| 328 | xz -9 -C crc32 $RANDOM_FILE_PATH |
| 329 | fi |
| 330 | config_set_name $RANDOM_FILE |
| 331 | config_trigger_async |
| 332 | release_all_firmware |
| 333 | echo "OK" |
| 334 | } |
| 335 | |
| 336 | # Only continue if batched request triggers are present on the |
| 337 | # test-firmware driver |
| 338 | test_config_present |
| 339 | |
| 340 | # test with the file present |
| 341 | echo |
| 342 | echo "Testing with the file present..." |
| 343 | for i in $(seq 1 5); do |
| 344 | test_batched_request_firmware $i normal |
| 345 | done |
| 346 | |
| 347 | for i in $(seq 1 5); do |
| 348 | test_batched_request_firmware_into_buf $i normal |
| 349 | done |
| 350 | |
| 351 | for i in $(seq 1 5); do |
| 352 | test_batched_request_firmware_direct $i normal |
| 353 | done |
| 354 | |
| 355 | for i in $(seq 1 5); do |
| 356 | test_request_firmware_nowait_uevent $i normal |
| 357 | done |
| 358 | |
| 359 | for i in $(seq 1 5); do |
| 360 | test_request_firmware_nowait_custom $i normal |
| 361 | done |
| 362 | |
| 363 | # Test for file not found, errors are expected, the failure would be |
| 364 | # a hung task, which would require a hard reset. |
| 365 | echo |
| 366 | echo "Testing with the file missing..." |
| 367 | for i in $(seq 1 5); do |
| 368 | test_batched_request_firmware_nofile $i |
| 369 | done |
| 370 | |
| 371 | for i in $(seq 1 5); do |
| 372 | test_batched_request_firmware_into_buf_nofile $i |
| 373 | done |
| 374 | |
| 375 | for i in $(seq 1 5); do |
| 376 | test_batched_request_firmware_direct_nofile $i |
| 377 | done |
| 378 | |
| 379 | for i in $(seq 1 5); do |
| 380 | test_request_firmware_nowait_uevent_nofile $i |
| 381 | done |
| 382 | |
| 383 | for i in $(seq 1 5); do |
| 384 | test_request_firmware_nowait_custom_nofile $i |
| 385 | done |
| 386 | |
| 387 | test "$HAS_FW_LOADER_COMPRESS" != "yes" && exit 0 |
| 388 | |
| 389 | # test with both files present |
| 390 | xz -9 -C crc32 -k $FW |
| 391 | config_set_name $NAME |
| 392 | echo |
| 393 | echo "Testing with both plain and xz files present..." |
| 394 | for i in $(seq 1 5); do |
| 395 | test_batched_request_firmware $i both |
| 396 | done |
| 397 | |
| 398 | for i in $(seq 1 5); do |
| 399 | test_batched_request_firmware_into_buf $i both |
| 400 | done |
| 401 | |
| 402 | for i in $(seq 1 5); do |
| 403 | test_batched_request_firmware_direct $i both |
| 404 | done |
| 405 | |
| 406 | for i in $(seq 1 5); do |
| 407 | test_request_firmware_nowait_uevent $i both |
| 408 | done |
| 409 | |
| 410 | for i in $(seq 1 5); do |
| 411 | test_request_firmware_nowait_custom $i both |
| 412 | done |
| 413 | |
| 414 | # test with only xz file present |
| 415 | mv "$FW" "${FW}-orig" |
| 416 | echo |
| 417 | echo "Testing with only xz file present..." |
| 418 | for i in $(seq 1 5); do |
| 419 | test_batched_request_firmware $i xzonly |
| 420 | done |
| 421 | |
| 422 | for i in $(seq 1 5); do |
| 423 | test_batched_request_firmware_into_buf $i xzonly |
| 424 | done |
| 425 | |
| 426 | for i in $(seq 1 5); do |
| 427 | test_batched_request_firmware_direct $i xzonly |
| 428 | done |
| 429 | |
| 430 | for i in $(seq 1 5); do |
| 431 | test_request_firmware_nowait_uevent $i xzonly |
| 432 | done |
| 433 | |
| 434 | for i in $(seq 1 5); do |
| 435 | test_request_firmware_nowait_custom $i xzonly |
| 436 | done |
| 437 | |
| 438 | exit 0 |