Files
Panama/setup/scripts/install-hardware
T

155 lines
7.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# The parts of an install that depend on what the machine actually is: the
# NVIDIA driver, the machine owner key that lets it load under Secure Boot,
# Fedora's preinstalled extras, and firmware.
#
# Runs last. MOK enrollment arms a prompt consumed at the next boot and firmware
# updates can ask for a reboot, so neither belongs in front of the package work
# or the dotfiles -- a machine that reboots out of this stage has already been
# fully configured.
#
# Nothing here decides anything. Every branch is an answer the interview
# collected before the run began, and an absent answer means no, which is what
# makes this safe to re-run by hand while repairing one piece of a machine.
#
# sunhat's version of this opened an editor in the middle of the run so grub
# could be hand-corrected. That is the exact failure this repository exists to
# avoid, and it is unnecessary: grubby replaces an argument that already exists
# rather than appending a second copy, so the duplicates that had to be cleaned
# up by hand cannot accumulate in the first place.
set -uo pipefail
log() { echo -e "\033[1;34m[INFO]\033[0m $*"; }
warn() { echo -e "\033[1;33m[WARN]\033[0m $*" >&2; }
# Fedora ships these and Panama uses none of them. Named here rather than in the
# interview so there is one list: the interview asks for it with --debloat-list
# to name what it is about to remove, and this stage removes it. A package that
# is not installed is skipped rather than passed to dnf, so the list can outlive
# a Fedora release -- totem left in Fedora 43 and the list should not start
# failing because of it.
DEBLOAT=(gnome-contacts gnome-tour gnome-maps showtime)
if [[ "${1:-}" == "--debloat-list" ]]; then
printf '%s\n' "${DEBLOAT[@]}"
exit 0
fi
# ── NVIDIA ───────────────────────────────────────────────────────────────────
if [[ "${PANAMA_NVIDIA:-no}" == yes ]]; then
# The interview asks about MOK enrollment only when mokutil was present to
# see Secure Boot at all. Re-check here rather than trusting that the
# question was ever asked: installing akmod-nvidia and blacklisting
# nouveau under Secure Boot with no key to enroll produces a machine that
# reboots into an unloadable driver with its fallback disabled -- the one
# failure in this installer that costs a person their display.
if mokutil --sb-state 2>/dev/null | grep -qi 'secureboot enabled' \
&& [[ -z "${PANAMA_MOK_HASH:-}" ]]; then
warn "Secure Boot is on and no MOK enrollment was prepared; refusing to install"
warn "the NVIDIA driver, which could not load. Re-run ./install and answer the"
warn "Secure Boot question, or disable Secure Boot first."
else
log "Installing the NVIDIA driver"
if sudo dnf install -y \
--repo=fedora --repo=updates \
--repo=rpmfusion-free --repo=rpmfusion-free-updates \
--repo=rpmfusion-nonfree --repo=rpmfusion-nonfree-updates \
--from-repo=rpmfusion-nonfree,rpmfusion-nonfree-updates \
akmod-nvidia xorg-x11-drv-nvidia-cuda; then
# nouveau has to be out of the way before the kernel would otherwise
# bind it, which is why these are kernel arguments and not a modprobe
# drop-in. modeset=1 is what makes the Wayland session work at all.
if command -v grubby >/dev/null 2>&1; then
sudo grubby --update-kernel=ALL \
--args="rd.driver.blacklist=nouveau modprobe.blacklist=nouveau nvidia-drm.modeset=1"
log "Kernel arguments set for every installed kernel"
else
warn "grubby is not installed; nouveau was not blacklisted"
fi
# Suspend and resume are where a proprietary driver most visibly fails.
# These units save and restore VRAM across it.
sudo systemctl enable nvidia-hibernate.service nvidia-suspend.service \
nvidia-resume.service nvidia-powerd.service
log "NVIDIA power management services enabled"
else
warn "The NVIDIA driver did not install; skipping its kernel arguments and services"
fi
fi
fi
# ── Secure Boot ──────────────────────────────────────────────────────────────
#
# akmods signs the modules it builds with a key it generates on installation.
# Under Secure Boot that key means nothing until it is enrolled, and enrollment
# is deliberately a thing only somebody at the physical machine can complete:
# the request is queued here, and the next boot shows a blue screen asking for
# the password before it will trust the key.
#
# The password itself never reaches this stage. The interview hashed it and
# recorded the hash, so nothing readable is passed on a command line or left in
# this process's environment -- which is the whole reason mokutil has
# --generate-hash and --hash-file.
mok_hash="${PANAMA_MOK_HASH:-}"
if [[ -n "$mok_hash" ]]; then
# Overridable so the contract can exercise this against a certificate it is
# allowed to create. Nothing else sets it.
cert="${PANAMA_MOK_CERT:-/etc/pki/akmods/certs/public_key.der}"
if [[ ! -r "$cert" ]]; then
warn "No akmods certificate at $cert, so there is no key to enroll"
elif mokutil --test-key "$cert" 2>/dev/null | grep -q 'already enrolled'; then
log "The akmods key is already enrolled"
else
hash_file="$(mktemp -t panama-mok.XXXXXX)"
chmod 600 "$hash_file"
printf '%s\n' "$mok_hash" >"$hash_file"
if sudo mokutil --import "$cert" --hash-file "$hash_file"; then
log "Key enrollment requested"
log "At the next boot, choose 'Enroll MOK' and enter the password you gave the installer"
else
warn "Key enrollment failed; the NVIDIA module will not load until it is enrolled"
fi
rm -f "$hash_file"
fi
fi
# ── Fedora's preinstalled extras ─────────────────────────────────────────────
if [[ "${PANAMA_DEBLOAT:-no}" == yes ]]; then
present=()
for package in "${DEBLOAT[@]}"; do
rpm -q "$package" >/dev/null 2>&1 && present+=("$package")
done
if (( ${#present[@]} > 0 )); then
log "Removing ${present[*]}"
sudo dnf remove -y "${present[@]}" >/dev/null \
|| warn "Some packages could not be removed"
else
log "None of Fedora's extras are installed"
fi
fi
# ── Firmware ─────────────────────────────────────────────────────────────────
#
# Panama's Updates page covers this from then on; the installer covers the
# first run. --no-reboot-check because deciding to reboot is not this stage's
# call to make in the middle of an unattended install.
if [[ "${PANAMA_FIRMWARE:-no}" == yes ]]; then
if command -v fwupdmgr >/dev/null 2>&1; then
log "Refreshing firmware metadata"
sudo fwupdmgr refresh --force >/dev/null 2>&1
log "Applying firmware updates"
sudo fwupdmgr update -y --no-reboot-check \
|| log "No firmware updates were applied"
else
warn "fwupdmgr is not installed; skipping firmware"
fi
fi