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