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 void run_readipl(SubChannelId schid, uint16_t cutype) 115 { 116 if (do_cio(schid, cutype, 0x00, CCW_FMT0)) { 117 panic("dasd-ipl: Failed to run Read IPL channel program\n"); 118 } 119 } 120 121 /* 122 * The architecture states that IPL1 data should consist of a psw followed by 123 * format-0 READ and TIC CCWs. Let's sanity check. 124 */ 125 static void check_ipl1(void) 126 { 127 Ccw0 *ccwread = (Ccw0 *)0x08; 128 Ccw0 *ccwtic = (Ccw0 *)0x10; 129 130 if (ccwread->cmd_code != CCW_CMD_DASD_READ || 131 ccwtic->cmd_code != CCW_CMD_TIC) { 132 panic("dasd-ipl: IPL1 data invalid. Is this disk really bootable?\n"); 133 } 134 } 135 136 static void check_ipl2(uint32_t ipl2_addr) 137 { 138 Ccw0 *ccw = u32toptr(ipl2_addr); 139 140 if (ipl2_addr == 0x00) { 141 panic("IPL2 address invalid. Is this disk really bootable?\n"); 142 } 143 if (ccw->cmd_code == 0x00) { 144 panic("IPL2 ccw data invalid. Is this disk really bootable?\n"); 145 } 146 } 147 148 static uint32_t read_ipl2_addr(void) 149 { 150 Ccw0 *ccwtic = (Ccw0 *)0x10; 151 152 return ccwtic->cda; 153 } 154 155 static void ipl1_fixup(void) 156 { 157 Ccw0 *ccwSeek = (Ccw0 *) 0x08; 158 Ccw0 *ccwSearchID = (Ccw0 *) 0x10; 159 Ccw0 *ccwSearchTic = (Ccw0 *) 0x18; 160 Ccw0 *ccwRead = (Ccw0 *) 0x20; 161 CcwSeekData *seekData = (CcwSeekData *) 0x30; 162 CcwSearchIdData *searchData = (CcwSearchIdData *) 0x38; 163 164 /* move IPL1 CCWs to make room for CCWs needed to locate record 2 */ 165 memcpy(ccwRead, (void *)0x08, 16); 166 167 /* Disable chaining so we don't TIC to IPL2 channel program */ 168 ccwRead->chain = 0x00; 169 170 ccwSeek->cmd_code = CCW_CMD_DASD_SEEK; 171 ccwSeek->cda = ptr2u32(seekData); 172 ccwSeek->chain = 1; 173 ccwSeek->count = sizeof(*seekData); 174 seekData->reserved = 0x00; 175 seekData->cyl = 0x00; 176 seekData->head = 0x00; 177 178 ccwSearchID->cmd_code = CCW_CMD_DASD_SEARCH_ID_EQ; 179 ccwSearchID->cda = ptr2u32(searchData); 180 ccwSearchID->chain = 1; 181 ccwSearchID->count = sizeof(*searchData); 182 searchData->cyl = 0; 183 searchData->head = 0; 184 searchData->record = 2; 185 186 /* Go back to Search CCW if correct record not yet found */ 187 ccwSearchTic->cmd_code = CCW_CMD_TIC; 188 ccwSearchTic->cda = ptr2u32(ccwSearchID); 189 } 190 191 static void run_ipl1(SubChannelId schid, uint16_t cutype) 192 { 193 uint32_t startAddr = 0x08; 194 195 if (do_cio(schid, cutype, startAddr, CCW_FMT0)) { 196 panic("dasd-ipl: Failed to run IPL1 channel program\n"); 197 } 198 } 199 200 static void run_ipl2(SubChannelId schid, uint16_t cutype, uint32_t addr) 201 { 202 if (run_dynamic_ccw_program(schid, cutype, addr)) { 203 panic("dasd-ipl: Failed to run IPL2 channel program\n"); 204 } 205 } 206 207 /* 208 * Limitations in vfio-ccw support complicate the IPL process. Details can 209 * be found in docs/devel/s390-dasd-ipl.rst 210 */ 211 void dasd_ipl(SubChannelId schid, uint16_t cutype) 212 { 213 PSWLegacy *pswl = (PSWLegacy *) 0x00; 214 uint32_t ipl2_addr; 215 216 /* Construct Read IPL CCW and run it to read IPL1 from boot disk */ 217 make_readipl(); 218 run_readipl(schid, cutype); 219 ipl2_addr = read_ipl2_addr(); 220 check_ipl1(); 221 222 /* 223 * Fixup IPL1 channel program to account for vfio-ccw limitations, then run 224 * it to read IPL2 channel program from boot disk. 225 */ 226 ipl1_fixup(); 227 run_ipl1(schid, cutype); 228 check_ipl2(ipl2_addr); 229 230 /* 231 * Run IPL2 channel program to read operating system code from boot disk 232 */ 233 run_ipl2(schid, cutype, ipl2_addr); 234 235 /* Transfer control to the guest operating system */ 236 pswl->mask |= PSW_MASK_EAMODE; /* Force z-mode */ 237 pswl->addr |= PSW_MASK_BAMODE; /* ... */ 238 jump_to_low_kernel(); 239 } 240