static atomic_t cpus_in_fadump;
 static DEFINE_MUTEX(fadump_mutex);
 
-static struct fadump_mrange_info crash_mrange_info = { "crash", NULL, 0, 0, 0, false };
-
 #define RESERVED_RNGS_SZ       16384 /* 16K - 128 entries */
 #define RESERVED_RNGS_CNT      (RESERVED_RNGS_SZ / \
                                 sizeof(struct fadump_memory_range))
        size = PAGE_ALIGN(size);
        size += fw_dump.boot_memory_size;
        size += sizeof(struct fadump_crash_info_header);
-       size += sizeof(struct elfhdr); /* ELF core header.*/
-       size += sizeof(struct elf_phdr); /* place holder for cpu notes */
-       /* Program headers for crash memory regions. */
-       size += sizeof(struct elf_phdr) * (memblock_num_regions(memory) + 2);
-
-       size = PAGE_ALIGN(size);
 
        /* This is to hold kernel metadata on platforms that support it */
        size += (fw_dump.ops->fadump_get_metadata_size ?
        return 0;
 }
 
-static int fadump_exclude_reserved_area(u64 start, u64 end)
-{
-       u64 ra_start, ra_end;
-       int ret = 0;
-
-       ra_start = fw_dump.reserve_dump_area_start;
-       ra_end = ra_start + fw_dump.reserve_dump_area_size;
-
-       if ((ra_start < end) && (ra_end > start)) {
-               if ((start < ra_start) && (end > ra_end)) {
-                       ret = fadump_add_mem_range(&crash_mrange_info,
-                                                  start, ra_start);
-                       if (ret)
-                               return ret;
-
-                       ret = fadump_add_mem_range(&crash_mrange_info,
-                                                  ra_end, end);
-               } else if (start < ra_start) {
-                       ret = fadump_add_mem_range(&crash_mrange_info,
-                                                  start, ra_start);
-               } else if (ra_end < end) {
-                       ret = fadump_add_mem_range(&crash_mrange_info,
-                                                  ra_end, end);
-               }
-       } else
-               ret = fadump_add_mem_range(&crash_mrange_info, start, end);
-
-       return ret;
-}
-
 static int fadump_init_elfcore_header(char *bufp)
 {
        struct elfhdr *elf;
        return 0;
 }
 
-/*
- * Traverse through memblock structure and setup crash memory ranges. These
- * ranges will be used create PT_LOAD program headers in elfcore header.
- */
-static int fadump_setup_crash_memory_ranges(void)
-{
-       u64 i, start, end;
-       int ret;
-
-       pr_debug("Setup crash memory ranges.\n");
-       crash_mrange_info.mem_range_cnt = 0;
-
-       /*
-        * Boot memory region(s) registered with firmware are moved to
-        * different location at the time of crash. Create separate program
-        * header(s) for this memory chunk(s) with the correct offset.
-        */
-       for (i = 0; i < fw_dump.boot_mem_regs_cnt; i++) {
-               start = fw_dump.boot_mem_addr[i];
-               end = start + fw_dump.boot_mem_sz[i];
-               ret = fadump_add_mem_range(&crash_mrange_info, start, end);
-               if (ret)
-                       return ret;
-       }
-
-       for_each_mem_range(i, &start, &end) {
-               /*
-                * skip the memory chunk that is already added
-                * (0 through boot_memory_top).
-                */
-               if (start < fw_dump.boot_mem_top) {
-                       if (end > fw_dump.boot_mem_top)
-                               start = fw_dump.boot_mem_top;
-                       else
-                               continue;
-               }
-
-               /* add this range excluding the reserved dump area. */
-               ret = fadump_exclude_reserved_area(start, end);
-               if (ret)
-                       return ret;
-       }
-
-       return 0;
-}
-
 /*
  * If the given physical address falls within the boot memory region then
  * return the relocated address that points to the dump region reserved
        return raddr;
 }
 
-static int fadump_create_elfcore_headers(char *bufp)
+static void __init populate_elf_pt_load(struct elf_phdr *phdr, u64 start,
+                            u64 size, unsigned long long offset)
 {
-       unsigned long long raddr, offset;
-       struct elf_phdr *phdr;
+       phdr->p_align   = 0;
+       phdr->p_memsz   = size;
+       phdr->p_filesz  = size;
+       phdr->p_paddr   = start;
+       phdr->p_offset  = offset;
+       phdr->p_type    = PT_LOAD;
+       phdr->p_flags   = PF_R|PF_W|PF_X;
+       phdr->p_vaddr   = (unsigned long)__va(start);
+}
+
+static void __init fadump_populate_elfcorehdr(struct fadump_crash_info_header *fdh)
+{
+       char *bufp;
        struct elfhdr *elf;
-       int i, j;
+       struct elf_phdr *phdr;
+       u64 boot_mem_dest_offset;
+       unsigned long long i, ra_start, ra_end, ra_size, mstart, mend;
 
+       bufp = (char *) fw_dump.elfcorehdr_addr;
        fadump_init_elfcore_header(bufp);
        elf = (struct elfhdr *)bufp;
        bufp += sizeof(struct elfhdr);
 
        /*
-        * setup ELF PT_NOTE, place holder for cpu notes info. The notes info
-        * will be populated during second kernel boot after crash. Hence
-        * this PT_NOTE will always be the first elf note.
+        * Set up ELF PT_NOTE, a placeholder for CPU notes information.
+        * The notes info will be populated later by platform-specific code.
+        * Hence, this PT_NOTE will always be the first ELF note.
         *
         * NOTE: Any new ELF note addition should be placed after this note.
         */
        phdr = (struct elf_phdr *)bufp;
        bufp += sizeof(struct elf_phdr);
        phdr->p_type = PT_NOTE;
