<...snip...>

On 1/27/2026 8:15 AM, [email protected] wrote:
int virtio_pci_setup(VDev *vdev)
+{
+    VRing *vr;
+    int rc;
+    uint8_t status;
+    int i = 0;
+
+    vdev->guessed_disk_nature = VIRTIO_GDN_NONE;
+    vdev->cmd_vr_idx = 0;
+
+    if (virtio_reset(vdev)) {
+        return -EIO;
+    }
+
+    status = VPCI_S_ACKNOWLEDGE;
+    if (virtio_pci_set_status(status)) {
+        puts("Virtio-pci device Failed to ACKNOWLEDGE");
+        return -EIO;
+    }
+
+    rc = virtio_pci_read_pci_cap_config();
+    if (rc) {
+        puts("Invalid virtio PCI capabilities");
+        return -EIO;
+    }
+
+    vdev->guest_features[1] = VIRTIO_F_VERSION_1;
+    if (virtio_pci_negotiate()) {
+        panic("Virtio feature negotation failed!");
+    }
+
+    switch (vdev->dev_type) {
+    case VIRTIO_ID_BLOCK:
+        vdev->nr_vqs = 1;
+        vdev->cmd_vr_idx = 0;
+        vdev->config.blk.blk_size = 0;
+        virtio_pci_get_blk_config();
+        break;
+    default:
+        puts("Unsupported virtio device");
+        return -ENODEV;
+    }
+
+    status |= VPCI_S_DRIVER;
+    rc = virtio_pci_set_status(status);
+    if (rc) {
+        puts("Set status failed");
+        return -EIO;
+    }
+
+    /* Configure virt-queues for pci */
+    for (i = 0; i < vdev->nr_vqs; i++) {
+        VqInfo info = {
+            .queue = (unsigned long long) virtio_get_ring_area(i),
+            .align = KVM_S390_VIRTIO_RING_ALIGN,
+            .index = i,
+            .num = 0,
+        };
+
+        vr = &vdev->vrings[i];
+
+        if (vpci_read_flex(VPCI_C_COMMON_NUMQ, c_cap.bar, &info.num, 2)) {
+            return -EIO;
+        }
+
+        vring_init(vr, &info);
+
+        if (vpci_set_selected_vq(vr->id)) {
+            puts("Failed to set selected virt-queue");
+            return -EIO;
+        }
+
+        if (vpci_set_queue_size(VIRTIO_RING_SIZE)) {
+            puts("Failed to set virt-queue size");
+            return -EIO;
+        }
+
+        rc = set_pci_vq_addr(VPCI_C_OFFSET_Q_DESCLO, vr->desc);
+        rc |= set_pci_vq_addr(VPCI_C_OFFSET_Q_AVAILLO, vr->avail);
+        rc |= set_pci_vq_addr(VPCI_C_OFFSET_Q_USEDLO, vr->used);
+        if (rc) {
+            puts("Failed to configure virt-queue address");
+            return -EIO;
+        }
+
+        if (vpci_set_queue_enable(true)) {
+            puts("Failed to set virt-queue enabled");
+            return -EIO;
+        }
+    }
+
+    status |= VPCI_S_FEATURES_OK | VPCI_S_DRIVER_OK;
+    return virtio_pci_set_status(status);

Should we also enable Bus master bit as part of the device setup? QEMU though has a workaround where if we have setup the ACKNOWLEDGE and DRIVER status flags set then it would enable the Bus master by default since older Linux virtio-pci drivers didn't do it (https://github.com/qemu/qemu/blob/cd5a79dc98e3087e7658e643bdbbb0baec77ac8a/hw/virtio/virtio-pci.c#L480-L487). But latest virtio-pci drivers seems to be doing the right thing, so not sure how long this workaround would be supported :) .


+}
+

<..snip..>


