Patchwork [1/3] virtio/vhost: support 64 bit features

login
register
mail settings
Submitter Michael S. Tsirkin
Date May 4, 2011, 9:01 p.m.
Message ID <57ef43151e0838ea332fa4d65783185a0e1782dc.1304542880.git.mst@redhat.com>
Download mbox | patch
Permalink /patch/94163/
State New
Headers show

Comments

Michael S. Tsirkin - May 4, 2011, 9:01 p.m.
Add support for extended feature bits: up to 64 bit.
Only virtio-pci is actually implemented,
s390 and syborg are stubbed out (and untested).

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
 hw/qdev-properties.c   |   39 ++++++++++++++++----
 hw/qdev.h              |   10 +++++
 hw/s390-virtio-bus.c   |    5 ++-
 hw/s390-virtio-bus.h   |    2 +-
 hw/syborg_virtio.c     |    7 ++--
 hw/vhost_net.c         |    8 ++--
 hw/vhost_net.h         |    4 +-
 hw/virtio-9p.c         |    2 +-
 hw/virtio-balloon.c    |    2 +-
 hw/virtio-blk.c        |    2 +-
 hw/virtio-blk.h        |    2 +-
 hw/virtio-net.c        |   11 +++---
 hw/virtio-net.h        |   34 +++++++++---------
 hw/virtio-pci.c        |   91 +++++++++++++++++++++++++++++++++++-------------
 hw/virtio-serial-bus.c |    2 +-
 hw/virtio.c            |   24 ++++++++++---
 hw/virtio.h            |   17 +++++----
 17 files changed, 179 insertions(+), 83 deletions(-)

Patch

diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c
index 1088a26..a933c9e 100644
--- a/hw/qdev-properties.c
+++ b/hw/qdev-properties.c
@@ -10,20 +10,35 @@  void *qdev_get_prop_ptr(DeviceState *dev, Property *prop)
     return ptr;
 }
 
-static uint32_t qdev_get_prop_mask(Property *prop)
+static uint64_t qdev_get_prop_mask(Property *prop)
 {
     assert(prop->info->type == PROP_TYPE_BIT);
-    return 0x1 << prop->bitnr;
+    return 0x1ULL << prop->bitnr;
+}
+
+static uint64_t qdev_get_prop_val(DeviceState *dev, Property *prop)
+{
+    assert(prop->info->type == PROP_TYPE_BIT);
+    if (prop->info->size == sizeof(uint32_t)) {
+        return *(uint32_t *)qdev_get_prop_ptr(dev, prop);
+    } else {
+        return *(uint64_t *)qdev_get_prop_ptr(dev, prop);
+    }
 }
 
 static void bit_prop_set(DeviceState *dev, Property *props, bool val)
 {
-    uint32_t *p = qdev_get_prop_ptr(dev, props);
-    uint32_t mask = qdev_get_prop_mask(props);
+    uint64_t p = qdev_get_prop_val(dev, props);
+    uint64_t mask = qdev_get_prop_mask(props);
     if (val)
-        *p |= mask;
+        p |= mask;
     else
-        *p &= ~mask;
+        p &= ~mask;
+    if (props->info->size == sizeof(uint32_t)) {
+        *(uint32_t *)qdev_get_prop_ptr(dev, props) = p;
+    } else {
+        *(uint64_t *)qdev_get_prop_ptr(dev, props) = p;
+    }
 }
 
 static void qdev_prop_cpy(DeviceState *dev, Property *props, void *src)
@@ -51,8 +66,8 @@  static int parse_bit(DeviceState *dev, Property *prop, const char *str)
 
 static int print_bit(DeviceState *dev, Property *prop, char *dest, size_t len)
 {
-    uint32_t *p = qdev_get_prop_ptr(dev, prop);
-    return snprintf(dest, len, (*p & qdev_get_prop_mask(prop)) ? "on" : "off");
+    uint64_t val = qdev_get_prop_val(dev, prop);
+    return snprintf(dest, len, (val & qdev_get_prop_mask(prop)) ? "on" : "off");
 }
 
 PropertyInfo qdev_prop_bit = {
@@ -63,6 +78,14 @@  PropertyInfo qdev_prop_bit = {
     .print = print_bit,
 };
 
+PropertyInfo qdev_prop_bit64 = {
+    .name  = "on/off",
+    .type  = PROP_TYPE_BIT,
+    .size  = sizeof(uint64_t),
+    .parse = parse_bit,
+    .print = print_bit,
+};
+
 /* --- 8bit integer --- */
 
 static int parse_uint8(DeviceState *dev, Property *prop, const char *str)
diff --git a/hw/qdev.h b/hw/qdev.h
index 8a13ec9..e65cab0 100644
--- a/hw/qdev.h
+++ b/hw/qdev.h
@@ -219,6 +219,7 @@  int do_device_del(Monitor *mon, const QDict *qdict, QObject **ret_data);
 /*** qdev-properties.c ***/
 
 extern PropertyInfo qdev_prop_bit;
+extern PropertyInfo qdev_prop_bit64;
 extern PropertyInfo qdev_prop_uint8;
 extern PropertyInfo qdev_prop_uint16;
 extern PropertyInfo qdev_prop_uint32;
@@ -257,6 +258,15 @@  extern PropertyInfo qdev_prop_pci_devfn;
         .defval    = (bool[]) { (_defval) },                     \
         }
 
