|
|
a6d77e |
From 21014aa0868f11223e3b33afa1b6320b79187dbc Mon Sep 17 00:00:00 2001
|
|
|
a6d77e |
Message-Id: <21014aa0868f11223e3b33afa1b6320b79187dbc.1431592766.git.panand@redhat.com>
|
|
|
a6d77e |
In-Reply-To: <1fb6841aa15407dbf371589d7abca7bc2d35815c.1431592766.git.panand@redhat.com>
|
|
|
a6d77e |
References: <1fb6841aa15407dbf371589d7abca7bc2d35815c.1431592766.git.panand@redhat.com>
|
|
|
a6d77e |
From: Geoff Levand <geoff@infradead.org>
|
|
|
a6d77e |
Date: Fri, 17 Apr 2015 10:54:55 -0700
|
|
|
a6d77e |
Subject: [PATCH 02/17] arm64: Add arm64 kexec support
|
|
|
a6d77e |
|
|
|
a6d77e |
Add kexec reboot support for ARM64 platforms. Uses purgatory.
|
|
|
a6d77e |
|
|
|
a6d77e |
Signed-off-by: Geoff Levand <geoff@infradead.org>
|
|
|
a6d77e |
---
|
|
|
a6d77e |
configure.ac | 3 +
|
|
|
a6d77e |
kexec/Makefile | 1 +
|
|
|
a6d77e |
kexec/arch/arm64/Makefile | 40 ++
|
|
|
a6d77e |
kexec/arch/arm64/crashdump-arm64.c | 21 +
|
|
|
a6d77e |
kexec/arch/arm64/crashdump-arm64.h | 12 +
|
|
|
a6d77e |
kexec/arch/arm64/image-header.h | 95 +++
|
|
|
a6d77e |
kexec/arch/arm64/include/arch/options.h | 44 ++
|
|
|
a6d77e |
kexec/arch/arm64/kexec-arm64.c | 1041 +++++++++++++++++++++++++++++++
|
|
|
a6d77e |
kexec/arch/arm64/kexec-arm64.h | 51 ++
|
|
|
a6d77e |
kexec/arch/arm64/kexec-elf-arm64.c | 123 ++++
|
|
|
a6d77e |
kexec/arch/arm64/kexec-image-arm64.c | 50 ++
|
|
|
a6d77e |
kexec/kexec-syscall.h | 9 +-
|
|
|
a6d77e |
purgatory/Makefile | 1 +
|
|
|
a6d77e |
purgatory/arch/arm64/Makefile | 18 +
|
|
|
a6d77e |
purgatory/arch/arm64/entry.S | 54 ++
|
|
|
a6d77e |
purgatory/arch/arm64/purgatory-arm64.c | 35 ++
|
|
|
a6d77e |
16 files changed, 1596 insertions(+), 2 deletions(-)
|
|
|
a6d77e |
create mode 100644 kexec/arch/arm64/Makefile
|
|
|
a6d77e |
create mode 100644 kexec/arch/arm64/crashdump-arm64.c
|
|
|
a6d77e |
create mode 100644 kexec/arch/arm64/crashdump-arm64.h
|
|
|
a6d77e |
create mode 100644 kexec/arch/arm64/image-header.h
|
|
|
a6d77e |
create mode 100644 kexec/arch/arm64/include/arch/options.h
|
|
|
a6d77e |
create mode 100644 kexec/arch/arm64/kexec-arm64.c
|
|
|
a6d77e |
create mode 100644 kexec/arch/arm64/kexec-arm64.h
|
|
|
a6d77e |
create mode 100644 kexec/arch/arm64/kexec-elf-arm64.c
|
|
|
a6d77e |
create mode 100644 kexec/arch/arm64/kexec-image-arm64.c
|
|
|
a6d77e |
create mode 100644 purgatory/arch/arm64/Makefile
|
|
|
a6d77e |
create mode 100644 purgatory/arch/arm64/entry.S
|
|
|
a6d77e |
create mode 100644 purgatory/arch/arm64/purgatory-arm64.c
|
|
|
a6d77e |
|
|
|
a6d77e |
diff --git a/configure.ac b/configure.ac
|
|
|
a6d77e |
index 6946780d498f..701ed69c5033 100644
|
|
|
a6d77e |
--- a/configure.ac
|
|
|
a6d77e |
+++ b/configure.ac
|
|
|
a6d77e |
@@ -35,6 +35,9 @@ case $target_cpu in
|
|
|
a6d77e |
ARCH="ppc64"
|
|
|
a6d77e |
SUBARCH="LE"
|
|
|
a6d77e |
;;
|
|
|
a6d77e |
+ aarch64* )
|
|
|
a6d77e |
+ ARCH="arm64"
|
|
|
a6d77e |
+ ;;
|
|
|
a6d77e |
arm* )
|
|
|
a6d77e |
ARCH="arm"
|
|
|
a6d77e |
;;
|
|
|
a6d77e |
diff --git a/kexec/Makefile b/kexec/Makefile
|
|
|
a6d77e |
index 6937a4dbb38b..fac668072463 100644
|
|
|
a6d77e |
--- a/kexec/Makefile
|
|
|
a6d77e |
+++ b/kexec/Makefile
|
|
|
a6d77e |
@@ -75,6 +75,7 @@ KEXEC_SRCS += $($(ARCH)_DT_OPS)
|
|
|
a6d77e |
|
|
|
a6d77e |
include $(srcdir)/kexec/arch/alpha/Makefile
|
|
|
a6d77e |
include $(srcdir)/kexec/arch/arm/Makefile
|
|
|
a6d77e |
+include $(srcdir)/kexec/arch/arm64/Makefile
|
|
|
a6d77e |
include $(srcdir)/kexec/arch/i386/Makefile
|
|
|
a6d77e |
include $(srcdir)/kexec/arch/ia64/Makefile
|
|
|
a6d77e |
include $(srcdir)/kexec/arch/m68k/Makefile
|
|
|
a6d77e |
diff --git a/kexec/arch/arm64/Makefile b/kexec/arch/arm64/Makefile
|
|
|
a6d77e |
new file mode 100644
|
|
|
a6d77e |
index 000000000000..37414dc5c900
|
|
|
a6d77e |
--- /dev/null
|
|
|
a6d77e |
+++ b/kexec/arch/arm64/Makefile
|
|
|
a6d77e |
@@ -0,0 +1,40 @@
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+arm64_FS2DT += kexec/fs2dt.c
|
|
|
a6d77e |
+arm64_FS2DT_INCLUDE += -include $(srcdir)/kexec/arch/arm64/kexec-arm64.h \
|
|
|
a6d77e |
+ -include $(srcdir)/kexec/arch/arm64/crashdump-arm64.h
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+arm64_DT_OPS += kexec/dt-ops.c
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+arm64_CPPFLAGS += -I $(srcdir)/kexec/
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+arm64_KEXEC_SRCS += \
|
|
|
a6d77e |
+ kexec/arch/arm64/kexec-arm64.c \
|
|
|
a6d77e |
+ kexec/arch/arm64/kexec-image-arm64.c \
|
|
|
a6d77e |
+ kexec/arch/arm64/kexec-elf-arm64.c \
|
|
|
a6d77e |
+ kexec/arch/arm64/crashdump-arm64.c
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+arm64_ARCH_REUSE_INITRD =
|
|
|
a6d77e |
+arm64_ADD_SEGMENT =
|
|
|
a6d77e |
+arm64_VIRT_TO_PHYS =
|
|
|
a6d77e |
+arm64_PHYS_TO_VIRT =
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+dist += $(arm64_KEXEC_SRCS) \
|
|
|
a6d77e |
+ kexec/arch/arm64/Makefile \
|
|
|
a6d77e |
+ kexec/arch/arm64/kexec-arm64.h \
|
|
|
a6d77e |
+ kexec/arch/arm64/crashdump-arm64.h
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ifdef HAVE_LIBFDT
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+LIBS += -lfdt
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+else
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+include $(srcdir)/kexec/libfdt/Makefile.libfdt
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+libfdt_SRCS += $(LIBFDT_SRCS:%=kexec/libfdt/%)
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+arm64_CPPFLAGS += -I$(srcdir)/kexec/libfdt
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+arm64_KEXEC_SRCS += $(libfdt_SRCS)
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+endif
|
|
|
a6d77e |
diff --git a/kexec/arch/arm64/crashdump-arm64.c b/kexec/arch/arm64/crashdump-arm64.c
|
|
|
a6d77e |
new file mode 100644
|
|
|
a6d77e |
index 000000000000..d2272c8124d0
|
|
|
a6d77e |
--- /dev/null
|
|
|
a6d77e |
+++ b/kexec/arch/arm64/crashdump-arm64.c
|
|
|
a6d77e |
@@ -0,0 +1,21 @@
|
|
|
a6d77e |
+/*
|
|
|
a6d77e |
+ * ARM64 crashdump.
|
|
|
a6d77e |
+ */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#define _GNU_SOURCE
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#include <errno.h>
|
|
|
a6d77e |
+#include <linux/elf.h>
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#include "kexec.h"
|
|
|
a6d77e |
+#include "crashdump.h"
|
|
|
a6d77e |
+#include "crashdump-arm64.h"
|
|
|
a6d77e |
+#include "kexec-arm64.h"
|
|
|
a6d77e |
+#include "kexec-elf.h"
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+struct memory_ranges usablemem_rgns = {};
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+int is_crashkernel_mem_reserved(void)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ return 0;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
diff --git a/kexec/arch/arm64/crashdump-arm64.h b/kexec/arch/arm64/crashdump-arm64.h
|
|
|
a6d77e |
new file mode 100644
|
|
|
a6d77e |
index 000000000000..f33c7a25b454
|
|
|
a6d77e |
--- /dev/null
|
|
|
a6d77e |
+++ b/kexec/arch/arm64/crashdump-arm64.h
|
|
|
a6d77e |
@@ -0,0 +1,12 @@
|
|
|
a6d77e |
+/*
|
|
|
a6d77e |
+ * ARM64 crashdump.
|
|
|
a6d77e |
+ */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#if !defined(CRASHDUMP_ARM64_H)
|
|
|
a6d77e |
+#define CRASHDUMP_ARM64_H
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#include "kexec.h"
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+extern struct memory_ranges usablemem_rgns;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#endif
|
|
|
a6d77e |
diff --git a/kexec/arch/arm64/image-header.h b/kexec/arch/arm64/image-header.h
|
|
|
a6d77e |
new file mode 100644
|
|
|
a6d77e |
index 000000000000..acb839ab889c
|
|
|
a6d77e |
--- /dev/null
|
|
|
a6d77e |
+++ b/kexec/arch/arm64/image-header.h
|
|
|
a6d77e |
@@ -0,0 +1,95 @@
|
|
|
a6d77e |
+/*
|
|
|
a6d77e |
+ * ARM64 binary image support.
|
|
|
a6d77e |
+ * Copyright (C) 2014 Linaro.
|
|
|
a6d77e |
+ */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#if !defined(__ARM64_IMAGE_HEADER_H)
|
|
|
a6d77e |
+#define __ARM64_IMAGE_HEADER_H
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#if !defined(__KERNEL__)
|
|
|
a6d77e |
+#include <stdint.h>
|
|
|
a6d77e |
+#endif
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#if !defined(__ASSEMBLY__)
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+/**
|
|
|
a6d77e |
+ * struct arm64_image_header - arm64 kernel image header.
|
|
|
a6d77e |
+ *
|
|
|
a6d77e |
+ * @pe_sig: Optional PE format 'MZ' signature.
|
|
|
a6d77e |
+ * @branch_code: Reserved for instructions to branch to stext.
|
|
|
a6d77e |
+ * @text_offset: The image load offset in LSB byte order.
|
|
|
a6d77e |
+ * @image_size: An estimated size of the memory image size in LSB byte order.
|
|
|
a6d77e |
+ * @flags: Bit flags:
|
|
|
a6d77e |
+ * Bit 7.0: Image byte order, 1=MSB.
|
|
|
a6d77e |
+ * @reserved_1: Reserved.
|
|
|
a6d77e |
+ * @magic: Magic number, "ARM\x64".
|
|
|
a6d77e |
+ * @pe_header: Optional offset to a PE format header.
|
|
|
a6d77e |
+ **/
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+struct arm64_image_header {
|
|
|
a6d77e |
+ uint8_t pe_sig[2];
|
|
|
a6d77e |
+ uint16_t branch_code[3];
|
|
|
a6d77e |
+ uint64_t text_offset;
|
|
|
a6d77e |
+ uint64_t image_size;
|
|
|
a6d77e |
+ uint8_t flags[8];
|
|
|
a6d77e |
+ uint64_t reserved_1[3];
|
|
|
a6d77e |
+ uint8_t magic[4];
|
|
|
a6d77e |
+ uint32_t pe_header;
|
|
|
a6d77e |
+};
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+static const uint8_t arm64_image_magic[4] = {'A', 'R', 'M', 0x64U};
|
|
|
a6d77e |
+static const uint8_t arm64_image_pe_sig[2] = {'M', 'Z'};
|
|
|
a6d77e |
+static const uint64_t arm64_image_flag_7_be = 0x01U;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+/**
|
|
|
a6d77e |
+ * arm64_header_check_magic - Helper to check the arm64 image header.
|
|
|
a6d77e |
+ *
|
|
|
a6d77e |
+ * Returns non-zero if header is OK.
|
|
|
a6d77e |
+ */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+static inline int arm64_header_check_magic(const struct arm64_image_header *h)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ if (!h)
|
|
|
a6d77e |
+ return 0;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (!h->text_offset)
|
|
|
a6d77e |
+ return 0;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ return (h->magic[0] == arm64_image_magic[0]
|
|
|
a6d77e |
+ && h->magic[1] == arm64_image_magic[1]
|
|
|
a6d77e |
+ && h->magic[2] == arm64_image_magic[2]
|
|
|
a6d77e |
+ && h->magic[3] == arm64_image_magic[3]);
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+/**
|
|
|
a6d77e |
+ * arm64_header_check_pe_sig - Helper to check the arm64 image header.
|
|
|
a6d77e |
+ *
|
|
|
a6d77e |
+ * Returns non-zero if 'MZ' signature is found.
|
|
|
a6d77e |
+ */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+static inline int arm64_header_check_pe_sig(const struct arm64_image_header *h)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ if (!h)
|
|
|
a6d77e |
+ return 0;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ return (h->pe_sig[0] == arm64_image_pe_sig[0]
|
|
|
a6d77e |
+ && h->pe_sig[1] == arm64_image_pe_sig[1]);
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+/**
|
|
|
a6d77e |
+ * arm64_header_check_msb - Helper to check the arm64 image header.
|
|
|
a6d77e |
+ *
|
|
|
a6d77e |
+ * Returns non-zero if the image was built as big endian.
|
|
|
a6d77e |
+ */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+static inline int arm64_header_check_msb(const struct arm64_image_header *h)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ if (!h)
|
|
|
a6d77e |
+ return 0;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ return !!(h->flags[7] & arm64_image_flag_7_be);
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#endif /* !defined(__ASSEMBLY__) */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#endif
|
|
|
a6d77e |
diff --git a/kexec/arch/arm64/include/arch/options.h b/kexec/arch/arm64/include/arch/options.h
|
|
|
a6d77e |
new file mode 100644
|
|
|
a6d77e |
index 000000000000..51afc5f1f6f2
|
|
|
a6d77e |
--- /dev/null
|
|
|
a6d77e |
+++ b/kexec/arch/arm64/include/arch/options.h
|
|
|
a6d77e |
@@ -0,0 +1,44 @@
|
|
|
a6d77e |
+#if !defined(KEXEC_ARCH_ARM64_OPTIONS_H)
|
|
|
a6d77e |
+#define KEXEC_ARCH_ARM64_OPTIONS_H
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#define OPT_APPEND ((OPT_MAX)+0)
|
|
|
a6d77e |
+#define OPT_DTB ((OPT_MAX)+1)
|
|
|
a6d77e |
+#define OPT_INITRD ((OPT_MAX)+2)
|
|
|
a6d77e |
+#define OPT_LITE ((OPT_MAX)+3)
|
|
|
a6d77e |
+#define OPT_PORT ((OPT_MAX)+4)
|
|
|
a6d77e |
+#define OPT_ARCH_MAX ((OPT_MAX)+5)
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#define KEXEC_ARCH_OPTIONS \
|
|
|
a6d77e |
+ KEXEC_OPTIONS \
|
|
|
a6d77e |
+ { "append", 1, NULL, OPT_APPEND }, \
|
|
|
a6d77e |
+ { "command-line", 1, NULL, OPT_APPEND }, \
|
|
|
a6d77e |
+ { "dtb", 1, NULL, OPT_DTB }, \
|
|
|
a6d77e |
+ { "initrd", 1, NULL, OPT_INITRD }, \
|
|
|
a6d77e |
+ { "lite", 0, NULL, OPT_LITE }, \
|
|
|
a6d77e |
+ { "port", 1, NULL, OPT_PORT }, \
|
|
|
a6d77e |
+ { "ramdisk", 1, NULL, OPT_INITRD }, \
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR /* Only accept long arch options. */
|
|
|
a6d77e |
+#define KEXEC_ALL_OPTIONS KEXEC_ARCH_OPTIONS
|
|
|
a6d77e |
+#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+static const char arm64_opts_usage[] __attribute__ ((unused)) =
|
|
|
a6d77e |
+" --append=STRING Set the kernel command line to STRING.\n"
|
|
|
a6d77e |
+" --command-line=STRING Set the kernel command line to STRING.\n"
|
|
|
a6d77e |
+" --dtb=FILE Use FILE as the device tree blob.\n"
|
|
|
a6d77e |
+" --initrd=FILE Use FILE as the kernel initial ramdisk.\n"
|
|
|
a6d77e |
+" --lite Fast reboot, no memory integrity checks - currently NOT SUPPORTED.\n");
|
|
|
a6d77e |
+" --port=ADDRESS Purgatory output to port ADDRESS.\n"
|
|
|
a6d77e |
+" --ramdisk=FILE Use FILE as the kernel initial ramdisk.\n";
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+struct arm64_opts {
|
|
|
a6d77e |
+ const char *command_line;
|
|
|
a6d77e |
+ const char *dtb;
|
|
|
a6d77e |
+ const char *initrd;
|
|
|
a6d77e |
+ uint64_t port;
|
|
|
a6d77e |
+ int lite;
|
|
|
a6d77e |
+};
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+extern struct arm64_opts arm64_opts;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#endif
|
|
|
a6d77e |
diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c
|
|
|
a6d77e |
new file mode 100644
|
|
|
a6d77e |
index 000000000000..0860810b6e86
|
|
|
a6d77e |
--- /dev/null
|
|
|
a6d77e |
+++ b/kexec/arch/arm64/kexec-arm64.c
|
|
|
a6d77e |
@@ -0,0 +1,1041 @@
|
|
|
a6d77e |
+/*
|
|
|
a6d77e |
+ * ARM64 kexec.
|
|
|
a6d77e |
+ */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#define _GNU_SOURCE
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#include <assert.h>
|
|
|
a6d77e |
+#include <ctype.h>
|
|
|
a6d77e |
+#include <dirent.h>
|
|
|
a6d77e |
+#include <errno.h>
|
|
|
a6d77e |
+#include <getopt.h>
|
|
|
a6d77e |
+#include <inttypes.h>
|
|
|
a6d77e |
+#include <libfdt.h>
|
|
|
a6d77e |
+#include <limits.h>
|
|
|
a6d77e |
+#include <stdio.h>
|
|
|
a6d77e |
+#include <stddef.h>
|
|
|
a6d77e |
+#include <stdlib.h>
|
|
|
a6d77e |
+#include <string.h>
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#include <sys/stat.h>
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#include <linux/elf.h>
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#include "dt-ops.h"
|
|
|
a6d77e |
+#include "kexec.h"
|
|
|
a6d77e |
+#include "crashdump.h"
|
|
|
a6d77e |
+#include "crashdump-arm64.h"
|
|
|
a6d77e |
+#include "kexec-arm64.h"
|
|
|
a6d77e |
+#include "fs2dt.h"
|
|
|
a6d77e |
+#include "kexec-syscall.h"
|
|
|
a6d77e |
+#include "arch/options.h"
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+/* Global varables the core kexec routines expect. */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+unsigned char reuse_initrd;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+off_t initrd_base;
|
|
|
a6d77e |
+off_t initrd_size;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+const struct arch_map_entry arches[] = {
|
|
|
a6d77e |
+ { "aarch64", KEXEC_ARCH_ARM64 },
|
|
|
a6d77e |
+ { "aarch64_be", KEXEC_ARCH_ARM64 },
|
|
|
a6d77e |
+ { NULL, 0 },
|
|
|
a6d77e |
+};
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+/* arm64 global varables. */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+struct arm64_opts arm64_opts;
|
|
|
a6d77e |
+struct arm64_mem arm64_mem = {
|
|
|
a6d77e |
+ .memstart = UINT64_MAX,
|
|
|
a6d77e |
+};
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+static void set_memstart(uint64_t v)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ if (arm64_mem.memstart == UINT64_MAX || v < arm64_mem.memstart)
|
|
|
a6d77e |
+ arm64_mem.memstart = v;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+static int check_memstart(void)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ return arm64_mem.memstart != UINT64_MAX;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+void arch_usage(void)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ dbgprintf("Build time: %s : %s\n", __DATE__, __TIME__);
|
|
|
a6d77e |
+ printf(arm64_opts_usage);
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+int arch_process_options(int argc, char **argv)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ static const char short_options[] = KEXEC_OPT_STR "";
|
|
|
a6d77e |
+ static const struct option options[] = {
|
|
|
a6d77e |
+ KEXEC_ARCH_OPTIONS
|
|
|
a6d77e |
+ { 0 }
|
|
|
a6d77e |
+ };
|
|
|
a6d77e |
+ int opt;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ for (opt = 0; opt != -1; ) {
|
|
|
a6d77e |
+ opt = getopt_long(argc, argv, short_options, options, 0);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ switch (opt) {
|
|
|
a6d77e |
+ case OPT_APPEND:
|
|
|
a6d77e |
+ arm64_opts.command_line = optarg;
|
|
|
a6d77e |
+ break;
|
|
|
a6d77e |
+ case OPT_DTB:
|
|
|
a6d77e |
+ arm64_opts.dtb = optarg;
|
|
|
a6d77e |
+ break;
|
|
|
a6d77e |
+ case OPT_INITRD:
|
|
|
a6d77e |
+ arm64_opts.initrd = optarg;
|
|
|
a6d77e |
+ break;
|
|
|
a6d77e |
+ case OPT_LITE:
|
|
|
a6d77e |
+ arm64_opts.lite = 1;
|
|
|
a6d77e |
+ fprintf(stderr, "kexec: --lite option currently NOT SUPPORTED.\n");
|
|
|
a6d77e |
+ break;
|
|
|
a6d77e |
+ case OPT_PORT:
|
|
|
a6d77e |
+ arm64_opts.port = strtoull(optarg, NULL, 0);
|
|
|
a6d77e |
+ break;
|
|
|
a6d77e |
+ default:
|
|
|
a6d77e |
+ break; /* Ignore core and unknown options. */
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ kexec_debug = 1; // FIXME: for debugging only.
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dbgprintf("%s:%d: command_line: %s\n", __func__, __LINE__,
|
|
|
a6d77e |
+ arm64_opts.command_line);
|
|
|
a6d77e |
+ dbgprintf("%s:%d: initrd: %s\n", __func__, __LINE__,
|
|
|
a6d77e |
+ arm64_opts.initrd);
|
|
|
a6d77e |
+ dbgprintf("%s:%d: dtb: %s\n", __func__, __LINE__, arm64_opts.dtb);
|
|
|
a6d77e |
+ dbgprintf("%s:%d: lite: %d\n", __func__, __LINE__, arm64_opts.lite);
|
|
|
a6d77e |
+ dbgprintf("%s:%d: port: 0x%" PRIx64 "\n", __func__, __LINE__,
|
|
|
a6d77e |
+ arm64_opts.port);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ return 0;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+struct dtb {
|
|
|
a6d77e |
+ char *buf;
|
|
|
a6d77e |
+ off_t size;
|
|
|
a6d77e |
+ const char *name;
|
|
|
a6d77e |
+ const char *path;
|
|
|
a6d77e |
+};
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+static void dump_reservemap(const struct dtb *dtb)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ int i;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ for (i = 0; ; i++) {
|
|
|
a6d77e |
+ uint64_t address;
|
|
|
a6d77e |
+ uint64_t size;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ fdt_get_mem_rsv(dtb->buf, i, &address, &size);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (!size)
|
|
|
a6d77e |
+ break;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dbgprintf("%s: %s {%" PRIx64 ", %" PRIx64 "}\n", __func__,
|
|
|
a6d77e |
+ dtb->name, address, size);
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+enum cpu_enable_method {
|
|
|
a6d77e |
+ cpu_enable_method_unknown,
|
|
|
a6d77e |
+ cpu_enable_method_psci,
|
|
|
a6d77e |
+ cpu_enable_method_spin_table,
|
|
|
a6d77e |
+};
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+/**
|
|
|
a6d77e |
+ * struct cpu_properties - Various properties from a device tree cpu node.
|
|
|
a6d77e |
+ *
|
|
|
a6d77e |
+ * These properties will be valid over a dtb re-size.
|
|
|
a6d77e |
+ */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+struct cpu_properties {
|
|
|
a6d77e |
+ uint64_t hwid;
|
|
|
a6d77e |
+ uint64_t cpu_release_addr;
|
|
|
a6d77e |
+ char node_path[128];
|
|
|
a6d77e |
+ char enable_method[128];
|
|
|
a6d77e |
+ enum cpu_enable_method type;
|
|
|
a6d77e |
+};
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+/**
|
|
|
a6d77e |
+ * read_cpu_properties - Helper to read the device tree cpu properties.
|
|
|
a6d77e |
+ */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+static int read_cpu_properties(struct cpu_properties *cp,
|
|
|
a6d77e |
+ const struct dtb *dtb, int node_offset, unsigned int address_cells)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ int result;
|
|
|
a6d77e |
+ const void *data;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = fdt_get_path(dtb->buf, node_offset, cp->node_path,
|
|
|
a6d77e |
+ sizeof(cp->node_path));
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result < 0) {
|
|
|
a6d77e |
+ fprintf(stderr, "kexec: %s:%d: %s: fdt_get_path failed: %s\n",
|
|
|
a6d77e |
+ __func__, __LINE__, dtb->name, fdt_strerror(result));
|
|
|
a6d77e |
+ return result;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ data = fdt_getprop(dtb->buf, node_offset, "device_type", &result);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (!data) {
|
|
|
a6d77e |
+ dbgprintf("%s: %s (%s) read device_type failed: %s\n",
|
|
|
a6d77e |
+ __func__, dtb->name, cp->node_path,
|
|
|
a6d77e |
+ fdt_strerror(result));
|
|
|
a6d77e |
+ return result == -FDT_ERR_NOTFOUND ? 0 : result;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (strcmp(data, "cpu")) {
|
|
|
a6d77e |
+ dbgprintf("%s: %s (%s): '%s'\n", __func__, dtb->name,
|
|
|
a6d77e |
+ cp->node_path, (const char *)data);
|
|
|
a6d77e |
+ return 0;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ data = fdt_getprop(dtb->buf, node_offset, "reg", &result);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (!data) {
|
|
|
a6d77e |
+ fprintf(stderr, "kexec: %s:%d: read hwid failed: %s\n",
|
|
|
a6d77e |
+ __func__, __LINE__, fdt_strerror(result));
|
|
|
a6d77e |
+ return result;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ cp->hwid = (address_cells == 1) ? fdt32_to_cpu(*(uint32_t *)data) :
|
|
|
a6d77e |
+ fdt64_to_cpu(*(uint64_t *)data);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ data = fdt_getprop(dtb->buf, node_offset, "enable-method", &result);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (!data) {
|
|
|
a6d77e |
+ fprintf(stderr,
|
|
|
a6d77e |
+ "kexec: %s:%d: read enable_method failed: %s\n",
|
|
|
a6d77e |
+ __func__, __LINE__, fdt_strerror(result));
|
|
|
a6d77e |
+ return result;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ strncpy(cp->enable_method, data, sizeof(cp->enable_method));
|
|
|
a6d77e |
+ cp->enable_method[sizeof(cp->enable_method) - 1] = 0;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (!strcmp(cp->enable_method, "psci")) {
|
|
|
a6d77e |
+ cp->type = cpu_enable_method_psci;
|
|
|
a6d77e |
+ return 1;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (strcmp(cp->enable_method, "spin-table")) {
|
|
|
a6d77e |
+ cp->type = cpu_enable_method_unknown;
|
|
|
a6d77e |
+ return -1;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ cp->type = cpu_enable_method_spin_table;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ data = fdt_getprop(dtb->buf, node_offset, "cpu-release-addr", &result);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (!data) {
|
|
|
a6d77e |
+ fprintf(stderr, "kexec: %s:%d: "
|
|
|
a6d77e |
+ "read cpu-release-addr failed: %s\n",
|
|
|
a6d77e |
+ __func__, __LINE__, fdt_strerror(result));
|
|
|
a6d77e |
+ return result;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ cp->cpu_release_addr = fdt64_to_cpu(*(uint64_t *)data);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ return 1;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+static int check_cpu_properties(const struct cpu_properties *cp_1,
|
|
|
a6d77e |
+ const struct cpu_properties *cp_2)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ assert(cp_1->hwid == cp_2->hwid);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (cp_1->type != cp_2->type) {
|
|
|
a6d77e |
+ fprintf(stderr,
|
|
|
a6d77e |
+ "%s:%d: hwid-%" PRIx64 ": "
|
|
|
a6d77e |
+ "Error: Different enable methods: %s -> %s\n",
|
|
|
a6d77e |
+ __func__, __LINE__, cp_1->hwid, cp_1->enable_method,
|
|
|
a6d77e |
+ cp_2->enable_method);
|
|
|
a6d77e |
+ return -EINVAL;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (cp_1->type != cpu_enable_method_psci
|
|
|
a6d77e |
+ && cp_1->type != cpu_enable_method_spin_table) {
|
|
|
a6d77e |
+ fprintf(stderr,
|
|
|
a6d77e |
+ "%s:%d: hwid-%" PRIx64 ": "
|
|
|
a6d77e |
+ "Warning: Unknown enable method: %s.\n",
|
|
|
a6d77e |
+ __func__, __LINE__, cp_1->hwid,
|
|
|
a6d77e |
+ cp_1->enable_method);
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (cp_1->type == cpu_enable_method_spin_table) {
|
|
|
a6d77e |
+ if (cp_1->cpu_release_addr != cp_2->cpu_release_addr) {
|
|
|
a6d77e |
+ fprintf(stderr, "%s:%d: hwid-%" PRIx64 ": "
|
|
|
a6d77e |
+ "Error: Different cpu-release-addr: "
|
|
|
a6d77e |
+ "%" PRIx64 " -> %" PRIx64 ".\n",
|
|
|
a6d77e |
+ __func__, __LINE__,
|
|
|
a6d77e |
+ cp_1->hwid,
|
|
|
a6d77e |
+ cp_2->cpu_release_addr,
|
|
|
a6d77e |
+ cp_1->cpu_release_addr);
|
|
|
a6d77e |
+ return -EINVAL;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dbgprintf("%s: hwid-%" PRIx64 ": OK\n", __func__, cp_1->hwid);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ return 0;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+struct cpu_info {
|
|
|
a6d77e |
+ unsigned int cpu_count;
|
|
|
a6d77e |
+ struct cpu_properties *cp;
|
|
|
a6d77e |
+};
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+static int read_cpu_info(struct cpu_info *info, const struct dtb *dtb)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ int i;
|
|
|
a6d77e |
+ int offset;
|
|
|
a6d77e |
+ int result;
|
|
|
a6d77e |
+ int depth;
|
|
|
a6d77e |
+ const void *data;
|
|
|
a6d77e |
+ unsigned int address_cells;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ offset = fdt_subnode_offset(dtb->buf, 0, "cpus");
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (offset < 0) {
|
|
|
a6d77e |
+ fprintf(stderr, "kexec: %s:%d: read cpus node failed: %s\n",
|
|
|
a6d77e |
+ __func__, __LINE__, fdt_strerror(offset));
|
|
|
a6d77e |
+ return offset;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ data = fdt_getprop(dtb->buf, offset, "#address-cells", &result);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (!data) {
|
|
|
a6d77e |
+ fprintf(stderr,
|
|
|
a6d77e |
+ "kexec: %s:%d: read cpus address-cells failed: %s\n",
|
|
|
a6d77e |
+ __func__, __LINE__, fdt_strerror(result));
|
|
|
a6d77e |
+ return result;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ address_cells = fdt32_to_cpu(*(uint32_t *)data);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (address_cells < 1 || address_cells > 2) {
|
|
|
a6d77e |
+ fprintf(stderr,
|
|
|
a6d77e |
+ "kexec: %s:%d: bad cpus address-cells value: %u\n",
|
|
|
a6d77e |
+ __func__, __LINE__, address_cells);
|
|
|
a6d77e |
+ return -EINVAL;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ for (i = 0, depth = 0; ; i++) {
|
|
|
a6d77e |
+ info->cp = realloc(info->cp, (i + 1) * sizeof(*info->cp));
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (!info->cp) {
|
|
|
a6d77e |
+ fprintf(stderr, "kexec: %s:%d: malloc failed: %s\n",
|
|
|
a6d77e |
+ __func__, __LINE__, fdt_strerror(offset));
|
|
|
a6d77e |
+ result = -ENOMEM;
|
|
|
a6d77e |
+ goto on_error;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+next_node:
|
|
|
a6d77e |
+ memset(&info->cp[i], 0, sizeof(*info->cp));
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ offset = fdt_next_node(dtb->buf, offset, &depth);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (offset < 0) {
|
|
|
a6d77e |
+ fprintf(stderr, "kexec: %s:%d: "
|
|
|
a6d77e |
+ "read cpu node failed: %s\n", __func__,
|
|
|
a6d77e |
+ __LINE__, fdt_strerror(offset));
|
|
|
a6d77e |
+ result = offset;
|
|
|
a6d77e |
+ goto on_error;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (depth != 1)
|
|
|
a6d77e |
+ break;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = read_cpu_properties(&info->cp[i], dtb, offset,
|
|
|
a6d77e |
+ address_cells);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result == 0)
|
|
|
a6d77e |
+ goto next_node;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result < 0)
|
|
|
a6d77e |
+ goto on_error;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (info->cp[i].type == cpu_enable_method_psci)
|
|
|
a6d77e |
+ dbgprintf("%s: %s cpu-%d (%s): hwid-%" PRIx64 ", '%s'\n",
|
|
|
a6d77e |
+ __func__, dtb->name, i, info->cp[i].node_path,
|
|
|
a6d77e |
+ info->cp[i].hwid,
|
|
|
a6d77e |
+ info->cp[i].enable_method);
|
|
|
a6d77e |
+ else
|
|
|
a6d77e |
+ dbgprintf("%s: %s cpu-%d (%s): hwid-%" PRIx64 ", '%s', "
|
|
|
a6d77e |
+ "cpu-release-addr %" PRIx64 "\n",
|
|
|
a6d77e |
+ __func__, dtb->name, i, info->cp[i].node_path,
|
|
|
a6d77e |
+ info->cp[i].hwid,
|
|
|
a6d77e |
+ info->cp[i].enable_method,
|
|
|
a6d77e |
+ info->cp[i].cpu_release_addr);
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ info->cpu_count = i;
|
|
|
a6d77e |
+ return 0;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+on_error:
|
|
|
a6d77e |
+ free(info->cp);
|
|
|
a6d77e |
+ info->cp = NULL;
|
|
|
a6d77e |
+ return result;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+static int check_cpu_nodes(const struct dtb *dtb_1, const struct dtb *dtb_2)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ int result;
|
|
|
a6d77e |
+ unsigned int cpu_1;
|
|
|
a6d77e |
+ struct cpu_info info_1;
|
|
|
a6d77e |
+ struct cpu_info info_2;
|
|
|
a6d77e |
+ unsigned int to_process;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ memset(&info_1, 0, sizeof(info_1));
|
|
|
a6d77e |
+ memset(&info_2, 0, sizeof(info_2));
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = read_cpu_info(&info_1, dtb_1);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result)
|
|
|
a6d77e |
+ goto on_exit;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = read_cpu_info(&info_2, dtb_2);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result)
|
|
|
a6d77e |
+ goto on_exit;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ to_process = info_1.cpu_count < info_2.cpu_count
|
|
|
a6d77e |
+ ? info_1.cpu_count : info_2.cpu_count;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ for (cpu_1 = 0; cpu_1 < info_1.cpu_count; cpu_1++) {
|
|
|
a6d77e |
+ struct cpu_properties *cp_1 = &info_1.cp[cpu_1];
|
|
|
a6d77e |
+ unsigned int cpu_2;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ for (cpu_2 = 0; cpu_2 < info_2.cpu_count; cpu_2++) {
|
|
|
a6d77e |
+ struct cpu_properties *cp_2 = &info_2.cp[cpu_2];
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (cp_1->hwid != cp_2->hwid)
|
|
|
a6d77e |
+ continue;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ to_process--;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = check_cpu_properties(cp_1, cp_2);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result)
|
|
|
a6d77e |
+ goto on_exit;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (to_process) {
|
|
|
a6d77e |
+ fprintf(stderr, "kexec: %s:%d: Warning: "
|
|
|
a6d77e |
+ "Failed to process %u CPUs.\n",
|
|
|
a6d77e |
+ __func__, __LINE__, to_process);
|
|
|
a6d77e |
+ result = -EINVAL;
|
|
|
a6d77e |
+ goto on_exit;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+on_exit:
|
|
|
a6d77e |
+ free(info_1.cp);
|
|
|
a6d77e |
+ free(info_2.cp);
|
|
|
a6d77e |
+ return result;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+static int set_bootargs(struct dtb *dtb, const char *command_line)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ int result;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (!command_line || !command_line[0])
|
|
|
a6d77e |
+ return 0;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = dtb_set_bootargs((char **)&dtb->buf, &dtb->size, command_line);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result)
|
|
|
a6d77e |
+ fprintf(stderr,
|
|
|
a6d77e |
+ "kexec: Set device tree bootargs failed.\n");
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ return result;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+static int read_proc_dtb(struct dtb *dtb, const char *command_line)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ int result;
|
|
|
a6d77e |
+ struct stat s;
|
|
|
a6d77e |
+ static const char path[] = "/proc/device-tree";
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = stat(path, &s);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result) {
|
|
|
a6d77e |
+ dbgprintf("%s: %s\n", __func__, strerror(errno));
|
|
|
a6d77e |
+ return -1;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dtb->path = path;
|
|
|
a6d77e |
+ create_flatten_tree((char **)&dtb->buf, &dtb->size,
|
|
|
a6d77e |
+ (command_line && command_line[0]) ? command_line : NULL);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ return 0;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+static int read_sys_dtb(struct dtb *dtb, const char *command_line)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ int result;
|
|
|
a6d77e |
+ struct stat s;
|
|
|
a6d77e |
+ static const char path[] = "/sys/firmware/fdt";
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = stat(path, &s);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result) {
|
|
|
a6d77e |
+ dbgprintf("%s: %s\n", __func__, strerror(errno));
|
|
|
a6d77e |
+ return -1;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dtb->path = path;
|
|
|
a6d77e |
+ dtb->buf = slurp_file("/sys/firmware/fdt", &dtb->size);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ return set_bootargs(dtb, command_line);
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+static int read_1st_dtb(struct dtb *dtb, const char *command_line)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ int result;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = read_sys_dtb(dtb, command_line);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (!result)
|
|
|
a6d77e |
+ goto on_success;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = read_proc_dtb(dtb, command_line);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (!result)
|
|
|
a6d77e |
+ goto on_success;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ return -1;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+on_success:
|
|
|
a6d77e |
+ dbgprintf("%s: found %s\n", __func__, dtb->path);
|
|
|
a6d77e |
+ return 0;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+static int setup_2nd_dtb(char *command_line, const struct dtb *dtb_1,
|
|
|
a6d77e |
+ struct dtb *dtb_2)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ int result;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = fdt_check_header(dtb_2->buf);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result) {
|
|
|
a6d77e |
+ fprintf(stderr, "kexec: Invalid 2nd device tree.\n");
|
|
|
a6d77e |
+ return -EINVAL;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = set_bootargs(dtb_2, command_line);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dump_reservemap(dtb_2);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ return result;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+static uint64_t read_sink(const char *command_line)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ uint64_t v;
|
|
|
a6d77e |
+ const char *p;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (arm64_opts.port)
|
|
|
a6d77e |
+ return arm64_opts.port;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#if defined(ARM64_DEBUG_PORT)
|
|
|
a6d77e |
+ return (uint64_t)(ARM64_DEBUG_PORT);
|
|
|
a6d77e |
+#endif
|
|
|
a6d77e |
+ if (!command_line)
|
|
|
a6d77e |
+ return 0;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ p = strstr(command_line, "earlyprintk=");
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (!p)
|
|
|
a6d77e |
+ return 0;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ while (*p != ',')
|
|
|
a6d77e |
+ p++;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ p++;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ while (isspace(*p))
|
|
|
a6d77e |
+ p++;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (*p == 0)
|
|
|
a6d77e |
+ return 0;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ errno = 0;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ v = strtoull(p, NULL, 0);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (errno)
|
|
|
a6d77e |
+ return 0;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ return v;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+/**
|
|
|
a6d77e |
+ * arm64_load_other_segments - Prepare the dtb, initrd and purgatory segments.
|
|
|
a6d77e |
+ */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+int arm64_load_other_segments(struct kexec_info *info,
|
|
|
a6d77e |
+ unsigned long kernel_entry)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ int result;
|
|
|
a6d77e |
+ struct mem_ehdr ehdr;
|
|
|
a6d77e |
+ unsigned long dtb_max;
|
|
|
a6d77e |
+ unsigned long dtb_base;
|
|
|
a6d77e |
+ char *initrd_buf = NULL;
|
|
|
a6d77e |
+ uint64_t purgatory_sink;
|
|
|
a6d77e |
+ unsigned long purgatory_base;
|
|
|
a6d77e |
+ struct dtb dtb_1 = {.name = "dtb_1"};
|
|
|
a6d77e |
+ struct dtb dtb_2 = {.name = "dtb_2"};
|
|
|
a6d77e |
+ char command_line[COMMAND_LINE_SIZE] = "";
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (arm64_opts.command_line) {
|
|
|
a6d77e |
+ strncpy(command_line, arm64_opts.command_line,
|
|
|
a6d77e |
+ sizeof(command_line));
|
|
|
a6d77e |
+ command_line[sizeof(command_line) - 1] = 0;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ purgatory_sink = read_sink(command_line);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dbgprintf("%s:%d: purgatory sink: 0x%" PRIx64 "\n", __func__, __LINE__,
|
|
|
a6d77e |
+ purgatory_sink);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (arm64_opts.dtb) {
|
|
|
a6d77e |
+ dtb_2.buf = slurp_file(arm64_opts.dtb, &dtb_2.size);
|
|
|
a6d77e |
+ assert(dtb_2.buf);
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = read_1st_dtb(&dtb_1, command_line);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result && !arm64_opts.dtb) {
|
|
|
a6d77e |
+ fprintf(stderr, "kexec: Error: No device tree available.\n");
|
|
|
a6d77e |
+ return result;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result && arm64_opts.dtb)
|
|
|
a6d77e |
+ dtb_1 = dtb_2;
|
|
|
a6d77e |
+ else if (!result && !arm64_opts.dtb)
|
|
|
a6d77e |
+ dtb_2 = dtb_1;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = setup_2nd_dtb(command_line, &dtb_1, &dtb_2);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result)
|
|
|
a6d77e |
+ return result;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = check_cpu_nodes(&dtb_1, &dtb_2);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result)
|
|
|
a6d77e |
+ return result;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ /*
|
|
|
a6d77e |
+ * Put the DTB after the kernel with an alignment of 128 KiB, giving
|
|
|
a6d77e |
+ * a max supported DTB size of 128 KiB (worst case). Also add 2 KiB
|
|
|
a6d77e |
+ * to the DTB size for any DTB growth.
|
|
|
a6d77e |
+ */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dtb_max = dtb_2.size + 2 * 1024;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dtb_base = locate_hole(info, dtb_max, 128UL * 1024,
|
|
|
a6d77e |
+ arm64_mem.memstart + arm64_mem.text_offset
|
|
|
a6d77e |
+ + arm64_mem.image_size,
|
|
|
a6d77e |
+ _ALIGN_UP(arm64_mem.memstart + arm64_mem.text_offset,
|
|
|
a6d77e |
+ 512UL * 1024 * 1024),
|
|
|
a6d77e |
+ 1);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dbgprintf("dtb: base %lx, size %lxh (%ld)\n", dtb_base, dtb_2.size,
|
|
|
a6d77e |
+ dtb_2.size);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (dtb_base == ULONG_MAX)
|
|
|
a6d77e |
+ return -ENOMEM;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ purgatory_base = dtb_base + dtb_2.size;
|
|
|
a6d77e |
+ initrd_base = 0;
|
|
|
a6d77e |
+ initrd_size = 0;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (arm64_opts.initrd) {
|
|
|
a6d77e |
+ initrd_buf = slurp_file(arm64_opts.initrd, &initrd_size);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (!initrd_buf)
|
|
|
a6d77e |
+ fprintf(stderr, "kexec: Empty ramdisk file.\n");
|
|
|
a6d77e |
+ else {
|
|
|
a6d77e |
+ /* Put the initrd after the DTB with an alignment of
|
|
|
a6d77e |
+ * page size. */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ initrd_base = locate_hole(info, initrd_size, 0,
|
|
|
a6d77e |
+ dtb_base + dtb_max, -1, 1);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dbgprintf("initrd: base %lx, size %lxh (%ld)\n",
|
|
|
a6d77e |
+ initrd_base, initrd_size, initrd_size);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (initrd_base == ULONG_MAX)
|
|
|
a6d77e |
+ return -ENOMEM;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = dtb_set_initrd((char **)&dtb_2.buf,
|
|
|
a6d77e |
+ &dtb_2.size, initrd_base,
|
|
|
a6d77e |
+ initrd_base + initrd_size);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result)
|
|
|
a6d77e |
+ return result;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ purgatory_base = initrd_base + initrd_size;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (dtb_2.size > dtb_max) {
|
|
|
a6d77e |
+ fprintf(stderr, "%s: Error: Too many DTB mods.\n", __func__);
|
|
|
a6d77e |
+ return -EINVAL;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ add_segment_phys_virt(info, dtb_2.buf, dtb_2.size, dtb_base,
|
|
|
a6d77e |
+ dtb_2.size, 0);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (arm64_opts.initrd)
|
|
|
a6d77e |
+ add_segment_phys_virt(info, initrd_buf, initrd_size,
|
|
|
a6d77e |
+ initrd_base, initrd_size, 0);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (arm64_opts.lite) {
|
|
|
a6d77e |
+ fprintf(stderr, "kexec: --lite option currently NOT SUPPORTED.\n");
|
|
|
a6d77e |
+ return -ENOSYS;
|
|
|
a6d77e |
+ } else {
|
|
|
a6d77e |
+ result = build_elf_rel_info(purgatory, purgatory_size, &ehdr,
|
|
|
a6d77e |
+ 0);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result < 0) {
|
|
|
a6d77e |
+ fprintf(stderr, "%s: Error: "
|
|
|
a6d77e |
+ "build_elf_rel_info failed.\n", __func__);
|
|
|
a6d77e |
+ return -EBADF;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
|
|
|
a6d77e |
+ purgatory_base, ULONG_MAX, 1, 0);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ info->entry = (void *)elf_rel_get_addr(&info->rhdr,
|
|
|
a6d77e |
+ "purgatory_start");
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ elf_rel_set_symbol(&info->rhdr, "arm64_sink", &purgatory_sink,
|
|
|
a6d77e |
+ sizeof(purgatory_sink));
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ elf_rel_set_symbol(&info->rhdr, "arm64_kernel_entry",
|
|
|
a6d77e |
+ &kernel_entry, sizeof(kernel_entry));
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ elf_rel_set_symbol(&info->rhdr, "arm64_dtb_addr", &dtb_base,
|
|
|
a6d77e |
+ sizeof(dtb_base));
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ return 0;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+unsigned long virt_to_phys(unsigned long v)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ unsigned long p;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ assert(arm64_mem.page_offset);
|
|
|
a6d77e |
+ assert(check_memstart());
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ p = v - arm64_mem.page_offset + arm64_mem.memstart;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dbgprintf("%s: %016lx -> %016lx\n", __func__, v, p);
|
|
|
a6d77e |
+ return p;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+unsigned long phys_to_virt(struct crash_elf_info *UNUSED(elf_info),
|
|
|
a6d77e |
+ unsigned long p)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ unsigned long v;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ assert(arm64_mem.page_offset);
|
|
|
a6d77e |
+ assert(check_memstart());
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ v = p - arm64_mem.memstart + arm64_mem.page_offset;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dbgprintf("%s: %016lx -> %016lx\n", __func__, p, v);
|
|
|
a6d77e |
+ return p;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+void add_segment(struct kexec_info *info, const void *buf, size_t bufsz,
|
|
|
a6d77e |
+ unsigned long base, size_t memsz)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ add_segment_phys_virt(info, buf, bufsz, base, memsz, 1);
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+int arm64_process_image_header(const struct arm64_image_header *h)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+#if !defined(KERNEL_IMAGE_SIZE)
|
|
|
a6d77e |
+# define KERNEL_IMAGE_SIZE (768 * 1024)
|
|
|
a6d77e |
+#endif
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (!arm64_header_check_magic(h))
|
|
|
a6d77e |
+ return -EINVAL;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (h->image_size) {
|
|
|
a6d77e |
+ arm64_mem.text_offset = le64_to_cpu(h->text_offset);
|
|
|
a6d77e |
+ arm64_mem.image_size = le64_to_cpu(h->image_size);
|
|
|
a6d77e |
+ } else {
|
|
|
a6d77e |
+ /* For 3.16 and older kernels. */
|
|
|
a6d77e |
+ arm64_mem.text_offset = 0x80000;
|
|
|
a6d77e |
+ arm64_mem.image_size = KERNEL_IMAGE_SIZE;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ return 0;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+static int get_memory_ranges_dt(struct memory_range *array, unsigned int *count)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ struct region {uint64_t base; uint64_t size;};
|
|
|
a6d77e |
+ struct dtb dtb = {.name = "range_dtb"};
|
|
|
a6d77e |
+ int offset;
|
|
|
a6d77e |
+ int result;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ *count = 0;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = read_1st_dtb(&dtb, NULL);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result) {
|
|
|
a6d77e |
+ goto on_error;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = fdt_check_header(dtb.buf);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result) {
|
|
|
a6d77e |
+ dbgprintf("%s:%d: %s: fdt_check_header failed:%s\n", __func__,
|
|
|
a6d77e |
+ __LINE__, dtb.path, fdt_strerror(result));
|
|
|
a6d77e |
+ goto on_error;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ for (offset = 0; ; ) {
|
|
|
a6d77e |
+ const struct region *region;
|
|
|
a6d77e |
+ const struct region *end;
|
|
|
a6d77e |
+ int len;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ offset = fdt_subnode_offset(dtb.buf, offset, "memory");
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (offset == -FDT_ERR_NOTFOUND)
|
|
|
a6d77e |
+ break;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (offset <= 0) {
|
|
|
a6d77e |
+ dbgprintf("%s:%d: fdt_subnode_offset failed: %d %s\n",
|
|
|
a6d77e |
+ __func__, __LINE__, offset,
|
|
|
a6d77e |
+ fdt_strerror(offset));
|
|
|
a6d77e |
+ goto on_error;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dbgprintf("%s:%d: node_%d %s\n", __func__, __LINE__, offset,
|
|
|
a6d77e |
+ fdt_get_name(dtb.buf, offset, NULL));
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ region = fdt_getprop(dtb.buf, offset, "reg", &len;;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (region <= 0) {
|
|
|
a6d77e |
+ dbgprintf("%s:%d: fdt_getprop failed: %d %s\n",
|
|
|
a6d77e |
+ __func__, __LINE__, offset,
|
|
|
a6d77e |
+ fdt_strerror(offset));
|
|
|
a6d77e |
+ goto on_error;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ for (end = region + len / sizeof(*region);
|
|
|
a6d77e |
+ region < end && *count < KEXEC_SEGMENT_MAX;
|
|
|
a6d77e |
+ region++) {
|
|
|
a6d77e |
+ struct memory_range r;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ r.type = RANGE_RAM;
|
|
|
a6d77e |
+ r.start = fdt64_to_cpu(region->base);
|
|
|
a6d77e |
+ r.end = r.start + fdt64_to_cpu(region->size);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (!region->size) {
|
|
|
a6d77e |
+ dbgprintf("%s:%d: SKIP: %016llx - %016llx\n",
|
|
|
a6d77e |
+ __func__, __LINE__, r.start, r.end);
|
|
|
a6d77e |
+ continue;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dbgprintf("%s:%d: RAM: %016llx - %016llx\n", __func__,
|
|
|
a6d77e |
+ __LINE__, r.start, r.end);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ array[(*count)++] = r;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ set_memstart(r.start);
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (!*count) {
|
|
|
a6d77e |
+ dbgprintf("%s:%d: %s: No RAM found.\n", __func__, __LINE__,
|
|
|
a6d77e |
+ dtb.path);
|
|
|
a6d77e |
+ goto on_error;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dbgprintf("%s:%d: %s: Success\n", __func__, __LINE__, dtb.path);
|
|
|
a6d77e |
+ result = 0;
|
|
|
a6d77e |
+ goto on_exit;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+on_error:
|
|
|
a6d77e |
+ fprintf(stderr, "%s:%d: %s: Unusable device-tree file\n", __func__,
|
|
|
a6d77e |
+ __LINE__, dtb.path);
|
|
|
a6d77e |
+ result = -1;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+on_exit:
|
|
|
a6d77e |
+ free(dtb.buf);
|
|
|
a6d77e |
+ return result;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+static int get_memory_ranges_iomem(struct memory_range *array,
|
|
|
a6d77e |
+ unsigned int *count)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ const char *iomem;
|
|
|
a6d77e |
+ char line[MAX_LINE];
|
|
|
a6d77e |
+ FILE *fp;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ *count = 0;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ iomem = proc_iomem();
|
|
|
a6d77e |
+ fp = fopen(iomem, "r");
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (!fp) {
|
|
|
a6d77e |
+ fprintf(stderr, "Cannot open %s: %s\n", iomem, strerror(errno));
|
|
|
a6d77e |
+ return -1;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ while(fgets(line, sizeof(line), fp) != 0) {
|
|
|
a6d77e |
+ struct memory_range r;
|
|
|
a6d77e |
+ char *str;
|
|
|
a6d77e |
+ int consumed;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (*count >= KEXEC_SEGMENT_MAX)
|
|
|
a6d77e |
+ break;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (sscanf(line, "%Lx-%Lx : %n", &r.start, &r.end, &consumed)
|
|
|
a6d77e |
+ != 2)
|
|
|
a6d77e |
+ continue;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ str = line + consumed;
|
|
|
a6d77e |
+ r.end++;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (memcmp(str, "System RAM\n", 11)) {
|
|
|
a6d77e |
+ dbgprintf("%s:%d: SKIP: %016Lx - %016Lx : %s", __func__,
|
|
|
a6d77e |
+ __LINE__, r.start, r.end, str);
|
|
|
a6d77e |
+ continue;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ r.type = RANGE_RAM;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dbgprintf("%s:%d: RAM: %016llx - %016llx : %s", __func__,
|
|
|
a6d77e |
+ __LINE__, r.start, r.end, str);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ array[(*count)++] = r;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ set_memstart(r.start);
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ fclose(fp);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (!*count) {
|
|
|
a6d77e |
+ dbgprintf("%s:%d: failed: No RAM found.\n", __func__, __LINE__);
|
|
|
a6d77e |
+ return -1;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dbgprintf("%s:%d: Success\n", __func__, __LINE__);
|
|
|
a6d77e |
+ return 0;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+int get_memory_ranges(struct memory_range **range, int *ranges,
|
|
|
a6d77e |
+ unsigned long kexec_flags)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ static struct memory_range array[KEXEC_SEGMENT_MAX];
|
|
|
a6d77e |
+ unsigned int count;
|
|
|
a6d77e |
+ int result;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = get_memory_ranges_dt(array, &count);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result)
|
|
|
a6d77e |
+ result = get_memory_ranges_iomem(array, &count);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ *range = result ? NULL : array;
|
|
|
a6d77e |
+ *ranges = result ? 0 : count;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ return result;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+struct file_type file_type[] = {
|
|
|
a6d77e |
+ {"elf-arm64", elf_arm64_probe, elf_arm64_load, elf_arm64_usage},
|
|
|
a6d77e |
+ {"image-arm64", image_arm64_probe, image_arm64_load, image_arm64_usage},
|
|
|
a6d77e |
+};
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+int file_types = sizeof(file_type) / sizeof(file_type[0]);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+int arch_compat_trampoline(struct kexec_info *info)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ return 0;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+int machine_verify_elf_rel(struct mem_ehdr *ehdr)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ return (ehdr->e_machine == EM_AARCH64);
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+void machine_apply_elf_rel(struct mem_ehdr *ehdr, unsigned long r_type,
|
|
|
a6d77e |
+ void *ptr, unsigned long address, unsigned long value)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+#if !defined(R_AARCH64_ABS64)
|
|
|
a6d77e |
+# define R_AARCH64_ABS64 257
|
|
|
a6d77e |
+#endif
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#if !defined(R_AARCH64_LD_PREL_LO19)
|
|
|
a6d77e |
+# define R_AARCH64_LD_PREL_LO19 273
|
|
|
a6d77e |
+#endif
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#if !defined(R_AARCH64_ADR_PREL_LO21)
|
|
|
a6d77e |
+# define R_AARCH64_ADR_PREL_LO21 274
|
|
|
a6d77e |
+#endif
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#if !defined(R_AARCH64_JUMP26)
|
|
|
a6d77e |
+# define R_AARCH64_JUMP26 282
|
|
|
a6d77e |
+#endif
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#if !defined(R_AARCH64_CALL26)
|
|
|
a6d77e |
+# define R_AARCH64_CALL26 283
|
|
|
a6d77e |
+#endif
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ uint64_t *location = (uint64_t *)ptr;
|
|
|
a6d77e |
+ uint64_t data = *location;
|
|
|
a6d77e |
+ const char *type = NULL;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ switch(r_type) {
|
|
|
a6d77e |
+ case R_AARCH64_ABS64:
|
|
|
a6d77e |
+ type = "ABS64";
|
|
|
a6d77e |
+ *location += value;
|
|
|
a6d77e |
+ break;
|
|
|
a6d77e |
+ case R_AARCH64_LD_PREL_LO19:
|
|
|
a6d77e |
+ type = "LD_PREL_LO19";
|
|
|
a6d77e |
+ *location += ((value - address) << 3) & 0xffffe0;
|
|
|
a6d77e |
+ break;
|
|
|
a6d77e |
+ case R_AARCH64_ADR_PREL_LO21:
|
|
|
a6d77e |
+ if (value & 3)
|
|
|
a6d77e |
+ die("%s: ERROR Unaligned value: %lx\n", __func__,
|
|
|
a6d77e |
+ value);
|
|
|
a6d77e |
+ type = "ADR_PREL_LO21";
|
|
|
a6d77e |
+ *location += ((value - address) << 3) & 0xffffe0;
|
|
|
a6d77e |
+ break;
|
|
|
a6d77e |
+ case R_AARCH64_JUMP26:
|
|
|
a6d77e |
+ type = "JUMP26";
|
|
|
a6d77e |
+ *location += ((value - address) >> 2) & 0x3ffffff;
|
|
|
a6d77e |
+ break;
|
|
|
a6d77e |
+ case R_AARCH64_CALL26:
|
|
|
a6d77e |
+ type = "CALL26";
|
|
|
a6d77e |
+ *location += ((value - address) >> 2) & 0x3ffffff;
|
|
|
a6d77e |
+ break;
|
|
|
a6d77e |
+ default:
|
|
|
a6d77e |
+ die("%s: ERROR Unknown type: %lu\n", __func__, r_type);
|
|
|
a6d77e |
+ break;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dbgprintf("%s: %s %lx->%lx\n", __func__, type, data, *location);
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+void arch_reuse_initrd(void)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ reuse_initrd = 1;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+void arch_update_purgatory(struct kexec_info *UNUSED(info))
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
diff --git a/kexec/arch/arm64/kexec-arm64.h b/kexec/arch/arm64/kexec-arm64.h
|
|
|
a6d77e |
new file mode 100644
|
|
|
a6d77e |
index 000000000000..057acf313b49
|
|
|
a6d77e |
--- /dev/null
|
|
|
a6d77e |
+++ b/kexec/arch/arm64/kexec-arm64.h
|
|
|
a6d77e |
@@ -0,0 +1,51 @@
|
|
|
a6d77e |
+/*
|
|
|
a6d77e |
+ * ARM64 kexec.
|
|
|
a6d77e |
+ */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#if !defined(KEXEC_ARM64_H)
|
|
|
a6d77e |
+#define KEXEC_ARM64_H
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#include <stdbool.h>
|
|
|
a6d77e |
+#include <sys/types.h>
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#include "image-header.h"
|
|
|
a6d77e |
+#include "kexec.h"
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#define KEXEC_SEGMENT_MAX 16
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#define BOOT_BLOCK_VERSION 17
|
|
|
a6d77e |
+#define BOOT_BLOCK_LAST_COMP_VERSION 16
|
|
|
a6d77e |
+#define COMMAND_LINE_SIZE 512
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+int elf_arm64_probe(const char *kernel_buf, off_t kernel_size);
|
|
|
a6d77e |
+int elf_arm64_load(int argc, char **argv, const char *kernel_buf,
|
|
|
a6d77e |
+ off_t kernel_size, struct kexec_info *info);
|
|
|
a6d77e |
+void elf_arm64_usage(void);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+int image_arm64_probe(const char *kernel_buf, off_t kernel_size);
|
|
|
a6d77e |
+int image_arm64_load(int argc, char **argv, const char *kernel_buf,
|
|
|
a6d77e |
+ off_t kernel_size, struct kexec_info *info);
|
|
|
a6d77e |
+void image_arm64_usage(void);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+struct memory_ranges usablemem_rgns;
|
|
|
a6d77e |
+off_t initrd_base;
|
|
|
a6d77e |
+off_t initrd_size;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+/**
|
|
|
a6d77e |
+ * struct arm64_mem - Memory layout info.
|
|
|
a6d77e |
+ */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+struct arm64_mem {
|
|
|
a6d77e |
+ uint64_t text_offset;
|
|
|
a6d77e |
+ uint64_t image_size;
|
|
|
a6d77e |
+ uint64_t page_offset;
|
|
|
a6d77e |
+ uint64_t memstart;
|
|
|
a6d77e |
+};
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+extern struct arm64_mem arm64_mem;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+int arm64_process_image_header(const struct arm64_image_header *h);
|
|
|
a6d77e |
+int arm64_load_other_segments(struct kexec_info *info,
|
|
|
a6d77e |
+ unsigned long kernel_entry);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#endif
|
|
|
a6d77e |
diff --git a/kexec/arch/arm64/kexec-elf-arm64.c b/kexec/arch/arm64/kexec-elf-arm64.c
|
|
|
a6d77e |
new file mode 100644
|
|
|
a6d77e |
index 000000000000..13dc5e2724d9
|
|
|
a6d77e |
--- /dev/null
|
|
|
a6d77e |
+++ b/kexec/arch/arm64/kexec-elf-arm64.c
|
|
|
a6d77e |
@@ -0,0 +1,123 @@
|
|
|
a6d77e |
+/*
|
|
|
a6d77e |
+ * ARM64 kexec elf support.
|
|
|
a6d77e |
+ */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#define _GNU_SOURCE
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#include <assert.h>
|
|
|
a6d77e |
+#include <errno.h>
|
|
|
a6d77e |
+#include <getopt.h>
|
|
|
a6d77e |
+#include <libfdt.h>
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#include <linux/elf.h>
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#include "dt-ops.h"
|
|
|
a6d77e |
+#include "crashdump-arm64.h"
|
|
|
a6d77e |
+#include "kexec-arm64.h"
|
|
|
a6d77e |
+#include "fs2dt.h"
|
|
|
a6d77e |
+#include "kexec-syscall.h"
|
|
|
a6d77e |
+#include "arch/options.h"
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+int elf_arm64_probe(const char *kernel_buf, off_t kernel_size)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ int result;
|
|
|
a6d77e |
+ struct mem_ehdr ehdr;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result < 0) {
|
|
|
a6d77e |
+ dbgprintf("%s: Not an ELF executable.\n", __func__);
|
|
|
a6d77e |
+ goto on_exit;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (ehdr.e_machine != EM_AARCH64) {
|
|
|
a6d77e |
+ dbgprintf("%s: Not an AARCH64 ELF executable.\n", __func__);
|
|
|
a6d77e |
+ result = -EINVAL;
|
|
|
a6d77e |
+ goto on_exit;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = 0;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+on_exit:
|
|
|
a6d77e |
+ free_elf_info(&ehdr);
|
|
|
a6d77e |
+ return result;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+int elf_arm64_load(int argc, char **argv, const char *kernel_buf,
|
|
|
a6d77e |
+ off_t kernel_size, struct kexec_info *info)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ int result;
|
|
|
a6d77e |
+ struct mem_ehdr ehdr;
|
|
|
a6d77e |
+ bool found_header;
|
|
|
a6d77e |
+ int i;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
|
|
|
a6d77e |
+ fprintf(stderr, "kexec: kdump not yet supported on arm64\n");
|
|
|
a6d77e |
+ return -EINVAL;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result < 0) {
|
|
|
a6d77e |
+ dbgprintf("%s: build_elf_exec_info failed\n", __func__);
|
|
|
a6d77e |
+ goto exit;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ /* Find and process the arm64 image header. */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ for (i = 0, found_header = false; i < ehdr.e_phnum; i++) {
|
|
|
a6d77e |
+ struct mem_phdr *phdr = &ehdr.e_phdr[i];
|
|
|
a6d77e |
+ const struct arm64_image_header *h;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (phdr->p_type != PT_LOAD)
|
|
|
a6d77e |
+ continue;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ h = (const struct arm64_image_header *)(kernel_buf
|
|
|
a6d77e |
+ + phdr->p_offset);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (arm64_process_image_header(h))
|
|
|
a6d77e |
+ continue;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ found_header = true;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ arm64_mem.page_offset = phdr->p_vaddr - arm64_mem.text_offset;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dbgprintf("%s: PE format: %s\n", __func__,
|
|
|
a6d77e |
+ (arm64_header_check_pe_sig(h) ? "yes" : "no"));
|
|
|
a6d77e |
+ dbgprintf("p_vaddr: %016llx\n", phdr->p_vaddr);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ break;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (!found_header) {
|
|
|
a6d77e |
+ fprintf(stderr, "kexec: Bad arm64 image header.\n");
|
|
|
a6d77e |
+ result = -EINVAL;
|
|
|
a6d77e |
+ goto exit;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = elf_exec_load(&ehdr, info);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (result) {
|
|
|
a6d77e |
+ fprintf(stderr, "kexec: Elf load failed.\n");
|
|
|
a6d77e |
+ goto exit;
|
|
|
a6d77e |
+ }
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dbgprintf("%s: text_offset: %016lx\n", __func__, arm64_mem.text_offset);
|
|
|
a6d77e |
+ dbgprintf("%s: image_size: %016lx\n", __func__, arm64_mem.image_size);
|
|
|
a6d77e |
+ dbgprintf("%s: page_offset: %016lx\n", __func__, arm64_mem.page_offset);
|
|
|
a6d77e |
+ dbgprintf("%s: memstart: %016lx\n", __func__, arm64_mem.memstart);
|
|
|
a6d77e |
+ dbgprintf("%s: e_entry: %016llx -> %016lx\n", __func__,
|
|
|
a6d77e |
+ ehdr.e_entry, virt_to_phys(ehdr.e_entry));
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ result = arm64_load_other_segments(info, virt_to_phys(ehdr.e_entry));
|
|
|
a6d77e |
+exit:
|
|
|
a6d77e |
+ free_elf_info(&ehdr);
|
|
|
a6d77e |
+ return result;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+void elf_arm64_usage(void)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ printf(
|
|
|
a6d77e |
+" An arm64 ELF file, big or little endian.\n"
|
|
|
a6d77e |
+" Typically vmlinux or a stripped version of vmlinux.\n\n");
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
diff --git a/kexec/arch/arm64/kexec-image-arm64.c b/kexec/arch/arm64/kexec-image-arm64.c
|
|
|
a6d77e |
new file mode 100644
|
|
|
a6d77e |
index 000000000000..b025dc6c0185
|
|
|
a6d77e |
--- /dev/null
|
|
|
a6d77e |
+++ b/kexec/arch/arm64/kexec-image-arm64.c
|
|
|
a6d77e |
@@ -0,0 +1,50 @@
|
|
|
a6d77e |
+/*
|
|
|
a6d77e |
+ * ARM64 kexec binary image support.
|
|
|
a6d77e |
+ */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#define _GNU_SOURCE
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#include <assert.h>
|
|
|
a6d77e |
+#include <errno.h>
|
|
|
a6d77e |
+#include <getopt.h>
|
|
|
a6d77e |
+#include <libfdt.h>
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#include "dt-ops.h"
|
|
|
a6d77e |
+#include "image-header.h"
|
|
|
a6d77e |
+#include "kexec-arm64.h"
|
|
|
a6d77e |
+#include "fs2dt.h"
|
|
|
a6d77e |
+#include "kexec-syscall.h"
|
|
|
a6d77e |
+#include "arch/options.h"
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+int image_arm64_probe(const char *kernel_buf, off_t kernel_size)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ const struct arm64_image_header *h;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (kernel_size < sizeof(struct arm64_image_header))
|
|
|
a6d77e |
+ return -EINVAL;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ h = (const struct arm64_image_header *)(kernel_buf);
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (!arm64_header_check_magic(h))
|
|
|
a6d77e |
+ return -1;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ dbgprintf("%s: PE format: %s\n", __func__,
|
|
|
a6d77e |
+ (arm64_header_check_pe_sig(h) ? "yes" : "no"));
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ fprintf(stderr, "kexec: arm64 binary Image files are currently NOT SUPPORTED.\n");
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ return -1;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+int image_arm64_load(int argc, char **argv, const char *kernel_buf,
|
|
|
a6d77e |
+ off_t kernel_size, struct kexec_info *info)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ return -ENOSYS;
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+void image_arm64_usage(void)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ printf(
|
|
|
a6d77e |
+" An arm64 binary Image file, big or little endian.\n"
|
|
|
a6d77e |
+" This file type is currently NOT SUPPORTED.\n\n");
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
diff --git a/kexec/kexec-syscall.h b/kexec/kexec-syscall.h
|
|
|
a6d77e |
index ce2e20b8b04b..267b75b02272 100644
|
|
|
a6d77e |
--- a/kexec/kexec-syscall.h
|
|
|
a6d77e |
+++ b/kexec/kexec-syscall.h
|
|
|
a6d77e |
@@ -39,8 +39,8 @@
|
|
|
a6d77e |
#ifdef __s390__
|
|
|
a6d77e |
#define __NR_kexec_load 277
|
|
|
a6d77e |
#endif
|
|
|
a6d77e |
-#ifdef __arm__
|
|
|
a6d77e |
-#define __NR_kexec_load __NR_SYSCALL_BASE + 347
|
|
|
a6d77e |
+#if defined(__arm__) || defined(__arm64__)
|
|
|
a6d77e |
+#define __NR_kexec_load __NR_SYSCALL_BASE + 347
|
|
|
a6d77e |
#endif
|
|
|
a6d77e |
#if defined(__mips__)
|
|
|
a6d77e |
#define __NR_kexec_load 4311
|
|
|
a6d77e |
@@ -108,6 +108,8 @@ static inline long kexec_file_load(int kernel_fd, int initrd_fd,
|
|
|
a6d77e |
#define KEXEC_ARCH_PPC64 (21 << 16)
|
|
|
a6d77e |
#define KEXEC_ARCH_IA_64 (50 << 16)
|
|
|
a6d77e |
#define KEXEC_ARCH_ARM (40 << 16)
|
|
|
a6d77e |
+#define KEXEC_ARCH_ARM64 (183 << 16)
|
|
|
a6d77e |
+/* #define KEXEC_ARCH_AARCH64 (183 << 16) */
|
|
|
a6d77e |
#define KEXEC_ARCH_S390 (22 << 16)
|
|
|
a6d77e |
#define KEXEC_ARCH_SH (42 << 16)
|
|
|
a6d77e |
#define KEXEC_ARCH_MIPS_LE (10 << 16)
|
|
|
a6d77e |
@@ -153,5 +155,8 @@ static inline long kexec_file_load(int kernel_fd, int initrd_fd,
|
|
|
a6d77e |
#ifdef __m68k__
|
|
|
a6d77e |
#define KEXEC_ARCH_NATIVE KEXEC_ARCH_68K
|
|
|
a6d77e |
#endif
|
|
|
a6d77e |
+#if defined(__arm64__)
|
|
|
a6d77e |
+#define KEXEC_ARCH_NATIVE KEXEC_ARCH_ARM64
|
|
|
a6d77e |
+#endif
|
|
|
a6d77e |
|
|
|
a6d77e |
#endif /* KEXEC_SYSCALL_H */
|
|
|
a6d77e |
diff --git a/purgatory/Makefile b/purgatory/Makefile
|
|
|
a6d77e |
index 19457029e79f..0c85da6e29b8 100644
|
|
|
a6d77e |
--- a/purgatory/Makefile
|
|
|
a6d77e |
+++ b/purgatory/Makefile
|
|
|
a6d77e |
@@ -18,6 +18,7 @@ dist += purgatory/Makefile $(PURGATORY_SRCS) \
|
|
|
a6d77e |
|
|
|
a6d77e |
include $(srcdir)/purgatory/arch/alpha/Makefile
|
|
|
a6d77e |
include $(srcdir)/purgatory/arch/arm/Makefile
|
|
|
a6d77e |
+include $(srcdir)/purgatory/arch/arm64/Makefile
|
|
|
a6d77e |
include $(srcdir)/purgatory/arch/i386/Makefile
|
|
|
a6d77e |
include $(srcdir)/purgatory/arch/ia64/Makefile
|
|
|
a6d77e |
include $(srcdir)/purgatory/arch/mips/Makefile
|
|
|
a6d77e |
diff --git a/purgatory/arch/arm64/Makefile b/purgatory/arch/arm64/Makefile
|
|
|
a6d77e |
new file mode 100644
|
|
|
a6d77e |
index 000000000000..636abeab17b2
|
|
|
a6d77e |
--- /dev/null
|
|
|
a6d77e |
+++ b/purgatory/arch/arm64/Makefile
|
|
|
a6d77e |
@@ -0,0 +1,18 @@
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+arm64_PURGATORY_EXTRA_CFLAGS = \
|
|
|
a6d77e |
+ -mcmodel=large \
|
|
|
a6d77e |
+ -fno-stack-protector \
|
|
|
a6d77e |
+ -fno-asynchronous-unwind-tables \
|
|
|
a6d77e |
+ -Wundef \
|
|
|
a6d77e |
+ -Werror-implicit-function-declaration \
|
|
|
a6d77e |
+ -Wdeclaration-after-statement \
|
|
|
a6d77e |
+ -Werror=implicit-int \
|
|
|
a6d77e |
+ -Werror=strict-prototypes
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+arm64_PURGATORY_SRCS += \
|
|
|
a6d77e |
+ purgatory/arch/arm64/entry.S \
|
|
|
a6d77e |
+ purgatory/arch/arm64/purgatory-arm64.c
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+dist += \
|
|
|
a6d77e |
+ $(arm64_PURGATORY_SRCS) \
|
|
|
a6d77e |
+ purgatory/arch/arm64/Makefile
|
|
|
a6d77e |
diff --git a/purgatory/arch/arm64/entry.S b/purgatory/arch/arm64/entry.S
|
|
|
a6d77e |
new file mode 100644
|
|
|
a6d77e |
index 000000000000..140e91d87ab1
|
|
|
a6d77e |
--- /dev/null
|
|
|
a6d77e |
+++ b/purgatory/arch/arm64/entry.S
|
|
|
a6d77e |
@@ -0,0 +1,54 @@
|
|
|
a6d77e |
+/*
|
|
|
a6d77e |
+ * ARM64 purgatory.
|
|
|
a6d77e |
+ */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+.macro debug_brk
|
|
|
a6d77e |
+ mov x0, #0x18; /* angel_SWIreason_ReportException */
|
|
|
a6d77e |
+ mov x1, #0x20000;
|
|
|
a6d77e |
+ add x1, x1, #0x20; /* ADP_Stopped_BreakPoint */
|
|
|
a6d77e |
+ hlt #0xf000 /* A64 semihosting */
|
|
|
a6d77e |
+.endm
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+.macro size, sym:req
|
|
|
a6d77e |
+ .size \sym, . - \sym
|
|
|
a6d77e |
+.endm
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+.text
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+.globl purgatory_start
|
|
|
a6d77e |
+purgatory_start:
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ adr x19, .Lstack
|
|
|
a6d77e |
+ mov sp, x19
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ bl purgatory
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+1: debug_brk
|
|
|
a6d77e |
+ b 1b
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+size purgatory_start
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+.align 4
|
|
|
a6d77e |
+ .rept 256
|
|
|
a6d77e |
+ .quad 0
|
|
|
a6d77e |
+ .endr
|
|
|
a6d77e |
+.Lstack:
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+.data
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+.align 3
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+.globl arm64_sink
|
|
|
a6d77e |
+arm64_sink:
|
|
|
a6d77e |
+ .quad 0
|
|
|
a6d77e |
+size arm64_sink
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+.globl arm64_kernel_entry
|
|
|
a6d77e |
+arm64_kernel_entry:
|
|
|
a6d77e |
+ .quad 0
|
|
|
a6d77e |
+size arm64_kernel_entry
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+.globl arm64_dtb_addr
|
|
|
a6d77e |
+arm64_dtb_addr:
|
|
|
a6d77e |
+ .quad 0
|
|
|
a6d77e |
+size arm64_dtb_addr
|
|
|
a6d77e |
diff --git a/purgatory/arch/arm64/purgatory-arm64.c b/purgatory/arch/arm64/purgatory-arm64.c
|
|
|
a6d77e |
new file mode 100644
|
|
|
a6d77e |
index 000000000000..25960c30bd05
|
|
|
a6d77e |
--- /dev/null
|
|
|
a6d77e |
+++ b/purgatory/arch/arm64/purgatory-arm64.c
|
|
|
a6d77e |
@@ -0,0 +1,35 @@
|
|
|
a6d77e |
+/*
|
|
|
a6d77e |
+ * ARM64 purgatory.
|
|
|
a6d77e |
+ */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+#include <stdint.h>
|
|
|
a6d77e |
+#include <purgatory.h>
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+/* Symbols set by kexec. */
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+extern uint32_t *arm64_sink;
|
|
|
a6d77e |
+extern void (*arm64_kernel_entry)(uint64_t);
|
|
|
a6d77e |
+extern uint64_t arm64_dtb_addr;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+void putchar(int ch)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ if (!arm64_sink)
|
|
|
a6d77e |
+ return;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ *arm64_sink = ch;
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+ if (ch == '\n')
|
|
|
a6d77e |
+ *arm64_sink = '\r';
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+void setup_arch(void)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ printf("purgatory: kernel_entry: %lx\n",
|
|
|
a6d77e |
+ (unsigned long)arm64_kernel_entry);
|
|
|
a6d77e |
+ printf("purgatory: dtb: %lx\n", arm64_dtb_addr);
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
+
|
|
|
a6d77e |
+void post_verification_setup_arch(void)
|
|
|
a6d77e |
+{
|
|
|
a6d77e |
+ arm64_kernel_entry(arm64_dtb_addr);
|
|
|
a6d77e |
+}
|
|
|
a6d77e |
--
|
|
|
a6d77e |
2.1.0
|
|
|
a6d77e |
|