install windows onto usb drive using linux

2021-03-22
2021-09-11

disclaimer

The main purpose of this guide is to help myself remember things during future reinstallations, and also because a similar guide could not be easily found on the internet.

Please treat this guide only as a loose collection of notes. I am not an expert in virtualisation or system administration. Follow any seeming instructions at your own risk!

introduction

Unfortunately, users who prefer GNU/Linux might still require a Windows instance that runs on bare metal, for one reason or another. Fortunately, Microsoft has decided to make copies of Windows 10 (hereinafter "win") free to use. Having a machine dedicated to GNU/Linux (hereinafter "linux") but also a separate installation of win on an external USB drive would be ideal, and this guide describes the process of achieving just that.

Our basic plan is to use QEMU and KVM (hereinafter "qemu" and "kvm") to create a virtual win image. The image will then be converted into a UEFI-aware one and grafted onto a USB drive. The same idea should work with VirtualBox[1] or VMWare[2], but the benefit of using qemu+kvm is that you need not concern yourself with version-matching thirdparty modules every time you upgrade your kernel.

This general process is known as "virtual machine to physical machine migration" (V2P), and we need to endure it because official installers prevent direct installs to USB devices. Convenient thirdparty programs like WinToUSB achieve the same goal, but those can be hard to access from just linux. On that note, installing one of those programs in a win virtual machine with USB passthrough may be an alternative, and probably easier, solution. We will use our qemu method, however, so as to minimise the use of nonfree software.

assumptions

This guide assumes that a linux instance has already been installed on your UEFI/GPT-based CPU-virtualisation-capable 64-bit host machine. We will not be touching this linux installation (besides to enable kvm and install some programs), its partition, or any other hard drive built into the machine. Only an external USB drive (hereinafter "usb" or "usb drive") will be messed with. The usb should have at least 20gb of storage capacity (according to Microsoft[3]), and this guide uses a 128gb one. The usb should preferably be fast and capacious enough to accommodate a full operating system equipped with typical development environments.

Only the most basic single-drive setup will be demonstrated. More involved topics like RAID, encryption, etc. will not be addressed. This guide may also work for other unix-like systems but has not been tested on any.

Some commandline knowledge and comfort are assumed in the reader. Please read the linked pages carefully, and accompany this guide with further googling, in case versions/commands have changed.

kvm & qemu

Installing a virtual win instance might work without kvm. (In that case, skip the next paragraph, and remove the --enable-kvm option as seen in the win installation section below.) But let us assume that kvm hosting does need to be enabled in the running linux kernel. Note the following kernel options that a kvm host may want (may; not completely sure):

(On a 64-bit Intel-based machine with kernel version 5.11.7)

-> Virtualization
  -> Kernel-based Virtual Machine (KVM) support
    -> KVM for Intel (and compatible) processors support

-> Networking support
  -> Networking options
    -> Virtual Socket protocol
      -> virtio transport for Virtual Sockets

  -> Plan 9 Resource Sharing Support (9P2000)
    -> 9P Virtio Transport

-> Device Drivers
  -> Character devices
    -> Virtio console

    -> Hardware Random Number Generator Core support
      -> VirtIO Random Number Generator support

  -> Graphics support
    -> Intel 8xx/9xx/G3x/G4x/HD Graphics
      -> Enable Intel GVT-g graphics virtualization host support
        -> Enable KVM/VFIO support for Intel GVT-g

  -> Rpmsg drivers
    -> Virtio RPMSG bus driver

-> File systems
  -> FUSE (Filesystem in Userspace) support
    -> Virtio Filesystem
      -> Virtio Filesystem Direct Host Memory Access support

-> Cryptographic API
  -> Hardware crypto devices
    -> VirtIO crypto driver

If you switched kernels, reboot into the new kvm-capable one.

Install qemu. libvirt may be nice to have but is a bit overkill for our purposes. The rest of this guide will continue to do without libvirt or other addons to qemu/kvm.

Put yourself in the kvm group (and relogin/reboot).

win installer and drivers

Get the win installation iso at the official website[4].

As noted in the Gentoo wikis[5], win will have trouble detecting kvm-virtualised hard drive devices. For this reason, we should make use of the Red Hat-provided drivers[6].

Place the win iso and the driver iso somewhere convenient, say directory ~/DIR.

win installation

Create some virtual space that will act as win's hard drive that is large enough to hold the initial installation but that will probably be resized later, on metal:

$ qemu-img create -f qcow2 ~/{DIR/IMAGE.img} {20G}

NOTE: Setting a restricted size here, like 20gb, will require you to move and resize partitions later on. A Windows-native way of doing that seems to exist but one of the several thirdparty programs would likely be the easier way of dealing with it. If you have any reservations about this, calculate now exactly how much space you would like. Fixing the size upfront will probably be cleaner but temporarily consume a lot more space on your host machine.

An EFI partition of about 400mb and a recovery partition of about 500mb seem to get created somewhere down the line. That means you will want at most about (- (usb size) 1gb) for your C: drive (ie. this image file). This last statement needs review. Intuition would tell us that the win installer will establish all the partitions within the image size you specify.

