1685d49a6SAlexander Graf /*
2685d49a6SAlexander Graf * QEMU S390 bootmap interpreter
3685d49a6SAlexander Graf *
4685d49a6SAlexander Graf * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
5685d49a6SAlexander Graf *
6685d49a6SAlexander Graf * This work is licensed under the terms of the GNU GPL, version 2 or (at
7685d49a6SAlexander Graf * your option) any later version. See the COPYING file in the top-level
8685d49a6SAlexander Graf * directory.
9685d49a6SAlexander Graf */
10685d49a6SAlexander Graf
119f427883SJared Rossi #include <string.h>
129f427883SJared Rossi #include <stdio.h>
13685d49a6SAlexander Graf #include "s390-ccw.h"
144906a4e4SJanosch Frank #include "s390-arch.h"
1526f2bbd6SEugene (jno) Dvurechenski #include "bootmap.h"
1691a03f9bSEugene (jno) Dvurechenski #include "virtio.h"
17824df3b8SThomas Huth #include "bswap.h"
18685d49a6SAlexander Graf
19a00b33d9SEugene (jno) Dvurechenski #ifdef DEBUG
20abd696e4SEugene (jno) Dvurechenski /* #define DEBUG_FALLBACK */
21a00b33d9SEugene (jno) Dvurechenski #endif
22685d49a6SAlexander Graf
23685d49a6SAlexander Graf #ifdef DEBUG_FALLBACK
24685d49a6SAlexander Graf #define dputs(txt) \
259f427883SJared Rossi do { printf("zipl: " txt); } while (0)
26685d49a6SAlexander Graf #else
27685d49a6SAlexander Graf #define dputs(fmt, ...) \
28685d49a6SAlexander Graf do { } while (0)
29685d49a6SAlexander Graf #endif
30685d49a6SAlexander Graf
31685d49a6SAlexander Graf /* Scratch space */
32a00b33d9SEugene (jno) Dvurechenski static uint8_t sec[MAX_SECTOR_SIZE*4] __attribute__((__aligned__(PAGE_SIZE)));
33685d49a6SAlexander Graf
346af978aeSThomas Huth const uint8_t el_torito_magic[] = "EL TORITO SPECIFICATION"
356af978aeSThomas Huth "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
366af978aeSThomas Huth
376af978aeSThomas Huth /*
386af978aeSThomas Huth * Match two CCWs located after PSW and eight filler bytes.
396af978aeSThomas Huth * From libmagic and arch/s390/kernel/head.S.
406af978aeSThomas Huth */
416af978aeSThomas Huth const uint8_t linux_s390_magic[] = "\x02\x00\x00\x18\x60\x00\x00\x50\x02\x00"
426af978aeSThomas Huth "\x00\x68\x60\x00\x00\x50\x40\x40\x40\x40"
436af978aeSThomas Huth "\x40\x40\x40\x40";
446af978aeSThomas Huth
is_iso_vd_valid(IsoVolDesc * vd)456af978aeSThomas Huth static inline bool is_iso_vd_valid(IsoVolDesc *vd)
466af978aeSThomas Huth {
476af978aeSThomas Huth const uint8_t vol_desc_magic[] = "CD001";
486af978aeSThomas Huth
496af978aeSThomas Huth return !memcmp(&vd->ident[0], vol_desc_magic, 5) &&
506af978aeSThomas Huth vd->version == 0x1 &&
516af978aeSThomas Huth vd->type <= VOL_DESC_TYPE_PARTITION;
526af978aeSThomas Huth }
536af978aeSThomas Huth
54a00b33d9SEugene (jno) Dvurechenski /***********************************************************************
55e0aff4aaSEugene (jno) Dvurechenski * IPL an ECKD DASD (CDL or LDL/CMS format)
56e0aff4aaSEugene (jno) Dvurechenski */
57e0aff4aaSEugene (jno) Dvurechenski
58e0aff4aaSEugene (jno) Dvurechenski static unsigned char _bprs[8*1024]; /* guessed "max" ECKD sector size */
59f17a8430SChristian Borntraeger static const int max_bprs_entries = sizeof(_bprs) / sizeof(ExtEckdBlockPtr);
60ba831b25SCollin L. Walling static uint8_t _s2[MAX_SECTOR_SIZE * 3] __attribute__((__aligned__(PAGE_SIZE)));
61ba831b25SCollin L. Walling static void *s2_prev_blk = _s2;
62ba831b25SCollin L. Walling static void *s2_cur_blk = _s2 + MAX_SECTOR_SIZE;
63ba831b25SCollin L. Walling static void *s2_next_blk = _s2 + MAX_SECTOR_SIZE * 2;
64e0aff4aaSEugene (jno) Dvurechenski
verify_boot_info(BootInfo * bip)65*0181e237SJared Rossi static inline int verify_boot_info(BootInfo *bip)
66564e52b9SEugene (jno) Dvurechenski {
67*0181e237SJared Rossi if (!magic_match(bip->magic, ZIPL_MAGIC)) {
68*0181e237SJared Rossi puts("No zIPL sig in BootInfo");
69*0181e237SJared Rossi return -EINVAL;
70*0181e237SJared Rossi }
71*0181e237SJared Rossi if (bip->version != BOOT_INFO_VERSION) {
72*0181e237SJared Rossi puts("Wrong zIPL version");
73*0181e237SJared Rossi return -EINVAL;
74*0181e237SJared Rossi }
75*0181e237SJared Rossi if (bip->bp_type != BOOT_INFO_BP_TYPE_IPL) {
76*0181e237SJared Rossi puts("DASD is not for IPL");
77*0181e237SJared Rossi return -ENODEV;
78*0181e237SJared Rossi }
79*0181e237SJared Rossi if (bip->dev_type != BOOT_INFO_DEV_TYPE_ECKD) {
80*0181e237SJared Rossi puts("DASD is not ECKD");
81*0181e237SJared Rossi return -ENODEV;
82*0181e237SJared Rossi }
83*0181e237SJared Rossi if (bip->flags != BOOT_INFO_FLAGS_ARCH) {
84*0181e237SJared Rossi puts("Not for this arch");
85*0181e237SJared Rossi return -EINVAL;
86*0181e237SJared Rossi }
87*0181e237SJared Rossi if (!block_size_ok(bip->bp.ipl.bm_ptr.eckd.bptr.size)) {
88*0181e237SJared Rossi puts("Bad block size in zIPL section of 1st record");
89*0181e237SJared Rossi return -EINVAL;
90*0181e237SJared Rossi }
91*0181e237SJared Rossi
92*0181e237SJared Rossi return 0;
93564e52b9SEugene (jno) Dvurechenski }
94564e52b9SEugene (jno) Dvurechenski
eckd_format_chs(ExtEckdBlockPtr * ptr,bool ldipl,uint64_t * c,uint64_t * h,uint64_t * s)958af5d141SJared Rossi static void eckd_format_chs(ExtEckdBlockPtr *ptr, bool ldipl,
968af5d141SJared Rossi uint64_t *c,
978af5d141SJared Rossi uint64_t *h,
988af5d141SJared Rossi uint64_t *s)
998af5d141SJared Rossi {
1008af5d141SJared Rossi if (ldipl) {
1018af5d141SJared Rossi *c = ptr->ldptr.chs.cylinder;
1028af5d141SJared Rossi *h = ptr->ldptr.chs.head;
1038af5d141SJared Rossi *s = ptr->ldptr.chs.sector;
1048af5d141SJared Rossi } else {
1058af5d141SJared Rossi *c = ptr->bptr.chs.cylinder;
1068af5d141SJared Rossi *h = ptr->bptr.chs.head;
1078af5d141SJared Rossi *s = ptr->bptr.chs.sector;
1088af5d141SJared Rossi }
1098af5d141SJared Rossi }
1108af5d141SJared Rossi
eckd_chs_to_block(uint64_t c,uint64_t h,uint64_t s)1118af5d141SJared Rossi static block_number_t eckd_chs_to_block(uint64_t c, uint64_t h, uint64_t s)
112e0aff4aaSEugene (jno) Dvurechenski {
113e0aff4aaSEugene (jno) Dvurechenski const uint64_t sectors = virtio_get_sectors();
114e0aff4aaSEugene (jno) Dvurechenski const uint64_t heads = virtio_get_heads();
1158af5d141SJared Rossi const uint64_t cylinder = c + ((h & 0xfff0) << 12);
1168af5d141SJared Rossi const uint64_t head = h & 0x000f;
117e0aff4aaSEugene (jno) Dvurechenski const block_number_t block = sectors * heads * cylinder
118e0aff4aaSEugene (jno) Dvurechenski + sectors * head
1198af5d141SJared Rossi + s - 1; /* block nr starts with zero */
120e0aff4aaSEugene (jno) Dvurechenski return block;
121e0aff4aaSEugene (jno) Dvurechenski }
122e0aff4aaSEugene (jno) Dvurechenski
eckd_block_num(EckdCHS * chs)1238af5d141SJared Rossi static block_number_t eckd_block_num(EckdCHS *chs)
124f04db28bSEugene (jno) Dvurechenski {
1258af5d141SJared Rossi return eckd_chs_to_block(chs->cylinder, chs->head, chs->sector);
1268af5d141SJared Rossi }
127f04db28bSEugene (jno) Dvurechenski
gen_eckd_block_num(ExtEckdBlockPtr * ptr,bool ldipl)1288af5d141SJared Rossi static block_number_t gen_eckd_block_num(ExtEckdBlockPtr *ptr, bool ldipl)
1298af5d141SJared Rossi {
1308af5d141SJared Rossi uint64_t cyl, head, sec;
1318af5d141SJared Rossi eckd_format_chs(ptr, ldipl, &cyl, &head, &sec);
1328af5d141SJared Rossi return eckd_chs_to_block(cyl, head, sec);
1338af5d141SJared Rossi }
1348af5d141SJared Rossi
eckd_valid_chs(uint64_t cyl,uint64_t head,uint64_t sector)1358af5d141SJared Rossi static bool eckd_valid_chs(uint64_t cyl, uint64_t head, uint64_t sector)
1368af5d141SJared Rossi {
137f04db28bSEugene (jno) Dvurechenski if (head >= virtio_get_heads()
1388af5d141SJared Rossi || sector > virtio_get_sectors()
1398af5d141SJared Rossi || sector <= 0) {
140f04db28bSEugene (jno) Dvurechenski return false;
141f04db28bSEugene (jno) Dvurechenski }
142f04db28bSEugene (jno) Dvurechenski
143f04db28bSEugene (jno) Dvurechenski if (!virtio_guessed_disk_nature() &&
1448af5d141SJared Rossi eckd_chs_to_block(cyl, head, sector) >= virtio_get_blocks()) {
145f04db28bSEugene (jno) Dvurechenski return false;
146f04db28bSEugene (jno) Dvurechenski }
147f04db28bSEugene (jno) Dvurechenski
148f04db28bSEugene (jno) Dvurechenski return true;
149f04db28bSEugene (jno) Dvurechenski }
150f04db28bSEugene (jno) Dvurechenski
eckd_valid_address(ExtEckdBlockPtr * ptr,bool ldipl)1518af5d141SJared Rossi static bool eckd_valid_address(ExtEckdBlockPtr *ptr, bool ldipl)
1528af5d141SJared Rossi {
1538af5d141SJared Rossi uint64_t cyl, head, sec;
1548af5d141SJared Rossi eckd_format_chs(ptr, ldipl, &cyl, &head, &sec);
1558af5d141SJared Rossi return eckd_valid_chs(cyl, head, sec);
1568af5d141SJared Rossi }
1578af5d141SJared Rossi
load_eckd_segments(block_number_t blk,bool ldipl,uint64_t * address)1588af5d141SJared Rossi static block_number_t load_eckd_segments(block_number_t blk, bool ldipl,
1598af5d141SJared Rossi uint64_t *address)
160e0aff4aaSEugene (jno) Dvurechenski {
161e0aff4aaSEugene (jno) Dvurechenski block_number_t block_nr;
1628af5d141SJared Rossi int j, rc, count;
163e0aff4aaSEugene (jno) Dvurechenski BootMapPointer *bprs = (void *)_bprs;
164e0aff4aaSEugene (jno) Dvurechenski bool more_data;
165e0aff4aaSEugene (jno) Dvurechenski
166e0aff4aaSEugene (jno) Dvurechenski memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs));
16780631527SJared Rossi if (virtio_read(blk, bprs)) {
16880631527SJared Rossi puts("BPRS read failed");
16980631527SJared Rossi return ERROR_BLOCK_NR;
17080631527SJared Rossi }
171e0aff4aaSEugene (jno) Dvurechenski
172e0aff4aaSEugene (jno) Dvurechenski do {
173e0aff4aaSEugene (jno) Dvurechenski more_data = false;
174e0aff4aaSEugene (jno) Dvurechenski for (j = 0;; j++) {
1758af5d141SJared Rossi block_nr = gen_eckd_block_num(&bprs[j].xeckd, ldipl);
176e0aff4aaSEugene (jno) Dvurechenski if (is_null_block_number(block_nr)) { /* end of chunk */
17780631527SJared Rossi return NULL_BLOCK_NR;
178e0aff4aaSEugene (jno) Dvurechenski }
179e0aff4aaSEugene (jno) Dvurechenski
180e0aff4aaSEugene (jno) Dvurechenski /* we need the updated blockno for the next indirect entry
181e0aff4aaSEugene (jno) Dvurechenski * in the chain, but don't want to advance address
182e0aff4aaSEugene (jno) Dvurechenski */
183e0aff4aaSEugene (jno) Dvurechenski if (j == (max_bprs_entries - 1)) {
184e0aff4aaSEugene (jno) Dvurechenski break;
185e0aff4aaSEugene (jno) Dvurechenski }
186e0aff4aaSEugene (jno) Dvurechenski
1878af5d141SJared Rossi /* List directed pointer does not store block size */
18880631527SJared Rossi if (!ldipl && !block_size_ok(bprs[j].xeckd.bptr.size)) {
18980631527SJared Rossi puts("Bad chunk block size");
19080631527SJared Rossi return ERROR_BLOCK_NR;
19180631527SJared Rossi }
192e0aff4aaSEugene (jno) Dvurechenski
1938af5d141SJared Rossi if (!eckd_valid_address(&bprs[j].xeckd, ldipl)) {
1948af5d141SJared Rossi /*
1958af5d141SJared Rossi * If an invalid address is found during LD-IPL then break and
19680631527SJared Rossi * retry as CCW-IPL, otherwise abort on error
1978af5d141SJared Rossi */
19880631527SJared Rossi if (!ldipl) {
19980631527SJared Rossi puts("Bad chunk ECKD address");
20080631527SJared Rossi return ERROR_BLOCK_NR;
20180631527SJared Rossi }
2028af5d141SJared Rossi break;
2038af5d141SJared Rossi }
2048af5d141SJared Rossi
2058af5d141SJared Rossi if (ldipl) {
2068af5d141SJared Rossi count = bprs[j].xeckd.ldptr.count;
2078af5d141SJared Rossi } else {
2088af5d141SJared Rossi count = bprs[j].xeckd.bptr.count;
2098af5d141SJared Rossi }
2108af5d141SJared Rossi
2118af5d141SJared Rossi if (count == 0 && unused_space(&bprs[j + 1],
212e0aff4aaSEugene (jno) Dvurechenski sizeof(EckdBlockPtr))) {
213e0aff4aaSEugene (jno) Dvurechenski /* This is a "continue" pointer.
214e0aff4aaSEugene (jno) Dvurechenski * This ptr should be the last one in the current
215e0aff4aaSEugene (jno) Dvurechenski * script section.
216e0aff4aaSEugene (jno) Dvurechenski * I.e. the next ptr must point to the unused memory area
217e0aff4aaSEugene (jno) Dvurechenski */
218e0aff4aaSEugene (jno) Dvurechenski memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs));
21980631527SJared Rossi if (virtio_read(block_nr, bprs)) {
22080631527SJared Rossi puts("BPRS continuation read failed");
22180631527SJared Rossi return ERROR_BLOCK_NR;
22280631527SJared Rossi }
223e0aff4aaSEugene (jno) Dvurechenski more_data = true;
224e0aff4aaSEugene (jno) Dvurechenski break;
225e0aff4aaSEugene (jno) Dvurechenski }
226e0aff4aaSEugene (jno) Dvurechenski
227e0aff4aaSEugene (jno) Dvurechenski /* Load (count+1) blocks of code at (block_nr)
228e0aff4aaSEugene (jno) Dvurechenski * to memory (address).
229e0aff4aaSEugene (jno) Dvurechenski */
2308af5d141SJared Rossi rc = virtio_read_many(block_nr, (void *)(*address), count + 1);
23180631527SJared Rossi if (rc != 0) {
23280631527SJared Rossi puts("Code chunk read failed");
23380631527SJared Rossi return ERROR_BLOCK_NR;
23480631527SJared Rossi }
235e0aff4aaSEugene (jno) Dvurechenski
2368af5d141SJared Rossi *address += (count + 1) * virtio_get_block_size();
237e0aff4aaSEugene (jno) Dvurechenski }
238e0aff4aaSEugene (jno) Dvurechenski } while (more_data);
239e0aff4aaSEugene (jno) Dvurechenski return block_nr;
240e0aff4aaSEugene (jno) Dvurechenski }
241e0aff4aaSEugene (jno) Dvurechenski
find_zipl_boot_menu_banner(int * offset)242ba831b25SCollin L. Walling static bool find_zipl_boot_menu_banner(int *offset)
243ba831b25SCollin L. Walling {
244ba831b25SCollin L. Walling int i;
245ba831b25SCollin L. Walling
246ba831b25SCollin L. Walling /* Menu banner starts with "zIPL" */
2475f97ba0cSMarc Hartmayer for (i = 0; i <= virtio_get_block_size() - 4; i++) {
248ba831b25SCollin L. Walling if (magic_match(s2_cur_blk + i, ZIPL_MAGIC_EBCDIC)) {
249ba831b25SCollin L. Walling *offset = i;
250ba831b25SCollin L. Walling return true;
251ba831b25SCollin L. Walling }
252ba831b25SCollin L. Walling }
253ba831b25SCollin L. Walling
254ba831b25SCollin L. Walling return false;
255ba831b25SCollin L. Walling }
256ba831b25SCollin L. Walling
eckd_get_boot_menu_index(block_number_t s1b_block_nr)257ba831b25SCollin L. Walling static int eckd_get_boot_menu_index(block_number_t s1b_block_nr)
258ba831b25SCollin L. Walling {
259ba831b25SCollin L. Walling block_number_t cur_block_nr;
260ba831b25SCollin L. Walling block_number_t prev_block_nr = 0;
261ba831b25SCollin L. Walling block_number_t next_block_nr = 0;
262ba831b25SCollin L. Walling EckdStage1b *s1b = (void *)sec;
263ba831b25SCollin L. Walling int banner_offset;
264ba831b25SCollin L. Walling int i;
265ba831b25SCollin L. Walling
266ba831b25SCollin L. Walling /* Get Stage1b data */
267ba831b25SCollin L. Walling memset(sec, FREE_SPACE_FILLER, sizeof(sec));
26880631527SJared Rossi if (virtio_read(s1b_block_nr, s1b)) {
26980631527SJared Rossi puts("Cannot read stage1b boot loader");
27080631527SJared Rossi return -EIO;
27180631527SJared Rossi }
272ba831b25SCollin L. Walling
273ba831b25SCollin L. Walling memset(_s2, FREE_SPACE_FILLER, sizeof(_s2));
274ba831b25SCollin L. Walling
275ba831b25SCollin L. Walling /* Get Stage2 data */
276ba831b25SCollin L. Walling for (i = 0; i < STAGE2_BLK_CNT_MAX; i++) {
277ba831b25SCollin L. Walling cur_block_nr = eckd_block_num(&s1b->seek[i].chs);
278ba831b25SCollin L. Walling
279468184ecSMarc Hartmayer if (!cur_block_nr || is_null_block_number(cur_block_nr)) {
280ba831b25SCollin L. Walling break;
281ba831b25SCollin L. Walling }
282ba831b25SCollin L. Walling
28380631527SJared Rossi if (virtio_read(cur_block_nr, s2_cur_blk)) {
28480631527SJared Rossi puts("Cannot read stage2 boot loader");
28580631527SJared Rossi return -EIO;
28680631527SJared Rossi }
287ba831b25SCollin L. Walling
288ba831b25SCollin L. Walling if (find_zipl_boot_menu_banner(&banner_offset)) {
289ba831b25SCollin L. Walling /*
290ba831b25SCollin L. Walling * Load the adjacent blocks to account for the
291ba831b25SCollin L. Walling * possibility of menu data spanning multiple blocks.
292ba831b25SCollin L. Walling */
293ba831b25SCollin L. Walling if (prev_block_nr) {
29480631527SJared Rossi if (virtio_read(prev_block_nr, s2_prev_blk)) {
29580631527SJared Rossi puts("Cannot read stage2 boot loader");
29680631527SJared Rossi return -EIO;
29780631527SJared Rossi }
298ba831b25SCollin L. Walling }
299ba831b25SCollin L. Walling
300ba831b25SCollin L. Walling if (i + 1 < STAGE2_BLK_CNT_MAX) {
301ba831b25SCollin L. Walling next_block_nr = eckd_block_num(&s1b->seek[i + 1].chs);
302ba831b25SCollin L. Walling }
303ba831b25SCollin L. Walling
304a6625d38SMarc Hartmayer if (next_block_nr && !is_null_block_number(next_block_nr)) {
30580631527SJared Rossi if (virtio_read(next_block_nr, s2_next_blk)) {
30680631527SJared Rossi puts("Cannot read stage2 boot loader");
30780631527SJared Rossi return -EIO;
30880631527SJared Rossi }
309ba831b25SCollin L. Walling }
310ba831b25SCollin L. Walling
311ba831b25SCollin L. Walling return menu_get_zipl_boot_index(s2_cur_blk + banner_offset);
312ba831b25SCollin L. Walling }
313ba831b25SCollin L. Walling
314ba831b25SCollin L. Walling prev_block_nr = cur_block_nr;
315ba831b25SCollin L. Walling }
316ba831b25SCollin L. Walling
3179f427883SJared Rossi printf("No zipl boot menu data found. Booting default entry.");
318ba831b25SCollin L. Walling return 0;
319ba831b25SCollin L. Walling }
320ba831b25SCollin L. Walling
run_eckd_boot_script(block_number_t bmt_block_nr,block_number_t s1b_block_nr)32180631527SJared Rossi static int run_eckd_boot_script(block_number_t bmt_block_nr,
322ba831b25SCollin L. Walling block_number_t s1b_block_nr)
323e0aff4aaSEugene (jno) Dvurechenski {
324e0aff4aaSEugene (jno) Dvurechenski int i;
32582ca3941SFarhan Ali unsigned int loadparm = get_loadparm_index();
326e0aff4aaSEugene (jno) Dvurechenski block_number_t block_nr;
327e0aff4aaSEugene (jno) Dvurechenski uint64_t address;
3285340eb07SCollin L. Walling BootMapTable *bmt = (void *)sec;
329e0aff4aaSEugene (jno) Dvurechenski BootMapScript *bms = (void *)sec;
3308af5d141SJared Rossi /* The S1B block number is NULL_BLOCK_NR if and only if it's an LD-IPL */
3318af5d141SJared Rossi bool ldipl = (s1b_block_nr == NULL_BLOCK_NR);
332e0aff4aaSEugene (jno) Dvurechenski
3338af5d141SJared Rossi if (menu_is_enabled_zipl() && !ldipl) {
334ba831b25SCollin L. Walling loadparm = eckd_get_boot_menu_index(s1b_block_nr);
335ba831b25SCollin L. Walling }
336ba831b25SCollin L. Walling
33782ca3941SFarhan Ali debug_print_int("loadparm", loadparm);
33880631527SJared Rossi if (loadparm >= MAX_BOOT_ENTRIES) {
33980631527SJared Rossi puts("loadparm value greater than max number of boot entries allowed");
34080631527SJared Rossi return -EINVAL;
34180631527SJared Rossi }
34282ca3941SFarhan Ali
343e0aff4aaSEugene (jno) Dvurechenski memset(sec, FREE_SPACE_FILLER, sizeof(sec));
34480631527SJared Rossi if (virtio_read(bmt_block_nr, sec)) {
34580631527SJared Rossi puts("Cannot read Boot Map Table");
34680631527SJared Rossi return -EIO;
34780631527SJared Rossi }
348e0aff4aaSEugene (jno) Dvurechenski
3498af5d141SJared Rossi block_nr = gen_eckd_block_num(&bmt->entry[loadparm].xeckd, ldipl);
35080631527SJared Rossi if (block_nr == NULL_BLOCK_NR) {
35180631527SJared Rossi puts("Cannot find Boot Map Table Entry");
35280631527SJared Rossi return -EIO;
35380631527SJared Rossi }
354e0aff4aaSEugene (jno) Dvurechenski
355e0aff4aaSEugene (jno) Dvurechenski memset(sec, FREE_SPACE_FILLER, sizeof(sec));
35680631527SJared Rossi if (virtio_read(block_nr, sec)) {
35780631527SJared Rossi puts("Cannot read Boot Map Script");
35880631527SJared Rossi return -EIO;
35980631527SJared Rossi }
360e0aff4aaSEugene (jno) Dvurechenski
3612497b4a3SJason J. Herne for (i = 0; bms->entry[i].type == BOOT_SCRIPT_LOAD ||
3622497b4a3SJason J. Herne bms->entry[i].type == BOOT_SCRIPT_SIGNATURE; i++) {
3632497b4a3SJason J. Herne
3642497b4a3SJason J. Herne /* We don't support secure boot yet, so we skip signature entries */
3652497b4a3SJason J. Herne if (bms->entry[i].type == BOOT_SCRIPT_SIGNATURE) {
3662497b4a3SJason J. Herne continue;
3672497b4a3SJason J. Herne }
3682497b4a3SJason J. Herne
369e0aff4aaSEugene (jno) Dvurechenski address = bms->entry[i].address.load_address;
3708af5d141SJared Rossi block_nr = gen_eckd_block_num(&bms->entry[i].blkptr.xeckd, ldipl);
371e0aff4aaSEugene (jno) Dvurechenski
372e0aff4aaSEugene (jno) Dvurechenski do {
3738af5d141SJared Rossi block_nr = load_eckd_segments(block_nr, ldipl, &address);
37480631527SJared Rossi if (block_nr == ERROR_BLOCK_NR) {
37580631527SJared Rossi return ldipl ? 0 : -EIO;
37680631527SJared Rossi }
37780631527SJared Rossi } while (block_nr != NULL_BLOCK_NR);
378e0aff4aaSEugene (jno) Dvurechenski }
379e0aff4aaSEugene (jno) Dvurechenski
3808af5d141SJared Rossi if (ldipl && bms->entry[i].type != BOOT_SCRIPT_EXEC) {
3818af5d141SJared Rossi /* Abort LD-IPL and retry as CCW-IPL */
38280631527SJared Rossi return 0;
3838af5d141SJared Rossi }
3848af5d141SJared Rossi
38580631527SJared Rossi if (bms->entry[i].type != BOOT_SCRIPT_EXEC) {
38680631527SJared Rossi puts("Unknown script entry type");
38780631527SJared Rossi return -EINVAL;
38880631527SJared Rossi }
389*0181e237SJared Rossi write_reset_psw(bms->entry[i].address.load_address);
390*0181e237SJared Rossi jump_to_IPL_code(0);
39180631527SJared Rossi return -1;
392e0aff4aaSEugene (jno) Dvurechenski }
393e0aff4aaSEugene (jno) Dvurechenski
ipl_eckd_cdl(void)39480631527SJared Rossi static int ipl_eckd_cdl(void)
395e0aff4aaSEugene (jno) Dvurechenski {
396e0aff4aaSEugene (jno) Dvurechenski XEckdMbr *mbr;
397ac4c5958SCollin L. Walling EckdCdlIpl2 *ipl2 = (void *)sec;
398e0aff4aaSEugene (jno) Dvurechenski IplVolumeLabel *vlbl = (void *)sec;
399ba831b25SCollin L. Walling block_number_t bmt_block_nr, s1b_block_nr;
400e0aff4aaSEugene (jno) Dvurechenski
401e0aff4aaSEugene (jno) Dvurechenski /* we have just read the block #0 and recognized it as "IPL1" */
4029f427883SJared Rossi puts("CDL");
403e0aff4aaSEugene (jno) Dvurechenski
404e0aff4aaSEugene (jno) Dvurechenski memset(sec, FREE_SPACE_FILLER, sizeof(sec));
40580631527SJared Rossi if (virtio_read(1, ipl2)) {
40680631527SJared Rossi puts("Cannot read IPL2 record at block 1");
40780631527SJared Rossi return -EIO;
40880631527SJared Rossi }
409e0aff4aaSEugene (jno) Dvurechenski
410ac4c5958SCollin L. Walling mbr = &ipl2->mbr;
4115dc739f3SThomas Huth if (!magic_match(mbr, ZIPL_MAGIC)) {
4129f427883SJared Rossi puts("No zIPL section in IPL2 record.");
41380631527SJared Rossi return 0;
4145dc739f3SThomas Huth }
4155dc739f3SThomas Huth if (!block_size_ok(mbr->blockptr.xeckd.bptr.size)) {
4169f427883SJared Rossi puts("Bad block size in zIPL section of IPL2 record.");
41780631527SJared Rossi return 0;
4185dc739f3SThomas Huth }
419d08a6494SThomas Huth if (mbr->dev_type != DEV_TYPE_ECKD) {
4209f427883SJared Rossi puts("Non-ECKD device type in zIPL section of IPL2 record.");
42180631527SJared Rossi return 0;
4225dc739f3SThomas Huth }
423e0aff4aaSEugene (jno) Dvurechenski
4245340eb07SCollin L. Walling /* save pointer to Boot Map Table */
42580beedccSCollin L. Walling bmt_block_nr = eckd_block_num(&mbr->blockptr.xeckd.bptr.chs);
426e0aff4aaSEugene (jno) Dvurechenski
427ba831b25SCollin L. Walling /* save pointer to Stage1b Data */
428ba831b25SCollin L. Walling s1b_block_nr = eckd_block_num(&ipl2->stage1.seek[0].chs);
429ba831b25SCollin L. Walling
430e0aff4aaSEugene (jno) Dvurechenski memset(sec, FREE_SPACE_FILLER, sizeof(sec));
43180631527SJared Rossi if (virtio_read(2, vlbl)) {
43280631527SJared Rossi puts("Cannot read Volume Label at block 2");
43380631527SJared Rossi return -EIO;
43480631527SJared Rossi }
4355dc739f3SThomas Huth if (!magic_match(vlbl->key, VOL1_MAGIC)) {
4369f427883SJared Rossi puts("Invalid magic of volume label block.");
43780631527SJared Rossi return 0;
4385dc739f3SThomas Huth }
4395dc739f3SThomas Huth if (!magic_match(vlbl->f.key, VOL1_MAGIC)) {
4409f427883SJared Rossi puts("Invalid magic of volser block.");
44180631527SJared Rossi return 0;
4425dc739f3SThomas Huth }
443e0aff4aaSEugene (jno) Dvurechenski print_volser(vlbl->f.volser);
444e0aff4aaSEugene (jno) Dvurechenski
44580631527SJared Rossi return run_eckd_boot_script(bmt_block_nr, s1b_block_nr);
446e0aff4aaSEugene (jno) Dvurechenski }
447e0aff4aaSEugene (jno) Dvurechenski
print_eckd_ldl_msg(ECKD_IPL_mode_t mode)44814f56a2eSEugene (jno) Dvurechenski static void print_eckd_ldl_msg(ECKD_IPL_mode_t mode)
449564e52b9SEugene (jno) Dvurechenski {
450564e52b9SEugene (jno) Dvurechenski LDL_VTOC *vlbl = (void *)sec; /* already read, 3rd block */
451564e52b9SEugene (jno) Dvurechenski char msg[4] = { '?', '.', '\n', '\0' };
452564e52b9SEugene (jno) Dvurechenski
4539f427883SJared Rossi printf((mode == ECKD_CMS) ? "CMS" : "LDL");
4549f427883SJared Rossi printf(" version ");
455564e52b9SEugene (jno) Dvurechenski switch (vlbl->LDL_version) {
456564e52b9SEugene (jno) Dvurechenski case LDL1_VERSION:
457564e52b9SEugene (jno) Dvurechenski msg[0] = '1';
458564e52b9SEugene (jno) Dvurechenski break;
459564e52b9SEugene (jno) Dvurechenski case LDL2_VERSION:
460564e52b9SEugene (jno) Dvurechenski msg[0] = '2';
461564e52b9SEugene (jno) Dvurechenski break;
462564e52b9SEugene (jno) Dvurechenski default:
46378182aeaSJanosch Frank msg[0] = ebc2asc[vlbl->LDL_version];
464564e52b9SEugene (jno) Dvurechenski msg[1] = '?';
465564e52b9SEugene (jno) Dvurechenski break;
466564e52b9SEugene (jno) Dvurechenski }
4679f427883SJared Rossi printf("%s", msg);
468564e52b9SEugene (jno) Dvurechenski print_volser(vlbl->volser);
46914f56a2eSEugene (jno) Dvurechenski }
47014f56a2eSEugene (jno) Dvurechenski
ipl_eckd_ldl(ECKD_IPL_mode_t mode)47180631527SJared Rossi static int ipl_eckd_ldl(ECKD_IPL_mode_t mode)
47214f56a2eSEugene (jno) Dvurechenski {
473ba831b25SCollin L. Walling block_number_t bmt_block_nr, s1b_block_nr;
474ac4c5958SCollin L. Walling EckdLdlIpl1 *ipl1 = (void *)sec;
47514f56a2eSEugene (jno) Dvurechenski
47614f56a2eSEugene (jno) Dvurechenski if (mode != ECKD_LDL_UNLABELED) {
47714f56a2eSEugene (jno) Dvurechenski print_eckd_ldl_msg(mode);
47814f56a2eSEugene (jno) Dvurechenski }
479564e52b9SEugene (jno) Dvurechenski
480564e52b9SEugene (jno) Dvurechenski /* DO NOT read BootMap pointer (only one, xECKD) at block #2 */
481564e52b9SEugene (jno) Dvurechenski
482564e52b9SEugene (jno) Dvurechenski memset(sec, FREE_SPACE_FILLER, sizeof(sec));
48380631527SJared Rossi if (virtio_read(0, sec)) {
48480631527SJared Rossi puts("Cannot read block 0 to grab boot info.");
48580631527SJared Rossi return -EIO;
48680631527SJared Rossi }
48714f56a2eSEugene (jno) Dvurechenski if (mode == ECKD_LDL_UNLABELED) {
488ac4c5958SCollin L. Walling if (!magic_match(ipl1->bip.magic, ZIPL_MAGIC)) {
48980631527SJared Rossi return 0; /* not applicable layout */
49014f56a2eSEugene (jno) Dvurechenski }
4919f427883SJared Rossi puts("unlabeled LDL.");
49214f56a2eSEugene (jno) Dvurechenski }
493ac4c5958SCollin L. Walling verify_boot_info(&ipl1->bip);
494564e52b9SEugene (jno) Dvurechenski
4955340eb07SCollin L. Walling /* save pointer to Boot Map Table */
496ac4c5958SCollin L. Walling bmt_block_nr = eckd_block_num(&ipl1->bip.bp.ipl.bm_ptr.eckd.bptr.chs);
4975340eb07SCollin L. Walling
498ba831b25SCollin L. Walling /* save pointer to Stage1b Data */
499ba831b25SCollin L. Walling s1b_block_nr = eckd_block_num(&ipl1->stage1.seek[0].chs);
500ba831b25SCollin L. Walling
50180631527SJared Rossi return run_eckd_boot_script(bmt_block_nr, s1b_block_nr);
502564e52b9SEugene (jno) Dvurechenski }
503564e52b9SEugene (jno) Dvurechenski
eckd_find_bmt(ExtEckdBlockPtr * ptr)5048af5d141SJared Rossi static block_number_t eckd_find_bmt(ExtEckdBlockPtr *ptr)
5058af5d141SJared Rossi {
5068af5d141SJared Rossi block_number_t blockno;
5078af5d141SJared Rossi uint8_t tmp_sec[MAX_SECTOR_SIZE];
5088af5d141SJared Rossi BootRecord *br;
5098af5d141SJared Rossi
5108af5d141SJared Rossi blockno = gen_eckd_block_num(ptr, 0);
51180631527SJared Rossi if (virtio_read(blockno, tmp_sec)) {
51280631527SJared Rossi puts("Cannot read boot record");
51380631527SJared Rossi return ERROR_BLOCK_NR;
51480631527SJared Rossi }
5158af5d141SJared Rossi br = (BootRecord *)tmp_sec;
5168af5d141SJared Rossi if (!magic_match(br->magic, ZIPL_MAGIC)) {
5178af5d141SJared Rossi /* If the boot record is invalid, return and try CCW-IPL instead */
5188af5d141SJared Rossi return NULL_BLOCK_NR;
5198af5d141SJared Rossi }
5208af5d141SJared Rossi
5218af5d141SJared Rossi return gen_eckd_block_num(&br->pgt.xeckd, 1);
5228af5d141SJared Rossi }
5238af5d141SJared Rossi
print_eckd_msg(void)524b0885f75SEugene (jno) Dvurechenski static void print_eckd_msg(void)
525b0885f75SEugene (jno) Dvurechenski {
526b0885f75SEugene (jno) Dvurechenski char msg[] = "Using ECKD scheme (block size *****), ";
527b0885f75SEugene (jno) Dvurechenski char *p = &msg[34], *q = &msg[30];
528b0885f75SEugene (jno) Dvurechenski int n = virtio_get_block_size();
529b0885f75SEugene (jno) Dvurechenski
530b0885f75SEugene (jno) Dvurechenski /* Fill in the block size and show up the message */
531b0885f75SEugene (jno) Dvurechenski if (n > 0 && n <= 99999) {
532b0885f75SEugene (jno) Dvurechenski while (n) {
533b0885f75SEugene (jno) Dvurechenski *p-- = '0' + (n % 10);
534b0885f75SEugene (jno) Dvurechenski n /= 10;
535b0885f75SEugene (jno) Dvurechenski }
536b0885f75SEugene (jno) Dvurechenski while (p >= q) {
537b0885f75SEugene (jno) Dvurechenski *p-- = ' ';
538b0885f75SEugene (jno) Dvurechenski }
539b0885f75SEugene (jno) Dvurechenski }
5409f427883SJared Rossi printf("%s", msg);
541b0885f75SEugene (jno) Dvurechenski }
542b0885f75SEugene (jno) Dvurechenski
ipl_eckd(void)54380631527SJared Rossi static int ipl_eckd(void)
544f0386820SEugene (jno) Dvurechenski {
5458af5d141SJared Rossi IplVolumeLabel *vlbl = (void *)sec;
5468af5d141SJared Rossi LDL_VTOC *vtoc = (void *)sec;
5478af5d141SJared Rossi block_number_t ldipl_bmt; /* Boot Map Table for List-Directed IPL */
548f0386820SEugene (jno) Dvurechenski
549f0386820SEugene (jno) Dvurechenski print_eckd_msg();
550f0386820SEugene (jno) Dvurechenski
5518af5d141SJared Rossi /* Block 2 can contain either the CDL VOL1 label or the LDL VTOC */
552f0386820SEugene (jno) Dvurechenski memset(sec, FREE_SPACE_FILLER, sizeof(sec));
55380631527SJared Rossi if (virtio_read(2, vlbl)) {
55480631527SJared Rossi puts("Cannot read block 2");
55580631527SJared Rossi return -EIO;
55680631527SJared Rossi }
557f0386820SEugene (jno) Dvurechenski
5588af5d141SJared Rossi /*
5598af5d141SJared Rossi * First check for a list-directed-format pointer which would
5608af5d141SJared Rossi * supersede the CCW pointer.
5618af5d141SJared Rossi */
5628af5d141SJared Rossi if (eckd_valid_address((ExtEckdBlockPtr *)&vlbl->f.br, 0)) {
5638af5d141SJared Rossi ldipl_bmt = eckd_find_bmt((ExtEckdBlockPtr *)&vlbl->f.br);
56480631527SJared Rossi switch (ldipl_bmt) {
56580631527SJared Rossi case ERROR_BLOCK_NR:
56680631527SJared Rossi return -EIO;
56780631527SJared Rossi case NULL_BLOCK_NR:
56880631527SJared Rossi break; /* Invalid BMT but the device may still boot with CCW-IPL */
56980631527SJared Rossi default:
5709f427883SJared Rossi puts("List-Directed");
57180631527SJared Rossi /*
57280631527SJared Rossi * LD-IPL does not use the S1B bock, just make it NULL_BLOCK_NR.
57380631527SJared Rossi * In some failure cases retry IPL before aborting.
57480631527SJared Rossi */
57580631527SJared Rossi if (run_eckd_boot_script(ldipl_bmt, NULL_BLOCK_NR)) {
57680631527SJared Rossi return -EIO;
57780631527SJared Rossi }
57880631527SJared Rossi /* Non-fatal error, retry as CCW-IPL */
5799f427883SJared Rossi printf("Retrying IPL ");
5808af5d141SJared Rossi print_eckd_msg();
5818af5d141SJared Rossi }
5828af5d141SJared Rossi memset(sec, FREE_SPACE_FILLER, sizeof(sec));
58380631527SJared Rossi if (virtio_read(2, vtoc)) {
58480631527SJared Rossi puts("Cannot read block 2");
58580631527SJared Rossi return -EIO;
58680631527SJared Rossi }
5878af5d141SJared Rossi }
5888af5d141SJared Rossi
5898af5d141SJared Rossi /* Not list-directed */
5908af5d141SJared Rossi if (magic_match(vtoc->magic, VOL1_MAGIC)) {
59180631527SJared Rossi if (ipl_eckd_cdl()) {
59280631527SJared Rossi return -1;
59380631527SJared Rossi }
5948af5d141SJared Rossi }
5958af5d141SJared Rossi
5968af5d141SJared Rossi if (magic_match(vtoc->magic, CMS1_MAGIC)) {
59780631527SJared Rossi return ipl_eckd_ldl(ECKD_CMS);
598f0386820SEugene (jno) Dvurechenski }
5998af5d141SJared Rossi if (magic_match(vtoc->magic, LNX1_MAGIC)) {
60080631527SJared Rossi return ipl_eckd_ldl(ECKD_LDL);
601f0386820SEugene (jno) Dvurechenski }
602f0386820SEugene (jno) Dvurechenski
60380631527SJared Rossi if (ipl_eckd_ldl(ECKD_LDL_UNLABELED)) {
60480631527SJared Rossi return -1;
60580631527SJared Rossi }
606f0386820SEugene (jno) Dvurechenski /*
607f0386820SEugene (jno) Dvurechenski * Ok, it is not a LDL by any means.
608f0386820SEugene (jno) Dvurechenski * It still might be a CDL with zero record keys for IPL1 and IPL2
609f0386820SEugene (jno) Dvurechenski */
61080631527SJared Rossi return ipl_eckd_cdl();
611f0386820SEugene (jno) Dvurechenski }
612f0386820SEugene (jno) Dvurechenski
613e0aff4aaSEugene (jno) Dvurechenski /***********************************************************************
614a00b33d9SEugene (jno) Dvurechenski * IPL a SCSI disk
615a00b33d9SEugene (jno) Dvurechenski */
616685d49a6SAlexander Graf
zipl_load_segment(ComponentEntry * entry)617facd91acSJared Rossi static int zipl_load_segment(ComponentEntry *entry)
618685d49a6SAlexander Graf {
61991a03f9bSEugene (jno) Dvurechenski const int max_entries = (MAX_SECTOR_SIZE / sizeof(ScsiBlockPtr));
62026f2bbd6SEugene (jno) Dvurechenski ScsiBlockPtr *bprs = (void *)sec;
621c77cd87cSEugene (jno) Dvurechenski const int bprs_size = sizeof(sec);
622a94b485eSEugene (jno) Dvurechenski block_number_t blockno;
623a00b33d9SEugene (jno) Dvurechenski uint64_t address;
624685d49a6SAlexander Graf int i;
625a00b33d9SEugene (jno) Dvurechenski char err_msg[] = "zIPL failed to read BPRS at 0xZZZZZZZZZZZZZZZZ";
626a00b33d9SEugene (jno) Dvurechenski char *blk_no = &err_msg[30]; /* where to print blockno in (those ZZs) */
627685d49a6SAlexander Graf
628685d49a6SAlexander Graf blockno = entry->data.blockno;
6294906a4e4SJanosch Frank address = entry->compdat.load_addr;
630685d49a6SAlexander Graf
631685d49a6SAlexander Graf debug_print_int("loading segment at block", blockno);
632685d49a6SAlexander Graf debug_print_int("addr", address);
633685d49a6SAlexander Graf
634685d49a6SAlexander Graf do {
635c77cd87cSEugene (jno) Dvurechenski memset(bprs, FREE_SPACE_FILLER, bprs_size);
636a00b33d9SEugene (jno) Dvurechenski fill_hex_val(blk_no, &blockno, sizeof(blockno));
637facd91acSJared Rossi if (virtio_read(blockno, bprs)) {
638facd91acSJared Rossi puts(err_msg);
639facd91acSJared Rossi return -EIO;
640facd91acSJared Rossi }
641685d49a6SAlexander Graf
642685d49a6SAlexander Graf for (i = 0;; i++) {
643a00b33d9SEugene (jno) Dvurechenski uint64_t *cur_desc = (void *)&bprs[i];
644685d49a6SAlexander Graf
645685d49a6SAlexander Graf blockno = bprs[i].blockno;
646abd696e4SEugene (jno) Dvurechenski if (!blockno) {
647685d49a6SAlexander Graf break;
648abd696e4SEugene (jno) Dvurechenski }
649685d49a6SAlexander Graf
650685d49a6SAlexander Graf /* we need the updated blockno for the next indirect entry in the
651685d49a6SAlexander Graf chain, but don't want to advance address */
652abd696e4SEugene (jno) Dvurechenski if (i == (max_entries - 1)) {
653685d49a6SAlexander Graf break;
654abd696e4SEugene (jno) Dvurechenski }
655685d49a6SAlexander Graf
656c77cd87cSEugene (jno) Dvurechenski if (bprs[i].blockct == 0 && unused_space(&bprs[i + 1],
65726f2bbd6SEugene (jno) Dvurechenski sizeof(ScsiBlockPtr))) {
658c77cd87cSEugene (jno) Dvurechenski /* This is a "continue" pointer.
659c77cd87cSEugene (jno) Dvurechenski * This ptr is the last one in the current script section.
660c77cd87cSEugene (jno) Dvurechenski * I.e. the next ptr must point to the unused memory area.
661c77cd87cSEugene (jno) Dvurechenski * The blockno is not zero, so the upper loop must continue
662c77cd87cSEugene (jno) Dvurechenski * reading next section of BPRS.
663c77cd87cSEugene (jno) Dvurechenski */
664c77cd87cSEugene (jno) Dvurechenski break;
665c77cd87cSEugene (jno) Dvurechenski }
666685d49a6SAlexander Graf address = virtio_load_direct(cur_desc[0], cur_desc[1], 0,
667685d49a6SAlexander Graf (void *)address);
668facd91acSJared Rossi if (!address) {
669facd91acSJared Rossi puts("zIPL load segment failed");
670facd91acSJared Rossi return -EIO;
671facd91acSJared Rossi }
672abd696e4SEugene (jno) Dvurechenski }
673685d49a6SAlexander Graf } while (blockno);
674facd91acSJared Rossi
675facd91acSJared Rossi return 0;
676685d49a6SAlexander Graf }
677685d49a6SAlexander Graf
678685d49a6SAlexander Graf /* Run a zipl program */
zipl_run(ScsiBlockPtr * pte)679facd91acSJared Rossi static int zipl_run(ScsiBlockPtr *pte)
680685d49a6SAlexander Graf {
68126f2bbd6SEugene (jno) Dvurechenski ComponentHeader *header;
68226f2bbd6SEugene (jno) Dvurechenski ComponentEntry *entry;
68391a03f9bSEugene (jno) Dvurechenski uint8_t tmp_sec[MAX_SECTOR_SIZE];
684685d49a6SAlexander Graf
685facd91acSJared Rossi if (virtio_read(pte->blockno, tmp_sec)) {
686facd91acSJared Rossi puts("Cannot read header");
687facd91acSJared Rossi return -EIO;
688facd91acSJared Rossi }
68926f2bbd6SEugene (jno) Dvurechenski header = (ComponentHeader *)tmp_sec;
690685d49a6SAlexander Graf
691facd91acSJared Rossi if (!magic_match(tmp_sec, ZIPL_MAGIC)) {
692facd91acSJared Rossi puts("No zIPL magic in header");
693facd91acSJared Rossi return -EINVAL;
694facd91acSJared Rossi }
695facd91acSJared Rossi if (header->type != ZIPL_COMP_HEADER_IPL) {
696facd91acSJared Rossi puts("Bad header type");
697facd91acSJared Rossi return -EINVAL;
698facd91acSJared Rossi }
699685d49a6SAlexander Graf
700685d49a6SAlexander Graf dputs("start loading images\n");
701685d49a6SAlexander Graf
702685d49a6SAlexander Graf /* Load image(s) into RAM */
70326f2bbd6SEugene (jno) Dvurechenski entry = (ComponentEntry *)(&header[1]);
7042497b4a3SJason J. Herne while (entry->component_type == ZIPL_COMP_ENTRY_LOAD ||
7052497b4a3SJason J. Herne entry->component_type == ZIPL_COMP_ENTRY_SIGNATURE) {
7062497b4a3SJason J. Herne
7072497b4a3SJason J. Herne /* We don't support secure boot yet, so we skip signature entries */
7082497b4a3SJason J. Herne if (entry->component_type == ZIPL_COMP_ENTRY_SIGNATURE) {
7092497b4a3SJason J. Herne entry++;
7102497b4a3SJason J. Herne continue;
7112497b4a3SJason J. Herne }
7122497b4a3SJason J. Herne
713facd91acSJared Rossi if (zipl_load_segment(entry)) {
714facd91acSJared Rossi return -1;
715facd91acSJared Rossi }
716685d49a6SAlexander Graf
717685d49a6SAlexander Graf entry++;
718685d49a6SAlexander Graf
719facd91acSJared Rossi if ((uint8_t *)(&entry[1]) > (tmp_sec + MAX_SECTOR_SIZE)) {
720facd91acSJared Rossi puts("Wrong entry value");
721facd91acSJared Rossi return -EINVAL;
722facd91acSJared Rossi }
723685d49a6SAlexander Graf }
724685d49a6SAlexander Graf
725facd91acSJared Rossi if (entry->component_type != ZIPL_COMP_ENTRY_EXEC) {
726facd91acSJared Rossi puts("No EXEC entry");
727facd91acSJared Rossi return -EINVAL;
728facd91acSJared Rossi }
729685d49a6SAlexander Graf
730685d49a6SAlexander Graf /* should not return */
73142ab98e7SJanosch Frank write_reset_psw(entry->compdat.load_psw);
73242ab98e7SJanosch Frank jump_to_IPL_code(0);
733facd91acSJared Rossi return -1;
734685d49a6SAlexander Graf }
735685d49a6SAlexander Graf
ipl_scsi(void)736facd91acSJared Rossi static int ipl_scsi(void)
737685d49a6SAlexander Graf {
73826f2bbd6SEugene (jno) Dvurechenski ScsiMbr *mbr = (void *)sec;
739685d49a6SAlexander Graf int program_table_entries = 0;
7405340eb07SCollin L. Walling BootMapTable *prog_table = (void *)sec;
7419dd7823bSFarhan Ali unsigned int loadparm = get_loadparm_index();
742622b3917SCollin Walling bool valid_entries[MAX_BOOT_ENTRIES] = {false};
743622b3917SCollin Walling size_t i;
744685d49a6SAlexander Graf
745f0386820SEugene (jno) Dvurechenski /* Grab the MBR */
746f0386820SEugene (jno) Dvurechenski memset(sec, FREE_SPACE_FILLER, sizeof(sec));
747facd91acSJared Rossi if (virtio_read(0, mbr)) {
748facd91acSJared Rossi puts("Cannot read block 0");
749facd91acSJared Rossi return -EIO;
750facd91acSJared Rossi }
751f0386820SEugene (jno) Dvurechenski
752f0386820SEugene (jno) Dvurechenski if (!magic_match(mbr->magic, ZIPL_MAGIC)) {
753facd91acSJared Rossi return 0;
754f0386820SEugene (jno) Dvurechenski }
755685d49a6SAlexander Graf
7569f427883SJared Rossi puts("Using SCSI scheme.");
757b1be0972SEugene (jno) Dvurechenski debug_print_int("MBR Version", mbr->version_id);
758b1be0972SEugene (jno) Dvurechenski IPL_check(mbr->version_id == 1,
759b1be0972SEugene (jno) Dvurechenski "Unknown MBR layout version, assuming version 1");
7605340eb07SCollin L. Walling debug_print_int("program table", mbr->pt.blockno);
761facd91acSJared Rossi if (!mbr->pt.blockno) {
762facd91acSJared Rossi puts("No Program Table");
763facd91acSJared Rossi return -EINVAL;
764facd91acSJared Rossi }
765685d49a6SAlexander Graf
766685d49a6SAlexander Graf /* Parse the program table */
767facd91acSJared Rossi if (virtio_read(mbr->pt.blockno, sec)) {
768facd91acSJared Rossi puts("Error reading Program Table");
769facd91acSJared Rossi return -EIO;
770facd91acSJared Rossi }
771facd91acSJared Rossi if (!magic_match(sec, ZIPL_MAGIC)) {
772facd91acSJared Rossi puts("No zIPL magic in Program Table");
773facd91acSJared Rossi return -EINVAL;
774facd91acSJared Rossi }
775685d49a6SAlexander Graf
776622b3917SCollin Walling for (i = 0; i < MAX_BOOT_ENTRIES; i++) {
777622b3917SCollin Walling if (prog_table->entry[i].scsi.blockno) {
778622b3917SCollin Walling valid_entries[i] = true;
779685d49a6SAlexander Graf program_table_entries++;
780685d49a6SAlexander Graf }
781622b3917SCollin Walling }
782685d49a6SAlexander Graf
783685d49a6SAlexander Graf debug_print_int("program table entries", program_table_entries);
784facd91acSJared Rossi if (program_table_entries == 0) {
785facd91acSJared Rossi puts("Empty Program Table");
786facd91acSJared Rossi return -EINVAL;
787facd91acSJared Rossi }
788685d49a6SAlexander Graf
789ffb4a1c8SCollin L. Walling if (menu_is_enabled_enum()) {
790622b3917SCollin Walling loadparm = menu_get_enum_boot_index(valid_entries);
791ffb4a1c8SCollin L. Walling }
792ffb4a1c8SCollin L. Walling
7935340eb07SCollin L. Walling debug_print_int("loadparm", loadparm);
794facd91acSJared Rossi if (loadparm >= MAX_BOOT_ENTRIES) {
795facd91acSJared Rossi puts("loadparm value greater than max number of boot entries allowed");
796facd91acSJared Rossi return -EINVAL;
797facd91acSJared Rossi }
7985340eb07SCollin L. Walling
799facd91acSJared Rossi return zipl_run(&prog_table->entry[loadparm].scsi);
800685d49a6SAlexander Graf }
801a00b33d9SEugene (jno) Dvurechenski
802a00b33d9SEugene (jno) Dvurechenski /***********************************************************************
803866cac91SMaxim Samoylov * IPL El Torito ISO9660 image or DVD
804866cac91SMaxim Samoylov */
805866cac91SMaxim Samoylov
is_iso_bc_entry_compatible(IsoBcSection * s)806866cac91SMaxim Samoylov static bool is_iso_bc_entry_compatible(IsoBcSection *s)
807866cac91SMaxim Samoylov {
808ba21f0ccSMaxim Samoylov uint8_t *magic_sec = (uint8_t *)(sec + ISO_SECTOR_SIZE);
809ba21f0ccSMaxim Samoylov
810ba21f0ccSMaxim Samoylov if (s->unused || !s->sector_count) {
811ba21f0ccSMaxim Samoylov return false;
812ba21f0ccSMaxim Samoylov }
813bef2b8ddSJared Rossi if (virtio_read(bswap32(s->load_rba), magic_sec)) {
814bef2b8ddSJared Rossi puts("Failed to read image sector 0");
815bef2b8ddSJared Rossi return false;
816bef2b8ddSJared Rossi }
817ba21f0ccSMaxim Samoylov
818ba21f0ccSMaxim Samoylov /* Checking bytes 8 - 32 for S390 Linux magic */
819fc0e2087SCollin L. Walling return !memcmp(magic_sec + 8, linux_s390_magic, 24);
820866cac91SMaxim Samoylov }
821866cac91SMaxim Samoylov
822869648e8SMaxim Samoylov /* Location of the current sector of the directory */
823869648e8SMaxim Samoylov static uint32_t sec_loc[ISO9660_MAX_DIR_DEPTH];
824869648e8SMaxim Samoylov /* Offset in the current sector of the directory */
825869648e8SMaxim Samoylov static uint32_t sec_offset[ISO9660_MAX_DIR_DEPTH];
826869648e8SMaxim Samoylov /* Remained directory space in bytes */
827869648e8SMaxim Samoylov static uint32_t dir_rem[ISO9660_MAX_DIR_DEPTH];
828869648e8SMaxim Samoylov
iso_get_file_size(uint32_t load_rba)829bef2b8ddSJared Rossi static inline long iso_get_file_size(uint32_t load_rba)
830869648e8SMaxim Samoylov {
831869648e8SMaxim Samoylov IsoVolDesc *vd = (IsoVolDesc *)sec;
832869648e8SMaxim Samoylov IsoDirHdr *cur_record = &vd->vd.primary.rootdir;
833869648e8SMaxim Samoylov uint8_t *temp = sec + ISO_SECTOR_SIZE;
834869648e8SMaxim Samoylov int level = 0;
835869648e8SMaxim Samoylov
836bef2b8ddSJared Rossi if (virtio_read(ISO_PRIMARY_VD_SECTOR, sec)) {
837bef2b8ddSJared Rossi puts("Failed to read ISO primary descriptor");
838bef2b8ddSJared Rossi return -EIO;
839bef2b8ddSJared Rossi }
840bef2b8ddSJared Rossi
841869648e8SMaxim Samoylov sec_loc[0] = iso_733_to_u32(cur_record->ext_loc);
842869648e8SMaxim Samoylov dir_rem[0] = 0;
843869648e8SMaxim Samoylov sec_offset[0] = 0;
844869648e8SMaxim Samoylov
845869648e8SMaxim Samoylov while (level >= 0) {
846bef2b8ddSJared Rossi if (sec_offset[level] > ISO_SECTOR_SIZE) {
847bef2b8ddSJared Rossi puts("Directory tree structure violation");
848bef2b8ddSJared Rossi return -EIO;
849bef2b8ddSJared Rossi }
850869648e8SMaxim Samoylov
851869648e8SMaxim Samoylov cur_record = (IsoDirHdr *)(temp + sec_offset[level]);
852869648e8SMaxim Samoylov
853869648e8SMaxim Samoylov if (sec_offset[level] == 0) {
854bef2b8ddSJared Rossi if (virtio_read(sec_loc[level], temp)) {
855bef2b8ddSJared Rossi puts("Failed to read ISO directory");
856bef2b8ddSJared Rossi return -EIO;
857bef2b8ddSJared Rossi }
858869648e8SMaxim Samoylov if (dir_rem[level] == 0) {
859869648e8SMaxim Samoylov /* Skip self and parent records */
860869648e8SMaxim Samoylov dir_rem[level] = iso_733_to_u32(cur_record->data_len) -
861869648e8SMaxim Samoylov cur_record->dr_len;
862869648e8SMaxim Samoylov sec_offset[level] += cur_record->dr_len;
863869648e8SMaxim Samoylov
864869648e8SMaxim Samoylov cur_record = (IsoDirHdr *)(temp + sec_offset[level]);
865869648e8SMaxim Samoylov dir_rem[level] -= cur_record->dr_len;
866869648e8SMaxim Samoylov sec_offset[level] += cur_record->dr_len;
867869648e8SMaxim Samoylov continue;
868869648e8SMaxim Samoylov }
869869648e8SMaxim Samoylov }
870869648e8SMaxim Samoylov
871869648e8SMaxim Samoylov if (!cur_record->dr_len || sec_offset[level] == ISO_SECTOR_SIZE) {
872869648e8SMaxim Samoylov /* Zero-padding and/or the end of current sector */
873869648e8SMaxim Samoylov dir_rem[level] -= ISO_SECTOR_SIZE - sec_offset[level];
874869648e8SMaxim Samoylov sec_offset[level] = 0;
875869648e8SMaxim Samoylov sec_loc[level]++;
876869648e8SMaxim Samoylov } else {
877869648e8SMaxim Samoylov /* The directory record is valid */
878869648e8SMaxim Samoylov if (load_rba == iso_733_to_u32(cur_record->ext_loc)) {
879869648e8SMaxim Samoylov return iso_733_to_u32(cur_record->data_len);
880869648e8SMaxim Samoylov }
881869648e8SMaxim Samoylov
882869648e8SMaxim Samoylov dir_rem[level] -= cur_record->dr_len;
883869648e8SMaxim Samoylov sec_offset[level] += cur_record->dr_len;
884869648e8SMaxim Samoylov
885869648e8SMaxim Samoylov if (cur_record->file_flags & 0x2) {
886869648e8SMaxim Samoylov /* Subdirectory */
887869648e8SMaxim Samoylov if (level == ISO9660_MAX_DIR_DEPTH - 1) {
8889f427883SJared Rossi puts("ISO-9660 directory depth limit exceeded");
889869648e8SMaxim Samoylov } else {
890869648e8SMaxim Samoylov level++;
891869648e8SMaxim Samoylov sec_loc[level] = iso_733_to_u32(cur_record->ext_loc);
892869648e8SMaxim Samoylov sec_offset[level] = 0;
893869648e8SMaxim Samoylov dir_rem[level] = 0;
894869648e8SMaxim Samoylov continue;
895869648e8SMaxim Samoylov }
896869648e8SMaxim Samoylov }
897869648e8SMaxim Samoylov }
898869648e8SMaxim Samoylov
899869648e8SMaxim Samoylov if (dir_rem[level] == 0) {
900869648e8SMaxim Samoylov /* Nothing remaining */
901869648e8SMaxim Samoylov level--;
902bef2b8ddSJared Rossi if (virtio_read(sec_loc[level], temp)) {
903bef2b8ddSJared Rossi puts("Failed to read ISO directory");
904bef2b8ddSJared Rossi return -EIO;
905bef2b8ddSJared Rossi }
906869648e8SMaxim Samoylov }
907869648e8SMaxim Samoylov }
908869648e8SMaxim Samoylov
909869648e8SMaxim Samoylov return 0;
910869648e8SMaxim Samoylov }
911869648e8SMaxim Samoylov
load_iso_bc_entry(IsoBcSection * load)912866cac91SMaxim Samoylov static void load_iso_bc_entry(IsoBcSection *load)
913866cac91SMaxim Samoylov {
914866cac91SMaxim Samoylov IsoBcSection s = *load;
915866cac91SMaxim Samoylov /*
916866cac91SMaxim Samoylov * According to spec, extent for each file
917866cac91SMaxim Samoylov * is padded and ISO_SECTOR_SIZE bytes aligned
918866cac91SMaxim Samoylov */
919866cac91SMaxim Samoylov uint32_t blks_to_load = bswap16(s.sector_count) >> ET_SECTOR_SHIFT;
920bef2b8ddSJared Rossi long real_size = iso_get_file_size(bswap32(s.load_rba));
921869648e8SMaxim Samoylov
922bef2b8ddSJared Rossi if (real_size > 0) {
923869648e8SMaxim Samoylov /* Round up blocks to load */
924869648e8SMaxim Samoylov blks_to_load = (real_size + ISO_SECTOR_SIZE - 1) / ISO_SECTOR_SIZE;
9259f427883SJared Rossi puts("ISO boot image size verified");
926869648e8SMaxim Samoylov } else {
9279f427883SJared Rossi puts("ISO boot image size could not be verified");
928bef2b8ddSJared Rossi if (real_size < 0) {
929bef2b8ddSJared Rossi return;
930bef2b8ddSJared Rossi }
931869648e8SMaxim Samoylov }
932866cac91SMaxim Samoylov
933bef2b8ddSJared Rossi if (read_iso_boot_image(bswap32(s.load_rba),
934866cac91SMaxim Samoylov (void *)((uint64_t)bswap16(s.load_segment)),
935bef2b8ddSJared Rossi blks_to_load)) {
936bef2b8ddSJared Rossi return;
937bef2b8ddSJared Rossi }
938866cac91SMaxim Samoylov
9399a848adfSThomas Huth jump_to_low_kernel();
940866cac91SMaxim Samoylov }
941866cac91SMaxim Samoylov
find_iso_bc(void)942866cac91SMaxim Samoylov static uint32_t find_iso_bc(void)
943866cac91SMaxim Samoylov {
944866cac91SMaxim Samoylov IsoVolDesc *vd = (IsoVolDesc *)sec;
945866cac91SMaxim Samoylov uint32_t block_num = ISO_PRIMARY_VD_SECTOR;
946866cac91SMaxim Samoylov
947866cac91SMaxim Samoylov if (virtio_read_many(block_num++, sec, 1)) {
948866cac91SMaxim Samoylov /* If primary vd cannot be read, there is no boot catalog */
949866cac91SMaxim Samoylov return 0;
950866cac91SMaxim Samoylov }
951866cac91SMaxim Samoylov
952866cac91SMaxim Samoylov while (is_iso_vd_valid(vd) && vd->type != VOL_DESC_TERMINATOR) {
953866cac91SMaxim Samoylov if (vd->type == VOL_DESC_TYPE_BOOT) {
954866cac91SMaxim Samoylov IsoVdElTorito *et = &vd->vd.boot;
955866cac91SMaxim Samoylov
956fc0e2087SCollin L. Walling if (!memcmp(&et->el_torito[0], el_torito_magic, 32)) {
957866cac91SMaxim Samoylov return bswap32(et->bc_offset);
958866cac91SMaxim Samoylov }
959866cac91SMaxim Samoylov }
960bef2b8ddSJared Rossi if (virtio_read(block_num++, sec)) {
961bef2b8ddSJared Rossi puts("Failed to read ISO volume descriptor");
962bef2b8ddSJared Rossi return 0;
963bef2b8ddSJared Rossi }
964866cac91SMaxim Samoylov }
965866cac91SMaxim Samoylov
966866cac91SMaxim Samoylov return 0;
967866cac91SMaxim Samoylov }
968866cac91SMaxim Samoylov
find_iso_bc_entry(uint32_t offset)969bef2b8ddSJared Rossi static IsoBcSection *find_iso_bc_entry(uint32_t offset)
970866cac91SMaxim Samoylov {
971866cac91SMaxim Samoylov IsoBcEntry *e = (IsoBcEntry *)sec;
972866cac91SMaxim Samoylov int i;
9737a9762bfSEugene (jno) Dvurechenski unsigned int loadparm = get_loadparm_index();
974866cac91SMaxim Samoylov
975866cac91SMaxim Samoylov if (!offset) {
976866cac91SMaxim Samoylov return NULL;
977866cac91SMaxim Samoylov }
978866cac91SMaxim Samoylov
979bef2b8ddSJared Rossi if (virtio_read(offset, sec)) {
980bef2b8ddSJared Rossi puts("Failed to read El Torito boot catalog");
981bef2b8ddSJared Rossi return NULL;
982bef2b8ddSJared Rossi }
983866cac91SMaxim Samoylov
984866cac91SMaxim Samoylov if (!is_iso_bc_valid(e)) {
985866cac91SMaxim Samoylov /* The validation entry is mandatory */
986866cac91SMaxim Samoylov return NULL;
987866cac91SMaxim Samoylov }
988866cac91SMaxim Samoylov
989866cac91SMaxim Samoylov /*
990866cac91SMaxim Samoylov * Each entry has 32 bytes size, so one sector cannot contain > 64 entries.
991866cac91SMaxim Samoylov * We consider only boot catalogs with no more than 64 entries.
992866cac91SMaxim Samoylov */
993866cac91SMaxim Samoylov for (i = 1; i < ISO_BC_ENTRY_PER_SECTOR; i++) {
994866cac91SMaxim Samoylov if (e[i].id == ISO_BC_BOOTABLE_SECTION) {
995866cac91SMaxim Samoylov if (is_iso_bc_entry_compatible(&e[i].body.sect)) {
9967a9762bfSEugene (jno) Dvurechenski if (loadparm <= 1) {
9977a9762bfSEugene (jno) Dvurechenski /* found, default, or unspecified */
998866cac91SMaxim Samoylov return &e[i].body.sect;
999866cac91SMaxim Samoylov }
10007a9762bfSEugene (jno) Dvurechenski loadparm--;
10017a9762bfSEugene (jno) Dvurechenski }
1002866cac91SMaxim Samoylov }
1003866cac91SMaxim Samoylov }
1004866cac91SMaxim Samoylov
1005866cac91SMaxim Samoylov return NULL;
1006866cac91SMaxim Samoylov }
1007866cac91SMaxim Samoylov
ipl_iso_el_torito(void)1008bef2b8ddSJared Rossi static int ipl_iso_el_torito(void)
1009866cac91SMaxim Samoylov {
1010bef2b8ddSJared Rossi uint32_t offset = find_iso_bc();
1011bef2b8ddSJared Rossi if (!offset) {
1012bef2b8ddSJared Rossi return 0;
1013bef2b8ddSJared Rossi }
1014bef2b8ddSJared Rossi
1015bef2b8ddSJared Rossi IsoBcSection *s = find_iso_bc_entry(offset);
1016866cac91SMaxim Samoylov
1017866cac91SMaxim Samoylov if (s) {
1018bef2b8ddSJared Rossi load_iso_bc_entry(s); /* only return in error */
1019bef2b8ddSJared Rossi return -1;
1020866cac91SMaxim Samoylov }
1021bef2b8ddSJared Rossi
1022bef2b8ddSJared Rossi puts("No suitable boot entry found on ISO-9660 media!");
1023bef2b8ddSJared Rossi return -EIO;
1024866cac91SMaxim Samoylov }
1025866cac91SMaxim Samoylov
1026422865f6SThomas Huth /**
1027422865f6SThomas Huth * Detect whether we're trying to boot from an .ISO image.
1028422865f6SThomas Huth * These always have a signature string "CD001" at offset 0x8001.
1029422865f6SThomas Huth */
has_iso_signature(void)1030422865f6SThomas Huth static bool has_iso_signature(void)
1031422865f6SThomas Huth {
1032422865f6SThomas Huth int blksize = virtio_get_block_size();
1033422865f6SThomas Huth
1034422865f6SThomas Huth if (!blksize || virtio_read(0x8000 / blksize, sec)) {
1035422865f6SThomas Huth return false;
1036422865f6SThomas Huth }
1037422865f6SThomas Huth
1038422865f6SThomas Huth return !memcmp("CD001", &sec[1], 5);
1039422865f6SThomas Huth }
1040422865f6SThomas Huth
1041866cac91SMaxim Samoylov /***********************************************************************
1042f0386820SEugene (jno) Dvurechenski * Bus specific IPL sequences
1043a00b33d9SEugene (jno) Dvurechenski */
1044a00b33d9SEugene (jno) Dvurechenski
zipl_load_vblk(void)104580631527SJared Rossi static int zipl_load_vblk(void)
1046a00b33d9SEugene (jno) Dvurechenski {
1047422865f6SThomas Huth int blksize = virtio_get_block_size();
1048422865f6SThomas Huth
1049422865f6SThomas Huth if (blksize == VIRTIO_ISO_BLOCK_SIZE || has_iso_signature()) {
1050422865f6SThomas Huth if (blksize != VIRTIO_ISO_BLOCK_SIZE) {
1051866cac91SMaxim Samoylov virtio_assume_iso9660();
1052866cac91SMaxim Samoylov }
1053bef2b8ddSJared Rossi if (ipl_iso_el_torito()) {
105480631527SJared Rossi return 0;
1055bef2b8ddSJared Rossi }
1056422865f6SThomas Huth }
1057866cac91SMaxim Samoylov
1058422865f6SThomas Huth if (blksize != VIRTIO_DASD_DEFAULT_BLOCK_SIZE) {
10599f427883SJared Rossi puts("Using guessed DASD geometry.");
1060564e52b9SEugene (jno) Dvurechenski virtio_assume_eckd();
1061564e52b9SEugene (jno) Dvurechenski }
106280631527SJared Rossi return ipl_eckd();
1063564e52b9SEugene (jno) Dvurechenski }
1064564e52b9SEugene (jno) Dvurechenski
zipl_load_vscsi(void)106580631527SJared Rossi static int zipl_load_vscsi(void)
1066f0386820SEugene (jno) Dvurechenski {
1067f0386820SEugene (jno) Dvurechenski if (virtio_get_block_size() == VIRTIO_ISO_BLOCK_SIZE) {
1068f0386820SEugene (jno) Dvurechenski /* Is it an ISO image in non-CD drive? */
1069bef2b8ddSJared Rossi if (ipl_iso_el_torito()) {
107080631527SJared Rossi return 0;
1071bef2b8ddSJared Rossi }
1072e0aff4aaSEugene (jno) Dvurechenski }
1073a00b33d9SEugene (jno) Dvurechenski
10749f427883SJared Rossi puts("Using guessed DASD geometry.");
1075f0386820SEugene (jno) Dvurechenski virtio_assume_eckd();
107680631527SJared Rossi return ipl_eckd();
1077f0386820SEugene (jno) Dvurechenski }
1078f0386820SEugene (jno) Dvurechenski
1079f0386820SEugene (jno) Dvurechenski /***********************************************************************
1080f0386820SEugene (jno) Dvurechenski * IPL starts here
108114f56a2eSEugene (jno) Dvurechenski */
1082f0386820SEugene (jno) Dvurechenski
zipl_load(void)1083f0386820SEugene (jno) Dvurechenski void zipl_load(void)
1084f0386820SEugene (jno) Dvurechenski {
108599b72e0fSFarhan Ali VDev *vdev = virtio_get_device();
108699b72e0fSFarhan Ali
108799b72e0fSFarhan Ali if (vdev->is_cdrom) {
1088f0386820SEugene (jno) Dvurechenski ipl_iso_el_torito();
1089*0181e237SJared Rossi puts("Failed to IPL this ISO image!");
1090*0181e237SJared Rossi return;
1091f0386820SEugene (jno) Dvurechenski }
1092f0386820SEugene (jno) Dvurechenski
109399b72e0fSFarhan Ali if (virtio_get_device_type() == VIRTIO_ID_NET) {
10948e5739ceSJared Rossi netmain();
1095*0181e237SJared Rossi puts("Failed to IPL from this network!");
1096*0181e237SJared Rossi return;
109799b72e0fSFarhan Ali }
109899b72e0fSFarhan Ali
1099facd91acSJared Rossi if (ipl_scsi()) {
1100*0181e237SJared Rossi puts("Failed to IPL from this SCSI device!");
1101*0181e237SJared Rossi return;
1102facd91acSJared Rossi }
1103f0386820SEugene (jno) Dvurechenski
1104f0386820SEugene (jno) Dvurechenski switch (virtio_get_device_type()) {
1105f0386820SEugene (jno) Dvurechenski case VIRTIO_ID_BLOCK:
1106f0386820SEugene (jno) Dvurechenski zipl_load_vblk();
1107f0386820SEugene (jno) Dvurechenski break;
1108f0386820SEugene (jno) Dvurechenski case VIRTIO_ID_SCSI:
1109f0386820SEugene (jno) Dvurechenski zipl_load_vscsi();
1110f0386820SEugene (jno) Dvurechenski break;
1111f0386820SEugene (jno) Dvurechenski default:
1112*0181e237SJared Rossi puts("Unknown IPL device type!");
1113*0181e237SJared Rossi return;
1114f0386820SEugene (jno) Dvurechenski }
111514f56a2eSEugene (jno) Dvurechenski
1116*0181e237SJared Rossi puts("zIPL load failed!");
1117a00b33d9SEugene (jno) Dvurechenski }
1118