mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-05 02:19:51 +00:00
Merge branch 'for-next/kexec' into aarch64/for-next/core
Merge in kexec_file_load() support from Akashi Takahiro.
This commit is contained in:
commit
d34664f63b
18 changed files with 623 additions and 90 deletions
|
@ -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);
|
||||
|
|
59
arch/arm64/include/asm/image.h
Normal file
59
arch/arm64/include/asm/image.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/* 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 */
|
||||
|
|
130
arch/arm64/kernel/kexec_image.c
Normal file
130
arch/arm64/kernel/kexec_image.c
Normal file
|
@ -0,0 +1,130 @@
|
|||
// 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. */
|
||||
}
|
||||
|
|
223
arch/arm64/kernel/machine_kexec_file.c
Normal file
223
arch/arm64/kernel/machine_kexec_file.c
Normal file
|
@ -0,0 +1,223 @@
|
|||
// 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,8 +561,8 @@ 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,
|
||||
int (*func)(struct resource *, void *))
|
||||
static int kexec_walk_resources(struct kexec_buf *kbuf,
|
||||
int (*func)(struct resource *, void *))
|
||||
{
|
||||
if (kbuf->image->type == KEXEC_TYPE_CRASH)
|
||||
return walk_iomem_res_desc(crashk_res.desc,
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue