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