diff --git a/docs/online/recovery-shell.rst b/docs/online/recovery-shell.rst index c397bdd9d..883d2a3c8 100644 --- a/docs/online/recovery-shell.rst +++ b/docs/online/recovery-shell.rst @@ -63,3 +63,7 @@ Common Commands **reboot** Reboot the system using a SysRq magic invocation. + +**firmware-setup** + + Reboot the system into the UEFI Firmware Setup interface (if available). diff --git a/zfsbootmenu/bin/firmware-setup b/zfsbootmenu/bin/firmware-setup new file mode 120000 index 000000000..dde059f3e --- /dev/null +++ b/zfsbootmenu/bin/firmware-setup @@ -0,0 +1 @@ +reboot \ No newline at end of file diff --git a/zfsbootmenu/bin/reboot b/zfsbootmenu/bin/reboot index ad150a26e..5832eb57d 100755 --- a/zfsbootmenu/bin/reboot +++ b/zfsbootmenu/bin/reboot @@ -4,6 +4,7 @@ # shellcheck disable=SC1091 sources=( /lib/kmsg-log-lib.sh + /lib/efi-firmware-lib.sh /lib/zfsbootmenu-core.sh ) @@ -24,6 +25,13 @@ case "${0##*/}" in shutdown|poweroff) trigger="o" ;; + firmware-setup) + if ! check_fw_setup || ! set_fw_setup; then + echo -e "\033[0;31mWARNING: Reboot to UEFI firmware UI unsupported; unable to proceed\033[0m" + exit 1 + fi + trigger="b" + ;; *) exit ;; @@ -34,7 +42,7 @@ while read -r _pool; do done <<<"$( zpool list -H -o name )" sysrq="/proc/sysrq-trigger" -if [ -e "${sysrq}" ] && [ -w "${sysrq}" ]; then +if [ -e "${sysrq}" ] && [ -w "${sysrq}" ]; then echo "${trigger}" > /proc/sysrq-trigger else echo "${sysrq} does not exist, hard reset system" diff --git a/zfsbootmenu/lib/efi-firmware-lib.sh b/zfsbootmenu/lib/efi-firmware-lib.sh new file mode 100644 index 000000000..8be55a6c8 --- /dev/null +++ b/zfsbootmenu/lib/efi-firmware-lib.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# vim: softtabstop=2 shiftwidth=2 expandtab + +source /lib/kmsg-log-lib.sh >/dev/null 2>&1 || exit 1 +source /lib/zfsbootmenu-core.sh >/dev/null 2>&1 || exit 1 + +check_fw_setup() { + local osind_path="/sys/firmware/efi/efivars/OsIndications-8be4df61-93ca-11d2-aa0d-00e098032b8c" + local osind_supp_path="/sys/firmware/efi/efivars/OsIndicationsSupported-8be4df61-93ca-11d2-aa0d-00e098032b8c" + if ! is_efi_system; then + zdebug "efivarfs unsupported" + return 1 + elif [ ! -r "${osind_supp_path}" ]; then + zdebug "OsIndicationsSupported unsupported" + return 1 + elif [ ! -r "${osind_path}" ]; then + zdebug "OsIndications unsupported" + return 1 + fi + + # Check if the EFI_OS_INDICATIONS_BOOT_TO_FW_UI = 0x01 bit is set + if ! (( $(od -An -t u1 -j4 -N1 "${osind_supp_path}" | tr -dc '0-9') & 1 )); then + zdebug "EFI reboot to firmware setup unsupported" + return 1 + fi +} + +set_fw_setup() { + local bytes osind_path="/sys/firmware/efi/efivars/OsIndications-8be4df61-93ca-11d2-aa0d-00e098032b8c" + local -a osindications + + mount_efivarfs rw + if [ ! -w "${osind_path}" ]; then + zdebug "OsIndications not writable" + return 1 + fi + + mapfile -t osindications < <(od -An -t x1 -v -w1 "${osind_path}" | tr -dc '[:alnum:]\n') + + # Set the EFI_OS_INDICATIONS_BOOT_TO_FW_UI = 0x01 bit if not already set + if ! (( "${osindications[4]}" & 0x01 )); then + printf -v osindications[4] '%02x' $(( 0x"${osindications[4]}" | 0x01 )) + + printf -v bytes '\\x%02x' "${osindications[@]}" + printf '%b' "$bytes" > "${osind_path}" + fi +}