Skip to content

Commit 8acea45

Browse files
Li Zhengyupalmer-dabbelt
authored andcommitted
RISC-V: Support for kexec_file on panic
This patch adds support for loading a kexec on panic (kdump) kernel. It has been tested with vmcore-dmesg on riscv64 QEMU on both an smp and a non-smp system. Signed-off-by: Li Zhengyu <lizhengyu3@huawei.com> Link: https://lore.kernel.org/r/20220408100914.150110-5-lizhengyu3@huawei.com Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
1 parent 6261586 commit 8acea45

1 file changed

Lines changed: 118 additions & 1 deletion

File tree

arch/riscv/kernel/elf_kexec.c

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#include <linux/of.h>
1919
#include <linux/libfdt.h>
2020
#include <linux/types.h>
21+
#include <linux/memblock.h>
22+
#include <asm/setup.h>
2123

2224
static int riscv_kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
2325
struct kexec_elf_info *elf_info, unsigned long old_pbase,
@@ -97,6 +99,79 @@ static int elf_find_pbase(struct kimage *image, unsigned long kernel_len,
9799
return ret;
98100
}
99101

102+
static int get_nr_ram_ranges_callback(struct resource *res, void *arg)
103+
{
104+
unsigned int *nr_ranges = arg;
105+
106+
(*nr_ranges)++;
107+
return 0;
108+
}
109+
110+
static int prepare_elf64_ram_headers_callback(struct resource *res, void *arg)
111+
{
112+
struct crash_mem *cmem = arg;
113+
114+
cmem->ranges[cmem->nr_ranges].start = res->start;
115+
cmem->ranges[cmem->nr_ranges].end = res->end;
116+
cmem->nr_ranges++;
117+
118+
return 0;
119+
}
120+
121+
static int prepare_elf_headers(void **addr, unsigned long *sz)
122+
{
123+
struct crash_mem *cmem;
124+
unsigned int nr_ranges;
125+
int ret;
126+
127+
nr_ranges = 1; /* For exclusion of crashkernel region */
128+
walk_system_ram_res(0, -1, &nr_ranges, get_nr_ram_ranges_callback);
129+
130+
cmem = kmalloc(struct_size(cmem, ranges, nr_ranges), GFP_KERNEL);
131+
if (!cmem)
132+
return -ENOMEM;
133+
134+
cmem->max_nr_ranges = nr_ranges;
135+
cmem->nr_ranges = 0;
136+
ret = walk_system_ram_res(0, -1, cmem, prepare_elf64_ram_headers_callback);
137+
if (ret)
138+
goto out;
139+
140+
/* Exclude crashkernel region */
141+
ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end);
142+
if (!ret)
143+
ret = crash_prepare_elf64_headers(cmem, true, addr, sz);
144+
145+
out:
146+
kfree(cmem);
147+
return ret;
148+
}
149+
150+
static char *setup_kdump_cmdline(struct kimage *image, char *cmdline,
151+
unsigned long cmdline_len)
152+
{
153+
int elfcorehdr_strlen;
154+
char *cmdline_ptr;
155+
156+
cmdline_ptr = kzalloc(COMMAND_LINE_SIZE, GFP_KERNEL);
157+
if (!cmdline_ptr)
158+
return NULL;
159+
160+
elfcorehdr_strlen = sprintf(cmdline_ptr, "elfcorehdr=0x%lx ",
161+
image->elf_load_addr);
162+
163+
if (elfcorehdr_strlen + cmdline_len > COMMAND_LINE_SIZE) {
164+
pr_err("Appending elfcorehdr=<addr> exceeds cmdline size\n");
165+
kfree(cmdline_ptr);
166+
return NULL;
167+
}
168+
169+
memcpy(cmdline_ptr + elfcorehdr_strlen, cmdline, cmdline_len);
170+
/* Ensure it's nul terminated */
171+
cmdline_ptr[COMMAND_LINE_SIZE - 1] = '\0';
172+
return cmdline_ptr;
173+
}
174+
100175
static void *elf_kexec_load(struct kimage *image, char *kernel_buf,
101176
unsigned long kernel_len, char *initrd,
102177
unsigned long initrd_len, char *cmdline,
@@ -106,10 +181,12 @@ static void *elf_kexec_load(struct kimage *image, char *kernel_buf,
106181
unsigned long old_kernel_pbase = ULONG_MAX;
107182
unsigned long new_kernel_pbase = 0UL;
108183
unsigned long initrd_pbase = 0UL;
109-
void *fdt;
184+
unsigned long headers_sz;
185+
void *fdt, *headers;
110186
struct elfhdr ehdr;
111187
struct kexec_buf kbuf;
112188
struct kexec_elf_info elf_info;
189+
char *modified_cmdline = NULL;
113190

114191
ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
115192
if (ret)
@@ -130,6 +207,45 @@ static void *elf_kexec_load(struct kimage *image, char *kernel_buf,
130207
kbuf.image = image;
131208
kbuf.buf_min = new_kernel_pbase + kernel_len;
132209
kbuf.buf_max = ULONG_MAX;
210+
211+
/* Add elfcorehdr */
212+
if (image->type == KEXEC_TYPE_CRASH) {
213+
ret = prepare_elf_headers(&headers, &headers_sz);
214+
if (ret) {
215+
pr_err("Preparing elf core header failed\n");
216+
goto out;
217+
}
218+
219+
kbuf.buffer = headers;
220+
kbuf.bufsz = headers_sz;
221+
kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
222+
kbuf.memsz = headers_sz;
223+
kbuf.buf_align = ELF_CORE_HEADER_ALIGN;
224+
kbuf.top_down = true;
225+
226+
ret = kexec_add_buffer(&kbuf);
227+
if (ret) {
228+
vfree(headers);
229+
goto out;
230+
}
231+
image->elf_headers = headers;
232+
image->elf_load_addr = kbuf.mem;
233+
image->elf_headers_sz = headers_sz;
234+
235+
pr_debug("Loaded elf core header at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
236+
image->elf_load_addr, kbuf.bufsz, kbuf.memsz);
237+
238+
/* Setup cmdline for kdump kernel case */
239+
modified_cmdline = setup_kdump_cmdline(image, cmdline,
240+
cmdline_len);
241+
if (!modified_cmdline) {
242+
pr_err("Setting up cmdline for kdump kernel failed\n");
243+
ret = -EINVAL;
244+
goto out;
245+
}
246+
cmdline = modified_cmdline;
247+
}
248+
133249
/* Add the initrd to the image */
134250
if (initrd != NULL) {
135251
kbuf.buffer = initrd;
@@ -170,6 +286,7 @@ static void *elf_kexec_load(struct kimage *image, char *kernel_buf,
170286
out_free_fdt:
171287
kvfree(fdt);
172288
out:
289+
kfree(modified_cmdline);
173290
kexec_free_elf_info(&elf_info);
174291
return ret ? ERR_PTR(ret) : NULL;
175292
}

0 commit comments

Comments
 (0)