zipl: Extend DASD stand-alone dumpers to drop zero pages

Update zipl DASD standalone dumpers for single-volume, multi-volume and FBA
to drop the sets of zero pages when dumping memory to disk. Thus, all
continuous sets of zero-megabytes in the memory are dropped during the dump
stage.

Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Mikhail Zaslonko
2015-12-03 08:47:25 +01:00
committed by Jan Höppner
parent 54519b4465
commit ac0ac48f6f
8 changed files with 341 additions and 95 deletions

View File

@@ -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
*/

View File

@@ -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 */

View File

@@ -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));
}

View File

@@ -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));
}

View File

@@ -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));
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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;