* [PATCH 01/10] pc-bios/s390-ccw: Fix misattributed function prototypes
2025-12-10 20:54 [PATCH V2 0/10] s390x: Add support for virtio-blk-pci IPL device jrossi
@ 2025-12-10 20:54 ` jrossi
2026-01-06 20:55 ` Eric Farman
2025-12-10 20:54 ` [PATCH 02/10] pc-bios/s390-ccw: Store boot device type and bus separately jrossi
` (8 subsequent siblings)
9 siblings, 1 reply; 36+ messages in thread
From: jrossi @ 2025-12-10 20:54 UTC (permalink / raw)
To: qemu-devel, qemu-s390x, thuth, mst
Cc: jjherne, alifm, farman, mjrosato, jrossi, zycai
From: Jared Rossi <jrossi@linux.ibm.com>
The virtio-blkdev functions are incorrectly listed in s390-ccw.h as belonging to
virtio.c. Additionally, virtio_load_direct() has an unused subchan_id argument.
Remove the unused argument and move the prototypes to virtio.h so that they are
independent from the CCW bus.
Reviewed-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
---
pc-bios/s390-ccw/s390-ccw.h | 4 ----
pc-bios/s390-ccw/virtio.h | 7 +++++++
pc-bios/s390-ccw/bootmap.c | 2 +-
pc-bios/s390-ccw/virtio-blkdev.c | 2 +-
4 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index b1dc35cded..47ea66bd4d 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -67,11 +67,7 @@ void sclp_get_loadparm_ascii(char *loadparm);
int sclp_read(char *str, size_t count);
/* virtio.c */
-unsigned long virtio_load_direct(unsigned long rec_list1, unsigned long rec_list2,
- unsigned long subchan_id, void *load_addr);
bool virtio_is_supported(SubChannelId schid);
-int virtio_blk_setup_device(SubChannelId schid);
-int virtio_read(unsigned long sector, void *load_addr);
/* bootmap.c */
void zipl_load(void);
diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
index 5c5e808a50..597bd42358 100644
--- a/pc-bios/s390-ccw/virtio.h
+++ b/pc-bios/s390-ccw/virtio.h
@@ -277,7 +277,14 @@ int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd);
int virtio_reset(VDev *vdev);
int virtio_setup_ccw(VDev *vdev);
+/* virtio-net.c */
int virtio_net_init(void *mac_addr);
void virtio_net_deinit(void);
+/* virtio-blkdev.c */
+int virtio_blk_setup_device(SubChannelId schid);
+int virtio_read(unsigned long sector, void *load_addr);
+unsigned long virtio_load_direct(unsigned long rec_list1, unsigned long rec_list2,
+ void *load_addr);
+
#endif /* VIRTIO_H */
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 0f8baa0198..420ee32eff 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -662,7 +662,7 @@ static int zipl_load_segment(ComponentEntry *entry)
*/
break;
}
- address = virtio_load_direct(cur_desc[0], cur_desc[1], 0,
+ address = virtio_load_direct(cur_desc[0], cur_desc[1],
(void *)address);
if (!address) {
puts("zIPL load segment failed");
diff --git a/pc-bios/s390-ccw/virtio-blkdev.c b/pc-bios/s390-ccw/virtio-blkdev.c
index 7b2d1e20f4..4b819dd80f 100644
--- a/pc-bios/s390-ccw/virtio-blkdev.c
+++ b/pc-bios/s390-ccw/virtio-blkdev.c
@@ -64,7 +64,7 @@ int virtio_read_many(unsigned long sector, void *load_addr, int sec_num)
}
unsigned long virtio_load_direct(unsigned long rec_list1, unsigned long rec_list2,
- unsigned long subchan_id, void *load_addr)
+ void *load_addr)
{
u8 status;
int sec = rec_list1;
--
2.49.0
^ permalink raw reply related [flat|nested] 36+ messages in thread* Re: [PATCH 01/10] pc-bios/s390-ccw: Fix misattributed function prototypes
2025-12-10 20:54 ` [PATCH 01/10] pc-bios/s390-ccw: Fix misattributed function prototypes jrossi
@ 2026-01-06 20:55 ` Eric Farman
0 siblings, 0 replies; 36+ messages in thread
From: Eric Farman @ 2026-01-06 20:55 UTC (permalink / raw)
To: jrossi, qemu-devel, qemu-s390x, thuth, mst
Cc: jjherne, alifm, mjrosato, zycai
On Wed, 2025-12-10 at 15:54 -0500, jrossi@linux.ibm.com wrote:
> From: Jared Rossi <jrossi@linux.ibm.com>
>
> The virtio-blkdev functions are incorrectly listed in s390-ccw.h as belonging to
> virtio.c. Additionally, virtio_load_direct() has an unused subchan_id argument.
>
> Remove the unused argument and move the prototypes to virtio.h so that they are
> independent from the CCW bus.
>
> Reviewed-by: Thomas Huth <thuth@redhat.com>
> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
> ---
> pc-bios/s390-ccw/s390-ccw.h | 4 ----
> pc-bios/s390-ccw/virtio.h | 7 +++++++
> pc-bios/s390-ccw/bootmap.c | 2 +-
> pc-bios/s390-ccw/virtio-blkdev.c | 2 +-
> 4 files changed, 9 insertions(+), 6 deletions(-)
Reviewed-by: Eric Farman <farman@linux.ibm.com>
>
> diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
> index b1dc35cded..47ea66bd4d 100644
> --- a/pc-bios/s390-ccw/s390-ccw.h
> +++ b/pc-bios/s390-ccw/s390-ccw.h
> @@ -67,11 +67,7 @@ void sclp_get_loadparm_ascii(char *loadparm);
> int sclp_read(char *str, size_t count);
>
> /* virtio.c */
> -unsigned long virtio_load_direct(unsigned long rec_list1, unsigned long rec_list2,
> - unsigned long subchan_id, void *load_addr);
> bool virtio_is_supported(SubChannelId schid);
> -int virtio_blk_setup_device(SubChannelId schid);
> -int virtio_read(unsigned long sector, void *load_addr);
>
> /* bootmap.c */
> void zipl_load(void);
> diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
> index 5c5e808a50..597bd42358 100644
> --- a/pc-bios/s390-ccw/virtio.h
> +++ b/pc-bios/s390-ccw/virtio.h
> @@ -277,7 +277,14 @@ int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd);
> int virtio_reset(VDev *vdev);
> int virtio_setup_ccw(VDev *vdev);
>
> +/* virtio-net.c */
> int virtio_net_init(void *mac_addr);
> void virtio_net_deinit(void);
>
> +/* virtio-blkdev.c */
> +int virtio_blk_setup_device(SubChannelId schid);
> +int virtio_read(unsigned long sector, void *load_addr);
> +unsigned long virtio_load_direct(unsigned long rec_list1, unsigned long rec_list2,
> + void *load_addr);
> +
> #endif /* VIRTIO_H */
> diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
> index 0f8baa0198..420ee32eff 100644
> --- a/pc-bios/s390-ccw/bootmap.c
> +++ b/pc-bios/s390-ccw/bootmap.c
> @@ -662,7 +662,7 @@ static int zipl_load_segment(ComponentEntry *entry)
> */
> break;
> }
> - address = virtio_load_direct(cur_desc[0], cur_desc[1], 0,
> + address = virtio_load_direct(cur_desc[0], cur_desc[1],
> (void *)address);
> if (!address) {
> puts("zIPL load segment failed");
> diff --git a/pc-bios/s390-ccw/virtio-blkdev.c b/pc-bios/s390-ccw/virtio-blkdev.c
> index 7b2d1e20f4..4b819dd80f 100644
> --- a/pc-bios/s390-ccw/virtio-blkdev.c
> +++ b/pc-bios/s390-ccw/virtio-blkdev.c
> @@ -64,7 +64,7 @@ int virtio_read_many(unsigned long sector, void *load_addr, int sec_num)
> }
>
> unsigned long virtio_load_direct(unsigned long rec_list1, unsigned long rec_list2,
> - unsigned long subchan_id, void *load_addr)
> + void *load_addr)
> {
> u8 status;
> int sec = rec_list1;
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH 02/10] pc-bios/s390-ccw: Store boot device type and bus separately
2025-12-10 20:54 [PATCH V2 0/10] s390x: Add support for virtio-blk-pci IPL device jrossi
2025-12-10 20:54 ` [PATCH 01/10] pc-bios/s390-ccw: Fix misattributed function prototypes jrossi
@ 2025-12-10 20:54 ` jrossi
2026-01-06 21:08 ` Eric Farman
2026-01-07 8:53 ` Thomas Huth
2025-12-10 20:54 ` [PATCH 03/10] pc-bios/s390-ccw: Split virtio-ccw and generic virtio jrossi
` (7 subsequent siblings)
9 siblings, 2 replies; 36+ messages in thread
From: jrossi @ 2025-12-10 20:54 UTC (permalink / raw)
To: qemu-devel, qemu-s390x, thuth, mst
Cc: jjherne, alifm, farman, mjrosato, jrossi, zycai
From: Jared Rossi <jrossi@linux.ibm.com>
Store both the device type (e.g. block) and device bus (e.g. CCW) to determine
IPL format rather than assume all devices can be identified by CCW specific
sense data.
Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
---
hw/s390x/ipl.h | 5 ----
include/hw/s390x/ipl/qipl.h | 7 ++++++
pc-bios/s390-ccw/iplb.h | 4 ----
pc-bios/s390-ccw/virtio.h | 2 ++
pc-bios/s390-ccw/main.c | 10 +++++---
pc-bios/s390-ccw/virtio-blkdev.c | 39 +++++++++++++++++++-------------
pc-bios/s390-ccw/virtio.c | 13 +++++++----
7 files changed, 48 insertions(+), 32 deletions(-)
diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index 505cded490..aec2244321 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -103,11 +103,6 @@ QEMU_BUILD_BUG_MSG(offsetof(S390IPLState, iplb) & 3, "alignment of iplb wrong");
#define DIAG308_PV_STORE 9
#define DIAG308_PV_START 10
-#define S390_IPL_TYPE_FCP 0x00
-#define S390_IPL_TYPE_CCW 0x02
-#define S390_IPL_TYPE_PV 0x05
-#define S390_IPL_TYPE_QEMU_SCSI 0xff
-
#define S390_IPLB_HEADER_LEN 8
#define S390_IPLB_MIN_PV_LEN 148
#define S390_IPLB_MIN_CCW_LEN 200
diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
index 6824391111..8199b839f0 100644
--- a/include/hw/s390x/ipl/qipl.h
+++ b/include/hw/s390x/ipl/qipl.h
@@ -20,6 +20,13 @@
#define LOADPARM_LEN 8
#define NO_LOADPARM "\0\0\0\0\0\0\0\0"
+#define S390_IPL_TYPE_FCP 0x00
+#define S390_IPL_TYPE_CCW 0x02
+#define S390_IPL_TYPE_PV 0x05
+#define S390_IPL_TYPE_QEMU_SCSI 0xff
+
+#define QEMU_DEFAULT_IPL S390_IPL_TYPE_CCW
+
/*
* The QEMU IPL Parameters will be stored at absolute address
* 204 (0xcc) which means it is 32-bit word aligned but not
diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h
index 08f259ff31..926e8eed5d 100644
--- a/pc-bios/s390-ccw/iplb.h
+++ b/pc-bios/s390-ccw/iplb.h
@@ -23,10 +23,6 @@ extern QemuIplParameters qipl;
extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
extern bool have_iplb;
-#define S390_IPL_TYPE_FCP 0x00
-#define S390_IPL_TYPE_CCW 0x02
-#define S390_IPL_TYPE_QEMU_SCSI 0xff
-
static inline bool manage_iplb(IplParameterBlock *iplb, bool store)
{
register unsigned long addr asm("0") = (unsigned long) iplb;
diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
index 597bd42358..d557a4a90e 100644
--- a/pc-bios/s390-ccw/virtio.h
+++ b/pc-bios/s390-ccw/virtio.h
@@ -239,6 +239,8 @@ struct VDev {
VirtioGDN guessed_disk_nature;
SubChannelId schid;
SenseId senseid;
+ VirtioDevType dev_type;
+ int ipl_type;
union {
VirtioBlkConfig blk;
VirtioScsiConfig scsi;
diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
index 76bf743900..fef192c934 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -162,7 +162,7 @@ static void menu_setup(void)
return;
}
- switch (iplb.pbt) {
+ switch (virtio_get_device()->ipl_type) {
case S390_IPL_TYPE_CCW:
case S390_IPL_TYPE_QEMU_SCSI:
menu_set_parms(qipl.qipl_flags & BOOT_MENU_FLAG_MASK,
@@ -190,6 +190,7 @@ static void css_setup(void)
static void boot_setup(void)
{
char lpmsg[] = "LOADPARM=[________]\n";
+ VDev *vdev = virtio_get_device();
if (have_iplb && memcmp(iplb.loadparm, NO_LOADPARM, LOADPARM_LEN) != 0) {
ebcdic_to_ascii((char *) iplb.loadparm, loadparm_str, LOADPARM_LEN);
@@ -198,7 +199,10 @@ static void boot_setup(void)
}
if (have_iplb) {
+ vdev->ipl_type = iplb.pbt;
menu_setup();
+ } else {
+ vdev->ipl_type = QEMU_DEFAULT_IPL;
}
memcpy(lpmsg + 10, loadparm_str, 8);
@@ -216,7 +220,7 @@ static bool find_boot_device(void)
VDev *vdev = virtio_get_device();
bool found = false;
- switch (iplb.pbt) {
+ switch (vdev->ipl_type) {
case S390_IPL_TYPE_CCW:
vdev->scsi_device_selected = false;
debug_print_int("device no. ", iplb.ccw.devno);
@@ -245,7 +249,7 @@ static int virtio_setup(void)
vdev->is_cdrom = false;
int ret;
- switch (vdev->senseid.cu_model) {
+ switch (vdev->dev_type) {
case VIRTIO_ID_NET:
puts("Network boot device detected");
return 0;
diff --git a/pc-bios/s390-ccw/virtio-blkdev.c b/pc-bios/s390-ccw/virtio-blkdev.c
index 4b819dd80f..f40a9407c2 100644
--- a/pc-bios/s390-ccw/virtio-blkdev.c
+++ b/pc-bios/s390-ccw/virtio-blkdev.c
@@ -53,14 +53,14 @@ int virtio_read_many(unsigned long sector, void *load_addr, int sec_num)
{
VDev *vdev = virtio_get_device();
- switch (vdev->senseid.cu_model) {
+ switch (vdev->dev_type) {
case VIRTIO_ID_BLOCK:
return virtio_blk_read_many(vdev, sector, load_addr, sec_num);
case VIRTIO_ID_SCSI:
return virtio_scsi_read_many(vdev, sector, load_addr, sec_num);
+ default:
+ return -1;
}
-
- return -1;
}
unsigned long virtio_load_direct(unsigned long rec_list1, unsigned long rec_list2,
@@ -119,7 +119,7 @@ void virtio_assume_iso9660(void)
{
VDev *vdev = virtio_get_device();
- switch (vdev->senseid.cu_model) {
+ switch (vdev->dev_type) {
case VIRTIO_ID_BLOCK:
vdev->guessed_disk_nature = VIRTIO_GDN_SCSI;
vdev->config.blk.blk_size = VIRTIO_ISO_BLOCK_SIZE;
@@ -129,6 +129,8 @@ void virtio_assume_iso9660(void)
case VIRTIO_ID_SCSI:
vdev->scsi_block_size = VIRTIO_ISO_BLOCK_SIZE;
break;
+ default:
+ return;
}
}
@@ -139,13 +141,15 @@ void virtio_assume_eckd(void)
vdev->guessed_disk_nature = VIRTIO_GDN_DASD;
vdev->blk_factor = 1;
vdev->config.blk.physical_block_exp = 0;
- switch (vdev->senseid.cu_model) {
+ switch (vdev->dev_type) {
case VIRTIO_ID_BLOCK:
vdev->config.blk.blk_size = VIRTIO_DASD_DEFAULT_BLOCK_SIZE;
break;
case VIRTIO_ID_SCSI:
vdev->config.blk.blk_size = vdev->scsi_block_size;
break;
+ default:
+ return;
}
vdev->config.blk.geometry.heads = 15;
vdev->config.blk.geometry.sectors =
@@ -162,50 +166,52 @@ bool virtio_ipl_disk_is_valid(void)
return true;
}
- return (vdev->senseid.cu_model == VIRTIO_ID_BLOCK ||
- vdev->senseid.cu_model == VIRTIO_ID_SCSI) &&
- blksize >= 512 && blksize <= 4096;
+ return (vdev->dev_type == VIRTIO_ID_BLOCK || vdev->dev_type == VIRTIO_ID_SCSI)
+ && blksize >= 512 && blksize <= 4096;
}
int virtio_get_block_size(void)
{
VDev *vdev = virtio_get_device();
- switch (vdev->senseid.cu_model) {
+ switch (vdev->dev_type) {
case VIRTIO_ID_BLOCK:
return vdev->config.blk.blk_size;
case VIRTIO_ID_SCSI:
return vdev->scsi_block_size;
+ default:
+ return 0;
}
- return 0;
}
uint8_t virtio_get_heads(void)
{
VDev *vdev = virtio_get_device();
- switch (vdev->senseid.cu_model) {
+ switch (vdev->dev_type) {
case VIRTIO_ID_BLOCK:
return vdev->config.blk.geometry.heads;
case VIRTIO_ID_SCSI:
return vdev->guessed_disk_nature == VIRTIO_GDN_DASD
? vdev->config.blk.geometry.heads : 255;
+ default:
+ return 0;
}
- return 0;
}
uint8_t virtio_get_sectors(void)
{
VDev *vdev = virtio_get_device();
- switch (vdev->senseid.cu_model) {
+ switch (vdev->dev_type) {
case VIRTIO_ID_BLOCK:
return vdev->config.blk.geometry.sectors;
case VIRTIO_ID_SCSI:
return vdev->guessed_disk_nature == VIRTIO_GDN_DASD
? vdev->config.blk.geometry.sectors : 63;
+ default:
+ return 0;
}
- return 0;
}
uint64_t virtio_get_blocks(void)
@@ -213,13 +219,14 @@ uint64_t virtio_get_blocks(void)
VDev *vdev = virtio_get_device();
const uint64_t factor = virtio_get_block_size() / VIRTIO_SECTOR_SIZE;
- switch (vdev->senseid.cu_model) {
+ switch (vdev->dev_type) {
case VIRTIO_ID_BLOCK:
return vdev->config.blk.capacity / factor;
case VIRTIO_ID_SCSI:
return vdev->scsi_last_block / factor;
+ default:
+ return 0;
}
- return 0;
}
int virtio_blk_setup_device(SubChannelId schid)
diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c
index cd6c99c7e3..0f4f201038 100644
--- a/pc-bios/s390-ccw/virtio.c
+++ b/pc-bios/s390-ccw/virtio.c
@@ -41,7 +41,7 @@ VDev *virtio_get_device(void)
VirtioDevType virtio_get_device_type(void)
{
- return vdev.senseid.cu_model;
+ return vdev.dev_type;
}
/* virtio spec v1.0 para 4.3.3.2 */
@@ -103,7 +103,7 @@ static int run_ccw(VDev *vdev, int cmd, void *ptr, int len, bool sli)
ccw.flags |= CCW_FLAG_SLI;
}
- return do_cio(vdev->schid, vdev->senseid.cu_type, ptr2u32(&ccw), CCW_FMT1);
+ return do_cio(vdev->schid, vdev->dev_type, ptr2u32(&ccw), CCW_FMT1);
}
static void vring_init(VRing *vr, VqInfo *info)
@@ -248,7 +248,7 @@ int virtio_setup_ccw(VDev *vdev)
return -EIO;
}
- switch (vdev->senseid.cu_model) {
+ switch (vdev->dev_type) {
case VIRTIO_ID_NET:
vdev->nr_vqs = 2;
vdev->cmd_vr_idx = 0;
@@ -347,12 +347,17 @@ bool virtio_is_supported(SubChannelId schid)
true)) {
return false;
}
+
+ vdev.dev_type = vdev.senseid.cu_model;
+
if (vdev.senseid.cu_type == 0x3832) {
- switch (vdev.senseid.cu_model) {
+ switch (vdev.dev_type) {
case VIRTIO_ID_BLOCK:
case VIRTIO_ID_SCSI:
case VIRTIO_ID_NET:
return true;
+ default:
+ return false;
}
}
return false;
--
2.49.0
^ permalink raw reply related [flat|nested] 36+ messages in thread* Re: [PATCH 02/10] pc-bios/s390-ccw: Store boot device type and bus separately
2025-12-10 20:54 ` [PATCH 02/10] pc-bios/s390-ccw: Store boot device type and bus separately jrossi
@ 2026-01-06 21:08 ` Eric Farman
2026-01-07 8:53 ` Thomas Huth
1 sibling, 0 replies; 36+ messages in thread
From: Eric Farman @ 2026-01-06 21:08 UTC (permalink / raw)
To: jrossi, qemu-devel, qemu-s390x, thuth, mst
Cc: jjherne, alifm, mjrosato, zycai
On Wed, 2025-12-10 at 15:54 -0500, jrossi@linux.ibm.com wrote:
> From: Jared Rossi <jrossi@linux.ibm.com>
>
> Store both the device type (e.g. block) and device bus (e.g. CCW) to determine
> IPL format rather than assume all devices can be identified by CCW specific
> sense data.
>
> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
> ---
> hw/s390x/ipl.h | 5 ----
> include/hw/s390x/ipl/qipl.h | 7 ++++++
> pc-bios/s390-ccw/iplb.h | 4 ----
> pc-bios/s390-ccw/virtio.h | 2 ++
> pc-bios/s390-ccw/main.c | 10 +++++---
> pc-bios/s390-ccw/virtio-blkdev.c | 39 +++++++++++++++++++-------------
> pc-bios/s390-ccw/virtio.c | 13 +++++++----
> 7 files changed, 48 insertions(+), 32 deletions(-)
I might've suggested that this be broken up even further, to distinguish between the vdev->ipl_type
(nee iplb.pbt) and the vdev->dev_type (nee vdev->senseid.cu_model) changes, and maybe even the
consolidation of the S390_IPL_TYPE_* defines, to make it more obvious that things are just moving
around without any actual change. But, this is honestly fine.
Reviewed-by: Eric Farman <farman@linux.ibm.com>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH 02/10] pc-bios/s390-ccw: Store boot device type and bus separately
2025-12-10 20:54 ` [PATCH 02/10] pc-bios/s390-ccw: Store boot device type and bus separately jrossi
2026-01-06 21:08 ` Eric Farman
@ 2026-01-07 8:53 ` Thomas Huth
1 sibling, 0 replies; 36+ messages in thread
From: Thomas Huth @ 2026-01-07 8:53 UTC (permalink / raw)
To: jrossi, qemu-devel, qemu-s390x, mst
Cc: jjherne, alifm, farman, mjrosato, zycai
On 10/12/2025 21.54, jrossi@linux.ibm.com wrote:
> From: Jared Rossi <jrossi@linux.ibm.com>
>
> Store both the device type (e.g. block) and device bus (e.g. CCW) to determine
> IPL format rather than assume all devices can be identified by CCW specific
> sense data.
>
> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
> ---
...
> diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
> index 6824391111..8199b839f0 100644
> --- a/include/hw/s390x/ipl/qipl.h
> +++ b/include/hw/s390x/ipl/qipl.h
> @@ -20,6 +20,13 @@
> #define LOADPARM_LEN 8
> #define NO_LOADPARM "\0\0\0\0\0\0\0\0"
>
> +#define S390_IPL_TYPE_FCP 0x00
> +#define S390_IPL_TYPE_CCW 0x02
> +#define S390_IPL_TYPE_PV 0x05
> +#define S390_IPL_TYPE_QEMU_SCSI 0xff
> +
> +#define QEMU_DEFAULT_IPL S390_IPL_TYPE_CCW
Would it make sense to turn this into an enum as well?
> /*
> * The QEMU IPL Parameters will be stored at absolute address
> * 204 (0xcc) which means it is 32-bit word aligned but not
...
> diff --git a/pc-bios/s390-ccw/virtio-blkdev.c b/pc-bios/s390-ccw/virtio-blkdev.c
> index 4b819dd80f..f40a9407c2 100644
> --- a/pc-bios/s390-ccw/virtio-blkdev.c
> +++ b/pc-bios/s390-ccw/virtio-blkdev.c
...
> @@ -129,6 +129,8 @@ void virtio_assume_iso9660(void)
> case VIRTIO_ID_SCSI:
> vdev->scsi_block_size = VIRTIO_ISO_BLOCK_SIZE;
> break;
> + default:
> + return;
> }
> }
>
> @@ -139,13 +141,15 @@ void virtio_assume_eckd(void)
> vdev->guessed_disk_nature = VIRTIO_GDN_DASD;
> vdev->blk_factor = 1;
> vdev->config.blk.physical_block_exp = 0;
> - switch (vdev->senseid.cu_model) {
> + switch (vdev->dev_type) {
> case VIRTIO_ID_BLOCK:
> vdev->config.blk.blk_size = VIRTIO_DASD_DEFAULT_BLOCK_SIZE;
> break;
> case VIRTIO_ID_SCSI:
> vdev->config.blk.blk_size = vdev->scsi_block_size;
> break;
> + default:
> + return;
This looks like errors could silently be ignored here. Maybe rather panic()
here instead of returning? Or use "break" to maintain the old behavior?
> }
> vdev->config.blk.geometry.heads = 15;
> vdev->config.blk.geometry.sectors =
Thomas
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH 03/10] pc-bios/s390-ccw: Split virtio-ccw and generic virtio
2025-12-10 20:54 [PATCH V2 0/10] s390x: Add support for virtio-blk-pci IPL device jrossi
2025-12-10 20:54 ` [PATCH 01/10] pc-bios/s390-ccw: Fix misattributed function prototypes jrossi
2025-12-10 20:54 ` [PATCH 02/10] pc-bios/s390-ccw: Store boot device type and bus separately jrossi
@ 2025-12-10 20:54 ` jrossi
2026-01-07 9:40 ` Thomas Huth
2026-01-07 14:27 ` Eric Farman
2025-12-10 20:54 ` [PATCH 04/10] include/hw/s390x: Move CLP definitions for easier BIOS access jrossi
` (6 subsequent siblings)
9 siblings, 2 replies; 36+ messages in thread
From: jrossi @ 2025-12-10 20:54 UTC (permalink / raw)
To: qemu-devel, qemu-s390x, thuth, mst
Cc: jjherne, alifm, farman, mjrosato, jrossi, zycai
From: Jared Rossi <jrossi@linux.ibm.com>
Separate the CCW specific virtio routines and create generic wrappers for easier
reuse of existing virtio functions with non-CCW devices.
Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
---
pc-bios/s390-ccw/s390-ccw.h | 3 -
pc-bios/s390-ccw/virtio-ccw.h | 24 +++
pc-bios/s390-ccw/virtio-scsi.h | 2 +-
pc-bios/s390-ccw/virtio.h | 7 +-
pc-bios/s390-ccw/main.c | 10 +-
pc-bios/s390-ccw/netmain.c | 2 +-
pc-bios/s390-ccw/virtio-blkdev.c | 15 +-
pc-bios/s390-ccw/virtio-ccw.c | 242 +++++++++++++++++++++++++++++++
pc-bios/s390-ccw/virtio-net.c | 5 +-
pc-bios/s390-ccw/virtio-scsi.c | 6 +-
pc-bios/s390-ccw/virtio.c | 237 +++++-------------------------
pc-bios/s390-ccw/Makefile | 3 +-
12 files changed, 334 insertions(+), 222 deletions(-)
create mode 100644 pc-bios/s390-ccw/virtio-ccw.h
create mode 100644 pc-bios/s390-ccw/virtio-ccw.c
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 47ea66bd4d..ccd68ff0a4 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -66,9 +66,6 @@ void sclp_setup(void);
void sclp_get_loadparm_ascii(char *loadparm);
int sclp_read(char *str, size_t count);
-/* virtio.c */
-bool virtio_is_supported(SubChannelId schid);
-
/* bootmap.c */
void zipl_load(void);
diff --git a/pc-bios/s390-ccw/virtio-ccw.h b/pc-bios/s390-ccw/virtio-ccw.h
new file mode 100644
index 0000000000..cdf6a55dc8
--- /dev/null
+++ b/pc-bios/s390-ccw/virtio-ccw.h
@@ -0,0 +1,24 @@
+/*
+ * Virtio definitions for CCW devices
+ *
+ * Copyright 2025 IBM Corp.
+ * Author(s): Jared Rossi <jrossi@linux.ibm.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef VIRTIO_CCW_H
+#define VIRTIO_CCW_H
+
+/* main.c */
+extern SubChannelId blk_schid;
+
+/* virtio-ccw.c */
+int drain_irqs_ccw(SubChannelId schid);
+bool virtio_ccw_is_supported(SubChannelId schid);
+int virtio_ccw_run(VDev *vdev, int vqid, VirtioCmd *cmd);
+long virtio_ccw_notify(SubChannelId schid, int vq_idx, long cookie);
+int virtio_ccw_setup(VDev *vdev);
+int virtio_ccw_reset(VDev *vdev);
+
+#endif
diff --git a/pc-bios/s390-ccw/virtio-scsi.h b/pc-bios/s390-ccw/virtio-scsi.h
index c5612e16a2..7a37f8b45a 100644
--- a/pc-bios/s390-ccw/virtio-scsi.h
+++ b/pc-bios/s390-ccw/virtio-scsi.h
@@ -69,6 +69,6 @@ static inline bool virtio_scsi_response_ok(const VirtioScsiCmdResp *r)
int virtio_scsi_read_many(VDev *vdev,
unsigned long sector, void *load_addr, int sec_num);
-int virtio_scsi_setup_device(SubChannelId schid);
+int virtio_scsi_setup_device(void);
#endif /* VIRTIO_SCSI_H */
diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
index d557a4a90e..e747891a2c 100644
--- a/pc-bios/s390-ccw/virtio.h
+++ b/pc-bios/s390-ccw/virtio.h
@@ -109,6 +109,7 @@ struct VRing {
};
typedef struct VRing VRing;
+char *virtio_get_ring_area(void);
/***********************************************
* Virtio block *
@@ -270,8 +271,10 @@ struct VirtioCmd {
};
typedef struct VirtioCmd VirtioCmd;
+void vring_init(VRing *vr, VqInfo *info);
+bool virtio_is_supported(VDev *vdev);
bool vring_notify(VRing *vr);
-int drain_irqs(SubChannelId schid);
+int drain_irqs(VRing *vr);
void vring_send_buf(VRing *vr, void *p, int len, int flags);
int vr_poll(VRing *vr);
int vring_wait_reply(void);
@@ -284,7 +287,7 @@ int virtio_net_init(void *mac_addr);
void virtio_net_deinit(void);
/* virtio-blkdev.c */
-int virtio_blk_setup_device(SubChannelId schid);
+int virtio_blk_setup_device(void);
int virtio_read(unsigned long sector, void *load_addr);
unsigned long virtio_load_direct(unsigned long rec_list1, unsigned long rec_list2,
void *load_addr);
diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
index fef192c934..e82d60bbb7 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -72,6 +72,7 @@ static int is_dev_possibly_bootable(int dev_no, int sch_no)
Schib schib;
int r;
+ VDev *vdev = virtio_get_device();
blk_schid.sch_no = sch_no;
r = stsch_err(blk_schid, &schib);
if (r == 3 || r == -EIO) {
@@ -91,7 +92,8 @@ static int is_dev_possibly_bootable(int dev_no, int sch_no)
* Note: we always have to run virtio_is_supported() here to make
* sure that the vdev.senseid data gets pre-initialized correctly
*/
- is_virtio = virtio_is_supported(blk_schid);
+ vdev->schid = blk_schid;
+ is_virtio = virtio_is_supported(vdev);
/* No specific devno given, just return whether the device is possibly bootable */
if (dev_no < 0) {
@@ -254,10 +256,12 @@ static int virtio_setup(void)
puts("Network boot device detected");
return 0;
case VIRTIO_ID_BLOCK:
- ret = virtio_blk_setup_device(blk_schid);
+ vdev->schid = blk_schid;
+ ret = virtio_blk_setup_device();
break;
case VIRTIO_ID_SCSI:
- ret = virtio_scsi_setup_device(blk_schid);
+ vdev->schid = blk_schid;
+ ret = virtio_scsi_setup_device();
break;
default:
puts("\n! No IPL device available !\n");
diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c
index a9521dff41..651cedf6ef 100644
--- a/pc-bios/s390-ccw/netmain.c
+++ b/pc-bios/s390-ccw/netmain.c
@@ -500,7 +500,7 @@ static bool find_net_dev(Schib *schib, int dev_no)
continue;
}
enable_subchannel(net_schid);
- if (!virtio_is_supported(net_schid)) {
+ if (!virtio_is_supported(virtio_get_device())) {
continue;
}
if (virtio_get_device_type() != VIRTIO_ID_NET) {
diff --git a/pc-bios/s390-ccw/virtio-blkdev.c b/pc-bios/s390-ccw/virtio-blkdev.c
index f40a9407c2..87ab9a9513 100644
--- a/pc-bios/s390-ccw/virtio-blkdev.c
+++ b/pc-bios/s390-ccw/virtio-blkdev.c
@@ -12,6 +12,7 @@
#include "s390-ccw.h"
#include "virtio.h"
#include "virtio-scsi.h"
+#include "virtio-ccw.h"
#define VIRTIO_BLK_F_GEOMETRY (1 << 4)
#define VIRTIO_BLK_F_BLK_SIZE (1 << 6)
@@ -42,7 +43,7 @@ static int virtio_blk_read_many(VDev *vdev, unsigned long sector, void *load_add
/* Now we can tell the host to read */
vring_wait_reply();
- if (drain_irqs(vr->schid)) {
+ if (drain_irqs(vr)) {
/* Well, whatever status is supposed to contain... */
status = 1;
}
@@ -229,15 +230,19 @@ uint64_t virtio_get_blocks(void)
}
}
-int virtio_blk_setup_device(SubChannelId schid)
+int virtio_blk_setup_device(void)
{
VDev *vdev = virtio_get_device();
vdev->guest_features[0] = VIRTIO_BLK_F_GEOMETRY | VIRTIO_BLK_F_BLK_SIZE;
- vdev->schid = schid;
- virtio_setup_ccw(vdev);
puts("Using virtio-blk.");
- return 0;
+ switch (virtio_get_device()->ipl_type) {
+ case S390_IPL_TYPE_QEMU_SCSI:
+ case S390_IPL_TYPE_CCW:
+ return virtio_ccw_setup(vdev);
+ }
+
+ return 1;
}
diff --git a/pc-bios/s390-ccw/virtio-ccw.c b/pc-bios/s390-ccw/virtio-ccw.c
new file mode 100644
index 0000000000..e121826625
--- /dev/null
+++ b/pc-bios/s390-ccw/virtio-ccw.c
@@ -0,0 +1,242 @@
+/*
+ * Virtio functionality for CCW devices
+ *
+ * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
+ * Copyright 2025 IBM Corp. Author(s): Jared Rossi <jrossi@linux.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
+ * your option) any later version. See the COPYING file in the top-level
+ * directory.
+ */
+
+#include <string.h>
+#include "s390-ccw.h"
+#include "cio.h"
+#include "virtio.h"
+#include "virtio-ccw.h"
+#include "virtio-scsi.h"
+#include "bswap.h"
+#include "helper.h"
+#include "s390-time.h"
+
+/* virtio spec v1.0 para 4.3.3.2 */
+static long kvm_hypercall(unsigned long nr, unsigned long param1,
+ unsigned long param2, unsigned long param3)
+{
+ register unsigned long r_nr asm("1") = nr;
+ register unsigned long r_param1 asm("2") = param1;
+ register unsigned long r_param2 asm("3") = param2;
+ register unsigned long r_param3 asm("4") = param3;
+ register long retval asm("2");
+
+ asm volatile ("diag %%r2,%%r4,0x500"
+ : "=d" (retval)
+ : "d" (r_nr), "0" (r_param1), "r"(r_param2), "d"(r_param3)
+ : "memory", "cc");
+
+ return retval;
+}
+
+static int run_ccw(VDev *vdev, int cmd, void *ptr, int len, bool sli)
+{
+ Ccw1 ccw = {};
+
+ ccw.cmd_code = cmd;
+ ccw.cda = (long)ptr;
+ ccw.count = len;
+
+ if (sli) {
+ ccw.flags |= CCW_FLAG_SLI;
+ }
+
+ return do_cio(vdev->schid, vdev->senseid.cu_type, ptr2u32(&ccw), CCW_FMT1);
+}
+
+bool virtio_ccw_is_supported(SubChannelId schid)
+{
+ VDev *vdev = virtio_get_device();
+ vdev->schid = schid;
+ memset(&vdev->senseid, 0, sizeof(vdev->senseid));
+
+ /*
+ * Run sense id command.
+ * The size of the senseid data differs between devices (notably,
+ * between virtio devices and dasds), so specify the largest possible
+ * size and suppress the incorrect length indication for smaller sizes.
+ */
+ if (run_ccw(vdev, CCW_CMD_SENSE_ID, &vdev->senseid, sizeof(vdev->senseid),
+ true)) {
+ return false;
+ }
+
+ vdev->dev_type = vdev->senseid.cu_model;
+
+ if (vdev->senseid.cu_type == 0x3832) {
+ switch (vdev->dev_type) {
+ case VIRTIO_ID_BLOCK:
+ case VIRTIO_ID_SCSI:
+ case VIRTIO_ID_NET:
+ return true;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+int drain_irqs_ccw(SubChannelId schid)
+{
+ Irb irb = {};
+ int r = 0;
+
+ while (1) {
+ /* FIXME: make use of TPI, for that enable subchannel and isc */
+ if (tsch(schid, &irb)) {
+ /* Might want to differentiate error codes later on. */
+ if (irb.scsw.cstat) {
+ r = -EIO;
+ } else if (irb.scsw.dstat != 0xc) {
+ r = -EIO;
+ }
+ return r;
+ }
+ }
+}
+
+long virtio_ccw_notify(SubChannelId schid, int vq_idx, long cookie)
+{
+ return kvm_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, *(u32 *)&schid,
+ vq_idx, cookie);
+}
+
+int virtio_ccw_run(VDev *vdev, int vqid, VirtioCmd *cmd)
+{
+ VRing *vr = &vdev->vrings[vqid];
+ int i = 0;
+
+ do {
+ vring_send_buf(vr, cmd[i].data, cmd[i].size,
+ cmd[i].flags | (i ? VRING_HIDDEN_IS_CHAIN : 0));
+ } while (cmd[i++].flags & VRING_DESC_F_NEXT);
+
+ vring_wait_reply();
+ if (drain_irqs(vr)) {
+ return -1;
+ }
+ return 0;
+}
+
+int virtio_ccw_reset(VDev *vdev)
+{
+ return run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0, false);
+}
+
+int virtio_ccw_setup(VDev *vdev)
+{
+ int i, cfg_size = 0;
+ uint8_t status;
+ struct VirtioFeatureDesc {
+ uint32_t features;
+ uint8_t index;
+ } __attribute__((packed)) feats;
+
+ if (!virtio_ccw_is_supported(vdev->schid)) {
+ puts("Virtio unsupported for this device ID");
+ return -ENODEV;
+ }
+ /* device ID has been established now */
+
+ vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */
+ vdev->guessed_disk_nature = VIRTIO_GDN_NONE;
+
+ virtio_reset(vdev);
+
+ status = VIRTIO_CONFIG_S_ACKNOWLEDGE;
+ if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
+ puts("Could not write ACKNOWLEDGE status to host");
+ return -EIO;
+ }
+
+ switch (vdev->dev_type) {
+ case VIRTIO_ID_NET:
+ vdev->nr_vqs = 2;
+ vdev->cmd_vr_idx = 0;
+ cfg_size = sizeof(vdev->config.net);
+ break;
+ case VIRTIO_ID_BLOCK:
+ vdev->nr_vqs = 1;
+ vdev->cmd_vr_idx = 0;
+ cfg_size = sizeof(vdev->config.blk);
+ break;
+ case VIRTIO_ID_SCSI:
+ vdev->nr_vqs = 3;
+ vdev->cmd_vr_idx = VR_REQUEST;
+ cfg_size = sizeof(vdev->config.scsi);
+ break;
+ default:
+ puts("Unsupported virtio device");
+ return -ENODEV;
+ }
+
+ status |= VIRTIO_CONFIG_S_DRIVER;
+ if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
+ puts("Could not write DRIVER status to host");
+ return -EIO;
+ }
+
+ /* Feature negotiation */
+ for (i = 0; i < ARRAY_SIZE(vdev->guest_features); i++) {
+ feats.features = 0;
+ feats.index = i;
+ if (run_ccw(vdev, CCW_CMD_READ_FEAT, &feats, sizeof(feats), false)) {
+ puts("Could not get features bits");
+ return -EIO;
+ }
+
+ vdev->guest_features[i] &= bswap32(feats.features);
+ feats.features = bswap32(vdev->guest_features[i]);
+ if (run_ccw(vdev, CCW_CMD_WRITE_FEAT, &feats, sizeof(feats), false)) {
+ puts("Could not set features bits");
+ return -EIO;
+ }
+ }
+
+ if (run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false)) {
+ puts("Could not get virtio device configuration");
+ return -EIO;
+ }
+
+ for (i = 0; i < vdev->nr_vqs; i++) {
+ VqInfo info = {
+ .queue = (unsigned long long) virtio_get_ring_area() + (i * VIRTIO_RING_SIZE),
+ .align = KVM_S390_VIRTIO_RING_ALIGN,
+ .index = i,
+ .num = 0,
+ };
+ VqConfig config = {
+ .index = i,
+ .num = 0,
+ };
+
+ if (run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config),
+ false)) {
+ puts("Could not get virtio device VQ config");
+ return -EIO;
+ }
+ info.num = config.num;
+ vring_init(&vdev->vrings[i], &info);
+ vdev->vrings[i].schid = vdev->schid;
+ if (run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info), false)) {
+ puts("Cannot set VQ info");
+ return -EIO;
+ }
+ }
+
+ status |= VIRTIO_CONFIG_S_DRIVER_OK;
+ if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
+ puts("Could not write DRIVER_OK status to host");
+ return -EIO;
+ }
+
+ return 0;
+}
diff --git a/pc-bios/s390-ccw/virtio-net.c b/pc-bios/s390-ccw/virtio-net.c
index 301445bf97..604f1cf003 100644
--- a/pc-bios/s390-ccw/virtio-net.c
+++ b/pc-bios/s390-ccw/virtio-net.c
@@ -19,6 +19,7 @@
#include <ethernet.h>
#include "s390-ccw.h"
#include "virtio.h"
+#include "virtio-ccw.h"
#include "s390-time.h"
#include "helper.h"
@@ -54,7 +55,7 @@ int virtio_net_init(void *mac_addr)
rx_last_idx = 0;
vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT;
- virtio_setup_ccw(vdev);
+ virtio_ccw_setup(vdev);
if (!(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT)) {
puts("virtio-net device does not support the MAC address feature");
@@ -88,7 +89,7 @@ int send(int fd, const void *buf, int len, int flags)
while (!vr_poll(txvq)) {
yield();
}
- if (drain_irqs(txvq->schid)) {
+ if (drain_irqs(txvq)) {
puts("send: drain irqs failed");
return -1;
}
diff --git a/pc-bios/s390-ccw/virtio-scsi.c b/pc-bios/s390-ccw/virtio-scsi.c
index 71db75ce7b..6ab0f755f2 100644
--- a/pc-bios/s390-ccw/virtio-scsi.c
+++ b/pc-bios/s390-ccw/virtio-scsi.c
@@ -15,6 +15,7 @@
#include "virtio.h"
#include "scsi.h"
#include "virtio-scsi.h"
+#include "virtio-ccw.h"
#include "s390-time.h"
#include "helper.h"
@@ -476,12 +477,11 @@ static int virtio_scsi_setup(VDev *vdev)
return 0;
}
-int virtio_scsi_setup_device(SubChannelId schid)
+int virtio_scsi_setup_device(void)
{
VDev *vdev = virtio_get_device();
- vdev->schid = schid;
- virtio_setup_ccw(vdev);
+ virtio_ccw_setup(vdev);
if (vdev->config.scsi.sense_size != VIRTIO_SCSI_SENSE_SIZE) {
puts("Config: sense size mismatch");
diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c
index 0f4f201038..0488b3a07e 100644
--- a/pc-bios/s390-ccw/virtio.c
+++ b/pc-bios/s390-ccw/virtio.c
@@ -2,6 +2,7 @@
* Virtio driver bits
*
* Copyright (c) 2013 Alexander Graf <agraf@suse.de>
+ * Copyright 2025 IBM Corp. Author(s): Jared Rossi <jrossi@linux.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or (at
* your option) any later version. See the COPYING file in the top-level
@@ -13,6 +14,7 @@
#include "cio.h"
#include "virtio.h"
#include "virtio-scsi.h"
+#include "virtio-ccw.h"
#include "bswap.h"
#include "helper.h"
#include "s390-time.h"
@@ -44,69 +46,38 @@ VirtioDevType virtio_get_device_type(void)
return vdev.dev_type;
}
-/* virtio spec v1.0 para 4.3.3.2 */
-static long kvm_hypercall(unsigned long nr, unsigned long param1,
- unsigned long param2, unsigned long param3)
+char *virtio_get_ring_area(void)
{
- register unsigned long r_nr asm("1") = nr;
- register unsigned long r_param1 asm("2") = param1;
- register unsigned long r_param2 asm("3") = param2;
- register unsigned long r_param3 asm("4") = param3;
- register long retval asm("2");
-
- asm volatile ("diag %%r2,%%r4,0x500"
- : "=d" (retval)
- : "d" (r_nr), "0" (r_param1), "r"(r_param2), "d"(r_param3)
- : "memory", "cc");
-
- return retval;
-}
-
-static long virtio_notify(SubChannelId schid, int vq_idx, long cookie)
-{
- return kvm_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, *(u32 *)&schid,
- vq_idx, cookie);
+ return ring_area;
}
/***********************************************
* Virtio functions *
***********************************************/
-int drain_irqs(SubChannelId schid)
+int drain_irqs(VRing *vr)
{
- Irb irb = {};
- int r = 0;
-
- while (1) {
- /* FIXME: make use of TPI, for that enable subchannel and isc */
- if (tsch(schid, &irb)) {
- /* Might want to differentiate error codes later on. */
- if (irb.scsw.cstat) {
- r = -EIO;
- } else if (irb.scsw.dstat != 0xc) {
- r = -EIO;
- }
- return r;
- }
+ switch (vdev.ipl_type) {
+ case S390_IPL_TYPE_QEMU_SCSI:
+ case S390_IPL_TYPE_CCW:
+ return drain_irqs_ccw(vr->schid);
+ default:
+ return 0;
}
}
-static int run_ccw(VDev *vdev, int cmd, void *ptr, int len, bool sli)
+int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd)
{
- Ccw1 ccw = {};
-
- ccw.cmd_code = cmd;
- ccw.cda = (long)ptr;
- ccw.count = len;
-
- if (sli) {
- ccw.flags |= CCW_FLAG_SLI;
+ switch (vdev->ipl_type) {
+ case S390_IPL_TYPE_QEMU_SCSI:
+ case S390_IPL_TYPE_CCW:
+ return virtio_ccw_run(vdev, vqid, cmd);
+ default:
+ return -1;
}
-
- return do_cio(vdev->schid, vdev->dev_type, ptr2u32(&ccw), CCW_FMT1);
}
-static void vring_init(VRing *vr, VqInfo *info)
+void vring_init(VRing *vr, VqInfo *info)
{
void *p = (void *) info->queue;
@@ -134,7 +105,12 @@ static void vring_init(VRing *vr, VqInfo *info)
bool vring_notify(VRing *vr)
{
- vr->cookie = virtio_notify(vr->schid, vr->id, vr->cookie);
+ switch (vdev.ipl_type) {
+ case S390_IPL_TYPE_QEMU_SCSI:
+ case S390_IPL_TYPE_CCW:
+ vr->cookie = virtio_ccw_notify(vr->schid, vr->id, vr->cookie);
+ }
+
return vr->cookie >= 0;
}
@@ -200,165 +176,24 @@ int vring_wait_reply(void)
return 1;
}
-int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd)
-{
- VRing *vr = &vdev->vrings[vqid];
- int i = 0;
-
- do {
- vring_send_buf(vr, cmd[i].data, cmd[i].size,
- cmd[i].flags | (i ? VRING_HIDDEN_IS_CHAIN : 0));
- } while (cmd[i++].flags & VRING_DESC_F_NEXT);
-
- vring_wait_reply();
- if (drain_irqs(vr->schid)) {
- return -1;
- }
- return 0;
-}
-
int virtio_reset(VDev *vdev)
{
- return run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0, false);
-}
-
-int virtio_setup_ccw(VDev *vdev)
-{
- int i, cfg_size = 0;
- uint8_t status;
- struct VirtioFeatureDesc {
- uint32_t features;
- uint8_t index;
- } __attribute__((packed)) feats;
-
- if (!virtio_is_supported(vdev->schid)) {
- puts("Virtio unsupported for this device ID");
- return -ENODEV;
- }
- /* device ID has been established now */
-
- vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */
- vdev->guessed_disk_nature = VIRTIO_GDN_NONE;
-
- virtio_reset(vdev);
-
- status = VIRTIO_CONFIG_S_ACKNOWLEDGE;
- if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
- puts("Could not write ACKNOWLEDGE status to host");
- return -EIO;
- }
-
- switch (vdev->dev_type) {
- case VIRTIO_ID_NET:
- vdev->nr_vqs = 2;
- vdev->cmd_vr_idx = 0;
- cfg_size = sizeof(vdev->config.net);
- break;
- case VIRTIO_ID_BLOCK:
- vdev->nr_vqs = 1;
- vdev->cmd_vr_idx = 0;
- cfg_size = sizeof(vdev->config.blk);
- break;
- case VIRTIO_ID_SCSI:
- vdev->nr_vqs = 3;
- vdev->cmd_vr_idx = VR_REQUEST;
- cfg_size = sizeof(vdev->config.scsi);
- break;
+ switch (vdev->ipl_type) {
+ case S390_IPL_TYPE_QEMU_SCSI:
+ case S390_IPL_TYPE_CCW:
+ return virtio_ccw_reset(vdev);
default:
- puts("Unsupported virtio device");
- return -ENODEV;
- }
-
- status |= VIRTIO_CONFIG_S_DRIVER;
- if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
- puts("Could not write DRIVER status to host");
- return -EIO;
- }
-
- /* Feature negotiation */
- for (i = 0; i < ARRAY_SIZE(vdev->guest_features); i++) {
- feats.features = 0;
- feats.index = i;
- if (run_ccw(vdev, CCW_CMD_READ_FEAT, &feats, sizeof(feats), false)) {
- puts("Could not get features bits");
- return -EIO;
- }
-
- vdev->guest_features[i] &= bswap32(feats.features);
- feats.features = bswap32(vdev->guest_features[i]);
- if (run_ccw(vdev, CCW_CMD_WRITE_FEAT, &feats, sizeof(feats), false)) {
- puts("Could not set features bits");
- return -EIO;
- }
- }
-
- if (run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false)) {
- puts("Could not get virtio device configuration");
- return -EIO;
- }
-
- for (i = 0; i < vdev->nr_vqs; i++) {
- VqInfo info = {
- .queue = (unsigned long long) ring_area + (i * VIRTIO_RING_SIZE),
- .align = KVM_S390_VIRTIO_RING_ALIGN,
- .index = i,
- .num = 0,
- };
- VqConfig config = {
- .index = i,
- .num = 0,
- };
-
- if (run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config),
- false)) {
- puts("Could not get virtio device VQ config");
- return -EIO;
- }
- info.num = config.num;
- vring_init(&vdev->vrings[i], &info);
- vdev->vrings[i].schid = vdev->schid;
- if (run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info), false)) {
- puts("Cannot set VQ info");
- return -EIO;
- }
- }
-
- status |= VIRTIO_CONFIG_S_DRIVER_OK;
- if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
- puts("Could not write DRIVER_OK status to host");
- return -EIO;
+ return -1;
}
-
- return 0;
}
-bool virtio_is_supported(SubChannelId schid)
+bool virtio_is_supported(VDev *vdev)
{
- vdev.schid = schid;
- memset(&vdev.senseid, 0, sizeof(vdev.senseid));
-
- /*
- * Run sense id command.
- * The size of the senseid data differs between devices (notably,
- * between virtio devices and dasds), so specify the largest possible
- * size and suppress the incorrect length indication for smaller sizes.
- */
- if (run_ccw(&vdev, CCW_CMD_SENSE_ID, &vdev.senseid, sizeof(vdev.senseid),
- true)) {
+ switch (vdev->ipl_type) {
+ case S390_IPL_TYPE_QEMU_SCSI:
+ case S390_IPL_TYPE_CCW:
+ return virtio_ccw_is_supported(vdev->schid);
+ default:
return false;
}
-
- vdev.dev_type = vdev.senseid.cu_model;
-
- if (vdev.senseid.cu_type == 0x3832) {
- switch (vdev.dev_type) {
- case VIRTIO_ID_BLOCK:
- case VIRTIO_ID_SCSI:
- case VIRTIO_ID_NET:
- return true;
- default:
- return false;
- }
- }
- return false;
}
diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
index a0f24c94a8..259cff09db 100644
--- a/pc-bios/s390-ccw/Makefile
+++ b/pc-bios/s390-ccw/Makefile
@@ -34,7 +34,8 @@ QEMU_DGFLAGS = -MMD -MP -MT $@ -MF $(@D)/$(*F).d
.PHONY : all clean build-all distclean
OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o netmain.o \
- virtio.o virtio-net.o virtio-scsi.o virtio-blkdev.o cio.o dasd-ipl.o
+ virtio.o virtio-net.o virtio-scsi.o virtio-blkdev.o cio.o dasd-ipl.o \
+ virtio-ccw.o
SLOF_DIR := $(SRC_PATH)/../../roms/SLOF
--
2.49.0
^ permalink raw reply related [flat|nested] 36+ messages in thread* Re: [PATCH 03/10] pc-bios/s390-ccw: Split virtio-ccw and generic virtio
2025-12-10 20:54 ` [PATCH 03/10] pc-bios/s390-ccw: Split virtio-ccw and generic virtio jrossi
@ 2026-01-07 9:40 ` Thomas Huth
2026-01-07 16:38 ` Jared Rossi
2026-01-07 14:27 ` Eric Farman
1 sibling, 1 reply; 36+ messages in thread
From: Thomas Huth @ 2026-01-07 9:40 UTC (permalink / raw)
To: jrossi, qemu-devel, qemu-s390x, mst
Cc: jjherne, alifm, farman, mjrosato, zycai
On 10/12/2025 21.54, jrossi@linux.ibm.com wrote:
> From: Jared Rossi <jrossi@linux.ibm.com>
>
> Separate the CCW specific virtio routines and create generic wrappers for easier
> reuse of existing virtio functions with non-CCW devices.
>
> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
> ---
...
> diff --git a/pc-bios/s390-ccw/virtio-ccw.c b/pc-bios/s390-ccw/virtio-ccw.c
> new file mode 100644
> index 0000000000..e121826625
> --- /dev/null
> +++ b/pc-bios/s390-ccw/virtio-ccw.c
> @@ -0,0 +1,242 @@
> +/*
> + * Virtio functionality for CCW devices
> + *
> + * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
> + * Copyright 2025 IBM Corp. Author(s): Jared Rossi <jrossi@linux.ibm.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or (at
> + * your option) any later version. See the COPYING file in the top-level
> + * directory.
> + */
> +
> +#include <string.h>
> +#include "s390-ccw.h"
> +#include "cio.h"
> +#include "virtio.h"
> +#include "virtio-ccw.h"
> +#include "virtio-scsi.h"
> +#include "bswap.h"
> +#include "helper.h"
> +#include "s390-time.h"
> +
> +/* virtio spec v1.0 para 4.3.3.2 */
> +static long kvm_hypercall(unsigned long nr, unsigned long param1,
> + unsigned long param2, unsigned long param3)
> +{
> + register unsigned long r_nr asm("1") = nr;
> + register unsigned long r_param1 asm("2") = param1;
> + register unsigned long r_param2 asm("3") = param2;
> + register unsigned long r_param3 asm("4") = param3;
> + register long retval asm("2");
> +
> + asm volatile ("diag %%r2,%%r4,0x500"
> + : "=d" (retval)
> + : "d" (r_nr), "0" (r_param1), "r"(r_param2), "d"(r_param3)
> + : "memory", "cc");
> +
> + return retval;
> +}
> +
> +static int run_ccw(VDev *vdev, int cmd, void *ptr, int len, bool sli)
> +{
> + Ccw1 ccw = {};
> +
> + ccw.cmd_code = cmd;
> + ccw.cda = (long)ptr;
> + ccw.count = len;
> +
> + if (sli) {
> + ccw.flags |= CCW_FLAG_SLI;
> + }
> +
> + return do_cio(vdev->schid, vdev->senseid.cu_type, ptr2u32(&ccw), CCW_FMT1);
> +}
> +
> +bool virtio_ccw_is_supported(SubChannelId schid)
> +{
> + VDev *vdev = virtio_get_device();
> + vdev->schid = schid;
At the calling site (virtio_ccw_setup), you're doing:
if (!virtio_ccw_is_supported(vdev->schid)) ...
i.e. vdev->schid is already set up, and you already have a pointer to vdev
there. So I think it would make more sense to replace the "schid" parameter
of this function here to a "vdev" pointer, and drop the call to
virtio_get_device() here, and not to re-assign vdev->schid here.
> + memset(&vdev->senseid, 0, sizeof(vdev->senseid));
> +
> + /*
> + * Run sense id command.
> + * The size of the senseid data differs between devices (notably,
> + * between virtio devices and dasds), so specify the largest possible
> + * size and suppress the incorrect length indication for smaller sizes.
> + */
> + if (run_ccw(vdev, CCW_CMD_SENSE_ID, &vdev->senseid, sizeof(vdev->senseid),
> + true)) {
> + return false;
> + }
> +
> + vdev->dev_type = vdev->senseid.cu_model;
> +
> + if (vdev->senseid.cu_type == 0x3832) {
> + switch (vdev->dev_type) {
> + case VIRTIO_ID_BLOCK:
> + case VIRTIO_ID_SCSI:
> + case VIRTIO_ID_NET:
> + return true;
> + default:
> + return false;
> + }
> + }
> + return false;
> +}
...
> +int virtio_ccw_setup(VDev *vdev)
> +{
> + int i, cfg_size = 0;
> + uint8_t status;
> + struct VirtioFeatureDesc {
> + uint32_t features;
> + uint8_t index;
> + } __attribute__((packed)) feats;
> +
> + if (!virtio_ccw_is_supported(vdev->schid)) {
> + puts("Virtio unsupported for this device ID");
> + return -ENODEV;
> + }
> + /* device ID has been established now */
> +
> + vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */
> + vdev->guessed_disk_nature = VIRTIO_GDN_NONE;
> +
> + virtio_reset(vdev);
> +
> + status = VIRTIO_CONFIG_S_ACKNOWLEDGE;
> + if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
> + puts("Could not write ACKNOWLEDGE status to host");
> + return -EIO;
> + }
> +
> + switch (vdev->dev_type) {
> + case VIRTIO_ID_NET:
> + vdev->nr_vqs = 2;
> + vdev->cmd_vr_idx = 0;
> + cfg_size = sizeof(vdev->config.net);
> + break;
> + case VIRTIO_ID_BLOCK:
> + vdev->nr_vqs = 1;
> + vdev->cmd_vr_idx = 0;
> + cfg_size = sizeof(vdev->config.blk);
> + break;
> + case VIRTIO_ID_SCSI:
> + vdev->nr_vqs = 3;
> + vdev->cmd_vr_idx = VR_REQUEST;
> + cfg_size = sizeof(vdev->config.scsi);
> + break;
> + default:
> + puts("Unsupported virtio device");
> + return -ENODEV;
> + }
> +
> + status |= VIRTIO_CONFIG_S_DRIVER;
> + if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
> + puts("Could not write DRIVER status to host");
> + return -EIO;
> + }
> +
> + /* Feature negotiation */
> + for (i = 0; i < ARRAY_SIZE(vdev->guest_features); i++) {
> + feats.features = 0;
> + feats.index = i;
> + if (run_ccw(vdev, CCW_CMD_READ_FEAT, &feats, sizeof(feats), false)) {
> + puts("Could not get features bits");
> + return -EIO;
> + }
> +
> + vdev->guest_features[i] &= bswap32(feats.features);
> + feats.features = bswap32(vdev->guest_features[i]);
> + if (run_ccw(vdev, CCW_CMD_WRITE_FEAT, &feats, sizeof(feats), false)) {
> + puts("Could not set features bits");
> + return -EIO;
> + }
> + }
> +
> + if (run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false)) {
> + puts("Could not get virtio device configuration");
> + return -EIO;
> + }
> +
> + for (i = 0; i < vdev->nr_vqs; i++) {
> + VqInfo info = {
> + .queue = (unsigned long long) virtio_get_ring_area() + (i * VIRTIO_RING_SIZE),
Would it make sense to add a "ring_num" parameter to virtio_get_ring_area(),
so that you could call virtio_get_ring_area(i) here instead?
virtio_get_ring_area() would then look like this:
char *virtio_get_ring_area(int ring_num)
{
return ring_area + ring_num * VIRTIO_RING_SIZE;
}
?
> + .align = KVM_S390_VIRTIO_RING_ALIGN,
> + .index = i,
> + .num = 0,
> + };
> + VqConfig config = {
> + .index = i,
> + .num = 0,
> + };
> +
> + if (run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config),
> + false)) {
> + puts("Could not get virtio device VQ config");
> + return -EIO;
> + }
> + info.num = config.num;
> + vring_init(&vdev->vrings[i], &info);
> + vdev->vrings[i].schid = vdev->schid;
> + if (run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info), false)) {
> + puts("Cannot set VQ info");
> + return -EIO;
> + }
> + }
> +
> + status |= VIRTIO_CONFIG_S_DRIVER_OK;
> + if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
> + puts("Could not write DRIVER_OK status to host");
> + return -EIO;
> + }
> +
> + return 0;
> +}
...
> diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c
> index 0f4f201038..0488b3a07e 100644
> --- a/pc-bios/s390-ccw/virtio.c
> +++ b/pc-bios/s390-ccw/virtio.c
> @@ -2,6 +2,7 @@
> * Virtio driver bits
> *
> * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
> + * Copyright 2025 IBM Corp. Author(s): Jared Rossi <jrossi@linux.ibm.com>
I assume you wanted to put Authors on a separate line?
Thomas
^ permalink raw reply [flat|nested] 36+ messages in thread* Re: [PATCH 03/10] pc-bios/s390-ccw: Split virtio-ccw and generic virtio
2026-01-07 9:40 ` Thomas Huth
@ 2026-01-07 16:38 ` Jared Rossi
2026-01-08 6:02 ` Thomas Huth
0 siblings, 1 reply; 36+ messages in thread
From: Jared Rossi @ 2026-01-07 16:38 UTC (permalink / raw)
To: Thomas Huth, qemu-devel, qemu-s390x, mst
Cc: jjherne, alifm, farman, mjrosato, zycai
On 1/7/26 4:40 AM, Thomas Huth wrote:
> On 10/12/2025 21.54, jrossi@linux.ibm.com wrote:
>> From: Jared Rossi <jrossi@linux.ibm.com>
>>
>> Separate the CCW specific virtio routines and create generic wrappers
>> for easier
>> reuse of existing virtio functions with non-CCW devices.
>>
>> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
>> ---
> ...
>> diff --git a/pc-bios/s390-ccw/virtio-ccw.c
>> b/pc-bios/s390-ccw/virtio-ccw.c
>> new file mode 100644
>> index 0000000000..e121826625
>> --- /dev/null
>> +++ b/pc-bios/s390-ccw/virtio-ccw.c
>> @@ -0,0 +1,242 @@
>> +/*
>> + * Virtio functionality for CCW devices
>> + *
>> + * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
>> + * Copyright 2025 IBM Corp. Author(s): Jared Rossi
>> <jrossi@linux.ibm.com>
>> + *
>> + * This work is licensed under the terms of the GNU GPL, version 2
>> or (at
>> + * your option) any later version. See the COPYING file in the
>> top-level
>> + * directory.
>> + */
>> +
>> +#include <string.h>
>> +#include "s390-ccw.h"
>> +#include "cio.h"
>> +#include "virtio.h"
>> +#include "virtio-ccw.h"
>> +#include "virtio-scsi.h"
>> +#include "bswap.h"
>> +#include "helper.h"
>> +#include "s390-time.h"
>> +
>> +/* virtio spec v1.0 para 4.3.3.2 */
>> +static long kvm_hypercall(unsigned long nr, unsigned long param1,
>> + unsigned long param2, unsigned long param3)
>> +{
>> + register unsigned long r_nr asm("1") = nr;
>> + register unsigned long r_param1 asm("2") = param1;
>> + register unsigned long r_param2 asm("3") = param2;
>> + register unsigned long r_param3 asm("4") = param3;
>> + register long retval asm("2");
>> +
>> + asm volatile ("diag %%r2,%%r4,0x500"
>> + : "=d" (retval)
>> + : "d" (r_nr), "0" (r_param1), "r"(r_param2),
>> "d"(r_param3)
>> + : "memory", "cc");
>> +
>> + return retval;
>> +}
>> +
>> +static int run_ccw(VDev *vdev, int cmd, void *ptr, int len, bool sli)
>> +{
>> + Ccw1 ccw = {};
>> +
>> + ccw.cmd_code = cmd;
>> + ccw.cda = (long)ptr;
>> + ccw.count = len;
>> +
>> + if (sli) {
>> + ccw.flags |= CCW_FLAG_SLI;
>> + }
>> +
>> + return do_cio(vdev->schid, vdev->senseid.cu_type, ptr2u32(&ccw),
>> CCW_FMT1);
>> +}
>> +
>> +bool virtio_ccw_is_supported(SubChannelId schid)
>> +{
>> + VDev *vdev = virtio_get_device();
> > + vdev->schid = schid;
>
> At the calling site (virtio_ccw_setup), you're doing:
>
> if (!virtio_ccw_is_supported(vdev->schid)) ...
>
> i.e. vdev->schid is already set up, and you already have a pointer to
> vdev there. So I think it would make more sense to replace the "schid"
> parameter of this function here to a "vdev" pointer, and drop the call
> to virtio_get_device() here, and not to re-assign vdev->schid here.
I will work on cleaning up the vdev->schid assignments and accesses
throughout. Eric pointed out several areas that it seems to be
redundant also.
>
>> + memset(&vdev->senseid, 0, sizeof(vdev->senseid));
>> +
>> + /*
>> + * Run sense id command.
>> + * The size of the senseid data differs between devices (notably,
>> + * between virtio devices and dasds), so specify the largest
>> possible
>> + * size and suppress the incorrect length indication for smaller
>> sizes.
>> + */
>> + if (run_ccw(vdev, CCW_CMD_SENSE_ID, &vdev->senseid,
>> sizeof(vdev->senseid),
>> + true)) {
>> + return false;
>> + }
>> +
>> + vdev->dev_type = vdev->senseid.cu_model;
>> +
>> + if (vdev->senseid.cu_type == 0x3832) {
>> + switch (vdev->dev_type) {
>> + case VIRTIO_ID_BLOCK:
>> + case VIRTIO_ID_SCSI:
>> + case VIRTIO_ID_NET:
>> + return true;
>> + default:
>> + return false;
>> + }
>> + }
>> + return false;
>> +}
> ...
>> +int virtio_ccw_setup(VDev *vdev)
>> +{
>> + int i, cfg_size = 0;
>> + uint8_t status;
>> + struct VirtioFeatureDesc {
>> + uint32_t features;
>> + uint8_t index;
>> + } __attribute__((packed)) feats;
>> +
>> + if (!virtio_ccw_is_supported(vdev->schid)) {
>> + puts("Virtio unsupported for this device ID");
>> + return -ENODEV;
>> + }
>> + /* device ID has been established now */
>> +
>> + vdev->config.blk.blk_size = 0; /* mark "illegal" - setup
>> started... */
>> + vdev->guessed_disk_nature = VIRTIO_GDN_NONE;
>> +
>> + virtio_reset(vdev);
>> +
>> + status = VIRTIO_CONFIG_S_ACKNOWLEDGE;
>> + if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status),
>> false)) {
>> + puts("Could not write ACKNOWLEDGE status to host");
>> + return -EIO;
>> + }
>> +
>> + switch (vdev->dev_type) {
>> + case VIRTIO_ID_NET:
>> + vdev->nr_vqs = 2;
>> + vdev->cmd_vr_idx = 0;
>> + cfg_size = sizeof(vdev->config.net);
>> + break;
>> + case VIRTIO_ID_BLOCK:
>> + vdev->nr_vqs = 1;
>> + vdev->cmd_vr_idx = 0;
>> + cfg_size = sizeof(vdev->config.blk);
>> + break;
>> + case VIRTIO_ID_SCSI:
>> + vdev->nr_vqs = 3;
>> + vdev->cmd_vr_idx = VR_REQUEST;
>> + cfg_size = sizeof(vdev->config.scsi);
>> + break;
>> + default:
>> + puts("Unsupported virtio device");
>> + return -ENODEV;
>> + }
>> +
>> + status |= VIRTIO_CONFIG_S_DRIVER;
>> + if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status),
>> false)) {
>> + puts("Could not write DRIVER status to host");
>> + return -EIO;
>> + }
>> +
>> + /* Feature negotiation */
>> + for (i = 0; i < ARRAY_SIZE(vdev->guest_features); i++) {
>> + feats.features = 0;
>> + feats.index = i;
>> + if (run_ccw(vdev, CCW_CMD_READ_FEAT, &feats, sizeof(feats),
>> false)) {
>> + puts("Could not get features bits");
>> + return -EIO;
>> + }
>> +
>> + vdev->guest_features[i] &= bswap32(feats.features);
>> + feats.features = bswap32(vdev->guest_features[i]);
>> + if (run_ccw(vdev, CCW_CMD_WRITE_FEAT, &feats, sizeof(feats),
>> false)) {
>> + puts("Could not set features bits");
>> + return -EIO;
>> + }
>> + }
>> +
>> + if (run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size,
>> false)) {
>> + puts("Could not get virtio device configuration");
>> + return -EIO;
>> + }
>> +
>> + for (i = 0; i < vdev->nr_vqs; i++) {
>> + VqInfo info = {
>> + .queue = (unsigned long long) virtio_get_ring_area() +
>> (i * VIRTIO_RING_SIZE),
>
> Would it make sense to add a "ring_num" parameter to
> virtio_get_ring_area(), so that you could call virtio_get_ring_area(i)
> here instead?
>
> virtio_get_ring_area() would then look like this:
>
> char *virtio_get_ring_area(int ring_num)
> {
> return ring_area + ring_num * VIRTIO_RING_SIZE;
> }
>
> ?
That makes sense to me. I'll change it.
>
>> + .align = KVM_S390_VIRTIO_RING_ALIGN,
>> + .index = i,
>> + .num = 0,
>> + };
>> + VqConfig config = {
>> + .index = i,
>> + .num = 0,
>> + };
>> +
>> + if (run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config,
>> sizeof(config),
>> + false)) {
>> + puts("Could not get virtio device VQ config");
>> + return -EIO;
>> + }
>> + info.num = config.num;
>> + vring_init(&vdev->vrings[i], &info);
>> + vdev->vrings[i].schid = vdev->schid;
>> + if (run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info),
>> false)) {
>> + puts("Cannot set VQ info");
>> + return -EIO;
>> + }
>> + }
>> +
>> + status |= VIRTIO_CONFIG_S_DRIVER_OK;
>> + if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status),
>> false)) {
>> + puts("Could not write DRIVER_OK status to host");
>> + return -EIO;
>> + }
>> +
>> + return 0;
>> +}
> ...
>> diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c
>> index 0f4f201038..0488b3a07e 100644
>> --- a/pc-bios/s390-ccw/virtio.c
>> +++ b/pc-bios/s390-ccw/virtio.c
>> @@ -2,6 +2,7 @@
>> * Virtio driver bits
>> *
>> * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
>> + * Copyright 2025 IBM Corp. Author(s): Jared Rossi
>> <jrossi@linux.ibm.com>
>
> I assume you wanted to put Authors on a separate line?
I don't quite understand what you are asking about here. Or maybe I
don't understand how to attribute the authors in this case?
Much of this was just copy/pasted to the new file, so I included the
existing byline too. Should I format things differently?
Thanks,
Jared Rossi
^ permalink raw reply [flat|nested] 36+ messages in thread* Re: [PATCH 03/10] pc-bios/s390-ccw: Split virtio-ccw and generic virtio
2026-01-07 16:38 ` Jared Rossi
@ 2026-01-08 6:02 ` Thomas Huth
2026-01-08 9:19 ` Daniel P. Berrangé
0 siblings, 1 reply; 36+ messages in thread
From: Thomas Huth @ 2026-01-08 6:02 UTC (permalink / raw)
To: Jared Rossi, qemu-devel, qemu-s390x, mst
Cc: jjherne, alifm, farman, mjrosato, zycai
On 07/01/2026 17.38, Jared Rossi wrote:
>
>
> On 1/7/26 4:40 AM, Thomas Huth wrote:
>> On 10/12/2025 21.54, jrossi@linux.ibm.com wrote:
>>> From: Jared Rossi <jrossi@linux.ibm.com>
...
>>> diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c
>>> index 0f4f201038..0488b3a07e 100644
>>> --- a/pc-bios/s390-ccw/virtio.c
>>> +++ b/pc-bios/s390-ccw/virtio.c
>>> @@ -2,6 +2,7 @@
>>> * Virtio driver bits
>>> *
>>> * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
>>> + * Copyright 2025 IBM Corp. Author(s): Jared Rossi <jrossi@linux.ibm.com>
>>
>> I assume you wanted to put Authors on a separate line?
>
> I don't quite understand what you are asking about here. Or maybe I don't
> understand how to attribute the authors in this case?
>
> Much of this was just copy/pasted to the new file, so I included the
> existing byline too. Should I format things differently?
No, you don't have to change it, I was just surprised to see "Copyright" and
"Author(s):" in the same line. People normally put it on separate lines, see
e.g. cio.c, menu.c or virtio-scsi.c in the pc-ios/s390-ccw/ directory.
Thomas
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH 03/10] pc-bios/s390-ccw: Split virtio-ccw and generic virtio
2026-01-08 6:02 ` Thomas Huth
@ 2026-01-08 9:19 ` Daniel P. Berrangé
0 siblings, 0 replies; 36+ messages in thread
From: Daniel P. Berrangé @ 2026-01-08 9:19 UTC (permalink / raw)
To: Thomas Huth
Cc: Jared Rossi, qemu-devel, qemu-s390x, mst, jjherne, alifm, farman,
mjrosato, zycai
On Thu, Jan 08, 2026 at 07:02:47AM +0100, Thomas Huth wrote:
> On 07/01/2026 17.38, Jared Rossi wrote:
> >
> >
> > On 1/7/26 4:40 AM, Thomas Huth wrote:
> > > On 10/12/2025 21.54, jrossi@linux.ibm.com wrote:
> > > > From: Jared Rossi <jrossi@linux.ibm.com>
> ...
> > > > diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c
> > > > index 0f4f201038..0488b3a07e 100644
> > > > --- a/pc-bios/s390-ccw/virtio.c
> > > > +++ b/pc-bios/s390-ccw/virtio.c
> > > > @@ -2,6 +2,7 @@
> > > > * Virtio driver bits
> > > > *
> > > > * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
> > > > + * Copyright 2025 IBM Corp. Author(s): Jared Rossi <jrossi@linux.ibm.com>
> > >
> > > I assume you wanted to put Authors on a separate line?
> >
> > I don't quite understand what you are asking about here. Or maybe I
> > don't understand how to attribute the authors in this case?
> >
> > Much of this was just copy/pasted to the new file, so I included the
> > existing byline too. Should I format things differently?
>
> No, you don't have to change it, I was just surprised to see "Copyright" and
> "Author(s):" in the same line. People normally put it on separate lines, see
> e.g. cio.c, menu.c or virtio-scsi.c in the pc-ios/s390-ccw/ directory.
The reason for the usual separation is that they have different effects.
Any text that is part of a "Copyright" line is legally associated with,
and their presence is protected by the license terms.
Lists of authors have no legal effects, they're merely credits, and are
not protected by the license.
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH 03/10] pc-bios/s390-ccw: Split virtio-ccw and generic virtio
2025-12-10 20:54 ` [PATCH 03/10] pc-bios/s390-ccw: Split virtio-ccw and generic virtio jrossi
2026-01-07 9:40 ` Thomas Huth
@ 2026-01-07 14:27 ` Eric Farman
2026-01-07 16:16 ` Jared Rossi
1 sibling, 1 reply; 36+ messages in thread
From: Eric Farman @ 2026-01-07 14:27 UTC (permalink / raw)
To: jrossi, qemu-devel, qemu-s390x, thuth, mst
Cc: jjherne, alifm, mjrosato, zycai
On Wed, 2025-12-10 at 15:54 -0500, jrossi@linux.ibm.com wrote:
> From: Jared Rossi <jrossi@linux.ibm.com>
>
> Separate the CCW specific virtio routines and create generic wrappers for easier
> reuse of existing virtio functions with non-CCW devices.
>
> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
> ---
> pc-bios/s390-ccw/s390-ccw.h | 3 -
> pc-bios/s390-ccw/virtio-ccw.h | 24 +++
> pc-bios/s390-ccw/virtio-scsi.h | 2 +-
> pc-bios/s390-ccw/virtio.h | 7 +-
> pc-bios/s390-ccw/main.c | 10 +-
> pc-bios/s390-ccw/netmain.c | 2 +-
> pc-bios/s390-ccw/virtio-blkdev.c | 15 +-
> pc-bios/s390-ccw/virtio-ccw.c | 242 +++++++++++++++++++++++++++++++
> pc-bios/s390-ccw/virtio-net.c | 5 +-
> pc-bios/s390-ccw/virtio-scsi.c | 6 +-
> pc-bios/s390-ccw/virtio.c | 237 +++++-------------------------
> pc-bios/s390-ccw/Makefile | 3 +-
> 12 files changed, 334 insertions(+), 222 deletions(-)
> create mode 100644 pc-bios/s390-ccw/virtio-ccw.h
> create mode 100644 pc-bios/s390-ccw/virtio-ccw.c
>
> diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
> index 47ea66bd4d..ccd68ff0a4 100644
> --- a/pc-bios/s390-ccw/s390-ccw.h
> +++ b/pc-bios/s390-ccw/s390-ccw.h
> @@ -66,9 +66,6 @@ void sclp_setup(void);
> void sclp_get_loadparm_ascii(char *loadparm);
> int sclp_read(char *str, size_t count);
>
> -/* virtio.c */
> -bool virtio_is_supported(SubChannelId schid);
> -
> /* bootmap.c */
> void zipl_load(void);
>
> diff --git a/pc-bios/s390-ccw/virtio-ccw.h b/pc-bios/s390-ccw/virtio-ccw.h
> new file mode 100644
> index 0000000000..cdf6a55dc8
> --- /dev/null
> +++ b/pc-bios/s390-ccw/virtio-ccw.h
> @@ -0,0 +1,24 @@
> +/*
> + * Virtio definitions for CCW devices
> + *
> + * Copyright 2025 IBM Corp.
> + * Author(s): Jared Rossi <jrossi@linux.ibm.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#ifndef VIRTIO_CCW_H
> +#define VIRTIO_CCW_H
> +
> +/* main.c */
> +extern SubChannelId blk_schid;
> +
> +/* virtio-ccw.c */
> +int drain_irqs_ccw(SubChannelId schid);
> +bool virtio_ccw_is_supported(SubChannelId schid);
> +int virtio_ccw_run(VDev *vdev, int vqid, VirtioCmd *cmd);
> +long virtio_ccw_notify(SubChannelId schid, int vq_idx, long cookie);
> +int virtio_ccw_setup(VDev *vdev);
> +int virtio_ccw_reset(VDev *vdev);
> +
> +#endif
> diff --git a/pc-bios/s390-ccw/virtio-scsi.h b/pc-bios/s390-ccw/virtio-scsi.h
> index c5612e16a2..7a37f8b45a 100644
> --- a/pc-bios/s390-ccw/virtio-scsi.h
> +++ b/pc-bios/s390-ccw/virtio-scsi.h
> @@ -69,6 +69,6 @@ static inline bool virtio_scsi_response_ok(const VirtioScsiCmdResp *r)
>
> int virtio_scsi_read_many(VDev *vdev,
> unsigned long sector, void *load_addr, int sec_num);
> -int virtio_scsi_setup_device(SubChannelId schid);
> +int virtio_scsi_setup_device(void);
>
> #endif /* VIRTIO_SCSI_H */
> diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
> index d557a4a90e..e747891a2c 100644
> --- a/pc-bios/s390-ccw/virtio.h
> +++ b/pc-bios/s390-ccw/virtio.h
> @@ -109,6 +109,7 @@ struct VRing {
> };
> typedef struct VRing VRing;
>
> +char *virtio_get_ring_area(void);
>
> /***********************************************
> * Virtio block *
> @@ -270,8 +271,10 @@ struct VirtioCmd {
> };
> typedef struct VirtioCmd VirtioCmd;
>
> +void vring_init(VRing *vr, VqInfo *info);
> +bool virtio_is_supported(VDev *vdev);
> bool vring_notify(VRing *vr);
> -int drain_irqs(SubChannelId schid);
> +int drain_irqs(VRing *vr);
> void vring_send_buf(VRing *vr, void *p, int len, int flags);
> int vr_poll(VRing *vr);
> int vring_wait_reply(void);
> @@ -284,7 +287,7 @@ int virtio_net_init(void *mac_addr);
> void virtio_net_deinit(void);
>
> /* virtio-blkdev.c */
> -int virtio_blk_setup_device(SubChannelId schid);
> +int virtio_blk_setup_device(void);
> int virtio_read(unsigned long sector, void *load_addr);
> unsigned long virtio_load_direct(unsigned long rec_list1, unsigned long rec_list2,
> void *load_addr);
> diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
> index fef192c934..e82d60bbb7 100644
> --- a/pc-bios/s390-ccw/main.c
> +++ b/pc-bios/s390-ccw/main.c
> @@ -72,6 +72,7 @@ static int is_dev_possibly_bootable(int dev_no, int sch_no)
> Schib schib;
> int r;
>
> + VDev *vdev = virtio_get_device();
Newline would need to be after this, but I'm not sure you need the vdev definition here.
> blk_schid.sch_no = sch_no;
> r = stsch_err(blk_schid, &schib);
> if (r == 3 || r == -EIO) {
> @@ -91,7 +92,8 @@ static int is_dev_possibly_bootable(int dev_no, int sch_no)
> * Note: we always have to run virtio_is_supported() here to make
> * sure that the vdev.senseid data gets pre-initialized correctly
> */
> - is_virtio = virtio_is_supported(blk_schid);
> + vdev->schid = blk_schid;
You save this in four different places (here, [2a], [2b], and [3])... But maybe only need one? In
the ccw path ([3]?) ?
> + is_virtio = virtio_is_supported(vdev);
>
> /* No specific devno given, just return whether the device is possibly bootable */
> if (dev_no < 0) {
> @@ -254,10 +256,12 @@ static int virtio_setup(void)
> puts("Network boot device detected");
> return 0;
> case VIRTIO_ID_BLOCK:
> - ret = virtio_blk_setup_device(blk_schid);
> + vdev->schid = blk_schid;
[2a]
> + ret = virtio_blk_setup_device();
> break;
> case VIRTIO_ID_SCSI:
> - ret = virtio_scsi_setup_device(blk_schid);
> + vdev->schid = blk_schid;
[2b]
> + ret = virtio_scsi_setup_device();
> break;
> default:
> puts("\n! No IPL device available !\n");
> diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c
> index a9521dff41..651cedf6ef 100644
> --- a/pc-bios/s390-ccw/netmain.c
> +++ b/pc-bios/s390-ccw/netmain.c
> @@ -500,7 +500,7 @@ static bool find_net_dev(Schib *schib, int dev_no)
> continue;
> }
> enable_subchannel(net_schid);
> - if (!virtio_is_supported(net_schid)) {
> + if (!virtio_is_supported(virtio_get_device())) {
> continue;
> }
> if (virtio_get_device_type() != VIRTIO_ID_NET) {
> diff --git a/pc-bios/s390-ccw/virtio-blkdev.c b/pc-bios/s390-ccw/virtio-blkdev.c
> index f40a9407c2..87ab9a9513 100644
> --- a/pc-bios/s390-ccw/virtio-blkdev.c
> +++ b/pc-bios/s390-ccw/virtio-blkdev.c
> @@ -12,6 +12,7 @@
> #include "s390-ccw.h"
> #include "virtio.h"
> #include "virtio-scsi.h"
> +#include "virtio-ccw.h"
>
> #define VIRTIO_BLK_F_GEOMETRY (1 << 4)
> #define VIRTIO_BLK_F_BLK_SIZE (1 << 6)
> @@ -42,7 +43,7 @@ static int virtio_blk_read_many(VDev *vdev, unsigned long sector, void *load_add
> /* Now we can tell the host to read */
> vring_wait_reply();
>
> - if (drain_irqs(vr->schid)) {
> + if (drain_irqs(vr)) {
>
> /* Well, whatever status is supposed to contain... */
> status = 1;
> }
> @@ -229,15 +230,19 @@ uint64_t virtio_get_blocks(void)
> }
> }
>
> -int virtio_blk_setup_device(SubChannelId schid)
> +int virtio_blk_setup_device(void)
> {
> VDev *vdev = virtio_get_device();
>
> vdev->guest_features[0] = VIRTIO_BLK_F_GEOMETRY | VIRTIO_BLK_F_BLK_SIZE;
> - vdev->schid = schid;
> - virtio_setup_ccw(vdev);
>
> puts("Using virtio-blk.");
>
> - return 0;
> + switch (virtio_get_device()->ipl_type) {
vdev->ipl_type
> + case S390_IPL_TYPE_QEMU_SCSI:
> + case S390_IPL_TYPE_CCW:
> + return virtio_ccw_setup(vdev);
default:
return 1;
...instead of...
> + }
> +
> + return 1;
...this?
> }
> diff --git a/pc-bios/s390-ccw/virtio-ccw.c b/pc-bios/s390-ccw/virtio-ccw.c
> new file mode 100644
> index 0000000000..e121826625
> --- /dev/null
> +++ b/pc-bios/s390-ccw/virtio-ccw.c
> @@ -0,0 +1,242 @@
> +/*
> + * Virtio functionality for CCW devices
> + *
> + * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
> + * Copyright 2025 IBM Corp. Author(s): Jared Rossi <jrossi@linux.ibm.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or (at
> + * your option) any later version. See the COPYING file in the top-level
> + * directory.
> + */
> +
> +#include <string.h>
> +#include "s390-ccw.h"
> +#include "cio.h"
> +#include "virtio.h"
> +#include "virtio-ccw.h"
> +#include "virtio-scsi.h"
> +#include "bswap.h"
> +#include "helper.h"
> +#include "s390-time.h"
> +
> +/* virtio spec v1.0 para 4.3.3.2 */
> +static long kvm_hypercall(unsigned long nr, unsigned long param1,
> + unsigned long param2, unsigned long param3)
> +{
> + register unsigned long r_nr asm("1") = nr;
> + register unsigned long r_param1 asm("2") = param1;
> + register unsigned long r_param2 asm("3") = param2;
> + register unsigned long r_param3 asm("4") = param3;
> + register long retval asm("2");
> +
> + asm volatile ("diag %%r2,%%r4,0x500"
> + : "=d" (retval)
> + : "d" (r_nr), "0" (r_param1), "r"(r_param2), "d"(r_param3)
> + : "memory", "cc");
> +
> + return retval;
> +}
> +
> +static int run_ccw(VDev *vdev, int cmd, void *ptr, int len, bool sli)
> +{
> + Ccw1 ccw = {};
> +
> + ccw.cmd_code = cmd;
> + ccw.cda = (long)ptr;
> + ccw.count = len;
> +
> + if (sli) {
> + ccw.flags |= CCW_FLAG_SLI;
> + }
> +
> + return do_cio(vdev->schid, vdev->senseid.cu_type, ptr2u32(&ccw), CCW_FMT1);
The previous patch changed vdev->senseid.cu_type to vdev->dev_type, and here you're switching it
back. Probably because dev_type is assigned from cu_model, which... Wait does that mean the previous
patch would break IPL? (Sorry if I missed that yesterday.)
> +}
> +
> +bool virtio_ccw_is_supported(SubChannelId schid)
> +{
> + VDev *vdev = virtio_get_device();
> + vdev->schid = schid;
[3]
> + memset(&vdev->senseid, 0, sizeof(vdev->senseid));
> +
> + /*
> + * Run sense id command.
> + * The size of the senseid data differs between devices (notably,
> + * between virtio devices and dasds), so specify the largest possible
> + * size and suppress the incorrect length indication for smaller sizes.
> + */
> + if (run_ccw(vdev, CCW_CMD_SENSE_ID, &vdev->senseid, sizeof(vdev->senseid),
> + true)) {
> + return false;
> + }
> +
> + vdev->dev_type = vdev->senseid.cu_model;
> +
> + if (vdev->senseid.cu_type == 0x3832) {
> + switch (vdev->dev_type) {
> + case VIRTIO_ID_BLOCK:
> + case VIRTIO_ID_SCSI:
> + case VIRTIO_ID_NET:
> + return true;
> + default:
> + return false;
> + }
> + }
> + return false;
> +}
> +
> +int drain_irqs_ccw(SubChannelId schid)
> +{
> + Irb irb = {};
> + int r = 0;
> +
> + while (1) {
> + /* FIXME: make use of TPI, for that enable subchannel and isc */
> + if (tsch(schid, &irb)) {
> + /* Might want to differentiate error codes later on. */
> + if (irb.scsw.cstat) {
> + r = -EIO;
> + } else if (irb.scsw.dstat != 0xc) {
> + r = -EIO;
> + }
> + return r;
> + }
> + }
> +}
> +
> +long virtio_ccw_notify(SubChannelId schid, int vq_idx, long cookie)
> +{
> + return kvm_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, *(u32 *)&schid,
> + vq_idx, cookie);
> +}
> +
> +int virtio_ccw_run(VDev *vdev, int vqid, VirtioCmd *cmd)
> +{
> + VRing *vr = &vdev->vrings[vqid];
> + int i = 0;
> +
> + do {
> + vring_send_buf(vr, cmd[i].data, cmd[i].size,
> + cmd[i].flags | (i ? VRING_HIDDEN_IS_CHAIN : 0));
> + } while (cmd[i++].flags & VRING_DESC_F_NEXT);
> +
> + vring_wait_reply();
> + if (drain_irqs(vr)) {
> + return -1;
> + }
> + return 0;
> +}
> +
> +int virtio_ccw_reset(VDev *vdev)
> +{
> + return run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0, false);
> +}
> +
> +int virtio_ccw_setup(VDev *vdev)
> +{
> + int i, cfg_size = 0;
> + uint8_t status;
> + struct VirtioFeatureDesc {
> + uint32_t features;
> + uint8_t index;
> + } __attribute__((packed)) feats;
> +
> + if (!virtio_ccw_is_supported(vdev->schid)) {
> + puts("Virtio unsupported for this device ID");
> + return -ENODEV;
> + }
> + /* device ID has been established now */
> +
> + vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */
> + vdev->guessed_disk_nature = VIRTIO_GDN_NONE;
> +
> + virtio_reset(vdev);
> +
> + status = VIRTIO_CONFIG_S_ACKNOWLEDGE;
> + if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
> + puts("Could not write ACKNOWLEDGE status to host");
> + return -EIO;
> + }
> +
> + switch (vdev->dev_type) {
> + case VIRTIO_ID_NET:
> + vdev->nr_vqs = 2;
> + vdev->cmd_vr_idx = 0;
> + cfg_size = sizeof(vdev->config.net);
> + break;
> + case VIRTIO_ID_BLOCK:
> + vdev->nr_vqs = 1;
> + vdev->cmd_vr_idx = 0;
> + cfg_size = sizeof(vdev->config.blk);
> + break;
> + case VIRTIO_ID_SCSI:
> + vdev->nr_vqs = 3;
> + vdev->cmd_vr_idx = VR_REQUEST;
> + cfg_size = sizeof(vdev->config.scsi);
> + break;
> + default:
> + puts("Unsupported virtio device");
> + return -ENODEV;
> + }
> +
> + status |= VIRTIO_CONFIG_S_DRIVER;
> + if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
> + puts("Could not write DRIVER status to host");
> + return -EIO;
> + }
> +
> + /* Feature negotiation */
> + for (i = 0; i < ARRAY_SIZE(vdev->guest_features); i++) {
> + feats.features = 0;
> + feats.index = i;
> + if (run_ccw(vdev, CCW_CMD_READ_FEAT, &feats, sizeof(feats), false)) {
> + puts("Could not get features bits");
> + return -EIO;
> + }
> +
> + vdev->guest_features[i] &= bswap32(feats.features);
> + feats.features = bswap32(vdev->guest_features[i]);
> + if (run_ccw(vdev, CCW_CMD_WRITE_FEAT, &feats, sizeof(feats), false)) {
> + puts("Could not set features bits");
> + return -EIO;
> + }
> + }
> +
> + if (run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false)) {
> + puts("Could not get virtio device configuration");
> + return -EIO;
> + }
> +
> + for (i = 0; i < vdev->nr_vqs; i++) {
> + VqInfo info = {
> + .queue = (unsigned long long) virtio_get_ring_area() + (i * VIRTIO_RING_SIZE),
> + .align = KVM_S390_VIRTIO_RING_ALIGN,
> + .index = i,
> + .num = 0,
> + };
> + VqConfig config = {
> + .index = i,
> + .num = 0,
> + };
> +
> + if (run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config),
> + false)) {
> + puts("Could not get virtio device VQ config");
> + return -EIO;
> + }
> + info.num = config.num;
> + vring_init(&vdev->vrings[i], &info);
> + vdev->vrings[i].schid = vdev->schid;
> + if (run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info), false)) {
> + puts("Cannot set VQ info");
> + return -EIO;
> + }
> + }
> +
> + status |= VIRTIO_CONFIG_S_DRIVER_OK;
> + if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
> + puts("Could not write DRIVER_OK status to host");
> + return -EIO;
> + }
> +
> + return 0;
> +}
> diff --git a/pc-bios/s390-ccw/virtio-net.c b/pc-bios/s390-ccw/virtio-net.c
> index 301445bf97..604f1cf003 100644
> --- a/pc-bios/s390-ccw/virtio-net.c
> +++ b/pc-bios/s390-ccw/virtio-net.c
> @@ -19,6 +19,7 @@
> #include <ethernet.h>
> #include "s390-ccw.h"
> #include "virtio.h"
> +#include "virtio-ccw.h"
> #include "s390-time.h"
> #include "helper.h"
>
> @@ -54,7 +55,7 @@ int virtio_net_init(void *mac_addr)
> rx_last_idx = 0;
>
> vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT;
> - virtio_setup_ccw(vdev);
> + virtio_ccw_setup(vdev);
>
> if (!(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT)) {
> puts("virtio-net device does not support the MAC address feature");
> @@ -88,7 +89,7 @@ int send(int fd, const void *buf, int len, int flags)
> while (!vr_poll(txvq)) {
> yield();
> }
> - if (drain_irqs(txvq->schid)) {
> + if (drain_irqs(txvq)) {
> puts("send: drain irqs failed");
> return -1;
> }
> diff --git a/pc-bios/s390-ccw/virtio-scsi.c b/pc-bios/s390-ccw/virtio-scsi.c
> index 71db75ce7b..6ab0f755f2 100644
> --- a/pc-bios/s390-ccw/virtio-scsi.c
> +++ b/pc-bios/s390-ccw/virtio-scsi.c
> @@ -15,6 +15,7 @@
> #include "virtio.h"
> #include "scsi.h"
> #include "virtio-scsi.h"
> +#include "virtio-ccw.h"
> #include "s390-time.h"
> #include "helper.h"
>
> @@ -476,12 +477,11 @@ static int virtio_scsi_setup(VDev *vdev)
> return 0;
> }
>
> -int virtio_scsi_setup_device(SubChannelId schid)
> +int virtio_scsi_setup_device(void)
> {
> VDev *vdev = virtio_get_device();
>
> - vdev->schid = schid;
> - virtio_setup_ccw(vdev);
> + virtio_ccw_setup(vdev);
>
> if (vdev->config.scsi.sense_size != VIRTIO_SCSI_SENSE_SIZE) {
> puts("Config: sense size mismatch");
> diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c
> index 0f4f201038..0488b3a07e 100644
> --- a/pc-bios/s390-ccw/virtio.c
> +++ b/pc-bios/s390-ccw/virtio.c
> @@ -2,6 +2,7 @@
> * Virtio driver bits
> *
> * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
> + * Copyright 2025 IBM Corp. Author(s): Jared Rossi <jrossi@linux.ibm.com>
> *
> * This work is licensed under the terms of the GNU GPL, version 2 or (at
> * your option) any later version. See the COPYING file in the top-level
> @@ -13,6 +14,7 @@
> #include "cio.h"
> #include "virtio.h"
> #include "virtio-scsi.h"
> +#include "virtio-ccw.h"
> #include "bswap.h"
> #include "helper.h"
> #include "s390-time.h"
> @@ -44,69 +46,38 @@ VirtioDevType virtio_get_device_type(void)
> return vdev.dev_type;
> }
>
> -/* virtio spec v1.0 para 4.3.3.2 */
> -static long kvm_hypercall(unsigned long nr, unsigned long param1,
> - unsigned long param2, unsigned long param3)
> +char *virtio_get_ring_area(void)
> {
> - register unsigned long r_nr asm("1") = nr;
> - register unsigned long r_param1 asm("2") = param1;
> - register unsigned long r_param2 asm("3") = param2;
> - register unsigned long r_param3 asm("4") = param3;
> - register long retval asm("2");
> -
> - asm volatile ("diag %%r2,%%r4,0x500"
> - : "=d" (retval)
> - : "d" (r_nr), "0" (r_param1), "r"(r_param2), "d"(r_param3)
> - : "memory", "cc");
> -
> - return retval;
> -}
> -
> -static long virtio_notify(SubChannelId schid, int vq_idx, long cookie)
> -{
> - return kvm_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, *(u32 *)&schid,
> - vq_idx, cookie);
> + return ring_area;
> }
>
> /***********************************************
> * Virtio functions *
> ***********************************************/
>
> -int drain_irqs(SubChannelId schid)
> +int drain_irqs(VRing *vr)
> {
> - Irb irb = {};
> - int r = 0;
> -
> - while (1) {
> - /* FIXME: make use of TPI, for that enable subchannel and isc */
> - if (tsch(schid, &irb)) {
> - /* Might want to differentiate error codes later on. */
> - if (irb.scsw.cstat) {
> - r = -EIO;
> - } else if (irb.scsw.dstat != 0xc) {
> - r = -EIO;
> - }
> - return r;
> - }
> + switch (vdev.ipl_type) {
Wait, we're just accessing the global vdev directly here?
> + case S390_IPL_TYPE_QEMU_SCSI:
> + case S390_IPL_TYPE_CCW:
> + return drain_irqs_ccw(vr->schid);
In that case, what's the point of vr being passed in? Isn't vr->schid == vdev->schid?
This makes me think that vr->schid is not needed, and it can just be pulled from the vdev.
> + default:
> + return 0;
> }
> }
>
> -static int run_ccw(VDev *vdev, int cmd, void *ptr, int len, bool sli)
> +int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd)
> {
> - Ccw1 ccw = {};
> -
> - ccw.cmd_code = cmd;
> - ccw.cda = (long)ptr;
> - ccw.count = len;
> -
> - if (sli) {
> - ccw.flags |= CCW_FLAG_SLI;
> + switch (vdev->ipl_type) {
> + case S390_IPL_TYPE_QEMU_SCSI:
> + case S390_IPL_TYPE_CCW:
> + return virtio_ccw_run(vdev, vqid, cmd);
> + default:
> + return -1;
> }
> -
> - return do_cio(vdev->schid, vdev->dev_type, ptr2u32(&ccw), CCW_FMT1);
> }
>
> -static void vring_init(VRing *vr, VqInfo *info)
> +void vring_init(VRing *vr, VqInfo *info)
> {
> void *p = (void *) info->queue;
>
> @@ -134,7 +105,12 @@ static void vring_init(VRing *vr, VqInfo *info)
>
> bool vring_notify(VRing *vr)
> {
> - vr->cookie = virtio_notify(vr->schid, vr->id, vr->cookie);
> + switch (vdev.ipl_type) {
> + case S390_IPL_TYPE_QEMU_SCSI:
> + case S390_IPL_TYPE_CCW:
> + vr->cookie = virtio_ccw_notify(vr->schid, vr->id, vr->cookie);
> + }
> +
> return vr->cookie >= 0;
> }
>
> @@ -200,165 +176,24 @@ int vring_wait_reply(void)
> return 1;
> }
>
> -int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd)
> -{
> - VRing *vr = &vdev->vrings[vqid];
> - int i = 0;
> -
> - do {
> - vring_send_buf(vr, cmd[i].data, cmd[i].size,
> - cmd[i].flags | (i ? VRING_HIDDEN_IS_CHAIN : 0));
> - } while (cmd[i++].flags & VRING_DESC_F_NEXT);
> -
> - vring_wait_reply();
> - if (drain_irqs(vr->schid)) {
> - return -1;
> - }
> - return 0;
> -}
> -
> int virtio_reset(VDev *vdev)
> {
> - return run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0, false);
> -}
> -
> -int virtio_setup_ccw(VDev *vdev)
> -{
> - int i, cfg_size = 0;
> - uint8_t status;
> - struct VirtioFeatureDesc {
> - uint32_t features;
> - uint8_t index;
> - } __attribute__((packed)) feats;
> -
> - if (!virtio_is_supported(vdev->schid)) {
> - puts("Virtio unsupported for this device ID");
> - return -ENODEV;
> - }
> - /* device ID has been established now */
> -
> - vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */
> - vdev->guessed_disk_nature = VIRTIO_GDN_NONE;
> -
> - virtio_reset(vdev);
> -
> - status = VIRTIO_CONFIG_S_ACKNOWLEDGE;
> - if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
> - puts("Could not write ACKNOWLEDGE status to host");
> - return -EIO;
> - }
> -
> - switch (vdev->dev_type) {
> - case VIRTIO_ID_NET:
> - vdev->nr_vqs = 2;
> - vdev->cmd_vr_idx = 0;
> - cfg_size = sizeof(vdev->config.net);
> - break;
> - case VIRTIO_ID_BLOCK:
> - vdev->nr_vqs = 1;
> - vdev->cmd_vr_idx = 0;
> - cfg_size = sizeof(vdev->config.blk);
> - break;
> - case VIRTIO_ID_SCSI:
> - vdev->nr_vqs = 3;
> - vdev->cmd_vr_idx = VR_REQUEST;
> - cfg_size = sizeof(vdev->config.scsi);
> - break;
> + switch (vdev->ipl_type) {
> + case S390_IPL_TYPE_QEMU_SCSI:
> + case S390_IPL_TYPE_CCW:
> + return virtio_ccw_reset(vdev);
> default:
> - puts("Unsupported virtio device");
> - return -ENODEV;
> - }
> -
> - status |= VIRTIO_CONFIG_S_DRIVER;
> - if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
> - puts("Could not write DRIVER status to host");
> - return -EIO;
> - }
> -
> - /* Feature negotiation */
> - for (i = 0; i < ARRAY_SIZE(vdev->guest_features); i++) {
> - feats.features = 0;
> - feats.index = i;
> - if (run_ccw(vdev, CCW_CMD_READ_FEAT, &feats, sizeof(feats), false)) {
> - puts("Could not get features bits");
> - return -EIO;
> - }
> -
> - vdev->guest_features[i] &= bswap32(feats.features);
> - feats.features = bswap32(vdev->guest_features[i]);
> - if (run_ccw(vdev, CCW_CMD_WRITE_FEAT, &feats, sizeof(feats), false)) {
> - puts("Could not set features bits");
> - return -EIO;
> - }
> - }
> -
> - if (run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false)) {
> - puts("Could not get virtio device configuration");
> - return -EIO;
> - }
> -
> - for (i = 0; i < vdev->nr_vqs; i++) {
> - VqInfo info = {
> - .queue = (unsigned long long) ring_area + (i * VIRTIO_RING_SIZE),
> - .align = KVM_S390_VIRTIO_RING_ALIGN,
> - .index = i,
> - .num = 0,
> - };
> - VqConfig config = {
> - .index = i,
> - .num = 0,
> - };
> -
> - if (run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config),
> - false)) {
> - puts("Could not get virtio device VQ config");
> - return -EIO;
> - }
> - info.num = config.num;
> - vring_init(&vdev->vrings[i], &info);
> - vdev->vrings[i].schid = vdev->schid;
> - if (run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info), false)) {
> - puts("Cannot set VQ info");
> - return -EIO;
> - }
> - }
> -
> - status |= VIRTIO_CONFIG_S_DRIVER_OK;
> - if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
> - puts("Could not write DRIVER_OK status to host");
> - return -EIO;
> + return -1;
> }
> -
> - return 0;
> }
>
> -bool virtio_is_supported(SubChannelId schid)
> +bool virtio_is_supported(VDev *vdev)
> {
> - vdev.schid = schid;
> - memset(&vdev.senseid, 0, sizeof(vdev.senseid));
> -
> - /*
> - * Run sense id command.
> - * The size of the senseid data differs between devices (notably,
> - * between virtio devices and dasds), so specify the largest possible
> - * size and suppress the incorrect length indication for smaller sizes.
> - */
> - if (run_ccw(&vdev, CCW_CMD_SENSE_ID, &vdev.senseid, sizeof(vdev.senseid),
> - true)) {
> + switch (vdev->ipl_type) {
> + case S390_IPL_TYPE_QEMU_SCSI:
> + case S390_IPL_TYPE_CCW:
> + return virtio_ccw_is_supported(vdev->schid);
> + default:
> return false;
> }
> -
> - vdev.dev_type = vdev.senseid.cu_model;
> -
> - if (vdev.senseid.cu_type == 0x3832) {
> - switch (vdev.dev_type) {
> - case VIRTIO_ID_BLOCK:
> - case VIRTIO_ID_SCSI:
> - case VIRTIO_ID_NET:
> - return true;
> - default:
> - return false;
> - }
> - }
> - return false;
> }
> diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
> index a0f24c94a8..259cff09db 100644
> --- a/pc-bios/s390-ccw/Makefile
> +++ b/pc-bios/s390-ccw/Makefile
> @@ -34,7 +34,8 @@ QEMU_DGFLAGS = -MMD -MP -MT $@ -MF $(@D)/$(*F).d
> .PHONY : all clean build-all distclean
>
> OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o netmain.o \
> - virtio.o virtio-net.o virtio-scsi.o virtio-blkdev.o cio.o dasd-ipl.o
> + virtio.o virtio-net.o virtio-scsi.o virtio-blkdev.o cio.o dasd-ipl.o \
> + virtio-ccw.o
>
> SLOF_DIR := $(SRC_PATH)/../../roms/SLOF
>
^ permalink raw reply [flat|nested] 36+ messages in thread* Re: [PATCH 03/10] pc-bios/s390-ccw: Split virtio-ccw and generic virtio
2026-01-07 14:27 ` Eric Farman
@ 2026-01-07 16:16 ` Jared Rossi
0 siblings, 0 replies; 36+ messages in thread
From: Jared Rossi @ 2026-01-07 16:16 UTC (permalink / raw)
To: Eric Farman, qemu-devel, qemu-s390x, thuth, mst
Cc: jjherne, alifm, mjrosato, zycai
On 1/7/26 9:27 AM, Eric Farman wrote:
> On Wed, 2025-12-10 at 15:54 -0500, jrossi@linux.ibm.com wrote:
>> From: Jared Rossi <jrossi@linux.ibm.com>
>>
>> Separate the CCW specific virtio routines and create generic wrappers for easier
>> reuse of existing virtio functions with non-CCW devices.
>>
>> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
>> ---
>> pc-bios/s390-ccw/s390-ccw.h | 3 -
>> pc-bios/s390-ccw/virtio-ccw.h | 24 +++
>> pc-bios/s390-ccw/virtio-scsi.h | 2 +-
>> pc-bios/s390-ccw/virtio.h | 7 +-
>> pc-bios/s390-ccw/main.c | 10 +-
>> pc-bios/s390-ccw/netmain.c | 2 +-
>> pc-bios/s390-ccw/virtio-blkdev.c | 15 +-
>> pc-bios/s390-ccw/virtio-ccw.c | 242 +++++++++++++++++++++++++++++++
>> pc-bios/s390-ccw/virtio-net.c | 5 +-
>> pc-bios/s390-ccw/virtio-scsi.c | 6 +-
>> pc-bios/s390-ccw/virtio.c | 237 +++++-------------------------
>> pc-bios/s390-ccw/Makefile | 3 +-
>> 12 files changed, 334 insertions(+), 222 deletions(-)
>> create mode 100644 pc-bios/s390-ccw/virtio-ccw.h
>> create mode 100644 pc-bios/s390-ccw/virtio-ccw.c
>>
>> diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
>> index 47ea66bd4d..ccd68ff0a4 100644
>> --- a/pc-bios/s390-ccw/s390-ccw.h
>> +++ b/pc-bios/s390-ccw/s390-ccw.h
>> @@ -66,9 +66,6 @@ void sclp_setup(void);
>> void sclp_get_loadparm_ascii(char *loadparm);
>> int sclp_read(char *str, size_t count);
>>
>> -/* virtio.c */
>> -bool virtio_is_supported(SubChannelId schid);
>> -
>> /* bootmap.c */
>> void zipl_load(void);
>>
>> diff --git a/pc-bios/s390-ccw/virtio-ccw.h b/pc-bios/s390-ccw/virtio-ccw.h
>> new file mode 100644
>> index 0000000000..cdf6a55dc8
>> --- /dev/null
>> +++ b/pc-bios/s390-ccw/virtio-ccw.h
>> @@ -0,0 +1,24 @@
>> +/*
>> + * Virtio definitions for CCW devices
>> + *
>> + * Copyright 2025 IBM Corp.
>> + * Author(s): Jared Rossi <jrossi@linux.ibm.com>
>> + *
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + */
>> +
>> +#ifndef VIRTIO_CCW_H
>> +#define VIRTIO_CCW_H
>> +
>> +/* main.c */
>> +extern SubChannelId blk_schid;
>> +
>> +/* virtio-ccw.c */
>> +int drain_irqs_ccw(SubChannelId schid);
>> +bool virtio_ccw_is_supported(SubChannelId schid);
>> +int virtio_ccw_run(VDev *vdev, int vqid, VirtioCmd *cmd);
>> +long virtio_ccw_notify(SubChannelId schid, int vq_idx, long cookie);
>> +int virtio_ccw_setup(VDev *vdev);
>> +int virtio_ccw_reset(VDev *vdev);
>> +
>> +#endif
>> diff --git a/pc-bios/s390-ccw/virtio-scsi.h b/pc-bios/s390-ccw/virtio-scsi.h
>> index c5612e16a2..7a37f8b45a 100644
>> --- a/pc-bios/s390-ccw/virtio-scsi.h
>> +++ b/pc-bios/s390-ccw/virtio-scsi.h
>> @@ -69,6 +69,6 @@ static inline bool virtio_scsi_response_ok(const VirtioScsiCmdResp *r)
>>
>> int virtio_scsi_read_many(VDev *vdev,
>> unsigned long sector, void *load_addr, int sec_num);
>> -int virtio_scsi_setup_device(SubChannelId schid);
>> +int virtio_scsi_setup_device(void);
>>
>> #endif /* VIRTIO_SCSI_H */
>> diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
>> index d557a4a90e..e747891a2c 100644
>> --- a/pc-bios/s390-ccw/virtio.h
>> +++ b/pc-bios/s390-ccw/virtio.h
>> @@ -109,6 +109,7 @@ struct VRing {
>> };
>> typedef struct VRing VRing;
>>
>> +char *virtio_get_ring_area(void);
>>
>> /***********************************************
>> * Virtio block *
>> @@ -270,8 +271,10 @@ struct VirtioCmd {
>> };
>> typedef struct VirtioCmd VirtioCmd;
>>
>> +void vring_init(VRing *vr, VqInfo *info);
>> +bool virtio_is_supported(VDev *vdev);
>> bool vring_notify(VRing *vr);
>> -int drain_irqs(SubChannelId schid);
>> +int drain_irqs(VRing *vr);
>> void vring_send_buf(VRing *vr, void *p, int len, int flags);
>> int vr_poll(VRing *vr);
>> int vring_wait_reply(void);
>> @@ -284,7 +287,7 @@ int virtio_net_init(void *mac_addr);
>> void virtio_net_deinit(void);
>>
>> /* virtio-blkdev.c */
>> -int virtio_blk_setup_device(SubChannelId schid);
>> +int virtio_blk_setup_device(void);
>> int virtio_read(unsigned long sector, void *load_addr);
>> unsigned long virtio_load_direct(unsigned long rec_list1, unsigned long rec_list2,
>> void *load_addr);
>> diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
>> index fef192c934..e82d60bbb7 100644
>> --- a/pc-bios/s390-ccw/main.c
>> +++ b/pc-bios/s390-ccw/main.c
>> @@ -72,6 +72,7 @@ static int is_dev_possibly_bootable(int dev_no, int sch_no)
>> Schib schib;
>> int r;
>>
>> + VDev *vdev = virtio_get_device();
> Newline would need to be after this, but I'm not sure you need the vdev definition here.
At a minimum I'll fix the formatting. It might be unneeded if the schid
assignment/access is simplified as you suggest, so I'll see if I can
clean that up too.
>
>> blk_schid.sch_no = sch_no;
>> r = stsch_err(blk_schid, &schib);
>> if (r == 3 || r == -EIO) {
>> @@ -91,7 +92,8 @@ static int is_dev_possibly_bootable(int dev_no, int sch_no)
>> * Note: we always have to run virtio_is_supported() here to make
>> * sure that the vdev.senseid data gets pre-initialized correctly
>> */
>> - is_virtio = virtio_is_supported(blk_schid);
>> + vdev->schid = blk_schid;
> You save this in four different places (here, [2a], [2b], and [3])... But maybe only need one? In
> the ccw path ([3]?) ?
Yes, it shouldn't need to be saved in all these places. I'll
consolidate it.
>
>> + is_virtio = virtio_is_supported(vdev);
>>
>> /* No specific devno given, just return whether the device is possibly bootable */
>> if (dev_no < 0) {
>> @@ -254,10 +256,12 @@ static int virtio_setup(void)
>> puts("Network boot device detected");
>> return 0;
>> case VIRTIO_ID_BLOCK:
>> - ret = virtio_blk_setup_device(blk_schid);
>> + vdev->schid = blk_schid;
> [2a]
>
>> + ret = virtio_blk_setup_device();
>> break;
>> case VIRTIO_ID_SCSI:
>> - ret = virtio_scsi_setup_device(blk_schid);
>> + vdev->schid = blk_schid;
> [2b]
>
>> + ret = virtio_scsi_setup_device();
>> break;
>> default:
>> puts("\n! No IPL device available !\n");
>> diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c
>> index a9521dff41..651cedf6ef 100644
>> --- a/pc-bios/s390-ccw/netmain.c
>> +++ b/pc-bios/s390-ccw/netmain.c
>> @@ -500,7 +500,7 @@ static bool find_net_dev(Schib *schib, int dev_no)
>> continue;
>> }
>> enable_subchannel(net_schid);
>> - if (!virtio_is_supported(net_schid)) {
>> + if (!virtio_is_supported(virtio_get_device())) {
>> continue;
>> }
>> if (virtio_get_device_type() != VIRTIO_ID_NET) {
>> diff --git a/pc-bios/s390-ccw/virtio-blkdev.c b/pc-bios/s390-ccw/virtio-blkdev.c
>> index f40a9407c2..87ab9a9513 100644
>> --- a/pc-bios/s390-ccw/virtio-blkdev.c
>> +++ b/pc-bios/s390-ccw/virtio-blkdev.c
>> @@ -12,6 +12,7 @@
>> #include "s390-ccw.h"
>> #include "virtio.h"
>> #include "virtio-scsi.h"
>> +#include "virtio-ccw.h"
>>
>> #define VIRTIO_BLK_F_GEOMETRY (1 << 4)
>> #define VIRTIO_BLK_F_BLK_SIZE (1 << 6)
>> @@ -42,7 +43,7 @@ static int virtio_blk_read_many(VDev *vdev, unsigned long sector, void *load_add
>> /* Now we can tell the host to read */
>> vring_wait_reply();
>>
>> - if (drain_irqs(vr->schid)) {
>> + if (drain_irqs(vr)) {
>>
>> /* Well, whatever status is supposed to contain... */
>> status = 1;
>> }
>> @@ -229,15 +230,19 @@ uint64_t virtio_get_blocks(void)
>> }
>> }
>>
>> -int virtio_blk_setup_device(SubChannelId schid)
>> +int virtio_blk_setup_device(void)
>> {
>> VDev *vdev = virtio_get_device();
>>
>> vdev->guest_features[0] = VIRTIO_BLK_F_GEOMETRY | VIRTIO_BLK_F_BLK_SIZE;
>> - vdev->schid = schid;
>> - virtio_setup_ccw(vdev);
>>
>> puts("Using virtio-blk.");
>>
>> - return 0;
>> + switch (virtio_get_device()->ipl_type) {
> vdev->ipl_type
Good catch, will fix.
>
>> + case S390_IPL_TYPE_QEMU_SCSI:
>> + case S390_IPL_TYPE_CCW:
>> + return virtio_ccw_setup(vdev);
> default:
> return 1;
>
> ...instead of...
>
>> + }
>> +
>> + return 1;
> ...this?
Makes sense.
>
>> }
>> diff --git a/pc-bios/s390-ccw/virtio-ccw.c b/pc-bios/s390-ccw/virtio-ccw.c
>> new file mode 100644
>> index 0000000000..e121826625
>> --- /dev/null
>> +++ b/pc-bios/s390-ccw/virtio-ccw.c
>> @@ -0,0 +1,242 @@
>> +/*
>> + * Virtio functionality for CCW devices
>> + *
>> + * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
>> + * Copyright 2025 IBM Corp. Author(s): Jared Rossi <jrossi@linux.ibm.com>
>> + *
>> + * This work is licensed under the terms of the GNU GPL, version 2 or (at
>> + * your option) any later version. See the COPYING file in the top-level
>> + * directory.
>> + */
>> +
>> +#include <string.h>
>> +#include "s390-ccw.h"
>> +#include "cio.h"
>> +#include "virtio.h"
>> +#include "virtio-ccw.h"
>> +#include "virtio-scsi.h"
>> +#include "bswap.h"
>> +#include "helper.h"
>> +#include "s390-time.h"
>> +
>> +/* virtio spec v1.0 para 4.3.3.2 */
>> +static long kvm_hypercall(unsigned long nr, unsigned long param1,
>> + unsigned long param2, unsigned long param3)
>> +{
>> + register unsigned long r_nr asm("1") = nr;
>> + register unsigned long r_param1 asm("2") = param1;
>> + register unsigned long r_param2 asm("3") = param2;
>> + register unsigned long r_param3 asm("4") = param3;
>> + register long retval asm("2");
>> +
>> + asm volatile ("diag %%r2,%%r4,0x500"
>> + : "=d" (retval)
>> + : "d" (r_nr), "0" (r_param1), "r"(r_param2), "d"(r_param3)
>> + : "memory", "cc");
>> +
>> + return retval;
>> +}
>> +
>> +static int run_ccw(VDev *vdev, int cmd, void *ptr, int len, bool sli)
>> +{
>> + Ccw1 ccw = {};
>> +
>> + ccw.cmd_code = cmd;
>> + ccw.cda = (long)ptr;
>> + ccw.count = len;
>> +
>> + if (sli) {
>> + ccw.flags |= CCW_FLAG_SLI;
>> + }
>> +
>> + return do_cio(vdev->schid, vdev->senseid.cu_type, ptr2u32(&ccw), CCW_FMT1);
> The previous patch changed vdev->senseid.cu_type to vdev->dev_type, and here you're switching it
> back. Probably because dev_type is assigned from cu_model, which... Wait does that mean the previous
> patch would break IPL? (Sorry if I missed that yesterday.)
The previous patch didn't break IPL when I ran my smoke test on it, but
now I'm second guessing myself...
I believe using vdev->senseid.cu_type here is correct, so the other
patch might need to be fixed. I'll look over it.
>
>> +}
>> +
>> +bool virtio_ccw_is_supported(SubChannelId schid)
>> +{
>> + VDev *vdev = virtio_get_device();
>> + vdev->schid = schid;
> [3]
>
>> + memset(&vdev->senseid, 0, sizeof(vdev->senseid));
>> +
>> + /*
>> + * Run sense id command.
>> + * The size of the senseid data differs between devices (notably,
>> + * between virtio devices and dasds), so specify the largest possible
>> + * size and suppress the incorrect length indication for smaller sizes.
>> + */
>> + if (run_ccw(vdev, CCW_CMD_SENSE_ID, &vdev->senseid, sizeof(vdev->senseid),
>> + true)) {
>> + return false;
>> + }
>> +
>> + vdev->dev_type = vdev->senseid.cu_model;
>> +
>> + if (vdev->senseid.cu_type == 0x3832) {
>> + switch (vdev->dev_type) {
>> + case VIRTIO_ID_BLOCK:
>> + case VIRTIO_ID_SCSI:
>> + case VIRTIO_ID_NET:
>> + return true;
>> + default:
>> + return false;
>> + }
>> + }
>> + return false;
>> +}
>> +
>> +int drain_irqs_ccw(SubChannelId schid)
>> +{
>> + Irb irb = {};
>> + int r = 0;
>> +
>> + while (1) {
>> + /* FIXME: make use of TPI, for that enable subchannel and isc */
>> + if (tsch(schid, &irb)) {
>> + /* Might want to differentiate error codes later on. */
>> + if (irb.scsw.cstat) {
>> + r = -EIO;
>> + } else if (irb.scsw.dstat != 0xc) {
>> + r = -EIO;
>> + }
>> + return r;
>> + }
>> + }
>> +}
>> +
>> +long virtio_ccw_notify(SubChannelId schid, int vq_idx, long cookie)
>> +{
>> + return kvm_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, *(u32 *)&schid,
>> + vq_idx, cookie);
>> +}
>> +
>> +int virtio_ccw_run(VDev *vdev, int vqid, VirtioCmd *cmd)
>> +{
>> + VRing *vr = &vdev->vrings[vqid];
>> + int i = 0;
>> +
>> + do {
>> + vring_send_buf(vr, cmd[i].data, cmd[i].size,
>> + cmd[i].flags | (i ? VRING_HIDDEN_IS_CHAIN : 0));
>> + } while (cmd[i++].flags & VRING_DESC_F_NEXT);
>> +
>> + vring_wait_reply();
>> + if (drain_irqs(vr)) {
>> + return -1;
>> + }
>> + return 0;
>> +}
>> +
>> +int virtio_ccw_reset(VDev *vdev)
>> +{
>> + return run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0, false);
>> +}
>> +
>> +int virtio_ccw_setup(VDev *vdev)
>> +{
>> + int i, cfg_size = 0;
>> + uint8_t status;
>> + struct VirtioFeatureDesc {
>> + uint32_t features;
>> + uint8_t index;
>> + } __attribute__((packed)) feats;
>> +
>> + if (!virtio_ccw_is_supported(vdev->schid)) {
>> + puts("Virtio unsupported for this device ID");
>> + return -ENODEV;
>> + }
>> + /* device ID has been established now */
>> +
>> + vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */
>> + vdev->guessed_disk_nature = VIRTIO_GDN_NONE;
>> +
>> + virtio_reset(vdev);
>> +
>> + status = VIRTIO_CONFIG_S_ACKNOWLEDGE;
>> + if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
>> + puts("Could not write ACKNOWLEDGE status to host");
>> + return -EIO;
>> + }
>> +
>> + switch (vdev->dev_type) {
>> + case VIRTIO_ID_NET:
>> + vdev->nr_vqs = 2;
>> + vdev->cmd_vr_idx = 0;
>> + cfg_size = sizeof(vdev->config.net);
>> + break;
>> + case VIRTIO_ID_BLOCK:
>> + vdev->nr_vqs = 1;
>> + vdev->cmd_vr_idx = 0;
>> + cfg_size = sizeof(vdev->config.blk);
>> + break;
>> + case VIRTIO_ID_SCSI:
>> + vdev->nr_vqs = 3;
>> + vdev->cmd_vr_idx = VR_REQUEST;
>> + cfg_size = sizeof(vdev->config.scsi);
>> + break;
>> + default:
>> + puts("Unsupported virtio device");
>> + return -ENODEV;
>> + }
>> +
>> + status |= VIRTIO_CONFIG_S_DRIVER;
>> + if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
>> + puts("Could not write DRIVER status to host");
>> + return -EIO;
>> + }
>> +
>> + /* Feature negotiation */
>> + for (i = 0; i < ARRAY_SIZE(vdev->guest_features); i++) {
>> + feats.features = 0;
>> + feats.index = i;
>> + if (run_ccw(vdev, CCW_CMD_READ_FEAT, &feats, sizeof(feats), false)) {
>> + puts("Could not get features bits");
>> + return -EIO;
>> + }
>> +
>> + vdev->guest_features[i] &= bswap32(feats.features);
>> + feats.features = bswap32(vdev->guest_features[i]);
>> + if (run_ccw(vdev, CCW_CMD_WRITE_FEAT, &feats, sizeof(feats), false)) {
>> + puts("Could not set features bits");
>> + return -EIO;
>> + }
>> + }
>> +
>> + if (run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false)) {
>> + puts("Could not get virtio device configuration");
>> + return -EIO;
>> + }
>> +
>> + for (i = 0; i < vdev->nr_vqs; i++) {
>> + VqInfo info = {
>> + .queue = (unsigned long long) virtio_get_ring_area() + (i * VIRTIO_RING_SIZE),
>> + .align = KVM_S390_VIRTIO_RING_ALIGN,
>> + .index = i,
>> + .num = 0,
>> + };
>> + VqConfig config = {
>> + .index = i,
>> + .num = 0,
>> + };
>> +
>> + if (run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config),
>> + false)) {
>> + puts("Could not get virtio device VQ config");
>> + return -EIO;
>> + }
>> + info.num = config.num;
>> + vring_init(&vdev->vrings[i], &info);
>> + vdev->vrings[i].schid = vdev->schid;
>> + if (run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info), false)) {
>> + puts("Cannot set VQ info");
>> + return -EIO;
>> + }
>> + }
>> +
>> + status |= VIRTIO_CONFIG_S_DRIVER_OK;
>> + if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
>> + puts("Could not write DRIVER_OK status to host");
>> + return -EIO;
>> + }
>> +
>> + return 0;
>> +}
>> diff --git a/pc-bios/s390-ccw/virtio-net.c b/pc-bios/s390-ccw/virtio-net.c
>> index 301445bf97..604f1cf003 100644
>> --- a/pc-bios/s390-ccw/virtio-net.c
>> +++ b/pc-bios/s390-ccw/virtio-net.c
>> @@ -19,6 +19,7 @@
>> #include <ethernet.h>
>> #include "s390-ccw.h"
>> #include "virtio.h"
>> +#include "virtio-ccw.h"
>> #include "s390-time.h"
>> #include "helper.h"
>>
>> @@ -54,7 +55,7 @@ int virtio_net_init(void *mac_addr)
>> rx_last_idx = 0;
>>
>> vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT;
>> - virtio_setup_ccw(vdev);
>> + virtio_ccw_setup(vdev);
>>
>> if (!(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT)) {
>> puts("virtio-net device does not support the MAC address feature");
>> @@ -88,7 +89,7 @@ int send(int fd, const void *buf, int len, int flags)
>> while (!vr_poll(txvq)) {
>> yield();
>> }
>> - if (drain_irqs(txvq->schid)) {
>> + if (drain_irqs(txvq)) {
>> puts("send: drain irqs failed");
>> return -1;
>> }
>> diff --git a/pc-bios/s390-ccw/virtio-scsi.c b/pc-bios/s390-ccw/virtio-scsi.c
>> index 71db75ce7b..6ab0f755f2 100644
>> --- a/pc-bios/s390-ccw/virtio-scsi.c
>> +++ b/pc-bios/s390-ccw/virtio-scsi.c
>> @@ -15,6 +15,7 @@
>> #include "virtio.h"
>> #include "scsi.h"
>> #include "virtio-scsi.h"
>> +#include "virtio-ccw.h"
>> #include "s390-time.h"
>> #include "helper.h"
>>
>> @@ -476,12 +477,11 @@ static int virtio_scsi_setup(VDev *vdev)
>> return 0;
>> }
>>
>> -int virtio_scsi_setup_device(SubChannelId schid)
>> +int virtio_scsi_setup_device(void)
>> {
>> VDev *vdev = virtio_get_device();
>>
>> - vdev->schid = schid;
>> - virtio_setup_ccw(vdev);
>> + virtio_ccw_setup(vdev);
>>
>> if (vdev->config.scsi.sense_size != VIRTIO_SCSI_SENSE_SIZE) {
>> puts("Config: sense size mismatch");
>> diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c
>> index 0f4f201038..0488b3a07e 100644
>> --- a/pc-bios/s390-ccw/virtio.c
>> +++ b/pc-bios/s390-ccw/virtio.c
>> @@ -2,6 +2,7 @@
>> * Virtio driver bits
>> *
>> * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
>> + * Copyright 2025 IBM Corp. Author(s): Jared Rossi <jrossi@linux.ibm.com>
>> *
>> * This work is licensed under the terms of the GNU GPL, version 2 or (at
>> * your option) any later version. See the COPYING file in the top-level
>> @@ -13,6 +14,7 @@
>> #include "cio.h"
>> #include "virtio.h"
>> #include "virtio-scsi.h"
>> +#include "virtio-ccw.h"
>> #include "bswap.h"
>> #include "helper.h"
>> #include "s390-time.h"
>> @@ -44,69 +46,38 @@ VirtioDevType virtio_get_device_type(void)
>> return vdev.dev_type;
>> }
>>
>> -/* virtio spec v1.0 para 4.3.3.2 */
>> -static long kvm_hypercall(unsigned long nr, unsigned long param1,
>> - unsigned long param2, unsigned long param3)
>> +char *virtio_get_ring_area(void)
>> {
>> - register unsigned long r_nr asm("1") = nr;
>> - register unsigned long r_param1 asm("2") = param1;
>> - register unsigned long r_param2 asm("3") = param2;
>> - register unsigned long r_param3 asm("4") = param3;
>> - register long retval asm("2");
>> -
>> - asm volatile ("diag %%r2,%%r4,0x500"
>> - : "=d" (retval)
>> - : "d" (r_nr), "0" (r_param1), "r"(r_param2), "d"(r_param3)
>> - : "memory", "cc");
>> -
>> - return retval;
>> -}
>> -
>> -static long virtio_notify(SubChannelId schid, int vq_idx, long cookie)
>> -{
>> - return kvm_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, *(u32 *)&schid,
>> - vq_idx, cookie);
>> + return ring_area;
>> }
>>
>> /***********************************************
>> * Virtio functions *
>> ***********************************************/
>>
>> -int drain_irqs(SubChannelId schid)
>> +int drain_irqs(VRing *vr)
>> {
>> - Irb irb = {};
>> - int r = 0;
>> -
>> - while (1) {
>> - /* FIXME: make use of TPI, for that enable subchannel and isc */
>> - if (tsch(schid, &irb)) {
>> - /* Might want to differentiate error codes later on. */
>> - if (irb.scsw.cstat) {
>> - r = -EIO;
>> - } else if (irb.scsw.dstat != 0xc) {
>> - r = -EIO;
>> - }
>> - return r;
>> - }
>> + switch (vdev.ipl_type) {
> Wait, we're just accessing the global vdev directly here?
>
>> + case S390_IPL_TYPE_QEMU_SCSI:
>> + case S390_IPL_TYPE_CCW:
>> + return drain_irqs_ccw(vr->schid);
> In that case, what's the point of vr being passed in? Isn't vr->schid == vdev->schid?
>
> This makes me think that vr->schid is not needed, and it can just be pulled from the vdev.
Hmm... I suspect you are correct the more I think about it.
>
>> + default:
>> + return 0;
>> }
>> }
>>
>> -static int run_ccw(VDev *vdev, int cmd, void *ptr, int len, bool sli)
>> +int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd)
>> {
>> - Ccw1 ccw = {};
>> -
>> - ccw.cmd_code = cmd;
>> - ccw.cda = (long)ptr;
>> - ccw.count = len;
>> -
>> - if (sli) {
>> - ccw.flags |= CCW_FLAG_SLI;
>> + switch (vdev->ipl_type) {
>> + case S390_IPL_TYPE_QEMU_SCSI:
>> + case S390_IPL_TYPE_CCW:
>> + return virtio_ccw_run(vdev, vqid, cmd);
>> + default:
>> + return -1;
>> }
>> -
>> - return do_cio(vdev->schid, vdev->dev_type, ptr2u32(&ccw), CCW_FMT1);
>> }
>>
>> -static void vring_init(VRing *vr, VqInfo *info)
>> +void vring_init(VRing *vr, VqInfo *info)
>> {
>> void *p = (void *) info->queue;
>>
>> @@ -134,7 +105,12 @@ static void vring_init(VRing *vr, VqInfo *info)
>>
>> bool vring_notify(VRing *vr)
>> {
>> - vr->cookie = virtio_notify(vr->schid, vr->id, vr->cookie);
>> + switch (vdev.ipl_type) {
>> + case S390_IPL_TYPE_QEMU_SCSI:
>> + case S390_IPL_TYPE_CCW:
>> + vr->cookie = virtio_ccw_notify(vr->schid, vr->id, vr->cookie);
>> + }
>> +
>> return vr->cookie >= 0;
>> }
>>
>> @@ -200,165 +176,24 @@ int vring_wait_reply(void)
>> return 1;
>> }
>>
>> -int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd)
>> -{
>> - VRing *vr = &vdev->vrings[vqid];
>> - int i = 0;
>> -
>> - do {
>> - vring_send_buf(vr, cmd[i].data, cmd[i].size,
>> - cmd[i].flags | (i ? VRING_HIDDEN_IS_CHAIN : 0));
>> - } while (cmd[i++].flags & VRING_DESC_F_NEXT);
>> -
>> - vring_wait_reply();
>> - if (drain_irqs(vr->schid)) {
>> - return -1;
>> - }
>> - return 0;
>> -}
>> -
>> int virtio_reset(VDev *vdev)
>> {
>> - return run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0, false);
>> -}
>> -
>> -int virtio_setup_ccw(VDev *vdev)
>> -{
>> - int i, cfg_size = 0;
>> - uint8_t status;
>> - struct VirtioFeatureDesc {
>> - uint32_t features;
>> - uint8_t index;
>> - } __attribute__((packed)) feats;
>> -
>> - if (!virtio_is_supported(vdev->schid)) {
>> - puts("Virtio unsupported for this device ID");
>> - return -ENODEV;
>> - }
>> - /* device ID has been established now */
>> -
>> - vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */
>> - vdev->guessed_disk_nature = VIRTIO_GDN_NONE;
>> -
>> - virtio_reset(vdev);
>> -
>> - status = VIRTIO_CONFIG_S_ACKNOWLEDGE;
>> - if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
>> - puts("Could not write ACKNOWLEDGE status to host");
>> - return -EIO;
>> - }
>> -
>> - switch (vdev->dev_type) {
>> - case VIRTIO_ID_NET:
>> - vdev->nr_vqs = 2;
>> - vdev->cmd_vr_idx = 0;
>> - cfg_size = sizeof(vdev->config.net);
>> - break;
>> - case VIRTIO_ID_BLOCK:
>> - vdev->nr_vqs = 1;
>> - vdev->cmd_vr_idx = 0;
>> - cfg_size = sizeof(vdev->config.blk);
>> - break;
>> - case VIRTIO_ID_SCSI:
>> - vdev->nr_vqs = 3;
>> - vdev->cmd_vr_idx = VR_REQUEST;
>> - cfg_size = sizeof(vdev->config.scsi);
>> - break;
>> + switch (vdev->ipl_type) {
>> + case S390_IPL_TYPE_QEMU_SCSI:
>> + case S390_IPL_TYPE_CCW:
>> + return virtio_ccw_reset(vdev);
>> default:
>> - puts("Unsupported virtio device");
>> - return -ENODEV;
>> - }
>> -
>> - status |= VIRTIO_CONFIG_S_DRIVER;
>> - if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
>> - puts("Could not write DRIVER status to host");
>> - return -EIO;
>> - }
>> -
>> - /* Feature negotiation */
>> - for (i = 0; i < ARRAY_SIZE(vdev->guest_features); i++) {
>> - feats.features = 0;
>> - feats.index = i;
>> - if (run_ccw(vdev, CCW_CMD_READ_FEAT, &feats, sizeof(feats), false)) {
>> - puts("Could not get features bits");
>> - return -EIO;
>> - }
>> -
>> - vdev->guest_features[i] &= bswap32(feats.features);
>> - feats.features = bswap32(vdev->guest_features[i]);
>> - if (run_ccw(vdev, CCW_CMD_WRITE_FEAT, &feats, sizeof(feats), false)) {
>> - puts("Could not set features bits");
>> - return -EIO;
>> - }
>> - }
>> -
>> - if (run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false)) {
>> - puts("Could not get virtio device configuration");
>> - return -EIO;
>> - }
>> -
>> - for (i = 0; i < vdev->nr_vqs; i++) {
>> - VqInfo info = {
>> - .queue = (unsigned long long) ring_area + (i * VIRTIO_RING_SIZE),
>> - .align = KVM_S390_VIRTIO_RING_ALIGN,
>> - .index = i,
>> - .num = 0,
>> - };
>> - VqConfig config = {
>> - .index = i,
>> - .num = 0,
>> - };
>> -
>> - if (run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config),
>> - false)) {
>> - puts("Could not get virtio device VQ config");
>> - return -EIO;
>> - }
>> - info.num = config.num;
>> - vring_init(&vdev->vrings[i], &info);
>> - vdev->vrings[i].schid = vdev->schid;
>> - if (run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info), false)) {
>> - puts("Cannot set VQ info");
>> - return -EIO;
>> - }
>> - }
>> -
>> - status |= VIRTIO_CONFIG_S_DRIVER_OK;
>> - if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
>> - puts("Could not write DRIVER_OK status to host");
>> - return -EIO;
>> + return -1;
>> }
>> -
>> - return 0;
>> }
>>
>> -bool virtio_is_supported(SubChannelId schid)
>> +bool virtio_is_supported(VDev *vdev)
>> {
>> - vdev.schid = schid;
>> - memset(&vdev.senseid, 0, sizeof(vdev.senseid));
>> -
>> - /*
>> - * Run sense id command.
>> - * The size of the senseid data differs between devices (notably,
>> - * between virtio devices and dasds), so specify the largest possible
>> - * size and suppress the incorrect length indication for smaller sizes.
>> - */
>> - if (run_ccw(&vdev, CCW_CMD_SENSE_ID, &vdev.senseid, sizeof(vdev.senseid),
>> - true)) {
>> + switch (vdev->ipl_type) {
>> + case S390_IPL_TYPE_QEMU_SCSI:
>> + case S390_IPL_TYPE_CCW:
>> + return virtio_ccw_is_supported(vdev->schid);
>> + default:
>> return false;
>> }
>> -
>> - vdev.dev_type = vdev.senseid.cu_model;
>> -
>> - if (vdev.senseid.cu_type == 0x3832) {
>> - switch (vdev.dev_type) {
>> - case VIRTIO_ID_BLOCK:
>> - case VIRTIO_ID_SCSI:
>> - case VIRTIO_ID_NET:
>> - return true;
>> - default:
>> - return false;
>> - }
>> - }
>> - return false;
>> }
>> diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
>> index a0f24c94a8..259cff09db 100644
>> --- a/pc-bios/s390-ccw/Makefile
>> +++ b/pc-bios/s390-ccw/Makefile
>> @@ -34,7 +34,8 @@ QEMU_DGFLAGS = -MMD -MP -MT $@ -MF $(@D)/$(*F).d
>> .PHONY : all clean build-all distclean
>>
>> OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o netmain.o \
>> - virtio.o virtio-net.o virtio-scsi.o virtio-blkdev.o cio.o dasd-ipl.o
>> + virtio.o virtio-net.o virtio-scsi.o virtio-blkdev.o cio.o dasd-ipl.o \
>> + virtio-ccw.o
>>
>> SLOF_DIR := $(SRC_PATH)/../../roms/SLOF
>>
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH 04/10] include/hw/s390x: Move CLP definitions for easier BIOS access
2025-12-10 20:54 [PATCH V2 0/10] s390x: Add support for virtio-blk-pci IPL device jrossi
` (2 preceding siblings ...)
2025-12-10 20:54 ` [PATCH 03/10] pc-bios/s390-ccw: Split virtio-ccw and generic virtio jrossi
@ 2025-12-10 20:54 ` jrossi
2026-01-07 9:50 ` Thomas Huth
2025-12-10 20:54 ` [PATCH 05/10] pc-bios/s390-ccw: Introduce CLP Architecture jrossi
` (5 subsequent siblings)
9 siblings, 1 reply; 36+ messages in thread
From: jrossi @ 2025-12-10 20:54 UTC (permalink / raw)
To: qemu-devel, qemu-s390x, thuth, mst
Cc: jjherne, alifm, farman, mjrosato, jrossi, zycai
From: Jared Rossi <jrossi@linux.ibm.com>
Move the s390-pci-clp definitions into the "ipl" sub-directory, which is visible
to the s390-bios. This allows the bios to reuse the architected definitions and
prevents code duplication.
Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
---
include/hw/s390x/{ => ipl}/s390-pci-clp.h | 0
include/hw/s390x/s390-pci-bus.h | 2 +-
hw/s390x/s390-pci-vfio.c | 2 +-
3 files changed, 2 insertions(+), 2 deletions(-)
rename include/hw/s390x/{ => ipl}/s390-pci-clp.h (100%)
diff --git a/include/hw/s390x/s390-pci-clp.h b/include/hw/s390x/ipl/s390-pci-clp.h
similarity index 100%
rename from include/hw/s390x/s390-pci-clp.h
rename to include/hw/s390x/ipl/s390-pci-clp.h
diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h
index 04944d4fed..f643e13057 100644
--- a/include/hw/s390x/s390-pci-bus.h
+++ b/include/hw/s390x/s390-pci-bus.h
@@ -19,7 +19,7 @@
#include "hw/s390x/sclp.h"
#include "hw/s390x/s390_flic.h"
#include "hw/s390x/css.h"
-#include "hw/s390x/s390-pci-clp.h"
+#include "hw/s390x/ipl/s390-pci-clp.h"
#include "qom/object.h"
#define TYPE_S390_PCI_HOST_BRIDGE "s390-pcihost"
diff --git a/hw/s390x/s390-pci-vfio.c b/hw/s390x/s390-pci-vfio.c
index 9e31029d7a..8ce44dbecc 100644
--- a/hw/s390x/s390-pci-vfio.c
+++ b/hw/s390x/s390-pci-vfio.c
@@ -17,7 +17,7 @@
#include "trace.h"
#include "hw/s390x/s390-pci-bus.h"
-#include "hw/s390x/s390-pci-clp.h"
+#include "hw/s390x/ipl/s390-pci-clp.h"
#include "hw/s390x/s390-pci-vfio.h"
#include "hw/vfio/pci.h"
#include "hw/vfio/vfio-container-legacy.h"
--
2.49.0
^ permalink raw reply related [flat|nested] 36+ messages in thread* Re: [PATCH 04/10] include/hw/s390x: Move CLP definitions for easier BIOS access
2025-12-10 20:54 ` [PATCH 04/10] include/hw/s390x: Move CLP definitions for easier BIOS access jrossi
@ 2026-01-07 9:50 ` Thomas Huth
0 siblings, 0 replies; 36+ messages in thread
From: Thomas Huth @ 2026-01-07 9:50 UTC (permalink / raw)
To: jrossi, qemu-devel, qemu-s390x, mst
Cc: jjherne, alifm, farman, mjrosato, zycai
On 10/12/2025 21.54, jrossi@linux.ibm.com wrote:
> From: Jared Rossi <jrossi@linux.ibm.com>
>
> Move the s390-pci-clp definitions into the "ipl" sub-directory, which is visible
> to the s390-bios. This allows the bios to reuse the architected definitions and
> prevents code duplication.
>
> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
> ---
> include/hw/s390x/{ => ipl}/s390-pci-clp.h | 0
> include/hw/s390x/s390-pci-bus.h | 2 +-
> hw/s390x/s390-pci-vfio.c | 2 +-
> 3 files changed, 2 insertions(+), 2 deletions(-)
> rename include/hw/s390x/{ => ipl}/s390-pci-clp.h (100%)
Reviewed-by: Thomas Huth <thuth@redhat.com>
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH 05/10] pc-bios/s390-ccw: Introduce CLP Architecture
2025-12-10 20:54 [PATCH V2 0/10] s390x: Add support for virtio-blk-pci IPL device jrossi
` (3 preceding siblings ...)
2025-12-10 20:54 ` [PATCH 04/10] include/hw/s390x: Move CLP definitions for easier BIOS access jrossi
@ 2025-12-10 20:54 ` jrossi
2026-01-07 10:25 ` Thomas Huth
2025-12-10 20:54 ` [PATCH 06/10] pc-bios/s390-ccw: Introduce PCI device jrossi
` (4 subsequent siblings)
9 siblings, 1 reply; 36+ messages in thread
From: jrossi @ 2025-12-10 20:54 UTC (permalink / raw)
To: qemu-devel, qemu-s390x, thuth, mst
Cc: jjherne, alifm, farman, mjrosato, jrossi, zycai
From: Jared Rossi <jrossi@linux.ibm.com>
Call Logical Processor (CLP) Architecture is used for managing PCI functions on
s390x. Define and include the structures and routines needed to interact with
PCI devices during IPL.
Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
---
pc-bios/s390-ccw/clp.h | 24 ++++++++++
pc-bios/s390-ccw/clp.c | 96 +++++++++++++++++++++++++++++++++++++++
pc-bios/s390-ccw/Makefile | 2 +-
3 files changed, 121 insertions(+), 1 deletion(-)
create mode 100644 pc-bios/s390-ccw/clp.h
create mode 100644 pc-bios/s390-ccw/clp.c
diff --git a/pc-bios/s390-ccw/clp.h b/pc-bios/s390-ccw/clp.h
new file mode 100644
index 0000000000..1ac2f8c177
--- /dev/null
+++ b/pc-bios/s390-ccw/clp.h
@@ -0,0 +1,24 @@
+/*
+ * Call Logical Processor (CLP) architecture definitions
+ *
+ * Copyright 2025 IBM Corp.
+ * Author(s): Jared Rossi <jrossi@linux.ibm.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef CLP_H
+#define CLP_H
+
+#ifndef QEMU_PACKED
+#define QEMU_PACKED __attribute__((packed))
+#endif
+
+#include <stdint.h>
+#include <s390-pci-clp.h>
+
+int clp_pci(void *data);
+int find_pci_function(uint32_t fid, ClpFhListEntry *entry);
+int enable_pci_function(uint32_t *fhandle);
+
+#endif
diff --git a/pc-bios/s390-ccw/clp.c b/pc-bios/s390-ccw/clp.c
new file mode 100644
index 0000000000..6dc72c685c
--- /dev/null
+++ b/pc-bios/s390-ccw/clp.c
@@ -0,0 +1,96 @@
+/*
+ * Call Logical Processor (CLP) architecture
+ *
+ * Copyright 2025 IBM Corp.
+ * Author(s): Jared Rossi <jrossi@linux.ibm.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "clp.h"
+#include <stdio.h>
+#include <string.h>
+
+int clp_pci(void *data)
+{
+ struct { uint8_t _[2048]; } *req = data;
+ int cc = 3;
+
+ asm volatile (
+ " .insn rrf,0xb9a00000,0,%[req],0,2\n"
+ " ipm %[cc]\n"
+ " srl %[cc],28\n"
+ : [cc] "+d" (cc), "+m" (*req)
+ : [req] "a" (req)
+ : "cc");
+ return cc;
+}
+
+/*
+ * Get the PCI function entry for a given function ID
+ * Return 0 on success, 1 if the FID is not found, or a negative RC on error
+ */
+int find_pci_function(uint32_t fid, ClpFhListEntry *entry)
+{
+ int count = 0;
+ int limit = PCI_MAX_FUNCTIONS;
+ ClpReqRspListPci rrb;
+
+ rrb.request.hdr.len = 32;
+ rrb.request.hdr.cmd = 0x02;
+ rrb.request.resume_token = 0;
+ rrb.response.hdr.len = sizeof(ClpRspListPci);
+
+ do {
+ if (clp_pci(&rrb) || (rrb.response.hdr.rsp != 0x0010)) {
+ puts("Failed to list PCI functions");
+ return -1;
+ }
+
+ /* Resume token set when max enteries are returned */
+ if (rrb.response.resume_token) {
+ count = CLP_FH_LIST_NR_ENTRIES;
+ rrb.request.resume_token = rrb.response.resume_token;
+ } else {
+ count = (rrb.response.hdr.len - 32) / sizeof(ClpFhListEntry);
+ }
+
+ limit -= count;
+
+ for (int i = 0; i < count; i++) {
+ if (rrb.response.fh_list[i].fid == fid) {
+ memcpy(entry, &rrb.response.fh_list[i], sizeof(ClpFhListEntry));
+ return 0;
+ }
+ }
+
+ } while (rrb.request.resume_token && limit);
+
+ puts("No function entry found for FID!");
+
+ return 1;
+}
+
+/*
+ * Enable the PCI function associated with a given handle
+ * Return 0 on success or a negative RC on error
+ */
+int enable_pci_function(uint32_t *fhandle)
+{
+ ClpReqRspSetPci rrb;
+
+ rrb.request.hdr.len = 32;
+ rrb.request.hdr.cmd = 0x05;
+ rrb.request.fh = *fhandle;
+ rrb.request.oc = 0;
+ rrb.request.ndas = 1;
+ rrb.response.hdr.len = 32;
+
+ if (clp_pci(&rrb) || (rrb.response.hdr.rsp != 0x0010)) {
+ puts("Failed to enable PCI function");
+ return -1;
+ }
+
+ *fhandle = rrb.response.fh;
+ return 0;
+}
diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
index 259cff09db..9c29548f84 100644
--- a/pc-bios/s390-ccw/Makefile
+++ b/pc-bios/s390-ccw/Makefile
@@ -35,7 +35,7 @@ QEMU_DGFLAGS = -MMD -MP -MT $@ -MF $(@D)/$(*F).d
OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o netmain.o \
virtio.o virtio-net.o virtio-scsi.o virtio-blkdev.o cio.o dasd-ipl.o \
- virtio-ccw.o
+ virtio-ccw.o clp.o
SLOF_DIR := $(SRC_PATH)/../../roms/SLOF
--
2.49.0
^ permalink raw reply related [flat|nested] 36+ messages in thread* Re: [PATCH 05/10] pc-bios/s390-ccw: Introduce CLP Architecture
2025-12-10 20:54 ` [PATCH 05/10] pc-bios/s390-ccw: Introduce CLP Architecture jrossi
@ 2026-01-07 10:25 ` Thomas Huth
2026-01-07 16:40 ` Jared Rossi
0 siblings, 1 reply; 36+ messages in thread
From: Thomas Huth @ 2026-01-07 10:25 UTC (permalink / raw)
To: jrossi, qemu-devel, qemu-s390x, mst
Cc: jjherne, alifm, farman, mjrosato, zycai
On 10/12/2025 21.54, jrossi@linux.ibm.com wrote:
> From: Jared Rossi <jrossi@linux.ibm.com>
>
> Call Logical Processor (CLP) Architecture is used for managing PCI functions on
> s390x. Define and include the structures and routines needed to interact with
> PCI devices during IPL.
>
> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
> ---
> pc-bios/s390-ccw/clp.h | 24 ++++++++++
> pc-bios/s390-ccw/clp.c | 96 +++++++++++++++++++++++++++++++++++++++
> pc-bios/s390-ccw/Makefile | 2 +-
> 3 files changed, 121 insertions(+), 1 deletion(-)
> create mode 100644 pc-bios/s390-ccw/clp.h
> create mode 100644 pc-bios/s390-ccw/clp.c
>
> diff --git a/pc-bios/s390-ccw/clp.h b/pc-bios/s390-ccw/clp.h
> new file mode 100644
> index 0000000000..1ac2f8c177
> --- /dev/null
> +++ b/pc-bios/s390-ccw/clp.h
> @@ -0,0 +1,24 @@
> +/*
> + * Call Logical Processor (CLP) architecture definitions
> + *
> + * Copyright 2025 IBM Corp.
> + * Author(s): Jared Rossi <jrossi@linux.ibm.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#ifndef CLP_H
> +#define CLP_H
> +
> +#ifndef QEMU_PACKED
> +#define QEMU_PACKED __attribute__((packed))
> +#endif
> +
> +#include <stdint.h>
> +#include <s390-pci-clp.h>
> +
> +int clp_pci(void *data);
> +int find_pci_function(uint32_t fid, ClpFhListEntry *entry);
> +int enable_pci_function(uint32_t *fhandle);
> +
> +#endif
> diff --git a/pc-bios/s390-ccw/clp.c b/pc-bios/s390-ccw/clp.c
> new file mode 100644
> index 0000000000..6dc72c685c
> --- /dev/null
> +++ b/pc-bios/s390-ccw/clp.c
> @@ -0,0 +1,96 @@
> +/*
> + * Call Logical Processor (CLP) architecture
> + *
> + * Copyright 2025 IBM Corp.
> + * Author(s): Jared Rossi <jrossi@linux.ibm.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "clp.h"
> +#include <stdio.h>
> +#include <string.h>
> +
> +int clp_pci(void *data)
> +{
> + struct { uint8_t _[2048]; } *req = data;
> + int cc = 3;
> +
> + asm volatile (
> + " .insn rrf,0xb9a00000,0,%[req],0,2\n"
> + " ipm %[cc]\n"
> + " srl %[cc],28\n"
> + : [cc] "+d" (cc), "+m" (*req)
> + : [req] "a" (req)
> + : "cc");
> + return cc;
> +}
> +
> +/*
> + * Get the PCI function entry for a given function ID
> + * Return 0 on success, 1 if the FID is not found, or a negative RC on error
> + */
> +int find_pci_function(uint32_t fid, ClpFhListEntry *entry)
> +{
> + int count = 0;
> + int limit = PCI_MAX_FUNCTIONS;
> + ClpReqRspListPci rrb;
> +
> + rrb.request.hdr.len = 32;
> + rrb.request.hdr.cmd = 0x02;
> + rrb.request.resume_token = 0;
> + rrb.response.hdr.len = sizeof(ClpRspListPci);
> +
> + do {
> + if (clp_pci(&rrb) || (rrb.response.hdr.rsp != 0x0010)) {
You could drop the parentheses around rrb.response.hdr.rsp != 0x0010 here.
> + puts("Failed to list PCI functions");
> + return -1;
> + }
> +
> + /* Resume token set when max enteries are returned */
> + if (rrb.response.resume_token) {
> + count = CLP_FH_LIST_NR_ENTRIES;
> + rrb.request.resume_token = rrb.response.resume_token;
> + } else {
> + count = (rrb.response.hdr.len - 32) / sizeof(ClpFhListEntry);
> + }
> +
> + limit -= count;
> +
> + for (int i = 0; i < count; i++) {
> + if (rrb.response.fh_list[i].fid == fid) {
> + memcpy(entry, &rrb.response.fh_list[i], sizeof(ClpFhListEntry));
> + return 0;
> + }
> + }
> +
> + } while (rrb.request.resume_token && limit);
Maybe check for "limit > 0", just in case... ?
> + puts("No function entry found for FID!");
> +
> + return 1;
> +}
> +
> +/*
> + * Enable the PCI function associated with a given handle
> + * Return 0 on success or a negative RC on error
> + */
> +int enable_pci_function(uint32_t *fhandle)
> +{
> + ClpReqRspSetPci rrb;
> +
> + rrb.request.hdr.len = 32;
> + rrb.request.hdr.cmd = 0x05;
> + rrb.request.fh = *fhandle;
> + rrb.request.oc = 0;
> + rrb.request.ndas = 1;
> + rrb.response.hdr.len = 32;
> +
> + if (clp_pci(&rrb) || (rrb.response.hdr.rsp != 0x0010)) {
Drop parentheses, please.
> + puts("Failed to enable PCI function");
> + return -1;
> + }
> +
> + *fhandle = rrb.response.fh;
> + return 0;
> +}
> diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
> index 259cff09db..9c29548f84 100644
> --- a/pc-bios/s390-ccw/Makefile
> +++ b/pc-bios/s390-ccw/Makefile
> @@ -35,7 +35,7 @@ QEMU_DGFLAGS = -MMD -MP -MT $@ -MF $(@D)/$(*F).d
>
> OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o netmain.o \
> virtio.o virtio-net.o virtio-scsi.o virtio-blkdev.o cio.o dasd-ipl.o \
> - virtio-ccw.o
> + virtio-ccw.o clp.o
>
> SLOF_DIR := $(SRC_PATH)/../../roms/SLOF
>
Thomas
^ permalink raw reply [flat|nested] 36+ messages in thread* Re: [PATCH 05/10] pc-bios/s390-ccw: Introduce CLP Architecture
2026-01-07 10:25 ` Thomas Huth
@ 2026-01-07 16:40 ` Jared Rossi
0 siblings, 0 replies; 36+ messages in thread
From: Jared Rossi @ 2026-01-07 16:40 UTC (permalink / raw)
To: Thomas Huth, qemu-devel, qemu-s390x, mst
Cc: jjherne, alifm, farman, mjrosato, zycai
Hi Thomas,
All of the comments below raise good points, I'll fix them in the next
version.
Thanks for your reviews.
Regards,
Jared Rossi
On 1/7/26 5:25 AM, Thomas Huth wrote:
> On 10/12/2025 21.54, jrossi@linux.ibm.com wrote:
>> From: Jared Rossi <jrossi@linux.ibm.com>
>>
>> Call Logical Processor (CLP) Architecture is used for managing PCI
>> functions on
>> s390x. Define and include the structures and routines needed to
>> interact with
>> PCI devices during IPL.
>>
>> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
>> ---
>> pc-bios/s390-ccw/clp.h | 24 ++++++++++
>> pc-bios/s390-ccw/clp.c | 96 +++++++++++++++++++++++++++++++++++++++
>> pc-bios/s390-ccw/Makefile | 2 +-
>> 3 files changed, 121 insertions(+), 1 deletion(-)
>> create mode 100644 pc-bios/s390-ccw/clp.h
>> create mode 100644 pc-bios/s390-ccw/clp.c
>>
>> diff --git a/pc-bios/s390-ccw/clp.h b/pc-bios/s390-ccw/clp.h
>> new file mode 100644
>> index 0000000000..1ac2f8c177
>> --- /dev/null
>> +++ b/pc-bios/s390-ccw/clp.h
>> @@ -0,0 +1,24 @@
>> +/*
>> + * Call Logical Processor (CLP) architecture definitions
>> + *
>> + * Copyright 2025 IBM Corp.
>> + * Author(s): Jared Rossi <jrossi@linux.ibm.com>
>> + *
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + */
>> +
>> +#ifndef CLP_H
>> +#define CLP_H
>> +
>> +#ifndef QEMU_PACKED
>> +#define QEMU_PACKED __attribute__((packed))
>> +#endif
>> +
>> +#include <stdint.h>
>> +#include <s390-pci-clp.h>
>> +
>> +int clp_pci(void *data);
>> +int find_pci_function(uint32_t fid, ClpFhListEntry *entry);
>> +int enable_pci_function(uint32_t *fhandle);
>> +
>> +#endif
>> diff --git a/pc-bios/s390-ccw/clp.c b/pc-bios/s390-ccw/clp.c
>> new file mode 100644
>> index 0000000000..6dc72c685c
>> --- /dev/null
>> +++ b/pc-bios/s390-ccw/clp.c
>> @@ -0,0 +1,96 @@
>> +/*
>> + * Call Logical Processor (CLP) architecture
>> + *
>> + * Copyright 2025 IBM Corp.
>> + * Author(s): Jared Rossi <jrossi@linux.ibm.com>
>> + *
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + */
>> +
>> +#include "clp.h"
>> +#include <stdio.h>
>> +#include <string.h>
>> +
>> +int clp_pci(void *data)
>> +{
>> + struct { uint8_t _[2048]; } *req = data;
>> + int cc = 3;
>> +
>> + asm volatile (
>> + " .insn rrf,0xb9a00000,0,%[req],0,2\n"
>> + " ipm %[cc]\n"
>> + " srl %[cc],28\n"
>> + : [cc] "+d" (cc), "+m" (*req)
>> + : [req] "a" (req)
>> + : "cc");
>> + return cc;
>> +}
>> +
>> +/*
>> + * Get the PCI function entry for a given function ID
>> + * Return 0 on success, 1 if the FID is not found, or a negative RC
>> on error
>> + */
>> +int find_pci_function(uint32_t fid, ClpFhListEntry *entry)
>> +{
>> + int count = 0;
>> + int limit = PCI_MAX_FUNCTIONS;
>> + ClpReqRspListPci rrb;
>> +
>> + rrb.request.hdr.len = 32;
>> + rrb.request.hdr.cmd = 0x02;
>> + rrb.request.resume_token = 0;
>> + rrb.response.hdr.len = sizeof(ClpRspListPci);
>> +
>> + do {
>> + if (clp_pci(&rrb) || (rrb.response.hdr.rsp != 0x0010)) {
>
> You could drop the parentheses around rrb.response.hdr.rsp != 0x0010
> here.
>
>> + puts("Failed to list PCI functions");
>> + return -1;
>> + }
>> +
>> + /* Resume token set when max enteries are returned */
>> + if (rrb.response.resume_token) {
>> + count = CLP_FH_LIST_NR_ENTRIES;
>> + rrb.request.resume_token = rrb.response.resume_token;
>> + } else {
>> + count = (rrb.response.hdr.len - 32) /
>> sizeof(ClpFhListEntry);
>> + }
>> +
>> + limit -= count;
>> +
>> + for (int i = 0; i < count; i++) {
>> + if (rrb.response.fh_list[i].fid == fid) {
>> + memcpy(entry, &rrb.response.fh_list[i],
>> sizeof(ClpFhListEntry));
>> + return 0;
>> + }
>> + }
>> +
>> + } while (rrb.request.resume_token && limit);
>
> Maybe check for "limit > 0", just in case... ?
>
>> + puts("No function entry found for FID!");
>> +
>> + return 1;
>> +}
>> +
>> +/*
>> + * Enable the PCI function associated with a given handle
>> + * Return 0 on success or a negative RC on error
>> + */
>> +int enable_pci_function(uint32_t *fhandle)
>> +{
>> + ClpReqRspSetPci rrb;
>> +
>> + rrb.request.hdr.len = 32;
>> + rrb.request.hdr.cmd = 0x05;
>> + rrb.request.fh = *fhandle;
>> + rrb.request.oc = 0;
>> + rrb.request.ndas = 1;
>> + rrb.response.hdr.len = 32;
>> +
>> + if (clp_pci(&rrb) || (rrb.response.hdr.rsp != 0x0010)) {
>
> Drop parentheses, please.
>
>> + puts("Failed to enable PCI function");
>> + return -1;
>> + }
>> +
>> + *fhandle = rrb.response.fh;
>> + return 0;
>> +}
>> diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
>> index 259cff09db..9c29548f84 100644
>> --- a/pc-bios/s390-ccw/Makefile
>> +++ b/pc-bios/s390-ccw/Makefile
>> @@ -35,7 +35,7 @@ QEMU_DGFLAGS = -MMD -MP -MT $@ -MF $(@D)/$(*F).d
>> OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o
>> netmain.o \
>> virtio.o virtio-net.o virtio-scsi.o virtio-blkdev.o cio.o
>> dasd-ipl.o \
>> - virtio-ccw.o
>> + virtio-ccw.o clp.o
>> SLOF_DIR := $(SRC_PATH)/../../roms/SLOF
>
> Thomas
>
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH 06/10] pc-bios/s390-ccw: Introduce PCI device
2025-12-10 20:54 [PATCH V2 0/10] s390x: Add support for virtio-blk-pci IPL device jrossi
` (4 preceding siblings ...)
2025-12-10 20:54 ` [PATCH 05/10] pc-bios/s390-ccw: Introduce CLP Architecture jrossi
@ 2025-12-10 20:54 ` jrossi
2026-01-07 11:02 ` Thomas Huth
2025-12-10 20:54 ` [PATCH 07/10] pc-bios/s390-ccw: Add support for virtio-blk-pci IPL jrossi
` (3 subsequent siblings)
9 siblings, 1 reply; 36+ messages in thread
From: jrossi @ 2025-12-10 20:54 UTC (permalink / raw)
To: qemu-devel, qemu-s390x, thuth, mst
Cc: jjherne, alifm, farman, mjrosato, jrossi, zycai
From: Jared Rossi <jrossi@linux.ibm.com>
Define selected s390x PCI instructions and extend IPLB to allow PCI devices.
Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
---
pc-bios/s390-ccw/pci.h | 88 ++++++++++
pc-bios/s390-ccw/pci.c | 331 ++++++++++++++++++++++++++++++++++++++
pc-bios/s390-ccw/Makefile | 2 +-
3 files changed, 420 insertions(+), 1 deletion(-)
create mode 100644 pc-bios/s390-ccw/pci.h
create mode 100644 pc-bios/s390-ccw/pci.c
diff --git a/pc-bios/s390-ccw/pci.h b/pc-bios/s390-ccw/pci.h
new file mode 100644
index 0000000000..847cf3f194
--- /dev/null
+++ b/pc-bios/s390-ccw/pci.h
@@ -0,0 +1,88 @@
+/*
+ * s390x PCI definitions
+ *
+ * Copyright 2025 IBM Corp.
+ * Author(s): Jared Rossi <jrossi@linux.ibm.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef PCI_H
+#define PCI_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "clp.h"
+
+#define ZPCI_CREATE_REQ(handle, space, len) \
+ ((uint64_t) handle << 32 | space << 16 | len)
+
+union register_pair {
+ unsigned __int128 pair;
+ struct {
+ unsigned long even;
+ unsigned long odd;
+ };
+};
+
+#define PCIFIB_FC_ENABLED 0x80
+#define PCIFIB_FC_ERROR 0x40
+#define PCIFIB_FC_BLOCKED 0x20
+#define PCIFIB_FC_DMAREG 0x10
+
+#define PCIST_DISABLED 0x0
+#define PCIST_ENABLED 0x1
+
+#define PCI_CFGBAR 0xF /* Base Address Register for config space */
+#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */
+
+struct PciFib {
+ uint32_t reserved0[2];
+ uint8_t fcflags;
+ uint8_t reserved1[3];
+ uint32_t reserved2;
+ uint64_t pba;
+ uint64_t pal;
+ uint64_t iota;
+ uint16_t isc:4;
+ uint16_t noi:12;
+ uint8_t reserved3:2;
+ uint8_t aibvo:6;
+ uint8_t s:1;
+ uint8_t reserved4:1;
+ uint8_t aisbo:6;
+ uint32_t reserved5;
+ uint64_t aibv;
+ uint64_t aisb;
+ uint64_t fmba;
+ uint32_t reserved6[2];
+};
+typedef struct PciFib PciFib;
+
+struct PciDevice {
+ uint16_t device_id;
+ uint16_t vendor_id;
+ uint32_t fid;
+ uint32_t fhandle;
+ uint8_t status;
+ PciFib fib;
+};
+typedef struct PciDevice PciDevice;
+
+int pci_write_flex(uint32_t fhandle, uint64_t offset, uint8_t pcias, void *data, int len);
+int pci_write_byte(uint32_t fhandle, uint64_t offset, uint8_t pcias, uint8_t data);
+int pci_bswap16_write(uint32_t fhandle, uint64_t offset, uint8_t pcias, uint16_t data);
+int pci_bswap32_write(uint32_t fhandle, uint64_t offset, uint8_t pcias, uint32_t data);
+int pci_bswap64_write(uint32_t fhandle, uint64_t offset, uint8_t pcias, uint64_t data);
+
+int pci_read_flex(uint32_t fhandle, uint64_t offset, uint8_t pcias, void *buf, int len);
+int pci_read_bswap64(uint32_t fhandle, uint64_t offset, uint8_t pcias, uint64_t *buf);
+int pci_read_bswap32(uint32_t fhandle, uint64_t offset, uint8_t pcias, uint32_t *buf);
+int pci_read_bswap16(uint32_t fhandle, uint64_t offset, uint8_t pcias, uint16_t *buf);
+int pci_read_byte(uint32_t fhandle, uint64_t offset, uint8_t pcias, uint8_t *buf);
+
+int pci_dev_enable(PciDevice *pcidev);
+int get_fib(PciFib *fib, uint32_t fhandle);
+int set_fib(PciFib *fib, uint32_t fhandle, uint8_t dma_as, uint8_t opcontrol);
+
+#endif
diff --git a/pc-bios/s390-ccw/pci.c b/pc-bios/s390-ccw/pci.c
new file mode 100644
index 0000000000..c149789386
--- /dev/null
+++ b/pc-bios/s390-ccw/pci.c
@@ -0,0 +1,331 @@
+/*
+ * s390x PCI funcionality
+ *
+ * Copyright 2025 IBM Corp.
+ * Author(s): Jared Rossi <jrossi@linux.ibm.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "clp.h"
+#include "pci.h"
+#include "bswap.h"
+#include <stdio.h>
+#include <stdbool.h>
+
+/* PCI load */
+static inline int pcilg(uint64_t *data, uint64_t req, uint64_t offset, uint8_t *status)
+{
+ union register_pair req_off = {.even = req, .odd = offset};
+ int cc = -1;
+ uint64_t __data;
+
+ asm volatile (
+ " .insn rre,0xb9d20000,%[data],%[req_off]\n"
+ " ipm %[cc]\n"
+ " srl %[cc],28\n"
+ : [cc] "+d" (cc), [data] "=d" (__data),
+ [req_off] "+d" (req_off.pair) :: "cc");
+ *status = req_off.even >> 24 & 0xff;
+ *data = __data;
+ return cc;
+}
+
+/* PCI store */
+int pcistg(uint64_t data, uint64_t req, uint64_t offset, uint8_t *status)
+{
+ union register_pair req_off = {.even = req, .odd = offset};
+ int cc = -1;
+
+ asm volatile (
+ " .insn rre,0xb9d00000,%[data],%[req_off]\n"
+ " ipm %[cc]\n"
+ " srl %[cc],28\n"
+ : [cc] "+d" (cc), [req_off] "+d" (req_off.pair)
+ : [data] "d" (data)
+ : "cc");
+ *status = req_off.even >> 24 & 0xff;
+ return cc;
+}
+
+/* store PCI function controls */
+int stpcifc(uint64_t req, PciFib *fib, uint8_t *status)
+{
+ uint8_t cc;
+
+ asm volatile (
+ " .insn rxy,0xe300000000d4,%[req],%[fib]\n"
+ " ipm %[cc]\n"
+ " srl %[cc],28\n"
+ : [cc] "=d" (cc), [req] "+d" (req), [fib] "+Q" (*fib)
+ : : "cc");
+ *status = req >> 24 & 0xff;
+ return cc;
+}
+
+/* modify PCI function controls */
+int mpcifc(uint64_t req, PciFib *fib, uint8_t *status)
+{
+ uint8_t cc;
+
+ asm volatile (
+ " .insn rxy,0xe300000000d0,%[req],%[fib]\n"
+ " ipm %[cc]\n"
+ " srl %[cc],28\n"
+ : [cc] "=d" (cc), [req] "+d" (req), [fib] "+Q" (*fib)
+ : : "cc");
+ *status = req >> 24 & 0xff;
+ return cc;
+}
+
+static int pci_write(uint32_t fhandle, uint64_t offset, uint8_t pcias,
+ uint64_t data, uint8_t len)
+{
+
+ uint64_t req = ZPCI_CREATE_REQ(fhandle, pcias, len);
+ uint8_t status;
+ int rc;
+
+ /* writes must be non-zero powers of 2 with a maximum of 8 bytes per read */
+ switch (len) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ rc = pcistg(data, req, offset, &status);
+ break;
+ default:
+ rc = -1;
+ }
+
+ /* Error condition detected */
+ if (rc == 1) {
+ printf("PCI store failed with status condition %d\n", status);
+ return -1;
+ }
+
+ return rc ? -1 : 0;
+}
+
+/* Write an arbitrary length of data without byte swapping */
+int pci_write_flex(uint32_t fh, uint64_t offset, uint8_t pcias, void *data, int len)
+{
+ uint8_t writelen, tmp;
+ int rc;
+ int remaining = len;
+
+ /* write bytes in powers of 2, up to a maximum of 8 bytes per read */
+ while (remaining) {
+ if (remaining > 7) {
+ writelen = 8;
+ } else {
+ writelen = 1;
+ while (true) {
+ tmp = writelen * 2;
+ if (tmp > remaining) {
+ break;
+ }
+
+ writelen = tmp;
+ }
+ }
+
+ /* Access next data based on write size */
+ switch (writelen) {
+ case 1:
+ rc = pci_write(fh, offset, pcias, ((uint8_t *)data)[0], 1);
+ break;
+ case 2:
+ rc = pci_write(fh, offset, pcias, ((uint16_t *)data)[0], 2);
+ break;
+ case 4:
+ rc = pci_write(fh, offset, pcias, ((uint32_t *)data)[0], 4);
+ break;
+ case 8:
+ rc = pci_write(fh, offset, pcias, ((uint64_t *)data)[0], 8);
+ break;
+ default:
+ rc = -1;
+ }
+
+ if (rc) {
+ return -1;
+ }
+
+ remaining -= writelen;
+ data += writelen;
+ offset += writelen;
+ }
+
+ return 0;
+}
+
+int pci_write_byte(uint32_t fhandle, uint64_t offset, uint8_t pcias, uint8_t data)
+{
+ return pci_write(fhandle, offset, pcias, (uint64_t)data, 1);
+}
+
+/* Wrappers to byte swap common data sizes then write */
+int pci_bswap16_write(uint32_t fhandle, uint64_t offset, uint8_t pcias, uint16_t data)
+{
+ uint64_t le_data = bswap16(data);
+ return pci_write(fhandle, offset, pcias, le_data, 2);
+}
+
+int pci_bswap32_write(uint32_t fhandle, uint64_t offset, uint8_t pcias, uint32_t data)
+{
+ uint64_t le_data = bswap32(data);
+ return pci_write(fhandle, offset, pcias, le_data, 4);
+}
+
+int pci_bswap64_write(uint32_t fhandle, uint64_t offset, uint8_t pcias, uint64_t data)
+{
+ uint64_t le_data = bswap64(data);
+ return pci_write(fhandle, offset, pcias, le_data, 8);
+}
+
+static int pci_read(uint32_t fh, uint64_t offset, uint8_t pcias, void *buf, uint8_t len)
+{
+ uint64_t req, data;
+ uint8_t status;
+ int rc;
+
+ req = ZPCI_CREATE_REQ(fh, pcias, len);
+ rc = pcilg(&data, req, offset, &status);
+
+ /* Error condition detected */
+ if (rc == 1) {
+ printf("PCI load failed with status condition %d\n", status);
+ return -1;
+ }
+
+ switch (len) {
+ case 1:
+ *(uint8_t *)buf = data;
+ break;
+ case 2:
+ *(uint16_t *)buf = data;
+ break;
+ case 4:
+ *(uint32_t *)buf = data;
+ break;
+ case 8:
+ *(uint64_t *)buf = data;
+ break;
+ default:
+ return -1;
+ }
+
+ return rc ? -1 : 0;
+}
+
+/* Read to an arbitrary length buffer without byte swapping */
+int pci_read_flex(uint32_t fh, uint64_t offset, uint8_t pcias, void *buf, int len)
+{
+ uint8_t readlen, tmp;
+ int rc;
+ int remaining = len;
+
+ /* Read bytes in powers of 2, up to a maximum of 8 bytes per read */
+ while (remaining) {
+ if (remaining > 7) {
+ readlen = 8;
+ } else {
+ readlen = 1;
+ while (true) {
+ tmp = readlen * 2;
+ if (tmp > remaining) {
+ break;
+ }
+
+ readlen = tmp;
+ }
+ }
+
+ rc = pci_read(fh, offset, pcias, buf, readlen);
+ if (rc) {
+ return -1;
+ }
+
+ remaining -= readlen;
+ buf += readlen;
+ offset += readlen;
+ }
+
+ return 0;
+}
+
+int pci_read_byte(uint32_t fh, uint64_t offset, uint8_t pcias, uint8_t *buf)
+{
+ return pci_read(fh, offset, pcias, buf, 1);
+}
+
+/* Wrappers to read common data sizes then byte swap */
+int pci_read_bswap16(uint32_t fh, uint64_t offset, uint8_t pcias, uint16_t *buf)
+{
+ int rc = pci_read(fh, offset, pcias, buf, 2);
+ *buf = bswap16(*buf);
+ return rc;
+}
+
+int pci_read_bswap32(uint32_t fh, uint64_t offset, uint8_t pcias, uint32_t *buf)
+{
+ int rc = pci_read(fh, offset, pcias, buf, 4);
+ *buf = bswap32(*buf);
+ return rc;
+}
+
+int pci_read_bswap64(uint32_t fh, uint64_t offset, uint8_t pcias, uint64_t *buf)
+{
+ int rc = pci_read(fh, offset, pcias, buf, 8);
+ *buf = bswap64(*buf);
+ return rc;
+}
+
+int pci_dev_enable(PciDevice *pcidev)
+{
+ int rc;
+
+ rc = enable_pci_function(&pcidev->fhandle);
+ if (rc) {
+ return rc;
+ }
+
+ pcidev->status = PCIST_ENABLED;
+
+ return get_fib(&pcidev->fib, pcidev->fhandle);
+}
+
+int get_fib(PciFib *fib, uint32_t fhandle)
+{
+ uint64_t req = ZPCI_CREATE_REQ(fhandle, 0, 0);
+ uint8_t status;
+ int rc;
+
+ rc = stpcifc(req, fib, &status);
+
+ if (rc == 1) {
+ return status;
+ } else if (rc) {
+ return rc;
+ }
+
+ return 0;
+}
+
+int set_fib(PciFib *fib, uint32_t fhandle, uint8_t dma_as, uint8_t opcontrol)
+{
+ uint64_t req = ZPCI_CREATE_REQ(fhandle, dma_as, opcontrol);
+ uint8_t status;
+ int rc;
+
+ rc = mpcifc(req, fib, &status);
+
+ if (rc == 1) {
+ return status;
+ } else if (rc) {
+ return rc;
+ }
+
+ return 0;
+}
diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
index 9c29548f84..a62fc9d766 100644
--- a/pc-bios/s390-ccw/Makefile
+++ b/pc-bios/s390-ccw/Makefile
@@ -35,7 +35,7 @@ QEMU_DGFLAGS = -MMD -MP -MT $@ -MF $(@D)/$(*F).d
OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o netmain.o \
virtio.o virtio-net.o virtio-scsi.o virtio-blkdev.o cio.o dasd-ipl.o \
- virtio-ccw.o clp.o
+ virtio-ccw.o clp.o pci.o
SLOF_DIR := $(SRC_PATH)/../../roms/SLOF
--
2.49.0
^ permalink raw reply related [flat|nested] 36+ messages in thread* Re: [PATCH 06/10] pc-bios/s390-ccw: Introduce PCI device
2025-12-10 20:54 ` [PATCH 06/10] pc-bios/s390-ccw: Introduce PCI device jrossi
@ 2026-01-07 11:02 ` Thomas Huth
0 siblings, 0 replies; 36+ messages in thread
From: Thomas Huth @ 2026-01-07 11:02 UTC (permalink / raw)
To: jrossi, qemu-devel, qemu-s390x, mst
Cc: jjherne, alifm, farman, mjrosato, zycai
On 10/12/2025 21.54, jrossi@linux.ibm.com wrote:
> From: Jared Rossi <jrossi@linux.ibm.com>
>
> Define selected s390x PCI instructions and extend IPLB to allow PCI devices.
>
> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
> ---
> pc-bios/s390-ccw/pci.h | 88 ++++++++++
> pc-bios/s390-ccw/pci.c | 331 ++++++++++++++++++++++++++++++++++++++
> pc-bios/s390-ccw/Makefile | 2 +-
> 3 files changed, 420 insertions(+), 1 deletion(-)
> create mode 100644 pc-bios/s390-ccw/pci.h
> create mode 100644 pc-bios/s390-ccw/pci.c
...
> diff --git a/pc-bios/s390-ccw/pci.c b/pc-bios/s390-ccw/pci.c
> new file mode 100644
> index 0000000000..c149789386
> --- /dev/null
> +++ b/pc-bios/s390-ccw/pci.c
> @@ -0,0 +1,331 @@
> +/*
> + * s390x PCI funcionality
> + *
> + * Copyright 2025 IBM Corp.
> + * Author(s): Jared Rossi <jrossi@linux.ibm.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "clp.h"
> +#include "pci.h"
> +#include "bswap.h"
> +#include <stdio.h>
> +#include <stdbool.h>
> +
> +/* PCI load */
> +static inline int pcilg(uint64_t *data, uint64_t req, uint64_t offset, uint8_t *status)
> +{
> + union register_pair req_off = {.even = req, .odd = offset};
> + int cc = -1;
> + uint64_t __data;
> +
> + asm volatile (
> + " .insn rre,0xb9d20000,%[data],%[req_off]\n"
> + " ipm %[cc]\n"
> + " srl %[cc],28\n"
> + : [cc] "+d" (cc), [data] "=d" (__data),
> + [req_off] "+d" (req_off.pair) :: "cc");
> + *status = req_off.even >> 24 & 0xff;
> + *data = __data;
> + return cc;
> +}
> +
> +/* PCI store */
> +int pcistg(uint64_t data, uint64_t req, uint64_t offset, uint8_t *status)
> +{
> + union register_pair req_off = {.even = req, .odd = offset};
> + int cc = -1;
> +
> + asm volatile (
> + " .insn rre,0xb9d00000,%[data],%[req_off]\n"
> + " ipm %[cc]\n"
> + " srl %[cc],28\n"
> + : [cc] "+d" (cc), [req_off] "+d" (req_off.pair)
> + : [data] "d" (data)
> + : "cc");
> + *status = req_off.even >> 24 & 0xff;
> + return cc;
> +}
> +
> +/* store PCI function controls */
> +int stpcifc(uint64_t req, PciFib *fib, uint8_t *status)
> +{
> + uint8_t cc;
> +
> + asm volatile (
> + " .insn rxy,0xe300000000d4,%[req],%[fib]\n"
> + " ipm %[cc]\n"
> + " srl %[cc],28\n"
> + : [cc] "=d" (cc), [req] "+d" (req), [fib] "+Q" (*fib)
> + : : "cc");
> + *status = req >> 24 & 0xff;
> + return cc;
> +}
> +
> +/* modify PCI function controls */
> +int mpcifc(uint64_t req, PciFib *fib, uint8_t *status)
> +{
> + uint8_t cc;
> +
> + asm volatile (
> + " .insn rxy,0xe300000000d0,%[req],%[fib]\n"
> + " ipm %[cc]\n"
> + " srl %[cc],28\n"
> + : [cc] "=d" (cc), [req] "+d" (req), [fib] "+Q" (*fib)
> + : : "cc");
> + *status = req >> 24 & 0xff;
> + return cc;
> +}
> +
> +static int pci_write(uint32_t fhandle, uint64_t offset, uint8_t pcias,
> + uint64_t data, uint8_t len)
> +{
> +
> + uint64_t req = ZPCI_CREATE_REQ(fhandle, pcias, len);
> + uint8_t status;
> + int rc;
> +
> + /* writes must be non-zero powers of 2 with a maximum of 8 bytes per read */
> + switch (len) {
> + case 1:
> + case 2:
> + case 4:
> + case 8:
> + rc = pcistg(data, req, offset, &status);
> + break;
> + default:
> + rc = -1;
> + }
> +
> + /* Error condition detected */
> + if (rc == 1) {
> + printf("PCI store failed with status condition %d\n", status);
> + return -1;
> + }
> +
> + return rc ? -1 : 0;
> +}
> +
> +/* Write an arbitrary length of data without byte swapping */
> +int pci_write_flex(uint32_t fh, uint64_t offset, uint8_t pcias, void *data, int len)
You don't seem to use the pci_write_flex() functions in any later patches
... could it be dropped?
> +{
> + uint8_t writelen, tmp;
> + int rc;
> + int remaining = len;
> +
> + /* write bytes in powers of 2, up to a maximum of 8 bytes per read */
> + while (remaining) {
> + if (remaining > 7) {
> + writelen = 8;
> + } else {
> + writelen = 1;
> + while (true) {
> + tmp = writelen * 2;
> + if (tmp > remaining) {
> + break;
> + }
> +
> + writelen = tmp;
> + }
> + }
> +
> + /* Access next data based on write size */
> + switch (writelen) {
> + case 1:
> + rc = pci_write(fh, offset, pcias, ((uint8_t *)data)[0], 1);
> + break;
> + case 2:
> + rc = pci_write(fh, offset, pcias, ((uint16_t *)data)[0], 2);
> + break;
> + case 4:
> + rc = pci_write(fh, offset, pcias, ((uint32_t *)data)[0], 4);
> + break;
> + case 8:
> + rc = pci_write(fh, offset, pcias, ((uint64_t *)data)[0], 8);
> + break;
> + default:
> + rc = -1;
> + }
> +
> + if (rc) {
> + return -1;
> + }
> +
> + remaining -= writelen;
> + data += writelen;
> + offset += writelen;
> + }
> +
> + return 0;
> +}
> +
> +int pci_write_byte(uint32_t fhandle, uint64_t offset, uint8_t pcias, uint8_t data)
> +{
> + return pci_write(fhandle, offset, pcias, (uint64_t)data, 1);
> +}
> +
> +/* Wrappers to byte swap common data sizes then write */
> +int pci_bswap16_write(uint32_t fhandle, uint64_t offset, uint8_t pcias, uint16_t data)
> +{
> + uint64_t le_data = bswap16(data);
> + return pci_write(fhandle, offset, pcias, le_data, 2);
> +}
> +
> +int pci_bswap32_write(uint32_t fhandle, uint64_t offset, uint8_t pcias, uint32_t data)
> +{
> + uint64_t le_data = bswap32(data);
> + return pci_write(fhandle, offset, pcias, le_data, 4);
> +}
> +
> +int pci_bswap64_write(uint32_t fhandle, uint64_t offset, uint8_t pcias, uint64_t data)
> +{
> + uint64_t le_data = bswap64(data);
> + return pci_write(fhandle, offset, pcias, le_data, 8);
> +}
> +
> +static int pci_read(uint32_t fh, uint64_t offset, uint8_t pcias, void *buf, uint8_t len)
> +{
> + uint64_t req, data;
> + uint8_t status;
> + int rc;
> +
> + req = ZPCI_CREATE_REQ(fh, pcias, len);
> + rc = pcilg(&data, req, offset, &status);
> +
> + /* Error condition detected */
> + if (rc == 1) {
> + printf("PCI load failed with status condition %d\n", status);
> + return -1;
> + }
> +
> + switch (len) {
> + case 1:
> + *(uint8_t *)buf = data;
> + break;
> + case 2:
> + *(uint16_t *)buf = data;
> + break;
> + case 4:
> + *(uint32_t *)buf = data;
> + break;
> + case 8:
> + *(uint64_t *)buf = data;
> + break;
> + default:
> + return -1;
> + }
> +
> + return rc ? -1 : 0;
> +}
> +
> +/* Read to an arbitrary length buffer without byte swapping */
> +int pci_read_flex(uint32_t fh, uint64_t offset, uint8_t pcias, void *buf, int len)
> +{
> + uint8_t readlen, tmp;
> + int rc;
> + int remaining = len;
> +
> + /* Read bytes in powers of 2, up to a maximum of 8 bytes per read */
> + while (remaining) {
> + if (remaining > 7) {
> + readlen = 8;
> + } else {
> + readlen = 1;
> + while (true) {
> + tmp = readlen * 2;
> + if (tmp > remaining) {
> + break;
> + }
> +
> + readlen = tmp;
> + }
> + }
This looks cumbersome ... maybe rather something like this (untested):
for (int i = 3; i >= 0; i--) {
readlen = 1 << i;
if (remaining >= readlen)
break;
}
?
> + rc = pci_read(fh, offset, pcias, buf, readlen);
> + if (rc) {
> + return -1;
> + }
> +
> + remaining -= readlen;
> + buf += readlen;
> + offset += readlen;
> + }
> +
> + return 0;
> +}
Thomas
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH 07/10] pc-bios/s390-ccw: Add support for virtio-blk-pci IPL
2025-12-10 20:54 [PATCH V2 0/10] s390x: Add support for virtio-blk-pci IPL device jrossi
` (5 preceding siblings ...)
2025-12-10 20:54 ` [PATCH 06/10] pc-bios/s390-ccw: Introduce PCI device jrossi
@ 2025-12-10 20:54 ` jrossi
2026-01-07 14:44 ` Thomas Huth
2026-01-07 20:39 ` Zhuoying Cai
2025-12-10 20:54 ` [PATCH 08/10] s390x: Build IPLB for virtio-pci devices jrossi
` (2 subsequent siblings)
9 siblings, 2 replies; 36+ messages in thread
From: jrossi @ 2025-12-10 20:54 UTC (permalink / raw)
To: qemu-devel, qemu-s390x, thuth, mst
Cc: jjherne, alifm, farman, mjrosato, jrossi, zycai
From: Jared Rossi <jrossi@linux.ibm.com>
Enable virt-queue PCI configuration and add routines for virtio-blk-pci devices.
Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
---
include/hw/s390x/ipl/qipl.h | 10 +
pc-bios/s390-ccw/virtio-pci.h | 79 +++++++
pc-bios/s390-ccw/virtio.h | 1 +
pc-bios/s390-ccw/main.c | 60 +++++-
pc-bios/s390-ccw/virtio-blkdev.c | 3 +
pc-bios/s390-ccw/virtio-pci.c | 360 +++++++++++++++++++++++++++++++
pc-bios/s390-ccw/virtio.c | 5 +
pc-bios/s390-ccw/Makefile | 2 +-
8 files changed, 517 insertions(+), 3 deletions(-)
create mode 100644 pc-bios/s390-ccw/virtio-pci.h
create mode 100644 pc-bios/s390-ccw/virtio-pci.c
diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
index 8199b839f0..5c7779a1c3 100644
--- a/include/hw/s390x/ipl/qipl.h
+++ b/include/hw/s390x/ipl/qipl.h
@@ -22,6 +22,7 @@
#define S390_IPL_TYPE_FCP 0x00
#define S390_IPL_TYPE_CCW 0x02
+#define S390_IPL_TYPE_PCI 0x04
#define S390_IPL_TYPE_PV 0x05
#define S390_IPL_TYPE_QEMU_SCSI 0xff
@@ -105,6 +106,14 @@ struct IplBlockQemuScsi {
} QEMU_PACKED;
typedef struct IplBlockQemuScsi IplBlockQemuScsi;
+struct IplBlockPci {
+ uint32_t reserved0[80];
+ uint8_t opt;
+ uint8_t reserved1[3];
+ uint32_t fid;
+} QEMU_PACKED;
+typedef struct IplBlockPci IplBlockPci;
+
union IplParameterBlock {
struct {
uint32_t len;
@@ -120,6 +129,7 @@ union IplParameterBlock {
IplBlockFcp fcp;
IPLBlockPV pv;
IplBlockQemuScsi scsi;
+ IplBlockPci pci;
};
} QEMU_PACKED;
struct {
diff --git a/pc-bios/s390-ccw/virtio-pci.h b/pc-bios/s390-ccw/virtio-pci.h
new file mode 100644
index 0000000000..b203f37aea
--- /dev/null
+++ b/pc-bios/s390-ccw/virtio-pci.h
@@ -0,0 +1,79 @@
+/*
+ * Definitions for virtio-pci
+ *
+ * Copyright 2025 IBM Corp.
+ * Author(s): Jared Rossi <jrossi@linux.ibm.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef VIRTIO_PCI_H
+#define VIRTIO_PCI_H
+
+/* Common configuration */
+#define VPCI_CAP_COMMON_CFG 1
+/* Notifications */
+#define VPCI_CAP_NOTIFY_CFG 2
+/* ISR access */
+#define VPCI_CAP_ISR_CFG 3
+/* Device specific configuration */
+#define VPCI_CAP_DEVICE_CFG 4
+/* PCI configuration access */
+#define VPCI_CAP_PCI_CFG 5
+/* Additional shared memory capability */
+#define VPCI_CAP_SHARED_MEMORY_CFG 8
+/* PCI vendor data configuration */
+#define VPCI_CAP_VENDOR_CFG 9
+
+/* Offsets within capability header */
+#define VPCI_CAP_VNDR 0
+#define VPCI_CAP_NEXT 1
+#define VPCI_CAP_LEN 2
+#define VPCI_CAP_CFG_TYPE 3
+#define VPCI_CAP_BAR 4
+#define VPCI_CAP_OFFSET 8
+#define VPCI_CAP_LENGTH 12
+
+#define VPCI_N_CAP_MULT 16 /* Notify multiplier, VPCI_CAP_NOTIFY_CFG only */
+
+/* Common Area Offsets for virtio-pci queue */
+#define VPCI_C_OFFSET_DFSELECT 0
+#define VPCI_C_OFFSET_DF 4
+#define VPCI_C_OFFSET_GFSELECT 8
+#define VPCI_C_OFFSET_GF 12
+#define VPCI_C_COMMON_NUMQ 18
+#define VPCI_C_OFFSET_STATUS 20
+#define VPCI_C_OFFSET_Q_SELECT 22
+#define VPCI_C_OFFSET_Q_SIZE 24
+#define VPCI_C_OFFSET_Q_ENABLE 28
+#define VPCI_C_OFFSET_Q_NOFF 30
+#define VPCI_C_OFFSET_Q_DESCLO 32
+#define VPCI_C_OFFSET_Q_DESCHI 36
+#define VPCI_C_OFFSET_Q_AVAILLO 40
+#define VPCI_C_OFFSET_Q_AVAILHI 44
+#define VPCI_C_OFFSET_Q_USEDLO 48
+#define VPCI_C_OFFSET_Q_USEDHI 52
+
+#define VPCI_S_RESET 0
+#define VPCI_S_ACKNOWLEDGE 1
+#define VPCI_S_DRIVER 2
+#define VPCI_S_DRIVER_OK 4
+#define VPCI_S_FEATURES_OK 8
+
+#define VIRTIO_F_VERSION_1 (1 << (32 - 32)) /* Feature bit 32 */
+
+#define VIRT_Q_SIZE 16
+
+struct VirtioPciCap {
+ uint8_t bar; /* Which PCIAS it's in */
+ uint32_t off; /* Offset within bar */
+};
+typedef struct VirtioPciCap VirtioPciCap;
+
+long virtio_pci_notify(uint32_t fhandle, int vq_id);
+int virtio_pci_setup(VDev *vdev);
+int virtio_pci_setup_device(void);
+int virtio_pci_reset(VDev *vdev);
+void virtio_pci_id2type(VDev *vdev, uint16_t device_id);
+
+#endif
diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
index e747891a2c..cc68c2f8a9 100644
--- a/pc-bios/s390-ccw/virtio.h
+++ b/pc-bios/s390-ccw/virtio.h
@@ -256,6 +256,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 e82d60bbb7..562eeede63 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -15,7 +15,9 @@
#include "s390-arch.h"
#include "s390-ccw.h"
#include "cio.h"
+#include "clp.h"
#include "virtio.h"
+#include "virtio-pci.h"
#include "virtio-scsi.h"
#include "dasd-ipl.h"
@@ -151,6 +153,21 @@ static bool find_subch(int dev_no)
return false;
}
+static bool find_fid(uint32_t fid)
+{
+ 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->dev_type != 0;
+}
+
static void menu_setup(void)
{
if (memcmp(loadparm_str, LOADPARM_PROMPT, LOADPARM_LEN) == 0) {
@@ -238,6 +255,9 @@ static bool find_boot_device(void)
blk_schid.ssid = iplb.scsi.ssid & 0x3;
found = find_subch(iplb.scsi.devno);
break;
+ case S390_IPL_TYPE_PCI:
+ found = find_fid(iplb.pci.fid);
+ break;
default:
puts("Unsupported IPLB");
}
@@ -276,7 +296,7 @@ static int virtio_setup(void)
return ret;
}
-static void ipl_boot_device(void)
+static void ipl_ccw_device(void)
{
switch (cutype) {
case CU_TYPE_DASD_3990:
@@ -289,7 +309,43 @@ static void ipl_boot_device(void)
}
break;
default:
- printf("Attempting to boot from unexpected device type 0x%X\n", cutype);
+ printf("Cannot boot CCW device with cu type 0x%X\n", cutype);
+ }
+}
+
+static void ipl_pci_device(void)
+{
+ VDev *vdev = virtio_get_device();
+ vdev->is_cdrom = false;
+ vdev->scsi_device_selected = false;
+
+ if (virtio_pci_setup_device()) {
+ return;
+ }
+
+ switch (vdev->dev_type) {
+ case VIRTIO_ID_BLOCK:
+ if (virtio_setup() == 0) {
+ zipl_load();
+ }
+ break;
+ default:
+ printf("Cannot boot PCI device type 0x%X\n", vdev->dev_type);
+ }
+}
+
+static void ipl_boot_device(void)
+{
+ switch (virtio_get_device()->ipl_type) {
+ case S390_IPL_TYPE_QEMU_SCSI:
+ case S390_IPL_TYPE_CCW:
+ ipl_ccw_device();
+ break;
+ case S390_IPL_TYPE_PCI:
+ ipl_pci_device();
+ break;
+ default:
+ puts("Unrecognized IPL type!");
}
}
diff --git a/pc-bios/s390-ccw/virtio-blkdev.c b/pc-bios/s390-ccw/virtio-blkdev.c
index 87ab9a9513..0542b4feee 100644
--- a/pc-bios/s390-ccw/virtio-blkdev.c
+++ b/pc-bios/s390-ccw/virtio-blkdev.c
@@ -13,6 +13,7 @@
#include "virtio.h"
#include "virtio-scsi.h"
#include "virtio-ccw.h"
+#include "virtio-pci.h"
#define VIRTIO_BLK_F_GEOMETRY (1 << 4)
#define VIRTIO_BLK_F_BLK_SIZE (1 << 6)
@@ -242,6 +243,8 @@ int virtio_blk_setup_device(void)
case S390_IPL_TYPE_QEMU_SCSI:
case S390_IPL_TYPE_CCW:
return virtio_ccw_setup(vdev);
+ case S390_IPL_TYPE_PCI:
+ return virtio_pci_setup(vdev);
}
return 1;
diff --git a/pc-bios/s390-ccw/virtio-pci.c b/pc-bios/s390-ccw/virtio-pci.c
new file mode 100644
index 0000000000..838231d86c
--- /dev/null
+++ b/pc-bios/s390-ccw/virtio-pci.c
@@ -0,0 +1,360 @@
+/*
+ * Functionality for virtio-pci
+ *
+ * Copyright 2025 IBM Corp.
+ * Author(s): Jared Rossi <jrossi@linux.ibm.com>
+ *
+ * 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 regions */
+VirtioPciCap c_cap; /* Common capabilities */
+VirtioPciCap d_cap; /* Device capabilities */
+VirtioPciCap n_cap; /* Notify capabilities */
+uint32_t notify_mult;
+uint16_t q_notify_offset;
+
+static int virtio_pci_set_status(VDev *vdev, uint8_t status)
+{
+ int rc = pci_write_byte(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_STATUS,
+ c_cap.bar, status);
+ if (rc) {
+ puts("Failed to write virtio-pci status");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int virtio_pci_get_status(VDev *vdev, uint8_t *status)
+{
+ int rc = pci_read_byte(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_STATUS,
+ c_cap.bar, status);
+ if (rc) {
+ puts("Failed to read virtio-pci status");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * Find the position of the capability config within PCI configuration
+ * space for a given cfg type. Return the position if found, otherwise 0.
+ */
+static uint8_t find_cap_pos(uint32_t fhandle, uint8_t cfg_type)
+{
+ uint8_t next, cfg;
+ int rc;
+
+ rc = pci_read_byte(fhandle, PCI_CAPABILITY_LIST, PCI_CFGBAR, &next);
+ rc |= pci_read_byte(fhandle, next + 3, PCI_CFGBAR, &cfg);
+
+ while (!rc && (cfg != cfg_type) && next) {
+ rc = pci_read_byte(fhandle, next + 1, PCI_CFGBAR, &next);
+ rc |= pci_read_byte(fhandle, next + 3, PCI_CFGBAR, &cfg);
+ }
+
+ return rc ? 0 : next;
+}
+
+static int virtio_pci_get_hfeatures(VDev *vdev, uint64_t *features)
+{
+ uint32_t feat0, feat1;
+ int rc;
+
+ rc = pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_DFSELECT,
+ c_cap.bar, 0);
+
+ rc |= pci_read_bswap32(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_DF,
+ c_cap.bar, &feat0);
+
+ rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_DFSELECT,
+ c_cap.bar, 1);
+
+ rc |= pci_read_bswap32(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_DF,
+ c_cap.bar, &feat1);
+
+ if (rc) {
+ puts("Failed to get PCI feature bits");
+ return -EIO;
+ }
+
+ *features = (uint64_t) feat1 << 32;
+ *features |= (uint64_t) feat0;
+
+ return 0;
+}
+
+static int virtio_pci_set_gfeatures(VDev *vdev)
+{
+ int rc;
+
+ rc = pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_GFSELECT,
+ c_cap.bar, 0);
+
+ rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_GF,
+ c_cap.bar, vdev->guest_features[1]);
+
+ rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_GFSELECT,
+ c_cap.bar, 1);
+
+ rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_GF,
+ c_cap.bar, vdev->guest_features[0]);
+
+ if (rc) {
+ puts("Failed to set PCI feature bits");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int virtio_pci_get_blk_config(VDev *vdev)
+{
+ return pci_read_flex(vdev->pci_fh, d_cap.off, d_cap.bar, &vdev->config.blk,
+ sizeof(VirtioBlkConfig));
+}
+
+/* virtio spec v1.3 section 4.1.2.1 */
+void virtio_pci_id2type(VDev *vdev, uint16_t device_id)
+{
+ switch (device_id) {
+ case 0x1001:
+ vdev->dev_type = VIRTIO_ID_BLOCK;
+ break;
+ case 0x1000: /* Other valid but currently unsupported virtio device types */
+ case 0x1004:
+ default:
+ vdev->dev_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;
+ int rc;
+
+ /* Common capabilities */
+ pos = find_cap_pos(vdev->pci_fh, VPCI_CAP_COMMON_CFG);
+ if (!pos) {
+ puts("Failed to locate PCI common configuration");
+ return 1;
+ }
+
+ rc = pci_read_byte(vdev->pci_fh, pos + VPCI_CAP_BAR, PCI_CFGBAR, &c_cap.bar);
+ if (rc || pci_read_bswap32(vdev->pci_fh, pos + VPCI_CAP_OFFSET, PCI_CFGBAR,
+ &c_cap.off)) {
+ puts("Failed to read PCI common configuration");
+ return -EIO;
+ }
+
+ /* Device capabilities */
+ pos = find_cap_pos(vdev->pci_fh, VPCI_CAP_DEVICE_CFG);
+ if (!pos) {
+ puts("Failed to locate PCI device configuration");
+ return 1;
+ }
+
+ rc = pci_read_byte(vdev->pci_fh, pos + VPCI_CAP_BAR, PCI_CFGBAR, &d_cap.bar);
+ if (rc || pci_read_bswap32(vdev->pci_fh, pos + VPCI_CAP_OFFSET, PCI_CFGBAR,
+ &d_cap.off)) {
+ puts("Failed to read PCI device configuration");
+ return -EIO;
+ }
+
+ /* Notification capabilities */
+ pos = find_cap_pos(vdev->pci_fh, VPCI_CAP_NOTIFY_CFG);
+ if (!pos) {
+ puts("Failed to locate PCI notification configuration");
+ return 1;
+ }
+
+ rc = pci_read_byte(vdev->pci_fh, pos + VPCI_CAP_BAR, PCI_CFGBAR, &n_cap.bar);
+ if (rc || pci_read_bswap32(vdev->pci_fh, pos + VPCI_CAP_OFFSET, PCI_CFGBAR,
+ &n_cap.off)) {
+ puts("Failed to read PCI notification configuration");
+ return -EIO;
+ }
+
+ rc = pci_read_bswap32(vdev->pci_fh, pos + VPCI_N_CAP_MULT, PCI_CFGBAR, ¬ify_mult);
+ if (rc || pci_read_bswap16(vdev->pci_fh, d_cap.off + VPCI_C_OFFSET_Q_NOFF,
+ d_cap.bar, &q_notify_offset)) {
+ puts("Failed to read notification queue configuration");
+ return -EIO;
+ }
+
+ 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;
+ }
+
+ return 0;
+}
+
+int virtio_pci_set_selected_vq(VDev *vdev, uint16_t queue_num)
+{
+ return pci_bswap16_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_Q_SELECT,
+ c_cap.bar, queue_num);
+}
+
+int virtio_pci_set_queue_size(VDev *vdev, uint16_t queue_size)
+{
+ return pci_bswap16_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_Q_SIZE,
+ c_cap.bar, queue_size);
+}
+
+static int virtio_pci_set_queue_enable(VDev *vdev, uint16_t enabled)
+{
+ return pci_bswap16_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_Q_ENABLE,
+ c_cap.bar, enabled);
+}
+
+static int set_pci_vq_addr(VDev *vdev, uint64_t config_off, void *addr)
+{
+ return pci_bswap64_write(vdev->pci_fh, c_cap.off + config_off, c_cap.bar,
+ (uint64_t) addr);
+}
+
+int virtio_pci_setup(VDev *vdev)
+{
+ VRing *vr;
+ int rc;
+ uint64_t pci_features;
+ uint8_t status;
+ int i = 0;
+
+ vdev->config.blk.blk_size = 0;
+ vdev->guessed_disk_nature = VIRTIO_GDN_NONE;
+ vdev->cmd_vr_idx = 0;
+
+ if (virtio_reset(vdev)) {
+ return -EIO;
+ }
+
+ status = VPCI_S_ACKNOWLEDGE;
+ rc = virtio_pci_set_status(vdev, status);
+ if (rc) {
+ puts("Virtio-pci device Failed to ACKNOWLEDGE");
+ return -EIO;
+ }
+
+ rc = virtio_pci_read_pci_cap_config(vdev);
+ if (rc) {
+ printf("Invalid PCI capabilities");
+ return -EIO;
+ }
+
+ switch (vdev->dev_type) {
+ case VIRTIO_ID_BLOCK:
+ vdev->nr_vqs = 1;
+ vdev->cmd_vr_idx = 0;
+ virtio_pci_get_blk_config(vdev);
+ break;
+ default:
+ puts("Unsupported virtio device");
+ return -ENODEV;
+ }
+
+ status |= VPCI_S_DRIVER;
+ rc = virtio_pci_set_status(vdev, status);
+ if (rc) {
+ puts("Set status failed");
+ return -EIO;
+ }
+
+ /* Feature negotiation */
+ rc = virtio_pci_get_hfeatures(vdev, &pci_features);
+ if (rc || virtio_pci_set_gfeatures(vdev)) {
+ 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 * VIRTIO_RING_SIZE),
+ .align = KVM_S390_VIRTIO_RING_ALIGN,
+ .index = i,
+ .num = 0,
+ };
+
+ vr = &vdev->vrings[i];
+
+ if (pci_read_flex(vdev->pci_fh, VPCI_C_COMMON_NUMQ, c_cap.bar, &info.num, 2)) {
+ return -EIO;
+ }
+
+ vring_init(vr, &info);
+
+ if (virtio_pci_set_selected_vq(vdev, vr->id)) {
+ puts("Failed to set selected virt-queue");
+ return -EIO;
+ }
+
+ if (virtio_pci_set_queue_size(vdev, VIRTIO_RING_SIZE)) {
+ puts("Failed to set virt-queue size");
+ return -EIO;
+ }
+
+ rc = set_pci_vq_addr(vdev, VPCI_C_OFFSET_Q_DESCLO, vr->desc);
+ rc |= set_pci_vq_addr(vdev, VPCI_C_OFFSET_Q_AVAILLO, vr->avail);
+ rc |= set_pci_vq_addr(vdev, VPCI_C_OFFSET_Q_USEDLO, vr->used);
+ if (rc) {
+ puts("Failed to configure virt-queue address");
+ return -EIO;
+ }
+
+ if (virtio_pci_set_queue_enable(vdev, true)) {
+ puts("Failed to set virt-queue enabled");
+ return -EIO;
+ }
+ }
+
+ status |= VPCI_S_FEATURES_OK | VPCI_S_DRIVER_OK;
+ return virtio_pci_set_status(vdev, status);
+}
+
+int virtio_pci_setup_device(void)
+{
+ VDev *vdev = virtio_get_device();
+
+ if (enable_pci_function(&vdev->pci_fh)) {
+ puts("Failed to enable PCI function");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+long virtio_pci_notify(uint32_t fhandle, int vq_id)
+{
+ uint32_t offset = n_cap.off + notify_mult * q_notify_offset;
+ return pci_bswap16_write(fhandle, offset, n_cap.bar, (uint16_t) vq_id);
+}
diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c
index 0488b3a07e..50afb11f6a 100644
--- a/pc-bios/s390-ccw/virtio.c
+++ b/pc-bios/s390-ccw/virtio.c
@@ -15,6 +15,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"
@@ -109,6 +110,8 @@ bool vring_notify(VRing *vr)
case S390_IPL_TYPE_QEMU_SCSI:
case S390_IPL_TYPE_CCW:
vr->cookie = virtio_ccw_notify(vr->schid, vr->id, vr->cookie);
+ case S390_IPL_TYPE_PCI:
+ vr->cookie = virtio_pci_notify(virtio_get_device()->pci_fh, vr->id);
}
return vr->cookie >= 0;
@@ -182,6 +185,8 @@ int virtio_reset(VDev *vdev)
case S390_IPL_TYPE_QEMU_SCSI:
case S390_IPL_TYPE_CCW:
return virtio_ccw_reset(vdev);
+ case S390_IPL_TYPE_PCI:
+ return virtio_pci_reset(vdev);
default:
return -1;
}
diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
index a62fc9d766..3e5dfb64d5 100644
--- a/pc-bios/s390-ccw/Makefile
+++ b/pc-bios/s390-ccw/Makefile
@@ -35,7 +35,7 @@ QEMU_DGFLAGS = -MMD -MP -MT $@ -MF $(@D)/$(*F).d
OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o netmain.o \
virtio.o virtio-net.o virtio-scsi.o virtio-blkdev.o cio.o dasd-ipl.o \
- virtio-ccw.o clp.o pci.o
+ virtio-ccw.o clp.o pci.o virtio-pci.o
SLOF_DIR := $(SRC_PATH)/../../roms/SLOF
--
2.49.0
^ permalink raw reply related [flat|nested] 36+ messages in thread* Re: [PATCH 07/10] pc-bios/s390-ccw: Add support for virtio-blk-pci IPL
2025-12-10 20:54 ` [PATCH 07/10] pc-bios/s390-ccw: Add support for virtio-blk-pci IPL jrossi
@ 2026-01-07 14:44 ` Thomas Huth
2026-01-07 16:08 ` Thomas Huth
2026-01-07 18:32 ` Jared Rossi
2026-01-07 20:39 ` Zhuoying Cai
1 sibling, 2 replies; 36+ messages in thread
From: Thomas Huth @ 2026-01-07 14:44 UTC (permalink / raw)
To: jrossi, qemu-devel, qemu-s390x, mst
Cc: jjherne, alifm, farman, mjrosato, zycai
On 10/12/2025 21.54, jrossi@linux.ibm.com wrote:
> From: Jared Rossi <jrossi@linux.ibm.com>
>
> Enable virt-queue PCI configuration and add routines for virtio-blk-pci devices.
>
> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
> ---
> include/hw/s390x/ipl/qipl.h | 10 +
> pc-bios/s390-ccw/virtio-pci.h | 79 +++++++
> pc-bios/s390-ccw/virtio.h | 1 +
> pc-bios/s390-ccw/main.c | 60 +++++-
> pc-bios/s390-ccw/virtio-blkdev.c | 3 +
> pc-bios/s390-ccw/virtio-pci.c | 360 +++++++++++++++++++++++++++++++
> pc-bios/s390-ccw/virtio.c | 5 +
> pc-bios/s390-ccw/Makefile | 2 +-
> 8 files changed, 517 insertions(+), 3 deletions(-)
> create mode 100644 pc-bios/s390-ccw/virtio-pci.h
> create mode 100644 pc-bios/s390-ccw/virtio-pci.c
>
> diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
> index 8199b839f0..5c7779a1c3 100644
> --- a/include/hw/s390x/ipl/qipl.h
> +++ b/include/hw/s390x/ipl/qipl.h
> @@ -22,6 +22,7 @@
>
> #define S390_IPL_TYPE_FCP 0x00
> #define S390_IPL_TYPE_CCW 0x02
> +#define S390_IPL_TYPE_PCI 0x04
> #define S390_IPL_TYPE_PV 0x05
> #define S390_IPL_TYPE_QEMU_SCSI 0xff
>
> @@ -105,6 +106,14 @@ struct IplBlockQemuScsi {
> } QEMU_PACKED;
> typedef struct IplBlockQemuScsi IplBlockQemuScsi;
>
> +struct IplBlockPci {
> + uint32_t reserved0[80];
> + uint8_t opt;
> + uint8_t reserved1[3];
> + uint32_t fid;
> +} QEMU_PACKED;
> +typedef struct IplBlockPci IplBlockPci;
Is this supposed to have the same positions for "fid" and "opt" as in the
ipl_pb0_nvme structure in the kernel (arch/s390/include/uapi/asm/ipl.h)?
> union IplParameterBlock {
> struct {
> uint32_t len;
> @@ -120,6 +129,7 @@ union IplParameterBlock {
> IplBlockFcp fcp;
> IPLBlockPV pv;
> IplBlockQemuScsi scsi;
> + IplBlockPci pci;
> };
> } QEMU_PACKED;
> struct {
...
> diff --git a/pc-bios/s390-ccw/virtio-pci.c b/pc-bios/s390-ccw/virtio-pci.c
> new file mode 100644
> index 0000000000..838231d86c
> --- /dev/null
> +++ b/pc-bios/s390-ccw/virtio-pci.c
> @@ -0,0 +1,360 @@
> +/*
> + * Functionality for virtio-pci
> + *
> + * Copyright 2025 IBM Corp.
> + * Author(s): Jared Rossi <jrossi@linux.ibm.com>
> + *
> + * 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 regions */
> +VirtioPciCap c_cap; /* Common capabilities */
> +VirtioPciCap d_cap; /* Device capabilities */
> +VirtioPciCap n_cap; /* Notify capabilities */
> +uint32_t notify_mult;
> +uint16_t q_notify_offset;
> +
> +static int virtio_pci_set_status(VDev *vdev, uint8_t status)
> +{
> + int rc = pci_write_byte(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_STATUS,
> + c_cap.bar, status);
> + if (rc) {
> + puts("Failed to write virtio-pci status");
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +static int virtio_pci_get_status(VDev *vdev, uint8_t *status)
> +{
> + int rc = pci_read_byte(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_STATUS,
> + c_cap.bar, status);
> + if (rc) {
> + puts("Failed to read virtio-pci status");
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * Find the position of the capability config within PCI configuration
> + * space for a given cfg type. Return the position if found, otherwise 0.
> + */
> +static uint8_t find_cap_pos(uint32_t fhandle, uint8_t cfg_type)
> +{
> + uint8_t next, cfg;
> + int rc;
> +
> + rc = pci_read_byte(fhandle, PCI_CAPABILITY_LIST, PCI_CFGBAR, &next);
> + rc |= pci_read_byte(fhandle, next + 3, PCI_CFGBAR, &cfg);
> +
> + while (!rc && (cfg != cfg_type) && next) {
> + rc = pci_read_byte(fhandle, next + 1, PCI_CFGBAR, &next);
> + rc |= pci_read_byte(fhandle, next + 3, PCI_CFGBAR, &cfg);
> + }
> +
> + return rc ? 0 : next;
> +}
> +
> +static int virtio_pci_get_hfeatures(VDev *vdev, uint64_t *features)
> +{
> + uint32_t feat0, feat1;
> + int rc;
> +
> + rc = pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_DFSELECT,
> + c_cap.bar, 0);
> +
> + rc |= pci_read_bswap32(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_DF,
> + c_cap.bar, &feat0);
> +
> + rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_DFSELECT,
> + c_cap.bar, 1);
> +
> + rc |= pci_read_bswap32(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_DF,
> + c_cap.bar, &feat1);
> +
> + if (rc) {
> + puts("Failed to get PCI feature bits");
> + return -EIO;
> + }
> +
> + *features = (uint64_t) feat1 << 32;
> + *features |= (uint64_t) feat0;
> +
> + return 0;
> +}
> +
> +static int virtio_pci_set_gfeatures(VDev *vdev)
> +{
> + int rc;
> +
> + rc = pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_GFSELECT,
> + c_cap.bar, 0);
> +
> + rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_GF,
> + c_cap.bar, vdev->guest_features[1]);
> +
> + rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_GFSELECT,
> + c_cap.bar, 1);
> +
> + rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_GF,
> + c_cap.bar, vdev->guest_features[0]);
> +
> + if (rc) {
> + puts("Failed to set PCI feature bits");
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +static int virtio_pci_get_blk_config(VDev *vdev)
> +{
> + return pci_read_flex(vdev->pci_fh, d_cap.off, d_cap.bar, &vdev->config.blk,
> + sizeof(VirtioBlkConfig));
I'm a little bit surprised that there does not seem to be any endianess
swapping for the config.blk data anywhere here ... isn't that config space
data supposed to be in little endian?
... oh, wait, you're not negotiating VIRTIO_F_VERSION_1, are you? ... so the
config space is still in big endian for legacy virtio? ... hmm, I guess it's
ok for now, but in the long run, I think we should rather use VERSION_1 instead.
> +}
> +
> +/* virtio spec v1.3 section 4.1.2.1 */
> +void virtio_pci_id2type(VDev *vdev, uint16_t device_id)
> +{
> + switch (device_id) {
> + case 0x1001:
> + vdev->dev_type = VIRTIO_ID_BLOCK;
> + break;
> + case 0x1000: /* Other valid but currently unsupported virtio device types */
> + case 0x1004:
> + default:
> + vdev->dev_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;
> + int rc;
> +
> + /* Common capabilities */
> + pos = find_cap_pos(vdev->pci_fh, VPCI_CAP_COMMON_CFG);
> + if (!pos) {
> + puts("Failed to locate PCI common configuration");
> + return 1;
> + }
> +
> + rc = pci_read_byte(vdev->pci_fh, pos + VPCI_CAP_BAR, PCI_CFGBAR, &c_cap.bar);
> + if (rc || pci_read_bswap32(vdev->pci_fh, pos + VPCI_CAP_OFFSET, PCI_CFGBAR,
> + &c_cap.off)) {
> + puts("Failed to read PCI common configuration");
> + return -EIO;
> + }
> +
> + /* Device capabilities */
> + pos = find_cap_pos(vdev->pci_fh, VPCI_CAP_DEVICE_CFG);
> + if (!pos) {
> + puts("Failed to locate PCI device configuration");
> + return 1;
> + }
> +
> + rc = pci_read_byte(vdev->pci_fh, pos + VPCI_CAP_BAR, PCI_CFGBAR, &d_cap.bar);
> + if (rc || pci_read_bswap32(vdev->pci_fh, pos + VPCI_CAP_OFFSET, PCI_CFGBAR,
> + &d_cap.off)) {
> + puts("Failed to read PCI device configuration");
> + return -EIO;
> + }
> +
> + /* Notification capabilities */
> + pos = find_cap_pos(vdev->pci_fh, VPCI_CAP_NOTIFY_CFG);
> + if (!pos) {
> + puts("Failed to locate PCI notification configuration");
> + return 1;
> + }
> +
> + rc = pci_read_byte(vdev->pci_fh, pos + VPCI_CAP_BAR, PCI_CFGBAR, &n_cap.bar);
> + if (rc || pci_read_bswap32(vdev->pci_fh, pos + VPCI_CAP_OFFSET, PCI_CFGBAR,
> + &n_cap.off)) {
> + puts("Failed to read PCI notification configuration");
> + return -EIO;
> + }
> +
> + rc = pci_read_bswap32(vdev->pci_fh, pos + VPCI_N_CAP_MULT, PCI_CFGBAR, ¬ify_mult);
> + if (rc || pci_read_bswap16(vdev->pci_fh, d_cap.off + VPCI_C_OFFSET_Q_NOFF,
> + d_cap.bar, &q_notify_offset)) {
> + puts("Failed to read notification queue configuration");
> + return -EIO;
> + }
> +
> + 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;
> + }
> +
> + return 0;
> +}
> +
> +int virtio_pci_set_selected_vq(VDev *vdev, uint16_t queue_num)
> +{
> + return pci_bswap16_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_Q_SELECT,
> + c_cap.bar, queue_num);
> +}
> +
> +int virtio_pci_set_queue_size(VDev *vdev, uint16_t queue_size)
> +{
> + return pci_bswap16_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_Q_SIZE,
> + c_cap.bar, queue_size);
> +}
> +
> +static int virtio_pci_set_queue_enable(VDev *vdev, uint16_t enabled)
> +{
> + return pci_bswap16_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_Q_ENABLE,
> + c_cap.bar, enabled);
> +}
> +
> +static int set_pci_vq_addr(VDev *vdev, uint64_t config_off, void *addr)
> +{
> + return pci_bswap64_write(vdev->pci_fh, c_cap.off + config_off, c_cap.bar,
> + (uint64_t) addr);
> +}
> +
> +int virtio_pci_setup(VDev *vdev)
> +{
> + VRing *vr;
> + int rc;
> + uint64_t pci_features;
> + uint8_t status;
> + int i = 0;
> +
> + vdev->config.blk.blk_size = 0;
For really getting blk_size, I think you should negotiate
VIRTIO_BLK_F_BLK_SIZE ?
Also, I assume the generic part of this function should rather be agnostic
of virtio-block specific settings instead, so it can be used for
virtio-net-pci and virtio-scsi-pci later? So the above line should likely
rather go into the "case VIRTIO_ID_BLOCK" part below?
> + vdev->guessed_disk_nature = VIRTIO_GDN_NONE;
> + vdev->cmd_vr_idx = 0;
> +
> + if (virtio_reset(vdev)) {
> + return -EIO;
> + }
> +
> + status = VPCI_S_ACKNOWLEDGE;
> + rc = virtio_pci_set_status(vdev, status);
> + if (rc) {
> + puts("Virtio-pci device Failed to ACKNOWLEDGE");
> + return -EIO;
> + }
> +
> + rc = virtio_pci_read_pci_cap_config(vdev);
> + if (rc) {
> + printf("Invalid PCI capabilities");
> + return -EIO;
> + }
> +
> + switch (vdev->dev_type) {
> + case VIRTIO_ID_BLOCK:
> + vdev->nr_vqs = 1;
> + vdev->cmd_vr_idx = 0;
> + virtio_pci_get_blk_config(vdev);
> + break;
> + default:
> + puts("Unsupported virtio device");
> + return -ENODEV;
> + }
> +
> + status |= VPCI_S_DRIVER;
> + rc = virtio_pci_set_status(vdev, status);
> + if (rc) {
> + puts("Set status failed");
> + return -EIO;
> + }
> +
> + /* Feature negotiation */
> + rc = virtio_pci_get_hfeatures(vdev, &pci_features);
> + if (rc || virtio_pci_set_gfeatures(vdev)) {
> + 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 * VIRTIO_RING_SIZE),
> + .align = KVM_S390_VIRTIO_RING_ALIGN,
> + .index = i,
> + .num = 0,
> + };
> +
> + vr = &vdev->vrings[i];
> +
> + if (pci_read_flex(vdev->pci_fh, VPCI_C_COMMON_NUMQ, c_cap.bar, &info.num, 2)) {
> + return -EIO;
> + }
> +
> + vring_init(vr, &info);
> +
> + if (virtio_pci_set_selected_vq(vdev, vr->id)) {
> + puts("Failed to set selected virt-queue");
> + return -EIO;
> + }
> +
> + if (virtio_pci_set_queue_size(vdev, VIRTIO_RING_SIZE)) {
> + puts("Failed to set virt-queue size");
> + return -EIO;
> + }
> +
> + rc = set_pci_vq_addr(vdev, VPCI_C_OFFSET_Q_DESCLO, vr->desc);
> + rc |= set_pci_vq_addr(vdev, VPCI_C_OFFSET_Q_AVAILLO, vr->avail);
> + rc |= set_pci_vq_addr(vdev, VPCI_C_OFFSET_Q_USEDLO, vr->used);
> + if (rc) {
> + puts("Failed to configure virt-queue address");
> + return -EIO;
> + }
> +
> + if (virtio_pci_set_queue_enable(vdev, true)) {
> + puts("Failed to set virt-queue enabled");
> + return -EIO;
> + }
> + }
> +
> + status |= VPCI_S_FEATURES_OK | VPCI_S_DRIVER_OK;
> + return virtio_pci_set_status(vdev, status);
> +}
Thomas
^ permalink raw reply [flat|nested] 36+ messages in thread* Re: [PATCH 07/10] pc-bios/s390-ccw: Add support for virtio-blk-pci IPL
2026-01-07 14:44 ` Thomas Huth
@ 2026-01-07 16:08 ` Thomas Huth
2026-01-07 18:54 ` Jared Rossi
2026-01-07 18:32 ` Jared Rossi
1 sibling, 1 reply; 36+ messages in thread
From: Thomas Huth @ 2026-01-07 16:08 UTC (permalink / raw)
To: jrossi, qemu-devel, qemu-s390x, mst
Cc: jjherne, alifm, farman, mjrosato, zycai
On 07/01/2026 15.44, Thomas Huth wrote:
> On 10/12/2025 21.54, jrossi@linux.ibm.com wrote:
>> From: Jared Rossi <jrossi@linux.ibm.com>
>>
>> Enable virt-queue PCI configuration and add routines for virtio-blk-pci
>> devices.
>>
>> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
>> ---
...
>> +static int virtio_pci_set_gfeatures(VDev *vdev)
>> +{
>> + int rc;
>> +
>> + rc = pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_GFSELECT,
>> + c_cap.bar, 0);
>> +
>> + rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_GF,
>> + c_cap.bar, vdev->guest_features[1]);
>> +
>> + rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off +
>> VPCI_C_OFFSET_GFSELECT,
>> + c_cap.bar, 1);
>> +
>> + rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_GF,
>> + c_cap.bar, vdev->guest_features[0]);
>> +
>> + if (rc) {
>> + puts("Failed to set PCI feature bits");
>> + return -EIO;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int virtio_pci_get_blk_config(VDev *vdev)
>> +{
>> + return pci_read_flex(vdev->pci_fh, d_cap.off, d_cap.bar, &vdev-
>> >config.blk,
>> + sizeof(VirtioBlkConfig));
>
> I'm a little bit surprised that there does not seem to be any endianess
> swapping for the config.blk data anywhere here ... isn't that config space
> data supposed to be in little endian?
>
> ... oh, wait, you're not negotiating VIRTIO_F_VERSION_1, are you? ... so the
> config space is still in big endian for legacy virtio? ... hmm, I guess it's
> ok for now, but in the long run, I think we should rather use VERSION_1
> instead.
Thinking about this twice, could you please have a try to use
VIRTIO_F_VERSION_1 right from the start? I already heard from some people
that they'd rather want to get rid of legacy virtio in QEMU (e.g. for the
universal binary project that includes multiple targets in a single QEMU
binary), so I think it would be safer to immediately go with VERSION_1 here.
Thanks,
Thomas
^ permalink raw reply [flat|nested] 36+ messages in thread* Re: [PATCH 07/10] pc-bios/s390-ccw: Add support for virtio-blk-pci IPL
2026-01-07 16:08 ` Thomas Huth
@ 2026-01-07 18:54 ` Jared Rossi
0 siblings, 0 replies; 36+ messages in thread
From: Jared Rossi @ 2026-01-07 18:54 UTC (permalink / raw)
To: Thomas Huth, qemu-devel, qemu-s390x, mst
Cc: jjherne, alifm, farman, mjrosato, zycai
On 1/7/26 11:08 AM, Thomas Huth wrote:
> On 07/01/2026 15.44, Thomas Huth wrote:
>> On 10/12/2025 21.54, jrossi@linux.ibm.com wrote:
>>> From: Jared Rossi <jrossi@linux.ibm.com>
>>>
>>> Enable virt-queue PCI configuration and add routines for
>>> virtio-blk-pci devices.
>>>
>>> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
>>> ---
> ...
>>> +static int virtio_pci_set_gfeatures(VDev *vdev)
>>> +{
>>> + int rc;
>>> +
>>> + rc = pci_bswap32_write(vdev->pci_fh, c_cap.off +
>>> VPCI_C_OFFSET_GFSELECT,
>>> + c_cap.bar, 0);
>>> +
>>> + rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off +
>>> VPCI_C_OFFSET_GF,
>>> + c_cap.bar, vdev->guest_features[1]);
>>> +
>>> + rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off +
>>> VPCI_C_OFFSET_GFSELECT,
>>> + c_cap.bar, 1);
>>> +
>>> + rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off +
>>> VPCI_C_OFFSET_GF,
>>> + c_cap.bar, vdev->guest_features[0]);
>>> +
>>> + if (rc) {
>>> + puts("Failed to set PCI feature bits");
>>> + return -EIO;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int virtio_pci_get_blk_config(VDev *vdev)
>>> +{
>>> + return pci_read_flex(vdev->pci_fh, d_cap.off, d_cap.bar, &vdev-
>>> >config.blk,
>>> + sizeof(VirtioBlkConfig));
>>
>> I'm a little bit surprised that there does not seem to be any
>> endianess swapping for the config.blk data anywhere here ... isn't
>> that config space data supposed to be in little endian?
>>
>> ... oh, wait, you're not negotiating VIRTIO_F_VERSION_1, are you? ...
>> so the config space is still in big endian for legacy virtio? ...
>> hmm, I guess it's ok for now, but in the long run, I think we should
>> rather use VERSION_1 instead.
> Thinking about this twice, could you please have a try to use
> VIRTIO_F_VERSION_1 right from the start? I already heard from some
> people that they'd rather want to get rid of legacy virtio in QEMU
> (e.g. for the universal binary project that includes multiple targets
> in a single QEMU binary), so I think it would be safer to immediately
> go with VERSION_1 here.
>
> Thanks,
> Thomas
>
Hi Thomas,
I touched on this in reply to your other message, but yes, I agree that
it is better to do all of these negotiations in the most compatible way
right from the start. I will fix the feature negotiations and try to
avoid using anything legacy.
Thanks again for your reviews,
Jared Rossi
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH 07/10] pc-bios/s390-ccw: Add support for virtio-blk-pci IPL
2026-01-07 14:44 ` Thomas Huth
2026-01-07 16:08 ` Thomas Huth
@ 2026-01-07 18:32 ` Jared Rossi
2026-01-09 11:32 ` Thomas Huth
1 sibling, 1 reply; 36+ messages in thread
From: Jared Rossi @ 2026-01-07 18:32 UTC (permalink / raw)
To: Thomas Huth, qemu-devel, qemu-s390x, mst
Cc: jjherne, alifm, farman, mjrosato, zycai
On 1/7/26 9:44 AM, Thomas Huth wrote:
> On 10/12/2025 21.54, jrossi@linux.ibm.com wrote:
>> From: Jared Rossi <jrossi@linux.ibm.com>
>>
>> Enable virt-queue PCI configuration and add routines for
>> virtio-blk-pci devices.
>>
>> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
>> ---
>> include/hw/s390x/ipl/qipl.h | 10 +
>> pc-bios/s390-ccw/virtio-pci.h | 79 +++++++
>> pc-bios/s390-ccw/virtio.h | 1 +
>> pc-bios/s390-ccw/main.c | 60 +++++-
>> pc-bios/s390-ccw/virtio-blkdev.c | 3 +
>> pc-bios/s390-ccw/virtio-pci.c | 360 +++++++++++++++++++++++++++++++
>> pc-bios/s390-ccw/virtio.c | 5 +
>> pc-bios/s390-ccw/Makefile | 2 +-
>> 8 files changed, 517 insertions(+), 3 deletions(-)
>> create mode 100644 pc-bios/s390-ccw/virtio-pci.h
>> create mode 100644 pc-bios/s390-ccw/virtio-pci.c
>>
>> diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
>> index 8199b839f0..5c7779a1c3 100644
>> --- a/include/hw/s390x/ipl/qipl.h
>> +++ b/include/hw/s390x/ipl/qipl.h
>> @@ -22,6 +22,7 @@
>> #define S390_IPL_TYPE_FCP 0x00
>> #define S390_IPL_TYPE_CCW 0x02
>> +#define S390_IPL_TYPE_PCI 0x04
>> #define S390_IPL_TYPE_PV 0x05
>> #define S390_IPL_TYPE_QEMU_SCSI 0xff
>> @@ -105,6 +106,14 @@ struct IplBlockQemuScsi {
>> } QEMU_PACKED;
>> typedef struct IplBlockQemuScsi IplBlockQemuScsi;
>> +struct IplBlockPci {
>> + uint32_t reserved0[80];
>> + uint8_t opt;
>> + uint8_t reserved1[3];
>> + uint32_t fid;
>> +} QEMU_PACKED;
>> +typedef struct IplBlockPci IplBlockPci;
>
> Is this supposed to have the same positions for "fid" and "opt" as in
> the ipl_pb0_nvme structure in the kernel
> (arch/s390/include/uapi/asm/ipl.h)?
Yes, I think. That was my intention anyway. Are you suggesting it
shouldn't?
>
>> union IplParameterBlock {
>> struct {
>> uint32_t len;
>> @@ -120,6 +129,7 @@ union IplParameterBlock {
>> IplBlockFcp fcp;
>> IPLBlockPV pv;
>> IplBlockQemuScsi scsi;
>> + IplBlockPci pci;
>> };
>> } QEMU_PACKED;
>> struct {
> ...
>> diff --git a/pc-bios/s390-ccw/virtio-pci.c
>> b/pc-bios/s390-ccw/virtio-pci.c
>> new file mode 100644
>> index 0000000000..838231d86c
>> --- /dev/null
>> +++ b/pc-bios/s390-ccw/virtio-pci.c
>> @@ -0,0 +1,360 @@
>> +/*
>> + * Functionality for virtio-pci
>> + *
>> + * Copyright 2025 IBM Corp.
>> + * Author(s): Jared Rossi <jrossi@linux.ibm.com>
>> + *
>> + * 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 regions */
>> +VirtioPciCap c_cap; /* Common capabilities */
>> +VirtioPciCap d_cap; /* Device capabilities */
>> +VirtioPciCap n_cap; /* Notify capabilities */
>> +uint32_t notify_mult;
>> +uint16_t q_notify_offset;
>> +
>> +static int virtio_pci_set_status(VDev *vdev, uint8_t status)
>> +{
>> + int rc = pci_write_byte(vdev->pci_fh, c_cap.off +
>> VPCI_C_OFFSET_STATUS,
>> + c_cap.bar, status);
>> + if (rc) {
>> + puts("Failed to write virtio-pci status");
>> + return -EIO;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int virtio_pci_get_status(VDev *vdev, uint8_t *status)
>> +{
>> + int rc = pci_read_byte(vdev->pci_fh, c_cap.off +
>> VPCI_C_OFFSET_STATUS,
>> + c_cap.bar, status);
>> + if (rc) {
>> + puts("Failed to read virtio-pci status");
>> + return -EIO;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +/*
>> + * Find the position of the capability config within PCI configuration
>> + * space for a given cfg type. Return the position if found,
>> otherwise 0.
>> + */
>> +static uint8_t find_cap_pos(uint32_t fhandle, uint8_t cfg_type)
>> +{
>> + uint8_t next, cfg;
>> + int rc;
>> +
>> + rc = pci_read_byte(fhandle, PCI_CAPABILITY_LIST, PCI_CFGBAR,
>> &next);
>> + rc |= pci_read_byte(fhandle, next + 3, PCI_CFGBAR, &cfg);
>> +
>> + while (!rc && (cfg != cfg_type) && next) {
>> + rc = pci_read_byte(fhandle, next + 1, PCI_CFGBAR, &next);
>> + rc |= pci_read_byte(fhandle, next + 3, PCI_CFGBAR, &cfg);
>> + }
>> +
>> + return rc ? 0 : next;
>> +}
>> +
>> +static int virtio_pci_get_hfeatures(VDev *vdev, uint64_t *features)
>> +{
>> + uint32_t feat0, feat1;
>> + int rc;
>> +
>> + rc = pci_bswap32_write(vdev->pci_fh, c_cap.off +
>> VPCI_C_OFFSET_DFSELECT,
>> + c_cap.bar, 0);
>> +
>> + rc |= pci_read_bswap32(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_DF,
>> + c_cap.bar, &feat0);
>> +
>> + rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off +
>> VPCI_C_OFFSET_DFSELECT,
>> + c_cap.bar, 1);
>> +
>> + rc |= pci_read_bswap32(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_DF,
>> + c_cap.bar, &feat1);
>> +
>> + if (rc) {
>> + puts("Failed to get PCI feature bits");
>> + return -EIO;
>> + }
>> +
>> + *features = (uint64_t) feat1 << 32;
>> + *features |= (uint64_t) feat0;
>> +
>> + return 0;
>> +}
>> +
>> +static int virtio_pci_set_gfeatures(VDev *vdev)
>> +{
>> + int rc;
>> +
>> + rc = pci_bswap32_write(vdev->pci_fh, c_cap.off +
>> VPCI_C_OFFSET_GFSELECT,
>> + c_cap.bar, 0);
>> +
>> + rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_GF,
>> + c_cap.bar, vdev->guest_features[1]);
>> +
>> + rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off +
>> VPCI_C_OFFSET_GFSELECT,
>> + c_cap.bar, 1);
>> +
>> + rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_GF,
>> + c_cap.bar, vdev->guest_features[0]);
>> +
>> + if (rc) {
>> + puts("Failed to set PCI feature bits");
>> + return -EIO;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int virtio_pci_get_blk_config(VDev *vdev)
>> +{
>> + return pci_read_flex(vdev->pci_fh, d_cap.off, d_cap.bar,
>> &vdev->config.blk,
>> + sizeof(VirtioBlkConfig));
>
> I'm a little bit surprised that there does not seem to be any
> endianess swapping for the config.blk data anywhere here ... isn't
> that config space data supposed to be in little endian?
>
> ... oh, wait, you're not negotiating VIRTIO_F_VERSION_1, are you? ...
> so the config space is still in big endian for legacy virtio? ... hmm,
> I guess it's ok for now, but in the long run, I think we should rather
> use VERSION_1 instead.
I will figure out how to fix the negotiations, both for this and
VIRTIO_BLK_F_BLK_SIZE as you mention below.
>
>> +}
>> +
>> +/* virtio spec v1.3 section 4.1.2.1 */
>> +void virtio_pci_id2type(VDev *vdev, uint16_t device_id)
>> +{
>> + switch (device_id) {
>> + case 0x1001:
>> + vdev->dev_type = VIRTIO_ID_BLOCK;
>> + break;
>> + case 0x1000: /* Other valid but currently unsupported virtio
>> device types */
>> + case 0x1004:
>> + default:
>> + vdev->dev_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;
>> + int rc;
>> +
>> + /* Common capabilities */
>> + pos = find_cap_pos(vdev->pci_fh, VPCI_CAP_COMMON_CFG);
>> + if (!pos) {
>> + puts("Failed to locate PCI common configuration");
>> + return 1;
>> + }
>> +
>> + rc = pci_read_byte(vdev->pci_fh, pos + VPCI_CAP_BAR, PCI_CFGBAR,
>> &c_cap.bar);
>> + if (rc || pci_read_bswap32(vdev->pci_fh, pos + VPCI_CAP_OFFSET,
>> PCI_CFGBAR,
>> + &c_cap.off)) {
>> + puts("Failed to read PCI common configuration");
>> + return -EIO;
>> + }
>> +
>> + /* Device capabilities */
>> + pos = find_cap_pos(vdev->pci_fh, VPCI_CAP_DEVICE_CFG);
>> + if (!pos) {
>> + puts("Failed to locate PCI device configuration");
>> + return 1;
>> + }
>> +
>> + rc = pci_read_byte(vdev->pci_fh, pos + VPCI_CAP_BAR, PCI_CFGBAR,
>> &d_cap.bar);
>> + if (rc || pci_read_bswap32(vdev->pci_fh, pos + VPCI_CAP_OFFSET,
>> PCI_CFGBAR,
>> + &d_cap.off)) {
>> + puts("Failed to read PCI device configuration");
>> + return -EIO;
>> + }
>> +
>> + /* Notification capabilities */
>> + pos = find_cap_pos(vdev->pci_fh, VPCI_CAP_NOTIFY_CFG);
>> + if (!pos) {
>> + puts("Failed to locate PCI notification configuration");
>> + return 1;
>> + }
>> +
>> + rc = pci_read_byte(vdev->pci_fh, pos + VPCI_CAP_BAR, PCI_CFGBAR,
>> &n_cap.bar);
>> + if (rc || pci_read_bswap32(vdev->pci_fh, pos + VPCI_CAP_OFFSET,
>> PCI_CFGBAR,
>> + &n_cap.off)) {
>> + puts("Failed to read PCI notification configuration");
>> + return -EIO;
>> + }
>> +
>> + rc = pci_read_bswap32(vdev->pci_fh, pos + VPCI_N_CAP_MULT,
>> PCI_CFGBAR, ¬ify_mult);
>> + if (rc || pci_read_bswap16(vdev->pci_fh, d_cap.off +
>> VPCI_C_OFFSET_Q_NOFF,
>> + d_cap.bar, &q_notify_offset)) {
>> + puts("Failed to read notification queue configuration");
>> + return -EIO;
>> + }
>> +
>> + 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;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +int virtio_pci_set_selected_vq(VDev *vdev, uint16_t queue_num)
>> +{
>> + return pci_bswap16_write(vdev->pci_fh, c_cap.off +
>> VPCI_C_OFFSET_Q_SELECT,
>> + c_cap.bar, queue_num);
>> +}
>> +
>> +int virtio_pci_set_queue_size(VDev *vdev, uint16_t queue_size)
>> +{
>> + return pci_bswap16_write(vdev->pci_fh, c_cap.off +
>> VPCI_C_OFFSET_Q_SIZE,
>> + c_cap.bar, queue_size);
>> +}
>> +
>> +static int virtio_pci_set_queue_enable(VDev *vdev, uint16_t enabled)
>> +{
>> + return pci_bswap16_write(vdev->pci_fh, c_cap.off +
>> VPCI_C_OFFSET_Q_ENABLE,
>> + c_cap.bar, enabled);
>> +}
>> +
>> +static int set_pci_vq_addr(VDev *vdev, uint64_t config_off, void *addr)
>> +{
>> + return pci_bswap64_write(vdev->pci_fh, c_cap.off + config_off,
>> c_cap.bar,
>> + (uint64_t) addr);
>> +}
>> +
>> +int virtio_pci_setup(VDev *vdev)
>> +{
>> + VRing *vr;
>> + int rc;
>> + uint64_t pci_features;
>> + uint8_t status;
>> + int i = 0;
>> +
>> + vdev->config.blk.blk_size = 0;
>
> For really getting blk_size, I think you should negotiate
> VIRTIO_BLK_F_BLK_SIZE ?
>
> Also, I assume the generic part of this function should rather be
> agnostic of virtio-block specific settings instead, so it can be used
> for virtio-net-pci and virtio-scsi-pci later? So the above line should
> likely rather go into the "case VIRTIO_ID_BLOCK" part below?
In the existing virtio_ccw_setup code, config.blk.blk_size is
initialized to 0 for all device types like this, which is why I did it
that way here too.
Conceptually, I agree that it makes more sense to do it only for actual
block devices that care about it. I'll check if there is any reason why
it was done this way in the CCW code to begin with, but probably it can
be moved it into the VIRTIO_ID_BLOCK case.
>
>> + vdev->guessed_disk_nature = VIRTIO_GDN_NONE;
>> + vdev->cmd_vr_idx = 0;
>> +
>> + if (virtio_reset(vdev)) {
>> + return -EIO;
>> + }
>> +
>> + status = VPCI_S_ACKNOWLEDGE;
>> + rc = virtio_pci_set_status(vdev, status);
>> + if (rc) {
>> + puts("Virtio-pci device Failed to ACKNOWLEDGE");
>> + return -EIO;
>> + }
>> +
>> + rc = virtio_pci_read_pci_cap_config(vdev);
>> + if (rc) {
>> + printf("Invalid PCI capabilities");
>> + return -EIO;
>> + }
>> +
>> + switch (vdev->dev_type) {
>> + case VIRTIO_ID_BLOCK:
>> + vdev->nr_vqs = 1;
>> + vdev->cmd_vr_idx = 0;
>> + virtio_pci_get_blk_config(vdev);
>> + break;
>> + default:
>> + puts("Unsupported virtio device");
>> + return -ENODEV;
>> + }
>> +
>> + status |= VPCI_S_DRIVER;
>> + rc = virtio_pci_set_status(vdev, status);
>> + if (rc) {
>> + puts("Set status failed");
>> + return -EIO;
>> + }
>> +
>> + /* Feature negotiation */
>> + rc = virtio_pci_get_hfeatures(vdev, &pci_features);
>> + if (rc || virtio_pci_set_gfeatures(vdev)) {
>> + 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 * VIRTIO_RING_SIZE),
>> + .align = KVM_S390_VIRTIO_RING_ALIGN,
>> + .index = i,
>> + .num = 0,
>> + };
>> +
>> + vr = &vdev->vrings[i];
>> +
>> + if (pci_read_flex(vdev->pci_fh, VPCI_C_COMMON_NUMQ,
>> c_cap.bar, &info.num, 2)) {
>> + return -EIO;
>> + }
>> +
>> + vring_init(vr, &info);
>> +
>> + if (virtio_pci_set_selected_vq(vdev, vr->id)) {
>> + puts("Failed to set selected virt-queue");
>> + return -EIO;
>> + }
>> +
>> + if (virtio_pci_set_queue_size(vdev, VIRTIO_RING_SIZE)) {
>> + puts("Failed to set virt-queue size");
>> + return -EIO;
>> + }
>> +
>> + rc = set_pci_vq_addr(vdev, VPCI_C_OFFSET_Q_DESCLO, vr->desc);
>> + rc |= set_pci_vq_addr(vdev, VPCI_C_OFFSET_Q_AVAILLO,
>> vr->avail);
>> + rc |= set_pci_vq_addr(vdev, VPCI_C_OFFSET_Q_USEDLO, vr->used);
>> + if (rc) {
>> + puts("Failed to configure virt-queue address");
>> + return -EIO;
>> + }
>> +
>> + if (virtio_pci_set_queue_enable(vdev, true)) {
>> + puts("Failed to set virt-queue enabled");
>> + return -EIO;
>> + }
>> + }
>> +
>> + status |= VPCI_S_FEATURES_OK | VPCI_S_DRIVER_OK;
>> + return virtio_pci_set_status(vdev, status);
>> +}
>
> Thomas
>
>
^ permalink raw reply [flat|nested] 36+ messages in thread* Re: [PATCH 07/10] pc-bios/s390-ccw: Add support for virtio-blk-pci IPL
2026-01-07 18:32 ` Jared Rossi
@ 2026-01-09 11:32 ` Thomas Huth
2026-01-09 15:16 ` Jared Rossi
0 siblings, 1 reply; 36+ messages in thread
From: Thomas Huth @ 2026-01-09 11:32 UTC (permalink / raw)
To: Jared Rossi, qemu-devel, qemu-s390x, mst
Cc: jjherne, alifm, farman, mjrosato, zycai
On 07/01/2026 19.32, Jared Rossi wrote:
>
>
> On 1/7/26 9:44 AM, Thomas Huth wrote:
>> On 10/12/2025 21.54, jrossi@linux.ibm.com wrote:
>>> From: Jared Rossi <jrossi@linux.ibm.com>
>>>
>>> Enable virt-queue PCI configuration and add routines for virtio-blk-pci
>>> devices.
>>>
>>> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
>>> ---
>>> include/hw/s390x/ipl/qipl.h | 10 +
>>> pc-bios/s390-ccw/virtio-pci.h | 79 +++++++
>>> pc-bios/s390-ccw/virtio.h | 1 +
>>> pc-bios/s390-ccw/main.c | 60 +++++-
>>> pc-bios/s390-ccw/virtio-blkdev.c | 3 +
>>> pc-bios/s390-ccw/virtio-pci.c | 360 +++++++++++++++++++++++++++++++
>>> pc-bios/s390-ccw/virtio.c | 5 +
>>> pc-bios/s390-ccw/Makefile | 2 +-
>>> 8 files changed, 517 insertions(+), 3 deletions(-)
>>> create mode 100644 pc-bios/s390-ccw/virtio-pci.h
>>> create mode 100644 pc-bios/s390-ccw/virtio-pci.c
>>>
>>> diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
>>> index 8199b839f0..5c7779a1c3 100644
>>> --- a/include/hw/s390x/ipl/qipl.h
>>> +++ b/include/hw/s390x/ipl/qipl.h
>>> @@ -22,6 +22,7 @@
>>> #define S390_IPL_TYPE_FCP 0x00
>>> #define S390_IPL_TYPE_CCW 0x02
>>> +#define S390_IPL_TYPE_PCI 0x04
>>> #define S390_IPL_TYPE_PV 0x05
>>> #define S390_IPL_TYPE_QEMU_SCSI 0xff
>>> @@ -105,6 +106,14 @@ struct IplBlockQemuScsi {
>>> } QEMU_PACKED;
>>> typedef struct IplBlockQemuScsi IplBlockQemuScsi;
>>> +struct IplBlockPci {
>>> + uint32_t reserved0[80];
>>> + uint8_t opt;
>>> + uint8_t reserved1[3];
>>> + uint32_t fid;
>>> +} QEMU_PACKED;
>>> +typedef struct IplBlockPci IplBlockPci;
>>
>> Is this supposed to have the same positions for "fid" and "opt" as in the
>> ipl_pb0_nvme structure in the kernel (arch/s390/include/uapi/asm/ipl.h)?
>
> Yes, I think. That was my intention anyway. Are you suggesting it shouldn't?
Having the same positions sound like a good idea. But if I'm counting right,
it currently does not match:
In the code above, there are 80 * 4 = 320 bytes between the end of the
loadparm[] array and the "opt" field.
In the kernel, there is the reserved2 field with 304 bytes between the end
of the loadparm[] array and the "opt" field.
So either I'm counting wrong, or your reserved0 field should be decreased in
size?
Thomas
^ permalink raw reply [flat|nested] 36+ messages in thread* Re: [PATCH 07/10] pc-bios/s390-ccw: Add support for virtio-blk-pci IPL
2026-01-09 11:32 ` Thomas Huth
@ 2026-01-09 15:16 ` Jared Rossi
0 siblings, 0 replies; 36+ messages in thread
From: Jared Rossi @ 2026-01-09 15:16 UTC (permalink / raw)
To: Thomas Huth, qemu-devel, qemu-s390x, mst
Cc: jjherne, alifm, farman, mjrosato, zycai
On 1/9/26 6:32 AM, Thomas Huth wrote:
> On 07/01/2026 19.32, Jared Rossi wrote:
>>
>>
>> On 1/7/26 9:44 AM, Thomas Huth wrote:
>>> On 10/12/2025 21.54, jrossi@linux.ibm.com wrote:
>>>> From: Jared Rossi <jrossi@linux.ibm.com>
>>>>
>>>> Enable virt-queue PCI configuration and add routines for
>>>> virtio-blk-pci devices.
>>>>
>>>> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
>>>> ---
>>>> include/hw/s390x/ipl/qipl.h | 10 +
>>>> pc-bios/s390-ccw/virtio-pci.h | 79 +++++++
>>>> pc-bios/s390-ccw/virtio.h | 1 +
>>>> pc-bios/s390-ccw/main.c | 60 +++++-
>>>> pc-bios/s390-ccw/virtio-blkdev.c | 3 +
>>>> pc-bios/s390-ccw/virtio-pci.c | 360
>>>> +++++++++++++++++++++++++++++++
>>>> pc-bios/s390-ccw/virtio.c | 5 +
>>>> pc-bios/s390-ccw/Makefile | 2 +-
>>>> 8 files changed, 517 insertions(+), 3 deletions(-)
>>>> create mode 100644 pc-bios/s390-ccw/virtio-pci.h
>>>> create mode 100644 pc-bios/s390-ccw/virtio-pci.c
>>>>
>>>> diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
>>>> index 8199b839f0..5c7779a1c3 100644
>>>> --- a/include/hw/s390x/ipl/qipl.h
>>>> +++ b/include/hw/s390x/ipl/qipl.h
>>>> @@ -22,6 +22,7 @@
>>>> #define S390_IPL_TYPE_FCP 0x00
>>>> #define S390_IPL_TYPE_CCW 0x02
>>>> +#define S390_IPL_TYPE_PCI 0x04
>>>> #define S390_IPL_TYPE_PV 0x05
>>>> #define S390_IPL_TYPE_QEMU_SCSI 0xff
>>>> @@ -105,6 +106,14 @@ struct IplBlockQemuScsi {
>>>> } QEMU_PACKED;
>>>> typedef struct IplBlockQemuScsi IplBlockQemuScsi;
>>>> +struct IplBlockPci {
>>>> + uint32_t reserved0[80];
>>>> + uint8_t opt;
>>>> + uint8_t reserved1[3];
>>>> + uint32_t fid;
>>>> +} QEMU_PACKED;
>>>> +typedef struct IplBlockPci IplBlockPci;
>>>
>>> Is this supposed to have the same positions for "fid" and "opt" as
>>> in the ipl_pb0_nvme structure in the kernel
>>> (arch/s390/include/uapi/asm/ipl.h)?
>>
>> Yes, I think. That was my intention anyway. Are you suggesting it
>> shouldn't?
>
> Having the same positions sound like a good idea. But if I'm counting
> right, it currently does not match:
>
> In the code above, there are 80 * 4 = 320 bytes between the end of the
> loadparm[] array and the "opt" field.
>
> In the kernel, there is the reserved2 field with 304 bytes between the
> end of the loadparm[] array and the "opt" field.
>
> So either I'm counting wrong, or your reserved0 field should be
> decreased in size?
>
> Thomas
>
Ah, now I see. Your are correct. I will fix it. Thanks for catching that!
Regards,
Jared Rossi
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH 07/10] pc-bios/s390-ccw: Add support for virtio-blk-pci IPL
2025-12-10 20:54 ` [PATCH 07/10] pc-bios/s390-ccw: Add support for virtio-blk-pci IPL jrossi
2026-01-07 14:44 ` Thomas Huth
@ 2026-01-07 20:39 ` Zhuoying Cai
2026-01-07 23:40 ` Jared Rossi
1 sibling, 1 reply; 36+ messages in thread
From: Zhuoying Cai @ 2026-01-07 20:39 UTC (permalink / raw)
To: jrossi, qemu-devel, qemu-s390x, thuth, mst
Cc: jjherne, alifm, farman, mjrosato
On 12/10/25 3:54 PM, jrossi@linux.ibm.com wrote:
> From: Jared Rossi <jrossi@linux.ibm.com>
>
> Enable virt-queue PCI configuration and add routines for virtio-blk-pci devices.
>
> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
> ---
> include/hw/s390x/ipl/qipl.h | 10 +
> pc-bios/s390-ccw/virtio-pci.h | 79 +++++++
> pc-bios/s390-ccw/virtio.h | 1 +
> pc-bios/s390-ccw/main.c | 60 +++++-
> pc-bios/s390-ccw/virtio-blkdev.c | 3 +
> pc-bios/s390-ccw/virtio-pci.c | 360 +++++++++++++++++++++++++++++++
> pc-bios/s390-ccw/virtio.c | 5 +
> pc-bios/s390-ccw/Makefile | 2 +-
> 8 files changed, 517 insertions(+), 3 deletions(-)
> create mode 100644 pc-bios/s390-ccw/virtio-pci.h
> create mode 100644 pc-bios/s390-ccw/virtio-pci.c
>
> diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
> index 8199b839f0..5c7779a1c3 100644
> --- a/include/hw/s390x/ipl/qipl.h
> +++ b/include/hw/s390x/ipl/qipl.h
> @@ -22,6 +22,7 @@
>
> #define S390_IPL_TYPE_FCP 0x00
> #define S390_IPL_TYPE_CCW 0x02
> +#define S390_IPL_TYPE_PCI 0x04
> #define S390_IPL_TYPE_PV 0x05
> #define S390_IPL_TYPE_QEMU_SCSI 0xff
>
> @@ -105,6 +106,14 @@ struct IplBlockQemuScsi {
> } QEMU_PACKED;
> typedef struct IplBlockQemuScsi IplBlockQemuScsi;
>
> +struct IplBlockPci {
> + uint32_t reserved0[80];
> + uint8_t opt;
> + uint8_t reserved1[3];
> + uint32_t fid;
> +} QEMU_PACKED;
> +typedef struct IplBlockPci IplBlockPci;
> +
> union IplParameterBlock {
> struct {
> uint32_t len;
> @@ -120,6 +129,7 @@ union IplParameterBlock {
> IplBlockFcp fcp;
> IPLBlockPV pv;
> IplBlockQemuScsi scsi;
> + IplBlockPci pci;
> };
> } QEMU_PACKED;
> struct {
> diff --git a/pc-bios/s390-ccw/virtio-pci.h b/pc-bios/s390-ccw/virtio-pci.h
> new file mode 100644
> index 0000000000..b203f37aea
> --- /dev/null
> +++ b/pc-bios/s390-ccw/virtio-pci.h
> @@ -0,0 +1,79 @@
> +/*
> + * Definitions for virtio-pci
> + *
> + * Copyright 2025 IBM Corp.
> + * Author(s): Jared Rossi <jrossi@linux.ibm.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#ifndef VIRTIO_PCI_H
> +#define VIRTIO_PCI_H
> +
> +/* Common configuration */
> +#define VPCI_CAP_COMMON_CFG 1
> +/* Notifications */
> +#define VPCI_CAP_NOTIFY_CFG 2
> +/* ISR access */
> +#define VPCI_CAP_ISR_CFG 3
> +/* Device specific configuration */
> +#define VPCI_CAP_DEVICE_CFG 4
> +/* PCI configuration access */
> +#define VPCI_CAP_PCI_CFG 5
> +/* Additional shared memory capability */
> +#define VPCI_CAP_SHARED_MEMORY_CFG 8
> +/* PCI vendor data configuration */
> +#define VPCI_CAP_VENDOR_CFG 9
> +
> +/* Offsets within capability header */
> +#define VPCI_CAP_VNDR 0
> +#define VPCI_CAP_NEXT 1
> +#define VPCI_CAP_LEN 2
> +#define VPCI_CAP_CFG_TYPE 3
> +#define VPCI_CAP_BAR 4
> +#define VPCI_CAP_OFFSET 8
> +#define VPCI_CAP_LENGTH 12
> +
> +#define VPCI_N_CAP_MULT 16 /* Notify multiplier, VPCI_CAP_NOTIFY_CFG only */
> +
> +/* Common Area Offsets for virtio-pci queue */
> +#define VPCI_C_OFFSET_DFSELECT 0
> +#define VPCI_C_OFFSET_DF 4
> +#define VPCI_C_OFFSET_GFSELECT 8
> +#define VPCI_C_OFFSET_GF 12
> +#define VPCI_C_COMMON_NUMQ 18
> +#define VPCI_C_OFFSET_STATUS 20
> +#define VPCI_C_OFFSET_Q_SELECT 22
> +#define VPCI_C_OFFSET_Q_SIZE 24
> +#define VPCI_C_OFFSET_Q_ENABLE 28
> +#define VPCI_C_OFFSET_Q_NOFF 30
> +#define VPCI_C_OFFSET_Q_DESCLO 32
> +#define VPCI_C_OFFSET_Q_DESCHI 36
> +#define VPCI_C_OFFSET_Q_AVAILLO 40
> +#define VPCI_C_OFFSET_Q_AVAILHI 44
> +#define VPCI_C_OFFSET_Q_USEDLO 48
> +#define VPCI_C_OFFSET_Q_USEDHI 52
> +
> +#define VPCI_S_RESET 0
> +#define VPCI_S_ACKNOWLEDGE 1
> +#define VPCI_S_DRIVER 2
> +#define VPCI_S_DRIVER_OK 4
> +#define VPCI_S_FEATURES_OK 8
> +
> +#define VIRTIO_F_VERSION_1 (1 << (32 - 32)) /* Feature bit 32 */
> +
> +#define VIRT_Q_SIZE 16
> +
> +struct VirtioPciCap {
> + uint8_t bar; /* Which PCIAS it's in */
> + uint32_t off; /* Offset within bar */
> +};
> +typedef struct VirtioPciCap VirtioPciCap;
> +
> +long virtio_pci_notify(uint32_t fhandle, int vq_id);
> +int virtio_pci_setup(VDev *vdev);
> +int virtio_pci_setup_device(void);
> +int virtio_pci_reset(VDev *vdev);
> +void virtio_pci_id2type(VDev *vdev, uint16_t device_id);
> +
> +#endif
> diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
> index e747891a2c..cc68c2f8a9 100644
> --- a/pc-bios/s390-ccw/virtio.h
> +++ b/pc-bios/s390-ccw/virtio.h
> @@ -256,6 +256,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 e82d60bbb7..562eeede63 100644
> --- a/pc-bios/s390-ccw/main.c
> +++ b/pc-bios/s390-ccw/main.c
> @@ -15,7 +15,9 @@
> #include "s390-arch.h"
> #include "s390-ccw.h"
> #include "cio.h"
> +#include "clp.h"
> #include "virtio.h"
> +#include "virtio-pci.h"
> #include "virtio-scsi.h"
> #include "dasd-ipl.h"
>
> @@ -151,6 +153,21 @@ static bool find_subch(int dev_no)
> return false;
> }
>
> +static bool find_fid(uint32_t fid)
> +{
> + 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->dev_type != 0;
> +}
> +
> static void menu_setup(void)
> {
> if (memcmp(loadparm_str, LOADPARM_PROMPT, LOADPARM_LEN) == 0) {
> @@ -238,6 +255,9 @@ static bool find_boot_device(void)
> blk_schid.ssid = iplb.scsi.ssid & 0x3;
> found = find_subch(iplb.scsi.devno);
> break;
> + case S390_IPL_TYPE_PCI:
> + found = find_fid(iplb.pci.fid);
> + break;
> default:
> puts("Unsupported IPLB");
> }
> @@ -276,7 +296,7 @@ static int virtio_setup(void)
> return ret;
> }
>
> -static void ipl_boot_device(void)
> +static void ipl_ccw_device(void)
> {
> switch (cutype) {
> case CU_TYPE_DASD_3990:
> @@ -289,7 +309,43 @@ static void ipl_boot_device(void)
> }
> break;
> default:
> - printf("Attempting to boot from unexpected device type 0x%X\n", cutype);
> + printf("Cannot boot CCW device with cu type 0x%X\n", cutype);
> + }
> +}
> +
> +static void ipl_pci_device(void)
> +{
> + VDev *vdev = virtio_get_device();
> + vdev->is_cdrom = false;
> + vdev->scsi_device_selected = false;
> +
> + if (virtio_pci_setup_device()) {
> + return;
> + }
> +
> + switch (vdev->dev_type) {
> + case VIRTIO_ID_BLOCK:
> + if (virtio_setup() == 0) {
> + zipl_load();
> + }
> + break;
> + default:
> + printf("Cannot boot PCI device type 0x%X\n", vdev->dev_type);
> + }
> +}
> +
> +static void ipl_boot_device(void)
> +{
> + switch (virtio_get_device()->ipl_type) {
> + case S390_IPL_TYPE_QEMU_SCSI:
> + case S390_IPL_TYPE_CCW:
> + ipl_ccw_device();
> + break;
> + case S390_IPL_TYPE_PCI:
> + ipl_pci_device();
> + break;
> + default:
> + puts("Unrecognized IPL type!");
> }
> }
>
> diff --git a/pc-bios/s390-ccw/virtio-blkdev.c b/pc-bios/s390-ccw/virtio-blkdev.c
> index 87ab9a9513..0542b4feee 100644
> --- a/pc-bios/s390-ccw/virtio-blkdev.c
> +++ b/pc-bios/s390-ccw/virtio-blkdev.c
> @@ -13,6 +13,7 @@
> #include "virtio.h"
> #include "virtio-scsi.h"
> #include "virtio-ccw.h"
> +#include "virtio-pci.h"
>
> #define VIRTIO_BLK_F_GEOMETRY (1 << 4)
> #define VIRTIO_BLK_F_BLK_SIZE (1 << 6)
> @@ -242,6 +243,8 @@ int virtio_blk_setup_device(void)
> case S390_IPL_TYPE_QEMU_SCSI:
> case S390_IPL_TYPE_CCW:
> return virtio_ccw_setup(vdev);
> + case S390_IPL_TYPE_PCI:
> + return virtio_pci_setup(vdev);
> }
>
> return 1;
> diff --git a/pc-bios/s390-ccw/virtio-pci.c b/pc-bios/s390-ccw/virtio-pci.c
> new file mode 100644
> index 0000000000..838231d86c
> --- /dev/null
> +++ b/pc-bios/s390-ccw/virtio-pci.c
> @@ -0,0 +1,360 @@
> +/*
> + * Functionality for virtio-pci
> + *
> + * Copyright 2025 IBM Corp.
> + * Author(s): Jared Rossi <jrossi@linux.ibm.com>
> + *
> + * 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 regions */
> +VirtioPciCap c_cap; /* Common capabilities */
> +VirtioPciCap d_cap; /* Device capabilities */
> +VirtioPciCap n_cap; /* Notify capabilities */
> +uint32_t notify_mult;
> +uint16_t q_notify_offset;
> +
> +static int virtio_pci_set_status(VDev *vdev, uint8_t status)
> +{
> + int rc = pci_write_byte(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_STATUS,
> + c_cap.bar, status);
> + if (rc) {
> + puts("Failed to write virtio-pci status");
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +static int virtio_pci_get_status(VDev *vdev, uint8_t *status)
> +{
> + int rc = pci_read_byte(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_STATUS,
> + c_cap.bar, status);
> + if (rc) {
> + puts("Failed to read virtio-pci status");
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * Find the position of the capability config within PCI configuration
> + * space for a given cfg type. Return the position if found, otherwise 0.
> + */
> +static uint8_t find_cap_pos(uint32_t fhandle, uint8_t cfg_type)
> +{
> + uint8_t next, cfg;
> + int rc;
> +
> + rc = pci_read_byte(fhandle, PCI_CAPABILITY_LIST, PCI_CFGBAR, &next);
> + rc |= pci_read_byte(fhandle, next + 3, PCI_CFGBAR, &cfg);
> +
> + while (!rc && (cfg != cfg_type) && next) {
> + rc = pci_read_byte(fhandle, next + 1, PCI_CFGBAR, &next);
> + rc |= pci_read_byte(fhandle, next + 3, PCI_CFGBAR, &cfg);
> + }
> +
> + return rc ? 0 : next;
> +}
> +
> +static int virtio_pci_get_hfeatures(VDev *vdev, uint64_t *features)
> +{
> + uint32_t feat0, feat1;
> + int rc;
> +
> + rc = pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_DFSELECT,
> + c_cap.bar, 0);
> +
> + rc |= pci_read_bswap32(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_DF,
> + c_cap.bar, &feat0);
> +
> + rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_DFSELECT,
> + c_cap.bar, 1);
> +
> + rc |= pci_read_bswap32(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_DF,
> + c_cap.bar, &feat1);
> +
> + if (rc) {
> + puts("Failed to get PCI feature bits");
> + return -EIO;
> + }
> +
> + *features = (uint64_t) feat1 << 32;
> + *features |= (uint64_t) feat0;
> +
> + return 0;
> +}
> +
> +static int virtio_pci_set_gfeatures(VDev *vdev)
> +{
> + int rc;
> +
> + rc = pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_GFSELECT,
> + c_cap.bar, 0);
> +
> + rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_GF,
> + c_cap.bar, vdev->guest_features[1]);
> +
> + rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_GFSELECT,
> + c_cap.bar, 1);
> +
> + rc |= pci_bswap32_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_GF,
> + c_cap.bar, vdev->guest_features[0]);
> +
> + if (rc) {
> + puts("Failed to set PCI feature bits");
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +static int virtio_pci_get_blk_config(VDev *vdev)
> +{
> + return pci_read_flex(vdev->pci_fh, d_cap.off, d_cap.bar, &vdev->config.blk,
> + sizeof(VirtioBlkConfig));
> +}
> +
> +/* virtio spec v1.3 section 4.1.2.1 */
> +void virtio_pci_id2type(VDev *vdev, uint16_t device_id)
> +{
> + switch (device_id) {
> + case 0x1001:
> + vdev->dev_type = VIRTIO_ID_BLOCK;
> + break;
> + case 0x1000: /* Other valid but currently unsupported virtio device types */
> + case 0x1004:
> + default:
> + vdev->dev_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;
> + int rc;
> +
> + /* Common capabilities */
> + pos = find_cap_pos(vdev->pci_fh, VPCI_CAP_COMMON_CFG);
> + if (!pos) {
> + puts("Failed to locate PCI common configuration");
> + return 1;
> + }
> +
> + rc = pci_read_byte(vdev->pci_fh, pos + VPCI_CAP_BAR, PCI_CFGBAR, &c_cap.bar);
> + if (rc || pci_read_bswap32(vdev->pci_fh, pos + VPCI_CAP_OFFSET, PCI_CFGBAR,
> + &c_cap.off)) {
> + puts("Failed to read PCI common configuration");
> + return -EIO;
> + }
> +
> + /* Device capabilities */
> + pos = find_cap_pos(vdev->pci_fh, VPCI_CAP_DEVICE_CFG);
> + if (!pos) {
> + puts("Failed to locate PCI device configuration");
> + return 1;
> + }
> +
> + rc = pci_read_byte(vdev->pci_fh, pos + VPCI_CAP_BAR, PCI_CFGBAR, &d_cap.bar);
> + if (rc || pci_read_bswap32(vdev->pci_fh, pos + VPCI_CAP_OFFSET, PCI_CFGBAR,
> + &d_cap.off)) {
> + puts("Failed to read PCI device configuration");
> + return -EIO;
> + }
> +
> + /* Notification capabilities */
> + pos = find_cap_pos(vdev->pci_fh, VPCI_CAP_NOTIFY_CFG);
> + if (!pos) {
> + puts("Failed to locate PCI notification configuration");
> + return 1;
> + }
> +
> + rc = pci_read_byte(vdev->pci_fh, pos + VPCI_CAP_BAR, PCI_CFGBAR, &n_cap.bar);
> + if (rc || pci_read_bswap32(vdev->pci_fh, pos + VPCI_CAP_OFFSET, PCI_CFGBAR,
> + &n_cap.off)) {
> + puts("Failed to read PCI notification configuration");
> + return -EIO;
> + }
> +
> + rc = pci_read_bswap32(vdev->pci_fh, pos + VPCI_N_CAP_MULT, PCI_CFGBAR, ¬ify_mult);
> + if (rc || pci_read_bswap16(vdev->pci_fh, d_cap.off + VPCI_C_OFFSET_Q_NOFF,
> + d_cap.bar, &q_notify_offset)) {
Hi Jared, I think this came up in the previous version -- should
q_notify_offset be taken from the common configuration structure?
> + puts("Failed to read notification queue configuration");
> + return -EIO;
> + }
> +
> + 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;
> + }
> +
> + return 0;
> +}
> +
> +int virtio_pci_set_selected_vq(VDev *vdev, uint16_t queue_num)
> +{
> + return pci_bswap16_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_Q_SELECT,
> + c_cap.bar, queue_num);
> +}
> +
> +int virtio_pci_set_queue_size(VDev *vdev, uint16_t queue_size)
> +{
> + return pci_bswap16_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_Q_SIZE,
> + c_cap.bar, queue_size);
> +}
> +
> +static int virtio_pci_set_queue_enable(VDev *vdev, uint16_t enabled)
> +{
> + return pci_bswap16_write(vdev->pci_fh, c_cap.off + VPCI_C_OFFSET_Q_ENABLE,
> + c_cap.bar, enabled);
> +}
> +
> +static int set_pci_vq_addr(VDev *vdev, uint64_t config_off, void *addr)
> +{
> + return pci_bswap64_write(vdev->pci_fh, c_cap.off + config_off, c_cap.bar,
> + (uint64_t) addr);
> +}
> +
> +int virtio_pci_setup(VDev *vdev)
> +{
> + VRing *vr;
> + int rc;
> + uint64_t pci_features;
> + uint8_t status;
> + int i = 0;
> +
> + vdev->config.blk.blk_size = 0;
> + vdev->guessed_disk_nature = VIRTIO_GDN_NONE;
> + vdev->cmd_vr_idx = 0;
> +
> + if (virtio_reset(vdev)) {
> + return -EIO;
> + }
> +
> + status = VPCI_S_ACKNOWLEDGE;
> + rc = virtio_pci_set_status(vdev, status);
> + if (rc) {
> + puts("Virtio-pci device Failed to ACKNOWLEDGE");
> + return -EIO;
> + }
> +
> + rc = virtio_pci_read_pci_cap_config(vdev);
> + if (rc) {
> + printf("Invalid PCI capabilities");
> + return -EIO;
> + }
> +
> + switch (vdev->dev_type) {
> + case VIRTIO_ID_BLOCK:
> + vdev->nr_vqs = 1;
> + vdev->cmd_vr_idx = 0;
> + virtio_pci_get_blk_config(vdev);
> + break;
> + default:
> + puts("Unsupported virtio device");
> + return -ENODEV;
> + }
> +
> + status |= VPCI_S_DRIVER;
> + rc = virtio_pci_set_status(vdev, status);
> + if (rc) {
> + puts("Set status failed");
> + return -EIO;
> + }
> +
> + /* Feature negotiation */
> + rc = virtio_pci_get_hfeatures(vdev, &pci_features);
> + if (rc || virtio_pci_set_gfeatures(vdev)) {
> + 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 * VIRTIO_RING_SIZE),
> + .align = KVM_S390_VIRTIO_RING_ALIGN,
> + .index = i,
> + .num = 0,
> + };
> +
> + vr = &vdev->vrings[i];
> +
> + if (pci_read_flex(vdev->pci_fh, VPCI_C_COMMON_NUMQ, c_cap.bar, &info.num, 2)) {
> + return -EIO;
> + }
> +
> + vring_init(vr, &info);
> +
> + if (virtio_pci_set_selected_vq(vdev, vr->id)) {
> + puts("Failed to set selected virt-queue");
> + return -EIO;
> + }
> +
> + if (virtio_pci_set_queue_size(vdev, VIRTIO_RING_SIZE)) {
> + puts("Failed to set virt-queue size");
> + return -EIO;
> + }
> +
> + rc = set_pci_vq_addr(vdev, VPCI_C_OFFSET_Q_DESCLO, vr->desc);
> + rc |= set_pci_vq_addr(vdev, VPCI_C_OFFSET_Q_AVAILLO, vr->avail);
> + rc |= set_pci_vq_addr(vdev, VPCI_C_OFFSET_Q_USEDLO, vr->used);
> + if (rc) {
> + puts("Failed to configure virt-queue address");
> + return -EIO;
> + }
> +
> + if (virtio_pci_set_queue_enable(vdev, true)) {
> + puts("Failed to set virt-queue enabled");
> + return -EIO;
> + }
> + }
> +
> + status |= VPCI_S_FEATURES_OK | VPCI_S_DRIVER_OK;
> + return virtio_pci_set_status(vdev, status);
> +}
> +
> +int virtio_pci_setup_device(void)
> +{
> + VDev *vdev = virtio_get_device();
> +
> + if (enable_pci_function(&vdev->pci_fh)) {
> + puts("Failed to enable PCI function");
> + return -ENODEV;
> + }
> +
> + return 0;
> +}
> +
> +long virtio_pci_notify(uint32_t fhandle, int vq_id)
> +{
> + uint32_t offset = n_cap.off + notify_mult * q_notify_offset;
> + return pci_bswap16_write(fhandle, offset, n_cap.bar, (uint16_t) vq_id);
> +}
> diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c
> index 0488b3a07e..50afb11f6a 100644
> --- a/pc-bios/s390-ccw/virtio.c
> +++ b/pc-bios/s390-ccw/virtio.c
> @@ -15,6 +15,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"
> @@ -109,6 +110,8 @@ bool vring_notify(VRing *vr)
> case S390_IPL_TYPE_QEMU_SCSI:
> case S390_IPL_TYPE_CCW:
> vr->cookie = virtio_ccw_notify(vr->schid, vr->id, vr->cookie);
> + case S390_IPL_TYPE_PCI:
> + vr->cookie = virtio_pci_notify(virtio_get_device()->pci_fh, vr->id);
> }
>
> return vr->cookie >= 0;
> @@ -182,6 +185,8 @@ int virtio_reset(VDev *vdev)
> case S390_IPL_TYPE_QEMU_SCSI:
> case S390_IPL_TYPE_CCW:
> return virtio_ccw_reset(vdev);
> + case S390_IPL_TYPE_PCI:
> + return virtio_pci_reset(vdev);
> default:
> return -1;
> }
> diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
> index a62fc9d766..3e5dfb64d5 100644
> --- a/pc-bios/s390-ccw/Makefile
> +++ b/pc-bios/s390-ccw/Makefile
> @@ -35,7 +35,7 @@ QEMU_DGFLAGS = -MMD -MP -MT $@ -MF $(@D)/$(*F).d
>
> OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o netmain.o \
> virtio.o virtio-net.o virtio-scsi.o virtio-blkdev.o cio.o dasd-ipl.o \
> - virtio-ccw.o clp.o pci.o
> + virtio-ccw.o clp.o pci.o virtio-pci.o
>
> SLOF_DIR := $(SRC_PATH)/../../roms/SLOF
>
^ permalink raw reply [flat|nested] 36+ messages in thread* Re: [PATCH 07/10] pc-bios/s390-ccw: Add support for virtio-blk-pci IPL
2026-01-07 20:39 ` Zhuoying Cai
@ 2026-01-07 23:40 ` Jared Rossi
0 siblings, 0 replies; 36+ messages in thread
From: Jared Rossi @ 2026-01-07 23:40 UTC (permalink / raw)
To: Zhuoying Cai, qemu-devel, qemu-s390x, thuth, mst
Cc: jjherne, alifm, farman, mjrosato
On 1/7/26 3:39 PM, Zhuoying Cai wrote:
> On 12/10/25 3:54 PM, jrossi@linux.ibm.com wrote:
>> +
>> + rc = pci_read_bswap32(vdev->pci_fh, pos + VPCI_N_CAP_MULT, PCI_CFGBAR, ¬ify_mult);
>> + if (rc || pci_read_bswap16(vdev->pci_fh, d_cap.off + VPCI_C_OFFSET_Q_NOFF,
>> + d_cap.bar, &q_notify_offset)) {
> Hi Jared, I think this came up in the previous version -- should
> q_notify_offset be taken from the common configuration structure?
>
Ah, you are correct. I've missed fixing it. My bad... Thanks.
Regards,
Jared Rossi
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH 08/10] s390x: Build IPLB for virtio-pci devices
2025-12-10 20:54 [PATCH V2 0/10] s390x: Add support for virtio-blk-pci IPL device jrossi
` (6 preceding siblings ...)
2025-12-10 20:54 ` [PATCH 07/10] pc-bios/s390-ccw: Add support for virtio-blk-pci IPL jrossi
@ 2025-12-10 20:54 ` jrossi
2026-01-07 15:46 ` Thomas Huth
2025-12-10 20:54 ` [PATCH 09/10] hw: Add "loadparm" property to PCI devices for booting on s390x jrossi
2025-12-10 20:54 ` [PATCH 10/10] tests/qtest: Add s390x PCI boot test to cdrom-test.c jrossi
9 siblings, 1 reply; 36+ messages in thread
From: jrossi @ 2025-12-10 20:54 UTC (permalink / raw)
To: qemu-devel, qemu-s390x, thuth, mst
Cc: jjherne, alifm, farman, mjrosato, jrossi, zycai
From: Jared Rossi <jrossi@linux.ibm.com>
Search for a corresponding S390PCIBusDevice and build an IPLB if a device has
been indexed for boot but does not identify as a CCW device,
PCI devices are not yet included in boot probing (they must have a boot index).
Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
---
hw/s390x/ipl.h | 3 ++
include/hw/s390x/s390-pci-bus.h | 2 ++
hw/s390x/ipl.c | 56 ++++++++++++++++++++++++++++++---
hw/s390x/s390-pci-bus.c | 2 +-
4 files changed, 57 insertions(+), 6 deletions(-)
diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index aec2244321..5396d4ed91 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -107,6 +107,7 @@ QEMU_BUILD_BUG_MSG(offsetof(S390IPLState, iplb) & 3, "alignment of iplb wrong");
#define S390_IPLB_MIN_PV_LEN 148
#define S390_IPLB_MIN_CCW_LEN 200
#define S390_IPLB_MIN_FCP_LEN 384
+#define S390_IPLB_MIN_PCI_LEN 376
#define S390_IPLB_MIN_QEMU_SCSI_LEN 200
static inline bool iplb_valid_len(IplParameterBlock *iplb)
@@ -179,6 +180,8 @@ static inline bool iplb_valid(IplParameterBlock *iplb)
return len >= S390_IPLB_MIN_FCP_LEN;
case S390_IPL_TYPE_CCW:
return len >= S390_IPLB_MIN_CCW_LEN;
+ case S390_IPL_TYPE_PCI:
+ return len >= S390_IPLB_MIN_PCI_LEN;
case S390_IPL_TYPE_QEMU_SCSI:
default:
return false;
diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h
index f643e13057..6cbe26a0b8 100644
--- a/include/hw/s390x/s390-pci-bus.h
+++ b/include/hw/s390x/s390-pci-bus.h
@@ -402,6 +402,8 @@ S390PCIBusDevice *s390_pci_find_dev_by_fh(S390pciState *s, uint32_t fh);
S390PCIBusDevice *s390_pci_find_dev_by_fid(S390pciState *s, uint32_t fid);
S390PCIBusDevice *s390_pci_find_dev_by_target(S390pciState *s,
const char *target);
+S390PCIBusDevice *s390_pci_find_dev_by_pci(S390pciState *s,
+ PCIDevice *pci_dev);
S390PCIBusDevice *s390_pci_find_next_avail_dev(S390pciState *s,
S390PCIBusDevice *pbdev);
void s390_pci_ism_reset(void);
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 3843d2a850..cf423ac764 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -22,12 +22,14 @@
#include "hw/loader.h"
#include "hw/qdev-properties.h"
#include "hw/boards.h"
+#include "hw/s390x/s390-pci-bus.h"
#include "hw/s390x/virtio-ccw.h"
#include "hw/s390x/vfio-ccw.h"
#include "hw/s390x/css.h"
#include "hw/s390x/ebcdic.h"
#include "hw/scsi/scsi.h"
#include "hw/virtio/virtio-net.h"
+#include "hw/virtio/virtio-pci.h"
#include "ipl.h"
#include "qemu/error-report.h"
#include "qemu/config-file.h"
@@ -339,7 +341,8 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl)
ipl->qipl.boot_menu_timeout = cpu_to_be32(splash_time);
}
-#define CCW_DEVTYPE_NONE 0x00
+#define S390_DEVTYPE_NONE 0x00
+
#define CCW_DEVTYPE_VIRTIO 0x01
#define CCW_DEVTYPE_VIRTIO_NET 0x02
#define CCW_DEVTYPE_SCSI 0x03
@@ -348,7 +351,7 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl)
static CcwDevice *s390_get_ccw_device(DeviceState *dev_st, int *devtype)
{
CcwDevice *ccw_dev = NULL;
- int tmp_dt = CCW_DEVTYPE_NONE;
+ int tmp_dt = S390_DEVTYPE_NONE;
if (dev_st) {
VirtIONet *virtio_net_dev = (VirtIONet *)
@@ -395,6 +398,31 @@ static CcwDevice *s390_get_ccw_device(DeviceState *dev_st, int *devtype)
return ccw_dev;
}
+#define PCI_DEVTYPE_VIRTIO 0x05
+
+static S390PCIBusDevice *s390_get_pci_device(DeviceState *dev_st, int *devtype)
+{
+ S390PCIBusDevice *pbdev = NULL;
+ int tmp_dt = S390_DEVTYPE_NONE;
+
+ if (dev_st) {
+ PCIDevice *pci_dev = (PCIDevice *)
+ object_dynamic_cast(OBJECT(qdev_get_parent_bus(dev_st)->parent),
+ TYPE_PCI_DEVICE);
+ if (pci_dev) {
+ pbdev = s390_pci_find_dev_by_pci(s390_get_phb(), pci_dev);
+ if (pbdev) {
+ tmp_dt = PCI_DEVTYPE_VIRTIO;
+ }
+ }
+ }
+ if (devtype) {
+ *devtype = tmp_dt;
+ }
+
+ return pbdev;
+}
+
static uint64_t s390_ipl_map_iplb_chain(IplParameterBlock *iplb_chain)
{
S390IPLState *ipl = get_ipl_device();
@@ -427,14 +455,12 @@ void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp)
static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
{
CcwDevice *ccw_dev = NULL;
+ S390PCIBusDevice *pbdev = NULL;
SCSIDevice *sd;
int devtype;
uint8_t *lp;
g_autofree void *scsi_lp = NULL;
- /*
- * Currently allow IPL only from CCW devices.
- */
ccw_dev = s390_get_ccw_device(dev_st, &devtype);
if (ccw_dev) {
lp = ccw_dev->loadparm;
@@ -484,6 +510,26 @@ static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
return true;
}
+ pbdev = s390_get_pci_device(dev_st, &devtype);
+ if (pbdev) {
+ switch (devtype) {
+ case PCI_DEVTYPE_VIRTIO:
+ iplb->len = S390_IPLB_MIN_PCI_LEN;
+ iplb->pbt = S390_IPL_TYPE_PCI;
+ iplb->pci.fid = pbdev->fid;
+ break;
+ default:
+ return false;
+ }
+
+ /* Per-device loadparm not yet supported for non-ccw IPL */
+ lp = S390_CCW_MACHINE(qdev_get_machine())->loadparm;
+ s390_ipl_convert_loadparm((char *)lp, iplb->loadparm);
+ iplb->flags |= DIAG308_FLAGS_LP_VALID;
+
+ return true;
+ }
+
return false;
}
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
index 52820894fa..615974851b 100644
--- a/hw/s390x/s390-pci-bus.c
+++ b/hw/s390x/s390-pci-bus.c
@@ -249,7 +249,7 @@ S390PCIBusDevice *s390_pci_find_dev_by_target(S390pciState *s,
return NULL;
}
-static S390PCIBusDevice *s390_pci_find_dev_by_pci(S390pciState *s,
+S390PCIBusDevice *s390_pci_find_dev_by_pci(S390pciState *s,
PCIDevice *pci_dev)
{
S390PCIBusDevice *pbdev;
--
2.49.0
^ permalink raw reply related [flat|nested] 36+ messages in thread* Re: [PATCH 08/10] s390x: Build IPLB for virtio-pci devices
2025-12-10 20:54 ` [PATCH 08/10] s390x: Build IPLB for virtio-pci devices jrossi
@ 2026-01-07 15:46 ` Thomas Huth
0 siblings, 0 replies; 36+ messages in thread
From: Thomas Huth @ 2026-01-07 15:46 UTC (permalink / raw)
To: jrossi, qemu-devel, qemu-s390x, mst
Cc: jjherne, alifm, farman, mjrosato, zycai
On 10/12/2025 21.54, jrossi@linux.ibm.com wrote:
> From: Jared Rossi <jrossi@linux.ibm.com>
>
> Search for a corresponding S390PCIBusDevice and build an IPLB if a device has
> been indexed for boot but does not identify as a CCW device,
>
> PCI devices are not yet included in boot probing (they must have a boot index).
>
> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
> ---
> hw/s390x/ipl.h | 3 ++
> include/hw/s390x/s390-pci-bus.h | 2 ++
> hw/s390x/ipl.c | 56 ++++++++++++++++++++++++++++++---
> hw/s390x/s390-pci-bus.c | 2 +-
> 4 files changed, 57 insertions(+), 6 deletions(-)
>
> diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
> index aec2244321..5396d4ed91 100644
> --- a/hw/s390x/ipl.h
> +++ b/hw/s390x/ipl.h
> @@ -107,6 +107,7 @@ QEMU_BUILD_BUG_MSG(offsetof(S390IPLState, iplb) & 3, "alignment of iplb wrong");
> #define S390_IPLB_MIN_PV_LEN 148
> #define S390_IPLB_MIN_CCW_LEN 200
> #define S390_IPLB_MIN_FCP_LEN 384
> +#define S390_IPLB_MIN_PCI_LEN 376
> #define S390_IPLB_MIN_QEMU_SCSI_LEN 200
>
> static inline bool iplb_valid_len(IplParameterBlock *iplb)
> @@ -179,6 +180,8 @@ static inline bool iplb_valid(IplParameterBlock *iplb)
> return len >= S390_IPLB_MIN_FCP_LEN;
> case S390_IPL_TYPE_CCW:
> return len >= S390_IPLB_MIN_CCW_LEN;
> + case S390_IPL_TYPE_PCI:
> + return len >= S390_IPLB_MIN_PCI_LEN;
> case S390_IPL_TYPE_QEMU_SCSI:
> default:
> return false;
> diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h
> index f643e13057..6cbe26a0b8 100644
> --- a/include/hw/s390x/s390-pci-bus.h
> +++ b/include/hw/s390x/s390-pci-bus.h
> @@ -402,6 +402,8 @@ S390PCIBusDevice *s390_pci_find_dev_by_fh(S390pciState *s, uint32_t fh);
> S390PCIBusDevice *s390_pci_find_dev_by_fid(S390pciState *s, uint32_t fid);
> S390PCIBusDevice *s390_pci_find_dev_by_target(S390pciState *s,
> const char *target);
> +S390PCIBusDevice *s390_pci_find_dev_by_pci(S390pciState *s,
> + PCIDevice *pci_dev);
Please indent according to the "(" in the previous line.
> S390PCIBusDevice *s390_pci_find_next_avail_dev(S390pciState *s,
> S390PCIBusDevice *pbdev);
> void s390_pci_ism_reset(void);
> diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
> index 3843d2a850..cf423ac764 100644
> --- a/hw/s390x/ipl.c
> +++ b/hw/s390x/ipl.c
> @@ -22,12 +22,14 @@
> #include "hw/loader.h"
> #include "hw/qdev-properties.h"
> #include "hw/boards.h"
> +#include "hw/s390x/s390-pci-bus.h"
> #include "hw/s390x/virtio-ccw.h"
> #include "hw/s390x/vfio-ccw.h"
> #include "hw/s390x/css.h"
> #include "hw/s390x/ebcdic.h"
> #include "hw/scsi/scsi.h"
> #include "hw/virtio/virtio-net.h"
> +#include "hw/virtio/virtio-pci.h"
> #include "ipl.h"
> #include "qemu/error-report.h"
> #include "qemu/config-file.h"
> @@ -339,7 +341,8 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl)
> ipl->qipl.boot_menu_timeout = cpu_to_be32(splash_time);
> }
>
> -#define CCW_DEVTYPE_NONE 0x00
> +#define S390_DEVTYPE_NONE 0x00
> +
> #define CCW_DEVTYPE_VIRTIO 0x01
> #define CCW_DEVTYPE_VIRTIO_NET 0x02
> #define CCW_DEVTYPE_SCSI 0x03
> @@ -348,7 +351,7 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl)
> static CcwDevice *s390_get_ccw_device(DeviceState *dev_st, int *devtype)
> {
> CcwDevice *ccw_dev = NULL;
> - int tmp_dt = CCW_DEVTYPE_NONE;
> + int tmp_dt = S390_DEVTYPE_NONE;
>
> if (dev_st) {
> VirtIONet *virtio_net_dev = (VirtIONet *)
> @@ -395,6 +398,31 @@ static CcwDevice *s390_get_ccw_device(DeviceState *dev_st, int *devtype)
> return ccw_dev;
> }
>
> +#define PCI_DEVTYPE_VIRTIO 0x05
> +
> +static S390PCIBusDevice *s390_get_pci_device(DeviceState *dev_st, int *devtype)
> +{
> + S390PCIBusDevice *pbdev = NULL;
> + int tmp_dt = S390_DEVTYPE_NONE;
> +
> + if (dev_st) {
> + PCIDevice *pci_dev = (PCIDevice *)
> + object_dynamic_cast(OBJECT(qdev_get_parent_bus(dev_st)->parent),
> + TYPE_PCI_DEVICE);
> + if (pci_dev) {
> + pbdev = s390_pci_find_dev_by_pci(s390_get_phb(), pci_dev);
> + if (pbdev) {
> + tmp_dt = PCI_DEVTYPE_VIRTIO;
Hmm, so this code assumes that all PCI devices are virtio devices? ... I
guess that might cause trouble in case someone tries "-device
nvme,bootindex=1" ...? I think you might want to do the object_dynamic_cast
with TYPE_VIRTIO_PCI instead.
> + }
> + }
> + }
> + if (devtype) {
> + *devtype = tmp_dt;
> + }
> +
> + return pbdev;
> +}
Thomas
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH 09/10] hw: Add "loadparm" property to PCI devices for booting on s390x
2025-12-10 20:54 [PATCH V2 0/10] s390x: Add support for virtio-blk-pci IPL device jrossi
` (7 preceding siblings ...)
2025-12-10 20:54 ` [PATCH 08/10] s390x: Build IPLB for virtio-pci devices jrossi
@ 2025-12-10 20:54 ` jrossi
2026-01-07 15:50 ` Thomas Huth
2025-12-10 20:54 ` [PATCH 10/10] tests/qtest: Add s390x PCI boot test to cdrom-test.c jrossi
9 siblings, 1 reply; 36+ messages in thread
From: jrossi @ 2025-12-10 20:54 UTC (permalink / raw)
To: qemu-devel, qemu-s390x, thuth, mst
Cc: jjherne, alifm, farman, mjrosato, jrossi, zycai
From: Jared Rossi <jrossi@linux.ibm.com>
The loadparm is required on s390x to pass the information to the boot loader
such as which kernel should be started or whether the boot menu should be shown.
Because PCI devices do not naturally allocate space for this, the property is
added on an architecture specific basis.
Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
---
include/hw/pci/pci_device.h | 3 +++
hw/pci/pci.c | 39 +++++++++++++++++++++++++++++++++++++
hw/s390x/ipl.c | 11 +++++++++--
3 files changed, 51 insertions(+), 2 deletions(-)
diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h
index 88ccea5011..5cac6e1688 100644
--- a/include/hw/pci/pci_device.h
+++ b/include/hw/pci/pci_device.h
@@ -62,6 +62,9 @@ struct PCIDevice {
bool partially_hotplugged;
bool enabled;
+ /* only for s390x */
+ char *loadparm;
+
/* PCI config space */
uint8_t *config;
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index b1eba348e0..1ea0d7c54c 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -36,6 +36,7 @@
#include "migration/qemu-file-types.h"
#include "migration/vmstate.h"
#include "net/net.h"
+#include "system/arch_init.h"
#include "system/numa.h"
#include "system/runstate.h"
#include "system/system.h"
@@ -2825,6 +2826,43 @@ int pci_qdev_find_device(const char *id, PCIDevice **pdev)
return rc;
}
+static char *pci_qdev_property_get_loadparm(Object *obj, Error **errp)
+{
+ return g_strdup(PCI_DEVICE(obj)->loadparm);
+}
+
+static void pci_qdev_property_set_loadparm(Object *obj, const char *value,
+ Error **errp)
+{
+ void *lp_str;
+
+ if (object_property_get_int(obj, "bootindex", NULL) < 0) {
+ error_setg(errp, "'loadparm' is only valid for boot devices");
+ return;
+ }
+
+ lp_str = g_malloc0(strlen(value) + 1);
+ if (!qdev_prop_sanitize_s390x_loadparm(lp_str, value, errp)) {
+ g_free(lp_str);
+ return;
+ }
+ PCI_DEVICE(obj)->loadparm = lp_str;
+}
+
+static void pci_qdev_property_add_specifics(DeviceClass *dc)
+{
+ ObjectClass *oc = OBJECT_CLASS(dc);
+
+ /* The loadparm property is only supported on s390x */
+ if (qemu_arch_available(QEMU_ARCH_S390X)) {
+ object_class_property_add_str(oc, "loadparm",
+ pci_qdev_property_get_loadparm,
+ pci_qdev_property_set_loadparm);
+ object_class_property_set_description(oc, "loadparm",
+ "load parameter (s390x only)");
+ }
+}
+
MemoryRegion *pci_address_space(PCIDevice *dev)
{
return pci_get_bus(dev)->address_space_mem;
@@ -2843,6 +2881,7 @@ static void pci_device_class_init(ObjectClass *klass, const void *data)
k->unrealize = pci_qdev_unrealize;
k->bus_type = TYPE_PCI_BUS;
device_class_set_props(k, pci_props);
+ pci_qdev_property_add_specifics(k);
object_class_property_set_description(
klass, "x-max-bounce-buffer-size",
"Maximum buffer size allocated for bounce buffers used for mapped "
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index cf423ac764..57f5a7a204 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -460,6 +460,7 @@ static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
int devtype;
uint8_t *lp;
g_autofree void *scsi_lp = NULL;
+ g_autofree void *pci_lp = NULL;
ccw_dev = s390_get_ccw_device(dev_st, &devtype);
if (ccw_dev) {
@@ -512,6 +513,14 @@ static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
pbdev = s390_get_pci_device(dev_st, &devtype);
if (pbdev) {
+ pci_lp = object_property_get_str(OBJECT(pbdev->pdev), "loadparm", NULL);
+ if (pci_lp && strlen(pci_lp) > 0) {
+ lp = pci_lp;
+ } else {
+ /* Use machine loadparm as a place holder if PCI LP is unset */
+ lp = S390_CCW_MACHINE(qdev_get_machine())->loadparm;
+ }
+
switch (devtype) {
case PCI_DEVTYPE_VIRTIO:
iplb->len = S390_IPLB_MIN_PCI_LEN;
@@ -522,8 +531,6 @@ static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
return false;
}
- /* Per-device loadparm not yet supported for non-ccw IPL */
- lp = S390_CCW_MACHINE(qdev_get_machine())->loadparm;
s390_ipl_convert_loadparm((char *)lp, iplb->loadparm);
iplb->flags |= DIAG308_FLAGS_LP_VALID;
--
2.49.0
^ permalink raw reply related [flat|nested] 36+ messages in thread* Re: [PATCH 09/10] hw: Add "loadparm" property to PCI devices for booting on s390x
2025-12-10 20:54 ` [PATCH 09/10] hw: Add "loadparm" property to PCI devices for booting on s390x jrossi
@ 2026-01-07 15:50 ` Thomas Huth
2026-01-07 18:46 ` Jared Rossi
0 siblings, 1 reply; 36+ messages in thread
From: Thomas Huth @ 2026-01-07 15:50 UTC (permalink / raw)
To: jrossi, qemu-devel, qemu-s390x, mst
Cc: jjherne, alifm, farman, mjrosato, zycai
On 10/12/2025 21.54, jrossi@linux.ibm.com wrote:
> From: Jared Rossi <jrossi@linux.ibm.com>
>
> The loadparm is required on s390x to pass the information to the boot loader
> such as which kernel should be started or whether the boot menu should be shown.
>
> Because PCI devices do not naturally allocate space for this, the property is
> added on an architecture specific basis.
>
> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
> ---
> include/hw/pci/pci_device.h | 3 +++
> hw/pci/pci.c | 39 +++++++++++++++++++++++++++++++++++++
> hw/s390x/ipl.c | 11 +++++++++--
> 3 files changed, 51 insertions(+), 2 deletions(-)
>
> diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h
> index 88ccea5011..5cac6e1688 100644
> --- a/include/hw/pci/pci_device.h
> +++ b/include/hw/pci/pci_device.h
> @@ -62,6 +62,9 @@ struct PCIDevice {
> bool partially_hotplugged;
> bool enabled;
>
> + /* only for s390x */
> + char *loadparm;
> +
> /* PCI config space */
> uint8_t *config;
>
> diff --git a/hw/pci/pci.c b/hw/pci/pci.c
> index b1eba348e0..1ea0d7c54c 100644
> --- a/hw/pci/pci.c
> +++ b/hw/pci/pci.c
> @@ -36,6 +36,7 @@
> #include "migration/qemu-file-types.h"
> #include "migration/vmstate.h"
> #include "net/net.h"
> +#include "system/arch_init.h"
> #include "system/numa.h"
> #include "system/runstate.h"
> #include "system/system.h"
> @@ -2825,6 +2826,43 @@ int pci_qdev_find_device(const char *id, PCIDevice **pdev)
> return rc;
> }
>
> +static char *pci_qdev_property_get_loadparm(Object *obj, Error **errp)
> +{
> + return g_strdup(PCI_DEVICE(obj)->loadparm);
> +}
> +
> +static void pci_qdev_property_set_loadparm(Object *obj, const char *value,
> + Error **errp)
> +{
> + void *lp_str;
> +
> + if (object_property_get_int(obj, "bootindex", NULL) < 0) {
> + error_setg(errp, "'loadparm' is only valid for boot devices");
> + return;
> + }
> +
> + lp_str = g_malloc0(strlen(value) + 1);
> + if (!qdev_prop_sanitize_s390x_loadparm(lp_str, value, errp)) {
> + g_free(lp_str);
> + return;
> + }
> + PCI_DEVICE(obj)->loadparm = lp_str;
> +}
> +
> +static void pci_qdev_property_add_specifics(DeviceClass *dc)
> +{
> + ObjectClass *oc = OBJECT_CLASS(dc);
> +
> + /* The loadparm property is only supported on s390x */
> + if (qemu_arch_available(QEMU_ARCH_S390X)) {
> + object_class_property_add_str(oc, "loadparm",
> + pci_qdev_property_get_loadparm,
> + pci_qdev_property_set_loadparm);
> + object_class_property_set_description(oc, "loadparm",
> + "load parameter (s390x only)");
> + }
> +}
Adding this unconditionally to each and every PCI device is a little bit
ugly ... could we please limit this to virtio-blk-pci devices for now?
(or maybe check if there is a bootindex property, and only add it for
devices with a bootindex property?)
Thomas
^ permalink raw reply [flat|nested] 36+ messages in thread* Re: [PATCH 09/10] hw: Add "loadparm" property to PCI devices for booting on s390x
2026-01-07 15:50 ` Thomas Huth
@ 2026-01-07 18:46 ` Jared Rossi
2026-01-08 6:07 ` Thomas Huth
0 siblings, 1 reply; 36+ messages in thread
From: Jared Rossi @ 2026-01-07 18:46 UTC (permalink / raw)
To: Thomas Huth, qemu-devel, qemu-s390x, mst
Cc: jjherne, alifm, farman, mjrosato, zycai
On 1/7/26 10:50 AM, Thomas Huth wrote:
> On 10/12/2025 21.54, jrossi@linux.ibm.com wrote:
>> From: Jared Rossi <jrossi@linux.ibm.com>
>>
>> The loadparm is required on s390x to pass the information to the boot
>> loader
>> such as which kernel should be started or whether the boot menu
>> should be shown.
>>
>> Because PCI devices do not naturally allocate space for this, the
>> property is
>> added on an architecture specific basis.
>>
>> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
>> ---
>> include/hw/pci/pci_device.h | 3 +++
>> hw/pci/pci.c | 39 +++++++++++++++++++++++++++++++++++++
>> hw/s390x/ipl.c | 11 +++++++++--
>> 3 files changed, 51 insertions(+), 2 deletions(-)
>>
>> diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h
>> index 88ccea5011..5cac6e1688 100644
>> --- a/include/hw/pci/pci_device.h
>> +++ b/include/hw/pci/pci_device.h
>> @@ -62,6 +62,9 @@ struct PCIDevice {
>> bool partially_hotplugged;
>> bool enabled;
>> + /* only for s390x */
>> + char *loadparm;
>> +
>> /* PCI config space */
>> uint8_t *config;
>> diff --git a/hw/pci/pci.c b/hw/pci/pci.c
>> index b1eba348e0..1ea0d7c54c 100644
>> --- a/hw/pci/pci.c
>> +++ b/hw/pci/pci.c
>> @@ -36,6 +36,7 @@
>> #include "migration/qemu-file-types.h"
>> #include "migration/vmstate.h"
>> #include "net/net.h"
>> +#include "system/arch_init.h"
>> #include "system/numa.h"
>> #include "system/runstate.h"
>> #include "system/system.h"
>> @@ -2825,6 +2826,43 @@ int pci_qdev_find_device(const char *id,
>> PCIDevice **pdev)
>> return rc;
>> }
>> +static char *pci_qdev_property_get_loadparm(Object *obj, Error
>> **errp)
>> +{
>> + return g_strdup(PCI_DEVICE(obj)->loadparm);
>> +}
>> +
>> +static void pci_qdev_property_set_loadparm(Object *obj, const char
>> *value,
>> + Error **errp)
>> +{
>> + void *lp_str;
>> +
>> + if (object_property_get_int(obj, "bootindex", NULL) < 0) {
>> + error_setg(errp, "'loadparm' is only valid for boot devices");
>> + return;
>> + }
>> +
>> + lp_str = g_malloc0(strlen(value) + 1);
>> + if (!qdev_prop_sanitize_s390x_loadparm(lp_str, value, errp)) {
>> + g_free(lp_str);
>> + return;
>> + }
>> + PCI_DEVICE(obj)->loadparm = lp_str;
>> +}
>> +
>> +static void pci_qdev_property_add_specifics(DeviceClass *dc)
>> +{
>> + ObjectClass *oc = OBJECT_CLASS(dc);
>> +
>> + /* The loadparm property is only supported on s390x */
>> + if (qemu_arch_available(QEMU_ARCH_S390X)) {
>> + object_class_property_add_str(oc, "loadparm",
>> + pci_qdev_property_get_loadparm,
>> + pci_qdev_property_set_loadparm);
>> + object_class_property_set_description(oc, "loadparm",
>> + "load parameter (s390x
>> only)");
>> + }
>> +}
>
> Adding this unconditionally to each and every PCI device is a little
> bit ugly ... could we please limit this to virtio-blk-pci devices for
> now?
> (or maybe check if there is a bootindex property, and only add it for
> devices with a bootindex property?)
>
> Thomas
>
Maybe I'm missing some nuance, but won't the check in
pci_qdev_property_set_loadparm() already enforce that this is only added
to boot devices?
Hmm... on second though, I guess the problem is that a loadparm property
will still be added (but not necessarily set) for all PCI devices? I
agree that is not good. I will limit what qualifies for the property to
be added in the first place.
Thanks,
Jared Rossi
^ permalink raw reply [flat|nested] 36+ messages in thread* Re: [PATCH 09/10] hw: Add "loadparm" property to PCI devices for booting on s390x
2026-01-07 18:46 ` Jared Rossi
@ 2026-01-08 6:07 ` Thomas Huth
0 siblings, 0 replies; 36+ messages in thread
From: Thomas Huth @ 2026-01-08 6:07 UTC (permalink / raw)
To: Jared Rossi, qemu-devel, qemu-s390x, mst
Cc: jjherne, alifm, farman, mjrosato, zycai
On 07/01/2026 19.46, Jared Rossi wrote:
>
>
> On 1/7/26 10:50 AM, Thomas Huth wrote:
>> On 10/12/2025 21.54, jrossi@linux.ibm.com wrote:
>>> From: Jared Rossi <jrossi@linux.ibm.com>
>>>
>>> The loadparm is required on s390x to pass the information to the boot loader
>>> such as which kernel should be started or whether the boot menu should be
>>> shown.
>>>
>>> Because PCI devices do not naturally allocate space for this, the
>>> property is
>>> added on an architecture specific basis.
>>>
>>> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
>>> ---
>>> include/hw/pci/pci_device.h | 3 +++
>>> hw/pci/pci.c | 39 +++++++++++++++++++++++++++++++++++++
>>> hw/s390x/ipl.c | 11 +++++++++--
>>> 3 files changed, 51 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h
>>> index 88ccea5011..5cac6e1688 100644
>>> --- a/include/hw/pci/pci_device.h
>>> +++ b/include/hw/pci/pci_device.h
>>> @@ -62,6 +62,9 @@ struct PCIDevice {
>>> bool partially_hotplugged;
>>> bool enabled;
>>> + /* only for s390x */
>>> + char *loadparm;
>>> +
>>> /* PCI config space */
>>> uint8_t *config;
>>> diff --git a/hw/pci/pci.c b/hw/pci/pci.c
>>> index b1eba348e0..1ea0d7c54c 100644
>>> --- a/hw/pci/pci.c
>>> +++ b/hw/pci/pci.c
>>> @@ -36,6 +36,7 @@
>>> #include "migration/qemu-file-types.h"
>>> #include "migration/vmstate.h"
>>> #include "net/net.h"
>>> +#include "system/arch_init.h"
>>> #include "system/numa.h"
>>> #include "system/runstate.h"
>>> #include "system/system.h"
>>> @@ -2825,6 +2826,43 @@ int pci_qdev_find_device(const char *id, PCIDevice
>>> **pdev)
>>> return rc;
>>> }
>>> +static char *pci_qdev_property_get_loadparm(Object *obj, Error **errp)
>>> +{
>>> + return g_strdup(PCI_DEVICE(obj)->loadparm);
>>> +}
>>> +
>>> +static void pci_qdev_property_set_loadparm(Object *obj, const char *value,
>>> + Error **errp)
>>> +{
>>> + void *lp_str;
>>> +
>>> + if (object_property_get_int(obj, "bootindex", NULL) < 0) {
>>> + error_setg(errp, "'loadparm' is only valid for boot devices");
>>> + return;
>>> + }
>>> +
>>> + lp_str = g_malloc0(strlen(value) + 1);
>>> + if (!qdev_prop_sanitize_s390x_loadparm(lp_str, value, errp)) {
>>> + g_free(lp_str);
>>> + return;
>>> + }
>>> + PCI_DEVICE(obj)->loadparm = lp_str;
>>> +}
>>> +
>>> +static void pci_qdev_property_add_specifics(DeviceClass *dc)
>>> +{
>>> + ObjectClass *oc = OBJECT_CLASS(dc);
>>> +
>>> + /* The loadparm property is only supported on s390x */
>>> + if (qemu_arch_available(QEMU_ARCH_S390X)) {
>>> + object_class_property_add_str(oc, "loadparm",
>>> + pci_qdev_property_get_loadparm,
>>> + pci_qdev_property_set_loadparm);
>>> + object_class_property_set_description(oc, "loadparm",
>>> + "load parameter (s390x
>>> only)");
>>> + }
>>> +}
>>
>> Adding this unconditionally to each and every PCI device is a little bit
>> ugly ... could we please limit this to virtio-blk-pci devices for now?
>> (or maybe check if there is a bootindex property, and only add it for
>> devices with a bootindex property?)
>>
>> Thomas
>>
>
> Maybe I'm missing some nuance, but won't the check in
> pci_qdev_property_set_loadparm() already enforce that this is only added to
> boot devices?
>
> Hmm... on second though, I guess the problem is that a loadparm property
> will still be added (but not necessarily set) for all PCI devices? I agree
> that is not good. I will limit what qualifies for the property to be added
> in the first place.
Yes, the problem is that this shows up in the user interface, e.g. if you
run QEMU with "-device qemu-xhci,help" to show the properties of the
qemu-xhci device. Having a "loadparm" property there could cause quite some
confusion for the users.
Thomas
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH 10/10] tests/qtest: Add s390x PCI boot test to cdrom-test.c
2025-12-10 20:54 [PATCH V2 0/10] s390x: Add support for virtio-blk-pci IPL device jrossi
` (8 preceding siblings ...)
2025-12-10 20:54 ` [PATCH 09/10] hw: Add "loadparm" property to PCI devices for booting on s390x jrossi
@ 2025-12-10 20:54 ` jrossi
9 siblings, 0 replies; 36+ messages in thread
From: jrossi @ 2025-12-10 20:54 UTC (permalink / raw)
To: qemu-devel, qemu-s390x, thuth, mst
Cc: jjherne, alifm, farman, mjrosato, jrossi, zycai
From: Jared Rossi <jrossi@linux.ibm.com>
Add a rudimentary test for s390x IPL to verify that a guest may boot using
virtio-blk-pci device.
Reviewed-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Jared Rossi <jrossi@linux.ibm.com>
---
tests/qtest/cdrom-test.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/tests/qtest/cdrom-test.c b/tests/qtest/cdrom-test.c
index 56e2d283a9..a65854d2bc 100644
--- a/tests/qtest/cdrom-test.c
+++ b/tests/qtest/cdrom-test.c
@@ -246,6 +246,13 @@ static void add_s390x_tests(void)
"-drive if=none,id=d2,media=cdrom,file=",
test_cdboot);
}
+ if (qtest_has_device("virtio-blk-pci")) {
+ qtest_add_data_func("cdrom/boot/pci-bus-with-bootindex",
+ "-device virtio-scsi -device virtio-serial "
+ "-device virtio-blk-pci,drive=d1,bootindex=1 "
+ "-drive if=none,id=d1,media=cdrom,file=",
+ test_cdboot);
+ }
}
int main(int argc, char **argv)
--
2.49.0
^ permalink raw reply related [flat|nested] 36+ messages in thread