xref: /qemu/pc-bios/s390-ccw/dasd-ipl.c (revision c6fc0fc1)
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