Virtual Machine to Machine feat. Windows


My work requires me to use C# and ASP.NET MVC. This isn’t a problem - I love C# and I think the ASP.NET MVC web framework is wonderful to work with. However, the .NET ecosystem, especially with regard to IDEs and MVC support, is lacking on every platform except Windows. Visual Studio is a very powerful and intuitive tool to work with, but the operating system limitation can be a bit of a pain sometimes.

I initially got around this by working from within a virtual machine. On my laptop, this worked quite well - it’s certainly powerful enough to handle it, and it supports VT-x and VT-d so the processor doesn’t run itself into overdrive (as well as supporting multi-core VMs which is super cool). However, it’s not a great workflow to be constantly switching between Linux and Windows, especially on a single small display, and running a VM limits both your available RAM and your battery life.

However, I already had my workspace set up on Windows, within the virtual machine. I definitely didn’t want to bother with re-installing everything again from scratch, and setting up licenses, and that mess. Hence, I thought… why not migrate my virtual machine to an actual physical partition? Surely it must be possible. I definitely couldn’t imagine any physical limitation preventing me from doing so.

My laptop uses UEFI for booting. This process will likely be entirely different if you are not using UEFI and a GPT-based hard drive layout. This was quite a long process to do, and in hindsight, I saved absolutely no time whatsoever - however, it’s quite a cool story to tell. Here are the steps I took to migrate my VHD-based Windows 10 virtual machine to a physical partition, and get it to boot.

partition

To migrate your Windows installation to a physical partition, you need to make the partition first. This is easy enough to do; GParted offers a LiveCD (or LiveUSB) based distribution designed specifically for messing around with your partition table, and it’s available here. Put that onto a spare CD or a memory stick, boot into it (how you manage this depends on your own system) and fire up GParted.

Windows uses NTFS as its file system, so you’re going to want to re-arrange your current partitions and make space for a Windows based partition. You’re going to want a partition no less than 25 or 30 GB in size; I’m currently running my Windows installation in 42 GB of space with Visual Studio and other goodies installed, with about 15 GB to spare - and you really don’t to have any less empty space lest you run into swapping or SSD performance issues.

GParted supports creation of NTFS partitions directly so I won’t provide a step-by-step description of how to do this. However, while I’m at this point, I will mention that Windows, if installed the standard way, will create not one but up to four partitions on your disk. These are:

  • The EFI system partition. If you’re using a UEFI-based system you will already have this - it stores your bootloader, your Linux image, boot configuration files amongst other things. It’s just a FAT32 partition that your motherboard firmware will initially boot into, and if you are dual-booting, will probably contain more than one bootloader. This is the only time you’ll likely ever see Linux-related and Windows-related executables on the same partition co-existing; I have both systemd-boot (previously gummiboot) and the Windows Boot Manager installed in my ESP.
  • A Microsoft Reserved partition. I have no idea what this is or will be used for, and I think that Microsoft doesn’t either. This doesn’t seem to be used at all, and my laptop is running Windows fine without one. Don’t bother trying to create this.
  • A recovery partition. This contains a subset of WinRE (the Windows recovery environment) that you can also find on your installation media, and can come in handy if you need to repair your bootloader or fix a messed-up installation without digging out your install CD or burning an emergency ISO. You don’t need this as such, and I don’t know how to create one, but I thought I’d mention it for completeness.
  • The actual Windows partition - this is your C:\ drive.

Of these, you’ll only need two. One of them, the EFI system partition, you won’t need to create. However, take note of where it is on your disk. Usually (and as per the standard) this should be the first partition on your disk (partition 1). If it’s not, note where it is after you create the NTFS partition. If it ends up being the third partition along, then it’s partition 3. We can now move on to the next stage.

copy

This part relies quite heavily on using a VHD for your virtual machine’s hard drive. The procedure will likely work if you use a VMDK or whatever other formats VirtualBox supports or offers as default, but I can’t guarantee the steps will be the same. I did this part on Arch - the way of acquiring the tools may differ if you use another Linux distribution. I’m not sure what the procedure is for this if your host OS is also Windows.

You’ll want to acquire a tool called vdfuse. On Arch, this can be installed through the vdfuse package in the AUR. On Ubuntu, I believe this is done by installing the virtualbox-fuse package. This allows you to mount a VHD file. You’ll also need to install the ntfs-3g package to be able to mount your actual NTFS partition. You will probably want to be superuser for this section (eg. via sudo su) or you’ll be typing sudo a lot.

First, you’ll want to mount the VHD itself. This can be done like this:

mkdir /mnt/vhd
vdfuse -w -f [path to VHD] /mnt/vhd

This will mount the virtual hard disk to /mnt/vhd. You’re not done yet, however; inside /mnt/vhd is now a selection of files named EntireDisk, Partition1, Partition2, and so forth. In my case, there was only a Partition1, making it immediately obvious that this partition in the VHD contains Windows. Now, you’ll need to mount this partition separately again.

mkdir /mnt/vfs
mount /mnt/vhd/Partition1 /mnt/vfs

With a bit of luck, you should now be able to see your virtual machine’s Windows installation files inside /mnt/vfs. Now, we need to mount the partition we created earlier. This is simple enough. Find out the partition device name (eg. /dev/sda3 - make sure you get this right or you’ll mount the wrong thing) and do something like this.