-       phdr->p_flags = 0;
-       phdr->p_vaddr = 0;
-       phdr->p_align = 0;
-
-       phdr->p_offset = 0;
-       phdr->p_paddr = 0;
-       phdr->p_filesz = 0;
-       phdr->p_memsz = 0;
-
+       phdr->p_flags   = 0;
+       phdr->p_vaddr   = 0;
+       phdr->p_align   = 0;
+       phdr->p_offset  = 0;
+       phdr->p_paddr   = 0;
+       phdr->p_filesz  = 0;
+       phdr->p_memsz   = 0;
+       /* Increment number of program headers. */
        (elf->e_phnum)++;
 
        /* setup ELF PT_NOTE for vmcoreinfo */
        phdr->p_flags   = 0;
        phdr->p_vaddr   = 0;
        phdr->p_align   = 0;
-
-       phdr->p_paddr   = fadump_relocate(paddr_vmcoreinfo_note());
-       phdr->p_offset  = phdr->p_paddr;
-       phdr->p_memsz   = phdr->p_filesz = VMCOREINFO_NOTE_SIZE;
-
+       phdr->p_paddr   = phdr->p_offset = fdh->vmcoreinfo_raddr;
+       phdr->p_memsz   = phdr->p_filesz = fdh->vmcoreinfo_size;
        /* Increment number of program headers. */
        (elf->e_phnum)++;
 
