summaryrefslogtreecommitdiffstats
path: root/pc-bios
diff options
context:
space:
mode:
Diffstat (limited to 'pc-bios')
-rw-r--r--pc-bios/README2
-rw-r--r--pc-bios/s390-ccw.imgbin13856 -> 17760 bytes
-rw-r--r--pc-bios/s390-ccw/bootmap.c206
-rw-r--r--pc-bios/s390-ccw/bootmap.h206
-rw-r--r--pc-bios/s390-ccw/virtio.c7
-rw-r--r--pc-bios/s390-ccw/virtio.h8
-rw-r--r--pc-bios/slof.binbin915584 -> 914712 bytes
7 files changed, 422 insertions, 7 deletions
diff --git a/pc-bios/README b/pc-bios/README
index e4154ab..d260c1b 100644
--- a/pc-bios/README
+++ b/pc-bios/README
@@ -17,7 +17,7 @@
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
implementation for certain IBM POWER hardware. The sources are at
https://github.com/aik/SLOF, and the image currently in qemu is
- built from git tag qemu-slof-20150813.
+ built from git tag qemu-slof-20151103.
- sgabios (the Serial Graphics Adapter option ROM) provides a means for
legacy x86 software to communicate with an attached serial console as
diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img
index 0c540a1..e0d9452 100644
--- a/pc-bios/s390-ccw.img
+++ b/pc-bios/s390-ccw.img
Binary files differ
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index b678d5e..415508b 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -445,6 +445,206 @@ static void ipl_scsi(void)
}
/***********************************************************************
+ * IPL El Torito ISO9660 image or DVD
+ */
+
+static bool is_iso_bc_entry_compatible(IsoBcSection *s)
+{
+ uint8_t *magic_sec = (uint8_t *)(sec + ISO_SECTOR_SIZE);
+
+ if (s->unused || !s->sector_count) {
+ return false;
+ }
+ read_iso_sector(bswap32(s->load_rba), magic_sec,
+ "Failed to read image sector 0");
+
+ /* 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];
+
+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");
+ }
+ }
+
+ 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");
+ }
+
+ 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);
+ }
+
+ /* Try default linux start address */
+ jump_to_IPL_code(KERN_IMAGE_START);
+}
+
+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;
+ }
+
+ 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 */
+ virtio_panic("No valid boot catalog found!\n");
+ return NULL;
+ }
+
+ /*
+ * Each entry has 32 bytes size, so one sector cannot contain > 64 entries.
+ * We consider only boot catalogs with no more than 64 entries.
+ */
+ 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;
+ }
+ }
+ }
+
+ virtio_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 */
+ }
+}
+
+/***********************************************************************
* IPL starts here
*/
@@ -463,6 +663,12 @@ void zipl_load(void)
ipl_scsi(); /* no return */
}
+ /* Check if we can boot as ISO media */
+ if (virtio_guessed_disk_nature()) {
+ virtio_assume_iso9660();
+ }
+ ipl_iso_el_torito();
+
/* We have failed to follow the SCSI scheme, so */
if (virtio_guessed_disk_nature()) {
sclp_print("Using guessed DASD geometry.\n");
diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
index ab132e3..f98765b 100644
--- a/pc-bios/s390-ccw/bootmap.h
+++ b/pc-bios/s390-ccw/bootmap.h
@@ -341,4 +341,210 @@ static inline bool magic_match(const void *data, const void *magic)
return *((uint32_t *)data) == *((uint32_t *)magic);
}
+static inline int _memcmp(const void *s1, const void *s2, size_t n)
+{
+ int i;
+ const uint8_t *p1 = s1, *p2 = s2;
+
+ for (i = 0; i < n; i++) {
+ if (p1[i] != p2[i]) {
+ return p1[i] > p2[i] ? 1 : -1;
+ }
+ }
+
+ return 0;
+}
+
+/* from include/qemu/bswap.h */
+
+/* El Torito is always little-endian */
+static inline uint16_t bswap16(uint16_t x)
+{
+ return ((x & 0x00ff) << 8) | ((x & 0xff00) >> 8);
+}
+
+static inline uint32_t bswap32(uint32_t x)
+{
+ return ((x & 0x000000ffU) << 24) | ((x & 0x0000ff00U) << 8) |
+ ((x & 0x00ff0000U) >> 8) | ((x & 0xff000000U) >> 24);
+}
+
+static inline uint64_t bswap64(uint64_t x)
+{
+ return ((x & 0x00000000000000ffULL) << 56) |
+ ((x & 0x000000000000ff00ULL) << 40) |
+ ((x & 0x0000000000ff0000ULL) << 24) |
+ ((x & 0x00000000ff000000ULL) << 8) |
+ ((x & 0x000000ff00000000ULL) >> 8) |
+ ((x & 0x0000ff0000000000ULL) >> 24) |
+ ((x & 0x00ff000000000000ULL) >> 40) |
+ ((x & 0xff00000000000000ULL) >> 56);
+}
+
+static inline uint32_t iso_733_to_u32(uint64_t x)
+{
+ return (uint32_t)x;
+}
+
+#define ISO_SECTOR_SIZE 2048
+/* El Torito specifies boot image size in 512 byte blocks */
+#define ET_SECTOR_SHIFT 2
+#define KERN_IMAGE_START 0x010000UL
+#define PSW_MASK_64 0x0000000100000000ULL
+#define PSW_MASK_32 0x0000000080000000ULL
+#define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64)
+
+#define ISO_PRIMARY_VD_SECTOR 16
+
+static inline void read_iso_sector(uint32_t block_offset, void *buf,
+ const char *errmsg)
+{
+ IPL_assert(virtio_read_many(block_offset, buf, 1) == 0, errmsg);
+}
+
+static inline void read_iso_boot_image(uint32_t block_offset, void *load_addr,
+ uint32_t blks_to_load)
+{
+ IPL_assert(virtio_read_many(block_offset, load_addr, blks_to_load) == 0,
+ "Failed to read boot image!");
+}
+
+const uint8_t el_torito_magic[] = "EL TORITO SPECIFICATION"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+
+#define ISO9660_MAX_DIR_DEPTH 8
+
+typedef struct IsoDirHdr {
+ uint8_t dr_len;
+ uint8_t ear_len;
+ uint64_t ext_loc;
+ uint64_t data_len;
+ uint8_t recording_datetime[7];
+ uint8_t file_flags;
+ uint8_t file_unit_size;
+ uint8_t gap_size;
+ uint32_t vol_seqnum;
+ uint8_t fileid_len;
+} __attribute__((packed)) IsoDirHdr;
+
+typedef struct IsoVdElTorito {
+ uint8_t el_torito[32]; /* must contain el_torito_magic value */
+ uint8_t unused0[32];
+ uint32_t bc_offset;
+ uint8_t unused1[1974];
+} __attribute__((packed)) IsoVdElTorito;
+
+typedef struct IsoVdPrimary {
+ uint8_t unused1;
+ uint8_t sys_id[32];
+ uint8_t vol_id[32];
+ uint8_t unused2[8];
+ uint64_t vol_space_size;
+ uint8_t unused3[32];
+ uint32_t vol_set_size;
+ uint32_t vol_seqnum;
+ uint32_t log_block_size;
+ uint64_t path_table_size;
+ uint32_t l_path_table;
+ uint32_t opt_l_path_table;
+ uint32_t m_path_table;
+ uint32_t opt_m_path_table;
+ IsoDirHdr rootdir;
+ uint8_t root_null;
+ uint8_t reserved2[1858];
+} __attribute__((packed)) IsoVdPrimary;
+
+typedef struct IsoVolDesc {
+ uint8_t type;
+ uint8_t ident[5];
+ uint8_t version;
+ union {
+ IsoVdElTorito boot;
+ IsoVdPrimary primary;
+ } vd;
+} __attribute__((packed)) IsoVolDesc;
+
+const uint8_t vol_desc_magic[] = "CD001";
+#define VOL_DESC_TYPE_BOOT 0
+#define VOL_DESC_TYPE_PRIMARY 1
+#define VOL_DESC_TYPE_SUPPLEMENT 2
+#define VOL_DESC_TYPE_PARTITION 3
+#define VOL_DESC_TERMINATOR 255
+
+static inline bool is_iso_vd_valid(IsoVolDesc *vd)
+{
+ return !_memcmp(&vd->ident[0], vol_desc_magic, 5) &&
+ vd->version == 0x1 &&
+ vd->type <= VOL_DESC_TYPE_PARTITION;
+}
+
+typedef struct IsoBcValid {
+ uint8_t platform_id;
+ uint16_t reserved;
+ uint8_t id[24];
+ uint16_t checksum;
+ uint8_t key[2];
+} __attribute__((packed)) IsoBcValid;
+
+typedef struct IsoBcSection {
+ uint8_t boot_type;
+ uint16_t load_segment;
+ uint8_t sys_type;
+ uint8_t unused;
+ uint16_t sector_count;
+ uint32_t load_rba;
+ uint8_t selection[20];
+} __attribute__((packed)) IsoBcSection;
+
+typedef struct IsoBcHdr {
+ uint8_t platform_id;
+ uint16_t sect_num;
+ uint8_t id[28];
+} __attribute__((packed)) IsoBcHdr;
+
+/*
+ * Match two CCWs located after PSW and eight filler bytes.
+ * From libmagic and arch/s390/kernel/head.S.
+ */
+const uint8_t linux_s390_magic[] = "\x02\x00\x00\x18\x60\x00\x00\x50\x02\x00"
+ "\x00\x68\x60\x00\x00\x50\x40\x40\x40\x40"
+ "\x40\x40\x40\x40";
+
+typedef struct IsoBcEntry {
+ uint8_t id;
+ union {
+ IsoBcValid valid; /* id == 0x01 */
+ IsoBcSection sect; /* id == 0x88 || id == 0x0 */
+ IsoBcHdr hdr; /* id == 0x90 || id == 0x91 */
+ } body;
+} __attribute__((packed)) IsoBcEntry;
+
+#define ISO_BC_ENTRY_PER_SECTOR (ISO_SECTOR_SIZE / sizeof(IsoBcEntry))
+#define ISO_BC_HDR_VALIDATION 0x01
+#define ISO_BC_BOOTABLE_SECTION 0x88
+#define ISO_BC_MAGIC_55 0x55
+#define ISO_BC_MAGIC_AA 0xaa
+#define ISO_BC_PLATFORM_X86 0x0
+#define ISO_BC_PLATFORM_PPC 0x1
+#define ISO_BC_PLATFORM_MAC 0x2
+
+static inline bool is_iso_bc_valid(IsoBcEntry *e)
+{
+ IsoBcValid *v = &e->body.valid;
+
+ if (e->id != ISO_BC_HDR_VALIDATION) {
+ return false;
+ }
+
+ if (v->platform_id != ISO_BC_PLATFORM_X86 &&
+ v->platform_id != ISO_BC_PLATFORM_PPC &&
+ v->platform_id != ISO_BC_PLATFORM_MAC) {
+ return false;
+ }
+
+ return v->key[0] == ISO_BC_MAGIC_55 &&
+ v->key[1] == ISO_BC_MAGIC_AA &&
+ v->reserved == 0x0;
+}
+
#endif /* _PC_BIOS_S390_CCW_BOOTMAP_H */
diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c
index 57ff1b0..87aed38 100644
--- a/pc-bios/s390-ccw/virtio.c
+++ b/pc-bios/s390-ccw/virtio.c
@@ -278,6 +278,13 @@ void virtio_assume_scsi(void)
blk_cfg.physical_block_exp = 0;
}
+void virtio_assume_iso9660(void)
+{
+ guessed_disk_nature = true;
+ blk_cfg.blk_size = 2048;
+ blk_cfg.physical_block_exp = 0;
+}
+
void virtio_assume_eckd(void)
{
guessed_disk_nature = true;
diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
index c23466b..afa01a8 100644
--- a/pc-bios/s390-ccw/virtio.h
+++ b/pc-bios/s390-ccw/virtio.h
@@ -187,6 +187,7 @@ typedef struct VirtioBlkConfig {
bool virtio_guessed_disk_nature(void);
void virtio_assume_scsi(void);
void virtio_assume_eckd(void);
+void virtio_assume_iso9660(void);
extern bool virtio_disk_is_scsi(void);
extern bool virtio_disk_is_eckd(void);
@@ -199,14 +200,9 @@ extern int virtio_read_many(ulong sector, void *load_addr, int sec_num);
#define VIRTIO_SECTOR_SIZE 512
-static inline ulong virtio_eckd_sector_adjust(ulong sector)
-{
- return sector * (virtio_get_block_size() / VIRTIO_SECTOR_SIZE);
-}
-
static inline ulong virtio_sector_adjust(ulong sector)
{
- return virtio_disk_is_eckd() ? virtio_eckd_sector_adjust(sector) : sector;
+ return sector * (virtio_get_block_size() / VIRTIO_SECTOR_SIZE);
}
#endif /* VIRTIO_H */
diff --git a/pc-bios/slof.bin b/pc-bios/slof.bin
index 701933f..90f3099 100644
--- a/pc-bios/slof.bin
+++ b/pc-bios/slof.bin
Binary files differ
OpenPOWER on IntegriCloud