[PATCH kexec-tools 1/4] ppc64: ensure /memreserve/ sections exist in user-provided FDT
Shivang Upadhyay
shivangu at linux.ibm.com
Wed Oct 22 06:46:06 PDT 2025
User-provided FDTs may lack `/memreserve/` sections, leading to hangs
during kexec. To fix this, allocate a new FDT buffer and use libfdt to
add reserve entries for the initrd, RTAS/OPAL regions (via
/proc/device-tree), and the FDT itself.
Signed-off-by: Shivang Upadhyay <shivangu at linux.ibm.com>
---
kexec/arch/ppc64/kexec-elf-ppc64.c | 165 +++++++++++++++++++++++++++++
1 file changed, 165 insertions(+)
diff --git a/kexec/arch/ppc64/kexec-elf-ppc64.c b/kexec/arch/ppc64/kexec-elf-ppc64.c
index 858c994..e287392 100644
--- a/kexec/arch/ppc64/kexec-elf-ppc64.c
+++ b/kexec/arch/ppc64/kexec-elf-ppc64.c
@@ -40,6 +40,9 @@
#include <libfdt.h>
#include <arch/fdt.h>
#include <arch/options.h>
+#include <dirent.h>
+#include <assert.h>
+#include <linux/limits.h>
uint64_t initrd_base, initrd_size;
unsigned char reuse_initrd = 0;
@@ -180,6 +183,162 @@ out:
return ret;
}
+static int read_proc_file(char* filename, char* buf) {
+ FILE* f;
+ int len;
+
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ perror("unable to open file");
+ }
+ len = fread(buf, 1, 10, f);
+ fclose(f);
+
+ return len;
+}
+
+
+static void add_reserve_mem(void* dtb, uint64_t where, uint64_t length)
+{
+ int ret;
+
+ ret = fdt_add_mem_rsv(dtb, where, length);
+ assert(ret == 0 && "Failed to add memory reservation block to FDT.");
+
+ return;
+}
+
+/* this function is similar to kexec/fs2dt.c's checkprop function, only part
+ * different here is, this version uses libfdt's function to mark the reserve
+ * section
+ */
+static void checkprop(void* dtb, char *name, unsigned *data, int len)
+{
+ static unsigned long long base, size, end;
+
+ if ((data == NULL) && (base || size || end))
+ die("unrecoverable error: no property data");
+ else if (!strcmp(name, "linux,rtas-base"))
+ base = be32_to_cpu(*data);
+ else if (!strcmp(name, "opal-base-address"))
+ base = be64_to_cpu(*(unsigned long long *)data);
+ else if (!strcmp(name, "opal-runtime-size"))
+ size = be64_to_cpu(*(unsigned long long *)data);
+ else if (!strcmp(name, "linux,tce-base"))
+ base = be64_to_cpu(*(unsigned long long *) data);
+ else if (!strcmp(name, "rtas-size") ||
+ !strcmp(name, "linux,tce-size"))
+ size = be32_to_cpu(*data);
+ else if (reuse_initrd && !strcmp(name, "linux,initrd-start")) {
+ if (len == 8)
+ base = be64_to_cpu(*(unsigned long long *) data);
+ else
+ base = be32_to_cpu(*data);
+ } else if (reuse_initrd && !strcmp(name, "linux,initrd-end")) {
+ if (len == 8)
+ end = be64_to_cpu(*(unsigned long long *) data);
+ else
+ end = be32_to_cpu(*data);
+ }
+
+ if (size && end)
+ die("unrecoverable error: size and end set at same time\n");
+ if (base && size) {
+ add_reserve_mem(dtb, base, size);
+ base = size = 0;
+ }
+ if (base && end) {
+ add_reserve_mem(dtb, base, end-base);
+ base = end = 0;
+ }
+}
+
+static void file_traversal(void *dtb, const char *pathname) {
+ DIR *dir = opendir(pathname);
+ if (!dir) {
+ perror("opendir failed");
+ return;
+ }
+
+ struct dirent *entry;
+ while ((entry = readdir(dir)) != NULL) {
+ // Skip '.' and '..'
+ if (entry->d_name[0] == '.')
+ continue;
+
+ char full_path[PATH_MAX];
+ snprintf(full_path, sizeof(full_path), "%s/%s", pathname, entry->d_name);
+
+ // Check if entry is a directory
+ if (entry->d_type == DT_DIR) {
+ file_traversal(dtb, full_path);
+ } else {
+ char file_data[10];
+ int readlen = read_proc_file(full_path, file_data);
+ if (readlen > 0) {
+ checkprop(dtb, entry->d_name,
+ (unsigned *)file_data, readlen);
+ }
+ }
+ }
+
+ closedir(dir);
+}
+
+void patch_devicetree_with_initrd_info(char* dtb, uint64_t initrd_base,
+ uint64_t initrd_size) {
+
+ int ret, offset;
+ unsigned long initrd_end = initrd_base + initrd_size;
+ uint64_t base_be = cpu_to_be64(initrd_base);
+ uint64_t end_be = cpu_to_be64(initrd_end);
+
+ ret = 0;
+ offset = fdt_path_offset(dtb, "/chosen");
+ assert(offset >= 0 && "failed to find the /chosen node");
+
+ ret = fdt_setprop(dtb, offset, "linux,initrd-start", &base_be, sizeof(base_be));
+ assert(ret == 0 && "failed to set initrd-start on dtb");
+
+ ret = fdt_setprop(dtb, offset, "linux,initrd-end", &end_be, sizeof(end_be));
+ assert(ret == 0 && "failed to set initrd-end on dtb");
+
+}
+
+
+static void patch_devicetree(char *dtb, uint64_t initrd_base,
+ uint64_t initrd_size)
+{
+ int ret;
+
+ patch_devicetree_with_initrd_info(dtb, initrd_base, initrd_size);
+
+ file_traversal(dtb, "/proc/device-tree");
+
+ ret = fdt_add_mem_rsv(dtb, initrd_base, initrd_size);
+ assert(ret == 0 && "failed to add rsvmap");
+
+ ret = fdt_add_mem_rsv(dtb, 0, fdt_totalsize(dtb));
+ assert(ret == 0 && "failed to add rsvmap");
+
+ fdt_set_boot_cpuid_phys(dtb, 0x0);
+ fdt_set_last_comp_version(dtb, 17);
+
+}
+
+static char* alloc_new_dtb(char* dtb, off_t* newsize) {
+ int ret;
+
+ *newsize = fdt_totalsize(dtb) + 256;
+ dtb = (char*) realloc(dtb, *newsize);
+ ret = fdt_open_into(dtb, dtb, *newsize);
+
+ assert(ret == 0 &&
+ "fdt_open_into failed");
+
+ return dtb;
+}
+
int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len,
struct kexec_info *info)
{
@@ -340,6 +499,12 @@ int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len,
if (devicetreeblob) {
/* Grab device tree from buffer */
seg_buf = slurp_file(devicetreeblob, &seg_size);
+
+ if (ramdisk) {
+ seg_buf = alloc_new_dtb(seg_buf, &seg_size);
+ patch_devicetree(seg_buf, initrd_base, initrd_size);
+ }
+
} else {
/* create from fs2dt */
create_flatten_tree(&seg_buf, &seg_size, cmdline);
--
2.51.0
More information about the kexec
mailing list