Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1a2c5840 authored by Martin Schwidefsky's avatar Martin Schwidefsky
Browse files

s390/dump: cleanup CPU save area handling



Introduce save_area_alloc(), save_area_boot_cpu(), save_area_add_regs()
and save_area_add_vxrs to deal with storing the CPU state in case of a
system dump. Remove struct save_area and save_area_ext, and create a new
struct save_area as a local definition to arch/s390/kernel/crash_dump.c.
Copy each individual field from the hardware status area to the save area,
storing the minimum of required data.

Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 1a36a39e
Loading
Loading
Loading
Loading
+5 −6
Original line number Diff line number Diff line
@@ -88,12 +88,11 @@ struct ipl_parameter_block {
 */
extern u32 ipl_flags;

struct dump_save_areas {
	struct save_area_ext **areas;
	int count;
};

extern struct dump_save_areas dump_save_areas;
struct save_area;
struct save_area * __init save_area_alloc(bool is_boot_cpu);
struct save_area * __init save_area_boot_cpu(void);
void __init save_area_add_regs(struct save_area *, void *regs);
void __init save_area_add_vxrs(struct save_area *, __vector128 *vxrs);

extern void do_reipl(void);
extern void do_halt(void);
+0 −21
Original line number Diff line number Diff line
@@ -16,27 +16,6 @@
#define LC_ORDER 1
#define LC_PAGES 2

struct save_area {
	u64	fp_regs[16];
	u64	gp_regs[16];
	u8	psw[16];
	u8	pad1[8];
	u32	pref_reg;
	u32	fp_ctrl_reg;
	u8	pad2[4];
	u32	tod_reg;
	u64	timer;
	u64	clk_cmp;
	u8	pad3[8];
	u32	acc_regs[16];
	u64	ctrl_regs[16];
} __packed;

struct save_area_ext {
	struct save_area	sa;
	__vector128		vx_regs[32];
};

struct _lowcore {
	__u8	pad_0x0000[0x0014-0x0000];	/* 0x0000 */
	__u32	ipl_parmblock_ptr;		/* 0x0014 */
+121 −142
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include <linux/slab.h>
#include <linux/bootmem.h>
#include <linux/elf.h>
#include <asm/asm-offsets.h>
#include <linux/memblock.h>
#include <asm/os_info.h>
#include <asm/elf.h>
@@ -32,7 +33,84 @@ static struct memblock_type oldmem_type = {
	.regions = &oldmem_region,
};

struct dump_save_areas dump_save_areas;
struct save_area {
	struct list_head list;
	u64 psw[2];
	u64 ctrs[16];
	u64 gprs[16];
	u32 acrs[16];
	u64 fprs[16];
	u32 fpc;
	u32 prefix;
	u64 todpreg;
	u64 timer;
	u64 todcmp;
	u64 vxrs_low[16];
	__vector128 vxrs_high[16];
};

static LIST_HEAD(dump_save_areas);

/*
 * Allocate a save area
 */
struct save_area * __init save_area_alloc(bool is_boot_cpu)
{
	struct save_area *sa;

	sa = (void *) memblock_alloc(sizeof(*sa), 8);
	if (!sa)
		return NULL;
	if (is_boot_cpu)
		list_add(&sa->list, &dump_save_areas);
	else
		list_add_tail(&sa->list, &dump_save_areas);
	return sa;
}

/*
 * Return the address of the save area for the boot CPU
 */
struct save_area * __init save_area_boot_cpu(void)
{
	if (list_empty(&dump_save_areas))
		return NULL;
	return list_first_entry(&dump_save_areas, struct save_area, list);
}

/*
 * Copy CPU registers into the save area
 */
void __init save_area_add_regs(struct save_area *sa, void *regs)
{
	struct _lowcore *lc;

	lc = (struct _lowcore *)(regs - __LC_FPREGS_SAVE_AREA);
	memcpy(&sa->psw, &lc->psw_save_area, sizeof(sa->psw));
	memcpy(&sa->ctrs, &lc->cregs_save_area, sizeof(sa->ctrs));
	memcpy(&sa->gprs, &lc->gpregs_save_area, sizeof(sa->gprs));
	memcpy(&sa->acrs, &lc->access_regs_save_area, sizeof(sa->acrs));
	memcpy(&sa->fprs, &lc->floating_pt_save_area, sizeof(sa->fprs));
	memcpy(&sa->fpc, &lc->fpt_creg_save_area, sizeof(sa->fpc));
	memcpy(&sa->prefix, &lc->prefixreg_save_area, sizeof(sa->prefix));
	memcpy(&sa->todpreg, &lc->tod_progreg_save_area, sizeof(sa->todpreg));
	memcpy(&sa->timer, &lc->cpu_timer_save_area, sizeof(sa->timer));
	memcpy(&sa->todcmp, &lc->clock_comp_save_area, sizeof(sa->todcmp));
}

/*
 * Copy vector registers into the save area
 */
void __init save_area_add_vxrs(struct save_area *sa, __vector128 *vxrs)
{
	int i;

	/* Copy lower halves of vector registers 0-15 */
	for (i = 0; i < 16; i++)
		memcpy(&sa->vxrs_low[i], &vxrs[i].u[2], 8);
	/* Copy vector registers 16-31 */
	memcpy(sa->vxrs_high, vxrs + 16, 16 * sizeof(__vector128));
}

/*
 * Return physical address for virtual address
@@ -232,7 +310,7 @@ static void *kzalloc_panic(int len)
/*
 * Initialize ELF note
 */
static void *nt_init(void *buf, Elf64_Word type, void *desc, int d_len,
static void *nt_init_name(void *buf, Elf64_Word type, void *desc, int d_len,
			  const char *name)
{
	Elf64_Nhdr *note;
@@ -253,137 +331,42 @@ static void *nt_init(void *buf, Elf64_Word type, void *desc, int d_len,
	return PTR_ADD(buf, len);
}

/*
 * Initialize prstatus note
 */
static void *nt_prstatus(void *ptr, struct save_area *sa)
static inline void *nt_init(void *buf, Elf64_Word type, void *desc, int d_len)
{
	struct elf_prstatus nt_prstatus;
	static int cpu_nr = 1;

	memset(&nt_prstatus, 0, sizeof(nt_prstatus));
	memcpy(&nt_prstatus.pr_reg.gprs, sa->gp_regs, sizeof(sa->gp_regs));
	memcpy(&nt_prstatus.pr_reg.psw, sa->psw, sizeof(sa->psw));
	memcpy(&nt_prstatus.pr_reg.acrs, sa->acc_regs, sizeof(sa->acc_regs));
	nt_prstatus.pr_pid = cpu_nr;
	cpu_nr++;

	return nt_init(ptr, NT_PRSTATUS, &nt_prstatus, sizeof(nt_prstatus),
			 "CORE");
	return nt_init_name(buf, type, desc, d_len, KEXEC_CORE_NOTE_NAME);
}

/*
 * Initialize fpregset (floating point) note
 * Fill ELF notes for one CPU with save area registers
 */
static void *nt_fpregset(void *ptr, struct save_area *sa)
static void *fill_cpu_elf_notes(void *ptr, int cpu, struct save_area *sa)
{
	struct elf_prstatus nt_prstatus;
	elf_fpregset_t nt_fpregset;

	/* Prepare prstatus note */
	memset(&nt_prstatus, 0, sizeof(nt_prstatus));
	memcpy(&nt_prstatus.pr_reg.gprs, sa->gprs, sizeof(sa->gprs));
	memcpy(&nt_prstatus.pr_reg.psw, sa->psw, sizeof(sa->psw));
	memcpy(&nt_prstatus.pr_reg.acrs, sa->acrs, sizeof(sa->acrs));
	nt_prstatus.pr_pid = cpu;
	/* Prepare fpregset (floating point) note */
	memset(&nt_fpregset, 0, sizeof(nt_fpregset));
	memcpy(&nt_fpregset.fpc, &sa->fp_ctrl_reg, sizeof(sa->fp_ctrl_reg));
	memcpy(&nt_fpregset.fprs, &sa->fp_regs, sizeof(sa->fp_regs));

	return nt_init(ptr, NT_PRFPREG, &nt_fpregset, sizeof(nt_fpregset),
		       "CORE");
}

/*
 * Initialize timer note
 */
static void *nt_s390_timer(void *ptr, struct save_area *sa)
{
	return nt_init(ptr, NT_S390_TIMER, &sa->timer, sizeof(sa->timer),
			 KEXEC_CORE_NOTE_NAME);
}

/*
 * Initialize TOD clock comparator note
 */
static void *nt_s390_tod_cmp(void *ptr, struct save_area *sa)
{
	return nt_init(ptr, NT_S390_TODCMP, &sa->clk_cmp,
		       sizeof(sa->clk_cmp), KEXEC_CORE_NOTE_NAME);
}

/*
 * Initialize TOD programmable register note
 */
static void *nt_s390_tod_preg(void *ptr, struct save_area *sa)
{
	return nt_init(ptr, NT_S390_TODPREG, &sa->tod_reg,
		       sizeof(sa->tod_reg), KEXEC_CORE_NOTE_NAME);
}

/*
 * Initialize control register note
 */
static void *nt_s390_ctrs(void *ptr, struct save_area *sa)
{
	return nt_init(ptr, NT_S390_CTRS, &sa->ctrl_regs,
		       sizeof(sa->ctrl_regs), KEXEC_CORE_NOTE_NAME);
}

/*
 * Initialize prefix register note
 */
static void *nt_s390_prefix(void *ptr, struct save_area *sa)
{
	return nt_init(ptr, NT_S390_PREFIX, &sa->pref_reg,
			 sizeof(sa->pref_reg), KEXEC_CORE_NOTE_NAME);
}

/*
 * Initialize vxrs high note (full 128 bit VX registers 16-31)
 */
static void *nt_s390_vx_high(void *ptr, __vector128 *vx_regs)
{
	return nt_init(ptr, NT_S390_VXRS_HIGH, &vx_regs[16],
		       16 * sizeof(__vector128), KEXEC_CORE_NOTE_NAME);
}

/*
 * Initialize vxrs low note (lower halves of VX registers 0-15)
 */
static void *nt_s390_vx_low(void *ptr, __vector128 *vx_regs)
{
	Elf64_Nhdr *note;
	u64 len;
	int i;

	note = (Elf64_Nhdr *)ptr;
	note->n_namesz = strlen(KEXEC_CORE_NOTE_NAME) + 1;
	note->n_descsz = 16 * 8;
	note->n_type = NT_S390_VXRS_LOW;
	len = sizeof(Elf64_Nhdr);

	memcpy(ptr + len, KEXEC_CORE_NOTE_NAME, note->n_namesz);
	len = roundup(len + note->n_namesz, 4);

	ptr += len;
	/* Copy lower halves of SIMD registers 0-15 */
	for (i = 0; i < 16; i++) {
		memcpy(ptr, &vx_regs[i].u[2], 8);
		ptr += 8;
	}
	return ptr;
}

/*
 * Fill ELF notes for one CPU with save area registers
 */
static void *fill_cpu_elf_notes(void *ptr, struct save_area *sa,
				__vector128 *vx_regs)
{
	ptr = nt_prstatus(ptr, sa);
	ptr = nt_fpregset(ptr, sa);
	ptr = nt_s390_timer(ptr, sa);
	ptr = nt_s390_tod_cmp(ptr, sa);
	ptr = nt_s390_tod_preg(ptr, sa);
	ptr = nt_s390_ctrs(ptr, sa);
	ptr = nt_s390_prefix(ptr, sa);
	if (MACHINE_HAS_VX && vx_regs) {
		ptr = nt_s390_vx_low(ptr, vx_regs);
		ptr = nt_s390_vx_high(ptr, vx_regs);
	memcpy(&nt_fpregset.fpc, &sa->fpc, sizeof(sa->fpc));
	memcpy(&nt_fpregset.fprs, &sa->fprs, sizeof(sa->fprs));
	/* Create ELF notes for the CPU */
	ptr = nt_init(ptr, NT_PRSTATUS, &nt_prstatus, sizeof(nt_prstatus));
	ptr = nt_init(ptr, NT_PRFPREG, &nt_fpregset, sizeof(nt_fpregset));
	ptr = nt_init(ptr, NT_S390_TIMER, &sa->timer, sizeof(sa->timer));
	ptr = nt_init(ptr, NT_S390_TODCMP, &sa->todcmp, sizeof(sa->todcmp));
	ptr = nt_init(ptr, NT_S390_TODPREG, &sa->todpreg, sizeof(sa->todpreg));
	ptr = nt_init(ptr, NT_S390_CTRS, &sa->ctrs, sizeof(sa->ctrs));
	ptr = nt_init(ptr, NT_S390_PREFIX, &sa->prefix, sizeof(sa->prefix));
	if (MACHINE_HAS_VX) {
		ptr = nt_init(ptr, NT_S390_VXRS_HIGH,
			      &sa->vxrs_high, sizeof(sa->vxrs_high));
		ptr = nt_init(ptr, NT_S390_VXRS_LOW,
			      &sa->vxrs_low, sizeof(sa->vxrs_low));
	}
	return ptr;
}
@@ -398,8 +381,7 @@ static void *nt_prpsinfo(void *ptr)
	memset(&prpsinfo, 0, sizeof(prpsinfo));
	prpsinfo.pr_sname = 'R';
	strcpy(prpsinfo.pr_fname, "vmlinux");
	return nt_init(ptr, NT_PRPSINFO, &prpsinfo, sizeof(prpsinfo),
		       KEXEC_CORE_NOTE_NAME);
	return nt_init(ptr, NT_PRPSINFO, &prpsinfo, sizeof(prpsinfo));
}

/*
@@ -441,7 +423,7 @@ static void *nt_vmcoreinfo(void *ptr)
		vmcoreinfo = get_vmcoreinfo_old(&size);
	if (!vmcoreinfo)
		return ptr;
	return nt_init(ptr, 0, vmcoreinfo, size, "VMCOREINFO");
	return nt_init_name(ptr, 0, vmcoreinfo, size, "VMCOREINFO");
}

/*
@@ -470,13 +452,12 @@ static void *ehdr_init(Elf64_Ehdr *ehdr, int mem_chunk_cnt)
 */
static int get_cpu_cnt(void)
{
	int i, cpus = 0;
	struct save_area *sa;
	int cpus = 0;

	for (i = 0; i < dump_save_areas.count; i++) {
		if (dump_save_areas.areas[i]->sa.pref_reg == 0)
			continue;
	list_for_each_entry(sa, &dump_save_areas, list)
		if (sa->prefix != 0)
			cpus++;
	}
	return cpus;
}

@@ -521,18 +502,16 @@ static void loads_init(Elf64_Phdr *phdr, u64 loads_offset)
 */
static void *notes_init(Elf64_Phdr *phdr, void *ptr, u64 notes_offset)
{
	struct save_area_ext *sa_ext;
	struct save_area *sa;
	void *ptr_start = ptr;
	int i;
	int cpu;

	ptr = nt_prpsinfo(ptr);

	for (i = 0; i < dump_save_areas.count; i++) {
		sa_ext = dump_save_areas.areas[i];
		if (sa_ext->sa.pref_reg == 0)
			continue;
		ptr = fill_cpu_elf_notes(ptr, &sa_ext->sa, sa_ext->vx_regs);
	}
	cpu = 1;
	list_for_each_entry(sa, &dump_save_areas, list)
		if (sa->prefix != 0)
			ptr = fill_cpu_elf_notes(ptr, cpu++, sa);
	ptr = nt_vmcoreinfo(ptr);
	memset(phdr, 0, sizeof(*phdr));
	phdr->p_type = PT_NOTE;
+14 −23
Original line number Diff line number Diff line
@@ -585,7 +585,7 @@ int smp_store_status(int cpu)
 *    This case does not exist for s390 anymore, setup_arch explicitly
 *    deactivates the elfcorehdr= kernel parameter
 */
static __init void smp_save_cpu_vxrs(struct save_area_ext *sa_ext, u16 addr,
static __init void smp_save_cpu_vxrs(struct save_area *sa, u16 addr,
				     bool is_boot_cpu, unsigned long page)
{
	__vector128 *vxrs = (__vector128 *) page;
@@ -594,10 +594,10 @@ static __init void smp_save_cpu_vxrs(struct save_area_ext *sa_ext, u16 addr,
		vxrs = boot_cpu_vector_save_area;
	else
		__pcpu_sigp_relax(addr, SIGP_STORE_ADDITIONAL_STATUS, page);
	memcpy(&sa_ext->vx_regs, vxrs, sizeof(sa_ext->vx_regs));
	save_area_add_vxrs(sa, vxrs);
}

static __init void smp_save_cpu_regs(struct save_area_ext *sa_ext, u16 addr,
static __init void smp_save_cpu_regs(struct save_area *sa, u16 addr,
				     bool is_boot_cpu, unsigned long page)
{
	void *regs = (void *) page;
@@ -606,13 +606,13 @@ static __init void smp_save_cpu_regs(struct save_area_ext *sa_ext, u16 addr,
		copy_oldmem_kernel(regs, (void *) __LC_FPREGS_SAVE_AREA, 512);
	else
		__pcpu_sigp_relax(addr, SIGP_STORE_STATUS_AT_ADDRESS, page);
	memcpy(&sa_ext->sa, regs, sizeof(sa_ext->sa));
	save_area_add_regs(sa, regs);
}

void __init smp_save_dump_cpus(void)
{
	int addr, cpu, boot_cpu_addr, max_cpu_addr;
	struct save_area_ext *sa_ext;
	int addr, boot_cpu_addr, max_cpu_addr;
	struct save_area *sa;
	unsigned long page;
	bool is_boot_cpu;

@@ -625,29 +625,20 @@ void __init smp_save_dump_cpus(void)
		panic("could not allocate memory for save area\n");
	/* Set multi-threading state to the previous system. */
	pcpu_set_smt(sclp.mtid_prev);
	max_cpu_addr = SCLP_MAX_CORES << sclp.mtid_prev;
	for (cpu = 0, addr = 0; addr <= max_cpu_addr; addr++) {
		if (__pcpu_sigp_relax(addr, SIGP_SENSE, 0) ==
		    SIGP_CC_NOT_OPERATIONAL)
			continue;
		cpu += 1;
	}
	dump_save_areas.areas = (void *)memblock_alloc(sizeof(void *) * cpu, 8);
	dump_save_areas.count = cpu;
	boot_cpu_addr = stap();
	for (cpu = 0, addr = 0; addr <= max_cpu_addr; addr++) {
	max_cpu_addr = SCLP_MAX_CORES << sclp.mtid_prev;
	for (addr = 0; addr <= max_cpu_addr; addr++) {
		if (__pcpu_sigp_relax(addr, SIGP_SENSE, 0) ==
		    SIGP_CC_NOT_OPERATIONAL)
			continue;
		sa_ext = (void *) memblock_alloc(sizeof(*sa_ext), 8);
		dump_save_areas.areas[cpu] = sa_ext;
		if (!sa_ext)
			panic("could not allocate memory for save area\n");
		is_boot_cpu = (addr == boot_cpu_addr);
		cpu += 1;
		/* Allocate save area */
		sa = save_area_alloc(is_boot_cpu);
		if (!sa)
			panic("could not allocate memory for save area\n");
		if (MACHINE_HAS_VX)
			/* Get the vector registers */
			smp_save_cpu_vxrs(sa_ext, addr, is_boot_cpu, page);
			smp_save_cpu_vxrs(sa, addr, is_boot_cpu, page);
		/*
		 * For a zfcp dump OLDMEM_BASE == NULL and the registers
		 * of the boot CPU are stored in the HSA. To retrieve
@@ -656,7 +647,7 @@ void __init smp_save_dump_cpus(void)
		 */
		if (!is_boot_cpu || OLDMEM_BASE)
			/* Get the CPU registers */
			smp_save_cpu_regs(sa_ext, addr, is_boot_cpu, page);
			smp_save_cpu_regs(sa, addr, is_boot_cpu, page);
	}
	memblock_free(page, PAGE_SIZE);
	diag308_reset();
+5 −6
Original line number Diff line number Diff line
@@ -117,18 +117,17 @@ int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count)

static int __init init_cpu_info(void)
{
	struct save_area_ext *sa_ext;
	struct save_area *sa;

	/* get info for boot cpu from lowcore, stored in the HSA */

	sa_ext = dump_save_areas.areas[0];
	if (!sa_ext)
	sa = save_area_boot_cpu();
	if (!sa)
		return -ENOMEM;
	if (memcpy_hsa_kernel(&sa_ext->sa, __LC_FPREGS_SAVE_AREA,
			      sizeof(struct save_area)) < 0) {
	if (memcpy_hsa_kernel(hsa_buf, __LC_FPREGS_SAVE_AREA, 512) < 0) {
		TRACE("could not copy from HSA\n");
		return -EIO;
	}
	save_area_add_regs(sa, hsa_buf); /* vx registers are saved in smp.c */
	return 0;
}