Commit 40c75399 authored by Sourabh Jain's avatar Sourabh Jain Committed by Michael Ellerman
Browse files

powerpc/kexec_file: Use current CPU info while setting up FDT



kexec_file_load() uses initial_boot_params in setting up the device tree
for the kernel to be loaded. Though initial_boot_params holds info about
CPUs at the time of boot, it doesn't account for hot added CPUs.

So, kexec'ing with kexec_file_load() syscall leaves the kexec'ed kernel
with inaccurate CPU info.

If kdump kernel is loaded with kexec_file_load() syscall and the system
crashes on a hot added CPU, the capture kernel hangs failing to identify
the boot CPU, with no output.

To avoid this from happening, extract current CPU info from of_root
device node and use it for setting up the fdt in kexec_file_load case.

Fixes: 6ecd0163 ("powerpc/kexec_file: Add appropriate regions for memory reserve map")
Cc: stable@vger.kernel.org # v5.9+
Signed-off-by: default avatarSourabh Jain <sourabhjain@linux.ibm.com>
Reviewed-by: default avatarHari Bathini <hbathini@linux.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20210429060256.199714-1-sourabhjain@linux.ibm.com
parent 8abddd96
Loading
Loading
Loading
Loading
+92 −0
Original line number Diff line number Diff line
@@ -950,6 +950,93 @@ unsigned int kexec_extra_fdt_size_ppc64(struct kimage *image)
	return (unsigned int)(usm_entries * sizeof(u64));
}

/**
 * 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;
}

/**
 * setup_new_fdt_ppc64 - Update the flattend device-tree of the kernel
 *                       being loaded.
@@ -1006,6 +1093,11 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt,
		}
	}

	/* Update cpus nodes information to account hotplug CPUs. */
	ret =  update_cpus_node(fdt);
	if (ret < 0)
		goto out;

	/* Update memory reserve map */
	ret = get_reserved_memory_ranges(&rmem);
	if (ret)