Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
S390: ccw firmware: Add bootmap interpreter
On s390, there is an architected boot map format that we can read to boot a certain entry off the disk. Implement a simple reader for this that always boots the first (default) entry. Signed-off-by: Alexander Graf <agraf@suse.de>
- Loading branch information
Showing
1 changed file
with
235 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,235 @@ | ||
/* | ||
* QEMU S390 bootmap interpreter | ||
* | ||
* Copyright (c) 2009 Alexander Graf <agraf@suse.de> | ||
* | ||
* This work is licensed under the terms of the GNU GPL, version 2 or (at | ||
* your option) any later version. See the COPYING file in the top-level | ||
* directory. | ||
*/ | ||
|
||
#include "s390-ccw.h" | ||
|
||
// #define DEBUG_FALLBACK | ||
|
||
#ifdef DEBUG_FALLBACK | ||
#define dputs(txt) \ | ||
do { sclp_print("zipl: " txt); } while (0) | ||
#else | ||
#define dputs(fmt, ...) \ | ||
do { } while (0) | ||
#endif | ||
|
||
struct scsi_blockptr { | ||
uint64_t blockno; | ||
uint16_t size; | ||
uint16_t blockct; | ||
uint8_t reserved[4]; | ||
} __attribute__ ((packed)); | ||
|
||
struct component_entry { | ||
struct scsi_blockptr data; | ||
uint8_t pad[7]; | ||
uint8_t component_type; | ||
uint64_t load_address; | ||
} __attribute((packed)); | ||
|
||
struct component_header { | ||
uint8_t magic[4]; | ||
uint8_t type; | ||
uint8_t reserved[27]; | ||
} __attribute((packed)); | ||
|
||
struct mbr { | ||
uint8_t magic[4]; | ||
uint32_t version_id; | ||
uint8_t reserved[8]; | ||
struct scsi_blockptr blockptr; | ||
} __attribute__ ((packed)); | ||
|
||
#define ZIPL_MAGIC "zIPL" | ||
|
||
#define ZIPL_COMP_HEADER_IPL 0x00 | ||
#define ZIPL_COMP_HEADER_DUMP 0x01 | ||
|
||
#define ZIPL_COMP_ENTRY_LOAD 0x02 | ||
#define ZIPL_COMP_ENTRY_EXEC 0x01 | ||
|
||
/* Scratch space */ | ||
static uint8_t sec[SECTOR_SIZE] __attribute__((__aligned__(SECTOR_SIZE))); | ||
|
||
/* Check for ZIPL magic. Returns 0 if not matched. */ | ||
static int zipl_magic(uint8_t *ptr) | ||
{ | ||
uint32_t *p = (void*)ptr; | ||
uint32_t *z = (void*)ZIPL_MAGIC; | ||
|
||
if (*p != *z) { | ||
debug_print_int("invalid magic", *p); | ||
virtio_panic("invalid magic"); | ||
} | ||
|
||
return 1; | ||
} | ||
|
||
static int zipl_load_segment(struct component_entry *entry) | ||
{ | ||
const int max_entries = (SECTOR_SIZE / sizeof(struct scsi_blockptr)); | ||
struct scsi_blockptr *bprs = (void*)sec; | ||
uint64_t blockno; | ||
long address; | ||
int i; | ||
|
||
blockno = entry->data.blockno; | ||
address = entry->load_address; | ||
|
||
debug_print_int("loading segment at block", blockno); | ||
debug_print_int("addr", address); | ||
|
||
do { | ||
if (virtio_read(blockno, (uint8_t *)bprs)) { | ||
debug_print_int("failed reading bprs at", blockno); | ||
goto fail; | ||
} | ||
|
||
for (i = 0;; i++) { | ||
u64 *cur_desc = (void*)&bprs[i]; | ||
|
||
blockno = bprs[i].blockno; | ||
if (!blockno) | ||
break; | ||
|
||
/* we need the updated blockno for the next indirect entry in the | ||
chain, but don't want to advance address */ | ||
if (i == (max_entries - 1)) | ||
break; | ||
|
||
address = virtio_load_direct(cur_desc[0], cur_desc[1], 0, | ||
(void*)address); | ||
if (address == -1) | ||
goto fail; | ||
} | ||
} while (blockno); | ||
|
||
return 0; | ||
|
||
fail: | ||
sclp_print("failed loading segment\n"); | ||
return -1; | ||
} | ||
|
||
/* Run a zipl program */ | ||
static int zipl_run(struct scsi_blockptr *pte) | ||
{ | ||
struct component_header *header; | ||
struct component_entry *entry; | ||
void (*ipl)(void); | ||
uint8_t tmp_sec[SECTOR_SIZE]; | ||
|
||
virtio_read(pte->blockno, tmp_sec); | ||
header = (struct component_header *)tmp_sec; | ||
|
||
if (!zipl_magic(tmp_sec)) { | ||
goto fail; | ||
} | ||
|
||
if (header->type != ZIPL_COMP_HEADER_IPL) { | ||
goto fail; | ||
} | ||
|
||
dputs("start loading images\n"); | ||
|
||
/* Load image(s) into RAM */ | ||
entry = (struct component_entry *)(&header[1]); | ||
while (entry->component_type == ZIPL_COMP_ENTRY_LOAD) { | ||
if (zipl_load_segment(entry) < 0) { | ||
goto fail; | ||
} | ||
|
||
entry++; | ||
|
||
if ((uint8_t*)(&entry[1]) > (tmp_sec + SECTOR_SIZE)) { | ||
goto fail; | ||
} | ||
} | ||
|
||
if (entry->component_type != ZIPL_COMP_ENTRY_EXEC) { | ||
goto fail; | ||
} | ||
|
||
/* Ensure the guest output starts fresh */ | ||
sclp_print("\n"); | ||
|
||
/* And run the OS! */ | ||
ipl = (void*)(entry->load_address & 0x7fffffff); | ||
debug_print_addr("set IPL addr to", ipl); | ||
/* should not return */ | ||
ipl(); | ||
|
||
return 0; | ||
|
||
fail: | ||
sclp_print("failed running zipl\n"); | ||
return -1; | ||
} | ||
|
||
int zipl_load(void) | ||
{ | ||
struct mbr *mbr = (void*)sec; | ||
uint8_t *ns, *ns_end; | ||
int program_table_entries = 0; | ||
int pte_len = sizeof(struct scsi_blockptr); | ||
struct scsi_blockptr *prog_table_entry; | ||
const char *error = ""; | ||
|
||
/* Grab the MBR */ | ||
virtio_read(0, (void*)mbr); | ||
|
||
dputs("checking magic\n"); | ||
|
||
if (!zipl_magic(mbr->magic)) { | ||
error = "zipl_magic 1"; | ||
goto fail; | ||
} | ||
|
||
debug_print_int("program table", mbr->blockptr.blockno); | ||
|
||
/* Parse the program table */ | ||
if (virtio_read(mbr->blockptr.blockno, sec)) { | ||
error = "virtio_read"; | ||
goto fail; | ||
} | ||
|
||
if (!zipl_magic(sec)) { | ||
error = "zipl_magic 2"; | ||
goto fail; | ||
} | ||
|
||
ns_end = sec + SECTOR_SIZE; | ||
for (ns = (sec + pte_len); (ns + pte_len) < ns_end; ns++) { | ||
prog_table_entry = (struct scsi_blockptr *)ns; | ||
if (!prog_table_entry->blockno) { | ||
break; | ||
} | ||
|
||
program_table_entries++; | ||
} | ||
|
||
debug_print_int("program table entries", program_table_entries); | ||
|
||
if (!program_table_entries) { | ||
goto fail; | ||
} | ||
|
||
/* Run the default entry */ | ||
|
||
prog_table_entry = (struct scsi_blockptr *)(sec + pte_len); | ||
|
||
return zipl_run(prog_table_entry); | ||
|
||
fail: | ||
sclp_print("failed loading zipl: "); | ||
sclp_print(error); | ||
sclp_print("\n"); | ||
return -1; | ||
} |