+#define DEFINE_PROP_BIT64(_name, _state, _field, _bit, _defval) {  \
+        .name      = (_name),                                    \
+        .info      = &(qdev_prop_bit64),                           \
+        .bitnr    = (_bit),                                      \
+        .offset    = offsetof(_state, _field)                    \
+            + type_check(uint64_t,typeof_field(_state, _field)), \
+        .defval    = (bool[]) { (_defval) },                     \
+        }
+
 #define DEFINE_PROP_UINT8(_n, _s, _f, _d)                       \
     DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_uint8, uint8_t)
 #define DEFINE_PROP_UINT16(_n, _s, _f, _d)                      \
diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c
index 175e5cb..89e8c8e 100644
--- a/hw/s390-virtio-bus.c
+++ b/hw/s390-virtio-bus.c
@@ -310,10 +310,11 @@  static void virtio_s390_notify(void *opaque, uint16_t vector)
     kvm_s390_virtio_irq(s390_cpu_addr2state(0), 0, token);
 }
 
-static unsigned virtio_s390_get_features(void *opaque)
+static uint64_t virtio_s390_get_features(void *opaque)
 {
     VirtIOS390Device *dev = (VirtIOS390Device*)opaque;
-    return dev->host_features;
+    /* TODO: support high 32 bit features */
+    return dev->host_features & 0xFFFFFFFFULL;
 }
 
 /**************** S390 Virtio Bus Device Descriptions *******************/
diff --git a/hw/s390-virtio-bus.h b/hw/s390-virtio-bus.h
index edf6d04..5c851e3 100644
--- a/hw/s390-virtio-bus.h
+++ b/hw/s390-virtio-bus.h
@@ -43,7 +43,7 @@  typedef struct VirtIOS390Device {
     VirtIODevice *vdev;
     BlockConf block;
     NICConf nic;
-    uint32_t host_features;
+    uint64_t host_features;
     virtio_serial_conf serial;
     virtio_net_conf net;
 } VirtIOS390Device;
diff --git a/hw/syborg_virtio.c b/hw/syborg_virtio.c
index ee08c49..b64c357 100644
--- a/hw/syborg_virtio.c
+++ b/hw/syborg_virtio.c
@@ -67,7 +67,7 @@  typedef struct {
     uint32_t int_enable;
     uint32_t id;
     NICConf nic;
-    uint32_t host_features;
+    uint64_t host_features;
     virtio_net_conf net;
 } SyborgVirtIOProxy;
 
@@ -244,10 +244,11 @@  static void syborg_virtio_update_irq(void *opaque, uint16_t vector)
     qemu_set_irq(proxy->irq, level != 0);
 }
 
-static unsigned syborg_virtio_get_features(void *opaque)
+static uint32_t syborg_virtio_get_features(void *opaque)
 {
     SyborgVirtIOProxy *proxy = opaque;
-    return proxy->host_features;
+    /* TODO: support high 32 bit features */
+    return proxy->host_features & 0xFFFFFFFFULL;
 }
 
 static VirtIOBindings syborg_virtio_bindings = {
diff --git a/hw/vhost_net.c b/hw/vhost_net.c
index 420e05f..7e94f61 100644
--- a/hw/vhost_net.c
+++ b/hw/vhost_net.c
@@ -41,7 +41,7 @@  struct vhost_net {
     VLANClientState *vc;
 };
 
-unsigned vhost_net_get_features(struct vhost_net *net, unsigned features)
+uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features)
 {
     /* Clear features not supported by host kernel. */
     if (!(net->dev.features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY))) {
@@ -56,7 +56,7 @@  unsigned vhost_net_get_features(struct vhost_net *net, unsigned features)
     return features;
 }
 
