| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| From: Hari Bathini <hbathini@linux.ibm.com> |
| Date: Wed, 9 Oct 2019 10:45:34 +0530 |
| Subject: [PATCH 4/8] powerpc/fadump: add support to preserve crash data on |
| FADUMP disabled kernel |
| |
| Add a new kernel config option, CONFIG_PRESERVE_FA_DUMP that ensures |
| that crash data, from previously crash'ed kernel, is preserved. This |
| helps in cases where FADump is not enabled but the subsequent memory |
| preserving kernel boot is likely to process this crash data. One |
| typical usecase for this config option is petitboot kernel. |
| |
| As OPAL allows registering address with it in the first kernel and |
| retrieving it after MPIPL, use it to store the top of boot memory. |
| A kernel that intends to preserve crash data retrieves it and avoids |
| using memory beyond this address. |
| |
| Move arch_reserved_kernel_pages() function as it is needed for both |
| FA_DUMP and PRESERVE_FA_DUMP configurations. |
| |
| This is the backport of upstream commit bec53196adf4 ("powerpc/fadump: |
| add support to preserve crash data on FADUMP disabled kernel"). |
| |
| Also, OPAL FADump metadata definition from upstream commit 742a265accd3 |
| ("powerpc/fadump: register kernel metadata address with opal") is |
| backported here for completeness. |
| |
| Signed-off-by: Hari Bathini <hbathini@linux.ibm.com> |
| Signed-off-by: Joel Stanley <joel@jms.id.au> |
| --- |
| arch/powerpc/Kconfig | 9 +++ |
| arch/powerpc/include/asm/fadump.h | 17 ++++- |
| arch/powerpc/kernel/Makefile | 4 +- |
| arch/powerpc/kernel/fadump.c | 123 ++++++++++++++++++++++++++++-- |
| arch/powerpc/kernel/prom.c | 4 +- |
| 5 files changed, 146 insertions(+), 11 deletions(-) |
| |
| diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig |
| index d8dcd8820369..a286c203e732 100644 |
| --- a/arch/powerpc/Kconfig |
| +++ b/arch/powerpc/Kconfig |
| @@ -579,6 +579,15 @@ config FA_DUMP |
| |
| If unsure, say "N" |
| |
| +config PRESERVE_FA_DUMP |
| + bool "Preserve Firmware-assisted dump" |
| + depends on PPC64 && PPC_POWERNV && !FA_DUMP |
| + help |
| + On a kernel with FA_DUMP disabled, this option helps to preserve |
| + crash data from a previously crash'ed kernel. Useful when the next |
| + memory preserving kernel boot would process this crash data. |
| + Petitboot kernel is the typical usecase for this option. |
| + |
| config IRQ_ALL_CPUS |
| bool "Distribute interrupts on all CPUs by default" |
| depends on SMP |
| diff --git a/arch/powerpc/include/asm/fadump.h b/arch/powerpc/include/asm/fadump.h |
| index 17d9b6acaf63..0401a6ffde0c 100644 |
| --- a/arch/powerpc/include/asm/fadump.h |
| +++ b/arch/powerpc/include/asm/fadump.h |
| @@ -193,9 +193,6 @@ struct fad_crash_memory_ranges { |
| }; |
| |
| extern int is_fadump_memory_area(u64 addr, ulong size); |
| -extern int early_init_dt_scan_fw_dump(unsigned long node, |
| - const char *uname, int depth, void *data); |
| -extern int fadump_reserve_mem(void); |
| extern int setup_fadump(void); |
| extern int is_fadump_active(void); |
| extern int should_fadump_crash(void); |
| @@ -208,4 +205,18 @@ static inline int should_fadump_crash(void) { return 0; } |
| static inline void crash_fadump(struct pt_regs *regs, const char *str) { } |
| static inline void fadump_cleanup(void) { } |
| #endif |
| + |
| +#ifdef CONFIG_PRESERVE_FA_DUMP |
| +/* Firmware-assisted dump configuration details. */ |
| +struct fw_dump { |
| + u64 boot_mem_top; |
| + u64 dump_active; |
| +}; |
| +#endif |
| + |
| +#if defined(CONFIG_FA_DUMP) || defined(CONFIG_PRESERVE_FA_DUMP) |
| +extern int early_init_dt_scan_fw_dump(unsigned long node, const char *uname, |
| + int depth, void *data); |
| +extern int fadump_reserve_mem(void); |
| +#endif |
| #endif |
| diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile |
| index 56dfa7a2a6f2..ce70e7776cfd 100644 |
| --- a/arch/powerpc/kernel/Makefile |
| +++ b/arch/powerpc/kernel/Makefile |
| @@ -78,7 +78,9 @@ obj-$(CONFIG_EEH) += eeh.o eeh_pe.o eeh_dev.o eeh_cache.o \ |
| eeh_driver.o eeh_event.o eeh_sysfs.o |
| obj-$(CONFIG_GENERIC_TBSYNC) += smp-tbsync.o |
| obj-$(CONFIG_CRASH_DUMP) += crash_dump.o |
| -obj-$(CONFIG_FA_DUMP) += fadump.o |
| +ifneq ($(CONFIG_FA_DUMP)$(CONFIG_PRESERVE_FA_DUMP),) |
| +obj-y += fadump.o |
| +endif |
| ifdef CONFIG_PPC32 |
| obj-$(CONFIG_E500) += idle_e500.o |
| endif |
| diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c |
| index 4dacce3fac72..1202dadf1d58 100644 |
| --- a/arch/powerpc/kernel/fadump.c |
| +++ b/arch/powerpc/kernel/fadump.c |
| @@ -24,11 +24,13 @@ |
| #include <linux/slab.h> |
| #include <linux/cma.h> |
| #include <linux/hugetlb.h> |
| +#include <linux/libfdt.h> |
| |
| #include <asm/debugfs.h> |
| #include <asm/page.h> |
| #include <asm/prom.h> |
| #include <asm/rtas.h> |
| +#include <asm/opal.h> |
| #include <asm/fadump.h> |
| #include <asm/setup.h> |
| |
| @@ -36,6 +38,7 @@ static struct fw_dump fw_dump; |
| |
| static void __init fadump_reserve_crash_area(u64 base); |
| |
| +#ifndef CONFIG_PRESERVE_FA_DUMP |
| static struct fadump_mem_struct fdm; |
| static const struct fadump_mem_struct *fdm_active; |
| #ifdef CONFIG_CMA |
| @@ -542,11 +545,6 @@ int __init fadump_reserve_mem(void) |
| return 1; |
| } |
| |
| -unsigned long __init arch_reserved_kernel_pages(void) |
| -{ |
| - return memblock_reserved_size() / PAGE_SIZE; |
| -} |
| - |
| /* Look for fadump= cmdline option. */ |
| static int __init early_fadump_param(char *p) |
| { |
| @@ -1684,6 +1682,116 @@ int __init setup_fadump(void) |
| return 1; |
| } |
| subsys_initcall(setup_fadump); |
| +#else /* !CONFIG_PRESERVE_FA_DUMP */ |
| + |
| +/* 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; |
| + |
| + |
| +/* |
| + * When dump is active but PRESERVE_FA_DUMP is enabled on the kernel, |
| + * ensure crash data is preserved in hope that the subsequent memory |
| + * preserving kernel boot is going to process this crash data. |
| + */ |
| +void __init opal_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node) |
| +{ |
| + const struct opal_fadump_mem_struct *opal_fdm_active; |
| + const __be32 *prop; |
| + unsigned long dn; |
| + u64 addr = 0; |
| + s64 ret; |
| + |
| + dn = of_get_flat_dt_subnode_by_name(node, "dump"); |
| + if (dn == -FDT_ERR_NOTFOUND) |
| + return; |
| + |
| + /* |
| + * Check if dump has been initiated on last reboot. |
| + */ |
| + prop = of_get_flat_dt_prop(dn, "mpipl-boot", NULL); |
| + if (!prop) |
| + return; |
| + |
| + ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_KERNEL, &addr); |
| + if ((ret != OPAL_SUCCESS) || !addr) { |
| + pr_debug("Could not get Kernel metadata (%lld)\n", ret); |
| + return; |
| + } |
| + |
| + /* |
| + * Preserve memory only if kernel memory regions are registered |
| + * with f/w for MPIPL. |
| + */ |
| + addr = be64_to_cpu(addr); |
| + pr_debug("Kernel metadata addr: %llx\n", addr); |
| + opal_fdm_active = (void *)addr; |
| + if (opal_fdm_active->registered_regions == 0) |
| + return; |
| + |
| + ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_BOOT_MEM, &addr); |
| + if ((ret != OPAL_SUCCESS) || !addr) { |
| + pr_err("Failed to get boot memory tag (%lld)\n", ret); |
| + return; |
| + } |
| + |
| + /* |
| + * Memory below this address can be used for booting a |
| + * capture kernel or petitboot kernel. Preserve everything |
| + * above this address for processing crashdump. |
| + */ |
| + fadump_conf->boot_mem_top = be64_to_cpu(addr); |
| + pr_debug("Preserve everything above %llx\n", fadump_conf->boot_mem_top); |
| + |
| + pr_info("Firmware-assisted dump is active.\n"); |
| + fadump_conf->dump_active = 1; |
| +} |
| + |
| +/* Scan the Firmware Assisted dump configuration details. */ |
| +int __init early_init_dt_scan_fw_dump(unsigned long node, const char *uname, |
| + int depth, void *data) |
| +{ |
| + if ((depth != 1) || (strcmp(uname, "ibm,opal") != 0)) |
| + return 0; |
| + |
| + opal_fadump_dt_scan(&fw_dump, node); |
| + return 1; |
| +} |
| + |
| +/* |
| + * When dump is active but PRESERVE_FA_DUMP is enabled on the kernel, |
| + * preserve crash data. The subsequent memory preserving kernel boot |
| + * is likely to process this crash data. |
| + */ |
| +int __init fadump_reserve_mem(void) |
| +{ |
| + if (fw_dump.dump_active) { |
| + /* |
| + * If last boot has crashed then reserve all the memory |
| + * above boot memory to preserve crash data. |
| + */ |
| + pr_info("Preserving crash data for processing in next boot.\n"); |
| + fadump_reserve_crash_area(fw_dump.boot_mem_top); |
| + } else |
| + pr_debug("FADump-aware kernel..\n"); |
| + |
| + return 1; |
| +} |
| +#endif /* CONFIG_PRESERVE_FA_DUMP */ |
| |
| /* Preserve everything above the base address */ |
| static void __init fadump_reserve_crash_area(u64 base) |
| @@ -1708,3 +1816,8 @@ static void __init fadump_reserve_crash_area(u64 base) |
| memblock_reserve(mstart, msize); |
| } |
| } |
| + |
| +unsigned long __init arch_reserved_kernel_pages(void) |
| +{ |
| + return memblock_reserved_size() / PAGE_SIZE; |
| +} |
| diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c |
| index 7159e791a70d..9c3861bda216 100644 |
| --- a/arch/powerpc/kernel/prom.c |
| +++ b/arch/powerpc/kernel/prom.c |
| @@ -704,7 +704,7 @@ void __init early_init_devtree(void *params) |
| of_scan_flat_dt(early_init_dt_scan_opal, NULL); |
| #endif |
| |
| -#ifdef CONFIG_FA_DUMP |
| +#if defined(CONFIG_FA_DUMP) || defined(CONFIG_PRESERVE_FA_DUMP) |
| /* scan tree to see if dump is active during last boot */ |
| of_scan_flat_dt(early_init_dt_scan_fw_dump, NULL); |
| #endif |
| @@ -731,7 +731,7 @@ void __init early_init_devtree(void *params) |
| if (PHYSICAL_START > MEMORY_START) |
| memblock_reserve(MEMORY_START, 0x8000); |
| reserve_kdump_trampoline(); |
| -#ifdef CONFIG_FA_DUMP |
| +#if defined(CONFIG_FA_DUMP) || defined(CONFIG_PRESERVE_FA_DUMP) |
| /* |
| * If we fail to reserve memory for firmware-assisted dump then |
| * fallback to kexec based kdump. |