summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAKASHI Takahiro <takahiro.akashi@linaro.org>2017-05-17 14:51:47 +0900
committerSimon Horman <horms@verge.net.au>2017-05-22 13:36:18 +0200
commit1591926df5a602ffcbf55e99aa8a96fbebd0bafe (patch)
treeb2258f0d6721c5b1a26e0a5748e380443949b2b8
parent0bd5219da953639276cd17e067c030ac97feca97 (diff)
arm64: kdump: set up kernel image segment
On arm64, we can use the same kernel image as 1st kernel, but we have to modify the entry point as well as segments' addresses in the kernel's elf header in order to load them into correct places. Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org> Tested-by: David Woodhouse <dwmw@amazon.co.uk> Tested-by: Pratyush Anand <panand@redhat.com> Signed-off-by: Simon Horman <horms@verge.net.au>
-rw-r--r--kexec/arch/arm64/crashdump-arm64.c24
-rw-r--r--kexec/arch/arm64/crashdump-arm64.h1
-rw-r--r--kexec/arch/arm64/kexec-arm64.c25
-rw-r--r--kexec/arch/arm64/kexec-elf-arm64.c11
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) {