fw_fallback.sh 6.6 KB

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