mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Since memory chunks can overlap for vr-kernel dumps stored in elf format (ngdump, zfcpdump), we need to consider this when converting to s390 dump output format. For that sort DFI memory chunks by start address and adjust dfo_s390 logic for identifying memory gaps. Otherwise we might end up with bogus DFO memory chunks being created. 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>
273 lines
7.3 KiB
C
273 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;
|
|
if (dfi_cpu_content() == DFI_CPU_CONTENT_NONE)
|
|
dh->version = 4;
|
|
else
|
|
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_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,
|
|
};
|