mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
kexec: updates for 7.4-rc1
* 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() to 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
-----BEGIN PGP SIGNATURE-----
iQEzBAABCgAdFiEEeOVYVaWZL5900a/pOQOGJssO/ZEFAmrckAsACgkQOQOGJssO
/ZHlugf+KDVwiBN1i72OU0TKx9clWyixs20ov2yZ6JlrO46lXCLLXeWy+NNFTSyE
sDF7IQ6zrCWQFKizIsrk+KMyH09+LVRl5Lm8+Mc9Y+iVRuM72n9hJdWLNs6PyUhj
GiLwesq5uRPgmGSVaHHVJD4S2kpeNAMdcxSeKOQoYDyxjs/P5ARqbksUWaMMv5jn
uaUXtJpmR4LV/e/m2VrndvLbkekvZdixvFyx6fXJv+2F+g33QOP/OL1ocqqfqvQk
hBrmvEHWzpay4V6NWzhnlYI355/v+xC2fOM6fuwNRmQNLyMN6AYzvRSbF2mNPXVv
P2G6oYDLd5gVMPcwRmiSQIE/SLnlRA==
=Bi00
-----END PGP SIGNATURE-----
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
This commit is contained in:
commit
b3593aba31
|
|
@ -25,10 +25,10 @@ static int image_probe(const char *kernel_buf, unsigned long kernel_len)
|
|||
(const struct arm64_image_header *)(kernel_buf);
|
||||
|
||||
if (!h || (kernel_len < sizeof(*h)))
|
||||
return -EINVAL;
|
||||
return -ENOEXEC;
|
||||
|
||||
if (memcmp(&h->magic, ARM64_IMAGE_MAGIC, sizeof(h->magic)))
|
||||
return -EINVAL;
|
||||
return -ENOEXEC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,12 +24,12 @@ static int efi_kexec_probe(const char *kernel_buf, unsigned long kernel_len)
|
|||
|
||||
if (!h || (kernel_len < sizeof(*h))) {
|
||||
kexec_dprintk("No LoongArch image header.\n");
|
||||
return -EINVAL;
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
if (!loongarch_header_check_dos_sig(h)) {
|
||||
kexec_dprintk("No LoongArch PE image header.\n");
|
||||
return -EINVAL;
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ static int elf_find_pbase(struct kimage *image, unsigned long kernel_len,
|
|||
kbuf.memsz = ALIGN(kernel_len, PAGE_SIZE);
|
||||
kbuf.cma = NULL;
|
||||
kbuf.top_down = false;
|
||||
ret = arch_kexec_locate_mem_hole(&kbuf);
|
||||
ret = kexec_locate_mem_hole(&kbuf);
|
||||
if (!ret) {
|
||||
*old_pbase = lowest_paddr;
|
||||
*new_pbase = kbuf.mem;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ static int image_probe(const char *kernel_buf, unsigned long kernel_len)
|
|||
const struct riscv_image_header *h = (const struct riscv_image_header *)kernel_buf;
|
||||
|
||||
if (!h || kernel_len < sizeof(*h))
|
||||
return -EINVAL;
|
||||
return -ENOEXEC;
|
||||
|
||||
/* According to Documentation/arch/riscv/boot-image-header.rst,
|
||||
* use "magic2" field to check when version >= 0.2.
|
||||
|
|
@ -28,7 +28,7 @@ static int image_probe(const char *kernel_buf, unsigned long kernel_len)
|
|||
|
||||
if (h->version >= RISCV_HEADER_VERSION &&
|
||||
memcmp(&h->magic2, RISCV_IMAGE_MAGIC2, sizeof(h->magic2)))
|
||||
return -EINVAL;
|
||||
return -ENOEXEC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -259,21 +259,6 @@ int kexec_kernel_verify_pe_sig(const char *kernel, unsigned long kernel_len);
|
|||
extern int kexec_add_buffer(struct kexec_buf *kbuf);
|
||||
int kexec_locate_mem_hole(struct kexec_buf *kbuf);
|
||||
|
||||
#ifndef arch_kexec_locate_mem_hole
|
||||
/**
|
||||
* arch_kexec_locate_mem_hole - Find free memory to place the segments.
|
||||
* @kbuf: Parameters for the memory search.
|
||||
*
|
||||
* On success, kbuf->mem will have the start address of the memory region found.
|
||||
*
|
||||
* Return: 0 on success, negative errno on error.
|
||||
*/
|
||||
static inline int arch_kexec_locate_mem_hole(struct kexec_buf *kbuf)
|
||||
{
|
||||
return kexec_locate_mem_hole(kbuf);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef arch_kexec_apply_relocations_add
|
||||
/*
|
||||
* arch_kexec_apply_relocations_add - apply relocations of type RELA
|
||||
|
|
|
|||
|
|
@ -5234,6 +5234,8 @@ extern const struct attribute_group memory_failure_attr_group;
|
|||
extern void memory_failure_queue(unsigned long pfn, int flags);
|
||||
void num_poisoned_pages_inc(unsigned long pfn);
|
||||
void num_poisoned_pages_sub(unsigned long pfn, long i);
|
||||
phys_addr_t range_first_hwpoison(phys_addr_t start, unsigned long size);
|
||||
phys_addr_t range_last_hwpoison(phys_addr_t start, unsigned long size);
|
||||
#else
|
||||
static inline void memory_failure_queue(unsigned long pfn, int flags)
|
||||
{
|
||||
|
|
@ -5246,6 +5248,18 @@ static inline void num_poisoned_pages_inc(unsigned long pfn)
|
|||
static inline void num_poisoned_pages_sub(unsigned long pfn, long i)
|
||||
{
|
||||
}
|
||||
|
||||
static inline phys_addr_t range_first_hwpoison(phys_addr_t start,
|
||||
unsigned long size)
|
||||
{
|
||||
return PHYS_ADDR_MAX;
|
||||
}
|
||||
|
||||
static inline phys_addr_t range_last_hwpoison(phys_addr_t start,
|
||||
unsigned long size)
|
||||
{
|
||||
return PHYS_ADDR_MAX;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_MEMORY_FAILURE) && defined(CONFIG_MEMORY_HOTPLUG)
|
||||
|
|
|
|||
|
|
@ -212,6 +212,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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -96,6 +96,46 @@ void num_poisoned_pages_sub(unsigned long pfn, long i)
|
|||
memblk_nr_poison_sub(pfn, i);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the first or the last hardware-poisoned online page in [start,
|
||||
* start + size), or PHYS_ADDR_MAX if the range is clean.
|
||||
*/
|
||||
static phys_addr_t range_hwpoison(phys_addr_t start, unsigned long size,
|
||||
bool first)
|
||||
{
|
||||
phys_addr_t poison = PHYS_ADDR_MAX;
|
||||
unsigned long pfn, end_pfn;
|
||||
|
||||
if (!size || !atomic_long_read(&num_poisoned_pages))
|
||||
return poison;
|
||||
|
||||
end_pfn = PHYS_PFN(start + size - 1);
|
||||
for (pfn = PHYS_PFN(start); pfn <= end_pfn; pfn++) {
|
||||
struct page *page = pfn_to_online_page(pfn);
|
||||
|
||||
if (page && is_page_hwpoison(page)) {
|
||||
if (first)
|
||||
return PFN_PHYS(pfn);
|
||||
|
||||
poison = PFN_PHYS(pfn);
|
||||
}
|
||||
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
return poison;
|
||||
}
|
||||
|
||||
phys_addr_t range_first_hwpoison(phys_addr_t start, unsigned long size)
|
||||
{
|
||||
return range_hwpoison(start, size, true);
|
||||
}
|
||||
|
||||
phys_addr_t range_last_hwpoison(phys_addr_t start, unsigned long size)
|
||||
{
|
||||
return range_hwpoison(start, size, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* MF_ATTR_RO - Create sysfs entry for each memory failure statistics.
|
||||
* @_name: name of the file in the per NUMA sysfs directory.
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user