Skip to content

Commit 56a5c0b

Browse files
Migrate virtio-net from semu
This commit migrates virtio-net support from semu with the following modifications: 1. Implement virtio-net device model The virtio-net implementation follows the VirtIO-MMIO flow used by virtio-blk, including feature negotiation, queue setup, QueueNotify handling, used ring update, interrupt status, and device status reset. The device currently supports a TAP-backend network interface and handles basic RX/TX virtqueue processing for guest network packets. 2. Add TAP backend helper Introduce netdev.c and netdev.h to provide host-side TAP device access. Future work may support other host-side backend. 3. Handle virtio-net header processing For guest TX, the device skips the virtio-net header before writing the Ethernet frame to the TAP backend. For guest RX, the device prepends a virtio-net header before copying the received Ethernet frame into the guest-provided RX buffer. 4. Implement MMIO_VIRTIONET Add MMIO routing for virtio-net and connect the device interrupt status to the PLIC, following the existing virtio-blk and virtio-rng interrupt update model. 5. Introduce new argument '-x vnet:<tap>' When virtio-net is enabled, rv32emu dynamically creates a virtio-mmio node in the generated device tree and assigns an MMIO base address and IRQ for the device. 6. Support coexistence with virtio-blk and virtio-rng Update the dynamic virtio-mmio device tree allocation path so virtio-net can coexist with existing virtio-blk and virtio-rng devices without reusing MMIO base addresses or IRQs. 7. Use virtio-net state Unlike semu's device integration model, rv32emu stores the virtio-net state in vm_attr_t so MMIO routing, interrupt routing, and device cleanup can access the same device instance. The emulator should be run with sudo when using the virtio-net TAP backend. This implementation is based on semu's virtio-net device, originally introduced by Jserv. Co-authored-by: Jim Huang <jserv@biilabs.io>
1 parent 02be10d commit 56a5c0b

9 files changed

Lines changed: 1002 additions & 5 deletions

File tree

src/devices/netdev.c

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* rv32emu is freely redistributable under the MIT License. See the file
3+
* "LICENSE" for information on usage and redistribution of this file.
4+
*/
5+
6+
#include "netdev.h"
7+
8+
#include <stdbool.h>
9+
#include <string.h>
10+
11+
#include "utils.h"
12+
13+
#if defined(__linux__) && !defined(__EMSCRIPTEN__)
14+
15+
#include <errno.h>
16+
#include <fcntl.h>
17+
#include <linux/if.h>
18+
#include <linux/if_tun.h>
19+
#include <stdlib.h>
20+
#include <sys/ioctl.h>
21+
#include <unistd.h>
22+
23+
static int net_init_tap(netdev_t *netdev)
24+
{
25+
net_tap_options_t *tap = (net_tap_options_t *) netdev->op;
26+
27+
tap->tap_fd = open("/dev/net/tun", O_RDWR);
28+
if (tap->tap_fd < 0) {
29+
rv_log_error("failed to open TAP device: %s", strerror(errno));
30+
return -1;
31+
}
32+
33+
struct ifreq ifreq = {
34+
.ifr_flags = IFF_TAP | IFF_NO_PI,
35+
};
36+
37+
strncpy(ifreq.ifr_name, "tap%d", sizeof(ifreq.ifr_name) - 1);
38+
39+
if (ioctl(tap->tap_fd, TUNSETIFF, &ifreq) < 0) {
40+
rv_log_error("failed to allocate TAP device: %s", strerror(errno));
41+
close(tap->tap_fd);
42+
tap->tap_fd = -1;
43+
return -1;
44+
}
45+
46+
rv_log_info("allocated TAP interface: %s", ifreq.ifr_name);
47+
48+
if (fcntl(tap->tap_fd, F_SETFL,
49+
fcntl(tap->tap_fd, F_GETFL, 0) | O_NONBLOCK) < 0) {
50+
rv_log_error("failed to set TAP non-blocking mode: %s",
51+
strerror(errno));
52+
close(tap->tap_fd);
53+
tap->tap_fd = -1;
54+
return -1;
55+
}
56+
57+
return 0;
58+
}
59+
60+
bool netdev_init(netdev_t *netdev, const char *net_type)
61+
{
62+
if (!netdev || !net_type || strcmp(net_type, "tap"))
63+
return false;
64+
65+
netdev->name = (char *) net_type;
66+
netdev->type = NETDEV_IMPL_tap;
67+
netdev->op = calloc(1, sizeof(net_tap_options_t));
68+
if (!netdev->op)
69+
return false;
70+
71+
if (net_init_tap(netdev) < 0) {
72+
free(netdev->op);
73+
netdev->op = NULL;
74+
return false;
75+
}
76+
77+
return true;
78+
}
79+
80+
void netdev_delete(netdev_t *netdev)
81+
{
82+
if (!netdev || !netdev->op)
83+
return;
84+
85+
switch (netdev->type) {
86+
case NETDEV_IMPL_tap: {
87+
net_tap_options_t *tap = (net_tap_options_t *) netdev->op;
88+
if (tap->tap_fd >= 0)
89+
close(tap->tap_fd);
90+
break;
91+
}
92+
default:
93+
break;
94+
}
95+
96+
free(netdev->op);
97+
netdev->op = NULL;
98+
}
99+
100+
#else
101+
102+
bool netdev_init(netdev_t *netdev, const char *net_type)
103+
{
104+
(void) netdev;
105+
(void) net_type;
106+
107+
rv_log_error("virtio-net TAP backend is only supported on Linux hosts");
108+
return false;
109+
}
110+
111+
void netdev_delete(netdev_t *netdev)
112+
{
113+
(void) netdev;
114+
}
115+
116+
#endif

src/devices/netdev.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* rv32emu is freely redistributable under the MIT License. See the file
3+
* "LICENSE" for information on usage and redistribution of this file.
4+
*/
5+
6+
#pragma once
7+
8+
#include <stdbool.h>
9+
10+
typedef struct netdev netdev_t;
11+
12+
typedef enum {
13+
NETDEV_IMPL_tap,
14+
} netdev_impl_t;
15+
16+
typedef struct {
17+
int tap_fd;
18+
} net_tap_options_t;
19+
20+
struct netdev {
21+
char *name;
22+
netdev_impl_t type;
23+
void *op;
24+
};
25+
26+
bool netdev_init(netdev_t *netdev, const char *net_type);
27+
28+
void netdev_delete(netdev_t *netdev);

0 commit comments

Comments
 (0)