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