diff --git a/pc-bios/s390-ccw/virtio-pci.h b/pc-bios/s390-ccw/virtio-pci.h
index 96c17ac3c7..883b00e0c6 100644
--- a/pc-bios/s390-ccw/virtio-pci.h
+++ b/pc-bios/s390-ccw/virtio-pci.h
@@ -71,6 +71,8 @@ typedef struct VirtioPciCap  VirtioPciCap;
  void virtio_pci_id2type(VDev *vdev, uint16_t device_id);
  int virtio_pci_reset(VDev *vdev);
  long virtio_pci_notify(int vq_id);
+int virtio_pci_setup(VDev *vdev);
+int virtio_pci_setup_device(void);
int vpci_read_flex(uint64_t offset, uint8_t pcias, void *buf, int len);
  int vpci_read_bswap64(uint64_t offset, uint8_t pcias, uint64_t *buf);
diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c
index 956b34ff33..f65571a920 100644
--- a/pc-bios/s390-ccw/virtio.c
+++ b/pc-bios/s390-ccw/virtio.c
@@ -17,6 +17,7 @@
  #include "virtio.h"
  #include "virtio-scsi.h"
  #include "virtio-ccw.h"
+#include "virtio-pci.h"
  #include "bswap.h"
  #include "helper.h"
  #include "s390-time.h"
@@ -112,6 +113,8 @@ bool vring_notify(VRing *vr)
      case S390_IPL_TYPE_CCW:
          vr->cookie = virtio_ccw_notify(vdev.schid, vr->id, vr->cookie);
          break;
+    case S390_IPL_TYPE_PCI:
+        vr->cookie = virtio_pci_notify(vr->id);
      default:
          return 1;
      }
@@ -119,8 +122,43 @@ bool vring_notify(VRing *vr)
      return vr->cookie >= 0;
  }
+/*
+ * Get endienness of the IPL type
+ * Return true for s390x native big-endian
+ */
+bool be_ipl(void)
+{
+    switch (virtio_get_device()->ipl_type) {
+    case S390_IPL_TYPE_QEMU_SCSI:
+    case S390_IPL_TYPE_CCW:
+        return true;
+    case S390_IPL_TYPE_PCI:
+        return false;
+    default:
+        return true;
+    }
+}
+
+/*
+ * Format the virtio ring descriptor endianness
+ * Return the available index increment in the appropriate endianness
+ */
+static uint16_t vr_fmt_descriptor(VRingDesc *desc)
+{
+    if (!be_ipl()) {
+        desc->addr = bswap64(desc->addr);
+        desc->len = bswap32(desc->len);
+        desc->flags = bswap16(desc->flags);
+        desc->next = bswap16(desc->next);
+    }
+
+    return be_ipl() ? 1 : bswap16(1);
+}
+
  void vring_send_buf(VRing *vr, void *p, int len, int flags)
  {
+    uint16_t increment;
+
      /* For follow-up chains we need to keep the first entry point */
      if (!(flags & VRING_HIDDEN_IS_CHAIN)) {
          vr->avail->ring[vr->avail->idx % vr->num] = vr->next_idx;
@@ -131,11 +169,13 @@ void vring_send_buf(VRing *vr, void *p, int len, int 
flags)
      vr->desc[vr->next_idx].flags = flags & ~VRING_HIDDEN_IS_CHAIN;
      vr->desc[vr->next_idx].next = vr->next_idx;
      vr->desc[vr->next_idx].next++;
+
+    increment = vr_fmt_descriptor(&vr->desc[vr->next_idx]);

If we are only returning 1 from the function vr_fmt_descriptor(), why not just set increment=1 or remove it completely and keep vr->avail->idx++? That way we don't need to do any endian conversion?

Thanks

Farhan


      vr->next_idx++;
/* Chains only have a single ID */
      if (!(flags & VRING_DESC_F_NEXT)) {
-        vr->avail->idx++;
+        vr->avail->idx += increment;
      }
  }

Reply via email to