Alexander Graf | 92f2ca3 | 2013-04-22 20:57:58 +0200 | [diff] [blame] | 1 | /* |
| 2 | * S390 virtio-ccw loading program |
| 3 | * |
| 4 | * Copyright (c) 2013 Alexander Graf <agraf@suse.de> |
| 5 | * |
| 6 | * This work is licensed under the terms of the GNU GPL, version 2 or (at |
| 7 | * your option) any later version. See the COPYING file in the top-level |
| 8 | * directory. |
| 9 | */ |
| 10 | |
Thomas Huth | 90806fe | 2017-07-12 14:49:43 +0200 | [diff] [blame] | 11 | #include "libc.h" |
Janosch Frank | 9bfc04f | 2020-03-04 06:42:31 -0500 | [diff] [blame] | 12 | #include "helper.h" |
Jason J. Herne | c95df3d | 2019-04-04 10:34:25 -0400 | [diff] [blame] | 13 | #include "s390-arch.h" |
Alexander Graf | 92f2ca3 | 2013-04-22 20:57:58 +0200 | [diff] [blame] | 14 | #include "s390-ccw.h" |
Jason J. Herne | 120d041 | 2019-04-04 10:34:24 -0400 | [diff] [blame] | 15 | #include "cio.h" |
Eugene (jno) Dvurechenski | 60612d5 | 2014-05-19 20:11:07 +0200 | [diff] [blame] | 16 | #include "virtio.h" |
Thomas Huth | cf30b7c | 2022-07-04 13:19:00 +0200 | [diff] [blame] | 17 | #include "virtio-scsi.h" |
Jason J. Herne | efa47d3 | 2019-04-04 10:34:34 -0400 | [diff] [blame] | 18 | #include "dasd-ipl.h" |
Alexander Graf | 92f2ca3 | 2013-04-22 20:57:58 +0200 | [diff] [blame] | 19 | |
Eugene (jno) Dvurechenski | b88d7fa | 2015-11-10 14:10:20 +0100 | [diff] [blame] | 20 | static SubChannelId blk_schid = { .one = 1 }; |
Thomas Huth | 6673ded | 2019-08-28 14:25:55 +0200 | [diff] [blame] | 21 | static char loadparm_str[LOADPARM_LEN + 1]; |
Collin L. Walling | 118ee80 | 2018-02-23 10:43:11 -0500 | [diff] [blame] | 22 | QemuIplParameters qipl; |
Jason J. Herne | a5f6e09 | 2019-04-04 10:34:22 -0400 | [diff] [blame] | 23 | IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); |
| 24 | static bool have_iplb; |
Jason J. Herne | 2880469 | 2019-04-04 10:34:35 -0400 | [diff] [blame] | 25 | static uint16_t cutype; |
Janosch Frank | 9bfc04f | 2020-03-04 06:42:31 -0500 | [diff] [blame] | 26 | LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */ |
Christian Borntraeger | f2879a5 | 2014-07-01 12:17:41 +0200 | [diff] [blame] | 27 | |
Collin L. Walling | 9eaa654 | 2018-02-23 10:43:13 -0500 | [diff] [blame] | 28 | #define LOADPARM_PROMPT "PROMPT " |
Collin Walling | 074afe6 | 2018-04-16 12:56:08 -0400 | [diff] [blame] | 29 | #define LOADPARM_EMPTY " " |
Collin L. Walling | 53b310c | 2018-02-23 10:43:18 -0500 | [diff] [blame] | 30 | #define BOOT_MENU_FLAG_MASK (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL) |
Collin L. Walling | 9eaa654 | 2018-02-23 10:43:13 -0500 | [diff] [blame] | 31 | |
Christian Borntraeger | f2879a5 | 2014-07-01 12:17:41 +0200 | [diff] [blame] | 32 | /* |
Jason J. Herne | a5f6e09 | 2019-04-04 10:34:22 -0400 | [diff] [blame] | 33 | * Principles of Operations (SA22-7832-09) chapter 17 requires that |
Christian Borntraeger | f2879a5 | 2014-07-01 12:17:41 +0200 | [diff] [blame] | 34 | * a subsystem-identification is at 184-187 and bytes 188-191 are zero |
| 35 | * after list-directed-IPL and ccw-IPL. |
| 36 | */ |
| 37 | void write_subsystem_identification(void) |
| 38 | { |
Janosch Frank | e6d393d | 2020-06-24 03:52:18 -0400 | [diff] [blame] | 39 | lowcore->subchannel_id = blk_schid.sch_id; |
| 40 | lowcore->subchannel_nr = blk_schid.sch_no; |
| 41 | lowcore->io_int_parm = 0; |
Christian Borntraeger | f2879a5 | 2014-07-01 12:17:41 +0200 | [diff] [blame] | 42 | } |
| 43 | |
Janosch Frank | 9bfc04f | 2020-03-04 06:42:31 -0500 | [diff] [blame] | 44 | void write_iplb_location(void) |
| 45 | { |
Jason J. Herne | 872882e | 2020-10-30 13:28:23 +0100 | [diff] [blame] | 46 | if (cutype == CU_TYPE_VIRTIO && virtio_get_device_type() != VIRTIO_ID_NET) { |
| 47 | lowcore->ptr_iplb = ptr2u32(&iplb); |
| 48 | } |
Janosch Frank | 9bfc04f | 2020-03-04 06:42:31 -0500 | [diff] [blame] | 49 | } |
| 50 | |
Farhan Ali | 95fa1af | 2017-01-16 10:45:49 -0500 | [diff] [blame] | 51 | unsigned int get_loadparm_index(void) |
| 52 | { |
Collin Walling | 074afe6 | 2018-04-16 12:56:08 -0400 | [diff] [blame] | 53 | return atoui(loadparm_str); |
Farhan Ali | 95fa1af | 2017-01-16 10:45:49 -0500 | [diff] [blame] | 54 | } |
| 55 | |
Thomas Huth | d2cf4af | 2020-07-28 15:50:31 +0200 | [diff] [blame] | 56 | static int is_dev_possibly_bootable(int dev_no, int sch_no) |
| 57 | { |
| 58 | bool is_virtio; |
| 59 | Schib schib; |
| 60 | int r; |
| 61 | |
| 62 | blk_schid.sch_no = sch_no; |
| 63 | r = stsch_err(blk_schid, &schib); |
| 64 | if (r == 3 || r == -EIO) { |
| 65 | return -ENODEV; |
| 66 | } |
| 67 | if (!schib.pmcw.dnv) { |
| 68 | return false; |
| 69 | } |
| 70 | |
| 71 | enable_subchannel(blk_schid); |
| 72 | cutype = cu_type(blk_schid); |
| 73 | |
| 74 | /* |
| 75 | * Note: we always have to run virtio_is_supported() here to make |
| 76 | * sure that the vdev.senseid data gets pre-initialized correctly |
| 77 | */ |
| 78 | is_virtio = virtio_is_supported(blk_schid); |
| 79 | |
| 80 | /* No specific devno given, just return whether the device is possibly bootable */ |
| 81 | if (dev_no < 0) { |
| 82 | switch (cutype) { |
| 83 | case CU_TYPE_VIRTIO: |
| 84 | if (is_virtio) { |
| 85 | /* |
| 86 | * Skip net devices since no IPLB is created and therefore |
| 87 | * no network bootloader has been loaded |
| 88 | */ |
| 89 | if (virtio_get_device_type() != VIRTIO_ID_NET) { |
| 90 | return true; |
| 91 | } |
| 92 | } |
| 93 | return false; |
| 94 | case CU_TYPE_DASD_3990: |
| 95 | case CU_TYPE_DASD_2107: |
| 96 | return true; |
| 97 | default: |
| 98 | return false; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | /* Caller asked for a specific devno */ |
| 103 | if (schib.pmcw.dev == dev_no) { |
| 104 | return true; |
| 105 | } |
| 106 | |
| 107 | return false; |
| 108 | } |
| 109 | |
Jason J. Herne | 930072d | 2019-04-04 10:34:29 -0400 | [diff] [blame] | 110 | /* |
| 111 | * Find the subchannel connected to the given device (dev_no) and fill in the |
| 112 | * subchannel information block (schib) with the connected subchannel's info. |
| 113 | * NOTE: The global variable blk_schid is updated to contain the subchannel |
| 114 | * information. |
Jason J. Herne | 2880469 | 2019-04-04 10:34:35 -0400 | [diff] [blame] | 115 | * |
| 116 | * If the caller gives dev_no=-1 then the user did not specify a boot device. |
| 117 | * In this case we'll just use the first potentially bootable device we find. |
Jason J. Herne | 930072d | 2019-04-04 10:34:29 -0400 | [diff] [blame] | 118 | */ |
Jason J. Herne | 7b361db | 2019-04-04 10:34:30 -0400 | [diff] [blame] | 119 | static bool find_subch(int dev_no) |
Alexander Graf | 92f2ca3 | 2013-04-22 20:57:58 +0200 | [diff] [blame] | 120 | { |
Alexander Yarygin | 0f79b89 | 2015-06-25 18:35:58 +0300 | [diff] [blame] | 121 | int i, r; |
Dominik Dingel | ff151f4 | 2013-04-30 07:15:58 +0000 | [diff] [blame] | 122 | |
Alexander Graf | 92f2ca3 | 2013-04-22 20:57:58 +0200 | [diff] [blame] | 123 | for (i = 0; i < 0x10000; i++) { |
Thomas Huth | d2cf4af | 2020-07-28 15:50:31 +0200 | [diff] [blame] | 124 | r = is_dev_possibly_bootable(dev_no, i); |
| 125 | if (r < 0) { |
Cornelia Huck | 22d67ab | 2013-04-26 02:12:52 +0000 | [diff] [blame] | 126 | break; |
| 127 | } |
Thomas Huth | d2cf4af | 2020-07-28 15:50:31 +0200 | [diff] [blame] | 128 | if (r == true) { |
Alexander Yarygin | 0f79b89 | 2015-06-25 18:35:58 +0300 | [diff] [blame] | 129 | return true; |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | return false; |
| 134 | } |
| 135 | |
Collin L. Walling | 9eaa654 | 2018-02-23 10:43:13 -0500 | [diff] [blame] | 136 | static void menu_setup(void) |
| 137 | { |
Collin Walling | a0e11b6 | 2018-05-29 00:40:09 -0400 | [diff] [blame] | 138 | if (memcmp(loadparm_str, LOADPARM_PROMPT, LOADPARM_LEN) == 0) { |
Collin L. Walling | 9eaa654 | 2018-02-23 10:43:13 -0500 | [diff] [blame] | 139 | menu_set_parms(QIPL_FLAG_BM_OPTS_CMD, 0); |
| 140 | return; |
| 141 | } |
| 142 | |
| 143 | /* If loadparm was set to any other value, then do not enable menu */ |
Collin Walling | a0e11b6 | 2018-05-29 00:40:09 -0400 | [diff] [blame] | 144 | if (memcmp(loadparm_str, LOADPARM_EMPTY, LOADPARM_LEN) != 0) { |
Collin L. Walling | 9eaa654 | 2018-02-23 10:43:13 -0500 | [diff] [blame] | 145 | return; |
| 146 | } |
| 147 | |
| 148 | switch (iplb.pbt) { |
| 149 | case S390_IPL_TYPE_CCW: |
Collin L. Walling | ffb4a1c | 2018-02-23 10:43:19 -0500 | [diff] [blame] | 150 | case S390_IPL_TYPE_QEMU_SCSI: |
Collin L. Walling | 53b310c | 2018-02-23 10:43:18 -0500 | [diff] [blame] | 151 | menu_set_parms(qipl.qipl_flags & BOOT_MENU_FLAG_MASK, |
Collin L. Walling | 9eaa654 | 2018-02-23 10:43:13 -0500 | [diff] [blame] | 152 | qipl.boot_menu_timeout); |
| 153 | return; |
| 154 | } |
| 155 | } |
| 156 | |
Jason J. Herne | 87f910c | 2019-04-04 10:34:21 -0400 | [diff] [blame] | 157 | /* |
| 158 | * Initialize the channel I/O subsystem so we can talk to our ipl/boot device. |
| 159 | */ |
| 160 | static void css_setup(void) |
| 161 | { |
| 162 | /* |
| 163 | * Unconditionally enable mss support. In every sane configuration this |
| 164 | * will succeed; and even if it doesn't, stsch_err() can handle it. |
| 165 | */ |
| 166 | enable_mss_facility(); |
| 167 | } |
| 168 | |
Jason J. Herne | a5f6e09 | 2019-04-04 10:34:22 -0400 | [diff] [blame] | 169 | /* |
| 170 | * Collect various pieces of information from the hypervisor/hardware that |
| 171 | * we'll use to determine exactly how we'll boot. |
| 172 | */ |
| 173 | static void boot_setup(void) |
| 174 | { |
| 175 | char lpmsg[] = "LOADPARM=[________]\n"; |
| 176 | |
| 177 | sclp_get_loadparm_ascii(loadparm_str); |
| 178 | memcpy(lpmsg + 10, loadparm_str, 8); |
| 179 | sclp_print(lpmsg); |
| 180 | |
Eric Farman | 3d65199 | 2020-11-20 17:01:17 +0100 | [diff] [blame] | 181 | /* |
| 182 | * Clear out any potential S390EP magic (see jump_to_low_kernel()), |
| 183 | * so we don't taint our decision-making process during a reboot. |
| 184 | */ |
| 185 | memset((char *)S390EP, 0, 6); |
| 186 | |
Jason J. Herne | a5f6e09 | 2019-04-04 10:34:22 -0400 | [diff] [blame] | 187 | have_iplb = store_iplb(&iplb); |
| 188 | } |
| 189 | |
Jason J. Herne | 7b361db | 2019-04-04 10:34:30 -0400 | [diff] [blame] | 190 | static void find_boot_device(void) |
| 191 | { |
| 192 | VDev *vdev = virtio_get_device(); |
Jason J. Herne | 7b361db | 2019-04-04 10:34:30 -0400 | [diff] [blame] | 193 | bool found; |
| 194 | |
Jason J. Herne | 7b361db | 2019-04-04 10:34:30 -0400 | [diff] [blame] | 195 | switch (iplb.pbt) { |
| 196 | case S390_IPL_TYPE_CCW: |
| 197 | debug_print_int("device no. ", iplb.ccw.devno); |
| 198 | blk_schid.ssid = iplb.ccw.ssid & 0x3; |
| 199 | debug_print_int("ssid ", blk_schid.ssid); |
| 200 | found = find_subch(iplb.ccw.devno); |
| 201 | break; |
| 202 | case S390_IPL_TYPE_QEMU_SCSI: |
| 203 | vdev->scsi_device_selected = true; |
| 204 | vdev->selected_scsi_device.channel = iplb.scsi.channel; |
| 205 | vdev->selected_scsi_device.target = iplb.scsi.target; |
| 206 | vdev->selected_scsi_device.lun = iplb.scsi.lun; |
| 207 | blk_schid.ssid = iplb.scsi.ssid & 0x3; |
| 208 | found = find_subch(iplb.scsi.devno); |
| 209 | break; |
| 210 | default: |
| 211 | panic("List-directed IPL not supported yet!\n"); |
| 212 | } |
| 213 | |
| 214 | IPL_assert(found, "Boot device not found\n"); |
| 215 | } |
| 216 | |
Thomas Huth | 605751b | 2020-07-28 14:30:14 +0200 | [diff] [blame] | 217 | static int virtio_setup(void) |
Alexander Yarygin | 0f79b89 | 2015-06-25 18:35:58 +0300 | [diff] [blame] | 218 | { |
Farhan Ali | 99b72e0 | 2016-11-01 17:34:00 -0400 | [diff] [blame] | 219 | VDev *vdev = virtio_get_device(); |
Collin L. Walling | 118ee80 | 2018-02-23 10:43:11 -0500 | [diff] [blame] | 220 | QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS; |
Thomas Huth | cf30b7c | 2022-07-04 13:19:00 +0200 | [diff] [blame] | 221 | int ret; |
Alexander Yarygin | 0f79b89 | 2015-06-25 18:35:58 +0300 | [diff] [blame] | 222 | |
Collin L. Walling | 118ee80 | 2018-02-23 10:43:11 -0500 | [diff] [blame] | 223 | memcpy(&qipl, early_qipl, sizeof(QemuIplParameters)); |
| 224 | |
Jason J. Herne | a5f6e09 | 2019-04-04 10:34:22 -0400 | [diff] [blame] | 225 | if (have_iplb) { |
Collin L. Walling | 9eaa654 | 2018-02-23 10:43:13 -0500 | [diff] [blame] | 226 | menu_setup(); |
Alexander Graf | 92f2ca3 | 2013-04-22 20:57:58 +0200 | [diff] [blame] | 227 | } |
| 228 | |
Thomas Huth | cf30b7c | 2022-07-04 13:19:00 +0200 | [diff] [blame] | 229 | switch (vdev->senseid.cu_model) { |
| 230 | case VIRTIO_ID_NET: |
Farhan Ali | 99b72e0 | 2016-11-01 17:34:00 -0400 | [diff] [blame] | 231 | sclp_print("Network boot device detected\n"); |
Collin L. Walling | 118ee80 | 2018-02-23 10:43:11 -0500 | [diff] [blame] | 232 | vdev->netboot_start_addr = qipl.netboot_start_addr; |
Thomas Huth | cf30b7c | 2022-07-04 13:19:00 +0200 | [diff] [blame] | 233 | return 0; |
| 234 | case VIRTIO_ID_BLOCK: |
| 235 | ret = virtio_blk_setup_device(blk_schid); |
| 236 | break; |
| 237 | case VIRTIO_ID_SCSI: |
| 238 | ret = virtio_scsi_setup_device(blk_schid); |
| 239 | break; |
| 240 | default: |
| 241 | panic("\n! No IPL device available !\n"); |
| 242 | } |
| 243 | |
| 244 | if (!ret) { |
Farhan Ali | 99b72e0 | 2016-11-01 17:34:00 -0400 | [diff] [blame] | 245 | IPL_assert(virtio_ipl_disk_is_valid(), "No valid IPL device detected"); |
| 246 | } |
Thomas Huth | 605751b | 2020-07-28 14:30:14 +0200 | [diff] [blame] | 247 | |
Thomas Huth | cf30b7c | 2022-07-04 13:19:00 +0200 | [diff] [blame] | 248 | return ret; |
Alexander Graf | 92f2ca3 | 2013-04-22 20:57:58 +0200 | [diff] [blame] | 249 | } |
| 250 | |
Thomas Huth | d1f060a | 2020-07-28 10:35:53 +0200 | [diff] [blame] | 251 | static void ipl_boot_device(void) |
Alexander Graf | 92f2ca3 | 2013-04-22 20:57:58 +0200 | [diff] [blame] | 252 | { |
Jason J. Herne | 3668cb7 | 2019-04-04 10:34:32 -0400 | [diff] [blame] | 253 | switch (cutype) { |
Jason J. Herne | efa47d3 | 2019-04-04 10:34:34 -0400 | [diff] [blame] | 254 | case CU_TYPE_DASD_3990: |
| 255 | case CU_TYPE_DASD_2107: |
| 256 | dasd_ipl(blk_schid, cutype); /* no return */ |
| 257 | break; |
Jason J. Herne | 3668cb7 | 2019-04-04 10:34:32 -0400 | [diff] [blame] | 258 | case CU_TYPE_VIRTIO: |
Thomas Huth | 605751b | 2020-07-28 14:30:14 +0200 | [diff] [blame] | 259 | if (virtio_setup() == 0) { |
Thomas Huth | 5dc739f | 2020-07-28 18:14:50 +0200 | [diff] [blame] | 260 | zipl_load(); /* Only returns in case of errors */ |
Thomas Huth | 605751b | 2020-07-28 14:30:14 +0200 | [diff] [blame] | 261 | } |
Jason J. Herne | 3668cb7 | 2019-04-04 10:34:32 -0400 | [diff] [blame] | 262 | break; |
| 263 | default: |
| 264 | print_int("Attempting to boot from unexpected device type", cutype); |
Thomas Huth | d1f060a | 2020-07-28 10:35:53 +0200 | [diff] [blame] | 265 | panic("\nBoot failed.\n"); |
Jason J. Herne | 3668cb7 | 2019-04-04 10:34:32 -0400 | [diff] [blame] | 266 | } |
Thomas Huth | d1f060a | 2020-07-28 10:35:53 +0200 | [diff] [blame] | 267 | } |
| 268 | |
Thomas Huth | 869d0e2 | 2020-07-28 16:29:03 +0200 | [diff] [blame] | 269 | /* |
| 270 | * No boot device has been specified, so we have to scan through the |
| 271 | * channels to find one. |
| 272 | */ |
| 273 | static void probe_boot_device(void) |
| 274 | { |
| 275 | int ssid, sch_no, ret; |
| 276 | |
| 277 | for (ssid = 0; ssid < 0x3; ssid++) { |
| 278 | blk_schid.ssid = ssid; |
| 279 | for (sch_no = 0; sch_no < 0x10000; sch_no++) { |
| 280 | ret = is_dev_possibly_bootable(-1, sch_no); |
| 281 | if (ret < 0) { |
| 282 | break; |
| 283 | } |
| 284 | if (ret == true) { |
| 285 | ipl_boot_device(); /* Only returns if unsuccessful */ |
| 286 | } |
| 287 | } |
| 288 | } |
| 289 | |
| 290 | sclp_print("Could not find a suitable boot device (none specified)\n"); |
| 291 | } |
| 292 | |
Thomas Huth | 2ba3cc4 | 2022-07-04 13:18:52 +0200 | [diff] [blame] | 293 | void main(void) |
Thomas Huth | d1f060a | 2020-07-28 10:35:53 +0200 | [diff] [blame] | 294 | { |
| 295 | sclp_setup(); |
| 296 | css_setup(); |
| 297 | boot_setup(); |
Thomas Huth | 869d0e2 | 2020-07-28 16:29:03 +0200 | [diff] [blame] | 298 | if (have_iplb) { |
| 299 | find_boot_device(); |
Thomas Huth | 869d0e2 | 2020-07-28 16:29:03 +0200 | [diff] [blame] | 300 | ipl_boot_device(); |
| 301 | } else { |
| 302 | probe_boot_device(); |
| 303 | } |
Eugene (jno) Dvurechenski | 60612d5 | 2014-05-19 20:11:07 +0200 | [diff] [blame] | 304 | |
Eugene (jno) Dvurechenski | c9262e8 | 2015-09-17 12:47:27 +0200 | [diff] [blame] | 305 | panic("Failed to load OS from hard disk\n"); |
Alexander Graf | 92f2ca3 | 2013-04-22 20:57:58 +0200 | [diff] [blame] | 306 | } |