diff options
Diffstat (limited to 'arch/powerpc/kernel/fadump.c')
| -rw-r--r-- | arch/powerpc/kernel/fadump.c | 547 | 
1 files changed, 345 insertions, 202 deletions
diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c index d14eda1e8589..a612e7513a4f 100644 --- a/arch/powerpc/kernel/fadump.c +++ b/arch/powerpc/kernel/fadump.c @@ -53,8 +53,6 @@ static struct kobject *fadump_kobj;  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)) @@ -133,6 +131,41 @@ static int __init fadump_cma_init(void)  static int __init fadump_cma_init(void) { return 1; }  #endif /* CONFIG_CMA */ +/* + * Additional parameters meant for capture kernel are placed in a dedicated area. + * If this is capture kernel boot, append these parameters to bootargs. + */ +void __init fadump_append_bootargs(void) +{ +	char *append_args; +	size_t len; + +	if (!fw_dump.dump_active || !fw_dump.param_area_supported || !fw_dump.param_area) +		return; + +	if (fw_dump.param_area >= fw_dump.boot_mem_top) { +		if (memblock_reserve(fw_dump.param_area, COMMAND_LINE_SIZE)) { +			pr_warn("WARNING: Can't use additional parameters area!\n"); +			fw_dump.param_area = 0; +			return; +		} +	} + +	append_args = (char *)fw_dump.param_area; +	len = strlen(boot_command_line); + +	/* +	 * Too late to fail even if cmdline size exceeds. Truncate additional parameters +	 * to cmdline size and proceed anyway. +	 */ +	if (len + strlen(append_args) >= COMMAND_LINE_SIZE - 1) +		pr_warn("WARNING: Appending parameters exceeds cmdline size. Truncating!\n"); + +	pr_debug("Cmdline: %s\n", boot_command_line); +	snprintf(boot_command_line + len, COMMAND_LINE_SIZE - len, " %s", append_args); +	pr_info("Updated cmdline: %s\n", boot_command_line); +} +  /* Scan the Firmware Assisted dump configuration details. */  int __init early_init_dt_scan_fw_dump(unsigned long node, const char *uname,  				      int depth, void *data) @@ -223,28 +256,6 @@ static bool is_fadump_mem_area_contiguous(u64 d_start, u64 d_end)  }  /* - * 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.   */ @@ -373,12 +384,6 @@ static unsigned long __init get_fadump_area_size(void)  	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 ? @@ -389,10 +394,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;  	} @@ -573,22 +579,6 @@ int __init fadump_reserve_mem(void)  		}  	} -	/* -	 * Calculate the memory boundary. -	 * If memory_limit is less than actual memory boundary then reserve -	 * the memory for fadump beyond the memory_limit and adjust the -	 * memory_limit accordingly, so that the running kernel can run with -	 * specified memory_limit. -	 */ -	if (memory_limit && memory_limit < memblock_end_of_DRAM()) { -		size = get_fadump_area_size(); -		if ((memory_limit + size) < memblock_end_of_DRAM()) -			memory_limit += size; -		else -			memory_limit = memblock_end_of_DRAM(); -		printk(KERN_INFO "Adjusted memory_limit for firmware-assisted" -				" dump, now %#016llx\n", memory_limit); -	}  	if (memory_limit)  		mem_boundary = memory_limit;  	else @@ -705,7 +695,7 @@ void crash_fadump(struct pt_regs *regs, const char *str)  	 * old_cpu == -1 means this is the first CPU which has come here,  	 * go ahead and trigger fadump.  	 * -	 * old_cpu != -1 means some other CPU has already on it's way +	 * old_cpu != -1 means some other CPU has already on its way  	 * to trigger fadump, just keep looping here.  	 */  	this_cpu = smp_processor_id(); @@ -931,36 +921,6 @@ static inline int fadump_add_mem_range(struct fadump_mrange_info *mrange_info,  	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; @@ -998,52 +958,6 @@ static int fadump_init_elfcore_header(char *bufp)  }  /* - * 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   * for saving initial boot memory contents. @@ -1073,36 +987,50 @@ static inline unsigned long fadump_relocate(unsigned long paddr)  	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 */ @@ -1112,55 +1040,66 @@ static int fadump_create_elfcore_headers(char *bufp)  	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) @@ -1175,14 +1114,25 @@ 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;  } @@ -1190,8 +1140,6 @@ static unsigned long init_fadump_header(unsigned long 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- @@ -1200,18 +1148,10 @@ static int register_fadump(void)  	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"); @@ -1230,7 +1170,6 @@ void fadump_cleanup(void)  	} 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) @@ -1416,6 +1355,22 @@ static void fadump_release_memory(u64 begin, u64 end)  		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); @@ -1427,6 +1382,7 @@ static void fadump_invalidate_release_mem(void)  	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(); @@ -1484,6 +1440,18 @@ static ssize_t enabled_show(struct kobject *kobj,  	return sprintf(buf, "%d\n", fw_dump.fadump_enabled);  } +/* + * /sys/kernel/fadump/hotplug_ready sysfs node returns 1, which inidcates + * to usersapce that fadump re-registration is not required on memory + * hotplug events. + */ +static ssize_t hotplug_ready_show(struct kobject *kobj, +				      struct kobj_attribute *attr, +				      char *buf) +{ +	return sprintf(buf, "%d\n", 1); +} +  static ssize_t mem_reserved_show(struct kobject *kobj,  				 struct kobj_attribute *attr,  				 char *buf) @@ -1498,6 +1466,43 @@ static ssize_t registered_show(struct kobject *kobj,  	return sprintf(buf, "%d\n", fw_dump.dump_registered);  } +static ssize_t bootargs_append_show(struct kobject *kobj, +				   struct kobj_attribute *attr, +				   char *buf) +{ +	return sprintf(buf, "%s\n", (char *)__va(fw_dump.param_area)); +} + +static ssize_t bootargs_append_store(struct kobject *kobj, +				   struct kobj_attribute *attr, +				   const char *buf, size_t count) +{ +	char *params; + +	if (!fw_dump.fadump_enabled || fw_dump.dump_active) +		return -EPERM; + +	if (count >= COMMAND_LINE_SIZE) +		return -EINVAL; + +	/* +	 * Fail here instead of handling this scenario with +	 * some silly workaround in capture kernel. +	 */ +	if (saved_command_line_len + count >= COMMAND_LINE_SIZE) { +		pr_err("Appending parameters exceeds cmdline size!\n"); +		return -ENOSPC; +	} + +	params = __va(fw_dump.param_area); +	strscpy_pad(params, buf, COMMAND_LINE_SIZE); +	/* Remove newline character at the end. */ +	if (params[count-1] == '\n') +		params[count-1] = '\0'; + +	return count; +} +  static ssize_t registered_store(struct kobject *kobj,  				struct kobj_attribute *attr,  				const char *buf, size_t count) @@ -1556,11 +1561,14 @@ static struct kobj_attribute release_attr = __ATTR_WO(release_mem);  static struct kobj_attribute enable_attr = __ATTR_RO(enabled);  static struct kobj_attribute register_attr = __ATTR_RW(registered);  static struct kobj_attribute mem_reserved_attr = __ATTR_RO(mem_reserved); +static struct kobj_attribute hotplug_ready_attr = __ATTR_RO(hotplug_ready); +static struct kobj_attribute bootargs_append_attr = __ATTR_RW(bootargs_append);  static struct attribute *fadump_attrs[] = {  	&enable_attr.attr,  	®ister_attr.attr,  	&mem_reserved_attr.attr, +	&hotplug_ready_attr.attr,  	NULL,  }; @@ -1632,6 +1640,150 @@ static void __init fadump_init_files(void)  	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(); +} + +/* + * Reserve memory to store additional parameters to be passed + * for fadump/capture kernel. + */ +static void __init fadump_setup_param_area(void) +{ +	phys_addr_t range_start, range_end; + +	if (!fw_dump.param_area_supported || fw_dump.dump_active) +		return; + +	/* This memory can't be used by PFW or bootloader as it is shared across kernels */ +	if (radix_enabled()) { +		/* +		 * Anywhere in the upper half should be good enough as all memory +		 * is accessible in real mode. +		 */ +		range_start = memblock_end_of_DRAM() / 2; +		range_end = memblock_end_of_DRAM(); +	} else { +		/* +		 * Passing additional parameters is supported for hash MMU only +		 * if the first memory block size is 768MB or higher. +		 */ +		if (ppc64_rma_size < 0x30000000) +			return; + +		/* +		 * 640 MB to 768 MB is not used by PFW/bootloader. So, try reserving +		 * memory for passing additional parameters in this range to avoid +		 * being stomped on by PFW/bootloader. +		 */ +		range_start = 0x2A000000; +		range_end = range_start + 0x4000000; +	} + +	fw_dump.param_area = memblock_phys_alloc_range(COMMAND_LINE_SIZE, +						       COMMAND_LINE_SIZE, +						       range_start, +						       range_end); +	if (!fw_dump.param_area || sysfs_create_file(fadump_kobj, &bootargs_append_attr.attr)) { +		pr_warn("WARNING: Could not setup area to pass additional parameters!\n"); +		return; +	} + +	memset(phys_to_virt(fw_dump.param_area), 0, COMMAND_LINE_SIZE); +} +  /*   * Prepare for firmware-assisted dump.   */ @@ -1651,15 +1803,11 @@ int __init setup_fadump(void)  	 * 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) { +		fadump_setup_param_area();  		fw_dump.ops->fadump_init_mem_struct(&fw_dump);  		register_fadump();  	} @@ -1735,8 +1883,3 @@ static void __init fadump_reserve_crash_area(u64 base)  		memblock_reserve(mstart, msize);  	}  } - -unsigned long __init arch_reserved_kernel_pages(void) -{ -	return memblock_reserved_size() / PAGE_SIZE; -}  |