Please also see the partition resize section below.

Execute the virtual machine with something like the following (details will need to be adapted to your context, particularly the capitalised placeholders):

$ qemu-system-x86_64 \
  -enable-kvm \
  -drive file=~/{DIR}/Win10_{VER_v2}_English_x64.iso,index=1,media=cdrom \
  -drive file=~/{DIR}/virtio-win-{VER}.iso,media=cdrom \
  -boot d \
  -cpu host,hv_relaxed,hv_spinlocks=0x1fff,hv_vapic,hv_time \
  -device intel-hda -device hda-duplex \
  -device intel-iommu \
  -device qemu-xhci,id=xhci \
  -device virtio-balloon \
  -drive file=~/{DIR/IMAGE.img},if=virtio,aio=native,cache.direct=on \
  -m 2G \
  -machine q35 \
  -name {GUEST} \
  -net nic,model=virtio-net-pci \
  -net user,hostname={HOSTNAME} \
  -smp 2 \
  -usb \
  -vga virtio

After the "cdrom" boots up, at the step in the installation where you are supposed to choose a hard drive to install into, the installer will complain about missing drivers. Click "Load driver", and a list of the Red Hat virtio drivers should appear. Select the one corresponding to Windows 10 and proceed.

Internet may not work throughout the rest of the process, but the installation should succeed regardless. After completion, you will be logged into win and see the familiar desktop.

At this point, shut down the virtual machine, and rerun the above qemu-system-x86_64 command, but with the two cdrom options and the -boot option removed.

uefi

Once logged back into the virtual win, open up a cmd in administrator mode and convert the MBR disk format to GPT, per the official docs[7].

MBR2GPT /convert /allowFullOS

A warning message concerning "WinRE" might appear but can (probably) be ignored. We are effectively abandoning Windows recovery functionality (and the partition manipulation later on will probably bork things even more), but if you need a properly functioning recovery partition, please do your own research before the GPT conversion.

Shutdown the virtual machine. If it refuses to shutdown immediately, do a hard quit.

You will no longer be able to boot into the virtual win (short of booting qemu with a UEFI-enabled boot image). You can try starting it anyway and see it fail to be sure that the MBR->GPT conversion was successful.

usb

Plug in the usb drive into your host machine, noting the device path.

Make sure the usb is unmounted and the virtual win not running.

Write out the virtual image into your usb (please make sure the target device is devoid of any important data and correctly written on the commandline -- the capitalised text are placeholders!):

# qemu-img convert ~/{DIR/IMAGE.img} -O raw /dev/{SDA}

metal

With the copying-to-usb complete and sync'd, keep the usb plugged in and reboot the host machine. With a sanely configured bootloader (a standard installation of refind, for instance), you should be able to boot into the usb.

On a modern Windows-ready machine, win will likely detect some components automatically, the most critical being the network device. If internet refuses to work after logging into the fresh metal-win, you will have to search for the network device drivers from within linux, put them somewhere on the usb, like in the standard Downloads folder (see also ntfs-3g), and get back into win to install them.

And with internet working, now begins the thrilling process of driver-downloading and Windows-updating. You must always keep the usb plugged in when using win and also throughout the multiple reboots required by the updates. Refer to hardware manufacturer support sites for acquiring a complete set of driver updates...

partition resize

... But! If you specified only 20gb for the hard drive, as this guide has done, you will want to expand the main partition before installing too many things.

Windows disk management utilities do not overtly support moving partitions around (it only seems to support resizing "rightward"). But we need to get around this restriction as the partitions get stacked contiguously by default and the C: drive gets surrounded by other partitions with no immediate means of expansion.

Googling brings up several software products that advertise their own ways of moving partitions live, and at least a few of them work well -- that is, if one is willing to stomach the risk of installing malware. Forums also point to a native way[8], using diskpart, dism, and reagentc. But as this topic is a bit icky, the reader should do their own research and trials to decide on the best strategy.

dual-boot compatibility

time

Unless you live in the GMT time zone, time will be shown differently in win and in linux. Follow the instructions in this link[9] and make win use UTC. Login to win, go to Date & time settings, and turn off Set time automatically. Open regedit, navigate to HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\TimeZoneInformation, and create a new DWORD (32-bit) value of 1 for RealTimeIsUniversal.

Reboot and the times should be the same.

bluetooth peripheral

Some bluetooth devices need to be paired anew every time a different OS is booted, even if the device supposedly can handle multiple targets. A method of getting this to work has been documented on the internet, and it involves using regedit to get the key associated with the device and pasting that key into the linux counterpart file.

Please research this yourself, maybe starting with the keywords, "pair bluetooth dual-boot".

that's all

The process of getting this win-on-usb dual-booting setup has been rather simple. We only had to enable kvm in the linux kernel, install qemu, apply a few workarounds, and complete the usual installations.

With a fast SSD usb drive, win will work beautifully, all without encroaching upon your machine's extant linux setup.

If any part of this guide is questionable or can be improved, please let me know. I do hope this guide does not breach the Windows terms of use.

root/page/usb-win