mkdir /mnt/part
mount /dev/sda3 /mnt/part

Now the only remaining thing to do is literally copy all of your files from your virtual partition to the physical one. Don’t forget to unmount everything afterward.

cp -r /mnt/vfs/* /mnt/part
umount /mnt/part
umount /mnt/vfs
umount /mnt/vhd

WinRE

This next part can only be done through a proper Windows Recovery Environment, because we need to let Windows manage the recreation of the Windows Boot Manager and the other crap it pulls with it. Burn a Windows ISO to a flash drive (preferably the same Windows version as the one in your Virtual Machine), boot into it, and press Shift-F10. This should bring up a command line window. Now, recall from earlier where the EFI system partition was on your disk. You’ll need this in a second.

The recovery environment should have mounted your standard C: drive in the usual location. However, you need to manually mount the EFI System Partition yourself, which is easy enough. For this, we need to use a tool called diskpart, which is included in WinRE (and Windows itself, which occasionally proves useful). On the command line, run diskpart, and then type list disk:

X:\Sources>diskpart

Microsoft DiskPart version 10.0.10586

Copyright (C) 1999-2013 Microsoft Corporation.
On computer: X230

DISKPART> list disk

  Disk ###  Status         Size     Free     Dyn  Gpt
  --------  -------------  -------  -------  ---  ---
  Disk 0    Online          111 GB      0 B        *

This will present a list of disks present in your system; it should be obvious which one is the correct one by the size; here, it’s clearly disk 0, so enter the command select disk 0, followed by the command list partition:

DISKPART> select disk 0

Disk 0 is now the selected disk.

DISKPART> list partition

  Partition ###  Type              Size     Offset
  -------------  ----------------  -------  -------
  Partition 1    System             514 MB  1024 KB
  Partition 2    Unknown             69 GB   515 MB
  Partition 3    Primary             41 GB    70 GB

DISKPART>

Now we can see the partitions. On my system, the EFI System Partition is the first partition along (partition 1), so I’d enter the command select partition 1. In your case, you should have remembered which partition was your ESP, and verify this by ensuring it has the type System in the list. Finally, we need to assign a drive letter to the ESP. In this guide we’ll use drive letter B, so enter the command assign letter=b. After we do this, we’re done with diskpart, so type exit to get back to the command prompt.

DISKPART> select partition 1

Partition 1 is now the selected partition.

DISKPART> assign letter=b

DiskPart successfully assigned the drive letter or mount point.

DISKPART> exit

Leaving DiskPart...

X:\Sources>

Now, your ESP should be mounted as a standard drive at B:, which we can now access to set up the bootloader.

bootloader

First, we need to actually create the bootloader; this typically resides at \EFI\Microsoft\Boot on the ESP drive, so we need to create it if it doesn’t already exist, go to it, and then run a WinRE utility to repair (in our case, create) the bootloader.

X:\Sources>B:

B:\>mkdir \EFI\Microsoft\Boot

B:\>cd \EFI\Microsoft\Boot

B:\EFI\Microsoft\Boot>bootrec /fixboot

Now, if you do a directory listing, several files ending in .efi should be present, such as bootmgfw.efi. You might want to verify this for yourself. Next step is to create something that Windows calls the Boot Configuration Data, or BCD. This seems to contain references to all of the installed operating systems on the disk, including Windows, with information on how to boot it correctly. Stay in the same directory, and run the following commands:

bcdedit /createstore BCD
bcdedit /store BCD /create {bootmgr} /d “Windows Boot Manager”
bcdedit /store BCD /create /d “Windows” /application osloader

This creates the BCD with some initial config; the flag on the final line /d "Windows" just gives a name to the Windows loader and you can set this to whatever you want. The final command should print a success message containing a GUID; copy this because you’ll need it now. Run these commands, substituting the word GUID for the GUID returned by the last command, including the curly braces.

bcdedit /store BCD /set {bootmgr} default GUID
bcdedit /store BCD /set {bootmgr} path \EFI\Microsoft\Boot\bootmgfw.efi
bcdedit /store BCD /set {bootmgr} displayorder {default}

Finally, we want to configure the Windows boot loader in the BCD, which can be done like this:

bcdedit /store BCD /set {default} path \Windows\System32\winload.efi
bcdedit /store BCD /set {default} systemroot \Windows
bcdedit /store BCD /set {default} device partition=C:
bcdedit /store BCD /set {default} osdevice partition=C:

After I did this, I could access my Windows installation just fine. Note that you’ll need to hold the space bar on boot to bring up the boot options menu in systemd-boot - for me, Windows appeared automatically as another option. If you wish to set the Windows Boot Manager to the default bootloader you’ll need to do it in your system’s UEFI settings (which will depend on your system), and presumably add Linux to the WBM list. I don’t know how you’d do this on the command line, and at this point you can boot into Windows so it might just be easier to ues something like BCDedit to do it through a GUI.

At this point, you should be done. Bear in mind that the voodoo required to get Windows to boot properly can be a right pain sometimes; you might need to play around a little bit to get it to work properly. Don’t expect your Virtual Machine installation to work flawlessly out-of-the-box, either; you will probably still have some Guest Additions installed if you used VirtualBox which you will want to get rid of, and a few things might need tweaking before it works OK. Besides this, my installation worked fine, and I could dive straight in to using Windows as a host OS.