Dual boot NixOS with Windows. Careful and convenient handling of EFI
This commit is contained in:
parent
ebc3fda1d3
commit
be16241253
@ -332,9 +332,14 @@ in
|
||||
};
|
||||
|
||||
sunshine.enable = true;
|
||||
# sunshine.capSysAdmin = true;
|
||||
displayManager = {
|
||||
sddm.enable = true;
|
||||
#defaultSession = "plasmawayland";
|
||||
autoLogin = {
|
||||
enable = true;
|
||||
user = "blee";
|
||||
};
|
||||
};
|
||||
xserver = {
|
||||
enable = true;
|
||||
@ -351,13 +356,26 @@ in
|
||||
systemd = {
|
||||
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" ];
|
||||
after = [ "network.target" ];
|
||||
serviceConfig = {
|
||||
Type = "forking";
|
||||
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";
|
||||
};
|
||||
script = "${pkgs.nix}/bin/nix-shell";
|
||||
@ -399,5 +417,5 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
system.stateVersion = "23.11";
|
||||
system.stateVersion = "24.11";
|
||||
}
|
||||
|
@ -29,51 +29,51 @@ function FORMAT_DISK ()
|
||||
|
||||
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
|
||||
echo "Target not found in ARP table. Exiting."
|
||||
exit 1
|
||||
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."
|
||||
read
|
||||
|
||||
ssh-keygen -R ${TARGET}
|
||||
ssh-copy-id nixos@${TARGET}
|
||||
ssh-keygen -R $TARGET
|
||||
ssh-copy-id nixos@$TARGET
|
||||
|
||||
COMMANDS="
|
||||
sudo cp -r /home/nixos/.ssh /root/.;
|
||||
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
|
||||
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
|
||||
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_timburr_keys root@$TARGET:/mnt/etc/nixos/ssh/authorized_timburr_keys
|
||||
scp ~/.ssh/ansible_timburr_keys root@$TARGET:/etc/nixos/ssh/authorized_timburr_keys
|
||||
else
|
||||
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:/etc/nixos/ssh/authorized_keys
|
||||
scp ~/.ssh/authorized_keys root@$TARGET:/mnt/etc/nixos/ssh/authorized_keys
|
||||
fi
|
||||
|
||||
echo "Press [Enter] to run nixos-install on the target, or press ctrl+c to stop and do it manually."
|
||||
read
|
||||
ssh root@${TARGET} nixos-install
|
||||
#ssh root@${TARGET} openssl dhparam -out /etc/ssl/dhparams.pem 3072
|
||||
ssh root@$TARGET nixos-install
|
||||
#ssh root@$TARGET openssl dhparam -out /etc/ssl/dhparams.pem 3072
|
||||
|
||||
ssh-keygen -R ${TARGET}
|
||||
ssh-keygen -R $TARGET
|
||||
echo "Done."
|
||||
echo
|
||||
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 "passwd"
|
||||
|
||||
ssh-keygen -R ${TARGET}
|
||||
ssh-keygen -R $TARGET
|
111
incineroar.brenise.dev/notes/efi.md
Normal file
111
incineroar.brenise.dev/notes/efi.md
Normal 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
|
||||
```
|
56
incineroar.brenise.dev/notes/installing-windows.md
Normal file
56
incineroar.brenise.dev/notes/installing-windows.md
Normal 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/
|
||||
```
|
@ -57,7 +57,7 @@ if ! tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
|
||||
|
||||
# Start ollama in a new window
|
||||
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
|
||||
COMFYUI_BASE_COMMAND="python main.py --port $COMFYUI_PORT --listen 127.0.0.1"
|
||||
|
48
incineroar.brenise.dev/windows-scripts/List-BootEntries.ps1
Normal file
48
incineroar.brenise.dev/windows-scripts/List-BootEntries.ps1
Normal 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
|
47
incineroar.brenise.dev/windows-scripts/Set-BootNext.ps1
Normal file
47
incineroar.brenise.dev/windows-scripts/Set-BootNext.ps1
Normal 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"
|
||||
}
|
@ -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)
|
Loading…
x
Reference in New Issue
Block a user