mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Initial s390-tools-2.0.0 import
This commit is based on the s390-tools-1.39.0 version. Changes on top of s390-tools-1.39.0: - Add MIT license to all source files - Add LICENSE file - Transform REAMDE to README.md (markdown) - Add AUTHORS.md file - Add CONTRIBUTING.md file - Move changelog from README to CHANGELOG.md file Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com> Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
# Common definitions
|
||||
include ../../common.mak
|
||||
|
||||
ALL_CPPFLAGS += -I../include -I../boot \
|
||||
-DZFCPDUMP_DIR=$(ZFCPDUMP_DIR) \
|
||||
-DZFCPDUMP_FS_IMAGE=$(ZFCPDUMP_FS_IMAGE) \
|
||||
-DZFCPDUMP_FS_RD=$(ZFCPDUMP_FS_RD) \
|
||||
-DZFCPDUMP_PART_IMAGE=$(ZFCPDUMP_PART_IMAGE) \
|
||||
-DZFCPDUMP_PART_RD=$(ZFCPDUMP_PART_RD) \
|
||||
-D_FILE_OFFSET_BITS=64
|
||||
ALL_LDFLAGS += -Wl,-z,noexecstack
|
||||
|
||||
libs = $(rootdir)/libutil/libutil.a \
|
||||
$(rootdir)/libu2s/libu2s.a
|
||||
|
||||
objects = misc.o error.o scan.o job.o boot.o bootmap.o disk.o \
|
||||
install.o zipl.o $(rootdir)/zipl/boot/data.o
|
||||
|
||||
zipl_helpers = $(wildcard zipl_helper.*)
|
||||
chreipl_helpers = $(subst zipl_,chreipl_, $(zipl_helpers))
|
||||
|
||||
all: zipl $(chreipl_helpers)
|
||||
|
||||
zipl: $(objects) $(libs)
|
||||
|
||||
chreipl_helper.%: zipl_helper.%
|
||||
ln -s $< $@
|
||||
|
||||
install: all
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -c zipl $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -m 755 $(zipl_helpers) $(chreipl_helpers) \
|
||||
$(DESTDIR)$(TOOLS_LIBDIR)
|
||||
$(CP) --no-dereference $(chreipl_helpers) $(DESTDIR)$(TOOLS_LIBDIR)
|
||||
|
||||
clean:
|
||||
rm -f *.o $(chreipl_helpers) zipl
|
||||
|
||||
.PHONY: all install clean
|
||||
|
||||
# Additional manual dependencies
|
||||
|
||||
../boot/data.h:
|
||||
make -C ../boot data.h
|
||||
|
||||
../boot/data.o:
|
||||
make -C ../boot data.o
|
||||
+565
@@ -0,0 +1,565 @@
|
||||
/*
|
||||
* zipl - zSeries Initial Program Loader tool
|
||||
*
|
||||
* Functions to handle the boot loader data
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../boot/data.h"
|
||||
#include "boot.h"
|
||||
#include "bootmap.h"
|
||||
#include "error.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define DATA_SIZE(x) ((size_t) (&_binary_##x##_bin_size))
|
||||
#define DATA_ADDR(x) (&_binary_##x##_bin_start)
|
||||
|
||||
#define STAGE2_MAX_SIZE 0x3000
|
||||
#define STAGE1B_LOAD_ADDR 0xe000
|
||||
#define CCW_FLAG_CC 0x40
|
||||
#define CCW_FLAG_SLI 0x20
|
||||
#define FBA_BLK_SIZE 512
|
||||
|
||||
static struct boot_ccw0 tic_to_stage1b = {
|
||||
.cmd = 0x08, /* tic */
|
||||
.address_lo = STAGE1B_LOAD_ADDR,
|
||||
};
|
||||
|
||||
/* Check sizes of internal objects. Return 0 if everything is correct,
|
||||
* non-zero otherwise. */
|
||||
int
|
||||
boot_check_data(void)
|
||||
{
|
||||
if (DATA_SIZE(fba0) != sizeof(struct boot_fba_stage0)) {
|
||||
error_reason("Size mismatch of FBA stage 0 loader");
|
||||
return -1;
|
||||
}
|
||||
if (DATA_SIZE(fba1b) != sizeof(struct boot_fba_stage1b)) {
|
||||
error_reason("Size mismatch of FBA stage 1b loader");
|
||||
return -1;
|
||||
}
|
||||
if (DATA_SIZE(eckd0_ldl) !=
|
||||
sizeof(struct boot_eckd_ldl_stage0)) {
|
||||
error_reason("Size mismatch of ECKD LDL stage 0 loader");
|
||||
return -1;
|
||||
}
|
||||
if (DATA_SIZE(eckd0_cdl) != sizeof(struct boot_eckd_cdl_stage0)) {
|
||||
error_reason("Size mismatch of ECKD CDL stage 0 loader");
|
||||
return -1;
|
||||
}
|
||||
if (DATA_SIZE(eckd1) != sizeof(struct boot_eckd_stage1)) {
|
||||
error_reason("Size mismatch of ECKD stage 1 loader");
|
||||
return -1;
|
||||
}
|
||||
if (DATA_SIZE(eckd1b) != sizeof(struct boot_eckd_stage1b)) {
|
||||
error_reason("Size mismatch of ECKD stage 1b loader");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Export stage 3 size for partition dump with dump kernel */
|
||||
size_t
|
||||
get_stage3_size()
|
||||
{
|
||||
return DATA_SIZE(stage3);
|
||||
}
|
||||
|
||||
/* Create a stage 3 loader in memory.
|
||||
* Upon success, return 0 and set BUFFER to point to the data buffer and set
|
||||
* BYTECOUNT to contain the loader size in bytes. Return non-zero otherwise. */
|
||||
int
|
||||
boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr,
|
||||
address_t initrd_addr, size_t initrd_len, address_t image_addr,
|
||||
int extra_parm, uint16_t flags)
|
||||
{
|
||||
struct boot_stage3_params params;
|
||||
void* data;
|
||||
|
||||
if (image_addr != (image_addr & PSW_ADDRESS_MASK)) {
|
||||
error_reason("Kernel image load address to high (31 bit "
|
||||
"addressing mode)");
|
||||
return -1;
|
||||
}
|
||||
/* Get memory */
|
||||
data = misc_malloc(DATA_SIZE(stage3));
|
||||
if (data == NULL)
|
||||
return -1;
|
||||
/* Prepare params section */
|
||||
params.parm_addr = (uint64_t) parm_addr;
|
||||
params.initrd_addr = (uint64_t) initrd_addr;
|
||||
params.initrd_len = (uint64_t) initrd_len;
|
||||
params.load_psw = (uint64_t)(image_addr | PSW_LOAD);
|
||||
params.extra_parm = (uint64_t) extra_parm;
|
||||
params.flags = flags;
|
||||
/* Initialize buffer */
|
||||
memcpy(data, DATA_ADDR(stage3), DATA_SIZE(stage3));
|
||||
memcpy(data, ¶ms, sizeof(struct boot_stage3_params));
|
||||
*buffer = data;
|
||||
*bytecount = DATA_SIZE(stage3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
boot_init_fba_stage0(struct boot_fba_stage0 *stage0,
|
||||
disk_blockptr_t *stage1b_list, blocknum_t stage1b_count)
|
||||
{
|
||||
blocknum_t i;
|
||||
|
||||
/* Initialize stage 0 data */
|
||||
memcpy(stage0, DATA_ADDR(fba0), sizeof(*stage0));
|
||||
/* Fill in blocklist for stage 2 loader */
|
||||
if (stage1b_count > STAGE1B_BLK_CNT_MAX) {
|
||||
error_reason("Not enough room for FBA stage 1b loader");
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < stage1b_count; i++) {
|
||||
stage0->locdata[i].blocknr =
|
||||
(uint32_t) stage1b_list[i].linear.block;
|
||||
stage0->locread[i].read.address_lo =
|
||||
STAGE1B_LOAD_ADDR + i * FBA_BLK_SIZE;
|
||||
}
|
||||
/* Terminate CCW chain: Tic to stage 1b */
|
||||
memcpy(&stage0->locread[i], &tic_to_stage1b, sizeof(tic_to_stage1b));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
boot_init_eckd_ldl_stage0(struct boot_eckd_ldl_stage0 *stage0)
|
||||
{
|
||||
memcpy(stage0, DATA_ADDR(eckd0_ldl), sizeof(*stage0));
|
||||
/* Fill in size of stage 1 plus stage 0 loader */
|
||||
stage0->read_r1.count = sizeof(struct boot_eckd_stage1) +
|
||||
sizeof(struct boot_eckd_ldl_stage0);
|
||||
}
|
||||
|
||||
void
|
||||
boot_init_eckd_cdl_stage0(struct boot_eckd_cdl_stage0 *stage0)
|
||||
{
|
||||
memcpy(stage0, DATA_ADDR(eckd0_cdl), sizeof(*stage0));
|
||||
/* Fill in size of stage 1 loader */
|
||||
stage0->read.count = sizeof(struct boot_eckd_stage1);
|
||||
}
|
||||
|
||||
int
|
||||
boot_init_eckd_stage1(struct boot_eckd_stage1 *stage1,
|
||||
disk_blockptr_t *stage1b_list, blocknum_t stage1b_count)
|
||||
{
|
||||
blocknum_t i;
|
||||
|
||||
memcpy(stage1, DATA_ADDR(eckd1), sizeof(*stage1));
|
||||
/* Fill in blocklist for stage 1b loader */
|
||||
if (stage1b_count > STAGE1B_BLK_CNT_MAX) {
|
||||
error_reason("Not enough room for ECKD stage 1b loader "
|
||||
"(try larger block size)");
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < stage1b_count; i++) {
|
||||
stage1->ssrt[i].read.count = stage1b_list[i].chs.size;
|
||||
stage1->seek[i].cyl = stage1b_list[i].chs.cyl;
|
||||
stage1->seek[i].head = stage1b_list[i].chs.head |
|
||||
((stage1b_list[i].chs.cyl >> 12) & 0xfff0);
|
||||
stage1->seek[i].sec = stage1b_list[i].chs.sec;
|
||||
stage1->ssrt[i].read.address_lo =
|
||||
STAGE1B_LOAD_ADDR + i * stage1b_list[i].chs.size;
|
||||
stage1->ssrt[i].read.flags = CCW_FLAG_CC | CCW_FLAG_SLI;
|
||||
}
|
||||
/* Terminate CCW chain: Tic to stage 1b */
|
||||
memcpy(&stage1->ssrt[i], &tic_to_stage1b, sizeof(tic_to_stage1b));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
boot_init_fba_stage1b(struct boot_fba_stage1b *stage1b,
|
||||
disk_blockptr_t *stage2_list, blocknum_t stage2_count)
|
||||
{
|
||||
blocknum_t i;
|
||||
|
||||
memcpy(stage1b, DATA_ADDR(fba1b), sizeof(*stage1b));
|
||||
if (stage2_count > STAGE2_BLK_CNT_MAX) {
|
||||
error_reason("Not enough room for FBA stage 2 loader");
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < stage2_count; i++) {
|
||||
stage1b->locdata[i].blocknr =
|
||||
(uint32_t) stage2_list[i].linear.block;
|
||||
stage1b->locread[i].read.address_lo =
|
||||
ZIPL_STAGE2_LOAD_ADDRESS + i * FBA_BLK_SIZE;
|
||||
}
|
||||
/* Terminate CCW chain */
|
||||
stage1b->locread[i - 1].read.flags &= ~CCW_FLAG_CC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
boot_init_eckd_stage1b(struct boot_eckd_stage1b *stage1b,
|
||||
disk_blockptr_t *stage2_list, blocknum_t stage2_count)
|
||||
{
|
||||
blocknum_t i;
|
||||
|
||||
memcpy(stage1b, DATA_ADDR(eckd1b), sizeof(*stage1b));
|
||||
if (stage2_count > STAGE2_BLK_CNT_MAX) {
|
||||
error_reason("Not enough room for ECKD stage 2 loader "
|
||||
"(try larger block size)");
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < stage2_count; i++) {
|
||||
stage1b->ssrt[i].read.count = stage2_list[i].chs.size;
|
||||
stage1b->seek[i].cyl = stage2_list[i].chs.cyl;
|
||||
stage1b->seek[i].head = stage2_list[i].chs.head |
|
||||
((stage2_list[i].chs.cyl >> 12) & 0xfff0);
|
||||
stage1b->seek[i].sec = stage2_list[i].chs.sec;
|
||||
stage1b->ssrt[i].read.address_lo = ZIPL_STAGE2_LOAD_ADDRESS +
|
||||
i * stage2_list[i].chs.size;
|
||||
stage1b->ssrt[i].read.flags = CCW_FLAG_CC | CCW_FLAG_SLI;
|
||||
}
|
||||
/* Terminate CCW chain */
|
||||
stage1b->ssrt[i - 1].read.flags &= ~CCW_FLAG_CC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
boot_get_tape_ipl(void** data, size_t* size, address_t parm_addr,
|
||||
address_t initrd_addr, address_t image_addr)
|
||||
{
|
||||
struct boot_tape_ipl_params params;
|
||||
void* buffer;
|
||||
|
||||
if (image_addr != (image_addr & PSW_ADDRESS_MASK)) {
|
||||
error_reason("Kernel image load address to high (31 bit "
|
||||
"addressing mode)");
|
||||
return -1;
|
||||
}
|
||||
buffer = misc_malloc(DATA_SIZE(tape0));
|
||||
if (buffer == NULL)
|
||||
return -1;
|
||||
/* Prepare params section */
|
||||
params.parm_addr = (uint64_t) parm_addr;
|
||||
params.initrd_addr = (uint64_t) initrd_addr;
|
||||
params.load_psw = (uint64_t) (image_addr | PSW_LOAD);
|
||||
/* Initialize buffer */
|
||||
memcpy(buffer, DATA_ADDR(tape0), DATA_SIZE(tape0));
|
||||
memcpy(VOID_ADD(buffer, BOOT_TAPE_IPL_PARAMS_OFFSET), ¶ms,
|
||||
sizeof(struct boot_tape_ipl_params));
|
||||
*data = buffer;
|
||||
*size = DATA_SIZE(tape0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct menu_buffer {
|
||||
void *start;
|
||||
size_t size;
|
||||
size_t off;
|
||||
};
|
||||
|
||||
static void
|
||||
mb_init(struct menu_buffer *buffer, void *data, size_t size)
|
||||
{
|
||||
buffer-> start = data;
|
||||
buffer->size = size;
|
||||
buffer->off = 0;
|
||||
memset(data, 0, size);
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
mb_alloc(struct menu_buffer *buffer, size_t len)
|
||||
{
|
||||
void *result;
|
||||
|
||||
if (buffer->off + len > buffer->size)
|
||||
return NULL;
|
||||
result = VOID_ADD(buffer->start, buffer->off);
|
||||
buffer->off += len;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static void*
|
||||
mb_sprintf(struct menu_buffer *buffer, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int size;
|
||||
int len;
|
||||
char *str;
|
||||
|
||||
str = VOID_ADD(buffer->start, buffer->off);
|
||||
size = buffer->size - buffer->off;
|
||||
va_start(args, fmt);
|
||||
len = vsnprintf(str, size, fmt, args);
|
||||
va_end(args);
|
||||
if (len < 0 || len >= size)
|
||||
return NULL;
|
||||
misc_ascii_to_ebcdic((unsigned char *) str,
|
||||
(unsigned char *) str + len);
|
||||
mb_alloc(buffer, len + 1);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
store_stage2_menu(void* data, size_t size, struct job_data* job)
|
||||
{
|
||||
struct boot_stage2_params* params;
|
||||
char* name;
|
||||
int flag;
|
||||
int timeout;
|
||||
int i;
|
||||
struct menu_buffer mb;
|
||||
void *str;
|
||||
uint64_t config_kdump = 0;
|
||||
|
||||
mb_init(&mb, data, size);
|
||||
if (job->id == job_menu) {
|
||||
name = "-";
|
||||
for (i = 0; i < job->data.menu.num; i++) {
|
||||
if (job->data.menu.entry[i].id == job_ipl &&
|
||||
job->data.menu.entry[i].data.ipl.is_kdump)
|
||||
/* we start with 2nd bit */
|
||||
config_kdump |= (0x1 << (i + 1));
|
||||
if (job->data.menu.entry[i].pos ==
|
||||
job->data.menu.default_pos) {
|
||||
if (job->data.menu.entry[i].id == job_ipl &&
|
||||
job->data.menu.entry[i].data.ipl.is_kdump)
|
||||
/* default entry is first bit */
|
||||
config_kdump |= 0x1;
|
||||
name = job->data.menu.entry[i].name;
|
||||
}
|
||||
}
|
||||
flag = (job->data.menu.prompt != 0);
|
||||
timeout = job->data.menu.timeout;
|
||||
/* Be verbose */
|
||||
if (verbose) {
|
||||
printf("Preparing boot menu\n");
|
||||
printf(" Interactive prompt......: %s\n",
|
||||
job->data.menu.prompt ? "enabled" : "disabled");
|
||||
if (job->data.menu.timeout == 0)
|
||||
printf(" Menu timeout............: "
|
||||
"disabled\n");
|
||||
else
|
||||
printf(" Menu timeout............: %d "
|
||||
"seconds\n", job->data.menu.timeout);
|
||||
printf(" Default configuration...: '%s'\n", name);
|
||||
}
|
||||
} else {
|
||||
if (job->id == job_ipl && job->data.ipl.is_kdump)
|
||||
config_kdump |= 0x1;
|
||||
name = job->name;
|
||||
flag = 0;
|
||||
timeout = 0;
|
||||
}
|
||||
/* Header */
|
||||
params = mb_alloc(&mb, sizeof(struct boot_stage2_params));
|
||||
if (!params)
|
||||
goto err_nospace;
|
||||
params->flag = flag;
|
||||
params->config_kdump = config_kdump;
|
||||
params->timeout = timeout;
|
||||
/* Banner text */
|
||||
str = mb_sprintf(&mb, "zIPL v%s interactive boot menu\n ",
|
||||
RELEASE_STRING);
|
||||
if (!str)
|
||||
goto err_nospace;
|
||||
params->banner = (uint16_t) ((unsigned long) str -
|
||||
(unsigned long) data);
|
||||
/* Default config text */
|
||||
if (name != NULL)
|
||||
str = mb_sprintf(&mb, " 0. default (%s)", name);
|
||||
else
|
||||
str = mb_sprintf(&mb, " 0. default");
|
||||
if (!str)
|
||||
goto err_nospace;
|
||||
params->config[0] = (uint16_t) ((unsigned long) str -
|
||||
(unsigned long) data);
|
||||
/* Skip rest if job is not an actual menu */
|
||||
if (job->id != job_menu)
|
||||
return 0;
|
||||
/* Config texts */
|
||||
for (i = 0; i < job->data.menu.num; i++) {
|
||||
const char *kdump_str = "";
|
||||
if (job->data.menu.entry[i].data.ipl.is_kdump)
|
||||
kdump_str = " (kdump)";
|
||||
str = mb_sprintf(&mb, "%2d. %s%s",
|
||||
job->data.menu.entry[i].pos,
|
||||
job->data.menu.entry[i].name,
|
||||
kdump_str);
|
||||
if (!str)
|
||||
goto err_nospace_user;
|
||||
params->config[job->data.menu.entry[i].pos] = (uint16_t)
|
||||
((unsigned long) str - (unsigned long) data);
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_nospace:
|
||||
error_reason("Not enough room for menu data");
|
||||
return -1;
|
||||
err_nospace_user:
|
||||
error_reason("Not enough room for menu data (try fewer sections or "
|
||||
"shorter names)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
boot_get_fba_stage2(void** data, size_t* size, struct job_data* job)
|
||||
{
|
||||
void* buffer;
|
||||
int rc;
|
||||
|
||||
buffer = misc_malloc(STAGE2_MAX_SIZE);
|
||||
if (buffer == NULL)
|
||||
return -1;
|
||||
memcpy(buffer, DATA_ADDR(fba2), DATA_SIZE(fba2));
|
||||
rc = store_stage2_menu(VOID_ADD(buffer, DATA_SIZE(fba2)),
|
||||
STAGE2_MAX_SIZE - DATA_SIZE(fba2),
|
||||
job);
|
||||
if (rc) {
|
||||
free(buffer);
|
||||
return rc;
|
||||
}
|
||||
*data = buffer;
|
||||
*size = STAGE2_MAX_SIZE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
boot_get_eckd_stage2(void** data, size_t* size, struct job_data* job)
|
||||
{
|
||||
void* buffer;
|
||||
int rc;
|
||||
|
||||
buffer = misc_malloc(STAGE2_MAX_SIZE);
|
||||
if (buffer == NULL)
|
||||
return -1;
|
||||
memcpy(buffer, DATA_ADDR(eckd2), DATA_SIZE(eckd2));
|
||||
rc = store_stage2_menu(VOID_ADD(buffer, DATA_SIZE(eckd2)),
|
||||
STAGE2_MAX_SIZE - DATA_SIZE(eckd2),
|
||||
job);
|
||||
if (rc) {
|
||||
free(buffer);
|
||||
return rc;
|
||||
}
|
||||
*data = buffer;
|
||||
*size = STAGE2_MAX_SIZE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
boot_get_tape_dump(void** data, size_t* size, uint64_t mem)
|
||||
{
|
||||
void* buffer;
|
||||
|
||||
buffer = misc_malloc(DATA_SIZE(tape2dump));
|
||||
if (buffer == NULL)
|
||||
return -1;
|
||||
memcpy(buffer, DATA_ADDR(tape2dump), DATA_SIZE(tape2dump));
|
||||
/* Write mem size to end of dump record */
|
||||
memcpy(VOID_ADD(buffer, DATA_SIZE(tape2dump) - sizeof(mem)), &mem,
|
||||
sizeof(mem));
|
||||
*data = buffer;
|
||||
*size = DATA_SIZE(tape2dump);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
boot_get_eckd_dump_stage2(void** data, size_t* size, uint64_t mem)
|
||||
{
|
||||
void* buffer;
|
||||
|
||||
buffer = misc_malloc(DATA_SIZE(eckd2dump_sv));
|
||||
if (buffer == NULL)
|
||||
return -1;
|
||||
memcpy(buffer, DATA_ADDR(eckd2dump_sv), DATA_SIZE(eckd2dump_sv));
|
||||
/* Write mem size to end of dump record */
|
||||
memcpy(VOID_ADD(buffer, DATA_SIZE(eckd2dump_sv) - sizeof(mem)),
|
||||
&mem, sizeof(mem));
|
||||
*data = buffer;
|
||||
*size = DATA_SIZE(eckd2dump_sv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
boot_get_eckd_mvdump_stage2(void** data, size_t* size, uint64_t mem,
|
||||
uint8_t force, struct mvdump_parm_table parm)
|
||||
{
|
||||
void* buffer;
|
||||
|
||||
buffer = misc_malloc(DATA_SIZE(eckd2dump_mv));
|
||||
if (buffer == NULL)
|
||||
return -1;
|
||||
memcpy(buffer, DATA_ADDR(eckd2dump_mv), DATA_SIZE(eckd2dump_mv));
|
||||
/* Write mem size and force indicator (as specified by zipl -M)
|
||||
* to end of dump record, right before 512-byte parameter table */
|
||||
memcpy(VOID_ADD(buffer, DATA_SIZE(eckd2dump_mv) - sizeof(mem) -
|
||||
sizeof(struct mvdump_parm_table)), &mem, sizeof(mem));
|
||||
memcpy(VOID_ADD(buffer, DATA_SIZE(eckd2dump_mv) - sizeof(mem) -
|
||||
sizeof(force) - sizeof(struct mvdump_parm_table)),
|
||||
&force, sizeof(force));
|
||||
memcpy(VOID_ADD(buffer, DATA_SIZE(eckd2dump_mv) -
|
||||
sizeof(struct mvdump_parm_table)), &parm,
|
||||
sizeof(struct mvdump_parm_table));
|
||||
*data = buffer;
|
||||
*size = DATA_SIZE(eckd2dump_mv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
boot_get_fba_dump_stage2(void** data, size_t* size, uint64_t mem)
|
||||
{
|
||||
void* buffer;
|
||||
|
||||
buffer = misc_malloc(DATA_SIZE(fba2dump));
|
||||
if (buffer == NULL)
|
||||
return -1;
|
||||
memcpy(buffer, DATA_ADDR(fba2dump), DATA_SIZE(fba2dump));
|
||||
/* Write mem size to end of dump record */
|
||||
memcpy(VOID_ADD(buffer, DATA_SIZE(fba2dump) - sizeof(mem)),
|
||||
&mem, sizeof(mem));
|
||||
*data = buffer;
|
||||
*size = DATA_SIZE(fba2dump);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
boot_get_dump_info(struct boot_info *boot_info, uint8_t dev_type, void *param)
|
||||
{
|
||||
memset(boot_info, 0, sizeof(*boot_info));
|
||||
memcpy(&boot_info->magic, BOOT_INFO_MAGIC, sizeof(boot_info->magic));
|
||||
boot_info->flags |= BOOT_INFO_FLAGS_ARCH;
|
||||
boot_info->dev_type = dev_type;
|
||||
boot_info->bp_type = BOOT_INFO_BP_TYPE_DUMP;
|
||||
boot_info->version = BOOT_INFO_VERSION;
|
||||
memcpy(&boot_info->bp.dump.param, param,
|
||||
sizeof(boot_info->bp.dump.param));
|
||||
}
|
||||
|
||||
void
|
||||
boot_get_ipl_info(struct boot_info *boot_info, uint8_t dev_type,
|
||||
disk_blockptr_t *bm_ptr, struct disk_info *info)
|
||||
{
|
||||
memset(boot_info, 0, sizeof(*boot_info));
|
||||
memcpy(&boot_info->magic, BOOT_INFO_MAGIC, sizeof(boot_info->magic));
|
||||
boot_info->flags |= BOOT_INFO_FLAGS_ARCH;
|
||||
boot_info->dev_type = dev_type;
|
||||
boot_info->bp_type = BOOT_INFO_BP_TYPE_IPL;
|
||||
boot_info->version = BOOT_INFO_VERSION;
|
||||
bootmap_store_blockptr(&boot_info->bp, bm_ptr, info);
|
||||
}
|
||||
|
||||
+1046
File diff suppressed because it is too large
Load Diff
+1112
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* zipl - zSeries Initial Program Loader tool
|
||||
*
|
||||
* Functions to handle error messages
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "error.h"
|
||||
|
||||
|
||||
#define ERROR_STRING_SIZE 1024
|
||||
|
||||
static char error_reason_string[ERROR_STRING_SIZE];
|
||||
static char error_text_string[ERROR_STRING_SIZE];
|
||||
|
||||
static int error_is_reason = 0;
|
||||
static int error_is_text = 0;
|
||||
|
||||
|
||||
/* Specify the actual reason why an operation failed by providing a formatted
|
||||
* text string FMT and a variable amount of extra arguments. */
|
||||
void
|
||||
error_reason(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vsnprintf(error_reason_string, ERROR_STRING_SIZE, fmt, args);
|
||||
va_end(args);
|
||||
error_is_reason = 1;
|
||||
}
|
||||
|
||||
|
||||
/* Specify the (higher level) tool operation failure by providing a formatted
|
||||
* text string FMT and a variable amount of extra arguments. */
|
||||
void
|
||||
error_text(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vsnprintf(error_text_string, ERROR_STRING_SIZE, fmt, args);
|
||||
va_end(args);
|
||||
error_is_text = 1;
|
||||
}
|
||||
|
||||
|
||||
/* Clear a previously specified error_reason() message. */
|
||||
void
|
||||
error_clear_reason(void)
|
||||
{
|
||||
error_is_reason = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Clear a previously specified error_text() message. */
|
||||
void
|
||||
error_clear_text(void)
|
||||
{
|
||||
error_is_text = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Print out the error reason and text message to stderr. */
|
||||
void
|
||||
error_print(void)
|
||||
{
|
||||
if (error_is_text && error_is_reason) {
|
||||
fprintf(stderr, "Error: %s: %s\n", error_text_string,
|
||||
error_reason_string);
|
||||
} else if (error_is_text)
|
||||
fprintf(stderr, "Error: %s\n", error_text_string);
|
||||
else if (error_is_reason)
|
||||
fprintf(stderr, "Error: %s\n", error_reason_string);
|
||||
else
|
||||
fprintf(stderr, "Error: An unspecified error occurred\n");
|
||||
}
|
||||
+1202
File diff suppressed because it is too large
Load Diff
+1847
File diff suppressed because it is too large
Load Diff
+606
@@ -0,0 +1,606 @@
|
||||
/*
|
||||
* zipl - zSeries Initial Program Loader tool
|
||||
*
|
||||
* Miscellaneous helper functions
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "error.h"
|
||||
#include "misc.h"
|
||||
|
||||
|
||||
/* Allocate SIZE bytes of memory. Upon success, return pointer to memory.
|
||||
* Return NULL otherwise. */
|
||||
void *
|
||||
misc_malloc(size_t size)
|
||||
{
|
||||
void* result;
|
||||
|
||||
result = malloc(size);
|
||||
if (result == NULL) {
|
||||
error_reason("Could not allocate %lld bytes of memory",
|
||||
(unsigned long long) size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* asprintf with misc error checking */
|
||||
int misc_asprintf(char** out, const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int rc;
|
||||
|
||||
va_start(ap, fmt);
|
||||
rc = vasprintf(out, fmt, ap);
|
||||
va_end(ap);
|
||||
if (rc == -1)
|
||||
error_reason("Could not allocate space for new string");
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Allocate N * SIZE bytes of memory. Upon success, return pointer to memory.
|
||||
* Return NULL otherwise. */
|
||||
void *
|
||||
misc_calloc(size_t n, size_t size)
|
||||
{
|
||||
void* result;
|
||||
|
||||
result = calloc(n, size);
|
||||
if (result == NULL) {
|
||||
error_reason("Could not allocate %lld bytes of memory",
|
||||
(unsigned long long) n *
|
||||
(unsigned long long) size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* Duplicate the given string S. Upon success, return pointer to new string.
|
||||
* Return NULL otherwise. */
|
||||
char *
|
||||
misc_strdup(const char* s)
|
||||
{
|
||||
char* result;
|
||||
|
||||
result = strdup(s);
|
||||
if (result == NULL) {
|
||||
error_reason("Could not allocate %lld bytes of memory",
|
||||
(unsigned long long) strlen(s) + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* Open file exclusive */
|
||||
int
|
||||
misc_open_exclusive(const char* filename)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = open(filename, O_RDWR | O_EXCL);
|
||||
if (fd == -1 && errno == EBUSY)
|
||||
error_reason("Device is in use (probably mounted)");
|
||||
else if (fd == -1)
|
||||
error_reason(strerror(errno));
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
/* Read COUNT bytes of data from file identified by file descriptor FD to
|
||||
* memory at location BUFFER. Return 0 when all bytes were successfully read,
|
||||
* non-zero otherwise. */
|
||||
int
|
||||
misc_read(int fd, void* buffer, size_t count)
|
||||
{
|
||||
size_t done;
|
||||
ssize_t rc;
|
||||
|
||||
for (done=0; done < count; done += rc) {
|
||||
rc = read(fd, VOID_ADD(buffer, done), count - done);
|
||||
if (rc == -1) {
|
||||
error_reason(strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if(rc == 0) {
|
||||
error_reason("Reached unexpected end of file");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Read all of file FILENAME to memory. Upon success, return 0 and set BUFFER
|
||||
* to point to the data and SIZE (if non-NULL) to contain the file size.
|
||||
* If NIL_TERMINATE is non-zero, a nil-char will be added to the buffer string
|
||||
* Return non-zero otherwise. */
|
||||
int
|
||||
misc_read_file(const char* filename, char** buffer, size_t* size,
|
||||
int nil_terminate)
|
||||
{
|
||||
struct stat stats;
|
||||
void* data;
|
||||
int fd;
|
||||
int rc;
|
||||
|
||||
if (stat(filename, &stats)) {
|
||||
error_reason(strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (!S_ISREG(stats.st_mode)) {
|
||||
error_reason("Not a regular file");
|
||||
return -1;
|
||||
}
|
||||
data = misc_malloc(stats.st_size + (nil_terminate ? 1 : 0));
|
||||
if (data == NULL)
|
||||
return -1;
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
error_reason(strerror(errno));
|
||||
free(data);
|
||||
return -1;
|
||||
}
|
||||
rc = misc_read(fd, data, stats.st_size);
|
||||
close(fd);
|
||||
if (rc) {
|
||||
free(data);
|
||||
return rc;
|
||||
}
|
||||
*buffer = data;
|
||||
if (size != NULL)
|
||||
*size = stats.st_size;
|
||||
if (nil_terminate) {
|
||||
if (size != NULL)
|
||||
(*size)++;
|
||||
(*buffer)[stats.st_size] = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define INITIAL_FILE_BUFFER_SIZE 1024
|
||||
|
||||
/* Read file into buffer without querying its size (necessary for reading files
|
||||
* from /proc or /sys). Upon success, return zero and set BUFFER to point to
|
||||
* the file buffer and SIZE (if non-null) to contain the file size. Return
|
||||
* non-zero otherwise. Add a null-byte at the end of the buffer if
|
||||
* NIL_TERMINATE is non-zero. */
|
||||
int
|
||||
misc_read_special_file(const char* filename, char** buffer, size_t* size,
|
||||
int nil_terminate)
|
||||
{
|
||||
FILE* file;
|
||||
char* data;
|
||||
char* new_data;
|
||||
size_t count;
|
||||
size_t current_size;
|
||||
int current;
|
||||
|
||||
file = fopen(filename, "r");
|
||||
if (file == NULL) {
|
||||
error_reason(strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
current_size = INITIAL_FILE_BUFFER_SIZE;
|
||||
count = 0;
|
||||
data = (char *) misc_malloc(current_size);
|
||||
if (data == NULL) {
|
||||
fclose(file);
|
||||
return -1;
|
||||
}
|
||||
current = fgetc(file);
|
||||
while (current != EOF || nil_terminate) {
|
||||
if (current == EOF) {
|
||||
current = 0;
|
||||
nil_terminate = 0;
|
||||
}
|
||||
data[count++] = (char) current;
|
||||
if (count >= current_size) {
|
||||
new_data = (char *) misc_malloc(current_size * 2);
|
||||
if (new_data == NULL) {
|
||||
free(data);
|
||||
fclose(file);
|
||||
return -1;
|
||||
}
|
||||
memcpy(new_data, data, current_size);
|
||||
free(data);
|
||||
data = new_data;
|
||||
current_size *= 2;
|
||||
}
|
||||
current = fgetc(file);
|
||||
}
|
||||
fclose(file);
|
||||
*buffer = data;
|
||||
if (size)
|
||||
*size = count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Get contents of file identified by FILENAME and fill in the respective
|
||||
* fields of FILE. Return 0 on success, non-zero otherwise. */
|
||||
int
|
||||
misc_get_file_buffer(const char* filename, struct misc_file_buffer* file)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = misc_read_file(filename, &file->buffer, &file->length, 0);
|
||||
file->pos = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Free resources allocated for file buffer FILE. */
|
||||
void
|
||||
misc_free_file_buffer(struct misc_file_buffer* file)
|
||||
{
|
||||
if (file->buffer != NULL) {
|
||||
free(file->buffer);
|
||||
file->buffer = NULL;
|
||||
file->pos = 0;
|
||||
file->length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Return character at current FILE buffer position plus READAHEAD or EOF if
|
||||
* at end of file. */
|
||||
int
|
||||
misc_get_char(struct misc_file_buffer* file, off_t readahead)
|
||||
{
|
||||
if (file->buffer != NULL)
|
||||
if ((size_t) (file->pos + readahead) < file->length)
|
||||
return file->buffer[file->pos + readahead];
|
||||
return EOF;
|
||||
}
|
||||
|
||||
|
||||
char*
|
||||
misc_make_path(char* dirname, char* filename)
|
||||
{
|
||||
char* result;
|
||||
size_t len;
|
||||
|
||||
len = strlen(dirname) + strlen(filename) + 2;
|
||||
result = (char *) misc_malloc(len);
|
||||
if (result == NULL)
|
||||
return NULL;
|
||||
sprintf(result, "%s/%s", dirname, filename);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
#define TEMP_DEV_MAX_RETRIES 1000
|
||||
|
||||
int
|
||||
misc_temp_dev(dev_t dev, int blockdev, char** devno)
|
||||
{
|
||||
char* result;
|
||||
char* pathname[] = { "/dev", getenv("TMPDIR"), "/tmp",
|
||||
getenv("HOME"), "." , "/"};
|
||||
char filename[] = "zipl0000";
|
||||
mode_t mode;
|
||||
unsigned int path;
|
||||
int retry;
|
||||
int rc;
|
||||
int fd;
|
||||
|
||||
if (blockdev)
|
||||
mode = S_IFBLK | S_IRWXU;
|
||||
else
|
||||
mode = S_IFCHR | S_IRWXU;
|
||||
/* Try several locations as directory for the temporary device
|
||||
* node. */
|
||||
for (path=0; path < ARRAY_SIZE(pathname); path++) {
|
||||
if (pathname[path] == NULL)
|
||||
continue;
|
||||
for (retry=0; retry < TEMP_DEV_MAX_RETRIES; retry++) {
|
||||
sprintf(filename, "zipl%04d", retry);
|
||||
result = misc_make_path(pathname[path], filename);
|
||||
if (result == NULL)
|
||||
return -1;
|
||||
rc = mknod(result, mode, dev);
|
||||
if (rc == 0) {
|
||||
/* Need this test to cover 'nodev'-mounted
|
||||
* filesystems. */
|
||||
fd = open(result, O_RDWR);
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
*devno = result;
|
||||
return 0;
|
||||
}
|
||||
remove(result);
|
||||
retry = TEMP_DEV_MAX_RETRIES;
|
||||
} else if (errno != EEXIST)
|
||||
retry = TEMP_DEV_MAX_RETRIES;
|
||||
free(result);
|
||||
}
|
||||
}
|
||||
error_text("Unable to create temporary device node");
|
||||
error_reason(strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* Create a temporary device node for the device containing FILE. Upon
|
||||
* success, return zero and store a pointer to the name of the device node
|
||||
* file into DEVNO. Return non-zero otherwise. */
|
||||
int
|
||||
misc_temp_dev_from_file(char* file, char** devno)
|
||||
{
|
||||
struct stat stats;
|
||||
|
||||
if (stat(file, &stats)) {
|
||||
error_reason(strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return misc_temp_dev(stats.st_dev, 1, devno);
|
||||
}
|
||||
|
||||
|
||||
/* Delete temporary device node DEVICE and free memory allocated for device
|
||||
* name. */
|
||||
void
|
||||
misc_free_temp_dev(char* device)
|
||||
{
|
||||
if (remove(device)) {
|
||||
fprintf(stderr, "Warning: Could not remove "
|
||||
"temporary file %s: %s",
|
||||
device, strerror(errno));
|
||||
}
|
||||
free(device);
|
||||
}
|
||||
|
||||
|
||||
/* Write COUNT bytes from memory at location DATA to the file identified by
|
||||
* file descriptor FD. Return 0 when all bytes were successfully written,
|
||||
* non-zero otherwise. */
|
||||
int
|
||||
misc_write(int fd, const void* data, size_t count)
|
||||
{
|
||||
size_t written;
|
||||
ssize_t rc;
|
||||
|
||||
for (written=0; written < count; written += rc) {
|
||||
rc = write(fd, VOID_ADD(data, written), count - written);
|
||||
if (rc == -1) {
|
||||
error_reason(strerror(errno));
|
||||
error_text("Could not write to device");
|
||||
return -1;
|
||||
}
|
||||
if (rc == 0) {
|
||||
error_reason("Write failed");
|
||||
error_text("Could not write to device");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int misc_seek(int fd, off_t off)
|
||||
{
|
||||
if (lseek(fd, off, SEEK_SET) == off)
|
||||
return 0;
|
||||
error_reason(strerror(errno));
|
||||
error_text("Could not seek on device");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int misc_pwrite(int fd, void *buf, size_t size, off_t off)
|
||||
{
|
||||
if (misc_seek(fd, off))
|
||||
return -1;
|
||||
return misc_write(fd, buf, size);
|
||||
}
|
||||
|
||||
int
|
||||
misc_check_writable_directory(const char* directory)
|
||||
{
|
||||
struct stat stats;
|
||||
|
||||
if (stat(directory, &stats)) {
|
||||
error_reason(strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (!S_ISDIR(stats.st_mode)) {
|
||||
error_reason("Not a directory");
|
||||
return -1;
|
||||
}
|
||||
if (access(directory, W_OK)) {
|
||||
error_reason(strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
misc_check_readable_file(const char* filename)
|
||||
{
|
||||
struct stat stats;
|
||||
|
||||
if (stat(filename, &stats)) {
|
||||
error_reason(strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (!S_ISREG(stats.st_mode)) {
|
||||
error_reason("Not a regular file");
|
||||
return -1;
|
||||
}
|
||||
if (access(filename, R_OK)) {
|
||||
error_reason(strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
misc_check_writable_device(const char* devno, int blockdev, int chardev)
|
||||
{
|
||||
struct stat stats;
|
||||
|
||||
if (stat(devno, &stats)) {
|
||||
error_reason(strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (blockdev && chardev) {
|
||||
if (!(S_ISCHR(stats.st_mode) || S_ISBLK(stats.st_mode))) {
|
||||
error_reason("Not a device");
|
||||
return -1;
|
||||
}
|
||||
} else if (blockdev) {
|
||||
if (!S_ISBLK(stats.st_mode)) {
|
||||
error_reason("Not a block device");
|
||||
return -1;
|
||||
}
|
||||
} else if (chardev) {
|
||||
if (!S_ISCHR(stats.st_mode)) {
|
||||
error_reason("Not a character device");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (access(devno, W_OK)) {
|
||||
error_reason(strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ASCII to EBCDIC conversion table. */
|
||||
unsigned char ascebc[256] =
|
||||
{
|
||||
0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F,
|
||||
0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26,
|
||||
0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F,
|
||||
0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D,
|
||||
0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61,
|
||||
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
|
||||
0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F,
|
||||
0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
|
||||
0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
|
||||
0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6,
|
||||
0xE7, 0xE8, 0xE9, 0xBA, 0xE0, 0xBB, 0xB0, 0x6D,
|
||||
0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||||
0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
|
||||
0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6,
|
||||
0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF
|
||||
};
|
||||
|
||||
/* EBCDIC to ASCII conversion table. */
|
||||
unsigned char ebcasc[256] =
|
||||
{
|
||||
/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */
|
||||
0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F,
|
||||
/* 0x08 -GE -SPS -RPT VT FF CR SO SI */
|
||||
0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
/* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC */
|
||||
0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07,
|
||||
/* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB */
|
||||
0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
||||
/* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC */
|
||||
0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B,
|
||||
/* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL */
|
||||
0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07,
|
||||
/* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */
|
||||
0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04,
|
||||
/* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */
|
||||
0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A,
|
||||
/* 0x40 SP RSP ? ---- */
|
||||
0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86,
|
||||
/* 0x48 . < ( + | */
|
||||
0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
|
||||
/* 0x50 & ---- */
|
||||
0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07,
|
||||
/* 0x58 ? ! $ * ) ; */
|
||||
0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA,
|
||||
/* 0x60 - / ---- ? ---- ---- ---- */
|
||||
0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F,
|
||||
/* 0x68 ---- , % _ > ? */
|
||||
0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
|
||||
/* 0x70 --- ---- ---- ---- ---- ---- ---- */
|
||||
0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
||||
/* 0x78 * ` : # @ ' = " */
|
||||
0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
|
||||
/* 0x80 * a b c d e f g */
|
||||
0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
|
||||
/* 0x88 h i ---- ---- ---- */
|
||||
0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1,
|
||||
/* 0x90 ? j k l m n o p */
|
||||
0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
|
||||
/* 0x98 q r ---- ---- */
|
||||
0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07,
|
||||
/* 0xA0 ~ s t u v w x */
|
||||
0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
||||
/* 0xA8 y z ---- ---- ---- ---- */
|
||||
0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07,
|
||||
/* 0xB0 ^ ---- ? ---- */
|
||||
0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC,
|
||||
/* 0xB8 ---- [ ] ---- ---- ---- ---- */
|
||||
0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07,
|
||||
/* 0xC0 { A B C D E F G */
|
||||
0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
|
||||
/* 0xC8 H I ---- ? ---- */
|
||||
0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07,
|
||||
/* 0xD0 } J K L M N O P */
|
||||
0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
|
||||
/* 0xD8 Q R ---- ? */
|
||||
0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98,
|
||||
/* 0xE0 \ S T U V W X */
|
||||
0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||||
/* 0xE8 Y Z ---- ? ---- ---- ---- */
|
||||
0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07,
|
||||
/* 0xF0 0 1 2 3 4 5 6 7 */
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
||||
/* 0xF8 8 9 ---- ---- ? ---- ---- ---- */
|
||||
0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07
|
||||
};
|
||||
|
||||
void misc_ebcdic_to_ascii(unsigned char *from, unsigned char *to)
|
||||
{
|
||||
for (; from != to; from++)
|
||||
*from = ebcasc[*from];
|
||||
}
|
||||
|
||||
void misc_ascii_to_ebcdic(unsigned char *from, unsigned char *to)
|
||||
{
|
||||
for (; from != to; from++)
|
||||
*from = ascebc[*from];
|
||||
}
|
||||
|
||||
|
||||
+1819
File diff suppressed because it is too large
Load Diff
+242
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* zipl - zSeries Initial Program Loader tool
|
||||
*
|
||||
* Main function
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#include "boot.h"
|
||||
#include "bootmap.h"
|
||||
#include "disk.h"
|
||||
#include "error.h"
|
||||
#include "install.h"
|
||||
#include "job.h"
|
||||
#include "misc.h"
|
||||
#include "zipl.h"
|
||||
|
||||
|
||||
/* Flag deciding the level of verbosity */
|
||||
int verbose = 0;
|
||||
|
||||
/* Flag deciding whether confirmation questions are asked */
|
||||
int interactive = 1;
|
||||
|
||||
/* Flag deciding whether actions should only be simulated */
|
||||
int dry_run = 1;
|
||||
|
||||
/* Full tool name */
|
||||
static const char tool_name[] = "zipl: zSeries Initial Program Loader";
|
||||
|
||||
/* Copyright notice */
|
||||
static const char copyright_notice[] = "Copyright IBM Corp. 2001, 2017";
|
||||
|
||||
/* Usage information */
|
||||
static const char* usage_text[] = {
|
||||
"Usage: zipl [OPTIONS] [SECTION]",
|
||||
"",
|
||||
"Prepare a device for initial program load. Use OPTIONS described below or ",
|
||||
"provide the name of a SECTION defined in the zIPL configuration file.",
|
||||
"",
|
||||
"-h, --help Print this help, then exit",
|
||||
"-v, --version Print version information, then exit",
|
||||
"-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",
|
||||
"-P, --parameters PARMLINE Use specified kernel PARMLINE",
|
||||
"-T, --tape TAPEDEV Install bootloader on tape device TAPEDEV",
|
||||
"-s, --segment SEGMENT,ADDR Install a segment from file SEGMENT",
|
||||
"-d, --dumpto DUMPDEV[,SIZE] Install a system dump record on tape device",
|
||||
" or disk partition DUMPDEV",
|
||||
"-M, --mvdump DEVLIST[,SIZE] Install a multi-volume dump record on each",
|
||||
" disk partition listed in file DEVLIST",
|
||||
"-f, --force Disable sanity check while producing a",
|
||||
" multi-volume dump",
|
||||
"-m, --menu MENU Install multi-boot configuration MENU",
|
||||
"-n, --noninteractive Answer all confirmation questions with 'yes'",
|
||||
"-V, --verbose Provide more verbose output",
|
||||
"-a, --add-files Add all referenced files to bootmap file",
|
||||
" --dry-run Simulate run but don't modify IPL records"
|
||||
};
|
||||
|
||||
|
||||
/* Print usage information. */
|
||||
static void
|
||||
print_usage(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i=0; i < ARRAY_SIZE(usage_text); i++)
|
||||
printf("%s\n", usage_text[i]);
|
||||
}
|
||||
|
||||
|
||||
/* Print version information. */
|
||||
static void
|
||||
print_version(void)
|
||||
{
|
||||
printf("%s version %s\n", tool_name, RELEASE_STRING);
|
||||
printf("%s\n", copyright_notice);
|
||||
}
|
||||
|
||||
|
||||
/* Check whether calling user is root. Return 0 if user is root, non-zero
|
||||
* otherwise. */
|
||||
static int
|
||||
check_for_root(void)
|
||||
{
|
||||
if (geteuid() != 0) {
|
||||
error_clear_text();
|
||||
error_reason("Must be root to perform this operation");
|
||||
return -1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
struct disk_info* info;
|
||||
disk_blockptr_t program_table, scsi_dump_sb_blockptr, *stage1b_list;
|
||||
blocknum_t stage1b_count;
|
||||
struct job_data* job;
|
||||
char* device;
|
||||
int rc;
|
||||
|
||||
/* Check internals */
|
||||
rc = boot_check_data();
|
||||
if (rc) {
|
||||
error_text("Internal error");
|
||||
error_print();
|
||||
return 1;
|
||||
}
|
||||
/* Find out what we're supposed to do */
|
||||
rc = job_get(argc, argv, &job);
|
||||
if (rc) {
|
||||
error_print();
|
||||
return 1;
|
||||
}
|
||||
/* Check for priority options --help and --version */
|
||||
if (job->id == job_print_usage) {
|
||||
print_usage();
|
||||
job_free(job);
|
||||
return 0;
|
||||
} else if (job->id == job_print_version) {
|
||||
print_version();
|
||||
job_free(job);
|
||||
return 0;
|
||||
}
|
||||
/* Make sure we're running as root */
|
||||
if (check_for_root()) {
|
||||
job_free(job);
|
||||
error_print();
|
||||
return 1;
|
||||
}
|
||||
/* Set global option variables */
|
||||
verbose = job->verbose;
|
||||
interactive = !job->noninteractive;
|
||||
dry_run = job->dry_run;
|
||||
if (dry_run)
|
||||
printf("Starting dry-run, target device contents will NOT be "
|
||||
"modified\n");
|
||||
/* Make sure new files are only user-accessible */
|
||||
umask(077);
|
||||
/* Do it */
|
||||
switch (job->id) {
|
||||
case job_dump_partition:
|
||||
if (disk_is_tape(job->data.dump.device) ||
|
||||
!disk_is_scsi(job->data.dump.device, &job->target)) {
|
||||
rc = install_dump(job->data.dump.device, &job->target,
|
||||
job->data.dump.mem);
|
||||
break;
|
||||
}
|
||||
/* Dump to a raw SCSI partition */
|
||||
if (job->data.dump.mem != -1uLL) {
|
||||
error_reason("Dump size can not be limited for "
|
||||
"partition dump on a SCSI disk");
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
rc = check_job_dump_images(&job->data.dump, job->name);
|
||||
if (rc != 0)
|
||||
break;
|
||||
/* Fall through. */
|
||||
case job_ipl:
|
||||
case job_segment:
|
||||
case job_menu:
|
||||
/* Create bootmap */
|
||||
stage1b_list = NULL;
|
||||
rc = bootmap_create(job, &program_table, &scsi_dump_sb_blockptr,
|
||||
&stage1b_list, &stage1b_count, &device,
|
||||
&info);
|
||||
if (rc)
|
||||
break;
|
||||
/* Install boot loader */
|
||||
rc = install_bootloader(device, &program_table,
|
||||
&scsi_dump_sb_blockptr, stage1b_list,
|
||||
stage1b_count, info, job);
|
||||
if (stage1b_list != NULL)
|
||||
free(stage1b_list);
|
||||
misc_free_temp_dev(device);
|
||||
disk_free_info(info);
|
||||
break;
|
||||
case job_ipl_tape:
|
||||
rc = install_tapeloader(job->data.ipl_tape.device,
|
||||
job->data.ipl_tape.image,
|
||||
job->data.ipl_tape.parmline,
|
||||
job->data.ipl_tape.ramdisk,
|
||||
job->data.ipl_tape.image_addr,
|
||||
job->data.ipl_tape.parm_addr,
|
||||
job->data.ipl_tape.ramdisk_addr);
|
||||
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);
|
||||
break;
|
||||
case job_print_usage:
|
||||
case job_print_version:
|
||||
/* Should not happen */
|
||||
break;
|
||||
}
|
||||
switch (rc) {
|
||||
case 0: /* Operation completed successfully */
|
||||
if (verbose)
|
||||
printf("Syncing disks...\n");
|
||||
if (!dry_run)
|
||||
sync();
|
||||
printf("Done.\n");
|
||||
break;
|
||||
case -2: /* Operation canceled by user */
|
||||
break;
|
||||
default: /* An error occurred */
|
||||
error_print();
|
||||
break;
|
||||
}
|
||||
job_free(job);
|
||||
return abs(rc);
|
||||
}
|
||||
@@ -0,0 +1,862 @@
|
||||
#!/usr/bin/perl -w
|
||||
#
|
||||
# zipl_helper.device-mapper: print zipl parameters for a device-mapper device
|
||||
#
|
||||
# Copyright IBM Corp. 2009, 2017
|
||||
#
|
||||
# s390-tools is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
#
|
||||
# Depending on the name by which the script is called, it serves one of two
|
||||
# purposes:
|
||||
#
|
||||
# 1. Usage: zipl_helper.device-mapper <target directory> or
|
||||
# <major:minor of target device>
|
||||
#
|
||||
# This tool attempts to obtain zipl parameters for a target directory or
|
||||
# partition 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 mirrored or accessed through a multipath
|
||||
# target)
|
||||
# - any mirror in the device-mapper setup must include block 0 of the
|
||||
# physical device
|
||||
#
|
||||
# 2. Usage: chreipl_helper.device-mapper <major:minor of target device>
|
||||
#
|
||||
# This tool identifies the physical device which contains the specified
|
||||
# device-mapper target devices. If the physical device was found, its
|
||||
# major:minor parameters are printed. Otherwise, the script exits with an
|
||||
# error message and a non-zero return code.
|
||||
#
|
||||
|
||||
use strict;
|
||||
use File::Basename;
|
||||
use POSIX qw/locale_h/;
|
||||
|
||||
# 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_dir($);
|
||||
sub get_physical_device($$);
|
||||
sub get_major_minor($);
|
||||
sub get_table($$);
|
||||
sub get_linear_data($$);
|
||||
sub get_mirror_data($$);
|
||||
sub get_multipath_status($);
|
||||
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($$);
|
||||
sub check_tools();
|
||||
|
||||
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);
|
||||
|
||||
# Setup and use a standard locale to
|
||||
# avoid localized scripting output
|
||||
$ENV{LC_ALL} = "C"; # for child processes
|
||||
setlocale(LC_ALL, "C"); # for current process
|
||||
|
||||
# Use alternate code path if called as chreipl helper
|
||||
if ($toolname eq "chreipl_helper.device-mapper") {
|
||||
if (!defined($ARGV[0]) || !($ARGV[0] =~ /^\s*(\d+)\s*:\s*(\d+)\s*$/)) {
|
||||
die("Usage: $toolname <major:minor of target devies>\n");
|
||||
}
|
||||
($phy_major, $phy_minor, $phy_offset, @target_list) =
|
||||
get_physical_device($1, $2);
|
||||
print("$phy_major:$phy_minor\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$directory = $ARGV[0];
|
||||
if (!defined($directory)) {
|
||||
die("Usage: $toolname <target directory> or <major:minor of target devies>\n");
|
||||
}
|
||||
|
||||
# check if needed tools are available
|
||||
check_tools();
|
||||
|
||||
if (($ARGV[0] =~ /^\s*(\d+)\s*:\s*(\d+)\s*$/)) {
|
||||
# Determine physical (non-dm) device on which partition is located
|
||||
($phy_major, $phy_minor, $phy_offset, @target_list) =
|
||||
get_physical_device($1,$2);
|
||||
}
|
||||
else
|
||||
{
|
||||
# Determine physical (non-dm) device on which directory is located
|
||||
($phy_major, $phy_minor, $phy_offset, @target_list) =
|
||||
get_physical_device_dir($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_from_dir(dir)
|
||||
# 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_dir($)
|
||||
{
|
||||
my ($directory) = @_;
|
||||
my ($major, $minor) = get_major_minor($directory);
|
||||
|
||||
return get_physical_device($major, $minor);
|
||||
}
|
||||
|
||||
# get_physical_device(major, minor)
|
||||
# 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 ($major, $minor) = @_;
|
||||
my $table;
|
||||
my $target;
|
||||
my $start;
|
||||
my $length;
|
||||
my @target_list;
|
||||
|
||||
$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;
|
||||
my $num;
|
||||
|
||||
# Remove log_type + #logargs + logargs
|
||||
splice(@argv, 0, $argv[1] + 2);
|
||||
if (!@argv) {
|
||||
goto out_error;
|
||||
}
|
||||
$num = shift(@argv);
|
||||
while ($num-- > 0) {
|
||||
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_status(device)
|
||||
# Return map of nodes to F/A flags (i.e. $map{$node} is either "A" or "F").
|
||||
# See linux/drivers/md/dm-mpath.c:multipath_status for details.
|
||||
sub get_multipath_status($)
|
||||
{
|
||||
my $dev = shift();
|
||||
my %map;
|
||||
my $str;
|
||||
my $failed = 0;
|
||||
|
||||
open(my $fh, "$dmsetup status /dev/$dev 2>/dev/null|") or return undef;
|
||||
while ($str = <$fh>) {
|
||||
# sample output (single line):
|
||||
# 0 67108864 multipath \
|
||||
# 2 0 0 \
|
||||
# 0 \
|
||||
# 2 2 \
|
||||
# E 0 \
|
||||
# 2 2 \
|
||||
# 8:16 F 1 \
|
||||
# 0 1 \
|
||||
# 8:0 F 1 \
|
||||
# 0 1 \
|
||||
# A 0 \
|
||||
# 2 2 \
|
||||
# 8:32 A 0 \
|
||||
# 0 1 \
|
||||
# 8:48 A 0 \
|
||||
# 0 1
|
||||
my @line = split(/\s+/, $str);
|
||||
next if !@line;
|
||||
|
||||
my ($start, $length, $type) = splice(@line, 0, 3);
|
||||
next if $type ne "multipath";
|
||||
|
||||
my $cnt = shift(@line); # Remove #multipath_feature_args +
|
||||
splice(@line, 0, $cnt) if $cnt > 0; # multipath_feature_args
|
||||
|
||||
$cnt = shift(@line); # Remove #handler_status_args +
|
||||
splice(@line, 0, $cnt) if $cnt > 0; # handler_status_args
|
||||
|
||||
# num_groups init_group_number ...
|
||||
my ($ngr, $ign) = splice(@line, 0, 2);
|
||||
for (my $g = 0; $g < $ngr; $g++) {
|
||||
# Remove group_state + #ps_status_args
|
||||
# group_state: D(isabled), A(ctive), or E(nabled)
|
||||
my ($state, $cnt) = splice(@line, 0, 2);
|
||||
# Remove ps_status_args*
|
||||
splice(@line, 0, $cnt) if $cnt > 0;
|
||||
# Remove #paths + #selector_args
|
||||
my ($paths, $nsa) = splice(@line, 0, 2);
|
||||
for (my $p = 0; $p < $paths; $p++) {
|
||||
# Fetch single path description
|
||||
my ($node, $active, $fail_cnt)
|
||||
= splice(@line, 0, 3);
|
||||
# active: A(ctive) or F(ailed)
|
||||
$map{$node} = $active;
|
||||
$failed++ if $active ne "A";
|
||||
# Remove selector_args*
|
||||
splice(@line, 0, $nsa) if $nsa > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
close($fh);
|
||||
|
||||
die ("Error: No paths found for '$dev'\n") if scalar(keys %map) == 0;
|
||||
if ($failed) {
|
||||
die ("Error: All paths for '$dev' failed\n")
|
||||
if $failed == scalar(keys %map);
|
||||
|
||||
print(STDERR "Warning: There are one or more failed paths for"
|
||||
." device '$dev'\n");
|
||||
}
|
||||
return \%map;
|
||||
}
|
||||
|
||||
# get_multipath_data(dev_name, args)
|
||||
# Returns [[major1, minor1], [major2, minor2], ..]
|
||||
sub get_multipath_data($$)
|
||||
{
|
||||
my ($dev_name, $args) = @_;
|
||||
my $status = get_multipath_status($dev_name);
|
||||
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]) if $status->{$argv[0]} eq "A";
|
||||
# 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(major, minor)
|
||||
# Return the partition offset of device.
|
||||
sub get_partition_start($$)
|
||||
{
|
||||
my ($major, $minor) = @_;
|
||||
my $dir = "/sys/dev/block/$major:$minor";
|
||||
my $offset;
|
||||
local *HANDLE;
|
||||
|
||||
return undef if (!-d $dir);
|
||||
|
||||
open(HANDLE, "<", "$dir/start") or return 0;
|
||||
$offset = <HANDLE>;
|
||||
close(HANDLE);
|
||||
|
||||
chomp($offset);
|
||||
|
||||
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($major, $minor);
|
||||
unlink($dev);
|
||||
if (!defined($partstart)) {
|
||||
die("Error: Could not determine partition start for ".
|
||||
get_device_name($major, $minor)."\n");
|
||||
}
|
||||
# Convert partition start in sectors to blocks
|
||||
$partstart = $partstart / ($blocksize / $SECTOR_SIZE);
|
||||
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;
|
||||
}
|
||||
|
||||
sub check_tools()
|
||||
{
|
||||
system("$dmsetup --version &> /dev/null") >> 8 != 127
|
||||
or die("Error: dmsetup not found\n");
|
||||
system("$mknod --version &> /dev/null") >> 8 != 127
|
||||
or die("Error: mknod not found\n");
|
||||
system("$dasdview --version &> /dev/null") >> 8 != 127
|
||||
or die("Error: dasdview not found\n");
|
||||
system("$blockdev --version &> /dev/null") >> 8 != 127
|
||||
or die("Error: blockdev not found\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user