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