Skip to content

Commit 52b2a8a

Browse files
AKASHI Takahirowildea01
authored andcommitted
arm64: kexec_file: load initrd and device-tree
load_other_segments() is expected to allocate and place all the necessary memory segments other than kernel, including initrd and device-tree blob (and elf core header for crash). While most of the code was borrowed from kexec-tools' counterpart, users may not be allowed to specify dtb explicitly, instead, the dtb presented by the original boot loader is reused. arch_kimage_kernel_post_load_cleanup() is responsible for freeing arm64- specific data allocated in load_other_segments(). Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: Will Deacon <will.deacon@arm.com> Reviewed-by: James Morse <james.morse@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
1 parent 3ddd999 commit 52b2a8a

File tree

2 files changed

+202
-0
lines changed

2 files changed

+202
-0
lines changed

arch/arm64/include/asm/kexec.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,23 @@ static inline void crash_prepare_suspend(void) {}
9393
static inline void crash_post_resume(void) {}
9494
#endif
9595

96+
#ifdef CONFIG_KEXEC_FILE
97+
#define ARCH_HAS_KIMAGE_ARCH
98+
99+
struct kimage_arch {
100+
void *dtb;
101+
unsigned long dtb_mem;
102+
};
103+
104+
struct kimage;
105+
106+
extern int arch_kimage_file_post_load_cleanup(struct kimage *image);
107+
extern int load_other_segments(struct kimage *image,
108+
unsigned long kernel_load_addr, unsigned long kernel_size,
109+
char *initrd, unsigned long initrd_len,
110+
char *cmdline);
111+
#endif
112+
96113
#endif /* __ASSEMBLY__ */
97114

98115
#endif

arch/arm64/kernel/machine_kexec_file.c

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,197 @@
55
* Copyright (C) 2018 Linaro Limited
66
* Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
77
*
8+
* Most code is derived from arm64 port of kexec-tools
89
*/
910

1011
#define pr_fmt(fmt) "kexec_file: " fmt
1112

13+
#include <linux/ioport.h>
14+
#include <linux/kernel.h>
1215
#include <linux/kexec.h>
16+
#include <linux/libfdt.h>
17+
#include <linux/memblock.h>
18+
#include <linux/of_fdt.h>
19+
#include <linux/string.h>
20+
#include <linux/types.h>
21+
#include <asm/byteorder.h>
22+
23+
/* relevant device tree properties */
24+
#define FDT_PSTR_INITRD_STA "linux,initrd-start"
25+
#define FDT_PSTR_INITRD_END "linux,initrd-end"
26+
#define FDT_PSTR_BOOTARGS "bootargs"
1327

