[Tutorial] Add a systemd-boot loader Menu entry for a Windows installation using a separate ESP Partition

here’s a tutorial on how to get a Windows-entry in systemd-boot in case you use separate ESP partitions for Windows and Linux, with help taken from the Arch Systemd Boot Wiki entry.
It is recommended that Fast Boot is disabled in UEFI as it can lead to several issues especially when dual booting - at least for me, it has no influence on boot time anyway.

  1. Install edk2-shell:
    sudo pacman -S edk2-shell

  2. copy that to your esp-partition:
    sudo cp /usr/share/edk2-shell/x64/Shell.efi ESP/shellx64.efi (replace ESP with the path to your esp, usually /efi)

  3. Retrieve the PARTUUID for your Windows-esp Partition - for example the KDE Partition Manager shows it as UUID in the Partition Details, but you can also do in command line:
    sudo blkid | grep vfat
    Usually, the Windows EFI Partiton is labelled “EFI system partition”, you should get a line that looks like that:
    /dev/nvme1n1p2: UUID="52CC-E135" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="EFI system partition" PARTUUID="e2cc5bf0-9654-4ba3-bdc7-cdb1c2db2c3b"

You will want to remember/take a picture/write down the PARTUUID - depending on how many partitions you have in your system, you will see a lot of such strings in the following steps - check sudo blkid for how many you will see.

  1. The next part gets a lot easier if you set your systemd-boot resolution to max with adding
    console-mode max to your esp/loader/loader.conf but this step is optional. (Adding that will also enable the OEM boot logo for Windows)

  2. Reboot and choose EFI shell in the loader screen which gets autocreated if you did step 2 correct.
    It should now display a list of FS Aliases followed by that FS Alias’ partition details. If it does not, enter the command map
    You can scroll with Page Up / Down if it does not fit on your screen (it should when setting the console-mode max Parameter)

  3. Take note of the FS Alias that contains the PARTUUID you got in step 3. For me the Alias was HD2c but it can also look like HD0a66666a2:
    Enter the exit command and boot into your Linux installation again.

  4. Create a file in your esp - Partition (usually /efi) called windows.nsh and give it the following content:

HD2c:EFI\Microsoft\Boot\Bootmgfw.efi

where HD2c is the exact FS Alias you got from the EFI shell in step 6 and the part after the : is the exact path in your Windows esp Partition (in most cases the path should be exactly like in my example)

  1. Now create a loader entry for that Windows Installation by creating a file windows.conf in your esp/loader/entries directory (usually /efi/loader/entries/) containing the following:
title  Windows
efi     /shellx64.efi
options -nointerrupt -noconsolein -noconsoleout windows.nsh

The name of the .nsh file in the entry has to match the one you created in step 7, and the shellx64.efi also has to be in your esp and at that location where you created it in step 2.

Congratulations, you now have a working entry for your Windows - Installation on a different ESP partition in your Loader Menu.

