Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1#!/bin/sh
2# SPDX-License-Identifier: GPL-2.0
3# This validates that the kernel will fall back to using the fallback mechanism
4# to load firmware it can't find on disk itself. We must request a firmware
5# that the kernel won't find, and any installed helper (e.g. udev) also
6# won't find so that we can do the load ourself manually.
7set -e
8
9modprobe test_firmware
10
11DIR=/sys/devices/virtual/misc/test_firmware
12
13# CONFIG_FW_LOADER_USER_HELPER has a sysfs class under /sys/class/firmware/
14# These days no one enables CONFIG_FW_LOADER_USER_HELPER so check for that
15# as an indicator for CONFIG_FW_LOADER_USER_HELPER.
16HAS_FW_LOADER_USER_HELPER=$(if [ -d /sys/class/firmware/ ]; then echo yes; else echo no; fi)
17
18if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then
19 OLD_TIMEOUT=$(cat /sys/class/firmware/timeout)
20else
21 echo "usermode helper disabled so ignoring test"
22 exit 0
23fi
24
25FWPATH=$(mktemp -d)
26FW="$FWPATH/test-firmware.bin"
27
28test_finish()
29{
30 echo "$OLD_TIMEOUT" >/sys/class/firmware/timeout
31 rm -f "$FW"
32 rmdir "$FWPATH"
33}
34
35load_fw()
36{
37 local name="$1"
38 local file="$2"
39
40 # This will block until our load (below) has finished.
41 echo -n "$name" >"$DIR"/trigger_request &
42
43 # Give kernel a chance to react.
44 local timeout=10
45 while [ ! -e "$DIR"/"$name"/loading ]; do
46 sleep 0.1
47 timeout=$(( $timeout - 1 ))
48 if [ "$timeout" -eq 0 ]; then
49 echo "$0: firmware interface never appeared" >&2
50 exit 1
51 fi
52 done
53
54 echo 1 >"$DIR"/"$name"/loading
55 cat "$file" >"$DIR"/"$name"/data
56 echo 0 >"$DIR"/"$name"/loading
57
58 # Wait for request to finish.
59 wait
60}
61
62load_fw_cancel()
63{
64 local name="$1"
65 local file="$2"
66
67 # This will block until our load (below) has finished.
68 echo -n "$name" >"$DIR"/trigger_request 2>/dev/null &
69
70 # Give kernel a chance to react.
71 local timeout=10
72 while [ ! -e "$DIR"/"$name"/loading ]; do
73 sleep 0.1
74 timeout=$(( $timeout - 1 ))
75 if [ "$timeout" -eq 0 ]; then
76 echo "$0: firmware interface never appeared" >&2
77 exit 1
78 fi
79 done
80
81 echo -1 >"$DIR"/"$name"/loading
82
83 # Wait for request to finish.
84 wait
85}
86
87load_fw_custom()
88{
89 if [ ! -e "$DIR"/trigger_custom_fallback ]; then
90 echo "$0: custom fallback trigger not present, ignoring test" >&2
91 return 1
92 fi
93
94 local name="$1"
95 local file="$2"
96
97 echo -n "$name" >"$DIR"/trigger_custom_fallback 2>/dev/null &
98
99 # Give kernel a chance to react.
100 local timeout=10
101 while [ ! -e "$DIR"/"$name"/loading ]; do
102 sleep 0.1
103 timeout=$(( $timeout - 1 ))
104 if [ "$timeout" -eq 0 ]; then
105 echo "$0: firmware interface never appeared" >&2
106 exit 1
107 fi
108 done
109
110 echo 1 >"$DIR"/"$name"/loading
111 cat "$file" >"$DIR"/"$name"/data
112 echo 0 >"$DIR"/"$name"/loading
113
114 # Wait for request to finish.
115 wait
116 return 0
117}
118
119
120load_fw_custom_cancel()
121{
122 if [ ! -e "$DIR"/trigger_custom_fallback ]; then
123 echo "$0: canceling custom fallback trigger not present, ignoring test" >&2
124 return 1
125 fi
126
127 local name="$1"
128 local file="$2"
129
130 echo -n "$name" >"$DIR"/trigger_custom_fallback 2>/dev/null &
131
132 # Give kernel a chance to react.
133 local timeout=10
134 while [ ! -e "$DIR"/"$name"/loading ]; do
135 sleep 0.1
136 timeout=$(( $timeout - 1 ))
137 if [ "$timeout" -eq 0 ]; then
138 echo "$0: firmware interface never appeared" >&2
139 exit 1
140 fi
141 done
142
143 echo -1 >"$DIR"/"$name"/loading
144
145 # Wait for request to finish.
146 wait
147 return 0
148}
149
150load_fw_fallback_with_child()
151{
152 local name="$1"
153 local file="$2"
154
155 # This is the value already set but we want to be explicit
156 echo 4 >/sys/class/firmware/timeout
157
158 sleep 1 &
159 SECONDS_BEFORE=$(date +%s)
160 echo -n "$name" >"$DIR"/trigger_request 2>/dev/null
161 SECONDS_AFTER=$(date +%s)
162 SECONDS_DELTA=$(($SECONDS_AFTER - $SECONDS_BEFORE))
163 if [ "$SECONDS_DELTA" -lt 4 ]; then
164 RET=1
165 else
166 RET=0
167 fi
168 wait
169 return $RET
170}
171
172trap "test_finish" EXIT
173
174# This is an unlikely real-world firmware content. :)
175echo "ABCD0123" >"$FW"
176NAME=$(basename "$FW")
177
178test_syfs_timeout()
179{
180 DEVPATH="$DIR"/"nope-$NAME"/loading
181
182 # Test failure when doing nothing (timeout works).
183 echo -n 2 >/sys/class/firmware/timeout
184 echo -n "nope-$NAME" >"$DIR"/trigger_request 2>/dev/null &
185
186 # Give the kernel some time to load the loading file, must be less
187 # than the timeout above.
188 sleep 1
189 if [ ! -f $DEVPATH ]; then
190 echo "$0: fallback mechanism immediately cancelled"
191 echo ""
192 echo "The file never appeared: $DEVPATH"
193 echo ""
194 echo "This might be a distribution udev rule setup by your distribution"
195 echo "to immediately cancel all fallback requests, this must be"
196 echo "removed before running these tests. To confirm look for"
197 echo "a firmware rule like /lib/udev/rules.d/50-firmware.rules"
198 echo "and see if you have something like this:"
199 echo ""
200 echo "SUBSYSTEM==\"firmware\", ACTION==\"add\", ATTR{loading}=\"-1\""
201 echo ""
202 echo "If you do remove this file or comment out this line before"
203 echo "proceeding with these tests."
204 exit 1
205 fi
206
207 if diff -q "$FW" /dev/test_firmware >/dev/null ; then
208 echo "$0: firmware was not expected to match" >&2
209 exit 1
210 else
211 echo "$0: timeout works"
212 fi
213}
214
215run_sysfs_main_tests()
216{
217 test_syfs_timeout
218 # Put timeout high enough for us to do work but not so long that failures
219 # slow down this test too much.
220 echo 4 >/sys/class/firmware/timeout
221
222 # Load this script instead of the desired firmware.
223 load_fw "$NAME" "$0"
224 if diff -q "$FW" /dev/test_firmware >/dev/null ; then
225 echo "$0: firmware was not expected to match" >&2
226 exit 1
227 else
228 echo "$0: firmware comparison works"
229 fi
230
231 # Do a proper load, which should work correctly.
232 load_fw "$NAME" "$FW"
233 if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then
234 echo "$0: firmware was not loaded" >&2
235 exit 1
236 else
237 echo "$0: fallback mechanism works"
238 fi
239
240 load_fw_cancel "nope-$NAME" "$FW"
241 if diff -q "$FW" /dev/test_firmware >/dev/null ; then
242 echo "$0: firmware was expected to be cancelled" >&2
243 exit 1
244 else
245 echo "$0: cancelling fallback mechanism works"
246 fi
247
248 set +e
249 load_fw_fallback_with_child "nope-signal-$NAME" "$FW"
250 if [ "$?" -eq 0 ]; then
251 echo "$0: SIGCHLD on sync ignored as expected" >&2
252 else
253 echo "$0: error - sync firmware request cancelled due to SIGCHLD" >&2
254 exit 1
255 fi
256 set -e
257}
258
259run_sysfs_custom_load_tests()
260{
261 if load_fw_custom "$NAME" "$FW" ; then
262 if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then
263 echo "$0: firmware was not loaded" >&2
264 exit 1
265 else
266 echo "$0: custom fallback loading mechanism works"
267 fi
268 fi
269
270 if load_fw_custom "$NAME" "$FW" ; then
271 if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then
272 echo "$0: firmware was not loaded" >&2
273 exit 1
274 else
275 echo "$0: custom fallback loading mechanism works"
276 fi
277 fi
278
279 if load_fw_custom_cancel "nope-$NAME" "$FW" ; then
280 if diff -q "$FW" /dev/test_firmware >/dev/null ; then
281 echo "$0: firmware was expected to be cancelled" >&2
282 exit 1
283 else
284 echo "$0: cancelling custom fallback mechanism works"
285 fi
286 fi
287}
288
289run_sysfs_main_tests
290run_sysfs_custom_load_tests
291
292exit 0