powerpc/fadump: register kernel metadata address with opal

OPAL allows registering address with it in the first kernel and
retrieving it after MPIPL. Setup kernel metadata and register its
address with OPAL to use it for processing the crash dump.

Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/156821345011.5656.13567765019032928471.stgit@hbathini.in.ibm.com
This commit is contained in:
Hari Bathini 2019-09-11 20:20:57 +05:30 committed by Michael Ellerman
parent 6abec12c65
commit 742a265acc
4 changed files with 146 additions and 4 deletions

View File

@ -97,6 +97,8 @@ struct fw_dump {
unsigned long cpu_notes_buf_vaddr; unsigned long cpu_notes_buf_vaddr;
unsigned long cpu_notes_buf_size; unsigned long cpu_notes_buf_size;
u64 kernel_metadata;
int ibm_configure_kernel_dump; int ibm_configure_kernel_dump;
unsigned long fadump_enabled:1; unsigned long fadump_enabled:1;
@ -110,6 +112,8 @@ struct fw_dump {
struct fadump_ops { struct fadump_ops {
u64 (*fadump_init_mem_struct)(struct fw_dump *fadump_conf); u64 (*fadump_init_mem_struct)(struct fw_dump *fadump_conf);
u64 (*fadump_get_metadata_size)(void);
int (*fadump_setup_metadata)(struct fw_dump *fadump_conf);
int (*fadump_register)(struct fw_dump *fadump_conf); int (*fadump_register)(struct fw_dump *fadump_conf);
int (*fadump_unregister)(struct fw_dump *fadump_conf); int (*fadump_unregister)(struct fw_dump *fadump_conf);
int (*fadump_invalidate)(struct fw_dump *fadump_conf); int (*fadump_invalidate)(struct fw_dump *fadump_conf);

View File

@ -313,6 +313,10 @@ static unsigned long get_fadump_area_size(void)
size += sizeof(struct elf_phdr) * (memblock_num_regions(memory) + 2); size += sizeof(struct elf_phdr) * (memblock_num_regions(memory) + 2);
size = PAGE_ALIGN(size); size = PAGE_ALIGN(size);
/* This is to hold kernel metadata on platforms that support it */
size += (fw_dump.ops->fadump_get_metadata_size ?
fw_dump.ops->fadump_get_metadata_size() : 0);
return size; return size;
} }
@ -348,6 +352,7 @@ int __init fadump_reserve_mem(void)
pr_info("Firmware-Assisted Dump is not supported on this hardware\n"); pr_info("Firmware-Assisted Dump is not supported on this hardware\n");
goto error_out; goto error_out;
} }
/* /*
* Initialize boot memory size * Initialize boot memory size
* If dump is active then we have already calculated the size during * If dump is active then we have already calculated the size during
@ -426,8 +431,21 @@ int __init fadump_reserve_mem(void)
base += size; base += size;
} }
if ((base > (mem_boundary - size)) || if (base > (mem_boundary - size)) {
memblock_reserve(base, size)) { pr_err("Failed to find memory chunk for reservation!\n");
goto error_out;
}
fw_dump.reserve_dump_area_start = base;
/*
* Calculate the kernel metadata address and register it with
* f/w if the platform supports.
*/
if (fw_dump.ops->fadump_setup_metadata &&
(fw_dump.ops->fadump_setup_metadata(&fw_dump) < 0))
goto error_out;
if (memblock_reserve(base, size)) {
pr_err("Failed to reserve memory!\n"); pr_err("Failed to reserve memory!\n");
goto error_out; goto error_out;
} }
@ -435,7 +453,6 @@ int __init fadump_reserve_mem(void)
pr_info("Reserved %lldMB of memory at %#016llx (System RAM: %lldMB)\n", pr_info("Reserved %lldMB of memory at %#016llx (System RAM: %lldMB)\n",
(size >> 20), base, (memblock_phys_mem_size() >> 20)); (size >> 20), base, (memblock_phys_mem_size() >> 20));
fw_dump.reserve_dump_area_start = base;
ret = fadump_cma_init(); ret = fadump_cma_init();
} }

View File

