Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zfsbootmenu/bin/reboot: add reboot-to-uefi functionality #607

Merged
merged 3 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/online/main-screen.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,7 @@ Keyboard Shortcuts
*[MOD+L]* **view logs**

View logs, as indicated by *[!]*. The indicator will be yellow for warning conditions and red for errors.

*[MOD+X]* **power menu**

Show menu with options to shutdown, reboot, or reboot to UEFI firmware setup (if available).
4 changes: 4 additions & 0 deletions docs/online/recovery-shell.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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).
1 change: 1 addition & 0 deletions zfsbootmenu/bin/firmware-setup
10 changes: 9 additions & 1 deletion zfsbootmenu/bin/reboot
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# shellcheck disable=SC1091
sources=(
/lib/kmsg-log-lib.sh
/lib/efi-firmware-lib.sh
/lib/zfsbootmenu-core.sh
)

Expand All @@ -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
;;
Expand All @@ -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"
Expand Down
3 changes: 3 additions & 0 deletions zfsbootmenu/bin/zfsbootmenu
Original file line number Diff line number Diff line change
Expand Up @@ -282,5 +282,8 @@ while true; do
"mod-o")
change_sort
;;
"mod-x")
/libexec/zpowermenu
;;
esac
done
47 changes: 47 additions & 0 deletions zfsbootmenu/lib/efi-firmware-lib.sh
Original file line number Diff line number Diff line change
@@ -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
}
60 changes: 55 additions & 5 deletions zfsbootmenu/lib/zfsbootmenu-ui.sh
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,10 @@ draw_be() {
fi

header="$( column_wrap "\
^[RETURN] boot:[ESCAPE] refresh view:[CTRL+P] pool status
^[CTRL+D] set bootfs:[CTRL+S] snapshots:[CTRL+K] kernels
^[CTRL+E] edit kcl:[CTRL+J] jump into chroot:[CTRL+R] recovery shell
^${kcl_text:+${kcl_text}:}[CTRL+L] view logs:${blank}[CTRL+H] help" \
^[RETURN] boot:[CTRL+K] kernels:[CTRL+P] pool status
^[CTRL+D] set bootfs:[CTRL+J] jump into chroot:[CTRL+L] view logs
^[CTRL+S] snapshots:[CTRL+R] recovery shell:[CTRL+X] power menu
^[CTRL+E] edit kcl${kcl_text:+:${kcl_text}}:${blank}[CTRL+H] help" \
"\
^[RETURN] boot
^[CTRL+R] recovery shell
Expand All @@ -160,7 +160,7 @@ draw_be() {
sort_key="$( get_sort_key )"
preview_label="Sorted by: ${sort_key^}"

expects="--expect=alt-e,alt-k,alt-d,alt-s,alt-c,alt-r,alt-p,alt-w,alt-j,alt-o${kcl_bind:+,${kcl_bind}},right"
expects="--expect=alt-e,alt-k,alt-d,alt-s,alt-c,alt-r,alt-p,alt-w,alt-j,alt-o,alt-x${kcl_bind:+,${kcl_bind}},right"

# shellcheck disable=SC2086
if ! selected="$( ${FUZZYSEL} -0 --prompt "BE > " \
Expand Down Expand Up @@ -664,3 +664,53 @@ populate_be_list() {
done
return $ret
}

# arg1: header/title text
# arg2..N: prompt options in the form "action:display text"
# prints: selected action
# returns: nothing

draw_modal_prompt() {
local -i ROWS COLS LMARGIN TMARGIN maxlen=10 nopts=0
local PROMPT="" OUTPUT TITLE disp action text

TITLE=" ${1} "
if (( "${#TITLE}" > maxlen )); then
maxlen="${#TITLE}"
fi
shift

for itm; do
disp="${itm#*:}"
if (( "${#disp}" > maxlen )); then
maxlen="${#disp}"
fi
PROMPT+="${itm}"$'\n'
nopts=$(( nopts + 1 ))
done
PROMPT+=":Cancel"
nopts=$(( nopts + 1 ))

ROWS="$(tput lines)"
COLS="$(tput cols)"

LMARGIN=$(( (COLS - maxlen - 6) / 2 ))
TMARGIN=$(( (ROWS - nopts - 3) / 2 ))

if OUTPUT="$(echo -e "${PROMPT}" | \
fzf --no-info \
--with-nth="2.." \
--delimiter=":" \
--no-multi \
--no-sort \
${HAS_BORDER:+--border=sharp} \
${HAS_BORDER:+--border-label="${TITLE}"} \
--prompt="> " \
--layout=default \
--no-scrollbar \
--margin "${TMARGIN},${LMARGIN}")"; then
# shellcheck disable=SC2034
IFS=":" read -r action text <<< "${OUTPUT}"
printf "%s" "$action"
fi
}
14 changes: 14 additions & 0 deletions zfsbootmenu/libexec/zpowermenu
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash
# vim: softtabstop=2 shiftwidth=2 expandtab

source /lib/efi-firmware-lib.sh >/dev/null 2>&1 || exit 1
source /lib/zfsbootmenu-ui.sh >/dev/null 2>&1 || exit 1

PROMPT=("/bin/poweroff:Shutdown" "/bin/reboot:Reboot")
if check_fw_setup; then
PROMPT+=("/bin/firmware-setup:Reboot to UEFI Firmware")
fi

if execute="$(draw_modal_prompt "Power Menu" "${PROMPT[@]}")" && [ -x "$execute" ]; then
exec "$execute"
fi
Loading