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