summaryrefslogtreecommitdiffstats
path: root/qemu/pc-bios/s390-ccw/bootmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/pc-bios/s390-ccw/bootmap.c')
-rw-r--r--qemu/pc-bios/s390-ccw/bootmap.c321
1 files changed, 286 insertions, 35 deletions
diff --git a/qemu/pc-bios/s390-ccw/bootmap.c b/qemu/pc-bios/s390-ccw/bootmap.c
index b678d5ebb..611102e3e 100644
--- a/qemu/pc-bios/s390-ccw/bootmap.c
+++ b/qemu/pc-bios/s390-ccw/bootmap.c
@@ -72,7 +72,7 @@ static void jump_to_IPL_code(uint64_t address)
asm volatile("lghi 1,1\n\t"
"diag 1,1,0x308\n\t"
: : : "1", "memory");
- virtio_panic("\n! IPL returns !\n");
+ panic("\n! IPL returns !\n");
}
/***********************************************************************
@@ -84,7 +84,7 @@ static const int max_bprs_entries = sizeof(_bprs) / sizeof(ExtEckdBlockPtr);
static inline void verify_boot_info(BootInfo *bip)
{
- IPL_assert(magic_match(bip->magic, ZIPL_MAGIC), "No zIPL magic");
+ IPL_assert(magic_match(bip->magic, ZIPL_MAGIC), "No zIPL sig in BootInfo");
IPL_assert(bip->version == BOOT_INFO_VERSION, "Wrong zIPL version");
IPL_assert(bip->bp_type == BOOT_INFO_BP_TYPE_IPL, "DASD is not for IPL");
IPL_assert(bip->dev_type == BOOT_INFO_DEV_TYPE_ECKD, "DASD is not ECKD");
@@ -315,6 +315,40 @@ static void print_eckd_msg(void)
sclp_print(msg);
}
+static void ipl_eckd(void)
+{
+ ScsiMbr *mbr = (void *)sec;
+ LDL_VTOC *vlbl = (void *)sec;
+
+ print_eckd_msg();
+
+ /* Grab the MBR again */
+ memset(sec, FREE_SPACE_FILLER, sizeof(sec));
+ read_block(0, mbr, "Cannot read block 0 on DASD");
+
+ if (magic_match(mbr->magic, IPL1_MAGIC)) {
+ ipl_eckd_cdl(); /* no return */
+ }
+
+ /* LDL/CMS? */
+ memset(sec, FREE_SPACE_FILLER, sizeof(sec));
+ read_block(2, vlbl, "Cannot read block 2");
+
+ if (magic_match(vlbl->magic, CMS1_MAGIC)) {
+ ipl_eckd_ldl(ECKD_CMS); /* no return */
+ }
+ if (magic_match(vlbl->magic, LNX1_MAGIC)) {
+ ipl_eckd_ldl(ECKD_LDL); /* no return */
+ }
+
+ ipl_eckd_ldl(ECKD_LDL_UNLABELED); /* it still may return */
+ /*
+ * Ok, it is not a LDL by any means.
+ * It still might be a CDL with zero record keys for IPL1 and IPL2
+ */
+ ipl_eckd_cdl();
+}
+
/***********************************************************************
* IPL a SCSI disk
*/
@@ -382,7 +416,7 @@ static void zipl_run(ScsiBlockPtr *pte)
read_block(pte->blockno, tmp_sec, "Cannot read header");
header = (ComponentHeader *)tmp_sec;
- IPL_assert(magic_match(tmp_sec, ZIPL_MAGIC), "No zIPL magic");
+ IPL_assert(magic_match(tmp_sec, ZIPL_MAGIC), "No zIPL magic in header");
IPL_assert(header->type == ZIPL_COMP_HEADER_IPL, "Bad header type");
dputs("start loading images\n");
@@ -412,19 +446,29 @@ static void ipl_scsi(void)
const int pte_len = sizeof(ScsiBlockPtr);
ScsiBlockPtr *prog_table_entry;
- /* The 0-th block (MBR) was already read into sec[] */
+ /* Grab the MBR */
+ memset(sec, FREE_SPACE_FILLER, sizeof(sec));
+ read_block(0, mbr, "Cannot read block 0");
+
+ if (!magic_match(mbr->magic, ZIPL_MAGIC)) {
+ return;
+ }
sclp_print("Using SCSI scheme.\n");
+ debug_print_int("MBR Version", mbr->version_id);
+ IPL_check(mbr->version_id == 1,
+ "Unknown MBR layout version, assuming version 1");
debug_print_int("program table", mbr->blockptr.blockno);
+ IPL_assert(mbr->blockptr.blockno, "No Program Table");
/* Parse the program table */
read_block(mbr->blockptr.blockno, sec,
"Error reading Program Table");
- IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic");
+ IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT");
ns_end = sec + virtio_get_block_size();
- for (ns = (sec + pte_len); (ns + pte_len) < ns_end; ns++) {
+ for (ns = (sec + pte_len); (ns + pte_len) < ns_end; ns += pte_len) {
prog_table_entry = (ScsiBlockPtr *)ns;
if (!prog_table_entry->blockno) {
break;
@@ -445,51 +489,258 @@ static void ipl_scsi(void)
}
/***********************************************************************
- * IPL starts here
+ * IPL El Torito ISO9660 image or DVD
*/
-void zipl_load(void)
+static bool is_iso_bc_entry_compatible(IsoBcSection *s)
{
- ScsiMbr *mbr = (void *)sec;
- LDL_VTOC *vlbl = (void *)sec;
+ uint8_t *magic_sec = (uint8_t *)(sec + ISO_SECTOR_SIZE);
- /* Grab the MBR */
- memset(sec, FREE_SPACE_FILLER, sizeof(sec));
- read_block(0, mbr, "Cannot read block 0");
+ if (s->unused || !s->sector_count) {
+ return false;
+ }
+ read_iso_sector(bswap32(s->load_rba), magic_sec,
+ "Failed to read image sector 0");
- dputs("checking magic\n");
+ /* Checking bytes 8 - 32 for S390 Linux magic */
+ return !_memcmp(magic_sec + 8, linux_s390_magic, 24);
+}
+
+/* Location of the current sector of the directory */
+static uint32_t sec_loc[ISO9660_MAX_DIR_DEPTH];
+/* Offset in the current sector of the directory */
+static uint32_t sec_offset[ISO9660_MAX_DIR_DEPTH];
+/* Remained directory space in bytes */
+static uint32_t dir_rem[ISO9660_MAX_DIR_DEPTH];
- if (magic_match(mbr->magic, ZIPL_MAGIC)) {
- ipl_scsi(); /* no return */
+static inline uint32_t iso_get_file_size(uint32_t load_rba)
+{
+ IsoVolDesc *vd = (IsoVolDesc *)sec;
+ IsoDirHdr *cur_record = &vd->vd.primary.rootdir;
+ uint8_t *temp = sec + ISO_SECTOR_SIZE;
+ int level = 0;
+
+ read_iso_sector(ISO_PRIMARY_VD_SECTOR, sec,
+ "Failed to read ISO primary descriptor");
+ sec_loc[0] = iso_733_to_u32(cur_record->ext_loc);
+ dir_rem[0] = 0;
+ sec_offset[0] = 0;
+
+ while (level >= 0) {
+ IPL_assert(sec_offset[level] <= ISO_SECTOR_SIZE,
+ "Directory tree structure violation");
+
+ cur_record = (IsoDirHdr *)(temp + sec_offset[level]);
+
+ if (sec_offset[level] == 0) {
+ read_iso_sector(sec_loc[level], temp,
+ "Failed to read ISO directory");
+ if (dir_rem[level] == 0) {
+ /* Skip self and parent records */
+ dir_rem[level] = iso_733_to_u32(cur_record->data_len) -
+ cur_record->dr_len;
+ sec_offset[level] += cur_record->dr_len;
+
+ cur_record = (IsoDirHdr *)(temp + sec_offset[level]);
+ dir_rem[level] -= cur_record->dr_len;
+ sec_offset[level] += cur_record->dr_len;
+ continue;
+ }
+ }
+
+ if (!cur_record->dr_len || sec_offset[level] == ISO_SECTOR_SIZE) {
+ /* Zero-padding and/or the end of current sector */
+ dir_rem[level] -= ISO_SECTOR_SIZE - sec_offset[level];
+ sec_offset[level] = 0;
+ sec_loc[level]++;
+ } else {
+ /* The directory record is valid */
+ if (load_rba == iso_733_to_u32(cur_record->ext_loc)) {
+ return iso_733_to_u32(cur_record->data_len);
+ }
+
+ dir_rem[level] -= cur_record->dr_len;
+ sec_offset[level] += cur_record->dr_len;
+
+ if (cur_record->file_flags & 0x2) {
+ /* Subdirectory */
+ if (level == ISO9660_MAX_DIR_DEPTH - 1) {
+ sclp_print("ISO-9660 directory depth limit exceeded\n");
+ } else {
+ level++;
+ sec_loc[level] = iso_733_to_u32(cur_record->ext_loc);
+ sec_offset[level] = 0;
+ dir_rem[level] = 0;
+ continue;
+ }
+ }
+ }
+
+ if (dir_rem[level] == 0) {
+ /* Nothing remaining */
+ level--;
+ read_iso_sector(sec_loc[level], temp,
+ "Failed to read ISO directory");
+ }
}
- /* We have failed to follow the SCSI scheme, so */
- if (virtio_guessed_disk_nature()) {
- sclp_print("Using guessed DASD geometry.\n");
- virtio_assume_eckd();
+ return 0;
+}
+
+static void load_iso_bc_entry(IsoBcSection *load)
+{
+ IsoBcSection s = *load;
+ /*
+ * According to spec, extent for each file
+ * is padded and ISO_SECTOR_SIZE bytes aligned
+ */
+ uint32_t blks_to_load = bswap16(s.sector_count) >> ET_SECTOR_SHIFT;
+ uint32_t real_size = iso_get_file_size(bswap32(s.load_rba));
+
+ if (real_size) {
+ /* Round up blocks to load */
+ blks_to_load = (real_size + ISO_SECTOR_SIZE - 1) / ISO_SECTOR_SIZE;
+ sclp_print("ISO boot image size verified\n");
+ } else {
+ sclp_print("ISO boot image size could not be verified\n");
}
- print_eckd_msg();
- if (magic_match(mbr->magic, IPL1_MAGIC)) {
- ipl_eckd_cdl(); /* no return */
+
+ read_iso_boot_image(bswap32(s.load_rba),
+ (void *)((uint64_t)bswap16(s.load_segment)),
+ blks_to_load);
+
+ /* Trying to get PSW at zero address */
+ if (*((uint64_t *)0) & IPL_PSW_MASK) {
+ jump_to_IPL_code((*((uint64_t *)0)) & 0x7fffffff);
}
- /* LDL/CMS? */
- memset(sec, FREE_SPACE_FILLER, sizeof(sec));
- read_block(2, vlbl, "Cannot read block 2");
+ /* Try default linux start address */
+ jump_to_IPL_code(KERN_IMAGE_START);
+}
- if (magic_match(vlbl->magic, CMS1_MAGIC)) {
- ipl_eckd_ldl(ECKD_CMS); /* no return */
+static uint32_t find_iso_bc(void)
+{
+ IsoVolDesc *vd = (IsoVolDesc *)sec;
+ uint32_t block_num = ISO_PRIMARY_VD_SECTOR;
+
+ if (virtio_read_many(block_num++, sec, 1)) {
+ /* If primary vd cannot be read, there is no boot catalog */
+ return 0;
}
- if (magic_match(vlbl->magic, LNX1_MAGIC)) {
- ipl_eckd_ldl(ECKD_LDL); /* no return */
+
+ while (is_iso_vd_valid(vd) && vd->type != VOL_DESC_TERMINATOR) {
+ if (vd->type == VOL_DESC_TYPE_BOOT) {
+ IsoVdElTorito *et = &vd->vd.boot;
+
+ if (!_memcmp(&et->el_torito[0], el_torito_magic, 32)) {
+ return bswap32(et->bc_offset);
+ }
+ }
+ read_iso_sector(block_num++, sec,
+ "Failed to read ISO volume descriptor");
+ }
+
+ return 0;
+}
+
+static IsoBcSection *find_iso_bc_entry(void)
+{
+ IsoBcEntry *e = (IsoBcEntry *)sec;
+ uint32_t offset = find_iso_bc();
+ int i;
+
+ if (!offset) {
+ return NULL;
+ }
+
+ read_iso_sector(offset, sec, "Failed to read El Torito boot catalog");
+
+ if (!is_iso_bc_valid(e)) {
+ /* The validation entry is mandatory */
+ panic("No valid boot catalog found!\n");
+ return NULL;
}
- ipl_eckd_ldl(ECKD_LDL_UNLABELED); /* it still may return */
/*
- * Ok, it is not a LDL by any means.
- * It still might be a CDL with zero record keys for IPL1 and IPL2
+ * Each entry has 32 bytes size, so one sector cannot contain > 64 entries.
+ * We consider only boot catalogs with no more than 64 entries.
*/
- ipl_eckd_cdl();
+ for (i = 1; i < ISO_BC_ENTRY_PER_SECTOR; i++) {
+ if (e[i].id == ISO_BC_BOOTABLE_SECTION) {
+ if (is_iso_bc_entry_compatible(&e[i].body.sect)) {
+ return &e[i].body.sect;
+ }
+ }
+ }
+
+ panic("No suitable boot entry found on ISO-9660 media!\n");
+
+ return NULL;
+}
+
+static void ipl_iso_el_torito(void)
+{
+ IsoBcSection *s = find_iso_bc_entry();
+
+ if (s) {
+ load_iso_bc_entry(s);
+ /* no return */
+ }
+}
+
+/***********************************************************************
+ * Bus specific IPL sequences
+ */
+
+static void zipl_load_vblk(void)
+{
+ if (virtio_guessed_disk_nature()) {
+ virtio_assume_iso9660();
+ }
+ ipl_iso_el_torito();
+
+ if (virtio_guessed_disk_nature()) {
+ sclp_print("Using guessed DASD geometry.\n");
+ virtio_assume_eckd();
+ }
+ ipl_eckd();
+}
+
+static void zipl_load_vscsi(void)
+{
+ if (virtio_get_block_size() == VIRTIO_ISO_BLOCK_SIZE) {
+ /* Is it an ISO image in non-CD drive? */
+ ipl_iso_el_torito();
+ }
+
+ sclp_print("Using guessed DASD geometry.\n");
+ virtio_assume_eckd();
+ ipl_eckd();
+}
+
+/***********************************************************************
+ * IPL starts here
+ */
+
+void zipl_load(void)
+{
+ if (virtio_get_device()->is_cdrom) {
+ ipl_iso_el_torito();
+ panic("\n! Cannot IPL this ISO image !\n");
+ }
+
+ ipl_scsi();
+
+ switch (virtio_get_device_type()) {
+ case VIRTIO_ID_BLOCK:
+ zipl_load_vblk();
+ break;
+ case VIRTIO_ID_SCSI:
+ zipl_load_vscsi();
+ break;
+ default:
+ panic("\n! Unknown IPL device type !\n");
+ }
- virtio_panic("\n* this can never happen *\n");
+ panic("\n* this can never happen *\n");
}