271 lines
9.7 KiB
Diff
271 lines
9.7 KiB
Diff
|
From 416ee0a87ee4bfedf07bc37d328066375b36fdc1 Mon Sep 17 00:00:00 2001
|
||
|
From: Jared Rossi <jrossi@linux.ibm.com>
|
||
|
Date: Sat, 19 Oct 2024 21:29:49 -0400
|
||
|
Subject: [PATCH 17/38] hw/s390x: Build an IPLB for each boot device
|
||
|
MIME-Version: 1.0
|
||
|
Content-Type: text/plain; charset=UTF-8
|
||
|
Content-Transfer-Encoding: 8bit
|
||
|
|
||
|
RH-Author: Thomas Huth <thuth@redhat.com>
|
||
|
RH-MergeRequest: 278: Full boot order support for s390x [Centos 10]
|
||
|
RH-Jira: RHEL-58153
|
||
|
RH-Acked-by: Cédric Le Goater <clg@redhat.com>
|
||
|
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||
|
RH-Commit: [16/23] 40a579b400cebd1470bb632869ad4a5581e3c41f (thuth/qemu-kvm-cs9)
|
||
|
|
||
|
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 <jrossi@linux.ibm.com>
|
||
|
Reviewed-by: Thomas Huth <thuth@redhat.com>
|
||
|
Message-ID: <20241020012953.1380075-16-jrossi@linux.ibm.com>
|
||
|
[thuth: Fix endianness problem when accessing the qipl structure]
|
||
|
Signed-off-by: Thomas Huth <thuth@redhat.com>
|
||
|
(cherry picked from commit 0927875e704e93ace03bb7533c0877bf97e4bda9)
|
||
|
---
|
||
|
hw/s390x/ipl.c | 129 ++++++++++++++++++++++++++++--------
|
||
|
hw/s390x/ipl.h | 1 +
|
||
|
include/hw/s390x/ipl/qipl.h | 4 +-
|
||
|
3 files changed, 105 insertions(+), 29 deletions(-)
|
||
|
|
||
|
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
|
||
|
index d83832d975..f4576f8822 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 = 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);
|
||
|
+
|
||
|
+ 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 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:
|
||
|
- ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
|
||
|
- ipl->iplb.blk0_len =
|
||
|
+ iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
|
||
|
+ iplb->blk0_len =
|
||
|
cpu_to_be32(S390_IPLB_MIN_CCW_LEN - S390_IPLB_HEADER_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->pbt = S390_IPL_TYPE_CCW;
|
||
|
+ iplb->ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
|
||
|
+ iplb->ccw.ssid = ccw_dev->sch->ssid & 3;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
@@ -484,8 +499,8 @@ static bool s390_gen_initial_iplb(S390IPLState *ipl)
|
||
|
lp = S390_CCW_MACHINE(qdev_get_machine())->loadparm;
|
||
|
}
|
||
|
|
||
|
- s390_ipl_convert_loadparm((char *)lp, ipl->iplb.loadparm);
|
||
|
- ipl->iplb.flags |= DIAG308_FLAGS_LP_VALID;
|
||
|
+ s390_ipl_convert_loadparm((char *)lp, iplb->loadparm);
|
||
|
+ iplb->flags |= DIAG308_FLAGS_LP_VALID;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
@@ -493,6 +508,62 @@ static bool s390_gen_initial_iplb(S390IPLState *ipl)
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
+static bool s390_init_all_iplbs(S390IPLState *ipl)
|
||
|
+{
|
||
|
+ int iplb_num = 0;
|
||
|
+ IplParameterBlock iplb_chain[7];
|
||
|
+ DeviceState *dev_st = get_boot_device(0);
|
||
|
+ Object *machine = qdev_get_machine();
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Parse the boot devices. Generate an IPLB for only the first boot device
|
||
|
+ * which will later be set with DIAG308.
|
||
|
+ */
|
||
|
+ if (!dev_st) {
|
||
|
+ ipl->qipl.chain_len = 0;
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* If no machine loadparm was defined fill it with spaces */
|
||
|
+ if (memcmp(S390_CCW_MACHINE(machine)->loadparm, NO_LOADPARM, 8) == 0) {
|
||
|
+ object_property_set_str(machine, "loadparm", " ", NULL);
|
||
|
+ }
|
||
|
+
|
||
|
+ iplb_num = 1;
|
||
|
+ s390_build_iplb(dev_st, &ipl->iplb);
|
||
|
+
|
||
|
+ /* Index any fallback boot devices */
|
||
|
+ while (get_boot_device(iplb_num)) {
|
||
|
+ iplb_num++;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (iplb_num > MAX_BOOT_DEVS) {
|
||
|
+ warn_report("Excess boot devices defined! %d boot devices found, "
|
||
|
+ "but only the first %d will be considered.",
|
||
|
+ iplb_num, MAX_BOOT_DEVS);
|
||
|
+
|
||
|
+ iplb_num = MAX_BOOT_DEVS;
|
||
|
+ }
|
||
|
+
|
||
|
+ ipl->qipl.chain_len = cpu_to_be16(iplb_num - 1);
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Build fallback IPLBs for any boot devices above index 0, up to a
|
||
|
+ * maximum amount as defined in ipl.h
|
||
|
+ */
|
||
|
+ if (iplb_num > 1) {
|
||
|
+ /* Start at 1 because the IPLB for boot index 0 is not chained */
|
||
|
+ for (int i = 1; i < iplb_num; i++) {
|
||
|
+ dev_st = get_boot_device(i);
|
||
|
+ s390_build_iplb(dev_st, &iplb_chain[i - 1]);
|
||
|
+ }
|
||
|
+
|
||
|
+ ipl->qipl.next_iplb = cpu_to_be64(s390_ipl_map_iplb_chain(iplb_chain));
|
||
|
+ }
|
||
|
+
|
||
|
+ return iplb_num;
|
||
|
+}
|
||
|
+
|
||
|
static bool is_virtio_ccw_device_of_type(IplParameterBlock *iplb,
|
||
|
int virtio_id)
|
||
|
{
|
||
|
@@ -620,7 +691,7 @@ void s390_ipl_reset_request(CPUState *cs, enum s390_reset reset_type)
|
||
|
* this is the original boot device's SCSI
|
||
|
* so restore IPL parameter info from it
|
||
|
*/
|
||
|
- ipl->iplb_valid = s390_gen_initial_iplb(ipl);
|
||
|
+ ipl->iplb_valid = s390_build_iplb(get_boot_device(0), &ipl->iplb);
|
||
|
}
|
||
|
}
|
||
|
if (reset_type == S390_RESET_MODIFIED_CLEAR ||
|
||
|
@@ -714,7 +785,9 @@ void s390_ipl_prepare_cpu(S390CPU *cpu)
|
||
|
if (!ipl->kernel || ipl->iplb_valid) {
|
||
|
cpu->env.psw.addr = ipl->bios_start_addr;
|
||
|
if (!ipl->iplb_valid) {
|
||
|
- ipl->iplb_valid = s390_gen_initial_iplb(ipl);
|
||
|
+ ipl->iplb_valid = s390_init_all_iplbs(ipl);
|
||
|
+ } else {
|
||
|
+ ipl->qipl.chain_len = 0;
|
||
|
}
|
||
|
}
|
||
|
s390_ipl_set_boot_menu(ipl);
|
||
|
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;
|
||
|
|
||
|
--
|
||
|
2.39.3
|
||
|
|