Last active
April 2, 2021 17:17
-
-
Save openglfreak/77851807a36538f947607df0ed87bce2 to your computer and use it in GitHub Desktop.
My own EFISTUB setup/update script - Development has moved to https://github.com/openglfreak/efistubmgr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
VERBOSE=1 | |
_cmdline_quiet='quiet vga=current loglevel=3 rd.systemd.show_status=auto rd.udev.log_priority=3' | |
_cmdline_misc='rw splash add_efi_memmap threadirqs sysrq_always_enabled=1 systemd.unified_cgroup_hierarchy=1' | |
_cmdline_nospec='pti=off nopti spectre_v1=off nospectre_v1 spectre_v2=off nospectre_v2 spectre_v2_app2app=off nospectre_v2_app2app l1tf=off nol1tf kvm-intel.vmentry_l1d_flush=never spec_store_bypass_disable=off nospec_store_bypass_disable no_stf_barrier mitigations=off clearcpuid=514' | |
_cmdline_perf='nowatchdog nmi_watchdog=0 intel_iommu=igfx_off workqueue.power_efficient=0 intel_pstate=hwp_only libahci.ignore_sss=1 scsi_mod.use_blk_mq=1 cryptomgr.notests elevator=bfq noreplace-smp page_alloc.shuffle=1 rcupdate.rcu_expedited=1 skew_tick=1' | |
_cmdline_gfx='i915.fastboot=1 i915.modeset=1 i915.enable_fbc=0 i915.enable_guc=0 i915.alpha_support=1 i915.disable_power_well=1 i915.lvds_channel_mode=2 nouveau.modeset=0 nvidia-drm.modeset=1' | |
_cmdline_fixes='acpi_osi=Linux acpi_sleep=nonvs iomem=relaxed slab_common.usercopy_fallback=y' | |
CMDLINE="$_cmdline_quiet $_cmdline_misc $_cmdline_nospec $_cmdline_perf $_cmdline_gfx $_cmdline_fixes" | |
_LABEL='Arch Linux (Default) (fallback initramfs)' | |
_KERNEL=vmlinuz-linux | |
_INITRD=initramfs-linux-fallback.img | |
add_boot_entry | |
_LABEL='Arch Linux (Default)' | |
_KERNEL=vmlinuz-linux | |
_INITRD=initramfs-linux.img | |
add_boot_entry | |
_LABEL='Arch Linux (TkG 5.8 PDS) (fallback initramfs)' | |
_KERNEL=vmlinuz-linux58-tkg-pds | |
_INITRD=initramfs-linux58-tkg-pds-fallback.img | |
add_boot_entry | |
_LABEL='Arch Linux (TkG 5.8 PDS)' | |
_KERNEL=vmlinuz-linux58-tkg-pds | |
_INITRD=initramfs-linux58-tkg-pds.img | |
add_boot_entry | |
_LABEL='Arch Linux (TkG 5.9 PDS) (fallback initramfs)' | |
_KERNEL=vmlinuz-linux59-tkg-pds | |
_INITRD=initramfs-linux59-tkg-pds-fallback.img | |
add_boot_entry | |
_LABEL='Arch Linux (TkG 5.9 PDS)' | |
_KERNEL=vmlinuz-linux59-tkg-pds | |
_INITRD=initramfs-linux59-tkg-pds.img | |
add_boot_entry | |
_LABEL='Arch Linux (TkG 5.10 PDS) (fallback initramfs)' | |
_KERNEL=vmlinuz-linux510-tkg-pds | |
_INITRD=initramfs-linux510-tkg-pds-fallback.img | |
add_boot_entry | |
_LABEL='Arch Linux (TkG 5.10 PDS)' | |
_KERNEL=vmlinuz-linux510-tkg-pds | |
_INITRD=initramfs-linux510-tkg-pds.img | |
add_boot_entry |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
set -e 2>/dev/null ||: | |
set +C 2>/dev/null ||: | |
set +f 2>/dev/null ||: | |
set -u 2>/dev/null ||: | |
# description: | |
# Escapes a string for usage in a sed pattern. | |
# Sed expression copied from https://stackoverflow.com/a/2705678 | |
# params: | |
# [literal]: string | |
# The string to escape. If omitted it's read from stdin | |
# [separator]: char | |
# The separator char. Defaults to a / (slash) | |
# outputs: | |
# The escaped string | |
sed_escape_pattern() { | |
if [ $# -gt 2 ]; then | |
echo 'sed_escape_pattern: Too many arguments' >&2 | |
return 1 | |
fi | |
if [ $# -ge 1 ]; then | |
# Shellcheck bug. | |
# shellcheck disable=SC2221,SC2222 | |
case "${2:-}" in | |
??*) | |
echo 'sed_escape_pattern: Separator too long' >&2 | |
return 2;; | |
[]\\\$\*\.^[]) set -- "$1" '';; | |
''|*) set -- "$1" "${2:-/}" | |
esac | |
# shellcheck disable=SC1003 | |
printf '%s\n' "$1" | sed -e 's/[]\\'"$2"'$*.^[]/\\&/g' -e '$!s/$/\\/' | |
else | |
sed -e 's/[]\\/$*.^[]/\\&/g' -e '$!s/$/\\/' | |
fi | |
} | |
# description: | |
# Finds a boot entry by the label and returns the entry's bootnum | |
# params: | |
# name: string | |
# The label of the boot entry to find | |
# outputs: | |
# The bootnums of the found boot entries as 4-digit hexadecimal numbers, | |
# one per line, or nothing if no entry is found | |
find_bootnum_from_label() { | |
if [ $# -ne 1 ]; then | |
if [ $# -lt 1 ]; then | |
echo 'find_bootnum_from_label: Not enough arguments' >&2 | |
return 1 | |
else | |
echo 'find_bootnum_from_label: Too many arguments' >&2 | |
return 2 | |
fi | |
fi | |
LC_ALL=C efibootmgr | sed -n -e 's/^Boot\([0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]\)\*\? '"$(sed_escape_pattern "$1")"'$/\1/p' | |
} | |
# description: | |
# Deletes a list of boot entries by their bootnums | |
# params: | |
# [bootnums...]: string | |
# The bootnums of the boot entries to delete | |
delete_bootnums() { | |
while [ $# -ge 1 ]; do | |
efibootmgr --bootnum "$1" --delete-bootnum >/dev/null || return | |
shift | |
done | |
} | |
# description: | |
# Finds a boot entry by the label and deletes it | |
# params: | |
# name: string | |
# The label of the boot entry to delete | |
# outputs: | |
# The number of boot entries found | |
delete_bootentry_by_label() { | |
if [ $# -ne 1 ]; then | |
if [ $# -lt 1 ]; then | |
echo 'delete_bootentry_by_label: Not enough arguments' >&2 | |
return 1 | |
else | |
echo 'delete_bootentry_by_label: Too many arguments' >&2 | |
return 2 | |
fi | |
fi | |
# shellcheck disable=SC2046 | |
set -- $(find_bootnum_from_label "$1") | |
printf '%i\n' $# | |
delete_bootnums ${1+"$@"} | |
} | |
# description: | |
# Creates or updates a boot entry with new parameters. | |
# See efibootmgr(8) for the default values of optional parameters. | |
# Empty optional parameters are handled like unset | |
# params: | |
# label: string | |
# The label of the boot entry | |
# loader: string | |
# The loader for the boot entry | |
# [cmdline]: string | |
# Command line arguments for the loader | |
# [disk]: string | |
# The disk containing the loader | |
# [part]: string | |
# The partition containing the loader | |
# outputs: | |
# The efibootmgr output | |
update_bootentry() { | |
if [ $# -lt 2 ]; then | |
echo 'update_bootentry: Not enough arguments' >&2 | |
return 1 | |
fi | |
if [ $# -gt 5 ]; then | |
echo 'update_bootentry: Too many arguments' >&2 | |
return 2 | |
fi | |
delete_bootentry_by_label "$1" >/dev/null || return | |
# Separate presence checks for option and value because Zsh doesn't do | |
# word splitting by default. | |
efibootmgr --create ${4:+--disk} ${4:+"$4"} \ | |
${5:+--part} ${5:+"$5"} \ | |
--label "$1" \ | |
--loader "$2" \ | |
${3:+--unicode} ${3:+"$3"} | |
} | |
_is_dry_run() { [ "x${dry_run:-${DRY_RUN:-0}}" = 'x1' ] || return; } | |
_is_verbose() { [ "x${verbose:-${VERBOSE:-0}}" = 'x1' ] || return; } | |
_is_true() { | |
set -- "$(printf '%s\n' "$1" | tr '[:upper:]' '[:lower:]')" | |
case "$1" in | |
true|1|y|yes|on) :;; | |
*) return 1 | |
esac | |
} | |
_load_config() { | |
! _is_verbose || printf 'Loading config file %s\n' "$1" | |
# shellcheck source=/etc/xdg/efistubmgr.conf | |
# shellcheck disable=SC1091,SC2034 | |
if ! . "$1"; then | |
printf 'error: Error while processing config file %s\n' "$1" >&2 | |
return 1 | |
fi | |
} | |
_load_configs_dir() { | |
if [ -e "$1/efistubmgr.conf" ]; then | |
_load_config "$1/efistubmgr.conf" | |
fi | |
for config_file in "$1/efistubmgr.conf.d"/*.conf; do | |
[ -e "${config_file}" ] || continue | |
_load_config "${config_file}" 2>&3 3>&- | |
done 3>&2 2>/dev/null || : | |
} | |
_load_configs() { | |
if [ "x${single_config+set}" = 'xset' ]; then | |
# shellcheck disable=SC1090 | |
if ! . "${single_config}"; then | |
printf 'error: Error while processing config file %s\n' "${single_config}" >&2 | |
return 1 | |
fi | |
return | |
fi | |
! [ -d /boot ] || _load_configs_dir /boot | |
IFS=':' eval 'set -- ${XDG_CONFIG_DIRS:-/etc/xdg}' | |
i=$#; while [ "${i}" -ge 1 ]; do | |
eval "_load_configs_dir \"\${${i}}\"" | |
i="$((i-1))" | |
done | |
} | |
_create_data_dir() { | |
mkdir -p /var/lib || return | |
# shellcheck disable=SC2174 | |
mkdir -p -m 700 /var/lib/efistubmgr | |
} | |
_check_managed_entry_list_writable() { | |
if ! _is_dry_run; then | |
if [ -e /var/lib/efistubmgr/managed_entries ]; then | |
if ! [ -w /var/lib/efistubmgr/managed_entries ]; then | |
echo 'error: State file not writable' >&2 | |
return 1 | |
fi | |
else | |
if ! [ -w /var/lib/efistubmgr ]; then | |
echo 'error: State directory not writable' >&2 | |
return 2 | |
fi | |
fi | |
fi | |
} | |
_load_managed_entry_list() { | |
_check_managed_entry_list_writable || return | |
! _is_verbose || echo 'Loading managed entry list' | |
if [ -e /var/lib/efistubmgr/managed_entries ]; then | |
if ! managed_entries="$(cat /var/lib/efistubmgr/managed_entries)"; then | |
echo 'error: State file not readable' >&2 | |
return 1 | |
fi | |
else | |
managed_entries= | |
fi | |
} | |
_update_entry() { | |
eval "label=\"\${LABEL_$1:-}\"" | |
! _is_verbose || printf 'Creating/updating boot entry "%s"\n' "${label}" | |
eval "kernel=\"\${KERNEL_$1:-}\"" | |
eval "cmdline=\"\${CMDLINE_$1:-\${CMDLINE:-}}\"" | |
eval "no_autodetect_ucode=\"\${NO_AUTODETECT_UCODE_$1:-\${NO_AUTODETECT_UCODE:-}}\"" | |
if eval "[ \"x\${INITRD_$1+set}\" = 'xset' ]"; then | |
eval "initrds=\"initrd=\\\\\${INITRD_$1}\"" | |
else | |
initrds= | |
j=0; while eval "[ \"x\${INITRD_$1_${j}+set}\" = 'xset' ]"; do | |
eval "initrds=\"\${initrds} initrd=\\\\\${INITRD_$1_${j}}\"" | |
j="$((j+1))" | |
done | |
initrds="${initrds# }" | |
fi | |
# shellcheck disable=SC2154 | |
if [ "x${initrds:+set}" = 'xset' ] && _is_true "${no_autodetect_ucode}"; then | |
if [ "x${ucode_initrds+set}" = 'xset' ]; then | |
! _is_verbose || echo 'Searching for microcode initrds' | |
ucode_initrds= | |
for ucode_initrd in /boot/*-ucode.img; do | |
[ -e "${ucode_initrd}" ] || continue | |
! _is_verbose || printf 'Found microcode initrd %s\n' "${ucode_initrd}" | |
ucode_initrds="initrd=\\${ucode_initrd#/boot/} ${ucode_initrds}" | |
done | |
fi | |
initrds="${ucode_initrds}${initrds}" | |
fi | |
if [ "x${initrds:+set}" = 'xset' ]; then | |
cmdline="root=UUID=${rootuuid=$(findmnt -vkno UUID /)} rootfstype=${rootfstype=$(findmnt -vkno FSTYPE /)} rootflags=${rootflags=$(findmnt -vkno OPTIONS /)} ${initrds} ${cmdline}" | |
else | |
cmdline="root=${rootdev=$(findmnt -vkno SOURCE /)} rootfstype=${rootfstype=$(findmnt -vkno FSTYPE /)} rootflags=${rootflags=$(findmnt -vkno OPTIONS /)} ${cmdline}" | |
fi | |
if ! _is_dry_run; then | |
# shellcheck disable=SC2154 | |
if ! update_bootentry "${label}" "\\${kernel}" "${cmdline}" >/dev/null; then | |
printf 'error: Failed to create/update boot entry "%s"\n' "${label}" >&2 | |
return 1 | |
fi | |
fi | |
} | |
_update_entries() { | |
_new_managed_entries= | |
i=0; while eval "[ \"x\${LABEL_${i}+set}\" = 'xset' ]"; do | |
_update_entry "${i}" || return | |
_new_managed_entries="${_new_managed_entries} | |
${label}" | |
i="$((i+1))" | |
done | |
new_managed_entries="${_new_managed_entries#?}" | |
} | |
_get_tmpdir() { | |
if [ "x${XDG_RUNTIME_DIR:+set}" = 'xset' ]; then | |
tmpdir="${XDG_RUNTIME_DIR}" | |
# shellcheck disable=SC2174 | |
mkdir -p -m 700 "${tmpdir}" | |
else | |
tmpdir="${TMPDIR:-${TEMPDIR:-/tmp}}" | |
# shellcheck disable=SC2174 | |
mkdir -p -m 777 "${tmpdir}" | |
fi | |
} | |
_remove_old_entries() { | |
_get_tmpdir | |
printf '%s\n' "${managed_entries}" | LC_ALL=C sort -u >"${tmpdir}/efistubmgr-old-managed" || return | |
printf '%s\n' "${new_managed_entries}" | LC_ALL=C sort -u >"${tmpdir}/efistubmgr-new-managed" || return | |
entries_to_remove="$(LC_ALL=C comm -23 "${tmpdir}/efistubmgr-old-managed" "${tmpdir}/efistubmgr-new-managed")" | |
[ "x${entries_to_remove:+set}" = 'xset' ] || return 0 | |
_failed_to_remove= | |
while IFS= read -r label; do | |
! _is_verbose || printf 'Removing boot entry "%s"\n' "${label}" | |
if ! _is_dry_run; then | |
if ! delete_bootentry_by_label "${label}" >/dev/null; then | |
! _is_verbose || printf 'Failed to remove boot entry "%s"\n' "${label}" | |
_failed_to_remove="${_failed_to_remove}${label} | |
" | |
fi | |
fi | |
done <<EOF | |
${entries_to_remove} | |
EOF | |
new_managed_entries="${_failed_to_remove}${new_managed_entries}" | |
} | |
_write_new_managed_entry_list() { | |
! _is_verbose || echo 'Saving new managed entry list' | |
if ! _is_dry_run; then | |
if ! printf '%s\n' "${new_managed_entries}" >/var/lib/efistubmgr/managed_entries; then | |
echo 'error: Could not write new managed boot entry list' >&2 | |
return 1 | |
fi | |
fi | |
} | |
_update_entries_and_state() { | |
_create_data_dir || return | |
_load_managed_entry_list || return | |
if _update_entries; then | |
_remove_old_entries | |
else | |
if [ "x${new_managed_entries:+set}" = 'xset' ]; then | |
new_managed_entries="${managed_entries} | |
${new_managed_entries}" | |
else | |
new_managed_entries="${managed_entries}" | |
fi | |
fi | |
_write_new_managed_entry_list | |
} | |
_save_entry_data() { | |
for varname in LABEL KERNEL CMDLINE NO_AUTODETECT_UCODE; do | |
if eval "[ \"x\${_${varname}+set}\" = 'xset' ]"; then | |
eval "${varname}_${1}=\"\${_${varname}}\"" | |
fi | |
done | |
if [ "x${_INITRD+set}" = 'xset' ]; then | |
eval "INITRD_${1}=\"\${_INITRD}\"" | |
else | |
j=0; while eval "[ \"x\${_INITRD_${j}+set}\" = 'xset' ]"; do | |
eval "INITRD_$1_${j}=\"\${_INITRD_${j}}\"" | |
j="$((j+1))" | |
done | |
fi | |
} | |
_clear_entry_data() { | |
for varname in LABEL KERNEL CMDLINE NO_AUTODETECT_UCODE; do | |
if eval "[ \"x\${_${varname}+set}\" = 'xset' ]"; then | |
eval "unset _${varname}" | |
fi | |
done | |
unset _INITRD | |
i=0; while eval "[ \"x\${_INITRD_${i}+set}\" = 'xset' ]"; do | |
eval "unset \${_INITRD_${i}}" | |
i="$((i+1))" | |
done | |
} | |
add_boot_entry() { | |
i=0; while eval "[ \"x\${LABEL_${i}+set}\" = 'xset' ]"; do | |
i="$((i+1))" | |
done | |
_save_entry_data "${i}" || return | |
_clear_entry_data | |
} | |
# description: | |
# The main function of the program | |
# params: | |
# [options...]: string | |
# The command line options for the program | |
# outputs: | |
# Log messages, if enabled | |
main() { | |
while [ $# -ge 1 ]; do | |
case "$1" in | |
--config) | |
single_config="$2" | |
shift 2;; | |
--dry-run) | |
DRY_RUN=1 | |
shift;; | |
--verbose) | |
VERBOSE=1 | |
shift;; | |
--help) | |
cat <<EOF | |
Usage: ${0##*/} [options] | |
Options: | |
--config <file> Use an alternate config file | |
--dry-run Do not update any files or efi variables | |
--verbose Output status messages while working | |
--help Show this help message | |
EOF | |
return;; | |
*) | |
printf 'error: Unrecognized parameter: %s\n' "$1" | |
return 1 | |
esac | |
done | |
_load_configs || return | |
_update_entries_and_state | |
} | |
main ${1+"$@"} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment