[RESEND PATCH 0/1] pc-bios: Support List-Directed IPL from ECKD DASD

2023-02-21 Thread jrossi
From: Jared Rossi 

Add support for List-Directed IPL (LD-IPL) type pointers.

We check for a boot record indicating LD-IPL and use it if it is valid,
otherwise we use the CCW CDL or LDL format as usual. When a block is accessed
during IPL the block number is first calculated based on the cylinder, head,
and record numbers included in the block pointer; if LD-IPL has been initiated
then each pointer will be interpreted using the new format.

For simplicity, there is no choice presented to forcibly use CCW-IPL if LD-IPL
is available.  Because both sets of pointers ultimately go to the same
kernel/initrd, using CCW- or LD-IPL is transparent to the user.

One aspect of the user experience that does change is the availability of the
interactive boot menu when a loadparm is not specified.  For the existing
CCW-IPL, when the user does not specify a loadparm they are presented with a
list of boot options; however, this list is only written in the old style
pointers.  Therefore, if no loadparm is specified, and LD-IPL is supported, the
default boot option will be used automatically.

Jared Rossi (1):
  pc-bios: Add support for List-Directed IPL from ECKD DASD

 pc-bios/s390-ccw/bootmap.c | 157 -
 pc-bios/s390-ccw/bootmap.h |  30 ++-
 2 files changed, 148 insertions(+), 39 deletions(-)

-- 
2.31.1




[RESEND PATCH 1/1] pc-bios: Add support for List-Directed IPL from ECKD DASD

2023-02-21 Thread jrossi
From: Jared Rossi 

Check for a List Directed IPL Boot Record, which would supersede the CCW type
entries.  If the record is valid, proceed to use the new style pointers
and perform LD-IPL. Each block pointer is interpreted as either an LD-IPL
pointer or a legacy CCW pointer depending on the type of IPL initiated.

In either case CCW- or LD-IPL is transparent to the user and will boot the same
image regardless of which set of pointers is used. Because the interactive boot
menu is only written with the old style pointers, the menu will be disabled for
List Directed IPL from ECKD DASD.

If the LD-IPL fails, retry the IPL using the CCW type pointers.

If no LD-IPL boot record is found, simply perform CCW type IPL as usual.

Signed-off-by: Jared Rossi 

---
 pc-bios/s390-ccw/bootmap.c | 157 -
 pc-bios/s390-ccw/bootmap.h |  30 ++-
 2 files changed, 148 insertions(+), 39 deletions(-)

diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 994e59c0b0..77229f93f3 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -72,42 +72,74 @@ static inline void verify_boot_info(BootInfo *bip)
"Bad block size in zIPL section of the 1st record.");
 }
 
-static block_number_t eckd_block_num(EckdCHS *chs)
+static void eckd_format_chs(ExtEckdBlockPtr *ptr,  bool ldipl,
+uint64_t *c,
+uint64_t *h,
+uint64_t *s)
+{
+if (ldipl) {
+*c = ptr->ldptr.chs.cylinder;
+*h = ptr->ldptr.chs.head;
+*s = ptr->ldptr.chs.sector;
+} else {
+*c = ptr->bptr.chs.cylinder;
+*h = ptr->bptr.chs.head;
+*s = ptr->bptr.chs.sector;
+}
+}
+
+static block_number_t eckd_chs_to_block(uint64_t c, uint64_t h, uint64_t s)
 {
 const uint64_t sectors = virtio_get_sectors();
 const uint64_t heads = virtio_get_heads();
-const uint64_t cylinder = chs->cylinder
-+ ((chs->head & 0xfff0) << 12);
-const uint64_t head = chs->head & 0x000f;
+const uint64_t cylinder = c + ((h & 0xfff0) << 12);
+const uint64_t head = h & 0x000f;
 const block_number_t block = sectors * heads * cylinder
+ sectors * head
-   + chs->sector
-   - 1; /* block nr starts with zero */
+   + s - 1; /* block nr starts with zero */
 return block;
 }
 
-static bool eckd_valid_address(BootMapPointer *p)
+static block_number_t eckd_block_num(EckdCHS *chs)
 {
-const uint64_t head = p->eckd.chs.head & 0x000f;
+return eckd_chs_to_block(chs->cylinder, chs->head, chs->sector);
+}
+
+static block_number_t gen_eckd_block_num(ExtEckdBlockPtr *ptr, bool ldipl)
+{
+uint64_t cyl, head, sec;
+eckd_format_chs(ptr, ldipl, &cyl, &head, &sec);
+return eckd_chs_to_block(cyl, head, sec);
+}
 
+static bool eckd_valid_chs(uint64_t cyl, uint64_t head, uint64_t sector)
+{
 if (head >= virtio_get_heads()
-||  p->eckd.chs.sector > virtio_get_sectors()
-||  p->eckd.chs.sector <= 0) {
+|| sector > virtio_get_sectors()
+|| sector <= 0) {
 return false;
 }
 
 if (!virtio_guessed_disk_nature() &&
-eckd_block_num(&p->eckd.chs) >= virtio_get_blocks()) {
+eckd_chs_to_block(cyl, head, sector) >= virtio_get_blocks()) {
 return false;
 }
 
 return true;
 }
 
-static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address)
+static bool eckd_valid_address(ExtEckdBlockPtr *ptr, bool ldipl)
+{
+uint64_t cyl, head, sec;
+eckd_format_chs(ptr, ldipl, &cyl, &head, &sec);
+return eckd_valid_chs(cyl, head, sec);
+}
+
+static block_number_t load_eckd_segments(block_number_t blk, bool ldipl,
+ uint64_t *address)
 {
 block_number_t block_nr;
-int j, rc;
+int j, rc, count;
 BootMapPointer *bprs = (void *)_bprs;
 bool more_data;
 
@@ -117,7 +149,7 @@ static block_number_t load_eckd_segments(block_number_t 
blk, uint64_t *address)
 do {
 more_data = false;
 for (j = 0;; j++) {
-block_nr = eckd_block_num(&bprs[j].xeckd.bptr.chs);
+block_nr = gen_eckd_block_num(&bprs[j].xeckd, ldipl);
 if (is_null_block_number(block_nr)) { /* end of chunk */
 break;
 }
@@ -129,11 +161,26 @@ static block_number_t load_eckd_segments(block_number_t 
blk, uint64_t *address)
 break;
 }
 
-IPL_assert(block_size_ok(bprs[j].xeckd.bptr.size),
+/* List directed pointer does not store block size */
+IPL_assert(ldipl || block_size_ok(bprs[j].xeckd.bptr.size),
"bad chunk block size");
-IPL_assert(eckd_valid_address(&bprs[j]), "bad chunk ECKD addr");
 
-if ((bprs[j].xeckd.bptr.count == 0) && unused_

[PATCH 5/5] s390x: Enable and document boot device fallback on panic

2024-05-29 Thread jrossi
From: Jared Rossi 

On a panic during IPL (i.e. a device failed to boot) check for another device
to boot from, as indicated by the presence of an unused IPLB.

If an IPLB is successfully loaded, then jump to the start of BIOS, restarting
IPL using the updated IPLB.  Otherwise enter disabled wait.

Signed-off-by: Jared Rossi 
---
 docs/system/bootindex.rst | 7 ---
 docs/system/s390x/bootdevices.rst | 9 ++---
 pc-bios/s390-ccw/s390-ccw.h   | 6 ++
 3 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/docs/system/bootindex.rst b/docs/system/bootindex.rst
index 8b057f812f..de597561bd 100644
--- a/docs/system/bootindex.rst
+++ b/docs/system/bootindex.rst
@@ -50,9 +50,10 @@ Limitations
 
 Some firmware has limitations on which devices can be considered for
 booting.  For instance, the PC BIOS boot specification allows only one
-disk to be bootable.  If boot from disk fails for some reason, the BIOS
-won't retry booting from other disk.  It can still try to boot from
-floppy or net, though.
+disk to be bootable, except for on s390x machines. If boot from disk fails for
+some reason, the BIOS won't retry booting from other disk.  It can still try to
+boot from floppy or net, though.  In the case of s390x, the BIOS will try up to
+8 total devices, any number of which may be disks.
 
 Sometimes, firmware cannot map the device path QEMU wants firmware to
 boot from to a boot method.  It doesn't happen for devices the firmware
diff --git a/docs/system/s390x/bootdevices.rst 
b/docs/system/s390x/bootdevices.rst
index 1a7a18b43b..f096e1cc06 100644
--- a/docs/system/s390x/bootdevices.rst
+++ b/docs/system/s390x/bootdevices.rst
@@ -6,9 +6,7 @@ Booting with bootindex parameter
 
 For classical mainframe guests (i.e. LPAR or z/VM installations), you always
 have to explicitly specify the disk where you want to boot from (or "IPL" from,
-in s390x-speak -- IPL means "Initial Program Load"). In particular, there can
-also be only one boot device according to the architecture specification, thus
-specifying multiple boot devices is not possible (yet).
+in s390x-speak -- IPL means "Initial Program Load").
 
 So for booting an s390x guest in QEMU, you should always mark the
 device where you want to boot from with the ``bootindex`` property, for
@@ -17,6 +15,11 @@ example::
  qemu-system-s390x -drive if=none,id=dr1,file=guest.qcow2 \
-device virtio-blk,drive=dr1,bootindex=1
 
+Multiple devices may have a bootindex. The lowest bootindex is assigned to the
+device to IPL first.  If the IPL fails for the first, the device with the 
second
+lowest bootindex will be tried and so on until IPL is successful or there are 
no
+remaining boot devices to try.
+
 For booting from a CD-ROM ISO image (which needs to include El-Torito boot
 information in order to be bootable), it is recommended to specify a 
``scsi-cd``
 device, for example like this::
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index c977a52b50..de3d1f0d5a 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -43,6 +43,7 @@ typedef unsigned long long u64;
 #include "iplb.h"
 
 /* start.s */
+extern char _start[];
 void disabled_wait(void) __attribute__ ((__noreturn__));
 void consume_sclp_int(void);
 void consume_io_int(void);
@@ -88,6 +89,11 @@ __attribute__ ((__noreturn__))
 static inline void panic(const char *string)
 {
 sclp_print(string);
+if (load_next_iplb()) {
+sclp_print("\nTrying next boot device...");
+jump_to_IPL_code((long)_start);
+}
+
 disabled_wait();
 }
 
-- 
2.45.1



[PATCH 2/5] s390x: Add loadparm to CcwDevice

2024-05-29 Thread jrossi
From: Jared Rossi 

Add a loadparm property to the CcwDevice object so that different loadparms
can be defined on a per-device basis when using multiple boot devices.

The machine/global loadparm is still supported. If both a global and per-device
loadparm are defined, the per-device value will override the global value for
that device, but any other devices that do not specify a per-device loadparm
will still use the global loadparm.

Assigning a loadparm to a non-boot device is invalid and will be rejected.

Signed-off-by: Jared Rossi 
---
 hw/s390x/ccw-device.h  |  2 ++
 hw/s390x/ipl.h |  3 +-
 hw/s390x/ccw-device.c  | 49 
 hw/s390x/ipl.c | 67 +++---
 hw/s390x/s390-virtio-ccw.c | 18 +-
 hw/s390x/sclp.c|  3 +-
 pc-bios/s390-ccw/main.c| 10 --
 7 files changed, 104 insertions(+), 48 deletions(-)

diff --git a/hw/s390x/ccw-device.h b/hw/s390x/ccw-device.h
index 6dff95225d..35ccf1f7bb 100644
--- a/hw/s390x/ccw-device.h
+++ b/hw/s390x/ccw-device.h
@@ -26,6 +26,8 @@ struct CcwDevice {
 CssDevId dev_id;
 /* The actual busid of the virtual subchannel. */
 CssDevId subch_id;
+/* If set, use this loadparm value when device is boot target */
+uint8_t loadparm[8];
 };
 typedef struct CcwDevice CcwDevice;
 
diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index b066d9e8e5..1dcb8984bb 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -21,7 +21,8 @@
 
 #define DIAG308_FLAGS_LP_VALID 0x80
 
-int s390_ipl_set_loadparm(uint8_t *loadparm);
+void s390_ipl_set_loadparm(char *ascii_lp, uint8_t *ebcdic_lp);
+void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp);
 void s390_ipl_update_diag308(IplParameterBlock *iplb);
 int s390_ipl_prepare_pv_header(Error **errp);
 int s390_ipl_pv_unpack(void);
diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c
index fb8c1acc64..143e085279 100644
--- a/hw/s390x/ccw-device.c
+++ b/hw/s390x/ccw-device.c
@@ -13,6 +13,10 @@
 #include "ccw-device.h"
 #include "hw/qdev-properties.h"
 #include "qemu/module.h"
+#include "ipl.h"
+#include "qapi/visitor.h"
+#include "qemu/ctype.h"
+#include "qapi/error.h"
 
 static void ccw_device_refill_ids(CcwDevice *dev)
 {
@@ -36,10 +40,55 @@ static void ccw_device_realize(CcwDevice *dev, Error **errp)
 ccw_device_refill_ids(dev);
 }
 
+static void ccw_device_get_loadparm(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+CcwDevice *dev = CCW_DEVICE(obj);
+char *str = g_strndup((char *) dev->loadparm, sizeof(dev->loadparm));
+
+visit_type_str(v, name, &str, errp);
+g_free(str);
+}
+
+static void ccw_device_set_loadparm(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+CcwDevice *dev = CCW_DEVICE(obj);
+char *val;
+int index;
+
+index = object_property_get_int(obj, "bootindex", &error_abort);
+
+if (index < 0) {
+error_setg(errp, "LOADPARM: non-boot device");
+}
+
+if (!visit_type_str(v, name, &val, errp)) {
+return;
+}
+
+s390_ipl_fmt_loadparm(dev->loadparm, val, errp);
+}
+
+static const PropertyInfo ccw_loadparm = {
+.name  = "ccw_loadparm",
+.description = "Up to 8 chars in set of [A-Za-z0-9. ] (lower case"
+" chars converted to upper case) to pass to machine loader,"
+" boot manager, and guest kernel",
+.get = ccw_device_get_loadparm,
+.set = ccw_device_set_loadparm,
+};
+
+#define DEFINE_PROP_CCW_LOADPARM(_n, _s, _f) \
+DEFINE_PROP(_n, _s, _f, ccw_loadparm, typeof(uint8_t[8]))
+
 static Property ccw_device_properties[] = {
 DEFINE_PROP_CSS_DEV_ID("devno", CcwDevice, devno),
 DEFINE_PROP_CSS_DEV_ID_RO("dev_id", CcwDevice, dev_id),
 DEFINE_PROP_CSS_DEV_ID_RO("subch_id", CcwDevice, subch_id),
+DEFINE_PROP_CCW_LOADPARM("loadparm", CcwDevice, loadparm),
 DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index e934bf89d1..2d4f5152b3 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -34,6 +34,7 @@
 #include "qemu/config-file.h"
 #include "qemu/cutils.h"
 #include "qemu/option.h"
+#include "qemu/ctype.h"
 #include "standard-headers/linux/virtio_ids.h"
 
 #define KERN_IMAGE_START0x01UL
@@ -390,12 +391,44 @@ static CcwDevice *s390_get_ccw_device(DeviceState 
*dev_st, int *devtype)
 return ccw_dev;
 }
 
+void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp)
+{
+int i;
+
+/* Initialize the loadparm with spaces */
+memset(loadparm, ' ', LOADPARM_LEN);
+for (i = 0; i < LOADPARM_LEN && str[i]; i++) {
+uint8_t c = qemu_toupper(str[i]); /* mimic HMC */
+
+if (('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || (c == '.') ||
+(c == ' ')) {
+loadparm[i] = c;
+} else {
+

[PATCH 3/5] s390x: Build IPLB chain for multiple boot devices

2024-05-29 Thread jrossi
From: Jared Rossi 

Write a chain of IPLBs into memory for future use.

The IPLB chain is placed immediately before the BIOS in memory at the highest
unused page boundary providing sufficient space to fit the chain. Because this
is not a fixed address, the location of the next IPLB and number of remaining
boot devices is stored in the QIPL global variable for later access.

At this stage the IPLB chain is not accessed by the guest during IPL.

Signed-off-by: Jared Rossi 
---
 hw/s390x/ipl.h  |   1 +
 include/hw/s390x/ipl/qipl.h |   4 +-
 hw/s390x/ipl.c  | 129 +++-
 3 files changed, 103 insertions(+), 31 deletions(-)

diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index 1dcb8984bb..4f098d3a81 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -20,6 +20,7 @@
 #include "qom/object.h"
 
 #define DIAG308_FLAGS_LP_VALID 0x80
+#define MAX_IPLB_CHAIN 7
 
 void s390_ipl_set_loadparm(char *ascii_lp, uint8_t *ebcdic_lp);
 void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp);
diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
index a6ce6ddfe3..481c459a53 100644
--- a/include/hw/s390x/ipl/qipl.h
+++ b/include/hw/s390x/ipl/qipl.h
@@ -34,7 +34,9 @@ struct QemuIplParameters {
 uint8_t  reserved1[3];
 uint64_t netboot_start_addr;
 uint32_t boot_menu_timeout;
-uint8_t  reserved2[12];
+uint8_t  reserved2[2];
+uint16_t num_iplbs;
+uint64_t next_iplb;
 }  QEMU_PACKED;
 typedef struct QemuIplParameters QemuIplParameters;
 
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 2d4f5152b3..79429acabd 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -55,6 +55,13 @@ static bool iplb_extended_needed(void *opaque)
 return ipl->iplbext_migration;
 }
 
+/* Start IPLB chain from the boundary of the first unused page before BIOS */
+static uint64_t find_iplb_chain_addr(uint64_t bios_addr, uint16_t count)
+{
+return (bios_addr & TARGET_PAGE_MASK)
+- (count * sizeof(IplParameterBlock));
+}
+
 static const VMStateDescription vmstate_iplb_extended = {
 .name = "ipl/iplb_extended",
 .version_id = 0,
@@ -391,6 +398,17 @@ static CcwDevice *s390_get_ccw_device(DeviceState *dev_st, 
int *devtype)
 return ccw_dev;
 }
 
+static void s390_ipl_map_iplb_chain(IplParameterBlock *iplb_chain)
+{
+S390IPLState *ipl = get_ipl_device();
+uint16_t count = ipl->qipl.num_iplbs;
+uint64_t len = sizeof(IplParameterBlock) * count;
+uint64_t chain_addr = find_iplb_chain_addr(ipl->bios_start_addr, count);
+
+cpu_physical_memory_write(chain_addr, iplb_chain, be32_to_cpu(len));
+ipl->qipl.next_iplb = chain_addr;
+}
+
 void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp)
 {
 int i;
@@ -422,54 +440,51 @@ void s390_ipl_set_loadparm(char *ascii_lp, uint8_t 
*ebcdic_lp)
 }
 }
 
-static bool s390_gen_initial_iplb(S390IPLState *ipl)
+static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
 {
-DeviceState *dev_st;
+S390IPLState *ipl = get_ipl_device();
 CcwDevice *ccw_dev = NULL;
 SCSIDevice *sd;
 int devtype;
 uint8_t *lp;
 
-dev_st = get_boot_device(0);
-if (dev_st) {
-ccw_dev = s390_get_ccw_device(dev_st, &devtype);
-}
-
 /*
  * Currently allow IPL only from CCW devices.
  */
+ccw_dev = s390_get_ccw_device(dev_st, &devtype);
 if (ccw_dev) {
 lp = ccw_dev->loadparm;
 
-switch (devtype) {
-case CCW_DEVTYPE_SCSI:
+ switch (devtype) {
+ case CCW_DEVTYPE_SCSI:
 sd = SCSI_DEVICE(dev_st);
-ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN);
-ipl->iplb.blk0_len =
+iplb->len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN);
+iplb->blk0_len =
 cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN - 
S390_IPLB_HEADER_LEN);
-ipl->iplb.pbt = S390_IPL_TYPE_QEMU_SCSI;
-ipl->iplb.scsi.lun = cpu_to_be32(sd->lun);
-ipl->iplb.scsi.target = cpu_to_be16(sd->id);
-ipl->iplb.scsi.channel = cpu_to_be16(sd->channel);
-ipl->iplb.scsi.devno = cpu_to_be16(ccw_dev->sch->devno);
-ipl->iplb.scsi.ssid = ccw_dev->sch->ssid & 3;
+iplb->pbt = S390_IPL_TYPE_QEMU_SCSI;
+iplb->scsi.lun = cpu_to_be32(sd->lun);
+iplb->scsi.target = cpu_to_be16(sd->id);
+iplb->scsi.channel = cpu_to_be16(sd->channel);
+iplb->scsi.devno = cpu_to_be16(ccw_dev->sch->devno);
+iplb->scsi.ssid = ccw_dev->sch->ssid & 3;
 break;
 case CCW_DEVTYPE_VFIO:
-ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
-ipl->iplb.pbt = S390_IPL_TYPE_CCW;
-ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
-ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3;
+iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
+iplb->pbt = S390_IPL_TYPE_CCW;
+ 

[PATCH 0/5] s390x: Add Full Boot Order Support

2024-05-29 Thread jrossi
From: Jared Rossi 

This patch set primarily adds support for the specification of multiple boot
devices, allowing for the guest to automatically use an alternative device on
a failed boot without needing to be reconfigured. It additionally provides the
ability to define the loadparm attribute on a per-device bases, which allows
boot devices to use different loadparm values if needed.

In brief, an IPLB is generated for each designated boot device (up to a maximum
of 8) and stored in guest memory immediately before BIOS. If a device fails to
boot, the next IPLB is retrieved and we jump back to the start of BIOS.

Devices can be specified using the standard qemu device tag "bootindex" as with
other architectures. Lower number indices are tried first, with "bootindex=0"
indicating the first device to try.

A subsequent Libvirt patch will be necessary to allow assignment of per-device
loadparms in the guest XML

Jared Rossi (5):
  Create include files for s390x IPL definitions
  Add loadparm to CcwDevice
  Build IPLB chain for multiple boot devices
  Add boot device fallback infrastructure
  Enable and document boot device fallback on panic

 docs/system/bootindex.rst |   7 +-
 docs/system/s390x/bootdevices.rst |   9 +-
 hw/s390x/ccw-device.h |   2 +
 hw/s390x/ipl.h| 117 +---
 include/hw/s390x/ipl/qipl.h   | 128 ++
 pc-bios/s390-ccw/bootmap.h|   5 +
 pc-bios/s390-ccw/iplb.h   | 108 +--
 pc-bios/s390-ccw/s390-ccw.h   |   6 ++
 hw/s390x/ccw-device.c |  49 +
 hw/s390x/ipl.c| 170 ++
 hw/s390x/s390-virtio-ccw.c|  18 +---
 hw/s390x/sclp.c   |   3 +-
 pc-bios/s390-ccw/bootmap.c|  41 ---
 pc-bios/s390-ccw/main.c   |  25 +++--
 pc-bios/s390-ccw/netmain.c|   4 +
 pc-bios/s390-ccw/Makefile |   2 +-
 16 files changed, 413 insertions(+), 281 deletions(-)
 create mode 100644 include/hw/s390x/ipl/qipl.h

-- 
2.45.1




[PATCH 1/5] s390x: Create include files for s390x IPL definitions

2024-05-29 Thread jrossi
From: Jared Rossi 

Currently, stuctures defined in both hw/s390x/ipl.h and pc-bios/s390-ccw/iplb.h
must be kept in sync, which is prone to error. Instead, create a new directory
at include/hw/s390x/ipl/ to contain the definitions that must be shared.

Signed-off-by: Jared Rossi 
---
 hw/s390x/ipl.h  | 113 +---
 include/hw/s390x/ipl/qipl.h | 126 
 pc-bios/s390-ccw/iplb.h |  84 ++--
 pc-bios/s390-ccw/Makefile   |   2 +-
 4 files changed, 133 insertions(+), 192 deletions(-)
 create mode 100644 include/hw/s390x/ipl/qipl.h

diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index 57cd125769..b066d9e8e5 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -16,95 +16,11 @@
 #include "cpu.h"
 #include "exec/address-spaces.h"
 #include "hw/qdev-core.h"
+#include "hw/s390x/ipl/qipl.h"
 #include "qom/object.h"
 
-struct IPLBlockPVComp {
-uint64_t tweak_pref;
-uint64_t addr;
-uint64_t size;
-} QEMU_PACKED;
-typedef struct IPLBlockPVComp IPLBlockPVComp;
-
-struct IPLBlockPV {
-uint8_t  reserved18[87];/* 0x18 */
-uint8_t  version;   /* 0x6f */
-uint32_t reserved70;/* 0x70 */
-uint32_t num_comp;  /* 0x74 */
-uint64_t pv_header_addr;/* 0x78 */
-uint64_t pv_header_len; /* 0x80 */
-struct IPLBlockPVComp components[0];
-} QEMU_PACKED;
-typedef struct IPLBlockPV IPLBlockPV;
-
-struct IplBlockCcw {
-uint8_t  reserved0[85];
-uint8_t  ssid;
-uint16_t devno;
-uint8_t  vm_flags;
-uint8_t  reserved3[3];
-uint32_t vm_parm_len;
-uint8_t  nss_name[8];
-uint8_t  vm_parm[64];
-uint8_t  reserved4[8];
-} QEMU_PACKED;
-typedef struct IplBlockCcw IplBlockCcw;
-
-struct IplBlockFcp {
-uint8_t  reserved1[305 - 1];
-uint8_t  opt;
-uint8_t  reserved2[3];
-uint16_t reserved3;
-uint16_t devno;
-uint8_t  reserved4[4];
-uint64_t wwpn;
-uint64_t lun;
-uint32_t bootprog;
-uint8_t  reserved5[12];
-uint64_t br_lba;
-uint32_t scp_data_len;
-uint8_t  reserved6[260];
-uint8_t  scp_data[0];
-} QEMU_PACKED;
-typedef struct IplBlockFcp IplBlockFcp;
-
-struct IplBlockQemuScsi {
-uint32_t lun;
-uint16_t target;
-uint16_t channel;
-uint8_t  reserved0[77];
-uint8_t  ssid;
-uint16_t devno;
-} QEMU_PACKED;
-typedef struct IplBlockQemuScsi IplBlockQemuScsi;
-
 #define DIAG308_FLAGS_LP_VALID 0x80
 
-union IplParameterBlock {
-struct {
-uint32_t len;
-uint8_t  reserved0[3];
-uint8_t  version;
-uint32_t blk0_len;
-uint8_t  pbt;
-uint8_t  flags;
-uint16_t reserved01;
-uint8_t  loadparm[8];
-union {
-IplBlockCcw ccw;
-IplBlockFcp fcp;
-IPLBlockPV pv;
-IplBlockQemuScsi scsi;
-};
-} QEMU_PACKED;
-struct {
-uint8_t  reserved1[110];
-uint16_t devno;
-uint8_t  reserved2[88];
-uint8_t  reserved_ext[4096 - 200];
-} QEMU_PACKED;
-} QEMU_PACKED;
-typedef union IplParameterBlock IplParameterBlock;
-
 int s390_ipl_set_loadparm(uint8_t *loadparm);
 void s390_ipl_update_diag308(IplParameterBlock *iplb);
 int s390_ipl_prepare_pv_header(Error **errp);
@@ -125,33 +41,6 @@ void s390_ipl_reset_request(CPUState *cs, enum s390_reset 
reset_type);
 void s390_ipl_get_reset_request(CPUState **cs, enum s390_reset *reset_type);
 void s390_ipl_clear_reset_request(void);
 
-#define QIPL_ADDRESS  0xcc
-
-/* Boot Menu flags */
-#define QIPL_FLAG_BM_OPTS_CMD   0x80
-#define QIPL_FLAG_BM_OPTS_ZIPL  0x40
-
-/*
- * The QEMU IPL Parameters will be stored at absolute address
- * 204 (0xcc) which means it is 32-bit word aligned but not
- * double-word aligned.
- * Placement of data fields in this area must account for
- * their alignment needs. E.g., netboot_start_address must
- * have an offset of 4 + n * 8 bytes within the struct in order
- * to keep it double-word aligned.
- * The total size of the struct must never exceed 28 bytes.
- * This definition must be kept in sync with the definition
- * in pc-bios/s390-ccw/iplb.h.
- */
-struct QemuIplParameters {
-uint8_t  qipl_flags;
-uint8_t  reserved1[3];
-uint64_t netboot_start_addr;
-uint32_t boot_menu_timeout;
-uint8_t  reserved2[12];
-} QEMU_PACKED;
-typedef struct QemuIplParameters QemuIplParameters;
-
 #define TYPE_S390_IPL "s390-ipl"
 OBJECT_DECLARE_SIMPLE_TYPE(S390IPLState, S390_IPL)
 
diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
new file mode 100644
index 00..a6ce6ddfe3
--- /dev/null
+++ b/include/hw/s390x/ipl/qipl.h
@@ -0,0 +1,126 @@
+/*
+ * S/390 boot structures
+ *
+ * Copyright 2024 IBM Corp.
+ * Author(s): Jared Rossi 
+ *
+ * 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.
+ */
+
+#ifndef S390X_QIPL_H
+#define S390X_QIPL_H
+

[PATCH 4/5] s390x: Add boot device fallback infrastructure

2024-05-29 Thread jrossi
From: Jared Rossi 

Add a routine for loading the next IPLB if a device fails to boot.

This includes some minor changes to the List-Directed IPL routine so that the
failing device may be retried using the legacy boot pointers before moving on to
the next device.

Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/bootmap.h |  5 +
 pc-bios/s390-ccw/iplb.h| 24 ++
 pc-bios/s390-ccw/bootmap.c | 41 ++
 pc-bios/s390-ccw/main.c| 15 +-
 pc-bios/s390-ccw/netmain.c |  4 
 5 files changed, 71 insertions(+), 18 deletions(-)

diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
index d4690a88c2..d5061ed6c8 100644
--- a/pc-bios/s390-ccw/bootmap.h
+++ b/pc-bios/s390-ccw/bootmap.h
@@ -366,6 +366,11 @@ static inline void read_block(block_number_t blockno,
 IPL_assert(virtio_read(blockno, buffer) == 0, errmsg);
 }
 
+static inline bool read_block_nonfatal(block_number_t blockno, void *buffer)
+{
+return (virtio_read(blockno, buffer) == 0);
+}
+
 static inline bool block_size_ok(uint32_t block_size)
 {
 return block_size == virtio_get_block_size();
diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h
index 16643f5879..3c29d23375 100644
--- a/pc-bios/s390-ccw/iplb.h
+++ b/pc-bios/s390-ccw/iplb.h
@@ -49,4 +49,28 @@ static inline bool set_iplb(IplParameterBlock *iplb)
 return manage_iplb(iplb, false);
 }
 
+/*
+ * The IPL started on the device, but failed in some way.  If the IPLB chain
+ * still has more devices left to try, use the next device in order. Set the
+ * next IPLB and save the current qipl parameters state.
+ */
+static inline bool load_next_iplb(void)
+{
+IplParameterBlock *next_iplb;
+
+if (qipl.num_iplbs < 1) {
+return false;
+}
+
+next_iplb = (IplParameterBlock *) qipl.next_iplb;
+memcpy(&iplb, next_iplb, sizeof(IplParameterBlock));
+set_iplb(&iplb);
+
+qipl.num_iplbs--;
+qipl.next_iplb = qipl.next_iplb + sizeof(IplParameterBlock);
+memcpy((QemuIplParameters *)QIPL_ADDRESS, &qipl, 
sizeof(QemuIplParameters));
+
+return true;
+}
+
 #endif /* IPLB_H */
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index a2137449dc..69391557fa 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -144,7 +144,10 @@ static block_number_t load_eckd_segments(block_number_t 
blk, bool ldipl,
 bool more_data;
 
 memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs));
-read_block(blk, bprs, "BPRS read failed");
+if (!read_block_nonfatal(blk, bprs)) {
+IPL_assert(ldipl, "BPRS read failed");
+return -1;
+}
 
 do {
 more_data = false;
@@ -188,7 +191,10 @@ static block_number_t load_eckd_segments(block_number_t 
blk, bool ldipl,
  * I.e. the next ptr must point to the unused memory area
  */
 memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs));
-read_block(block_nr, bprs, "BPRS continuation read failed");
+if (!read_block_nonfatal(block_nr, bprs)) {
+IPL_assert(ldipl, "BPRS continuation read failed");
+break;
+}
 more_data = true;
 break;
 }
@@ -197,7 +203,10 @@ static block_number_t load_eckd_segments(block_number_t 
blk, bool ldipl,
  * to memory (address).
  */
 rc = virtio_read_many(block_nr, (void *)(*address), count + 1);
-IPL_assert(rc == 0, "code chunk read failed");
+if (rc != 0) {
+IPL_assert(ldipl, "code chunk read failed");
+break;
+}
 
 *address += (count + 1) * virtio_get_block_size();
 }
@@ -295,13 +304,22 @@ static void run_eckd_boot_script(block_number_t 
bmt_block_nr,
" maximum number of boot entries allowed");
 
 memset(sec, FREE_SPACE_FILLER, sizeof(sec));
-read_block(bmt_block_nr, sec, "Cannot read Boot Map Table");
+if (!read_block_nonfatal(bmt_block_nr, sec)) {
+IPL_assert(ldipl, "Cannot read Boot Map Table");
+return;
+}
 
 block_nr = gen_eckd_block_num(&bmt->entry[loadparm].xeckd, ldipl);
-IPL_assert(block_nr != -1, "Cannot find Boot Map Table Entry");
+if (block_nr == -1) {
+IPL_assert(ldipl, "Cannot find Boot Map Table Entry");
+return;
+}
 
 memset(sec, FREE_SPACE_FILLER, sizeof(sec));
-read_block(block_nr, sec, "Cannot read Boot Map Script");
+if (!read_block_nonfatal(block_nr, sec)) {
+IPL_assert(ldipl, "Cannot read Boot Map Script");
+return;
+}
 
 for (i = 0; bms->entry[i].type == BOOT_SCRIPT_LOAD ||
 bms->entry[i].type == BOOT_SCRIPT_SIGNATURE; i++) {
@@ -319,13 +337,10 @@ static void run_eckd_boot_script(block_number_t 
bmt_block_nr,
 } while (block_nr != -1);
 }
 
-if (ldipl && bms->entry[i].type != BOOT_SCRIPT_E

[PATCH v3 05/19] pc-bios/s390-ccw: Merge netboot.mak into the main Makefile

2024-10-07 Thread jrossi
From: Jared Rossi 

Now that the netboot code has been merged into the main s390-ccw.img,
it also does not make sense to keep the build rules in a separate
file. Thus let's merge netboot.mak into the main Makefile.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/netboot.mak |  45 -
 pc-bios/s390-ccw/Makefile|  47 ++-
 pc-bios/s390-netboot.img | Bin 67232 -> 0 bytes
 3 files changed, 46 insertions(+), 46 deletions(-)
 delete mode 100644 pc-bios/s390-ccw/netboot.mak
 delete mode 100644 pc-bios/s390-netboot.img

diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak
deleted file mode 100644
index 0a24257ff4..00
--- a/pc-bios/s390-ccw/netboot.mak
+++ /dev/null
@@ -1,45 +0,0 @@
-
-# libc files:
-
-LIBC_CFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
- -MMD -MP -MT $@ -MF $(@:%.o=%.d)
-
-CTYPE_OBJS = isdigit.o isxdigit.o toupper.o
-%.o : $(SLOF_DIR)/lib/libc/ctype/%.c
-   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
-
-STRING_OBJS = strcat.o strchr.o strrchr.o strcpy.o strlen.o strncpy.o \
- strcmp.o strncmp.o strcasecmp.o strncasecmp.o strstr.o \
- memset.o memcpy.o memmove.o memcmp.o
-%.o : $(SLOF_DIR)/lib/libc/string/%.c
-   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
-
-STDLIB_OBJS = atoi.o atol.o strtoul.o strtol.o rand.o malloc.o free.o
-%.o : $(SLOF_DIR)/lib/libc/stdlib/%.c
-   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
-
-STDIO_OBJS = sprintf.o snprintf.o vfprintf.o vsnprintf.o vsprintf.o fprintf.o \
-printf.o putc.o puts.o putchar.o stdchnls.o fileno.o
-%.o : $(SLOF_DIR)/lib/libc/stdio/%.c
-   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
-
-sbrk.o: $(SLOF_DIR)/slof/sbrk.c
-   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
-
-LIBCOBJS := $(STRING_OBJS) $(CTYPE_OBJS) $(STDLIB_OBJS) $(STDIO_OBJS) sbrk.o
-
-libc.a: $(LIBCOBJS)
-   $(call quiet-command,$(AR) -rc $@ $^,Creating static library)
-
-# libnet files:
-
-LIBNETOBJS := args.o dhcp.o dns.o icmpv6.o ipv6.o tcp.o udp.o bootp.o \
- dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o pxelinux.o
-LIBNETCFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
-  -DDHCPARCH=0x1F -MMD -MP -MT $@ -MF $(@:%.o=%.d)
-
-%.o : $(SLOF_DIR)/lib/libnet/%.c
-   $(call quiet-command,$(CC) $(LIBNETCFLAGS) -c -o $@ $<,Compiling)
-
-libnet.a: $(LIBNETOBJS)
-   $(call quiet-command,$(AR) -rc $@ $^,Creating static library)
diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
index cf6859823a..27cbb354af 100644
--- a/pc-bios/s390-ccw/Makefile
+++ b/pc-bios/s390-ccw/Makefile
@@ -61,7 +61,52 @@ config-cc.mak: Makefile
$(call cc-option,-march=z900,-march=z10)) 3> config-cc.mak
 -include config-cc.mak
 
-include $(SRC_PATH)/netboot.mak
+# libc files:
+
+LIBC_CFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
+ -MMD -MP -MT $@ -MF $(@:%.o=%.d)
+
+CTYPE_OBJS = isdigit.o isxdigit.o toupper.o
+%.o : $(SLOF_DIR)/lib/libc/ctype/%.c
+   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
+
+STRING_OBJS = strcat.o strchr.o strrchr.o strcpy.o strlen.o strncpy.o \
+ strcmp.o strncmp.o strcasecmp.o strncasecmp.o strstr.o \
+ memset.o memcpy.o memmove.o memcmp.o
+%.o : $(SLOF_DIR)/lib/libc/string/%.c
+   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
+
+STDLIB_OBJS = atoi.o atol.o strtoul.o strtol.o rand.o malloc.o free.o
+%.o : $(SLOF_DIR)/lib/libc/stdlib/%.c
+   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
+
+STDIO_OBJS = sprintf.o snprintf.o vfprintf.o vsnprintf.o vsprintf.o fprintf.o \
+printf.o putc.o puts.o putchar.o stdchnls.o fileno.o
+%.o : $(SLOF_DIR)/lib/libc/stdio/%.c
+   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
+
+sbrk.o: $(SLOF_DIR)/slof/sbrk.c
+   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
+
+LIBCOBJS := $(STRING_OBJS) $(CTYPE_OBJS) $(STDLIB_OBJS) $(STDIO_OBJS) sbrk.o
+
+libc.a: $(LIBCOBJS)
+   $(call quiet-command,$(AR) -rc $@ $^,Creating static library)
+
+# libnet files:
+
+LIBNETOBJS := args.o dhcp.o dns.o icmpv6.o ipv6.o tcp.o udp.o bootp.o \
+ dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o pxelinux.o
+LIBNETCFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
+  -DDHCPARCH=0x1F -MMD -MP -MT $@ -MF $(@:%.o=%.d)
+
+%.o : $(SLOF_DIR)/lib/libnet/%.c
+   $(call quiet-command,$(CC) $(LIBNETCFLAGS) -c -o $@ $<,Compiling)
+
+libnet.a: $(LIBNETOBJS)
+   $(call quiet-command,$(AR) -rc $@ $^,Creating static library)
+
+# Main targets:
 
 build-all: s390-ccw.img
 
diff --git a/pc-bios/s390-netboot.img b/pc-bios/s390-netboot.img
deleted file mode 100644
index 
6908e49f06801808b826d3a01f88132cf1b2f57c..00

[PATCH v3 01/19] hw/s390x/ipl: Provide more memory to the s390-ccw.img firmware

2024-10-07 Thread jrossi
From: Jared Rossi 

We are going to link the SLOF libc into the s390-ccw.img, and this
libc needs more memory for providing space for malloc() and friends.
Thus bump the memory size that we reserve for the bios to 3 MiB
instead of only 2 MiB. While we're at it, add a proper check that
there is really enough memory assigned to the machine before blindly
using it.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 
---
 hw/s390x/ipl.c | 10 +-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 5ab7433908..5f60977ceb 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -45,6 +45,7 @@
 #define INITRD_PARM_START   0x010408UL
 #define PARMFILE_START  0x001000UL
 #define ZIPL_IMAGE_START0x009000UL
+#define BIOS_MAX_SIZE   0x30UL
 #define IPL_PSW_MASK(PSW_MASK_32 | PSW_MASK_64)
 
 static bool iplb_extended_needed(void *opaque)
@@ -144,7 +145,14 @@ static void s390_ipl_realize(DeviceState *dev, Error 
**errp)
  * even if an external kernel has been defined.
  */
 if (!ipl->kernel || ipl->enforce_bios) {
-uint64_t fwbase = (MIN(ms->ram_size, 0x8000U) - 0x20) & 
~0xUL;
+uint64_t fwbase;
+
+if (ms->ram_size < BIOS_MAX_SIZE) {
+error_setg(errp, "not enough RAM to load the BIOS file");
+return;
+}
+
+fwbase = (MIN(ms->ram_size, 0x8000U) - BIOS_MAX_SIZE) & ~0xUL;
 
 bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->firmware);
 if (bios_filename == NULL) {
-- 
2.45.1




[PATCH v3 07/19] pc-bios/s390-ccw: Remove panics from ISO IPL path

2024-10-07 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from IPL ISO El Torito specific functions so that error
recovery may be possible in the future.

Functions that would previously panic now provide a return code.

Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/bootmap.h  | 15 +++
 pc-bios/s390-ccw/s390-ccw.h |  1 +
 pc-bios/s390-ccw/bootmap.c  | 78 -
 3 files changed, 59 insertions(+), 35 deletions(-)

diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
index bbe2c132aa..d639258eca 100644
--- a/pc-bios/s390-ccw/bootmap.h
+++ b/pc-bios/s390-ccw/bootmap.h
@@ -385,17 +385,14 @@ static inline uint32_t iso_733_to_u32(uint64_t x)
 
 #define ISO_PRIMARY_VD_SECTOR 16
 
-static inline void read_iso_sector(uint32_t block_offset, void *buf,
-   const char *errmsg)
-{
-IPL_assert(virtio_read_many(block_offset, buf, 1) == 0, errmsg);
-}
-
-static inline void read_iso_boot_image(uint32_t block_offset, void *load_addr,
+static inline int read_iso_boot_image(uint32_t block_offset, void *load_addr,
uint32_t blks_to_load)
 {
-IPL_assert(virtio_read_many(block_offset, load_addr, blks_to_load) == 0,
-   "Failed to read boot image!");
+if (virtio_read_many(block_offset, load_addr, blks_to_load)) {
+puts("Failed to read boot image!");
+return 1;
+}
+return 0;
 }
 
 #define ISO9660_MAX_DIR_DEPTH 8
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 0ed7eb8ade..cbd92f3671 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -30,6 +30,7 @@ typedef unsigned long long u64;
 #define EIO 1
 #define EBUSY   2
 #define ENODEV  3
+#define EINVAL  4
 
 #ifndef MIN
 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 414c3f1b47..7984de62fe 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -678,8 +678,10 @@ static bool is_iso_bc_entry_compatible(IsoBcSection *s)
 if (s->unused || !s->sector_count) {
 return false;
 }
-read_iso_sector(bswap32(s->load_rba), magic_sec,
-"Failed to read image sector 0");
+if (virtio_read(bswap32(s->load_rba), magic_sec)) {
+puts("Failed to read image sector 0");
+return false;
+}
 
 /* Checking bytes 8 - 32 for S390 Linux magic */
 return !memcmp(magic_sec + 8, linux_s390_magic, 24);
@@ -699,21 +701,28 @@ static inline uint32_t iso_get_file_size(uint32_t 
load_rba)
 uint8_t *temp = sec + ISO_SECTOR_SIZE;
 int level = 0;
 
-read_iso_sector(ISO_PRIMARY_VD_SECTOR, sec,
-"Failed to read ISO primary descriptor");
+if (virtio_read(ISO_PRIMARY_VD_SECTOR, sec)) {
+puts("Failed to read ISO primary descriptor");
+return -EIO;
+}
+
 sec_loc[0] = iso_733_to_u32(cur_record->ext_loc);
 dir_rem[0] = 0;
 sec_offset[0] = 0;
 
 while (level >= 0) {
-IPL_assert(sec_offset[level] <= ISO_SECTOR_SIZE,
-   "Directory tree structure violation");
+if (sec_offset[level] > ISO_SECTOR_SIZE) {
+puts("Directory tree structure violation");
+return -EIO;
+}
 
 cur_record = (IsoDirHdr *)(temp + sec_offset[level]);
 
 if (sec_offset[level] == 0) {
-read_iso_sector(sec_loc[level], temp,
-"Failed to read ISO directory");
+if (virtio_read(sec_loc[level], temp)) {
+puts("Failed to read ISO directory");
+return -EIO;
+}
 if (dir_rem[level] == 0) {
 /* Skip self and parent records */
 dir_rem[level] = iso_733_to_u32(cur_record->data_len) -
@@ -758,8 +767,10 @@ static inline uint32_t iso_get_file_size(uint32_t load_rba)
 if (dir_rem[level] == 0) {
 /* Nothing remaining */
 level--;
-read_iso_sector(sec_loc[level], temp,
-"Failed to read ISO directory");
+if (virtio_read(sec_loc[level], temp)) {
+puts("Failed to read ISO directory");
+return -EIO;
+}
 }
 }
 
@@ -784,9 +795,11 @@ static void load_iso_bc_entry(IsoBcSection *load)
 puts("ISO boot image size could not be verified");
 }
 
-read_iso_boot_image(bswap32(s.load_rba),
+if (read_iso_boot_image(bswap32(s.load_rba),
 (void *)((uint64_t)bswap16(s.load_segment)),
-blks_to_load);
+blks_to_load)) {
+return;
+}
 
 jump_to_low_kernel();
 }
@@ -809,17 +822,18 @@ static uint32_t find_iso_bc(void)
 return bswap32(et->bc_offset);
 }
 }
-read_iso_sector(block_num++, sec,
-"Failed to read ISO volume descriptor");
+if (virti

[PATCH v3 14/19] s390x: Add individual loadparm assignment to CCW device

2024-10-07 Thread jrossi
From: Jared Rossi 

Add a loadparm property to the VirtioCcwDevice object so that different
loadparms can be defined on a per-device basis for CCW boot devices.

The machine/global loadparm is still supported. If both a global and per-device
loadparm are defined, the per-device value will override the global value for
that device, but any other devices that do not specify a per-device loadparm
will still use the global loadparm.

It is invalid to assign a loadparm to a non-boot device.

Signed-off-by: Jared Rossi 
Reviewed-by: Thomas Huth 
---
 hw/s390x/ccw-device.h  |  2 ++
 hw/s390x/ipl.h |  3 +-
 hw/s390x/ccw-device.c  | 46 ++
 hw/s390x/ipl.c | 66 +++---
 hw/s390x/s390-virtio-ccw.c | 18 +--
 hw/s390x/sclp.c|  3 +-
 pc-bios/s390-ccw/main.c| 10 --
 7 files changed, 100 insertions(+), 48 deletions(-)

diff --git a/hw/s390x/ccw-device.h b/hw/s390x/ccw-device.h
index 5feeb0ee7a..1e1737c0f3 100644
--- a/hw/s390x/ccw-device.h
+++ b/hw/s390x/ccw-device.h
@@ -26,6 +26,8 @@ struct CcwDevice {
 CssDevId dev_id;
 /* The actual busid of the virtual subchannel. */
 CssDevId subch_id;
+/* If set, use this loadparm value when device is boot target */
+uint8_t loadparm[8];
 };
 typedef struct CcwDevice CcwDevice;
 
diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index fa394c339d..b670bad551 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -21,7 +21,8 @@
 
 #define DIAG308_FLAGS_LP_VALID 0x80
 
-int s390_ipl_set_loadparm(uint8_t *loadparm);
+void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp);
+void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp);
 void s390_ipl_update_diag308(IplParameterBlock *iplb);
 int s390_ipl_prepare_pv_header(Error **errp);
 int s390_ipl_pv_unpack(void);
diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c
index 14c24e3890..230cc09e03 100644
--- a/hw/s390x/ccw-device.c
+++ b/hw/s390x/ccw-device.c
@@ -13,6 +13,10 @@
 #include "ccw-device.h"
 #include "hw/qdev-properties.h"
 #include "qemu/module.h"
+#include "ipl.h"
+#include "qapi/visitor.h"
+#include "qemu/ctype.h"
+#include "qapi/error.h"
 
 static void ccw_device_refill_ids(CcwDevice *dev)
 {
@@ -37,10 +41,52 @@ static bool ccw_device_realize(CcwDevice *dev, Error **errp)
 return true;
 }
 
+static void ccw_device_get_loadparm(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+CcwDevice *dev = CCW_DEVICE(obj);
+char *str = g_strndup((char *) dev->loadparm, sizeof(dev->loadparm));
+
+visit_type_str(v, name, &str, errp);
+g_free(str);
+}
+
+static void ccw_device_set_loadparm(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+CcwDevice *dev = CCW_DEVICE(obj);
+char *val;
+int index;
+
+index = object_property_get_int(obj, "bootindex", NULL);
+
+if (index < 0) {
+error_setg(errp, "LOADPARM is only valid for boot devices!");
+}
+
+if (!visit_type_str(v, name, &val, errp)) {
+return;
+}
+
+s390_ipl_fmt_loadparm(dev->loadparm, val, errp);
+}
+
+static const PropertyInfo ccw_loadparm = {
+.name  = "ccw_loadparm",
+.description = "Up to 8 chars in set of [A-Za-z0-9. ] to pass"
+" to the guest loader/kernel",
+.get = ccw_device_get_loadparm,
+.set = ccw_device_set_loadparm,
+};
+
 static Property ccw_device_properties[] = {
 DEFINE_PROP_CSS_DEV_ID("devno", CcwDevice, devno),
 DEFINE_PROP_CSS_DEV_ID_RO("dev_id", CcwDevice, dev_id),
 DEFINE_PROP_CSS_DEV_ID_RO("subch_id", CcwDevice, subch_id),
+DEFINE_PROP("loadparm", CcwDevice, loadparm, ccw_loadparm,
+typeof(uint8_t[8])),
 DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 8c490eeb52..55a83bb836 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -34,6 +34,7 @@
 #include "qemu/config-file.h"
 #include "qemu/cutils.h"
 #include "qemu/option.h"
+#include "qemu/ctype.h"
 #include "standard-headers/linux/virtio_ids.h"
 
 #define KERN_IMAGE_START0x01UL
@@ -397,12 +398,43 @@ static CcwDevice *s390_get_ccw_device(DeviceState 
*dev_st, int *devtype)
 return ccw_dev;
 }
 
+void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp)
+{
+int i;
+
+/* Initialize the loadparm with spaces */
+memset(loadparm, ' ', LOADPARM_LEN);
+for (i = 0; i < LOADPARM_LEN && str[i]; i++) {
+uint8_t c = qemu_toupper(str[i]); /* mimic HMC */
+
+if (qemu_isalnum(c) || c == '.' || c == ' ') {
+loadparm[i] = c;
+} else {
+error_setg(errp, "LOADPARM: invalid character '%c' (ASCII 0x%02x)",
+   c, c);
+return;
+}
+}
+}
+
+void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic

[PATCH v3 03/19] pc-bios/s390-ccw: Link the netboot code into the main s390-ccw.img binary

2024-10-07 Thread jrossi
From: Jared Rossi 

We originally built a separate binary for the netboot code since it
was considered as experimental and we could not be sure that the
necessary SLOF module had been checked out. Time passed, the code
proved its usefulness, and the build system nowadays makes sure that
the SLOF module is checked out if you have a s390x compiler available
for building the s390-ccw bios. So there is no real compelling reason
anymore to keep the netboot code in a separate binary. Linking the
code together with the main s390-ccw.img will make future enhancements
much easier, like supporting more than one boot device.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/netboot.mak | 14 --
 pc-bios/s390-ccw/cio.h   |  2 ++
 pc-bios/s390-ccw/iplb.h  |  4 ++--
 pc-bios/s390-ccw/s390-ccw.h  |  3 +++
 pc-bios/s390-ccw/virtio.h|  1 -
 pc-bios/s390-ccw/bootmap.c   |  2 +-
 pc-bios/s390-ccw/main.c  | 10 +++---
 pc-bios/s390-ccw/netmain.c   | 15 ++-
 pc-bios/s390-ccw/Makefile| 13 +++--
 9 files changed, 24 insertions(+), 40 deletions(-)

diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak
index d2b3d8ee74..0a24257ff4 100644
--- a/pc-bios/s390-ccw/netboot.mak
+++ b/pc-bios/s390-ccw/netboot.mak
@@ -1,18 +1,4 @@
 
-NETOBJS := start.o sclp.o cio.o virtio.o virtio-net.o jump2ipl.o netmain.o
-
-LIBNET_INC := -I$(SLOF_DIR)/lib/libnet
-
-NETLDFLAGS := $(LDFLAGS) -Wl,-Ttext=0x780
-
-$(NETOBJS): EXTRA_CFLAGS += $(LIBC_INC) $(LIBNET_INC)
-
-s390-netboot.elf: $(NETOBJS) libnet.a libc.a
-   $(call quiet-command,$(CC) $(NETLDFLAGS) -o $@ $^,Linking)
-
-s390-netboot.img: s390-netboot.elf
-   $(call quiet-command,$(STRIP) --strip-unneeded $< -o $@,Stripping $< 
into)
-
 # libc files:
 
 LIBC_CFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
diff --git a/pc-bios/s390-ccw/cio.h b/pc-bios/s390-ccw/cio.h
index 8b18153deb..6a5e86ba01 100644
--- a/pc-bios/s390-ccw/cio.h
+++ b/pc-bios/s390-ccw/cio.h
@@ -361,6 +361,8 @@ typedef struct CcwSearchIdData {
 uint8_t record;
 } __attribute__((packed)) CcwSearchIdData;
 
+extern SubChannelId net_schid;
+
 int enable_mss_facility(void);
 void enable_subchannel(SubChannelId schid);
 uint16_t cu_type(SubChannelId schid);
diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h
index cb6ac8a880..3758698468 100644
--- a/pc-bios/s390-ccw/iplb.h
+++ b/pc-bios/s390-ccw/iplb.h
@@ -87,9 +87,9 @@ extern IplParameterBlock iplb 
__attribute__((__aligned__(PAGE_SIZE)));
 struct QemuIplParameters {
 uint8_t  qipl_flags;
 uint8_t  reserved1[3];
-uint64_t netboot_start_addr;
+uint64_t reserved2;
 uint32_t boot_menu_timeout;
-uint8_t  reserved2[12];
+uint8_t  reserved3[12];
 } __attribute__ ((packed));
 typedef struct QemuIplParameters QemuIplParameters;
 
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 9b622e616a..0ed7eb8ade 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -55,6 +55,9 @@ void write_iplb_location(void);
 unsigned int get_loadparm_index(void);
 void main(void);
 
+/* netmain.c */
+void netmain(void);
+
 /* sclp.c */
 void sclp_print(const char *string);
 void sclp_set_write_mask(uint32_t receive_mask, uint32_t send_mask);
diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
index 85bd9d1695..6f9a558ff5 100644
--- a/pc-bios/s390-ccw/virtio.h
+++ b/pc-bios/s390-ccw/virtio.h
@@ -253,7 +253,6 @@ struct VDev {
 uint8_t scsi_dev_heads;
 bool scsi_device_selected;
 ScsiDevice selected_scsi_device;
-uint64_t netboot_start_addr;
 uint32_t max_transfer;
 uint32_t guest_features[2];
 };
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 3cc79706be..414c3f1b47 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -929,7 +929,7 @@ void zipl_load(void)
 }
 
 if (virtio_get_device_type() == VIRTIO_ID_NET) {
-jump_to_IPL_code(vdev->netboot_start_addr);
+netmain();
 }
 
 ipl_scsi();
diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
index 73093d3f14..2345432abb 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -38,8 +38,13 @@ LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */
  */
 void write_subsystem_identification(void)
 {
-lowcore->subchannel_id = blk_schid.sch_id;
-lowcore->subchannel_nr = blk_schid.sch_no;
+if (cutype == CU_TYPE_VIRTIO && virtio_get_device_type() == VIRTIO_ID_NET) 
{
+lowcore->subchannel_id = net_schid.sch_id;
+lowcore->subchannel_nr = net_schid.sch_no;
+} else {
+lowcore->subchannel_id = blk_schid.sch_id;
+lowcore->subchannel_nr = blk_schid.sch_no;
+}
 lowcore->io_int_parm = 0;
 }
 
@@ -231,7 +236,6 @@ static int virtio_setup(void)
 switch (vdev->senseid.cu_model) {
 case VIRTIO_ID_NET:
 puts("Network boot device detected");
-vdev->netboo

[PATCH v3 00/19] s390x: Add Full Boot Order Support

2024-10-07 Thread jrossi
From: Jared Rossi 

Version 3 fixes ISO IPL and restores the ability to probe for boot devices when
no primary b0ot device has been specified.

Two automated qtests are added, one that tests a cdrom device as the only
fallback device after the primary boot target fails and one that that defines
the seven failing devices before one that succeeds.

Note: Although the qtest with multiple invalid IPL devices passes, it may
generate a log message about zero sized buffers. This message does not appear
to be directly related to the IPLB chaining or loading process, and it also
does not appear to cause any unexpected behavior during IPL. The log message
seems to be triggered when multiple virtio-scsi devices fail to IPL in
succession, but the exact cause is still unclear.

changes v2 -> v3:

- Fix return code mismatch in ISO IPL path
- Introduce a new RC for loading ECKD segments rather than rely on block 0
- Fix bug causing false negative in some SCSI device error cases
- Terminate entire IPL on bad PBT instead of trying next device
- Simplify routine for building IPLB chain
- Restore device probing
- Add qtests
- Minor stylistic clean-ups and comment clarifications

Changes v1 -> v2:

- Use the libc from SLOF and replace sclp_print calls with put/printf
- Merge netboot into the main s390-ccw.img
- Rework pc-bios to return on error instead of panic
- Handle non-archetected IPLB types (QEMU SCSI) from DIAG308
- Remove code jumps and instead restart the IPL using a traditional loop

Jared Rossi (19):
  hw/s390x/ipl: Provide more memory to the s390-ccw.img firmware
  pc-bios/s390-ccw: Use the libc from SLOF and remove sclp prints
  pc-bios/s390-ccw: Link the netboot code into the main s390-ccw.img
binary
  hw/s390x: Remove the possibility to load the s390-netboot.img binary
  pc-bios/s390-ccw: Merge netboot.mak into the main Makefile
  docs/system/s390x/bootdevices: Update the documentation about network
booting
  pc-bios/s390-ccw: Remove panics from ISO IPL path
  pc-bios/s390-ccw: Remove panics from ECKD IPL path
  pc-bios/s390-ccw: Remove panics from SCSI IPL path
  pc-bios/s390-ccw: Remove panics from DASD IPL path
  pc-bios/s390-ccw: Remove panics from Netboot IPL path
  pc-bios/s390-ccw: Enable failed IPL to return after error
  include/hw/s390x: Add include files for common IPL structs
  s390x: Add individual loadparm assignment to CCW device
  hw/s390x: Build an IPLB for each boot device
  s390x: Rebuild IPLB for SCSI device directly from DIAG308
  pc-bios/s390x: Enable multi-device boot loop
  docs/system: Update documentation for s390x IPL
  tests/qtest: Add s390x boot order tests to cdrom-test.c

 docs/system/bootindex.rst |   7 +-
 docs/system/s390x/bootdevices.rst |  29 +-
 pc-bios/s390-ccw/netboot.mak  |  62 -
 hw/s390x/ccw-device.h |   2 +
 hw/s390x/ipl.h| 123 +
 include/hw/s390x/ipl/qipl.h   | 126 +
 pc-bios/s390-ccw/bootmap.h|  19 +-
 pc-bios/s390-ccw/cio.h|   2 +
 pc-bios/s390-ccw/dasd-ipl.h   |   2 +-
 pc-bios/s390-ccw/iplb.h   | 107 ++--
 pc-bios/s390-ccw/libc.h   |  89 --
 pc-bios/s390-ccw/s390-ccw.h   |  37 +--
 pc-bios/s390-ccw/virtio.h |   3 +-
 hw/s390x/ccw-device.c |  46 
 hw/s390x/ipl.c| 279 +--
 hw/s390x/s390-virtio-ccw.c|  28 +-
 hw/s390x/sclp.c   |   3 +-
 pc-bios/s390-ccw/bootmap.c| 442 --
 pc-bios/s390-ccw/cio.c|  81 +++---
 pc-bios/s390-ccw/dasd-ipl.c   |  71 ++---
 pc-bios/s390-ccw/jump2ipl.c   |  23 +-
 pc-bios/s390-ccw/libc.c   |  88 --
 pc-bios/s390-ccw/main.c   | 100 ---
 pc-bios/s390-ccw/menu.c   |  51 ++--
 pc-bios/s390-ccw/netmain.c|  39 ++-
 pc-bios/s390-ccw/sclp.c   |   7 +-
 pc-bios/s390-ccw/virtio-blkdev.c  |  14 +-
 pc-bios/s390-ccw/virtio-net.c |   7 +-
 pc-bios/s390-ccw/virtio-scsi.c| 165 +++
 pc-bios/s390-ccw/virtio.c |  67 +++--
 target/s390x/diag.c   |   9 +-
 tests/qtest/cdrom-test.c  |  24 ++
 pc-bios/meson.build   |   1 -
 pc-bios/s390-ccw/Makefile |  69 -
 pc-bios/s390-netboot.img  | Bin 67232 -> 0 bytes
 35 files changed, 1150 insertions(+), 1072 deletions(-)
 delete mode 100644 pc-bios/s390-ccw/netboot.mak
 create mode 100644 include/hw/s390x/ipl/qipl.h
 delete mode 100644 pc-bios/s390-ccw/libc.h
 delete mode 100644 pc-bios/s390-ccw/libc.c
 delete mode 100644 pc-bios/s390-netboot.img

-- 
2.45.1




[PATCH v3 09/19] pc-bios/s390-ccw: Remove panics from SCSI IPL path

2024-10-07 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from virtio-scsi IPL specific functions so that error
recovery may be possible in the future.

Functions that would previously panic now provide a return code.

Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/bootmap.c   |  88 +-
 pc-bios/s390-ccw/jump2ipl.c  |   1 +
 pc-bios/s390-ccw/virtio-blkdev.c |   4 +-
 pc-bios/s390-ccw/virtio-scsi.c   | 148 +--
 4 files changed, 169 insertions(+), 72 deletions(-)

diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 266b38c034..534d900f9e 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -591,7 +591,7 @@ static int ipl_eckd(void)
  * IPL a SCSI disk
  */
 
-static void zipl_load_segment(ComponentEntry *entry)
+static int zipl_load_segment(ComponentEntry *entry)
 {
 const int max_entries = (MAX_SECTOR_SIZE / sizeof(ScsiBlockPtr));
 ScsiBlockPtr *bprs = (void *)sec;
@@ -611,7 +611,10 @@ static void zipl_load_segment(ComponentEntry *entry)
 do {
 memset(bprs, FREE_SPACE_FILLER, bprs_size);
 fill_hex_val(blk_no, &blockno, sizeof(blockno));
-read_block(blockno, bprs, err_msg);
+if (virtio_read(blockno, bprs)) {
+puts(err_msg);
+return -EIO;
+}
 
 for (i = 0;; i++) {
 uint64_t *cur_desc = (void *)&bprs[i];
@@ -639,23 +642,37 @@ static void zipl_load_segment(ComponentEntry *entry)
 }
 address = virtio_load_direct(cur_desc[0], cur_desc[1], 0,
  (void *)address);
-IPL_assert(address != -1, "zIPL load segment failed");
+if (!address) {
+puts("zIPL load segment failed");
+return -EIO;
+}
 }
 } while (blockno);
+
+return 0;
 }
 
 /* Run a zipl program */
-static void zipl_run(ScsiBlockPtr *pte)
+static int zipl_run(ScsiBlockPtr *pte)
 {
 ComponentHeader *header;
 ComponentEntry *entry;
 uint8_t tmp_sec[MAX_SECTOR_SIZE];
 
-read_block(pte->blockno, tmp_sec, "Cannot read header");
+if (virtio_read(pte->blockno, tmp_sec)) {
+puts("Cannot read header");
+return -EIO;
+}
 header = (ComponentHeader *)tmp_sec;
 
-IPL_assert(magic_match(tmp_sec, ZIPL_MAGIC), "No zIPL magic in header");
-IPL_assert(header->type == ZIPL_COMP_HEADER_IPL, "Bad header type");
+if (!magic_match(tmp_sec, ZIPL_MAGIC)) {
+puts("No zIPL magic in header");
+return -EINVAL;
+}
+if (header->type != ZIPL_COMP_HEADER_IPL) {
+puts("Bad header type");
+return -EINVAL;
+}
 
 dputs("start loading images\n");
 
@@ -670,22 +687,30 @@ static void zipl_run(ScsiBlockPtr *pte)
 continue;
 }
 
-zipl_load_segment(entry);
+if (zipl_load_segment(entry)) {
+return 1;
+}
 
 entry++;
 
-IPL_assert((uint8_t *)(&entry[1]) <= (tmp_sec + MAX_SECTOR_SIZE),
-   "Wrong entry value");
+if ((uint8_t *)(&entry[1]) > (tmp_sec + MAX_SECTOR_SIZE)) {
+puts("Wrong entry value");
+return -EINVAL;
+}
 }
 
-IPL_assert(entry->component_type == ZIPL_COMP_ENTRY_EXEC, "No EXEC entry");
+if (entry->component_type != ZIPL_COMP_ENTRY_EXEC) {
+puts("No EXEC entry");
+return -EINVAL;
+}
 
 /* should not return */
 write_reset_psw(entry->compdat.load_psw);
 jump_to_IPL_code(0);
+return 1;
 }
 
-static void ipl_scsi(void)
+static int ipl_scsi(void)
 {
 ScsiMbr *mbr = (void *)sec;
 int program_table_entries = 0;
@@ -696,10 +721,13 @@ static void ipl_scsi(void)
 
 /* Grab the MBR */
 memset(sec, FREE_SPACE_FILLER, sizeof(sec));
-read_block(0, mbr, "Cannot read block 0");
+if (virtio_read(0, mbr)) {
+puts("Cannot read block 0");
+return -EIO;
+}
 
 if (!magic_match(mbr->magic, ZIPL_MAGIC)) {
-return;
+return 0;
 }
 
 puts("Using SCSI scheme.");
@@ -707,11 +735,20 @@ static void ipl_scsi(void)
 IPL_check(mbr->version_id == 1,
   "Unknown MBR layout version, assuming version 1");
 debug_print_int("program table", mbr->pt.blockno);
-IPL_assert(mbr->pt.blockno, "No Program Table");
+if (!mbr->pt.blockno) {
+puts("No Program Table");
+return -EINVAL;
+}
 
 /* Parse the program table */
-read_block(mbr->pt.blockno, sec, "Error reading Program Table");
-IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT");
+if (virtio_read(mbr->pt.blockno, sec)) {
+puts("Error reading Program Table");
+return -EIO;
+}
+if (!magic_match(sec, ZIPL_MAGIC)) {
+puts("No zIPL magic in Program Table");
+return -EINVAL;
+}
 
 for (i = 0; i < MAX_BOOT_ENTRIES; i++) {
 if (prog_table->entry[i].scsi.blockno) {
@@ -721,17 +758,22 @@ static void 

[PATCH v3 11/19] pc-bios/s390-ccw: Remove panics from Netboot IPL path

2024-10-07 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from Netboot specific functions so that error recovery
may be possible in the future.

Functions that would previously panic now provide a return code.

Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/s390-ccw.h   |  2 +-
 pc-bios/s390-ccw/bootmap.c|  1 +
 pc-bios/s390-ccw/netmain.c| 17 +++--
 pc-bios/s390-ccw/virtio-net.c |  7 +--
 4 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 7516e96a14..496dccb2aa 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -58,7 +58,7 @@ unsigned int get_loadparm_index(void);
 void main(void);
 
 /* netmain.c */
-void netmain(void);
+int netmain(void);
 
 /* sclp.c */
 void sclp_print(const char *string);
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 534d900f9e..1874d4bf5e 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -1065,6 +1065,7 @@ void zipl_load(void)
 
 if (virtio_get_device_type() == VIRTIO_ID_NET) {
 netmain();
+panic("\n! Cannot IPL from this network !\n");
 }
 
 if (ipl_scsi()) {
diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c
index bc6ad8695f..0c2329c752 100644
--- a/pc-bios/s390-ccw/netmain.c
+++ b/pc-bios/s390-ccw/netmain.c
@@ -464,7 +464,7 @@ static bool find_net_dev(Schib *schib, int dev_no)
 return false;
 }
 
-static void virtio_setup(void)
+static int virtio_setup(void)
 {
 Schib schib;
 int ssid;
@@ -495,10 +495,10 @@ static void virtio_setup(void)
 }
 }
 
-IPL_assert(found, "No virtio net device found");
+return found;
 }
 
-void netmain(void)
+int netmain(void)
 {
 filename_ip_t fn_ip;
 int rc, fnlen;
@@ -506,11 +506,15 @@ void netmain(void)
 sclp_setup();
 puts("Network boot starting...");
 
-virtio_setup();
+if (!virtio_setup()) {
+puts("No virtio net device found.");
+return 1;
+}
 
 rc = net_init(&fn_ip);
 if (rc) {
-panic("Network initialization failed. Halting.");
+puts("Network initialization failed.");
+return 1;
 }
 
 fnlen = strlen(fn_ip.filename);
@@ -528,5 +532,6 @@ void netmain(void)
 jump_to_low_kernel();
 }
 
-panic("Failed to load OS from network.");
+puts("Failed to load OS from network.");
+return 1;
 }
diff --git a/pc-bios/s390-ccw/virtio-net.c b/pc-bios/s390-ccw/virtio-net.c
index 2fcb0a58c5..f9854a22c3 100644
--- a/pc-bios/s390-ccw/virtio-net.c
+++ b/pc-bios/s390-ccw/virtio-net.c
@@ -54,8 +54,11 @@ int virtio_net_init(void *mac_addr)
 vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT;
 virtio_setup_ccw(vdev);
 
-IPL_assert(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT,
-   "virtio-net device does not support the MAC address feature");
+if (!(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT)) {
+puts("virtio-net device does not support the MAC address feature");
+return -1;
+}
+
 memcpy(mac_addr, vdev->config.net.mac, ETH_ALEN);
 
 for (i = 0; i < 64; i++) {
-- 
2.45.1




[PATCH v3 17/19] pc-bios/s390x: Enable multi-device boot loop

2024-10-07 Thread jrossi
From: Jared Rossi 

Allow attempts to boot from multiple IPL devices. If the first device fails to
IPL, select the pre-built IPLB for the next device in the boot order and attempt
to IPL from it. Continue this process until IPL is successful or there are no
devices left to try.

Signed-off-by: Jared Rossi 
 pc-bios/s390-ccw/iplb.h | 23 ++
 pc-bios/s390-ccw/jump2ipl.c |  7 +++---
 pc-bios/s390-ccw/main.c | 47 ++---
 pc-bios/s390-ccw/netmain.c  |  3 ++-
 4 files changed, 57 insertions(+), 23 deletions(-)
---
 pc-bios/s390-ccw/iplb.h | 23 ++
 pc-bios/s390-ccw/jump2ipl.c |  7 +++---
 pc-bios/s390-ccw/main.c | 47 ++---
 pc-bios/s390-ccw/netmain.c  |  3 ++-
 4 files changed, 57 insertions(+), 23 deletions(-)

diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h
index 16643f5879..d58fe71974 100644
--- a/pc-bios/s390-ccw/iplb.h
+++ b/pc-bios/s390-ccw/iplb.h
@@ -17,6 +17,7 @@
 #endif
 
 #include 
+#include 
 
 extern QemuIplParameters qipl;
 extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
@@ -49,4 +50,26 @@ static inline bool set_iplb(IplParameterBlock *iplb)
 return manage_iplb(iplb, false);
 }
 
+/*
+ * The IPL started on the device, but failed in some way.  If the IPLB chain
+ * still has more devices left to try, use the next device in order.
+ */
+static inline bool load_next_iplb(void)
+{
+IplParameterBlock *next_iplb;
+
+if (qipl.chain_len < 1) {
+return false;
+}
+
+qipl.index++;
+next_iplb = (IplParameterBlock *) qipl.next_iplb;
+memcpy(&iplb, next_iplb, sizeof(IplParameterBlock));
+
+qipl.chain_len--;
+qipl.next_iplb = qipl.next_iplb + sizeof(IplParameterBlock);
+
+return true;
+}
+
 #endif /* IPLB_H */
diff --git a/pc-bios/s390-ccw/jump2ipl.c b/pc-bios/s390-ccw/jump2ipl.c
index 711a69a206..a3e5d55499 100644
--- a/pc-bios/s390-ccw/jump2ipl.c
+++ b/pc-bios/s390-ccw/jump2ipl.c
@@ -46,9 +46,10 @@ int jump_to_IPL_code(uint64_t address)
  */
 if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) {
 iplb.devno = qipl.index;
-if (!set_iplb(&iplb)) {
-panic("Failed to set IPLB");
-}
+}
+
+if (have_iplb && !set_iplb(&iplb)) {
+panic("Failed to set IPLB");
 }
 
 /*
diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
index d7c457e0ed..1b05174ace 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -23,7 +23,7 @@ static SubChannelId blk_schid = { .one = 1 };
 static char loadparm_str[LOADPARM_LEN + 1];
 QemuIplParameters qipl;
 IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
-static bool have_iplb;
+bool have_iplb;
 static uint16_t cutype;
 LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */
 
@@ -55,6 +55,12 @@ void write_iplb_location(void)
 }
 }
 
+static void copy_qipl(void)
+{
+QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS;
+memcpy(&qipl, early_qipl, sizeof(QemuIplParameters));
+}
+
 unsigned int get_loadparm_index(void)
 {
 return atoi(loadparm_str);
@@ -152,6 +158,7 @@ static void menu_setup(void)
 
 /* If loadparm was set to any other value, then do not enable menu */
 if (memcmp(loadparm_str, LOADPARM_EMPTY, LOADPARM_LEN) != 0) {
+menu_set_parms(qipl.qipl_flags && ~BOOT_MENU_FLAG_MASK, 0);
 return;
 }
 
@@ -183,7 +190,6 @@ static void css_setup(void)
 static void boot_setup(void)
 {
 char lpmsg[] = "LOADPARM=[]\n";
-have_iplb = store_iplb(&iplb);
 
 if (memcmp(iplb.loadparm, "", LOADPARM_LEN) != 0) {
 ebcdic_to_ascii((char *) iplb.loadparm, loadparm_str, LOADPARM_LEN);
@@ -191,6 +197,10 @@ static void boot_setup(void)
 sclp_get_loadparm_ascii(loadparm_str);
 }
 
+if (have_iplb) {
+menu_setup();
+}
+
 memcpy(lpmsg + 10, loadparm_str, 8);
 puts(lpmsg);
 
@@ -208,6 +218,7 @@ static bool find_boot_device(void)
 
 switch (iplb.pbt) {
 case S390_IPL_TYPE_CCW:
+vdev->scsi_device_selected = false;
 debug_print_int("device no. ", iplb.ccw.devno);
 blk_schid.ssid = iplb.ccw.ssid & 0x3;
 debug_print_int("ssid ", blk_schid.ssid);
@@ -231,15 +242,8 @@ static bool find_boot_device(void)
 static int virtio_setup(void)
 {
 VDev *vdev = virtio_get_device();
-QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS;
 int ret;
 
-memcpy(&qipl, early_qipl, sizeof(QemuIplParameters));
-
-if (have_iplb) {
-menu_setup();
-}
-
 switch (vdev->senseid.cu_model) {
 case VIRTIO_ID_NET:
 puts("Network boot device detected");
@@ -271,11 +275,9 @@ static void ipl_boot_device(void)
 dasd_ipl(blk_schid, cutype);
 break;
 case CU_TYPE_VIRTIO:
-if (virtio_setup()) {
-return;/* Only returns in case of errors */
+if (virtio_setup() == 0) {
+zipl_load();
   

[PATCH v3 06/19] docs/system/s390x/bootdevices: Update the documentation about network booting

2024-10-07 Thread jrossi
From: Jared Rossi 

Remove the information about the separate s390-netboot.img from
the documentation.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 
---
 docs/system/s390x/bootdevices.rst | 20 +++-
 1 file changed, 7 insertions(+), 13 deletions(-)

diff --git a/docs/system/s390x/bootdevices.rst 
b/docs/system/s390x/bootdevices.rst
index 1a7a18b43b..c97efb8fc0 100644
--- a/docs/system/s390x/bootdevices.rst
+++ b/docs/system/s390x/bootdevices.rst
@@ -82,23 +82,17 @@ Note that ``0`` can be used to boot the default entry.
 Booting from a network device
 -
 
-Beside the normal guest firmware (which is loaded from the file 
``s390-ccw.img``
-in the data directory of QEMU, or via the ``-bios`` option), QEMU ships with
-a small TFTP network bootloader firmware for virtio-net-ccw devices, too. This
-firmware is loaded from a file called ``s390-netboot.img`` in the QEMU data
-directory. In case you want to load it from a different filename instead,
-you can specify it via the ``-global s390-ipl.netboot_fw=filename``
-command line option.
-
-The ``bootindex`` property is especially important for booting via the network.
-If you don't specify the ``bootindex`` property here, the network bootloader
-firmware code won't get loaded into the guest memory so that the network boot
-will fail. For a successful network boot, try something like this::
+The firmware that ships with QEMU includes a small TFTP network bootloader
+for virtio-net-ccw devices.  The ``bootindex`` property is especially
+important for booting via the network. If you don't specify the ``bootindex``
+property here, the network bootloader won't be taken into consideration and
+the network boot will fail. For a successful network boot, try something
+like this::
 
  qemu-system-s390x -netdev user,id=n1,tftp=...,bootfile=... \
-device virtio-net-ccw,netdev=n1,bootindex=1
 
-The network bootloader firmware also has basic support for pxelinux.cfg-style
+The network bootloader also has basic support for pxelinux.cfg-style
 configuration files. See the `PXELINUX Configuration page
 `__
 for details how to set up the configuration file on your TFTP server.
-- 
2.45.1




[PATCH v3 13/19] include/hw/s390x: Add include files for common IPL structs

2024-10-07 Thread jrossi
From: Jared Rossi 

Currently, structures defined in both hw/s390x/ipl.h and pc-bios/s390-ccw/iplb.h
must be kept in sync, which is prone to error. Instead, create a new directory
at include/hw/s390x/ipl/ to contain the definitions that must be shared.

Signed-off-by: Jared Rossi 
Reviewed-by: Thomas Huth 
---
 hw/s390x/ipl.h  | 104 +-
 include/hw/s390x/ipl/qipl.h | 123 
 pc-bios/s390-ccw/iplb.h |  84 ++--
 pc-bios/s390-ccw/Makefile   |   2 +-
 4 files changed, 130 insertions(+), 183 deletions(-)
 create mode 100644 include/hw/s390x/ipl/qipl.h

diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index b2105b616a..fa394c339d 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -16,95 +16,11 @@
 #include "cpu.h"
 #include "exec/address-spaces.h"
 #include "hw/qdev-core.h"
+#include "hw/s390x/ipl/qipl.h"
 #include "qom/object.h"
 
-struct IPLBlockPVComp {
-uint64_t tweak_pref;
-uint64_t addr;
-uint64_t size;
-} QEMU_PACKED;
-typedef struct IPLBlockPVComp IPLBlockPVComp;
-
-struct IPLBlockPV {
-uint8_t  reserved18[87];/* 0x18 */
-uint8_t  version;   /* 0x6f */
-uint32_t reserved70;/* 0x70 */
-uint32_t num_comp;  /* 0x74 */
-uint64_t pv_header_addr;/* 0x78 */
-uint64_t pv_header_len; /* 0x80 */
-struct IPLBlockPVComp components[0];
-} QEMU_PACKED;
-typedef struct IPLBlockPV IPLBlockPV;
-
-struct IplBlockCcw {
-uint8_t  reserved0[85];
-uint8_t  ssid;
-uint16_t devno;
-uint8_t  vm_flags;
-uint8_t  reserved3[3];
-uint32_t vm_parm_len;
-uint8_t  nss_name[8];
-uint8_t  vm_parm[64];
-uint8_t  reserved4[8];
-} QEMU_PACKED;
-typedef struct IplBlockCcw IplBlockCcw;
-
-struct IplBlockFcp {
-uint8_t  reserved1[305 - 1];
-uint8_t  opt;
-uint8_t  reserved2[3];
-uint16_t reserved3;
-uint16_t devno;
-uint8_t  reserved4[4];
-uint64_t wwpn;
-uint64_t lun;
-uint32_t bootprog;
-uint8_t  reserved5[12];
-uint64_t br_lba;
-uint32_t scp_data_len;
-uint8_t  reserved6[260];
-uint8_t  scp_data[0];
-} QEMU_PACKED;
-typedef struct IplBlockFcp IplBlockFcp;
-
-struct IplBlockQemuScsi {
-uint32_t lun;
-uint16_t target;
-uint16_t channel;
-uint8_t  reserved0[77];
-uint8_t  ssid;
-uint16_t devno;
-} QEMU_PACKED;
-typedef struct IplBlockQemuScsi IplBlockQemuScsi;
-
 #define DIAG308_FLAGS_LP_VALID 0x80
 
-union IplParameterBlock {
-struct {
-uint32_t len;
-uint8_t  reserved0[3];
-uint8_t  version;
-uint32_t blk0_len;
-uint8_t  pbt;
-uint8_t  flags;
-uint16_t reserved01;
-uint8_t  loadparm[8];
-union {
-IplBlockCcw ccw;
-IplBlockFcp fcp;
-IPLBlockPV pv;
-IplBlockQemuScsi scsi;
-};
-} QEMU_PACKED;
-struct {
-uint8_t  reserved1[110];
-uint16_t devno;
-uint8_t  reserved2[88];
-uint8_t  reserved_ext[4096 - 200];
-} QEMU_PACKED;
-} QEMU_PACKED;
-typedef union IplParameterBlock IplParameterBlock;
-
 int s390_ipl_set_loadparm(uint8_t *loadparm);
 void s390_ipl_update_diag308(IplParameterBlock *iplb);
 int s390_ipl_prepare_pv_header(Error **errp);
@@ -131,24 +47,6 @@ void s390_ipl_clear_reset_request(void);
 #define QIPL_FLAG_BM_OPTS_CMD   0x80
 #define QIPL_FLAG_BM_OPTS_ZIPL  0x40
 
-/*
- * The QEMU IPL Parameters will be stored at absolute address
- * 204 (0xcc) which means it is 32-bit word aligned but not
- * double-word aligned. Placement of 64-bit data fields in this
- * area must account for their alignment needs.
- * The total size of the struct must never exceed 28 bytes.
- * This definition must be kept in sync with the definition
- * in pc-bios/s390-ccw/iplb.h.
- */
-struct QemuIplParameters {
-uint8_t  qipl_flags;
-uint8_t  reserved1[3];
-uint64_t reserved2;
-uint32_t boot_menu_timeout;
-uint8_t  reserved3[12];
-} QEMU_PACKED;
-typedef struct QemuIplParameters QemuIplParameters;
-
 #define TYPE_S390_IPL "s390-ipl"
 OBJECT_DECLARE_SIMPLE_TYPE(S390IPLState, S390_IPL)
 
diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
new file mode 100644
index 00..0ef04af027
--- /dev/null
+++ b/include/hw/s390x/ipl/qipl.h
@@ -0,0 +1,123 @@
+/*
+ * S/390 boot structures
+ *
+ * Copyright 2024 IBM Corp.
+ * Author(s): Jared Rossi 
+ *
+ * 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.
+ */
+
+#ifndef S390X_QIPL_H
+#define S390X_QIPL_H
+
+/* Boot Menu flags */
+#define QIPL_FLAG_BM_OPTS_CMD   0x80
+#define QIPL_FLAG_BM_OPTS_ZIPL  0x40
+
+#define QIPL_ADDRESS  0xcc
+#define LOADPARM_LEN8
+
+/*
+ * The QEMU IPL Parameters will be stored at absolute address
+ * 204 (0xcc) which means it is 32-bit word aligned but not
+ * double-word aligned. Placement 

[PATCH v3 12/19] pc-bios/s390-ccw: Enable failed IPL to return after error

2024-10-07 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from IPL functions such that a return code is propagated
back to the main IPL calling function (rather than terminating immediately),
which facilitates possible error recovery in the future.

A select few panics remain, which indicate fatal non-devices errors that must
result in termination.

Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/s390-ccw.h  |  2 +-
 pc-bios/s390-ccw/virtio.h|  2 +-
 pc-bios/s390-ccw/bootmap.c   | 53 ++
 pc-bios/s390-ccw/cio.c   |  3 +-
 pc-bios/s390-ccw/jump2ipl.c  |  5 ++-
 pc-bios/s390-ccw/main.c  | 35 +
 pc-bios/s390-ccw/virtio-blkdev.c |  4 +-
 pc-bios/s390-ccw/virtio.c| 65 +---
 8 files changed, 111 insertions(+), 58 deletions(-)

diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 496dccb2aa..cdf998868c 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -79,7 +79,7 @@ void zipl_load(void);
 
 /* jump2ipl.c */
 void write_reset_psw(uint64_t psw);
-void jump_to_IPL_code(uint64_t address);
+int jump_to_IPL_code(uint64_t address);
 void jump_to_low_kernel(void);
 
 /* menu.c */
diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
index 6f9a558ff5..9faf3986b1 100644
--- a/pc-bios/s390-ccw/virtio.h
+++ b/pc-bios/s390-ccw/virtio.h
@@ -274,7 +274,7 @@ void vring_send_buf(VRing *vr, void *p, int len, int flags);
 int vr_poll(VRing *vr);
 int vring_wait_reply(void);
 int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd);
-void virtio_setup_ccw(VDev *vdev);
+int virtio_setup_ccw(VDev *vdev);
 
 int virtio_net_init(void *mac_addr);
 
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 1874d4bf5e..c80fcd6f01 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -62,15 +62,34 @@ static void *s2_prev_blk = _s2;
 static void *s2_cur_blk = _s2 + MAX_SECTOR_SIZE;
 static void *s2_next_blk = _s2 + MAX_SECTOR_SIZE * 2;
 
-static inline void verify_boot_info(BootInfo *bip)
+static inline int verify_boot_info(BootInfo *bip)
 {
-IPL_assert(magic_match(bip->magic, ZIPL_MAGIC), "No zIPL sig in BootInfo");
-IPL_assert(bip->version == BOOT_INFO_VERSION, "Wrong zIPL version");
-IPL_assert(bip->bp_type == BOOT_INFO_BP_TYPE_IPL, "DASD is not for IPL");
-IPL_assert(bip->dev_type == BOOT_INFO_DEV_TYPE_ECKD, "DASD is not ECKD");
-IPL_assert(bip->flags == BOOT_INFO_FLAGS_ARCH, "Not for this arch");
-IPL_assert(block_size_ok(bip->bp.ipl.bm_ptr.eckd.bptr.size),
-   "Bad block size in zIPL section of the 1st record.");
+if (!magic_match(bip->magic, ZIPL_MAGIC)) {
+puts("No zIPL sig in BootInfo");
+return -EINVAL;
+}
+if (bip->version != BOOT_INFO_VERSION) {
+puts("Wrong zIPL version");
+return -EINVAL;
+}
+if (bip->bp_type != BOOT_INFO_BP_TYPE_IPL) {
+puts("DASD is not for IPL");
+return -ENODEV;
+}
+if (bip->dev_type != BOOT_INFO_DEV_TYPE_ECKD) {
+puts("DASD is not ECKD");
+return -ENODEV;
+}
+if (bip->flags != BOOT_INFO_FLAGS_ARCH) {
+puts("Not for this arch");
+return -EINVAL;
+}
+if (!block_size_ok(bip->bp.ipl.bm_ptr.eckd.bptr.size)) {
+puts("Bad block size in zIPL section of 1st record");
+return -EINVAL;
+}
+
+return 0;
 }
 
 static void eckd_format_chs(ExtEckdBlockPtr *ptr,  bool ldipl,
@@ -368,8 +387,8 @@ static int run_eckd_boot_script(block_number_t bmt_block_nr,
 puts("Unknown script entry type");
 return -EINVAL;
 }
-write_reset_psw(bms->entry[i].address.load_address); /* no return */
-jump_to_IPL_code(0); /* no return */
+write_reset_psw(bms->entry[i].address.load_address);
+jump_to_IPL_code(0);
 return 1;
 }
 
@@ -1060,16 +1079,19 @@ void zipl_load(void)
 
 if (vdev->is_cdrom) {
 ipl_iso_el_torito();
-panic("\n! Cannot IPL this ISO image !\n");
+puts("Failed to IPL this ISO image!");
+return;
 }
 
 if (virtio_get_device_type() == VIRTIO_ID_NET) {
 netmain();
-panic("\n! Cannot IPL from this network !\n");
+puts("Failed to IPL from this network!");
+return;
 }
 
 if (ipl_scsi()) {
-panic("\n! Cannot IPL this SCSI device !\n");
+puts("Failed to IPL from this SCSI device!");
+return;
 }
 
 switch (virtio_get_device_type()) {
@@ -1080,8 +1102,9 @@ void zipl_load(void)
 zipl_load_vscsi();
 break;
 default:
-panic("\n! Unknown IPL device type !\n");
+puts("Unknown IPL device type!");
+return;
 }
 
-puts("zIPL load failed.");
+puts("zIPL load failed!");
 }
diff --git a/pc-bios/s390-ccw/cio.c b/pc-bios/s390-ccw/cio.c
index 758e74965e..35b08ba7c1 100644
--- a/pc-bios/s390-ccw/cio.c
+++ b/pc-bios/s390-ccw/cio.c
@@ -59,7 +59,8 @@ uint16_t cu_type(SubChannel

[PATCH v3 18/19] docs/system: Update documentation for s390x IPL

2024-10-07 Thread jrossi
From: Jared Rossi 

Update docs to show that s390x PC BIOS can support more than one boot device.

Signed-off-by: Jared Rossi 
---
 docs/system/bootindex.rst | 7 ---
 docs/system/s390x/bootdevices.rst | 9 ++---
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/docs/system/bootindex.rst b/docs/system/bootindex.rst
index 8b057f812f..988f7b3beb 100644
--- a/docs/system/bootindex.rst
+++ b/docs/system/bootindex.rst
@@ -49,10 +49,11 @@ Limitations
 ---
 
 Some firmware has limitations on which devices can be considered for
-booting.  For instance, the PC BIOS boot specification allows only one
-disk to be bootable.  If boot from disk fails for some reason, the BIOS
+booting.  For instance, the x86 PC BIOS boot specification allows only one
+disk to be bootable.  If boot from disk fails for some reason, the x86 BIOS
 won't retry booting from other disk.  It can still try to boot from
-floppy or net, though.
+floppy or net, though. In the case of s390x BIOS, the BIOS will try up to
+8 total devices, any number of which may be disks.
 
 Sometimes, firmware cannot map the device path QEMU wants firmware to
 boot from to a boot method.  It doesn't happen for devices the firmware
diff --git a/docs/system/s390x/bootdevices.rst 
b/docs/system/s390x/bootdevices.rst
index c97efb8fc0..1a1a764c1c 100644
--- a/docs/system/s390x/bootdevices.rst
+++ b/docs/system/s390x/bootdevices.rst
@@ -6,9 +6,7 @@ Booting with bootindex parameter
 
 For classical mainframe guests (i.e. LPAR or z/VM installations), you always
 have to explicitly specify the disk where you want to boot from (or "IPL" from,
-in s390x-speak -- IPL means "Initial Program Load"). In particular, there can
-also be only one boot device according to the architecture specification, thus
-specifying multiple boot devices is not possible (yet).
+in s390x-speak -- IPL means "Initial Program Load").
 
 So for booting an s390x guest in QEMU, you should always mark the
 device where you want to boot from with the ``bootindex`` property, for
@@ -17,6 +15,11 @@ example::
  qemu-system-s390x -drive if=none,id=dr1,file=guest.qcow2 \
-device virtio-blk,drive=dr1,bootindex=1
 
+Multiple devices may have a bootindex. The lowest bootindex is assigned to the
+device to IPL first.  If the IPL fails for the first, the device with the 
second
+lowest bootindex will be tried and so on until IPL is successful or there are 
no
+remaining boot devices to try.
+
 For booting from a CD-ROM ISO image (which needs to include El-Torito boot
 information in order to be bootable), it is recommended to specify a 
``scsi-cd``
 device, for example like this::
-- 
2.45.1




[PATCH v3 08/19] pc-bios/s390-ccw: Remove panics from ECKD IPL path

2024-10-07 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from ECKD block device IPL specific functions so that
error recovery may be possible in the future.

Functions that would previously panic now provide a return code.

Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/s390-ccw.h |   1 +
 pc-bios/s390-ccw/bootmap.c  | 183 
 2 files changed, 126 insertions(+), 58 deletions(-)

diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index cbd92f3671..7516e96a14 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -31,6 +31,7 @@ typedef unsigned long long u64;
 #define EBUSY   2
 #define ENODEV  3
 #define EINVAL  4
+#define ENOENT  5
 
 #ifndef MIN
 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 7984de62fe..266b38c034 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -145,14 +145,17 @@ static block_number_t load_eckd_segments(block_number_t 
blk, bool ldipl,
 bool more_data;
 
 memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs));
-read_block(blk, bprs, "BPRS read failed");
+if (virtio_read(blk, bprs)) {
+puts("BPRS read failed");
+return -EIO;
+}
 
 do {
 more_data = false;
 for (j = 0;; j++) {
 block_nr = gen_eckd_block_num(&bprs[j].xeckd, ldipl);
 if (is_null_block_number(block_nr)) { /* end of chunk */
-break;
+return -ENOENT;
 }
 
 /* we need the updated blockno for the next indirect entry
@@ -163,15 +166,20 @@ static block_number_t load_eckd_segments(block_number_t 
blk, bool ldipl,
 }
 
 /* List directed pointer does not store block size */
-IPL_assert(ldipl || block_size_ok(bprs[j].xeckd.bptr.size),
-   "bad chunk block size");
+if (!ldipl && !block_size_ok(bprs[j].xeckd.bptr.size)) {
+puts("Bad chunk block size");
+return -EIO;
+}
 
 if (!eckd_valid_address(&bprs[j].xeckd, ldipl)) {
 /*
  * If an invalid address is found during LD-IPL then break and
- * retry as CCW
+ * retry as CCW-IPL, otherwise abort on error
  */
-IPL_assert(ldipl, "bad chunk ECKD addr");
+if (!ldipl) {
+puts("Bad chunk ECKD address");
+return -EIO;
+}
 break;
 }
 
@@ -189,7 +197,10 @@ static block_number_t load_eckd_segments(block_number_t 
blk, bool ldipl,
  * I.e. the next ptr must point to the unused memory area
  */
 memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs));
-read_block(block_nr, bprs, "BPRS continuation read failed");
+if (virtio_read(block_nr, bprs)) {
+puts("BPRS continuation read failed");
+return -EIO;
+}
 more_data = true;
 break;
 }
@@ -198,7 +209,10 @@ static block_number_t load_eckd_segments(block_number_t 
blk, bool ldipl,
  * to memory (address).
  */
 rc = virtio_read_many(block_nr, (void *)(*address), count + 1);
-IPL_assert(rc == 0, "code chunk read failed");
+if (rc != 0) {
+puts("Code chunk read failed");
+return -EIO;
+}
 
 *address += (count + 1) * virtio_get_block_size();
 }
@@ -232,7 +246,10 @@ static int eckd_get_boot_menu_index(block_number_t 
s1b_block_nr)
 
 /* Get Stage1b data */
 memset(sec, FREE_SPACE_FILLER, sizeof(sec));
-read_block(s1b_block_nr, s1b, "Cannot read stage1b boot loader");
+if (virtio_read(s1b_block_nr, s1b)) {
+puts("Cannot read stage1b boot loader");
+return -EIO;
+}
 
 memset(_s2, FREE_SPACE_FILLER, sizeof(_s2));
 
@@ -244,7 +261,10 @@ static int eckd_get_boot_menu_index(block_number_t 
s1b_block_nr)
 break;
 }
 
-read_block(cur_block_nr, s2_cur_blk, "Cannot read stage2 boot loader");
+if (virtio_read(cur_block_nr, s2_cur_blk)) {
+puts("Cannot read stage2 boot loader");
+return -EIO;
+}
 
 if (find_zipl_boot_menu_banner(&banner_offset)) {
 /*
@@ -252,8 +272,10 @@ static int eckd_get_boot_menu_index(block_number_t 
s1b_block_nr)
  * possibility of menu data spanning multiple blocks.
  */
 if (prev_block_nr) {
-read_block(prev_block_nr, s2_prev_blk,
-   "Cannot read stage2 boot loader");
+if (virtio_read(prev_block_nr, s2_prev_blk)) {
+puts("Cannot read stage2 boot loader");
+return -EIO;
+}
   

[PATCH v3 19/19] tests/qtest: Add s390x boot order tests to cdrom-test.c

2024-10-07 Thread jrossi
From: Jared Rossi 

Add two new qtests to verify that a valid IPL device can successfully boot after
failed IPL attempts from one or more invalid devices.

cdrom-test/as-fallback-device: Defines the primary boot target as a device that
is invalid for IPL and a second boot target that is valid for IPL. Ensures that
the valid device will be selected after the initial failed IPL.

cdrom-test/as-last-option: Defines the maximum number of boot devices (8)
where only the final entry in the boot order is valid. Ensures that a valid
device will be selected even after multiple failed IPL attempts from both
virtio-blk and virtio-scsi device types.

Signed-off-by: Jared Rossi 
---
 tests/qtest/cdrom-test.c | 24 
 1 file changed, 24 insertions(+)

diff --git a/tests/qtest/cdrom-test.c b/tests/qtest/cdrom-test.c
index 9d72b24e4b..123c067589 100644
--- a/tests/qtest/cdrom-test.c
+++ b/tests/qtest/cdrom-test.c
@@ -213,6 +213,30 @@ static void add_s390x_tests(void)
 "-drive driver=null-co,read-zeroes=on,if=none,id=d1 "
 "-device virtio-blk,drive=d2,bootindex=1 "
 "-drive if=none,id=d2,media=cdrom,file=", test_cdboot);
+qtest_add_data_func("cdrom/boot/as-fallback-device",
+"-device virtio-serial -device virtio-scsi "
+"-device virtio-blk,drive=d1,bootindex=1 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d1 "
+"-device virtio-blk,drive=d2,bootindex=2 "
+"-drive if=none,id=d2,media=cdrom,file=", test_cdboot);
+qtest_add_data_func("cdrom/boot/as-last-option",
+"-device virtio-serial -device virtio-scsi "
+"-device virtio-blk,drive=d1,bootindex=1 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d1 "
+"-device virtio-blk,drive=d2,bootindex=2 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d2 "
+"-device virtio-blk,drive=d3,bootindex=3 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d3 "
+"-device scsi-hd,drive=d4,bootindex=4 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d4 "
+"-device scsi-hd,drive=d5,bootindex=5 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d5 "
+"-device virtio-blk,drive=d6,bootindex=6 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d6 "
+"-device scsi-hd,drive=d7,bootindex=7 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d7 "
+"-device virtio-blk,drive=d8,bootindex=8 "
+"-drive if=none,id=d8,media=cdrom,file=", test_cdboot);
 if (qtest_has_device("x-terminal3270")) {
 qtest_add_data_func("cdrom/boot/without-bootindex",
 "-device virtio-scsi -device virtio-serial "
-- 
2.45.1




[PATCH v3 16/19] s390x: Rebuild IPLB for SCSI device directly from DIAG308

2024-10-07 Thread jrossi
From: Jared Rossi 

Because virtio-scsi type devices use a non-architected IPLB pbt code they cannot
be set and stored normally. Instead, the IPLB must be rebuilt during re-ipl.

As s390x does not natively support multiple boot devices, the devno field is
used to store the position in the boot order for the device.

Handling the rebuild as part of DIAG308 removes the need to check the devices
for invalid IPLBs later in the IPL.

Signed-off-by: Jared Rossi 
---
 hw/s390x/ipl.h  | 11 --
 include/hw/s390x/ipl/qipl.h |  3 +-
 hw/s390x/ipl.c  | 75 +++--
 pc-bios/s390-ccw/jump2ipl.c | 11 --
 target/s390x/diag.c |  9 -
 5 files changed, 39 insertions(+), 70 deletions(-)

diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index 54eb48fd6e..aead1d6ae5 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -24,6 +24,7 @@
 
 void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp);
 void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp);
+void s390_rebuild_iplb(uint16_t index, IplParameterBlock *iplb);
 void s390_ipl_update_diag308(IplParameterBlock *iplb);
 int s390_ipl_prepare_pv_header(Error **errp);
 int s390_ipl_pv_unpack(void);
@@ -65,7 +66,8 @@ struct S390IPLState {
 bool enforce_bios;
 bool iplb_valid;
 bool iplb_valid_pv;
-bool netboot;
+bool rebuilt_iplb;
+uint16_t iplb_index;
 /* reset related properties don't have to be migrated or reset */
 enum s390_reset reset_type;
 int reset_cpu_index;
@@ -172,11 +174,14 @@ static inline bool iplb_valid_pv(IplParameterBlock *iplb)
 
 static inline bool iplb_valid(IplParameterBlock *iplb)
 {
+uint32_t len = be32_to_cpu(iplb->len);
+
 switch (iplb->pbt) {
 case S390_IPL_TYPE_FCP:
-return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_FCP_LEN;
+return len >= S390_IPLB_MIN_FCP_LEN;
 case S390_IPL_TYPE_CCW:
-return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_CCW_LEN;
+return (len >= S390_IPLB_MIN_CCW_LEN);
+case S390_IPL_TYPE_QEMU_SCSI:
 default:
 return false;
 }
diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
index e56b181298..451f3b1684 100644
--- a/include/hw/s390x/ipl/qipl.h
+++ b/include/hw/s390x/ipl/qipl.h
@@ -28,7 +28,8 @@
  */
 struct QemuIplParameters {
 uint8_t  qipl_flags;
-uint8_t  reserved1[3];
+uint8_t  index;
+uint8_t  reserved1[2];
 uint64_t reserved2;
 uint32_t boot_menu_timeout;
 uint8_t  reserved3[2];
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index aec79f41a8..50fde05b67 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -448,7 +448,6 @@ void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t 
*ebcdic_lp)
 
 static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
 {
-S390IPLState *ipl = get_ipl_device();
 CcwDevice *ccw_dev = NULL;
 SCSIDevice *sd;
 int devtype;
@@ -481,9 +480,6 @@ static bool s390_build_iplb(DeviceState *dev_st, 
IplParameterBlock *iplb)
 iplb->ccw.ssid = ccw_dev->sch->ssid & 3;
 break;
 case CCW_DEVTYPE_VIRTIO_NET:
-/* The S390IPLState netboot is true if ANY IPLB may use netboot */
-ipl->netboot = true;
-/* Fall through to CCW_DEVTYPE_VIRTIO case */
 case CCW_DEVTYPE_VIRTIO:
 iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
 iplb->blk0_len =
@@ -508,6 +504,17 @@ static bool s390_build_iplb(DeviceState *dev_st, 
IplParameterBlock *iplb)
 return false;
 }
 
+
+void s390_rebuild_iplb(uint16_t dev_index, IplParameterBlock *iplb)
+{
+S390IPLState *ipl = get_ipl_device();
+uint16_t index;
+index = ipl->rebuilt_iplb ? ipl->iplb_index : dev_index;
+
+ipl->rebuilt_iplb = s390_build_iplb(get_boot_device(index), iplb);
+ipl->iplb_index = index;
+}
+
 static bool s390_init_all_iplbs(S390IPLState *ipl)
 {
 int iplb_num = 0;
@@ -558,44 +565,6 @@ static bool s390_init_all_iplbs(S390IPLState *ipl)
 return iplb_num;
 }
 
-static bool is_virtio_ccw_device_of_type(IplParameterBlock *iplb,
- int virtio_id)
-{
-uint8_t cssid;
-uint8_t ssid;
-uint16_t devno;
-uint16_t schid;
-SubchDev *sch = NULL;
-
-if (iplb->pbt != S390_IPL_TYPE_CCW) {
-return false;
-}
-
-devno = be16_to_cpu(iplb->ccw.devno);
-ssid = iplb->ccw.ssid & 3;
-
-for (schid = 0; schid < MAX_SCHID; schid++) {
-for (cssid = 0; cssid < MAX_CSSID; cssid++) {
-sch = css_find_subch(1, cssid, ssid, schid);
-
-if (sch && sch->devno == devno) {
-return sch->id.cu_model == virtio_id;
-}
-}
-}
-return false;
-}
-
-static bool is_virtio_net_device(IplParameterBlock *iplb)
-{
-return is_virtio_ccw_device_of_type(iplb, VIRTIO_ID_NET);
-}
-
-static bool is_virtio_scsi_device(IplParameterBlock *iplb)
-{
-return is_virtio_ccw_device_of_type(ip

[PATCH v3 02/19] pc-bios/s390-ccw: Use the libc from SLOF and remove sclp prints

2024-10-07 Thread jrossi
From: Jared Rossi 

We are already using the libc from SLOF for the s390-netboot.img, and
this libc implementation is way more complete and accurate than the
simple implementation that we currently use for the s390-ccw.img binary.
Since we are now always assuming that the SLOF submodule is available
when building the s390-ccw bios (see commit bf6903f6944f), we can drop
the simple implementation and use the SLOF libc for the s390-ccw.img
binary, too.

Additionally replace sclp_print calls with puts/printf now that it is avaliable.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/netboot.mak |  3 --
 pc-bios/s390-ccw/bootmap.h   |  4 +-
 pc-bios/s390-ccw/libc.h  | 89 
 pc-bios/s390-ccw/s390-ccw.h  | 30 ---
 pc-bios/s390-ccw/bootmap.c   | 47 -
 pc-bios/s390-ccw/cio.c   | 78 +---
 pc-bios/s390-ccw/dasd-ipl.c  |  5 +-
 pc-bios/s390-ccw/jump2ipl.c  |  5 +-
 pc-bios/s390-ccw/libc.c  | 88 ---
 pc-bios/s390-ccw/main.c  | 14 ++---
 pc-bios/s390-ccw/menu.c  | 51 +-
 pc-bios/s390-ccw/netmain.c   | 10 ++--
 pc-bios/s390-ccw/sclp.c  |  7 +--
 pc-bios/s390-ccw/virtio-blkdev.c |  6 +--
 pc-bios/s390-ccw/virtio-scsi.c   | 17 +++---
 pc-bios/s390-ccw/virtio.c|  2 +-
 pc-bios/s390-ccw/Makefile| 15 --
 17 files changed, 136 insertions(+), 335 deletions(-)
 delete mode 100644 pc-bios/s390-ccw/libc.h
 delete mode 100644 pc-bios/s390-ccw/libc.c

diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak
index 046aa35587..d2b3d8ee74 100644
--- a/pc-bios/s390-ccw/netboot.mak
+++ b/pc-bios/s390-ccw/netboot.mak
@@ -1,9 +1,6 @@
 
-SLOF_DIR := $(SRC_PATH)/../../roms/SLOF
-
 NETOBJS := start.o sclp.o cio.o virtio.o virtio-net.o jump2ipl.o netmain.o
 
-LIBC_INC := -nostdinc -I$(SLOF_DIR)/lib/libc/include
 LIBNET_INC := -I$(SLOF_DIR)/lib/libnet
 
 NETLDFLAGS := $(LDFLAGS) -Wl,-Ttext=0x780
diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
index d4690a88c2..bbe2c132aa 100644
--- a/pc-bios/s390-ccw/bootmap.h
+++ b/pc-bios/s390-ccw/bootmap.h
@@ -336,9 +336,7 @@ static inline void print_volser(const void *volser)
 
 ebcdic_to_ascii((char *)volser, ascii, 6);
 ascii[6] = '\0';
-sclp_print("VOLSER=[");
-sclp_print(ascii);
-sclp_print("]\n");
+printf("VOLSER=[%s]", ascii);
 }
 
 static inline bool unused_space(const void *p, size_t size)
diff --git a/pc-bios/s390-ccw/libc.h b/pc-bios/s390-ccw/libc.h
deleted file mode 100644
index bcdc45732d..00
--- a/pc-bios/s390-ccw/libc.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * libc-style definitions and functions
- *
- * Copyright (c) 2013 Alexander Graf 
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#ifndef S390_CCW_LIBC_H
-#define S390_CCW_LIBC_H
-
-typedef unsigned long  size_t;
-typedef intbool;
-typedef unsigned char  uint8_t;
-typedef unsigned short uint16_t;
-typedef unsigned int   uint32_t;
-typedef unsigned long long uint64_t;
-
-static inline void *memset(void *s, int c, size_t n)
-{
-size_t i;
-unsigned char *p = s;
-
-for (i = 0; i < n; i++) {
-p[i] = c;
-}
-
-return s;
-}
-
-static inline void *memcpy(void *s1, const void *s2, size_t n)
-{
-uint8_t *dest = s1;
-const uint8_t *src = s2;
-size_t i;
-
-for (i = 0; i < n; i++) {
-dest[i] = src[i];
-}
-
-return s1;
-}
-
-static inline int memcmp(const void *s1, const void *s2, size_t n)
-{
-size_t i;
-const uint8_t *p1 = s1, *p2 = s2;
-
-for (i = 0; i < n; i++) {
-if (p1[i] != p2[i]) {
-return p1[i] > p2[i] ? 1 : -1;
-}
-}
-
-return 0;
-}
-
-static inline size_t strlen(const char *str)
-{
-size_t i;
-for (i = 0; *str; i++) {
-str++;
-}
-return i;
-}
-
-static inline char *strcat(char *dest, const char *src)
-{
-int i;
-char *dest_end = dest + strlen(dest);
-
-for (i = 0; i <= strlen(src); i++) {
-dest_end[i] = src[i];
-}
-return dest;
-}
-
-static inline int isdigit(int c)
-{
-return (c >= '0') && (c <= '9');
-}
-
-uint64_t atoui(const char *str);
-char *uitoa(uint64_t num, char *str, size_t len);
-
-#endif
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index c977a52b50..9b622e616a 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -13,6 +13,11 @@
 
 /* #define DEBUG */
 
+#include 
+#include 
+#include 
+#include 
+
 typedef unsigned char  u8;
 typedef unsigned short u16;
 typedef unsigned int   u32;
@@ -26,9 +31,6 @@ typedef unsigned long long u64;
 #define EBUSY   2

[PATCH v3 15/19] hw/s390x: Build an IPLB for each boot device

2024-10-07 Thread jrossi
From: Jared Rossi 

Build an IPLB for any device with a bootindex (up to a maximum of 8 devices).

The IPLB chain is placed immediately before the BIOS in memory. Because this
is not a fixed address, the location of the next IPLB and number of remaining
boot devices is stored in the QIPL global variable for possible later access by
the guest during IPL.

Signed-off-by: Jared Rossi 
---
 hw/s390x/ipl.h  |   1 +
 include/hw/s390x/ipl/qipl.h |   4 +-
 hw/s390x/ipl.c  | 123 
 3 files changed, 99 insertions(+), 29 deletions(-)

diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index b670bad551..54eb48fd6e 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -20,6 +20,7 @@
 #include "qom/object.h"
 
 #define DIAG308_FLAGS_LP_VALID 0x80
+#define MAX_BOOT_DEVS 8 /* Max number of devices that may have a bootindex */
 
 void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp);
 void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp);
diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
index 0ef04af027..e56b181298 100644
--- a/include/hw/s390x/ipl/qipl.h
+++ b/include/hw/s390x/ipl/qipl.h
@@ -31,7 +31,9 @@ struct QemuIplParameters {
 uint8_t  reserved1[3];
 uint64_t reserved2;
 uint32_t boot_menu_timeout;
-uint8_t  reserved3[12];
+uint8_t  reserved3[2];
+uint16_t chain_len;
+uint64_t next_iplb;
 } QEMU_PACKED;
 typedef struct QemuIplParameters QemuIplParameters;
 
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 55a83bb836..aec79f41a8 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -56,6 +56,13 @@ static bool iplb_extended_needed(void *opaque)
 return ipl->iplbext_migration;
 }
 
+/* Place the IPLB chain immediately before the BIOS in memory */
+static uint64_t find_iplb_chain_addr(uint64_t bios_addr, uint16_t count)
+{
+return (bios_addr & TARGET_PAGE_MASK)
+- (count * sizeof(IplParameterBlock));
+}
+
 static const VMStateDescription vmstate_iplb_extended = {
 .name = "ipl/iplb_extended",
 .version_id = 0,
@@ -398,6 +405,17 @@ static CcwDevice *s390_get_ccw_device(DeviceState *dev_st, 
int *devtype)
 return ccw_dev;
 }
 
+static uint64_t s390_ipl_map_iplb_chain(IplParameterBlock *iplb_chain)
+{
+S390IPLState *ipl = get_ipl_device();
+uint16_t count = ipl->qipl.chain_len;
+uint64_t len = sizeof(IplParameterBlock) * count;
+uint64_t chain_addr = find_iplb_chain_addr(ipl->bios_start_addr, count);
+
+cpu_physical_memory_write(chain_addr, iplb_chain, len);
+return chain_addr;
+}
+
 void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp)
 {
 int i;
@@ -428,54 +446,51 @@ void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t 
*ebcdic_lp)
 }
 }
 
-static bool s390_gen_initial_iplb(S390IPLState *ipl)
+static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
 {
-DeviceState *dev_st;
+S390IPLState *ipl = get_ipl_device();
 CcwDevice *ccw_dev = NULL;
 SCSIDevice *sd;
 int devtype;
 uint8_t *lp;
 
-dev_st = get_boot_device(0);
-if (dev_st) {
-ccw_dev = s390_get_ccw_device(dev_st, &devtype);
-}
-
 /*
  * Currently allow IPL only from CCW devices.
  */
+ccw_dev = s390_get_ccw_device(dev_st, &devtype);
 if (ccw_dev) {
 lp = ccw_dev->loadparm;
 
 switch (devtype) {
 case CCW_DEVTYPE_SCSI:
 sd = SCSI_DEVICE(dev_st);
-ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN);
-ipl->iplb.blk0_len =
+iplb->len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN);
+iplb->blk0_len =
 cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN - 
S390_IPLB_HEADER_LEN);
-ipl->iplb.pbt = S390_IPL_TYPE_QEMU_SCSI;
-ipl->iplb.scsi.lun = cpu_to_be32(sd->lun);
-ipl->iplb.scsi.target = cpu_to_be16(sd->id);
-ipl->iplb.scsi.channel = cpu_to_be16(sd->channel);
-ipl->iplb.scsi.devno = cpu_to_be16(ccw_dev->sch->devno);
-ipl->iplb.scsi.ssid = ccw_dev->sch->ssid & 3;
+iplb->pbt = S390_IPL_TYPE_QEMU_SCSI;
+iplb->scsi.lun = cpu_to_be32(sd->lun);
+iplb->scsi.target = cpu_to_be16(sd->id);
+iplb->scsi.channel = cpu_to_be16(sd->channel);
+iplb->scsi.devno = cpu_to_be16(ccw_dev->sch->devno);
+iplb->scsi.ssid = ccw_dev->sch->ssid & 3;
 break;
 case CCW_DEVTYPE_VFIO:
-ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
-ipl->iplb.pbt = S390_IPL_TYPE_CCW;
-ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
-ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3;
+iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
+iplb->pbt = S390_IPL_TYPE_CCW;
+iplb->ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
+iplb->ccw.ssid = ccw_dev->sch->ssid & 3;
 break;
   

[PATCH v3 04/19] hw/s390x: Remove the possibility to load the s390-netboot.img binary

2024-10-07 Thread jrossi
From: Jared Rossi 

Since the netboot code has now been merged into the main s390-ccw.img
binary, we don't need the separate s390-netboot.img anymore. Remove
it and the code that was responsible for loading it.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 
---
 hw/s390x/ipl.h | 12 +++--
 hw/s390x/ipl.c | 55 --
 hw/s390x/s390-virtio-ccw.c | 10 ++-
 pc-bios/meson.build|  1 -
 4 files changed, 6 insertions(+), 72 deletions(-)

diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index 57cd125769..b2105b616a 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -134,11 +134,8 @@ void s390_ipl_clear_reset_request(void);
 /*
  * The QEMU IPL Parameters will be stored at absolute address
  * 204 (0xcc) which means it is 32-bit word aligned but not
- * double-word aligned.
- * Placement of data fields in this area must account for
- * their alignment needs. E.g., netboot_start_address must
- * have an offset of 4 + n * 8 bytes within the struct in order
- * to keep it double-word aligned.
+ * double-word aligned. Placement of 64-bit data fields in this
+ * area must account for their alignment needs.
  * The total size of the struct must never exceed 28 bytes.
  * This definition must be kept in sync with the definition
  * in pc-bios/s390-ccw/iplb.h.
@@ -146,9 +143,9 @@ void s390_ipl_clear_reset_request(void);
 struct QemuIplParameters {
 uint8_t  qipl_flags;
 uint8_t  reserved1[3];
-uint64_t netboot_start_addr;
+uint64_t reserved2;
 uint32_t boot_menu_timeout;
-uint8_t  reserved2[12];
+uint8_t  reserved3[12];
 } QEMU_PACKED;
 typedef struct QemuIplParameters QemuIplParameters;
 
@@ -178,7 +175,6 @@ struct S390IPLState {
 char *initrd;
 char *cmdline;
 char *firmware;
-char *netboot_fw;
 uint8_t cssid;
 uint8_t ssid;
 uint16_t devno;
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 5f60977ceb..8c490eeb52 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -288,7 +288,6 @@ static Property s390_ipl_properties[] = {
 DEFINE_PROP_STRING("initrd", S390IPLState, initrd),
 DEFINE_PROP_STRING("cmdline", S390IPLState, cmdline),
 DEFINE_PROP_STRING("firmware", S390IPLState, firmware),
-DEFINE_PROP_STRING("netboot_fw", S390IPLState, netboot_fw),
 DEFINE_PROP_BOOL("enforce_bios", S390IPLState, enforce_bios, false),
 DEFINE_PROP_BOOL("iplbext_migration", S390IPLState, iplbext_migration,
  true),
@@ -480,56 +479,6 @@ int s390_ipl_set_loadparm(uint8_t *loadparm)
 return -1;
 }
 
-static int load_netboot_image(Error **errp)
-{
-MachineState *ms = MACHINE(qdev_get_machine());
-S390IPLState *ipl = get_ipl_device();
-char *netboot_filename;
-MemoryRegion *sysmem =  get_system_memory();
-MemoryRegion *mr = NULL;
-void *ram_ptr = NULL;
-int img_size = -1;
-
-mr = memory_region_find(sysmem, 0, 1).mr;
-if (!mr) {
-error_setg(errp, "Failed to find memory region at address 0");
-return -1;
-}
-
-ram_ptr = memory_region_get_ram_ptr(mr);
-if (!ram_ptr) {
-error_setg(errp, "No RAM found");
-goto unref_mr;
-}
-
-netboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->netboot_fw);
-if (netboot_filename == NULL) {
-error_setg(errp, "Could not find network bootloader '%s'",
-   ipl->netboot_fw);
-goto unref_mr;
-}
-
-img_size = load_elf_ram(netboot_filename, NULL, NULL, NULL,
-&ipl->start_addr,
-NULL, NULL, NULL, 1, EM_S390, 0, 0, NULL,
-false);
-
-if (img_size < 0) {
-img_size = load_image_size(netboot_filename, ram_ptr, ms->ram_size);
-ipl->start_addr = KERN_IMAGE_START;
-}
-
-if (img_size < 0) {
-error_setg(errp, "Failed to load network bootloader");
-}
-
-g_free(netboot_filename);
-
-unref_mr:
-memory_region_unref(mr);
-return img_size;
-}
-
 static bool is_virtio_ccw_device_of_type(IplParameterBlock *iplb,
  int virtio_id)
 {
@@ -754,10 +703,6 @@ void s390_ipl_prepare_cpu(S390CPU *cpu)
 ipl->iplb_valid = s390_gen_initial_iplb(ipl);
 }
 }
-if (ipl->netboot) {
-load_netboot_image(&error_fatal);
-ipl->qipl.netboot_start_addr = cpu_to_be64(ipl->start_addr);
-}
 s390_ipl_set_boot_menu(ipl);
 s390_ipl_prepare_qipl(cpu);
 }
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index 5aa8d207a3..529e53f308 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -197,11 +197,10 @@ static void s390_memory_init(MemoryRegion *ram)
 static void s390_init_ipl_dev(const char *kernel_filename,
   const char *kernel_cmdline,
   const char *initrd_filename, const char 
*firmware,
-  const char *netb

[PATCH v3 10/19] pc-bios/s390-ccw: Remove panics from DASD IPL path

2024-10-07 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from DASD IPL specific functions so that error recovery
may be possible in the future.

Functions that would previously panic now provide a return code.

Signed-off-by: Jared Rossi 
Reviewed-by: Thomas Huth 
---
 pc-bios/s390-ccw/dasd-ipl.h |  2 +-
 pc-bios/s390-ccw/dasd-ipl.c | 66 -
 2 files changed, 37 insertions(+), 31 deletions(-)

diff --git a/pc-bios/s390-ccw/dasd-ipl.h b/pc-bios/s390-ccw/dasd-ipl.h
index c394828906..eb1898c84a 100644
--- a/pc-bios/s390-ccw/dasd-ipl.h
+++ b/pc-bios/s390-ccw/dasd-ipl.h
@@ -11,6 +11,6 @@
 #ifndef DASD_IPL_H
 #define DASD_IPL_H
 
-void dasd_ipl(SubChannelId schid, uint16_t cutype);
+int dasd_ipl(SubChannelId schid, uint16_t cutype);
 
 #endif /* DASD_IPL_H */
diff --git a/pc-bios/s390-ccw/dasd-ipl.c b/pc-bios/s390-ccw/dasd-ipl.c
index 99c0b93083..cd4fb884d6 100644
--- a/pc-bios/s390-ccw/dasd-ipl.c
+++ b/pc-bios/s390-ccw/dasd-ipl.c
@@ -111,38 +111,29 @@ static void make_readipl(void)
 ccwIplRead->count = 0x18; /* Read 0x18 bytes of data */
 }
 
-static void run_readipl(SubChannelId schid, uint16_t cutype)
+static int run_readipl(SubChannelId schid, uint16_t cutype)
 {
-if (do_cio(schid, cutype, 0x00, CCW_FMT0)) {
-panic("dasd-ipl: Failed to run Read IPL channel program\n");
-}
+return do_cio(schid, cutype, 0x00, CCW_FMT0);
 }
 
 /*
  * The architecture states that IPL1 data should consist of a psw followed by
  * format-0 READ and TIC CCWs. Let's sanity check.
  */
-static void check_ipl1(void)
+static bool check_ipl1(void)
 {
 Ccw0 *ccwread = (Ccw0 *)0x08;
 Ccw0 *ccwtic = (Ccw0 *)0x10;
 
-if (ccwread->cmd_code != CCW_CMD_DASD_READ ||
-ccwtic->cmd_code != CCW_CMD_TIC) {
-panic("dasd-ipl: IPL1 data invalid. Is this disk really bootable?\n");
-}
+return (ccwread->cmd_code == CCW_CMD_DASD_READ &&
+ccwtic->cmd_code == CCW_CMD_TIC);
 }
 
-static void check_ipl2(uint32_t ipl2_addr)
+static bool check_ipl2(uint32_t ipl2_addr)
 {
 Ccw0 *ccw = u32toptr(ipl2_addr);
 
-if (ipl2_addr == 0x00) {
-panic("IPL2 address invalid. Is this disk really bootable?\n");
-}
-if (ccw->cmd_code == 0x00) {
-panic("IPL2 ccw data invalid. Is this disk really bootable?\n");
-}
+return (ipl2_addr != 0x00 && ccw->cmd_code != 0x00);
 }
 
 static uint32_t read_ipl2_addr(void)
@@ -188,52 +179,67 @@ static void ipl1_fixup(void)
 ccwSearchTic->cda = ptr2u32(ccwSearchID);
 }
 
-static void run_ipl1(SubChannelId schid, uint16_t cutype)
+static int run_ipl1(SubChannelId schid, uint16_t cutype)
  {
 uint32_t startAddr = 0x08;
 
-if (do_cio(schid, cutype, startAddr, CCW_FMT0)) {
-panic("dasd-ipl: Failed to run IPL1 channel program\n");
-}
+return do_cio(schid, cutype, startAddr, CCW_FMT0);
 }
 
-static void run_ipl2(SubChannelId schid, uint16_t cutype, uint32_t addr)
+static int run_ipl2(SubChannelId schid, uint16_t cutype, uint32_t addr)
 {
-if (run_dynamic_ccw_program(schid, cutype, addr)) {
-panic("dasd-ipl: Failed to run IPL2 channel program\n");
-}
+return run_dynamic_ccw_program(schid, cutype, addr);
 }
 
 /*
  * Limitations in vfio-ccw support complicate the IPL process. Details can
  * be found in docs/devel/s390-dasd-ipl.rst
  */
-void dasd_ipl(SubChannelId schid, uint16_t cutype)
+int dasd_ipl(SubChannelId schid, uint16_t cutype)
 {
 PSWLegacy *pswl = (PSWLegacy *) 0x00;
 uint32_t ipl2_addr;
 
 /* Construct Read IPL CCW and run it to read IPL1 from boot disk */
 make_readipl();
-run_readipl(schid, cutype);
+if (run_readipl(schid, cutype)) {
+puts("Failed to run Read IPL channel program");
+return -EIO;
+}
+
 ipl2_addr = read_ipl2_addr();
-check_ipl1();
+
+if (!check_ipl1()) {
+puts("IPL1 invalid for DASD-IPL");
+return -EINVAL;
+}
 
 /*
  * Fixup IPL1 channel program to account for vfio-ccw limitations, then run
  * it to read IPL2 channel program from boot disk.
  */
 ipl1_fixup();
-run_ipl1(schid, cutype);
-check_ipl2(ipl2_addr);
+if (run_ipl1(schid, cutype)) {
+puts("Failed to run IPL1 channel program");
+return -EIO;
+}
+
+if (!check_ipl2(ipl2_addr)) {
+puts("IPL2 invalid for DASD-IPL");
+return -EINVAL;
+}
 
 /*
  * Run IPL2 channel program to read operating system code from boot disk
  */
-run_ipl2(schid, cutype, ipl2_addr);
+if (run_ipl2(schid, cutype, ipl2_addr)) {
+puts("Failed to run IPL2 channel program");
+return -EIO;
+}
 
 /* Transfer control to the guest operating system */
 pswl->mask |= PSW_MASK_EAMODE;   /* Force z-mode */
 pswl->addr |= PSW_MASK_BAMODE;   /* ...  */
 jump_to_low_kernel();
+return 1;
 }
-- 
2.45.1




[PATCH v5 01/19] hw/s390x/ipl: Provide more memory to the s390-ccw.img firmware

2024-10-19 Thread jrossi
From: Jared Rossi 

We are going to link the SLOF libc into the s390-ccw.img, and this
libc needs more memory for providing space for malloc() and friends.
Thus bump the memory size that we reserve for the bios to 3 MiB
instead of only 2 MiB. While we're at it, add a proper check that
there is really enough memory assigned to the machine before blindly
using it.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 
---
 hw/s390x/ipl.c | 10 +-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 5ab7433908..5f60977ceb 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -45,6 +45,7 @@
 #define INITRD_PARM_START   0x010408UL
 #define PARMFILE_START  0x001000UL
 #define ZIPL_IMAGE_START0x009000UL
+#define BIOS_MAX_SIZE   0x30UL
 #define IPL_PSW_MASK(PSW_MASK_32 | PSW_MASK_64)
 
 static bool iplb_extended_needed(void *opaque)
@@ -144,7 +145,14 @@ static void s390_ipl_realize(DeviceState *dev, Error 
**errp)
  * even if an external kernel has been defined.
  */
 if (!ipl->kernel || ipl->enforce_bios) {
-uint64_t fwbase = (MIN(ms->ram_size, 0x8000U) - 0x20) & 
~0xUL;
+uint64_t fwbase;
+
+if (ms->ram_size < BIOS_MAX_SIZE) {
+error_setg(errp, "not enough RAM to load the BIOS file");
+return;
+}
+
+fwbase = (MIN(ms->ram_size, 0x8000U) - BIOS_MAX_SIZE) & ~0xUL;
 
 bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->firmware);
 if (bios_filename == NULL) {
-- 
2.45.1




[PATCH v5 00/19] s390x: Add Full Boot Order Support

2024-10-19 Thread jrossi
From: Jared Rossi 

changes v4 -> v5:
- Fix a bug with per-deice loadparm support:
The machine loadparm is no longer overwritten by device values, which now
allows an empty machine loadparm to propagate to later devices even if
the primary boot device set an initial loadparm
- Fix two instances where changes were squashed into wrong patch
- Fix an instance where NULL_BLOCK_NR was returned instead of ERROR_BLOCK_NR
- Fix an instance of logical AND being used instead of bitwise AND
- Standardize all error values to be negative in all device type paths
- Minor stylistic changes and code simplification


changes v3 -> v4:
- Ensure signed-ness of return values is appropriate
- Add missing newline character in replacements of sclp_print_int()
- Add a missing return in a SCSI error path
- Restore break that was incorrectly removed for Virtio CU devices
- Remove an extra/early return that caused probing to end early
- Convert "good" device to scsi-cd in a cdrom-test for better coverage
- Minor stylistic clean-ups and variable name clarifications

Jared Rossi (19):
  hw/s390x/ipl: Provide more memory to the s390-ccw.img firmware
  pc-bios/s390-ccw: Use the libc from SLOF and remove sclp prints
  pc-bios/s390-ccw: Link the netboot code into the main s390-ccw.img
binary
  hw/s390x: Remove the possibility to load the s390-netboot.img binary
  pc-bios/s390-ccw: Merge netboot.mak into the main Makefile
  docs/system/s390x/bootdevices: Update the documentation about network
booting
  pc-bios/s390-ccw: Remove panics from ISO IPL path
  pc-bios/s390-ccw: Remove panics from ECKD IPL path
  pc-bios/s390-ccw: Remove panics from SCSI IPL path
  pc-bios/s390-ccw: Remove panics from DASD IPL path
  pc-bios/s390-ccw: Remove panics from Netboot IPL path
  pc-bios/s390-ccw: Enable failed IPL to return after error
  include/hw/s390x: Add include files for common IPL structs
  s390x: Add individual loadparm assignment to CCW device
  hw/s390x: Build an IPLB for each boot device
  s390x: Rebuild IPLB for SCSI device directly from DIAG308
  pc-bios/s390x: Enable multi-device boot loop
  docs/system: Update documentation for s390x IPL
  tests/qtest: Add s390x boot order tests to cdrom-test.c

 docs/system/bootindex.rst |   7 +-
 docs/system/s390x/bootdevices.rst |  29 +-
 pc-bios/s390-ccw/netboot.mak  |  62 
 hw/s390x/ccw-device.h |   2 +
 hw/s390x/ipl.h| 123 +---
 include/hw/s390x/ipl/qipl.h   | 127 +
 pc-bios/s390-ccw/bootmap.h|  20 +-
 pc-bios/s390-ccw/cio.h|   2 +
 pc-bios/s390-ccw/dasd-ipl.h   |   2 +-
 pc-bios/s390-ccw/iplb.h   | 108 ++-
 pc-bios/s390-ccw/libc.h   |  89 --
 pc-bios/s390-ccw/s390-ccw.h   |  36 +--
 pc-bios/s390-ccw/virtio.h |   3 +-
 hw/s390x/ccw-device.c |  46 +++
 hw/s390x/ipl.c| 282 +-
 hw/s390x/s390-virtio-ccw.c|  28 +-
 hw/s390x/sclp.c   |   9 +-
 pc-bios/s390-ccw/bootmap.c| 455 --
 pc-bios/s390-ccw/cio.c|  81 +++---
 pc-bios/s390-ccw/dasd-ipl.c   |  71 ++---
 pc-bios/s390-ccw/jump2ipl.c   |  22 +-
 pc-bios/s390-ccw/libc.c   |  88 --
 pc-bios/s390-ccw/main.c   |  97 ---
 pc-bios/s390-ccw/menu.c   |  51 ++--
 pc-bios/s390-ccw/netmain.c|  38 ++-
 pc-bios/s390-ccw/sclp.c   |   7 +-
 pc-bios/s390-ccw/virtio-blkdev.c  |  12 +-
 pc-bios/s390-ccw/virtio-net.c |   7 +-
 pc-bios/s390-ccw/virtio-scsi.c| 160 +++
 pc-bios/s390-ccw/virtio.c |  67 +++--
 target/s390x/diag.c   |   9 +-
 tests/qtest/cdrom-test.c  |  24 ++
 pc-bios/meson.build   |   1 -
 pc-bios/s390-ccw/Makefile |  69 -
 pc-bios/s390-netboot.img  | Bin 67232 -> 0 bytes
 35 files changed, 1158 insertions(+), 1076 deletions(-)
 delete mode 100644 pc-bios/s390-ccw/netboot.mak
 create mode 100644 include/hw/s390x/ipl/qipl.h
 delete mode 100644 pc-bios/s390-ccw/libc.h
 delete mode 100644 pc-bios/s390-ccw/libc.c
 delete mode 100644 pc-bios/s390-netboot.img

-- 
2.45.1




[PATCH v5 08/19] pc-bios/s390-ccw: Remove panics from ECKD IPL path

2024-10-19 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from ECKD block device IPL specific functions so that
error recovery may be possible in the future.

Functions that would previously panic now provide a return code.

Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/bootmap.h |   1 +
 pc-bios/s390-ccw/bootmap.c | 187 +
 2 files changed, 130 insertions(+), 58 deletions(-)

diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
index 3cb573b86b..95943441d3 100644
--- a/pc-bios/s390-ccw/bootmap.h
+++ b/pc-bios/s390-ccw/bootmap.h
@@ -16,6 +16,7 @@
 
 typedef uint64_t block_number_t;
 #define NULL_BLOCK_NR 0xULL
+#define ERROR_BLOCK_NR 0xfffeULL
 
 #define FREE_SPACE_FILLER '\xAA'
 
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index af73254acb..b9596e28c7 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -145,14 +145,17 @@ static block_number_t load_eckd_segments(block_number_t 
blk, bool ldipl,
 bool more_data;
 
 memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs));
-read_block(blk, bprs, "BPRS read failed");
+if (virtio_read(blk, bprs)) {
+puts("BPRS read failed");
+return ERROR_BLOCK_NR;
+}
 
 do {
 more_data = false;
 for (j = 0;; j++) {
 block_nr = gen_eckd_block_num(&bprs[j].xeckd, ldipl);
 if (is_null_block_number(block_nr)) { /* end of chunk */
-break;
+return NULL_BLOCK_NR;
 }
 
 /* we need the updated blockno for the next indirect entry
@@ -163,15 +166,20 @@ static block_number_t load_eckd_segments(block_number_t 
blk, bool ldipl,
 }
 
 /* List directed pointer does not store block size */
-IPL_assert(ldipl || block_size_ok(bprs[j].xeckd.bptr.size),
-   "bad chunk block size");
+if (!ldipl && !block_size_ok(bprs[j].xeckd.bptr.size)) {
+puts("Bad chunk block size");
+return ERROR_BLOCK_NR;
+}
 
 if (!eckd_valid_address(&bprs[j].xeckd, ldipl)) {
 /*
  * If an invalid address is found during LD-IPL then break and
- * retry as CCW
+ * retry as CCW-IPL, otherwise abort on error
  */
-IPL_assert(ldipl, "bad chunk ECKD addr");
+if (!ldipl) {
+puts("Bad chunk ECKD address");
+return ERROR_BLOCK_NR;
+}
 break;
 }
 
@@ -189,7 +197,10 @@ static block_number_t load_eckd_segments(block_number_t 
blk, bool ldipl,
  * I.e. the next ptr must point to the unused memory area
  */
 memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs));
-read_block(block_nr, bprs, "BPRS continuation read failed");
+if (virtio_read(block_nr, bprs)) {
+puts("BPRS continuation read failed");
+return ERROR_BLOCK_NR;
+}
 more_data = true;
 break;
 }
@@ -198,7 +209,10 @@ static block_number_t load_eckd_segments(block_number_t 
blk, bool ldipl,
  * to memory (address).
  */
 rc = virtio_read_many(block_nr, (void *)(*address), count + 1);
-IPL_assert(rc == 0, "code chunk read failed");
+if (rc != 0) {
+puts("Code chunk read failed");
+return ERROR_BLOCK_NR;
+}
 
 *address += (count + 1) * virtio_get_block_size();
 }
@@ -232,7 +246,10 @@ static int eckd_get_boot_menu_index(block_number_t 
s1b_block_nr)
 
 /* Get Stage1b data */
 memset(sec, FREE_SPACE_FILLER, sizeof(sec));
-read_block(s1b_block_nr, s1b, "Cannot read stage1b boot loader");
+if (virtio_read(s1b_block_nr, s1b)) {
+puts("Cannot read stage1b boot loader");
+return -EIO;
+}
 
 memset(_s2, FREE_SPACE_FILLER, sizeof(_s2));
 
@@ -244,7 +261,10 @@ static int eckd_get_boot_menu_index(block_number_t 
s1b_block_nr)
 break;
 }
 
-read_block(cur_block_nr, s2_cur_blk, "Cannot read stage2 boot loader");
+if (virtio_read(cur_block_nr, s2_cur_blk)) {
+puts("Cannot read stage2 boot loader");
+return -EIO;
+}
 
 if (find_zipl_boot_menu_banner(&banner_offset)) {
 /*
@@ -252,8 +272,10 @@ static int eckd_get_boot_menu_index(block_number_t 
s1b_block_nr)
  * possibility of menu data spanning multiple blocks.
  */
 if (prev_block_nr) {
-read_block(prev_block_nr, s2_prev_blk,
-   "Cannot read stage2 boot loader");
+if (virtio_read(prev_block_nr, s2_prev_blk)) {
+puts("Cannot read stage2 boot loader");
+   

[PATCH v5 05/19] pc-bios/s390-ccw: Merge netboot.mak into the main Makefile

2024-10-19 Thread jrossi
From: Jared Rossi 

Now that the netboot code has been merged into the main s390-ccw.img,
it also does not make sense to keep the build rules in a separate
file. Thus let's merge netboot.mak into the main Makefile.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/netboot.mak |  45 -
 pc-bios/s390-ccw/Makefile|  47 ++-
 pc-bios/s390-netboot.img | Bin 67232 -> 0 bytes
 3 files changed, 46 insertions(+), 46 deletions(-)
 delete mode 100644 pc-bios/s390-ccw/netboot.mak
 delete mode 100644 pc-bios/s390-netboot.img

diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak
deleted file mode 100644
index 0a24257ff4..00
--- a/pc-bios/s390-ccw/netboot.mak
+++ /dev/null
@@ -1,45 +0,0 @@
-
-# libc files:
-
-LIBC_CFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
- -MMD -MP -MT $@ -MF $(@:%.o=%.d)
-
-CTYPE_OBJS = isdigit.o isxdigit.o toupper.o
-%.o : $(SLOF_DIR)/lib/libc/ctype/%.c
-   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
-
-STRING_OBJS = strcat.o strchr.o strrchr.o strcpy.o strlen.o strncpy.o \
- strcmp.o strncmp.o strcasecmp.o strncasecmp.o strstr.o \
- memset.o memcpy.o memmove.o memcmp.o
-%.o : $(SLOF_DIR)/lib/libc/string/%.c
-   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
-
-STDLIB_OBJS = atoi.o atol.o strtoul.o strtol.o rand.o malloc.o free.o
-%.o : $(SLOF_DIR)/lib/libc/stdlib/%.c
-   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
-
-STDIO_OBJS = sprintf.o snprintf.o vfprintf.o vsnprintf.o vsprintf.o fprintf.o \
-printf.o putc.o puts.o putchar.o stdchnls.o fileno.o
-%.o : $(SLOF_DIR)/lib/libc/stdio/%.c
-   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
-
-sbrk.o: $(SLOF_DIR)/slof/sbrk.c
-   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
-
-LIBCOBJS := $(STRING_OBJS) $(CTYPE_OBJS) $(STDLIB_OBJS) $(STDIO_OBJS) sbrk.o
-
-libc.a: $(LIBCOBJS)
-   $(call quiet-command,$(AR) -rc $@ $^,Creating static library)
-
-# libnet files:
-
-LIBNETOBJS := args.o dhcp.o dns.o icmpv6.o ipv6.o tcp.o udp.o bootp.o \
- dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o pxelinux.o
-LIBNETCFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
-  -DDHCPARCH=0x1F -MMD -MP -MT $@ -MF $(@:%.o=%.d)
-
-%.o : $(SLOF_DIR)/lib/libnet/%.c
-   $(call quiet-command,$(CC) $(LIBNETCFLAGS) -c -o $@ $<,Compiling)
-
-libnet.a: $(LIBNETOBJS)
-   $(call quiet-command,$(AR) -rc $@ $^,Creating static library)
diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
index cf6859823a..27cbb354af 100644
--- a/pc-bios/s390-ccw/Makefile
+++ b/pc-bios/s390-ccw/Makefile
@@ -61,7 +61,52 @@ config-cc.mak: Makefile
$(call cc-option,-march=z900,-march=z10)) 3> config-cc.mak
 -include config-cc.mak
 
-include $(SRC_PATH)/netboot.mak
+# libc files:
+
+LIBC_CFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
+ -MMD -MP -MT $@ -MF $(@:%.o=%.d)
+
+CTYPE_OBJS = isdigit.o isxdigit.o toupper.o
+%.o : $(SLOF_DIR)/lib/libc/ctype/%.c
+   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
+
+STRING_OBJS = strcat.o strchr.o strrchr.o strcpy.o strlen.o strncpy.o \
+ strcmp.o strncmp.o strcasecmp.o strncasecmp.o strstr.o \
+ memset.o memcpy.o memmove.o memcmp.o
+%.o : $(SLOF_DIR)/lib/libc/string/%.c
+   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
+
+STDLIB_OBJS = atoi.o atol.o strtoul.o strtol.o rand.o malloc.o free.o
+%.o : $(SLOF_DIR)/lib/libc/stdlib/%.c
+   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
+
+STDIO_OBJS = sprintf.o snprintf.o vfprintf.o vsnprintf.o vsprintf.o fprintf.o \
+printf.o putc.o puts.o putchar.o stdchnls.o fileno.o
+%.o : $(SLOF_DIR)/lib/libc/stdio/%.c
+   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
+
+sbrk.o: $(SLOF_DIR)/slof/sbrk.c
+   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
+
+LIBCOBJS := $(STRING_OBJS) $(CTYPE_OBJS) $(STDLIB_OBJS) $(STDIO_OBJS) sbrk.o
+
+libc.a: $(LIBCOBJS)
+   $(call quiet-command,$(AR) -rc $@ $^,Creating static library)
+
+# libnet files:
+
+LIBNETOBJS := args.o dhcp.o dns.o icmpv6.o ipv6.o tcp.o udp.o bootp.o \
+ dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o pxelinux.o
+LIBNETCFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
+  -DDHCPARCH=0x1F -MMD -MP -MT $@ -MF $(@:%.o=%.d)
+
+%.o : $(SLOF_DIR)/lib/libnet/%.c
+   $(call quiet-command,$(CC) $(LIBNETCFLAGS) -c -o $@ $<,Compiling)
+
+libnet.a: $(LIBNETOBJS)
+   $(call quiet-command,$(AR) -rc $@ $^,Creating static library)
+
+# Main targets:
 
 build-all: s390-ccw.img
 
diff --git a/pc-bios/s390-netboot.img b/pc-bios/s390-netboot.img
deleted file mode 100644
index 
6908e49f06801808b826d3a01f88132cf1b2f57c..00

[PATCH v5 03/19] pc-bios/s390-ccw: Link the netboot code into the main s390-ccw.img binary

2024-10-19 Thread jrossi
From: Jared Rossi 

We originally built a separate binary for the netboot code since it
was considered as experimental and we could not be sure that the
necessary SLOF module had been checked out. Time passed, the code
proved its usefulness, and the build system nowadays makes sure that
the SLOF module is checked out if you have a s390x compiler available
for building the s390-ccw bios. So there is no real compelling reason
anymore to keep the netboot code in a separate binary. Linking the
code together with the main s390-ccw.img will make future enhancements
much easier, like supporting more than one boot device.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/netboot.mak | 14 --
 pc-bios/s390-ccw/cio.h   |  2 ++
 pc-bios/s390-ccw/iplb.h  |  4 ++--
 pc-bios/s390-ccw/s390-ccw.h  |  3 +++
 pc-bios/s390-ccw/virtio.h|  1 -
 pc-bios/s390-ccw/bootmap.c   |  2 +-
 pc-bios/s390-ccw/main.c  | 10 +++---
 pc-bios/s390-ccw/netmain.c   | 15 ++-
 pc-bios/s390-ccw/Makefile| 13 +++--
 9 files changed, 24 insertions(+), 40 deletions(-)

diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak
index d2b3d8ee74..0a24257ff4 100644
--- a/pc-bios/s390-ccw/netboot.mak
+++ b/pc-bios/s390-ccw/netboot.mak
@@ -1,18 +1,4 @@
 
-NETOBJS := start.o sclp.o cio.o virtio.o virtio-net.o jump2ipl.o netmain.o
-
-LIBNET_INC := -I$(SLOF_DIR)/lib/libnet
-
-NETLDFLAGS := $(LDFLAGS) -Wl,-Ttext=0x780
-
-$(NETOBJS): EXTRA_CFLAGS += $(LIBC_INC) $(LIBNET_INC)
-
-s390-netboot.elf: $(NETOBJS) libnet.a libc.a
-   $(call quiet-command,$(CC) $(NETLDFLAGS) -o $@ $^,Linking)
-
-s390-netboot.img: s390-netboot.elf
-   $(call quiet-command,$(STRIP) --strip-unneeded $< -o $@,Stripping $< 
into)
-
 # libc files:
 
 LIBC_CFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
diff --git a/pc-bios/s390-ccw/cio.h b/pc-bios/s390-ccw/cio.h
index 8b18153deb..6a5e86ba01 100644
--- a/pc-bios/s390-ccw/cio.h
+++ b/pc-bios/s390-ccw/cio.h
@@ -361,6 +361,8 @@ typedef struct CcwSearchIdData {
 uint8_t record;
 } __attribute__((packed)) CcwSearchIdData;
 
+extern SubChannelId net_schid;
+
 int enable_mss_facility(void);
 void enable_subchannel(SubChannelId schid);
 uint16_t cu_type(SubChannelId schid);
diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h
index cb6ac8a880..3758698468 100644
--- a/pc-bios/s390-ccw/iplb.h
+++ b/pc-bios/s390-ccw/iplb.h
@@ -87,9 +87,9 @@ extern IplParameterBlock iplb 
__attribute__((__aligned__(PAGE_SIZE)));
 struct QemuIplParameters {
 uint8_t  qipl_flags;
 uint8_t  reserved1[3];
-uint64_t netboot_start_addr;
+uint64_t reserved2;
 uint32_t boot_menu_timeout;
-uint8_t  reserved2[12];
+uint8_t  reserved3[12];
 } __attribute__ ((packed));
 typedef struct QemuIplParameters QemuIplParameters;
 
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 6f6d95d170..6abb34e563 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -55,6 +55,9 @@ void write_iplb_location(void);
 unsigned int get_loadparm_index(void);
 void main(void);
 
+/* netmain.c */
+void netmain(void);
+
 /* sclp.c */
 void sclp_print(const char *string);
 void sclp_set_write_mask(uint32_t receive_mask, uint32_t send_mask);
diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
index 85bd9d1695..6f9a558ff5 100644
--- a/pc-bios/s390-ccw/virtio.h
+++ b/pc-bios/s390-ccw/virtio.h
@@ -253,7 +253,6 @@ struct VDev {
 uint8_t scsi_dev_heads;
 bool scsi_device_selected;
 ScsiDevice selected_scsi_device;
-uint64_t netboot_start_addr;
 uint32_t max_transfer;
 uint32_t guest_features[2];
 };
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 3cc79706be..414c3f1b47 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -929,7 +929,7 @@ void zipl_load(void)
 }
 
 if (virtio_get_device_type() == VIRTIO_ID_NET) {
-jump_to_IPL_code(vdev->netboot_start_addr);
+netmain();
 }
 
 ipl_scsi();
diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
index 203df20965..fc44da3161 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -38,8 +38,13 @@ LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */
  */
 void write_subsystem_identification(void)
 {
-lowcore->subchannel_id = blk_schid.sch_id;
-lowcore->subchannel_nr = blk_schid.sch_no;
+if (cutype == CU_TYPE_VIRTIO && virtio_get_device_type() == VIRTIO_ID_NET) 
{
+lowcore->subchannel_id = net_schid.sch_id;
+lowcore->subchannel_nr = net_schid.sch_no;
+} else {
+lowcore->subchannel_id = blk_schid.sch_id;
+lowcore->subchannel_nr = blk_schid.sch_no;
+}
 lowcore->io_int_parm = 0;
 }
 
@@ -231,7 +236,6 @@ static int virtio_setup(void)
 switch (vdev->senseid.cu_model) {
 case VIRTIO_ID_NET:
 puts("Network boot device detected");
-vdev->netboo

[PATCH v5 16/19] s390x: Rebuild IPLB for SCSI device directly from DIAG308

2024-10-19 Thread jrossi
From: Jared Rossi 

Because virtio-scsi type devices use a non-architected IPLB pbt code they cannot
be set and stored normally. Instead, the IPLB must be rebuilt during re-ipl.

As s390x does not natively support multiple boot devices, the devno field is
used to store the position in the boot order for the device.

Handling the rebuild as part of DIAG308 removes the need to check the devices
for invalid IPLBs later in the IPL.

Signed-off-by: Jared Rossi 
Acked-by: Thomas Huth 
---
 hw/s390x/ipl.h  | 11 --
 include/hw/s390x/ipl/qipl.h |  3 +-
 hw/s390x/ipl.c  | 74 ++---
 pc-bios/s390-ccw/jump2ipl.c | 11 --
 target/s390x/diag.c |  9 -
 5 files changed, 38 insertions(+), 70 deletions(-)

diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index 54eb48fd6e..d7d0b7bfd2 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -24,6 +24,7 @@
 
 void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp);
 void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp);
+void s390_rebuild_iplb(uint16_t index, IplParameterBlock *iplb);
 void s390_ipl_update_diag308(IplParameterBlock *iplb);
 int s390_ipl_prepare_pv_header(Error **errp);
 int s390_ipl_pv_unpack(void);
@@ -65,7 +66,8 @@ struct S390IPLState {
 bool enforce_bios;
 bool iplb_valid;
 bool iplb_valid_pv;
-bool netboot;
+bool rebuilt_iplb;
+uint16_t iplb_index;
 /* reset related properties don't have to be migrated or reset */
 enum s390_reset reset_type;
 int reset_cpu_index;
@@ -172,11 +174,14 @@ static inline bool iplb_valid_pv(IplParameterBlock *iplb)
 
 static inline bool iplb_valid(IplParameterBlock *iplb)
 {
+uint32_t len = be32_to_cpu(iplb->len);
+
 switch (iplb->pbt) {
 case S390_IPL_TYPE_FCP:
-return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_FCP_LEN;
+return len >= S390_IPLB_MIN_FCP_LEN;
 case S390_IPL_TYPE_CCW:
-return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_CCW_LEN;
+return len >= S390_IPLB_MIN_CCW_LEN;
+case S390_IPL_TYPE_QEMU_SCSI:
 default:
 return false;
 }
diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
index 1da4f75aa8..682439 100644
--- a/include/hw/s390x/ipl/qipl.h
+++ b/include/hw/s390x/ipl/qipl.h
@@ -29,7 +29,8 @@
  */
 struct QemuIplParameters {
 uint8_t  qipl_flags;
-uint8_t  reserved1[3];
+uint8_t  index;
+uint8_t  reserved1[2];
 uint64_t reserved2;
 uint32_t boot_menu_timeout;
 uint8_t  reserved3[2];
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index ed152a9dd2..6702c6f80f 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -448,7 +448,6 @@ void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t 
*ebcdic_lp)
 
 static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
 {
-S390IPLState *ipl = get_ipl_device();
 CcwDevice *ccw_dev = NULL;
 SCSIDevice *sd;
 int devtype;
@@ -481,9 +480,6 @@ static bool s390_build_iplb(DeviceState *dev_st, 
IplParameterBlock *iplb)
 iplb->ccw.ssid = ccw_dev->sch->ssid & 3;
 break;
 case CCW_DEVTYPE_VIRTIO_NET:
-/* The S390IPLState netboot is true if ANY IPLB may use netboot */
-ipl->netboot = true;
-/* Fall through to CCW_DEVTYPE_VIRTIO case */
 case CCW_DEVTYPE_VIRTIO:
 iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
 iplb->blk0_len =
@@ -508,6 +504,16 @@ static bool s390_build_iplb(DeviceState *dev_st, 
IplParameterBlock *iplb)
 return false;
 }
 
+void s390_rebuild_iplb(uint16_t dev_index, IplParameterBlock *iplb)
+{
+S390IPLState *ipl = get_ipl_device();
+uint16_t index;
+index = ipl->rebuilt_iplb ? ipl->iplb_index : dev_index;
+
+ipl->rebuilt_iplb = s390_build_iplb(get_boot_device(index), iplb);
+ipl->iplb_index = index;
+}
+
 static bool s390_init_all_iplbs(S390IPLState *ipl)
 {
 int iplb_num = 0;
@@ -564,44 +570,6 @@ static bool s390_init_all_iplbs(S390IPLState *ipl)
 return iplb_num;
 }
 
-static bool is_virtio_ccw_device_of_type(IplParameterBlock *iplb,
- int virtio_id)
-{
-uint8_t cssid;
-uint8_t ssid;
-uint16_t devno;
-uint16_t schid;
-SubchDev *sch = NULL;
-
-if (iplb->pbt != S390_IPL_TYPE_CCW) {
-return false;
-}
-
-devno = be16_to_cpu(iplb->ccw.devno);
-ssid = iplb->ccw.ssid & 3;
-
-for (schid = 0; schid < MAX_SCHID; schid++) {
-for (cssid = 0; cssid < MAX_CSSID; cssid++) {
-sch = css_find_subch(1, cssid, ssid, schid);
-
-if (sch && sch->devno == devno) {
-return sch->id.cu_model == virtio_id;
-}
-}
-}
-return false;
-}
-
-static bool is_virtio_net_device(IplParameterBlock *iplb)
-{
-return is_virtio_ccw_device_of_type(iplb, VIRTIO_ID_NET);
-}
-
-static bool is_virtio_scsi_device(IplParameterBlock *iplb)
-{
-return is_virtio_cc

[PATCH v5 14/19] s390x: Add individual loadparm assignment to CCW device

2024-10-19 Thread jrossi
From: Jared Rossi 

Add a loadparm property to the VirtioCcwDevice object so that different
loadparms can be defined on a per-device basis for CCW boot devices.

The machine/global loadparm is still supported. If both a global and per-device
loadparm are defined, the per-device value will override the global value for
that device, but any other devices that do not specify a per-device loadparm
will still use the global loadparm.

It is invalid to assign a loadparm to a non-boot device.

Signed-off-by: Jared Rossi 
---
 hw/s390x/ccw-device.h   |  2 ++
 hw/s390x/ipl.h  |  3 +-
 include/hw/s390x/ipl/qipl.h |  1 +
 hw/s390x/ccw-device.c   | 46 +
 hw/s390x/ipl.c  | 68 ++---
 hw/s390x/s390-virtio-ccw.c  | 18 +-
 hw/s390x/sclp.c |  9 ++---
 pc-bios/s390-ccw/main.c | 10 --
 8 files changed, 102 insertions(+), 55 deletions(-)

diff --git a/hw/s390x/ccw-device.h b/hw/s390x/ccw-device.h
index 5feeb0ee7a..1e1737c0f3 100644
--- a/hw/s390x/ccw-device.h
+++ b/hw/s390x/ccw-device.h
@@ -26,6 +26,8 @@ struct CcwDevice {
 CssDevId dev_id;
 /* The actual busid of the virtual subchannel. */
 CssDevId subch_id;
+/* If set, use this loadparm value when device is boot target */
+uint8_t loadparm[8];
 };
 typedef struct CcwDevice CcwDevice;
 
diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index fa394c339d..b670bad551 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -21,7 +21,8 @@
 
 #define DIAG308_FLAGS_LP_VALID 0x80
 
-int s390_ipl_set_loadparm(uint8_t *loadparm);
+void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp);
+void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp);
 void s390_ipl_update_diag308(IplParameterBlock *iplb);
 int s390_ipl_prepare_pv_header(Error **errp);
 int s390_ipl_pv_unpack(void);
diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
index 0ef04af027..b67d2ae061 100644
--- a/include/hw/s390x/ipl/qipl.h
+++ b/include/hw/s390x/ipl/qipl.h
@@ -18,6 +18,7 @@
 
 #define QIPL_ADDRESS  0xcc
 #define LOADPARM_LEN8
+#define NO_LOADPARM "\0\0\0\0\0\0\0\0"
 
 /*
  * The QEMU IPL Parameters will be stored at absolute address
diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c
index 14c24e3890..230cc09e03 100644
--- a/hw/s390x/ccw-device.c
+++ b/hw/s390x/ccw-device.c
@@ -13,6 +13,10 @@
 #include "ccw-device.h"
 #include "hw/qdev-properties.h"
 #include "qemu/module.h"
+#include "ipl.h"
+#include "qapi/visitor.h"
+#include "qemu/ctype.h"
+#include "qapi/error.h"
 
 static void ccw_device_refill_ids(CcwDevice *dev)
 {
@@ -37,10 +41,52 @@ static bool ccw_device_realize(CcwDevice *dev, Error **errp)
 return true;
 }
 
+static void ccw_device_get_loadparm(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+CcwDevice *dev = CCW_DEVICE(obj);
+char *str = g_strndup((char *) dev->loadparm, sizeof(dev->loadparm));
+
+visit_type_str(v, name, &str, errp);
+g_free(str);
+}
+
+static void ccw_device_set_loadparm(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+CcwDevice *dev = CCW_DEVICE(obj);
+char *val;
+int index;
+
+index = object_property_get_int(obj, "bootindex", NULL);
+
+if (index < 0) {
+error_setg(errp, "LOADPARM is only valid for boot devices!");
+}
+
+if (!visit_type_str(v, name, &val, errp)) {
+return;
+}
+
+s390_ipl_fmt_loadparm(dev->loadparm, val, errp);
+}
+
+static const PropertyInfo ccw_loadparm = {
+.name  = "ccw_loadparm",
+.description = "Up to 8 chars in set of [A-Za-z0-9. ] to pass"
+" to the guest loader/kernel",
+.get = ccw_device_get_loadparm,
+.set = ccw_device_set_loadparm,
+};
+
 static Property ccw_device_properties[] = {
 DEFINE_PROP_CSS_DEV_ID("devno", CcwDevice, devno),
 DEFINE_PROP_CSS_DEV_ID_RO("dev_id", CcwDevice, dev_id),
 DEFINE_PROP_CSS_DEV_ID_RO("subch_id", CcwDevice, subch_id),
+DEFINE_PROP("loadparm", CcwDevice, loadparm, ccw_loadparm,
+typeof(uint8_t[8])),
 DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 8c490eeb52..656996b500 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -34,6 +34,7 @@
 #include "qemu/config-file.h"
 #include "qemu/cutils.h"
 #include "qemu/option.h"
+#include "qemu/ctype.h"
 #include "standard-headers/linux/virtio_ids.h"
 
 #define KERN_IMAGE_START0x01UL
@@ -397,12 +398,43 @@ static CcwDevice *s390_get_ccw_device(DeviceState 
*dev_st, int *devtype)
 return ccw_dev;
 }
 
+void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp)
+{
+int i;
+
+/* Initialize the loadparm with spaces */
+memset(loadparm, ' ', LOADPARM_LEN);
+for (i = 0; i < LOADPARM_LEN && str[i]; i++) {
+   

[PATCH v5 17/19] pc-bios/s390x: Enable multi-device boot loop

2024-10-19 Thread jrossi
From: Jared Rossi 

Allow attempts to boot from multiple IPL devices. If the first device fails to
IPL, select the pre-built IPLB for the next device in the boot order and attempt
to IPL from it. Continue this process until IPL is successful or there are no
devices left to try.

Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/iplb.h | 24 
 pc-bios/s390-ccw/jump2ipl.c |  7 +++---
 pc-bios/s390-ccw/main.c | 45 +++--
 pc-bios/s390-ccw/netmain.c  |  2 +-
 4 files changed, 57 insertions(+), 21 deletions(-)

diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h
index 16643f5879..08f259ff31 100644
--- a/pc-bios/s390-ccw/iplb.h
+++ b/pc-bios/s390-ccw/iplb.h
@@ -17,9 +17,11 @@
 #endif
 
 #include 
+#include 
 
 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
@@ -49,4 +51,26 @@ static inline bool set_iplb(IplParameterBlock *iplb)
 return manage_iplb(iplb, false);
 }
 
+/*
+ * The IPL started on the device, but failed in some way.  If the IPLB chain
+ * still has more devices left to try, use the next device in order.
+ */
+static inline bool load_next_iplb(void)
+{
+IplParameterBlock *next_iplb;
+
+if (qipl.chain_len < 1) {
+return false;
+}
+
+qipl.index++;
+next_iplb = (IplParameterBlock *) qipl.next_iplb;
+memcpy(&iplb, next_iplb, sizeof(IplParameterBlock));
+
+qipl.chain_len--;
+qipl.next_iplb = qipl.next_iplb + sizeof(IplParameterBlock);
+
+return true;
+}
+
 #endif /* IPLB_H */
diff --git a/pc-bios/s390-ccw/jump2ipl.c b/pc-bios/s390-ccw/jump2ipl.c
index 99d18947d1..86321d0f46 100644
--- a/pc-bios/s390-ccw/jump2ipl.c
+++ b/pc-bios/s390-ccw/jump2ipl.c
@@ -45,9 +45,10 @@ int jump_to_IPL_code(uint64_t address)
  */
 if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) {
 iplb.devno = qipl.index;
-if (!set_iplb(&iplb)) {
-panic("Failed to set IPLB");
-}
+}
+
+if (have_iplb && !set_iplb(&iplb)) {
+panic("Failed to set IPLB");
 }
 
 /*
diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
index ab4709e16e..a4d1c05aac 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -23,7 +23,7 @@ static SubChannelId blk_schid = { .one = 1 };
 static char loadparm_str[LOADPARM_LEN + 1];
 QemuIplParameters qipl;
 IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
-static bool have_iplb;
+bool have_iplb;
 static uint16_t cutype;
 LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */
 
@@ -55,6 +55,12 @@ void write_iplb_location(void)
 }
 }
 
+static void copy_qipl(void)
+{
+QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS;
+memcpy(&qipl, early_qipl, sizeof(QemuIplParameters));
+}
+
 unsigned int get_loadparm_index(void)
 {
 return atoi(loadparm_str);
@@ -152,6 +158,7 @@ static void menu_setup(void)
 
 /* If loadparm was set to any other value, then do not enable menu */
 if (memcmp(loadparm_str, LOADPARM_EMPTY, LOADPARM_LEN) != 0) {
+menu_set_parms(qipl.qipl_flags & ~BOOT_MENU_FLAG_MASK, 0);
 return;
 }
 
@@ -183,7 +190,6 @@ static void css_setup(void)
 static void boot_setup(void)
 {
 char lpmsg[] = "LOADPARM=[]\n";
-have_iplb = store_iplb(&iplb);
 
 if (memcmp(iplb.loadparm, NO_LOADPARM, LOADPARM_LEN) != 0) {
 ebcdic_to_ascii((char *) iplb.loadparm, loadparm_str, LOADPARM_LEN);
@@ -191,6 +197,10 @@ static void boot_setup(void)
 sclp_get_loadparm_ascii(loadparm_str);
 }
 
+if (have_iplb) {
+menu_setup();
+}
+
 memcpy(lpmsg + 10, loadparm_str, 8);
 puts(lpmsg);
 
@@ -208,6 +218,7 @@ static bool find_boot_device(void)
 
 switch (iplb.pbt) {
 case S390_IPL_TYPE_CCW:
+vdev->scsi_device_selected = false;
 debug_print_int("device no. ", iplb.ccw.devno);
 blk_schid.ssid = iplb.ccw.ssid & 0x3;
 debug_print_int("ssid ", blk_schid.ssid);
@@ -231,15 +242,8 @@ static bool find_boot_device(void)
 static int virtio_setup(void)
 {
 VDev *vdev = virtio_get_device();
-QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS;
 int ret;
 
-memcpy(&qipl, early_qipl, sizeof(QemuIplParameters));
-
-if (have_iplb) {
-menu_setup();
-}
-
 switch (vdev->senseid.cu_model) {
 case VIRTIO_ID_NET:
 puts("Network boot device detected");
@@ -271,10 +275,9 @@ static void ipl_boot_device(void)
 dasd_ipl(blk_schid, cutype);
 break;
 case CU_TYPE_VIRTIO:
-if (virtio_setup()) {
-return;/* Only returns in case of errors */
+if (virtio_setup() == 0) {
+zipl_load();
 }
-zipl_load();
 break;
 default:
 printf("Attempting to boot from unexpected device type 0x%X\n", 
cutype);
@@ -307,14 +310,22 @@

[PATCH v5 12/19] pc-bios/s390-ccw: Enable failed IPL to return after error

2024-10-19 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from IPL functions such that a return code is propagated
back to the main IPL calling function (rather than terminating immediately),
which facilitates possible error recovery in the future.

A select few panics remain, which indicate fatal non-devices errors that must
result in termination.

Signed-off-by: Jared Rossi 
Reviewed-by: Thomas Huth 
---
 pc-bios/s390-ccw/s390-ccw.h  |  2 +-
 pc-bios/s390-ccw/virtio.h|  2 +-
 pc-bios/s390-ccw/bootmap.c   | 53 ++
 pc-bios/s390-ccw/cio.c   |  3 +-
 pc-bios/s390-ccw/jump2ipl.c  |  5 ++-
 pc-bios/s390-ccw/main.c  | 32 +---
 pc-bios/s390-ccw/virtio-blkdev.c |  2 +-
 pc-bios/s390-ccw/virtio.c| 65 +---
 8 files changed, 108 insertions(+), 56 deletions(-)

diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 344ad15655..6cdce3e5e5 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -78,7 +78,7 @@ void zipl_load(void);
 
 /* jump2ipl.c */
 void write_reset_psw(uint64_t psw);
-void jump_to_IPL_code(uint64_t address);
+int jump_to_IPL_code(uint64_t address);
 void jump_to_low_kernel(void);
 
 /* menu.c */
diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
index 6f9a558ff5..9faf3986b1 100644
--- a/pc-bios/s390-ccw/virtio.h
+++ b/pc-bios/s390-ccw/virtio.h
@@ -274,7 +274,7 @@ void vring_send_buf(VRing *vr, void *p, int len, int flags);
 int vr_poll(VRing *vr);
 int vring_wait_reply(void);
 int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd);
-void virtio_setup_ccw(VDev *vdev);
+int virtio_setup_ccw(VDev *vdev);
 
 int virtio_net_init(void *mac_addr);
 
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 95ef9104d0..56f2f75640 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -62,15 +62,34 @@ static void *s2_prev_blk = _s2;
 static void *s2_cur_blk = _s2 + MAX_SECTOR_SIZE;
 static void *s2_next_blk = _s2 + MAX_SECTOR_SIZE * 2;
 
-static inline void verify_boot_info(BootInfo *bip)
+static inline int verify_boot_info(BootInfo *bip)
 {
-IPL_assert(magic_match(bip->magic, ZIPL_MAGIC), "No zIPL sig in BootInfo");
-IPL_assert(bip->version == BOOT_INFO_VERSION, "Wrong zIPL version");
-IPL_assert(bip->bp_type == BOOT_INFO_BP_TYPE_IPL, "DASD is not for IPL");
-IPL_assert(bip->dev_type == BOOT_INFO_DEV_TYPE_ECKD, "DASD is not ECKD");
-IPL_assert(bip->flags == BOOT_INFO_FLAGS_ARCH, "Not for this arch");
-IPL_assert(block_size_ok(bip->bp.ipl.bm_ptr.eckd.bptr.size),
-   "Bad block size in zIPL section of the 1st record.");
+if (!magic_match(bip->magic, ZIPL_MAGIC)) {
+puts("No zIPL sig in BootInfo");
+return -EINVAL;
+}
+if (bip->version != BOOT_INFO_VERSION) {
+puts("Wrong zIPL version");
+return -EINVAL;
+}
+if (bip->bp_type != BOOT_INFO_BP_TYPE_IPL) {
+puts("DASD is not for IPL");
+return -ENODEV;
+}
+if (bip->dev_type != BOOT_INFO_DEV_TYPE_ECKD) {
+puts("DASD is not ECKD");
+return -ENODEV;
+}
+if (bip->flags != BOOT_INFO_FLAGS_ARCH) {
+puts("Not for this arch");
+return -EINVAL;
+}
+if (!block_size_ok(bip->bp.ipl.bm_ptr.eckd.bptr.size)) {
+puts("Bad block size in zIPL section of 1st record");
+return -EINVAL;
+}
+
+return 0;
 }
 
 static void eckd_format_chs(ExtEckdBlockPtr *ptr,  bool ldipl,
@@ -367,8 +386,8 @@ static int run_eckd_boot_script(block_number_t bmt_block_nr,
 puts("Unknown script entry type");
 return -EINVAL;
 }
-write_reset_psw(bms->entry[i].address.load_address); /* no return */
-jump_to_IPL_code(0); /* no return */
+write_reset_psw(bms->entry[i].address.load_address);
+jump_to_IPL_code(0);
 return -1;
 }
 
@@ -1067,16 +1086,19 @@ void zipl_load(void)
 
 if (vdev->is_cdrom) {
 ipl_iso_el_torito();
-panic("\n! Cannot IPL this ISO image !\n");
+puts("Failed to IPL this ISO image!");
+return;
 }
 
 if (virtio_get_device_type() == VIRTIO_ID_NET) {
 netmain();
-panic("\n! Cannot IPL from this network !\n");
+puts("Failed to IPL from this network!");
+return;
 }
 
 if (ipl_scsi()) {
-panic("\n! Cannot IPL this SCSI device !\n");
+puts("Failed to IPL from this SCSI device!");
+return;
 }
 
 switch (virtio_get_device_type()) {
@@ -1087,8 +1109,9 @@ void zipl_load(void)
 zipl_load_vscsi();
 break;
 default:
-panic("\n! Unknown IPL device type !\n");
+puts("Unknown IPL device type!");
+return;
 }
 
-puts("zIPL load failed.");
+puts("zIPL load failed!");
 }
diff --git a/pc-bios/s390-ccw/cio.c b/pc-bios/s390-ccw/cio.c
index 7b09a38c96..5d543da73f 100644
--- a/pc-bios/s390-ccw/cio.c
+++ b/pc-bios/s390-ccw/cio.c
@@ -59,7 +59,8 @@ u

[PATCH v5 04/19] hw/s390x: Remove the possibility to load the s390-netboot.img binary

2024-10-19 Thread jrossi
From: Jared Rossi 

Since the netboot code has now been merged into the main s390-ccw.img
binary, we don't need the separate s390-netboot.img anymore. Remove
it and the code that was responsible for loading it.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 
---
 hw/s390x/ipl.h | 12 +++--
 hw/s390x/ipl.c | 55 --
 hw/s390x/s390-virtio-ccw.c | 10 ++-
 pc-bios/meson.build|  1 -
 4 files changed, 6 insertions(+), 72 deletions(-)

diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index 57cd125769..b2105b616a 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -134,11 +134,8 @@ void s390_ipl_clear_reset_request(void);
 /*
  * The QEMU IPL Parameters will be stored at absolute address
  * 204 (0xcc) which means it is 32-bit word aligned but not
- * double-word aligned.
- * Placement of data fields in this area must account for
- * their alignment needs. E.g., netboot_start_address must
- * have an offset of 4 + n * 8 bytes within the struct in order
- * to keep it double-word aligned.
+ * double-word aligned. Placement of 64-bit data fields in this
+ * area must account for their alignment needs.
  * The total size of the struct must never exceed 28 bytes.
  * This definition must be kept in sync with the definition
  * in pc-bios/s390-ccw/iplb.h.
@@ -146,9 +143,9 @@ void s390_ipl_clear_reset_request(void);
 struct QemuIplParameters {
 uint8_t  qipl_flags;
 uint8_t  reserved1[3];
-uint64_t netboot_start_addr;
+uint64_t reserved2;
 uint32_t boot_menu_timeout;
-uint8_t  reserved2[12];
+uint8_t  reserved3[12];
 } QEMU_PACKED;
 typedef struct QemuIplParameters QemuIplParameters;
 
@@ -178,7 +175,6 @@ struct S390IPLState {
 char *initrd;
 char *cmdline;
 char *firmware;
-char *netboot_fw;
 uint8_t cssid;
 uint8_t ssid;
 uint16_t devno;
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 5f60977ceb..8c490eeb52 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -288,7 +288,6 @@ static Property s390_ipl_properties[] = {
 DEFINE_PROP_STRING("initrd", S390IPLState, initrd),
 DEFINE_PROP_STRING("cmdline", S390IPLState, cmdline),
 DEFINE_PROP_STRING("firmware", S390IPLState, firmware),
-DEFINE_PROP_STRING("netboot_fw", S390IPLState, netboot_fw),
 DEFINE_PROP_BOOL("enforce_bios", S390IPLState, enforce_bios, false),
 DEFINE_PROP_BOOL("iplbext_migration", S390IPLState, iplbext_migration,
  true),
@@ -480,56 +479,6 @@ int s390_ipl_set_loadparm(uint8_t *loadparm)
 return -1;
 }
 
-static int load_netboot_image(Error **errp)
-{
-MachineState *ms = MACHINE(qdev_get_machine());
-S390IPLState *ipl = get_ipl_device();
-char *netboot_filename;
-MemoryRegion *sysmem =  get_system_memory();
-MemoryRegion *mr = NULL;
-void *ram_ptr = NULL;
-int img_size = -1;
-
-mr = memory_region_find(sysmem, 0, 1).mr;
-if (!mr) {
-error_setg(errp, "Failed to find memory region at address 0");
-return -1;
-}
-
-ram_ptr = memory_region_get_ram_ptr(mr);
-if (!ram_ptr) {
-error_setg(errp, "No RAM found");
-goto unref_mr;
-}
-
-netboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->netboot_fw);
-if (netboot_filename == NULL) {
-error_setg(errp, "Could not find network bootloader '%s'",
-   ipl->netboot_fw);
-goto unref_mr;
-}
-
-img_size = load_elf_ram(netboot_filename, NULL, NULL, NULL,
-&ipl->start_addr,
-NULL, NULL, NULL, 1, EM_S390, 0, 0, NULL,
-false);
-
-if (img_size < 0) {
-img_size = load_image_size(netboot_filename, ram_ptr, ms->ram_size);
-ipl->start_addr = KERN_IMAGE_START;
-}
-
-if (img_size < 0) {
-error_setg(errp, "Failed to load network bootloader");
-}
-
-g_free(netboot_filename);
-
-unref_mr:
-memory_region_unref(mr);
-return img_size;
-}
-
 static bool is_virtio_ccw_device_of_type(IplParameterBlock *iplb,
  int virtio_id)
 {
@@ -754,10 +703,6 @@ void s390_ipl_prepare_cpu(S390CPU *cpu)
 ipl->iplb_valid = s390_gen_initial_iplb(ipl);
 }
 }
-if (ipl->netboot) {
-load_netboot_image(&error_fatal);
-ipl->qipl.netboot_start_addr = cpu_to_be64(ipl->start_addr);
-}
 s390_ipl_set_boot_menu(ipl);
 s390_ipl_prepare_qipl(cpu);
 }
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index 5aa8d207a3..529e53f308 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -197,11 +197,10 @@ static void s390_memory_init(MemoryRegion *ram)
 static void s390_init_ipl_dev(const char *kernel_filename,
   const char *kernel_cmdline,
   const char *initrd_filename, const char 
*firmware,
-  const char *netb

[PATCH v5 19/19] tests/qtest: Add s390x boot order tests to cdrom-test.c

2024-10-19 Thread jrossi
From: Jared Rossi 

Add two new qtests to verify that a valid IPL device can successfully boot after
failed IPL attempts from one or more invalid devices.

cdrom-test/as-fallback-device: Defines the primary boot target as a device that
is invalid for IPL and a second boot target that is valid for IPL. Ensures that
the valid device will be selected after the initial failed IPL.

cdrom-test/as-last-option: Defines the maximum number of boot devices (8)
where only the final entry in the boot order is valid. Ensures that a valid
device will be selected even after multiple failed IPL attempts from both
virtio-blk and virtio-scsi device types.

Signed-off-by: Jared Rossi 
---
 tests/qtest/cdrom-test.c | 24 
 1 file changed, 24 insertions(+)

diff --git a/tests/qtest/cdrom-test.c b/tests/qtest/cdrom-test.c
index 9d72b24e4b..c86725a511 100644
--- a/tests/qtest/cdrom-test.c
+++ b/tests/qtest/cdrom-test.c
@@ -213,6 +213,30 @@ static void add_s390x_tests(void)
 "-drive driver=null-co,read-zeroes=on,if=none,id=d1 "
 "-device virtio-blk,drive=d2,bootindex=1 "
 "-drive if=none,id=d2,media=cdrom,file=", test_cdboot);
+qtest_add_data_func("cdrom/boot/as-fallback-device",
+"-device virtio-serial -device virtio-scsi "
+"-device virtio-blk,drive=d1,bootindex=1 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d1 "
+"-device virtio-blk,drive=d2,bootindex=2 "
+"-drive if=none,id=d2,media=cdrom,file=", test_cdboot);
+qtest_add_data_func("cdrom/boot/as-last-option",
+"-device virtio-serial -device virtio-scsi "
+"-device virtio-blk,drive=d1,bootindex=1 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d1 "
+"-device virtio-blk,drive=d2,bootindex=2 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d2 "
+"-device virtio-blk,drive=d3,bootindex=3 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d3 "
+"-device scsi-hd,drive=d4,bootindex=4 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d4 "
+"-device scsi-hd,drive=d5,bootindex=5 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d5 "
+"-device virtio-blk,drive=d6,bootindex=6 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d6 "
+"-device scsi-hd,drive=d7,bootindex=7 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d7 "
+"-device scsi-cd,drive=d8,bootindex=8 "
+"-drive if=none,id=d8,media=cdrom,file=", test_cdboot);
 if (qtest_has_device("x-terminal3270")) {
 qtest_add_data_func("cdrom/boot/without-bootindex",
 "-device virtio-scsi -device virtio-serial "
-- 
2.45.1




[PATCH v5 07/19] pc-bios/s390-ccw: Remove panics from ISO IPL path

2024-10-19 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from IPL ISO El Torito specific functions so that error
recovery may be possible in the future.

Functions that would previously panic now provide a return code.

Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/bootmap.h  | 15 +++
 pc-bios/s390-ccw/s390-ccw.h |  1 +
 pc-bios/s390-ccw/bootmap.c  | 87 -
 3 files changed, 65 insertions(+), 38 deletions(-)

diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
index 4a7d8a91f1..3cb573b86b 100644
--- a/pc-bios/s390-ccw/bootmap.h
+++ b/pc-bios/s390-ccw/bootmap.h
@@ -385,17 +385,14 @@ static inline uint32_t iso_733_to_u32(uint64_t x)
 
 #define ISO_PRIMARY_VD_SECTOR 16
 
-static inline void read_iso_sector(uint32_t block_offset, void *buf,
-   const char *errmsg)
-{
-IPL_assert(virtio_read_many(block_offset, buf, 1) == 0, errmsg);
-}
-
-static inline void read_iso_boot_image(uint32_t block_offset, void *load_addr,
+static inline int read_iso_boot_image(uint32_t block_offset, void *load_addr,
uint32_t blks_to_load)
 {
-IPL_assert(virtio_read_many(block_offset, load_addr, blks_to_load) == 0,
-   "Failed to read boot image!");
+if (virtio_read_many(block_offset, load_addr, blks_to_load)) {
+puts("Failed to read boot image!");
+return -1;
+}
+return 0;
 }
 
 #define ISO9660_MAX_DIR_DEPTH 8
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 6abb34e563..3e844abd71 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -30,6 +30,7 @@ typedef unsigned long long u64;
 #define EIO 1
 #define EBUSY   2
 #define ENODEV  3
+#define EINVAL  4
 
 #ifndef MIN
 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 414c3f1b47..af73254acb 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -678,8 +678,10 @@ static bool is_iso_bc_entry_compatible(IsoBcSection *s)
 if (s->unused || !s->sector_count) {
 return false;
 }
-read_iso_sector(bswap32(s->load_rba), magic_sec,
-"Failed to read image sector 0");
+if (virtio_read(bswap32(s->load_rba), magic_sec)) {
+puts("Failed to read image sector 0");
+return false;
+}
 
 /* Checking bytes 8 - 32 for S390 Linux magic */
 return !memcmp(magic_sec + 8, linux_s390_magic, 24);
@@ -692,28 +694,35 @@ static uint32_t sec_offset[ISO9660_MAX_DIR_DEPTH];
 /* Remained directory space in bytes */
 static uint32_t dir_rem[ISO9660_MAX_DIR_DEPTH];
 
-static inline uint32_t iso_get_file_size(uint32_t load_rba)
+static inline long iso_get_file_size(uint32_t load_rba)
 {
 IsoVolDesc *vd = (IsoVolDesc *)sec;
 IsoDirHdr *cur_record = &vd->vd.primary.rootdir;
 uint8_t *temp = sec + ISO_SECTOR_SIZE;
 int level = 0;
 
-read_iso_sector(ISO_PRIMARY_VD_SECTOR, sec,
-"Failed to read ISO primary descriptor");
+if (virtio_read(ISO_PRIMARY_VD_SECTOR, sec)) {
+puts("Failed to read ISO primary descriptor");
+return -EIO;
+}
+
 sec_loc[0] = iso_733_to_u32(cur_record->ext_loc);
 dir_rem[0] = 0;
 sec_offset[0] = 0;
 
 while (level >= 0) {
-IPL_assert(sec_offset[level] <= ISO_SECTOR_SIZE,
-   "Directory tree structure violation");
+if (sec_offset[level] > ISO_SECTOR_SIZE) {
+puts("Directory tree structure violation");
+return -EIO;
+}
 
 cur_record = (IsoDirHdr *)(temp + sec_offset[level]);
 
 if (sec_offset[level] == 0) {
-read_iso_sector(sec_loc[level], temp,
-"Failed to read ISO directory");
+if (virtio_read(sec_loc[level], temp)) {
+puts("Failed to read ISO directory");
+return -EIO;
+}
 if (dir_rem[level] == 0) {
 /* Skip self and parent records */
 dir_rem[level] = iso_733_to_u32(cur_record->data_len) -
@@ -758,8 +767,10 @@ static inline uint32_t iso_get_file_size(uint32_t load_rba)
 if (dir_rem[level] == 0) {
 /* Nothing remaining */
 level--;
-read_iso_sector(sec_loc[level], temp,
-"Failed to read ISO directory");
+if (virtio_read(sec_loc[level], temp)) {
+puts("Failed to read ISO directory");
+return -EIO;
+}
 }
 }
 
@@ -774,19 +785,24 @@ static void load_iso_bc_entry(IsoBcSection *load)
  * is padded and ISO_SECTOR_SIZE bytes aligned
  */
 uint32_t blks_to_load = bswap16(s.sector_count) >> ET_SECTOR_SHIFT;
-uint32_t real_size = iso_get_file_size(bswap32(s.load_rba));
+long real_size = iso_get_file_size(bswap32(s.load_rba));
 
-if (real_size) {
+if (real_size > 0) {
 /* Round 

[PATCH v5 15/19] hw/s390x: Build an IPLB for each boot device

2024-10-19 Thread jrossi
From: Jared Rossi 

Build an IPLB for any device with a bootindex (up to a maximum of 8 devices).

The IPLB chain is placed immediately before the BIOS in memory. Because this
is not a fixed address, the location of the next IPLB and number of remaining
boot devices is stored in the QIPL global variable for possible later access by
the guest during IPL.

Signed-off-by: Jared Rossi 
---
 hw/s390x/ipl.h  |   1 +
 include/hw/s390x/ipl/qipl.h |   4 +-
 hw/s390x/ipl.c  | 129 
 3 files changed, 105 insertions(+), 29 deletions(-)

diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index b670bad551..54eb48fd6e 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -20,6 +20,7 @@
 #include "qom/object.h"
 
 #define DIAG308_FLAGS_LP_VALID 0x80
+#define MAX_BOOT_DEVS 8 /* Max number of devices that may have a bootindex */
 
 void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp);
 void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp);
diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
index b67d2ae061..1da4f75aa8 100644
--- a/include/hw/s390x/ipl/qipl.h
+++ b/include/hw/s390x/ipl/qipl.h
@@ -32,7 +32,9 @@ struct QemuIplParameters {
 uint8_t  reserved1[3];
 uint64_t reserved2;
 uint32_t boot_menu_timeout;
-uint8_t  reserved3[12];
+uint8_t  reserved3[2];
+uint16_t chain_len;
+uint64_t next_iplb;
 } QEMU_PACKED;
 typedef struct QemuIplParameters QemuIplParameters;
 
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 656996b500..ed152a9dd2 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -56,6 +56,13 @@ static bool iplb_extended_needed(void *opaque)
 return ipl->iplbext_migration;
 }
 
+/* Place the IPLB chain immediately before the BIOS in memory */
+static uint64_t find_iplb_chain_addr(uint64_t bios_addr, uint16_t count)
+{
+return (bios_addr & TARGET_PAGE_MASK)
+- (count * sizeof(IplParameterBlock));
+}
+
 static const VMStateDescription vmstate_iplb_extended = {
 .name = "ipl/iplb_extended",
 .version_id = 0,
@@ -398,6 +405,17 @@ static CcwDevice *s390_get_ccw_device(DeviceState *dev_st, 
int *devtype)
 return ccw_dev;
 }
 
+static uint64_t s390_ipl_map_iplb_chain(IplParameterBlock *iplb_chain)
+{
+S390IPLState *ipl = get_ipl_device();
+uint16_t count = ipl->qipl.chain_len;
+uint64_t len = sizeof(IplParameterBlock) * count;
+uint64_t chain_addr = find_iplb_chain_addr(ipl->bios_start_addr, count);
+
+cpu_physical_memory_write(chain_addr, iplb_chain, len);
+return chain_addr;
+}
+
 void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp)
 {
 int i;
@@ -428,54 +446,51 @@ void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t 
*ebcdic_lp)
 }
 }
 
-static bool s390_gen_initial_iplb(S390IPLState *ipl)
+static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
 {
-DeviceState *dev_st;
+S390IPLState *ipl = get_ipl_device();
 CcwDevice *ccw_dev = NULL;
 SCSIDevice *sd;
 int devtype;
 uint8_t *lp;
 
-dev_st = get_boot_device(0);
-if (dev_st) {
-ccw_dev = s390_get_ccw_device(dev_st, &devtype);
-}
-
 /*
  * Currently allow IPL only from CCW devices.
  */
+ccw_dev = s390_get_ccw_device(dev_st, &devtype);
 if (ccw_dev) {
 lp = ccw_dev->loadparm;
 
 switch (devtype) {
 case CCW_DEVTYPE_SCSI:
 sd = SCSI_DEVICE(dev_st);
-ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN);
-ipl->iplb.blk0_len =
+iplb->len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN);
+iplb->blk0_len =
 cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN - 
S390_IPLB_HEADER_LEN);
-ipl->iplb.pbt = S390_IPL_TYPE_QEMU_SCSI;
-ipl->iplb.scsi.lun = cpu_to_be32(sd->lun);
-ipl->iplb.scsi.target = cpu_to_be16(sd->id);
-ipl->iplb.scsi.channel = cpu_to_be16(sd->channel);
-ipl->iplb.scsi.devno = cpu_to_be16(ccw_dev->sch->devno);
-ipl->iplb.scsi.ssid = ccw_dev->sch->ssid & 3;
+iplb->pbt = S390_IPL_TYPE_QEMU_SCSI;
+iplb->scsi.lun = cpu_to_be32(sd->lun);
+iplb->scsi.target = cpu_to_be16(sd->id);
+iplb->scsi.channel = cpu_to_be16(sd->channel);
+iplb->scsi.devno = cpu_to_be16(ccw_dev->sch->devno);
+iplb->scsi.ssid = ccw_dev->sch->ssid & 3;
 break;
 case CCW_DEVTYPE_VFIO:
-ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
-ipl->iplb.pbt = S390_IPL_TYPE_CCW;
-ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
-ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3;
+iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
+iplb->pbt = S390_IPL_TYPE_CCW;
+iplb->ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
+iplb->ccw.ssid = ccw_dev->sch->ssid & 3;
 break;
  

[PATCH v5 13/19] include/hw/s390x: Add include files for common IPL structs

2024-10-19 Thread jrossi
From: Jared Rossi 

Currently, structures defined in both hw/s390x/ipl.h and pc-bios/s390-ccw/iplb.h
must be kept in sync, which is prone to error. Instead, create a new directory
at include/hw/s390x/ipl/ to contain the definitions that must be shared.

Signed-off-by: Jared Rossi 
Reviewed-by: Thomas Huth 
---
 hw/s390x/ipl.h  | 104 +-
 include/hw/s390x/ipl/qipl.h | 123 
 pc-bios/s390-ccw/iplb.h |  84 ++--
 pc-bios/s390-ccw/Makefile   |   2 +-
 4 files changed, 130 insertions(+), 183 deletions(-)
 create mode 100644 include/hw/s390x/ipl/qipl.h

diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index b2105b616a..fa394c339d 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -16,95 +16,11 @@
 #include "cpu.h"
 #include "exec/address-spaces.h"
 #include "hw/qdev-core.h"
+#include "hw/s390x/ipl/qipl.h"
 #include "qom/object.h"
 
-struct IPLBlockPVComp {
-uint64_t tweak_pref;
-uint64_t addr;
-uint64_t size;
-} QEMU_PACKED;
-typedef struct IPLBlockPVComp IPLBlockPVComp;
-
-struct IPLBlockPV {
-uint8_t  reserved18[87];/* 0x18 */
-uint8_t  version;   /* 0x6f */
-uint32_t reserved70;/* 0x70 */
-uint32_t num_comp;  /* 0x74 */
-uint64_t pv_header_addr;/* 0x78 */
-uint64_t pv_header_len; /* 0x80 */
-struct IPLBlockPVComp components[0];
-} QEMU_PACKED;
-typedef struct IPLBlockPV IPLBlockPV;
-
-struct IplBlockCcw {
-uint8_t  reserved0[85];
-uint8_t  ssid;
-uint16_t devno;
-uint8_t  vm_flags;
-uint8_t  reserved3[3];
-uint32_t vm_parm_len;
-uint8_t  nss_name[8];
-uint8_t  vm_parm[64];
-uint8_t  reserved4[8];
-} QEMU_PACKED;
-typedef struct IplBlockCcw IplBlockCcw;
-
-struct IplBlockFcp {
-uint8_t  reserved1[305 - 1];
-uint8_t  opt;
-uint8_t  reserved2[3];
-uint16_t reserved3;
-uint16_t devno;
-uint8_t  reserved4[4];
-uint64_t wwpn;
-uint64_t lun;
-uint32_t bootprog;
-uint8_t  reserved5[12];
-uint64_t br_lba;
-uint32_t scp_data_len;
-uint8_t  reserved6[260];
-uint8_t  scp_data[0];
-} QEMU_PACKED;
-typedef struct IplBlockFcp IplBlockFcp;
-
-struct IplBlockQemuScsi {
-uint32_t lun;
-uint16_t target;
-uint16_t channel;
-uint8_t  reserved0[77];
-uint8_t  ssid;
-uint16_t devno;
-} QEMU_PACKED;
-typedef struct IplBlockQemuScsi IplBlockQemuScsi;
-
 #define DIAG308_FLAGS_LP_VALID 0x80
 
-union IplParameterBlock {
-struct {
-uint32_t len;
-uint8_t  reserved0[3];
-uint8_t  version;
-uint32_t blk0_len;
-uint8_t  pbt;
-uint8_t  flags;
-uint16_t reserved01;
-uint8_t  loadparm[8];
-union {
-IplBlockCcw ccw;
-IplBlockFcp fcp;
-IPLBlockPV pv;
-IplBlockQemuScsi scsi;
-};
-} QEMU_PACKED;
-struct {
-uint8_t  reserved1[110];
-uint16_t devno;
-uint8_t  reserved2[88];
-uint8_t  reserved_ext[4096 - 200];
-} QEMU_PACKED;
-} QEMU_PACKED;
-typedef union IplParameterBlock IplParameterBlock;
-
 int s390_ipl_set_loadparm(uint8_t *loadparm);
 void s390_ipl_update_diag308(IplParameterBlock *iplb);
 int s390_ipl_prepare_pv_header(Error **errp);
@@ -131,24 +47,6 @@ void s390_ipl_clear_reset_request(void);
 #define QIPL_FLAG_BM_OPTS_CMD   0x80
 #define QIPL_FLAG_BM_OPTS_ZIPL  0x40
 
-/*
- * The QEMU IPL Parameters will be stored at absolute address
- * 204 (0xcc) which means it is 32-bit word aligned but not
- * double-word aligned. Placement of 64-bit data fields in this
- * area must account for their alignment needs.
- * The total size of the struct must never exceed 28 bytes.
- * This definition must be kept in sync with the definition
- * in pc-bios/s390-ccw/iplb.h.
- */
-struct QemuIplParameters {
-uint8_t  qipl_flags;
-uint8_t  reserved1[3];
-uint64_t reserved2;
-uint32_t boot_menu_timeout;
-uint8_t  reserved3[12];
-} QEMU_PACKED;
-typedef struct QemuIplParameters QemuIplParameters;
-
 #define TYPE_S390_IPL "s390-ipl"
 OBJECT_DECLARE_SIMPLE_TYPE(S390IPLState, S390_IPL)
 
diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
new file mode 100644
index 00..0ef04af027
--- /dev/null
+++ b/include/hw/s390x/ipl/qipl.h
@@ -0,0 +1,123 @@
+/*
+ * S/390 boot structures
+ *
+ * Copyright 2024 IBM Corp.
+ * Author(s): Jared Rossi 
+ *
+ * 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.
+ */
+
+#ifndef S390X_QIPL_H
+#define S390X_QIPL_H
+
+/* Boot Menu flags */
+#define QIPL_FLAG_BM_OPTS_CMD   0x80
+#define QIPL_FLAG_BM_OPTS_ZIPL  0x40
+
+#define QIPL_ADDRESS  0xcc
+#define LOADPARM_LEN8
+
+/*
+ * The QEMU IPL Parameters will be stored at absolute address
+ * 204 (0xcc) which means it is 32-bit word aligned but not
+ * double-word aligned. Placement 

[PATCH v5 02/19] pc-bios/s390-ccw: Use the libc from SLOF and remove sclp prints

2024-10-19 Thread jrossi
From: Jared Rossi 

We are already using the libc from SLOF for the s390-netboot.img, and
this libc implementation is way more complete and accurate than the
simple implementation that we currently use for the s390-ccw.img binary.
Since we are now always assuming that the SLOF submodule is available
when building the s390-ccw bios (see commit bf6903f6944f), we can drop
the simple implementation and use the SLOF libc for the s390-ccw.img
binary, too.

Additionally replace sclp_print calls with puts/printf now that it is avaliable.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/netboot.mak |  3 --
 pc-bios/s390-ccw/bootmap.h   |  4 +-
 pc-bios/s390-ccw/libc.h  | 89 
 pc-bios/s390-ccw/s390-ccw.h  | 30 ---
 pc-bios/s390-ccw/bootmap.c   | 47 -
 pc-bios/s390-ccw/cio.c   | 78 +---
 pc-bios/s390-ccw/dasd-ipl.c  |  5 +-
 pc-bios/s390-ccw/jump2ipl.c  |  5 +-
 pc-bios/s390-ccw/libc.c  | 88 ---
 pc-bios/s390-ccw/main.c  | 14 ++---
 pc-bios/s390-ccw/menu.c  | 51 +-
 pc-bios/s390-ccw/netmain.c   | 10 ++--
 pc-bios/s390-ccw/sclp.c  |  7 +--
 pc-bios/s390-ccw/virtio-blkdev.c |  6 +--
 pc-bios/s390-ccw/virtio-scsi.c   | 17 +++---
 pc-bios/s390-ccw/virtio.c|  2 +-
 pc-bios/s390-ccw/Makefile| 15 --
 17 files changed, 136 insertions(+), 335 deletions(-)
 delete mode 100644 pc-bios/s390-ccw/libc.h
 delete mode 100644 pc-bios/s390-ccw/libc.c

diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak
index 046aa35587..d2b3d8ee74 100644
--- a/pc-bios/s390-ccw/netboot.mak
+++ b/pc-bios/s390-ccw/netboot.mak
@@ -1,9 +1,6 @@
 
-SLOF_DIR := $(SRC_PATH)/../../roms/SLOF
-
 NETOBJS := start.o sclp.o cio.o virtio.o virtio-net.o jump2ipl.o netmain.o
 
-LIBC_INC := -nostdinc -I$(SLOF_DIR)/lib/libc/include
 LIBNET_INC := -I$(SLOF_DIR)/lib/libnet
 
 NETLDFLAGS := $(LDFLAGS) -Wl,-Ttext=0x780
diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
index d4690a88c2..4a7d8a91f1 100644
--- a/pc-bios/s390-ccw/bootmap.h
+++ b/pc-bios/s390-ccw/bootmap.h
@@ -336,9 +336,7 @@ static inline void print_volser(const void *volser)
 
 ebcdic_to_ascii((char *)volser, ascii, 6);
 ascii[6] = '\0';
-sclp_print("VOLSER=[");
-sclp_print(ascii);
-sclp_print("]\n");
+printf("VOLSER=[%s]\n", ascii);
 }
 
 static inline bool unused_space(const void *p, size_t size)
diff --git a/pc-bios/s390-ccw/libc.h b/pc-bios/s390-ccw/libc.h
deleted file mode 100644
index bcdc45732d..00
--- a/pc-bios/s390-ccw/libc.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * libc-style definitions and functions
- *
- * Copyright (c) 2013 Alexander Graf 
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#ifndef S390_CCW_LIBC_H
-#define S390_CCW_LIBC_H
-
-typedef unsigned long  size_t;
-typedef intbool;
-typedef unsigned char  uint8_t;
-typedef unsigned short uint16_t;
-typedef unsigned int   uint32_t;
-typedef unsigned long long uint64_t;
-
-static inline void *memset(void *s, int c, size_t n)
-{
-size_t i;
-unsigned char *p = s;
-
-for (i = 0; i < n; i++) {
-p[i] = c;
-}
-
-return s;
-}
-
-static inline void *memcpy(void *s1, const void *s2, size_t n)
-{
-uint8_t *dest = s1;
-const uint8_t *src = s2;
-size_t i;
-
-for (i = 0; i < n; i++) {
-dest[i] = src[i];
-}
-
-return s1;
-}
-
-static inline int memcmp(const void *s1, const void *s2, size_t n)
-{
-size_t i;
-const uint8_t *p1 = s1, *p2 = s2;
-
-for (i = 0; i < n; i++) {
-if (p1[i] != p2[i]) {
-return p1[i] > p2[i] ? 1 : -1;
-}
-}
-
-return 0;
-}
-
-static inline size_t strlen(const char *str)
-{
-size_t i;
-for (i = 0; *str; i++) {
-str++;
-}
-return i;
-}
-
-static inline char *strcat(char *dest, const char *src)
-{
-int i;
-char *dest_end = dest + strlen(dest);
-
-for (i = 0; i <= strlen(src); i++) {
-dest_end[i] = src[i];
-}
-return dest;
-}
-
-static inline int isdigit(int c)
-{
-return (c >= '0') && (c <= '9');
-}
-
-uint64_t atoui(const char *str);
-char *uitoa(uint64_t num, char *str, size_t len);
-
-#endif
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index c977a52b50..6f6d95d170 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -13,6 +13,11 @@
 
 /* #define DEBUG */
 
+#include 
+#include 
+#include 
+#include 
+
 typedef unsigned char  u8;
 typedef unsigned short u16;
 typedef unsigned int   u32;
@@ -26,9 +31,6 @@ typedef unsigned long long u64;
 #define EBUSY   

[PATCH v5 18/19] docs/system: Update documentation for s390x IPL

2024-10-19 Thread jrossi
From: Jared Rossi 

Update docs to show that s390x PC BIOS can support more than one boot device.

Signed-off-by: Jared Rossi 
Reviewed-by: Thomas Huth 
---
 docs/system/bootindex.rst | 7 ---
 docs/system/s390x/bootdevices.rst | 9 ++---
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/docs/system/bootindex.rst b/docs/system/bootindex.rst
index 8b057f812f..988f7b3beb 100644
--- a/docs/system/bootindex.rst
+++ b/docs/system/bootindex.rst
@@ -49,10 +49,11 @@ Limitations
 ---
 
 Some firmware has limitations on which devices can be considered for
-booting.  For instance, the PC BIOS boot specification allows only one
-disk to be bootable.  If boot from disk fails for some reason, the BIOS
+booting.  For instance, the x86 PC BIOS boot specification allows only one
+disk to be bootable.  If boot from disk fails for some reason, the x86 BIOS
 won't retry booting from other disk.  It can still try to boot from
-floppy or net, though.
+floppy or net, though. In the case of s390x BIOS, the BIOS will try up to
+8 total devices, any number of which may be disks.
 
 Sometimes, firmware cannot map the device path QEMU wants firmware to
 boot from to a boot method.  It doesn't happen for devices the firmware
diff --git a/docs/system/s390x/bootdevices.rst 
b/docs/system/s390x/bootdevices.rst
index c97efb8fc0..1a1a764c1c 100644
--- a/docs/system/s390x/bootdevices.rst
+++ b/docs/system/s390x/bootdevices.rst
@@ -6,9 +6,7 @@ Booting with bootindex parameter
 
 For classical mainframe guests (i.e. LPAR or z/VM installations), you always
 have to explicitly specify the disk where you want to boot from (or "IPL" from,
-in s390x-speak -- IPL means "Initial Program Load"). In particular, there can
-also be only one boot device according to the architecture specification, thus
-specifying multiple boot devices is not possible (yet).
+in s390x-speak -- IPL means "Initial Program Load").
 
 So for booting an s390x guest in QEMU, you should always mark the
 device where you want to boot from with the ``bootindex`` property, for
@@ -17,6 +15,11 @@ example::
  qemu-system-s390x -drive if=none,id=dr1,file=guest.qcow2 \
-device virtio-blk,drive=dr1,bootindex=1
 
+Multiple devices may have a bootindex. The lowest bootindex is assigned to the
+device to IPL first.  If the IPL fails for the first, the device with the 
second
+lowest bootindex will be tried and so on until IPL is successful or there are 
no
+remaining boot devices to try.
+
 For booting from a CD-ROM ISO image (which needs to include El-Torito boot
 information in order to be bootable), it is recommended to specify a 
``scsi-cd``
 device, for example like this::
-- 
2.45.1




[PATCH v5 06/19] docs/system/s390x/bootdevices: Update the documentation about network booting

2024-10-19 Thread jrossi
From: Jared Rossi 

Remove the information about the separate s390-netboot.img from
the documentation.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 
---
 docs/system/s390x/bootdevices.rst | 20 +++-
 1 file changed, 7 insertions(+), 13 deletions(-)

diff --git a/docs/system/s390x/bootdevices.rst 
b/docs/system/s390x/bootdevices.rst
index 1a7a18b43b..c97efb8fc0 100644
--- a/docs/system/s390x/bootdevices.rst
+++ b/docs/system/s390x/bootdevices.rst
@@ -82,23 +82,17 @@ Note that ``0`` can be used to boot the default entry.
 Booting from a network device
 -
 
-Beside the normal guest firmware (which is loaded from the file 
``s390-ccw.img``
-in the data directory of QEMU, or via the ``-bios`` option), QEMU ships with
-a small TFTP network bootloader firmware for virtio-net-ccw devices, too. This
-firmware is loaded from a file called ``s390-netboot.img`` in the QEMU data
-directory. In case you want to load it from a different filename instead,
-you can specify it via the ``-global s390-ipl.netboot_fw=filename``
-command line option.
-
-The ``bootindex`` property is especially important for booting via the network.
-If you don't specify the ``bootindex`` property here, the network bootloader
-firmware code won't get loaded into the guest memory so that the network boot
-will fail. For a successful network boot, try something like this::
+The firmware that ships with QEMU includes a small TFTP network bootloader
+for virtio-net-ccw devices.  The ``bootindex`` property is especially
+important for booting via the network. If you don't specify the ``bootindex``
+property here, the network bootloader won't be taken into consideration and
+the network boot will fail. For a successful network boot, try something
+like this::
 
  qemu-system-s390x -netdev user,id=n1,tftp=...,bootfile=... \
-device virtio-net-ccw,netdev=n1,bootindex=1
 
-The network bootloader firmware also has basic support for pxelinux.cfg-style
+The network bootloader also has basic support for pxelinux.cfg-style
 configuration files. See the `PXELINUX Configuration page
 `__
 for details how to set up the configuration file on your TFTP server.
-- 
2.45.1




[PATCH v5 11/19] pc-bios/s390-ccw: Remove panics from Netboot IPL path

2024-10-19 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from Netboot specific functions so that error recovery
may be possible in the future.

Functions that would previously panic now provide a return code.

Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/s390-ccw.h   |  2 +-
 pc-bios/s390-ccw/bootmap.c|  1 +
 pc-bios/s390-ccw/netmain.c| 17 +++--
 pc-bios/s390-ccw/virtio-net.c |  7 +--
 4 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 3e844abd71..344ad15655 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -57,7 +57,7 @@ unsigned int get_loadparm_index(void);
 void main(void);
 
 /* netmain.c */
-void netmain(void);
+int netmain(void);
 
 /* sclp.c */
 void sclp_print(const char *string);
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 652807a16a..95ef9104d0 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -1072,6 +1072,7 @@ void zipl_load(void)
 
 if (virtio_get_device_type() == VIRTIO_ID_NET) {
 netmain();
+panic("\n! Cannot IPL from this network !\n");
 }
 
 if (ipl_scsi()) {
diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c
index bc6ad8695f..d1a6c9a91c 100644
--- a/pc-bios/s390-ccw/netmain.c
+++ b/pc-bios/s390-ccw/netmain.c
@@ -464,7 +464,7 @@ static bool find_net_dev(Schib *schib, int dev_no)
 return false;
 }
 
-static void virtio_setup(void)
+static bool virtio_setup(void)
 {
 Schib schib;
 int ssid;
@@ -495,10 +495,10 @@ static void virtio_setup(void)
 }
 }
 
-IPL_assert(found, "No virtio net device found");
+return found;
 }
 
-void netmain(void)
+int netmain(void)
 {
 filename_ip_t fn_ip;
 int rc, fnlen;
@@ -506,11 +506,15 @@ void netmain(void)
 sclp_setup();
 puts("Network boot starting...");
 
-virtio_setup();
+if (!virtio_setup()) {
+puts("No virtio net device found.");
+return -1;
+}
 
 rc = net_init(&fn_ip);
 if (rc) {
-panic("Network initialization failed. Halting.");
+puts("Network initialization failed.");
+return -1;
 }
 
 fnlen = strlen(fn_ip.filename);
@@ -528,5 +532,6 @@ void netmain(void)
 jump_to_low_kernel();
 }
 
-panic("Failed to load OS from network.");
+puts("Failed to load OS from network.");
+return -1;
 }
diff --git a/pc-bios/s390-ccw/virtio-net.c b/pc-bios/s390-ccw/virtio-net.c
index 2fcb0a58c5..f9854a22c3 100644
--- a/pc-bios/s390-ccw/virtio-net.c
+++ b/pc-bios/s390-ccw/virtio-net.c
@@ -54,8 +54,11 @@ int virtio_net_init(void *mac_addr)
 vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT;
 virtio_setup_ccw(vdev);
 
-IPL_assert(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT,
-   "virtio-net device does not support the MAC address feature");
+if (!(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT)) {
+puts("virtio-net device does not support the MAC address feature");
+return -1;
+}
+
 memcpy(mac_addr, vdev->config.net.mac, ETH_ALEN);
 
 for (i = 0; i < 64; i++) {
-- 
2.45.1




[PATCH v5 10/19] pc-bios/s390-ccw: Remove panics from DASD IPL path

2024-10-19 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from DASD IPL specific functions so that error recovery
may be possible in the future.

Functions that would previously panic now provide a return code.

Signed-off-by: Jared Rossi 
Reviewed-by: Thomas Huth 
---
 pc-bios/s390-ccw/dasd-ipl.h |  2 +-
 pc-bios/s390-ccw/dasd-ipl.c | 66 -
 2 files changed, 37 insertions(+), 31 deletions(-)

diff --git a/pc-bios/s390-ccw/dasd-ipl.h b/pc-bios/s390-ccw/dasd-ipl.h
index c394828906..eb1898c84a 100644
--- a/pc-bios/s390-ccw/dasd-ipl.h
+++ b/pc-bios/s390-ccw/dasd-ipl.h
@@ -11,6 +11,6 @@
 #ifndef DASD_IPL_H
 #define DASD_IPL_H
 
-void dasd_ipl(SubChannelId schid, uint16_t cutype);
+int dasd_ipl(SubChannelId schid, uint16_t cutype);
 
 #endif /* DASD_IPL_H */
diff --git a/pc-bios/s390-ccw/dasd-ipl.c b/pc-bios/s390-ccw/dasd-ipl.c
index ae751adec1..babece95ea 100644
--- a/pc-bios/s390-ccw/dasd-ipl.c
+++ b/pc-bios/s390-ccw/dasd-ipl.c
@@ -111,38 +111,29 @@ static void make_readipl(void)
 ccwIplRead->count = 0x18; /* Read 0x18 bytes of data */
 }
 
-static void run_readipl(SubChannelId schid, uint16_t cutype)
+static int run_readipl(SubChannelId schid, uint16_t cutype)
 {
-if (do_cio(schid, cutype, 0x00, CCW_FMT0)) {
-panic("dasd-ipl: Failed to run Read IPL channel program\n");
-}
+return do_cio(schid, cutype, 0x00, CCW_FMT0);
 }
 
 /*
  * The architecture states that IPL1 data should consist of a psw followed by
  * format-0 READ and TIC CCWs. Let's sanity check.
  */
-static void check_ipl1(void)
+static bool check_ipl1(void)
 {
 Ccw0 *ccwread = (Ccw0 *)0x08;
 Ccw0 *ccwtic = (Ccw0 *)0x10;
 
-if (ccwread->cmd_code != CCW_CMD_DASD_READ ||
-ccwtic->cmd_code != CCW_CMD_TIC) {
-panic("dasd-ipl: IPL1 data invalid. Is this disk really bootable?\n");
-}
+return (ccwread->cmd_code == CCW_CMD_DASD_READ &&
+ccwtic->cmd_code == CCW_CMD_TIC);
 }
 
-static void check_ipl2(uint32_t ipl2_addr)
+static bool check_ipl2(uint32_t ipl2_addr)
 {
 Ccw0 *ccw = u32toptr(ipl2_addr);
 
-if (ipl2_addr == 0x00) {
-panic("IPL2 address invalid. Is this disk really bootable?\n");
-}
-if (ccw->cmd_code == 0x00) {
-panic("IPL2 ccw data invalid. Is this disk really bootable?\n");
-}
+return (ipl2_addr != 0x00 && ccw->cmd_code != 0x00);
 }
 
 static uint32_t read_ipl2_addr(void)
@@ -188,52 +179,67 @@ static void ipl1_fixup(void)
 ccwSearchTic->cda = ptr2u32(ccwSearchID);
 }
 
-static void run_ipl1(SubChannelId schid, uint16_t cutype)
+static int run_ipl1(SubChannelId schid, uint16_t cutype)
  {
 uint32_t startAddr = 0x08;
 
-if (do_cio(schid, cutype, startAddr, CCW_FMT0)) {
-panic("dasd-ipl: Failed to run IPL1 channel program\n");
-}
+return do_cio(schid, cutype, startAddr, CCW_FMT0);
 }
 
-static void run_ipl2(SubChannelId schid, uint16_t cutype, uint32_t addr)
+static int run_ipl2(SubChannelId schid, uint16_t cutype, uint32_t addr)
 {
-if (run_dynamic_ccw_program(schid, cutype, addr)) {
-panic("dasd-ipl: Failed to run IPL2 channel program\n");
-}
+return run_dynamic_ccw_program(schid, cutype, addr);
 }
 
 /*
  * Limitations in vfio-ccw support complicate the IPL process. Details can
  * be found in docs/devel/s390-dasd-ipl.rst
  */
-void dasd_ipl(SubChannelId schid, uint16_t cutype)
+int dasd_ipl(SubChannelId schid, uint16_t cutype)
 {
 PSWLegacy *pswl = (PSWLegacy *) 0x00;
 uint32_t ipl2_addr;
 
 /* Construct Read IPL CCW and run it to read IPL1 from boot disk */
 make_readipl();
-run_readipl(schid, cutype);
+if (run_readipl(schid, cutype)) {
+puts("Failed to run Read IPL channel program");
+return -EIO;
+}
+
 ipl2_addr = read_ipl2_addr();
-check_ipl1();
+
+if (!check_ipl1()) {
+puts("IPL1 invalid for DASD-IPL");
+return -EINVAL;
+}
 
 /*
  * Fixup IPL1 channel program to account for vfio-ccw limitations, then run
  * it to read IPL2 channel program from boot disk.
  */
 ipl1_fixup();
-run_ipl1(schid, cutype);
-check_ipl2(ipl2_addr);
+if (run_ipl1(schid, cutype)) {
+puts("Failed to run IPL1 channel program");
+return -EIO;
+}
+
+if (!check_ipl2(ipl2_addr)) {
+puts("IPL2 invalid for DASD-IPL");
+return -EINVAL;
+}
 
 /*
  * Run IPL2 channel program to read operating system code from boot disk
  */
-run_ipl2(schid, cutype, ipl2_addr);
+if (run_ipl2(schid, cutype, ipl2_addr)) {
+puts("Failed to run IPL2 channel program");
+return -EIO;
+}
 
 /* Transfer control to the guest operating system */
 pswl->mask |= PSW_MASK_EAMODE;   /* Force z-mode */
 pswl->addr |= PSW_MASK_BAMODE;   /* ...  */
 jump_to_low_kernel();
+return -1;
 }
-- 
2.45.1




[PATCH v5 09/19] pc-bios/s390-ccw: Remove panics from SCSI IPL path

2024-10-19 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from virtio-scsi IPL specific functions so that error
recovery may be possible in the future.

Functions that would previously panic now provide a return code.

Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/bootmap.c   |  88 ++-
 pc-bios/s390-ccw/virtio-blkdev.c |   4 +-
 pc-bios/s390-ccw/virtio-scsi.c   | 143 +--
 3 files changed, 164 insertions(+), 71 deletions(-)

diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index b9596e28c7..652807a16a 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -595,7 +595,7 @@ static int ipl_eckd(void)
  * IPL a SCSI disk
  */
 
-static void zipl_load_segment(ComponentEntry *entry)
+static int zipl_load_segment(ComponentEntry *entry)
 {
 const int max_entries = (MAX_SECTOR_SIZE / sizeof(ScsiBlockPtr));
 ScsiBlockPtr *bprs = (void *)sec;
@@ -615,7 +615,10 @@ static void zipl_load_segment(ComponentEntry *entry)
 do {
 memset(bprs, FREE_SPACE_FILLER, bprs_size);
 fill_hex_val(blk_no, &blockno, sizeof(blockno));
-read_block(blockno, bprs, err_msg);
+if (virtio_read(blockno, bprs)) {
+puts(err_msg);
+return -EIO;
+}
 
 for (i = 0;; i++) {
 uint64_t *cur_desc = (void *)&bprs[i];
@@ -643,23 +646,37 @@ static void zipl_load_segment(ComponentEntry *entry)
 }
 address = virtio_load_direct(cur_desc[0], cur_desc[1], 0,
  (void *)address);
-IPL_assert(address != -1, "zIPL load segment failed");
+if (!address) {
+puts("zIPL load segment failed");
+return -EIO;
+}
 }
 } while (blockno);
+
+return 0;
 }
 
 /* Run a zipl program */
-static void zipl_run(ScsiBlockPtr *pte)
+static int zipl_run(ScsiBlockPtr *pte)
 {
 ComponentHeader *header;
 ComponentEntry *entry;
 uint8_t tmp_sec[MAX_SECTOR_SIZE];
 
-read_block(pte->blockno, tmp_sec, "Cannot read header");
+if (virtio_read(pte->blockno, tmp_sec)) {
+puts("Cannot read header");
+return -EIO;
+}
 header = (ComponentHeader *)tmp_sec;
 
-IPL_assert(magic_match(tmp_sec, ZIPL_MAGIC), "No zIPL magic in header");
-IPL_assert(header->type == ZIPL_COMP_HEADER_IPL, "Bad header type");
+if (!magic_match(tmp_sec, ZIPL_MAGIC)) {
+puts("No zIPL magic in header");
+return -EINVAL;
+}
+if (header->type != ZIPL_COMP_HEADER_IPL) {
+puts("Bad header type");
+return -EINVAL;
+}
 
 dputs("start loading images\n");
 
@@ -674,22 +691,30 @@ static void zipl_run(ScsiBlockPtr *pte)
 continue;
 }
 
-zipl_load_segment(entry);
+if (zipl_load_segment(entry)) {
+return -1;
+}
 
 entry++;
 
-IPL_assert((uint8_t *)(&entry[1]) <= (tmp_sec + MAX_SECTOR_SIZE),
-   "Wrong entry value");
+if ((uint8_t *)(&entry[1]) > (tmp_sec + MAX_SECTOR_SIZE)) {
+puts("Wrong entry value");
+return -EINVAL;
+}
 }
 
-IPL_assert(entry->component_type == ZIPL_COMP_ENTRY_EXEC, "No EXEC entry");
+if (entry->component_type != ZIPL_COMP_ENTRY_EXEC) {
+puts("No EXEC entry");
+return -EINVAL;
+}
 
 /* should not return */
 write_reset_psw(entry->compdat.load_psw);
 jump_to_IPL_code(0);
+return -1;
 }
 
-static void ipl_scsi(void)
+static int ipl_scsi(void)
 {
 ScsiMbr *mbr = (void *)sec;
 int program_table_entries = 0;
@@ -700,10 +725,13 @@ static void ipl_scsi(void)
 
 /* Grab the MBR */
 memset(sec, FREE_SPACE_FILLER, sizeof(sec));
-read_block(0, mbr, "Cannot read block 0");
+if (virtio_read(0, mbr)) {
+puts("Cannot read block 0");
+return -EIO;
+}
 
 if (!magic_match(mbr->magic, ZIPL_MAGIC)) {
-return;
+return 0;
 }
 
 puts("Using SCSI scheme.");
@@ -711,11 +739,20 @@ static void ipl_scsi(void)
 IPL_check(mbr->version_id == 1,
   "Unknown MBR layout version, assuming version 1");
 debug_print_int("program table", mbr->pt.blockno);
-IPL_assert(mbr->pt.blockno, "No Program Table");
+if (!mbr->pt.blockno) {
+puts("No Program Table");
+return -EINVAL;
+}
 
 /* Parse the program table */
-read_block(mbr->pt.blockno, sec, "Error reading Program Table");
-IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT");
+if (virtio_read(mbr->pt.blockno, sec)) {
+puts("Error reading Program Table");
+return -EIO;
+}
+if (!magic_match(sec, ZIPL_MAGIC)) {
+puts("No zIPL magic in Program Table");
+return -EINVAL;
+}
 
 for (i = 0; i < MAX_BOOT_ENTRIES; i++) {
 if (prog_table->entry[i].scsi.blockno) {
@@ -725,17 +762,22 @@ static void ipl_scsi(void)
 }
 
 debug_prin

[PATCH v4 00/19] s390x: Add Full Boot Order Support

2024-10-16 Thread jrossi
From: Jared Rossi 

changes v3 -> v4:
- Ensure signed-ness of return values is appropriate
- Add missing newline character in replacements of sclp_print_int()
- Add a missing return in a SCSI error path
- Restore break that was incorrectly removed for Virtio CU devices
- Remove an extra/early return that caused probing to end early
- Convert "good" device to scsi-cd in a cdrom-test for better coverage
- Minor stylistic clean-ups and variable name clarifications

changes v2 -> v3:

- Fix return code mismatch in ISO IPL path
- Introduce a new RC for loading ECKD segments rather than rely on block 0
- Fix bug causing false negative in some SCSI device error cases
- Terminate entire IPL on bad PBT instead of trying next device
- Simplify routine for building IPLB chain
- Restore device probing
- Add qtests
- Minor stylistic clean-ups and comment clarifications

Jared Rossi (19):
  hw/s390x/ipl: Provide more memory to the s390-ccw.img firmware
  pc-bios/s390-ccw: Use the libc from SLOF and remove sclp prints
  pc-bios/s390-ccw: Link the netboot code into the main s390-ccw.img
binary
  hw/s390x: Remove the possibility to load the s390-netboot.img binary
  pc-bios/s390-ccw: Merge netboot.mak into the main Makefile
  docs/system/s390x/bootdevices: Update the documentation about network
booting
  pc-bios/s390-ccw: Remove panics from ISO IPL path
  pc-bios/s390-ccw: Remove panics from ECKD IPL path
  pc-bios/s390-ccw: Remove panics from SCSI IPL path
  pc-bios/s390-ccw: Remove panics from DASD IPL path
  pc-bios/s390-ccw: Remove panics from Netboot IPL path
  pc-bios/s390-ccw: Enable failed IPL to return after error
  include/hw/s390x: Add include files for common IPL structs
  s390x: Add individual loadparm assignment to CCW device
  hw/s390x: Build an IPLB for each boot device
  s390x: Rebuild IPLB for SCSI device directly from DIAG308
  pc-bios/s390x: Enable multi-device boot loop
  docs/system: Update documentation for s390x IPL
  tests/qtest: Add s390x boot order tests to cdrom-test.c

 docs/system/bootindex.rst |   7 +-
 docs/system/s390x/bootdevices.rst |  29 +-
 pc-bios/s390-ccw/netboot.mak  |  62 
 hw/s390x/ccw-device.h |   2 +
 hw/s390x/ipl.h| 123 +---
 include/hw/s390x/ipl/qipl.h   | 126 +
 pc-bios/s390-ccw/bootmap.h|  20 +-
 pc-bios/s390-ccw/cio.h|   2 +
 pc-bios/s390-ccw/dasd-ipl.h   |   2 +-
 pc-bios/s390-ccw/iplb.h   | 108 ++-
 pc-bios/s390-ccw/libc.h   |  89 --
 pc-bios/s390-ccw/s390-ccw.h   |  36 +--
 pc-bios/s390-ccw/virtio.h |   3 +-
 hw/s390x/ccw-device.c |  46 +++
 hw/s390x/ipl.c| 279 +-
 hw/s390x/s390-virtio-ccw.c|  28 +-
 hw/s390x/sclp.c   |   3 +-
 pc-bios/s390-ccw/bootmap.c| 456 --
 pc-bios/s390-ccw/cio.c|  81 +++---
 pc-bios/s390-ccw/dasd-ipl.c   |  71 ++---
 pc-bios/s390-ccw/jump2ipl.c   |  23 +-
 pc-bios/s390-ccw/libc.c   |  88 --
 pc-bios/s390-ccw/main.c   |  97 ---
 pc-bios/s390-ccw/menu.c   |  51 ++--
 pc-bios/s390-ccw/netmain.c|  38 ++-
 pc-bios/s390-ccw/sclp.c   |   7 +-
 pc-bios/s390-ccw/virtio-blkdev.c  |  14 +-
 pc-bios/s390-ccw/virtio-net.c |   7 +-
 pc-bios/s390-ccw/virtio-scsi.c| 164 +++
 pc-bios/s390-ccw/virtio.c |  67 +++--
 target/s390x/diag.c   |   9 +-
 tests/qtest/cdrom-test.c  |  24 ++
 pc-bios/meson.build   |   1 -
 pc-bios/s390-ccw/Makefile |  69 -
 pc-bios/s390-netboot.img  | Bin 67232 -> 0 bytes
 35 files changed, 1160 insertions(+), 1072 deletions(-)
 delete mode 100644 pc-bios/s390-ccw/netboot.mak
 create mode 100644 include/hw/s390x/ipl/qipl.h
 delete mode 100644 pc-bios/s390-ccw/libc.h
 delete mode 100644 pc-bios/s390-ccw/libc.c
 delete mode 100644 pc-bios/s390-netboot.img

-- 
2.45.1




[PATCH v4 02/19] pc-bios/s390-ccw: Use the libc from SLOF and remove sclp prints

2024-10-16 Thread jrossi
From: Jared Rossi 

We are already using the libc from SLOF for the s390-netboot.img, and
this libc implementation is way more complete and accurate than the
simple implementation that we currently use for the s390-ccw.img binary.
Since we are now always assuming that the SLOF submodule is available
when building the s390-ccw bios (see commit bf6903f6944f), we can drop
the simple implementation and use the SLOF libc for the s390-ccw.img
binary, too.

Additionally replace sclp_print calls with puts/printf now that it is avaliable.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/netboot.mak |  3 --
 pc-bios/s390-ccw/bootmap.h   |  4 +-
 pc-bios/s390-ccw/libc.h  | 89 
 pc-bios/s390-ccw/s390-ccw.h  | 30 ---
 pc-bios/s390-ccw/bootmap.c   | 47 -
 pc-bios/s390-ccw/cio.c   | 78 +---
 pc-bios/s390-ccw/dasd-ipl.c  |  5 +-
 pc-bios/s390-ccw/jump2ipl.c  |  5 +-
 pc-bios/s390-ccw/libc.c  | 88 ---
 pc-bios/s390-ccw/main.c  | 14 ++---
 pc-bios/s390-ccw/menu.c  | 51 +-
 pc-bios/s390-ccw/netmain.c   | 10 ++--
 pc-bios/s390-ccw/sclp.c  |  7 +--
 pc-bios/s390-ccw/virtio-blkdev.c |  6 +--
 pc-bios/s390-ccw/virtio-scsi.c   | 17 +++---
 pc-bios/s390-ccw/virtio.c|  2 +-
 pc-bios/s390-ccw/Makefile| 15 --
 17 files changed, 136 insertions(+), 335 deletions(-)
 delete mode 100644 pc-bios/s390-ccw/libc.h
 delete mode 100644 pc-bios/s390-ccw/libc.c

diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak
index 046aa35587..d2b3d8ee74 100644
--- a/pc-bios/s390-ccw/netboot.mak
+++ b/pc-bios/s390-ccw/netboot.mak
@@ -1,9 +1,6 @@
 
-SLOF_DIR := $(SRC_PATH)/../../roms/SLOF
-
 NETOBJS := start.o sclp.o cio.o virtio.o virtio-net.o jump2ipl.o netmain.o
 
-LIBC_INC := -nostdinc -I$(SLOF_DIR)/lib/libc/include
 LIBNET_INC := -I$(SLOF_DIR)/lib/libnet
 
 NETLDFLAGS := $(LDFLAGS) -Wl,-Ttext=0x780
diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
index d4690a88c2..4a7d8a91f1 100644
--- a/pc-bios/s390-ccw/bootmap.h
+++ b/pc-bios/s390-ccw/bootmap.h
@@ -336,9 +336,7 @@ static inline void print_volser(const void *volser)
 
 ebcdic_to_ascii((char *)volser, ascii, 6);
 ascii[6] = '\0';
-sclp_print("VOLSER=[");
-sclp_print(ascii);
-sclp_print("]\n");
+printf("VOLSER=[%s]\n", ascii);
 }
 
 static inline bool unused_space(const void *p, size_t size)
diff --git a/pc-bios/s390-ccw/libc.h b/pc-bios/s390-ccw/libc.h
deleted file mode 100644
index bcdc45732d..00
--- a/pc-bios/s390-ccw/libc.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * libc-style definitions and functions
- *
- * Copyright (c) 2013 Alexander Graf 
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#ifndef S390_CCW_LIBC_H
-#define S390_CCW_LIBC_H
-
-typedef unsigned long  size_t;
-typedef intbool;
-typedef unsigned char  uint8_t;
-typedef unsigned short uint16_t;
-typedef unsigned int   uint32_t;
-typedef unsigned long long uint64_t;
-
-static inline void *memset(void *s, int c, size_t n)
-{
-size_t i;
-unsigned char *p = s;
-
-for (i = 0; i < n; i++) {
-p[i] = c;
-}
-
-return s;
-}
-
-static inline void *memcpy(void *s1, const void *s2, size_t n)
-{
-uint8_t *dest = s1;
-const uint8_t *src = s2;
-size_t i;
-
-for (i = 0; i < n; i++) {
-dest[i] = src[i];
-}
-
-return s1;
-}
-
-static inline int memcmp(const void *s1, const void *s2, size_t n)
-{
-size_t i;
-const uint8_t *p1 = s1, *p2 = s2;
-
-for (i = 0; i < n; i++) {
-if (p1[i] != p2[i]) {
-return p1[i] > p2[i] ? 1 : -1;
-}
-}
-
-return 0;
-}
-
-static inline size_t strlen(const char *str)
-{
-size_t i;
-for (i = 0; *str; i++) {
-str++;
-}
-return i;
-}
-
-static inline char *strcat(char *dest, const char *src)
-{
-int i;
-char *dest_end = dest + strlen(dest);
-
-for (i = 0; i <= strlen(src); i++) {
-dest_end[i] = src[i];
-}
-return dest;
-}
-
-static inline int isdigit(int c)
-{
-return (c >= '0') && (c <= '9');
-}
-
-uint64_t atoui(const char *str);
-char *uitoa(uint64_t num, char *str, size_t len);
-
-#endif
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index c977a52b50..6f6d95d170 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -13,6 +13,11 @@
 
 /* #define DEBUG */
 
+#include 
+#include 
+#include 
+#include 
+
 typedef unsigned char  u8;
 typedef unsigned short u16;
 typedef unsigned int   u32;
@@ -26,9 +31,6 @@ typedef unsigned long long u64;
 #define EBUSY   

[PATCH v4 12/19] pc-bios/s390-ccw: Enable failed IPL to return after error

2024-10-16 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from IPL functions such that a return code is propagated
back to the main IPL calling function (rather than terminating immediately),
which facilitates possible error recovery in the future.

A select few panics remain, which indicate fatal non-devices errors that must
result in termination.

Signed-off-by: Jared Rossi 
Reviewed-by: Thomas Huth 
---
 pc-bios/s390-ccw/s390-ccw.h  |  2 +-
 pc-bios/s390-ccw/virtio.h|  2 +-
 pc-bios/s390-ccw/bootmap.c   | 53 ++
 pc-bios/s390-ccw/cio.c   |  3 +-
 pc-bios/s390-ccw/jump2ipl.c  |  5 ++-
 pc-bios/s390-ccw/main.c  | 32 +---
 pc-bios/s390-ccw/virtio-blkdev.c |  4 +-
 pc-bios/s390-ccw/virtio.c| 65 +---
 8 files changed, 109 insertions(+), 57 deletions(-)

diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 344ad15655..6cdce3e5e5 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -78,7 +78,7 @@ void zipl_load(void);
 
 /* jump2ipl.c */
 void write_reset_psw(uint64_t psw);
-void jump_to_IPL_code(uint64_t address);
+int jump_to_IPL_code(uint64_t address);
 void jump_to_low_kernel(void);
 
 /* menu.c */
diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
index 6f9a558ff5..9faf3986b1 100644
--- a/pc-bios/s390-ccw/virtio.h
+++ b/pc-bios/s390-ccw/virtio.h
@@ -274,7 +274,7 @@ void vring_send_buf(VRing *vr, void *p, int len, int flags);
 int vr_poll(VRing *vr);
 int vring_wait_reply(void);
 int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd);
-void virtio_setup_ccw(VDev *vdev);
+int virtio_setup_ccw(VDev *vdev);
 
 int virtio_net_init(void *mac_addr);
 
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index f1cfb7aaa8..255d21c72f 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -62,15 +62,34 @@ static void *s2_prev_blk = _s2;
 static void *s2_cur_blk = _s2 + MAX_SECTOR_SIZE;
 static void *s2_next_blk = _s2 + MAX_SECTOR_SIZE * 2;
 
-static inline void verify_boot_info(BootInfo *bip)
+static inline int verify_boot_info(BootInfo *bip)
 {
-IPL_assert(magic_match(bip->magic, ZIPL_MAGIC), "No zIPL sig in BootInfo");
-IPL_assert(bip->version == BOOT_INFO_VERSION, "Wrong zIPL version");
-IPL_assert(bip->bp_type == BOOT_INFO_BP_TYPE_IPL, "DASD is not for IPL");
-IPL_assert(bip->dev_type == BOOT_INFO_DEV_TYPE_ECKD, "DASD is not ECKD");
-IPL_assert(bip->flags == BOOT_INFO_FLAGS_ARCH, "Not for this arch");
-IPL_assert(block_size_ok(bip->bp.ipl.bm_ptr.eckd.bptr.size),
-   "Bad block size in zIPL section of the 1st record.");
+if (!magic_match(bip->magic, ZIPL_MAGIC)) {
+puts("No zIPL sig in BootInfo");
+return -EINVAL;
+}
+if (bip->version != BOOT_INFO_VERSION) {
+puts("Wrong zIPL version");
+return -EINVAL;
+}
+if (bip->bp_type != BOOT_INFO_BP_TYPE_IPL) {
+puts("DASD is not for IPL");
+return -ENODEV;
+}
+if (bip->dev_type != BOOT_INFO_DEV_TYPE_ECKD) {
+puts("DASD is not ECKD");
+return -ENODEV;
+}
+if (bip->flags != BOOT_INFO_FLAGS_ARCH) {
+puts("Not for this arch");
+return -EINVAL;
+}
+if (!block_size_ok(bip->bp.ipl.bm_ptr.eckd.bptr.size)) {
+puts("Bad block size in zIPL section of 1st record");
+return -EINVAL;
+}
+
+return 0;
 }
 
 static void eckd_format_chs(ExtEckdBlockPtr *ptr,  bool ldipl,
@@ -368,8 +387,8 @@ static int run_eckd_boot_script(block_number_t bmt_block_nr,
 puts("Unknown script entry type");
 return -EINVAL;
 }
-write_reset_psw(bms->entry[i].address.load_address); /* no return */
-jump_to_IPL_code(0); /* no return */
+write_reset_psw(bms->entry[i].address.load_address);
+jump_to_IPL_code(0);
 return 1;
 }
 
@@ -1068,16 +1087,19 @@ void zipl_load(void)
 
 if (vdev->is_cdrom) {
 ipl_iso_el_torito();
-panic("\n! Cannot IPL this ISO image !\n");
+puts("Failed to IPL this ISO image!");
+return;
 }
 
 if (virtio_get_device_type() == VIRTIO_ID_NET) {
 netmain();
-panic("\n! Cannot IPL from this network !\n");
+puts("Failed to IPL from this network!");
+return;
 }
 
 if (ipl_scsi()) {
-panic("\n! Cannot IPL this SCSI device !\n");
+puts("Failed to IPL from this SCSI device!");
+return;
 }
 
 switch (virtio_get_device_type()) {
@@ -1088,8 +1110,9 @@ void zipl_load(void)
 zipl_load_vscsi();
 break;
 default:
-panic("\n! Unknown IPL device type !\n");
+puts("Unknown IPL device type!");
+return;
 }
 
-puts("zIPL load failed.");
+puts("zIPL load failed!");
 }
diff --git a/pc-bios/s390-ccw/cio.c b/pc-bios/s390-ccw/cio.c
index 7b09a38c96..5d543da73f 100644
--- a/pc-bios/s390-ccw/cio.c
+++ b/pc-bios/s390-ccw/cio.c
@@ -59,7 +59,8 @@ ui

[PATCH v4 03/19] pc-bios/s390-ccw: Link the netboot code into the main s390-ccw.img binary

2024-10-16 Thread jrossi
From: Jared Rossi 

We originally built a separate binary for the netboot code since it
was considered as experimental and we could not be sure that the
necessary SLOF module had been checked out. Time passed, the code
proved its usefulness, and the build system nowadays makes sure that
the SLOF module is checked out if you have a s390x compiler available
for building the s390-ccw bios. So there is no real compelling reason
anymore to keep the netboot code in a separate binary. Linking the
code together with the main s390-ccw.img will make future enhancements
much easier, like supporting more than one boot device.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/netboot.mak | 14 --
 pc-bios/s390-ccw/cio.h   |  2 ++
 pc-bios/s390-ccw/iplb.h  |  4 ++--
 pc-bios/s390-ccw/s390-ccw.h  |  3 +++
 pc-bios/s390-ccw/virtio.h|  1 -
 pc-bios/s390-ccw/bootmap.c   |  2 +-
 pc-bios/s390-ccw/main.c  | 10 +++---
 pc-bios/s390-ccw/netmain.c   | 15 ++-
 pc-bios/s390-ccw/Makefile| 13 +++--
 9 files changed, 24 insertions(+), 40 deletions(-)

diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak
index d2b3d8ee74..0a24257ff4 100644
--- a/pc-bios/s390-ccw/netboot.mak
+++ b/pc-bios/s390-ccw/netboot.mak
@@ -1,18 +1,4 @@
 
-NETOBJS := start.o sclp.o cio.o virtio.o virtio-net.o jump2ipl.o netmain.o
-
-LIBNET_INC := -I$(SLOF_DIR)/lib/libnet
-
-NETLDFLAGS := $(LDFLAGS) -Wl,-Ttext=0x780
-
-$(NETOBJS): EXTRA_CFLAGS += $(LIBC_INC) $(LIBNET_INC)
-
-s390-netboot.elf: $(NETOBJS) libnet.a libc.a
-   $(call quiet-command,$(CC) $(NETLDFLAGS) -o $@ $^,Linking)
-
-s390-netboot.img: s390-netboot.elf
-   $(call quiet-command,$(STRIP) --strip-unneeded $< -o $@,Stripping $< 
into)
-
 # libc files:
 
 LIBC_CFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
diff --git a/pc-bios/s390-ccw/cio.h b/pc-bios/s390-ccw/cio.h
index 8b18153deb..6a5e86ba01 100644
--- a/pc-bios/s390-ccw/cio.h
+++ b/pc-bios/s390-ccw/cio.h
@@ -361,6 +361,8 @@ typedef struct CcwSearchIdData {
 uint8_t record;
 } __attribute__((packed)) CcwSearchIdData;
 
+extern SubChannelId net_schid;
+
 int enable_mss_facility(void);
 void enable_subchannel(SubChannelId schid);
 uint16_t cu_type(SubChannelId schid);
diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h
index cb6ac8a880..3758698468 100644
--- a/pc-bios/s390-ccw/iplb.h
+++ b/pc-bios/s390-ccw/iplb.h
@@ -87,9 +87,9 @@ extern IplParameterBlock iplb 
__attribute__((__aligned__(PAGE_SIZE)));
 struct QemuIplParameters {
 uint8_t  qipl_flags;
 uint8_t  reserved1[3];
-uint64_t netboot_start_addr;
+uint64_t reserved2;
 uint32_t boot_menu_timeout;
-uint8_t  reserved2[12];
+uint8_t  reserved3[12];
 } __attribute__ ((packed));
 typedef struct QemuIplParameters QemuIplParameters;
 
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 6f6d95d170..6abb34e563 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -55,6 +55,9 @@ void write_iplb_location(void);
 unsigned int get_loadparm_index(void);
 void main(void);
 
+/* netmain.c */
+void netmain(void);
+
 /* sclp.c */
 void sclp_print(const char *string);
 void sclp_set_write_mask(uint32_t receive_mask, uint32_t send_mask);
diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
index 85bd9d1695..6f9a558ff5 100644
--- a/pc-bios/s390-ccw/virtio.h
+++ b/pc-bios/s390-ccw/virtio.h
@@ -253,7 +253,6 @@ struct VDev {
 uint8_t scsi_dev_heads;
 bool scsi_device_selected;
 ScsiDevice selected_scsi_device;
-uint64_t netboot_start_addr;
 uint32_t max_transfer;
 uint32_t guest_features[2];
 };
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 3cc79706be..414c3f1b47 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -929,7 +929,7 @@ void zipl_load(void)
 }
 
 if (virtio_get_device_type() == VIRTIO_ID_NET) {
-jump_to_IPL_code(vdev->netboot_start_addr);
+netmain();
 }
 
 ipl_scsi();
diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
index 203df20965..fc44da3161 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -38,8 +38,13 @@ LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */
  */
 void write_subsystem_identification(void)
 {
-lowcore->subchannel_id = blk_schid.sch_id;
-lowcore->subchannel_nr = blk_schid.sch_no;
+if (cutype == CU_TYPE_VIRTIO && virtio_get_device_type() == VIRTIO_ID_NET) 
{
+lowcore->subchannel_id = net_schid.sch_id;
+lowcore->subchannel_nr = net_schid.sch_no;
+} else {
+lowcore->subchannel_id = blk_schid.sch_id;
+lowcore->subchannel_nr = blk_schid.sch_no;
+}
 lowcore->io_int_parm = 0;
 }
 
@@ -231,7 +236,6 @@ static int virtio_setup(void)
 switch (vdev->senseid.cu_model) {
 case VIRTIO_ID_NET:
 puts("Network boot device detected");
-vdev->netboo

[PATCH v4 09/19] pc-bios/s390-ccw: Remove panics from SCSI IPL path

2024-10-16 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from virtio-scsi IPL specific functions so that error
recovery may be possible in the future.

Functions that would previously panic now provide a return code.

Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/iplb.h  |   2 +
 pc-bios/s390-ccw/bootmap.c   |  88 +-
 pc-bios/s390-ccw/jump2ipl.c  |   1 +
 pc-bios/s390-ccw/main.c  |   2 +-
 pc-bios/s390-ccw/virtio-blkdev.c |   4 +-
 pc-bios/s390-ccw/virtio-scsi.c   | 147 +--
 6 files changed, 172 insertions(+), 72 deletions(-)

diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h
index 3758698468..639fa34919 100644
--- a/pc-bios/s390-ccw/iplb.h
+++ b/pc-bios/s390-ccw/iplb.h
@@ -94,6 +94,8 @@ struct QemuIplParameters {
 typedef struct QemuIplParameters QemuIplParameters;
 
 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
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index dd04bb3384..a277fc3431 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -596,7 +596,7 @@ static int ipl_eckd(void)
  * IPL a SCSI disk
  */
 
-static void zipl_load_segment(ComponentEntry *entry)
+static int zipl_load_segment(ComponentEntry *entry)
 {
 const int max_entries = (MAX_SECTOR_SIZE / sizeof(ScsiBlockPtr));
 ScsiBlockPtr *bprs = (void *)sec;
@@ -616,7 +616,10 @@ static void zipl_load_segment(ComponentEntry *entry)
 do {
 memset(bprs, FREE_SPACE_FILLER, bprs_size);
 fill_hex_val(blk_no, &blockno, sizeof(blockno));
-read_block(blockno, bprs, err_msg);
+if (virtio_read(blockno, bprs)) {
+puts(err_msg);
+return -EIO;
+}
 
 for (i = 0;; i++) {
 uint64_t *cur_desc = (void *)&bprs[i];
@@ -644,23 +647,37 @@ static void zipl_load_segment(ComponentEntry *entry)
 }
 address = virtio_load_direct(cur_desc[0], cur_desc[1], 0,
  (void *)address);
-IPL_assert(address != -1, "zIPL load segment failed");
+if (!address) {
+puts("zIPL load segment failed");
+return -EIO;
+}
 }
 } while (blockno);
+
+return 0;
 }
 
 /* Run a zipl program */
-static void zipl_run(ScsiBlockPtr *pte)
+static int zipl_run(ScsiBlockPtr *pte)
 {
 ComponentHeader *header;
 ComponentEntry *entry;
 uint8_t tmp_sec[MAX_SECTOR_SIZE];
 
-read_block(pte->blockno, tmp_sec, "Cannot read header");
+if (virtio_read(pte->blockno, tmp_sec)) {
+puts("Cannot read header");
+return -EIO;
+}
 header = (ComponentHeader *)tmp_sec;
 
-IPL_assert(magic_match(tmp_sec, ZIPL_MAGIC), "No zIPL magic in header");
-IPL_assert(header->type == ZIPL_COMP_HEADER_IPL, "Bad header type");
+if (!magic_match(tmp_sec, ZIPL_MAGIC)) {
+puts("No zIPL magic in header");
+return -EINVAL;
+}
+if (header->type != ZIPL_COMP_HEADER_IPL) {
+puts("Bad header type");
+return -EINVAL;
+}
 
 dputs("start loading images\n");
 
@@ -675,22 +692,30 @@ static void zipl_run(ScsiBlockPtr *pte)
 continue;
 }
 
-zipl_load_segment(entry);
+if (zipl_load_segment(entry)) {
+return 1;
+}
 
 entry++;
 
-IPL_assert((uint8_t *)(&entry[1]) <= (tmp_sec + MAX_SECTOR_SIZE),
-   "Wrong entry value");
+if ((uint8_t *)(&entry[1]) > (tmp_sec + MAX_SECTOR_SIZE)) {
+puts("Wrong entry value");
+return -EINVAL;
+}
 }
 
-IPL_assert(entry->component_type == ZIPL_COMP_ENTRY_EXEC, "No EXEC entry");
+if (entry->component_type != ZIPL_COMP_ENTRY_EXEC) {
+puts("No EXEC entry");
+return -EINVAL;
+}
 
 /* should not return */
 write_reset_psw(entry->compdat.load_psw);
 jump_to_IPL_code(0);
+return 1;
 }
 
-static void ipl_scsi(void)
+static int ipl_scsi(void)
 {
 ScsiMbr *mbr = (void *)sec;
 int program_table_entries = 0;
@@ -701,10 +726,13 @@ static void ipl_scsi(void)
 
 /* Grab the MBR */
 memset(sec, FREE_SPACE_FILLER, sizeof(sec));
-read_block(0, mbr, "Cannot read block 0");
+if (virtio_read(0, mbr)) {
+puts("Cannot read block 0");
+return -EIO;
+}
 
 if (!magic_match(mbr->magic, ZIPL_MAGIC)) {
-return;
+return 0;
 }
 
 puts("Using SCSI scheme.");
@@ -712,11 +740,20 @@ static void ipl_scsi(void)
 IPL_check(mbr->version_id == 1,
   "Unknown MBR layout version, assuming version 1");
 debug_print_int("program table", mbr->pt.blockno);
-IPL_assert(mbr->pt.blockno, "No Program Table");
+if (!mbr->pt.blockno) {
+puts("No Program Table");
+return -EINVAL;
+}
 
 /* Parse

[PATCH 1/1] pc-bios/s390x: Initialize cdrom type to false for each IPL device

2024-11-08 Thread jrossi
From: Jared Rossi 

Clear information about cdrom type so that current IPL device isn't tainted
by stale data from previous devices.

Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/main.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
index a4d1c05aac..7509755e36 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -242,6 +242,7 @@ static bool find_boot_device(void)
 static int virtio_setup(void)
 {
 VDev *vdev = virtio_get_device();
+vdev->is_cdrom = false;
 int ret;
 
 switch (vdev->senseid.cu_model) {
-- 
2.45.1




[PATCH 12/18] pc-bios/s390-ccw: Enable failed IPL to return after error

2024-09-26 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from IPL functions such that a return code is propagated
back to the main IPL calling function (rather than terminating immediately),
which facilitates possible error recovery in the future.

A select few panics remain, which indicate fatal non-devices errors that must
result in termination.

Signed-off-by: Jared Rossi 

---
 pc-bios/s390-ccw/s390-ccw.h  |  2 +-
 pc-bios/s390-ccw/virtio.h|  2 +-
 pc-bios/s390-ccw/bootmap.c   | 53 ++
 pc-bios/s390-ccw/cio.c   |  3 +-
 pc-bios/s390-ccw/jump2ipl.c  |  5 ++-
 pc-bios/s390-ccw/main.c  | 35 +
 pc-bios/s390-ccw/virtio-blkdev.c |  8 ++--
 pc-bios/s390-ccw/virtio.c| 65 +---
 8 files changed, 113 insertions(+), 60 deletions(-)

diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 8dac070257..7e49da46a5 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -78,7 +78,7 @@ void zipl_load(void);
 
 /* jump2ipl.c */
 void write_reset_psw(uint64_t psw);
-void jump_to_IPL_code(uint64_t address);
+int jump_to_IPL_code(uint64_t address);
 void jump_to_low_kernel(void);
 
 /* menu.c */
diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
index 6f9a558ff5..9faf3986b1 100644
--- a/pc-bios/s390-ccw/virtio.h
+++ b/pc-bios/s390-ccw/virtio.h
@@ -274,7 +274,7 @@ void vring_send_buf(VRing *vr, void *p, int len, int flags);
 int vr_poll(VRing *vr);
 int vring_wait_reply(void);
 int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd);
-void virtio_setup_ccw(VDev *vdev);
+int virtio_setup_ccw(VDev *vdev);
 
 int virtio_net_init(void *mac_addr);
 
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 9459e19ffb..6e1f47821e 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -62,15 +62,34 @@ static void *s2_prev_blk = _s2;
 static void *s2_cur_blk = _s2 + MAX_SECTOR_SIZE;
 static void *s2_next_blk = _s2 + MAX_SECTOR_SIZE * 2;
 
-static inline void verify_boot_info(BootInfo *bip)
+static inline int verify_boot_info(BootInfo *bip)
 {
-IPL_assert(magic_match(bip->magic, ZIPL_MAGIC), "No zIPL sig in BootInfo");
-IPL_assert(bip->version == BOOT_INFO_VERSION, "Wrong zIPL version");
-IPL_assert(bip->bp_type == BOOT_INFO_BP_TYPE_IPL, "DASD is not for IPL");
-IPL_assert(bip->dev_type == BOOT_INFO_DEV_TYPE_ECKD, "DASD is not ECKD");
-IPL_assert(bip->flags == BOOT_INFO_FLAGS_ARCH, "Not for this arch");
-IPL_assert(block_size_ok(bip->bp.ipl.bm_ptr.eckd.bptr.size),
-   "Bad block size in zIPL section of the 1st record.");
+if (!magic_match(bip->magic, ZIPL_MAGIC)) {
+puts("No zIPL sig in BootInfo");
+return -EINVAL;
+}
+if (bip->version != BOOT_INFO_VERSION) {
+puts("Wrong zIPL version");
+return -EINVAL;
+}
+if (bip->bp_type != BOOT_INFO_BP_TYPE_IPL) {
+puts("DASD is not for IPL");
+return -ENODEV;
+}
+if (bip->dev_type != BOOT_INFO_DEV_TYPE_ECKD) {
+puts("DASD is not ECKD");
+return -ENODEV;
+}
+if (bip->flags != BOOT_INFO_FLAGS_ARCH) {
+puts("Not for this arch");
+return -EINVAL;
+}
+if (!block_size_ok(bip->bp.ipl.bm_ptr.eckd.bptr.size)) {
+puts("Bad block size in zIPL section of 1st record");
+return -EINVAL;
+}
+
+return 0;
 }
 
 static void eckd_format_chs(ExtEckdBlockPtr *ptr,  bool ldipl,
@@ -368,8 +387,8 @@ static int run_eckd_boot_script(block_number_t bmt_block_nr,
 puts("Unknown script entry type");
 return -EINVAL;
 }
-write_reset_psw(bms->entry[i].address.load_address); /* no return */
-jump_to_IPL_code(0); /* no return */
+write_reset_psw(bms->entry[i].address.load_address);
+jump_to_IPL_code(0);
 return 1;
 }
 
@@ -1054,16 +1073,19 @@ void zipl_load(void)
 
 if (vdev->is_cdrom) {
 ipl_iso_el_torito();
-panic("\n! Cannot IPL this ISO image !\n");
+puts("Failed to IPL this ISO image!");
+return;
 }
 
 if (virtio_get_device_type() == VIRTIO_ID_NET) {
 netmain();
-panic("\n! Cannot IPL from this network !\n");
+puts("Failed to IPL from this network!");
+return;
 }
 
 if (ipl_scsi()) {
-panic("\n! Cannot IPL this device !\n");
+puts("Failed to IPL from this device!");
+return;
 }
 
 switch (virtio_get_device_type()) {
@@ -1074,8 +1096,9 @@ void zipl_load(void)
 zipl_load_vscsi();
 break;
 default:
-panic("\n! Unknown IPL device type !\n");
+puts("Unknown IPL device type!");
+return;
 }
 
-puts("zIPL load failed.");
+puts("zIPL load failed!");
 }
diff --git a/pc-bios/s390-ccw/cio.c b/pc-bios/s390-ccw/cio.c
index 758e74965e..35b08ba7c1 100644
--- a/pc-bios/s390-ccw/cio.c
+++ b/pc-bios/s390-ccw/cio.c
@@ -59,7 +59,8 @@ uint16_t cu_type(SubChannelId schi

[PATCH 14/18] s390x: Add individual loadparm assignment to CCW device

2024-09-26 Thread jrossi
From: Jared Rossi 

Add a loadparm property to the VirtioCcwDevice object so that different
loadparms can be defined on a per-device basis for CCW boot devices.

The machine/global loadparm is still supported. If both a global and per-device
loadparm are defined, the per-device value will override the global value for
that device, but any other devices that do not specify a per-device loadparm
will still use the global loadparm.

It is invalid to assign a loadparm to a non-boot device.

Signed-off-by: Jared Rossi 

---
 hw/s390x/ccw-device.h  |  2 ++
 hw/s390x/ipl.h |  3 +-
 hw/s390x/ccw-device.c  | 46 ++
 hw/s390x/ipl.c | 66 +++---
 hw/s390x/s390-virtio-ccw.c | 18 +--
 hw/s390x/sclp.c|  3 +-
 pc-bios/s390-ccw/main.c| 10 --
 7 files changed, 100 insertions(+), 48 deletions(-)

diff --git a/hw/s390x/ccw-device.h b/hw/s390x/ccw-device.h
index 5feeb0ee7a..1e1737c0f3 100644
--- a/hw/s390x/ccw-device.h
+++ b/hw/s390x/ccw-device.h
@@ -26,6 +26,8 @@ struct CcwDevice {
 CssDevId dev_id;
 /* The actual busid of the virtual subchannel. */
 CssDevId subch_id;
+/* If set, use this loadparm value when device is boot target */
+uint8_t loadparm[8];
 };
 typedef struct CcwDevice CcwDevice;
 
diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index fa394c339d..b670bad551 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -21,7 +21,8 @@
 
 #define DIAG308_FLAGS_LP_VALID 0x80
 
-int s390_ipl_set_loadparm(uint8_t *loadparm);
+void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp);
+void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp);
 void s390_ipl_update_diag308(IplParameterBlock *iplb);
 int s390_ipl_prepare_pv_header(Error **errp);
 int s390_ipl_pv_unpack(void);
diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c
index 14c24e3890..230cc09e03 100644
--- a/hw/s390x/ccw-device.c
+++ b/hw/s390x/ccw-device.c
@@ -13,6 +13,10 @@
 #include "ccw-device.h"
 #include "hw/qdev-properties.h"
 #include "qemu/module.h"
+#include "ipl.h"
+#include "qapi/visitor.h"
+#include "qemu/ctype.h"
+#include "qapi/error.h"
 
 static void ccw_device_refill_ids(CcwDevice *dev)
 {
@@ -37,10 +41,52 @@ static bool ccw_device_realize(CcwDevice *dev, Error **errp)
 return true;
 }
 
+static void ccw_device_get_loadparm(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+CcwDevice *dev = CCW_DEVICE(obj);
+char *str = g_strndup((char *) dev->loadparm, sizeof(dev->loadparm));
+
+visit_type_str(v, name, &str, errp);
+g_free(str);
+}
+
+static void ccw_device_set_loadparm(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+CcwDevice *dev = CCW_DEVICE(obj);
+char *val;
+int index;
+
+index = object_property_get_int(obj, "bootindex", NULL);
+
+if (index < 0) {
+error_setg(errp, "LOADPARM is only valid for boot devices!");
+}
+
+if (!visit_type_str(v, name, &val, errp)) {
+return;
+}
+
+s390_ipl_fmt_loadparm(dev->loadparm, val, errp);
+}
+
+static const PropertyInfo ccw_loadparm = {
+.name  = "ccw_loadparm",
+.description = "Up to 8 chars in set of [A-Za-z0-9. ] to pass"
+" to the guest loader/kernel",
+.get = ccw_device_get_loadparm,
+.set = ccw_device_set_loadparm,
+};
+
 static Property ccw_device_properties[] = {
 DEFINE_PROP_CSS_DEV_ID("devno", CcwDevice, devno),
 DEFINE_PROP_CSS_DEV_ID_RO("dev_id", CcwDevice, dev_id),
 DEFINE_PROP_CSS_DEV_ID_RO("subch_id", CcwDevice, subch_id),
+DEFINE_PROP("loadparm", CcwDevice, loadparm, ccw_loadparm,
+typeof(uint8_t[8])),
 DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 07c8ca69f5..8a62ff6913 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -34,6 +34,7 @@
 #include "qemu/config-file.h"
 #include "qemu/cutils.h"
 #include "qemu/option.h"
+#include "qemu/ctype.h"
 #include "standard-headers/linux/virtio_ids.h"
 
 #define KERN_IMAGE_START0x01UL
@@ -397,12 +398,43 @@ static CcwDevice *s390_get_ccw_device(DeviceState 
*dev_st, int *devtype)
 return ccw_dev;
 }
 
+void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp)
+{
+int i;
+
+/* Initialize the loadparm with spaces */
+memset(loadparm, ' ', LOADPARM_LEN);
+for (i = 0; i < LOADPARM_LEN && str[i]; i++) {
+uint8_t c = qemu_toupper(str[i]); /* mimic HMC */
+
+if (qemu_isalnum(c) || c == '.' || c == ' ') {
+loadparm[i] = c;
+} else {
+error_setg(errp, "LOADPARM: invalid character '%c' (ASCII 0x%02x)",
+   c, c);
+return;
+}
+}
+}
+
+void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp)
+{
+int i;
+
+  

[PATCH 11/18] pc-bios/s390-ccw: Remove panics from Netboot IPL path

2024-09-26 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from Netboot specific functions so that error recovery
may be possible in the future.

Functions that would previously panic now provide a return code.

Signed-off-by: Jared Rossi 

---
 pc-bios/s390-ccw/s390-ccw.h   |  2 +-
 pc-bios/s390-ccw/bootmap.c|  1 +
 pc-bios/s390-ccw/netmain.c| 22 +++---
 pc-bios/s390-ccw/virtio-net.c |  7 +--
 4 files changed, 22 insertions(+), 10 deletions(-)

diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index cbd92f3671..8dac070257 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -57,7 +57,7 @@ unsigned int get_loadparm_index(void);
 void main(void);
 
 /* netmain.c */
-void netmain(void);
+int netmain(void);
 
 /* sclp.c */
 void sclp_print(const char *string);
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index dc7200c264..9459e19ffb 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -1059,6 +1059,7 @@ void zipl_load(void)
 
 if (virtio_get_device_type() == VIRTIO_ID_NET) {
 netmain();
+panic("\n! Cannot IPL from this network !\n");
 }
 
 if (ipl_scsi()) {
diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c
index bc6ad8695f..013f94d932 100644
--- a/pc-bios/s390-ccw/netmain.c
+++ b/pc-bios/s390-ccw/netmain.c
@@ -464,7 +464,7 @@ static bool find_net_dev(Schib *schib, int dev_no)
 return false;
 }
 
-static void virtio_setup(void)
+static int virtio_setup(void)
 {
 Schib schib;
 int ssid;
@@ -479,7 +479,10 @@ static void virtio_setup(void)
 enable_mss_facility();
 
 if (store_iplb(&iplb)) {
-IPL_assert(iplb.pbt == S390_IPL_TYPE_CCW, "IPL_TYPE_CCW expected");
+if (iplb.pbt != S390_IPL_TYPE_CCW) {
+puts("IPL_TYPE_CCW expected");
+}
+
 dev_no = iplb.ccw.devno;
 debug_print_int("device no. ", dev_no);
 net_schid.ssid = iplb.ccw.ssid & 0x3;
@@ -495,10 +498,10 @@ static void virtio_setup(void)
 }
 }
 
-IPL_assert(found, "No virtio net device found");
+return found;
 }
 
-void netmain(void)
+int netmain(void)
 {
 filename_ip_t fn_ip;
 int rc, fnlen;
@@ -506,11 +509,15 @@ void netmain(void)
 sclp_setup();
 puts("Network boot starting...");
 
-virtio_setup();
+if (!virtio_setup()) {
+puts("No virtio net device found.");
+return 1;
+}
 
 rc = net_init(&fn_ip);
 if (rc) {
-panic("Network initialization failed. Halting.");
+puts("Network initialization failed.");
+return 1;
 }
 
 fnlen = strlen(fn_ip.filename);
@@ -528,5 +535,6 @@ void netmain(void)
 jump_to_low_kernel();
 }
 
-panic("Failed to load OS from network.");
+puts("Failed to load OS from network.");
+return 1;
 }
diff --git a/pc-bios/s390-ccw/virtio-net.c b/pc-bios/s390-ccw/virtio-net.c
index 2fcb0a58c5..f9854a22c3 100644
--- a/pc-bios/s390-ccw/virtio-net.c
+++ b/pc-bios/s390-ccw/virtio-net.c
@@ -54,8 +54,11 @@ int virtio_net_init(void *mac_addr)
 vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT;
 virtio_setup_ccw(vdev);
 
-IPL_assert(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT,
-   "virtio-net device does not support the MAC address feature");
+if (!(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT)) {
+puts("virtio-net device does not support the MAC address feature");
+return -1;
+}
+
 memcpy(mac_addr, vdev->config.net.mac, ETH_ALEN);
 
 for (i = 0; i < 64; i++) {
-- 
2.45.1




[PATCH V2 0/18] s390x: Add Full Boot Order Support

2024-09-26 Thread jrossi
From: Jared Rossi 

Loosely a v2, this updated patch set is a significant rework to most aspects of
the initially proposed multi-device boot order. Of particular note, the
original patch set used code jumps to restart the IPL while this version
does not. In order to remove the code jumps, two significant prerequisite
changes are required for the pc-bios. Firstly, the netboot code is linked into
the main s390-ccw.img, which allows for return after a failed netboot, based on
the patch set from Thomas Huth 
(https://lists.gnu.org/archive/html/qemu-devel/2024-06/msg03956.html).
Secondly, IPL errors that result in an immediate termination are converted
to instead provide a return value back to the main IPL calling function.

The routines for building and loading fallback device IPLBs are largely
unchanged: An IPLB is built for each device with a boot index
specified (up to 8 devices). The IPLBs location in memory is passed to the guest
using the global QIPL structure, which is stored at a fixed address. If the
first device in the boot order successfully IPLs, then the additional IPLBs are
never used; however, if the first device fails, subsequent IPLBs are retrieved
from memory and the IPL process restarts using the specifications contained in
the new IPLB.

This continues until IPL is successful or there are no IPLBs left to try.

The per-device loadparm attribute is still uniformly added to CCW devices
although it may only be assigned a value if the device has a boot index.
This will need further rework if a more targeted approach is desired.

Two automated test cases are planned for v3: a minimal test where a guest has
two boot devices defined and the first fails, and also a limit test where the
guest has 8 boot devices defined but only the last one can actually IPL.

Changes v1 -> v2:

- Use the libc from SLOF and replace sclp_print calls with put/printf
- Merge netboot into the main s390-ccw.img
- Rework pc-bios to return on error instead of panic
- Handle non-archetected IPLB types (QEMU SCSI) from DIAG308
- Remove code jumps and instead restart the IPL using a traditional loop

Jared Rossi (18):
  hw/s390x/ipl: Provide more memory to the s390-ccw.img firmware
  pc-bios/s390-ccw: Use the libc from SLOF and remove sclp prints
  pc-bios/s390-ccw: Link the netboot code into the main s390-ccw.img
binary
  hw/s390x: Remove the possibility to load the s390-netboot.img binary
  pc-bios/s390-ccw: Merge netboot.mak into the main Makefile
  docs/system/s390x/bootdevices: Update the documentation about network
booting
  pc-bios/s390-ccw: Remove panics from ISO IPL path
  pc-bios/s390-ccw: Remove panics from SCSI IPL path
  pc-bios/s390-ccw: Remove panics from ECKD IPL path
  pc-bios/s390-ccw: Remove panics from DASD IPL path
  pc-bios/s390-ccw: Remove panics from netboot IPL path
  pc-bios/s390-ccw: Enable failed IPL to return after error
  include/hw/s390x: Add include files for common IPL structs
  s390x: Add individual loadparm assignment to CCW device
  s390x: Build an IPLB for each boot device
  s390x: Rebuild IPLB for SCSI device directly from DIAG308
  pc-bios/s390x: Enable multi-device boot loop
  docs/system: Update documentation for s390x IPL

 docs/system/bootindex.rst |   7 +-
 docs/system/s390x/bootdevices.rst |  29 +-
 pc-bios/s390-ccw/netboot.mak  |  62 -
 hw/s390x/ccw-device.h |   2 +
 hw/s390x/ipl.h| 123 +
 include/hw/s390x/ipl/qipl.h   | 126 +
 pc-bios/s390-ccw/bootmap.h|  21 +-
 pc-bios/s390-ccw/cio.h|   2 +
 pc-bios/s390-ccw/dasd-ipl.h   |   2 +-
 pc-bios/s390-ccw/iplb.h   | 107 ++--
 pc-bios/s390-ccw/libc.h   |  89 ---
 pc-bios/s390-ccw/s390-ccw.h   |  36 +--
 pc-bios/s390-ccw/virtio.h |   3 +-
 hw/s390x/ccw-device.c |  46 
 hw/s390x/ipl.c| 314 +++---
 hw/s390x/s390-virtio-ccw.c|  28 +-
 hw/s390x/sclp.c   |   3 +-
 pc-bios/s390-ccw/bootmap.c| 428 --
 pc-bios/s390-ccw/cio.c|  81 +++---
 pc-bios/s390-ccw/dasd-ipl.c   |  71 ++---
 pc-bios/s390-ccw/jump2ipl.c   |  22 +-
 pc-bios/s390-ccw/libc.c   |  88 --
 pc-bios/s390-ccw/main.c   | 125 -
 pc-bios/s390-ccw/menu.c   |  51 ++--
 pc-bios/s390-ccw/netmain.c|  44 ++-
 pc-bios/s390-ccw/sclp.c   |   7 +-
 pc-bios/s390-ccw/virtio-blkdev.c  |  14 +-
 pc-bios/s390-ccw/virtio-net.c |   7 +-
 pc-bios/s390-ccw/virtio-scsi.c| 164 
 pc-bios/s390-ccw/virtio.c |  67 +++--
 target/s390x/diag.c   |   9 +-
 pc-bios/meson.build   |   1 -
 pc-bios/s390-ccw/Makefile |  69 -
 pc-bios/s390-netboot.img  | Bin 67232 -> 0 bytes
 34 files changed, 1143 insertions(+), 1105 deletions(-)
 delete mode 100644 pc-bios/s390-ccw/netboot.mak
 create mode 100644 include/hw/s390x/ipl/qipl.h
 delete mode

[PATCH 04/18] hw/s390x: Remove the possibility to load the s390-netboot.img binary

2024-09-26 Thread jrossi
From: Jared Rossi 

Since the netboot code has now been merged into the main s390-ccw.img
binary, we don't need the separate s390-netboot.img anymore. Remove
it and the code that was responsible for loading it.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 

---
 hw/s390x/ipl.h | 12 +++--
 hw/s390x/ipl.c | 55 --
 hw/s390x/s390-virtio-ccw.c | 10 ++-
 pc-bios/meson.build|  1 -
 4 files changed, 6 insertions(+), 72 deletions(-)

diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index 57cd125769..b2105b616a 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -134,11 +134,8 @@ void s390_ipl_clear_reset_request(void);
 /*
  * The QEMU IPL Parameters will be stored at absolute address
  * 204 (0xcc) which means it is 32-bit word aligned but not
- * double-word aligned.
- * Placement of data fields in this area must account for
- * their alignment needs. E.g., netboot_start_address must
- * have an offset of 4 + n * 8 bytes within the struct in order
- * to keep it double-word aligned.
+ * double-word aligned. Placement of 64-bit data fields in this
+ * area must account for their alignment needs.
  * The total size of the struct must never exceed 28 bytes.
  * This definition must be kept in sync with the definition
  * in pc-bios/s390-ccw/iplb.h.
@@ -146,9 +143,9 @@ void s390_ipl_clear_reset_request(void);
 struct QemuIplParameters {
 uint8_t  qipl_flags;
 uint8_t  reserved1[3];
-uint64_t netboot_start_addr;
+uint64_t reserved2;
 uint32_t boot_menu_timeout;
-uint8_t  reserved2[12];
+uint8_t  reserved3[12];
 } QEMU_PACKED;
 typedef struct QemuIplParameters QemuIplParameters;
 
@@ -178,7 +175,6 @@ struct S390IPLState {
 char *initrd;
 char *cmdline;
 char *firmware;
-char *netboot_fw;
 uint8_t cssid;
 uint8_t ssid;
 uint16_t devno;
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index c33a044d1d..07c8ca69f5 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -288,7 +288,6 @@ static Property s390_ipl_properties[] = {
 DEFINE_PROP_STRING("initrd", S390IPLState, initrd),
 DEFINE_PROP_STRING("cmdline", S390IPLState, cmdline),
 DEFINE_PROP_STRING("firmware", S390IPLState, firmware),
-DEFINE_PROP_STRING("netboot_fw", S390IPLState, netboot_fw),
 DEFINE_PROP_BOOL("enforce_bios", S390IPLState, enforce_bios, false),
 DEFINE_PROP_BOOL("iplbext_migration", S390IPLState, iplbext_migration,
  true),
@@ -480,56 +479,6 @@ int s390_ipl_set_loadparm(uint8_t *loadparm)
 return -1;
 }
 
-static int load_netboot_image(Error **errp)
-{
-MachineState *ms = MACHINE(qdev_get_machine());
-S390IPLState *ipl = get_ipl_device();
-char *netboot_filename;
-MemoryRegion *sysmem =  get_system_memory();
-MemoryRegion *mr = NULL;
-void *ram_ptr = NULL;
-int img_size = -1;
-
-mr = memory_region_find(sysmem, 0, 1).mr;
-if (!mr) {
-error_setg(errp, "Failed to find memory region at address 0");
-return -1;
-}
-
-ram_ptr = memory_region_get_ram_ptr(mr);
-if (!ram_ptr) {
-error_setg(errp, "No RAM found");
-goto unref_mr;
-}
-
-netboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->netboot_fw);
-if (netboot_filename == NULL) {
-error_setg(errp, "Could not find network bootloader '%s'",
-   ipl->netboot_fw);
-goto unref_mr;
-}
-
-img_size = load_elf_ram(netboot_filename, NULL, NULL, NULL,
-&ipl->start_addr,
-NULL, NULL, NULL, 1, EM_S390, 0, 0, NULL,
-false);
-
-if (img_size < 0) {
-img_size = load_image_size(netboot_filename, ram_ptr, ms->ram_size);
-ipl->start_addr = KERN_IMAGE_START;
-}
-
-if (img_size < 0) {
-error_setg(errp, "Failed to load network bootloader");
-}
-
-g_free(netboot_filename);
-
-unref_mr:
-memory_region_unref(mr);
-return img_size;
-}
-
 static bool is_virtio_ccw_device_of_type(IplParameterBlock *iplb,
  int virtio_id)
 {
@@ -754,10 +703,6 @@ void s390_ipl_prepare_cpu(S390CPU *cpu)
 ipl->iplb_valid = s390_gen_initial_iplb(ipl);
 }
 }
-if (ipl->netboot) {
-load_netboot_image(&error_fatal);
-ipl->qipl.netboot_start_addr = cpu_to_be64(ipl->start_addr);
-}
 s390_ipl_set_boot_menu(ipl);
 s390_ipl_prepare_qipl(cpu);
 }
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index 18240a0fd8..c89a5475eb 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -197,11 +197,10 @@ static void s390_memory_init(MemoryRegion *ram)
 static void s390_init_ipl_dev(const char *kernel_filename,
   const char *kernel_cmdline,
   const char *initrd_filename, const char 
*firmware,
-  const char *net

[PATCH 15/18] hw/s390x: Build an IPLB for each boot device

2024-09-26 Thread jrossi
From: Jared Rossi 

Build an IPLB for any device with a bootindex (up to a maximum of 8 devices).

The IPLB chain is placed immediately before the BIOS in memory. Because this
is not a fixed address, the location of the next IPLB and number of remaining
boot devices is stored in the QIPL global variable for possible later access by
the guest during IPL.

Signed-off-by: Jared Rossi 

---
 hw/s390x/ipl.h  |   1 +
 include/hw/s390x/ipl/qipl.h |   4 +-
 hw/s390x/ipl.c  | 125 
 3 files changed, 101 insertions(+), 29 deletions(-)

diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index b670bad551..e4af8e3782 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -20,6 +20,7 @@
 #include "qom/object.h"
 
 #define DIAG308_FLAGS_LP_VALID 0x80
+#define MAX_IPLB_CHAIN 7 /* Max number of fallback boot devices */
 
 void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp);
 void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp);
diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
index d21a8f91e3..15342ac5cd 100644
--- a/include/hw/s390x/ipl/qipl.h
+++ b/include/hw/s390x/ipl/qipl.h
@@ -31,7 +31,9 @@ struct QemuIplParameters {
 uint8_t  reserved1[3];
 uint64_t reserved2;
 uint32_t boot_menu_timeout;
-uint8_t  reserved3[12];
+uint8_t  reserved3[2];
+uint16_t chain_len;
+uint64_t next_iplb;
 } QEMU_PACKED;
 typedef struct QemuIplParameters QemuIplParameters;
 
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 8a62ff6913..ba66847b9c 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -56,6 +56,13 @@ static bool iplb_extended_needed(void *opaque)
 return ipl->iplbext_migration;
 }
 
+/* Place the IPLB chain immediately before the BIOS in memory */
+static uint64_t find_iplb_chain_addr(uint64_t bios_addr, uint16_t count)
+{
+return (bios_addr & TARGET_PAGE_MASK)
+- (count * sizeof(IplParameterBlock));
+}
+
 static const VMStateDescription vmstate_iplb_extended = {
 .name = "ipl/iplb_extended",
 .version_id = 0,
@@ -398,6 +405,17 @@ static CcwDevice *s390_get_ccw_device(DeviceState *dev_st, 
int *devtype)
 return ccw_dev;
 }
 
+static uint64_t s390_ipl_map_iplb_chain(IplParameterBlock *iplb_chain)
+{
+S390IPLState *ipl = get_ipl_device();
+uint16_t count = ipl->qipl.chain_len;
+uint64_t len = sizeof(IplParameterBlock) * count;
+uint64_t chain_addr = find_iplb_chain_addr(ipl->bios_start_addr, count);
+
+cpu_physical_memory_write(chain_addr, iplb_chain, len);
+return chain_addr;
+}
+
 void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp)
 {
 int i;
@@ -428,54 +446,51 @@ void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t 
*ebcdic_lp)
 }
 }
 
-static bool s390_gen_initial_iplb(S390IPLState *ipl)
+static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
 {
-DeviceState *dev_st;
+S390IPLState *ipl = get_ipl_device();
 CcwDevice *ccw_dev = NULL;
 SCSIDevice *sd;
 int devtype;
 uint8_t *lp;
 
-dev_st = get_boot_device(0);
-if (dev_st) {
-ccw_dev = s390_get_ccw_device(dev_st, &devtype);
-}
-
 /*
  * Currently allow IPL only from CCW devices.
  */
+ccw_dev = s390_get_ccw_device(dev_st, &devtype);
 if (ccw_dev) {
 lp = ccw_dev->loadparm;
 
 switch (devtype) {
 case CCW_DEVTYPE_SCSI:
 sd = SCSI_DEVICE(dev_st);
-ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN);
-ipl->iplb.blk0_len =
+iplb->len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN);
+iplb->blk0_len =
 cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN - 
S390_IPLB_HEADER_LEN);
-ipl->iplb.pbt = S390_IPL_TYPE_QEMU_SCSI;
-ipl->iplb.scsi.lun = cpu_to_be32(sd->lun);
-ipl->iplb.scsi.target = cpu_to_be16(sd->id);
-ipl->iplb.scsi.channel = cpu_to_be16(sd->channel);
-ipl->iplb.scsi.devno = cpu_to_be16(ccw_dev->sch->devno);
-ipl->iplb.scsi.ssid = ccw_dev->sch->ssid & 3;
+iplb->pbt = S390_IPL_TYPE_QEMU_SCSI;
+iplb->scsi.lun = cpu_to_be32(sd->lun);
+iplb->scsi.target = cpu_to_be16(sd->id);
+iplb->scsi.channel = cpu_to_be16(sd->channel);
+iplb->scsi.devno = cpu_to_be16(ccw_dev->sch->devno);
+iplb->scsi.ssid = ccw_dev->sch->ssid & 3;
 break;
 case CCW_DEVTYPE_VFIO:
-ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
-ipl->iplb.pbt = S390_IPL_TYPE_CCW;
-ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
-ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3;
+iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
+iplb->pbt = S390_IPL_TYPE_CCW;
+iplb->ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
+iplb->ccw.ssid = ccw_dev->sch->ssid & 3;
 break;
 case CC

[PATCH 02/18] pc-bios/s390-ccw: Use the libc from SLOF and remove sclp prints

2024-09-26 Thread jrossi
From: Jared Rossi 

We are already using the libc from SLOF for the s390-netboot.img, and
this libc implementation is way more complete and accurate than the
simple implementation that we currently use for the s390-ccw.img binary.
Since we are now always assuming that the SLOF submodule is available
when building the s390-ccw bios (see commit bf6903f6944f), we can drop
the simple implementation and use the SLOF libc for the s390-ccw.img
binary, too.

Additionally replace sclp_print calls with puts/printf now that it is avaliable.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 

---
 pc-bios/s390-ccw/netboot.mak |  3 --
 pc-bios/s390-ccw/bootmap.h   |  4 +-
 pc-bios/s390-ccw/libc.h  | 89 
 pc-bios/s390-ccw/s390-ccw.h  | 30 ---
 pc-bios/s390-ccw/bootmap.c   | 47 -
 pc-bios/s390-ccw/cio.c   | 78 +---
 pc-bios/s390-ccw/dasd-ipl.c  |  5 +-
 pc-bios/s390-ccw/jump2ipl.c  |  5 +-
 pc-bios/s390-ccw/libc.c  | 88 ---
 pc-bios/s390-ccw/main.c  | 14 ++---
 pc-bios/s390-ccw/menu.c  | 51 +-
 pc-bios/s390-ccw/netmain.c   | 10 ++--
 pc-bios/s390-ccw/sclp.c  |  7 +--
 pc-bios/s390-ccw/virtio-blkdev.c |  6 +--
 pc-bios/s390-ccw/virtio-scsi.c   | 17 +++---
 pc-bios/s390-ccw/virtio.c|  2 +-
 pc-bios/s390-ccw/Makefile| 15 --
 17 files changed, 136 insertions(+), 335 deletions(-)
 delete mode 100644 pc-bios/s390-ccw/libc.h
 delete mode 100644 pc-bios/s390-ccw/libc.c

diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak
index 046aa35587..d2b3d8ee74 100644
--- a/pc-bios/s390-ccw/netboot.mak
+++ b/pc-bios/s390-ccw/netboot.mak
@@ -1,9 +1,6 @@
 
-SLOF_DIR := $(SRC_PATH)/../../roms/SLOF
-
 NETOBJS := start.o sclp.o cio.o virtio.o virtio-net.o jump2ipl.o netmain.o
 
-LIBC_INC := -nostdinc -I$(SLOF_DIR)/lib/libc/include
 LIBNET_INC := -I$(SLOF_DIR)/lib/libnet
 
 NETLDFLAGS := $(LDFLAGS) -Wl,-Ttext=0x780
diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
index d4690a88c2..bbe2c132aa 100644
--- a/pc-bios/s390-ccw/bootmap.h
+++ b/pc-bios/s390-ccw/bootmap.h
@@ -336,9 +336,7 @@ static inline void print_volser(const void *volser)
 
 ebcdic_to_ascii((char *)volser, ascii, 6);
 ascii[6] = '\0';
-sclp_print("VOLSER=[");
-sclp_print(ascii);
-sclp_print("]\n");
+printf("VOLSER=[%s]", ascii);
 }
 
 static inline bool unused_space(const void *p, size_t size)
diff --git a/pc-bios/s390-ccw/libc.h b/pc-bios/s390-ccw/libc.h
deleted file mode 100644
index bcdc45732d..00
--- a/pc-bios/s390-ccw/libc.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * libc-style definitions and functions
- *
- * Copyright (c) 2013 Alexander Graf 
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#ifndef S390_CCW_LIBC_H
-#define S390_CCW_LIBC_H
-
-typedef unsigned long  size_t;
-typedef intbool;
-typedef unsigned char  uint8_t;
-typedef unsigned short uint16_t;
-typedef unsigned int   uint32_t;
-typedef unsigned long long uint64_t;
-
-static inline void *memset(void *s, int c, size_t n)
-{
-size_t i;
-unsigned char *p = s;
-
-for (i = 0; i < n; i++) {
-p[i] = c;
-}
-
-return s;
-}
-
-static inline void *memcpy(void *s1, const void *s2, size_t n)
-{
-uint8_t *dest = s1;
-const uint8_t *src = s2;
-size_t i;
-
-for (i = 0; i < n; i++) {
-dest[i] = src[i];
-}
-
-return s1;
-}
-
-static inline int memcmp(const void *s1, const void *s2, size_t n)
-{
-size_t i;
-const uint8_t *p1 = s1, *p2 = s2;
-
-for (i = 0; i < n; i++) {
-if (p1[i] != p2[i]) {
-return p1[i] > p2[i] ? 1 : -1;
-}
-}
-
-return 0;
-}
-
-static inline size_t strlen(const char *str)
-{
-size_t i;
-for (i = 0; *str; i++) {
-str++;
-}
-return i;
-}
-
-static inline char *strcat(char *dest, const char *src)
-{
-int i;
-char *dest_end = dest + strlen(dest);
-
-for (i = 0; i <= strlen(src); i++) {
-dest_end[i] = src[i];
-}
-return dest;
-}
-
-static inline int isdigit(int c)
-{
-return (c >= '0') && (c <= '9');
-}
-
-uint64_t atoui(const char *str);
-char *uitoa(uint64_t num, char *str, size_t len);
-
-#endif
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index c977a52b50..9b622e616a 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -13,6 +13,11 @@
 
 /* #define DEBUG */
 
+#include 
+#include 
+#include 
+#include 
+
 typedef unsigned char  u8;
 typedef unsigned short u16;
 typedef unsigned int   u32;
@@ -26,9 +31,6 @@ typedef unsigned long long u64;
 #define EBUSY   2

[PATCH 08/18] pc-bios/s390-ccw: Remove panics from ECKD IPL path

2024-09-26 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from ECKD block device IPL specific functions so that
error recovery may be possible in the future.

Functions that would previously panic now provide a return code.

Signed-off-by: Jared Rossi 

---
 pc-bios/s390-ccw/bootmap.c | 183 +
 1 file changed, 125 insertions(+), 58 deletions(-)

diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 0751a225cd..dc7200c264 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -145,14 +145,17 @@ static block_number_t load_eckd_segments(block_number_t 
blk, bool ldipl,
 bool more_data;
 
 memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs));
-read_block(blk, bprs, "BPRS read failed");
+if (virtio_read(blk, bprs)) {
+puts("BPRS read failed");
+return -EIO;
+}
 
 do {
 more_data = false;
 for (j = 0;; j++) {
 block_nr = gen_eckd_block_num(&bprs[j].xeckd, ldipl);
 if (is_null_block_number(block_nr)) { /* end of chunk */
-break;
+return 0; /* use 0 to indicate end of load, not real block 0 */
 }
 
 /* we need the updated blockno for the next indirect entry
@@ -163,15 +166,20 @@ static block_number_t load_eckd_segments(block_number_t 
blk, bool ldipl,
 }
 
 /* List directed pointer does not store block size */
-IPL_assert(ldipl || block_size_ok(bprs[j].xeckd.bptr.size),
-   "bad chunk block size");
+if (!ldipl && !block_size_ok(bprs[j].xeckd.bptr.size)) {
+puts("Bad chunk block size");
+return -EIO;
+}
 
 if (!eckd_valid_address(&bprs[j].xeckd, ldipl)) {
 /*
  * If an invalid address is found during LD-IPL then break and
- * retry as CCW
+ * retry as CCW-IPL, otherwise abort on error
  */
-IPL_assert(ldipl, "bad chunk ECKD addr");
+if (!ldipl) {
+puts("Bad chunk ECKD address");
+return -EIO;
+}
 break;
 }
 
@@ -189,7 +197,10 @@ static block_number_t load_eckd_segments(block_number_t 
blk, bool ldipl,
  * I.e. the next ptr must point to the unused memory area
  */
 memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs));
-read_block(block_nr, bprs, "BPRS continuation read failed");
+if (virtio_read(block_nr, bprs)) {
+puts("BPRS continuation read failed");
+return -EIO;
+}
 more_data = true;
 break;
 }
@@ -198,7 +209,10 @@ static block_number_t load_eckd_segments(block_number_t 
blk, bool ldipl,
  * to memory (address).
  */
 rc = virtio_read_many(block_nr, (void *)(*address), count + 1);
-IPL_assert(rc == 0, "code chunk read failed");
+if (rc != 0) {
+puts("Code chunk read failed");
+return -EIO;
+}
 
 *address += (count + 1) * virtio_get_block_size();
 }
@@ -232,7 +246,10 @@ static int eckd_get_boot_menu_index(block_number_t 
s1b_block_nr)
 
 /* Get Stage1b data */
 memset(sec, FREE_SPACE_FILLER, sizeof(sec));
-read_block(s1b_block_nr, s1b, "Cannot read stage1b boot loader");
+if (virtio_read(s1b_block_nr, s1b)) {
+puts("Cannot read stage1b boot loader");
+return -EIO;
+}
 
 memset(_s2, FREE_SPACE_FILLER, sizeof(_s2));
 
@@ -244,7 +261,10 @@ static int eckd_get_boot_menu_index(block_number_t 
s1b_block_nr)
 break;
 }
 
-read_block(cur_block_nr, s2_cur_blk, "Cannot read stage2 boot loader");
+if (virtio_read(cur_block_nr, s2_cur_blk)) {
+puts("Cannot read stage2 boot loader");
+return -EIO;
+}
 
 if (find_zipl_boot_menu_banner(&banner_offset)) {
 /*
@@ -252,8 +272,10 @@ static int eckd_get_boot_menu_index(block_number_t 
s1b_block_nr)
  * possibility of menu data spanning multiple blocks.
  */
 if (prev_block_nr) {
-read_block(prev_block_nr, s2_prev_blk,
-   "Cannot read stage2 boot loader");
+if (virtio_read(prev_block_nr, s2_prev_blk)) {
+puts("Cannot read stage2 boot loader");
+return -EIO;
+}
 }
 
 if (i + 1 < STAGE2_BLK_CNT_MAX) {
@@ -261,8 +283,10 @@ static int eckd_get_boot_menu_index(block_number_t 
s1b_block_nr)
 }
 
 if (next_block_nr && !is_null_block_number(next_block_nr)) {
-read_block(next_block_nr, s2_next_blk,
-   "Cannot read stage2 boot loader");

[PATCH 06/18] docs/system/s390x/bootdevices: Update the documentation about network booting

2024-09-26 Thread jrossi
From: Jared Rossi 

Remove the information about the separate s390-netboot.img from
the documentation.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 

---
 docs/system/s390x/bootdevices.rst | 20 +++-
 1 file changed, 7 insertions(+), 13 deletions(-)

diff --git a/docs/system/s390x/bootdevices.rst 
b/docs/system/s390x/bootdevices.rst
index 1a7a18b43b..c97efb8fc0 100644
--- a/docs/system/s390x/bootdevices.rst
+++ b/docs/system/s390x/bootdevices.rst
@@ -82,23 +82,17 @@ Note that ``0`` can be used to boot the default entry.
 Booting from a network device
 -
 
-Beside the normal guest firmware (which is loaded from the file 
``s390-ccw.img``
-in the data directory of QEMU, or via the ``-bios`` option), QEMU ships with
-a small TFTP network bootloader firmware for virtio-net-ccw devices, too. This
-firmware is loaded from a file called ``s390-netboot.img`` in the QEMU data
-directory. In case you want to load it from a different filename instead,
-you can specify it via the ``-global s390-ipl.netboot_fw=filename``
-command line option.
-
-The ``bootindex`` property is especially important for booting via the network.
-If you don't specify the ``bootindex`` property here, the network bootloader
-firmware code won't get loaded into the guest memory so that the network boot
-will fail. For a successful network boot, try something like this::
+The firmware that ships with QEMU includes a small TFTP network bootloader
+for virtio-net-ccw devices.  The ``bootindex`` property is especially
+important for booting via the network. If you don't specify the ``bootindex``
+property here, the network bootloader won't be taken into consideration and
+the network boot will fail. For a successful network boot, try something
+like this::
 
  qemu-system-s390x -netdev user,id=n1,tftp=...,bootfile=... \
-device virtio-net-ccw,netdev=n1,bootindex=1
 
-The network bootloader firmware also has basic support for pxelinux.cfg-style
+The network bootloader also has basic support for pxelinux.cfg-style
 configuration files. See the `PXELINUX Configuration page
 `__
 for details how to set up the configuration file on your TFTP server.
-- 
2.45.1




[PATCH 16/18] s390x: Rebuild IPLB for SCSI device directly from DIAG308

2024-09-26 Thread jrossi
From: Jared Rossi 

Because virtio-scsi type devices use a non-architected IPLB pbt code they cannot
be set and stored normally. Instead, the IPLB must be rebuilt during re-ipl.

As s390x does not natively support multiple boot devices, the devno field is
used to store the position in the boot order for the device.

Handling the rebuild as part of DIAG308 removes the need to check the devices
for invalid IPLBs later in the IPL.

Signed-off-by: Jared Rossi 

---
 hw/s390x/ipl.h  |  11 ++--
 include/hw/s390x/ipl/qipl.h |   3 +-
 hw/s390x/ipl.c  | 104 ++--
 pc-bios/s390-ccw/jump2ipl.c |  11 ++--
 target/s390x/diag.c |   9 +++-
 5 files changed, 54 insertions(+), 84 deletions(-)

diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index e4af8e3782..d1ebc384d8 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -24,6 +24,7 @@
 
 void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp);
 void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp);
+void s390_rebuild_iplb(uint16_t index, IplParameterBlock *iplb);
 void s390_ipl_update_diag308(IplParameterBlock *iplb);
 int s390_ipl_prepare_pv_header(Error **errp);
 int s390_ipl_pv_unpack(void);
@@ -65,7 +66,8 @@ struct S390IPLState {
 bool enforce_bios;
 bool iplb_valid;
 bool iplb_valid_pv;
-bool netboot;
+bool rebuilt_iplb;
+uint16_t iplb_index;
 /* reset related properties don't have to be migrated or reset */
 enum s390_reset reset_type;
 int reset_cpu_index;
@@ -172,11 +174,14 @@ static inline bool iplb_valid_pv(IplParameterBlock *iplb)
 
 static inline bool iplb_valid(IplParameterBlock *iplb)
 {
+uint32_t len = be32_to_cpu(iplb->len);
+
 switch (iplb->pbt) {
 case S390_IPL_TYPE_FCP:
-return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_FCP_LEN;
+return len >= S390_IPLB_MIN_FCP_LEN;
 case S390_IPL_TYPE_CCW:
-return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_CCW_LEN;
+return (len >= S390_IPLB_MIN_CCW_LEN);
+case S390_IPL_TYPE_QEMU_SCSI:
 default:
 return false;
 }
diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
index 15342ac5cd..0f518adb62 100644
--- a/include/hw/s390x/ipl/qipl.h
+++ b/include/hw/s390x/ipl/qipl.h
@@ -28,7 +28,8 @@
  */
 struct QemuIplParameters {
 uint8_t  qipl_flags;
-uint8_t  reserved1[3];
+uint8_t  index;
+uint8_t  reserved1[2];
 uint64_t reserved2;
 uint32_t boot_menu_timeout;
 uint8_t  reserved3[2];
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index ba66847b9c..86c995b580 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -448,7 +448,6 @@ void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t 
*ebcdic_lp)
 
 static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
 {
-S390IPLState *ipl = get_ipl_device();
 CcwDevice *ccw_dev = NULL;
 SCSIDevice *sd;
 int devtype;
@@ -481,9 +480,6 @@ static bool s390_build_iplb(DeviceState *dev_st, 
IplParameterBlock *iplb)
 iplb->ccw.ssid = ccw_dev->sch->ssid & 3;
 break;
 case CCW_DEVTYPE_VIRTIO_NET:
-/* The S390IPLState netboot is true if ANY IPLB may use netboot */
-ipl->netboot = true;
-/* Fall through to CCW_DEVTYPE_VIRTIO case */
 case CCW_DEVTYPE_VIRTIO:
 iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
 iplb->blk0_len =
@@ -508,6 +504,16 @@ static bool s390_build_iplb(DeviceState *dev_st, 
IplParameterBlock *iplb)
 return false;
 }
 
+
+void s390_rebuild_iplb(uint16_t dev_index, IplParameterBlock *iplb) {
+S390IPLState *ipl = get_ipl_device();
+uint16_t index;
+index = ipl->rebuilt_iplb ? ipl->iplb_index : dev_index;
+
+ipl->rebuilt_iplb = s390_build_iplb(get_boot_device(index), iplb);
+ipl->iplb_index = index;
+}
+
 static bool s390_init_all_iplbs(S390IPLState *ipl)
 {
 int iplb_num = 0;
@@ -560,44 +566,6 @@ static bool s390_init_all_iplbs(S390IPLState *ipl)
 return iplb_num;
 }
 
-static bool is_virtio_ccw_device_of_type(IplParameterBlock *iplb,
- int virtio_id)
-{
-uint8_t cssid;
-uint8_t ssid;
-uint16_t devno;
-uint16_t schid;
-SubchDev *sch = NULL;
-
-if (iplb->pbt != S390_IPL_TYPE_CCW) {
-return false;
-}
-
-devno = be16_to_cpu(iplb->ccw.devno);
-ssid = iplb->ccw.ssid & 3;
-
-for (schid = 0; schid < MAX_SCHID; schid++) {
-for (cssid = 0; cssid < MAX_CSSID; cssid++) {
-sch = css_find_subch(1, cssid, ssid, schid);
-
-if (sch && sch->devno == devno) {
-return sch->id.cu_model == virtio_id;
-}
-}
-}
-return false;
-}
-
-static bool is_virtio_net_device(IplParameterBlock *iplb)
-{
-return is_virtio_ccw_device_of_type(iplb, VIRTIO_ID_NET);
-}
-
-static bool is_virtio_scsi_device(IplParameterBlock *iplb)
-{
-return is_virtio_ccw_device_of_type(ipl

[PATCH 09/18] pc-bios/s390-ccw: Remove panics from SCSI IPL path

2024-09-26 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from virtio-scsi IPL specific functions so that error
recovery may be possible in the future.

Functions that would previously panic now provide a return code.

Signed-off-by: Jared Rossi 

---
 pc-bios/s390-ccw/bootmap.c |  88 +++-
 pc-bios/s390-ccw/virtio-scsi.c | 147 ++---
 2 files changed, 165 insertions(+), 70 deletions(-)

diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 31cf0f6d97..0751a225cd 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -524,7 +524,7 @@ static void ipl_eckd(void)
  * IPL a SCSI disk
  */
 
-static void zipl_load_segment(ComponentEntry *entry)
+static int zipl_load_segment(ComponentEntry *entry)
 {
 const int max_entries = (MAX_SECTOR_SIZE / sizeof(ScsiBlockPtr));
 ScsiBlockPtr *bprs = (void *)sec;
@@ -544,7 +544,10 @@ static void zipl_load_segment(ComponentEntry *entry)
 do {
 memset(bprs, FREE_SPACE_FILLER, bprs_size);
 fill_hex_val(blk_no, &blockno, sizeof(blockno));
-read_block(blockno, bprs, err_msg);
+if (virtio_read(blockno, bprs)) {
+puts(err_msg);
+return -EIO;
+}
 
 for (i = 0;; i++) {
 uint64_t *cur_desc = (void *)&bprs[i];
@@ -572,23 +575,37 @@ static void zipl_load_segment(ComponentEntry *entry)
 }
 address = virtio_load_direct(cur_desc[0], cur_desc[1], 0,
  (void *)address);
-IPL_assert(address != -1, "zIPL load segment failed");
+if (!address) {
+puts("zIPL load segment failed");
+return -EIO;
+}
 }
 } while (blockno);
+
+return 0;
 }
 
 /* Run a zipl program */
-static void zipl_run(ScsiBlockPtr *pte)
+static int zipl_run(ScsiBlockPtr *pte)
 {
 ComponentHeader *header;
 ComponentEntry *entry;
 uint8_t tmp_sec[MAX_SECTOR_SIZE];
 
-read_block(pte->blockno, tmp_sec, "Cannot read header");
+if (virtio_read(pte->blockno, tmp_sec)) {
+puts("Cannot read header");
+return -EIO;
+}
 header = (ComponentHeader *)tmp_sec;
 
-IPL_assert(magic_match(tmp_sec, ZIPL_MAGIC), "No zIPL magic in header");
-IPL_assert(header->type == ZIPL_COMP_HEADER_IPL, "Bad header type");
+if (!magic_match(tmp_sec, ZIPL_MAGIC)) {
+puts("No zIPL magic in header");
+return -EINVAL;
+}
+if (header->type != ZIPL_COMP_HEADER_IPL) {
+puts("Bad header type");
+return -EINVAL;
+}
 
 dputs("start loading images\n");
 
@@ -603,22 +620,30 @@ static void zipl_run(ScsiBlockPtr *pte)
 continue;
 }
 
-zipl_load_segment(entry);
+if (zipl_load_segment(entry)) {
+return 1;
+}
 
 entry++;
 
-IPL_assert((uint8_t *)(&entry[1]) <= (tmp_sec + MAX_SECTOR_SIZE),
-   "Wrong entry value");
+if ((uint8_t *)(&entry[1]) > (tmp_sec + MAX_SECTOR_SIZE)) {
+puts("Wrong entry value");
+return -EINVAL;
+}
 }
 
-IPL_assert(entry->component_type == ZIPL_COMP_ENTRY_EXEC, "No EXEC entry");
+if (entry->component_type != ZIPL_COMP_ENTRY_EXEC) {
+puts("No EXEC entry");
+return -EINVAL;
+}
 
 /* should not return */
 write_reset_psw(entry->compdat.load_psw);
 jump_to_IPL_code(0);
+return 1;
 }
 
-static void ipl_scsi(void)
+static int ipl_scsi(void)
 {
 ScsiMbr *mbr = (void *)sec;
 int program_table_entries = 0;
@@ -629,10 +654,13 @@ static void ipl_scsi(void)
 
 /* Grab the MBR */
 memset(sec, FREE_SPACE_FILLER, sizeof(sec));
-read_block(0, mbr, "Cannot read block 0");
+if (virtio_read(0, mbr)) {
+puts("Cannot read block 0");
+return -EIO;
+}
 
 if (!magic_match(mbr->magic, ZIPL_MAGIC)) {
-return;
+return 0;
 }
 
 puts("Using SCSI scheme.");
@@ -640,11 +668,20 @@ static void ipl_scsi(void)
 IPL_check(mbr->version_id == 1,
   "Unknown MBR layout version, assuming version 1");
 debug_print_int("program table", mbr->pt.blockno);
-IPL_assert(mbr->pt.blockno, "No Program Table");
+if (!mbr->pt.blockno) {
+puts("No Program Table");
+return -EINVAL;
+}
 
 /* Parse the program table */
-read_block(mbr->pt.blockno, sec, "Error reading Program Table");
-IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT");
+if (virtio_read(mbr->pt.blockno, sec)) {
+puts("Error reading Program Table");
+return -EIO;
+}
+if (!magic_match(sec, ZIPL_MAGIC)) {
+puts("No zIPL magic in Program Table");
+return -EINVAL;
+}
 
 for (i = 0; i < MAX_BOOT_ENTRIES; i++) {
 if (prog_table->entry[i].scsi.blockno) {
@@ -654,17 +691,22 @@ static void ipl_scsi(void)
 }
 
 debug_print_int("program table entries", program_table

[PATCH 13/18] include/hw/s390x: Add include files for common IPL structs

2024-09-26 Thread jrossi
From: Jared Rossi 

Currently, structures defined in both hw/s390x/ipl.h and pc-bios/s390-ccw/iplb.h
must be kept in sync, which is prone to error. Instead, create a new directory
at include/hw/s390x/ipl/ to contain the definitions that must be shared.

Signed-off-by: Jared Rossi 

---
 hw/s390x/ipl.h  | 104 +-
 include/hw/s390x/ipl/qipl.h | 123 
 pc-bios/s390-ccw/iplb.h |  84 ++--
 pc-bios/s390-ccw/Makefile   |   2 +-
 4 files changed, 130 insertions(+), 183 deletions(-)
 create mode 100644 include/hw/s390x/ipl/qipl.h

diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index b2105b616a..fa394c339d 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -16,95 +16,11 @@
 #include "cpu.h"
 #include "exec/address-spaces.h"
 #include "hw/qdev-core.h"
+#include "hw/s390x/ipl/qipl.h"
 #include "qom/object.h"
 
-struct IPLBlockPVComp {
-uint64_t tweak_pref;
-uint64_t addr;
-uint64_t size;
-} QEMU_PACKED;
-typedef struct IPLBlockPVComp IPLBlockPVComp;
-
-struct IPLBlockPV {
-uint8_t  reserved18[87];/* 0x18 */
-uint8_t  version;   /* 0x6f */
-uint32_t reserved70;/* 0x70 */
-uint32_t num_comp;  /* 0x74 */
-uint64_t pv_header_addr;/* 0x78 */
-uint64_t pv_header_len; /* 0x80 */
-struct IPLBlockPVComp components[0];
-} QEMU_PACKED;
-typedef struct IPLBlockPV IPLBlockPV;
-
-struct IplBlockCcw {
-uint8_t  reserved0[85];
-uint8_t  ssid;
-uint16_t devno;
-uint8_t  vm_flags;
-uint8_t  reserved3[3];
-uint32_t vm_parm_len;
-uint8_t  nss_name[8];
-uint8_t  vm_parm[64];
-uint8_t  reserved4[8];
-} QEMU_PACKED;
-typedef struct IplBlockCcw IplBlockCcw;
-
-struct IplBlockFcp {
-uint8_t  reserved1[305 - 1];
-uint8_t  opt;
-uint8_t  reserved2[3];
-uint16_t reserved3;
-uint16_t devno;
-uint8_t  reserved4[4];
-uint64_t wwpn;
-uint64_t lun;
-uint32_t bootprog;
-uint8_t  reserved5[12];
-uint64_t br_lba;
-uint32_t scp_data_len;
-uint8_t  reserved6[260];
-uint8_t  scp_data[0];
-} QEMU_PACKED;
-typedef struct IplBlockFcp IplBlockFcp;
-
-struct IplBlockQemuScsi {
-uint32_t lun;
-uint16_t target;
-uint16_t channel;
-uint8_t  reserved0[77];
-uint8_t  ssid;
-uint16_t devno;
-} QEMU_PACKED;
-typedef struct IplBlockQemuScsi IplBlockQemuScsi;
-
 #define DIAG308_FLAGS_LP_VALID 0x80
 
-union IplParameterBlock {
-struct {
-uint32_t len;
-uint8_t  reserved0[3];
-uint8_t  version;
-uint32_t blk0_len;
-uint8_t  pbt;
-uint8_t  flags;
-uint16_t reserved01;
-uint8_t  loadparm[8];
-union {
-IplBlockCcw ccw;
-IplBlockFcp fcp;
-IPLBlockPV pv;
-IplBlockQemuScsi scsi;
-};
-} QEMU_PACKED;
-struct {
-uint8_t  reserved1[110];
-uint16_t devno;
-uint8_t  reserved2[88];
-uint8_t  reserved_ext[4096 - 200];
-} QEMU_PACKED;
-} QEMU_PACKED;
-typedef union IplParameterBlock IplParameterBlock;
-
 int s390_ipl_set_loadparm(uint8_t *loadparm);
 void s390_ipl_update_diag308(IplParameterBlock *iplb);
 int s390_ipl_prepare_pv_header(Error **errp);
@@ -131,24 +47,6 @@ void s390_ipl_clear_reset_request(void);
 #define QIPL_FLAG_BM_OPTS_CMD   0x80
 #define QIPL_FLAG_BM_OPTS_ZIPL  0x40
 
-/*
- * The QEMU IPL Parameters will be stored at absolute address
- * 204 (0xcc) which means it is 32-bit word aligned but not
- * double-word aligned. Placement of 64-bit data fields in this
- * area must account for their alignment needs.
- * The total size of the struct must never exceed 28 bytes.
- * This definition must be kept in sync with the definition
- * in pc-bios/s390-ccw/iplb.h.
- */
-struct QemuIplParameters {
-uint8_t  qipl_flags;
-uint8_t  reserved1[3];
-uint64_t reserved2;
-uint32_t boot_menu_timeout;
-uint8_t  reserved3[12];
-} QEMU_PACKED;
-typedef struct QemuIplParameters QemuIplParameters;
-
 #define TYPE_S390_IPL "s390-ipl"
 OBJECT_DECLARE_SIMPLE_TYPE(S390IPLState, S390_IPL)
 
diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
new file mode 100644
index 00..d21a8f91e3
--- /dev/null
+++ b/include/hw/s390x/ipl/qipl.h
@@ -0,0 +1,123 @@
+/*
+ * S/390 boot structures
+ *
+ * Copyright 2024 IBM Corp.
+ * Author(s): Jared Rossi 
+ *
+ * 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.
+ */
+
+#ifndef S390X_QIPL_H
+#define S390X_QIPL_H
+
+/* Boot Menu flags */
+#define QIPL_FLAG_BM_OPTS_CMD   0x80
+#define QIPL_FLAG_BM_OPTS_ZIPL  0x40
+
+#define QIPL_ADDRESS  0xcc
+#define LOADPARM_LEN8
+
+/*
+ * The QEMU IPL Parameters will be stored at absolute address
+ * 204 (0xcc) which means it is 32-bit word aligned but not
+ * double-word aligned. Placement of 64-bit data fields in 

[PATCH 03/18] pc-bios/s390-ccw: Link the netboot code into the main s390-ccw.img binary

2024-09-26 Thread jrossi
From: Jared Rossi 

We originally built a separate binary for the netboot code since it
was considered as experimental and we could not be sure that the
necessary SLOF module had been checked out. Time passed, the code
proved its usefulness, and the build system nowadays makes sure that
the SLOF module is checked out if you have a s390x compiler available
for building the s390-ccw bios. So there is no real compelling reason
anymore to keep the netboot code in a separate binary. Linking the
code together with the main s390-ccw.img will make future enhancements
much easier, like supporting more than one boot device.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 

---
 pc-bios/s390-ccw/netboot.mak | 14 --
 pc-bios/s390-ccw/cio.h   |  2 ++
 pc-bios/s390-ccw/iplb.h  |  4 ++--
 pc-bios/s390-ccw/s390-ccw.h  |  3 +++
 pc-bios/s390-ccw/virtio.h|  1 -
 pc-bios/s390-ccw/bootmap.c   |  2 +-
 pc-bios/s390-ccw/main.c  | 10 +++---
 pc-bios/s390-ccw/netmain.c   | 15 ++-
 pc-bios/s390-ccw/Makefile| 13 +++--
 9 files changed, 24 insertions(+), 40 deletions(-)

diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak
index d2b3d8ee74..0a24257ff4 100644
--- a/pc-bios/s390-ccw/netboot.mak
+++ b/pc-bios/s390-ccw/netboot.mak
@@ -1,18 +1,4 @@
 
-NETOBJS := start.o sclp.o cio.o virtio.o virtio-net.o jump2ipl.o netmain.o
-
-LIBNET_INC := -I$(SLOF_DIR)/lib/libnet
-
-NETLDFLAGS := $(LDFLAGS) -Wl,-Ttext=0x780
-
-$(NETOBJS): EXTRA_CFLAGS += $(LIBC_INC) $(LIBNET_INC)
-
-s390-netboot.elf: $(NETOBJS) libnet.a libc.a
-   $(call quiet-command,$(CC) $(NETLDFLAGS) -o $@ $^,Linking)
-
-s390-netboot.img: s390-netboot.elf
-   $(call quiet-command,$(STRIP) --strip-unneeded $< -o $@,Stripping $< 
into)
-
 # libc files:
 
 LIBC_CFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
diff --git a/pc-bios/s390-ccw/cio.h b/pc-bios/s390-ccw/cio.h
index 8b18153deb..6a5e86ba01 100644
--- a/pc-bios/s390-ccw/cio.h
+++ b/pc-bios/s390-ccw/cio.h
@@ -361,6 +361,8 @@ typedef struct CcwSearchIdData {
 uint8_t record;
 } __attribute__((packed)) CcwSearchIdData;
 
+extern SubChannelId net_schid;
+
 int enable_mss_facility(void);
 void enable_subchannel(SubChannelId schid);
 uint16_t cu_type(SubChannelId schid);
diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h
index cb6ac8a880..3758698468 100644
--- a/pc-bios/s390-ccw/iplb.h
+++ b/pc-bios/s390-ccw/iplb.h
@@ -87,9 +87,9 @@ extern IplParameterBlock iplb 
__attribute__((__aligned__(PAGE_SIZE)));
 struct QemuIplParameters {
 uint8_t  qipl_flags;
 uint8_t  reserved1[3];
-uint64_t netboot_start_addr;
+uint64_t reserved2;
 uint32_t boot_menu_timeout;
-uint8_t  reserved2[12];
+uint8_t  reserved3[12];
 } __attribute__ ((packed));
 typedef struct QemuIplParameters QemuIplParameters;
 
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 9b622e616a..0ed7eb8ade 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -55,6 +55,9 @@ void write_iplb_location(void);
 unsigned int get_loadparm_index(void);
 void main(void);
 
+/* netmain.c */
+void netmain(void);
+
 /* sclp.c */
 void sclp_print(const char *string);
 void sclp_set_write_mask(uint32_t receive_mask, uint32_t send_mask);
diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
index 85bd9d1695..6f9a558ff5 100644
--- a/pc-bios/s390-ccw/virtio.h
+++ b/pc-bios/s390-ccw/virtio.h
@@ -253,7 +253,6 @@ struct VDev {
 uint8_t scsi_dev_heads;
 bool scsi_device_selected;
 ScsiDevice selected_scsi_device;
-uint64_t netboot_start_addr;
 uint32_t max_transfer;
 uint32_t guest_features[2];
 };
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 3cc79706be..414c3f1b47 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -929,7 +929,7 @@ void zipl_load(void)
 }
 
 if (virtio_get_device_type() == VIRTIO_ID_NET) {
-jump_to_IPL_code(vdev->netboot_start_addr);
+netmain();
 }
 
 ipl_scsi();
diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
index 73093d3f14..2345432abb 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -38,8 +38,13 @@ LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */
  */
 void write_subsystem_identification(void)
 {
-lowcore->subchannel_id = blk_schid.sch_id;
-lowcore->subchannel_nr = blk_schid.sch_no;
+if (cutype == CU_TYPE_VIRTIO && virtio_get_device_type() == VIRTIO_ID_NET) 
{
+lowcore->subchannel_id = net_schid.sch_id;
+lowcore->subchannel_nr = net_schid.sch_no;
+} else {
+lowcore->subchannel_id = blk_schid.sch_id;
+lowcore->subchannel_nr = blk_schid.sch_no;
+}
 lowcore->io_int_parm = 0;
 }
 
@@ -231,7 +236,6 @@ static int virtio_setup(void)
 switch (vdev->senseid.cu_model) {
 case VIRTIO_ID_NET:
 puts("Network boot device detected");
-vdev->netbo

[PATCH 10/18] pc-bios/s390-ccw: Remove panics from DASD IPL path

2024-09-26 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from DASD IPL specific functions so that error recovery
may be possible in the future.

Functions that would previously panic now provide a return code.

Signed-off-by: Jared Rossi 

---
 pc-bios/s390-ccw/dasd-ipl.h |  2 +-
 pc-bios/s390-ccw/dasd-ipl.c | 66 -
 2 files changed, 37 insertions(+), 31 deletions(-)

diff --git a/pc-bios/s390-ccw/dasd-ipl.h b/pc-bios/s390-ccw/dasd-ipl.h
index c394828906..eb1898c84a 100644
--- a/pc-bios/s390-ccw/dasd-ipl.h
+++ b/pc-bios/s390-ccw/dasd-ipl.h
@@ -11,6 +11,6 @@
 #ifndef DASD_IPL_H
 #define DASD_IPL_H
 
-void dasd_ipl(SubChannelId schid, uint16_t cutype);
+int dasd_ipl(SubChannelId schid, uint16_t cutype);
 
 #endif /* DASD_IPL_H */
diff --git a/pc-bios/s390-ccw/dasd-ipl.c b/pc-bios/s390-ccw/dasd-ipl.c
index 99c0b93083..cd4fb884d6 100644
--- a/pc-bios/s390-ccw/dasd-ipl.c
+++ b/pc-bios/s390-ccw/dasd-ipl.c
@@ -111,38 +111,29 @@ static void make_readipl(void)
 ccwIplRead->count = 0x18; /* Read 0x18 bytes of data */
 }
 
-static void run_readipl(SubChannelId schid, uint16_t cutype)
+static int run_readipl(SubChannelId schid, uint16_t cutype)
 {
-if (do_cio(schid, cutype, 0x00, CCW_FMT0)) {
-panic("dasd-ipl: Failed to run Read IPL channel program\n");
-}
+return do_cio(schid, cutype, 0x00, CCW_FMT0);
 }
 
 /*
  * The architecture states that IPL1 data should consist of a psw followed by
  * format-0 READ and TIC CCWs. Let's sanity check.
  */
-static void check_ipl1(void)
+static bool check_ipl1(void)
 {
 Ccw0 *ccwread = (Ccw0 *)0x08;
 Ccw0 *ccwtic = (Ccw0 *)0x10;
 
-if (ccwread->cmd_code != CCW_CMD_DASD_READ ||
-ccwtic->cmd_code != CCW_CMD_TIC) {
-panic("dasd-ipl: IPL1 data invalid. Is this disk really bootable?\n");
-}
+return (ccwread->cmd_code == CCW_CMD_DASD_READ &&
+ccwtic->cmd_code == CCW_CMD_TIC);
 }
 
-static void check_ipl2(uint32_t ipl2_addr)
+static bool check_ipl2(uint32_t ipl2_addr)
 {
 Ccw0 *ccw = u32toptr(ipl2_addr);
 
-if (ipl2_addr == 0x00) {
-panic("IPL2 address invalid. Is this disk really bootable?\n");
-}
-if (ccw->cmd_code == 0x00) {
-panic("IPL2 ccw data invalid. Is this disk really bootable?\n");
-}
+return (ipl2_addr != 0x00 && ccw->cmd_code != 0x00);
 }
 
 static uint32_t read_ipl2_addr(void)
@@ -188,52 +179,67 @@ static void ipl1_fixup(void)
 ccwSearchTic->cda = ptr2u32(ccwSearchID);
 }
 
-static void run_ipl1(SubChannelId schid, uint16_t cutype)
+static int run_ipl1(SubChannelId schid, uint16_t cutype)
  {
 uint32_t startAddr = 0x08;
 
-if (do_cio(schid, cutype, startAddr, CCW_FMT0)) {
-panic("dasd-ipl: Failed to run IPL1 channel program\n");
-}
+return do_cio(schid, cutype, startAddr, CCW_FMT0);
 }
 
-static void run_ipl2(SubChannelId schid, uint16_t cutype, uint32_t addr)
+static int run_ipl2(SubChannelId schid, uint16_t cutype, uint32_t addr)
 {
-if (run_dynamic_ccw_program(schid, cutype, addr)) {
-panic("dasd-ipl: Failed to run IPL2 channel program\n");
-}
+return run_dynamic_ccw_program(schid, cutype, addr);
 }
 
 /*
  * Limitations in vfio-ccw support complicate the IPL process. Details can
  * be found in docs/devel/s390-dasd-ipl.rst
  */
-void dasd_ipl(SubChannelId schid, uint16_t cutype)
+int dasd_ipl(SubChannelId schid, uint16_t cutype)
 {
 PSWLegacy *pswl = (PSWLegacy *) 0x00;
 uint32_t ipl2_addr;
 
 /* Construct Read IPL CCW and run it to read IPL1 from boot disk */
 make_readipl();
-run_readipl(schid, cutype);
+if (run_readipl(schid, cutype)) {
+puts("Failed to run Read IPL channel program");
+return -EIO;
+}
+
 ipl2_addr = read_ipl2_addr();
-check_ipl1();
+
+if (!check_ipl1()) {
+puts("IPL1 invalid for DASD-IPL");
+return -EINVAL;
+}
 
 /*
  * Fixup IPL1 channel program to account for vfio-ccw limitations, then run
  * it to read IPL2 channel program from boot disk.
  */
 ipl1_fixup();
-run_ipl1(schid, cutype);
-check_ipl2(ipl2_addr);
+if (run_ipl1(schid, cutype)) {
+puts("Failed to run IPL1 channel program");
+return -EIO;
+}
+
+if (!check_ipl2(ipl2_addr)) {
+puts("IPL2 invalid for DASD-IPL");
+return -EINVAL;
+}
 
 /*
  * Run IPL2 channel program to read operating system code from boot disk
  */
-run_ipl2(schid, cutype, ipl2_addr);
+if (run_ipl2(schid, cutype, ipl2_addr)) {
+puts("Failed to run IPL2 channel program");
+return -EIO;
+}
 
 /* Transfer control to the guest operating system */
 pswl->mask |= PSW_MASK_EAMODE;   /* Force z-mode */
 pswl->addr |= PSW_MASK_BAMODE;   /* ...  */
 jump_to_low_kernel();
+return 1;
 }
-- 
2.45.1




[PATCH 05/18] pc-bios/s390-ccw: Merge netboot.mak into the main Makefile

2024-09-26 Thread jrossi
From: Jared Rossi 

Now that the netboot code has been merged into the main s390-ccw.img,
it also does not make sense to keep the build rules in a separate
file. Thus let's merge netboot.mak into the main Makefile.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 

---
 pc-bios/s390-ccw/netboot.mak |  45 -
 pc-bios/s390-ccw/Makefile|  47 ++-
 pc-bios/s390-netboot.img | Bin 67232 -> 0 bytes
 3 files changed, 46 insertions(+), 46 deletions(-)
 delete mode 100644 pc-bios/s390-ccw/netboot.mak
 delete mode 100644 pc-bios/s390-netboot.img

diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak
deleted file mode 100644
index 0a24257ff4..00
--- a/pc-bios/s390-ccw/netboot.mak
+++ /dev/null
@@ -1,45 +0,0 @@
-
-# libc files:
-
-LIBC_CFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
- -MMD -MP -MT $@ -MF $(@:%.o=%.d)
-
-CTYPE_OBJS = isdigit.o isxdigit.o toupper.o
-%.o : $(SLOF_DIR)/lib/libc/ctype/%.c
-   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
-
-STRING_OBJS = strcat.o strchr.o strrchr.o strcpy.o strlen.o strncpy.o \
- strcmp.o strncmp.o strcasecmp.o strncasecmp.o strstr.o \
- memset.o memcpy.o memmove.o memcmp.o
-%.o : $(SLOF_DIR)/lib/libc/string/%.c
-   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
-
-STDLIB_OBJS = atoi.o atol.o strtoul.o strtol.o rand.o malloc.o free.o
-%.o : $(SLOF_DIR)/lib/libc/stdlib/%.c
-   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
-
-STDIO_OBJS = sprintf.o snprintf.o vfprintf.o vsnprintf.o vsprintf.o fprintf.o \
-printf.o putc.o puts.o putchar.o stdchnls.o fileno.o
-%.o : $(SLOF_DIR)/lib/libc/stdio/%.c
-   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
-
-sbrk.o: $(SLOF_DIR)/slof/sbrk.c
-   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
-
-LIBCOBJS := $(STRING_OBJS) $(CTYPE_OBJS) $(STDLIB_OBJS) $(STDIO_OBJS) sbrk.o
-
-libc.a: $(LIBCOBJS)
-   $(call quiet-command,$(AR) -rc $@ $^,Creating static library)
-
-# libnet files:
-
-LIBNETOBJS := args.o dhcp.o dns.o icmpv6.o ipv6.o tcp.o udp.o bootp.o \
- dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o pxelinux.o
-LIBNETCFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
-  -DDHCPARCH=0x1F -MMD -MP -MT $@ -MF $(@:%.o=%.d)
-
-%.o : $(SLOF_DIR)/lib/libnet/%.c
-   $(call quiet-command,$(CC) $(LIBNETCFLAGS) -c -o $@ $<,Compiling)
-
-libnet.a: $(LIBNETOBJS)
-   $(call quiet-command,$(AR) -rc $@ $^,Creating static library)
diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
index cf6859823a..27cbb354af 100644
--- a/pc-bios/s390-ccw/Makefile
+++ b/pc-bios/s390-ccw/Makefile
@@ -61,7 +61,52 @@ config-cc.mak: Makefile
$(call cc-option,-march=z900,-march=z10)) 3> config-cc.mak
 -include config-cc.mak
 
-include $(SRC_PATH)/netboot.mak
+# libc files:
+
+LIBC_CFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
+ -MMD -MP -MT $@ -MF $(@:%.o=%.d)
+
+CTYPE_OBJS = isdigit.o isxdigit.o toupper.o
+%.o : $(SLOF_DIR)/lib/libc/ctype/%.c
+   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
+
+STRING_OBJS = strcat.o strchr.o strrchr.o strcpy.o strlen.o strncpy.o \
+ strcmp.o strncmp.o strcasecmp.o strncasecmp.o strstr.o \
+ memset.o memcpy.o memmove.o memcmp.o
+%.o : $(SLOF_DIR)/lib/libc/string/%.c
+   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
+
+STDLIB_OBJS = atoi.o atol.o strtoul.o strtol.o rand.o malloc.o free.o
+%.o : $(SLOF_DIR)/lib/libc/stdlib/%.c
+   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
+
+STDIO_OBJS = sprintf.o snprintf.o vfprintf.o vsnprintf.o vsprintf.o fprintf.o \
+printf.o putc.o puts.o putchar.o stdchnls.o fileno.o
+%.o : $(SLOF_DIR)/lib/libc/stdio/%.c
+   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
+
+sbrk.o: $(SLOF_DIR)/slof/sbrk.c
+   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
+
+LIBCOBJS := $(STRING_OBJS) $(CTYPE_OBJS) $(STDLIB_OBJS) $(STDIO_OBJS) sbrk.o
+
+libc.a: $(LIBCOBJS)
+   $(call quiet-command,$(AR) -rc $@ $^,Creating static library)
+
+# libnet files:
+
+LIBNETOBJS := args.o dhcp.o dns.o icmpv6.o ipv6.o tcp.o udp.o bootp.o \
+ dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o pxelinux.o
+LIBNETCFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
+  -DDHCPARCH=0x1F -MMD -MP -MT $@ -MF $(@:%.o=%.d)
+
+%.o : $(SLOF_DIR)/lib/libnet/%.c
+   $(call quiet-command,$(CC) $(LIBNETCFLAGS) -c -o $@ $<,Compiling)
+
+libnet.a: $(LIBNETOBJS)
+   $(call quiet-command,$(AR) -rc $@ $^,Creating static library)
+
+# Main targets:
 
 build-all: s390-ccw.img
 
diff --git a/pc-bios/s390-netboot.img b/pc-bios/s390-netboot.img
deleted file mode 100644
index 
6908e49f06801808b826d3a01f88132cf1b2f57c..0

[PATCH 01/18] hw/s390x/ipl: Provide more memory to the s390-ccw.img firmware

2024-09-26 Thread jrossi
From: Jared Rossi 

We are going to link the SLOF libc into the s390-ccw.img, and this
libc needs more memory for providing space for malloc() and friends.
Thus bump the memory size that we reserve for the bios to 3 MiB
instead of only 2 MiB. While we're at it, add a proper check that
there is really enough memory assigned to the machine before blindly
using it.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 

---
 hw/s390x/ipl.c | 10 +-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index dd71689642..c33a044d1d 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -45,6 +45,7 @@
 #define INITRD_PARM_START   0x010408UL
 #define PARMFILE_START  0x001000UL
 #define ZIPL_IMAGE_START0x009000UL
+#define BIOS_MAX_SIZE   0x30UL
 #define IPL_PSW_MASK(PSW_MASK_32 | PSW_MASK_64)
 
 static bool iplb_extended_needed(void *opaque)
@@ -144,7 +145,14 @@ static void s390_ipl_realize(DeviceState *dev, Error 
**errp)
  * even if an external kernel has been defined.
  */
 if (!ipl->kernel || ipl->enforce_bios) {
-uint64_t fwbase = (MIN(ms->ram_size, 0x8000U) - 0x20) & 
~0xUL;
+uint64_t fwbase;
+
+if (ms->ram_size < BIOS_MAX_SIZE) {
+error_setg(errp, "not enough RAM to load the BIOS file");
+return;
+}
+
+fwbase = (MIN(ms->ram_size, 0x8000U) - BIOS_MAX_SIZE) & ~0xUL;
 
 bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->firmware);
 if (bios_filename == NULL) {
-- 
2.45.1




[PATCH 17/18] pc-bios/s390x: Enable multi-device boot loop

2024-09-26 Thread jrossi
From: Jared Rossi 

Allow attempts to boot from multiple IPL devices. If the first device fails to
IPL, select the pre-built IPLB for the next device in the boot order and attempt
to IPL from it. Continue this process until IPL is successful or there are no
devices left to try.

Signed-off-by: Jared Rossi 

---
 pc-bios/s390-ccw/iplb.h | 23 +
 pc-bios/s390-ccw/jump2ipl.c |  7 ++--
 pc-bios/s390-ccw/main.c | 68 +++--
 pc-bios/s390-ccw/netmain.c  |  3 +-
 4 files changed, 56 insertions(+), 45 deletions(-)

diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h
index 16643f5879..d58fe71974 100644
--- a/pc-bios/s390-ccw/iplb.h
+++ b/pc-bios/s390-ccw/iplb.h
@@ -17,6 +17,7 @@
 #endif
 
 #include 
+#include 
 
 extern QemuIplParameters qipl;
 extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
@@ -49,4 +50,26 @@ static inline bool set_iplb(IplParameterBlock *iplb)
 return manage_iplb(iplb, false);
 }
 
+/*
+ * The IPL started on the device, but failed in some way.  If the IPLB chain
+ * still has more devices left to try, use the next device in order.
+ */
+static inline bool load_next_iplb(void)
+{
+IplParameterBlock *next_iplb;
+
+if (qipl.chain_len < 1) {
+return false;
+}
+
+qipl.index++;
+next_iplb = (IplParameterBlock *) qipl.next_iplb;
+memcpy(&iplb, next_iplb, sizeof(IplParameterBlock));
+
+qipl.chain_len--;
+qipl.next_iplb = qipl.next_iplb + sizeof(IplParameterBlock);
+
+return true;
+}
+
 #endif /* IPLB_H */
diff --git a/pc-bios/s390-ccw/jump2ipl.c b/pc-bios/s390-ccw/jump2ipl.c
index bcf0e12f02..afe1888511 100644
--- a/pc-bios/s390-ccw/jump2ipl.c
+++ b/pc-bios/s390-ccw/jump2ipl.c
@@ -45,9 +45,10 @@ int jump_to_IPL_code(uint64_t address)
  */
 if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) {
 iplb.devno = qipl.index;
-if (!set_iplb(&iplb)) {
-panic("Failed to set IPLB");
-}
+}
+
+if (!set_iplb(&iplb)) {
+panic("Failed to set IPLB");
 }
 
 /*
diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
index d7c457e0ed..e38eedd83a 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -23,7 +23,7 @@ static SubChannelId blk_schid = { .one = 1 };
 static char loadparm_str[LOADPARM_LEN + 1];
 QemuIplParameters qipl;
 IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
-static bool have_iplb;
+bool have_iplb;
 static uint16_t cutype;
 LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */
 
@@ -55,6 +55,12 @@ void write_iplb_location(void)
 }
 }
 
+static void copy_qipl(void)
+{
+QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS;
+memcpy(&qipl, early_qipl, sizeof(QemuIplParameters));
+}
+
 unsigned int get_loadparm_index(void)
 {
 return atoi(loadparm_str);
@@ -152,6 +158,7 @@ static void menu_setup(void)
 
 /* If loadparm was set to any other value, then do not enable menu */
 if (memcmp(loadparm_str, LOADPARM_EMPTY, LOADPARM_LEN) != 0) {
+menu_set_parms(qipl.qipl_flags && ~BOOT_MENU_FLAG_MASK, 0);
 return;
 }
 
@@ -183,7 +190,10 @@ static void css_setup(void)
 static void boot_setup(void)
 {
 char lpmsg[] = "LOADPARM=[]\n";
-have_iplb = store_iplb(&iplb);
+
+if (!have_iplb) {
+have_iplb = store_iplb(&iplb);
+}
 
 if (memcmp(iplb.loadparm, "", LOADPARM_LEN) != 0) {
 ebcdic_to_ascii((char *) iplb.loadparm, loadparm_str, LOADPARM_LEN);
@@ -191,6 +201,10 @@ static void boot_setup(void)
 sclp_get_loadparm_ascii(loadparm_str);
 }
 
+if (have_iplb) {
+menu_setup();
+}
+
 memcpy(lpmsg + 10, loadparm_str, 8);
 puts(lpmsg);
 
@@ -208,6 +222,7 @@ static bool find_boot_device(void)
 
 switch (iplb.pbt) {
 case S390_IPL_TYPE_CCW:
+vdev->scsi_device_selected = false;
 debug_print_int("device no. ", iplb.ccw.devno);
 blk_schid.ssid = iplb.ccw.ssid & 0x3;
 debug_print_int("ssid ", blk_schid.ssid);
@@ -231,15 +246,8 @@ static bool find_boot_device(void)
 static int virtio_setup(void)
 {
 VDev *vdev = virtio_get_device();
-QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS;
 int ret;
 
-memcpy(&qipl, early_qipl, sizeof(QemuIplParameters));
-
-if (have_iplb) {
-menu_setup();
-}
-
 switch (vdev->senseid.cu_model) {
 case VIRTIO_ID_NET:
 puts("Network boot device detected");
@@ -281,41 +289,19 @@ static void ipl_boot_device(void)
 }
 }
 
-/*
- * No boot device has been specified, so we have to scan through the
- * channels to find one.
- */
-static void probe_boot_device(void)
-{
-int ssid, sch_no, ret;
-
-for (ssid = 0; ssid < 0x3; ssid++) {
-blk_schid.ssid = ssid;
-for (sch_no = 0; sch_no < 0x1; sch_no++) {
-ret = is_dev_possibly_bootable(-1, sch_no);
-if (ret < 0) {
-break;
-

[PATCH 07/18] pc-bios/s390-ccw: Remove panics from ISO IPL path

2024-09-26 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from IPL ISO El Torito specific functions so that error
recovery may be possible in the future.

Functions that would previously panic now provide a return code.

Signed-off-by: Jared Rossi 

---
 pc-bios/s390-ccw/bootmap.h  | 17 +++---
 pc-bios/s390-ccw/s390-ccw.h |  1 +
 pc-bios/s390-ccw/bootmap.c  | 64 -
 3 files changed, 55 insertions(+), 27 deletions(-)

diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
index bbe2c132aa..cb5346829b 100644
--- a/pc-bios/s390-ccw/bootmap.h
+++ b/pc-bios/s390-ccw/bootmap.h
@@ -385,17 +385,24 @@ static inline uint32_t iso_733_to_u32(uint64_t x)
 
 #define ISO_PRIMARY_VD_SECTOR 16
 
-static inline void read_iso_sector(uint32_t block_offset, void *buf,
+static inline int read_iso_sector(uint32_t block_offset, void *buf,
const char *errmsg)
 {
-IPL_assert(virtio_read_many(block_offset, buf, 1) == 0, errmsg);
+if (virtio_read(block_offset, buf)) {
+puts(errmsg);
+return 1;
+}
+return 0;
 }
 
-static inline void read_iso_boot_image(uint32_t block_offset, void *load_addr,
+static inline int read_iso_boot_image(uint32_t block_offset, void *load_addr,
uint32_t blks_to_load)
 {
-IPL_assert(virtio_read_many(block_offset, load_addr, blks_to_load) == 0,
-   "Failed to read boot image!");
+if (!virtio_read_many(block_offset, load_addr, blks_to_load)) {
+puts("Failed to read boot image!");
+return 1;
+}
+return 0;
 }
 
 #define ISO9660_MAX_DIR_DEPTH 8
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 0ed7eb8ade..cbd92f3671 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -30,6 +30,7 @@ typedef unsigned long long u64;
 #define EIO 1
 #define EBUSY   2
 #define ENODEV  3
+#define EINVAL  4
 
 #ifndef MIN
 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 414c3f1b47..31cf0f6d97 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -678,8 +678,10 @@ static bool is_iso_bc_entry_compatible(IsoBcSection *s)
 if (s->unused || !s->sector_count) {
 return false;
 }
-read_iso_sector(bswap32(s->load_rba), magic_sec,
-"Failed to read image sector 0");
+if (read_iso_sector(bswap32(s->load_rba), magic_sec,
+"Failed to read image sector 0")) {
+return false;
+}
 
 /* Checking bytes 8 - 32 for S390 Linux magic */
 return !memcmp(magic_sec + 8, linux_s390_magic, 24);
@@ -706,14 +708,18 @@ static inline uint32_t iso_get_file_size(uint32_t 
load_rba)
 sec_offset[0] = 0;
 
 while (level >= 0) {
-IPL_assert(sec_offset[level] <= ISO_SECTOR_SIZE,
-   "Directory tree structure violation");
+if (sec_offset[level] > ISO_SECTOR_SIZE) {
+puts("Directory tree structure violation");
+return -EIO;
+}
 
 cur_record = (IsoDirHdr *)(temp + sec_offset[level]);
 
 if (sec_offset[level] == 0) {
-read_iso_sector(sec_loc[level], temp,
-"Failed to read ISO directory");
+if (virtio_read(sec_loc[level], temp)) {
+puts("Failed to read ISO directory");
+return -EIO;
+}
 if (dir_rem[level] == 0) {
 /* Skip self and parent records */
 dir_rem[level] = iso_733_to_u32(cur_record->data_len) -
@@ -784,9 +790,11 @@ static void load_iso_bc_entry(IsoBcSection *load)
 puts("ISO boot image size could not be verified");
 }
 
-read_iso_boot_image(bswap32(s.load_rba),
+if (read_iso_boot_image(bswap32(s.load_rba),
 (void *)((uint64_t)bswap16(s.load_segment)),
-blks_to_load);
+blks_to_load)) {
+return;
+}
 
 jump_to_low_kernel();
 }
@@ -809,17 +817,18 @@ static uint32_t find_iso_bc(void)
 return bswap32(et->bc_offset);
 }
 }
-read_iso_sector(block_num++, sec,
-"Failed to read ISO volume descriptor");
+if (read_iso_sector(block_num++, sec,
+"Failed to read ISO volume descriptor")) {
+return 0;
+}
 }
 
 return 0;
 }
 
-static IsoBcSection *find_iso_bc_entry(void)
+static IsoBcSection *find_iso_bc_entry(uint32_t offset)
 {
 IsoBcEntry *e = (IsoBcEntry *)sec;
-uint32_t offset = find_iso_bc();
 int i;
 unsigned int loadparm = get_loadparm_index();
 
@@ -827,11 +836,12 @@ static IsoBcSection *find_iso_bc_entry(void)
 return NULL;
 }
 
-read_iso_sector(offset, sec, "Failed to read El Torito boot catalog");
+if (read_iso_sector(offset, sec, "Failed to read El Torito boot catalog")) 
{

[PATCH 18/18] docs/system: Update documentation for s390x IPL

2024-09-26 Thread jrossi
From: Jared Rossi 

Update docs to show that s390x PC BIOS can support more than one boot device.

Signed-off-by: Jared Rossi 

---
 docs/system/bootindex.rst | 7 ---
 docs/system/s390x/bootdevices.rst | 9 ++---
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/docs/system/bootindex.rst b/docs/system/bootindex.rst
index 8b057f812f..142ae1a0a2 100644
--- a/docs/system/bootindex.rst
+++ b/docs/system/bootindex.rst
@@ -49,10 +49,11 @@ Limitations
 ---
 
 Some firmware has limitations on which devices can be considered for
-booting.  For instance, the PC BIOS boot specification allows only one
-disk to be bootable.  If boot from disk fails for some reason, the BIOS
+booting.  For instance, the x86 PC BIOS boot specification allows only one
+disk to be bootable.  If boot from disk fails for some reason, the x86 BIOS
 won't retry booting from other disk.  It can still try to boot from
-floppy or net, though.
+floppy or net, though. In the case of s390x PC BIOS, the BIOS will try up to
+8 total devices, any number of which may be disks.
 
 Sometimes, firmware cannot map the device path QEMU wants firmware to
 boot from to a boot method.  It doesn't happen for devices the firmware
diff --git a/docs/system/s390x/bootdevices.rst 
b/docs/system/s390x/bootdevices.rst
index c97efb8fc0..1a1a764c1c 100644
--- a/docs/system/s390x/bootdevices.rst
+++ b/docs/system/s390x/bootdevices.rst
@@ -6,9 +6,7 @@ Booting with bootindex parameter
 
 For classical mainframe guests (i.e. LPAR or z/VM installations), you always
 have to explicitly specify the disk where you want to boot from (or "IPL" from,
-in s390x-speak -- IPL means "Initial Program Load"). In particular, there can
-also be only one boot device according to the architecture specification, thus
-specifying multiple boot devices is not possible (yet).
+in s390x-speak -- IPL means "Initial Program Load").
 
 So for booting an s390x guest in QEMU, you should always mark the
 device where you want to boot from with the ``bootindex`` property, for
@@ -17,6 +15,11 @@ example::
  qemu-system-s390x -drive if=none,id=dr1,file=guest.qcow2 \
-device virtio-blk,drive=dr1,bootindex=1
 
+Multiple devices may have a bootindex. The lowest bootindex is assigned to the
+device to IPL first.  If the IPL fails for the first, the device with the 
second
+lowest bootindex will be tried and so on until IPL is successful or there are 
no
+remaining boot devices to try.
+
 For booting from a CD-ROM ISO image (which needs to include El-Torito boot
 information in order to be bootable), it is recommended to specify a 
``scsi-cd``
 device, for example like this::
-- 
2.45.1




[PATCH v4 01/19] hw/s390x/ipl: Provide more memory to the s390-ccw.img firmware

2024-10-16 Thread jrossi
From: Jared Rossi 

We are going to link the SLOF libc into the s390-ccw.img, and this
libc needs more memory for providing space for malloc() and friends.
Thus bump the memory size that we reserve for the bios to 3 MiB
instead of only 2 MiB. While we're at it, add a proper check that
there is really enough memory assigned to the machine before blindly
using it.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 
---
 hw/s390x/ipl.c | 10 +-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 5ab7433908..5f60977ceb 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -45,6 +45,7 @@
 #define INITRD_PARM_START   0x010408UL
 #define PARMFILE_START  0x001000UL
 #define ZIPL_IMAGE_START0x009000UL
+#define BIOS_MAX_SIZE   0x30UL
 #define IPL_PSW_MASK(PSW_MASK_32 | PSW_MASK_64)
 
 static bool iplb_extended_needed(void *opaque)
@@ -144,7 +145,14 @@ static void s390_ipl_realize(DeviceState *dev, Error 
**errp)
  * even if an external kernel has been defined.
  */
 if (!ipl->kernel || ipl->enforce_bios) {
-uint64_t fwbase = (MIN(ms->ram_size, 0x8000U) - 0x20) & 
~0xUL;
+uint64_t fwbase;
+
+if (ms->ram_size < BIOS_MAX_SIZE) {
+error_setg(errp, "not enough RAM to load the BIOS file");
+return;
+}
+
+fwbase = (MIN(ms->ram_size, 0x8000U) - BIOS_MAX_SIZE) & ~0xUL;
 
 bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->firmware);
 if (bios_filename == NULL) {
-- 
2.45.1




[PATCH v4 05/19] pc-bios/s390-ccw: Merge netboot.mak into the main Makefile

2024-10-16 Thread jrossi
From: Jared Rossi 

Now that the netboot code has been merged into the main s390-ccw.img,
it also does not make sense to keep the build rules in a separate
file. Thus let's merge netboot.mak into the main Makefile.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/netboot.mak |  45 -
 pc-bios/s390-ccw/Makefile|  47 ++-
 pc-bios/s390-netboot.img | Bin 67232 -> 0 bytes
 3 files changed, 46 insertions(+), 46 deletions(-)
 delete mode 100644 pc-bios/s390-ccw/netboot.mak
 delete mode 100644 pc-bios/s390-netboot.img

diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak
deleted file mode 100644
index 0a24257ff4..00
--- a/pc-bios/s390-ccw/netboot.mak
+++ /dev/null
@@ -1,45 +0,0 @@
-
-# libc files:
-
-LIBC_CFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
- -MMD -MP -MT $@ -MF $(@:%.o=%.d)
-
-CTYPE_OBJS = isdigit.o isxdigit.o toupper.o
-%.o : $(SLOF_DIR)/lib/libc/ctype/%.c
-   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
-
-STRING_OBJS = strcat.o strchr.o strrchr.o strcpy.o strlen.o strncpy.o \
- strcmp.o strncmp.o strcasecmp.o strncasecmp.o strstr.o \
- memset.o memcpy.o memmove.o memcmp.o
-%.o : $(SLOF_DIR)/lib/libc/string/%.c
-   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
-
-STDLIB_OBJS = atoi.o atol.o strtoul.o strtol.o rand.o malloc.o free.o
-%.o : $(SLOF_DIR)/lib/libc/stdlib/%.c
-   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
-
-STDIO_OBJS = sprintf.o snprintf.o vfprintf.o vsnprintf.o vsprintf.o fprintf.o \
-printf.o putc.o puts.o putchar.o stdchnls.o fileno.o
-%.o : $(SLOF_DIR)/lib/libc/stdio/%.c
-   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
-
-sbrk.o: $(SLOF_DIR)/slof/sbrk.c
-   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
-
-LIBCOBJS := $(STRING_OBJS) $(CTYPE_OBJS) $(STDLIB_OBJS) $(STDIO_OBJS) sbrk.o
-
-libc.a: $(LIBCOBJS)
-   $(call quiet-command,$(AR) -rc $@ $^,Creating static library)
-
-# libnet files:
-
-LIBNETOBJS := args.o dhcp.o dns.o icmpv6.o ipv6.o tcp.o udp.o bootp.o \
- dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o pxelinux.o
-LIBNETCFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
-  -DDHCPARCH=0x1F -MMD -MP -MT $@ -MF $(@:%.o=%.d)
-
-%.o : $(SLOF_DIR)/lib/libnet/%.c
-   $(call quiet-command,$(CC) $(LIBNETCFLAGS) -c -o $@ $<,Compiling)
-
-libnet.a: $(LIBNETOBJS)
-   $(call quiet-command,$(AR) -rc $@ $^,Creating static library)
diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
index cf6859823a..27cbb354af 100644
--- a/pc-bios/s390-ccw/Makefile
+++ b/pc-bios/s390-ccw/Makefile
@@ -61,7 +61,52 @@ config-cc.mak: Makefile
$(call cc-option,-march=z900,-march=z10)) 3> config-cc.mak
 -include config-cc.mak
 
-include $(SRC_PATH)/netboot.mak
+# libc files:
+
+LIBC_CFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
+ -MMD -MP -MT $@ -MF $(@:%.o=%.d)
+
+CTYPE_OBJS = isdigit.o isxdigit.o toupper.o
+%.o : $(SLOF_DIR)/lib/libc/ctype/%.c
+   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
+
+STRING_OBJS = strcat.o strchr.o strrchr.o strcpy.o strlen.o strncpy.o \
+ strcmp.o strncmp.o strcasecmp.o strncasecmp.o strstr.o \
+ memset.o memcpy.o memmove.o memcmp.o
+%.o : $(SLOF_DIR)/lib/libc/string/%.c
+   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
+
+STDLIB_OBJS = atoi.o atol.o strtoul.o strtol.o rand.o malloc.o free.o
+%.o : $(SLOF_DIR)/lib/libc/stdlib/%.c
+   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
+
+STDIO_OBJS = sprintf.o snprintf.o vfprintf.o vsnprintf.o vsprintf.o fprintf.o \
+printf.o putc.o puts.o putchar.o stdchnls.o fileno.o
+%.o : $(SLOF_DIR)/lib/libc/stdio/%.c
+   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
+
+sbrk.o: $(SLOF_DIR)/slof/sbrk.c
+   $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
+
+LIBCOBJS := $(STRING_OBJS) $(CTYPE_OBJS) $(STDLIB_OBJS) $(STDIO_OBJS) sbrk.o
+
+libc.a: $(LIBCOBJS)
+   $(call quiet-command,$(AR) -rc $@ $^,Creating static library)
+
+# libnet files:
+
+LIBNETOBJS := args.o dhcp.o dns.o icmpv6.o ipv6.o tcp.o udp.o bootp.o \
+ dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o pxelinux.o
+LIBNETCFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
+  -DDHCPARCH=0x1F -MMD -MP -MT $@ -MF $(@:%.o=%.d)
+
+%.o : $(SLOF_DIR)/lib/libnet/%.c
+   $(call quiet-command,$(CC) $(LIBNETCFLAGS) -c -o $@ $<,Compiling)
+
+libnet.a: $(LIBNETOBJS)
+   $(call quiet-command,$(AR) -rc $@ $^,Creating static library)
+
+# Main targets:
 
 build-all: s390-ccw.img
 
diff --git a/pc-bios/s390-netboot.img b/pc-bios/s390-netboot.img
deleted file mode 100644
index 
6908e49f06801808b826d3a01f88132cf1b2f57c..00

[PATCH v4 13/19] include/hw/s390x: Add include files for common IPL structs

2024-10-16 Thread jrossi
From: Jared Rossi 

Currently, structures defined in both hw/s390x/ipl.h and pc-bios/s390-ccw/iplb.h
must be kept in sync, which is prone to error. Instead, create a new directory
at include/hw/s390x/ipl/ to contain the definitions that must be shared.

Signed-off-by: Jared Rossi 
Reviewed-by: Thomas Huth 
---
 hw/s390x/ipl.h  | 104 +-
 include/hw/s390x/ipl/qipl.h | 123 
 pc-bios/s390-ccw/iplb.h |  83 ++--
 pc-bios/s390-ccw/Makefile   |   2 +-
 4 files changed, 129 insertions(+), 183 deletions(-)
 create mode 100644 include/hw/s390x/ipl/qipl.h

diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index b2105b616a..fa394c339d 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -16,95 +16,11 @@
 #include "cpu.h"
 #include "exec/address-spaces.h"
 #include "hw/qdev-core.h"
+#include "hw/s390x/ipl/qipl.h"
 #include "qom/object.h"
 
-struct IPLBlockPVComp {
-uint64_t tweak_pref;
-uint64_t addr;
-uint64_t size;
-} QEMU_PACKED;
-typedef struct IPLBlockPVComp IPLBlockPVComp;
-
-struct IPLBlockPV {
-uint8_t  reserved18[87];/* 0x18 */
-uint8_t  version;   /* 0x6f */
-uint32_t reserved70;/* 0x70 */
-uint32_t num_comp;  /* 0x74 */
-uint64_t pv_header_addr;/* 0x78 */
-uint64_t pv_header_len; /* 0x80 */
-struct IPLBlockPVComp components[0];
-} QEMU_PACKED;
-typedef struct IPLBlockPV IPLBlockPV;
-
-struct IplBlockCcw {
-uint8_t  reserved0[85];
-uint8_t  ssid;
-uint16_t devno;
-uint8_t  vm_flags;
-uint8_t  reserved3[3];
-uint32_t vm_parm_len;
-uint8_t  nss_name[8];
-uint8_t  vm_parm[64];
-uint8_t  reserved4[8];
-} QEMU_PACKED;
-typedef struct IplBlockCcw IplBlockCcw;
-
-struct IplBlockFcp {
-uint8_t  reserved1[305 - 1];
-uint8_t  opt;
-uint8_t  reserved2[3];
-uint16_t reserved3;
-uint16_t devno;
-uint8_t  reserved4[4];
-uint64_t wwpn;
-uint64_t lun;
-uint32_t bootprog;
-uint8_t  reserved5[12];
-uint64_t br_lba;
-uint32_t scp_data_len;
-uint8_t  reserved6[260];
-uint8_t  scp_data[0];
-} QEMU_PACKED;
-typedef struct IplBlockFcp IplBlockFcp;
-
-struct IplBlockQemuScsi {
-uint32_t lun;
-uint16_t target;
-uint16_t channel;
-uint8_t  reserved0[77];
-uint8_t  ssid;
-uint16_t devno;
-} QEMU_PACKED;
-typedef struct IplBlockQemuScsi IplBlockQemuScsi;
-
 #define DIAG308_FLAGS_LP_VALID 0x80
 
-union IplParameterBlock {
-struct {
-uint32_t len;
-uint8_t  reserved0[3];
-uint8_t  version;
-uint32_t blk0_len;
-uint8_t  pbt;
-uint8_t  flags;
-uint16_t reserved01;
-uint8_t  loadparm[8];
-union {
-IplBlockCcw ccw;
-IplBlockFcp fcp;
-IPLBlockPV pv;
-IplBlockQemuScsi scsi;
-};
-} QEMU_PACKED;
-struct {
-uint8_t  reserved1[110];
-uint16_t devno;
-uint8_t  reserved2[88];
-uint8_t  reserved_ext[4096 - 200];
-} QEMU_PACKED;
-} QEMU_PACKED;
-typedef union IplParameterBlock IplParameterBlock;
-
 int s390_ipl_set_loadparm(uint8_t *loadparm);
 void s390_ipl_update_diag308(IplParameterBlock *iplb);
 int s390_ipl_prepare_pv_header(Error **errp);
@@ -131,24 +47,6 @@ void s390_ipl_clear_reset_request(void);
 #define QIPL_FLAG_BM_OPTS_CMD   0x80
 #define QIPL_FLAG_BM_OPTS_ZIPL  0x40
 
-/*
- * The QEMU IPL Parameters will be stored at absolute address
- * 204 (0xcc) which means it is 32-bit word aligned but not
- * double-word aligned. Placement of 64-bit data fields in this
- * area must account for their alignment needs.
- * The total size of the struct must never exceed 28 bytes.
- * This definition must be kept in sync with the definition
- * in pc-bios/s390-ccw/iplb.h.
- */
-struct QemuIplParameters {
-uint8_t  qipl_flags;
-uint8_t  reserved1[3];
-uint64_t reserved2;
-uint32_t boot_menu_timeout;
-uint8_t  reserved3[12];
-} QEMU_PACKED;
-typedef struct QemuIplParameters QemuIplParameters;
-
 #define TYPE_S390_IPL "s390-ipl"
 OBJECT_DECLARE_SIMPLE_TYPE(S390IPLState, S390_IPL)
 
diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
new file mode 100644
index 00..0ef04af027
--- /dev/null
+++ b/include/hw/s390x/ipl/qipl.h
@@ -0,0 +1,123 @@
+/*
+ * S/390 boot structures
+ *
+ * Copyright 2024 IBM Corp.
+ * Author(s): Jared Rossi 
+ *
+ * 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.
+ */
+
+#ifndef S390X_QIPL_H
+#define S390X_QIPL_H
+
+/* Boot Menu flags */
+#define QIPL_FLAG_BM_OPTS_CMD   0x80
+#define QIPL_FLAG_BM_OPTS_ZIPL  0x40
+
+#define QIPL_ADDRESS  0xcc
+#define LOADPARM_LEN8
+
+/*
+ * The QEMU IPL Parameters will be stored at absolute address
+ * 204 (0xcc) which means it is 32-bit word aligned but not
+ * double-word aligned. Placement 

[PATCH v4 06/19] docs/system/s390x/bootdevices: Update the documentation about network booting

2024-10-16 Thread jrossi
From: Jared Rossi 

Remove the information about the separate s390-netboot.img from
the documentation.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 
---
 docs/system/s390x/bootdevices.rst | 20 +++-
 1 file changed, 7 insertions(+), 13 deletions(-)

diff --git a/docs/system/s390x/bootdevices.rst 
b/docs/system/s390x/bootdevices.rst
index 1a7a18b43b..c97efb8fc0 100644
--- a/docs/system/s390x/bootdevices.rst
+++ b/docs/system/s390x/bootdevices.rst
@@ -82,23 +82,17 @@ Note that ``0`` can be used to boot the default entry.
 Booting from a network device
 -
 
-Beside the normal guest firmware (which is loaded from the file 
``s390-ccw.img``
-in the data directory of QEMU, or via the ``-bios`` option), QEMU ships with
-a small TFTP network bootloader firmware for virtio-net-ccw devices, too. This
-firmware is loaded from a file called ``s390-netboot.img`` in the QEMU data
-directory. In case you want to load it from a different filename instead,
-you can specify it via the ``-global s390-ipl.netboot_fw=filename``
-command line option.
-
-The ``bootindex`` property is especially important for booting via the network.
-If you don't specify the ``bootindex`` property here, the network bootloader
-firmware code won't get loaded into the guest memory so that the network boot
-will fail. For a successful network boot, try something like this::
+The firmware that ships with QEMU includes a small TFTP network bootloader
+for virtio-net-ccw devices.  The ``bootindex`` property is especially
+important for booting via the network. If you don't specify the ``bootindex``
+property here, the network bootloader won't be taken into consideration and
+the network boot will fail. For a successful network boot, try something
+like this::
 
  qemu-system-s390x -netdev user,id=n1,tftp=...,bootfile=... \
-device virtio-net-ccw,netdev=n1,bootindex=1
 
-The network bootloader firmware also has basic support for pxelinux.cfg-style
+The network bootloader also has basic support for pxelinux.cfg-style
 configuration files. See the `PXELINUX Configuration page
 `__
 for details how to set up the configuration file on your TFTP server.
-- 
2.45.1




[PATCH v4 04/19] hw/s390x: Remove the possibility to load the s390-netboot.img binary

2024-10-16 Thread jrossi
From: Jared Rossi 

Since the netboot code has now been merged into the main s390-ccw.img
binary, we don't need the separate s390-netboot.img anymore. Remove
it and the code that was responsible for loading it.

Co-authored by: Thomas Huth 
Signed-off-by: Jared Rossi 
---
 hw/s390x/ipl.h | 12 +++--
 hw/s390x/ipl.c | 55 --
 hw/s390x/s390-virtio-ccw.c | 10 ++-
 pc-bios/meson.build|  1 -
 4 files changed, 6 insertions(+), 72 deletions(-)

diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index 57cd125769..b2105b616a 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -134,11 +134,8 @@ void s390_ipl_clear_reset_request(void);
 /*
  * The QEMU IPL Parameters will be stored at absolute address
  * 204 (0xcc) which means it is 32-bit word aligned but not
- * double-word aligned.
- * Placement of data fields in this area must account for
- * their alignment needs. E.g., netboot_start_address must
- * have an offset of 4 + n * 8 bytes within the struct in order
- * to keep it double-word aligned.
+ * double-word aligned. Placement of 64-bit data fields in this
+ * area must account for their alignment needs.
  * The total size of the struct must never exceed 28 bytes.
  * This definition must be kept in sync with the definition
  * in pc-bios/s390-ccw/iplb.h.
@@ -146,9 +143,9 @@ void s390_ipl_clear_reset_request(void);
 struct QemuIplParameters {
 uint8_t  qipl_flags;
 uint8_t  reserved1[3];
-uint64_t netboot_start_addr;
+uint64_t reserved2;
 uint32_t boot_menu_timeout;
-uint8_t  reserved2[12];
+uint8_t  reserved3[12];
 } QEMU_PACKED;
 typedef struct QemuIplParameters QemuIplParameters;
 
@@ -178,7 +175,6 @@ struct S390IPLState {
 char *initrd;
 char *cmdline;
 char *firmware;
-char *netboot_fw;
 uint8_t cssid;
 uint8_t ssid;
 uint16_t devno;
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 5f60977ceb..8c490eeb52 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -288,7 +288,6 @@ static Property s390_ipl_properties[] = {
 DEFINE_PROP_STRING("initrd", S390IPLState, initrd),
 DEFINE_PROP_STRING("cmdline", S390IPLState, cmdline),
 DEFINE_PROP_STRING("firmware", S390IPLState, firmware),
-DEFINE_PROP_STRING("netboot_fw", S390IPLState, netboot_fw),
 DEFINE_PROP_BOOL("enforce_bios", S390IPLState, enforce_bios, false),
 DEFINE_PROP_BOOL("iplbext_migration", S390IPLState, iplbext_migration,
  true),
@@ -480,56 +479,6 @@ int s390_ipl_set_loadparm(uint8_t *loadparm)
 return -1;
 }
 
-static int load_netboot_image(Error **errp)
-{
-MachineState *ms = MACHINE(qdev_get_machine());
-S390IPLState *ipl = get_ipl_device();
-char *netboot_filename;
-MemoryRegion *sysmem =  get_system_memory();
-MemoryRegion *mr = NULL;
-void *ram_ptr = NULL;
-int img_size = -1;
-
-mr = memory_region_find(sysmem, 0, 1).mr;
-if (!mr) {
-error_setg(errp, "Failed to find memory region at address 0");
-return -1;
-}
-
-ram_ptr = memory_region_get_ram_ptr(mr);
-if (!ram_ptr) {
-error_setg(errp, "No RAM found");
-goto unref_mr;
-}
-
-netboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->netboot_fw);
-if (netboot_filename == NULL) {
-error_setg(errp, "Could not find network bootloader '%s'",
-   ipl->netboot_fw);
-goto unref_mr;
-}
-
-img_size = load_elf_ram(netboot_filename, NULL, NULL, NULL,
-&ipl->start_addr,
-NULL, NULL, NULL, 1, EM_S390, 0, 0, NULL,
-false);
-
-if (img_size < 0) {
-img_size = load_image_size(netboot_filename, ram_ptr, ms->ram_size);
-ipl->start_addr = KERN_IMAGE_START;
-}
-
-if (img_size < 0) {
-error_setg(errp, "Failed to load network bootloader");
-}
-
-g_free(netboot_filename);
-
-unref_mr:
-memory_region_unref(mr);
-return img_size;
-}
-
 static bool is_virtio_ccw_device_of_type(IplParameterBlock *iplb,
  int virtio_id)
 {
@@ -754,10 +703,6 @@ void s390_ipl_prepare_cpu(S390CPU *cpu)
 ipl->iplb_valid = s390_gen_initial_iplb(ipl);
 }
 }
-if (ipl->netboot) {
-load_netboot_image(&error_fatal);
-ipl->qipl.netboot_start_addr = cpu_to_be64(ipl->start_addr);
-}
 s390_ipl_set_boot_menu(ipl);
 s390_ipl_prepare_qipl(cpu);
 }
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index 5aa8d207a3..529e53f308 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -197,11 +197,10 @@ static void s390_memory_init(MemoryRegion *ram)
 static void s390_init_ipl_dev(const char *kernel_filename,
   const char *kernel_cmdline,
   const char *initrd_filename, const char 
*firmware,
-  const char *netb

[PATCH v4 16/19] s390x: Rebuild IPLB for SCSI device directly from DIAG308

2024-10-16 Thread jrossi
From: Jared Rossi 

Because virtio-scsi type devices use a non-architected IPLB pbt code they cannot
be set and stored normally. Instead, the IPLB must be rebuilt during re-ipl.

As s390x does not natively support multiple boot devices, the devno field is
used to store the position in the boot order for the device.

Handling the rebuild as part of DIAG308 removes the need to check the devices
for invalid IPLBs later in the IPL.

Signed-off-by: Jared Rossi 
---
 hw/s390x/ipl.h  | 11 --
 include/hw/s390x/ipl/qipl.h |  3 +-
 hw/s390x/ipl.c  | 75 +++--
 pc-bios/s390-ccw/jump2ipl.c | 11 --
 target/s390x/diag.c |  9 -
 5 files changed, 39 insertions(+), 70 deletions(-)

diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index 54eb48fd6e..aead1d6ae5 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -24,6 +24,7 @@
 
 void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp);
 void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp);
+void s390_rebuild_iplb(uint16_t index, IplParameterBlock *iplb);
 void s390_ipl_update_diag308(IplParameterBlock *iplb);
 int s390_ipl_prepare_pv_header(Error **errp);
 int s390_ipl_pv_unpack(void);
@@ -65,7 +66,8 @@ struct S390IPLState {
 bool enforce_bios;
 bool iplb_valid;
 bool iplb_valid_pv;
-bool netboot;
+bool rebuilt_iplb;
+uint16_t iplb_index;
 /* reset related properties don't have to be migrated or reset */
 enum s390_reset reset_type;
 int reset_cpu_index;
@@ -172,11 +174,14 @@ static inline bool iplb_valid_pv(IplParameterBlock *iplb)
 
 static inline bool iplb_valid(IplParameterBlock *iplb)
 {
+uint32_t len = be32_to_cpu(iplb->len);
+
 switch (iplb->pbt) {
 case S390_IPL_TYPE_FCP:
-return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_FCP_LEN;
+return len >= S390_IPLB_MIN_FCP_LEN;
 case S390_IPL_TYPE_CCW:
-return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_CCW_LEN;
+return (len >= S390_IPLB_MIN_CCW_LEN);
+case S390_IPL_TYPE_QEMU_SCSI:
 default:
 return false;
 }
diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
index e56b181298..451f3b1684 100644
--- a/include/hw/s390x/ipl/qipl.h
+++ b/include/hw/s390x/ipl/qipl.h
@@ -28,7 +28,8 @@
  */
 struct QemuIplParameters {
 uint8_t  qipl_flags;
-uint8_t  reserved1[3];
+uint8_t  index;
+uint8_t  reserved1[2];
 uint64_t reserved2;
 uint32_t boot_menu_timeout;
 uint8_t  reserved3[2];
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index aec79f41a8..50fde05b67 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -448,7 +448,6 @@ void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t 
*ebcdic_lp)
 
 static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
 {
-S390IPLState *ipl = get_ipl_device();
 CcwDevice *ccw_dev = NULL;
 SCSIDevice *sd;
 int devtype;
@@ -481,9 +480,6 @@ static bool s390_build_iplb(DeviceState *dev_st, 
IplParameterBlock *iplb)
 iplb->ccw.ssid = ccw_dev->sch->ssid & 3;
 break;
 case CCW_DEVTYPE_VIRTIO_NET:
-/* The S390IPLState netboot is true if ANY IPLB may use netboot */
-ipl->netboot = true;
-/* Fall through to CCW_DEVTYPE_VIRTIO case */
 case CCW_DEVTYPE_VIRTIO:
 iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
 iplb->blk0_len =
@@ -508,6 +504,17 @@ static bool s390_build_iplb(DeviceState *dev_st, 
IplParameterBlock *iplb)
 return false;
 }
 
+
+void s390_rebuild_iplb(uint16_t dev_index, IplParameterBlock *iplb)
+{
+S390IPLState *ipl = get_ipl_device();
+uint16_t index;
+index = ipl->rebuilt_iplb ? ipl->iplb_index : dev_index;
+
+ipl->rebuilt_iplb = s390_build_iplb(get_boot_device(index), iplb);
+ipl->iplb_index = index;
+}
+
 static bool s390_init_all_iplbs(S390IPLState *ipl)
 {
 int iplb_num = 0;
@@ -558,44 +565,6 @@ static bool s390_init_all_iplbs(S390IPLState *ipl)
 return iplb_num;
 }
 
-static bool is_virtio_ccw_device_of_type(IplParameterBlock *iplb,
- int virtio_id)
-{
-uint8_t cssid;
-uint8_t ssid;
-uint16_t devno;
-uint16_t schid;
-SubchDev *sch = NULL;
-
-if (iplb->pbt != S390_IPL_TYPE_CCW) {
-return false;
-}
-
-devno = be16_to_cpu(iplb->ccw.devno);
-ssid = iplb->ccw.ssid & 3;
-
-for (schid = 0; schid < MAX_SCHID; schid++) {
-for (cssid = 0; cssid < MAX_CSSID; cssid++) {
-sch = css_find_subch(1, cssid, ssid, schid);
-
-if (sch && sch->devno == devno) {
-return sch->id.cu_model == virtio_id;
-}
-}
-}
-return false;
-}
-
-static bool is_virtio_net_device(IplParameterBlock *iplb)
-{
-return is_virtio_ccw_device_of_type(iplb, VIRTIO_ID_NET);
-}
-
-static bool is_virtio_scsi_device(IplParameterBlock *iplb)
-{
-return is_virtio_ccw_device_of_type(ip

[PATCH v4 07/19] pc-bios/s390-ccw: Remove panics from ISO IPL path

2024-10-16 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from IPL ISO El Torito specific functions so that error
recovery may be possible in the future.

Functions that would previously panic now provide a return code.

Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/bootmap.h  | 15 +++
 pc-bios/s390-ccw/s390-ccw.h |  1 +
 pc-bios/s390-ccw/bootmap.c  | 82 -
 3 files changed, 61 insertions(+), 37 deletions(-)

diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
index 4a7d8a91f1..09f4e6fb40 100644
--- a/pc-bios/s390-ccw/bootmap.h
+++ b/pc-bios/s390-ccw/bootmap.h
@@ -385,17 +385,14 @@ static inline uint32_t iso_733_to_u32(uint64_t x)
 
 #define ISO_PRIMARY_VD_SECTOR 16
 
-static inline void read_iso_sector(uint32_t block_offset, void *buf,
-   const char *errmsg)
-{
-IPL_assert(virtio_read_many(block_offset, buf, 1) == 0, errmsg);
-}
-
-static inline void read_iso_boot_image(uint32_t block_offset, void *load_addr,
+static inline int read_iso_boot_image(uint32_t block_offset, void *load_addr,
uint32_t blks_to_load)
 {
-IPL_assert(virtio_read_many(block_offset, load_addr, blks_to_load) == 0,
-   "Failed to read boot image!");
+if (virtio_read_many(block_offset, load_addr, blks_to_load)) {
+puts("Failed to read boot image!");
+return 1;
+}
+return 0;
 }
 
 #define ISO9660_MAX_DIR_DEPTH 8
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 6abb34e563..3e844abd71 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -30,6 +30,7 @@ typedef unsigned long long u64;
 #define EIO 1
 #define EBUSY   2
 #define ENODEV  3
+#define EINVAL  4
 
 #ifndef MIN
 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 414c3f1b47..5477cfe228 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -678,8 +678,10 @@ static bool is_iso_bc_entry_compatible(IsoBcSection *s)
 if (s->unused || !s->sector_count) {
 return false;
 }
-read_iso_sector(bswap32(s->load_rba), magic_sec,
-"Failed to read image sector 0");
+if (virtio_read(bswap32(s->load_rba), magic_sec)) {
+puts("Failed to read image sector 0");
+return false;
+}
 
 /* Checking bytes 8 - 32 for S390 Linux magic */
 return !memcmp(magic_sec + 8, linux_s390_magic, 24);
@@ -692,28 +694,35 @@ static uint32_t sec_offset[ISO9660_MAX_DIR_DEPTH];
 /* Remained directory space in bytes */
 static uint32_t dir_rem[ISO9660_MAX_DIR_DEPTH];
 
-static inline uint32_t iso_get_file_size(uint32_t load_rba)
+static inline long iso_get_file_size(uint32_t load_rba)
 {
 IsoVolDesc *vd = (IsoVolDesc *)sec;
 IsoDirHdr *cur_record = &vd->vd.primary.rootdir;
 uint8_t *temp = sec + ISO_SECTOR_SIZE;
 int level = 0;
 
-read_iso_sector(ISO_PRIMARY_VD_SECTOR, sec,
-"Failed to read ISO primary descriptor");
+if (virtio_read(ISO_PRIMARY_VD_SECTOR, sec)) {
+puts("Failed to read ISO primary descriptor");
+return -EIO;
+}
+
 sec_loc[0] = iso_733_to_u32(cur_record->ext_loc);
 dir_rem[0] = 0;
 sec_offset[0] = 0;
 
 while (level >= 0) {
-IPL_assert(sec_offset[level] <= ISO_SECTOR_SIZE,
-   "Directory tree structure violation");
+if (sec_offset[level] > ISO_SECTOR_SIZE) {
+puts("Directory tree structure violation");
+return -EIO;
+}
 
 cur_record = (IsoDirHdr *)(temp + sec_offset[level]);
 
 if (sec_offset[level] == 0) {
-read_iso_sector(sec_loc[level], temp,
-"Failed to read ISO directory");
+if (virtio_read(sec_loc[level], temp)) {
+puts("Failed to read ISO directory");
+return -EIO;
+}
 if (dir_rem[level] == 0) {
 /* Skip self and parent records */
 dir_rem[level] = iso_733_to_u32(cur_record->data_len) -
@@ -758,8 +767,10 @@ static inline uint32_t iso_get_file_size(uint32_t load_rba)
 if (dir_rem[level] == 0) {
 /* Nothing remaining */
 level--;
-read_iso_sector(sec_loc[level], temp,
-"Failed to read ISO directory");
+if (virtio_read(sec_loc[level], temp)) {
+puts("Failed to read ISO directory");
+return -EIO;
+}
 }
 }
 
@@ -774,7 +785,7 @@ static void load_iso_bc_entry(IsoBcSection *load)
  * is padded and ISO_SECTOR_SIZE bytes aligned
  */
 uint32_t blks_to_load = bswap16(s.sector_count) >> ET_SECTOR_SHIFT;
-uint32_t real_size = iso_get_file_size(bswap32(s.load_rba));
+long real_size = iso_get_file_size(bswap32(s.load_rba));
 
 if (real_size) {
 /* Round up blocks to load */
@@ -784,

[PATCH v4 10/19] pc-bios/s390-ccw: Remove panics from DASD IPL path

2024-10-16 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from DASD IPL specific functions so that error recovery
may be possible in the future.

Functions that would previously panic now provide a return code.

Signed-off-by: Jared Rossi 
Reviewed-by: Thomas Huth 
---
 pc-bios/s390-ccw/dasd-ipl.h |  2 +-
 pc-bios/s390-ccw/dasd-ipl.c | 66 -
 2 files changed, 37 insertions(+), 31 deletions(-)

diff --git a/pc-bios/s390-ccw/dasd-ipl.h b/pc-bios/s390-ccw/dasd-ipl.h
index c394828906..eb1898c84a 100644
--- a/pc-bios/s390-ccw/dasd-ipl.h
+++ b/pc-bios/s390-ccw/dasd-ipl.h
@@ -11,6 +11,6 @@
 #ifndef DASD_IPL_H
 #define DASD_IPL_H
 
-void dasd_ipl(SubChannelId schid, uint16_t cutype);
+int dasd_ipl(SubChannelId schid, uint16_t cutype);
 
 #endif /* DASD_IPL_H */
diff --git a/pc-bios/s390-ccw/dasd-ipl.c b/pc-bios/s390-ccw/dasd-ipl.c
index ae751adec1..e7b487809c 100644
--- a/pc-bios/s390-ccw/dasd-ipl.c
+++ b/pc-bios/s390-ccw/dasd-ipl.c
@@ -111,38 +111,29 @@ static void make_readipl(void)
 ccwIplRead->count = 0x18; /* Read 0x18 bytes of data */
 }
 
-static void run_readipl(SubChannelId schid, uint16_t cutype)
+static int run_readipl(SubChannelId schid, uint16_t cutype)
 {
-if (do_cio(schid, cutype, 0x00, CCW_FMT0)) {
-panic("dasd-ipl: Failed to run Read IPL channel program\n");
-}
+return do_cio(schid, cutype, 0x00, CCW_FMT0);
 }
 
 /*
  * The architecture states that IPL1 data should consist of a psw followed by
  * format-0 READ and TIC CCWs. Let's sanity check.
  */
-static void check_ipl1(void)
+static bool check_ipl1(void)
 {
 Ccw0 *ccwread = (Ccw0 *)0x08;
 Ccw0 *ccwtic = (Ccw0 *)0x10;
 
-if (ccwread->cmd_code != CCW_CMD_DASD_READ ||
-ccwtic->cmd_code != CCW_CMD_TIC) {
-panic("dasd-ipl: IPL1 data invalid. Is this disk really bootable?\n");
-}
+return (ccwread->cmd_code == CCW_CMD_DASD_READ &&
+ccwtic->cmd_code == CCW_CMD_TIC);
 }
 
-static void check_ipl2(uint32_t ipl2_addr)
+static bool check_ipl2(uint32_t ipl2_addr)
 {
 Ccw0 *ccw = u32toptr(ipl2_addr);
 
-if (ipl2_addr == 0x00) {
-panic("IPL2 address invalid. Is this disk really bootable?\n");
-}
-if (ccw->cmd_code == 0x00) {
-panic("IPL2 ccw data invalid. Is this disk really bootable?\n");
-}
+return (ipl2_addr != 0x00 && ccw->cmd_code != 0x00);
 }
 
 static uint32_t read_ipl2_addr(void)
@@ -188,52 +179,67 @@ static void ipl1_fixup(void)
 ccwSearchTic->cda = ptr2u32(ccwSearchID);
 }
 
-static void run_ipl1(SubChannelId schid, uint16_t cutype)
+static int run_ipl1(SubChannelId schid, uint16_t cutype)
  {
 uint32_t startAddr = 0x08;
 
-if (do_cio(schid, cutype, startAddr, CCW_FMT0)) {
-panic("dasd-ipl: Failed to run IPL1 channel program\n");
-}
+return do_cio(schid, cutype, startAddr, CCW_FMT0);
 }
 
-static void run_ipl2(SubChannelId schid, uint16_t cutype, uint32_t addr)
+static int run_ipl2(SubChannelId schid, uint16_t cutype, uint32_t addr)
 {
-if (run_dynamic_ccw_program(schid, cutype, addr)) {
-panic("dasd-ipl: Failed to run IPL2 channel program\n");
-}
+return run_dynamic_ccw_program(schid, cutype, addr);
 }
 
 /*
  * Limitations in vfio-ccw support complicate the IPL process. Details can
  * be found in docs/devel/s390-dasd-ipl.rst
  */
-void dasd_ipl(SubChannelId schid, uint16_t cutype)
+int dasd_ipl(SubChannelId schid, uint16_t cutype)
 {
 PSWLegacy *pswl = (PSWLegacy *) 0x00;
 uint32_t ipl2_addr;
 
 /* Construct Read IPL CCW and run it to read IPL1 from boot disk */
 make_readipl();
-run_readipl(schid, cutype);
+if (run_readipl(schid, cutype)) {
+puts("Failed to run Read IPL channel program");
+return -EIO;
+}
+
 ipl2_addr = read_ipl2_addr();
-check_ipl1();
+
+if (!check_ipl1()) {
+puts("IPL1 invalid for DASD-IPL");
+return -EINVAL;
+}
 
 /*
  * Fixup IPL1 channel program to account for vfio-ccw limitations, then run
  * it to read IPL2 channel program from boot disk.
  */
 ipl1_fixup();
-run_ipl1(schid, cutype);
-check_ipl2(ipl2_addr);
+if (run_ipl1(schid, cutype)) {
+puts("Failed to run IPL1 channel program");
+return -EIO;
+}
+
+if (!check_ipl2(ipl2_addr)) {
+puts("IPL2 invalid for DASD-IPL");
+return -EINVAL;
+}
 
 /*
  * Run IPL2 channel program to read operating system code from boot disk
  */
-run_ipl2(schid, cutype, ipl2_addr);
+if (run_ipl2(schid, cutype, ipl2_addr)) {
+puts("Failed to run IPL2 channel program");
+return -EIO;
+}
 
 /* Transfer control to the guest operating system */
 pswl->mask |= PSW_MASK_EAMODE;   /* Force z-mode */
 pswl->addr |= PSW_MASK_BAMODE;   /* ...  */
 jump_to_low_kernel();
+return 1;
 }
-- 
2.45.1




[PATCH v4 08/19] pc-bios/s390-ccw: Remove panics from ECKD IPL path

2024-10-16 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from ECKD block device IPL specific functions so that
error recovery may be possible in the future.

Functions that would previously panic now provide a return code.

Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/bootmap.h |   1 +
 pc-bios/s390-ccw/bootmap.c | 193 +
 2 files changed, 135 insertions(+), 59 deletions(-)

diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
index 09f4e6fb40..271dbabbc3 100644
--- a/pc-bios/s390-ccw/bootmap.h
+++ b/pc-bios/s390-ccw/bootmap.h
@@ -16,6 +16,7 @@
 
 typedef uint64_t block_number_t;
 #define NULL_BLOCK_NR 0xULL
+#define ERROR_BLOCK_NR 0xfffeULL
 
 #define FREE_SPACE_FILLER '\xAA'
 
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 5477cfe228..dd04bb3384 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -145,14 +145,17 @@ static block_number_t load_eckd_segments(block_number_t 
blk, bool ldipl,
 bool more_data;
 
 memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs));
-read_block(blk, bprs, "BPRS read failed");
+if (virtio_read(blk, bprs)) {
+puts("BPRS read failed");
+return ERROR_BLOCK_NR;
+}
 
 do {
 more_data = false;
 for (j = 0;; j++) {
 block_nr = gen_eckd_block_num(&bprs[j].xeckd, ldipl);
 if (is_null_block_number(block_nr)) { /* end of chunk */
-break;
+return NULL_BLOCK_NR;
 }
 
 /* we need the updated blockno for the next indirect entry
@@ -163,15 +166,20 @@ static block_number_t load_eckd_segments(block_number_t 
blk, bool ldipl,
 }
 
 /* List directed pointer does not store block size */
-IPL_assert(ldipl || block_size_ok(bprs[j].xeckd.bptr.size),
-   "bad chunk block size");
+if (!ldipl && !block_size_ok(bprs[j].xeckd.bptr.size)) {
+puts("Bad chunk block size");
+return NULL_BLOCK_NR;
+}
 
 if (!eckd_valid_address(&bprs[j].xeckd, ldipl)) {
 /*
  * If an invalid address is found during LD-IPL then break and
- * retry as CCW
+ * retry as CCW-IPL, otherwise abort on error
  */
-IPL_assert(ldipl, "bad chunk ECKD addr");
+if (!ldipl) {
+puts("Bad chunk ECKD address");
+return ERROR_BLOCK_NR;
+}
 break;
 }
 
@@ -189,7 +197,10 @@ static block_number_t load_eckd_segments(block_number_t 
blk, bool ldipl,
  * I.e. the next ptr must point to the unused memory area
  */
 memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs));
-read_block(block_nr, bprs, "BPRS continuation read failed");
+if (virtio_read(block_nr, bprs)) {
+puts("BPRS continuation read failed");
+return ERROR_BLOCK_NR;
+}
 more_data = true;
 break;
 }
@@ -198,7 +209,10 @@ static block_number_t load_eckd_segments(block_number_t 
blk, bool ldipl,
  * to memory (address).
  */
 rc = virtio_read_many(block_nr, (void *)(*address), count + 1);
-IPL_assert(rc == 0, "code chunk read failed");
+if (rc != 0) {
+puts("Code chunk read failed");
+return ERROR_BLOCK_NR;
+}
 
 *address += (count + 1) * virtio_get_block_size();
 }
@@ -232,7 +246,10 @@ static int eckd_get_boot_menu_index(block_number_t 
s1b_block_nr)
 
 /* Get Stage1b data */
 memset(sec, FREE_SPACE_FILLER, sizeof(sec));
-read_block(s1b_block_nr, s1b, "Cannot read stage1b boot loader");
+if (virtio_read(s1b_block_nr, s1b)) {
+puts("Cannot read stage1b boot loader");
+return -EIO;
+}
 
 memset(_s2, FREE_SPACE_FILLER, sizeof(_s2));
 
@@ -244,7 +261,10 @@ static int eckd_get_boot_menu_index(block_number_t 
s1b_block_nr)
 break;
 }
 
-read_block(cur_block_nr, s2_cur_blk, "Cannot read stage2 boot loader");
+if (virtio_read(cur_block_nr, s2_cur_blk)) {
+puts("Cannot read stage2 boot loader");
+return -EIO;
+}
 
 if (find_zipl_boot_menu_banner(&banner_offset)) {
 /*
@@ -252,8 +272,10 @@ static int eckd_get_boot_menu_index(block_number_t 
s1b_block_nr)
  * possibility of menu data spanning multiple blocks.
  */
 if (prev_block_nr) {
-read_block(prev_block_nr, s2_prev_blk,
-   "Cannot read stage2 boot loader");
+if (virtio_read(prev_block_nr, s2_prev_blk)) {
+puts("Cannot read stage2 boot loader");
+

[PATCH v4 15/19] hw/s390x: Build an IPLB for each boot device

2024-10-16 Thread jrossi
From: Jared Rossi 

Build an IPLB for any device with a bootindex (up to a maximum of 8 devices).

The IPLB chain is placed immediately before the BIOS in memory. Because this
is not a fixed address, the location of the next IPLB and number of remaining
boot devices is stored in the QIPL global variable for possible later access by
the guest during IPL.

Signed-off-by: Jared Rossi 
Reviewed-by: Thomas Huth 
---
 hw/s390x/ipl.h  |   1 +
 include/hw/s390x/ipl/qipl.h |   4 +-
 hw/s390x/ipl.c  | 123 
 3 files changed, 99 insertions(+), 29 deletions(-)

diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index b670bad551..54eb48fd6e 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -20,6 +20,7 @@
 #include "qom/object.h"
 
 #define DIAG308_FLAGS_LP_VALID 0x80
+#define MAX_BOOT_DEVS 8 /* Max number of devices that may have a bootindex */
 
 void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp);
 void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp);
diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
index 0ef04af027..e56b181298 100644
--- a/include/hw/s390x/ipl/qipl.h
+++ b/include/hw/s390x/ipl/qipl.h
@@ -31,7 +31,9 @@ struct QemuIplParameters {
 uint8_t  reserved1[3];
 uint64_t reserved2;
 uint32_t boot_menu_timeout;
-uint8_t  reserved3[12];
+uint8_t  reserved3[2];
+uint16_t chain_len;
+uint64_t next_iplb;
 } QEMU_PACKED;
 typedef struct QemuIplParameters QemuIplParameters;
 
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 55a83bb836..aec79f41a8 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -56,6 +56,13 @@ static bool iplb_extended_needed(void *opaque)
 return ipl->iplbext_migration;
 }
 
+/* Place the IPLB chain immediately before the BIOS in memory */
+static uint64_t find_iplb_chain_addr(uint64_t bios_addr, uint16_t count)
+{
+return (bios_addr & TARGET_PAGE_MASK)
+- (count * sizeof(IplParameterBlock));
+}
+
 static const VMStateDescription vmstate_iplb_extended = {
 .name = "ipl/iplb_extended",
 .version_id = 0,
@@ -398,6 +405,17 @@ static CcwDevice *s390_get_ccw_device(DeviceState *dev_st, 
int *devtype)
 return ccw_dev;
 }
 
+static uint64_t s390_ipl_map_iplb_chain(IplParameterBlock *iplb_chain)
+{
+S390IPLState *ipl = get_ipl_device();
+uint16_t count = ipl->qipl.chain_len;
+uint64_t len = sizeof(IplParameterBlock) * count;
+uint64_t chain_addr = find_iplb_chain_addr(ipl->bios_start_addr, count);
+
+cpu_physical_memory_write(chain_addr, iplb_chain, len);
+return chain_addr;
+}
+
 void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp)
 {
 int i;
@@ -428,54 +446,51 @@ void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t 
*ebcdic_lp)
 }
 }
 
-static bool s390_gen_initial_iplb(S390IPLState *ipl)
+static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
 {
-DeviceState *dev_st;
+S390IPLState *ipl = get_ipl_device();
 CcwDevice *ccw_dev = NULL;
 SCSIDevice *sd;
 int devtype;
 uint8_t *lp;
 
-dev_st = get_boot_device(0);
-if (dev_st) {
-ccw_dev = s390_get_ccw_device(dev_st, &devtype);
-}
-
 /*
  * Currently allow IPL only from CCW devices.
  */
+ccw_dev = s390_get_ccw_device(dev_st, &devtype);
 if (ccw_dev) {
 lp = ccw_dev->loadparm;
 
 switch (devtype) {
 case CCW_DEVTYPE_SCSI:
 sd = SCSI_DEVICE(dev_st);
-ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN);
-ipl->iplb.blk0_len =
+iplb->len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN);
+iplb->blk0_len =
 cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN - 
S390_IPLB_HEADER_LEN);
-ipl->iplb.pbt = S390_IPL_TYPE_QEMU_SCSI;
-ipl->iplb.scsi.lun = cpu_to_be32(sd->lun);
-ipl->iplb.scsi.target = cpu_to_be16(sd->id);
-ipl->iplb.scsi.channel = cpu_to_be16(sd->channel);
-ipl->iplb.scsi.devno = cpu_to_be16(ccw_dev->sch->devno);
-ipl->iplb.scsi.ssid = ccw_dev->sch->ssid & 3;
+iplb->pbt = S390_IPL_TYPE_QEMU_SCSI;
+iplb->scsi.lun = cpu_to_be32(sd->lun);
+iplb->scsi.target = cpu_to_be16(sd->id);
+iplb->scsi.channel = cpu_to_be16(sd->channel);
+iplb->scsi.devno = cpu_to_be16(ccw_dev->sch->devno);
+iplb->scsi.ssid = ccw_dev->sch->ssid & 3;
 break;
 case CCW_DEVTYPE_VFIO:
-ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
-ipl->iplb.pbt = S390_IPL_TYPE_CCW;
-ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
-ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3;
+iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
+iplb->pbt = S390_IPL_TYPE_CCW;
+iplb->ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
+iplb->ccw.ssid = ccw_dev->sch->ssid & 3;
 

[PATCH v4 19/19] tests/qtest: Add s390x boot order tests to cdrom-test.c

2024-10-16 Thread jrossi
From: Jared Rossi 

Add two new qtests to verify that a valid IPL device can successfully boot after
failed IPL attempts from one or more invalid devices.

cdrom-test/as-fallback-device: Defines the primary boot target as a device that
is invalid for IPL and a second boot target that is valid for IPL. Ensures that
the valid device will be selected after the initial failed IPL.

cdrom-test/as-last-option: Defines the maximum number of boot devices (8)
where only the final entry in the boot order is valid. Ensures that a valid
device will be selected even after multiple failed IPL attempts from both
virtio-blk and virtio-scsi device types.

Signed-off-by: Jared Rossi 
---
 tests/qtest/cdrom-test.c | 24 
 1 file changed, 24 insertions(+)

diff --git a/tests/qtest/cdrom-test.c b/tests/qtest/cdrom-test.c
index 9d72b24e4b..c86725a511 100644
--- a/tests/qtest/cdrom-test.c
+++ b/tests/qtest/cdrom-test.c
@@ -213,6 +213,30 @@ static void add_s390x_tests(void)
 "-drive driver=null-co,read-zeroes=on,if=none,id=d1 "
 "-device virtio-blk,drive=d2,bootindex=1 "
 "-drive if=none,id=d2,media=cdrom,file=", test_cdboot);
+qtest_add_data_func("cdrom/boot/as-fallback-device",
+"-device virtio-serial -device virtio-scsi "
+"-device virtio-blk,drive=d1,bootindex=1 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d1 "
+"-device virtio-blk,drive=d2,bootindex=2 "
+"-drive if=none,id=d2,media=cdrom,file=", test_cdboot);
+qtest_add_data_func("cdrom/boot/as-last-option",
+"-device virtio-serial -device virtio-scsi "
+"-device virtio-blk,drive=d1,bootindex=1 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d1 "
+"-device virtio-blk,drive=d2,bootindex=2 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d2 "
+"-device virtio-blk,drive=d3,bootindex=3 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d3 "
+"-device scsi-hd,drive=d4,bootindex=4 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d4 "
+"-device scsi-hd,drive=d5,bootindex=5 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d5 "
+"-device virtio-blk,drive=d6,bootindex=6 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d6 "
+"-device scsi-hd,drive=d7,bootindex=7 "
+"-drive driver=null-co,read-zeroes=on,if=none,id=d7 "
+"-device scsi-cd,drive=d8,bootindex=8 "
+"-drive if=none,id=d8,media=cdrom,file=", test_cdboot);
 if (qtest_has_device("x-terminal3270")) {
 qtest_add_data_func("cdrom/boot/without-bootindex",
 "-device virtio-scsi -device virtio-serial "
-- 
2.45.1




[PATCH v4 14/19] s390x: Add individual loadparm assignment to CCW device

2024-10-16 Thread jrossi
From: Jared Rossi 

Add a loadparm property to the VirtioCcwDevice object so that different
loadparms can be defined on a per-device basis for CCW boot devices.

The machine/global loadparm is still supported. If both a global and per-device
loadparm are defined, the per-device value will override the global value for
that device, but any other devices that do not specify a per-device loadparm
will still use the global loadparm.

It is invalid to assign a loadparm to a non-boot device.

Signed-off-by: Jared Rossi 
Reviewed-by: Thomas Huth 
---
 hw/s390x/ccw-device.h  |  2 ++
 hw/s390x/ipl.h |  3 +-
 hw/s390x/ccw-device.c  | 46 ++
 hw/s390x/ipl.c | 66 +++---
 hw/s390x/s390-virtio-ccw.c | 18 +--
 hw/s390x/sclp.c|  3 +-
 pc-bios/s390-ccw/main.c| 10 --
 7 files changed, 100 insertions(+), 48 deletions(-)

diff --git a/hw/s390x/ccw-device.h b/hw/s390x/ccw-device.h
index 5feeb0ee7a..1e1737c0f3 100644
--- a/hw/s390x/ccw-device.h
+++ b/hw/s390x/ccw-device.h
@@ -26,6 +26,8 @@ struct CcwDevice {
 CssDevId dev_id;
 /* The actual busid of the virtual subchannel. */
 CssDevId subch_id;
+/* If set, use this loadparm value when device is boot target */
+uint8_t loadparm[8];
 };
 typedef struct CcwDevice CcwDevice;
 
diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index fa394c339d..b670bad551 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -21,7 +21,8 @@
 
 #define DIAG308_FLAGS_LP_VALID 0x80
 
-int s390_ipl_set_loadparm(uint8_t *loadparm);
+void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp);
+void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp);
 void s390_ipl_update_diag308(IplParameterBlock *iplb);
 int s390_ipl_prepare_pv_header(Error **errp);
 int s390_ipl_pv_unpack(void);
diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c
index 14c24e3890..230cc09e03 100644
--- a/hw/s390x/ccw-device.c
+++ b/hw/s390x/ccw-device.c
@@ -13,6 +13,10 @@
 #include "ccw-device.h"
 #include "hw/qdev-properties.h"
 #include "qemu/module.h"
+#include "ipl.h"
+#include "qapi/visitor.h"
+#include "qemu/ctype.h"
+#include "qapi/error.h"
 
 static void ccw_device_refill_ids(CcwDevice *dev)
 {
@@ -37,10 +41,52 @@ static bool ccw_device_realize(CcwDevice *dev, Error **errp)
 return true;
 }
 
+static void ccw_device_get_loadparm(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+CcwDevice *dev = CCW_DEVICE(obj);
+char *str = g_strndup((char *) dev->loadparm, sizeof(dev->loadparm));
+
+visit_type_str(v, name, &str, errp);
+g_free(str);
+}
+
+static void ccw_device_set_loadparm(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+CcwDevice *dev = CCW_DEVICE(obj);
+char *val;
+int index;
+
+index = object_property_get_int(obj, "bootindex", NULL);
+
+if (index < 0) {
+error_setg(errp, "LOADPARM is only valid for boot devices!");
+}
+
+if (!visit_type_str(v, name, &val, errp)) {
+return;
+}
+
+s390_ipl_fmt_loadparm(dev->loadparm, val, errp);
+}
+
+static const PropertyInfo ccw_loadparm = {
+.name  = "ccw_loadparm",
+.description = "Up to 8 chars in set of [A-Za-z0-9. ] to pass"
+" to the guest loader/kernel",
+.get = ccw_device_get_loadparm,
+.set = ccw_device_set_loadparm,
+};
+
 static Property ccw_device_properties[] = {
 DEFINE_PROP_CSS_DEV_ID("devno", CcwDevice, devno),
 DEFINE_PROP_CSS_DEV_ID_RO("dev_id", CcwDevice, dev_id),
 DEFINE_PROP_CSS_DEV_ID_RO("subch_id", CcwDevice, subch_id),
+DEFINE_PROP("loadparm", CcwDevice, loadparm, ccw_loadparm,
+typeof(uint8_t[8])),
 DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 8c490eeb52..55a83bb836 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -34,6 +34,7 @@
 #include "qemu/config-file.h"
 #include "qemu/cutils.h"
 #include "qemu/option.h"
+#include "qemu/ctype.h"
 #include "standard-headers/linux/virtio_ids.h"
 
 #define KERN_IMAGE_START0x01UL
@@ -397,12 +398,43 @@ static CcwDevice *s390_get_ccw_device(DeviceState 
*dev_st, int *devtype)
 return ccw_dev;
 }
 
+void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp)
+{
+int i;
+
+/* Initialize the loadparm with spaces */
+memset(loadparm, ' ', LOADPARM_LEN);
+for (i = 0; i < LOADPARM_LEN && str[i]; i++) {
+uint8_t c = qemu_toupper(str[i]); /* mimic HMC */
+
+if (qemu_isalnum(c) || c == '.' || c == ' ') {
+loadparm[i] = c;
+} else {
+error_setg(errp, "LOADPARM: invalid character '%c' (ASCII 0x%02x)",
+   c, c);
+return;
+}
+}
+}
+
+void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic

[PATCH v4 17/19] pc-bios/s390x: Enable multi-device boot loop

2024-10-16 Thread jrossi
From: Jared Rossi 

Allow attempts to boot from multiple IPL devices. If the first device fails to
IPL, select the pre-built IPLB for the next device in the boot order and attempt
to IPL from it. Continue this process until IPL is successful or there are no
devices left to try.

Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/iplb.h | 23 
 pc-bios/s390-ccw/jump2ipl.c |  7 +++---
 pc-bios/s390-ccw/main.c | 43 +++--
 pc-bios/s390-ccw/netmain.c  |  2 +-
 4 files changed, 55 insertions(+), 20 deletions(-)

diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h
index 81ff9d4497..08f259ff31 100644
--- a/pc-bios/s390-ccw/iplb.h
+++ b/pc-bios/s390-ccw/iplb.h
@@ -17,6 +17,7 @@
 #endif
 
 #include 
+#include 
 
 extern QemuIplParameters qipl;
 extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
@@ -50,4 +51,26 @@ static inline bool set_iplb(IplParameterBlock *iplb)
 return manage_iplb(iplb, false);
 }
 
+/*
+ * The IPL started on the device, but failed in some way.  If the IPLB chain
+ * still has more devices left to try, use the next device in order.
+ */
+static inline bool load_next_iplb(void)
+{
+IplParameterBlock *next_iplb;
+
+if (qipl.chain_len < 1) {
+return false;
+}
+
+qipl.index++;
+next_iplb = (IplParameterBlock *) qipl.next_iplb;
+memcpy(&iplb, next_iplb, sizeof(IplParameterBlock));
+
+qipl.chain_len--;
+qipl.next_iplb = qipl.next_iplb + sizeof(IplParameterBlock);
+
+return true;
+}
+
 #endif /* IPLB_H */
diff --git a/pc-bios/s390-ccw/jump2ipl.c b/pc-bios/s390-ccw/jump2ipl.c
index 4bc9ba4f04..6aa44ccd05 100644
--- a/pc-bios/s390-ccw/jump2ipl.c
+++ b/pc-bios/s390-ccw/jump2ipl.c
@@ -46,9 +46,10 @@ int jump_to_IPL_code(uint64_t address)
  */
 if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) {
 iplb.devno = qipl.index;
-if (!set_iplb(&iplb)) {
-panic("Failed to set IPLB");
-}
+}
+
+if (have_iplb && !set_iplb(&iplb)) {
+panic("Failed to set IPLB");
 }
 
 /*
diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
index 9770290958..da3394de7c 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -55,6 +55,12 @@ void write_iplb_location(void)
 }
 }
 
+static void copy_qipl(void)
+{
+QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS;
+memcpy(&qipl, early_qipl, sizeof(QemuIplParameters));
+}
+
 unsigned int get_loadparm_index(void)
 {
 return atoi(loadparm_str);
@@ -152,6 +158,7 @@ static void menu_setup(void)
 
 /* If loadparm was set to any other value, then do not enable menu */
 if (memcmp(loadparm_str, LOADPARM_EMPTY, LOADPARM_LEN) != 0) {
+menu_set_parms(qipl.qipl_flags && ~BOOT_MENU_FLAG_MASK, 0);
 return;
 }
 
@@ -183,7 +190,6 @@ static void css_setup(void)
 static void boot_setup(void)
 {
 char lpmsg[] = "LOADPARM=[]\n";
-have_iplb = store_iplb(&iplb);
 
 if (memcmp(iplb.loadparm, "", LOADPARM_LEN) != 0) {
 ebcdic_to_ascii((char *) iplb.loadparm, loadparm_str, LOADPARM_LEN);
@@ -191,6 +197,10 @@ static void boot_setup(void)
 sclp_get_loadparm_ascii(loadparm_str);
 }
 
+if (have_iplb) {
+menu_setup();
+}
+
 memcpy(lpmsg + 10, loadparm_str, 8);
 puts(lpmsg);
 
@@ -208,6 +218,7 @@ static bool find_boot_device(void)
 
 switch (iplb.pbt) {
 case S390_IPL_TYPE_CCW:
+vdev->scsi_device_selected = false;
 debug_print_int("device no. ", iplb.ccw.devno);
 blk_schid.ssid = iplb.ccw.ssid & 0x3;
 debug_print_int("ssid ", blk_schid.ssid);
@@ -231,15 +242,8 @@ static bool find_boot_device(void)
 static int virtio_setup(void)
 {
 VDev *vdev = virtio_get_device();
-QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS;
 int ret;
 
-memcpy(&qipl, early_qipl, sizeof(QemuIplParameters));
-
-if (have_iplb) {
-menu_setup();
-}
-
 switch (vdev->senseid.cu_model) {
 case VIRTIO_ID_NET:
 puts("Network boot device detected");
@@ -271,10 +275,9 @@ static void ipl_boot_device(void)
 dasd_ipl(blk_schid, cutype);
 break;
 case CU_TYPE_VIRTIO:
-if (virtio_setup()) {
-return;/* Only returns in case of errors */
+if (virtio_setup() == 0) {
+zipl_load();
 }
-zipl_load();
 break;
 default:
 printf("Attempting to boot from unexpected device type 0x%X\n", 
cutype);
@@ -307,14 +310,22 @@ static void probe_boot_device(void)
 
 void main(void)
 {
+copy_qipl();
 sclp_setup();
 css_setup();
-boot_setup();
-if (have_iplb && find_boot_device()) {
-ipl_boot_device();
-} else {
+have_iplb = store_iplb(&iplb);
+if (!have_iplb) {
 probe_boot_device();
 }
 
-panic("Failed to IPL. Halting...");
+while (have_iplb) {
+boot_setup();
+if (ha

[PATCH v4 18/19] docs/system: Update documentation for s390x IPL

2024-10-16 Thread jrossi
From: Jared Rossi 

Update docs to show that s390x PC BIOS can support more than one boot device.

Signed-off-by: Jared Rossi 
Reviewed-by: Thomas Huth 
---
 docs/system/bootindex.rst | 7 ---
 docs/system/s390x/bootdevices.rst | 9 ++---
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/docs/system/bootindex.rst b/docs/system/bootindex.rst
index 8b057f812f..988f7b3beb 100644
--- a/docs/system/bootindex.rst
+++ b/docs/system/bootindex.rst
@@ -49,10 +49,11 @@ Limitations
 ---
 
 Some firmware has limitations on which devices can be considered for
-booting.  For instance, the PC BIOS boot specification allows only one
-disk to be bootable.  If boot from disk fails for some reason, the BIOS
+booting.  For instance, the x86 PC BIOS boot specification allows only one
+disk to be bootable.  If boot from disk fails for some reason, the x86 BIOS
 won't retry booting from other disk.  It can still try to boot from
-floppy or net, though.
+floppy or net, though. In the case of s390x BIOS, the BIOS will try up to
+8 total devices, any number of which may be disks.
 
 Sometimes, firmware cannot map the device path QEMU wants firmware to
 boot from to a boot method.  It doesn't happen for devices the firmware
diff --git a/docs/system/s390x/bootdevices.rst 
b/docs/system/s390x/bootdevices.rst
index c97efb8fc0..1a1a764c1c 100644
--- a/docs/system/s390x/bootdevices.rst
+++ b/docs/system/s390x/bootdevices.rst
@@ -6,9 +6,7 @@ Booting with bootindex parameter
 
 For classical mainframe guests (i.e. LPAR or z/VM installations), you always
 have to explicitly specify the disk where you want to boot from (or "IPL" from,
-in s390x-speak -- IPL means "Initial Program Load"). In particular, there can
-also be only one boot device according to the architecture specification, thus
-specifying multiple boot devices is not possible (yet).
+in s390x-speak -- IPL means "Initial Program Load").
 
 So for booting an s390x guest in QEMU, you should always mark the
 device where you want to boot from with the ``bootindex`` property, for
@@ -17,6 +15,11 @@ example::
  qemu-system-s390x -drive if=none,id=dr1,file=guest.qcow2 \
-device virtio-blk,drive=dr1,bootindex=1
 
+Multiple devices may have a bootindex. The lowest bootindex is assigned to the
+device to IPL first.  If the IPL fails for the first, the device with the 
second
+lowest bootindex will be tried and so on until IPL is successful or there are 
no
+remaining boot devices to try.
+
 For booting from a CD-ROM ISO image (which needs to include El-Torito boot
 information in order to be bootable), it is recommended to specify a 
``scsi-cd``
 device, for example like this::
-- 
2.45.1




[PATCH v4 11/19] pc-bios/s390-ccw: Remove panics from Netboot IPL path

2024-10-16 Thread jrossi
From: Jared Rossi 

Remove panic-on-error from Netboot specific functions so that error recovery
may be possible in the future.

Functions that would previously panic now provide a return code.

Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/s390-ccw.h   |  2 +-
 pc-bios/s390-ccw/bootmap.c|  1 +
 pc-bios/s390-ccw/netmain.c| 17 +++--
 pc-bios/s390-ccw/virtio-net.c |  7 +--
 4 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 3e844abd71..344ad15655 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -57,7 +57,7 @@ unsigned int get_loadparm_index(void);
 void main(void);
 
 /* netmain.c */
-void netmain(void);
+int netmain(void);
 
 /* sclp.c */
 void sclp_print(const char *string);
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index a277fc3431..f1cfb7aaa8 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -1073,6 +1073,7 @@ void zipl_load(void)
 
 if (virtio_get_device_type() == VIRTIO_ID_NET) {
 netmain();
+panic("\n! Cannot IPL from this network !\n");
 }
 
 if (ipl_scsi()) {
diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c
index bc6ad8695f..0bb359e09f 100644
--- a/pc-bios/s390-ccw/netmain.c
+++ b/pc-bios/s390-ccw/netmain.c
@@ -464,7 +464,7 @@ static bool find_net_dev(Schib *schib, int dev_no)
 return false;
 }
 
-static void virtio_setup(void)
+static bool virtio_setup(void)
 {
 Schib schib;
 int ssid;
@@ -495,10 +495,10 @@ static void virtio_setup(void)
 }
 }
 
-IPL_assert(found, "No virtio net device found");
+return found;
 }
 
-void netmain(void)
+int netmain(void)
 {
 filename_ip_t fn_ip;
 int rc, fnlen;
@@ -506,11 +506,15 @@ void netmain(void)
 sclp_setup();
 puts("Network boot starting...");
 
-virtio_setup();
+if (!virtio_setup()) {
+puts("No virtio net device found.");
+return 1;
+}
 
 rc = net_init(&fn_ip);
 if (rc) {
-panic("Network initialization failed. Halting.");
+puts("Network initialization failed.");
+return 1;
 }
 
 fnlen = strlen(fn_ip.filename);
@@ -528,5 +532,6 @@ void netmain(void)
 jump_to_low_kernel();
 }
 
-panic("Failed to load OS from network.");
+puts("Failed to load OS from network.");
+return 1;
 }
diff --git a/pc-bios/s390-ccw/virtio-net.c b/pc-bios/s390-ccw/virtio-net.c
index 2fcb0a58c5..f9854a22c3 100644
--- a/pc-bios/s390-ccw/virtio-net.c
+++ b/pc-bios/s390-ccw/virtio-net.c
@@ -54,8 +54,11 @@ int virtio_net_init(void *mac_addr)
 vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT;
 virtio_setup_ccw(vdev);
 
-IPL_assert(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT,
-   "virtio-net device does not support the MAC address feature");
+if (!(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT)) {
+puts("virtio-net device does not support the MAC address feature");
+return -1;
+}
+
 memcpy(mac_addr, vdev->config.net.mac, ETH_ALEN);
 
 for (i = 0; i < 64; i++) {
-- 
2.45.1




[PATCH] docs/system/s390x/bootdevices: Update loadparm documentation

2024-11-14 Thread jrossi
From: Jared Rossi 

Update documentation to include per-device loadparm support.

Signed-off-by: Jared Rossi 
---
 docs/system/s390x/bootdevices.rst | 24 +++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/docs/system/s390x/bootdevices.rst 
b/docs/system/s390x/bootdevices.rst
index 1a1a764c1c..ecb22de04a 100644
--- a/docs/system/s390x/bootdevices.rst
+++ b/docs/system/s390x/bootdevices.rst
@@ -79,7 +79,29 @@ The second way to use this parameter is to use a number in 
the range from 0
 to 31. The numbers that can be used here correspond to the numbers that are
 shown when using the ``PROMPT`` option, and the s390-ccw bios will then try
 to automatically boot the kernel that is associated with the given number.
-Note that ``0`` can be used to boot the default entry.
+Note that ``0`` can be used to boot the default entry. If the machine
+``loadparm`` is not assigned a value, then the default entry is used.
+
+By default, the machine ``loadparm`` applies to all boot devices. If multiple
+devices are assigned a ``bootindex`` and the ``loadparm`` is to be different
+between them, an independent ``loadparm`` may be assigned on a per-device 
basis.
+
+An example guest using per-device ``loadparm``::
+
+  qemu-system-s390x -drive if=none,id=dr1,file=primary.qcow2 \
+   -device virtio-blk,drive=dr1,bootindex=1 \
+   -drive if=none,id=dr2,file=secondary.qcow2 \
+   -device virtio-blk,drive=dr2,bootindex=2,loadparm=3
+
+In this case, the primary boot device will attempt to IPL using the default
+entry (because no ``loadparm`` is specified for this device or for the
+machine). If that device fails to boot, the secondary device will attempt to
+IPL using entry number 3.
+
+If a ``loadparm`` is specified on both the machine and a device, the per-device
+value will superseded the machine value.  Per-device ``loadparm`` values are
+only used for devices with an assigned ``bootindex``. The machine ``loadparm``
+is used when attempting to boot without a ``bootindex``.
 
 
 Booting from a network device
-- 
2.45.1




[PATCH] pc-bios/s390x: Initialize machine loadparm before probing IPL devices

2024-11-14 Thread jrossi
From: Jared Rossi 

Commit bb185de423 ("s390x: Add individual loadparm assignment to
CCW device") allowed boot devices to be assigned a loadparm value independent
of the machine value, however, when no boot devices are defined, the machine
loadparm becomes ignored. Therefore, let's check the machine loadparm
prior to probing the devices.

Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/main.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
index a4d1c05aac..a6cc6d7906 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -191,7 +191,7 @@ static void boot_setup(void)
 {
 char lpmsg[] = "LOADPARM=[]\n";
 
-if (memcmp(iplb.loadparm, NO_LOADPARM, LOADPARM_LEN) != 0) {
+if (have_iplb && memcmp(iplb.loadparm, NO_LOADPARM, LOADPARM_LEN) != 0) {
 ebcdic_to_ascii((char *) iplb.loadparm, loadparm_str, LOADPARM_LEN);
 } else {
 sclp_get_loadparm_ascii(loadparm_str);
@@ -315,6 +315,7 @@ void main(void)
 css_setup();
 have_iplb = store_iplb(&iplb);
 if (!have_iplb) {
+boot_setup();
 probe_boot_device();
 }
 
-- 
2.45.1




[PATCH 1/1] pc-bios/s390-ccw: Abort IPL on invalid loadparm

2025-01-17 Thread jrossi
From: Jared Rossi 

Because the loadparm specifies an exact kernel the user wants to boot, if the
loadparm is invalid it must represent a misconfiguration of the guest. Thus we
should abort the IPL immediately, without attempting to use other devices, to
avoid booting into an unintended guest image.

Signed-off-by: Jared Rossi 
---
 pc-bios/s390-ccw/bootmap.c | 15 +--
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 56f2f75640..0f8baa0198 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -336,8 +336,7 @@ static int run_eckd_boot_script(block_number_t bmt_block_nr,
 
 debug_print_int("loadparm", loadparm);
 if (loadparm >= MAX_BOOT_ENTRIES) {
-puts("loadparm value greater than max number of boot entries allowed");
-return -EINVAL;
+panic("loadparm value greater than max number of boot entries 
allowed");
 }
 
 memset(sec, FREE_SPACE_FILLER, sizeof(sec));
@@ -348,8 +347,8 @@ static int run_eckd_boot_script(block_number_t bmt_block_nr,
 
 block_nr = gen_eckd_block_num(&bmt->entry[loadparm].xeckd, ldipl);
 if (block_nr == NULL_BLOCK_NR) {
-puts("Cannot find Boot Map Table Entry");
-return -EIO;
+printf("The requested boot entry (%d) is invalid\n", loadparm);
+panic("Invalid loadparm");
 }
 
 memset(sec, FREE_SPACE_FILLER, sizeof(sec));
@@ -792,8 +791,12 @@ static int ipl_scsi(void)
 
 debug_print_int("loadparm", loadparm);
 if (loadparm >= MAX_BOOT_ENTRIES) {
-puts("loadparm value greater than max number of boot entries allowed");
-return -EINVAL;
+panic("loadparm value greater than max number of boot entries 
allowed");
+}
+
+if (!valid_entries[loadparm]) {
+printf("The requested boot entry (%d) is invalid\n", loadparm);
+panic("Invalid loadparm");
 }
 
 return zipl_run(&prog_table->entry[loadparm].scsi);
-- 
2.43.5




[PATCH 0/1] s390x: Abort immediately on invalid loadparm

2025-01-17 Thread jrossi
From: Jared Rossi 

This is a small fix to the IPL behavior in case the user has entered an invalid
loadparm.  The loadparm is a very specific value, which must be deliberately set
by the user. Therefore, if it is not valid, then it is a mistake in the guest 
configuration.

As such, we immediately terminate the IPL process if the loadparm is invalid,
either because it is too large or because it points to an empty boot entry.

Jared Rossi (1):
  pc-bios/s390-ccw: Abort IPL on invalid loadparm

 pc-bios/s390-ccw/bootmap.c | 15 +--
 1 file changed, 9 insertions(+), 6 deletions(-)

-- 
2.43.5




[PATCH 1/1] hw/s390x: Use preferred API call for IPLB chain write

2025-06-23 Thread jrossi
From: Jared Rossi 

Replace a recently introduced legacy API call with the preferred API call.

fixes: 0927875 (hw/s390x: Build an IPLB for each boot device)
Signed-off-by: Jared Rossi 
---
 hw/s390x/ipl.c | 10 +-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 2f082396c7..f2606303e6 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -399,8 +399,16 @@ static uint64_t s390_ipl_map_iplb_chain(IplParameterBlock 
*iplb_chain)
 uint16_t count = be16_to_cpu(ipl->qipl.chain_len);
 uint64_t len = sizeof(IplParameterBlock) * count;
 uint64_t chain_addr = find_iplb_chain_addr(ipl->bios_start_addr, count);
+MemTxResult ret;
+
+ret = address_space_write(&address_space_memory, chain_addr,
+MEMTXATTRS_UNSPECIFIED, iplb_chain, len);
+
+if (ret != MEMTX_OK) {
+error_report("Failed to map IPLB chain.");
+exit(1);
+}
 
-cpu_physical_memory_write(chain_addr, iplb_chain, len);
 return chain_addr;
 }
 
-- 
2.49.0




[PATCH 0/1] hw/s390x: Remediate legacy API call

2025-06-23 Thread jrossi
From: Jared Rossi 

Remediate the use of a legacy API call introduced by a recent patch.

Jared Rossi (1):
  hw/s390x: Use preferred API call for IPLB chain write

 hw/s390x/ipl.c | 10 +-
 1 file changed, 9 insertions(+), 1 deletion(-)

-- 
2.49.0