-void vhost_net_ack_features(struct vhost_net *net, unsigned features)
+void vhost_net_ack_features(struct vhost_net *net, uint64_t features)
 {
     net->dev.acked_features = net->dev.backend_features;
     if (features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) {
@@ -219,11 +219,11 @@  void vhost_net_cleanup(struct vhost_net *net)
 {
 }
 
-unsigned vhost_net_get_features(struct vhost_net *net, unsigned features)
+uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features)
 {
     return features;
 }
-void vhost_net_ack_features(struct vhost_net *net, unsigned features)
+void vhost_net_ack_features(struct vhost_net *net, uint64_t features)
 {
 }
 #endif
diff --git a/hw/vhost_net.h b/hw/vhost_net.h
index 91e40b1..4069258 100644
--- a/hw/vhost_net.h
+++ b/hw/vhost_net.h
@@ -14,7 +14,7 @@  void vhost_net_stop(VHostNetState *net, VirtIODevice *dev);
 
 void vhost_net_cleanup(VHostNetState *net);
 
-unsigned vhost_net_get_features(VHostNetState *net, unsigned features);
-void vhost_net_ack_features(VHostNetState *net, unsigned features);
+uint64_t vhost_net_get_features(VHostNetState *net, uint64_t features);
+void vhost_net_ack_features(VHostNetState *net, uint64_t features);
 
 #endif
diff --git a/hw/virtio-9p.c b/hw/virtio-9p.c
index 7e29535..184691a 100644
--- a/hw/virtio-9p.c
+++ b/hw/virtio-9p.c
@@ -3624,7 +3624,7 @@  static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq)
     free_pdu(s, pdu);
 }
 
-static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features)
+static uint64_t virtio_9p_get_features(VirtIODevice *vdev, uint64_t features)
 {
     features |= 1 << VIRTIO_9P_MOUNT_TAG;
     return features;
diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c
index f9add1c..b34adc1 100644
--- a/hw/virtio-balloon.c
+++ b/hw/virtio-balloon.c
@@ -195,7 +195,7 @@  static void virtio_balloon_set_config(VirtIODevice *vdev,
     dev->actual = le32_to_cpu(config.actual);
 }
 
-static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f)
+static uint64_t virtio_balloon_get_features(VirtIODevice *vdev, uint64_t f)
 {
     f |= (1 << VIRTIO_BALLOON_F_STATS_VQ);
     return f;
diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c
index 91e0394..442ce11 100644
--- a/hw/virtio-blk.c
+++ b/hw/virtio-blk.c
@@ -466,7 +466,7 @@  static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config)
     memcpy(config, &blkcfg, sizeof(struct virtio_blk_config));
 }
 
-static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features)
+static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features)
 {
     VirtIOBlock *s = to_virtio_blk(vdev);
 
diff --git a/hw/virtio-blk.h b/hw/virtio-blk.h
index fff46da..61104ea 100644
--- a/hw/virtio-blk.h
+++ b/hw/virtio-blk.h
@@ -98,7 +98,7 @@  struct virtio_scsi_inhdr
 #ifdef __linux__
 #define DEFINE_VIRTIO_BLK_FEATURES(_state, _field) \
         DEFINE_VIRTIO_COMMON_FEATURES(_state, _field), \
-        DEFINE_PROP_BIT("scsi", _state, _field, VIRTIO_BLK_F_SCSI, true)
+        DEFINE_PROP_BIT64("scsi", _state, _field, VIRTIO_BLK_F_SCSI, true)
 #else
 #define DEFINE_VIRTIO_BLK_FEATURES(_state, _field) \
         DEFINE_VIRTIO_COMMON_FEATURES(_state, _field)
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 6997e02..c242fd1 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -223,7 +223,7 @@  static int peer_has_ufo(VirtIONet *n)
     return n->has_ufo;
 }
 
-static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features)
+static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features)
 {
     VirtIONet *n = to_virtio_net(vdev);
 
@@ -258,9 +258,9 @@  static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features)
     return vhost_net_get_features(tap_get_vhost_net(n->nic->nc.peer), features);
 }
 
