How to install OpenWrt on Hyper-V Gen2 VM

Recently I was building a homebrew x86 router on a Hyper-V VM using OpenWrt. However, few tutorial can I found to install it on a UEFI-based platform like Hyper-V Gen2 VM, after searching and trying, I came up with the following solution.

This tutorial will show you how to install OpenWrt 18.06.2 on a Hyper-V Gen2 VM.

__Warning: Only OpenWrt/LEDE version 17.01 or higher supports Hyper-V. DO NOT try install any old version.__


## Prerequisites

* Windows environment with Hyper-V enabled.
* A Hyper-V Gen2 VM with any Linux distribution installed. (Here we use Ubuntu 18.04)


## Step1: Build the VHDX image

First, lets download the OpenWrt x86-64 firmware image from https://downloads.openwrt.org

```console
$ wget https://downloads.openwrt.org/releases/18.06.2/targets/x86/64/openwrt-18.06.2-x86-64-generic-rootfs.tar.gz
$ wget https://downloads.openwrt.org/releases/18.06.2/targets/x86/64/openwrt-18.06.2-x86-64-vmlinuz
```

Now we open the Hyper-V Manager and create a virtual hard disk. Here we create a 4GB dynamically expanding .vhdx format file named `openwrt.vhdx`. Then attach it to the Linux VM, here the its device name is `/dev/sdb`. You can check the device name with `dmesg` or `lsblk` command.

__WARNING: Following commands will remove ALL THE DATA on the device. BE SURE you have selected the right disk.__

Now we used `parted` command to initialize the VHD and create partitions.

```console
# parted /dev/sdb
(parted) unit MiB
(parted) mklabel gpt
(parted) mkpart ESI fat32 1 129
(parted) mkpart primary ext4 129 4095
(parted) set 1 boot on
(parted) quit
```

Now format the partitions.

```console
# mkfs.vfat -F 32 /dev/sdb1
# mkfs.ext4 /dev/sdb2
```

If everything is OK, running the `parted /dev/sdb print` command will give you a result similar to below.

```console
# parted /dev/sdb print
Model: Msft Virtual Disk (scsi)
Disk /dev/sdb: 4295MB
Sector size (logical/physical): 512B/4096B
Partition Table: gpt
Disk Flags:

Number  Start   End     Size    File system  Name     Flags
 1      1049kB  135MB   134MB   fat32        ESI      boot, esp
 2      135MB   4294MB  4159MB  ext4         primary
```

Now mount these partitions.

```console
# mkdir -p /tmp/sdb1
# mkdir -p /tmp/sdb2
# mount /dev/sdb1 /tmp/sdb1
# mount /dev/sdb2 /tmp/sdb2
```

Now install `systemd-boot` to the ESP partition `/dev/sdb1`

```console
# bootctl --path=/tmp/sdb1 install
```

If success, something like following will be printed.

```console
# bootctl --path=/tmp/sdb1 install
Created "/tmp/sdb1/EFI".
Created "/tmp/sdb1/EFI/systemd".
Created "/tmp/sdb1/EFI/BOOT".
Created "/tmp/sdb1/loader".
Created "/tmp/sdb1/loader/entries".
Copied "/usr/lib/systemd/boot/efi/systemd-bootx64.efi" to "/tmp/sdb1/EFI/systemd/systemd-bootx64.efi".
Copied "/usr/lib/systemd/boot/efi/systemd-bootx64.efi" to "/tmp/sdb1/EFI/BOOT/BOOTX64.EFI".
Created EFI boot entry "Linux Boot Manager".
```

Now edit boot configuration files.

```console
# vi /tmp/sdb1/loader/loader.conf
# vi /tmp/sdb1/loader/entries/openwrt.conf
```

> loader.conf
>
> ```conf
> timeout 0       # Loader timeout
> default openwrt    # Default boot option, here is openwrt
> ```
>
> openwrt.conf
>
> ```conf
> title OpenWrt     # Boot option title to be displayed
> linux /vmlinuz    # Path to vmlinuz file
> options ro root=/dev/sda2 rootfstype=ext4 rootwait console=tty0
> ```

Then copy OpenWrt firmware to the VHD.

```console
# mv ./openwrt-18.06.2-x86-64-vmlinuz /tmp/sdb1/vmlinuz
# tar -zx -C /tmp/sdb2 -f openwrt-18.06.2-x86-64-generic-rootfs.tar.gz
```

You can use `ls` or `tree` command to check if it has been written successfully.

Finally，unmount the partitions and disattach `openwrt.vhdx` in VM settings.

```console
# umount /dev/sdb1
# umount /dev/sdb2
# eject /dev/sdb
```


## Step2: Configure the VM

Now create two virtual switches, one is of Internal type and here we call it `LAN`, it can be used by the Hyper-V host and other VMs. The other is of External type, here we call it `WAN`, bind it to the NIC connected to the modem and make sure you have unchecked "Allow management operating system to share this network adapter".

If you need to configure more physical ports, either a LAN port or a WAN port, do the same step as creating the `WAN` port above. Make sure you have unchecked "Allow management operating system to share this network adapter".

Now create a new virtual machine in Hyper-V Manager. Select generation 2, uncheck dynamic memory, connect network to `LAN` and use the `openwrt.vhdx` we created in step 1. After finishing the wizard, open VM settings, add a new network adapter and connect it to `WAN`. For all the adapters, click the "+" on the left, go to "Advanced Features" and check "Enable MAC address spoofing"

Then go to "Secure Boot" and disable it.

After that, boot the virtual machine. If `Please press Enter to activate this console.` is shown on your screen, it means it works. Now you can ssh to it or access the Luci configuration page at http://192.168.1.1


## References

* [OpenWrt Wiki>>ToH>>Minnowboard Max](https://openwrt.org/toh/minnowboard/minnowboard-max)
* [LEDE installation on UEFI-only x86(_64) systems and HyperV G2 possible?](https://forum.lede-project.org/t/solved-lede-installation-on-uefi-only-x86--64-systems-and-hyperv-g2-possible/1177)
* [Managing EFI Boot Loaders for Linux : Using gummiboot/systemd-boot](http://www.rodsbooks.com/efi-bootloaders/gummiboot.html)


*This article is under [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0/).*