Dual Booting Arch Linux and Windows with UEFI/Secure Boot

PhDs are hard. It has been tough to find a week to mess around with software, etc, that isn’t research related. I thought I would put out at least one piece of content this year, so here goes.

I have been running both a desktop and a Surface Pro X for a few years now, and see no reason why that won’t continue into the future. If I could get away with not using Windows I would, but essentially the entire point of using a Surface is OneNote which is definitely amazing, and also totally free now (OneNote 2016 standalone download for desktop). Also, plenty of entities in academia and industry use windows environments, so in the interest of daily productivity, it’s not going away. It’s highly unlikely that there will be remotely comparable open source alternatives to OneNote with stylus support unless some entity like Canonical debuts some tablet hardware. Therefore, it seems likely I will be running dual boot setups well into the future. VMs are ok, but have their limitations, regardless of choice of host OS, especially when it comes to support of cutting edge hardware (my desktop has a PCIei NVMe drive, etc). I spent some time this spring attempting to configure a VM box with some old hardware, and can safely say that, for my purposes, dual booting is much less demanding. Other academics and engineers will likely find it useful as well.

I’ve been asked multiple times about my setup, so I am writing this quick little guide for setting up a dual boot Arch Linux and Windows configuration running on UEFI hardware that works with Secure Boot enabled (signed kernels, loader, etc). Pieces of these instructions are sourced from forums/wikis across the web, but none of it is available in one place and most of them leave out crucial steps without elaboration. This guide will make minimum assumptions regarding background knowledge, and should work for distributions other than Arch with regards to the bootloader setup. I will cite where it makes sense to.

I will provide instructions for both single disk (Linux and Windows on same disk) and multi disk (Linux and Windows on different disks) setups. The only significant difference is the bootloader used. Some helpful webpages:

In summary, I am outlining two schemes here that are not completely addressed elsewhere:

using systemd-boot only. A Microsoft signed bootloader and hashing tool, available here enable compatibility with Secure Boot. All loaders sit on the same ESP (EFI partition). This should also give people ideas on what they might have to do with other, more exotic configurations. For a more general overview of loading OSs and kernels in a UEFI system, see this resource written by Rod Smith.

1 Overview

Based on past experience, I prefer to install Linux first and on the first few partitions, and then install Windows afterwards. I mostly do this to have explicit control over the size and order of partitions (see partitioning section below). For single disk PCs, Windows 10 should find your existing EFI partition and add its bootloader and some recovery stuff to /boot/EFI. For multi disk PCs, Windows 10 will be installed to a separate drive and, presumably, have no knowledge of the Linux drive, which will be used as the boot drive. The multi disk configuration has worked for me with a variety of bootloaders (grub2, refind) in the past, but with Windows 10 sitting on an Intel 750 PCIe drive, only refind, and with some tinkering, has worked well.

If Secure Boot functionality is desired, the setup will vary depending on motherboard vendor. If running on Surface Pro hardware, there is no option to enable secure boot out of the box for alternate operating systems (Linux, etc). One must either use Microsoft-prepared loader and hash tool (to generate signatures for kernels), or wipe the keys and certificates that ship with the platform and generate their own (see Creating Keys section of the Secure Boot entry in the Arch wiki). I see no reason why, for personal use, using the Microsoft loader and keygen is undesirable, so that is the method outlined here.

Regarding installation media, I use a live USB of the latest Arch build (see this entry on bootable media) and a bootable USB of Windows 10 which can be created either by an existing Windows 10 installation, or just download the ISO from here. Note that this requires a Windows 10 OS key for full activation. From windows, rufus can be used to create bootable media. There are a plethora of methods to create a live USB of either OS from common Linux distros, from using packages to manual partitioning and file copying.

Regardless of which hardware platform is being used, start by turing Secure Boot OFF. It is not guaranteed that the linux distro live ISO image will have correctly signed loaders. Some firmware interfaces, like on my ASUS X99-A motherboard, allow switching the Secure Boot option to “Other OS”, which presumably allows non-vendor signed kernels/modules to load. However, on my Surface Pro 3, there is no such option. Disabling Secure Boot completely during initial installation will likely minimize headaches. Additionally disable Windows Fast Boot. It is a type of hibernation mode and may lead to headaches when you are jumping around between operating systems.

2 Partitioning

The installation starts with the Linux distro. On the Surface Pro, insert the live USB and boot by holding volume down, then the power button. Most Linux distributions have live ISOs with GUI installers. Arch does not, so if this is your first time with it, keep the Beginner’s Installation guide handy. Start by navigating to your motherboard’s BIOS and turning Secure Boot off. Then, load the live USB.

