<...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;
}
}