Dual boot NixOS with Windows. Careful and convenient handling of EFI

This commit is contained in:
Brian Lee 2025-01-31 09:45:44 -08:00
parent ebc3fda1d3
commit be16241253
8 changed files with 315 additions and 18 deletions

View File

@ -332,9 +332,14 @@ in
}; };
sunshine.enable = true; sunshine.enable = true;
# sunshine.capSysAdmin = true;
displayManager = { displayManager = {
sddm.enable = true; sddm.enable = true;
#defaultSession = "plasmawayland"; #defaultSession = "plasmawayland";
autoLogin = {
enable = true;
user = "blee";
};
}; };
xserver = { xserver = {
enable = true; enable = true;
@ -351,13 +356,26 @@ in
systemd = { systemd = {
services = { services = {
playground = { # BUG: unable to attach to the tmux session. LLMs can't figure out why 1/9/2025
# playground = {
# wantedBy = [ "multi-user.target" ];
# after = [ "network.target" ];
# serviceConfig = {
# Type = "forking";
# User = "blee";
# WorkingDirectory = "/opt/playground";
# Environment = "NIX_PATH=nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels";
# };
# script = "${pkgs.nix}/bin/nix-shell";
# };
ollama = {
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
after = [ "network.target" ]; after = [ "network.target" ];
serviceConfig = { serviceConfig = {
Type = "forking"; Type = "forking";
User = "blee"; User = "blee";
WorkingDirectory = "/opt/playground"; WorkingDirectory = "/opt/ollama";
Environment = "NIX_PATH=nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels"; Environment = "NIX_PATH=nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels";
}; };
script = "${pkgs.nix}/bin/nix-shell"; script = "${pkgs.nix}/bin/nix-shell";
@ -399,5 +417,5 @@ in
}; };
}; };
system.stateVersion = "23.11"; system.stateVersion = "24.11";
} }

View File

@ -29,51 +29,51 @@ function FORMAT_DISK ()
nixos-generate-config --root /mnt nixos-generate-config --root /mnt
} }
ping -c1 ${TARGET} 2>&1 > /dev/null || (echo "Target not found. Exiting." && exit 1) ping -c1 $TARGET 2>&1 > /dev/null || (echo "Target not found. Exiting." && exit 1)
if ! arp -n | grep $TARGET_MAC; then if ! arp -n | grep $TARGET_MAC; then
echo "Target not found in ARP table. Exiting." echo "Target not found in ARP table. Exiting."
exit 1 exit 1
fi fi
echo "Install NixOS on ${TARGET}? You must set a password on the target before running this." echo "Install NixOS on $TARGET? You must set a password on the target before running this."
echo "Press enter to continue or ctrl+c to quit." echo "Press enter to continue or ctrl+c to quit."
read read
ssh-keygen -R ${TARGET} ssh-keygen -R $TARGET
ssh-copy-id nixos@${TARGET} ssh-copy-id nixos@$TARGET
COMMANDS=" COMMANDS="
sudo cp -r /home/nixos/.ssh /root/.; sudo cp -r /home/nixos/.ssh /root/.;
sudo chown -R root:root /root/.ssh; sudo chown -R root:root /root/.ssh;
" "
ssh -t nixos@${TARGET} "${COMMANDS}" ssh -t nixos@$TARGET "${COMMANDS}"
ssh root@${TARGET} "$(typeset -f FORMAT_DISK); FORMAT_DISK" ssh root@$TARGET "$(typeset -f FORMAT_DISK); FORMAT_DISK"
scp configuration.nix root@${TARGET}:/mnt/etc/nixos/ scp configuration.nix root@$TARGET:/mnt/etc/nixos/
# copy authorized keys to both the target and the target's chroot, because nixos-install runs outside the chroot # copy authorized keys to both the target and the target's chroot, because nixos-install runs outside the chroot
ssh root@${TARGET} mkdir -p /etc/nixos/ssh /mnt/etc/nixos/ssh ssh root@$TARGET mkdir -p /etc/nixos/ssh /mnt/etc/nixos/ssh
if [ -f ~/.ssh/ansible_root_keys ]; then if [ -f ~/.ssh/ansible_root_keys ]; then
scp ~/.ssh/ansible_root_keys root@$TARGET:/mnt/etc/nixos/ssh/authorized_keys scp ~/.ssh/ansible_root_keys root@$TARGET:/mnt/etc/nixos/ssh/authorized_keys
scp ~/.ssh/ansible_root_keys root@$TARGET:/etc/nixos/ssh/authorized_keys scp ~/.ssh/ansible_root_keys root@$TARGET:/etc/nixos/ssh/authorized_keys
scp ~/.ssh/ansible_timburr_keys root@$TARGET:/mnt/etc/nixos/ssh/authorized_timburr_keys scp ~/.ssh/ansible_timburr_keys root@$TARGET:/mnt/etc/nixos/ssh/authorized_timburr_keys
scp ~/.ssh/ansible_timburr_keys root@$TARGET:/etc/nixos/ssh/authorized_timburr_keys scp ~/.ssh/ansible_timburr_keys root@$TARGET:/etc/nixos/ssh/authorized_timburr_keys
else else
scp ~/.ssh/authorized_keys root@${TARGET}:/etc/nixos/ssh/authorized_keys scp ~/.ssh/authorized_keys root@$TARGET:/etc/nixos/ssh/authorized_keys
scp ~/.ssh/authorized_keys root@${TARGET}:/mnt/etc/nixos/ssh/authorized_keys scp ~/.ssh/authorized_keys root@$TARGET:/mnt/etc/nixos/ssh/authorized_keys
fi fi
echo "Press [Enter] to run nixos-install on the target, or press ctrl+c to stop and do it manually." echo "Press [Enter] to run nixos-install on the target, or press ctrl+c to stop and do it manually."
read read
ssh root@${TARGET} nixos-install ssh root@$TARGET nixos-install
#ssh root@${TARGET} openssl dhparam -out /etc/ssl/dhparams.pem 3072 #ssh root@$TARGET openssl dhparam -out /etc/ssl/dhparams.pem 3072
ssh-keygen -R ${TARGET} ssh-keygen -R $TARGET
echo "Done." echo "Done."
echo echo
echo "You should set a password before restarting in case networking doesn't come up on first boot. To chroot run this:" echo "You should set a password before restarting in case networking doesn't come up on first boot. To chroot run this:"
echo "nixos-enter --root /mnt" echo "nixos-enter --root /mnt"
echo "passwd" echo "passwd"
ssh-keygen -R ${TARGET} ssh-keygen -R $TARGET

