Files
s390-tools/zdump/dfo_s390.c
T
Mikhail Zaslonko 2c5d4073a7 zdump: Drop support of obsolete dumps and dumpers
Drop support of obsolete dump and dump-tool versions (single-volume DASD,
FBA and Tape) in order to simplify zgetdump logic:
- CCW dumpers written in assembler instructions as well as stage2 dumpers
  of size less than 0x3000 (STAGE2_DUMPER_SIZE_V1 or STAGE2_DUMPER_SIZE_V2)
  haven't been used for years. Remove its traces completely as a cleanup.
  Keep the last version (version 5) of non-extended DASD dumper as well as
  newer extended DASD dumpers.
- Rename STAGE2_DUMPER_SIZE_V3 and STAGE2_DUMPER_SIZE_ZLIB constants.
- Drop support of non-extended s390 dumps of version < 5. Dump files
  of s390 format version 5 can be still produced by zgetdump (dfo_s390).
- Drop excessive dump version checking in df_s390_cpu_info_add() and
  df_s390_hdr_add() considering that obsolete s390 dumps of version lower
  than 5 no longer supported.
- Use cpu_cnt field in s390 dump header instead of the s390 dump version to
  indicate no cpu info available (DFI_CPU_CONTENT_NONE) for dfo_s390.
- Make df_s390_dumper_read() return error code upon unknown dumper
  version/magic detection.

Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-04-07 17:42:18 +02:00

272 lines
7.3 KiB
C

