1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4# Library of helpers for test scripts.
5set -e
6
7DIR=/sys/devices/virtual/misc/test_firmware
8
9PROC_CONFIG="/proc/config.gz"
10TEST_DIR=$(dirname $0)
11
12# We need to load a different file to test request_firmware_into_buf
13# I believe the issue is firmware loaded cached vs. non-cached
14# with same filename is bungled.
15# To reproduce rename this to test-firmware.bin
16TEST_FIRMWARE_INTO_BUF_FILENAME=test-firmware-into-buf.bin
17
18# Kselftest framework requirement - SKIP code is 4.
19ksft_skip=4
20
21print_reqs_exit()
22{
23	echo "You must have the following enabled in your kernel:" >&2
24	cat $TEST_DIR/config >&2
25	exit $ksft_skip
26}
27
28test_modprobe()
29{
30	if [ ! -d $DIR ]; then
31		print_reqs_exit
32	fi
33}
34
35check_mods()
36{
37	local uid=$(id -u)
38	if [ $uid -ne 0 ]; then
39		echo "skip all tests: must be run as root" >&2
40		exit $ksft_skip
41	fi
42
43	trap "test_modprobe" EXIT
44	if [ ! -d $DIR ]; then
45		modprobe test_firmware
46	fi
47	if [ ! -f $PROC_CONFIG ]; then
48		if modprobe configs 2>/dev/null; then
49			echo "Loaded configs module"
50			if [ ! -f $PROC_CONFIG ]; then
51				echo "You must have the following enabled in your kernel:" >&2
52				cat $TEST_DIR/config >&2
53				echo "Resorting to old heuristics" >&2
54			fi
55		else
56			echo "Failed to load configs module, using old heuristics" >&2
57		fi
58	fi
59}
60
61check_setup()
62{
63	HAS_FW_LOADER_USER_HELPER="$(kconfig_has CONFIG_FW_LOADER_USER_HELPER=y)"
64	HAS_FW_LOADER_USER_HELPER_FALLBACK="$(kconfig_has CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y)"
65	HAS_FW_LOADER_COMPRESS_XZ="$(kconfig_has CONFIG_FW_LOADER_COMPRESS_XZ=y)"
66	HAS_FW_LOADER_COMPRESS_ZSTD="$(kconfig_has CONFIG_FW_LOADER_COMPRESS_ZSTD=y)"
67	PROC_FW_IGNORE_SYSFS_FALLBACK="0"
68	PROC_FW_FORCE_SYSFS_FALLBACK="0"
69
70	if [ -z $PROC_SYS_DIR ]; then
71		PROC_SYS_DIR="/proc/sys/kernel"
72	fi
73
74	FW_PROC="${PROC_SYS_DIR}/firmware_config"
75	FW_FORCE_SYSFS_FALLBACK="$FW_PROC/force_sysfs_fallback"
76	FW_IGNORE_SYSFS_FALLBACK="$FW_PROC/ignore_sysfs_fallback"
77
78	if [ -f $FW_FORCE_SYSFS_FALLBACK ]; then
79		PROC_FW_FORCE_SYSFS_FALLBACK="$(cat $FW_FORCE_SYSFS_FALLBACK)"
80	fi
81
82	if [ -f $FW_IGNORE_SYSFS_FALLBACK ]; then
83		PROC_FW_IGNORE_SYSFS_FALLBACK="$(cat $FW_IGNORE_SYSFS_FALLBACK)"
84	fi
85
86	if [ "$PROC_FW_FORCE_SYSFS_FALLBACK" = "1" ]; then
87		HAS_FW_LOADER_USER_HELPER="yes"
88		HAS_FW_LOADER_USER_HELPER_FALLBACK="yes"
89	fi
90
91	if [ "$PROC_FW_IGNORE_SYSFS_FALLBACK" = "1" ]; then
92		HAS_FW_LOADER_USER_HELPER_FALLBACK="no"
93		HAS_FW_LOADER_USER_HELPER="no"
94	fi
95
96	if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then
97	       OLD_TIMEOUT="$(cat /sys/class/firmware/timeout)"
98	fi
99
100	OLD_FWPATH="$(cat /sys/module/firmware_class/parameters/path)"
101
102	if [ "$HAS_FW_LOADER_COMPRESS_XZ" = "yes" ]; then
103		if ! which xz 2> /dev/null > /dev/null; then
104			HAS_FW_LOADER_COMPRESS_XZ=""
105		fi
106	fi
107	if [ "$HAS_FW_LOADER_COMPRESS_ZSTD" = "yes" ]; then
108		if ! which zstd 2> /dev/null > /dev/null; then
109			HAS_FW_LOADER_COMPRESS_ZSTD=""
110		fi
111	fi
112}
113
114verify_reqs()
115{
116	if [ "$TEST_REQS_FW_SYSFS_FALLBACK" = "yes" ]; then
117		if [ ! "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then
118			echo "usermode helper disabled so ignoring test"
119			exit 0
120		fi
121	fi
122}
123
124setup_tmp_file()
125{
126	FWPATH=$(mktemp -d)
127	FW="$FWPATH/test-firmware.bin"
128	echo "ABCD0123" >"$FW"
129	FW_INTO_BUF="$FWPATH/$TEST_FIRMWARE_INTO_BUF_FILENAME"
130	echo "EFGH4567" >"$FW_INTO_BUF"
131	NAME=$(basename "$FW")
132	if [ "$TEST_REQS_FW_SET_CUSTOM_PATH" = "yes" ]; then
133		echo -n "$FWPATH" >/sys/module/firmware_class/parameters/path
134	fi
135}
136
137__setup_random_file()
138{
139	RANDOM_FILE_PATH="$(mktemp -p $FWPATH)"
140	# mktemp says dry-run -n is unsafe, so...
141	if [[ "$1" = "fake" ]]; then
142		rm -rf $RANDOM_FILE_PATH
143		sync
144	else
145		echo "ABCD0123" >"$RANDOM_FILE_PATH"
146	fi
147	echo $RANDOM_FILE_PATH
148}
149
150setup_random_file()
151{
152	echo $(__setup_random_file)
153}
154
155setup_random_file_fake()
156{
157	echo $(__setup_random_file fake)
158}
159
160proc_set_force_sysfs_fallback()
161{
162	if [ -f $FW_FORCE_SYSFS_FALLBACK ]; then
163		echo -n $1 > $FW_FORCE_SYSFS_FALLBACK
164		check_setup
165	fi
166}
167
168proc_set_ignore_sysfs_fallback()
169{
170	if [ -f $FW_IGNORE_SYSFS_FALLBACK ]; then
171		echo -n $1 > $FW_IGNORE_SYSFS_FALLBACK
172		check_setup
173	fi
174}
175
176proc_restore_defaults()
177{
178	proc_set_force_sysfs_fallback 0
179	proc_set_ignore_sysfs_fallback 0
180}
181
182test_finish()
183{
184	if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then
185		echo "$OLD_TIMEOUT" >/sys/class/firmware/timeout
186	fi
187	if [ "$TEST_REQS_FW_SET_CUSTOM_PATH" = "yes" ]; then
188		if [ "$OLD_FWPATH" = "" ]; then
189			# A zero-length write won't work; write a null byte
190			printf '\000' >/sys/module/firmware_class/parameters/path
191		else
192			echo -n "$OLD_FWPATH" >/sys/module/firmware_class/parameters/path
193		fi
194	fi
195	if [ -f $FW ]; then
196		rm -f "$FW"
197	fi
198	if [ -f $FW_INTO_BUF ]; then
199		rm -f "$FW_INTO_BUF"
200	fi
201	if [ -d $FWPATH ]; then
202		rm -rf "$FWPATH"
203	fi
204	proc_restore_defaults
205}
206
207kconfig_has()
208{
209	if [ -f $PROC_CONFIG ]; then
210		if zgrep -q $1 $PROC_CONFIG 2>/dev/null; then
211			echo "yes"
212		else
213			echo "no"
214		fi
215	else
216		# We currently don't have easy heuristics to infer this
217		# so best we can do is just try to use the kernel assuming
218		# you had enabled it. This matches the old behaviour.
219		if [ "$1" = "CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y" ]; then
220			echo "yes"
221		elif [ "$1" = "CONFIG_FW_LOADER_USER_HELPER=y" ]; then
222			if [ -d /sys/class/firmware/ ]; then
223				echo yes
224			else
225				echo no
226			fi
227		fi
228	fi
229}
230