mirror of
https://github.com/linuxboot/heads.git
synced 2025-01-18 10:46:44 +00:00
gui-init: Implement blob jail feature
Blob jail provides device firmware blobs to the OS, so the OS does not have to ship them. The firmware is passed through the initrd to /run/firmware, so it works with both installed and live OSes, and there are no race conditions between firmware load and firmware availability. The injection method in the initrd is specific to the style of init script used by PureOS, since it must add a copy command to copy the firmware from the initrd to /run. If the init script is not of this type, boot proceeds without device firmware. This feature can be enabled or disabled from the config GUI. Blob jail is enabled automatically if the Intel AX200 Wi-Fi module is installed and the feature hasn't been explicitly configured. Signed-off-by: Matt DeVillier <matt.devillier@puri.sm>
This commit is contained in:
parent
2d3ecfa41e
commit
87eff7b775
@ -31,34 +31,44 @@ while true; do
|
||||
BASIC_MODE="$(load_config_value CONFIG_PUREBOOT_BASIC)"
|
||||
# check current Restricted Boot Mode
|
||||
RESTRICTED_BOOT="$(load_config_value CONFIG_RESTRICTED_BOOT)"
|
||||
# check current state of blob jail
|
||||
USE_JAIL="$(load_config_value CONFIG_USE_BLOB_JAIL)"
|
||||
AUTOMATIC_POWERON="$(load_config_value CONFIG_AUTOMATIC_POWERON)"
|
||||
BASIC_NO_AUTOMATIC_DEFAULT="$(load_config_value CONFIG_BASIC_NO_AUTOMATIC_DEFAULT)"
|
||||
BASIC_USB_AUTOBOOT="$(load_config_value CONFIG_BASIC_USB_AUTOBOOT)"
|
||||
AUTOMATIC_POWERON="$(load_config_value CONFIG_AUTOMATIC_POWERON)"
|
||||
|
||||
dynamic_config_options=()
|
||||
|
||||
if [ "$BASIC_MODE" = "y" ]; then
|
||||
dynamic_config_options+=( \
|
||||
'P' " $(get_config_display_action "$BASIC_MODE") PureBoot Basic Mode" \
|
||||
'A' " $(get_inverted_config_display_action "$BASIC_NO_AUTOMATIC_DEFAULT") automatic default boot" \
|
||||
'U' " $(get_config_display_action "$BASIC_USB_AUTOBOOT") USB automatic boot" \
|
||||
)
|
||||
else
|
||||
dynamic_config_options+=( \
|
||||
'r' ' Clear GPG key(s) and reset all user settings' \
|
||||
'R' ' Change the root device for hashing' \
|
||||
'D' ' Change the root directories to hash' \
|
||||
'B' ' Check root hashes at boot' \
|
||||
'P' " $(get_config_display_action "$BASIC_MODE") PureBoot Basic Mode" \
|
||||
'L' " $(get_config_display_action "$RESTRICTED_BOOT") Restricted Boot" \
|
||||
)
|
||||
fi
|
||||
# Options that don't apply to basic mode
|
||||
[ "$BASIC_MODE" != "y" ] && dynamic_config_options+=(
|
||||
'r' ' Clear GPG key(s) and reset all user settings'
|
||||
'R' ' Change the root device for hashing'
|
||||
'D' ' Change the root directories to hash'
|
||||
'B' ' Check root hashes at boot'
|
||||
'L' " $(get_config_display_action "$RESTRICTED_BOOT") Restricted Boot"
|
||||
)
|
||||
|
||||
if [ "$CONFIG_SUPPORT_AUTOMATIC_POWERON" = "y" ]; then
|
||||
dynamic_config_options+=( \
|
||||
'N' " $(get_config_display_action "$AUTOMATIC_POWERON") Automatic Power-On" \
|
||||
)
|
||||
fi
|
||||
# Basic itself is always available
|
||||
dynamic_config_options+=(
|
||||
'P' " $(get_config_display_action "$BASIC_MODE") PureBoot Basic Mode"
|
||||
)
|
||||
|
||||
# Blob jail is only offered if this is a configuration with the blobs in
|
||||
# firmware
|
||||
[ "$CONFIG_SUPPORT_BLOB_JAIL" = "y" ] && dynamic_config_options+=(
|
||||
'J' " $(get_config_display_action "$USE_JAIL") Firmware Blob Jail"
|
||||
)
|
||||
|
||||
# Basic-only options for automatic boot
|
||||
[ "$BASIC_MODE" = "y" ] && dynamic_config_options+=(
|
||||
'A' " $(get_inverted_config_display_action "$BASIC_NO_AUTOMATIC_DEFAULT") automatic default boot"
|
||||
'U' " $(get_config_display_action "$BASIC_USB_AUTOBOOT") USB automatic boot"
|
||||
)
|
||||
|
||||
# Automatic power on - requires board support
|
||||
[ "$CONFIG_SUPPORT_AUTOMATIC_POWERON" = "y" ] && dynamic_config_options+=(
|
||||
'N' " $(get_config_display_action "$AUTOMATIC_POWERON") Automatic Power-On"
|
||||
)
|
||||
|
||||
unset menu_choice
|
||||
whiptail $BG_COLOR_MAIN_MENU --title "Config Management Menu" \
|
||||
@ -344,6 +354,31 @@ while true; do
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
"J" )
|
||||
if [ "$USE_JAIL" = "n" ]; then
|
||||
if (whiptail --title 'Enable Firmware Blob Jail?' \
|
||||
--yesno "This will enable loading of firmware from flash on each boot
|
||||
\n\nDo you want to proceed?" 0 80) then
|
||||
|
||||
toggle_config /etc/config.user "CONFIG_USE_BLOB_JAIL"
|
||||
combine_configs
|
||||
|
||||
whiptail --title 'Config change successful' \
|
||||
--msgbox "Firmware Blob Jail use has been enabled;\nsave the config change and reboot for it to go into effect." 16 60
|
||||
|
||||
fi
|
||||
else
|
||||
if (whiptail --title 'Disable Firmware Blob Jail?' \
|
||||
--yesno "This will disable loading of firmware from flash on each boot.
|
||||
\n\nDo you want to proceed?" 0 80) then
|
||||
toggle_config /etc/config.user "CONFIG_USE_BLOB_JAIL"
|
||||
combine_configs
|
||||
|
||||
whiptail --title 'Config change successful' \
|
||||
--msgbox "Firmware Blob Jail use has been disabled;\nsave the config change and reboot for it to go into effect." 0 80
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
"A" )
|
||||
if [ "$BASIC_NO_AUTOMATIC_DEFAULT" = "n" ]; then
|
||||
if (whiptail --title 'Disable automatic default boot?' \
|
||||
|
@ -6,6 +6,7 @@ MAIN_MENU_TITLE="${BOARD_NAME} | Heads Boot Menu"
|
||||
export BG_COLOR_MAIN_MENU=""
|
||||
|
||||
. /etc/functions
|
||||
. /etc/gui_functions
|
||||
. /etc/luks-functions
|
||||
. /tmp/config
|
||||
|
||||
|
89
initrd/bin/inject_firmware.sh
Executable file
89
initrd/bin/inject_firmware.sh
Executable file
@ -0,0 +1,89 @@
|
||||
#!/bin/bash
|
||||
|
||||
# If blob jail is enabled, copy initrd and inject firmware.
|
||||
# Prints new initrd path (in memory) if firmware was injected.
|
||||
#
|
||||
# This does not alter the initrd on disk:
|
||||
# * Signatures are not invalidated
|
||||
# * If the injection fails for any reason, we just proceed with the original
|
||||
# initrd (lacking firmware, but still booting).
|
||||
# * If, somehow, this injection malfunctions (without failing outright) and
|
||||
# prevents a boot, the user can work around it just by disabling blob jail.
|
||||
# We do not risk ruining the real initrd.
|
||||
#
|
||||
# The injection has some requirements on the initrd that are all true for
|
||||
# Debian:
|
||||
# * initrd must be a gzipped cpio (Linux supports other compression methods)
|
||||
# * /init must be a shell script (so we can inject a command to copy firmware)
|
||||
# * There must be an 'exec run-init ... ${rootmnt} ...' line that moves the
|
||||
# real root to / and invokes init
|
||||
#
|
||||
# If the injection can't be performed, boot will continue with no firmware.
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
. /tmp/config
|
||||
. /etc/functions
|
||||
|
||||
if [ "$(load_config_value CONFIG_USE_BLOB_JAIL)" != "y" ]; then
|
||||
# Blob jail not active, nothing to do
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ORIG_INITRD="$1"
|
||||
|
||||
# Extract the init script from the initrd
|
||||
INITRD_ROOT="/tmp/inject_firmware_initrd_root"
|
||||
rm -rf "$INITRD_ROOT" || true
|
||||
mkdir "$INITRD_ROOT"
|
||||
gunzip <"$ORIG_INITRD" | (cd "$INITRD_ROOT"; cpio -i init 2>/dev/null)
|
||||
|
||||
# Copy the firmware into the initrd
|
||||
for f in $(cbfs -l | grep firmware); do
|
||||
mkdir -p "$INITRD_ROOT/$(dirname "$f")"
|
||||
cbfs -r "$f" > "$INITRD_ROOT/$f"
|
||||
if [[ "$f" == *.lzma ]]; then
|
||||
lzma -d "$INITRD_ROOT/$f"
|
||||
fi
|
||||
done
|
||||
|
||||
# awk will happily pass through a binary file, so look for the match we want
|
||||
# before modifying init to ensure it's a shell script and not an ELF, etc.
|
||||
if ! grep -E -q '^exec run-init .*\$\{rootmnt\}' "$INITRD_ROOT/init"; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# The initrd's /init has to copy the firmware to /run/firmware, so it will be
|
||||
# present when the real root is moved to /.
|
||||
# * Wi-Fi/BT firmware loading doesn't happen during the initrd - these modules
|
||||
# aren't in the initrd anyway, typically.
|
||||
# * /run is a tmpfs mount, so this works even if the root filesystem is
|
||||
# read-only, and it doesn't persist anything.
|
||||
#
|
||||
# kexec-boot will add a kernel parameter for the kernel to look for firmware in
|
||||
# /run/firmware.
|
||||
#
|
||||
# Debian's init script ends with an "exec run-init ..." (followed by a few lines
|
||||
# to print a message in case it fails). At that point, root is mounted, and
|
||||
# run-init will move it to / and then exec init. We can copy the firmware just
|
||||
# before that, so we don't have to know anything about how root was mounted.
|
||||
#
|
||||
# The root path is in ${rootmnt}, which should appear in the run-init command.
|
||||
# If it doesn't, then we don't understand the init script.
|
||||
AWK_INSERT_CP='
|
||||
BEGIN{inserted=0}
|
||||
/^exec run-init .*\$\{rootmnt\}/ && inserted==0 {print "cp -r /firmware ${rootmnt}/run/firmware"; inserted=1}
|
||||
{print $0}'
|
||||
|
||||
awk -e "$AWK_INSERT_CP" "$INITRD_ROOT/init" >"$INITRD_ROOT/init_fw"
|
||||
mv "$INITRD_ROOT/init_fw" "$INITRD_ROOT/init"
|
||||
chmod a+x "$INITRD_ROOT/init"
|
||||
|
||||
# Pad the original initrd to 512 byte blocks, the last gzip blob is often not
|
||||
# padded. (If it is not gzip-compressed, we would already have failed above.)
|
||||
FW_INITRD="/tmp/inject_firmware_initrd.cpio.gz"
|
||||
dd if="$ORIG_INITRD" of="$FW_INITRD" bs=512 conv=sync status=none
|
||||
# Pack up the new contents and append to the initrd
|
||||
(cd "$INITRD_ROOT"; find . | cpio -o -H newc) | gzip >>"$FW_INITRD"
|
||||
# Use this initrd
|
||||
echo "$FW_INITRD"
|
@ -34,6 +34,10 @@ kexeccmd="kexec"
|
||||
cmdadd="$CONFIG_BOOT_KERNEL_ADD $cmdadd"
|
||||
cmdremove="$CONFIG_BOOT_KERNEL_REMOVE $cmdremove"
|
||||
|
||||
if [ "$(load_config_value CONFIG_USE_BLOB_JAIL)" = "y" ]; then
|
||||
cmdadd="$cmdadd firmware_class.path=/run/firmware/"
|
||||
fi
|
||||
|
||||
fix_file_path() {
|
||||
if [ "$printfiles" = "y" ]; then
|
||||
# output file relative to local boot directory
|
||||
@ -120,6 +124,10 @@ do
|
||||
if [ -n "$override_initrd" ]; then
|
||||
filepath="$override_initrd"
|
||||
fi
|
||||
firmware_initrd="$(inject_firmware.sh "$filepath" || true)"
|
||||
if [ -n "$firmware_initrd" ]; then
|
||||
filepath="$firmware_initrd"
|
||||
fi
|
||||
kexeccmd="$kexeccmd --initrd=$filepath"
|
||||
fi
|
||||
if [ "$key" = "append" ]; then
|
||||
|
@ -344,6 +344,17 @@ set_config() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Load a config value to a variable, defaulting to 'n'
|
||||
load_config_value()
|
||||
{
|
||||
local config_name="$1"
|
||||
if grep -q "$config_name" /tmp/config; then
|
||||
grep "$config_name=" /tmp/config | tail -n1 | cut -f2 -d '=' | tr -d '"'
|
||||
else
|
||||
echo n
|
||||
fi
|
||||
}
|
||||
|
||||
# Generate secret value using first 20 chars of ROM SHA256 hash
|
||||
secret_from_rom_hash() {
|
||||
local ROM_IMAGE="/tmp/coreboot-notpm.rom"
|
||||
|
@ -102,17 +102,6 @@ show_system_info()
|
||||
--msgbox "${BOARD_NAME}\n\nFW_VER: ${FW_VER}\nKernel: ${kernel}\n\nCPU: ${cpustr}\nRAM: ${memtotal} GB\n$battery_status\n$(fdisk -l | grep -e '/dev/sd.:' -e '/dev/nvme.*:' | sed 's/B,.*/B/')" 16 60
|
||||
}
|
||||
|
||||
# Load a config value to a variable, defaulting to 'n'
|
||||
load_config_value()
|
||||
{
|
||||
local config_name="$1"
|
||||
if grep -q "$config_name" /tmp/config; then
|
||||
grep "$config_name=" /tmp/config | tail -n1 | cut -f2 -d '=' | tr -d '"'
|
||||
else
|
||||
echo n
|
||||
fi
|
||||
}
|
||||
|
||||
# Get "Enable" or "Disable" to display in the configuration menu, based on a
|
||||
# setting value
|
||||
get_config_display_action()
|
||||
|
@ -120,6 +120,13 @@ if [ "$boot_option" = "r" ]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
# Override CONFIG_USE_BLOB_JAIL if needed and persist via user config
|
||||
if lspci -n | grep -q "8086:2723"; then
|
||||
if ! cat /etc/config.user 2>/dev/null | grep -q "USE_BLOB_JAIL"; then
|
||||
echo "CONFIG_USE_BLOB_JAIL=y" >> /etc/config.user
|
||||
fi
|
||||
fi
|
||||
|
||||
# Override CONFIG_TPM and CONFIG_TPM2_TOOLS from /etc/config with runtime value
|
||||
# determined above.
|
||||
#
|
||||
|
Loading…
Reference in New Issue
Block a user