X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/73795cea968ba2e9342a3122cd66d52d13d46a61..771b6ed37e3aa188a7485560b949a41c6cf174dc:/hw/net/virtio-net.c diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 3c0342e17a..e574bd4322 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -22,7 +22,8 @@ #include "net/vhost_net.h" #include "hw/virtio/virtio-bus.h" #include "qapi/qmp/qjson.h" -#include "monitor/monitor.h" +#include "qapi-event.h" +#include "hw/virtio/virtio-access.h" #define VIRTIO_NET_VM_VERSION 11 @@ -72,8 +73,8 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) VirtIONet *n = VIRTIO_NET(vdev); struct virtio_net_config netcfg; - stw_p(&netcfg.status, n->status); - stw_p(&netcfg.max_virtqueue_pairs, n->max_queues); + virtio_stw_p(vdev, &netcfg.status, n->status); + virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queues); memcpy(netcfg.mac, n->mac, ETH_ALEN); memcpy(config, &netcfg, n->config_size); } @@ -99,20 +100,23 @@ static bool virtio_net_started(VirtIONet *n, uint8_t status) (n->status & VIRTIO_NET_S_LINK_UP) && vdev->vm_running; } +static void virtio_net_announce_timer(void *opaque) +{ + VirtIONet *n = opaque; + VirtIODevice *vdev = VIRTIO_DEVICE(n); + + n->announce_counter--; + n->status |= VIRTIO_NET_S_ANNOUNCE; + virtio_notify_config(vdev); +} + static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) { VirtIODevice *vdev = VIRTIO_DEVICE(n); NetClientState *nc = qemu_get_queue(n->nic); int queues = n->multiqueue ? n->max_queues : 1; - if (!nc->peer) { - return; - } - if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { - return; - } - - if (!tap_get_vhost_net(nc->peer)) { + if (!get_vhost_net(nc->peer)) { return; } @@ -121,10 +125,23 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) return; } if (!n->vhost_started) { - int r; - if (!vhost_net_query(tap_get_vhost_net(nc->peer), vdev)) { + int r, i; + + if (!vhost_net_query(get_vhost_net(nc->peer), vdev)) { return; } + + /* Any packets outstanding? Purge them to avoid touching rings + * when vhost is running. + */ + for (i = 0; i < queues; i++) { + NetClientState *qnc = qemu_get_subqueue(n->nic, i); + + /* Purge both directions: TX and RX. */ + qemu_net_queue_purge(qnc->peer->incoming_queue, qnc); + qemu_net_queue_purge(qnc->incoming_queue, qnc->peer); + } + n->vhost_started = 1; r = vhost_net_start(vdev, n->nic->ncs, queues); if (r < 0) { @@ -196,19 +213,12 @@ static void virtio_net_set_link_status(NetClientState *nc) static void rxfilter_notify(NetClientState *nc) { - QObject *event_data; VirtIONet *n = qemu_get_nic_opaque(nc); if (nc->rxfilter_notify_enabled) { gchar *path = object_get_canonical_path(OBJECT(n->qdev)); - if (n->netclient_name) { - event_data = qobject_from_jsonf("{ 'name': %s, 'path': %s }", - n->netclient_name, path); - } else { - event_data = qobject_from_jsonf("{ 'path': %s }", path); - } - monitor_protocol_event(QEVENT_NIC_RX_FILTER_CHANGED, event_data); - qobject_decref(event_data); + qapi_event_send_nic_rx_filter_changed(!!n->netclient_name, + n->netclient_name, path, &error_abort); g_free(path); /* disable event notification to avoid events flooding */ @@ -222,13 +232,33 @@ static char *mac_strdup_printf(const uint8_t *mac) mac[1], mac[2], mac[3], mac[4], mac[5]); } +static intList *get_vlan_table(VirtIONet *n) +{ + intList *list, *entry; + int i, j; + + list = NULL; + for (i = 0; i < MAX_VLAN >> 5; i++) { + for (j = 0; n->vlans[i] && j <= 0x1f; j++) { + if (n->vlans[i] & (1U << j)) { + entry = g_malloc0(sizeof(*entry)); + entry->value = (i << 5) + j; + entry->next = list; + list = entry; + } + } + } + + return list; +} + static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc) { VirtIONet *n = qemu_get_nic_opaque(nc); + VirtIODevice *vdev = VIRTIO_DEVICE(n); RxFilterInfo *info; strList *str_list, *entry; - intList *int_list, *int_entry; - int i, j; + int i; info = g_malloc0(sizeof(*info)); info->name = g_strdup(nc->name); @@ -273,19 +303,15 @@ static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc) str_list = entry; } info->multicast_table = str_list; + info->vlan_table = get_vlan_table(n); - int_list = NULL; - for (i = 0; i < MAX_VLAN >> 5; i++) { - for (j = 0; n->vlans[i] && j < 0x1f; j++) { - if (n->vlans[i] & (1U << j)) { - int_entry = g_malloc0(sizeof(*int_entry)); - int_entry->value = (i << 5) + j; - int_entry->next = int_list; - int_list = int_entry; - } - } + if (!((1 << VIRTIO_NET_F_CTRL_VLAN) & vdev->guest_features)) { + info->vlan = RX_STATE_ALL; + } else if (!info->vlan_table) { + info->vlan = RX_STATE_NONE; + } else { + info->vlan = RX_STATE_NORMAL; } - info->vlan_table = int_list; /* enable event notification after query */ nc->rxfilter_notify_enabled = 1; @@ -306,6 +332,9 @@ static void virtio_net_reset(VirtIODevice *vdev) n->nobcast = 0; /* multiqueue is disabled by default */ n->curr_queues = 1; + timer_del(n->announce_timer); + n->announce_counter = 0; + n->status &= ~VIRTIO_NET_S_ANNOUNCE; /* Flush any MAC and VLAN filter table state */ n->mac_table.in_use = 0; @@ -397,12 +426,15 @@ static int peer_detach(VirtIONet *n, int index) static void virtio_net_set_queues(VirtIONet *n) { int i; + int r; for (i = 0; i < n->max_queues; i++) { if (i < n->curr_queues) { - assert(!peer_attach(n, i)); + r = peer_attach(n, i); + assert(!r); } else { - assert(!peer_detach(n, i)); + r = peer_detach(n, i); + assert(!r); } } } @@ -433,13 +465,10 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) features &= ~(0x1 << VIRTIO_NET_F_HOST_UFO); } - if (!nc->peer || nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { - return features; - } - if (!tap_get_vhost_net(nc->peer)) { + if (!get_vhost_net(nc->peer)) { return features; } - return vhost_net_get_features(tap_get_vhost_net(nc->peer), features); + return vhost_net_get_features(get_vhost_net(nc->peer), features); } static uint32_t virtio_net_bad_features(VirtIODevice *vdev) @@ -503,13 +532,16 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) for (i = 0; i < n->max_queues; i++) { NetClientState *nc = qemu_get_subqueue(n->nic, i); - if (!nc->peer || nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { + if (!get_vhost_net(nc->peer)) { continue; } - if (!tap_get_vhost_net(nc->peer)) { - continue; - } - vhost_net_ack_features(tap_get_vhost_net(nc->peer), features); + vhost_net_ack_features(get_vhost_net(nc->peer), features); + } + + if ((1 << VIRTIO_NET_F_CTRL_VLAN) & features) { + memset(n->vlans, 0, MAX_VLAN >> 3); + } else { + memset(n->vlans, 0xff, MAX_VLAN >> 3); } } @@ -586,6 +618,7 @@ static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd, static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, struct iovec *iov, unsigned int iov_cnt) { + VirtIODevice *vdev = VIRTIO_DEVICE(n); struct virtio_net_ctrl_mac mac_data; size_t s; NetClientState *nc = qemu_get_queue(n->nic); @@ -614,7 +647,7 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries, sizeof(mac_data.entries)); - mac_data.entries = ldl_p(&mac_data.entries); + mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries); if (s != sizeof(mac_data.entries)) { goto error; } @@ -641,7 +674,7 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries, sizeof(mac_data.entries)); - mac_data.entries = ldl_p(&mac_data.entries); + mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries); if (s != sizeof(mac_data.entries)) { goto error; } @@ -652,7 +685,7 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, goto error; } - if (in_use + mac_data.entries <= MAC_TABLE_ENTRIES) { + if (mac_data.entries <= MAC_TABLE_ENTRIES - in_use) { s = iov_to_buf(iov, iov_cnt, 0, &macs[in_use * ETH_ALEN], mac_data.entries * ETH_ALEN); if (s != mac_data.entries * ETH_ALEN) { @@ -681,12 +714,13 @@ error: static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd, struct iovec *iov, unsigned int iov_cnt) { + VirtIODevice *vdev = VIRTIO_DEVICE(n); uint16_t vid; size_t s; NetClientState *nc = qemu_get_queue(n->nic); s = iov_to_buf(iov, iov_cnt, 0, &vid, sizeof(vid)); - vid = lduw_p(&vid); + vid = virtio_lduw_p(vdev, &vid); if (s != sizeof(vid)) { return VIRTIO_NET_ERR; } @@ -706,6 +740,23 @@ static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd, return VIRTIO_NET_OK; } +static int virtio_net_handle_announce(VirtIONet *n, uint8_t cmd, + struct iovec *iov, unsigned int iov_cnt) +{ + if (cmd == VIRTIO_NET_CTRL_ANNOUNCE_ACK && + n->status & VIRTIO_NET_S_ANNOUNCE) { + n->status &= ~VIRTIO_NET_S_ANNOUNCE; + if (n->announce_counter) { + timer_mod(n->announce_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + self_announce_delay(n->announce_counter)); + } + return VIRTIO_NET_OK; + } else { + return VIRTIO_NET_ERR; + } +} + static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd, struct iovec *iov, unsigned int iov_cnt) { @@ -723,7 +774,7 @@ static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd, return VIRTIO_NET_ERR; } - queues = lduw_p(&mq.virtqueue_pairs); + queues = virtio_lduw_p(vdev, &mq.virtqueue_pairs); if (queues < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN || queues > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX || @@ -747,7 +798,7 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) virtio_net_ctrl_ack status = VIRTIO_NET_ERR; VirtQueueElement elem; size_t s; - struct iovec *iov; + struct iovec *iov, *iov2; unsigned int iov_cnt; while (virtqueue_pop(vq, &elem)) { @@ -757,8 +808,8 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) exit(1); } - iov = elem.out_sg; iov_cnt = elem.out_num; + iov2 = iov = g_memdup(elem.out_sg, sizeof(struct iovec) * elem.out_num); s = iov_to_buf(iov, iov_cnt, 0, &ctrl, sizeof(ctrl)); iov_discard_front(&iov, &iov_cnt, sizeof(ctrl)); if (s != sizeof(ctrl)) { @@ -769,6 +820,8 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) status = virtio_net_handle_mac(n, ctrl.cmd, iov, iov_cnt); } else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) { status = virtio_net_handle_vlan_table(n, ctrl.cmd, iov, iov_cnt); + } else if (ctrl.class == VIRTIO_NET_CTRL_ANNOUNCE) { + status = virtio_net_handle_announce(n, ctrl.cmd, iov, iov_cnt); } else if (ctrl.class == VIRTIO_NET_CTRL_MQ) { status = virtio_net_handle_mq(n, ctrl.cmd, iov, iov_cnt); } else if (ctrl.class == VIRTIO_NET_CTRL_GUEST_OFFLOADS) { @@ -780,6 +833,7 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) virtqueue_push(vq, &elem, sizeof(status)); virtio_notify(vdev, vq); + g_free(iov2); } } @@ -838,6 +892,14 @@ static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize) return 1; } +static void virtio_net_hdr_swap(VirtIODevice *vdev, struct virtio_net_hdr *hdr) +{ + virtio_tswap16s(vdev, &hdr->hdr_len); + virtio_tswap16s(vdev, &hdr->gso_size); + virtio_tswap16s(vdev, &hdr->csum_start); + virtio_tswap16s(vdev, &hdr->csum_offset); +} + /* dhclient uses AF_PACKET but doesn't pass auxdata to the kernel so * it never finds out that the packets don't have valid checksums. This * causes dhclient to get upset. Fedora's carried a patch for ages to @@ -873,6 +935,7 @@ static void receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt, void *wbuf = (void *)buf; work_around_broken_dhclient(wbuf, wbuf + n->host_hdr_len, size - n->host_hdr_len); + virtio_net_hdr_swap(VIRTIO_DEVICE(n), wbuf); iov_from_buf(iov, iov_cnt, 0, buf, sizeof(struct virtio_net_hdr)); } else { struct virtio_net_hdr hdr = { @@ -1022,7 +1085,7 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t } if (mhdr_cnt) { - stw_p(&mhdr.num_buffers, i); + virtio_stw_p(vdev, &mhdr.num_buffers, i); iov_from_buf(mhdr_sg, mhdr_cnt, 0, &mhdr.num_buffers, sizeof mhdr.num_buffers); @@ -1063,8 +1126,6 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) return num_packets; } - assert(vdev->vm_running); - if (q->async_tx.elem.out_num) { virtio_queue_set_notification(q->tx_vq, 0); return num_packets; @@ -1081,6 +1142,14 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) exit(1); } + if (n->has_vnet_hdr) { + if (out_sg[0].iov_len < n->guest_hdr_len) { + error_report("virtio-net header incorrect"); + exit(1); + } + virtio_net_hdr_swap(vdev, (void *) out_sg[0].iov_base); + } + /* * If host wants to see the guest header as is, we can * pass it on unchanged. Otherwise, copy just the parts @@ -1167,7 +1236,12 @@ static void virtio_net_tx_timer(void *opaque) VirtIONetQueue *q = opaque; VirtIONet *n = q->n; VirtIODevice *vdev = VIRTIO_DEVICE(n); - assert(vdev->vm_running); + /* This happens when device was stopped but BH wasn't. */ + if (!vdev->vm_running) { + /* Make sure tx waiting is set, so we'll run when restarted. */ + assert(q->tx_waiting); + return; + } q->tx_waiting = 0; @@ -1187,7 +1261,12 @@ static void virtio_net_tx_bh(void *opaque) VirtIODevice *vdev = VIRTIO_DEVICE(n); int32_t ret; - assert(vdev->vm_running); + /* This happens when device was stopped but BH wasn't. */ + if (!vdev->vm_running) { + /* Make sure tx waiting is set, so we'll run when restarted. */ + assert(q->tx_waiting); + return; + } q->tx_waiting = 0; @@ -1260,7 +1339,6 @@ static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue) static void virtio_net_save(QEMUFile *f, void *opaque) { - int i; VirtIONet *n = opaque; VirtIODevice *vdev = VIRTIO_DEVICE(n); @@ -1268,6 +1346,12 @@ static void virtio_net_save(QEMUFile *f, void *opaque) * it might keep writing to memory. */ assert(!n->vhost_started); virtio_save(vdev, f); +} + +static void virtio_net_save_device(VirtIODevice *vdev, QEMUFile *f) +{ + VirtIONet *n = VIRTIO_NET(vdev); + int i; qemu_put_buffer(f, n->mac, ETH_ALEN); qemu_put_be32(f, n->vqs[0].tx_waiting); @@ -1303,15 +1387,18 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) { VirtIONet *n = opaque; VirtIODevice *vdev = VIRTIO_DEVICE(n); - int ret, i, link_down; if (version_id < 2 || version_id > VIRTIO_NET_VM_VERSION) return -EINVAL; - ret = virtio_load(vdev, f); - if (ret) { - return ret; - } + return virtio_load(vdev, f, version_id); +} + +static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, + int version_id) +{ + VirtIONet *n = VIRTIO_NET(vdev); + int i, link_down; qemu_get_buffer(f, n->mac, ETH_ALEN); n->vqs[0].tx_waiting = qemu_get_be32(f); @@ -1337,10 +1424,17 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) if (n->mac_table.in_use <= MAC_TABLE_ENTRIES) { qemu_get_buffer(f, n->mac_table.macs, n->mac_table.in_use * ETH_ALEN); - } else if (n->mac_table.in_use) { - uint8_t *buf = g_malloc0(n->mac_table.in_use); - qemu_get_buffer(f, buf, n->mac_table.in_use * ETH_ALEN); - g_free(buf); + } else { + int64_t i; + + /* Overflow detected - can happen if source has a larger MAC table. + * We simply set overflow flag so there's no need to maintain the + * table of addresses, discard them all. + * Note: 64 bit math to avoid integer overflow. + */ + for (i = 0; i < (int64_t)n->mac_table.in_use * ETH_ALEN; ++i) { + qemu_get_byte(f); + } n->mac_table.multi_overflow = n->mac_table.uni_overflow = 1; n->mac_table.in_use = 0; } @@ -1382,6 +1476,11 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) } n->curr_queues = qemu_get_be16(f); + if (n->curr_queues > n->max_queues) { + error_report("virtio-net: curr_queues %x > max_queues %x", + n->curr_queues, n->max_queues); + return -1; + } for (i = 1; i < n->curr_queues; i++) { n->vqs[i].tx_waiting = qemu_get_be32(f); } @@ -1414,6 +1513,12 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) qemu_get_subqueue(n->nic, i)->link_down = link_down; } + if (vdev->guest_features & (0x1 << VIRTIO_NET_F_GUEST_ANNOUNCE) && + vdev->guest_features & (0x1 << VIRTIO_NET_F_CTRL_VQ)) { + n->announce_counter = SELF_ANNOUNCE_ROUNDS; + timer_mod(n->announce_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL)); + } + return 0; } @@ -1439,7 +1544,7 @@ static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx) VirtIONet *n = VIRTIO_NET(vdev); NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx)); assert(n->vhost_started); - return vhost_net_virtqueue_pending(tap_get_vhost_net(nc->peer), idx); + return vhost_net_virtqueue_pending(get_vhost_net(nc->peer), idx); } static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx, @@ -1448,7 +1553,7 @@ static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx, VirtIONet *n = VIRTIO_NET(vdev); NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx)); assert(n->vhost_started); - vhost_net_virtqueue_mask(tap_get_vhost_net(nc->peer), + vhost_net_virtqueue_mask(get_vhost_net(nc->peer), vdev, idx, mask); } @@ -1472,18 +1577,9 @@ void virtio_net_set_netclient_name(VirtIONet *n, const char *name, */ assert(type != NULL); - if (n->netclient_name) { - g_free(n->netclient_name); - n->netclient_name = NULL; - } - if (n->netclient_type) { - g_free(n->netclient_type); - n->netclient_type = NULL; - } - - if (name != NULL) { - n->netclient_name = g_strdup(name); - } + g_free(n->netclient_name); + g_free(n->netclient_type); + n->netclient_name = g_strdup(name); n->netclient_type = g_strdup(type); } @@ -1496,7 +1592,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) virtio_init(vdev, "virtio-net", VIRTIO_ID_NET, n->config_size); - n->max_queues = MAX(n->nic_conf.queues, 1); + n->max_queues = MAX(n->nic_conf.peers.queues, 1); n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queues); n->vqs[0].rx_vq = virtio_add_queue(vdev, 256, virtio_net_handle_rx); n->curr_queues = 1; @@ -1525,6 +1621,8 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&n->nic_conf.macaddr); memcpy(&n->mac[0], &n->nic_conf.macaddr, sizeof(n->mac)); n->status = VIRTIO_NET_S_LINK_UP; + n->announce_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, + virtio_net_announce_timer, n); if (n->netclient_type) { /* @@ -1564,8 +1662,6 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) n->qdev = dev; register_savevm(dev, "virtio-net", -1, VIRTIO_NET_VM_VERSION, virtio_net_save, virtio_net_load, n); - - add_boot_device_path(n->nic_conf.bootindex, dev, "/ethernet-phy@0"); } static void virtio_net_device_unrealize(DeviceState *dev, Error **errp) @@ -1579,14 +1675,10 @@ static void virtio_net_device_unrealize(DeviceState *dev, Error **errp) unregister_savevm(dev, "virtio-net", n); - if (n->netclient_name) { - g_free(n->netclient_name); - n->netclient_name = NULL; - } - if (n->netclient_type) { - g_free(n->netclient_type); - n->netclient_type = NULL; - } + g_free(n->netclient_name); + n->netclient_name = NULL; + g_free(n->netclient_type); + n->netclient_type = NULL; g_free(n->mac_table.macs); g_free(n->vlans); @@ -1605,6 +1697,8 @@ static void virtio_net_device_unrealize(DeviceState *dev, Error **errp) } } + timer_del(n->announce_timer); + timer_free(n->announce_timer); g_free(n->vqs); qemu_del_nic(n->nic); virtio_cleanup(vdev); @@ -1619,6 +1713,9 @@ static void virtio_net_instance_init(Object *obj) * Can be overriden with virtio_net_set_config_size. */ n->config_size = sizeof(struct virtio_net_config); + device_add_bootindex_property(obj, &n->nic_conf.bootindex, + "bootindex", "/ethernet-phy@0", + DEVICE(n), NULL); } static Property virtio_net_properties[] = { @@ -1648,6 +1745,8 @@ static void virtio_net_class_init(ObjectClass *klass, void *data) vdc->set_status = virtio_net_set_status; vdc->guest_notifier_mask = virtio_net_guest_notifier_mask; vdc->guest_notifier_pending = virtio_net_guest_notifier_pending; + vdc->load = virtio_net_load_device; + vdc->save = virtio_net_save_device; } static const TypeInfo virtio_net_info = {