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