diff --git a/zipl/boot/eckd2dump.c b/zipl/boot/eckd2dump.c index 1c73f10f..3cfa1580 100644 --- a/zipl/boot/eckd2dump.c +++ b/zipl/boot/eckd2dump.c @@ -3,7 +3,7 @@ * * Common ECKD dump I/O functions * - * Copyright IBM Corp. 2013, 2017 + * Copyright IBM Corp. 2013, 2018 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -444,6 +444,34 @@ void readblock(unsigned long blk, unsigned long addr, unsigned long blk_count) io_block(cmd, blk, addr, blk_count, 0, 1); } +/* + * Write dump segment with the header to DASD and return the next free + * block number + */ +unsigned long write_dump_segment(unsigned long blk, + struct df_s390_dump_segm_hdr *segm, + unsigned long zero_page) +{ + unsigned long addr, start_blk, blk_count; + + /* Write the dump segment header itself (1 page) */ + writeblock(blk, (unsigned long)segm, m2b(PAGE_SIZE), zero_page); + blk += m2b(PAGE_SIZE); + /* Write the dump segment */ + addr = segm->start; + start_blk = blk; + while (addr < segm->start + segm->len) { + /* Remaining blocks to write */ + blk_count = m2b(segm->len) - (blk - start_blk); + blk_count = MIN(blk_count, eckd_blk_max); + writeblock(blk, addr, blk_count, zero_page); + progress_print(addr); + blk += blk_count; + addr += b2m(blk_count); + } + return blk; +} + /* * Init ECKD common */ diff --git a/zipl/boot/eckd2dump.h b/zipl/boot/eckd2dump.h index 3112dbaf..1f5dc3ed 100644 --- a/zipl/boot/eckd2dump.h +++ b/zipl/boot/eckd2dump.h @@ -3,7 +3,7 @@ * * Common ECKD dump I/O functions * - * Copyright IBM Corp. 2013, 2017 + * Copyright IBM Corp. 2013, 2018 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -12,6 +12,7 @@ #define ECKD2DUMP_H #include "cio.h" +#include "stage2dump.h" struct eckd_device { uint32_t blk_start; @@ -46,5 +47,8 @@ void stage2dump_eckd_init(); void writeblock(unsigned long blk, unsigned long addr, unsigned long blk_count, unsigned long zero_page); void readblock(unsigned long blk, unsigned long addr, unsigned long blk_count); +unsigned long write_dump_segment(unsigned long blk, + struct df_s390_dump_segm_hdr *segm, + unsigned long zero_page); #endif /* ECKD2DUMP_H */ diff --git a/zipl/boot/eckd2dump_mv.c b/zipl/boot/eckd2dump_mv.c index 9802dd91..9fb25fd1 100644 --- a/zipl/boot/eckd2dump_mv.c +++ b/zipl/boot/eckd2dump_mv.c @@ -3,7 +3,7 @@ * * Multi-volume ECKD DASD dump tool * - * Copyright IBM Corp. 2013, 2017 + * Copyright IBM Corp. 2013, 2018 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -13,14 +13,14 @@ #include "error.h" #include "stage2dump.h" -#define MVDUMP_SIZE 0x3000 /* Size of dump record */ +#define MVDUMP_TOOL_SIZE 0x3000 /* Size of dump record */ #define MAX_DUMP_VOLUMES 32 /* Up to 32 dump volumes possible */ /* * Magic number at start of dump record */ uint64_t magic __attribute__((section(".stage2.head"))) - = 0x5a4d554c54363405ULL; /* ZMULT64, version 5 */ + = 0x584d554c54363401ULL; /* XMULT64, version 1 */ /* * Parameter format for ECKD MV dumper (13 bytes): @@ -188,73 +188,117 @@ void dt_device_enable(void) stage2dump_eckd_init(); } +/* + * Check for the volume timestamp and validate the dump signature + * before writing a dump. + */ +static void check_volume(unsigned long page) +{ + struct mvdump_parm_table *mvdump_table_new; + struct df_s390_hdr *hdr_new; + + /* + * Check whether parameter table on dump device has a valid + * time stamp. The parameter table is located right behind + * the dump tool, the corresponding block number is: + * MAGIC_BLK_ECKD + (MVDUMP_TOOL_SIZE / blocksize) + */ + mvdump_table_new = (void *) page; + readblock(DF_S390_MAGIC_BLK_ECKD + m2b(MVDUMP_TOOL_SIZE), + __pa(mvdump_table_new), 1); + /* + * Check if time stamps match + */ + if (mvdump_table.tod != mvdump_table_new->tod) + panic(ENOTIME, "Inconsistent time stamps"); + /* + * Check if dump partition has a valid dump signature. + * Bypass signature check if "--force" had been specified during + * zipl -M. + */ + hdr_new = (void *) page; + if (!parm_tail.mvdump_force) { + readblock(device.blk_start, __pa(hdr_new), 1); + if (dump_hdr->magic != hdr_new->mvdump_sign) + panic(ENOSIGN, "Wrong signature"); + } +} + +/* + * Write the dump header and memory to the current volume and return the next + * address to write for the next volume or memory size if the dump ended + * on this volume + */ +static unsigned long write_volume(unsigned long addr, unsigned long page, + struct df_s390_dump_segm_hdr *dump_segm) +{ + unsigned long free_space, blk; + + /* + * Write dump header + */ + blk = device.blk_start; + writeblock(blk, __pa(dump_hdr), m2b(DF_S390_HDR_SIZE), 0); + blk += m2b(DF_S390_HDR_SIZE); + /* + * The free space left on the volume minus 2 blocks (for the segment + * header and the end marker) + */ + free_space = b2m(device.blk_end - blk + 1) - b2m(2); + memset((void *) page, 0, PAGE_SIZE); + /* + * Write dump data + */ + while (addr < dump_hdr->mem_size) { + /* + * Find the next non-zero dump segment with the limit + * of segment length set to the amount of free space left + */ + addr = find_dump_segment(addr, dump_hdr->mem_size, + ROUND_DOWN(free_space, MIB), + dump_segm); + blk = write_dump_segment(blk, dump_segm, page); + /* Update free space left on vol */ + free_space -= dump_segm->len; + /* Reserve one block for the next segment header */ + if (free_space) + free_space -= b2m(1); + total_dump_size += dump_segm->len; + /* Check if no more dump segments follow */ + if (dump_segm->stop_marker) { + /* Write end marker */ + df_s390_em_page_init(page); + writeblock(blk, page, 1, 0); + return dump_hdr->mem_size; + } + /* + * Go to the new volume if not enough space + */ + if (free_space < MIB) + break; + } + return addr; +} + /* * Dump all memory to multiple DASD partitions */ void dt_dump_mem(void) { - unsigned long blk, addr, addr_end, blk_num, dev_mem_size, page; - struct mvdump_parm_table *mvdump_table_new; - struct df_s390_hdr *hdr_new; + struct df_s390_dump_segm_hdr *dump_segm; + unsigned long addr, page; - dump_hdr->mvdump_sign = DF_S390_MAGIC; + dump_hdr->mvdump_sign = DF_S390_MAGIC_EXT; dump_hdr->mvdump = 1; addr = 0; - + total_dump_size = 0; page = get_zeroed_page(); - do { + dump_segm = (void *)get_zeroed_page(); + + while (1) { printf("Dumping to: 0.%x.%04x", device.sid.ssid, device.devno); - - /* - * Check whether parameter table on dump device has a valid - * time stamp. The parameter table is located right behind - * the dump tool, the corresponding block number is: - * MAGIC_BLOCK_OFFSET + (MVDUMP_TOOL_SIZE / blocksize) - * So dump tool starts on track 0, block 3 - */ - mvdump_table_new = (void *) page; - readblock(3 + MVDUMP_SIZE / 0x1000, - __pa(mvdump_table_new), m2b(0x1000)); - /* - * Check if time stamps match - */ - if (mvdump_table.tod != mvdump_table_new->tod) - panic(ENOTIME, "Inconsistent time stamps"); - - /* - * Check if dump partition has a valid dump signature. - * Bypass signature check if "--force" had been specified during - * zipl -M. - */ - hdr_new = (void *) page; - if (!parm_tail.mvdump_force) { - readblock(device.blk_start, __pa(hdr_new), 1); - if (dump_hdr->magic != hdr_new->mvdump_sign) - panic(ENOSIGN, "Wrong signature"); - } - - /* - * Write dump header - */ - blk = device.blk_start; - writeblock(blk, __pa(dump_hdr), m2b(DF_S390_HDR_SIZE), 0); - blk += m2b(DF_S390_HDR_SIZE); - - dev_mem_size = b2m(device.blk_end - blk + 1); - addr_end = MIN(dump_hdr->mem_size, addr + dev_mem_size); - - /* - * Write memory - */ - memset((void *) page, 0, PAGE_SIZE); - while (addr < addr_end) { - blk_num = MIN(eckd_blk_max, device.blk_end - blk + 1); - blk_num = MIN(m2b(addr_end - addr), blk_num); - writeblock(blk, addr, blk_num, page); - progress_print(addr); - blk += blk_num; - addr += b2m(blk_num); - } + check_volume(page); + addr = write_volume(addr, page, dump_segm); if (addr == dump_hdr->mem_size) break; /* @@ -267,12 +311,8 @@ void dt_dump_mem(void) dt_device_parm_setup(); set_device(device.sid, DISABLED); dt_device_enable(); - } while (1); + } progress_print(addr); - /* - * Write end marker - */ - df_s390_em_page_init(page); - writeblock(blk, page, 1, 0); free_page(page); + free_page(__pa(dump_segm)); } diff --git a/zipl/boot/eckd2dump_sv.c b/zipl/boot/eckd2dump_sv.c index 39e5762c..047cc22c 100644 --- a/zipl/boot/eckd2dump_sv.c +++ b/zipl/boot/eckd2dump_sv.c @@ -3,7 +3,7 @@ * * Single-volume ECKD DASD dump tool * - * Copyright IBM Corp. 2013, 2017 + * Copyright IBM Corp. 2013, 2018 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -17,7 +17,7 @@ * Magic number at start of dump record */ uint64_t magic __attribute__((section(".stage2.head"))) = - 0x5a45434b44363404ULL; /* "ZECKD64", version 4 */ + 0x5845434b44363401ULL; /* "XECKD64", version 1 */ /* * ECKD parameter block passed by zipl @@ -61,20 +61,29 @@ void dt_device_enable(void) */ void dt_dump_mem(void) { - unsigned long blk, addr, page; + unsigned long blk, addr, end, page; + struct df_s390_dump_segm_hdr *dump_segm; blk = device.blk_start; page = get_zeroed_page(); + dump_segm = (void *)get_zeroed_page(); /* Write dump header */ writeblock(blk, __pa(dump_hdr), m2b(DF_S390_HDR_SIZE), 0); blk += m2b(DF_S390_HDR_SIZE); /* Write memory */ - for (addr = 0; addr < dump_hdr->mem_size; addr += b2m(eckd_blk_max)) { - writeblock(blk, addr, eckd_blk_max, page); - progress_print(addr); - blk += eckd_blk_max; + addr = 0; + total_dump_size = 0; + end = dump_hdr->mem_size; + while (addr < end) { + addr = find_dump_segment(addr, end, 0, dump_segm); + blk = write_dump_segment(blk, dump_segm, page); + total_dump_size += dump_segm->len; + if (dump_segm->stop_marker) { + addr = end; + break; + } } progress_print(addr); @@ -82,4 +91,5 @@ void dt_dump_mem(void) df_s390_em_page_init(page); writeblock(blk, page, 1, 0); free_page(page); + free_page(__pa(dump_segm)); } diff --git a/zipl/boot/fba2dump.c b/zipl/boot/fba2dump.c index d855552c..9a8f5fed 100644 --- a/zipl/boot/fba2dump.c +++ b/zipl/boot/fba2dump.c @@ -3,7 +3,7 @@ * * Single-volume FBA DASD dump tool * - * Copyright IBM Corp. 2013, 2017 + * Copyright IBM Corp. 2013, 2018 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -13,14 +13,15 @@ #include "fba.h" #include "stage2dump.h" -#define BLK_PWRT 64 /* Blocks per write */ -#define BLK_SIZE 0x200 /* FBA block size */ +#define BLK_PWRT 64 /* Blocks per write */ +#define BLK_SIZE 0x200 /* FBA block size */ +#define BLK_PER_PAGE (PAGE_SIZE / BLK_SIZE) /* FBA blocks per page */ /* * Magic number at start of dump record */ uint64_t magic __attribute__((section(".stage2.head"))) - = 0x5a44464241363404ULL; /* ZDFBA64, version 4 */ + = 0x5844464241363401ULL; /* XDFBA64, version 1 */ /* * FBA dump device partition specification @@ -120,8 +121,8 @@ void dt_device_enable(void) /* * Write memory with number of blocks to start block */ -static void writeblock(unsigned long blk, unsigned long addr, unsigned long blk_count, - unsigned long zero_page) +static void writeblock_fba(unsigned long blk, unsigned long addr, + unsigned long blk_count, unsigned long zero_page) { unsigned long blk_end; @@ -135,6 +136,35 @@ static void writeblock(unsigned long blk, unsigned long addr, unsigned long blk_ start_io(IPL_SC, &irb, &orb, 1); } +/* + * Write dump segment with the header to FBA and return the next free + * block number + */ +unsigned long write_dump_segment_fba(unsigned long blk, + struct df_s390_dump_segm_hdr *dump_segm, + unsigned long zero_page) +{ + unsigned long addr, start_blk, blk_count; + + /* Write the dump segment header itself (1 page) */ + writeblock_fba(blk, __pa(dump_segm), BLK_PER_PAGE, zero_page); + blk += BLK_PER_PAGE; + /* Write the dump segment */ + addr = dump_segm->start; + start_blk = blk; + while (addr < dump_segm->start + dump_segm->len) { + /* Remaining blocks to write */ + blk_count = m2b(dump_segm->len) - (blk - start_blk); + blk_count = MIN(blk_count, BLK_PWRT); + writeblock_fba(blk, addr, blk_count, zero_page); + progress_print(addr); + blk += blk_count; + addr += b2m(blk_count); + } + return blk; +} + + /* * Initialize the CCW program */ @@ -163,26 +193,36 @@ static void ccw_program_init(void) */ void dt_dump_mem(void) { - unsigned long blk, addr, page; + struct df_s390_dump_segm_hdr *dump_segm; + unsigned long blk, addr, end, page; ccw_program_init(); blk = device.blk_start; page = get_zeroed_page(); + dump_segm = (void *)get_zeroed_page(); /* Write dump header */ - writeblock(blk, __pa(dump_hdr), m2b(DF_S390_HDR_SIZE), 0); - blk += DF_S390_HDR_SIZE / BLK_SIZE; + writeblock_fba(blk, __pa(dump_hdr), m2b(DF_S390_HDR_SIZE), 0); + blk += m2b(DF_S390_HDR_SIZE); /* Write memory */ - for (addr = 0; addr < dump_hdr->mem_size; addr += b2m(BLK_PWRT)) { - writeblock(blk, addr, BLK_PWRT, page); - progress_print(addr); - blk += BLK_PWRT; + addr = 0; + total_dump_size = 0; + end = dump_hdr->mem_size; + while (addr < end) { + addr = find_dump_segment(addr, end, 0, dump_segm); + blk = write_dump_segment_fba(blk, dump_segm, page); + total_dump_size += dump_segm->len; + if (dump_segm->stop_marker) { + addr = end; + break; + } } progress_print(addr); /* Write end marker */ df_s390_em_page_init(page); - writeblock(blk, page, 1, 0); + writeblock_fba(blk, page, 1, 0); free_page(page); + free_page(__pa(dump_segm)); } diff --git a/zipl/boot/stage2dump.c b/zipl/boot/stage2dump.c index ee909d97..7161dc01 100644 --- a/zipl/boot/stage2dump.c +++ b/zipl/boot/stage2dump.c @@ -3,7 +3,7 @@ * * Common functions for stand-alone dump tools * - * Copyright IBM Corp. 2013, 2017 + * Copyright IBM Corp. 2013, 2018 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -45,6 +45,7 @@ struct stage2dump_parm_tail parm_tail * Globals */ struct df_s390_hdr *dump_hdr; +unsigned long total_dump_size; /* * Init dumper: Allocate standard pages, set timers @@ -86,6 +87,112 @@ void progress_print(unsigned long addr) progress_next_addr += progress_inc; } +/* + * Check if one MB memory after addr is zero + */ +static int __is_zero_mb(unsigned long addr) +{ + register unsigned long _addr1 asm("2") = (unsigned long) addr; + register unsigned long _len1 asm("3") = (unsigned long) MIB; + register unsigned long _addr2 asm("4") = (unsigned long) addr; + register unsigned long _len2 asm("5") = (unsigned long) 0; + unsigned int ipm; + + asm volatile( + "0: clcle %1,%3,0(0)\n" + " jo 0b\n" + " ipm %0\n" + " srl %0,28\n" + : "=d" (ipm), "+d" (_addr1), "+d" (_len1), "+d" (_addr2), "+d" (_len2) + : + : "cc"); + return !ipm; +} + +/* + * Check if the megabyte contains all zeroes. + */ +int is_zero_mb(unsigned long addr) +{ + if (!page_is_valid(addr)) + return 1; + if (__is_zero_mb(addr)) + return 1; + return 0; +} + +/* + * Find next non-zero megabyte in the address range + */ +static unsigned long find_next_non_zero(unsigned long start, unsigned long end) +{ + while (start < end) { + if (!is_zero_mb(start)) + break; + start += MIB; + } + if (start > end) + start = end; + return start; +} + +/* + * Find next zero megabyte in the address range + */ +static unsigned long find_next_zero(unsigned long start, unsigned long end) +{ + while (start < end) { + if (is_zero_mb(start)) + break; + start += MIB; + } + if (start > end) + start = end; + return start; +} + +/* + * Find first continuous set of non-zero megabytes in the specified + * address range and update *dump_segm structure accordingly. The segment's + * length is truncated to max_len if required. Afterwards, look for the next + * non-zero megabyte. Return the address of the next non-zero megabyte or zero + * if no more follow. + */ +unsigned long find_dump_segment(unsigned long start, unsigned long end, + unsigned long max_len, + struct df_s390_dump_segm_hdr *dump_segm) +{ + unsigned long addr, limit; + + memset(dump_segm, 0, sizeof(*dump_segm)); + /* Check the input values for MB alingment */ + if (!IS_ALIGNED(start, MIB) || !IS_ALIGNED(max_len, MIB)) + panic(EINTERNAL, "start or max_len not MB aligned"); + /* Search for dump segment start */ + addr = find_next_non_zero(start, end); + if (addr == end) + goto out; + dump_segm->start = addr; + /* Search for dump segment end */ + limit = end; + if (max_len) + limit = MIN(addr + max_len, end); + addr = find_next_zero(dump_segm->start, limit); + dump_segm->len = addr - dump_segm->start; + /* Search for next dump segment start */ + addr = find_next_non_zero(addr, end); + /* Return the start address of the next segment */ + if (addr < end) + return addr; +out: + /* + * Set the stop-marker indicating no more non-zero + * segments in the address range + */ + dump_segm->stop_marker = -1UL; + return 0; +} + /* * Create IDA list starting at "addr" with "len" bytes */ @@ -113,11 +220,11 @@ static void df_s390_dump_init(void) { struct df_s390_hdr *dh = dump_hdr; - dh->magic = DF_S390_MAGIC; + dh->magic = DF_S390_MAGIC_EXT; dh->hdr_size = DF_S390_HDR_SIZE; dh->page_size = PAGE_SIZE; dh->dump_level = 4; - dh->version = 5; + dh->version = 1; dh->mem_start = 0; dh->arch = DF_S390_ARCH_64; dh->build_arch = DF_S390_ARCH_64; diff --git a/zipl/boot/stage2dump.h b/zipl/boot/stage2dump.h index 8df4bea1..2a7e7fac 100644 --- a/zipl/boot/stage2dump.h +++ b/zipl/boot/stage2dump.h @@ -3,7 +3,7 @@ * * Common dump functions * - * Copyright IBM Corp. 2013, 2017 + * Copyright IBM Corp. 2013, 2018 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -16,6 +16,8 @@ #include "s390.h" #define IPL_SC *((struct subchannel_id *) &S390_lowcore.subchannel_id) +#define ROUND_DOWN(x, a) ((x) & ~((typeof(x))(a) - 1)) +#define IS_ALIGNED(x, a) ~((x) & ((typeof(x))(a) - 1)) /* * zipl parameters passed at tail of dump tools @@ -33,6 +35,7 @@ extern struct stage2dump_parm_tail parm_tail * S390 dump format defines */ #define DF_S390_MAGIC 0xa8190173618f23fdULL +#define DF_S390_MAGIC_EXT 0xa8190173618f23feULL #define DF_S390_HDR_SIZE 0x1000 #define DF_S390_EM_SIZE 16 #define DF_S390_EM_MAGIC 0x44554d505f454e44ULL @@ -85,6 +88,15 @@ struct df_s390_em { uint64_t tod; } __packed __aligned(16); +/* + * Dump segment header + */ +struct df_s390_dump_segm_hdr { + uint64_t start; + uint64_t len; + uint64_t stop_marker; +}; + /* * Linker script defined symbols */ @@ -95,6 +107,7 @@ extern char __stage2_desc[]; * Dump common globals */ extern struct df_s390_hdr *dump_hdr; +extern unsigned long total_dump_size; /* * Common functions @@ -105,6 +118,10 @@ void init_progress_print(void); void progress_print(unsigned long addr); void df_s390_em_page_init(unsigned long page); void pgm_check_handler(void); +int is_zero_mb(unsigned long addr); +unsigned long find_dump_segment(unsigned long start, unsigned long end, + unsigned long max_len, + struct df_s390_dump_segm_hdr *dump_segm); /* * Dump tool backend functions diff --git a/zipl/src/install.c b/zipl/src/install.c index 1a1bc457..a6c43334 100644 --- a/zipl/src/install.c +++ b/zipl/src/install.c @@ -3,7 +3,7 @@ * * Functions handling the installation of the boot loader code onto disk * - * Copyright IBM Corp. 2001, 2017 + * Copyright IBM Corp. 2001, 2018 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -636,7 +636,7 @@ overwrite_partition_start(int fd, struct disk_info* info, int mv_dump_magic) unsigned int bytes = 65536; char* buffer; const char dump_magic[] = {0xa8, 0x19, 0x01, 0x73, - 0x61, 0x8f, 0x23, 0xfd}; + 0x61, 0x8f, 0x23, 0xfe}; if (misc_seek(fd, info->geo.start * info->phy_block_size)) return -1;