summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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) {