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