diff mbox series

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

Message ID 20240927005117.1679506-16-jrossi@linux.ibm.com (mailing list archive)
State New, archived
Headers show
Series s390x: Add Full Boot Order Support | expand

Commit Message

Jared Rossi Sept. 27, 2024, 12:51 a.m. UTC
From: Jared Rossi <jrossi@linux.ibm.com>

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>

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

Comments

Thomas Huth Sept. 30, 2024, 11:59 a.m. UTC | #1
On 27/09/2024 02.51, jrossi@linux.ibm.com wrote:
> From: Jared Rossi <jrossi@linux.ibm.com>
> 
> 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>
> 

Note: There's an empty line below your SoB in most of your patch 
descriptions ... it's just cosmetics, but in case you touch the patch 
anyway, please remove it.

> ---
...
> @@ -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,58 @@ 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);
> +
> +    /*
> +     * Parse the boot devices.  Generate an IPLB for the first boot device,
> +     * which will later be set with DIAG308. Index any fallback boot devices.
> +     */

The comment looks like it rather belongs to the whole function, and not to 
the if-statement below? So maybe move it at the top?

> +    if (!dev_st) {
> +        ipl->qipl.chain_len = 0;
> +        return false;
> +    }
> +
> +    iplb_num = 1;
> +    s390_build_iplb(dev_st, &ipl->iplb);
> +    ipl->iplb.flags |= DIAG308_FLAGS_LP_VALID;

Isn't DIAG308_FLAGS_LP_VALID set within s390_build_iplb() already?

> +    while (get_boot_device(iplb_num)) {
> +        iplb_num++;
> +    }

I'd maybe move the code block below to this spot here, so you've got to 
assign ipl->qipl.chain_len only once:

+    if (iplb_num > MAX_IPLB_CHAIN + 1) {
+        warn_report("Excess boot devices defined! %d boot devices found, "
+                    "but only the first %d will be considered.",
+                    iplb_num, MAX_IPLB_CHAIN + 1);
+
+        iplb_num = MAX_IPLB_CHAIN + 1;
+    }

> +    ipl->qipl.chain_len = 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) {
> +        if (iplb_num > MAX_IPLB_CHAIN + 1) {
> +            warn_report("Excess boot devices defined! %d boot devices found, "
> +                        "but only the first %d will be considered.",
> +                        iplb_num, MAX_IPLB_CHAIN + 1);
> +
> +            ipl->qipl.chain_len = MAX_IPLB_CHAIN;
> +            iplb_num = MAX_IPLB_CHAIN + 1;
> +        }

i.e. move this code block -^
and remove the "ipl->qipl.chain_len = MAX_IPLB_CHAIN;" in there.

> +        /* 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]);
> +            iplb_chain[i - 1].flags |= DIAG308_FLAGS_LP_VALID;

Again, setting DIAG308_FLAGS_LP_VALID should not be necessary since it is 
already done in s390_build_iplb() ?

> +        }
> +
> +        ipl->qipl.next_iplb = 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 +687,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 +781,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);

  Thomas
Jared Rossi Sept. 30, 2024, 1:39 p.m. UTC | #2
On 9/30/24 7:59 AM, Thomas Huth wrote:
> On 27/09/2024 02.51, jrossi@linux.ibm.com wrote:
>> From: Jared Rossi <jrossi@linux.ibm.com>
>> ... 
>> @@ -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,58 @@ 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);
>> +
>> +    /*
>> +     * Parse the boot devices.  Generate an IPLB for the first boot 
>> device,
>> +     * which will later be set with DIAG308. Index any fallback boot 
>> devices.
>> +     */
>
> The comment looks like it rather belongs to the whole function, and 
> not to the if-statement below? So maybe move it at the top?

Agreed.

>
>> +    if (!dev_st) {
>> +        ipl->qipl.chain_len = 0;
>> +        return false;
>> +    }
>> +
>> +    iplb_num = 1;
>> +    s390_build_iplb(dev_st, &ipl->iplb);
>> +    ipl->iplb.flags |= DIAG308_FLAGS_LP_VALID;
>
> Isn't DIAG308_FLAGS_LP_VALID set within s390_build_iplb() already?
>
>> +    while (get_boot_device(iplb_num)) {
>> +        iplb_num++;
>> +    }
>
> I'd maybe move the code block below to this spot here, so you've got 
> to assign ipl->qipl.chain_len only once:
>
> +    if (iplb_num > MAX_IPLB_CHAIN + 1) {
> +        warn_report("Excess boot devices defined! %d boot devices 
> found, "
> +                    "but only the first %d will be considered.",
> +                    iplb_num, MAX_IPLB_CHAIN + 1);
> +
> +        iplb_num = MAX_IPLB_CHAIN + 1;
> +    }
>
>> +    ipl->qipl.chain_len = 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) {
>> +        if (iplb_num > MAX_IPLB_CHAIN + 1) {
>> +            warn_report("Excess boot devices defined! %d boot 
>> devices found, "
>> +                        "but only the first %d will be considered.",
>> +                        iplb_num, MAX_IPLB_CHAIN + 1);
>> +
>> +            ipl->qipl.chain_len = MAX_IPLB_CHAIN;
>> +            iplb_num = MAX_IPLB_CHAIN + 1;
>> +        }
>
> i.e. move this code block -^
> and remove the "ipl->qipl.chain_len = MAX_IPLB_CHAIN;" in there.
>
>> +        /* 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]);
>> +            iplb_chain[i - 1].flags |= DIAG308_FLAGS_LP_VALID;
>
> Again, setting DIAG308_FLAGS_LP_VALID should not be necessary since it 
> is already done in s390_build_iplb() ?
>

I’ll see if I can clean section this up and remove redundant flag 
assignments.

Jared Rossi
diff mbox series

Patch

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 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,58 @@  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);
+
+    /*
+     * Parse the boot devices.  Generate an IPLB for the first boot device,
+     * which will later be set with DIAG308. Index any fallback boot devices.
+     */
+    if (!dev_st) {
+        ipl->qipl.chain_len = 0;
+        return false;
+    }
+
+    iplb_num = 1;
+    s390_build_iplb(dev_st, &ipl->iplb);
+    ipl->iplb.flags |= DIAG308_FLAGS_LP_VALID;
+
+    while (get_boot_device(iplb_num)) {
+        iplb_num++;
+    }
+
+    ipl->qipl.chain_len = 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) {
+        if (iplb_num > MAX_IPLB_CHAIN + 1) {
+            warn_report("Excess boot devices defined! %d boot devices found, "
+                        "but only the first %d will be considered.",
+                        iplb_num, MAX_IPLB_CHAIN + 1);
+
+            ipl->qipl.chain_len = MAX_IPLB_CHAIN;
+            iplb_num = MAX_IPLB_CHAIN + 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]);
+            iplb_chain[i - 1].flags |= DIAG308_FLAGS_LP_VALID;
+        }
+
+        ipl->qipl.next_iplb = 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 +687,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 +781,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);