162306a36Sopenharmony_ci#!/bin/bash
262306a36Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0
362306a36Sopenharmony_ci# This validates that the kernel will fall back to using the fallback mechanism
462306a36Sopenharmony_ci# to load firmware it can't find on disk itself. We must request a firmware
562306a36Sopenharmony_ci# that the kernel won't find, and any installed helper (e.g. udev) also
662306a36Sopenharmony_ci# won't find so that we can do the load ourself manually.
762306a36Sopenharmony_ciset -e
862306a36Sopenharmony_ci
962306a36Sopenharmony_ciTEST_REQS_FW_SYSFS_FALLBACK="yes"
1062306a36Sopenharmony_ciTEST_REQS_FW_SET_CUSTOM_PATH="no"
1162306a36Sopenharmony_ciTEST_DIR=$(dirname $0)
1262306a36Sopenharmony_cisource $TEST_DIR/fw_lib.sh
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cicheck_mods
1562306a36Sopenharmony_cicheck_setup
1662306a36Sopenharmony_civerify_reqs
1762306a36Sopenharmony_cisetup_tmp_file
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_citrap "test_finish" EXIT
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ciload_fw()
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	local name="$1"
2462306a36Sopenharmony_ci	local file="$2"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	# This will block until our load (below) has finished.
2762306a36Sopenharmony_ci	echo -n "$name" >"$DIR"/trigger_request &
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	# Give kernel a chance to react.
3062306a36Sopenharmony_ci	local timeout=10
3162306a36Sopenharmony_ci	while [ ! -e "$DIR"/"$name"/loading ]; do
3262306a36Sopenharmony_ci		sleep 0.1
3362306a36Sopenharmony_ci		timeout=$(( $timeout - 1 ))
3462306a36Sopenharmony_ci		if [ "$timeout" -eq 0 ]; then
3562306a36Sopenharmony_ci			echo "$0: firmware interface never appeared" >&2
3662306a36Sopenharmony_ci			exit 1
3762306a36Sopenharmony_ci		fi
3862306a36Sopenharmony_ci	done
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	echo 1 >"$DIR"/"$name"/loading
4162306a36Sopenharmony_ci	cat "$file" >"$DIR"/"$name"/data
4262306a36Sopenharmony_ci	echo 0 >"$DIR"/"$name"/loading
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	# Wait for request to finish.
4562306a36Sopenharmony_ci	wait
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ciload_fw_cancel()
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	local name="$1"
5162306a36Sopenharmony_ci	local file="$2"
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	# This will block until our load (below) has finished.
5462306a36Sopenharmony_ci	echo -n "$name" >"$DIR"/trigger_request 2>/dev/null &
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	# Give kernel a chance to react.
5762306a36Sopenharmony_ci	local timeout=10
5862306a36Sopenharmony_ci	while [ ! -e "$DIR"/"$name"/loading ]; do
5962306a36Sopenharmony_ci		sleep 0.1
6062306a36Sopenharmony_ci		timeout=$(( $timeout - 1 ))
6162306a36Sopenharmony_ci		if [ "$timeout" -eq 0 ]; then
6262306a36Sopenharmony_ci			echo "$0: firmware interface never appeared" >&2
6362306a36Sopenharmony_ci			exit 1
6462306a36Sopenharmony_ci		fi
6562306a36Sopenharmony_ci	done
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	echo -1 >"$DIR"/"$name"/loading
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	# Wait for request to finish.
7062306a36Sopenharmony_ci	wait
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ciload_fw_custom()
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	if [ ! -e "$DIR"/trigger_custom_fallback ]; then
7662306a36Sopenharmony_ci		echo "$0: custom fallback trigger not present, ignoring test" >&2
7762306a36Sopenharmony_ci		exit $ksft_skip
7862306a36Sopenharmony_ci	fi
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	local name="$1"
8162306a36Sopenharmony_ci	local file="$2"
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	echo -n "$name" >"$DIR"/trigger_custom_fallback 2>/dev/null &
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	# Give kernel a chance to react.
8662306a36Sopenharmony_ci	local timeout=10
8762306a36Sopenharmony_ci	while [ ! -e "$DIR"/"$name"/loading ]; do
8862306a36Sopenharmony_ci		sleep 0.1
8962306a36Sopenharmony_ci		timeout=$(( $timeout - 1 ))
9062306a36Sopenharmony_ci		if [ "$timeout" -eq 0 ]; then
9162306a36Sopenharmony_ci			echo "$0: firmware interface never appeared" >&2
9262306a36Sopenharmony_ci			exit 1
9362306a36Sopenharmony_ci		fi
9462306a36Sopenharmony_ci	done
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	echo 1 >"$DIR"/"$name"/loading
9762306a36Sopenharmony_ci	cat "$file" >"$DIR"/"$name"/data
9862306a36Sopenharmony_ci	echo 0 >"$DIR"/"$name"/loading
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	# Wait for request to finish.
10162306a36Sopenharmony_ci	wait
10262306a36Sopenharmony_ci	return 0
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ciload_fw_custom_cancel()
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	if [ ! -e "$DIR"/trigger_custom_fallback ]; then
10962306a36Sopenharmony_ci		echo "$0: canceling custom fallback trigger not present, ignoring test" >&2
11062306a36Sopenharmony_ci		exit $ksft_skip
11162306a36Sopenharmony_ci	fi
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	local name="$1"
11462306a36Sopenharmony_ci	local file="$2"
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	echo -n "$name" >"$DIR"/trigger_custom_fallback 2>/dev/null &
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	# Give kernel a chance to react.
11962306a36Sopenharmony_ci	local timeout=10
12062306a36Sopenharmony_ci	while [ ! -e "$DIR"/"$name"/loading ]; do
12162306a36Sopenharmony_ci		sleep 0.1
12262306a36Sopenharmony_ci		timeout=$(( $timeout - 1 ))
12362306a36Sopenharmony_ci		if [ "$timeout" -eq 0 ]; then
12462306a36Sopenharmony_ci			echo "$0: firmware interface never appeared" >&2
12562306a36Sopenharmony_ci			exit 1
12662306a36Sopenharmony_ci		fi
12762306a36Sopenharmony_ci	done
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	echo -1 >"$DIR"/"$name"/loading
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	# Wait for request to finish.
13262306a36Sopenharmony_ci	wait
13362306a36Sopenharmony_ci	return 0
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ciload_fw_fallback_with_child()
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	local name="$1"
13962306a36Sopenharmony_ci	local file="$2"
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	# This is the value already set but we want to be explicit
14262306a36Sopenharmony_ci	echo 4 >/sys/class/firmware/timeout
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	sleep 1 &
14562306a36Sopenharmony_ci	SECONDS_BEFORE=$(date +%s)
14662306a36Sopenharmony_ci	echo -n "$name" >"$DIR"/trigger_request 2>/dev/null
14762306a36Sopenharmony_ci	SECONDS_AFTER=$(date +%s)
14862306a36Sopenharmony_ci	SECONDS_DELTA=$(($SECONDS_AFTER - $SECONDS_BEFORE))
14962306a36Sopenharmony_ci	if [ "$SECONDS_DELTA" -lt 4 ]; then
15062306a36Sopenharmony_ci		RET=1
15162306a36Sopenharmony_ci	else
15262306a36Sopenharmony_ci		RET=0
15362306a36Sopenharmony_ci	fi
15462306a36Sopenharmony_ci	wait
15562306a36Sopenharmony_ci	return $RET
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_citest_syfs_timeout()
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	DEVPATH="$DIR"/"nope-$NAME"/loading
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	# Test failure when doing nothing (timeout works).
16362306a36Sopenharmony_ci	echo -n 2 >/sys/class/firmware/timeout
16462306a36Sopenharmony_ci	echo -n "nope-$NAME" >"$DIR"/trigger_request 2>/dev/null &
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	# Give the kernel some time to load the loading file, must be less
16762306a36Sopenharmony_ci	# than the timeout above.
16862306a36Sopenharmony_ci	sleep 1
16962306a36Sopenharmony_ci	if [ ! -f $DEVPATH ]; then
17062306a36Sopenharmony_ci		echo "$0: fallback mechanism immediately cancelled"
17162306a36Sopenharmony_ci		echo ""
17262306a36Sopenharmony_ci		echo "The file never appeared: $DEVPATH"
17362306a36Sopenharmony_ci		echo ""
17462306a36Sopenharmony_ci		echo "This might be a distribution udev rule setup by your distribution"
17562306a36Sopenharmony_ci		echo "to immediately cancel all fallback requests, this must be"
17662306a36Sopenharmony_ci		echo "removed before running these tests. To confirm look for"
17762306a36Sopenharmony_ci		echo "a firmware rule like /lib/udev/rules.d/50-firmware.rules"
17862306a36Sopenharmony_ci		echo "and see if you have something like this:"
17962306a36Sopenharmony_ci		echo ""
18062306a36Sopenharmony_ci		echo "SUBSYSTEM==\"firmware\", ACTION==\"add\", ATTR{loading}=\"-1\""
18162306a36Sopenharmony_ci		echo ""
18262306a36Sopenharmony_ci		echo "If you do remove this file or comment out this line before"
18362306a36Sopenharmony_ci		echo "proceeding with these tests."
18462306a36Sopenharmony_ci		exit 1
18562306a36Sopenharmony_ci	fi
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if diff -q "$FW" /dev/test_firmware >/dev/null ; then
18862306a36Sopenharmony_ci		echo "$0: firmware was not expected to match" >&2
18962306a36Sopenharmony_ci		exit 1
19062306a36Sopenharmony_ci	else
19162306a36Sopenharmony_ci		echo "$0: timeout works"
19262306a36Sopenharmony_ci	fi
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cirun_sysfs_main_tests()
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	test_syfs_timeout
19862306a36Sopenharmony_ci	# Put timeout high enough for us to do work but not so long that failures
19962306a36Sopenharmony_ci	# slow down this test too much.
20062306a36Sopenharmony_ci	echo 4 >/sys/class/firmware/timeout
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	# Load this script instead of the desired firmware.
20362306a36Sopenharmony_ci	load_fw "$NAME" "$0"
20462306a36Sopenharmony_ci	if diff -q "$FW" /dev/test_firmware >/dev/null ; then
20562306a36Sopenharmony_ci		echo "$0: firmware was not expected to match" >&2
20662306a36Sopenharmony_ci		exit 1
20762306a36Sopenharmony_ci	else
20862306a36Sopenharmony_ci		echo "$0: firmware comparison works"
20962306a36Sopenharmony_ci	fi
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	# Do a proper load, which should work correctly.
21262306a36Sopenharmony_ci	load_fw "$NAME" "$FW"
21362306a36Sopenharmony_ci	if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then
21462306a36Sopenharmony_ci		echo "$0: firmware was not loaded" >&2
21562306a36Sopenharmony_ci		exit 1
21662306a36Sopenharmony_ci	else
21762306a36Sopenharmony_ci		echo "$0: fallback mechanism works"
21862306a36Sopenharmony_ci	fi
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	load_fw_cancel "nope-$NAME" "$FW"
22162306a36Sopenharmony_ci	if diff -q "$FW" /dev/test_firmware >/dev/null ; then
22262306a36Sopenharmony_ci		echo "$0: firmware was expected to be cancelled" >&2
22362306a36Sopenharmony_ci		exit 1
22462306a36Sopenharmony_ci	else
22562306a36Sopenharmony_ci		echo "$0: cancelling fallback mechanism works"
22662306a36Sopenharmony_ci	fi
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	set +e
22962306a36Sopenharmony_ci	load_fw_fallback_with_child "nope-signal-$NAME" "$FW"
23062306a36Sopenharmony_ci	if [ "$?" -eq 0 ]; then
23162306a36Sopenharmony_ci		echo "$0: SIGCHLD on sync ignored as expected" >&2
23262306a36Sopenharmony_ci	else
23362306a36Sopenharmony_ci		echo "$0: error - sync firmware request cancelled due to SIGCHLD" >&2
23462306a36Sopenharmony_ci		exit 1
23562306a36Sopenharmony_ci	fi
23662306a36Sopenharmony_ci	set -e
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cirun_sysfs_custom_load_tests()
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	RANDOM_FILE_PATH=$(setup_random_file)
24262306a36Sopenharmony_ci	RANDOM_FILE="$(basename $RANDOM_FILE_PATH)"
24362306a36Sopenharmony_ci	if load_fw_custom "$RANDOM_FILE" "$RANDOM_FILE_PATH" ; then
24462306a36Sopenharmony_ci		if ! diff -q "$RANDOM_FILE_PATH" /dev/test_firmware >/dev/null ; then
24562306a36Sopenharmony_ci			echo "$0: firmware was not loaded" >&2
24662306a36Sopenharmony_ci			exit 1
24762306a36Sopenharmony_ci		else
24862306a36Sopenharmony_ci			echo "$0: custom fallback loading mechanism works"
24962306a36Sopenharmony_ci		fi
25062306a36Sopenharmony_ci	fi
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	RANDOM_FILE_PATH=$(setup_random_file)
25362306a36Sopenharmony_ci	RANDOM_FILE="$(basename $RANDOM_FILE_PATH)"
25462306a36Sopenharmony_ci	if load_fw_custom "$RANDOM_FILE" "$RANDOM_FILE_PATH" ; then
25562306a36Sopenharmony_ci		if ! diff -q "$RANDOM_FILE_PATH" /dev/test_firmware >/dev/null ; then
25662306a36Sopenharmony_ci			echo "$0: firmware was not loaded" >&2
25762306a36Sopenharmony_ci			exit 1
25862306a36Sopenharmony_ci		else
25962306a36Sopenharmony_ci			echo "$0: custom fallback loading mechanism works"
26062306a36Sopenharmony_ci		fi
26162306a36Sopenharmony_ci	fi
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	RANDOM_FILE_REAL="$RANDOM_FILE_PATH"
26462306a36Sopenharmony_ci	FAKE_RANDOM_FILE_PATH=$(setup_random_file_fake)
26562306a36Sopenharmony_ci	FAKE_RANDOM_FILE="$(basename $FAKE_RANDOM_FILE_PATH)"
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if load_fw_custom_cancel "$FAKE_RANDOM_FILE" "$RANDOM_FILE_REAL" ; then
26862306a36Sopenharmony_ci		if diff -q "$RANDOM_FILE_PATH" /dev/test_firmware >/dev/null ; then
26962306a36Sopenharmony_ci			echo "$0: firmware was expected to be cancelled" >&2
27062306a36Sopenharmony_ci			exit 1
27162306a36Sopenharmony_ci		else
27262306a36Sopenharmony_ci			echo "$0: cancelling custom fallback mechanism works"
27362306a36Sopenharmony_ci		fi
27462306a36Sopenharmony_ci	fi
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ciif [ "$HAS_FW_LOADER_USER_HELPER_FALLBACK" = "yes" ]; then
27862306a36Sopenharmony_ci	run_sysfs_main_tests
27962306a36Sopenharmony_cifi
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cirun_sysfs_custom_load_tests
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ciexit 0
284