Skip to content

Commit cb350c1

Browse files
hbathinimpe
authored andcommitted
powerpc/kexec_file: Prepare elfcore header for crashing kernel
Prepare elf headers for the crashing kernel's core file using crash_prepare_elf64_headers() and pass on this info to kdump kernel by updating its command line with elfcorehdr parameter. Also, add elfcorehdr location to reserve map to avoid it from being stomped on while booting. Signed-off-by: Hari Bathini <[email protected]> Tested-by: Pingfan Liu <[email protected]> Reviewed-by: Thiago Jung Bauermann <[email protected]> [mpe: Ensure cmdline is nul terminated] Signed-off-by: Michael Ellerman <[email protected]> Link: https://lore.kernel.org/r/159602298855.575379.15819225623219909517.stgit@hbathini
1 parent 1a1cf93 commit cb350c1

File tree

4 files changed

+234
-0
lines changed

4 files changed

+234
-0
lines changed

arch/powerpc/include/asm/kexec.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,18 @@ struct kimage_arch {
108108
unsigned long backup_start;
109109
void *backup_buf;
110110

111+
unsigned long elfcorehdr_addr;
112+
unsigned long elf_headers_sz;
113+
void *elf_headers;
114+
111115
#ifdef CONFIG_IMA_KEXEC
112116
phys_addr_t ima_buffer_addr;
113117
size_t ima_buffer_size;
114118
#endif
115119
};
116120

121+
char *setup_kdump_cmdline(struct kimage *image, char *cmdline,
122+
unsigned long cmdline_len);
117123
int setup_purgatory(struct kimage *image, const void *slave_code,
118124
const void *fdt, unsigned long kernel_load_addr,
119125
unsigned long fdt_load_addr);

arch/powerpc/kexec/elf_64.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
3535
void *fdt;
3636
const void *slave_code;
3737
struct elfhdr ehdr;
38+
char *modified_cmdline = NULL;
3839
struct kexec_elf_info elf_info;
3940
struct kexec_buf kbuf = { .image = image, .buf_min = 0,
4041
.buf_max = ppc64_rma_size };
@@ -75,6 +76,16 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
7576
pr_err("Failed to load kdump kernel segments\n");
7677
goto out;
7778
}
79+
80+
/* Setup cmdline for kdump kernel case */
81+
modified_cmdline = setup_kdump_cmdline(image, cmdline,
82+
cmdline_len);
83+
if (!modified_cmdline) {
84+
pr_err("Setting up cmdline for kdump kernel failed\n");
85+
ret = -EINVAL;
86+
goto out;
87+
}
88+
cmdline = modified_cmdline;
7889
}
7990

8091
if (initrd != NULL) {
@@ -131,6 +142,7 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
131142
pr_err("Error setting up the purgatory.\n");
132143

133144
out:
145+
kfree(modified_cmdline);
134146
kexec_free_elf_info(&elf_info);
135147

136148
/* Make kimage_file_post_load_cleanup free the fdt buffer for us. */

arch/powerpc/kexec/file_load.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,47 @@
1818
#include <linux/kexec.h>
1919
#include <linux/of_fdt.h>
2020
#include <linux/libfdt.h>
21+
#include <asm/setup.h>
2122
#include <asm/ima.h>
2223

2324
#define SLAVE_CODE_SIZE 256 /* First 0x100 bytes */
2425

26+
/**
27+
* setup_kdump_cmdline - Prepend "elfcorehdr=<addr> " to command line
28+
* of kdump kernel for exporting the core.
29+
* @image: Kexec image
30+
* @cmdline: Command line parameters to update.
31+
* @cmdline_len: Length of the cmdline parameters.
32+
*
33+
* kdump segment must be setup before calling this function.
34+
*
35+
* Returns new cmdline buffer for kdump kernel on success, NULL otherwise.
36+
*/
37+
char *setup_kdump_cmdline(struct kimage *image, char *cmdline,
38+
unsigned long cmdline_len)
39+
{
40+
int elfcorehdr_strlen;
41+
char *cmdline_ptr;
42+
43+
cmdline_ptr = kzalloc(COMMAND_LINE_SIZE, GFP_KERNEL);
44+
if (!cmdline_ptr)
45+
return NULL;
46+
47+
elfcorehdr_strlen = sprintf(cmdline_ptr, "elfcorehdr=0x%lx ",
48+
image->arch.elfcorehdr_addr);
49+
50+
if (elfcorehdr_strlen + cmdline_len > COMMAND_LINE_SIZE) {
51+
pr_err("Appending elfcorehdr=<addr> exceeds cmdline size\n");
52+
kfree(cmdline_ptr);
53+
return NULL;
54+
}
55+
56+
memcpy(cmdline_ptr + elfcorehdr_strlen, cmdline, cmdline_len);
57+
// Ensure it's nul terminated
58+
cmdline_ptr[COMMAND_LINE_SIZE - 1] = '\0';
59+
return cmdline_ptr;
60+
}
61+
2562
/**
2663
* setup_purgatory - initialize the purgatory's global variables
2764
* @image: kexec image.
@@ -221,6 +258,20 @@ int setup_new_fdt(const struct kimage *image, void *fdt,
221258
}
222259
}
223260

261+
if (image->type == KEXEC_TYPE_CRASH) {
262+
/*
263+
* Avoid elfcorehdr from being stomped on in kdump kernel by
264+
* setting up memory reserve map.
265+
*/
266+
ret = fdt_add_mem_rsv(fdt, image->arch.elfcorehdr_addr,
267+
image->arch.elf_headers_sz);
268+
if (ret) {
269+
pr_err("Error reserving elfcorehdr memory: %s\n",
270+
fdt_strerror(ret));
271+
goto err;
272+
}
273+
}
274+
224275
ret = setup_ima_buffer(image, fdt, chosen_node);
225276
if (ret) {
226277
pr_err("Error setting up the new device tree.\n");

arch/powerpc/kexec/file_load_64.c

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,83 @@ static int get_usable_memory_ranges(struct crash_mem **mem_ranges)
128128
return ret;
129129
}
130130

