1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3# This validates that the kernel will load firmware out of its list of
4# firmware locations on disk. Since the user helper does similar work,
5# we reset the custom load directory to a location the user helper doesn't
6# know so we can be sure we're not accidentally testing the user helper.
7set -e
8
9TEST_REQS_FW_SYSFS_FALLBACK="no"
10TEST_REQS_FW_SET_CUSTOM_PATH="yes"
11TEST_DIR=$(dirname $0)
12source $TEST_DIR/fw_lib.sh
13
14check_mods
15check_setup
16verify_reqs
17setup_tmp_file
18
19trap "test_finish" EXIT
20
21if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then
22	# Turn down the timeout so failures don't take so long.
23	echo 1 >/sys/class/firmware/timeout
24fi
25
26if printf '\000' >"$DIR"/trigger_request 2> /dev/null; then
27	echo "$0: empty filename should not succeed" >&2
28	exit 1
29fi
30
31if [ ! -e "$DIR"/trigger_async_request ]; then
32	echo "$0: empty filename: async trigger not present, ignoring test" >&2
33	exit $ksft_skip
34else
35	if printf '\000' >"$DIR"/trigger_async_request 2> /dev/null; then
36		echo "$0: empty filename should not succeed (async)" >&2
37		exit 1
38	fi
39fi
40
41# Request a firmware that doesn't exist, it should fail.
42if echo -n "nope-$NAME" >"$DIR"/trigger_request 2> /dev/null; then
43	echo "$0: firmware shouldn't have loaded" >&2
44	exit 1
45fi
46if diff -q "$FW" /dev/test_firmware >/dev/null ; then
47	echo "$0: firmware was not expected to match" >&2
48	exit 1
49else
50	if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then
51		echo "$0: timeout works"
52	fi
53fi
54
55# This should succeed via kernel load or will fail after 1 second after
56# being handed over to the user helper, which won't find the fw either.
57if ! echo -n "$NAME" >"$DIR"/trigger_request ; then
58	echo "$0: could not trigger request" >&2
59	exit 1
60fi
61
62# Verify the contents are what we expect.
63if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then
64	echo "$0: firmware was not loaded" >&2
65	exit 1
66else
67	echo "$0: filesystem loading works"
68fi
69
70# Try the asynchronous version too
71if [ ! -e "$DIR"/trigger_async_request ]; then
72	echo "$0: firmware loading: async trigger not present, ignoring test" >&2
73	exit $ksft_skip
74else
75	if ! echo -n "$NAME" >"$DIR"/trigger_async_request ; then
76		echo "$0: could not trigger async request" >&2
77		exit 1
78	fi
79
80	# Verify the contents are what we expect.
81	if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then
82		echo "$0: firmware was not loaded (async)" >&2
83		exit 1
84	else
85		echo "$0: async filesystem loading works"
86	fi
87fi
88
89### Batched requests tests
90test_config_present()
91{
92	if [ ! -f $DIR/reset ]; then
93		echo "Configuration triggers not present, ignoring test"
94		exit $ksft_skip
95	fi
96}
97
98# Defaults :
99#
100# send_uevent: 1
101# sync_direct: 0
102# name: test-firmware.bin
103# num_requests: 4
104config_reset()
105{
106	echo 1 >  $DIR/reset
107}
108
109release_all_firmware()
110{
111	echo 1 >  $DIR/release_all_firmware
112}
113
114config_set_name()
115{
116	echo -n $1 >  $DIR/config_name
117}
118
119config_set_sync_direct()
120{
121	echo 1 >  $DIR/config_sync_direct
122}
123
124config_unset_sync_direct()
125{
126	echo 0 >  $DIR/config_sync_direct
127}
128
129config_set_uevent()
130{
131	echo 1 >  $DIR/config_send_uevent
132}
133
134config_unset_uevent()
135{
136	echo 0 >  $DIR/config_send_uevent
137}
138
139config_trigger_sync()
140{
141	echo -n 1 > $DIR/trigger_batched_requests 2>/dev/null
142}
143
144config_trigger_async()
145{
146	echo -n 1 > $DIR/trigger_batched_requests_async 2> /dev/null
147}
148
149config_set_read_fw_idx()
150{
151	echo -n $1 > $DIR/config_read_fw_idx 2> /dev/null
152}
153
154read_firmwares()
155{
156	for i in $(seq 0 3); do
157		config_set_read_fw_idx $i
158		# Verify the contents match
159		if ! diff -q "$FW" $DIR/read_firmware 2>/dev/null ; then
160			echo "request #$i: firmware was not loaded" >&2
161			exit 1
162		fi
163	done
164}
165
166read_firmwares_expect_nofile()
167{
168	for i in $(seq 0 3); do
169		config_set_read_fw_idx $i
170		# Ensures contents differ
171		if diff -q "$FW" $DIR/read_firmware 2>/dev/null ; then
172			echo "request $i: file was not expected to match" >&2
173			exit 1
174		fi
175	done
176}
177
178test_batched_request_firmware_nofile()
179{
180	echo -n "Batched request_firmware() nofile try #$1: "
181	config_reset
182	config_set_name nope-test-firmware.bin
183	config_trigger_sync
184	read_firmwares_expect_nofile
185	release_all_firmware
186	echo "OK"
187}
188
189test_batched_request_firmware_direct_nofile()
190{
191	echo -n "Batched request_firmware_direct() nofile try #$1: "
192	config_reset
193	config_set_name nope-test-firmware.bin
194	config_set_sync_direct
195	config_trigger_sync
196	release_all_firmware
197	echo "OK"
198}
199
200test_request_firmware_nowait_uevent_nofile()
201{
202	echo -n "Batched request_firmware_nowait(uevent=true) nofile try #$1: "
203	config_reset
204	config_set_name nope-test-firmware.bin
205	config_trigger_async
206	release_all_firmware
207	echo "OK"
208}
209
210test_wait_and_cancel_custom_load()
211{
212	if [ "$HAS_FW_LOADER_USER_HELPER" != "yes" ]; then
213		return
214	fi
215	local timeout=10
216	name=$1
217	while [ ! -e "$DIR"/"$name"/loading ]; do
218		sleep 0.1
219		timeout=$(( $timeout - 1 ))
220		if [ "$timeout" -eq 0 ]; then
221			echo "firmware interface never appeared:" >&2
222			echo "$DIR/$name/loading" >&2
223			exit 1
224		fi
225	done
226	echo -1 >"$DIR"/"$name"/loading
227}
228
229test_request_firmware_nowait_custom_nofile()
230{
231	echo -n "Batched request_firmware_nowait(uevent=false) nofile try #$1: "
232	config_reset
233	config_unset_uevent
234	RANDOM_FILE_PATH=$(setup_random_file_fake)
235	RANDOM_FILE="$(basename $RANDOM_FILE_PATH)"
236	config_set_name $RANDOM_FILE
237	config_trigger_async &
238	test_wait_and_cancel_custom_load $RANDOM_FILE
239	wait
240	release_all_firmware
241	echo "OK"
242}
243
244test_batched_request_firmware()
245{
246	echo -n "Batched request_firmware() try #$1: "
247	config_reset
248	config_trigger_sync
249	read_firmwares
250	release_all_firmware
251	echo "OK"
252}
253
254test_batched_request_firmware_direct()
255{
256	echo -n "Batched request_firmware_direct() try #$1: "
257	config_reset
258	config_set_sync_direct
259	config_trigger_sync
260	release_all_firmware
261	echo "OK"
262}
263
264test_request_firmware_nowait_uevent()
265{
266	echo -n "Batched request_firmware_nowait(uevent=true) try #$1: "
267	config_reset
268	config_trigger_async
269	release_all_firmware
270	echo "OK"
271}
272
273test_request_firmware_nowait_custom()
274{
275	echo -n "Batched request_firmware_nowait(uevent=false) try #$1: "
276	config_reset
277	config_unset_uevent
278	RANDOM_FILE_PATH=$(setup_random_file)
279	RANDOM_FILE="$(basename $RANDOM_FILE_PATH)"
280	config_set_name $RANDOM_FILE
281	config_trigger_async
282	release_all_firmware
283	echo "OK"
284}
285
286# Only continue if batched request triggers are present on the
287# test-firmware driver
288test_config_present
289
290# test with the file present
291echo
292echo "Testing with the file present..."
293for i in $(seq 1 5); do
294	test_batched_request_firmware $i
295done
296
297for i in $(seq 1 5); do
298	test_batched_request_firmware_direct $i
299done
300
301for i in $(seq 1 5); do
302	test_request_firmware_nowait_uevent $i
303done
304
305for i in $(seq 1 5); do
306	test_request_firmware_nowait_custom $i
307done
308
309# Test for file not found, errors are expected, the failure would be
310# a hung task, which would require a hard reset.
311echo
312echo "Testing with the file missing..."
313for i in $(seq 1 5); do
314	test_batched_request_firmware_nofile $i
315done
316
317for i in $(seq 1 5); do
318	test_batched_request_firmware_direct_nofile $i
319done
320
321for i in $(seq 1 5); do
322	test_request_firmware_nowait_uevent_nofile $i
323done
324
325for i in $(seq 1 5); do
326	test_request_firmware_nowait_custom_nofile $i
327done
328
329exit 0
330