-       /* setup PT_LOAD sections. */
-       j = 0;
-       offset = 0;
-       raddr = fw_dump.boot_mem_addr[0];
-       for (i = 0; i < crash_mrange_info.mem_range_cnt; i++) {
-               u64 mbase, msize;
-
-               mbase = crash_mrange_info.mem_ranges[i].base;
-               msize = crash_mrange_info.mem_ranges[i].size;
-               if (!msize)
-                       continue;
-
+       /*
+        * Setup PT_LOAD sections. first include boot memory regions
+        * and then add rest of the memory regions.
+        */
+       boot_mem_dest_offset = fw_dump.boot_mem_dest_addr;
+       for (i = 0; i < fw_dump.boot_mem_regs_cnt; i++) {
                phdr = (struct elf_phdr *)bufp;
                bufp += sizeof(struct elf_phdr);
-               phdr->p_type    = PT_LOAD;
-               phdr->p_flags   = PF_R|PF_W|PF_X;
-               phdr->p_offset  = mbase;
-
-               if (mbase == raddr) {
-                       /*
-                        * The entire real memory region will be moved by
-                        * firmware to the specified destination_address.
-                        * Hence set the correct offset.
-                        */
-                       phdr->p_offset = fw_dump.boot_mem_dest_addr + offset;
-                       if (j < (fw_dump.boot_mem_regs_cnt - 1)) {
-                               offset += fw_dump.boot_mem_sz[j];
-                               raddr = fw_dump.boot_mem_addr[++j];
-                       }
+               populate_elf_pt_load(phdr, fw_dump.boot_mem_addr[i],
+                                    fw_dump.boot_mem_sz[i],
+                                    boot_mem_dest_offset);
+               /* Increment number of program headers. */
+               (elf->e_phnum)++;
+               boot_mem_dest_offset += fw_dump.boot_mem_sz[i];
+       }
+
+       /* Memory reserved for fadump in first kernel */
+       ra_start = fw_dump.reserve_dump_area_start;
+       ra_size = get_fadump_area_size();
+       ra_end = ra_start + ra_size;
+
+       phdr = (struct elf_phdr *)bufp;
+       for_each_mem_range(i, &mstart, &mend) {
+               /* Boot memory regions already added, skip them now */
+               if (mstart < fw_dump.boot_mem_top) {
+                       if (mend > fw_dump.boot_mem_top)
+                               mstart = fw_dump.boot_mem_top;
+                       else
+                               continue;
                }
 
-               phdr->p_paddr = mbase;
-               phdr->p_vaddr = (unsigned long)__va(mbase);
-               phdr->p_filesz = msize;
-               phdr->p_memsz = msize;
-               phdr->p_align = 0;
+               /* Handle memblock regions overlaps with fadump reserved area */
+               if ((ra_start < mend) && (ra_end > mstart)) {
+                       if ((mstart < ra_start) && (mend > ra_end)) {
+                               populate_elf_pt_load(phdr, mstart, ra_start - mstart, mstart);
+                               /* Increment number of program headers. */
+                               (elf->e_phnum)++;
+                               bufp += sizeof(struct elf_phdr);
+                               phdr = (struct elf_phdr *)bufp;
+                               populate_elf_pt_load(phdr, ra_end, mend - ra_end, ra_end);
+                       } else if (mstart < ra_start) {
+                               populate_elf_pt_load(phdr, mstart, ra_start - mstart, mstart);
+                       } else if (ra_end < mend) {
+                               populate_elf_pt_load(phdr, ra_end, mend - ra_end, ra_end);
+                       }
+               } else {
+               /* No overlap with fadump reserved memory region */
+                       populate_elf_pt_load(phdr, mstart, mend - mstart, mstart);
+               }
 
                /* Increment number of program headers. */
                (elf->e_phnum)++;
+               bufp += sizeof(struct elf_phdr);
+               phdr = (struct elf_phdr *) bufp;
        }
-       return 0;
 }
 
 static unsigned long init_fadump_header(unsigned long addr)
 
        memset(fdh, 0, sizeof(struct fadump_crash_info_header));
        fdh->magic_number = FADUMP_CRASH_INFO_MAGIC;
-       fdh->elfcorehdr_addr = addr;
+       fdh->version = FADUMP_HEADER_VERSION;
        /* We will set the crashing cpu id in crash_fadump() during crash. */
        fdh->crashing_cpu = FADUMP_CPU_UNKNOWN;
+
+       /*
+        * The physical address and size of vmcoreinfo are required in the
+        * second kernel to prepare elfcorehdr.
+        */
+       fdh->vmcoreinfo_raddr = fadump_relocate(paddr_vmcoreinfo_note());
+       fdh->vmcoreinfo_size = VMCOREINFO_NOTE_SIZE;
+
+
+       fdh->pt_regs_sz = sizeof(struct pt_regs);
        /*
         * When LPAR is terminated by PYHP, ensure all possible CPUs'
         * register data is processed while exporting the vmcore.
         */
        fdh->cpu_mask = *cpu_possible_mask;
+       fdh->cpu_mask_sz = sizeof(struct cpumask);
 
        return addr;
 }
 static int register_fadump(void)
 {
        unsigned long addr;
-       void *vaddr;
-       int ret;
 
        /*
         * If no memory is reserved then we can not register for firmware-
        if (!fw_dump.reserve_dump_area_size)
                return -ENODEV;
 
-       ret = fadump_setup_crash_memory_ranges();
-       if (ret)
-               return ret;
-
        addr = fw_dump.fadumphdr_addr;
 
        /* Initialize fadump crash info header. */
        addr = init_fadump_header(addr);
-       vaddr = __va(addr);
-
-       pr_debug("Creating ELF core headers at %#016lx\n", addr);
-       fadump_create_elfcore_headers(vaddr);
 
        /* register the future kernel dump with firmware. */
        pr_debug("Registering for firmware-assisted kernel dump...\n");
        } else if (fw_dump.dump_registered) {
                /* Un-register Firmware-assisted dump if it was registered. */
                fw_dump.ops->fadump_unregister(&fw_dump);
-               fadump_free_mem_ranges(&crash_mrange_info);
        }
 
        if (fw_dump.ops->fadump_cleanup)
                fadump_release_reserved_area(tstart, end);
 }
 
+static void fadump_free_elfcorehdr_buf(void)
+{
+       if (fw_dump.elfcorehdr_addr == 0 || fw_dump.elfcorehdr_size == 0)
+               return;
+
+       /*
+        * Before freeing the memory of `elfcorehdr`, reset the global
+        * `elfcorehdr_addr` to prevent modules like `vmcore` from accessing
+        * invalid memory.
+        */
+       elfcorehdr_addr = ELFCORE_ADDR_ERR;
+       fadump_free_buffer(fw_dump.elfcorehdr_addr, fw_dump.elfcorehdr_size);
+       fw_dump.elfcorehdr_addr = 0;
+       fw_dump.elfcorehdr_size = 0;
+}
+
 static void fadump_invalidate_release_mem(void)
 {
        mutex_lock(&fadump_mutex);
        fadump_cleanup();
        mutex_unlock(&fadump_mutex);
 
+       fadump_free_elfcorehdr_buf();
        fadump_release_memory(fw_dump.boot_mem_top, memblock_end_of_DRAM());
        fadump_free_cpu_notes_buf();
 
        return;
 }
 
