Create a Windows 10 that Boots off a USB Stick, from Linux (Shallow Thoughts)

Akkana's Musings on Open Source Computing and Technology, Science, and Nature.

Sun, 01 Oct 2023

Create a Windows 10 that Boots off a USB Stick, from Linux

In 2019, I wrote about struggling to get any sort of Windows booting off an external USB stick, in order to Install Lenovo Firmware Packaged as a .exe on a Linux Machine. I ended up needing to borrow a real Windows machine and install Rufus on it.

In 2023, things are much better. Aki at atkdinosaurus has written a clear, concise tutorial on that topic: How to create a Windows 10 installation on a USB stick in UEFI mode. I love that it's all command-line, so you can duplicate the steps exactly.

The really important change was getting qemu to use the raw disk device, /dev/sda, as its disk drive rather than a .qcow file. -drive format=raw,file=/dev/sda,index=0 to get qemu to use the external drive in raw format instead of a .qcow file.

But there were a couple of things that didn't quite work on my system, or that weren't fully specified so I had to figure them out.

Things I Changed

Debian has no edk2-ovmf, so I installed ovmf instead. I also needed qemu-system-gui to get around the error message

qemu: module ui-ui-gtk not found, do you want to install qemu-system-gui package?
qemu-system-x86_64: Display 'gtk' is not available.

My complete list of installed qemu-related packages now (they might not all be necessary) are: qemu qemu-system-common qemu-system-data qemu-system-gui qemu-system-x86 qemu-utils libvirt-daemon-driver-qemu ipxe-qemu ovmf

But Debian's ovmf puts the OVMF_CODE.fd file in a different place, so in the long qemu-system-x86_64 command, I changed the -bios line to

-bios /usr/share/OVMF/OVMF_CODE.fd \

Update, December 2023: Debian just split OVMF_CODE into four files (actually two files and two symlinks), none of which work with the -bios flag. Now I first

cp /usr/share/OVMF/OVMF_VARS_4M.fd /tmp/
and then I've replaced the -bios argument in the qemu-system-x86_64 command with:
  -drive if=pflash,format=raw,unit=0,file=/usr/share/OVMF/OVMF_CODE_4M.fd,readonly=on \
  -drive if=pflash,format=raw,unit=1,file=/tmp/OVMF_VARS_4M.fd \

You can read more about that here: Update to running Windows 10 under QEMU: Debian Changed OVMF.

System Information and MSDM

I also wanted to use my system's MSDM data, since this laptop came with an OEM Windows license. My old Windows virtual machine (which I created under VirtualBox and then migrated to qemu) used my laptop's MSDM tables to tell Windows that it was running on a system that came with an OEM Windows license. For that:

First, make a directory somewhere and extract the various hardware and ACPI tables into it (these commands require root or sudo).

dmidecode -t 0 -u | grep $'^\t\t[^"]' | xargs -n1 | perl -lne 'printf "%c", hex($_)' > smbios_type_0.bin
dmidecode -t 1 -u | grep $'^\t\t[^"]' | xargs -n1 | perl -lne 'printf "%c", hex($_)' > smbios_type_1.bin
cat /sys/firmware/acpi/tables/MSDM > msdm.bin
cat /sys/firmware/acpi/tables/SLIC > slic.bin
You can omit the slic.bin line if your system doesn't have a SLIC table (my CX1 doesn't).

Then pass the info to qemu with these arguments: -acpitable file=/path/to/QEMU/acpitables/MSDM -smbios file=/path/to/smbios_type_0.bin -smbios file=/path/to/smbios_type_1.bin

The Step By Step Instructions

Here are the steps I followed, slightly modified from Aki's instructions:

First, download a Windows 10 ISO, e.g. Win10_22H2_English_x64v1.iso.

Set USB_STICK to the disk where you'll be installing.

USB_STICK=/dev/sdX

Zero the USB disk partition table:

sgdisk -Z ${USB_STICK}

Create the partitions on the USB stick:

