2026-08-05 20:23:33 +01:00
|
|
|
{
|
|
|
|
|
self,
|
|
|
|
|
inputs,
|
|
|
|
|
...
|
|
|
|
|
}: {
|
|
|
|
|
flake.nixosModules.shadowConfiguration = {...}: {
|
|
|
|
|
networking = {
|
|
|
|
|
hostName = "shadow";
|
|
|
|
|
};
|
2026-08-06 17:08:30 +01:00
|
|
|
|
|
|
|
|
# Windows lives on the 476G NVMe -- the one with three partitions: 16M
|
|
|
|
|
# "Microsoft reserved" MSR, 476G NTFS (fs-uuid 70E4E841E4E80AE4, the C:
|
|
|
|
|
# volume), 832M WinRE. Identified by layout rather than by nvmeXnY on
|
|
|
|
|
# purpose; see the enumeration note below.
|
|
|
|
|
#
|
|
|
|
|
# That disk has NO EFI System Partition of its own -- its 16M partition is a
|
|
|
|
|
# "Microsoft reserved" MSR with no filesystem. Windows was installed against
|
|
|
|
|
# the ESP that NixOS now owns: the 1G vfat partition fs-uuid 5873-AD05 on the
|
|
|
|
|
# 1.9T NVMe (that disk holds only two partitions, the ESP and the ext4 root
|
|
|
|
|
# deff63fc-1738-4cfc-a79c-06e58c78471a). So both OSes boot from one shared
|
|
|
|
|
# ESP; there is no separate Windows boot drive.
|
|
|
|
|
#
|
|
|
|
|
# The NixOS install reformatted that ESP, which wiped
|
|
|
|
|
# EFI/Microsoft/Boot/{bootmgfw.efi,BCD} and left Windows unbootable. That has
|
|
|
|
|
# since been repaired from WinRE and both files are back in place, so the
|
|
|
|
|
# menuentry below is live. Keep the recovery command to hand in case the ESP
|
|
|
|
|
# is ever reformatted again -- it has to be run from Windows install media or
|
|
|
|
|
# WinRE, because the BCD store cannot be generated from Linux:
|
|
|
|
|
#
|
|
|
|
|
# bcdboot C:\Windows /s S: /f UEFI (S: = the ESP, fs-uuid 5873-AD05)
|
|
|
|
|
#
|
|
|
|
|
# Copying bootmgfw.efi alone is not enough; without BCD it fails 0xc000000f.
|
|
|
|
|
# Note bcdboot also drops a copy of the Windows boot manager at
|
|
|
|
|
# EFI/BOOT/bootx64.efi -- see the fallback-path note in core/boot.nix.
|
|
|
|
|
#
|
|
|
|
|
# Chainloaded explicitly rather than via grub.useOSProber: deterministic,
|
|
|
|
|
# pulls in no extra tooling, and avoids probing every disk on each bootloader
|
|
|
|
|
# install. The `-f` guard makes the entry self-disabling: it renders only
|
|
|
|
|
# while bootmgfw.efi is actually present, so a future ESP reformat degrades
|
|
|
|
|
# to a missing menu item rather than one that boots into an error.
|
|
|
|
|
#
|
|
|
|
|
# fs-uuid is shadow's ESP, matching the "/boot" entry in disks.nix. Searching
|
|
|
|
|
# by UUID rather than (hdX,gptN) matters here: NVMe enumeration is not stable
|
|
|
|
|
# on this board -- /boot has been seen as both nvme0n1p1 and nvme1n1p1. For
|
|
|
|
|
# the same reason, re-derive device names before running anything
|
|
|
|
|
# destructive:
|
|
|
|
|
#
|
|
|
|
|
# lsblk -o NAME,SIZE,FSTYPE,PARTTYPENAME,UUID,MOUNTPOINT
|
|
|
|
|
#
|
|
|
|
|
# Windows' boot files now occupy ~30-60MiB of that shared 1G ESP and Windows
|
|
|
|
|
# Update rewrites them periodically, which is part of why
|
|
|
|
|
# grub.configurationLimit is held at 10 in core/boot.nix.
|
|
|
|
|
#
|
|
|
|
|
# Before dual-booting, turn off Windows Fast Startup. hiberfil.sys is present
|
|
|
|
|
# on the 476G NTFS volume, and Fast Startup plus this host's hibernation
|
|
|
|
|
# setup will corrupt any NTFS volume mounted read-write from both sides.
|
|
|
|
|
#
|
|
|
|
|
# On the GRUB script itself:
|
|
|
|
|
#
|
|
|
|
|
# The outer `search` + `-f` test run at *boot*, while GRUB parses grub.cfg —
|
|
|
|
|
# not at Nix eval time. That is why repairing Windows needed no rebuild: the
|
|
|
|
|
# entry started rendering as soon as bcdboot put bootmgfw.efi back, and it
|
|
|
|
|
# will stop again by itself if the file disappears.
|
|
|
|
|
#
|
|
|
|
|
# The `-n "$esp"` test is a *separate,
|
|
|
|
|
# nested* if rather than `[ -n "$esp" -a -f ... ]`: a failed search leaves
|
|
|
|
|
# $esp unset, and GRUB's `test` builtin evaluates both operands of `-a`
|
|
|
|
|
# rather than short-circuiting, so the combined form would still expand the
|
|
|
|
|
# `-f` to `()/...` and could complain about a bad device on every boot.
|
|
|
|
|
# Nesting guarantees the path test is not reached at all.
|
|
|
|
|
#
|
|
|
|
|
# No `insmod` is needed for the outer search: GRUB's EFI core image already
|
|
|
|
|
# embeds part_gpt/fat/search_fs_uuid, since it must find its own $prefix on
|
|
|
|
|
# the FAT ESP before reading this file. NixOS' own install-grub.pl relies on
|
|
|
|
|
# exactly that for the search it emits. The insmods inside the menuentry are
|
|
|
|
|
# kept only because `chain` genuinely is a separate module.
|
|
|
|
|
#
|
|
|
|
|
# The inner `search` deliberately repeats the outer one rather than reusing
|
|
|
|
|
# `set root=$esp`. It costs one extra disk scan, and only when the entry is
|
|
|
|
|
# actually selected, in exchange for a menuentry that is self-contained and
|
|
|
|
|
# cannot be broken by anything that clobbers $esp in between. This also
|
|
|
|
|
# matches the per-entry search install-grub.pl generates.
|
|
|
|
|
boot.loader.grub.extraEntries = ''
|
|
|
|
|
search --no-floppy --set=esp --fs-uuid 5873-AD05
|
|
|
|
|
if [ -n "$esp" ]; then
|
|
|
|
|
if [ -f ($esp)/EFI/Microsoft/Boot/bootmgfw.efi ]; then
|
|
|
|
|
menuentry "Windows Boot Manager" --class windows --class os {
|
|
|
|
|
insmod part_gpt
|
|
|
|
|
insmod fat
|
|
|
|
|
insmod chain
|
|
|
|
|
search --no-floppy --set=root --fs-uuid 5873-AD05
|
|
|
|
|
chainloader /EFI/Microsoft/Boot/bootmgfw.efi
|
|
|
|
|
}
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
'';
|
2026-08-05 20:23:33 +01:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|