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