s390utils/0015-s390tools-1.8.2-zipl-d...

2717 lines
83 KiB
Diff

From e089f907d7ba7f18479eaff61852171842a219e2 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
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 <cotte@de.ibm.com>
* Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
@@ -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 <cotte@de.ibm.com>
* Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
@@ -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 <cotte@de.ibm.com>
* Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
@@ -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 <cotte@de.ibm.com>
* Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
@@ -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=<BASE DEVICE>"
+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=<TARGET TYPE>"
+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=<CYLINDERS,HEADS,SECTORS>"
+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=<SIZE>"
+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=<OFFSET>"
+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 <TAPE DEVICE>" " or " "\-\-tape=<TAPE DEVICE>"
Install bootloader on the specified <TAPE DEVICE>. 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 <cotte@de.ibm.com>
* Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
@@ -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 <cotte@de.ibm.com>
* Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
*/
#include "disk.h"
+#include "job.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -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 <cotte@de.ibm.com>
* Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
@@ -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 <cotte@de.ibm.com>
* Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
@@ -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 <cotte@de.ibm.com>
* Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
@@ -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, &sectors, &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 <cotte@de.ibm.com>
* Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
@@ -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 <peter.oberparleiter@de.ibm.com>
+#
+# Usage: zipl_helper.device-mapper <target directory>
+#
+# 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 <target directory>\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 (<HANDLE>) {
+ 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 = <HANDLE>;
+ 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 (<HANDLE>) {
+ 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 = <HANDLE>;
+ if ($line =~ /RO\s+RA\s+SSZ\s+BSZ\s+StartSec\s+Size\s+Device/) {
+ $line = <HANDLE>;
+ 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, "</proc/partitions") or goto out;
+ while (<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