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:
parent
38150be860
commit
866cac91e0
@ -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");
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user