diff --git a/docs/MANUAL.md b/docs/MANUAL.md index 3d54c77a..1eeac5a7 100644 --- a/docs/MANUAL.md +++ b/docs/MANUAL.md @@ -207,6 +207,7 @@ and implements the following devices: * Console * Block * Sound +* Network These devices are implemented using MMIO, we do not use any PCI devices at this stage. @@ -261,6 +262,18 @@ There are no feature bits to implement. The legacy interface is not supported. The sound device communicates with a hardware sound device via a sDDF sound virtualiser. +### Network + +The network device makes use of the 'net' device class in sDDF. + +The device supports `VIRTIO_NET_F_MAC`. No other features are available. + +The legacy interface is not supported. + +The network device communicates with a hardware network card via a pair of sDDF RX and TX +net virtualisers. In the future, this communication may be done through +intermediary components such as a virtual network switch (VSwitch). + # Adding platform support This section will describe how to add support for a new platform to the `simple` diff --git a/include/libvmm/virtio/net.h b/include/libvmm/virtio/net.h new file mode 100644 index 00000000..eb7294cd --- /dev/null +++ b/include/libvmm/virtio/net.h @@ -0,0 +1,240 @@ +/* SPDX-License-Identifier: BSD-3-Clause + Copyright Linux */ + +/* This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of IBM nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ + +#pragma once + +#include +#include +#include + +/* The feature bitmap for virtio net */ +#define VIRTIO_NET_F_CSUM 0 /* Host handles pkts w/ partial csum */ +#define VIRTIO_NET_F_GUEST_CSUM 1 /* Guest handles pkts w/ partial csum */ +#define VIRTIO_NET_F_MAC 5 /* Host has given MAC address. */ +#define VIRTIO_NET_F_GSO 6 /* Host handles pkts w/ any GSO type */ +#define VIRTIO_NET_F_GUEST_TSO4 7 /* Guest can handle TSOv4 in. */ +#define VIRTIO_NET_F_GUEST_TSO6 8 /* Guest can handle TSOv6 in. */ +#define VIRTIO_NET_F_GUEST_ECN 9 /* Guest can handle TSO[6] w/ ECN in. */ +#define VIRTIO_NET_F_GUEST_UFO 10 /* Guest can handle UFO in. */ +#define VIRTIO_NET_F_HOST_TSO4 11 /* Host can handle TSOv4 in. */ +#define VIRTIO_NET_F_HOST_TSO6 12 /* Host can handle TSOv6 in. */ +#define VIRTIO_NET_F_HOST_ECN 13 /* Host can handle TSO[6] w/ ECN in. */ +#define VIRTIO_NET_F_HOST_UFO 14 /* Host can handle UFO in. */ +#define VIRTIO_NET_F_MRG_RXBUF 15 /* Host can merge receive buffers. */ +#define VIRTIO_NET_F_STATUS 16 /* virtio_net_config.status available */ +#define VIRTIO_NET_F_CTRL_VQ 17 /* Control channel available */ +#define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */ +#define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */ +#define VIRTIO_NET_F_CTRL_RX_EXTRA 20 /* Extra RX mode control support */ +#define VIRTIO_NET_F_GUEST_ANNOUNCE 21 /* Guest can announce device on the network */ +#define VIRTIO_NET_F_MQ 22 /* Device supports Receive Flow Steering */ +#define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */ + +#define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ +#define VIRTIO_NET_S_ANNOUNCE 2 /* Announcement is needed */ + +#define VIRTIO_NET_CONFIG_MAC_SZ 6 + +struct virtio_net_config { + /* The config defining mac address (if VIRTIO_NET_F_MAC) */ + uint8_t mac[VIRTIO_NET_CONFIG_MAC_SZ]; + /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ + // uint16_t status; + /* Maximum number of each of transmit and receive queues; + * see VIRTIO_NET_F_MQ and VIRTIO_NET_CTRL_MQ. + * Legal values are between 1 and 0x8000 + */ + // uint16_t max_virtqueue_pairs; +} __attribute__((packed)); + +/* This header comes first in the scatter-gather list. + * If VIRTIO_F_ANY_LAYOUT is not negotiated, it must + * be the first element of the scatter-gather list. If you don't + * specify GSO or CSUM features, you can simply ignore the header. */ +struct virtio_net_hdr { +#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 // Use csum_start, csum_offset +#define VIRTIO_NET_HDR_F_DATA_VALID 2 // Csum is valid + uint8_t flags; +#define VIRTIO_NET_HDR_GSO_NONE 0 // Not a GSO frame +#define VIRTIO_NET_HDR_GSO_TCPV4 1 // GSO frame, IPv4 TCP (TSO) +#define VIRTIO_NET_HDR_GSO_UDP 3 // GSO frame, IPv4 UDP (UFO) +#define VIRTIO_NET_HDR_GSO_TCPV6 4 // GSO frame, IPv6 TCP +#define VIRTIO_NET_HDR_GSO_ECN 0x80 // TCP has ECN set + uint8_t gso_type; + uint16_t hdr_len; /* Ethernet + IP + tcp/udp hdrs */ + uint16_t gso_size; /* Bytes to append to hdr_len per frame */ + uint16_t csum_start; /* Position to start checksumming from */ + uint16_t csum_offset; /* Offset after that to place checksum */ +}; + +/* The legacy driver only presented num_buffers in the struct virtio_net_hdr + * when VIRTIO_NET_F_MRG_RXBUF was negotiated; without that feature the + * structure was 2 bytes shorter. So please use this one if you're not dealing + * with a legacy driver + */ +struct virtio_net_hdr_mrg_rxbuf { + struct virtio_net_hdr hdr; + uint16_t num_buffers; /* Number of merged rx buffers */ +}; + +/* + * Control virtqueue data structures + * + * The control virtqueue expects a header in the first sg entry + * and an ack/status response in the last entry. Data for the + * command goes in between. + */ +struct virtio_net_ctrl_hdr { + uint8_t class; + uint8_t cmd; +} __attribute__((packed)); + +typedef uint8_t virtio_net_ctrl_ack; + +#define VIRTIO_NET_OK 0 +#define VIRTIO_NET_ERR 1 + +/* + * Control the RX mode, ie. promisucous, allmulti, etc... + * All commands require an "out" sg entry containing a 1 byte + * state value, zero = disable, non-zero = enable. Commands + * 0 and 1 are supported with the VIRTIO_NET_F_CTRL_RX feature. + * Commands 2-5 are added with VIRTIO_NET_F_CTRL_RX_EXTRA. + */ +#define VIRTIO_NET_CTRL_RX 0 +#define VIRTIO_NET_CTRL_RX_PROMISC 0 +#define VIRTIO_NET_CTRL_RX_ALLMULTI 1 +#define VIRTIO_NET_CTRL_RX_ALLUNI 2 +#define VIRTIO_NET_CTRL_RX_NOMULTI 3 +#define VIRTIO_NET_CTRL_RX_NOUNI 4 +#define VIRTIO_NET_CTRL_RX_NOBCAST 5 + +/* + * Control the MAC + * + * The MAC filter table is managed by the hypervisor, the guest should + * assume the size is infinite. Filtering should be considered + * non-perfect, ie. based on hypervisor resources, the guest may + * received packets from sources not specified in the filter list. + * + * In addition to the class/cmd header, the TABLE_SET command requires + * two out scatterlists. Each contains a 4 byte count of entries followed + * by a concatenated byte stream of the ETH_ALEN MAC addresses. The + * first sg list contains unicast addresses, the second is for multicast. + * This functionality is present if the VIRTIO_NET_F_CTRL_RX feature + * is available. + * + * The ADDR_SET command requests one out scatterlist, it contains a + * 6 bytes MAC address. This functionality is present if the + * VIRTIO_NET_F_CTRL_MAC_ADDR feature is available. + */ +struct virtio_net_ctrl_mac { + uint32_t entries; + uint8_t macs[][6]; +} __attribute__((packed)); + +#define VIRTIO_NET_CTRL_MAC 1 +#define VIRTIO_NET_CTRL_MAC_TABLE_SET 0 +#define VIRTIO_NET_CTRL_MAC_ADDR_SET 1 + +/* + * Control VLAN filtering + * + * The VLAN filter table is controlled via a simple ADD/DEL interface. + * VLAN IDs not added may be filterd by the hypervisor. Del is the + * opposite of add. Both commands expect an out entry containing a 2 + * byte VLAN ID. VLAN filterting is available with the + * VIRTIO_NET_F_CTRL_VLAN feature bit. + */ +#define VIRTIO_NET_CTRL_VLAN 2 +#define VIRTIO_NET_CTRL_VLAN_ADD 0 +#define VIRTIO_NET_CTRL_VLAN_DEL 1 + +/* + * Control link announce acknowledgement + * + * The command VIRTIO_NET_CTRL_ANNOUNCE_ACK is used to indicate that + * driver has recevied the notification; device would clear the + * VIRTIO_NET_S_ANNOUNCE bit in the status field after it receives + * this command. + */ +#define VIRTIO_NET_CTRL_ANNOUNCE 3 +#define VIRTIO_NET_CTRL_ANNOUNCE_ACK 0 + +/* + * Control Receive Flow Steering + * + * The command VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET + * enables Receive Flow Steering, specifying the number of the transmit and + * receive queues that will be used. After the command is consumed and acked by + * the device, the device will not steer new packets on receive virtqueues + * other than specified nor read from transmit virtqueues other than specified. + * Accordingly, driver should not transmit new packets on virtqueues other than + * specified. + */ +struct virtio_net_ctrl_mq { + uint16_t virtqueue_pairs; +}; + +#define VIRTIO_NET_CTRL_MQ 4 +#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET 0 +#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN 1 +#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX 0x8000 + +#define VIRTIO_NET_RX_VIRTQ 0 +#define VIRTIO_NET_TX_VIRTQ 1 +#define VIRTIO_NET_NUM_VIRTQ 2 + +struct virtio_net_device { + struct virtio_device virtio_device; + + struct virtio_net_config config; + struct virtio_queue_handler vqs[VIRTIO_NET_NUM_VIRTQ]; + + net_queue_handle_t rx; + net_queue_handle_t tx; + void *rx_data; + void *tx_data; + microkit_channel tx_ch; + microkit_channel rx_ch; +}; + +bool virtio_mmio_net_init(struct virtio_net_device *dev, + uint8_t mac[VIRTIO_NET_CONFIG_MAC_SZ], + uintptr_t region_base, + uintptr_t region_size, + size_t virq, + net_queue_handle_t *rx, + net_queue_handle_t *tx, + uintptr_t rx_data, + uintptr_t tx_data, + microkit_channel rx_ch, + microkit_channel tx_ch); + +bool virtio_net_handle_rx(struct virtio_net_device *dev); diff --git a/include/libvmm/virtio/virtio.h b/include/libvmm/virtio/virtio.h index 0a6ba442..7dbb0f20 100644 --- a/include/libvmm/virtio/virtio.h +++ b/include/libvmm/virtio/virtio.h @@ -3,6 +3,7 @@ #include #include #include +#include #include /* diff --git a/src/virtio/net.c b/src/virtio/net.c new file mode 100644 index 00000000..a47e4c96 --- /dev/null +++ b/src/virtio/net.c @@ -0,0 +1,404 @@ +/* + * Copyright 2024, UNSW (ABN 57 195 873 179) + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#include +#include +#include +#include +#include +#include +#include + +/* Uncomment this to enable debug logging */ +// #define DEBUG_NET + +#if defined(DEBUG_NET) +#define LOG_NET(...) do{ printf("VIRTIO(NET): "); printf(__VA_ARGS__); }while(0) +#else +#define LOG_NET(...) do{}while(0) +#endif + +#define LOG_NET_ERR(...) do{ printf("VIRTIO(NET)|ERROR: "); printf(__VA_ARGS__); }while(0) + + +static inline struct virtio_net_device *device_state(struct virtio_device *dev) +{ + return (struct virtio_net_device *)dev->device_data; +} + +static void virtio_net_reset(struct virtio_device *dev) +{ + LOG_NET("operation: reset\n"); + for (int i = 0; i < dev->num_vqs; i++) { + dev->vqs[i].ready = false; + dev->vqs[i].last_idx = 0; + } +} + +static bool driver_ok(struct virtio_device *dev) +{ + return (dev->data.Status & VIRTIO_CONFIG_S_DRIVER_OK) && + (dev->data.Status & VIRTIO_CONFIG_S_FEATURES_OK); +} + +static bool virtio_net_get_device_features(struct virtio_device *dev, uint32_t *features) +{ + LOG_NET("operation: get device features\n"); + + if (dev->data.Status & VIRTIO_CONFIG_S_FEATURES_OK) { + LOG_NET_ERR("Driver tried to read device features after FEATURES_OK\n"); + } + + switch (dev->data.DeviceFeaturesSel) { + /* Feature bits 0 to 31 */ + case 0: + *features = BIT_LOW(VIRTIO_NET_F_MAC); + break; + /* Features bits 32 to 63 */ + case 1: + *features = BIT_HIGH(VIRTIO_F_VERSION_1); + break; + default: + LOG_NET_ERR("Bad DeviceFeaturesSel 0x%x\n", dev->data.DeviceFeaturesSel); + return false; + } + return true; +} + + +static bool virtio_net_set_driver_features(struct virtio_device *dev, uint32_t features) +{ + bool success = true; + + switch (dev->data.DriverFeaturesSel) { + /* Feature bits 0 to 31 */ + case 0: + /** F_MAC is required */ + success = (features == BIT_LOW(VIRTIO_NET_F_MAC)); + break; + + /* Features bits 32 to 63 */ + case 1: + success = (features == BIT_HIGH(VIRTIO_F_VERSION_1)); + break; + + default: + LOG_NET_ERR("Bad DriverFeaturesSel 0x%x\n", dev->data.DriverFeaturesSel); + success = false; + } + if (success) { + dev->data.features_happy = 1; + } + return success; +} + +static bool virtio_net_get_device_config(struct virtio_device *dev, + uint32_t offset, + uint32_t *ret_val) +{ + struct virtio_net_config *config = &device_state(dev)->config; + + uint32_t word_offset = (offset - REG_VIRTIO_MMIO_CONFIG) / sizeof(uint32_t); + switch (word_offset) { + case 0: + *ret_val = config->mac[0]; + *ret_val |= config->mac[1] << 8; + *ret_val |= config->mac[2] << 16; + *ret_val |= config->mac[3] << 24; + break; + + case 1: + *ret_val = config->mac[4]; + *ret_val |= config->mac[5] << 8; + break; + + default: + LOG_NET_ERR("Unknown device config register: 0x%x\n", offset); + return false; + } + return true; +} + +static bool virtio_net_set_device_config(struct virtio_device *dev, uint32_t offset, uint32_t val) +{ + LOG_NET_ERR("All configuration fields are readonly\n"); + return false; +} + +static void virtq_enqueue_used(struct virtq *virtq, uint32_t desc_head, uint32_t bytes_written) +{ + struct virtq_used_elem *used_elem = &virtq->used->ring[virtq->used->idx % virtq->num]; + used_elem->id = desc_head; + used_elem->len = bytes_written; + virtq->used->idx++; +} + +static bool virtio_net_respond(struct virtio_device *dev) +{ + dev->data.InterruptStatus = BIT_LOW(0); + bool success = virq_inject(GUEST_VCPU_ID, dev->virq); + assert(success); + + return success; +} + +static void handle_tx_msg(struct virtio_device *dev, + struct virtq *virtq, + uint16_t desc_head, + bool *notify_tx_server, + bool *respond_to_guest) +{ + struct virtio_net_device *state = device_state(dev); + + if (net_queue_full_active(&state->tx)) { + goto fail; + } + + net_buff_desc_t sddf_buffer; + int error = net_dequeue_free(&state->tx, &sddf_buffer); + if (error) { + goto fail; + } + + void *dest_buf = state->tx_data + sddf_buffer.io_or_offset; + + uint32_t written = 0; + uint32_t dest_remaining = NET_BUFFER_SIZE; + + /* Strip virtio header before copying to sDDF */ + uint32_t skip_remaining = sizeof(struct virtio_net_hdr_mrg_rxbuf); + + struct virtq_desc *desc = &virtq->desc[desc_head]; + + while (dest_remaining > 0) { + uint32_t skipping = 0; + /* Work out how much of this descriptor must be skipped */ + skipping = MIN(skip_remaining, desc->len); + /* Truncate packets that are large than BUF_SIZE */ + uint32_t writing = MIN(dest_remaining, desc->len - skipping); + + memcpy(dest_buf + written, (void *)desc->addr + skipping, writing); + + skip_remaining -= skipping; + written += writing; + dest_remaining -= writing; + + if (desc->flags & VIRTQ_DESC_F_NEXT) { + desc = &virtq->desc[desc->next]; + } else { + break; + } + } + + sddf_buffer.len = written; + error = net_enqueue_active(&state->tx, sddf_buffer); + /* This cannot fail as we check above */ + assert(!error); + + virtq_enqueue_used(virtq, desc_head, written); + *respond_to_guest = true; + *notify_tx_server = true; + return; + +fail: + virtq_enqueue_used(virtq, desc_head, 0); + *respond_to_guest = true; +} + +static bool virtio_net_queue_notify(struct virtio_device *dev) +{ + struct virtio_net_device *state = device_state(dev); + + if (!driver_ok(dev)) { + LOG_NET_ERR("Driver not ready\n"); + return false; + } + if (dev->data.QueueSel != VIRTIO_NET_TX_VIRTQ) { + LOG_NET_ERR("Invalid queue\n"); + return false; + } + if (!dev->vqs[VIRTIO_NET_TX_VIRTQ].ready) { + LOG_NET_ERR("TX virtq not ready\n"); + return false; + } + + virtio_queue_handler_t *vq = &dev->vqs[VIRTIO_NET_TX_VIRTQ]; + struct virtq *virtq = &vq->virtq; + + uint16_t guest_idx = virtq->avail->idx; + uint16_t idx = vq->last_idx; + + bool notify_tx_server = false; + bool respond_to_guest = false; + + for (; idx != guest_idx; idx++) { + uint16_t desc_head = virtq->avail->ring[idx % virtq->num]; + handle_tx_msg(dev, virtq, desc_head, ¬ify_tx_server, &respond_to_guest); + } + + vq->last_idx = idx; + + if (notify_tx_server && net_require_signal_active(&state->tx)) { + net_cancel_signal_active(&state->tx); + microkit_notify(state->tx_ch); + } + + bool success = true; + if (respond_to_guest) { + success = virtio_net_respond(dev); + } + + return success; +} + +static uint32_t copy_rx(struct virtq *virtq, + uint16_t *curr_desc_head, + uint32_t *desc_copied, + const void *buf, uint32_t size) +{ + uint32_t copied = 0; + do { + uint32_t copying = MIN(size - copied, virtq->desc[*curr_desc_head].len - *desc_copied); + + memcpy((void *)virtq->desc[*curr_desc_head].addr + *desc_copied, buf + copied, copying); + + copied += copying; + *desc_copied += copying; + + if (*desc_copied == virtq->desc[*curr_desc_head].len) { + if (!(virtq->desc[*curr_desc_head].flags & VIRTQ_DESC_F_NEXT)) { + break; + } + *curr_desc_head = virtq->desc[*curr_desc_head].next; + *desc_copied = 0; + } + } while (copied < size); + + return copied; +} + +static void handle_rx_buffer(struct virtio_device *dev, + uint64_t buf_offset, uint32_t size, + bool *respond_to_guest) +{ + struct virtio_net_device *state = device_state(dev); + + virtio_queue_handler_t *vq = &dev->vqs[VIRTIO_NET_RX_VIRTQ]; + struct virtq *virtq = &vq->virtq; + + uint16_t guest_idx = virtq->avail->idx; + uint16_t idx = vq->last_idx; + + if (idx == guest_idx) { + /* vq is full or not initialised, drop the packet */ + return; + } + + /* Read the head of the descriptor chain */ + uint16_t desc_head = virtq->avail->ring[idx % virtq->num]; + uint16_t curr_desc_head = desc_head; + + uint32_t copied = 0; + /* Amount of the current descriptor copied */ + uint32_t desc_copied = 0; + + struct virtio_net_hdr_mrg_rxbuf virtio_hdr = {0}; + virtio_hdr.num_buffers = 1; + + copied += copy_rx(virtq, &curr_desc_head, &desc_copied, &virtio_hdr, sizeof(struct virtio_net_hdr_mrg_rxbuf)); + copied += copy_rx(virtq, &curr_desc_head, &desc_copied, state->rx_data + buf_offset, size); + + /* Put it in the used ring */ + virtq_enqueue_used(virtq, desc_head, copied); + + /* Record that we've used this descriptor chain now */ + vq->last_idx++; + + *respond_to_guest = true; +} + +bool virtio_net_handle_rx(struct virtio_net_device *state) +{ + struct virtio_device *dev = &state->virtio_device; + + if (!driver_ok(dev)) { + return false; + } + if (!dev->vqs[VIRTIO_NET_RX_VIRTQ].ready) { + /* vq is not initialised, drop the packet */ + return false; + } + + net_buff_desc_t sddf_buffer; + bool reprocess = true; + bool respond_to_guest = false; + + while (reprocess) { + while (net_dequeue_active(&state->rx, &sddf_buffer) != -1) { + /* On failure, drop packet since we don't know how long until next interrupt */ + handle_rx_buffer(dev, sddf_buffer.io_or_offset, sddf_buffer.len, &respond_to_guest); + + sddf_buffer.len = 0; + net_enqueue_free(&state->rx, sddf_buffer); + } + + net_request_signal_active(&state->rx); + reprocess = false; + + if (!net_queue_empty_active(&state->rx)) { + net_cancel_signal_active(&state->rx); + reprocess = true; + } + } + + if (respond_to_guest) { + return virtio_net_respond(dev); + } + + return true; +} + +static virtio_device_funs_t functions = { + .device_reset = virtio_net_reset, + .get_device_features = virtio_net_get_device_features, + .set_driver_features = virtio_net_set_driver_features, + .get_device_config = virtio_net_get_device_config, + .set_device_config = virtio_net_set_device_config, + .queue_notify = virtio_net_queue_notify, +}; + +bool virtio_mmio_net_init(struct virtio_net_device *net_dev, + uint8_t mac[VIRTIO_NET_CONFIG_MAC_SZ], + uintptr_t region_base, + uintptr_t region_size, + size_t virq, + net_queue_handle_t *rx, + net_queue_handle_t *tx, + uintptr_t rx_data, + uintptr_t tx_data, + microkit_channel rx_ch, + microkit_channel tx_ch) +{ + struct virtio_device *dev = &net_dev->virtio_device; + + dev->data.DeviceID = DEVICE_ID_VIRTIO_NET; + dev->data.VendorID = VIRTIO_MMIO_DEV_VENDOR_ID; + dev->funs = &functions; + dev->vqs = net_dev->vqs; + dev->num_vqs = VIRTIO_NET_NUM_VIRTQ; + dev->virq = virq; + dev->device_data = net_dev; + + memcpy(net_dev->config.mac, mac, VIRTIO_NET_CONFIG_MAC_SZ); + + net_dev->rx = *rx; + net_dev->tx = *tx; + net_dev->rx_data = (void *)rx_data; + net_dev->tx_data = (void *)tx_data; + net_dev->rx_ch = rx_ch; + net_dev->tx_ch = tx_ch; + + return virtio_mmio_register_device(dev, region_base, region_size, virq); +} diff --git a/vmm.mk b/vmm.mk index a7a8891f..40a2b87c 100644 --- a/vmm.mk +++ b/vmm.mk @@ -37,6 +37,7 @@ ARCH_INDEP_FILES := src/util/printf.c \ src/virtio/block.c \ src/virtio/console.c \ src/virtio/mmio.c \ + src/virtio/net.c \ src/virtio/sound.c \ src/guest.c