View File

@ -0,0 +1,111 @@
# EFI notes
## Dual Boot
We can use the EFI `NextBoot` feature to signal that the system should boot the 2nd OS.
For initial setup, find the 2nd OS boot entry:
```sh
PS C:\Users\pleb\Documents\windows-scripts> PowerShell.exe -ExecutionPolicy Bypass -File .\List-BootEntries.ps1
Found Boot0000
Found Boot0002
```
Hard-code the boot entry in the `Set-BootNext.ps1` script:
```powershell
# NixOS boot entry ID
$NixOSBootEntryID = "0002"
```
Then whenever you want to run NixOS, run the script:
```sh
PowerShell.exe -ExecutionPolicy Bypass -File \Users\pleb\Documents\windows-scripts\Set-BootNext.ps1
```
## EFI Recovery
Sometimes Windows Update will trample the EFI boot entry for NixOS.
## Linux
* Boot into a NixOS live environment (USB or CD).
* Mount your NixOS root partition and the ESP (EFI System Partition):
```sh
mount /dev/nvme0n1p4 /mnt
mount /dev/nvme0n1p1 /mnt/boot
```
* Chroot into your NixOS installation:
```sh
nixos-enter --root /mnt
```
* Manually reinstall the bootloader:
```sh
bootctl install --path /boot
```
* Exit the chroot environment:
```sh
exit
```
* Unmount the partitions:
```sh
umount /mnt/boot
umount /mnt
```
8. Ensure the boot order is to your satisfaction
```sh
efibootmgr -v
efibootmgr -o XXXX,YYYY
```
9. Reboot your system.
By following these steps, you should be able to recover your NixOS boot option if it disappears after a Windows update.
## Windows
In the unlikely scenario that the Windows boot entry is removed, here's how to recover from that:
* Mount the EFI System Partition if it's not already mounted:
```sh
mount /dev/nvme0n1p1 /mnt/boot
```
(Replace nvme0n1p1 with the correct partition if different)
* Verify that the Windows bootloader files exist:
```sh
ls /mnt/boot/EFI/Microsoft/Boot/
```
You should see a file named `bootmgfw.efi`.
* If the files are present, you can create a new boot entry for Windows using `efibootmgr`:
```sh
efibootmgr -c -d /dev/nvme0n1 -p 1 -L "Windows" -l '\EFI\Microsoft\Boot\bootmgfw.efi'
```
This command creates a new boot entry labeled "Windows" that points to the Windows bootloader file.
* Verify that the new entry was created:
```sh
efibootmgr -v
```