1428
const struct kexec_file_ops * const kexec_file_loaders[] = {
1529
NULL
1630
};
31+
32+
int arch_kimage_file_post_load_cleanup(struct kimage *image)
33+
{
34+
vfree(image->arch.dtb);
35+
image->arch.dtb = NULL;
36+
37+
return kexec_image_post_load_cleanup_default(image);
38+
}
39+
40+
static int setup_dtb(struct kimage *image,
41+
unsigned long initrd_load_addr, unsigned long initrd_len,
42+
char *cmdline, void *dtb)
43+
{
44+
int nodeoffset;
45+
int ret;
46+
47+
nodeoffset = fdt_path_offset(dtb, "/chosen");
48+
if (nodeoffset < 0)
49+
return -EINVAL;
50+
51+
/* add bootargs */
52+
if (cmdline) {
53+
ret = fdt_setprop_string(dtb, nodeoffset, FDT_PSTR_BOOTARGS,
54+
cmdline);
55+
if (ret)
56+
return (ret == -FDT_ERR_NOSPACE ? -ENOMEM : -EINVAL);
57+
} else {
58+
ret = fdt_delprop(dtb, nodeoffset, FDT_PSTR_BOOTARGS);
59+
if (ret && (ret != -FDT_ERR_NOTFOUND))
60+
return -EINVAL;
61+
}
62+
63+
/* add initrd-* */
64+
if (initrd_load_addr) {
65+
ret = fdt_setprop_u64(dtb, nodeoffset, FDT_PSTR_INITRD_STA,
66+
initrd_load_addr);
67+
if (ret)
68+
return (ret == -FDT_ERR_NOSPACE ? -ENOMEM : -EINVAL);
69+
70+
ret = fdt_setprop_u64(dtb, nodeoffset, FDT_PSTR_INITRD_END,
71+
initrd_load_addr + initrd_len);
72+
if (ret)
73+
return (ret == -FDT_ERR_NOSPACE ? -ENOMEM : -EINVAL);
74+
} else {
75+
ret = fdt_delprop(dtb, nodeoffset, FDT_PSTR_INITRD_STA);
76+
if (ret && (ret != -FDT_ERR_NOTFOUND))
77+
return -EINVAL;
78+
79+
ret = fdt_delprop(dtb, nodeoffset, FDT_PSTR_INITRD_END);
80+
if (ret && (ret != -FDT_ERR_NOTFOUND))
81+
return -EINVAL;
82+
}
83+
84+
return 0;
85+
}
86+
87+
/*
88+
* More space needed so that we can add initrd and bootargs.
89+
*/
90+
#define DTB_EXTRA_SPACE 0x1000
91+
92+
static int create_dtb(struct kimage *image,
93+
unsigned long initrd_load_addr, unsigned long initrd_len,
94+
char *cmdline, void **dtb)
95+
{
96+
void *buf;
97+
size_t buf_size;
98+
int ret;
99+
100+
buf_size = fdt_totalsize(initial_boot_params)
101+
+ strlen(cmdline) + DTB_EXTRA_SPACE;
102+
103+
for (;;) {
104+
buf = vmalloc(buf_size);
105+
if (!buf)
106+
return -ENOMEM;
107+
108+
/* duplicate a device tree blob */
109+
ret = fdt_open_into(initial_boot_params, buf, buf_size);
110+
if (ret)
111+
return -EINVAL;
112+
113+
ret = setup_dtb(image, initrd_load_addr, initrd_len,
114+
cmdline, buf);
115+
if (ret) {
116+
vfree(buf);
117+
if (ret == -ENOMEM) {
118+
/* unlikely, but just in case */
119+
buf_size += DTB_EXTRA_SPACE;
120+
continue;
121+
} else {
122+
return ret;
123+
}
124+
}
125+
126+
/* trim it */
127+
fdt_pack(buf);
128+
*dtb = buf;
129+
130+
return 0;
131+
}
132+
}
133+
134+
int load_other_segments(struct kimage *image,
135+
unsigned long kernel_load_addr,
136+
unsigned long kernel_size,
137+
char *initrd, unsigned long initrd_len,
138+
char *cmdline)
139+
{
140+
struct kexec_buf kbuf;
141+
void *dtb = NULL;
142+
unsigned long initrd_load_addr = 0, dtb_len;
143+
int ret = 0;
144+
145+
kbuf.image = image;
146+
/* not allocate anything below the kernel */
147+
kbuf.buf_min = kernel_load_addr + kernel_size;
148+
149+
/* load initrd */
150+
if (initrd) {
151+
kbuf.buffer = initrd;
152+
kbuf.bufsz = initrd_len;
153+
kbuf.mem = 0;
154+
kbuf.memsz = initrd_len;
155+
kbuf.buf_align = 0;
156+
/* within 1GB-aligned window of up to 32GB in size */
157+
kbuf.buf_max = round_down(kernel_load_addr, SZ_1G)
158+
+ (unsigned long)SZ_1G * 32;
159+
kbuf.top_down = false;
160+
161+
ret = kexec_add_buffer(&kbuf);
162+
if (ret)
163+
goto out_err;
164+
initrd_load_addr = kbuf.mem;
165+
166+
pr_debug("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
167+
initrd_load_addr, initrd_len, initrd_len);
168+
}
169+
170+
/* load dtb */
171+
ret = create_dtb(image, initrd_load_addr, initrd_len, cmdline, &dtb);
172+
if (ret) {
173+
pr_err("Preparing for new dtb failed\n");
174+
goto out_err;
175+
}
176+
177+
dtb_len = fdt_totalsize(dtb);
178+
kbuf.buffer = dtb;
179+
kbuf.bufsz = dtb_len;
180+
kbuf.mem = 0;
181+
kbuf.memsz = dtb_len;
182+
/* not across 2MB boundary */
183+
kbuf.buf_align = SZ_2M;
184+
kbuf.buf_max = ULONG_MAX;
185+
kbuf.top_down = true;
186+
187+
ret = kexec_add_buffer(&kbuf);
188+
if (ret)
189+
goto out_err;
190+
image->arch.dtb = dtb;
191+
image->arch.dtb_mem = kbuf.mem;
192+
193+
pr_debug("Loaded dtb at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
194+
kbuf.mem, dtb_len, dtb_len);
195+
196+
return 0;
197+
198+
out_err:
199+
vfree(dtb);
200+
return ret;
201+
}

0 commit comments

Comments
 (0)