From 1729ef40b7285cfa817b62aebe07fad12dbf1fdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Thu, 10 Dec 2009 17:48:03 +0000 Subject: [PATCH] - added device-mapper support into zipl (#546280) - added missing check and print NSS name in case an NSS has been IPLed (#546297) --- 0015-s390tools-1.8.2-zipl-dm.patch | 2716 ++++++++++++++++++++++++ 0016-s390tools-1.8.2-lsreipl-nss.patch | 376 ++++ s390utils.spec | 12 +- 3 files changed, 3103 insertions(+), 1 deletion(-) create mode 100644 0015-s390tools-1.8.2-zipl-dm.patch create mode 100644 0016-s390tools-1.8.2-lsreipl-nss.patch diff --git a/0015-s390tools-1.8.2-zipl-dm.patch b/0015-s390tools-1.8.2-zipl-dm.patch new file mode 100644 index 0000000..306006d --- /dev/null +++ b/0015-s390tools-1.8.2-zipl-dm.patch @@ -0,0 +1,2716 @@ +From e089f907d7ba7f18479eaff61852171842a219e2 Mon Sep 17 00:00:00 2001 +From: =?utf-8?q?Dan=20Hor=C3=A1k?= +Date: Thu, 10 Dec 2009 18:27:58 +0100 +Subject: [PATCH 15/16] s390tools-1.8.2-zipl-dm + +device mapper support for zipl +--- + zipl/include/disk.h | 25 ++- + zipl/include/install.h | 9 +- + zipl/include/job.h | 17 +- + zipl/include/scan.h | 19 +- + zipl/man/zipl.8 | 88 +++++- + zipl/man/zipl.conf.5 | 71 ++++- + zipl/src/Makefile | 1 + + zipl/src/bootmap.c | 70 ++--- + zipl/src/disk.c | 242 ++++++++++--- + zipl/src/install.c | 11 +- + zipl/src/job.c | 362 +++++++++++++++++- + zipl/src/scan.c | 262 ++++++++++++- + zipl/src/zipl.c | 13 +- + zipl/src/zipl_helper.device-mapper | 716 ++++++++++++++++++++++++++++++++++++ + 14 files changed, 1759 insertions(+), 147 deletions(-) + create mode 100644 zipl/src/zipl_helper.device-mapper + +diff --git a/zipl/include/disk.h b/zipl/include/disk.h +index c5179b7..4b39698 100644 +--- a/zipl/include/disk.h ++++ b/zipl/include/disk.h +@@ -2,7 +2,7 @@ + * s390-tools/zipl/include/disk.h + * Functions to handle disk layout specific operations. + * +- * Copyright IBM Corp. 2001, 2006. ++ * Copyright IBM Corp. 2001, 2009. + * + * Author(s): Carsten Otte + * Peter Oberparleiter +@@ -59,6 +59,19 @@ struct hd_geometry { + unsigned long start; + }; + ++/* Disk information source */ ++typedef enum { ++ source_auto, ++ source_user, ++ source_script ++} source_t; ++ ++/* targetbase definition */ ++typedef enum { ++ defined_as_device, ++ defined_as_name ++} definition_t; ++ + /* Disk information type */ + struct disk_info { + disk_type_t type; +@@ -72,11 +85,17 @@ struct disk_info { + struct hd_geometry geo; + char* name; + char* drv_name; ++ source_t source; ++ definition_t targetbase; + }; + ++struct job_target_data; + +-int disk_get_info(const char* device, struct disk_info** info); +-int disk_get_info_from_file(const char* filename, struct disk_info** info); ++int disk_get_info(const char* device, struct job_target_data* target, ++ struct disk_info** info); ++int disk_get_info_from_file(const char* filename, ++ struct job_target_data* target, ++ struct disk_info** info); + void disk_free_info(struct disk_info* info); + char* disk_get_type_name(disk_type_t type); + int disk_is_large_volume(struct disk_info* info); +diff --git a/zipl/include/install.h b/zipl/include/install.h +index ba31bff..5504deb 100644 +--- a/zipl/include/install.h ++++ b/zipl/include/install.h +@@ -2,7 +2,7 @@ + * s390-tools/zipl/include/install.h + * Functions handling the installation of the boot loader code onto disk. + * +- * Copyright IBM Corp. 2001, 2006. ++ * Copyright IBM Corp. 2001, 2009. + * + * Author(s): Carsten Otte + * Peter Oberparleiter +@@ -24,8 +24,9 @@ int install_tapeloader(const char* device, const char* image, + const char* parmline, const char* ramdisk, + address_t image_addr, address_t parm_addr, + address_t initrd_addr); +-int install_dump(const char* device, uint64_t mem); +-int install_mvdump(char* const device[], int device_count, uint64_t mem, +- uint8_t force); ++int install_dump(const char* device, struct job_target_data* target, ++ uint64_t mem); ++int install_mvdump(char* const device[], struct job_target_data* target, ++ int device_count, uint64_t mem, uint8_t force); + + #endif /* INSTALL_H */ +diff --git a/zipl/include/job.h b/zipl/include/job.h +index 824ffc4..cf881db 100644 +--- a/zipl/include/job.h ++++ b/zipl/include/job.h +@@ -3,7 +3,7 @@ + * Functions and data structures representing the actual 'job' that the + * user wants us to execute. + * +- * Copyright IBM Corp. 2001, 2006. ++ * Copyright IBM Corp. 2001, 2009. + * + * Author(s): Carsten Otte + * Peter Oberparleiter +@@ -13,6 +13,7 @@ + #define JOB_H + + #include "zipl.h" ++#include "disk.h" + + + enum job_id { +@@ -27,6 +28,17 @@ enum job_id { + job_mvdump = 9, + }; + ++struct job_target_data { ++ char* bootmap_dir; ++ char* targetbase; ++ disk_type_t targettype; ++ int targetcylinders; ++ int targetheads; ++ int targetsectors; ++ int targetblocksize; ++ blocknum_t targetoffset; ++}; ++ + struct job_ipl_data { + char* image; + char* parmline; +@@ -94,7 +106,7 @@ struct job_menu_data { + + struct job_data { + enum job_id id; +- char* bootmap_dir; ++ struct job_target_data target; + char* name; + union { + struct job_ipl_data ipl; +@@ -115,5 +127,6 @@ struct job_data { + + int job_get(int argc, char* argv[], struct job_data** data); + void job_free(struct job_data* job); ++int type_from_target(char *target, disk_type_t *type); + + #endif /* not JOB_H */ +diff --git a/zipl/include/scan.h b/zipl/include/scan.h +index b1c0e3a..ed5714e 100644 +--- a/zipl/include/scan.h ++++ b/zipl/include/scan.h +@@ -2,7 +2,7 @@ + * s390-tools/zipl/include/scan.h + * Scanner for zipl.conf configuration files + * +- * Copyright IBM Corp. 2001, 2006. ++ * Copyright IBM Corp. 2001, 2009. + * + * Author(s): Carsten Otte + * Peter Oberparleiter +@@ -15,7 +15,7 @@ + + + #define SCAN_SECTION_NUM 7 +-#define SCAN_KEYWORD_NUM 14 ++#define SCAN_KEYWORD_NUM 19 + + enum scan_id { + scan_id_empty = 0, +@@ -40,6 +40,11 @@ enum scan_keyword_id { + scan_keyword_defaultmenu = 11, + scan_keyword_tape = 12, + scan_keyword_mvdump = 13, ++ scan_keyword_targetbase = 14, ++ scan_keyword_targettype = 15, ++ scan_keyword_targetgeometry = 16, ++ scan_keyword_targetblocksize = 17, ++ scan_keyword_targetoffset = 18, + }; + + enum scan_section_type { +@@ -53,6 +58,14 @@ enum scan_section_type { + section_mvdump = 6, + }; + ++enum scan_target_type { ++ target_type_invalid = -1, ++ target_type_scsi = 0, ++ target_type_fba = 1, ++ target_type_ldl = 2, ++ target_type_cdl = 3, ++}; ++ + enum scan_key_state { + req, /* Keyword is required */ + opt, /* Keyword is optional */ +@@ -100,6 +113,8 @@ int scan_find_section(struct scan_token* scan, char* name, enum scan_id type, + int offset); + int scan_check_section_data(char* keyword[], int* line, char* name, + int section_line, enum scan_section_type* type); ++int scan_check_target_data(char* keyword[], int* line); + enum scan_section_type scan_get_section_type(char* keyword[]); ++enum scan_target_type scan_get_target_type(char *type); + + #endif /* not SCAN_H */ +diff --git a/zipl/man/zipl.8 b/zipl/man/zipl.8 +index 6df6026..e291445 100644 +--- a/zipl/man/zipl.8 ++++ b/zipl/man/zipl.8 +@@ -1,4 +1,4 @@ +-.TH ZIPL 8 "Apr 2006" "s390-tools" ++.TH ZIPL 8 "Nov 2009" "s390-tools" + .SH NAME + zipl \- boot loader for IBM S/390 and zSeries architectures + +@@ -58,6 +58,45 @@ See the + .BR zipl.conf (5) + man page for details on how to use the boot menu. + ++.B Logical devices ++ ++zipl can be used to prepare logical devices (e.g. a linear device mapper target) ++for booting when the following requirements are met by the logical device setup: ++.IP " -" ++all boot relevant files (i.e. kernel, ramdisk and parameter files) must be ++located on a logical device which is mapped to a single physical disk of a type ++supported by zipl (i.e. DASD or SCSI disk) ++.IP " -" ++adjacent data blocks on the logical device must correspond to adjacent blocks on ++the physical device ++.IP " -" ++access to the first blocks (starting at block 0) of the physical device must be ++given ++.PP ++Examples for logical device setups that are supported are linear and mirror ++mapping. ++ ++When working with logical devices, zipl requires that the user provides more ++information about the target device: ++.IP " -" ++device characteristics of the underlying physical device: disk type and format ++(e.g. ECKD CDL or FCP SCSI), disk geometry in case of ECKD DASDs and block size ++.IP " -" ++target device offset, i.e. the number of blocks between the physical device ++start and the start of the logical device containing the filesystem with all ++boot relevant files ++.IP " -" ++a device node which provides access to the first blocks of the device ++.PP ++If the user does not provide this information explicitly by parameters ++zipl automatically runs a driver specific helper script to obtain these data, ++e.g. zipl_helper.device-mapper. ++ ++Note that zipl uses /proc/devices to determine the driver name for a given ++device. If the driver name cannot be determined the preparation of a logical ++device for boot might fail. ++This can be the case in a chroot environment when /proc is not mounted ++explicitly. + + .SH OPTIONS + .TP +@@ -85,6 +124,53 @@ It is not possible to specify both this parameter and the name of a menu + or configuration section on the command line at the same time. + + .TP ++.BR "\-\-targetbase=" ++Install the actual boot loader on the device node specified by BASE DEVICE. ++ ++This option is required when working with logical devices (see section ++"Logical devices" above). ++ ++.TP ++.BR "\-\-targettype=" ++Assume that the physical device is of the specified type. Valid values are: ++.IP " -" 12 ++CDL: DASD disk with ECKD/compatible disk layout ++.IP " -" 12 ++LDL: DASD disk with ECDK/linux disk layout ++.IP " -" 12 ++FBA: FBA disk DASD ++.IP " -" 12 ++SCSI: SCSI disk ++.PP ++.IP " " 8 ++This option is required when working with logical devices (see section ++"Logical devices" above). ++ ++.TP ++.BR "\-\-targetgeometry=" ++Assume that the physical device has the specified number of cylinders, heads and ++sectors. ++ ++This option is required when working with logical devices which are located on ++DASD ECKD disks (see section "Logical devices" above). ++ ++.TP ++.BR "\-\-targetblocksize=" ++Assume that blocks on the physical device are SIZE bytes long. ++ ++This option is required when working with logical devices (see section ++"Logical devices" above). ++ ++.TP ++.BR "\-\-targetoffset=" ++Assume that the logical device containing the directory specified by the ++--target option is located on the physical device starting at the block ++specified by OFFSET. ++ ++This option is required when working with logical devices (see section ++"Logical devices" above). ++ ++.TP + .BR "\-T " " or " "\-\-tape=" + Install bootloader on the specified . Use this option instead + of the 'target' option to prepare a tape device for IPL. +diff --git a/zipl/man/zipl.conf.5 b/zipl/man/zipl.conf.5 +index 6be623e..9d0f60d 100644 +--- a/zipl/man/zipl.conf.5 ++++ b/zipl/man/zipl.conf.5 +@@ -1,4 +1,4 @@ +-.TH ZIPL.CONF 5 "Apr 2006" "s390-tools" ++.TH ZIPL.CONF 5 "Nov 2009" "s390-tools" + + .SH NAME + zipl.conf \- zipl configuration file +@@ -490,6 +490,75 @@ The device on which the target directory is located will be used as 'target + device', i.e. it will be prepared for booting (initial program load). + .PP + ++.B targetbase ++= ++.I base\-device ++(configuration and menu) ++.IP ++.B Configuration and menu section: ++.br ++Specify the device which will be prepared for booting. ++ ++This parameter is required when working with logical devices (see zipl(8)). ++.PP ++ ++.B targettype ++= ++.I type ++(configuration and menu) ++.IP ++.B Configuration and menu section: ++.br ++Specify the device type for the physical device. ++.IP " - " 12 ++CDL: DASD disk with ECKD/compatible disk layout ++.IP " - " 12 ++LDL: DASD disk with ECDK/linux disk layout ++.IP " - " 12 ++FBA: FBA disk DASD ++.IP " - " 12 ++SCSI disk ++.PP ++.IP " " 8 ++This parameter is required when working with logical devices (see zipl(8)). ++.PP ++ ++.B targetgeometry ++= ++.I cylinders,heads,sectors ++(configuration and menu) ++.IP ++.B Configuration and menu section: ++.br ++Specify the number of cylinders, heads and sectors for the physical device. ++ ++This parameter is required when working with logical devices (see zipl(8)). ++.PP ++ ++.B targetblocksize ++= ++.I size ++(configuration and menu) ++.IP ++.B Configuration and menu section: ++.br ++Specify the number of bytes per block for the physical device. ++ ++This parameter is required when working with logical devices (see zipl(8)). ++.PP ++ ++.B targetoffset ++= ++.I offset ++(configuration and menu) ++.IP ++.B Configuration and menu section: ++.br ++Specify the starting block number of the logical device on the physical device. ++ ++This parameter is required when working with logical devices (see zipl(8)). ++.PP ++ + .B timeout + = + .I menu-timeout +diff --git a/zipl/src/Makefile b/zipl/src/Makefile +index 234464b..f95bb55 100644 +--- a/zipl/src/Makefile ++++ b/zipl/src/Makefile +@@ -17,6 +17,7 @@ zipl: $(objects) + install: all + $(INSTALL) -d -m 755 $(BINDIR) + $(INSTALL) -c zipl $(BINDIR) ++ $(INSTALL) -m 755 $(wildcard zipl_helper.*) $(TOOLS_LIBDIR) + + clean: + rm -f *.o zipl +diff --git a/zipl/src/bootmap.c b/zipl/src/bootmap.c +index 566e59d..526aa48 100644 +--- a/zipl/src/bootmap.c ++++ b/zipl/src/bootmap.c +@@ -2,7 +2,7 @@ + * s390-tools/zipl/src/bootmap.c + * Functions to build the bootmap file. + * +- * Copyright IBM Corp. 2001, 2006. ++ * Copyright IBM Corp. 2001, 2009. + * + * Author(s): Carsten Otte + * Peter Oberparleiter +@@ -298,7 +298,8 @@ struct component_loc { + static int + add_component_file(int fd, const char* filename, address_t load_address, + off_t offset, void* component, int add_files, +- struct disk_info* info, struct component_loc *location) ++ struct disk_info* info, struct job_target_data* target, ++ struct component_loc *location) + { + struct disk_info* file_info; + struct component_loc loc; +@@ -336,7 +337,7 @@ add_component_file(int fd, const char* filename, address_t load_address, + } + } else { + /* Make sure file is on correct device */ +- rc = disk_get_info_from_file(filename, &file_info); ++ rc = disk_get_info_from_file(filename, target, &file_info); + if (rc) + return -1; + if (file_info->device != info->device) { +@@ -472,7 +473,7 @@ check_component_overlap(const char *name[], struct component_loc *loc, int num) + static int + add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, + int verbose, int add_files, component_header_type type, +- struct disk_info* info) ++ struct disk_info* info, struct job_target_data* target) + { + struct stat stats; + void* table; +@@ -500,7 +501,7 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, + } + rc = add_component_file(fd, ipl->image, ipl->image_addr, + KERNEL_HEADER_SIZE, VOID_ADD(table, offset), +- add_files, info, &comp_loc[0]); ++ add_files, info, target, &comp_loc[0]); + if (rc) { + error_text("Could not add image file '%s'", ipl->image); + free(table); +@@ -542,7 +543,7 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, + rc = add_component_file(fd, ipl->ramdisk, + ipl->ramdisk_addr, 0, + VOID_ADD(table, offset), +- add_files, info, &comp_loc[2]); ++ add_files, info, target, &comp_loc[2]); + if (rc) { + error_text("Could not add ramdisk '%s'", + ipl->ramdisk); +@@ -594,7 +595,8 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, + 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) ++ component_header_type type, struct disk_info* info, ++ struct job_target_data* target) + { + const char *comp_name[1] = {"segment file"}; + struct component_loc comp_loc[1]; +@@ -618,7 +620,7 @@ add_segment_program(int fd, struct job_segment_data* segment, + } + rc = add_component_file(fd, segment->segment, segment->segment_addr, 0, + VOID_ADD(table, offset), add_files, info, +- &comp_loc[0]); ++ target, &comp_loc[0]); + if (rc) { + error_text("Could not add segment file '%s'", + segment->segment); +@@ -661,14 +663,15 @@ create_dump_fs_parmline(const char* parmline, const char* root_dev, + + static int + get_dump_fs_parmline(char* partition, char* parameters, uint64_t mem, +- char** result, struct disk_info* target_info) ++ struct disk_info* target_info, ++ struct job_target_data* target, char** result) + { + char* buffer; + struct disk_info* info; + int rc; + + /* Get information about partition */ +- rc = disk_get_info(partition, &info); ++ rc = disk_get_info(partition, target, &info); + if (rc) { + error_text("Could not get information for dump partition '%s'", + partition); +@@ -700,7 +703,7 @@ static int + add_dump_fs_program(int fd, struct job_dump_fs_data* dump_fs, + disk_blockptr_t* program, int verbose, + int add_files, component_header_type type, +- struct disk_info* info) ++ struct disk_info* info, struct job_target_data* target) + { + struct job_ipl_data ipl; + int rc; +@@ -725,12 +728,12 @@ add_dump_fs_program(int fd, struct job_dump_fs_data* dump_fs, + + /* Get file system dump parmline */ + rc = get_dump_fs_parmline(dump_fs->partition, dump_fs->parmline, +- dump_fs->mem, &ipl.parmline, info); ++ dump_fs->mem, info, target, &ipl.parmline); + if (rc) + return rc; + ipl.parm_addr = DEFAULT_PARMFILE_ADDRESS; + return add_ipl_program(fd, &ipl, program, verbose, 1, +- type, info); ++ type, info, target); + } + + +@@ -763,7 +766,7 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer, + rc = add_ipl_program(fd, &job->data.ipl, &table[0], + verbose || job->command_line, + job->add_files, component_header_ipl, +- info); ++ info, &job->target); + break; + case job_segment: + if (job->command_line) +@@ -774,7 +777,7 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer, + rc = add_segment_program(fd, &job->data.segment, &table[0], + verbose || job->command_line, + job->add_files, component_header_ipl, +- info); ++ info, &job->target); + break; + case job_dump_fs: + if (job->command_line) +@@ -785,7 +788,7 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer, + rc = add_dump_fs_program(fd, &job->data.dump_fs, &table[0], + verbose || job->command_line, + job->add_files, component_header_dump, +- info); ++ info, &job->target); + break; + case job_menu: + printf("Building menu '%s'\n", job->name); +@@ -804,7 +807,7 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer, + &table[job->data.menu.entry[i].pos], + verbose || job->command_line, + job->add_files, component_header_ipl, +- info); ++ info, &job->target); + break; + case job_dump_fs: + printf("Adding #%d: fs-dump section '%s'%s\n", +@@ -818,7 +821,7 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer, + &table[job->data.menu.entry[i].pos], + verbose || job->command_line, + job->add_files, component_header_dump, +- info); ++ info, &job->target); + break; + case job_print_usage: + case job_print_version: +@@ -888,9 +891,9 @@ bootmap_create(struct job_data* job, disk_blockptr_t* program_table, + size_t stage2_size; + int fd; + int rc; +- + /* Get full path of bootmap file */ +- filename = misc_make_path(job->bootmap_dir, BOOTMAP_TEMPLATE_FILENAME); ++ filename = misc_make_path(job->target.bootmap_dir, ++ BOOTMAP_TEMPLATE_FILENAME); + if (filename == NULL) + return -1; + /* Create temporary bootmap file */ +@@ -904,32 +907,14 @@ bootmap_create(struct job_data* job, disk_blockptr_t* program_table, + /* Retrieve target device information. Note that we have to + * call disk_get_info_from_file() to also get the file system + * block size. */ +- rc = disk_get_info_from_file(filename, &info); ++ rc = disk_get_info_from_file(filename, &job->target, &info); + if (rc) { + close(fd); + free(filename); + return -1; + } + /* Check for supported disk and driver types */ +- switch (info->type) { +- case disk_type_scsi: +- case disk_type_fba: +- break; +- case disk_type_eckd_classic: +- case disk_type_eckd_compatible: +- /* Check for valid CHS geometry data. */ +- if ((info->geo.cylinders == 0) || (info->geo.heads == 0) || +- (info->geo.sectors == 0)) { +- error_reason("Invalid disk geometry (CHS=%d/%d/%d)", +- info->geo.cylinders, info->geo.heads, +- info->geo.sectors); +- disk_free_info(info); +- close(fd); +- free(filename); +- return -1; +- } +- break; +- case disk_type_diag: ++ if ((info->source == source_auto) && (info->type == disk_type_diag)) { + error_reason("Unsupported disk type (%s)", + disk_get_type_name(info->type)); + disk_free_info(info); +@@ -959,7 +944,7 @@ bootmap_create(struct job_data* job, disk_blockptr_t* program_table, + return rc; + } + } +- printf("Building bootmap in '%s'%s\n", job->bootmap_dir, ++ printf("Building bootmap in '%s'%s\n", job->target.bootmap_dir, + job->add_files ? " (files will be added to bootmap file)" : + ""); + /* Write bootmap header */ +@@ -1046,7 +1031,8 @@ bootmap_create(struct job_data* job, disk_blockptr_t* program_table, + "file %s!\n", filename); + } else { + /* Rename to final bootmap name */ +- mapname = misc_make_path(job->bootmap_dir, BOOTMAP_FILENAME); ++ mapname = misc_make_path(job->target.bootmap_dir, ++ BOOTMAP_FILENAME); + if (mapname == NULL) { + misc_free_temp_dev(device); + disk_free_info(info); +diff --git a/zipl/src/disk.c b/zipl/src/disk.c +index 08f0c64..9392968 100644 +--- a/zipl/src/disk.c ++++ b/zipl/src/disk.c +@@ -2,13 +2,14 @@ + * s390-tools/zipl/src/disk.c + * Functions to handle disk layout specific operations. + * +- * Copyright IBM Corp. 2001, 2006. ++ * Copyright IBM Corp. 2001, 2009. + * + * Author(s): Carsten Otte + * Peter Oberparleiter + */ + + #include "disk.h" ++#include "job.h" + + #include + #include +@@ -84,16 +85,34 @@ disk_determine_dasd_type(struct disk_info *data, + return 0; + } + ++/* Return non-zero for ECKD type. */ + int +-disk_get_info(const char* device, struct disk_info** info) ++disk_is_eckd(disk_type_t type) ++{ ++ return (type == disk_type_eckd_classic || ++ type == disk_type_eckd_compatible); ++} ++ ++int ++disk_get_info(const char* device, struct job_target_data* target, ++ struct disk_info** info) + { + struct stat stats; ++ struct stat script_stats; + struct proc_part_entry part_entry; + struct proc_dev_entry dev_entry; + struct dasd_information dasd_info; + struct disk_info *data; + int fd; + long devsize; ++ FILE *fh; ++ char *script_pre = "/lib/s390-tools/zipl_helper."; ++ char script_file[80]; ++ char ppn_cmd[80]; ++ char buffer[80]; ++ char value[40]; ++ int majnum, minnum; ++ int checkparm; + + /* Get file information */ + if (stat(device, &stats)) { +@@ -113,37 +132,140 @@ disk_get_info(const char* device, struct disk_info** info) + return -1; + } + memset((void *) data, 0, sizeof(struct disk_info)); +- /* Get disk geometry. Note: geo.start contains a sector number +- * offset measured in physical blocks, not sectors (512 bytes) */ +- if (ioctl(fd, HDIO_GETGEO, &data->geo)) { +- error_reason("Could not get disk geometry"); +- free(data); +- close(fd); +- return -1; +- } + /* Try to get device driver name */ + if (proc_dev_get_entry(stats.st_rdev, 1, &dev_entry) == 0) { + data->drv_name = misc_strdup(dev_entry.name); + proc_dev_free_entry(&dev_entry); ++ } else { ++ fprintf(stderr, "Warning: Could not determine driver name for " ++ "major %d from /proc/devices\n", major(stats.st_rdev)); ++ fprintf(stderr, "Warning: Preparing a logical device for boot " ++ "might fail\n"); ++ } ++ data->source = source_user; ++ /* Check if targetbase script is available */ ++ strcpy(script_file, script_pre); ++ if (data->drv_name) { ++ strcat(script_file, data->drv_name); ++ } ++ if ((target->targetbase == NULL) && ++ (!stat(script_file, &script_stats))) { ++ data->source = source_script; ++ /* Run targetbase script */ ++ strcpy(ppn_cmd, script_file); ++ strcat(ppn_cmd, " "); ++ strcat(ppn_cmd, target->bootmap_dir); ++ printf("Run %s\n", ppn_cmd); ++ fh = popen(ppn_cmd, "r"); ++ if (fh == NULL) { ++ error_reason("Failed to run popen(%s,\"r\",)"); ++ goto out_close; ++ } ++ checkparm = 0; ++ while (fgets(buffer, 80, fh) != NULL) { ++ if (sscanf(buffer, "targetbase=%s", value) == 1) { ++ target->targetbase = misc_strdup(value); ++ checkparm++; ++ } ++ if (sscanf(buffer, "targettype=%s", value) == 1) { ++ type_from_target(value, &target->targettype); ++ checkparm++; ++ } ++ if (sscanf(buffer, "targetgeometry=%s", value) == 1) { ++ target->targetcylinders = ++ atoi(strtok(value, ",")); ++ target->targetheads = atoi(strtok(NULL, ",")); ++ target->targetsectors = atoi(strtok(NULL, ",")); ++ checkparm++; ++ } ++ if (sscanf(buffer, "targetblocksize=%s", value) == 1) { ++ target->targetblocksize = atoi(value); ++ checkparm++; ++ } ++ if (sscanf(buffer, "targetoffset=%s", value) == 1) { ++ target->targetoffset = atol(value); ++ checkparm++; ++ } ++ } ++ switch (pclose(fh)) { ++ case 0 : ++ /* success */ ++ break; ++ case -1 : ++ error_reason("Failed to run pclose"); ++ goto out_close; ++ default : ++ error_reason("Script could not determine target " ++ "parameters"); ++ goto out_close; ++ } ++ if ((!disk_is_eckd(target->targettype) && checkparm < 4) || ++ (disk_is_eckd(target->targettype) && checkparm != 5)) { ++ error_reason("Target parameters missing from script"); ++ goto out_close; ++ } + } +- /* Get DASD information */ +- if (ioctl(fd, BIODASDINFO, &dasd_info)) ++ ++ /* Get disk geometry. Note: geo.start contains a sector number ++ * offset measured in physical blocks, not sectors (512 bytes) */ ++ if (target->targetbase != NULL) { ++ data->geo.heads = target->targetheads; ++ data->geo.sectors = target->targetsectors; ++ data->geo.cylinders = target->targetcylinders; ++ data->geo.start = target->targetoffset; ++ } else { ++ data->source = source_auto; ++ if (ioctl(fd, HDIO_GETGEO, &data->geo)) { ++ error_reason("Could not get disk geometry"); ++ goto out_close; ++ } ++ } ++ if ((data->source == source_user) || (data->source == source_script)) { + data->devno = -1; +- else +- data->devno = dasd_info.devno; +- /* Get physical block size */ +- if (ioctl(fd, BLKSSZGET, &data->phy_block_size)) { +- error_reason("Could not get blocksize"); +- free(data); +- close(fd); +- return -1; ++ data->phy_block_size = target->targetblocksize; ++ } else { ++ /* Get DASD information */ ++ if (ioctl(fd, BIODASDINFO, &dasd_info)) ++ data->devno = -1; ++ else ++ data->devno = dasd_info.devno; ++ /* Get physical block size */ ++ if (ioctl(fd, BLKSSZGET, &data->phy_block_size)) { ++ error_reason("Could not get blocksize"); ++ goto out_close; ++ } + } + /* Get size of device in sectors (512 byte) */ + if (ioctl(fd, BLKGETSIZE, &devsize)) { + error_reason("Could not get device size"); +- close(fd); +- free(data); +- return -1; ++ goto out_close; ++ } ++ if ((data->source == source_user) || (data->source == source_script)) { ++ data->type = target->targettype; ++ data->partnum = 0; ++ /* Get file information */ ++ if (sscanf(target->targetbase, "%d:%d", &majnum, &minnum) ++ == 2) { ++ data->device = makedev(majnum, minnum); ++ data->targetbase = defined_as_device; ++ } ++ else { ++ if (stat(target->targetbase, &stats)) { ++ error_reason(strerror(errno)); ++ error_text("Could not get information for " ++ "file '%s'", target->targetbase); ++ goto out_close; ++ } ++ if (!S_ISBLK(stats.st_mode)) { ++ error_reason("Target base device '%s' is not " ++ "a block device", ++ target->targetbase); ++ goto out_close; ++ } ++ data->device = stats.st_rdev; ++ data->targetbase = defined_as_name; ++ } ++ goto type_determined; + } + /* Determine disk type */ + if (!data->drv_name) { +@@ -194,6 +316,15 @@ disk_get_info(const char* device, struct disk_info** info) + goto out_close; + } + ++type_determined: ++ /* Check for valid CHS geometry data. */ ++ if (disk_is_eckd(data->type) && (data->geo.cylinders == 0 || ++ data->geo.heads == 0 || data->geo.sectors == 0)) { ++ error_reason("Invalid disk geometry (CHS=%d/%d/%d)", ++ data->geo.cylinders, data->geo.heads, ++ data->geo.sectors); ++ goto out_close; ++ } + /* Convert device size to size in physical blocks */ + data->phy_blocks = devsize / (data->phy_block_size / 512); + if (data->partnum != 0) +@@ -221,7 +352,8 @@ out_close: + + + int +-disk_get_info_from_file(const char* filename, struct disk_info** info) ++disk_get_info_from_file(const char* filename, struct job_target_data* target, ++ struct disk_info** info) + { + struct stat stats; + char* device; +@@ -251,7 +383,7 @@ disk_get_info_from_file(const char* filename, struct disk_info** info) + if (rc) + return -1; + /* Get device info */ +- rc = disk_get_info(device, info); ++ rc = disk_get_info(device, target, info); + if (rc == 0) + (*info)->fs_block_size = blocksize; + /* Clean up */ +@@ -523,8 +655,14 @@ disk_is_large_volume(struct disk_info* info) + void + disk_print_info(struct disk_info* info) + { ++ char footnote[4] = ""; ++ if ((info->source == source_user) || (info->source == source_script)) ++ strcpy(footnote, " *)"); ++ + printf(" Device..........................: "); + disk_print_devt(info->device); ++ if (info->targetbase == defined_as_device) ++ printf("%s", footnote); + printf("\n"); + if (info->partnum != 0) { + printf(" Partition.......................: "); +@@ -532,45 +670,55 @@ disk_print_info(struct disk_info* info) + printf("\n"); + } + if (info->name) { +- printf(" Device name.....................: %s\n", ++ printf(" Device name.....................: %s", + info->name); ++ if (info->targetbase == defined_as_name) ++ printf("%s", footnote); ++ printf("\n"); + } + if (info->drv_name) { + printf(" Device driver name..............: %s\n", + info->drv_name); + } +- if ((info->type == disk_type_fba) || +- (info->type == disk_type_diag) || +- (info->type == disk_type_eckd_classic) || +- (info->type == disk_type_eckd_compatible)) { ++ if (((info->type == disk_type_fba) || ++ (info->type == disk_type_diag) || ++ (info->type == disk_type_eckd_classic) || ++ (info->type == disk_type_eckd_compatible)) && ++ (info->source == source_auto)) { + printf(" DASD device number..............: %04x\n", + info->devno); + } + printf(" Type............................: disk %s\n", + (info->partnum != 0) ? "partition" : "device"); +- printf(" Disk layout.....................: %s\n", +- disk_get_type_name(info->type)); +- printf(" Geometry - heads................: %d\n", +- info->geo.heads); +- printf(" Geometry - sectors..............: %d\n", +- info->geo.sectors); +- if (disk_is_large_volume(info)) { +- /* ECKD large volume. There is not enough information +- * available in INFO to calculate disk cylinder size. */ +- printf(" Geometry - cylinders............: > 65534\n"); +- } else { +- printf(" Geometry - cylinders............: %d\n", +- info->geo.cylinders); ++ printf(" Disk layout.....................: %s%s\n", ++ disk_get_type_name(info->type), footnote); ++ if (disk_is_eckd(info->type)) { ++ printf(" Geometry - heads................: %d%s\n", ++ info->geo.heads, footnote); ++ printf(" Geometry - sectors..............: %d%s\n", ++ info->geo.sectors, footnote); ++ if (disk_is_large_volume(info)) { ++ /* ECKD large volume. There is not enough information ++ * available in INFO to calculate disk cylinder size. */ ++ printf(" Geometry - cylinders............: > 65534\n"); ++ } else { ++ printf(" Geometry - cylinders............: %d%s\n", ++ info->geo.cylinders, footnote); ++ } + } +- printf(" Geometry - start................: %ld\n", +- info->geo.start); ++ printf(" Geometry - start................: %ld%s\n", ++ info->geo.start, footnote); + if (info->fs_block_size >= 0) + printf(" File system block size..........: %d\n", + info->fs_block_size); +- printf(" Physical block size.............: %d\n", +- info->phy_block_size); ++ printf(" Physical block size.............: %d%s\n", ++ info->phy_block_size, footnote); + printf(" Device size in physical blocks..: %ld\n", + (long) info->phy_blocks); ++ if (info->source == source_user) ++ printf(" *) Data provided by user.\n"); ++ if (info->source == source_script) ++ printf(" *) Data provided by script.\n"); + } + + +diff --git a/zipl/src/install.c b/zipl/src/install.c +index 55b0dd3..ec84821 100644 +--- a/zipl/src/install.c ++++ b/zipl/src/install.c +@@ -2,7 +2,7 @@ + * s390-tools/zipl/src/install.c + * Functions handling the installation of the boot loader code onto disk. + * +- * Copyright IBM Corp. 2001, 2006. ++ * Copyright IBM Corp. 2001, 2009. + * + * Author(s): Carsten Otte + * Peter Oberparleiter +@@ -944,7 +944,7 @@ install_dump_tape(int fd, uint64_t mem) + + + int +-install_dump(const char* device, uint64_t mem) ++install_dump(const char* device, struct job_target_data* target, uint64_t mem) + { + struct disk_info* info; + char* tempdev; +@@ -985,7 +985,7 @@ install_dump(const char* device, uint64_t mem) + } + close(fd); + /* This is a disk device */ +- rc = disk_get_info(device, &info); ++ rc = disk_get_info(device, target, &info); + if (rc) { + error_text("Could not get information for dump target " + "'%s'", device); +@@ -1063,7 +1063,8 @@ install_dump(const char* device, uint64_t mem) + + + int +-install_mvdump(char* const device[], int count, uint64_t mem, uint8_t force) ++install_mvdump(char* const device[], struct job_target_data* target, int count, ++ uint64_t mem, uint8_t force) + { + struct disk_info* info[MAX_DUMP_VOLUMES] = {0}; + struct mvdump_parm_table parm; +@@ -1095,7 +1096,7 @@ install_mvdump(char* const device[], int count, uint64_t mem, uint8_t force) + } + close(fd); + /* This is a disk device */ +- rc = disk_get_info(device[i], &info[i]); ++ rc = disk_get_info(device[i], target, &info[i]); + if (rc) { + error_text("Could not get information for dump target " + "'%s'", device[i]); +diff --git a/zipl/src/job.c b/zipl/src/job.c +index 89c8c23..a65e8c1 100644 +--- a/zipl/src/job.c ++++ b/zipl/src/job.c +@@ -3,7 +3,7 @@ + * Functions and data structures representing the actual 'job' that the + * user wants us to execute. + * +- * Copyright IBM Corp. 2001, 2006. ++ * Copyright IBM Corp. 2001, 2009. + * + * Author(s): Carsten Otte + * Peter Oberparleiter +@@ -29,6 +29,11 @@ + static struct option options[] = { + { "config", required_argument, NULL, 'c'}, + { "target", required_argument, NULL, 't'}, ++ { "targetbase", required_argument, NULL, 0xaa}, ++ { "targettype", required_argument, NULL, 0xab}, ++ { "targetgeometry", required_argument, NULL, 0xac}, ++ { "targetblocksize", required_argument, NULL, 0xad}, ++ { "targetoffset", required_argument, NULL, 0xae}, + { "image", required_argument, NULL, 'i'}, + { "ramdisk", required_argument, NULL, 'r'}, + { "parmfile", required_argument, NULL, 'p'}, +@@ -71,6 +76,12 @@ struct command_line { + + /* Global variable for default boot target. Ugly but necessary... */ + static char *default_target; ++static char *default_targetbase; ++static char *default_targettype; ++static char *default_targetgeometry; ++static char *temp_targetgeometry; ++static char *default_targetblocksize; ++static char *default_targetoffset; + + static int + store_option(struct command_line* cmdline, enum scan_keyword_id keyword, +@@ -171,6 +182,32 @@ get_command_line(int argc, char* argv[], struct command_line* line) + rc = store_option(&cmdline, scan_keyword_target, + optarg); + break; ++ case 0xaa: ++ is_keyword = 1; ++ rc = store_option(&cmdline, scan_keyword_targetbase, ++ optarg); ++ break; ++ case 0xab: ++ is_keyword = 1; ++ rc = store_option(&cmdline, scan_keyword_targettype, ++ optarg); ++ break; ++ case 0xac: ++ is_keyword = 1; ++ rc = store_option(&cmdline, scan_keyword_targetgeometry, ++ optarg); ++ break; ++ case 0xad: ++ is_keyword = 1; ++ rc = store_option(&cmdline, ++ scan_keyword_targetblocksize, ++ optarg); ++ break; ++ case 0xae: ++ is_keyword = 1; ++ rc = store_option(&cmdline, scan_keyword_targetoffset, ++ optarg); ++ break; + case 'T': + is_keyword = 1; + cmdline.automenu = 0; +@@ -288,6 +325,9 @@ get_command_line(int argc, char* argv[], struct command_line* line) + } + return rc; + } ++ rc = scan_check_target_data(cmdline.data, NULL); ++ if (rc) ++ return rc; + } + *line = cmdline; + return 0; +@@ -295,6 +335,13 @@ get_command_line(int argc, char* argv[], struct command_line* line) + + + static void ++free_target_data(struct job_target_data* data) ++{ ++ if (data->targetbase != NULL) ++ free(data->targetbase); ++} ++ ++static void + free_ipl_data(struct job_ipl_data* data) + { + if (data->image != NULL) +@@ -388,8 +435,9 @@ free_mvdump_data(struct job_mvdump_data* data) + void + job_free(struct job_data* job) + { +- if (job->bootmap_dir != NULL) +- free(job->bootmap_dir); ++ if (job->target.bootmap_dir != NULL) ++ free(job->target.bootmap_dir); ++ free_target_data(&job->target); + if (job->name != NULL) + free(job->name); + switch (job->id) { +@@ -667,16 +715,16 @@ check_job_data(struct job_data* job) + int rc; + + /* Check for missing information */ +- if (job->bootmap_dir != NULL) { +- rc = misc_check_writable_directory(job->bootmap_dir); ++ if (job->target.bootmap_dir != NULL) { ++ rc = misc_check_writable_directory(job->target.bootmap_dir); + if (rc) { + if (job->name == NULL) { + error_text("Target directory '%s'", +- job->bootmap_dir); ++ job->target.bootmap_dir); + } else { + error_text("Target directory '%s' in section " + "'%s'", +- job->bootmap_dir, job->name); ++ job->target.bootmap_dir, job->name); + } + return rc; + } +@@ -854,6 +902,28 @@ get_parmline(char* filename, char* line, char** parmline, address_t* address, + + #define MEGABYTE_MASK (1024LL * 1024LL - 1LL) + ++int ++type_from_target(char *target, disk_type_t *type) ++{ ++ switch (scan_get_target_type(target)) { ++ case target_type_scsi: ++ *type = disk_type_scsi; ++ return 0; ++ case target_type_fba: ++ *type = disk_type_fba; ++ return 0; ++ case target_type_ldl: ++ *type = disk_type_eckd_classic; ++ return 0; ++ case target_type_cdl: ++ *type = disk_type_eckd_compatible; ++ return 0; ++ default: ++ return -1; ++ } ++} ++ ++ + static int + get_job_from_section_data(char* data[], struct job_data* job, char* section) + { +@@ -869,11 +939,76 @@ get_job_from_section_data(char* data[], struct job_data* job, char* section) + error_text("Unable to find default section in your config file."); + break; + } +- job->bootmap_dir = misc_strdup(default_target); ++ job->target.bootmap_dir = misc_strdup(default_target); + } else +- job->bootmap_dir = misc_strdup(data[(int) scan_keyword_target]); +- if (job->bootmap_dir == NULL) ++ job->target.bootmap_dir = misc_strdup(data[(int) scan_keyword_target]); ++ if (job->target.bootmap_dir == NULL) + return -1; ++ /* Fill in target */ ++ if (data[(int) scan_keyword_targetbase] != NULL) { ++ job->target.targetbase = ++ misc_strdup(data[(int) ++ scan_keyword_targetbase]); ++ if (job->target.targetbase == NULL) ++ return -1; ++ } else { ++ if ((data[(int) scan_keyword_target] == NULL) && ++ (default_targetbase != NULL)) { ++ job->target.targetbase = ++ misc_strdup(default_targetbase); ++ if (job->target.targetbase == NULL) ++ return -1; ++ } ++ } ++ if (data[(int) scan_keyword_targettype] != NULL) { ++ if (type_from_target( ++ data[(int) scan_keyword_targettype], ++ &job->target.targettype)) ++ return -1; ++ } else { ++ if ((data[(int) scan_keyword_target] == NULL) && ++ (default_targettype != NULL)) ++ type_from_target(default_targettype, ++ &job->target.targettype); ++ } ++ if (data[(int) scan_keyword_targetgeometry] != NULL) { ++ job->target.targetcylinders = ++ atoi(strtok(data[(int) ++ scan_keyword_targetgeometry], ",")); ++ job->target.targetheads = atoi(strtok(NULL, ",")); ++ job->target.targetsectors = atoi(strtok(NULL, ",")); ++ } else { ++ if ((data[(int) scan_keyword_target] == NULL) && ++ (default_targetgeometry != NULL)) { ++ temp_targetgeometry = ++ misc_strdup(default_targetgeometry); ++ if (temp_targetgeometry == NULL) ++ return -1; ++ job->target.targetcylinders = ++ atoi(strtok(temp_targetgeometry, ",")); ++ job->target.targetheads = atoi(strtok(NULL, ",")); ++ job->target.targetsectors = atoi(strtok(NULL, ",")); ++ free(temp_targetgeometry); ++ } ++ } ++ if (data[(int) scan_keyword_targetblocksize] != NULL) ++ job->target.targetblocksize = ++ atoi(data[(int) scan_keyword_targetblocksize]); ++ else { ++ if ((data[(int) scan_keyword_target] == NULL) && ++ (default_targetblocksize != NULL)) ++ job->target.targetblocksize = ++ atoi(default_targetblocksize); ++ } ++ if (data[(int) scan_keyword_targetoffset] != NULL) ++ job->target.targetoffset = ++ atol(data[(int) scan_keyword_targetoffset]); ++ else { ++ if ((data[(int) scan_keyword_target] == NULL) && ++ (default_targetoffset != NULL)) ++ job->target.targetoffset = ++ atol(default_targetoffset); ++ } + /* Fill in name and address of image file */ + job->data.ipl.image = misc_strdup( + data[(int) scan_keyword_image]); +@@ -942,8 +1077,9 @@ get_job_from_section_data(char* data[], struct job_data* job, char* section) + /* SEGMENT LOAD job */ + job->id = job_segment; + /* Fill in name of bootmap directory */ +- job->bootmap_dir = misc_strdup(data[(int) scan_keyword_target]); +- if (job->bootmap_dir == NULL) ++ job->target.bootmap_dir = ++ misc_strdup(data[(int) scan_keyword_target]); ++ if (job->target.bootmap_dir == NULL) + return -1; + /* Fill in segment filename */ + job->data.segment.segment = +@@ -979,8 +1115,9 @@ get_job_from_section_data(char* data[], struct job_data* job, char* section) + /* DUMP TO FILESYSTEM job */ + job->id = job_dump_fs; + /* Fill in name of bootmap directory */ +- job->bootmap_dir = misc_strdup(data[(int) scan_keyword_target]); +- if (job->bootmap_dir == NULL) ++ job->target.bootmap_dir = ++ misc_strdup(data[(int) scan_keyword_target]); ++ if (job->target.bootmap_dir == NULL) + return -1; + /* Fill in partition name */ + job->data.dump_fs.partition = +@@ -1085,11 +1222,43 @@ get_menu_job(struct scan_token* scan, char* menu, struct job_data* job) + atol(scan[i].content.keyword.value); + break; + case scan_keyword_target: +- job->bootmap_dir = misc_strdup( ++ job->target.bootmap_dir = misc_strdup( + scan[i].content.keyword.value); +- if (job->bootmap_dir == NULL) ++ if (job->target.bootmap_dir == NULL) + return -1; + break; ++ case scan_keyword_targetbase: ++ job->target.targetbase = misc_strdup( ++ scan[i].content.keyword.value); ++ if (job->target.targetbase == NULL) ++ return -1; ++ break; ++ case scan_keyword_targettype: ++ if (type_from_target( ++ scan[i].content.keyword.value, ++ &job->target.targettype)) ++ return -1; ++ break; ++ case scan_keyword_targetgeometry: ++ job->target.targetcylinders = ++ atoi(strtok( ++ scan[i].content.keyword.value, ++ ",")); ++ job->target.targetheads = ++ atoi(strtok(NULL, ",")); ++ job->target.targetsectors = ++ atoi(strtok(NULL, ",")); ++ break; ++ case scan_keyword_targetblocksize: ++ job->target.targetblocksize = ++ atoi( ++ scan[i].content.keyword.value); ++ break; ++ case scan_keyword_targetoffset: ++ job->target.targetoffset = ++ atol( ++ scan[i].content.keyword.value); ++ break; + default: + /* Should not happen */ + break; +@@ -1142,7 +1311,8 @@ get_menu_job(struct scan_token* scan, char* menu, struct job_data* job) + return -1; + memset((void *) temp_job, 0, sizeof(struct job_data)); + if (data[(int) scan_keyword_target] == NULL) +- data[(int) scan_keyword_target] = misc_strdup(job->bootmap_dir); ++ data[(int) scan_keyword_target] = ++ misc_strdup(job->target.bootmap_dir); + rc = get_job_from_section_data(data, temp_job, + job->data.menu.entry[current].name); + if (rc) { +@@ -1254,6 +1424,56 @@ get_section_job(struct scan_token* scan, char* section, struct job_data* job, + scan[i].content.keyword.keyword == scan_keyword_target && + !strcmp(DEFAULTBOOT_SECTION, name)) + default_target = misc_strdup(scan[i].content.keyword.value); ++ if (scan[i].id == scan_id_keyword_assignment && ++ scan[i].content.keyword.keyword == ++ scan_keyword_targetbase && ++ scan[i].content.keyword.value != NULL && ++ !strcmp(DEFAULTBOOT_SECTION, name)) { ++ default_targetbase = ++ misc_strdup(scan[i].content.keyword.value); ++ if (default_targetbase == NULL) ++ return -1; ++ } ++ if (scan[i].id == scan_id_keyword_assignment && ++ scan[i].content.keyword.keyword == ++ scan_keyword_targettype && ++ scan[i].content.keyword.value != NULL && ++ !strcmp(DEFAULTBOOT_SECTION, name)) { ++ default_targettype = ++ misc_strdup(scan[i].content.keyword.value); ++ if (default_targettype == NULL) ++ return -1; ++ } ++ if (scan[i].id == scan_id_keyword_assignment && ++ scan[i].content.keyword.keyword == ++ scan_keyword_targetgeometry && ++ scan[i].content.keyword.value != NULL && ++ !strcmp(DEFAULTBOOT_SECTION, name)) { ++ default_targetgeometry = ++ misc_strdup(scan[i].content.keyword.value); ++ if (default_targetgeometry == NULL) ++ return -1; ++ } ++ if (scan[i].id == scan_id_keyword_assignment && ++ scan[i].content.keyword.keyword == ++ scan_keyword_targetblocksize && ++ scan[i].content.keyword.value != NULL && ++ !strcmp(DEFAULTBOOT_SECTION, name)) { ++ default_targetblocksize = ++ misc_strdup(scan[i].content.keyword.value); ++ if (default_targetblocksize == NULL) ++ return -1; ++ } ++ if (scan[i].id == scan_id_keyword_assignment && ++ scan[i].content.keyword.keyword == ++ scan_keyword_targetoffset && ++ scan[i].content.keyword.value != NULL && ++ !strcmp(DEFAULTBOOT_SECTION, name)) { ++ default_targetoffset = ++ misc_strdup(scan[i].content.keyword.value); ++ if (default_targetoffset == NULL) ++ return -1; ++ } + } + } + if (strcmp(section, DEFAULTBOOT_SECTION) == 0) { +@@ -1335,16 +1555,25 @@ create_fake_menu(struct scan_token *scan) + int i, j, pos, numsec, size, defaultpos; + char *name; + char *target; ++ char *targetbase; ++ char *targettype; ++ char *targetgeometry; ++ char *targetblocksize; ++ char *targetoffset; + char *timeout; + char *seclist[1024]; + char *defaultsection; + char buf[1024]; + struct scan_token *tmp; +- + /* Count # of sections */ + numsec = 0; + name = NULL; + target = NULL; ++ targetbase = NULL; ++ targettype = NULL; ++ targetgeometry = NULL; ++ targetblocksize = NULL; ++ targetoffset = NULL; + timeout = NULL; + for (i = 0; (int) scan[i].id != 0; i++) { + if (scan[i].id == scan_id_section_heading) { +@@ -1364,6 +1593,36 @@ create_fake_menu(struct scan_token *scan) + target = scan[i].content.keyword.value; + + if (scan[i].id == scan_id_keyword_assignment && ++ scan[i].content.keyword.keyword == ++ scan_keyword_targetbase && ++ !strcmp(DEFAULTBOOT_SECTION, name)) ++ targetbase = scan[i].content.keyword.value; ++ ++ if (scan[i].id == scan_id_keyword_assignment && ++ scan[i].content.keyword.keyword == ++ scan_keyword_targettype && ++ !strcmp(DEFAULTBOOT_SECTION, name)) ++ targettype = scan[i].content.keyword.value; ++ ++ if (scan[i].id == scan_id_keyword_assignment && ++ scan[i].content.keyword.keyword == ++ scan_keyword_targetgeometry && ++ !strcmp(DEFAULTBOOT_SECTION, name)) ++ targetgeometry = scan[i].content.keyword.value; ++ ++ if (scan[i].id == scan_id_keyword_assignment && ++ scan[i].content.keyword.keyword == ++ scan_keyword_targetblocksize && ++ !strcmp(DEFAULTBOOT_SECTION, name)) ++ targetblocksize = scan[i].content.keyword.value; ++ ++ if (scan[i].id == scan_id_keyword_assignment && ++ scan[i].content.keyword.keyword == ++ scan_keyword_targetoffset && ++ !strcmp(DEFAULTBOOT_SECTION, name)) ++ targetoffset = scan[i].content.keyword.value; ++ ++ if (scan[i].id == scan_id_keyword_assignment && + scan[i].content.keyword.keyword == scan_keyword_timeout) + timeout = scan[i].content.keyword.value; + } +@@ -1380,8 +1639,33 @@ create_fake_menu(struct scan_token *scan) + } + + default_target = misc_strdup(target); ++ if (targetbase != NULL) { ++ default_targetbase = misc_strdup(targetbase); ++ if (default_targetbase == NULL) ++ return NULL; ++ } ++ if (targettype != NULL) { ++ default_targettype = misc_strdup(targettype); ++ if (default_targettype == NULL) ++ return NULL; ++ } ++ if (targetgeometry != NULL) { ++ default_targetgeometry = misc_strdup(targetgeometry); ++ if (default_targetgeometry == NULL) ++ return NULL; ++ } ++ if (targetblocksize != NULL) { ++ default_targetblocksize = misc_strdup(targetblocksize); ++ if (default_targetblocksize == NULL) ++ return NULL; ++ } ++ if (targetoffset != NULL) { ++ default_targetoffset = misc_strdup(targetoffset); ++ if (default_targetoffset == NULL) ++ return NULL; ++ } + +- size = i+6+numsec; ++ size = i+11+numsec; + tmp = (struct scan_token *) misc_malloc(size * sizeof(struct scan_token)); + if (tmp == NULL) { + error_text("Couldn't allocate memory for menu entries"); +@@ -1408,6 +1692,46 @@ create_fake_menu(struct scan_token *scan) + scan[i].line = i; + scan[i].content.keyword.keyword = scan_keyword_target; + scan[i++].content.keyword.value = misc_strdup(target); ++ if ( targetbase) { ++ scan[i].id = scan_id_keyword_assignment; ++ scan[i].line = i; ++ scan[i].content.keyword.keyword = scan_keyword_targetbase; ++ scan[i++].content.keyword.value = misc_strdup(targetbase); ++ if (scan[i - 1].content.keyword.value == NULL) ++ return NULL; ++ } ++ if ( targettype) { ++ scan[i].id = scan_id_keyword_assignment; ++ scan[i].line = i; ++ scan[i].content.keyword.keyword = scan_keyword_targettype; ++ scan[i++].content.keyword.value = misc_strdup(targettype); ++ if (scan[i - 1].content.keyword.value == NULL) ++ return NULL; ++ } ++ if ( targetgeometry) { ++ scan[i].id = scan_id_keyword_assignment; ++ scan[i].line = i; ++ scan[i].content.keyword.keyword = scan_keyword_targetgeometry; ++ scan[i++].content.keyword.value = misc_strdup(targetgeometry); ++ if (scan[i - 1].content.keyword.value == NULL) ++ return NULL; ++ } ++ if ( targetblocksize) { ++ scan[i].id = scan_id_keyword_assignment; ++ scan[i].line = i; ++ scan[i].content.keyword.keyword = scan_keyword_targetblocksize; ++ scan[i++].content.keyword.value = misc_strdup(targetblocksize); ++ if (scan[i - 1].content.keyword.value == NULL) ++ return NULL; ++ } ++ if ( targetoffset) { ++ scan[i].id = scan_id_keyword_assignment; ++ scan[i].line = i; ++ scan[i].content.keyword.keyword = scan_keyword_targetoffset; ++ scan[i++].content.keyword.value = misc_strdup(targetoffset); ++ if (scan[i - 1].content.keyword.value == NULL) ++ return NULL; ++ } + scan[i].id = scan_id_keyword_assignment; + scan[i].line = i; + scan[i].content.keyword.keyword = scan_keyword_default; +diff --git a/zipl/src/scan.c b/zipl/src/scan.c +index caca3cf..16da9b3 100644 +--- a/zipl/src/scan.c ++++ b/zipl/src/scan.c +@@ -2,7 +2,7 @@ + * s390-tools/zipl/src/scan.c + * Scanner for zipl.conf configuration files + * +- * Copyright IBM Corp. 2001, 2006. ++ * Copyright IBM Corp. 2001, 2009. + * + * Author(s): Carsten Otte + * Peter Oberparleiter +@@ -31,21 +31,33 @@ enum scan_key_state scan_key_table[SCAN_SECTION_NUM][SCAN_KEYWORD_NUM] = { + /* defa dump dump imag para parm ramd segm targ prom time defa tape mv + * ult to tofs e mete file isk ent et pt out ultm dump + * rs enu ++ * ++ * targ targ targ targ targ ++ * etba etty etge etbl etof ++ * se pe omet ocks fset ++ * ry ize + */ + /* defaultboot */ +- {opt, inv, inv, inv, inv, inv, inv, inv, req, inv, opt, opt, inv, inv}, ++ {opt, inv, inv, inv, inv, inv, inv, inv, req, inv, opt, opt, inv, inv, ++ opt, opt, opt, opt, opt}, + /* ipl */ +- {inv, inv, inv, req, opt, opt, opt, inv, opt, inv, inv, inv, inv, inv}, ++ {inv, inv, inv, req, opt, opt, opt, inv, opt, inv, inv, inv, inv, inv, ++ opt, opt, opt, opt, opt}, + /* segment load */ +- {inv, inv, inv, inv, inv, inv, inv, req, req, inv, inv, inv, inv, inv}, ++ {inv, inv, inv, inv, inv, inv, inv, req, req, inv, inv, inv, inv, inv, ++ inv, inv, inv, inv, inv}, + /* part dump */ +- {inv, req, inv, inv, inv, inv, inv, inv, opt, inv, inv, inv, inv, inv}, ++ {inv, req, inv, inv, inv, inv, inv, inv, opt, inv, inv, inv, inv, inv, ++ inv, inv, inv, inv, inv}, + /* fs dump */ +- {inv, inv, req, inv, opt, opt, inv, inv, req, inv, inv, inv, inv, inv}, ++ {inv, inv, req, inv, opt, opt, inv, inv, req, inv, inv, inv, inv, inv, ++ inv, inv, inv, inv, inv}, + /* ipl tape */ +- {inv, inv, inv, req, opt, opt, opt, inv, inv, inv, inv, inv, req, inv}, ++ {inv, inv, inv, req, opt, opt, opt, inv, inv, inv, inv, inv, req, inv, ++ inv, inv, inv, inv, inv}, + /* multi volume dump */ +- {inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req} ++ {inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, ++ inv, inv, inv, inv, inv} + }; + + /* Mapping of keyword IDs to strings */ +@@ -63,13 +75,17 @@ static const struct { + { "parmfile", scan_keyword_parmfile }, + { "ramdisk", scan_keyword_ramdisk }, + { "segment", scan_keyword_segment }, ++ { "targetbase", scan_keyword_targetbase}, ++ { "targettype", scan_keyword_targettype}, ++ { "targetgeometry", scan_keyword_targetgeometry}, ++ { "targetblocksize", scan_keyword_targetblocksize}, ++ { "targetoffset", scan_keyword_targetoffset}, + { "target", scan_keyword_target}, + { "prompt", scan_keyword_prompt}, + { "timeout", scan_keyword_timeout}, + { "tape", scan_keyword_tape} + }; + +- + /* Retrieve name of keyword identified by ID. */ + char * + scan_keyword_name(enum scan_keyword_id id) +@@ -608,6 +624,19 @@ scan_get_section_type(char* keyword[]) + return section_invalid; + } + ++enum scan_target_type ++scan_get_target_type(char *type) ++{ ++ if (strcasecmp(type, "SCSI") == 0) ++ return target_type_scsi; ++ else if (strcasecmp(type, "FBA") == 0) ++ return target_type_fba; ++ else if (strcasecmp(type, "LDL") == 0) ++ return target_type_ldl; ++ else if (strcasecmp(type, "CDL") == 0) ++ return target_type_cdl; ++ return target_type_invalid; ++} + + #define MAX(a,b) ((a)>(b)?(a):(b)) + +@@ -643,9 +672,13 @@ scan_check_section_data(char* keyword[], int* line, char* name, + } else if (keyword[(int) scan_keyword_mvdump]) { + *type = section_mvdump; + main_keyword = scan_keyword_name(scan_keyword_mvdump); +- } else +- /* Incomplete section data */ ++ } else { ++ error_reason("Line %d: section '%s' must contain " ++ "either one of keywords 'image', " ++ "'segment', 'dumpto', 'dumptofs', " ++ "'mvdump' or 'tape'", section_line, name); + return -1; ++ } + } + /* Check keywords */ + for (i=0; i < SCAN_KEYWORD_NUM; i++) { +@@ -734,6 +767,174 @@ scan_check_section_data(char* keyword[], int* line, char* name, + } + + ++static int ++check_blocksize(int size) ++{ ++ switch (size) { ++ case 512: ++ case 1024: ++ case 2048: ++ case 4096: ++ return 0; ++ } ++ return -1; ++} ++ ++ ++int ++scan_check_target_data(char* keyword[], int* line) ++{ ++ int cylinders, heads, sectors; ++ char dummy; ++ int number; ++ enum scan_keyword_id errid; ++ ++ if ((keyword[(int) scan_keyword_targetbase] != 0) && ++ (keyword[(int) scan_keyword_target] == 0)) { ++ if (line != NULL) ++ error_reason("Line %d: keyword 'target' required " ++ "when specifying 'targetbase'", ++ line[(int) scan_keyword_targetbase]); ++ else ++ error_reason("Option 'target' required when " ++ "specifying 'targetbase'"); ++ return -1; ++ } ++ if (keyword[(int) scan_keyword_targetbase] == 0) { ++ if (keyword[(int) scan_keyword_targettype] != 0) ++ errid = scan_keyword_targettype; ++ else if ((keyword[(int) scan_keyword_targetgeometry] != 0)) ++ errid = scan_keyword_targetgeometry; ++ else if ((keyword[(int) scan_keyword_targetblocksize] != 0)) ++ errid = scan_keyword_targetblocksize; ++ else if ((keyword[(int) scan_keyword_targetoffset] != 0)) ++ errid = scan_keyword_targetoffset; ++ else ++ return 0; ++ if (line != NULL) ++ error_reason("Line %d: keyword 'targetbase' required " ++ "when specifying '%s'", ++ line[(int) errid], scan_keyword_name(errid)); ++ else ++ error_reason("Option 'targetbase' required when " ++ "specifying '%s'", ++ scan_keyword_name(errid)); ++ return -1; ++ } ++ if (keyword[(int) scan_keyword_targettype] == 0) { ++ if (line != NULL) ++ error_reason("Line %d: keyword 'targettype' " ++ "required when specifying 'targetbase'", ++ line[(int) scan_keyword_targetbase]); ++ else ++ error_reason("Option 'targettype' required " ++ "when specifying 'targetbase'"); ++ return -1; ++ } ++ switch (scan_get_target_type(keyword[(int) scan_keyword_targettype])) { ++ case target_type_cdl: ++ case target_type_ldl: ++ if ((keyword[(int) scan_keyword_targetgeometry] != 0)) ++ break; ++ if (line != NULL) ++ error_reason("Line %d: keyword 'targetgeometry' " ++ "required when specifying 'targettype' %s", ++ line[(int) scan_keyword_targettype], ++ keyword[(int) scan_keyword_targettype]); ++ else ++ error_reason("Option 'targetgeometry' required when " ++ "specifying 'targettype' %s", ++ keyword[(int) scan_keyword_targettype]); ++ return -1; ++ case target_type_scsi: ++ case target_type_fba: ++ if ((keyword[(int) scan_keyword_targetgeometry] == 0)) ++ break; ++ if (line != NULL) ++ error_reason("Line %d: keyword " ++ "'targetgeometry' not allowed for " ++ "'targettype' %s", ++ line[(int) scan_keyword_targetgeometry], ++ keyword[(int) scan_keyword_targettype]); ++ else ++ error_reason("Keyword 'targetgeometry' not " ++ "allowed for 'targettype' %s", ++ keyword[(int) scan_keyword_targettype]); ++ return -1; ++ case target_type_invalid: ++ if (line != NULL) ++ error_reason("Line %d: Unrecognized 'targettype' value " ++ "'%s'", ++ line[(int) scan_keyword_targettype], ++ keyword[(int) scan_keyword_targettype]); ++ else ++ error_reason("Unrecognized 'targettype' value '%s'", ++ keyword[(int) scan_keyword_targettype]); ++ return -1; ++ } ++ if (keyword[(int) scan_keyword_targetgeometry] != 0) { ++ if ((sscanf(keyword[(int) scan_keyword_targetgeometry], ++ "%d,%d,%d %c", &cylinders, &heads, §ors, &dummy) ++ != 3) || (cylinders <= 0) || (heads <= 0) || ++ (sectors <= 0)) { ++ if (line != NULL) ++ error_reason("Line %d: Invalid target geometry " ++ "'%s'", line[ ++ (int) scan_keyword_targetgeometry], ++ keyword[ ++ (int) scan_keyword_targetgeometry]); ++ else ++ error_reason("Invalid target geometry '%s'", ++ keyword[ ++ (int) scan_keyword_targetgeometry]); ++ return -1; ++ } ++ } ++ if (keyword[(int) scan_keyword_targetblocksize] == 0) { ++ if (line != NULL) ++ error_reason("Line %d: Keyword 'targetblocksize' " ++ "required when specifying 'targetbase'", ++ line[(int) scan_keyword_targetbase]); ++ else ++ error_reason("Option 'targetblocksize' required when " ++ "specifying 'targetbase'"); ++ return -1; ++ } ++ if ((sscanf(keyword[(int) scan_keyword_targetblocksize], "%d %c", ++ &number, &dummy) != 1) || check_blocksize(number)) { ++ if (line != NULL) ++ error_reason("Line %d: Invalid target blocksize '%s'", ++ line[(int) scan_keyword_targetblocksize], ++ keyword[(int) scan_keyword_targetblocksize]); ++ else ++ error_reason("Invalid target blocksize '%s'", ++ keyword[(int) scan_keyword_targetblocksize]); ++ return -1; ++ } ++ if (keyword[(int) scan_keyword_targetoffset] == 0) { ++ if (line != NULL) ++ error_reason("Line %d: Keyword 'targetoffset' " ++ "required when specifying 'targetbase'", ++ line[(int) scan_keyword_targetbase]); ++ else ++ error_reason("Option 'targetoffset' required when " ++ "specifying 'targetbase'"); ++ return -1; ++ } ++ if (sscanf(keyword[(int) scan_keyword_targetoffset], "%d %c", ++ &number, &dummy) != 1) { ++ if (line != NULL) ++ error_reason("Line %d: Invalid target offset '%s'", ++ line[(int) scan_keyword_targetoffset], ++ keyword[(int) scan_keyword_targetoffset]); ++ else ++ error_reason("Invalid target offset '%s'", ++ keyword[(int) scan_keyword_targetoffset]); ++ return -1; ++ } ++ return 0; ++} ++ + /* Check section at INDEX for compliance with config file rules. Upon success, + * return zero and advance INDEX to point to the end of the section. Return + * non-zero otherwise. */ +@@ -764,6 +965,7 @@ check_section(struct scan_token* scan, int* index) + else + type = section_invalid; + memset(keyword, 0, sizeof(keyword)); ++ memset(keyword_line, 0, sizeof(keyword_line)); + line = scan[i].line; + /* Account for keywords */ + for (i++; (int) scan[i].id != 0; i++) { +@@ -802,13 +1004,10 @@ check_section(struct scan_token* scan, int* index) + } + } + rc = scan_check_section_data(keyword, keyword_line, name, line, &type); +- /* Check for missing keyword */ +- if (type == section_invalid) { +- error_reason("Line %d: section '%s' must contain either one " +- "of keywords 'image', 'segment', 'dumpto', " +- "'dumptofs', 'mvdump' or 'tape'", line, name); +- return -1; +- } ++ if (rc) ++ return rc; ++ /* Check target data */ ++ rc = scan_check_target_data(keyword, keyword_line); + if (rc) + return rc; + /* Advance index to end of section */ +@@ -840,6 +1039,8 @@ find_num_assignment(struct scan_token* scan, int num, int offset) + static int + check_menu(struct scan_token* scan, int* index) + { ++ char* keyword[SCAN_KEYWORD_NUM]; ++ int keyword_line[SCAN_KEYWORD_NUM]; + enum scan_keyword_id key_id; + char* name; + char* str; +@@ -852,6 +1053,7 @@ check_menu(struct scan_token* scan, int* index) + int is_default; + int is_prompt; + int is_timeout; ++ int rc; + + i = *index; + name = scan[i].content.menu.name; +@@ -862,6 +1064,8 @@ check_menu(struct scan_token* scan, int* index) + scan[line].line, name); + return -1; + } ++ memset(keyword, 0, sizeof(keyword)); ++ memset(keyword_line, 0, sizeof(keyword_line)); + line = scan[i].line; + is_num = 0; + is_target = 0; +@@ -886,6 +1090,24 @@ check_menu(struct scan_token* scan, int* index) + } + is_target = 1; + break; ++ case scan_keyword_targetbase: ++ case scan_keyword_targettype: ++ case scan_keyword_targetgeometry: ++ case scan_keyword_targetblocksize: ++ case scan_keyword_targetoffset: ++ key_id = scan[i].content.keyword.keyword; ++ keyword_line[key_id] = scan[i].line; ++ /* Rule 5 */ ++ if (keyword[(int) key_id] != NULL) { ++ error_reason("Line %d: keyword '%s' " ++ "already specified", ++ scan[i].line, ++ scan_keyword_name(key_id)); ++ return -1; ++ } ++ keyword[(int) key_id] = ++ scan[i].content.keyword.value; ++ break; + case scan_keyword_default: + if (is_default) { + error_reason("Line %d: keyword '%s' " +@@ -1044,6 +1266,10 @@ check_menu(struct scan_token* scan, int* index) + name); + return -1; + } ++ /* Check target data */ ++ rc = scan_check_target_data(keyword, keyword_line); ++ if (rc) ++ return rc; + /* Advance index to end of menu section */ + *index = i - 1; + return 0; +diff --git a/zipl/src/zipl.c b/zipl/src/zipl.c +index 4d9fd36..3a4c18c 100644 +--- a/zipl/src/zipl.c ++++ b/zipl/src/zipl.c +@@ -2,7 +2,7 @@ + * s390-tools/zipl/src/zipl.c + * zSeries Initial Program Loader tool. + * +- * Copyright IBM Corp. 2001, 2006. ++ * Copyright IBM Corp. 2001, 2009. + * + * Author(s): Carsten Otte + * Peter Oberparleiter +@@ -41,7 +41,7 @@ int dry_run = 1; + static const char tool_name[] = "zipl: zSeries Initial Program Loader"; + + /* Copyright notice */ +-static const char copyright_notice[] = "Copyright IBM Corp. 2001, 2008"; ++static const char copyright_notice[] = "Copyright IBM Corp. 2001, 2009"; + + /* Usage information */ + static const char* usage_text[] = { +@@ -55,6 +55,11 @@ static const char* usage_text[] = { + "-c, --config CONFIGFILE Read configuration from CONFIGFILE", + "-t, --target TARGETDIR Write bootmap file to TARGETDIR and install", + " bootloader on device containing TARGETDIR", ++" --targetbase BASEDEVICE Install bootloader on BASEDEVICE", ++" --targettype TYPE Use device type: CDL, LDL, FBA, SCSI", ++" --targetgeometry C,H,S Use disk geometry: cylinders,heads,sectors", ++" --targetblocksize SIZE Use number of bytes per block", ++" --targetoffset OFFSET Use offset between logical and physical disk", + "-i, --image IMAGEFILE[,ADDR] Install Linux kernel image from IMAGEFILE", + "-r, --ramdisk RAMDISK[,ADDR] Install initial ramdisk from file RAMDISK", + "-p, --parmfile PARMFILE[,ADDR] Use kernel parmline stored in PARMFILE", +@@ -190,10 +195,12 @@ main(int argc, char* argv[]) + break; + case job_dump_partition: + /* Retrieve target device information */ +- rc = install_dump(job->data.dump.device, job->data.dump.mem); ++ rc = install_dump(job->data.dump.device, &job->target, ++ job->data.dump.mem); + break; + case job_mvdump: + rc = install_mvdump(job->data.mvdump.device, ++ &job->target, + job->data.mvdump.device_count, + job->data.mvdump.mem, + job->data.mvdump.force); +diff --git a/zipl/src/zipl_helper.device-mapper b/zipl/src/zipl_helper.device-mapper +new file mode 100644 +index 0000000..669f3e3 +--- /dev/null ++++ b/zipl/src/zipl_helper.device-mapper +@@ -0,0 +1,716 @@ ++#!/usr/bin/perl -w ++# ++# zipl_helper.device-mapper: print zipl parameters for a device-mapper device ++# ++# Copyright IBM Corp. 2009 ++# ++# Author(s): Peter Oberparleiter ++# ++# Usage: zipl_helper.device-mapper ++# ++# This tool attempts to obtain zipl parameters for a target directory located ++# on a device-mapper device. It assumes that the device-mapper table for this ++# device conforms to the following rules: ++# - directory is located on a device consisting of a single device-mapper ++# target ++# - only linear, mirror and multipath targets are supported ++# - supported physical device types are DASD and SCSI devices ++# - all of the device which contains the directory must be located on a single ++# physical device (which may be mirrorred or accessed through a multipath ++# target) ++# - any mirror in the device-mapper setup must include block 0 of the ++# physical device ++# ++ ++use strict; ++use File::Basename; ++ ++# Required tools ++our $dmsetup = "dmsetup"; ++our $mknod = "mknod"; ++our $dasdview = "dasdview"; ++our $blockdev = "blockdev"; ++ ++# Constants ++our $SECTOR_SIZE = 512; ++our $DASD_PARTN_MASK = 0x03; ++our $SCSI_PARTN_MASK = 0x0f; ++ ++# Internal constants ++our $DEV_TYPE_CDL = 0; ++our $DEV_TYPE_LDL = 1; ++our $DEV_TYPE_FBA = 2; ++our $DEV_TYPE_SCSI = 3; ++ ++our $TARGET_START = 0; ++our $TARGET_LENGTH = 1; ++our $TARGET_TYPE = 2; ++our $TARGET_DATA = 3; ++ ++our $TARGET_TYPE_LINEAR = 0; ++our $TARGET_TYPE_MIRROR = 1; ++our $TARGET_TYPE_MULTIPATH = 2; ++ ++our $LINEAR_MAJOR = 0; ++our $LINEAR_MINOR = 1; ++our $LINEAR_START_SECTOR = 2; ++ ++our $MIRROR_MAJOR = 0; ++our $MIRROR_MINOR = 1; ++our $MIRROR_START_SECTOR = 2; ++ ++our $MULTIPATH_MAJOR = 0; ++our $MULTIPATH_MINOR = 1; ++ ++sub get_physical_device($); ++sub get_major_minor($); ++sub get_table($$); ++sub get_linear_data($$); ++sub get_mirror_data($$); ++sub get_multipath_data($$); ++sub filter_table($$$); ++sub get_target_start($); ++sub get_target_major_minor($); ++sub create_temp_device_node($$$); ++sub get_blocksize($); ++sub get_dasd_info($); ++sub get_partition_start($); ++sub is_dasd($); ++sub get_partition_base($$$); ++sub get_device_characteristics($$); ++sub get_type_name($); ++sub check_for_mirror($@); ++sub get_target_base($$$$@); ++sub get_device_name($$); ++ ++my $phy_geometry; # Disk geometry of physical device ++my $phy_blocksize; # Blocksize of physical device ++my $phy_offset; # Offset in 512-byte sectors between start of physical ++ # device and start of filesystem ++my $phy_type; # Type of physical device ++my $phy_bootsectors; # Size of boot record in 512-byte sectors ++my $phy_partstart; # Partition offset of physical device ++my $phy_major; # Major device number of physical device ++my $phy_minor; # Minor device number of physical device ++my @target_list; # List of dm-targets between filesystem and physical ++ # device. ++my $base_major; # Major device number of base device. ++my $base_minor; # Minor device number of base device ++my $directory; # Command line parameter ++my $toolname; # Name of tool ++ ++# Start ++$toolname = basename($0); ++$directory = $ARGV[0]; ++if (!defined($directory)) { ++ die("Usage: $toolname \n"); ++} ++ ++# Determine physical (non-dm) device on which directory is located ++($phy_major, $phy_minor, $phy_offset, @target_list) = ++ get_physical_device($directory); ++# Determine type and characteristics of physical device ++($phy_type, $phy_blocksize, $phy_geometry, $phy_bootsectors, $phy_partstart) = ++ get_device_characteristics($phy_major, $phy_minor); ++ ++# Handle partitions ++if ($phy_partstart > 0) { ++ # Only the partition of the physical device is mapped so only the ++ # physical device can provide access to the boot record. ++ ($base_major, $base_minor) = ++ get_partition_base($phy_type, $phy_major, $phy_minor); ++ # Check for mirror ++ check_for_mirror(scalar(@target_list) - 1, @target_list); ++ # Adjust filesystem offset ++ $phy_offset += $phy_partstart * ($phy_blocksize / $SECTOR_SIZE); ++ $phy_partstart = 0; ++ # Update device geometry ++ (undef, undef, $phy_geometry, undef, undef) = ++ get_device_characteristics($base_major, $base_minor); ++} else { ++ # All of the device is mapped, so the base device is the top most ++ # dm device which provides access to boot sectors ++ ($base_major, $base_minor) = ++ get_target_base($phy_major, $phy_minor, 0, $phy_bootsectors, ++ @target_list); ++} ++ ++# Check for valid offset of file system ++if (($phy_offset % ($phy_blocksize / $SECTOR_SIZE)) != 0) { ++ die("Error: File system not aligned on physical block size\n"); ++} ++ ++# Print resulting information ++print("targetbase=$base_major:$base_minor\n"); ++print("targettype=".get_type_name($phy_type)."\n"); ++if (defined($phy_geometry)) { ++ print("targetgeometry=$phy_geometry\n"); ++} ++print("targetblocksize=$phy_blocksize\n"); ++print("targetoffset=".($phy_offset / ($phy_blocksize / $SECTOR_SIZE))."\n"); ++ ++exit(0); ++ ++# get_physical_device(directory) ++# Returns (phy_major, phy_minor, phy_offset, @target_list). ++# target_list: [target_data1, target_data2, ..., target_datan] ++# target_data: [major, minor, target] ++sub get_physical_device($) ++{ ++ my ($directory) = @_; ++ my $major; ++ my $minor; ++ my $table; ++ my $target; ++ my $start; ++ my $length; ++ my @target_list; ++ ++ # Get information about device containing filesystem ++ ($major, $minor) = get_major_minor($directory); ++ $table = get_table($major, $minor); ++ if (scalar(@$table) == 0) { ++ die("Error: Could not retrieve device-mapper information for ". ++ "device '".get_device_name($major, $minor)."'\n"); ++ } ++ # Filesystem must be on a single dm target ++ if (scalar(@$table) != 1) { ++ die("Error: Unsupported setup: Directory '$directory' is ". ++ "located on a multi-target device-mapper device\n"); ++ } ++ ++ $target = $table->[0]; ++ push(@target_list, [$major, $minor, $target]); ++ $start = $target->[$TARGET_START]; ++ $length = $target->[$TARGET_LENGTH]; ++ while (1) { ++ # Convert fs_start to offset on parent dm device ++ $start += get_target_start($target); ++ ($major, $minor) = get_target_major_minor($target); ++ $table = get_table($major, $minor); ++ if (scalar(@$table) == 0) { ++ # Found non-dm device ++ return ($major, $minor, $start, @target_list); ++ } ++ # Get target in parent table which contains filesystem ++ $table = filter_table($table, $start, $length); ++ if (scalar(@$table) != 1) { ++ die("Error: Unsupported setup: Could not map ". ++ "directory '$directory' to a single physical ". ++ "device\n"); ++ } ++ $target = $table->[0]; ++ push(@target_list, [$major, $minor, $target]); ++ # Convert fs_start to offset on parent target ++ $start -= $target->[$TARGET_START]; ++ } ++} ++ ++# get_major_minor(filename) ++# Returns: (device major, device minor) of the device containing the ++# specified file. ++sub get_major_minor($) ++{ ++ my ($filename) = @_; ++ my @stat; ++ my $dev; ++ my $major; ++ my $minor; ++ ++ @stat = stat($filename); ++ if (!@stat) { ++ die("Error: Could not stat '$filename'\n"); ++ } ++ $dev = $stat[0]; ++ $major = ($dev & 0xfff00) >> 8; ++ $minor = ($dev & 0xff) | (($dev >> 12) & 0xfff00); ++ ++ return ($major, $minor); ++} ++ ++# get_table(major, minor) ++# Returns: [target1, target2, ..., targetn] ++# target: [start, length, type, data] ++# data: linear_data|mirror_data|multipath_data ++sub get_table($$) ++{ ++ my ($major, $minor) = @_; ++ my @table; ++ my $dev_name = get_device_name($major, $minor); ++ local *HANDLE; ++ ++ open(HANDLE, "$dmsetup table -j $major -m $minor 2>/dev/null|") or ++ return undef; ++ while () { ++ if (!(/^(\d+)\s+(\d+)\s+(\S+)\s+(\S.*)$/)) { ++ die("Error: Unrecognized device-mapper table format ". ++ "for device '$dev_name'\n"); ++ } ++ my ($start, $length, $target_type, $args) = ($1, $2, $3, $4); ++ my $data; ++ my $type; ++ ++ if ($target_type eq "linear") { ++ $type = $TARGET_TYPE_LINEAR; ++ $data = get_linear_data($dev_name, $args); ++ } elsif ($target_type eq "mirror") { ++ $type = $TARGET_TYPE_MIRROR; ++ $data = get_mirror_data($dev_name, $args); ++ } elsif ($target_type eq "multipath") { ++ $type = $TARGET_TYPE_MULTIPATH; ++ $data = get_multipath_data($dev_name, $args); ++ } else { ++ die("Error: Unsupported setup: Unsupported ". ++ "device-mapper target type '$target_type' for ". ++ "device '$dev_name'\n"); ++ } ++ push(@table, [$start, $length, $type, $data]); ++ } ++ close(HANDLE); ++ return \@table; ++} ++ ++# get_linear_data(dev_name, args) ++# Returns: [major, minor, start_sector] ++sub get_linear_data($$) ++{ ++ my ($dev_name, $args) = @_; ++ ++ if (!($args =~ /^(\d+):(\d+)\s+(\d+)$/)) { ++ die("Error: Unrecognized device-mapper table format for ". ++ "device '$dev_name'\n"); ++ } ++ return [$1, $2, $3]; ++} ++ ++# get_mirror_data(dev_name, args) ++# Returns [[major1, minor1, start_sector1], [major2, minor2, start_sector2], ..] ++sub get_mirror_data($$) ++{ ++ my ($dev_name, $args) = @_; ++ my @argv = split(/\s+/, $args); ++ my @data; ++ my $offset; ++ ++ # Remove log_type + #logargs + logargs + #devs ++ splice(@argv, 0, $argv[1] + 3); ++ if (!@argv) { ++ goto out_error; ++ } ++ while (@argv) { ++ if (!($argv[0] =~ /^(\d+):(\d+)$/)) { ++ goto out_error; ++ } ++ push(@data, [$1, $2, $argv[1]]); ++ if (!defined($offset)) { ++ $offset = $argv[1]; ++ } elsif ($argv[1] != $offset) { ++ die("Error: Unsupported setup: Mirror target on ". ++ "device '$dev_name' contains entries with varying ". ++ "sector offsets\n"); ++ } ++ splice(@argv, 0, 2); ++ } ++ if (!scalar(@data)) { ++ goto out_error; ++ } ++ return \@data; ++ ++out_error: ++ die("Error: Unrecognized device-mapper table format for device ". ++ "'$dev_name'\n"); ++} ++ ++# get_multipath_data(dev_name, args) ++# Returns [[major1, minor1], [major2, minor2], ..] ++sub get_multipath_data($$) ++{ ++ my ($dev_name, $args) = @_; ++ my @argv = split(/\s+/, $args); ++ my @data; ++ ++ # Remove #features + features ++ splice(@argv, 0, $argv[0] + 1); ++ if (!@argv) { ++ goto out_error; ++ } ++ # Remove #handlerargs + handlerargs ++ splice(@argv, 0, $argv[0] + 1); ++ if (!@argv) { ++ goto out_error; ++ } ++ # Remove #pathgroups + pathgroup ++ splice(@argv, 0, 2); ++ while (@argv) { ++ # Remove pathselector + #selectorargs + selectorargs ++ splice(@argv, 0, 2 + $argv[1]); ++ if (!@argv) { ++ goto out_error; ++ } ++ my $num_paths = $argv[0]; ++ my $num_path_args = $argv[1]; ++ # Remove #paths + #pathargs ++ splice(@argv, 0, 2); ++ while ($num_paths-- > 0) { ++ if (!@argv) { ++ goto out_error; ++ } ++ if (!($argv[0] =~ /(\d+):(\d+)/)) { ++ goto out_error; ++ } ++ push(@data, [$1, $2]); ++ # Remove device + deviceargs ++ splice(@argv, 0, 1 + $num_path_args); ++ } ++ } ++ if (!@data) { ++ goto out_error; ++ } ++ return \@data; ++ ++out_error: ++ die("Error: Unrecognized device-mapper table format for device ". ++ "'$dev_name'\n"); ++} ++ ++# filter_table(table, start, length) ++# Returns table containing only targets between start and start + length - 1. ++sub filter_table($$$) ++{ ++ my ($table, $start, $length) = @_; ++ my $end = $start + $length - 1; ++ my @result; ++ my $target; ++ ++ foreach $target (@$table) { ++ my $target_start = $target->[$TARGET_START]; ++ my $target_end = $target_start + $target->[$TARGET_LENGTH] - 1; ++ ++ if (!(($target_end < $start) || ($target_start > $end))) { ++ push(@result, $target); ++ } ++ } ++ return \@result; ++} ++ ++# get_target_start(target) ++# Returns the start sector of target. ++sub get_target_start($) ++{ ++ my ($target) = @_; ++ my $type = $target->[$TARGET_TYPE]; ++ my $data = $target->[$TARGET_DATA]; ++ ++ if ($type == $TARGET_TYPE_LINEAR) { ++ return $data->[$LINEAR_START_SECTOR]; ++ } elsif ($type == $TARGET_TYPE_MIRROR) { ++ my $mirror_data = $data->[0]; ++ return $mirror_data->[$MIRROR_START_SECTOR]; ++ } else { ++ return 0; ++ } ++} ++ ++# get_target_major_minor(target) ++# Returns (major, minor) of target of target. ++sub get_target_major_minor($) ++{ ++ my ($target) = @_; ++ my $type = $target->[$TARGET_TYPE]; ++ my $data = $target->[$TARGET_DATA]; ++ my $major; ++ my $minor; ++ ++ if ($type == $TARGET_TYPE_LINEAR) { ++ $major = $data->[$LINEAR_MAJOR]; ++ $minor = $data->[$LINEAR_MINOR]; ++ } elsif ($type == $TARGET_TYPE_MIRROR) { ++ # Use data of first device in list ++ my $mirror_data = $data->[0]; ++ $major = $mirror_data->[$MIRROR_MAJOR]; ++ $minor = $mirror_data->[$MIRROR_MINOR]; ++ } elsif ($type == $TARGET_TYPE_MULTIPATH) { ++ # Use data of first device in list ++ my $multipath_data = $data->[0]; ++ $major = $multipath_data->[$MULTIPATH_MAJOR]; ++ $minor = $multipath_data->[$MULTIPATH_MINOR]; ++ } ++ return ($major, $minor); ++} ++ ++# create_temp_device_node(type, major, minor) ++# Returns the name of a temporary device node. ++sub create_temp_device_node($$$) ++{ ++ my ($type, $major, $minor) = @_; ++ my $path = "/dev"; ++ my $name; ++ my $num; ++ ++ for ($num = 0; $num < 100; $num++) { ++ $name = sprintf("$path/zipl-dm-temp-%02d", $num); ++ if (-e $name) { ++ next; ++ } ++ if (system("$mknod $name $type $major $minor --mode 0600 ". ++ "2>/dev/null")) { ++ next; ++ } ++ return $name; ++ } ++ die("Error: Could not create temporary device node in '$path'\n"); ++} ++ ++# get_blocksize(device) ++# # Return blocksize in bytes for device. ++sub get_blocksize($) ++{ ++ my ($dev) = @_; ++ my $blocksize; ++ local *HANDLE; ++ ++ open(HANDLE, "$blockdev --getss $dev 2>/dev/null|") or ++ return undef; ++ $blocksize = ; ++ chomp($blocksize); ++ close(HANDLE); ++ ++ return $blocksize; ++} ++ ++# get_dasd_info(device) ++# Returns (type, cylinders, heads, sectors) ++sub get_dasd_info($) ++{ ++ my ($dev) = @_; ++ my $disk_type; ++ my $format; ++ my $cyl; ++ my $heads; ++ my $sectors; ++ my $type; ++ local *HANDLE; ++ ++ open(HANDLE, "$dasdview -x -f $dev 2>/dev/null|") or ++ # dasdview returned with an error ++ return undef; ++ while () { ++ if (/^number of cylinders.*\s(\d+)\s*$/) { ++ $cyl = $1; ++ } elsif (/^tracks per cylinder.*\s(\d+)\s*$/) { ++ $heads = $1; ++ } elsif (/^blocks per track.*\s(\d+)\s*$/) { ++ $sectors = $1; ++ } elsif (/^type\s+:\s+(\S+)\s*$/) { ++ $disk_type = $1; ++ } elsif (/^format.*\s+dec\s(\d+)\s/) { ++ $format = $1; ++ } ++ } ++ close(HANDLE); ++ if (!defined($cyl) || !defined($heads) || !defined($sectors) || ++ !defined($disk_type) || !defined($format)) { ++ # Unrecognized dadsview output format ++ return undef; ++ } ++ if ($disk_type eq "FBA") { ++ $type = $DEV_TYPE_FBA; ++ } elsif ($disk_type eq "ECKD") { ++ if ($format == 1) { ++ $type = $DEV_TYPE_LDL; ++ } elsif ($format == 2) { ++ $type = $DEV_TYPE_CDL; ++ } ++ } ++ ++ return ($type, $cyl, $heads, $sectors); ++} ++ ++# get_partition_start(device) ++# Return the partition offset of device. ++sub get_partition_start($) ++{ ++ my ($dev) = @_; ++ my $line; ++ my $offset; ++ local *HANDLE; ++ ++ open(HANDLE, "$blockdev --report $dev 2>/dev/null|") or ++ return undef; ++ $line = ; ++ if ($line =~ /RO\s+RA\s+SSZ\s+BSZ\s+StartSec\s+Size\s+Device/) { ++ $line = ; ++ if ($line =~ /^\S+\s+\d+\s+\d+\s+\d+\s+(\d+)/) { ++ $offset = $1; ++ } ++ } ++ close(HANDLE); ++ return $offset; ++} ++ ++# is_dasd(type) ++# Return whether disk with type is a DASD. ++sub is_dasd($) ++{ ++ my ($type) = @_; ++ ++ return ($type == $DEV_TYPE_CDL) || ($type == $DEV_TYPE_LDL) || ++ ($type == $DEV_TYPE_FBA); ++} ++ ++# get_partition_base(type, major, minor) ++# Return (major, minor) of the base device on which the partition is located. ++sub get_partition_base($$$) ++{ ++ my ($type, $major, $minor) = @_; ++ ++ if (is_dasd($type)) { ++ return ($major, $minor & ~$DASD_PARTN_MASK); ++ } else { ++ return ($major, $minor & ~$SCSI_PARTN_MASK); ++ } ++} ++ ++# get_device_characteristics(major, minor) ++# Returns (type, blocksize, geometry, bootsectors, partstart) for device. ++sub get_device_characteristics($$) ++{ ++ my ($major, $minor) = @_; ++ my $dev; ++ my $blocksize; ++ my $type; ++ my $cyl; ++ my $heads; ++ my $sectors; ++ my $geometry; ++ my $bootsectors; ++ my $partstart; ++ ++ $dev = create_temp_device_node("b", $major, $minor); ++ $blocksize = get_blocksize($dev); ++ if (!defined($blocksize)) { ++ unlink($dev); ++ die("Error: Could not get block size for ". ++ get_device_name($major, $minor)."\n"); ++ } ++ ($type, $cyl, $heads, $sectors) = get_dasd_info($dev); ++ if (defined($type)) { ++ $geometry = "$cyl,$heads,$sectors"; ++ if ($type == $DEV_TYPE_CDL) { ++ # First track contains IPL records ++ $bootsectors = $blocksize * $sectors / $SECTOR_SIZE; ++ } elsif ($type == $DEV_TYPE_LDL) { ++ # First two blocks contain IPL records ++ $bootsectors = $blocksize * 2 / $SECTOR_SIZE; ++ } elsif ($type == $DEV_TYPE_FBA) { ++ # First block contains IPL records ++ $bootsectors = $blocksize / $SECTOR_SIZE; ++ } ++ } else { ++ # Assume SCSI if get_dasd_info failed ++ $type = $DEV_TYPE_SCSI; ++ # First block contains IPL records ++ $bootsectors = $blocksize / $SECTOR_SIZE; ++ } ++ $partstart = get_partition_start($dev); ++ unlink($dev); ++ if (!defined($partstart)) { ++ die("Error: Could not determine partition start for ". ++ get_device_name($major, $minor)."\n"); ++ } ++ return ($type, $blocksize, $geometry, $bootsectors, $partstart); ++} ++ ++# get_type_name(type) ++# Return textual representation of device type. ++sub get_type_name($) ++{ ++ my ($type) = @_; ++ ++ if ($type == $DEV_TYPE_CDL) { ++ return "CDL"; ++ } elsif ($type == $DEV_TYPE_LDL) { ++ return "LDL"; ++ } elsif ($type == $DEV_TYPE_FBA) { ++ return "FBA"; ++ } elsif ($type == $DEV_TYPE_SCSI) { ++ return "SCSI"; ++ } ++ return undef; ++} ++ ++ ++# check_for_mirror(index, target_list) ++# Die if there is a mirror target between index and 0. ++sub check_for_mirror($@) ++{ ++ my ($i, @target_list) = @_; ++ ++ for (;$i >= 0; $i--) { ++ my $entry = $target_list[$i]; ++ my ($major, $minor, $target) = @$entry; ++ ++ if ($target->[$TARGET_TYPE] == $TARGET_TYPE_MIRROR) { ++ # IPL records are not mirrored. ++ die("Error: Unsupported setup: Block 0 is not ". ++ "mirrored in device '". ++ get_device_name($major, $minor)."'\n"); ++ } ++ } ++} ++ ++# get_target_base(bottom_major, bottom_minor, start, length, target_list) ++# Return (major, minor) for the top most target in the target list that maps ++# the region on (bottom_major, bottom_minor) defined by start and length at ++# offset 0. ++sub get_target_base($$$$@) ++{ ++ my ($bot_major, $bot_minor, $start, $length, @target_list) = @_; ++ my $entry; ++ my $top_major; ++ my $top_minor; ++ my $i; ++ ++ # Pre-initialize with bottom major-minor ++ $top_major = $bot_major; ++ $top_minor = $bot_minor; ++ # Process all entries starting with the last one ++ for ($i = scalar(@target_list) - 1; $i >= 0; $i--) { ++ my $entry = $target_list[$i]; ++ my ($major, $minor, $target) = @$entry; ++ ++ if (($target->[$TARGET_START] != 0) || ++ (get_target_start($target) != 0) || ++ ($target->[$TARGET_LENGTH] < $length)) { ++ last; ++ } ++ $top_major = $major; ++ $top_minor = $minor; ++ } ++ # Check for mirrorring between base device and fs device. ++ check_for_mirror($i, @target_list); ++ return ($top_major, $top_minor); ++} ++ ++# get_device_name(major, minor) ++# Return the name of the device specified by major and minor. ++sub get_device_name($$) ++{ ++ my ($major, $minor) = @_; ++ my $name; ++ local *HANDLE; ++ ++ $name = "$major:$minor"; ++ open(HANDLE, ") { ++ if (/^\s*(\d+)\s+(\d+)\s+\d+\s+(\S+)\s*$/) { ++ if (($major == $1) && ($minor == $2)) { ++ $name = $3; ++ last; ++ } ++ } ++ } ++ close(HANDLE); ++out: ++ return $name; ++} +-- +1.6.3.3 + diff --git a/0016-s390tools-1.8.2-lsreipl-nss.patch b/0016-s390tools-1.8.2-lsreipl-nss.patch new file mode 100644 index 0000000..e2b3413 --- /dev/null +++ b/0016-s390tools-1.8.2-lsreipl-nss.patch @@ -0,0 +1,376 @@ +From 62fb535a68f1df693869e4361150259b42c6f211 Mon Sep 17 00:00:00 2001 +From: =?utf-8?q?Dan=20Hor=C3=A1k?= +Date: Thu, 10 Dec 2009 18:30:52 +0100 +Subject: [PATCH 16/16] s390tools-1.8.2-lsreipl-nss + +--- + ipl_tools/ccw.c | 42 ++++++++++++++++--------------------- + ipl_tools/chreipl.h | 9 +++++-- + ipl_tools/fcp.c | 13 ++++++----- + ipl_tools/ipl.c | 57 +++++++++++++++++++++++++++++++------------------- + ipl_tools/main.c | 25 +++++++++++++++++----- + ipl_tools/system.c | 3 +- + 6 files changed, 87 insertions(+), 62 deletions(-) + +diff --git a/ipl_tools/ccw.c b/ipl_tools/ccw.c +index 7959831..eef4550 100644 +--- a/ipl_tools/ccw.c ++++ b/ipl_tools/ccw.c +@@ -52,22 +52,19 @@ int isccwdev(const char *devno) + } + + +-int get_ccw_devno_old_sysfs(char *device, char *devno) ++int get_ccw_devno_old_sysfs(char *device) + { + FILE *filp; +- int len, errorpath, rc; ++ int errorpath; + char path1[4096]; + char buf[4096]; +- char *match, *s1, *s2; ++ char *match = NULL, *s1, *s2; + + errorpath = 1; +- rc = 0; + sprintf(path1, "/sys/block/%s/uevent", device); + filp = fopen(path1, "r"); +- if (!filp) { +- rc = -1; +- return rc; +- } ++ if (!filp) ++ return -1; + /* + * the uevent file contains an entry like this: + * PHYSDEVPATH=/devices/css0/0.0.206a/0.0.7e78 +@@ -77,16 +74,16 @@ int get_ccw_devno_old_sysfs(char *device, char *devno) + if (match != NULL) + break; + } ++ fclose(filp); ++ if (!match) ++ return -1; + s1 = strchr(buf, '/'); + s2 = strrchr(buf, '/'); +- len = s2-s1; +- strncpy(devno, s2 + 1, sizeof(devno)); +- devno[len] = '\0'; +- fclose(filp); ++ strncpy(devno, s2 + 1, sizeof(devno) - 1); + return 0; + } + +-int get_ccw_devno_new_sysfs(char *device, char *devno) ++int get_ccw_devno_new_sysfs(char *device) + { + int len, errorpath, rc; + char path2[4096]; +@@ -119,8 +116,7 @@ int get_ccw_devno_new_sysfs(char *device, char *devno) + return rc; + } + } +- strncpy(devno, s2 + 1, sizeof(devno)); +- devno[len] = '\0'; ++ strncpy(devno, s2 + 1, sizeof(devno) - 1); + return 0; + } + +@@ -134,16 +130,14 @@ int get_ccw_devno_new_sysfs(char *device, char *devno) + * + * This does not work when booting from tape + */ +-int get_ccw_devno(char *device, char *devno) ++int get_ccw_devno(char *device) + { +- if (get_ccw_devno_old_sysfs(device, devno) != 0) { +- if (get_ccw_devno_new_sysfs(device, devno) != 0) { +- fprintf(stderr, "%s: Failed to lookup the device number\n", +- name); +- return -1; +- } +- } +- return 0; ++ if (get_ccw_devno_old_sysfs(device) == 0) ++ return 0; ++ if (get_ccw_devno_new_sysfs(device) == 0) ++ return 0; ++ fprintf(stderr, "%s: Failed to lookup the device number\n", name); ++ return -1; + } + + int get_ccw_dev(char *partition, char *device) +diff --git a/ipl_tools/chreipl.h b/ipl_tools/chreipl.h +index 19a83eb..37807a7 100644 +--- a/ipl_tools/chreipl.h ++++ b/ipl_tools/chreipl.h +@@ -34,6 +34,9 @@ extern char saction[8]; + extern char name[256]; + extern int action; + ++#define IPL_TYPE_LEN_MAX 100 ++#define NSS_NAME_LEN_MAX 8 ++ + #define ACT_CCW 1 + #define ACT_FCP 2 + #define ACT_NODE 3 +@@ -63,10 +66,10 @@ int is_valid_case(char *c); + int is_valid_action(char *action); + void parse_shutdown_options(int argc, char **argv); + void strlow(char *s); +-int get_ccw_devno(char *device, char *devno); +-int get_reipl_type(void); ++int get_ccw_devno(char *device); ++int get_reipl_type(char *reipltype); + void parse_lsreipl_options(int argc, char **argv); +-int get_ipl_type(); ++int get_ipl_type(char *reipltype); + int get_ipl_loadparam(void); + void print_ipl_settings(void); + int get_sa(char *action, char *file); +diff --git a/ipl_tools/fcp.c b/ipl_tools/fcp.c +index 86fa95b..7a8b4c5 100644 +--- a/ipl_tools/fcp.c ++++ b/ipl_tools/fcp.c +@@ -33,12 +33,11 @@ + + /* + * return the current reipl type from /sys/firmware/reipl/reipl_type +- * 0 = fcp, 1 = ccw, -1, error ++ * 0 = fcp, 1 = ccw, 2 = nss, -1 = unknown + */ +-int get_reipl_type(void) ++int get_reipl_type(char *reipltype) + { + FILE *filp; +- char reipltype[4]; + char path[4096]; + int rc; + +@@ -49,7 +48,7 @@ int get_reipl_type(void) + fprintf(stderr, "%s: Can not open /sys/firmware/" + "reipl/reipl_type: ", name); + fprintf(stderr, "%s\n", strerror(errno)); +- return -1; ++ exit(1); + } + rc = fscanf(filp, "%s", reipltype); + fclose(filp); +@@ -57,17 +56,19 @@ int get_reipl_type(void) + fprintf(stderr, "%s: Failed to read " + "/sys/firmware/reipl/reipl_type:", name); + fprintf(stderr, "%s\n", strerror(errno)); +- return -1; ++ exit(1); + } + if (strncmp(reipltype, "fcp", strlen("fcp")) == 0) + return T_FCP; + else if (strncmp(reipltype, "ccw", strlen("ccw")) == 0) + return T_CCW; +- /* TODO: add NSS support */ ++ else if (strncmp(reipltype, "nss", strlen("nss")) == 0) ++ return T_NSS; + } else { + fprintf(stderr, "%s: Can not open /sys/firmware/reipl/" + "reipl_type:", name); + fprintf(stderr, " %s\n", strerror(errno)); ++ exit(1); + } + return -1; + } +diff --git a/ipl_tools/ipl.c b/ipl_tools/ipl.c +index 2bf817a..8cca700 100644 +--- a/ipl_tools/ipl.c ++++ b/ipl_tools/ipl.c +@@ -33,12 +33,11 @@ + + /* + * return the ipl type based on /sys/firmware/ipl/ipl_type +- * returns 0 in case of fcp and 1 in case of ccw, -1 otherwise ++ * returns 0 in case of fcp and 1 in case of ccw, 2 for nss and -1 otherwise + */ +-int get_ipl_type() ++int get_ipl_type(char *reipltype) + { + FILE *filp; +- char reipltype[4]; + char path[4096]; + int rc; + +@@ -49,7 +48,7 @@ int get_ipl_type() + fprintf(stderr, "%s: Can not open /sys/firmware/ipl/" + "ipl_type: ", name); + fprintf(stderr, "%s\n", strerror(errno)); +- return -1; ++ exit(1); + } + rc = fscanf(filp, "%s", reipltype); + fclose(filp); +@@ -57,12 +56,14 @@ int get_ipl_type() + fprintf(stderr, "%s: Failed to read " + "/sys/firmware/ipl/ipl_type: ", name); + fprintf(stderr, "%s\n", strerror(errno)); +- return -1; ++ exit(1); + } + if (strncmp(reipltype, "fcp", strlen("fcp")) == 0) + return T_FCP; + else if (strncmp(reipltype, "ccw", strlen("ccw")) == 0) + return T_CCW; ++ else if (strncmp(reipltype, "nss", strlen("nss")) == 0) ++ return T_NSS; + } else { + fprintf(stderr, "%s: Can not open /sys/firmware/ipl/" + "ipl_type:", name); +@@ -111,30 +112,38 @@ int get_ipl_loadparam(void) + void print_ipl_settings(void) + { + int rc, type; +- char bootprog[1024], lba[1024]; ++ char bootprog[1024], lba[1024], nss_name[NSS_NAME_LEN_MAX + 1]; ++ char reipltype[IPL_TYPE_LEN_MAX + 1]; + +- type = get_ipl_type(); +- /* +- * TODO: add nss support +- */ +- if (type == 1) ++ type = get_ipl_type(reipltype); ++ switch (type) { ++ case T_NSS: ++ printf("IPL type: nss\n"); ++ rc = strrd(nss_name, "/sys/firmware/ipl/name"); ++ if (rc != 0) ++ exit(1); ++ printf("Name: %s\n", nss_name); ++ break; ++ case T_CCW: + printf("IPL type: ccw\n"); +- if (type == 0) +- printf("IPL type: fcp\n"); +- rc = strrd(devno, "/sys/firmware/ipl/device"); +- if (rc != 0) +- exit(1) /* the error msg comes from get_ipl_device */; +- if (strlen(devno) > 0) +- printf("Device: %s\n", devno); +- if (type == 1) { ++ rc = strrd(devno, "/sys/firmware/ipl/device"); ++ if (rc != 0) ++ exit(1); ++ if (strlen(devno) > 0) ++ printf("Device: %s\n", devno); + rc = get_ipl_loadparam(); + if (rc != -1) + printf("Loadparm: %d\n", rc); + else + printf("Loadparm: \n"); +- } +- if (type == 0) { +- /* these settings are only available for fcp */ ++ break; ++ case T_FCP: ++ printf("IPL type: fcp\n"); ++ rc = strrd(devno, "/sys/firmware/ipl/device"); ++ if (rc != 0) ++ exit(1); ++ if (strlen(devno) > 0) ++ printf("Device: %s\n", devno); + rc = strrd(wwpn, "/sys/firmware/reipl/fcp/wwpn"); + if (rc != -1 && strlen(wwpn) > 0) + printf("WWPN: %s\n", wwpn); +@@ -147,6 +156,10 @@ void print_ipl_settings(void) + rc = strrd(lba, "/sys/firmware/ipl/br_lba"); + if (rc != -1 && strlen(lba) > 0) + printf("br_lba: %s\n", lba); ++ break; ++ default: ++ printf("IPL type: %s (unknown)\n", reipltype); ++ break; + } + exit(0); + } +diff --git a/ipl_tools/main.c b/ipl_tools/main.c +index 2eaa043..bcebabe 100644 +--- a/ipl_tools/main.c ++++ b/ipl_tools/main.c +@@ -57,13 +57,22 @@ int action; /* either CCW, FCP or NODE */ + int lsreipl(int argc, char *argv[]) + { + int rc; +- char bootprog[1024], lba[1024], val[9]; ++ char bootprog[1024], lba[1024], val[9], reipltype[IPL_TYPE_LEN_MAX + 1]; ++ char nss_name[NSS_NAME_LEN_MAX + 1]; + + /* parse the command line options in getop.c */ + parse_lsreipl_options(argc, argv); + +- rc = get_reipl_type(); +- if (rc == 0) { ++ rc = get_reipl_type(reipltype); ++ switch (rc) { ++ case T_NSS: ++ printf("Re-IPL type: nss\n"); ++ rc = strrd(nss_name, "/sys/firmware/reipl/nss/name"); ++ if (rc != 0) ++ exit(1); ++ printf("Name: %s\n", nss_name); ++ break; ++ case T_FCP: + printf("Re-IPL type: fcp\n"); + rc = strrd(wwpn, "/sys/firmware/reipl/fcp/wwpn"); + if (rc != 0) +@@ -90,8 +99,8 @@ int lsreipl(int argc, char *argv[]) + printf("bootprog: %s\n", bootprog); + if (strlen(lba) > 0) + printf("br_lba: %s\n", lba); +- } +- if (rc == 1) { ++ break; ++ case T_CCW: + printf("Re-IPL type: ccw\n"); + rc = strrd(devno, "/sys/firmware/reipl/ccw/device"); + if (rc != 0) +@@ -106,6 +115,10 @@ int lsreipl(int argc, char *argv[]) + printf("Loadparm: %s\n", val); + else + printf("Loadparm: \n"); ++ break; ++ default: ++ printf("Re-IPL type: %s (unknown)\n", reipltype); ++ break; + } + return 0; + } +@@ -134,7 +147,7 @@ int reipl(int argc, char *argv[]) + "partition: %s\n", name, partition); + exit(1); + } +- rc = get_ccw_devno(device, devno); ++ rc = get_ccw_devno(device); + if (rc != 0) { + fprintf(stderr, "%s: Unable to lookup device" + " number for device %s\n", name, +diff --git a/ipl_tools/system.c b/ipl_tools/system.c +index fd5b76b..ca6c5af 100644 +--- a/ipl_tools/system.c ++++ b/ipl_tools/system.c +@@ -174,7 +174,7 @@ int strrd(char *string, char *file) + fprintf(stderr, "%s\n", strerror(errno)); + exit(1); + } +- rc = fread(string, 4096, 1, filp); ++ rc = fread(string, 1, 4096, filp); + fclose(filp); + /* + * special handling is required for +@@ -193,6 +193,7 @@ int strrd(char *string, char *file) + fprintf(stderr, "%s\n", strerror(errno)); + return -1; + } else { ++ string[rc] = 0; + if (string[strlen(string) - 1] == '\n') + string[strlen(string) - 1] = 0; + return 0; +-- +1.6.3.3 + diff --git a/s390utils.spec b/s390utils.spec index d58bcaa..c6a7622 100644 --- a/s390utils.spec +++ b/s390utils.spec @@ -46,6 +46,8 @@ Patch11: 0011-update-readahead-value-for-better-performance.patch Patch12: 0012-fix-multipath-device-detection-in-ziomon.patch Patch13: 0013-zipl-handle-status-during-ipl.patch Patch14: 0014-dasdview-fdasd-fix-floating-point-error-for-unformat.patch +Patch15: 0015-s390tools-1.8.2-zipl-dm.patch +Patch16: 0016-s390tools-1.8.2-lsreipl-nss.patch Patch100: cmsfs-1.1.8-warnings.patch Patch101: cmsfs-1.1.8-kernel26.patch @@ -117,6 +119,12 @@ be used together with the zSeries (s390) Linux kernel and device drivers. # Fix floating point error for unformatted devices in fdasd and dasdview (#537144) %patch14 -p1 -b .dasd-zero-division +# Add device-mapper support into zipl (#546280) +%patch15 -p1 -b .zipl-dm + +# Add missing check and print NSS name in case an NSS has been IPLed (#546297) +%patch16 -p1 -b .lsreipl-nss + # # cmsfs # @@ -798,9 +806,11 @@ User-space development files for the s390/s390x architecture. %changelog -* Tue Dec 1 2009 Dan Horák 2:1.8.2-6 +* Thu Dec 10 2009 Dan Horák 2:1.8.2-6 - fixed return value in cpi initscript (#541389) - updated zfcpconf.sh script from dracut +- added device-mapper support into zipl (#546280) +- added missing check and print NSS name in case an NSS has been IPLed (#546297) * Fri Nov 13 2009 Dan Horák 2:1.8.2-5 - added multiple fixes from IBM (#533955, #537142, #537144)