View File

@ -0,0 +1,56 @@
# Dual booting Windows 11
- Before the install drop files onto the flash drive
- autologin.reg
```toml
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon]
"DefaultUserName"="pleb"
"DefaultPassword"="PASSWORD_HERE"
"AutoAdminLogon"="1"
```
- setup ssh
```powershell
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
Start-Service sshd
Set-Service -Name sshd -StartupType 'Automatic'
Get-Service -Name sshd | Select-Object -Property Name, DisplayName, Status, StartType
```
- ssh network access
- The default firewall policy is the in "private" group
- Settings > Network & internet > select Private Network
- ssh access
```powershell
scp ~/.ssh/ansible_timburr_keys winroar:/ProgramData/ssh/administrators_authorized_keys
ssh winroar
powershell
Get-Content -Path $env:ProgramData\ssh\administrators_authorized_keys
icacls.exe ""$env:ProgramData\ssh\administrators_authorized_keys"" /inheritance:r /grant ""Administrators:F"" /grant ""SYSTEM:F""
```
- Once it asks you to connect to a network (which it does even if you're on one)
- avoid creating a microsoft account
- unplug the network
- When you reach the network connection screen, press Shift + F10 to open Command Prompt.
- Type `oobe\bypassnro` and press Enter.
- The system will restart, and you should be able to create a local account without a network connection
- It forces you to enter security questions
- After hitting next its safe to restore the network (although the NIC driver is missing anyway, until updates get installed)
- First install steps to get out of Windows
- connect to 2GHz wifi because win11 installer doesn't have the NIC driver for #incineroar and out-of-box wireless driver can't negotiate with the 5GHz network
- Install sunshine `winget install lizardbyte.sunshine`
- open edge using incognito mode and go to sunshine: `localhost:47990`
- set sunshine credentials and then pair it to moonlight on litten
- go to taskbar settings and disable everything
- Setup autologin
- Run `netplwiz`
- The machine can now be operated headlessly, continue setup from a more comfortable Linux environment
## Windows Scripts
A script to minimize dark patterns can be copied via the following copypasta:
```sh
scp -r ~/ops/brenise.dev/nix/incineroar.brenise.dev/windows-scripts winroar:Documents/
```

View File

@ -57,7 +57,7 @@ if ! tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
# Start ollama in a new window # Start ollama in a new window
tmux new-window -t "$SESSION_NAME" -n "ollama" tmux new-window -t "$SESSION_NAME" -n "ollama"
tmux send-keys -t "$SESSION_NAME":1 "cd /opt/ollama && ./bin/ollama serve" C-m tmux send-keys -t "$SESSION_NAME":1 "cd /opt/ollama && source .env && ./bin/ollama serve" C-m
# Prepare the ComfyUI launch command # Prepare the ComfyUI launch command
COMFYUI_BASE_COMMAND="python main.py --port $COMFYUI_PORT --listen 127.0.0.1" COMFYUI_BASE_COMMAND="python main.py --port $COMFYUI_PORT --listen 127.0.0.1"

View File

@ -0,0 +1,48 @@
# Requires running PowerShell as Administrator
# GUID for the EFI global variable
$EFI_GLOBAL_GUID = "{8BE4DF61-93CA-11D2-AA0D-00E098032B8C}"
# Define the GetFirmwareEnvironmentVariable function
$GetFirmwareEnvironmentVariable = @"
using System;
using System.Runtime.InteropServices;
public class UEFI
{
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern uint GetFirmwareEnvironmentVariable(
string lpName,
string lpGuid,
byte[] pBuffer,
uint nSize
);
}
"@
Add-Type -TypeDefinition $GetFirmwareEnvironmentVariable
# Function to read a boot entry
function Read-BootEntry {
param([string]$BootEntryID)
$buffer = New-Object byte[] 4096 # Adjust size if needed
$size = [UEFI]::GetFirmwareEnvironmentVariable("Boot$BootEntryID", $EFI_GLOBAL_GUID, $buffer, $buffer.Length)
if ($size -ne 0) {
$data = $buffer[0..($size - 1)]
[string]::Join("", ($data | ForEach-Object { "{0:X2}" -f $_ }))
} else {
$null
}
}
# Enumerate boot entries from 0000 to FFFF
for ($i = 0; $i -le 0xFFFF; $i++) {
$id = "{0:X4}" -f $i
$entry = Read-BootEntry $id
if ($entry) {
Write-Host "Found Boot$id"
}
}
# Note: Parsing the boot entry data to extract the description requires more complex parsing

View File

@ -0,0 +1,47 @@
# Requires running PowerShell as Administrator
# Define the GUID for the EFI global variable
$EFI_GLOBAL_GUID = "{8BE4DF61-93CA-11D2-AA0D-00E098032B8C}"
# NixOS boot entry ID
$NixOSBootEntryID = "0002"
# Convert the Boot Entry ID to a byte array
$BootNextValue = [UInt16]::Parse($NixOSBootEntryID, "AllowHexSpecifier")
$BootNextBytes = [BitConverter]::GetBytes($BootNextValue)
# Define the SetFirmwareEnvironmentVariable function
$SetFirmwareEnvironmentVariable = @"
using System;
using System.Runtime.InteropServices;
public class UEFI
{
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool SetFirmwareEnvironmentVariable(
string lpName,
string lpGuid,
byte[] pValue,
int nSize
);
}
"@
Add-Type -TypeDefinition $SetFirmwareEnvironmentVariable
# Set the BootNext variable
$success = [UEFI]::SetFirmwareEnvironmentVariable(
"BootNext",
$EFI_GLOBAL_GUID,
$BootNextBytes,
$BootNextBytes.Length
)
if ($success) {
Write-Host "BootNext variable set successfully to Boot$NixOSBootEntryID."
# reboot to nixos
Write-Host "Rebooting to NixOS..."
Restart-Computer -Force
} else {
$errorCode = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Host "Failed to set BootNext variable. Error code: $errorCode"
}

View File

@ -0,0 +1,17 @@
#Requires AutoHotkey v2.0
; Define the function that checks for specific processes and minimizes a window
CheckAndMinimize() {
; Check if either "BSManager.exe" or "vrmonitor.exe" processes are running
if ProcessExist("BSManager.exe") or ProcessExist("vrmonitor.exe") {
; Check if the Oculus Client window exists
oculusClient := WinExist("ahk_exe OculusClient.exe")
; If the Oculus Client window exists, minimize it
if oculusClient
WinMinimize("ahk_id " . oculusClient)
}
}
; Set a timer to call the CheckAndMinimize function every 5000 milliseconds (5 seconds)
SetTimer(CheckAndMinimize, 5000)