e47d5e3f02
- zipl/src: add basic support for multiple target base disks - s390-tools-01-zipl-src-add-basic-support-for-multiple-target-base-disks.patch - s390-tools-02-zipl-src-add-basic-support-for-multiple-target-base-disks.patch OBS-URL: https://build.opensuse.org/package/show/Base:System/s390-tools?expand=0&rev=224
970 lines
28 KiB
Diff
970 lines
28 KiB
Diff
From d6b702d5791b47f735960ad1f6986e0a32768df6 Mon Sep 17 00:00:00 2001
|
|
From: Eduard Shishkin <edward6@linux.ibm.com>
|
|
Date: Thu, 11 Jul 2024 10:43:37 +0200
|
|
Subject: [PATCH] zipl/src: add basic support for multiple target base disks
|
|
|
|
. Modify disk_get_info() to process multiple sets of target parameters
|
|
provided by the helper script and store it in the array of "targets"
|
|
of the structure job_target_data;
|
|
. Besides the logical device, maintain an array of physical base disks
|
|
in the disk_info structure;
|
|
. Use the logical target device only to create bootmap (it is
|
|
automatically mirrored by the respective linux driver (dm, or md)
|
|
managing the mirrored target). In contrast, install bootstrap blocks
|
|
to each physical base disk individually, bypassing that driver;
|
|
. Report in verbose mode on which base disks the bootstrap
|
|
installation was performed;
|
|
. Use the following logic of setting @info->device (which is printed
|
|
as "Device...:" in verbose mode):
|
|
. source_auto - the target base disk is set;
|
|
. source_script - the target (logical) device is set;
|
|
. source_user - the device specified by user (via --targetbase
|
|
option), or config file is set.
|
|
|
|
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
|
|
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
|
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
|
---
|
|
zipl/include/disk.h | 14 +-
|
|
zipl/include/install.h | 2
|
|
zipl/include/job.h | 122 ++++++++++++++++++--
|
|
zipl/include/zipl.h | 1
|
|
zipl/src/bootmap.c | 23 ++-
|
|
zipl/src/disk.c | 295 +++++++++++++++++++++++++++++++++++++------------
|
|
zipl/src/install.c | 89 +++++++++-----
|
|
zipl/src/job.c | 82 +++++++------
|
|
8 files changed, 469 insertions(+), 159 deletions(-)
|
|
|
|
--- a/zipl/include/disk.h
|
|
+++ b/zipl/include/disk.h
|
|
@@ -56,13 +56,14 @@
|
|
/* targetbase definition */
|
|
typedef enum {
|
|
defined_as_device,
|
|
- defined_as_name
|
|
+ defined_as_name,
|
|
+ undefined
|
|
} definition_t;
|
|
|
|
/* Disk information type */
|
|
struct disk_info {
|
|
disk_type_t type;
|
|
- dev_t device;
|
|
+ dev_t device; /* logical device for bootmap creation */
|
|
dev_t partition;
|
|
int devno;
|
|
int partnum;
|
|
@@ -72,8 +73,11 @@
|
|
struct hd_geometry geo;
|
|
char* name;
|
|
char* drv_name;
|
|
- definition_t targetbase;
|
|
+ definition_t targetbase_def;
|
|
int is_nvme;
|
|
+ dev_t basedisks[MAX_TARGETS]; /* array of physical disks for
|
|
+ * bootstrap blocks recording
|
|
+ */
|
|
};
|
|
|
|
struct file_range {
|
|
@@ -113,6 +117,9 @@
|
|
struct disk_info *info, int align,
|
|
off_t *offset);
|
|
void disk_print_devt(dev_t d);
|
|
+void disk_print_devname(dev_t d);
|
|
+void prepare_footnote_ptr(int source, char *ptr);
|
|
+void print_footnote_ref(int source, const char *prefix);
|
|
void disk_print_info(struct disk_info *info, int source);
|
|
int disk_is_zero_block(disk_blockptr_t* block, struct disk_info* info);
|
|
blocknum_t disk_compact_blocklist(disk_blockptr_t* list, blocknum_t count,
|
|
@@ -122,7 +129,6 @@
|
|
disk_blockptr_t** blocklist,
|
|
struct disk_info* pinfo);
|
|
int disk_check_subchannel_set(int devno, dev_t device, char* dev_name);
|
|
-void disk_print_geo(struct disk_info *data);
|
|
int fs_map(int fd, uint64_t offset, blocknum_t *mapped, int fs_block_size);
|
|
|
|
#endif /* not DISK_H */
|
|
--- a/zipl/include/install.h
|
|
+++ b/zipl/include/install.h
|
|
@@ -71,7 +71,7 @@
|
|
struct program_component *components[NR_PROGRAM_COMPONENTS];
|
|
int nr_menu_entries;
|
|
int fd;
|
|
- char *device;
|
|
+ char *basetmp[MAX_TARGETS];
|
|
char *filename;
|
|
unsigned int tmp_filename_created:1;
|
|
unsigned int skip_prepare:1;
|
|
--- a/zipl/include/job.h
|
|
+++ b/zipl/include/job.h
|
|
@@ -18,7 +18,6 @@
|
|
#include "disk.h"
|
|
#include "zipl.h"
|
|
|
|
-
|
|
enum job_id {
|
|
job_print_usage = 1,
|
|
job_print_version = 2,
|
|
@@ -30,6 +29,21 @@
|
|
job_mvdump = 8,
|
|
};
|
|
|
|
+/*
|
|
+ * Set of parameters per physical disk, which are provided
|
|
+ * either by user, or by helper script
|
|
+ */
|
|
+struct target {
|
|
+ char *targetbase;
|
|
+ disk_type_t targettype;
|
|
+ int targetcylinders;
|
|
+ int targetheads;
|
|
+ int targetsectors;
|
|
+ int targetblocksize;
|
|
+ blocknum_t targetoffset;
|
|
+ int check_params;
|
|
+};
|
|
+
|
|
/* target information source */
|
|
typedef enum {
|
|
source_unknown = 0,
|
|
@@ -39,17 +53,21 @@
|
|
} source_t;
|
|
|
|
struct job_target_data {
|
|
- char* bootmap_dir;
|
|
- char* targetbase;
|
|
- disk_type_t targettype;
|
|
- int targetcylinders;
|
|
- int targetheads;
|
|
- int targetsectors;
|
|
- int targetblocksize;
|
|
- blocknum_t targetoffset;
|
|
+ char *bootmap_dir;
|
|
+ int nr_targets;
|
|
+ struct target targets[MAX_TARGETS];
|
|
source_t source;
|
|
};
|
|
|
|
+enum target_params {
|
|
+ TARGET_BASE,
|
|
+ TARGET_TYPE,
|
|
+ TARGET_GEOMETRY,
|
|
+ TARGET_BLOCKSIZE,
|
|
+ TARGET_OFFSET,
|
|
+ LAST_TARGET_PARAM
|
|
+};
|
|
+
|
|
struct job_common_ipl_data {
|
|
char* image;
|
|
char* parmline;
|
|
@@ -142,12 +160,94 @@
|
|
int is_ldipl_dump;
|
|
};
|
|
|
|
+static inline struct target *target_at(struct job_target_data *data,
|
|
+ int index)
|
|
+{
|
|
+ return index >= MAX_TARGETS ? NULL : &data->targets[index];
|
|
+}
|
|
+
|
|
+static inline char *get_targetbase(struct job_target_data *data, int index)
|
|
+{
|
|
+ return target_at(data, index)->targetbase;
|
|
+}
|
|
+
|
|
+static inline void set_targetbase(struct job_target_data *data, int index,
|
|
+ char *value)
|
|
+{
|
|
+ target_at(data, index)->targetbase = value;
|
|
+}
|
|
+
|
|
+static inline disk_type_t get_targettype(struct job_target_data *data,
|
|
+ int index)
|
|
+{
|
|
+ return target_at(data, index)->targettype;
|
|
+}
|
|
+
|
|
+int set_targettype(struct job_target_data *data, int index, char *value);
|
|
+
|
|
+static inline char *job_get_targetbase(struct job_data *job)
|
|
+{
|
|
+ return get_targetbase(&job->target, 0);
|
|
+}
|
|
+
|
|
+static inline void job_set_targetbase(struct job_data *job, char *value)
|
|
+{
|
|
+ set_targetbase(&job->target, 0, value);
|
|
+}
|
|
+
|
|
+static inline int job_get_nr_targets(struct job_data *job)
|
|
+{
|
|
+ return job->target.nr_targets;
|
|
+}
|
|
+
|
|
+static inline void job_set_nr_targets(struct job_data *job, int value)
|
|
+{
|
|
+ job->target.nr_targets = value;
|
|
+}
|
|
+
|
|
+static inline disk_type_t job_get_targettype(struct job_data *job)
|
|
+{
|
|
+ return get_targettype(&job->target, 0);
|
|
+}
|
|
+
|
|
+int job_set_targettype(struct job_data *job, char *value);
|
|
+
|
|
+#define define_target_param_ops(_TYPE_, _PARAM_) \
|
|
+static inline _TYPE_ get_target##_PARAM_(struct job_target_data *data, \
|
|
+ int index) \
|
|
+{ \
|
|
+ return target_at(data, index)->target##_PARAM_; \
|
|
+} \
|
|
+ \
|
|
+static inline void set_target##_PARAM_(struct job_target_data *data, \
|
|
+ int index, _TYPE_ value) \
|
|
+{ \
|
|
+ target_at(data, index)->target##_PARAM_ = value; \
|
|
+} \
|
|
+ \
|
|
+static inline _TYPE_ job_get_target##_PARAM_(struct job_data *job) \
|
|
+{ \
|
|
+ return get_target##_PARAM_(&job->target, 0); \
|
|
+} \
|
|
+ \
|
|
+static inline void job_set_target##_PARAM_(struct job_data *job, \
|
|
+ _TYPE_ value) \
|
|
+{ \
|
|
+ set_target##_PARAM_(&job->target, 0, value); \
|
|
+}
|
|
+
|
|
+define_target_param_ops(int, cylinders)
|
|
+define_target_param_ops(int, heads)
|
|
+define_target_param_ops(int, sectors)
|
|
+define_target_param_ops(int, blocksize)
|
|
+define_target_param_ops(blocknum_t, offset)
|
|
+
|
|
/**
|
|
- * Return true, if target parameters for the base disk are set
|
|
+ * Return true, if target parameters are set at least for one target base disk
|
|
*/
|
|
static inline int target_parameters_are_set(struct job_target_data *td)
|
|
{
|
|
- return td->targetbase != NULL;
|
|
+ return get_targetbase(td, 0) != NULL;
|
|
}
|
|
|
|
int job_get(int argc, char* argv[], struct job_data** data);
|
|
--- a/zipl/include/zipl.h
|
|
+++ b/zipl/include/zipl.h
|
|
@@ -41,6 +41,7 @@
|
|
#define MENU_DEFAULT_TIMEOUT 0
|
|
|
|
#define MAX_DUMP_VOLUMES 32
|
|
+#define MAX_TARGETS 32
|
|
|
|
#define SECURE_BOOT_UNDEFINED -1
|
|
#define SECURE_BOOT_DISABLED 0
|
|
--- a/zipl/src/bootmap.c
|
|
+++ b/zipl/src/bootmap.c
|
|
@@ -1477,9 +1477,9 @@
|
|
printf("Target device information\n");
|
|
disk_print_info(bis->info, job->target.source);
|
|
}
|
|
- if (misc_temp_dev(bis->info->device, 1, &bis->device))
|
|
+ if (misc_temp_dev(bis->info->device, 1, &bis->basetmp[0]))
|
|
return -1;
|
|
- if (check_dump_device(job, bis->info, bis->device))
|
|
+ if (check_dump_device(job, bis->info, bis->basetmp[0]))
|
|
return -1;
|
|
printf("Building bootmap directly on partition '%s'%s\n",
|
|
bis->filename,
|
|
@@ -1543,6 +1543,8 @@
|
|
static int prepare_build_program_table_file(struct job_data *job,
|
|
struct install_set *bis)
|
|
{
|
|
+ int i;
|
|
+
|
|
if (bis->skip_prepare)
|
|
/* skip the preparation work */
|
|
return 0;
|
|
@@ -1576,8 +1578,12 @@
|
|
printf("Target device information\n");
|
|
disk_print_info(bis->info, job->target.source);
|
|
}
|
|
- if (misc_temp_dev(bis->info->device, 1, &bis->device))
|
|
- return -1;
|
|
+ for (i = 0; i < job_get_nr_targets(job); i++) {
|
|
+ if (misc_temp_dev(bis->info->basedisks[i],
|
|
+ 1,
|
|
+ &bis->basetmp[i]))
|
|
+ return -1;
|
|
+ }
|
|
/* Check configuration number limits */
|
|
if (job->id == job_menu) {
|
|
if (check_menu_positions(&job->data.menu, job->name,
|
|
@@ -1692,9 +1698,9 @@
|
|
/* Retrieve target device information */
|
|
if (disk_get_info(job->data.dump.device, &job->target, &info))
|
|
return -1;
|
|
- if (misc_temp_dev(info->device, 1, &bis->device))
|
|
+ if (misc_temp_dev(info->device, 1, &bis->basetmp[0]))
|
|
return -1;
|
|
- if (check_dump_device(job, info, bis->device))
|
|
+ if (check_dump_device(job, info, bis->basetmp[0]))
|
|
return -1;
|
|
|
|
assert(!job->target.bootmap_dir);
|
|
@@ -1844,6 +1850,9 @@
|
|
if (bis->tmp_filename_created)
|
|
misc_free_temp_file(bis->filename);
|
|
free(bis->filename);
|
|
- misc_free_temp_dev(bis->device);
|
|
+ for (i = 0; i < MAX_TARGETS; i++) {
|
|
+ if (bis->basetmp[i])
|
|
+ misc_free_temp_dev(bis->basetmp[i]);
|
|
+ }
|
|
disk_free_info(bis->info);
|
|
}
|
|
--- a/zipl/src/disk.c
|
|
+++ b/zipl/src/disk.c
|
|
@@ -187,43 +187,134 @@
|
|
return rc;
|
|
}
|
|
|
|
+/**
|
|
+ * Process a script output represented by FH and consisting
|
|
+ * of pairs 'key=value' (each such pair is on a separate line).
|
|
+ * Check its consistency and set the extracted target parameters
|
|
+ * to the array of "targets" at TD.
|
|
+ *
|
|
+ * NOTE: this function defines specifications on valid output of
|
|
+ * zipl helper scripts. See zipl-support-for-mirrored-devices.txt
|
|
+ * for details. Before modifying this function, make sure that it
|
|
+ * won't lead to format change.
|
|
+ */
|
|
static int set_target_parameters(FILE *fh, struct job_target_data *td)
|
|
{
|
|
- int checkparm = 0;
|
|
+ int idx[LAST_TARGET_PARAM] = {0};
|
|
+ struct target *t;
|
|
char buffer[80];
|
|
char value[40];
|
|
+ char *error;
|
|
+ int i;
|
|
|
|
+ /**
|
|
+ * Process a stream of 'key=value' pairs and distribute
|
|
+ * them into groups.
|
|
+ * The i-th occurrence of some "key" in the stream means
|
|
+ * that the respective pair belongs to the group #i
|
|
+ */
|
|
+ error = "Exceeded the maximum number of base disks";
|
|
while (fgets(buffer, 80, fh)) {
|
|
if (sscanf(buffer, "targetbase=%s", value) == 1) {
|
|
- td->targetbase = misc_strdup(value);
|
|
- checkparm++;
|
|
+ t = target_at(td, idx[TARGET_BASE]++);
|
|
+ if (!t)
|
|
+ goto error;
|
|
+ t->targetbase = misc_strdup(value);
|
|
+ goto found;
|
|
}
|
|
if (sscanf(buffer, "targettype=%s", value) == 1) {
|
|
- type_from_target(value, &td->targettype);
|
|
- checkparm++;
|
|
+ t = target_at(td, idx[TARGET_TYPE]++);
|
|
+ if (!t)
|
|
+ goto error;
|
|
+ type_from_target(value, &t->targettype);
|
|
+ goto found;
|
|
}
|
|
if (sscanf(buffer, "targetgeometry=%s", value) == 1) {
|
|
- td->targetcylinders =
|
|
- atoi(strtok(value, ","));
|
|
- td->targetheads = atoi(strtok(NULL, ","));
|
|
- td->targetsectors = atoi(strtok(NULL, ","));
|
|
- checkparm++;
|
|
+ t = target_at(td, idx[TARGET_GEOMETRY]++);
|
|
+ if (!t)
|
|
+ goto error;
|
|
+ t->targetcylinders = atoi(strtok(value, ","));
|
|
+ t->targetheads = atoi(strtok(NULL, ","));
|
|
+ t->targetsectors = atoi(strtok(NULL, ","));
|
|
+ goto found;
|
|
}
|
|
if (sscanf(buffer, "targetblocksize=%s", value) == 1) {
|
|
- td->targetblocksize = atoi(value);
|
|
- checkparm++;
|
|
+ t = target_at(td, idx[TARGET_BLOCKSIZE]++);
|
|
+ if (!t)
|
|
+ goto error;
|
|
+ t->targetblocksize = atoi(value);
|
|
+ goto found;
|
|
}
|
|
if (sscanf(buffer, "targetoffset=%s", value) == 1) {
|
|
- td->targetoffset = atol(value);
|
|
- checkparm++;
|
|
+ t = target_at(td, idx[TARGET_OFFSET]++);
|
|
+ if (!t)
|
|
+ goto error;
|
|
+ t->targetoffset = atol(value);
|
|
+ goto found;
|
|
}
|
|
+ continue;
|
|
+found:
|
|
+ t->check_params++;
|
|
}
|
|
- if ((!disk_is_eckd(td->targettype) && checkparm < 4) ||
|
|
- (disk_is_eckd(td->targettype) && checkparm != 5)) {
|
|
- error_reason("Target parameters missing from script");
|
|
- return -1;
|
|
+ /* Check for consistency */
|
|
+ error = "Inconsistent script output";
|
|
+ /*
|
|
+ * First, calculate total number of groups
|
|
+ */
|
|
+ td->nr_targets = 0;
|
|
+ for (i = 0; i < MAX_TARGETS; i++) {
|
|
+ t = target_at(td, i);
|
|
+ if (t->check_params == 0)
|
|
+ break;
|
|
+ td->nr_targets++;
|
|
+ }
|
|
+ if (!td->nr_targets)
|
|
+ /* No keywords found in the stream */
|
|
+ goto error;
|
|
+ /*
|
|
+ * Each group has to include targetbase, targettype,
|
|
+ * targetblocksize and targetoffset.
|
|
+ */
|
|
+ if (td->nr_targets != idx[TARGET_BASE] ||
|
|
+ td->nr_targets != idx[TARGET_TYPE] ||
|
|
+ td->nr_targets != idx[TARGET_BLOCKSIZE] ||
|
|
+ td->nr_targets != idx[TARGET_OFFSET])
|
|
+ goto error;
|
|
+ /*
|
|
+ * In addition, any group of "ECKD" type has to include
|
|
+ * targetgeometry
|
|
+ */
|
|
+ for (i = 0; i < td->nr_targets; i++) {
|
|
+ t = target_at(td, i);
|
|
+ assert(t->check_params >= 4);
|
|
+ if (disk_is_eckd(t->targettype) && t->check_params != 5)
|
|
+ goto error;
|
|
}
|
|
return 0;
|
|
+error:
|
|
+ error_reason("%s", error);
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static void print_base_disk_params(struct job_target_data *td, int index)
|
|
+{
|
|
+ disk_type_t type = get_targettype(td, index);
|
|
+
|
|
+ if (!verbose)
|
|
+ return;
|
|
+ {
|
|
+ fprintf(stderr, "Base disk '%s':\n", get_targetbase(td, index));
|
|
+ fprintf(stderr, " layout........: %s\n", disk_get_type_name(type));
|
|
+ }
|
|
+ if (disk_is_eckd(type)) {
|
|
+ fprintf(stderr, " heads.........: %u\n", get_targetheads(td, index));
|
|
+ fprintf(stderr, " sectors.......: %u\n", get_targetsectors(td, index));
|
|
+ fprintf(stderr, " cylinders.....: %u\n", get_targetcylinders(td, index));
|
|
+ }
|
|
+ {
|
|
+ fprintf(stderr, " start.........: %lu\n", get_targetoffset(td, index));
|
|
+ fprintf(stderr, " blksize.......: %u\n", get_targetblocksize(td, index));
|
|
+ }
|
|
}
|
|
|
|
/**
|
|
@@ -235,31 +326,57 @@
|
|
{
|
|
int majnum, minnum;
|
|
struct stat stats;
|
|
-
|
|
+ int i;
|
|
+ /*
|
|
+ * Currently multiple base disks with different parameters
|
|
+ * are not supported
|
|
+ */
|
|
data->devno = -1;
|
|
- data->phy_block_size = td->targetblocksize;
|
|
- data->type = td->targettype;
|
|
- data->partnum = 0;
|
|
+ data->phy_block_size = get_targetblocksize(td, 0);
|
|
+ data->type = get_targettype(td, 0);
|
|
|
|
- if (sscanf(td->targetbase, "%d:%d", &majnum, &minnum) == 2) {
|
|
- data->device = makedev(majnum, minnum);
|
|
- data->targetbase = defined_as_device;
|
|
- data->partnum = minor(stats.st_rdev) - minnum;
|
|
- } else {
|
|
- if (stat(td->targetbase, &stats)) {
|
|
- error_reason(strerror(errno));
|
|
- error_text("Could not get information for "
|
|
- "file '%s'", td->targetbase);
|
|
+ assert(td->nr_targets != 0);
|
|
+ for (i = 1; i < td->nr_targets; i++) {
|
|
+ if (data->type != get_targettype(td, i) ||
|
|
+ data->phy_block_size != get_targetblocksize(td, i)) {
|
|
+ print_base_disk_params(td, 0);
|
|
+ print_base_disk_params(td, i);
|
|
+ error_reason("Inconsistent base disk geometry in target device");
|
|
return -1;
|
|
}
|
|
- if (!S_ISBLK(stats.st_mode)) {
|
|
- error_reason("Target base device '%s' is not "
|
|
- "a block device",
|
|
- td->targetbase);
|
|
+ }
|
|
+ data->partnum = 0;
|
|
+ data->targetbase_def = undefined;
|
|
+
|
|
+ for (i = 0; i < td->nr_targets; i++) {
|
|
+ definition_t defined_as;
|
|
+
|
|
+ if (sscanf(get_targetbase(td, i),
|
|
+ "%d:%d", &majnum, &minnum) == 2) {
|
|
+ data->basedisks[i] = makedev(majnum, minnum);
|
|
+ defined_as = defined_as_device;
|
|
+ } else {
|
|
+ if (stat(get_targetbase(td, i), &stats)) {
|
|
+ error_reason(strerror(errno));
|
|
+ error_text("Could not get information for "
|
|
+ "file '%s'", get_targetbase(td, i));
|
|
+ return -1;
|
|
+ }
|
|
+ if (!S_ISBLK(stats.st_mode)) {
|
|
+ error_reason("Target base device '%s' is not "
|
|
+ "a block device",
|
|
+ get_targetbase(td, i));
|
|
+ return -1;
|
|
+ }
|
|
+ data->basedisks[i] = stats.st_rdev;
|
|
+ defined_as = defined_as_name;
|
|
+ }
|
|
+ if (data->targetbase_def != undefined &&
|
|
+ data->targetbase_def != defined_as) {
|
|
+ error_reason("Target base disks are defined by different ways");
|
|
return -1;
|
|
}
|
|
- data->device = stats.st_rdev;
|
|
- data->targetbase = defined_as_name;
|
|
+ data->targetbase_def = defined_as;
|
|
}
|
|
if (data->type == disk_type_scsi && ioctl(fd, NVME_IOCTL_ID) >= 0)
|
|
data->is_nvme = 1;
|
|
@@ -446,11 +563,28 @@
|
|
static int disk_set_geometry_by_hint(struct job_target_data *td,
|
|
struct disk_info *data)
|
|
{
|
|
- data->geo.heads = td->targetheads;
|
|
- data->geo.sectors = td->targetsectors;
|
|
- data->geo.cylinders = td->targetcylinders;
|
|
- data->geo.start = td->targetoffset;
|
|
-
|
|
+ int i;
|
|
+ /*
|
|
+ * Currently multiple base disks with different parameters
|
|
+ * are not supported
|
|
+ */
|
|
+ data->geo.heads = get_targetheads(td, 0);
|
|
+ data->geo.sectors = get_targetsectors(td, 0);
|
|
+ data->geo.cylinders = get_targetcylinders(td, 0);
|
|
+ data->geo.start = get_targetoffset(td, 0);
|
|
+
|
|
+ assert(td->nr_targets != 0);
|
|
+ for (i = 1; i < td->nr_targets; i++) {
|
|
+ if (data->geo.heads != get_targetheads(td, i) ||
|
|
+ data->geo.sectors != get_targetsectors(td, i) ||
|
|
+ data->geo.cylinders != get_targetcylinders(td, i) ||
|
|
+ data->geo.start != get_targetoffset(td, i)) {
|
|
+ print_base_disk_params(td, 0);
|
|
+ print_base_disk_params(td, i);
|
|
+ error_reason("Inconsistent base disk geometry in target device");
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
return 0;
|
|
}
|
|
|
|
@@ -515,14 +649,16 @@
|
|
}
|
|
|
|
/**
|
|
- * Prepare INFO required to perform IPL installation on the physical
|
|
- * disk where the logical DEVICE is located.
|
|
+ * Prepare INFO required to perform IPL installation on physical disks
|
|
+ * participating in the logical DEVICE.
|
|
* Preparation is performed in 2 steps:
|
|
*
|
|
- * 1. Find out a physical "base" disk where the logical DEVICE is
|
|
- * located. Calculate "target" parameters (type, geometry, physical
|
|
- * block size, data offset, etc);
|
|
- * 2. Complete INFO by the found base disk and target parameters.
|
|
+ * 1. Find out a set of physical "base" disks participating in the
|
|
+ * logical DEVICE. For each found disk calculate "target" parameters
|
|
+ * (type, geometry, physical block size, data offset, etc) and store
|
|
+ * it in the array of "targets" of TD;
|
|
+ * 2. Complete INFO using the found base disks and calculated target
|
|
+ * parameters.
|
|
*
|
|
* TD: optionally contains target parameters specified by user via
|
|
* config file, or special "target options" of zipl tool.
|
|
@@ -566,6 +702,7 @@
|
|
goto error;
|
|
if (disk_set_info_by_hint(td, data, fd))
|
|
goto error;
|
|
+ data->device = stats.st_rdev;
|
|
break;
|
|
case source_user:
|
|
/*
|
|
@@ -578,6 +715,12 @@
|
|
goto error;
|
|
if (disk_set_info_by_hint(td, data, fd))
|
|
goto error;
|
|
+ /*
|
|
+ * multiple base disks are not supported
|
|
+ * with this source type
|
|
+ */
|
|
+ assert(td->nr_targets == 1);
|
|
+ data->device = data->basedisks[0];
|
|
break;
|
|
case source_auto:
|
|
/* no ready target parameters are available */
|
|
@@ -585,6 +728,12 @@
|
|
goto error;
|
|
if (disk_set_info_auto(data, &stats, fd))
|
|
goto error;
|
|
+ /*
|
|
+ * multiple base disks are not supported
|
|
+ * with this source type
|
|
+ */
|
|
+ data->basedisks[0] = data->device;
|
|
+ td->nr_targets = 1;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
@@ -940,6 +1089,33 @@
|
|
printf("%02x:%02x", major(d), minor(d));
|
|
}
|
|
|
|
+void disk_print_devname(dev_t dev)
|
|
+{
|
|
+ struct util_proc_part_entry part_entry;
|
|
+
|
|
+ if (!util_proc_part_get_entry(dev, &part_entry)) {
|
|
+ printf("%s", part_entry.name);
|
|
+ util_proc_part_free_entry(&part_entry);
|
|
+ } else {
|
|
+ disk_print_devt(dev);
|
|
+ }
|
|
+}
|
|
+
|
|
+void prepare_footnote_ptr(int source, char *ptr)
|
|
+{
|
|
+ if (source == source_user || source == source_script)
|
|
+ strcpy(ptr, " *)");
|
|
+ else
|
|
+ strcpy(ptr, "");
|
|
+}
|
|
+
|
|
+void print_footnote_ref(int source, const char *prefix)
|
|
+{
|
|
+ if (source == source_user)
|
|
+ printf("%s*) Data provided by user.\n", prefix);
|
|
+ else if (source == source_script)
|
|
+ printf("%s*) Data provided by script.\n", prefix);
|
|
+}
|
|
|
|
/* Return a name for a given disk TYPE. */
|
|
char *
|
|
@@ -991,12 +1167,11 @@
|
|
void disk_print_info(struct disk_info *info, int source)
|
|
{
|
|
char footnote[4] = "";
|
|
- if (source == source_user || source == source_script)
|
|
- strcpy(footnote, " *)");
|
|
|
|
+ prepare_footnote_ptr(source, footnote);
|
|
printf(" Device..........................: ");
|
|
disk_print_devt(info->device);
|
|
- if (info->targetbase == defined_as_device)
|
|
+ if (info->targetbase_def == defined_as_device)
|
|
printf("%s", footnote);
|
|
printf("\n");
|
|
if (info->partnum != 0) {
|
|
@@ -1007,7 +1182,7 @@
|
|
if (info->name) {
|
|
printf(" Device name.....................: %s",
|
|
info->name);
|
|
- if (info->targetbase == defined_as_name)
|
|
+ if (info->targetbase_def == defined_as_name)
|
|
printf("%s", footnote);
|
|
printf("\n");
|
|
}
|
|
@@ -1050,21 +1225,7 @@
|
|
info->phy_block_size, footnote);
|
|
printf(" Device size in physical blocks..: %ld\n",
|
|
(long) info->phy_blocks);
|
|
- if (source == source_user)
|
|
- printf(" *) Data provided by user.\n");
|
|
- if (source == source_script)
|
|
- printf(" *) Data provided by script.\n");
|
|
-}
|
|
-
|
|
-/* Print textual representation of geo structure. */
|
|
-void
|
|
-disk_print_geo(struct disk_info *data)
|
|
-{
|
|
- printf(" geo.heads.........:%u\n", data->geo.heads);
|
|
- printf(" geo.sectors.......:%u\n", data->geo.sectors);
|
|
- printf(" geo.cylinders.....:%u\n", data->geo.cylinders);
|
|
- printf(" geo.start.........:%lu\n", data->geo.start);
|
|
- printf(" blksize...........:%u\n", data->phy_block_size);
|
|
+ print_footnote_ref(source, " ");
|
|
}
|
|
|
|
/* Check whether a block is a zero block which identifies a hole in a file.
|
|
--- a/zipl/src/install.c
|
|
+++ b/zipl/src/install.c
|
|
@@ -434,11 +434,14 @@
|
|
{
|
|
disk_blockptr_t *scsi_dump_sb_blockptr = &bis->scsi_dump_sb_blockptr;
|
|
struct disk_info *info = bis->info;
|
|
- char *device = bis->device;
|
|
- int fd, rc;
|
|
+ char footnote[4];
|
|
+ int rc;
|
|
+ int i;
|
|
|
|
if (!info)
|
|
return 0;
|
|
+
|
|
+ prepare_footnote_ptr(job->target.source, footnote);
|
|
/* Inform user about what we're up to */
|
|
printf("Preparing boot device for %s%s: ",
|
|
disk_get_ipl_type(info->type,
|
|
@@ -455,40 +458,58 @@
|
|
disk_print_devt(info->device);
|
|
printf(".\n");
|
|
}
|
|
- /* Open device file */
|
|
- fd = open(device, O_RDWR);
|
|
- if (fd == -1) {
|
|
- error_reason(strerror(errno));
|
|
- error_text("Could not open temporary device file '%s'",
|
|
- device);
|
|
- return -1;
|
|
- }
|
|
- /* Ensure that potential cache inconsistencies between disk and
|
|
- * partition are resolved by flushing the corresponding buffers. */
|
|
- if (!dry_run) {
|
|
- if (ioctl(fd, BLKFLSBUF)) {
|
|
- fprintf(stderr, "Warning: Could not flush disk "
|
|
- "caches.\n");
|
|
+ /* Install independently on each physical target base */
|
|
+
|
|
+ for (i = 0; i < job_get_nr_targets(job); i++) {
|
|
+ int fd;
|
|
+
|
|
+ if (verbose) {
|
|
+ printf("Installing on base disk: ");
|
|
+ disk_print_devname(info->basedisks[i]);
|
|
+ printf("%s.\n", footnote);
|
|
}
|
|
+ /* Open device file */
|
|
+ fd = open(bis->basetmp[i], O_RDWR);
|
|
+ if (fd == -1) {
|
|
+ error_reason(strerror(errno));
|
|
+ error_text("Could not open temporary device file '%s'",
|
|
+ bis->basetmp[i]);
|
|
+ return -1;
|
|
+ }
|
|
+ /* Ensure that potential cache inconsistencies between disk and
|
|
+ * partition are resolved by flushing the corresponding buffers.
|
|
+ */
|
|
+ if (!dry_run) {
|
|
+ if (ioctl(fd, BLKFLSBUF)) {
|
|
+ fprintf(stderr, "Warning: Could not flush disk "
|
|
+ "caches.\n");
|
|
+ }
|
|
+ }
|
|
+ /*
|
|
+ * Depending on disk type, install one or two program tables
|
|
+ * for CCW-type IPL and (or) for List-Directed IPL (see the
|
|
+ * picture in comments above)
|
|
+ */
|
|
+ if (job->id == job_dump_partition) {
|
|
+ rc = install_bootloader_dump(bis->tables, info,
|
|
+ scsi_dump_sb_blockptr,
|
|
+ is_ngdump_enabled(job),
|
|
+ fd);
|
|
+ } else {
|
|
+ rc = install_bootloader_ipl(bis->tables, info,
|
|
+ fd);
|
|
+ }
|
|
+ if (fsync(fd))
|
|
+ error_text("Could not sync device file '%s'",
|
|
+ bis->basetmp[i]);
|
|
+ if (close(fd))
|
|
+ error_text("Could not close device file '%s'",
|
|
+ bis->basetmp[i]);
|
|
+ if (rc)
|
|
+ break;
|
|
}
|
|
- /*
|
|
- * Depending on disk type, install one or two program tables
|
|
- * for CCW-type IPL and (or) for List-Directed IPL (see the
|
|
- * picture in comments above)
|
|
- */
|
|
- if (job->id == job_dump_partition) {
|
|
- rc = install_bootloader_dump(bis->tables, info,
|
|
- scsi_dump_sb_blockptr,
|
|
- is_ngdump_enabled(job),
|
|
- fd);
|
|
- } else {
|
|
- rc = install_bootloader_ipl(bis->tables, info, fd);
|
|
- }
|
|
-
|
|
- if (fsync(fd))
|
|
- error_text("Could not sync device file '%s'", device);
|
|
- if (close(fd))
|
|
- error_text("Could not close device file '%s'", device);
|
|
+ if (verbose)
|
|
+ print_footnote_ref(job->target.source, "");
|
|
|
|
if (!dry_run && rc == 0) {
|
|
if (info->devno >= 0)
|
|
--- a/zipl/src/job.c
|
|
+++ b/zipl/src/job.c
|
|
@@ -1346,6 +1346,27 @@
|
|
}
|
|
}
|
|
|
|
+int set_targettype(struct job_target_data *data, int index, char *value)
|
|
+{
|
|
+ return type_from_target(value,
|
|
+ &target_at(data, index)->targettype);
|
|
+}
|
|
+
|
|
+int job_set_targettype(struct job_data *job, char *value)
|
|
+{
|
|
+ return set_targettype(&job->target, 0, value);
|
|
+}
|
|
+
|
|
+static int job_set_target(struct job_data *job, char *value)
|
|
+{
|
|
+ job_set_targetbase(job, value);
|
|
+ if (!job_get_targetbase(job))
|
|
+ return -1;
|
|
+ job_set_nr_targets(job, 1);
|
|
+ job->target.source = source_user;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int
|
|
get_job_from_section_data(char* data[], struct job_data* job, char* section)
|
|
{
|
|
@@ -1362,32 +1383,28 @@
|
|
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)
|
|
+ if (job_set_target(job, misc_strdup(data[(int)
|
|
+ scan_keyword_targetbase])))
|
|
return -1;
|
|
- job->target.source = source_user;
|
|
}
|
|
if (data[(int) scan_keyword_targettype] != NULL) {
|
|
- if (type_from_target(
|
|
- data[(int) scan_keyword_targettype],
|
|
- &job->target.targettype))
|
|
+ if (job_set_targettype(job,
|
|
+ data[(int) scan_keyword_targettype]))
|
|
return -1;
|
|
}
|
|
if (data[(int) scan_keyword_targetgeometry] != NULL) {
|
|
- job->target.targetcylinders =
|
|
+ job_set_targetcylinders(job,
|
|
atoi(strtok(data[(int)
|
|
- scan_keyword_targetgeometry], ","));
|
|
- job->target.targetheads = atoi(strtok(NULL, ","));
|
|
- job->target.targetsectors = atoi(strtok(NULL, ","));
|
|
+ scan_keyword_targetgeometry], ",")));
|
|
+ job_set_targetheads(job, atoi(strtok(NULL, ",")));
|
|
+ job_set_targetsectors(job, atoi(strtok(NULL, ",")));
|
|
}
|
|
if (data[(int) scan_keyword_targetblocksize] != NULL)
|
|
- job->target.targetblocksize =
|
|
- atoi(data[(int) scan_keyword_targetblocksize]);
|
|
+ job_set_targetblocksize(job,
|
|
+ atoi(data[(int) scan_keyword_targetblocksize]));
|
|
if (data[(int) scan_keyword_targetoffset] != NULL)
|
|
- job->target.targetoffset =
|
|
- atol(data[(int) scan_keyword_targetoffset]);
|
|
+ job_set_targetoffset(job,
|
|
+ atol(data[(int) scan_keyword_targetoffset]));
|
|
/* Fill in name and address of image file */
|
|
|
|
job->data.ipl.common.image = misc_strdup(
|
|
@@ -1615,37 +1632,32 @@
|
|
return -1;
|
|
break;
|
|
case scan_keyword_targetbase:
|
|
- job->target.targetbase = misc_strdup(
|
|
- scan[i].content.keyword.value);
|
|
- if (job->target.targetbase == NULL)
|
|
+ if (job_set_target(job, misc_strdup(
|
|
+ scan[i].content.keyword.value)))
|
|
return -1;
|
|
- job->target.source = source_user;
|
|
break;
|
|
case scan_keyword_targettype:
|
|
- if (type_from_target(
|
|
- scan[i].content.keyword.value,
|
|
- &job->target.targettype))
|
|
+ if (job_set_targettype(job,
|
|
+ scan[i].content.keyword.value))
|
|
return -1;
|
|
break;
|
|
case scan_keyword_targetgeometry:
|
|
- job->target.targetcylinders =
|
|
+ job_set_targetcylinders(job,
|
|
atoi(strtok(
|
|
scan[i].content.keyword.value,
|
|
- ","));
|
|
- job->target.targetheads =
|
|
- atoi(strtok(NULL, ","));
|
|
- job->target.targetsectors =
|
|
- atoi(strtok(NULL, ","));
|
|
+ ",")));
|
|
+ job_set_targetheads(job,
|
|
+ atoi(strtok(NULL, ",")));
|
|
+ job_set_targetsectors(job,
|
|
+ atoi(strtok(NULL, ",")));
|
|
break;
|
|
case scan_keyword_targetblocksize:
|
|
- job->target.targetblocksize =
|
|
- atoi(
|
|
- scan[i].content.keyword.value);
|
|
+ job_set_targetblocksize(job, atoi(
|
|
+ scan[i].content.keyword.value));
|
|
break;
|
|
case scan_keyword_targetoffset:
|
|
- job->target.targetoffset =
|
|
- atol(
|
|
- scan[i].content.keyword.value);
|
|
+ job_set_targetoffset(job, atol(
|
|
+ scan[i].content.keyword.value));
|
|
break;
|
|
default:
|
|
/* Should not happen */
|