From: Pingfan Liu <piliu(a)redhat.com>
Fix Bug 925630 - kexec-tools: support for arm64
https://bugzilla.redhat.com/show_bug.cgi?id=925630
involves two things:
1. back porting upstream code to enable the function of kexec-tools on arm64
patchset backported from upstream:
commit abdfe97736f89d9bc73662b9134604b0229a599e
commit 522df5f7217fda01ece3f6ac3e9987b0320c2bb0
commit 217bcc00c9309416a6c6cd0584196559d28a9259
2. fix the arm related building issue by using autoreconf in spec file
Signed-off-by: Pingfan Liu <piliu(a)redhat.com>
---
0001-kexec-Add-common-device-tree-routines.patch | 208 +++
0002-arm64-Add-arm64-kexec-support.patch | 1407 ++++++++++++++++++++
...-arm64-Add-support-for-binary-image-files.patch | 96 ++
kexec-tools.spec | 20 +-
4 files changed, 1726 insertions(+), 5 deletions(-)
create mode 100644 0001-kexec-Add-common-device-tree-routines.patch
create mode 100644 0002-arm64-Add-arm64-kexec-support.patch
create mode 100644 0003-arm64-Add-support-for-binary-image-files.patch
diff --git a/0001-kexec-Add-common-device-tree-routines.patch
b/0001-kexec-Add-common-device-tree-routines.patch
new file mode 100644
index 0000000..87897f0
--- /dev/null
+++ b/0001-kexec-Add-common-device-tree-routines.patch
@@ -0,0 +1,208 @@
+From 217bcc00c9309416a6c6cd0584196559d28a9259 Mon Sep 17 00:00:00 2001
+From: Geoff Levand <geoff(a)infradead.org>
+Date: Wed, 21 Sep 2016 18:14:25 +0000
+Subject: [PATCH 1/3] kexec: Add common device tree routines
+
+Common device tree routines that can be shared between all arches
+that have device tree support.
+
+Signed-off-by: Geoff Levand <geoff(a)infradead.org>
+Tested-By: Pratyush Anand <panand(a)redhat.com>
+Tested-By: Matthias Brugger <mbrugger(a)suse.com>
+Signed-off-by: Simon Horman <horms(a)verge.net.au>
+---
+ 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.9.3
+
diff --git a/0002-arm64-Add-arm64-kexec-support.patch
b/0002-arm64-Add-arm64-kexec-support.patch
new file mode 100644
index 0000000..0c14b1b
--- /dev/null
+++ b/0002-arm64-Add-arm64-kexec-support.patch
@@ -0,0 +1,1407 @@
+From 522df5f7217fda01ece3f6ac3e9987b0320c2bb0 Mon Sep 17 00:00:00 2001
+From: Geoff Levand <geoff(a)infradead.org>
+Date: Wed, 21 Sep 2016 18:14:25 +0000
+Subject: [PATCH 2/3] arm64: Add arm64 kexec support
+
+Add kexec reboot support for ARM64 platforms.
+
+Signed-off-by: Geoff Levand <geoff(a)infradead.org>
+Tested-By: Pratyush Anand <panand(a)redhat.com>
+Tested-By: Matthias Brugger <mbrugger(a)suse.com>
+Signed-off-by: Simon Horman <horms(a)verge.net.au>
+---
+ 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 | 615 ++++++++++++++++++++++++++++++++
+ 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, 1230 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 2bc5767..252b048 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..2e8839a
+--- /dev/null
++++ b/kexec/arch/arm64/kexec-arm64.c
+@@ -0,0 +1,615 @@
++/*
++ * 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;
++
++ dtb->name = "dtb_sys";
++ result = read_sys_dtb(dtb);
++
++ if (!result)
++ goto on_success;
++
++ dtb->name = "dtb_proc";
++ 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_user";
++ dtb.buf = slurp_file(arm64_opts.dtb, &dtb.size);
++ } else {
++ 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 - 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);
++
++ *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.9.3
+
diff --git a/0003-arm64-Add-support-for-binary-image-files.patch
b/0003-arm64-Add-support-for-binary-image-files.patch
new file mode 100644
index 0000000..8e8dde0
--- /dev/null
+++ b/0003-arm64-Add-support-for-binary-image-files.patch
@@ -0,0 +1,96 @@
+From abdfe97736f89d9bc73662b9134604b0229a599e Mon Sep 17 00:00:00 2001
+From: Pratyush Anand <panand(a)redhat.com>
+Date: Wed, 21 Sep 2016 18:14:25 +0000
+Subject: [PATCH 3/3] arm64: Add support for binary image files
+
+Signed-off-by: Pratyush Anand <panand(a)redhat.com>
+[Reworked and cleaned up]
+Signed-off-by: Geoff Levand <geoff(a)infradead.org>
+Tested-By: Pratyush Anand <panand(a)redhat.com>
+Tested-By: Matthias Brugger <mbrugger(a)suse.com>
+Signed-off-by: Simon Horman <horms(a)verge.net.au>
+---
+ 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.9.3
+
diff --git a/kexec-tools.spec b/kexec-tools.spec
index cd26ec3..985fd2d 100644
--- a/kexec-tools.spec
+++ b/kexec-tools.spec
@@ -1,9 +1,10 @@
Name: kexec-tools
Version: 2.0.13
-Release: 5%{?dist}
+Release: 6%{?dist}
License: GPLv2
Group: Applications/System
Summary: The kexec/kdump userspace component
+
Source0:
http://kernel.org/pub/linux/utils/kernel/kexec/%{name}-%{version}.tar.xz
Source1: kdumpctl
Source2: kdump.sysconfig
@@ -50,14 +51,13 @@ Requires: dracut-network, ethtool
BuildRequires: zlib-devel zlib zlib-static elfutils-devel-static glib2-devel bzip2-devel
ncurses-devel bison flex lzo-devel snappy-devel
BuildRequires: pkgconfig intltool gettext
BuildRequires: systemd-units
+BuildRequires: automake autoconf libtool
%ifarch %{ix86} x86_64 ppc64 ppc s390x ppc64le
Obsoletes: diskdumputils netdump kexec-tools-eppic
%endif
%undefine _hardened_build
-ExcludeArch: aarch64
-
#START INSERT
#
@@ -76,8 +76,12 @@ ExcludeArch: aarch64
# Patches 401 through 500 are meant for s390 kexec-tools enablement
#
#
-# Patches 501 through 600 are meant for ppc kexec-tools enablement
+# Patches 501 through 600 are meant for ARM kexec-tools enablement
#
+# kexec v5 -
http://lists.infradead.org/pipermail/kexec/2016-September/017110.html
+Patch500: 0001-kexec-Add-common-device-tree-routines.patch
+Patch501: 0002-arm64-Add-arm64-kexec-support.patch
+Patch502: 0003-arm64-Add-support-for-binary-image-files.patch
#
# Patches 601 onward are generic patches
@@ -107,6 +111,9 @@ tar -z -x -v -f %{SOURCE9}
tar -z -x -v -f %{SOURCE19}
tar -z -x -v -f %{SOURCE23}
+%patch500 -p1
+%patch501 -p1
+%patch502 -p1
%patch601 -p1
%patch602 -p1
@@ -117,7 +124,7 @@ tar -z -x -v -f %{SOURCE23}
%endif
%build
-
+autoreconf
%configure \
%ifarch ppc64
--host=powerpc64-redhat-linux-gnu \
@@ -315,6 +322,9 @@ done
%doc
%changelog
+* Mon Sep 19 2016 Peter Robinson <pbrobinson(a)fedoraproject.org> 2.0.13-6
+- Add initial upstream support for kexec on aarch64
+
* Fri Sep 16 2016 Dave Young <dyoung(a)redhat.com> - 2.0.13-5
- Fix bug 1373958 for system boot without initrd
- Do not depend on /etc/fstab in kdumpctl in case it does not exist
--
2.9.3