pc-bios/s390-ccw: ISO-9660 El Torito boot implementation

This patch enables boot from media formatted according to
ISO-9660 and El Torito bootable CD specification.

We try to boot from device as ISO-9660 media when SCSI IPL failed.

The first boot catalog entry with bootable flag is used.

ISO-9660 media with default 2048-bytes sector size only is supported.

Signed-off-by: Maxim Samoylov <max7255@linux.vnet.ibm.com>
Reviewed-by: David Hildenbrand <dahi@linux.vnet.ibm.com>
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
This commit is contained in:
Maxim Samoylov 2015-10-12 17:50:20 +02:00 committed by Cornelia Huck
parent 38150be860
commit 866cac91e0
4 changed files with 306 additions and 0 deletions

View File

@ -444,6 +444,107 @@ static void ipl_scsi(void)
zipl_run(prog_table_entry); /* no return */
}
/***********************************************************************
* IPL El Torito ISO9660 image or DVD
*/
static bool is_iso_bc_entry_compatible(IsoBcSection *s)
{
return true;
}
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;
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 +564,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");

View File

@ -341,4 +341,195 @@ 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);
}
#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";
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;
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 */

View File

@ -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;

View File

@ -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);