diff options
Diffstat (limited to 'arch/powerpc/kexec/file_load_64.c')
| -rw-r--r-- | arch/powerpc/kexec/file_load_64.c | 314 | 
1 files changed, 36 insertions, 278 deletions
diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c index 1bc65de6174f..925a69ad2468 100644 --- a/arch/powerpc/kexec/file_load_64.c +++ b/arch/powerpc/kexec/file_load_64.c @@ -30,6 +30,7 @@  #include <asm/iommu.h>  #include <asm/prom.h>  #include <asm/plpks.h> +#include <asm/cputhreads.h>  struct umem_info {  	__be64 *buf;		/* data buffer for usable-memory property */ @@ -48,83 +49,6 @@ const struct kexec_file_ops * const kexec_file_loaders[] = {  };  /** - * get_exclude_memory_ranges - Get exclude memory ranges. This list includes - *                             regions like opal/rtas, tce-table, initrd, - *                             kernel, htab which should be avoided while - *                             setting up kexec load segments. - * @mem_ranges:                Range list to add the memory ranges to. - * - * Returns 0 on success, negative errno on error. - */ -static int get_exclude_memory_ranges(struct crash_mem **mem_ranges) -{ -	int ret; - -	ret = add_tce_mem_ranges(mem_ranges); -	if (ret) -		goto out; - -	ret = add_initrd_mem_range(mem_ranges); -	if (ret) -		goto out; - -	ret = add_htab_mem_range(mem_ranges); -	if (ret) -		goto out; - -	ret = add_kernel_mem_range(mem_ranges); -	if (ret) -		goto out; - -	ret = add_rtas_mem_range(mem_ranges); -	if (ret) -		goto out; - -	ret = add_opal_mem_range(mem_ranges); -	if (ret) -		goto out; - -	ret = add_reserved_mem_ranges(mem_ranges); -	if (ret) -		goto out; - -	/* exclude memory ranges should be sorted for easy lookup */ -	sort_memory_ranges(*mem_ranges, true); -out: -	if (ret) -		pr_err("Failed to setup exclude memory ranges\n"); -	return ret; -} - -/** - * get_reserved_memory_ranges - Get reserve memory ranges. This list includes - *                              memory regions that should be added to the - *                              memory reserve map to ensure the region is - *                              protected from any mischief. - * @mem_ranges:                 Range list to add the memory ranges to. - * - * Returns 0 on success, negative errno on error. - */ -static int get_reserved_memory_ranges(struct crash_mem **mem_ranges) -{ -	int ret; - -	ret = add_rtas_mem_range(mem_ranges); -	if (ret) -		goto out; - -	ret = add_tce_mem_ranges(mem_ranges); -	if (ret) -		goto out; - -	ret = add_reserved_mem_ranges(mem_ranges); -out: -	if (ret) -		pr_err("Failed to setup reserved memory ranges\n"); -	return ret; -} - -/**   * __locate_mem_hole_top_down - Looks top down for a large enough memory hole   *                              in the memory regions between buf_min & buf_max   *                              for the buffer. If found, sets kbuf->mem. @@ -323,119 +247,6 @@ static int locate_mem_hole_bottom_up_ppc64(struct kexec_buf *kbuf,  #ifdef CONFIG_CRASH_DUMP  /** - * get_usable_memory_ranges - Get usable memory ranges. This list includes - *                            regions like crashkernel, opal/rtas & tce-table, - *                            that kdump kernel could use. - * @mem_ranges:               Range list to add the memory ranges to. - * - * Returns 0 on success, negative errno on error. - */ -static int get_usable_memory_ranges(struct crash_mem **mem_ranges) -{ -	int ret; - -	/* -	 * Early boot failure observed on guests when low memory (first memory -	 * block?) is not added to usable memory. So, add [0, crashk_res.end] -	 * instead of [crashk_res.start, crashk_res.end] to workaround it. -	 * Also, crashed kernel's memory must be added to reserve map to -	 * avoid kdump kernel from using it. -	 */ -	ret = add_mem_range(mem_ranges, 0, crashk_res.end + 1); -	if (ret) -		goto out; - -	ret = add_rtas_mem_range(mem_ranges); -	if (ret) -		goto out; - -	ret = add_opal_mem_range(mem_ranges); -	if (ret) -		goto out; - -	ret = add_tce_mem_ranges(mem_ranges); -out: -	if (ret) -		pr_err("Failed to setup usable memory ranges\n"); -	return ret; -} - -/** - * get_crash_memory_ranges - Get crash memory ranges. This list includes - *                           first/crashing kernel's memory regions that - *                           would be exported via an elfcore. - * @mem_ranges:              Range list to add the memory ranges to. - * - * Returns 0 on success, negative errno on error. - */ -static int get_crash_memory_ranges(struct crash_mem **mem_ranges) -{ -	phys_addr_t base, end; -	struct crash_mem *tmem; -	u64 i; -	int ret; - -	for_each_mem_range(i, &base, &end) { -		u64 size = end - base; - -		/* Skip backup memory region, which needs a separate entry */ -		if (base == BACKUP_SRC_START) { -			if (size > BACKUP_SRC_SIZE) { -				base = BACKUP_SRC_END + 1; -				size -= BACKUP_SRC_SIZE; -			} else -				continue; -		} - -		ret = add_mem_range(mem_ranges, base, size); -		if (ret) -			goto out; - -		/* Try merging adjacent ranges before reallocation attempt */ -		if ((*mem_ranges)->nr_ranges == (*mem_ranges)->max_nr_ranges) -			sort_memory_ranges(*mem_ranges, true); -	} - -	/* Reallocate memory ranges if there is no space to split ranges */ -	tmem = *mem_ranges; -	if (tmem && (tmem->nr_ranges == tmem->max_nr_ranges)) { -		tmem = realloc_mem_ranges(mem_ranges); -		if (!tmem) -			goto out; -	} - -	/* Exclude crashkernel region */ -	ret = crash_exclude_mem_range(tmem, crashk_res.start, crashk_res.end); -	if (ret) -		goto out; - -	/* -	 * FIXME: For now, stay in parity with kexec-tools but if RTAS/OPAL -	 *        regions are exported to save their context at the time of -	 *        crash, they should actually be backed up just like the -	 *        first 64K bytes of memory. -	 */ -	ret = add_rtas_mem_range(mem_ranges); -	if (ret) -		goto out; - -	ret = add_opal_mem_range(mem_ranges); -	if (ret) -		goto out; - -	/* create a separate program header for the backup region */ -	ret = add_mem_range(mem_ranges, BACKUP_SRC_START, BACKUP_SRC_SIZE); -	if (ret) -		goto out; - -	sort_memory_ranges(*mem_ranges, false); -out: -	if (ret) -		pr_err("Failed to setup crash memory ranges\n"); -	return ret; -} - -/**   * check_realloc_usable_mem - Reallocate buffer if it can't accommodate entries   * @um_info:                  Usable memory buffer and ranges info.   * @cnt:                      No. of entries to accommodate. @@ -784,6 +595,23 @@ static void update_backup_region_phdr(struct kimage *image, Elf64_Ehdr *ehdr)  	}  } +static unsigned int kdump_extra_elfcorehdr_size(struct crash_mem *cmem) +{ +#if defined(CONFIG_CRASH_HOTPLUG) && defined(CONFIG_MEMORY_HOTPLUG) +	unsigned int extra_sz = 0; + +	if (CONFIG_CRASH_MAX_MEMORY_RANGES > (unsigned int)PN_XNUM) +		pr_warn("Number of Phdrs %u exceeds max\n", CONFIG_CRASH_MAX_MEMORY_RANGES); +	else if (cmem->nr_ranges >= CONFIG_CRASH_MAX_MEMORY_RANGES) +		pr_warn("Configured crash mem ranges may not be enough\n"); +	else +		extra_sz = (CONFIG_CRASH_MAX_MEMORY_RANGES - cmem->nr_ranges) * sizeof(Elf64_Phdr); + +	return extra_sz; +#endif +	return 0; +} +  /**   * load_elfcorehdr_segment - Setup crash memory ranges and initialize elfcorehdr   *                           segment needed to load kdump kernel. @@ -815,7 +643,8 @@ static int load_elfcorehdr_segment(struct kimage *image, struct kexec_buf *kbuf)  	kbuf->buffer = headers;  	kbuf->mem = KEXEC_BUF_MEM_UNKNOWN; -	kbuf->bufsz = kbuf->memsz = headers_sz; +	kbuf->bufsz = headers_sz; +	kbuf->memsz = headers_sz + kdump_extra_elfcorehdr_size(cmem);  	kbuf->top_down = false;  	ret = kexec_add_buffer(kbuf); @@ -979,6 +808,9 @@ static unsigned int kdump_extra_fdt_size_ppc64(struct kimage *image)  	unsigned int cpu_nodes, extra_size = 0;  	struct device_node *dn;  	u64 usm_entries; +#ifdef CONFIG_CRASH_HOTPLUG +	unsigned int possible_cpu_nodes; +#endif  	if (!IS_ENABLED(CONFIG_CRASH_DUMP) || image->type != KEXEC_TYPE_CRASH)  		return 0; @@ -1006,6 +838,19 @@ static unsigned int kdump_extra_fdt_size_ppc64(struct kimage *image)  	if (cpu_nodes > boot_cpu_node_count)  		extra_size += (cpu_nodes - boot_cpu_node_count) * cpu_node_size(); +#ifdef CONFIG_CRASH_HOTPLUG +	/* +	 * Make sure enough space is reserved to accommodate possible CPU nodes +	 * in the crash FDT. This allows packing possible CPU nodes which are +	 * not yet present in the system without regenerating the entire FDT. +	 */ +	if (image->type == KEXEC_TYPE_CRASH) { +		possible_cpu_nodes = num_possible_cpus() / threads_per_core; +		if (possible_cpu_nodes > cpu_nodes) +			extra_size += (possible_cpu_nodes - cpu_nodes) * cpu_node_size(); +	} +#endif +  	return extra_size;  } @@ -1028,93 +873,6 @@ unsigned int kexec_extra_fdt_size_ppc64(struct kimage *image)  	return extra_size + kdump_extra_fdt_size_ppc64(image);  } -/** - * add_node_props - Reads node properties from device node structure and add - *                  them to fdt. - * @fdt:            Flattened device tree of the kernel - * @node_offset:    offset of the node to add a property at - * @dn:             device node pointer - * - * Returns 0 on success, negative errno on error. - */ -static int add_node_props(void *fdt, int node_offset, const struct device_node *dn) -{ -	int ret = 0; -	struct property *pp; - -	if (!dn) -		return -EINVAL; - -	for_each_property_of_node(dn, pp) { -		ret = fdt_setprop(fdt, node_offset, pp->name, pp->value, pp->length); -		if (ret < 0) { -			pr_err("Unable to add %s property: %s\n", pp->name, fdt_strerror(ret)); -			return ret; -		} -	} -	return ret; -} - -/** - * update_cpus_node - Update cpus node of flattened device tree using of_root - *                    device node. - * @fdt:              Flattened device tree of the kernel. - * - * Returns 0 on success, negative errno on error. - */ -static int update_cpus_node(void *fdt) -{ -	struct device_node *cpus_node, *dn; -	int cpus_offset, cpus_subnode_offset, ret = 0; - -	cpus_offset = fdt_path_offset(fdt, "/cpus"); -	if (cpus_offset < 0 && cpus_offset != -FDT_ERR_NOTFOUND) { -		pr_err("Malformed device tree: error reading /cpus node: %s\n", -		       fdt_strerror(cpus_offset)); -		return cpus_offset; -	} - -	if (cpus_offset > 0) { -		ret = fdt_del_node(fdt, cpus_offset); -		if (ret < 0) { -			pr_err("Error deleting /cpus node: %s\n", fdt_strerror(ret)); -			return -EINVAL; -		} -	} - -	/* Add cpus node to fdt */ -	cpus_offset = fdt_add_subnode(fdt, fdt_path_offset(fdt, "/"), "cpus"); -	if (cpus_offset < 0) { -		pr_err("Error creating /cpus node: %s\n", fdt_strerror(cpus_offset)); -		return -EINVAL; -	} - -	/* Add cpus node properties */ -	cpus_node = of_find_node_by_path("/cpus"); -	ret = add_node_props(fdt, cpus_offset, cpus_node); -	of_node_put(cpus_node); -	if (ret < 0) -		return ret; - -	/* Loop through all subnodes of cpus and add them to fdt */ -	for_each_node_by_type(dn, "cpu") { -		cpus_subnode_offset = fdt_add_subnode(fdt, cpus_offset, dn->full_name); -		if (cpus_subnode_offset < 0) { -			pr_err("Unable to add %s subnode: %s\n", dn->full_name, -			       fdt_strerror(cpus_subnode_offset)); -			ret = cpus_subnode_offset; -			goto out; -		} - -		ret = add_node_props(fdt, cpus_subnode_offset, dn); -		if (ret < 0) -			goto out; -	} -out: -	of_node_put(dn); -	return ret; -} -  static int copy_property(void *fdt, int node_offset, const struct device_node *dn,  			 const char *propname)  {  |