On 2019-07-16 17:02:45 Tue, Hari Bathini wrote:
> Move code that supports processing the crash'ed kernel's memory
> preserved by firmware to platform specific callback functions.
> 
> Signed-off-by: Hari Bathini <hbath...@linux.ibm.com>
> ---
>  arch/powerpc/kernel/fadump-common.h          |    6 
>  arch/powerpc/kernel/fadump.c                 |  340 
> +-------------------------
>  arch/powerpc/platforms/pseries/rtas-fadump.c |  278 +++++++++++++++++++++
>  3 files changed, 299 insertions(+), 325 deletions(-)
> 

Reviewed-by: Mahesh Salgaonkar <mah...@linux.vnet.ibm.com>

Thanks,
-Mahesh.

> diff --git a/arch/powerpc/kernel/fadump-common.h 
> b/arch/powerpc/kernel/fadump-common.h
> index 273247d..0231a0b 100644
> --- a/arch/powerpc/kernel/fadump-common.h
> +++ b/arch/powerpc/kernel/fadump-common.h
> @@ -100,6 +100,12 @@ struct fw_dump {
>       /* cmd line option during boot */
>       unsigned long   reserve_bootvar;
>  
> +     /*
> +      * Start address of preserve area. This memory is reserved
> +      * permanently (production or capture kernel) for FADump.
> +      */
> +     unsigned long   preserv_area_start;
> +
>       unsigned long   cpu_state_data_size;
>       unsigned long   hpte_region_size;
>       unsigned long   boot_memory_size;
> diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c
> index 650ebf8..e995db1 100644
> --- a/arch/powerpc/kernel/fadump.c
> +++ b/arch/powerpc/kernel/fadump.c
> @@ -28,15 +28,12 @@
>  #include <asm/debugfs.h>
>  #include <asm/page.h>
>  #include <asm/prom.h>
> -#include <asm/rtas.h>
>  #include <asm/fadump.h>
>  #include <asm/setup.h>
>  
>  #include "fadump-common.h"
> -#include "../platforms/pseries/rtas-fadump.h"
>  
>  static struct fw_dump fw_dump;
> -static const struct rtas_fadump_mem_struct *fdm_active;
>  
>  static DEFINE_MUTEX(fadump_mutex);
>  struct fad_crash_memory_ranges *crash_memory_ranges;
> @@ -111,22 +108,13 @@ static int __init fadump_cma_init(void) { return 1; }
>  int __init early_init_dt_scan_fw_dump(unsigned long node, const char *uname,
>                                     int depth, void *data)
>  {
> -     int ret;
> -
> -     if (depth != 1 || strcmp(uname, "rtas") != 0)
> +     if (depth != 1)
>               return 0;
>  
> -     ret = rtas_fadump_dt_scan(&fw_dump, node);
> +     if (strcmp(uname, "rtas") == 0)
> +             return rtas_fadump_dt_scan(&fw_dump, node);
>  
> -     /*
> -      * The 'ibm,kernel-dump' rtas node is present only if there is
> -      * dump data waiting for us.
> -      */
> -     fdm_active = of_get_flat_dt_prop(node, "ibm,kernel-dump", NULL);
> -     if (fdm_active)
> -             fw_dump.dump_active = 1;
> -
> -     return ret;
> +     return 0;
>  }
>  
>  /*
> @@ -308,9 +296,7 @@ int __init fadump_reserve_mem(void)
>        * If dump is active then we have already calculated the size during
>        * first kernel.
>        */
> -     if (fdm_active)
> -             fw_dump.boot_memory_size = 
> be64_to_cpu(fdm_active->rmr_region.source_len);
> -     else {
> +     if (!fw_dump.dump_active) {
>               fw_dump.boot_memory_size = fadump_calculate_reserve_size();
>  #ifdef CONFIG_CMA
>               if (!fw_dump.nocma)
> @@ -320,6 +306,7 @@ int __init fadump_reserve_mem(void)
>  #endif
>       }
>  
> +     size = get_fadump_area_size();
>       if (memory_limit)
>               memory_boundary = memory_limit;
>       else
> @@ -346,15 +333,10 @@ int __init fadump_reserve_mem(void)
>               size = memory_boundary - base;
>               fadump_reserve_crash_area(base, size);
>  
> -             fw_dump.fadumphdr_addr =
> -                             
> be64_to_cpu(fdm_active->rmr_region.destination_address) +
> -                             be64_to_cpu(fdm_active->rmr_region.source_len);
> -             pr_debug("fadumphdr_addr = %pa\n", &fw_dump.fadumphdr_addr);
> +             pr_debug("fadumphdr_addr = %#016lx\n", fw_dump.fadumphdr_addr);
>               fw_dump.reserve_dump_area_start = base;
>               fw_dump.reserve_dump_area_size = size;
>       } else {
> -             size = get_fadump_area_size();
> -
>               /*
>                * Reserve memory at an offset closer to bottom of the RAM to
>                * minimize the impact of memory hot-remove operation. We can't
> @@ -469,218 +451,6 @@ void crash_fadump(struct pt_regs *regs, const char *str)
>       fw_dump.ops->fadump_trigger(fdh, str);
>  }
>  
> -#define GPR_MASK     0xffffff0000000000
> -static inline int fadump_gpr_index(u64 id)
> -{
> -     int i = -1;
> -     char str[3];
> -
> -     if ((id & GPR_MASK) == fadump_str_to_u64("GPR")) {
> -             /* get the digits at the end */
> -             id &= ~GPR_MASK;
> -             id >>= 24;
> -             str[2] = '\0';
> -             str[1] = id & 0xff;
> -             str[0] = (id >> 8) & 0xff;
> -             sscanf(str, "%d", &i);
> -             if (i > 31)
> -                     i = -1;
> -     }
> -     return i;
> -}
> -
> -static inline void fadump_set_regval(struct pt_regs *regs, u64 reg_id,
> -                                                             u64 reg_val)
> -{
> -     int i;
> -
> -     i = fadump_gpr_index(reg_id);
> -     if (i >= 0)
> -             regs->gpr[i] = (unsigned long)reg_val;
> -     else if (reg_id == fadump_str_to_u64("NIA"))
> -             regs->nip = (unsigned long)reg_val;
> -     else if (reg_id == fadump_str_to_u64("MSR"))
> -             regs->msr = (unsigned long)reg_val;
> -     else if (reg_id == fadump_str_to_u64("CTR"))
> -             regs->ctr = (unsigned long)reg_val;
> -     else if (reg_id == fadump_str_to_u64("LR"))
> -             regs->link = (unsigned long)reg_val;
> -     else if (reg_id == fadump_str_to_u64("XER"))
> -             regs->xer = (unsigned long)reg_val;
> -     else if (reg_id == fadump_str_to_u64("CR"))
> -             regs->ccr = (unsigned long)reg_val;
> -     else if (reg_id == fadump_str_to_u64("DAR"))
> -             regs->dar = (unsigned long)reg_val;
> -     else if (reg_id == fadump_str_to_u64("DSISR"))
> -             regs->dsisr = (unsigned long)reg_val;
> -}
> -
> -static struct rtas_fadump_reg_entry*
> -fadump_read_registers(struct rtas_fadump_reg_entry *reg_entry, struct 
> pt_regs *regs)
> -{
> -     memset(regs, 0, sizeof(struct pt_regs));
> -
> -     while (be64_to_cpu(reg_entry->reg_id) != fadump_str_to_u64("CPUEND")) {
> -             fadump_set_regval(regs, be64_to_cpu(reg_entry->reg_id),
> -                                     be64_to_cpu(reg_entry->reg_value));
> -             reg_entry++;
> -     }
> -     reg_entry++;
> -     return reg_entry;
> -}
> -
> -/*
> - * Read CPU state dump data and convert it into ELF notes.
> - * The CPU dump starts with magic number "REGSAVE". NumCpusOffset should be
> - * used to access the data to allow for additional fields to be added without
> - * affecting compatibility. Each list of registers for a CPU starts with
> - * "CPUSTRT" and ends with "CPUEND". Each register entry is of 16 bytes,
> - * 8 Byte ASCII identifier and 8 Byte register value. The register entry
> - * with identifier "CPUSTRT" and "CPUEND" contains 4 byte cpu id as part
> - * of register value. For more details refer to PAPR document.
> - *
> - * Only for the crashing cpu we ignore the CPU dump data and get exact
> - * state from fadump crash info structure populated by first kernel at the
> - * time of crash.
> - */
> -static int __init fadump_build_cpu_notes(const struct rtas_fadump_mem_struct 
> *fdm)
> -{
> -     struct rtas_fadump_reg_save_area_header *reg_header;
> -     struct rtas_fadump_reg_entry *reg_entry;
> -     struct fadump_crash_info_header *fdh = NULL;
> -     void *vaddr;
> -     unsigned long addr;
> -     u32 num_cpus, *note_buf;
> -     struct pt_regs regs;
> -     int i, rc = 0, cpu = 0;
> -
> -     if (!fdm->cpu_state_data.bytes_dumped)
> -             return -EINVAL;
> -
> -     addr = be64_to_cpu(fdm->cpu_state_data.destination_address);
> -     vaddr = __va(addr);
> -
> -     reg_header = vaddr;
> -     if (be64_to_cpu(reg_header->magic_number) !=
> -         fadump_str_to_u64("REGSAVE")) {
> -             printk(KERN_ERR "Unable to read register save area.\n");
> -             return -ENOENT;
> -     }
> -     pr_debug("--------CPU State Data------------\n");
> -     pr_debug("Magic Number: %llx\n", be64_to_cpu(reg_header->magic_number));
> -     pr_debug("NumCpuOffset: %x\n", be32_to_cpu(reg_header->num_cpu_offset));
> -
> -     vaddr += be32_to_cpu(reg_header->num_cpu_offset);
> -     num_cpus = be32_to_cpu(*((__be32 *)(vaddr)));
> -     pr_debug("NumCpus     : %u\n", num_cpus);
> -     vaddr += sizeof(u32);
> -     reg_entry = (struct rtas_fadump_reg_entry *)vaddr;
> -
> -     /* Allocate buffer to hold cpu crash notes. */
> -     fw_dump.cpu_notes_buf_size = num_cpus * sizeof(note_buf_t);
> -     fw_dump.cpu_notes_buf_size = PAGE_ALIGN(fw_dump.cpu_notes_buf_size);
> -     note_buf = fadump_cpu_notes_buf_alloc(fw_dump.cpu_notes_buf_size);
> -     if (!note_buf) {
> -             printk(KERN_ERR "Failed to allocate 0x%lx bytes for "
> -                     "cpu notes buffer\n", fw_dump.cpu_notes_buf_size);
> -             return -ENOMEM;
> -     }
> -     fw_dump.cpu_notes_buf = __pa(note_buf);
> -
> -     pr_debug("Allocated buffer for cpu notes of size %ld at %p\n",
> -                     (num_cpus * sizeof(note_buf_t)), note_buf);
> -
> -     if (fw_dump.fadumphdr_addr)
> -             fdh = __va(fw_dump.fadumphdr_addr);
> -
> -     for (i = 0; i < num_cpus; i++) {
> -             if (be64_to_cpu(reg_entry->reg_id) != 
> fadump_str_to_u64("CPUSTRT")) {
> -                     printk(KERN_ERR "Unable to read CPU state data\n");
> -                     rc = -ENOENT;
> -                     goto error_out;
> -             }
> -             /* Lower 4 bytes of reg_value contains logical cpu id */
> -             cpu = be64_to_cpu(reg_entry->reg_value) & 
> RTAS_FADUMP_CPU_ID_MASK;
> -             if (fdh && !cpumask_test_cpu(cpu, &fdh->online_mask)) {
> -                     RTAS_FADUMP_SKIP_TO_NEXT_CPU(reg_entry);
> -                     continue;
> -             }
> -             pr_debug("Reading register data for cpu %d...\n", cpu);
> -             if (fdh && fdh->crashing_cpu == cpu) {
> -                     regs = fdh->regs;
> -                     note_buf = fadump_regs_to_elf_notes(note_buf, &regs);
> -                     RTAS_FADUMP_SKIP_TO_NEXT_CPU(reg_entry);
> -             } else {
> -                     reg_entry++;
> -                     reg_entry = fadump_read_registers(reg_entry, &regs);
> -                     note_buf = fadump_regs_to_elf_notes(note_buf, &regs);
> -             }
> -     }
> -     final_note(note_buf);
> -
> -     if (fdh) {
> -             addr = fdh->elfcorehdr_addr;
> -             pr_debug("Updating elfcore header(%lx) with cpu notes\n", addr);
> -             fadump_update_elfcore_header(&fw_dump, (char *)__va(addr));
> -     }
> -     return 0;
> -
> -error_out:
> -     fadump_cpu_notes_buf_free((unsigned long)__va(fw_dump.cpu_notes_buf),
> -                                     fw_dump.cpu_notes_buf_size);
> -     fw_dump.cpu_notes_buf = 0;
> -     fw_dump.cpu_notes_buf_size = 0;
> -     return rc;
> -
> -}
> -
> -/*
> - * Validate and process the dump data stored by firmware before exporting
> - * it through '/proc/vmcore'.
> - */
> -static int __init process_fadump(const struct rtas_fadump_mem_struct 
> *fdm_active)
> -{
> -     struct fadump_crash_info_header *fdh;
> -     int rc = 0;
> -
> -     if (!fdm_active || !fw_dump.fadumphdr_addr)
> -             return -EINVAL;
> -
> -     /* Check if the dump data is valid. */
> -     if ((be16_to_cpu(fdm_active->header.dump_status_flag) == 
> RTAS_FADUMP_ERROR_FLAG) ||
> -                     (fdm_active->cpu_state_data.error_flags != 0) ||
> -                     (fdm_active->rmr_region.error_flags != 0)) {
> -             printk(KERN_ERR "Dump taken by platform is not valid\n");
> -             return -EINVAL;
> -     }
> -     if ((fdm_active->rmr_region.bytes_dumped !=
> -                     fdm_active->rmr_region.source_len) ||
> -                     !fdm_active->cpu_state_data.bytes_dumped) {
> -             printk(KERN_ERR "Dump taken by platform is incomplete\n");
> -             return -EINVAL;
> -     }
> -
> -     /* Validate the fadump crash info header */
> -     fdh = __va(fw_dump.fadumphdr_addr);
> -     if (fdh->magic_number != FADUMP_CRASH_INFO_MAGIC) {
> -             printk(KERN_ERR "Crash info header is not valid.\n");
> -             return -EINVAL;
> -     }
> -
> -     rc = fadump_build_cpu_notes(fdm_active);
> -     if (rc)
> -             return rc;
> -
> -     /*
> -      * We are done validating dump info and elfcore header is now ready
> -      * to be exported. set elfcorehdr_addr so that vmcore module will
> -      * export the elfcore header through '/proc/vmcore'.
> -      */
> -     elfcorehdr_addr = fdh->elfcorehdr_addr;
> -
> -     return 0;
> -}
> -
>  static void free_crash_memory_ranges(void)
>  {
>       kfree(crash_memory_ranges);
> @@ -970,7 +740,6 @@ static unsigned long init_fadump_header(unsigned long 
> addr)
>       if (!addr)
>               return 0;
>  
> -     fw_dump.fadumphdr_addr = addr;
>       fdh = __va(addr);
>       addr += sizeof(struct fadump_crash_info_header);
>  
> @@ -1014,39 +783,12 @@ static int register_fadump(void)
>       return fw_dump.ops->register_fadump(&fw_dump);
>  }
>  
> -static int fadump_invalidate_dump(const struct rtas_fadump_mem_struct *fdm)
> -{
> -     int rc = 0;
> -     unsigned int wait_time;
> -
> -     pr_debug("Invalidating firmware-assisted dump registration\n");
> -
> -     /* TODO: Add upper time limit for the delay */
> -     do {
> -             rc = rtas_call(fw_dump.ibm_configure_kernel_dump, 3, 1, NULL,
> -                     FADUMP_INVALIDATE, fdm,
> -                     sizeof(struct rtas_fadump_mem_struct));
> -
> -             wait_time = rtas_busy_delay_time(rc);
> -             if (wait_time)
> -                     mdelay(wait_time);
> -     } while (wait_time);
> -
> -     if (rc) {
> -             pr_err("Failed to invalidate firmware-assisted dump 
> registration. Unexpected error (%d).\n", rc);
> -             return rc;
> -     }
> -     fw_dump.dump_active = 0;
> -     fdm_active = NULL;
> -     return 0;
> -}
> -
>  void fadump_cleanup(void)
>  {
>       /* Invalidate the registration only if dump is active. */
>       if (fw_dump.dump_active) {
> -             /* pass the same memory dump structure provided by platform */
> -             fadump_invalidate_dump(fdm_active);
> +             pr_debug("Invalidating firmware-assisted dump registration\n");
> +             fw_dump.ops->invalidate_fadump(&fw_dump);
>       } else if (fw_dump.dump_registered) {
>               /* Un-register Firmware-assisted dump if it was registered. */
>               fw_dump.ops->unregister_fadump(&fw_dump);
> @@ -1132,7 +874,7 @@ static void fadump_invalidate_release_mem(void)
>               return;
>       }
>  
> -     destination_address = 
> be64_to_cpu(fdm_active->cpu_state_data.destination_address);
> +     destination_address = fw_dump.preserv_area_start;
>       fadump_cleanup();
>       mutex_unlock(&fadump_mutex);
>  
> @@ -1158,6 +900,7 @@ static void fadump_invalidate_release_mem(void)
>               fw_dump.cpu_notes_buf = 0;
>               fw_dump.cpu_notes_buf_size = 0;
>       }
> +
>       /* Initialize the kernel dump memory structure for FAD registration. */
>       fw_dump.ops->init_fadump_mem_struct(&fw_dump);
>  }
> @@ -1210,7 +953,7 @@ static ssize_t fadump_register_store(struct kobject 
> *kobj,
>       int ret = 0;
>       int input = -1;
>  
> -     if (!fw_dump.fadump_enabled || fdm_active)
> +     if (!fw_dump.fadump_enabled || fw_dump.dump_active)
>               return -EPERM;
>  
>       if (kstrtoint(buf, 0, &input))
> @@ -1223,7 +966,9 @@ static ssize_t fadump_register_store(struct kobject 
> *kobj,
>               if (fw_dump.dump_registered == 0) {
>                       goto unlock_out;
>               }
> +
>               /* Un-register Firmware-assisted dump */
> +             pr_debug("Un-register firmware-assisted dump\n");
>               fw_dump.ops->unregister_fadump(&fw_dump);
>               break;
>       case 1:
> @@ -1246,63 +991,13 @@ static ssize_t fadump_register_store(struct kobject 
> *kobj,
>  
>  static int fadump_region_show(struct seq_file *m, void *private)
>  {
> -     const struct rtas_fadump_mem_struct *fdm_ptr;
> -
>       if (!fw_dump.fadump_enabled)
>               return 0;
>  
>       mutex_lock(&fadump_mutex);
> -     if (fdm_active)
> -             fdm_ptr = fdm_active;
> -     else {
> -             mutex_unlock(&fadump_mutex);
> -             fw_dump.ops->fadump_region_show(&fw_dump, m);
> -             return 0;
> -     }
> +     fw_dump.ops->fadump_region_show(&fw_dump, m);
> +     mutex_unlock(&fadump_mutex);
>  
> -     seq_printf(m,
> -                     "CPU : [%#016llx-%#016llx] %#llx bytes, "
> -                     "Dumped: %#llx\n",
> -                     
> be64_to_cpu(fdm_ptr->cpu_state_data.destination_address),
> -                     
> be64_to_cpu(fdm_ptr->cpu_state_data.destination_address) +
> -                     be64_to_cpu(fdm_ptr->cpu_state_data.source_len) - 1,
> -                     be64_to_cpu(fdm_ptr->cpu_state_data.source_len),
> -                     be64_to_cpu(fdm_ptr->cpu_state_data.bytes_dumped));
> -     seq_printf(m,
> -                     "HPTE: [%#016llx-%#016llx] %#llx bytes, "
> -                     "Dumped: %#llx\n",
> -                     be64_to_cpu(fdm_ptr->hpte_region.destination_address),
> -                     be64_to_cpu(fdm_ptr->hpte_region.destination_address) +
> -                     be64_to_cpu(fdm_ptr->hpte_region.source_len) - 1,
> -                     be64_to_cpu(fdm_ptr->hpte_region.source_len),
> -                     be64_to_cpu(fdm_ptr->hpte_region.bytes_dumped));
> -     seq_printf(m,
> -                     "DUMP: [%#016llx-%#016llx] %#llx bytes, "
> -                     "Dumped: %#llx\n",
> -                     be64_to_cpu(fdm_ptr->rmr_region.destination_address),
> -                     be64_to_cpu(fdm_ptr->rmr_region.destination_address) +
> -                     be64_to_cpu(fdm_ptr->rmr_region.source_len) - 1,
> -                     be64_to_cpu(fdm_ptr->rmr_region.source_len),
> -                     be64_to_cpu(fdm_ptr->rmr_region.bytes_dumped));
> -
> -     if (!fdm_active ||
> -             (fw_dump.reserve_dump_area_start ==
> -             be64_to_cpu(fdm_ptr->cpu_state_data.destination_address)))
> -             goto out;
> -
> -     /* Dump is active. Show reserved memory region. */
> -     seq_printf(m,
> -                     "    : [%#016llx-%#016llx] %#llx bytes, "
> -                     "Dumped: %#llx\n",
> -                     (unsigned long long)fw_dump.reserve_dump_area_start,
> -                     
> be64_to_cpu(fdm_ptr->cpu_state_data.destination_address) - 1,
> -                     
> be64_to_cpu(fdm_ptr->cpu_state_data.destination_address) -
> -                     fw_dump.reserve_dump_area_start,
> -                     
> be64_to_cpu(fdm_ptr->cpu_state_data.destination_address) -
> -                     fw_dump.reserve_dump_area_start);
> -out:
> -     if (fdm_active)
> -             mutex_unlock(&fadump_mutex);
>       return 0;
>  }
>  
> @@ -1373,12 +1068,13 @@ int __init setup_fadump(void)
>                * if dump process fails then invalidate the registration
>                * and release memory before proceeding for re-registration.
>                */
> -             if (process_fadump(fdm_active) < 0)
> +             if (fw_dump.ops->process_fadump(&fw_dump) < 0)
>                       fadump_invalidate_release_mem();
>       }
>       /* Initialize the kernel dump memory structure for FAD registration. */
>       else if (fw_dump.reserve_dump_area_size)
>               fw_dump.ops->init_fadump_mem_struct(&fw_dump);
> +
>       fadump_init_files();
>  
>       return 1;
> diff --git a/arch/powerpc/platforms/pseries/rtas-fadump.c 
> b/arch/powerpc/platforms/pseries/rtas-fadump.c
> index 790a37d..7ce84f8 100644
> --- a/arch/powerpc/platforms/pseries/rtas-fadump.c
> +++ b/arch/powerpc/platforms/pseries/rtas-fadump.c
> @@ -31,6 +31,7 @@
>  #include "rtas-fadump.h"
>  
>  static struct rtas_fadump_mem_struct fdm;
> +static const struct rtas_fadump_mem_struct *fdm_active;
>  
>  static void rtas_fadump_update_config(struct fw_dump *fadump_conf,
>                                     const struct rtas_fadump_mem_struct *fdm)
> @@ -40,6 +41,23 @@ static void rtas_fadump_update_config(struct fw_dump 
> *fadump_conf,
>  
>       fadump_conf->fadumphdr_addr = (fadump_conf->boot_mem_dest_addr +
>                                      fadump_conf->boot_memory_size);
> +
> +     /* Start address of preserve area (permanent reservation) */
> +     fadump_conf->preserv_area_start =
> +             be64_to_cpu(fdm->cpu_state_data.destination_address);
> +     pr_debug("Preserve area start address: 0x%lx\n",
> +              fadump_conf->preserv_area_start);
> +}
> +
> +/*
> + * This function is called in the capture kernel to get configuration details
> + * setup in the first kernel and passed to the f/w.
> + */
> +static void rtas_fadump_get_config(struct fw_dump *fadump_conf,
> +                                const struct rtas_fadump_mem_struct *fdm)
> +{
> +     fadump_conf->boot_memory_size = be64_to_cpu(fdm->rmr_region.source_len);
> +     rtas_fadump_update_config(fadump_conf, fdm);
>  }
>  
>  static ulong rtas_fadump_init_mem_struct(struct fw_dump *fadump_conf)
> @@ -180,7 +198,196 @@ static int rtas_fadump_unregister_fadump(struct fw_dump 
> *fadump_conf)
>  
>  static int rtas_fadump_invalidate_fadump(struct fw_dump *fadump_conf)
>  {
> -     return -EIO;
> +     int rc;
> +     unsigned int wait_time;
> +
> +     /* TODO: Add upper time limit for the delay */
> +     do {
> +             rc =  rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1,
> +                             NULL, FADUMP_INVALIDATE, fdm_active,
> +                             sizeof(struct rtas_fadump_mem_struct));
> +
> +             wait_time = rtas_busy_delay_time(rc);
> +             if (wait_time)
> +                     mdelay(wait_time);
> +     } while (wait_time);
> +
> +     if (rc) {
> +             pr_err("Failed to invalidate - unexpected error (%d).\n", rc);
> +             return -EIO;
> +     }
> +
> +     fadump_conf->dump_active = 0;
> +     fdm_active = NULL;
> +     return 0;
> +}
> +
> +#define RTAS_FADUMP_GPR_MASK                 0xffffff0000000000
> +static inline int rtas_fadump_gpr_index(u64 id)
> +{
> +     int i = -1;
> +     char str[3];
> +
> +     if ((id & RTAS_FADUMP_GPR_MASK) == fadump_str_to_u64("GPR")) {
> +             /* get the digits at the end */
> +             id &= ~RTAS_FADUMP_GPR_MASK;
> +             id >>= 24;
> +             str[2] = '\0';
> +             str[1] = id & 0xff;
> +             str[0] = (id >> 8) & 0xff;
> +             if (kstrtoint(str, 10, &i))
> +                     i = -EINVAL;
> +             if (i > 31)
> +                     i = -1;
> +     }
> +     return i;
> +}
> +
> +void rtas_fadump_set_regval(struct pt_regs *regs, u64 reg_id, u64 reg_val)
> +{
> +     int i;
> +
> +     i = rtas_fadump_gpr_index(reg_id);
> +     if (i >= 0)
> +             regs->gpr[i] = (unsigned long)reg_val;
> +     else if (reg_id == fadump_str_to_u64("NIA"))
> +             regs->nip = (unsigned long)reg_val;
> +     else if (reg_id == fadump_str_to_u64("MSR"))
> +             regs->msr = (unsigned long)reg_val;
> +     else if (reg_id == fadump_str_to_u64("CTR"))
> +             regs->ctr = (unsigned long)reg_val;
> +     else if (reg_id == fadump_str_to_u64("LR"))
> +             regs->link = (unsigned long)reg_val;
> +     else if (reg_id == fadump_str_to_u64("XER"))
> +             regs->xer = (unsigned long)reg_val;
> +     else if (reg_id == fadump_str_to_u64("CR"))
> +             regs->ccr = (unsigned long)reg_val;
> +     else if (reg_id == fadump_str_to_u64("DAR"))
> +             regs->dar = (unsigned long)reg_val;
> +     else if (reg_id == fadump_str_to_u64("DSISR"))
> +             regs->dsisr = (unsigned long)reg_val;
> +}
> +
> +static struct rtas_fadump_reg_entry*
> +rtas_fadump_read_regs(struct rtas_fadump_reg_entry *reg_entry,
> +                   struct pt_regs *regs)
> +{
> +     memset(regs, 0, sizeof(struct pt_regs));
> +
> +     while (be64_to_cpu(reg_entry->reg_id) != fadump_str_to_u64("CPUEND")) {
> +             rtas_fadump_set_regval(regs, be64_to_cpu(reg_entry->reg_id),
> +                                    be64_to_cpu(reg_entry->reg_value));
> +             reg_entry++;
> +     }
> +     reg_entry++;
> +     return reg_entry;
> +}
> +
> +/*
> + * Read CPU state dump data and convert it into ELF notes.
> + * The CPU dump starts with magic number "REGSAVE". NumCpusOffset should be
> + * used to access the data to allow for additional fields to be added without
> + * affecting compatibility. Each list of registers for a CPU starts with
> + * "CPUSTRT" and ends with "CPUEND". Each register entry is of 16 bytes,
> + * 8 Byte ASCII identifier and 8 Byte register value. The register entry
> + * with identifier "CPUSTRT" and "CPUEND" contains 4 byte cpu id as part
> + * of register value. For more details refer to PAPR document.
> + *
> + * Only for the crashing cpu we ignore the CPU dump data and get exact
> + * state from fadump crash info structure populated by first kernel at the
> + * time of crash.
> + */
> +static int __init rtas_fadump_build_cpu_notes(struct fw_dump *fadump_conf)
> +{
> +     struct rtas_fadump_reg_save_area_header *reg_header;
> +     struct rtas_fadump_reg_entry *reg_entry;
> +     struct fadump_crash_info_header *fdh = NULL;
> +     void *vaddr;
> +     unsigned long addr;
> +     u32 num_cpus, *note_buf;
> +     struct pt_regs regs;
> +     int i, rc = 0, cpu = 0;
> +
> +     addr = be64_to_cpu(fdm_active->cpu_state_data.destination_address);
> +     vaddr = __va(addr);
> +
> +     reg_header = vaddr;
> +     if (be64_to_cpu(reg_header->magic_number) !=
> +         fadump_str_to_u64("REGSAVE")) {
> +             pr_err("Unable to read register save area.\n");
> +             return -ENOENT;
> +     }
> +
> +     pr_debug("--------CPU State Data------------\n");
> +     pr_debug("Magic Number: %llx\n", be64_to_cpu(reg_header->magic_number));
> +     pr_debug("NumCpuOffset: %x\n", be32_to_cpu(reg_header->num_cpu_offset));
> +
> +     vaddr += be32_to_cpu(reg_header->num_cpu_offset);
> +     num_cpus = be32_to_cpu(*((__be32 *)(vaddr)));
> +     pr_debug("NumCpus     : %u\n", num_cpus);
> +     vaddr += sizeof(u32);
> +     reg_entry = (struct rtas_fadump_reg_entry *)vaddr;
> +
> +     /* Allocate buffer to hold cpu crash notes. */
> +     fadump_conf->cpu_notes_buf_size = num_cpus * sizeof(note_buf_t);
> +     fadump_conf->cpu_notes_buf_size =
> +             PAGE_ALIGN(fadump_conf->cpu_notes_buf_size);
> +     note_buf = fadump_cpu_notes_buf_alloc(fadump_conf->cpu_notes_buf_size);
> +     if (!note_buf) {
> +             pr_err("Failed to allocate 0x%lx bytes for cpu notes buffer\n",
> +                    fadump_conf->cpu_notes_buf_size);
> +             return -ENOMEM;
> +     }
> +     fadump_conf->cpu_notes_buf = __pa(note_buf);
> +
> +     pr_debug("Allocated buffer for cpu notes of size %ld at %p\n",
> +                     (num_cpus * sizeof(note_buf_t)), note_buf);
> +
> +     if (fadump_conf->fadumphdr_addr)
> +             fdh = __va(fadump_conf->fadumphdr_addr);
> +
> +     for (i = 0; i < num_cpus; i++) {
> +             if (be64_to_cpu(reg_entry->reg_id) !=
> +                 fadump_str_to_u64("CPUSTRT")) {
> +                     pr_err("Unable to read CPU state data\n");
> +                     rc = -ENOENT;
> +                     goto error_out;
> +             }
> +             /* Lower 4 bytes of reg_value contains logical cpu id */
> +             cpu = (be64_to_cpu(reg_entry->reg_value) &
> +                    RTAS_FADUMP_CPU_ID_MASK);
> +             if (fdh && !cpumask_test_cpu(cpu, &fdh->online_mask)) {
> +                     RTAS_FADUMP_SKIP_TO_NEXT_CPU(reg_entry);
> +                     continue;
> +             }
> +             pr_debug("Reading register data for cpu %d...\n", cpu);
> +             if (fdh && fdh->crashing_cpu == cpu) {
> +                     regs = fdh->regs;
> +                     note_buf = fadump_regs_to_elf_notes(note_buf, &regs);
> +                     RTAS_FADUMP_SKIP_TO_NEXT_CPU(reg_entry);
> +             } else {
> +                     reg_entry++;
> +                     reg_entry = rtas_fadump_read_regs(reg_entry, &regs);
> +                     note_buf = fadump_regs_to_elf_notes(note_buf, &regs);
> +             }
> +     }
> +     final_note(note_buf);
> +
> +     if (fdh) {
> +             pr_debug("Updating elfcore header (%llx) with cpu notes\n",
> +                      fdh->elfcorehdr_addr);
> +             fadump_update_elfcore_header(fadump_conf,
> +                                          __va(fdh->elfcorehdr_addr));
> +     }
> +     return 0;
> +
> +error_out:
> +     fadump_cpu_notes_buf_free((ulong)__va(fadump_conf->cpu_notes_buf),
> +                               fadump_conf->cpu_notes_buf_size);
> +     fadump_conf->cpu_notes_buf = 0;
> +     fadump_conf->cpu_notes_buf_size = 0;
> +     return rc;
> +
>  }
>  
>  /*
> @@ -189,15 +396,62 @@ static int rtas_fadump_invalidate_fadump(struct fw_dump 
> *fadump_conf)
>   */
>  static int __init rtas_fadump_process_fadump(struct fw_dump *fadump_conf)
>  {
> -     return -EINVAL;
> +     struct fadump_crash_info_header *fdh;
> +     int rc = 0;
> +
> +     if (!fdm_active || !fadump_conf->fadumphdr_addr)
> +             return -EINVAL;
> +
> +     /* Check if the dump data is valid. */
> +     if ((be16_to_cpu(fdm_active->header.dump_status_flag) ==
> +                     RTAS_FADUMP_ERROR_FLAG) ||
> +                     (fdm_active->cpu_state_data.error_flags != 0) ||
> +                     (fdm_active->rmr_region.error_flags != 0)) {
> +             pr_err("Dump taken by platform is not valid\n");
> +             return -EINVAL;
> +     }
> +     if ((fdm_active->rmr_region.bytes_dumped !=
> +                     fdm_active->rmr_region.source_len) ||
> +                     !fdm_active->cpu_state_data.bytes_dumped) {
> +             pr_err("Dump taken by platform is incomplete\n");
> +             return -EINVAL;
> +     }
> +
> +     /* Validate the fadump crash info header */
> +     fdh = __va(fadump_conf->fadumphdr_addr);
> +     if (fdh->magic_number != FADUMP_CRASH_INFO_MAGIC) {
> +             pr_err("Crash info header is not valid.\n");
> +             return -EINVAL;
> +     }
> +
> +     if (!fdm_active->cpu_state_data.bytes_dumped)
> +             return -EINVAL;
> +
> +     rc = rtas_fadump_build_cpu_notes(fadump_conf);
> +     if (rc)
> +             return rc;
> +
> +     /*
> +      * We are done validating dump info and elfcore header is now ready
> +      * to be exported. set elfcorehdr_addr so that vmcore module will
> +      * export the elfcore header through '/proc/vmcore'.
> +      */
> +     elfcorehdr_addr = fdh->elfcorehdr_addr;
> +
> +     return 0;
>  }
>  
>  static void rtas_fadump_region_show(struct fw_dump *fadump_conf,
>                                   struct seq_file *m)
>  {
> -     const struct rtas_fadump_mem_struct *fdm_ptr = &fdm;
> +     const struct rtas_fadump_mem_struct *fdm_ptr;
>       const struct rtas_fadump_section *cpu_data_section;
>  
> +     if (fdm_active)
> +             fdm_ptr = fdm_active;
> +     else
> +             fdm_ptr = &fdm;
> +
>       cpu_data_section = &(fdm_ptr->cpu_state_data);
>       seq_printf(m, "CPU :[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n",
>                  be64_to_cpu(cpu_data_section->destination_address),
> @@ -219,6 +473,12 @@ static void rtas_fadump_region_show(struct fw_dump 
> *fadump_conf,
>       seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n",
>                  be64_to_cpu(fdm_ptr->rmr_region.source_len),
>                  be64_to_cpu(fdm_ptr->rmr_region.bytes_dumped));
> +
> +     /* Dump is active. Show reserved area start address. */
> +     if (fdm_active) {
> +             seq_printf(m, "\nMemory above %#016lx is reserved for saving 
> crash dump\n",
> +                        fadump_conf->reserve_dump_area_start);
> +     }
>  }
>  
>  static void rtas_fadump_trigger(struct fadump_crash_info_header *fdh,
> @@ -228,6 +488,7 @@ static void rtas_fadump_trigger(struct 
> fadump_crash_info_header *fdh,
>       rtas_os_term((char *)msg);
>  }
>  
> +
>  static struct fadump_ops rtas_fadump_ops = {
>       .init_fadump_mem_struct = rtas_fadump_init_mem_struct,
>       .register_fadump        = rtas_fadump_register_fadump,
> @@ -258,6 +519,17 @@ int __init rtas_fadump_dt_scan(struct fw_dump 
> *fadump_conf, ulong node)
>       fadump_conf->fadump_platform    = FADUMP_PLATFORM_PSERIES;
>       fadump_conf->fadump_supported   = 1;
>  
> +     /*
> +      * The 'ibm,kernel-dump' rtas node is present only if there is
> +      * dump data waiting for us.
> +      */
> +     fdm_active = of_get_flat_dt_prop(node, "ibm,kernel-dump", NULL);
> +     if (fdm_active) {
> +             pr_info("Firmware-assisted dump is active.\n");
> +             fadump_conf->dump_active = 1;
> +             rtas_fadump_get_config(fadump_conf, (void *)__pa(fdm_active));
> +     }
> +
>       /* Get the sizes required to store dump data for the firmware provided
>        * dump sections.
>        * For each dump section type supported, a 32bit cell which defines
> 

-- 
Mahesh J Salgaonkar

Reply via email to