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