diff mbox series

[RFC,1/3] powerpc/pseries/fadump: add support for multiple boot memory regions

Message ID 20231205201835.388030-2-hbathini@linux.ibm.com (mailing list archive)
State New
Headers show
Series powerpc/fadump: pass additional args to dump capture kernel | expand

Commit Message

Hari Bathini Dec. 5, 2023, 8:18 p.m. UTC
From: Sourabh Jain <sourabhjain@linux.ibm.com>

Currently, fadump on pseries assumes a single boot memory region even
though f/w supports more than one boot memory region. Add support for
more boot memory regions to make the implementation flexible for any
enhancements that introduce other region types. For this, rtas memory
structure for fadump is updated to have multiple boot memory regions
instead of just one. Additionally, methods responsible for creating
the fadump memory structure during both the first and second kernel
boot have been modified to take these multiple boot memory regions
into account. Also, a new callback has been added to the fadump_ops
structure to get the maximum boot memory regions supported by the
platform.

Signed-off-by: Sourabh Jain <sourabhjain@linux.ibm.com>
Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
---
 arch/powerpc/include/asm/fadump-internal.h   |   2 +-
 arch/powerpc/kernel/fadump.c                 |  27 +-
 arch/powerpc/platforms/powernv/opal-fadump.c |   8 +
 arch/powerpc/platforms/pseries/rtas-fadump.c | 258 ++++++++++++-------
 arch/powerpc/platforms/pseries/rtas-fadump.h |  26 +-
 5 files changed, 199 insertions(+), 122 deletions(-)

Comments

Sourabh Jain Dec. 15, 2023, 8:28 a.m. UTC | #1
Hello Hari,

