/* * zipl - zSeries Initial Program Loader tool * * Single-volume FBA DASD dump tool * * 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. */ #include "error.h" #include "fba.h" #include "stage2dump.h" #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"))) = 0x5844464241363401ULL; /* XDFBA64, version 1 */ /* * FBA dump device partition specification */ static struct { unsigned int blk_start; unsigned int blk_end; } device; /* * ORB / IRB */ static struct orb orb = { .intparm = 0x0049504c, /* Interruption Parameter */ .fmt = 0x1, /* Use format 1 CCWs */ .c64 = 0x1, /* Use IDAs */ .lpm = 0xff, /* Logical path mask */ }; static struct irb irb; /* * Data for Locate Record CCW */ static struct LO_fba_data lodata = { .operation = { .cmd = 0x1, }, }; /* * Data for Define Extend CCW */ static struct DE_fba_data dedata = { .mask = { .perm = 0x3, }, .blk_size = 0x200, }; /* * CCW program and IDA list */ static struct { struct ccw1 deccw; struct ccw1 loccw; struct ccw1 wrccw; unsigned long ida_list[BLK_PWRT * BLK_SIZE / 4096]; } ccw_program __aligned(8); /* * FBA parameter block passed by zipl */ struct fba_dump_param { uint32_t res1; uint32_t blk_start; uint32_t res2; uint32_t blk_end; } __packed; /* * Convert memory size to number of blocks */ static inline unsigned long m2b(unsigned long mem) { return mem / BLK_SIZE; } /* * Convert number of blocks to memory size */ static inline unsigned long b2m(unsigned long blk) { return blk * BLK_SIZE; } /* * Get device characteristics from zipl parameter block */ void dt_device_parm_setup(void) { struct fba_dump_param *param = (void *) __stage2_desc; device.blk_start = param->blk_start; device.blk_end = param->blk_end; } /* * Enable DASD device */ void dt_device_enable(void) { io_irq_enable(); set_device(IPL_SC, ENABLED); } /* * Write memory with number of blocks to start block */ static void writeblock_fba(unsigned long blk, unsigned long addr, unsigned long blk_count, unsigned long zero_page) { unsigned long blk_end; blk_end = blk + blk_count; if (blk_end >= device.blk_end) panic(EMEM, "Device too small"); ccw_program.wrccw.count = b2m(blk_count); lodata.blk_ct = blk_count; lodata.blk_nr = blk; create_ida_list(ccw_program.ida_list, b2m(blk_count), addr, zero_page); 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 */ static void ccw_program_init(void) { ccw_program.deccw.cmd_code = DASD_FBA_CCW_DE; ccw_program.deccw.flags = CCW_FLAG_CC; ccw_program.deccw.count = 0x0010; ccw_program.deccw.cda = __pa32(&dedata); ccw_program.loccw.cmd_code = DASD_FBA_CCW_LOCATE; ccw_program.loccw.flags = CCW_FLAG_CC; ccw_program.loccw.count = 0x0008; ccw_program.loccw.cda = __pa32(&lodata); ccw_program.wrccw.cmd_code = DASD_FBA_CCW_WRITE; ccw_program.wrccw.flags = CCW_FLAG_IDA | CCW_FLAG_SLI; ccw_program.wrccw.cda = __pa32(ccw_program.ida_list); orb.cpa = __pa32(&ccw_program); dedata.ext_end = device.blk_end; } /* * Dump all memory to DASD partition */ void dt_dump_mem(void) { 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_fba(blk, __pa(dump_hdr), m2b(DF_S390_HDR_SIZE), 0); blk += m2b(DF_S390_HDR_SIZE); /* Write memory */ 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_fba(blk, page, 1, 0); free_page(page); free_page(__pa(dump_segm)); }