~drscream

Using bhyve PCI passthrough on OmniOS

Some hardware is not supported in illumos yet, but luckily there is bhyve which supports pci passthrough to any guest operating system. To continue with my OmniOS desktop on “modern” hardware I would love wifi support, so why not using a bhyve guest as router zone which provide the required drivers?

First of all it requires an up-to-date OmniOS version which provide bhyve support. Including the following IPS packages:

$ pkg install system/bhyve
$ pkg install system/zones/brand/bhyve

You can also found additional system requirements at the OmniOS bhyve hypervisor page.

Simple overview

                                   __   _
                                 _(  )_( )_
                                (_  inet  _)
                                  (_) (__)
                                     |
                                     |
                                     |
           o-- global zone ----------|--------------------------o
           |        o-- bhyve zone --|----o                     |
           |        |                |    |                     |
pci-wifi --|- passthrough -> wifi-device  |                     |
           |        |         |           |                     |
           |        |       {NAT}         |                     |
           |        |         |           |                     |
           |        |        vnic <--> [ etherstub ] <---> vnic |
           |        o---------------------o                     |
           |                                                    |
           o----------------------------------------------------o

Global zone network configuration

An etherstub is required which provides a virtual switch between the virtual nic in the global zone and the one in the bhyve zone.

$ dladm create-etherstub switch0

Now it’s required to assign to virtual nics on this etherstub.

$ dladm create-vnic -l switch0 uplink0
$ dladm create-vnic -l switch0 bhyve0

You can verify your configuration with dladm show-vnic. It’s also required to assign an IP address to the uplink0 vnic which allow the communication into the bhyve zone.

$ ipadm create-addr -T static -a 10.1.1.2/30 uplink0/v4

Choose whatever network you like, but because of the communication from only two nics I choose a /30 network.

Configure passthrough device via ppt

Lookup the PCI information for the device you like to passthrough to your bhyve zone. On this example I’m looking for the wireless network card.

$ prtconf -dD
...
   pci8086,1311 (pciex8086,85) [Intel Corporation Centrino Adv....] (...)

Look for the correct pciex entry, I didn’t got it working with the pci entry only. The prtpicl -v tool might also provide you additional information about your PCI devices.

The pciex information need to be stored in the following two files:

The /etc/ppt_aliases should look similar to the /etc/driver_aliases file, which will overwrite or assign the required ppt driver on the device:

ppt "pciex8086,85"

The /etc/ppt_matches file contains only the PCI information:

pciex8086,85

An reboot is recommended to attach the driver on the device. A verification can be done with the following commands:

Look for the attached driver on the device:

$ prtconf -dD | grep ppt

Use pptadm to list dev and path:

$ pptadm list -a
DEV        VENDOR DEVICE PATH
/dev/ppt0  8086   85     /pci@0,0/pci8086,1e12@1c,1/pci8086,1311@0

The /dev/ppt0 will be used later to passthrough it to the bhyve zone.

Use zonecfg to configure guest zone

For the bhyve zone it’s required to create a dataset for the zonepath and a zvol which contains the guest itself. My zone will be called gw because it’s the gateway to the internet.

# Create the zvol
$ zfs create -V 10G rpool/bhyve0

# Create the zonepath
$ zfs create rpool/zones/gw

# Create some dataset for isos
$ zfs create rpool/iso

Download the latest FreeBSD (or any operating system you like) ISO and store it in /rpool/iso. For me it looks like:

$ cd /rpool/iso
$ wget -O freebsd.iso https://download.freebsd.org/...-bootonly.iso

To create a zone via zonecfg, I always recommend create one file which contains the relevant data for zonecfg to work correctly, for example: /tmp/gw.zone:

create -b
set brand=bhyve
set zonepath=/zones/gw
add net
  set physical=bhyve0
  set global-nic=switch0
end
add device
  set match=/dev/zvol/rdsk/rpool/bhyve0
end
add device
  set match=/dev/ppt0
end

add fs
  set dir=/rpool/iso/freebsd.iso
  set special=/rpool/iso/freebsd.iso
  set type=lofs
  add options ro
  add options nodevices
end
add attr
  set name=cdrom
  set type=string
  set value=/rpool/iso/freebsd.iso
end

add attr
  set name=bootrom
  set type=string
  set value=BHYVE_RELEASE
end
add attr
  set name=bootdisk
  set type=string
  set value=rpool/bhyve0
end
add attr
  set name=extra
  set type=string
  set value="-S -s 8:0,passthru,/dev/ppt0"
end

Everything related to the freebsd.iso can be removed later if you do not need the install medium anymore. The BHYVE_RELEASE bootrom is required for FreeBSD to work correctly, it might depends on your operating system which one you need to use.

The following two settings are required to get passthrough working correctly:

This will add the ppt0 device into the zone and allow the required access.

add device
  set match=/dev/ppt0
end

Will add extra parameters to the bhyve command to passthrough /dev/ppt0 correctly to the PCI ID 8:0 in the guest. The PCI ID can be chosen by yourself as long as it’s not used as an parameter already.

add attr
  set name=extra
  set type=string
  set value="-S -s 8:0,passthru,/dev/ppt0"
end

The -S is required to disallow the guest memory to be swapped. Otherwise passthrough isn’t supported!

Finish the configuration and creating the zone

$ zonecfg -z gw -f /tmp/gw.zone

Use zoneadm to install and boot the zone:

$ zoneadm -z gw install
$ zoneadm -z gw boot

If something is going wrong and the zone doesn’t boot correctly check the bhyve log file in zonepath/root/tmp/init.log (example: /zones/gw/root/tmp/init.log).

Configure FreeBSD bhyve guest

You should be able to log into the zone via zlogin:

$ zlogin -C gw

Follow the operating system installation based on you needs. For me it’s an awesome FreeBSD installation which is straightforward anyway.

After the installation some network configuration is required to support the wireless card and setup NAT.

Modify the /boot/loader.conf to load the correct firmware:

if_iwn_load="YES"
iwn6000fw_load="YES"
iwn6000g2afw_load="YES"

wlan_wep="YES"
wlan_ccmp_load="YES"
wlan_tkip_load="YES"

Setup the network interfaces via /etc/rc.conf:

# Check for the guest "ethernet" interface
ifconfig_vtnet0="inet 10.1.1.1 netmask 255.255.255.252"

# Wireless network interface
wlans_iwn0="wlan0"
ifconfig_wlan0="WPA up"
create_args_wlan0="country DE"

# Enable PF and IP forwarding
gateway_enable="YES"
pf_enable="YES"

Add the NAT entry for /etc/pf.conf:

nat on wlan0 from {10.1.1.0/30} to any -> (wlan0)

To configure the wireless network (as required) I use the wpa_supplicant and store the information in /etc/wpa_supplicant.conf:

wpa_passphrase "SSID" "PWD" >> /etc/wpa_supplicant.conf

A reboot might be required to load the required firmware and configure the network as described.

Test network configuration

The global zone should be able to ping the bhyve guest via:

$ ping 10.1.1.1

If you haven’t configured a default gateway yet, it might be good time to do that:

$ route add default 10.1.1.1

After that you should be able to reach other networks and the always loved internet via your awesome bhyve router :-)


Send your comment by mail.