Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 1 | /* |
| 2 | * bootloader support |
| 3 | * |
Janosch Frank | c3347ed | 2020-03-23 04:36:06 -0400 | [diff] [blame] | 4 | * Copyright IBM, Corp. 2012, 2020 |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 5 | * |
| 6 | * Authors: |
| 7 | * Christian Borntraeger <borntraeger@de.ibm.com> |
Janosch Frank | c3347ed | 2020-03-23 04:36:06 -0400 | [diff] [blame] | 8 | * Janosch Frank <frankja@linux.ibm.com> |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 9 | * |
| 10 | * This work is licensed under the terms of the GNU GPL, version 2 or (at your |
| 11 | * option) any later version. See the COPYING file in the top-level directory. |
| 12 | * |
| 13 | */ |
| 14 | |
Peter Maydell | 9615495 | 2016-01-26 18:17:00 +0000 | [diff] [blame] | 15 | #include "qemu/osdep.h" |
Markus Armbruster | a8d2532 | 2019-05-23 16:35:08 +0200 | [diff] [blame] | 16 | #include "qemu-common.h" |
Paolo Bonzini | 2c65db5 | 2020-10-28 07:36:57 -0400 | [diff] [blame] | 17 | #include "qemu/datadir.h" |
Markus Armbruster | da34e65 | 2016-03-14 09:01:28 +0100 | [diff] [blame] | 18 | #include "qapi/error.h" |
Markus Armbruster | 71e8a91 | 2019-08-12 07:23:38 +0200 | [diff] [blame] | 19 | #include "sysemu/reset.h" |
Markus Armbruster | 54d3123 | 2019-08-12 07:23:59 +0200 | [diff] [blame] | 20 | #include "sysemu/runstate.h" |
Markus Armbruster | 14a48c1 | 2019-05-23 16:35:05 +0200 | [diff] [blame] | 21 | #include "sysemu/tcg.h" |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 22 | #include "elf.h" |
| 23 | #include "hw/loader.h" |
Markus Armbruster | a27bd6c | 2019-08-12 07:23:51 +0200 | [diff] [blame] | 24 | #include "hw/qdev-properties.h" |
Farhan Ali | bd1badf | 2016-03-29 16:28:40 +0200 | [diff] [blame] | 25 | #include "hw/boards.h" |
Dominik Dingel | ba1509c | 2013-04-30 07:15:57 +0000 | [diff] [blame] | 26 | #include "hw/s390x/virtio-ccw.h" |
Jason J. Herne | 44445d8 | 2019-04-04 10:34:20 -0400 | [diff] [blame] | 27 | #include "hw/s390x/vfio-ccw.h" |
Dominik Dingel | ba1509c | 2013-04-30 07:15:57 +0000 | [diff] [blame] | 28 | #include "hw/s390x/css.h" |
Farhan Ali | bd1badf | 2016-03-29 16:28:40 +0200 | [diff] [blame] | 29 | #include "hw/s390x/ebcdic.h" |
Janosch Frank | c3347ed | 2020-03-23 04:36:06 -0400 | [diff] [blame] | 30 | #include "hw/s390x/pv.h" |
Fan Zhang | df75a4e | 2015-02-12 18:02:14 +0100 | [diff] [blame] | 31 | #include "ipl.h" |
Farhan Ali | f38b5b7 | 2016-10-20 17:59:20 -0400 | [diff] [blame] | 32 | #include "qemu/error-report.h" |
Collin L. Walling | 26b2a2a | 2018-02-23 10:43:12 -0500 | [diff] [blame] | 33 | #include "qemu/config-file.h" |
| 34 | #include "qemu/cutils.h" |
| 35 | #include "qemu/option.h" |
David Hildenbrand | a30fb81 | 2018-04-24 12:18:59 +0200 | [diff] [blame] | 36 | #include "exec/exec-all.h" |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 37 | |
| 38 | #define KERN_IMAGE_START 0x010000UL |
Christian Borntraeger | acd7ef8 | 2018-06-12 14:59:33 +0200 | [diff] [blame] | 39 | #define LINUX_MAGIC_ADDR 0x010008UL |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 40 | #define KERN_PARM_AREA 0x010480UL |
| 41 | #define INITRD_START 0x800000UL |
| 42 | #define INITRD_PARM_START 0x010408UL |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 43 | #define PARMFILE_START 0x001000UL |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 44 | #define ZIPL_IMAGE_START 0x009000UL |
| 45 | #define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64) |
| 46 | |
Alexander Yarygin | 04ca4b9 | 2015-07-13 15:04:36 +0300 | [diff] [blame] | 47 | static bool iplb_extended_needed(void *opaque) |
| 48 | { |
| 49 | S390IPLState *ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL)); |
| 50 | |
| 51 | return ipl->iplbext_migration; |
| 52 | } |
| 53 | |
| 54 | static const VMStateDescription vmstate_iplb_extended = { |
| 55 | .name = "ipl/iplb_extended", |
| 56 | .version_id = 0, |
| 57 | .minimum_version_id = 0, |
| 58 | .needed = iplb_extended_needed, |
| 59 | .fields = (VMStateField[]) { |
| 60 | VMSTATE_UINT8_ARRAY(reserved_ext, IplParameterBlock, 4096 - 200), |
| 61 | VMSTATE_END_OF_LIST() |
| 62 | } |
| 63 | }; |
| 64 | |
Fan Zhang | 2e13fbe | 2015-02-12 18:02:16 +0100 | [diff] [blame] | 65 | static const VMStateDescription vmstate_iplb = { |
| 66 | .name = "ipl/iplb", |
| 67 | .version_id = 0, |
| 68 | .minimum_version_id = 0, |
| 69 | .fields = (VMStateField[]) { |
| 70 | VMSTATE_UINT8_ARRAY(reserved1, IplParameterBlock, 110), |
| 71 | VMSTATE_UINT16(devno, IplParameterBlock), |
| 72 | VMSTATE_UINT8_ARRAY(reserved2, IplParameterBlock, 88), |
| 73 | VMSTATE_END_OF_LIST() |
Alexander Yarygin | 04ca4b9 | 2015-07-13 15:04:36 +0300 | [diff] [blame] | 74 | }, |
| 75 | .subsections = (const VMStateDescription*[]) { |
| 76 | &vmstate_iplb_extended, |
| 77 | NULL |
Fan Zhang | 2e13fbe | 2015-02-12 18:02:16 +0100 | [diff] [blame] | 78 | } |
| 79 | }; |
| 80 | |
| 81 | static const VMStateDescription vmstate_ipl = { |
| 82 | .name = "ipl", |
| 83 | .version_id = 0, |
| 84 | .minimum_version_id = 0, |
| 85 | .fields = (VMStateField[]) { |
David Hildenbrand | bb09954 | 2016-06-09 15:36:41 +0200 | [diff] [blame] | 86 | VMSTATE_UINT64(compat_start_addr, S390IPLState), |
| 87 | VMSTATE_UINT64(compat_bios_start_addr, S390IPLState), |
Fan Zhang | 2e13fbe | 2015-02-12 18:02:16 +0100 | [diff] [blame] | 88 | VMSTATE_STRUCT(iplb, S390IPLState, 0, vmstate_iplb, IplParameterBlock), |
| 89 | VMSTATE_BOOL(iplb_valid, S390IPLState), |
| 90 | VMSTATE_UINT8(cssid, S390IPLState), |
| 91 | VMSTATE_UINT8(ssid, S390IPLState), |
| 92 | VMSTATE_UINT16(devno, S390IPLState), |
| 93 | VMSTATE_END_OF_LIST() |
| 94 | } |
| 95 | }; |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 96 | |
David Hildenbrand | feacc6c | 2015-06-25 09:55:55 +0200 | [diff] [blame] | 97 | static S390IPLState *get_ipl_device(void) |
| 98 | { |
| 99 | return S390_IPL(object_resolve_path_type("", TYPE_S390_IPL, NULL)); |
| 100 | } |
| 101 | |
Thomas Huth | d884c86 | 2015-03-09 11:12:53 +0100 | [diff] [blame] | 102 | static uint64_t bios_translate_addr(void *opaque, uint64_t srcaddr) |
| 103 | { |
| 104 | uint64_t dstaddr = *(uint64_t *) opaque; |
| 105 | /* |
| 106 | * Assuming that our s390-ccw.img was linked for starting at address 0, |
| 107 | * we can simply add the destination address for the final location |
| 108 | */ |
| 109 | return srcaddr + dstaddr; |
| 110 | } |
| 111 | |
David Hildenbrand | 04fccf1 | 2015-10-08 12:32:13 +0200 | [diff] [blame] | 112 | static void s390_ipl_realize(DeviceState *dev, Error **errp) |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 113 | { |
Paolo Bonzini | 382a04a | 2020-10-28 06:17:07 -0400 | [diff] [blame] | 114 | MachineState *ms = MACHINE(qdev_get_machine()); |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 115 | S390IPLState *ipl = S390_IPL(dev); |
Christian Borntraeger | acd7ef8 | 2018-06-12 14:59:33 +0200 | [diff] [blame] | 116 | uint32_t *ipl_psw; |
| 117 | uint64_t pentry; |
| 118 | char *magic; |
Michael S. Tsirkin | 376827d | 2013-11-21 15:34:26 +0200 | [diff] [blame] | 119 | int kernel_size; |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 120 | |
Fan Zhang | f0180f9 | 2015-02-12 18:02:13 +0100 | [diff] [blame] | 121 | int bios_size; |
| 122 | char *bios_filename; |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 123 | |
Fan Zhang | f0180f9 | 2015-02-12 18:02:13 +0100 | [diff] [blame] | 124 | /* |
| 125 | * Always load the bios if it was enforced, |
| 126 | * even if an external kernel has been defined. |
| 127 | */ |
| 128 | if (!ipl->kernel || ipl->enforce_bios) { |
Paolo Bonzini | 382a04a | 2020-10-28 06:17:07 -0400 | [diff] [blame] | 129 | uint64_t fwbase = (MIN(ms->ram_size, 0x80000000U) - 0x200000) & ~0xffffUL; |
Thomas Huth | d884c86 | 2015-03-09 11:12:53 +0100 | [diff] [blame] | 130 | |
Paolo Bonzini | f034439 | 2020-10-26 10:30:25 -0400 | [diff] [blame] | 131 | bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->firmware); |
Dominik Dingel | 1f7de85 | 2013-04-29 04:52:05 +0000 | [diff] [blame] | 132 | if (bios_filename == NULL) { |
Markus Armbruster | dcfe480 | 2020-07-07 18:06:01 +0200 | [diff] [blame] | 133 | error_setg(errp, "could not find stage1 bootloader"); |
| 134 | return; |
Dominik Dingel | 1f7de85 | 2013-04-29 04:52:05 +0000 | [diff] [blame] | 135 | } |
| 136 | |
Liam Merwick | 4366e1d | 2019-01-15 12:18:03 +0000 | [diff] [blame] | 137 | bios_size = load_elf(bios_filename, NULL, |
| 138 | bios_translate_addr, &fwbase, |
Aleksandar Markovic | 6cdda0f | 2020-01-26 23:55:04 +0100 | [diff] [blame] | 139 | &ipl->bios_start_addr, NULL, NULL, NULL, 1, |
Peter Crosthwaite | 7ef295e | 2016-03-04 11:30:21 +0000 | [diff] [blame] | 140 | EM_S390, 0, 0); |
Thomas Huth | d884c86 | 2015-03-09 11:12:53 +0100 | [diff] [blame] | 141 | if (bios_size > 0) { |
| 142 | /* Adjust ELF start address to final location */ |
| 143 | ipl->bios_start_addr += fwbase; |
| 144 | } else { |
Michael Tokarev | f35c1f6 | 2016-02-16 09:29:07 +0300 | [diff] [blame] | 145 | /* Try to load non-ELF file */ |
Alexander Graf | 3325995 | 2013-04-22 16:47:32 +0200 | [diff] [blame] | 146 | bios_size = load_image_targphys(bios_filename, ZIPL_IMAGE_START, |
| 147 | 4096); |
Fan Zhang | f0180f9 | 2015-02-12 18:02:13 +0100 | [diff] [blame] | 148 | ipl->bios_start_addr = ZIPL_IMAGE_START; |
Alexander Graf | 3325995 | 2013-04-22 16:47:32 +0200 | [diff] [blame] | 149 | } |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 150 | g_free(bios_filename); |
| 151 | |
Michael S. Tsirkin | 376827d | 2013-11-21 15:34:26 +0200 | [diff] [blame] | 152 | if (bios_size == -1) { |
Paolo Bonzini | f034439 | 2020-10-26 10:30:25 -0400 | [diff] [blame] | 153 | error_setg(errp, "could not load bootloader '%s'", ipl->firmware); |
Markus Armbruster | dcfe480 | 2020-07-07 18:06:01 +0200 | [diff] [blame] | 154 | return; |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 155 | } |
Fan Zhang | f0180f9 | 2015-02-12 18:02:13 +0100 | [diff] [blame] | 156 | |
| 157 | /* default boot target is the bios */ |
| 158 | ipl->start_addr = ipl->bios_start_addr; |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 159 | } |
Thomas Huth | 7691993 | 2014-02-11 09:12:27 +0100 | [diff] [blame] | 160 | |
Fan Zhang | f0180f9 | 2015-02-12 18:02:13 +0100 | [diff] [blame] | 161 | if (ipl->kernel) { |
Liam Merwick | 4366e1d | 2019-01-15 12:18:03 +0000 | [diff] [blame] | 162 | kernel_size = load_elf(ipl->kernel, NULL, NULL, NULL, |
| 163 | &pentry, NULL, |
Aleksandar Markovic | 6cdda0f | 2020-01-26 23:55:04 +0100 | [diff] [blame] | 164 | NULL, NULL, 1, EM_S390, 0, 0); |
Fan Zhang | f0180f9 | 2015-02-12 18:02:13 +0100 | [diff] [blame] | 165 | if (kernel_size < 0) { |
Paolo Bonzini | 382a04a | 2020-10-28 06:17:07 -0400 | [diff] [blame] | 166 | kernel_size = load_image_targphys(ipl->kernel, 0, ms->ram_size); |
Christian Borntraeger | acd7ef8 | 2018-06-12 14:59:33 +0200 | [diff] [blame] | 167 | if (kernel_size < 0) { |
Markus Armbruster | dcfe480 | 2020-07-07 18:06:01 +0200 | [diff] [blame] | 168 | error_setg(errp, "could not load kernel '%s'", ipl->kernel); |
| 169 | return; |
Christian Borntraeger | acd7ef8 | 2018-06-12 14:59:33 +0200 | [diff] [blame] | 170 | } |
| 171 | /* if this is Linux use KERN_IMAGE_START */ |
Thomas Huth | 0f0f8b6 | 2018-06-26 11:35:40 +0200 | [diff] [blame] | 172 | magic = rom_ptr(LINUX_MAGIC_ADDR, 6); |
Christian Borntraeger | acd7ef8 | 2018-06-12 14:59:33 +0200 | [diff] [blame] | 173 | if (magic && !memcmp(magic, "S390EP", 6)) { |
| 174 | pentry = KERN_IMAGE_START; |
| 175 | } else { |
| 176 | /* if not Linux load the address of the (short) IPL PSW */ |
Thomas Huth | 0f0f8b6 | 2018-06-26 11:35:40 +0200 | [diff] [blame] | 177 | ipl_psw = rom_ptr(4, 4); |
Christian Borntraeger | acd7ef8 | 2018-06-12 14:59:33 +0200 | [diff] [blame] | 178 | if (ipl_psw) { |
Janosch Frank | b6c2dbd | 2020-02-27 04:23:41 -0500 | [diff] [blame] | 179 | pentry = be32_to_cpu(*ipl_psw) & PSW_MASK_SHORT_ADDR; |
Christian Borntraeger | acd7ef8 | 2018-06-12 14:59:33 +0200 | [diff] [blame] | 180 | } else { |
Markus Armbruster | dcfe480 | 2020-07-07 18:06:01 +0200 | [diff] [blame] | 181 | error_setg(errp, "Could not get IPL PSW"); |
| 182 | return; |
Christian Borntraeger | acd7ef8 | 2018-06-12 14:59:33 +0200 | [diff] [blame] | 183 | } |
| 184 | } |
Fan Zhang | f0180f9 | 2015-02-12 18:02:13 +0100 | [diff] [blame] | 185 | } |
| 186 | /* |
| 187 | * Is it a Linux kernel (starting at 0x10000)? If yes, we fill in the |
| 188 | * kernel parameters here as well. Note: For old kernels (up to 3.2) |
| 189 | * we can not rely on the ELF entry point - it was 0x800 (the SALIPL |
| 190 | * loader) and it won't work. For this case we force it to 0x10000, too. |
| 191 | */ |
| 192 | if (pentry == KERN_IMAGE_START || pentry == 0x800) { |
Thomas Huth | 0f0f8b6 | 2018-06-26 11:35:40 +0200 | [diff] [blame] | 193 | char *parm_area = rom_ptr(KERN_PARM_AREA, strlen(ipl->cmdline) + 1); |
Fan Zhang | f0180f9 | 2015-02-12 18:02:13 +0100 | [diff] [blame] | 194 | ipl->start_addr = KERN_IMAGE_START; |
| 195 | /* Overwrite parameters in the kernel image, which are "rom" */ |
Thomas Huth | 0f0f8b6 | 2018-06-26 11:35:40 +0200 | [diff] [blame] | 196 | if (parm_area) { |
| 197 | strcpy(parm_area, ipl->cmdline); |
| 198 | } |
Fan Zhang | f0180f9 | 2015-02-12 18:02:13 +0100 | [diff] [blame] | 199 | } else { |
| 200 | ipl->start_addr = pentry; |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 201 | } |
| 202 | |
Fan Zhang | f0180f9 | 2015-02-12 18:02:13 +0100 | [diff] [blame] | 203 | if (ipl->initrd) { |
| 204 | ram_addr_t initrd_offset; |
| 205 | int initrd_size; |
Thomas Huth | 0f0f8b6 | 2018-06-26 11:35:40 +0200 | [diff] [blame] | 206 | uint64_t *romptr; |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 207 | |
Fan Zhang | f0180f9 | 2015-02-12 18:02:13 +0100 | [diff] [blame] | 208 | initrd_offset = INITRD_START; |
| 209 | while (kernel_size + 0x100000 > initrd_offset) { |
| 210 | initrd_offset += 0x100000; |
| 211 | } |
| 212 | initrd_size = load_image_targphys(ipl->initrd, initrd_offset, |
Paolo Bonzini | 382a04a | 2020-10-28 06:17:07 -0400 | [diff] [blame] | 213 | ms->ram_size - initrd_offset); |
Fan Zhang | f0180f9 | 2015-02-12 18:02:13 +0100 | [diff] [blame] | 214 | if (initrd_size == -1) { |
Markus Armbruster | dcfe480 | 2020-07-07 18:06:01 +0200 | [diff] [blame] | 215 | error_setg(errp, "could not load initrd '%s'", ipl->initrd); |
| 216 | return; |
Fan Zhang | f0180f9 | 2015-02-12 18:02:13 +0100 | [diff] [blame] | 217 | } |
| 218 | |
| 219 | /* |
| 220 | * we have to overwrite values in the kernel image, |
| 221 | * which are "rom" |
| 222 | */ |
Thomas Huth | 0f0f8b6 | 2018-06-26 11:35:40 +0200 | [diff] [blame] | 223 | romptr = rom_ptr(INITRD_PARM_START, 16); |
| 224 | if (romptr) { |
| 225 | stq_p(romptr, initrd_offset); |
| 226 | stq_p(romptr + 1, initrd_size); |
| 227 | } |
Fan Zhang | f0180f9 | 2015-02-12 18:02:13 +0100 | [diff] [blame] | 228 | } |
| 229 | } |
David Hildenbrand | bb09954 | 2016-06-09 15:36:41 +0200 | [diff] [blame] | 230 | /* |
| 231 | * Don't ever use the migrated values, they could come from a different |
| 232 | * BIOS and therefore don't work. But still migrate the values, so |
| 233 | * QEMUs relying on it don't break. |
| 234 | */ |
| 235 | ipl->compat_start_addr = ipl->start_addr; |
| 236 | ipl->compat_bios_start_addr = ipl->bios_start_addr; |
Damien Hedde | cd45c50 | 2020-01-30 16:02:05 +0000 | [diff] [blame] | 237 | /* |
| 238 | * Because this Device is not on any bus in the qbus tree (it is |
| 239 | * not a sysbus device and it's not on some other bus like a PCI |
| 240 | * bus) it will not be automatically reset by the 'reset the |
| 241 | * sysbus' hook registered by vl.c like most devices. So we must |
| 242 | * manually register a reset hook for it. |
| 243 | * TODO: there should be a better way to do this. |
| 244 | */ |
| 245 | qemu_register_reset(resettable_cold_reset_fn, dev); |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 246 | } |
| 247 | |
| 248 | static Property s390_ipl_properties[] = { |
| 249 | DEFINE_PROP_STRING("kernel", S390IPLState, kernel), |
| 250 | DEFINE_PROP_STRING("initrd", S390IPLState, initrd), |
| 251 | DEFINE_PROP_STRING("cmdline", S390IPLState, cmdline), |
Alexander Graf | d0249ce | 2013-04-22 16:52:53 +0200 | [diff] [blame] | 252 | DEFINE_PROP_STRING("firmware", S390IPLState, firmware), |
Farhan Ali | 5f31ade | 2016-10-21 12:17:08 -0400 | [diff] [blame] | 253 | DEFINE_PROP_STRING("netboot_fw", S390IPLState, netboot_fw), |
Fan Zhang | f0180f9 | 2015-02-12 18:02:13 +0100 | [diff] [blame] | 254 | DEFINE_PROP_BOOL("enforce_bios", S390IPLState, enforce_bios, false), |
Alexander Yarygin | 04ca4b9 | 2015-07-13 15:04:36 +0300 | [diff] [blame] | 255 | DEFINE_PROP_BOOL("iplbext_migration", S390IPLState, iplbext_migration, |
| 256 | true), |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 257 | DEFINE_PROP_END_OF_LIST(), |
| 258 | }; |
| 259 | |
Collin L. Walling | 26b2a2a | 2018-02-23 10:43:12 -0500 | [diff] [blame] | 260 | static void s390_ipl_set_boot_menu(S390IPLState *ipl) |
| 261 | { |
| 262 | QemuOptsList *plist = qemu_find_opts("boot-opts"); |
| 263 | QemuOpts *opts = QTAILQ_FIRST(&plist->head); |
Collin L. Walling | 26b2a2a | 2018-02-23 10:43:12 -0500 | [diff] [blame] | 264 | const char *tmp; |
| 265 | unsigned long splash_time = 0; |
| 266 | |
| 267 | if (!get_boot_device(0)) { |
| 268 | if (boot_menu) { |
| 269 | error_report("boot menu requires a bootindex to be specified for " |
Collin L. Walling | 5600086 | 2018-02-27 14:35:21 -0500 | [diff] [blame] | 270 | "the IPL device"); |
Collin L. Walling | 26b2a2a | 2018-02-23 10:43:12 -0500 | [diff] [blame] | 271 | } |
| 272 | return; |
| 273 | } |
| 274 | |
| 275 | switch (ipl->iplb.pbt) { |
| 276 | case S390_IPL_TYPE_CCW: |
Collin L. Walling | 53b310c | 2018-02-23 10:43:18 -0500 | [diff] [blame] | 277 | /* In the absence of -boot menu, use zipl parameters */ |
| 278 | if (!qemu_opt_get(opts, "menu")) { |
Daniel P. Berrangé | 5d45a33 | 2019-03-29 11:11:03 +0000 | [diff] [blame] | 279 | ipl->qipl.qipl_flags |= QIPL_FLAG_BM_OPTS_ZIPL; |
Collin L. Walling | 53b310c | 2018-02-23 10:43:18 -0500 | [diff] [blame] | 280 | return; |
| 281 | } |
Collin L. Walling | 26b2a2a | 2018-02-23 10:43:12 -0500 | [diff] [blame] | 282 | break; |
Collin L. Walling | ffb4a1c | 2018-02-23 10:43:19 -0500 | [diff] [blame] | 283 | case S390_IPL_TYPE_QEMU_SCSI: |
| 284 | break; |
Collin L. Walling | 26b2a2a | 2018-02-23 10:43:12 -0500 | [diff] [blame] | 285 | default: |
Collin L. Walling | 5600086 | 2018-02-27 14:35:21 -0500 | [diff] [blame] | 286 | if (boot_menu) { |
| 287 | error_report("boot menu is not supported for this device type"); |
| 288 | } |
Collin L. Walling | 26b2a2a | 2018-02-23 10:43:12 -0500 | [diff] [blame] | 289 | return; |
| 290 | } |
| 291 | |
| 292 | if (!boot_menu) { |
| 293 | return; |
| 294 | } |
| 295 | |
Daniel P. Berrangé | 5d45a33 | 2019-03-29 11:11:03 +0000 | [diff] [blame] | 296 | ipl->qipl.qipl_flags |= QIPL_FLAG_BM_OPTS_CMD; |
Collin L. Walling | 26b2a2a | 2018-02-23 10:43:12 -0500 | [diff] [blame] | 297 | |
| 298 | tmp = qemu_opt_get(opts, "splash-time"); |
| 299 | |
| 300 | if (tmp && qemu_strtoul(tmp, NULL, 10, &splash_time)) { |
Collin L. Walling | 5600086 | 2018-02-27 14:35:21 -0500 | [diff] [blame] | 301 | error_report("splash-time is invalid, forcing it to 0"); |
Daniel P. Berrangé | 5d45a33 | 2019-03-29 11:11:03 +0000 | [diff] [blame] | 302 | ipl->qipl.boot_menu_timeout = 0; |
Collin L. Walling | 26b2a2a | 2018-02-23 10:43:12 -0500 | [diff] [blame] | 303 | return; |
| 304 | } |
| 305 | |
| 306 | if (splash_time > 0xffffffff) { |
Collin L. Walling | 5600086 | 2018-02-27 14:35:21 -0500 | [diff] [blame] | 307 | error_report("splash-time is too large, forcing it to max value"); |
Daniel P. Berrangé | 5d45a33 | 2019-03-29 11:11:03 +0000 | [diff] [blame] | 308 | ipl->qipl.boot_menu_timeout = 0xffffffff; |
Collin L. Walling | 26b2a2a | 2018-02-23 10:43:12 -0500 | [diff] [blame] | 309 | return; |
| 310 | } |
| 311 | |
Daniel P. Berrangé | 5d45a33 | 2019-03-29 11:11:03 +0000 | [diff] [blame] | 312 | ipl->qipl.boot_menu_timeout = cpu_to_be32(splash_time); |
Collin L. Walling | 26b2a2a | 2018-02-23 10:43:12 -0500 | [diff] [blame] | 313 | } |
| 314 | |
Jason J. Herne | 44445d8 | 2019-04-04 10:34:20 -0400 | [diff] [blame] | 315 | #define CCW_DEVTYPE_NONE 0x00 |
| 316 | #define CCW_DEVTYPE_VIRTIO 0x01 |
| 317 | #define CCW_DEVTYPE_VIRTIO_NET 0x02 |
| 318 | #define CCW_DEVTYPE_SCSI 0x03 |
| 319 | #define CCW_DEVTYPE_VFIO 0x04 |
| 320 | |
| 321 | static CcwDevice *s390_get_ccw_device(DeviceState *dev_st, int *devtype) |
Fan Zhang | df75a4e | 2015-02-12 18:02:14 +0100 | [diff] [blame] | 322 | { |
Viktor Mihajlovski | 11ef6d5 | 2018-04-05 17:07:22 +0200 | [diff] [blame] | 323 | CcwDevice *ccw_dev = NULL; |
Jason J. Herne | 44445d8 | 2019-04-04 10:34:20 -0400 | [diff] [blame] | 324 | int tmp_dt = CCW_DEVTYPE_NONE; |
Fan Zhang | df75a4e | 2015-02-12 18:02:14 +0100 | [diff] [blame] | 325 | |
Fan Zhang | df75a4e | 2015-02-12 18:02:14 +0100 | [diff] [blame] | 326 | if (dev_st) { |
Jason J. Herne | 44445d8 | 2019-04-04 10:34:20 -0400 | [diff] [blame] | 327 | VirtIONet *virtio_net_dev = (VirtIONet *) |
| 328 | object_dynamic_cast(OBJECT(dev_st), TYPE_VIRTIO_NET); |
Jing Liu | b804e8a | 2016-02-26 06:46:12 +0100 | [diff] [blame] | 329 | VirtioCcwDevice *virtio_ccw_dev = (VirtioCcwDevice *) |
| 330 | object_dynamic_cast(OBJECT(qdev_get_parent_bus(dev_st)->parent), |
Viktor Mihajlovski | 11ef6d5 | 2018-04-05 17:07:22 +0200 | [diff] [blame] | 331 | TYPE_VIRTIO_CCW_DEVICE); |
Jason J. Herne | 44445d8 | 2019-04-04 10:34:20 -0400 | [diff] [blame] | 332 | VFIOCCWDevice *vfio_ccw_dev = (VFIOCCWDevice *) |
| 333 | object_dynamic_cast(OBJECT(dev_st), TYPE_VFIO_CCW); |
| 334 | |
Viktor Mihajlovski | 11ef6d5 | 2018-04-05 17:07:22 +0200 | [diff] [blame] | 335 | if (virtio_ccw_dev) { |
| 336 | ccw_dev = CCW_DEVICE(virtio_ccw_dev); |
Jason J. Herne | 44445d8 | 2019-04-04 10:34:20 -0400 | [diff] [blame] | 337 | if (virtio_net_dev) { |
| 338 | tmp_dt = CCW_DEVTYPE_VIRTIO_NET; |
| 339 | } else { |
| 340 | tmp_dt = CCW_DEVTYPE_VIRTIO; |
| 341 | } |
| 342 | } else if (vfio_ccw_dev) { |
| 343 | ccw_dev = CCW_DEVICE(vfio_ccw_dev); |
| 344 | tmp_dt = CCW_DEVTYPE_VFIO; |
Viktor Mihajlovski | 11ef6d5 | 2018-04-05 17:07:22 +0200 | [diff] [blame] | 345 | } else { |
| 346 | SCSIDevice *sd = (SCSIDevice *) |
| 347 | object_dynamic_cast(OBJECT(dev_st), |
| 348 | TYPE_SCSI_DEVICE); |
| 349 | if (sd) { |
| 350 | SCSIBus *bus = scsi_bus_from_device(sd); |
| 351 | VirtIOSCSI *vdev = container_of(bus, VirtIOSCSI, bus); |
| 352 | VirtIOSCSICcw *scsi_ccw = container_of(vdev, VirtIOSCSICcw, |
| 353 | vdev); |
| 354 | |
| 355 | ccw_dev = (CcwDevice *)object_dynamic_cast(OBJECT(scsi_ccw), |
| 356 | TYPE_CCW_DEVICE); |
Jason J. Herne | 44445d8 | 2019-04-04 10:34:20 -0400 | [diff] [blame] | 357 | tmp_dt = CCW_DEVTYPE_SCSI; |
Viktor Mihajlovski | 11ef6d5 | 2018-04-05 17:07:22 +0200 | [diff] [blame] | 358 | } |
| 359 | } |
| 360 | } |
Jason J. Herne | 44445d8 | 2019-04-04 10:34:20 -0400 | [diff] [blame] | 361 | if (devtype) { |
| 362 | *devtype = tmp_dt; |
| 363 | } |
Viktor Mihajlovski | 11ef6d5 | 2018-04-05 17:07:22 +0200 | [diff] [blame] | 364 | return ccw_dev; |
| 365 | } |
| 366 | |
| 367 | static bool s390_gen_initial_iplb(S390IPLState *ipl) |
| 368 | { |
| 369 | DeviceState *dev_st; |
| 370 | CcwDevice *ccw_dev = NULL; |
Jason J. Herne | 44445d8 | 2019-04-04 10:34:20 -0400 | [diff] [blame] | 371 | SCSIDevice *sd; |
| 372 | int devtype; |
Viktor Mihajlovski | 11ef6d5 | 2018-04-05 17:07:22 +0200 | [diff] [blame] | 373 | |
| 374 | dev_st = get_boot_device(0); |
| 375 | if (dev_st) { |
Jason J. Herne | 44445d8 | 2019-04-04 10:34:20 -0400 | [diff] [blame] | 376 | ccw_dev = s390_get_ccw_device(dev_st, &devtype); |
Viktor Mihajlovski | 11ef6d5 | 2018-04-05 17:07:22 +0200 | [diff] [blame] | 377 | } |
| 378 | |
| 379 | /* |
| 380 | * Currently allow IPL only from CCW devices. |
| 381 | */ |
| 382 | if (ccw_dev) { |
Jason J. Herne | 44445d8 | 2019-04-04 10:34:20 -0400 | [diff] [blame] | 383 | switch (devtype) { |
| 384 | case CCW_DEVTYPE_SCSI: |
Cornelia Huck | 216bdd2 | 2019-05-02 17:46:51 +0200 | [diff] [blame] | 385 | sd = SCSI_DEVICE(dev_st); |
Alexander Yarygin | e468b67 | 2016-06-09 15:54:10 +0300 | [diff] [blame] | 386 | ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN); |
| 387 | ipl->iplb.blk0_len = |
| 388 | cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN - S390_IPLB_HEADER_LEN); |
| 389 | ipl->iplb.pbt = S390_IPL_TYPE_QEMU_SCSI; |
| 390 | ipl->iplb.scsi.lun = cpu_to_be32(sd->lun); |
| 391 | ipl->iplb.scsi.target = cpu_to_be16(sd->id); |
| 392 | ipl->iplb.scsi.channel = cpu_to_be16(sd->channel); |
Jing Liu | b804e8a | 2016-02-26 06:46:12 +0100 | [diff] [blame] | 393 | ipl->iplb.scsi.devno = cpu_to_be16(ccw_dev->sch->devno); |
| 394 | ipl->iplb.scsi.ssid = ccw_dev->sch->ssid & 3; |
Jason J. Herne | 44445d8 | 2019-04-04 10:34:20 -0400 | [diff] [blame] | 395 | break; |
| 396 | case CCW_DEVTYPE_VFIO: |
| 397 | ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN); |
| 398 | ipl->iplb.pbt = S390_IPL_TYPE_CCW; |
| 399 | ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno); |
| 400 | ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3; |
| 401 | break; |
| 402 | case CCW_DEVTYPE_VIRTIO_NET: |
| 403 | ipl->netboot = true; |
| 404 | /* Fall through to CCW_DEVTYPE_VIRTIO case */ |
| 405 | case CCW_DEVTYPE_VIRTIO: |
Viktor Mihajlovski | 11ef6d5 | 2018-04-05 17:07:22 +0200 | [diff] [blame] | 406 | ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN); |
| 407 | ipl->iplb.blk0_len = |
| 408 | cpu_to_be32(S390_IPLB_MIN_CCW_LEN - S390_IPLB_HEADER_LEN); |
| 409 | ipl->iplb.pbt = S390_IPL_TYPE_CCW; |
| 410 | ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno); |
| 411 | ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3; |
Jason J. Herne | 44445d8 | 2019-04-04 10:34:20 -0400 | [diff] [blame] | 412 | break; |
Fan Zhang | df75a4e | 2015-02-12 18:02:14 +0100 | [diff] [blame] | 413 | } |
Farhan Ali | bd1badf | 2016-03-29 16:28:40 +0200 | [diff] [blame] | 414 | |
| 415 | if (!s390_ipl_set_loadparm(ipl->iplb.loadparm)) { |
| 416 | ipl->iplb.flags |= DIAG308_FLAGS_LP_VALID; |
| 417 | } |
Viktor Mihajlovski | 11ef6d5 | 2018-04-05 17:07:22 +0200 | [diff] [blame] | 418 | |
Farhan Ali | bd1badf | 2016-03-29 16:28:40 +0200 | [diff] [blame] | 419 | return true; |
Fan Zhang | df75a4e | 2015-02-12 18:02:14 +0100 | [diff] [blame] | 420 | } |
| 421 | |
Alexander Yarygin | 010d45d | 2015-09-22 12:58:09 +0300 | [diff] [blame] | 422 | return false; |
Fan Zhang | df75a4e | 2015-02-12 18:02:14 +0100 | [diff] [blame] | 423 | } |
| 424 | |
Farhan Ali | bd1badf | 2016-03-29 16:28:40 +0200 | [diff] [blame] | 425 | int s390_ipl_set_loadparm(uint8_t *loadparm) |
| 426 | { |
| 427 | MachineState *machine = MACHINE(qdev_get_machine()); |
| 428 | char *lp = object_property_get_str(OBJECT(machine), "loadparm", NULL); |
| 429 | |
| 430 | if (lp) { |
| 431 | int i; |
| 432 | |
| 433 | /* lp is an uppercase string without leading/embedded spaces */ |
| 434 | for (i = 0; i < 8 && lp[i]; i++) { |
| 435 | loadparm[i] = ascii2ebcdic[(uint8_t) lp[i]]; |
| 436 | } |
| 437 | |
Collin Walling | 074afe6 | 2018-04-16 12:56:08 -0400 | [diff] [blame] | 438 | if (i < 8) { |
| 439 | memset(loadparm + i, 0x40, 8 - i); /* fill with EBCDIC spaces */ |
| 440 | } |
| 441 | |
Farhan Ali | bd1badf | 2016-03-29 16:28:40 +0200 | [diff] [blame] | 442 | g_free(lp); |
| 443 | return 0; |
| 444 | } |
| 445 | |
| 446 | return -1; |
| 447 | } |
| 448 | |
Farhan Ali | f38b5b7 | 2016-10-20 17:59:20 -0400 | [diff] [blame] | 449 | static int load_netboot_image(Error **errp) |
| 450 | { |
Paolo Bonzini | 382a04a | 2020-10-28 06:17:07 -0400 | [diff] [blame] | 451 | MachineState *ms = MACHINE(qdev_get_machine()); |
Farhan Ali | f38b5b7 | 2016-10-20 17:59:20 -0400 | [diff] [blame] | 452 | S390IPLState *ipl = get_ipl_device(); |
| 453 | char *netboot_filename; |
| 454 | MemoryRegion *sysmem = get_system_memory(); |
| 455 | MemoryRegion *mr = NULL; |
| 456 | void *ram_ptr = NULL; |
| 457 | int img_size = -1; |
| 458 | |
| 459 | mr = memory_region_find(sysmem, 0, 1).mr; |
| 460 | if (!mr) { |
| 461 | error_setg(errp, "Failed to find memory region at address 0"); |
| 462 | return -1; |
| 463 | } |
| 464 | |
| 465 | ram_ptr = memory_region_get_ram_ptr(mr); |
| 466 | if (!ram_ptr) { |
| 467 | error_setg(errp, "No RAM found"); |
| 468 | goto unref_mr; |
| 469 | } |
| 470 | |
| 471 | netboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->netboot_fw); |
| 472 | if (netboot_filename == NULL) { |
Thomas Huth | c575fa6 | 2018-02-27 11:05:13 +0100 | [diff] [blame] | 473 | error_setg(errp, "Could not find network bootloader '%s'", |
| 474 | ipl->netboot_fw); |
Farhan Ali | f38b5b7 | 2016-10-20 17:59:20 -0400 | [diff] [blame] | 475 | goto unref_mr; |
| 476 | } |
| 477 | |
Liam Merwick | 4366e1d | 2019-01-15 12:18:03 +0000 | [diff] [blame] | 478 | img_size = load_elf_ram(netboot_filename, NULL, NULL, NULL, |
| 479 | &ipl->start_addr, |
Aleksandar Markovic | 6cdda0f | 2020-01-26 23:55:04 +0100 | [diff] [blame] | 480 | NULL, NULL, NULL, 1, EM_S390, 0, 0, NULL, |
| 481 | false); |
Farhan Ali | f38b5b7 | 2016-10-20 17:59:20 -0400 | [diff] [blame] | 482 | |
| 483 | if (img_size < 0) { |
Paolo Bonzini | 382a04a | 2020-10-28 06:17:07 -0400 | [diff] [blame] | 484 | img_size = load_image_size(netboot_filename, ram_ptr, ms->ram_size); |
Farhan Ali | f38b5b7 | 2016-10-20 17:59:20 -0400 | [diff] [blame] | 485 | ipl->start_addr = KERN_IMAGE_START; |
| 486 | } |
| 487 | |
| 488 | if (img_size < 0) { |
| 489 | error_setg(errp, "Failed to load network bootloader"); |
| 490 | } |
| 491 | |
| 492 | g_free(netboot_filename); |
| 493 | |
| 494 | unref_mr: |
| 495 | memory_region_unref(mr); |
| 496 | return img_size; |
| 497 | } |
| 498 | |
Viktor Mihajlovski | 789b5a4 | 2018-04-05 17:07:23 +0200 | [diff] [blame] | 499 | static bool is_virtio_ccw_device_of_type(IplParameterBlock *iplb, |
| 500 | int virtio_id) |
Farhan Ali | f38b5b7 | 2016-10-20 17:59:20 -0400 | [diff] [blame] | 501 | { |
| 502 | uint8_t cssid; |
| 503 | uint8_t ssid; |
| 504 | uint16_t devno; |
| 505 | uint16_t schid; |
| 506 | SubchDev *sch = NULL; |
| 507 | |
| 508 | if (iplb->pbt != S390_IPL_TYPE_CCW) { |
| 509 | return false; |
| 510 | } |
| 511 | |
| 512 | devno = be16_to_cpu(iplb->ccw.devno); |
| 513 | ssid = iplb->ccw.ssid & 3; |
| 514 | |
| 515 | for (schid = 0; schid < MAX_SCHID; schid++) { |
| 516 | for (cssid = 0; cssid < MAX_CSSID; cssid++) { |
| 517 | sch = css_find_subch(1, cssid, ssid, schid); |
| 518 | |
| 519 | if (sch && sch->devno == devno) { |
Viktor Mihajlovski | 789b5a4 | 2018-04-05 17:07:23 +0200 | [diff] [blame] | 520 | return sch->id.cu_model == virtio_id; |
Farhan Ali | f38b5b7 | 2016-10-20 17:59:20 -0400 | [diff] [blame] | 521 | } |
| 522 | } |
| 523 | } |
| 524 | return false; |
| 525 | } |
| 526 | |
Viktor Mihajlovski | 789b5a4 | 2018-04-05 17:07:23 +0200 | [diff] [blame] | 527 | static bool is_virtio_net_device(IplParameterBlock *iplb) |
| 528 | { |
| 529 | return is_virtio_ccw_device_of_type(iplb, VIRTIO_ID_NET); |
| 530 | } |
| 531 | |
| 532 | static bool is_virtio_scsi_device(IplParameterBlock *iplb) |
| 533 | { |
| 534 | return is_virtio_ccw_device_of_type(iplb, VIRTIO_ID_SCSI); |
| 535 | } |
| 536 | |
Halil Pasic | 0a01e08 | 2020-03-09 14:32:23 +0100 | [diff] [blame] | 537 | static void update_machine_ipl_properties(IplParameterBlock *iplb) |
| 538 | { |
| 539 | Object *machine = qdev_get_machine(); |
| 540 | Error *err = NULL; |
| 541 | |
| 542 | /* Sync loadparm */ |
| 543 | if (iplb->flags & DIAG308_FLAGS_LP_VALID) { |
| 544 | uint8_t *ebcdic_loadparm = iplb->loadparm; |
Halil Pasic | 7722837 | 2020-03-20 15:31:01 +0100 | [diff] [blame] | 545 | char ascii_loadparm[9]; |
Halil Pasic | 0a01e08 | 2020-03-09 14:32:23 +0100 | [diff] [blame] | 546 | int i; |
| 547 | |
| 548 | for (i = 0; i < 8 && ebcdic_loadparm[i]; i++) { |
| 549 | ascii_loadparm[i] = ebcdic2ascii[(uint8_t) ebcdic_loadparm[i]]; |
| 550 | } |
| 551 | ascii_loadparm[i] = 0; |
Markus Armbruster | 5325cc3 | 2020-07-07 18:05:54 +0200 | [diff] [blame] | 552 | object_property_set_str(machine, "loadparm", ascii_loadparm, &err); |
Halil Pasic | 0a01e08 | 2020-03-09 14:32:23 +0100 | [diff] [blame] | 553 | } else { |
Markus Armbruster | 5325cc3 | 2020-07-07 18:05:54 +0200 | [diff] [blame] | 554 | object_property_set_str(machine, "loadparm", "", &err); |
Halil Pasic | 0a01e08 | 2020-03-09 14:32:23 +0100 | [diff] [blame] | 555 | } |
| 556 | if (err) { |
| 557 | warn_report_err(err); |
| 558 | } |
| 559 | } |
| 560 | |
David Hildenbrand | feacc6c | 2015-06-25 09:55:55 +0200 | [diff] [blame] | 561 | void s390_ipl_update_diag308(IplParameterBlock *iplb) |
Fan Zhang | df75a4e | 2015-02-12 18:02:14 +0100 | [diff] [blame] | 562 | { |
David Hildenbrand | feacc6c | 2015-06-25 09:55:55 +0200 | [diff] [blame] | 563 | S390IPLState *ipl = get_ipl_device(); |
Fan Zhang | df75a4e | 2015-02-12 18:02:14 +0100 | [diff] [blame] | 564 | |
Janosch Frank | c3347ed | 2020-03-23 04:36:06 -0400 | [diff] [blame] | 565 | /* |
| 566 | * The IPLB set and retrieved by subcodes 8/9 is completely |
| 567 | * separate from the one managed via subcodes 5/6. |
| 568 | */ |
| 569 | if (iplb->pbt == S390_IPL_TYPE_PV) { |
| 570 | ipl->iplb_pv = *iplb; |
| 571 | ipl->iplb_valid_pv = true; |
| 572 | } else { |
| 573 | ipl->iplb = *iplb; |
| 574 | ipl->iplb_valid = true; |
| 575 | } |
Farhan Ali | f38b5b7 | 2016-10-20 17:59:20 -0400 | [diff] [blame] | 576 | ipl->netboot = is_virtio_net_device(iplb); |
Halil Pasic | 0a01e08 | 2020-03-09 14:32:23 +0100 | [diff] [blame] | 577 | update_machine_ipl_properties(iplb); |
Fan Zhang | df75a4e | 2015-02-12 18:02:14 +0100 | [diff] [blame] | 578 | } |
| 579 | |
Janosch Frank | c3347ed | 2020-03-23 04:36:06 -0400 | [diff] [blame] | 580 | IplParameterBlock *s390_ipl_get_iplb_pv(void) |
| 581 | { |
| 582 | S390IPLState *ipl = get_ipl_device(); |
| 583 | |
| 584 | if (!ipl->iplb_valid_pv) { |
| 585 | return NULL; |
| 586 | } |
| 587 | return &ipl->iplb_pv; |
| 588 | } |
| 589 | |
Fan Zhang | df75a4e | 2015-02-12 18:02:14 +0100 | [diff] [blame] | 590 | IplParameterBlock *s390_ipl_get_iplb(void) |
| 591 | { |
David Hildenbrand | feacc6c | 2015-06-25 09:55:55 +0200 | [diff] [blame] | 592 | S390IPLState *ipl = get_ipl_device(); |
Fan Zhang | df75a4e | 2015-02-12 18:02:14 +0100 | [diff] [blame] | 593 | |
David Hildenbrand | feacc6c | 2015-06-25 09:55:55 +0200 | [diff] [blame] | 594 | if (!ipl->iplb_valid) { |
Fan Zhang | df75a4e | 2015-02-12 18:02:14 +0100 | [diff] [blame] | 595 | return NULL; |
| 596 | } |
| 597 | return &ipl->iplb; |
| 598 | } |
| 599 | |
David Hildenbrand | a30fb81 | 2018-04-24 12:18:59 +0200 | [diff] [blame] | 600 | void s390_ipl_reset_request(CPUState *cs, enum s390_reset reset_type) |
Fan Zhang | e91e972 | 2015-02-12 18:02:15 +0100 | [diff] [blame] | 601 | { |
David Hildenbrand | feacc6c | 2015-06-25 09:55:55 +0200 | [diff] [blame] | 602 | S390IPLState *ipl = get_ipl_device(); |
Fan Zhang | e91e972 | 2015-02-12 18:02:15 +0100 | [diff] [blame] | 603 | |
David Hildenbrand | a30fb81 | 2018-04-24 12:18:59 +0200 | [diff] [blame] | 604 | if (reset_type == S390_RESET_EXTERNAL || reset_type == S390_RESET_REIPL) { |
| 605 | /* use CPU 0 for full resets */ |
| 606 | ipl->reset_cpu_index = 0; |
| 607 | } else { |
| 608 | ipl->reset_cpu_index = cs->cpu_index; |
| 609 | } |
| 610 | ipl->reset_type = reset_type; |
| 611 | |
| 612 | if (reset_type == S390_RESET_REIPL && |
| 613 | ipl->iplb_valid && |
Viktor Mihajlovski | 789b5a4 | 2018-04-05 17:07:23 +0200 | [diff] [blame] | 614 | !ipl->netboot && |
| 615 | ipl->iplb.pbt == S390_IPL_TYPE_CCW && |
| 616 | is_virtio_scsi_device(&ipl->iplb)) { |
Jason J. Herne | 44445d8 | 2019-04-04 10:34:20 -0400 | [diff] [blame] | 617 | CcwDevice *ccw_dev = s390_get_ccw_device(get_boot_device(0), NULL); |
Viktor Mihajlovski | 789b5a4 | 2018-04-05 17:07:23 +0200 | [diff] [blame] | 618 | |
| 619 | if (ccw_dev && |
| 620 | cpu_to_be16(ccw_dev->sch->devno) == ipl->iplb.ccw.devno && |
| 621 | (ccw_dev->sch->ssid & 3) == ipl->iplb.ccw.ssid) { |
| 622 | /* |
| 623 | * this is the original boot device's SCSI |
| 624 | * so restore IPL parameter info from it |
| 625 | */ |
| 626 | ipl->iplb_valid = s390_gen_initial_iplb(ipl); |
| 627 | } |
| 628 | } |
Christian Borntraeger | 76ed4b1 | 2018-06-22 12:29:28 +0200 | [diff] [blame] | 629 | if (reset_type == S390_RESET_MODIFIED_CLEAR || |
Christian Borntraeger | d1bb69d | 2020-07-21 06:32:02 -0400 | [diff] [blame] | 630 | reset_type == S390_RESET_LOAD_NORMAL || |
| 631 | reset_type == S390_RESET_PV) { |
Christian Borntraeger | 76ed4b1 | 2018-06-22 12:29:28 +0200 | [diff] [blame] | 632 | /* ignore -no-reboot, send no event */ |
| 633 | qemu_system_reset_request(SHUTDOWN_CAUSE_SUBSYSTEM_RESET); |
| 634 | } else { |
| 635 | qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); |
| 636 | } |
David Hildenbrand | a30fb81 | 2018-04-24 12:18:59 +0200 | [diff] [blame] | 637 | /* as this is triggered by a CPU, make sure to exit the loop */ |
| 638 | if (tcg_enabled()) { |
| 639 | cpu_loop_exit(cs); |
| 640 | } |
| 641 | } |
| 642 | |
| 643 | void s390_ipl_get_reset_request(CPUState **cs, enum s390_reset *reset_type) |
| 644 | { |
| 645 | S390IPLState *ipl = get_ipl_device(); |
| 646 | |
| 647 | *cs = qemu_get_cpu(ipl->reset_cpu_index); |
| 648 | if (!*cs) { |
| 649 | /* use any CPU */ |
| 650 | *cs = first_cpu; |
| 651 | } |
| 652 | *reset_type = ipl->reset_type; |
| 653 | } |
| 654 | |
| 655 | void s390_ipl_clear_reset_request(void) |
| 656 | { |
| 657 | S390IPLState *ipl = get_ipl_device(); |
| 658 | |
| 659 | ipl->reset_type = S390_RESET_EXTERNAL; |
| 660 | /* use CPU 0 for full resets */ |
| 661 | ipl->reset_cpu_index = 0; |
Fan Zhang | e91e972 | 2015-02-12 18:02:15 +0100 | [diff] [blame] | 662 | } |
| 663 | |
Collin L. Walling | 118ee80 | 2018-02-23 10:43:11 -0500 | [diff] [blame] | 664 | static void s390_ipl_prepare_qipl(S390CPU *cpu) |
| 665 | { |
| 666 | S390IPLState *ipl = get_ipl_device(); |
| 667 | uint8_t *addr; |
| 668 | uint64_t len = 4096; |
| 669 | |
Philippe Mathieu-Daudé | 85eb7c1 | 2020-02-19 20:20:42 +0100 | [diff] [blame] | 670 | addr = cpu_physical_memory_map(cpu->env.psa, &len, true); |
Collin L. Walling | 118ee80 | 2018-02-23 10:43:11 -0500 | [diff] [blame] | 671 | if (!addr || len < QIPL_ADDRESS + sizeof(QemuIplParameters)) { |
| 672 | error_report("Cannot set QEMU IPL parameters"); |
| 673 | return; |
| 674 | } |
| 675 | memcpy(addr + QIPL_ADDRESS, &ipl->qipl, sizeof(QemuIplParameters)); |
| 676 | cpu_physical_memory_unmap(addr, len, 1, len); |
| 677 | } |
| 678 | |
Janosch Frank | c3347ed | 2020-03-23 04:36:06 -0400 | [diff] [blame] | 679 | int s390_ipl_prepare_pv_header(void) |
| 680 | { |
| 681 | IplParameterBlock *ipib = s390_ipl_get_iplb_pv(); |
| 682 | IPLBlockPV *ipib_pv = &ipib->pv; |
| 683 | void *hdr = g_malloc(ipib_pv->pv_header_len); |
| 684 | int rc; |
| 685 | |
| 686 | cpu_physical_memory_read(ipib_pv->pv_header_addr, hdr, |
| 687 | ipib_pv->pv_header_len); |
| 688 | rc = s390_pv_set_sec_parms((uintptr_t)hdr, |
| 689 | ipib_pv->pv_header_len); |
| 690 | g_free(hdr); |
| 691 | return rc; |
| 692 | } |
| 693 | |
| 694 | int s390_ipl_pv_unpack(void) |
| 695 | { |
| 696 | IplParameterBlock *ipib = s390_ipl_get_iplb_pv(); |
| 697 | IPLBlockPV *ipib_pv = &ipib->pv; |
| 698 | int i, rc = 0; |
| 699 | |
| 700 | for (i = 0; i < ipib_pv->num_comp; i++) { |
| 701 | rc = s390_pv_unpack(ipib_pv->components[i].addr, |
| 702 | TARGET_PAGE_ALIGN(ipib_pv->components[i].size), |
| 703 | ipib_pv->components[i].tweak_pref); |
| 704 | if (rc) { |
| 705 | break; |
| 706 | } |
| 707 | } |
| 708 | return rc; |
| 709 | } |
| 710 | |
David Hildenbrand | db3b256 | 2015-07-21 13:47:32 +0200 | [diff] [blame] | 711 | void s390_ipl_prepare_cpu(S390CPU *cpu) |
| 712 | { |
| 713 | S390IPLState *ipl = get_ipl_device(); |
Farhan Ali | f38b5b7 | 2016-10-20 17:59:20 -0400 | [diff] [blame] | 714 | Error *err = NULL; |
David Hildenbrand | db3b256 | 2015-07-21 13:47:32 +0200 | [diff] [blame] | 715 | |
| 716 | cpu->env.psw.addr = ipl->start_addr; |
| 717 | cpu->env.psw.mask = IPL_PSW_MASK; |
| 718 | |
| 719 | if (!ipl->kernel || ipl->iplb_valid) { |
| 720 | cpu->env.psw.addr = ipl->bios_start_addr; |
Alexander Yarygin | 010d45d | 2015-09-22 12:58:09 +0300 | [diff] [blame] | 721 | if (!ipl->iplb_valid) { |
| 722 | ipl->iplb_valid = s390_gen_initial_iplb(ipl); |
| 723 | } |
David Hildenbrand | db3b256 | 2015-07-21 13:47:32 +0200 | [diff] [blame] | 724 | } |
Farhan Ali | f38b5b7 | 2016-10-20 17:59:20 -0400 | [diff] [blame] | 725 | if (ipl->netboot) { |
| 726 | if (load_netboot_image(&err) < 0) { |
| 727 | error_report_err(err); |
Thomas Huth | c575fa6 | 2018-02-27 11:05:13 +0100 | [diff] [blame] | 728 | exit(1); |
Farhan Ali | f38b5b7 | 2016-10-20 17:59:20 -0400 | [diff] [blame] | 729 | } |
Collin L. Walling | 118ee80 | 2018-02-23 10:43:11 -0500 | [diff] [blame] | 730 | ipl->qipl.netboot_start_addr = cpu_to_be64(ipl->start_addr); |
Farhan Ali | f38b5b7 | 2016-10-20 17:59:20 -0400 | [diff] [blame] | 731 | } |
Collin L. Walling | 26b2a2a | 2018-02-23 10:43:12 -0500 | [diff] [blame] | 732 | s390_ipl_set_boot_menu(ipl); |
Collin L. Walling | 118ee80 | 2018-02-23 10:43:11 -0500 | [diff] [blame] | 733 | s390_ipl_prepare_qipl(cpu); |
David Hildenbrand | db3b256 | 2015-07-21 13:47:32 +0200 | [diff] [blame] | 734 | } |
| 735 | |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 736 | static void s390_ipl_reset(DeviceState *dev) |
| 737 | { |
| 738 | S390IPLState *ipl = S390_IPL(dev); |
Dominik Dingel | ba1509c | 2013-04-30 07:15:57 +0000 | [diff] [blame] | 739 | |
David Hildenbrand | a30fb81 | 2018-04-24 12:18:59 +0200 | [diff] [blame] | 740 | if (ipl->reset_type != S390_RESET_REIPL) { |
Fan Zhang | e91e972 | 2015-02-12 18:02:15 +0100 | [diff] [blame] | 741 | ipl->iplb_valid = false; |
Alexander Yarygin | 6aed958 | 2015-07-21 14:10:39 +0300 | [diff] [blame] | 742 | memset(&ipl->iplb, 0, sizeof(IplParameterBlock)); |
Fan Zhang | e91e972 | 2015-02-12 18:02:15 +0100 | [diff] [blame] | 743 | } |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 744 | } |
| 745 | |
| 746 | static void s390_ipl_class_init(ObjectClass *klass, void *data) |
| 747 | { |
| 748 | DeviceClass *dc = DEVICE_CLASS(klass); |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 749 | |
David Hildenbrand | 04fccf1 | 2015-10-08 12:32:13 +0200 | [diff] [blame] | 750 | dc->realize = s390_ipl_realize; |
Marc-André Lureau | 4f67d30 | 2020-01-10 19:30:32 +0400 | [diff] [blame] | 751 | device_class_set_props(dc, s390_ipl_properties); |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 752 | dc->reset = s390_ipl_reset; |
Fan Zhang | 2e13fbe | 2015-02-12 18:02:16 +0100 | [diff] [blame] | 753 | dc->vmsd = &vmstate_ipl; |
Cornelia Huck | b4ab457 | 2015-03-20 10:17:08 +0100 | [diff] [blame] | 754 | set_bit(DEVICE_CATEGORY_MISC, dc->categories); |
Thomas Huth | 0d4fa49 | 2017-08-16 07:30:58 +0200 | [diff] [blame] | 755 | /* Reason: Loads the ROMs and thus can only be used one time - internally */ |
| 756 | dc->user_creatable = false; |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 757 | } |
| 758 | |
Alexander Graf | 49973eb | 2013-01-24 19:11:26 +0100 | [diff] [blame] | 759 | static const TypeInfo s390_ipl_info = { |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 760 | .class_init = s390_ipl_class_init, |
David Hildenbrand | 04fccf1 | 2015-10-08 12:32:13 +0200 | [diff] [blame] | 761 | .parent = TYPE_DEVICE, |
| 762 | .name = TYPE_S390_IPL, |
Christian Borntraeger | e674a49 | 2012-12-18 07:50:57 +0000 | [diff] [blame] | 763 | .instance_size = sizeof(S390IPLState), |
| 764 | }; |
| 765 | |
| 766 | static void s390_ipl_register_types(void) |
| 767 | { |
| 768 | type_register_static(&s390_ipl_info); |
| 769 | } |
| 770 | |
| 771 | type_init(s390_ipl_register_types) |