* [PATCH v5 1/3] kexec: Add common device tree routines
2016-09-01 21:51 ` Geoff Levand
@ 2016-09-01 21:51 ` Geoff Levand
-1 siblings, 0 replies; 14+ messages in thread
From: Geoff Levand @ 2016-09-01 21:51 UTC (permalink / raw)
To: linux-arm-kernel
Common device tree routines that can be shared between all arches
that have device tree support.
Signed-off-by: Geoff Levand <geoff@infradead.org>
---
kexec/Makefile | 4 ++
kexec/dt-ops.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
kexec/dt-ops.h | 13 ++++++
3 files changed, 162 insertions(+)
create mode 100644 kexec/dt-ops.c
create mode 100644 kexec/dt-ops.h
diff --git a/kexec/Makefile b/kexec/Makefile
index e2aee84..cc3f08b 100644
--- a/kexec/Makefile
+++ b/kexec/Makefile
@@ -73,6 +73,10 @@ dist += kexec/mem_regions.c kexec/mem_regions.h
$(ARCH)_MEM_REGIONS =
KEXEC_SRCS += $($(ARCH)_MEM_REGIONS)
+dist += kexec/dt-ops.c kexec/dt-ops.h
+$(ARCH)_DT_OPS =
+KEXEC_SRCS += $($(ARCH)_DT_OPS)
+
include $(srcdir)/kexec/arch/alpha/Makefile
include $(srcdir)/kexec/arch/arm/Makefile
include $(srcdir)/kexec/arch/i386/Makefile
diff --git a/kexec/dt-ops.c b/kexec/dt-ops.c
new file mode 100644
index 0000000..915dbf5
--- /dev/null
+++ b/kexec/dt-ops.c
@@ -0,0 +1,145 @@
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <libfdt.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "kexec.h"
+#include "dt-ops.h"
+
+static const char n_chosen[] = "/chosen";
+
+static const char p_bootargs[] = "bootargs";
+static const char p_initrd_start[] = "linux,initrd-start";
+static const char p_initrd_end[] = "linux,initrd-end";
+
+int dtb_set_initrd(char **dtb, off_t *dtb_size, off_t start, off_t end)
+{
+ int result;
+ uint64_t value;
+
+ dbgprintf("%s: start %jd, end %jd, size %jd (%jd KiB)\n",
+ __func__, (intmax_t)start, (intmax_t)end,
+ (intmax_t)(end - start),
+ (intmax_t)(end - start) / 1024);
+
+ value = cpu_to_fdt64(start);
+
+ result = dtb_set_property(dtb, dtb_size, n_chosen, p_initrd_start,
+ &value, sizeof(value));
+
+ if (result)
+ return result;
+
+ value = cpu_to_fdt64(end);
+
+ result = dtb_set_property(dtb, dtb_size, n_chosen, p_initrd_end,
+ &value, sizeof(value));
+
+ if (result) {
+ dtb_delete_property(*dtb, n_chosen, p_initrd_start);
+ return result;
+ }
+
+ return 0;
+}
+
+int dtb_set_bootargs(char **dtb, off_t *dtb_size, const char *command_line)
+{
+ return dtb_set_property(dtb, dtb_size, n_chosen, p_bootargs,
+ command_line, strlen(command_line) + 1);
+}
+
+int dtb_set_property(char **dtb, off_t *dtb_size, const char *node,
+ const char *prop, const void *value, int value_len)
+{
+ int result;
+ int nodeoffset;
+ void *new_dtb;
+ int new_size;
+
+ value_len = FDT_TAGALIGN(value_len);
+
+ new_size = FDT_TAGALIGN(*dtb_size + fdt_node_len(node)
+ + fdt_prop_len(prop, value_len));
+
+ new_dtb = malloc(new_size);
+
+ if (!new_dtb) {
+ dbgprintf("%s: malloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ result = fdt_open_into(*dtb, new_dtb, new_size);
+
+ if (result) {
+ dbgprintf("%s: fdt_open_into failed: %s\n", __func__,
+ fdt_strerror(result));
+ goto on_error;
+ }
+
+ nodeoffset = fdt_path_offset(new_dtb, node);
+
+ if (nodeoffset == -FDT_ERR_NOTFOUND) {
+ result = fdt_add_subnode(new_dtb, nodeoffset, node);
+
+ if (result) {
+ dbgprintf("%s: fdt_add_subnode failed: %s\n", __func__,
+ fdt_strerror(result));
+ goto on_error;
+ }
+ } else if (nodeoffset < 0) {
+ dbgprintf("%s: fdt_path_offset failed: %s\n", __func__,
+ fdt_strerror(nodeoffset));
+ goto on_error;
+ }
+
+ result = fdt_setprop(new_dtb, nodeoffset, prop, value, value_len);
+
+ if (result) {
+ dbgprintf("%s: fdt_setprop failed: %s\n", __func__,
+ fdt_strerror(result));
+ goto on_error;
+ }
+
+ /*
+ * Can't call free on dtb since dtb may have been mmaped by
+ * slurp_file().
+ */
+
+ result = fdt_pack(new_dtb);
+
+ if (result)
+ dbgprintf("%s: Unable to pack device tree: %s\n", __func__,
+ fdt_strerror(result));
+
+ *dtb = new_dtb;
+ *dtb_size = fdt_totalsize(*dtb);
+
+ return 0;
+
+on_error:
+ free(new_dtb);
+ return result;
+}
+
+int dtb_delete_property(char *dtb, const char *node, const char *prop)
+{
+ int result;
+ int nodeoffset = fdt_path_offset(dtb, node);
+
+ if (nodeoffset < 0) {
+ dbgprintf("%s: fdt_path_offset failed: %s\n", __func__,
+ fdt_strerror(nodeoffset));
+ return nodeoffset;
+ }
+
+ result = fdt_delprop(dtb, nodeoffset, prop);
+
+ if (result)
+ dbgprintf("%s: fdt_delprop failed: %s\n", __func__,
+ fdt_strerror(nodeoffset));
+
+ return result;
+}
diff --git a/kexec/dt-ops.h b/kexec/dt-ops.h
new file mode 100644
index 0000000..e70d15d
--- /dev/null
+++ b/kexec/dt-ops.h
@@ -0,0 +1,13 @@
+#if !defined(KEXEC_DT_OPS_H)
+#define KEXEC_DT_OPS_H
+
+#include <sys/types.h>
+
+int dtb_set_initrd(char **dtb, off_t *dtb_size, off_t start, off_t end);
+int dtb_set_bootargs(char **dtb, off_t *dtb_size, const char *command_line);
+int dtb_set_property(char **dtb, off_t *dtb_size, const char *node,
+ const char *prop, const void *value, int value_len);
+
+int dtb_delete_property(char *dtb, const char *node, const char *prop);
+
+#endif
--
2.7.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v5 0/3] arm64 kexec-tools patches
@ 2016-09-01 21:51 ` Geoff Levand
0 siblings, 0 replies; 14+ messages in thread
From: Geoff Levand @ 2016-09-01 21:51 UTC (permalink / raw)
To: linux-arm-kernel
This series adds the core support for kexec re-boot on ARM64.
Linux kernel support for ARM64 kexec reboot has been merged in v4.8-rc1 with the
expectation that it will be included in the v4.8 stable kernel release.
For ARM64 kdump support see Takahiro's latest kdump patches [1].
[1] http://lists.infradead.org/pipermail/kexec
Changes for v5 (Sep 1, 2016, 2m):
o Add common routine arm64_locate_kernel_segment().
Changes for v4 (Aug 18, 2016, 1m):
o Fix some error return values and error messages.
o Add enum arm64_header_page_size.
o Rename page_offset to vp_offset.
Changes for v3 (Aug 10, 2016, 1m):
o Rebase to 2.0.13.
o Add support for flag bits 1-3 to arm64 image-header.h.
o Fix OPT_ARCH_MAX value.
o Use fdt_pack() in dtb_set_property().
o Remove patch ("kexec: (bugfix) calc correct end address of memory ranges in device tree").
Changes for v2 (July 26, 2016, 0m):
o Inline some small routines.
o Reformat some dbgprintf messages.
o Remove debug_brk from entry.S
o Change arm64_image_header.flags to uint64_t.
o Look in iomem then dt for mem info.
o Remove check_cpu_nodes.
o Remove purgatory printing.
First submission v1 (July 20, 2016).
-Geoff
The following changes since commit 67488beb0a6ee8ad2c0b05f721a9e00041fab93a:
kexec-tools 2.0.13 (2016-08-08 12:26:44 +0200)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/geoff/kexec-tools.git for-merge-arm64-v5
for you to fetch changes up to 26a67f5fa8ecf7d76c908a5e0d8ee8bf4f9cb578:
arm64: Add support for binary image files (2016-09-01 14:33:49 -0700)
----------------------------------------------------------------
Geoff Levand (2):
kexec: Add common device tree routines
arm64: Add arm64 kexec support
Pratyush Anand (1):
arm64: Add support for binary image files
configure.ac | 3 +
kexec/Makefile | 5 +
kexec/arch/arm64/Makefile | 40 ++
kexec/arch/arm64/crashdump-arm64.c | 21 +
kexec/arch/arm64/crashdump-arm64.h | 12 +
kexec/arch/arm64/image-header.h | 146 +++++++
kexec/arch/arm64/include/arch/options.h | 39 ++
kexec/arch/arm64/kexec-arm64.c | 717 ++++++++++++++++++++++++++++++++
kexec/arch/arm64/kexec-arm64.h | 71 ++++
kexec/arch/arm64/kexec-elf-arm64.c | 146 +++++++
kexec/arch/arm64/kexec-image-arm64.c | 80 ++++
kexec/dt-ops.c | 145 +++++++
kexec/dt-ops.h | 13 +
kexec/kexec-syscall.h | 8 +-
purgatory/Makefile | 1 +
purgatory/arch/arm64/Makefile | 18 +
purgatory/arch/arm64/entry.S | 51 +++
purgatory/arch/arm64/purgatory-arm64.c | 19 +
18 files changed, 1533 insertions(+), 2 deletions(-)
create mode 100644 kexec/arch/arm64/Makefile
create mode 100644 kexec/arch/arm64/crashdump-arm64.c
create mode 100644 kexec/arch/arm64/crashdump-arm64.h
create mode 100644 kexec/arch/arm64/image-header.h
create mode 100644 kexec/arch/arm64/include/arch/options.h
create mode 100644 kexec/arch/arm64/kexec-arm64.c
create mode 100644 kexec/arch/arm64/kexec-arm64.h
create mode 100644 kexec/arch/arm64/kexec-elf-arm64.c
create mode 100644 kexec/arch/arm64/kexec-image-arm64.c
create mode 100644 kexec/dt-ops.c
create mode 100644 kexec/dt-ops.h
create mode 100644 purgatory/arch/arm64/Makefile
create mode 100644 purgatory/arch/arm64/entry.S
create mode 100644 purgatory/arch/arm64/purgatory-arm64.c
--
2.7.4
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v5 3/3] arm64: Add support for binary image files
2016-09-01 21:51 ` Geoff Levand
@ 2016-09-01 21:51 ` Geoff Levand
-1 siblings, 0 replies; 14+ messages in thread
From: Geoff Levand @ 2016-09-01 21:51 UTC (permalink / raw)
To: linux-arm-kernel
From: Pratyush Anand <panand@redhat.com>
Signed-off-by: Pratyush Anand <panand@redhat.com>
[Reworked and cleaned up]
Signed-off-by: Geoff Levand <geoff@infradead.org>
---
kexec/arch/arm64/kexec-image-arm64.c | 49 ++++++++++++++++++++++++++++++++----
1 file changed, 44 insertions(+), 5 deletions(-)
diff --git a/kexec/arch/arm64/kexec-image-arm64.c b/kexec/arch/arm64/kexec-image-arm64.c
index 42d2ea7..960ed96 100644
--- a/kexec/arch/arm64/kexec-image-arm64.c
+++ b/kexec/arch/arm64/kexec-image-arm64.c
@@ -3,7 +3,9 @@
*/
#define _GNU_SOURCE
+
#include "kexec-arm64.h"
+#include <limits.h>
int image_arm64_probe(const char *kernel_buf, off_t kernel_size)
{
@@ -21,14 +23,53 @@ int image_arm64_probe(const char *kernel_buf, off_t kernel_size)
return -1;
}
- fprintf(stderr, "kexec: ARM64 binary image files are currently NOT SUPPORTED.\n");
- return -1;
+ return 0;
}
int image_arm64_load(int argc, char **argv, const char *kernel_buf,
off_t kernel_size, struct kexec_info *info)
{
- return -1;
+ const struct arm64_image_header *header;
+ unsigned long kernel_segment;
+ int result;
+
+ header = (const struct arm64_image_header *)(kernel_buf);
+
+ if (arm64_process_image_header(header))
+ return -1;
+
+ kernel_segment = arm64_locate_kernel_segment(info);
+
+ if (kernel_segment == ULONG_MAX) {
+ dbgprintf("%s: Kernel segment is not allocated\n", __func__);
+ result = -EFAILED;
+ goto exit;
+ }
+
+ dbgprintf("%s: kernel_segment: %016lx\n", __func__, kernel_segment);
+ dbgprintf("%s: text_offset: %016lx\n", __func__,
+ arm64_mem.text_offset);
+ dbgprintf("%s: image_size: %016lx\n", __func__,
+ arm64_mem.image_size);
+ dbgprintf("%s: phys_offset: %016lx\n", __func__,
+ arm64_mem.phys_offset);
+ dbgprintf("%s: vp_offset: %016lx\n", __func__,
+ arm64_mem.vp_offset);
+ dbgprintf("%s: PE format: %s\n", __func__,
+ (arm64_header_check_pe_sig(header) ? "yes" : "no"));
+
+ /* load the kernel */
+ add_segment_phys_virt(info, kernel_buf, kernel_size,
+ kernel_segment + arm64_mem.text_offset,
+ arm64_mem.image_size, 0);
+
+ result = arm64_load_other_segments(info, kernel_segment
+ + arm64_mem.text_offset);
+
+exit:
+ if (result)
+ fprintf(stderr, "kexec: load failed.\n");
+ return result;
}
void image_arm64_usage(void)
@@ -36,6 +77,4 @@ void image_arm64_usage(void)
printf(
" An ARM64 binary image, compressed or not, big or little endian.\n"
" Typically an Image, Image.gz or Image.lzma file.\n\n");
- printf(
-" ARM64 binary image files are currently NOT SUPPORTED.\n\n");
}
--
2.7.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v5 2/3] arm64: Add arm64 kexec support
2016-09-01 21:51 ` Geoff Levand
@ 2016-09-01 21:51 ` Geoff Levand
-1 siblings, 0 replies; 14+ messages in thread
From: Geoff Levand @ 2016-09-01 21:51 UTC (permalink / raw)
To: linux-arm-kernel
Add kexec reboot support for ARM64 platforms.
Signed-off-by: Geoff Levand <geoff@infradead.org>
---
configure.ac | 3 +
kexec/Makefile | 1 +
kexec/arch/arm64/Makefile | 40 ++
kexec/arch/arm64/crashdump-arm64.c | 21 +
kexec/arch/arm64/crashdump-arm64.h | 12 +
| 146 +++++++
kexec/arch/arm64/include/arch/options.h | 39 ++
kexec/arch/arm64/kexec-arm64.c | 717 ++++++++++++++++++++++++++++++++
kexec/arch/arm64/kexec-arm64.h | 71 ++++
kexec/arch/arm64/kexec-elf-arm64.c | 146 +++++++
kexec/arch/arm64/kexec-image-arm64.c | 41 ++
kexec/kexec-syscall.h | 8 +-
purgatory/Makefile | 1 +
purgatory/arch/arm64/Makefile | 18 +
purgatory/arch/arm64/entry.S | 51 +++
purgatory/arch/arm64/purgatory-arm64.c | 19 +
16 files changed, 1332 insertions(+), 2 deletions(-)
create mode 100644 kexec/arch/arm64/Makefile
create mode 100644 kexec/arch/arm64/crashdump-arm64.c
create mode 100644 kexec/arch/arm64/crashdump-arm64.h
create mode 100644 kexec/arch/arm64/image-header.h
create mode 100644 kexec/arch/arm64/include/arch/options.h
create mode 100644 kexec/arch/arm64/kexec-arm64.c
create mode 100644 kexec/arch/arm64/kexec-arm64.h
create mode 100644 kexec/arch/arm64/kexec-elf-arm64.c
create mode 100644 kexec/arch/arm64/kexec-image-arm64.c
create mode 100644 purgatory/arch/arm64/Makefile
create mode 100644 purgatory/arch/arm64/entry.S
create mode 100644 purgatory/arch/arm64/purgatory-arm64.c
diff --git a/configure.ac b/configure.ac
index 736da6e..ba89075 100644
--- a/configure.ac
+++ b/configure.ac
@@ -34,6 +34,9 @@ case $target_cpu in
ARCH="ppc64"
SUBARCH="LE"
;;
+ aarch64* )
+ ARCH="arm64"
+ ;;
arm* )
ARCH="arm"
;;
diff --git a/kexec/Makefile b/kexec/Makefile
index cc3f08b..39f365f 100644
--- a/kexec/Makefile
+++ b/kexec/Makefile
@@ -79,6 +79,7 @@ KEXEC_SRCS += $($(ARCH)_DT_OPS)
include $(srcdir)/kexec/arch/alpha/Makefile
include $(srcdir)/kexec/arch/arm/Makefile
+include $(srcdir)/kexec/arch/arm64/Makefile
include $(srcdir)/kexec/arch/i386/Makefile
include $(srcdir)/kexec/arch/ia64/Makefile
include $(srcdir)/kexec/arch/m68k/Makefile
diff --git a/kexec/arch/arm64/Makefile b/kexec/arch/arm64/Makefile
new file mode 100644
index 0000000..37414dc
--- /dev/null
+++ b/kexec/arch/arm64/Makefile
@@ -0,0 +1,40 @@
+
+arm64_FS2DT += kexec/fs2dt.c
+arm64_FS2DT_INCLUDE += -include $(srcdir)/kexec/arch/arm64/kexec-arm64.h \
+ -include $(srcdir)/kexec/arch/arm64/crashdump-arm64.h
+
+arm64_DT_OPS += kexec/dt-ops.c
+
+arm64_CPPFLAGS += -I $(srcdir)/kexec/
+
+arm64_KEXEC_SRCS += \
+ kexec/arch/arm64/kexec-arm64.c \
+ kexec/arch/arm64/kexec-image-arm64.c \
+ kexec/arch/arm64/kexec-elf-arm64.c \
+ kexec/arch/arm64/crashdump-arm64.c
+
+arm64_ARCH_REUSE_INITRD =
+arm64_ADD_SEGMENT =
+arm64_VIRT_TO_PHYS =
+arm64_PHYS_TO_VIRT =
+
+dist += $(arm64_KEXEC_SRCS) \
+ kexec/arch/arm64/Makefile \
+ kexec/arch/arm64/kexec-arm64.h \
+ kexec/arch/arm64/crashdump-arm64.h
+
+ifdef HAVE_LIBFDT
+
+LIBS += -lfdt
+
+else
+
+include $(srcdir)/kexec/libfdt/Makefile.libfdt
+
+libfdt_SRCS += $(LIBFDT_SRCS:%=kexec/libfdt/%)
+
+arm64_CPPFLAGS += -I$(srcdir)/kexec/libfdt
+
+arm64_KEXEC_SRCS += $(libfdt_SRCS)
+
+endif
diff --git a/kexec/arch/arm64/crashdump-arm64.c b/kexec/arch/arm64/crashdump-arm64.c
new file mode 100644
index 0000000..d2272c8
--- /dev/null
+++ b/kexec/arch/arm64/crashdump-arm64.c
@@ -0,0 +1,21 @@
+/*
+ * ARM64 crashdump.
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <linux/elf.h>
+
+#include "kexec.h"
+#include "crashdump.h"
+#include "crashdump-arm64.h"
+#include "kexec-arm64.h"
+#include "kexec-elf.h"
+
+struct memory_ranges usablemem_rgns = {};
+
+int is_crashkernel_mem_reserved(void)
+{
+ return 0;
+}
diff --git a/kexec/arch/arm64/crashdump-arm64.h b/kexec/arch/arm64/crashdump-arm64.h
new file mode 100644
index 0000000..f33c7a2
--- /dev/null
+++ b/kexec/arch/arm64/crashdump-arm64.h
@@ -0,0 +1,12 @@
+/*
+ * ARM64 crashdump.
+ */
+
+#if !defined(CRASHDUMP_ARM64_H)
+#define CRASHDUMP_ARM64_H
+
+#include "kexec.h"
+
+extern struct memory_ranges usablemem_rgns;
+
+#endif
--git a/kexec/arch/arm64/image-header.h b/kexec/arch/arm64/image-header.h
new file mode 100644
index 0000000..158d411
--- /dev/null
+++ b/kexec/arch/arm64/image-header.h
@@ -0,0 +1,146 @@
+/*
+ * ARM64 binary image header.
+ */
+
+#if !defined(__ARM64_IMAGE_HEADER_H)
+#define __ARM64_IMAGE_HEADER_H
+
+#include <endian.h>
+#include <stdint.h>
+
+/**
+ * struct arm64_image_header - arm64 kernel image header.
+ *
+ * @pe_sig: Optional PE format 'MZ' signature.
+ * @branch_code: Reserved for instructions to branch to stext.
+ * @text_offset: The image load offset in LSB byte order.
+ * @image_size: An estimated size of the memory image size in LSB byte order.
+ * @flags: Bit flags in LSB byte order:
+ * Bit 0: Image byte order: 1=MSB.
+ * Bit 1-2: Kernel page size: 1=4K, 2=16K, 3=64K.
+ * Bit 3: Image placement: 0=low.
+ * @reserved_1: Reserved.
+ * @magic: Magic number, "ARM\x64".
+ * @pe_header: Optional offset to a PE format header.
+ **/
+
+struct arm64_image_header {
+ uint8_t pe_sig[2];
+ uint16_t branch_code[3];
+ uint64_t text_offset;
+ uint64_t image_size;
+ uint64_t flags;
+ uint64_t reserved_1[3];
+ uint8_t magic[4];
+ uint32_t pe_header;
+};
+
+static const uint8_t arm64_image_magic[4] = {'A', 'R', 'M', 0x64U};
+static const uint8_t arm64_image_pe_sig[2] = {'M', 'Z'};
+static const uint64_t arm64_image_flag_be = (1UL << 0);
+static const uint64_t arm64_image_flag_page_size = (3UL << 1);
+static const uint64_t arm64_image_flag_placement = (1UL << 3);
+
+/**
+ * enum arm64_header_page_size
+ */
+
+enum arm64_header_page_size {
+ arm64_header_page_size_invalid = 0,
+ arm64_header_page_size_4k,
+ arm64_header_page_size_16k,
+ arm64_header_page_size_64k
+};
+
+/**
+ * arm64_header_check_magic - Helper to check the arm64 image header.
+ *
+ * Returns non-zero if header is OK.
+ */
+
+static inline int arm64_header_check_magic(const struct arm64_image_header *h)
+{
+ if (!h)
+ return 0;
+
+ return (h->magic[0] == arm64_image_magic[0]
+ && h->magic[1] == arm64_image_magic[1]
+ && h->magic[2] == arm64_image_magic[2]
+ && h->magic[3] == arm64_image_magic[3]);
+}
+
+/**
+ * arm64_header_check_pe_sig - Helper to check the arm64 image header.
+ *
+ * Returns non-zero if 'MZ' signature is found.
+ */
+
+static inline int arm64_header_check_pe_sig(const struct arm64_image_header *h)
+{
+ if (!h)
+ return 0;
+
+ return (h->pe_sig[0] == arm64_image_pe_sig[0]
+ && h->pe_sig[1] == arm64_image_pe_sig[1]);
+}
+
+/**
+ * arm64_header_check_msb - Helper to check the arm64 image header.
+ *
+ * Returns non-zero if the image was built as big endian.
+ */
+
+static inline int arm64_header_check_msb(const struct arm64_image_header *h)
+{
+ if (!h)
+ return 0;
+
+ return (le64toh(h->flags) & arm64_image_flag_be) >> 0;
+}
+
+/**
+ * arm64_header_page_size
+ */
+
+static inline enum arm64_header_page_size arm64_header_page_size(
+ const struct arm64_image_header *h)
+{
+ if (!h)
+ return 0;
+
+ return (le64toh(h->flags) & arm64_image_flag_page_size) >> 1;
+}
+
+/**
+ * arm64_header_placement
+ *
+ * Returns non-zero if the image has no physical placement restrictions.
+ */
+
+static inline int arm64_header_placement(const struct arm64_image_header *h)
+{
+ if (!h)
+ return 0;
+
+ return (le64toh(h->flags) & arm64_image_flag_placement) >> 3;
+}
+
+static inline uint64_t arm64_header_text_offset(
+ const struct arm64_image_header *h)
+{
+ if (!h)
+ return 0;
+
+ return le64toh(h->text_offset);
+}
+
+static inline uint64_t arm64_header_image_size(
+ const struct arm64_image_header *h)
+{
+ if (!h)
+ return 0;
+
+ return le64toh(h->image_size);
+}
+
+#endif
diff --git a/kexec/arch/arm64/include/arch/options.h b/kexec/arch/arm64/include/arch/options.h
new file mode 100644
index 0000000..a17d933
--- /dev/null
+++ b/kexec/arch/arm64/include/arch/options.h
@@ -0,0 +1,39 @@
+#if !defined(KEXEC_ARCH_ARM64_OPTIONS_H)
+#define KEXEC_ARCH_ARM64_OPTIONS_H
+
+#define OPT_APPEND ((OPT_MAX)+0)
+#define OPT_DTB ((OPT_MAX)+1)
+#define OPT_INITRD ((OPT_MAX)+2)
+#define OPT_REUSE_CMDLINE ((OPT_MAX)+3)
+#define OPT_ARCH_MAX ((OPT_MAX)+4)
+
+#define KEXEC_ARCH_OPTIONS \
+ KEXEC_OPTIONS \
+ { "append", 1, NULL, OPT_APPEND }, \
+ { "command-line", 1, NULL, OPT_APPEND }, \
+ { "dtb", 1, NULL, OPT_DTB }, \
+ { "initrd", 1, NULL, OPT_INITRD }, \
+ { "ramdisk", 1, NULL, OPT_INITRD }, \
+ { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE }, \
+
+#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR /* Only accept long arch options. */
+#define KEXEC_ALL_OPTIONS KEXEC_ARCH_OPTIONS
+#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR
+
+static const char arm64_opts_usage[] __attribute__ ((unused)) =
+" --append=STRING Set the kernel command line to STRING.\n"
+" --command-line=STRING Set the kernel command line to STRING.\n"
+" --dtb=FILE Use FILE as the device tree blob.\n"
+" --initrd=FILE Use FILE as the kernel initial ramdisk.\n"
+" --ramdisk=FILE Use FILE as the kernel initial ramdisk.\n"
+" --reuse-cmdline Use kernel command line from running system.\n";
+
+struct arm64_opts {
+ const char *command_line;
+ const char *dtb;
+ const char *initrd;
+};
+
+extern struct arm64_opts arm64_opts;
+
+#endif
diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c
new file mode 100644
index 0000000..7183dac
--- /dev/null
+++ b/kexec/arch/arm64/kexec-arm64.c
@@ -0,0 +1,717 @@
+/*
+ * ARM64 kexec.
+ */
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <libfdt.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <linux/elf-em.h>
+#include <elf.h>
+
+#include "kexec.h"
+#include "kexec-arm64.h"
+#include "crashdump.h"
+#include "crashdump-arm64.h"
+#include "dt-ops.h"
+#include "fs2dt.h"
+#include "kexec-syscall.h"
+#include "arch/options.h"
+
+/* Global varables the core kexec routines expect. */
+
+unsigned char reuse_initrd;
+
+off_t initrd_base;
+off_t initrd_size;
+
+const struct arch_map_entry arches[] = {
+ { "aarch64", KEXEC_ARCH_ARM64 },
+ { "aarch64_be", KEXEC_ARCH_ARM64 },
+ { NULL, 0 },
+};
+
+struct file_type file_type[] = {
+ {"vmlinux", elf_arm64_probe, elf_arm64_load, elf_arm64_usage},
+ {"Image", image_arm64_probe, image_arm64_load, image_arm64_usage},
+};
+
+int file_types = sizeof(file_type) / sizeof(file_type[0]);
+
+/* arm64 global varables. */
+
+struct arm64_opts arm64_opts;
+struct arm64_mem arm64_mem = {
+ .phys_offset = arm64_mem_ngv,
+ .vp_offset = arm64_mem_ngv,
+};
+
+uint64_t get_phys_offset(void)
+{
+ assert(arm64_mem.phys_offset != arm64_mem_ngv);
+ return arm64_mem.phys_offset;
+}
+
+uint64_t get_vp_offset(void)
+{
+ assert(arm64_mem.vp_offset != arm64_mem_ngv);
+ return arm64_mem.vp_offset;
+}
+
+/**
+ * arm64_process_image_header - Process the arm64 image header.
+ *
+ * Make a guess that KERNEL_IMAGE_SIZE will be enough for older kernels.
+ */
+
+int arm64_process_image_header(const struct arm64_image_header *h)
+{
+#if !defined(KERNEL_IMAGE_SIZE)
+# define KERNEL_IMAGE_SIZE MiB(16)
+#endif
+
+ if (!arm64_header_check_magic(h))
+ return -EFAILED;
+
+ if (h->image_size) {
+ arm64_mem.text_offset = arm64_header_text_offset(h);
+ arm64_mem.image_size = arm64_header_image_size(h);
+ } else {
+ /* For 3.16 and older kernels. */
+ arm64_mem.text_offset = 0x80000;
+ arm64_mem.image_size = KERNEL_IMAGE_SIZE;
+ fprintf(stderr,
+ "kexec: %s: Warning: Kernel image size set to %lu MiB.\n"
+ " Please verify compatability with lodaed kernel.\n",
+ __func__, KERNEL_IMAGE_SIZE / 1024UL / 1024UL);
+ }
+
+ return 0;
+}
+
+void arch_usage(void)
+{
+ printf(arm64_opts_usage);
+}
+
+int arch_process_options(int argc, char **argv)
+{
+ static const char short_options[] = KEXEC_OPT_STR "";
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { 0 }
+ };
+ int opt;
+ char *cmdline = NULL;
+ const char *append = NULL;
+
+ for (opt = 0; opt != -1; ) {
+ opt = getopt_long(argc, argv, short_options, options, 0);
+
+ switch (opt) {
+ case OPT_APPEND:
+ append = optarg;
+ break;
+ case OPT_REUSE_CMDLINE:
+ cmdline = get_command_line();
+ break;
+ case OPT_DTB:
+ arm64_opts.dtb = optarg;
+ break;
+ case OPT_INITRD:
+ arm64_opts.initrd = optarg;
+ break;
+ case OPT_PANIC:
+ die("load-panic (-p) not supported");
+ break;
+ default:
+ break; /* Ignore core and unknown options. */
+ }
+ }
+
+ arm64_opts.command_line = concat_cmdline(cmdline, append);
+
+ dbgprintf("%s:%d: command_line: %s\n", __func__, __LINE__,
+ arm64_opts.command_line);
+ dbgprintf("%s:%d: initrd: %s\n", __func__, __LINE__,
+ arm64_opts.initrd);
+ dbgprintf("%s:%d: dtb: %s\n", __func__, __LINE__, arm64_opts.dtb);
+
+ return 0;
+}
+
+/**
+ * struct dtb - Info about a binary device tree.
+ *
+ * @buf: Device tree data.
+ * @size: Device tree data size.
+ * @name: Shorthand name of this dtb for messages.
+ * @path: Filesystem path.
+ */
+
+struct dtb {
+ char *buf;
+ off_t size;
+ const char *name;
+ const char *path;
+};
+
+/**
+ * dump_reservemap - Dump the dtb's reservemap.
+ */
+
+static void dump_reservemap(const struct dtb *dtb)
+{
+ int i;
+
+ for (i = 0; ; i++) {
+ uint64_t address;
+ uint64_t size;
+
+ fdt_get_mem_rsv(dtb->buf, i, &address, &size);
+
+ if (!size)
+ break;
+
+ dbgprintf("%s: %s {%" PRIx64 ", %" PRIx64 "}\n", __func__,
+ dtb->name, address, size);
+ }
+}
+
+/**
+ * set_bootargs - Set the dtb's bootargs.
+ */
+
+static int set_bootargs(struct dtb *dtb, const char *command_line)
+{
+ int result;
+
+ if (!command_line || !command_line[0])
+ return 0;
+
+ result = dtb_set_bootargs(&dtb->buf, &dtb->size, command_line);
+
+ if (result) {
+ fprintf(stderr,
+ "kexec: Set device tree bootargs failed.\n");
+ return -EFAILED;
+ }
+
+ return 0;
+}
+
+/**
+ * read_proc_dtb - Read /proc/device-tree.
+ */
+
+static int read_proc_dtb(struct dtb *dtb)
+{
+ int result;
+ struct stat s;
+ static const char path[] = "/proc/device-tree";
+
+ result = stat(path, &s);
+
+ if (result) {
+ dbgprintf("%s: %s\n", __func__, strerror(errno));
+ return -EFAILED;
+ }
+
+ dtb->path = path;
+ create_flatten_tree((char **)&dtb->buf, &dtb->size, NULL);
+
+ return 0;
+}
+
+/**
+ * read_sys_dtb - Read /sys/firmware/fdt.
+ */
+
+static int read_sys_dtb(struct dtb *dtb)
+{
+ int result;
+ struct stat s;
+ static const char path[] = "/sys/firmware/fdt";
+
+ result = stat(path, &s);
+
+ if (result) {
+ dbgprintf("%s: %s\n", __func__, strerror(errno));
+ return -EFAILED;
+ }
+
+ dtb->path = path;
+ dtb->buf = slurp_file(path, &dtb->size);
+
+ return 0;
+}
+
+/**
+ * read_1st_dtb - Read the 1st stage kernel's dtb.
+ */
+
+static int read_1st_dtb(struct dtb *dtb)
+{
+ int result;
+
+ result = read_sys_dtb(dtb);
+
+ if (!result)
+ goto on_success;
+
+ result = read_proc_dtb(dtb);
+
+ if (!result)
+ goto on_success;
+
+ dbgprintf("%s: not found\n", __func__);
+ return -EFAILED;
+
+on_success:
+ dbgprintf("%s: found %s\n", __func__, dtb->path);
+ return 0;
+}
+
+/**
+ * setup_2nd_dtb - Setup the 2nd stage kernel's dtb.
+ */
+
+static int setup_2nd_dtb(struct dtb *dtb, char *command_line)
+{
+ int result;
+
+ result = fdt_check_header(dtb->buf);
+
+ if (result) {
+ fprintf(stderr, "kexec: Invalid 2nd device tree.\n");
+ return -EFAILED;
+ }
+
+ result = set_bootargs(dtb, command_line);
+
+ dump_reservemap(dtb);
+
+ return result;
+}
+
+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 (hole == ULONG_MAX)
+ dbgprintf("%s: locate_hole failed\n", __func__);
+
+ return hole;
+}
+
+/**
+ * arm64_load_other_segments - Prepare the dtb, initrd and purgatory segments.
+ */
+
+int arm64_load_other_segments(struct kexec_info *info,
+ unsigned long image_base)
+{
+ int result;
+ unsigned long dtb_base;
+ unsigned long hole_min;
+ unsigned long hole_max;
+ char *initrd_buf = NULL;
+ struct dtb dtb;
+ char command_line[COMMAND_LINE_SIZE] = "";
+
+ if (arm64_opts.command_line) {
+ strncpy(command_line, arm64_opts.command_line,
+ sizeof(command_line));
+ command_line[sizeof(command_line) - 1] = 0;
+ }
+
+ if (arm64_opts.dtb) {
+ dtb.name = "dtb_2";
+ dtb.buf = slurp_file(arm64_opts.dtb, &dtb.size);
+ } else {
+ dtb.name = "dtb_1";
+ result = read_1st_dtb(&dtb);
+
+ if (result) {
+ fprintf(stderr,
+ "kexec: Error: No device tree available.\n");
+ return -EFAILED;
+ }
+ }
+
+ result = setup_2nd_dtb(&dtb, command_line);
+
+ if (result)
+ return -EFAILED;
+
+ /* Put the other segments after the image. */
+
+ hole_min = image_base + arm64_mem.image_size;
+ hole_max = ULONG_MAX;
+
+ if (arm64_opts.initrd) {
+ initrd_buf = slurp_file(arm64_opts.initrd, &initrd_size);
+
+ if (!initrd_buf)
+ fprintf(stderr, "kexec: Empty ramdisk file.\n");
+ else {
+ /*
+ * Put the initrd after the kernel. As specified in
+ * booting.txt, align to 1 GiB.
+ */
+
+ initrd_base = add_buffer_phys_virt(info, initrd_buf,
+ initrd_size, initrd_size, GiB(1),
+ hole_min, hole_max, 1, 0);
+
+ /* initrd_base is valid if we got here. */
+
+ dbgprintf("initrd: base %lx, size %lxh (%ld)\n",
+ initrd_base, initrd_size, initrd_size);
+
+ /* Check size limit as specified in booting.txt. */
+
+ if (initrd_base - image_base + initrd_size > GiB(32)) {
+ fprintf(stderr, "kexec: Error: image + initrd too big.\n");
+ return -EFAILED;
+ }
+
+ result = dtb_set_initrd((char **)&dtb.buf,
+ &dtb.size, initrd_base,
+ initrd_base + initrd_size);
+
+ if (result)
+ return -EFAILED;
+ }
+ }
+
+ /* Check size limit as specified in booting.txt. */
+
+ if (dtb.size > MiB(2)) {
+ fprintf(stderr, "kexec: Error: dtb too big.\n");
+ return -EFAILED;
+ }
+
+ dtb_base = add_buffer_phys_virt(info, dtb.buf, dtb.size, dtb.size,
+ 0, hole_min, hole_max, 1, 0);
+
+ /* dtb_base is valid if we got here. */
+
+ dbgprintf("dtb: base %lx, size %lxh (%ld)\n", dtb_base, dtb.size,
+ dtb.size);
+
+ elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
+ hole_min, hole_max, 1, 0);
+
+ info->entry = (void *)elf_rel_get_addr(&info->rhdr, "purgatory_start");
+
+ elf_rel_set_symbol(&info->rhdr, "arm64_kernel_entry", &image_base,
+ sizeof(image_base));
+
+ elf_rel_set_symbol(&info->rhdr, "arm64_dtb_addr", &dtb_base,
+ sizeof(dtb_base));
+
+ return 0;
+}
+
+/**
+ * virt_to_phys - For processing elf file values.
+ */
+
+unsigned long virt_to_phys(unsigned long v)
+{
+ unsigned long p;
+
+ p = v - get_vp_offset() + get_phys_offset();
+
+ return p;
+}
+
+/**
+ * phys_to_virt - For crashdump setup.
+ */
+
+unsigned long phys_to_virt(struct crash_elf_info *elf_info,
+ unsigned long long p)
+{
+ unsigned long v;
+
+ v = p - get_phys_offset() + elf_info->page_offset;
+
+ return v;
+}
+
+/**
+ * add_segment - Use virt_to_phys when loading elf files.
+ */
+
+void add_segment(struct kexec_info *info, const void *buf, size_t bufsz,
+ unsigned long base, size_t memsz)
+{
+ add_segment_phys_virt(info, buf, bufsz, base, memsz, 1);
+}
+
+/**
+ * get_memory_ranges_iomem_cb - Helper for get_memory_ranges_iomem.
+ */
+
+static int get_memory_ranges_iomem_cb(void *data, int nr, char *str,
+ unsigned long long base, unsigned long long length)
+{
+ struct memory_range *r;
+
+ if (nr >= KEXEC_SEGMENT_MAX)
+ return -1;
+
+ r = (struct memory_range *)data + nr;
+ r->type = RANGE_RAM;
+ r->start = base;
+ r->end = base + length - 1;
+
+ set_phys_offset(r->start);
+
+ dbgprintf("%s: %016llx - %016llx : %s", __func__, r->start,
+ r->end, str);
+
+ return 0;
+}
+
+/**
+ * get_memory_ranges_iomem - Try to get the memory ranges from /proc/iomem.
+ */
+
+static int get_memory_ranges_iomem(struct memory_range *array,
+ unsigned int *count)
+{
+ *count = kexec_iomem_for_each_line("System RAM\n",
+ get_memory_ranges_iomem_cb, array);
+
+ if (!*count) {
+ dbgprintf("%s: failed: No RAM found.\n", __func__);
+ return -EFAILED;
+ }
+
+ return 0;
+}
+
+/**
+ * get_memory_ranges_dt - Try to get the memory ranges from the 1st stage dtb.
+ */
+
+static int get_memory_ranges_dt(struct memory_range *array, unsigned int *count)
+{
+ struct region {uint64_t base; uint64_t size;};
+ struct dtb dtb = {.name = "range_dtb"};
+ int offset;
+ int result;
+
+ *count = 0;
+
+ result = read_1st_dtb(&dtb);
+
+ if (result) {
+ goto on_error;
+ }
+
+ result = fdt_check_header(dtb.buf);
+
+ if (result) {
+ dbgprintf("%s:%d: %s: fdt_check_header failed:%s\n", __func__,
+ __LINE__, dtb.path, fdt_strerror(result));
+ goto on_error;
+ }
+
+ for (offset = 0; ; ) {
+ const struct region *region;
+ const struct region *end;
+ int len;
+
+ offset = fdt_subnode_offset(dtb.buf, offset, "memory");
+
+ if (offset == -FDT_ERR_NOTFOUND)
+ break;
+
+ if (offset <= 0) {
+ dbgprintf("%s:%d: fdt_subnode_offset failed: %d %s\n",
+ __func__, __LINE__, offset,
+ fdt_strerror(offset));
+ goto on_error;
+ }
+
+ dbgprintf("%s:%d: node_%d %s\n", __func__, __LINE__, offset,
+ fdt_get_name(dtb.buf, offset, NULL));
+
+ region = fdt_getprop(dtb.buf, offset, "reg", &len);
+
+ if (region <= 0) {
+ dbgprintf("%s:%d: fdt_getprop failed: %d %s\n",
+ __func__, __LINE__, offset,
+ fdt_strerror(offset));
+ goto on_error;
+ }
+
+ for (end = region + len / sizeof(*region);
+ region < end && *count < KEXEC_SEGMENT_MAX;
+ region++) {
+ struct memory_range r;
+
+ r.type = RANGE_RAM;
+ r.start = fdt64_to_cpu(region->base);
+ r.end = r.start + fdt64_to_cpu(region->size) - 1;
+
+ if (!region->size) {
+ dbgprintf("%s:%d: SKIP: %016llx - %016llx\n",
+ __func__, __LINE__, r.start, r.end);
+ continue;
+ }
+
+ dbgprintf("%s:%d: RAM: %016llx - %016llx\n", __func__,
+ __LINE__, r.start, r.end);
+
+ array[(*count)++] = r;
+
+ set_phys_offset(r.start);
+ }
+ }
+
+ if (!*count) {
+ dbgprintf("%s:%d: %s: No RAM found.\n", __func__, __LINE__,
+ dtb.path);
+ goto on_error;
+ }
+
+ dbgprintf("%s:%d: %s: Success\n", __func__, __LINE__, dtb.path);
+ result = 0;
+ goto on_exit;
+
+on_error:
+ fprintf(stderr, "%s:%d: %s: Unusable device-tree file\n", __func__,
+ __LINE__, dtb.path);
+ result = -EFAILED;
+
+on_exit:
+ free(dtb.buf);
+ return result;
+}
+
+/**
+ * get_memory_ranges - Try to get the memory ranges some how.
+ */
+
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags)
+{
+ static struct memory_range array[KEXEC_SEGMENT_MAX];
+ unsigned int count;
+ int result;
+
+ result = get_memory_ranges_iomem(array, &count);
+
+ if (result)
+ result = get_memory_ranges_dt(array, &count);
+
+ *range = result ? NULL : array;
+ *ranges = result ? 0 : count;
+
+ return result;
+}
+
+int arch_compat_trampoline(struct kexec_info *info)
+{
+ return 0;
+}
+
+int machine_verify_elf_rel(struct mem_ehdr *ehdr)
+{
+ return (ehdr->e_machine == EM_AARCH64);
+}
+
+void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
+ unsigned long r_type, void *ptr, unsigned long address,
+ unsigned long value)
+{
+#if !defined(R_AARCH64_ABS64)
+# define R_AARCH64_ABS64 257
+#endif
+
+#if !defined(R_AARCH64_LD_PREL_LO19)
+# define R_AARCH64_LD_PREL_LO19 273
+#endif
+
+#if !defined(R_AARCH64_ADR_PREL_LO21)
+# define R_AARCH64_ADR_PREL_LO21 274
+#endif
+
+#if !defined(R_AARCH64_JUMP26)
+# define R_AARCH64_JUMP26 282
+#endif
+
+#if !defined(R_AARCH64_CALL26)
+# define R_AARCH64_CALL26 283
+#endif
+
+ uint64_t *loc64;
+ uint32_t *loc32;
+ uint64_t *location = (uint64_t *)ptr;
+ uint64_t data = *location;
+ const char *type = NULL;
+
+ switch(r_type) {
+ case R_AARCH64_ABS64:
+ type = "ABS64";
+ loc64 = ptr;
+ *loc64 = cpu_to_elf64(ehdr, elf64_to_cpu(ehdr, *loc64) + value);
+ break;
+ case R_AARCH64_LD_PREL_LO19:
+ type = "LD_PREL_LO19";
+ loc32 = ptr;
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+ + (((value - address) << 3) & 0xffffe0));
+ break;
+ case R_AARCH64_ADR_PREL_LO21:
+ if (value & 3)
+ die("%s: ERROR Unaligned value: %lx\n", __func__,
+ value);
+ type = "ADR_PREL_LO21";
+ loc32 = ptr;
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+ + (((value - address) << 3) & 0xffffe0));
+ break;
+ case R_AARCH64_JUMP26:
+ type = "JUMP26";
+ loc32 = ptr;
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+ + (((value - address) >> 2) & 0x3ffffff));
+ break;
+ case R_AARCH64_CALL26:
+ type = "CALL26";
+ loc32 = ptr;
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+ + (((value - address) >> 2) & 0x3ffffff));
+ break;
+ default:
+ die("%s: ERROR Unknown type: %lu\n", __func__, r_type);
+ break;
+ }
+
+ dbgprintf("%s: %s %016lx->%016lx\n", __func__, type, data, *location);
+}
+
+void arch_reuse_initrd(void)
+{
+ reuse_initrd = 1;
+}
+
+void arch_update_purgatory(struct kexec_info *UNUSED(info))
+{
+}
diff --git a/kexec/arch/arm64/kexec-arm64.h b/kexec/arch/arm64/kexec-arm64.h
new file mode 100644
index 0000000..bac62f8
--- /dev/null
+++ b/kexec/arch/arm64/kexec-arm64.h
@@ -0,0 +1,71 @@
+/*
+ * ARM64 kexec.
+ */
+
+#if !defined(KEXEC_ARM64_H)
+#define KEXEC_ARM64_H
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "image-header.h"
+#include "kexec.h"
+
+#define KEXEC_SEGMENT_MAX 16
+
+#define BOOT_BLOCK_VERSION 17
+#define BOOT_BLOCK_LAST_COMP_VERSION 16
+#define COMMAND_LINE_SIZE 512
+
+#define KiB(x) ((x) * 1024UL)
+#define MiB(x) (KiB(x) * 1024UL)
+#define GiB(x) (MiB(x) * 1024UL)
+
+int elf_arm64_probe(const char *kernel_buf, off_t kernel_size);
+int elf_arm64_load(int argc, char **argv, const char *kernel_buf,
+ off_t kernel_size, struct kexec_info *info);
+void elf_arm64_usage(void);
+
+int image_arm64_probe(const char *kernel_buf, off_t kernel_size);
+int image_arm64_load(int argc, char **argv, const char *kernel_buf,
+ off_t kernel_size, struct kexec_info *info);
+void image_arm64_usage(void);
+
+off_t initrd_base;
+off_t initrd_size;
+
+/**
+ * struct arm64_mem - Memory layout info.
+ */
+
+struct arm64_mem {
+ uint64_t phys_offset;
+ uint64_t text_offset;
+ uint64_t image_size;
+ uint64_t vp_offset;
+};
+
+#define arm64_mem_ngv UINT64_MAX
+struct arm64_mem arm64_mem;
+
+uint64_t get_phys_offset(void);
+uint64_t get_vp_offset(void);
+
+static inline void reset_vp_offset(void)
+{
+ arm64_mem.vp_offset = arm64_mem_ngv;
+}
+
+static inline void set_phys_offset(uint64_t v)
+{
+ if (arm64_mem.phys_offset == arm64_mem_ngv
+ || v < arm64_mem.phys_offset)
+ arm64_mem.phys_offset = v;
+}
+
+int arm64_process_image_header(const struct arm64_image_header *h);
+unsigned long arm64_locate_kernel_segment(struct kexec_info *info);
+int arm64_load_other_segments(struct kexec_info *info,
+ unsigned long image_base);
+
+#endif
diff --git a/kexec/arch/arm64/kexec-elf-arm64.c b/kexec/arch/arm64/kexec-elf-arm64.c
new file mode 100644
index 0000000..daf8bf0
--- /dev/null
+++ b/kexec/arch/arm64/kexec-elf-arm64.c
@@ -0,0 +1,146 @@
+/*
+ * ARM64 kexec elf support.
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <linux/elf.h>
+
+#include "kexec-arm64.h"
+#include "kexec-elf.h"
+#include "kexec-syscall.h"
+
+int elf_arm64_probe(const char *kernel_buf, off_t kernel_size)
+{
+ struct mem_ehdr ehdr;
+ int result;
+
+ result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0);
+
+ if (result < 0) {
+ dbgprintf("%s: Not an ELF executable.\n", __func__);
+ goto on_exit;
+ }
+
+ if (ehdr.e_machine != EM_AARCH64) {
+ dbgprintf("%s: Not an AARCH64 ELF executable.\n", __func__);
+ result = -1;
+ goto on_exit;
+ }
+
+ result = 0;
+on_exit:
+ free_elf_info(&ehdr);
+ return result;
+}
+
+int elf_arm64_load(int argc, char **argv, const char *kernel_buf,
+ off_t kernel_size, struct kexec_info *info)
+{
+ const struct arm64_image_header *header = NULL;
+ unsigned long kernel_segment;
+ struct mem_ehdr ehdr;
+ int result;
+ int i;
+
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ fprintf(stderr, "kexec: kdump not yet supported on arm64\n");
+ return -EFAILED;
+ }
+
+ result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0);
+
+ if (result < 0) {
+ dbgprintf("%s: build_elf_exec_info failed\n", __func__);
+ goto exit;
+ }
+
+ /* Find and process the arm64 image header. */
+
+ for (i = 0; i < ehdr.e_phnum; i++) {
+ struct mem_phdr *phdr = &ehdr.e_phdr[i];
+ unsigned long header_offset;
+
+ if (phdr->p_type != PT_LOAD)
+ continue;
+
+ /*
+ * When CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET=y the image header
+ * could be offset in the elf segment. The linker script sets
+ * ehdr.e_entry to the start of text.
+ */
+
+ header_offset = ehdr.e_entry - phdr->p_vaddr;
+
+ header = (const struct arm64_image_header *)(
+ kernel_buf + phdr->p_offset + header_offset);
+
+ if (!arm64_process_image_header(header)) {
+ dbgprintf("%s: e_entry: %016llx\n", __func__,
+ ehdr.e_entry);
+ dbgprintf("%s: p_vaddr: %016llx\n", __func__,
+ phdr->p_vaddr);
+ dbgprintf("%s: header_offset: %016lx\n", __func__,
+ header_offset);
+
+ break;
+ }
+ }
+
+ if (i == ehdr.e_phnum) {
+ dbgprintf("%s: Valid arm64 header not found\n", __func__);
+ result = -EFAILED;
+ goto exit;
+ }
+
+ kernel_segment = arm64_locate_kernel_segment(info);
+
+ if (kernel_segment == ULONG_MAX) {
+ dbgprintf("%s: Kernel segment is not allocated\n", __func__);
+ result = -EFAILED;
+ goto exit;
+ }
+
+ arm64_mem.vp_offset = _ALIGN_DOWN(ehdr.e_entry, MiB(2));
+ 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__,
+ arm64_mem.text_offset);
+ dbgprintf("%s: image_size: %016lx\n", __func__,
+ arm64_mem.image_size);
+ dbgprintf("%s: phys_offset: %016lx\n", __func__,
+ arm64_mem.phys_offset);
+ dbgprintf("%s: vp_offset: %016lx\n", __func__,
+ arm64_mem.vp_offset);
+ dbgprintf("%s: PE format: %s\n", __func__,
+ (arm64_header_check_pe_sig(header) ? "yes" : "no"));
+
+ /* load the kernel */
+ result = elf_exec_load(&ehdr, info);
+
+ if (result) {
+ dbgprintf("%s: elf_exec_load failed\n", __func__);
+ goto exit;
+ }
+
+ result = arm64_load_other_segments(info, kernel_segment
+ + arm64_mem.text_offset);
+
+exit:
+ reset_vp_offset();
+ free_elf_info(&ehdr);
+ if (result)
+ fprintf(stderr, "kexec: Bad elf image file, load failed.\n");
+ return result;
+}
+
+void elf_arm64_usage(void)
+{
+ printf(
+" An ARM64 ELF image, big or little endian.\n"
+" Typically vmlinux or a stripped version of vmlinux.\n\n");
+}
diff --git a/kexec/arch/arm64/kexec-image-arm64.c b/kexec/arch/arm64/kexec-image-arm64.c
new file mode 100644
index 0000000..42d2ea7
--- /dev/null
+++ b/kexec/arch/arm64/kexec-image-arm64.c
@@ -0,0 +1,41 @@
+/*
+ * ARM64 kexec binary image support.
+ */
+
+#define _GNU_SOURCE
+#include "kexec-arm64.h"
+
+int image_arm64_probe(const char *kernel_buf, off_t kernel_size)
+{
+ const struct arm64_image_header *h;
+
+ if (kernel_size < sizeof(struct arm64_image_header)) {
+ dbgprintf("%s: No arm64 image header.\n", __func__);
+ return -1;
+ }
+
+ h = (const struct arm64_image_header *)(kernel_buf);
+
+ if (!arm64_header_check_magic(h)) {
+ dbgprintf("%s: Bad arm64 image header.\n", __func__);
+ return -1;
+ }
+
+ fprintf(stderr, "kexec: ARM64 binary image files are currently NOT SUPPORTED.\n");
+ return -1;
+}
+
+int image_arm64_load(int argc, char **argv, const char *kernel_buf,
+ off_t kernel_size, struct kexec_info *info)
+{
+ return -1;
+}
+
+void image_arm64_usage(void)
+{
+ printf(
+" An ARM64 binary image, compressed or not, big or little endian.\n"
+" Typically an Image, Image.gz or Image.lzma file.\n\n");
+ printf(
+" ARM64 binary image files are currently NOT SUPPORTED.\n\n");
+}
diff --git a/kexec/kexec-syscall.h b/kexec/kexec-syscall.h
index ce2e20b..c0d0bea 100644
--- a/kexec/kexec-syscall.h
+++ b/kexec/kexec-syscall.h
@@ -39,8 +39,8 @@
#ifdef __s390__
#define __NR_kexec_load 277
#endif
-#ifdef __arm__
-#define __NR_kexec_load __NR_SYSCALL_BASE + 347
+#if defined(__arm__) || defined(__arm64__)
+#define __NR_kexec_load __NR_SYSCALL_BASE + 347
#endif
#if defined(__mips__)
#define __NR_kexec_load 4311
@@ -108,6 +108,7 @@ static inline long kexec_file_load(int kernel_fd, int initrd_fd,
#define KEXEC_ARCH_PPC64 (21 << 16)
#define KEXEC_ARCH_IA_64 (50 << 16)
#define KEXEC_ARCH_ARM (40 << 16)
+#define KEXEC_ARCH_ARM64 (183 << 16)
#define KEXEC_ARCH_S390 (22 << 16)
#define KEXEC_ARCH_SH (42 << 16)
#define KEXEC_ARCH_MIPS_LE (10 << 16)
@@ -153,5 +154,8 @@ static inline long kexec_file_load(int kernel_fd, int initrd_fd,
#ifdef __m68k__
#define KEXEC_ARCH_NATIVE KEXEC_ARCH_68K
#endif
+#if defined(__arm64__)
+#define KEXEC_ARCH_NATIVE KEXEC_ARCH_ARM64
+#endif
#endif /* KEXEC_SYSCALL_H */
diff --git a/purgatory/Makefile b/purgatory/Makefile
index 2b5c061..ca0443c 100644
--- a/purgatory/Makefile
+++ b/purgatory/Makefile
@@ -19,6 +19,7 @@ dist += purgatory/Makefile $(PURGATORY_SRCS) \
include $(srcdir)/purgatory/arch/alpha/Makefile
include $(srcdir)/purgatory/arch/arm/Makefile
+include $(srcdir)/purgatory/arch/arm64/Makefile
include $(srcdir)/purgatory/arch/i386/Makefile
include $(srcdir)/purgatory/arch/ia64/Makefile
include $(srcdir)/purgatory/arch/mips/Makefile
diff --git a/purgatory/arch/arm64/Makefile b/purgatory/arch/arm64/Makefile
new file mode 100644
index 0000000..636abea
--- /dev/null
+++ b/purgatory/arch/arm64/Makefile
@@ -0,0 +1,18 @@
+
+arm64_PURGATORY_EXTRA_CFLAGS = \
+ -mcmodel=large \
+ -fno-stack-protector \
+ -fno-asynchronous-unwind-tables \
+ -Wundef \
+ -Werror-implicit-function-declaration \
+ -Wdeclaration-after-statement \
+ -Werror=implicit-int \
+ -Werror=strict-prototypes
+
+arm64_PURGATORY_SRCS += \
+ purgatory/arch/arm64/entry.S \
+ purgatory/arch/arm64/purgatory-arm64.c
+
+dist += \
+ $(arm64_PURGATORY_SRCS) \
+ purgatory/arch/arm64/Makefile
diff --git a/purgatory/arch/arm64/entry.S b/purgatory/arch/arm64/entry.S
new file mode 100644
index 0000000..adf16f4
--- /dev/null
+++ b/purgatory/arch/arm64/entry.S
@@ -0,0 +1,51 @@
+/*
+ * ARM64 purgatory.
+ */
+
+.macro size, sym:req
+ .size \sym, . - \sym
+.endm
+
+.text
+
+.globl purgatory_start
+purgatory_start:
+
+ adr x19, .Lstack
+ mov sp, x19
+
+ bl purgatory
+
+ /* Start new image. */
+ ldr x17, arm64_kernel_entry
+ ldr x0, arm64_dtb_addr
+ mov x1, xzr
+ mov x2, xzr
+ mov x3, xzr
+ br x17
+
+size purgatory_start
+
+.ltorg
+
+.align 4
+ .rept 256
+ .quad 0
+ .endr
+.Lstack:
+
+.data
+
+.align 3
+
+.globl arm64_kernel_entry
+arm64_kernel_entry:
+ .quad 0
+size arm64_kernel_entry
+
+.globl arm64_dtb_addr
+arm64_dtb_addr:
+ .quad 0
+size arm64_dtb_addr
+
+.end
\ No newline@end of file
diff --git a/purgatory/arch/arm64/purgatory-arm64.c b/purgatory/arch/arm64/purgatory-arm64.c
new file mode 100644
index 0000000..fe50fcf
--- /dev/null
+++ b/purgatory/arch/arm64/purgatory-arm64.c
@@ -0,0 +1,19 @@
+/*
+ * ARM64 purgatory.
+ */
+
+#include <stdint.h>
+#include <purgatory.h>
+
+void putchar(int ch)
+{
+ /* Nothing for now */
+}
+
+void post_verification_setup_arch(void)
+{
+}
+
+void setup_arch(void)
+{
+}
--
2.7.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v5 1/3] kexec: Add common device tree routines
@ 2016-09-01 21:51 ` Geoff Levand
0 siblings, 0 replies; 14+ messages in thread
From: Geoff Levand @ 2016-09-01 21:51 UTC (permalink / raw)
To: Simon Horman
Cc: Pratyush Anand, AKASHI Takahiro, Mark Rutland, kexec, linux-arm-kernel
Common device tree routines that can be shared between all arches
that have device tree support.
Signed-off-by: Geoff Levand <geoff@infradead.org>
---
kexec/Makefile | 4 ++
kexec/dt-ops.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
kexec/dt-ops.h | 13 ++++++
3 files changed, 162 insertions(+)
create mode 100644 kexec/dt-ops.c
create mode 100644 kexec/dt-ops.h
diff --git a/kexec/Makefile b/kexec/Makefile
index e2aee84..cc3f08b 100644
--- a/kexec/Makefile
+++ b/kexec/Makefile
@@ -73,6 +73,10 @@ dist += kexec/mem_regions.c kexec/mem_regions.h
$(ARCH)_MEM_REGIONS =
KEXEC_SRCS += $($(ARCH)_MEM_REGIONS)
+dist += kexec/dt-ops.c kexec/dt-ops.h
+$(ARCH)_DT_OPS =
+KEXEC_SRCS += $($(ARCH)_DT_OPS)
+
include $(srcdir)/kexec/arch/alpha/Makefile
include $(srcdir)/kexec/arch/arm/Makefile
include $(srcdir)/kexec/arch/i386/Makefile
diff --git a/kexec/dt-ops.c b/kexec/dt-ops.c
new file mode 100644
index 0000000..915dbf5
--- /dev/null
+++ b/kexec/dt-ops.c
@@ -0,0 +1,145 @@
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <libfdt.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "kexec.h"
+#include "dt-ops.h"
+
+static const char n_chosen[] = "/chosen";
+
+static const char p_bootargs[] = "bootargs";
+static const char p_initrd_start[] = "linux,initrd-start";
+static const char p_initrd_end[] = "linux,initrd-end";
+
+int dtb_set_initrd(char **dtb, off_t *dtb_size, off_t start, off_t end)
+{
+ int result;
+ uint64_t value;
+
+ dbgprintf("%s: start %jd, end %jd, size %jd (%jd KiB)\n",
+ __func__, (intmax_t)start, (intmax_t)end,
+ (intmax_t)(end - start),
+ (intmax_t)(end - start) / 1024);
+
+ value = cpu_to_fdt64(start);
+
+ result = dtb_set_property(dtb, dtb_size, n_chosen, p_initrd_start,
+ &value, sizeof(value));
+
+ if (result)
+ return result;
+
+ value = cpu_to_fdt64(end);
+
+ result = dtb_set_property(dtb, dtb_size, n_chosen, p_initrd_end,
+ &value, sizeof(value));
+
+ if (result) {
+ dtb_delete_property(*dtb, n_chosen, p_initrd_start);
+ return result;
+ }
+
+ return 0;
+}
+
+int dtb_set_bootargs(char **dtb, off_t *dtb_size, const char *command_line)
+{
+ return dtb_set_property(dtb, dtb_size, n_chosen, p_bootargs,
+ command_line, strlen(command_line) + 1);
+}
+
+int dtb_set_property(char **dtb, off_t *dtb_size, const char *node,
+ const char *prop, const void *value, int value_len)
+{
+ int result;
+ int nodeoffset;
+ void *new_dtb;
+ int new_size;
+
+ value_len = FDT_TAGALIGN(value_len);
+
+ new_size = FDT_TAGALIGN(*dtb_size + fdt_node_len(node)
+ + fdt_prop_len(prop, value_len));
+
+ new_dtb = malloc(new_size);
+
+ if (!new_dtb) {
+ dbgprintf("%s: malloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ result = fdt_open_into(*dtb, new_dtb, new_size);
+
+ if (result) {
+ dbgprintf("%s: fdt_open_into failed: %s\n", __func__,
+ fdt_strerror(result));
+ goto on_error;
+ }
+
+ nodeoffset = fdt_path_offset(new_dtb, node);
+
+ if (nodeoffset == -FDT_ERR_NOTFOUND) {
+ result = fdt_add_subnode(new_dtb, nodeoffset, node);
+
+ if (result) {
+ dbgprintf("%s: fdt_add_subnode failed: %s\n", __func__,
+ fdt_strerror(result));
+ goto on_error;
+ }
+ } else if (nodeoffset < 0) {
+ dbgprintf("%s: fdt_path_offset failed: %s\n", __func__,
+ fdt_strerror(nodeoffset));
+ goto on_error;
+ }
+
+ result = fdt_setprop(new_dtb, nodeoffset, prop, value, value_len);
+
+ if (result) {
+ dbgprintf("%s: fdt_setprop failed: %s\n", __func__,
+ fdt_strerror(result));
+ goto on_error;
+ }
+
+ /*
+ * Can't call free on dtb since dtb may have been mmaped by
+ * slurp_file().
+ */
+
+ result = fdt_pack(new_dtb);
+
+ if (result)
+ dbgprintf("%s: Unable to pack device tree: %s\n", __func__,
+ fdt_strerror(result));
+
+ *dtb = new_dtb;
+ *dtb_size = fdt_totalsize(*dtb);
+
+ return 0;
+
+on_error:
+ free(new_dtb);
+ return result;
+}
+
+int dtb_delete_property(char *dtb, const char *node, const char *prop)
+{
+ int result;
+ int nodeoffset = fdt_path_offset(dtb, node);
+
+ if (nodeoffset < 0) {
+ dbgprintf("%s: fdt_path_offset failed: %s\n", __func__,
+ fdt_strerror(nodeoffset));
+ return nodeoffset;
+ }
+
+ result = fdt_delprop(dtb, nodeoffset, prop);
+
+ if (result)
+ dbgprintf("%s: fdt_delprop failed: %s\n", __func__,
+ fdt_strerror(nodeoffset));
+
+ return result;
+}
diff --git a/kexec/dt-ops.h b/kexec/dt-ops.h
new file mode 100644
index 0000000..e70d15d
--- /dev/null
+++ b/kexec/dt-ops.h
@@ -0,0 +1,13 @@
+#if !defined(KEXEC_DT_OPS_H)
+#define KEXEC_DT_OPS_H
+
+#include <sys/types.h>
+
+int dtb_set_initrd(char **dtb, off_t *dtb_size, off_t start, off_t end);
+int dtb_set_bootargs(char **dtb, off_t *dtb_size, const char *command_line);
+int dtb_set_property(char **dtb, off_t *dtb_size, const char *node,
+ const char *prop, const void *value, int value_len);
+
+int dtb_delete_property(char *dtb, const char *node, const char *prop);
+
+#endif
--
2.7.4
_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v5 0/3] arm64 kexec-tools patches
@ 2016-09-01 21:51 ` Geoff Levand
0 siblings, 0 replies; 14+ messages in thread
From: Geoff Levand @ 2016-09-01 21:51 UTC (permalink / raw)
To: Simon Horman
Cc: Pratyush Anand, AKASHI Takahiro, Mark Rutland, kexec, linux-arm-kernel
This series adds the core support for kexec re-boot on ARM64.
Linux kernel support for ARM64 kexec reboot has been merged in v4.8-rc1 with the
expectation that it will be included in the v4.8 stable kernel release.
For ARM64 kdump support see Takahiro's latest kdump patches [1].
[1] http://lists.infradead.org/pipermail/kexec
Changes for v5 (Sep 1, 2016, 2m):
o Add common routine arm64_locate_kernel_segment().
Changes for v4 (Aug 18, 2016, 1m):
o Fix some error return values and error messages.
o Add enum arm64_header_page_size.
o Rename page_offset to vp_offset.
Changes for v3 (Aug 10, 2016, 1m):
o Rebase to 2.0.13.
o Add support for flag bits 1-3 to arm64 image-header.h.
o Fix OPT_ARCH_MAX value.
o Use fdt_pack() in dtb_set_property().
o Remove patch ("kexec: (bugfix) calc correct end address of memory ranges in device tree").
Changes for v2 (July 26, 2016, 0m):
o Inline some small routines.
o Reformat some dbgprintf messages.
o Remove debug_brk from entry.S
o Change arm64_image_header.flags to uint64_t.
o Look in iomem then dt for mem info.
o Remove check_cpu_nodes.
o Remove purgatory printing.
First submission v1 (July 20, 2016).
-Geoff
The following changes since commit 67488beb0a6ee8ad2c0b05f721a9e00041fab93a:
kexec-tools 2.0.13 (2016-08-08 12:26:44 +0200)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/geoff/kexec-tools.git for-merge-arm64-v5
for you to fetch changes up to 26a67f5fa8ecf7d76c908a5e0d8ee8bf4f9cb578:
arm64: Add support for binary image files (2016-09-01 14:33:49 -0700)
----------------------------------------------------------------
Geoff Levand (2):
kexec: Add common device tree routines
arm64: Add arm64 kexec support
Pratyush Anand (1):
arm64: Add support for binary image files
configure.ac | 3 +
kexec/Makefile | 5 +
kexec/arch/arm64/Makefile | 40 ++
kexec/arch/arm64/crashdump-arm64.c | 21 +
kexec/arch/arm64/crashdump-arm64.h | 12 +
kexec/arch/arm64/image-header.h | 146 +++++++
kexec/arch/arm64/include/arch/options.h | 39 ++
kexec/arch/arm64/kexec-arm64.c | 717 ++++++++++++++++++++++++++++++++
kexec/arch/arm64/kexec-arm64.h | 71 ++++
kexec/arch/arm64/kexec-elf-arm64.c | 146 +++++++
kexec/arch/arm64/kexec-image-arm64.c | 80 ++++
kexec/dt-ops.c | 145 +++++++
kexec/dt-ops.h | 13 +
kexec/kexec-syscall.h | 8 +-
purgatory/Makefile | 1 +
purgatory/arch/arm64/Makefile | 18 +
purgatory/arch/arm64/entry.S | 51 +++
purgatory/arch/arm64/purgatory-arm64.c | 19 +
18 files changed, 1533 insertions(+), 2 deletions(-)
create mode 100644 kexec/arch/arm64/Makefile
create mode 100644 kexec/arch/arm64/crashdump-arm64.c
create mode 100644 kexec/arch/arm64/crashdump-arm64.h
create mode 100644 kexec/arch/arm64/image-header.h
create mode 100644 kexec/arch/arm64/include/arch/options.h
create mode 100644 kexec/arch/arm64/kexec-arm64.c
create mode 100644 kexec/arch/arm64/kexec-arm64.h
create mode 100644 kexec/arch/arm64/kexec-elf-arm64.c
create mode 100644 kexec/arch/arm64/kexec-image-arm64.c
create mode 100644 kexec/dt-ops.c
create mode 100644 kexec/dt-ops.h
create mode 100644 purgatory/arch/arm64/Makefile
create mode 100644 purgatory/arch/arm64/entry.S
create mode 100644 purgatory/arch/arm64/purgatory-arm64.c
--
2.7.4
_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v5 3/3] arm64: Add support for binary image files
@ 2016-09-01 21:51 ` Geoff Levand
0 siblings, 0 replies; 14+ messages in thread
From: Geoff Levand @ 2016-09-01 21:51 UTC (permalink / raw)
To: Simon Horman
Cc: Pratyush Anand, AKASHI Takahiro, Mark Rutland, kexec, linux-arm-kernel
From: Pratyush Anand <panand@redhat.com>
Signed-off-by: Pratyush Anand <panand@redhat.com>
[Reworked and cleaned up]
Signed-off-by: Geoff Levand <geoff@infradead.org>
---
kexec/arch/arm64/kexec-image-arm64.c | 49 ++++++++++++++++++++++++++++++++----
1 file changed, 44 insertions(+), 5 deletions(-)
diff --git a/kexec/arch/arm64/kexec-image-arm64.c b/kexec/arch/arm64/kexec-image-arm64.c
index 42d2ea7..960ed96 100644
--- a/kexec/arch/arm64/kexec-image-arm64.c
+++ b/kexec/arch/arm64/kexec-image-arm64.c
@@ -3,7 +3,9 @@
*/
#define _GNU_SOURCE
+
#include "kexec-arm64.h"
+#include <limits.h>
int image_arm64_probe(const char *kernel_buf, off_t kernel_size)
{
@@ -21,14 +23,53 @@ int image_arm64_probe(const char *kernel_buf, off_t kernel_size)
return -1;
}
- fprintf(stderr, "kexec: ARM64 binary image files are currently NOT SUPPORTED.\n");
- return -1;
+ return 0;
}
int image_arm64_load(int argc, char **argv, const char *kernel_buf,
off_t kernel_size, struct kexec_info *info)
{
- return -1;
+ const struct arm64_image_header *header;
+ unsigned long kernel_segment;
+ int result;
+
+ header = (const struct arm64_image_header *)(kernel_buf);
+
+ if (arm64_process_image_header(header))
+ return -1;
+
+ kernel_segment = arm64_locate_kernel_segment(info);
+
+ if (kernel_segment == ULONG_MAX) {
+ dbgprintf("%s: Kernel segment is not allocated\n", __func__);
+ result = -EFAILED;
+ goto exit;
+ }
+
+ dbgprintf("%s: kernel_segment: %016lx\n", __func__, kernel_segment);
+ dbgprintf("%s: text_offset: %016lx\n", __func__,
+ arm64_mem.text_offset);
+ dbgprintf("%s: image_size: %016lx\n", __func__,
+ arm64_mem.image_size);
+ dbgprintf("%s: phys_offset: %016lx\n", __func__,
+ arm64_mem.phys_offset);
+ dbgprintf("%s: vp_offset: %016lx\n", __func__,
+ arm64_mem.vp_offset);
+ dbgprintf("%s: PE format: %s\n", __func__,
+ (arm64_header_check_pe_sig(header) ? "yes" : "no"));
+
+ /* load the kernel */
+ add_segment_phys_virt(info, kernel_buf, kernel_size,
+ kernel_segment + arm64_mem.text_offset,
+ arm64_mem.image_size, 0);
+
+ result = arm64_load_other_segments(info, kernel_segment
+ + arm64_mem.text_offset);
+
+exit:
+ if (result)
+ fprintf(stderr, "kexec: load failed.\n");
+ return result;
}
void image_arm64_usage(void)
@@ -36,6 +77,4 @@ void image_arm64_usage(void)
printf(
" An ARM64 binary image, compressed or not, big or little endian.\n"
" Typically an Image, Image.gz or Image.lzma file.\n\n");
- printf(
-" ARM64 binary image files are currently NOT SUPPORTED.\n\n");
}
--
2.7.4
_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v5 2/3] arm64: Add arm64 kexec support
@ 2016-09-01 21:51 ` Geoff Levand
0 siblings, 0 replies; 14+ messages in thread
From: Geoff Levand @ 2016-09-01 21:51 UTC (permalink / raw)
To: Simon Horman
Cc: Pratyush Anand, AKASHI Takahiro, Mark Rutland, kexec, linux-arm-kernel
Add kexec reboot support for ARM64 platforms.
Signed-off-by: Geoff Levand <geoff@infradead.org>
---
configure.ac | 3 +
kexec/Makefile | 1 +
kexec/arch/arm64/Makefile | 40 ++
kexec/arch/arm64/crashdump-arm64.c | 21 +
kexec/arch/arm64/crashdump-arm64.h | 12 +
| 146 +++++++
kexec/arch/arm64/include/arch/options.h | 39 ++
kexec/arch/arm64/kexec-arm64.c | 717 ++++++++++++++++++++++++++++++++
kexec/arch/arm64/kexec-arm64.h | 71 ++++
kexec/arch/arm64/kexec-elf-arm64.c | 146 +++++++
kexec/arch/arm64/kexec-image-arm64.c | 41 ++
kexec/kexec-syscall.h | 8 +-
purgatory/Makefile | 1 +
purgatory/arch/arm64/Makefile | 18 +
purgatory/arch/arm64/entry.S | 51 +++
purgatory/arch/arm64/purgatory-arm64.c | 19 +
16 files changed, 1332 insertions(+), 2 deletions(-)
create mode 100644 kexec/arch/arm64/Makefile
create mode 100644 kexec/arch/arm64/crashdump-arm64.c
create mode 100644 kexec/arch/arm64/crashdump-arm64.h
create mode 100644 kexec/arch/arm64/image-header.h
create mode 100644 kexec/arch/arm64/include/arch/options.h
create mode 100644 kexec/arch/arm64/kexec-arm64.c
create mode 100644 kexec/arch/arm64/kexec-arm64.h
create mode 100644 kexec/arch/arm64/kexec-elf-arm64.c
create mode 100644 kexec/arch/arm64/kexec-image-arm64.c
create mode 100644 purgatory/arch/arm64/Makefile
create mode 100644 purgatory/arch/arm64/entry.S
create mode 100644 purgatory/arch/arm64/purgatory-arm64.c
diff --git a/configure.ac b/configure.ac
index 736da6e..ba89075 100644
--- a/configure.ac
+++ b/configure.ac
@@ -34,6 +34,9 @@ case $target_cpu in
ARCH="ppc64"
SUBARCH="LE"
;;
+ aarch64* )
+ ARCH="arm64"
+ ;;
arm* )
ARCH="arm"
;;
diff --git a/kexec/Makefile b/kexec/Makefile
index cc3f08b..39f365f 100644
--- a/kexec/Makefile
+++ b/kexec/Makefile
@@ -79,6 +79,7 @@ KEXEC_SRCS += $($(ARCH)_DT_OPS)
include $(srcdir)/kexec/arch/alpha/Makefile
include $(srcdir)/kexec/arch/arm/Makefile
+include $(srcdir)/kexec/arch/arm64/Makefile
include $(srcdir)/kexec/arch/i386/Makefile
include $(srcdir)/kexec/arch/ia64/Makefile
include $(srcdir)/kexec/arch/m68k/Makefile
diff --git a/kexec/arch/arm64/Makefile b/kexec/arch/arm64/Makefile
new file mode 100644
index 0000000..37414dc
--- /dev/null
+++ b/kexec/arch/arm64/Makefile
@@ -0,0 +1,40 @@
+
+arm64_FS2DT += kexec/fs2dt.c
+arm64_FS2DT_INCLUDE += -include $(srcdir)/kexec/arch/arm64/kexec-arm64.h \
+ -include $(srcdir)/kexec/arch/arm64/crashdump-arm64.h
+
+arm64_DT_OPS += kexec/dt-ops.c
+
+arm64_CPPFLAGS += -I $(srcdir)/kexec/
+
+arm64_KEXEC_SRCS += \
+ kexec/arch/arm64/kexec-arm64.c \
+ kexec/arch/arm64/kexec-image-arm64.c \
+ kexec/arch/arm64/kexec-elf-arm64.c \
+ kexec/arch/arm64/crashdump-arm64.c
+
+arm64_ARCH_REUSE_INITRD =
+arm64_ADD_SEGMENT =
+arm64_VIRT_TO_PHYS =
+arm64_PHYS_TO_VIRT =
+
+dist += $(arm64_KEXEC_SRCS) \
+ kexec/arch/arm64/Makefile \
+ kexec/arch/arm64/kexec-arm64.h \
+ kexec/arch/arm64/crashdump-arm64.h
+
+ifdef HAVE_LIBFDT
+
+LIBS += -lfdt
+
+else
+
+include $(srcdir)/kexec/libfdt/Makefile.libfdt
+
+libfdt_SRCS += $(LIBFDT_SRCS:%=kexec/libfdt/%)
+
+arm64_CPPFLAGS += -I$(srcdir)/kexec/libfdt
+
+arm64_KEXEC_SRCS += $(libfdt_SRCS)
+
+endif
diff --git a/kexec/arch/arm64/crashdump-arm64.c b/kexec/arch/arm64/crashdump-arm64.c
new file mode 100644
index 0000000..d2272c8
--- /dev/null
+++ b/kexec/arch/arm64/crashdump-arm64.c
@@ -0,0 +1,21 @@
+/*
+ * ARM64 crashdump.
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <linux/elf.h>
+
+#include "kexec.h"
+#include "crashdump.h"
+#include "crashdump-arm64.h"
+#include "kexec-arm64.h"
+#include "kexec-elf.h"
+
+struct memory_ranges usablemem_rgns = {};
+
+int is_crashkernel_mem_reserved(void)
+{
+ return 0;
+}
diff --git a/kexec/arch/arm64/crashdump-arm64.h b/kexec/arch/arm64/crashdump-arm64.h
new file mode 100644
index 0000000..f33c7a2
--- /dev/null
+++ b/kexec/arch/arm64/crashdump-arm64.h
@@ -0,0 +1,12 @@
+/*
+ * ARM64 crashdump.
+ */
+
+#if !defined(CRASHDUMP_ARM64_H)
+#define CRASHDUMP_ARM64_H
+
+#include "kexec.h"
+
+extern struct memory_ranges usablemem_rgns;
+
+#endif
--git a/kexec/arch/arm64/image-header.h b/kexec/arch/arm64/image-header.h
new file mode 100644
index 0000000..158d411
--- /dev/null
+++ b/kexec/arch/arm64/image-header.h
@@ -0,0 +1,146 @@
+/*
+ * ARM64 binary image header.
+ */
+
+#if !defined(__ARM64_IMAGE_HEADER_H)
+#define __ARM64_IMAGE_HEADER_H
+
+#include <endian.h>
+#include <stdint.h>
+
+/**
+ * struct arm64_image_header - arm64 kernel image header.
+ *
+ * @pe_sig: Optional PE format 'MZ' signature.
+ * @branch_code: Reserved for instructions to branch to stext.
+ * @text_offset: The image load offset in LSB byte order.
+ * @image_size: An estimated size of the memory image size in LSB byte order.
+ * @flags: Bit flags in LSB byte order:
+ * Bit 0: Image byte order: 1=MSB.
+ * Bit 1-2: Kernel page size: 1=4K, 2=16K, 3=64K.
+ * Bit 3: Image placement: 0=low.
+ * @reserved_1: Reserved.
+ * @magic: Magic number, "ARM\x64".
+ * @pe_header: Optional offset to a PE format header.
+ **/
+
+struct arm64_image_header {
+ uint8_t pe_sig[2];
+ uint16_t branch_code[3];
+ uint64_t text_offset;
+ uint64_t image_size;
+ uint64_t flags;
+ uint64_t reserved_1[3];
+ uint8_t magic[4];
+ uint32_t pe_header;
+};
+
+static const uint8_t arm64_image_magic[4] = {'A', 'R', 'M', 0x64U};
+static const uint8_t arm64_image_pe_sig[2] = {'M', 'Z'};
+static const uint64_t arm64_image_flag_be = (1UL << 0);
+static const uint64_t arm64_image_flag_page_size = (3UL << 1);
+static const uint64_t arm64_image_flag_placement = (1UL << 3);
+
+/**
+ * enum arm64_header_page_size
+ */
+
+enum arm64_header_page_size {
+ arm64_header_page_size_invalid = 0,
+ arm64_header_page_size_4k,
+ arm64_header_page_size_16k,
+ arm64_header_page_size_64k
+};
+
+/**
+ * arm64_header_check_magic - Helper to check the arm64 image header.
+ *
+ * Returns non-zero if header is OK.
+ */
+
+static inline int arm64_header_check_magic(const struct arm64_image_header *h)
+{
+ if (!h)
+ return 0;
+
+ return (h->magic[0] == arm64_image_magic[0]
+ && h->magic[1] == arm64_image_magic[1]
+ && h->magic[2] == arm64_image_magic[2]
+ && h->magic[3] == arm64_image_magic[3]);
+}
+
+/**
+ * arm64_header_check_pe_sig - Helper to check the arm64 image header.
+ *
+ * Returns non-zero if 'MZ' signature is found.
+ */
+
+static inline int arm64_header_check_pe_sig(const struct arm64_image_header *h)
+{
+ if (!h)
+ return 0;
+
+ return (h->pe_sig[0] == arm64_image_pe_sig[0]
+ && h->pe_sig[1] == arm64_image_pe_sig[1]);
+}
+
+/**
+ * arm64_header_check_msb - Helper to check the arm64 image header.
+ *
+ * Returns non-zero if the image was built as big endian.
+ */
+
+static inline int arm64_header_check_msb(const struct arm64_image_header *h)
+{
+ if (!h)
+ return 0;
+
+ return (le64toh(h->flags) & arm64_image_flag_be) >> 0;
+}
+
+/**
+ * arm64_header_page_size
+ */
+
+static inline enum arm64_header_page_size arm64_header_page_size(
+ const struct arm64_image_header *h)
+{
+ if (!h)
+ return 0;
+
+ return (le64toh(h->flags) & arm64_image_flag_page_size) >> 1;
+}
+
+/**
+ * arm64_header_placement
+ *
+ * Returns non-zero if the image has no physical placement restrictions.
+ */
+
+static inline int arm64_header_placement(const struct arm64_image_header *h)
+{
+ if (!h)
+ return 0;
+
+ return (le64toh(h->flags) & arm64_image_flag_placement) >> 3;
+}
+
+static inline uint64_t arm64_header_text_offset(
+ const struct arm64_image_header *h)
+{
+ if (!h)
+ return 0;
+
+ return le64toh(h->text_offset);
+}
+
+static inline uint64_t arm64_header_image_size(
+ const struct arm64_image_header *h)
+{
+ if (!h)
+ return 0;
+
+ return le64toh(h->image_size);
+}
+
+#endif
diff --git a/kexec/arch/arm64/include/arch/options.h b/kexec/arch/arm64/include/arch/options.h
new file mode 100644
index 0000000..a17d933
--- /dev/null
+++ b/kexec/arch/arm64/include/arch/options.h
@@ -0,0 +1,39 @@
+#if !defined(KEXEC_ARCH_ARM64_OPTIONS_H)
+#define KEXEC_ARCH_ARM64_OPTIONS_H
+
+#define OPT_APPEND ((OPT_MAX)+0)
+#define OPT_DTB ((OPT_MAX)+1)
+#define OPT_INITRD ((OPT_MAX)+2)
+#define OPT_REUSE_CMDLINE ((OPT_MAX)+3)
+#define OPT_ARCH_MAX ((OPT_MAX)+4)
+
+#define KEXEC_ARCH_OPTIONS \
+ KEXEC_OPTIONS \
+ { "append", 1, NULL, OPT_APPEND }, \
+ { "command-line", 1, NULL, OPT_APPEND }, \
+ { "dtb", 1, NULL, OPT_DTB }, \
+ { "initrd", 1, NULL, OPT_INITRD }, \
+ { "ramdisk", 1, NULL, OPT_INITRD }, \
+ { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE }, \
+
+#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR /* Only accept long arch options. */
+#define KEXEC_ALL_OPTIONS KEXEC_ARCH_OPTIONS
+#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR
+
+static const char arm64_opts_usage[] __attribute__ ((unused)) =
+" --append=STRING Set the kernel command line to STRING.\n"
+" --command-line=STRING Set the kernel command line to STRING.\n"
+" --dtb=FILE Use FILE as the device tree blob.\n"
+" --initrd=FILE Use FILE as the kernel initial ramdisk.\n"
+" --ramdisk=FILE Use FILE as the kernel initial ramdisk.\n"
+" --reuse-cmdline Use kernel command line from running system.\n";
+
+struct arm64_opts {
+ const char *command_line;
+ const char *dtb;
+ const char *initrd;
+};
+
+extern struct arm64_opts arm64_opts;
+
+#endif
diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c
new file mode 100644
index 0000000..7183dac
--- /dev/null
+++ b/kexec/arch/arm64/kexec-arm64.c
@@ -0,0 +1,717 @@
+/*
+ * ARM64 kexec.
+ */
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <libfdt.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <linux/elf-em.h>
+#include <elf.h>
+
+#include "kexec.h"
+#include "kexec-arm64.h"
+#include "crashdump.h"
+#include "crashdump-arm64.h"
+#include "dt-ops.h"
+#include "fs2dt.h"
+#include "kexec-syscall.h"
+#include "arch/options.h"
+
+/* Global varables the core kexec routines expect. */
+
+unsigned char reuse_initrd;
+
+off_t initrd_base;
+off_t initrd_size;
+
+const struct arch_map_entry arches[] = {
+ { "aarch64", KEXEC_ARCH_ARM64 },
+ { "aarch64_be", KEXEC_ARCH_ARM64 },
+ { NULL, 0 },
+};
+
+struct file_type file_type[] = {
+ {"vmlinux", elf_arm64_probe, elf_arm64_load, elf_arm64_usage},
+ {"Image", image_arm64_probe, image_arm64_load, image_arm64_usage},
+};
+
+int file_types = sizeof(file_type) / sizeof(file_type[0]);
+
+/* arm64 global varables. */
+
+struct arm64_opts arm64_opts;
+struct arm64_mem arm64_mem = {
+ .phys_offset = arm64_mem_ngv,
+ .vp_offset = arm64_mem_ngv,
+};
+
+uint64_t get_phys_offset(void)
+{
+ assert(arm64_mem.phys_offset != arm64_mem_ngv);
+ return arm64_mem.phys_offset;
+}
+
+uint64_t get_vp_offset(void)
+{
+ assert(arm64_mem.vp_offset != arm64_mem_ngv);
+ return arm64_mem.vp_offset;
+}
+
+/**
+ * arm64_process_image_header - Process the arm64 image header.
+ *
+ * Make a guess that KERNEL_IMAGE_SIZE will be enough for older kernels.
+ */
+
+int arm64_process_image_header(const struct arm64_image_header *h)
+{
+#if !defined(KERNEL_IMAGE_SIZE)
+# define KERNEL_IMAGE_SIZE MiB(16)
+#endif
+
+ if (!arm64_header_check_magic(h))
+ return -EFAILED;
+
+ if (h->image_size) {
+ arm64_mem.text_offset = arm64_header_text_offset(h);
+ arm64_mem.image_size = arm64_header_image_size(h);
+ } else {
+ /* For 3.16 and older kernels. */
+ arm64_mem.text_offset = 0x80000;
+ arm64_mem.image_size = KERNEL_IMAGE_SIZE;
+ fprintf(stderr,
+ "kexec: %s: Warning: Kernel image size set to %lu MiB.\n"
+ " Please verify compatability with lodaed kernel.\n",
+ __func__, KERNEL_IMAGE_SIZE / 1024UL / 1024UL);
+ }
+
+ return 0;
+}
+
+void arch_usage(void)
+{
+ printf(arm64_opts_usage);
+}
+
+int arch_process_options(int argc, char **argv)
+{
+ static const char short_options[] = KEXEC_OPT_STR "";
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { 0 }
+ };
+ int opt;
+ char *cmdline = NULL;
+ const char *append = NULL;
+
+ for (opt = 0; opt != -1; ) {
+ opt = getopt_long(argc, argv, short_options, options, 0);
+
+ switch (opt) {
+ case OPT_APPEND:
+ append = optarg;
+ break;
+ case OPT_REUSE_CMDLINE:
+ cmdline = get_command_line();
+ break;
+ case OPT_DTB:
+ arm64_opts.dtb = optarg;
+ break;
+ case OPT_INITRD:
+ arm64_opts.initrd = optarg;
+ break;
+ case OPT_PANIC:
+ die("load-panic (-p) not supported");
+ break;
+ default:
+ break; /* Ignore core and unknown options. */
+ }
+ }
+
+ arm64_opts.command_line = concat_cmdline(cmdline, append);
+
+ dbgprintf("%s:%d: command_line: %s\n", __func__, __LINE__,
+ arm64_opts.command_line);
+ dbgprintf("%s:%d: initrd: %s\n", __func__, __LINE__,
+ arm64_opts.initrd);
+ dbgprintf("%s:%d: dtb: %s\n", __func__, __LINE__, arm64_opts.dtb);
+
+ return 0;
+}
+
+/**
+ * struct dtb - Info about a binary device tree.
+ *
+ * @buf: Device tree data.
+ * @size: Device tree data size.
+ * @name: Shorthand name of this dtb for messages.
+ * @path: Filesystem path.
+ */
+
+struct dtb {
+ char *buf;
+ off_t size;
+ const char *name;
+ const char *path;
+};
+
+/**
+ * dump_reservemap - Dump the dtb's reservemap.
+ */
+
+static void dump_reservemap(const struct dtb *dtb)
+{
+ int i;
+
+ for (i = 0; ; i++) {
+ uint64_t address;
+ uint64_t size;
+
+ fdt_get_mem_rsv(dtb->buf, i, &address, &size);
+
+ if (!size)
+ break;
+
+ dbgprintf("%s: %s {%" PRIx64 ", %" PRIx64 "}\n", __func__,
+ dtb->name, address, size);
+ }
+}
+
+/**
+ * set_bootargs - Set the dtb's bootargs.
+ */
+
+static int set_bootargs(struct dtb *dtb, const char *command_line)
+{
+ int result;
+
+ if (!command_line || !command_line[0])
+ return 0;
+
+ result = dtb_set_bootargs(&dtb->buf, &dtb->size, command_line);
+
+ if (result) {
+ fprintf(stderr,
+ "kexec: Set device tree bootargs failed.\n");
+ return -EFAILED;
+ }
+
+ return 0;
+}
+
+/**
+ * read_proc_dtb - Read /proc/device-tree.
+ */
+
+static int read_proc_dtb(struct dtb *dtb)
+{
+ int result;
+ struct stat s;
+ static const char path[] = "/proc/device-tree";
+
+ result = stat(path, &s);
+
+ if (result) {
+ dbgprintf("%s: %s\n", __func__, strerror(errno));
+ return -EFAILED;
+ }
+
+ dtb->path = path;
+ create_flatten_tree((char **)&dtb->buf, &dtb->size, NULL);
+
+ return 0;
+}
+
+/**
+ * read_sys_dtb - Read /sys/firmware/fdt.
+ */
+
+static int read_sys_dtb(struct dtb *dtb)
+{
+ int result;
+ struct stat s;
+ static const char path[] = "/sys/firmware/fdt";
+
+ result = stat(path, &s);
+
+ if (result) {
+ dbgprintf("%s: %s\n", __func__, strerror(errno));
+ return -EFAILED;
+ }
+
+ dtb->path = path;
+ dtb->buf = slurp_file(path, &dtb->size);
+
+ return 0;
+}
+
+/**
+ * read_1st_dtb - Read the 1st stage kernel's dtb.
+ */
+
+static int read_1st_dtb(struct dtb *dtb)
+{
+ int result;
+
+ result = read_sys_dtb(dtb);
+
+ if (!result)
+ goto on_success;
+
+ result = read_proc_dtb(dtb);
+
+ if (!result)
+ goto on_success;
+
+ dbgprintf("%s: not found\n", __func__);
+ return -EFAILED;
+
+on_success:
+ dbgprintf("%s: found %s\n", __func__, dtb->path);
+ return 0;
+}
+
+/**
+ * setup_2nd_dtb - Setup the 2nd stage kernel's dtb.
+ */
+
+static int setup_2nd_dtb(struct dtb *dtb, char *command_line)
+{
+ int result;
+
+ result = fdt_check_header(dtb->buf);
+
+ if (result) {
+ fprintf(stderr, "kexec: Invalid 2nd device tree.\n");
+ return -EFAILED;
+ }
+
+ result = set_bootargs(dtb, command_line);
+
+ dump_reservemap(dtb);
+
+ return result;
+}
+
+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 (hole == ULONG_MAX)
+ dbgprintf("%s: locate_hole failed\n", __func__);
+
+ return hole;
+}
+
+/**
+ * arm64_load_other_segments - Prepare the dtb, initrd and purgatory segments.
+ */
+
+int arm64_load_other_segments(struct kexec_info *info,
+ unsigned long image_base)
+{
+ int result;
+ unsigned long dtb_base;
+ unsigned long hole_min;
+ unsigned long hole_max;
+ char *initrd_buf = NULL;
+ struct dtb dtb;
+ char command_line[COMMAND_LINE_SIZE] = "";
+
+ if (arm64_opts.command_line) {
+ strncpy(command_line, arm64_opts.command_line,
+ sizeof(command_line));
+ command_line[sizeof(command_line) - 1] = 0;
+ }
+
+ if (arm64_opts.dtb) {
+ dtb.name = "dtb_2";
+ dtb.buf = slurp_file(arm64_opts.dtb, &dtb.size);
+ } else {
+ dtb.name = "dtb_1";
+ result = read_1st_dtb(&dtb);
+
+ if (result) {
+ fprintf(stderr,
+ "kexec: Error: No device tree available.\n");
+ return -EFAILED;
+ }
+ }
+
+ result = setup_2nd_dtb(&dtb, command_line);
+
+ if (result)
+ return -EFAILED;
+
+ /* Put the other segments after the image. */
+
+ hole_min = image_base + arm64_mem.image_size;
+ hole_max = ULONG_MAX;
+
+ if (arm64_opts.initrd) {
+ initrd_buf = slurp_file(arm64_opts.initrd, &initrd_size);
+
+ if (!initrd_buf)
+ fprintf(stderr, "kexec: Empty ramdisk file.\n");
+ else {
+ /*
+ * Put the initrd after the kernel. As specified in
+ * booting.txt, align to 1 GiB.
+ */
+
+ initrd_base = add_buffer_phys_virt(info, initrd_buf,
+ initrd_size, initrd_size, GiB(1),
+ hole_min, hole_max, 1, 0);
+
+ /* initrd_base is valid if we got here. */
+
+ dbgprintf("initrd: base %lx, size %lxh (%ld)\n",
+ initrd_base, initrd_size, initrd_size);
+
+ /* Check size limit as specified in booting.txt. */
+
+ if (initrd_base - image_base + initrd_size > GiB(32)) {
+ fprintf(stderr, "kexec: Error: image + initrd too big.\n");
+ return -EFAILED;
+ }
+
+ result = dtb_set_initrd((char **)&dtb.buf,
+ &dtb.size, initrd_base,
+ initrd_base + initrd_size);
+
+ if (result)
+ return -EFAILED;
+ }
+ }
+
+ /* Check size limit as specified in booting.txt. */
+
+ if (dtb.size > MiB(2)) {
+ fprintf(stderr, "kexec: Error: dtb too big.\n");
+ return -EFAILED;
+ }
+
+ dtb_base = add_buffer_phys_virt(info, dtb.buf, dtb.size, dtb.size,
+ 0, hole_min, hole_max, 1, 0);
+
+ /* dtb_base is valid if we got here. */
+
+ dbgprintf("dtb: base %lx, size %lxh (%ld)\n", dtb_base, dtb.size,
+ dtb.size);
+
+ elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
+ hole_min, hole_max, 1, 0);
+
+ info->entry = (void *)elf_rel_get_addr(&info->rhdr, "purgatory_start");
+
+ elf_rel_set_symbol(&info->rhdr, "arm64_kernel_entry", &image_base,
+ sizeof(image_base));
+
+ elf_rel_set_symbol(&info->rhdr, "arm64_dtb_addr", &dtb_base,
+ sizeof(dtb_base));
+
+ return 0;
+}
+
+/**
+ * virt_to_phys - For processing elf file values.
+ */
+
+unsigned long virt_to_phys(unsigned long v)
+{
+ unsigned long p;
+
+ p = v - get_vp_offset() + get_phys_offset();
+
+ return p;
+}
+
+/**
+ * phys_to_virt - For crashdump setup.
+ */
+
+unsigned long phys_to_virt(struct crash_elf_info *elf_info,
+ unsigned long long p)
+{
+ unsigned long v;
+
+ v = p - get_phys_offset() + elf_info->page_offset;
+
+ return v;
+}
+
+/**
+ * add_segment - Use virt_to_phys when loading elf files.
+ */
+
+void add_segment(struct kexec_info *info, const void *buf, size_t bufsz,
+ unsigned long base, size_t memsz)
+{
+ add_segment_phys_virt(info, buf, bufsz, base, memsz, 1);
+}
+
+/**
+ * get_memory_ranges_iomem_cb - Helper for get_memory_ranges_iomem.
+ */
+
+static int get_memory_ranges_iomem_cb(void *data, int nr, char *str,
+ unsigned long long base, unsigned long long length)
+{
+ struct memory_range *r;
+
+ if (nr >= KEXEC_SEGMENT_MAX)
+ return -1;
+
+ r = (struct memory_range *)data + nr;
+ r->type = RANGE_RAM;
+ r->start = base;
+ r->end = base + length - 1;
+
+ set_phys_offset(r->start);
+
+ dbgprintf("%s: %016llx - %016llx : %s", __func__, r->start,
+ r->end, str);
+
+ return 0;
+}
+
+/**
+ * get_memory_ranges_iomem - Try to get the memory ranges from /proc/iomem.
+ */
+
+static int get_memory_ranges_iomem(struct memory_range *array,
+ unsigned int *count)
+{
+ *count = kexec_iomem_for_each_line("System RAM\n",
+ get_memory_ranges_iomem_cb, array);
+
+ if (!*count) {
+ dbgprintf("%s: failed: No RAM found.\n", __func__);
+ return -EFAILED;
+ }
+
+ return 0;
+}
+
+/**
+ * get_memory_ranges_dt - Try to get the memory ranges from the 1st stage dtb.
+ */
+
+static int get_memory_ranges_dt(struct memory_range *array, unsigned int *count)
+{
+ struct region {uint64_t base; uint64_t size;};
+ struct dtb dtb = {.name = "range_dtb"};
+ int offset;
+ int result;
+
+ *count = 0;
+
+ result = read_1st_dtb(&dtb);
+
+ if (result) {
+ goto on_error;
+ }
+
+ result = fdt_check_header(dtb.buf);
+
+ if (result) {
+ dbgprintf("%s:%d: %s: fdt_check_header failed:%s\n", __func__,
+ __LINE__, dtb.path, fdt_strerror(result));
+ goto on_error;
+ }
+
+ for (offset = 0; ; ) {
+ const struct region *region;
+ const struct region *end;
+ int len;
+
+ offset = fdt_subnode_offset(dtb.buf, offset, "memory");
+
+ if (offset == -FDT_ERR_NOTFOUND)
+ break;
+
+ if (offset <= 0) {
+ dbgprintf("%s:%d: fdt_subnode_offset failed: %d %s\n",
+ __func__, __LINE__, offset,
+ fdt_strerror(offset));
+ goto on_error;
+ }
+
+ dbgprintf("%s:%d: node_%d %s\n", __func__, __LINE__, offset,
+ fdt_get_name(dtb.buf, offset, NULL));
+
+ region = fdt_getprop(dtb.buf, offset, "reg", &len);
+
+ if (region <= 0) {
+ dbgprintf("%s:%d: fdt_getprop failed: %d %s\n",
+ __func__, __LINE__, offset,
+ fdt_strerror(offset));
+ goto on_error;
+ }
+
+ for (end = region + len / sizeof(*region);
+ region < end && *count < KEXEC_SEGMENT_MAX;
+ region++) {
+ struct memory_range r;
+
+ r.type = RANGE_RAM;
+ r.start = fdt64_to_cpu(region->base);
+ r.end = r.start + fdt64_to_cpu(region->size) - 1;
+
+ if (!region->size) {
+ dbgprintf("%s:%d: SKIP: %016llx - %016llx\n",
+ __func__, __LINE__, r.start, r.end);
+ continue;
+ }
+
+ dbgprintf("%s:%d: RAM: %016llx - %016llx\n", __func__,
+ __LINE__, r.start, r.end);
+
+ array[(*count)++] = r;
+
+ set_phys_offset(r.start);
+ }
+ }
+
+ if (!*count) {
+ dbgprintf("%s:%d: %s: No RAM found.\n", __func__, __LINE__,
+ dtb.path);
+ goto on_error;
+ }
+
+ dbgprintf("%s:%d: %s: Success\n", __func__, __LINE__, dtb.path);
+ result = 0;
+ goto on_exit;
+
+on_error:
+ fprintf(stderr, "%s:%d: %s: Unusable device-tree file\n", __func__,
+ __LINE__, dtb.path);
+ result = -EFAILED;
+
+on_exit:
+ free(dtb.buf);
+ return result;
+}
+
+/**
+ * get_memory_ranges - Try to get the memory ranges some how.
+ */
+
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags)
+{
+ static struct memory_range array[KEXEC_SEGMENT_MAX];
+ unsigned int count;
+ int result;
+
+ result = get_memory_ranges_iomem(array, &count);
+
+ if (result)
+ result = get_memory_ranges_dt(array, &count);
+
+ *range = result ? NULL : array;
+ *ranges = result ? 0 : count;
+
+ return result;
+}
+
+int arch_compat_trampoline(struct kexec_info *info)
+{
+ return 0;
+}
+
+int machine_verify_elf_rel(struct mem_ehdr *ehdr)
+{
+ return (ehdr->e_machine == EM_AARCH64);
+}
+
+void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
+ unsigned long r_type, void *ptr, unsigned long address,
+ unsigned long value)
+{
+#if !defined(R_AARCH64_ABS64)
+# define R_AARCH64_ABS64 257
+#endif
+
+#if !defined(R_AARCH64_LD_PREL_LO19)
+# define R_AARCH64_LD_PREL_LO19 273
+#endif
+
+#if !defined(R_AARCH64_ADR_PREL_LO21)
+# define R_AARCH64_ADR_PREL_LO21 274
+#endif
+
+#if !defined(R_AARCH64_JUMP26)
+# define R_AARCH64_JUMP26 282
+#endif
+
+#if !defined(R_AARCH64_CALL26)
+# define R_AARCH64_CALL26 283
+#endif
+
+ uint64_t *loc64;
+ uint32_t *loc32;
+ uint64_t *location = (uint64_t *)ptr;
+ uint64_t data = *location;
+ const char *type = NULL;
+
+ switch(r_type) {
+ case R_AARCH64_ABS64:
+ type = "ABS64";
+ loc64 = ptr;
+ *loc64 = cpu_to_elf64(ehdr, elf64_to_cpu(ehdr, *loc64) + value);
+ break;
+ case R_AARCH64_LD_PREL_LO19:
+ type = "LD_PREL_LO19";
+ loc32 = ptr;
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+ + (((value - address) << 3) & 0xffffe0));
+ break;
+ case R_AARCH64_ADR_PREL_LO21:
+ if (value & 3)
+ die("%s: ERROR Unaligned value: %lx\n", __func__,
+ value);
+ type = "ADR_PREL_LO21";
+ loc32 = ptr;
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+ + (((value - address) << 3) & 0xffffe0));
+ break;
+ case R_AARCH64_JUMP26:
+ type = "JUMP26";
+ loc32 = ptr;
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+ + (((value - address) >> 2) & 0x3ffffff));
+ break;
+ case R_AARCH64_CALL26:
+ type = "CALL26";
+ loc32 = ptr;
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+ + (((value - address) >> 2) & 0x3ffffff));
+ break;
+ default:
+ die("%s: ERROR Unknown type: %lu\n", __func__, r_type);
+ break;
+ }
+
+ dbgprintf("%s: %s %016lx->%016lx\n", __func__, type, data, *location);
+}
+
+void arch_reuse_initrd(void)
+{
+ reuse_initrd = 1;
+}
+
+void arch_update_purgatory(struct kexec_info *UNUSED(info))
+{
+}
diff --git a/kexec/arch/arm64/kexec-arm64.h b/kexec/arch/arm64/kexec-arm64.h
new file mode 100644
index 0000000..bac62f8
--- /dev/null
+++ b/kexec/arch/arm64/kexec-arm64.h
@@ -0,0 +1,71 @@
+/*
+ * ARM64 kexec.
+ */
+
+#if !defined(KEXEC_ARM64_H)
+#define KEXEC_ARM64_H
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "image-header.h"
+#include "kexec.h"
+
+#define KEXEC_SEGMENT_MAX 16
+
+#define BOOT_BLOCK_VERSION 17
+#define BOOT_BLOCK_LAST_COMP_VERSION 16
+#define COMMAND_LINE_SIZE 512
+
+#define KiB(x) ((x) * 1024UL)
+#define MiB(x) (KiB(x) * 1024UL)
+#define GiB(x) (MiB(x) * 1024UL)
+
+int elf_arm64_probe(const char *kernel_buf, off_t kernel_size);
+int elf_arm64_load(int argc, char **argv, const char *kernel_buf,
+ off_t kernel_size, struct kexec_info *info);
+void elf_arm64_usage(void);
+
+int image_arm64_probe(const char *kernel_buf, off_t kernel_size);
+int image_arm64_load(int argc, char **argv, const char *kernel_buf,
+ off_t kernel_size, struct kexec_info *info);
+void image_arm64_usage(void);
+
+off_t initrd_base;
+off_t initrd_size;
+
+/**
+ * struct arm64_mem - Memory layout info.
+ */
+
+struct arm64_mem {
+ uint64_t phys_offset;
+ uint64_t text_offset;
+ uint64_t image_size;
+ uint64_t vp_offset;
+};
+
+#define arm64_mem_ngv UINT64_MAX
+struct arm64_mem arm64_mem;
+
+uint64_t get_phys_offset(void);
+uint64_t get_vp_offset(void);
+
+static inline void reset_vp_offset(void)
+{
+ arm64_mem.vp_offset = arm64_mem_ngv;
+}
+
+static inline void set_phys_offset(uint64_t v)
+{
+ if (arm64_mem.phys_offset == arm64_mem_ngv
+ || v < arm64_mem.phys_offset)
+ arm64_mem.phys_offset = v;
+}
+
+int arm64_process_image_header(const struct arm64_image_header *h);
+unsigned long arm64_locate_kernel_segment(struct kexec_info *info);
+int arm64_load_other_segments(struct kexec_info *info,
+ unsigned long image_base);
+
+#endif
diff --git a/kexec/arch/arm64/kexec-elf-arm64.c b/kexec/arch/arm64/kexec-elf-arm64.c
new file mode 100644
index 0000000..daf8bf0
--- /dev/null
+++ b/kexec/arch/arm64/kexec-elf-arm64.c
@@ -0,0 +1,146 @@
+/*
+ * ARM64 kexec elf support.
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <linux/elf.h>
+
+#include "kexec-arm64.h"
+#include "kexec-elf.h"
+#include "kexec-syscall.h"
+
+int elf_arm64_probe(const char *kernel_buf, off_t kernel_size)
+{
+ struct mem_ehdr ehdr;
+ int result;
+
+ result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0);
+
+ if (result < 0) {
+ dbgprintf("%s: Not an ELF executable.\n", __func__);
+ goto on_exit;
+ }
+
+ if (ehdr.e_machine != EM_AARCH64) {
+ dbgprintf("%s: Not an AARCH64 ELF executable.\n", __func__);
+ result = -1;
+ goto on_exit;
+ }
+
+ result = 0;
+on_exit:
+ free_elf_info(&ehdr);
+ return result;
+}
+
+int elf_arm64_load(int argc, char **argv, const char *kernel_buf,
+ off_t kernel_size, struct kexec_info *info)
+{
+ const struct arm64_image_header *header = NULL;
+ unsigned long kernel_segment;
+ struct mem_ehdr ehdr;
+ int result;
+ int i;
+
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ fprintf(stderr, "kexec: kdump not yet supported on arm64\n");
+ return -EFAILED;
+ }
+
+ result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0);
+
+ if (result < 0) {
+ dbgprintf("%s: build_elf_exec_info failed\n", __func__);
+ goto exit;
+ }
+
+ /* Find and process the arm64 image header. */
+
+ for (i = 0; i < ehdr.e_phnum; i++) {
+ struct mem_phdr *phdr = &ehdr.e_phdr[i];
+ unsigned long header_offset;
+
+ if (phdr->p_type != PT_LOAD)
+ continue;
+
+ /*
+ * When CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET=y the image header
+ * could be offset in the elf segment. The linker script sets
+ * ehdr.e_entry to the start of text.
+ */
+
+ header_offset = ehdr.e_entry - phdr->p_vaddr;
+
+ header = (const struct arm64_image_header *)(
+ kernel_buf + phdr->p_offset + header_offset);
+
+ if (!arm64_process_image_header(header)) {
+ dbgprintf("%s: e_entry: %016llx\n", __func__,
+ ehdr.e_entry);
+ dbgprintf("%s: p_vaddr: %016llx\n", __func__,
+ phdr->p_vaddr);
+ dbgprintf("%s: header_offset: %016lx\n", __func__,
+ header_offset);
+
+ break;
+ }
+ }
+
+ if (i == ehdr.e_phnum) {
+ dbgprintf("%s: Valid arm64 header not found\n", __func__);
+ result = -EFAILED;
+ goto exit;
+ }
+
+ kernel_segment = arm64_locate_kernel_segment(info);
+
+ if (kernel_segment == ULONG_MAX) {
+ dbgprintf("%s: Kernel segment is not allocated\n", __func__);
+ result = -EFAILED;
+ goto exit;
+ }
+
+ arm64_mem.vp_offset = _ALIGN_DOWN(ehdr.e_entry, MiB(2));
+ 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__,
+ arm64_mem.text_offset);
+ dbgprintf("%s: image_size: %016lx\n", __func__,
+ arm64_mem.image_size);
+ dbgprintf("%s: phys_offset: %016lx\n", __func__,
+ arm64_mem.phys_offset);
+ dbgprintf("%s: vp_offset: %016lx\n", __func__,
+ arm64_mem.vp_offset);
+ dbgprintf("%s: PE format: %s\n", __func__,
+ (arm64_header_check_pe_sig(header) ? "yes" : "no"));
+
+ /* load the kernel */
+ result = elf_exec_load(&ehdr, info);
+
+ if (result) {
+ dbgprintf("%s: elf_exec_load failed\n", __func__);
+ goto exit;
+ }
+
+ result = arm64_load_other_segments(info, kernel_segment
+ + arm64_mem.text_offset);
+
+exit:
+ reset_vp_offset();
+ free_elf_info(&ehdr);
+ if (result)
+ fprintf(stderr, "kexec: Bad elf image file, load failed.\n");
+ return result;
+}
+
+void elf_arm64_usage(void)
+{
+ printf(
+" An ARM64 ELF image, big or little endian.\n"
+" Typically vmlinux or a stripped version of vmlinux.\n\n");
+}
diff --git a/kexec/arch/arm64/kexec-image-arm64.c b/kexec/arch/arm64/kexec-image-arm64.c
new file mode 100644
index 0000000..42d2ea7
--- /dev/null
+++ b/kexec/arch/arm64/kexec-image-arm64.c
@@ -0,0 +1,41 @@
+/*
+ * ARM64 kexec binary image support.
+ */
+
+#define _GNU_SOURCE
+#include "kexec-arm64.h"
+
+int image_arm64_probe(const char *kernel_buf, off_t kernel_size)
+{
+ const struct arm64_image_header *h;
+
+ if (kernel_size < sizeof(struct arm64_image_header)) {
+ dbgprintf("%s: No arm64 image header.\n", __func__);
+ return -1;
+ }
+
+ h = (const struct arm64_image_header *)(kernel_buf);
+
+ if (!arm64_header_check_magic(h)) {
+ dbgprintf("%s: Bad arm64 image header.\n", __func__);
+ return -1;
+ }
+
+ fprintf(stderr, "kexec: ARM64 binary image files are currently NOT SUPPORTED.\n");
+ return -1;
+}
+
+int image_arm64_load(int argc, char **argv, const char *kernel_buf,
+ off_t kernel_size, struct kexec_info *info)
+{
+ return -1;
+}
+
+void image_arm64_usage(void)
+{
+ printf(
+" An ARM64 binary image, compressed or not, big or little endian.\n"
+" Typically an Image, Image.gz or Image.lzma file.\n\n");
+ printf(
+" ARM64 binary image files are currently NOT SUPPORTED.\n\n");
+}
diff --git a/kexec/kexec-syscall.h b/kexec/kexec-syscall.h
index ce2e20b..c0d0bea 100644
--- a/kexec/kexec-syscall.h
+++ b/kexec/kexec-syscall.h
@@ -39,8 +39,8 @@
#ifdef __s390__
#define __NR_kexec_load 277
#endif
-#ifdef __arm__
-#define __NR_kexec_load __NR_SYSCALL_BASE + 347
+#if defined(__arm__) || defined(__arm64__)
+#define __NR_kexec_load __NR_SYSCALL_BASE + 347
#endif
#if defined(__mips__)
#define __NR_kexec_load 4311
@@ -108,6 +108,7 @@ static inline long kexec_file_load(int kernel_fd, int initrd_fd,
#define KEXEC_ARCH_PPC64 (21 << 16)
#define KEXEC_ARCH_IA_64 (50 << 16)
#define KEXEC_ARCH_ARM (40 << 16)
+#define KEXEC_ARCH_ARM64 (183 << 16)
#define KEXEC_ARCH_S390 (22 << 16)
#define KEXEC_ARCH_SH (42 << 16)
#define KEXEC_ARCH_MIPS_LE (10 << 16)
@@ -153,5 +154,8 @@ static inline long kexec_file_load(int kernel_fd, int initrd_fd,
#ifdef __m68k__
#define KEXEC_ARCH_NATIVE KEXEC_ARCH_68K
#endif
+#if defined(__arm64__)
+#define KEXEC_ARCH_NATIVE KEXEC_ARCH_ARM64
+#endif
#endif /* KEXEC_SYSCALL_H */
diff --git a/purgatory/Makefile b/purgatory/Makefile
index 2b5c061..ca0443c 100644
--- a/purgatory/Makefile
+++ b/purgatory/Makefile
@@ -19,6 +19,7 @@ dist += purgatory/Makefile $(PURGATORY_SRCS) \
include $(srcdir)/purgatory/arch/alpha/Makefile
include $(srcdir)/purgatory/arch/arm/Makefile
+include $(srcdir)/purgatory/arch/arm64/Makefile
include $(srcdir)/purgatory/arch/i386/Makefile
include $(srcdir)/purgatory/arch/ia64/Makefile
include $(srcdir)/purgatory/arch/mips/Makefile
diff --git a/purgatory/arch/arm64/Makefile b/purgatory/arch/arm64/Makefile
new file mode 100644
index 0000000..636abea
--- /dev/null
+++ b/purgatory/arch/arm64/Makefile
@@ -0,0 +1,18 @@
+
+arm64_PURGATORY_EXTRA_CFLAGS = \
+ -mcmodel=large \
+ -fno-stack-protector \
+ -fno-asynchronous-unwind-tables \
+ -Wundef \
+ -Werror-implicit-function-declaration \
+ -Wdeclaration-after-statement \
+ -Werror=implicit-int \
+ -Werror=strict-prototypes
+
+arm64_PURGATORY_SRCS += \
+ purgatory/arch/arm64/entry.S \
+ purgatory/arch/arm64/purgatory-arm64.c
+
+dist += \
+ $(arm64_PURGATORY_SRCS) \
+ purgatory/arch/arm64/Makefile
diff --git a/purgatory/arch/arm64/entry.S b/purgatory/arch/arm64/entry.S
new file mode 100644
index 0000000..adf16f4
--- /dev/null
+++ b/purgatory/arch/arm64/entry.S
@@ -0,0 +1,51 @@
+/*
+ * ARM64 purgatory.
+ */
+
+.macro size, sym:req
+ .size \sym, . - \sym
+.endm
+
+.text
+
+.globl purgatory_start
+purgatory_start:
+
+ adr x19, .Lstack
+ mov sp, x19
+
+ bl purgatory
+
+ /* Start new image. */
+ ldr x17, arm64_kernel_entry
+ ldr x0, arm64_dtb_addr
+ mov x1, xzr
+ mov x2, xzr
+ mov x3, xzr
+ br x17
+
+size purgatory_start
+
+.ltorg
+
+.align 4
+ .rept 256
+ .quad 0
+ .endr
+.Lstack:
+
+.data
+
+.align 3
+
+.globl arm64_kernel_entry
+arm64_kernel_entry:
+ .quad 0
+size arm64_kernel_entry
+
+.globl arm64_dtb_addr
+arm64_dtb_addr:
+ .quad 0
+size arm64_dtb_addr
+
+.end
\ No newline at end of file
diff --git a/purgatory/arch/arm64/purgatory-arm64.c b/purgatory/arch/arm64/purgatory-arm64.c
new file mode 100644
index 0000000..fe50fcf
--- /dev/null
+++ b/purgatory/arch/arm64/purgatory-arm64.c
@@ -0,0 +1,19 @@
+/*
+ * ARM64 purgatory.
+ */
+
+#include <stdint.h>
+#include <purgatory.h>
+
+void putchar(int ch)
+{
+ /* Nothing for now */
+}
+
+void post_verification_setup_arch(void)
+{
+}
+
+void setup_arch(void)
+{
+}
--
2.7.4
_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v5 2/3] arm64: Add arm64 kexec support
2016-09-01 21:51 ` Geoff Levand
@ 2016-09-06 5:44 ` AKASHI Takahiro
-1 siblings, 0 replies; 14+ messages in thread
From: AKASHI Takahiro @ 2016-09-06 5:44 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, Sep 01, 2016 at 09:51:10PM +0000, Geoff Levand wrote:
> Add kexec reboot support for ARM64 platforms.
>
> Signed-off-by: Geoff Levand <geoff@infradead.org>
> ---
> configure.ac | 3 +
> kexec/Makefile | 1 +
> kexec/arch/arm64/Makefile | 40 ++
> kexec/arch/arm64/crashdump-arm64.c | 21 +
> kexec/arch/arm64/crashdump-arm64.h | 12 +
> kexec/arch/arm64/image-header.h | 146 +++++++
> kexec/arch/arm64/include/arch/options.h | 39 ++
> kexec/arch/arm64/kexec-arm64.c | 717 ++++++++++++++++++++++++++++++++
> kexec/arch/arm64/kexec-arm64.h | 71 ++++
> kexec/arch/arm64/kexec-elf-arm64.c | 146 +++++++
> kexec/arch/arm64/kexec-image-arm64.c | 41 ++
> kexec/kexec-syscall.h | 8 +-
> purgatory/Makefile | 1 +
> purgatory/arch/arm64/Makefile | 18 +
> purgatory/arch/arm64/entry.S | 51 +++
> purgatory/arch/arm64/purgatory-arm64.c | 19 +
> 16 files changed, 1332 insertions(+), 2 deletions(-)
> create mode 100644 kexec/arch/arm64/Makefile
> create mode 100644 kexec/arch/arm64/crashdump-arm64.c
> create mode 100644 kexec/arch/arm64/crashdump-arm64.h
> create mode 100644 kexec/arch/arm64/image-header.h
> create mode 100644 kexec/arch/arm64/include/arch/options.h
> create mode 100644 kexec/arch/arm64/kexec-arm64.c
> create mode 100644 kexec/arch/arm64/kexec-arm64.h
> create mode 100644 kexec/arch/arm64/kexec-elf-arm64.c
> create mode 100644 kexec/arch/arm64/kexec-image-arm64.c
> create mode 100644 purgatory/arch/arm64/Makefile
> create mode 100644 purgatory/arch/arm64/entry.S
> create mode 100644 purgatory/arch/arm64/purgatory-arm64.c
>
> diff --git a/configure.ac b/configure.ac
> index 736da6e..ba89075 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -34,6 +34,9 @@ case $target_cpu in
> ARCH="ppc64"
> SUBARCH="LE"
> ;;
> + aarch64* )
> + ARCH="arm64"
> + ;;
> arm* )
> ARCH="arm"
> ;;
> diff --git a/kexec/Makefile b/kexec/Makefile
> index cc3f08b..39f365f 100644
> --- a/kexec/Makefile
> +++ b/kexec/Makefile
> @@ -79,6 +79,7 @@ KEXEC_SRCS += $($(ARCH)_DT_OPS)
>
> include $(srcdir)/kexec/arch/alpha/Makefile
> include $(srcdir)/kexec/arch/arm/Makefile
> +include $(srcdir)/kexec/arch/arm64/Makefile
> include $(srcdir)/kexec/arch/i386/Makefile
> include $(srcdir)/kexec/arch/ia64/Makefile
> include $(srcdir)/kexec/arch/m68k/Makefile
> diff --git a/kexec/arch/arm64/Makefile b/kexec/arch/arm64/Makefile
> new file mode 100644
> index 0000000..37414dc
> --- /dev/null
> +++ b/kexec/arch/arm64/Makefile
> @@ -0,0 +1,40 @@
> +
> +arm64_FS2DT += kexec/fs2dt.c
> +arm64_FS2DT_INCLUDE += -include $(srcdir)/kexec/arch/arm64/kexec-arm64.h \
> + -include $(srcdir)/kexec/arch/arm64/crashdump-arm64.h
> +
> +arm64_DT_OPS += kexec/dt-ops.c
> +
> +arm64_CPPFLAGS += -I $(srcdir)/kexec/
> +
> +arm64_KEXEC_SRCS += \
> + kexec/arch/arm64/kexec-arm64.c \
> + kexec/arch/arm64/kexec-image-arm64.c \
> + kexec/arch/arm64/kexec-elf-arm64.c \
> + kexec/arch/arm64/crashdump-arm64.c
> +
> +arm64_ARCH_REUSE_INITRD =
> +arm64_ADD_SEGMENT =
> +arm64_VIRT_TO_PHYS =
> +arm64_PHYS_TO_VIRT =
> +
> +dist += $(arm64_KEXEC_SRCS) \
> + kexec/arch/arm64/Makefile \
> + kexec/arch/arm64/kexec-arm64.h \
> + kexec/arch/arm64/crashdump-arm64.h
> +
> +ifdef HAVE_LIBFDT
> +
> +LIBS += -lfdt
> +
> +else
> +
> +include $(srcdir)/kexec/libfdt/Makefile.libfdt
> +
> +libfdt_SRCS += $(LIBFDT_SRCS:%=kexec/libfdt/%)
> +
> +arm64_CPPFLAGS += -I$(srcdir)/kexec/libfdt
> +
> +arm64_KEXEC_SRCS += $(libfdt_SRCS)
> +
> +endif
> diff --git a/kexec/arch/arm64/crashdump-arm64.c b/kexec/arch/arm64/crashdump-arm64.c
> new file mode 100644
> index 0000000..d2272c8
> --- /dev/null
> +++ b/kexec/arch/arm64/crashdump-arm64.c
> @@ -0,0 +1,21 @@
> +/*
> + * ARM64 crashdump.
> + */
> +
> +#define _GNU_SOURCE
> +
> +#include <errno.h>
> +#include <linux/elf.h>
> +
> +#include "kexec.h"
> +#include "crashdump.h"
> +#include "crashdump-arm64.h"
> +#include "kexec-arm64.h"
> +#include "kexec-elf.h"
> +
> +struct memory_ranges usablemem_rgns = {};
> +
> +int is_crashkernel_mem_reserved(void)
> +{
> + return 0;
> +}
> diff --git a/kexec/arch/arm64/crashdump-arm64.h b/kexec/arch/arm64/crashdump-arm64.h
> new file mode 100644
> index 0000000..f33c7a2
> --- /dev/null
> +++ b/kexec/arch/arm64/crashdump-arm64.h
> @@ -0,0 +1,12 @@
> +/*
> + * ARM64 crashdump.
> + */
> +
> +#if !defined(CRASHDUMP_ARM64_H)
> +#define CRASHDUMP_ARM64_H
> +
> +#include "kexec.h"
> +
> +extern struct memory_ranges usablemem_rgns;
> +
> +#endif
> diff --git a/kexec/arch/arm64/image-header.h b/kexec/arch/arm64/image-header.h
> new file mode 100644
> index 0000000..158d411
> --- /dev/null
> +++ b/kexec/arch/arm64/image-header.h
> @@ -0,0 +1,146 @@
> +/*
> + * ARM64 binary image header.
> + */
> +
> +#if !defined(__ARM64_IMAGE_HEADER_H)
> +#define __ARM64_IMAGE_HEADER_H
> +
> +#include <endian.h>
> +#include <stdint.h>
> +
> +/**
> + * struct arm64_image_header - arm64 kernel image header.
> + *
> + * @pe_sig: Optional PE format 'MZ' signature.
> + * @branch_code: Reserved for instructions to branch to stext.
> + * @text_offset: The image load offset in LSB byte order.
> + * @image_size: An estimated size of the memory image size in LSB byte order.
> + * @flags: Bit flags in LSB byte order:
> + * Bit 0: Image byte order: 1=MSB.
> + * Bit 1-2: Kernel page size: 1=4K, 2=16K, 3=64K.
> + * Bit 3: Image placement: 0=low.
> + * @reserved_1: Reserved.
> + * @magic: Magic number, "ARM\x64".
> + * @pe_header: Optional offset to a PE format header.
> + **/
> +
> +struct arm64_image_header {
> + uint8_t pe_sig[2];
> + uint16_t branch_code[3];
> + uint64_t text_offset;
> + uint64_t image_size;
> + uint64_t flags;
> + uint64_t reserved_1[3];
> + uint8_t magic[4];
> + uint32_t pe_header;
> +};
> +
> +static const uint8_t arm64_image_magic[4] = {'A', 'R', 'M', 0x64U};
> +static const uint8_t arm64_image_pe_sig[2] = {'M', 'Z'};
> +static const uint64_t arm64_image_flag_be = (1UL << 0);
> +static const uint64_t arm64_image_flag_page_size = (3UL << 1);
> +static const uint64_t arm64_image_flag_placement = (1UL << 3);
> +
> +/**
> + * enum arm64_header_page_size
> + */
> +
> +enum arm64_header_page_size {
> + arm64_header_page_size_invalid = 0,
> + arm64_header_page_size_4k,
> + arm64_header_page_size_16k,
> + arm64_header_page_size_64k
> +};
> +
> +/**
> + * arm64_header_check_magic - Helper to check the arm64 image header.
> + *
> + * Returns non-zero if header is OK.
> + */
> +
> +static inline int arm64_header_check_magic(const struct arm64_image_header *h)
> +{
> + if (!h)
> + return 0;
> +
> + return (h->magic[0] == arm64_image_magic[0]
> + && h->magic[1] == arm64_image_magic[1]
> + && h->magic[2] == arm64_image_magic[2]
> + && h->magic[3] == arm64_image_magic[3]);
> +}
> +
> +/**
> + * arm64_header_check_pe_sig - Helper to check the arm64 image header.
> + *
> + * Returns non-zero if 'MZ' signature is found.
> + */
> +
> +static inline int arm64_header_check_pe_sig(const struct arm64_image_header *h)
> +{
> + if (!h)
> + return 0;
> +
> + return (h->pe_sig[0] == arm64_image_pe_sig[0]
> + && h->pe_sig[1] == arm64_image_pe_sig[1]);
> +}
> +
> +/**
> + * arm64_header_check_msb - Helper to check the arm64 image header.
> + *
> + * Returns non-zero if the image was built as big endian.
> + */
> +
> +static inline int arm64_header_check_msb(const struct arm64_image_header *h)
> +{
> + if (!h)
> + return 0;
> +
> + return (le64toh(h->flags) & arm64_image_flag_be) >> 0;
> +}
> +
> +/**
> + * arm64_header_page_size
> + */
> +
> +static inline enum arm64_header_page_size arm64_header_page_size(
> + const struct arm64_image_header *h)
> +{
> + if (!h)
> + return 0;
> +
> + return (le64toh(h->flags) & arm64_image_flag_page_size) >> 1;
> +}
> +
> +/**
> + * arm64_header_placement
> + *
> + * Returns non-zero if the image has no physical placement restrictions.
> + */
> +
> +static inline int arm64_header_placement(const struct arm64_image_header *h)
> +{
> + if (!h)
> + return 0;
> +
> + return (le64toh(h->flags) & arm64_image_flag_placement) >> 3;
> +}
> +
> +static inline uint64_t arm64_header_text_offset(
> + const struct arm64_image_header *h)
> +{
> + if (!h)
> + return 0;
> +
> + return le64toh(h->text_offset);
> +}
> +
> +static inline uint64_t arm64_header_image_size(
> + const struct arm64_image_header *h)
> +{
> + if (!h)
> + return 0;
> +
> + return le64toh(h->image_size);
> +}
> +
> +#endif
> diff --git a/kexec/arch/arm64/include/arch/options.h b/kexec/arch/arm64/include/arch/options.h
> new file mode 100644
> index 0000000..a17d933
> --- /dev/null
> +++ b/kexec/arch/arm64/include/arch/options.h
> @@ -0,0 +1,39 @@
> +#if !defined(KEXEC_ARCH_ARM64_OPTIONS_H)
> +#define KEXEC_ARCH_ARM64_OPTIONS_H
> +
> +#define OPT_APPEND ((OPT_MAX)+0)
> +#define OPT_DTB ((OPT_MAX)+1)
> +#define OPT_INITRD ((OPT_MAX)+2)
> +#define OPT_REUSE_CMDLINE ((OPT_MAX)+3)
> +#define OPT_ARCH_MAX ((OPT_MAX)+4)
> +
> +#define KEXEC_ARCH_OPTIONS \
> + KEXEC_OPTIONS \
> + { "append", 1, NULL, OPT_APPEND }, \
> + { "command-line", 1, NULL, OPT_APPEND }, \
> + { "dtb", 1, NULL, OPT_DTB }, \
> + { "initrd", 1, NULL, OPT_INITRD }, \
> + { "ramdisk", 1, NULL, OPT_INITRD }, \
> + { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE }, \
> +
> +#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR /* Only accept long arch options. */
> +#define KEXEC_ALL_OPTIONS KEXEC_ARCH_OPTIONS
> +#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR
> +
> +static const char arm64_opts_usage[] __attribute__ ((unused)) =
> +" --append=STRING Set the kernel command line to STRING.\n"
> +" --command-line=STRING Set the kernel command line to STRING.\n"
> +" --dtb=FILE Use FILE as the device tree blob.\n"
> +" --initrd=FILE Use FILE as the kernel initial ramdisk.\n"
> +" --ramdisk=FILE Use FILE as the kernel initial ramdisk.\n"
> +" --reuse-cmdline Use kernel command line from running system.\n";
> +
> +struct arm64_opts {
> + const char *command_line;
> + const char *dtb;
> + const char *initrd;
> +};
> +
> +extern struct arm64_opts arm64_opts;
> +
> +#endif
> diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c
> new file mode 100644
> index 0000000..7183dac
> --- /dev/null
> +++ b/kexec/arch/arm64/kexec-arm64.c
> @@ -0,0 +1,717 @@
> +/*
> + * ARM64 kexec.
> + */
> +
> +#define _GNU_SOURCE
> +
> +#include <assert.h>
> +#include <errno.h>
> +#include <getopt.h>
> +#include <inttypes.h>
> +#include <libfdt.h>
> +#include <limits.h>
> +#include <stdlib.h>
> +#include <sys/stat.h>
> +#include <linux/elf-em.h>
> +#include <elf.h>
> +
> +#include "kexec.h"
> +#include "kexec-arm64.h"
> +#include "crashdump.h"
> +#include "crashdump-arm64.h"
> +#include "dt-ops.h"
> +#include "fs2dt.h"
> +#include "kexec-syscall.h"
> +#include "arch/options.h"
> +
> +/* Global varables the core kexec routines expect. */
> +
> +unsigned char reuse_initrd;
> +
> +off_t initrd_base;
> +off_t initrd_size;
> +
> +const struct arch_map_entry arches[] = {
> + { "aarch64", KEXEC_ARCH_ARM64 },
> + { "aarch64_be", KEXEC_ARCH_ARM64 },
> + { NULL, 0 },
> +};
> +
> +struct file_type file_type[] = {
> + {"vmlinux", elf_arm64_probe, elf_arm64_load, elf_arm64_usage},
> + {"Image", image_arm64_probe, image_arm64_load, image_arm64_usage},
> +};
> +
> +int file_types = sizeof(file_type) / sizeof(file_type[0]);
> +
> +/* arm64 global varables. */
> +
> +struct arm64_opts arm64_opts;
> +struct arm64_mem arm64_mem = {
> + .phys_offset = arm64_mem_ngv,
> + .vp_offset = arm64_mem_ngv,
> +};
> +
> +uint64_t get_phys_offset(void)
> +{
> + assert(arm64_mem.phys_offset != arm64_mem_ngv);
> + return arm64_mem.phys_offset;
> +}
> +
> +uint64_t get_vp_offset(void)
> +{
> + assert(arm64_mem.vp_offset != arm64_mem_ngv);
> + return arm64_mem.vp_offset;
> +}
> +
> +/**
> + * arm64_process_image_header - Process the arm64 image header.
> + *
> + * Make a guess that KERNEL_IMAGE_SIZE will be enough for older kernels.
> + */
> +
> +int arm64_process_image_header(const struct arm64_image_header *h)
> +{
> +#if !defined(KERNEL_IMAGE_SIZE)
> +# define KERNEL_IMAGE_SIZE MiB(16)
> +#endif
> +
> + if (!arm64_header_check_magic(h))
> + return -EFAILED;
> +
> + if (h->image_size) {
> + arm64_mem.text_offset = arm64_header_text_offset(h);
> + arm64_mem.image_size = arm64_header_image_size(h);
> + } else {
> + /* For 3.16 and older kernels. */
> + arm64_mem.text_offset = 0x80000;
> + arm64_mem.image_size = KERNEL_IMAGE_SIZE;
> + fprintf(stderr,
> + "kexec: %s: Warning: Kernel image size set to %lu MiB.\n"
> + " Please verify compatability with lodaed kernel.\n",
> + __func__, KERNEL_IMAGE_SIZE / 1024UL / 1024UL);
> + }
> +
> + return 0;
> +}
> +
> +void arch_usage(void)
> +{
> + printf(arm64_opts_usage);
> +}
> +
> +int arch_process_options(int argc, char **argv)
> +{
> + static const char short_options[] = KEXEC_OPT_STR "";
> + static const struct option options[] = {
> + KEXEC_ARCH_OPTIONS
> + { 0 }
> + };
> + int opt;
> + char *cmdline = NULL;
> + const char *append = NULL;
> +
> + for (opt = 0; opt != -1; ) {
> + opt = getopt_long(argc, argv, short_options, options, 0);
> +
> + switch (opt) {
> + case OPT_APPEND:
> + append = optarg;
> + break;
> + case OPT_REUSE_CMDLINE:
> + cmdline = get_command_line();
> + break;
> + case OPT_DTB:
> + arm64_opts.dtb = optarg;
> + break;
> + case OPT_INITRD:
> + arm64_opts.initrd = optarg;
> + break;
> + case OPT_PANIC:
> + die("load-panic (-p) not supported");
> + break;
> + default:
> + break; /* Ignore core and unknown options. */
> + }
> + }
> +
> + arm64_opts.command_line = concat_cmdline(cmdline, append);
> +
> + dbgprintf("%s:%d: command_line: %s\n", __func__, __LINE__,
> + arm64_opts.command_line);
> + dbgprintf("%s:%d: initrd: %s\n", __func__, __LINE__,
> + arm64_opts.initrd);
> + dbgprintf("%s:%d: dtb: %s\n", __func__, __LINE__, arm64_opts.dtb);
> +
> + return 0;
> +}
> +
> +/**
> + * struct dtb - Info about a binary device tree.
> + *
> + * @buf: Device tree data.
> + * @size: Device tree data size.
> + * @name: Shorthand name of this dtb for messages.
> + * @path: Filesystem path.
> + */
> +
> +struct dtb {
> + char *buf;
> + off_t size;
> + const char *name;
> + const char *path;
> +};
> +
> +/**
> + * dump_reservemap - Dump the dtb's reservemap.
> + */
> +
> +static void dump_reservemap(const struct dtb *dtb)
> +{
> + int i;
> +
> + for (i = 0; ; i++) {
> + uint64_t address;
> + uint64_t size;
> +
> + fdt_get_mem_rsv(dtb->buf, i, &address, &size);
> +
> + if (!size)
> + break;
> +
> + dbgprintf("%s: %s {%" PRIx64 ", %" PRIx64 "}\n", __func__,
> + dtb->name, address, size);
> + }
> +}
> +
> +/**
> + * set_bootargs - Set the dtb's bootargs.
> + */
> +
> +static int set_bootargs(struct dtb *dtb, const char *command_line)
> +{
> + int result;
> +
> + if (!command_line || !command_line[0])
> + return 0;
> +
> + result = dtb_set_bootargs(&dtb->buf, &dtb->size, command_line);
> +
> + if (result) {
> + fprintf(stderr,
> + "kexec: Set device tree bootargs failed.\n");
> + return -EFAILED;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * read_proc_dtb - Read /proc/device-tree.
> + */
> +
> +static int read_proc_dtb(struct dtb *dtb)
> +{
> + int result;
> + struct stat s;
> + static const char path[] = "/proc/device-tree";
> +
> + result = stat(path, &s);
> +
> + if (result) {
> + dbgprintf("%s: %s\n", __func__, strerror(errno));
> + return -EFAILED;
> + }
> +
> + dtb->path = path;
> + create_flatten_tree((char **)&dtb->buf, &dtb->size, NULL);
> +
> + return 0;
> +}
> +
> +/**
> + * read_sys_dtb - Read /sys/firmware/fdt.
> + */
> +
> +static int read_sys_dtb(struct dtb *dtb)
> +{
> + int result;
> + struct stat s;
> + static const char path[] = "/sys/firmware/fdt";
> +
> + result = stat(path, &s);
> +
> + if (result) {
> + dbgprintf("%s: %s\n", __func__, strerror(errno));
> + return -EFAILED;
> + }
> +
> + dtb->path = path;
> + dtb->buf = slurp_file(path, &dtb->size);
> +
> + return 0;
> +}
> +
> +/**
> + * read_1st_dtb - Read the 1st stage kernel's dtb.
> + */
> +
> +static int read_1st_dtb(struct dtb *dtb)
> +{
> + int result;
> +
> + result = read_sys_dtb(dtb);
> +
> + if (!result)
> + goto on_success;
> +
> + result = read_proc_dtb(dtb);
> +
> + if (!result)
> + goto on_success;
> +
> + dbgprintf("%s: not found\n", __func__);
> + return -EFAILED;
> +
> +on_success:
> + dbgprintf("%s: found %s\n", __func__, dtb->path);
> + return 0;
> +}
> +
> +/**
> + * setup_2nd_dtb - Setup the 2nd stage kernel's dtb.
> + */
> +
> +static int setup_2nd_dtb(struct dtb *dtb, char *command_line)
> +{
> + int result;
> +
> + result = fdt_check_header(dtb->buf);
> +
> + if (result) {
> + fprintf(stderr, "kexec: Invalid 2nd device tree.\n");
> + return -EFAILED;
> + }
> +
> + result = set_bootargs(dtb, command_line);
> +
> + dump_reservemap(dtb);
> +
> + return result;
> +}
> +
> +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 (hole == ULONG_MAX)
> + dbgprintf("%s: locate_hole failed\n", __func__);
> +
> + return hole;
> +}
> +
> +/**
> + * arm64_load_other_segments - Prepare the dtb, initrd and purgatory segments.
> + */
> +
> +int arm64_load_other_segments(struct kexec_info *info,
> + unsigned long image_base)
> +{
> + int result;
> + unsigned long dtb_base;
> + unsigned long hole_min;
> + unsigned long hole_max;
> + char *initrd_buf = NULL;
> + struct dtb dtb;
> + char command_line[COMMAND_LINE_SIZE] = "";
> +
> + if (arm64_opts.command_line) {
> + strncpy(command_line, arm64_opts.command_line,
> + sizeof(command_line));
> + command_line[sizeof(command_line) - 1] = 0;
> + }
> +
> + if (arm64_opts.dtb) {
> + dtb.name = "dtb_2";
"dtb_1" and "dtb_2" are not very meaningful.
Instead, I'm going to use:
dtb_2 => "dtb_user" (if a command line option is specified)
dtb_1 => "dtb_sys" (if from /sys/firmware/fdt), or
"dtb_unflatten" (if created from /proc/device-tree)
to distinguish the origins in my kdump patches.
> + dtb.buf = slurp_file(arm64_opts.dtb, &dtb.size);
> + } else {
> + dtb.name = "dtb_1";
> + result = read_1st_dtb(&dtb);
> +
> + if (result) {
> + fprintf(stderr,
> + "kexec: Error: No device tree available.\n");
> + return -EFAILED;
> + }
> + }
> +
> + result = setup_2nd_dtb(&dtb, command_line);
> +
> + if (result)
> + return -EFAILED;
> +
> + /* Put the other segments after the image. */
> +
> + hole_min = image_base + arm64_mem.image_size;
> + hole_max = ULONG_MAX;
> +
> + if (arm64_opts.initrd) {
> + initrd_buf = slurp_file(arm64_opts.initrd, &initrd_size);
> +
> + if (!initrd_buf)
> + fprintf(stderr, "kexec: Empty ramdisk file.\n");
> + else {
> + /*
> + * Put the initrd after the kernel. As specified in
> + * booting.txt, align to 1 GiB.
> + */
> +
> + initrd_base = add_buffer_phys_virt(info, initrd_buf,
> + initrd_size, initrd_size, GiB(1),
> + hole_min, hole_max, 1, 0);
> +
> + /* initrd_base is valid if we got here. */
> +
> + dbgprintf("initrd: base %lx, size %lxh (%ld)\n",
> + initrd_base, initrd_size, initrd_size);
> +
> + /* Check size limit as specified in booting.txt. */
> +
> + if (initrd_base - image_base + initrd_size > GiB(32)) {
> + fprintf(stderr, "kexec: Error: image + initrd too big.\n");
> + return -EFAILED;
> + }
> +
> + result = dtb_set_initrd((char **)&dtb.buf,
> + &dtb.size, initrd_base,
> + initrd_base + initrd_size);
> +
> + if (result)
> + return -EFAILED;
> + }
> + }
> +
> + /* Check size limit as specified in booting.txt. */
> +
> + if (dtb.size > MiB(2)) {
> + fprintf(stderr, "kexec: Error: dtb too big.\n");
> + return -EFAILED;
> + }
> +
> + dtb_base = add_buffer_phys_virt(info, dtb.buf, dtb.size, dtb.size,
> + 0, hole_min, hole_max, 1, 0);
> +
> + /* dtb_base is valid if we got here. */
> +
> + dbgprintf("dtb: base %lx, size %lxh (%ld)\n", dtb_base, dtb.size,
> + dtb.size);
> +
> + elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
> + hole_min, hole_max, 1, 0);
> +
> + info->entry = (void *)elf_rel_get_addr(&info->rhdr, "purgatory_start");
> +
> + elf_rel_set_symbol(&info->rhdr, "arm64_kernel_entry", &image_base,
> + sizeof(image_base));
> +
> + elf_rel_set_symbol(&info->rhdr, "arm64_dtb_addr", &dtb_base,
> + sizeof(dtb_base));
> +
> + return 0;
> +}
> +
> +/**
> + * virt_to_phys - For processing elf file values.
> + */
> +
> +unsigned long virt_to_phys(unsigned long v)
> +{
> + unsigned long p;
> +
> + p = v - get_vp_offset() + get_phys_offset();
> +
> + return p;
> +}
> +
> +/**
> + * phys_to_virt - For crashdump setup.
> + */
> +
> +unsigned long phys_to_virt(struct crash_elf_info *elf_info,
> + unsigned long long p)
> +{
> + unsigned long v;
> +
> + v = p - get_phys_offset() + elf_info->page_offset;
> +
> + return v;
> +}
> +
> +/**
> + * add_segment - Use virt_to_phys when loading elf files.
> + */
> +
> +void add_segment(struct kexec_info *info, const void *buf, size_t bufsz,
> + unsigned long base, size_t memsz)
> +{
> + add_segment_phys_virt(info, buf, bufsz, base, memsz, 1);
> +}
> +
> +/**
> + * get_memory_ranges_iomem_cb - Helper for get_memory_ranges_iomem.
> + */
> +
> +static int get_memory_ranges_iomem_cb(void *data, int nr, char *str,
> + unsigned long long base, unsigned long long length)
> +{
> + struct memory_range *r;
> +
> + if (nr >= KEXEC_SEGMENT_MAX)
> + return -1;
> +
> + r = (struct memory_range *)data + nr;
> + r->type = RANGE_RAM;
> + r->start = base;
> + r->end = base + length - 1;
> +
> + set_phys_offset(r->start);
This will no longer work correctly for identifying PHYS_OFFSET
once the following patch is applied:
http://lists.infradead.org/pipermail/linux-arm-kernel/2016-August/450433.html
and it is now queued in arm64/for-next.
I will include a fixup in my kdump patches as the current code of kexec
doesn't rely on a result of virt_to_phys() seriously.
> +
> + dbgprintf("%s: %016llx - %016llx : %s", __func__, r->start,
> + r->end, str);
> +
> + return 0;
> +}
> +
> +/**
> + * get_memory_ranges_iomem - Try to get the memory ranges from /proc/iomem.
> + */
> +
> +static int get_memory_ranges_iomem(struct memory_range *array,
> + unsigned int *count)
> +{
> + *count = kexec_iomem_for_each_line("System RAM\n",
> + get_memory_ranges_iomem_cb, array);
> +
> + if (!*count) {
> + dbgprintf("%s: failed: No RAM found.\n", __func__);
> + return -EFAILED;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * get_memory_ranges_dt - Try to get the memory ranges from the 1st stage dtb.
> + */
> +
> +static int get_memory_ranges_dt(struct memory_range *array, unsigned int *count)
> +{
> + struct region {uint64_t base; uint64_t size;};
> + struct dtb dtb = {.name = "range_dtb"};
> + int offset;
> + int result;
> +
> + *count = 0;
> +
> + result = read_1st_dtb(&dtb);
> +
> + if (result) {
> + goto on_error;
> + }
> +
> + result = fdt_check_header(dtb.buf);
> +
> + if (result) {
> + dbgprintf("%s:%d: %s: fdt_check_header failed:%s\n", __func__,
> + __LINE__, dtb.path, fdt_strerror(result));
> + goto on_error;
> + }
> +
> + for (offset = 0; ; ) {
> + const struct region *region;
> + const struct region *end;
> + int len;
> +
> + offset = fdt_subnode_offset(dtb.buf, offset, "memory");
> +
> + if (offset == -FDT_ERR_NOTFOUND)
> + break;
> +
> + if (offset <= 0) {
> + dbgprintf("%s:%d: fdt_subnode_offset failed: %d %s\n",
> + __func__, __LINE__, offset,
> + fdt_strerror(offset));
> + goto on_error;
> + }
> +
> + dbgprintf("%s:%d: node_%d %s\n", __func__, __LINE__, offset,
> + fdt_get_name(dtb.buf, offset, NULL));
> +
> + region = fdt_getprop(dtb.buf, offset, "reg", &len);
> +
> + if (region <= 0) {
> + dbgprintf("%s:%d: fdt_getprop failed: %d %s\n",
> + __func__, __LINE__, offset,
> + fdt_strerror(offset));
> + goto on_error;
> + }
> +
> + for (end = region + len / sizeof(*region);
> + region < end && *count < KEXEC_SEGMENT_MAX;
> + region++) {
> + struct memory_range r;
> +
> + r.type = RANGE_RAM;
> + r.start = fdt64_to_cpu(region->base);
> + r.end = r.start + fdt64_to_cpu(region->size) - 1;
> +
> + if (!region->size) {
> + dbgprintf("%s:%d: SKIP: %016llx - %016llx\n",
> + __func__, __LINE__, r.start, r.end);
> + continue;
> + }
> +
> + dbgprintf("%s:%d: RAM: %016llx - %016llx\n", __func__,
> + __LINE__, r.start, r.end);
> +
> + array[(*count)++] = r;
> +
> + set_phys_offset(r.start);
> + }
> + }
> +
> + if (!*count) {
> + dbgprintf("%s:%d: %s: No RAM found.\n", __func__, __LINE__,
> + dtb.path);
> + goto on_error;
> + }
> +
> + dbgprintf("%s:%d: %s: Success\n", __func__, __LINE__, dtb.path);
> + result = 0;
> + goto on_exit;
> +
> +on_error:
> + fprintf(stderr, "%s:%d: %s: Unusable device-tree file\n", __func__,
> + __LINE__, dtb.path);
> + result = -EFAILED;
> +
> +on_exit:
> + free(dtb.buf);
> + return result;
> +}
> +
> +/**
> + * get_memory_ranges - Try to get the memory ranges some how.
> + */
> +
> +int get_memory_ranges(struct memory_range **range, int *ranges,
> + unsigned long kexec_flags)
> +{
> + static struct memory_range array[KEXEC_SEGMENT_MAX];
> + unsigned int count;
> + int result;
> +
> + result = get_memory_ranges_iomem(array, &count);
> +
> + if (result)
> + result = get_memory_ranges_dt(array, &count);
Do we really need to scan a DT blob here?
I think that all the (usable) memory regions are added to
/proc/iomem anyway.
> +
> + *range = result ? NULL : array;
> + *ranges = result ? 0 : count;
> +
> + return result;
> +}
> +
> +int arch_compat_trampoline(struct kexec_info *info)
> +{
> + return 0;
> +}
> +
> +int machine_verify_elf_rel(struct mem_ehdr *ehdr)
> +{
> + return (ehdr->e_machine == EM_AARCH64);
> +}
> +
> +void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
> + unsigned long r_type, void *ptr, unsigned long address,
> + unsigned long value)
> +{
> +#if !defined(R_AARCH64_ABS64)
> +# define R_AARCH64_ABS64 257
> +#endif
> +
> +#if !defined(R_AARCH64_LD_PREL_LO19)
> +# define R_AARCH64_LD_PREL_LO19 273
> +#endif
> +
> +#if !defined(R_AARCH64_ADR_PREL_LO21)
> +# define R_AARCH64_ADR_PREL_LO21 274
> +#endif
> +
> +#if !defined(R_AARCH64_JUMP26)
> +# define R_AARCH64_JUMP26 282
> +#endif
> +
> +#if !defined(R_AARCH64_CALL26)
> +# define R_AARCH64_CALL26 283
> +#endif
Did you see my comment?
http://lists.infradead.org/pipermail/kexec/2016-August/016947.html
-Takahiro AKASHI
> +
> + uint64_t *loc64;
> + uint32_t *loc32;
> + uint64_t *location = (uint64_t *)ptr;
> + uint64_t data = *location;
> + const char *type = NULL;
> +
> + switch(r_type) {
> + case R_AARCH64_ABS64:
> + type = "ABS64";
> + loc64 = ptr;
> + *loc64 = cpu_to_elf64(ehdr, elf64_to_cpu(ehdr, *loc64) + value);
> + break;
> + case R_AARCH64_LD_PREL_LO19:
> + type = "LD_PREL_LO19";
> + loc32 = ptr;
> + *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
> + + (((value - address) << 3) & 0xffffe0));
> + break;
> + case R_AARCH64_ADR_PREL_LO21:
> + if (value & 3)
> + die("%s: ERROR Unaligned value: %lx\n", __func__,
> + value);
> + type = "ADR_PREL_LO21";
> + loc32 = ptr;
> + *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
> + + (((value - address) << 3) & 0xffffe0));
> + break;
> + case R_AARCH64_JUMP26:
> + type = "JUMP26";
> + loc32 = ptr;
> + *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
> + + (((value - address) >> 2) & 0x3ffffff));
> + break;
> + case R_AARCH64_CALL26:
> + type = "CALL26";
> + loc32 = ptr;
> + *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
> + + (((value - address) >> 2) & 0x3ffffff));
> + break;
> + default:
> + die("%s: ERROR Unknown type: %lu\n", __func__, r_type);
> + break;
> + }
> +
> + dbgprintf("%s: %s %016lx->%016lx\n", __func__, type, data, *location);
> +}
> +
> +void arch_reuse_initrd(void)
> +{
> + reuse_initrd = 1;
> +}
> +
> +void arch_update_purgatory(struct kexec_info *UNUSED(info))
> +{
> +}
> diff --git a/kexec/arch/arm64/kexec-arm64.h b/kexec/arch/arm64/kexec-arm64.h
> new file mode 100644
> index 0000000..bac62f8
> --- /dev/null
> +++ b/kexec/arch/arm64/kexec-arm64.h
> @@ -0,0 +1,71 @@
> +/*
> + * ARM64 kexec.
> + */
> +
> +#if !defined(KEXEC_ARM64_H)
> +#define KEXEC_ARM64_H
> +
> +#include <stdbool.h>
> +#include <sys/types.h>
> +
> +#include "image-header.h"
> +#include "kexec.h"
> +
> +#define KEXEC_SEGMENT_MAX 16
> +
> +#define BOOT_BLOCK_VERSION 17
> +#define BOOT_BLOCK_LAST_COMP_VERSION 16
> +#define COMMAND_LINE_SIZE 512
> +
> +#define KiB(x) ((x) * 1024UL)
> +#define MiB(x) (KiB(x) * 1024UL)
> +#define GiB(x) (MiB(x) * 1024UL)
> +
> +int elf_arm64_probe(const char *kernel_buf, off_t kernel_size);
> +int elf_arm64_load(int argc, char **argv, const char *kernel_buf,
> + off_t kernel_size, struct kexec_info *info);
> +void elf_arm64_usage(void);
> +
> +int image_arm64_probe(const char *kernel_buf, off_t kernel_size);
> +int image_arm64_load(int argc, char **argv, const char *kernel_buf,
> + off_t kernel_size, struct kexec_info *info);
> +void image_arm64_usage(void);
> +
> +off_t initrd_base;
> +off_t initrd_size;
> +
> +/**
> + * struct arm64_mem - Memory layout info.
> + */
> +
> +struct arm64_mem {
> + uint64_t phys_offset;
> + uint64_t text_offset;
> + uint64_t image_size;
> + uint64_t vp_offset;
> +};
> +
> +#define arm64_mem_ngv UINT64_MAX
> +struct arm64_mem arm64_mem;
> +
> +uint64_t get_phys_offset(void);
> +uint64_t get_vp_offset(void);
> +
> +static inline void reset_vp_offset(void)
> +{
> + arm64_mem.vp_offset = arm64_mem_ngv;
> +}
> +
> +static inline void set_phys_offset(uint64_t v)
> +{
> + if (arm64_mem.phys_offset == arm64_mem_ngv
> + || v < arm64_mem.phys_offset)
> + arm64_mem.phys_offset = v;
> +}
> +
> +int arm64_process_image_header(const struct arm64_image_header *h);
> +unsigned long arm64_locate_kernel_segment(struct kexec_info *info);
> +int arm64_load_other_segments(struct kexec_info *info,
> + unsigned long image_base);
> +
> +#endif
> diff --git a/kexec/arch/arm64/kexec-elf-arm64.c b/kexec/arch/arm64/kexec-elf-arm64.c
> new file mode 100644
> index 0000000..daf8bf0
> --- /dev/null
> +++ b/kexec/arch/arm64/kexec-elf-arm64.c
> @@ -0,0 +1,146 @@
> +/*
> + * ARM64 kexec elf support.
> + */
> +
> +#define _GNU_SOURCE
> +
> +#include <errno.h>
> +#include <limits.h>
> +#include <stdlib.h>
> +#include <linux/elf.h>
> +
> +#include "kexec-arm64.h"
> +#include "kexec-elf.h"
> +#include "kexec-syscall.h"
> +
> +int elf_arm64_probe(const char *kernel_buf, off_t kernel_size)
> +{
> + struct mem_ehdr ehdr;
> + int result;
> +
> + result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0);
> +
> + if (result < 0) {
> + dbgprintf("%s: Not an ELF executable.\n", __func__);
> + goto on_exit;
> + }
> +
> + if (ehdr.e_machine != EM_AARCH64) {
> + dbgprintf("%s: Not an AARCH64 ELF executable.\n", __func__);
> + result = -1;
> + goto on_exit;
> + }
> +
> + result = 0;
> +on_exit:
> + free_elf_info(&ehdr);
> + return result;
> +}
> +
> +int elf_arm64_load(int argc, char **argv, const char *kernel_buf,
> + off_t kernel_size, struct kexec_info *info)
> +{
> + const struct arm64_image_header *header = NULL;
> + unsigned long kernel_segment;
> + struct mem_ehdr ehdr;
> + int result;
> + int i;
> +
> + if (info->kexec_flags & KEXEC_ON_CRASH) {
> + fprintf(stderr, "kexec: kdump not yet supported on arm64\n");
> + return -EFAILED;
> + }
> +
> + result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0);
> +
> + if (result < 0) {
> + dbgprintf("%s: build_elf_exec_info failed\n", __func__);
> + goto exit;
> + }
> +
> + /* Find and process the arm64 image header. */
> +
> + for (i = 0; i < ehdr.e_phnum; i++) {
> + struct mem_phdr *phdr = &ehdr.e_phdr[i];
> + unsigned long header_offset;
> +
> + if (phdr->p_type != PT_LOAD)
> + continue;
> +
> + /*
> + * When CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET=y the image header
> + * could be offset in the elf segment. The linker script sets
> + * ehdr.e_entry to the start of text.
> + */
> +
> + header_offset = ehdr.e_entry - phdr->p_vaddr;
> +
> + header = (const struct arm64_image_header *)(
> + kernel_buf + phdr->p_offset + header_offset);
> +
> + if (!arm64_process_image_header(header)) {
> + dbgprintf("%s: e_entry: %016llx\n", __func__,
> + ehdr.e_entry);
> + dbgprintf("%s: p_vaddr: %016llx\n", __func__,
> + phdr->p_vaddr);
> + dbgprintf("%s: header_offset: %016lx\n", __func__,
> + header_offset);
> +
> + break;
> + }
> + }
> +
> + if (i == ehdr.e_phnum) {
> + dbgprintf("%s: Valid arm64 header not found\n", __func__);
> + result = -EFAILED;
> + goto exit;
> + }
> +
> + kernel_segment = arm64_locate_kernel_segment(info);
> +
> + if (kernel_segment == ULONG_MAX) {
> + dbgprintf("%s: Kernel segment is not allocated\n", __func__);
> + result = -EFAILED;
> + goto exit;
> + }
> +
> + arm64_mem.vp_offset = _ALIGN_DOWN(ehdr.e_entry, MiB(2));
> + 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__,
> + arm64_mem.text_offset);
> + dbgprintf("%s: image_size: %016lx\n", __func__,
> + arm64_mem.image_size);
> + dbgprintf("%s: phys_offset: %016lx\n", __func__,
> + arm64_mem.phys_offset);
> + dbgprintf("%s: vp_offset: %016lx\n", __func__,
> + arm64_mem.vp_offset);
> + dbgprintf("%s: PE format: %s\n", __func__,
> + (arm64_header_check_pe_sig(header) ? "yes" : "no"));
> +
> + /* load the kernel */
> + result = elf_exec_load(&ehdr, info);
> +
> + if (result) {
> + dbgprintf("%s: elf_exec_load failed\n", __func__);
> + goto exit;
> + }
> +
> + result = arm64_load_other_segments(info, kernel_segment
> + + arm64_mem.text_offset);
> +
> +exit:
> + reset_vp_offset();
> + free_elf_info(&ehdr);
> + if (result)
> + fprintf(stderr, "kexec: Bad elf image file, load failed.\n");
> + return result;
> +}
> +
> +void elf_arm64_usage(void)
> +{
> + printf(
> +" An ARM64 ELF image, big or little endian.\n"
> +" Typically vmlinux or a stripped version of vmlinux.\n\n");
> +}
> diff --git a/kexec/arch/arm64/kexec-image-arm64.c b/kexec/arch/arm64/kexec-image-arm64.c
> new file mode 100644
> index 0000000..42d2ea7
> --- /dev/null
> +++ b/kexec/arch/arm64/kexec-image-arm64.c
> @@ -0,0 +1,41 @@
> +/*
> + * ARM64 kexec binary image support.
> + */
> +
> +#define _GNU_SOURCE
> +#include "kexec-arm64.h"
> +
> +int image_arm64_probe(const char *kernel_buf, off_t kernel_size)
> +{
> + const struct arm64_image_header *h;
> +
> + if (kernel_size < sizeof(struct arm64_image_header)) {
> + dbgprintf("%s: No arm64 image header.\n", __func__);
> + return -1;
> + }
> +
> + h = (const struct arm64_image_header *)(kernel_buf);
> +
> + if (!arm64_header_check_magic(h)) {
> + dbgprintf("%s: Bad arm64 image header.\n", __func__);
> + return -1;
> + }
> +
> + fprintf(stderr, "kexec: ARM64 binary image files are currently NOT SUPPORTED.\n");
> + return -1;
> +}
> +
> +int image_arm64_load(int argc, char **argv, const char *kernel_buf,
> + off_t kernel_size, struct kexec_info *info)
> +{
> + return -1;
> +}
> +
> +void image_arm64_usage(void)
> +{
> + printf(
> +" An ARM64 binary image, compressed or not, big or little endian.\n"
> +" Typically an Image, Image.gz or Image.lzma file.\n\n");
> + printf(
> +" ARM64 binary image files are currently NOT SUPPORTED.\n\n");
> +}
> diff --git a/kexec/kexec-syscall.h b/kexec/kexec-syscall.h
> index ce2e20b..c0d0bea 100644
> --- a/kexec/kexec-syscall.h
> +++ b/kexec/kexec-syscall.h
> @@ -39,8 +39,8 @@
> #ifdef __s390__
> #define __NR_kexec_load 277
> #endif
> -#ifdef __arm__
> -#define __NR_kexec_load __NR_SYSCALL_BASE + 347
> +#if defined(__arm__) || defined(__arm64__)
> +#define __NR_kexec_load __NR_SYSCALL_BASE + 347
> #endif
> #if defined(__mips__)
> #define __NR_kexec_load 4311
> @@ -108,6 +108,7 @@ static inline long kexec_file_load(int kernel_fd, int initrd_fd,
> #define KEXEC_ARCH_PPC64 (21 << 16)
> #define KEXEC_ARCH_IA_64 (50 << 16)
> #define KEXEC_ARCH_ARM (40 << 16)
> +#define KEXEC_ARCH_ARM64 (183 << 16)
> #define KEXEC_ARCH_S390 (22 << 16)
> #define KEXEC_ARCH_SH (42 << 16)
> #define KEXEC_ARCH_MIPS_LE (10 << 16)
> @@ -153,5 +154,8 @@ static inline long kexec_file_load(int kernel_fd, int initrd_fd,
> #ifdef __m68k__
> #define KEXEC_ARCH_NATIVE KEXEC_ARCH_68K
> #endif
> +#if defined(__arm64__)
> +#define KEXEC_ARCH_NATIVE KEXEC_ARCH_ARM64
> +#endif
>
> #endif /* KEXEC_SYSCALL_H */
> diff --git a/purgatory/Makefile b/purgatory/Makefile
> index 2b5c061..ca0443c 100644
> --- a/purgatory/Makefile
> +++ b/purgatory/Makefile
> @@ -19,6 +19,7 @@ dist += purgatory/Makefile $(PURGATORY_SRCS) \
>
> include $(srcdir)/purgatory/arch/alpha/Makefile
> include $(srcdir)/purgatory/arch/arm/Makefile
> +include $(srcdir)/purgatory/arch/arm64/Makefile
> include $(srcdir)/purgatory/arch/i386/Makefile
> include $(srcdir)/purgatory/arch/ia64/Makefile
> include $(srcdir)/purgatory/arch/mips/Makefile
> diff --git a/purgatory/arch/arm64/Makefile b/purgatory/arch/arm64/Makefile
> new file mode 100644
> index 0000000..636abea
> --- /dev/null
> +++ b/purgatory/arch/arm64/Makefile
> @@ -0,0 +1,18 @@
> +
> +arm64_PURGATORY_EXTRA_CFLAGS = \
> + -mcmodel=large \
> + -fno-stack-protector \
> + -fno-asynchronous-unwind-tables \
> + -Wundef \
> + -Werror-implicit-function-declaration \
> + -Wdeclaration-after-statement \
> + -Werror=implicit-int \
> + -Werror=strict-prototypes
> +
> +arm64_PURGATORY_SRCS += \
> + purgatory/arch/arm64/entry.S \
> + purgatory/arch/arm64/purgatory-arm64.c
> +
> +dist += \
> + $(arm64_PURGATORY_SRCS) \
> + purgatory/arch/arm64/Makefile
> diff --git a/purgatory/arch/arm64/entry.S b/purgatory/arch/arm64/entry.S
> new file mode 100644
> index 0000000..adf16f4
> --- /dev/null
> +++ b/purgatory/arch/arm64/entry.S
> @@ -0,0 +1,51 @@
> +/*
> + * ARM64 purgatory.
> + */
> +
> +.macro size, sym:req
> + .size \sym, . - \sym
> +.endm
> +
> +.text
> +
> +.globl purgatory_start
> +purgatory_start:
> +
> + adr x19, .Lstack
> + mov sp, x19
> +
> + bl purgatory
> +
> + /* Start new image. */
> + ldr x17, arm64_kernel_entry
> + ldr x0, arm64_dtb_addr
> + mov x1, xzr
> + mov x2, xzr
> + mov x3, xzr
> + br x17
> +
> +size purgatory_start
> +
> +.ltorg
> +
> +.align 4
> + .rept 256
> + .quad 0
> + .endr
> +.Lstack:
> +
> +.data
> +
> +.align 3
> +
> +.globl arm64_kernel_entry
> +arm64_kernel_entry:
> + .quad 0
> +size arm64_kernel_entry
> +
> +.globl arm64_dtb_addr
> +arm64_dtb_addr:
> + .quad 0
> +size arm64_dtb_addr
> +
> +.end
> \ No newline at end of file
> diff --git a/purgatory/arch/arm64/purgatory-arm64.c b/purgatory/arch/arm64/purgatory-arm64.c
> new file mode 100644
> index 0000000..fe50fcf
> --- /dev/null
> +++ b/purgatory/arch/arm64/purgatory-arm64.c
> @@ -0,0 +1,19 @@
> +/*
> + * ARM64 purgatory.
> + */
> +
> +#include <stdint.h>
> +#include <purgatory.h>
> +
> +void putchar(int ch)
> +{
> + /* Nothing for now */
> +}
> +
> +void post_verification_setup_arch(void)
> +{
> +}
> +
> +void setup_arch(void)
> +{
> +}
> --
> 2.7.4
>
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v5 2/3] arm64: Add arm64 kexec support
@ 2016-09-06 5:44 ` AKASHI Takahiro
0 siblings, 0 replies; 14+ messages in thread
From: AKASHI Takahiro @ 2016-09-06 5:44 UTC (permalink / raw)
To: Geoff Levand
Cc: Pratyush Anand, Mark Rutland, Simon Horman, kexec, linux-arm-kernel
On Thu, Sep 01, 2016 at 09:51:10PM +0000, Geoff Levand wrote:
> Add kexec reboot support for ARM64 platforms.
>
> Signed-off-by: Geoff Levand <geoff@infradead.org>
> ---
> configure.ac | 3 +
> kexec/Makefile | 1 +
> kexec/arch/arm64/Makefile | 40 ++
> kexec/arch/arm64/crashdump-arm64.c | 21 +
> kexec/arch/arm64/crashdump-arm64.h | 12 +
> kexec/arch/arm64/image-header.h | 146 +++++++
> kexec/arch/arm64/include/arch/options.h | 39 ++
> kexec/arch/arm64/kexec-arm64.c | 717 ++++++++++++++++++++++++++++++++
> kexec/arch/arm64/kexec-arm64.h | 71 ++++
> kexec/arch/arm64/kexec-elf-arm64.c | 146 +++++++
> kexec/arch/arm64/kexec-image-arm64.c | 41 ++
> kexec/kexec-syscall.h | 8 +-
> purgatory/Makefile | 1 +
> purgatory/arch/arm64/Makefile | 18 +
> purgatory/arch/arm64/entry.S | 51 +++
> purgatory/arch/arm64/purgatory-arm64.c | 19 +
> 16 files changed, 1332 insertions(+), 2 deletions(-)
> create mode 100644 kexec/arch/arm64/Makefile
> create mode 100644 kexec/arch/arm64/crashdump-arm64.c
> create mode 100644 kexec/arch/arm64/crashdump-arm64.h
> create mode 100644 kexec/arch/arm64/image-header.h
> create mode 100644 kexec/arch/arm64/include/arch/options.h
> create mode 100644 kexec/arch/arm64/kexec-arm64.c
> create mode 100644 kexec/arch/arm64/kexec-arm64.h
> create mode 100644 kexec/arch/arm64/kexec-elf-arm64.c
> create mode 100644 kexec/arch/arm64/kexec-image-arm64.c
> create mode 100644 purgatory/arch/arm64/Makefile
> create mode 100644 purgatory/arch/arm64/entry.S
> create mode 100644 purgatory/arch/arm64/purgatory-arm64.c
>
> diff --git a/configure.ac b/configure.ac
> index 736da6e..ba89075 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -34,6 +34,9 @@ case $target_cpu in
> ARCH="ppc64"
> SUBARCH="LE"
> ;;
> + aarch64* )
> + ARCH="arm64"
> + ;;
> arm* )
> ARCH="arm"
> ;;
> diff --git a/kexec/Makefile b/kexec/Makefile
> index cc3f08b..39f365f 100644
> --- a/kexec/Makefile
> +++ b/kexec/Makefile
> @@ -79,6 +79,7 @@ KEXEC_SRCS += $($(ARCH)_DT_OPS)
>
> include $(srcdir)/kexec/arch/alpha/Makefile
> include $(srcdir)/kexec/arch/arm/Makefile
> +include $(srcdir)/kexec/arch/arm64/Makefile
> include $(srcdir)/kexec/arch/i386/Makefile
> include $(srcdir)/kexec/arch/ia64/Makefile
> include $(srcdir)/kexec/arch/m68k/Makefile
> diff --git a/kexec/arch/arm64/Makefile b/kexec/arch/arm64/Makefile
> new file mode 100644
> index 0000000..37414dc
> --- /dev/null
> +++ b/kexec/arch/arm64/Makefile
> @@ -0,0 +1,40 @@
> +
> +arm64_FS2DT += kexec/fs2dt.c
> +arm64_FS2DT_INCLUDE += -include $(srcdir)/kexec/arch/arm64/kexec-arm64.h \
> + -include $(srcdir)/kexec/arch/arm64/crashdump-arm64.h
> +
> +arm64_DT_OPS += kexec/dt-ops.c
> +
> +arm64_CPPFLAGS += -I $(srcdir)/kexec/
> +
> +arm64_KEXEC_SRCS += \
> + kexec/arch/arm64/kexec-arm64.c \
> + kexec/arch/arm64/kexec-image-arm64.c \
> + kexec/arch/arm64/kexec-elf-arm64.c \
> + kexec/arch/arm64/crashdump-arm64.c
> +
> +arm64_ARCH_REUSE_INITRD =
> +arm64_ADD_SEGMENT =
> +arm64_VIRT_TO_PHYS =
> +arm64_PHYS_TO_VIRT =
> +
> +dist += $(arm64_KEXEC_SRCS) \
> + kexec/arch/arm64/Makefile \
> + kexec/arch/arm64/kexec-arm64.h \
> + kexec/arch/arm64/crashdump-arm64.h
> +
> +ifdef HAVE_LIBFDT
> +
> +LIBS += -lfdt
> +
> +else
> +
> +include $(srcdir)/kexec/libfdt/Makefile.libfdt
> +
> +libfdt_SRCS += $(LIBFDT_SRCS:%=kexec/libfdt/%)
> +
> +arm64_CPPFLAGS += -I$(srcdir)/kexec/libfdt
> +
> +arm64_KEXEC_SRCS += $(libfdt_SRCS)
> +
> +endif
> diff --git a/kexec/arch/arm64/crashdump-arm64.c b/kexec/arch/arm64/crashdump-arm64.c
> new file mode 100644
> index 0000000..d2272c8
> --- /dev/null
> +++ b/kexec/arch/arm64/crashdump-arm64.c
> @@ -0,0 +1,21 @@
> +/*
> + * ARM64 crashdump.
> + */
> +
> +#define _GNU_SOURCE
> +
> +#include <errno.h>
> +#include <linux/elf.h>
> +
> +#include "kexec.h"
> +#include "crashdump.h"
> +#include "crashdump-arm64.h"
> +#include "kexec-arm64.h"
> +#include "kexec-elf.h"
> +
> +struct memory_ranges usablemem_rgns = {};
> +
> +int is_crashkernel_mem_reserved(void)
> +{
> + return 0;
> +}
> diff --git a/kexec/arch/arm64/crashdump-arm64.h b/kexec/arch/arm64/crashdump-arm64.h
> new file mode 100644
> index 0000000..f33c7a2
> --- /dev/null
> +++ b/kexec/arch/arm64/crashdump-arm64.h
> @@ -0,0 +1,12 @@
> +/*
> + * ARM64 crashdump.
> + */
> +
> +#if !defined(CRASHDUMP_ARM64_H)
> +#define CRASHDUMP_ARM64_H
> +
> +#include "kexec.h"
> +
> +extern struct memory_ranges usablemem_rgns;
> +
> +#endif
> diff --git a/kexec/arch/arm64/image-header.h b/kexec/arch/arm64/image-header.h
> new file mode 100644
> index 0000000..158d411
> --- /dev/null
> +++ b/kexec/arch/arm64/image-header.h
> @@ -0,0 +1,146 @@
> +/*
> + * ARM64 binary image header.
> + */
> +
> +#if !defined(__ARM64_IMAGE_HEADER_H)
> +#define __ARM64_IMAGE_HEADER_H
> +
> +#include <endian.h>
> +#include <stdint.h>
> +
> +/**
> + * struct arm64_image_header - arm64 kernel image header.
> + *
> + * @pe_sig: Optional PE format 'MZ' signature.
> + * @branch_code: Reserved for instructions to branch to stext.
> + * @text_offset: The image load offset in LSB byte order.
> + * @image_size: An estimated size of the memory image size in LSB byte order.
> + * @flags: Bit flags in LSB byte order:
> + * Bit 0: Image byte order: 1=MSB.
> + * Bit 1-2: Kernel page size: 1=4K, 2=16K, 3=64K.
> + * Bit 3: Image placement: 0=low.
> + * @reserved_1: Reserved.
> + * @magic: Magic number, "ARM\x64".
> + * @pe_header: Optional offset to a PE format header.
> + **/
> +
> +struct arm64_image_header {
> + uint8_t pe_sig[2];
> + uint16_t branch_code[3];
> + uint64_t text_offset;
> + uint64_t image_size;
> + uint64_t flags;
> + uint64_t reserved_1[3];
> + uint8_t magic[4];
> + uint32_t pe_header;
> +};
> +
> +static const uint8_t arm64_image_magic[4] = {'A', 'R', 'M', 0x64U};
> +static const uint8_t arm64_image_pe_sig[2] = {'M', 'Z'};
> +static const uint64_t arm64_image_flag_be = (1UL << 0);
> +static const uint64_t arm64_image_flag_page_size = (3UL << 1);
> +static const uint64_t arm64_image_flag_placement = (1UL << 3);
> +
> +/**
> + * enum arm64_header_page_size
> + */
> +
> +enum arm64_header_page_size {
> + arm64_header_page_size_invalid = 0,
> + arm64_header_page_size_4k,
> + arm64_header_page_size_16k,
> + arm64_header_page_size_64k
> +};
> +
> +/**
> + * arm64_header_check_magic - Helper to check the arm64 image header.
> + *
> + * Returns non-zero if header is OK.
> + */
> +
> +static inline int arm64_header_check_magic(const struct arm64_image_header *h)
> +{
> + if (!h)
> + return 0;
> +
> + return (h->magic[0] == arm64_image_magic[0]
> + && h->magic[1] == arm64_image_magic[1]
> + && h->magic[2] == arm64_image_magic[2]
> + && h->magic[3] == arm64_image_magic[3]);
> +}
> +
> +/**
> + * arm64_header_check_pe_sig - Helper to check the arm64 image header.
> + *
> + * Returns non-zero if 'MZ' signature is found.
> + */
> +
> +static inline int arm64_header_check_pe_sig(const struct arm64_image_header *h)
> +{
> + if (!h)
> + return 0;
> +
> + return (h->pe_sig[0] == arm64_image_pe_sig[0]
> + && h->pe_sig[1] == arm64_image_pe_sig[1]);
> +}
> +
> +/**
> + * arm64_header_check_msb - Helper to check the arm64 image header.
> + *
> + * Returns non-zero if the image was built as big endian.
> + */
> +
> +static inline int arm64_header_check_msb(const struct arm64_image_header *h)
> +{
> + if (!h)
> + return 0;
> +
> + return (le64toh(h->flags) & arm64_image_flag_be) >> 0;
> +}
> +
> +/**
> + * arm64_header_page_size
> + */
> +
> +static inline enum arm64_header_page_size arm64_header_page_size(
> + const struct arm64_image_header *h)
> +{
> + if (!h)
> + return 0;
> +
> + return (le64toh(h->flags) & arm64_image_flag_page_size) >> 1;
> +}
> +
> +/**
> + * arm64_header_placement
> + *
> + * Returns non-zero if the image has no physical placement restrictions.
> + */
> +
> +static inline int arm64_header_placement(const struct arm64_image_header *h)
> +{
> + if (!h)
> + return 0;
> +
> + return (le64toh(h->flags) & arm64_image_flag_placement) >> 3;
> +}
> +
> +static inline uint64_t arm64_header_text_offset(
> + const struct arm64_image_header *h)
> +{
> + if (!h)
> + return 0;
> +
> + return le64toh(h->text_offset);
> +}
> +
> +static inline uint64_t arm64_header_image_size(
> + const struct arm64_image_header *h)
> +{
> + if (!h)
> + return 0;
> +
> + return le64toh(h->image_size);
> +}
> +
> +#endif
> diff --git a/kexec/arch/arm64/include/arch/options.h b/kexec/arch/arm64/include/arch/options.h
> new file mode 100644
> index 0000000..a17d933
> --- /dev/null
> +++ b/kexec/arch/arm64/include/arch/options.h
> @@ -0,0 +1,39 @@
> +#if !defined(KEXEC_ARCH_ARM64_OPTIONS_H)
> +#define KEXEC_ARCH_ARM64_OPTIONS_H
> +
> +#define OPT_APPEND ((OPT_MAX)+0)
> +#define OPT_DTB ((OPT_MAX)+1)
> +#define OPT_INITRD ((OPT_MAX)+2)
> +#define OPT_REUSE_CMDLINE ((OPT_MAX)+3)
> +#define OPT_ARCH_MAX ((OPT_MAX)+4)
> +
> +#define KEXEC_ARCH_OPTIONS \
> + KEXEC_OPTIONS \
> + { "append", 1, NULL, OPT_APPEND }, \
> + { "command-line", 1, NULL, OPT_APPEND }, \
> + { "dtb", 1, NULL, OPT_DTB }, \
> + { "initrd", 1, NULL, OPT_INITRD }, \
> + { "ramdisk", 1, NULL, OPT_INITRD }, \
> + { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE }, \
> +
> +#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR /* Only accept long arch options. */
> +#define KEXEC_ALL_OPTIONS KEXEC_ARCH_OPTIONS
> +#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR
> +
> +static const char arm64_opts_usage[] __attribute__ ((unused)) =
> +" --append=STRING Set the kernel command line to STRING.\n"
> +" --command-line=STRING Set the kernel command line to STRING.\n"
> +" --dtb=FILE Use FILE as the device tree blob.\n"
> +" --initrd=FILE Use FILE as the kernel initial ramdisk.\n"
> +" --ramdisk=FILE Use FILE as the kernel initial ramdisk.\n"
> +" --reuse-cmdline Use kernel command line from running system.\n";
> +
> +struct arm64_opts {
> + const char *command_line;
> + const char *dtb;
> + const char *initrd;
> +};
> +
> +extern struct arm64_opts arm64_opts;
> +
> +#endif
> diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c
> new file mode 100644
> index 0000000..7183dac
> --- /dev/null
> +++ b/kexec/arch/arm64/kexec-arm64.c
> @@ -0,0 +1,717 @@
> +/*
> + * ARM64 kexec.
> + */
> +
> +#define _GNU_SOURCE
> +
> +#include <assert.h>
> +#include <errno.h>
> +#include <getopt.h>
> +#include <inttypes.h>
> +#include <libfdt.h>
> +#include <limits.h>
> +#include <stdlib.h>
> +#include <sys/stat.h>
> +#include <linux/elf-em.h>
> +#include <elf.h>
> +
> +#include "kexec.h"
> +#include "kexec-arm64.h"
> +#include "crashdump.h"
> +#include "crashdump-arm64.h"
> +#include "dt-ops.h"
> +#include "fs2dt.h"
> +#include "kexec-syscall.h"
> +#include "arch/options.h"
> +
> +/* Global varables the core kexec routines expect. */
> +
> +unsigned char reuse_initrd;
> +
> +off_t initrd_base;
> +off_t initrd_size;
> +
> +const struct arch_map_entry arches[] = {
> + { "aarch64", KEXEC_ARCH_ARM64 },
> + { "aarch64_be", KEXEC_ARCH_ARM64 },
> + { NULL, 0 },
> +};
> +
> +struct file_type file_type[] = {
> + {"vmlinux", elf_arm64_probe, elf_arm64_load, elf_arm64_usage},
> + {"Image", image_arm64_probe, image_arm64_load, image_arm64_usage},
> +};
> +
> +int file_types = sizeof(file_type) / sizeof(file_type[0]);
> +
> +/* arm64 global varables. */
> +
> +struct arm64_opts arm64_opts;
> +struct arm64_mem arm64_mem = {
> + .phys_offset = arm64_mem_ngv,
> + .vp_offset = arm64_mem_ngv,
> +};
> +
> +uint64_t get_phys_offset(void)
> +{
> + assert(arm64_mem.phys_offset != arm64_mem_ngv);
> + return arm64_mem.phys_offset;
> +}
> +
> +uint64_t get_vp_offset(void)
> +{
> + assert(arm64_mem.vp_offset != arm64_mem_ngv);
> + return arm64_mem.vp_offset;
> +}
> +
> +/**
> + * arm64_process_image_header - Process the arm64 image header.
> + *
> + * Make a guess that KERNEL_IMAGE_SIZE will be enough for older kernels.
> + */
> +
> +int arm64_process_image_header(const struct arm64_image_header *h)
> +{
> +#if !defined(KERNEL_IMAGE_SIZE)
> +# define KERNEL_IMAGE_SIZE MiB(16)
> +#endif
> +
> + if (!arm64_header_check_magic(h))
> + return -EFAILED;
> +
> + if (h->image_size) {
> + arm64_mem.text_offset = arm64_header_text_offset(h);
> + arm64_mem.image_size = arm64_header_image_size(h);
> + } else {
> + /* For 3.16 and older kernels. */
> + arm64_mem.text_offset = 0x80000;
> + arm64_mem.image_size = KERNEL_IMAGE_SIZE;
> + fprintf(stderr,
> + "kexec: %s: Warning: Kernel image size set to %lu MiB.\n"
> + " Please verify compatability with lodaed kernel.\n",
> + __func__, KERNEL_IMAGE_SIZE / 1024UL / 1024UL);
> + }
> +
> + return 0;
> +}
> +
> +void arch_usage(void)
> +{
> + printf(arm64_opts_usage);
> +}
> +
> +int arch_process_options(int argc, char **argv)
> +{
> + static const char short_options[] = KEXEC_OPT_STR "";
> + static const struct option options[] = {
> + KEXEC_ARCH_OPTIONS
> + { 0 }
> + };
> + int opt;
> + char *cmdline = NULL;
> + const char *append = NULL;
> +
> + for (opt = 0; opt != -1; ) {
> + opt = getopt_long(argc, argv, short_options, options, 0);
> +
> + switch (opt) {
> + case OPT_APPEND:
> + append = optarg;
> + break;
> + case OPT_REUSE_CMDLINE:
> + cmdline = get_command_line();
> + break;
> + case OPT_DTB:
> + arm64_opts.dtb = optarg;
> + break;
> + case OPT_INITRD:
> + arm64_opts.initrd = optarg;
> + break;
> + case OPT_PANIC:
> + die("load-panic (-p) not supported");
> + break;
> + default:
> + break; /* Ignore core and unknown options. */
> + }
> + }
> +
> + arm64_opts.command_line = concat_cmdline(cmdline, append);
> +
> + dbgprintf("%s:%d: command_line: %s\n", __func__, __LINE__,
> + arm64_opts.command_line);
> + dbgprintf("%s:%d: initrd: %s\n", __func__, __LINE__,
> + arm64_opts.initrd);
> + dbgprintf("%s:%d: dtb: %s\n", __func__, __LINE__, arm64_opts.dtb);
> +
> + return 0;
> +}
> +
> +/**
> + * struct dtb - Info about a binary device tree.
> + *
> + * @buf: Device tree data.
> + * @size: Device tree data size.
> + * @name: Shorthand name of this dtb for messages.
> + * @path: Filesystem path.
> + */
> +
> +struct dtb {
> + char *buf;
> + off_t size;
> + const char *name;
> + const char *path;
> +};
> +
> +/**
> + * dump_reservemap - Dump the dtb's reservemap.
> + */
> +
> +static void dump_reservemap(const struct dtb *dtb)
> +{
> + int i;
> +
> + for (i = 0; ; i++) {
> + uint64_t address;
> + uint64_t size;
> +
> + fdt_get_mem_rsv(dtb->buf, i, &address, &size);
> +
> + if (!size)
> + break;
> +
> + dbgprintf("%s: %s {%" PRIx64 ", %" PRIx64 "}\n", __func__,
> + dtb->name, address, size);
> + }
> +}
> +
> +/**
> + * set_bootargs - Set the dtb's bootargs.
> + */
> +
> +static int set_bootargs(struct dtb *dtb, const char *command_line)
> +{
> + int result;
> +
> + if (!command_line || !command_line[0])
> + return 0;
> +
> + result = dtb_set_bootargs(&dtb->buf, &dtb->size, command_line);
> +
> + if (result) {
> + fprintf(stderr,
> + "kexec: Set device tree bootargs failed.\n");
> + return -EFAILED;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * read_proc_dtb - Read /proc/device-tree.
> + */
> +
> +static int read_proc_dtb(struct dtb *dtb)
> +{
> + int result;
> + struct stat s;
> + static const char path[] = "/proc/device-tree";
> +
> + result = stat(path, &s);
> +
> + if (result) {
> + dbgprintf("%s: %s\n", __func__, strerror(errno));
> + return -EFAILED;
> + }
> +
> + dtb->path = path;
> + create_flatten_tree((char **)&dtb->buf, &dtb->size, NULL);
> +
> + return 0;
> +}
> +
> +/**
> + * read_sys_dtb - Read /sys/firmware/fdt.
> + */
> +
> +static int read_sys_dtb(struct dtb *dtb)
> +{
> + int result;
> + struct stat s;
> + static const char path[] = "/sys/firmware/fdt";
> +
> + result = stat(path, &s);
> +
> + if (result) {
> + dbgprintf("%s: %s\n", __func__, strerror(errno));
> + return -EFAILED;
> + }
> +
> + dtb->path = path;
> + dtb->buf = slurp_file(path, &dtb->size);
> +
> + return 0;
> +}
> +
> +/**
> + * read_1st_dtb - Read the 1st stage kernel's dtb.
> + */
> +
> +static int read_1st_dtb(struct dtb *dtb)
> +{
> + int result;
> +
> + result = read_sys_dtb(dtb);
> +
> + if (!result)
> + goto on_success;
> +
> + result = read_proc_dtb(dtb);
> +
> + if (!result)
> + goto on_success;
> +
> + dbgprintf("%s: not found\n", __func__);
> + return -EFAILED;
> +
> +on_success:
> + dbgprintf("%s: found %s\n", __func__, dtb->path);
> + return 0;
> +}
> +
> +/**
> + * setup_2nd_dtb - Setup the 2nd stage kernel's dtb.
> + */
> +
> +static int setup_2nd_dtb(struct dtb *dtb, char *command_line)
> +{
> + int result;
> +
> + result = fdt_check_header(dtb->buf);
> +
> + if (result) {
> + fprintf(stderr, "kexec: Invalid 2nd device tree.\n");
> + return -EFAILED;
> + }
> +
> + result = set_bootargs(dtb, command_line);
> +
> + dump_reservemap(dtb);
> +
> + return result;
> +}
> +
> +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 (hole == ULONG_MAX)
> + dbgprintf("%s: locate_hole failed\n", __func__);
> +
> + return hole;
> +}
> +
> +/**
> + * arm64_load_other_segments - Prepare the dtb, initrd and purgatory segments.
> + */
> +
> +int arm64_load_other_segments(struct kexec_info *info,
> + unsigned long image_base)
> +{
> + int result;
> + unsigned long dtb_base;
> + unsigned long hole_min;
> + unsigned long hole_max;
> + char *initrd_buf = NULL;
> + struct dtb dtb;
> + char command_line[COMMAND_LINE_SIZE] = "";
> +
> + if (arm64_opts.command_line) {
> + strncpy(command_line, arm64_opts.command_line,
> + sizeof(command_line));
> + command_line[sizeof(command_line) - 1] = 0;
> + }
> +
> + if (arm64_opts.dtb) {
> + dtb.name = "dtb_2";
"dtb_1" and "dtb_2" are not very meaningful.
Instead, I'm going to use:
dtb_2 => "dtb_user" (if a command line option is specified)
dtb_1 => "dtb_sys" (if from /sys/firmware/fdt), or
"dtb_unflatten" (if created from /proc/device-tree)
to distinguish the origins in my kdump patches.
> + dtb.buf = slurp_file(arm64_opts.dtb, &dtb.size);
> + } else {
> + dtb.name = "dtb_1";
> + result = read_1st_dtb(&dtb);
> +
> + if (result) {
> + fprintf(stderr,
> + "kexec: Error: No device tree available.\n");
> + return -EFAILED;
> + }
> + }
> +
> + result = setup_2nd_dtb(&dtb, command_line);
> +
> + if (result)
> + return -EFAILED;
> +
> + /* Put the other segments after the image. */
> +
> + hole_min = image_base + arm64_mem.image_size;
> + hole_max = ULONG_MAX;
> +
> + if (arm64_opts.initrd) {
> + initrd_buf = slurp_file(arm64_opts.initrd, &initrd_size);
> +
> + if (!initrd_buf)
> + fprintf(stderr, "kexec: Empty ramdisk file.\n");
> + else {
> + /*
> + * Put the initrd after the kernel. As specified in
> + * booting.txt, align to 1 GiB.
> + */
> +
> + initrd_base = add_buffer_phys_virt(info, initrd_buf,
> + initrd_size, initrd_size, GiB(1),
> + hole_min, hole_max, 1, 0);
> +
> + /* initrd_base is valid if we got here. */
> +
> + dbgprintf("initrd: base %lx, size %lxh (%ld)\n",
> + initrd_base, initrd_size, initrd_size);
> +
> + /* Check size limit as specified in booting.txt. */
> +
> + if (initrd_base - image_base + initrd_size > GiB(32)) {
> + fprintf(stderr, "kexec: Error: image + initrd too big.\n");
> + return -EFAILED;
> + }
> +
> + result = dtb_set_initrd((char **)&dtb.buf,
> + &dtb.size, initrd_base,
> + initrd_base + initrd_size);
> +
> + if (result)
> + return -EFAILED;
> + }
> + }
> +
> + /* Check size limit as specified in booting.txt. */
> +
> + if (dtb.size > MiB(2)) {
> + fprintf(stderr, "kexec: Error: dtb too big.\n");
> + return -EFAILED;
> + }
> +
> + dtb_base = add_buffer_phys_virt(info, dtb.buf, dtb.size, dtb.size,
> + 0, hole_min, hole_max, 1, 0);
> +
> + /* dtb_base is valid if we got here. */
> +
> + dbgprintf("dtb: base %lx, size %lxh (%ld)\n", dtb_base, dtb.size,
> + dtb.size);
> +
> + elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
> + hole_min, hole_max, 1, 0);
> +
> + info->entry = (void *)elf_rel_get_addr(&info->rhdr, "purgatory_start");
> +
> + elf_rel_set_symbol(&info->rhdr, "arm64_kernel_entry", &image_base,
> + sizeof(image_base));
> +
> + elf_rel_set_symbol(&info->rhdr, "arm64_dtb_addr", &dtb_base,
> + sizeof(dtb_base));
> +
> + return 0;
> +}
> +
> +/**
> + * virt_to_phys - For processing elf file values.
> + */
> +
> +unsigned long virt_to_phys(unsigned long v)
> +{
> + unsigned long p;
> +
> + p = v - get_vp_offset() + get_phys_offset();
> +
> + return p;
> +}
> +
> +/**
> + * phys_to_virt - For crashdump setup.
> + */
> +
> +unsigned long phys_to_virt(struct crash_elf_info *elf_info,
> + unsigned long long p)
> +{
> + unsigned long v;
> +
> + v = p - get_phys_offset() + elf_info->page_offset;
> +
> + return v;
> +}
> +
> +/**
> + * add_segment - Use virt_to_phys when loading elf files.
> + */
> +
> +void add_segment(struct kexec_info *info, const void *buf, size_t bufsz,
> + unsigned long base, size_t memsz)
> +{
> + add_segment_phys_virt(info, buf, bufsz, base, memsz, 1);
> +}
> +
> +/**
> + * get_memory_ranges_iomem_cb - Helper for get_memory_ranges_iomem.
> + */
> +
> +static int get_memory_ranges_iomem_cb(void *data, int nr, char *str,
> + unsigned long long base, unsigned long long length)
> +{
> + struct memory_range *r;
> +
> + if (nr >= KEXEC_SEGMENT_MAX)
> + return -1;
> +
> + r = (struct memory_range *)data + nr;
> + r->type = RANGE_RAM;
> + r->start = base;
> + r->end = base + length - 1;
> +
> + set_phys_offset(r->start);
This will no longer work correctly for identifying PHYS_OFFSET
once the following patch is applied:
http://lists.infradead.org/pipermail/linux-arm-kernel/2016-August/450433.html
and it is now queued in arm64/for-next.
I will include a fixup in my kdump patches as the current code of kexec
doesn't rely on a result of virt_to_phys() seriously.
> +
> + dbgprintf("%s: %016llx - %016llx : %s", __func__, r->start,
> + r->end, str);
> +
> + return 0;
> +}
> +
> +/**
> + * get_memory_ranges_iomem - Try to get the memory ranges from /proc/iomem.
> + */
> +
> +static int get_memory_ranges_iomem(struct memory_range *array,
> + unsigned int *count)
> +{
> + *count = kexec_iomem_for_each_line("System RAM\n",
> + get_memory_ranges_iomem_cb, array);
> +
> + if (!*count) {
> + dbgprintf("%s: failed: No RAM found.\n", __func__);
> + return -EFAILED;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * get_memory_ranges_dt - Try to get the memory ranges from the 1st stage dtb.
> + */
> +
> +static int get_memory_ranges_dt(struct memory_range *array, unsigned int *count)
> +{
> + struct region {uint64_t base; uint64_t size;};
> + struct dtb dtb = {.name = "range_dtb"};
> + int offset;
> + int result;
> +
> + *count = 0;
> +
> + result = read_1st_dtb(&dtb);
> +
> + if (result) {
> + goto on_error;
> + }
> +
> + result = fdt_check_header(dtb.buf);
> +
> + if (result) {
> + dbgprintf("%s:%d: %s: fdt_check_header failed:%s\n", __func__,
> + __LINE__, dtb.path, fdt_strerror(result));
> + goto on_error;
> + }
> +
> + for (offset = 0; ; ) {
> + const struct region *region;
> + const struct region *end;
> + int len;
> +
> + offset = fdt_subnode_offset(dtb.buf, offset, "memory");
> +
> + if (offset == -FDT_ERR_NOTFOUND)
> + break;
> +
> + if (offset <= 0) {
> + dbgprintf("%s:%d: fdt_subnode_offset failed: %d %s\n",
> + __func__, __LINE__, offset,
> + fdt_strerror(offset));
> + goto on_error;
> + }
> +
> + dbgprintf("%s:%d: node_%d %s\n", __func__, __LINE__, offset,
> + fdt_get_name(dtb.buf, offset, NULL));
> +
> + region = fdt_getprop(dtb.buf, offset, "reg", &len);
> +
> + if (region <= 0) {
> + dbgprintf("%s:%d: fdt_getprop failed: %d %s\n",
> + __func__, __LINE__, offset,
> + fdt_strerror(offset));
> + goto on_error;
> + }
> +
> + for (end = region + len / sizeof(*region);
> + region < end && *count < KEXEC_SEGMENT_MAX;
> + region++) {
> + struct memory_range r;
> +
> + r.type = RANGE_RAM;
> + r.start = fdt64_to_cpu(region->base);
> + r.end = r.start + fdt64_to_cpu(region->size) - 1;
> +
> + if (!region->size) {
> + dbgprintf("%s:%d: SKIP: %016llx - %016llx\n",
> + __func__, __LINE__, r.start, r.end);
> + continue;
> + }
> +
> + dbgprintf("%s:%d: RAM: %016llx - %016llx\n", __func__,
> + __LINE__, r.start, r.end);
> +
> + array[(*count)++] = r;
> +
> + set_phys_offset(r.start);
> + }
> + }
> +
> + if (!*count) {
> + dbgprintf("%s:%d: %s: No RAM found.\n", __func__, __LINE__,
> + dtb.path);
> + goto on_error;
> + }
> +
> + dbgprintf("%s:%d: %s: Success\n", __func__, __LINE__, dtb.path);
> + result = 0;
> + goto on_exit;
> +
> +on_error:
> + fprintf(stderr, "%s:%d: %s: Unusable device-tree file\n", __func__,
> + __LINE__, dtb.path);
> + result = -EFAILED;
> +
> +on_exit:
> + free(dtb.buf);
> + return result;
> +}
> +
> +/**
> + * get_memory_ranges - Try to get the memory ranges some how.
> + */
> +
> +int get_memory_ranges(struct memory_range **range, int *ranges,
> + unsigned long kexec_flags)
> +{
> + static struct memory_range array[KEXEC_SEGMENT_MAX];
> + unsigned int count;
> + int result;
> +
> + result = get_memory_ranges_iomem(array, &count);
> +
> + if (result)
> + result = get_memory_ranges_dt(array, &count);
Do we really need to scan a DT blob here?
I think that all the (usable) memory regions are added to
/proc/iomem anyway.
> +
> + *range = result ? NULL : array;
> + *ranges = result ? 0 : count;
> +
> + return result;
> +}
> +
> +int arch_compat_trampoline(struct kexec_info *info)
> +{
> + return 0;
> +}
> +
> +int machine_verify_elf_rel(struct mem_ehdr *ehdr)
> +{
> + return (ehdr->e_machine == EM_AARCH64);
> +}
> +
> +void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
> + unsigned long r_type, void *ptr, unsigned long address,
> + unsigned long value)
> +{
> +#if !defined(R_AARCH64_ABS64)
> +# define R_AARCH64_ABS64 257
> +#endif
> +
> +#if !defined(R_AARCH64_LD_PREL_LO19)
> +# define R_AARCH64_LD_PREL_LO19 273
> +#endif
> +
> +#if !defined(R_AARCH64_ADR_PREL_LO21)
> +# define R_AARCH64_ADR_PREL_LO21 274
> +#endif
> +
> +#if !defined(R_AARCH64_JUMP26)
> +# define R_AARCH64_JUMP26 282
> +#endif
> +
> +#if !defined(R_AARCH64_CALL26)
> +# define R_AARCH64_CALL26 283
> +#endif
Did you see my comment?
http://lists.infradead.org/pipermail/kexec/2016-August/016947.html
-Takahiro AKASHI
> +
> + uint64_t *loc64;
> + uint32_t *loc32;
> + uint64_t *location = (uint64_t *)ptr;
> + uint64_t data = *location;
> + const char *type = NULL;
> +
> + switch(r_type) {
> + case R_AARCH64_ABS64:
> + type = "ABS64";
> + loc64 = ptr;
> + *loc64 = cpu_to_elf64(ehdr, elf64_to_cpu(ehdr, *loc64) + value);
> + break;
> + case R_AARCH64_LD_PREL_LO19:
> + type = "LD_PREL_LO19";
> + loc32 = ptr;
> + *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
> + + (((value - address) << 3) & 0xffffe0));
> + break;
> + case R_AARCH64_ADR_PREL_LO21:
> + if (value & 3)
> + die("%s: ERROR Unaligned value: %lx\n", __func__,
> + value);
> + type = "ADR_PREL_LO21";
> + loc32 = ptr;
> + *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
> + + (((value - address) << 3) & 0xffffe0));
> + break;
> + case R_AARCH64_JUMP26:
> + type = "JUMP26";
> + loc32 = ptr;
> + *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
> + + (((value - address) >> 2) & 0x3ffffff));
> + break;
> + case R_AARCH64_CALL26:
> + type = "CALL26";
> + loc32 = ptr;
> + *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
> + + (((value - address) >> 2) & 0x3ffffff));
> + break;
> + default:
> + die("%s: ERROR Unknown type: %lu\n", __func__, r_type);
> + break;
> + }
> +
> + dbgprintf("%s: %s %016lx->%016lx\n", __func__, type, data, *location);
> +}
> +
> +void arch_reuse_initrd(void)
> +{
> + reuse_initrd = 1;
> +}
> +
> +void arch_update_purgatory(struct kexec_info *UNUSED(info))
> +{
> +}
> diff --git a/kexec/arch/arm64/kexec-arm64.h b/kexec/arch/arm64/kexec-arm64.h
> new file mode 100644
> index 0000000..bac62f8
> --- /dev/null
> +++ b/kexec/arch/arm64/kexec-arm64.h
> @@ -0,0 +1,71 @@
> +/*
> + * ARM64 kexec.
> + */
> +
> +#if !defined(KEXEC_ARM64_H)
> +#define KEXEC_ARM64_H
> +
> +#include <stdbool.h>
> +#include <sys/types.h>
> +
> +#include "image-header.h"
> +#include "kexec.h"
> +
> +#define KEXEC_SEGMENT_MAX 16
> +
> +#define BOOT_BLOCK_VERSION 17
> +#define BOOT_BLOCK_LAST_COMP_VERSION 16
> +#define COMMAND_LINE_SIZE 512
> +
> +#define KiB(x) ((x) * 1024UL)
> +#define MiB(x) (KiB(x) * 1024UL)
> +#define GiB(x) (MiB(x) * 1024UL)
> +
> +int elf_arm64_probe(const char *kernel_buf, off_t kernel_size);
> +int elf_arm64_load(int argc, char **argv, const char *kernel_buf,
> + off_t kernel_size, struct kexec_info *info);
> +void elf_arm64_usage(void);
> +
> +int image_arm64_probe(const char *kernel_buf, off_t kernel_size);
> +int image_arm64_load(int argc, char **argv, const char *kernel_buf,
> + off_t kernel_size, struct kexec_info *info);
> +void image_arm64_usage(void);
> +
> +off_t initrd_base;
> +off_t initrd_size;
> +
> +/**
> + * struct arm64_mem - Memory layout info.
> + */
> +
> +struct arm64_mem {
> + uint64_t phys_offset;
> + uint64_t text_offset;
> + uint64_t image_size;
> + uint64_t vp_offset;
> +};
> +
> +#define arm64_mem_ngv UINT64_MAX
> +struct arm64_mem arm64_mem;
> +
> +uint64_t get_phys_offset(void);
> +uint64_t get_vp_offset(void);
> +
> +static inline void reset_vp_offset(void)
> +{
> + arm64_mem.vp_offset = arm64_mem_ngv;
> +}
> +
> +static inline void set_phys_offset(uint64_t v)
> +{
> + if (arm64_mem.phys_offset == arm64_mem_ngv
> + || v < arm64_mem.phys_offset)
> + arm64_mem.phys_offset = v;
> +}
> +
> +int arm64_process_image_header(const struct arm64_image_header *h);
> +unsigned long arm64_locate_kernel_segment(struct kexec_info *info);
> +int arm64_load_other_segments(struct kexec_info *info,
> + unsigned long image_base);
> +
> +#endif
> diff --git a/kexec/arch/arm64/kexec-elf-arm64.c b/kexec/arch/arm64/kexec-elf-arm64.c
> new file mode 100644
> index 0000000..daf8bf0
> --- /dev/null
> +++ b/kexec/arch/arm64/kexec-elf-arm64.c
> @@ -0,0 +1,146 @@
> +/*
> + * ARM64 kexec elf support.
> + */
> +
> +#define _GNU_SOURCE
> +
> +#include <errno.h>
> +#include <limits.h>
> +#include <stdlib.h>
> +#include <linux/elf.h>
> +
> +#include "kexec-arm64.h"
> +#include "kexec-elf.h"
> +#include "kexec-syscall.h"
> +
> +int elf_arm64_probe(const char *kernel_buf, off_t kernel_size)
> +{
> + struct mem_ehdr ehdr;
> + int result;
> +
> + result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0);
> +
> + if (result < 0) {
> + dbgprintf("%s: Not an ELF executable.\n", __func__);
> + goto on_exit;
> + }
> +
> + if (ehdr.e_machine != EM_AARCH64) {
> + dbgprintf("%s: Not an AARCH64 ELF executable.\n", __func__);
> + result = -1;
> + goto on_exit;
> + }
> +
> + result = 0;
> +on_exit:
> + free_elf_info(&ehdr);
> + return result;
> +}
> +
> +int elf_arm64_load(int argc, char **argv, const char *kernel_buf,
> + off_t kernel_size, struct kexec_info *info)
> +{
> + const struct arm64_image_header *header = NULL;
> + unsigned long kernel_segment;
> + struct mem_ehdr ehdr;
> + int result;
> + int i;
> +
> + if (info->kexec_flags & KEXEC_ON_CRASH) {
> + fprintf(stderr, "kexec: kdump not yet supported on arm64\n");
> + return -EFAILED;
> + }
> +
> + result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0);
> +
> + if (result < 0) {
> + dbgprintf("%s: build_elf_exec_info failed\n", __func__);
> + goto exit;
> + }
> +
> + /* Find and process the arm64 image header. */
> +
> + for (i = 0; i < ehdr.e_phnum; i++) {
> + struct mem_phdr *phdr = &ehdr.e_phdr[i];
> + unsigned long header_offset;
> +
> + if (phdr->p_type != PT_LOAD)
> + continue;
> +
> + /*
> + * When CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET=y the image header
> + * could be offset in the elf segment. The linker script sets
> + * ehdr.e_entry to the start of text.
> + */
> +
> + header_offset = ehdr.e_entry - phdr->p_vaddr;
> +
> + header = (const struct arm64_image_header *)(
> + kernel_buf + phdr->p_offset + header_offset);
> +
> + if (!arm64_process_image_header(header)) {
> + dbgprintf("%s: e_entry: %016llx\n", __func__,
> + ehdr.e_entry);
> + dbgprintf("%s: p_vaddr: %016llx\n", __func__,
> + phdr->p_vaddr);
> + dbgprintf("%s: header_offset: %016lx\n", __func__,
> + header_offset);
> +
> + break;
> + }
> + }
> +
> + if (i == ehdr.e_phnum) {
> + dbgprintf("%s: Valid arm64 header not found\n", __func__);
> + result = -EFAILED;
> + goto exit;
> + }
> +
> + kernel_segment = arm64_locate_kernel_segment(info);
> +
> + if (kernel_segment == ULONG_MAX) {
> + dbgprintf("%s: Kernel segment is not allocated\n", __func__);
> + result = -EFAILED;
> + goto exit;
> + }
> +
> + arm64_mem.vp_offset = _ALIGN_DOWN(ehdr.e_entry, MiB(2));
> + 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__,
> + arm64_mem.text_offset);
> + dbgprintf("%s: image_size: %016lx\n", __func__,
> + arm64_mem.image_size);
> + dbgprintf("%s: phys_offset: %016lx\n", __func__,
> + arm64_mem.phys_offset);
> + dbgprintf("%s: vp_offset: %016lx\n", __func__,
> + arm64_mem.vp_offset);
> + dbgprintf("%s: PE format: %s\n", __func__,
> + (arm64_header_check_pe_sig(header) ? "yes" : "no"));
> +
> + /* load the kernel */
> + result = elf_exec_load(&ehdr, info);
> +
> + if (result) {
> + dbgprintf("%s: elf_exec_load failed\n", __func__);
> + goto exit;
> + }
> +
> + result = arm64_load_other_segments(info, kernel_segment
> + + arm64_mem.text_offset);
> +
> +exit:
> + reset_vp_offset();
> + free_elf_info(&ehdr);
> + if (result)
> + fprintf(stderr, "kexec: Bad elf image file, load failed.\n");
> + return result;
> +}
> +
> +void elf_arm64_usage(void)
> +{
> + printf(
> +" An ARM64 ELF image, big or little endian.\n"
> +" Typically vmlinux or a stripped version of vmlinux.\n\n");
> +}
> diff --git a/kexec/arch/arm64/kexec-image-arm64.c b/kexec/arch/arm64/kexec-image-arm64.c
> new file mode 100644
> index 0000000..42d2ea7
> --- /dev/null
> +++ b/kexec/arch/arm64/kexec-image-arm64.c
> @@ -0,0 +1,41 @@
> +/*
> + * ARM64 kexec binary image support.
> + */
> +
> +#define _GNU_SOURCE
> +#include "kexec-arm64.h"
> +
> +int image_arm64_probe(const char *kernel_buf, off_t kernel_size)
> +{
> + const struct arm64_image_header *h;
> +
> + if (kernel_size < sizeof(struct arm64_image_header)) {
> + dbgprintf("%s: No arm64 image header.\n", __func__);
> + return -1;
> + }
> +
> + h = (const struct arm64_image_header *)(kernel_buf);
> +
> + if (!arm64_header_check_magic(h)) {
> + dbgprintf("%s: Bad arm64 image header.\n", __func__);
> + return -1;
> + }
> +
> + fprintf(stderr, "kexec: ARM64 binary image files are currently NOT SUPPORTED.\n");
> + return -1;
> +}
> +
> +int image_arm64_load(int argc, char **argv, const char *kernel_buf,
> + off_t kernel_size, struct kexec_info *info)
> +{
> + return -1;
> +}
> +
> +void image_arm64_usage(void)
> +{
> + printf(
> +" An ARM64 binary image, compressed or not, big or little endian.\n"
> +" Typically an Image, Image.gz or Image.lzma file.\n\n");
> + printf(
> +" ARM64 binary image files are currently NOT SUPPORTED.\n\n");
> +}
> diff --git a/kexec/kexec-syscall.h b/kexec/kexec-syscall.h
> index ce2e20b..c0d0bea 100644
> --- a/kexec/kexec-syscall.h
> +++ b/kexec/kexec-syscall.h
> @@ -39,8 +39,8 @@
> #ifdef __s390__
> #define __NR_kexec_load 277
> #endif
> -#ifdef __arm__
> -#define __NR_kexec_load __NR_SYSCALL_BASE + 347
> +#if defined(__arm__) || defined(__arm64__)
> +#define __NR_kexec_load __NR_SYSCALL_BASE + 347
> #endif
> #if defined(__mips__)
> #define __NR_kexec_load 4311
> @@ -108,6 +108,7 @@ static inline long kexec_file_load(int kernel_fd, int initrd_fd,
> #define KEXEC_ARCH_PPC64 (21 << 16)
> #define KEXEC_ARCH_IA_64 (50 << 16)
> #define KEXEC_ARCH_ARM (40 << 16)
> +#define KEXEC_ARCH_ARM64 (183 << 16)
> #define KEXEC_ARCH_S390 (22 << 16)
> #define KEXEC_ARCH_SH (42 << 16)
> #define KEXEC_ARCH_MIPS_LE (10 << 16)
> @@ -153,5 +154,8 @@ static inline long kexec_file_load(int kernel_fd, int initrd_fd,
> #ifdef __m68k__
> #define KEXEC_ARCH_NATIVE KEXEC_ARCH_68K
> #endif
> +#if defined(__arm64__)
> +#define KEXEC_ARCH_NATIVE KEXEC_ARCH_ARM64
> +#endif
>
> #endif /* KEXEC_SYSCALL_H */
> diff --git a/purgatory/Makefile b/purgatory/Makefile
> index 2b5c061..ca0443c 100644
> --- a/purgatory/Makefile
> +++ b/purgatory/Makefile
> @@ -19,6 +19,7 @@ dist += purgatory/Makefile $(PURGATORY_SRCS) \
>
> include $(srcdir)/purgatory/arch/alpha/Makefile
> include $(srcdir)/purgatory/arch/arm/Makefile
> +include $(srcdir)/purgatory/arch/arm64/Makefile
> include $(srcdir)/purgatory/arch/i386/Makefile
> include $(srcdir)/purgatory/arch/ia64/Makefile
> include $(srcdir)/purgatory/arch/mips/Makefile
> diff --git a/purgatory/arch/arm64/Makefile b/purgatory/arch/arm64/Makefile
> new file mode 100644
> index 0000000..636abea
> --- /dev/null
> +++ b/purgatory/arch/arm64/Makefile
> @@ -0,0 +1,18 @@
> +
> +arm64_PURGATORY_EXTRA_CFLAGS = \
> + -mcmodel=large \
> + -fno-stack-protector \
> + -fno-asynchronous-unwind-tables \
> + -Wundef \
> + -Werror-implicit-function-declaration \
> + -Wdeclaration-after-statement \
> + -Werror=implicit-int \
> + -Werror=strict-prototypes
> +
> +arm64_PURGATORY_SRCS += \
> + purgatory/arch/arm64/entry.S \
> + purgatory/arch/arm64/purgatory-arm64.c
> +
> +dist += \
> + $(arm64_PURGATORY_SRCS) \
> + purgatory/arch/arm64/Makefile
> diff --git a/purgatory/arch/arm64/entry.S b/purgatory/arch/arm64/entry.S
> new file mode 100644
> index 0000000..adf16f4
> --- /dev/null
> +++ b/purgatory/arch/arm64/entry.S
> @@ -0,0 +1,51 @@
> +/*
> + * ARM64 purgatory.
> + */
> +
> +.macro size, sym:req
> + .size \sym, . - \sym
> +.endm
> +
> +.text
> +
> +.globl purgatory_start
> +purgatory_start:
> +
> + adr x19, .Lstack
> + mov sp, x19
> +
> + bl purgatory
> +
> + /* Start new image. */
> + ldr x17, arm64_kernel_entry
> + ldr x0, arm64_dtb_addr
> + mov x1, xzr
> + mov x2, xzr
> + mov x3, xzr
> + br x17
> +
> +size purgatory_start
> +
> +.ltorg
> +
> +.align 4
> + .rept 256
> + .quad 0
> + .endr
> +.Lstack:
> +
> +.data
> +
> +.align 3
> +
> +.globl arm64_kernel_entry
> +arm64_kernel_entry:
> + .quad 0
> +size arm64_kernel_entry
> +
> +.globl arm64_dtb_addr
> +arm64_dtb_addr:
> + .quad 0
> +size arm64_dtb_addr
> +
> +.end
> \ No newline at end of file
> diff --git a/purgatory/arch/arm64/purgatory-arm64.c b/purgatory/arch/arm64/purgatory-arm64.c
> new file mode 100644
> index 0000000..fe50fcf
> --- /dev/null
> +++ b/purgatory/arch/arm64/purgatory-arm64.c
> @@ -0,0 +1,19 @@
> +/*
> + * ARM64 purgatory.
> + */
> +
> +#include <stdint.h>
> +#include <purgatory.h>
> +
> +void putchar(int ch)
> +{
> + /* Nothing for now */
> +}
> +
> +void post_verification_setup_arch(void)
> +{
> +}
> +
> +void setup_arch(void)
> +{
> +}
> --
> 2.7.4
>
>
_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v5 2/3] arm64: Add arm64 kexec support
2016-09-06 5:44 ` AKASHI Takahiro
@ 2016-09-07 23:57 ` Geoff Levand
-1 siblings, 0 replies; 14+ messages in thread
From: Geoff Levand @ 2016-09-07 23:57 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, 2016-09-06 at 14:44 +0900, AKASHI Takahiro wrote:
> On Thu, Sep 01, 2016 at 09:51:10PM +0000, Geoff Levand wrote:
> > +int arm64_load_other_segments(struct kexec_info *info,
> > + unsigned long image_base)
> > +{
> > + int result;
> > + unsigned long dtb_base;
> > + unsigned long hole_min;
> > + unsigned long hole_max;
> > + char *initrd_buf = NULL;
> > + struct dtb dtb;
> > + char command_line[COMMAND_LINE_SIZE] = "";
> > +
> > + if (arm64_opts.command_line) {
> > + strncpy(command_line, arm64_opts.command_line,
> > + sizeof(command_line));
> > + command_line[sizeof(command_line) - 1] = 0;
> > + }
> > +
> > + if (arm64_opts.dtb) {
> > + dtb.name = "dtb_2";
> "dtb_1" and "dtb_2" are not very meaningful.
dtb_1 means from 1st stage, dtb_2 means a new 2nd stage.
> Instead, I'm going to use:
> ????dtb_2 => "dtb_user" (if a command line option is specified)
> ????dtb_1 => "dtb_sys" (if from /sys/firmware/fdt), or
> ?????????????"dtb_unflatten" (if created from /proc/device-tree)
> to distinguish the origins in my kdump patches.
Sure, that's OK. If you make a fixup patch I'll merge it in.
>
> > +static int get_memory_ranges_iomem_cb(void *data, int nr, char *str,
> > + unsigned long long base, unsigned long long length)
> > +{
> > + struct memory_range *r;
> > +
> > + if (nr >= KEXEC_SEGMENT_MAX)
> > + return -1;
> > +
> > + r = (struct memory_range *)data + nr;
> > + r->type = RANGE_RAM;
> > + r->start = base;
> > + r->end = base + length - 1;
> > +
> > + set_phys_offset(r->start);
> This will no longer work correctly for identifying PHYS_OFFSET
> once the following patch is applied:
> http://lists.infradead.org/pipermail/linux-arm-kernel/2016-August/450433.html
> and it is now queued in arm64/for-next.
>
> I will include a fixup in my kdump patches as the current code of kexec
> doesn't rely on a result of virt_to_phys() seriously.
OK.
> +int get_memory_ranges(struct memory_range **range, int *ranges,
> > + unsigned long kexec_flags)
> > +{
> > + static struct memory_range array[KEXEC_SEGMENT_MAX];
> > + unsigned int count;
> > + int result;
> > +
> > + result = get_memory_ranges_iomem(array, &count);
> > +
> > + if (result)
> > + result = get_memory_ranges_dt(array, &count);
> Do we really need to scan a DT blob here?
> I think that all the (usable) memory regions are added to
> /proc/iomem anyway.
I guess it is now expected that /proc/iomem has memory
in it.
> > +void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
> > + unsigned long r_type, void *ptr, unsigned long address,
> > + unsigned long value)
> > +{
> > +#if !defined(R_AARCH64_ABS64)
> > +# define R_AARCH64_ABS64 257
> > +#endif
> > +
> > +#if !defined(R_AARCH64_LD_PREL_LO19)
> > +# define R_AARCH64_LD_PREL_LO19 273
> > +#endif
> > +
> > +#if !defined(R_AARCH64_ADR_PREL_LO21)
> > +# define R_AARCH64_ADR_PREL_LO21 274
> > +#endif
> > +
> > +#if !defined(R_AARCH64_JUMP26)
> > +# define R_AARCH64_JUMP26 282
> > +#endif
> > +
> > +#if !defined(R_AARCH64_CALL26)
> > +# define R_AARCH64_CALL26 283
> > +#endif
> Did you see my comment?
> http://lists.infradead.org/pipermail/kexec/2016-August/016947.html
I'm still expecting?elf.h to have these.
-Geoff
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v5 2/3] arm64: Add arm64 kexec support
@ 2016-09-07 23:57 ` Geoff Levand
0 siblings, 0 replies; 14+ messages in thread
From: Geoff Levand @ 2016-09-07 23:57 UTC (permalink / raw)
To: AKASHI Takahiro
Cc: Pratyush Anand, Mark Rutland, Simon Horman, kexec, linux-arm-kernel
On Tue, 2016-09-06 at 14:44 +0900, AKASHI Takahiro wrote:
> On Thu, Sep 01, 2016 at 09:51:10PM +0000, Geoff Levand wrote:
> > +int arm64_load_other_segments(struct kexec_info *info,
> > + unsigned long image_base)
> > +{
> > + int result;
> > + unsigned long dtb_base;
> > + unsigned long hole_min;
> > + unsigned long hole_max;
> > + char *initrd_buf = NULL;
> > + struct dtb dtb;
> > + char command_line[COMMAND_LINE_SIZE] = "";
> > +
> > + if (arm64_opts.command_line) {
> > + strncpy(command_line, arm64_opts.command_line,
> > + sizeof(command_line));
> > + command_line[sizeof(command_line) - 1] = 0;
> > + }
> > +
> > + if (arm64_opts.dtb) {
> > + dtb.name = "dtb_2";
> "dtb_1" and "dtb_2" are not very meaningful.
dtb_1 means from 1st stage, dtb_2 means a new 2nd stage.
> Instead, I'm going to use:
> dtb_2 => "dtb_user" (if a command line option is specified)
> dtb_1 => "dtb_sys" (if from /sys/firmware/fdt), or
> "dtb_unflatten" (if created from /proc/device-tree)
> to distinguish the origins in my kdump patches.
Sure, that's OK. If you make a fixup patch I'll merge it in.
>
> > +static int get_memory_ranges_iomem_cb(void *data, int nr, char *str,
> > + unsigned long long base, unsigned long long length)
> > +{
> > + struct memory_range *r;
> > +
> > + if (nr >= KEXEC_SEGMENT_MAX)
> > + return -1;
> > +
> > + r = (struct memory_range *)data + nr;
> > + r->type = RANGE_RAM;
> > + r->start = base;
> > + r->end = base + length - 1;
> > +
> > + set_phys_offset(r->start);
> This will no longer work correctly for identifying PHYS_OFFSET
> once the following patch is applied:
> http://lists.infradead.org/pipermail/linux-arm-kernel/2016-August/450433.html
> and it is now queued in arm64/for-next.
>
> I will include a fixup in my kdump patches as the current code of kexec
> doesn't rely on a result of virt_to_phys() seriously.
OK.
> +int get_memory_ranges(struct memory_range **range, int *ranges,
> > + unsigned long kexec_flags)
> > +{
> > + static struct memory_range array[KEXEC_SEGMENT_MAX];
> > + unsigned int count;
> > + int result;
> > +
> > + result = get_memory_ranges_iomem(array, &count);
> > +
> > + if (result)
> > + result = get_memory_ranges_dt(array, &count);
> Do we really need to scan a DT blob here?
> I think that all the (usable) memory regions are added to
> /proc/iomem anyway.
I guess it is now expected that /proc/iomem has memory
in it.
> > +void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
> > + unsigned long r_type, void *ptr, unsigned long address,
> > + unsigned long value)
> > +{
> > +#if !defined(R_AARCH64_ABS64)
> > +# define R_AARCH64_ABS64 257
> > +#endif
> > +
> > +#if !defined(R_AARCH64_LD_PREL_LO19)
> > +# define R_AARCH64_LD_PREL_LO19 273
> > +#endif
> > +
> > +#if !defined(R_AARCH64_ADR_PREL_LO21)
> > +# define R_AARCH64_ADR_PREL_LO21 274
> > +#endif
> > +
> > +#if !defined(R_AARCH64_JUMP26)
> > +# define R_AARCH64_JUMP26 282
> > +#endif
> > +
> > +#if !defined(R_AARCH64_CALL26)
> > +# define R_AARCH64_CALL26 283
> > +#endif
> Did you see my comment?
> http://lists.infradead.org/pipermail/kexec/2016-August/016947.html
I'm still expecting elf.h to have these.
-Geoff
_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v5 2/3] arm64: Add arm64 kexec support
2016-09-06 5:44 ` AKASHI Takahiro
@ 2016-09-08 0:11 ` Geoff Levand
-1 siblings, 0 replies; 14+ messages in thread
From: Geoff Levand @ 2016-09-08 0:11 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, 2016-09-06 at 14:44 +0900, AKASHI Takahiro wrote:
>
> On Thu, Sep 01, 2016 at 09:51:10PM +0000, Geoff Levand wrote:
>
> >
> > +int arm64_load_other_segments(struct kexec_info *info,
> > + unsigned long image_base)
> > +{
> > + int result;
> > + unsigned long dtb_base;
> > + unsigned long hole_min;
> > + unsigned long hole_max;
> > + char *initrd_buf = NULL;
> > + struct dtb dtb;
> > + char command_line[COMMAND_LINE_SIZE] = "";
> > +
> > + if (arm64_opts.command_line) {
> > + strncpy(command_line, arm64_opts.command_line,
> > + sizeof(command_line));
> > + command_line[sizeof(command_line) - 1] = 0;
> > + }
> > +
> > + if (arm64_opts.dtb) {
> > + dtb.name = "dtb_2";
> "dtb_1" and "dtb_2" are not very meaningful.
dtb_1 means from 1st stage, dtb_2 means a new 2nd stage.
>
> Instead, I'm going to use:
> ????dtb_2 => "dtb_user" (if a command line option is specified)
> ????dtb_1 => "dtb_sys" (if from /sys/firmware/fdt), or
> ?????????????"dtb_unflatten" (if created from /proc/device-tree)
> to distinguish the origins in my kdump patches.
Sure, that's OK.??I pushed ot a change to my master branch.
>
>
> >
> > +static int get_memory_ranges_iomem_cb(void *data, int nr, char
> > *str,
> > + unsigned long long base, unsigned long long length)
> > +{
> > + struct memory_range *r;
> > +
> > + if (nr >= KEXEC_SEGMENT_MAX)
> > + return -1;
> > +
> > + r = (struct memory_range *)data + nr;
> > + r->type = RANGE_RAM;
> > + r->start = base;
> > + r->end = base + length - 1;
> > +
> > + set_phys_offset(r->start);
> This will no longer work correctly for identifying PHYS_OFFSET
> once the following patch is applied:
> http://lists.infradead.org/pipermail/linux-arm-kernel/2016-August/450
> 433.html
> and it is now queued in arm64/for-next.
>
> I will include a fixup in my kdump patches as the current code of
> kexec
> doesn't rely on a result of virt_to_phys() seriously.
OK.
>
> +int get_memory_ranges(struct memory_range **range, int *ranges,
> >
> > + unsigned long kexec_flags)
> > +{
> > + static struct memory_range array[KEXEC_SEGMENT_MAX];
> > + unsigned int count;
> > + int result;
> > +
> > + result = get_memory_ranges_iomem(array, &count);
> > +
> > + if (result)
> > + result = get_memory_ranges_dt(array, &count);
> Do we really need to scan a DT blob here?
> I think that all the (usable) memory regions are added to
> /proc/iomem anyway.
I guess it is now expected that /proc/iomem has memory
in it.
>
> >
> > +void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym
> > *UNUSED(sym),
> > + unsigned long r_type, void *ptr, unsigned long address,
> > + unsigned long value)
> > +{
> > +#if !defined(R_AARCH64_ABS64)
> > +# define R_AARCH64_ABS64 257
> > +#endif
> > +
> > +#if !defined(R_AARCH64_LD_PREL_LO19)
> > +# define R_AARCH64_LD_PREL_LO19 273
> > +#endif
> > +
> > +#if !defined(R_AARCH64_ADR_PREL_LO21)
> > +# define R_AARCH64_ADR_PREL_LO21 274
> > +#endif
> > +
> > +#if !defined(R_AARCH64_JUMP26)
> > +# define R_AARCH64_JUMP26 282
> > +#endif
> > +
> > +#if !defined(R_AARCH64_CALL26)
> > +# define R_AARCH64_CALL26 283
> > +#endif
> Did you see my comment?
> http://lists.infradead.org/pipermail/kexec/2016-August/016947.html
I'm still expecting?elf.h to have these.
-Geoff
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v5 2/3] arm64: Add arm64 kexec support
@ 2016-09-08 0:11 ` Geoff Levand
0 siblings, 0 replies; 14+ messages in thread
From: Geoff Levand @ 2016-09-08 0:11 UTC (permalink / raw)
To: AKASHI Takahiro
Cc: Pratyush Anand, Mark Rutland, Simon Horman, kexec, linux-arm-kernel
On Tue, 2016-09-06 at 14:44 +0900, AKASHI Takahiro wrote:
>
> On Thu, Sep 01, 2016 at 09:51:10PM +0000, Geoff Levand wrote:
>
> >
> > +int arm64_load_other_segments(struct kexec_info *info,
> > + unsigned long image_base)
> > +{
> > + int result;
> > + unsigned long dtb_base;
> > + unsigned long hole_min;
> > + unsigned long hole_max;
> > + char *initrd_buf = NULL;
> > + struct dtb dtb;
> > + char command_line[COMMAND_LINE_SIZE] = "";
> > +
> > + if (arm64_opts.command_line) {
> > + strncpy(command_line, arm64_opts.command_line,
> > + sizeof(command_line));
> > + command_line[sizeof(command_line) - 1] = 0;
> > + }
> > +
> > + if (arm64_opts.dtb) {
> > + dtb.name = "dtb_2";
> "dtb_1" and "dtb_2" are not very meaningful.
dtb_1 means from 1st stage, dtb_2 means a new 2nd stage.
>
> Instead, I'm going to use:
> dtb_2 => "dtb_user" (if a command line option is specified)
> dtb_1 => "dtb_sys" (if from /sys/firmware/fdt), or
> "dtb_unflatten" (if created from /proc/device-tree)
> to distinguish the origins in my kdump patches.
Sure, that's OK. I pushed ot a change to my master branch.
>
>
> >
> > +static int get_memory_ranges_iomem_cb(void *data, int nr, char
> > *str,
> > + unsigned long long base, unsigned long long length)
> > +{
> > + struct memory_range *r;
> > +
> > + if (nr >= KEXEC_SEGMENT_MAX)
> > + return -1;
> > +
> > + r = (struct memory_range *)data + nr;
> > + r->type = RANGE_RAM;
> > + r->start = base;
> > + r->end = base + length - 1;
> > +
> > + set_phys_offset(r->start);
> This will no longer work correctly for identifying PHYS_OFFSET
> once the following patch is applied:
> http://lists.infradead.org/pipermail/linux-arm-kernel/2016-August/450
> 433.html
> and it is now queued in arm64/for-next.
>
> I will include a fixup in my kdump patches as the current code of
> kexec
> doesn't rely on a result of virt_to_phys() seriously.
OK.
>
> +int get_memory_ranges(struct memory_range **range, int *ranges,
> >
> > + unsigned long kexec_flags)
> > +{
> > + static struct memory_range array[KEXEC_SEGMENT_MAX];
> > + unsigned int count;
> > + int result;
> > +
> > + result = get_memory_ranges_iomem(array, &count);
> > +
> > + if (result)
> > + result = get_memory_ranges_dt(array, &count);
> Do we really need to scan a DT blob here?
> I think that all the (usable) memory regions are added to
> /proc/iomem anyway.
I guess it is now expected that /proc/iomem has memory
in it.
>
> >
> > +void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym
> > *UNUSED(sym),
> > + unsigned long r_type, void *ptr, unsigned long address,
> > + unsigned long value)
> > +{
> > +#if !defined(R_AARCH64_ABS64)
> > +# define R_AARCH64_ABS64 257
> > +#endif
> > +
> > +#if !defined(R_AARCH64_LD_PREL_LO19)
> > +# define R_AARCH64_LD_PREL_LO19 273
> > +#endif
> > +
> > +#if !defined(R_AARCH64_ADR_PREL_LO21)
> > +# define R_AARCH64_ADR_PREL_LO21 274
> > +#endif
> > +
> > +#if !defined(R_AARCH64_JUMP26)
> > +# define R_AARCH64_JUMP26 282
> > +#endif
> > +
> > +#if !defined(R_AARCH64_CALL26)
> > +# define R_AARCH64_CALL26 283
> > +#endif
> Did you see my comment?
> http://lists.infradead.org/pipermail/kexec/2016-August/016947.html
I'm still expecting elf.h to have these.
-Geoff
_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2016-09-08 0:11 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-09-01 21:51 [PATCH v5 0/3] arm64 kexec-tools patches Geoff Levand
2016-09-01 21:51 ` Geoff Levand
2016-09-01 21:51 ` [PATCH v5 2/3] arm64: Add arm64 kexec support Geoff Levand
2016-09-01 21:51 ` Geoff Levand
2016-09-06 5:44 ` AKASHI Takahiro
2016-09-06 5:44 ` AKASHI Takahiro
2016-09-07 23:57 ` Geoff Levand
2016-09-07 23:57 ` Geoff Levand
2016-09-08 0:11 ` Geoff Levand
2016-09-08 0:11 ` Geoff Levand
2016-09-01 21:51 ` [PATCH v5 3/3] arm64: Add support for binary image files Geoff Levand
2016-09-01 21:51 ` Geoff Levand
2016-09-01 21:51 ` [PATCH v5 1/3] kexec: Add common device tree routines Geoff Levand
2016-09-01 21:51 ` Geoff Levand
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.