/*
* zgetdump - Tool for copying and converting System z dumps
*
* S390 dump output format
*
* Copyright IBM Corp. 2001, 2017
*
* 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 <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include "dump/s390_dump.h"
#include "df_s390.h"
#include "dfi_mem_chunk.h"
#include "dfo_mem_chunk.h"
#include "dfo.h"
/*
* File local static data
*/
static struct {
struct df_s390_hdr hdr;
struct df_s390_em em;
u64 dfi_max_end; // Max end address among processed DFI memory chunks
} l;
/*
* Copy internal register set to 64 bit lowcore
*/
static void cpu2lc_64(void *lc_64, struct dfi_cpu *cpu)
{
struct dfi_lowcore_64 *lc = lc_64;
memcpy(lc->gpregs_save_area, &cpu->gprs, sizeof(cpu->gprs));
memcpy(lc->cregs_save_area, &cpu->ctrs, sizeof(cpu->ctrs));
memcpy(lc->access_regs_save_area, &cpu->acrs, sizeof(cpu->acrs));
memcpy(lc->floating_pt_save_area, &cpu->fprs, sizeof(cpu->fprs));
memcpy(&lc->fpt_creg_save_area, &cpu->fpc, sizeof(cpu->fpc));
memcpy(lc->st_status_fixed_logout, &cpu->psw, sizeof(cpu->psw));
memcpy(&lc->prefixreg_save_area, &cpu->prefix, sizeof(cpu->prefix));
memcpy(lc->timer_save_area, &cpu->timer, sizeof(cpu->timer));
memcpy(lc->clock_comp_save_area, &cpu->todcmp, sizeof(cpu->todcmp));
}
/*
* Copy internal register set to 32 bit lowcore
*/
static void cpu2lc_32(void *lc_32, struct dfi_cpu *cpu_64)
{
struct dfi_lowcore_32 *lc = lc_32;
struct dfi_cpu_32 cpu;
dfi_cpu_64_to_32(&cpu, cpu_64);
memcpy(lc->gpregs_save_area, &cpu.gprs, sizeof(cpu.gprs));
memcpy(lc->cregs_save_area, &cpu.ctrs, sizeof(cpu.ctrs));
memcpy(lc->access_regs_save_area, &cpu.acrs, sizeof(cpu.acrs));
memcpy(lc->floating_pt_save_area, &cpu.fprs, sizeof(cpu.fprs));
memcpy(lc->st_status_fixed_logout, &cpu.psw, sizeof(cpu.psw));
memcpy(&lc->prefixreg_save_area, &cpu.prefix, sizeof(cpu.prefix));
memcpy(lc->timer_save_area, &cpu.timer, sizeof(cpu.timer));
memcpy(lc->clock_comp_save_area, &cpu.todcmp, sizeof(cpu.todcmp));
}
/*
* Convert timeval to s390 TOD clock
*/
static void timeval2tod(uint64_t *tod, struct timeval *xtime)
{
u64 us = xtime->tv_sec * 1000000 + xtime->tv_usec;
*tod = (us << 12);
*tod += 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096);
}
/*
* Setup lowcore array in dump header
*/
static void lc_setup(struct df_s390_hdr *dh)
{
struct dfi_cpu *cpu;
unsigned int i = 0;
dfi_cpu_iterate(cpu) {
if (i >= DF_S390_CPU_MAX)
ERR_EXIT("Too many CPUs in source dump (%i)", i);
dh->lc_vec[i] = cpu->prefix;
i++;
}
}
/*
* Copy register set to prefix page
*/
static void dfo_s390_dump_chunk_lc_fn(struct dfo_chunk *dump_chunk,
u64 off, void *buf, u64 cnt)
{
struct dfi_cpu *cpu = dump_chunk->data;
char lc[0x2000];
if (dfi_mem_virt_read(cpu->prefix + off, &lc[off], cnt))
return;
if (dfi_arch() == DFI_ARCH_64)
cpu2lc_64(lc, cpu);
else
cpu2lc_32(lc, cpu);
memcpy(buf, &lc[off], cnt);
}
/*
* Copy register set to prefix page
*/
static void dfo_s390_dump_chunk_vx_fn(struct dfo_chunk *dump_chunk,
u64 off, void *buf, u64 cnt)
{
char *vx_regs = dump_chunk->data;
memcpy(buf, &vx_regs[off], cnt);
}
/*
* Add register set to dump layout. We copy the register sets to the
* lowcore pages.
*/
static void add_cpu_to_dfo(struct dfi_cpu *cpu)
{
struct dfi_lowcore_64 lc;
void *vx_regs;
if (dfi_cpu_content() != DFI_CPU_CONTENT_ALL)
return;
if (!dfi_mem_range_valid(cpu->prefix, dfi_lc_size(dfi_arch()))) {
STDERR("Info: Could not read CPU prefix page: %x\n",
cpu->prefix);
return;
}
/* Add lowcore to memory */
dfo_chunk_add(cpu->prefix + DF_S390_HDR_SIZE,
dfi_lc_size(dfi_arch()), cpu,
dfo_s390_dump_chunk_lc_fn);
/* Add VX save area to memory */
if (dfi_arch() != DFI_ARCH_64)
return;
if (!dfi_cpu_content_fac_check(DFI_CPU_CONTENT_FAC_VX))
return;
if (dfi_mem_virt_read(cpu->prefix, &lc, sizeof(lc)))
return;
if (!dfi_cpu_lc_has_vx_sa(&lc))
return;
vx_regs = zg_alloc(DFI_VX_SA_SIZE);
dfi_cpu_vx_copy(vx_regs, cpu);
dfo_chunk_add(lc.vector_save_area_addr + DF_S390_HDR_SIZE,
DFI_VX_SA_SIZE, vx_regs, dfo_s390_dump_chunk_vx_fn);
}
/*
* Add memory chunk to DFO dump layout.
* The function is supposed to be called in a loop for each DFI mem chunk in the list.
* DFI memory chunk list is considered to be sorted by start address, thus the
* input memory chunk coming last has a priority for DFO.
*/
static void add_mem_chunk_to_dfo(struct dfi_mem_chunk *mem_chunk)
{
const u64 dfo_current_memsz = dfo_chunk_dump_size() - DF_S390_HDR_SIZE;
/*
* Consider memory holes and filtered memory pages filling the missing
* areas with zeroes for s390 output format. Keep in mind that dfi_mem_chunks
* can overlap for VR-kernel dumps.
* If no dfi_mem_chunk with zero start address registered, add
* zero DFO memory chunk first.
*
* See the diagram of resulting dfo_s390 for several cases of input memory
* chunks layout below:
*
* DFI DFO(s390)
* ---------------------------------
* aaaa.... HDR|aa|bb|cc|cc
* ..bbbb..
* ....cccc
* ---------------------------------
* ..aa.... HDR|00|aa|00|bb
* ......bb
* ---------------------------------
* aaaaaaaa HDR|aa|bb|aa|cc
* ..bb....
* ......cc
*/
if (l.dfi_max_end < mem_chunk->start &&
mem_chunk->start - l.dfi_max_end > 1)
dfo_chunk_add(dfo_chunk_dump_size(),
mem_chunk->start - dfo_current_memsz,
NULL, dfo_chunk_zero_fn);
dfo_chunk_add(mem_chunk->start + DF_S390_HDR_SIZE, mem_chunk->size,
mem_chunk, dfo_chunk_mem_fn);
l.dfi_max_end = MAX(mem_chunk->end, l.dfi_max_end);
}
/*
* Setup dump chunks
*/
static void dump_chunks_init(void)
{
struct dfi_mem_chunk *mem_chunk;
struct dfi_cpu *cpu;
dfo_chunk_add(0, DF_S390_HDR_SIZE, &l.hdr, dfo_chunk_buf_fn);
dfi_mem_chunk_iterate(mem_chunk)
add_mem_chunk_to_dfo(mem_chunk);
dfi_cpu_iterate(cpu)
add_cpu_to_dfo(cpu);
dfo_chunk_add(l.hdr.mem_size + DF_S390_HDR_SIZE,
DF_S390_EM_SIZE,
&l.em, dfo_chunk_buf_fn);
}
/*
* Initialize s390 output dump format
*/
static void df_s390_dump_init(void)
{
struct df_s390_hdr *dh = &l.hdr;
struct df_s390_em *em = &l.em;
dh->magic = DF_S390_MAGIC;
dh->hdr_size = DF_S390_HDR_SIZE;
dh->page_size = PAGE_SIZE;
dh->dump_level = 4;
dh->version = 5;
dh->mem_start = 0;
dh->mem_end = dfi_mem_end();
dh->mem_size = dh->mem_end + 1;
dh->num_pages = dh->mem_size / PAGE_SIZE;
dh->arch = df_s390_from_dfi_arch(dfi_arch());
if (dfi_attr_build_arch())
dh->build_arch = df_s390_from_dfi_arch(*dfi_attr_build_arch());
dh->cpu_cnt = dfi_cpu_cnt();
if (dfi_cpu_content() == DFI_CPU_CONTENT_NONE)
dh->cpu_cnt = 0;
if (dfi_attr_real_cpu_cnt())
dh->real_cpu_cnt = *dfi_attr_real_cpu_cnt();
if (dfi_attr_cpu_id())
dh->cpu_id = *dfi_attr_cpu_id();
if (dfi_attr_mem_size_real())
dh->mem_size_real = *dfi_attr_mem_size_real();
if (dfi_attr_time()) {
timeval2tod(&dh->tod, dfi_attr_time());
timeval2tod(&em->tod, dfi_attr_time());
}
if (dfi_attr_time_end())
timeval2tod(&em->tod, dfi_attr_time_end());
lc_setup(dh);
memcpy(em->str, DF_S390_EM_STR, sizeof(em->str));
dump_chunks_init();
}
/*
* S390 DFO operations
*/
struct dfo dfo_s390 = {
.name = "s390",
.init = df_s390_dump_init,
};