[PATCH v2 0/3] use trace to debug large kernel module memory consumption
by Xunlei Pang
The current method for kdump memory debug is to use dracut "rd.memdebug=[0-3]",
it is not enough for debugging kernel modules. For example, when we want to find
out which kernel module consumes a large amount of memory, "rd.memdebug" won't
help too much.
A better way is needed to achieve this requirement, this is very useful for kdump
OOM debugging.
The principle of this patch series is to use kernel trace to track slab and buddy
allocation calls during kernel module loading(module_init), thus we can analyze
all the trace data and get the total memory consumption. As for large slab allocation,
it will probably fall into buddy allocation, thus tracing "mm_page_alloc" alone should
be enough for the purpose.
The trace events include memory calls under "tracing/events/":
kmem/mm_page_alloc
We also inpect the following events to detect the module loading:
module/module_load
module/module_put
We can get the module name and task pid from "module_load" event which
also mark the beginning of the loading, and module_put called by the
same task pid implies the end of the loading. So the memory events
recorded in between by the same task pid are consumed by this module
during loading(i.e. modprobe or module_init()).
With these information, we can record the total memory(the larger, the more
precise) consumption involved by each kernel module loading.
One major flaw of this method is that the trace ring buffer consumes a lot
of memory. If it is too small, old records maybe be overwritten by subsequent
records. The trace ring buffer is set to be 5MB by default, but it can be
overridden by users via the standard kernel boot parameter "trace_buf_size".
Users should increase the crash kernel memory reservation as needed after
setting large trace ring buffer size, in case oom happens during debugging.
Usage:
1)Pass "rd.kodebug" to kdump kernel cmdline using "KDUMP_COMMANDLINE_APPEND" in /etc/sysconfig/kdump.
2)Pass the extra "trace_buf_size=nn[KMG]" to specify trace ring buffer size(per cpu) as needed.
As an example, it prints out something below on my kvm machine:
== debug_mem for kernel modules during loading begin ==
4 pages consumed by "dm_mod" [load finished]
0 pages consumed by "dm_log" [load finished]
0 pages consumed by "dm_region_hash" [load finished]
0 pages consumed by "dm_mirror" [load finished]
9 pages consumed by "sunrpc" [load finished]
24 pages consumed by "floppy" [load finished]
0 pages consumed by "libata" [load finished]
0 pages consumed by "i2c_core" [load finished]
27 pages consumed by "ata_piix" [load finished]
0 pages consumed by "drm" [load finished]
1 pages consumed by "ttm" [load finished]
0 pages consumed by "drm_kms_helper" [load finished]
1604 pages consumed by "qxl" [load finished]
0 pages consumed by "virtio" [load finished]
0 pages consumed by "virtio_ring" [load finished]
10 pages consumed by "virtio_pci" [load finished]
1 pages consumed by "pata_acpi" [load finished]
0 pages consumed by "ata_generic" [load finished]
0 pages consumed by "serio_raw" [load finished]
0 pages consumed by "crc32c_intel" [load finished]
0 pages consumed by "crct10dif_common" [load finished]
0 pages consumed by "crct10dif_pclmul" [load finished]
278 pages consumed by "virtio_net" [load finished]
198 pages consumed by "virtio_console" [load finished]
0 pages consumed by "cdrom" [load finished]
6 pages consumed by "sr_mod" [load finished]
162 pages consumed by "virtio_blk" [load finished]
1 pages consumed by "fscache" [load finished]
0 pages consumed by "lockd" [load finished]
17 pages consumed by "nfs" [load finished]
0 pages consumed by "libcrc32c" [load finished]
0 pages consumed by "dns_resolver" [load finished]
8 pages consumed by "xfs" [load finished]
0 pages consumed by "nfsv4" [load finished]
== debug_mem for kernel modules during loading end ==
We can clearly see that "qxl" loading consumed more than 6MB memory.
Xunlei Pang (3):
memdebug-ko: add dracut-memdebug-ko.sh to debug kernel module memory
consumption
module-setup: apply kernel module memory debug support
kexec-kdump-howto: add the debugging tip for rd.kodebug
dracut-kdump.sh | 9 ++++
dracut-memdebug-ko.sh | 117 +++++++++++++++++++++++++++++++++++++++++++++++++
dracut-module-setup.sh | 9 ++++
kdumpctl | 14 ++++++
kexec-kdump-howto.txt | 37 ++++++++++++++++
kexec-tools.spec | 2 +
6 files changed, 188 insertions(+)
create mode 100755 dracut-memdebug-ko.sh
--
1.8.3.1
6 years, 6 months
[RFC PATCH 0/2] Use trace to debug kernel module memory consumption
by Xunlei Pang
The current method for kdump memory debug is to use dracut "rd.memdebug=[0-3]",
it is not enough for debugging kernel modules. For example, when we want to find
out which kernel module consumes a large amount of memory, "rd.memdebug" won't
help too much.
A better way is needed to achieve this requirement, this is very useful for kdump
OOM debugging.
The principle of this patch series is to use kernel trace to track slab and buddy
allocation calls during kernel module loading(module_init), thus we can analyze
all the trace data and get the total memory consumption.
The trace events include memory calls under /sys/kernel/debug/tracing/events:
kmem/mm_page_alloc
kmem/mm_page_free
kmem/kmalloc
kmem/kmalloc_node
kmem/kmem_cache_alloc
kmem/kmem_cache_alloc_node
We also inpect the following events to detect the module loading
module/module_load
module/module_put
We can get the module name and task pid from "module_load" event which
also mark the beginning of the loading, and module_put called by the
same task pid implies the end of the loading. So the memory events
recorded in between by the same task pid are consumed by this module
during loading(i.e. modprobe or module_init()).
With these information, we can record approximately the total memory
consumption involved by each kernel module loading.
One major flaw of this method is that the trace ring buffer consumes a lot
of memory. If it is too small, old records maybe be overwritten by subsequent
records. The trace ring buffer is set to be 10MB by default, but it can be
overridden by users via the standard kernel boot parameter "trace_buf_size".
Users should increase the crash kernel memory reservation as needed after
setting large trace ring buffer size, in case oom happens during debugging.
Usage:
1)Pass "rd.memdebug" to kdump kernel cmdline using "KDUMP_COMMANDLINE_APPEND" in /etc/sysconfig/kdump.
2)Pass the extra "trace_buf_size=nn[KMG]" to specify trace ring buffer size(per cpu) as needed.
Xunlei Pang (2):
memdebug-ko: add dracut-memdebug-ko.sh to debug kernel module memory
consumption
module-setup: apply kernel module memory debug support
dracut-kdump.sh | 11 ++++
dracut-memdebug-ko.sh | 144 +++++++++++++++++++++++++++++++++++++++++++++++++
dracut-module-setup.sh | 12 +++++
kdumpctl | 14 +++++
kexec-tools.spec | 2 +
5 files changed, 183 insertions(+)
create mode 100755 dracut-memdebug-ko.sh
--
1.8.3.1
6 years, 6 months
[PATCH v2 3/3] kexec-kdump-howto: add the debugging tip for rd.kodebug
by Xunlei Pang
Update the doc to include the help for rd.kodebug.
Signed-off-by: Xunlei Pang <xlpang(a)redhat.com>
---
kexec-kdump-howto.txt | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/kexec-kdump-howto.txt b/kexec-kdump-howto.txt
index f46563f..87f3821 100644
--- a/kexec-kdump-howto.txt
+++ b/kexec-kdump-howto.txt
@@ -706,4 +706,41 @@ Debugging Tips
Now minicom should be logging serial console in file console-logs.
+- Debug the large memory consumption by kernel modules
+
+ Besides dracut "rd.memdebug=[0-3]" support to debug kdump system memory,
+ kdump supports a fine-grained memory monitor for kernel modules to help
+ figure out which kernel module consumes a large amount of memory(the larger,
+ the more precise the result is).
+
+ One can pass "rd.kodebug" to kdump kernel through "KDUMP_COMMANDLINE_APPEND"
+ defined in /etc/sysconfig/kdump, touch /etc/kdump.conf, restart kdump, then
+ the memory consumption information by kernel modules will be printed out to
+ the console before the vmcore dumping starts.
+
+ As an example, it prints out something below:
+ == debug_mem for kernel modules during loading begin ==
+ 0 pages consumed by "pata_acpi" [load finished]
+ 0 pages consumed by "ata_generic" [load finished]
+ 0 pages consumed by "drm" [load finished]
+ 1 pages consumed by "ttm" [load finished]
+ 0 pages consumed by "drm_kms_helper" [load finished]
+ 834 pages consumed by "qxl" [load finished]
+ 0 pages consumed by "mii" [load finished]
+ 5 pages consumed by "8139cp" [load finished]
+ 0 pages consumed by "8139too" [load finished]
+ 0 pages consumed by "virtio" [load finished]
+ 0 pages consumed by "virtio_ring" [load finished]
+ 10 pages consumed by "virtio_pci" [load finished]
+ 0 pages consumed by "serio_raw" [load finished]
+ 0 pages consumed by "crc32c_intel" [load finished]
+ 199 pages consumed by "virtio_console" [load finished]
+ 0 pages consumed by "libcrc32c" [load finished]
+ 8 pages consumed by "xfs" [load finished]
+ == debug_mem for kernel modules during loading end ==
+
+ If you encounters OOM when using the feature, you can enlarge the reserved memory
+ via "crashkernel" and try again. If you find some kernel module is not included
+ in the print, you can enlarge the trace buffer via "trace_buf_size"(the default
+ size set is 5MB in total for all cpus), and try again.
--
1.8.3.1
6 years, 7 months
[F25 PATCH] x86_64: fix page_offset calculation
by Pratyush Anand
V1 of the KASLR patch which was backported in 2.0.13-6 had wrong
calculation for page_offset. This patch takes the correct difference.
Signed-off-by: Pratyush Anand <panand(a)redhat.com>
---
...mpfile-x86_64-fix-page_offset-calculation.patch | 32 ++++++++++++++++++++++
kexec-tools.spec | 2 ++
2 files changed, 34 insertions(+)
create mode 100644 kexec-tools-2.0.13-makedumpfile-x86_64-fix-page_offset-calculation.patch
diff --git a/kexec-tools-2.0.13-makedumpfile-x86_64-fix-page_offset-calculation.patch b/kexec-tools-2.0.13-makedumpfile-x86_64-fix-page_offset-calculation.patch
new file mode 100644
index 000000000000..3b021deda950
--- /dev/null
+++ b/kexec-tools-2.0.13-makedumpfile-x86_64-fix-page_offset-calculation.patch
@@ -0,0 +1,32 @@
+From 0ebcd6f3570dd66bff0930872b8bf859bdcf302a Mon Sep 17 00:00:00 2001
+Message-Id: <0ebcd6f3570dd66bff0930872b8bf859bdcf302a.1477548646.git.panand(a)redhat.com>
+From: Pratyush Anand <panand(a)redhat.com>
+Date: Thu, 27 Oct 2016 11:34:33 +0530
+Subject: [PATCH] x86_64: fix page_offset calculation
+
+Kernel text region lies above __START_KERNEL_map, which is linearly mapped
+however not a direct mapping. Direct mapping region lies below it instead.
+So, page_offset can only be calculated with a region which is below
+__START_KERNEL_map.
+
+Signed-off-by: Pratyush Anand <panand(a)redhat.com>
+---
+ arch/x86_64.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/x86_64.c b/arch/x86_64.c
+index 13990cef839b..3ef33ae4ef2d 100644
+--- a/makedumpfile-1.6.0/arch/x86_64.c
++++ b/makedumpfile-1.6.0/arch/x86_64.c
+@@ -41,7 +41,7 @@ get_page_offset_x86_64(void)
+ unsigned long long virt_start;
+
+ for (i = 0; get_pt_load(i, &phys_start, NULL, &virt_start, NULL); i++) {
+- if (virt_start >= __START_KERNEL_map) {
++ if (virt_start < __START_KERNEL_map) {
+ info->page_offset = virt_start - phys_start;
+ return TRUE;
+ }
+--
+2.7.4
+
diff --git a/kexec-tools.spec b/kexec-tools.spec
index a7c11605f4f8..a80774ea75dc 100644
--- a/kexec-tools.spec
+++ b/kexec-tools.spec
@@ -95,6 +95,7 @@ Patch604: kexec-tools-2.0.13-makedumpfile-x86-64-calculate-page-offset-from-pt-l
Patch605: kexec-tools-2.0.13-makedumpfile-x86-64-translate-all-VA-to-PA-using-page-table-values.patch
Patch606: kexec-tools-2.0.13-makedumpfile-x86-64-kill-is-vmalloc-addr-x86-64.patch
Patch607: kexec-tools-2.0.13-makedumpfile-x86-64-kill-some-unused-initialization.patch
+Patch608: kexec-tools-2.0.13-makedumpfile-x86_64-fix-page_offset-calculation.patch
%description
kexec-tools provides /sbin/kexec binary that facilitates a new
@@ -130,6 +131,7 @@ tar -z -x -v -f %{SOURCE23}
%patch605 -p1
%patch606 -p1
%patch607 -p1
+%patch608 -p1
%ifarch ppc
%define archdef ARCH=ppc
--
2.7.4
6 years, 7 months
[F25 PATCH v2]: Add initial upstream support for kexec on aarch64
by Pingfan Liu
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
6 years, 7 months
[F25 PATCH] Support kdump for kaslr enabled Fedora kernel
by Dave Young
Since in Fedora 25 kernel kaslr is enabled (x86) but makedumpfile can not save
a correct vmcore, so it means kdump default setup will not work.
Pratyush posted a patch series to upstream which can fix the issue. Let's merge them in F25, will get the normal fixes after it being merged in upstream, we hopefully can rebase soon in rawhide.
This is an urgent fix for F25 since F25 freeze is this week.
https://bugzilla.redhat.com/show_bug.cgi?id=1377567
Signed-off-by: Dave Young <dyoung(a)redhat.com>
---
...x86-64-calculate-page-offset-from-pt-load.patch | 69 ++++++++++
...mpfile-x86-64-kill-is-vmalloc-addr-x86-64.patch | 89 +++++++++++++
...le-x86-64-kill-some-unused-initialization.patch | 58 ++++++++
...late-all-VA-to-PA-using-page-table-values.patch | 148 +++++++++++++++++++++
kexec-tools.spec | 9 ++
5 files changed, 373 insertions(+)
create mode 100644 kexec-tools-2.0.13-makedumpfile-x86-64-calculate-page-offset-from-pt-load.patch
create mode 100644 kexec-tools-2.0.13-makedumpfile-x86-64-kill-is-vmalloc-addr-x86-64.patch
create mode 100644 kexec-tools-2.0.13-makedumpfile-x86-64-kill-some-unused-initialization.patch
create mode 100644 kexec-tools-2.0.13-makedumpfile-x86-64-translate-all-VA-to-PA-using-page-table-values.patch
diff --git a/kexec-tools-2.0.13-makedumpfile-x86-64-calculate-page-offset-from-pt-load.patch b/kexec-tools-2.0.13-makedumpfile-x86-64-calculate-page-offset-from-pt-load.patch
new file mode 100644
index 0000000..50e7717
--- /dev/null
+++ b/kexec-tools-2.0.13-makedumpfile-x86-64-calculate-page-offset-from-pt-load.patch
@@ -0,0 +1,69 @@
+From: Pratyush Anand <panand(a)redhat.com>
+To: ats-kumagai(a)wm.jp.nec.com
+Subject: [PATCH Makedumpfile 1/4] x86_64: Calculate page_offset from pt_load
+Date: Mon, 24 Oct 2016 22:18:43 +0530
+Cc: Pratyush Anand <panand(a)redhat.com>, dyoung(a)redhat.com,
+ kexec(a)lists.infradead.org, bhe(a)redhat.com
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+Content-Type: text/plain; charset=utf-8
+
+page_offset can always be calculated as 'virtual - physical' for a direct
+mapping area on x86. Therefore, remove the version dependent calculation
+and use this method.
+
+Signed-off-by: Pratyush Anand <panand(a)redhat.com>
+---
+ arch/x86_64.c | 24 ++++++++++++++++++++----
+ 1 file changed, 20 insertions(+), 4 deletions(-)
+
+diff --git a/arch/x86_64.c b/arch/x86_64.c
+index ddf7be6bc57b..a96fd8ae00a1 100644
+--- a/makedumpfile-1.6.0/arch/x86_64.c
++++ b/makedumpfile-1.6.0/arch/x86_64.c
+@@ -44,6 +44,24 @@ get_xen_p2m_mfn(void)
+ return NOT_FOUND_LONG_VALUE;
+ }
+
++static int
++get_page_offset_x86_64(void)
++{
++ int i;
++ unsigned long long phys_start;
++ unsigned long long virt_start;
++
++ for (i = 0; get_pt_load(i, &phys_start, NULL, &virt_start, NULL); i++) {
++ if (virt_start >= __START_KERNEL_map) {
++ info->page_offset = virt_start - phys_start;
++ return TRUE;
++ }
++ }
++
++ ERRMSG("Can't get any pt_load to calculate page offset.\n");
++ return FALSE;
++}
++
+ int
+ get_phys_base_x86_64(void)
+ {
+@@ -159,10 +177,8 @@ get_versiondep_info_x86_64(void)
+ else
+ info->max_physmem_bits = _MAX_PHYSMEM_BITS_2_6_31;
+
+- if (info->kernel_version < KERNEL_VERSION(2, 6, 27))
+- info->page_offset = __PAGE_OFFSET_ORIG;
+- else
+- info->page_offset = __PAGE_OFFSET_2_6_27;
++ if (!get_page_offset_x86_64())
++ return FALSE;
+
+ if (info->kernel_version < KERNEL_VERSION(2, 6, 31)) {
+ info->vmalloc_start = VMALLOC_START_ORIG;
+--
+2.7.4
+
+
+_______________________________________________
+kexec mailing list
+kexec(a)lists.infradead.org
+http://lists.infradead.org/mailman/listinfo/kexec
diff --git a/kexec-tools-2.0.13-makedumpfile-x86-64-kill-is-vmalloc-addr-x86-64.patch b/kexec-tools-2.0.13-makedumpfile-x86-64-kill-is-vmalloc-addr-x86-64.patch
new file mode 100644
index 0000000..be12ee6
--- /dev/null
+++ b/kexec-tools-2.0.13-makedumpfile-x86-64-kill-is-vmalloc-addr-x86-64.patch
@@ -0,0 +1,89 @@
+From: Pratyush Anand <panand(a)redhat.com>
+To: ats-kumagai(a)wm.jp.nec.com
+Subject: [PATCH Makedumpfile 3/4] x86_64: kill is_vmalloc_addr_x86_64()
+Date: Mon, 24 Oct 2016 22:18:45 +0530
+Cc: Pratyush Anand <panand(a)redhat.com>, dyoung(a)redhat.com,
+ kexec(a)lists.infradead.org, bhe(a)redhat.com
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+Content-Type: text/plain; charset=utf-8
+
+From kernel documentation:
+ffffffff80000000 - ffffffff9fffffff (=512 MB) kernel text mapping, from phys 0
+ffffffffa0000000 - ffffffffff5fffff (=1526 MB) module mapping space
+
+So, it is only the module area which is lying above __START_KERNEL_map.
+However, kexec-tools only creates PT_LOAD segments for kernel text region
+and crash memory region. So, we can safely remove the check for
+!is_vmalloc_addr_x86_64() from get_phys_base_x86_64().
+
+Since, this was the last usage of is_vmalloc_addr_x86_64(), so kill it as
+well.
+
+Signed-off-by: Pratyush Anand <panand(a)redhat.com>
+---
+ arch/x86_64.c | 14 +-------------
+ makedumpfile.h | 3 +--
+ 2 files changed, 2 insertions(+), 15 deletions(-)
+
+diff --git a/arch/x86_64.c b/arch/x86_64.c
+index fe2764a8bec2..597cdac36dfc 100644
+--- a/makedumpfile-1.6.0/arch/x86_64.c
++++ b/makedumpfile-1.6.0/arch/x86_64.c
+@@ -21,17 +21,6 @@
+ extern struct vmap_pfns *gvmem_pfns;
+ extern int nr_gvmem_pfns;
+
+-int
+-is_vmalloc_addr_x86_64(ulong vaddr)
+-{
+- /*
+- * vmalloc, virtual memmap, and module space as VMALLOC space.
+- */
+- return ((vaddr >= VMALLOC_START && vaddr <= VMALLOC_END)
+- || (vaddr >= VMEMMAP_START && vaddr <= VMEMMAP_END)
+- || (vaddr >= MODULES_VADDR && vaddr <= MODULES_END));
+-}
+-
+ static unsigned long
+ get_xen_p2m_mfn(void)
+ {
+@@ -75,8 +64,7 @@ get_phys_base_x86_64(void)
+ info->phys_base = 0; /* default/traditional */
+
+ for (i = 0; get_pt_load(i, &phys_start, NULL, &virt_start, NULL); i++) {
+- if ((virt_start >= __START_KERNEL_map) &&
+- !(is_vmalloc_addr_x86_64(virt_start))) {
++ if (virt_start >= __START_KERNEL_map) {
+
+ info->phys_base = phys_start -
+ (virt_start & ~(__START_KERNEL_map));
+diff --git a/makedumpfile.h b/makedumpfile.h
+index 13559651feb6..8a96da1f61bd 100644
+--- a/makedumpfile-1.6.0/makedumpfile.h
++++ b/makedumpfile-1.6.0/makedumpfile.h
+@@ -859,7 +859,6 @@ unsigned long long vaddr_to_paddr_x86(unsigned long vaddr);
+ #endif /* x86 */
+
+ #ifdef __x86_64__
+-int is_vmalloc_addr_x86_64(ulong vaddr);
+ int get_phys_base_x86_64(void);
+ int get_machdep_info_x86_64(void);
+ int get_versiondep_info_x86_64(void);
+@@ -869,7 +868,7 @@ unsigned long long vtop4_x86_64(unsigned long vaddr);
+ #define get_machdep_info() get_machdep_info_x86_64()
+ #define get_versiondep_info() get_versiondep_info_x86_64()
+ #define vaddr_to_paddr(X) vtop4_x86_64(X)
+-#define is_phys_addr(X) (!is_vmalloc_addr_x86_64(X))
++#define is_phys_addr(X) stub_true_ul(X)
+ #endif /* x86_64 */
+
+ #ifdef __powerpc64__ /* powerpc64 */
+--
+2.7.4
+
+
+_______________________________________________
+kexec mailing list
+kexec(a)lists.infradead.org
+http://lists.infradead.org/mailman/listinfo/kexec
diff --git a/kexec-tools-2.0.13-makedumpfile-x86-64-kill-some-unused-initialization.patch b/kexec-tools-2.0.13-makedumpfile-x86-64-kill-some-unused-initialization.patch
new file mode 100644
index 0000000..750f279
--- /dev/null
+++ b/kexec-tools-2.0.13-makedumpfile-x86-64-kill-some-unused-initialization.patch
@@ -0,0 +1,58 @@
+From: Pratyush Anand <panand(a)redhat.com>
+To: ats-kumagai(a)wm.jp.nec.com
+Subject: [PATCH Makedumpfile 4/4] x86_64: kill some unused initialization
+Date: Mon, 24 Oct 2016 22:18:46 +0530
+Cc: Pratyush Anand <panand(a)redhat.com>, dyoung(a)redhat.com,
+ kexec(a)lists.infradead.org, bhe(a)redhat.com
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+Content-Type: text/plain; charset=utf-8
+
+VMALLOC_START, VMALLOC_END, MODULES_VADDR and MODULES_END are mo more
+needed for x86_64 now. So, kill their initialization.
+
+Signed-off-by: Pratyush Anand <panand(a)redhat.com>
+---
+ arch/x86_64.c | 4 ----
+ makedumpfile.h | 2 --
+ 2 files changed, 6 deletions(-)
+
+diff --git a/arch/x86_64.c b/arch/x86_64.c
+index 597cdac36dfc..13990cef839b 100644
+--- a/makedumpfile-1.6.0/arch/x86_64.c
++++ b/makedumpfile-1.6.0/arch/x86_64.c
+@@ -169,13 +169,9 @@ get_versiondep_info_x86_64(void)
+ return FALSE;
+
+ if (info->kernel_version < KERNEL_VERSION(2, 6, 31)) {
+- info->vmalloc_start = VMALLOC_START_ORIG;
+- info->vmalloc_end = VMALLOC_END_ORIG;
+ info->vmemmap_start = VMEMMAP_START_ORIG;
+ info->vmemmap_end = VMEMMAP_END_ORIG;
+ } else {
+- info->vmalloc_start = VMALLOC_START_2_6_31;
+- info->vmalloc_end = VMALLOC_END_2_6_31;
+ info->vmemmap_start = VMEMMAP_START_2_6_31;
+ info->vmemmap_end = VMEMMAP_END_2_6_31;
+ }
+diff --git a/makedumpfile.h b/makedumpfile.h
+index 8a96da1f61bd..338c651388f0 100644
+--- a/makedumpfile-1.6.0/makedumpfile.h
++++ b/makedumpfile-1.6.0/makedumpfile.h
+@@ -575,8 +575,6 @@ int get_va_bits_arm64(void);
+ #define __START_KERNEL_map (0xffffffff80000000)
+ #define KERNEL_IMAGE_SIZE_ORIG (0x0000000008000000) /* 2.6.25, or former */
+ #define KERNEL_IMAGE_SIZE_2_6_26 (0x0000000020000000) /* 2.6.26, or later */
+-#define MODULES_VADDR (__START_KERNEL_map + NUMBER(KERNEL_IMAGE_SIZE))
+-#define MODULES_END (0xfffffffffff00000)
+ #define KVBASE PAGE_OFFSET
+ #define _SECTION_SIZE_BITS (27)
+ #define _MAX_PHYSMEM_BITS_ORIG (40)
+--
+2.7.4
+
+
+_______________________________________________
+kexec mailing list
+kexec(a)lists.infradead.org
+http://lists.infradead.org/mailman/listinfo/kexec
diff --git a/kexec-tools-2.0.13-makedumpfile-x86-64-translate-all-VA-to-PA-using-page-table-values.patch b/kexec-tools-2.0.13-makedumpfile-x86-64-translate-all-VA-to-PA-using-page-table-values.patch
new file mode 100644
index 0000000..4920c87
--- /dev/null
+++ b/kexec-tools-2.0.13-makedumpfile-x86-64-translate-all-VA-to-PA-using-page-table-values.patch
@@ -0,0 +1,148 @@
+From: Pratyush Anand <panand(a)redhat.com>
+To: ats-kumagai(a)wm.jp.nec.com
+Subject: [PATCH Makedumpfile 2/4] x86_64: translate all VA to PA using page
+ table values
+Date: Mon, 24 Oct 2016 22:18:44 +0530
+Cc: Pratyush Anand <panand(a)redhat.com>, dyoung(a)redhat.com,
+ kexec(a)lists.infradead.org, bhe(a)redhat.com
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+Content-Type: text/plain; charset=utf-8
+
+Currently we translate some of the VA areas using linear mapping while some
+other(which can not be linearly mapped) using page table.
+
+However, we will have entry of a page in the page table irrespective of its
+virtual region. So, we can always look into page table for any VA to PA
+translation. This approach will solve lot of complexity in makedumpfile. It
+will in turn remove dependency over variables like VMALLOC_START,
+MODULES_VADDR etc whose definition keeps changing in newer kernel version.
+
+Moreover, I do not see any side effect of this approach in terms of
+execution timing. I tested with IBM x3950 X6 machine having 4136359 MB of
+memory. These are the results of makedumpfile execution time:
+
+Without this patch:
+===================
+With -d 31:
+Trial 1: 237.59526248 S
+Trial 2: 235.236914962 S
+Trail 3: 237.678712045 S
+
+With -d 1:
+Trial 1: 2548.905296877 S
+Trial 2: 2549.759881756 S
+
+With this patch:
+===================
+With -d 31:
+Trial 1: 232.713841516 S
+Trial 2: 228.45697177 S
+Trail 3: 232.942262441 S
+
+With -d 1:
+Trial 1: 2768.424565806 S
+Trial 2: 2749.622115455 S
+Trail 3: 2537.770359073 S
+
+Signed-off-by: Pratyush Anand <panand(a)redhat.com>
+---
+ arch/x86_64.c | 42 ++++++++----------------------------------
+ makedumpfile.h | 4 ++--
+ 2 files changed, 10 insertions(+), 36 deletions(-)
+
+diff --git a/arch/x86_64.c b/arch/x86_64.c
+index a96fd8ae00a1..fe2764a8bec2 100644
+--- a/makedumpfile-1.6.0/arch/x86_64.c
++++ b/makedumpfile-1.6.0/arch/x86_64.c
+@@ -203,6 +203,12 @@ vtop4_x86_64(unsigned long vaddr)
+ {
+ unsigned long page_dir, pml4, pgd_paddr, pgd_pte, pmd_paddr, pmd_pte;
+ unsigned long pte_paddr, pte;
++ unsigned long phys_base;
++
++ if (SYMBOL(phys_base) != NOT_FOUND_SYMBOL)
++ phys_base = info->phys_base;
++ else
++ phys_base = 0;
+
+ if (SYMBOL(init_level4_pgt) == NOT_FOUND_SYMBOL) {
+ ERRMSG("Can't get the symbol of init_level4_pgt.\n");
+@@ -212,9 +218,9 @@ vtop4_x86_64(unsigned long vaddr)
+ /*
+ * Get PGD.
+ */
+- page_dir = SYMBOL(init_level4_pgt);
++ page_dir = SYMBOL(init_level4_pgt) - __START_KERNEL_map + phys_base;
+ page_dir += pml4_index(vaddr) * sizeof(unsigned long);
+- if (!readmem(VADDR, page_dir, &pml4, sizeof pml4)) {
++ if (!readmem(PADDR, page_dir, &pml4, sizeof pml4)) {
+ ERRMSG("Can't get pml4 (page_dir:%lx).\n", page_dir);
+ return NOT_PADDR;
+ }
+@@ -285,38 +291,6 @@ vtop4_x86_64(unsigned long vaddr)
+ return (pte & ENTRY_MASK) + PAGEOFFSET(vaddr);
+ }
+
+-unsigned long long
+-vaddr_to_paddr_x86_64(unsigned long vaddr)
+-{
+- unsigned long phys_base;
+- unsigned long long paddr;
+-
+- /*
+- * Check the relocatable kernel.
+- */
+- if (SYMBOL(phys_base) != NOT_FOUND_SYMBOL)
+- phys_base = info->phys_base;
+- else
+- phys_base = 0;
+-
+- if (is_vmalloc_addr_x86_64(vaddr)) {
+- if ((paddr = vtop4_x86_64(vaddr)) == NOT_PADDR) {
+- ERRMSG("Can't convert a virtual address(%lx) to " \
+- "physical address.\n", vaddr);
+- return NOT_PADDR;
+- }
+- } else if (vaddr >= __START_KERNEL_map) {
+- paddr = vaddr - __START_KERNEL_map + phys_base;
+-
+- } else {
+- if (is_xen_memory())
+- paddr = vaddr - PAGE_OFFSET_XEN_DOM0;
+- else
+- paddr = vaddr - PAGE_OFFSET;
+- }
+- return paddr;
+-}
+-
+ /*
+ * for Xen extraction
+ */
+diff --git a/makedumpfile.h b/makedumpfile.h
+index a5955ff750e5..13559651feb6 100644
+--- a/makedumpfile-1.6.0/makedumpfile.h
++++ b/makedumpfile-1.6.0/makedumpfile.h
+@@ -863,12 +863,12 @@ int is_vmalloc_addr_x86_64(ulong vaddr);
+ int get_phys_base_x86_64(void);
+ int get_machdep_info_x86_64(void);
+ int get_versiondep_info_x86_64(void);
+-unsigned long long vaddr_to_paddr_x86_64(unsigned long vaddr);
++unsigned long long vtop4_x86_64(unsigned long vaddr);
+ #define find_vmemmap() find_vmemmap_x86_64()
+ #define get_phys_base() get_phys_base_x86_64()
+ #define get_machdep_info() get_machdep_info_x86_64()
+ #define get_versiondep_info() get_versiondep_info_x86_64()
+-#define vaddr_to_paddr(X) vaddr_to_paddr_x86_64(X)
++#define vaddr_to_paddr(X) vtop4_x86_64(X)
+ #define is_phys_addr(X) (!is_vmalloc_addr_x86_64(X))
+ #endif /* x86_64 */
+
+--
+2.7.4
+
+
+_______________________________________________
+kexec mailing list
+kexec(a)lists.infradead.org
+http://lists.infradead.org/mailman/listinfo/kexec
diff --git a/kexec-tools.spec b/kexec-tools.spec
index f798cad..b7eb8b9 100644
--- a/kexec-tools.spec
+++ b/kexec-tools.spec
@@ -90,6 +90,11 @@ Patch601: kexec-tools-2.0.3-disable-kexec-test.patch
Patch602: kexec-tools-2.0.12-makedumpfile-Support-_count-_refcount-rename-in-struct-p.patch
Patch603: kexec-tools-2.0.13-fix-armv7-build-failure.patch
+Patch604: kexec-tools-2.0.13-makedumpfile-x86-64-calculate-page-offset-from-pt-load.patch
+Patch605: kexec-tools-2.0.13-makedumpfile-x86-64-translate-all-VA-to-PA-using-page-table-values.patch
+Patch606: kexec-tools-2.0.13-makedumpfile-x86-64-kill-is-vmalloc-addr-x86-64.patch
+Patch607: kexec-tools-2.0.13-makedumpfile-x86-64-kill-some-unused-initialization.patch
+
%description
kexec-tools provides /sbin/kexec binary that facilitates a new
kernel to boot using the kernel's kexec feature either on a
@@ -118,6 +123,10 @@ tar -z -x -v -f %{SOURCE23}
%patch601 -p1
%patch602 -p1
%patch603 -p1
+%patch604 -p1
+%patch605 -p1
+%patch606 -p1
+%patch607 -p1
%ifarch ppc
%define archdef ARCH=ppc
--
2.10.1
6 years, 7 months
[PATCH v4] Add initial upstream support for kexec on aarch64
by Pingfan Liu
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 three 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
3. patches to fix the issue of higher version gcc used by koji (not upstrem yet,
and the corresponding fix in kernel side is in other package)
kexec-tools-2.0.13-arm64-Add-support-for-additional-relocations-in-the-kexec-purgatory-code.patch
kexec-tools-2.0.13-arm64-Add-support-of-R_AARCH64_PREL32-relocation-in-.patch
Signed-off-by: Pingfan Liu <piliu(a)redhat.com>
---
v3 -> v4:
rename patches with the prefix "kexec-tools-2.0.13-*"
v2 -> v3:
add patches to fix the issue of higher version gcc
---
...ools-2.0.13-arm64-Add-arm64-kexec-support.patch | 1407 ++++++++++++++++++++
...l-relocations-in-the-kexec-purgatory-code.patch | 106 ++
...-arm64-Add-support-for-binary-image-files.patch | 96 ++
...upport-of-R_AARCH64_PREL32-relocation-in-.patch | 42 +
....13-kexec-Add-common-device-tree-routines.patch | 208 +++
kexec-tools.spec | 22 +-
6 files changed, 1876 insertions(+), 5 deletions(-)
create mode 100644 kexec-tools-2.0.13-arm64-Add-arm64-kexec-support.patch
create mode 100644 kexec-tools-2.0.13-arm64-Add-support-for-additional-relocations-in-the-kexec-purgatory-code.patch
create mode 100644 kexec-tools-2.0.13-arm64-Add-support-for-binary-image-files.patch
create mode 100644 kexec-tools-2.0.13-arm64-Add-support-of-R_AARCH64_PREL32-relocation-in-.patch
create mode 100644 kexec-tools-2.0.13-kexec-Add-common-device-tree-routines.patch
diff --git a/kexec-tools-2.0.13-arm64-Add-arm64-kexec-support.patch b/kexec-tools-2.0.13-arm64-Add-arm64-kexec-support.patch
new file mode 100644
index 0000000..0c14b1b
--- /dev/null
+++ b/kexec-tools-2.0.13-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/kexec-tools-2.0.13-arm64-Add-support-for-additional-relocations-in-the-kexec-purgatory-code.patch b/kexec-tools-2.0.13-arm64-Add-support-for-additional-relocations-in-the-kexec-purgatory-code.patch
new file mode 100644
index 0000000..ef52b79
--- /dev/null
+++ b/kexec-tools-2.0.13-arm64-Add-support-for-additional-relocations-in-the-kexec-purgatory-code.patch
@@ -0,0 +1,106 @@
+From patchwork Thu Oct 20 10:43:31 2016
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v2] arm64: Add support for additional relocations in the kexec
+ purgatory code
+From: Catalin Marinas <catalin.marinas(a)arm.com>
+X-Patchwork-Id: 9386541
+Message-Id: <1476960211-25594-1-git-send-email-catalin.marinas(a)arm.com>
+To: Simon Horman <horms(a)verge.net.au>
+Cc: Geoff Levand <geoff(a)infradead.org>, kexec(a)lists.infradead.org,
+ linux-arm-kernel(a)lists.infradead.org
+Date: Thu, 20 Oct 2016 11:43:31 +0100
+
+When compiling the kexec-tools with gcc6, the following additional
+reolcations are generated in the purgatory.ro file:
+
+R_AARCH64_ADR_PREL_PG_HI21
+R_AARCH64_ADD_ABS_LO12_NC
+R_AARCH64_LDST64_ABS_LO12_NC
+
+This patch modifies the arm64 machine_apply_elf_rel() function to handle
+these relocations.
+
+Cc: Geoff Levand <geoff(a)infradead.org>
+Signed-off-by: Catalin Marinas <catalin.marinas(a)arm.com>
+Reviewed-by: Geoff Levand <geoff(a)infradead.org>
+---
+
+Changes for v2:
+- Fixed the type string to drop the "R_AARCH64_" prefix
+
+ kexec/arch/arm64/kexec-arm64.c | 35 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 35 insertions(+)
+
+diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c
+index 2e8839a..e067a23 100644
+--- a/kexec/arch/arm64/kexec-arm64.c
++++ b/kexec/arch/arm64/kexec-arm64.c
+@@ -550,6 +550,14 @@ void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
+ # define R_AARCH64_ADR_PREL_LO21 274
+ #endif
+
++#if !defined(R_AARCH64_ADR_PREL_PG_HI21)
++# define R_AARCH64_ADR_PREL_PG_HI21 275
++#endif
++
++#if !defined(R_AARCH64_ADD_ABS_LO12_NC)
++# define R_AARCH64_ADD_ABS_LO12_NC 277
++#endif
++
+ #if !defined(R_AARCH64_JUMP26)
+ # define R_AARCH64_JUMP26 282
+ #endif
+@@ -558,10 +566,15 @@ void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
+ # define R_AARCH64_CALL26 283
+ #endif
+
++#if !defined(R_AARCH64_LDST64_ABS_LO12_NC)
++# define R_AARCH64_LDST64_ABS_LO12_NC 286
++#endif
++
+ uint64_t *loc64;
+ uint32_t *loc32;
+ uint64_t *location = (uint64_t *)ptr;
+ uint64_t data = *location;
++ uint64_t imm;
+ const char *type = NULL;
+
+ switch(r_type) {
+@@ -585,6 +598,19 @@ void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+ + (((value - address) << 3) & 0xffffe0));
+ break;
++ case R_AARCH64_ADR_PREL_PG_HI21:
++ type = "ADR_PREL_PG_HI21";
++ imm = ((value & ~0xfff) - (address & ~0xfff)) >> 12;
++ loc32 = ptr;
++ *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
++ + ((imm & 3) << 29) + ((imm & 0x1ffffc) << (5 - 2)));
++ break;
++ case R_AARCH64_ADD_ABS_LO12_NC:
++ type = "ADD_ABS_LO12_NC";
++ loc32 = ptr;
++ *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
++ + ((value & 0xfff) << 10));
++ break;
+ case R_AARCH64_JUMP26:
+ type = "JUMP26";
+ loc32 = ptr;
+@@ -597,6 +623,15 @@ void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+ + (((value - address) >> 2) & 0x3ffffff));
+ break;
++ case R_AARCH64_LDST64_ABS_LO12_NC:
++ if (value & 7)
++ die("%s: ERROR Unaligned value: %lx\n", __func__,
++ value);
++ type = "LDST64_ABS_LO12_NC";
++ loc32 = ptr;
++ *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
++ + ((value & 0xff8) << (10 - 3)));
++ break;
+ default:
+ die("%s: ERROR Unknown type: %lu\n", __func__, r_type);
+ break;
diff --git a/kexec-tools-2.0.13-arm64-Add-support-for-binary-image-files.patch b/kexec-tools-2.0.13-arm64-Add-support-for-binary-image-files.patch
new file mode 100644
index 0000000..8e8dde0
--- /dev/null
+++ b/kexec-tools-2.0.13-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-2.0.13-arm64-Add-support-of-R_AARCH64_PREL32-relocation-in-.patch b/kexec-tools-2.0.13-arm64-Add-support-of-R_AARCH64_PREL32-relocation-in-.patch
new file mode 100644
index 0000000..a1b55e9
--- /dev/null
+++ b/kexec-tools-2.0.13-arm64-Add-support-of-R_AARCH64_PREL32-relocation-in-.patch
@@ -0,0 +1,42 @@
+From 3a3c61cb7f129936e5752d1ccc304c183575a8b0 Mon Sep 17 00:00:00 2001
+Message-Id: <3a3c61cb7f129936e5752d1ccc304c183575a8b0.1477402102.git.panand(a)redhat.com>
+From: Pratyush Anand <panand(a)redhat.com>
+Date: Tue, 25 Oct 2016 18:55:49 +0530
+Subject: [PATCH] arm64: Add support of R_AARCH64_PREL32 relocation in
+ purgatory
+
+Signed-off-by: Pratyush Anand <panand(a)redhat.com>
+---
+ kexec/arch/arm64/kexec-arm64.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c
+index 218f0bc832cd..b12de4772412 100644
+--- a/kexec/arch/arm64/kexec-arm64.c
++++ b/kexec/arch/arm64/kexec-arm64.c
+@@ -720,6 +720,10 @@ void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
+ # define R_AARCH64_ABS64 257
+ #endif
+
++#if !defined(R_AARCH64_PREL32)
++# define R_AARCH64_PREL32 261
++#endif
++
+ #if !defined(R_AARCH64_LD_PREL_LO19)
+ # define R_AARCH64_LD_PREL_LO19 273
+ #endif
+@@ -761,6 +765,11 @@ void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
+ loc64 = ptr;
+ *loc64 = cpu_to_elf64(ehdr, elf64_to_cpu(ehdr, *loc64) + value);
+ break;
++ case R_AARCH64_PREL32:
++ type = "PREL32";
++ loc32 = ptr;
++ *loc32 = cpu_to_elf32(ehdr, elf32_to_cpu(ehdr, *loc32) + value - address);
++ break;
+ case R_AARCH64_LD_PREL_LO19:
+ type = "LD_PREL_LO19";
+ loc32 = ptr;
+--
+2.7.4
+
diff --git a/kexec-tools-2.0.13-kexec-Add-common-device-tree-routines.patch b/kexec-tools-2.0.13-kexec-Add-common-device-tree-routines.patch
new file mode 100644
index 0000000..87897f0
--- /dev/null
+++ b/kexec-tools-2.0.13-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/kexec-tools.spec b/kexec-tools.spec
index cd26ec3..2597651 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,14 @@ 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: kexec-tools-2.0.13-kexec-Add-common-device-tree-routines.patch
+Patch501: kexec-tools-2.0.13-arm64-Add-arm64-kexec-support.patch
+Patch502: kexec-tools-2.0.13-arm64-Add-support-for-binary-image-files.patch
+Patch503: kexec-tools-2.0.13-arm64-Add-support-for-additional-relocations-in-the-kexec-purgatory-code.patch
+Patch504: kexec-tools-2.0.13-arm64-Add-support-of-R_AARCH64_PREL32-relocation-in-.patch
#
# Patches 601 onward are generic patches
@@ -107,6 +113,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 +126,7 @@ tar -z -x -v -f %{SOURCE23}
%endif
%build
-
+autoreconf
%configure \
%ifarch ppc64
--host=powerpc64-redhat-linux-gnu \
@@ -315,6 +324,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
6 years, 7 months
[PATCH v3] Add initial upstream support for kexec on aarch64
by Pingfan Liu
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 three 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
3. patches to fix the issue of higher version gcc used by koji (not upstrem yet,
and the corresponding fix in kernel side is in other package)
0004-arm64-Add-support-for-additional-relocations-in-the-kexec-purgatory-code.patch
0005-arm64-Add-support-of-R_AARCH64_PREL32-relocation-in-.patch
Signed-off-by: Pingfan Liu <piliu(a)redhat.com>
---
changes from v2 -> v3
add patches to fix the issue of higher version gcc
---
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 ++
...l-relocations-in-the-kexec-purgatory-code.patch | 106 ++
...upport-of-R_AARCH64_PREL32-relocation-in-.patch | 42 +
kexec-tools.spec | 23 +-
6 files changed, 1877 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
create mode 100644 0004-arm64-Add-support-for-additional-relocations-in-the-kexec-purgatory-code.patch
create mode 100644 0005-arm64-Add-support-of-R_AARCH64_PREL32-relocation-in-.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/0004-arm64-Add-support-for-additional-relocations-in-the-kexec-purgatory-code.patch b/0004-arm64-Add-support-for-additional-relocations-in-the-kexec-purgatory-code.patch
new file mode 100644
index 0000000..ef52b79
--- /dev/null
+++ b/0004-arm64-Add-support-for-additional-relocations-in-the-kexec-purgatory-code.patch
@@ -0,0 +1,106 @@
+From patchwork Thu Oct 20 10:43:31 2016
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v2] arm64: Add support for additional relocations in the kexec
+ purgatory code
+From: Catalin Marinas <catalin.marinas(a)arm.com>
+X-Patchwork-Id: 9386541
+Message-Id: <1476960211-25594-1-git-send-email-catalin.marinas(a)arm.com>
+To: Simon Horman <horms(a)verge.net.au>
+Cc: Geoff Levand <geoff(a)infradead.org>, kexec(a)lists.infradead.org,
+ linux-arm-kernel(a)lists.infradead.org
+Date: Thu, 20 Oct 2016 11:43:31 +0100
+
+When compiling the kexec-tools with gcc6, the following additional
+reolcations are generated in the purgatory.ro file:
+
+R_AARCH64_ADR_PREL_PG_HI21
+R_AARCH64_ADD_ABS_LO12_NC
+R_AARCH64_LDST64_ABS_LO12_NC
+
+This patch modifies the arm64 machine_apply_elf_rel() function to handle
+these relocations.
+
+Cc: Geoff Levand <geoff(a)infradead.org>
+Signed-off-by: Catalin Marinas <catalin.marinas(a)arm.com>
+Reviewed-by: Geoff Levand <geoff(a)infradead.org>
+---
+
+Changes for v2:
+- Fixed the type string to drop the "R_AARCH64_" prefix
+
+ kexec/arch/arm64/kexec-arm64.c | 35 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 35 insertions(+)
+
+diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c
+index 2e8839a..e067a23 100644
+--- a/kexec/arch/arm64/kexec-arm64.c
++++ b/kexec/arch/arm64/kexec-arm64.c
+@@ -550,6 +550,14 @@ void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
+ # define R_AARCH64_ADR_PREL_LO21 274
+ #endif
+
++#if !defined(R_AARCH64_ADR_PREL_PG_HI21)
++# define R_AARCH64_ADR_PREL_PG_HI21 275
++#endif
++
++#if !defined(R_AARCH64_ADD_ABS_LO12_NC)
++# define R_AARCH64_ADD_ABS_LO12_NC 277
++#endif
++
+ #if !defined(R_AARCH64_JUMP26)
+ # define R_AARCH64_JUMP26 282
+ #endif
+@@ -558,10 +566,15 @@ void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
+ # define R_AARCH64_CALL26 283
+ #endif
+
++#if !defined(R_AARCH64_LDST64_ABS_LO12_NC)
++# define R_AARCH64_LDST64_ABS_LO12_NC 286
++#endif
++
+ uint64_t *loc64;
+ uint32_t *loc32;
+ uint64_t *location = (uint64_t *)ptr;
+ uint64_t data = *location;
++ uint64_t imm;
+ const char *type = NULL;
+
+ switch(r_type) {
+@@ -585,6 +598,19 @@ void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+ + (((value - address) << 3) & 0xffffe0));
+ break;
++ case R_AARCH64_ADR_PREL_PG_HI21:
++ type = "ADR_PREL_PG_HI21";
++ imm = ((value & ~0xfff) - (address & ~0xfff)) >> 12;
++ loc32 = ptr;
++ *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
++ + ((imm & 3) << 29) + ((imm & 0x1ffffc) << (5 - 2)));
++ break;
++ case R_AARCH64_ADD_ABS_LO12_NC:
++ type = "ADD_ABS_LO12_NC";
++ loc32 = ptr;
++ *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
++ + ((value & 0xfff) << 10));
++ break;
+ case R_AARCH64_JUMP26:
+ type = "JUMP26";
+ loc32 = ptr;
+@@ -597,6 +623,15 @@ void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+ + (((value - address) >> 2) & 0x3ffffff));
+ break;
++ case R_AARCH64_LDST64_ABS_LO12_NC:
++ if (value & 7)
++ die("%s: ERROR Unaligned value: %lx\n", __func__,
++ value);
++ type = "LDST64_ABS_LO12_NC";
++ loc32 = ptr;
++ *loc32 = cpu_to_le32(le32_to_cpu(*loc32)
++ + ((value & 0xff8) << (10 - 3)));
++ break;
+ default:
+ die("%s: ERROR Unknown type: %lu\n", __func__, r_type);
+ break;
diff --git a/0005-arm64-Add-support-of-R_AARCH64_PREL32-relocation-in-.patch b/0005-arm64-Add-support-of-R_AARCH64_PREL32-relocation-in-.patch
new file mode 100644
index 0000000..a1b55e9
--- /dev/null
+++ b/0005-arm64-Add-support-of-R_AARCH64_PREL32-relocation-in-.patch
@@ -0,0 +1,42 @@
+From 3a3c61cb7f129936e5752d1ccc304c183575a8b0 Mon Sep 17 00:00:00 2001
+Message-Id: <3a3c61cb7f129936e5752d1ccc304c183575a8b0.1477402102.git.panand(a)redhat.com>
+From: Pratyush Anand <panand(a)redhat.com>
+Date: Tue, 25 Oct 2016 18:55:49 +0530
+Subject: [PATCH] arm64: Add support of R_AARCH64_PREL32 relocation in
+ purgatory
+
+Signed-off-by: Pratyush Anand <panand(a)redhat.com>
+---
+ kexec/arch/arm64/kexec-arm64.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c
+index 218f0bc832cd..b12de4772412 100644
+--- a/kexec/arch/arm64/kexec-arm64.c
++++ b/kexec/arch/arm64/kexec-arm64.c
+@@ -720,6 +720,10 @@ void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
+ # define R_AARCH64_ABS64 257
+ #endif
+
++#if !defined(R_AARCH64_PREL32)
++# define R_AARCH64_PREL32 261
++#endif
++
+ #if !defined(R_AARCH64_LD_PREL_LO19)
+ # define R_AARCH64_LD_PREL_LO19 273
+ #endif
+@@ -761,6 +765,11 @@ void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
+ loc64 = ptr;
+ *loc64 = cpu_to_elf64(ehdr, elf64_to_cpu(ehdr, *loc64) + value);
+ break;
++ case R_AARCH64_PREL32:
++ type = "PREL32";
++ loc32 = ptr;
++ *loc32 = cpu_to_elf32(ehdr, elf32_to_cpu(ehdr, *loc32) + value - address);
++ break;
+ case R_AARCH64_LD_PREL_LO19:
+ type = "LD_PREL_LO19";
+ loc32 = ptr;
+--
+2.7.4
+
diff --git a/kexec-tools.spec b/kexec-tools.spec
index cd26ec3..b41137a 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,15 @@ 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
+Patch503: 0004-arm64-Add-support-for-additional-relocations-in-the-kexec-purgatory-code.patch
+Patch504: 0005-arm64-Add-support-of-R_AARCH64_PREL32-relocation-in-.patch
+
#
# Patches 601 onward are generic patches
@@ -107,6 +114,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 +127,7 @@ tar -z -x -v -f %{SOURCE23}
%endif
%build
-
+autoreconf
%configure \
%ifarch ppc64
--host=powerpc64-redhat-linux-gnu \
@@ -315,6 +325,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
6 years, 7 months
Re: [F25 PATCH]: Add initial upstream support for kexec on aarch64
by Baoquan He
On 10/25/16 at 02:50pm, Pingfan Liu wrote:
> From: Pingfan Liu <piliu(a)redhat.com>
>
> Fix Bug 925630 - kexec-tools: support for arm64
> https://bugzilla.redhat.com/show_bug.cgi?id=925630
>
> Support for the ARM 64 bit CPU architecture (aarch64) was introduced in
> autoconf 2.69. kexec-tools appears to use an earlier version of
> autoconf, preventing its being built. This series fix this issue by
> updates config.guess and config.sub to recognize aarch64
Don't understand what you are trying to do. What I got is you are back
porting aarch64 supporting code to f25, meanwhile you found it still
doesn't work because kexec-tools is still using old autoconf. So you fix
it too in this patch. Right?
>
> patchset backported from upstream:
> commit abdfe97736f89d9bc73662b9134604b0229a599e
> commit 522df5f7217fda01ece3f6ac3e9987b0320c2bb0
> commit 217bcc00c9309416a6c6cd0584196559d28a9259
>
> 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
>
6 years, 7 months
[PATCH v4] Documentation: step by step guide on confiuring kdump in live images
by Tong Li
This is a short document about how to setup kdump on live images. All
steps were tested on Fedora 25 Alpha LiveCD and saved vmcore captured
by kdump to a USB stick successfully.
Signed-off-by: Tong Li <tonli(a)redhat.com>
---
kexec-tools.spec | 3 +++
live-image-kdump-howto.txt | 23 +++++++++++++++++++++++
2 files changed, 26 insertions(+)
create mode 100644 live-image-kdump-howto.txt
diff --git a/kexec-tools.spec b/kexec-tools.spec
index 8063078..315c29a 100644
--- a/kexec-tools.spec
+++ b/kexec-tools.spec
@@ -28,6 +28,7 @@ Source23: kdump-anaconda-addon-005-19-g6577df4.tar.gz
Source24: kdump-lib-initramfs.sh
Source25: kdump.sysconfig.ppc64le
Source26: kdumpctl.8
+Source27: live-image-kdump-howto.txt
#######################################
# These are sources for mkdumpramfs
@@ -133,6 +134,7 @@ rm -f kexec-tools.spec.in
# setup the docs
cp %{SOURCE10} .
cp %{SOURCE21} .
+cp %{SOURCE27} .
make
%ifarch %{ix86} x86_64 ppc64 s390x ppc64le
@@ -305,6 +307,7 @@ done
%doc TODO
%doc kexec-kdump-howto.txt
%doc kdump-in-cluster-environment.txt
+%doc live-image-kdump-howto.txt
%ifarch %{ix86} x86_64 ppc64 s390x ppc64le
%{_libdir}/eppic_makedumpfile.so
/usr/share/makedumpfile/eppic_scripts/
diff --git a/live-image-kdump-howto.txt b/live-image-kdump-howto.txt
new file mode 100644
index 0000000..e0bc9f1
--- /dev/null
+++ b/live-image-kdump-howto.txt
@@ -0,0 +1,23 @@
+Kdump now works on live images with some manual configurations. Here is the step
+by step guide.
+
+1. Enable crashkernel reservation
+
+Since there isn't any config file that can be used to configure kernel
+parameters for live images before booting them, we have to append 'crashkernel'
+argument in boot menu every time we boot a live image.
+
+2. Change dump target in /etc/kdump.conf
+
+When kdump kernel boots in a live environment, the default target /var/crash is
+in RAM so you need to change the dump target to an external disk or a network
+dump target.
+
+3. Start kdump service
+
+ $ kdumpctl start
+
+4. Trigger a kdump test
+
+ $ echo 1 > /proc/sys/kernel/sysrq
+ $ echo c > /proc/sysrq-trigger
--
2.7.4
6 years, 7 months