+static int __init fadump_setup_elfcorehdr_buf(void)
+{
+       int elf_phdr_cnt;
+       unsigned long elfcorehdr_size;
+
+       /*
+        * Program header for CPU notes comes first, followed by one for
+        * vmcoreinfo, and the remaining program headers correspond to
+        * memory regions.
+        */
+       elf_phdr_cnt = 2 + fw_dump.boot_mem_regs_cnt + memblock_num_regions(memory);
+       elfcorehdr_size = sizeof(struct elfhdr) + (elf_phdr_cnt * sizeof(struct elf_phdr));
+       elfcorehdr_size = PAGE_ALIGN(elfcorehdr_size);
+
+       fw_dump.elfcorehdr_addr = (u64)fadump_alloc_buffer(elfcorehdr_size);
+       if (!fw_dump.elfcorehdr_addr) {
+               pr_err("Failed to allocate %lu bytes for elfcorehdr\n",
+                      elfcorehdr_size);
+               return -ENOMEM;
+       }
+       fw_dump.elfcorehdr_size = elfcorehdr_size;
+       return 0;
+}
+
+/*
+ * Check if the fadump header of crashed kernel is compatible with fadump kernel.
+ *
+ * It checks the magic number, endianness, and size of non-primitive type
+ * members of fadump header to ensure safe dump collection.
+ */
+static bool __init is_fadump_header_compatible(struct fadump_crash_info_header *fdh)
+{
+       if (fdh->magic_number == FADUMP_CRASH_INFO_MAGIC_OLD) {
+               pr_err("Old magic number, can't process the dump.\n");
+               return false;
+       }
+
+       if (fdh->magic_number != FADUMP_CRASH_INFO_MAGIC) {
+               if (fdh->magic_number == swab64(FADUMP_CRASH_INFO_MAGIC))
+                       pr_err("Endianness mismatch between the crashed and fadump kernels.\n");
+               else
+                       pr_err("Fadump header is corrupted.\n");
+
+               return false;
+       }
+
+       /*
+        * Dump collection is not safe if the size of non-primitive type members
+        * of the fadump header do not match between crashed and fadump kernel.
+        */
+       if (fdh->pt_regs_sz != sizeof(struct pt_regs) ||
+           fdh->cpu_mask_sz != sizeof(struct cpumask)) {
+               pr_err("Fadump header size mismatch.\n");
+               return false;
+       }
+
+       return true;
+}
+
+static void __init fadump_process(void)
+{
+       struct fadump_crash_info_header *fdh;
+
+       fdh = (struct fadump_crash_info_header *) __va(fw_dump.fadumphdr_addr);
+       if (!fdh) {
+               pr_err("Crash info header is empty.\n");
+               goto err_out;
+       }
+
+       /* Avoid processing the dump if fadump header isn't compatible */
+       if (!is_fadump_header_compatible(fdh))
+               goto err_out;
+
+       /* Allocate buffer for elfcorehdr */
+       if (fadump_setup_elfcorehdr_buf())
+               goto err_out;
+
+       fadump_populate_elfcorehdr(fdh);
+
+       /* Let platform update the CPU notes in elfcorehdr */
+       if (fw_dump.ops->fadump_process(&fw_dump) < 0)
+               goto err_out;
+
+       /*
+        * elfcorehdr is now ready to be exported.
+        *
+        * set elfcorehdr_addr so that vmcore module will export the
+        * elfcorehdr through '/proc/vmcore'.
+        */
+       elfcorehdr_addr = virt_to_phys((void *)fw_dump.elfcorehdr_addr);
+       return;
+
+err_out:
+       fadump_invalidate_release_mem();
+}
+
 /*
  * Prepare for firmware-assisted dump.
  */
         * saving it to the disk.
         */
        if (fw_dump.dump_active) {
-               /*
-                * if dump process fails then invalidate the registration
-                * and release memory before proceeding for re-registration.
-                */
-               if (fw_dump.ops->fadump_process(&fw_dump) < 0)
-                       fadump_invalidate_release_mem();
+               fadump_process();
        }
        /* Initialize the kernel dump memory structure and register with f/w */
        else if (fw_dump.reserve_dump_area_size) {