blob: 0d4527c5e8a4f288331fac0cf2e3465a8704180c [file] [log] [blame]
Kees Cook0a8adf52014-07-14 14:38:12 -07001#!/bin/sh
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01002# SPDX-License-Identifier: GPL-2.0
Luis R. Rodriguez823b0222017-01-23 08:11:08 -08003# This validates that the kernel will fall back to using the fallback mechanism
Kees Cook0a8adf52014-07-14 14:38:12 -07004# 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
Luis R. Rodriguez1d0fbb32015-07-24 15:10:22 -070013# 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
Kees Cook0a8adf52014-07-14 14:38:12 -070024
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
Luis R. Rodriguezeb67bc32017-01-23 08:11:09 -080062load_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
Luis R. Rodriguez061132d2017-01-23 08:11:10 -080087load_fw_custom()
88{
Amit Pundirc3e0d172017-11-09 01:10:35 +053089 if [ ! -e "$DIR"/trigger_custom_fallback ]; then
90 echo "$0: custom fallback trigger not present, ignoring test" >&2
91 return 1
92 fi
93
Luis R. Rodriguez061132d2017-01-23 08:11:10 -080094 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
Amit Pundirc3e0d172017-11-09 01:10:35 +0530116 return 0
Luis R. Rodriguez061132d2017-01-23 08:11:10 -0800117}
118
119
120load_fw_custom_cancel()
121{
Amit Pundirc3e0d172017-11-09 01:10:35 +0530122 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
Luis R. Rodriguez061132d2017-01-23 08:11:10 -0800127 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
Amit Pundirc3e0d172017-11-09 01:10:35 +0530147 return 0
Luis R. Rodriguez061132d2017-01-23 08:11:10 -0800148}
149
Luis R. Rodriguez0d1f4172017-07-20 13:13:38 -0700150load_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}
Luis R. Rodriguez061132d2017-01-23 08:11:10 -0800171
Kees Cook0a8adf52014-07-14 14:38:12 -0700172trap "test_finish" EXIT
173
174# This is an unlikely real-world firmware content. :)
175echo "ABCD0123" >"$FW"
176NAME=$(basename "$FW")
177
Luis R. Rodriguez881c23d2017-11-20 10:23:59 -0800178test_syfs_timeout()
179{
180 DEVPATH="$DIR"/"nope-$NAME"/loading
Luis R. Rodriguezafb999c2017-01-23 08:11:07 -0800181
Luis R. Rodriguez881c23d2017-11-20 10:23:59 -0800182 # 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 &
Luis R. Rodriguezafb999c2017-01-23 08:11:07 -0800185
Luis R. Rodriguez881c23d2017-11-20 10:23:59 -0800186 # 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
Luis R. Rodriguezafb999c2017-01-23 08:11:07 -0800206
Luis R. Rodriguez881c23d2017-11-20 10:23:59 -0800207 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
Luis R. Rodriguez59106c82017-11-20 10:24:00 -0800215run_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
Kees Cook0a8adf52014-07-14 14:38:12 -0700221
Luis R. Rodriguez59106c82017-11-20 10:24:00 -0800222 # 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
Kees Cook0a8adf52014-07-14 14:38:12 -0700230
Luis R. Rodriguez59106c82017-11-20 10:24:00 -0800231 # 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
Kees Cook0a8adf52014-07-14 14:38:12 -0700239
Luis R. Rodriguez59106c82017-11-20 10:24:00 -0800240 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
Luis R. Rodriguez823b0222017-01-23 08:11:08 -0800247
Luis R. Rodriguez59106c82017-11-20 10:24:00 -0800248 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_main_tests
Kees Cook0a8adf52014-07-14 14:38:12 -0700260
Amit Pundirc3e0d172017-11-09 01:10:35 +0530261if 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
Luis R. Rodriguez061132d2017-01-23 08:11:10 -0800268fi
269
Amit Pundirc3e0d172017-11-09 01:10:35 +0530270if load_fw_custom_cancel "nope-$NAME" "$FW" ; then
271 if diff -q "$FW" /dev/test_firmware >/dev/null ; then
272 echo "$0: firmware was expected to be cancelled" >&2
273 exit 1
274 else
275 echo "$0: cancelling custom fallback mechanism works"
276 fi
Luis R. Rodriguez061132d2017-01-23 08:11:10 -0800277fi
278
Kees Cook0a8adf52014-07-14 14:38:12 -0700279exit 0