On 20/10/2025 18.20, [email protected] wrote:
From: Jared Rossi <[email protected]>

Enable virt-queue PCI configuration and add routines for virtio-blk-pci devices.

Signed-off-by: Jared Rossi <[email protected]>
---
...
diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
index 1c1017a0db..4e4a7280b6 100644
--- a/pc-bios/s390-ccw/virtio.h
+++ b/pc-bios/s390-ccw/virtio.h
@@ -259,6 +259,7 @@ struct VDev {
      uint8_t scsi_dev_heads;
      bool scsi_device_selected;
      ScsiDevice selected_scsi_device;
+    uint32_t pci_fh;
      uint32_t max_transfer;
      uint32_t guest_features[2];
  };
diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
index 7299b8911f..69e7d39862 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -15,8 +15,10 @@
  #include "s390-arch.h"
  #include "s390-ccw.h"
  #include "cio.h"
+#include "clp.h"
  #include "virtio.h"
  #include "virtio-scsi.h"
+#include "virtio-pci.h"
  #include "dasd-ipl.h"
SubChannelId blk_schid = { .one = 1 };
@@ -150,6 +152,20 @@ static bool find_subch(int dev_no)
      return false;
  }
+static bool find_fid(uint32_t fid) {

Put the curly bracket into the next line, please.

+    ClpFhListEntry entry;
+    VDev *vdev = virtio_get_device();
+
+    if (find_pci_function(fid, &entry)) {
+        return false;
+    }
+
+    vdev->pci_fh = entry.fh;
+    virtio_pci_id2type(vdev, entry.device_id);
+
+    return (vdev->type != 0);

You could drop the braces here.

+}
+
...
diff --git a/pc-bios/s390-ccw/virtio-pci.c b/pc-bios/s390-ccw/virtio-pci.c
new file mode 100644
index 0000000000..b6cb4a661a
--- /dev/null
+++ b/pc-bios/s390-ccw/virtio-pci.c
@@ -0,0 +1,357 @@
+/*
+ * Functionality for virtio-pci
+ *
+ * Copyright 2025 IBM Corp.
+ * Author(s): Jared Rossi <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "clp.h"
+#include "pci.h"
+#include "helper.h"
+#include "s390-ccw.h"
+#include "virtio.h"
+#include "bswap.h"
+#include "virtio-pci.h"
+#include "s390-time.h"
+#include <stdio.h>
+
+/* Variable offsets used for reads/writes to modern memory region BAR 4 */
+uint32_t common_offset;
+uint32_t device_offset;
+uint32_t notify_offset;
+uint32_t notify_mult;
+uint16_t q_notify_offset;
+
+static int virtio_pci_set_status(VDev *vdev, uint8_t status)
+{
+    uint64_t status64 = status;
+
+    return pci_write(vdev->pci_fh, VPCI_C_OFFSET_STATUS, status64, 1);
+}
+
+static int virtio_pci_get_status(VDev *vdev, uint8_t *status)
+{
+    uint64_t status64;
+    int rc;
+
+    rc = pci_read(vdev->pci_fh, VPCI_C_OFFSET_STATUS, 4, &status64, 1);
+    if (rc) {
+        puts("Failed to read virtio-pci status");
+        return rc;
+    }
+
+    *status = (uint8_t) status64;

Ok, so after reading this code, I realized that pci_read is definitely only supposed to work on 64-bit values.

Could you please change the "buf" parameter of pci_read() from "void *" to "uint64_t *", otherwise this is super-confusing.

+    return 0;
+}
+
+static int virtio_pci_get_hfeatures(VDev *vdev, uint64_t *features)
+{
+    uint64_t feat0, feat1;
+    uint32_t selector;
+    int rc;
+
+    selector = bswap32(0);
+    rc = pci_write(vdev->pci_fh, VPCI_C_OFFSET_DFSELECT, selector, 4);
+    rc |= pci_read(vdev->pci_fh, VPCI_C_OFFSET_DF, 4, &feat0, 4);
+    feat0 = bswap32(feat0);
> +> +    selector = bswap32(1);
+    rc |= pci_write(vdev->pci_fh, VPCI_C_OFFSET_DFSELECT, selector, 4);
+    rc |= pci_read(vdev->pci_fh, VPCI_C_OFFSET_DF, 4, &feat1, 4);
+    feat1 = bswap32(feat1);
+> +    *features = feat1 << 32;
+    *features |= feat0;
+
+    return rc;
+}
+
+static int virtio_pci_set_gfeatures(VDev *vdev)
+{
+    uint64_t feats;
+    uint32_t selector;
+    int rc;
+
+    selector = bswap32(0);
+    rc = pci_write(vdev->pci_fh, VPCI_C_OFFSET_GFSELECT, selector, 4);
+
+    feats = bswap32((uint64_t)vdev->guest_features[1]);

The cast to 64-bit does not make sense here.

+    rc |= pci_write(vdev->pci_fh, VPCI_C_OFFSET_GF, feats, 4);
+
+    selector = bswap32(1);
+    rc |= pci_write(vdev->pci_fh, VPCI_C_OFFSET_GFSELECT, selector, 4);
+
+    feats = bswap32((uint64_t)vdev->guest_features[0]);

dito

+    rc |= pci_write(vdev->pci_fh, VPCI_C_OFFSET_GF, feats, 4);
+
+    return rc;
+}
+
+static int virtio_pci_get_blk_config(VDev *vdev)
+{
+    return pci_read(vdev->pci_fh, device_offset, 4, (uint64_t 
*)&vdev->config.blk,
+                    sizeof(VirtioBlkConfig));
+

Please remove the empty line.

+}
+
+int virtio_pci_set_selected_vq(VDev *vdev, uint16_t queue_num)
+{
+    uint16_t le_queue_num;
+
+    le_queue_num = bswap16(queue_num);
+    return pci_write(vdev->pci_fh, VPCI_C_OFFSET_Q_SELECT, 
(uint64_t)le_queue_num, 2);

The "(uint64_t)" is not required here, I think.

+}
+
+int virtio_pci_set_queue_size(VDev *vdev, uint16_t queue_size)
+{
+    uint16_t le_queue_size;
+
+    le_queue_size = bswap16(queue_size);
+    return pci_write(vdev->pci_fh, VPCI_C_OFFSET_Q_SIZE, 
(uint64_t)le_queue_size, 2);

dito

+}
+
+static int virtio_pci_set_queue_enable(VDev *vdev, uint16_t enabled)
+{
+    uint16_t le_enabled;
+
+    le_enabled = bswap16(enabled);
+    return pci_write(vdev->pci_fh, VPCI_C_OFFSET_Q_ENABLE, 
(uint64_t)le_enabled, 2);

dito

+}
+
+static int set_pci_vq_addr(VDev *vdev, void* addr, uint64_t config_offset_lo)
+{
+    uint32_t le_lo, le_hi;
+    uint32_t tmp;
+    int rc;
+
+    tmp = (uint32_t)(((uint64_t)addr) >> 32);
+    le_hi = bswap32(tmp);
+
+    tmp = (uint32_t)((uint64_t)addr & 0xFFFFFFFF);
+    le_lo = bswap32(tmp);
+
+    rc =  pci_write(vdev->pci_fh, config_offset_lo, (uint64_t)le_lo, 4);
+    rc |=  pci_write(vdev->pci_fh, config_offset_lo + 4, (uint64_t)le_hi, 4);

Wouldn't it be possible bswap64() the value and write all 8 bytes at once?

+    return rc;
+}
+
+/* virtio spec v1.1 para 4.1.2.1 */
+void virtio_pci_id2type(VDev *vdev, uint16_t device_id)
+{
+    switch(device_id) {
+    case 0x1001:
+        vdev->type = VIRTIO_ID_BLOCK;
+        break;
+    case 0x1000: /* Other valid but currently unsupported virtio device types 
*/
+    case 0x1004:
+    default:
+        vdev->type = 0;
+    }
+}
+
+/*
+ * Read PCI configuration space to find the offset of the Common, Device, and
+ * Notification memory regions within the modern memory space.
+ * Returns 0 if success, 1 if a capability could not be located, or a
+ * negative RC if the configuration read failed.
+ */
+static int virtio_pci_read_pci_cap_config(VDev *vdev)
+{
+    uint8_t pos;
+    uint64_t data;
+
+    /* Common cabilities */
+    pos = find_cap_pos(vdev->pci_fh, VIRTIO_PCI_CAP_COMMON_CFG);
+    if (!pos) {
+        puts("Failed to locate PCI common configuration");
+        return 1;
+    }
+    if (pci_read(vdev->pci_fh, pos + VIRTIO_PCI_CAP_OFFSET, 15, &data, 4)) {
+        return -EIO;
+    }
+    common_offset = bswap32(data);
+
+    /* Device cabilities */
+    pos = find_cap_pos(vdev->pci_fh, VIRTIO_PCI_CAP_DEVICE_CFG);
+    if (!pos) {
+        puts("Failed to locate PCI device configuration");
+        return 1;
+    }
+    if (pci_read(vdev->pci_fh, pos + VIRTIO_PCI_CAP_OFFSET, 15, &data, 4)) {
+        return -EIO;
+    }
+    device_offset = bswap32(data);
+
+    /* Notification cabilities */
+    pos = find_cap_pos(vdev->pci_fh, VIRTIO_PCI_CAP_NOTIFY_CFG);
+    if (!pos) {
+        puts("Failed to locate PCI notification configuration");
+        return 1;
+    }
+    if (pci_read(vdev->pci_fh, pos + VIRTIO_PCI_CAP_OFFSET, 15, &data, 4)) {
+        return -EIO;
+    }
+    notify_offset = bswap32(data);
+
+    if (pci_read(vdev->pci_fh, pos + VIRTIO_PCI_NOTIFY_CAP_MULT, 15, &data, 
4)) {
+        return -EIO;
+    }
+    notify_mult = bswap32(data);
+
+    if (pci_read(vdev->pci_fh, device_offset + VPCI_C_OFFSET_Q_NOFF, 4, &data, 
2)) {
+        return -EIO;
+    }
+    q_notify_offset = bswap16(data);

After reading all this code, I wonder whether it would make sense to have proper pci_read16(), pci_read32() and pci_read64() functions that do also the byte-swapping for you and operate on the right size of variable size?

+    return 0;
+}
+
+int virtio_pci_reset(VDev *vdev)
+{
+    int rc;
+    uint8_t status = VPCI_S_RESET;
+
+    rc = virtio_pci_set_status(vdev, status);
+    rc |= virtio_pci_get_status(vdev, &status);
+
+    if (rc || status) {
+        puts("Failed to reset virtio-pci device");
+        return 1;

Maybe nicer to return a negative error code (-1 if nothing else fits)?

+    }
+
+    return 0;
+}


 Thomas



Reply via email to