2.1 Single Disk

Most live ISOs will have gparted available for use. If not, you can make a gparted live USB with this image. The following can be done either in the gparted GUI or from the available linux shell:

$ parted /dev/sdx # on Surface Pro this will be /dev/sda
(parted) mklabel gpt # say Yes to the prompt
(parted) mkpart ESP fat32 1MiB 512MiB
(parted) set 1 boot on
(parted) mkpart primary linux-swap 512MiB 17GiB # I have 16GB DRAM
(parted) mkpart primary ext4 17GiB 40%
(parted) mkpart primary ntfs 40% 60%
(parted) quit

That last entry is not for Windows, it is for a shared NTFS filesystem that both operating systems use (for Dropbox, email, downloads, etc). The Windows section is left unallocated, it will be formatted by the windows installer. Afterwards, format the partitions as needed:

$ mkfs.fat -F32 /dev/sdx1
$ mkswap /dev/sdx2
$ swapon /dev/sdx2
$ mkfs.ext4 /dev/sdx3
$ mkfs.ntfs -f /dev/sdx4
Single Disk Partition Scheme
FIGURE 1 Single disk partition scheme for dual booting in UEFI mode. Works on, for example, Surface Pro line, etc.

2.2 Multi Disk

With a multiple disk environment, I run Arch on one drive, Windows on another and shared storage on yet another drive (or two (or three…)). So partitioning the Arch drive would look like this:

$ parted /dev/sdx
(parted) mklabel gpt # say Yes to the prompt
(parted) mkpart ESP fat32 1MiB 512MiB
(parted) set 1 boot on
(parted) mkpart primary linux-swap 512MiB 17GiB # I have 16GB DRAM
(parted) mkpart primary ext4 17GiB 100%

make sure to format the partitions as shown above.

3 Arch Installation and Bootloader (systemd-boot)

The rest of the installation should proceed exactly as in the Arch Beginner’s Guide until the last few steps or so. Please do everything on that page, except the “Boot Loader” and “Unmount/Reboot” sections, as this is covered here.

After completing the other installation tasks, do not exit the mounted disk’s root shell. If you restart at this point you will have a bad time. The bootloader needs to be installed. Go ahead and enter the following:

$ pacman -S efibootmgr efitools
$ bootctl --path=/boot install

Systemd-boot does not automatically detect Linux kernels, but it does automatically detect the windows loader (bootmgfw.efi, I believe). Custom entries must be made for linux kernels as well as tools like MemTest86+. Simply edit the following files:

$ vi /boot/loader/loader.conf
### file contents
default arch
timeout 4
editor  0

Then, add the boot entry for the linux kernel. You can also install the intel microcode updater and load it before initramfs (recommended)

$ pacman -S intel-ucode
$ blkid
### NOTE THE PARTUUID FOR THE / PARTITION ON YOUR ARCH DRIVE
$ vi /boot/loader/entries/arch.conf
### file contents
title   Arch
linux   /vmlinuz-linux
initrd  /intel-ucode.img
initrd  /initramfs-linux.img
options root=PARTUUID=THE-NUMBER-GIVEN-BY-BLKID rw

The last step is to sign the necessary items so that they are compatible with the present Secure Boot keys, etc. This will download a Microsoft-signed loader and hashing tool (so that you can generate compatible keys for any kernel, tool, etc). Also, apparently, the name of the standard systemd bootloader has to be changed so that PreLoader will recognize it (the last step below):

$ cd /boot/EFI/systemd
$ wget http://blog.hansenpartnership.com/wp-uploads/2013/PreLoader.efi
$ wget http://blog.hansenpartnership.com/wp-uploads/2013/HashTool.efi
$ cp /boot/EFI/systemd/systemd-bootx64.efi /boot/EFI/systemd/loader.efi

Now the boot order on the motherboard NVRAM has to be changed so that PreLoader.efi and HashTool.efi are a) present, and b) in the correct order.

$ efibootmgr -c -d /dev/sdx -p 1 -L PreLoader -l /EFI/systemd/PreLoader.efi
$ efibootmgr -c -d /dev/sdx -p 1 -L HashTool -l /EFI/systemd/HashTool.efi
$ efibootmgr -v # see the order and note the codes for each item
...
$ efibootmgr -o 000P, 000H, 000L

