diff options
-rw-r--r-- | kexec/arch/arm64/crashdump-arm64.c | 24 | ||||
-rw-r--r-- | kexec/arch/arm64/crashdump-arm64.h | 1 | ||||
-rw-r--r-- | kexec/arch/arm64/kexec-arm64.c | 25 | ||||
-rw-r--r-- | kexec/arch/arm64/kexec-elf-arm64.c | 11 |
4 files changed, 55 insertions, 6 deletions
diff --git a/kexec/arch/arm64/crashdump-arm64.c b/kexec/arch/arm64/crashdump-arm64.c index d142435..4fd7aa8 100644 --- a/kexec/arch/arm64/crashdump-arm64.c +++ b/kexec/arch/arm64/crashdump-arm64.c @@ -211,6 +211,30 @@ int load_crashdump_segments(struct kexec_info *info) return 0; } +/* + * e_entry and p_paddr are actually in virtual address space. + * Those values will be translated to physcal addresses by using + * virt_to_phys() in add_segment(). + * So let's fix up those values for later use so the memory base + * (arm64_mm.phys_offset) will be correctly replaced with + * crash_reserved_mem.start. + */ +void fixup_elf_addrs(struct mem_ehdr *ehdr) +{ + struct mem_phdr *phdr; + int i; + + ehdr->e_entry += - arm64_mem.phys_offset + crash_reserved_mem.start; + + for (i = 0; i < ehdr->e_phnum; i++) { + phdr = &ehdr->e_phdr[i]; + if (phdr->p_type != PT_LOAD) + continue; + phdr->p_paddr += + (-arm64_mem.phys_offset + crash_reserved_mem.start); + } +} + int get_crash_kernel_load_range(uint64_t *start, uint64_t *end) { if (!usablemem_rgns.size) diff --git a/kexec/arch/arm64/crashdump-arm64.h b/kexec/arch/arm64/crashdump-arm64.h index 64c677d..880b83a 100644 --- a/kexec/arch/arm64/crashdump-arm64.h +++ b/kexec/arch/arm64/crashdump-arm64.h @@ -21,5 +21,6 @@ extern struct memory_range crash_reserved_mem; extern struct memory_range elfcorehdr_mem; extern int load_crashdump_segments(struct kexec_info *info); +extern void fixup_elf_addrs(struct mem_ehdr *ehdr); #endif /* CRASHDUMP_ARM64_H */ diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c index 153c96f..6c21756 100644 --- a/kexec/arch/arm64/kexec-arm64.c +++ b/kexec/arch/arm64/kexec-arm64.c @@ -308,12 +308,27 @@ unsigned long arm64_locate_kernel_segment(struct kexec_info *info) { unsigned long hole; - hole = locate_hole(info, - arm64_mem.text_offset + arm64_mem.image_size, - MiB(2), 0, ULONG_MAX, 1); + if (info->kexec_flags & KEXEC_ON_CRASH) { + unsigned long hole_end; + + hole = (crash_reserved_mem.start < mem_min ? + mem_min : crash_reserved_mem.start); + hole = _ALIGN_UP(hole, MiB(2)); + hole_end = hole + arm64_mem.text_offset + arm64_mem.image_size; + + if ((hole_end > mem_max) || + (hole_end > crash_reserved_mem.end)) { + dbgprintf("%s: Crash kernel out of range\n", __func__); + hole = ULONG_MAX; + } + } else { + hole = locate_hole(info, + arm64_mem.text_offset + arm64_mem.image_size, + MiB(2), 0, ULONG_MAX, 1); - if (hole == ULONG_MAX) - dbgprintf("%s: locate_hole failed\n", __func__); + if (hole == ULONG_MAX) + dbgprintf("%s: locate_hole failed\n", __func__); + } return hole; } diff --git a/kexec/arch/arm64/kexec-elf-arm64.c b/kexec/arch/arm64/kexec-elf-arm64.c index 900c693..a961147 100644 --- a/kexec/arch/arm64/kexec-elf-arm64.c +++ b/kexec/arch/arm64/kexec-elf-arm64.c @@ -9,6 +9,7 @@ #include <stdlib.h> #include <linux/elf.h> +#include "crashdump-arm64.h" #include "kexec-arm64.h" #include "kexec-elf.h" #include "kexec-syscall.h" @@ -105,7 +106,8 @@ int elf_arm64_load(int argc, char **argv, const char *kernel_buf, } arm64_mem.vp_offset = _ALIGN_DOWN(ehdr.e_entry, MiB(2)); - arm64_mem.vp_offset -= kernel_segment - get_phys_offset(); + if (!(info->kexec_flags & KEXEC_ON_CRASH)) + arm64_mem.vp_offset -= kernel_segment - get_phys_offset(); dbgprintf("%s: kernel_segment: %016lx\n", __func__, kernel_segment); dbgprintf("%s: text_offset: %016lx\n", __func__, @@ -130,6 +132,13 @@ int elf_arm64_load(int argc, char **argv, const char *kernel_buf, } /* load the kernel */ + if (info->kexec_flags & KEXEC_ON_CRASH) + /* + * offset addresses in elf header in order to load + * vmlinux (elf_exec) into crash kernel's memory + */ + fixup_elf_addrs(&ehdr); + result = elf_exec_load(&ehdr, info); if (result) { |