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