zbm-dev/zfsbootmenu

feature request: option to reboot into efi firmware setup

Closed this issue · 5 comments

Use case: You want to go to the BIOS/EFI setup menu. But for example didn't press the key in time, or perhaps don't know which key to press for this particular motherboard, etc. What if you could just press a simple hotkey, for example "Ctrl-F" for firmware, and let ZBM reboot into it?

Similar features exist for example in GRUB, the Windows bootloader, systemd (systemctl reboot --firmware-setup).

Looks like it should be fairly straightforward to implement, just set a value in the efivarfs and reboot. We can detect if it is supported before showing the hotkey option at all. A quickly thrown together example:

#!/bin/bash

# args: none
# prints: nothing
# returns: 0 if reboot to EFI firmware UI is supported, 1 if not
efi_supports_reboot_to_fw_ui() {
    if ! is_efi_system ; then
        zdebug "efivarfs unsupported"
        return 1
    elif [ ! -r /sys/firmware/efi/efivars/OsIndicationsSupported-8be4df61-93ca-11d2-aa0d-00e098032b8c ] ; then
        zdebug "OsIndicationsSupported unsupported"
        return 1
    elif [ ! -r /sys/firmware/efi/efivars/OsIndications-8be4df61-93ca-11d2-aa0d-00e098032b8c ] ; then
        zdebug "OsIndications unsupported"
        return 1
    fi

    # Check if the EFI_OS_INDICATIONS_BOOT_TO_FW_UI = 0x01 bit is set
    perl -e 'exit !(unpack("x4Q<", <STDIN>) & 1)' \
        < /sys/firmware/efi/efivars/OsIndicationsSupported-8be4df61-93ca-11d2-aa0d-00e098032b8c
}

# args: none
# prints: nothing
# returns: 1 on error, 2 if unsupported, otherwise does not return
efi_reboot_to_fw_ui() {
    if ! efi_supports_reboot_to_fw_ui ; then
        zdebug "efi reboot to fw unsupported"
        return 2
    fi

    # Remount efivarfs to be writable
    mount_efivarfs rw
    if [ ! -w /sys/firmware/efi/efivars/OsIndications-8be4df61-93ca-11d2-aa0d-00e098032b8c ] ; then
        zdebug "OsIndications not writable"
        return 1
    fi

    # Set the EFI_OS_INDICATIONS_BOOT_TO_FW_UI = 0x01 bit,
    # with required flags set:
    # EFI_VARIABLE_NON_VOLATILE = 0x01
    # EFI_VARIABLE_BOOTSERVICE_ACCESS = 0x02
    # EFI_VARIABLE_RUNTIME_ACCESS = 0x04
    perl -e 'print pack("(LQ)<", 1|2|4, unpack("x4Q<", <STDIN>) | 1)' \
        < /sys/firmware/efi/efivars/OsIndications-8be4df61-93ca-11d2-aa0d-00e098032b8c \
        | sponge /sys/firmware/efi/efivars/OsIndications-8be4df61-93ca-11d2-aa0d-00e098032b8c
    reboot
}

This example depends on perl (for the unsigned 64 bit LE integers) and sponge (from moreutils), but it can probably be done in pure Bash as well.

Implementing this as a script that can be run in the recovery shell would be acceptable. It would be need to be done with the current tooling/binaries that we ship, though. Perl in the initramfs is a non-starter.

If you'd like to implement and document the tool, I'll be glad to review the PR. Thanks!

It would be need to be done with the current tooling/binaries that we ship, though. Perl in the initramfs is a non-starter.

Of course. :) Doing the bit manipulation in pure Shell will probably be a bit more verbose, and I didn't want to make the example complicated. Since I saw this repo contained some Perl code already I figured using that would make the high-level thing it's doing easy enough to understand.

I'll have a look at making a PR with a script for the recovery shell when I have some more time. :)

Awesome! Let us know if you have any questions - we're also active on IRC if you'd prefer real time communication.

    # with required flags set:
    # EFI_VARIABLE_NON_VOLATILE = 0x01
    # EFI_VARIABLE_BOOTSERVICE_ACCESS = 0x02
    # EFI_VARIABLE_RUNTIME_ACCESS = 0x04

why do you set these attributes? systemd does not and from my reading of the UEFI spec, it appears that they should be set by the firmware only:

If a preexisting variable is rewritten with different attributes, SetVariable() shall not modify the variable and shall return EFI_INVALID_PARAMETER.

For reference, this was the WIP shell-based version I have on my local system: https://gist.github.com/DvdGiessen/f0421b0d921008ab570d69fcb1505c85

why do you set these attributes?

Hm, it's been a while so I don't recall the exact details. Seeing that line from the spec indeed makes it looks like I shouldn't be doing that. I might have misread the UEFI spec back then; I never got further than a quick proof-of-concept.

EDIT: I think I tested it a few times and haven't used it since either. Sorry I didn't find time to make a PR myself as mentioned, but happy to see someone else is. 👍🏻