test_firmware: Test partial read support
[linux-2.6-microblaze.git] / tools / testing / selftests / firmware / fw_filesystem.sh
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 # Try platform (EFI embedded fw) loading too
90 if [ ! -e "$DIR"/trigger_request_platform ]; then
91         echo "$0: firmware loading: platform trigger not present, ignoring test" >&2
92 else
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"
110 fi
111
112 ### Batched requests tests
113 test_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
127 config_reset()
128 {
129         echo 1 >  $DIR/reset
130 }
131
132 release_all_firmware()
133 {
134         echo 1 >  $DIR/release_all_firmware
135 }
136
137 config_set_name()
138 {
139         echo -n $1 >  $DIR/config_name
140 }
141
142 config_set_into_buf()
143 {
144         echo 1 >  $DIR/config_into_buf
145 }
146
147 config_unset_into_buf()
148 {
149         echo 0 >  $DIR/config_into_buf
150 }
151
152 config_set_buf_size()
153 {
154         echo $1 >  $DIR/config_buf_size
155 }
156
157 config_set_file_offset()
158 {
159         echo $1 >  $DIR/config_file_offset
160 }
161
162 config_set_partial()
163 {
164         echo 1 >  $DIR/config_partial
165 }
166
167 config_unset_partial()
168 {
169         echo 0 >  $DIR/config_partial
170 }
171
172 config_set_sync_direct()
173 {
174         echo 1 >  $DIR/config_sync_direct
175 }
176
177 config_unset_sync_direct()
178 {
179         echo 0 >  $DIR/config_sync_direct
180 }
181
182 config_set_uevent()
183 {
184         echo 1 >  $DIR/config_send_uevent
185 }
186
187 config_unset_uevent()
188 {
189         echo 0 >  $DIR/config_send_uevent
190 }
191
192 config_trigger_sync()
193 {
194         echo -n 1 > $DIR/trigger_batched_requests 2>/dev/null
195 }
196
197 config_trigger_async()
198 {
199         echo -n 1 > $DIR/trigger_batched_requests_async 2> /dev/null
200 }
201
202 config_set_read_fw_idx()
203 {
204         echo -n $1 > $DIR/config_read_fw_idx 2> /dev/null
205 }
206
207 read_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
230 read_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
259 read_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
271 test_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
282 test_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
294 test_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
309 test_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
320 test_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
330 test_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
349 test_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
364 test_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
374 test_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
386 test_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
396 test_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
405 test_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
423 test_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
440 test_config_present
441
442 # test with the file present
443 echo
444 echo "Testing with the file present..."
445 for i in $(seq 1 5); do
446         test_batched_request_firmware $i normal
447 done
448
449 for i in $(seq 1 5); do
450         test_batched_request_firmware_into_buf $i normal
451 done
452
453 for i in $(seq 1 5); do
454         test_batched_request_firmware_direct $i normal
455 done
456
457 for i in $(seq 1 5); do
458         test_request_firmware_nowait_uevent $i normal
459 done
460
461 for i in $(seq 1 5); do
462         test_request_firmware_nowait_custom $i normal
463 done
464
465 # Partial loads cannot use fallback, so do not repeat tests.
466 test_request_partial_firmware_into_buf 0 10
467 test_request_partial_firmware_into_buf 0 5
468 test_request_partial_firmware_into_buf 1 6
469 test_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.
473 echo
474 echo "Testing with the file missing..."
475 for i in $(seq 1 5); do
476         test_batched_request_firmware_nofile $i
477 done
478
479 for i in $(seq 1 5); do
480         test_batched_request_firmware_into_buf_nofile $i
481 done
482
483 for i in $(seq 1 5); do
484         test_batched_request_firmware_direct_nofile $i
485 done
486
487 for i in $(seq 1 5); do
488         test_request_firmware_nowait_uevent_nofile $i
489 done
490
491 for i in $(seq 1 5); do
492         test_request_firmware_nowait_custom_nofile $i
493 done
494
495 # Partial loads cannot use fallback, so do not repeat tests.
496 test_request_partial_firmware_into_buf_nofile 0 10
497 test_request_partial_firmware_into_buf_nofile 0 5
498 test_request_partial_firmware_into_buf_nofile 1 6
499 test_request_partial_firmware_into_buf_nofile 2 10
500
501 test "$HAS_FW_LOADER_COMPRESS" != "yes" && exit 0
502
503 # test with both files present
504 xz -9 -C crc32 -k $FW
505 config_set_name $NAME
506 echo
507 echo "Testing with both plain and xz files present..."
508 for i in $(seq 1 5); do
509         test_batched_request_firmware $i both
510 done
511
512 for i in $(seq 1 5); do
513         test_batched_request_firmware_into_buf $i both
514 done
515
516 for i in $(seq 1 5); do
517         test_batched_request_firmware_direct $i both
518 done
519
520 for i in $(seq 1 5); do
521         test_request_firmware_nowait_uevent $i both
522 done
523
524 for i in $(seq 1 5); do
525         test_request_firmware_nowait_custom $i both
526 done
527
528 # test with only xz file present
529 mv "$FW" "${FW}-orig"
530 echo
531 echo "Testing with only xz file present..."
532 for i in $(seq 1 5); do
533         test_batched_request_firmware $i xzonly
534 done
535
536 for i in $(seq 1 5); do
537         test_batched_request_firmware_into_buf $i xzonly
538 done
539
540 for i in $(seq 1 5); do
541         test_batched_request_firmware_direct $i xzonly
542 done
543
544 for i in $(seq 1 5); do
545         test_request_firmware_nowait_uevent $i xzonly
546 done
547
548 for i in $(seq 1 5); do
549         test_request_firmware_nowait_custom $i xzonly
550 done
551
552 exit 0