On 06/12/23 01:48, Hari Bathini wrote:
> From: Sourabh Jain <sourabhjain@linux.ibm.com>
>
> Currently, fadump on pseries assumes a single boot memory region even
> though f/w supports more than one boot memory region. Add support for
> more boot memory regions to make the implementation flexible for any
> enhancements that introduce other region types. For this, rtas memory
> structure for fadump is updated to have multiple boot memory regions
> instead of just one. Additionally, methods responsible for creating
> the fadump memory structure during both the first and second kernel
> boot have been modified to take these multiple boot memory regions
> into account. Also, a new callback has been added to the fadump_ops
> structure to get the maximum boot memory regions supported by the
> platform.
>
> Signed-off-by: Sourabh Jain <sourabhjain@linux.ibm.com>
> Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
> ---
>   arch/powerpc/include/asm/fadump-internal.h   |   2 +-
>   arch/powerpc/kernel/fadump.c                 |  27 +-
>   arch/powerpc/platforms/powernv/opal-fadump.c |   8 +
>   arch/powerpc/platforms/pseries/rtas-fadump.c | 258 ++++++++++++-------
>   arch/powerpc/platforms/pseries/rtas-fadump.h |  26 +-
>   5 files changed, 199 insertions(+), 122 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/fadump-internal.h b/arch/powerpc/include/asm/fadump-internal.h
> index 27f9e11eda28..b3956c400519 100644
> --- a/arch/powerpc/include/asm/fadump-internal.h
> +++ b/arch/powerpc/include/asm/fadump-internal.h
> @@ -129,6 +129,7 @@ struct fadump_ops {
>   				      struct seq_file *m);
>   	void	(*fadump_trigger)(struct fadump_crash_info_header *fdh,
>   				  const char *msg);
> +	int	(*fadump_max_boot_mem_rgns)(void);
>   };
>   
>   /* Helper functions */
> @@ -136,7 +137,6 @@ s32 __init fadump_setup_cpu_notes_buf(u32 num_cpus);
>   void fadump_free_cpu_notes_buf(void);
>   u32 *__init fadump_regs_to_elf_notes(u32 *buf, struct pt_regs *regs);
>   void __init fadump_update_elfcore_header(char *bufp);
> -bool is_fadump_boot_mem_contiguous(void);
>   bool is_fadump_reserved_mem_contiguous(void);
>   
>   #else /* !CONFIG_PRESERVE_FA_DUMP */
> diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c
> index d14eda1e8589..757681658dda 100644
> --- a/arch/powerpc/kernel/fadump.c
> +++ b/arch/powerpc/kernel/fadump.c
> @@ -222,28 +222,6 @@ static bool is_fadump_mem_area_contiguous(u64 d_start, u64 d_end)
>   	return ret;
>   }
>   
> -/*
> - * Returns true, if there are no holes in boot memory area,
> - * false otherwise.
> - */
> -bool is_fadump_boot_mem_contiguous(void)
> -{
> -	unsigned long d_start, d_end;
> -	bool ret = false;
> -	int i;
> -
> -	for (i = 0; i < fw_dump.boot_mem_regs_cnt; i++) {
> -		d_start = fw_dump.boot_mem_addr[i];
> -		d_end   = d_start + fw_dump.boot_mem_sz[i];
> -
> -		ret = is_fadump_mem_area_contiguous(d_start, d_end);
> -		if (!ret)
> -			break;
> -	}
> -
> -	return ret;
> -}
> -
>   /*
>    * Returns true, if there are no holes in reserved memory area,
>    * false otherwise.
> @@ -389,10 +367,11 @@ static unsigned long __init get_fadump_area_size(void)
>   static int __init add_boot_mem_region(unsigned long rstart,
>   				      unsigned long rsize)
>   {
> +	int max_boot_mem_rgns = fw_dump.ops->fadump_max_boot_mem_rgns();
>   	int i = fw_dump.boot_mem_regs_cnt++;
>   
> -	if (fw_dump.boot_mem_regs_cnt > FADUMP_MAX_MEM_REGS) {
> -		fw_dump.boot_mem_regs_cnt = FADUMP_MAX_MEM_REGS;
> +	if (fw_dump.boot_mem_regs_cnt > max_boot_mem_rgns) {
> +		fw_dump.boot_mem_regs_cnt = max_boot_mem_rgns;
>   		return 0;
>   	}
>   
> diff --git a/arch/powerpc/platforms/powernv/opal-fadump.c b/arch/powerpc/platforms/powernv/opal-fadump.c
> index 964f464b1b0e..fa26c21a08d9 100644
> --- a/arch/powerpc/platforms/powernv/opal-fadump.c
> +++ b/arch/powerpc/platforms/powernv/opal-fadump.c
> @@ -615,6 +615,13 @@ static void opal_fadump_trigger(struct fadump_crash_info_header *fdh,
>   		pr_emerg("No backend support for MPIPL!\n");
>   }
>   
> +/* FADUMP_MAX_MEM_REGS or lower */
> +static int opal_fadump_max_boot_mem_rgns(void)
> +{
> +	return FADUMP_MAX_MEM_REGS;
> +

Nitpick: we can get rid of the above blank line.

- Sourabh

> +}
> +
>   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,
> @@ -627,6 +634,7 @@ static struct fadump_ops opal_fadump_ops = {
>   	.fadump_process			= opal_fadump_process,
>   	.fadump_region_show		= opal_fadump_region_show,
>   	.fadump_trigger			= opal_fadump_trigger,
> +	.fadump_max_boot_mem_rgns	= opal_fadump_max_boot_mem_rgns,
>   };
>   
>   void __init opal_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node)
> diff --git a/arch/powerpc/platforms/pseries/rtas-fadump.c b/arch/powerpc/platforms/pseries/rtas-fadump.c
> index b5853e9fcc3c..1b05b4cefdfd 100644
> --- a/arch/powerpc/platforms/pseries/rtas-fadump.c
> +++ b/arch/powerpc/platforms/pseries/rtas-fadump.c
> @@ -29,9 +29,6 @@ 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)
>   {
> -	fadump_conf->boot_mem_dest_addr =
> -		be64_to_cpu(fdm->rmr_region.destination_address);
> -
>   	fadump_conf->fadumphdr_addr = (fadump_conf->boot_mem_dest_addr +
>   				       fadump_conf->boot_memory_size);
>   }
> @@ -43,20 +40,52 @@ static void rtas_fadump_update_config(struct fw_dump *fadump_conf,
>   static void __init rtas_fadump_get_config(struct fw_dump *fadump_conf,
>   				   const struct rtas_fadump_mem_struct *fdm)
>   {
> -	fadump_conf->boot_mem_addr[0] =
> -		be64_to_cpu(fdm->rmr_region.source_address);
> -	fadump_conf->boot_mem_sz[0] = be64_to_cpu(fdm->rmr_region.source_len);
> -	fadump_conf->boot_memory_size = fadump_conf->boot_mem_sz[0];
> +	unsigned long base, size, last_end, hole_size;
>   
> -	fadump_conf->boot_mem_top = fadump_conf->boot_memory_size;
> -	fadump_conf->boot_mem_regs_cnt = 1;
> +	last_end = 0;
> +	hole_size = 0;
> +	fadump_conf->boot_memory_size = 0;
> +	fadump_conf->boot_mem_regs_cnt = 0;
> +	pr_debug("Boot memory regions:\n");
> +	for (int i = 0; i < be16_to_cpu(fdm->header.dump_num_sections); i++) {
> +		int type = be16_to_cpu(fdm->rgn[i].source_data_type);
>   
> -	/*
> -	 * Start address of reserve dump area (permanent reservation) for
> -	 * re-registering FADump after dump capture.
> -	 */
> -	fadump_conf->reserve_dump_area_start =
> -		be64_to_cpu(fdm->cpu_state_data.destination_address);
> +		switch (type) {
> +		case RTAS_FADUMP_CPU_STATE_DATA:
> +			u64 addr = be64_to_cpu(fdm->rgn[i].destination_address);
> +
> +			fadump_conf->cpu_state_dest_vaddr = (u64)__va(addr);
> +			/*
> +			 * Start address of reserve dump area (permanent reservation) for
> +			 * re-registering FADump after dump capture.
> +			 */
> +			fadump_conf->reserve_dump_area_start = addr;
> +			break;
> +		case RTAS_FADUMP_HPTE_REGION:
> +			/* Not processed currently. */
> +			break;
> +		case RTAS_FADUMP_REAL_MODE_REGION:
> +			base = be64_to_cpu(fdm->rgn[i].source_address);
> +			size = be64_to_cpu(fdm->rgn[i].source_len);
> +			pr_debug("\t[%03d] base: 0x%lx, size: 0x%lx\n", i, base, size);
> +			if (!base) {
> +				fadump_conf->boot_mem_dest_addr =
> +					be64_to_cpu(fdm->rgn[i].destination_address);
> +			}
> +
> +			fadump_conf->boot_mem_addr[fadump_conf->boot_mem_regs_cnt] = base;
> +			fadump_conf->boot_mem_sz[fadump_conf->boot_mem_regs_cnt] = size;
> +			fadump_conf->boot_memory_size += size;
> +			hole_size += (base - last_end);
> +			last_end = base + size;
> +			fadump_conf->boot_mem_regs_cnt++;
> +			break;
> +		default:
> +			pr_warn("Section type %d unsupported on this kernel. Ignoring!\n", type);
> +			break;
> +		}
> +	}
> +	fadump_conf->boot_mem_top = fadump_conf->boot_memory_size + hole_size;
>   
>   	rtas_fadump_update_config(fadump_conf, fdm);
>   }
> @@ -64,16 +93,15 @@ static void __init rtas_fadump_get_config(struct fw_dump *fadump_conf,
>   static u64 rtas_fadump_init_mem_struct(struct fw_dump *fadump_conf)
>   {
>   	u64 addr = fadump_conf->reserve_dump_area_start;
> +	u16 sec_cnt = 0;
>   
>   	memset(&fdm, 0, sizeof(struct rtas_fadump_mem_struct));
>   	addr = addr & PAGE_MASK;
>   
>   	fdm.header.dump_format_version = cpu_to_be32(0x00000001);
> -	fdm.header.dump_num_sections = cpu_to_be16(3);
>   	fdm.header.dump_status_flag = 0;
>   	fdm.header.offset_first_dump_section =
> -		cpu_to_be32((u32)offsetof(struct rtas_fadump_mem_struct,
> -					  cpu_state_data));
> +		cpu_to_be32((u32)offsetof(struct rtas_fadump_mem_struct, rgn));
>   
>   	/*
>   	 * Fields for disk dump option.
> @@ -89,25 +117,22 @@ static u64 rtas_fadump_init_mem_struct(struct fw_dump *fadump_conf)
>   
>   	/* Kernel dump sections */
>   	/* cpu state data section. */
> -	fdm.cpu_state_data.request_flag =
> -		cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
> -	fdm.cpu_state_data.source_data_type =
> -		cpu_to_be16(RTAS_FADUMP_CPU_STATE_DATA);
> -	fdm.cpu_state_data.source_address = 0;
> -	fdm.cpu_state_data.source_len =
> -		cpu_to_be64(fadump_conf->cpu_state_data_size);
> -	fdm.cpu_state_data.destination_address = cpu_to_be64(addr);
> +	fdm.rgn[sec_cnt].request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
> +	fdm.rgn[sec_cnt].source_data_type = cpu_to_be16(RTAS_FADUMP_CPU_STATE_DATA);
> +	fdm.rgn[sec_cnt].source_address = 0;
> +	fdm.rgn[sec_cnt].source_len = cpu_to_be64(fadump_conf->cpu_state_data_size);
> +	fdm.rgn[sec_cnt].destination_address = cpu_to_be64(addr);
>   	addr += fadump_conf->cpu_state_data_size;
> +	sec_cnt++;
>   
>   	/* hpte region section */
> -	fdm.hpte_region.request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
> -	fdm.hpte_region.source_data_type =
> -		cpu_to_be16(RTAS_FADUMP_HPTE_REGION);
> -	fdm.hpte_region.source_address = 0;
> -	fdm.hpte_region.source_len =
> -		cpu_to_be64(fadump_conf->hpte_region_size);
> -	fdm.hpte_region.destination_address = cpu_to_be64(addr);
> +	fdm.rgn[sec_cnt].request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
> +	fdm.rgn[sec_cnt].source_data_type = cpu_to_be16(RTAS_FADUMP_HPTE_REGION);
> +	fdm.rgn[sec_cnt].source_address = 0;
> +	fdm.rgn[sec_cnt].source_len = cpu_to_be64(fadump_conf->hpte_region_size);
> +	fdm.rgn[sec_cnt].destination_address = cpu_to_be64(addr);
>   	addr += fadump_conf->hpte_region_size;
> +	sec_cnt++;
>   
>   	/*
>   	 * Align boot memory area destination address to page boundary to
> @@ -115,15 +140,20 @@ static u64 rtas_fadump_init_mem_struct(struct fw_dump *fadump_conf)
>   	 */
>   	addr = PAGE_ALIGN(addr);
>   
> -	/* RMA region section */
> -	fdm.rmr_region.request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
> -	fdm.rmr_region.source_data_type =
> -		cpu_to_be16(RTAS_FADUMP_REAL_MODE_REGION);
> -	fdm.rmr_region.source_address = cpu_to_be64(0);
> -	fdm.rmr_region.source_len = cpu_to_be64(fadump_conf->boot_memory_size);
> -	fdm.rmr_region.destination_address = cpu_to_be64(addr);
> -	addr += fadump_conf->boot_memory_size;
> +	/* First boot memory region destination address */
> +	fadump_conf->boot_mem_dest_addr = addr;
> +	for (int i = 0; i < fadump_conf->boot_mem_regs_cnt; i++) {
> +		/* Boot memory regions */
> +		fdm.rgn[sec_cnt].request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
> +		fdm.rgn[sec_cnt].source_data_type = cpu_to_be16(RTAS_FADUMP_REAL_MODE_REGION);
> +		fdm.rgn[sec_cnt].source_address = cpu_to_be64(fadump_conf->boot_mem_addr[i]);
> +		fdm.rgn[sec_cnt].source_len = cpu_to_be64(fadump_conf->boot_mem_sz[i]);
> +		fdm.rgn[sec_cnt].destination_address = cpu_to_be64(addr);
> +		addr += fadump_conf->boot_mem_sz[i];
> +		sec_cnt++;
> +	}
>   
> +	fdm.header.dump_num_sections = cpu_to_be16(sec_cnt);
>   	rtas_fadump_update_config(fadump_conf, &fdm);
>   
>   	return addr;
> @@ -136,14 +166,21 @@ static u64 rtas_fadump_get_bootmem_min(void)
>   
>   static int rtas_fadump_register(struct fw_dump *fadump_conf)
>   {
> -	unsigned int wait_time;
> +	unsigned int wait_time, fdm_size;
>   	int rc, err = -EIO;
>   
> +	/*
> +	 * Platform requires the exact size of the Dump Memory Structure.
> +	 * Avoid including any unused rgns in the calculation, as this
> +	 * could result in a parameter error (-3) from the platform.
> +	 */
> +	fdm_size = sizeof(struct rtas_fadump_section_header);
> +	fdm_size += be16_to_cpu(fdm.header.dump_num_sections) * sizeof(struct rtas_fadump_section);
> +
>   	/* TODO: Add upper time limit for the delay */
>   	do {
>   		rc =  rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1,
> -				NULL, FADUMP_REGISTER, &fdm,
> -				sizeof(struct rtas_fadump_mem_struct));
> +				NULL, FADUMP_REGISTER, &fdm, fdm_size);
>   
>   		wait_time = rtas_busy_delay_time(rc);
>   		if (wait_time)
> @@ -161,9 +198,7 @@ static int rtas_fadump_register(struct fw_dump *fadump_conf)
>   		pr_err("Failed to register. Hardware Error(%d).\n", rc);
>   		break;
>   	case -3:
> -		if (!is_fadump_boot_mem_contiguous())
> -			pr_err("Can't have holes in boot memory area.\n");
> -		else if (!is_fadump_reserved_mem_contiguous())
> +		if (!is_fadump_reserved_mem_contiguous())
>   			pr_err("Can't have holes in reserved memory area.\n");
>   
>   		pr_err("Failed to register. Parameter Error(%d).\n", rc);
> @@ -316,11 +351,9 @@ static int __init rtas_fadump_build_cpu_notes(struct fw_dump *fadump_conf)
>   	u32 num_cpus, *note_buf;
>   	int i, rc = 0, cpu = 0;
>   	struct pt_regs regs;
> -	unsigned long addr;
>   	void *vaddr;
>   
> -	addr = be64_to_cpu(fdm_active->cpu_state_data.destination_address);
> -	vaddr = __va(addr);
> +	vaddr = (void *)fadump_conf->cpu_state_dest_vaddr;
>   
>   	reg_header = vaddr;
>   	if (be64_to_cpu(reg_header->magic_number) !=
> @@ -395,24 +428,48 @@ static int __init rtas_fadump_build_cpu_notes(struct fw_dump *fadump_conf)
>   static int __init rtas_fadump_process(struct fw_dump *fadump_conf)
>   {
>   	struct fadump_crash_info_header *fdh;
> -	int rc = 0;
> +	int i, 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;
> +	/* Check all regions. */
> +	for (i = 0; i < be16_to_cpu(fdm_active->header.dump_num_sections); i++) {
> +		int type = be16_to_cpu(fdm_active->rgn[i].source_data_type);
> +
> +		switch (type) {
> +		case RTAS_FADUMP_CPU_STATE_DATA:
> +		case RTAS_FADUMP_HPTE_REGION:
> +		case RTAS_FADUMP_REAL_MODE_REGION:
> +			if (fdm_active->rgn[i].error_flags != 0) {
> +				pr_err("Dump taken by platform is not valid (%d)\n", i);
> +				rc = -EINVAL;
> +			}
> +			if (fdm_active->rgn[i].bytes_dumped != fdm_active->rgn[i].source_len) {
> +				pr_err("Dump taken by platform is incomplete (%d)\n", i);
> +				rc = -EINVAL;
> +			}
> +			if (rc) {
> +				pr_warn("Region type: %u src addr: 0x%llx dest addr: 0x%llx\n",
> +					be16_to_cpu(fdm_active->rgn[i].source_data_type),
> +					be64_to_cpu(fdm_active->rgn[i].source_address),
> +					be64_to_cpu(fdm_active->rgn[i].destination_address));
> +				return rc;
> +			}
> +			break;
> +		default:
> +			/*
> +			 * If the first/crashed kernel added a new region type that the
> +			 * second/fadump kernel doesn't recognize, skip it and process
> +			 * assuming backward compatibility.
> +			 */
> +			pr_warn("Unknown region found: type: %u src addr: 0x%llx dest addr: 0x%llx\n",
> +				be16_to_cpu(fdm_active->rgn[i].source_data_type),
> +				be64_to_cpu(fdm_active->rgn[i].source_address),
> +				be64_to_cpu(fdm_active->rgn[i].destination_address));
> +			break;
> +		}
> +
>   	}
>   
>   	/* Validate the fadump crash info header */
> @@ -439,7 +496,6 @@ static int __init rtas_fadump_process(struct fw_dump *fadump_conf)
>   static void rtas_fadump_region_show(struct fw_dump *fadump_conf,
>   				    struct seq_file *m)
>   {
> -	const struct rtas_fadump_section *cpu_data_section;
>   	const struct rtas_fadump_mem_struct *fdm_ptr;
>   
>   	if (fdm_active)
> @@ -447,27 +503,42 @@ static void rtas_fadump_region_show(struct fw_dump *fadump_conf,
>   	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),
> -		   be64_to_cpu(cpu_data_section->destination_address) +
> -		   be64_to_cpu(cpu_data_section->source_len) - 1,
> -		   be64_to_cpu(cpu_data_section->source_len),
> -		   be64_to_cpu(cpu_data_section->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: Src: %#016llx, Dest: %#016llx, ",
> -		   be64_to_cpu(fdm_ptr->rmr_region.source_address),
> -		   be64_to_cpu(fdm_ptr->rmr_region.destination_address));
> -	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));
> +
> +	for (int i = 0; i < be16_to_cpu(fdm_ptr->header.dump_num_sections); i++) {
> +		int type = be16_to_cpu(fdm_ptr->rgn[i].source_data_type);
> +
> +		switch (type) {
> +		case RTAS_FADUMP_CPU_STATE_DATA:
> +			seq_printf(m, "CPU :[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n",
> +				   be64_to_cpu(fdm_ptr->rgn[i].destination_address),
> +				   be64_to_cpu(fdm_ptr->rgn[i].destination_address) +
> +				   be64_to_cpu(fdm_ptr->rgn[i].source_len) - 1,
> +				   be64_to_cpu(fdm_ptr->rgn[i].source_len),
> +				   be64_to_cpu(fdm_ptr->rgn[i].bytes_dumped));
> +			break;
> +		case RTAS_FADUMP_HPTE_REGION:
> +			seq_printf(m, "HPTE:[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n",
> +				   be64_to_cpu(fdm_ptr->rgn[i].destination_address),
> +				   be64_to_cpu(fdm_ptr->rgn[i].destination_address) +
> +				   be64_to_cpu(fdm_ptr->rgn[i].source_len) - 1,
> +				   be64_to_cpu(fdm_ptr->rgn[i].source_len),
> +				   be64_to_cpu(fdm_ptr->rgn[i].bytes_dumped));
> +			break;
> +		case RTAS_FADUMP_REAL_MODE_REGION:
> +			seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ",
> +				   be64_to_cpu(fdm_ptr->rgn[i].source_address),
> +				   be64_to_cpu(fdm_ptr->rgn[i].destination_address));
> +			seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n",
> +				   be64_to_cpu(fdm_ptr->rgn[i].source_len),
> +				   be64_to_cpu(fdm_ptr->rgn[i].bytes_dumped));
> +			break;
> +		default:
> +			seq_printf(m, "Unknown region type %d : Src: %#016llx, Dest: %#016llx, ",
> +				   type, be64_to_cpu(fdm_ptr->rgn[i].source_address),
> +				   be64_to_cpu(fdm_ptr->rgn[i].destination_address));
> +			break;
> +		}
> +	}
>   
>   	/* Dump is active. Show preserved area start address. */
>   	if (fdm_active) {
> @@ -483,6 +554,20 @@ static void rtas_fadump_trigger(struct fadump_crash_info_header *fdh,
>   	rtas_os_term((char *)msg);
>   }
>   
> +/* FADUMP_MAX_MEM_REGS or lower */
> +static int rtas_fadump_max_boot_mem_rgns(void)
> +{
> +	/*
> +	 * Version 1 of Kernel Assisted Dump Memory Structure (PAPR) supports 10 sections.
> +	 * With one each section taken for CPU state data & HPTE respectively, 8 sections
> +	 * can be used for boot memory regions.
> +	 *
> +	 * If new region(s) is(are) defined, maximum boot memory regions will decrease
> +	 * proportionally.
> +	 */
> +	return RTAS_FADUMP_MAX_BOOT_MEM_REGS;
> +}
> +
>   static struct fadump_ops rtas_fadump_ops = {
>   	.fadump_init_mem_struct		= rtas_fadump_init_mem_struct,
>   	.fadump_get_bootmem_min		= rtas_fadump_get_bootmem_min,
> @@ -492,6 +577,7 @@ static struct fadump_ops rtas_fadump_ops = {
>   	.fadump_process			= rtas_fadump_process,
>   	.fadump_region_show		= rtas_fadump_region_show,
>   	.fadump_trigger			= rtas_fadump_trigger,
> +	.fadump_max_boot_mem_rgns	= rtas_fadump_max_boot_mem_rgns,
>   };
>   
>   void __init rtas_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node)
> diff --git a/arch/powerpc/platforms/pseries/rtas-fadump.h b/arch/powerpc/platforms/pseries/rtas-fadump.h
> index fd59bd7ca9c3..6740f4981bb8 100644
> --- a/arch/powerpc/platforms/pseries/rtas-fadump.h
> +++ b/arch/powerpc/platforms/pseries/rtas-fadump.h
> @@ -29,6 +29,15 @@
>   /* Dump status flag */
>   #define RTAS_FADUMP_ERROR_FLAG		0x2000
>   
> +/*
> + * The Firmware Assisted Dump Memory structure supports a maximum of 10 sections
> + * in the dump memory structure. Presently, first two sections are used for
> + * CPU and HPTE data, while the remaining eight sections can be used for
> + * boot memory regions.
> + */
> +#define MAX_SECTIONS				10
> +#define RTAS_FADUMP_MAX_BOOT_MEM_REGS		8
> +
>   /* Kernel Dump section info */
>   struct rtas_fadump_section {
>   	__be32	request_flag;
> @@ -61,20 +70,15 @@ struct rtas_fadump_section_header {
>    * Firmware Assisted dump memory structure. This structure is required for
>    * registering future kernel dump with power firmware through rtas call.
>    *
> - * No disk dump option. Hence disk dump path string section is not included.
> + * In version 1, the platform permits one section header, dump-disk path
> + * and ten sections.
> + *
> + * Note: No disk dump option. Hence disk dump path string section is not
> + * included.
>    */
>   struct rtas_fadump_mem_struct {
>   	struct rtas_fadump_section_header	header;
> -
> -	/* Kernel dump sections */
> -	struct rtas_fadump_section		cpu_state_data;
> -	struct rtas_fadump_section		hpte_region;
> -
> -	/*
> -	 * TODO: Extend multiple boot memory regions support in the kernel
> -	 *       for this platform.
> -	 */
> -	struct rtas_fadump_section		rmr_region;
> +	struct rtas_fadump_section		rgn[MAX_SECTIONS];
>   };
>   
>   /*
Aditya Gupta Dec. 17, 2023, 8:41 a.m. UTC | #2
Hi sourabh,

On 06/12/23 01:48, Hari Bathini wrote:
> From: Sourabh Jain <sourabhjain@linux.ibm.com>
>
> Currently, fadump on pseries assumes a single boot memory region even
> though f/w supports more than one boot memory region. Add support for
> more boot memory regions to make the implementation flexible for any
> enhancements that introduce other region types. For this, rtas memory
> structure for fadump is updated to have multiple boot memory regions
> instead of just one. Additionally, methods responsible for creating
> the fadump memory structure during both the first and second kernel
> boot have been modified to take these multiple boot memory regions
> into account. Also, a new callback has been added to the fadump_ops
> structure to get the maximum boot memory regions supported by the
> platform.
>
> Signed-off-by: Sourabh Jain <sourabhjain@linux.ibm.com>
> Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
> ---
>   arch/powerpc/include/asm/fadump-internal.h   |   2 +-
>   arch/powerpc/kernel/fadump.c                 |  27 +-
>   arch/powerpc/platforms/powernv/opal-fadump.c |   8 +
>   arch/powerpc/platforms/pseries/rtas-fadump.c | 258 ++++++++++++-------
>   arch/powerpc/platforms/pseries/rtas-fadump.h |  26 +-
>   5 files changed, 199 insertions(+), 122 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/fadump-internal.h b/arch/powerpc/include/asm/fadump-internal.h
> index 27f9e11eda28..b3956c400519 100644
> --- a/arch/powerpc/include/asm/fadump-internal.h
> +++ b/arch/powerpc/include/asm/fadump-internal.h
> @@ -129,6 +129,7 @@ struct fadump_ops {
>   				      struct seq_file *m);
>   	void	(*fadump_trigger)(struct fadump_crash_info_header *fdh,
>   				  const char *msg);
> +	int	(*fadump_max_boot_mem_rgns)(void);
>   };
>   
>   /* Helper functions */
> @@ -136,7 +137,6 @@ s32 __init fadump_setup_cpu_notes_buf(u32 num_cpus);
>   void fadump_free_cpu_notes_buf(void);
>   u32 *__init fadump_regs_to_elf_notes(u32 *buf, struct pt_regs *regs);
>   void __init fadump_update_elfcore_header(char *bufp);
> -bool is_fadump_boot_mem_contiguous(void);
>   bool is_fadump_reserved_mem_contiguous(void);
>   
>   #else /* !CONFIG_PRESERVE_FA_DUMP */
> diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c
> index d14eda1e8589..757681658dda 100644
> --- a/arch/powerpc/kernel/fadump.c
> +++ b/arch/powerpc/kernel/fadump.c
> @@ -222,28 +222,6 @@ static bool is_fadump_mem_area_contiguous(u64 d_start, u64 d_end)
>   	return ret;
>   }
>   
> -/*
> - * Returns true, if there are no holes in boot memory area,
> - * false otherwise.
> - */
> -bool is_fadump_boot_mem_contiguous(void)
> -{
> -	unsigned long d_start, d_end;
> -	bool ret = false;
> -	int i;
> -
> -	for (i = 0; i < fw_dump.boot_mem_regs_cnt; i++) {
> -		d_start = fw_dump.boot_mem_addr[i];
> -		d_end   = d_start + fw_dump.boot_mem_sz[i];
> -
> -		ret = is_fadump_mem_area_contiguous(d_start, d_end);
> -		if (!ret)
> -			break;
> -	}
> -
> -	return ret;
> -}
> -
>   /*
>    * Returns true, if there are no holes in reserved memory area,
>    * false otherwise.
> @@ -389,10 +367,11 @@ static unsigned long __init get_fadump_area_size(void)
>   static int __init add_boot_mem_region(unsigned long rstart,
>   				      unsigned long rsize)
>   {
> +	int max_boot_mem_rgns = fw_dump.ops->fadump_max_boot_mem_rgns();
>   	int i = fw_dump.boot_mem_regs_cnt++;
>   
> -	if (fw_dump.boot_mem_regs_cnt > FADUMP_MAX_MEM_REGS) {
> -		fw_dump.boot_mem_regs_cnt = FADUMP_MAX_MEM_REGS;
> +	if (fw_dump.boot_mem_regs_cnt > max_boot_mem_rgns) {
> +		fw_dump.boot_mem_regs_cnt = max_boot_mem_rgns;
>   		return 0;
>   	}
>   
> diff --git a/arch/powerpc/platforms/powernv/opal-fadump.c b/arch/powerpc/platforms/powernv/opal-fadump.c
> index 964f464b1b0e..fa26c21a08d9 100644
> --- a/arch/powerpc/platforms/powernv/opal-fadump.c
> +++ b/arch/powerpc/platforms/powernv/opal-fadump.c
> @@ -615,6 +615,13 @@ static void opal_fadump_trigger(struct fadump_crash_info_header *fdh,
>   		pr_emerg("No backend support for MPIPL!\n");
>   }
>   
> +/* FADUMP_MAX_MEM_REGS or lower */
> +static int opal_fadump_max_boot_mem_rgns(void)
> +{
> +	return FADUMP_MAX_MEM_REGS;
> +
> +}
> +
>   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,
> @@ -627,6 +634,7 @@ static struct fadump_ops opal_fadump_ops = {
>   	.fadump_process			= opal_fadump_process,
>   	.fadump_region_show		= opal_fadump_region_show,
>   	.fadump_trigger			= opal_fadump_trigger,
> +	.fadump_max_boot_mem_rgns	= opal_fadump_max_boot_mem_rgns,
>   };
>   
>   void __init opal_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node)
> diff --git a/arch/powerpc/platforms/pseries/rtas-fadump.c b/arch/powerpc/platforms/pseries/rtas-fadump.c
> index b5853e9fcc3c..1b05b4cefdfd 100644
> --- a/arch/powerpc/platforms/pseries/rtas-fadump.c
> +++ b/arch/powerpc/platforms/pseries/rtas-fadump.c
> @@ -29,9 +29,6 @@ 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)
>   {
> -	fadump_conf->boot_mem_dest_addr =
> -		be64_to_cpu(fdm->rmr_region.destination_address);
> -
>   	fadump_conf->fadumphdr_addr = (fadump_conf->boot_mem_dest_addr +
>   				       fadump_conf->boot_memory_size);
>   }
> @@ -43,20 +40,52 @@ static void rtas_fadump_update_config(struct fw_dump *fadump_conf,
>   static void __init rtas_fadump_get_config(struct fw_dump *fadump_conf,
>   				   const struct rtas_fadump_mem_struct *fdm)
>   {
> -	fadump_conf->boot_mem_addr[0] =
> -		be64_to_cpu(fdm->rmr_region.source_address);
> -	fadump_conf->boot_mem_sz[0] = be64_to_cpu(fdm->rmr_region.source_len);
> -	fadump_conf->boot_memory_size = fadump_conf->boot_mem_sz[0];
> +	unsigned long base, size, last_end, hole_size;
>   
> -	fadump_conf->boot_mem_top = fadump_conf->boot_memory_size;
> -	fadump_conf->boot_mem_regs_cnt = 1;
> +	last_end = 0;
> +	hole_size = 0;
> +	fadump_conf->boot_memory_size = 0;
> +	fadump_conf->boot_mem_regs_cnt = 0;
> +	pr_debug("Boot memory regions:\n");
> +	for (int i = 0; i < be16_to_cpu(fdm->header.dump_num_sections); i++) {
> +		int type = be16_to_cpu(fdm->rgn[i].source_data_type);
>   
> -	/*
> -	 * Start address of reserve dump area (permanent reservation) for
> -	 * re-registering FADump after dump capture.
> -	 */
> -	fadump_conf->reserve_dump_area_start =
> -		be64_to_cpu(fdm->cpu_state_data.destination_address);
> +		switch (type) {
> +		case RTAS_FADUMP_CPU_STATE_DATA:
> +			u64 addr = be64_to_cpu(fdm->rgn[i].destination_address);

This caused a compiler error on my system:

arch/powerpc/platforms/pseries/rtas-fadump.c: In function 
‘rtas_fadump_get_config’:
arch/powerpc/platforms/pseries/rtas-fadump.c:56:4: error: a label can 
only be part of a statement and a declaration is not a statement
     u64 addr = be64_to_cpu(fdm->rgn[i].destination_address);
     ^~~

Probably the 'addr' local variable needs to be in it's own local scope.

Adding the case block in brackets {} solved the error for me.
Rest of the code looks good to me. I will also try to test the patch 
series later.


Thanks,

Aditya Gupta

> +
> +			fadump_conf->cpu_state_dest_vaddr = (u64)__va(addr);
> +			/*
> +			 * Start address of reserve dump area (permanent reservation) for
> +			 * re-registering FADump after dump capture.
> +			 */
> +			fadump_conf->reserve_dump_area_start = addr;
> +			break;
> +		case RTAS_FADUMP_HPTE_REGION:
> +			/* Not processed currently. */
> +			break;
> +		case RTAS_FADUMP_REAL_MODE_REGION:
> +			base = be64_to_cpu(fdm->rgn[i].source_address);
> +			size = be64_to_cpu(fdm->rgn[i].source_len);
> +			pr_debug("\t[%03d] base: 0x%lx, size: 0x%lx\n", i, base, size);
> +			if (!base) {
> +				fadump_conf->boot_mem_dest_addr =
> +					be64_to_cpu(fdm->rgn[i].destination_address);
> +			}
> +
> +			fadump_conf->boot_mem_addr[fadump_conf->boot_mem_regs_cnt] = base;
> +			fadump_conf->boot_mem_sz[fadump_conf->boot_mem_regs_cnt] = size;
> +			fadump_conf->boot_memory_size += size;
> +			hole_size += (base - last_end);
> +			last_end = base + size;
> +			fadump_conf->boot_mem_regs_cnt++;
> +			break;
> +		default:
> +			pr_warn("Section type %d unsupported on this kernel. Ignoring!\n", type);
> +			break;
> +		}
> +	}
> +	fadump_conf->boot_mem_top = fadump_conf->boot_memory_size + hole_size;
>   
>   	rtas_fadump_update_config(fadump_conf, fdm);
>   }
> @@ -64,16 +93,15 @@ static void __init rtas_fadump_get_config(struct fw_dump *fadump_conf,
>   static u64 rtas_fadump_init_mem_struct(struct fw_dump *fadump_conf)
>   {
>   	u64 addr = fadump_conf->reserve_dump_area_start;
> +	u16 sec_cnt = 0;
>   
>   	memset(&fdm, 0, sizeof(struct rtas_fadump_mem_struct));
>   	addr = addr & PAGE_MASK;
>   
>   	fdm.header.dump_format_version = cpu_to_be32(0x00000001);
> -	fdm.header.dump_num_sections = cpu_to_be16(3);
>   	fdm.header.dump_status_flag = 0;
>   	fdm.header.offset_first_dump_section =
> -		cpu_to_be32((u32)offsetof(struct rtas_fadump_mem_struct,
> -					  cpu_state_data));
> +		cpu_to_be32((u32)offsetof(struct rtas_fadump_mem_struct, rgn));
>   
>   	/*
>   	 * Fields for disk dump option.
> @@ -89,25 +117,22 @@ static u64 rtas_fadump_init_mem_struct(struct fw_dump *fadump_conf)
>   
>   	/* Kernel dump sections */
>   	/* cpu state data section. */
> -	fdm.cpu_state_data.request_flag =
> -		cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
> -	fdm.cpu_state_data.source_data_type =
> -		cpu_to_be16(RTAS_FADUMP_CPU_STATE_DATA);
> -	fdm.cpu_state_data.source_address = 0;
> -	fdm.cpu_state_data.source_len =
> -		cpu_to_be64(fadump_conf->cpu_state_data_size);
> -	fdm.cpu_state_data.destination_address = cpu_to_be64(addr);
> +	fdm.rgn[sec_cnt].request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
> +	fdm.rgn[sec_cnt].source_data_type = cpu_to_be16(RTAS_FADUMP_CPU_STATE_DATA);
> +	fdm.rgn[sec_cnt].source_address = 0;
> +	fdm.rgn[sec_cnt].source_len = cpu_to_be64(fadump_conf->cpu_state_data_size);
> +	fdm.rgn[sec_cnt].destination_address = cpu_to_be64(addr);
>   	addr += fadump_conf->cpu_state_data_size;
> +	sec_cnt++;
>   
>   	/* hpte region section */
> -	fdm.hpte_region.request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
> -	fdm.hpte_region.source_data_type =
> -		cpu_to_be16(RTAS_FADUMP_HPTE_REGION);
> -	fdm.hpte_region.source_address = 0;
> -	fdm.hpte_region.source_len =
> -		cpu_to_be64(fadump_conf->hpte_region_size);
> -	fdm.hpte_region.destination_address = cpu_to_be64(addr);
> +	fdm.rgn[sec_cnt].request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
> +	fdm.rgn[sec_cnt].source_data_type = cpu_to_be16(RTAS_FADUMP_HPTE_REGION);
> +	fdm.rgn[sec_cnt].source_address = 0;
> +	fdm.rgn[sec_cnt].source_len = cpu_to_be64(fadump_conf->hpte_region_size);
> +	fdm.rgn[sec_cnt].destination_address = cpu_to_be64(addr);
>   	addr += fadump_conf->hpte_region_size;
> +	sec_cnt++;
>   
>   	/*
>   	 * Align boot memory area destination address to page boundary to
> @@ -115,15 +140,20 @@ static u64 rtas_fadump_init_mem_struct(struct fw_dump *fadump_conf)
>   	 */
>   	addr = PAGE_ALIGN(addr);
>   
> -	/* RMA region section */
> -	fdm.rmr_region.request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
> -	fdm.rmr_region.source_data_type =
> -		cpu_to_be16(RTAS_FADUMP_REAL_MODE_REGION);
> -	fdm.rmr_region.source_address = cpu_to_be64(0);
> -	fdm.rmr_region.source_len = cpu_to_be64(fadump_conf->boot_memory_size);
> -	fdm.rmr_region.destination_address = cpu_to_be64(addr);
> -	addr += fadump_conf->boot_memory_size;
> +	/* First boot memory region destination address */
> +	fadump_conf->boot_mem_dest_addr = addr;
> +	for (int i = 0; i < fadump_conf->boot_mem_regs_cnt; i++) {
> +		/* Boot memory regions */
> +		fdm.rgn[sec_cnt].request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
> +		fdm.rgn[sec_cnt].source_data_type = cpu_to_be16(RTAS_FADUMP_REAL_MODE_REGION);
> +		fdm.rgn[sec_cnt].source_address = cpu_to_be64(fadump_conf->boot_mem_addr[i]);
> +		fdm.rgn[sec_cnt].source_len = cpu_to_be64(fadump_conf->boot_mem_sz[i]);
> +		fdm.rgn[sec_cnt].destination_address = cpu_to_be64(addr);
> +		addr += fadump_conf->boot_mem_sz[i];
> +		sec_cnt++;
> +	}
>   
> +	fdm.header.dump_num_sections = cpu_to_be16(sec_cnt);
>   	rtas_fadump_update_config(fadump_conf, &fdm);
>   
>   	return addr;
> @@ -136,14 +166,21 @@ static u64 rtas_fadump_get_bootmem_min(void)
>   
>   static int rtas_fadump_register(struct fw_dump *fadump_conf)
>   {
> -	unsigned int wait_time;
> +	unsigned int wait_time, fdm_size;
>   	int rc, err = -EIO;
>   
> +	/*
> +	 * Platform requires the exact size of the Dump Memory Structure.
> +	 * Avoid including any unused rgns in the calculation, as this
> +	 * could result in a parameter error (-3) from the platform.
> +	 */
> +	fdm_size = sizeof(struct rtas_fadump_section_header);
> +	fdm_size += be16_to_cpu(fdm.header.dump_num_sections) * sizeof(struct rtas_fadump_section);
> +
>   	/* TODO: Add upper time limit for the delay */
>   	do {
>   		rc =  rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1,
> -				NULL, FADUMP_REGISTER, &fdm,
> -				sizeof(struct rtas_fadump_mem_struct));
> +				NULL, FADUMP_REGISTER, &fdm, fdm_size);
>   
>   		wait_time = rtas_busy_delay_time(rc);
>   		if (wait_time)
> @@ -161,9 +198,7 @@ static int rtas_fadump_register(struct fw_dump *fadump_conf)
>   		pr_err("Failed to register. Hardware Error(%d).\n", rc);
>   		break;
>   	case -3:
> -		if (!is_fadump_boot_mem_contiguous())
> -			pr_err("Can't have holes in boot memory area.\n");
> -		else if (!is_fadump_reserved_mem_contiguous())
> +		if (!is_fadump_reserved_mem_contiguous())
>   			pr_err("Can't have holes in reserved memory area.\n");
>   
>   		pr_err("Failed to register. Parameter Error(%d).\n", rc);
> @@ -316,11 +351,9 @@ static int __init rtas_fadump_build_cpu_notes(struct fw_dump *fadump_conf)
>   	u32 num_cpus, *note_buf;
>   	int i, rc = 0, cpu = 0;
>   	struct pt_regs regs;
> -	unsigned long addr;
>   	void *vaddr;
>   
> -	addr = be64_to_cpu(fdm_active->cpu_state_data.destination_address);
> -	vaddr = __va(addr);
> +	vaddr = (void *)fadump_conf->cpu_state_dest_vaddr;
>   
>   	reg_header = vaddr;
>   	if (be64_to_cpu(reg_header->magic_number) !=
> @@ -395,24 +428,48 @@ static int __init rtas_fadump_build_cpu_notes(struct fw_dump *fadump_conf)
>   static int __init rtas_fadump_process(struct fw_dump *fadump_conf)
>   {
>   	struct fadump_crash_info_header *fdh;
> -	int rc = 0;
> +	int i, 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;
> +	/* Check all regions. */
> +	for (i = 0; i < be16_to_cpu(fdm_active->header.dump_num_sections); i++) {
> +		int type = be16_to_cpu(fdm_active->rgn[i].source_data_type);
> +
> +		switch (type) {
> +		case RTAS_FADUMP_CPU_STATE_DATA:
> +		case RTAS_FADUMP_HPTE_REGION:
> +		case RTAS_FADUMP_REAL_MODE_REGION:
> +			if (fdm_active->rgn[i].error_flags != 0) {
> +				pr_err("Dump taken by platform is not valid (%d)\n", i);
> +				rc = -EINVAL;
> +			}
> +			if (fdm_active->rgn[i].bytes_dumped != fdm_active->rgn[i].source_len) {
> +				pr_err("Dump taken by platform is incomplete (%d)\n", i);
> +				rc = -EINVAL;
> +			}
> +			if (rc) {
> +				pr_warn("Region type: %u src addr: 0x%llx dest addr: 0x%llx\n",
> +					be16_to_cpu(fdm_active->rgn[i].source_data_type),
> +					be64_to_cpu(fdm_active->rgn[i].source_address),
> +					be64_to_cpu(fdm_active->rgn[i].destination_address));
> +				return rc;
> +			}
> +			break;
> +		default:
> +			/*
> +			 * If the first/crashed kernel added a new region type that the
> +			 * second/fadump kernel doesn't recognize, skip it and process
> +			 * assuming backward compatibility.
> +			 */
> +			pr_warn("Unknown region found: type: %u src addr: 0x%llx dest addr: 0x%llx\n",
> +				be16_to_cpu(fdm_active->rgn[i].source_data_type),
> +				be64_to_cpu(fdm_active->rgn[i].source_address),
> +				be64_to_cpu(fdm_active->rgn[i].destination_address));
> +			break;
> +		}
> +
>   	}
>   
>   	/* Validate the fadump crash info header */
> @@ -439,7 +496,6 @@ static int __init rtas_fadump_process(struct fw_dump *fadump_conf)
>   static void rtas_fadump_region_show(struct fw_dump *fadump_conf,
>   				    struct seq_file *m)
>   {
> -	const struct rtas_fadump_section *cpu_data_section;
>   	const struct rtas_fadump_mem_struct *fdm_ptr;
>   
>   	if (fdm_active)
> @@ -447,27 +503,42 @@ static void rtas_fadump_region_show(struct fw_dump *fadump_conf,
>   	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),
> -		   be64_to_cpu(cpu_data_section->destination_address) +
> -		   be64_to_cpu(cpu_data_section->source_len) - 1,
> -		   be64_to_cpu(cpu_data_section->source_len),
> -		   be64_to_cpu(cpu_data_section->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: Src: %#016llx, Dest: %#016llx, ",
> -		   be64_to_cpu(fdm_ptr->rmr_region.source_address),
> -		   be64_to_cpu(fdm_ptr->rmr_region.destination_address));
> -	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));
> +
> +	for (int i = 0; i < be16_to_cpu(fdm_ptr->header.dump_num_sections); i++) {
> +		int type = be16_to_cpu(fdm_ptr->rgn[i].source_data_type);
> +
> +		switch (type) {
> +		case RTAS_FADUMP_CPU_STATE_DATA:
> +			seq_printf(m, "CPU :[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n",
> +				   be64_to_cpu(fdm_ptr->rgn[i].destination_address),
> +				   be64_to_cpu(fdm_ptr->rgn[i].destination_address) +
> +				   be64_to_cpu(fdm_ptr->rgn[i].source_len) - 1,
> +				   be64_to_cpu(fdm_ptr->rgn[i].source_len),
> +				   be64_to_cpu(fdm_ptr->rgn[i].bytes_dumped));
> +			break;
> +		case RTAS_FADUMP_HPTE_REGION:
> +			seq_printf(m, "HPTE:[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n",
> +				   be64_to_cpu(fdm_ptr->rgn[i].destination_address),
> +				   be64_to_cpu(fdm_ptr->rgn[i].destination_address) +
> +				   be64_to_cpu(fdm_ptr->rgn[i].source_len) - 1,
> +				   be64_to_cpu(fdm_ptr->rgn[i].source_len),
> +				   be64_to_cpu(fdm_ptr->rgn[i].bytes_dumped));
> +			break;
> +		case RTAS_FADUMP_REAL_MODE_REGION:
> +			seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ",
> +				   be64_to_cpu(fdm_ptr->rgn[i].source_address),
> +				   be64_to_cpu(fdm_ptr->rgn[i].destination_address));
> +			seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n",
> +				   be64_to_cpu(fdm_ptr->rgn[i].source_len),
> +				   be64_to_cpu(fdm_ptr->rgn[i].bytes_dumped));
> +			break;
> +		default:
> +			seq_printf(m, "Unknown region type %d : Src: %#016llx, Dest: %#016llx, ",
> +				   type, be64_to_cpu(fdm_ptr->rgn[i].source_address),
> +				   be64_to_cpu(fdm_ptr->rgn[i].destination_address));
> +			break;
> +		}
> +	}
>   
>   	/* Dump is active. Show preserved area start address. */
>   	if (fdm_active) {
> @@ -483,6 +554,20 @@ static void rtas_fadump_trigger(struct fadump_crash_info_header *fdh,
>   	rtas_os_term((char *)msg);
>   }
>   
> +/* FADUMP_MAX_MEM_REGS or lower */
> +static int rtas_fadump_max_boot_mem_rgns(void)
> +{
> +	/*
> +	 * Version 1 of Kernel Assisted Dump Memory Structure (PAPR) supports 10 sections.
> +	 * With one each section taken for CPU state data & HPTE respectively, 8 sections
> +	 * can be used for boot memory regions.
> +	 *
> +	 * If new region(s) is(are) defined, maximum boot memory regions will decrease
> +	 * proportionally.
> +	 */
> +	return RTAS_FADUMP_MAX_BOOT_MEM_REGS;
> +}
> +
>   static struct fadump_ops rtas_fadump_ops = {
>   	.fadump_init_mem_struct		= rtas_fadump_init_mem_struct,
>   	.fadump_get_bootmem_min		= rtas_fadump_get_bootmem_min,
> @@ -492,6 +577,7 @@ static struct fadump_ops rtas_fadump_ops = {
>   	.fadump_process			= rtas_fadump_process,
>   	.fadump_region_show		= rtas_fadump_region_show,
>   	.fadump_trigger			= rtas_fadump_trigger,
> +	.fadump_max_boot_mem_rgns	= rtas_fadump_max_boot_mem_rgns,
>   };
>   
>   void __init rtas_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node)
> diff --git a/arch/powerpc/platforms/pseries/rtas-fadump.h b/arch/powerpc/platforms/pseries/rtas-fadump.h
> index fd59bd7ca9c3..6740f4981bb8 100644
> --- a/arch/powerpc/platforms/pseries/rtas-fadump.h
> +++ b/arch/powerpc/platforms/pseries/rtas-fadump.h
> @@ -29,6 +29,15 @@
>   /* Dump status flag */
>   #define RTAS_FADUMP_ERROR_FLAG		0x2000
>   
> +/*
> + * The Firmware Assisted Dump Memory structure supports a maximum of 10 sections
> + * in the dump memory structure. Presently, first two sections are used for
> + * CPU and HPTE data, while the remaining eight sections can be used for
> + * boot memory regions.
> + */
> +#define MAX_SECTIONS				10
> +#define RTAS_FADUMP_MAX_BOOT_MEM_REGS		8
> +
>   /* Kernel Dump section info */
>   struct rtas_fadump_section {
>   	__be32	request_flag;
> @@ -61,20 +70,15 @@ struct rtas_fadump_section_header {
>    * Firmware Assisted dump memory structure. This structure is required for
>    * registering future kernel dump with power firmware through rtas call.
>    *
> - * No disk dump option. Hence disk dump path string section is not included.
> + * In version 1, the platform permits one section header, dump-disk path
> + * and ten sections.
> + *
> + * Note: No disk dump option. Hence disk dump path string section is not
> + * included.
>    */
>   struct rtas_fadump_mem_struct {
>   	struct rtas_fadump_section_header	header;
> -
> -	/* Kernel dump sections */
> -	struct rtas_fadump_section		cpu_state_data;
> -	struct rtas_fadump_section		hpte_region;
> -
> -	/*
> -	 * TODO: Extend multiple boot memory regions support in the kernel
> -	 *       for this platform.
> -	 */
> -	struct rtas_fadump_section		rmr_region;
> +	struct rtas_fadump_section		rgn[MAX_SECTIONS];
>   };
>   
>   /*
Sourabh Jain Dec. 19, 2023, 6:58 a.m. UTC | #3
Hello Aditya,

On 17/12/23 14:11, Aditya Gupta wrote:
> Hi sourabh,
>
> On 06/12/23 01:48, Hari Bathini wrote:
>> From: Sourabh Jain <sourabhjain@linux.ibm.com>
>>
>> Currently, fadump on pseries assumes a single boot memory region even
>> though f/w supports more than one boot memory region. Add support for
>> more boot memory regions to make the implementation flexible for any
>> enhancements that introduce other region types. For this, rtas memory
>> structure for fadump is updated to have multiple boot memory regions
>> instead of just one. Additionally, methods responsible for creating
>> the fadump memory structure during both the first and second kernel
>> boot have been modified to take these multiple boot memory regions
>> into account. Also, a new callback has been added to the fadump_ops
>> structure to get the maximum boot memory regions supported by the
>> platform.
>>
>> Signed-off-by: Sourabh Jain <sourabhjain@linux.ibm.com>
>> Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
>> ---
>>   arch/powerpc/include/asm/fadump-internal.h   |   2 +-
>>   arch/powerpc/kernel/fadump.c                 |  27 +-
>>   arch/powerpc/platforms/powernv/opal-fadump.c |   8 +
>>   arch/powerpc/platforms/pseries/rtas-fadump.c | 258 ++++++++++++-------
>>   arch/powerpc/platforms/pseries/rtas-fadump.h |  26 +-
>>   5 files changed, 199 insertions(+), 122 deletions(-)
>>
>> diff --git a/arch/powerpc/include/asm/fadump-internal.h 
>> b/arch/powerpc/include/asm/fadump-internal.h
>> index 27f9e11eda28..b3956c400519 100644
>> --- a/arch/powerpc/include/asm/fadump-internal.h
>> +++ b/arch/powerpc/include/asm/fadump-internal.h
>> @@ -129,6 +129,7 @@ struct fadump_ops {
>>                         struct seq_file *m);
>>       void    (*fadump_trigger)(struct fadump_crash_info_header *fdh,
>>                     const char *msg);
>> +    int    (*fadump_max_boot_mem_rgns)(void);
>>   };
>>     /* Helper functions */
>> @@ -136,7 +137,6 @@ s32 __init fadump_setup_cpu_notes_buf(u32 num_cpus);
>>   void fadump_free_cpu_notes_buf(void);
>>   u32 *__init fadump_regs_to_elf_notes(u32 *buf, struct pt_regs *regs);
>>   void __init fadump_update_elfcore_header(char *bufp);
>> -bool is_fadump_boot_mem_contiguous(void);
>>   bool is_fadump_reserved_mem_contiguous(void);
>>     #else /* !CONFIG_PRESERVE_FA_DUMP */
>> diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c
>> index d14eda1e8589..757681658dda 100644
>> --- a/arch/powerpc/kernel/fadump.c
>> +++ b/arch/powerpc/kernel/fadump.c
>> @@ -222,28 +222,6 @@ static bool is_fadump_mem_area_contiguous(u64 
>> d_start, u64 d_end)
>>       return ret;
>>   }
>>   -/*
>> - * Returns true, if there are no holes in boot memory area,
>> - * false otherwise.
>> - */
>> -bool is_fadump_boot_mem_contiguous(void)
>> -{
>> -    unsigned long d_start, d_end;
>> -    bool ret = false;
>> -    int i;
>> -
>> -    for (i = 0; i < fw_dump.boot_mem_regs_cnt; i++) {
>> -        d_start = fw_dump.boot_mem_addr[i];
>> -        d_end   = d_start + fw_dump.boot_mem_sz[i];
>> -
>> -        ret = is_fadump_mem_area_contiguous(d_start, d_end);
>> -        if (!ret)
>> -            break;
>> -    }
>> -
>> -    return ret;
>> -}
>> -
>>   /*
>>    * Returns true, if there are no holes in reserved memory area,
>>    * false otherwise.
>> @@ -389,10 +367,11 @@ static unsigned long __init 
>> get_fadump_area_size(void)
>>   static int __init add_boot_mem_region(unsigned long rstart,
>>                         unsigned long rsize)
>>   {
>> +    int max_boot_mem_rgns = fw_dump.ops->fadump_max_boot_mem_rgns();
>>       int i = fw_dump.boot_mem_regs_cnt++;
>>   -    if (fw_dump.boot_mem_regs_cnt > FADUMP_MAX_MEM_REGS) {
>> -        fw_dump.boot_mem_regs_cnt = FADUMP_MAX_MEM_REGS;
>> +    if (fw_dump.boot_mem_regs_cnt > max_boot_mem_rgns) {
>> +        fw_dump.boot_mem_regs_cnt = max_boot_mem_rgns;
>>           return 0;
>>       }
>>   diff --git a/arch/powerpc/platforms/powernv/opal-fadump.c 
>> b/arch/powerpc/platforms/powernv/opal-fadump.c
>> index 964f464b1b0e..fa26c21a08d9 100644
>> --- a/arch/powerpc/platforms/powernv/opal-fadump.c
>> +++ b/arch/powerpc/platforms/powernv/opal-fadump.c
>> @@ -615,6 +615,13 @@ static void opal_fadump_trigger(struct 
>> fadump_crash_info_header *fdh,
>>           pr_emerg("No backend support for MPIPL!\n");
>>   }
>>   +/* FADUMP_MAX_MEM_REGS or lower */
>> +static int opal_fadump_max_boot_mem_rgns(void)
>> +{
>> +    return FADUMP_MAX_MEM_REGS;
>> +
>> +}
>> +
>>   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,
>> @@ -627,6 +634,7 @@ static struct fadump_ops opal_fadump_ops = {
>>       .fadump_process            = opal_fadump_process,
>>       .fadump_region_show        = opal_fadump_region_show,
>>       .fadump_trigger            = opal_fadump_trigger,
>> +    .fadump_max_boot_mem_rgns    = opal_fadump_max_boot_mem_rgns,
>>   };
>>     void __init opal_fadump_dt_scan(struct fw_dump *fadump_conf, u64 
>> node)
>> diff --git a/arch/powerpc/platforms/pseries/rtas-fadump.c 
>> b/arch/powerpc/platforms/pseries/rtas-fadump.c
>> index b5853e9fcc3c..1b05b4cefdfd 100644
>> --- a/arch/powerpc/platforms/pseries/rtas-fadump.c
>> +++ b/arch/powerpc/platforms/pseries/rtas-fadump.c
>> @@ -29,9 +29,6 @@ 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)
>>   {
>> -    fadump_conf->boot_mem_dest_addr =
>> -        be64_to_cpu(fdm->rmr_region.destination_address);
>> -
>>       fadump_conf->fadumphdr_addr = (fadump_conf->boot_mem_dest_addr +
>>                          fadump_conf->boot_memory_size);
>>   }
>> @@ -43,20 +40,52 @@ static void rtas_fadump_update_config(struct 
>> fw_dump *fadump_conf,
>>   static void __init rtas_fadump_get_config(struct fw_dump *fadump_conf,
>>                      const struct rtas_fadump_mem_struct *fdm)
>>   {
>> -    fadump_conf->boot_mem_addr[0] =
>> -        be64_to_cpu(fdm->rmr_region.source_address);
>> -    fadump_conf->boot_mem_sz[0] = 
>> be64_to_cpu(fdm->rmr_region.source_len);
>> -    fadump_conf->boot_memory_size = fadump_conf->boot_mem_sz[0];
>> +    unsigned long base, size, last_end, hole_size;
>>   -    fadump_conf->boot_mem_top = fadump_conf->boot_memory_size;
>> -    fadump_conf->boot_mem_regs_cnt = 1;
>> +    last_end = 0;
>> +    hole_size = 0;
>> +    fadump_conf->boot_memory_size = 0;
>> +    fadump_conf->boot_mem_regs_cnt = 0;
>> +    pr_debug("Boot memory regions:\n");
>> +    for (int i = 0; i < be16_to_cpu(fdm->header.dump_num_sections); 
>> i++) {
>> +        int type = be16_to_cpu(fdm->rgn[i].source_data_type);
>>   -    /*
>> -     * Start address of reserve dump area (permanent reservation) for
>> -     * re-registering FADump after dump capture.
>> -     */
>> -    fadump_conf->reserve_dump_area_start =
>> - be64_to_cpu(fdm->cpu_state_data.destination_address);
>> +        switch (type) {
>> +        case RTAS_FADUMP_CPU_STATE_DATA:
>> +            u64 addr = be64_to_cpu(fdm->rgn[i].destination_address);
>
> This caused a compiler error on my system:
>
> arch/powerpc/platforms/pseries/rtas-fadump.c: In function 
> ‘rtas_fadump_get_config’:
> arch/powerpc/platforms/pseries/rtas-fadump.c:56:4: error: a label can 
> only be part of a statement and a declaration is not a statement
>     u64 addr = be64_to_cpu(fdm->rgn[i].destination_address);
>     ^~~
>
> Probably the 'addr' local variable needs to be in it's own local scope.
>
> Adding the case block in brackets {} solved the error for me.
> Rest of the code looks good to me. I will also try to test the patch 
> series later.
>

Thanks for reporting, we will fix it in next version.

- Sourabh
diff mbox series

Patch

diff --git a/arch/powerpc/include/asm/fadump-internal.h b/arch/powerpc/include/asm/fadump-internal.h
index 27f9e11eda28..b3956c400519 100644
--- a/arch/powerpc/include/asm/fadump-internal.h
+++ b/arch/powerpc/include/asm/fadump-internal.h
@@ -129,6 +129,7 @@  struct fadump_ops {
 				      struct seq_file *m);
 	void	(*fadump_trigger)(struct fadump_crash_info_header *fdh,
 				  const char *msg);
+	int	(*fadump_max_boot_mem_rgns)(void);
 };
 
 /* Helper functions */
@@ -136,7 +137,6 @@  s32 __init fadump_setup_cpu_notes_buf(u32 num_cpus);
 void fadump_free_cpu_notes_buf(void);
 u32 *__init fadump_regs_to_elf_notes(u32 *buf, struct pt_regs *regs);
 void __init fadump_update_elfcore_header(char *bufp);
-bool is_fadump_boot_mem_contiguous(void);
 bool is_fadump_reserved_mem_contiguous(void);
 
 #else /* !CONFIG_PRESERVE_FA_DUMP */
diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c
index d14eda1e8589..757681658dda 100644
--- a/arch/powerpc/kernel/fadump.c
+++ b/arch/powerpc/kernel/fadump.c
@@ -222,28 +222,6 @@  static bool is_fadump_mem_area_contiguous(u64 d_start, u64 d_end)
 	return ret;
 }
 
-/*
- * Returns true, if there are no holes in boot memory area,
- * false otherwise.
- */
-bool is_fadump_boot_mem_contiguous(void)
-{
-	unsigned long d_start, d_end;
-	bool ret = false;
-	int i;
-
-	for (i = 0; i < fw_dump.boot_mem_regs_cnt; i++) {
-		d_start = fw_dump.boot_mem_addr[i];
-		d_end   = d_start + fw_dump.boot_mem_sz[i];
-
-		ret = is_fadump_mem_area_contiguous(d_start, d_end);
-		if (!ret)
-			break;
-	}
-
-	return ret;
-}
-
 /*
  * Returns true, if there are no holes in reserved memory area,
  * false otherwise.
@@ -389,10 +367,11 @@  static unsigned long __init get_fadump_area_size(void)
 static int __init add_boot_mem_region(unsigned long rstart,
 				      unsigned long rsize)
 {
+	int max_boot_mem_rgns = fw_dump.ops->fadump_max_boot_mem_rgns();
 	int i = fw_dump.boot_mem_regs_cnt++;
 
-	if (fw_dump.boot_mem_regs_cnt > FADUMP_MAX_MEM_REGS) {
-		fw_dump.boot_mem_regs_cnt = FADUMP_MAX_MEM_REGS;
+	if (fw_dump.boot_mem_regs_cnt > max_boot_mem_rgns) {
+		fw_dump.boot_mem_regs_cnt = max_boot_mem_rgns;
 		return 0;
 	}
 
diff --git a/arch/powerpc/platforms/powernv/opal-fadump.c b/arch/powerpc/platforms/powernv/opal-fadump.c
index 964f464b1b0e..fa26c21a08d9 100644
--- a/arch/powerpc/platforms/powernv/opal-fadump.c
+++ b/arch/powerpc/platforms/powernv/opal-fadump.c
@@ -615,6 +615,13 @@  static void opal_fadump_trigger(struct fadump_crash_info_header *fdh,
 		pr_emerg("No backend support for MPIPL!\n");
 }
 
+/* FADUMP_MAX_MEM_REGS or lower */
+static int opal_fadump_max_boot_mem_rgns(void)
+{
+	return FADUMP_MAX_MEM_REGS;
+
+}
+
 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,
@@ -627,6 +634,7 @@  static struct fadump_ops opal_fadump_ops = {
 	.fadump_process			= opal_fadump_process,
 	.fadump_region_show		= opal_fadump_region_show,
 	.fadump_trigger			= opal_fadump_trigger,
+	.fadump_max_boot_mem_rgns	= opal_fadump_max_boot_mem_rgns,
 };
 
 void __init opal_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node)
diff --git a/arch/powerpc/platforms/pseries/rtas-fadump.c b/arch/powerpc/platforms/pseries/rtas-fadump.c
index b5853e9fcc3c..1b05b4cefdfd 100644
--- a/arch/powerpc/platforms/pseries/rtas-fadump.c
+++ b/arch/powerpc/platforms/pseries/rtas-fadump.c
@@ -29,9 +29,6 @@  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)
 {
-	fadump_conf->boot_mem_dest_addr =
-		be64_to_cpu(fdm->rmr_region.destination_address);
-
 	fadump_conf->fadumphdr_addr = (fadump_conf->boot_mem_dest_addr +
 				       fadump_conf->boot_memory_size);
 }
@@ -43,20 +40,52 @@  static void rtas_fadump_update_config(struct fw_dump *fadump_conf,
 static void __init rtas_fadump_get_config(struct fw_dump *fadump_conf,
 				   const struct rtas_fadump_mem_struct *fdm)
 {
-	fadump_conf->boot_mem_addr[0] =
-		be64_to_cpu(fdm->rmr_region.source_address);
-	fadump_conf->boot_mem_sz[0] = be64_to_cpu(fdm->rmr_region.source_len);
-	fadump_conf->boot_memory_size = fadump_conf->boot_mem_sz[0];
+	unsigned long base, size, last_end, hole_size;
 
-	fadump_conf->boot_mem_top = fadump_conf->boot_memory_size;
-	fadump_conf->boot_mem_regs_cnt = 1;
+	last_end = 0;
+	hole_size = 0;
+	fadump_conf->boot_memory_size = 0;
+	fadump_conf->boot_mem_regs_cnt = 0;
+	pr_debug("Boot memory regions:\n");
+	for (int i = 0; i < be16_to_cpu(fdm->header.dump_num_sections); i++) {
+		int type = be16_to_cpu(fdm->rgn[i].source_data_type);
 
-	/*
-	 * Start address of reserve dump area (permanent reservation) for
-	 * re-registering FADump after dump capture.
-	 */
-	fadump_conf->reserve_dump_area_start =
-		be64_to_cpu(fdm->cpu_state_data.destination_address);
+		switch (type) {
+		case RTAS_FADUMP_CPU_STATE_DATA:
+			u64 addr = be64_to_cpu(fdm->rgn[i].destination_address);
+
+			fadump_conf->cpu_state_dest_vaddr = (u64)__va(addr);
+			/*
+			 * Start address of reserve dump area (permanent reservation) for
+			 * re-registering FADump after dump capture.
+			 */
+			fadump_conf->reserve_dump_area_start = addr;
+			break;
+		case RTAS_FADUMP_HPTE_REGION:
+			/* Not processed currently. */
+			break;
+		case RTAS_FADUMP_REAL_MODE_REGION:
+			base = be64_to_cpu(fdm->rgn[i].source_address);
+			size = be64_to_cpu(fdm->rgn[i].source_len);
+			pr_debug("\t[%03d] base: 0x%lx, size: 0x%lx\n", i, base, size);
+			if (!base) {
+				fadump_conf->boot_mem_dest_addr =
+					be64_to_cpu(fdm->rgn[i].destination_address);
+			}
+
+			fadump_conf->boot_mem_addr[fadump_conf->boot_mem_regs_cnt] = base;
+			fadump_conf->boot_mem_sz[fadump_conf->boot_mem_regs_cnt] = size;
+			fadump_conf->boot_memory_size += size;
+			hole_size += (base - last_end);
+			last_end = base + size;
+			fadump_conf->boot_mem_regs_cnt++;
+			break;
+		default:
+			pr_warn("Section type %d unsupported on this kernel. Ignoring!\n", type);
+			break;
+		}
+	}
+	fadump_conf->boot_mem_top = fadump_conf->boot_memory_size + hole_size;
 
 	rtas_fadump_update_config(fadump_conf, fdm);
 }
@@ -64,16 +93,15 @@  static void __init rtas_fadump_get_config(struct fw_dump *fadump_conf,
 static u64 rtas_fadump_init_mem_struct(struct fw_dump *fadump_conf)
 {
 	u64 addr = fadump_conf->reserve_dump_area_start;
+	u16 sec_cnt = 0;
 
 	memset(&fdm, 0, sizeof(struct rtas_fadump_mem_struct));
 	addr = addr & PAGE_MASK;
 
 	fdm.header.dump_format_version = cpu_to_be32(0x00000001);
-	fdm.header.dump_num_sections = cpu_to_be16(3);
 	fdm.header.dump_status_flag = 0;
 	fdm.header.offset_first_dump_section =
-		cpu_to_be32((u32)offsetof(struct rtas_fadump_mem_struct,
-					  cpu_state_data));
+		cpu_to_be32((u32)offsetof(struct rtas_fadump_mem_struct, rgn));
 
 	/*
 	 * Fields for disk dump option.
@@ -89,25 +117,22 @@  static u64 rtas_fadump_init_mem_struct(struct fw_dump *fadump_conf)
 
 	/* Kernel dump sections */
 	/* cpu state data section. */
-	fdm.cpu_state_data.request_flag =
-		cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
-	fdm.cpu_state_data.source_data_type =
-		cpu_to_be16(RTAS_FADUMP_CPU_STATE_DATA);
-	fdm.cpu_state_data.source_address = 0;
-	fdm.cpu_state_data.source_len =
-		cpu_to_be64(fadump_conf->cpu_state_data_size);
-	fdm.cpu_state_data.destination_address = cpu_to_be64(addr);
+	fdm.rgn[sec_cnt].request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
+	fdm.rgn[sec_cnt].source_data_type = cpu_to_be16(RTAS_FADUMP_CPU_STATE_DATA);
+	fdm.rgn[sec_cnt].source_address = 0;
+	fdm.rgn[sec_cnt].source_len = cpu_to_be64(fadump_conf->cpu_state_data_size);
+	fdm.rgn[sec_cnt].destination_address = cpu_to_be64(addr);
 	addr += fadump_conf->cpu_state_data_size;
+	sec_cnt++;
 
 	/* hpte region section */
-	fdm.hpte_region.request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
-	fdm.hpte_region.source_data_type =
-		cpu_to_be16(RTAS_FADUMP_HPTE_REGION);
-	fdm.hpte_region.source_address = 0;
-	fdm.hpte_region.source_len =
-		cpu_to_be64(fadump_conf->hpte_region_size);
-	fdm.hpte_region.destination_address = cpu_to_be64(addr);
+	fdm.rgn[sec_cnt].request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
+	fdm.rgn[sec_cnt].source_data_type = cpu_to_be16(RTAS_FADUMP_HPTE_REGION);
+	fdm.rgn[sec_cnt].source_address = 0;
+	fdm.rgn[sec_cnt].source_len = cpu_to_be64(fadump_conf->hpte_region_size);
+	fdm.rgn[sec_cnt].destination_address = cpu_to_be64(addr);
 	addr += fadump_conf->hpte_region_size;
+	sec_cnt++;
 
 	/*
 	 * Align boot memory area destination address to page boundary to
@@ -115,15 +140,20 @@  static u64 rtas_fadump_init_mem_struct(struct fw_dump *fadump_conf)
 	 */
 	addr = PAGE_ALIGN(addr);
 
-	/* RMA region section */
-	fdm.rmr_region.request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
-	fdm.rmr_region.source_data_type =
-		cpu_to_be16(RTAS_FADUMP_REAL_MODE_REGION);
-	fdm.rmr_region.source_address = cpu_to_be64(0);
-	fdm.rmr_region.source_len = cpu_to_be64(fadump_conf->boot_memory_size);
-	fdm.rmr_region.destination_address = cpu_to_be64(addr);
-	addr += fadump_conf->boot_memory_size;
+	/* First boot memory region destination address */
+	fadump_conf->boot_mem_dest_addr = addr;
+	for (int i = 0; i < fadump_conf->boot_mem_regs_cnt; i++) {
+		/* Boot memory regions */
+		fdm.rgn[sec_cnt].request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
+		fdm.rgn[sec_cnt].source_data_type = cpu_to_be16(RTAS_FADUMP_REAL_MODE_REGION);
+		fdm.rgn[sec_cnt].source_address = cpu_to_be64(fadump_conf->boot_mem_addr[i]);
+		fdm.rgn[sec_cnt].source_len = cpu_to_be64(fadump_conf->boot_mem_sz[i]);
+		fdm.rgn[sec_cnt].destination_address = cpu_to_be64(addr);
+		addr += fadump_conf->boot_mem_sz[i];
+		sec_cnt++;
+	}
 
+	fdm.header.dump_num_sections = cpu_to_be16(sec_cnt);
 	rtas_fadump_update_config(fadump_conf, &fdm);
 
 	return addr;
@@ -136,14 +166,21 @@  static u64 rtas_fadump_get_bootmem_min(void)
 
 static int rtas_fadump_register(struct fw_dump *fadump_conf)
 {
-	unsigned int wait_time;
+	unsigned int wait_time, fdm_size;
 	int rc, err = -EIO;
 
+	/*
+	 * Platform requires the exact size of the Dump Memory Structure.
+	 * Avoid including any unused rgns in the calculation, as this
+	 * could result in a parameter error (-3) from the platform.
+	 */
+	fdm_size = sizeof(struct rtas_fadump_section_header);
+	fdm_size += be16_to_cpu(fdm.header.dump_num_sections) * sizeof(struct rtas_fadump_section);
+
 	/* TODO: Add upper time limit for the delay */
 	do {
 		rc =  rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1,
-				NULL, FADUMP_REGISTER, &fdm,
-				sizeof(struct rtas_fadump_mem_struct));
+				NULL, FADUMP_REGISTER, &fdm, fdm_size);
 
 		wait_time = rtas_busy_delay_time(rc);
 		if (wait_time)
@@ -161,9 +198,7 @@  static int rtas_fadump_register(struct fw_dump *fadump_conf)
 		pr_err("Failed to register. Hardware Error(%d).\n", rc);
 		break;
 	case -3:
-		if (!is_fadump_boot_mem_contiguous())
-			pr_err("Can't have holes in boot memory area.\n");
-		else if (!is_fadump_reserved_mem_contiguous())
+		if (!is_fadump_reserved_mem_contiguous())
 			pr_err("Can't have holes in reserved memory area.\n");
 
 		pr_err("Failed to register. Parameter Error(%d).\n", rc);
@@ -316,11 +351,9 @@  static int __init rtas_fadump_build_cpu_notes(struct fw_dump *fadump_conf)
 	u32 num_cpus, *note_buf;
 	int i, rc = 0, cpu = 0;
 	struct pt_regs regs;
-	unsigned long addr;
 	void *vaddr;
 
-	addr = be64_to_cpu(fdm_active->cpu_state_data.destination_address);
-	vaddr = __va(addr);
+	vaddr = (void *)fadump_conf->cpu_state_dest_vaddr;
 
 	reg_header = vaddr;
 	if (be64_to_cpu(reg_header->magic_number) !=
@@ -395,24 +428,48 @@  static int __init rtas_fadump_build_cpu_notes(struct fw_dump *fadump_conf)
 static int __init rtas_fadump_process(struct fw_dump *fadump_conf)
 {
 	struct fadump_crash_info_header *fdh;
-	int rc = 0;
+	int i, 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;
+	/* Check all regions. */
+	for (i = 0; i < be16_to_cpu(fdm_active->header.dump_num_sections); i++) {
+		int type = be16_to_cpu(fdm_active->rgn[i].source_data_type);
+
+		switch (type) {
+		case RTAS_FADUMP_CPU_STATE_DATA:
+		case RTAS_FADUMP_HPTE_REGION:
+		case RTAS_FADUMP_REAL_MODE_REGION:
+			if (fdm_active->rgn[i].error_flags != 0) {
+				pr_err("Dump taken by platform is not valid (%d)\n", i);
+				rc = -EINVAL;
+			}
+			if (fdm_active->rgn[i].bytes_dumped != fdm_active->rgn[i].source_len) {
+				pr_err("Dump taken by platform is incomplete (%d)\n", i);
+				rc = -EINVAL;
+			}
+			if (rc) {
+				pr_warn("Region type: %u src addr: 0x%llx dest addr: 0x%llx\n",
+					be16_to_cpu(fdm_active->rgn[i].source_data_type),
+					be64_to_cpu(fdm_active->rgn[i].source_address),
+					be64_to_cpu(fdm_active->rgn[i].destination_address));
+				return rc;
+			}
+			break;
+		default:
+			/*
+			 * If the first/crashed kernel added a new region type that the
+			 * second/fadump kernel doesn't recognize, skip it and process
+			 * assuming backward compatibility.
+			 */
+			pr_warn("Unknown region found: type: %u src addr: 0x%llx dest addr: 0x%llx\n",
+				be16_to_cpu(fdm_active->rgn[i].source_data_type),
+				be64_to_cpu(fdm_active->rgn[i].source_address),
+				be64_to_cpu(fdm_active->rgn[i].destination_address));
+			break;
+		}
+
 	}
 
 	/* Validate the fadump crash info header */
@@ -439,7 +496,6 @@  static int __init rtas_fadump_process(struct fw_dump *fadump_conf)
 static void rtas_fadump_region_show(struct fw_dump *fadump_conf,
 				    struct seq_file *m)
 {
-	const struct rtas_fadump_section *cpu_data_section;
 	const struct rtas_fadump_mem_struct *fdm_ptr;
 
 	if (fdm_active)
@@ -447,27 +503,42 @@  static void rtas_fadump_region_show(struct fw_dump *fadump_conf,
 	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),
-		   be64_to_cpu(cpu_data_section->destination_address) +
-		   be64_to_cpu(cpu_data_section->source_len) - 1,
-		   be64_to_cpu(cpu_data_section->source_len),
-		   be64_to_cpu(cpu_data_section->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: Src: %#016llx, Dest: %#016llx, ",
-		   be64_to_cpu(fdm_ptr->rmr_region.source_address),
-		   be64_to_cpu(fdm_ptr->rmr_region.destination_address));
-	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));
+
+	for (int i = 0; i < be16_to_cpu(fdm_ptr->header.dump_num_sections); i++) {
+		int type = be16_to_cpu(fdm_ptr->rgn[i].source_data_type);
+
+		switch (type) {
+		case RTAS_FADUMP_CPU_STATE_DATA:
+			seq_printf(m, "CPU :[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n",
+				   be64_to_cpu(fdm_ptr->rgn[i].destination_address),
+				   be64_to_cpu(fdm_ptr->rgn[i].destination_address) +
+				   be64_to_cpu(fdm_ptr->rgn[i].source_len) - 1,
+				   be64_to_cpu(fdm_ptr->rgn[i].source_len),
+				   be64_to_cpu(fdm_ptr->rgn[i].bytes_dumped));
+			break;
+		case RTAS_FADUMP_HPTE_REGION:
+			seq_printf(m, "HPTE:[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n",
+				   be64_to_cpu(fdm_ptr->rgn[i].destination_address),
+				   be64_to_cpu(fdm_ptr->rgn[i].destination_address) +
+				   be64_to_cpu(fdm_ptr->rgn[i].source_len) - 1,
+				   be64_to_cpu(fdm_ptr->rgn[i].source_len),
+				   be64_to_cpu(fdm_ptr->rgn[i].bytes_dumped));
+			break;
+		case RTAS_FADUMP_REAL_MODE_REGION:
+			seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ",
+				   be64_to_cpu(fdm_ptr->rgn[i].source_address),
+				   be64_to_cpu(fdm_ptr->rgn[i].destination_address));
+			seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n",
+				   be64_to_cpu(fdm_ptr->rgn[i].source_len),
+				   be64_to_cpu(fdm_ptr->rgn[i].bytes_dumped));
+			break;
+		default:
+			seq_printf(m, "Unknown region type %d : Src: %#016llx, Dest: %#016llx, ",
+				   type, be64_to_cpu(fdm_ptr->rgn[i].source_address),
+				   be64_to_cpu(fdm_ptr->rgn[i].destination_address));
+			break;
+		}
+	}
 
 	/* Dump is active. Show preserved area start address. */
 	if (fdm_active) {
@@ -483,6 +554,20 @@  static void rtas_fadump_trigger(struct fadump_crash_info_header *fdh,
 	rtas_os_term((char *)msg);
 }
 
+/* FADUMP_MAX_MEM_REGS or lower */
+static int rtas_fadump_max_boot_mem_rgns(void)
+{
+	/*
+	 * Version 1 of Kernel Assisted Dump Memory Structure (PAPR) supports 10 sections.
+	 * With one each section taken for CPU state data & HPTE respectively, 8 sections
+	 * can be used for boot memory regions.
+	 *
+	 * If new region(s) is(are) defined, maximum boot memory regions will decrease
+	 * proportionally.
+	 */
+	return RTAS_FADUMP_MAX_BOOT_MEM_REGS;
+}
+
 static struct fadump_ops rtas_fadump_ops = {
 	.fadump_init_mem_struct		= rtas_fadump_init_mem_struct,
 	.fadump_get_bootmem_min		= rtas_fadump_get_bootmem_min,
@@ -492,6 +577,7 @@  static struct fadump_ops rtas_fadump_ops = {
 	.fadump_process			= rtas_fadump_process,
 	.fadump_region_show		= rtas_fadump_region_show,
 	.fadump_trigger			= rtas_fadump_trigger,
+	.fadump_max_boot_mem_rgns	= rtas_fadump_max_boot_mem_rgns,
 };
 
 void __init rtas_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node)
diff --git a/arch/powerpc/platforms/pseries/rtas-fadump.h b/arch/powerpc/platforms/pseries/rtas-fadump.h
index fd59bd7ca9c3..6740f4981bb8 100644
--- a/arch/powerpc/platforms/pseries/rtas-fadump.h
+++ b/arch/powerpc/platforms/pseries/rtas-fadump.h
@@ -29,6 +29,15 @@ 
 /* Dump status flag */
 #define RTAS_FADUMP_ERROR_FLAG		0x2000
 
+/*
+ * The Firmware Assisted Dump Memory structure supports a maximum of 10 sections
+ * in the dump memory structure. Presently, first two sections are used for
+ * CPU and HPTE data, while the remaining eight sections can be used for
+ * boot memory regions.
+ */
+#define MAX_SECTIONS				10
+#define RTAS_FADUMP_MAX_BOOT_MEM_REGS		8
+
 /* Kernel Dump section info */
 struct rtas_fadump_section {
 	__be32	request_flag;
@@ -61,20 +70,15 @@  struct rtas_fadump_section_header {
  * Firmware Assisted dump memory structure. This structure is required for
  * registering future kernel dump with power firmware through rtas call.
  *
- * No disk dump option. Hence disk dump path string section is not included.
+ * In version 1, the platform permits one section header, dump-disk path
+ * and ten sections.
+ *
+ * Note: No disk dump option. Hence disk dump path string section is not
+ * included.
  */
 struct rtas_fadump_mem_struct {
 	struct rtas_fadump_section_header	header;
-
-	/* Kernel dump sections */
-	struct rtas_fadump_section		cpu_state_data;
-	struct rtas_fadump_section		hpte_region;
-
-	/*
-	 * TODO: Extend multiple boot memory regions support in the kernel
-	 *       for this platform.
-	 */
-	struct rtas_fadump_section		rmr_region;
+	struct rtas_fadump_section		rgn[MAX_SECTIONS];
 };
 
 /*