Add GICv3BatchEntry typedef and three helper functions for batch register
access to KVM ARM64 VGICv3:
- kvm_arm_gic_batch_add: Accumulates register operations into a dynamic array
- kvm_arm_gic_batch_commit: Submits the batch array to kernel via a single ioctl
- kvm_arm_gic_batch_supported: Probes kernel support for the batch interface

These helpers will be used by kvm_arm_gicv3_get and kvm_arm_gicv3_put in 
subsequent
patches to enable efficient batch register access.

Signed-off-by: Chuan Zheng <[email protected]>
Signed-off-by: Yize Wang <[email protected]>
---
 hw/intc/arm_gicv3_kvm.c | 95 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 95 insertions(+)

diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c
index 22fcac1668..ea6c8462c7 100644
--- a/hw/intc/arm_gicv3_kvm.c
+++ b/hw/intc/arm_gicv3_kvm.c
@@ -118,6 +118,101 @@ static inline void kvm_gic_line_level_access(GICv3State 
*s, int irq, int cpu,
                       val, write, &error_abort);
 }
 
+typedef struct {
+    uint32_t group;
+    uint64_t attr;
+    uint32_t *val;
+} GICv3BatchEntry;
+
+/* Accumulates register operations into a dynamic array. */
+static void kvm_arm_gic_batch_add(GICv3BatchEntry **entries, int *n,
+                                  int *capacity, uint32_t group,
+                                  uint64_t attr, uint32_t *val)
+{
+    if (*n >= *capacity) {
+        *capacity = MAX(*capacity * 2, 256);
+        *entries = g_renew(GICv3BatchEntry, *entries, *capacity);
+    }
+    (*entries)[*n].group = group;
+    (*entries)[*n].attr = attr;
+    /* Store the pointer, not the value, to avoid copying data now. */
+    (*entries)[*n].val = val;
+    (*n)++;
+}
+
+/*
+ * Submit the batched GIC register operations to the kernel via a single ioctl.
+ *
+ * Workflow:
+ * 1. Allocate a kernel-compatible structure array
+ *    (kvm_dev_arm_vgic_batch_entry).
+ * 2. Convert QEMU's internal format to the kernel's expected format.
+ *    - For WRITE: Copy the actual values from QEMU's buffers into the
+ *      kernel struct.
+ *    - For READ:  Only set the register IDs; the kernel will fill in
+ *      the values.
+ * 3. Perform a single ioctl() to send/receive all data at once.
+ * 4. For READ: Copy the values returned by the kernel back into QEMU's
+ *    buffers.
+ */
+static void kvm_arm_gic_batch_commit(int dev_fd, GICv3BatchEntry *entries,
+                                     int n, bool write)
+{
+    struct kvm_dev_arm_vgic_batch_entry *batch;
+    struct kvm_device_attr attr;
+    int i, ret;
+
+    if (!n) {
+        return;
+    }
+
+    batch = g_new0(struct kvm_dev_arm_vgic_batch_entry, n);
+    for (i = 0; i < n; i++) {
+        batch[i].group = entries[i].group;
+        batch[i].attr = entries[i].attr;
+        if (write) {
+            batch[i].val = *entries[i].val;
+        }
+    }
+
+    attr.group = KVM_DEV_ARM_VGIC_GRP_BATCH_REGS;
+    attr.addr = (uint64_t)batch;
+    attr.attr = n;
+
+    if (write) {
+        ret = kvm_device_ioctl(dev_fd, KVM_SET_DEVICE_ATTR, &attr);
+    } else {
+        ret = kvm_device_ioctl(dev_fd, KVM_GET_DEVICE_ATTR, &attr);
+    }
+
+    if (ret < 0) {
+        error_report("KVM_%s_DEVICE_ATTR failed on batch regs: %s",
+                     write ? "SET" : "GET", strerror(-ret));
+        abort();
+    }
+
+    if (!write) {
+        for (i = 0; i < n; i++) {
+            *entries[i].val = batch[i].val;
+        }
+    }
+
+    g_free(batch);
+}
+
+/*
+ * Check if the kernel supports GIC batch register operations.
+ * Returns: true if the kernel supports the BATCH_REGS group,
+ * false otherwise.
+ */
+static bool kvm_arm_gic_batch_supported(int dev_fd)
+{
+    struct kvm_device_attr attr = {
+        .group = KVM_DEV_ARM_VGIC_GRP_BATCH_REGS,
+    };
+    return kvm_device_ioctl(dev_fd, KVM_HAS_DEVICE_ATTR, &attr) == 0;
+}
+
 /* Loop through each distributor IRQ related register; since bits
  * corresponding to SPIs and PPIs are RAZ/WI when affinity routing
  * is enabled, we skip those.
-- 
2.43.0


Reply via email to