diff --git a/modules/system/core/boot.nix b/modules/system/core/boot.nix index 8b008f1..ef0cfcc 100644 --- a/modules/system/core/boot.nix +++ b/modules/system/core/boot.nix @@ -10,10 +10,67 @@ efi = { canTouchEfiVariables = true; }; + # Pinned explicitly. grub.enable defaults to true, so this host silently + # switched from systemd-boot (used at install time) to GRUB when this + # block was added. NixOS never removes the previous bootloader's files + # or its NVRAM entry, so both lived on the ESP and the firmware kept + # booting the frozen systemd-boot menu. Keep this explicit, and never + # set systemd-boot.enable alongside it. + # + # Note what this does and does not do. Both of these lines are no-ops + # against nixpkgs' defaults (grub.enable is already true, + # systemd-boot.enable already false); their value is that a conflicting + # definition elsewhere now fails loudly instead of silently winning. They + # delete nothing — stale EFI/systemd/, loader/ and the leftover NVRAM + # entry have to be removed by hand on any host installed with + # systemd-boot. Done on shadow; NOT yet verified on t480. + # + # This is stock UEFI GRUB: NixOS installs EFI//grubx64.efi + # (here EFI/NixOS-boot/grubx64.efi) and registers a matching NVRAM entry + # via efibootmgr, because canTouchEfiVariables is true. That NVRAM entry + # is what makes GRUB the default; NixOS does not manage the removable + # fallback path EFI/BOOT/BOOTX64.EFI and we deliberately do not ask it to + # (grub.efiInstallAsRemovable, which would need canTouchEfiVariables=false, + # is only for firmware that ignores NVRAM entries — neither host needs it). + # + # On shadow that fallback path is NOT empty, though: bcdboot left a copy of + # the *Windows* boot manager at EFI/BOOT/bootx64.efi (byte-identical to + # EFI/Microsoft/Boot/bootmgfw.efi). Normal boots are unaffected because the + # firmware follows its NVRAM entry to GRUB. But it does mean that if NVRAM + # is ever cleared — CMOS reset, battery, firmware update — the fallback + # lands on Windows rather than GRUB. Left as-is by choice; recover by + # rerunning `nixos-rebuild boot` to reinstate the NVRAM entry. + systemd-boot.enable = false; grub = { + enable = true; efiSupport = true; device = "nodev"; theme = pkgs.catppuccin-grub; + # Bound the menu so the ESP cannot fill up with old kernels. + # install-grub.pl unlinks any $boot/kernels/* not referenced by a kept + # generation, so this genuinely caps ESP usage rather than just + # trimming the menu. + # + # Sizing, measured on shadow rather than assumed. Generations share + # aggressively in practice: 10 kept generations referenced 1 bzImage + # (13.3MiB) and 3 initrds (~33.4MiB each) for 114MiB total, i.e. ~11MiB + # per generation, not ~47MiB. Only a kernel or initrd change mints a new + # file — and the initrd does change more often than you'd expect, since + # nixpkgs embeds /etc/modprobe.d/nixos.conf inside it, so every kernel + # module parameter edit produces a fresh one. + # + # So the real bound is between ~11MiB and ~47MiB per generation + # depending on churn: 10 generations costs ~115MiB typically and ~470MiB + # worst case, against a 1GiB ESP that also carries grub/x86_64-efi, the + # catppuccin theme, and Windows' own boot files (bootmgfw.efi + BCD, + # ~30-60MiB) since Windows was bcdboot'd onto this same ESP. 10 keeps + # the worst case under half the partition. + # + # Keep this conservative: overflow fails as `copy ... or die` in + # install-grub.pl, which runs *after* the system profile has already + # been switched, so the result is an activated but unbootable + # generation. t480 reaches this module too and its ESP may be smaller. + configurationLimit = 10; }; }; plymouth = {