-static uint32_t virtio_net_bad_features(VirtIODevice *vdev)
+static uint64_t virtio_net_bad_features(VirtIODevice *vdev)
 {
-    uint32_t features = 0;
+    uint64_t features = 0;
 
     /* Linux kernel 2.6.25.  It understood MAC (as everyone must),
      * but also these: */
@@ -273,7 +273,7 @@  static uint32_t virtio_net_bad_features(VirtIODevice *vdev)
     return features;
 }
 
-static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features)
+static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
 {
     VirtIONet *n = to_virtio_net(vdev);
 
@@ -628,7 +628,8 @@  static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_
                 return -1;
             error_report("virtio-net unexpected empty queue: "
                     "i %zd mergeable %d offset %zd, size %zd, "
-                    "guest hdr len %zd, host hdr len %zd guest features 0x%x",
+                    "guest hdr len %zd, host hdr len %zd "
+                    "guest features 0x%" PRIx64,
                     i, n->mergeable_rx_bufs, offset, size,
                     guest_hdr_len, host_hdr_len, n->vdev.guest_features);
             exit(1);
diff --git a/hw/virtio-net.h b/hw/virtio-net.h
index 8af9a1c..8f8ac7f 100644
--- a/hw/virtio-net.h
+++ b/hw/virtio-net.h
@@ -169,21 +169,21 @@  struct virtio_net_ctrl_mac {
 
 #define DEFINE_VIRTIO_NET_FEATURES(_state, _field) \
         DEFINE_VIRTIO_COMMON_FEATURES(_state, _field), \
-        DEFINE_PROP_BIT("csum", _state, _field, VIRTIO_NET_F_CSUM, true), \
-        DEFINE_PROP_BIT("guest_csum", _state, _field, VIRTIO_NET_F_GUEST_CSUM, true), \
-        DEFINE_PROP_BIT("gso", _state, _field, VIRTIO_NET_F_GSO, true), \
-        DEFINE_PROP_BIT("guest_tso4", _state, _field, VIRTIO_NET_F_GUEST_TSO4, true), \
-        DEFINE_PROP_BIT("guest_tso6", _state, _field, VIRTIO_NET_F_GUEST_TSO6, true), \
-        DEFINE_PROP_BIT("guest_ecn", _state, _field, VIRTIO_NET_F_GUEST_ECN, true), \
-        DEFINE_PROP_BIT("guest_ufo", _state, _field, VIRTIO_NET_F_GUEST_UFO, true), \
-        DEFINE_PROP_BIT("host_tso4", _state, _field, VIRTIO_NET_F_HOST_TSO4, true), \
-        DEFINE_PROP_BIT("host_tso6", _state, _field, VIRTIO_NET_F_HOST_TSO6, true), \
-        DEFINE_PROP_BIT("host_ecn", _state, _field, VIRTIO_NET_F_HOST_ECN, true), \
-        DEFINE_PROP_BIT("host_ufo", _state, _field, VIRTIO_NET_F_HOST_UFO, true), \
-        DEFINE_PROP_BIT("mrg_rxbuf", _state, _field, VIRTIO_NET_F_MRG_RXBUF, true), \
-        DEFINE_PROP_BIT("status", _state, _field, VIRTIO_NET_F_STATUS, true), \
-        DEFINE_PROP_BIT("ctrl_vq", _state, _field, VIRTIO_NET_F_CTRL_VQ, true), \
-        DEFINE_PROP_BIT("ctrl_rx", _state, _field, VIRTIO_NET_F_CTRL_RX, true), \
-        DEFINE_PROP_BIT("ctrl_vlan", _state, _field, VIRTIO_NET_F_CTRL_VLAN, true), \
-        DEFINE_PROP_BIT("ctrl_rx_extra", _state, _field, VIRTIO_NET_F_CTRL_RX_EXTRA, true)
+        DEFINE_PROP_BIT64("csum", _state, _field, VIRTIO_NET_F_CSUM, true), \
+        DEFINE_PROP_BIT64("guest_csum", _state, _field, VIRTIO_NET_F_GUEST_CSUM, true), \
+        DEFINE_PROP_BIT64("gso", _state, _field, VIRTIO_NET_F_GSO, true), \
+        DEFINE_PROP_BIT64("guest_tso4", _state, _field, VIRTIO_NET_F_GUEST_TSO4, true), \
+        DEFINE_PROP_BIT64("guest_tso6", _state, _field, VIRTIO_NET_F_GUEST_TSO6, true), \
+        DEFINE_PROP_BIT64("guest_ecn", _state, _field, VIRTIO_NET_F_GUEST_ECN, true), \
+        DEFINE_PROP_BIT64("guest_ufo", _state, _field, VIRTIO_NET_F_GUEST_UFO, true), \
+        DEFINE_PROP_BIT64("host_tso4", _state, _field, VIRTIO_NET_F_HOST_TSO4, true), \
+        DEFINE_PROP_BIT64("host_tso6", _state, _field, VIRTIO_NET_F_HOST_TSO6, true), \
+        DEFINE_PROP_BIT64("host_ecn", _state, _field, VIRTIO_NET_F_HOST_ECN, true), \
+        DEFINE_PROP_BIT64("host_ufo", _state, _field, VIRTIO_NET_F_HOST_UFO, true), \
+        DEFINE_PROP_BIT64("mrg_rxbuf", _state, _field, VIRTIO_NET_F_MRG_RXBUF, true), \
+        DEFINE_PROP_BIT64("status", _state, _field, VIRTIO_NET_F_STATUS, true), \
+        DEFINE_PROP_BIT64("ctrl_vq", _state, _field, VIRTIO_NET_F_CTRL_VQ, true), \
+        DEFINE_PROP_BIT64("ctrl_rx", _state, _field, VIRTIO_NET_F_CTRL_RX, true), \
+        DEFINE_PROP_BIT64("ctrl_vlan", _state, _field, VIRTIO_NET_F_CTRL_VLAN, true), \
+        DEFINE_PROP_BIT64("ctrl_rx_extra", _state, _field, VIRTIO_NET_F_CTRL_RX_EXTRA, true)
 #endif
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
index 5236470..eb86de2 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -64,15 +64,28 @@ 
 /* Config space size */
 #define VIRTIO_PCI_CONFIG_NOMSI         20
 #define VIRTIO_PCI_CONFIG_MSI           24
-#define VIRTIO_PCI_REGION_SIZE(dev)     (msix_present(dev) ? \
-                                         VIRTIO_PCI_CONFIG_MSI : \
-                                         VIRTIO_PCI_CONFIG_NOMSI)
+#define VIRTIO_PCI_CONFIG_HI            32
+/* An extended 32-bit r/o bitmask of the features supported by the host */
+#define VIRTIO_PCI_HOST_FEATURES_HI     24
+
+/* An extended 32-bit r/w bitmask of features activated by the guest */
+#define VIRTIO_PCI_GUEST_FEATURES_HI    28
+
+#define VIRTIO_PCI_REGION_SIZE(proxy)   (((proxy)->host_features & \
+                                          (1ULL << VIRTIO_F_FEATURES_HI)) ? \
+                                         VIRTIO_PCI_CONFIG_HI : \
+                                         (msix_present(&(proxy)->pci_dev) ? \
+                                          VIRTIO_PCI_CONFIG_MSI : \
+                                          VIRTIO_PCI_CONFIG_NOMSI))
 
 /* The remaining space is defined by each driver as the per-driver
  * configuration space */