Extra: If you don’t want to have the Shell - entry in your Loader Menu permanently, you can easily move it into a subfolder after doing all the steps above (for example tools) and modify the windows.conf file accordingly (for example to read

title  Windows
efi     /tools/shellx64.efi
options -nointerrupt -noconsolein -noconsoleout windows.nsh
12 Likes

I just followed this tutorial and everything worked as expected.
Thanks again.

2 Likes

IIRC if you copy /EFI/Microsoft from Windows ESP to systemd-boot ESP, systemd-boot will automatically add an entry for Windows.

and when your Windows updates it’s ESP partition, it might no longer boot, because the actually used, copied stuff doesn’t get updated.

Do you know how often that happens?

not sure but I guess everytime it happens there are posts like “windows update removed my Linux”. Just copying essential stuff instead of telling the system where to find that stuff looks broken by design - it temporarily works but its flawed.

I was trying to resolve this on my own, but stumbled upon this thread. My second drive (the one with Windows) doesn’t appear in the ekd2-shell after I reboot or even shutdown Linux. The only way it appears is if I load into motherboard’s firmware. As a result I can’t boot into Windows, unless I do it through firmware or the boot menu. SATA mode is set to AHCI and hot-swap is disabled. Even the external usb HDD appears there.

that sounds to me like your Windows was not installed as UEFI/GPT but still in MBR/BIOS mode. In that case, you won’t be able to boot it from systemd-boot.

sudo fdisk -l should show you if a disk is in MBR or GPT mode.

Edit: Apparently, those are the ways to check from within Windows if it is using UEFI or BIOS/Legacy booting:

It definitely is in UEFI/GPT mode. Here’s fdisk output:

Disk /dev/sda: 1,82 TiB, 2000398934016 bytes, 3907029168 sectors
Disk model: Samsung SSD 870
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 661CD2DF-C21E-11ED-9B74-BC091B0E1FAE

Device       Start        End    Sectors  Size Type
/dev/sda1     2048    1282055    1280008  625M Windows recovery environment
/dev/sda2  1282056    1486863     204808  100M EFI System
/dev/sda3  1486864    1519639      32776   16M Microsoft reserved
/dev/sda4  1519640 3516328007 3514808368  1,6T Microsoft basic data


Disk /dev/nvme0n1: 1,82 TiB, 2000398934016 bytes, 3907029168 sectors
Disk model: Samsung SSD 990 PRO 2TB
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: C7671EF2-DF6D-E248-9EEF-773423C8E173

Device              Start        End    Sectors  Size Type
/dev/nvme0n1p1       4096    2052095    2048000 1000M EFI System
/dev/nvme0n1p2    2052096 3764110999 3762058904  1,8T Linux filesystem
/dev/nvme0n1p3 3764111000 3907024064  142913065 68,1G Linux swap

And a msinfo32 screenshot.
msinfo32

Interesting, it was just automatically there for me, and both OS’s are on separate drives.

There is an automatically generated systemd-boot Windows 11 entry for me as well, but it didn’t work, so I tried to add it manually and encountered this issue.

May I ask what laptop/PC/motherboard you are using? I have a similarly weird issue.
When I first replaced Windows on my Acer ES1-132-C0VW netbook I had the problem that there was no bootable device found. So, I didn’t even reach the bootloader. The eMMC was simply not listed in the UEFI settings as bootable device, but somehow there was still an entry for the Windows Boot Manager, even though it was not in place anymore. All I got when trying to boot this “Windows Boot Manager” entry was the message “No bootable device”.

After some trial and error I found the solution: cp /boot/EFI/systemd/systemd-bootx64.efi /boot/EFI/Microsoft/Boot/bootmgfw.efi
So, whatever distribution, whatever bootloader I use, this works (of course just with the correct path to the linux-bootloader .efi-file). As soon as the linux-bootloader .efi-file is disguised as Microsoft bootmgfw.efi everything is working.

In my UEFI settings I boot “Microsoft Boot Manager”

It’s a problem I have never seen on other devices.

I also have a pacman-hook that copies the .efi-file after every bootloader update.

The system seems to have no issues with this “solution”. Never had problems with it…

To be fair, I haven’t tried to use it yet. So it’ll be interesting to see if it actually works.

I’m on desktop AM5 Asus X670E-E motherboard. My system worked fine with only Windows, and I am still able to boot into Windows with the Linux installed. It’s just that I have to enter and exit BIOS or enter the firmware boot menu to successfully boot into Windows.

I have solved the issue by disabling Fast Boot in motherboard’s firmware. I guess I just glossed over it, because I’ve only seen mentions of disabling Windows’ fast startup and Secure Boot. Sorry for bothering.

2 Likes

Thanks, I will add a hint to check if fastboot is disabled. It is recommended in several guides, but one might still miss that.

Hello, I’m having trouble configuring all this :confused:

Here’s my filetree

$ pwd
/efi
$ tree 
.
├── c3776b42e6164574afe11ae61f984758
│   └── 6.2.11-arch1-1
│       ├── initrd
│       ├── initrd-fallback
│       └── linux
├── EFI
│   ├── BOOT
│   │   └── BOOTX64.EFI
│   ├── Linux
│   ├── shellx64.efi
│   ├── systemd
│   │   └── systemd-bootx64.efi
│   └── windows.nsh
├── loader
│   ├── entries
│   │   ├── c3776b42e6164574afe11ae61f984758-6.2.11-arch1-1.conf
│   │   ├── c3776b42e6164574afe11ae61f984758-6.2.11-arch1-1-fallback.conf
│   │   └── windows.conf
│   ├── entries.srel
│   ├── loader.conf
│   └── random-seed
└── System Volume Information

Here’s my /efi/loader/entries/windows.conf

GNU nano 7.2                                     
windows.conf                                               
title  Windows
efi    /EFI/shellx64.efi
options -nointerrupt -noconsolein -noconsoleout /EFI/windows.nsh

Here’s my windows.nsh

HD1b:EFI\Microsoft\Boot\Bootmgfw.efi

I don’t know, most likely I entered something wrong in the newly created files or I entered the Alias wrong. Here’s EFI shell

The correct one is: FS1

When starting the system, the option to select Windows appears, but after selecting it, nothing happens

What am I doing wrong?

that should look like that:

title  Windows
efi    /shellx64.efi
options -nointerrupt -noconsolein -noconsoleout windows.nsh

the rest should be correct.

2 Likes

Awesome tutorial, just got it configured and everything works great! One question though. Will this entry continue to work after a Windows update?

yes, it should, as long as MS does not decide to change their EFI partition structure.

1 Like