The virtio specifications allows for up to 128 bits for the
device features. Soon we are going to use some of the 'extended'
bits features (above 64) for the virtio net driver.

Introduce a specific type to represent the virtio features bitmask.
On platform where 128 bits integer are available use such wide int
for the features bitmask, otherwise maintain the current u64.

Most drivers will keep using only 64 bits features space; use union
to allow them access the lower part of the extended space without any
per driver change, but let the features field initializers set the
extended space.

Signed-off-by: Paolo Abeni <pab...@redhat.com>
---
 hw/net/virtio-net.c                 |  2 +-
 hw/virtio/virtio-bus.c              |  4 +-
 hw/virtio/virtio.c                  |  4 +-
 include/hw/virtio/virtio-features.h | 90 +++++++++++++++++++++++++++++
 include/hw/virtio/virtio.h          |  9 +--
 5 files changed, 100 insertions(+), 9 deletions(-)
 create mode 100644 include/hw/virtio/virtio-features.h

diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 2de037c273..9f500c64e7 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -799,7 +799,7 @@ static uint64_t virtio_net_get_features(VirtIODevice *vdev, 
uint64_t features,
         virtio_clear_feature(&features, VIRTIO_NET_F_RSS);
     }
     features = vhost_net_get_features(get_vhost_net(nc->peer), features);
-    vdev->backend_features = features;
+    vdev->backend_features_ex = features;
 
     if (n->mtu_bypass_backend &&
             (n->host_features & 1ULL << VIRTIO_NET_F_MTU)) {
diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c
index 11adfbf3ab..9b84ead831 100644
--- a/hw/virtio/virtio-bus.c
+++ b/hw/virtio/virtio-bus.c
@@ -63,8 +63,8 @@ void virtio_bus_device_plugged(VirtIODevice *vdev, Error 
**errp)
 
     /* Get the features of the plugged device. */
     assert(vdc->get_features != NULL);
-    vdev->host_features = vdc->get_features(vdev, vdev->host_features,
-                                            &local_err);
+    vdev->host_features_ex = vdc->get_features(vdev, vdev->host_features,
+                                               &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 480c2e5036..701f59884d 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -2346,7 +2346,7 @@ void virtio_reset(void *opaque)
     vdev->start_on_kick = false;
     vdev->started = false;
     vdev->broken = false;
-    vdev->guest_features = 0;
+    vdev->guest_features_ex = 0;
     vdev->queue_sel = 0;
     vdev->status = 0;
     vdev->disabled = false;
@@ -3239,7 +3239,7 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int 
version_id)
      * Note: devices should always test host features in future - don't create
      * new dependencies like this.
      */
-    vdev->guest_features = features;
+    vdev->guest_features_ex = features;
 
     config_len = qemu_get_be32(f);
 
diff --git a/include/hw/virtio/virtio-features.h 
b/include/hw/virtio/virtio-features.h
new file mode 100644
index 0000000000..a0a115cd66
--- /dev/null
+++ b/include/hw/virtio/virtio-features.h
@@ -0,0 +1,90 @@
+/*
+ * Virtio features helpers
+ *
+ * Copyright 2025 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _QEMU_VIRTIO_FEATURES_H
+#define _QEMU_VIRTIO_FEATURES_H
+
+#define VIRTIO_FEATURES_FMT             "%016"PRIx64"%016"PRIx64
+
+#ifdef CONFIG_INT128
+#define VIRTIO_BIT(b)              ((__int128_t)1 << b)
+#define VIRTIO_FEATURES_WORDS      4
+#define VIRTIO_FEATURES_HI(f)      ((uint64_t)((f) >> 64))
+#define VIRTIO_FEATURES_LOW(f)     ((uint64_t)(f))
+
+typedef __uint128_t virtio_features_t;
+
+#if HOST_BIG_ENDIAN
+#define DECLARE_FEATURES(name)      \
+    union {                         \
+        struct {                    \
+            uint64_t name##_hi;     \
+            uint64_t name;          \
+        };                          \
+        __uint128_t  name##_ex;     \
+    }
+#else
+#define DECLARE_FEATURES(name)      \
+    union {                         \
+        struct {                    \
+            uint64_t name;          \
+            uint64_t name##_hi;     \
+        };                          \
+        __uint128_t  name##_ex;     \
+    }
+#endif
+
+static inline void virtio_add_feature_ex(__uint128_t *features,
+                                         unsigned int fbit)
+{
+    assert(fbit < 128);
+    *features |= VIRTIO_BIT(fbit);
+}
+
+static inline void virtio_clear_feature_ex(__uint128_t *features,
+                                           unsigned int fbit)
+{
+    assert(fbit < 128);
+    *features &= ~VIRTIO_BIT(fbit);
+}
+
+static inline bool virtio_has_feature_ex(__uint128_t features,
+                                         unsigned int fbit)
+{
+    assert(fbit < 128);
+    return !!(features & VIRTIO_BIT(fbit));
+}
+
+#else /* !CONFIG_INT128 */
+
+#define VIRTIO_BIT(b)              (1ULL << b)
+#define VIRTIO_FEATURES_WORDS      2
+#define VIRTIO_FEATURES_HI(f)      0
+#define VIRTIO_FEATURES_LOW(f)     f
+
+typedef uint64_t virtio_features_t;
+
+/*
+ * Without 128 bits support, 'features_ex' is just an alias for the 64 bits
+ * variable. This help avoiding conditionals in the core virtio code
+ * manipulation the features
+ */
+#define DECLARE_FEATURES(name)      \
+    union {                         \
+        uint64_t name;              \
+        uint64_t name##_ex;         \
+    }
+
+#define virtio_clear_feature_ex virtio_clear_feature
+#define virtio_add_feature_ex virtio_add_feature
+#define virtio_has_feature_ex virtio_has_feature
+
+#endif
+
+#endif
+
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index 7e0c471ea4..82ff6c1630 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -16,6 +16,7 @@
 
 #include "system/memory.h"
 #include "hw/qdev-core.h"
+#include "hw/virtio/virtio-features.h"
 #include "net/net.h"
 #include "migration/vmstate.h"
 #include "qemu/event_notifier.h"
@@ -121,9 +122,9 @@ struct VirtIODevice
      * backend (e.g. vhost) and could potentially be a subset of the
      * total feature set offered by QEMU.
      */
-    uint64_t host_features;
-    uint64_t guest_features;
-    uint64_t backend_features;
+    DECLARE_FEATURES(host_features);
+    DECLARE_FEATURES(guest_features);
+    DECLARE_FEATURES(backend_features);
 
     size_t config_len;
     void *config;
@@ -195,7 +196,7 @@ struct VirtioDeviceClass {
      * that are only exposed on the legacy interface but not
      * the modern one.
      */
-    uint64_t legacy_features;
+    virtio_features_t legacy_features;
     /* Test and clear event pending status.
      * Should be called after unmask to avoid losing events.
      * If backend does not support masking,
-- 
2.49.0


Reply via email to