summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-10-25 17:33:22 -1000
committerLinus Torvalds <torvalds@linux-foundation.org>2026-10-25 17:33:22 -1000
commitb3593aba315322d875f295eef8b1a19e24658ba6 (patch)
tree3294c7ad1453b0fe3570f936f6eb0dc834fe82e6 /kernel
parent394023fbe34fccec5d4db2576169d21e14c8d3d4 (diff)
parent833d2b79ed9520e77c5d83eb11644e5239b2c5bb (diff)
Merge tag 'kexec-v7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux
Pull kexec updates from Mike Rapoport: - Fix probe error codes and error propagation: - make architecture functions that probe loaded image consistenly return -ENOEXEC when they do not recognize the image format - make kexec_image_probe_default() distinguish image format mismatches (-ENOEXEC) from real errors (e.g. -ENOMEM), propagating real errors immediately and only continuing to the next loader on -ENOEXEC - Make sure the next kernel does not land on hardware-poisoned pages - Remove unused arch_kexec_locate_mem_hole hook * tag 'kexec-v7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux: kexec_file: remove unused arch_kexec_locate_mem_hole hook kexec: fix error propagation in kexec_image_probe_default() kexec: return -ENOEXEC from image probe functions on mismatch kexec: keep the next kernel off hardware-poisoned pages kexec_file: stop the top-down search before it underflows
Diffstat (limited to 'kernel')
-rw-r--r--kernel/kexec_core.c10
-rw-r--r--kernel/kexec_elf.c4
-rw-r--r--kernel/kexec_file.c34
3 files changed, 39 insertions, 9 deletions
diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c
index dc770b9a6d05..7ee8c9f078f6 100644
--- a/kernel/kexec_core.c
+++ b/kernel/kexec_core.c
@@ -213,6 +213,16 @@ int sanity_check_segment_list(struct kimage *image)
#endif
/*
+ * Reject destinations that land on hardware-poisoned memory: the
+ * relocation copy would machine-check on the bad frame.
+ */
+ for (i = 0; i < nr_segments; i++) {
+ if (range_first_hwpoison(image->segment[i].mem,
+ image->segment[i].memsz) != PHYS_ADDR_MAX)
+ return -EHWPOISON;
+ }
+
+ /*
* The destination addresses are searched from system RAM rather than
* being allocated from the buddy allocator, so they are not guaranteed
* to be accepted by the current kernel. Accept the destination
diff --git a/kernel/kexec_elf.c b/kernel/kexec_elf.c
index 3a5c25b2adc9..89a444a00693 100644
--- a/kernel/kexec_elf.c
+++ b/kernel/kexec_elf.c
@@ -172,7 +172,7 @@ static int elf_read_ehdr(const char *buf, size_t len, struct elfhdr *ehdr)
default:
pr_debug("Unknown ELF class.\n");
- return -EINVAL;
+ return -ENOEXEC;
}
return elf_is_ehdr_sane(ehdr, len) ? 0 : -ENOEXEC;
@@ -236,7 +236,7 @@ static int elf_read_phdr(const char *buf, size_t len,
default:
pr_debug("Unknown ELF class.\n");
- return -EINVAL;
+ return -ENOEXEC;
}
return elf_is_phdr_sane(phdr, len) ? 0 : -ENOEXEC;
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index 59fb9d71e9d8..a8455481f639 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -68,17 +68,19 @@ int kexec_image_probe_default(struct kimage *image, void *buf,
unsigned long buf_len)
{
const struct kexec_file_ops * const *fops;
- int ret = -ENOEXEC;
for (fops = &kexec_file_loaders[0]; *fops && (*fops)->probe; ++fops) {
- ret = (*fops)->probe(buf, buf_len);
- if (!ret) {
+ int err = (*fops)->probe(buf, buf_len);
+
+ if (!err) {
image->fops = *fops;
- return ret;
+ return 0;
}
+ if (err != -ENOEXEC)
+ return err;
}
- return ret;
+ return -ENOEXEC;
}
static void *kexec_image_load_default(struct kimage *image)
@@ -475,6 +477,7 @@ static int locate_mem_hole_top_down(unsigned long start, unsigned long end,
{
struct kimage *image = kbuf->image;
unsigned long temp_start, temp_end;
+ phys_addr_t poison;
temp_end = min(end, kbuf->buf_max);
temp_start = temp_end - kbuf->memsz + 1;
@@ -484,7 +487,9 @@ static int locate_mem_hole_top_down(unsigned long start, unsigned long end,
/* align down start */
temp_start = ALIGN_DOWN(temp_start, kbuf->buf_align);
- if (temp_start < start || temp_start < kbuf->buf_min)
+ /* A candidate above the range means the walk wrapped around */
+ if (temp_start < start || temp_start < kbuf->buf_min ||
+ temp_start > end)
return 0;
temp_end = temp_start + kbuf->memsz - 1;
@@ -504,6 +509,13 @@ static int locate_mem_hole_top_down(unsigned long start, unsigned long end,
continue;
}
+ poison = range_first_hwpoison(temp_start, kbuf->memsz);
+ if (poison != PHYS_ADDR_MAX) {
+ /* we hit a poisoned page */
+ temp_start = poison - kbuf->memsz;
+ continue;
+ }
+
/* We found a suitable memory range */
break;
} while (1);
@@ -520,6 +532,7 @@ static int locate_mem_hole_bottom_up(unsigned long start, unsigned long end,
{
struct kimage *image = kbuf->image;
unsigned long temp_start, temp_end;
+ phys_addr_t poison;
temp_start = max(start, kbuf->buf_min);
@@ -546,6 +559,13 @@ static int locate_mem_hole_bottom_up(unsigned long start, unsigned long end,
continue;
}
+ poison = range_last_hwpoison(temp_start, kbuf->memsz);
+ if (poison != PHYS_ADDR_MAX) {
+ /* we hit a poisoned page */
+ temp_start = poison + PAGE_SIZE;
+ continue;
+ }
+
/* We found a suitable memory range */
break;
} while (1);
@@ -784,7 +804,7 @@ int kexec_add_buffer(struct kexec_buf *kbuf)
kbuf->cma = NULL;
/* Walk the RAM ranges and allocate a suitable range for the buffer */
- ret = arch_kexec_locate_mem_hole(kbuf);
+ ret = kexec_locate_mem_hole(kbuf);
if (ret)
return ret;