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
enable_prefixing(void)21 static void enable_prefixing(void)
22 {
23 memcpy(&prefix_page, lowcore, 4096);
24 set_prefix(ptr2u32(&prefix_page));
25 }
26
disable_prefixing(void)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
is_read_tic_ccw_chain(Ccw0 * ccw)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
dynamic_cp_fixup(uint32_t ccw_addr,uint32_t * next_cpa)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
run_dynamic_ccw_program(SubChannelId schid,uint16_t cutype,uint32_t cpa)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
make_readipl(void)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
run_readipl(SubChannelId schid,uint16_t cutype)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 */
check_ipl1(void)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
check_ipl2(uint32_t ipl2_addr)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
read_ipl2_addr(void)139 static uint32_t read_ipl2_addr(void)
140 {
141 Ccw0 *ccwtic = (Ccw0 *)0x10;
142
143 return ccwtic->cda;
144 }
145
ipl1_fixup(void)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
run_ipl1(SubChannelId schid,uint16_t cutype)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
run_ipl2(SubChannelId schid,uint16_t cutype,uint32_t addr)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 */
dasd_ipl(SubChannelId schid,uint16_t cutype)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 }
246