@ -11,13 +11,83 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/of_fdt.h> #include <linux/of_fdt.h>
#include <linux/libfdt.h> #include <linux/libfdt.h>
#include <linux/mm.h>
#include <asm/page.h>
#include <asm/opal.h> #include <asm/opal.h>
#include <asm/fadump-internal.h> #include <asm/fadump-internal.h>
#include "opal-fadump.h"
static struct opal_fadump_mem_struct *opal_fdm;
/* Initialize kernel metadata */
static void opal_fadump_init_metadata(struct opal_fadump_mem_struct *fdm)
{
fdm->version = OPAL_FADUMP_VERSION;
fdm->region_cnt = 0;
fdm->registered_regions = 0;
fdm->fadumphdr_addr = 0;
}
static u64 opal_fadump_init_mem_struct(struct fw_dump *fadump_conf) static u64 opal_fadump_init_mem_struct(struct fw_dump *fadump_conf)
{ {
return fadump_conf->reserve_dump_area_start; u64 addr = fadump_conf->reserve_dump_area_start;
opal_fdm = __va(fadump_conf->kernel_metadata);
opal_fadump_init_metadata(opal_fdm);
opal_fdm->region_cnt = 1;
opal_fdm->rgn[0].src = 0;
opal_fdm->rgn[0].dest = addr;
opal_fdm->rgn[0].size = fadump_conf->boot_memory_size;
addr += fadump_conf->boot_memory_size;
/*
* Kernel metadata is passed to f/w and retrieved in capture kerenl.
* So, use it to save fadump header address instead of calculating it.
*/
opal_fdm->fadumphdr_addr = (opal_fdm->rgn[0].dest +
fadump_conf->boot_memory_size);
return addr;
}
static u64 opal_fadump_get_metadata_size(void)
{
return PAGE_ALIGN(sizeof(struct opal_fadump_mem_struct));
}
static int opal_fadump_setup_metadata(struct fw_dump *fadump_conf)
{
int err = 0;
s64 ret;
/*
* Use the last page(s) in FADump memory reservation for
* kernel metadata.
*/
fadump_conf->kernel_metadata = (fadump_conf->reserve_dump_area_start +
fadump_conf->reserve_dump_area_size -
opal_fadump_get_metadata_size());
pr_info("Kernel metadata addr: %llx\n", fadump_conf->kernel_metadata);
/* Initialize kernel metadata before registering the address with f/w */
opal_fdm = __va(fadump_conf->kernel_metadata);
opal_fadump_init_metadata(opal_fdm);
/*
* Register metadata address with f/w. Can be retrieved in
* the capture kernel.
*/
ret = opal_mpipl_register_tag(OPAL_MPIPL_TAG_KERNEL,
fadump_conf->kernel_metadata);
if (ret != OPAL_SUCCESS) {
pr_err("Failed to set kernel metadata tag!\n");
err = -EPERM;
}
return err;
} }
static int opal_fadump_register(struct fw_dump *fadump_conf) static int opal_fadump_register(struct fw_dump *fadump_conf)
@ -43,6 +113,16 @@ static int __init opal_fadump_process(struct fw_dump *fadump_conf)
static void opal_fadump_region_show(struct fw_dump *fadump_conf, static void opal_fadump_region_show(struct fw_dump *fadump_conf,
struct seq_file *m) struct seq_file *m)
{ {
const struct opal_fadump_mem_struct *fdm_ptr = opal_fdm;
u64 dumped_bytes = 0;
int i;
for (i = 0; i < fdm_ptr->region_cnt; i++) {
seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ",
fdm_ptr->rgn[i].src, fdm_ptr->rgn[i].dest);
seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n",
fdm_ptr->rgn[i].size, dumped_bytes);
}
} }
static void opal_fadump_trigger(struct fadump_crash_info_header *fdh, static void opal_fadump_trigger(struct fadump_crash_info_header *fdh,
@ -60,6 +140,8 @@ static void opal_fadump_trigger(struct fadump_crash_info_header *fdh,
static struct fadump_ops opal_fadump_ops = { static struct fadump_ops opal_fadump_ops = {
.fadump_init_mem_struct = opal_fadump_init_mem_struct, .fadump_init_mem_struct = opal_fadump_init_mem_struct,
.fadump_get_metadata_size = opal_fadump_get_metadata_size,
.fadump_setup_metadata = opal_fadump_setup_metadata,
.fadump_register = opal_fadump_register, .fadump_register = opal_fadump_register,
.fadump_unregister = opal_fadump_unregister, .fadump_unregister = opal_fadump_unregister,
.fadump_invalidate = opal_fadump_invalidate, .fadump_invalidate = opal_fadump_invalidate,

View File

@ -0,0 +1,39 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Firmware-Assisted Dump support on POWER platform (OPAL).
*
* Copyright 2019, Hari Bathini, IBM Corporation.
*/
#ifndef _POWERNV_OPAL_FADUMP_H
#define _POWERNV_OPAL_FADUMP_H
/*
* OPAL FADump metadata structure format version
*
* OPAL FADump kernel metadata structure stores kernel metadata needed to
* register-for/process crash dump. Format version is used to keep a tab on
* the changes in the structure format. The changes, if any, to the format
* are expected to be minimal and backward compatible.
*/
#define OPAL_FADUMP_VERSION 0x1
/* Maximum number of memory regions kernel supports */
#define OPAL_FADUMP_MAX_MEM_REGS 128
/*
* OPAL FADump kernel metadata
*
* The address of this structure will be registered with f/w for retrieving
* and processing during crash dump.
*/
struct opal_fadump_mem_struct {
u8 version;
u8 reserved[3];
u16 region_cnt; /* number of regions */
u16 registered_regions; /* Regions registered for MPIPL */
u64 fadumphdr_addr;
struct opal_mpipl_region rgn[OPAL_FADUMP_MAX_MEM_REGS];
} __packed;
#endif /* _POWERNV_OPAL_FADUMP_H */