-#define VIRTIO_PCI_CONFIG(dev)          (msix_enabled(dev) ? \
-                                         VIRTIO_PCI_CONFIG_MSI : \
-                                         VIRTIO_PCI_CONFIG_NOMSI)
+#define VIRTIO_PCI_CONFIG(proxy)        (((proxy)->vdev->guest_features & \
+                                          (1ULL << VIRTIO_F_FEATURES_HI)) ? \
+                                         VIRTIO_PCI_CONFIG_HI : \
+                                         (msix_enabled(&(proxy)->pci_dev) ? \
+                                          VIRTIO_PCI_CONFIG_MSI : \
+                                          VIRTIO_PCI_CONFIG_NOMSI))
 
 /* Virtio ABI version, if we increment this, we break the guest driver. */
 #define VIRTIO_PCI_ABI_VERSION          0
@@ -106,7 +119,7 @@  typedef struct {
     uint32_t nvectors;
     BlockConf block;
     NICConf nic;
-    uint32_t host_features;
+    uint64_t host_features;
 #ifdef CONFIG_LINUX
     V9fsConf fsconf;
 #endif
@@ -314,11 +327,22 @@  static void virtio_pci_reset(DeviceState *d)
     proxy->flags &= ~VIRTIO_PCI_FLAG_BUS_MASTER_BUG;
 }
 
