Skip to content

Commit 108aa50

Browse files
Benjamin Gwinwilldeacon
authored andcommitted
arm64: kexec_file: try more regions if loading segments fails
It's possible that the first region picked for the new kernel will make it impossible to fit the other segments in the required 32GB window, especially if we have a very large initrd. Instead of giving up, we can keep testing other regions for the kernel until we find one that works. Suggested-by: Ryan O'Leary <ryanoleary@google.com> Signed-off-by: Benjamin Gwin <bgwin@google.com> Link: https://lore.kernel.org/r/20201103201106.2397844-1-bgwin@google.com Signed-off-by: Will Deacon <will@kernel.org>
1 parent 7ee31a3 commit 108aa50

2 files changed

Lines changed: 39 additions & 11 deletions

File tree

arch/arm64/kernel/kexec_image.c

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ static void *image_load(struct kimage *image,
4343
u64 flags, value;
4444
bool be_image, be_kernel;
4545
struct kexec_buf kbuf;
46-
unsigned long text_offset;
46+
unsigned long text_offset, kernel_segment_number;
4747
struct kexec_segment *kernel_segment;
4848
int ret;
4949

@@ -88,11 +88,37 @@ static void *image_load(struct kimage *image,
8888
/* Adjust kernel segment with TEXT_OFFSET */
8989
kbuf.memsz += text_offset;
9090

91-
ret = kexec_add_buffer(&kbuf);
92-
if (ret)
91+
kernel_segment_number = image->nr_segments;
92+
93+
/*
94+
* The location of the kernel segment may make it impossible to satisfy
95+
* the other segment requirements, so we try repeatedly to find a
96+
* location that will work.
97+
*/
98+
while ((ret = kexec_add_buffer(&kbuf)) == 0) {
99+
/* Try to load additional data */
100+
kernel_segment = &image->segment[kernel_segment_number];
101+
ret = load_other_segments(image, kernel_segment->mem,
102+
kernel_segment->memsz, initrd,
103+
initrd_len, cmdline);
104+
if (!ret)
105+
break;
106+
107+
/*
108+
* We couldn't find space for the other segments; erase the
109+
* kernel segment and try the next available hole.
110+
*/
111+
image->nr_segments -= 1;
112+
kbuf.buf_min = kernel_segment->mem + kernel_segment->memsz;
113+
kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
114+
}
115+
116+
if (ret) {
117+
pr_err("Could not find any suitable kernel location!");
93118
return ERR_PTR(ret);
119+
}
94120

95-
kernel_segment = &image->segment[image->nr_segments - 1];
121+
kernel_segment = &image->segment[kernel_segment_number];
96122
kernel_segment->mem += text_offset;
97123
kernel_segment->memsz -= text_offset;
98124
image->start = kernel_segment->mem;
@@ -101,12 +127,7 @@ static void *image_load(struct kimage *image,
101127
kernel_segment->mem, kbuf.bufsz,
102128
kernel_segment->memsz);
103129

104-
/* Load additional data */
105-
ret = load_other_segments(image,
106-
kernel_segment->mem, kernel_segment->memsz,
107-
initrd, initrd_len, cmdline);
108-
109-
return ERR_PTR(ret);
130+
return 0;
110131
}
111132

112133
#ifdef CONFIG_KEXEC_IMAGE_VERIFY_SIG

arch/arm64/kernel/machine_kexec_file.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,11 @@ static int prepare_elf_headers(void **addr, unsigned long *sz)
240240
return ret;
241241
}
242242

243+
/*
244+
* Tries to add the initrd and DTB to the image. If it is not possible to find
245+
* valid locations, this function will undo changes to the image and return non
246+
* zero.
247+
*/
243248
int load_other_segments(struct kimage *image,
244249
unsigned long kernel_load_addr,
245250
unsigned long kernel_size,
@@ -248,7 +253,8 @@ int load_other_segments(struct kimage *image,
248253
{
249254
struct kexec_buf kbuf;
250255
void *headers, *dtb = NULL;
251-
unsigned long headers_sz, initrd_load_addr = 0, dtb_len;
256+
unsigned long headers_sz, initrd_load_addr = 0, dtb_len,
257+
orig_segments = image->nr_segments;
252258
int ret = 0;
253259

254260
kbuf.image = image;
@@ -334,6 +340,7 @@ int load_other_segments(struct kimage *image,
334340
return 0;
335341

336342
out_err:
343+
image->nr_segments = orig_segments;
337344
vfree(dtb);
338345
return ret;
339346
}

0 commit comments

Comments
 (0)