]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
powerpc/kexec_file: fix cpus node update to FDT
authorSourabh Jain <sourabhjain@linux.ibm.com>
Fri, 10 May 2024 10:22:35 +0000 (15:52 +0530)
committerMichael Ellerman <mpe@ellerman.id.au>
Mon, 17 Jun 2024 12:48:45 +0000 (22:48 +1000)
While updating the cpus node, commit 40c753993e3a ("powerpc/kexec_file:
 Use current CPU info while setting up FDT") first deletes all subnodes
under the /cpus node. However, while adding sub-nodes back, it missed
adding cpus subnodes whose device_type != "cpu", such as l2-cache*,
l3-cache*, ibm,powerpc-cpu-features.

Fix this by only deleting cpus sub-nodes of device_type == "cpus" and
then adding all available nodes with device_type == "cpu".

Fixes: 40c753993e3a ("powerpc/kexec_file: Use current CPU info while setting up FDT")
Signed-off-by: Sourabh Jain <sourabhjain@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://msgid.link/20240510102235.2269496-3-sourabhjain@linux.ibm.com
arch/powerpc/kexec/core_64.c

index 85050be08a23da88fc704effc0b22579ca00507f..2e625c2cb6b9e5e59c6138bae2b2cf16f9d78606 100644 (file)
@@ -456,9 +456,15 @@ static int add_node_props(void *fdt, int node_offset, const struct device_node *
  * @fdt:              Flattened device tree of the kernel.
  *
  * Returns 0 on success, negative errno on error.
+ *
+ * Note: expecting no subnodes under /cpus/<node> with device_type == "cpu".
+ * If this changes, update this function to include them.
  */
 int update_cpus_node(void *fdt)
 {
+       int prev_node_offset;
+       const char *device_type;
+       const struct fdt_property *prop;
        struct device_node *cpus_node, *dn;
        int cpus_offset, cpus_subnode_offset, ret = 0;
 
@@ -469,30 +475,44 @@ int update_cpus_node(void *fdt)
                return cpus_offset;
        }
 
-       if (cpus_offset > 0) {
-               ret = fdt_del_node(fdt, cpus_offset);
+       prev_node_offset = cpus_offset;
+       /* Delete sub-nodes of /cpus node with device_type == "cpu" */
+       for (cpus_subnode_offset = fdt_first_subnode(fdt, cpus_offset); cpus_subnode_offset >= 0;) {
+               /* Ignore nodes that do not have a device_type property or device_type != "cpu" */
+               prop = fdt_get_property(fdt, cpus_subnode_offset, "device_type", NULL);
+               if (!prop || strcmp(prop->data, "cpu")) {
+                       prev_node_offset = cpus_subnode_offset;
+                       goto next_node;
+               }
+
+               ret = fdt_del_node(fdt, cpus_subnode_offset);
                if (ret < 0) {
-                       pr_err("Error deleting /cpus node: %s\n", fdt_strerror(ret));
-                       return -EINVAL;
+                       pr_err("Failed to delete a cpus sub-node: %s\n", fdt_strerror(ret));
+                       return ret;
                }
+next_node:
+               if (prev_node_offset == cpus_offset)
+                       cpus_subnode_offset = fdt_first_subnode(fdt, cpus_offset);
+               else
+                       cpus_subnode_offset = fdt_next_subnode(fdt, prev_node_offset);
        }
 
-       /* 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));
+       cpus_node = of_find_node_by_path("/cpus");
+       /* Fail here to avoid kexec/kdump kernel boot hung */
+       if (!cpus_node) {
+               pr_err("No /cpus node found\n");
                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;
+       /* Add all /cpus sub-nodes of device_type == "cpu" to FDT */
+       for_each_child_of_node(cpus_node, dn) {
+               /* Ignore device nodes that do not have a device_type property
+                * or device_type != "cpu".
+                */
+               device_type = of_get_property(dn, "device_type", NULL);
+               if (!device_type || strcmp(device_type, "cpu"))
+                       continue;
 
-       /* 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,
@@ -506,6 +526,7 @@ int update_cpus_node(void *fdt)
                        goto out;
        }
 out:
+       of_node_put(cpus_node);
        of_node_put(dn);
        return ret;
 }