+static inline uint64_t virtio_replace_hi(uint64_t features, uint32_t hi)
+{
+	return (features & 0xffffffffull) | ((uint64_t)hi) << 32;
+}
+
+static inline uint64_t virtio_replace_lo(uint64_t features, uint32_t lo)
+{
+	return (features & 0xffffffff00000000ull) | lo;
+}
+
 static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
 {
     VirtIOPCIProxy *proxy = opaque;
     VirtIODevice *vdev = proxy->vdev;
     target_phys_addr_t pa;
+    uint64_t f;
 
     switch (addr) {
     case VIRTIO_PCI_GUEST_FEATURES:
@@ -329,9 +353,15 @@  static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
 	    else
 		val = 0;
 	}
+        /* Clearing VIRTIO_F_FEATURES_HI clears high 32 bit. */
+	if (val & (1ULL << VIRTIO_F_FEATURES_HI)) {
+	    f = virtio_replace_lo(vdev->guest_features, val);
+	} else {
+	    f = val;
+        }
         if (vdev->set_features)
-            vdev->set_features(vdev, val);
-        vdev->guest_features = val;
+            vdev->set_features(vdev, f);
+        vdev->guest_features = f;
         break;
     case VIRTIO_PCI_QUEUE_PFN:
         pa = (target_phys_addr_t)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT;
@@ -389,6 +419,12 @@  static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
             val = VIRTIO_NO_VECTOR;
         virtio_queue_set_vector(vdev, vdev->queue_sel, val);
         break;
+    case VIRTIO_PCI_GUEST_FEATURES_HI:
+	f = virtio_replace_hi(vdev->guest_features, val);
+        if (vdev->set_features)
+            vdev->set_features(vdev, f);
+        vdev->guest_features = f;
+        break;
     default:
         error_report("%s: unexpected address 0x%x value 0x%x",
                      __func__, addr, val);
@@ -433,6 +469,11 @@  static uint32_t virtio_ioport_read(VirtIOPCIProxy *proxy, uint32_t addr)
     case VIRTIO_MSI_QUEUE_VECTOR:
         ret = virtio_queue_vector(vdev, vdev->queue_sel);
         break;
+    case VIRTIO_PCI_HOST_FEATURES_HI:
+        ret = proxy->host_features >> 32;
+        break;
+    case VIRTIO_PCI_GUEST_FEATURES_HI:
+        ret = vdev->guest_features >> 32;
     default:
         break;
     }
