From 54fc9e362bb3319539f9e4295efbd72db2ea5e55 Mon Sep 17 00:00:00 2001 From: Rick Peters Date: Thu, 24 Sep 2026 10:40:16 +0200 Subject: [PATCH] feat: Menu comes back after each run, BIOS dry run, version 0.8.0 - After a run the menu returns; [m] back to the menu or [r] restart now when a restart is needed; q quits. - A staged BIOS update greys the item out until the restart. - WIZARD_BIOS_DRY_RUN=1: whole BIOS flow without the device check, never flashes. - VERSION 0.8.0; CHANGELOG: BIOS work moved to 0.8.0; README: BIOS update step by step. --- AGENTS.md | 7 ++- CHANGELOG.md | 44 ++++++++++----- README.md | 52 ++++++++++++----- lib/bios.sh | 29 ++++++++-- setup-gamescope-boot.sh | 122 ++++++++++++++++++++++++++++------------ 5 files changed, 182 insertions(+), 72 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 6676adb..3094a4a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -143,7 +143,12 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine Keep both confirmations (y/N, then typing `UPDATE`) and the warnings; the firmware comes from the newest `holo-X.Y` repo (`.files` db names the `.cab`, `.db` gives the SHA-256), and fwupd itself refuses non-Fremont - hardware. It can only be tested up to fwupd's refusal in the VM. + hardware. In the VM, test it with `WIZARD_BIOS_DRY_RUN=1` (skips only the + device check, never flashes) and a faked version (dev-env + `BIOS_VERSION=F7F0107 ./run.sh --fremont`). +- The entry point loops: menu, run, "back to the menu" (or `[m]`/`[r]` when a + restart is needed), until `q`; the restart question is asked once at the + end. Scripted input that runs out ends the loop like `q`. - `Relogin=true` means a gamescope that fails to start is relaunched in a tight loop; keep that in mind when changing session handling. diff --git a/CHANGELOG.md b/CHANGELOG.md index 4731128..8fb73f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,35 @@ All notable changes, per version and per commit. Versions follow `setup-gamescope-boot.sh`. Versions before 0.7.0 were numbered afterwards, one per merged pull request. +## 0.8.0 - 2026-09-24 + +An opt-in BIOS update for the Steam Machine, and a menu that comes back after +every run. Merged together with 0.7.0, so v0.8.0 is the release that has both. + +- `2f9b2a5` **feat: Opt-in BIOS update for the Steam Machine** + - New menu item (Steam Machine only, never ticked by default) showing the + current BIOS version and the newest from Valve's `fremont-hw-support`. + - Two large warnings and two confirmations (y/N, then typing `UPDATE`), + checksum-verified download, installed with fwupd; the wizard then offers + the restart that writes it, with a warning to keep the power on. +- `40c9ee6` **fix: BIOS update only when newer, device check first, aligned warnings** + - The item is greyed out and can't be ticked unless Valve has a newer BIOS. + - Before any warning: SHA-256 of Valve's package, then fwupd confirms the + firmware fits this machine's hardware; otherwise it stops. + - Warning box drawn with a solid red frame whose edges line up, and the + menu's "Now" column aligned. +- `b70c9d6` **fix: shellcheck in the bundle, and the docs for the BIOS checks** + - A variable name in `lib/bios.sh` clashed with `lib/state.sh` in the bundle + (CI's shellcheck would fail); README, AGENTS.md and this changelog updated. +- **feat: Menu comes back after each run, BIOS dry run, version 0.8.0** + - After a run the menu returns with the new state; quit with `q`. When + something needs a restart, choose between back to the menu (`m`) and + restart now (`r`); quitting asks once more. + - A staged BIOS update greys the item out until the restart. + - `WIZARD_BIOS_DRY_RUN=1` walks through the whole BIOS update (download, + checksum, both warnings) but only prints the install and never flashes. + - README: the menu loop and the BIOS update step by step. + ## 0.7.0 - 2026-09-24 The SteamOS theme comes from CachyOS's `cachyos-vapor` package, applied with @@ -56,21 +85,6 @@ Machine LED driver works on every installed kernel and survives kernel updates. - When a new version is released, the `latest` tag and release move to it (bundle replaced), so the older `.../releases/download/latest/...` URL also always gives the newest version. -- `2f9b2a5` **feat: Opt-in BIOS update for the Steam Machine** - - New menu item (Steam Machine only, never ticked by default) showing the - current BIOS version and the newest from Valve's `fremont-hw-support`. - - Two large warnings and two confirmations (y/N, then typing `UPDATE`), - checksum-verified download, installed with fwupd; the wizard then offers - the restart that writes it, with a warning to keep the power on. -- `40c9ee6` **fix: BIOS update only when newer, device check first, aligned warnings** - - The item is greyed out and can't be ticked unless Valve has a newer BIOS. - - Before any warning: SHA-256 of Valve's package, then fwupd confirms the - firmware fits this machine's hardware; otherwise it stops. - - Warning box drawn with a solid red frame whose edges line up, and the - menu's "Now" column aligned. -- **fix: shellcheck in the bundle, and the docs for the BIOS checks** - - A variable name in `lib/bios.sh` clashed with `lib/state.sh` in the bundle - (CI's shellcheck would fail); README, AGENTS.md and this changelog updated. ## 0.6.2 - 2026-09-23 diff --git a/README.md b/README.md index f444c32..aa413e6 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,9 @@ Everything you turn off is put back the way it was. 5. **Steam Machine support** - only shown on a Valve Steam Machine: the driver for the front LED bar, and the hardware settings in Steam (fan, TV control over HDMI-CEC). +6. **Update BIOS** - only on a Steam Machine, opt-in and at your own risk: + installs the newest Steam Machine BIOS from Valve (see + [BIOS updates](#bios-updates-steam-machine)). ## Requirements @@ -74,6 +77,10 @@ it's loaded right now, so a kernel update is easy to check. The wizard then shows what it will change, asks your password once, and at the end offers to restart (needed for changes to how the PC starts). +After each run the menu comes back with the new state, so you can change more +in one go; quit with **q**. When something needs a restart, you choose +between going back to the menu and restarting now; quitting asks once more. + Run it again whenever you like - to change your choices, to turn things off again, or after a CachyOS update (press `a` in the menu to re-apply everything that's on). @@ -283,22 +290,41 @@ cat /var/lib/dkms/leds-valve-dkms/0.1/build/make.log journalctl --user -b | grep -i led ``` -**BIOS updates** (opt-in, never ticked by default). On a Steam Machine the menu -has an **Update BIOS** item that shows the current BIOS version and the newest -one Valve ships (`F7F0108.cab` in its `fremont-hw-support` package, looked up -on Valve's SteamOS mirror). It can only be ticked when Valve has a newer BIOS -than the one installed; otherwise it's greyed out. Before asking anything it -downloads the package and checks it: the SHA-256 from Valve's repository -proves it's Valve's file, and fwupd confirms the firmware is for this very -machine (it compares the firmware's hardware IDs with the device). Only then -it shows a large warning and asks for confirmation, shows the warning again -and continues when you type `UPDATE`; the BIOS is written during the next restart. +### BIOS updates (Steam Machine) + +The **Update BIOS** item is only shown on a Steam Machine and is never ticked +by default. It shows the BIOS version you have now and the newest one Valve +ships (the `.cab` file in its `fremont-hw-support` package, looked up on +Valve's SteamOS mirror): + +``` + [ ] Update BIOS (at your own risk): now F7F0107, newest F7F0108 (opt-in, runs once) +``` + +It can only be ticked when Valve has a newer BIOS than yours; when you're up +to date, when the newest version can't be looked up (offline), or when an +update is already waiting for a restart, it's greyed out. + +When you run it, the wizard: + +1. downloads Valve's package and checks its **SHA-256** against Valve's + repository, so it's exactly Valve's file; +2. asks **fwupd** whether the firmware is for this very machine (fwupd compares + the firmware's hardware IDs with the device) and stops if it isn't; +3. shows a large red **warning** with the current and the new version, and asks + whether you understand the risks (default: no); +4. shows the warning **again** and only continues when you type `UPDATE`; +5. hands the firmware to fwupd, which writes it during the **next restart**: + choose "restart now" or restart later yourself. **At your own risk:** a failed or interrupted BIOS update can leave the machine unable to start. Keep it on mains power, and never turn off the power, unplug -it or press the power button while it updates, including during the restart -afterwards; the screen can stay black for several minutes. fwupd refuses the -file on anything that isn't a Steam Machine. +it or press the power button while it updates, including during the restart; +the screen can stay black for several minutes. + +To walk through it without flashing anything, run the wizard with +`WIZARD_BIOS_DRY_RUN=1`: it downloads and checks the package and shows both +warnings, skips fwupd's device check, and only prints the install command. ### Manual session control diff --git a/lib/bios.sh b/lib/bios.sh index 0fd8075..00c5e4b 100644 --- a/lib/bios.sh +++ b/lib/bios.sh @@ -6,6 +6,10 @@ # Sourced by setup-gamescope-boot.sh; not meant to be run on its own. BIOS_REPO_PREFIX=holo +# WIZARD_BIOS_DRY_RUN=1 walks through the whole BIOS update (download, +# checksum, both warnings) but never flashes: fwupd's device check is +# skipped and the install is only printed. For testing, e.g. in a VM. +BIOS_DRY_RUN="${WIZARD_BIOS_DRY_RUN:-}" bios_available() { detect_valve_fremont; } @@ -45,7 +49,7 @@ bios_lookup_newest() { bios_selectable() { # Only when Valve has a newer BIOS than the one installed; the menu # greys the item out otherwise (up to date, or newest unknown/offline). - [[ -n "${BIOS_NEWEST:-}" && "$BIOS_NEWEST" != "$(bios_current)" ]] + [[ -z "${BIOS_NEEDS_RESTART:-}" && -n "${BIOS_NEWEST:-}" && "$BIOS_NEWEST" != "$(bios_current)" ]] } bios_label() { @@ -53,6 +57,7 @@ bios_label() { local newest="newest unknown (offline?)" [[ -n "${BIOS_NEWEST:-}" ]] && newest="newest $BIOS_NEWEST" [[ "${BIOS_NEWEST:-}" == "$(bios_current)" ]] && newest="up to date" + [[ -n "${BIOS_NEEDS_RESTART:-}" ]] && newest="$BIOS_NEWEST staged, restart to install" echo "Update BIOS (at your own risk): now $(bios_current), $newest" } @@ -114,8 +119,9 @@ bios_enable() { err "Couldn't find the newest Steam Machine BIOS on Valve's mirror ($VALVE_MIRROR)." return 1 fi - local current tmp + local current tmp compatible current="$(bios_current)" + [[ -n "$BIOS_DRY_RUN" ]] && warn "DRY RUN (WIZARD_BIOS_DRY_RUN): nothing will be flashed." if [[ "$current" == "$BIOS_NEWEST" ]]; then ok "The BIOS is already the newest version ($current); nothing to do." return 0 @@ -136,19 +142,24 @@ bios_enable() { return 1 fi ok "Checksum OK: this is Valve's $BIOS_PKG." - if ! bios_fits_device "$tmp/$BIOS_CAB"; then + local yes="$c_green$c_bold" b="$c_bold" n="$c_reset" + if [[ -n "$BIOS_DRY_RUN" ]]; then + warn "Dry run: skipping fwupd's check that the firmware fits this machine." + compatible="${c_yellow}${b}not checked${n} (dry run)" + elif bios_fits_device "$tmp/$BIOS_CAB"; then + ok "fwupd confirms BIOS $BIOS_NEWEST is firmware for this machine." + compatible="${yes}yes${n} (checked by fwupd)" + else err "fwupd says BIOS $BIOS_NEWEST is not for this machine's hardware; not installing it." rm -rf "$tmp" return 1 fi - ok "fwupd confirms BIOS $BIOS_NEWEST is firmware for this machine." - local yes="$c_green$c_bold" b="$c_bold" n="$c_reset" bios_disclaimer "WARNING: BIOS UPDATE - ENTIRELY AT YOUR OWN RISK" \ "Current BIOS: ${b}$current${n}" \ "New BIOS: ${b}$BIOS_NEWEST${n}" \ "Checksum: ${yes}OK${n} (Valve's package)" \ - "Compatible: ${yes}yes${n} (checked by fwupd)" + "Compatible: $compatible" if ! ask_yn "Do you understand the risks and want to continue?" n; then info "BIOS update cancelled; nothing was changed."; rm -rf "$tmp"; return 0 fi @@ -161,6 +172,12 @@ bios_enable() { info "BIOS update cancelled; nothing was changed."; rm -rf "$tmp"; return 0 fi + if [[ -n "$BIOS_DRY_RUN" ]]; then + ok "Dry run: would now run: sudo fwupdmgr install -y --no-reboot-check $BIOS_CAB" + ok "Dry run finished; nothing was flashed and no restart is needed." + rm -rf "$tmp" + return 0 + fi info "Handing BIOS $BIOS_NEWEST to fwupd. Do NOT turn off the power from now on." # -y: we already asked twice; --no-reboot-check: the wizard's own # restart question comes at the end. diff --git a/setup-gamescope-boot.sh b/setup-gamescope-boot.sh index 3c3c202..c12e97a 100755 --- a/setup-gamescope-boot.sh +++ b/setup-gamescope-boot.sh @@ -15,7 +15,7 @@ set -uo pipefail # Release version, see CHANGELOG.md. -VERSION=0.7.0 +VERSION=0.8.0 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -39,41 +39,91 @@ fi # user running the script. TARGET_USER="$(id -un)" -detect_components -run_menu || { info "Nothing changed."; exit 0; } -plan_changes +restart_needed() { [[ -n "${BIOS_NEEDS_RESTART:-}" || "$RESTART_FOR_LOGIN" == true ]]; } -if [[ ${#TO_DISABLE[@]} -eq 0 && ${#TO_ENABLE[@]} -eq 0 ]]; then - ok "Everything is already the way you want it." +restart_now() { + if [[ -n "${BIOS_NEEDS_RESTART:-}" ]]; then + warn "The BIOS update is written during this restart. Keep the power on and don't" + warn "touch the machine until it has fully started again, even if the screen stays black." + fi + info "Restarting..." + sudo reboot exit 0 -fi +} -echo -echo -e "${c_bold}This will:${c_reset}" -for c in "${TO_DISABLE[@]}"; do echo " - turn off: ${LABEL[$c]}"; done -for c in "${TO_ENABLE[@]}"; do - if is_action "$c"; then echo " - run: ${LABEL[$c]%%:*} (checks, then asks twice more)" - elif [[ "${CURRENT[$c]}" == 1 ]]; then echo " - re-apply: ${LABEL[$c]}"; else echo " - turn on: ${LABEL[$c]}"; fi +after_run() { + # After a run: back to the menu, or restart right away when something + # that just ran needs it. Returns 1 when input has ended (scripted runs). + local reply + if ! restart_needed; then + read -rp "Press Enter to go back to the menu... " _ || return 1 + return 0 + fi + if [[ -n "${BIOS_NEEDS_RESTART:-}" ]]; then + warn "The BIOS update is installed at the next restart." + else + info "The login changes take effect after a restart." + fi + while true; do + read -rp "$(echo -e "${c_bold}[m]${c_reset} back to the menu ${c_bold}[r]${c_reset} restart now: ")" reply || return 1 + case "$reply" in + m|M|"") return 0 ;; + r|R) restart_now ;; + *) warn "Type m or r." ;; + esac + done +} + +# Menu loop: after each run the menu comes back with the new state, until +# the user quits; the restart question comes then, once, for everything. +RESTART_FOR_LOGIN=false +SUDO_KEEPALIVE=false +while true; do + detect_components + run_menu || break + plan_changes + + if [[ ${#TO_DISABLE[@]} -eq 0 && ${#TO_ENABLE[@]} -eq 0 ]]; then + ok "Everything is already the way you want it." + read -rp "Press Enter to go back to the menu... " _ || break + continue + fi + + echo + echo -e "${c_bold}This will:${c_reset}" + for c in "${TO_DISABLE[@]}"; do echo " - turn off: ${LABEL[$c]}"; done + for c in "${TO_ENABLE[@]}"; do + if is_action "$c"; then echo " - run: ${LABEL[$c]%%:*} (checks, then asks twice more)" + elif [[ "${CURRENT[$c]}" == 1 ]]; then echo " - re-apply: ${LABEL[$c]}"; else echo " - turn on: ${LABEL[$c]}"; fi + done + ask_yn "Go ahead?" y || { info "Nothing changed."; continue; } + + # Ask for the sudo password once and keep it fresh, instead of prompting + # at random points during the run. + sudo -n true 2>/dev/null || sudo -v || exit 1 + if [[ "$SUDO_KEEPALIVE" == false ]]; then + SUDO_KEEPALIVE=true + while true; do sudo -n true; sleep 50; kill -0 "$$" 2>/dev/null || exit; done 2>/dev/null & + fi + + apply_changes + # Login manager changes only take effect after a restart. + [[ " ${TO_DISABLE[*]} ${TO_ENABLE[*]} " == *" gaming "* || " ${TO_DISABLE[*]} ${TO_ENABLE[*]} " == *" single "* ]] && + RESTART_FOR_LOGIN=true + + echo + detect_components + echo -e "${c_bold}Done. Current state:${c_reset}" + for c in "${COMPONENTS[@]}"; do + component_available "$c" && ! is_action "$c" || continue + if [[ "${CURRENT[$c]}" == 1 ]]; then echo -e " ${c_green}on ${c_reset} ${LABEL[$c]}"; else echo " off ${LABEL[$c]}"; fi + done + if [[ ${#FAILED[@]} -gt 0 ]]; then + warn "These had problems (see above): ${FAILED[*]}" + fi + echo + after_run || break done -ask_yn "Go ahead?" y || { info "Nothing changed."; exit 0; } - -# Ask for the sudo password once and keep it fresh, instead of prompting at -# random points during the run. -sudo -n true 2>/dev/null || sudo -v || exit 1 -while true; do sudo -n true; sleep 50; kill -0 "$$" 2>/dev/null || exit; done 2>/dev/null & - -apply_changes - -echo -detect_components -echo -e "${c_bold}Done. Current state:${c_reset}" -for c in "${COMPONENTS[@]}"; do - component_available "$c" && ! is_action "$c" || continue - if [[ "${CURRENT[$c]}" == 1 ]]; then echo -e " ${c_green}on ${c_reset} ${LABEL[$c]}"; else echo " off ${LABEL[$c]}"; fi -done -if [[ ${#FAILED[@]} -gt 0 ]]; then - warn "These had problems (see above): ${FAILED[*]}" -fi echo # A staged BIOS update is written during the restart. @@ -85,14 +135,12 @@ if [[ -n "${BIOS_NEEDS_RESTART:-}" ]]; then else info "The BIOS update installs at your next restart." fi - exit 0 -fi - -# Login manager changes only take effect after a restart. -if [[ " ${TO_DISABLE[*]} ${TO_ENABLE[*]} " == *" gaming "* || " ${TO_DISABLE[*]} ${TO_ENABLE[*]} " == *" single "* ]]; then +elif [[ "$RESTART_FOR_LOGIN" == true ]]; then if ask_yn "Restart now so the changes take effect?" n; then sudo reboot else info "Restart whenever you're ready." fi +else + info "Bye." fi