diff options
-rw-r--r-- | arch/powerpc/include/asm/fadump-internal.h | 4 | ||||
-rw-r--r-- | arch/powerpc/kernel/fadump.c | 23 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/opal-fadump.c | 84 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/opal-fadump.h | 39 |
4 files changed, 146 insertions, 4 deletions
diff --git a/arch/powerpc/include/asm/fadump-internal.h b/arch/powerpc/include/asm/fadump-internal.h index 8046fe0b742e..5262c764c5fc 100644 --- a/arch/powerpc/include/asm/fadump-internal.h +++ b/arch/powerpc/include/asm/fadump-internal.h @@ -97,6 +97,8 @@ struct fw_dump { unsigned long cpu_notes_buf_vaddr; unsigned long cpu_notes_buf_size; + u64 kernel_metadata; + int ibm_configure_kernel_dump; unsigned long fadump_enabled:1; @@ -110,6 +112,8 @@ struct fw_dump { struct fadump_ops { 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_unregister)(struct fw_dump *fadump_conf); int (*fadump_invalidate)(struct fw_dump *fadump_conf); diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c index 7d47d4bb7d6e..7e7056382d98 100644 --- a/arch/powerpc/kernel/fadump.c +++ b/arch/powerpc/kernel/fadump.c @@ -313,6 +313,10 @@ static unsigned long get_fadump_area_size(void) size += sizeof(struct elf_phdr) * (memblock_num_regions(memory) + 2); 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; } @@ -348,6 +352,7 @@ int __init fadump_reserve_mem(void) pr_info("Firmware-Assisted Dump is not supported on this hardware\n"); goto error_out; } + /* * Initialize boot memory size * If dump is active then we have already calculated the size during @@ -426,8 +431,21 @@ int __init fadump_reserve_mem(void) base += size; } - if ((base > (mem_boundary - size)) || - memblock_reserve(base, size)) { + if (base > (mem_boundary - 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"); 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", (size >> 20), base, (memblock_phys_mem_size() >> 20)); - fw_dump.reserve_dump_area_start = base; ret = fadump_cma_init(); } diff --git a/arch/powerpc/platforms/powernv/opal-fadump.c b/arch/powerpc/platforms/powernv/opal-fadump.c index bbc5356bdc77..21de832f2ba4 100644 --- a/arch/powerpc/platforms/powernv/opal-fadump.c +++ b/arch/powerpc/platforms/powernv/opal-fadump.c @@ -11,13 +11,83 @@ #include <linux/seq_file.h> #include <linux/of_fdt.h> #include <linux/libfdt.h> +#include <linux/mm.h> +#include <asm/page.h> #include <asm/opal.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) { - 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) @@ -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, 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, @@ -60,6 +140,8 @@ static void opal_fadump_trigger(struct fadump_crash_info_header *fdh, static struct fadump_ops opal_fadump_ops = { .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_unregister = opal_fadump_unregister, .fadump_invalidate = opal_fadump_invalidate, diff --git a/arch/powerpc/platforms/powernv/opal-fadump.h b/arch/powerpc/platforms/powernv/opal-fadump.h new file mode 100644 index 000000000000..0b83d895485c --- /dev/null +++ b/arch/powerpc/platforms/powernv/opal-fadump.h @@ -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 */ |