Files
s390-tools/zipl/boot/stage2dump.c
Mikhail Zaslonko 2363269c1c zipl/boot: Set the new version in the dumper and in the dump header
Since we are using the existing s390 extended dump format for compressed
dumps as well, set the version of the s390_ext dumper with compression
support and also dump header of the compressed dump to '2' (in order for
zgetdump to distinguish).

Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Acked-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2023-09-27 18:29:06 +02:00

473 lines
12 KiB
C

/*
* zipl - zSeries Initial Program Loader tool
*
* Common functions for stand-alone dump tools
*
* Copyright IBM Corp. 2013, 2023
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <stdarg.h>
#include "lib/zt_common.h"
#include "boot/error.h"
#include "dump/s390_dump.h"
#include "libc.h"
#include "sclp.h"
#include "stage2dump.h"
#include "boot/ipl.h"
#include "boot/os_info.h"
#include "boot/s390.h"
#define CPU_ADDRESS_MAX 1000
#define MACHINE_HAS_VX machine_has_vx
/*
* Static globals
*/
static uint8_t machine_has_vx;
/*
* Tail parameters
*/
struct stage2dump_parm_tail parm_tail = {
.mem_upper_limit = 0xffffffffffffffffULL,
};
/*
* Globals
*/
struct df_s390_hdr *dump_hdr;
unsigned long total_dump_size;
/*
* Init dumper: Allocate standard pages, set timers
*/
static void init_early(void)
{
/* Allocate dump header */
dump_hdr = (void *) get_zeroed_page();
/* Set clock comparator and CPU timer to future */
set_clock_comparator(0xffffffffffffffffULL);
set_cpu_timer(0x7fffffffffffffffULL);
if (test_facility(129))
machine_has_vx = 1;
}
/*
* Print progress message
*/
void progress_print(unsigned long addr)
{
/* Print a message approximately every 5 sec */
static unsigned long delta = 5 * (1UL << 32);
static unsigned long next;
unsigned long time = get_tod_clock();
if (next == 0) {
next = time + delta;
return;
}
if (time < next && addr != dump_hdr->mem_size)
return;
printf("%08lu / %08lu MB (Dump file size %08lu MB)", addr >> 20,
dump_hdr->mem_size >> 20, total_dump_size >> 20);
next = time + delta;
}
/*
* 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
*/
void create_ida_list(unsigned long *list, int len, unsigned long addr,
unsigned long zero_page)
{
unsigned long ida_addr;
while (len > 0) {
if (zero_page)
ida_addr = page_is_valid(addr) ? addr : zero_page;
else
ida_addr = addr;
*list = ida_addr;
list++;
addr += PAGE_SIZE;
len -= PAGE_SIZE;
}
}
/*
* Initialize s390 dump header
*/
static void df_s390_dump_init(void)
{
struct df_s390_hdr *dh = dump_hdr;
dh->magic = DF_S390_MAGIC_EXT;
dh->hdr_size = DF_S390_HDR_SIZE;
dh->page_size = PAGE_SIZE;
dh->dump_level = 4;
dh->version = 2;
dh->mem_start = 0;
dh->arch = DF_S390_ARCH_64;
dh->build_arch = DF_S390_ARCH_64;
get_cpu_id((struct cpuid *) &dh->cpu_id);
dh->tod = get_tod_clock();
dh->volnr = 0;
dh->zlib_version_s390 = 0;
/*
* Ignore compression if --no-compress attribute has been specified
* or DFLTCC facility not available.
*/
if (test_facility(DFLTCC_FACILITY) &&
parm_tail.no_compress == 0) {
dh->zlib_version_s390 = 1; /* Indicate interlnal Zlib version */
dh->zlib_entry_size = DUMP_SEGM_ZLIB_ENTSIZE;
}
}
/*
* Initialize page with end marker
*/
void df_s390_em_page_init(unsigned long addr)
{
struct df_s390_em *em = (void *) addr;
em->magic = DF_S390_EM_MAGIC;
em->tod = get_tod_clock();
}
/*
* Find out memory size
*/
static void mem_and_cpu_init(void)
{
unsigned long mem_size_max, mem_size, addr, rnmax, rzm;
struct read_info_sccb *sccb;
unsigned int mtid_prev;
sccb = (void *) get_zeroed_page();
/* Get memory max */
if (sclp_read_info(sccb))
panic(EMEMCOUNT, "Could not evaluate memory layout");
rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2;
rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2;
rzm <<= 20;
mem_size_max = rnmax * rzm;
mem_size = 0;
/* Find out real memory end without standby memory */
for (addr = 0; addr < mem_size_max; addr += rzm) {
if (!page_is_valid(addr))
continue;
mem_size = addr + rzm;
}
/* Restore maximum thread ID of previous system */
mtid_prev = (sccb->fac42 & 0x80) ? (sccb->fac66 & 31) : 0;
sigp(0, SIGP_SET_MULTI_THREADING, mtid_prev, NULL);
free_page((unsigned long) sccb);
dump_hdr->mem_size_real = mem_size;
/* Check if we have an upper limit */
if (mem_size > parm_tail.mem_upper_limit) {
printf("Using memory limit");
mem_size = parm_tail.mem_upper_limit;
}
dump_hdr->mem_size = dump_hdr->mem_end = mem_size;
dump_hdr->num_pages = dump_hdr->mem_size >> 12;
}
/*
* Copy 64 bit lowcore after store status
*/
static void copy_lowcore_64(void)
{
char *real_cpu_cnt_ptr = ((char *) &dump_hdr->mem_size_real) + 11;
char *cpu_cnt_ptr = ((char *) &dump_hdr->mem_size_real) + 9;
unsigned long prefix;
uint16_t cpu_cnt = 0;
prefix = S390_lowcore.prefixreg_save_area;
/* Need memcpy because of aligment problem of members */
memcpy(&cpu_cnt, real_cpu_cnt_ptr, sizeof(cpu_cnt));
cpu_cnt++;
memcpy(real_cpu_cnt_ptr, &cpu_cnt, sizeof(cpu_cnt));
if (prefix < 0x10000) /* if < linux-start addr */
return;
if (prefix % 0x1000) /* check page alignment */
return;
/* Save lowcore pointer (32 bit) in dump header */
memcpy(&cpu_cnt, cpu_cnt_ptr, sizeof(cpu_cnt));
dump_hdr->lc_vec[cpu_cnt] = prefix;
cpu_cnt++;
memcpy(cpu_cnt_ptr, &cpu_cnt, sizeof(cpu_cnt));
/*
* |-----------------------------------------------------------|
* | Decimal | Length | Data |
* | Address | in Bytes | |
* |_________|___________|_____________________________________|
* | 163 | 1 | Architectural Mode ID |
* | 4608 | 128 | Fl-pt registers 0-15 |
* | 4736 | 128 | General registers 0-15 |
* | 4864 | 16 | Current PSW |
* | 4888 | 4 | Prefix register |
* | 4892 | 4 | Fl-pt control register |
* | 4900 | 4 | TOD programmable register |
* | 4904 | 8 | CPU timer |
* | 4912 | 1 | Zeros |
* | 4913 | 7 | Bits 0-55 of clock comparator |
* | 4928 | 64 | Access registers 0-15 |
* | 4992 | 128 | Control registers 0-15 |
* |_________|___________|_____________________________________|
*/
memcpy((void *) prefix + 4608, (void *) 4608, 272);
memcpy((void *) prefix + 4888, (void *) 4888, 8);
memcpy((void *) prefix + 4900, (void *) 4900, 20);
memcpy((void *) prefix + 4928, (void *) 4928, 192);
}
/*
* Has machine VX and has kernel prepared save area?
*/
static int should_dump_vx(struct _lowcore *lc)
{
unsigned long vx_sa;
if (!MACHINE_HAS_VX)
return 0;
vx_sa = lc->vector_save_area_addr;
if (vx_sa == 0 || (vx_sa % 1024) != 0)
return 0;
if (!page_is_valid(vx_sa))
return 0;
return 1;
}
/*
* Do store status
*/
static void store_status(void)
{
unsigned short current_cpu;
struct _lowcore *lc;
unsigned long page;
int addr, cc;
/* Save absolute zero lowcore */
page = get_zeroed_page();
memcpy((void *) page, (void *) 0x1000, PAGE_SIZE);
current_cpu = stap();
lc = (void *)(unsigned long) S390_lowcore.prefixreg_save_area;
if (should_dump_vx(lc))
save_vx_regs_safe((__vector128 *) lc->vector_save_area_addr);
copy_lowcore_64();
for (addr = 0; addr < CPU_ADDRESS_MAX; addr++) {
if (addr == current_cpu)
continue;
cc = sigp_busy(addr, SIGP_STOP_AND_STORE_STATUS, 0, NULL);
if (cc != SIGP_CC_ORDER_CODE_ACCEPTED)
continue;
lc = (void *)(unsigned long) S390_lowcore.prefixreg_save_area;
copy_lowcore_64();
if (!should_dump_vx(lc))
continue;
sigp_busy(addr, SIGP_STORE_ASTATUS_AT_ADDRESS,
lc->vector_save_area_addr, NULL);
}
/* Restore absolute zero lowcore */
memcpy((void *) 0x1000, (void *) page, PAGE_SIZE);
free_page(page);
}
/*
* Perform reipl: check lowcore for the address of an IPL Information
* Block followed by a valid checksum (as defined in lowcore.h and set
* by ipl.c). In case of match use diag308 to IPL.
*/
static __noreturn void dump_exit(unsigned long code)
{
struct ipl_parameter_block *ipib = (struct ipl_parameter_block *)S390_lowcore.ipib;
const struct os_info *os_info = (struct os_info *)S390_lowcore.os_info;
const struct os_info_entry *os_info_entry = NULL;
unsigned long os_info_flags = 0;
uint32_t csum;
if (!ipib)
libc_stop(code);
csum = csum_partial(ipib, ipib->hdr.len, 0);
/* Verify ipib checksum against the value stored in lowcore */
if (csum != S390_lowcore.ipib_checksum)
libc_stop(code);
diag308(DIAG308_SET, ipib);
/*
* Check os_info flags entry for REIPL_CLEAR flag and make
* diag308 call with a proper subcode.
* Verify the bit flags field size before accessing it.
*/
if (os_info_check(os_info) == 0) {
os_info_entry = &os_info->entry[OS_INFO_FLAGS_ENTRY];
if (os_info_entry_is_valid(os_info_entry) &&
os_info_entry->size >= OS_INFO_FLAGS_ENTRY_SIZE)
os_info_flags = *((unsigned long *)os_info_entry->addr);
else
printf("Warning: os_info flags entry is missing or corrupted");
} else {
printf("Warning: os_info is missing or corrupted");
}
if (os_info_flags & OS_INFO_FLAG_REIPL_CLEAR)
diag308(DIAG308_LOAD_CLEAR, NULL);
/* Use special diag308 subcode for CCW normal ipl */
if (ipib->pb0_hdr.pbt == IPL_PBT_CCW)
diag308(DIAG308_LOAD_NORMAL_DUMP, NULL);
else
diag308(DIAG308_LOAD_NORMAL, NULL);
__builtin_unreachable();
}
/*
* Print message and exit dumper
*/
void panic_notify(unsigned long code)
{
printf("Dump failed");
dump_exit(code);
}
/*
* Create stand-alone dump
*/
void __noreturn start(void)
{
init_early();
dt_device_parm_setup();
sclp_setup(SCLP_INIT);
/* Store facility-list for future checks */
stfle(S390_lowcore.stfle_fac_list,
ARRAY_SIZE(S390_lowcore.stfle_fac_list));
dt_device_enable();
df_s390_dump_init();
printf("zIPL v%s dump tool (64 bit)", RELEASE_STRING);
printf("Dumping 64 bit OS");
if (parm_tail.no_compress)
printf("Dump compression prevented by the --no-compress attribute");
mem_and_cpu_init();
store_status();
dt_dump_mem();
printf("Dump successful");
dump_exit(0);
}