Hello all, this next building block of the kboot_sl code contains the parts that are specific for the s390 platform. The code can also serve as an example how manual device activation or 'unusual' boot disks could be added for other platforms. best regards Michael *** patch follows *** diff -Nuar kboot-10/doc/description.lyx kboot-10_patch4/doc/description.lyx --- kboot-10/doc/description.lyx 2006-06-21 14:58:30.000000000 +0200 +++ kboot-10_patch4/doc/description.lyx 2006-06-27 16:41:41.000000000 +0200 @@ -46,7 +46,7 @@ \end_layout \begin_layout Date -$Id: specs.lyx,v 1.21 2006/06/21 11:41:27 loehr Exp $ +$Id: description.lyx,v 1.1.2.1 2006/06/27 14:41:41 loehr Exp $ \end_layout \begin_layout Standard diff -Nuar kboot-10/ui/bootmap_common.c kboot-10_patch4/ui/bootmap_common.c --- kboot-10/ui/bootmap_common.c 1970-01-01 01:00:00.000000000 +0100 +++ kboot-10_patch4/ui/bootmap_common.c 2006-02-10 16:09:35.000000000 +0100 @@ -0,0 +1,1231 @@ +/*! + * (C) Copyright IBM Corp. 2005, 2005 + * + * \file bootmap.c + * \brief Common functions to load bootmap data + * + * $Id: bootmap_common.c,v 1.10 2006/02/10 15:09:35 loehr Exp $ + * + * Author(s): Ralph Wuerthner (rwuerthn@de.ibm.com) + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kboot.h" +#include "loader.h" +#include "bootmap.h" + + +// Most of the following definitions are take from zipl + +// from linux/fs.h +#define BLKGETSIZE _IO(0x12, 96) +#define BLKSSZGET _IO(0x12, 104) + +// from linux/hdregs.h +#define HDIO_GETGEO 0x0301 + +#define DASD_IOCTL_LETTER 'D' +#define BIODASDINFO _IOR(DASD_IOCTL_LETTER, 1, \ + struct dasd_information) + +// Layout of SCSI disk block pointer +struct scsi_blockptr { + uint64_t blockno; + uint16_t size; + uint16_t blockct; + uint8_t reserved[4]; +} __attribute((packed)); + +// Layout of FBA disk block pointer +struct fba_blockptr { + uint32_t blockno; + uint16_t size; + uint16_t blockct; +} __attribute((packed)); + +// Layout of ECKD disk block pointer +struct eckd_blockptr { + uint16_t cyl; + uint16_t head; + uint8_t sec; + uint16_t size; + uint8_t blockct; +} __attribute((packed)); + +// Layout of component table entry +struct component_entry { + uint8_t data[23]; + uint8_t type; + union { + uint64_t load_address; + uint64_t load_psw; + } address; +} __attribute((packed)); + +typedef enum { + component_execute = 0x01, + component_load = 0x02 +} component_type; + +// Component table entry +struct component { + disk_blockptr_t segment_ptr; + component_type type; + union { + uint64_t load_address; + uint64_t load_psw; + } address; +}; + + +// Definitions for dasd device driver, taken from linux/include/asm/dasd.h +struct dasd_information { + unsigned int devno; /* S/390 devno */ + unsigned int real_devno; /* for aliases */ + unsigned int schid; /* S/390 subchannel identifier */ + unsigned int cu_type : 16; /* from SenseID */ + unsigned int cu_model : 8; /* from SenseID */ + unsigned int dev_type : 16; /* from SenseID */ + unsigned int dev_model : 8; /* from SenseID */ + unsigned int open_count; + unsigned int req_queue_len; + unsigned int chanq_len; /* length of chanq */ + char type[4]; /* from discipline.name */ + unsigned int status; /* current device level */ + unsigned int label_block; /* where to find the VOLSER */ + unsigned int FBA_layout; /* fixed block size (like AIXVOL) */ + unsigned int characteristics_size; + unsigned int confdata_size; + char characteristics[64]; /* from read_device_characteristics */ + char configuration_data[256]; /* from read_configuration_data */ +}; + + +// Data structure at beginning of zipl's stage 3 boot loader +struct zipl_stage3_params { + uint64_t parm_addr; + uint64_t initrd_addr; + uint64_t initrd_len; + uint64_t load_psw; +}; +#define PSW_ADDRESS_MASK 0x000000007fffffffLL +#define PSW_DISABLED_WAIT 0x000a000000000000LL +#define KERNEL_HEADER_SIZE 65536 + +// IPL type in component table +typedef enum { + component_header_ipl = 0x00, + component_header_dump = 0x01 +} component_header_type; + + +/** + * Open file \p echo, write \p data and sleep \p msleep milliseconds. + * Next open file \p test for reading and return received data converted + * to integer. + * + * \param[in] echo File path to open for writing + * \param[in] data Data to be writen to echo file + * \param[in] msleep Sleep time in milliseconds + * \param[in] test File path for testing + * \return Received data from \p test converted to integer or -1 if an + * file I/O error occurred + */ + +int +echo_and_test(const char *echo, const char *data, unsigned long msleep, + const char *test) +{ + char buffer[32]; + int fd, len, ret; + struct timespec req, rem; + + // echo data + if (echo && data && strlen(echo) && strlen(data)) { + fd = open(echo, O_WRONLY); + if (fd == -1) { + return -1; + } + if (write(fd, data, strlen(data)) != strlen(data)) { + close(fd); + return -1; + } + close(fd); + } + + // sleep + if (usleep > 0) { + rem.tv_sec = msleep / 1000; + rem.tv_nsec = (msleep % 1000) * 1000000; + do { + req = rem; + } while (nanosleep(&req, &rem) == -1 && errno == EINTR); + } + + // test + if (test && strlen(test)) { + fd = open(test, O_RDONLY); + if (fd == -1) + return -1; + len = cfg_read(fd, buffer, sizeof(buffer)-1); + close(fd); + if (len == -1) + return -1; + buffer[len] = '\0'; + if (sscanf(buffer, "%i", &ret) != 1) + return -1; + else + return ret; + } + + return 0; +} + + +/** + * Read file contents into provided buffer. A trailing \p '\n' character + * is removed and read data is terminated by \p '\0'. + * + * \param[in] path Path to file to be read + * \param[in] buffer Pointer to buffer to store file data + * \param[in] buffer_len Size of buffer in bytes + * \return On success number of bytes read. On error -1. + */ + +ssize_t +read_file(const char *path, char *buffer, size_t buffer_len) +{ + int fd; + ssize_t read_len; + + fd = open(path, O_RDONLY); + if (fd == -1) + return -1; + read_len = cfg_read(fd, buffer, buffer_len - 1); + if (read_len == -1) { + close(fd); + return -1; + } + close(fd); + if (read_len && buffer[read_len-1] == '\n') + read_len--; + buffer[read_len] = '\0'; + + return read_len; +} + + +/** + * Return block count element in \p blockptr structure. + * + * \param[in] disk Pointer to disk structure for identifing \p blockptr + * format + * \param[in] ptr Block pointer to be used + * \return block count element + */ + +static int +get_blockptr_blockct(struct disk *disk, disk_blockptr_t *ptr) +{ + int blockct; + + switch (disk->type) { + case disk_type_scsi: + case disk_type_fba: + blockct = ptr->linear.blockct; + break; + + case disk_type_eckd_classic: + case disk_type_eckd_compatible: + blockct = ptr->chs.blockct; + break; + + case disk_type_diag: + case disk_type_unknown: + blockct = 0; + break; + } + + return blockct; +} + + +/** + * Calculate size of \p struct \p blockptr structure depended on disk type. + * + * \param[in] disk Reference to disk type. + * \return size of \p struct \p blockptr structure in bytes. + */ + +static int +get_blockptr_size(struct disk *disk) +{ + switch (disk->type) { + case disk_type_scsi: + return sizeof(struct scsi_blockptr); + + case disk_type_fba: + return sizeof(struct fba_blockptr); + + case disk_type_eckd_classic: + case disk_type_eckd_compatible: + return sizeof(struct eckd_blockptr); + + case disk_type_diag: + case disk_type_unknown: + break; + } + + return 0; +} + + +/** + * Test if disk block pointer is \p NULL. + * + * \param[in] disk Pointer to disk structure for identifing \p blockptr + * format + * \param[in] blockptr Block pointer to be tested + * \return -1 if \p blockptr is \p NULL, 0 if \p blockptr is not \p NULL + */ + +static int +blockptr_is_null(struct disk *disk, disk_blockptr_t *blockptr) +{ + char *ptr = (char *) blockptr; + int n, size = get_blockptr_size(disk); + + for (n = 0; n < size; n++) { + if (*ptr != 0x0) + return 0; + ptr++; + } + + return -1; +} + + +/** + * Convert packed block pointer in \p buffer into regular representation. + * + * \param[in] disk Pointer to disk structure for identifing pointer + * format + * \param[in] buffer Pointer to data buffer with packed block pointer + * \param[out] ptr Pointer to destination for converted block pointer + */ + +void +read_packed_blockptr(struct disk *disk, disk_blockptr_t *ptr, void *buffer) +{ + struct scsi_blockptr *scsi; + struct eckd_blockptr *eckd; + struct fba_blockptr *fba; + + memset(ptr, 0x0, sizeof (*ptr)); + switch (disk->type) { + case disk_type_scsi: + scsi = (struct scsi_blockptr *) buffer; + ptr->linear.block = scsi->blockno; + ptr->linear.size = scsi->size; + ptr->linear.blockct = scsi->blockct; + break; + + case disk_type_eckd_classic: + case disk_type_eckd_compatible: + eckd = (struct eckd_blockptr *) buffer; + ptr->chs.cyl = eckd->cyl; + ptr->chs.head = eckd->head; + ptr->chs.sec = eckd->sec; + ptr->chs.size = eckd->size; + ptr->chs.blockct = eckd->blockct; + break; + + case disk_type_fba: + fba = (struct fba_blockptr *) buffer; + ptr->linear.block = fba->blockno; + ptr->linear.size = fba->size; + ptr->linear.blockct = fba->blockct; + break; + + case disk_type_diag: + case disk_type_unknown: + break; + } +} + + +/** + * Print disk_blockptr_t structure. + * + * \param[in] disk Pointer to disk structure for identifing pointer format + * \param[in] ptr Pointer to block pointer to be printed + */ + +void print_blockptr(struct disk *disk, disk_blockptr_t *blockptr) +{ + switch (disk->type) { + case disk_type_scsi: + case disk_type_fba: + printf("block=%i, size=%i, blockct=%i\n", + (int) blockptr->linear.block, + blockptr->linear.size, + blockptr->linear.blockct); + break; + + case disk_type_eckd_classic: + case disk_type_eckd_compatible: + printf("cyl=%i, head=%i, sec=%i, size=%i, blockct=%i\n", + blockptr->chs.cyl, + blockptr->chs.head, + blockptr->chs.sec, + blockptr->chs.size, + blockptr->chs.blockct); + break; + + case disk_type_diag: + case disk_type_unknown: + break; + } +} + + +/** + * Check if block pointer is valid. + * + * \param[in] disk Pointer to initialized disk structure + * \param[in] blockptr Block pointer to be checked + * \return \p 0 in case block pointer is valid, \p -1 in case block + * pointer is not valid + */ + +static int +check_blockptr(struct disk *disk, disk_blockptr_t *blockptr) +{ + switch (disk->type) { + case disk_type_scsi: + case disk_type_fba: + if (blockptr->linear.blockct < 0 || + blockptr->linear.size != disk->phy_block_size || + blockptr->linear.block >= disk->phy_blocks) + return -1; + break; + + case disk_type_eckd_classic: + case disk_type_eckd_compatible: + if (blockptr->chs.blockct < 0 || + blockptr->chs.size != disk->phy_block_size || + blockptr->chs.cyl < 0 || + blockptr->chs.cyl >= disk->geo.cylinders || + blockptr->chs.head < 0 || + blockptr->chs.head >= disk->geo.heads || + blockptr->chs.sec < 1 || + blockptr->chs.sec > disk->geo.sectors) + return -1; + break; + + case disk_type_diag: + case disk_type_unknown: + break; + } + + return 0; +} + + +/** + * Open specified block device and read vital disk information. On + * success NULL is returned. On error a dynamically allocated error message + * is returned. + * + * \param[in] disk Pointer to disk structure + * \param[in] path Path to DASD block device + * \return In case of error dynamically allocated error message. + */ + +char * +disk_open(struct disk *disk, const char *path) +{ + char *errmsg = NULL; + long devsize; + struct dasd_information dasd_info; + + memset(disk, 0x0, sizeof (*disk)); + + disk->fd = open(path, O_RDONLY); + if (disk->fd == -1) { + cfg_strprintf(&errmsg, "Error opening disk - %s", + strerror(errno)); + return errmsg; + } + + // get geometry information + if (ioctl(disk->fd, HDIO_GETGEO, &(disk->geo))) { + cfg_strprintf(&errmsg, "Error getting disk geometry - %s", + strerror(errno)); + close(disk->fd); + return errmsg; + } + + // determine disk type + if (!ioctl(disk->fd, BIODASDINFO, &dasd_info)) { + if (strncmp(dasd_info.type, "FBA ", 4) == 0) + disk->type = disk_type_fba; + else if (strncmp(dasd_info.type, "DIAG", 4) == 0) + disk->type = disk_type_diag; + else if (strncmp(dasd_info.type, "ECKD", 4) == 0) { + if (dasd_info.FBA_layout) + disk->type = disk_type_eckd_classic; + else + disk->type = disk_type_eckd_compatible; + } + else + disk->type = disk_type_unknown; + } else + disk->type = disk_type_scsi; + + // get physical block size + if (ioctl(disk->fd, BLKSSZGET, &disk->phy_block_size)) { + cfg_strprintf(&errmsg, "Error getting blocksize - %s", + strerror(errno)); + close(disk->fd); + return errmsg; + } + + // get size of device in sectors (512 byte) + if (ioctl(disk->fd, BLKGETSIZE, &devsize)) { + cfg_strprintf(&errmsg, "Error getting device size - %s", + strerror(errno)); + close(disk->fd); + return errmsg; + } + + // convert device size to size in physical blocks + disk->phy_blocks = devsize / (disk->phy_block_size / 512); + + // read boot record + switch (disk->type) { + case disk_type_scsi: + case disk_type_fba: + errmsg = disk_read_scsi_mbr(disk); + break; + + case disk_type_eckd_classic: + case disk_type_eckd_compatible: + errmsg = disk_read_dasd_boot_record(disk); + break; + + case disk_type_diag: + case disk_type_unknown: + cfg_strcpy(&errmsg, "Unsupported disk type."); + break; + } + if (errmsg) { + close(disk->fd); + return errmsg; + } + + // check program table pointer + if (check_blockptr(disk, &disk->program_table_ptr)) { + cfg_strcpy(&errmsg, + "Error - invalid program table pointer in boot record"); + close(disk->fd); + return errmsg; + } + if (get_blockptr_blockct(disk, &disk->program_table_ptr) !=0) { + cfg_strcpy(&errmsg, + "Error - invalid program table pointer in boot record"); + close(disk->fd); + return errmsg; + } + + return NULL; +} + + +/** + * Close specified disk. + * + * \param[in] disk Pointer to disk structure + * \param[in] path Path to DASD block device + * \return In case of error dynamically allocated error message. + */ + +void +disk_close(struct disk *disk) +{ + close(disk->fd); +} + + +/** + * Read physical blocks addressed by \p blockptr from disk into + * memory. \p blockptr format must match disk type. On success NULL is + * returned. On error a dynamically allocated error message is returned. + * + * \param[in] disk Pointer to initialized disk structure + * \param[out] buffer Pointer to allocated memory buffer where blocks + * will be stored + * \param[in] blockptr Block address of 1st block to be read from disk + * \return In case of error dynamically allocated error message. + */ + +char * +disk_read_phy_blocks(struct disk *disk, void *buffer, + disk_blockptr_t *blockptr) +{ + char *errmsg = NULL; + off_t offset; + ssize_t read_len, read_total, read_cnt = 0; + + if (check_blockptr(disk, blockptr)) { + cfg_strcpy(&errmsg, "Error reading block from disk " + "- invalid block pointer"); + return errmsg; + } + + // convert block pointer to byte offset + switch (disk->type) { + case disk_type_scsi: + case disk_type_fba: + case disk_type_diag: + offset = blockptr->linear.block; + read_total = disk->phy_block_size * + (blockptr->linear.blockct+1); + break; + + case disk_type_eckd_classic: + case disk_type_eckd_compatible: + offset = blockptr->chs.sec + disk->geo.sectors * + (blockptr->chs.head + + blockptr->chs.cyl*disk->geo.heads) - 1; + read_total = disk->phy_block_size * (blockptr->chs.blockct+1); + break; + + default: + cfg_strcpy(&errmsg, + "Invalid disk type for physical block read"); + return errmsg; + } + + offset *= disk->phy_block_size; + if (lseek(disk->fd, offset, SEEK_SET) == (off_t)-1) { + cfg_strprintf(&errmsg, + "Error seeking physical block - %s", + strerror(errno)); + return errmsg; + } + while (read_cnt < read_total) { + read_len = read(disk->fd, buffer+read_cnt, + read_total - read_cnt); + if (read_len == -1) { + cfg_strprintf(&errmsg, + "Error reading data from disk - %s", + strerror(errno)); + return errmsg; + } + read_cnt += read_len; + } + + return NULL; +} + + +/** + * Calculate maximum number of program entries in program table based + * on disk type. + * + * \param[in] disk Pointer to initialized disk structure + * \return maximum number of program entries in program table + */ + +int +get_program_table_size(struct disk *disk) +{ + return 512 / get_blockptr_size(disk) - 1; +} + + +/** + * Read program table from disk. On success NULL is returned. On error a + * dynamically allocated error message is returned. + * + * \param[in] disk Pointer to initialized disk structure + * \param[out] program_table Pointer to dynamically allocated memory buffer + * with program table + * \return In case of error dynamically allocated error message. + */ + +static char * +get_program_table(struct disk *disk, disk_blockptr_t **program_table) +{ + char *errmsg = NULL, *buffer; + int table_size, n; + + // verify program table pointer + if (get_blockptr_blockct(disk, &disk->program_table_ptr) != 0) { + cfg_strcpy(&errmsg, + "Error - invalid program table pointer"); + return errmsg; + } + + // read physical program table from disk + buffer = malloc(disk->phy_block_size); + MEM_ASSERT(buffer); + errmsg = disk_read_phy_blocks(disk, buffer, &disk->program_table_ptr); + if (errmsg) { + free(buffer); + return errmsg; + } + + // verify program table magic number + if (strncmp(buffer, MAGIC, MAGIC_LENGTH) != 0) { + cfg_strcpy(&errmsg, + "Error - invalid magic number in program table"); + free(buffer); + return errmsg; + } + + // convert program table entries to regular format + table_size = get_program_table_size(disk); + *program_table = malloc(sizeof (disk_blockptr_t) * table_size); + MEM_ASSERT(*program_table); + for (n = 0; n < table_size; n++) { + read_packed_blockptr(disk, &((*program_table)[n]), + buffer + (n+1) * get_blockptr_size(disk)); + } + free(buffer); + + return NULL; +} + + +/** + * Calculate maximum number of component entries in component table based + * on disk type. + * + * \param[in] disk Pointer to initialized disk structure + * \return maximum number of component entries in component table + */ + +static int +get_component_table_size(struct disk *disk) +{ + return disk->phy_block_size / get_blockptr_size(disk) - 1; +} + + +/** + * Read component table from disk. On success NULL is returned. On error a + * dynamically allocated error message is returned. + * + * \param[in] disk Pointer to initialized disk structure + * \param[out] component_table Pointer to dynamically allocated memory buffer + * with component table + * \param[out] opt \p opt field from component table + * \param[in] table_ptr Block pointer to requested component table + * \return In case of error dynamically allocated error message. + */ + +static char * +get_component_table(struct disk *disk, struct component **component_table, + int *opt, disk_blockptr_t *table_ptr) +{ + char *errmsg = NULL; + int table_size, n; + struct component_entry *buffer; + + // verify block pointer to component table + if (get_blockptr_blockct(disk, table_ptr) != 0) { + cfg_strcpy(&errmsg, + "Error - invalid component table pointer"); + return errmsg; + } + + // read component table from disk + buffer = malloc(disk->phy_block_size); + MEM_ASSERT(buffer); + errmsg = disk_read_phy_blocks(disk, buffer, table_ptr); + if (errmsg) { + free(buffer); + return errmsg; + } + + // verify component table magic number + if (strncmp((char *) buffer, MAGIC, MAGIC_LENGTH) != 0) { + cfg_strcpy(&errmsg, + "Error - invalid magic number in component table"); + free(buffer); + return errmsg; + } + *opt = ((uint8_t *) buffer)[4]; + + // convert program table entries to regular format + table_size = get_component_table_size(disk); + *component_table = malloc(sizeof (disk_blockptr_t) * table_size); + MEM_ASSERT(*component_table); + for (n = 0; n < table_size; n++) { + read_packed_blockptr(disk, + &((*component_table)[n].segment_ptr), &buffer[n+1]); + (*component_table)[n].type = buffer[n+1].type; + (*component_table)[n].address.load_address = + buffer[n+1].address.load_address; + } + free(buffer); + + return NULL; +} + + +/** + * Copy component from disk into memory. On success NULL is returned. On error + * a dynamically allocated error message is returned. + * + * \param[in] disk Pointer to initialized disk structure + * \param[in] component Pointer to component to be loaded + * \param[out] buffer Dynamically allocated memory buffer with component + * \return In case of error dynamically allocated error message. + * + */ + +static char * +copy_component_to_memory(struct disk *disk, void **buffer, + int *buffer_size, disk_blockptr_t *component_ptr) +{ + char *errmsg, *segment_table; + int pointer_in_segment_table, load_position = 0, entry = 0; + disk_blockptr_t code_ptr; + + // read 1st segment table for component + segment_table = malloc(disk->phy_block_size* + (get_blockptr_blockct(disk, component_ptr)+1)); + MEM_ASSERT(segment_table); + errmsg = disk_read_phy_blocks(disk, segment_table, component_ptr); + if (errmsg) { + free(segment_table); + return errmsg; + } + pointer_in_segment_table = disk->phy_block_size * + (get_blockptr_blockct(disk, component_ptr)+1) / + get_blockptr_size(disk); + read_packed_blockptr(disk, &code_ptr, segment_table); + + // walk through all segment tables and load code segments + *buffer = NULL; + *buffer_size = 0; + do { + // load code segment + *buffer_size += disk->phy_block_size * + (get_blockptr_blockct(disk, &code_ptr)+1); + *buffer = realloc(*buffer, *buffer_size); + MEM_ASSERT(*buffer); + errmsg = disk_read_phy_blocks(disk, (*buffer)+load_position, + &code_ptr); + if (errmsg) { + free(segment_table); + free(*buffer); + return errmsg; + } + load_position += disk->phy_block_size * + (get_blockptr_blockct(disk, &code_ptr)+1); + entry++; + read_packed_blockptr(disk, &code_ptr, + segment_table + entry * get_blockptr_size(disk)); + + // read new segment table if end of current segment + // table is reached + if (entry == pointer_in_segment_table-1 && + !blockptr_is_null(disk, &code_ptr)) { + segment_table = realloc(segment_table, + disk->phy_block_size * + (get_blockptr_blockct(disk, &code_ptr)+1)); + MEM_ASSERT(segment_table); + errmsg = disk_read_phy_blocks(disk, segment_table, + &code_ptr); + if (errmsg) { + free(segment_table); + free(*buffer); + return errmsg; + } + pointer_in_segment_table = disk->phy_block_size * + (get_blockptr_blockct(disk, &code_ptr)+1) / + get_blockptr_size(disk); + read_packed_blockptr(disk, &code_ptr, segment_table); + entry = 0; + } + } while (!blockptr_is_null(disk, &code_ptr)); + + free(segment_table); + + return NULL; +} + + +/** + * Identify boot objects (kernel image, initrd, parmfile) by looking at + * zipl's stage 3 parameter structure. On success NULL is returned. On + * error a dynamically allocated error message is returned. + * + * \param[in] disk Pointer to initialized disk structure + * \param[in] component_table Component table to be booted + * \param[out] kernel Block pointer to kernel component + * \param[out] initrd Block pointer to initrd component + * \param[out] initrd_len Length of initrd + * \param[out] parmfile Block pointer to parmfile component + * \return In case of error dynamically allocated error message. + */ + +static char * +identify_boot_objects(struct disk *disk, struct component *component_table, + disk_blockptr_t *kernel, disk_blockptr_t *initrd, + int *initrd_len, disk_blockptr_t *parmfile) +{ + void *buffer; + char *errmsg = NULL; + int comp_num, buffer_size, n, table_size = + get_component_table_size(disk); + uint64_t kernel_addr; + struct zipl_stage3_params stage3; + + memset(kernel, 0x0, sizeof (disk_blockptr_t)); + memset(initrd, 0x0, sizeof (disk_blockptr_t)); + *initrd_len = 0; + memset(parmfile, 0x0, sizeof (disk_blockptr_t)); + + // find stage 3 boot loader component + for (comp_num = 0; comp_num < table_size && + component_table[comp_num].type != component_execute; + comp_num++); + if (comp_num == 0 || comp_num == table_size) { + cfg_strcpy(&errmsg, + "Error - invalid component table found"); + return errmsg; + } + if (component_table[comp_num].address.load_psw == PSW_DISABLED_WAIT) { + cfg_strcpy(&errmsg, + "Error - data segment load is not supported"); + return errmsg; + } + errmsg = copy_component_to_memory(disk, &buffer, &buffer_size, + &component_table[comp_num-1].segment_ptr); + if (errmsg) + return errmsg; + stage3 = *((struct zipl_stage3_params *) buffer); + free(buffer); + + // lookup kernel component + kernel_addr = stage3.load_psw & PSW_ADDRESS_MASK; + for (n = 0; n < comp_num && + component_table[n].address.load_address != kernel_addr; + n++); + if (n == comp_num) { + cfg_strcpy(&errmsg, + "Error - invalid component table found"); + return errmsg; + } + *kernel = component_table[n].segment_ptr; + + // lookup initrd component + for (n = 0; n < comp_num && component_table[n].address.load_address + != stage3.initrd_addr; n++); + if (n != comp_num) { + *initrd = component_table[n].segment_ptr; + *initrd_len = stage3.initrd_len; + } + + // lookup parmfile component + for (n = 0; n < comp_num && component_table[n].address.load_address + != stage3.parm_addr; n++); + if (n != comp_num) + *parmfile = component_table[n].segment_ptr; + + return NULL; +} + +/** + * Copy kernel component to local filesystem. On success NULL is returned. On + * error a dynamically allocated error message is returned. + * + * \param[in] disk Pointer to initialized disk structure + * \param[in] kernel Block pointer to kernel component + * \return In case of error dynamically allocated error message. + */ + +static char * +read_kernel_component(struct disk *disk, disk_blockptr_t *kernel) +{ + void *buffer; + char *errmsg = NULL; + int fd, n, buffer_size, zero = 0x0; + + // open local kernel file and write kernel header because kernel header + // was removed by zipl + fd = creat(KBOOT_FILENAME_KERNEL, S_IRUSR | S_IWUSR); + if (fd == -1) { + cfg_strprintf(&errmsg, "Error writing kernel file - %s", + strerror(errno)); + return errmsg; + } + for (n = 0; n < KERNEL_HEADER_SIZE / sizeof(zero); n++) { + if (write(fd, &zero, sizeof zero) != sizeof(zero)) { + cfg_strprintf(&errmsg, + "Error writing kernel file - %s", strerror(errno)); + close(fd); + unlink(KBOOT_FILENAME_KERNEL); + return errmsg; + } + } + + // write kernel image + errmsg = copy_component_to_memory(disk, &buffer, &buffer_size, kernel); + if (errmsg) { + close(fd); + return errmsg; + } + if (write(fd, buffer, buffer_size) != buffer_size) { + cfg_strprintf(&errmsg, "Error writing kernel file - %s", + strerror(errno)); + free(buffer); + close(fd); + unlink(KBOOT_FILENAME_KERNEL); + return errmsg; + } + free(buffer); + if (close(fd)) { + cfg_strprintf(&errmsg, "Error writing kernel file - %s", + strerror(errno)); + unlink(KBOOT_FILENAME_KERNEL); + return errmsg; + } + + return NULL; +} + + +/** + * Copy initrd component to local filesystem. On success NULL is returned. On + * error a dynamically allocated error message is returned. + * + * \param[in] disk Pointer to initialized disk structure + * \param[in] kernel Block pointer to initrd component + * \param[in] initrd length found in stage 3 parameter structure + * \return In case of error dynamically allocated error message. + */ + +static char * +read_initrd_component(struct disk *disk, disk_blockptr_t *initrd, + int initrd_len) +{ + void *buffer; + char *errmsg = NULL; + int fd, buffer_size; + + // open local initrd file + fd = creat(KBOOT_FILENAME_INITRD, S_IRUSR | S_IWUSR); + if (fd == -1) { + cfg_strprintf(&errmsg, "Error writing initrd file - %s", + strerror(errno)); + return errmsg; + } + + // write initrd image + errmsg = copy_component_to_memory(disk, &buffer, &buffer_size, initrd); + if (errmsg) { + close(fd); + unlink(KBOOT_FILENAME_INITRD); + return errmsg; + } + if (buffer_size < initrd_len) { + cfg_strcpy(&errmsg, "Error writing initrd file " + "- invalid initrd component length"); + free(buffer); + close(fd); + unlink(KBOOT_FILENAME_INITRD); + return errmsg; + } + if (write(fd, buffer, initrd_len) != initrd_len) { + cfg_strprintf(&errmsg, "Error writing initrd file - %s", + strerror(errno)); + free(buffer); + close(fd); + unlink(KBOOT_FILENAME_INITRD); + return errmsg; + } + free(buffer); + if (close(fd)) { + cfg_strprintf(&errmsg, "Error writing kernel file - %s", + strerror(errno)); + unlink(KBOOT_FILENAME_INITRD); + return errmsg; + } + + return NULL; +} + + +/** + * Read parmfile component. On success NULL is returned. On error a + * dynamically allocated error message is returned. + * + * \param[in] disk Pointer to initialized disk structure + * \param[in] parmfile Block pointer to parmfile component + * \param[out] cmdline Dynamically allocated string with kernel command line + * \return In case of error dynamically allocated error message. + */ + +static char * +read_parmfile_component(struct disk *disk, disk_blockptr_t *parmfile, + char **cmdline) +{ + char *errmsg = NULL, *buffer; + int buffer_size; + + errmsg = copy_component_to_memory(disk, (void **) &buffer, + &buffer_size, + parmfile); + if (errmsg) + return errmsg; + buffer[buffer_size-1] = '\0'; + cfg_strinitcpy(cmdline, buffer); + + return NULL; +} + + +/** + * Prepare command line passed via config file's parmfile and cmdline + * commands. On success NULL is returned. On error a dynamically allocated + * error message is returned. + * + * \param[in] boot Pointer to cfg_bentry structure with boot information. + * \param[out] cmdline Dynamically allocated string with assembled command line + * \return In case of error dynamically allocated error message. + * + */ + +static char * +prepare_config_command_line(struct cfg_bentry *boot, char **cmdline) +{ + char *errmsg = NULL, *tmp_uri, *tmp_msg; + + if (strlen(boot->parmfile)) { + tmp_uri = prefix_root(boot->root, boot->parmfile); + if (comp_load(KBOOT_FILENAME_PARMFILE, tmp_uri, NULL, + &tmp_msg)) { + cfg_strprintf(&errmsg, "Error loading parmfile - %s", + tmp_msg); + cfg_strfree(&tmp_msg); + cfg_strfree(&tmp_uri); + return errmsg; + } + cfg_strfree(&tmp_uri); + errmsg = compose_commandline(cmdline, KBOOT_FILENAME_PARMFILE, + boot->cmdline); + if (errmsg) + return errmsg; + } else + cfg_strinitcpy(cmdline, boot->cmdline); + + return NULL; +} + + +/** + * Load boot objects from disk and boot new system by calling \p kexec() + * On success NULL is returned. On error a dynamically allocated error + * message is returned. + * + * \param[in] boot Pointer to cfg_bentry structure with boot information. + * \param[in] disk Pointer to initialized disk structure + * \param[in] program Program number in program table to be booted + * \return In case of error dynamically allocated error message. + */ + +char * +boot_bootmap_disk(struct cfg_bentry *boot, struct disk *disk, int program) +{ + char *errmsg = NULL, *cmdline, *extra_cmdline; + int opt, initrd_len; + disk_blockptr_t *program_table, kernel, initrd, parmfile; + struct component *component_table; + + // check program entry + if (program < 0 || program >= get_program_table_size(disk)) { + cfg_strcpy(&errmsg, + "Error - program number out of valid range."); + return errmsg; + } + + // load component table for requested program + errmsg = get_program_table(disk, &program_table); + if (errmsg) + return errmsg; + if (blockptr_is_null(disk, &program_table[program])) { + cfg_strprintf(&errmsg, + "Error - program entry %i is empty.", program); + return errmsg; + } + errmsg = get_component_table(disk, &component_table, &opt, + &program_table[program]); + free(program_table); + if (errmsg) + return errmsg; + if (opt == component_header_dump) { + cfg_strcpy(&errmsg, "Error - load-with-dump-list-directed" + " IPL is not supported."); + return errmsg; + } + if (opt != component_header_ipl) { + cfg_strcpy(&errmsg, "Error - invalid list directed IPL" + " type."); + return errmsg; + } + + // load boot objects + identify_boot_objects(disk, component_table, &kernel, &initrd, + &initrd_len, &parmfile); + free(component_table); + errmsg = read_kernel_component(disk, &kernel); + if (errmsg) + return errmsg; + if (!blockptr_is_null(disk, &initrd)) { + errmsg = read_initrd_component(disk, &initrd, initrd_len); + if (errmsg) + return errmsg; + } + if (!blockptr_is_null(disk, &parmfile)) { + errmsg = read_parmfile_component(disk, &parmfile, &cmdline); + if (errmsg) + return errmsg; + } else + cfg_strinit(&cmdline); + errmsg = prepare_config_command_line(boot, &extra_cmdline); + if (errmsg) { + free(cmdline); + return errmsg; + } + cfg_strcat(&cmdline, " "); + cfg_strcat(&cmdline, extra_cmdline); + cfg_strfree(&extra_cmdline); + + // call kexec + if (!blockptr_is_null(disk, &initrd)) + errmsg = kexec(KBOOT_FILENAME_KERNEL, KBOOT_FILENAME_INITRD, + cmdline); + else + errmsg = kexec(KBOOT_FILENAME_KERNEL, "", cmdline); + cfg_strfree(&cmdline); + + // if we are still here, something went wrong + return errmsg; +} diff -Nuar kboot-10/ui/bootmap_dasd.c kboot-10_patch4/ui/bootmap_dasd.c --- kboot-10/ui/bootmap_dasd.c 1970-01-01 01:00:00.000000000 +0100 +++ kboot-10_patch4/ui/bootmap_dasd.c 2006-02-10 16:09:35.000000000 +0100 @@ -0,0 +1,225 @@ +/*! + * (C) Copyright IBM Corp. 2005, 2005 + * + * \file bootmap.c + * \brief DASD specific functions to load bootmap data + * + * $Id: bootmap_dasd.c,v 1.5 2006/02/10 15:09:35 loehr Exp $ + * + * Author(s): Ralph Wuerthner (rwuerthn@de.ibm.com) + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "loader.h" +#include "bootmap.h" + + +// definitions to extract URI fields +#define URI_DASD_BOOTMAP_RE "^dasd://\\(([[:xdigit:]])\\.([[:xdigit:]])" \ + "\\.([[:xdigit:]]{4})(,([[:digit:]]{1,2}))?\\)$" +#define URI_DASD_BOOTMAP_BUSID 1 +#define URI_DASD_BOOTMAP_PROG 5 +#define URI_DASD_BOOTMAP_MAX 7 + +#define SYS_PATH_DASD "/sys/bus/ccw/devices" + + +/** + * Set DASD Channel device online and return device node. On success NULL + * is returned. On error a dynamically allocated error message is returned. + * + * \param[in] busid Bus ID of channel device to be set online + * \param[out] dev Dynamically allocated string with path to block device + * \return In case of error dynamically allocated error message. + */ + +static char * +set_dasd_online(const char *busid, char **dev) +{ + char *errmsg = NULL, *syspath = NULL, *echo = NULL; + char *block = NULL, nodestr[16]; + int fd, major = 0, minor = 0; + + // check if channel device exists + cfg_strprintf(&syspath, "%s/%s", SYS_PATH_DASD, busid); + if (access(syspath, F_OK)) { + cfg_strprintf(&errmsg, "Error setting DASD '%s' online - " + "no such channel device", busid); + cfg_strfree(&syspath); + return errmsg; + } + + // set channel device online + cfg_strprintf(&echo, "%s/online", syspath); + if (echo_and_test(echo, "1", 100, echo) != 1) { + cfg_strprintf(&errmsg, "Error setting DASD '%s' online", + busid); + cfg_strfree(&syspath); + cfg_strfree(&echo); + return errmsg; + } + cfg_strfree(&echo); + + // create device node + cfg_strprintf(&block, "%s/block/dev", syspath); + cfg_strfree(&syspath); + fd = open(block, O_RDONLY); + if (fd == -1) { + cfg_strprintf(&errmsg, "Error getting device node information" + " for DASD '%s' - %s", busid, strerror(errno)); + cfg_strfree(&block); + return errmsg; + } + memset(nodestr, 0x0, sizeof(nodestr)); + cfg_read(fd, nodestr, sizeof(nodestr)-1); + close(fd); + cfg_strfree(&block); + if (sscanf(nodestr, "%i:%i", &major, &minor) != 2) { + cfg_strprintf(&errmsg, + "Error getting device node information for DASD '%s'", + busid); + return errmsg; + } + cfg_strinit(dev); + cfg_strprintf(dev, "%s/b%s", BLOCKDEV_PATH, busid); + unlink(*dev); + if (mknod(*dev, 0660 | S_IFBLK, makedev(major, minor))) { + cfg_strprintf(&errmsg, + "Error creating device node for DASD '%s' - %s", + busid, strerror(errno)); + cfg_strfree(dev); + return errmsg; + } + + return NULL; +} + + +/** + * Set DASD Channel device offline and delete device node. + * + * \param[in] busid Bus ID of channel device to be set offline + */ + +static void +set_dasd_offline(const char *busid) +{ + char *echo = NULL, *dev = NULL; + + // set channel device offline + cfg_strprintf(&echo, "%s/%s/online", SYS_PATH_DASD, busid); + echo_and_test(echo, "0", 0, NULL); + cfg_strfree(&echo); + + // delete device node + cfg_strprintf(&dev, "%s/b%s", BLOCKDEV_PATH, busid); + unlink(dev); + cfg_strfree(&dev); + +} + + +/** + * Read DASD boot record and update program table pointer. On success NULL is + * returned. On error a dynamically allocated error message is returned. + * + * \param[in] disk Pointer to partially initialized disk structure + * \return In case of error dynamically allocated error message. + * + */ + +char * +disk_read_dasd_boot_record(struct disk *disk) +{ + char *bootrecord; + char *errmsg = NULL; + disk_blockptr_t blockptr; + + bootrecord = malloc(disk->phy_block_size); + MEM_ASSERT(bootrecord); + memset(&blockptr, 0x0, sizeof(blockptr)); + blockptr.chs.cyl = 0; + blockptr.chs.head = 0; + blockptr.chs.sec = 2; + blockptr.chs.size = disk->phy_block_size; + blockptr.chs.blockct = 0; + errmsg = disk_read_phy_blocks(disk, bootrecord, &blockptr); + if (!errmsg) + read_packed_blockptr(disk, &disk->program_table_ptr, + bootrecord+4); + free(bootrecord); + + return errmsg; +} + + +/** + * Handle bootmap boot from DASD devices. On error a dynamically allocated + * error message is returned. + * + * \param[in] boot Pointer to cfg_bentry structure with boot information. + * \return In case of error dynamically allocated error message. + */ + +char * +action_bootmap_boot_dasd(struct cfg_bentry *boot) +{ + char *errmsg = NULL, tmp_buffer[64], busid[16], *dev; + int ret, n, program = 0; + regex_t preg; + regmatch_t pmatch[URI_DASD_BOOTMAP_MAX]; + struct disk disk; + + // compile regular expression + if ((ret = regcomp(&preg, URI_DASD_BOOTMAP_RE, REG_EXTENDED))) { + regerror(ret, &preg, tmp_buffer, sizeof(tmp_buffer)); + cfg_strprintf(&errmsg, "Internal error: unable to compile " + "regular expression - %s", tmp_buffer); + return errmsg; + } + + // match URI & extract fields + ret = regexec(&preg, boot->bootmap, URI_DASD_BOOTMAP_MAX, pmatch, 0); + if (ret) { + regfree(&preg); + cfg_strcpy(&errmsg, + "Error - invalid 'dasd' boot map URI."); + return errmsg; + } + strncpy(busid, boot->bootmap + pmatch[URI_DASD_BOOTMAP_BUSID].rm_so, + 8); + busid[8] = '\0'; + for (n = 0; n < strlen(busid); n++) + busid[n] = tolower(busid[n]); + if (pmatch[URI_DASD_BOOTMAP_PROG].rm_so != -1) + sscanf(boot->bootmap+pmatch[URI_DASD_BOOTMAP_PROG].rm_so, + "%i", &program); + regfree(&preg); + + // set DASD online and boot + errmsg = set_dasd_online(busid, &dev); + if (errmsg) + return errmsg; + errmsg = disk_open(&disk, dev); + cfg_strfree(&dev); + if (errmsg) + return errmsg; + errmsg = boot_bootmap_disk(boot, &disk, program); + + // if we are still here boot failed + disk_close(&disk); + set_dasd_offline(busid); + + return errmsg; +} diff -Nuar kboot-10/ui/bootmap_fcp.c kboot-10_patch4/ui/bootmap_fcp.c --- kboot-10/ui/bootmap_fcp.c 1970-01-01 01:00:00.000000000 +0100 +++ kboot-10_patch4/ui/bootmap_fcp.c 2006-02-10 16:09:35.000000000 +0100 @@ -0,0 +1,413 @@ +/*! + * (C) Copyright IBM Corp. 2005, 2005 + * + * \file bootmap.c + * \brief FCP specific functions to load bootmap data + * + * $Id: bootmap_fcp.c,v 1.5 2006/02/10 15:09:35 loehr Exp $ + * + * Author(s): Ralph Wuerthner (rwuerthn@de.ibm.com) + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "loader.h" +#include "bootmap.h" + + +// definitions to extract URI fields +#define URI_ZFCP_BOOTMAP_RE "^zfcp://\\(([[:xdigit:]])\\.([[:xdigit:]])" \ + "\\.([[:xdigit:]]{4}),(0x[[:xdigit:]]{16}),(0x[[:xdigit:]]{16})" \ + "(,([[:digit:]]{1,2}))?\\)$" +#define URI_ZFCP_BOOTMAP_BUSID 1 +#define URI_ZFCP_BOOTMAP_WWPN 4 +#define URI_ZFCP_BOOTMAP_LUN 5 +#define URI_ZFCP_BOOTMAP_PROG 7 +#define URI_ZFCP_BOOTMAP_MAX 9 + +#define SYS_PATH_FCP "/sys/bus/ccw/drivers/zfcp" +#define SYS_PATH_SCSI "/sys/bus/scsi/devices" + + +/** + * Find FCP SCSI device identified by bus ID, WWPN and LUN and return host + * number, channel, SCSI ID and SCSI LUN. + * + * \param[in] busid Bus ID of the FCP channel through which the SCSI + * device is attached + * \param[in] wwpn WWPN through which the SCSI device is accessed + * \param[in] lun LUN of the SCSI device + * \param[out] host Host number of FCP SCSI device + * \param[out] channel Channel of FCP SCSI device + * \param[out] scsi_id SCSI ID of FCP SCSI device + * \param[out] scsi_lun SCSI LUN of FCP SCSI device + * \return If FCP SCSI device was found 0, otherwise -1. + * + */ + +static int +find_scsi_device(const char *busid, const char *wwpn, const char *lun, + int *host, int *channel, int *scsi_id, int *scsi_lun) +{ + char *path = NULL, tmpstr[20]; + int ret = -1; + DIR *dir; + struct dirent *dirent; + + dir = opendir(SYS_PATH_SCSI); + if (dir == NULL) + return -1; + while ((dirent = readdir(dir)) != NULL) { + if (strcmp(dirent->d_name, ".") == 0 || + strcmp(dirent->d_name, "..") == 0 ) + continue; + + cfg_strprintf(&path, "%s/%s/hba_id", SYS_PATH_SCSI, + dirent->d_name); + if (read_file(path, tmpstr, sizeof(tmpstr)) == -1 || + strcmp(busid, tmpstr) !=0 ) + continue; + + cfg_strprintf(&path, "%s/%s/wwpn", SYS_PATH_SCSI, + dirent->d_name); + if (read_file(path, tmpstr, sizeof(tmpstr)) == -1 || + strcmp(wwpn, tmpstr) !=0 ) + continue; + + cfg_strprintf(&path, "%s/%s/fcp_lun", SYS_PATH_SCSI, + dirent->d_name); + if (read_file(path, tmpstr, sizeof(tmpstr)) == -1 || + strcmp(lun, tmpstr) !=0 ) + continue; + + if (sscanf(dirent->d_name, "%i:%i:%i:%i", host, channel, + scsi_id, scsi_lun) == 4) { + ret = 0; + break; + } + } + cfg_strfree(&path); + closedir(dir); + + return ret; +} + + +/** + * Set FCP attached SCSI disk online and return device node. On success NULL + * is returned. On error a dynamically allocated error message is returned. + * + * \param[in] busid Bus ID of the FCP channel through which the SCSI disk + * is attached + * \param[in] wwpn WWPN through which the SCSI is accessed + * \param[in] lun LUN of the SCSI disk + * \param[out] dev Dynamically allocated string with path to block device + * \return In case of error dynamically allocated error message. + */ + +static char * +set_fcp_disk_online(const char *busid, const char *wwpn, const char *lun, + char **dev) +{ + char *errmsg = NULL, *syspath = NULL, *echo = NULL; + char *test = NULL, *block = NULL, nodestr[16]; + int fd, host, channel, scsi_id, scsi_lun, major = 0, minor = 0; + + // check if FCP channel exists + cfg_strprintf(&syspath, "%s/%s", SYS_PATH_FCP, busid); + if (access(syspath, F_OK)) { + cfg_strprintf(&errmsg, "Error setting FCP channel '%s' online" + " - no such channel device", busid); + cfg_strfree(&syspath); + return errmsg; + } + + // set FCP channel online + cfg_strprintf(&echo, "%s/online", syspath); + if (echo_and_test(echo, "1", 200, echo) != 1) { + cfg_strprintf(&errmsg, + "Error setting FCP channel '%s' online", busid); + cfg_strfree(&syspath); + cfg_strfree(&echo); + return errmsg; + } + + // configure WWPN + cfg_strprintf(&test, "%s/%s", syspath, wwpn); + if (access(test, F_OK)) { + cfg_strprintf(&echo, "%s/port_add", syspath); + cfg_strprintf(&test, "%s/%s/failed", syspath, wwpn); + if (echo_and_test(echo, wwpn, 200, test) != 0) { + cfg_strprintf(&errmsg, + "Error configuring WWPN '%s' on FCP channel '%s'", + wwpn, busid); + cfg_strfree(&syspath); + cfg_strfree(&echo); + cfg_strfree(&test); + return errmsg; + } + } + + // configure LUN + cfg_strprintf(&test, "%s/%s/%s", syspath, wwpn, lun); + if (access(test, F_OK)) { + cfg_strprintf(&echo, "%s/%s/unit_add", syspath, wwpn); + cfg_strprintf(&test, "%s/%s/%s/failed", syspath, wwpn, lun); + if (echo_and_test(echo, lun, 200, test) != 0) { + cfg_strprintf(&errmsg, + "Error configuring LUN '%s' on WWPN '%s' on FCP " + "channel '%s'", lun, wwpn, busid); + cfg_strfree(&syspath); + cfg_strfree(&echo); + cfg_strfree(&test); + return errmsg; + } + } + cfg_strfree(&syspath); + cfg_strfree(&echo); + cfg_strfree(&test); + + // create device node + if (find_scsi_device(busid, wwpn, lun, &host, &channel, + &scsi_id, &scsi_lun)) { + cfg_strprintf(&errmsg, "Error getting device node information" + " for FCP disk %s:%s:%s", busid, wwpn, lun); + return errmsg; + } + cfg_strprintf(&block, "%s/%i:%i:%i:%i/block/dev", SYS_PATH_SCSI, host, + channel, scsi_id, scsi_lun); + fd = open(block, O_RDONLY); + if (fd == -1) { + cfg_strprintf(&errmsg, "Error getting device node information" + " for FCP disk %s:%s:%s - %s", busid, wwpn, lun, + strerror(errno)); + cfg_strfree(&block); + return errmsg; + } + memset(nodestr, 0x0, sizeof(nodestr)); + cfg_read(fd, nodestr, sizeof(nodestr)-1); + close(fd); + cfg_strfree(&block); + if (sscanf(nodestr, "%i:%i", &major, &minor) != 2) { + cfg_strprintf(&errmsg, + "Error getting device node information for " + "FCP disk %s:%s:%s", busid, wwpn, lun); + return errmsg; + } + cfg_strinit(dev); + cfg_strprintf(dev, "%s/b%s:%s:%s", BLOCKDEV_PATH, busid, wwpn, lun); + unlink(*dev); + if (mknod(*dev, 0660 | S_IFBLK, makedev(major, minor))) { + cfg_strprintf(&errmsg, + "Error creating device node for FCP disk " + "%s:%s:%s - %s", + busid, wwpn, lun, strerror(errno)); + cfg_strfree(dev); + return errmsg; + } + + return NULL; +} + + +/** + * Delete FCP SCSI device. + * + * \param[in] busid Bus ID of the FCP channel through which the SCSI + * device is attached + * \param[in] wwpn WWPN through which the SCSI device is accessed + * \param[in] lun LUN of the SCSI device + * \return On success 0, on error -1. + */ + +static int +delete_scsi_device(const char *busid, const char *wwpn, const char *lun) +{ + char *echo = NULL; + int host, scsi_id, channel, scsi_lun, ret; + + if (find_scsi_device(busid, wwpn, lun, &host, &channel, &scsi_id, + &scsi_lun)) + return -1; + cfg_strprintf(&echo, "%s/%i:%i:%i:%i/delete", SYS_PATH_SCSI, host, + channel, scsi_id, scsi_lun); + ret = echo_and_test(echo, "1", 0, NULL); + cfg_strfree(&echo); + return ret; +} + + +/** + * Set FCP attached SCSI disk offline and delete device node. + * + * \param[in] busid Bus ID of the FCP channel through which the SCSI disk + * is attached + * \param[in] wwpn WWPN through which the SCSI is accessed + * \param[in] lun LUN of the SCSI disk + * \return If FCP SCSI disk was set offline 0, otherwise -1. + */ + +static int +set_fcp_disk_offline(const char *busid, const char *wwpn, const char *lun) +{ + char *path = NULL; + int ret; + + // delete FCP SCS disk + delete_scsi_device(busid, wwpn, lun); + + // remove LUN + cfg_strprintf(&path, "%s/%s/%s/unit_remove", SYS_PATH_FCP, busid, + wwpn); + echo_and_test(path, lun, 0, NULL); + + // remove WWPN + cfg_strprintf(&path, "%s/%s/port_remove", SYS_PATH_FCP, busid); + echo_and_test(path, wwpn, 0, NULL); + + // set FCP channel offline + cfg_strprintf(&path, "%s/%s/online", SYS_PATH_FCP, busid); + echo_and_test(path, "0", 0, NULL); + + // delete device node + cfg_strprintf(&path, "%s/b%s:%s:%s", BLOCKDEV_PATH, busid, wwpn, lun); + unlink(path); + cfg_strfree(&path); + + // final check + cfg_strprintf(&path, "%s/%s/%s/%s", SYS_PATH_FCP, busid, wwpn, lun); + ret = !access(path, F_OK); + cfg_strfree(&path); + + return ret; +} + + +/** + * Read SCSI MBR and update program table pointer. On success NULL is + * returned. On error a dynamically allocated error message is returned. + * Note: currently are only PC BIOS disk layouts supported! + * + * \param[in] disk Pointer to partially initialized disk structure + * \return In case of error dynamically allocated error message. + * + */ + +char * +disk_read_scsi_mbr(struct disk *disk) +{ + unsigned char *bootrecord; + char *errmsg = NULL; + disk_blockptr_t blockptr; + + // load MBR + bootrecord = malloc(disk->phy_block_size); + MEM_ASSERT(bootrecord); + memset(&blockptr, 0x0, sizeof(blockptr)); + blockptr.linear.block = 0; + blockptr.linear.size = disk->phy_block_size; + blockptr.linear.blockct = 0; + errmsg = disk_read_phy_blocks(disk, bootrecord, &blockptr); + if (errmsg) { + free(bootrecord); + return errmsg; + } + + // verify MBR + if (bootrecord[510] != 0x55 || bootrecord[511] != 0xaa) { + cfg_strcpy(&errmsg, "Unsupported SCSI disk layout."); + return errmsg; + } + if (strncmp((char *) bootrecord, MAGIC, MAGIC_LENGTH) !=0) { + cfg_strcpy(&errmsg, + "Missing boot signature on SCSI disk."); + return errmsg; + } + + read_packed_blockptr(disk, &disk->program_table_ptr, bootrecord+16); + free(bootrecord); + + return NULL; +} + + +/** + * Handle bootmap boot from FCP SCSI disk devices. On error a dynamically + * allocated error message is returned. + * + * \param[in] boot Pointer to cfg_bentry structure with boot information. + * \return In case of error dynamically allocated error message. + */ + +char * +action_bootmap_boot_zfcp(struct cfg_bentry *boot) +{ + char *errmsg = NULL, tmp_buffer[64], busid[16], wwpn[20]; + char lun[20], *dev; + int ret, n, program = 0; + regex_t preg; + regmatch_t pmatch[URI_ZFCP_BOOTMAP_MAX]; + struct disk disk; + + // compile regular expression + if ((ret = regcomp(&preg, URI_ZFCP_BOOTMAP_RE, REG_EXTENDED))) { + regerror(ret, &preg, tmp_buffer, sizeof(tmp_buffer)); + cfg_strprintf(&errmsg, + "Internal error: unable to compile regular " + "expression - %s", tmp_buffer); + return errmsg; + } + + // match URI & extract fields + ret = regexec(&preg, boot->bootmap, URI_ZFCP_BOOTMAP_MAX, pmatch, 0); + if (ret) { + regfree(&preg); + cfg_strcpy(&errmsg, + "Error - invalid 'zfcp' boot map URI."); + return errmsg; + } + strncpy(busid, boot->bootmap + pmatch[URI_ZFCP_BOOTMAP_BUSID].rm_so, + 8); + busid[8] = '\0'; + for (n = 0; n < strlen(busid); n++) + busid[n] = tolower(busid[n]); + strncpy(wwpn, boot->bootmap + pmatch[URI_ZFCP_BOOTMAP_WWPN].rm_so, 18); + wwpn[18] = '\0'; + for (n = 0; n < strlen(wwpn); n++) + wwpn[n] = tolower(wwpn[n]); + strncpy(lun, boot->bootmap + pmatch[URI_ZFCP_BOOTMAP_LUN].rm_so, 18); + lun[18] = '\0'; + for (n = 0; n < strlen(lun); n++) + lun[n] = tolower(lun[n]); + if (pmatch[URI_ZFCP_BOOTMAP_PROG].rm_so != -1) + sscanf(boot->bootmap+pmatch[URI_ZFCP_BOOTMAP_PROG].rm_so, + "%i", &program); + regfree(&preg); + + // set FCP disk online and boot + errmsg = set_fcp_disk_online(busid, wwpn, lun, &dev); + if (errmsg) + return errmsg; + errmsg = disk_open(&disk, dev); + cfg_strfree(&dev); + if (errmsg) + return errmsg; + errmsg = boot_bootmap_disk(boot, &disk, program); + + // if we are still here boot failed + disk_close(&disk); + set_fcp_disk_offline(busid, wwpn, lun); + + return errmsg; +} diff -Nuar kboot-10/ui/bootmap.h kboot-10_patch4/ui/bootmap.h --- kboot-10/ui/bootmap.h 1970-01-01 01:00:00.000000000 +0100 +++ kboot-10_patch4/ui/bootmap.h 2006-02-09 17:17:56.000000000 +0100 @@ -0,0 +1,99 @@ +/*! + * (C) Copyright IBM Corp. 2005, 2005 + * + * \file bootmap.h + * \brief Include file for reading boot maps + * + * $Id: bootmap.h,v 1.4 2006/02/09 16:17:56 loehr Exp $ + * + * Author(s): Ralph Wuerthner (rwuerthn@de.ibm.com) + */ + + +#ifndef _BOTTMAP_H_ +#define _BOOTMAP_H_ + +#include +#include + + +// Most of the following definitions are take from zipl + +// Type for representing disk block numbers +typedef uint64_t blocknum_t; + +// Pointer to block on a disk with cyl/head/sec layout +struct disk_blockptr_chs { + int cyl; + int head; + int sec; + int size; + int blockct; +}; + +// Pointer to a block on a disk with linear layout +struct disk_blockptr_linear { + blocknum_t block; + int size; + int blockct; +}; + +// Pointer to a block on disk +typedef union { + struct disk_blockptr_chs chs; + struct disk_blockptr_linear linear; +} disk_blockptr_t; + +// Disk type identifier +typedef enum { + disk_type_scsi, + disk_type_fba, + disk_type_diag, + disk_type_eckd_classic, + disk_type_eckd_compatible, + disk_type_unknown +} disk_type_t; + +// from linux/hdregs.h +struct hd_geometry { + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; + unsigned long start; +}; + +// Disk type +struct disk { + int fd; + disk_type_t type; + int phy_block_size; + uint64_t phy_blocks; + struct hd_geometry geo; + disk_blockptr_t program_table_ptr; +}; + +// path to create private block device nodes +#define BLOCKDEV_PATH "/dev" + +// magic number to idetify boot map data structures +#define MAGIC "zIPL" +#define MAGIC_LENGTH 4 + +int echo_and_test(const char *echo, const char *data, unsigned long msleep, + const char *test); +ssize_t read_file(const char *path, char *buffer, size_t buffer_len); +void disk_close(struct disk *disk); +char *disk_read_dasd_boot_record(struct disk *disk); +char *disk_read_scsi_mbr(struct disk *disk); +char *disk_open(struct disk *disk, const char *path); +void read_packed_blockptr(struct disk *disk, disk_blockptr_t *ptr, + void *buffer); +char *disk_read_phy_blocks(struct disk *disk, void *buffer, + disk_blockptr_t *blockptr); +int get_program_table_size(struct disk *disk); +char *boot_bootmap_disk(struct cfg_bentry *boot, struct disk *disk, + int program); +char *action_bootmap_boot_dasd(struct cfg_bentry *boot); +char *action_bootmap_boot_zfcp(struct cfg_bentry *boot); + +#endif /* #ifndef _BOOTMAP_H_ */ diff -Nuar kboot-10/ui/cl_dasd kboot-10_patch4/ui/cl_dasd --- kboot-10/ui/cl_dasd 1970-01-01 01:00:00.000000000 +0100 +++ kboot-10_patch4/ui/cl_dasd 2006-04-13 12:48:33.000000000 +0200 @@ -0,0 +1,155 @@ +#!/bin/sh +# +# (C) Copyright IBM Corp. 2005, 2005 +# +# cl_dasd +# +# kboot component loader module for dasd URI scheme: access files stored on +# DASD devices attached via a zSeries channel subsystem. +# +# URI structure: +# dasd://([,[,]])/ +# +# bus id: device id of DASD to be accessed +# partition: partition id on DASD, empty or '0' for whole DASD, +# '1' for 1st partition, ... +# filesystem type: optional filesystem type if autodetection doesn't work +# path to file: path within filesystem to access requested file +# +# Example: dasd://(0.0.5e89,1,)/boot/vmlinux-2.6.4 +# +# $Id: cl_dasd,v 1.6 2006/04/13 10:48:33 swen Exp $ +# +# Author(s): Ralph Wuerthner (rwuerthn@de.ibm.com) +# + +MOUNT_DIRECTORY=/var/kboot +PATH=/bin:/sbin:/usr/bin + +# extract expression from string +extract() +{ + if [ $# -eq 1 ] ; then + awk -v "RE=$1" \ + '{ match($0, RE); print substr($0, RSTART, RLENGTH) }' 2> /dev/null + else + echo "$2" | awk -v "RE=$1" \ + '{ match($0, RE); print substr($0, RSTART, RLENGTH) }' 2> /dev/null + fi +} + +# return remains after removing expression from string +remains() +{ + if [ $# -eq 1 ] ; then + awk -v "RE=$1" \ + '{ match($0, RE); print substr($0, RSTART+RLENGTH) }' 2> /dev/null + else + echo "$2" | awk -v "RE=$1" \ + '{ match($0, RE); print substr($0, RSTART+RLENGTH) }' 2> /dev/null + fi +} + +# cleanup - unregister DASD device +cleanup() +{ + umount $MOUNT_DIRECTORY/$$ > /dev/null 2>&1 + rmdir $MOUNT_DIRECTORY/$$ > /dev/null 2>&1 + if [ -n "$SET_DASD_OFFLINE" ] ; then + echo 0 > /sys/bus/ccw/devices/$BUS_ID/online + usleep 100000 + fi +} + + +# check command line arguments +if [ $# -ne 2 ] ; then + echo "Invalid number of arguments." >&2 + exit 1 +fi +DESTINATION=$1 +URI=$2 + +# extract URI components +URI_SCHEME=$( extract '^(([^:/?#]+):)?' "$URI" | extract '^([^:/?#]+)' ) +REMAINS=$( remains '^(([^:/?#]+):)?' "$URI" ) +URI_AUTHORITY=$( extract '^(//([^/?#]*))?' "$REMAINS" | cut -c 3- ) +REMAINS=$( remains '^(//([^/?#]*))?' "$REMAINS" ) +URI_PATH=$( extract '^([^?#]*)' "$REMAINS" ) +REMAINS=$( remains '^([^?#]*)' "$REMAINS" ) +URI_QUERY=$( extract '^(\?([^#]*))?' "$REMAINS" ) +REMAINS=$( remains '^(\?([^#]*))?' "$REMAINS" ) +URI_FRAGMENT=$( extract '^(#(.*))' "$REMAINS" ) + +# some sanity checks +if [ -n "$URI_QUERY" -o -n "$URI_FRAGMENT" ] ; then + echo "Invalid URI." >&2 + exit 1 +fi + +if [ -z "$( echo "$URI_AUTHORITY" | egrep \ + '^\([[:xdigit:]]\.[[:xdigit:]]\.[[:xdigit:]]{4}(,[0-3]?(,.+)?)?\)' )" ] +then + echo "Invalid DASD address." >&2 + exit 1 +fi + +# split authority +BUS_ID=$( extract '^\\([[:xdigit:]]\.[[:xdigit:]]\.[[:xdigit:]]*' \ + "$URI_AUTHORITY" | tr A-Z a-z | cut -c 2- ) +REMAINS=$( remains '^\\([[:xdigit:]]\.[[:xdigit:]]\.[[:xdigit:]]*' \ + "$URI_AUTHORITY" ) +PARTITION=$( extract '^,[0-3]?' "$REMAINS" | cut -c 2- ) +FS=$( remains '^,[0-3]?,?' "$REMAINS" | extract '[^)]*' ) +if [ "$PARTITION" = "0" ] ; then + PARTITION= +fi + +# set DASD online +SYSPATH=/sys/bus/ccw/devices/$BUS_ID +if [ ! -d $SYSPATH ] ; then + echo "No such channel device $BUS_ID." >&2 + exit 1 +fi + +if [ "$( cat $SYSPATH/online )" != 1 ] ; then + echo 1 > $SYSPATH/online + usleep 500000 + if [ "$( cat $SYSPATH/online )" != 1 ] ; then + echo "Unable to set channel device $BUS_ID online." >&2 + exit 1 + fi + SET_DASD_OFFLINE=true +fi +udevstart +DEV=/dev/$( basename $( readlink $SYSPATH/block* ) )$PARTITION + +# mount source filesystem +mkdir -p $MOUNT_DIRECTORY/$$ +if [ -n "$FS" ] ; then + mount -o ro -t "$FS" $DEV $MOUNT_DIRECTORY/$$ +else + mount -o ro $DEV $MOUNT_DIRECTORY/$$ +fi +if [ $? -ne 0 ] ; then + echo "Error mounting filesystem." >&2 + cleanup + exit 1 +fi +if [ ! -r "$MOUNT_DIRECTORY/$$/$URI_PATH" ] ; then + echo "No such file." >&2 + cleanup + exit 1 +fi + +# copy file +MSG=$( cp "$MOUNT_DIRECTORY/$$/$URI_PATH" "$DESTINATION" 2>&1 ) +if [ $? -ne 0 ] ; then + echo $MSG >&2 + cleanup + exit 1 +fi + +cleanup + +exit 0 diff -Nuar kboot-10/ui/cl_zfcp kboot-10_patch4/ui/cl_zfcp --- kboot-10/ui/cl_zfcp 1970-01-01 01:00:00.000000000 +0100 +++ kboot-10_patch4/ui/cl_zfcp 2006-02-09 17:17:56.000000000 +0100 @@ -0,0 +1,220 @@ +#!/bin/sh +# +# (C) Copyright IBM Corp. 2005, 2005 +# +# cl_zfcp +# +# kboot component loader module for zfcp URI scheme: access file stored on +# Fibre Channel disks attached via a zSeries FCP channel. +# +# URI structure: +# zfcp://(,,[,[,]])/ +# +# bus id: device id of FCP channel to be used +# WWPN: WWPN of Fibre Channel disk +# LUN: FCP LUN +# partition: partition id on Fibre Channel disk, empty or '0' for whole disk, +# '1' for 1st partition, ... +# filesystem type: optional filesystem type if autodetection doesn't work +# path to file: path within filesystem to access requested file +# +# Example: zfcp://(0.0.04ae,0x500507630e01fca2,0x4010404500000000,1,reiserfs)/boot/initrd +# +# $Id: cl_zfcp,v 1.6 2006/02/09 16:17:56 loehr Exp $ +# +# Author(s): Ralph Wuerthner (rwuerthn@de.ibm.com) +# + +MOUNT_DIRECTORY=/var/kboot +PATH=/bin:/sbin:/usr/bin + +# extract expression from string +extract() +{ + if [ $# -eq 1 ] ; then + awk -v "RE=$1" \ + '{ match($0, RE); print substr($0, RSTART, RLENGTH) }' 2> /dev/null + else + echo "$2" | awk -v "RE=$1" \ + '{ match($0, RE); print substr($0, RSTART, RLENGTH) }' 2> /dev/null + fi +} + +# return remains after removing expression from string +remains() +{ + if [ $# -eq 1 ] ; then + awk -v "RE=$1" \ + '{ match($0, RE); print substr($0, RSTART+RLENGTH) }' 2> /dev/null + else + echo "$2" | awk -v "RE=$1" \ + '{ match($0, RE); print substr($0, RSTART+RLENGTH) }' 2> /dev/null + fi +} + +# cleanup: unmount und unregister FCP disk +cleanup() +{ + umount $MOUNT_DIRECTORY/$$ > /dev/null 2>&1 + rmdir $MOUNT_DIRECTORY/$$ > /dev/null 2>&1 + + if [ -w $SCSI_DEV/delete ] ; then + echo 1 > $SCSI_DEV/delete + usleep 100000 + fi + + if [ -d $SYSPATH/$WWPN/$LUN ] ; then + echo $LUN > $SYSPATH/$WWPN/unit_remove + usleep 100000 + fi + + if [ -d $SYSPATH/$WWPN ] ; then + echo $WWPN > $SYSPATH/port_remove + usleep 100000 + fi + + if [ $( cat $SYSPATH/online ) -ne 0 ] ; then + echo 0 > $SYSPATH/online + usleep 100000 + fi +} + + +# check command line arguments +if [ $# -ne 2 ] ; then + echo "Invalid number of arguments." >&2 + exit 1 +fi +DESTINATION=$1 +URI=$2 + +# extract URI components +URI_SCHEME=$( extract '^(([^:/?#]+):)?' "$URI" | extract '^([^:/?#]+)' ) +REMAINS=$( remains '^(([^:/?#]+):)?' "$URI" ) +URI_AUTHORITY=$( extract '^(//([^/?#]*))?' "$REMAINS" | cut -c 3- ) +REMAINS=$( remains '^(//([^/?#]*))?' "$REMAINS" ) +URI_PATH=$( extract '^([^?#]*)' "$REMAINS" ) +REMAINS=$( remains '^([^?#]*)' "$REMAINS" ) +URI_QUERY=$( extract '^(\?([^#]*))?' "$REMAINS" ) +REMAINS=$( remains '^(\?([^#]*))?' "$REMAINS" ) +URI_FRAGMENT=$( extract '^(#(.*))' "$REMAINS" ) + +# some sanity checks +if [ -n "$URI_QUERY" -o -n "$URI_FRAGMENT" ] ; then + echo "Invalid URI." >&2 + exit 1 +fi + +if [ -z "$( echo "$URI_AUTHORITY" | egrep '^\([[:xdigit:]]\.[[:xdigit:]]\.[[:xdigit:]]{4},0x[[:xdigit:]]{16},0x[[:xdigit:]]{16}(,[[:digit:]]*(,.+)?)?\)' )" ] +then + echo "Invalid FCP device address." >&2 + exit 1 +fi + +# split authority +BUS_ID=$( extract '^\\([[:xdigit:]]\.[[:xdigit:]]\.[[:xdigit:]]*' \ + "$URI_AUTHORITY" | tr A-Z a-z | cut -c 2-) +REMAINS=$( remains '^\\([[:xdigit:]]\.[[:xdigit:]]\.[[:xdigit:]]*,' \ + "$URI_AUTHORITY" ) +WWPN=$( extract '^0x[[:xdigit:]]*' "$REMAINS" | tr A-Z a-z ) +REMAINS=$( remains '^0x[[:xdigit:]]*,' "$REMAINS" ) +LUN=$( extract '^0x[[:xdigit:]]*' "$REMAINS" | tr A-Z a-z ) +REMAINS=$( remains '^0x[[:xdigit:]]*' "$REMAINS" ) +PARTITION=$( extract '^,[[:digit:]]*' "$REMAINS" | cut -c 2- ) +FS=$( remains '^,[[:digit:]]*,?' "$REMAINS" | extract '[^)]*' ) +if [ "$PARTITION" = "0" ] ; then + PARTITION= +fi + +# set FCP channel online +SYSPATH=/sys/bus/ccw/drivers/zfcp/$BUS_ID +if [ ! -r $SYSPATH ]; then + echo "$BUS_ID is not a channel device." >&2 + exit 1 +fi +if [ $( cat $SYSPATH/online ) != "1" ] ; then + echo 1 > $SYSPATH/online + usleep 500000 + if [ $( cat $SYSPATH/online ) != "1" ] ; then + echo "Bus ID $BUS_ID cannot be set online." >&2 + exit 1 + fi +fi + +# add WWPN +if [ ! -d $SYSPATH/$WWPN ] ; then + echo $WWPN > $SYSPATH/port_add + usleep 100000 + if [ $( cat $SYSPATH/$WWPN/failed ) != "0" ] ; then + echo "WWPN $WWPN on $BUS_ID cannot be added." >&2 + cleanup + exit 1 + fi +fi + +# add LUN +if [ ! -d $SYSPATH/$WWPN/$LUN ] ; then + echo $LUN > $SYSPATH/$WWPN/unit_add + usleep 100000 + if [ $( cat $SYSPATH/$WWPN/$LUN/failed ) != "0" ] ; then + echo "LUN $LUN on WWPN $WWPN on $BUS_ID cannot be added." >&2 + cleanup + exit 1 + fi +fi + +# find SCSI block device +udevstart +for SCSI_DEV in /sys/bus/scsi/devices/* ; do + if [ "$( cat $SCSI_DEV/hba_id )" = "$BUS_ID" -a \ + "$( cat $SCSI_DEV/wwpn )" = "$WWPN" -a \ + "$( cat $SCSI_DEV/fcp_lun )" = "$LUN" ] ; then + if [ ! -L $SCSI_DEV/block ] ; then + echo "LUN $LUN on WWPN $WWPN on $BUS_ID is not a block device." >&2 + cleanup + exit 1 + fi + DEV="/dev/$( basename $( readlink $SCSI_DEV/block ) )$PARTITION" + break + fi +done +if [ -z "$DEV" ] ; then + echo "Unable find block device." >&2 + cleanup + exit 1 +fi +if [ ! -b "$DEV" ] ; then + echo "Unable to access block device $DEV." >&2 + cleanup + exit 1 +fi + +# mount source filesystem +mkdir -p $MOUNT_DIRECTORY/$$ +if [ -n "$FS" ] ; then + mount -o ro -t "$FS" $DEV $MOUNT_DIRECTORY/$$ +else + mount -o ro $DEV $MOUNT_DIRECTORY/$$ +fi +if [ $? -ne 0 ] ; then + echo "Error mounting filesystem." >&2 + cleanup + exit 1 +fi +if [ ! -r "$MOUNT_DIRECTORY/$$/$URI_PATH" ] ; then + echo "No such file." >&2 + exit 1 +fi + +# copy file +MSG=$( cp "$MOUNT_DIRECTORY/$$/$URI_PATH" "$DESTINATION" 2>&1 ) +if [ $? -ne 0 ] ; then + echo $MSG >&2 + cleanup + exit 1 +fi + +cleanup + +exit 0 diff -Nuar kboot-10/ui/insfile.c kboot-10_patch4/ui/insfile.c --- kboot-10/ui/insfile.c 1970-01-01 01:00:00.000000000 +0100 +++ kboot-10_patch4/ui/insfile.c 2006-02-09 17:17:57.000000000 +0100 @@ -0,0 +1,273 @@ +/*! + * (C) Copyright IBM Corp. 2005 + * + * \file insfile.c + * \brief analyzes a given insfile and collects all referenced boot files + * + * $Id: insfile.c,v 1.10 2006/02/09 16:17:57 loehr Exp $ + * + * Author(s): Michael Loehr (mloehr@de.ibm.com) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "kboot.h" +#include "insfile.h" + + +/** + * get the path (everything except the filename at the end) + * from an uri + * + * \param[out] uri_path string to be initialized with the path + * \param[in] uri URI to be analyzed + */ +void get_uri_path(char **uri_path, const char *uri) +{ + int i = 0; + int copylen = strlen(uri); + + cfg_strinit(uri_path); + + for (i = strlen(uri)-1; i >= 0; i--) { + if (uri[i] == '/') { + copylen = i+1; // everything including the last '/' + break; + } + } + + cfg_strncpy(uri_path, uri, copylen); +} + + +/** + * takes the info from a single insfile line and determines the type + * of this insfile component. implements a kind of 'voting' mechanism + * to identify the file type. this allows to add more rules to + * increase the reliability of the identification. + * + * \param[in] path path in the local file system + * \param[in] name name of the file + * \param[in] addr load address for this insfile component + * \return type of the insfile component + */ +enum ins_component_type identify_file(const char *name, long addr) +{ + FILE *file = NULL; + struct stat fileinfo; + int probability[INS_LAST]; + int type = INS_OTHER; + int highest_probability = 0; + int most_probable = INS_OTHER; + int signature = 0; + int i = 0; + int ch = 0; + + for (type = INS_KERNEL; type < INS_LAST; type++) + probability[type] = 0; + + if (stat(name, &fileinfo) != 0) { + fprintf(stderr, "%s: stat not possible for %s\n", arg0, name); + goto cleanup; + } + + // now we have some rules of thumb: + + // is it a big file ? + if (fileinfo.st_size > (512*1024)) { + probability[INS_KERNEL]++; + probability[INS_INITRD]++; + + // ... and first 2 bytes are a 0x1f8b gzip signature ? + file = fopen(name, "r"); + if (!file) { + fprintf(stderr, "%s: fopen not possible for %s\n", arg0, name); + goto cleanup; + } + for (i = 0; i < 2; i++) { + signature = (signature<<8) | fgetc(file); + } + fclose(file); + if (signature == 0x1f8b) { + probability[INS_INITRD]++; + } + } + + // size is exactly 4 bytes + if (fileinfo.st_size == 4) { + probability[INS_INITRDSIZE]++; + } + + // size is in the valid range for a kernel commandline + if (fileinfo.st_size <= KBOOT_MAX_KERNEL_CMDLINE) { + probability[INS_PARMFILE]++; + + // ... and file contains only valid printable characters + file = fopen(name, "r"); + if (!file) { + fprintf(stderr, "%s: fopen not possible for %s\n", arg0, name); + goto cleanup; + } + probability[INS_PARMFILE]++; // assume only valid characters + while ((ch = fgetc(file)) != EOF) { + if (!isprint(ch)) { + probability[INS_PARMFILE]--; // assumption was wrong + break; + } + } + fclose(file); + } + + // have a look on the addresses: + if (addr == 0x00000000) + probability[INS_KERNEL]++; + + if (addr == 0x00010480) + probability[INS_PARMFILE]++; + + if (addr == 0x00800000) + probability[INS_INITRD]++; + + if (addr == 0x00010414) + probability[INS_INITRDSIZE]++; + + // finally we select the most probable filetype + for (type = INS_KERNEL; type < INS_LAST; type++) { + if (probability[type] > highest_probability) { + highest_probability = probability[type]; + most_probable = type; + } + } + + cleanup: + + return most_probable; +} + + +/** + * Insfile boot method: Read and analyse INSFILE, copy files to local + * filesystem and call \p kexec to boot new kernel. On success function + * does not return. On error a dynamically allocated error message + * is returned. + * + * \param boot Pointer to boot entry. + * \return Dynamically allocated error message. + */ +char *action_insfile_boot(struct cfg_bentry *boot) +{ + char *msg = NULL; + char *tmp_uri = NULL; + char *uri_path = NULL; + char *tmp_msg = NULL; + char *local_name = NULL; + char *filename = NULL; + FILE *insfile = NULL; /*!< insfile to read from */ + char input[CFG_STR_MAX_LEN] = ""; /* input line from insfile */ + char input_name[CFG_STR_MAX_LEN] = ""; + long addr = 0; + char *cmdline = 0; + int linecount = 0; + + cfg_strinit(&msg); + cfg_strinit(&local_name); + cfg_strinit(&cmdline); + cfg_strinit(&filename); + + // get the insfile + if (strlen(boot->insfile)) { + tmp_uri = prefix_root(boot->root, boot->insfile); + if (comp_load(KBOOT_FILENAME_INSFILE, tmp_uri, NULL, &tmp_msg)) { + cfg_strprintf(&msg, "Error loading insfile '%s' - %s", + tmp_uri, tmp_msg); + goto cleanup; + } + + get_uri_path(&uri_path, tmp_uri); + cfg_strfree(&tmp_uri); + } + + // get filenames from insfile + insfile = fopen(KBOOT_FILENAME_INSFILE, "r"); + if (!insfile) { + cfg_strprintf(&msg, "Unable to open insfile - %s\n", + KBOOT_FILENAME_INSFILE); + goto cleanup; + } + + while (fgets(input, CFG_STR_MAX_LEN, insfile) != NULL) { + if (input[0] != '*') { // comments start with '*' + sscanf(input, "%s %lx", (char *) (&input_name), &addr); + cfg_strcpy(&filename, input_name); + linecount++; + + // get the file referenced by the insfile + if (strlen(filename)) { + tmp_uri = prefix_root(uri_path, filename); + + cfg_strprintf(&local_name, "/tmp/file_%d", linecount); + + if (comp_load(local_name, tmp_uri, NULL, &tmp_msg)) { + cfg_strprintf(&msg, "Error loading insfile component '%s' - %s", + filename, tmp_msg); + goto cleanup; + } + + switch (identify_file(local_name, addr)) { + case INS_KERNEL: + rename(local_name, KBOOT_FILENAME_KERNEL); + break; + + case INS_INITRD: + rename(local_name, KBOOT_FILENAME_INITRD); + break; + + case INS_PARMFILE: + rename(local_name, KBOOT_FILENAME_PARMFILE); + break; + + case INS_INITRDSIZE: // from here on nothing usefull + case INS_OTHER: + default: + unlink(local_name); + } + } + } + } + + fclose(insfile); + unlink(KBOOT_FILENAME_INSFILE); + + // if all required files exist + if ((access(KBOOT_FILENAME_KERNEL, F_OK) == 0) && + (access(KBOOT_FILENAME_INITRD, F_OK) == 0) && + (access(KBOOT_FILENAME_PARMFILE, F_OK) == 0)) { + + msg = compose_commandline(&cmdline, KBOOT_FILENAME_PARMFILE, + boot->cmdline); + if (msg) + goto cleanup; + + msg = kexec(KBOOT_FILENAME_KERNEL, KBOOT_FILENAME_INITRD, cmdline); + } + + cfg_strfree(&cmdline); + + cleanup: + + cfg_strfree(&local_name); + cfg_strfree(&cmdline); + cfg_strfree(&filename); + + unlink(KBOOT_FILENAME_KERNEL); + unlink(KBOOT_FILENAME_INITRD); + unlink(KBOOT_FILENAME_PARMFILE); + + return msg; +} diff -Nuar kboot-10/ui/insfile.h kboot-10_patch4/ui/insfile.h --- kboot-10/ui/insfile.h 1970-01-01 01:00:00.000000000 +0100 +++ kboot-10_patch4/ui/insfile.h 2006-02-09 17:17:57.000000000 +0100 @@ -0,0 +1,33 @@ +/*! + * (C) Copyright IBM Corp. 2005 + * + * \file insfile.h + * \brief general definitions for the insfile loader + * + * $Id: insfile.h,v 1.4 2006/02/09 16:17:57 loehr Exp $ + * + * Author(s): Ralph Wuerthner (rwuerthn@de.ibm.com) + * Michael Loehr (mloehr@de.ibm.com) + * + */ + +#ifndef _INSFILE_H_ +#define _INSFILE_H_ + +#include "config.h" + +/** + * types of components referenced by *.ins-file + */ +enum ins_component_type { + INS_KERNEL, //!< must be the first entry of this enum! + INS_INITRD, //!< component was identified as initrd + INS_PARMFILE, //!< component was identified as parmfile + INS_INITRDSIZE, //!< component was identified as initrd size + INS_OTHER, //!< component was not identified + INS_LAST //!< must be the last entry of this enum! +}; + +char *action_insfile_boot(struct cfg_bentry *boot); + +#endif /* #ifndef _INSFILE_H_ */ diff -Nuar kboot-10/ui/loader.c kboot-10_patch4/ui/loader.c --- kboot-10/ui/loader.c 2006-06-21 15:34:16.000000000 +0200 +++ kboot-10_patch4/ui/loader.c 2006-04-25 16:06:46.000000000 +0200 @@ -23,6 +23,8 @@ #include #include #include "kboot.h" +#include "insfile.h" +#include "bootmap.h" #include "debug.h" @@ -334,6 +336,32 @@ /** + * Boot map boot method: Read and analyse boot map, copy files to local + * filesystem and call \p kexec to boot new kernel. On success function + * does not return. On error a dynamically allocated error message + * is returned. + * + * \param boot Pointer to boot entry. + * \return Dynamically allocated error message. + */ + +char * +action_bootmap_boot(struct cfg_bentry *boot) +{ + char *msg; + + if (strstr(boot->bootmap, "dasd://") == boot->bootmap) + msg = action_bootmap_boot_dasd(boot); + else if (strstr(boot->bootmap, "zfcp://") == boot->bootmap) + msg = action_bootmap_boot_zfcp(boot); + else + cfg_strinitcpy(&msg, "Unsupported bootmap URI scheme."); + + return msg; +} + + +/** * Reboot method: reboot system by calling /sbin/reboot. On success function * does not return. On error a dynamically allocated error message * is returned. @@ -455,6 +483,14 @@ msg = action_kernel_boot(boot); break; + case INSFILE_BOOT: + msg = action_insfile_boot(boot); + break; + + case BOOTMAP_BOOT: + msg = action_bootmap_boot(boot); + break; + case REBOOT: msg = action_reboot(); break; diff -Nuar kboot-10/ui/Makefile kboot-10_patch4/ui/Makefile --- kboot-10/ui/Makefile 2006-06-21 15:48:19.000000000 +0200 +++ kboot-10_patch4/ui/Makefile 2006-06-28 10:52:48.000000000 +0200 @@ -13,6 +13,7 @@ INSTALL = install -c kboot: kboot.o config.o parser.o parser_kboot.o ui_control.o debug.o \ + insfile.o bootmap_common.o bootmap_dasd.c bootmap_fcp.c \ loader.o comp_load.o netbase.o dhcp_request.o config_parser.o \ config_scanner.o modbase.o $(CC) $(CFLAGS) -o $@ $^ @@ -50,10 +51,15 @@ mkdir -p $(INSTDIR)/setup $(INSTALL) kboot $(INSTDIR) $(INSTALL) cl_file $(INSTDIR)/cl + $(INSTALL) cl_dasd $(INSTDIR)/cl + $(INSTALL) cl_zfcp $(INSTDIR)/cl $(INSTALL) cl_block $(INSTDIR)/cl $(INSTALL) cl_ftp $(INSTDIR)/cl $(INSTALL) cl_http $(INSTDIR)/cl $(INSTALL) cl_scp $(INSTDIR)/cl + $(INSTALL) setup_zfcp $(INSTDIR)/setup + $(INSTALL) setup_qeth $(INSTDIR)/setup + $(INSTALL) setup_dasd $(INSTDIR)/setup $(INSTALL) ui_linemode $(INSTDIR)/ui $(INSTALL) ui_ssh $(INSTDIR)/ui diff -Nuar kboot-10/ui/setup_dasd kboot-10_patch4/ui/setup_dasd --- kboot-10/ui/setup_dasd 1970-01-01 01:00:00.000000000 +0100 +++ kboot-10_patch4/ui/setup_dasd 2006-05-03 16:46:27.000000000 +0200 @@ -0,0 +1,22 @@ +#!/bin/sh +# +# (C) Copyright IBM Corp. 2006 +# +# setup_dasd +# +# kboot script to set dasd devices online on s390 +# +# +# $Id: setup_dasd,v 1.1 2006/05/03 14:46:27 loehr Exp $ +# +# Author(s): Michael Loehr (mloehr@de.ibm.com) +# + +if [ $# -ne 1 ]; then + echo "Invalid number of parameters." + exit 1 +fi + +echo 1 >/sys/bus/ccw/devices/$1/online +udevstart +evms_activate diff -Nuar kboot-10/ui/setup_qeth kboot-10_patch4/ui/setup_qeth --- kboot-10/ui/setup_qeth 1970-01-01 01:00:00.000000000 +0100 +++ kboot-10_patch4/ui/setup_qeth 2006-05-03 16:46:27.000000000 +0200 @@ -0,0 +1,22 @@ +#!/bin/sh +# +# (C) Copyright IBM Corp. 2006 +# +# setup_qeth +# +# kboot script to set qeth devices online on s390 +# +# +# $Id: setup_qeth,v 1.1 2006/05/03 14:46:27 loehr Exp $ +# +# Author(s): Michael Loehr (mloehr@de.ibm.com) +# + +if [ $# -ne 3 ]; then + echo "Invalid number of parameters." + exit 1 +fi + +echo $1,$2,$3 >/sys/bus/ccwgroup/drivers/qeth/group +echo 1 >/sys/bus/ccwgroup/drivers/qeth/$1/online +udevstart diff -Nuar kboot-10/ui/setup_zfcp kboot-10_patch4/ui/setup_zfcp --- kboot-10/ui/setup_zfcp 1970-01-01 01:00:00.000000000 +0100 +++ kboot-10_patch4/ui/setup_zfcp 2006-05-03 16:46:27.000000000 +0200 @@ -0,0 +1,35 @@ +#!/bin/sh +# +# (C) Copyright IBM Corp. 2006 +# +# setup_zfcp +# +# kboot script to set zfcp devices online on s390 +# +# +# $Id: setup_zfcp,v 1.1 2006/05/03 14:46:27 loehr Exp $ +# +# Author(s): Michael Loehr (mloehr@de.ibm.com) +# + +if [ $# -ne 3 ]; then + echo "Invalid number of parameters." + exit 1 +fi + +BUSID=$1 +WWPN=$2 +LUN=$3 + +cd /sys/bus/ccw/drivers/zfcp/$BUSID/ +echo 1 >online +usleep 500000 +echo $WWPN >port_add +usleep 500000 +cd $WWPN +echo $LUN >unit_add +usleep 500000 +udevstart +usleep 500000 +evms_activate +usleep 500000 diff -Nuar kboot-10/ui/ui_ssh.c kboot-10_patch4/ui/ui_ssh.c --- kboot-10/ui/ui_ssh.c 2006-06-27 10:30:21.000000000 +0200 +++ kboot-10_patch4/ui/ui_ssh.c 2006-06-27 14:55:46.000000000 +0200 @@ -4,7 +4,7 @@ * \file ui_ssh.c * \brief kboot user interface module for remote ssh-clients * - * $Id: ui_ssh.c,v 1.9 2006/06/26 12:36:57 loehr Exp $ + * $Id: ui_ssh.c,v 1.10 2006/06/27 12:55:46 loehr Exp $ * * Author(s): Swen Schillig (swen@vnet.ibm.com) * Using Tomcat but need to do more? Need to support web services, security? Get stuff done quickly with pre-integrated technology to make your job easier Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642 _______________________________________________ Kboot-general mailing list Kboot-general@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/kboot-general