1 // SPDX-License-Identifier: GPL-2.0-only
3 * Load ELF vmlinux file for the kexec_file_load syscall.
5 * Copyright (C) 2004 Adam Litke (agl@us.ibm.com)
6 * Copyright (C) 2004 IBM Corp.
7 * Copyright (C) 2005 R Sharada (sharada@in.ibm.com)
8 * Copyright (C) 2006 Mohan Kumar M (mohan@in.ibm.com)
9 * Copyright (C) 2016 IBM Corporation
11 * Based on kexec-tools' kexec-elf-exec.c and kexec-elf-ppc64.c.
12 * Heavily modified for the kernel by
13 * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>.
16 #define pr_fmt(fmt) "kexec_elf: " fmt
18 #include <linux/elf.h>
19 #include <linux/kexec.h>
20 #include <linux/libfdt.h>
21 #include <linux/module.h>
22 #include <linux/of_fdt.h>
23 #include <linux/slab.h>
24 #include <linux/types.h>
26 static void *elf64_load(struct kimage
*image
, char *kernel_buf
,
27 unsigned long kernel_len
, char *initrd
,
28 unsigned long initrd_len
, char *cmdline
,
29 unsigned long cmdline_len
)
32 unsigned int fdt_size
;
33 unsigned long kernel_load_addr
;
34 unsigned long initrd_load_addr
= 0, fdt_load_addr
;
36 const void *slave_code
;
38 struct kexec_elf_info elf_info
;
39 struct kexec_buf kbuf
= { .image
= image
, .buf_min
= 0,
40 .buf_max
= ppc64_rma_size
};
41 struct kexec_buf pbuf
= { .image
= image
, .buf_min
= 0,
42 .buf_max
= ppc64_rma_size
, .top_down
= true,
43 .mem
= KEXEC_BUF_MEM_UNKNOWN
};
45 ret
= kexec_build_elf_info(kernel_buf
, kernel_len
, &ehdr
, &elf_info
);
49 ret
= kexec_elf_load(image
, &ehdr
, &elf_info
, &kbuf
, &kernel_load_addr
);
53 pr_debug("Loaded the kernel at 0x%lx\n", kernel_load_addr
);
55 ret
= kexec_load_purgatory(image
, &pbuf
);
57 pr_err("Loading purgatory failed.\n");
61 pr_debug("Loaded purgatory at 0x%lx\n", pbuf
.mem
);
65 kbuf
.bufsz
= kbuf
.memsz
= initrd_len
;
66 kbuf
.buf_align
= PAGE_SIZE
;
67 kbuf
.top_down
= false;
68 kbuf
.mem
= KEXEC_BUF_MEM_UNKNOWN
;
69 ret
= kexec_add_buffer(&kbuf
);
72 initrd_load_addr
= kbuf
.mem
;
74 pr_debug("Loaded initrd at 0x%lx\n", initrd_load_addr
);
77 fdt_size
= fdt_totalsize(initial_boot_params
) * 2;
78 fdt
= kmalloc(fdt_size
, GFP_KERNEL
);
80 pr_err("Not enough memory for the device tree.\n");
84 ret
= fdt_open_into(initial_boot_params
, fdt
, fdt_size
);
86 pr_err("Error setting up the new device tree.\n");
91 ret
= setup_new_fdt(image
, fdt
, initrd_load_addr
, initrd_len
, cmdline
);
98 kbuf
.bufsz
= kbuf
.memsz
= fdt_size
;
99 kbuf
.buf_align
= PAGE_SIZE
;
100 kbuf
.top_down
= true;
101 kbuf
.mem
= KEXEC_BUF_MEM_UNKNOWN
;
102 ret
= kexec_add_buffer(&kbuf
);
105 fdt_load_addr
= kbuf
.mem
;
107 pr_debug("Loaded device tree at 0x%lx\n", fdt_load_addr
);
109 slave_code
= elf_info
.buffer
+ elf_info
.proghdrs
[0].p_offset
;
110 ret
= setup_purgatory(image
, slave_code
, fdt
, kernel_load_addr
,
113 pr_err("Error setting up the purgatory.\n");
116 kexec_free_elf_info(&elf_info
);
118 /* Make kimage_file_post_load_cleanup free the fdt buffer for us. */
119 return ret
? ERR_PTR(ret
) : fdt
;
122 const struct kexec_file_ops kexec_elf64_ops
= {
123 .probe
= kexec_elf_probe
,