where P, H, L are the hex codes for the PreLoader.efi, HashTool.efi and Linux Boot Manager, respectively. After this is done, you can exit the root shell. Unmount the installation drive ($ sudo umount -R /mnt) and reboot. Go into your BIOS and turn secure boot back on. Upon a subsequent reboot, you should be loaded into a blue screen that prompts you that no signed binaries were found and that you need to sign them using the tool. The following items MUST be signed for the Linux kernel to boot correctly:

You can navigate back through folders using the ../ option.If you forgot to sign something and can not boot, don’t worry. You can load up the live USB (have to disable Secure Boot first), and set HashTool.efi to be the first boot entry using efibootmgr. Then, you can re-sign as needed.

4 Windows

Install windows normally on either the unallocated space of the single disk setup, or on a separate disk. Remember to disable Fast Boot (in “Power Options”) before you restart for the first time. Systemd-boot will not be able to load windows through PreLoader.efi if the Windows boot manager is not on the same ESP. This is a trivial fix.

For the single disk environment, this is not an issue. Windows will see the ESP and install it’s manager to /boot/EFI/Microsoft/...

For the multi disk environment, you can simply copy the manager from the other disk(s) to the first ESP. For example, I have Windows installed on an NVMe drive:

$ lsblk
NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda           8:0    0 931.5G  0 disk
└─sda1        8:1    0 931.5G  0 part /home/USER/STORAGE_DISK
sdb           8:16   0 223.6G  0 disk
├─sdb1        8:17   0   512M  0 part /boot
├─sdb2        8:18   0 191.1G  0 part /
└─sdb3        8:19   0    32G  0 part [SWAP]
sdc           8:32   0 465.8G  0 disk /home/USER/OTHER_STORAGE_DISK
sr0          11:0    1  1024M  0 rom
sr1          11:1    1  1024M  0 rom
nvme0n1     259:0    0 372.6G  0 disk
├─nvme0n1p1 259:1    0   450M  0 part
├─nvme0n1p2 259:2    0   100M  0 part
├─nvme0n1p3 259:3    0    16M  0 part
└─nvme0n1p4 259:4    0 372.1G  0 part

The boot partition is clearly on /dev/nvme0n1p2 (Windows partitions are recovery, boot, filesystem), so we can mount it and copy the relevant files over:

$ sudo mount /dev/nvme0n1p2 /mnt
$ sudo cp -R /mnt/EFI/Boot /boot/EFI/
$ sudo cp -R /mnt/EFI/Microsoft /boot/EFI/
$ sudo umount /mnt

For the single disk environment, Windows will likely overwrite your existing boot order in NVRAM to place its boot manager first. This is an easy fix. Just load up the live USB again (have to disable Secure Boot first) and change the boot order back to PreLoader.efi being first:

$ mount /dev/sdx3 /mnt # the Arch / partition
$ mount /dev/sdx1 /mnt/boot # The Arch /boot partition
$ arch-chroot /mnt /bin/bash
$ efibootmgr -o 000P, 000H, 000L, 000W
$ exit
$ umount -R /mnt
$ reboot

where P, H, L and W are the hex codes for the PreLoader.efi, HashTool.efi, Linux Boot Manager and Windows Boot Manager entries, respectively. Again, you can check the order with $ efibootmgr -v. The mentioned order is also desired in the multi disk environment. We want to be able to sign new kernels, etc, upon restart.

After restarting, you should be booted into the systemd-boot menu. I like to make Windows the default entry because of automatic updates that require multiple restarts, etc. This can be done by just moving the cursor to the Windows entry and pressing “d” in the systemd-boot menu at startup.

5 Conclusion

Hopefully this was a straightforward, non-mysterious guide to setting up dual boot with Arch Linux and Windows for common disk configurations.

This should work with any other Linux distribution with minimal tweaking. Maybe one day I’ll care about having a sexy bootloader (ReFIND + themes), but the ease of setup for systemd-boot is attractive. From my experience with ReFIND I don’t see why this method (copying the loader from the NVMe disk over to the ESP on the Arch disk) wouldn’t simply work, but that’s a project for another day.

Lastly, most desktop motherboards have their own bootloaders now. If you don’t switch operating systems often enough, it may be sufficient for you to just do two separate installs and then just spam F8 on reboot (or whatever button it is for your BIOS) and just load the correct disk. Regardless, if you want Microsoft-signed Secure Boot to work with your Linux OS, you need to install the signed loader, etc, as outlined above. The Surface Pro line, unfortunately, does not have it’s own loader. Also, if I want to tinker, I can enable the boot options editor (change the line editor 0 to editor 1 in /boot/loader/loader.conf), and add cool words like “quiet” and “splash”.