From f7d2339c6ad6c0adf1902a1a97a30b3168bc2c46 Mon Sep 17 00:00:00 2001 From: Eduard Shishkin Date: Tue, 22 Nov 2022 13:31:40 +0100 Subject: [PATCH] zipl: List-Directed IPL from ECKD DASD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make zipl tool prepare ECKD DASD for booting by different IPL programs (with the same boot record installed). Besides standard CCW-type IPL, user gets an ability to trigger (with the same boot record installed!) List-Directed IPL. This allows to use the feature of secure boot from ECKD DASD (which is not available for CCW-type IPL). When using the old boot interfaces, the usual CCW-type IPL is triggered for DASD. Also for compatibility reasons zipl(8) tool is modified to create and install one, or two "similar" program tables per boot partition, depending on job and disk type. The "similar" program tables differ only in block pointers format. The old IPL programs (CCW-type IPL) use program table based on the old format. All program tables are packed to the same bootmap file. Their order and logical offsets in the file are not significant (not used by anyone). The picture below shows which program table is used for IPL of specified type from disk of specified type. Here "0" and "1" are identifiers of program tables based on the old and new block pointers format respectively. 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 Signed-off-by: Eduard Shishkin Reviewed-by: Stefan Haberland Signed-off-by: Jan Höppner --- include/boot/boot_defs.h | 45 +- include/lib/vtoc.h | 11 + zipl/boot/eckd2.c | 8 +- zipl/boot/stage2.h | 2 +- zipl/include/boot.h | 6 +- zipl/include/bootmap.h | 8 +- zipl/include/disk.h | 12 +- zipl/include/install.h | 86 ++- zipl/src/Makefile | 3 +- zipl/src/boot.c | 34 +- zipl/src/bootmap.c | 1254 ++++++++++++++++++++++---------------- zipl/src/disk.c | 44 +- zipl/src/install.c | 149 +++-- zipl/src/zipl.c | 75 ++- 14 files changed, 1100 insertions(+), 637 deletions(-) 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" + } +};