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
 | |
| 
 |