]> git.ipfire.org Git - thirdparty/linux.git/blob - tools/testing/selftests/firmware/fw_fallback.sh
License cleanup: add SPDX GPL-2.0 license identifier to files with no license
[thirdparty/linux.git] / tools / testing / selftests / firmware / fw_fallback.sh
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.
7 set -e
8
9 modprobe test_firmware
10
11 DIR=/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.
16 HAS_FW_LOADER_USER_HELPER=$(if [ -d /sys/class/firmware/ ]; then echo yes; else echo no; fi)
17
18 if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then
19 OLD_TIMEOUT=$(cat /sys/class/firmware/timeout)
20 else
21 echo "usermode helper disabled so ignoring test"
22 exit 0
23 fi
24
25 FWPATH=$(mktemp -d)
26 FW="$FWPATH/test-firmware.bin"
27
28 test_finish()
29 {
30 echo "$OLD_TIMEOUT" >/sys/class/firmware/timeout
31 rm -f "$FW"
32 rmdir "$FWPATH"
33 }
34
35 load_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
62 load_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
87 load_fw_custom()
88 {
89 local name="$1"
90 local file="$2"
91
92 echo -n "$name" >"$DIR"/trigger_custom_fallback 2>/dev/null &
93
94 # Give kernel a chance to react.
95 local timeout=10
96 while [ ! -e "$DIR"/"$name"/loading ]; do
97 sleep 0.1
98 timeout=$(( $timeout - 1 ))
99 if [ "$timeout" -eq 0 ]; then
100 echo "$0: firmware interface never appeared" >&2
101 exit 1
102 fi
103 done
104
105 echo 1 >"$DIR"/"$name"/loading
106 cat "$file" >"$DIR"/"$name"/data
107 echo 0 >"$DIR"/"$name"/loading
108
109 # Wait for request to finish.
110 wait
111 }
112
113
114 load_fw_custom_cancel()
115 {
116 local name="$1"
117 local file="$2"
118
119 echo -n "$name" >"$DIR"/trigger_custom_fallback 2>/dev/null &
120
121 # Give kernel a chance to react.
122 local timeout=10
123 while [ ! -e "$DIR"/"$name"/loading ]; do
124 sleep 0.1
125 timeout=$(( $timeout - 1 ))
126 if [ "$timeout" -eq 0 ]; then
127 echo "$0: firmware interface never appeared" >&2
128 exit 1
129 fi
130 done
131
132 echo -1 >"$DIR"/"$name"/loading
133
134 # Wait for request to finish.
135 wait
136 }
137
138 load_fw_fallback_with_child()
139 {
140 local name="$1"
141 local file="$2"
142
143 # This is the value already set but we want to be explicit
144 echo 4 >/sys/class/firmware/timeout
145
146 sleep 1 &
147 SECONDS_BEFORE=$(date +%s)
148 echo -n "$name" >"$DIR"/trigger_request 2>/dev/null
149 SECONDS_AFTER=$(date +%s)
150 SECONDS_DELTA=$(($SECONDS_AFTER - $SECONDS_BEFORE))
151 if [ "$SECONDS_DELTA" -lt 4 ]; then
152 RET=1
153 else
154 RET=0
155 fi
156 wait
157 return $RET
158 }
159
160 trap "test_finish" EXIT
161
162 # This is an unlikely real-world firmware content. :)
163 echo "ABCD0123" >"$FW"
164 NAME=$(basename "$FW")
165
166 DEVPATH="$DIR"/"nope-$NAME"/loading
167
168 # Test failure when doing nothing (timeout works).
169 echo -n 2 >/sys/class/firmware/timeout
170 echo -n "nope-$NAME" >"$DIR"/trigger_request 2>/dev/null &
171
172 # Give the kernel some time to load the loading file, must be less
173 # than the timeout above.
174 sleep 1
175 if [ ! -f $DEVPATH ]; then
176 echo "$0: fallback mechanism immediately cancelled"
177 echo ""
178 echo "The file never appeared: $DEVPATH"
179 echo ""
180 echo "This might be a distribution udev rule setup by your distribution"
181 echo "to immediately cancel all fallback requests, this must be"
182 echo "removed before running these tests. To confirm look for"
183 echo "a firmware rule like /lib/udev/rules.d/50-firmware.rules"
184 echo "and see if you have something like this:"
185 echo ""
186 echo "SUBSYSTEM==\"firmware\", ACTION==\"add\", ATTR{loading}=\"-1\""
187 echo ""
188 echo "If you do remove this file or comment out this line before"
189 echo "proceeding with these tests."
190 exit 1
191 fi
192
193 if diff -q "$FW" /dev/test_firmware >/dev/null ; then
194 echo "$0: firmware was not expected to match" >&2
195 exit 1
196 else
197 echo "$0: timeout works"
198 fi
199
200 # Put timeout high enough for us to do work but not so long that failures
201 # slow down this test too much.
202 echo 4 >/sys/class/firmware/timeout
203
204 # Load this script instead of the desired firmware.
205 load_fw "$NAME" "$0"
206 if diff -q "$FW" /dev/test_firmware >/dev/null ; then
207 echo "$0: firmware was not expected to match" >&2
208 exit 1
209 else
210 echo "$0: firmware comparison works"
211 fi
212
213 # Do a proper load, which should work correctly.
214 load_fw "$NAME" "$FW"
215 if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then
216 echo "$0: firmware was not loaded" >&2
217 exit 1
218 else
219 echo "$0: fallback mechanism works"
220 fi
221
222 load_fw_cancel "nope-$NAME" "$FW"
223 if diff -q "$FW" /dev/test_firmware >/dev/null ; then
224 echo "$0: firmware was expected to be cancelled" >&2
225 exit 1
226 else
227 echo "$0: cancelling fallback mechanism works"
228 fi
229
230 load_fw_custom "$NAME" "$FW"
231 if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then
232 echo "$0: firmware was not loaded" >&2
233 exit 1
234 else
235 echo "$0: custom fallback loading mechanism works"
236 fi
237
238 load_fw_custom_cancel "nope-$NAME" "$FW"
239 if diff -q "$FW" /dev/test_firmware >/dev/null ; then
240 echo "$0: firmware was expected to be cancelled" >&2
241 exit 1
242 else
243 echo "$0: cancelling custom fallback mechanism works"
244 fi
245
246 set +e
247 load_fw_fallback_with_child "nope-signal-$NAME" "$FW"
248 if [ "$?" -eq 0 ]; then
249 echo "$0: SIGCHLD on sync ignored as expected" >&2
250 else
251 echo "$0: error - sync firmware request cancelled due to SIGCHLD" >&2
252 exit 1
253 fi
254 set -e
255
256 exit 0