NE2000 Emulation For Bhyve
Student: Alex Teaca (<iateaca AT SPAMFREE freebsd DOT org>)
Mentor: Gavin Atkinson (<gavin AT SPAMFREE freebsd DOT org>)
The bhyve hypervisor uses a virtio net PCI device emulation. However, a number of guest operating systems do not have virtio device drivers and are not able to use this emulation. One of the best supported devices, especially in older operating systems, is the NE2000 device. An implementation of a device model for this NIC would allow a larger number of unmodified guest operating systems to run under bhyve. The proposal aims to emulate the National Semiconductor DS8390/WD83C690 Ethernet adapter which is going to be used for the NE1000, NE2000 and a variety of similar clones.
Approach to solving the problem
The emulation is going to work tightly for the /sys/dev/ed/if_ed.c device driver in FreeBSD operating system and will be seen as a PCI device being attached under /sys/dev/if_ed_pci.c driver.
There is a datasheet for the 8390 NIC  which will be used for a proper understanding of the chip registers and transmit, receive, and control operations. Also, the ED device drivers mentioned above represent a good source of documentation.
I have found an implementation that emulates the NE2000 NIC in the qemu emulator . Obviously, this code can not be ported in the bhyve tree sources, but certainly will be a good source of inspiration regarding the register emulation and transmit, receive protocols. So, an analysis should be done before starting to design and develop the implementation in bhyve. Actually, the qemu implementation uses the RTL8029AS controller which is an NE2000 compatible Ethernet Controller for PCI interface . This controller I intend to use for the PCI configuration.
The implementation contains three logical phases. The first one is the module initialization where the PCI configuration space is configured and the data structures are initialized. The second one consists in register interpretation by handling the read and write BAR callbacks and eventually the third phase where transmit and receive protocols are emulated using read / write calls on the TAP interface. It should be mentioned that for the data transmission the packet is transferred from the memory to the device FIFO respectively for the data reception from the FIFO to the received buffer using the DMA protocol. Both packet reception and packet transmission are well explained in the 8390 NIC specification .
- the PCI NE2000 module
- the automated tester which is going to validate the NE2000 module
Implement the module initialization so the ED driver in the guest operating system recognizes the NE2000 module as a PCI device.
Design the bridge between the guest and the host operating systems through the TAP interface and find how to get notifications in order to read from the TAP file descriptor. For example, in the “virtio_net” module a callback is registered for the EVF_READ event on the TAP file descriptor.
Analyze of the structure of the registers from the datasheet ; design and implement the read and write register operations according to the specification.
26 June – Mid Term Evaluation
Deliver the module initialization and read and write register operations implemented in 25 May - 26 Jun period.
Analyze and understand the transmit and receive protocols through DMA looking on both datasheet specification and qemu implementation ; design and implement the packet transmission protocol through DMA according to the specification.
Design and implement the packet reception protocol through DMA according to the specification.
Code and design review with the mentor and fix possible issues; thinking for a testing schema and implement an automatic tester which will be used firstly on the “virtio_net” module; after the implementation is functional it will be used for NE2000 module testing.
Test the NE2000 module implementation, fix some possible bugs and prepare the deliverables and the final evaluation.
I've implemented the NE2000 emulation in the bhyve hypervisor under both PCI and LPC attachments. The full implementation is located in: /usr/src/usr.sbin/bhyve/ne2000.c, and emulates the ne2000 device for the ED driver in FreeBSD: /usr/src/sys/dev/ed/if_ed.c
In order to use the device in bhyve, you need to configure it in the parameters list as:
- under lpc: -l ne2k0,/dev/tap0
- under pci: -s 4:0,ne2k,/dev/tap0
Under the LPC bus, you can add at most two ne2000 devices which are ne2k0 and ne2k1. If you want to add two interfaces at the same time you will configure it like:
- -l ne2k0,/dev/tap0 -l ne2k1,/dev/tap1,00:a0:98:4a:0e:de
In the guest operating system you will see the network interfaces as:
ed0 at port 0x310-0x32f irq 10 on isa0 ed0: Ethernet address: 00:a0:98:4a:0e:ee ed1 at port 0x330-0x34f irq 11 on isa0 ed1: Ethernet address: 00:a0:98:4a:0e:de ed0: flags=8802<BROADCAST,SIMPLEX,MULTICAST> metric 0 mtu 1500 ether 00:a0:98:4a:0e:ee nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL> media: Ethernet 10baseT/UTP <full-duplex> ed1: flags=8802<BROADCAST,SIMPLEX,MULTICAST> metric 0 mtu 1500 ether 00:a0:98:4a:0e:de nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL> media: Ethernet 10baseT/UTP <full-duplex>
Any ne2000 device attached under the PCI bus will be probed as the ed2, ed3 ... network interfaces.
Note0: For the LPC attachments you need to add in the /boot/device.hints file from the guest operating system the following lines:
hint.ed.0.at="isa" hint.ed.0.port="0x310" hint.ed.0.irq="10" hint.ed.1.at="isa" hint.ed.1.port="0x330" hint.ed.1.irq="11"
All these tests have passed successfully:
- transmit/receive tcp/udp traffic on the ED interface
- stop, start the ED interface during the traffic is running
- transmit/receive multicast traffic
- enable promiscuous mode with the tcpdump enabled, so all packets are accepted
In order to measure the N2000 emulation performance I've used the iperf tool running as server and client in two virtual machine instances.
guest1: ifconfig ed0 inet 192.168.0.1 guest2: ifconfig ed1 inet 192.168.0.2
In order to make connectivity between them, I've attached their tap interfaces in a bridge. For example:
ifconfig bridge create ifconfig bridge0 addm tap0 addm tap1 ifconfig bridge0 inet 192.168.0.10
For the UDP traffic I've run these iperf commands:
guest2: iperf -s -u guest1: iperf -c 192.168.0.2 -f Mbyte -u -b 7m ------------------------------------------------------------ Client connecting to 192.168.0.2, UDP port 5001 Sending 1470 byte datagrams UDP buffer size: 0.01 MByte (default) ------------------------------------------------------------ [ 3] local 192.168.0.1 port 54052 connected with 192.168.0.2 port 5001 [ ID] Interval Transfer Bandwidth [ 3] 0.0-10.0 sec 7.09 MBytes 0.71 MBytes/sec [ 3] Sent 5057 datagrams [ 3] Server Report: [ 3] 0.0-10.0 sec 7.09 MBytes 0.71 MBytes/sec 2.649 ms 0/ 5056 (0%) [ 3] 0.0-10.0 sec 1 datagrams received out-of-order
For the TCP traffic I've run these iperf commands:
guest2: iperf -s guest1: iperf -c 192.168.0.2 -f Mbyte ------------------------------------------------------------ Client connecting to 192.168.0.2, TCP port 5001 TCP window size: 0.03 MByte (default) ------------------------------------------------------------ [ 3] local 192.168.0.1 port 31221 connected with 192.168.0.2 port 5001 [ ID] Interval Transfer Bandwidth [ 3] 0.0-10.0 sec 6.75 MBytes 0.67 MBytes/sec
Note1: The hardware N2000 devices are supposed to work at 10 Mbits/sec which means 1.25 MBytes/sec.