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