131+
/**
132+
* get_crash_memory_ranges - Get crash memory ranges. This list includes
133+
* first/crashing kernel's memory regions that
134+
* would be exported via an elfcore.
135+
* @mem_ranges: Range list to add the memory ranges to.
136+
*
137+
* Returns 0 on success, negative errno on error.
138+
*/
139+
static int get_crash_memory_ranges(struct crash_mem **mem_ranges)
140+
{
141+
struct memblock_region *reg;
142+
struct crash_mem *tmem;
143+
int ret;
144+
145+
for_each_memblock(memory, reg) {
146+
u64 base, size;
147+
148+
base = (u64)reg->base;
149+
size = (u64)reg->size;
150+
151+
/* Skip backup memory region, which needs a separate entry */
152+
if (base == BACKUP_SRC_START) {
153+
if (size > BACKUP_SRC_SIZE) {
154+
base = BACKUP_SRC_END + 1;
155+
size -= BACKUP_SRC_SIZE;
156+
} else
157+
continue;
158+
}
159+
160+
ret = add_mem_range(mem_ranges, base, size);
161+
if (ret)
162+
goto out;
163+
164+
/* Try merging adjacent ranges before reallocation attempt */
165+
if ((*mem_ranges)->nr_ranges == (*mem_ranges)->max_nr_ranges)
166+
sort_memory_ranges(*mem_ranges, true);
167+
}
168+
169+
/* Reallocate memory ranges if there is no space to split ranges */
170+
tmem = *mem_ranges;
171+
if (tmem && (tmem->nr_ranges == tmem->max_nr_ranges)) {
172+
tmem = realloc_mem_ranges(mem_ranges);
173+
if (!tmem)
174+
goto out;
175+
}
176+
177+
/* Exclude crashkernel region */
178+
ret = crash_exclude_mem_range(tmem, crashk_res.start, crashk_res.end);
179+
if (ret)
180+
goto out;
181+
182+
/*
183+
* FIXME: For now, stay in parity with kexec-tools but if RTAS/OPAL
184+
* regions are exported to save their context at the time of
185+
* crash, they should actually be backed up just like the
186+
* first 64K bytes of memory.
187+
*/
188+
ret = add_rtas_mem_range(mem_ranges);
189+
if (ret)
190+
goto out;
191+
192+
ret = add_opal_mem_range(mem_ranges);
193+
if (ret)
194+
goto out;
195+
196+
/* create a separate program header for the backup region */
197+
ret = add_mem_range(mem_ranges, BACKUP_SRC_START, BACKUP_SRC_SIZE);
198+
if (ret)
199+
goto out;
200+
201+
sort_memory_ranges(*mem_ranges, false);
202+
out:
203+
if (ret)
204+
pr_err("Failed to setup crash memory ranges\n");
205+
return ret;
206+
}
207+
131208
/**
132209
* __locate_mem_hole_top_down - Looks top down for a large enough memory hole
133210
* in the memory regions between buf_min & buf_max
@@ -647,6 +724,81 @@ static int load_backup_segment(struct kimage *image, struct kexec_buf *kbuf)
647724
return 0;
648725
}
649726

727+
/**
728+
* update_backup_region_phdr - Update backup region's offset for the core to
729+
* export the region appropriately.
730+
* @image: Kexec image.
731+
* @ehdr: ELF core header.
732+
*
733+
* Assumes an exclusive program header is setup for the backup region
734+
* in the ELF headers
735+
*
736+
* Returns nothing.
737+
*/
738+
static void update_backup_region_phdr(struct kimage *image, Elf64_Ehdr *ehdr)
739+
{
740+
Elf64_Phdr *phdr;
741+
unsigned int i;
742+
743+
phdr = (Elf64_Phdr *)(ehdr + 1);
744+
for (i = 0; i < ehdr->e_phnum; i++) {
745+
if (phdr->p_paddr == BACKUP_SRC_START) {
746+
phdr->p_offset = image->arch.backup_start;
747+
pr_debug("Backup region offset updated to 0x%lx\n",
748+
image->arch.backup_start);
749+
return;
750+
}
751+
}
752+
}
753+
754+
/**
755+
* load_elfcorehdr_segment - Setup crash memory ranges and initialize elfcorehdr
756+
* segment needed to load kdump kernel.
757+
* @image: Kexec image.
758+
* @kbuf: Buffer contents and memory parameters.
759+
*
760+
* Returns 0 on success, negative errno on error.
761+
*/
762+
static int load_elfcorehdr_segment(struct kimage *image, struct kexec_buf *kbuf)
763+
{
764+
struct crash_mem *cmem = NULL;
765+
unsigned long headers_sz;
766+
void *headers = NULL;
767+
int ret;
768+
769+
ret = get_crash_memory_ranges(&cmem);
770+
if (ret)
771+
goto out;
772+
773+
/* Setup elfcorehdr segment */
774+
ret = crash_prepare_elf64_headers(cmem, false, &headers, &headers_sz);
775+
if (ret) {
776+
pr_err("Failed to prepare elf headers for the core\n");
777+
goto out;
778+
}
779+
780+
/* Fix the offset for backup region in the ELF header */
781+
update_backup_region_phdr(image, headers);
782+
783+
kbuf->buffer = headers;
784+
kbuf->mem = KEXEC_BUF_MEM_UNKNOWN;
785+
kbuf->bufsz = kbuf->memsz = headers_sz;
786+
kbuf->top_down = false;
787+
788+
ret = kexec_add_buffer(kbuf);
789+
if (ret) {
790+
vfree(headers);
791+
goto out;
792+
}
793+
794+
image->arch.elfcorehdr_addr = kbuf->mem;
795+
image->arch.elf_headers_sz = headers_sz;
796+
image->arch.elf_headers = headers;
797+
out:
798+
kfree(cmem);
799+
return ret;
800+
}
801+
650802
/**
651803
* load_crashdump_segments_ppc64 - Initialize the additional segements needed
652804
* to load kdump kernel.
@@ -668,6 +820,15 @@ int load_crashdump_segments_ppc64(struct kimage *image,
668820
}
669821
pr_debug("Loaded the backup region at 0x%lx\n", kbuf->mem);
670822

823+
/* Load elfcorehdr segment - to export crashing kernel's vmcore */
824+
ret = load_elfcorehdr_segment(image, kbuf);
825+
if (ret) {
826+
pr_err("Failed to load elfcorehdr segment\n");
827+
return ret;
828+
}
829+
pr_debug("Loaded elf core header at 0x%lx, bufsz=0x%lx memsz=0x%lx\n",
830+
image->arch.elfcorehdr_addr, kbuf->bufsz, kbuf->memsz);
831+
671832
return 0;
672833
}
673834

@@ -887,5 +1048,9 @@ int arch_kimage_file_post_load_cleanup(struct kimage *image)
8871048
vfree(image->arch.backup_buf);
8881049
image->arch.backup_buf = NULL;
8891050

1051+
vfree(image->arch.elf_headers);
1052+
image->arch.elf_headers = NULL;
1053+
image->arch.elf_headers_sz = 0;
1054+
8901055
return kexec_image_post_load_cleanup_default(image);
8911056
}

0 commit comments

Comments
 (0)