diff --git a/include/boot/boot_defs.h b/include/boot/boot_defs.h index 8066a864..41ab4b09 100644 --- a/include/boot/boot_defs.h +++ b/include/boot/boot_defs.h @@ -57,11 +57,41 @@ struct linear_blockptr { uint8_t reserved[4]; } __packed; +/* + * Format of a boot record on ECKD DASD for List-Directed IPL + */ +struct eckd_boot_record { + uint8_t magic[4]; + uint32_t version_id; + uint8_t unused[8]; + uint8_t program_table_pointer[16]; + uint8_t reserved[478]; + uint16_t os_id; +} __packed; + /* * Layout of block pointer for cylinder/head/sector devices * e.g. ECKD */ -struct eckd_blockptr { + +enum blkptr_format_id { + /* + * this is the old format which serves only CCW-type IPL, + * and doesn't fit List-Directed IPL. Still supported for + * compatibility reasons. + */ + LEGACY_BLKPTR_FORMAT_ID, + /* + * this is the "new" format which serves only List-Directed IPL, + * but is also suitable for CCW-type IPL. + */ + BLKPTR_FORMAT_ID +}; + +/* + * Block pointers format identified as LEGACY_BLKPTR_FORMAT_ID. + */ +struct eckd_blockptr_legacy { uint16_t cyl; uint16_t head; uint8_t sec; @@ -70,6 +100,18 @@ struct eckd_blockptr { uint8_t reserved[8]; } __packed; +/* + * Block pointers format identified as BLKPTR_FORMAT_ID. + */ +struct eckd_blockptr { + uint32_t cyl; + uint8_t head; + uint8_t sec; + uint8_t reserved1[4]; + uint16_t blockct; + uint8_t reserved2[4]; +} __packed; + typedef enum { COMPONENT_TYPE_EXECUTE = 0x01, COMPONENT_TYPE_LOAD = 0x02, @@ -153,6 +195,7 @@ struct boot_info_bp_dump { struct boot_info_bp_ipl { union { + struct eckd_blockptr_legacy eckd_legacy; struct eckd_blockptr eckd; struct linear_blockptr lin; } bm_ptr; diff --git a/include/lib/vtoc.h b/include/lib/vtoc.h index 6c1b24ac..971fdaa8 100644 --- a/include/lib/vtoc.h +++ b/include/lib/vtoc.h @@ -97,6 +97,17 @@ typedef struct volume_label unsigned long long formatted_blocks; /* valid when ldl_version >= f2 */ } __attribute__ ((packed)) volume_label_t; +/** + * This represents a volume label specific for OS/390 compatible disk layout + */ +struct vol_label_cdl { + char volkey[4]; + char vollbl[4]; + char opaq[70]; + cchhb_t br; /* boot record address */ + char stdv[1]; /* standard version ID */ +} __attribute__ ((packed)); + static inline int is_vol1(char *this) { char vol1[] = {0xe5, 0xd6, 0xd3, 0xf1, 0x00} /* "VOL1" in EBCDIC */; diff --git a/zipl/boot/eckd2.c b/zipl/boot/eckd2.c index 8062db67..ae598aec 100644 --- a/zipl/boot/eckd2.c +++ b/zipl/boot/eckd2.c @@ -15,14 +15,16 @@ int extract_length(void *data) { - struct eckd_blockptr *blockptr = (struct eckd_blockptr *)data; + struct eckd_blockptr_legacy *blockptr = + (struct eckd_blockptr_legacy *)data; return blockptr->size * (blockptr->blockct + 1); } int is_zero_block(void *data) { - struct eckd_blockptr *blockptr = (struct eckd_blockptr *)data; + struct eckd_blockptr_legacy *blockptr = + (struct eckd_blockptr_legacy *)data; return blockptr->cyl || blockptr->head || blockptr->sec; } @@ -30,7 +32,7 @@ int is_zero_block(void *data) void * load_direct(disk_blockptr_t *data, struct subchannel_id subchannel_id, void *load_addr) { - struct eckd_blockptr *blockptr = &data->eckd; + struct eckd_blockptr_legacy *blockptr = &data->eckd; struct ccw1 *ccws; unsigned long record_size; struct seek_arg seek_addr; diff --git a/zipl/boot/stage2.h b/zipl/boot/stage2.h index 79c647e3..786002c8 100644 --- a/zipl/boot/stage2.h +++ b/zipl/boot/stage2.h @@ -27,7 +27,7 @@ #include "error.h" typedef union { - struct eckd_blockptr eckd; + struct eckd_blockptr_legacy eckd; struct linear_blockptr linear; } disk_blockptr_t; diff --git a/zipl/include/boot.h b/zipl/include/boot.h index 34a01478..54755f52 100644 --- a/zipl/include/boot.h +++ b/zipl/include/boot.h @@ -158,8 +158,8 @@ struct mvdump_parm_table { void boot_get_dump_info(struct boot_info *boot_info, uint8_t dev_type, void *param); -void boot_get_ipl_info(struct boot_info *boot_info, uint8_t dev_type, - disk_blockptr_t *bm_ptr, struct disk_info *info); +void boot_get_ipl_info_ccw(struct boot_info *boot_info, uint8_t dev_type, + disk_blockptr_t *bm_ptr, struct disk_info *info); int boot_check_data(void); int boot_init_fba_stage0(struct boot_fba_stage0* stage0, @@ -187,6 +187,8 @@ int boot_get_tape_ipl(void** data, size_t* size, address_t parm_addr, address_t initrd_addr, address_t image_addr); int boot_get_tape_dump(void** data, size_t* size, uint64_t mem); int boot_get_eckd_dump_stage2(void** data, size_t* size, uint64_t mem); +int boot_get_eckd_ld_ipl_br(void **br_ptr, size_t *size_ptr, + disk_blockptr_t *table, struct disk_info *info); int boot_get_eckd_mvdump_stage2(void** data, size_t* size, uint64_t mem, uint8_t force, struct mvdump_parm_table); int boot_get_fba_dump_stage2(void** data, size_t* size, uint64_t mem); diff --git a/zipl/include/bootmap.h b/zipl/include/bootmap.h index bd0aff26..c1c921a3 100644 --- a/zipl/include/bootmap.h +++ b/zipl/include/bootmap.h @@ -51,11 +51,7 @@ struct file_signature { int bootmap_header_init(int fd); int bootmap_header_read(int fd, struct bootmap_header *bh); int bootmap_header_write(int fd, struct bootmap_header *bh); -int bootmap_create(struct job_data* job, disk_blockptr_t* program_table, - disk_blockptr_t *scsi_dump_sb_blockptr, - disk_blockptr_t** stage2_list, blocknum_t* stage2_count, - char** device, struct disk_info** new_info); -void bootmap_store_blockptr(void* buffer, disk_blockptr_t* ptr, - struct disk_info* info); +void bootmap_store_blockptr(void *buffer, disk_blockptr_t *ptr, + struct disk_info *info, int fid); #endif /* if not BOOTMAP_H */ diff --git a/zipl/include/disk.h b/zipl/include/disk.h index bb7f9f82..0147cee5 100644 --- a/zipl/include/disk.h +++ b/zipl/include/disk.h @@ -16,6 +16,7 @@ #include #include "zipl.h" +#include "lib/vtoc.h" /* Type for representing disk block numbers */ @@ -23,7 +24,7 @@ typedef uint64_t blocknum_t; /* Pointer to block on a disk with cyl/head/sec layout */ struct disk_blockptr_chs { - int cyl; + unsigned int cyl; int head; int sec; int size; @@ -52,14 +53,6 @@ typedef enum { disk_type_eckd_cdl, } disk_type_t; -/* from linux/hdregs.h */ -struct hd_geometry { - unsigned char heads; - unsigned char sectors; - unsigned short cylinders; - unsigned long start; -}; - /* Disk information source */ typedef enum { source_auto, @@ -108,6 +101,7 @@ int disk_get_info_from_file(const char* filename, struct disk_info** info); void disk_free_info(struct disk_info* info); char* disk_get_type_name(disk_type_t type); +char *disk_get_ipl_type(disk_type_t type); int disk_is_large_volume(struct disk_info* info); int disk_cyl_from_blocknum(blocknum_t blocknum, struct disk_info* info); int disk_head_from_blocknum(blocknum_t blocknum, struct disk_info* info); diff --git a/zipl/include/install.h b/zipl/include/install.h index 6a270610..79d65185 100644 --- a/zipl/include/install.h +++ b/zipl/include/install.h @@ -15,12 +15,90 @@ #include "disk.h" #include "job.h" #include "zipl.h" +#include "boot/boot_defs.h" +/* + * For compatibility reasons zIPL can build multiple "similar" + * program tables, which differ only in block pointers format + */ +enum program_table_id { + PROGRAM_TABLE_0, + PROGRAM_TABLE_1, + NR_PROGRAM_TABLES +}; -int install_bootloader(const char* device, disk_blockptr_t* program_table, - disk_blockptr_t* scsi_dump_sb_blockptr, - disk_blockptr_t* stage2_data, blocknum_t stage2_count, - struct disk_info* info, struct job_data* job); +enum program_component_id { + COMPONENT_ID_HEAP_AREA, + COMPONENT_ID_STACK_AREA, + COMPONENT_ID_LOADER_SIGNATURE, + COMPONENT_ID_LOADER, + COMPONENT_ID_PARAMETERS, + COMPONENT_ID_IMAGE_SIGNATURE, + COMPONENT_ID_KERNEL_IMAGE, + COMPONENT_ID_PARMLINE, + COMPONENT_ID_RAMDISK_SIGNATURE, + COMPONENT_ID_RAMDISK, + COMPONENT_ID_ENVBLK, + COMPONENT_ID_SEGMENT_FILE, + NR_PROGRAM_COMPONENTS +}; + +struct component_loc { + address_t addr; + size_t size; +}; + +struct component_footer { + component_type type; + const char *desc; +}; + +struct program_component { + struct component_loc loc; + disk_blockptr_t *list; + blocknum_t count; +}; + +struct program_table { + disk_blockptr_t table; + disk_blockptr_t *stage1b_list; + blocknum_t stage1b_count; +}; + +/* Bootloader Installation Set */ +struct install_set { + struct program_table tables[NR_PROGRAM_TABLES]; + struct program_component *components[NR_PROGRAM_COMPONENTS]; + int nr_tables; /* number of "similar" program tables to be installed */ + int nr_menu_entries; + int fd; + char *device; + char *filename; + struct disk_info *info; + disk_blockptr_t scsi_dump_sb_blockptr; +}; + +extern struct component_footer component_footers[NR_PROGRAM_COMPONENTS]; + +static inline component_type component_type_by_id(enum program_component_id id) +{ + return component_footers[id].type; +} + +static inline const char *component_desc_by_id(enum program_component_id id) +{ + return component_footers[id].desc; +} + +static inline struct program_component *get_component(struct install_set *bis, + int i, int j) +{ + return bis->components[i] + j; +} + +int prepare_bootloader(struct job_data *job, struct install_set *bis); +int install_bootloader(struct job_data *job, struct install_set *bis); +void free_bootloader(struct install_set *bis); int install_tapeloader(const char* device, const char* image, const char* parmline, const char* ramdisk, address_t image_addr, address_t parm_addr, diff --git a/zipl/src/Makefile b/zipl/src/Makefile index 786bb7f3..64eabe40 100644 --- a/zipl/src/Makefile +++ b/zipl/src/Makefile @@ -7,7 +7,8 @@ ALL_CPPFLAGS += -I../include -I../boot \ -D_FILE_OFFSET_BITS=64 $(NO_PIE_CFLAGS) ALL_LDFLAGS += -Wl,-z,noexecstack $(NO_PIE_LDFLAGS) -libs = $(rootdir)/libutil/libutil.a +libs = $(rootdir)/libutil/libutil.a \ + $(rootdir)/libvtoc/libvtoc.a \ objects = misc.o error.o scan.o job.o boot.o bootmap.o fs-map.o disk.o \ bootmap_header.o envblk.o install.o zipl.o $(rootdir)/zipl/boot/data.o diff --git a/zipl/src/boot.c b/zipl/src/boot.c index a5d1302b..2f040345 100644 --- a/zipl/src/boot.c +++ b/zipl/src/boot.c @@ -16,6 +16,7 @@ #include #include #include +#include "lib/util_libc.h" #include "stage3.h" @@ -560,9 +561,12 @@ boot_get_dump_info(struct boot_info *boot_info, uint8_t dev_type, void *param) sizeof(boot_info->bp.dump.param)); } +/* + * Helper function to install a boot record for CCW-type IPL from DASD + */ void -boot_get_ipl_info(struct boot_info *boot_info, uint8_t dev_type, - disk_blockptr_t *bm_ptr, struct disk_info *info) +boot_get_ipl_info_ccw(struct boot_info *boot_info, uint8_t dev_type, + disk_blockptr_t *bm_ptr, struct disk_info *info) { memset(boot_info, 0, sizeof(*boot_info)); memcpy(&boot_info->magic, BOOT_INFO_MAGIC, sizeof(boot_info->magic)); @@ -570,6 +574,30 @@ boot_get_ipl_info(struct boot_info *boot_info, uint8_t dev_type, boot_info->dev_type = dev_type; boot_info->bp_type = BOOT_INFO_BP_TYPE_IPL; boot_info->version = BOOT_INFO_VERSION; - bootmap_store_blockptr(&boot_info->bp, bm_ptr, info); + bootmap_store_blockptr(&boot_info->bp, bm_ptr, info, + LEGACY_BLKPTR_FORMAT_ID); } +/** + * Allocate and initialize a boot record for List-Directed IPL from DASD + * + * TABLE: address of an actual program table + */ +int boot_get_eckd_ld_ipl_br(void **br_ptr, size_t *size_ptr, + disk_blockptr_t *table, struct disk_info *info) +{ + struct eckd_boot_record *br; + + br = util_zalloc(info->phy_block_size); + if (!br) + return -1; + + memcpy(&br->magic, ZIPL_MAGIC, ZIPL_MAGIC_SIZE); + br->version_id = DISK_LAYOUT_ID; + bootmap_store_blockptr(&br->program_table_pointer, table, info, + BLKPTR_FORMAT_ID); + br->os_id = 0x55aa; /* LINUX */ + *br_ptr = br; + *size_ptr = info->phy_block_size; + return 0; +} diff --git a/zipl/src/bootmap.c b/zipl/src/bootmap.c index 26825b95..0bf9ca39 100644 --- a/zipl/src/bootmap.c +++ b/zipl/src/bootmap.c @@ -53,6 +53,9 @@ get_blockptr_size(struct disk_info* info) return sizeof(struct linear_blockptr); case disk_type_eckd_ldl: case disk_type_eckd_cdl: + assert(sizeof(struct eckd_blockptr_legacy) == + sizeof(struct eckd_blockptr)); + return sizeof(struct eckd_blockptr); case disk_type_diag: break; @@ -61,12 +64,17 @@ get_blockptr_size(struct disk_info* info) } -void -bootmap_store_blockptr(void* buffer, disk_blockptr_t* ptr, - struct disk_info* info) +/** + * Pack a "plain" disk block pointer defined by PTR to BUFFER in the format + * defined by FORMAT_ID (relevant only for ECKD disk types) + */ +void bootmap_store_blockptr(void *buffer, disk_blockptr_t *ptr, + struct disk_info *info, + int format_id) { - struct linear_blockptr *lin; + struct eckd_blockptr_legacy *eckd_legacy; struct eckd_blockptr *eckd; + struct linear_blockptr *lin; memset(buffer, 0, get_blockptr_size(info)); if (ptr != NULL) { @@ -80,13 +88,29 @@ bootmap_store_blockptr(void* buffer, disk_blockptr_t* ptr, break; case disk_type_eckd_ldl: case disk_type_eckd_cdl: - eckd = (struct eckd_blockptr *) buffer; - eckd->cyl = ptr->chs.cyl; - eckd->head = ptr->chs.head | - ((ptr->chs.cyl >> 12) & 0xfff0); - eckd->sec = ptr->chs.sec; - eckd->size = ptr->chs.size; - eckd->blockct = ptr->chs.blockct; + switch (format_id) { + case LEGACY_BLKPTR_FORMAT_ID: + eckd_legacy = + (struct eckd_blockptr_legacy *)buffer; + + eckd_legacy->cyl = ptr->chs.cyl; + eckd_legacy->head = ptr->chs.head | + ((ptr->chs.cyl >> 12) & 0xfff0); + eckd_legacy->sec = ptr->chs.sec; + eckd_legacy->size = ptr->chs.size; + eckd_legacy->blockct = ptr->chs.blockct; + break; + case BLKPTR_FORMAT_ID: + eckd = (struct eckd_blockptr *)buffer; + + eckd->cyl = ptr->chs.cyl; + eckd->head = ptr->chs.head; + eckd->sec = ptr->chs.sec; + eckd->blockct = ptr->chs.blockct; + break; + default: + assert(0); + } break; case disk_type_diag: break; @@ -94,7 +118,6 @@ bootmap_store_blockptr(void* buffer, disk_blockptr_t* ptr, } } - #define PROGRAM_TABLE_BLOCK_SIZE 512 /* Calculate the maximum number of entries in the program table. INFO @@ -160,10 +183,9 @@ check_secure_boot_support(void) * of segment table blocks to the file identified by file descriptor FD. Upon * success, return 0 and set SECTION_POINTER to point to the first block in * the resulting segment table. Return non-zero otherwise. */ -static int -add_segment_table(int fd, disk_blockptr_t* list, blocknum_t count, - disk_blockptr_t* segment_pointer, - struct disk_info* info) +static int add_segment_table(int fd, disk_blockptr_t *list, blocknum_t count, + disk_blockptr_t *segment_pointer, + struct disk_info *info, int program_table_id) { disk_blockptr_t next; void* buffer; @@ -185,18 +207,21 @@ add_segment_table(int fd, disk_blockptr_t* list, blocknum_t count, /* Replace holes with empty block if necessary*/ if (disk_is_zero_block(&list[count-1], info)) bootmap_store_blockptr( - VOID_ADD(buffer, offset * pointer_size), - &empty_block, info); + VOID_ADD(buffer, offset * pointer_size), + &empty_block, info, + program_table_id); else bootmap_store_blockptr( - VOID_ADD(buffer, offset * pointer_size), - &list[count-1], info); + VOID_ADD(buffer, offset * pointer_size), + &list[count - 1], info, + program_table_id); if (offset > 0) continue; /* Finalize segment table */ offset = max_offset; bootmap_store_blockptr(VOID_ADD(buffer, offset * pointer_size), - &next, info); + &next, info, + program_table_id); rc = disk_write_block_aligned(fd, buffer, info->phy_block_size, &next, info); if (rc) { @@ -210,9 +235,9 @@ add_segment_table(int fd, disk_blockptr_t* list, blocknum_t count, } -static int -add_program_table(int fd, disk_blockptr_t* table, int entries, - disk_blockptr_t* pointer, struct disk_info* info) +static int add_program_table(int fd, disk_blockptr_t *table, int entries, + disk_blockptr_t *pointer, struct disk_info *info, + int program_table_id) { void* block; int i; @@ -227,7 +252,8 @@ add_program_table(int fd, disk_blockptr_t* table, int entries, offset = get_blockptr_size(info); for (i=0; i < entries; i++) { bootmap_store_blockptr(VOID_ADD(block, offset), &table[i], - info); + info, + program_table_id); offset += get_blockptr_size(info); } /* Write program table */ @@ -237,10 +263,9 @@ add_program_table(int fd, disk_blockptr_t* table, int entries, return rc; } -static void -create_component_entry(void* buffer, disk_blockptr_t* pointer, - component_type type, component_data data, - struct disk_info* info) +static void create_component_entry(void *buffer, disk_blockptr_t *pointer, + component_type type, component_data data, + struct disk_info *info, int program_table_id) { struct component_entry* entry; @@ -249,16 +274,16 @@ create_component_entry(void* buffer, disk_blockptr_t* pointer, entry->type = (uint8_t) type; switch (type) { case COMPONENT_TYPE_LOAD: - bootmap_store_blockptr(&entry->data, pointer, - info); + bootmap_store_blockptr(&entry->data, pointer, info, + program_table_id); entry->compdat.load_address = data.load_address; break; case COMPONENT_TYPE_EXECUTE: entry->compdat.load_psw = data.load_psw; break; case COMPONENT_TYPE_SIGNATURE: - bootmap_store_blockptr(&entry->data, pointer, - info); + bootmap_store_blockptr(&entry->data, pointer, info, + program_table_id); entry->compdat.sig_head = data.sig_head; break; } @@ -275,28 +300,29 @@ create_component_header(void* buffer, component_header_type type) header->type = (uint8_t) type; } - -struct component_loc { - address_t addr; - size_t size; -}; - -static int -add_component_file_range(int fd, const char *filename, struct file_range *reg, - address_t load_address, - size_t trailer, void *component, int add_files, - struct disk_info *info, struct job_target_data *target, - struct component_loc *location) +static int add_component_file_range(struct install_set *bis, + const char *filename, + struct file_range *reg, + address_t load_address, + size_t trailer, void *component, + int add_files, + struct job_target_data *target, + int comp_id, int menu_idx, + int program_table_id) { + struct program_component *pc = get_component(bis, comp_id, menu_idx); + struct component_loc *location = &pc->loc; + disk_blockptr_t **list = &pc->list; + blocknum_t *count = &pc->count; struct disk_info* file_info; - struct component_loc loc; disk_blockptr_t segment; - disk_blockptr_t* list; char* buffer; size_t size; - blocknum_t count; int rc; + if (program_table_id) + /* skip the preparation work */ + goto write_segment_table; if (add_files) { assert(reg == NULL); /* not implemented */ /* Read file to buffer */ @@ -307,10 +333,10 @@ add_component_file_range(int fd, const char *filename, struct file_range *reg, } size -= trailer; /* Write buffer */ - count = disk_write_block_buffer(fd, 0, buffer, - size, &list, info); + *count = disk_write_block_buffer(bis->fd, 0, buffer, + size, list, bis->info); free(buffer); - if (count == 0) { + if (*count == 0) { error_text("Could not write to bootmap file"); return -1; } @@ -319,140 +345,142 @@ add_component_file_range(int fd, const char *filename, struct file_range *reg, rc = disk_get_info_from_file(filename, target, &file_info); if (rc) return -1; - if (file_info->device != info->device) { + if (file_info->device != bis->info->device) { disk_free_info(file_info); error_reason("File is not on target device"); return -1; } /* Get block list from existing file */ - count = disk_get_blocklist_from_file(filename, reg, - &list, file_info); + *count = disk_get_blocklist_from_file(filename, reg, + list, file_info); disk_free_info(file_info); - if (count == 0) + if (*count == 0) return -1; - count -= DIV_ROUND_UP(trailer, info->phy_block_size); + *count -= DIV_ROUND_UP(trailer, bis->info->phy_block_size); } /* Fill in component location */ - loc.addr = load_address; - loc.size = count * info->phy_block_size; + location->addr = load_address; + location->size = *count * bis->info->phy_block_size; /* Try to compact list */ - count = disk_compact_blocklist(list, count, info); - /* Write segment table */ - rc = add_segment_table(fd, list, count, &segment, info); - free(list); - if (rc == 0) { - create_component_entry(component, &segment, COMPONENT_TYPE_LOAD, - (component_data) load_address, info); - /* Return location if requested */ - if (location != NULL) - *location = loc; - } + *count = disk_compact_blocklist(*list, *count, bis->info); +write_segment_table: + assert(*list != NULL); + assert(*count != 0); + rc = add_segment_table(bis->fd, *list, *count, &segment, bis->info, + program_table_id); + if (rc == 0) + create_component_entry(component, &segment, + component_type_by_id(comp_id), + (component_data)load_address, + bis->info, program_table_id); return rc; } -static int -add_component_file(int fd, const char *filename, address_t load_address, - size_t trailer, void *component, int add_files, - struct disk_info *info, struct job_target_data *target, - struct component_loc *location) +static int add_component_file(struct install_set *bis, const char *filename, + address_t load_address, size_t trailer, + void *component, int add_files, + struct job_target_data *target, int comp_id, + int menu_idx, int program_table_id) { - return add_component_file_range(fd, filename, NULL, load_address, + return add_component_file_range(bis, filename, NULL, load_address, trailer, component, add_files, - info, target, location); + target, comp_id, menu_idx, + program_table_id); } -static int -add_component_buffer_align(int fd, void *buffer, size_t size, - component_data data, void *component, - struct disk_info *info, - struct component_loc *location, int type, - int align, off_t *offset) +static int add_component_buffer_align(struct install_set *bis, void *buffer, + size_t size, component_data data, + void *component, int align, + off_t *offset, int comp_id, int menu_idx, + int program_table_id) { - struct component_loc loc; + struct program_component *pc = get_component(bis, comp_id, menu_idx); + struct component_loc *location = &pc->loc; + disk_blockptr_t **list = &pc->list; + blocknum_t *count = &pc->count; disk_blockptr_t segment; - disk_blockptr_t* list; - blocknum_t count; int rc; + if (program_table_id) + /* skip the preparation work */ + goto write_segment_table; /* Write buffer */ - count = disk_write_block_buffer_align(fd, 0, buffer, size, &list, info, - align, offset); - if (count == 0) { + *count = disk_write_block_buffer_align(bis->fd, 0, buffer, size, list, + bis->info, align, offset); + if (*count == 0) { error_text("Could not write to bootmap file"); return -1; } - if (type == COMPONENT_TYPE_LOAD) { + if (component_type_by_id(comp_id) == COMPONENT_TYPE_LOAD) { /* Fill in component location */ - loc.addr = data.load_address; - loc.size = count * info->phy_block_size; + location->addr = data.load_address; + location->size = *count * bis->info->phy_block_size; } else { - loc.addr = 0; - loc.size = 0; + location->addr = 0; + location->size = 0; } /* Try to compact list */ - count = disk_compact_blocklist(list, count, info); - /* Write segment table */ - rc = add_segment_table(fd, list, count, &segment, info); - free(list); - if (rc == 0) { - create_component_entry(component, &segment, type, data, info); - /* Return location if requested */ - if (location != NULL) - *location = loc; - } + *count = disk_compact_blocklist(*list, *count, bis->info); +write_segment_table: + assert(*list != NULL); + assert(*count != 0); + + rc = add_segment_table(bis->fd, *list, *count, &segment, bis->info, + program_table_id); + if (rc == 0) + create_component_entry(component, &segment, + component_type_by_id(comp_id), + data, bis->info, program_table_id); return rc; } -static int -add_component_buffer(int fd, void *buffer, size_t size, component_data data, - void *component, struct disk_info *info, - struct component_loc *location, int type) +static int add_component_buffer(struct install_set *bis, void *buffer, + size_t size, component_data data, + void *component, int comp_id, int menu_idx, + int program_table_id) { - return add_component_buffer_align(fd, buffer, size, data, component, - info, location, type, - info->phy_block_size, NULL); + return add_component_buffer_align(bis, buffer, size, data, component, + bis->info->phy_block_size, NULL, + comp_id, menu_idx, program_table_id); } -static int -add_dummy_buffer(int fd, size_t size, address_t addr, void *component, - struct disk_info *info, struct component_loc *comp_loc) +static int add_dummy_buffer(struct install_set *bis, size_t size, + address_t addr, void *component, int comp_id, + int menu_idx, int program_table_id) { char *buffer; - int rc; + int rc = 0; buffer = misc_malloc(size); if (buffer == NULL) return -1; memset(buffer, 0, size); - rc = add_component_buffer(fd, buffer, size, - (component_data) (uint64_t) addr, - component, info, comp_loc, - COMPONENT_TYPE_LOAD); - if (rc) { - free(buffer); - return rc; - } + rc = add_component_buffer(bis, buffer, size, + (component_data)(uint64_t)addr, + component, comp_id, menu_idx, + program_table_id); free(buffer); - return 0; + return rc; } -static void -print_components(const char *name[], struct component_loc *loc, int num) +static void print_components(struct install_set *bis, int menu_idx) { const char *padding = "................"; int i; printf(" component address:\n"); /* Process all available components */ - for (i = 0; i < num; i++) { - if (loc[i].size == 0) + for (i = 0; i < NR_PROGRAM_COMPONENTS; i++) { + struct program_component *pc = get_component(bis, i, menu_idx); + + if (pc->loc.size == 0) continue; - printf(" %s%s: 0x%08llx-0x%08llx\n", name[i], - &padding[strlen(name[i])], - (unsigned long long) loc[i].addr, - (unsigned long long) (loc[i].addr + loc[i].size - 1)); + printf(" %s%s: 0x%08llx-0x%08llx\n", component_desc_by_id(i), + &padding[strlen(component_desc_by_id(i))], + (unsigned long long)pc->loc.addr, + (unsigned long long)(pc->loc.addr + pc->loc.size - 1)); } } @@ -513,18 +541,24 @@ check_remaining_filesize(size_t filesize, size_t signature_size, } } -static int add_ipl_program(int fd, char *filename, +static int is_last_table(struct install_set *bis, int table_id) +{ + assert(bis->nr_tables > 0 && bis->nr_tables <= NR_PROGRAM_TABLES); + + return table_id == bis->nr_tables - 1; +} + +static int add_ipl_program(struct install_set *bis, char *filename, bool add_envblk, struct job_envblk_data *envblk, struct job_ipl_data *ipl, disk_blockptr_t *program, int verbose, int add_files, component_header_type type, - struct disk_info* info, struct job_target_data* target, - int is_secure) + struct job_target_data *target, int is_secure, + int menu_idx, int program_table_id) { - struct component_loc comp_loc[10]; + int last_table = is_last_table(bis, program_table_id); struct signature_header sig_head; size_t ramdisk_size, image_size; size_t stage3_params_size; - const char *comp_name[11]; size_t signature_size; int offset; uint64_t flags = 0; @@ -532,16 +566,13 @@ static int add_ipl_program(int fd, char *filename, struct stat stats; off_t envblk_off; void *signature; - int comp_nr = 0; void *table; int rc; - memset(comp_loc, 0, sizeof(comp_loc)); memset(&sig_head, 0, sizeof(sig_head)); - table = misc_malloc(info->phy_block_size); + table = util_zalloc(bis->info->phy_block_size); if (table == NULL) return -1; - memset(table, 0, info->phy_block_size); /* Create component table */ offset = 0; /* Fill in component table header */ @@ -556,9 +587,8 @@ static int add_ipl_program(int fd, char *filename, stats.st_size = 0; if (ipl->common.ramdisk != NULL) { /* Add ramdisk */ - if (verbose) { + if (verbose && last_table) printf(" initial ramdisk...: %s\n", ipl->common.ramdisk); - } /* Get ramdisk file size */ if (stat(ipl->common.ramdisk, &stats)) { error_reason(strerror(errno)); @@ -569,35 +599,34 @@ static int add_ipl_program(int fd, char *filename, } } ramdisk_size = stats.st_size; - if (info->type == disk_type_scsi) { + if (bis->info->type == disk_type_scsi) { flags |= STAGE3_FLAG_SCSI; /* * Add dummy components for stage 3 heap and stack to block the * associated memory areas against firmware use. */ - rc = add_dummy_buffer(fd, STAGE3_HEAP_SIZE, STAGE3_HEAP_ADDRESS, - VOID_ADD(table, offset), info, - &comp_loc[comp_nr]); + rc = add_dummy_buffer(bis, STAGE3_HEAP_SIZE, + STAGE3_HEAP_ADDRESS, + VOID_ADD(table, offset), + COMPONENT_ID_HEAP_AREA, + menu_idx, program_table_id); if (rc) { error_text("Could not add stage3 HEAP dummy"); free(table); return rc; } - comp_name[comp_nr] = "heap area"; offset += sizeof(struct component_entry); - comp_nr++; - rc = add_dummy_buffer(fd, STAGE3_STACK_SIZE, + rc = add_dummy_buffer(bis, STAGE3_STACK_SIZE, STAGE3_STACK_ADDRESS, - VOID_ADD(table, offset), info, - &comp_loc[comp_nr]); + VOID_ADD(table, offset), + COMPONENT_ID_STACK_AREA, + menu_idx, program_table_id); if (rc) { error_text("Could not add stage3 STACK dummy"); free(table); return rc; } - comp_name[comp_nr] = "stack area"; offset += sizeof(struct component_entry); - comp_nr++; } if (ipl->is_kdump) flags |= STAGE3_FLAG_KDUMP; @@ -616,22 +645,20 @@ static int add_ipl_program(int fd, char *filename, if (signature_size && (is_secure == SECURE_BOOT_ENABLED || (is_secure == SECURE_BOOT_AUTO && secure_boot_supported))) { - if (verbose) + if (verbose && last_table) printf(" signature for.....: %s\n", ZIPL_STAGE3_PATH); - rc = add_component_buffer(fd, signature, sig_head.length, + rc = add_component_buffer(bis, signature, sig_head.length, (component_data)sig_head, - VOID_ADD(table, offset), info, - &comp_loc[comp_nr], - COMPONENT_TYPE_SIGNATURE); + VOID_ADD(table, offset), + COMPONENT_ID_LOADER_SIGNATURE, + menu_idx, program_table_id); if (rc) { error_text("Could not add stage3 signature"); free(table); return rc; } - comp_name[comp_nr] = "loader signature"; offset += sizeof(struct component_entry); - comp_nr++; free(signature); } else if (is_secure == SECURE_BOOT_ENABLED) { /* @@ -647,9 +674,10 @@ static int add_ipl_program(int fd, char *filename, } /* Add stage 3 loader to bootmap */ - rc = add_component_file(fd, ZIPL_STAGE3_PATH, STAGE3_LOAD_ADDRESS, + rc = add_component_file(bis, ZIPL_STAGE3_PATH, STAGE3_LOAD_ADDRESS, signature_size, VOID_ADD(table, offset), 1, - info, target, &comp_loc[comp_nr]); + target, COMPONENT_ID_LOADER, menu_idx, + program_table_id); if (rc) { error_text("Could not add internal loader file '%s'", ZIPL_STAGE3_PATH); @@ -657,8 +685,6 @@ static int add_ipl_program(int fd, char *filename, return rc; } offset += sizeof(struct component_entry); - comp_name[comp_nr] = "internal loader"; - comp_nr++; /* Add stage 3 parameter to bootmap */ rc = boot_get_stage3_parms(&stage3_params, &stage3_params_size, @@ -666,7 +692,7 @@ static int add_ipl_program(int fd, char *filename, ramdisk_size, ipl->is_kdump ? IMAGE_ENTRY_KDUMP : IMAGE_ENTRY, - (info->type == disk_type_scsi) ? 0 : 1, + (bis->info->type == disk_type_scsi) ? 0 : 1, flags, ipl->common.image_addr, image_size, ipl->envblk_addr, add_envblk ? envblk->size : 0); @@ -674,11 +700,12 @@ static int add_ipl_program(int fd, char *filename, free(table); return rc; } - rc = add_component_buffer(fd, stage3_params, stage3_params_size, + rc = add_component_buffer(bis, stage3_params, stage3_params_size, (component_data) (uint64_t) STAGE3_PARAMS_ADDRESS, - VOID_ADD(table, offset), info, - &comp_loc[comp_nr], COMPONENT_TYPE_LOAD); + VOID_ADD(table, offset), + COMPONENT_ID_PARAMETERS, + menu_idx, program_table_id); free(stage3_params); if (rc) { error_text("Could not add parameters"); @@ -686,35 +713,31 @@ static int add_ipl_program(int fd, char *filename, return -1; } offset += sizeof(struct component_entry); - comp_name[comp_nr] = "parameters"; - comp_nr++; /* Add kernel image */ - if (verbose) { + if (verbose && last_table) printf(" kernel image......: %s\n", ipl->common.image); - } + signature_size = extract_signature(ipl->common.image, &signature, &sig_head); if (signature_size && (is_secure == SECURE_BOOT_ENABLED || (is_secure == SECURE_BOOT_AUTO && secure_boot_supported))) { - if (verbose) + if (verbose && last_table) printf(" signature for.....: %s\n", ipl->common.image); - rc = add_component_buffer(fd, signature, sig_head.length, + rc = add_component_buffer(bis, signature, sig_head.length, (component_data)sig_head, - VOID_ADD(table, offset), info, - &comp_loc[comp_nr], - COMPONENT_TYPE_SIGNATURE); + VOID_ADD(table, offset), + COMPONENT_ID_IMAGE_SIGNATURE, + menu_idx, program_table_id); if (rc) { error_text("Could not add image signature"); free(table); return rc; } - comp_name[comp_nr] = "image signature"; offset += sizeof(struct component_entry); - comp_nr++; free(signature); - check_remaining_filesize(image_size, signature_size, info, + check_remaining_filesize(image_size, signature_size, bis->info, ipl->common.image); } else if (is_secure == SECURE_BOOT_ENABLED) { /* @@ -729,29 +752,27 @@ static int add_ipl_program(int fd, char *filename, return -1; } - rc = add_component_file(fd, ipl->common.image, ipl->common.image_addr, + rc = add_component_file(bis, ipl->common.image, ipl->common.image_addr, signature_size, VOID_ADD(table, offset), - add_files, info, target, &comp_loc[comp_nr]); + add_files, target, COMPONENT_ID_KERNEL_IMAGE, + menu_idx, program_table_id); if (rc) { error_text("Could not add image file '%s'", ipl->common.image); free(table); return rc; } offset += sizeof(struct component_entry); - comp_name[comp_nr] = "kernel image"; - comp_nr++; /* Add kernel parmline */ if (ipl->common.parmline != NULL) { - if (verbose) { + if (verbose && last_table) printf(" kernel parmline...: '%s'\n", ipl->common.parmline); - } - rc = add_component_buffer(fd, ipl->common.parmline, + rc = add_component_buffer(bis, ipl->common.parmline, strlen(ipl->common.parmline) + 1, - (component_data) ipl->common.parm_addr, + (component_data)ipl->common.parm_addr, VOID_ADD(table, offset), - info, &comp_loc[comp_nr], - COMPONENT_TYPE_LOAD); + COMPONENT_ID_PARMLINE, + menu_idx, program_table_id); if (rc) { error_text("Could not add parmline '%s'", ipl->common.parmline); @@ -759,8 +780,6 @@ static int add_ipl_program(int fd, char *filename, return -1; } offset += sizeof(struct component_entry); - comp_name[comp_nr] = "parmline"; - comp_nr++; } /* add ramdisk */ if (ipl->common.ramdisk != NULL) { @@ -770,33 +789,33 @@ static int add_ipl_program(int fd, char *filename, (is_secure == SECURE_BOOT_ENABLED || (is_secure == SECURE_BOOT_AUTO && secure_boot_supported))) { - if (verbose) { + if (verbose && last_table) { printf(" signature for.....: %s\n", ipl->common.ramdisk); } - rc = add_component_buffer(fd, signature, + rc = add_component_buffer(bis, signature, sig_head.length, (component_data)sig_head, - VOID_ADD(table, offset), info, - &comp_loc[comp_nr], - COMPONENT_TYPE_SIGNATURE); + VOID_ADD(table, offset), + COMPONENT_ID_RAMDISK_SIGNATURE, + menu_idx, program_table_id); if (rc) { error_text("Could not add ramdisk signature"); free(table); return rc; } - comp_name[comp_nr] = "ramdisk signature"; offset += sizeof(struct component_entry); - comp_nr++; free(signature); check_remaining_filesize(ramdisk_size, signature_size, - info, ipl->common.ramdisk); + bis->info, + ipl->common.ramdisk); } - rc = add_component_file(fd, ipl->common.ramdisk, - ipl->common.ramdisk_addr, signature_size, + rc = add_component_file(bis, ipl->common.ramdisk, + ipl->common.ramdisk_addr, + signature_size, VOID_ADD(table, offset), - add_files, info, target, - &comp_loc[comp_nr]); + add_files, target, COMPONENT_ID_RAMDISK, + menu_idx, program_table_id); if (rc) { error_text("Could not add ramdisk '%s'", ipl->common.ramdisk); @@ -804,43 +823,40 @@ static int add_ipl_program(int fd, char *filename, return -1; } offset += sizeof(struct component_entry); - comp_name[comp_nr] = "initial ramdisk"; - comp_nr++; } if (add_envblk == true) { /* * finally add environment block */ - rc = envblk_offset_get(fd, &envblk_off); + rc = envblk_offset_get(bis->fd, &envblk_off); if (rc) { free(table); return rc; } if (envblk_off == 0) { /* - * write with fs_block_size alignment to make sure that the - * logical environment block will get to single file system - * block + * write with fs_block_size alignment to make sure + * that the logical environment block will get to + * single file system block */ - rc = add_component_buffer_align(fd, + rc = add_component_buffer_align(bis, envblk->buf, envblk->size, (component_data)ipl->envblk_addr, VOID_ADD(table, offset), - info, &comp_loc[comp_nr], - COMPONENT_TYPE_LOAD, - info->fs_block_size, - &envblk_off); + bis->info->fs_block_size, + &envblk_off, COMPONENT_ID_ENVBLK, + menu_idx, program_table_id); if (rc) { error_text("Could not add environment block"); free(table); return rc; } - assert(envblk_off % info->fs_block_size == 0); + assert(envblk_off % bis->info->fs_block_size == 0); /* * store environment block location * in the bootmap header */ - rc = envblk_offset_set(fd, envblk_off); + rc = envblk_offset_set(bis->fd, envblk_off); if (rc) { error_text("Could not store environment block location"); free(table); @@ -851,11 +867,13 @@ static int add_ipl_program(int fd, char *filename, reg.offset = envblk_off; reg.len = envblk->size; - rc = add_component_file_range(fd, filename, ®, + rc = add_component_file_range(bis, filename, ®, ipl->envblk_addr, 0, VOID_ADD(table, offset), - 0, info, target, - &comp_loc[comp_nr]); + 0, target, + COMPONENT_ID_ENVBLK, + menu_idx, + program_table_id); if (rc) { error_text("Could not add environment block"); free(table); @@ -863,54 +881,51 @@ static int add_ipl_program(int fd, char *filename, } } offset += sizeof(struct component_entry); - comp_name[comp_nr] = "environment blk"; - comp_nr++; } - if (verbose) - print_components(comp_name, comp_loc, comp_nr); + if (verbose && last_table) + print_components(bis, menu_idx); /* Terminate component table */ create_component_entry(VOID_ADD(table, offset), NULL, COMPONENT_TYPE_EXECUTE, (component_data) (uint64_t) (STAGE3_ENTRY | PSW_LOAD), - info); + bis->info, program_table_id); /* Write component table */ - rc = disk_write_block_aligned(fd, table, info->phy_block_size, - program, info); + rc = disk_write_block_aligned(bis->fd, table, + bis->info->phy_block_size, + program, bis->info); free(table); return rc; } - -static int -add_segment_program(int fd, struct job_segment_data* segment, - disk_blockptr_t* program, int verbose, int add_files, - component_header_type type, struct disk_info* info, - struct job_target_data* target) +static int add_segment_program(struct install_set *bis, + struct job_segment_data *segment, + disk_blockptr_t *program, int verbose, + int add_files, component_header_type type, + struct job_target_data *target, + int program_table_id) { - const char *comp_name[1] = {"segment file"}; - struct component_loc comp_loc[1]; - void* table; + int last_table = is_last_table(bis, program_table_id); + void *table; int offset; int rc; - memset(comp_loc, 0, sizeof(comp_loc)); - table = misc_malloc(info->phy_block_size); + table = util_zalloc(bis->info->phy_block_size); if (table == NULL) return -1; - memset(table, 0, info->phy_block_size); /* Create component table */ offset = 0; /* Fill in component table header */ create_component_header(VOID_ADD(table, offset), type); offset += sizeof(struct component_header); /* Add segment file */ - if (verbose) { + if (verbose && last_table) printf(" segment file......: %s\n", segment->segment); - } - rc = add_component_file(fd, segment->segment, segment->segment_addr, 0, - VOID_ADD(table, offset), add_files, info, - target, &comp_loc[0]); + + rc = add_component_file(bis, segment->segment, segment->segment_addr, 0, + VOID_ADD(table, offset), add_files, target, + COMPONENT_ID_SEGMENT_FILE, 0 /* menu_idx */, + program_table_id); if (rc) { error_text("Could not add segment file '%s'", segment->segment); @@ -919,16 +934,17 @@ add_segment_program(int fd, struct job_segment_data* segment, } offset += sizeof(struct component_entry); /* Print component addresses */ - if (verbose) - print_components(comp_name, comp_loc, 1); + if (verbose && last_table) + print_components(bis, 0 /* menu_idx */); /* Terminate component table */ create_component_entry(VOID_ADD(table, offset), NULL, COMPONENT_TYPE_EXECUTE, - (component_data)(uint64_t) - PSW_DISABLED_WAIT, info); + (component_data)(uint64_t)PSW_DISABLED_WAIT, + bis->info, program_table_id); /* Write component table */ - rc = disk_write_block_aligned(fd, table, info->phy_block_size, - program, info); + rc = disk_write_block_aligned(bis->fd, table, + bis->info->phy_block_size, + program, bis->info); free(table); return rc; } @@ -994,12 +1010,11 @@ get_dump_parmline(char *partition, char *parameters, return 0; } - -static int -add_dump_program(int fd, struct job_dump_data* dump, - disk_blockptr_t* program, int verbose, - component_header_type type, - struct disk_info* info, struct job_target_data* target) +static int add_dump_program(struct install_set *bis, struct job_dump_data *dump, + disk_blockptr_t *program, int verbose, + component_header_type type, + struct job_target_data *target, + int program_table_id) { struct job_ipl_data ipl; int rc; @@ -1010,28 +1025,31 @@ add_dump_program(int fd, struct job_dump_data* dump, /* Get file system dump parmline */ rc = get_dump_parmline(dump->device, dump->common.parmline, - info, target, &ipl.common.parmline); + bis->info, target, &ipl.common.parmline); if (rc) return rc; ipl.common.parm_addr = dump->common.parm_addr; - return add_ipl_program(fd, NULL, false, NULL, &ipl, program, verbose, 1, - type, info, target, SECURE_BOOT_DISABLED); + return add_ipl_program(bis, NULL, false, NULL, &ipl, program, + verbose, 1, type, target, SECURE_BOOT_DISABLED, + 0 /* menu_idx */, program_table_id); } -/* Build a program table from job data and set pointer to program table - * block upon success. */ -static int -build_program_table(int fd, char *filename, struct job_data *job, - disk_blockptr_t *pointer, struct disk_info *info) +/** + * Build a program table from job data and set pointer to program table + * block upon success + */ +static int build_program_table(struct job_data *job, + struct install_set *bis, int program_table_id) { - disk_blockptr_t* table; + int last_table = is_last_table(bis, program_table_id); int entries, component_header; + disk_blockptr_t *table; int is_secure; int i; int rc; - entries = get_program_table_size(info); + entries = get_program_table_size(bis->info); /* Get some memory for the program table */ table = (disk_blockptr_t *) misc_malloc(sizeof(disk_blockptr_t) * entries); @@ -1042,84 +1060,96 @@ build_program_table(int fd, char *filename, struct job_data *job, /* Add programs */ switch (job->id) { case job_ipl: - if (job->command_line) - printf("Adding IPL section\n"); - else - printf("Adding IPL section '%s' (default)\n", - job->name); + if (last_table) { + if (job->command_line) + printf("Adding IPL section\n"); + else + printf("Adding IPL section '%s' (default)\n", + job->name); + } if (job->data.ipl.is_kdump) component_header = COMPONENT_HEADER_DUMP; else component_header = COMPONENT_HEADER_IPL; - rc = add_ipl_program(fd, filename, + rc = add_ipl_program(bis, bis->filename, true, &job->envblk, &job->data.ipl, &table[0], verbose || job->command_line, job->add_files, component_header, - info, &job->target, job->is_secure); + &job->target, job->is_secure, 0, + program_table_id); break; case job_segment: - if (job->command_line) - printf("Adding segment load section\n"); - else - printf("Adding segment load section '%s' (default)\n", - job->name); - rc = add_segment_program(fd, &job->data.segment, &table[0], + if (last_table) { + if (job->command_line) + printf("Adding segment load section\n"); + else + printf("Adding segment load section '%s' (default)\n", + job->name); + } + rc = add_segment_program(bis, &job->data.segment, &table[0], verbose || job->command_line, job->add_files, COMPONENT_HEADER_IPL, - info, &job->target); + &job->target, program_table_id); break; case job_dump_partition: /* Only useful for a partition dump that uses a dump kernel*/ - if (job->command_line) - printf("Adding dump section\n"); - else - printf("Adding dump section '%s' (default)\n", - job->name); - rc = add_dump_program(fd, &job->data.dump, &table[0], - verbose || job->command_line, - COMPONENT_HEADER_DUMP, - info, &job->target); + if (last_table) { + if (job->command_line) + printf("Adding dump section\n"); + else + printf("Adding dump section '%s' (default)\n", + job->name); + } + rc = add_dump_program(bis, &job->data.dump, &table[0], + verbose || job->command_line, + COMPONENT_HEADER_DUMP, &job->target, + program_table_id); break; case job_menu: - printf("Building menu '%s'\n", job->name); + if (last_table) + printf("Building menu '%s'\n", job->name); rc = 0; for (i=0; i < job->data.menu.num; i++) { switch (job->data.menu.entry[i].id) { case job_ipl: - if (job->data.menu.entry[i].data.ipl.common.ignore) { + if (last_table && + job->data.menu.entry[i].data.ipl.common.ignore) { printf("Skipping #%d: IPL section '%s' (missing files)\n", job->data.menu.entry[i].pos, job->data.menu.entry[i].name); break; } - printf("Adding #%d: IPL section '%s'%s", - job->data.menu.entry[i].pos, - job->data.menu.entry[i].name, - (job->data.menu.entry[i].pos == - job->data.menu.default_pos) ? - " (default)": ""); + if (last_table) + printf("Adding #%d: IPL section '%s'%s", + job->data.menu.entry[i].pos, + job->data.menu.entry[i].name, + (job->data.menu.entry[i].pos == + job->data.menu.default_pos) ? + " (default)" : ""); if (job->data.menu.entry[i].data.ipl.is_kdump) { component_header = COMPONENT_HEADER_DUMP; - printf(" (kdump)\n"); + if (last_table) + printf(" (kdump)\n"); } else { component_header = COMPONENT_HEADER_IPL; - printf("\n"); + if (last_table) + printf("\n"); } if (job->is_secure != SECURE_BOOT_UNDEFINED) is_secure = job->is_secure; else is_secure = job->data.menu.entry[i].is_secure; - rc = add_ipl_program(fd, filename, + rc = add_ipl_program(bis, bis->filename, true, &job->envblk, &job->data.menu.entry[i].data.ipl, &table[job->data.menu.entry[i].pos], verbose || job->command_line, - job->add_files, component_header, - info, &job->target, - is_secure); + job->add_files, component_header, + &job->target, is_secure, i, + program_table_id); break; case job_print_usage: case job_print_version: @@ -1147,12 +1177,17 @@ build_program_table(int fd, char *filename, struct job_data *job, rc = -1; break; } - if (job->envblk.buf && verbose) + if (job->envblk.buf && verbose && last_table) envblk_print(job->envblk.buf, job->envblk.size); if (rc == 0) { + disk_blockptr_t *pointer; + /* Add program table block */ - rc = add_program_table(fd, table, entries, pointer, info); + pointer = &bis->tables[program_table_id].table; + rc = add_program_table(bis->fd, table, entries, + pointer, bis->info, + program_table_id); } free(table); return rc; @@ -1178,57 +1213,147 @@ write_empty_block(int fd, disk_blockptr_t* block, struct disk_info* info) } -static int -bootmap_install_stages(struct job_data *job, struct disk_info *info, int fd, - disk_blockptr_t **stage1b_list, blocknum_t *stage1b_count) +static int install_stages_dasd_fba(int fd, char *filename, + struct job_data *job, + struct disk_info *info, + disk_blockptr_t **stage1b_list, + blocknum_t *stage1b_count, + int program_table_id) { disk_blockptr_t *stage2_list; blocknum_t stage2_count; size_t stage2_size; void *stage2_data; - int rc; - switch (info->type) { - case disk_type_fba: + switch (program_table_id) { + case PROGRAM_TABLE_0: + /* + * This program table is used for CCW-type IPL (see comments + * for install_bootloader()). + * Add stage2 loader + */ if (boot_get_fba_stage2(&stage2_data, &stage2_size, job)) return -1; stage2_count = disk_write_block_buffer(fd, 0, stage2_data, - stage2_size, &stage2_list, - info); + stage2_size, + &stage2_list, info); free(stage2_data); - if (stage2_count == 0) + if (stage2_count == 0) { + error_text("Could not write to file '%s'", filename); + return -1; + } + if (install_fba_stage1b(fd, stage1b_list, stage1b_count, + stage2_list, stage2_count, info)) return -1; - rc = install_fba_stage1b(fd, stage1b_list, stage1b_count, - stage2_list, stage2_count, info); free(stage2_list); - if (rc) - return -1; break; - case disk_type_eckd_ldl: - case disk_type_eckd_cdl: - if (boot_get_eckd_stage2(&stage2_data, &stage2_size, job)) - return -1; - stage2_count = disk_write_block_buffer(fd, 0, stage2_data, - stage2_size, &stage2_list, - info); - free(stage2_data); - if (stage2_count == 0) - return -1; - rc = install_eckd_stage1b(fd, stage1b_list, stage1b_count, - stage2_list, stage2_count, info); - free(stage2_list); - if (rc) - return -1; - break; - case disk_type_scsi: - case disk_type_diag: + case PROGRAM_TABLE_1: + /* + * This program table is not used when booting from DASD FBA + * (see comments for install_bootloader() + */ *stage1b_list = NULL; *stage1b_count = 0; break; + default: + assert(0); } return 0; } +static int install_stages_eckd_dasd(int fd, char *filename, + struct job_data *job, + struct disk_info *info, + disk_blockptr_t *program_table, + disk_blockptr_t **stage1b_list, + blocknum_t *stage1b_count, + int program_table_id) +{ + disk_blockptr_t *stage2b_list; + blocknum_t stage2b_count; + size_t stage2b_size; + void *stage2b_data; + + switch (program_table_id) { + case PROGRAM_TABLE_0: + /* + * This program table is used for CCW-type IPL. + * Add stage2 loader + */ + if (boot_get_eckd_stage2(&stage2b_data, &stage2b_size, job)) + return -1; + stage2b_count = disk_write_block_buffer(fd, 0, stage2b_data, + stage2b_size, + &stage2b_list, + info); + free(stage2b_data); + if (stage2b_count == 0) { + error_text("Could not write to file '%s'", filename); + return -1; + } + if (install_eckd_stage1b(fd, stage1b_list, stage1b_count, + stage2b_list, stage2b_count, info)) + return -1; + free(stage2b_list); + break; + case PROGRAM_TABLE_1: + /* + * This program table is used for List-Directed IPL, + * which doesn't invoke stage2 loader. + * Link the program table with the boot record, that + * will be installed later by install_bootloader() + */ + if (boot_get_eckd_ld_ipl_br(&stage2b_data, &stage2b_size, + program_table, info)) + return -1; + stage2b_count = disk_write_block_buffer(fd, 0, stage2b_data, + stage2b_size, + stage1b_list, + info); + free(stage2b_data); + if (stage2b_count == 0) { + error_text("Could not write to file '%s'", filename); + return -1; + } + *stage1b_count = stage2b_count; + break; + default: + assert(0); + } + return 0; +} + +static int bootmap_install_stages(struct job_data *job, struct install_set *bis, + int program_table_id) +{ + struct program_table *pt = &bis->tables[program_table_id]; + int rc = 0; + + switch (bis->info->type) { + case disk_type_fba: + rc = install_stages_dasd_fba(bis->fd, bis->filename, job, + bis->info, + &pt->stage1b_list, + &pt->stage1b_count, + program_table_id); + break; + case disk_type_eckd_ldl: + case disk_type_eckd_cdl: + rc = install_stages_eckd_dasd(bis->fd, bis->filename, job, + bis->info, + &pt->table, + &pt->stage1b_list, + &pt->stage1b_count, + program_table_id); + break; + case disk_type_scsi: + case disk_type_diag: + pt->stage1b_list = NULL; + pt->stage1b_count = 0; + break; + } + return rc; +} static int bootmap_write_scsi_superblock(int fd, struct disk_info *info, @@ -1287,6 +1412,20 @@ estimate_scsi_dump_size(struct job_data *job, struct disk_info *info, ulong *dum return 0; } +/** + * Check that disk is appropriate for the JOB + */ +static int disk_is_appropriate(const struct job_data *job, + const struct disk_info *info) +{ + if (job->is_secure == SECURE_BOOT_ENABLED && + info->type != disk_type_scsi && + info->type != disk_type_eckd_cdl) { + error_reason("Secure boot forced for improper disk type"); + return 0; + } + return 1; +} static int check_dump_device(const struct job_data *job, const struct disk_info *info, @@ -1300,12 +1439,9 @@ check_dump_device(const struct job_data *job, const struct disk_info *info, disk_get_type_name(info->type)); return -1; } - /* Check if secure boot was enabled only for SCSI */ - if (job->is_secure == SECURE_BOOT_ENABLED && - info->type != disk_type_scsi) { - error_reason("Secure boot forced for non-SCSI disk type"); + if (!disk_is_appropriate(job, info)) return -1; - } + rc = util_part_search(device, info->geo.start, info->phy_blocks, info->phy_block_size, &part_ext); if (rc <= 0 || part_ext) { @@ -1321,218 +1457,221 @@ check_dump_device(const struct job_data *job, const struct disk_info *info, return 0; } - -static int -bootmap_create_device(struct job_data *job, disk_blockptr_t *program_table, - disk_blockptr_t *scsi_dump_sb_blockptr, - disk_blockptr_t **stage1b_list, blocknum_t *stage1b_count, - char **new_device, struct disk_info **new_info) +/** + * Set actual number of "similar" program tables to be installed + */ +static void set_nr_tables(struct job_data *job, struct install_set *bis) { - char *device, *filename; - struct disk_info *info; - ulong unused_size; - int fd; + assert(bis->nr_tables == 0); + if (bis->info->type == disk_type_eckd_cdl && + (job->id == job_ipl || job->id == job_menu)) + bis->nr_tables = NR_PROGRAM_TABLES; + else + bis->nr_tables = 1; +} + +/** + * Prepare resources to build a program table + */ +static int prepare_build_program_table_device(struct job_data *job, + struct install_set *bis, + int program_table_id) +{ + ulong unused_size; + + if (program_table_id) + /* skip the preparation work */ + return 0; /* Get full path of bootmap file */ if (!dry_run) { - filename = misc_strdup(job->data.dump.device); - if (filename == NULL) + bis->filename = misc_strdup(job->data.dump.device); + if (!bis->filename) + return -1; + bis->fd = misc_open_exclusive(bis->filename); + if (bis->fd == -1) { + error_text("Could not open file '%s'", bis->filename); return -1; - fd = misc_open_exclusive(filename); - if (fd == -1) { - error_text("Could not open file '%s'", filename); - goto out_free_filename; } - } else { - filename = misc_make_path(job->target.bootmap_dir, - BOOTMAP_TEMPLATE_FILENAME); - if (filename == NULL) + bis->filename = misc_make_path(job->target.bootmap_dir, + BOOTMAP_TEMPLATE_FILENAME); + if (!bis->filename) return -1; /* Create temporary bootmap file */ - fd = mkstemp(filename); - if (fd == -1) { + bis->fd = mkstemp(bis->filename); + if (bis->fd == -1) { error_reason(strerror(errno)); - error_text("Could not create file '%s':", filename); - goto out_free_filename; + error_text("Could not create file '%s':", + bis->filename); + return -1; } } /* Retrieve target device information */ - if (disk_get_info(filename, &job->target, &info)) - goto out_close_fd; + if (disk_get_info(bis->filename, &job->target, &bis->info)) + return -1; + if (verbose) { printf("Target device information\n"); - disk_print_info(info); + disk_print_info(bis->info); } - if (misc_temp_dev(info->device, 1, &device)) - goto out_disk_free_info; - if (check_dump_device(job, info, device)) - goto out_misc_free_temp_dev; + if (misc_temp_dev(bis->info->device, 1, &bis->device)) + return -1; + if (check_dump_device(job, bis->info, bis->device)) + return -1; printf("Building bootmap directly on partition '%s'%s\n", - filename, + bis->filename, job->add_files ? " (files will be added to partition)" : ""); /* For partition dump set raw partition offset to expected size before end of disk */ - if (estimate_scsi_dump_size(job, info, &unused_size)) - goto out_misc_free_temp_dev; - if (lseek(fd, unused_size, SEEK_SET) < 0) - goto out_misc_free_temp_dev; + if (estimate_scsi_dump_size(job, bis->info, &unused_size)) + return -1; + if (lseek(bis->fd, unused_size, SEEK_SET) < 0) + return -1; /* Initialize bootmap header */ - if (bootmap_header_init(fd)) { - error_text("Could not init bootmap header at '%s'", filename); - goto out_misc_free_temp_dev; + if (bootmap_header_init(bis->fd)) { + error_text("Could not init bootmap header at '%s'", + bis->filename); + return -1; } /* Write empty block to be read in place of holes in files */ - if (write_empty_block(fd, &empty_block, info)) { - error_text("Could not write to file '%s'", filename); - goto out_misc_free_temp_dev; + if (write_empty_block(bis->fd, &empty_block, bis->info)) { + error_text("Could not write to file '%s'", + bis->filename); + return -1; } - /* Build program table */ - if (build_program_table(fd, filename, job, program_table, info)) - goto out_misc_free_temp_dev; - if (bootmap_write_scsi_superblock(fd, info, scsi_dump_sb_blockptr, + if (bootmap_write_scsi_superblock(bis->fd, bis->info, + &bis->scsi_dump_sb_blockptr, unused_size)) { error_text("Could not write SCSI superblock to file '%s'", - filename); - goto out_misc_free_temp_dev; + bis->filename); + return -1; } - /* Install stage 2 loader to bootmap if necessary */ - if (bootmap_install_stages(job, info, fd, stage1b_list, stage1b_count)) { - error_text("Could not install loader stages to bootmap"); - goto out_misc_free_temp_dev; - } - if (dry_run) - misc_free_temp_file(filename); - *new_device = device; - *new_info = info; - close(fd); - free(filename); + set_nr_tables(job, bis); return 0; - -out_misc_free_temp_dev: - misc_free_temp_dev(device); -out_disk_free_info: - disk_free_info(info); -out_close_fd: - close(fd); -out_free_filename: - free(filename); - return -1; } - -static int -bootmap_create_file(struct job_data *job, char *bootmap_dir, - disk_blockptr_t *program_table, - disk_blockptr_t *scsi_dump_sb_blockptr, - disk_blockptr_t **stage1b_list, blocknum_t *stage1b_count, - char **new_device, struct disk_info **new_info) +static int bootmap_create_device(struct job_data *job, struct install_set *bis, + int program_table_id) { - char *device, *filename, *mapname; - struct disk_info *info; - int fd; - - /* Get full path of bootmap file */ - filename = misc_make_path(bootmap_dir, BOOTMAP_TEMPLATE_FILENAME); - if (filename == NULL) + if (prepare_build_program_table_device(job, bis, program_table_id)) return -1; + if (build_program_table(job, bis, program_table_id)) + return -1; + /* Install stage 2 loader to bootmap if necessary */ + if (bootmap_install_stages(job, bis, program_table_id)) { + error_text("Could not install loader stages to bootmap"); + return -1; + } + return 0; +} + +/** + * Prepare resources to build a program table + */ +static int prepare_build_program_table_file(struct job_data *job, + char *bootmap_dir, + struct install_set *bis, + int program_table_id) +{ + if (program_table_id) + /* skip the preparation work */ + return 0; /* Create temporary bootmap file */ - fd = mkstemp(filename); - if (fd == -1) { + bis->filename = misc_make_path(bootmap_dir, BOOTMAP_TEMPLATE_FILENAME); + if (!bis->filename) + return -1; + bis->fd = mkstemp(bis->filename); + if (bis->fd == -1) { error_reason(strerror(errno)); - error_text("Could not create file '%s':", filename); - goto out_free_filename; + error_text("Could not create file '%s':", bis->filename); + return -1; } /* Retrieve target device information. Note that we have to * call disk_get_info_from_file() to also get the file system * block size. */ - if (disk_get_info_from_file(filename, &job->target, &info)) - goto out_close_fd; + if (disk_get_info_from_file(bis->filename, &job->target, &bis->info)) + return -1; /* Check for supported disk and driver types */ - if ((info->source == source_auto) && (info->type == disk_type_diag)) { + if (bis->info->source == source_auto && + bis->info->type == disk_type_diag) { error_reason("Unsupported disk type (%s)", - disk_get_type_name(info->type)); - goto out_disk_free_info; - } - /* Check if secure boot was enabled only for SCSI */ - if (job->is_secure == SECURE_BOOT_ENABLED && - info->type != disk_type_scsi) { - error_reason("Secure boot forced for non-SCSI disk type"); - goto out_disk_free_info; + disk_get_type_name(bis->info->type)); + return -1; } + if (!disk_is_appropriate(job, bis->info)) + return -1; if (verbose) { printf("Target device information\n"); - disk_print_info(info); + disk_print_info(bis->info); } - if (misc_temp_dev(info->device, 1, &device)) - goto out_disk_free_info; + if (misc_temp_dev(bis->info->device, 1, &bis->device)) + return -1; /* Check configuration number limits */ if (job->id == job_menu) { - if (check_menu_positions(&job->data.menu, job->name, info)) - goto out_misc_free_temp_dev; + if (check_menu_positions(&job->data.menu, job->name, + bis->info)) + return -1; } - printf("Building bootmap in '%s'%s\n", bootmap_dir, job->add_files ? " (files will be added to bootmap file)" : ""); - /* Initialize bootmap header */ - if (bootmap_header_init(fd)) { - error_text("Could not init bootmap header at '%s'", filename); - goto out_misc_free_temp_dev; + if (bootmap_header_init(bis->fd)) { + error_text("Could not init bootmap header at '%s'", + bis->filename); + return -1; } /* Write empty block to be read in place of holes in files */ - if (write_empty_block(fd, &empty_block, info)) { - error_text("Could not write to file '%s'", filename); - goto out_misc_free_temp_dev; + if (write_empty_block(bis->fd, &empty_block, bis->info)) { + error_text("Could not write to file '%s'", bis->filename); + return -1; } - /* Build program table */ - if (build_program_table(fd, filename, job, program_table, info)) - goto out_misc_free_temp_dev; - - scsi_dump_sb_blockptr->linear.block = 0; - - /* Install stage 2 loader to bootmap if necessary */ - if (bootmap_install_stages(job, info, fd, stage1b_list, stage1b_count)) { - error_text("Could not install loader stages to file '%s'", - filename); - goto out_misc_free_temp_dev; - } - if (dry_run) { - misc_free_temp_file(filename); - } else { - /* Rename to final bootmap name */ - mapname = misc_make_path(bootmap_dir, BOOTMAP_FILENAME); - if (mapname == NULL) - goto out_misc_free_temp_dev; - if (rename(filename, mapname)) { - error_reason(strerror(errno)); - error_text("Could not overwrite file '%s':", mapname); - free(mapname); - goto out_misc_free_temp_dev; - } - free(mapname); - } - *new_device = device; - *new_info = info; - close(fd); - free(filename); + set_nr_tables(job, bis); return 0; - -out_misc_free_temp_dev: - misc_free_temp_dev(device); -out_disk_free_info: - disk_free_info(info); -out_close_fd: - close(fd); - misc_free_temp_file(filename); -out_free_filename: - free(filename); - return -1; } +/** + * Rename to final bootmap name + */ +static int finalize_create_file(char *bootmap_dir, struct install_set *bis) +{ + char *final_name; + + final_name = misc_make_path(bootmap_dir, BOOTMAP_FILENAME); + if (!final_name) + return -1; + if (rename(bis->filename, final_name)) { + error_reason(strerror(errno)); + error_text("Could not rename '%s' to '%s'", + bis->filename, final_name); + free(final_name); + return -1; + } + free(final_name); + return 0; +} + +static int bootmap_create_file(struct job_data *job, char *bootmap_dir, + struct install_set *bis, int program_table_id) +{ + if (prepare_build_program_table_file(job, bootmap_dir, bis, + program_table_id)) + return -1; + if (build_program_table(job, bis, program_table_id)) + return -1; + /* Install stage 2 loader to bootmap if necessary */ + if (bootmap_install_stages(job, bis, program_table_id)) { + error_text("Could not install loader stages to file '%s'", + bis->filename); + return -1; + } + if (!dry_run && is_last_table(bis, program_table_id)) + return finalize_create_file(bootmap_dir, bis); + return 0; +} static int ngdump_create_meta(const char *path) @@ -1568,30 +1707,27 @@ ngdump_create_meta(const char *path) return 0; } - -static int -bootmap_create_device_ngdump(struct job_data *job, disk_blockptr_t *program_table, - disk_blockptr_t *scsi_dump_sb_blockptr, - disk_blockptr_t **stage1b_list, blocknum_t *stage1b_count, - char **new_device, struct disk_info **new_info) +static int bootmap_create_device_ngdump(struct job_data *job, + struct install_set *bis, + int program_table_id) { char mount_point[] = "/tmp/zipl-dump-mount-point-XXXXXX"; struct disk_info *info; - char *device; int rc; + assert(program_table_id == 0); /* Retrieve target device information */ if (disk_get_info(job->data.dump.device, &job->target, &info)) return -1; - if (misc_temp_dev(info->device, 1, &device)) - goto out_disk_free_info; - if (check_dump_device(job, info, device)) - goto out_misc_free_temp_dev; + if (misc_temp_dev(info->device, 1, &bis->device)) + return -1; + if (check_dump_device(job, info, bis->device)) + return -1; /* Create a mount point directory */ if (mkdtemp(mount_point) == NULL) { error_reason(strerror(errno)); error_text("Could not create mount point '%s'", mount_point); - goto out_misc_free_temp_dev; + return -1; } if (!dry_run) { char *cmd = NULL; @@ -1619,56 +1755,102 @@ bootmap_create_device_ngdump(struct job_data *job, disk_blockptr_t *program_tabl job->data.dump.device); goto out_rmdir; } - if (bootmap_create_file(job, mount_point, program_table, - scsi_dump_sb_blockptr, - stage1b_list, stage1b_count, - new_device, new_info)) + if (bootmap_create_file(job, mount_point, bis, program_table_id)) goto out_umount; if (ngdump_create_meta(mount_point)) goto out_umount; /* Cleanup */ umount(mount_point); rmdir(mount_point); - misc_free_temp_dev(device); - disk_free_info(info); return 0; out_umount: umount(mount_point); out_rmdir: rmdir(mount_point); -out_misc_free_temp_dev: - misc_free_temp_dev(device); -out_disk_free_info: - disk_free_info(info); return -1; } -int -bootmap_create(struct job_data *job, disk_blockptr_t *program_table, - disk_blockptr_t *scsi_dump_sb_blockptr, - disk_blockptr_t **stage1b_list, blocknum_t *stage1b_count, - char **new_device, struct disk_info **new_info) +static int +bootmap_create(struct job_data *job, struct install_set *bis, + int program_table_id) { - secure_boot_supported = check_secure_boot_support(); - if (job->id == job_dump_partition) { if (is_ngdump_enabled(job->data.dump.device, &job->target)) - return bootmap_create_device_ngdump(job, program_table, - scsi_dump_sb_blockptr, - stage1b_list, - stage1b_count, - new_device, new_info); + return bootmap_create_device_ngdump(job, bis, + program_table_id); else - return bootmap_create_device(job, program_table, - scsi_dump_sb_blockptr, - stage1b_list, stage1b_count, - new_device, new_info); + return bootmap_create_device(job, bis, + program_table_id); } else return bootmap_create_file(job, job->target.bootmap_dir, - program_table, - scsi_dump_sb_blockptr, - stage1b_list, stage1b_count, - new_device, new_info); + bis, program_table_id); +} + +/** + * Initialize Bootloader Installation Set + */ +static int init_bis(struct job_data *job, struct install_set *bis) +{ + int i; + + memset(bis, 0, sizeof(*bis)); + bis->nr_menu_entries = 1; + if (job->id == job_menu) + bis->nr_menu_entries = job->data.menu.num; + /* + * allocate "matrix" of program components + */ + for (i = 0; i < NR_PROGRAM_COMPONENTS; i++) { + bis->components[i] = + util_zalloc(sizeof(struct program_component) * + bis->nr_menu_entries); + if (!bis->components[i]) + return -1; + } + return 0; +} + +/** + * Prapare a Bootloader Installation Set BIS based on one, or two + * "similar" program tables, depinding on job ID and disk type (see + * comments For install_bootloader()) + */ +int prepare_bootloader(struct job_data *job, struct install_set *bis) +{ + int i; + int rc; + + secure_boot_supported = check_secure_boot_support(); + + rc = init_bis(job, bis); + if (rc) + return rc; + for (i = 0;; i++) { + rc = bootmap_create(job, bis, i); + if (rc || is_last_table(bis, i)) + break; + } + return rc; +} + +void free_bootloader(struct install_set *bis) +{ + int i, j; + + for (i = 0; i < NR_PROGRAM_TABLES; i++) + free(bis->tables[i].stage1b_list); + for (i = 0; i < NR_PROGRAM_COMPONENTS; i++) { + for (j = 0; j < bis->nr_menu_entries; j++) + free(get_component(bis, i, j)->list); + free(bis->components[i]); + } + if (bis->fd > 0) + close(bis->fd); + if (dry_run) + misc_free_temp_file(bis->filename); + free(bis->filename); + misc_free_temp_dev(bis->device); + disk_free_info(bis->info); } diff --git a/zipl/src/disk.c b/zipl/src/disk.c index 144f3b94..d57050a5 100644 --- a/zipl/src/disk.c +++ b/zipl/src/disk.c @@ -28,6 +28,7 @@ #include "lib/util_proc.h" #include "lib/util_sys.h" +#include "lib/util_libc.h" #include "disk.h" #include "error.h" #include "install.h" @@ -755,7 +756,6 @@ disk_write_block_buffer_align(int fd, int fd_is_basedisk, const void *buffer, size_t bytecount, disk_blockptr_t **blocklist, struct disk_info *info, int align, off_t *offset) { - disk_blockptr_t* list; blocknum_t count; blocknum_t i; size_t written; @@ -764,13 +764,12 @@ disk_write_block_buffer_align(int fd, int fd_is_basedisk, const void *buffer, int rc; count = (bytecount + info->phy_block_size - 1) / info->phy_block_size; - list = (disk_blockptr_t *) misc_malloc(sizeof(disk_blockptr_t) * - count); - if (list == NULL) { + *blocklist = (disk_blockptr_t *)util_zalloc(sizeof(disk_blockptr_t) * + count); + if (*blocklist == NULL) { close(fd); return 0; } - memset((void *) list, 0, sizeof(disk_blockptr_t) * count); /* Build list */ for (i=0, written=0; i < count; i++, written += chunk_size) { chunk_size = bytecount - written; @@ -778,17 +777,15 @@ disk_write_block_buffer_align(int fd, int fd_is_basedisk, const void *buffer, chunk_size = info->phy_block_size; rc = disk_write_block_aligned_base(fd, fd_is_basedisk, VOID_ADD(buffer, written), - chunk_size, &list[i], info, + chunk_size, &(*blocklist)[i], + info, i == 0 ? align : info->phy_block_size, &pos); - if (rc) { - free(list); + if (rc) return 0; - } if (offset != NULL && i == 0) *offset = pos; } - *blocklist = list; return count; } @@ -830,6 +827,21 @@ disk_get_type_name(disk_type_t type) } } +/* Return IPL types supported for a given disk TYPE */ +char *disk_get_ipl_type(disk_type_t type) +{ + switch (type) { + case disk_type_scsi: + return "LD-"; + case disk_type_fba: + case disk_type_eckd_ldl: + return "CCW-"; + case disk_type_eckd_cdl: + return "CCW- and LD-"; + default: + return ""; + } +} /* Return non-zero for ECKD large volumes. */ int @@ -1050,7 +1062,6 @@ disk_get_blocklist_from_file(const char *filename, struct file_range *reg, disk_blockptr_t **blocklist, struct disk_info* info) { - disk_blockptr_t* list; struct stat stats; int fd; off_t off; @@ -1094,24 +1105,21 @@ disk_get_blocklist_from_file(const char *filename, struct file_range *reg, blk_count = ((blocknum_t) count + info->phy_block_size - 1) / info->phy_block_size; - list = (disk_blockptr_t *) misc_malloc(sizeof(disk_blockptr_t) * - blk_count); - if (list == NULL) { + *blocklist = (disk_blockptr_t *)util_zalloc(sizeof(disk_blockptr_t) * + blk_count); + if (*blocklist == NULL) { close(fd); return 0; } - memset((void *) list, 0, sizeof(disk_blockptr_t) * blk_count); /* Build list */ for (i = 0; i < blk_count; i++) { if (disk_get_blocknum(fd, 0, blk_off + i, &blocknum, info)) { - free(list); close(fd); return 0; } - disk_blockptr_from_blocknum(&list[i], blocknum, info); + disk_blockptr_from_blocknum(&(*blocklist)[i], blocknum, info); } close(fd); - *blocklist = list; return blk_count; } diff --git a/zipl/src/install.c b/zipl/src/install.c index d8404c55..4d146278 100644 --- a/zipl/src/install.c +++ b/zipl/src/install.c @@ -25,6 +25,7 @@ #include "lib/zt_common.h" #include "lib/util_sys.h" +#include "lib/vtoc.h" #include "boot.h" #include "bootmap.h" @@ -109,7 +110,8 @@ update_scsi_mbr(void* bootblock, disk_blockptr_t* table, memset(buffer, 0, sizeof(struct scsi_mbr)); memcpy(&mbr->magic, ZIPL_MAGIC, ZIPL_MAGIC_SIZE); mbr->version_id = DISK_LAYOUT_ID; - bootmap_store_blockptr(&mbr->program_table_pointer, table, info); + bootmap_store_blockptr(&mbr->program_table_pointer, table, info, + 0 /* this argument is ignored for scsi */); if (scsi_dump_sb_blockptr->linear.block != 0) { /* Write dump boot_info */ param.block = scsi_dump_sb_blockptr->linear.block * @@ -168,11 +170,11 @@ install_scsi(int fd, disk_blockptr_t* program_table, struct disk_info* info, } -/* Install bootloader for initial program load from an FBA type disk. */ +/* Install bootloader for CCW-type IPL from a FBA type disk */ static int -install_fba(int fd, disk_blockptr_t *program_table, - disk_blockptr_t *stage1b_list, blocknum_t stage1b_count, - struct disk_info *info) +install_fba_ccw(int fd, disk_blockptr_t *program_table, + disk_blockptr_t *stage1b_list, blocknum_t stage1b_count, + struct disk_info *info) { struct boot_fba_stage0 stage0; @@ -180,8 +182,8 @@ install_fba(int fd, disk_blockptr_t *program_table, if (boot_init_fba_stage0(&stage0, stage1b_list, stage1b_count)) return -1; - boot_get_ipl_info(&stage0.boot_info, BOOT_INFO_DEV_TYPE_FBA, - program_table, info); + boot_get_ipl_info_ccw(&stage0.boot_info, BOOT_INFO_DEV_TYPE_FBA, + program_table, info); if (DRY_RUN_FUNC(misc_pwrite(fd, &stage0, sizeof(stage0), 0))) return -1; @@ -218,12 +220,14 @@ out: return rc; } -/* Install bootloader for initial program load from an ECKD type disk with - * Linux Disk Layout. */ +/* + * Install bootloader for CCW-type IPL from ECKD type disk with + * Linux Disk Layout + */ static int -install_eckd_ldl(int fd, disk_blockptr_t *program_table, - disk_blockptr_t *stage1b_list, blocknum_t stage1b_count, - struct disk_info *info) +install_eckd_ldl_ccw(int fd, disk_blockptr_t *program_table, + disk_blockptr_t *stage1b_list, blocknum_t stage1b_count, + struct disk_info *info) { struct boot_eckd_ldl_stage0 stage0; struct boot_eckd_stage1 stage1; @@ -236,8 +240,8 @@ install_eckd_ldl(int fd, disk_blockptr_t *program_table, if (boot_init_eckd_stage1(&stage1, stage1b_list, stage1b_count)) return -1; - boot_get_ipl_info(&stage1.boot_info, BOOT_INFO_DEV_TYPE_ECKD, - program_table, info); + boot_get_ipl_info_ccw(&stage1.boot_info, BOOT_INFO_DEV_TYPE_ECKD, + program_table, info); if (DRY_RUN_FUNC(misc_pwrite(fd, &stage1, sizeof(stage1), sizeof(stage0)))) @@ -246,12 +250,14 @@ install_eckd_ldl(int fd, disk_blockptr_t *program_table, return 0; } -/* Install bootloader for initial program load from an ECKD type disk with - * OS/390 compatible disk layout. */ -static int -install_eckd_cdl(int fd, disk_blockptr_t *program_table, - disk_blockptr_t *stage1b_list, blocknum_t stage1b_count, - struct disk_info *info) +/** + * Install bootloader for CCW-type IPL from ECKD type disk with + * OS/390 compatible disk layout + */ +static int install_eckd_cdl_ccw(int fd, disk_blockptr_t *program_table, + disk_blockptr_t *stage1b_list, + blocknum_t stage1b_count, + struct disk_info *info) { struct boot_eckd_cdl_stage0 stage0; struct boot_eckd_stage1 stage1; @@ -264,8 +270,8 @@ install_eckd_cdl(int fd, disk_blockptr_t *program_table, if (boot_init_eckd_stage1(&stage1, stage1b_list, stage1b_count)) return -1; - boot_get_ipl_info(&stage1.boot_info, BOOT_INFO_DEV_TYPE_ECKD, - program_table, info); + boot_get_ipl_info_ccw(&stage1.boot_info, BOOT_INFO_DEV_TYPE_ECKD, + program_table, info); if (DRY_RUN_FUNC(misc_pwrite(fd, &stage1, sizeof(stage1), 4 + info->phy_block_size))) @@ -273,16 +279,74 @@ install_eckd_cdl(int fd, disk_blockptr_t *program_table, return 0; } -int -install_bootloader(const char *device, disk_blockptr_t *program_table, - disk_blockptr_t *scsi_dump_sb_blockptr, - disk_blockptr_t *stage1b_list, blocknum_t stage1b_count, - struct disk_info *info, struct job_data *job) +/** + * Install a program table for List-Directed IPL on a CDL-formatted DASD. + * + * The installation means storing an actual boot record address in the + * volume label. + * + * BR: represents an actual boot record address. + */ +static int install_eckd_cdl_ld(int fd, disk_blockptr_t *br, + struct disk_info *info) { + struct vol_label_cdl vl; + int rc; + + /* Read a volume label from CDL-formatted DASD */ + if (misc_seek(fd, 2 * info->phy_block_size)) + return -1; + rc = misc_read(fd, &vl, sizeof(vl)); + if (rc) { + error_text("Could not read volume label"); + return rc; + } + /* Verify that we have a VOL1 label */ + if (!is_vol1(vl.vollbl)) { + error_text("Volume label 'vol1' not initialized"); + return -1; + } + /* Pack the actual boot record address */ + vtoc_set_cchhb(&vl.br, br->chs.cyl, br->chs.head, br->chs.sec); + + /* Write out the updated volume label */ + if (misc_seek(fd, 2 * info->phy_block_size)) + return -1; + rc = DRY_RUN_FUNC(misc_write(fd, &vl, sizeof(vl))); + if (rc) + error_text("Could not update volume lablel 'vol1'"); + return 0; +} + +/** + * Install a "compatible" boot record referring one, or two "similar" + * program tables. + * + * The picture below shows which program table is used for IPL + * of specified type from disk of specified type. + * E.g. program table "0" is used for CCW-type IPL from ECKD DASD, + * LD-IPL from DASD FBA is unsupported (respectively, only one program + * table "0" is used), etc. + * + * CCW-IPL LD-IPL + * + * SCSI X 0 + * DASD FBA 0 X + * ECKD DASD LDL 0 X + * ECKD DASD CDL 0 1 + */ +int install_bootloader(struct job_data *job, struct install_set *bis) +{ + disk_blockptr_t *scsi_dump_sb_blockptr = &bis->scsi_dump_sb_blockptr; + struct program_table *pt0 = &bis->tables[PROGRAM_TABLE_0]; + struct program_table *pt1 = &bis->tables[PROGRAM_TABLE_1]; + struct disk_info *info = bis->info; + char *device = bis->device; int fd, rc; /* Inform user about what we're up to */ - printf("Preparing boot device: "); + printf("Preparing boot device for %sIPL: ", + disk_get_ipl_type(info->type)); if (info->name) { printf("%s", info->name); if (info->devno >= 0) @@ -310,27 +374,40 @@ install_bootloader(const char *device, disk_blockptr_t *program_table, "caches.\n"); } } - /* Call disk specific install functions */ + /* + * Depending on disk type, install one or two program tables + * for CCW-type IPL and (or) for List-Directed IPL (see the + * picture in comments above) + */ rc = -1; switch (info->type) { case disk_type_scsi: - rc = install_scsi(fd, program_table, info, + rc = install_scsi(fd, &pt0->table, info, scsi_dump_sb_blockptr); if (rc == 0 && job->id == job_dump_partition && !is_ngdump_enabled(device, &job->target)) rc = overwrite_partition_start(fd, info, 0); break; case disk_type_fba: - rc = install_fba(fd, program_table, stage1b_list, - stage1b_count, info); + rc = install_fba_ccw(fd, + &pt0->table, + pt0->stage1b_list, + pt0->stage1b_count, info); break; case disk_type_eckd_ldl: - rc = install_eckd_ldl(fd, program_table, stage1b_list, - stage1b_count, info); + rc = install_eckd_ldl_ccw(fd, + &pt0->table, + pt0->stage1b_list, + pt0->stage1b_count, info); break; case disk_type_eckd_cdl: - rc = install_eckd_cdl(fd, program_table, stage1b_list, - stage1b_count, info); + rc = install_eckd_cdl_ccw(fd, + &pt0->table, + pt0->stage1b_list, + pt0->stage1b_count, info); + if (rc) + break; + rc = install_eckd_cdl_ld(fd, pt1->stage1b_list, info); break; case disk_type_diag: /* Should not happen */ diff --git a/zipl/src/zipl.c b/zipl/src/zipl.c index eed5423b..64a42de9 100644 --- a/zipl/src/zipl.c +++ b/zipl/src/zipl.c @@ -126,11 +126,8 @@ check_for_root(void) int main(int argc, char* argv[]) { - struct disk_info* info; - disk_blockptr_t program_table, scsi_dump_sb_blockptr, *stage1b_list; - blocknum_t stage1b_count; + struct install_set bis; struct job_data* job; - char* device; int rc; /* Check internals */ @@ -197,21 +194,11 @@ main(int argc, char* argv[]) case job_ipl: case job_segment: case job_menu: - /* Create bootmap */ - stage1b_list = NULL; - rc = bootmap_create(job, &program_table, &scsi_dump_sb_blockptr, - &stage1b_list, &stage1b_count, &device, - &info); + rc = prepare_bootloader(job, &bis); if (rc) break; - /* Install boot loader */ - rc = install_bootloader(device, &program_table, - &scsi_dump_sb_blockptr, stage1b_list, - stage1b_count, info, job); - if (stage1b_list != NULL) - free(stage1b_list); - misc_free_temp_dev(device); - disk_free_info(info); + rc = install_bootloader(job, &bis); + free_bootloader(&bis); break; case job_ipl_tape: rc = install_tapeloader(job->data.ipl_tape.device, @@ -251,3 +238,57 @@ main(int argc, char* argv[]) job_free(job); return abs(rc); } + +/** + * Program Component Footers + */ +struct component_footer component_footers[NR_PROGRAM_COMPONENTS] = { + [COMPONENT_ID_HEAP_AREA] = { + .type = COMPONENT_TYPE_LOAD, + .desc = "heap area" + }, + [COMPONENT_ID_STACK_AREA] = { + .type = COMPONENT_TYPE_LOAD, + .desc = "stack area" + }, + [COMPONENT_ID_LOADER_SIGNATURE] = { + .type = COMPONENT_TYPE_SIGNATURE, + .desc = "loader signature" + }, + [COMPONENT_ID_LOADER] = { + .type = COMPONENT_TYPE_LOAD, + .desc = "internal loader" + }, + [COMPONENT_ID_PARAMETERS] = { + .type = COMPONENT_TYPE_LOAD, + .desc = "parameters" + }, + [COMPONENT_ID_IMAGE_SIGNATURE] = { + .type = COMPONENT_TYPE_SIGNATURE, + .desc = "image signature" + }, + [COMPONENT_ID_KERNEL_IMAGE] = { + .type = COMPONENT_TYPE_LOAD, + .desc = "kernel image" + }, + [COMPONENT_ID_PARMLINE] = { + .type = COMPONENT_TYPE_LOAD, + .desc = "parmline" + }, + [COMPONENT_ID_RAMDISK_SIGNATURE] = { + .type = COMPONENT_TYPE_SIGNATURE, + .desc = "ramdisk signature" + }, + [COMPONENT_ID_RAMDISK] = { + .type = COMPONENT_TYPE_LOAD, + .desc = "initial ramdisk" + }, + [COMPONENT_ID_ENVBLK] = { + .type = COMPONENT_TYPE_LOAD, + .desc = "environment blk" + }, + [COMPONENT_ID_SEGMENT_FILE] = { + .type = COMPONENT_TYPE_EXECUTE, + .desc = "segment file" + } +};