@@ -443,7 +484,7 @@  static uint32_t virtio_ioport_read(VirtIOPCIProxy *proxy, uint32_t addr)
 static uint32_t virtio_pci_config_readb(void *opaque, uint32_t addr)
 {
     VirtIOPCIProxy *proxy = opaque;
-    uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
+    uint32_t config = VIRTIO_PCI_CONFIG(proxy);
     addr -= proxy->addr;
     if (addr < config)
         return virtio_ioport_read(proxy, addr);
@@ -454,7 +495,7 @@  static uint32_t virtio_pci_config_readb(void *opaque, uint32_t addr)
 static uint32_t virtio_pci_config_readw(void *opaque, uint32_t addr)
 {
     VirtIOPCIProxy *proxy = opaque;
-    uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
+    uint32_t config = VIRTIO_PCI_CONFIG(proxy);
     addr -= proxy->addr;
     if (addr < config)
         return virtio_ioport_read(proxy, addr);
@@ -465,7 +506,7 @@  static uint32_t virtio_pci_config_readw(void *opaque, uint32_t addr)
 static uint32_t virtio_pci_config_readl(void *opaque, uint32_t addr)
 {
     VirtIOPCIProxy *proxy = opaque;
-    uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
+    uint32_t config = VIRTIO_PCI_CONFIG(proxy);
     addr -= proxy->addr;
     if (addr < config)
         return virtio_ioport_read(proxy, addr);
@@ -476,7 +517,7 @@  static uint32_t virtio_pci_config_readl(void *opaque, uint32_t addr)
 static void virtio_pci_config_writeb(void *opaque, uint32_t addr, uint32_t val)
 {
     VirtIOPCIProxy *proxy = opaque;
-    uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
+    uint32_t config = VIRTIO_PCI_CONFIG(proxy);
     addr -= proxy->addr;
     if (addr < config) {
         virtio_ioport_write(proxy, addr, val);
@@ -489,7 +530,7 @@  static void virtio_pci_config_writeb(void *opaque, uint32_t addr, uint32_t val)
 static void virtio_pci_config_writew(void *opaque, uint32_t addr, uint32_t val)
 {
     VirtIOPCIProxy *proxy = opaque;
-    uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
+    uint32_t config = VIRTIO_PCI_CONFIG(proxy);
     addr -= proxy->addr;
     if (addr < config) {
         virtio_ioport_write(proxy, addr, val);
@@ -502,7 +543,7 @@  static void virtio_pci_config_writew(void *opaque, uint32_t addr, uint32_t val)
 static void virtio_pci_config_writel(void *opaque, uint32_t addr, uint32_t val)
 {
     VirtIOPCIProxy *proxy = opaque;
-    uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
+    uint32_t config = VIRTIO_PCI_CONFIG(proxy);
     addr -= proxy->addr;
     if (addr < config) {
         virtio_ioport_write(proxy, addr, val);
@@ -517,7 +558,7 @@  static void virtio_map(PCIDevice *pci_dev, int region_num,
 {
     VirtIOPCIProxy *proxy = container_of(pci_dev, VirtIOPCIProxy, pci_dev);
     VirtIODevice *vdev = proxy->vdev;
-    unsigned config_len = VIRTIO_PCI_REGION_SIZE(pci_dev) + vdev->config_len;
+    unsigned config_len = VIRTIO_PCI_REGION_SIZE(proxy) + vdev->config_len;
 
     proxy->addr = addr;
 
@@ -551,7 +592,7 @@  static void virtio_write_config(PCIDevice *pci_dev, uint32_t address,
     msix_write_config(pci_dev, address, val, len);
 }
 
-static unsigned virtio_pci_get_features(void *opaque)
+static uint64_t virtio_pci_get_features(void *opaque)
 {
     VirtIOPCIProxy *proxy = opaque;
     return proxy->host_features;
@@ -788,13 +829,6 @@  static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev,
 
     proxy->pci_dev.config_write = virtio_write_config;
 
-    size = VIRTIO_PCI_REGION_SIZE(&proxy->pci_dev) + vdev->config_len;
-    if (size & (size-1))
-        size = 1 << qemu_fls(size);
-
-    pci_register_bar(&proxy->pci_dev, 0, size, PCI_BASE_ADDRESS_SPACE_IO,
-                           virtio_map);
-
     if (!kvm_has_many_ioeventfds()) {
         proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD;
     }
@@ -803,6 +837,15 @@  static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev,
     proxy->host_features |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY;
     proxy->host_features |= 0x1 << VIRTIO_F_BAD_FEATURE;
     proxy->host_features = vdev->get_features(vdev, proxy->host_features);
+    if (proxy->host_features & 0xffffffff00000000ull) {
+        proxy->host_features |= 0x1ULL << VIRTIO_F_FEATURES_HI;
+    }
+    size = VIRTIO_PCI_REGION_SIZE(proxy) + vdev->config_len;
+    if (size & (size-1))
+        size = 1 << qemu_fls(size);
+
+    pci_register_bar(&proxy->pci_dev, 0, size, PCI_BASE_ADDRESS_SPACE_IO,
+                           virtio_map);
 }
 
 static int virtio_blk_init_pci(PCIDevice *pci_dev)
diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c
index 6227379..76acd02 100644
--- a/hw/virtio-serial-bus.c
+++ b/hw/virtio-serial-bus.c
@@ -461,7 +461,7 @@  static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
 {
 }
 
-static uint32_t get_features(VirtIODevice *vdev, uint32_t features)
+static uint64_t get_features(VirtIODevice *vdev, uint64_t features)
 {
     VirtIOSerial *vser;
 
diff --git a/hw/virtio.c b/hw/virtio.c
index 31bd9e3..b9acbd4 100644
--- a/hw/virtio.c
+++ b/hw/virtio.c
@@ -653,6 +653,8 @@  void virtio_notify_config(VirtIODevice *vdev)
 
 void virtio_save(VirtIODevice *vdev, QEMUFile *f)
 {
+    uint32_t guest_features_hi = vdev->guest_features >> 32;
+    uint32_t guest_features_lo = vdev->guest_features;
     int i;
 
     if (vdev->binding->save_config)
@@ -661,7 +663,10 @@  void virtio_save(VirtIODevice *vdev, QEMUFile *f)
     qemu_put_8s(f, &vdev->status);
     qemu_put_8s(f, &vdev->isr);
     qemu_put_be16s(f, &vdev->queue_sel);
-    qemu_put_be32s(f, &vdev->guest_features);
+    qemu_put_be32s(f, &guest_features_lo);
+    if (guest_features_lo & (1ULL << VIRTIO_F_FEATURES_HI)) {
+        qemu_put_be32s(f, &guest_features_hi);
+    }
     qemu_put_be32(f, vdev->config_len);
     qemu_put_buffer(f, vdev->config, vdev->config_len);
 
@@ -687,8 +692,9 @@  void virtio_save(VirtIODevice *vdev, QEMUFile *f)
 int virtio_load(VirtIODevice *vdev, QEMUFile *f)
 {
     int num, i, ret;
-    uint32_t features;
-    uint32_t supported_features =
+    uint32_t features_hi, features_lo;
+    uint64_t features;
+    uint64_t supported_features =
         vdev->binding->get_features(vdev->binding_opaque);
 
     if (vdev->binding->load_config) {
@@ -700,9 +706,17 @@  int virtio_load(VirtIODevice *vdev, QEMUFile *f)
     qemu_get_8s(f, &vdev->status);
     qemu_get_8s(f, &vdev->isr);
     qemu_get_be16s(f, &vdev->queue_sel);
-    qemu_get_be32s(f, &features);
+    qemu_get_be32s(f, &features_lo);
+    if (features_lo & (1ULL << VIRTIO_F_FEATURES_HI)) {
+        qemu_get_be32s(f, &features_hi);
+    } else {
+        features_hi = 0;
+    }
+    features = ((uint64_t)features_hi) << 32 | features_lo;
+    
     if (features & ~supported_features) {
-        error_report("Features 0x%x unsupported. Allowed features: 0x%x",
+        error_report("Features 0x%" PRIx64 " unsupported. "
+                     "Allowed features: 0x%" PRIx64,
                      features, supported_features);
         return -1;
     }
diff --git a/hw/virtio.h b/hw/virtio.h
index bc72289..9517b97 100644
--- a/hw/virtio.h
+++ b/hw/virtio.h
@@ -49,6 +49,9 @@ 
 /* A guest should never accept this.  It implies negotiation is broken. */
 #define VIRTIO_F_BAD_FEATURE		30
 
+/* Enables feature bits 32 to 63 (only really required for virtio_pci). */
+#define VIRTIO_F_FEATURES_HI		31
+
 /* from Linux's linux/virtio_ring.h */
 
 /* This marks a buffer as continuing via the next field. */
@@ -93,7 +96,7 @@  typedef struct {
     int (*load_config)(void * opaque, QEMUFile *f);
     int (*load_queue)(void * opaque, int n, QEMUFile *f);
     int (*load_done)(void * opaque, QEMUFile *f);
-    unsigned (*get_features)(void * opaque);
+    uint64_t (*get_features)(void * opaque);
     bool (*query_guest_notifiers)(void * opaque);
     int (*set_guest_notifiers)(void * opaque, bool assigned);
     int (*set_host_notifier)(void * opaque, int n, bool assigned);
@@ -110,14 +113,14 @@  struct VirtIODevice
     uint8_t status;
     uint8_t isr;
     uint16_t queue_sel;
-    uint32_t guest_features;
+    uint64_t guest_features;
     size_t config_len;
     void *config;
     uint16_t config_vector;
     int nvectors;
-    uint32_t (*get_features)(VirtIODevice *vdev, uint32_t requested_features);
-    uint32_t (*bad_features)(VirtIODevice *vdev);
-    void (*set_features)(VirtIODevice *vdev, uint32_t val);
+    uint64_t (*get_features)(VirtIODevice *vdev, uint64_t requested_features);
+    uint64_t (*bad_features)(VirtIODevice *vdev);
+    void (*set_features)(VirtIODevice *vdev, uint64_t val);
     void (*get_config)(VirtIODevice *vdev, uint8_t *config);
     void (*set_config)(VirtIODevice *vdev, const uint8_t *config);
     void (*reset)(VirtIODevice *vdev);
@@ -209,8 +212,8 @@  void virtio_blk_exit(VirtIODevice *vdev);
 void virtio_serial_exit(VirtIODevice *vdev);
 
 #define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \
-	DEFINE_PROP_BIT("indirect_desc", _state, _field, \
-			VIRTIO_RING_F_INDIRECT_DESC, true)
+	DEFINE_PROP_BIT64("indirect_desc", _state, _field, \
+			  VIRTIO_RING_F_INDIRECT_DESC, true)
 
 target_phys_addr_t virtio_queue_get_desc_addr(VirtIODevice *vdev, int n);
 target_phys_addr_t virtio_queue_get_avail_addr(VirtIODevice *vdev, int n);