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