]>
Commit | Line | Data |
---|---|---|
685d49a6 AG |
1 | /* |
2 | * QEMU S390 bootmap interpreter | |
3 | * | |
4 | * Copyright (c) 2009 Alexander Graf <agraf@suse.de> | |
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 | ||
90806fec | 11 | #include "libc.h" |
685d49a6 | 12 | #include "s390-ccw.h" |
26f2bbd6 | 13 | #include "bootmap.h" |
91a03f9b | 14 | #include "virtio.h" |
824df3b8 | 15 | #include "bswap.h" |
685d49a6 | 16 | |
a00b33d9 | 17 | #ifdef DEBUG |
abd696e4 | 18 | /* #define DEBUG_FALLBACK */ |
a00b33d9 | 19 | #endif |
685d49a6 AG |
20 | |
21 | #ifdef DEBUG_FALLBACK | |
22 | #define dputs(txt) \ | |
23 | do { sclp_print("zipl: " txt); } while (0) | |
24 | #else | |
25 | #define dputs(fmt, ...) \ | |
26 | do { } while (0) | |
27 | #endif | |
28 | ||
685d49a6 | 29 | /* Scratch space */ |
a00b33d9 | 30 | static uint8_t sec[MAX_SECTOR_SIZE*4] __attribute__((__aligned__(PAGE_SIZE))); |
685d49a6 | 31 | |
6af978ae TH |
32 | const uint8_t el_torito_magic[] = "EL TORITO SPECIFICATION" |
33 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; | |
34 | ||
35 | /* | |
36 | * Match two CCWs located after PSW and eight filler bytes. | |
37 | * From libmagic and arch/s390/kernel/head.S. | |
38 | */ | |
39 | const uint8_t linux_s390_magic[] = "\x02\x00\x00\x18\x60\x00\x00\x50\x02\x00" | |
40 | "\x00\x68\x60\x00\x00\x50\x40\x40\x40\x40" | |
41 | "\x40\x40\x40\x40"; | |
42 | ||
43 | static inline bool is_iso_vd_valid(IsoVolDesc *vd) | |
44 | { | |
45 | const uint8_t vol_desc_magic[] = "CD001"; | |
46 | ||
47 | return !memcmp(&vd->ident[0], vol_desc_magic, 5) && | |
48 | vd->version == 0x1 && | |
49 | vd->type <= VOL_DESC_TYPE_PARTITION; | |
50 | } | |
51 | ||
e0aff4aa ED |
52 | /*********************************************************************** |
53 | * IPL an ECKD DASD (CDL or LDL/CMS format) | |
54 | */ | |
55 | ||
56 | static unsigned char _bprs[8*1024]; /* guessed "max" ECKD sector size */ | |
f17a8430 | 57 | static const int max_bprs_entries = sizeof(_bprs) / sizeof(ExtEckdBlockPtr); |
ba831b25 CW |
58 | static uint8_t _s2[MAX_SECTOR_SIZE * 3] __attribute__((__aligned__(PAGE_SIZE))); |
59 | static void *s2_prev_blk = _s2; | |
60 | static void *s2_cur_blk = _s2 + MAX_SECTOR_SIZE; | |
61 | static void *s2_next_blk = _s2 + MAX_SECTOR_SIZE * 2; | |
e0aff4aa | 62 | |
564e52b9 ED |
63 | static inline void verify_boot_info(BootInfo *bip) |
64 | { | |
688e697f | 65 | IPL_assert(magic_match(bip->magic, ZIPL_MAGIC), "No zIPL sig in BootInfo"); |
564e52b9 ED |
66 | IPL_assert(bip->version == BOOT_INFO_VERSION, "Wrong zIPL version"); |
67 | IPL_assert(bip->bp_type == BOOT_INFO_BP_TYPE_IPL, "DASD is not for IPL"); | |
68 | IPL_assert(bip->dev_type == BOOT_INFO_DEV_TYPE_ECKD, "DASD is not ECKD"); | |
69 | IPL_assert(bip->flags == BOOT_INFO_FLAGS_ARCH, "Not for this arch"); | |
70 | IPL_assert(block_size_ok(bip->bp.ipl.bm_ptr.eckd.bptr.size), | |
71 | "Bad block size in zIPL section of the 1st record."); | |
72 | } | |
73 | ||
80beedcc | 74 | static block_number_t eckd_block_num(EckdCHS *chs) |
e0aff4aa | 75 | { |
f04db28b ED |
76 | const uint64_t sectors = virtio_get_sectors(); |
77 | const uint64_t heads = virtio_get_heads(); | |
80beedcc CW |
78 | const uint64_t cylinder = chs->cylinder |
79 | + ((chs->head & 0xfff0) << 12); | |
80 | const uint64_t head = chs->head & 0x000f; | |
f04db28b ED |
81 | const block_number_t block = sectors * heads * cylinder |
82 | + sectors * head | |
80beedcc | 83 | + chs->sector |
f04db28b ED |
84 | - 1; /* block nr starts with zero */ |
85 | return block; | |
86 | } | |
87 | ||
88 | static bool eckd_valid_address(BootMapPointer *p) | |
89 | { | |
80beedcc | 90 | const uint64_t head = p->eckd.chs.head & 0x000f; |
e0aff4aa ED |
91 | |
92 | if (head >= virtio_get_heads() | |
80beedcc CW |
93 | || p->eckd.chs.sector > virtio_get_sectors() |
94 | || p->eckd.chs.sector <= 0) { | |
e0aff4aa ED |
95 | return false; |
96 | } | |
97 | ||
f04db28b | 98 | if (!virtio_guessed_disk_nature() && |
80beedcc | 99 | eckd_block_num(&p->eckd.chs) >= virtio_get_blocks()) { |
e0aff4aa ED |
100 | return false; |
101 | } | |
102 | ||
103 | return true; | |
104 | } | |
105 | ||
e0aff4aa ED |
106 | static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address) |
107 | { | |
108 | block_number_t block_nr; | |
109 | int j, rc; | |
110 | BootMapPointer *bprs = (void *)_bprs; | |
111 | bool more_data; | |
112 | ||
113 | memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs)); | |
114 | read_block(blk, bprs, "BPRS read failed"); | |
115 | ||
116 | do { | |
117 | more_data = false; | |
118 | for (j = 0;; j++) { | |
80beedcc | 119 | block_nr = eckd_block_num(&bprs[j].xeckd.bptr.chs); |
e0aff4aa ED |
120 | if (is_null_block_number(block_nr)) { /* end of chunk */ |
121 | break; | |
122 | } | |
123 | ||
124 | /* we need the updated blockno for the next indirect entry | |
125 | * in the chain, but don't want to advance address | |
126 | */ | |
127 | if (j == (max_bprs_entries - 1)) { | |
128 | break; | |
129 | } | |
130 | ||
131 | IPL_assert(block_size_ok(bprs[j].xeckd.bptr.size), | |
132 | "bad chunk block size"); | |
133 | IPL_assert(eckd_valid_address(&bprs[j]), "bad chunk ECKD addr"); | |
134 | ||
135 | if ((bprs[j].xeckd.bptr.count == 0) && unused_space(&(bprs[j+1]), | |
136 | sizeof(EckdBlockPtr))) { | |
137 | /* This is a "continue" pointer. | |
138 | * This ptr should be the last one in the current | |
139 | * script section. | |
140 | * I.e. the next ptr must point to the unused memory area | |
141 | */ | |
142 | memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs)); | |
143 | read_block(block_nr, bprs, "BPRS continuation read failed"); | |
144 | more_data = true; | |
145 | break; | |
146 | } | |
147 | ||
148 | /* Load (count+1) blocks of code at (block_nr) | |
149 | * to memory (address). | |
150 | */ | |
151 | rc = virtio_read_many(block_nr, (void *)(*address), | |
152 | bprs[j].xeckd.bptr.count+1); | |
153 | IPL_assert(rc == 0, "code chunk read failed"); | |
154 | ||
155 | *address += (bprs[j].xeckd.bptr.count+1) * virtio_get_block_size(); | |
156 | } | |
157 | } while (more_data); | |
158 | return block_nr; | |
159 | } | |
160 | ||
ba831b25 CW |
161 | static bool find_zipl_boot_menu_banner(int *offset) |
162 | { | |
163 | int i; | |
164 | ||
165 | /* Menu banner starts with "zIPL" */ | |
166 | for (i = 0; i < virtio_get_block_size() - 4; i++) { | |
167 | if (magic_match(s2_cur_blk + i, ZIPL_MAGIC_EBCDIC)) { | |
168 | *offset = i; | |
169 | return true; | |
170 | } | |
171 | } | |
172 | ||
173 | return false; | |
174 | } | |
175 | ||
176 | static int eckd_get_boot_menu_index(block_number_t s1b_block_nr) | |
177 | { | |
178 | block_number_t cur_block_nr; | |
179 | block_number_t prev_block_nr = 0; | |
180 | block_number_t next_block_nr = 0; | |
181 | EckdStage1b *s1b = (void *)sec; | |
182 | int banner_offset; | |
183 | int i; | |
184 | ||
185 | /* Get Stage1b data */ | |
186 | memset(sec, FREE_SPACE_FILLER, sizeof(sec)); | |
187 | read_block(s1b_block_nr, s1b, "Cannot read stage1b boot loader"); | |
188 | ||
189 | memset(_s2, FREE_SPACE_FILLER, sizeof(_s2)); | |
190 | ||
191 | /* Get Stage2 data */ | |
192 | for (i = 0; i < STAGE2_BLK_CNT_MAX; i++) { | |
193 | cur_block_nr = eckd_block_num(&s1b->seek[i].chs); | |
194 | ||
195 | if (!cur_block_nr) { | |
196 | break; | |
197 | } | |
198 | ||
199 | read_block(cur_block_nr, s2_cur_blk, "Cannot read stage2 boot loader"); | |
200 | ||
201 | if (find_zipl_boot_menu_banner(&banner_offset)) { | |
202 | /* | |
203 | * Load the adjacent blocks to account for the | |
204 | * possibility of menu data spanning multiple blocks. | |
205 | */ | |
206 | if (prev_block_nr) { | |
207 | read_block(prev_block_nr, s2_prev_blk, | |
208 | "Cannot read stage2 boot loader"); | |
209 | } | |
210 | ||
211 | if (i + 1 < STAGE2_BLK_CNT_MAX) { | |
212 | next_block_nr = eckd_block_num(&s1b->seek[i + 1].chs); | |
213 | } | |
214 | ||
215 | if (next_block_nr) { | |
216 | read_block(next_block_nr, s2_next_blk, | |
217 | "Cannot read stage2 boot loader"); | |
218 | } | |
219 | ||
220 | return menu_get_zipl_boot_index(s2_cur_blk + banner_offset); | |
221 | } | |
222 | ||
223 | prev_block_nr = cur_block_nr; | |
224 | } | |
225 | ||
226 | sclp_print("No zipl boot menu data found. Booting default entry."); | |
227 | return 0; | |
228 | } | |
229 | ||
230 | static void run_eckd_boot_script(block_number_t bmt_block_nr, | |
231 | block_number_t s1b_block_nr) | |
e0aff4aa ED |
232 | { |
233 | int i; | |
82ca3941 | 234 | unsigned int loadparm = get_loadparm_index(); |
e0aff4aa ED |
235 | block_number_t block_nr; |
236 | uint64_t address; | |
5340eb07 | 237 | BootMapTable *bmt = (void *)sec; |
e0aff4aa ED |
238 | BootMapScript *bms = (void *)sec; |
239 | ||
ba831b25 CW |
240 | if (menu_is_enabled_zipl()) { |
241 | loadparm = eckd_get_boot_menu_index(s1b_block_nr); | |
242 | } | |
243 | ||
82ca3941 | 244 | debug_print_int("loadparm", loadparm); |
6df2a829 | 245 | IPL_assert(loadparm < MAX_BOOT_ENTRIES, "loadparm value greater than" |
82ca3941 FA |
246 | " maximum number of boot entries allowed"); |
247 | ||
e0aff4aa | 248 | memset(sec, FREE_SPACE_FILLER, sizeof(sec)); |
5340eb07 | 249 | read_block(bmt_block_nr, sec, "Cannot read Boot Map Table"); |
e0aff4aa | 250 | |
80beedcc | 251 | block_nr = eckd_block_num(&bmt->entry[loadparm].xeckd.bptr.chs); |
5340eb07 | 252 | IPL_assert(block_nr != -1, "Cannot find Boot Map Table Entry"); |
e0aff4aa ED |
253 | |
254 | memset(sec, FREE_SPACE_FILLER, sizeof(sec)); | |
255 | read_block(block_nr, sec, "Cannot read Boot Map Script"); | |
256 | ||
257 | for (i = 0; bms->entry[i].type == BOOT_SCRIPT_LOAD; i++) { | |
258 | address = bms->entry[i].address.load_address; | |
80beedcc | 259 | block_nr = eckd_block_num(&bms->entry[i].blkptr.xeckd.bptr.chs); |
e0aff4aa ED |
260 | |
261 | do { | |
262 | block_nr = load_eckd_segments(block_nr, &address); | |
263 | } while (block_nr != -1); | |
264 | } | |
265 | ||
266 | IPL_assert(bms->entry[i].type == BOOT_SCRIPT_EXEC, | |
267 | "Unknown script entry type"); | |
268 | jump_to_IPL_code(bms->entry[i].address.load_address); /* no return */ | |
269 | } | |
270 | ||
564e52b9 | 271 | static void ipl_eckd_cdl(void) |
e0aff4aa ED |
272 | { |
273 | XEckdMbr *mbr; | |
ac4c5958 | 274 | EckdCdlIpl2 *ipl2 = (void *)sec; |
e0aff4aa | 275 | IplVolumeLabel *vlbl = (void *)sec; |
ba831b25 | 276 | block_number_t bmt_block_nr, s1b_block_nr; |
e0aff4aa | 277 | |
e0aff4aa | 278 | /* we have just read the block #0 and recognized it as "IPL1" */ |
564e52b9 | 279 | sclp_print("CDL\n"); |
e0aff4aa ED |
280 | |
281 | memset(sec, FREE_SPACE_FILLER, sizeof(sec)); | |
282 | read_block(1, ipl2, "Cannot read IPL2 record at block 1"); | |
e0aff4aa | 283 | |
ac4c5958 | 284 | mbr = &ipl2->mbr; |
e0aff4aa ED |
285 | IPL_assert(magic_match(mbr, ZIPL_MAGIC), "No zIPL section in IPL2 record."); |
286 | IPL_assert(block_size_ok(mbr->blockptr.xeckd.bptr.size), | |
287 | "Bad block size in zIPL section of IPL2 record."); | |
288 | IPL_assert(mbr->dev_type == DEV_TYPE_ECKD, | |
289 | "Non-ECKD device type in zIPL section of IPL2 record."); | |
290 | ||
5340eb07 | 291 | /* save pointer to Boot Map Table */ |
80beedcc | 292 | bmt_block_nr = eckd_block_num(&mbr->blockptr.xeckd.bptr.chs); |
e0aff4aa | 293 | |
ba831b25 CW |
294 | /* save pointer to Stage1b Data */ |
295 | s1b_block_nr = eckd_block_num(&ipl2->stage1.seek[0].chs); | |
296 | ||
e0aff4aa ED |
297 | memset(sec, FREE_SPACE_FILLER, sizeof(sec)); |
298 | read_block(2, vlbl, "Cannot read Volume Label at block 2"); | |
299 | IPL_assert(magic_match(vlbl->key, VOL1_MAGIC), | |
300 | "Invalid magic of volume label block"); | |
301 | IPL_assert(magic_match(vlbl->f.key, VOL1_MAGIC), | |
302 | "Invalid magic of volser block"); | |
303 | print_volser(vlbl->f.volser); | |
304 | ||
ba831b25 | 305 | run_eckd_boot_script(bmt_block_nr, s1b_block_nr); |
e0aff4aa ED |
306 | /* no return */ |
307 | } | |
308 | ||
14f56a2e | 309 | static void print_eckd_ldl_msg(ECKD_IPL_mode_t mode) |
564e52b9 ED |
310 | { |
311 | LDL_VTOC *vlbl = (void *)sec; /* already read, 3rd block */ | |
312 | char msg[4] = { '?', '.', '\n', '\0' }; | |
564e52b9 ED |
313 | |
314 | sclp_print((mode == ECKD_CMS) ? "CMS" : "LDL"); | |
315 | sclp_print(" version "); | |
316 | switch (vlbl->LDL_version) { | |
317 | case LDL1_VERSION: | |
318 | msg[0] = '1'; | |
319 | break; | |
320 | case LDL2_VERSION: | |
321 | msg[0] = '2'; | |
322 | break; | |
323 | default: | |
324 | msg[0] = vlbl->LDL_version; | |
325 | msg[0] &= 0x0f; /* convert EBCDIC */ | |
326 | msg[0] |= 0x30; /* to ASCII (digit) */ | |
327 | msg[1] = '?'; | |
328 | break; | |
329 | } | |
330 | sclp_print(msg); | |
331 | print_volser(vlbl->volser); | |
14f56a2e ED |
332 | } |
333 | ||
334 | static void ipl_eckd_ldl(ECKD_IPL_mode_t mode) | |
335 | { | |
ba831b25 | 336 | block_number_t bmt_block_nr, s1b_block_nr; |
ac4c5958 | 337 | EckdLdlIpl1 *ipl1 = (void *)sec; |
14f56a2e ED |
338 | |
339 | if (mode != ECKD_LDL_UNLABELED) { | |
340 | print_eckd_ldl_msg(mode); | |
341 | } | |
564e52b9 ED |
342 | |
343 | /* DO NOT read BootMap pointer (only one, xECKD) at block #2 */ | |
344 | ||
345 | memset(sec, FREE_SPACE_FILLER, sizeof(sec)); | |
14f56a2e ED |
346 | read_block(0, sec, "Cannot read block 0 to grab boot info."); |
347 | if (mode == ECKD_LDL_UNLABELED) { | |
ac4c5958 | 348 | if (!magic_match(ipl1->bip.magic, ZIPL_MAGIC)) { |
14f56a2e ED |
349 | return; /* not applicable layout */ |
350 | } | |
351 | sclp_print("unlabeled LDL.\n"); | |
352 | } | |
ac4c5958 | 353 | verify_boot_info(&ipl1->bip); |
564e52b9 | 354 | |
5340eb07 | 355 | /* save pointer to Boot Map Table */ |
ac4c5958 | 356 | bmt_block_nr = eckd_block_num(&ipl1->bip.bp.ipl.bm_ptr.eckd.bptr.chs); |
5340eb07 | 357 | |
ba831b25 CW |
358 | /* save pointer to Stage1b Data */ |
359 | s1b_block_nr = eckd_block_num(&ipl1->stage1.seek[0].chs); | |
360 | ||
361 | run_eckd_boot_script(bmt_block_nr, s1b_block_nr); | |
564e52b9 ED |
362 | /* no return */ |
363 | } | |
364 | ||
b0885f75 ED |
365 | static void print_eckd_msg(void) |
366 | { | |
367 | char msg[] = "Using ECKD scheme (block size *****), "; | |
368 | char *p = &msg[34], *q = &msg[30]; | |
369 | int n = virtio_get_block_size(); | |
370 | ||
371 | /* Fill in the block size and show up the message */ | |
372 | if (n > 0 && n <= 99999) { | |
373 | while (n) { | |
374 | *p-- = '0' + (n % 10); | |
375 | n /= 10; | |
376 | } | |
377 | while (p >= q) { | |
378 | *p-- = ' '; | |
379 | } | |
380 | } | |
381 | sclp_print(msg); | |
382 | } | |
383 | ||
f0386820 ED |
384 | static void ipl_eckd(void) |
385 | { | |
5340eb07 | 386 | XEckdMbr *mbr = (void *)sec; |
f0386820 ED |
387 | LDL_VTOC *vlbl = (void *)sec; |
388 | ||
389 | print_eckd_msg(); | |
390 | ||
391 | /* Grab the MBR again */ | |
392 | memset(sec, FREE_SPACE_FILLER, sizeof(sec)); | |
393 | read_block(0, mbr, "Cannot read block 0 on DASD"); | |
394 | ||
395 | if (magic_match(mbr->magic, IPL1_MAGIC)) { | |
396 | ipl_eckd_cdl(); /* no return */ | |
397 | } | |
398 | ||
399 | /* LDL/CMS? */ | |
400 | memset(sec, FREE_SPACE_FILLER, sizeof(sec)); | |
401 | read_block(2, vlbl, "Cannot read block 2"); | |
402 | ||
403 | if (magic_match(vlbl->magic, CMS1_MAGIC)) { | |
404 | ipl_eckd_ldl(ECKD_CMS); /* no return */ | |
405 | } | |
406 | if (magic_match(vlbl->magic, LNX1_MAGIC)) { | |
407 | ipl_eckd_ldl(ECKD_LDL); /* no return */ | |
408 | } | |
409 | ||
410 | ipl_eckd_ldl(ECKD_LDL_UNLABELED); /* it still may return */ | |
411 | /* | |
412 | * Ok, it is not a LDL by any means. | |
413 | * It still might be a CDL with zero record keys for IPL1 and IPL2 | |
414 | */ | |
415 | ipl_eckd_cdl(); | |
416 | } | |
417 | ||
a00b33d9 ED |
418 | /*********************************************************************** |
419 | * IPL a SCSI disk | |
420 | */ | |
685d49a6 | 421 | |
60612d5c | 422 | static void zipl_load_segment(ComponentEntry *entry) |
685d49a6 | 423 | { |
91a03f9b | 424 | const int max_entries = (MAX_SECTOR_SIZE / sizeof(ScsiBlockPtr)); |
26f2bbd6 | 425 | ScsiBlockPtr *bprs = (void *)sec; |
c77cd87c | 426 | const int bprs_size = sizeof(sec); |
a94b485e | 427 | block_number_t blockno; |
a00b33d9 | 428 | uint64_t address; |
685d49a6 | 429 | int i; |
a00b33d9 ED |
430 | char err_msg[] = "zIPL failed to read BPRS at 0xZZZZZZZZZZZZZZZZ"; |
431 | char *blk_no = &err_msg[30]; /* where to print blockno in (those ZZs) */ | |
685d49a6 AG |
432 | |
433 | blockno = entry->data.blockno; | |
434 | address = entry->load_address; | |
435 | ||
436 | debug_print_int("loading segment at block", blockno); | |
437 | debug_print_int("addr", address); | |
438 | ||
439 | do { | |
c77cd87c | 440 | memset(bprs, FREE_SPACE_FILLER, bprs_size); |
a00b33d9 ED |
441 | fill_hex_val(blk_no, &blockno, sizeof(blockno)); |
442 | read_block(blockno, bprs, err_msg); | |
685d49a6 AG |
443 | |
444 | for (i = 0;; i++) { | |
a00b33d9 | 445 | uint64_t *cur_desc = (void *)&bprs[i]; |
685d49a6 AG |
446 | |
447 | blockno = bprs[i].blockno; | |
abd696e4 | 448 | if (!blockno) { |
685d49a6 | 449 | break; |
abd696e4 | 450 | } |
685d49a6 AG |
451 | |
452 | /* we need the updated blockno for the next indirect entry in the | |
453 | chain, but don't want to advance address */ | |
abd696e4 | 454 | if (i == (max_entries - 1)) { |
685d49a6 | 455 | break; |
abd696e4 | 456 | } |
685d49a6 | 457 | |
c77cd87c | 458 | if (bprs[i].blockct == 0 && unused_space(&bprs[i + 1], |
26f2bbd6 | 459 | sizeof(ScsiBlockPtr))) { |
c77cd87c ED |
460 | /* This is a "continue" pointer. |
461 | * This ptr is the last one in the current script section. | |
462 | * I.e. the next ptr must point to the unused memory area. | |
463 | * The blockno is not zero, so the upper loop must continue | |
464 | * reading next section of BPRS. | |
465 | */ | |
466 | break; | |
467 | } | |
685d49a6 | 468 | address = virtio_load_direct(cur_desc[0], cur_desc[1], 0, |
abd696e4 | 469 | (void *)address); |
a00b33d9 | 470 | IPL_assert(address != -1, "zIPL load segment failed"); |
685d49a6 AG |
471 | } |
472 | } while (blockno); | |
685d49a6 AG |
473 | } |
474 | ||
475 | /* Run a zipl program */ | |
60612d5c | 476 | static void zipl_run(ScsiBlockPtr *pte) |
685d49a6 | 477 | { |
26f2bbd6 ED |
478 | ComponentHeader *header; |
479 | ComponentEntry *entry; | |
91a03f9b | 480 | uint8_t tmp_sec[MAX_SECTOR_SIZE]; |
685d49a6 | 481 | |
a00b33d9 | 482 | read_block(pte->blockno, tmp_sec, "Cannot read header"); |
26f2bbd6 | 483 | header = (ComponentHeader *)tmp_sec; |
685d49a6 | 484 | |
688e697f | 485 | IPL_assert(magic_match(tmp_sec, ZIPL_MAGIC), "No zIPL magic in header"); |
a00b33d9 | 486 | IPL_assert(header->type == ZIPL_COMP_HEADER_IPL, "Bad header type"); |
685d49a6 AG |
487 | |
488 | dputs("start loading images\n"); | |
489 | ||
490 | /* Load image(s) into RAM */ | |
26f2bbd6 | 491 | entry = (ComponentEntry *)(&header[1]); |
685d49a6 | 492 | while (entry->component_type == ZIPL_COMP_ENTRY_LOAD) { |
60612d5c | 493 | zipl_load_segment(entry); |
685d49a6 AG |
494 | |
495 | entry++; | |
496 | ||
60612d5c | 497 | IPL_assert((uint8_t *)(&entry[1]) <= (tmp_sec + MAX_SECTOR_SIZE), |
a00b33d9 | 498 | "Wrong entry value"); |
685d49a6 AG |
499 | } |
500 | ||
a00b33d9 | 501 | IPL_assert(entry->component_type == ZIPL_COMP_ENTRY_EXEC, "No EXEC entry"); |
685d49a6 | 502 | |
685d49a6 | 503 | /* should not return */ |
96298232 | 504 | jump_to_IPL_code(entry->load_address); |
685d49a6 AG |
505 | } |
506 | ||
a00b33d9 | 507 | static void ipl_scsi(void) |
685d49a6 | 508 | { |
26f2bbd6 | 509 | ScsiMbr *mbr = (void *)sec; |
685d49a6 | 510 | int program_table_entries = 0; |
5340eb07 | 511 | BootMapTable *prog_table = (void *)sec; |
9dd7823b | 512 | unsigned int loadparm = get_loadparm_index(); |
622b3917 CW |
513 | bool valid_entries[MAX_BOOT_ENTRIES] = {false}; |
514 | size_t i; | |
685d49a6 | 515 | |
f0386820 ED |
516 | /* Grab the MBR */ |
517 | memset(sec, FREE_SPACE_FILLER, sizeof(sec)); | |
518 | read_block(0, mbr, "Cannot read block 0"); | |
519 | ||
520 | if (!magic_match(mbr->magic, ZIPL_MAGIC)) { | |
521 | return; | |
522 | } | |
685d49a6 | 523 | |
a00b33d9 | 524 | sclp_print("Using SCSI scheme.\n"); |
b1be0972 ED |
525 | debug_print_int("MBR Version", mbr->version_id); |
526 | IPL_check(mbr->version_id == 1, | |
527 | "Unknown MBR layout version, assuming version 1"); | |
5340eb07 CW |
528 | debug_print_int("program table", mbr->pt.blockno); |
529 | IPL_assert(mbr->pt.blockno, "No Program Table"); | |
685d49a6 AG |
530 | |
531 | /* Parse the program table */ | |
5340eb07 | 532 | read_block(mbr->pt.blockno, sec, "Error reading Program Table"); |
688e697f | 533 | IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT"); |
685d49a6 | 534 | |
622b3917 CW |
535 | for (i = 0; i < MAX_BOOT_ENTRIES; i++) { |
536 | if (prog_table->entry[i].scsi.blockno) { | |
537 | valid_entries[i] = true; | |
538 | program_table_entries++; | |
685d49a6 | 539 | } |
685d49a6 AG |
540 | } |
541 | ||
542 | debug_print_int("program table entries", program_table_entries); | |
a00b33d9 | 543 | IPL_assert(program_table_entries != 0, "Empty Program Table"); |
685d49a6 | 544 | |
ffb4a1c8 | 545 | if (menu_is_enabled_enum()) { |
622b3917 | 546 | loadparm = menu_get_enum_boot_index(valid_entries); |
ffb4a1c8 CW |
547 | } |
548 | ||
5340eb07 | 549 | debug_print_int("loadparm", loadparm); |
6df2a829 | 550 | IPL_assert(loadparm < MAX_BOOT_ENTRIES, "loadparm value greater than" |
5340eb07 CW |
551 | " maximum number of boot entries allowed"); |
552 | ||
553 | zipl_run(&prog_table->entry[loadparm].scsi); /* no return */ | |
685d49a6 | 554 | } |
a00b33d9 | 555 | |
866cac91 MS |
556 | /*********************************************************************** |
557 | * IPL El Torito ISO9660 image or DVD | |
558 | */ | |
559 | ||
560 | static bool is_iso_bc_entry_compatible(IsoBcSection *s) | |
561 | { | |
ba21f0cc MS |
562 | uint8_t *magic_sec = (uint8_t *)(sec + ISO_SECTOR_SIZE); |
563 | ||
564 | if (s->unused || !s->sector_count) { | |
565 | return false; | |
566 | } | |
567 | read_iso_sector(bswap32(s->load_rba), magic_sec, | |
568 | "Failed to read image sector 0"); | |
569 | ||
570 | /* Checking bytes 8 - 32 for S390 Linux magic */ | |
fc0e2087 | 571 | return !memcmp(magic_sec + 8, linux_s390_magic, 24); |
866cac91 MS |
572 | } |
573 | ||
869648e8 MS |
574 | /* Location of the current sector of the directory */ |
575 | static uint32_t sec_loc[ISO9660_MAX_DIR_DEPTH]; | |
576 | /* Offset in the current sector of the directory */ | |
577 | static uint32_t sec_offset[ISO9660_MAX_DIR_DEPTH]; | |
578 | /* Remained directory space in bytes */ | |
579 | static uint32_t dir_rem[ISO9660_MAX_DIR_DEPTH]; | |
580 | ||
581 | static inline uint32_t iso_get_file_size(uint32_t load_rba) | |
582 | { | |
583 | IsoVolDesc *vd = (IsoVolDesc *)sec; | |
584 | IsoDirHdr *cur_record = &vd->vd.primary.rootdir; | |
585 | uint8_t *temp = sec + ISO_SECTOR_SIZE; | |
586 | int level = 0; | |
587 | ||
588 | read_iso_sector(ISO_PRIMARY_VD_SECTOR, sec, | |
589 | "Failed to read ISO primary descriptor"); | |
590 | sec_loc[0] = iso_733_to_u32(cur_record->ext_loc); | |
591 | dir_rem[0] = 0; | |
592 | sec_offset[0] = 0; | |
593 | ||
594 | while (level >= 0) { | |
595 | IPL_assert(sec_offset[level] <= ISO_SECTOR_SIZE, | |
596 | "Directory tree structure violation"); | |
597 | ||
598 | cur_record = (IsoDirHdr *)(temp + sec_offset[level]); | |
599 | ||
600 | if (sec_offset[level] == 0) { | |
601 | read_iso_sector(sec_loc[level], temp, | |
602 | "Failed to read ISO directory"); | |
603 | if (dir_rem[level] == 0) { | |
604 | /* Skip self and parent records */ | |
605 | dir_rem[level] = iso_733_to_u32(cur_record->data_len) - | |
606 | cur_record->dr_len; | |
607 | sec_offset[level] += cur_record->dr_len; | |
608 | ||
609 | cur_record = (IsoDirHdr *)(temp + sec_offset[level]); | |
610 | dir_rem[level] -= cur_record->dr_len; | |
611 | sec_offset[level] += cur_record->dr_len; | |
612 | continue; | |
613 | } | |
614 | } | |
615 | ||
616 | if (!cur_record->dr_len || sec_offset[level] == ISO_SECTOR_SIZE) { | |
617 | /* Zero-padding and/or the end of current sector */ | |
618 | dir_rem[level] -= ISO_SECTOR_SIZE - sec_offset[level]; | |
619 | sec_offset[level] = 0; | |
620 | sec_loc[level]++; | |
621 | } else { | |
622 | /* The directory record is valid */ | |
623 | if (load_rba == iso_733_to_u32(cur_record->ext_loc)) { | |
624 | return iso_733_to_u32(cur_record->data_len); | |
625 | } | |
626 | ||
627 | dir_rem[level] -= cur_record->dr_len; | |
628 | sec_offset[level] += cur_record->dr_len; | |
629 | ||
630 | if (cur_record->file_flags & 0x2) { | |
631 | /* Subdirectory */ | |
632 | if (level == ISO9660_MAX_DIR_DEPTH - 1) { | |
633 | sclp_print("ISO-9660 directory depth limit exceeded\n"); | |
634 | } else { | |
635 | level++; | |
636 | sec_loc[level] = iso_733_to_u32(cur_record->ext_loc); | |
637 | sec_offset[level] = 0; | |
638 | dir_rem[level] = 0; | |
639 | continue; | |
640 | } | |
641 | } | |
642 | } | |
643 | ||
644 | if (dir_rem[level] == 0) { | |
645 | /* Nothing remaining */ | |
646 | level--; | |
647 | read_iso_sector(sec_loc[level], temp, | |
648 | "Failed to read ISO directory"); | |
649 | } | |
650 | } | |
651 | ||
652 | return 0; | |
653 | } | |
654 | ||
866cac91 MS |
655 | static void load_iso_bc_entry(IsoBcSection *load) |
656 | { | |
657 | IsoBcSection s = *load; | |
658 | /* | |
659 | * According to spec, extent for each file | |
660 | * is padded and ISO_SECTOR_SIZE bytes aligned | |
661 | */ | |
662 | uint32_t blks_to_load = bswap16(s.sector_count) >> ET_SECTOR_SHIFT; | |
869648e8 MS |
663 | uint32_t real_size = iso_get_file_size(bswap32(s.load_rba)); |
664 | ||
665 | if (real_size) { | |
666 | /* Round up blocks to load */ | |
667 | blks_to_load = (real_size + ISO_SECTOR_SIZE - 1) / ISO_SECTOR_SIZE; | |
668 | sclp_print("ISO boot image size verified\n"); | |
669 | } else { | |
670 | sclp_print("ISO boot image size could not be verified\n"); | |
671 | } | |
866cac91 MS |
672 | |
673 | read_iso_boot_image(bswap32(s.load_rba), | |
674 | (void *)((uint64_t)bswap16(s.load_segment)), | |
675 | blks_to_load); | |
676 | ||
9a848adf | 677 | jump_to_low_kernel(); |
866cac91 MS |
678 | } |
679 | ||
680 | static uint32_t find_iso_bc(void) | |
681 | { | |
682 | IsoVolDesc *vd = (IsoVolDesc *)sec; | |
683 | uint32_t block_num = ISO_PRIMARY_VD_SECTOR; | |
684 | ||
685 | if (virtio_read_many(block_num++, sec, 1)) { | |
686 | /* If primary vd cannot be read, there is no boot catalog */ | |
687 | return 0; | |
688 | } | |
689 | ||
690 | while (is_iso_vd_valid(vd) && vd->type != VOL_DESC_TERMINATOR) { | |
691 | if (vd->type == VOL_DESC_TYPE_BOOT) { | |
692 | IsoVdElTorito *et = &vd->vd.boot; | |
693 | ||
fc0e2087 | 694 | if (!memcmp(&et->el_torito[0], el_torito_magic, 32)) { |
866cac91 MS |
695 | return bswap32(et->bc_offset); |
696 | } | |
697 | } | |
698 | read_iso_sector(block_num++, sec, | |
699 | "Failed to read ISO volume descriptor"); | |
700 | } | |
701 | ||
702 | return 0; | |
703 | } | |
704 | ||
705 | static IsoBcSection *find_iso_bc_entry(void) | |
706 | { | |
707 | IsoBcEntry *e = (IsoBcEntry *)sec; | |
708 | uint32_t offset = find_iso_bc(); | |
709 | int i; | |
7a9762bf | 710 | unsigned int loadparm = get_loadparm_index(); |
866cac91 MS |
711 | |
712 | if (!offset) { | |
713 | return NULL; | |
714 | } | |
715 | ||
716 | read_iso_sector(offset, sec, "Failed to read El Torito boot catalog"); | |
717 | ||
718 | if (!is_iso_bc_valid(e)) { | |
719 | /* The validation entry is mandatory */ | |
c9262e8a | 720 | panic("No valid boot catalog found!\n"); |
866cac91 MS |
721 | return NULL; |
722 | } | |
723 | ||
724 | /* | |
725 | * Each entry has 32 bytes size, so one sector cannot contain > 64 entries. | |
726 | * We consider only boot catalogs with no more than 64 entries. | |
727 | */ | |
728 | for (i = 1; i < ISO_BC_ENTRY_PER_SECTOR; i++) { | |
729 | if (e[i].id == ISO_BC_BOOTABLE_SECTION) { | |
730 | if (is_iso_bc_entry_compatible(&e[i].body.sect)) { | |
7a9762bf ED |
731 | if (loadparm <= 1) { |
732 | /* found, default, or unspecified */ | |
733 | return &e[i].body.sect; | |
734 | } | |
735 | loadparm--; | |
866cac91 MS |
736 | } |
737 | } | |
738 | } | |
739 | ||
c9262e8a | 740 | panic("No suitable boot entry found on ISO-9660 media!\n"); |
866cac91 MS |
741 | |
742 | return NULL; | |
743 | } | |
744 | ||
745 | static void ipl_iso_el_torito(void) | |
746 | { | |
747 | IsoBcSection *s = find_iso_bc_entry(); | |
748 | ||
749 | if (s) { | |
750 | load_iso_bc_entry(s); | |
751 | /* no return */ | |
752 | } | |
753 | } | |
754 | ||
a00b33d9 | 755 | /*********************************************************************** |
f0386820 | 756 | * Bus specific IPL sequences |
a00b33d9 ED |
757 | */ |
758 | ||
f0386820 | 759 | static void zipl_load_vblk(void) |
a00b33d9 | 760 | { |
866cac91 MS |
761 | if (virtio_guessed_disk_nature()) { |
762 | virtio_assume_iso9660(); | |
763 | } | |
764 | ipl_iso_el_torito(); | |
765 | ||
564e52b9 ED |
766 | if (virtio_guessed_disk_nature()) { |
767 | sclp_print("Using guessed DASD geometry.\n"); | |
768 | virtio_assume_eckd(); | |
769 | } | |
f0386820 ED |
770 | ipl_eckd(); |
771 | } | |
772 | ||
773 | static void zipl_load_vscsi(void) | |
774 | { | |
775 | if (virtio_get_block_size() == VIRTIO_ISO_BLOCK_SIZE) { | |
776 | /* Is it an ISO image in non-CD drive? */ | |
777 | ipl_iso_el_torito(); | |
564e52b9 ED |
778 | } |
779 | ||
f0386820 ED |
780 | sclp_print("Using guessed DASD geometry.\n"); |
781 | virtio_assume_eckd(); | |
782 | ipl_eckd(); | |
783 | } | |
564e52b9 | 784 | |
f0386820 ED |
785 | /*********************************************************************** |
786 | * IPL starts here | |
787 | */ | |
788 | ||
789 | void zipl_load(void) | |
790 | { | |
99b72e0f FA |
791 | VDev *vdev = virtio_get_device(); |
792 | ||
793 | if (vdev->is_cdrom) { | |
f0386820 ED |
794 | ipl_iso_el_torito(); |
795 | panic("\n! Cannot IPL this ISO image !\n"); | |
e0aff4aa | 796 | } |
a00b33d9 | 797 | |
99b72e0f FA |
798 | if (virtio_get_device_type() == VIRTIO_ID_NET) { |
799 | jump_to_IPL_code(vdev->netboot_start_addr); | |
800 | } | |
801 | ||
f0386820 ED |
802 | ipl_scsi(); |
803 | ||
804 | switch (virtio_get_device_type()) { | |
805 | case VIRTIO_ID_BLOCK: | |
806 | zipl_load_vblk(); | |
807 | break; | |
808 | case VIRTIO_ID_SCSI: | |
809 | zipl_load_vscsi(); | |
810 | break; | |
811 | default: | |
812 | panic("\n! Unknown IPL device type !\n"); | |
813 | } | |
14f56a2e | 814 | |
c9262e8a | 815 | panic("\n* this can never happen *\n"); |
a00b33d9 | 816 | } |