powerpc/fadump: register kernel metadata address with opal
authorHari Bathini <hbathini@linux.ibm.com>
Wed, 11 Sep 2019 14:50:57 +0000 (20:20 +0530)
committerMichael Ellerman <mpe@ellerman.id.au>
Fri, 13 Sep 2019 14:04:43 +0000 (00:04 +1000)
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
arch/powerpc/include/asm/fadump-internal.h
arch/powerpc/kernel/fadump.c
arch/powerpc/platforms/powernv/opal-fadump.c
arch/powerpc/platforms/powernv/opal-fadump.h [new file with mode: 0644]

index 8046fe0b742e8dbf47dd8f8f20784217d8e424f2..5262c764c5fc858c218533be8d36708a2199059a 100644 (file)
@@ -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);
index 7d47d4bb7d6e25595f774940d027de050d163d74..7e7056382d9872d84fe999c9dd59fa722447a9d0 100644 (file)
@@ -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();
        }
 
index bbc5356bdc772b3166d82510900cb5ed9fb10381..21de832f2ba44d2c9f21e0773aea9f524213491f 100644 (file)
 #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 (file)
index 0000000..0b83d89
--- /dev/null
@@ -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 */