Commit d34664f6 authored by Will Deacon's avatar Will Deacon

Merge branch 'for-next/kexec' into aarch64/for-next/core

Merge in kexec_file_load() support from Akashi Takahiro.
parents bc84a2d1 394135c1
......@@ -905,6 +905,39 @@ config KEXEC
but it is independent of the system firmware. And like a reboot
you can start any kernel with it, not just Linux.
config KEXEC_FILE
bool "kexec file based system call"
select KEXEC_CORE
help
This is new version of kexec system call. This system call is
file based and takes file descriptors as system call argument
for kernel and initramfs as opposed to list of segments as
accepted by previous system call.
config KEXEC_VERIFY_SIG
bool "Verify kernel signature during kexec_file_load() syscall"
depends on KEXEC_FILE
help
Select this option to verify a signature with loaded kernel
image. If configured, any attempt of loading a image without
valid signature will fail.
In addition to that option, you need to enable signature
verification for the corresponding kernel image type being
loaded in order for this to work.
config KEXEC_IMAGE_VERIFY_SIG
bool "Enable Image signature verification support"
default y
depends on KEXEC_VERIFY_SIG
depends on EFI && SIGNED_PE_FILE_VERIFICATION
help
Enable Image signature verification support.
comment "Support for PE file signature verification disabled"
depends on KEXEC_VERIFY_SIG
depends on !EFI || !SIGNED_PE_FILE_VERIFICATION
config CRASH_DUMP
bool "Build kdump crash kernel"
help
......
......@@ -489,11 +489,59 @@ static inline bool system_supports_32bit_el0(void)
return cpus_have_const_cap(ARM64_HAS_32BIT_EL0);
}
static inline bool system_supports_4kb_granule(void)
{
u64 mmfr0;
u32 val;
mmfr0 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR0_EL1);
val = cpuid_feature_extract_unsigned_field(mmfr0,
ID_AA64MMFR0_TGRAN4_SHIFT);
return val == ID_AA64MMFR0_TGRAN4_SUPPORTED;
}
static inline bool system_supports_64kb_granule(void)
{
u64 mmfr0;
u32 val;
mmfr0 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR0_EL1);
val = cpuid_feature_extract_unsigned_field(mmfr0,
ID_AA64MMFR0_TGRAN64_SHIFT);
return val == ID_AA64MMFR0_TGRAN64_SUPPORTED;
}
static inline bool system_supports_16kb_granule(void)
{
u64 mmfr0;
u32 val;
mmfr0 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR0_EL1);
val = cpuid_feature_extract_unsigned_field(mmfr0,
ID_AA64MMFR0_TGRAN16_SHIFT);
return val == ID_AA64MMFR0_TGRAN16_SUPPORTED;
}
static inline bool system_supports_mixed_endian_el0(void)
{
return id_aa64mmfr0_mixed_endian_el0(read_sanitised_ftr_reg(SYS_ID_AA64MMFR0_EL1));
}
static inline bool system_supports_mixed_endian(void)
{
u64 mmfr0;
u32 val;
mmfr0 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR0_EL1);
val = cpuid_feature_extract_unsigned_field(mmfr0,
ID_AA64MMFR0_BIGENDEL_SHIFT);
return val == 0x1;
}
static inline bool system_supports_fpsimd(void)
{
return !cpus_have_const_cap(ARM64_HAS_NO_FPSIMD);
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ASM_IMAGE_H
#define __ASM_IMAGE_H
#define ARM64_IMAGE_MAGIC "ARM\x64"
#define ARM64_IMAGE_FLAG_BE_SHIFT 0
#define ARM64_IMAGE_FLAG_PAGE_SIZE_SHIFT (ARM64_IMAGE_FLAG_BE_SHIFT + 1)
#define ARM64_IMAGE_FLAG_PHYS_BASE_SHIFT \
(ARM64_IMAGE_FLAG_PAGE_SIZE_SHIFT + 2)
#define ARM64_IMAGE_FLAG_BE_MASK 0x1
#define ARM64_IMAGE_FLAG_PAGE_SIZE_MASK 0x3
#define ARM64_IMAGE_FLAG_PHYS_BASE_MASK 0x1
#define ARM64_IMAGE_FLAG_LE 0
#define ARM64_IMAGE_FLAG_BE 1
#define ARM64_IMAGE_FLAG_PAGE_SIZE_4K 1
#define ARM64_IMAGE_FLAG_PAGE_SIZE_16K 2
#define ARM64_IMAGE_FLAG_PAGE_SIZE_64K 3
#define ARM64_IMAGE_FLAG_PHYS_BASE 1
#ifndef __ASSEMBLY__
#define arm64_image_flag_field(flags, field) \
(((flags) >> field##_SHIFT) & field##_MASK)
/*
* struct arm64_image_header - arm64 kernel image header
* See Documentation/arm64/booting.txt for details
*
* @code0: Executable code, or
* @mz_header alternatively used for part of MZ header
* @code1: Executable code
* @text_offset: Image load offset
* @image_size: Effective Image size
* @flags: kernel flags
* @reserved: reserved
* @magic: Magic number
* @reserved5: reserved, or
* @pe_header: alternatively used for PE COFF offset
*/
struct arm64_image_header {
__le32 code0;
__le32 code1;
__le64 text_offset;
__le64 image_size;
__le64 flags;
__le64 res2;
__le64 res3;
__le64 res4;
__le32 magic;
__le32 res5;
};
#endif /* __ASSEMBLY__ */
#endif /* __ASM_IMAGE_H */
......@@ -93,6 +93,25 @@ static inline void crash_prepare_suspend(void) {}
static inline void crash_post_resume(void) {}
#endif
#ifdef CONFIG_KEXEC_FILE
#define ARCH_HAS_KIMAGE_ARCH
struct kimage_arch {
void *dtb;
unsigned long dtb_mem;
};
extern const struct kexec_file_ops kexec_image_ops;
struct kimage;
extern int arch_kimage_file_post_load_cleanup(struct kimage *image);
extern int load_other_segments(struct kimage *image,
unsigned long kernel_load_addr, unsigned long kernel_size,
char *initrd, unsigned long initrd_len,
char *cmdline);
#endif
#endif /* __ASSEMBLY__ */
#endif
......@@ -49,8 +49,9 @@ arm64-obj-$(CONFIG_ARM64_ACPI_PARKING_PROTOCOL) += acpi_parking_protocol.o
arm64-obj-$(CONFIG_PARAVIRT) += paravirt.o
arm64-obj-$(CONFIG_RANDOMIZE_BASE) += kaslr.o
arm64-obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate-asm.o
arm64-obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o \
arm64-obj-$(CONFIG_KEXEC_CORE) += machine_kexec.o relocate_kernel.o \
cpu-reset.o
arm64-obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o kexec_image.o
arm64-obj-$(CONFIG_ARM64_RELOC_TEST) += arm64-reloc-test.o
arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
arm64-obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
......
......@@ -22,11 +22,11 @@
* __cpu_soft_restart(el2_switch, entry, arg0, arg1, arg2) - Helper for
* cpu_soft_restart.
*
* @el2_switch: Flag to indicate a swich to EL2 is needed.
* @el2_switch: Flag to indicate a switch to EL2 is needed.
* @entry: Location to jump to for soft reset.
* arg0: First argument passed to @entry.
* arg1: Second argument passed to @entry.
* arg2: Third argument passed to @entry.
* arg0: First argument passed to @entry. (relocation list)
* arg1: Second argument passed to @entry.(physical kernel entry)
* arg2: Third argument passed to @entry. (physical dtb address)
*
* Put the CPU into the same state as it would be if it had been reset, and
* branch to what would be the reset vector. It must be executed with the
......
......@@ -31,6 +31,7 @@
#include <asm/cache.h>
#include <asm/cputype.h>
#include <asm/elf.h>
#include <asm/image.h>
#include <asm/kernel-pgtable.h>
#include <asm/kvm_arm.h>
#include <asm/memory.h>
......@@ -91,7 +92,7 @@ _head:
.quad 0 // reserved
.quad 0 // reserved
.quad 0 // reserved
.ascii "ARM\x64" // Magic number
.ascii ARM64_IMAGE_MAGIC // Magic number
#ifdef CONFIG_EFI
.long pe_header - _head // Offset to the PE header.
......
......@@ -15,13 +15,15 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ASM_IMAGE_H
#define __ASM_IMAGE_H
#ifndef __ARM64_KERNEL_IMAGE_H
#define __ARM64_KERNEL_IMAGE_H
#ifndef LINKER_SCRIPT
#error This file should only be included in vmlinux.lds.S
#endif
#include <asm/image.h>
/*
* There aren't any ELF relocations we can use to endian-swap values known only
* at link time (e.g. the subtraction of two symbol addresses), so we must get
......@@ -47,19 +49,22 @@
sym##_lo32 = DATA_LE32((data) & 0xffffffff); \
sym##_hi32 = DATA_LE32((data) >> 32)
#define __HEAD_FLAG(field) (__HEAD_FLAG_##field << \
ARM64_IMAGE_FLAG_##field##_SHIFT)
#ifdef CONFIG_CPU_BIG_ENDIAN
#define __HEAD_FLAG_BE 1
#define __HEAD_FLAG_BE ARM64_IMAGE_FLAG_BE
#else
#define __HEAD_FLAG_BE 0
#define __HEAD_FLAG_BE ARM64_IMAGE_FLAG_LE
#endif
#define __HEAD_FLAG_PAGE_SIZE ((PAGE_SHIFT - 10) / 2)
#define __HEAD_FLAG_PHYS_BASE 1
#define __HEAD_FLAGS ((__HEAD_FLAG_BE << 0) | \
(__HEAD_FLAG_PAGE_SIZE << 1) | \
(__HEAD_FLAG_PHYS_BASE << 3))
#define __HEAD_FLAGS (__HEAD_FLAG(BE) | \
__HEAD_FLAG(PAGE_SIZE) | \
__HEAD_FLAG(PHYS_BASE))
/*
* These will output as part of the Image header, which should be little-endian
......@@ -109,4 +114,4 @@ __efistub_screen_info = screen_info;
#endif
#endif /* __ASM_IMAGE_H */
#endif /* __ARM64_KERNEL_IMAGE_H */
// SPDX-License-Identifier: GPL-2.0
/*
* Kexec image loader
* Copyright (C) 2018 Linaro Limited
* Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
*/
#define pr_fmt(fmt) "kexec_file(Image): " fmt
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/kexec.h>
#include <linux/pe.h>
#include <linux/string.h>
#include <linux/verification.h>
#include <asm/byteorder.h>
#include <asm/cpufeature.h>
#include <asm/image.h>
#include <asm/memory.h>
static int image_probe(const char *kernel_buf, unsigned long kernel_len)
{
const struct arm64_image_header *h =
(const struct arm64_image_header *)(kernel_buf);
if (!h || (kernel_len < sizeof(*h)))
return -EINVAL;
if (memcmp(&h->magic, ARM64_IMAGE_MAGIC, sizeof(h->magic)))
return -EINVAL;
return 0;
}
static void *image_load(struct kimage *image,
char *kernel, unsigned long kernel_len,
char *initrd, unsigned long initrd_len,
char *cmdline, unsigned long cmdline_len)
{
struct arm64_image_header *h;
u64 flags, value;
bool be_image, be_kernel;
struct kexec_buf kbuf;
unsigned long text_offset;
struct kexec_segment *kernel_segment;
int ret;
/* We don't support crash kernels yet. */
if (image->type == KEXEC_TYPE_CRASH)
return ERR_PTR(-EOPNOTSUPP);
/*
* We require a kernel with an unambiguous Image header. Per
* Documentation/booting.txt, this is the case when image_size
* is non-zero (practically speaking, since v3.17).
*/
h = (struct arm64_image_header *)kernel;
if (!h->image_size)
return ERR_PTR(-EINVAL);
/* Check cpu features */
flags = le64_to_cpu(h->flags);
be_image = arm64_image_flag_field(flags, ARM64_IMAGE_FLAG_BE);
be_kernel = IS_ENABLED(CONFIG_CPU_BIG_ENDIAN);
if ((be_image != be_kernel) && !system_supports_mixed_endian())
return ERR_PTR(-EINVAL);
value = arm64_image_flag_field(flags, ARM64_IMAGE_FLAG_PAGE_SIZE);
if (((value == ARM64_IMAGE_FLAG_PAGE_SIZE_4K) &&
!system_supports_4kb_granule()) ||
((value == ARM64_IMAGE_FLAG_PAGE_SIZE_64K) &&
!system_supports_64kb_granule()) ||
((value == ARM64_IMAGE_FLAG_PAGE_SIZE_16K) &&
!system_supports_16kb_granule()))
return ERR_PTR(-EINVAL);
/* Load the kernel */
kbuf.image = image;
kbuf.buf_min = 0;
kbuf.buf_max = ULONG_MAX;
kbuf.top_down = false;
kbuf.buffer = kernel;
kbuf.bufsz = kernel_len;
kbuf.mem = 0;
kbuf.memsz = le64_to_cpu(h->image_size);
text_offset = le64_to_cpu(h->text_offset);
kbuf.buf_align = MIN_KIMG_ALIGN;
/* Adjust kernel segment with TEXT_OFFSET */
kbuf.memsz += text_offset;
ret = kexec_add_buffer(&kbuf);
if (ret)
return ERR_PTR(ret);
kernel_segment = &image->segment[image->nr_segments - 1];
kernel_segment->mem += text_offset;
kernel_segment->memsz -= text_offset;
image->start = kernel_segment->mem;
pr_debug("Loaded kernel at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
kernel_segment->mem, kbuf.bufsz,
kernel_segment->memsz);
/* Load additional data */
ret = load_other_segments(image,
kernel_segment->mem, kernel_segment->memsz,
initrd, initrd_len, cmdline);
return ERR_PTR(ret);
}
#ifdef CONFIG_KEXEC_IMAGE_VERIFY_SIG
static int image_verify_sig(const char *kernel, unsigned long kernel_len)
{
return verify_pefile_signature(kernel, kernel_len, NULL,
VERIFYING_KEXEC_PE_SIGNATURE);
}
#endif
const struct kexec_file_ops kexec_image_ops = {
.probe = image_probe,
.load = image_load,
#ifdef CONFIG_KEXEC_IMAGE_VERIFY_SIG
.verify_sig = image_verify_sig,
#endif
};
......@@ -212,9 +212,17 @@ void machine_kexec(struct kimage *kimage)
* uses physical addressing to relocate the new image to its final
* position and transfers control to the image entry point when the
* relocation is complete.
* In kexec case, kimage->start points to purgatory assuming that
* kernel entry and dtb address are embedded in purgatory by
* userspace (kexec-tools).
* In kexec_file case, the kernel starts directly without purgatory.
*/
cpu_soft_restart(reboot_code_buffer_phys, kimage->head, kimage->start, 0);
cpu_soft_restart(reboot_code_buffer_phys, kimage->head, kimage->start,
#ifdef CONFIG_KEXEC_FILE
kimage->arch.dtb_mem);
#else
0);
#endif
BUG(); /* Should never get here. */
}
......
// SPDX-License-Identifier: GPL-2.0
/*
* kexec_file for arm64
*
* Copyright (C) 2018 Linaro Limited
* Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
*
* Most code is derived from arm64 port of kexec-tools
*/
#define pr_fmt(fmt) "kexec_file: " fmt
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/kexec.h>
#include <linux/libfdt.h>
#include <linux/memblock.h>
#include <linux/of_fdt.h>
#include <linux/random.h>
#include <linux/string.h>
#include <linux/types.h>
#include <asm/byteorder.h>
/* relevant device tree properties */
#define FDT_PROP_INITRD_START "linux,initrd-start"
#define FDT_PROP_INITRD_END "linux,initrd-end"
#define FDT_PROP_BOOTARGS "bootargs"
#define FDT_PROP_KASLR_SEED "kaslr-seed"
const struct kexec_file_ops * const kexec_file_loaders[] = {
&kexec_image_ops,
NULL
};
int arch_kimage_file_post_load_cleanup(struct kimage *image)
{
vfree(image->arch.dtb);
image->arch.dtb = NULL;
return kexec_image_post_load_cleanup_default(image);
}
static int setup_dtb(struct kimage *image,
unsigned long initrd_load_addr, unsigned long initrd_len,
char *cmdline, void *dtb)
{
int off, ret;
ret = fdt_path_offset(dtb, "/chosen");
if (ret < 0)
goto out;
off = ret;
/* add bootargs */
if (cmdline) {
ret = fdt_setprop_string(dtb, off, FDT_PROP_BOOTARGS, cmdline);
if (ret)
goto out;
} else {
ret = fdt_delprop(dtb, off, FDT_PROP_BOOTARGS);
if (ret && (ret != -FDT_ERR_NOTFOUND))
goto out;
}
/* add initrd-* */
if (initrd_load_addr) {
ret = fdt_setprop_u64(dtb, off, FDT_PROP_INITRD_START,
initrd_load_addr);
if (ret)
goto out;
ret = fdt_setprop_u64(dtb, off, FDT_PROP_INITRD_END,
initrd_load_addr + initrd_len);
if (ret)
goto out;
} else {
ret = fdt_delprop(dtb, off, FDT_PROP_INITRD_START);
if (ret && (ret != -FDT_ERR_NOTFOUND))
goto out;
ret = fdt_delprop(dtb, off, FDT_PROP_INITRD_END);
if (ret && (ret != -FDT_ERR_NOTFOUND))
goto out;
}
/* add kaslr-seed */
ret = fdt_delprop(dtb, off, FDT_PROP_KASLR_SEED);
if (ret && (ret != -FDT_ERR_NOTFOUND))
goto out;
if (rng_is_initialized()) {
u64 seed = get_random_u64();
ret = fdt_setprop_u64(dtb, off, FDT_PROP_KASLR_SEED, seed);
if (ret)
goto out;
} else {
pr_notice("RNG is not initialised: omitting \"%s\" property\n",
FDT_PROP_KASLR_SEED);
}
out:
if (ret)
return (ret == -FDT_ERR_NOSPACE) ? -ENOMEM : -EINVAL;
return 0;
}
/*
* More space needed so that we can add initrd, bootargs and kaslr-seed.
*/
#define DTB_EXTRA_SPACE 0x1000
static int create_dtb(struct kimage *image,
unsigned long initrd_load_addr, unsigned long initrd_len,
char *cmdline, void **dtb)
{
void *buf;
size_t buf_size;
int ret;
buf_size = fdt_totalsize(initial_boot_params)
+ strlen(cmdline) + DTB_EXTRA_SPACE;
for (;;) {
buf = vmalloc(buf_size);
if (!buf)
return -ENOMEM;
/* duplicate a device tree blob */
ret = fdt_open_into(initial_boot_params, buf, buf_size);
if (ret)
return -EINVAL;
ret = setup_dtb(image, initrd_load_addr, initrd_len,
cmdline, buf);
if (ret) {
vfree(buf);
if (ret == -ENOMEM) {
/* unlikely, but just in case */
buf_size += DTB_EXTRA_SPACE;
continue;
} else {
return ret;
}
}
/* trim it */
fdt_pack(buf);
*dtb = buf;
return 0;
}
}
int load_other_segments(struct kimage *image,
unsigned long kernel_load_addr,
unsigned long kernel_size,
char *initrd, unsigned long initrd_len,
char *cmdline)
{
struct kexec_buf kbuf;
void *dtb = NULL;
unsigned long initrd_load_addr = 0, dtb_len;
int ret = 0;
kbuf.image = image;
/* not allocate anything below the kernel */
kbuf.buf_min = kernel_load_addr + kernel_size;
/* load initrd */
if (initrd) {
kbuf.buffer = initrd;
kbuf.bufsz = initrd_len;
kbuf.mem = 0;
kbuf.memsz = initrd_len;
kbuf.buf_align = 0;
/* within 1GB-aligned window of up to 32GB in size */
kbuf.buf_max = round_down(kernel_load_addr, SZ_1G)
+ (unsigned long)SZ_1G * 32;
kbuf.top_down = false;
ret = kexec_add_buffer(&kbuf);
if (ret)
goto out_err;
initrd_load_addr = kbuf.mem;
pr_debug("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
initrd_load_addr, initrd_len, initrd_len);
}
/* load dtb */
ret = create_dtb(image, initrd_load_addr, initrd_len, cmdline, &dtb);
if (ret) {
pr_err("Preparing for new dtb failed\n");
goto out_err;
}
dtb_len = fdt_totalsize(dtb);
kbuf.buffer = dtb;
kbuf.bufsz = dtb_len;
kbuf.mem = 0;
kbuf.memsz = dtb_len;
/* not across 2MB boundary */
kbuf.buf_align = SZ_2M;
kbuf.buf_max = ULONG_MAX;
kbuf.top_down = true;
ret = kexec_add_buffer(&kbuf);
if (ret)
goto out_err;
image->arch.dtb = dtb;
image->arch.dtb_mem = kbuf.mem;
pr_debug("Loaded dtb at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
kbuf.mem, dtb_len, dtb_len);
return 0;
out_err:
vfree(dtb);
return ret;
}
......@@ -32,6 +32,7 @@
ENTRY(arm64_relocate_new_kernel)
/* Setup the list loop variables. */
mov x18, x2 /* x18 = dtb address */
mov x17, x1 /* x17 = kimage_start */
mov x16, x0 /* x16 = kimage_head */
raw_dcache_line_size x15, x0 /* x15 = dcache line size */
......@@ -107,7 +108,7 @@ ENTRY(arm64_relocate_new_kernel)
isb
/* Start new image. */
mov x0, xzr
mov x0, x18
mov x1, xzr
mov x2, xzr
mov x3, xzr
......
......@@ -24,7 +24,6 @@
#include <linux/slab.h>
#include <linux/kexec.h>
#include <linux/memblock.h>
#include <linux/of_fdt.h>
#include <linux/libfdt.h>
#include <asm/ima.h>
......@@ -46,59 +45,6 @@ int arch_kexec_kernel_image_probe(struct kimage *image, void *buf,
return kexec_image_probe_default(image, buf, buf_len);
}
/**
* arch_kexec_walk_mem - call func(data) for each unreserved memory block
* @kbuf: Context info for the search. Also passed to @func.
* @func: Function to call for each memory block.
*
* This function is used by kexec_add_buffer and kexec_locate_mem_hole
* to find unreserved memory to load kexec segments into.
*
* Return: The memory walk will stop when func returns a non-zero value
* and that value will be returned. If all free regions are visited without
* func returning non-zero, then zero will be returned.
*/
int arch_kexec_walk_mem(struct kexec_buf *kbuf,
int (*func)(struct resource *, void *))
{
int ret = 0;
u64 i;
phys_addr_t mstart, mend;
struct resource res = { };
if (kbuf->top_down) {
for_each_free_mem_range_reverse(i, NUMA_NO_NODE, 0,
&mstart, &mend, NULL) {
/*
* In memblock, end points to the first byte after the
* range while in kexec, end points to the last byte
* in the range.
*/
res.start = mstart;
res.end = mend - 1;
ret = func(&res, kbuf);
if (ret)
break;
}
} else {
for_each_free_mem_range(i, NUMA_NO_NODE, 0, &mstart, &mend,
NULL) {
/*
* In memblock, end points to the first byte after the
* range while in kexec, end points to the last byte
* in the range.
*/
res.start = mstart;
res.end = mend - 1;
ret = func(&res, kbuf);
if (ret)
break;
}
}
return ret;
}
/**
* setup_purgatory - initialize the purgatory's global variables
* @image: kexec image.
......
......@@ -134,16 +134,6 @@ int kexec_file_add_initrd(struct kimage *image, struct s390_load_data *data,
return ret;
}
/*
* The kernel is loaded to a fixed location. Turn off kexec_locate_mem_hole
* and provide kbuf->mem by hand.
*/
int arch_kexec_walk_mem(struct kexec_buf *kbuf,
int (*func)(struct resource *, void *))
{
return 1;
}
int arch_kexec_apply_relocations_add(struct purgatory_info *pi,
Elf_Shdr *section,
const Elf_Shdr *relsec,
......
......@@ -143,6 +143,15 @@ extern const struct kexec_file_ops * const kexec_file_loaders[];
int kexec_image_probe_default(struct kimage *image, void *buf,
unsigned long buf_len);
int kexec_image_post_load_cleanup_default(struct kimage *image);
/*
* If kexec_buf.mem is set to this value, kexec_locate_mem_hole()
* will try to allocate free memory. Arch may overwrite it.
*/
#ifndef KEXEC_BUF_MEM_UNKNOWN
#define KEXEC_BUF_MEM_UNKNOWN 0
#endif
/**
* struct kexec_buf - parameters for finding a place for a buffer in memory
......@@ -183,8 +192,6 @@ int __weak arch_kexec_apply_relocations(struct purgatory_info *pi,
const Elf_Shdr *relsec,
const Elf_Shdr *symtab);
int __weak arch_kexec_walk_mem(struct kexec_buf *kbuf,
int (*func)(struct resource *, void *));
extern int kexec_add_buffer(struct kexec_buf *kbuf);
int kexec_locate_mem_hole(struct kexec_buf *kbuf);
......
......@@ -166,7 +166,7 @@ struct mz_hdr {
uint16_t oem_info; /* oem specific */
uint16_t reserved1[10]; /* reserved */
uint32_t peaddr; /* address of pe header */
char message[64]; /* message to print */
char message[]; /* message to print */
};
struct mz_reloc {
......
......@@ -738,9 +738,11 @@ __SYSCALL(__NR_statx, sys_statx)
__SC_COMP(__NR_io_pgetevents, sys_io_pgetevents, compat_sys_io_pgetevents)
#define __NR_rseq 293
__SYSCALL(__NR_rseq, sys_rseq)
#define __NR_kexec_file_load 294
__SYSCALL(__NR_kexec_file_load, sys_kexec_file_load)
#undef __NR_syscalls
#define __NR_syscalls 294
#define __NR_syscalls 295
/*
* 32 bit systems traditionally used different
......
......@@ -16,6 +16,7 @@
#include <linux/file.h>
#include <linux/slab.h>
#include <linux/kexec.h>
#include <linux/memblock.h>
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/fs.h>
......@@ -76,7 +77,7 @@ void * __weak arch_kexec_kernel_image_load(struct kimage *image)
return kexec_image_load_default(image);
}
static int kexec_image_post_load_cleanup_default(struct kimage *image)
int kexec_image_post_load_cleanup_default(struct kimage *image)
{
if (!image->fops || !image->fops->cleanup)
return 0;
......@@ -499,8 +500,60 @@ static int locate_mem_hole_callback(struct resource *res, void *arg)
return locate_mem_hole_bottom_up(start, end, kbuf);
}
#ifdef CONFIG_ARCH_DISCARD_MEMBLOCK
static int kexec_walk_memblock(struct kexec_buf *kbuf,
int (*func)(struct resource *, void *))
{
return 0;
}
#else
static int kexec_walk_memblock(struct kexec_buf *kbuf,
int (*func)(struct resource *, void *))
{
int ret = 0;
u64 i;
phys_addr_t mstart, mend;
struct resource res = { };
if (kbuf->image->type == KEXEC_TYPE_CRASH)
return func(&crashk_res, kbuf);
if (kbuf->top_down) {
for_each_free_mem_range_reverse(i, NUMA_NO_NODE, MEMBLOCK_NONE,
&mstart, &mend, NULL) {
/*
* In memblock, end points to the first byte after the
* range while in kexec, end points to the last byte
* in the range.
*/
res.start = mstart;
res.end = mend - 1;
ret = func(&res, kbuf);
if (ret)
break;
}
} else {
for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE,
&mstart, &mend, NULL) {
/*
* In memblock, end points to the first byte after the
* range while in kexec, end points to the last byte
* in the range.
*/
res.start = mstart;
res.end = mend - 1;
ret = func(&res, kbuf);
if (ret)
break;
}
}
return ret;
}
#endif
/**
* arch_kexec_walk_mem - call func(data) on free memory regions
* kexec_walk_resources - call func(data) on free memory regions
* @kbuf: Context info for the search. Also passed to @func.
* @func: Function to call for each memory region.
*
......@@ -508,7 +561,7 @@ static int locate_mem_hole_callback(struct resource *res, void *arg)
* and that value will be returned. If all free regions are visited without
* func returning non-zero, then zero will be returned.
*/
int __weak arch_kexec_walk_mem(struct kexec_buf *kbuf,
static int kexec_walk_resources(struct kexec_buf *kbuf,
int (*func)(struct resource *, void *))
{
if (kbuf->image->type == KEXEC_TYPE_CRASH)
......@@ -532,7 +585,14 @@ int kexec_locate_mem_hole(struct kexec_buf *kbuf)
{
int ret;
ret = arch_kexec_walk_mem(kbuf, locate_mem_hole_callback);
/* Arch knows where to place */
if (kbuf->mem != KEXEC_BUF_MEM_UNKNOWN)
return 0;
if (IS_ENABLED(CONFIG_ARCH_DISCARD_MEMBLOCK))
ret = kexec_walk_resources(kbuf, locate_mem_hole_callback);
else
ret = kexec_walk_memblock(kbuf, locate_mem_hole_callback);
return ret == 1 ? 0 : -EADDRNOTAVAIL;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment