mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
The build_arch field in s390 DASD dump header has originally been used to indicate whether the dump tool has been built on s390 or s390x system. Since no other architectures but s390x are supported for Linux on z, do not process build_arch attribute. Bail out if any build architecture other than ARCH_64 has been detected in s390_ext or s390mv_ext DASD dump header. Remove build architecture line from 'zgetdump -i' output: Build arch.........: s390x (64 bit) The man file for zgetdump is updated accordingly. 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>
246 lines
6.3 KiB
C
246 lines
6.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));
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
cpu2lc_64(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, LOWCORE_SIZE)) {
|
|
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,
|
|
LOWCORE_SIZE, cpu,
|
|
dfo_s390_dump_chunk_lc_fn);
|
|
/* Add VX save area to memory */
|
|
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_ARCH_64;
|
|
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,
|
|
};
|