Currently, the config size for virtio devices is hard coded. When a new feature is added that changes the config size, drivers that assume a static config size will break. For purposes of backward compatibility, there needs to be a way to inform drivers of the config size needed to accommodate the set of features enabled.
Signed-off-by: Jesse Larrew <jlar...@linux.vnet.ibm.com> --- hw/virtio-net.c | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/hw/virtio-net.c b/hw/virtio-net.c index f1c2884..8f521b3 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -73,8 +73,31 @@ typedef struct VirtIONet int multiqueue; uint16_t max_queues; uint16_t curr_queues; + int config_size; } VirtIONet; +/* + * Calculate the number of bytes up to and including the given 'field' of + * 'container'. + */ +#define endof(container, field) \ + ((intptr_t)(&(((container *)0)->field)+1)) + +typedef struct VirtIOFeature { + uint32_t flags; + size_t end; +} VirtIOFeature; + +static VirtIOFeature feature_sizes[] = { + {.flags = 1 << VIRTIO_NET_F_MAC, + .end = endof(struct virtio_net_config, mac)}, + {.flags = 1 << VIRTIO_NET_F_STATUS, + .end = endof(struct virtio_net_config, status)}, + {.flags = 1 << VIRTIO_NET_F_MQ, + .end = endof(struct virtio_net_config, max_virtqueue_pairs)}, + {} +}; + static VirtIONetQueue *virtio_net_get_subqueue(NetClientState *nc) { VirtIONet *n = qemu_get_nic_opaque(nc); @@ -104,15 +127,15 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) stw_p(&netcfg.status, n->status); stw_p(&netcfg.max_virtqueue_pairs, n->max_queues); memcpy(netcfg.mac, n->mac, ETH_ALEN); - memcpy(config, &netcfg, sizeof(netcfg)); + memcpy(config, &netcfg, n->config_size); } static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config) { VirtIONet *n = to_virtio_net(vdev); - struct virtio_net_config netcfg; + struct virtio_net_config netcfg = {}; - memcpy(&netcfg, config, sizeof(netcfg)); + memcpy(&netcfg, config, n->config_size); if (!(n->vdev.guest_features >> VIRTIO_NET_F_CTRL_MAC_ADDR & 1) && memcmp(netcfg.mac, n->mac, ETH_ALEN)) { @@ -1279,16 +1302,21 @@ static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx, } VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, - virtio_net_conf *net, - uint32_t host_features) + virtio_net_conf *net, uint32_t host_features) { VirtIONet *n; - int i; + int i, config_size = 0; + + for (i = 0; feature_sizes[i].flags != 0; i++) { + if (host_features & feature_sizes[i].flags) { + config_size = MAX(feature_sizes[i].end, config_size); + } + } n = (VirtIONet *)virtio_common_init("virtio-net", VIRTIO_ID_NET, - sizeof(struct virtio_net_config), - sizeof(VirtIONet)); + config_size, sizeof(VirtIONet)); + n->config_size = config_size; n->vdev.get_config = virtio_net_get_config; n->vdev.set_config = virtio_net_set_config; n->vdev.get_features = virtio_net_get_features; -- 1.7.11.7