sgdisk \
  --new 1:0:+1G  --typecode=1:ef00 --change-name=1:'EFI System Partition' \
  --new 2:0:+16M --typecode=2:0c01 --change-name=2:'Microsoft reserved' \
  --new 3:0:+36G --typecode=3:0700 --change-name=3:'Windows 10' \
  --new 4:0:+36G --typecode=4:0700 --change-name=4:'Data' \
  --new 5:0:+1G  --typecode=5:2700 \
  ${USB_STICK}

Set the hidden flag on the 5th (Recovery) partition:

parted ${USB_STICK} set 5 hidden on

Format the partitions:

mkfs.vfat ${USB_STICK}1
mkfs.vfat ${USB_STICK}2
mkfs.ntfs -F -Q ${USB_STICK}3
mkfs.ntfs -F -Q ${USB_STICK}4
mkfs.ntfs -F -Q ${USB_STICK}5

Change the USB stick's owner device file for your own username:

chown username ${USB_STICK}

Now you can get out of the root shell, and do the rest as your user, You’ll need read/write access to /dev/kvm, which means being in group kvm.

Run qemu using the USB drive as the virtual disk, pointing at the MSDM tables you extracted earlier, using your Win10 image as the CD drive:

qemu-system-x86_64 \
  -display gtk,window-close=off \
  -machine type=q35,accel=kvm \
  -enable-kvm \
  -cpu host,hv-relaxed,hv-vapic,hv_spinlocks=0x1fff,hv-runtime,hv-time,hv-frequencies \
  -smp 2 \
  -m 2048 \
  -object rng-random,id=rng0,filename=/dev/urandom \
  -device virtio-rng-pci,max-bytes=1024,period=1000 \
  -bios /usr/share/OVMF/OVMF_CODE.fd \
  -acpitable file=/path/to/QEMU/acpitables/MSDM \
  -smbios file=/path/to/QEMU/acpitables/smbios_type_0.bin \
  -smbios file=/path/to/QEMU/acpitables/smbios_type_1.bin \
  -vga std \
  -device intel-hda \
  -device hda-duplex \
  -device qemu-xhci,id=xhci \
  -device usb-tablet,bus=xhci.0 \
  -drive format=raw,file=${USB_STICK},index=0 \
  -drive file="/PATH/TO/Win10_22H2_English_x64v1.iso",index=1,media=cdrom
(Note: I'm not confident about omitting the -smbios type=0,vendor=0vendor,version=0version,date=0date,release=0.0,uefi=on line, but my install seems to be working.)

When you run that, be ready to hit a key right away, so it boots from the (simulated) CDROM. Otherwise it tries to do some sort of net boot, which doesn't work.

When it asks "Which type of installation do you want?" choose Custom: Install Windows only (advanced) since you don't have anything to upgrade nor settings to keep. It will install to partition 3.

The install is remarkably fast, and then it reboots itself. Don't let it! Shut down instead. I'm not sure how to do this gracefully; the qemu File menu has a "Power Down" option but it didn't work for me, and Windows was clearly getting ready to reboot, so after a few tries I chose File->Quit instead.

Why not let it reboot?

Well, after the reboot, Windows configures some stuff (dive for the speaker icon to mute the smarmy Cortana narration), but when it gets to "Let's add your account", there's no way to continue without creating a Microsoft account. You can only get past that point if the network is shut down. But you probably would rather not shut down your Linux host's network just to get past this silly Windows thing, and qemu doesn't seem to have a way to stop the network on an already running session.

So instead, shut down, close out the qemu-system-x86_64 session, then run the same qemu-system-x86_64 command as before, except this time, insert -nic none in the options.

(And this time, don't hit a key to have it boot from CD, or it will go through the whole installation again except it won't be able to download its updates.)

When it gets to its no network error, click on "I don't have internet", then "Continue with limited setup". After entering an account name and password you'll have to answer three annoying security questions; there doesn't seem to be any way around them, but you don't have to give them your real answers. And now you can let it finish configuring Windows.

And, wonder of wonders, when you shut Windows down, you can now unplug that USB drive and it will work as a bootable Windows install, which you can use to install firmware, play games, download ebooks or whatever it is you need from Windows. Or you can use it under QEMU, using that same qemu-system-x86_64 command you used before.

Tags: , , , ,
[ 10:07 Oct 01, 2023    More linux | permalink to this entry | ]

Comments via Disqus:

blog comments powered by Disqus