| 1 | /* |
| 2 | * S390 IPL (boot) from a real DASD device via vfio framework. |
| 3 | * |
| 4 | * Copyright (c) 2019 Jason J. Herne <jjherne@us.ibm.com> |
| 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 | |
| 11 | #include <string.h> |
| 12 | #include <stdio.h> |
| 13 | #include "s390-ccw.h" |
| 14 | #include "s390-arch.h" |
| 15 | #include "dasd-ipl.h" |
| 16 | #include "helper.h" |
| 17 | |
| 18 | static char prefix_page[PAGE_SIZE * 2] |
| 19 | __attribute__((__aligned__(PAGE_SIZE * 2))); |
| 20 | |
| 21 | static void enable_prefixing(void) |
| 22 | { |
| 23 | memcpy(&prefix_page, lowcore, 4096); |
| 24 | set_prefix(ptr2u32(&prefix_page)); |
| 25 | } |
| 26 | |
| 27 | static void disable_prefixing(void) |
| 28 | { |
| 29 | set_prefix(0); |
| 30 | /* Copy io interrupt info back to low core */ |
| 31 | memcpy((void *)&lowcore->subchannel_id, prefix_page + 0xB8, 12); |
| 32 | } |
| 33 | |
| 34 | static bool is_read_tic_ccw_chain(Ccw0 *ccw) |
| 35 | { |
| 36 | Ccw0 *next_ccw = ccw + 1; |
| 37 | |
| 38 | return ((ccw->cmd_code == CCW_CMD_DASD_READ || |
| 39 | ccw->cmd_code == CCW_CMD_DASD_READ_MT) && |
| 40 | ccw->chain && next_ccw->cmd_code == CCW_CMD_TIC); |
| 41 | } |
| 42 | |
| 43 | static bool dynamic_cp_fixup(uint32_t ccw_addr, uint32_t *next_cpa) |
| 44 | { |
| 45 | Ccw0 *cur_ccw = (Ccw0 *)(uint64_t)ccw_addr; |
| 46 | Ccw0 *tic_ccw; |
| 47 | |
| 48 | while (true) { |
| 49 | /* Skip over inline TIC (it might not have the chain bit on) */ |
| 50 | if (cur_ccw->cmd_code == CCW_CMD_TIC && |
| 51 | cur_ccw->cda == ptr2u32(cur_ccw) - 8) { |
| 52 | cur_ccw += 1; |
| 53 | continue; |
| 54 | } |
| 55 | |
| 56 | if (!cur_ccw->chain) { |
| 57 | break; |
| 58 | } |
| 59 | if (is_read_tic_ccw_chain(cur_ccw)) { |
| 60 | /* |
| 61 | * Breaking a chain of CCWs may alter the semantics or even the |
| 62 | * validity of a channel program. The heuristic implemented below |
| 63 | * seems to work well in practice for the channel programs |
| 64 | * generated by zipl. |
| 65 | */ |
| 66 | tic_ccw = cur_ccw + 1; |
| 67 | *next_cpa = tic_ccw->cda; |
| 68 | cur_ccw->chain = 0; |
| 69 | return true; |
| 70 | } |
| 71 | cur_ccw += 1; |
| 72 | } |
| 73 | return false; |
| 74 | } |
| 75 | |
| 76 | static int run_dynamic_ccw_program(SubChannelId schid, uint16_t cutype, |
| 77 | uint32_t cpa) |
| 78 | { |
| 79 | bool has_next; |
| 80 | uint32_t next_cpa = 0; |
| 81 | int rc; |
| 82 | |
| 83 | do { |
| 84 | has_next = dynamic_cp_fixup(cpa, &next_cpa); |
| 85 | |
| 86 | printf("executing ccw chain at 0x%X\n", cpa); |
| 87 | enable_prefixing(); |
| 88 | rc = do_cio(schid, cutype, cpa, CCW_FMT0); |
| 89 | disable_prefixing(); |
| 90 | |
| 91 | if (rc) { |
| 92 | break; |
| 93 | } |
| 94 | cpa = next_cpa; |
| 95 | } while (has_next); |
| 96 | |
| 97 | return rc; |
| 98 | } |
| 99 | |
| 100 | static void make_readipl(void) |
| 101 | { |
| 102 | Ccw0 *ccwIplRead = (Ccw0 *)0x00; |
| 103 | |
| 104 | /* Clear out any existing data */ |
| 105 | memset(ccwIplRead, 0, sizeof(Ccw0)); |
| 106 | |
| 107 | /* Create Read IPL ccw at address 0 */ |
| 108 | ccwIplRead->cmd_code = CCW_CMD_READ_IPL; |
| 109 | ccwIplRead->cda = 0x00; /* Read into address 0x00 in main memory */ |
| 110 | ccwIplRead->chain = 0; /* Chain flag */ |
| 111 | ccwIplRead->count = 0x18; /* Read 0x18 bytes of data */ |
| 112 | } |
| 113 | |
| 114 | static int run_readipl(SubChannelId schid, uint16_t cutype) |
| 115 | { |
| 116 | return do_cio(schid, cutype, 0x00, CCW_FMT0); |
| 117 | } |
| 118 | |
| 119 | /* |
| 120 | * The architecture states that IPL1 data should consist of a psw followed by |
| 121 | * format-0 READ and TIC CCWs. Let's sanity check. |
| 122 | */ |
| 123 | static bool check_ipl1(void) |
| 124 | { |
| 125 | Ccw0 *ccwread = (Ccw0 *)0x08; |
| 126 | Ccw0 *ccwtic = (Ccw0 *)0x10; |
| 127 | |
| 128 | return (ccwread->cmd_code == CCW_CMD_DASD_READ && |
| 129 | ccwtic->cmd_code == CCW_CMD_TIC); |
| 130 | } |
| 131 | |
| 132 | static bool check_ipl2(uint32_t ipl2_addr) |
| 133 | { |
| 134 | Ccw0 *ccw = u32toptr(ipl2_addr); |
| 135 | |
| 136 | return (ipl2_addr != 0x00 && ccw->cmd_code != 0x00); |
| 137 | } |
| 138 | |
| 139 | static uint32_t read_ipl2_addr(void) |
| 140 | { |
| 141 | Ccw0 *ccwtic = (Ccw0 *)0x10; |
| 142 | |
| 143 | return ccwtic->cda; |
| 144 | } |
| 145 | |
| 146 | static void ipl1_fixup(void) |
| 147 | { |
| 148 | Ccw0 *ccwSeek = (Ccw0 *) 0x08; |
| 149 | Ccw0 *ccwSearchID = (Ccw0 *) 0x10; |
| 150 | Ccw0 *ccwSearchTic = (Ccw0 *) 0x18; |
| 151 | Ccw0 *ccwRead = (Ccw0 *) 0x20; |
| 152 | CcwSeekData *seekData = (CcwSeekData *) 0x30; |
| 153 | CcwSearchIdData *searchData = (CcwSearchIdData *) 0x38; |
| 154 | |
| 155 | /* move IPL1 CCWs to make room for CCWs needed to locate record 2 */ |
| 156 | memcpy(ccwRead, (void *)0x08, 16); |
| 157 | |
| 158 | /* Disable chaining so we don't TIC to IPL2 channel program */ |
| 159 | ccwRead->chain = 0x00; |
| 160 | |
| 161 | ccwSeek->cmd_code = CCW_CMD_DASD_SEEK; |
| 162 | ccwSeek->cda = ptr2u32(seekData); |
| 163 | ccwSeek->chain = 1; |
| 164 | ccwSeek->count = sizeof(*seekData); |
| 165 | seekData->reserved = 0x00; |
| 166 | seekData->cyl = 0x00; |
| 167 | seekData->head = 0x00; |
| 168 | |
| 169 | ccwSearchID->cmd_code = CCW_CMD_DASD_SEARCH_ID_EQ; |
| 170 | ccwSearchID->cda = ptr2u32(searchData); |
| 171 | ccwSearchID->chain = 1; |
| 172 | ccwSearchID->count = sizeof(*searchData); |
| 173 | searchData->cyl = 0; |
| 174 | searchData->head = 0; |
| 175 | searchData->record = 2; |
| 176 | |
| 177 | /* Go back to Search CCW if correct record not yet found */ |
| 178 | ccwSearchTic->cmd_code = CCW_CMD_TIC; |
| 179 | ccwSearchTic->cda = ptr2u32(ccwSearchID); |
| 180 | } |
| 181 | |
| 182 | static int run_ipl1(SubChannelId schid, uint16_t cutype) |
| 183 | { |
| 184 | uint32_t startAddr = 0x08; |
| 185 | |
| 186 | return do_cio(schid, cutype, startAddr, CCW_FMT0); |
| 187 | } |
| 188 | |
| 189 | static int run_ipl2(SubChannelId schid, uint16_t cutype, uint32_t addr) |
| 190 | { |
| 191 | return run_dynamic_ccw_program(schid, cutype, addr); |
| 192 | } |
| 193 | |
| 194 | /* |
| 195 | * Limitations in vfio-ccw support complicate the IPL process. Details can |
| 196 | * be found in docs/devel/s390-dasd-ipl.rst |
| 197 | */ |
| 198 | int dasd_ipl(SubChannelId schid, uint16_t cutype) |
| 199 | { |
| 200 | PSWLegacy *pswl = (PSWLegacy *) 0x00; |
| 201 | uint32_t ipl2_addr; |
| 202 | |
| 203 | /* Construct Read IPL CCW and run it to read IPL1 from boot disk */ |
| 204 | make_readipl(); |
| 205 | if (run_readipl(schid, cutype)) { |
| 206 | puts("Failed to run Read IPL channel program"); |
| 207 | return -EIO; |
| 208 | } |
| 209 | |
| 210 | ipl2_addr = read_ipl2_addr(); |
| 211 | |
| 212 | if (!check_ipl1()) { |
| 213 | puts("IPL1 invalid for DASD-IPL"); |
| 214 | return -EINVAL; |
| 215 | } |
| 216 | |
| 217 | /* |
| 218 | * Fixup IPL1 channel program to account for vfio-ccw limitations, then run |
| 219 | * it to read IPL2 channel program from boot disk. |
| 220 | */ |
| 221 | ipl1_fixup(); |
| 222 | if (run_ipl1(schid, cutype)) { |
| 223 | puts("Failed to run IPL1 channel program"); |
| 224 | return -EIO; |
| 225 | } |
| 226 | |
| 227 | if (!check_ipl2(ipl2_addr)) { |
| 228 | puts("IPL2 invalid for DASD-IPL"); |
| 229 | return -EINVAL; |
| 230 | } |
| 231 | |
| 232 | /* |
| 233 | * Run IPL2 channel program to read operating system code from boot disk |
| 234 | */ |
| 235 | if (run_ipl2(schid, cutype, ipl2_addr)) { |
| 236 | puts("Failed to run IPL2 channel program"); |
| 237 | return -EIO; |
| 238 | } |
| 239 | |
| 240 | /* Transfer control to the guest operating system */ |
| 241 | pswl->mask |= PSW_MASK_EAMODE; /* Force z-mode */ |
| 242 | pswl->addr |= PSW_MASK_BAMODE; /* ... */ |
| 243 | jump_to_low_kernel(); |
| 244 | return -1; |
| 245 | } |