summaryrefslogtreecommitdiffstats
path: root/kexec/arch
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 02:56:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 02:56:35 +0000
commiteba0cfa6b0bef4f2e73c8630a7efa3944df8b0f8 (patch)
tree74c37eede1f0634cc5de1c63c934edaa1630c6bc /kexec/arch
parentInitial commit. (diff)
downloadkexec-tools-upstream/1%2.0.27.tar.xz
kexec-tools-upstream/1%2.0.27.zip
Adding upstream version 1:2.0.27.upstream/1%2.0.27upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'kexec/arch')
-rw-r--r--kexec/arch/alpha/Makefile4
-rw-r--r--kexec/arch/alpha/include/arch/options.h20
-rw-r--r--kexec/arch/arm/Makefile34
-rw-r--r--kexec/arch/arm/crashdump-arm.c388
-rw-r--r--kexec/arch/arm/crashdump-arm.h27
-rw-r--r--kexec/arch/arm/include/arch/options.h52
-rw-r--r--kexec/arch/arm/iomem.h9
-rw-r--r--kexec/arch/arm/kexec-arm.c150
-rw-r--r--kexec/arch/arm/kexec-arm.h22
-rw-r--r--kexec/arch/arm/kexec-elf-rel-arm.c36
-rw-r--r--kexec/arch/arm/kexec-uImage-arm.c22
-rw-r--r--kexec/arch/arm/kexec-zImage-arm.c914
-rw-r--r--kexec/arch/arm/phys_to_virt.c22
-rw-r--r--kexec/arch/arm/phys_to_virt.h8
-rw-r--r--kexec/arch/arm64/Makefile50
-rw-r--r--kexec/arch/arm64/crashdump-arm64.c248
-rw-r--r--kexec/arch/arm64/crashdump-arm64.h29
-rw-r--r--kexec/arch/arm64/image-header.h147
-rw-r--r--kexec/arch/arm64/include/arch/options.h43
-rw-r--r--kexec/arch/arm64/iomem.h10
-rw-r--r--kexec/arch/arm64/kexec-arm64.c1365
-rw-r--r--kexec/arch/arm64/kexec-arm64.h84
-rw-r--r--kexec/arch/arm64/kexec-elf-arm64.c170
-rw-r--r--kexec/arch/arm64/kexec-image-arm64.c119
-rw-r--r--kexec/arch/arm64/kexec-uImage-arm64.c52
-rw-r--r--kexec/arch/arm64/kexec-vmlinuz-arm64.c110
-rw-r--r--kexec/arch/cris/Makefile13
-rw-r--r--kexec/arch/cris/cris-setup-simple.S31
-rw-r--r--kexec/arch/cris/include/arch/options.h35
-rw-r--r--kexec/arch/cris/kexec-cris.c111
-rw-r--r--kexec/arch/cris/kexec-cris.h9
-rw-r--r--kexec/arch/cris/kexec-elf-cris.c133
-rw-r--r--kexec/arch/cris/kexec-elf-rel-cris.c43
-rw-r--r--kexec/arch/hppa/Makefile13
-rw-r--r--kexec/arch/hppa/include/arch/options.h32
-rw-r--r--kexec/arch/hppa/kexec-elf-hppa.c159
-rw-r--r--kexec/arch/hppa/kexec-elf-rel-hppa.c37
-rw-r--r--kexec/arch/hppa/kexec-hppa.c148
-rw-r--r--kexec/arch/hppa/kexec-hppa.h9
-rw-r--r--kexec/arch/i386/Makefile20
-rw-r--r--kexec/arch/i386/crashdump-x86.c1049
-rw-r--r--kexec/arch/i386/crashdump-x86.h33
-rw-r--r--kexec/arch/i386/include/arch/options.h86
-rw-r--r--kexec/arch/i386/kexec-beoboot-x86.c132
-rw-r--r--kexec/arch/i386/kexec-bzImage.c471
-rw-r--r--kexec/arch/i386/kexec-elf-rel-x86.c36
-rw-r--r--kexec/arch/i386/kexec-elf-x86.c331
-rw-r--r--kexec/arch/i386/kexec-mb2-x86.c616
-rw-r--r--kexec/arch/i386/kexec-multiboot-x86.c505
-rw-r--r--kexec/arch/i386/kexec-nbi.c249
-rw-r--r--kexec/arch/i386/kexec-x86-common.c444
-rw-r--r--kexec/arch/i386/kexec-x86.c210
-rw-r--r--kexec/arch/i386/kexec-x86.h97
-rw-r--r--kexec/arch/i386/x86-linux-setup.c971
-rw-r--r--kexec/arch/i386/x86-linux-setup.h38
-rw-r--r--kexec/arch/ia64/Makefile16
-rw-r--r--kexec/arch/ia64/crashdump-ia64.c293
-rw-r--r--kexec/arch/ia64/crashdump-ia64.h12
-rw-r--r--kexec/arch/ia64/include/arch/options.h42
-rw-r--r--kexec/arch/ia64/kexec-elf-ia64.c303
-rw-r--r--kexec/arch/ia64/kexec-elf-rel-ia64.c160
-rw-r--r--kexec/arch/ia64/kexec-ia64.c247
-rw-r--r--kexec/arch/ia64/kexec-ia64.h14
-rw-r--r--kexec/arch/ia64/kexec-iomem.c23
-rw-r--r--kexec/arch/loongarch/Makefile22
-rw-r--r--kexec/arch/loongarch/crashdump-loongarch.c220
-rw-r--r--kexec/arch/loongarch/crashdump-loongarch.h26
-rw-r--r--kexec/arch/loongarch/image-header.h79
-rw-r--r--kexec/arch/loongarch/include/arch/options.h28
-rw-r--r--kexec/arch/loongarch/kexec-elf-loongarch.c125
-rw-r--r--kexec/arch/loongarch/kexec-elf-rel-loongarch.c42
-rw-r--r--kexec/arch/loongarch/kexec-loongarch.c375
-rw-r--r--kexec/arch/loongarch/kexec-loongarch.h60
-rw-r--r--kexec/arch/loongarch/kexec-pei-loongarch.c124
-rw-r--r--kexec/arch/m68k/Makefile15
-rw-r--r--kexec/arch/m68k/bootinfo.c285
-rw-r--r--kexec/arch/m68k/bootinfo.h53
-rw-r--r--kexec/arch/m68k/include/arch/options.h45
-rw-r--r--kexec/arch/m68k/kexec-elf-m68k.c183
-rw-r--r--kexec/arch/m68k/kexec-elf-rel-m68k.c39
-rw-r--r--kexec/arch/m68k/kexec-m68k.c110
-rw-r--r--kexec/arch/m68k/kexec-m68k.h9
-rw-r--r--kexec/arch/mips/Makefile29
-rw-r--r--kexec/arch/mips/crashdump-mips.c408
-rw-r--r--kexec/arch/mips/crashdump-mips.h30
-rw-r--r--kexec/arch/mips/include/arch/options.h43
-rw-r--r--kexec/arch/mips/kexec-elf-mips.c242
-rw-r--r--kexec/arch/mips/kexec-elf-rel-mips.c46
-rw-r--r--kexec/arch/mips/kexec-mips.c191
-rw-r--r--kexec/arch/mips/kexec-mips.h30
-rw-r--r--kexec/arch/ppc/Makefile34
-rw-r--r--kexec/arch/ppc/crashdump-powerpc.c433
-rw-r--r--kexec/arch/ppc/crashdump-powerpc.h45
-rw-r--r--kexec/arch/ppc/fixup_dtb.c408
-rw-r--r--kexec/arch/ppc/fixup_dtb.h10
-rw-r--r--kexec/arch/ppc/fs2dt.c471
-rw-r--r--kexec/arch/ppc/include/arch/options.h47
-rw-r--r--kexec/arch/ppc/include/page.h27
-rw-r--r--kexec/arch/ppc/include/types.h27
-rw-r--r--kexec/arch/ppc/kexec-dol-ppc.c468
-rw-r--r--kexec/arch/ppc/kexec-elf-ppc.c458
-rw-r--r--kexec/arch/ppc/kexec-elf-rel-ppc.c69
-rw-r--r--kexec/arch/ppc/kexec-ppc.c968
-rw-r--r--kexec/arch/ppc/kexec-ppc.h86
-rw-r--r--kexec/arch/ppc/kexec-uImage-ppc.c325
-rw-r--r--kexec/arch/ppc/libfdt-wrapper.c189
-rw-r--r--kexec/arch/ppc/ops.h147
-rw-r--r--kexec/arch/ppc/ppc-setup-dol.S174
-rw-r--r--kexec/arch/ppc/ppc-setup-simple.S39
-rw-r--r--kexec/arch/ppc/ppc_asm.h506
-rw-r--r--kexec/arch/ppc64/Makefile26
-rw-r--r--kexec/arch/ppc64/crashdump-ppc64.c644
-rw-r--r--kexec/arch/ppc64/crashdump-ppc64.h51
-rw-r--r--kexec/arch/ppc64/fdt.c78
-rw-r--r--kexec/arch/ppc64/include/arch/fdt.h8
-rw-r--r--kexec/arch/ppc64/include/arch/options.h51
-rw-r--r--kexec/arch/ppc64/kexec-elf-ppc64.c496
-rw-r--r--kexec/arch/ppc64/kexec-elf-rel-ppc64.c204
-rw-r--r--kexec/arch/ppc64/kexec-ppc64.c969
-rw-r--r--kexec/arch/ppc64/kexec-ppc64.h45
-rw-r--r--kexec/arch/ppc64/kexec-zImage-ppc64.c184
-rw-r--r--kexec/arch/s390/Makefile11
-rw-r--r--kexec/arch/s390/crashdump-s390.c90
-rw-r--r--kexec/arch/s390/include/arch/options.h40
-rw-r--r--kexec/arch/s390/kexec-elf-rel-s390.c78
-rw-r--r--kexec/arch/s390/kexec-image.c236
-rw-r--r--kexec/arch/s390/kexec-s390.c269
-rw-r--r--kexec/arch/s390/kexec-s390.h38
-rw-r--r--kexec/arch/sh/Makefile22
-rw-r--r--kexec/arch/sh/crashdump-sh.c166
-rw-r--r--kexec/arch/sh/crashdump-sh.h9
-rw-r--r--kexec/arch/sh/include/arch/options.h42
-rw-r--r--kexec/arch/sh/kexec-elf-rel-sh.c54
-rw-r--r--kexec/arch/sh/kexec-elf-sh.c136
-rw-r--r--kexec/arch/sh/kexec-netbsd-sh.c149
-rw-r--r--kexec/arch/sh/kexec-sh.c259
-rw-r--r--kexec/arch/sh/kexec-sh.h29
-rw-r--r--kexec/arch/sh/kexec-uImage-sh.c24
-rw-r--r--kexec/arch/sh/kexec-zImage-sh.c142
-rw-r--r--kexec/arch/sh/netbsd_booter.S47
-rw-r--r--kexec/arch/x86_64/Makefile24
l---------kexec/arch/x86_64/include/arch/options.h1
-rw-r--r--kexec/arch/x86_64/kexec-bzImage64.c397
-rw-r--r--kexec/arch/x86_64/kexec-elf-rel-x86_64.c94
-rw-r--r--kexec/arch/x86_64/kexec-elf-x86_64.c257
-rw-r--r--kexec/arch/x86_64/kexec-x86_64.c190
-rw-r--r--kexec/arch/x86_64/kexec-x86_64.h41
147 files changed, 24697 insertions, 0 deletions
diff --git a/kexec/arch/alpha/Makefile b/kexec/arch/alpha/Makefile
new file mode 100644
index 0000000..4575d61
--- /dev/null
+++ b/kexec/arch/alpha/Makefile
@@ -0,0 +1,4 @@
+alpha_KEXEC_SRCS=
+dist += kexec/arch/alpha/Makefile kexec/arch/alpha/include/arch/options.h
+ $(alpha_KEXEC_SRCS)
+
diff --git a/kexec/arch/alpha/include/arch/options.h b/kexec/arch/alpha/include/arch/options.h
new file mode 100644
index 0000000..a012c8a
--- /dev/null
+++ b/kexec/arch/alpha/include/arch/options.h
@@ -0,0 +1,20 @@
+#ifndef KEXEC_ARCH_ALPHA_OPTIONS_H
+#define KEXEC_ARCH_ALPHA_OPTIONS_H
+
+#define OPT_ARCH_MAX (OPT_MAX+0)
+
+/* Options relevant to the architecture (excluding loader-specific ones),
+ * in this case none:
+ */
+#define KEXEC_ARCH_OPTIONS \
+ KEXEC_OPTIONS \
+
+#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
+
+/* See the other architectures for details of these; Alpha has no
+ * loader-specific options yet.
+ */
+#define KEXEC_ALL_OPTIONS KEXEC_ARCH_OPTIONS
+#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR
+
+#endif /* KEXEC_ARCH_ALPHA_OPTIONS_H */
diff --git a/kexec/arch/arm/Makefile b/kexec/arch/arm/Makefile
new file mode 100644
index 0000000..4454f47
--- /dev/null
+++ b/kexec/arch/arm/Makefile
@@ -0,0 +1,34 @@
+#
+# kexec arm (linux booting linux)
+#
+include $(srcdir)/kexec/libfdt/Makefile.libfdt
+
+arm_FS2DT = kexec/fs2dt.c
+arm_FS2DT_INCLUDE = -include $(srcdir)/kexec/arch/arm/crashdump-arm.h \
+ -include $(srcdir)/kexec/arch/arm/kexec-arm.h
+
+arm_MEM_REGIONS = kexec/mem_regions.c
+
+arm_KEXEC_SRCS= kexec/arch/arm/kexec-elf-rel-arm.c
+arm_KEXEC_SRCS+= kexec/arch/arm/kexec-zImage-arm.c
+arm_KEXEC_SRCS+= kexec/arch/arm/kexec-uImage-arm.c
+arm_KEXEC_SRCS+= kexec/arch/arm/kexec-arm.c
+arm_KEXEC_SRCS+= kexec/arch/arm/crashdump-arm.c
+arm_KEXEC_SRCS+= kexec/fs2dt.c
+
+libfdt_SRCS += $(LIBFDT_SRCS:%=kexec/libfdt/%)
+
+arm_CPPFLAGS = -I$(srcdir)/kexec/libfdt
+
+# We want 64-bit file IO for kdump to work correctly on LPAE systems
+arm_CPPFLAGS += -D_FILE_OFFSET_BITS=64
+
+arm_KEXEC_SRCS += $(libfdt_SRCS)
+
+arm_UIMAGE = kexec/kexec-uImage.c
+arm_PHYS_TO_VIRT = kexec/arch/arm/phys_to_virt.c
+
+dist += kexec/arch/arm/Makefile $(arm_KEXEC_SRCS) $(arm_PHYS_TO_VIRT) \
+ kexec/arch/arm/iomem.h kexec/arch/arm/phys_to_virt.h \
+ kexec/arch/arm/crashdump-arm.h kexec/arch/arm/kexec-arm.h \
+ kexec/arch/arm/include/arch/options.h
diff --git a/kexec/arch/arm/crashdump-arm.c b/kexec/arch/arm/crashdump-arm.c
new file mode 100644
index 0000000..1ec1826
--- /dev/null
+++ b/kexec/arch/arm/crashdump-arm.c
@@ -0,0 +1,388 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) Nokia Corporation, 2010.
+ * Author: Mika Westerberg
+ *
+ * Based on x86 implementation
+ * Copyright (C) IBM Corporation, 2005. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <limits.h>
+#include <elf.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "../../crashdump.h"
+#include "../../mem_regions.h"
+#include "crashdump-arm.h"
+#include "iomem.h"
+#include "phys_to_virt.h"
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define ELFDATANATIVE ELFDATA2LSB
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define ELFDATANATIVE ELFDATA2MSB
+#else
+#error "Unknown machine endian"
+#endif
+
+/*
+ * Used to save various memory ranges/regions needed for the captured
+ * kernel to boot. (lime memmap= option in other archs)
+ */
+static struct memory_range crash_memory_ranges[CRASH_MAX_MEMORY_RANGES];
+struct memory_ranges usablemem_rgns = {
+ .max_size = CRASH_MAX_MEMORY_RANGES,
+ .ranges = crash_memory_ranges,
+};
+
+/* The boot-time physical memory range reserved for crashkernel region */
+struct memory_range crash_kernel_mem;
+
+/* reserved regions */
+#define CRASH_MAX_RESERVED_RANGES 2
+static struct memory_range crash_reserved_ranges[CRASH_MAX_RESERVED_RANGES];
+static struct memory_ranges crash_reserved_rgns = {
+ .max_size = CRASH_MAX_RESERVED_RANGES,
+ .ranges = crash_reserved_ranges,
+};
+
+struct memory_range elfcorehdr_mem;
+
+static struct crash_elf_info elf_info = {
+ .class = ELFCLASS32,
+ .data = ELFDATANATIVE,
+ .machine = EM_ARM,
+ .page_offset = DEFAULT_PAGE_OFFSET,
+};
+
+extern unsigned long long user_page_offset;
+
+static int get_kernel_page_offset(struct kexec_info *info,
+ struct crash_elf_info *elf_info)
+{
+ unsigned long long stext_sym_addr = get_kernel_sym("_stext");
+ if (stext_sym_addr == 0) {
+ if (user_page_offset != (-1ULL)) {
+ elf_info->page_offset = user_page_offset;
+ dbgprintf("Unable to get _stext symbol from /proc/kallsyms, "
+ "use user provided vaule: %llx\n",
+ elf_info->page_offset);
+ return 0;
+ }
+ elf_info->page_offset = (unsigned long long)DEFAULT_PAGE_OFFSET;
+ dbgprintf("Unable to get _stext symbol from /proc/kallsyms, "
+ "use default: %llx\n",
+ elf_info->page_offset);
+ return 0;
+ } else if ((user_page_offset != (-1ULL)) &&
+ (user_page_offset != stext_sym_addr)) {
+ fprintf(stderr, "PAGE_OFFSET is set to %llx "
+ "instead of user provided value %llx\n",
+ stext_sym_addr & (~KVBASE_MASK),
+ user_page_offset);
+ }
+ elf_info->page_offset = stext_sym_addr & (~KVBASE_MASK);
+ return 0;
+}
+
+/**
+ * crash_get_memory_ranges() - read system physical memory
+ *
+ * Function reads through system physical memory and stores found memory regions
+ * in @crash_memory_ranges. Number of memory regions found is placed in
+ * @crash_memory_nr_ranges. Regions are sorted in ascending order.
+ *
+ * Returns %0 in case of success and %-1 otherwise (errno is set).
+ */
+static int crash_get_memory_ranges(void)
+{
+ int i;
+
+ if (usablemem_rgns.size < 1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ dbgprint_mem_range("Reserved memory ranges",
+ crash_reserved_rgns.ranges,
+ crash_reserved_rgns.size);
+
+ /*
+ * Exclude all reserved memory from the usable memory regions.
+ * We want to avoid dumping the crashkernel region itself. Note
+ * that this may result memory regions in usablemem_rgns being
+ * split.
+ */
+ for (i = 0; i < crash_reserved_rgns.size; i++) {
+ if (mem_regions_exclude(&usablemem_rgns,
+ &crash_reserved_rgns.ranges[i])) {
+ fprintf(stderr,
+ "Error: Number of crash memory ranges excedeed the max limit\n");
+ errno = ENOMEM;
+ return -1;
+ }
+ }
+
+ /*
+ * Make sure that the memory regions are sorted.
+ */
+ mem_regions_sort(&usablemem_rgns);
+
+ dbgprint_mem_range("Coredump memory ranges",
+ usablemem_rgns.ranges, usablemem_rgns.size);
+
+ return 0;
+}
+
+/**
+ * cmdline_add_elfcorehdr() - adds elfcorehdr= to @cmdline
+ * @cmdline: buffer where parameter is placed
+ * @elfcorehdr: physical address of elfcorehdr
+ *
+ * Function appends 'elfcorehdr=start' at the end of the command line given in
+ * @cmdline. Note that @cmdline must be at least %COMMAND_LINE_SIZE bytes long
+ * (inclunding %NUL).
+ */
+static void cmdline_add_elfcorehdr(char *cmdline, unsigned long elfcorehdr)
+{
+ char buf[COMMAND_LINE_SIZE];
+ int buflen;
+
+ buflen = snprintf(buf, sizeof(buf), "%s elfcorehdr=%#lx",
+ cmdline, elfcorehdr);
+ if (buflen < 0)
+ die("Failed to construct elfcorehdr= command line parameter\n");
+ if (buflen >= sizeof(buf))
+ die("Command line overflow\n");
+
+ (void) strncpy(cmdline, buf, COMMAND_LINE_SIZE);
+ cmdline[COMMAND_LINE_SIZE - 1] = '\0';
+}
+
+/**
+ * cmdline_add_mem() - adds mem= parameter to kernel command line
+ * @cmdline: buffer where parameter is placed
+ * @size: size of the kernel reserved memory (in bytes)
+ *
+ * This function appends 'mem=size' at the end of the command line given in
+ * @cmdline. Note that @cmdline must be at least %COMMAND_LINE_SIZE bytes long
+ * (including %NUL).
+ */
+static void cmdline_add_mem(char *cmdline, unsigned long size)
+{
+ char buf[COMMAND_LINE_SIZE];
+ int buflen;
+
+ buflen = snprintf(buf, sizeof(buf), "%s mem=%ldK", cmdline, size >> 10);
+ if (buflen < 0)
+ die("Failed to construct mem= command line parameter\n");
+ if (buflen >= sizeof(buf))
+ die("Command line overflow\n");
+
+ (void) strncpy(cmdline, buf, COMMAND_LINE_SIZE);
+ cmdline[COMMAND_LINE_SIZE - 1] = '\0';
+}
+
+static unsigned long long range_size(const struct memory_range *r)
+{
+ return r->end - r->start + 1;
+}
+
+static void dump_memory_ranges(void)
+{
+ int i;
+
+ if (!kexec_debug)
+ return;
+
+ dbgprintf("crashkernel: [%#llx - %#llx] (%ldM)\n",
+ crash_kernel_mem.start, crash_kernel_mem.end,
+ (unsigned long)range_size(&crash_kernel_mem) >> 20);
+
+ for (i = 0; i < usablemem_rgns.size; i++) {
+ struct memory_range *r = usablemem_rgns.ranges + i;
+ dbgprintf("memory range: [%#llx - %#llx] (%ldM)\n",
+ r->start, r->end, (unsigned long)range_size(r) >> 20);
+ }
+}
+
+/**
+ * load_crashdump_segments() - loads additional segments needed for kdump
+ * @info: kexec info structure
+ * @mod_cmdline: kernel command line
+ *
+ * This function loads additional segments which are needed for the dump capture
+ * kernel. It also updates kernel command line passed in @mod_cmdline to have
+ * right parameters for the dump capture kernel.
+ *
+ * Return %0 in case of success and %-1 in case of error.
+ */
+int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline)
+{
+ unsigned long elfcorehdr;
+ unsigned long bufsz;
+ void *buf;
+ int err;
+ int last_ranges;
+
+ /*
+ * First fetch all the memory (RAM) ranges that we are going to pass to
+ * the crashdump kernel during panic.
+ */
+ err = crash_get_memory_ranges();
+ if (err)
+ return err;
+
+ /*
+ * Now that we have memory regions sorted, we can use first memory
+ * region as PHYS_OFFSET.
+ */
+ phys_offset = usablemem_rgns.ranges->start;
+
+ if (get_kernel_page_offset(info, &elf_info))
+ return -1;
+
+ dbgprintf("phys offset = %#llx, page offset = %llx\n",
+ phys_offset, elf_info.page_offset);
+
+ /*
+ * Ensure that the crash kernel memory range is sane. The crash kernel
+ * must be located within memory which is visible during booting.
+ */
+ if (crash_kernel_mem.end > ARM_MAX_VIRTUAL) {
+ fprintf(stderr,
+ "Crash kernel memory [0x%llx-0x%llx] is inaccessible at boot - unable to load crash kernel\n",
+ crash_kernel_mem.start, crash_kernel_mem.end);
+ return -1;
+ }
+
+ last_ranges = usablemem_rgns.size - 1;
+ if (last_ranges < 0)
+ last_ranges = 0;
+
+ if (crash_memory_ranges[last_ranges].end > UINT32_MAX) {
+ dbgprintf("Using 64-bit ELF core format\n");
+
+ /* for support LPAE enabled kernel*/
+ elf_info.class = ELFCLASS64;
+
+ err = crash_create_elf64_headers(info, &elf_info,
+ usablemem_rgns.ranges,
+ usablemem_rgns.size, &buf, &bufsz,
+ ELF_CORE_HEADER_ALIGN);
+ } else {
+ dbgprintf("Using 32-bit ELF core format\n");
+ err = crash_create_elf32_headers(info, &elf_info,
+ usablemem_rgns.ranges,
+ usablemem_rgns.size, &buf, &bufsz,
+ ELF_CORE_HEADER_ALIGN);
+ }
+ if (err)
+ return err;
+
+ /*
+ * We allocate ELF core header from the end of the memory area reserved
+ * for the crashkernel. We align the header to SECTION_SIZE (which is
+ * 1MB) so that available memory passed in kernel command line will be
+ * aligned to 1MB. This is because kernel create_mapping() wants memory
+ * regions to be aligned to SECTION_SIZE.
+ */
+ elfcorehdr = add_buffer_phys_virt(info, buf, bufsz, bufsz, 1 << 20,
+ crash_kernel_mem.start,
+ crash_kernel_mem.end, -1, 0);
+
+ elfcorehdr_mem.start = elfcorehdr;
+ elfcorehdr_mem.end = elfcorehdr + bufsz - 1;
+
+ dbgprintf("elfcorehdr 0x%llx-0x%llx\n", elfcorehdr_mem.start,
+ elfcorehdr_mem.end);
+ cmdline_add_elfcorehdr(mod_cmdline, elfcorehdr);
+
+ /*
+ * Add 'mem=size' parameter to dump capture kernel command line. This
+ * prevents the dump capture kernel from using any other memory regions
+ * which belong to the primary kernel.
+ */
+ cmdline_add_mem(mod_cmdline, elfcorehdr - crash_kernel_mem.start);
+
+ dump_memory_ranges();
+ dbgprintf("kernel command line: \"%s\"\n", mod_cmdline);
+
+ return 0;
+}
+
+/**
+ * iomem_range_callback() - callback called for each iomem region
+ * @data: not used
+ * @nr: not used
+ * @str: name of the memory region (not NULL terminated)
+ * @base: start address of the memory region
+ * @length: size of the memory region
+ *
+ * This function is called for each memory range in /proc/iomem, stores
+ * the location of the crash kernel range into @crash_kernel_mem, and
+ * stores the system RAM into @usablemem_rgns.
+ */
+static int iomem_range_callback(void *UNUSED(data), int UNUSED(nr),
+ char *str, unsigned long long base,
+ unsigned long long length)
+{
+ if (strncmp(str, CRASH_KERNEL_BOOT, strlen(CRASH_KERNEL_BOOT)) == 0) {
+ crash_kernel_mem.start = base;
+ crash_kernel_mem.end = base + length - 1;
+ crash_kernel_mem.type = RANGE_RAM;
+ return mem_regions_add(&crash_reserved_rgns,
+ base, length, RANGE_RAM);
+ }
+ else if (strncmp(str, CRASH_KERNEL, strlen(CRASH_KERNEL)) == 0) {
+ if (crash_kernel_mem.start == crash_kernel_mem.end) {
+ crash_kernel_mem.start = base;
+ crash_kernel_mem.end = base + length - 1;
+ crash_kernel_mem.type = RANGE_RAM;
+ }
+ return mem_regions_add(&crash_reserved_rgns,
+ base, length, RANGE_RAM);
+ }
+ else if (strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM)) == 0) {
+ return mem_regions_add(&usablemem_rgns,
+ base, length, RANGE_RAM);
+ }
+ return 0;
+}
+
+/**
+ * is_crashkernel_mem_reserved() - check for the crashkernel reserved region
+ *
+ * Check for the crashkernel reserved region in /proc/iomem, and return
+ * true if it is present, or false otherwise. We use this to store the
+ * location of this region, and system RAM regions.
+ */
+int is_crashkernel_mem_reserved(void)
+{
+ kexec_iomem_for_each_line(NULL, iomem_range_callback, NULL);
+
+ return crash_kernel_mem.start != crash_kernel_mem.end;
+}
+
+int get_crash_kernel_load_range(uint64_t *start, uint64_t *end)
+{
+ return parse_iomem_single("Crash kernel\n", start, end);
+}
diff --git a/kexec/arch/arm/crashdump-arm.h b/kexec/arch/arm/crashdump-arm.h
new file mode 100644
index 0000000..bbdf8bf
--- /dev/null
+++ b/kexec/arch/arm/crashdump-arm.h
@@ -0,0 +1,27 @@
+#ifndef CRASHDUMP_ARM_H
+#define CRASHDUMP_ARM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define COMMAND_LINE_SIZE 1024
+#define DEFAULT_PAGE_OFFSET (0xc0000000)
+#define KVBASE_MASK (0x1ffffff)
+#define CRASH_MAX_MEMORY_RANGES 32
+#define ARM_MAX_VIRTUAL UINT32_MAX
+
+
+extern struct memory_ranges usablemem_rgns;
+extern struct memory_range crash_kernel_mem;
+extern struct memory_range elfcorehdr_mem;
+
+struct kexec_info;
+
+extern int load_crashdump_segments(struct kexec_info *, char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CRASHDUMP_ARM_H */
diff --git a/kexec/arch/arm/include/arch/options.h b/kexec/arch/arm/include/arch/options.h
new file mode 100644
index 0000000..6fabfb7
--- /dev/null
+++ b/kexec/arch/arm/include/arch/options.h
@@ -0,0 +1,52 @@
+#ifndef KEXEC_ARCH_ARM_OPTIONS_H
+#define KEXEC_ARCH_ARM_OPTIONS_H
+
+#define OPT_DT_NO_OLD_ROOT (OPT_MAX+0)
+#define OPT_ARCH_MAX (OPT_MAX+1)
+
+#define OPT_DTB (OPT_ARCH_MAX+0)
+#define OPT_ATAGS (OPT_ARCH_MAX+1)
+#define OPT_IMAGE_SIZE (OPT_ARCH_MAX+2)
+#define OPT_PAGE_OFFSET (OPT_ARCH_MAX+3)
+#define OPT_APPEND (OPT_ARCH_MAX+4)
+#define OPT_RAMDISK (OPT_ARCH_MAX+5)
+
+/* Options relevant to the architecture (excluding loader-specific ones),
+ * in this case none:
+ */
+#define KEXEC_ARCH_OPTIONS \
+ KEXEC_OPTIONS \
+ { "dt-no-old-root", 0, 0, OPT_DT_NO_OLD_ROOT }, \
+
+#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
+
+/* The following two #defines list ALL of the options added by all of the
+ * architecture's loaders.
+ * o main() uses this complete list to scan for its options, ignoring
+ * arch-specific/loader-specific ones.
+ * o Then, arch_process_options() uses this complete list to scan for its
+ * options, ignoring general/loader-specific ones.
+ * o Then, the file_type[n].load re-scans for options, using
+ * KEXEC_ARCH_OPTIONS plus its loader-specific options subset.
+ * Any unrecognised options cause an error here.
+ *
+ * This is done so that main()'s/arch_process_options()'s getopt_long() calls
+ * don't choose a kernel filename from random arguments to options they don't
+ * recognise -- as they now recognise (if not act upon) all possible options.
+ */
+#define KEXEC_ALL_OPTIONS \
+ KEXEC_ARCH_OPTIONS \
+ { "command-line", 1, 0, OPT_APPEND }, \
+ { "append", 1, 0, OPT_APPEND }, \
+ { "initrd", 1, 0, OPT_RAMDISK }, \
+ { "ramdisk", 1, 0, OPT_RAMDISK }, \
+ { "dtb", 1, 0, OPT_DTB }, \
+ { "atags", 0, 0, OPT_ATAGS }, \
+ { "image-size", 1, 0, OPT_IMAGE_SIZE }, \
+ { "page-offset", 1, 0, OPT_PAGE_OFFSET },
+
+#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR ""
+
+extern unsigned int kexec_arm_image_size;
+
+#endif /* KEXEC_ARCH_ARM_OPTIONS_H */
diff --git a/kexec/arch/arm/iomem.h b/kexec/arch/arm/iomem.h
new file mode 100644
index 0000000..85f958e
--- /dev/null
+++ b/kexec/arch/arm/iomem.h
@@ -0,0 +1,9 @@
+#ifndef IOMEM_H
+#define IOMEM_H
+
+#define SYSTEM_RAM "System RAM\n"
+#define SYSTEM_RAM_BOOT "System RAM (boot alias)\n"
+#define CRASH_KERNEL "Crash kernel\n"
+#define CRASH_KERNEL_BOOT "Crash kernel (boot alias)\n"
+
+#endif
diff --git a/kexec/arch/arm/kexec-arm.c b/kexec/arch/arm/kexec-arm.c
new file mode 100644
index 0000000..49f35b1
--- /dev/null
+++ b/kexec/arch/arm/kexec-arm.c
@@ -0,0 +1,150 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * modified from kexec-ppc.c
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "kexec-arm.h"
+#include <arch/options.h>
+#include "../../fs2dt.h"
+#include "iomem.h"
+
+#define MAX_MEMORY_RANGES 64
+#define MAX_LINE 160
+static struct memory_range memory_range[MAX_MEMORY_RANGES];
+
+/* Return a sorted list of available memory ranges. */
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long UNUSED(kexec_flags))
+{
+ const char *iomem = proc_iomem();
+ int memory_ranges = 0;
+ char line[MAX_LINE];
+ FILE *fp;
+ fp = fopen(iomem, "r");
+ if (!fp) {
+ fprintf(stderr, "Cannot open %s: %s\n",
+ iomem, strerror(errno));
+ return -1;
+ }
+
+ while(fgets(line, sizeof(line), fp) != 0) {
+ unsigned long long start, end;
+ char *str;
+ int type;
+ int consumed;
+ int count;
+ if (memory_ranges >= MAX_MEMORY_RANGES)
+ break;
+ count = sscanf(line, "%llx-%llx : %n",
+ &start, &end, &consumed);
+ if (count != 2)
+ continue;
+ str = line + consumed;
+
+ if (memcmp(str, SYSTEM_RAM_BOOT, strlen(SYSTEM_RAM_BOOT)) == 0 ||
+ memcmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM)) == 0) {
+ type = RANGE_RAM;
+ }
+ else if (memcmp(str, "reserved\n", 9) == 0) {
+ type = RANGE_RESERVED;
+ }
+ else {
+ continue;
+ }
+
+ memory_range[memory_ranges].start = start;
+ memory_range[memory_ranges].end = end;
+ memory_range[memory_ranges].type = type;
+ memory_ranges++;
+ }
+ fclose(fp);
+ *range = memory_range;
+ *ranges = memory_ranges;
+
+ dbgprint_mem_range("MEMORY RANGES", *range, *ranges);
+
+ return 0;
+}
+
+/* Supported file types and callbacks */
+struct file_type file_type[] = {
+ /* uImage is probed before zImage because the latter also accepts
+ uncompressed images. */
+ {"uImage", uImage_arm_probe, uImage_arm_load, zImage_arm_usage},
+ {"zImage", zImage_arm_probe, zImage_arm_load, zImage_arm_usage},
+};
+int file_types = sizeof(file_type) / sizeof(file_type[0]);
+
+void arch_usage(void)
+{
+ printf(" --image-size=<size>\n"
+ " Specify the assumed total image size of\n"
+ " the kernel that is about to be loaded,\n"
+ " including the .bss section, as reported\n"
+ " by 'arm-linux-size vmlinux'. If not\n"
+ " specified, this value is implicitly set\n"
+ " to the compressed images size * 4.\n"
+ " --dt-no-old-root\n"
+ " do not reuse old kernel root= param.\n"
+ " while creating flatten device tree.\n");
+}
+
+int arch_process_options(int argc, char **argv)
+{
+ /* We look for all options so getopt_long doesn't start reordering
+ * argv[] before file_type[n].load() gets a look in.
+ */
+ static const struct option options[] = {
+ KEXEC_ALL_OPTIONS
+ { 0, 0, NULL, 0 },
+ };
+ static const char short_options[] = KEXEC_ALL_OPT_STR;
+ int opt;
+
+ opterr = 0; /* Don't complain about unrecognized options here */
+ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch(opt) {
+ case OPT_DT_NO_OLD_ROOT:
+ dt_no_old_root = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ /* Reset getopt for the next pass; called in other source modules */
+ opterr = 1;
+ optind = 1;
+ return 0;
+}
+
+const struct arch_map_entry arches[] = {
+ { "arm", KEXEC_ARCH_ARM },
+ { NULL, 0 },
+};
+
+int arch_compat_trampoline(struct kexec_info *UNUSED(info))
+{
+ return 0;
+}
+
+void arch_update_purgatory(struct kexec_info *UNUSED(info))
+{
+}
+
+/* return 1 if /sys/firmware/fdt exists, otherwise return 0 */
+int have_sysfs_fdt(void)
+{
+ return !access(SYSFS_FDT, F_OK);
+}
diff --git a/kexec/arch/arm/kexec-arm.h b/kexec/arch/arm/kexec-arm.h
new file mode 100644
index 0000000..a74cce2
--- /dev/null
+++ b/kexec/arch/arm/kexec-arm.h
@@ -0,0 +1,22 @@
+#ifndef KEXEC_ARM_H
+#define KEXEC_ARM_H
+
+#include <sys/types.h>
+
+#define SYSFS_FDT "/sys/firmware/fdt"
+#define BOOT_BLOCK_VERSION 17
+#define BOOT_BLOCK_LAST_COMP_VERSION 16
+
+extern off_t initrd_base, initrd_size;
+
+int zImage_arm_probe(const char *buf, off_t len);
+int zImage_arm_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void zImage_arm_usage(void);
+
+int uImage_arm_probe(const char *buf, off_t len);
+int uImage_arm_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+extern int have_sysfs_fdt(void);
+
+#endif /* KEXEC_ARM_H */
diff --git a/kexec/arch/arm/kexec-elf-rel-arm.c b/kexec/arch/arm/kexec-elf-rel-arm.c
new file mode 100644
index 0000000..a939cf4
--- /dev/null
+++ b/kexec/arch/arm/kexec-elf-rel-arm.c
@@ -0,0 +1,36 @@
+#include <stdio.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+
+int machine_verify_elf_rel(struct mem_ehdr *ehdr)
+{
+ if (ehdr->ei_data != ELFDATA2MSB) {
+ return 0;
+ }
+ if (ehdr->ei_class != ELFCLASS32) {
+ return 0;
+ }
+ if (ehdr->e_machine != EM_ARM)
+ {
+ return 0;
+ }
+ return 1;
+}
+
+void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr),
+ struct mem_sym *UNUSED(sym), unsigned long r_type, void *location,
+ unsigned long address, unsigned long value)
+{
+ switch(r_type) {
+ case R_ARM_ABS32:
+ *((uint32_t *)location) += value;
+ break;
+ case R_ARM_REL32:
+ *((uint32_t *)location) += value - address;
+ break;
+ default:
+ die("Unknown rel relocation: %lu\n", r_type);
+ break;
+ }
+}
diff --git a/kexec/arch/arm/kexec-uImage-arm.c b/kexec/arch/arm/kexec-uImage-arm.c
new file mode 100644
index 0000000..03c2f4d
--- /dev/null
+++ b/kexec/arch/arm/kexec-uImage-arm.c
@@ -0,0 +1,22 @@
+/*
+ * uImage support added by Marc Andre Tanner <mat@brain-dump.org>
+ */
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <image.h>
+#include <kexec-uImage.h>
+#include "../../kexec.h"
+#include "kexec-arm.h"
+
+int uImage_arm_probe(const char *buf, off_t len)
+{
+ return uImage_probe_kernel(buf, len, IH_ARCH_ARM);
+}
+
+int uImage_arm_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ return zImage_arm_load(argc, argv, buf + sizeof(struct image_header),
+ len - sizeof(struct image_header), info);
+}
diff --git a/kexec/arch/arm/kexec-zImage-arm.c b/kexec/arch/arm/kexec-zImage-arm.c
new file mode 100644
index 0000000..8b474dd
--- /dev/null
+++ b/kexec/arch/arm/kexec-zImage-arm.c
@@ -0,0 +1,914 @@
+/*
+ * - 08/21/2007 ATAG support added by Uli Luckas <u.luckas@road.de>
+ *
+ */
+#define _GNU_SOURCE
+#define _XOPEN_SOURCE
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <libfdt.h>
+#include <arch/options.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "kexec-arm.h"
+#include "../../fs2dt.h"
+#include "crashdump-arm.h"
+#include "iomem.h"
+
+#define BOOT_PARAMS_SIZE 1536
+
+off_t initrd_base, initrd_size;
+unsigned int kexec_arm_image_size = 0;
+unsigned long long user_page_offset = (-1ULL);
+
+struct zimage_header {
+ uint32_t instr[9];
+ uint32_t magic;
+#define ZIMAGE_MAGIC cpu_to_le32(0x016f2818)
+ uint32_t start;
+ uint32_t end;
+ uint32_t endian;
+
+ /* Extension to the data passed to the boot agent. The offset
+ * points at a tagged table following a similar format to the
+ * ATAGs.
+ */
+ uint32_t magic2;
+#define ZIMAGE_MAGIC2 (0x45454545)
+ uint32_t extension_tag_offset;
+};
+
+struct android_image {
+ char magic[8];
+ uint32_t kernel_size;
+ uint32_t kernel_addr;
+ uint32_t ramdisk_size;
+ uint32_t ramdisk_addr;
+ uint32_t stage2_size;
+ uint32_t stage2_addr;
+ uint32_t tags_addr;
+ uint32_t page_size;
+ uint32_t reserved1;
+ uint32_t reserved2;
+ char name[16];
+ char command_line[512];
+ uint32_t chksum[8];
+};
+
+struct tag_header {
+ uint32_t size;
+ uint32_t tag;
+};
+
+/* The list must start with an ATAG_CORE node */
+#define ATAG_CORE 0x54410001
+
+struct tag_core {
+ uint32_t flags; /* bit 0 = read-only */
+ uint32_t pagesize;
+ uint32_t rootdev;
+};
+
+/* it is allowed to have multiple ATAG_MEM nodes */
+#define ATAG_MEM 0x54410002
+
+struct tag_mem32 {
+ uint32_t size;
+ uint32_t start; /* physical start address */
+};
+
+/* describes where the compressed ramdisk image lives (virtual address) */
+/*
+ * this one accidentally used virtual addresses - as such,
+ * it's deprecated.
+ */
+#define ATAG_INITRD 0x54410005
+
+/* describes where the compressed ramdisk image lives (physical address) */
+#define ATAG_INITRD2 0x54420005
+
+struct tag_initrd {
+ uint32_t start; /* physical start address */
+ uint32_t size; /* size of compressed ramdisk image in bytes */
+};
+
+/* command line: \0 terminated string */
+#define ATAG_CMDLINE 0x54410009
+
+struct tag_cmdline {
+ char cmdline[1]; /* this is the minimum size */
+};
+
+/* The list ends with an ATAG_NONE node. */
+#define ATAG_NONE 0x00000000
+
+struct tag {
+ struct tag_header hdr;
+ union {
+ struct tag_core core;
+ struct tag_mem32 mem;
+ struct tag_initrd initrd;
+ struct tag_cmdline cmdline;
+ } u;
+};
+
+#define tag_next(t) ((struct tag *)((uint32_t *)(t) + (t)->hdr.size))
+#define byte_size(t) ((t)->hdr.size << 2)
+#define tag_size(type) ((sizeof(struct tag_header) + sizeof(struct type) + 3) >> 2)
+
+struct zimage_tag {
+ struct tag_header hdr;
+ union {
+#define ZIMAGE_TAG_KRNL_SIZE cpu_to_le32(0x5a534c4b)
+ struct zimage_krnl_size {
+ uint32_t size_ptr;
+ uint32_t bss_size;
+ } krnl_size;
+ } u;
+};
+
+int zImage_arm_probe(const char *UNUSED(buf), off_t UNUSED(len))
+{
+ /*
+ * Only zImage loading is supported. Do not check if
+ * the buffer is valid kernel image
+ */
+ return 0;
+}
+
+void zImage_arm_usage(void)
+{
+ printf( " --command-line=STRING Set the kernel command line to STRING.\n"
+ " --append=STRING Set the kernel command line to STRING.\n"
+ " --initrd=FILE Use FILE as the kernel's initial ramdisk.\n"
+ " --ramdisk=FILE Use FILE as the kernel's initial ramdisk.\n"
+ " --dtb=FILE Use FILE as the fdt blob.\n"
+ " --atags Use ATAGs instead of device-tree.\n"
+ " --page-offset=PAGE_OFFSET\n"
+ " Set PAGE_OFFSET of crash dump vmcore\n"
+ );
+}
+
+static
+struct tag * atag_read_tags(void)
+{
+ static unsigned long buf[BOOT_PARAMS_SIZE];
+ const char fn[]= "/proc/atags";
+ FILE *fp;
+ fp = fopen(fn, "r");
+ if (!fp) {
+ fprintf(stderr, "Cannot open %s: %s\n",
+ fn, strerror(errno));
+ return NULL;
+ }
+
+ if (!fread(buf, sizeof(buf[1]), BOOT_PARAMS_SIZE, fp)) {
+ fclose(fp);
+ return NULL;
+ }
+
+ if (ferror(fp)) {
+ fprintf(stderr, "Cannot read %s: %s\n",
+ fn, strerror(errno));
+ fclose(fp);
+ return NULL;
+ }
+
+ fclose(fp);
+ return (struct tag *) buf;
+}
+
+static
+int create_mem32_tag(struct tag_mem32 *tag_mem32)
+{
+ const char fn[]= "/proc/device-tree/memory/reg";
+ uint32_t tmp[2];
+ FILE *fp;
+
+ fp = fopen(fn, "r");
+ if (!fp) {
+ fprintf(stderr, "Cannot open %s: %m\n", fn);
+ return -1;
+ }
+
+ if (fread(tmp, sizeof(tmp[0]), 2, fp) != 2) {
+ fprintf(stderr, "Short read %s\n", fn);
+ fclose(fp);
+ return -1;
+ }
+
+ if (ferror(fp)) {
+ fprintf(stderr, "Cannot read %s: %m\n", fn);
+ fclose(fp);
+ return -1;
+ }
+
+ /* atags_mem32 has base/size fields reversed! */
+ tag_mem32->size = be32_to_cpu(tmp[1]);
+ tag_mem32->start = be32_to_cpu(tmp[0]);
+
+ fclose(fp);
+ return 0;
+}
+
+static
+int atag_arm_load(struct kexec_info *info, unsigned long base,
+ const char *command_line, off_t command_line_len, const char *initrd)
+{
+ struct tag *saved_tags = atag_read_tags();
+ char *buf;
+ off_t len;
+ struct tag *params;
+
+ buf = xmalloc(getpagesize());
+ memset(buf, 0xff, getpagesize());
+ params = (struct tag *)buf;
+
+ if (saved_tags) {
+ // Copy tags
+ saved_tags = (struct tag *) saved_tags;
+ while(byte_size(saved_tags)) {
+ switch (saved_tags->hdr.tag) {
+ case ATAG_INITRD:
+ case ATAG_INITRD2:
+ case ATAG_CMDLINE:
+ case ATAG_NONE:
+ // skip these tags
+ break;
+ default:
+ // copy all other tags
+ memcpy(params, saved_tags, byte_size(saved_tags));
+ params = tag_next(params);
+ }
+ saved_tags = tag_next(saved_tags);
+ }
+ } else {
+ params->hdr.size = 2;
+ params->hdr.tag = ATAG_CORE;
+ params = tag_next(params);
+
+ if (!create_mem32_tag(&params->u.mem)) {
+ params->hdr.size = 4;
+ params->hdr.tag = ATAG_MEM;
+ params = tag_next(params);
+ }
+ }
+
+ if (initrd) {
+ params->hdr.size = tag_size(tag_initrd);
+ params->hdr.tag = ATAG_INITRD2;
+ params->u.initrd.start = initrd_base;
+ params->u.initrd.size = initrd_size;
+ params = tag_next(params);
+ }
+
+ if (command_line) {
+ params->hdr.size = (sizeof(struct tag_header) + command_line_len + 3) >> 2;
+ params->hdr.tag = ATAG_CMDLINE;
+ memcpy(params->u.cmdline.cmdline, command_line,
+ command_line_len);
+ params->u.cmdline.cmdline[command_line_len - 1] = '\0';
+ params = tag_next(params);
+ }
+
+ params->hdr.size = 0;
+ params->hdr.tag = ATAG_NONE;
+
+ len = ((char *)params - buf) + sizeof(struct tag_header);
+
+ add_segment(info, buf, len, base, len);
+
+ return 0;
+}
+
+static int setup_dtb_prop(char **bufp, off_t *sizep, int parentoffset,
+ const char *node_name, const char *prop_name,
+ const void *val, int len)
+{
+ char *dtb_buf;
+ off_t dtb_size;
+ int off;
+ int prop_len = 0;
+ const struct fdt_property *prop;
+
+ if ((bufp == NULL) || (sizep == NULL) || (*bufp == NULL))
+ die("Internal error\n");
+
+ dtb_buf = *bufp;
+ dtb_size = *sizep;
+
+ /* check if the subnode has already exist */
+ off = fdt_subnode_offset(dtb_buf, parentoffset, node_name);
+ if (off == -FDT_ERR_NOTFOUND) {
+ dtb_size += fdt_node_len(node_name);
+ fdt_set_totalsize(dtb_buf, dtb_size);
+ dtb_buf = xrealloc(dtb_buf, dtb_size);
+ off = fdt_add_subnode(dtb_buf, parentoffset, node_name);
+ }
+
+ if (off < 0) {
+ fprintf(stderr, "FDT: Error adding %s node.\n", node_name);
+ return -1;
+ }
+
+ prop = fdt_get_property(dtb_buf, off, prop_name, &prop_len);
+ if ((prop == NULL) && (prop_len != -FDT_ERR_NOTFOUND)) {
+ die("FDT: fdt_get_property");
+ } else if (prop == NULL) {
+ /* prop_len == -FDT_ERR_NOTFOUND */
+ /* prop doesn't exist */
+ dtb_size += fdt_prop_len(prop_name, len);
+ } else {
+ if (prop_len < len)
+ dtb_size += FDT_TAGALIGN(len - prop_len);
+ }
+
+ if (fdt_totalsize(dtb_buf) < dtb_size) {
+ fdt_set_totalsize(dtb_buf, dtb_size);
+ dtb_buf = xrealloc(dtb_buf, dtb_size);
+ }
+
+ if (fdt_setprop(dtb_buf, off, prop_name,
+ val, len) != 0) {
+ fprintf(stderr, "FDT: Error setting %s/%s property.\n",
+ node_name, prop_name);
+ return -1;
+ }
+ *bufp = dtb_buf;
+ *sizep = dtb_size;
+ return 0;
+}
+
+static const struct zimage_tag *find_extension_tag(const char *buf, off_t len,
+ uint32_t tag_id)
+{
+ const struct zimage_header *hdr = (const struct zimage_header *)buf;
+ const struct zimage_tag *tag;
+ uint32_t offset, size;
+ uint32_t max = len - sizeof(struct tag_header);
+
+ if (len < sizeof(*hdr) ||
+ hdr->magic != ZIMAGE_MAGIC ||
+ hdr->magic2 != ZIMAGE_MAGIC2)
+ return NULL;
+
+ for (offset = hdr->extension_tag_offset;
+ (tag = (void *)(buf + offset)) != NULL &&
+ offset < max &&
+ (size = le32_to_cpu(byte_size(tag))) != 0 &&
+ offset + size < len;
+ offset += size) {
+ dbgprintf(" offset 0x%08x tag 0x%08x size %u\n",
+ offset, le32_to_cpu(tag->hdr.tag), size);
+ if (tag->hdr.tag == tag_id)
+ return tag;
+ }
+
+ return NULL;
+}
+
+static int get_cells_size(void *fdt, uint32_t *address_cells,
+ uint32_t *size_cells)
+{
+ int nodeoffset;
+ const uint32_t *prop = NULL;
+ int prop_len;
+
+ /* default values */
+ *address_cells = 1;
+ *size_cells = 1;
+
+ /* under root node */
+ nodeoffset = fdt_path_offset(fdt, "/");
+ if (nodeoffset < 0)
+ return -1;
+
+ prop = fdt_getprop(fdt, nodeoffset, "#address-cells", &prop_len);
+ if (prop) {
+ if (prop_len != sizeof(*prop))
+ return -1;
+
+ *address_cells = fdt32_to_cpu(*prop);
+ }
+
+ prop = fdt_getprop(fdt, nodeoffset, "#size-cells", &prop_len);
+ if (prop) {
+ if (prop_len != sizeof(*prop))
+ return -1;
+
+ *size_cells = fdt32_to_cpu(*prop);
+ }
+
+ dbgprintf("%s: #address-cells:%d #size-cells:%d\n", __func__,
+ *address_cells, *size_cells);
+ return 0;
+}
+
+static bool cells_size_fitted(uint32_t address_cells, uint32_t size_cells,
+ struct memory_range *range)
+{
+ dbgprintf("%s: %llx-%llx\n", __func__, range->start, range->end);
+
+ /* if *_cells >= 2, cells can hold 64-bit values anyway */
+ if ((address_cells == 1) && (range->start >= (1ULL << 32)))
+ return false;
+
+ if ((size_cells == 1) &&
+ ((range->end - range->start + 1) >= (1ULL << 32)))
+ return false;
+
+ return true;
+}
+
+static void fill_property(void *buf, uint64_t val, uint32_t cells)
+{
+ uint32_t val32;
+ int i;
+
+ if (cells == 1) {
+ val32 = cpu_to_fdt32((uint32_t)val);
+ memcpy(buf, &val32, sizeof(uint32_t));
+ } else {
+ for (i = 0;
+ i < (cells * sizeof(uint32_t) - sizeof(uint64_t)); i++)
+ *(char *)buf++ = 0;
+
+ val = cpu_to_fdt64(val);
+ memcpy(buf, &val, sizeof(uint64_t));
+ }
+}
+
+static int setup_dtb_prop_range(char **bufp, off_t *sizep, int parentoffset,
+ const char *node_name, const char *prop_name,
+ struct memory_range *range,
+ uint32_t address_cells, uint32_t size_cells)
+{
+ void *buf, *prop;
+ size_t buf_size;
+ int result;
+
+ buf_size = (address_cells + size_cells) * sizeof(uint32_t);
+ prop = buf = xmalloc(buf_size);
+
+ fill_property(prop, range->start, address_cells);
+ prop += address_cells * sizeof(uint32_t);
+
+ fill_property(prop, range->end - range->start + 1, size_cells);
+ prop += size_cells * sizeof(uint32_t);
+
+ result = setup_dtb_prop(bufp, sizep, parentoffset, node_name,
+ prop_name, buf, buf_size);
+
+ free(buf);
+
+ return result;
+}
+
+int zImage_arm_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ unsigned long page_size = getpagesize();
+ unsigned long base, kernel_base;
+ unsigned int atag_offset = 0x1000; /* 4k offset from memory start */
+ unsigned int extra_size = 0x8000; /* TEXT_OFFSET */
+ uint32_t address_cells, size_cells;
+ const struct zimage_tag *tag;
+ size_t kernel_buf_size;
+ size_t kernel_mem_size;
+ const char *command_line;
+ char *modified_cmdline = NULL;
+ off_t command_line_len;
+ const char *ramdisk;
+ const char *ramdisk_buf;
+ int opt;
+ int use_atags;
+ int result;
+ char *dtb_buf;
+ off_t dtb_length;
+ char *dtb_file;
+ off_t dtb_offset;
+ char *end;
+
+ /* See options.h -- add any more there, too. */
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { "command-line", 1, 0, OPT_APPEND },
+ { "append", 1, 0, OPT_APPEND },
+ { "initrd", 1, 0, OPT_RAMDISK },
+ { "ramdisk", 1, 0, OPT_RAMDISK },
+ { "dtb", 1, 0, OPT_DTB },
+ { "atags", 0, 0, OPT_ATAGS },
+ { "image-size", 1, 0, OPT_IMAGE_SIZE },
+ { "page-offset", 1, 0, OPT_PAGE_OFFSET },
+ { 0, 0, 0, 0 },
+ };
+ static const char short_options[] = KEXEC_ARCH_OPT_STR "";
+
+ /*
+ * Parse the command line arguments
+ */
+ command_line = 0;
+ command_line_len = 0;
+ ramdisk = 0;
+ ramdisk_buf = 0;
+ initrd_size = 0;
+ use_atags = 0;
+ dtb_file = NULL;
+ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch(opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ case OPT_APPEND:
+ command_line = optarg;
+ break;
+ case OPT_RAMDISK:
+ ramdisk = optarg;
+ break;
+ case OPT_DTB:
+ dtb_file = optarg;
+ break;
+ case OPT_ATAGS:
+ use_atags = 1;
+ break;
+ case OPT_IMAGE_SIZE:
+ kexec_arm_image_size = strtoul(optarg, &end, 0);
+ break;
+ case OPT_PAGE_OFFSET:
+ user_page_offset = strtoull(optarg, &end, 0);
+ break;
+ }
+ }
+
+ if (use_atags && dtb_file) {
+ fprintf(stderr, "You can only use ATAGs if you don't specify a "
+ "dtb file.\n");
+ return -1;
+ }
+
+ if (!use_atags && !dtb_file) {
+ int f;
+
+ f = have_sysfs_fdt();
+ if (f)
+ dtb_file = SYSFS_FDT;
+ }
+
+ if (command_line) {
+ command_line_len = strlen(command_line) + 1;
+ if (command_line_len > COMMAND_LINE_SIZE)
+ command_line_len = COMMAND_LINE_SIZE;
+ }
+ if (ramdisk)
+ ramdisk_buf = slurp_file_mmap(ramdisk, &initrd_size);
+
+ if (dtb_file)
+ dtb_buf = slurp_file(dtb_file, &dtb_length);
+
+ if (len > sizeof(struct zimage_header)) {
+ const struct zimage_header *hdr;
+ off_t size;
+
+ hdr = (const struct zimage_header *)buf;
+
+ dbgprintf("zImage header: 0x%08x 0x%08x 0x%08x\n",
+ hdr->magic, hdr->start, hdr->end);
+
+ if (hdr->magic == ZIMAGE_MAGIC) {
+ size = le32_to_cpu(hdr->end) - le32_to_cpu(hdr->start);
+
+ dbgprintf("zImage size 0x%llx, file size 0x%llx\n",
+ (unsigned long long)size,
+ (unsigned long long)len);
+
+ if (size > len) {
+ fprintf(stderr,
+ "zImage is truncated - file 0x%llx vs header 0x%llx\n",
+ (unsigned long long)len,
+ (unsigned long long)size);
+ return -1;
+ }
+ if (size < len)
+ len = size;
+ }
+ }
+
+ /* Handle android images, 2048 is the minimum page size */
+ if (len > 2048 && !strncmp(buf, "ANDROID!", 8)) {
+ const struct android_image *aimg = (const void *)buf;
+ uint32_t page_size = le32_to_cpu(aimg->page_size);
+ uint32_t kernel_size = le32_to_cpu(aimg->kernel_size);
+ uint32_t ramdisk_size = le32_to_cpu(aimg->ramdisk_size);
+ uint32_t stage2_size = le32_to_cpu(aimg->stage2_size);
+ off_t aimg_size = page_size + _ALIGN(kernel_size, page_size) +
+ _ALIGN(ramdisk_size, page_size) + stage2_size;
+
+ if (len < aimg_size) {
+ fprintf(stderr, "Android image size is incorrect\n");
+ return -1;
+ }
+
+ /* Get the kernel */
+ buf = buf + page_size;
+ len = kernel_size;
+
+ /* And the ramdisk if none was given on the command line */
+ if (!ramdisk && ramdisk_size) {
+ initrd_size = ramdisk_size;
+ ramdisk_buf = buf + _ALIGN(kernel_size, page_size);
+ }
+
+ /* Likewise for the command line */
+ if (!command_line && aimg->command_line[0]) {
+ command_line = aimg->command_line;
+ if (command_line[sizeof(aimg->command_line) - 1])
+ command_line_len = sizeof(aimg->command_line);
+ else
+ command_line_len = strlen(command_line) + 1;
+ }
+ }
+
+ /*
+ * Save the length of the compressed kernel image w/o the appended DTB.
+ * This will be required later on when the kernel image contained
+ * in the zImage will be loaded into a kernel memory segment.
+ * And we want to load ONLY the compressed kernel image from the zImage
+ * and discard the appended DTB.
+ */
+ kernel_buf_size = len;
+
+ /*
+ * Always extend the zImage by four bytes to ensure that an appended
+ * DTB image always sees an initialised value after _edata.
+ */
+ kernel_mem_size = len + 4;
+
+ /*
+ * Check for a kernel size extension, and set or validate the
+ * image size. This is the total space needed to avoid the
+ * boot kernel BSS, so other data (such as initrd) does not get
+ * overwritten.
+ */
+ tag = find_extension_tag(buf, len, ZIMAGE_TAG_KRNL_SIZE);
+
+ /*
+ * The zImage length does not include its stack (4k) or its
+ * malloc space (64k). Include this.
+ */
+ len += 0x11000;
+
+ dbgprintf("zImage requires 0x%08llx bytes\n", (unsigned long long)len);
+
+ if (tag) {
+ uint32_t *p = (void *)buf + le32_to_cpu(tag->u.krnl_size.size_ptr);
+ uint32_t edata_size = le32_to_cpu(get_unaligned(p));
+ uint32_t bss_size = le32_to_cpu(tag->u.krnl_size.bss_size);
+ uint32_t kernel_size = edata_size + bss_size;
+
+ dbgprintf("Decompressed kernel sizes:\n");
+ dbgprintf(" text+data 0x%08lx bss 0x%08lx total 0x%08lx\n",
+ (unsigned long)edata_size,
+ (unsigned long)bss_size,
+ (unsigned long)kernel_size);
+
+ /*
+ * While decompressing, the zImage is placed past _edata
+ * of the decompressed kernel. Ensure we account for that.
+ */
+ if (kernel_size < edata_size + len)
+ kernel_size = edata_size + len;
+
+ dbgprintf("Resulting kernel space: 0x%08lx\n",
+ (unsigned long)kernel_size);
+
+ if (kexec_arm_image_size == 0)
+ kexec_arm_image_size = kernel_size;
+ else if (kexec_arm_image_size < kernel_size) {
+ fprintf(stderr,
+ "Kernel size is too small, increasing to 0x%lx\n",
+ (unsigned long)kernel_size);
+ kexec_arm_image_size = kernel_size;
+ }
+ }
+
+ /*
+ * If the user didn't specify the size of the image, and we don't
+ * have the extension tables, assume the maximum kernel compression
+ * ratio is 4. Note that we must include space for the compressed
+ * image here as well.
+ */
+ if (!kexec_arm_image_size)
+ kexec_arm_image_size = len * 5;
+
+ /*
+ * If we are loading a dump capture kernel, we need to update kernel
+ * command line and also add some additional segments.
+ */
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ uint64_t start, end;
+
+ modified_cmdline = xmalloc(COMMAND_LINE_SIZE);
+ memset(modified_cmdline, '\0', COMMAND_LINE_SIZE);
+
+ if (command_line) {
+ (void) strncpy(modified_cmdline, command_line,
+ COMMAND_LINE_SIZE);
+ modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
+ }
+
+ if (load_crashdump_segments(info, modified_cmdline) < 0) {
+ free(modified_cmdline);
+ return -1;
+ }
+
+ command_line = modified_cmdline;
+ command_line_len = strlen(command_line) + 1;
+
+ /*
+ * We put the dump capture kernel at the start of crashkernel
+ * reserved memory.
+ */
+ if (parse_iomem_single(CRASH_KERNEL_BOOT, &start, &end) &&
+ parse_iomem_single(CRASH_KERNEL, &start, &end)) {
+ /*
+ * No crash kernel memory reserved. We cannot do more
+ * but just bail out.
+ */
+ return ENOCRASHKERNEL;
+ }
+ base = start;
+ } else {
+ base = locate_hole(info, len + extra_size, 0, 0,
+ ULONG_MAX, INT_MAX);
+ }
+
+ if (base == ULONG_MAX)
+ return -1;
+
+ kernel_base = base + extra_size;
+
+ /*
+ * Calculate the minimum address of the initrd, which must be
+ * above the memory used by the zImage while it runs. This
+ * needs to be page-size aligned.
+ */
+ initrd_base = kernel_base + _ALIGN(kexec_arm_image_size, page_size);
+
+ dbgprintf("%-6s: address=0x%08lx size=0x%08lx\n", "Kernel",
+ (unsigned long)kernel_base,
+ (unsigned long)kexec_arm_image_size);
+
+ if (ramdisk_buf) {
+ /*
+ * Find a hole to place the initrd. The crash kernel use
+ * fixed address, so no check is ok.
+ */
+ if (!(info->kexec_flags & KEXEC_ON_CRASH)) {
+ initrd_base = locate_hole(info, initrd_size, page_size,
+ initrd_base,
+ ULONG_MAX, INT_MAX);
+ if (initrd_base == ULONG_MAX)
+ return -1;
+ }
+
+ dbgprintf("%-6s: address=0x%08lx size=0x%08lx\n", "Initrd",
+ (unsigned long)initrd_base,
+ (unsigned long)initrd_size);
+
+ add_segment(info, ramdisk_buf, initrd_size, initrd_base,
+ initrd_size);
+ }
+
+ if (use_atags) {
+ /*
+ * use ATAGs from /proc/atags
+ */
+ if (atag_arm_load(info, base + atag_offset,
+ command_line, command_line_len,
+ ramdisk_buf) == -1)
+ return -1;
+ } else {
+ /*
+ * Read a user-specified DTB file.
+ */
+ if (dtb_file) {
+ if (fdt_check_header(dtb_buf) != 0) {
+ fprintf(stderr, "Invalid FDT buffer.\n");
+ return -1;
+ }
+
+ if (command_line) {
+ /*
+ * Error should have been reported so
+ * directly return -1
+ */
+ if (setup_dtb_prop(&dtb_buf, &dtb_length, 0, "chosen",
+ "bootargs", command_line,
+ strlen(command_line) + 1))
+ return -1;
+ }
+ } else {
+ /*
+ * Extract the DTB from /proc/device-tree.
+ */
+ create_flatten_tree(&dtb_buf, &dtb_length, command_line);
+ }
+
+ /*
+ * Add the initrd parameters to the dtb
+ */
+ if (ramdisk_buf) {
+ unsigned long start, end;
+
+ start = cpu_to_be32((unsigned long)(initrd_base));
+ end = cpu_to_be32((unsigned long)(initrd_base + initrd_size));
+
+ if (setup_dtb_prop(&dtb_buf, &dtb_length, 0, "chosen",
+ "linux,initrd-start", &start,
+ sizeof(start)))
+ return -1;
+ if (setup_dtb_prop(&dtb_buf, &dtb_length, 0, "chosen",
+ "linux,initrd-end", &end,
+ sizeof(end)))
+ return -1;
+ }
+
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ /* Determine #address-cells and #size-cells */
+ result = get_cells_size(dtb_buf, &address_cells,
+ &size_cells);
+ if (result) {
+ fprintf(stderr, "Cannot determine cells-size.\n");
+ return -1;
+ }
+
+ if (!cells_size_fitted(address_cells, size_cells,
+ &elfcorehdr_mem)) {
+ fprintf(stderr, "elfcorehdr doesn't fit cells-size.\n");
+ return -1;
+ }
+
+ if (!cells_size_fitted(address_cells, size_cells,
+ &crash_kernel_mem)) {
+ fprintf(stderr, "kexec: usable memory range doesn't fit cells-size.\n");
+ return -1;
+ }
+
+ /* Add linux,elfcorehdr */
+ if (setup_dtb_prop_range(&dtb_buf, &dtb_length, 0,
+ "chosen", "linux,elfcorehdr",
+ &elfcorehdr_mem,
+ address_cells, size_cells))
+ return -1;
+
+ /* Add linux,usable-memory-range */
+ if (setup_dtb_prop_range(&dtb_buf, &dtb_length, 0,
+ "chosen",
+ "linux,usable-memory-range",
+ &crash_kernel_mem,
+ address_cells, size_cells))
+ return -1;
+ }
+
+ /*
+ * The dtb must also be placed above the memory used by
+ * the zImage. We don't care about its position wrt the
+ * ramdisk, but we might as well place it after the initrd.
+ * We leave a buffer page between the initrd and the dtb.
+ */
+ dtb_offset = initrd_base + initrd_size + page_size;
+ dtb_offset = _ALIGN_DOWN(dtb_offset, page_size);
+
+ /*
+ * Find a hole to place the dtb above the initrd.
+ * Crash kernel use fixed address, no check is ok.
+ */
+ if (!(info->kexec_flags & KEXEC_ON_CRASH)) {
+ dtb_offset = locate_hole(info, dtb_length, page_size,
+ dtb_offset, ULONG_MAX, INT_MAX);
+ if (dtb_offset == ULONG_MAX)
+ return -1;
+ }
+
+ dbgprintf("%-6s: address=0x%08lx size=0x%08lx\n", "DT",
+ (unsigned long)dtb_offset, (unsigned long)dtb_length);
+
+ add_segment(info, dtb_buf, dtb_length, dtb_offset, dtb_length);
+ }
+
+ add_segment(info, buf, kernel_buf_size, kernel_base, kernel_mem_size);
+
+ info->entry = (void*)kernel_base;
+
+ return 0;
+}
diff --git a/kexec/arch/arm/phys_to_virt.c b/kexec/arch/arm/phys_to_virt.c
new file mode 100644
index 0000000..46a4f68
--- /dev/null
+++ b/kexec/arch/arm/phys_to_virt.c
@@ -0,0 +1,22 @@
+#include "../../kexec.h"
+#include "../../crashdump.h"
+#include "phys_to_virt.h"
+
+uint64_t phys_offset;
+
+/**
+ * phys_to_virt() - translate physical address to virtual address
+ * @paddr: physical address to translate
+ *
+ * For ARM we have following equation to translate from virtual address to
+ * physical:
+ * paddr = vaddr - PAGE_OFFSET + PHYS_OFFSET
+ *
+ * See also:
+ * http://lists.arm.linux.org.uk/lurker/message/20010723.185051.94ce743c.en.html
+ */
+unsigned long
+phys_to_virt(struct crash_elf_info *elf_info, unsigned long long paddr)
+{
+ return paddr + elf_info->page_offset - phys_offset;
+}
diff --git a/kexec/arch/arm/phys_to_virt.h b/kexec/arch/arm/phys_to_virt.h
new file mode 100644
index 0000000..b3147dd
--- /dev/null
+++ b/kexec/arch/arm/phys_to_virt.h
@@ -0,0 +1,8 @@
+#ifndef PHYS_TO_VIRT_H
+#define PHYS_TO_VIRT_H
+
+#include <stdint.h>
+
+extern uint64_t phys_offset;
+
+#endif
diff --git a/kexec/arch/arm64/Makefile b/kexec/arch/arm64/Makefile
new file mode 100644
index 0000000..59212f1
--- /dev/null
+++ b/kexec/arch/arm64/Makefile
@@ -0,0 +1,50 @@
+
+arm64_FS2DT += kexec/fs2dt.c
+arm64_FS2DT_INCLUDE += \
+ -include $(srcdir)/kexec/arch/arm64/crashdump-arm64.h \
+ -include $(srcdir)/kexec/arch/arm64/kexec-arm64.h
+
+arm64_DT_OPS += kexec/dt-ops.c
+
+arm64_MEM_REGIONS = kexec/mem_regions.c
+
+arm64_CPPFLAGS += -I $(srcdir)/kexec/
+
+arm64_KEXEC_SRCS += \
+ kexec/arch/arm64/crashdump-arm64.c \
+ kexec/arch/arm64/kexec-arm64.c \
+ kexec/arch/arm64/kexec-elf-arm64.c \
+ kexec/arch/arm64/kexec-uImage-arm64.c \
+ kexec/arch/arm64/kexec-image-arm64.c \
+ kexec/arch/arm64/kexec-vmlinuz-arm64.c
+
+arm64_UIMAGE = kexec/kexec-uImage.c
+
+arm64_ARCH_REUSE_INITRD =
+arm64_ADD_SEGMENT =
+arm64_VIRT_TO_PHYS =
+arm64_PHYS_TO_VIRT =
+
+dist += $(arm64_KEXEC_SRCS) \
+ kexec/arch/arm64/include/arch/options.h \
+ kexec/arch/arm64/crashdump-arm64.h \
+ kexec/arch/arm64/image-header.h \
+ kexec/arch/arm64/iomem.h \
+ kexec/arch/arm64/kexec-arm64.h \
+ kexec/arch/arm64/Makefile
+
+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..3098315
--- /dev/null
+++ b/kexec/arch/arm64/crashdump-arm64.c
@@ -0,0 +1,248 @@
+/*
+ * ARM64 crashdump.
+ * partly derived from arm implementation
+ *
+ * Copyright (c) 2014-2017 Linaro Limited
+ * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <linux/elf.h>
+
+#include "kexec.h"
+#include "crashdump.h"
+#include "crashdump-arm64.h"
+#include "iomem.h"
+#include "kexec-arm64.h"
+#include "kexec-elf.h"
+#include "mem_regions.h"
+
+/* memory ranges of crashed kernel */
+static struct memory_ranges system_memory_rgns;
+
+/* memory range reserved for crashkernel */
+struct memory_range crash_reserved_mem[CRASH_MAX_RESERVED_RANGES];
+struct memory_ranges usablemem_rgns = {
+ .size = 0,
+ .max_size = CRASH_MAX_RESERVED_RANGES,
+ .ranges = crash_reserved_mem,
+};
+
+struct memory_range elfcorehdr_mem;
+
+static struct crash_elf_info elf_info = {
+ .class = ELFCLASS64,
+#if (__BYTE_ORDER == __LITTLE_ENDIAN)
+ .data = ELFDATA2LSB,
+#else
+ .data = ELFDATA2MSB,
+#endif
+ .machine = EM_AARCH64,
+};
+
+/*
+ * iomem_range_callback() - callback called for each iomem region
+ * @data: not used
+ * @nr: not used
+ * @str: name of the memory region
+ * @base: start address of the memory region
+ * @length: size of the memory region
+ *
+ * This function is called once for each memory region found in /proc/iomem.
+ * It locates system RAM and crashkernel reserved memory and places these to
+ * variables, respectively, system_memory_rgns and usablemem_rgns.
+ */
+
+static int iomem_range_callback(void *UNUSED(data), int UNUSED(nr),
+ char *str, unsigned long long base,
+ unsigned long long length)
+{
+ if (strncmp(str, CRASH_KERNEL, strlen(CRASH_KERNEL)) == 0)
+ return mem_regions_alloc_and_add(&usablemem_rgns,
+ base, length, RANGE_RAM);
+ else if (strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM)) == 0)
+ return mem_regions_alloc_and_add(&system_memory_rgns,
+ base, length, RANGE_RAM);
+ else if (strncmp(str, KERNEL_CODE, strlen(KERNEL_CODE)) == 0) {
+
+ unsigned long long kva_text = get_kernel_sym("_text");
+ unsigned long long kva_stext = get_kernel_sym("_stext");
+ unsigned long long kva_text_end = get_kernel_sym("__init_begin");
+
+ /*
+ * old: kernel_code.start = __pa_symbol(_text);
+ * new: kernel_code.start = __pa_symbol(_stext);
+ *
+ * For compatibility, deduce by comparing the gap "__init_begin - _stext"
+ * and the res size of "Kernel code" in /proc/iomem
+ */
+ if (kva_text_end - kva_stext == length)
+ elf_info.kern_paddr_start = base - (kva_stext - kva_text);
+ else
+ elf_info.kern_paddr_start = base;
+ }
+ else if (strncmp(str, KERNEL_DATA, strlen(KERNEL_DATA)) == 0)
+ elf_info.kern_size = base + length - elf_info.kern_paddr_start;
+
+ return 0;
+}
+
+int is_crashkernel_mem_reserved(void)
+{
+ if (!usablemem_rgns.size)
+ kexec_iomem_for_each_line(NULL, iomem_range_callback, NULL);
+
+ return usablemem_rgns.size;
+}
+
+/*
+ * crash_get_memory_ranges() - read system physical memory
+ *
+ * Function reads through system physical memory and stores found memory
+ * regions in system_memory_ranges.
+ * Regions are sorted in ascending order.
+ *
+ * Returns 0 in case of success and a negative value otherwise.
+ */
+static int crash_get_memory_ranges(void)
+{
+ int i;
+
+ /*
+ * First read all memory regions that can be considered as
+ * system memory including the crash area.
+ */
+ if (!usablemem_rgns.size)
+ kexec_iomem_for_each_line(NULL, iomem_range_callback, NULL);
+
+ /* allow one or two regions for crash dump kernel */
+ if (!usablemem_rgns.size)
+ return -EINVAL;
+
+ dbgprint_mem_range("Reserved memory range",
+ usablemem_rgns.ranges, usablemem_rgns.size);
+
+ for (i = 0; i < usablemem_rgns.size; i++) {
+ if (mem_regions_alloc_and_exclude(&system_memory_rgns,
+ &crash_reserved_mem[i])) {
+ fprintf(stderr, "Cannot allocate memory for ranges\n");
+ return -ENOMEM;
+ }
+ }
+
+ /*
+ * Make sure that the memory regions are sorted.
+ */
+ mem_regions_sort(&system_memory_rgns);
+
+ dbgprint_mem_range("Coredump memory ranges",
+ system_memory_rgns.ranges, system_memory_rgns.size);
+
+ /*
+ * For additional kernel code/data segment.
+ * kern_paddr_start/kern_size are determined in iomem_range_callback
+ */
+ elf_info.kern_vaddr_start = get_kernel_sym("_text");
+ if (!elf_info.kern_vaddr_start)
+ elf_info.kern_vaddr_start = UINT64_MAX;
+
+ return 0;
+}
+
+/*
+ * load_crashdump_segments() - load the elf core header
+ * @info: kexec info structure
+ *
+ * This function creates and loads an additional segment of elf core header
+ : which is used to construct /proc/vmcore on crash dump kernel.
+ *
+ * Return 0 in case of success and -1 in case of error.
+ */
+
+int load_crashdump_segments(struct kexec_info *info)
+{
+ unsigned long elfcorehdr;
+ unsigned long bufsz;
+ void *buf;
+ int err;
+
+ /*
+ * First fetch all the memory (RAM) ranges that we are going to
+ * pass to the crash dump kernel during panic.
+ */
+
+ err = crash_get_memory_ranges();
+
+ if (err)
+ return EFAILED;
+
+ get_page_offset((unsigned long *)&elf_info.page_offset);
+ dbgprintf("%s: page_offset: %016llx\n", __func__,
+ elf_info.page_offset);
+
+ err = crash_create_elf64_headers(info, &elf_info,
+ system_memory_rgns.ranges, system_memory_rgns.size,
+ &buf, &bufsz, ELF_CORE_HEADER_ALIGN);
+
+ if (err)
+ return EFAILED;
+
+ elfcorehdr = add_buffer_phys_virt(info, buf, bufsz, bufsz, 0,
+ crash_reserved_mem[usablemem_rgns.size - 1].start,
+ crash_reserved_mem[usablemem_rgns.size - 1].end,
+ -1, 0);
+
+ elfcorehdr_mem.start = elfcorehdr;
+ elfcorehdr_mem.end = elfcorehdr + bufsz - 1;
+
+ dbgprintf("%s: elfcorehdr 0x%llx-0x%llx\n", __func__,
+ elfcorehdr_mem.start, elfcorehdr_mem.end);
+
+ return 0;
+}
+
+/*
+ * e_entry and p_paddr are actually in virtual address space.
+ * Those values will be translated to physcal addresses by using
+ * virt_to_phys() in add_segment().
+ * So let's fix up those values for later use so the memory base
+ * (arm64_mm.phys_offset) will be correctly replaced with
+ * crash_reserved_mem[usablemem_rgns.size - 1].start.
+ */
+void fixup_elf_addrs(struct mem_ehdr *ehdr)
+{
+ struct mem_phdr *phdr;
+ int i;
+
+ ehdr->e_entry += -arm64_mem.phys_offset +
+ crash_reserved_mem[usablemem_rgns.size - 1].start;
+
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ phdr = &ehdr->e_phdr[i];
+ if (phdr->p_type != PT_LOAD)
+ continue;
+ phdr->p_paddr +=
+ (-arm64_mem.phys_offset +
+ crash_reserved_mem[usablemem_rgns.size - 1].start);
+ }
+}
+
+int get_crash_kernel_load_range(uint64_t *start, uint64_t *end)
+{
+ if (!usablemem_rgns.size)
+ kexec_iomem_for_each_line(NULL, iomem_range_callback, NULL);
+
+ if (!usablemem_rgns.size)
+ return -1;
+
+ *start = crash_reserved_mem[usablemem_rgns.size - 1].start;
+ *end = crash_reserved_mem[usablemem_rgns.size - 1].end;
+
+ return 0;
+}
diff --git a/kexec/arch/arm64/crashdump-arm64.h b/kexec/arch/arm64/crashdump-arm64.h
new file mode 100644
index 0000000..82fa69b
--- /dev/null
+++ b/kexec/arch/arm64/crashdump-arm64.h
@@ -0,0 +1,29 @@
+/*
+ * ARM64 crashdump.
+ *
+ * Copyright (c) 2014-2017 Linaro Limited
+ * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef CRASHDUMP_ARM64_H
+#define CRASHDUMP_ARM64_H
+
+#include "kexec.h"
+
+#define CRASH_MAX_MEMORY_RANGES 32768
+
+/* crash dump kernel support at most two regions, low_region and high region. */
+#define CRASH_MAX_RESERVED_RANGES 2
+
+extern struct memory_ranges usablemem_rgns;
+extern struct memory_range crash_reserved_mem[];
+extern struct memory_range elfcorehdr_mem;
+
+extern int load_crashdump_segments(struct kexec_info *info);
+extern void fixup_elf_addrs(struct mem_ehdr *ehdr);
+
+#endif /* CRASHDUMP_ARM64_H */
diff --git a/kexec/arch/arm64/image-header.h b/kexec/arch/arm64/image-header.h
new file mode 100644
index 0000000..26bb02f
--- /dev/null
+++ b/kexec/arch/arm64/image-header.h
@@ -0,0 +1,147 @@
+/*
+ * 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 uint8_t arm64_pe_machtype[6] = {'P','E', 0x0, 0x0, 0x64, 0xAA};
+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..8c695f3
--- /dev/null
+++ b/kexec/arch/arm64/include/arch/options.h
@@ -0,0 +1,43 @@
+#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_SERIAL ((OPT_MAX)+4)
+#define OPT_ARCH_MAX ((OPT_MAX)+5)
+
+#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 }, \
+ { "serial", 1, NULL, OPT_SERIAL }, \
+ { "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"
+" --serial=STRING Name of console used for purgatory printing. (e.g. ttyAMA0)\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;
+ const char *console;
+};
+
+extern struct arm64_opts arm64_opts;
+
+#endif
diff --git a/kexec/arch/arm64/iomem.h b/kexec/arch/arm64/iomem.h
new file mode 100644
index 0000000..d4864bb
--- /dev/null
+++ b/kexec/arch/arm64/iomem.h
@@ -0,0 +1,10 @@
+#ifndef IOMEM_H
+#define IOMEM_H
+
+#define SYSTEM_RAM "System RAM\n"
+#define KERNEL_CODE "Kernel code\n"
+#define KERNEL_DATA "Kernel data\n"
+#define CRASH_KERNEL "Crash kernel\n"
+#define IOMEM_RESERVED "reserved\n"
+
+#endif
diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c
new file mode 100644
index 0000000..4a67b0d
--- /dev/null
+++ b/kexec/arch/arm64/kexec-arm64.c
@@ -0,0 +1,1365 @@
+/*
+ * ARM64 kexec.
+ */
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <libfdt.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <linux/elf-em.h>
+#include <elf.h>
+#include <elf_info.h>
+
+#include <unistd.h>
+#include <syscall.h>
+#include <errno.h>
+#include <linux/random.h>
+
+#include "kexec.h"
+#include "kexec-arm64.h"
+#include "crashdump.h"
+#include "crashdump-arm64.h"
+#include "dt-ops.h"
+#include "fs2dt.h"
+#include "iomem.h"
+#include "kexec-syscall.h"
+#include "mem_regions.h"
+#include "arch/options.h"
+
+#define ROOT_NODE_ADDR_CELLS_DEFAULT 1
+#define ROOT_NODE_SIZE_CELLS_DEFAULT 1
+
+#define PROP_ADDR_CELLS "#address-cells"
+#define PROP_SIZE_CELLS "#size-cells"
+#define PROP_ELFCOREHDR "linux,elfcorehdr"
+#define PROP_USABLE_MEM_RANGE "linux,usable-memory-range"
+
+#define PAGE_OFFSET_36 ((0xffffffffffffffffUL) << 36)
+#define PAGE_OFFSET_39 ((0xffffffffffffffffUL) << 39)
+#define PAGE_OFFSET_42 ((0xffffffffffffffffUL) << 42)
+#define PAGE_OFFSET_47 ((0xffffffffffffffffUL) << 47)
+#define PAGE_OFFSET_48 ((0xffffffffffffffffUL) << 48)
+
+/* Global flag which indicates that we have tried reading
+ * PHYS_OFFSET from 'kcore' already.
+ */
+static bool try_read_phys_offset_from_kcore = false;
+
+/* Machine specific details. */
+static int va_bits = -1;
+static unsigned long page_offset;
+
+/* 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},
+ {"uImage", uImage_arm64_probe, uImage_arm64_load, uImage_arm64_usage},
+ {"vmlinuz", pez_arm64_probe, pez_arm64_load, pez_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;
+ int do_kexec_file_syscall = 0;
+
+ 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_KEXEC_FILE_SYSCALL:
+ do_kexec_file_syscall = 1;
+ case OPT_SERIAL:
+ arm64_opts.console = optarg;
+ 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__,
+ (do_kexec_file_syscall && arm64_opts.dtb ? "(ignored)" :
+ arm64_opts.dtb));
+ dbgprintf("%s:%d: console: %s\n", __func__, __LINE__,
+ arm64_opts.console);
+
+ if (do_kexec_file_syscall)
+ arm64_opts.dtb = NULL;
+
+ return 0;
+}
+
+/**
+ * find_purgatory_sink - Find a sink for purgatory output.
+ */
+
+static uint64_t find_purgatory_sink(const char *console)
+{
+ int fd, ret;
+ char device[255], mem[255];
+ struct stat sb;
+ char buffer[10];
+ uint64_t iomem = 0x0;
+
+ if (!console)
+ return 0;
+
+ ret = snprintf(device, sizeof(device), "/sys/class/tty/%s", console);
+ if (ret < 0 || ret >= sizeof(device)) {
+ fprintf(stderr, "snprintf failed: %s\n", strerror(errno));
+ return 0;
+ }
+
+ if (stat(device, &sb) || !S_ISDIR(sb.st_mode)) {
+ fprintf(stderr, "kexec: %s: No valid console found for %s\n",
+ __func__, device);
+ return 0;
+ }
+
+ ret = snprintf(mem, sizeof(mem), "%s%s", device, "/iomem_base");
+ if (ret < 0 || ret >= sizeof(mem)) {
+ fprintf(stderr, "snprintf failed: %s\n", strerror(errno));
+ return 0;
+ }
+
+ printf("console memory read from %s\n", mem);
+
+ fd = open(mem, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "kexec: %s: No able to open %s\n",
+ __func__, mem);
+ return 0;
+ }
+
+ memset(buffer, '\0', sizeof(buffer));
+ ret = read(fd, buffer, sizeof(buffer));
+ if (ret < 0) {
+ fprintf(stderr, "kexec: %s: not able to read fd\n", __func__);
+ close(fd);
+ return 0;
+ }
+
+ sscanf(buffer, "%lx", &iomem);
+ printf("console memory is at %#lx\n", iomem);
+
+ close(fd);
+ return iomem;
+}
+
+/**
+ * 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;
+}
+
+static int get_cells_size(void *fdt, uint32_t *address_cells,
+ uint32_t *size_cells)
+{
+ int nodeoffset;
+ const uint32_t *prop = NULL;
+ int prop_len;
+
+ /* default values */
+ *address_cells = ROOT_NODE_ADDR_CELLS_DEFAULT;
+ *size_cells = ROOT_NODE_SIZE_CELLS_DEFAULT;
+
+ /* under root node */
+ nodeoffset = fdt_path_offset(fdt, "/");
+ if (nodeoffset < 0)
+ goto on_error;
+
+ prop = fdt_getprop(fdt, nodeoffset, PROP_ADDR_CELLS, &prop_len);
+ if (prop) {
+ if (prop_len == sizeof(*prop))
+ *address_cells = fdt32_to_cpu(*prop);
+ else
+ goto on_error;
+ }
+
+ prop = fdt_getprop(fdt, nodeoffset, PROP_SIZE_CELLS, &prop_len);
+ if (prop) {
+ if (prop_len == sizeof(*prop))
+ *size_cells = fdt32_to_cpu(*prop);
+ else
+ goto on_error;
+ }
+
+ dbgprintf("%s: #address-cells:%d #size-cells:%d\n", __func__,
+ *address_cells, *size_cells);
+ return 0;
+
+on_error:
+ return EFAILED;
+}
+
+static bool cells_size_fitted(uint32_t address_cells, uint32_t size_cells,
+ struct memory_range *range)
+{
+ dbgprintf("%s: %llx-%llx\n", __func__, range->start, range->end);
+
+ /* if *_cells >= 2, cells can hold 64-bit values anyway */
+ if ((address_cells == 1) && (range->start >= (1ULL << 32)))
+ return false;
+
+ if ((size_cells == 1) &&
+ ((range->end - range->start + 1) >= (1ULL << 32)))
+ return false;
+
+ return true;
+}
+
+static void fill_property(void *buf, uint64_t val, uint32_t cells)
+{
+ uint32_t val32;
+ int i;
+
+ if (cells == 1) {
+ val32 = cpu_to_fdt32((uint32_t)val);
+ memcpy(buf, &val32, sizeof(uint32_t));
+ } else {
+ for (i = 0;
+ i < (cells * sizeof(uint32_t) - sizeof(uint64_t)); i++)
+ *(char *)buf++ = 0;
+
+ val = cpu_to_fdt64(val);
+ memcpy(buf, &val, sizeof(uint64_t));
+ }
+}
+
+static int fdt_setprop_ranges(void *fdt, int nodeoffset, const char *name,
+ struct memory_range *ranges, int nr_ranges, bool reverse,
+ uint32_t address_cells, uint32_t size_cells)
+{
+ void *buf, *prop;
+ size_t buf_size;
+ int i, result;
+ struct memory_range *range;
+
+ buf_size = (address_cells + size_cells) * sizeof(uint32_t) * nr_ranges;
+ prop = buf = xmalloc(buf_size);
+ if (!buf)
+ return -ENOMEM;
+
+ for (i = 0; i < nr_ranges; i++) {
+ if (reverse)
+ range = ranges + (nr_ranges - 1 - i);
+ else
+ range = ranges + i;
+
+ fill_property(prop, range->start, address_cells);
+ prop += address_cells * sizeof(uint32_t);
+
+ fill_property(prop, range->end - range->start + 1, size_cells);
+ prop += size_cells * sizeof(uint32_t);
+ }
+
+ result = fdt_setprop(fdt, nodeoffset, name, buf, buf_size);
+
+ free(buf);
+
+ return result;
+}
+
+/**
+ * setup_2nd_dtb - Setup the 2nd stage kernel's dtb.
+ */
+
+static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash)
+{
+ uint32_t address_cells, size_cells;
+ uint64_t fdt_val64;
+ uint64_t *prop;
+ char *new_buf = NULL;
+ int len, range_len;
+ int nodeoffset;
+ int new_size;
+ int i, result, kaslr_seed;
+
+ result = fdt_check_header(dtb->buf);
+
+ if (result) {
+ fprintf(stderr, "kexec: Invalid 2nd device tree.\n");
+ return EFAILED;
+ }
+
+ result = set_bootargs(dtb, command_line);
+ if (result) {
+ fprintf(stderr, "kexec: cannot set bootargs.\n");
+ result = -EINVAL;
+ goto on_error;
+ }
+
+ /* determine #address-cells and #size-cells */
+ result = get_cells_size(dtb->buf, &address_cells, &size_cells);
+ if (result) {
+ fprintf(stderr, "kexec: cannot determine cells-size.\n");
+ result = -EINVAL;
+ goto on_error;
+ }
+
+ if (!cells_size_fitted(address_cells, size_cells,
+ &elfcorehdr_mem)) {
+ fprintf(stderr, "kexec: elfcorehdr doesn't fit cells-size.\n");
+ result = -EINVAL;
+ goto on_error;
+ }
+
+ for (i = 0; i < usablemem_rgns.size; i++) {
+ if (!cells_size_fitted(address_cells, size_cells,
+ &crash_reserved_mem[i])) {
+ fprintf(stderr, "kexec: usable memory range doesn't fit cells-size.\n");
+ result = -EINVAL;
+ goto on_error;
+ }
+ }
+
+ /* duplicate dt blob */
+ range_len = sizeof(uint32_t) * (address_cells + size_cells);
+ new_size = fdt_totalsize(dtb->buf)
+ + fdt_prop_len(PROP_ELFCOREHDR, range_len)
+ + fdt_prop_len(PROP_USABLE_MEM_RANGE, range_len * usablemem_rgns.size);
+
+ new_buf = xmalloc(new_size);
+ result = fdt_open_into(dtb->buf, new_buf, new_size);
+ if (result) {
+ dbgprintf("%s: fdt_open_into failed: %s\n", __func__,
+ fdt_strerror(result));
+ result = -ENOSPC;
+ goto on_error;
+ }
+
+ /* fixup 'kaslr-seed' with a random value, if supported */
+ nodeoffset = fdt_path_offset(new_buf, "/chosen");
+ prop = fdt_getprop_w(new_buf, nodeoffset,
+ "kaslr-seed", &len);
+ if (!prop || len != sizeof(uint64_t)) {
+ dbgprintf("%s: no kaslr-seed found\n",
+ __func__);
+ /* for kexec warm reboot case, we don't need to fixup
+ * other dtb properties
+ */
+ if (!on_crash) {
+ dump_reservemap(dtb);
+ if (new_buf)
+ free(new_buf);
+
+ return result;
+ }
+ } else {
+ kaslr_seed = fdt64_to_cpu(*prop);
+
+ /* kaslr_seed must be wiped clean by primary
+ * kernel during boot
+ */
+ if (kaslr_seed != 0) {
+ dbgprintf("%s: kaslr-seed is not wiped to 0.\n",
+ __func__);
+ result = -EINVAL;
+ goto on_error;
+ }
+
+ /*
+ * Invoke the getrandom system call with
+ * GRND_NONBLOCK, to make sure we
+ * have a valid random seed to pass to the
+ * secondary kernel.
+ */
+ result = syscall(SYS_getrandom, &fdt_val64,
+ sizeof(fdt_val64),
+ GRND_NONBLOCK);
+
+ if(result == -1) {
+ fprintf(stderr, "%s: Reading random bytes failed.\n",
+ __func__);
+
+ /* Currently on some arm64 platforms this
+ * 'getrandom' system call fails while booting
+ * the platform.
+ *
+ * In case, this happens at best we can set
+ * the 'kaslr_seed' as 0, indicating that the
+ * 2nd kernel will be booted with a 'nokaslr'
+ * like behaviour.
+ */
+ fdt_val64 = 0UL;
+ dbgprintf("%s: Disabling KASLR in secondary kernel.\n",
+ __func__);
+ }
+
+ nodeoffset = fdt_path_offset(new_buf, "/chosen");
+ result = fdt_setprop_inplace(new_buf,
+ nodeoffset, "kaslr-seed",
+ &fdt_val64, sizeof(fdt_val64));
+ if (result) {
+ dbgprintf("%s: fdt_setprop failed: %s\n",
+ __func__, fdt_strerror(result));
+ result = -EINVAL;
+ goto on_error;
+ }
+ }
+
+ if (on_crash) {
+ /* add linux,elfcorehdr */
+ nodeoffset = fdt_path_offset(new_buf, "/chosen");
+ result = fdt_setprop_ranges(new_buf, nodeoffset,
+ PROP_ELFCOREHDR, &elfcorehdr_mem, 1, false,
+ address_cells, size_cells);
+ if (result) {
+ dbgprintf("%s: fdt_setprop failed: %s\n", __func__,
+ fdt_strerror(result));
+ result = -EINVAL;
+ goto on_error;
+ }
+
+ /*
+ * add linux,usable-memory-range
+ *
+ * crash dump kernel support one or two regions, to make
+ * compatibility with existing user-space and older kdump, the
+ * low region is always the last one.
+ */
+ nodeoffset = fdt_path_offset(new_buf, "/chosen");
+ result = fdt_setprop_ranges(new_buf, nodeoffset,
+ PROP_USABLE_MEM_RANGE,
+ usablemem_rgns.ranges, usablemem_rgns.size, true,
+ address_cells, size_cells);
+ if (result) {
+ dbgprintf("%s: fdt_setprop failed: %s\n", __func__,
+ fdt_strerror(result));
+ result = -EINVAL;
+ goto on_error;
+ }
+ }
+
+ fdt_pack(new_buf);
+ dtb->buf = new_buf;
+ dtb->size = fdt_totalsize(new_buf);
+
+ dump_reservemap(dtb);
+
+ return result;
+
+on_error:
+ fprintf(stderr, "kexec: %s failed.\n", __func__);
+ if (new_buf)
+ free(new_buf);
+
+ return result;
+}
+
+unsigned long arm64_locate_kernel_segment(struct kexec_info *info)
+{
+ unsigned long hole;
+
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ unsigned long hole_end;
+
+ hole = (crash_reserved_mem[usablemem_rgns.size - 1].start < mem_min ?
+ mem_min : crash_reserved_mem[usablemem_rgns.size - 1].start);
+ hole = _ALIGN_UP(hole, MiB(2));
+ hole_end = hole + arm64_mem.text_offset + arm64_mem.image_size;
+
+ if ((hole_end > mem_max) ||
+ (hole_end > crash_reserved_mem[usablemem_rgns.size - 1].end)) {
+ dbgprintf("%s: Crash kernel out of range\n", __func__);
+ hole = ULONG_MAX;
+ }
+ } else {
+ hole = locate_hole(info,
+ arm64_mem.text_offset + arm64_mem.image_size,
+ MiB(2), 0, ULONG_MAX, 1);
+
+ if (hole == ULONG_MAX)
+ dbgprintf("%s: locate_hole failed\n", __func__);
+ }
+
+ 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;
+ unsigned long initrd_end;
+ uint64_t purgatory_sink;
+ char *initrd_buf = NULL;
+ struct dtb dtb;
+ char command_line[COMMAND_LINE_SIZE] = "";
+
+ if (arm64_opts.command_line) {
+ if (strlen(arm64_opts.command_line) >
+ sizeof(command_line) - 1) {
+ fprintf(stderr,
+ "Kernel command line too long for kernel!\n");
+ return EFAILED;
+ }
+
+ strncpy(command_line, arm64_opts.command_line,
+ sizeof(command_line) - 1);
+ command_line[sizeof(command_line) - 1] = 0;
+ }
+
+ purgatory_sink = find_purgatory_sink(arm64_opts.console);
+
+ dbgprintf("%s:%d: purgatory sink: 0x%" PRIx64 "\n", __func__, __LINE__,
+ purgatory_sink);
+
+ 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,
+ info->kexec_flags & KEXEC_ON_CRASH);
+
+ if (result)
+ return EFAILED;
+
+ /* Put the other segments after the image. */
+
+ hole_min = image_base + arm64_mem.image_size;
+ if (info->kexec_flags & KEXEC_ON_CRASH)
+ hole_max = crash_reserved_mem[usablemem_rgns.size - 1].end;
+ else
+ 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. */
+
+ initrd_base = add_buffer_phys_virt(info, initrd_buf,
+ initrd_size, initrd_size, 0,
+ hole_min, hole_max, 1, 0);
+
+ initrd_end = initrd_base + initrd_size;
+
+ /* Check limits as specified in booting.txt.
+ * The kernel may have as little as 32 GB of address space to map
+ * system memory and both kernel and initrd must be 1GB aligend.
+ */
+
+ if (_ALIGN_UP(initrd_end, GiB(1)) - _ALIGN_DOWN(image_base, GiB(1)) > GiB(32)) {
+ fprintf(stderr, "kexec: Error: image + initrd too big.\n");
+ return EFAILED;
+ }
+
+ dbgprintf("initrd: base %lx, size %lxh (%ld)\n",
+ initrd_base, initrd_size, initrd_size);
+
+ result = dtb_set_initrd((char **)&dtb.buf,
+ &dtb.size, initrd_base,
+ initrd_base + initrd_size);
+
+ if (result)
+ return EFAILED;
+ }
+ }
+
+ if (!initrd_buf) {
+ /* Don't reuse the initrd addresses from 1st DTB */
+ dtb_clear_initrd((char **)&dtb.buf, &dtb.size);
+ }
+
+ /* 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_sink", &purgatory_sink,
+ sizeof(purgatory_sink));
+
+ 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);
+}
+
+static inline void set_phys_offset(int64_t v, char *set_method)
+{
+ if (arm64_mem.phys_offset == arm64_mem_ngv
+ || v < arm64_mem.phys_offset) {
+ arm64_mem.phys_offset = v;
+ dbgprintf("%s: phys_offset : %016lx (method : %s)\n",
+ __func__, arm64_mem.phys_offset,
+ set_method);
+ }
+}
+
+/**
+ * get_va_bits - Helper for getting VA_BITS
+ */
+
+static int get_va_bits(void)
+{
+ unsigned long long stext_sym_addr;
+
+ /*
+ * if already got from kcore
+ */
+ if (va_bits != -1)
+ goto out;
+
+
+ /* For kernel older than v4.19 */
+ fprintf(stderr, "Warning, can't get the VA_BITS from kcore\n");
+ stext_sym_addr = get_kernel_sym("_stext");
+
+ if (stext_sym_addr == 0) {
+ fprintf(stderr, "Can't get the symbol of _stext.\n");
+ return -1;
+ }
+
+ /* Derive va_bits as per arch/arm64/Kconfig */
+ if ((stext_sym_addr & PAGE_OFFSET_36) == PAGE_OFFSET_36) {
+ va_bits = 36;
+ } else if ((stext_sym_addr & PAGE_OFFSET_39) == PAGE_OFFSET_39) {
+ va_bits = 39;
+ } else if ((stext_sym_addr & PAGE_OFFSET_42) == PAGE_OFFSET_42) {
+ va_bits = 42;
+ } else if ((stext_sym_addr & PAGE_OFFSET_47) == PAGE_OFFSET_47) {
+ va_bits = 47;
+ } else if ((stext_sym_addr & PAGE_OFFSET_48) == PAGE_OFFSET_48) {
+ va_bits = 48;
+ } else {
+ fprintf(stderr,
+ "Cannot find a proper _stext for calculating VA_BITS\n");
+ return -1;
+ }
+
+out:
+ dbgprintf("va_bits : %d\n", va_bits);
+
+ return 0;
+}
+
+/**
+ * get_page_offset - Helper for getting PAGE_OFFSET
+ */
+
+int get_page_offset(unsigned long *page_offset)
+{
+ unsigned long long text_sym_addr, kernel_va_mid;
+ int ret;
+
+ text_sym_addr = get_kernel_sym("_text");
+ if (text_sym_addr == 0) {
+ fprintf(stderr, "Can't get the symbol of _text to calculate page_offset.\n");
+ return -1;
+ }
+
+ ret = get_va_bits();
+ if (ret < 0)
+ return ret;
+
+ /* Since kernel 5.4, kernel image is put above
+ * UINT64_MAX << (va_bits - 1)
+ */
+ kernel_va_mid = UINT64_MAX << (va_bits - 1);
+ /* older kernel */
+ if (text_sym_addr < kernel_va_mid)
+ *page_offset = UINT64_MAX << (va_bits - 1);
+ else
+ *page_offset = UINT64_MAX << va_bits;
+
+ dbgprintf("page_offset : %lx\n", *page_offset);
+
+ return 0;
+}
+
+static void arm64_scan_vmcoreinfo(char *pos)
+{
+ const char *str;
+
+ str = "NUMBER(VA_BITS)=";
+ if (memcmp(str, pos, strlen(str)) == 0)
+ va_bits = strtoul(pos + strlen(str), NULL, 10);
+}
+
+/**
+ * get_phys_offset_from_vmcoreinfo_pt_note - Helper for getting PHYS_OFFSET (and va_bits)
+ * from VMCOREINFO note inside 'kcore'.
+ */
+
+static int get_phys_offset_from_vmcoreinfo_pt_note(long *phys_offset)
+{
+ int fd, ret = 0;
+
+ if ((fd = open("/proc/kcore", O_RDONLY)) < 0) {
+ fprintf(stderr, "Can't open (%s).\n", "/proc/kcore");
+ return EFAILED;
+ }
+
+ arch_scan_vmcoreinfo = arm64_scan_vmcoreinfo;
+ ret = read_phys_offset_elf_kcore(fd, phys_offset);
+
+ close(fd);
+ return ret;
+}
+
+/**
+ * get_phys_base_from_pt_load - Helper for getting PHYS_OFFSET
+ * from PT_LOADs inside 'kcore'.
+ */
+
+int get_phys_base_from_pt_load(long *phys_offset)
+{
+ int i, fd, ret;
+ unsigned long long phys_start;
+ unsigned long long virt_start;
+
+ ret = get_page_offset(&page_offset);
+ if (ret < 0)
+ return ret;
+
+ if ((fd = open("/proc/kcore", O_RDONLY)) < 0) {
+ fprintf(stderr, "Can't open (%s).\n", "/proc/kcore");
+ return EFAILED;
+ }
+
+ read_elf(fd);
+
+ for (i = 0; get_pt_load(i,
+ &phys_start, NULL, &virt_start, NULL);
+ i++) {
+ if (virt_start != NOT_KV_ADDR
+ && virt_start >= page_offset
+ && phys_start != NOT_PADDR)
+ *phys_offset = phys_start -
+ (virt_start & ~page_offset);
+ }
+
+ close(fd);
+ return 0;
+}
+
+static bool to_be_excluded(char *str, unsigned long long start, unsigned long long end)
+{
+ if (!strncmp(str, CRASH_KERNEL, strlen(CRASH_KERNEL))) {
+ uint64_t load_start, load_end;
+
+ if (!get_crash_kernel_load_range(&load_start, &load_end) &&
+ (load_start == start) && (load_end == end))
+ return false;
+
+ return true;
+ }
+
+ if (!strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM)) ||
+ !strncmp(str, KERNEL_CODE, strlen(KERNEL_CODE)) ||
+ !strncmp(str, KERNEL_DATA, strlen(KERNEL_DATA)))
+ return false;
+ else
+ return true;
+}
+
+/**
+ * get_memory_ranges - Try to get the memory ranges from
+ * /proc/iomem.
+ */
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags)
+{
+ long phys_offset = -1;
+ FILE *fp;
+ const char *iomem = proc_iomem();
+ char line[MAX_LINE], *str;
+ unsigned long long start, end;
+ int n, consumed;
+ struct memory_ranges memranges;
+ struct memory_range *last, excl_range;
+ int ret;
+
+ if (!try_read_phys_offset_from_kcore) {
+ /* Since kernel version 4.19, 'kcore' contains
+ * a new PT_NOTE which carries the VMCOREINFO
+ * information.
+ * If the same is available, one should prefer the
+ * same to retrieve 'PHYS_OFFSET' value exported by
+ * the kernel as this is now the standard interface
+ * exposed by kernel for sharing machine specific
+ * details with the userland.
+ */
+ ret = get_phys_offset_from_vmcoreinfo_pt_note(&phys_offset);
+ if (!ret) {
+ if (phys_offset != -1)
+ set_phys_offset(phys_offset,
+ "vmcoreinfo pt_note");
+ } else {
+ /* If we are running on a older kernel,
+ * try to retrieve the 'PHYS_OFFSET' value
+ * exported by the kernel in the 'kcore'
+ * file by reading the PT_LOADs and determining
+ * the correct combination.
+ */
+ ret = get_phys_base_from_pt_load(&phys_offset);
+ if (!ret)
+ if (phys_offset != -1)
+ set_phys_offset(phys_offset,
+ "pt_load");
+ }
+
+ try_read_phys_offset_from_kcore = true;
+ }
+
+ fp = fopen(iomem, "r");
+ if (!fp)
+ die("Cannot open %s\n", iomem);
+
+ memranges.ranges = NULL;
+ memranges.size = memranges.max_size = 0;
+
+ while (fgets(line, sizeof(line), fp) != 0) {
+ n = sscanf(line, "%llx-%llx : %n", &start, &end, &consumed);
+ if (n != 2)
+ continue;
+ str = line + consumed;
+
+ if (!strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM))) {
+ ret = mem_regions_alloc_and_add(&memranges,
+ start, end - start + 1, RANGE_RAM);
+ if (ret) {
+ fprintf(stderr,
+ "Cannot allocate memory for ranges\n");
+ fclose(fp);
+ return -ENOMEM;
+ }
+
+ dbgprintf("%s:+[%d] %016llx - %016llx\n", __func__,
+ memranges.size - 1,
+ memranges.ranges[memranges.size - 1].start,
+ memranges.ranges[memranges.size - 1].end);
+ } else if (to_be_excluded(str, start, end)) {
+ if (!memranges.size)
+ continue;
+
+ /*
+ * Note: mem_regions_exclude() doesn't guarantee
+ * that the ranges are sorted out, but as long as
+ * we cope with /proc/iomem, we only operate on
+ * the last entry and so it is safe.
+ */
+
+ /* The last System RAM range */
+ last = &memranges.ranges[memranges.size - 1];
+
+ if (last->end < start)
+ /* New resource outside of System RAM */
+ continue;
+ if (end < last->start)
+ /* Already excluded by parent resource */
+ continue;
+
+ excl_range.start = start;
+ excl_range.end = end;
+ ret = mem_regions_alloc_and_exclude(&memranges, &excl_range);
+ if (ret) {
+ fprintf(stderr,
+ "Cannot allocate memory for ranges (exclude)\n");
+ fclose(fp);
+ return -ENOMEM;
+ }
+ dbgprintf("%s:- %016llx - %016llx\n",
+ __func__, start, end);
+ }
+ }
+
+ fclose(fp);
+
+ *range = memranges.ranges;
+ *ranges = memranges.size;
+
+ /* As a fallback option, we can try determining the PHYS_OFFSET
+ * value from the '/proc/iomem' entries as well.
+ *
+ * But note that this can be flaky, as on certain arm64
+ * platforms, it has been noticed that due to a hole at the
+ * start of physical ram exposed to kernel
+ * (i.e. it doesn't start from address 0), the kernel still
+ * calculates the 'memstart_addr' kernel variable as 0.
+ *
+ * Whereas the SYSTEM_RAM or IOMEM_RESERVED range in
+ * '/proc/iomem' would carry a first entry whose start address
+ * is non-zero (as the physical ram exposed to the kernel
+ * starts from a non-zero address).
+ *
+ * In such cases, if we rely on '/proc/iomem' entries to
+ * calculate the phys_offset, then we will have mismatch
+ * between the user-space and kernel space 'PHYS_OFFSET'
+ * value.
+ */
+ if (memranges.size)
+ set_phys_offset(memranges.ranges[0].start, "iomem");
+
+ dbgprint_mem_range("System RAM ranges;",
+ memranges.ranges, memranges.size);
+
+ return 0;
+}
+
+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);
+}
+
+enum aarch64_rel_type {
+ R_AARCH64_NONE = 0,
+ R_AARCH64_ABS64 = 257,
+ R_AARCH64_PREL32 = 261,
+ R_AARCH64_MOVW_UABS_G0_NC = 264,
+ R_AARCH64_MOVW_UABS_G1_NC = 266,
+ R_AARCH64_MOVW_UABS_G2_NC = 268,
+ R_AARCH64_MOVW_UABS_G3 =269,
+ R_AARCH64_LD_PREL_LO19 = 273,
+ R_AARCH64_ADR_PREL_LO21 = 274,
+ R_AARCH64_ADR_PREL_PG_HI21 = 275,
+ R_AARCH64_ADD_ABS_LO12_NC = 277,
+ R_AARCH64_JUMP26 = 282,
+ R_AARCH64_CALL26 = 283,
+ R_AARCH64_LDST64_ABS_LO12_NC = 286,
+ R_AARCH64_LDST128_ABS_LO12_NC = 299
+};
+
+static uint32_t get_bits(uint32_t value, int start, int end)
+{
+ uint32_t mask = ((uint32_t)1 << (end + 1 - start)) - 1;
+ return (value >> start) & mask;
+}
+
+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)
+{
+ uint64_t *loc64;
+ uint32_t *loc32;
+ uint64_t *location = (uint64_t *)ptr;
+ uint64_t data = *location;
+ uint64_t imm;
+ const char *type = NULL;
+
+ switch((enum aarch64_rel_type)r_type) {
+ case R_AARCH64_ABS64:
+ type = "ABS64";
+ loc64 = ptr;
+ *loc64 = cpu_to_elf64(ehdr, value);
+ break;
+ case R_AARCH64_PREL32:
+ type = "PREL32";
+ loc32 = ptr;
+ *loc32 = cpu_to_elf32(ehdr, value - address);
+ break;
+
+ /* Set a MOV[KZ] immediate field to bits [15:0] of X. No overflow check */
+ case R_AARCH64_MOVW_UABS_G0_NC:
+ type = "MOVW_UABS_G0_NC";
+ loc32 = ptr;
+ imm = get_bits(value, 0, 15);
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + (imm << 5));
+ break;
+ /* Set a MOV[KZ] immediate field to bits [31:16] of X. No overflow check */
+ case R_AARCH64_MOVW_UABS_G1_NC:
+ type = "MOVW_UABS_G1_NC";
+ loc32 = ptr;
+ imm = get_bits(value, 16, 31);
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + (imm << 5));
+ break;
+ /* Set a MOV[KZ] immediate field to bits [47:32] of X. No overflow check */
+ case R_AARCH64_MOVW_UABS_G2_NC:
+ type = "MOVW_UABS_G2_NC";
+ loc32 = ptr;
+ imm = get_bits(value, 32, 47);
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + (imm << 5));
+ break;
+ /* Set a MOV[KZ] immediate field to bits [63:48] of X */
+ case R_AARCH64_MOVW_UABS_G3:
+ type = "MOVW_UABS_G3";
+ loc32 = ptr;
+ imm = get_bits(value, 48, 63);
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + (imm << 5));
+ 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_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;
+ *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;
+ /* encode imm field with bits [11:3] of value */
+ 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;
+
+ /* encode imm field with bits [11:4] of value */
+ case R_AARCH64_LDST128_ABS_LO12_NC:
+ if (value & 15)
+ die("%s: ERROR Unaligned value: %lx\n", __func__,
+ value);
+ type = "LDST128_ABS_LO12_NC";
+ loc32 = ptr;
+ imm = value & 0xff0;
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + (imm << (10 - 4)));
+ 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..95fb5c2
--- /dev/null
+++ b/kexec/arch/arm64/kexec-arm64.h
@@ -0,0 +1,84 @@
+/*
+ * 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 64
+
+#define BOOT_BLOCK_VERSION 17
+#define BOOT_BLOCK_LAST_COMP_VERSION 16
+#define COMMAND_LINE_SIZE 2048 /* from kernel */
+
+#define KiB(x) ((x) * 1024UL)
+#define MiB(x) (KiB(x) * 1024UL)
+#define GiB(x) (MiB(x) * 1024UL)
+
+#define ULONGLONG_MAX (~0ULL)
+
+/*
+ * Incorrect address
+ */
+#define NOT_KV_ADDR (0x0)
+#define NOT_PADDR (ULONGLONG_MAX)
+
+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);
+
+int uImage_arm64_probe(const char *buf, off_t len);
+int uImage_arm64_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void uImage_arm64_usage(void);
+
+int pez_arm64_probe(const char *kernel_buf, off_t kernel_size);
+int pez_arm64_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void pez_arm64_usage(void);
+
+
+extern off_t initrd_base;
+extern off_t initrd_size;
+
+/**
+ * struct arm64_mem - Memory layout info.
+ */
+
+struct arm64_mem {
+ int64_t phys_offset;
+ uint64_t text_offset;
+ uint64_t image_size;
+ uint64_t vp_offset;
+};
+
+#define arm64_mem_ngv UINT64_MAX
+extern struct arm64_mem arm64_mem;
+
+uint64_t get_phys_offset(void);
+uint64_t get_vp_offset(void);
+int get_page_offset(unsigned long *offset);
+
+static inline void reset_vp_offset(void)
+{
+ arm64_mem.vp_offset = arm64_mem_ngv;
+}
+
+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..e14f8e9
--- /dev/null
+++ b/kexec/arch/arm64/kexec-elf-arm64.c
@@ -0,0 +1,170 @@
+/*
+ * ARM64 kexec elf support.
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <linux/elf.h>
+
+#include "arch/options.h"
+#include "crashdump-arm64.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->file_mode) {
+ fprintf(stderr,
+ "ELF executable is not supported in kexec_file\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));
+ if (!(info->kexec_flags & KEXEC_ON_CRASH))
+ arm64_mem.vp_offset -= kernel_segment - get_phys_offset();
+
+ dbgprintf("%s: kernel_segment: %016lx\n", __func__, kernel_segment);
+ dbgprintf("%s: text_offset: %016lx\n", __func__,
+ 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"));
+
+ /* create and initialize elf core header segment */
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ result = load_crashdump_segments(info);
+ if (result) {
+ dbgprintf("%s: Creating eflcorehdr failed.\n",
+ __func__);
+ goto exit;
+ }
+ }
+
+ /* load the kernel */
+ if (info->kexec_flags & KEXEC_ON_CRASH)
+ /*
+ * offset addresses in elf header in order to load
+ * vmlinux (elf_exec) into crash kernel's memory
+ */
+ fixup_elf_addrs(&ehdr);
+
+ result = elf_exec_load(&ehdr, info);
+
+ if (result) {
+ dbgprintf("%s: elf_exec_load failed\n", __func__);
+ goto exit;
+ }
+
+ /* load additional data */
+ 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..a196747
--- /dev/null
+++ b/kexec/arch/arm64/kexec-image-arm64.c
@@ -0,0 +1,119 @@
+/*
+ * ARM64 kexec binary image support.
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include "crashdump-arm64.h"
+#include "image-header.h"
+#include "kexec.h"
+#include "kexec-arm64.h"
+#include "kexec-syscall.h"
+#include "arch/options.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;
+ }
+
+ return 0;
+}
+
+int image_arm64_load(int argc, char **argv, const char *kernel_buf,
+ off_t kernel_size, struct kexec_info *info)
+{
+ const struct arm64_image_header *header;
+ unsigned long kernel_segment;
+ int result;
+
+ if (info->file_mode) {
+ if (arm64_opts.initrd) {
+ info->initrd_fd = open(arm64_opts.initrd, O_RDONLY);
+ if (info->initrd_fd == -1) {
+ fprintf(stderr,
+ "Could not open initrd file %s:%s\n",
+ arm64_opts.initrd, strerror(errno));
+ result = EFAILED;
+ goto exit;
+ }
+ }
+
+ if (arm64_opts.command_line) {
+ info->command_line = (char *)arm64_opts.command_line;
+ info->command_line_len =
+ strlen(arm64_opts.command_line) + 1;
+ }
+
+ return 0;
+ }
+
+ header = (const struct arm64_image_header *)(kernel_buf);
+
+ if (arm64_process_image_header(header))
+ return EFAILED;
+
+ 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"));
+
+ /* create and initialize elf core header segment */
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ result = load_crashdump_segments(info);
+ if (result) {
+ dbgprintf("%s: Creating eflcorehdr failed.\n",
+ __func__);
+ goto exit;
+ }
+ }
+
+ /* load the kernel */
+ add_segment_phys_virt(info, kernel_buf, kernel_size,
+ kernel_segment + arm64_mem.text_offset,
+ arm64_mem.image_size, 0);
+
+ /* load additional data */
+ 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)
+{
+ printf(
+" An ARM64 binary image, compressed or not, big or little endian.\n"
+" Typically an Image file.\n\n");
+}
diff --git a/kexec/arch/arm64/kexec-uImage-arm64.c b/kexec/arch/arm64/kexec-uImage-arm64.c
new file mode 100644
index 0000000..c466913
--- /dev/null
+++ b/kexec/arch/arm64/kexec-uImage-arm64.c
@@ -0,0 +1,52 @@
+/*
+ * uImage support added by David Woodhouse <dwmw2@infradead.org>
+ */
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <image.h>
+#include <kexec-uImage.h>
+#include "../../kexec.h"
+#include "kexec-arm64.h"
+
+int uImage_arm64_probe(const char *buf, off_t len)
+{
+ int ret;
+
+ ret = uImage_probe_kernel(buf, len, IH_ARCH_ARM64);
+
+ /* 0 - valid uImage.
+ * -1 - uImage is corrupted.
+ * 1 - image is not a uImage.
+ */
+ if (!ret)
+ return 0;
+ else
+ return -1;
+}
+
+int uImage_arm64_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ struct Image_info img;
+ int ret;
+
+ if (info->file_mode) {
+ fprintf(stderr,
+ "uImage is not supported in kexec_file\n");
+
+ return EFAILED;
+ }
+
+ ret = uImage_load(buf, len, &img);
+ if (ret)
+ return ret;
+
+ return image_arm64_load(argc, argv, img.buf, img.len, info);
+}
+
+void uImage_arm64_usage(void)
+{
+ printf(
+" An ARM64 U-boot uImage file, compressed or not, big or little endian.\n\n");
+}
diff --git a/kexec/arch/arm64/kexec-vmlinuz-arm64.c b/kexec/arch/arm64/kexec-vmlinuz-arm64.c
new file mode 100644
index 0000000..c0ee47c
--- /dev/null
+++ b/kexec/arch/arm64/kexec-vmlinuz-arm64.c
@@ -0,0 +1,110 @@
+/*
+ * ARM64 PE compressed Image (vmlinuz, ZBOOT) support.
+ *
+ * Several distros use 'make zinstall' rule inside
+ * 'arch/arm64/boot/Makefile' to install the arm64
+ * ZBOOT compressed file inside the boot destination
+ * directory (for e.g. /boot).
+ *
+ * Currently we cannot use kexec_file_load() to load vmlinuz
+ * PE images that self decompress.
+ *
+ * To support ZBOOT, we should:
+ * a). Copy the compressed contents of vmlinuz to a temporary file.
+ * b). Decompress (gunzip-decompress) the contents inside the
+ * temporary file.
+ * c). Validate the resulting image and write it back to the
+ * temporary file.
+ * d). Pass the 'fd' of the temporary file to the kernel space.
+ *
+ * Note this, module doesn't provide a _load() function instead
+ * relying on image_arm64_load() to load the resulting decompressed
+ * image.
+ *
+ * So basically the kernel space still gets a decompressed
+ * kernel image to load via kexec-tools.
+ */
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "kexec-arm64.h"
+#include <kexec-pe-zboot.h>
+#include "arch/options.h"
+
+static int kernel_fd = -1;
+
+/* Returns:
+ * -1 : in case of error/invalid format (not a valid PE+compressed ZBOOT format.
+ */
+int pez_arm64_probe(const char *kernel_buf, off_t kernel_size)
+{
+ int ret = -1;
+ const struct arm64_image_header *h;
+ char *buf;
+ off_t buf_sz;
+
+ buf = (char *)kernel_buf;
+ buf_sz = kernel_size;
+ if (!buf)
+ return -1;
+ h = (const struct arm64_image_header *)buf;
+
+ dbgprintf("%s: PROBE.\n", __func__);
+ if (buf_sz < sizeof(struct arm64_image_header)) {
+ dbgprintf("%s: Not large enough to be a PE image.\n", __func__);
+ return -1;
+ }
+ if (!arm64_header_check_pe_sig(h)) {
+ dbgprintf("%s: Not an PE image.\n", __func__);
+ return -1;
+ }
+
+ if (buf_sz < sizeof(struct arm64_image_header) + h->pe_header) {
+ dbgprintf("%s: PE image offset larger than image.\n", __func__);
+ return -1;
+ }
+
+ if (memcmp(&buf[h->pe_header],
+ arm64_pe_machtype, sizeof(arm64_pe_machtype))) {
+ dbgprintf("%s: PE header doesn't match machine type.\n", __func__);
+ return -1;
+ }
+
+ ret = pez_prepare(buf, buf_sz, &kernel_fd);
+
+ if (!ret) {
+ /* validate the arm64 specific header */
+ struct arm64_image_header hdr_check;
+ if (read(kernel_fd, &hdr_check, sizeof(hdr_check)) != sizeof(hdr_check))
+ goto bad_header;
+
+ lseek(kernel_fd, 0, SEEK_SET);
+
+ if (!arm64_header_check_magic(&hdr_check)) {
+ dbgprintf("%s: Bad arm64 image header.\n", __func__);
+ goto bad_header;
+ }
+ }
+
+ return ret;
+bad_header:
+ close(kernel_fd);
+ free(buf);
+ return -1;
+}
+
+int pez_arm64_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ info->kernel_fd = kernel_fd;
+ return image_arm64_load(argc, argv, buf, len, info);
+}
+
+void pez_arm64_usage(void)
+{
+ printf(
+" An ARM64 vmlinuz, PE image of a compressed, little endian.\n"
+" kernel, built with ZBOOT enabled.\n\n");
+}
diff --git a/kexec/arch/cris/Makefile b/kexec/arch/cris/Makefile
new file mode 100644
index 0000000..4982f3e
--- /dev/null
+++ b/kexec/arch/cris/Makefile
@@ -0,0 +1,13 @@
+cris_KEXEC_SRCS = kexec/arch/cris/kexec-cris.c
+cris_KEXEC_SRCS += kexec/arch/cris/kexec-elf-cris.c
+cris_KEXEC_SRCS += kexec/arch/cris/cris-setup-simple.S
+cris_KEXEC_SRCS += kexec/arch/cris/kexec-elf-rel-cris.c
+
+cris_ADD_BUFFER =
+cris_ADD_SEGMENT =
+cris_VIRT_TO_PHYS =
+
+dist += kexec/arch/cris/Makefile $(cris_KEXEC_SRCS) \
+ kexec/arch/cris/kexec-cris.h \
+ kexec/arch/cris/include/arch/options.h
+
diff --git a/kexec/arch/cris/cris-setup-simple.S b/kexec/arch/cris/cris-setup-simple.S
new file mode 100644
index 0000000..764f188
--- /dev/null
+++ b/kexec/arch/cris/cris-setup-simple.S
@@ -0,0 +1,31 @@
+/*
+ * cris-setup-simple.S - code to execute before stepping into the new kernel.
+ * Copyright (C) 2008 AXIS Communications AB
+ * Written by Edgar E. Iglesias
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+ .data
+ .globl cris_trampoline
+cris_trampoline:
+ .balign 4
+ lapc cris_regframe, $sp
+ moveq 0, $r0
+ move $r0, $pid
+
+ movem [$sp+], $r14
+ jump $r0
+ nop
+
+ .globl cris_regframe
+cris_regframe:
+ .balign 4
+ .fill 16, 4, 0
+cris_trampoline_end:
+
+ .globl cris_trampoline_size
+cris_trampoline_size:
+ .long cris_trampoline_end - cris_trampoline
+
diff --git a/kexec/arch/cris/include/arch/options.h b/kexec/arch/cris/include/arch/options.h
new file mode 100644
index 0000000..1c1a029
--- /dev/null
+++ b/kexec/arch/cris/include/arch/options.h
@@ -0,0 +1,35 @@
+#ifndef KEXEC_ARCH_CRIS_OPTIONS_H
+#define KEXEC_ARCH_CRIS_OPTIONS_H
+
+#define OPT_ARCH_MAX (OPT_MAX+0)
+#define OPT_APPEND (OPT_ARCH_MAX+0)
+
+/* Options relevant to the architecture (excluding loader-specific ones),
+ * in this case none:
+ */
+#define KEXEC_ARCH_OPTIONS \
+ KEXEC_OPTIONS \
+
+#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
+
+/* The following two #defines list ALL of the options added by all of the
+ * architecture's loaders.
+ * o main() uses this complete list to scan for its options, ignoring
+ * arch-specific/loader-specific ones.
+ * o Then, arch_process_options() uses this complete list to scan for its
+ * options, ignoring general/loader-specific ones.
+ * o Then, the file_type[n].load re-scans for options, using
+ * KEXEC_ARCH_OPTIONS plus its loader-specific options subset.
+ * Any unrecognised options cause an error here.
+ *
+ * This is done so that main()'s/arch_process_options()'s getopt_long() calls
+ * don't choose a kernel filename from random arguments to options they don't
+ * recognise -- as they now recognise (if not act upon) all possible options.
+ */
+#define KEXEC_ALL_OPTIONS \
+ KEXEC_ARCH_OPTIONS \
+ {"append", 1, 0, OPT_APPEND},
+
+#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR
+
+#endif /* KEXEC_ARCH_CRIS_OPTIONS_H */
diff --git a/kexec/arch/cris/kexec-cris.c b/kexec/arch/cris/kexec-cris.c
new file mode 100644
index 0000000..3b69709
--- /dev/null
+++ b/kexec/arch/cris/kexec-cris.c
@@ -0,0 +1,111 @@
+/*
+ * kexec-cris.c
+ * Copyright (C) 2008 AXIS Communications AB
+ * Written by Edgar E. Iglesias
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "kexec-cris.h"
+#include <arch/options.h>
+
+#define MAX_MEMORY_RANGES 64
+#define MAX_LINE 160
+static struct memory_range memory_range[MAX_MEMORY_RANGES];
+
+/* Return a sorted list of memory ranges. */
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long UNUSED(kexec_flags))
+{
+ int memory_ranges = 0;
+
+ memory_range[memory_ranges].start = 0x40000000;
+ memory_range[memory_ranges].end = 0x41000000;
+ memory_range[memory_ranges].type = RANGE_RAM;
+ memory_ranges++;
+
+ memory_range[memory_ranges].start = 0xc0000000;
+ memory_range[memory_ranges].end = 0xc1000000;
+ memory_range[memory_ranges].type = RANGE_RAM;
+ memory_ranges++;
+
+ *range = memory_range;
+ *ranges = memory_ranges;
+ return 0;
+}
+
+struct file_type file_type[] = {
+ {"elf-cris", elf_cris_probe, elf_cris_load, elf_cris_usage},
+};
+int file_types = sizeof(file_type) / sizeof(file_type[0]);
+
+void arch_usage(void)
+{
+}
+
+int arch_process_options(int argc, char **argv)
+{
+ return 0;
+}
+
+const struct arch_map_entry arches[] = {
+ { "cris", KEXEC_ARCH_CRIS },
+ { "crisv32", KEXEC_ARCH_CRIS },
+ { 0 },
+};
+
+int arch_compat_trampoline(struct kexec_info *UNUSED(info))
+{
+ return 0;
+}
+
+void arch_update_purgatory(struct kexec_info *UNUSED(info))
+{
+}
+
+int is_crashkernel_mem_reserved(void)
+{
+ return 0;
+}
+
+int get_crash_kernel_load_range(uint64_t *start, uint64_t *end)
+{
+ /* Crash kernel region size is not exposed by the system */
+ return -1;
+}
+
+unsigned long virt_to_phys(unsigned long addr)
+{
+ return (addr) & 0x7fffffff;
+}
+
+/*
+ * add_segment() should convert base to a physical address on cris,
+ * while the default is just to work with base as is */
+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);
+}
+
+/*
+ * add_buffer() should convert base to a physical address on cris,
+ * while the default is just to work with base as is */
+unsigned long add_buffer(struct kexec_info *info, const void *buf,
+ unsigned long bufsz, unsigned long memsz,
+ unsigned long buf_align, unsigned long buf_min,
+ unsigned long buf_max, int buf_end)
+{
+ return add_buffer_phys_virt(info, buf, bufsz, memsz, buf_align,
+ buf_min, buf_max, buf_end, 1);
+}
+
diff --git a/kexec/arch/cris/kexec-cris.h b/kexec/arch/cris/kexec-cris.h
new file mode 100644
index 0000000..7ee9945
--- /dev/null
+++ b/kexec/arch/cris/kexec-cris.h
@@ -0,0 +1,9 @@
+#ifndef KEXEC_CRIS_H
+#define KEXEC_CRIS_H
+
+int elf_cris_probe(const char *buf, off_t len);
+int elf_cris_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void elf_cris_usage(void);
+
+#endif /* KEXEC_CRIS_H */
diff --git a/kexec/arch/cris/kexec-elf-cris.c b/kexec/arch/cris/kexec-elf-cris.c
new file mode 100644
index 0000000..7e251e6
--- /dev/null
+++ b/kexec/arch/cris/kexec-elf-cris.c
@@ -0,0 +1,133 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2008 AXIS Communications AB
+ * Written by Edgar E. Iglesias
+ *
+ * Based on x86 implementation,
+ * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-elf-boot.h"
+#include <arch/options.h>
+#include "kexec-cris.h"
+
+int elf_cris_probe(const char *buf, off_t len)
+{
+ struct mem_ehdr ehdr;
+ int result;
+ result = build_elf_exec_info(buf, len, &ehdr, 0);
+ if (result < 0)
+ goto out;
+
+ /* Verify the architecuture specific bits */
+ if (ehdr.e_machine != EM_CRIS) {
+ result = -1;
+ goto out;
+ }
+
+ result = 0;
+ out:
+ free_elf_info(&ehdr);
+ return result;
+}
+
+void elf_cris_usage(void)
+{
+ printf(" --append=STRING Set the kernel command line to STRING\n"
+ );
+}
+
+#define CRAMFS_MAGIC 0x28cd3d45
+#define JHEAD_MAGIC 0x1FF528A6
+#define JHEAD_SIZE 8
+#define RAM_INIT_MAGIC 0x56902387
+#define COMMAND_LINE_MAGIC 0x87109563
+#define NAND_BOOT_MAGIC 0x9a9db001
+
+int elf_cris_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ struct mem_ehdr ehdr;
+ char *command_line;
+ unsigned int *trampoline_buf;
+ unsigned long trampoline_base;
+ int opt;
+ extern void cris_trampoline(void);
+ extern unsigned long cris_trampoline_size;
+ extern struct regframe_t {
+ unsigned int regs[16];
+ } cris_regframe;
+
+ /* See options.h -- add any more there, too. */
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ {"append", 1, 0, OPT_APPEND},
+ { 0, 0, 0, 0 },
+ };
+
+ static const char short_options[] = KEXEC_OPT_STR "";
+
+ /*
+ * Parse the command line arguments
+ */
+ command_line = 0;
+ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch(opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ case OPT_APPEND:
+ command_line = optarg;
+ break;
+ }
+ }
+
+ /* Load the ELF executable */
+ elf_exec_build_load(info, &ehdr, buf, len, 0);
+
+ cris_regframe.regs[0] = virt_to_phys(ehdr.e_entry);
+ cris_regframe.regs[8] = RAM_INIT_MAGIC;
+ cris_regframe.regs[12] = NAND_BOOT_MAGIC;
+
+ trampoline_buf = xmalloc(cris_trampoline_size);
+ trampoline_base = add_buffer_virt(info,
+ trampoline_buf,
+ cris_trampoline_size,
+ cris_trampoline_size,
+ 4, 0, elf_max_addr(&ehdr), 1);
+ memcpy(trampoline_buf,
+ cris_trampoline, cris_trampoline_size);
+ info->entry = (void *)trampoline_base;
+ return 0;
+}
diff --git a/kexec/arch/cris/kexec-elf-rel-cris.c b/kexec/arch/cris/kexec-elf-rel-cris.c
new file mode 100644
index 0000000..255cc2c
--- /dev/null
+++ b/kexec/arch/cris/kexec-elf-rel-cris.c
@@ -0,0 +1,43 @@
+/*
+ * kexec-elf-rel-cris.c - kexec Elf relocation routines
+ * Copyright (C) 2008 AXIS Communications AB
+ * Written by Edgar E. Iglesias
+ *
+ * derived from ../ppc/kexec-elf-rel-ppc.c
+ * Copyright (C) 2004 Albert Herranz
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+*/
+
+#include <stdio.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+
+int machine_verify_elf_rel(struct mem_ehdr *ehdr)
+{
+ if (ehdr->ei_data != ELFDATA2MSB) {
+ return 0;
+ }
+ if (ehdr->ei_class != ELFCLASS32) {
+ return 0;
+ }
+ if (ehdr->e_machine != EM_CRIS) {
+ return 0;
+ }
+ return 1;
+}
+
+void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr),
+ struct mem_sym *UNUSED(sym), unsigned long r_type, void *location,
+ unsigned long address, unsigned long value)
+{
+ switch(r_type) {
+
+ default:
+ die("Unknown rela relocation: %lu\n", r_type);
+ break;
+ }
+ return;
+}
diff --git a/kexec/arch/hppa/Makefile b/kexec/arch/hppa/Makefile
new file mode 100644
index 0000000..e98f6da
--- /dev/null
+++ b/kexec/arch/hppa/Makefile
@@ -0,0 +1,13 @@
+#
+# kexec hppa (linux booting linux)
+#
+hppa_KEXEC_SRCS = kexec/arch/hppa/kexec-hppa.c
+hppa_KEXEC_SRCS += kexec/arch/hppa/kexec-elf-hppa.c
+hppa_KEXEC_SRCS += kexec/arch/hppa/kexec-elf-rel-hppa.c
+
+hppa_ADD_SEGMENT =
+hppa_VIRT_TO_PHYS =
+
+dist += kexec/arch/hppa/Makefile $(hppa_KEXEC_SRCS) \
+ kexec/arch/hppa/kexec-hppa.h \
+ kexec/arch/hppa/include/arch/options.h
diff --git a/kexec/arch/hppa/include/arch/options.h b/kexec/arch/hppa/include/arch/options.h
new file mode 100644
index 0000000..e9deb51
--- /dev/null
+++ b/kexec/arch/hppa/include/arch/options.h
@@ -0,0 +1,32 @@
+#ifndef KEXEC_ARCH_HPPA_OPTIONS_H
+#define KEXEC_ARCH_HPPA_OPTIONS_H
+
+#define OPT_ARCH_MAX (OPT_MAX+0)
+
+#define KEXEC_ARCH_OPTIONS \
+ KEXEC_OPTIONS
+
+#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
+
+#define KEXEC_ALL_OPTIONS \
+ KEXEC_ARCH_OPTIONS \
+ { "command-line", 1, 0, OPT_APPEND }, \
+ { "reuse-cmdline", 0, 0, OPT_REUSE_CMDLINE }, \
+ { "append", 1, 0, OPT_APPEND }, \
+ { "initrd", 1, 0, OPT_RAMDISK }, \
+ { "ramdisk", 1, 0, OPT_RAMDISK },
+
+
+#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR ""
+
+/* See the other architectures for details of these; HPPA has no
+ * loader-specific options yet.
+ */
+#define OPT_ARCH_MAX (OPT_MAX+0)
+
+#define OPT_APPEND (OPT_ARCH_MAX+0)
+#define OPT_REUSE_CMDLINE (OPT_ARCH_MAX+1)
+#define OPT_RAMDISK (OPT_ARCH_MAX+2)
+
+#define MAX_MEMORY_RANGES 16
+#endif /* KEXEC_ARCH_HPPA_OPTIONS_H */
diff --git a/kexec/arch/hppa/kexec-elf-hppa.c b/kexec/arch/hppa/kexec-elf-hppa.c
new file mode 100644
index 0000000..474a919
--- /dev/null
+++ b/kexec/arch/hppa/kexec-elf-hppa.c
@@ -0,0 +1,159 @@
+/*
+ * kexec-elf-hppa.c - kexec Elf loader for hppa
+ *
+ * Copyright (c) 2019 Sven Schnelle <svens@stackframe.org>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+*/
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <elf.h>
+#include <boot/elf_boot.h>
+#include <ip_checksum.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-syscall.h"
+#include "kexec-hppa.h"
+#include <arch/options.h>
+
+#define PAGE_SIZE 4096
+
+extern unsigned long phys_offset;
+
+int elf_hppa_probe(const char *buf, off_t len)
+{
+ struct mem_ehdr ehdr;
+ int result;
+ result = build_elf_exec_info(buf, len, &ehdr, 0);
+ if (result < 0)
+ goto out;
+
+ phys_offset = ehdr.e_entry & 0xf0000000;
+ /* Verify the architecuture specific bits */
+ if (ehdr.e_machine != EM_PARISC) {
+ /* for a different architecture */
+ fprintf(stderr, "Not for this architecture.\n");
+ result = -1;
+ goto out;
+ }
+ result = 0;
+ out:
+ free_elf_info(&ehdr);
+ return result;
+}
+
+void elf_hppa_usage(void)
+{
+ printf(" --command-line=STRING Set the kernel command line to STRING\n"
+ " --append=STRING Set the kernel command line to STRING\n"
+ " --reuse-cmdline Use kernel command line from running system.\n"
+ " --ramdisk=FILE Use FILE as the kernel's initial ramdisk.\n"
+ " --initrd=FILE Use FILE as the kernel's initial ramdisk.\n"
+ );
+}
+
+int elf_hppa_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ struct mem_ehdr ehdr;
+ char *cmdline = NULL, *ramdisk = NULL;
+ int opt, result, i;
+ unsigned long ramdisk_addr = 0;
+ off_t ramdisk_size = 0;
+
+ static const struct option options[] = {
+ KEXEC_ALL_OPTIONS
+ { 0, 0, NULL, 0 },
+ };
+
+ static const char short_options[] = KEXEC_ALL_OPT_STR;
+
+ while ((opt = getopt_long(argc, argv, short_options, options, 0)) !=
+ -1) {
+ switch (opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX)
+ break;
+ case OPT_APPEND:
+ cmdline = strdup(optarg);
+ break;
+ case OPT_REUSE_CMDLINE:
+ cmdline = get_command_line();
+ break;
+ case OPT_RAMDISK:
+ ramdisk = optarg;
+ break;
+ }
+ }
+
+ if (info->file_mode) {
+ if (cmdline) {
+ info->command_line = cmdline;
+ info->command_line_len = strlen(cmdline) + 1;
+ }
+
+ if (ramdisk) {
+ info->initrd_fd = open(ramdisk, O_RDONLY);
+ if (info->initrd_fd == -1) {
+ fprintf(stderr, "Could not open initrd file "
+ "%s:%s\n", ramdisk, strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ result = build_elf_exec_info(buf, len, &ehdr, 0);
+ if (result < 0)
+ die("ELF exec parse failed\n");
+
+ /* Fixup PT_LOAD segments that include the ELF header (offset zero) */
+ for (i = 0; i < ehdr.e_phnum; i++) {
+ struct mem_phdr *phdr;
+ phdr = &ehdr.e_phdr[i];
+ if (phdr->p_type != PT_LOAD || phdr->p_offset)
+ continue;
+
+ dbgprintf("Removing ELF header from segment %d\n", i);
+ phdr->p_paddr += PAGE_SIZE;
+ phdr->p_vaddr += PAGE_SIZE;
+ phdr->p_filesz -= PAGE_SIZE;
+ phdr->p_memsz -= PAGE_SIZE;
+ phdr->p_offset += PAGE_SIZE;
+ phdr->p_data += PAGE_SIZE;
+ }
+
+ /* Load the ELF data */
+ result = elf_exec_load(&ehdr, info);
+ if (result < 0)
+ die("ELF exec load failed\n");
+
+ info->entry = (void *)virt_to_phys(ehdr.e_entry);
+
+
+ /* Load ramdisk */
+ if (ramdisk) {
+ void *initrd = slurp_decompress_file(ramdisk, &ramdisk_size);
+ /* Store ramdisk at top of first memory chunk */
+ ramdisk_addr = _ALIGN_DOWN(info->memory_range[0].end -
+ ramdisk_size + 1, PAGE_SIZE);
+ if (!buf)
+ die("Ramdisk load failed\n");
+ add_buffer(info, initrd, ramdisk_size, ramdisk_size,
+ PAGE_SIZE, ramdisk_addr, info->memory_range[0].end,
+ 1);
+ }
+
+ return 0;
+}
diff --git a/kexec/arch/hppa/kexec-elf-rel-hppa.c b/kexec/arch/hppa/kexec-elf-rel-hppa.c
new file mode 100644
index 0000000..661b67b
--- /dev/null
+++ b/kexec/arch/hppa/kexec-elf-rel-hppa.c
@@ -0,0 +1,37 @@
+/*
+ * kexec-elf-rel-hppa.c - kexec Elf relocation routines
+ *
+ * Copyright (C) 2019 Sven Schnelle <svens@stackframe.org>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+*/
+
+#include <stdio.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+
+int machine_verify_elf_rel(struct mem_ehdr *ehdr)
+{
+ if (ehdr->ei_data != ELFDATA2MSB)
+ return 0;
+ if (ehdr->e_machine != EM_PARISC)
+ return 0;
+ return 1;
+}
+
+void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr),
+ struct mem_sym *UNUSED(sym),
+ unsigned long r_type,
+ void *UNUSED(location),
+ unsigned long UNUSED(address),
+ unsigned long UNUSED(value))
+{
+ switch (r_type) {
+ default:
+ die("Unknown rela relocation: %lu\n", r_type);
+ break;
+ }
+ return;
+}
diff --git a/kexec/arch/hppa/kexec-hppa.c b/kexec/arch/hppa/kexec-hppa.c
new file mode 100644
index 0000000..77c9739
--- /dev/null
+++ b/kexec/arch/hppa/kexec-hppa.c
@@ -0,0 +1,148 @@
+/*
+ * kexec-hppa.c - kexec for hppa
+ *
+ * Copyright (C) 2019 Sven Schnelle <svens@stackframe.org>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "kexec-hppa.h"
+#include <arch/options.h>
+
+#define SYSTEM_RAM "System RAM\n"
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+static struct memory_range memory_range[MAX_MEMORY_RANGES];
+unsigned long phys_offset;
+
+/* Return a sorted list of available memory ranges. */
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long UNUSED(kexec_flags))
+{
+ const char *iomem = proc_iomem();
+ int memory_ranges = 0;
+ char line[512];
+ FILE *fp;
+
+ fp = fopen(iomem, "r");
+
+ if (!fp) {
+ fprintf(stderr, "Cannot open %s: %s\n",
+ iomem, strerror(errno));
+ return -1;
+ }
+
+ while(fgets(line, sizeof(line), fp) != 0) {
+ unsigned long long start, end;
+ char *str;
+ int type;
+ int consumed;
+ int count;
+
+
+ count = sscanf(line, "%llx-%llx : %n", &start, &end, &consumed);
+
+ if (count != 2)
+ continue;
+
+ str = line + consumed;
+
+ if (memcmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM)) == 0) {
+ type = RANGE_RAM;
+ } else if (memcmp(str, "reserved\n", 9) == 0) {
+ type = RANGE_RESERVED;
+ } else {
+ continue;
+ }
+
+ memory_range[memory_ranges].start = start;
+ memory_range[memory_ranges].end = end;
+ memory_range[memory_ranges].type = type;
+ if (++memory_ranges >= MAX_MEMORY_RANGES)
+ break;
+ }
+ fclose(fp);
+ *range = memory_range;
+ *ranges = memory_ranges;
+
+ dbgprint_mem_range("MEMORY RANGES", *range, *ranges);
+ return 0;
+}
+
+struct file_type file_type[] = {
+ {"elf-hppa", elf_hppa_probe, elf_hppa_load, elf_hppa_usage},
+};
+int file_types = ARRAY_SIZE(file_type);
+
+void arch_usage(void)
+{
+}
+
+int arch_process_options(int argc, char **argv)
+{
+ static const struct option options[] = {
+ KEXEC_ALL_OPTIONS
+ { 0, 0, NULL, 0 },
+ };
+ static const char short_options[] = KEXEC_ALL_OPT_STR;
+ int opt;
+
+ opterr = 0; /* Don't complain about unrecognized options here */
+ while ((opt = getopt_long(argc, argv, short_options, options, 0)) !=
+ -1) {
+ switch (opt) {
+ default:
+ break;
+ }
+ }
+ /* Reset getopt for the next pass; called in other source modules */
+ opterr = 1;
+ optind = 1;
+ return 0;
+}
+
+const struct arch_map_entry arches[] = {
+ { "parisc64", KEXEC_ARCH_HPPA },
+ { "parisc", KEXEC_ARCH_HPPA },
+ { NULL, 0 },
+};
+
+int arch_compat_trampoline(struct kexec_info *UNUSED(info))
+{
+ return 0;
+}
+
+void arch_update_purgatory(struct kexec_info *UNUSED(info))
+{
+}
+
+int is_crashkernel_mem_reserved(void)
+{
+ return 0;
+}
+
+int get_crash_kernel_load_range(uint64_t *start, uint64_t *end)
+{
+ /* Crash kernel region size is not exposed by the system */
+ return -1;
+}
+
+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);
+}
+
+unsigned long virt_to_phys(unsigned long addr)
+{
+ return addr - phys_offset;
+}
diff --git a/kexec/arch/hppa/kexec-hppa.h b/kexec/arch/hppa/kexec-hppa.h
new file mode 100644
index 0000000..485e5b6
--- /dev/null
+++ b/kexec/arch/hppa/kexec-hppa.h
@@ -0,0 +1,9 @@
+#ifndef KEXEC_HPPA_H
+#define KEXEC_HPPA_H
+
+int elf_hppa_probe(const char *buf, off_t len);
+int elf_hppa_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void elf_hppa_usage(void);
+
+#endif /* KEXEC_HPPA_H */
diff --git a/kexec/arch/i386/Makefile b/kexec/arch/i386/Makefile
new file mode 100644
index 0000000..f486103
--- /dev/null
+++ b/kexec/arch/i386/Makefile
@@ -0,0 +1,20 @@
+#
+# kexec i386 (linux booting linux)
+#
+i386_KEXEC_SRCS = kexec/arch/i386/kexec-x86.c
+i386_KEXEC_SRCS += kexec/arch/i386/kexec-x86-common.c
+i386_KEXEC_SRCS += kexec/arch/i386/kexec-elf-x86.c
+i386_KEXEC_SRCS += kexec/arch/i386/kexec-elf-rel-x86.c
+i386_KEXEC_SRCS += kexec/arch/i386/kexec-bzImage.c
+i386_KEXEC_SRCS += kexec/arch/i386/kexec-multiboot-x86.c
+i386_KEXEC_SRCS += kexec/arch/i386/kexec-mb2-x86.c
+i386_KEXEC_SRCS += kexec/arch/i386/kexec-beoboot-x86.c
+i386_KEXEC_SRCS += kexec/arch/i386/kexec-nbi.c
+i386_KEXEC_SRCS += kexec/arch/i386/x86-linux-setup.c
+i386_KEXEC_SRCS += kexec/arch/i386/crashdump-x86.c
+
+dist += kexec/arch/i386/Makefile $(i386_KEXEC_SRCS) \
+ kexec/arch/i386/crashdump-x86.h \
+ kexec/arch/i386/kexec-x86.h \
+ kexec/arch/i386/x86-linux-setup.h \
+ kexec/arch/i386/include/arch/options.h
diff --git a/kexec/arch/i386/crashdump-x86.c b/kexec/arch/i386/crashdump-x86.c
new file mode 100644
index 0000000..df1f24c
--- /dev/null
+++ b/kexec/arch/i386/crashdump-x86.c
@@ -0,0 +1,1049 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Created by: Vivek Goyal (vgoyal@in.ibm.com)
+ * old x86_64 version Created by: Murali M Chakravarthy (muralim@in.ibm.com)
+ * Copyright (C) IBM Corporation, 2005. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define _XOPEN_SOURCE 600
+#define _BSD_SOURCE
+#define _DEFAULT_SOURCE
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <elf.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-syscall.h"
+#include "../../firmware_memmap.h"
+#include "../../crashdump.h"
+#include "kexec-x86.h"
+#include "crashdump-x86.h"
+#include "../../kexec-xen.h"
+#include "x86-linux-setup.h"
+#include <x86/x86-linux.h>
+
+extern struct arch_options_t arch_options;
+
+static int get_kernel_page_offset(struct kexec_info *UNUSED(info),
+ struct crash_elf_info *elf_info)
+{
+
+ if (elf_info->machine == EM_X86_64) {
+ /* get_kernel_vaddr_and_size will override this */
+ elf_info->page_offset = X86_64_PAGE_OFFSET;
+ }
+ else if (elf_info->machine == EM_386) {
+ elf_info->page_offset = X86_PAGE_OFFSET;
+ }
+
+ return 0;
+}
+
+#define X86_64_KERN_VADDR_ALIGN 0x100000 /* 1MB */
+
+/* Read kernel physical load addr from the file returned by proc_iomem()
+ * (Kernel Code) and store in kexec_info */
+static int get_kernel_paddr(struct kexec_info *UNUSED(info),
+ struct crash_elf_info *elf_info)
+{
+ uint64_t start;
+
+ if (elf_info->machine != EM_X86_64)
+ return 0;
+
+ if (xen_present()) /* Kernel not entity mapped under Xen */
+ return 0;
+
+ if (parse_iomem_single("Kernel code\n", &start, NULL) == 0) {
+ elf_info->kern_paddr_start = start;
+ dbgprintf("kernel load physical addr start = 0x%016Lx\n",
+ (unsigned long long)start);
+ return 0;
+ }
+
+ fprintf(stderr, "Cannot determine kernel physical load addr\n");
+ return -1;
+}
+
+/* Retrieve info regarding virtual address kernel has been compiled for and
+ * size of the kernel from /proc/kcore. Current /proc/kcore parsing from
+ * from kexec-tools fails because of malformed elf notes. A kernel patch has
+ * been submitted. For the folks using older kernels, this function
+ * hard codes the values to remain backward compatible. Once things stablize
+ * we should get rid of backward compatible code. */
+
+static int get_kernel_vaddr_and_size(struct kexec_info *UNUSED(info),
+ struct crash_elf_info *elf_info)
+{
+ int result;
+ const char kcore[] = "/proc/kcore";
+ char *buf;
+ struct mem_ehdr ehdr;
+ struct mem_phdr *phdr, *end_phdr;
+ int align;
+ off_t size;
+ uint32_t elf_flags = 0;
+ uint64_t stext_sym;
+ const unsigned long long pud_mask = ~((1 << 30) - 1);
+ unsigned long long vaddr, lowest_vaddr = 0;
+
+ if (elf_info->machine != EM_X86_64)
+ return 0;
+
+ if (xen_present()) /* Kernel not entity mapped under Xen */
+ return 0;
+
+ align = getpagesize();
+ buf = slurp_file_len(kcore, KCORE_ELF_HEADERS_SIZE, &size);
+ if (!buf) {
+ fprintf(stderr, "Cannot read %s: %s\n", kcore, strerror(errno));
+ return -1;
+ }
+
+ /* Don't perform checks to make sure stated phdrs and shdrs are
+ * actually present in the core file. It is not practical
+ * to read the GB size file into a user space buffer, Given the
+ * fact that we don't use any info from that.
+ */
+ elf_flags |= ELF_SKIP_FILESZ_CHECK;
+ result = build_elf_core_info(buf, size, &ehdr, elf_flags);
+ if (result < 0) {
+ /* Perhaps KCORE_ELF_HEADERS_SIZE is too small? */
+ fprintf(stderr, "ELF core (kcore) parse failed\n");
+ return -1;
+ }
+
+ end_phdr = &ehdr.e_phdr[ehdr.e_phnum];
+
+ /* Search for the real PAGE_OFFSET when KASLR memory randomization
+ * is enabled */
+ for(phdr = ehdr.e_phdr; phdr != end_phdr; phdr++) {
+ if (phdr->p_type == PT_LOAD) {
+ vaddr = phdr->p_vaddr & pud_mask;
+ if (lowest_vaddr == 0 || lowest_vaddr > vaddr)
+ lowest_vaddr = vaddr;
+ }
+ }
+ if (lowest_vaddr != 0)
+ elf_info->page_offset = lowest_vaddr;
+
+ /* Traverse through the Elf headers and find the region where
+ * _stext symbol is located in. That's where kernel is mapped */
+ stext_sym = get_kernel_sym("_stext");
+ for(phdr = ehdr.e_phdr; stext_sym && phdr != end_phdr; phdr++) {
+ if (phdr->p_type == PT_LOAD) {
+ unsigned long long saddr = phdr->p_vaddr;
+ unsigned long long eaddr = phdr->p_vaddr + phdr->p_memsz;
+ unsigned long long size;
+
+ /* Look for kernel text mapping header. */
+ if (saddr <= stext_sym && eaddr > stext_sym) {
+ saddr = _ALIGN_DOWN(saddr, X86_64_KERN_VADDR_ALIGN);
+ elf_info->kern_vaddr_start = saddr;
+ size = eaddr - saddr;
+ /* Align size to page size boundary. */
+ size = _ALIGN(size, align);
+ elf_info->kern_size = size;
+ dbgprintf("kernel vaddr = 0x%llx size = 0x%llx\n",
+ saddr, size);
+ return 0;
+ }
+ }
+ }
+
+ /* If failed to retrieve kernel text mapping through
+ * /proc/kallsyms, Traverse through the Elf headers again and
+ * find the region where kernel is mapped using hard-coded
+ * kernel mapping boundries */
+ for(phdr = ehdr.e_phdr; phdr != end_phdr; phdr++) {
+ if (phdr->p_type == PT_LOAD) {
+ unsigned long long saddr = phdr->p_vaddr;
+ unsigned long long eaddr = phdr->p_vaddr + phdr->p_memsz;
+ unsigned long long size;
+
+ /* Look for kernel text mapping header. */
+ if ((saddr >= X86_64__START_KERNEL_map) &&
+ (eaddr <= X86_64__START_KERNEL_map + X86_64_KERNEL_TEXT_SIZE)) {
+ saddr = _ALIGN_DOWN(saddr, X86_64_KERN_VADDR_ALIGN);
+ elf_info->kern_vaddr_start = saddr;
+ size = eaddr - saddr;
+ /* Align size to page size boundary. */
+ size = _ALIGN(size, align);
+ elf_info->kern_size = size;
+ dbgprintf("kernel vaddr = 0x%llx size = 0x%llx\n",
+ saddr, size);
+ return 0;
+ }
+ }
+ }
+
+ fprintf(stderr, "Can't find kernel text map area from kcore\n");
+ return -1;
+}
+
+/* Forward Declaration. */
+static void segregate_lowmem_region(int *nr_ranges, unsigned long lowmem_limit);
+static int exclude_region(int *nr_ranges, uint64_t start, uint64_t end);
+
+/* Stores a sorted list of RAM memory ranges for which to create elf headers.
+ * A separate program header is created for backup region */
+static struct memory_range crash_memory_range[CRASH_MAX_MEMORY_RANGES];
+
+/* Memory region reserved for storing panic kernel and other data. */
+#define CRASH_RESERVED_MEM_NR 8
+static struct memory_range crash_reserved_mem[CRASH_RESERVED_MEM_NR];
+static int crash_reserved_mem_nr;
+
+/* Reads the appropriate file and retrieves the SYSTEM RAM regions for whom to
+ * create Elf headers. Keeping it separate from get_memory_ranges() as
+ * requirements are different in the case of normal kexec and crashdumps.
+ *
+ * Normal kexec needs to look at all of available physical memory irrespective
+ * of the fact how much of it is being used by currently running kernel.
+ * Crashdumps need to have access to memory regions actually being used by
+ * running kernel. Expecting a different file/data structure than /proc/iomem
+ * to look into down the line. May be something like /proc/kernelmem or may
+ * be zone data structures exported from kernel.
+ */
+static int get_crash_memory_ranges(struct memory_range **range, int *ranges,
+ int kexec_flags, unsigned long lowmem_limit)
+{
+ const char *iomem = proc_iomem();
+ int memory_ranges = 0, gart = 0, i;
+ char line[MAX_LINE];
+ FILE *fp;
+ unsigned long long start, end;
+ uint64_t gart_start = 0, gart_end = 0;
+
+ fp = fopen(iomem, "r");
+ if (!fp) {
+ fprintf(stderr, "Cannot open %s: %s\n",
+ iomem, strerror(errno));
+ return -1;
+ }
+
+ while(fgets(line, sizeof(line), fp) != 0) {
+ char *str;
+ int type, consumed, count;
+
+ if (memory_ranges >= CRASH_MAX_MEMORY_RANGES)
+ break;
+ count = sscanf(line, "%llx-%llx : %n",
+ &start, &end, &consumed);
+ if (count != 2)
+ continue;
+ str = line + consumed;
+ dbgprintf("%016llx-%016llx : %s",
+ start, end, str);
+ /*
+ * We want to dump any System RAM -- memory regions currently
+ * used by the kernel. In the usual case, this is "System RAM"
+ * on the top level. However, we can also have "System RAM
+ * (virtio_mem)" below virtio devices or "System RAM (kmem)"
+ * below "Persistent Memory".
+ */
+ if (strstr(str, "System RAM")) {
+ type = RANGE_RAM;
+ } else if (memcmp(str, "ACPI Tables\n", 12) == 0) {
+ /*
+ * ACPI Tables area need to be passed to new
+ * kernel with appropriate memmap= option. This
+ * is needed so that x86_64 kernel creates linear
+ * mapping for this region which is required for
+ * initializing acpi tables in second kernel.
+ */
+ type = RANGE_ACPI;
+ } else if(memcmp(str,"ACPI Non-volatile Storage\n",26) == 0 ) {
+ type = RANGE_ACPI_NVS;
+ } else if(memcmp(str,"Persistent Memory (legacy)\n",27) == 0 ) {
+ type = RANGE_PRAM;
+ } else if(memcmp(str,"Persistent Memory\n",18) == 0 ) {
+ type = RANGE_PMEM;
+ } else if(memcmp(str,"reserved\n",9) == 0 ) {
+ type = RANGE_RESERVED;
+ } else if (memcmp(str, "Reserved\n", 9) == 0) {
+ type = RANGE_RESERVED;
+ } else if (memcmp(str, "GART\n", 5) == 0) {
+ gart_start = start;
+ gart_end = end;
+ gart = 1;
+ continue;
+ } else {
+ continue;
+ }
+
+ crash_memory_range[memory_ranges].start = start;
+ crash_memory_range[memory_ranges].end = end;
+ crash_memory_range[memory_ranges].type = type;
+
+ segregate_lowmem_region(&memory_ranges, lowmem_limit);
+
+ memory_ranges++;
+ }
+ fclose(fp);
+ if (kexec_flags & KEXEC_PRESERVE_CONTEXT) {
+ for (i = 0; i < memory_ranges; i++) {
+ if (crash_memory_range[i].end > 0x0009ffff) {
+ crash_reserved_mem[0].start = \
+ crash_memory_range[i].start;
+ break;
+ }
+ }
+ if (crash_reserved_mem[0].start >= mem_max) {
+ fprintf(stderr, "Too small mem_max: 0x%llx.\n",
+ mem_max);
+ return -1;
+ }
+ crash_reserved_mem[0].end = mem_max;
+ crash_reserved_mem[0].type = RANGE_RAM;
+ crash_reserved_mem_nr = 1;
+ }
+
+ for (i = 0; i < crash_reserved_mem_nr; i++)
+ if (exclude_region(&memory_ranges, crash_reserved_mem[i].start,
+ crash_reserved_mem[i].end) < 0)
+ return -1;
+
+ if (gart) {
+ /* exclude GART region if the system has one */
+ if (exclude_region(&memory_ranges, gart_start, gart_end) < 0)
+ return -1;
+ }
+ *range = crash_memory_range;
+ *ranges = memory_ranges;
+
+ return 0;
+}
+
+#ifdef HAVE_LIBXENCTRL
+static int get_crash_memory_ranges_xen(struct memory_range **range,
+ int *ranges, unsigned long lowmem_limit)
+{
+ struct e820entry *e820entries;
+ int j, rc, ret = -1;
+ unsigned int i;
+ xc_interface *xc;
+
+ xc = xc_interface_open(NULL, NULL, 0);
+
+ if (!xc) {
+ fprintf(stderr, "%s: Failed to open Xen control interface\n", __func__);
+ return -1;
+ }
+
+ e820entries = xmalloc(sizeof(*e820entries) * CRASH_MAX_MEMORY_RANGES);
+
+ rc = xc_get_machine_memory_map(xc, e820entries, CRASH_MAX_MEMORY_RANGES);
+
+ if (rc < 0) {
+ fprintf(stderr, "%s: xc_get_machine_memory_map: %s\n", __func__, strerror(-rc));
+ goto err;
+ }
+
+ for (i = 0, j = 0; i < rc && j < CRASH_MAX_MEMORY_RANGES; ++i, ++j) {
+ crash_memory_range[j].start = e820entries[i].addr;
+ crash_memory_range[j].end = e820entries[i].addr + e820entries[i].size - 1;
+ crash_memory_range[j].type = xen_e820_to_kexec_type(e820entries[i].type);
+ segregate_lowmem_region(&j, lowmem_limit);
+ }
+
+ *range = crash_memory_range;
+ *ranges = j;
+
+ qsort(*range, *ranges, sizeof(struct memory_range), compare_ranges);
+
+ for (i = 0; i < crash_reserved_mem_nr; i++)
+ if (exclude_region(ranges, crash_reserved_mem[i].start,
+ crash_reserved_mem[i].end) < 0)
+ goto err;
+
+ ret = 0;
+
+err:
+ xc_interface_close(xc);
+ free(e820entries);
+ return ret;
+}
+#else
+static int get_crash_memory_ranges_xen(struct memory_range **range,
+ int *ranges, unsigned long lowmem_limit)
+{
+ return 0;
+}
+#endif /* HAVE_LIBXENCTRL */
+
+static void segregate_lowmem_region(int *nr_ranges, unsigned long lowmem_limit)
+{
+ unsigned long long end, start;
+ unsigned type;
+
+ start = crash_memory_range[*nr_ranges].start;
+ end = crash_memory_range[*nr_ranges].end;
+ type = crash_memory_range[*nr_ranges].type;
+
+ if (!(lowmem_limit && lowmem_limit > start && lowmem_limit < end))
+ return;
+
+ crash_memory_range[*nr_ranges].end = lowmem_limit - 1;
+
+ if (*nr_ranges >= CRASH_MAX_MEMORY_RANGES - 1)
+ return;
+
+ ++*nr_ranges;
+
+ crash_memory_range[*nr_ranges].start = lowmem_limit;
+ crash_memory_range[*nr_ranges].end = end;
+ crash_memory_range[*nr_ranges].type = type;
+}
+
+/* Removes crash reserve region from list of memory chunks for whom elf program
+ * headers have to be created. Assuming crash reserve region to be a single
+ * continuous area fully contained inside one of the memory chunks */
+static int exclude_region(int *nr_ranges, uint64_t start, uint64_t end)
+{
+ int i, j, tidx = -1;
+ struct memory_range temp_region = {0, 0, 0};
+
+
+ for (i = 0; i < (*nr_ranges); i++) {
+ unsigned long long mstart, mend;
+ mstart = crash_memory_range[i].start;
+ mend = crash_memory_range[i].end;
+ if (start < mend && end > mstart) {
+ if (start != mstart && end != mend) {
+ /* Split memory region */
+ crash_memory_range[i].end = start - 1;
+ temp_region.start = end + 1;
+ temp_region.end = mend;
+ temp_region.type = RANGE_RAM;
+ tidx = i+1;
+ } else if (start != mstart)
+ crash_memory_range[i].end = start - 1;
+ else
+ crash_memory_range[i].start = end + 1;
+ }
+ }
+ /* Insert split memory region, if any. */
+ if (tidx >= 0) {
+ if (*nr_ranges == CRASH_MAX_MEMORY_RANGES) {
+ /* No space to insert another element. */
+ fprintf(stderr, "Error: Number of crash memory ranges"
+ " excedeed the max limit\n");
+ return -1;
+ }
+ for (j = (*nr_ranges - 1); j >= tidx; j--)
+ crash_memory_range[j+1] = crash_memory_range[j];
+ crash_memory_range[tidx] = temp_region;
+ (*nr_ranges)++;
+ }
+ return 0;
+}
+
+/* Adds a segment from list of memory regions which new kernel can use to
+ * boot. Segment start and end should be aligned to 1K boundary. */
+static int add_memmap(struct memory_range *memmap_p, int *nr_memmap,
+ unsigned long long addr, size_t size, int type)
+{
+ int i, j, nr_entries = 0, tidx = 0, align = 1024;
+ unsigned long long mstart, mend;
+
+ /* Shrink to 1KiB alignment if needed. */
+ if (type == RANGE_RAM && ((addr%align) || (size%align))) {
+ unsigned long long end = addr + size;
+
+ printf("%s: RAM chunk %#llx - %#llx unaligned\n", __func__, addr, end);
+ addr = _ALIGN_UP(addr, align);
+ end = _ALIGN_DOWN(end, align);
+ if (addr >= end)
+ return -1;
+ size = end - addr;
+ printf("%s: RAM chunk shrunk to %#llx - %#llx\n", __func__, addr, end);
+ }
+
+ /* Make sure at least one entry in list is free. */
+ for (i = 0; i < CRASH_MAX_MEMMAP_NR; i++) {
+ mstart = memmap_p[i].start;
+ mend = memmap_p[i].end;
+ if (!mstart && !mend)
+ break;
+ else
+ nr_entries++;
+ }
+ if (nr_entries == CRASH_MAX_MEMMAP_NR)
+ return -1;
+
+ for (i = 0; i < CRASH_MAX_MEMMAP_NR; i++) {
+ mstart = memmap_p[i].start;
+ mend = memmap_p[i].end;
+ if (mstart == 0 && mend == 0)
+ break;
+ if (mstart <= (addr+size-1) && mend >=addr)
+ /* Overlapping region. */
+ return -1;
+ else if (addr > mend)
+ tidx = i+1;
+ }
+ /* Insert the memory region. */
+ for (j = nr_entries-1; j >= tidx; j--)
+ memmap_p[j+1] = memmap_p[j];
+ memmap_p[tidx].start = addr;
+ memmap_p[tidx].end = addr + size - 1;
+ memmap_p[tidx].type = type;
+ *nr_memmap = nr_entries + 1;
+
+ dbgprint_mem_range("Memmap after adding segment", memmap_p, *nr_memmap);
+
+ return 0;
+}
+
+/* Removes a segment from list of memory regions which new kernel can use to
+ * boot. Segment start and end should be aligned to 1K boundary. */
+static int delete_memmap(struct memory_range *memmap_p, int *nr_memmap,
+ unsigned long long addr, size_t size)
+{
+ int i, j, nr_entries = 0, tidx = -1, operation = 0, align = 1024;
+ unsigned long long mstart, mend;
+ struct memory_range temp_region;
+
+ /* Do alignment check. */
+ if ((addr%align) || (size%align))
+ return -1;
+
+ /* Make sure at least one entry in list is free. */
+ for (i = 0; i < CRASH_MAX_MEMMAP_NR; i++) {
+ mstart = memmap_p[i].start;
+ mend = memmap_p[i].end;
+ if (!mstart && !mend)
+ break;
+ else
+ nr_entries++;
+ }
+ if (nr_entries == CRASH_MAX_MEMMAP_NR)
+ /* List if full */
+ return -1;
+
+ for (i = 0; i < CRASH_MAX_MEMMAP_NR; i++) {
+ mstart = memmap_p[i].start;
+ mend = memmap_p[i].end;
+ if (mstart == 0 && mend == 0)
+ /* Did not find the segment in the list. */
+ return -1;
+ if (mstart <= addr && mend >= (addr + size - 1)) {
+ if (mstart == addr && mend == (addr + size - 1)) {
+ /* Exact match. Delete region */
+ operation = -1;
+ tidx = i;
+ break;
+ }
+ if (mstart != addr && mend != (addr + size - 1)) {
+ /* Split in two */
+ memmap_p[i].end = addr - 1;
+ temp_region.start = addr + size;
+ temp_region.end = mend;
+ temp_region.type = memmap_p[i].type;
+ operation = 1;
+ tidx = i;
+ break;
+ }
+
+ /* No addition/deletion required. Adjust the existing.*/
+ if (mstart != addr) {
+ memmap_p[i].end = addr - 1;
+ break;
+ } else {
+ memmap_p[i].start = addr + size;
+ break;
+ }
+ }
+ }
+ if ((operation == 1) && tidx >=0) {
+ /* Insert the split memory region. */
+ for (j = nr_entries-1; j > tidx; j--)
+ memmap_p[j+1] = memmap_p[j];
+ memmap_p[tidx+1] = temp_region;
+ *nr_memmap = nr_entries + 1;
+ }
+ if ((operation == -1) && tidx >=0) {
+ /* Delete the exact match memory region. */
+ for (j = i+1; j < CRASH_MAX_MEMMAP_NR; j++)
+ memmap_p[j-1] = memmap_p[j];
+ memmap_p[j-1].start = memmap_p[j-1].end = 0;
+ *nr_memmap = nr_entries - 1;
+ }
+
+ dbgprint_mem_range("Memmap after deleting segment", memmap_p, *nr_memmap);
+
+ return 0;
+}
+
+static void cmdline_add_memmap_internal(char *cmdline, unsigned long startk,
+ unsigned long endk, int type)
+{
+ int cmdlen, len;
+ char str_mmap[256], str_tmp[20];
+
+ strcpy (str_mmap, " memmap=");
+ ultoa((endk-startk), str_tmp);
+ strcat (str_mmap, str_tmp);
+
+ if (type == RANGE_RAM)
+ strcat (str_mmap, "K@");
+ else if (type == RANGE_RESERVED)
+ strcat (str_mmap, "K$");
+ else if (type == RANGE_ACPI || type == RANGE_ACPI_NVS)
+ strcat (str_mmap, "K#");
+ else if (type == RANGE_PRAM)
+ strcat (str_mmap, "K!");
+
+ ultoa(startk, str_tmp);
+ strcat (str_mmap, str_tmp);
+ strcat (str_mmap, "K");
+ len = strlen(str_mmap);
+ cmdlen = strlen(cmdline) + len;
+ if (cmdlen > (COMMAND_LINE_SIZE - 1))
+ die("Command line overflow\n");
+ strcat(cmdline, str_mmap);
+}
+
+/* Adds the appropriate memmap= options to command line, indicating the
+ * memory regions the new kernel can use to boot into. */
+static int cmdline_add_memmap(char *cmdline, struct memory_range *memmap_p)
+{
+ int i, cmdlen, len;
+ unsigned long min_sizek = 100;
+ char str_mmap[256];
+
+ /* Exact map */
+ strcpy(str_mmap, " memmap=exactmap");
+ len = strlen(str_mmap);
+ cmdlen = strlen(cmdline) + len;
+ if (cmdlen > (COMMAND_LINE_SIZE - 1))
+ die("Command line overflow\n");
+ strcat(cmdline, str_mmap);
+
+ for (i = 0; i < CRASH_MAX_MEMMAP_NR; i++) {
+ unsigned long startk, endk, type;
+
+ startk = memmap_p[i].start/1024;
+ endk = (memmap_p[i].end + 1)/1024;
+ type = memmap_p[i].type;
+
+ /* Only adding memory regions of RAM and ACPI and Persistent Mem */
+ if (type != RANGE_RAM &&
+ type != RANGE_ACPI &&
+ type != RANGE_ACPI_NVS &&
+ type != RANGE_PRAM)
+ continue;
+
+ if (type == RANGE_ACPI || type == RANGE_ACPI_NVS)
+ endk = _ALIGN_UP(memmap_p[i].end + 1, 1024)/1024;
+
+ if (!startk && !endk)
+ /* All regions traversed. */
+ break;
+
+ /* A RAM region is not worth adding if region size < 100K.
+ * It eats up precious command line length. */
+ if (type == RANGE_RAM && (endk - startk) < min_sizek)
+ continue;
+ /* And do not add e820 reserved region either */
+ cmdline_add_memmap_internal(cmdline, startk, endk, type);
+ }
+
+ dbgprintf("Command line after adding memmap\n");
+ dbgprintf("%s\n", cmdline);
+
+ return 0;
+}
+
+/* Adds the elfcorehdr= command line parameter to command line. */
+static int cmdline_add_elfcorehdr(char *cmdline, unsigned long addr)
+{
+ int cmdlen, len, align = 1024;
+ char str[30], *ptr;
+
+ /* Passing in elfcorehdr=xxxK format. Saves space required in cmdline.
+ * Ensure 1K alignment*/
+ if (addr%align)
+ return -1;
+ addr = addr/align;
+ ptr = str;
+ strcpy(str, " elfcorehdr=");
+ ptr += strlen(str);
+ ultoa(addr, ptr);
+ strcat(str, "K");
+ len = strlen(str);
+ cmdlen = strlen(cmdline) + len;
+ if (cmdlen > (COMMAND_LINE_SIZE - 1))
+ die("Command line overflow\n");
+ strcat(cmdline, str);
+
+ dbgprintf("Command line after adding elfcorehdr\n");
+ dbgprintf("%s\n", cmdline);
+
+ return 0;
+}
+
+
+/*
+ * This routine is specific to i386 architecture to maintain the
+ * backward compatibility, other architectures can use the per
+ * cpu version get_crash_notes_per_cpu() directly.
+ */
+static int get_crash_notes(int cpu, uint64_t *addr, uint64_t *len)
+{
+ const char *crash_notes = "/sys/kernel/crash_notes";
+ char line[MAX_LINE];
+ FILE *fp;
+ unsigned long vaddr;
+ int count;
+
+ fp = fopen(crash_notes, "r");
+ if (fp) {
+ if (fgets(line, sizeof(line), fp) != 0) {
+ count = sscanf(line, "%lx", &vaddr);
+ if (count != 1)
+ die("Cannot parse %s: %s\n", crash_notes,
+ strerror(errno));
+ }
+
+ *addr = x86__pa(vaddr + (cpu * MAX_NOTE_BYTES));
+ *len = MAX_NOTE_BYTES;
+
+ dbgprintf("crash_notes addr = %llx\n",
+ (unsigned long long)*addr);
+
+ fclose(fp);
+ return 0;
+ } else
+ return get_crash_notes_per_cpu(cpu, addr, len);
+}
+
+static enum coretype get_core_type(struct crash_elf_info *elf_info,
+ struct memory_range *range, int ranges)
+{
+ if ((elf_info->machine) == EM_X86_64)
+ return CORE_TYPE_ELF64;
+ else {
+ /* fall back to default */
+ if (ranges == 0)
+ return CORE_TYPE_ELF64;
+
+ if (range[ranges - 1].end > 0xFFFFFFFFUL)
+ return CORE_TYPE_ELF64;
+ else
+ return CORE_TYPE_ELF32;
+ }
+}
+
+static int sysfs_efi_runtime_map_exist(void)
+{
+ DIR *dir;
+
+ dir = opendir("/sys/firmware/efi/runtime-map");
+ if (!dir)
+ return 0;
+
+ closedir(dir);
+ return 1;
+}
+
+/* Appends 'acpi_rsdp=' commandline for efi boot crash dump */
+static void cmdline_add_efi(char *cmdline)
+{
+ uint64_t acpi_rsdp;
+ char acpi_rsdp_buf[MAX_LINE];
+
+ acpi_rsdp = get_acpi_rsdp();
+
+ if (!acpi_rsdp)
+ return;
+
+ sprintf(acpi_rsdp_buf, " acpi_rsdp=0x%lx", acpi_rsdp);
+ if (strlen(cmdline) + strlen(acpi_rsdp_buf) > (COMMAND_LINE_SIZE - 1))
+ die("Command line overflow\n");
+
+ strcat(cmdline, acpi_rsdp_buf);
+}
+
+static void get_backup_area(struct kexec_info *info,
+ struct memory_range *range, int ranges)
+{
+ int i;
+
+ /* Look for first 640 KiB RAM region. */
+ for (i = 0; i < ranges; ++i) {
+ if (range[i].type != RANGE_RAM || range[i].end > 0xa0000)
+ continue;
+
+ info->backup_src_start = range[i].start;
+ info->backup_src_size = range[i].end - range[i].start + 1;
+
+ dbgprintf("%s: %016llx-%016llx : System RAM\n", __func__,
+ range[i].start, range[i].end);
+
+ return;
+ }
+
+ /* First 640 KiB RAM region not found. Assume defaults. */
+ info->backup_src_start = BACKUP_SRC_START;
+ info->backup_src_size = BACKUP_SRC_END - BACKUP_SRC_START + 1;
+}
+
+/* Loads additional segments in case of a panic kernel is being loaded.
+ * One segment for backup region, another segment for storing elf headers
+ * for crash memory image.
+ */
+int load_crashdump_segments(struct kexec_info *info, char* mod_cmdline,
+ unsigned long max_addr, unsigned long min_base)
+{
+ void *tmp;
+ unsigned long sz, bufsz, memsz, elfcorehdr;
+ int nr_ranges = 0, nr_memmap = 0, align = 1024, i;
+ struct memory_range *mem_range, *memmap_p;
+ struct crash_elf_info elf_info;
+ unsigned kexec_arch;
+
+ memset(&elf_info, 0x0, sizeof(elf_info));
+
+ /* Constant parts of the elf_info */
+ memset(&elf_info, 0, sizeof(elf_info));
+ elf_info.data = ELFDATA2LSB;
+
+ /* Get the architecture of the running kernel */
+ kexec_arch = info->kexec_flags & KEXEC_ARCH_MASK;
+ if (kexec_arch == KEXEC_ARCH_DEFAULT)
+ kexec_arch = KEXEC_ARCH_NATIVE;
+
+ /* Get the elf architecture of the running kernel */
+ switch(kexec_arch) {
+ case KEXEC_ARCH_X86_64:
+ elf_info.machine = EM_X86_64;
+ break;
+ case KEXEC_ARCH_386:
+ elf_info.machine = EM_386;
+ elf_info.lowmem_limit = X86_MAXMEM;
+ elf_info.get_note_info = get_crash_notes;
+ break;
+ default:
+ fprintf(stderr, "unsupported crashdump architecture: %04x\n",
+ kexec_arch);
+ return -1;
+ }
+
+ if (xen_present()) {
+ if (get_crash_memory_ranges_xen(&mem_range, &nr_ranges,
+ elf_info.lowmem_limit) < 0)
+ return -1;
+ } else
+ if (get_crash_memory_ranges(&mem_range, &nr_ranges,
+ info->kexec_flags,
+ elf_info.lowmem_limit) < 0)
+ return -1;
+
+ get_backup_area(info, mem_range, nr_ranges);
+
+ dbgprint_mem_range("CRASH MEMORY RANGES", mem_range, nr_ranges);
+
+ /*
+ * if the core type has not been set on command line, set it here
+ * automatically
+ */
+ if (arch_options.core_header_type == CORE_TYPE_UNDEF) {
+ arch_options.core_header_type =
+ get_core_type(&elf_info, mem_range, nr_ranges);
+ }
+ /* Get the elf class... */
+ elf_info.class = ELFCLASS32;
+ if (arch_options.core_header_type == CORE_TYPE_ELF64) {
+ elf_info.class = ELFCLASS64;
+ }
+
+ if (get_kernel_page_offset(info, &elf_info))
+ return -1;
+
+ if (get_kernel_paddr(info, &elf_info))
+ return -1;
+
+ if (get_kernel_vaddr_and_size(info, &elf_info))
+ return -1;
+
+ /* Memory regions which panic kernel can safely use to boot into */
+ sz = (sizeof(struct memory_range) * CRASH_MAX_MEMMAP_NR);
+ memmap_p = xmalloc(sz);
+ memset(memmap_p, 0, sz);
+ add_memmap(memmap_p, &nr_memmap, info->backup_src_start, info->backup_src_size, RANGE_RAM);
+ for (i = 0; i < crash_reserved_mem_nr; i++) {
+ sz = crash_reserved_mem[i].end - crash_reserved_mem[i].start +1;
+ if (add_memmap(memmap_p, &nr_memmap, crash_reserved_mem[i].start,
+ sz, RANGE_RAM) < 0) {
+ free(memmap_p);
+ return ENOCRASHKERNEL;
+ }
+ }
+
+ /* Create a backup region segment to store backup data*/
+ if (!(info->kexec_flags & KEXEC_PRESERVE_CONTEXT)) {
+ sz = _ALIGN(info->backup_src_size, align);
+ tmp = xmalloc(sz);
+ memset(tmp, 0, sz);
+ info->backup_start = add_buffer(info, tmp, sz, sz, align,
+ 0, max_addr, -1);
+ dbgprintf("Created backup segment at 0x%lx\n",
+ info->backup_start);
+ if (delete_memmap(memmap_p, &nr_memmap, info->backup_start, sz) < 0) {
+ free(tmp);
+ free(memmap_p);
+ return EFAILED;
+ }
+ }
+
+ /* Create elf header segment and store crash image data. */
+ if (arch_options.core_header_type == CORE_TYPE_ELF64) {
+ if (crash_create_elf64_headers(info, &elf_info, mem_range,
+ nr_ranges, &tmp, &bufsz,
+ ELF_CORE_HEADER_ALIGN) < 0) {
+ free(memmap_p);
+ return EFAILED;
+ }
+ }
+ else {
+ if (crash_create_elf32_headers(info, &elf_info, mem_range,
+ nr_ranges, &tmp, &bufsz,
+ ELF_CORE_HEADER_ALIGN) < 0) {
+ free(memmap_p);
+ return EFAILED;
+ }
+ }
+ /* the size of the elf headers allocated is returned in 'bufsz' */
+
+ /* Hack: With some ld versions (GNU ld version 2.14.90.0.4 20030523),
+ * vmlinux program headers show a gap of two pages between bss segment
+ * and data segment but effectively kernel considers it as bss segment
+ * and overwrites the any data placed there. Hence bloat the memsz of
+ * elf core header segment to 16K to avoid being placed in such gaps.
+ * This is a makeshift solution until it is fixed in kernel.
+ */
+ if (bufsz < (16*1024)) {
+ /* bufsize is big enough for all the PT_NOTE's and PT_LOAD's */
+ memsz = 16*1024;
+ /* memsz will be the size of the memory hole we look for */
+ } else {
+ memsz = bufsz;
+ }
+ elfcorehdr = add_buffer(info, tmp, bufsz, memsz, align, min_base,
+ max_addr, -1);
+ dbgprintf("Created elf header segment at 0x%lx\n", elfcorehdr);
+ if (delete_memmap(memmap_p, &nr_memmap, elfcorehdr, memsz) < 0) {
+ free(memmap_p);
+ return -1;
+ }
+ if (!bzImage_support_efi_boot || arch_options.noefi ||
+ !sysfs_efi_runtime_map_exist())
+ cmdline_add_efi(mod_cmdline);
+ cmdline_add_elfcorehdr(mod_cmdline, elfcorehdr);
+
+ /* Inform second kernel about the presence of ACPI tables. */
+ for (i = 0; i < nr_ranges; i++) {
+ unsigned long start, end, size, type;
+ if ( !( mem_range[i].type == RANGE_ACPI
+ || mem_range[i].type == RANGE_ACPI_NVS
+ || mem_range[i].type == RANGE_RESERVED
+ || mem_range[i].type == RANGE_PMEM
+ || mem_range[i].type == RANGE_PRAM))
+ continue;
+ start = mem_range[i].start;
+ end = mem_range[i].end;
+ type = mem_range[i].type;
+ size = end - start + 1;
+ add_memmap(memmap_p, &nr_memmap, start, size, type);
+ }
+
+ if (arch_options.pass_memmap_cmdline)
+ cmdline_add_memmap(mod_cmdline, memmap_p);
+
+ /* Store 2nd kernel boot memory ranges for later reference in
+ * x86-setup-linux.c: setup_linux_system_parameters() */
+ info->crash_range = memmap_p;
+ info->nr_crash_ranges = nr_memmap;
+
+ return 0;
+}
+
+/* On x86, the kernel may make a low reservation in addition to the
+ * normal reservation. However, the kernel refuses to load the panic
+ * kernel to low memory, so always choose the highest range.
+ */
+int get_crash_kernel_load_range(uint64_t *start, uint64_t *end)
+{
+ if (!crash_reserved_mem_nr)
+ return -1;
+
+ *start = crash_reserved_mem[crash_reserved_mem_nr - 1].start;
+ *end = crash_reserved_mem[crash_reserved_mem_nr - 1].end;
+
+ return 0;
+}
+
+static int crashkernel_mem_callback(void *UNUSED(data), int nr,
+ char *UNUSED(str),
+ unsigned long long base,
+ unsigned long long length)
+{
+ if (nr >= CRASH_RESERVED_MEM_NR)
+ return 1;
+
+ crash_reserved_mem[nr].start = base;
+ crash_reserved_mem[nr].end = base + length - 1;
+ crash_reserved_mem[nr].type = RANGE_RAM;
+ return 0;
+}
+
+int is_crashkernel_mem_reserved(void)
+{
+ int ret;
+
+ if (xen_present()) {
+ uint64_t start, end;
+
+ ret = xen_get_crashkernel_region(&start, &end);
+ if (ret < 0)
+ return 0;
+
+ crash_reserved_mem[0].start = start;
+ crash_reserved_mem[0].end = end;
+ crash_reserved_mem[0].type = RANGE_RAM;
+ crash_reserved_mem_nr = 1;
+ } else {
+ ret = kexec_iomem_for_each_line("Crash kernel\n",
+ crashkernel_mem_callback, NULL);
+ crash_reserved_mem_nr = ret;
+ }
+
+ return !!crash_reserved_mem_nr;
+}
diff --git a/kexec/arch/i386/crashdump-x86.h b/kexec/arch/i386/crashdump-x86.h
new file mode 100644
index 0000000..479a549
--- /dev/null
+++ b/kexec/arch/i386/crashdump-x86.h
@@ -0,0 +1,33 @@
+#ifndef CRASHDUMP_X86_H
+#define CRASHDUMP_X86_H
+
+struct kexec_info;
+int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline,
+ unsigned long max_addr, unsigned long min_base);
+
+#define X86_PAGE_OFFSET 0xc0000000
+#define x86__pa(x) ((unsigned long)(x)-X86_PAGE_OFFSET)
+
+#define X86__VMALLOC_RESERVE (128 << 20)
+#define X86_MAXMEM (-X86_PAGE_OFFSET-X86__VMALLOC_RESERVE)
+
+#define X86_64__START_KERNEL_map 0xffffffff80000000ULL
+#define X86_64_PAGE_OFFSET_PRE_2_6_27 0xffff810000000000ULL
+#define X86_64_PAGE_OFFSET_PRE_4_20_0 0xffff880000000000ULL
+#define X86_64_PAGE_OFFSET 0xffff888000000000ULL
+
+#define X86_64_MAXMEM 0x3fffffffffffUL
+
+/* Kernel text size */
+#define X86_64_KERNEL_TEXT_SIZE (512UL*1024*1024)
+
+#define CRASH_MAX_MEMMAP_NR 1024
+
+#define CRASH_MAX_MEMORY_RANGES 32768
+
+/* Backup Region, First 640K of System RAM. */
+#define BACKUP_SRC_START 0x00000000
+#define BACKUP_SRC_END 0x0009ffff
+#define BACKUP_SRC_SIZE (BACKUP_SRC_END - BACKUP_SRC_START + 1)
+
+#endif /* CRASHDUMP_X86_H */
diff --git a/kexec/arch/i386/include/arch/options.h b/kexec/arch/i386/include/arch/options.h
new file mode 100644
index 0000000..89e0a95
--- /dev/null
+++ b/kexec/arch/i386/include/arch/options.h
@@ -0,0 +1,86 @@
+#ifndef KEXEC_ARCH_I386_OPTIONS_H
+#define KEXEC_ARCH_I386_OPTIONS_H
+
+/*
+ *************************************************************************
+ * NOTE NOTE NOTE
+ * This file is included for i386 builds *and* x86_64 builds (which build
+ * both x86_64 and i386 loaders).
+ * It contains the combined set of options used by i386 and x86_64.
+ *************************************************************************
+ */
+
+#define OPT_RESET_VGA (OPT_MAX+0)
+#define OPT_SERIAL (OPT_MAX+1)
+#define OPT_SERIAL_BAUD (OPT_MAX+2)
+#define OPT_CONSOLE_VGA (OPT_MAX+3)
+#define OPT_CONSOLE_SERIAL (OPT_MAX+4)
+#define OPT_ELF32_CORE (OPT_MAX+5)
+#define OPT_ELF64_CORE (OPT_MAX+6)
+#define OPT_ARCH_MAX (OPT_MAX+7)
+
+#define OPT_APPEND (OPT_ARCH_MAX+0)
+#define OPT_REUSE_CMDLINE (OPT_ARCH_MAX+1)
+#define OPT_RAMDISK (OPT_ARCH_MAX+2)
+#define OPT_ARGS_ELF (OPT_ARCH_MAX+3)
+#define OPT_ARGS_LINUX (OPT_ARCH_MAX+4)
+#define OPT_ARGS_NONE (OPT_ARCH_MAX+5)
+#define OPT_CL (OPT_ARCH_MAX+6)
+#define OPT_MOD (OPT_ARCH_MAX+7)
+#define OPT_VGA (OPT_ARCH_MAX+8)
+#define OPT_REAL_MODE (OPT_ARCH_MAX+9)
+#define OPT_ENTRY_32BIT (OPT_ARCH_MAX+10)
+#define OPT_PASS_MEMMAP_CMDLINE (OPT_ARCH_MAX+11)
+#define OPT_NOEFI (OPT_ARCH_MAX+12)
+#define OPT_REUSE_VIDEO_TYPE (OPT_ARCH_MAX+13)
+#define OPT_DTB (OPT_ARCH_MAX+14)
+
+/* Options relevant to the architecture (excluding loader-specific ones): */
+#define KEXEC_ARCH_OPTIONS \
+ KEXEC_OPTIONS \
+ { "reset-vga", 0, 0, OPT_RESET_VGA }, \
+ { "serial", 1, 0, OPT_SERIAL }, \
+ { "serial-baud", 1, 0, OPT_SERIAL_BAUD }, \
+ { "console-vga", 0, 0, OPT_CONSOLE_VGA }, \
+ { "console-serial", 0, 0, OPT_CONSOLE_SERIAL }, \
+ { "elf32-core-headers", 0, 0, OPT_ELF32_CORE }, \
+ { "elf64-core-headers", 0, 0, OPT_ELF64_CORE }, \
+ { "pass-memmap-cmdline", 0, 0, OPT_PASS_MEMMAP_CMDLINE }, \
+ { "noefi", 0, 0, OPT_NOEFI}, \
+ { "reuse-video-type", 0, 0, OPT_REUSE_VIDEO_TYPE }, \
+
+#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
+
+/* The following two #defines list ALL of the options added by all of the
+ * architecture's loaders.
+ * o main() uses this complete list to scan for its options, ignoring
+ * arch-specific/loader-specific ones.
+ * o Then, arch_process_options() uses this complete list to scan for its
+ * options, ignoring general/loader-specific ones.
+ * o Then, the file_type[n].load re-scans for options, using
+ * KEXEC_ARCH_OPTIONS plus its loader-specific options subset.
+ * Any unrecognised options cause an error here.
+ *
+ * This is done so that main()'s/arch_process_options()'s getopt_long() calls
+ * don't choose a kernel filename from random arguments to options they don't
+ * recognise -- as they now recognise (if not act upon) all possible options.
+ */
+#define KEXEC_ALL_OPTIONS \
+ KEXEC_ARCH_OPTIONS \
+ { "command-line", 1, NULL, OPT_APPEND }, \
+ { "append", 1, NULL, OPT_APPEND }, \
+ { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE }, \
+ { "initrd", 1, NULL, OPT_RAMDISK }, \
+ { "ramdisk", 1, NULL, OPT_RAMDISK }, \
+ { "args-elf", 0, NULL, OPT_ARGS_ELF }, \
+ { "args-linux", 0, NULL, OPT_ARGS_LINUX }, \
+ { "args-none", 0, NULL, OPT_ARGS_NONE }, \
+ { "module", 1, 0, OPT_MOD }, \
+ { "real-mode", 0, NULL, OPT_REAL_MODE }, \
+ { "entry-32bit", 0, NULL, OPT_ENTRY_32BIT }, \
+ { "dtb", 1, NULL, OPT_DTB },
+
+#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR
+
+#endif /* KEXEC_ARCH_I386_OPTIONS_H */
+
diff --git a/kexec/arch/i386/kexec-beoboot-x86.c b/kexec/arch/i386/kexec-beoboot-x86.c
new file mode 100644
index 0000000..d949ab8
--- /dev/null
+++ b/kexec/arch/i386/kexec-beoboot-x86.c
@@ -0,0 +1,132 @@
+/*------------------------------------------------------------ -*- C -*-
+ * Eric Biederman <ebiederman@xmission.com>
+ * Erik Arjan Hendriks <hendriks@lanl.gov>
+ *
+ * 14 December 2004
+ * This file is a derivative of the beoboot image loader, modified
+ * to work with kexec.
+ *
+ * This version is derivative from the orignal mkbootimg.c which is
+ * Copyright (C) 2000 Scyld Computing Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+
+ *
+ *--------------------------------------------------------------------*/
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <x86/x86-linux.h>
+#include <boot/beoboot.h>
+#include "../../kexec.h"
+#include "kexec-x86.h"
+#include <arch/options.h>
+
+int beoboot_probe(const char *buf, off_t len)
+{
+ struct beoboot_header bb_header;
+ const char *cmdline, *kernel;
+ int result;
+ if ((uintmax_t)len < (uintmax_t)sizeof(bb_header)) {
+ return -1;
+ }
+ memcpy(&bb_header, buf, sizeof(bb_header));
+ if (memcmp(bb_header.magic, BEOBOOT_MAGIC, 4) != 0) {
+ return -1;
+ }
+ if (bb_header.arch != BEOBOOT_ARCH) {
+ return -1;
+ }
+ /* Make certain a bzImage is packed into there.
+ */
+ cmdline = buf + sizeof(bb_header);
+ kernel = cmdline + bb_header.cmdline_size;
+ result = bzImage_probe(kernel, bb_header.kernel_size);
+
+ return result;
+}
+
+void beoboot_usage(void)
+{
+ printf( " --real-mode Use the kernels real mode entry point.\n"
+ );
+
+ /* No parameters are parsed */
+}
+
+#define SETUP_BASE 0x90000
+#define KERN32_BASE 0x100000 /* 1MB */
+#define INITRD_BASE 0x1000000 /* 16MB */
+
+int beoboot_load(int argc, char **argv, const char *buf, off_t UNUSED(len),
+ struct kexec_info *info)
+{
+ struct beoboot_header bb_header;
+ const char *command_line, *kernel, *initrd;
+
+ int real_mode_entry;
+ int opt;
+ int result;
+
+ /* See options.h -- add any more there, too. */
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { "real-mode", 0, 0, OPT_REAL_MODE },
+ { 0, 0, 0, 0 },
+ };
+ static const char short_options[] = KEXEC_ARCH_OPT_STR "";
+
+ /*
+ * Parse the command line arguments
+ */
+ real_mode_entry = 0;
+ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch(opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ case OPT_REAL_MODE:
+ real_mode_entry = 1;
+ break;
+ }
+ }
+
+
+ /*
+ * Parse the file
+ */
+ memcpy(&bb_header, buf, sizeof(bb_header));
+ command_line = buf + sizeof(bb_header);
+ kernel = command_line + bb_header.cmdline_size;
+ initrd = NULL;
+ if (bb_header.flags & BEOBOOT_INITRD_PRESENT) {
+ initrd = kernel + bb_header.kernel_size;
+ }
+
+ result = do_bzImage_load(info,
+ kernel, bb_header.kernel_size,
+ command_line, bb_header.cmdline_size,
+ initrd, bb_header.initrd_size,
+ 0, 0, real_mode_entry);
+
+ return result;
+}
+
diff --git a/kexec/arch/i386/kexec-bzImage.c b/kexec/arch/i386/kexec-bzImage.c
new file mode 100644
index 0000000..1b8f20c
--- /dev/null
+++ b/kexec/arch/i386/kexec-bzImage.c
@@ -0,0 +1,471 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2003-2010 Eric Biederman (ebiederm@xmission.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <elf.h>
+#include <boot/elf_boot.h>
+#include <ip_checksum.h>
+#include <x86/x86-linux.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-syscall.h"
+#include "kexec-x86.h"
+#include "x86-linux-setup.h"
+#include "crashdump-x86.h"
+#include <arch/options.h>
+
+static const int probe_debug = 0;
+int bzImage_support_efi_boot = 0;
+
+int bzImage_probe(const char *buf, off_t len)
+{
+ const struct x86_linux_header *header;
+ if ((uintmax_t)len < (uintmax_t)(2 * 512)) {
+ if (probe_debug) {
+ fprintf(stderr, "File is too short to be a bzImage!\n");
+ }
+ return -1;
+ }
+ header = (const struct x86_linux_header *)buf;
+ if (memcmp(header->header_magic, "HdrS", 4) != 0) {
+ if (probe_debug) {
+ fprintf(stderr, "Not a bzImage\n");
+ }
+ return -1;
+ }
+ if (header->boot_sector_magic != 0xAA55) {
+ if (probe_debug) {
+ fprintf(stderr, "No x86 boot sector present\n");
+ }
+ /* No x86 boot sector present */
+ return -1;
+ }
+ if (header->protocol_version < 0x0200) {
+ if (probe_debug) {
+ fprintf(stderr, "Must be at least protocol version 2.00\n");
+ }
+ /* Must be at least protocol version 2.00 */
+ return -1;
+ }
+ if ((header->loadflags & 1) == 0) {
+ if (probe_debug) {
+ fprintf(stderr, "zImage not a bzImage\n");
+ }
+ /* Not a bzImage */
+ return -1;
+ }
+ /* I've got a bzImage */
+ if (probe_debug) {
+ fprintf(stderr, "It's a bzImage\n");
+ }
+ return 0;
+}
+
+
+void bzImage_usage(void)
+{
+ printf( " --real-mode Use the kernels real mode entry point.\n"
+ " --command-line=STRING Set the kernel command line to STRING.\n"
+ " --append=STRING Set the kernel command line to STRING.\n"
+ " --reuse-cmdline Use kernel command line from running system.\n"
+ " --initrd=FILE Use FILE as the kernel's initial ramdisk.\n"
+ " --ramdisk=FILE Use FILE as the kernel's initial ramdisk.\n"
+ " --dtb=FILE Use FILE as devicetree.\n"
+ );
+
+}
+
+int do_bzImage_load(struct kexec_info *info,
+ const char *kernel, off_t kernel_len,
+ const char *command_line, off_t command_line_len,
+ const char *initrd, off_t initrd_len,
+ const char *dtb, off_t dtb_len,
+ int real_mode_entry)
+{
+ struct x86_linux_header setup_header;
+ struct x86_linux_param_header *real_mode;
+ int setup_sects;
+ size_t size;
+ int kern16_size;
+ unsigned long setup_base, setup_size;
+ struct entry32_regs regs32;
+ struct entry16_regs regs16;
+ unsigned int relocatable_kernel = 0;
+ unsigned long kernel32_load_addr;
+ char *modified_cmdline;
+ unsigned long cmdline_end;
+ unsigned long kern16_size_needed;
+ unsigned long heap_size = 0;
+
+ /*
+ * Find out about the file I am about to load.
+ */
+ if ((uintmax_t)kernel_len < (uintmax_t)(2 * 512)) {
+ return -1;
+ }
+ memcpy(&setup_header, kernel, sizeof(setup_header));
+ setup_sects = setup_header.setup_sects;
+ if (setup_sects == 0) {
+ setup_sects = 4;
+ }
+
+ kern16_size = (setup_sects +1) *512;
+ if (kernel_len < kern16_size) {
+ fprintf(stderr, "BzImage truncated?\n");
+ return -1;
+ }
+
+ if (setup_header.protocol_version >= 0x0206) {
+ if ((uintmax_t)command_line_len >
+ (uintmax_t)setup_header.cmdline_size) {
+ dbgprintf("Kernel command line too long for kernel!\n");
+ return -1;
+ }
+ } else {
+ if (command_line_len > 255) {
+ dbgprintf("WARNING: This kernel may only support 255 byte command lines\n");
+ }
+ }
+
+ if (setup_header.protocol_version >= 0x0205) {
+ relocatable_kernel = setup_header.relocatable_kernel;
+ dbgprintf("bzImage is relocatable\n");
+ }
+
+ /* Can't use bzImage for crash dump purposes with real mode entry */
+ if((info->kexec_flags & KEXEC_ON_CRASH) && real_mode_entry) {
+ fprintf(stderr, "Can't use bzImage for crash dump purposes"
+ " with real mode entry\n");
+ return -1;
+ }
+
+ if((info->kexec_flags & KEXEC_ON_CRASH) && !relocatable_kernel) {
+ fprintf(stderr, "BzImage is not relocatable. Can't be used"
+ " as capture kernel.\n");
+ return -1;
+ }
+
+ /* Need to append some command line parameters internally in case of
+ * taking crash dumps.
+ */
+ if (info->kexec_flags & (KEXEC_ON_CRASH | KEXEC_PRESERVE_CONTEXT)) {
+ modified_cmdline = xmalloc(COMMAND_LINE_SIZE);
+ memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE);
+ if (command_line) {
+ strncpy(modified_cmdline, command_line,
+ COMMAND_LINE_SIZE);
+ modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
+ }
+
+ /* If panic kernel is being loaded, additional segments need
+ * to be created. load_crashdump_segments will take care of
+ * loading the segments as high in memory as possible, hence
+ * in turn as away as possible from kernel to avoid being
+ * stomped by the kernel.
+ */
+ if (load_crashdump_segments(info, modified_cmdline, -1, 0) < 0)
+ return -1;
+
+ /* Use new command line buffer */
+ command_line = modified_cmdline;
+ command_line_len = strlen(command_line) +1;
+ }
+
+ /* Load the trampoline. This must load at a higher address
+ * than the argument/parameter segment or the kernel will stomp
+ * it's gdt.
+ *
+ * x86_64 purgatory code has got relocations type R_X86_64_32S
+ * that means purgatory got to be loaded within first 2G otherwise
+ * overflow takes place while applying relocations.
+ */
+ if (!real_mode_entry && relocatable_kernel)
+ elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
+ 0x3000, 0x7fffffff, -1, 0);
+ else
+ elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
+ 0x3000, 640*1024, -1, 0);
+ dbgprintf("Loaded purgatory at addr 0x%lx\n", info->rhdr.rel_addr);
+
+ /* The argument/parameter segment */
+ if (real_mode_entry) {
+ /* need to include size for bss and heap etc */
+ if (setup_header.protocol_version >= 0x0201)
+ kern16_size_needed = setup_header.heap_end_ptr;
+ else
+ kern16_size_needed = kern16_size + 8192; /* bss */
+ if (kern16_size_needed < kern16_size)
+ kern16_size_needed = kern16_size;
+ if (kern16_size_needed > 0xfffc)
+ die("kern16_size_needed is more then 64k\n");
+ heap_size = 0xfffc - kern16_size_needed; /* less 64k */
+ heap_size = _ALIGN_DOWN(heap_size, 0x200);
+ kern16_size_needed += heap_size;
+ } else {
+ kern16_size_needed = kern16_size;
+ /* need to bigger than size of struct bootparams */
+ if (kern16_size_needed < 4096)
+ kern16_size_needed = 4096;
+ }
+ setup_size = kern16_size_needed + command_line_len +
+ PURGATORY_CMDLINE_SIZE;
+ real_mode = xmalloc(setup_size);
+ memset(real_mode, 0, setup_size);
+ if (!real_mode_entry) {
+ unsigned long setup_header_size = kernel[0x201] + 0x202 - 0x1f1;
+
+ /* only copy setup_header */
+ if (setup_header_size > 0x7f)
+ setup_header_size = 0x7f;
+ memcpy((unsigned char *)real_mode + 0x1f1, kernel + 0x1f1,
+ setup_header_size);
+ } else {
+ /* copy setup code and setup_header */
+ memcpy(real_mode, kernel, kern16_size);
+ }
+
+ if (info->kexec_flags & (KEXEC_ON_CRASH | KEXEC_PRESERVE_CONTEXT)) {
+ /* If using bzImage for capture kernel, then we will not be
+ * executing real mode code. setup segment can be loaded
+ * anywhere as we will be just reading command line.
+ */
+ setup_base = add_buffer(info, real_mode, setup_size, setup_size,
+ 16, 0x3000, -1, -1);
+ }
+ else if (real_mode->protocol_version >= 0x0200) {
+ /* Careful setup_base must be greater than 8K */
+ setup_base = add_buffer(info, real_mode, setup_size, setup_size,
+ 16, 0x3000, 640*1024, 1);
+ } else {
+ add_segment(info, real_mode, setup_size, SETUP_BASE, setup_size);
+ setup_base = SETUP_BASE;
+ }
+ dbgprintf("Loaded real-mode code and command line at 0x%lx\n",
+ setup_base);
+
+ /* Verify purgatory loads higher than the parameters */
+ if (info->rhdr.rel_addr < setup_base) {
+ die("Could not put setup code above the kernel parameters\n");
+ }
+
+ /* The main kernel segment */
+ size = kernel_len - kern16_size;
+
+ if (real_mode->protocol_version >=0x0205 && relocatable_kernel) {
+ /* Relocatable bzImage */
+ unsigned long kern_align = real_mode->kernel_alignment;
+ unsigned long kernel32_max_addr = DEFAULT_BZIMAGE_ADDR_MAX;
+
+ if (kernel32_max_addr > real_mode->initrd_addr_max)
+ kernel32_max_addr = real_mode->initrd_addr_max;
+
+ kernel32_load_addr = add_buffer(info, kernel + kern16_size,
+ size, size, kern_align,
+ 0x100000, kernel32_max_addr,
+ 1);
+ }
+ else {
+ kernel32_load_addr = KERN32_BASE;
+ add_segment(info, kernel + kern16_size, size,
+ kernel32_load_addr, size);
+ }
+
+ dbgprintf("Loaded 32bit kernel at 0x%lx\n", kernel32_load_addr);
+
+ /* Tell the kernel what is going on */
+ setup_linux_bootloader_parameters(info, real_mode, setup_base,
+ kern16_size_needed, command_line, command_line_len,
+ initrd, initrd_len);
+
+ if (real_mode_entry && real_mode->protocol_version >= 0x0201) {
+ real_mode->loader_flags |= 0x80; /* CAN_USE_HEAP */
+ real_mode->heap_end_ptr += heap_size - 0x200; /*stack*/
+ }
+
+ /* Get the initial register values */
+ if (real_mode_entry)
+ elf_rel_get_symbol(&info->rhdr, "entry16_regs",
+ &regs16, sizeof(regs16));
+
+ /*
+ * Initialize the 32bit start information.
+ */
+ regs32.eax = 0; /* unused */
+ regs32.ebx = 0; /* 0 == boot not AP processor start */
+ regs32.ecx = 0; /* unused */
+ regs32.edx = 0; /* unused */
+ regs32.esi = setup_base; /* kernel parameters */
+ regs32.edi = 0; /* unused */
+ regs32.esp = elf_rel_get_addr(&info->rhdr, "stack_end"); /* stack, unused */
+ regs32.ebp = 0; /* unused */
+ regs32.eip = kernel32_load_addr; /* kernel entry point */
+
+ /*
+ * Initialize the 16bit start information.
+ */
+ if (real_mode_entry) {
+ regs16.ds = regs16.es = regs16.fs = regs16.gs = setup_base >> 4;
+ regs16.cs = regs16.ds + 0x20;
+ regs16.ip = 0;
+ /* XXX: Documentation/i386/boot.txt says 'ss' must equal 'ds' */
+ regs16.ss = (elf_rel_get_addr(&info->rhdr, "stack_end") - 64*1024) >> 4;
+ /* XXX: Documentation/i386/boot.txt says 'sp' must equal heap_end */
+ regs16.esp = 0xFFFC;
+
+ printf("Starting the kernel in real mode\n");
+ regs32.eip = elf_rel_get_addr(&info->rhdr, "entry16");
+ real_mode->kernel_start = kernel32_load_addr;
+ }
+ if (real_mode_entry && kexec_debug) {
+ unsigned long entry16_debug, pre32, first32;
+ uint32_t old_first32;
+ /* Find the location of the symbols */
+ entry16_debug = elf_rel_get_addr(&info->rhdr, "entry16_debug");
+ pre32 = elf_rel_get_addr(&info->rhdr, "entry16_debug_pre32");
+ first32 = elf_rel_get_addr(&info->rhdr, "entry16_debug_first32");
+
+ /* Hook all of the linux kernel hooks */
+ real_mode->rmode_switch_cs = entry16_debug >> 4;
+ real_mode->rmode_switch_ip = pre32 - entry16_debug;
+ old_first32 = real_mode->kernel_start;
+ real_mode->kernel_start = first32;
+ elf_rel_set_symbol(&info->rhdr, "entry16_debug_old_first32",
+ &old_first32, sizeof(old_first32));
+
+ regs32.eip = entry16_debug;
+ }
+ if (real_mode_entry) {
+ elf_rel_set_symbol(&info->rhdr, "entry16_regs",
+ &regs16, sizeof(regs16));
+ elf_rel_set_symbol(&info->rhdr, "entry16_debug_regs",
+ &regs16, sizeof(regs16));
+ }
+ elf_rel_set_symbol(&info->rhdr, "entry32_regs", &regs32, sizeof(regs32));
+ cmdline_end = setup_base + kern16_size_needed + command_line_len - 1;
+ elf_rel_set_symbol(&info->rhdr, "cmdline_end", &cmdline_end,
+ sizeof(unsigned long));
+
+ /* Fill in the information BIOS calls would normally provide. */
+ if (!real_mode_entry) {
+ setup_linux_system_parameters(info, real_mode);
+ }
+
+ if (dtb) {
+ setup_linux_dtb(info, real_mode, dtb, dtb_len);
+ }
+
+ return 0;
+}
+
+int bzImage_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ char *command_line = NULL;
+ char *tmp_cmdline = NULL;
+ const char *ramdisk, *append = NULL, *dtb;
+ char *ramdisk_buf;
+ off_t ramdisk_length;
+ int command_line_len;
+ int real_mode_entry;
+ int opt;
+ int result;
+ char *dtb_buf;
+ off_t dtb_length;
+
+ /* See options.h -- add any more there, too. */
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { "command-line", 1, 0, OPT_APPEND },
+ { "append", 1, 0, OPT_APPEND },
+ { "reuse-cmdline", 0, 0, OPT_REUSE_CMDLINE },
+ { "initrd", 1, 0, OPT_RAMDISK },
+ { "ramdisk", 1, 0, OPT_RAMDISK },
+ { "real-mode", 0, 0, OPT_REAL_MODE },
+ { "dtb", 1, 0, OPT_DTB },
+ { 0, 0, 0, 0 },
+ };
+ static const char short_options[] = KEXEC_ARCH_OPT_STR "d";
+
+ real_mode_entry = 0;
+ ramdisk = 0;
+ ramdisk_length = 0;
+ dtb = 0;
+ dtb_length = 0;
+ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch(opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ case OPT_APPEND:
+ append = optarg;
+ break;
+ case OPT_REUSE_CMDLINE:
+ tmp_cmdline = get_command_line();
+ break;
+ case OPT_RAMDISK:
+ ramdisk = optarg;
+ break;
+ case OPT_REAL_MODE:
+ real_mode_entry = 1;
+ break;
+ case OPT_DTB:
+ dtb = optarg;
+ break;
+ }
+ }
+ command_line = concat_cmdline(tmp_cmdline, append);
+ if (tmp_cmdline) {
+ free(tmp_cmdline);
+ }
+ command_line_len = 0;
+ if (command_line) {
+ command_line_len = strlen(command_line) +1;
+ } else {
+ command_line = strdup("\0");
+ command_line_len = 1;
+ }
+ ramdisk_buf = 0;
+ if (ramdisk) {
+ ramdisk_buf = slurp_file(ramdisk, &ramdisk_length);
+ }
+ dtb_buf = 0;
+ if (dtb) {
+ dtb_buf = slurp_file(dtb, &dtb_length);
+ }
+ result = do_bzImage_load(info,
+ buf, len,
+ command_line, command_line_len,
+ ramdisk_buf, ramdisk_length,
+ dtb_buf, dtb_length,
+ real_mode_entry);
+
+ free(command_line);
+ return result;
+}
diff --git a/kexec/arch/i386/kexec-elf-rel-x86.c b/kexec/arch/i386/kexec-elf-rel-x86.c
new file mode 100644
index 0000000..55a214e
--- /dev/null
+++ b/kexec/arch/i386/kexec-elf-rel-x86.c
@@ -0,0 +1,36 @@
+#include <stdio.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+
+int machine_verify_elf_rel(struct mem_ehdr *ehdr)
+{
+ if (ehdr->ei_data != ELFDATA2LSB) {
+ return 0;
+ }
+ if (ehdr->ei_class != ELFCLASS32) {
+ return 0;
+ }
+ if ((ehdr->e_machine != EM_386) && (ehdr->e_machine != EM_486))
+ {
+ return 0;
+ }
+ return 1;
+}
+
+void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr),
+ struct mem_sym *UNUSED(sym), unsigned long r_type, void *location,
+ unsigned long address, unsigned long value)
+{
+ switch(r_type) {
+ case R_386_32:
+ *((uint32_t *)location) += value;
+ break;
+ case R_386_PC32:
+ *((uint32_t *)location) += value - address;
+ break;
+ default:
+ die("Unknown rel relocation: %lu\n", r_type);
+ break;
+ }
+}
diff --git a/kexec/arch/i386/kexec-elf-x86.c b/kexec/arch/i386/kexec-elf-x86.c
new file mode 100644
index 0000000..8eba242
--- /dev/null
+++ b/kexec/arch/i386/kexec-elf-x86.c
@@ -0,0 +1,331 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define _GNU_SOURCE
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <elf.h>
+#include <x86/x86-linux.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-elf-boot.h"
+#include "x86-linux-setup.h"
+#include "kexec-x86.h"
+#include "crashdump-x86.h"
+#include <arch/options.h>
+
+static const int probe_debug = 0;
+
+int elf_x86_any_probe(const char *buf, off_t len, enum coretype arch)
+{
+
+ struct mem_ehdr ehdr;
+ int result;
+ result = build_elf_exec_info(buf, len, &ehdr, 0);
+ if (result < 0) {
+ if (probe_debug) {
+ fprintf(stderr, "Not an ELF executable\n");
+ }
+ goto out;
+ }
+
+ /* Verify the architecuture specific bits */
+ switch (arch) {
+ case CORE_TYPE_ELF32:
+ if ((ehdr.e_machine != EM_386) && (ehdr.e_machine != EM_486)) {
+ if (probe_debug)
+ fprintf(stderr, "Not i386 ELF executable\n");
+ result = -1;
+ goto out;
+ }
+ break;
+
+ case CORE_TYPE_ELF64:
+ if (ehdr.e_machine != EM_X86_64) {
+ if (probe_debug)
+ fprintf(stderr, "Not x86_64 ELF executable\n");
+ result = -1;
+ goto out;
+ }
+ break;
+
+ case CORE_TYPE_UNDEF:
+ default:
+ if (
+ (ehdr.e_machine != EM_386) &&
+ (ehdr.e_machine != EM_486) &&
+ (ehdr.e_machine != EM_X86_64)
+ ) {
+ if (probe_debug)
+ fprintf(stderr, "Not i386 or x86_64 ELF executable\n");
+ result = -1;
+ goto out;
+ }
+ break;
+ }
+
+ result = 0;
+ out:
+ free_elf_info(&ehdr);
+ return result;
+}
+
+int elf_x86_probe(const char *buf, off_t len) {
+ return elf_x86_any_probe(buf, len, CORE_TYPE_ELF32);
+}
+
+void elf_x86_usage(void)
+{
+ printf( " --command-line=STRING Set the kernel command line to STRING\n"
+ " --append=STRING Set the kernel command line to STRING\n"
+ " --reuse-cmdline Use kernel command line from running system.\n"
+ " --initrd=FILE Use FILE as the kernel's initial ramdisk.\n"
+ " --ramdisk=FILE Use FILE as the kernel's initial ramdisk.\n"
+ " --args-linux Pass linux kernel style options\n"
+ " --args-elf Pass elf boot notes\n"
+ );
+
+
+}
+
+int elf_x86_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ struct mem_ehdr ehdr;
+ char *command_line = NULL, *modified_cmdline = NULL;
+ const char *append = NULL;
+ char *tmp_cmdline = NULL;
+ const char *error_msg = NULL;
+ int result;
+ int command_line_len;
+ const char *ramdisk;
+ unsigned long entry, max_addr;
+ int arg_style;
+#define ARG_STYLE_ELF 0
+#define ARG_STYLE_LINUX 1
+#define ARG_STYLE_NONE 2
+ int opt;
+
+ /* See options.h -- add any more there, too. */
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { "command-line", 1, NULL, OPT_APPEND },
+ { "append", 1, NULL, OPT_APPEND },
+ { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE },
+ { "initrd", 1, NULL, OPT_RAMDISK },
+ { "ramdisk", 1, NULL, OPT_RAMDISK },
+ { "args-elf", 0, NULL, OPT_ARGS_ELF },
+ { "args-linux", 0, NULL, OPT_ARGS_LINUX },
+ { "args-none", 0, NULL, OPT_ARGS_NONE },
+ { 0, 0, NULL, 0 },
+ };
+
+ static const char short_options[] = KEXEC_OPT_STR "";
+
+ /*
+ * Parse the command line arguments
+ */
+ arg_style = ARG_STYLE_ELF;
+ ramdisk = 0;
+ result = 0;
+ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch(opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ case OPT_APPEND:
+ append = optarg;
+ break;
+ case OPT_REUSE_CMDLINE:
+ tmp_cmdline = get_command_line();
+ break;
+ case OPT_RAMDISK:
+ ramdisk = optarg;
+ break;
+ case OPT_ARGS_ELF:
+ arg_style = ARG_STYLE_ELF;
+ break;
+ case OPT_ARGS_LINUX:
+ arg_style = ARG_STYLE_LINUX;
+ break;
+ case OPT_ARGS_NONE:
+#ifdef __i386__
+ arg_style = ARG_STYLE_NONE;
+#else
+ die("--args-none only works on arch i386\n");
+#endif
+ break;
+ }
+ }
+ command_line = concat_cmdline(tmp_cmdline, append);
+ if (tmp_cmdline) {
+ free(tmp_cmdline);
+ }
+ command_line_len = 0;
+ if (command_line) {
+ command_line_len = strlen(command_line) +1;
+ } else {
+ command_line = strdup("\0");
+ command_line_len = 1;
+ }
+
+ /* Need to append some command line parameters internally in case of
+ * taking crash dumps.
+ */
+ if (info->kexec_flags & (KEXEC_ON_CRASH|KEXEC_PRESERVE_CONTEXT)) {
+ modified_cmdline = xmalloc(COMMAND_LINE_SIZE);
+ memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE);
+ if (command_line) {
+ strncpy(modified_cmdline, command_line,
+ COMMAND_LINE_SIZE);
+ modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
+ }
+ }
+
+ /* Load the ELF executable */
+ elf_exec_build_load(info, &ehdr, buf, len, 0);
+
+ entry = ehdr.e_entry;
+ max_addr = elf_max_addr(&ehdr);
+
+ /* Do we want arguments? */
+ if (arg_style != ARG_STYLE_NONE) {
+ /* Load the setup code */
+ elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
+ 0, ULONG_MAX, 1, 0);
+ }
+ if (arg_style == ARG_STYLE_NONE) {
+ info->entry = (void *)entry;
+
+ }
+ else if (arg_style == ARG_STYLE_ELF) {
+ unsigned long note_base;
+ struct entry32_regs regs;
+ uint32_t arg1, arg2;
+
+ /* Setup the ELF boot notes */
+ note_base = elf_boot_notes(info, max_addr,
+ command_line, command_line_len);
+
+ /* Initialize the stack arguments */
+ arg2 = 0; /* No return address */
+ arg1 = note_base;
+ elf_rel_set_symbol(&info->rhdr, "stack_arg32_1", &arg1, sizeof(arg1));
+ elf_rel_set_symbol(&info->rhdr, "stack_arg32_2", &arg2, sizeof(arg2));
+
+ /* Initialize the registers */
+ elf_rel_get_symbol(&info->rhdr, "entry32_regs", &regs, sizeof(regs));
+ regs.eip = entry; /* The entry point */
+ regs.esp = elf_rel_get_addr(&info->rhdr, "stack_arg32_2");
+ elf_rel_set_symbol(&info->rhdr, "entry32_regs", &regs, sizeof(regs));
+
+ if (ramdisk) {
+ error_msg = "Ramdisks not supported with generic elf arguments";
+ goto out;
+ }
+ }
+ else if (arg_style == ARG_STYLE_LINUX) {
+ struct x86_linux_faked_param_header *hdr;
+ unsigned long param_base;
+ const char *ramdisk_buf;
+ off_t ramdisk_length;
+ struct entry32_regs regs;
+ int rc = 0;
+
+ /* Get the linux parameter header */
+ hdr = xmalloc(sizeof(*hdr));
+
+ /* Hack: With some ld versions, vmlinux program headers show
+ * a gap of two pages between bss segment and data segment
+ * but effectively kernel considers it as bss segment and
+ * overwrites the any data placed there. Hence bloat the
+ * memsz of parameter segment to 16K to avoid being placed
+ * in such gaps.
+ * This is a makeshift solution until it is fixed in kernel
+ */
+ param_base = add_buffer(info, hdr, sizeof(*hdr), 16*1024,
+ 16, 0, max_addr, 1);
+
+ /* Initialize the parameter header */
+ memset(hdr, 0, sizeof(*hdr));
+ init_linux_parameters(&hdr->hdr);
+
+ /* Add a ramdisk to the current image */
+ ramdisk_buf = NULL;
+ ramdisk_length = 0;
+ if (ramdisk) {
+ ramdisk_buf = slurp_file(ramdisk, &ramdisk_length);
+ }
+
+ /* If panic kernel is being loaded, additional segments need
+ * to be created. */
+ if (info->kexec_flags & (KEXEC_ON_CRASH|KEXEC_PRESERVE_CONTEXT)) {
+ rc = load_crashdump_segments(info, modified_cmdline,
+ max_addr, 0);
+ if (rc < 0) {
+ result = -1;
+ goto out;
+ }
+ /* Use new command line. */
+ free(command_line);
+ command_line = modified_cmdline;
+ command_line_len = strlen(modified_cmdline) + 1;
+ modified_cmdline = NULL;
+ }
+
+ /* Tell the kernel what is going on */
+ setup_linux_bootloader_parameters(info, &hdr->hdr, param_base,
+ offsetof(struct x86_linux_faked_param_header, command_line),
+ command_line, command_line_len,
+ ramdisk_buf, ramdisk_length);
+
+ /* Fill in the information bios calls would usually provide */
+ setup_linux_system_parameters(info, &hdr->hdr);
+
+ /* Initialize the registers */
+ elf_rel_get_symbol(&info->rhdr, "entry32_regs", &regs, sizeof(regs));
+ regs.ebx = 0; /* Bootstrap processor */
+ regs.esi = param_base; /* Pointer to the parameters */
+ regs.eip = entry; /* The entry point */
+ regs.esp = elf_rel_get_addr(&info->rhdr, "stack_end"); /* Stack, unused */
+ elf_rel_set_symbol(&info->rhdr, "entry32_regs", &regs, sizeof(regs));
+ }
+ else {
+ error_msg = "Unknown argument style\n";
+ }
+
+out:
+ free(command_line);
+ free(modified_cmdline);
+ if (error_msg)
+ die("%s", error_msg);
+ return result;
+}
diff --git a/kexec/arch/i386/kexec-mb2-x86.c b/kexec/arch/i386/kexec-mb2-x86.c
new file mode 100644
index 0000000..0d2e93b
--- /dev/null
+++ b/kexec/arch/i386/kexec-mb2-x86.c
@@ -0,0 +1,616 @@
+/*
+ * kexec-mb2-x86.c
+ *
+ * multiboot2 support for kexec to boot xen.
+ *
+ * Copyright (C) 2019 Varad Gautam (vrd at amazon.de), Amazon.com, Inc. or its affiliates.
+ *
+ * Parts based on GNU GRUB, Copyright (C) 2000 Free Software Foundation, Inc
+ * Parts taken from kexec-multiboot-x86.c, Eric Biederman (ebiederm@xmission.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <elf.h>
+#include <boot/elf_boot.h>
+#include <ip_checksum.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "kexec-x86.h"
+#include "../../kexec-syscall.h"
+#include "../../kexec-xen.h"
+#include <arch/options.h>
+
+/* From GNU GRUB */
+#include <x86/multiboot2.h>
+#include <x86/mb_info.h>
+
+/* Framebuffer */
+#include <sys/ioctl.h>
+#include <linux/fb.h>
+
+extern struct arch_options_t arch_options;
+
+/* Static storage */
+static char headerbuf[MULTIBOOT_SEARCH];
+static struct multiboot_header *mbh = NULL;
+struct multiboot2_header_info {
+ struct multiboot_header_tag_information_request *request_tag;
+ struct multiboot_header_tag_address *addr_tag;
+ struct multiboot_header_tag_entry_address *entry_addr_tag;
+ struct multiboot_header_tag_console_flags *console_tag;
+ struct multiboot_header_tag_framebuffer *fb_tag;
+ struct multiboot_header_tag_module_align *mod_align_tag;
+ struct multiboot_header_tag_relocatable *rel_tag;
+} mhi;
+
+#define ALIGN_UP(addr, align) \
+ ((addr + (typeof (addr)) align - 1) & ~((typeof (addr)) align - 1))
+
+int multiboot2_x86_probe(const char *buf, off_t buf_len)
+/* Is it a good idea to try booting this file? */
+{
+ int i, len;
+
+ /* First of all, check that this is an ELF file for either x86 or x86-64 */
+ i = elf_x86_any_probe(buf, buf_len, CORE_TYPE_UNDEF);
+ if (i < 0)
+ return i;
+
+ /* Now look for a multiboot header. */
+ len = MULTIBOOT_SEARCH;
+ if (len > buf_len)
+ len = buf_len;
+
+ memcpy(headerbuf, buf, len);
+ if (len < sizeof(struct multiboot_header)) {
+ /* Short file */
+ return -1;
+ }
+ for (mbh = (struct multiboot_header *) headerbuf;
+ ((char *) mbh <= (char *) headerbuf + len - sizeof(struct multiboot_header));
+ mbh = (struct multiboot_header *) ((char *) mbh + MULTIBOOT_HEADER_ALIGN)) {
+ if (mbh->magic == MULTIBOOT2_HEADER_MAGIC
+ && !((mbh->magic+mbh->architecture+mbh->header_length+mbh->checksum) & 0xffffffff)) {
+ /* Found multiboot header. */
+ return 0;
+ }
+ }
+ /* Not multiboot */
+ return -1;
+}
+
+void multiboot2_x86_usage(void)
+/* Multiboot-specific options */
+{
+ printf(" --command-line=STRING Set the kernel command line to STRING.\n");
+ printf(" --reuse-cmdline Use kernel command line from running system.\n");
+ printf(" --module=\"MOD arg1 arg2...\" Load module MOD with command-line \"arg1...\"\n");
+ printf(" (can be used multiple times).\n");
+}
+
+static size_t
+multiboot2_get_mbi_size(int ranges, int cmdline_size, int modcount, int modcmd_size)
+{
+ size_t mbi_size;
+
+ mbi_size = (2 * sizeof (uint32_t) /* u32 total_size, u32 reserved */
+ + ALIGN_UP (sizeof (struct multiboot_tag_basic_meminfo), MULTIBOOT_TAG_ALIGN)
+ + ALIGN_UP ((sizeof (struct multiboot_tag_mmap)
+ + ranges * sizeof (struct multiboot_mmap_entry)), MULTIBOOT_TAG_ALIGN)
+ + (sizeof (struct multiboot_tag_string)
+ + ALIGN_UP (cmdline_size, MULTIBOOT_TAG_ALIGN))
+ + (sizeof (struct multiboot_tag_string)
+ + ALIGN_UP (strlen(BOOTLOADER " " BOOTLOADER_VERSION) + 1, MULTIBOOT_TAG_ALIGN))
+ + (modcount * sizeof (struct multiboot_tag_module) + modcmd_size))
+ + sizeof (struct multiboot_tag); /* end tag */
+
+ if (mhi.rel_tag)
+ mbi_size += ALIGN_UP (sizeof (struct multiboot_tag_load_base_addr), MULTIBOOT_TAG_ALIGN);
+
+ if (mhi.fb_tag)
+ mbi_size += ALIGN_UP (sizeof (struct multiboot_tag_framebuffer), MULTIBOOT_TAG_ALIGN);
+
+ return mbi_size;
+}
+
+static void multiboot2_read_header_tags(void)
+{
+ struct multiboot_header_tag *tag;
+
+ for (tag = (struct multiboot_header_tag *) (mbh + 1);
+ tag->type != MULTIBOOT_TAG_TYPE_END;
+ tag = (struct multiboot_header_tag *) ((char *) tag + ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN)))
+ {
+ switch (tag->type)
+ {
+ case MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST:
+ {
+ mhi.request_tag = (struct multiboot_header_tag_information_request *) tag;
+ break;
+ }
+ case MULTIBOOT_HEADER_TAG_RELOCATABLE:
+ {
+ mhi.rel_tag = (struct multiboot_header_tag_relocatable *) tag;
+ break;
+ }
+ case MULTIBOOT_HEADER_TAG_ADDRESS:
+ {
+ mhi.addr_tag = (struct multiboot_header_tag_address *) tag;
+ break;
+ }
+ case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS:
+ {
+ mhi.entry_addr_tag = (struct multiboot_header_tag_entry_address *) tag;
+ break;
+ }
+ case MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS:
+ {
+ mhi.console_tag = (struct multiboot_header_tag_console_flags *) tag;
+ break;
+ }
+ case MULTIBOOT_HEADER_TAG_FRAMEBUFFER:
+ {
+ mhi.fb_tag = (struct multiboot_header_tag_framebuffer *) tag;
+ break;
+ }
+ case MULTIBOOT_HEADER_TAG_MODULE_ALIGN:
+ {
+ mhi.mod_align_tag = (struct multiboot_header_tag_module_align *) tag;
+ break;
+ }
+ case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64:
+ case MULTIBOOT_HEADER_TAG_EFI_BS:
+ /* Ignoring EFI. */
+ break;
+ default:
+ {
+ if (!(tag->flags & MULTIBOOT_HEADER_TAG_OPTIONAL))
+ fprintf(stderr, "unsupported tag: 0x%x", tag->type);
+ break;
+ }
+ }
+ }
+}
+
+struct multiboot_mmap_entry *multiboot_construct_memory_map(struct memory_range *range,
+ int ranges,
+ unsigned long long *mem_lower,
+ unsigned long long *mem_upper)
+{
+ struct multiboot_mmap_entry *entries;
+ int i;
+
+ *mem_lower = *mem_upper = 0;
+ entries = xmalloc(ranges * sizeof(*entries));
+ for (i = 0; i < ranges; i++) {
+ entries[i].addr = range[i].start;
+ entries[i].len = range[i].end - range[i].start + 1;
+
+ if (range[i].type == RANGE_RAM) {
+ entries[i].type = MULTIBOOT_MEMORY_AVAILABLE;
+ /*
+ * Is this the "low" memory? Can't just test
+ * against zero, because Linux protects (and
+ * hides) the first few pages of physical
+ * memory.
+ */
+
+ if ((range[i].start <= 64*1024)
+ && (range[i].end > *mem_lower)) {
+ range[i].start = 0;
+ *mem_lower = range[i].end;
+ }
+ /* Is this the "high" memory? */
+ if ((range[i].start <= 0x100000)
+ && (range[i].end > *mem_upper + 0x100000))
+ *mem_upper = range[i].end - 0x100000;
+ } else if (range[i].type == RANGE_ACPI)
+ entries[i].type = MULTIBOOT_MEMORY_ACPI_RECLAIMABLE;
+ else if (range[i].type == RANGE_ACPI_NVS)
+ entries[i].type = MULTIBOOT_MEMORY_NVS;
+ else if (range[i].type == RANGE_RESERVED)
+ entries[i].type = MULTIBOOT_MEMORY_RESERVED;
+ }
+ return entries;
+}
+
+static uint64_t multiboot2_make_mbi(struct kexec_info *info, char *cmdline, int cmdline_len,
+ unsigned long load_base_addr, void *mbi_buf, size_t mbi_bytes)
+{
+ uint64_t *ptrorig = mbi_buf;
+ struct multiboot_mmap_entry *mmap_entries;
+ unsigned long long mem_lower = 0, mem_upper = 0;
+
+ *ptrorig = mbi_bytes; /* u32 total_size, u32 reserved */
+ ptrorig++;
+
+ mmap_entries = multiboot_construct_memory_map(info->memory_range, info->memory_ranges, &mem_lower, &mem_upper);
+ {
+ struct multiboot_tag_basic_meminfo *tag = (struct multiboot_tag_basic_meminfo *) ptrorig;
+
+ tag->type = MULTIBOOT_TAG_TYPE_BASIC_MEMINFO;
+ tag->size = sizeof (struct multiboot_tag_basic_meminfo);
+ tag->mem_lower = mem_lower >> 10;
+ tag->mem_upper = mem_upper >> 10;
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (*ptrorig);
+ }
+
+ {
+ struct multiboot_tag_mmap *tag = (struct multiboot_tag_mmap *) ptrorig;
+
+ tag->type = MULTIBOOT_TAG_TYPE_MMAP;
+ tag->size = sizeof(struct multiboot_tag_mmap) + sizeof(struct multiboot_mmap_entry) * info->memory_ranges;
+ tag->entry_size = sizeof(struct multiboot_mmap_entry);
+ tag->entry_version = 0;
+ memcpy(tag->entries, mmap_entries, tag->entry_size * info->memory_ranges);
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (*ptrorig);
+ }
+
+ if (mhi.rel_tag) {
+ struct multiboot_tag_load_base_addr *tag = (struct multiboot_tag_load_base_addr *) ptrorig;
+
+ tag->type = MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR;
+ tag->size = sizeof (struct multiboot_tag_load_base_addr);
+ tag->load_base_addr = load_base_addr;
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (*ptrorig);
+ }
+
+ {
+ struct multiboot_tag_string *tag = (struct multiboot_tag_string *) ptrorig;
+
+ tag->type = MULTIBOOT_TAG_TYPE_CMDLINE;
+ tag->size = sizeof (struct multiboot_tag_string) + cmdline_len;
+ memcpy(tag->string, cmdline, cmdline_len);
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (*ptrorig);
+ }
+
+ {
+ struct multiboot_tag_string *tag = (struct multiboot_tag_string *) ptrorig;
+
+ tag->type = MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME;
+ tag->size = sizeof(struct multiboot_tag_string) + strlen(BOOTLOADER " " BOOTLOADER_VERSION) + 1;
+ sprintf(tag->string, "%s", BOOTLOADER " " BOOTLOADER_VERSION);
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (*ptrorig);
+ }
+
+ if (mhi.fb_tag) {
+ struct multiboot_tag_framebuffer *tag = (struct multiboot_tag_framebuffer *) ptrorig;
+ struct fb_fix_screeninfo info;
+ struct fb_var_screeninfo mode;
+ int fd;
+
+ tag->common.type = MULTIBOOT_TAG_TYPE_FRAMEBUFFER;
+ tag->common.size = sizeof(struct multiboot_tag_framebuffer);
+ /* check if purgatory will reset to standard ega text mode */
+ if (arch_options.reset_vga || arch_options.console_vga) {
+ tag->common.framebuffer_type = MB_FRAMEBUFFER_TYPE_EGA_TEXT;
+ tag->common.framebuffer_addr = 0xb8000;
+ tag->common.framebuffer_pitch = 80*2;
+ tag->common.framebuffer_width = 80;
+ tag->common.framebuffer_height = 25;
+ tag->common.framebuffer_bpp = 16;
+
+ ptrorig += ALIGN_UP (tag->common.size, MULTIBOOT_TAG_ALIGN) / sizeof (*ptrorig);
+ goto out;
+ }
+
+ /* use current graphics framebuffer settings */
+ fd = open("/dev/fb0", O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "can't open /dev/fb0: %s\n", strerror(errno));
+ goto out;
+ }
+ if (ioctl(fd, FBIOGET_FSCREENINFO, &info) < 0){
+ fprintf(stderr, "can't get screeninfo: %s\n", strerror(errno));
+ close(fd);
+ goto out;
+ }
+ if (ioctl(fd, FBIOGET_VSCREENINFO, &mode) < 0){
+ fprintf(stderr, "can't get modeinfo: %s\n", strerror(errno));
+ close(fd);
+ goto out;
+ }
+ close(fd);
+
+ if (info.smem_start == 0 || info.smem_len == 0) {
+ fprintf(stderr, "can't get linerar framebuffer address\n");
+ goto out;
+ }
+
+ if (info.type != FB_TYPE_PACKED_PIXELS) {
+ fprintf(stderr, "unsupported framebuffer type\n");
+ goto out;
+ }
+
+ if (info.visual != FB_VISUAL_TRUECOLOR) {
+ fprintf(stderr, "unsupported framebuffer visual\n");
+ goto out;
+ }
+
+ tag->common.framebuffer_type = MB_FRAMEBUFFER_TYPE_RGB;
+ tag->common.framebuffer_addr = info.smem_start;
+ tag->common.framebuffer_pitch = info.line_length;
+ tag->common.framebuffer_width = mode.xres;
+ tag->common.framebuffer_height = mode.yres;
+ tag->common.framebuffer_bpp = mode.bits_per_pixel;
+
+ tag->framebuffer_red_field_position = mode.red.offset;
+ tag->framebuffer_red_mask_size = mode.red.length;
+ tag->framebuffer_green_field_position = mode.green.offset;
+ tag->framebuffer_green_mask_size = mode.green.length;
+ tag->framebuffer_blue_field_position = mode.blue.offset;
+ tag->framebuffer_blue_mask_size = mode.blue.length;
+
+ ptrorig += ALIGN_UP (tag->common.size, MULTIBOOT_TAG_ALIGN) / sizeof (*ptrorig);
+ }
+
+out:
+ return (uint64_t) (uintptr_t) ptrorig;
+}
+
+static uint64_t multiboot2_mbi_add_module(void *mbi_buf, uint64_t mbi_ptr, uint32_t mod_start,
+ uint32_t mod_end, char *mod_clp)
+{
+ struct multiboot_tag_module *tag = (struct multiboot_tag_module *) (uintptr_t) mbi_ptr;
+
+ tag->type = MULTIBOOT_TAG_TYPE_MODULE;
+ tag->size = sizeof(struct multiboot_tag_module) + strlen((char *)(long) mod_clp) + 1;
+ tag->mod_start = mod_start;
+ tag->mod_end = mod_end;
+
+ memcpy(tag->cmdline, (char *)(long) mod_clp, strlen((char *)(long) mod_clp) + 1);
+ mbi_ptr += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN);
+
+ return mbi_ptr;
+}
+
+static uint64_t multiboot2_mbi_end(void *mbi_buf, uint64_t mbi_ptr)
+{
+ struct multiboot_tag *tag = (struct multiboot_tag *) (uintptr_t) mbi_ptr;
+
+ tag->type = MULTIBOOT_TAG_TYPE_END;
+ tag->size = sizeof (struct multiboot_tag);
+ mbi_ptr += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN);
+
+ return mbi_ptr;
+}
+
+static inline int multiboot2_rel_valid(struct multiboot_header_tag_relocatable *rel_tag,
+ uint64_t rel_start, uint64_t rel_end)
+{
+ if (rel_start >= rel_tag->min_addr && rel_end <= rel_tag->max_addr)
+ return 1;
+
+ return 0;
+}
+
+int multiboot2_x86_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ struct mem_ehdr ehdr;
+ void *mbi_buf;
+ size_t mbi_bytes;
+ unsigned long addr;
+ struct entry32_regs regs;
+ char *command_line = NULL, *tmp_cmdline = NULL;
+ int command_line_len;
+ char *imagename, *cp, *append = NULL;;
+ int opt;
+ int modules, mod_command_line_space;
+ uint64_t mbi_ptr;
+ char *mod_clp_base;
+ /* See options.h -- add any more there, too. */
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { "command-line", 1, 0, OPT_CL },
+ { "append", 1, 0, OPT_CL },
+ { "reuse-cmdline", 0, 0, OPT_REUSE_CMDLINE },
+ { "module", 1, 0, OPT_MOD },
+ { 0, 0, 0, 0 },
+ };
+ static const char short_options[] = KEXEC_ARCH_OPT_STR "";
+ uint64_t rel_min, rel_max;
+
+ /* Probe for the MB header if it's not already found */
+ if (mbh == NULL && multiboot_x86_probe(buf, len) != 1)
+ {
+ fprintf(stderr, "Cannot find a loadable multiboot2 header.\n");
+ return -1;
+ }
+
+ /* Parse the header tags. */
+ multiboot2_read_header_tags();
+
+ /* Parse the command line */
+ command_line_len = 0;
+ modules = 0;
+ mod_command_line_space = 0;
+ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1)
+ {
+ switch(opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ case OPT_CL:
+ append = optarg;
+ break;
+ case OPT_REUSE_CMDLINE:
+ tmp_cmdline = get_command_line();
+ break;
+ case OPT_MOD:
+ modules++;
+ mod_command_line_space += strlen(optarg) + 1;
+ break;
+ }
+ }
+ imagename = argv[optind];
+
+ /* Final command line = imagename + <OPT_REUSE_CMDLINE> + <OPT_CL> */
+ tmp_cmdline = concat_cmdline(command_line, append);
+ if (command_line) {
+ free(command_line);
+ }
+ command_line = concat_cmdline(imagename, tmp_cmdline);
+ if (tmp_cmdline) {
+ free(tmp_cmdline);
+ }
+
+ if (xen_present() && info->kexec_flags & KEXEC_LIVE_UPDATE ) {
+ if (!mhi.rel_tag) {
+ fprintf(stderr, "Multiboot2 image must be relocatable"
+ "for KEXEC_LIVE_UPDATE.\n");
+ return -1;
+ }
+ cmdline_add_liveupdate(&command_line);
+ }
+
+ command_line_len = strlen(command_line) + 1;
+
+ /* Load the ELF executable */
+ if (mhi.rel_tag) {
+ rel_min = mhi.rel_tag->min_addr;
+ rel_max = mhi.rel_tag->max_addr;
+
+ if (info->kexec_flags & KEXEC_LIVE_UPDATE && xen_present()) {
+ /* TODO also check if elf is xen */
+ /* On a live update, load target xen over the current xen image. */
+ uint64_t xen_start, xen_end;
+
+ xen_get_kexec_range(KEXEC_RANGE_MA_XEN, &xen_start, &xen_end);
+ if (multiboot2_rel_valid(mhi.rel_tag, xen_start, xen_end)) {
+ rel_min = xen_start;
+ } else {
+ fprintf(stderr, "Cannot place Elf into "
+ "KEXEC_RANGE_MA_XEN for KEXEC_LIVE_UPDATE.\n");
+ return -1;
+ }
+ }
+
+ elf_exec_build_load_relocatable(info, &ehdr, buf, len, 0,
+ rel_min, rel_max, mhi.rel_tag->align);
+ } else
+ elf_exec_build_load(info, &ehdr, buf, len, 0);
+
+ if (info->kexec_flags & KEXEC_LIVE_UPDATE && xen_present()) {
+ uint64_t lu_start, lu_end;
+
+ xen_get_kexec_range(7 /* KEXEC_RANGE_MA_LIVEUPDATE */, &lu_start, &lu_end);
+ /* Fit everything else into lu_start-lu_end. First page after lu_start is
+ * reserved for LU breadcrumb. */
+ rel_min = lu_start + 4096;
+ rel_max = lu_end;
+ } else {
+ rel_min = 0x500;
+ rel_max = ULONG_MAX;
+ }
+
+ /* Load the setup code */
+ elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
+ rel_min, rel_max, 1, 0);
+
+ /* Construct information tags. */
+ mbi_bytes = multiboot2_get_mbi_size(info->memory_ranges, command_line_len, modules, mod_command_line_space);
+ mbi_buf = xmalloc(mbi_bytes);
+
+ mbi_ptr = multiboot2_make_mbi(info, command_line, command_line_len, info->rhdr.rel_addr, mbi_buf, mbi_bytes);
+ free(command_line);
+
+ if (info->kexec_flags & KEXEC_LIVE_UPDATE && xen_present()) {
+ if (multiboot2_rel_valid(mhi.rel_tag, rel_min, rel_max)) {
+ /* Shrink the reloc range to fit into LU region for xen. */
+ mhi.rel_tag->min_addr = rel_min;
+ mhi.rel_tag->max_addr = rel_max;
+ } else {
+ fprintf(stderr, "Multiboot2 image cannot be relocated into "
+ "KEXEC_RANGE_MA_LIVEUPDATE for KEXEC_LIVE_UPDATE.\n");
+ return -1;
+ }
+ }
+
+ /* Load modules */
+ if (modules) {
+ char *mod_filename, *mod_command_line, *mod_clp, *buf;
+ off_t mod_size;
+ int i = 0;
+
+ mod_clp_base = xmalloc(mod_command_line_space);
+
+ /* Go back and parse the module command lines */
+ mod_clp = mod_clp_base;
+ optind = opterr = 1;
+ while((opt = getopt_long(argc, argv,
+ short_options, options, 0)) != -1) {
+ if (opt != OPT_MOD) continue;
+
+ /* Split module filename from command line */
+ mod_command_line = mod_filename = optarg;
+ if ((cp = strchr(mod_filename, ' ')) != NULL) {
+ /* See as I discard the 'const' modifier */
+ *cp = '\0';
+ }
+
+ /* Load the module */
+ buf = slurp_decompress_file(mod_filename, &mod_size);
+
+ if (cp != NULL) *cp = ' ';
+
+ /* Pick the next aligned spot to load it in. Always page align. */
+ addr = add_buffer(info, buf, mod_size, mod_size, getpagesize(),
+ rel_min, rel_max, 1);
+
+ /* Add the module command line */
+ sprintf(mod_clp, "%s", mod_command_line);
+
+ mbi_ptr = multiboot2_mbi_add_module(mbi_buf, mbi_ptr, addr, addr + mod_size, mod_clp);
+
+ mod_clp += strlen(mod_clp) + 1;
+ i++;
+ }
+
+ free(mod_clp_base);
+ }
+
+ mbi_ptr = multiboot2_mbi_end(mbi_buf, mbi_ptr);
+
+ if (sort_segments(info) < 0)
+ return -1;
+
+ addr = add_buffer(info, mbi_buf, mbi_bytes, mbi_bytes, 4,
+ rel_min, rel_max, 1);
+
+ elf_rel_get_symbol(&info->rhdr, "entry32_regs", &regs, sizeof(regs));
+ regs.eax = MULTIBOOT2_BOOTLOADER_MAGIC;
+ regs.ebx = addr;
+ regs.eip = ehdr.e_entry;
+ elf_rel_set_symbol(&info->rhdr, "entry32_regs", &regs, sizeof(regs));
+
+ return 0;
+}
diff --git a/kexec/arch/i386/kexec-multiboot-x86.c b/kexec/arch/i386/kexec-multiboot-x86.c
new file mode 100644
index 0000000..33c885a
--- /dev/null
+++ b/kexec/arch/i386/kexec-multiboot-x86.c
@@ -0,0 +1,505 @@
+/*
+ * kexec-multiboot-x86.c
+ *
+ * (partial) multiboot support for kexec. Only supports ELF32
+ * kernels, and a subset of the multiboot info page options
+ * (i.e. enough to boot the Xen hypervisor).
+ *
+ * TODO:
+ * - smarter allocation of new segments
+ * - proper support for the MULTIBOOT_VIDEO_MODE bit
+ *
+ *
+ * Copyright (C) 2003 Tim Deegan (tjd21 at cl.cam.ac.uk)
+ *
+ * Parts based on GNU GRUB, Copyright (C) 2000 Free Software Foundation, Inc
+ * Parts copied from kexec-elf32-x86.c, written by Eric Biederman
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <elf.h>
+#include <boot/elf_boot.h>
+#include <ip_checksum.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "kexec-x86.h"
+#include <arch/options.h>
+
+/* From GNU GRUB */
+#include <x86/mb_header.h>
+#include <x86/mb_info.h>
+
+/* Framebuffer */
+#include <sys/ioctl.h>
+#include <linux/fb.h>
+
+extern struct arch_options_t arch_options;
+
+/* Static storage */
+static char headerbuf[MULTIBOOT_SEARCH];
+static struct multiboot_header *mbh = NULL;
+static off_t mbh_offset = 0;
+
+#define MIN(_x,_y) (((_x)<=(_y))?(_x):(_y))
+
+
+int multiboot_x86_probe(const char *buf, off_t buf_len)
+/* Is it a good idea to try booting this file? */
+{
+ int i, len;
+ /* Now look for a multiboot header in the first 8KB */
+ len = MULTIBOOT_SEARCH;
+ if (len > buf_len) {
+ len = buf_len;
+ }
+ memcpy(headerbuf, buf, len);
+ if (len < 12) {
+ /* Short file */
+ return -1;
+ }
+ for (mbh_offset = 0; mbh_offset <= (len - 12); mbh_offset += 4)
+ {
+ /* Search for a multiboot header */
+ mbh = (struct multiboot_header *)(headerbuf + mbh_offset);
+ if (mbh->magic != MULTIBOOT_MAGIC
+ || ((mbh->magic+mbh->flags+mbh->checksum) & 0xffffffff))
+ {
+ /* Not a multiboot header */
+ continue;
+ }
+ if (mbh->flags & MULTIBOOT_AOUT_KLUDGE) {
+ if (mbh->load_addr & 0xfff) {
+ fprintf(stderr, "multiboot load address not 4k aligned\n");
+ return -1;
+ }
+ if (mbh->load_addr > mbh->header_addr) {
+ fprintf(stderr, "multiboot header address > load address\n");
+ return -1;
+ }
+ if (mbh->load_end_addr < mbh->load_addr) {
+ fprintf(stderr, "multiboot load end address < load address\n");
+ return -1;
+ }
+ if (mbh->bss_end_addr < mbh->load_end_addr) {
+ fprintf(stderr, "multiboot bss end address < load end address\n");
+ return -1;
+ }
+ if (mbh->load_end_addr - mbh->header_addr > buf_len - mbh_offset) {
+ fprintf(stderr, "multiboot file truncated\n");
+ return -1;
+ }
+ if (mbh->entry_addr < mbh->load_addr || mbh->entry_addr >= mbh->load_end_addr) {
+ fprintf(stderr, "multiboot entry out of range\n");
+ return -1;
+ }
+ } else {
+ if ((i=elf_x86_probe(buf, buf_len)) < 0)
+ return i;
+ }
+ if (mbh->flags & MULTIBOOT_UNSUPPORTED) {
+ /* Requires options we don't support */
+ fprintf(stderr,
+ "Found a multiboot header, but it "
+ "requires multiboot options that I\n"
+ "don't understand. Sorry.\n");
+ return -1;
+ }
+ /* Bootable */
+ return 0;
+ }
+ /* Not multiboot */
+ return -1;
+}
+
+
+void multiboot_x86_usage(void)
+/* Multiboot-specific options */
+{
+ printf(" --command-line=STRING Set the kernel command line to STRING.\n");
+ printf(" --reuse-cmdline Use kernel command line from running system.\n");
+ printf(" --module=\"MOD arg1 arg2...\" Load module MOD with command-line \"arg1...\"\n");
+ printf(" (can be used multiple times).\n");
+}
+
+
+static int framebuffer_info(struct multiboot_info *mbi)
+{
+ struct fb_fix_screeninfo info;
+ struct fb_var_screeninfo mode;
+ int fd;
+
+ /* check if purgatory will reset to standard ega text mode */
+ if (arch_options.reset_vga || arch_options.console_vga) {
+ mbi->framebuffer_type = MB_FRAMEBUFFER_TYPE_EGA_TEXT;
+ mbi->framebuffer_addr = 0xb8000;
+ mbi->framebuffer_pitch = 80*2;
+ mbi->framebuffer_width = 80;
+ mbi->framebuffer_height = 25;
+ mbi->framebuffer_bpp = 16;
+
+ mbi->flags |= MB_INFO_FRAMEBUFFER_INFO;
+ return 0;
+ }
+
+ /* use current graphics framebuffer settings */
+ fd = open("/dev/fb0", O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "can't open /dev/fb0: %s\n", strerror(errno));
+ return -1;
+ }
+ if (ioctl(fd, FBIOGET_FSCREENINFO, &info) < 0){
+ fprintf(stderr, "can't get screeninfo: %s\n", strerror(errno));
+ close(fd);
+ return -1;
+ }
+ if (ioctl(fd, FBIOGET_VSCREENINFO, &mode) < 0){
+ fprintf(stderr, "can't get modeinfo: %s\n", strerror(errno));
+ close(fd);
+ return -1;
+ }
+ close(fd);
+
+ if (info.smem_start == 0 || info.smem_len == 0) {
+ fprintf(stderr, "can't get linerar framebuffer address\n");
+ return -1;
+ }
+
+ if (info.type != FB_TYPE_PACKED_PIXELS) {
+ fprintf(stderr, "unsupported framebuffer type\n");
+ return -1;
+ }
+
+ if (info.visual != FB_VISUAL_TRUECOLOR) {
+ fprintf(stderr, "unsupported framebuffer visual\n");
+ return -1;
+ }
+
+ mbi->framebuffer_type = MB_FRAMEBUFFER_TYPE_RGB;
+ mbi->framebuffer_addr = info.smem_start;
+ mbi->framebuffer_pitch = info.line_length;
+ mbi->framebuffer_width = mode.xres;
+ mbi->framebuffer_height = mode.yres;
+ mbi->framebuffer_bpp = mode.bits_per_pixel;
+ mbi->framebuffer_red_field_position = mode.red.offset;
+ mbi->framebuffer_red_mask_size = mode.red.length;
+ mbi->framebuffer_green_field_position = mode.green.offset;
+ mbi->framebuffer_green_mask_size = mode.green.length;
+ mbi->framebuffer_blue_field_position = mode.blue.offset;
+ mbi->framebuffer_blue_mask_size = mode.blue.length;
+
+ mbi->flags |= MB_INFO_FRAMEBUFFER_INFO;
+ return 0;
+}
+
+int multiboot_x86_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+/* Marshal up a multiboot-style kernel */
+{
+ struct multiboot_info *mbi;
+ void *mbi_buf;
+ struct mod_list *modp;
+ unsigned long freespace;
+ unsigned long long mem_lower = 0, mem_upper = 0;
+ struct mem_ehdr ehdr;
+ unsigned long mbi_base;
+ struct entry32_regs regs;
+ size_t mbi_bytes, mbi_offset;
+ char *command_line = NULL, *tmp_cmdline = NULL;
+ char *imagename, *cp, *append = NULL;;
+ struct memory_range *range;
+ int ranges;
+ struct AddrRangeDesc *mmap;
+ int command_line_len;
+ int i, result;
+ uint32_t u, entry;
+ int opt;
+ int modules, mod_command_line_space;
+ /* See options.h -- add any more there, too. */
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { "command-line", 1, 0, OPT_CL },
+ { "append", 1, 0, OPT_CL },
+ { "reuse-cmdline", 0, 0, OPT_REUSE_CMDLINE },
+ { "module", 1, 0, OPT_MOD },
+ { 0, 0, 0, 0 },
+ };
+ static const char short_options[] = KEXEC_ARCH_OPT_STR "";
+
+ /* Probe for the MB header if it's not already found */
+ if (mbh == NULL && multiboot_x86_probe(buf, len) != 1) {
+ fprintf(stderr, "Cannot find a loadable multiboot header.\n");
+ return -1;
+ }
+
+ /* Parse the command line */
+ command_line_len = 0;
+ modules = 0;
+ mod_command_line_space = 0;
+ result = 0;
+ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch(opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ case OPT_CL:
+ append = optarg;
+ break;
+ case OPT_REUSE_CMDLINE:
+ command_line = get_command_line();
+ break;
+ case OPT_MOD:
+ modules++;
+ mod_command_line_space += strlen(optarg) + 1;
+ break;
+ }
+ }
+ imagename = argv[optind];
+
+ /* Final command line = imagename + <OPT_REUSE_CMDLINE> + <OPT_CL> */
+ tmp_cmdline = concat_cmdline(command_line, append);
+ if (command_line) {
+ free(command_line);
+ }
+ command_line = concat_cmdline(imagename, tmp_cmdline);
+ if (tmp_cmdline) {
+ free(tmp_cmdline);
+ }
+ command_line_len = strlen(command_line) + 1;
+
+ if (mbh->flags & MULTIBOOT_AOUT_KLUDGE) {
+ add_segment(info,
+ buf + (mbh_offset - (mbh->header_addr - mbh->load_addr)),
+ mbh->load_end_addr - mbh->load_addr,
+ mbh->load_addr,
+ mbh->bss_end_addr - mbh->load_addr);
+ entry = mbh->entry_addr;
+ } else {
+ /* Load the ELF executable */
+ elf_exec_build_load(info, &ehdr, buf, len, 0);
+ entry = ehdr.e_entry;
+ }
+
+ /* Load the setup code */
+ elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, 0,
+ ULONG_MAX, 1, 0);
+
+ /* The first segment will contain the multiboot headers:
+ * =============
+ * multiboot information (mbi)
+ * -------------
+ * kernel command line
+ * -------------
+ * bootloader name
+ * -------------
+ * module information entries
+ * -------------
+ * module command lines
+ * ==============
+ */
+ mbi_bytes = _ALIGN(sizeof(*mbi) + command_line_len
+ + strlen (BOOTLOADER " " BOOTLOADER_VERSION) + 1, 4);
+ mbi_buf = xmalloc(mbi_bytes);
+ mbi = mbi_buf;
+ memset(mbi, 0, sizeof(*mbi));
+ sprintf(((char *)mbi) + sizeof(*mbi), "%s", command_line);
+ sprintf(((char *)mbi) + sizeof(*mbi) + command_line_len, "%s",
+ BOOTLOADER " " BOOTLOADER_VERSION);
+ mbi->flags = MB_INFO_CMDLINE | MB_INFO_BOOT_LOADER_NAME;
+ /* We'll relocate these to absolute addresses later. For now,
+ * all addresses within the first segment are relative to the
+ * start of the MBI. */
+ mbi->cmdline = sizeof(*mbi);
+ mbi->boot_loader_name = sizeof(*mbi) + command_line_len;
+
+ /* Memory map */
+ range = info->memory_range;
+ ranges = info->memory_ranges;
+ mmap = xmalloc(ranges * sizeof(*mmap));
+ for (i=0; i<ranges; i++) {
+ unsigned long long length;
+ length = range[i].end - range[i].start + 1;
+ /* Translate bzImage mmap to multiboot-speak */
+ mmap[i].size = sizeof(mmap[i]) - 4;
+ mmap[i].base_addr_low = range[i].start & 0xffffffff;
+ mmap[i].base_addr_high = range[i].start >> 32;
+ mmap[i].length_low = length & 0xffffffff;
+ mmap[i].length_high = length >> 32;
+ switch (range[i].type) {
+ case RANGE_RAM:
+ mmap[i].Type = 1; /* RAM */
+ /*
+ * Is this the "low" memory? Can't just test
+ * against zero, because Linux protects (and
+ * hides) the first few pages of physical
+ * memory.
+ */
+
+ if ((range[i].start <= 64*1024)
+ && (range[i].end > mem_lower)) {
+ range[i].start = 0;
+ mem_lower = range[i].end;
+ }
+ /* Is this the "high" memory? */
+ if ((range[i].start <= 0x100000)
+ && (range[i].end > mem_upper + 0x100000))
+ mem_upper = range[i].end - 0x100000;
+ break;
+ case RANGE_ACPI:
+ mmap[i].Type = 3;
+ break;
+ case RANGE_ACPI_NVS:
+ mmap[i].Type = 4;
+ break;
+ case RANGE_RESERVED:
+ default:
+ mmap[i].Type = 2; /* Not RAM (reserved) */
+ }
+ }
+
+ if (mbh->flags & MULTIBOOT_MEMORY_INFO) {
+ /* Provide a copy of the memory map to the kernel */
+
+ mbi->flags |= MB_INFO_MEMORY | MB_INFO_MEM_MAP;
+
+ freespace = add_buffer(info,
+ mmap, ranges * sizeof(*mmap), ranges * sizeof(*mmap),
+ 4, 0, 0xFFFFFFFFUL, 1);
+
+ mbi->mmap_addr = freespace;
+ mbi->mmap_length = ranges * sizeof(*mmap);
+
+ /* For kernels that care naught for fancy memory maps
+ * and just want the size of low and high memory */
+ mbi->mem_lower = MIN(mem_lower>>10, 0xffffffff);
+ mbi->mem_upper = MIN(mem_upper>>10, 0xffffffff);
+
+ /* done */
+ }
+
+ /* Video */
+ if (mbh->flags & MULTIBOOT_VIDEO_MODE) {
+ if (framebuffer_info(mbi) < 0)
+ fprintf(stderr, "not providing framebuffer information.\n");
+ }
+
+ /* Load modules */
+ if (modules) {
+ char *mod_filename, *mod_command_line, *mod_clp, *buf;
+ off_t mod_size;
+
+ /* We'll relocate this to an absolute address later */
+ mbi->mods_addr = mbi_bytes;
+ mbi->mods_count = 0;
+ mbi->flags |= MB_INFO_MODS;
+
+ /* Add room for the module descriptors to the MBI buffer */
+ mbi_bytes += (sizeof(*modp) * modules)
+ + mod_command_line_space;
+ mbi_buf = xrealloc(mbi_buf, mbi_bytes);
+
+ /* mbi might have moved */
+ mbi = mbi_buf;
+ /* module descriptors go in the newly added space */
+ modp = ((void *)mbi) + mbi->mods_addr;
+ /* module command lines go after the descriptors */
+ mod_clp = ((void *)modp) + (sizeof(*modp) * modules);
+
+ /* Go back and parse the module command lines */
+ optind = opterr = 1;
+ while((opt = getopt_long(argc, argv,
+ short_options, options, 0)) != -1) {
+ if (opt != OPT_MOD) continue;
+
+ /* Split module filename from command line */
+ mod_command_line = mod_filename = optarg;
+ if ((cp = strchr(mod_filename, ' ')) != NULL) {
+ /* See as I discard the 'const' modifier */
+ *cp = '\0';
+ }
+
+ /* Load the module */
+ buf = slurp_decompress_file(mod_filename, &mod_size);
+
+ if (cp != NULL) *cp = ' ';
+
+ /* Pick the next aligned spot to load it in */
+ freespace = add_buffer(info,
+ buf, mod_size, mod_size,
+ getpagesize(), 0, 0xffffffffUL, 1);
+
+ /* Add the module command line */
+ sprintf(mod_clp, "%s", mod_command_line);
+
+ modp->mod_start = freespace;
+ modp->mod_end = freespace + mod_size;
+ modp->cmdline = (void *)mod_clp - (void *)mbi;
+ modp->pad = 0;
+
+ /* Done */
+ mbi->mods_count++;
+ mod_clp += strlen(mod_clp) + 1;
+ modp++;
+ }
+ }
+
+ /* Find a place for the MBI to live */
+ if (sort_segments(info) < 0) {
+ result = -1;
+ goto out;
+ }
+ mbi_base = add_buffer(info,
+ mbi_buf, mbi_bytes, mbi_bytes, 4, 0, 0xFFFFFFFFUL, 1);
+
+ /* Relocate offsets in the MBI to absolute addresses */
+ mbi_offset = mbi_base;
+ modp = ((void *)mbi) + mbi->mods_addr;
+ for (u = 0; u < mbi->mods_count; u++) {
+ modp[u].cmdline += mbi_offset;
+ }
+ mbi->mods_addr += mbi_offset;
+ mbi->cmdline += mbi_offset;
+ mbi->boot_loader_name += mbi_offset;
+
+ /* Specify the initial CPU state and copy the setup code */
+ elf_rel_get_symbol(&info->rhdr, "entry32_regs", &regs, sizeof(regs));
+ regs.eax = 0x2BADB002;
+ regs.ebx = mbi_offset;
+ regs.eip = entry;
+ elf_rel_set_symbol(&info->rhdr, "entry32_regs", &regs, sizeof(regs));
+
+out:
+ free(command_line);
+ return result;
+}
+
+/*
+ * EOF (kexec-multiboot-x86.c)
+ */
diff --git a/kexec/arch/i386/kexec-nbi.c b/kexec/arch/i386/kexec-nbi.c
new file mode 100644
index 0000000..8eb2154
--- /dev/null
+++ b/kexec/arch/i386/kexec-nbi.c
@@ -0,0 +1,249 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2005 Eric Biederman (ebiederm@xmission.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-elf-boot.h"
+#include "kexec-x86.h"
+#include <arch/options.h>
+
+struct segheader
+{
+ uint8_t length;
+ uint8_t vendortag;
+ uint8_t reserved;
+ uint8_t flags;
+#define NBI_SEG 0x3
+#define NBI_SEG_ABSOLUTE 0
+#define NBI_SEG_APPEND 1
+#define NBI_SEG_NEGATIVE 2
+#define NBI_SEG_PREPEND 3
+#define NBI_LAST_SEG (1 << 2)
+ uint32_t loadaddr;
+ uint32_t imglength;
+ uint32_t memlength;
+};
+
+struct imgheader
+{
+#define NBI_MAGIC "\x36\x13\x03\x1b"
+ uint8_t magic[4];
+#define NBI_RETURNS (1 << 8)
+#define NBI_ENTRY32 (1 << 31)
+ uint32_t length; /* and flags */
+ struct { uint16_t bx, ds; } segoff;
+ union {
+ struct { uint16_t ip, cs; } segoff;
+ uint32_t linear;
+ } execaddr;
+};
+
+
+static const int probe_debug = 0;
+
+int nbi_probe(const char *buf, off_t len)
+{
+ struct imgheader hdr;
+ struct segheader seg;
+ off_t seg_off;
+ /* If we don't have enough data give up */
+ if (((uintmax_t)len < (uintmax_t)sizeof(hdr)) || (len < 512)) {
+ return -1;
+ }
+ memcpy(&hdr, buf, sizeof(hdr));
+ if (memcmp(hdr.magic, NBI_MAGIC, sizeof(hdr.magic)) != 0) {
+ return -1;
+ }
+ /* Ensure we have a properly sized header */
+ if (((hdr.length & 0xf)*4) != sizeof(hdr)) {
+ if (probe_debug) {
+ fprintf(stderr, "NBI: Bad vendor header size\n");
+ }
+ return -1;
+ }
+ /* Ensure the vendor header is not too large.
+ * This can't actually happen but....
+ */
+ if ((((hdr.length & 0xf0) >> 4)*4) > (512 - sizeof(hdr))) {
+ if (probe_debug) {
+ fprintf(stderr, "NBI: vendor headr too large\n");
+ }
+ return -1;
+ }
+ /* Reserved bits are set in the image... */
+ if ((hdr.length & 0x7ffffe00)) {
+ if (probe_debug) {
+ fprintf(stderr, "NBI: Reserved header bits set\n");
+ }
+ return -1;
+ }
+ /* If the image can return refuse to load it */
+ if (hdr.length & (1 << 8)) {
+ if (probe_debug) {
+ printf("NBI: image wants to return\n");
+ }
+ return -1;
+ }
+ /* Now verify the segments all fit within 512 bytes */
+ seg_off = (((hdr.length & 0xf0) >> 4) + (hdr.length & 0x0f)) << 2;
+ do {
+ memcpy(&seg, buf + seg_off, sizeof(seg));
+ if ((seg.length & 0xf) != 4) {
+ if (probe_debug) {
+ fprintf(stderr, "NBI: Invalid segment length\n");
+ }
+ return -1;
+ }
+ seg_off += ((seg.length & 0xf) + ((seg.length >> 4) & 0xf)) << 2;
+ if (seg.flags & 0xf8) {
+ if (probe_debug) {
+ fprintf(stderr, "NBI: segment reserved flags set\n");
+ }
+ return -1;
+ }
+ if ((seg.flags & NBI_SEG) == NBI_SEG_NEGATIVE) {
+ if (probe_debug) {
+ fprintf(stderr, "NBI: negative segment addresses not supported\n");
+ }
+ return -1;
+ }
+ if (seg_off > 512) {
+ if (probe_debug) {
+ fprintf(stderr, "NBI: segment outside 512 header\n");
+ }
+ return -1;
+ }
+ } while(!(seg.flags & NBI_LAST_SEG));
+ return 0;
+}
+
+void nbi_usage(void)
+{
+ printf( "\n"
+ );
+}
+
+int nbi_load(int argc, char **argv, const char *buf, off_t UNUSED(len),
+ struct kexec_info *info)
+{
+ struct imgheader hdr;
+ struct segheader seg;
+ off_t seg_off;
+ off_t file_off;
+ uint32_t last0, last1;
+ int opt;
+
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { 0, 0, NULL, 0 },
+ };
+
+ static const char short_options[] = KEXEC_OPT_STR "";
+
+ /*
+ * Parse the command line arguments
+ */
+ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch(opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ }
+ }
+ /* Get a copy of the header */
+ memcpy(&hdr, buf, sizeof(hdr));
+
+ /* Load the first 512 bytes */
+ add_segment(info, buf + 0, 512,
+ (hdr.segoff.ds << 4) + hdr.segoff.bx, 512);
+
+ /* Initialize variables */
+ file_off = 512;
+ last0 = (hdr.segoff.ds << 4) + hdr.segoff.bx;
+ last1 = last0 + 512;
+
+ /* Load the segments */
+ seg_off = (((hdr.length & 0xf0) >> 4) + (hdr.length & 0x0f)) << 2;
+ do {
+ uint32_t loadaddr;
+ memcpy(&seg, buf + seg_off, sizeof(seg));
+ seg_off += ((seg.length & 0xf) + ((seg.length >> 4) & 0xf)) << 2;
+ if ((seg.flags & NBI_SEG) == NBI_SEG_ABSOLUTE) {
+ loadaddr = seg.loadaddr;
+ }
+ else if ((seg.flags & NBI_SEG) == NBI_SEG_APPEND) {
+ loadaddr = last1 + seg.loadaddr;
+ }
+#if 0
+ else if ((seg.flags & NBI_SEG) == NBI_SEG_NEGATIVE) {
+ loadaddr = memsize - seg.loadaddr;
+ }
+#endif
+ else if ((seg.flags & NBI_SEG) == NBI_SEG_PREPEND) {
+ loadaddr = last0 - seg.loadaddr;
+ }
+ else {
+ printf("warning: unhandled segment of type %0x\n",
+ seg.flags & NBI_SEG);
+ continue;
+ }
+ add_segment(info, buf + file_off, seg.imglength,
+ loadaddr, seg.memlength);
+ last0 = loadaddr;
+ last1 = last0 + seg.memlength;
+ file_off += seg.imglength;
+ } while(!(seg.flags & NBI_LAST_SEG));
+
+ if (hdr.length & NBI_ENTRY32) {
+ struct entry32_regs regs32;
+ /* Initialize the registers */
+ elf_rel_get_symbol(&info->rhdr, "entry32_regs32", &regs32, sizeof(regs32));
+ regs32.eip = hdr.execaddr.linear;
+ elf_rel_set_symbol(&info->rhdr, "entry32_regs32", &regs32, sizeof(regs32));
+ }
+ else {
+ struct entry32_regs regs32;
+ struct entry16_regs regs16;
+
+ /* Initialize the 16 bit registers */
+ elf_rel_get_symbol(&info->rhdr, "entry16_regs", &regs16, sizeof(regs16));
+ regs16.cs = hdr.execaddr.segoff.cs;
+ regs16.ip = hdr.execaddr.segoff.ip;
+ elf_rel_set_symbol(&info->rhdr, "entry16_regs", &regs16, sizeof(regs16));
+
+ /* Initialize the 32 bit registers */
+ elf_rel_get_symbol(&info->rhdr, "entry32_regs", &regs32, sizeof(regs32));
+ regs32.eip = elf_rel_get_addr(&info->rhdr, "entry16");
+ elf_rel_set_symbol(&info->rhdr, "entry32_regs", &regs32, sizeof(regs32));
+ }
+ return 0;
+}
diff --git a/kexec/arch/i386/kexec-x86-common.c b/kexec/arch/i386/kexec-x86-common.c
new file mode 100644
index 0000000..ffc95a9
--- /dev/null
+++ b/kexec/arch/i386/kexec-x86-common.c
@@ -0,0 +1,444 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define _XOPEN_SOURCE 600
+#define _BSD_SOURCE
+#define _DEFAULT_SOURCE
+
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "../../firmware_memmap.h"
+#include "../../crashdump.h"
+#include "../../kexec-xen.h"
+#include "kexec-x86.h"
+#include "x86-linux-setup.h"
+
+/* Used below but not present in (older?) xenctrl.h */
+#ifndef E820_PMEM
+#define E820_PMEM 7
+#define E820_PRAM 12
+#endif
+
+/*
+ * The real mode IVT ends at 0x400.
+ * See https://wiki.osdev.org/Interrupt_Vector_Table.
+ */
+#define REALMODE_IVT_END 0x400
+
+static struct memory_range memory_range[MAX_MEMORY_RANGES];
+
+/**
+ * The old /proc/iomem parsing code.
+ *
+ * @param[out] range pointer that will be set to an array that holds the
+ * memory ranges
+ * @param[out] ranges number of ranges valid in @p range
+ *
+ * @return 0 on success, any other value on failure.
+ */
+static int get_memory_ranges_proc_iomem(struct memory_range **range, int *ranges)
+{
+ const char *iomem= proc_iomem();
+ int memory_ranges = 0;
+ char line[MAX_LINE];
+ FILE *fp;
+ fp = fopen(iomem, "r");
+ if (!fp) {
+ fprintf(stderr, "Cannot open %s: %s\n",
+ iomem, strerror(errno));
+ return -1;
+ }
+ while(fgets(line, sizeof(line), fp) != 0) {
+ unsigned long long start, end;
+ char *str;
+ int type;
+ int consumed;
+ int count;
+ if (memory_ranges >= MAX_MEMORY_RANGES)
+ break;
+ count = sscanf(line, "%llx-%llx : %n",
+ &start, &end, &consumed);
+ if (count != 2)
+ continue;
+ str = line + consumed;
+
+ dbgprintf("%016Lx-%016Lx : %s", start, end, str);
+
+ if (memcmp(str, "System RAM\n", 11) == 0) {
+ type = RANGE_RAM;
+ }
+ else if (strncasecmp(str, "reserved\n", 9) == 0) {
+ type = RANGE_RESERVED;
+ }
+ else if (memcmp(str, "ACPI Tables\n", 12) == 0) {
+ type = RANGE_ACPI;
+ }
+ else if (memcmp(str, "ACPI Non-volatile Storage\n", 26) == 0) {
+ type = RANGE_ACPI_NVS;
+ }
+ else if (memcmp(str, "Persistent Memory (legacy)\n", 27) == 0) {
+ type = RANGE_PRAM;
+ }
+ else if (memcmp(str, "Persistent Memory\n", 18) == 0) {
+ type = RANGE_PMEM;
+ }
+ else {
+ continue;
+ }
+ memory_range[memory_ranges].start = start;
+ memory_range[memory_ranges].end = end;
+ memory_range[memory_ranges].type = type;
+
+ dbgprintf("%016Lx-%016Lx : %x\n", start, end, type);
+
+ memory_ranges++;
+ }
+ fclose(fp);
+ *range = memory_range;
+ *ranges = memory_ranges;
+ return 0;
+}
+
+/**
+ * Calls the architecture independent get_firmware_memmap_ranges() to parse
+ * /sys/firmware/memmap and then do some x86 only modifications.
+ *
+ * @param[out] range pointer that will be set to an array that holds the
+ * memory ranges
+ * @param[out] ranges number of ranges valid in @p range
+ *
+ * @return 0 on success, any other value on failure.
+ */
+static int get_memory_ranges_sysfs(struct memory_range **range, int *ranges)
+{
+ int ret;
+ size_t range_number = MAX_MEMORY_RANGES;
+
+ ret = get_firmware_memmap_ranges(memory_range, &range_number);
+ if (ret != 0) {
+ fprintf(stderr, "Parsing the /sys/firmware memory map failed. "
+ "Falling back to /proc/iomem.\n");
+ return get_memory_ranges_proc_iomem(range, ranges);
+ }
+
+ *range = memory_range;
+ *ranges = range_number;
+
+ return 0;
+}
+
+#ifdef HAVE_LIBXENCTRL
+unsigned xen_e820_to_kexec_type(uint32_t type)
+{
+ switch (type) {
+ case E820_RAM:
+ return RANGE_RAM;
+ case E820_ACPI:
+ return RANGE_ACPI;
+ case E820_NVS:
+ return RANGE_ACPI_NVS;
+ case E820_PMEM:
+ return RANGE_PMEM;
+ case E820_PRAM:
+ return RANGE_PRAM;
+ case E820_RESERVED:
+ default:
+ return RANGE_RESERVED;
+ }
+}
+
+/**
+ * Memory map detection for Xen.
+ *
+ * @param[out] range pointer that will be set to an array that holds the
+ * memory ranges
+ * @param[out] ranges number of ranges valid in @p range
+ *
+ * @return 0 on success, any other value on failure.
+ */
+static int get_memory_ranges_xen(struct memory_range **range, int *ranges)
+{
+ int rc, ret = -1;
+ struct e820entry e820entries[MAX_MEMORY_RANGES];
+ unsigned int i;
+ xc_interface *xc;
+
+ xc = xc_interface_open(NULL, NULL, 0);
+
+ if (!xc) {
+ fprintf(stderr, "%s: Failed to open Xen control interface\n", __func__);
+ return -1;
+ }
+
+ rc = xc_get_machine_memory_map(xc, e820entries, MAX_MEMORY_RANGES);
+
+ if (rc < 0) {
+ fprintf(stderr, "%s: xc_get_machine_memory_map: %s\n", __func__, strerror(rc));
+ goto err;
+ }
+
+ for (i = 0; i < rc; ++i) {
+ memory_range[i].start = e820entries[i].addr;
+ memory_range[i].end = e820entries[i].addr + e820entries[i].size - 1;
+ memory_range[i].type = xen_e820_to_kexec_type(e820entries[i].type);
+ }
+
+ qsort(memory_range, rc, sizeof(struct memory_range), compare_ranges);
+
+ *range = memory_range;
+ *ranges = rc;
+
+ ret = 0;
+
+err:
+ xc_interface_close(xc);
+
+ return ret;
+}
+#else
+static int get_memory_ranges_xen(struct memory_range **range, int *ranges)
+{
+ return 0;
+}
+#endif /* HAVE_LIBXENCTRL */
+
+static void remove_range(struct memory_range *range, int nr_ranges, int index)
+{
+ int i, j;
+
+ for (i = index; i < (nr_ranges-1); i++) {
+ j = i+1;
+ range[i] = range[j];
+ }
+}
+
+/**
+ * Verifies and corrects any overlapping ranges.
+ * The ranges array is assumed to be sorted already.
+ *
+ * @param[out] range pointer that will be set to an array that holds the
+ * memory ranges
+ * @param[out] ranges number of ranges valid in @p range
+ *
+ * @return 0 on success, any other value on failure.
+ */
+static int fixup_memory_ranges(struct memory_range **range, int *ranges)
+{
+ int i;
+ int j;
+ int change_made;
+ int nr_ranges = *ranges;
+ struct memory_range *rp = *range;
+
+again:
+ change_made = 0;
+ for (i = 0; i < (nr_ranges-1); i++) {
+ j = i+1;
+ if (rp[i].start > rp[j].start) {
+ fprintf(stderr, "memory out of order!!\n");
+ return 1;
+ }
+
+ if (rp[i].type != rp[j].type)
+ continue;
+
+ if (rp[i].start == rp[j].start) {
+ if (rp[i].end >= rp[j].end) {
+ remove_range(rp, nr_ranges, j);
+ nr_ranges--;
+ change_made++;
+ } else {
+ remove_range(rp, nr_ranges, i);
+ nr_ranges--;
+ change_made++;
+ }
+ } else {
+ if (rp[i].end > rp[j].start) {
+ if (rp[i].end < rp[j].end) {
+ rp[j].start = rp[i].end;
+ change_made++;
+ } else if (rp[i].end >= rp[j].end) {
+ remove_range(rp, nr_ranges, j);
+ nr_ranges--;
+ change_made++;
+ }
+ }
+ }
+ }
+
+ /* fixing/removing an entry may make it wrong relative to the next */
+ if (change_made)
+ goto again;
+
+ *ranges = nr_ranges;
+ return 0;
+}
+
+/**
+ * Detect the add_efi_memmap kernel parameter.
+ *
+ * On some EFI-based systems, the e820 map is empty, or does not contain a
+ * complete memory map. The add_efi_memmap parameter adds these entries to
+ * the kernel's memory map, but does not add them under sysfs, which causes
+ * kexec to fail in a way similar to how it does not work on Xen.
+ *
+ * @return 1 if parameter is present, 0 if not or if an error occurs.
+ */
+int efi_map_added( void ) {
+ char buf[512];
+ FILE *fp = fopen( "/proc/cmdline", "r" );
+ if( fp ) {
+ fgets( buf, 512, fp );
+ fclose( fp );
+ return strstr( buf, "add_efi_memmap" ) != NULL;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * Return a sorted list of memory ranges.
+ *
+ * If we have the /sys/firmware/memmap interface, then use that. If not,
+ * or if parsing of that fails, use /proc/iomem as fallback.
+ *
+ * @param[out] range pointer that will be set to an array that holds the
+ * memory ranges
+ * @param[out] ranges number of ranges valid in @p range
+ * @param[in] kexec_flags the kexec_flags to determine if we load a normal
+ * or a crashdump kernel
+ *
+ * @return 0 on success, any other value on failure.
+ */
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags)
+{
+ int ret, i;
+
+ if (!efi_map_added() && !xen_present() && have_sys_firmware_memmap()) {
+ ret = get_memory_ranges_sysfs(range, ranges);
+ if (!ret)
+ ret = fixup_memory_ranges(range, ranges);
+ } else if (xen_present()) {
+ ret = get_memory_ranges_xen(range, ranges);
+ if (!ret)
+ ret = fixup_memory_ranges(range, ranges);
+ } else
+ ret = get_memory_ranges_proc_iomem(range, ranges);
+
+ /*
+ * get_memory_ranges_sysfs(), get_memory_ranges_proc_iomem() and
+ * get_memory_ranges_xen() have already printed an error message,
+ * so fail silently here.
+ */
+ if (ret != 0)
+ return ret;
+
+ /* Don't report the interrupt table as ram */
+ for (i = 0; i < *ranges; i++) {
+ if ((*range)[i].type == RANGE_RAM &&
+ ((*range)[i].start < REALMODE_IVT_END)) {
+ (*range)[i].start = REALMODE_IVT_END;
+ break;
+ }
+ }
+
+ /*
+ * Redefine the memory region boundaries if kernel
+ * exports the limits and if it is panic kernel.
+ * Override user values only if kernel exported values are
+ * subset of user defined values.
+ */
+ if ((kexec_flags & KEXEC_ON_CRASH) &&
+ !(kexec_flags & KEXEC_PRESERVE_CONTEXT)) {
+ uint64_t start, end;
+
+ ret = get_crash_kernel_load_range(&start, &end);
+ if (ret != 0) {
+ fprintf(stderr, "get_crash_kernel_load_range failed.\n");
+ return -1;
+ }
+
+ if (start > mem_min)
+ mem_min = start;
+ if (end < mem_max)
+ mem_max = end;
+ }
+
+ dbgprint_mem_range("MEMORY RANGES", *range, *ranges);
+
+ return ret;
+}
+
+static uint64_t bootparam_get_acpi_rsdp(void) {
+ uint64_t acpi_rsdp = 0;
+ off_t offset = offsetof(struct x86_linux_param_header, acpi_rsdp_addr);
+
+ if (get_bootparam(&acpi_rsdp, offset, sizeof(acpi_rsdp)))
+ return 0;
+
+ return acpi_rsdp;
+}
+
+static uint64_t efi_get_acpi_rsdp(void) {
+ FILE *fp;
+ char line[MAX_LINE], *s;
+ uint64_t acpi_rsdp = 0;
+
+ fp = fopen("/sys/firmware/efi/systab", "r");
+ if (!fp)
+ return acpi_rsdp;
+
+ while(fgets(line, sizeof(line), fp) != 0) {
+ /* ACPI20= always goes before ACPI= */
+ if ((strstr(line, "ACPI20=")) || (strstr(line, "ACPI="))) {
+ s = strchr(line, '=') + 1;
+ sscanf(s, "0x%lx", &acpi_rsdp);
+ break;
+ }
+ }
+ fclose(fp);
+
+ return acpi_rsdp;
+}
+
+uint64_t get_acpi_rsdp(void)
+{
+ uint64_t acpi_rsdp = 0;
+
+ acpi_rsdp = bootparam_get_acpi_rsdp();
+
+ if (!acpi_rsdp)
+ acpi_rsdp = efi_get_acpi_rsdp();
+
+ return acpi_rsdp;
+}
diff --git a/kexec/arch/i386/kexec-x86.c b/kexec/arch/i386/kexec-x86.c
new file mode 100644
index 0000000..444cb69
--- /dev/null
+++ b/kexec/arch/i386/kexec-x86.c
@@ -0,0 +1,210 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-syscall.h"
+#include "../../firmware_memmap.h"
+#include "kexec-x86.h"
+#include "crashdump-x86.h"
+#include <arch/options.h>
+
+struct file_type file_type[] = {
+ { "multiboot-x86", multiboot_x86_probe, multiboot_x86_load,
+ multiboot_x86_usage },
+ { "multiboot2-x86", multiboot2_x86_probe, multiboot2_x86_load,
+ multiboot2_x86_usage },
+ { "elf-x86", elf_x86_probe, elf_x86_load, elf_x86_usage },
+ { "bzImage", bzImage_probe, bzImage_load, bzImage_usage },
+ { "beoboot-x86", beoboot_probe, beoboot_load, beoboot_usage },
+ { "nbi-x86", nbi_probe, nbi_load, nbi_usage },
+};
+int file_types = sizeof(file_type)/sizeof(file_type[0]);
+
+
+void arch_usage(void)
+{
+ printf(
+ " --reset-vga Attempt to reset a standard vga device\n"
+ " --serial=<port> Specify the serial port for debug output\n"
+ " --serial-baud=<baud_rate> Specify the serial port baud rate\n"
+ " --console-vga Enable the vga console\n"
+ " --console-serial Enable the serial console\n"
+ " --elf32-core-headers Prepare core headers in ELF32 format\n"
+ " --elf64-core-headers Prepare core headers in ELF64 format\n"
+ " --pass-memmap-cmdline Pass memory map via command line in kexec on panic case\n"
+ " --noefi Disable efi support\n"
+ );
+}
+
+struct arch_options_t arch_options = {
+ .reset_vga = 0,
+ .serial_base = 0x3f8,
+ .serial_baud = 0,
+ .console_vga = 0,
+ .console_serial = 0,
+ .core_header_type = CORE_TYPE_UNDEF,
+ .pass_memmap_cmdline = 0,
+ .noefi = 0,
+};
+
+int arch_process_options(int argc, char **argv)
+{
+ static const struct option options[] = {
+ KEXEC_ALL_OPTIONS
+ { 0, 0, NULL, 0 },
+ };
+ static const char short_options[] = KEXEC_ALL_OPT_STR;
+ int opt;
+ unsigned long value;
+ char *end;
+
+ opterr = 0; /* Don't complain about unrecognized options here */
+ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch(opt) {
+ default:
+ break;
+ case OPT_RESET_VGA:
+ arch_options.reset_vga = 1;
+ break;
+ case OPT_CONSOLE_VGA:
+ arch_options.console_vga = 1;
+ break;
+ case OPT_CONSOLE_SERIAL:
+ arch_options.console_serial = 1;
+ break;
+ case OPT_SERIAL:
+ value = ULONG_MAX;
+ if (strcmp(optarg, "ttyS0") == 0) {
+ value = 0x3f8;
+ }
+ else if (strcmp(optarg, "ttyS1") == 0) {
+ value = 0x2f8;
+ }
+ else if (strncmp(optarg, "0x", 2) == 0) {
+ value = strtoul(optarg +2, &end, 16);
+ if (*end != '\0') {
+ value = ULONG_MAX;
+ }
+ }
+ if (value >= 65536) {
+ fprintf(stderr, "Bad serial port base '%s'\n",
+ optarg);
+ usage();
+ return -1;
+
+ }
+ arch_options.serial_base = value;
+ break;
+ case OPT_SERIAL_BAUD:
+ value = strtoul(optarg, &end, 0);
+ if ((value > 115200) || ((115200 %value) != 0) ||
+ (value < 9600) || (*end))
+ {
+ fprintf(stderr, "Bad serial port baud rate '%s'\n",
+ optarg);
+ usage();
+ return -1;
+
+ }
+ arch_options.serial_baud = value;
+ break;
+ case OPT_ELF32_CORE:
+ arch_options.core_header_type = CORE_TYPE_ELF32;
+ break;
+ case OPT_ELF64_CORE:
+ arch_options.core_header_type = CORE_TYPE_ELF64;
+ break;
+ case OPT_PASS_MEMMAP_CMDLINE:
+ arch_options.pass_memmap_cmdline = 1;
+ break;
+ case OPT_NOEFI:
+ arch_options.noefi = 1;
+ break;
+ }
+ }
+ /* Reset getopt for the next pass; called in other source modules */
+ opterr = 1;
+ optind = 1;
+ return 0;
+}
+
+const struct arch_map_entry arches[] = {
+ /* For compatibility with older patches
+ * use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_386 here.
+ */
+ { "i386", KEXEC_ARCH_DEFAULT },
+ { "i486", KEXEC_ARCH_DEFAULT },
+ { "i586", KEXEC_ARCH_DEFAULT },
+ { "i686", KEXEC_ARCH_DEFAULT },
+ { "x86_64", KEXEC_ARCH_X86_64 },
+ { 0, 0 },
+};
+
+int arch_compat_trampoline(struct kexec_info *info)
+{
+ if ((info->kexec_flags & KEXEC_ARCH_MASK) == KEXEC_ARCH_X86_64)
+ {
+ if (!info->rhdr.e_shdr) {
+ fprintf(stderr,
+ "A trampoline is required for cross architecture support\n");
+ return -1;
+ }
+ elf_rel_set_symbol(&info->rhdr, "compat_x86_64_entry32",
+ &info->entry, sizeof(info->entry));
+
+ info->entry = (void *)elf_rel_get_addr(&info->rhdr, "compat_x86_64");
+ }
+ return 0;
+}
+
+void arch_update_purgatory(struct kexec_info *info)
+{
+ uint8_t panic_kernel = 0;
+
+ elf_rel_set_symbol(&info->rhdr, "reset_vga",
+ &arch_options.reset_vga, sizeof(arch_options.reset_vga));
+ elf_rel_set_symbol(&info->rhdr, "serial_base",
+ &arch_options.serial_base, sizeof(arch_options.serial_base));
+ elf_rel_set_symbol(&info->rhdr, "serial_baud",
+ &arch_options.serial_baud, sizeof(arch_options.serial_baud));
+ elf_rel_set_symbol(&info->rhdr, "console_vga",
+ &arch_options.console_vga, sizeof(arch_options.console_vga));
+ elf_rel_set_symbol(&info->rhdr, "console_serial",
+ &arch_options.console_serial, sizeof(arch_options.console_serial));
+ elf_rel_set_symbol(&info->rhdr, "backup_src_start",
+ &info->backup_src_start, sizeof(info->backup_src_start));
+ elf_rel_set_symbol(&info->rhdr, "backup_src_size",
+ &info->backup_src_size, sizeof(info->backup_src_size));
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ panic_kernel = 1;
+ elf_rel_set_symbol(&info->rhdr, "backup_start",
+ &info->backup_start, sizeof(info->backup_start));
+ }
+ elf_rel_set_symbol(&info->rhdr, "panic_kernel",
+ &panic_kernel, sizeof(panic_kernel));
+}
diff --git a/kexec/arch/i386/kexec-x86.h b/kexec/arch/i386/kexec-x86.h
new file mode 100644
index 0000000..46e2898
--- /dev/null
+++ b/kexec/arch/i386/kexec-x86.h
@@ -0,0 +1,97 @@
+#ifndef KEXEC_X86_H
+#define KEXEC_X86_H
+
+#define MAX_MEMORY_RANGES 2048
+
+enum coretype {
+ CORE_TYPE_UNDEF = 0,
+ CORE_TYPE_ELF32 = 1,
+ CORE_TYPE_ELF64 = 2
+};
+
+extern unsigned char compat_x86_64[];
+extern uint32_t compat_x86_64_size, compat_x86_64_entry32;
+
+struct entry32_regs {
+ uint32_t eax;
+ uint32_t ebx;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t esp;
+ uint32_t ebp;
+ uint32_t eip;
+};
+
+struct entry16_regs {
+ uint32_t eax;
+ uint32_t ebx;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t esp;
+ uint32_t ebp;
+ uint16_t ds;
+ uint16_t es;
+ uint16_t ss;
+ uint16_t fs;
+ uint16_t gs;
+ uint16_t ip;
+ uint16_t cs;
+ uint16_t pad;
+};
+
+struct arch_options_t {
+ uint8_t reset_vga;
+ uint16_t serial_base;
+ uint32_t serial_baud;
+ uint8_t console_vga;
+ uint8_t console_serial;
+ enum coretype core_header_type;
+ uint8_t pass_memmap_cmdline;
+ uint8_t noefi;
+ uint8_t reuse_video_type;
+};
+
+int multiboot_x86_probe(const char *buf, off_t len);
+int multiboot_x86_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void multiboot_x86_usage(void);
+
+int multiboot2_x86_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void multiboot2_x86_usage(void);
+int multiboot2_x86_probe(const char *buf, off_t buf_len);
+
+int elf_x86_probe(const char *buf, off_t len);
+int elf_x86_any_probe(const char *buf, off_t len, enum coretype arch);
+int elf_x86_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void elf_x86_usage(void);
+
+int bzImage_probe(const char *buf, off_t len);
+int bzImage_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void bzImage_usage(void);
+int do_bzImage_load(struct kexec_info *info,
+ const char *kernel, off_t kernel_len,
+ const char *command_line, off_t command_line_len,
+ const char *initrd, off_t initrd_len,
+ const char *dtb, off_t dtb_len,
+ int real_mode_entry);
+
+int beoboot_probe(const char *buf, off_t len);
+int beoboot_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void beoboot_usage(void);
+
+int nbi_probe(const char *buf, off_t len);
+int nbi_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void nbi_usage(void);
+
+extern unsigned xen_e820_to_kexec_type(uint32_t type);
+extern uint64_t get_acpi_rsdp(void);
+#endif /* KEXEC_X86_H */
diff --git a/kexec/arch/i386/x86-linux-setup.c b/kexec/arch/i386/x86-linux-setup.c
new file mode 100644
index 0000000..9a281dc
--- /dev/null
+++ b/kexec/arch/i386/x86-linux-setup.c
@@ -0,0 +1,971 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#define _GNU_SOURCE
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/random.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/fb.h>
+#include <linux/screen_info.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <mntent.h>
+#include <x86/x86-linux.h>
+#include "../../kexec.h"
+#include "kexec-x86.h"
+#include "x86-linux-setup.h"
+#include "../../kexec/kexec-syscall.h"
+
+#ifndef VIDEO_CAPABILITY_64BIT_BASE
+#define VIDEO_CAPABILITY_64BIT_BASE (1 << 1) /* Frame buffer base is 64-bit */
+#endif
+
+void init_linux_parameters(struct x86_linux_param_header *real_mode)
+{
+ /* Fill in the values that are usually provided by the kernel. */
+
+ /* Boot block magic */
+ memcpy(real_mode->header_magic, "HdrS", 4);
+ real_mode->protocol_version = 0x0206;
+ real_mode->initrd_addr_max = DEFAULT_INITRD_ADDR_MAX;
+ real_mode->cmdline_size = COMMAND_LINE_SIZE;
+}
+
+void setup_linux_bootloader_parameters_high(
+ struct kexec_info *info, struct x86_linux_param_header *real_mode,
+ unsigned long real_mode_base, unsigned long cmdline_offset,
+ const char *cmdline, off_t cmdline_len,
+ const char *initrd_buf, off_t initrd_size, int initrd_high)
+{
+ char *cmdline_ptr;
+ unsigned long initrd_base, initrd_addr_max;
+
+ /* Say I'm a boot loader */
+ real_mode->loader_type = LOADER_TYPE_KEXEC << 4;
+
+ /* No loader flags */
+ real_mode->loader_flags = 0;
+
+ /* Find the maximum initial ramdisk address */
+ if (initrd_high)
+ initrd_addr_max = ULONG_MAX;
+ else {
+ initrd_addr_max = DEFAULT_INITRD_ADDR_MAX;
+ if (real_mode->protocol_version >= 0x0203) {
+ initrd_addr_max = real_mode->initrd_addr_max;
+ dbgprintf("initrd_addr_max is 0x%lx\n",
+ initrd_addr_max);
+ }
+ }
+
+ /* Load the initrd if we have one */
+ if (initrd_buf) {
+ initrd_base = add_buffer(info,
+ initrd_buf, initrd_size, initrd_size,
+ 4096, INITRD_BASE, initrd_addr_max, -1);
+ dbgprintf("Loaded initrd at 0x%lx size 0x%lx\n", initrd_base,
+ initrd_size);
+ } else {
+ initrd_base = 0;
+ initrd_size = 0;
+ }
+
+ /* Ramdisk address and size */
+ real_mode->initrd_start = initrd_base & 0xffffffffUL;
+ real_mode->initrd_size = initrd_size & 0xffffffffUL;
+
+ if (real_mode->protocol_version >= 0x020c &&
+ (initrd_base & 0xffffffffUL) != initrd_base)
+ real_mode->ext_ramdisk_image = initrd_base >> 32;
+
+ if (real_mode->protocol_version >= 0x020c &&
+ (initrd_size & 0xffffffffUL) != initrd_size)
+ real_mode->ext_ramdisk_size = initrd_size >> 32;
+
+ /* The location of the command line */
+ /* if (real_mode_base == 0x90000) { */
+ real_mode->cl_magic = CL_MAGIC_VALUE;
+ real_mode->cl_offset = cmdline_offset;
+ /* setup_move_size */
+ /* } */
+ if (real_mode->protocol_version >= 0x0202) {
+ unsigned long cmd_line_ptr = real_mode_base + cmdline_offset;
+
+ real_mode->cmd_line_ptr = cmd_line_ptr & 0xffffffffUL;
+ if ((real_mode->protocol_version >= 0x020c) &&
+ ((cmd_line_ptr & 0xffffffffUL) != cmd_line_ptr))
+ real_mode->ext_cmd_line_ptr = cmd_line_ptr >> 32;
+ }
+
+ /* Fill in the command line */
+ if (cmdline_len > COMMAND_LINE_SIZE) {
+ cmdline_len = COMMAND_LINE_SIZE;
+ }
+ cmdline_ptr = ((char *)real_mode) + cmdline_offset;
+ memcpy(cmdline_ptr, cmdline, cmdline_len);
+ cmdline_ptr[cmdline_len - 1] = '\0';
+}
+
+static int setup_linux_vesafb(struct x86_linux_param_header *real_mode)
+{
+ struct fb_fix_screeninfo fix;
+ struct fb_var_screeninfo var;
+ int fd;
+
+ fd = open("/dev/fb0", O_RDONLY);
+ if (-1 == fd)
+ return -1;
+
+ if (-1 == ioctl(fd, FBIOGET_FSCREENINFO, &fix))
+ goto out;
+ if (-1 == ioctl(fd, FBIOGET_VSCREENINFO, &var))
+ goto out;
+ if (0 == strcmp(fix.id, "VESA VGA")) {
+ /* VIDEO_TYPE_VLFB */
+ real_mode->orig_video_isVGA = 0x23;
+ } else if (0 == strcmp(fix.id, "EFI VGA")) {
+ /* VIDEO_TYPE_EFI */
+ real_mode->orig_video_isVGA = 0x70;
+ } else if (arch_options.reuse_video_type) {
+ int err;
+ off_t offset = offsetof(typeof(*real_mode), orig_video_isVGA);
+
+ /* blindly try old boot time video type */
+ err = get_bootparam(&real_mode->orig_video_isVGA, offset, 1);
+ if (err)
+ goto out;
+ } else {
+ real_mode->orig_video_isVGA = 0;
+ close(fd);
+ return 0;
+ }
+ close(fd);
+
+ real_mode->lfb_width = var.xres;
+ real_mode->lfb_height = var.yres;
+ real_mode->lfb_depth = var.bits_per_pixel;
+ real_mode->lfb_base = fix.smem_start & 0xffffffffUL;
+ real_mode->lfb_linelength = fix.line_length;
+ real_mode->vesapm_seg = 0;
+
+ if (fix.smem_start > 0xffffffffUL) {
+ real_mode->ext_lfb_base = fix.smem_start >> 32;
+ real_mode->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
+ }
+
+ /* FIXME: better get size from the file returned by proc_iomem() */
+ real_mode->lfb_size = (fix.smem_len + 65535) / 65536;
+ real_mode->pages = (fix.smem_len + 4095) / 4096;
+
+ if (var.bits_per_pixel > 8) {
+ real_mode->red_pos = var.red.offset;
+ real_mode->red_size = var.red.length;
+ real_mode->green_pos = var.green.offset;
+ real_mode->green_size = var.green.length;
+ real_mode->blue_pos = var.blue.offset;
+ real_mode->blue_size = var.blue.length;
+ real_mode->rsvd_pos = var.transp.offset;
+ real_mode->rsvd_size = var.transp.length;
+ }
+ return 0;
+
+ out:
+ close(fd);
+ return -1;
+}
+
+#define EDD_SYFS_DIR "/sys/firmware/edd"
+
+#define EDD_EXT_FIXED_DISK_ACCESS (1 << 0)
+#define EDD_EXT_DEVICE_LOCKING_AND_EJECTING (1 << 1)
+#define EDD_EXT_ENHANCED_DISK_DRIVE_SUPPORT (1 << 2)
+#define EDD_EXT_64BIT_EXTENSIONS (1 << 3)
+
+/*
+ * Scans one line from a given filename. Returns on success the number of
+ * items written (same like scanf()).
+ */
+static int file_scanf(const char *dir, const char *file, const char *scanf_line, ...)
+{
+ va_list argptr;
+ FILE *fp;
+ int retno;
+ char filename[PATH_MAX];
+
+ snprintf(filename, PATH_MAX, "%s/%s", dir, file);
+ filename[PATH_MAX-1] = 0;
+
+ fp = fopen(filename, "r");
+ if (!fp) {
+ return -errno;
+ }
+
+ va_start(argptr, scanf_line);
+ retno = vfscanf(fp, scanf_line, argptr);
+ va_end(argptr);
+
+ fclose(fp);
+
+ return retno;
+}
+
+static int parse_edd_extensions(const char *dir, struct edd_info *edd_info)
+{
+ char filename[PATH_MAX];
+ char line[1024];
+ uint16_t flags = 0;
+ FILE *fp;
+ int ret;
+
+ ret = snprintf(filename, PATH_MAX, "%s/%s", dir, "extensions");
+ if (ret < 0 || ret >= PATH_MAX) {
+ fprintf(stderr, "snprintf failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ filename[PATH_MAX-1] = 0;
+
+ fp = fopen(filename, "r");
+ if (!fp) {
+ return -errno;
+ }
+
+ while (fgets(line, 1024, fp)) {
+ /*
+ * strings are in kernel source, function edd_show_extensions()
+ * drivers/firmware/edd.c
+ */
+ if (strstr(line, "Fixed disk access") == line)
+ flags |= EDD_EXT_FIXED_DISK_ACCESS;
+ else if (strstr(line, "Device locking and ejecting") == line)
+ flags |= EDD_EXT_DEVICE_LOCKING_AND_EJECTING;
+ else if (strstr(line, "Enhanced Disk Drive support") == line)
+ flags |= EDD_EXT_ENHANCED_DISK_DRIVE_SUPPORT;
+ else if (strstr(line, "64-bit extensions") == line)
+ flags |= EDD_EXT_64BIT_EXTENSIONS;
+ }
+
+ fclose(fp);
+
+ edd_info->interface_support = flags;
+
+ return 0;
+}
+
+static int read_edd_raw_data(const char *dir, struct edd_info *edd_info)
+{
+ char filename[PATH_MAX];
+ FILE *fp;
+ size_t read_chars;
+ uint16_t len;
+ int ret;
+
+ ret = snprintf(filename, PATH_MAX, "%s/%s", dir, "raw_data");
+ if (ret < 0 || ret >= PATH_MAX) {
+ fprintf(stderr, "snprintf failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ filename[PATH_MAX-1] = 0;
+
+ fp = fopen(filename, "r");
+ if (!fp) {
+ return -errno;
+ }
+
+ memset(edd_info->edd_device_params, 0, EDD_DEVICE_PARAM_SIZE);
+ read_chars = fread(edd_info->edd_device_params, sizeof(uint8_t),
+ EDD_DEVICE_PARAM_SIZE, fp);
+ fclose(fp);
+
+ len = ((uint16_t *)edd_info->edd_device_params)[0];
+ dbgprintf("EDD raw data has length %d\n", len);
+
+ if (read_chars < len) {
+ fprintf(stderr, "BIOS reported EDD length of %hd but only "
+ "%d chars read.\n", len, (int)read_chars);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int add_edd_entry(struct x86_linux_param_header *real_mode,
+ const char *sysfs_name, int *current_edd, int *current_mbr)
+{
+ uint8_t devnum, version;
+ uint32_t mbr_sig;
+ struct edd_info *edd_info;
+
+ if (!current_mbr || !current_edd) {
+ fprintf(stderr, "%s: current_edd and current_edd "
+ "must not be NULL", __FUNCTION__);
+ return -1;
+ }
+
+ edd_info = &real_mode->eddbuf[*current_edd];
+ memset(edd_info, 0, sizeof(struct edd_info));
+
+ /* extract the device number */
+ if (sscanf(basename(sysfs_name), "int13_dev%hhx", &devnum) != 1) {
+ fprintf(stderr, "Invalid format of int13_dev dir "
+ "entry: %s\n", basename(sysfs_name));
+ return -1;
+ }
+
+ /* if there's a MBR signature, then add it */
+ if (file_scanf(sysfs_name, "mbr_signature", "0x%x", &mbr_sig) == 1) {
+ real_mode->edd_mbr_sig_buffer[*current_mbr] = mbr_sig;
+ (*current_mbr)++;
+ dbgprintf("EDD Device 0x%x: mbr_sig=0x%x\n", devnum, mbr_sig);
+ }
+
+ /* set the device number */
+ edd_info->device = devnum;
+
+ /* set the version */
+ if (file_scanf(sysfs_name, "version", "0x%hhx", &version) != 1)
+ return -1;
+
+ edd_info->version = version;
+
+ /* if version == 0, that's some kind of dummy entry */
+ if (version != 0) {
+ /* legacy_max_cylinder */
+ if (file_scanf(sysfs_name, "legacy_max_cylinder", "%hu",
+ &edd_info->legacy_max_cylinder) != 1) {
+ fprintf(stderr, "Reading legacy_max_cylinder failed.\n");
+ return -1;
+ }
+
+ /* legacy_max_head */
+ if (file_scanf(sysfs_name, "legacy_max_head", "%hhu",
+ &edd_info->legacy_max_head) != 1) {
+ fprintf(stderr, "Reading legacy_max_head failed.\n");
+ return -1;
+ }
+
+ /* legacy_sectors_per_track */
+ if (file_scanf(sysfs_name, "legacy_sectors_per_track", "%hhu",
+ &edd_info->legacy_sectors_per_track) != 1) {
+ fprintf(stderr, "Reading legacy_sectors_per_track failed.\n");
+ return -1;
+ }
+
+ /* Parse the EDD extensions */
+ if (parse_edd_extensions(sysfs_name, edd_info) != 0) {
+ fprintf(stderr, "Parsing EDD extensions failed.\n");
+ return -1;
+ }
+
+ /* Parse the raw info */
+ if (read_edd_raw_data(sysfs_name, edd_info) != 0) {
+ fprintf(stderr, "Reading EDD raw data failed.\n");
+ return -1;
+ }
+ }
+
+ (*current_edd)++;
+
+ return 0;
+}
+
+static void zero_edd(struct x86_linux_param_header *real_mode)
+{
+ real_mode->eddbuf_entries = 0;
+ real_mode->edd_mbr_sig_buf_entries = 0;
+ memset(real_mode->eddbuf, 0,
+ EDDMAXNR * sizeof(struct edd_info));
+ memset(real_mode->edd_mbr_sig_buffer, 0,
+ EDD_MBR_SIG_MAX * sizeof(uint32_t));
+}
+
+void setup_edd_info(struct x86_linux_param_header *real_mode)
+{
+ DIR *edd_dir;
+ struct dirent *cursor;
+ int current_edd = 0;
+ int current_mbr = 0;
+
+ edd_dir = opendir(EDD_SYFS_DIR);
+ if (!edd_dir) {
+ dbgprintf(EDD_SYFS_DIR " does not exist.\n");
+ return;
+ }
+
+ zero_edd(real_mode);
+ while ((cursor = readdir(edd_dir))) {
+ char full_dir_name[PATH_MAX];
+
+ /* only read the entries that start with "int13_dev" */
+ if (strstr(cursor->d_name, "int13_dev") != cursor->d_name)
+ continue;
+
+ snprintf(full_dir_name, PATH_MAX, "%s/%s",
+ EDD_SYFS_DIR, cursor->d_name);
+ full_dir_name[PATH_MAX-1] = 0;
+
+ if (add_edd_entry(real_mode, full_dir_name, &current_edd,
+ &current_mbr) != 0) {
+ zero_edd(real_mode);
+ goto out;
+ }
+ }
+
+ real_mode->eddbuf_entries = current_edd;
+ real_mode->edd_mbr_sig_buf_entries = current_mbr;
+
+out:
+ closedir(edd_dir);
+
+ dbgprintf("Added %d EDD MBR entries and %d EDD entries.\n",
+ real_mode->edd_mbr_sig_buf_entries,
+ real_mode->eddbuf_entries);
+}
+
+/*
+ * This really only makes sense for virtual filesystems that are only expected
+ * to be mounted once (sysfs, debugsfs, proc), as it will return the first
+ * instance listed in /proc/mounts, falling back to mtab if absent.
+ * We search by type and not by name because the name can be anything;
+ * while setting the name equal to the mount point is common, it cannot be
+ * relied upon, as even kernel documentation examples recommends using
+ * "none" as the name e.g. for debugfs.
+ */
+char *find_mnt_by_type(char *type)
+{
+ FILE *mtab;
+ struct mntent *mnt;
+ char *mntdir;
+
+ mtab = setmntent("/proc/mounts", "r");
+ if (!mtab) {
+ // Fall back to mtab
+ mtab = setmntent("/etc/mtab", "r");
+ }
+ if (!mtab)
+ return NULL;
+ for(mnt = getmntent(mtab); mnt; mnt = getmntent(mtab)) {
+ if (strcmp(mnt->mnt_type, type) == 0)
+ break;
+ }
+ mntdir = mnt ? strdup(mnt->mnt_dir) : NULL;
+ endmntent(mtab);
+ return mntdir;
+}
+
+int get_bootparam(void *buf, off_t offset, size_t size)
+{
+ int data_file;
+ char *debugfs_mnt, *sysfs_mnt;
+ char filename[PATH_MAX];
+ int err, has_sysfs_params = 0;
+
+ sysfs_mnt = find_mnt_by_type("sysfs");
+ if (sysfs_mnt) {
+ snprintf(filename, PATH_MAX, "%s/%s", sysfs_mnt,
+ "kernel/boot_params/data");
+ free(sysfs_mnt);
+ err = access(filename, F_OK);
+ if (!err)
+ has_sysfs_params = 1;
+ }
+
+ if (!has_sysfs_params) {
+ debugfs_mnt = find_mnt_by_type("debugfs");
+ if (!debugfs_mnt)
+ return 1;
+ snprintf(filename, PATH_MAX, "%s/%s", debugfs_mnt,
+ "boot_params/data");
+ free(debugfs_mnt);
+ }
+
+ data_file = open(filename, O_RDONLY);
+ if (data_file < 0)
+ return 1;
+ if (lseek(data_file, offset, SEEK_SET) < 0)
+ goto close;
+ read(data_file, buf, size);
+close:
+ close(data_file);
+ return 0;
+}
+
+void setup_subarch(struct x86_linux_param_header *real_mode)
+{
+ off_t offset = offsetof(typeof(*real_mode), hardware_subarch);
+
+ get_bootparam(&real_mode->hardware_subarch, offset, sizeof(uint32_t));
+}
+
+struct efi_mem_descriptor {
+ uint32_t type;
+ uint32_t pad;
+ uint64_t phys_addr;
+ uint64_t virt_addr;
+ uint64_t num_pages;
+ uint64_t attribute;
+};
+
+struct efi_setup_data {
+ uint64_t fw_vendor;
+ uint64_t runtime;
+ uint64_t tables;
+ uint64_t smbios;
+ uint64_t reserved[8];
+};
+
+struct setup_data {
+ uint64_t next;
+ uint32_t type;
+#define SETUP_NONE 0
+#define SETUP_E820_EXT 1
+#define SETUP_DTB 2
+#define SETUP_PCI 3
+#define SETUP_EFI 4
+#define SETUP_RNG_SEED 9
+ uint32_t len;
+ uint8_t data[0];
+} __attribute__((packed));
+
+static int get_efi_value(const char *filename,
+ const char *pattern, uint64_t *val)
+{
+ FILE *fp;
+ char line[1024], *s, *end;
+
+ fp = fopen(filename, "r");
+ if (!fp)
+ return 1;
+
+ while (fgets(line, sizeof(line), fp) != 0) {
+ s = strstr(line, pattern);
+ if (!s)
+ continue;
+ *val = strtoull(s + strlen(pattern), &end, 16);
+ if (*val == ULLONG_MAX) {
+ fclose(fp);
+ return 2;
+ }
+ break;
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+static int get_efi_values(struct efi_setup_data *esd)
+{
+ int ret = 0;
+
+ ret = get_efi_value("/sys/firmware/efi/systab", "SMBIOS=0x",
+ &esd->smbios);
+ ret |= get_efi_value("/sys/firmware/efi/fw_vendor", "0x",
+ &esd->fw_vendor);
+ ret |= get_efi_value("/sys/firmware/efi/runtime", "0x",
+ &esd->runtime);
+ ret |= get_efi_value("/sys/firmware/efi/config_table", "0x",
+ &esd->tables);
+ return ret;
+}
+
+static int get_efi_runtime_map(struct efi_mem_descriptor **map)
+{
+ DIR *dirp;
+ struct dirent *entry;
+ char filename[1024];
+ struct efi_mem_descriptor md, *p = NULL;
+ int nr_maps = 0;
+
+ dirp = opendir("/sys/firmware/efi/runtime-map");
+ if (!dirp)
+ return 0;
+ while ((entry = readdir(dirp)) != NULL) {
+ sprintf(filename,
+ "/sys/firmware/efi/runtime-map/%s",
+ (char *)entry->d_name);
+ if (*entry->d_name == '.')
+ continue;
+ file_scanf(filename, "type", "0x%x", (unsigned int *)&md.type);
+ file_scanf(filename, "phys_addr", "0x%llx",
+ (unsigned long long *)&md.phys_addr);
+ file_scanf(filename, "virt_addr", "0x%llx",
+ (unsigned long long *)&md.virt_addr);
+ file_scanf(filename, "num_pages", "0x%llx",
+ (unsigned long long *)&md.num_pages);
+ file_scanf(filename, "attribute", "0x%llx",
+ (unsigned long long *)&md.attribute);
+ p = realloc(p, (nr_maps + 1) * sizeof(md));
+ if (!p)
+ goto err_out;
+
+ *(p + nr_maps) = md;
+ *map = p;
+ nr_maps++;
+ }
+
+ closedir(dirp);
+ return nr_maps;
+err_out:
+ if (*map)
+ free(*map);
+ closedir(dirp);
+ return 0;
+}
+
+struct efi_info {
+ uint32_t efi_loader_signature;
+ uint32_t efi_systab;
+ uint32_t efi_memdesc_size;
+ uint32_t efi_memdesc_version;
+ uint32_t efi_memmap;
+ uint32_t efi_memmap_size;
+ uint32_t efi_systab_hi;
+ uint32_t efi_memmap_hi;
+};
+
+/*
+ * Add another instance to single linked list of struct setup_data.
+ * Please refer to kernel Documentation/x86/boot.txt for more details
+ * about setup_data structure.
+ */
+static void add_setup_data(struct kexec_info *info,
+ struct x86_linux_param_header *real_mode,
+ struct setup_data *sd)
+{
+ int sdsize = sizeof(struct setup_data) + sd->len;
+
+ sd->next = real_mode->setup_data;
+ real_mode->setup_data = add_buffer(info, sd, sdsize, sdsize, getpagesize(),
+ 0x100000, ULONG_MAX, INT_MAX);
+}
+
+/*
+ * setup_efi_data will collect below data and pass them to 2nd kernel.
+ * 1) SMBIOS, fw_vendor, runtime, config_table, they are passed via x86
+ * setup_data.
+ * 2) runtime memory regions, set the memmap related fields in efi_info.
+ */
+static int setup_efi_data(struct kexec_info *info,
+ struct x86_linux_param_header *real_mode)
+{
+ int64_t memmap_paddr;
+ struct setup_data *sd;
+ struct efi_setup_data *esd;
+ struct efi_mem_descriptor *maps;
+ int nr_maps, size, ret = 0;
+ struct efi_info *ei = (struct efi_info *)real_mode->efi_info;
+
+ ret = access("/sys/firmware/efi/systab", F_OK);
+ if (ret < 0)
+ goto out;
+
+ esd = malloc(sizeof(struct efi_setup_data));
+ if (!esd) {
+ ret = 1;
+ goto out;
+ }
+ memset(esd, 0, sizeof(struct efi_setup_data));
+ ret = get_efi_values(esd);
+ if (ret)
+ goto free_esd;
+ nr_maps = get_efi_runtime_map(&maps);
+ if (!nr_maps) {
+ ret = 2;
+ goto free_esd;
+ }
+ sd = malloc(sizeof(struct setup_data) + sizeof(*esd));
+ if (!sd) {
+ ret = 3;
+ goto free_maps;
+ }
+
+ memset(sd, 0, sizeof(struct setup_data) + sizeof(*esd));
+ sd->next = 0;
+ sd->type = SETUP_EFI;
+ sd->len = sizeof(*esd);
+ memcpy(sd->data, esd, sizeof(*esd));
+ free(esd);
+
+ add_setup_data(info, real_mode, sd);
+
+ size = nr_maps * sizeof(struct efi_mem_descriptor);
+ memmap_paddr = add_buffer(info, maps, size, size, getpagesize(),
+ 0x100000, ULONG_MAX, INT_MAX);
+ ei->efi_memmap = memmap_paddr & 0xffffffff;
+ ei->efi_memmap_hi = memmap_paddr >> 32;
+ ei->efi_memmap_size = size;
+ ei->efi_memdesc_size = sizeof(struct efi_mem_descriptor);
+
+ return 0;
+free_maps:
+ free(maps);
+free_esd:
+ free(esd);
+out:
+ return ret;
+}
+
+static void add_e820_map_from_mr(struct x86_linux_param_header *real_mode,
+ struct e820entry *e820, struct memory_range *range, int nr_range)
+{
+ int i;
+
+ for (i = 0; i < nr_range; i++) {
+ e820[i].addr = range[i].start;
+ e820[i].size = range[i].end - range[i].start + 1;
+ switch (range[i].type) {
+ case RANGE_RAM:
+ e820[i].type = E820_RAM;
+ break;
+ case RANGE_ACPI:
+ e820[i].type = E820_ACPI;
+ break;
+ case RANGE_ACPI_NVS:
+ e820[i].type = E820_NVS;
+ break;
+ case RANGE_PMEM:
+ e820[i].type = E820_PMEM;
+ break;
+ case RANGE_PRAM:
+ e820[i].type = E820_PRAM;
+ break;
+ default:
+ case RANGE_RESERVED:
+ e820[i].type = E820_RESERVED;
+ break;
+ }
+ dbgprintf("%016lx-%016lx (%d)\n",
+ e820[i].addr,
+ e820[i].addr + e820[i].size - 1,
+ e820[i].type);
+
+ if (range[i].type != RANGE_RAM)
+ continue;
+ if ((range[i].start <= 0x100000) && range[i].end > 0x100000) {
+ unsigned long long mem_k = (range[i].end >> 10) - (0x100000 >> 10);
+ real_mode->ext_mem_k = mem_k;
+ real_mode->alt_mem_k = mem_k;
+ if (mem_k > 0xfc00) {
+ real_mode->ext_mem_k = 0xfc00; /* 64M */
+ }
+ if (mem_k > 0xffffffff) {
+ real_mode->alt_mem_k = 0xffffffff;
+ }
+ }
+ }
+}
+
+static void setup_e820_ext(struct kexec_info *info, struct x86_linux_param_header *real_mode,
+ struct memory_range *range, int nr_range)
+{
+ struct setup_data *sd;
+ struct e820entry *e820;
+ int nr_range_ext;
+
+ nr_range_ext = nr_range - E820MAX;
+ sd = xmalloc(sizeof(struct setup_data) + nr_range_ext * sizeof(struct e820entry));
+ sd->next = 0;
+ sd->len = nr_range_ext * sizeof(struct e820entry);
+ sd->type = SETUP_E820_EXT;
+
+ e820 = (struct e820entry *) sd->data;
+ dbgprintf("Extended E820 via setup_data:\n");
+ add_e820_map_from_mr(real_mode, e820, range + E820MAX, nr_range_ext);
+ add_setup_data(info, real_mode, sd);
+}
+
+static void setup_e820(struct kexec_info *info, struct x86_linux_param_header *real_mode)
+{
+ struct memory_range *range;
+ int nr_range, nr_range_saved;
+
+
+ if (info->kexec_flags & KEXEC_ON_CRASH && !arch_options.pass_memmap_cmdline) {
+ range = info->crash_range;
+ nr_range = info->nr_crash_ranges;
+ } else {
+ range = info->memory_range;
+ nr_range = info->memory_ranges;
+ }
+
+ nr_range_saved = nr_range;
+ if (nr_range > E820MAX) {
+ nr_range = E820MAX;
+ }
+
+ real_mode->e820_map_nr = nr_range;
+ dbgprintf("E820 memmap:\n");
+ add_e820_map_from_mr(real_mode, real_mode->e820_map, range, nr_range);
+
+ if (nr_range_saved > E820MAX) {
+ dbgprintf("extra E820 memmap are passed via setup_data\n");
+ setup_e820_ext(info, real_mode, range, nr_range_saved);
+ }
+}
+
+static void setup_rng_seed(struct kexec_info *info,
+ struct x86_linux_param_header *real_mode)
+{
+ struct {
+ struct setup_data header;
+ uint8_t rng_seed[32];
+ } *sd;
+
+ sd = xmalloc(sizeof(*sd));
+ sd->header.next = 0;
+ sd->header.len = sizeof(sd->rng_seed);
+ sd->header.type = SETUP_RNG_SEED;
+
+ if (getrandom(sd->rng_seed, sizeof(sd->rng_seed), GRND_NONBLOCK) !=
+ sizeof(sd->rng_seed))
+ return; /* Not initialized, so don't pass a seed. */
+
+ add_setup_data(info, real_mode, &sd->header);
+}
+
+static int
+get_efi_mem_desc_version(struct x86_linux_param_header *real_mode)
+{
+ struct efi_info *ei = (struct efi_info *)real_mode->efi_info;
+
+ return ei->efi_memdesc_version;
+}
+
+static void setup_efi_info(struct kexec_info *info,
+ struct x86_linux_param_header *real_mode)
+{
+ int ret, desc_version;
+ off_t offset = offsetof(typeof(*real_mode), efi_info);
+
+ ret = get_bootparam(&real_mode->efi_info, offset, 32);
+ if (ret)
+ return;
+ if (((struct efi_info *)real_mode->efi_info)->efi_memmap_size == 0)
+ /* zero filled efi_info */
+ goto out;
+ desc_version = get_efi_mem_desc_version(real_mode);
+ if (desc_version != 1) {
+ fprintf(stderr,
+ "efi memory descriptor version %d is not supported!\n",
+ desc_version);
+ goto out;
+ }
+ ret = setup_efi_data(info, real_mode);
+ if (ret)
+ goto out;
+
+ return;
+
+out:
+ memset(&real_mode->efi_info, 0, 32);
+ return;
+}
+
+void setup_linux_system_parameters(struct kexec_info *info,
+ struct x86_linux_param_header *real_mode)
+{
+ int err;
+
+ /* get subarch from running kernel */
+ setup_subarch(real_mode);
+ if (bzImage_support_efi_boot && !arch_options.noefi)
+ setup_efi_info(info, real_mode);
+
+ /* Default screen size */
+ real_mode->orig_x = 0;
+ real_mode->orig_y = 0;
+ real_mode->orig_video_page = 0;
+ real_mode->orig_video_mode = 0;
+ real_mode->orig_video_cols = 80;
+ real_mode->orig_video_lines = 25;
+ real_mode->orig_video_ega_bx = 0;
+ real_mode->orig_video_isVGA = 1;
+ real_mode->orig_video_points = 16;
+
+ /* setup vesa fb if possible, or just use original screen_info */
+ err = setup_linux_vesafb(real_mode);
+ if (err) {
+ uint16_t cl_magic, cl_offset;
+
+ /* save and restore the old cmdline param if needed */
+ cl_magic = real_mode->cl_magic;
+ cl_offset = real_mode->cl_offset;
+
+ err = get_bootparam(real_mode, 0, sizeof(struct screen_info));
+ if (!err) {
+ real_mode->cl_magic = cl_magic;
+ real_mode->cl_offset = cl_offset;
+ }
+ }
+ /* Fill in the memsize later */
+ real_mode->ext_mem_k = 0;
+ real_mode->alt_mem_k = 0;
+ real_mode->e820_map_nr = 0;
+
+ /* Default APM info */
+ memset(&real_mode->apm_bios_info, 0, sizeof(real_mode->apm_bios_info));
+ /* Default drive info */
+ memset(&real_mode->drive_info, 0, sizeof(real_mode->drive_info));
+ /* Default sysdesc table */
+ real_mode->sys_desc_table.length = 0;
+
+ /* default yes: this can be overridden on the command line */
+ real_mode->mount_root_rdonly = 0xFFFF;
+
+ /* default /dev/hda
+ * this can be overrident on the command line if necessary.
+ */
+ real_mode->root_dev = (0x3 <<8)| 0;
+
+ /* another safe default */
+ real_mode->aux_device_info = 0;
+
+ setup_e820(info, real_mode);
+
+ /* pass RNG seed */
+ setup_rng_seed(info, real_mode);
+
+ /* fill the EDD information */
+ setup_edd_info(real_mode);
+
+ /* Always try to fill acpi_rsdp_addr */
+ real_mode->acpi_rsdp_addr = get_acpi_rsdp();
+}
+
+void setup_linux_dtb(struct kexec_info *info, struct x86_linux_param_header *real_mode,
+ const char *dtb_buf, int dtb_len)
+{
+ struct setup_data *sd;
+
+ sd = xmalloc(sizeof(struct setup_data) + dtb_len);
+ sd->next = 0;
+ sd->len = dtb_len;
+ sd->type = SETUP_DTB;
+ memcpy(sd->data, dtb_buf, dtb_len);
+
+
+ add_setup_data(info, real_mode, sd);
+}
diff --git a/kexec/arch/i386/x86-linux-setup.h b/kexec/arch/i386/x86-linux-setup.h
new file mode 100644
index 0000000..b5e1ad5
--- /dev/null
+++ b/kexec/arch/i386/x86-linux-setup.h
@@ -0,0 +1,38 @@
+#ifndef X86_LINUX_SETUP_H
+#define X86_LINUX_SETUP_H
+#include <x86/x86-linux.h>
+
+void init_linux_parameters(struct x86_linux_param_header *real_mode);
+void setup_linux_bootloader_parameters_high(
+ struct kexec_info *info, struct x86_linux_param_header *real_mode,
+ unsigned long real_mode_base, unsigned long cmdline_offset,
+ const char *cmdline, off_t cmdline_len,
+ const char *initrd_buf, off_t initrd_size, int initrd_high);
+static inline void setup_linux_bootloader_parameters(
+ struct kexec_info *info, struct x86_linux_param_header *real_mode,
+ unsigned long real_mode_base, unsigned long cmdline_offset,
+ const char *cmdline, off_t cmdline_len,
+ const char *initrd_buf, off_t initrd_size)
+{
+ setup_linux_bootloader_parameters_high(info,
+ real_mode, real_mode_base,
+ cmdline_offset, cmdline, cmdline_len,
+ initrd_buf, initrd_size, 0);
+}
+void setup_linux_system_parameters(struct kexec_info *info,
+ struct x86_linux_param_header *real_mode);
+void setup_linux_dtb(struct kexec_info *info, struct x86_linux_param_header *real_mode,
+ const char *dtb_buf, int dtb_len);
+int get_bootparam(void *buf, off_t offset, size_t size);
+
+
+#define SETUP_BASE 0x90000
+#define KERN32_BASE 0x100000 /* 1MB */
+#define INITRD_BASE 0x1000000 /* 16MB */
+
+/* command line parameter may be appended by purgatory */
+#define PURGATORY_CMDLINE_SIZE 64
+extern int bzImage_support_efi_boot;
+extern struct arch_options_t arch_options;
+
+#endif /* X86_LINUX_SETUP_H */
diff --git a/kexec/arch/ia64/Makefile b/kexec/arch/ia64/Makefile
new file mode 100644
index 0000000..f5b212b
--- /dev/null
+++ b/kexec/arch/ia64/Makefile
@@ -0,0 +1,16 @@
+#
+# kexec ia64 (linux booting linux)
+#
+ia64_KEXEC_SRCS = kexec/arch/ia64/kexec-iomem.c
+ia64_KEXEC_SRCS += kexec/arch/ia64/kexec-ia64.c
+ia64_KEXEC_SRCS += kexec/arch/ia64/kexec-elf-ia64.c
+ia64_KEXEC_SRCS += kexec/arch/ia64/kexec-elf-rel-ia64.c
+ia64_KEXEC_SRCS += kexec/arch/ia64/crashdump-ia64.c
+
+ia64_PROC_IOMEM =
+
+dist += kexec/arch/ia64/Makefile $(ia64_KEXEC_SRCS) \
+ kexec/arch/ia64/kexec-ia64.h kexec/arch/ia64/crashdump-ia64.h \
+ kexec/arch/ia64/include/arch/options.h
+
+
diff --git a/kexec/arch/ia64/crashdump-ia64.c b/kexec/arch/ia64/crashdump-ia64.c
new file mode 100644
index 0000000..755ee5e
--- /dev/null
+++ b/kexec/arch/ia64/crashdump-ia64.c
@@ -0,0 +1,293 @@
+/*
+ * kexec: crashdump support
+ * Copyright (C) 2005-2006 Zou Nan hai <nanhai.zou@intel.com> Intel Corp
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-syscall.h"
+#include "kexec-ia64.h"
+#include "crashdump-ia64.h"
+#include "../kexec/crashdump.h"
+
+int memory_ranges = 0;
+#define LOAD_OFFSET (0xa000000000000000UL + 0x100000000UL - \
+ kernel_code_start)
+
+static struct crash_elf_info elf_info =
+{
+ class: ELFCLASS64,
+ data: ELFDATA2LSB,
+ machine: EM_IA_64,
+ page_offset: PAGE_OFFSET,
+};
+
+/* Stores a sorted list of RAM memory ranges for which to create elf headers.
+ * A separate program header is created for backup region.
+ * The number of entries in memory_range array is always smaller than
+ * the number of entries in the file returned by proc_iomem(),
+ * stored in max_memory_ranges. */
+static struct memory_range *crash_memory_range;
+/* Memory region reserved for storing panic kernel and other data. */
+static struct memory_range crash_reserved_mem;
+unsigned long elfcorehdr;
+static unsigned long kernel_code_start;
+static unsigned long kernel_code_end;
+struct loaded_segment {
+ unsigned long start;
+ unsigned long end;
+};
+
+#define MAX_LOAD_SEGMENTS 128
+struct loaded_segment loaded_segments[MAX_LOAD_SEGMENTS];
+
+unsigned long loaded_segments_num, loaded_segments_base;
+static int seg_comp(const void *a, const void *b)
+{
+ const struct loaded_segment *x = a, *y = b;
+ /* avoid overflow */
+ if (x->start > y->start) return 1;
+ if (x->start < y->start) return -1;
+ return 0;
+}
+
+/* purgatory code need this info to patch the EFI memmap
+ */
+static void add_loaded_segments_info(struct mem_ehdr *ehdr)
+{
+ unsigned i = 0;
+ while(i < ehdr->e_phnum) {
+ struct mem_phdr *phdr;
+ phdr = &ehdr->e_phdr[i];
+ if (phdr->p_type != PT_LOAD) {
+ i++;
+ continue;
+ }
+
+ loaded_segments[loaded_segments_num].start =
+ _ALIGN_DOWN(phdr->p_paddr, ELF_PAGE_SIZE);
+ loaded_segments[loaded_segments_num].end =
+ loaded_segments[loaded_segments_num].start;
+
+ /* Consolidate consecutive PL_LOAD segments into one.
+ * The end addr of the last PL_LOAD segment, calculated by
+ * adding p_memsz to p_paddr & rounded up to ELF_PAGE_SIZE,
+ * will be the end address of this loaded_segments entry.
+ */
+ while (i < ehdr->e_phnum) {
+ phdr = &ehdr->e_phdr[i];
+ if (phdr->p_type != PT_LOAD)
+ break;
+ loaded_segments[loaded_segments_num].end =
+ _ALIGN(phdr->p_paddr + phdr->p_memsz,
+ ELF_PAGE_SIZE);
+ i++;
+ }
+ loaded_segments_num++;
+ }
+}
+
+/* Removes crash reserve region from list of memory chunks for whom elf program
+ * headers have to be created. Assuming crash reserve region to be a single
+ * continuous area fully contained inside one of the memory chunks */
+static int exclude_crash_reserve_region(int *nr_ranges)
+{
+ int i, j, tidx = -1;
+ unsigned long cstart, cend;
+ struct memory_range temp_region;
+
+ /* Crash reserved region. */
+ cstart = crash_reserved_mem.start;
+ cend = crash_reserved_mem.end;
+
+ for (i = 0; i < (*nr_ranges); i++) {
+ unsigned long mstart, mend;
+ mstart = crash_memory_range[i].start;
+ mend = crash_memory_range[i].end;
+ if (cstart < mend && cend > mstart) {
+ if (cstart != mstart && cend != mend) {
+ /* Split memory region */
+ crash_memory_range[i].end = cstart - 1;
+ temp_region.start = cend + 1;
+ temp_region.end = mend;
+ temp_region.type = RANGE_RAM;
+ tidx = i+1;
+ } else if (cstart != mstart)
+ crash_memory_range[i].end = cstart - 1;
+ else
+ crash_memory_range[i].start = cend + 1;
+ }
+ }
+ /* Insert split memory region, if any. */
+ if (tidx >= 0) {
+ if (*nr_ranges == max_memory_ranges) {
+ /* No space to insert another element. */
+ fprintf(stderr, "Error: Number of crash memory ranges"
+ " excedeed the max limit\n");
+ return -1;
+ }
+ for (j = (*nr_ranges - 1); j >= tidx; j--)
+ crash_memory_range[j+1] = crash_memory_range[j];
+ crash_memory_range[tidx].start = temp_region.start;
+ crash_memory_range[tidx].end = temp_region.end;
+ crash_memory_range[tidx].type = temp_region.type;
+ (*nr_ranges)++;
+ }
+ return 0;
+}
+
+static int get_crash_memory_ranges(int *ranges)
+{
+ const char *iomem = proc_iomem();
+ char line[MAX_LINE];
+ FILE *fp;
+ unsigned long start, end;
+
+ crash_memory_range = xmalloc(sizeof(struct memory_range) *
+ max_memory_ranges);
+ fp = fopen(iomem, "r");
+ if (!fp) {
+ fprintf(stderr, "Cannot open %s: %s\n",
+ iomem, strerror(errno));
+ return -1;
+ }
+ while(fgets(line, sizeof(line), fp) != 0) {
+ char *str;
+ int type, consumed, count;
+ if (memory_ranges >= max_memory_ranges)
+ break;
+ count = sscanf(line, "%lx-%lx : %n",
+ &start, &end, &consumed);
+ str = line + consumed;
+ if (count != 2)
+ continue;
+
+ if (memcmp(str, "System RAM\n", 11) == 0) {
+ type = RANGE_RAM;
+ } else if (memcmp(str, "Crash kernel\n", 13) == 0) {
+ /* Reserved memory region. New kernel can
+ * use this region to boot into. */
+ crash_reserved_mem.start = start;
+ crash_reserved_mem.end = end;
+ crash_reserved_mem.type = RANGE_RAM;
+ continue;
+ }
+ else if (memcmp(str, "Kernel code\n", 12) == 0) {
+ kernel_code_start = start;
+ kernel_code_end = end;
+ continue;
+ } else if (memcmp(str, "Uncached RAM\n", 13) == 0) {
+ type = RANGE_UNCACHED;
+ } else {
+ continue;
+ }
+ crash_memory_range[memory_ranges].start = start;
+ crash_memory_range[memory_ranges].end = end;
+ crash_memory_range[memory_ranges].type = type;
+ memory_ranges++;
+ }
+ fclose(fp);
+ if (exclude_crash_reserve_region(&memory_ranges) < 0)
+ return -1;
+ *ranges = memory_ranges;
+ return 0;
+}
+
+/*
+ * Note that this assignes a malloced pointer to *cmdline,
+ * which is likely never freed by the caller
+ */
+static void
+cmdline_add_elfcorehdr(const char **cmdline, unsigned long addr)
+{
+ char *str;
+ char buf[64];
+ size_t len;
+ sprintf(buf, " elfcorehdr=%ldK", addr/1024);
+ len = strlen(*cmdline) + strlen(buf) + 1;
+ str = xmalloc(len);
+ sprintf(str, "%s%s", *cmdline, buf);
+ *cmdline = str;
+}
+
+int load_crashdump_segments(struct kexec_info *info, struct mem_ehdr *ehdr,
+ unsigned long max_addr, unsigned long min_base,
+ const char **cmdline)
+{
+ int nr_ranges;
+ unsigned long sz;
+ size_t size;
+ void *tmp;
+ if (info->kexec_flags & KEXEC_ON_CRASH &&
+ get_crash_memory_ranges(&nr_ranges) == 0) {
+ int i;
+
+ elf_info.kern_paddr_start = kernel_code_start;
+ for (i=0; i < nr_ranges; i++) {
+ unsigned long long mstart = crash_memory_range[i].start;
+ unsigned long long mend = crash_memory_range[i].end;
+ if (!mstart && !mend)
+ continue;
+ if (kernel_code_start >= mstart &&
+ kernel_code_start < mend) {
+ elf_info.kern_vaddr_start = mstart + LOAD_OFFSET;
+ break;
+ }
+ }
+ elf_info.kern_size = kernel_code_end - kernel_code_start + 1;
+ if (crash_create_elf64_headers(info, &elf_info,
+ crash_memory_range, nr_ranges,
+ &tmp, &sz, EFI_PAGE_SIZE) < 0)
+ return -1;
+
+ elfcorehdr = add_buffer(info, tmp, sz, sz, EFI_PAGE_SIZE,
+ min_base, max_addr, -1);
+ loaded_segments[loaded_segments_num].start = elfcorehdr;
+ loaded_segments[loaded_segments_num].end = elfcorehdr + sz;
+ loaded_segments_num++;
+ cmdline_add_elfcorehdr(cmdline, elfcorehdr);
+ }
+ add_loaded_segments_info(ehdr);
+ size = sizeof(struct loaded_segment) * loaded_segments_num;
+ qsort(loaded_segments, loaded_segments_num,
+ sizeof(struct loaded_segment), seg_comp);
+ loaded_segments_base = add_buffer(info, loaded_segments,
+ size, size, 16, 0, max_addr, -1);
+
+ elf_rel_set_symbol(&info->rhdr, "__loaded_segments",
+ &loaded_segments_base, sizeof(long));
+ elf_rel_set_symbol(&info->rhdr, "__loaded_segments_num",
+ &loaded_segments_num, sizeof(long));
+ return 0;
+}
+
+int is_crashkernel_mem_reserved(void)
+{
+ uint64_t start, end;
+
+ return parse_iomem_single("Crash kernel\n", &start,
+ &end) == 0 ? (start != end) : 0;
+}
+
+int get_crash_kernel_load_range(uint64_t *start, uint64_t *end)
+{
+ return parse_iomem_single("Crash kernel\n", start, end);
+}
diff --git a/kexec/arch/ia64/crashdump-ia64.h b/kexec/arch/ia64/crashdump-ia64.h
new file mode 100644
index 0000000..72ba054
--- /dev/null
+++ b/kexec/arch/ia64/crashdump-ia64.h
@@ -0,0 +1,12 @@
+#ifndef CRASHDUMP_IA64_H
+#define CRASHDUMP_IA64_H
+
+#define PAGE_OFFSET 0xe000000000000000UL
+#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
+extern int load_crashdump_segments(struct kexec_info *info,
+ struct mem_ehdr *ehdr, unsigned long max_addr,
+ unsigned long min_base, const char **cmdline);
+
+#define CRASH_MAX_MEMMAP_NR (KEXEC_MAX_SEGMENTS + 1)
+
+#endif
diff --git a/kexec/arch/ia64/include/arch/options.h b/kexec/arch/ia64/include/arch/options.h
new file mode 100644
index 0000000..e8754ad
--- /dev/null
+++ b/kexec/arch/ia64/include/arch/options.h
@@ -0,0 +1,42 @@
+#ifndef KEXEC_ARCH_IA64_OPTIONS_H
+#define KEXEC_ARCH_IA64_OPTIONS_H
+
+#define OPT_ARCH_MAX (OPT_MAX+0)
+#define OPT_APPEND (OPT_ARCH_MAX+0)
+#define OPT_RAMDISK (OPT_ARCH_MAX+1)
+#define OPT_NOIO (OPT_ARCH_MAX+2)
+#define OPT_VMM (OPT_ARCH_MAX+3)
+
+/* Options relevant to the architecture (excluding loader-specific ones),
+ * in this case none:
+ */
+#define KEXEC_ARCH_OPTIONS \
+ KEXEC_OPTIONS \
+
+#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
+
+/* The following two #defines list ALL of the options added by all of the
+ * architecture's loaders.
+ * o main() uses this complete list to scan for its options, ignoring
+ * arch-specific/loader-specific ones.
+ * o Then, arch_process_options() uses this complete list to scan for its
+ * options, ignoring general/loader-specific ones.
+ * o Then, the file_type[n].load re-scans for options, using
+ * KEXEC_ARCH_OPTIONS plus its loader-specific options subset.
+ * Any unrecognised options cause an error here.
+ *
+ * This is done so that main()'s/arch_process_options()'s getopt_long() calls
+ * don't choose a kernel filename from random arguments to options they don't
+ * recognise -- as they now recognise (if not act upon) all possible options.
+ */
+#define KEXEC_ALL_OPTIONS \
+ KEXEC_ARCH_OPTIONS \
+ {"command-line", 1, 0, OPT_APPEND}, \
+ {"append", 1, 0, OPT_APPEND}, \
+ {"initrd", 1, 0, OPT_RAMDISK}, \
+ {"noio", 0, 0, OPT_NOIO}, \
+ {"vmm", 1, 0, OPT_VMM}, \
+
+#define KEXEC_ALL_OPT_STR KEXEC_OPT_STR
+
+#endif /* KEXEC_ARCH_IA64_OPTIONS_H */
diff --git a/kexec/arch/ia64/kexec-elf-ia64.c b/kexec/arch/ia64/kexec-elf-ia64.c
new file mode 100644
index 0000000..142dee3
--- /dev/null
+++ b/kexec/arch/ia64/kexec-elf-ia64.c
@@ -0,0 +1,303 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com)
+ * Copyright (C) 2004 Albert Herranz
+ * Copyright (C) 2004 Silicon Graphics, Inc.
+ * Jesse Barnes <jbarnes@sgi.com>
+ * Copyright (C) 2004 Khalid Aziz <khalid.aziz@hp.com> Hewlett Packard Co
+ * Copyright (C) 2005 Zou Nan hai <nanhai.zou@intel.com> Intel Corp
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define _GNU_SOURCE
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <limits.h>
+#include <elf.h>
+#include <boot/elf_boot.h>
+#include <ip_checksum.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "../../kexec-elf.h"
+#include "kexec-ia64.h"
+#include "crashdump-ia64.h"
+#include <arch/options.h>
+
+static const int probe_debug = 0;
+extern unsigned long saved_efi_memmap_size;
+
+/*
+ * elf_ia64_probe - sanity check the elf image
+ *
+ * Make sure that the file image has a reasonable chance of working.
+ */
+int elf_ia64_probe(const char *buf, off_t len)
+{
+ struct mem_ehdr ehdr;
+ int result;
+ result = build_elf_exec_info(buf, len, &ehdr, 0);
+ if (result < 0) {
+ if (probe_debug) {
+ fprintf(stderr, "Not an ELF executable\n");
+ }
+ return -1;
+ }
+ /* Verify the architecuture specific bits */
+ if (ehdr.e_machine != EM_IA_64) {
+ /* for a different architecture */
+ if (probe_debug) {
+ fprintf(stderr, "Not for this architecture.\n");
+ }
+ return -1;
+ }
+ return 0;
+}
+
+void elf_ia64_usage(void)
+{
+ printf(" --command-line=STRING Set the kernel command line to "
+ "STRING.\n"
+ " --append=STRING Set the kernel command line to "
+ "STRING.\n"
+ " --initrd=FILE Use FILE as the kernel's initial "
+ "ramdisk.\n"
+ " --noio Disable I/O in purgatory code.\n"
+ " --vmm=FILE Use FILE as the kernel image for a\n"
+ " virtual machine monitor "
+ "(aka hypervisor)\n");
+}
+
+/* Move the crash kerenl physical offset to reserved region
+ */
+void move_loaded_segments(struct mem_ehdr *ehdr, unsigned long addr)
+{
+ unsigned i;
+ long offset = 0;
+ int found = 0;
+ struct mem_phdr *phdr;
+ for(i = 0; i < ehdr->e_phnum; i++) {
+ phdr = &ehdr->e_phdr[i];
+ if (phdr->p_type == PT_LOAD) {
+ offset = addr - phdr->p_paddr;
+ found++;
+ break;
+ }
+ }
+ if (!found)
+ die("move_loaded_segments: no PT_LOAD region 0x%016x\n", addr);
+ ehdr->e_entry += offset;
+ for(i = 0; i < ehdr->e_phnum; i++) {
+ phdr = &ehdr->e_phdr[i];
+ if (phdr->p_type == PT_LOAD)
+ phdr->p_paddr += offset;
+ }
+}
+
+int elf_ia64_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ struct mem_ehdr ehdr;
+ const char *command_line, *ramdisk=0, *vmm=0, *kernel_buf;
+ char *ramdisk_buf = NULL;
+ off_t ramdisk_size = 0, kernel_size;
+ unsigned long command_line_len;
+ unsigned long entry, max_addr, gp_value;
+ unsigned long command_line_base, ramdisk_base, image_base;
+ unsigned long efi_memmap_base, efi_memmap_size;
+ unsigned long boot_param_base;
+ unsigned long noio=0;
+ int result;
+ int opt;
+ char *efi_memmap_buf, *boot_param;
+
+ /* See options.h -- add any more there, too. */
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ {"command-line", 1, 0, OPT_APPEND},
+ {"append", 1, 0, OPT_APPEND},
+ {"initrd", 1, 0, OPT_RAMDISK},
+ {"noio", 0, 0, OPT_NOIO},
+ {"vmm", 1, 0, OPT_VMM},
+ {0, 0, 0, 0},
+ };
+
+ static const char short_options[] = KEXEC_ARCH_OPT_STR "";
+
+ command_line = 0;
+ while ((opt = getopt_long(argc, argv, short_options,
+ options, 0)) != -1) {
+ switch (opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ case OPT_APPEND:
+ command_line = optarg;
+ break;
+ case OPT_RAMDISK:
+ ramdisk = optarg;
+ break;
+ case OPT_NOIO: /* disable PIO and MMIO in purgatory code*/
+ noio = 1;
+ break;
+ case OPT_VMM:
+ vmm = optarg;
+ break;
+ }
+ }
+ command_line_len = 0;
+ if (command_line) {
+ command_line_len = strlen(command_line) + 16;
+ }
+
+ if (vmm)
+ kernel_buf = slurp_decompress_file(vmm, &kernel_size);
+ else {
+ kernel_buf = buf;
+ kernel_size = len;
+ }
+
+ /* Parse the Elf file */
+ result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0);
+ if (result < 0) {
+ fprintf(stderr, "ELF parse failed\n");
+ free_elf_info(&ehdr);
+ return result;
+ }
+
+ if (info->kexec_flags & KEXEC_ON_CRASH ) {
+ if ((mem_min == 0x00) && (mem_max == ULONG_MAX)) {
+ fprintf(stderr, "Failed to find crash kernel region "
+ "in %s\n", proc_iomem());
+ free_elf_info(&ehdr);
+ return -1;
+ }
+ move_loaded_segments(&ehdr, mem_min);
+ } else if (update_loaded_segments(&ehdr) < 0) {
+ fprintf(stderr, "Failed to place kernel\n");
+ return -1;
+ }
+
+ entry = ehdr.e_entry;
+ max_addr = elf_max_addr(&ehdr);
+
+ /* Load the Elf data */
+ result = elf_exec_load(&ehdr, info);
+ if (result < 0) {
+ fprintf(stderr, "ELF load failed\n");
+ free_elf_info(&ehdr);
+ return result;
+ }
+
+
+ /* Load the setup code */
+ elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
+ 0x0, ULONG_MAX, -1, 0);
+
+
+ if (load_crashdump_segments(info, &ehdr, max_addr, 0,
+ &command_line) < 0)
+ return -1;
+
+ // reverve 4k for ia64_boot_param
+ boot_param = xmalloc(4096);
+ boot_param_base = add_buffer(info, boot_param, 4096, 4096, 4096, 0,
+ max_addr, -1);
+
+ elf_rel_set_symbol(&info->rhdr, "__noio",
+ &noio, sizeof(long));
+
+ elf_rel_set_symbol(&info->rhdr, "__boot_param_base",
+ &boot_param_base, sizeof(long));
+
+ // reserve efi_memmap of actual size allocated in production kernel
+ efi_memmap_size = saved_efi_memmap_size;
+ efi_memmap_buf = xmalloc(efi_memmap_size);
+ efi_memmap_base = add_buffer(info, efi_memmap_buf,
+ efi_memmap_size, efi_memmap_size, 4096, 0,
+ max_addr, -1);
+
+ elf_rel_set_symbol(&info->rhdr, "__efi_memmap_base",
+ &efi_memmap_base, sizeof(long));
+
+ elf_rel_set_symbol(&info->rhdr, "__efi_memmap_size",
+ &efi_memmap_size, sizeof(long));
+ if (command_line) {
+ command_line_len = strlen(command_line) + 1;
+ }
+ if (command_line_len || (info->kexec_flags & KEXEC_ON_CRASH )) {
+ char *cmdline = xmalloc(command_line_len);
+ strcpy(cmdline, command_line);
+
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ char buf[128];
+ sprintf(buf," max_addr=%lluM min_addr=%lluM",
+ mem_max>>20, mem_min>>20);
+ command_line_len = strlen(cmdline) + strlen(buf) + 1;
+ cmdline = xrealloc(cmdline, command_line_len);
+ strcat(cmdline, buf);
+ }
+
+ command_line_len = _ALIGN(command_line_len, 16);
+ command_line_base = add_buffer(info, cmdline,
+ command_line_len, command_line_len,
+ getpagesize(), 0UL,
+ max_addr, -1);
+ elf_rel_set_symbol(&info->rhdr, "__command_line_len",
+ &command_line_len, sizeof(long));
+ elf_rel_set_symbol(&info->rhdr, "__command_line",
+ &command_line_base, sizeof(long));
+ }
+
+ if (ramdisk) {
+ ramdisk_buf = slurp_file(ramdisk, &ramdisk_size);
+ ramdisk_base = add_buffer(info, ramdisk_buf, ramdisk_size,
+ ramdisk_size,
+ getpagesize(), 0, max_addr, -1);
+ elf_rel_set_symbol(&info->rhdr, "__ramdisk_base",
+ &ramdisk_base, sizeof(long));
+ elf_rel_set_symbol(&info->rhdr, "__ramdisk_size",
+ &ramdisk_size, sizeof(long));
+ }
+
+ if (vmm) {
+ image_base = add_buffer(info, buf, len, len,
+ getpagesize(), 0, max_addr, -1);
+ elf_rel_set_symbol(&info->rhdr, "__vmcode_base",
+ &image_base, sizeof(long));
+ elf_rel_set_symbol(&info->rhdr, "__vmcode_size",
+ &len, sizeof(long));
+ }
+
+ gp_value = info->rhdr.rel_addr + 0x200000;
+ elf_rel_set_symbol(&info->rhdr, "__gp_value", &gp_value,
+ sizeof(gp_value));
+
+ elf_rel_set_symbol(&info->rhdr, "__kernel_entry", &entry,
+ sizeof(entry));
+ free_elf_info(&ehdr);
+ return 0;
+}
diff --git a/kexec/arch/ia64/kexec-elf-rel-ia64.c b/kexec/arch/ia64/kexec-elf-rel-ia64.c
new file mode 100644
index 0000000..500f247
--- /dev/null
+++ b/kexec/arch/ia64/kexec-elf-rel-ia64.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2005-2006 Zou Nan hai (nanhai.zou@intel.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* pugatory relocation code
+ * Most of the code in this file is
+ * based on arch/ia64/kernel/module.c in Linux kernel
+ */
+
+
+/* Most of the code in this file is
+ * based on arch/ia64/kernel/module.c in Linux kernel
+ */
+
+#include <stdio.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+
+#define MAX_LTOFF ((uint64_t) (1 << 22))
+
+int machine_verify_elf_rel(struct mem_ehdr *ehdr)
+{
+ if (ehdr->ei_data != ELFDATA2LSB) {
+ return 0;
+ }
+ if (ehdr->ei_class != ELFCLASS64) {
+ return 0;
+ }
+ if (ehdr->e_machine != EM_IA_64) {
+ return 0;
+ }
+ return 1;
+}
+
+static void
+ia64_patch (uint64_t insn_addr, uint64_t mask, uint64_t val)
+{
+ uint64_t m0, m1, v0, v1, b0, b1, *b = (uint64_t *) (insn_addr & -16);
+# define insn_mask ((1UL << 41) - 1)
+ unsigned long shift;
+
+ b0 = b[0]; b1 = b[1];
+ shift = 5 + 41 * (insn_addr % 16); /* 5 bits of template, then 3 x 41-bit instructions */
+ if (shift >= 64) {
+ m1 = mask << (shift - 64);
+ v1 = val << (shift - 64);
+ } else {
+ m0 = mask << shift; m1 = mask >> (64 - shift);
+ v0 = val << shift; v1 = val >> (64 - shift);
+ b[0] = (b0 & ~m0) | (v0 & m0);
+ }
+ b[1] = (b1 & ~m1) | (v1 & m1);
+}
+
+static inline uint64_t
+bundle (const uint64_t insn)
+{
+ return insn & ~0xfUL;
+}
+
+void machine_apply_elf_rel(struct mem_ehdr *ehdr,
+ struct mem_sym *UNUSED(sym), unsigned long r_type, void *location,
+ unsigned long address, unsigned long value)
+{
+ uint64_t gp_value = ehdr->rel_addr + 0x200000;
+ switch(r_type) {
+ case R_IA64_NONE:
+ break;
+ case R_IA64_SEGREL64LSB:
+ case R_IA64_DIR64LSB:
+ *((uint64_t *)location) = value;
+ break;
+ case R_IA64_DIR32LSB:
+ *((uint32_t *)location) = value;
+ if (value != *((uint32_t *)location))
+ goto overflow;
+ break;
+ case R_IA64_IMM64:
+ ia64_patch((uint64_t)location, 0x01fffefe000UL,
+ /* bit 63 -> 36 */
+ (((value & 0x8000000000000000UL) >> 27)
+ /* bit 21 -> 21 */
+ | ((value & 0x0000000000200000UL) << 0)
+ /* bit 16 -> 22 */
+ | ((value & 0x00000000001f0000UL) << 6)
+ /* bit 7 -> 27 */
+ | ((value & 0x000000000000ff80UL) << 20)
+ /* bit 0 -> 13 */
+ | ((value & 0x000000000000007fUL) << 13)));
+ ia64_patch((uint64_t)location - 1, 0x1ffffffffffUL, value>>22);
+ break;
+ case R_IA64_IMM22:
+ if (value + (1 << 21) >= (1 << 22))
+ die("value out of IMM22 range\n");
+ ia64_patch((uint64_t)location, 0x01fffcfe000UL,
+ /* bit 21 -> 36 */
+ (((value & 0x200000UL) << 15)
+ /* bit 16 -> 22 */
+ | ((value & 0x1f0000UL) << 6)
+ /* bit 7 -> 27 */
+ | ((value & 0x00ff80UL) << 20)
+ /* bit 0 -> 13 */
+ | ((value & 0x00007fUL) << 13) ));
+ break;
+ case R_IA64_PCREL21B: {
+ uint64_t delta = ((int64_t)value - (int64_t)address)/16;
+ if (delta + (1 << 20) >= (1 << 21))
+ die("value out of IMM21B range\n");
+ value = ((int64_t)(value - bundle(address)))/16;
+ ia64_patch((uint64_t)location, 0x11ffffe000UL,
+ (((value & 0x100000UL) << 16) /* bit 20 -> 36 */
+ | ((value & 0x0fffffUL) << 13) /* bit 0 -> 13 */));
+ }
+ break;
+ case R_IA64_PCREL64LSB: {
+ value = value - address;
+ put_unaligned(value, (uint64_t *)location);
+ } break;
+ case R_IA64_GPREL22:
+ case R_IA64_LTOFF22X:
+ if (value - gp_value + MAX_LTOFF/2 >= MAX_LTOFF)
+ die("value out of gp relative range");
+ value -= gp_value;
+ ia64_patch((uint64_t)location, 0x01fffcfe000UL,
+ (((value & 0x200000UL) << 15) /* bit 21 -> 36 */
+ |((value & 0x1f0000UL) << 6) /* bit 16 -> 22 */
+ |((value & 0x00ff80UL) << 20) /* bit 7 -> 27 */
+ |((value & 0x00007fUL) << 13) /* bit 0 -> 13 */));
+ break;
+ case R_IA64_LDXMOV:
+ if (value - gp_value + MAX_LTOFF/2 >= MAX_LTOFF)
+ die("value out of gp relative range");
+ ia64_patch((uint64_t)location, 0x1fff80fe000UL, 0x10000000000UL);
+ break;
+ case R_IA64_LTOFF22:
+
+ default:
+ die("Unknown rela relocation: 0x%lx 0x%lx\n",
+ r_type, address);
+ break;
+ }
+ return;
+overflow:
+ die("overflow in relocation type %lu val %llx\n",
+ r_type, value);
+}
diff --git a/kexec/arch/ia64/kexec-ia64.c b/kexec/arch/ia64/kexec-ia64.c
new file mode 100644
index 0000000..418d997
--- /dev/null
+++ b/kexec/arch/ia64/kexec-ia64.c
@@ -0,0 +1,247 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com)
+ * Copyright (C) 2004 Albert Herranz
+ * Copyright (C) 2004 Silicon Graphics, Inc.
+ * Jesse Barnes <jbarnes@sgi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define _GNU_SOURCE
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <sched.h>
+#include <limits.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "elf.h"
+#include "kexec-ia64.h"
+#include <arch/options.h>
+
+/* The number of entries in memory_range array is always smaller than
+ * the number of entries in the file returned by proc_iomem(),
+ * stored in max_memory_ranges. */
+static struct memory_range *memory_range;
+int max_memory_ranges;
+static int memory_ranges;
+unsigned long saved_efi_memmap_size;
+
+/* Reserve range for EFI memmap and Boot parameter */
+static int split_range(int range, unsigned long start, unsigned long end)
+{
+ unsigned long ram_end = memory_range[range - 1].end;
+ unsigned int type = memory_range[range - 1].type;
+ int i;
+ //align end and start to page size of EFI
+ start = _ALIGN_DOWN(start, 1UL<<12);
+ end = _ALIGN(end, 1UL<<12);
+ for (i = 0; i < range; i++)
+ if(memory_range[i].start <= start && memory_range[i].end >=end)
+ break;
+ if (i >= range)
+ return range;
+ range = i;
+ if (memory_range[range].start < start) {
+ memory_range[range].end = start;
+ range++;
+ }
+ memory_range[range].start = start;
+ memory_range[range].end = end;
+ memory_range[range].type = RANGE_RESERVED;
+ range++;
+ if (end < ram_end) {
+ memory_range[range].start = end;
+ memory_range[range].end = ram_end;
+ memory_range[range].type = type;
+ range++;
+ }
+ return range;
+}
+
+/* Return a sorted list of available memory ranges. */
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags)
+{
+ const char *iomem = proc_iomem();
+ char line[MAX_LINE];
+ FILE *fp;
+ fp = fopen(iomem, "r");
+ if (!fp) {
+ fprintf(stderr, "Cannot open %s: %s\n",
+ iomem, strerror(errno));
+ return -1;
+ }
+
+ /* allocate memory_range dynamically */
+ max_memory_ranges = 0;
+ while(fgets(line, sizeof(line), fp) != 0) {
+ max_memory_ranges++;
+ }
+ memory_range = xmalloc(sizeof(struct memory_range) *
+ max_memory_ranges);
+ rewind(fp);
+
+ while(fgets(line, sizeof(line), fp) != 0) {
+ unsigned long start, end;
+ char *str;
+ unsigned type;
+ int consumed;
+ int count;
+ if (memory_ranges >= max_memory_ranges)
+ break;
+ count = sscanf(line, "%lx-%lx : %n",
+ &start, &end, &consumed);
+ if (count != 2)
+ continue;
+ str = line + consumed;
+ end = end + 1;
+ if (memcmp(str, "System RAM\n", 11) == 0) {
+ type = RANGE_RAM;
+ }
+ else if (memcmp(str, "reserved\n", 9) == 0) {
+ type = RANGE_RESERVED;
+ }
+ else if (memcmp(str, "Crash kernel\n", 13) == 0) {
+ /* Redefine the memory region boundaries if kernel
+ * exports the limits and if it is panic kernel.
+ * Override user values only if kernel exported
+ * values are subset of user defined values.
+ */
+
+ if (kexec_flags & KEXEC_ON_CRASH) {
+ if (start > mem_min)
+ mem_min = start;
+ if (end < mem_max)
+ mem_max = end;
+ }
+ continue;
+ } else if (memcmp(str, "Boot parameter\n", 14) == 0) {
+ memory_ranges = split_range(memory_ranges, start, end);
+ continue;
+ } else if (memcmp(str, "EFI Memory Map\n", 14) == 0) {
+ memory_ranges = split_range(memory_ranges, start, end);
+ saved_efi_memmap_size = end - start;
+ continue;
+ } else if (memcmp(str, "Uncached RAM\n", 13) == 0) {
+ type = RANGE_UNCACHED;
+ } else {
+ continue;
+ }
+ /*
+ * Check if this memory range can be coalesced with
+ * the previous range
+ */
+ if ((memory_ranges > 0) &&
+ (start == memory_range[memory_ranges-1].end) &&
+ (type == memory_range[memory_ranges-1].type)) {
+ memory_range[memory_ranges-1].end = end;
+ }
+ else {
+ memory_range[memory_ranges].start = start;
+ memory_range[memory_ranges].end = end;
+ memory_range[memory_ranges].type = type;
+ memory_ranges++;
+ }
+ }
+ fclose(fp);
+ *range = memory_range;
+ *ranges = memory_ranges;
+
+ return 0;
+}
+
+/* Supported file types and callbacks */
+struct file_type file_type[] = {
+ {"elf-ia64", elf_ia64_probe, elf_ia64_load, elf_ia64_usage},
+};
+int file_types = sizeof(file_type) / sizeof(file_type[0]);
+
+
+void arch_usage(void)
+{
+}
+
+int arch_process_options(int argc, char **argv)
+{
+ /* This doesn't belong here! Some sort of arch_init() ? */
+
+ /* execute from monarch processor */
+ cpu_set_t affinity;
+ CPU_ZERO(&affinity);
+ CPU_SET(0, &affinity);
+ sched_setaffinity(0, sizeof(affinity), &affinity);
+
+ return 0;
+}
+
+const struct arch_map_entry arches[] = {
+ { "ia64", KEXEC_ARCH_IA_64 },
+ { NULL, 0 },
+};
+
+int arch_compat_trampoline(struct kexec_info *UNUSED(info))
+{
+ return 0;
+}
+
+int update_loaded_segments(struct mem_ehdr *ehdr)
+{
+ int i;
+ unsigned u;
+ struct mem_phdr *phdr;
+ unsigned long start_addr = ULONG_MAX, end_addr = 0;
+ unsigned long align = 1UL<<26; /* 64M */
+ unsigned long start, end;
+
+ for (u = 0; u < ehdr->e_phnum; u++) {
+ phdr = &ehdr->e_phdr[u];
+ if (phdr->p_type != PT_LOAD)
+ continue;
+ if (phdr->p_paddr < start_addr)
+ start_addr = phdr->p_paddr;
+ if ((phdr->p_paddr + phdr->p_memsz) > end_addr)
+ end_addr = phdr->p_paddr + phdr->p_memsz;
+ }
+
+ for (i = 0; i < memory_ranges && memory_range[i].start <= start_addr;
+ i++) {
+ if (memory_range[i].type == RANGE_RAM &&
+ memory_range[i].end > end_addr)
+ return 0;
+ }
+
+ for (i = 0; i < memory_ranges; i++) {
+ if (memory_range[i].type != RANGE_RAM)
+ continue;
+ start = _ALIGN(memory_range[i].start, align);
+ end = memory_range[i].end;
+ if (end > start && (end - start) > (end_addr - start_addr)) {
+ move_loaded_segments(ehdr, start);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+void arch_update_purgatory(struct kexec_info *UNUSED(info))
+{
+}
+
diff --git a/kexec/arch/ia64/kexec-ia64.h b/kexec/arch/ia64/kexec-ia64.h
new file mode 100644
index 0000000..31e4041
--- /dev/null
+++ b/kexec/arch/ia64/kexec-ia64.h
@@ -0,0 +1,14 @@
+#ifndef KEXEC_IA64_H
+#define KEXEC_IA64_H
+
+extern int max_memory_ranges;
+int elf_ia64_probe(const char *buf, off_t len);
+int elf_ia64_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void elf_ia64_usage(void);
+int update_loaded_segments(struct mem_ehdr *ehdr);
+void move_loaded_segments(struct mem_ehdr *ehdr, unsigned long addr);
+
+#define EFI_PAGE_SIZE (1UL<<12)
+#define ELF_PAGE_SIZE (1UL<<16)
+#endif /* KEXEC_IA64_H */
diff --git a/kexec/arch/ia64/kexec-iomem.c b/kexec/arch/ia64/kexec-iomem.c
new file mode 100644
index 0000000..7fd50cd
--- /dev/null
+++ b/kexec/arch/ia64/kexec-iomem.c
@@ -0,0 +1,23 @@
+#include <stdint.h>
+#include <stdio.h>
+#include "../../kexec.h"
+#include "../../crashdump.h"
+
+static const char proc_iomem_str[]= "/proc/iomem";
+static const char proc_iomem_machine_str[]= "/proc/iomem_machine";
+
+/*
+ * On IA64 XEN the EFI tables are virtualised.
+ * For this reason on such systems /proc/iomem_machine is provided,
+ * which is based on the hypervisor's (machine's) EFI tables.
+ * If Xen is in use, then /proc/iomem is used for memory regions relating
+ * to the currently running dom0 kernel, and /proc/iomem_machine is used
+ * for regions relating to the machine itself or the hypervisor.
+ * If Xen is not in used, then /proc/iomem used.
+ */
+const char *proc_iomem(void)
+{
+ if (xen_present())
+ return proc_iomem_machine_str;
+ return proc_iomem_str;
+}
diff --git a/kexec/arch/loongarch/Makefile b/kexec/arch/loongarch/Makefile
new file mode 100644
index 0000000..3b33b96
--- /dev/null
+++ b/kexec/arch/loongarch/Makefile
@@ -0,0 +1,22 @@
+#
+# kexec loongarch (linux booting linux)
+#
+loongarch_KEXEC_SRCS = kexec/arch/loongarch/kexec-loongarch.c
+loongarch_KEXEC_SRCS += kexec/arch/loongarch/kexec-elf-loongarch.c
+loongarch_KEXEC_SRCS += kexec/arch/loongarch/kexec-pei-loongarch.c
+loongarch_KEXEC_SRCS += kexec/arch/loongarch/kexec-elf-rel-loongarch.c
+loongarch_KEXEC_SRCS += kexec/arch/loongarch/crashdump-loongarch.c
+
+loongarch_MEM_REGIONS = kexec/mem_regions.c
+
+loongarch_CPPFLAGS += -I $(srcdir)/kexec/
+
+loongarch_ADD_BUFFER =
+loongarch_ADD_SEGMENT =
+loongarch_VIRT_TO_PHYS =
+
+dist += kexec/arch/loongarch/Makefile $(loongarch_KEXEC_SRCS) \
+ kexec/arch/loongarch/kexec-loongarch.h \
+ kexec/arch/loongarch/image-header.h \
+ kexec/arch/loongarch/crashdump-loongarch.h \
+ kexec/arch/loongarch/include/arch/options.h
diff --git a/kexec/arch/loongarch/crashdump-loongarch.c b/kexec/arch/loongarch/crashdump-loongarch.c
new file mode 100644
index 0000000..81250e4
--- /dev/null
+++ b/kexec/arch/loongarch/crashdump-loongarch.c
@@ -0,0 +1,220 @@
+/*
+ * LoongArch crashdump.
+ *
+ * Copyright (C) 2022 Loongson Technology Corporation Limited.
+ * Youling Tang <tangyouling@loongson.cn>
+ *
+ * derived from crashdump-arm64.c
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <linux/elf.h>
+
+#include "kexec.h"
+#include "crashdump.h"
+#include "crashdump-loongarch.h"
+#include "iomem.h"
+#include "kexec-loongarch.h"
+#include "kexec-elf.h"
+#include "mem_regions.h"
+
+/* memory ranges of crashed kernel */
+static struct memory_ranges system_memory_rgns;
+
+/* memory range reserved for crashkernel */
+struct memory_range crash_reserved_mem[CRASH_MAX_RESERVED_RANGES];
+struct memory_ranges usablemem_rgns = {
+ .size = 0,
+ .max_size = CRASH_MAX_RESERVED_RANGES,
+ .ranges = crash_reserved_mem,
+};
+
+struct memory_range elfcorehdr_mem;
+
+static struct crash_elf_info elf_info64 = {
+ .class = ELFCLASS64,
+ .data = ELFDATA2LSB,
+ .machine = EM_LOONGARCH,
+ .page_offset = PAGE_OFFSET,
+};
+
+/*
+ * iomem_range_callback() - callback called for each iomem region
+ * @data: not used
+ * @nr: not used
+ * @str: name of the memory region
+ * @base: start address of the memory region
+ * @length: size of the memory region
+ *
+ * This function is called once for each memory region found in /proc/iomem.
+ * It locates system RAM and crashkernel reserved memory and places these to
+ * variables, respectively, system_memory_rgns and usablemem_rgns.
+ */
+
+static int iomem_range_callback(void *UNUSED(data), int UNUSED(nr),
+ char *str, unsigned long long base,
+ unsigned long long length)
+{
+ if (strncmp(str, CRASH_KERNEL, strlen(CRASH_KERNEL)) == 0)
+ return mem_regions_alloc_and_add(&usablemem_rgns,
+ base, length, RANGE_RAM);
+ else if (strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM)) == 0)
+ return mem_regions_alloc_and_add(&system_memory_rgns,
+ base, length, RANGE_RAM);
+ else if (strncmp(str, KERNEL_CODE, strlen(KERNEL_CODE)) == 0)
+ elf_info64.kern_paddr_start = base;
+ else if (strncmp(str, KERNEL_DATA, strlen(KERNEL_DATA)) == 0)
+ elf_info64.kern_size = base + length - elf_info64.kern_paddr_start;
+
+ return 0;
+}
+
+int is_crashkernel_mem_reserved(void)
+{
+ if (!usablemem_rgns.size)
+ kexec_iomem_for_each_line(NULL, iomem_range_callback, NULL);
+
+ return usablemem_rgns.size;
+}
+
+/*
+ * crash_get_memory_ranges() - read system physical memory
+ *
+ * Function reads through system physical memory and stores found memory
+ * regions in system_memory_ranges.
+ * Regions are sorted in ascending order.
+ *
+ * Returns 0 in case of success and a negative value otherwise.
+ */
+static int crash_get_memory_ranges(void)
+{
+ int i;
+
+ /*
+ * First read all memory regions that can be considered as
+ * system memory including the crash area.
+ */
+ if (!usablemem_rgns.size)
+ kexec_iomem_for_each_line(NULL, iomem_range_callback, NULL);
+
+ /* allow one or two regions for crash dump kernel */
+ if (!usablemem_rgns.size)
+ return -EINVAL;
+
+ dbgprint_mem_range("Reserved memory range",
+ usablemem_rgns.ranges, usablemem_rgns.size);
+
+ for (i = 0; i < usablemem_rgns.size; i++) {
+ if (mem_regions_alloc_and_exclude(&system_memory_rgns,
+ &crash_reserved_mem[i])) {
+ fprintf(stderr, "Cannot allocate memory for ranges\n");
+ return -ENOMEM;
+ }
+ }
+
+ /*
+ * Make sure that the memory regions are sorted.
+ */
+ mem_regions_sort(&system_memory_rgns);
+
+ dbgprint_mem_range("Coredump memory ranges",
+ system_memory_rgns.ranges, system_memory_rgns.size);
+
+ /*
+ * For additional kernel code/data segment.
+ * kern_paddr_start/kern_size are determined in iomem_range_callback
+ */
+ elf_info64.kern_vaddr_start = get_kernel_sym("_text");
+ if (!elf_info64.kern_vaddr_start)
+ elf_info64.kern_vaddr_start = UINT64_MAX;
+
+ return 0;
+}
+
+/*
+ * load_crashdump_segments() - load the elf core header
+ * @info: kexec info structure
+ *
+ * This function creates and loads an additional segment of elf core header
+ : which is used to construct /proc/vmcore on crash dump kernel.
+ *
+ * Return 0 in case of success and -1 in case of error.
+ */
+
+int load_crashdump_segments(struct kexec_info *info)
+{
+ unsigned long elfcorehdr;
+ unsigned long bufsz;
+ void *buf;
+ int err;
+
+ /*
+ * First fetch all the memory (RAM) ranges that we are going to
+ * pass to the crash dump kernel during panic.
+ */
+
+ err = crash_get_memory_ranges();
+
+ if (err)
+ return EFAILED;
+
+ err = crash_create_elf64_headers(info, &elf_info64,
+ system_memory_rgns.ranges, system_memory_rgns.size,
+ &buf, &bufsz, ELF_CORE_HEADER_ALIGN);
+
+ if (err)
+ return EFAILED;
+
+ elfcorehdr = add_buffer(info, buf, bufsz, bufsz, 1024,
+ crash_reserved_mem[usablemem_rgns.size - 1].start,
+ crash_reserved_mem[usablemem_rgns.size - 1].end, -1);
+
+ elfcorehdr_mem.start = elfcorehdr;
+ elfcorehdr_mem.end = elfcorehdr + bufsz - 1;
+
+ dbgprintf("%s: elfcorehdr 0x%llx-0x%llx\n", __func__,
+ elfcorehdr_mem.start, elfcorehdr_mem.end);
+
+ return 0;
+}
+
+/*
+ * e_entry and p_paddr are actually in virtual address space.
+ * Those values will be translated to physcal addresses by using
+ * virt_to_phys() in add_segment().
+ * So let's fix up those values for later use so the memory base will be
+ * correctly replaced with crash_reserved_mem[usablemem_rgns.size - 1].start.
+ */
+void fixup_elf_addrs(struct mem_ehdr *ehdr)
+{
+ struct mem_phdr *phdr;
+ int i;
+
+ ehdr->e_entry += crash_reserved_mem[usablemem_rgns.size - 1].start;
+
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ phdr = &ehdr->e_phdr[i];
+ if (phdr->p_type != PT_LOAD)
+ continue;
+ phdr->p_paddr += crash_reserved_mem[usablemem_rgns.size - 1].start;
+ }
+}
+
+int get_crash_kernel_load_range(uint64_t *start, uint64_t *end)
+{
+ if (!usablemem_rgns.size)
+ kexec_iomem_for_each_line(NULL, iomem_range_callback, NULL);
+
+ if (!usablemem_rgns.size)
+ return -1;
+
+ *start = crash_reserved_mem[usablemem_rgns.size - 1].start;
+ *end = crash_reserved_mem[usablemem_rgns.size - 1].end;
+
+ return 0;
+}
diff --git a/kexec/arch/loongarch/crashdump-loongarch.h b/kexec/arch/loongarch/crashdump-loongarch.h
new file mode 100644
index 0000000..25ff24b
--- /dev/null
+++ b/kexec/arch/loongarch/crashdump-loongarch.h
@@ -0,0 +1,26 @@
+#ifndef CRASHDUMP_LOONGARCH_H
+#define CRASHDUMP_LOONGARCH_H
+
+struct kexec_info;
+extern struct memory_ranges usablemem_rgns;
+extern struct memory_range crash_reserved_mem[];
+extern struct memory_range elfcorehdr_mem;
+
+int load_crashdump_segments(struct kexec_info *info);
+int is_crashkernel_mem_reserved(void);
+void fixup_elf_addrs(struct mem_ehdr *ehdr);
+int get_crash_kernel_load_range(uint64_t *start, uint64_t *end);
+
+#define PAGE_OFFSET 0x9000000000000000ULL
+#define MAXMEM 0
+
+#define CRASH_MAX_MEMMAP_NR (KEXEC_MAX_SEGMENTS + 1)
+#define CRASH_MAX_MEMORY_RANGES (MAX_MEMORY_RANGES + 2)
+
+/* crash dump kernel support at most two regions, low_region and high region. */
+#define CRASH_MAX_RESERVED_RANGES 2
+
+#define COMMAND_LINE_SIZE 512
+
+extern struct arch_options_t arch_options;
+#endif /* CRASHDUMP_LOONGARCH_H */
diff --git a/kexec/arch/loongarch/image-header.h b/kexec/arch/loongarch/image-header.h
new file mode 100644
index 0000000..3b75765
--- /dev/null
+++ b/kexec/arch/loongarch/image-header.h
@@ -0,0 +1,79 @@
+/*
+ * LoongArch binary image header.
+ */
+
+#if !defined(__LOONGARCH_IMAGE_HEADER_H)
+#define __LOONGARCH_IMAGE_HEADER_H
+
+#include <endian.h>
+#include <stdint.h>
+
+/**
+ * struct loongarch_image_header
+ *
+ * @pe_sig: Optional PE format 'MZ' signature.
+ * @reserved_1: Reserved.
+ * @kernel_entry: Kernel image entry pointer.
+ * @image_size: An estimated size of the memory image size in LSB byte order.
+ * @text_offset: The image load offset in LSB byte order.
+ * @reserved_2: Reserved.
+ * @reserved_3: Reserved.
+ * @pe_header: Optional offset to a PE format header.
+ **/
+
+struct loongarch_image_header {
+ uint8_t pe_sig[2];
+ uint16_t reserved_1[3];
+ uint64_t kernel_entry;
+ uint64_t image_size;
+ uint64_t text_offset;
+ uint64_t reserved_2[3];
+ uint32_t reserved_3;
+ uint32_t pe_header;
+};
+
+static const uint8_t loongarch_image_pe_sig[2] = {'M', 'Z'};
+
+/**
+ * loongarch_header_check_pe_sig - Helper to check the loongarch image header.
+ *
+ * Returns non-zero if 'MZ' signature is found.
+ */
+
+static inline int loongarch_header_check_pe_sig(const struct loongarch_image_header *h)
+{
+ if (!h)
+ return 0;
+
+ return (h->pe_sig[0] == loongarch_image_pe_sig[0]
+ && h->pe_sig[1] == loongarch_image_pe_sig[1]);
+}
+
+static inline uint64_t loongarch_header_text_offset(
+ const struct loongarch_image_header *h)
+{
+ if (!h)
+ return 0;
+
+ return le64toh(h->text_offset);
+}
+
+static inline uint64_t loongarch_header_image_size(
+ const struct loongarch_image_header *h)
+{
+ if (!h)
+ return 0;
+
+ return le64toh(h->image_size);
+}
+
+static inline uint64_t loongarch_header_kernel_entry(
+ const struct loongarch_image_header *h)
+{
+ if (!h)
+ return 0;
+
+ return le64toh(h->kernel_entry);
+}
+
+#endif
diff --git a/kexec/arch/loongarch/include/arch/options.h b/kexec/arch/loongarch/include/arch/options.h
new file mode 100644
index 0000000..25a7dc1
--- /dev/null
+++ b/kexec/arch/loongarch/include/arch/options.h
@@ -0,0 +1,28 @@
+#ifndef KEXEC_ARCH_LOONGARCH_OPTIONS_H
+#define KEXEC_ARCH_LOONGARCH_OPTIONS_H
+
+#define OPT_APPEND ((OPT_MAX)+0)
+#define OPT_INITRD ((OPT_MAX)+1)
+#define OPT_REUSE_CMDLINE ((OPT_MAX)+2)
+#define OPT_ARCH_MAX ((OPT_MAX)+3)
+
+#define KEXEC_ARCH_OPTIONS \
+ KEXEC_OPTIONS \
+ { "append", 1, NULL, OPT_APPEND }, \
+ { "command-line", 1, NULL, OPT_APPEND }, \
+ { "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 loongarch_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"
+" --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";
+
+#endif /* KEXEC_ARCH_LOONGARCH_OPTIONS_H */
diff --git a/kexec/arch/loongarch/kexec-elf-loongarch.c b/kexec/arch/loongarch/kexec-elf-loongarch.c
new file mode 100644
index 0000000..45387ca
--- /dev/null
+++ b/kexec/arch/loongarch/kexec-elf-loongarch.c
@@ -0,0 +1,125 @@
+/*
+ * kexec-elf-loongarch.c - kexec Elf loader for loongarch
+ *
+ * Copyright (C) 2022 Loongson Technology Corporation Limited.
+ * Youling Tang <tangyouling@loongson.cn>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+*/
+
+#define _GNU_SOURCE
+
+#include <limits.h>
+#include <errno.h>
+#include <elf.h>
+
+#include "kexec.h"
+#include "kexec-elf.h"
+#include "kexec-syscall.h"
+#include "crashdump-loongarch.h"
+#include "kexec-loongarch.h"
+#include "arch/options.h"
+
+off_t initrd_base, initrd_size;
+
+int elf_loongarch_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 out;
+ }
+
+ /* Verify the architecuture specific bits. */
+ if (ehdr.e_machine != EM_LOONGARCH) {
+ dbgprintf("%s: Not an LoongArch ELF executable.\n", __func__);
+ result = -1;
+ goto out;
+ }
+
+ result = 0;
+out:
+ free_elf_info(&ehdr);
+ return result;
+}
+
+int elf_loongarch_load(int argc, char **argv, const char *kernel_buf,
+ off_t kernel_size, struct kexec_info *info)
+{
+ const struct loongarch_image_header *header = NULL;
+ unsigned long kernel_segment;
+ struct mem_ehdr ehdr;
+ int result;
+
+ 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;
+ }
+
+ kernel_segment = loongarch_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: image_size: %016lx\n", __func__,
+ kernel_size);
+ dbgprintf("%s: text_offset: %016lx\n", __func__,
+ loongarch_mem.text_offset);
+ dbgprintf("%s: phys_offset: %016lx\n", __func__,
+ loongarch_mem.phys_offset);
+ dbgprintf("%s: PE format: %s\n", __func__,
+ (loongarch_header_check_pe_sig(header) ? "yes" : "no"));
+
+ /* create and initialize elf core header segment */
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ result = load_crashdump_segments(info);
+ if (result) {
+ dbgprintf("%s: Creating eflcorehdr failed.\n",
+ __func__);
+ goto exit;
+ }
+ }
+
+ /* load the kernel */
+ if (info->kexec_flags & KEXEC_ON_CRASH)
+ /*
+ * offset addresses in elf header in order to load
+ * vmlinux (elf_exec) into crash kernel's memory.
+ */
+ fixup_elf_addrs(&ehdr);
+
+ info->entry = (void *)virt_to_phys(ehdr.e_entry);
+
+ result = elf_exec_load(&ehdr, info);
+
+ if (result) {
+ dbgprintf("%s: elf_exec_load failed\n", __func__);
+ goto exit;
+ }
+
+ /* load additional data */
+ result = loongarch_load_other_segments(info, kernel_segment + kernel_size);
+
+exit:
+ free_elf_info(&ehdr);
+ if (result)
+ fprintf(stderr, "kexec: Bad elf image file, load failed.\n");
+ return result;
+}
+
+void elf_loongarch_usage(void)
+{
+ printf(
+" An LoongArch ELF image, little endian.\n"
+" Typically vmlinux or a stripped version of vmlinux.\n\n");
+}
diff --git a/kexec/arch/loongarch/kexec-elf-rel-loongarch.c b/kexec/arch/loongarch/kexec-elf-rel-loongarch.c
new file mode 100644
index 0000000..59f7f5d
--- /dev/null
+++ b/kexec/arch/loongarch/kexec-elf-rel-loongarch.c
@@ -0,0 +1,42 @@
+/*
+ * kexec-elf-rel-loongarch.c - kexec Elf relocation routines
+ *
+ * Copyright (C) 2022 Loongson Technology Corporation Limited.
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+*/
+
+#include <stdio.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+
+int machine_verify_elf_rel(struct mem_ehdr *ehdr)
+{
+ if (ehdr->ei_data != ELFDATA2MSB)
+ return 0;
+
+ if (ehdr->ei_class != ELFCLASS32)
+ return 0;
+
+ if (ehdr->e_machine != EM_LOONGARCH)
+ return 0;
+
+ return 1;
+}
+
+void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr),
+ struct mem_sym *UNUSED(sym),
+ unsigned long r_type,
+ void *UNUSED(location),
+ unsigned long UNUSED(address),
+ unsigned long UNUSED(value))
+{
+ switch (r_type) {
+
+ default:
+ die("Unknown rela relocation: %lu\n", r_type);
+ break;
+ }
+}
diff --git a/kexec/arch/loongarch/kexec-loongarch.c b/kexec/arch/loongarch/kexec-loongarch.c
new file mode 100644
index 0000000..f47c998
--- /dev/null
+++ b/kexec/arch/loongarch/kexec-loongarch.c
@@ -0,0 +1,375 @@
+/*
+ * kexec-loongarch.c - kexec for loongarch
+ *
+ * Copyright (C) 2022 Loongson Technology Corporation Limited.
+ * Youling Tang <tangyouling@loongson.cn>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <linux/elf-em.h>
+#include <elf.h>
+#include <elf_info.h>
+
+#include "kexec.h"
+#include "kexec-loongarch.h"
+#include "crashdump-loongarch.h"
+#include "iomem.h"
+#include "kexec-syscall.h"
+#include "mem_regions.h"
+#include "arch/options.h"
+
+#define CMDLINE_PREFIX "kexec "
+static char cmdline[COMMAND_LINE_SIZE] = CMDLINE_PREFIX;
+
+/* Adds "initrd=start,size" parameters to command line. */
+static int cmdline_add_initrd(char *cmdline, unsigned long addr,
+ unsigned long size)
+{
+ int cmdlen, len;
+ char str[50], *ptr;
+
+ ptr = str;
+ strcpy(str, " initrd=");
+ ptr += strlen(str);
+ ultoa(addr, ptr);
+ strcat(str, ",");
+ ptr = str + strlen(str);
+ ultoa(size, ptr);
+ len = strlen(str);
+ cmdlen = strlen(cmdline) + len;
+ if (cmdlen > (COMMAND_LINE_SIZE - 1))
+ die("Command line overflow\n");
+ strcat(cmdline, str);
+
+ return 0;
+}
+
+/* Adds the appropriate "mem=size@start" options to command line, indicating the
+ * memory region the new kernel can use to boot into. */
+static int cmdline_add_mem(char *cmdline, unsigned long addr,
+ unsigned long size)
+{
+ int cmdlen, len;
+ char str[50], *ptr;
+
+ addr = addr/1024;
+ size = size/1024;
+ ptr = str;
+ strcpy(str, " mem=");
+ ptr += strlen(str);
+ ultoa(size, ptr);
+ strcat(str, "K@");
+ ptr = str + strlen(str);
+ ultoa(addr, ptr);
+ strcat(str, "K");
+ len = strlen(str);
+ cmdlen = strlen(cmdline) + len;
+ if (cmdlen > (COMMAND_LINE_SIZE - 1))
+ die("Command line overflow\n");
+ strcat(cmdline, str);
+
+ return 0;
+}
+
+/* Adds the "elfcorehdr=size@start" command line parameter to command line. */
+static int cmdline_add_elfcorehdr(char *cmdline, unsigned long addr,
+ unsigned long size)
+{
+ int cmdlen, len;
+ char str[50], *ptr;
+
+ addr = addr/1024;
+ size = size/1024;
+ ptr = str;
+ strcpy(str, " elfcorehdr=");
+ ptr += strlen(str);
+ ultoa(size, ptr);
+ strcat(str, "K@");
+ ptr = str + strlen(str);
+ ultoa(addr, ptr);
+ strcat(str, "K");
+ len = strlen(str);
+ cmdlen = strlen(cmdline) + len;
+ if (cmdlen > (COMMAND_LINE_SIZE - 1))
+ die("Command line overflow\n");
+ strcat(cmdline, str);
+
+ return 0;
+}
+
+/* Return a sorted list of memory ranges. */
+static struct memory_range memory_range[MAX_MEMORY_RANGES];
+
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long UNUSED(kexec_flags))
+{
+ int memory_ranges = 0;
+
+ const char *iomem = proc_iomem();
+ char line[MAX_LINE];
+ FILE *fp;
+ unsigned long long start, end;
+ char *str;
+ int type, consumed, count;
+
+ fp = fopen(iomem, "r");
+ if (!fp) {
+ fprintf(stderr, "Cannot open %s: %s\n", iomem, strerror(errno));
+ return -1;
+ }
+
+ while (fgets(line, sizeof(line), fp) != 0) {
+ if (memory_ranges >= MAX_MEMORY_RANGES)
+ break;
+ count = sscanf(line, "%llx-%llx : %n", &start, &end, &consumed);
+ if (count != 2)
+ continue;
+ str = line + consumed;
+ end = end + 1;
+ if (!strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM)))
+ type = RANGE_RAM;
+ else if (!strncmp(str, IOMEM_RESERVED, strlen(IOMEM_RESERVED)))
+ type = RANGE_RESERVED;
+ else
+ continue;
+
+ if (memory_ranges > 0 &&
+ memory_range[memory_ranges - 1].end == start &&
+ memory_range[memory_ranges - 1].type == type) {
+ memory_range[memory_ranges - 1].end = end;
+ } else {
+ memory_range[memory_ranges].start = start;
+ memory_range[memory_ranges].end = end;
+ memory_range[memory_ranges].type = type;
+ memory_ranges++;
+ }
+ }
+ fclose(fp);
+ *range = memory_range;
+ *ranges = memory_ranges;
+
+ dbgprint_mem_range("MEMORY RANGES:", *range, *ranges);
+ return 0;
+}
+
+struct file_type file_type[] = {
+ {"elf-loongarch", elf_loongarch_probe, elf_loongarch_load, elf_loongarch_usage},
+ {"pei-loongarch", pei_loongarch_probe, pei_loongarch_load, pei_loongarch_usage},
+};
+int file_types = sizeof(file_type) / sizeof(file_type[0]);
+
+/* loongarch global varables. */
+
+struct loongarch_mem loongarch_mem;
+
+/**
+ * loongarch_process_image_header - Process the loongarch image header.
+ */
+
+int loongarch_process_image_header(const struct loongarch_image_header *h)
+{
+
+ if (!loongarch_header_check_pe_sig(h))
+ return EFAILED;
+
+ if (h->image_size) {
+ loongarch_mem.text_offset = loongarch_header_text_offset(h);
+ loongarch_mem.image_size = loongarch_header_image_size(h);
+ }
+
+ return 0;
+}
+
+void arch_usage(void)
+{
+ printf(loongarch_opts_usage);
+}
+
+struct arch_options_t arch_options = {
+ .core_header_type = CORE_TYPE_ELF64,
+};
+
+int arch_process_options(int argc, char **argv)
+{
+ static const char short_options[] = KEXEC_ARCH_OPT_STR "";
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { 0 },
+ };
+ int opt;
+ char *cmdline = NULL;
+ const char *append = NULL;
+
+ while ((opt = getopt_long(argc, argv, short_options,
+ options, 0)) != -1) {
+ switch (opt) {
+ case OPT_APPEND:
+ append = optarg;
+ break;
+ case OPT_REUSE_CMDLINE:
+ cmdline = get_command_line();
+ remove_parameter(cmdline, "kexec");
+ remove_parameter(cmdline, "initrd");
+ break;
+ case OPT_INITRD:
+ arch_options.initrd_file = optarg;
+ break;
+ default:
+ break;
+ }
+ }
+
+ arch_options.command_line = concat_cmdline(cmdline, append);
+
+ dbgprintf("%s:%d: command_line: %s\n", __func__, __LINE__,
+ arch_options.command_line);
+ dbgprintf("%s:%d: initrd: %s\n", __func__, __LINE__,
+ arch_options.initrd_file);
+
+ return 0;
+}
+
+const struct arch_map_entry arches[] = {
+ { "loongarch64", KEXEC_ARCH_LOONGARCH },
+ { NULL, 0 },
+};
+
+unsigned long loongarch_locate_kernel_segment(struct kexec_info *info)
+{
+ unsigned long hole;
+
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ unsigned long hole_end;
+
+ hole = (crash_reserved_mem[usablemem_rgns.size - 1].start < mem_min ?
+ mem_min : crash_reserved_mem[usablemem_rgns.size - 1].start) +
+ loongarch_mem.text_offset;
+ hole = _ALIGN_UP(hole, MiB(1));
+ hole_end = hole + loongarch_mem.text_offset + loongarch_mem.image_size;
+
+ if ((hole_end > mem_max) ||
+ (hole_end > crash_reserved_mem[usablemem_rgns.size - 1].end)) {
+ dbgprintf("%s: Crash kernel out of range\n", __func__);
+ hole = ULONG_MAX;
+ }
+ } else {
+ hole = locate_hole(info,
+ loongarch_mem.text_offset + loongarch_mem.image_size,
+ MiB(1), 0, ULONG_MAX, 1);
+
+ if (hole == ULONG_MAX)
+ dbgprintf("%s: locate_hole failed\n", __func__);
+ }
+
+ return hole;
+}
+
+/*
+ * loongarch_load_other_segments - Prepare the initrd and cmdline segments.
+ */
+
+int loongarch_load_other_segments(struct kexec_info *info, unsigned long hole_min)
+{
+ unsigned long initrd_min, hole_max;
+ char *initrd_buf = NULL;
+ unsigned long pagesize = getpagesize();
+
+ if (arch_options.command_line) {
+ if (strlen(arch_options.command_line) >
+ sizeof(cmdline) - 1) {
+ fprintf(stderr,
+ "Kernel command line too long for kernel!\n");
+ return EFAILED;
+ }
+
+ strncat(cmdline, arch_options.command_line, sizeof(cmdline) - 1);
+ }
+
+ /* Put the other segments after the image. */
+
+ initrd_min = hole_min;
+ if (info->kexec_flags & KEXEC_ON_CRASH)
+ hole_max = crash_reserved_mem[usablemem_rgns.size - 1].end;
+ else
+ hole_max = ULONG_MAX;
+
+ if (arch_options.initrd_file) {
+
+ initrd_buf = slurp_decompress_file(arch_options.initrd_file, &initrd_size);
+
+ initrd_base = add_buffer(info, initrd_buf, initrd_size,
+ initrd_size, sizeof(void *),
+ _ALIGN_UP(initrd_min,
+ pagesize), hole_max, 1);
+ dbgprintf("initrd_base: %lx, initrd_size: %lx\n", initrd_base, initrd_size);
+
+ cmdline_add_initrd(cmdline, initrd_base, initrd_size);
+ }
+
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ cmdline_add_elfcorehdr(cmdline, elfcorehdr_mem.start,
+ elfcorehdr_mem.end - elfcorehdr_mem.start + 1);
+
+ cmdline_add_mem(cmdline, crash_reserved_mem[usablemem_rgns.size - 1].start,
+ crash_reserved_mem[usablemem_rgns.size - 1].end -
+ crash_reserved_mem[usablemem_rgns.size - 1].start + 1);
+ }
+
+ cmdline[sizeof(cmdline) - 1] = 0;
+ add_buffer(info, cmdline, sizeof(cmdline), sizeof(cmdline),
+ sizeof(void *), _ALIGN_UP(hole_min, getpagesize()),
+ 0xffffffff, 1);
+
+ dbgprintf("%s:%d: command_line: %s\n", __func__, __LINE__, cmdline);
+
+ return 0;
+
+}
+
+int arch_compat_trampoline(struct kexec_info *UNUSED(info))
+{
+ return 0;
+}
+
+void arch_update_purgatory(struct kexec_info *UNUSED(info))
+{
+}
+
+unsigned long virt_to_phys(unsigned long addr)
+{
+ return addr & ((1ULL << 48) - 1);
+}
+
+/*
+ * add_segment() should convert base to a physical address on loongarch,
+ * while the default is just to work with base as is
+ */
+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, virt_to_phys(base), memsz, 1);
+}
+
+/*
+ * add_buffer() should convert base to a physical address on loongarch,
+ * while the default is just to work with base as is
+ */
+unsigned long add_buffer(struct kexec_info *info, const void *buf,
+ unsigned long bufsz, unsigned long memsz,
+ unsigned long buf_align, unsigned long buf_min,
+ unsigned long buf_max, int buf_end)
+{
+ return add_buffer_phys_virt(info, buf, bufsz, memsz, buf_align,
+ buf_min, buf_max, buf_end, 1);
+}
diff --git a/kexec/arch/loongarch/kexec-loongarch.h b/kexec/arch/loongarch/kexec-loongarch.h
new file mode 100644
index 0000000..5120a26
--- /dev/null
+++ b/kexec/arch/loongarch/kexec-loongarch.h
@@ -0,0 +1,60 @@
+#ifndef KEXEC_LOONGARCH_H
+#define KEXEC_LOONGARCH_H
+
+#include <sys/types.h>
+
+#include "image-header.h"
+
+#define BOOT_BLOCK_VERSION 17
+#define BOOT_BLOCK_LAST_COMP_VERSION 16
+
+#define MAX_MEMORY_RANGES 64
+#define MAX_LINE 160
+
+#define CORE_TYPE_ELF64 1
+
+#define COMMAND_LINE_SIZE 512
+
+#define KiB(x) ((x) * 1024UL)
+#define MiB(x) (KiB(x) * 1024UL)
+
+int elf_loongarch_probe(const char *kernel_buf, off_t kernel_size);
+int elf_loongarch_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void elf_loongarch_usage(void);
+
+int pei_loongarch_probe(const char *buf, off_t len);
+int pei_loongarch_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void pei_loongarch_usage(void);
+
+int loongarch_process_image_header(const struct loongarch_image_header *h);
+
+unsigned long loongarch_locate_kernel_segment(struct kexec_info *info);
+int loongarch_load_other_segments(struct kexec_info *info,
+ unsigned long hole_min);
+
+struct arch_options_t {
+ char *command_line;
+ char *initrd_file;
+ char *dtb;
+ int core_header_type;
+};
+
+/**
+ * struct loongarch_mem - Memory layout info.
+ */
+
+struct loongarch_mem {
+ uint64_t phys_offset;
+ uint64_t text_offset;
+ uint64_t image_size;
+};
+
+extern struct loongarch_mem loongarch_mem;
+
+extern struct memory_ranges usablemem_rgns;
+extern struct arch_options_t arch_options;
+extern off_t initrd_base, initrd_size;
+
+#endif /* KEXEC_LOONGARCH_H */
diff --git a/kexec/arch/loongarch/kexec-pei-loongarch.c b/kexec/arch/loongarch/kexec-pei-loongarch.c
new file mode 100644
index 0000000..1a11103
--- /dev/null
+++ b/kexec/arch/loongarch/kexec-pei-loongarch.c
@@ -0,0 +1,124 @@
+/*
+ * LoongArch kexec PE format binary image support.
+ *
+ * Copyright (C) 2022 Loongson Technology Corporation Limited.
+ * Youling Tang <tangyouling@loongson.cn>
+ *
+ * derived from kexec-image-arm64.c
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#define _GNU_SOURCE
+
+#include <limits.h>
+#include <errno.h>
+#include <elf.h>
+
+#include "kexec.h"
+#include "kexec-elf.h"
+#include "image-header.h"
+#include "kexec-syscall.h"
+#include "crashdump-loongarch.h"
+#include "kexec-loongarch.h"
+#include "arch/options.h"
+
+int pei_loongarch_probe(const char *kernel_buf, off_t kernel_size)
+{
+ const struct loongarch_image_header *h;
+
+ if (kernel_size < sizeof(struct loongarch_image_header)) {
+ dbgprintf("%s: No loongarch image header.\n", __func__);
+ return -1;
+ }
+
+ h = (const struct loongarch_image_header *)(kernel_buf);
+
+ if (!loongarch_header_check_pe_sig(h)) {
+ dbgprintf("%s: Bad loongarch PE image header.\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int pei_loongarch_load(int argc, char **argv, const char *buf,
+ off_t len, struct kexec_info *info)
+{
+ int result;
+ unsigned long hole_min = 0;
+ unsigned long kernel_segment, kernel_entry;
+ const struct loongarch_image_header *header;
+
+ header = (const struct loongarch_image_header *)(buf);
+
+ if (loongarch_process_image_header(header))
+ return EFAILED;
+
+ kernel_segment = loongarch_locate_kernel_segment(info);
+
+ if (kernel_segment == ULONG_MAX) {
+ dbgprintf("%s: Kernel segment is not allocated\n", __func__);
+ result = EFAILED;
+ goto exit;
+ }
+
+ kernel_entry = virt_to_phys(loongarch_header_kernel_entry(header));
+
+ if (info->kexec_flags & KEXEC_ON_CRASH)
+ /*
+ * offset addresses in order to load vmlinux.efi into
+ * crash kernel's memory.
+ */
+ kernel_entry += crash_reserved_mem[usablemem_rgns.size - 1].start;
+
+ dbgprintf("%s: kernel_segment: %016lx\n", __func__, kernel_segment);
+ dbgprintf("%s: kernel_entry: %016lx\n", __func__, kernel_entry);
+ dbgprintf("%s: image_size: %016lx\n", __func__,
+ loongarch_mem.image_size);
+ dbgprintf("%s: text_offset: %016lx\n", __func__,
+ loongarch_mem.text_offset);
+ dbgprintf("%s: phys_offset: %016lx\n", __func__,
+ loongarch_mem.phys_offset);
+ dbgprintf("%s: PE format: %s\n", __func__,
+ (loongarch_header_check_pe_sig(header) ? "yes" : "no"));
+
+ /* Get kernel entry point */
+ info->entry = (void *)kernel_entry;
+
+ hole_min = kernel_segment + loongarch_mem.image_size;
+
+ /* Create and initialize elf core header segment */
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ result = load_crashdump_segments(info);
+ if (result) {
+ dbgprintf("%s: Creating eflcorehdr failed.\n",
+ __func__);
+ goto exit;
+ }
+ }
+
+ /* Load the kernel */
+ add_segment(info, buf, len, kernel_segment, loongarch_mem.image_size);
+
+ /* Prepare and load dtb and initrd data */
+ result = loongarch_load_other_segments(info, hole_min);
+ if (result) {
+ fprintf(stderr, "kexec: Load dtb and initrd segments failed.\n");
+ goto exit;
+ }
+
+exit:
+ if (result)
+ fprintf(stderr, "kexec: load failed.\n");
+
+ return result;
+}
+
+void pei_loongarch_usage(void)
+{
+ printf(
+" An LoongArch PE format binary image, uncompressed, little endian.\n"
+" Typically a vmlinux.efi file.\n\n");
+}
diff --git a/kexec/arch/m68k/Makefile b/kexec/arch/m68k/Makefile
new file mode 100644
index 0000000..eeaacbd
--- /dev/null
+++ b/kexec/arch/m68k/Makefile
@@ -0,0 +1,15 @@
+#
+# kexec m68k (linux booting linux)
+#
+m68k_KEXEC_SRCS = kexec/arch/m68k/kexec-m68k.c
+m68k_KEXEC_SRCS += kexec/arch/m68k/kexec-elf-m68k.c
+m68k_KEXEC_SRCS += kexec/arch/m68k/kexec-elf-rel-m68k.c
+m68k_KEXEC_SRCS += kexec/arch/m68k/bootinfo.c
+
+m68k_ADD_SEGMENT =
+m68k_VIRT_TO_PHYS =
+
+dist += kexec/arch/m68k/Makefile $(m68k_KEXEC_SRCS) \
+ kexec/arch/m68k/bootinfo.h \
+ kexec/arch/m68k/kexec-m68k.h \
+ kexec/arch/m68k/include/arch/options.h
diff --git a/kexec/arch/m68k/bootinfo.c b/kexec/arch/m68k/bootinfo.c
new file mode 100644
index 0000000..086a34b
--- /dev/null
+++ b/kexec/arch/m68k/bootinfo.c
@@ -0,0 +1,285 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/random.h>
+
+#include "../../kexec.h"
+
+#include "bootinfo.h"
+
+const char *bootinfo_file = DEFAULT_BOOTINFO_FILE;
+static struct bi_rec *bootinfo;
+static off_t bootinfo_size;
+
+static unsigned int num_memchunks;
+
+static struct bi_rec *bi_next(struct bi_rec *bi, uint16_t size)
+{
+ return (void *)((unsigned long)bi + size);
+}
+
+static struct bi_rec *bi_find(struct bi_rec *prev, uint16_t tag)
+{
+ struct bi_rec *bi = prev ? bi_next(prev, prev->size) : bootinfo;
+
+ for (bi = prev ? bi_next(prev, prev->size) : bootinfo;
+ bi->tag != BI_LAST; bi = bi_next(bi, bi->size))
+ if (bi->tag == tag)
+ return bi;
+ return NULL;
+}
+
+static void bi_remove(uint16_t tag)
+{
+ struct bi_rec *bi;
+ off_t rem;
+ uint16_t size;
+
+ bi = bootinfo;
+ rem = bootinfo_size;
+ while (1) {
+ if (bi->tag == BI_LAST)
+ break;
+
+ size = bi->size;
+ if (bi->tag == tag) {
+ memmove(bi, bi_next(bi, size), rem - size);
+ bootinfo_size -= size;
+ rem -= size;
+ continue;
+ }
+
+ bi = bi_next(bi, size);
+ rem -= size;
+ }
+}
+
+static struct bi_rec *bi_add(uint16_t tag, uint16_t size)
+{
+ struct bi_rec *bi;
+
+ /* Add 4-byte header and round up to multiple of 4 bytes */
+ size = _ALIGN_UP(4 + size, 4);
+
+ bootinfo = xrealloc(bootinfo, bootinfo_size + size);
+
+ /* Replace old sentinel by new record */
+ bi = bi_next(bootinfo, bootinfo_size - 2);
+ bootinfo_size += size;
+ memset(bi, 0, size);
+ bi->tag = tag;
+ bi->size = size;
+
+ /* Re-add sentinel */
+ bi_next(bi, size)->tag = BI_LAST;
+
+ return bi;
+}
+
+void bootinfo_load(void)
+{
+ struct bi_rec *bi;
+ off_t rem;
+ uint16_t tag, size;
+
+ dbgprintf("Loading bootinfo from %s\n", bootinfo_file);
+ bootinfo = (void *)slurp_file_len(bootinfo_file, MAX_BOOTINFO_SIZE,
+ &bootinfo_size);
+ if (!bootinfo)
+ die("No bootinfo\n");
+
+ bi = bootinfo;
+ rem = bootinfo_size;
+ while (1) {
+ if (rem < 2)
+ die("Unexpected end of bootinfo\n");
+
+ tag = bi->tag;
+ if (tag == BI_LAST) {
+ rem -= 2;
+ break;
+ }
+
+ if (rem < 4)
+ die("Unexpected end of bootinfo\n");
+
+ size = bi->size;
+ if (size < 4 || size % 4)
+ die("Invalid tag size\n");
+ if (rem < size)
+ die("Unexpected end of bootinfo\n");
+
+ if (tag == BI_MEMCHUNK)
+ num_memchunks++;
+
+ bi = bi_next(bi, size);
+ rem -= size;
+ }
+
+ if (rem)
+ die("Trailing data at end of bootinfo\n");
+}
+
+void bootinfo_print(void)
+{
+ struct bi_rec *bi = bootinfo;
+ uint16_t tag, size;
+
+ while (1) {
+ tag = bi->tag;
+ if (tag == BI_LAST) {
+ puts("BI_LAST");
+ break;
+ }
+
+ size = bi->size;
+ switch (tag) {
+ case BI_MACHTYPE:
+ printf("BI_MACHTYPE: 0x%08x\n", bi->machtype);
+ break;
+
+ case BI_MEMCHUNK:
+ printf("BI_MEMCHUNK: 0x%08x bytes at 0x%08x\n",
+ bi->mem_info.size, bi->mem_info.addr);
+ break;
+
+ case BI_RAMDISK:
+ printf("BI_RAMDISK: 0x%08x bytes at 0x%08x\n",
+ bi->mem_info.size, bi->mem_info.addr);
+ break;
+
+ case BI_COMMAND_LINE:
+ printf("BI_COMMAND_LINE: %s\n", bi->string);
+ break;
+
+ case BI_RNG_SEED:
+ /* These are secret, so never print them to the console */
+ printf("BI_RNG_SEED: 0x%08x bytes\n", be16_to_cpu(bi->rng_seed.len));
+ break;
+
+ default:
+ printf("BI tag 0x%04x size %u\n", tag, size);
+ break;
+ }
+ bi = bi_next(bi, size);
+ }
+}
+
+int bootinfo_get_memory_ranges(struct memory_range **range)
+{
+ struct memory_range *ranges;
+ unsigned int i;
+ struct bi_rec *bi;
+
+ ranges = xmalloc(num_memchunks * sizeof(struct memory_range));
+ for (i = 0, bi = NULL;
+ i < num_memchunks && (bi = bi_find(bi, BI_MEMCHUNK)); i++) {
+ ranges[i].start = bi->mem_info.addr;
+ ranges[i].end = bi->mem_info.addr + bi->mem_info.size - 1;
+ ranges[i].type = RANGE_RAM;
+ }
+
+ *range = ranges;
+ return i;
+}
+
+void bootinfo_set_cmdline(const char *cmdline)
+{
+ struct bi_rec *bi;
+ uint16_t size;
+
+ /* Remove existing command line records */
+ bi_remove(BI_COMMAND_LINE);
+
+ if (!cmdline)
+ return;
+
+ /* Add new command line record */
+ size = strlen(cmdline) + 1;
+ bi = bi_add(BI_COMMAND_LINE, size);
+ memcpy(bi->string, cmdline, size);
+}
+
+void bootinfo_set_ramdisk(unsigned long ramdisk_addr,
+ unsigned long ramdisk_size)
+{
+ struct bi_rec *bi;
+
+ /* Remove existing ramdisk records */
+ bi_remove(BI_RAMDISK);
+
+ if (!ramdisk_size)
+ return;
+
+ /* Add new ramdisk record */
+ bi = bi_add(BI_RAMDISK, sizeof(bi->mem_info));
+ bi->mem_info.addr = ramdisk_addr;
+ bi->mem_info.size = ramdisk_size;
+}
+
+void bootinfo_add_rng_seed(void)
+{
+ enum { RNG_SEED_LEN = 32 };
+ struct bi_rec *bi;
+
+ /* Remove existing rng seed records */
+ bi_remove(BI_RNG_SEED);
+
+ /* Add new rng seed record */
+ bi = bi_add(BI_RNG_SEED, sizeof(bi->rng_seed) + RNG_SEED_LEN);
+ if (getrandom(bi->rng_seed.data, RNG_SEED_LEN, GRND_NONBLOCK) != RNG_SEED_LEN) {
+ bi_remove(BI_RNG_SEED);
+ return;
+ }
+ bi->rng_seed.len = cpu_to_be16(RNG_SEED_LEN);
+}
+
+
+ /*
+ * Check the bootinfo version in the kernel image
+ * All failures are non-fatal, as kexec may be used to load
+ * non-Linux images
+ */
+
+void bootinfo_check_bootversion(const struct kexec_info *info)
+{
+ struct bi_rec *bi;
+ const struct bootversion *bv;
+ uint16_t major, minor;
+ unsigned int i;
+
+ bv = info->segment[0].buf;
+ if (bv->magic != BOOTINFOV_MAGIC) {
+ printf("WARNING: No bootversion in kernel image\n");
+ return;
+ }
+
+ bi = bi_find(NULL, BI_MACHTYPE);
+ if (!bi) {
+ printf("WARNING: No machtype in bootinfo\n");
+ return;
+ }
+
+ for (i = 0; bv->machversions[i].machtype != bi->machtype; i++)
+ if (!bv->machversions[i].machtype) {
+ printf("WARNING: Machtype 0x%08x not in kernel bootversion\n",
+ bi->machtype);
+ return;
+ }
+
+ major = BI_VERSION_MAJOR(bv->machversions[i].version);
+ minor = BI_VERSION_MINOR(bv->machversions[i].version);
+ dbgprintf("Kernel uses bootversion %u.%u\n", major, minor);
+ if (major != SUPPORTED_BOOTINFO_VERSION)
+ printf("WARNING: Kernel bootversion %u.%u is too %s for this kexec (expected %u.x)\n",
+ major, minor,
+ major < SUPPORTED_BOOTINFO_VERSION ? "old" : "new",
+ SUPPORTED_BOOTINFO_VERSION);
+}
+
+void add_bootinfo(struct kexec_info *info, unsigned long addr)
+{
+ add_buffer(info, bootinfo, bootinfo_size, bootinfo_size,
+ sizeof(void *), addr, 0x0fffffff, 1);
+}
diff --git a/kexec/arch/m68k/bootinfo.h b/kexec/arch/m68k/bootinfo.h
new file mode 100644
index 0000000..bb8a03a
--- /dev/null
+++ b/kexec/arch/m68k/bootinfo.h
@@ -0,0 +1,53 @@
+#include <asm/bootinfo.h>
+
+#define DEFAULT_BOOTINFO_FILE "/proc/bootinfo"
+#define MAX_BOOTINFO_SIZE 1536
+
+/* New in 6.2's <asm/bootinfo.h>. Remove once 6.2 is baseline version. */
+#ifndef BI_RNG_SEED
+#define BI_RNG_SEED 0x0008
+#endif
+
+
+ /*
+ * Convenience overlay of several struct bi_record variants
+ */
+
+struct bi_rec {
+ __be16 tag;
+ __be16 size;
+ union {
+ __be32 data[0];
+ /* shorthands for the types we use */
+ __be32 machtype;
+ struct {
+ __be32 addr;
+ __be32 size;
+ } mem_info;
+ char string[0];
+ struct {
+ __be16 len;
+ __u8 data[0];
+ } rng_seed;
+ };
+};
+
+
+ /*
+ * We only support the "new" tagged bootinfo (v2)
+ */
+
+#define SUPPORTED_BOOTINFO_VERSION 2
+
+
+extern const char *bootinfo_file;
+
+extern void bootinfo_load(void);
+extern void bootinfo_print(void);
+extern int bootinfo_get_memory_ranges(struct memory_range **range);
+extern void bootinfo_set_cmdline(const char *cmdline);
+extern void bootinfo_set_ramdisk(unsigned long ramdisk_addr,
+ unsigned long ramdisk_size);
+extern void bootinfo_add_rng_seed(void);
+extern void bootinfo_check_bootversion(const struct kexec_info *info);
+extern void add_bootinfo(struct kexec_info *info, unsigned long addr);
diff --git a/kexec/arch/m68k/include/arch/options.h b/kexec/arch/m68k/include/arch/options.h
new file mode 100644
index 0000000..f279d54
--- /dev/null
+++ b/kexec/arch/m68k/include/arch/options.h
@@ -0,0 +1,45 @@
+#ifndef KEXEC_ARCH_M68K_OPTIONS_H
+#define KEXEC_ARCH_M68K_OPTIONS_H
+
+#define OPT_ARCH_MAX (OPT_MAX+0)
+
+/* All 'local' loader options: */
+#define OPT_APPEND (OPT_ARCH_MAX+0)
+#define OPT_REUSE_CMDLINE (OPT_ARCH_MAX+1)
+#define OPT_RAMDISK (OPT_ARCH_MAX+2)
+#define OPT_BOOTINFO (OPT_ARCH_MAX+3)
+
+/* Options relevant to the architecture (excluding loader-specific ones),
+ * in this case none:
+ */
+#define KEXEC_ARCH_OPTIONS \
+ KEXEC_OPTIONS \
+
+#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
+
+/* The following two #defines list ALL of the options added by all of the
+ * architecture's loaders.
+ * o main() uses this complete list to scan for its options, ignoring
+ * arch-specific/loader-specific ones.
+ * o Then, arch_process_options() uses this complete list to scan for its
+ * options, ignoring general/loader-specific ones.
+ * o Then, the file_type[n].load re-scans for options, using
+ * KEXEC_ARCH_OPTIONS plus its loader-specific options subset.
+ * Any unrecognised options cause an error here.
+ *
+ * This is done so that main()'s/arch_process_options()'s getopt_long() calls
+ * don't choose a kernel filename from random arguments to options they don't
+ * recognise -- as they now recognise (if not act upon) all possible options.
+ */
+#define KEXEC_ALL_OPTIONS \
+ KEXEC_ARCH_OPTIONS \
+ { "command-line", 1, NULL, OPT_APPEND }, \
+ { "append", 1, NULL, OPT_APPEND }, \
+ { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE }, \
+ { "ramdisk", 1, NULL, OPT_RAMDISK }, \
+ { "initrd", 1, NULL, OPT_RAMDISK }, \
+ { "bootinfo", 1, NULL, OPT_BOOTINFO },
+
+#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR
+
+#endif /* KEXEC_ARCH_M68K_OPTIONS_H */
diff --git a/kexec/arch/m68k/kexec-elf-m68k.c b/kexec/arch/m68k/kexec-elf-m68k.c
new file mode 100644
index 0000000..a2bf7ee
--- /dev/null
+++ b/kexec/arch/m68k/kexec-elf-m68k.c
@@ -0,0 +1,183 @@
+/*
+ * kexec-elf-m68k.c - kexec Elf loader for m68k
+ *
+ * Copyright (C) 2013 Geert Uytterhoeven
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+*/
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <elf.h>
+#include <boot/elf_boot.h>
+#include <ip_checksum.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-syscall.h"
+#include "kexec-m68k.h"
+#include "bootinfo.h"
+#include <arch/options.h>
+
+#define KiB * 1024
+#define MiB * 1024 KiB
+
+#define PAGE_SIZE 4 KiB
+
+
+int elf_m68k_probe(const char *buf, off_t len)
+{
+ struct mem_ehdr ehdr;
+ int result;
+ result = build_elf_exec_info(buf, len, &ehdr, 0);
+ if (result < 0)
+ goto out;
+
+ /* Verify the architecuture specific bits */
+ if (ehdr.e_machine != EM_68K) {
+ /* for a different architecture */
+ fprintf(stderr, "Not for this architecture.\n");
+ result = -1;
+ goto out;
+ }
+ result = 0;
+ out:
+ free_elf_info(&ehdr);
+ return result;
+}
+
+void elf_m68k_usage(void)
+{
+ printf(" --command-line=STRING Set the kernel command line to STRING\n"
+ " --append=STRING Set the kernel command line to STRING\n"
+ " --reuse-cmdline Use kernel command line from running system.\n"
+ " --ramdisk=FILE Use FILE as the kernel's initial ramdisk.\n"
+ " --initrd=FILE Use FILE as the kernel's initial ramdisk.\n"
+ " --bootinfo=FILE Use FILE as the kernel's bootinfo\n"
+ );
+}
+
+static unsigned long segment_end(const struct kexec_info *info, int i)
+{
+ return (unsigned long)info->segment[i].mem + info->segment[i].memsz - 1;
+}
+
+int elf_m68k_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ struct mem_ehdr ehdr;
+ const char *cmdline = NULL, *ramdisk_file = NULL;
+ int opt, result, i;
+ unsigned long bootinfo_addr, ramdisk_addr = 0;
+ off_t ramdisk_size = 0;
+
+ /* See options.h if adding any more options. */
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { "command-line", 1, NULL, OPT_APPEND },
+ { "append", 1, NULL, OPT_APPEND },
+ { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE },
+ { "ramdisk", 1, NULL, OPT_RAMDISK },
+ { "initrd", 1, NULL, OPT_RAMDISK },
+ { "bootinfo", 1, NULL, OPT_BOOTINFO },
+ { 0, 0, NULL, 0 },
+ };
+
+ static const char short_options[] = KEXEC_ARCH_OPT_STR "d";
+
+ while ((opt = getopt_long(argc, argv, short_options, options, 0)) !=
+ -1) {
+ switch (opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX)
+ break;
+ case OPT_APPEND:
+ cmdline = optarg;
+ break;
+ case OPT_REUSE_CMDLINE:
+ cmdline = get_command_line();
+ break;
+ case OPT_RAMDISK:
+ ramdisk_file = optarg;
+ break;
+ case OPT_BOOTINFO:
+ break;
+ }
+ }
+
+ result = build_elf_exec_info(buf, len, &ehdr, 0);
+ if (result < 0)
+ die("ELF exec parse failed\n");
+
+ /* Fixup PT_LOAD segments that include the ELF header (offset zero) */
+ for (i = 0; i < ehdr.e_phnum; i++) {
+ struct mem_phdr *phdr;
+ phdr = &ehdr.e_phdr[i];
+ if (phdr->p_type != PT_LOAD || phdr->p_offset)
+ continue;
+
+ dbgprintf("Removing ELF header from segment %d\n", i);
+ phdr->p_paddr += PAGE_SIZE;
+ phdr->p_vaddr += PAGE_SIZE;
+ phdr->p_filesz -= PAGE_SIZE;
+ phdr->p_memsz -= PAGE_SIZE;
+ phdr->p_offset += PAGE_SIZE;
+ phdr->p_data += PAGE_SIZE;
+ }
+
+ /* Load the ELF data */
+ result = elf_exec_load(&ehdr, info);
+ if (result < 0)
+ die("ELF exec load failed\n");
+
+ info->entry = (void *)virt_to_phys(ehdr.e_entry);
+
+ /* Bootinfo must be stored right after the kernel */
+ bootinfo_addr = segment_end(info, info->nr_segments - 1) + 1;
+
+ /* Load ramdisk */
+ if (ramdisk_file) {
+ void *ramdisk = slurp_decompress_file(ramdisk_file,
+ &ramdisk_size);
+ /* Store ramdisk at top of first memory chunk */
+ ramdisk_addr = _ALIGN_DOWN(info->memory_range[0].end -
+ ramdisk_size + 1,
+ PAGE_SIZE);
+ if (!buf)
+ die("Ramdisk load failed\n");
+ add_buffer(info, ramdisk, ramdisk_size, ramdisk_size,
+ PAGE_SIZE, ramdisk_addr, info->memory_range[0].end,
+ 1);
+ }
+
+ /* Update and add bootinfo */
+ bootinfo_set_cmdline(cmdline);
+ bootinfo_set_ramdisk(ramdisk_addr, ramdisk_size);
+ bootinfo_add_rng_seed();
+ if (kexec_debug)
+ bootinfo_print();
+ add_bootinfo(info, bootinfo_addr);
+
+ /*
+ * Check if the kernel (and bootinfo) exceed 4 MiB, as current kernels
+ * don't support that.
+ * As the segments are still unsorted, the bootinfo is located in the
+ * last segment.
+ */
+ if (segment_end(info, info->nr_segments - 1) >= virt_to_phys(4 MiB - 1))
+ printf("WARNING: Kernel is larger than 4 MiB\n");
+
+ /* Check struct bootversion at start of kernel */
+ bootinfo_check_bootversion(info);
+
+ return 0;
+}
diff --git a/kexec/arch/m68k/kexec-elf-rel-m68k.c b/kexec/arch/m68k/kexec-elf-rel-m68k.c
new file mode 100644
index 0000000..0cc38cc
--- /dev/null
+++ b/kexec/arch/m68k/kexec-elf-rel-m68k.c
@@ -0,0 +1,39 @@
+/*
+ * kexec-elf-rel-m68k.c - kexec Elf relocation routines
+ *
+ * Copyright (C) 2013 Geert Uytterhoeven
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+*/
+
+#include <stdio.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+
+int machine_verify_elf_rel(struct mem_ehdr *ehdr)
+{
+ if (ehdr->ei_data != ELFDATA2MSB)
+ return 0;
+ if (ehdr->ei_class != ELFCLASS32)
+ return 0;
+ if (ehdr->e_machine != EM_68K)
+ return 0;
+ return 1;
+}
+
+void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr),
+ struct mem_sym *UNUSED(sym),
+ unsigned long r_type,
+ void *UNUSED(location),
+ unsigned long UNUSED(address),
+ unsigned long UNUSED(value))
+{
+ switch (r_type) {
+ default:
+ die("Unknown rela relocation: %lu\n", r_type);
+ break;
+ }
+ return;
+}
diff --git a/kexec/arch/m68k/kexec-m68k.c b/kexec/arch/m68k/kexec-m68k.c
new file mode 100644
index 0000000..cb54927
--- /dev/null
+++ b/kexec/arch/m68k/kexec-m68k.c
@@ -0,0 +1,110 @@
+/*
+ * kexec-m68k.c - kexec for m68k
+ *
+ * Copyright (C) 2013 Geert Uytterhoeven
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "kexec-m68k.h"
+#include "bootinfo.h"
+#include <arch/options.h>
+
+
+static unsigned long m68k_memoffset;
+
+
+/* Return a sorted list of memory ranges. */
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags)
+{
+ bootinfo_load();
+ *ranges = bootinfo_get_memory_ranges(range);
+ m68k_memoffset = (*range)[0].start;
+ return 0;
+}
+
+
+struct file_type file_type[] = {
+ {"elf-m68k", elf_m68k_probe, elf_m68k_load, elf_m68k_usage},
+};
+int file_types = sizeof(file_type) / sizeof(file_type[0]);
+
+void arch_usage(void)
+{
+}
+
+int arch_process_options(int argc, char **argv)
+{
+ static const struct option options[] = {
+ KEXEC_ALL_OPTIONS
+ { "bootinfo", 1, NULL, OPT_BOOTINFO },
+ { 0, 0, NULL, 0 },
+ };
+ static const char short_options[] = KEXEC_ALL_OPT_STR;
+ int opt;
+
+ opterr = 0; /* Don't complain about unrecognized options here */
+ while ((opt = getopt_long(argc, argv, short_options, options, 0)) !=
+ -1) {
+ switch (opt) {
+ default:
+ break;
+ case OPT_BOOTINFO:
+ bootinfo_file = optarg;
+ break;
+ }
+ }
+ /* Reset getopt for the next pass; called in other source modules */
+ opterr = 1;
+ optind = 1;
+ return 0;
+}
+
+const struct arch_map_entry arches[] = {
+ { "m68k", KEXEC_ARCH_68K },
+ { NULL, 0 },
+};
+
+int arch_compat_trampoline(struct kexec_info *UNUSED(info))
+{
+ return 0;
+}
+
+void arch_update_purgatory(struct kexec_info *UNUSED(info))
+{
+}
+
+int is_crashkernel_mem_reserved(void)
+{
+ return 0;
+}
+
+int get_crash_kernel_load_range(uint64_t *start, uint64_t *end)
+{
+ /* Crash kernel region size is not exposed by the system */
+ return -1;
+}
+
+unsigned long virt_to_phys(unsigned long addr)
+{
+ return addr + m68k_memoffset;
+}
+
+/*
+ * add_segment() should convert base to a physical address on m68k,
+ * while the default is just to work with base as is */
+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);
+}
diff --git a/kexec/arch/m68k/kexec-m68k.h b/kexec/arch/m68k/kexec-m68k.h
new file mode 100644
index 0000000..99482c4
--- /dev/null
+++ b/kexec/arch/m68k/kexec-m68k.h
@@ -0,0 +1,9 @@
+#ifndef KEXEC_M68K_H
+#define KEXEC_M68K_H
+
+int elf_m68k_probe(const char *buf, off_t len);
+int elf_m68k_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void elf_m68k_usage(void);
+
+#endif /* KEXEC_M68K_H */
diff --git a/kexec/arch/mips/Makefile b/kexec/arch/mips/Makefile
new file mode 100644
index 0000000..1fe7886
--- /dev/null
+++ b/kexec/arch/mips/Makefile
@@ -0,0 +1,29 @@
+#
+# kexec mips (linux booting linux)
+#
+mips_KEXEC_SRCS = kexec/arch/mips/kexec-mips.c
+mips_KEXEC_SRCS += kexec/arch/mips/kexec-elf-mips.c
+mips_KEXEC_SRCS += kexec/arch/mips/kexec-elf-rel-mips.c
+mips_KEXEC_SRCS += kexec/arch/mips/crashdump-mips.c
+
+mips_FS2DT = kexec/fs2dt.c
+mips_FS2DT_INCLUDE = \
+ -include $(srcdir)/kexec/arch/mips/crashdump-mips.h \
+ -include $(srcdir)/kexec/arch/mips/kexec-mips.h
+
+mips_DT_OPS += kexec/dt-ops.c
+
+include $(srcdir)/kexec/libfdt/Makefile.libfdt
+
+libfdt_SRCS += $(LIBFDT_SRCS:%=kexec/libfdt/%)
+mips_CPPFLAGS += -I$(srcdir)/kexec/libfdt
+mips_KEXEC_SRCS += $(libfdt_SRCS)
+
+mips_ADD_BUFFER =
+mips_ADD_SEGMENT =
+mips_VIRT_TO_PHYS =
+
+dist += kexec/arch/mips/Makefile $(mips_KEXEC_SRCS) \
+ kexec/arch/mips/kexec-mips.h \
+ kexec/arch/mips/crashdump-mips.h \
+ kexec/arch/mips/include/arch/options.h
diff --git a/kexec/arch/mips/crashdump-mips.c b/kexec/arch/mips/crashdump-mips.c
new file mode 100644
index 0000000..00f4335
--- /dev/null
+++ b/kexec/arch/mips/crashdump-mips.c
@@ -0,0 +1,408 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * 2005 (C) IBM Corporation.
+ * 2008 (C) MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <inttypes.h>
+#include <elf.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-syscall.h"
+#include "../../crashdump.h"
+#include "kexec-mips.h"
+#include "crashdump-mips.h"
+#include "unused.h"
+
+/* Stores a sorted list of RAM memory ranges for which to create elf headers.
+ * A separate program header is created for backup region */
+static struct memory_range crash_memory_range[CRASH_MAX_MEMORY_RANGES];
+
+/* Not used currently but required by generic fs2dt code */
+struct memory_ranges usablemem_rgns;
+
+/* Memory region reserved for storing panic kernel and other data. */
+static struct memory_range crash_reserved_mem;
+
+/* Read kernel physical load addr from the file returned by proc_iomem()
+ * (Kernel Code) and store in kexec_info */
+static int get_kernel_paddr(struct crash_elf_info *elf_info)
+{
+ uint64_t start;
+
+ if (xen_present()) /* Kernel not entity mapped under Xen */
+ return 0;
+
+ if (parse_iomem_single("Kernel code\n", &start, NULL) == 0) {
+ elf_info->kern_paddr_start = start;
+ dbgprintf("kernel load physical addr start = 0x%" PRIu64 "\n", start);
+ return 0;
+ }
+
+ fprintf(stderr, "Cannot determine kernel physical load addr\n");
+ return -1;
+}
+
+static int get_kernel_vaddr_and_size(struct crash_elf_info *elf_info,
+ unsigned long start_offset)
+{
+ uint64_t end;
+
+ if (!elf_info->kern_paddr_start)
+ return -1;
+
+ elf_info->kern_vaddr_start = elf_info->kern_paddr_start |
+ start_offset;
+ /* If "Kernel bss" exists, the kernel ends there, else fall
+ * through and say that it ends at "Kernel data" */
+ if (parse_iomem_single("Kernel bss\n", NULL, &end) == 0 ||
+ parse_iomem_single("Kernel data\n", NULL, &end) == 0) {
+ elf_info->kern_size = end - elf_info->kern_paddr_start;
+ dbgprintf("kernel_vaddr= 0x%llx paddr %llx\n",
+ elf_info->kern_vaddr_start,
+ elf_info->kern_paddr_start);
+ dbgprintf("kernel size = 0x%lx\n", elf_info->kern_size);
+ return 0;
+ }
+ fprintf(stderr, "Cannot determine kernel virtual load addr and size\n");
+ return -1;
+}
+
+/* Removes crash reserve region from list of memory chunks for whom elf program
+ * headers have to be created. Assuming crash reserve region to be a single
+ * continuous area fully contained inside one of the memory chunks */
+static int exclude_crash_reserve_region(int *nr_ranges)
+{
+ int i, j, tidx = -1;
+ unsigned long long cstart, cend;
+ struct memory_range temp_region = {
+ .start = 0,
+ .end = 0
+ };
+
+ /* Crash reserved region. */
+ cstart = crash_reserved_mem.start;
+ cend = crash_reserved_mem.end;
+
+ for (i = 0; i < (*nr_ranges); i++) {
+ unsigned long long mstart, mend;
+ mstart = crash_memory_range[i].start;
+ mend = crash_memory_range[i].end;
+ if (cstart < mend && cend > mstart) {
+ if (cstart != mstart && cend != mend) {
+ /* Split memory region */
+ crash_memory_range[i].end = cstart - 1;
+ temp_region.start = cend + 1;
+ temp_region.end = mend;
+ temp_region.type = RANGE_RAM;
+ tidx = i+1;
+ } else if (cstart != mstart)
+ crash_memory_range[i].end = cstart - 1;
+ else
+ crash_memory_range[i].start = cend + 1;
+ }
+ }
+ /* Insert split memory region, if any. */
+ if (tidx >= 0) {
+ if (*nr_ranges == CRASH_MAX_MEMORY_RANGES) {
+ /* No space to insert another element. */
+ fprintf(stderr, "Error: Number of crash memory ranges"
+ " excedeed the max limit\n");
+ return -1;
+ }
+ for (j = (*nr_ranges - 1); j >= tidx; j--)
+ crash_memory_range[j+1] = crash_memory_range[j];
+ crash_memory_range[tidx].start = temp_region.start;
+ crash_memory_range[tidx].end = temp_region.end;
+ crash_memory_range[tidx].type = temp_region.type;
+ (*nr_ranges)++;
+ }
+ return 0;
+}
+/* Reads the appropriate file and retrieves the SYSTEM RAM regions for whom to
+ * create Elf headers. Keeping it separate from get_memory_ranges() as
+ * requirements are different in the case of normal kexec and crashdumps.
+ *
+ * Normal kexec needs to look at all of available physical memory irrespective
+ * of the fact how much of it is being used by currently running kernel.
+ * Crashdumps need to have access to memory regions actually being used by
+ * running kernel. Expecting a different file/data structure than /proc/iomem
+ * to look into down the line. May be something like /proc/kernelmem or may
+ * be zone data structures exported from kernel.
+ */
+static int get_crash_memory_ranges(struct memory_range **range, int *ranges)
+{
+ const char iomem[] = "/proc/iomem";
+ int memory_ranges = 0;
+ char line[MAX_LINE];
+ FILE *fp;
+ unsigned long long start, end;
+
+ fp = fopen(iomem, "r");
+ if (!fp) {
+ fprintf(stderr, "Cannot open %s: %s\n",
+ iomem, strerror(errno));
+ return -1;
+ }
+ /* Separate segment for backup region */
+ crash_memory_range[0].start = BACKUP_SRC_START;
+ crash_memory_range[0].end = BACKUP_SRC_END;
+ crash_memory_range[0].type = RANGE_RAM;
+ memory_ranges++;
+
+ while (fgets(line, sizeof(line), fp) != 0) {
+ char *str;
+ int type, consumed, count;
+ if (memory_ranges >= CRASH_MAX_MEMORY_RANGES)
+ break;
+ count = sscanf(line, "%llx-%llx : %n",
+ &start, &end, &consumed);
+ if (count != 2)
+ continue;
+ str = line + consumed;
+
+ /* Only Dumping memory of type System RAM. */
+ if (memcmp(str, "System RAM\n", 11) == 0) {
+ type = RANGE_RAM;
+ } else if (memcmp(str, "Crash kernel\n", 13) == 0) {
+ /* Reserved memory region. New kernel can
+ * use this region to boot into. */
+ crash_reserved_mem.start = start;
+ crash_reserved_mem.end = end;
+ crash_reserved_mem.type = RANGE_RAM;
+ continue;
+ } else
+ continue;
+
+ if (start == BACKUP_SRC_START && end >= (BACKUP_SRC_END + 1))
+ start = BACKUP_SRC_END + 1;
+
+ crash_memory_range[memory_ranges].start = start;
+ crash_memory_range[memory_ranges].end = end;
+ crash_memory_range[memory_ranges].type = type;
+ memory_ranges++;
+
+ /* Segregate linearly mapped region. */
+ if (MAXMEM && (MAXMEM - 1) >= start && (MAXMEM - 1) <= end) {
+ crash_memory_range[memory_ranges - 1].end = MAXMEM - 1;
+
+ /* Add segregated region. */
+ crash_memory_range[memory_ranges].start = MAXMEM;
+ crash_memory_range[memory_ranges].end = end;
+ crash_memory_range[memory_ranges].type = type;
+ memory_ranges++;
+ }
+ }
+ fclose(fp);
+
+ if (exclude_crash_reserve_region(&memory_ranges) < 0)
+ return -1;
+
+ *range = crash_memory_range;
+ *ranges = memory_ranges;
+ return 0;
+}
+
+/* Adds the appropriate mem= options to command line, indicating the
+ * memory region the new kernel can use to boot into. */
+static int cmdline_add_mem(char *cmdline, unsigned long addr,
+ unsigned long size)
+{
+ int cmdlen, len;
+ char str[50], *ptr;
+
+ addr = addr/1024;
+ size = size/1024;
+ ptr = str;
+ strcpy(str, " mem=");
+ ptr += strlen(str);
+ ultoa(size, ptr);
+ strcat(str, "K@");
+ ptr = str + strlen(str);
+ ultoa(addr, ptr);
+ strcat(str, "K");
+ len = strlen(str);
+ cmdlen = strlen(cmdline) + len;
+ if (cmdlen > (COMMAND_LINE_SIZE - 1))
+ die("Command line overflow\n");
+ strcat(cmdline, str);
+
+ return 0;
+}
+
+/* Adds the elfcorehdr= command line parameter to command line. */
+static int cmdline_add_elfcorehdr(char *cmdline, unsigned long addr)
+{
+ int cmdlen, len, align = 1024;
+ char str[30], *ptr;
+
+ /* Passing in elfcorehdr=xxxK format. Saves space required in cmdline.
+ * Ensure 1K alignment*/
+ if (addr%align)
+ return -1;
+ addr = addr/align;
+ ptr = str;
+ strcpy(str, " elfcorehdr=");
+ ptr += strlen(str);
+ ultoa(addr, ptr);
+ strcat(str, "K");
+ len = strlen(str);
+ cmdlen = strlen(cmdline) + len;
+ if (cmdlen > (COMMAND_LINE_SIZE - 1))
+ die("Command line overflow\n");
+ strcat(cmdline, str);
+ return 0;
+}
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# define ELFDATALOCAL ELFDATA2LSB
+#elif __BYTE_ORDER == __BIG_ENDIAN
+# define ELFDATALOCAL ELFDATA2MSB
+#else
+# error Unknown byte order
+#endif
+
+static struct crash_elf_info elf_info64 = {
+ class: ELFCLASS64,
+ data : ELFDATALOCAL,
+ machine : EM_MIPS,
+ page_offset : PAGE_OFFSET,
+ lowmem_limit : 0, /* 0 == no limit */
+};
+
+static struct crash_elf_info elf_info32 = {
+ class: ELFCLASS32,
+ data : ELFDATALOCAL,
+ machine : EM_MIPS,
+ page_offset : PAGE_OFFSET,
+ lowmem_limit : MAXMEM,
+};
+
+static int patch_elf_info(void)
+{
+ const char cpuinfo[] = "/proc/cpuinfo";
+ char line[MAX_LINE];
+ FILE *fp;
+
+ fp = fopen(cpuinfo, "r");
+ if (!fp) {
+ fprintf(stderr, "Cannot open %s: %s\n",
+ cpuinfo, strerror(errno));
+ return -1;
+ }
+ while (fgets(line, sizeof(line), fp) != 0) {
+ if (strncmp(line, "cpu model", 9) == 0) {
+ /* OCTEON uses a different page_offset. */
+ if (strstr(line, "Octeon"))
+ elf_info64.page_offset = OCTEON_PAGE_OFFSET;
+ /* LOONGSON uses a different page_offset. */
+ else if (strstr(line, "Loongson"))
+ elf_info64.page_offset = LOONGSON_PAGE_OFFSET;
+ break;
+ }
+ }
+ fclose(fp);
+ return 0;
+}
+
+/* Loads additional segments in case of a panic kernel is being loaded.
+ * One segment for backup region, another segment for storing elf headers
+ * for crash memory image.
+ */
+int load_crashdump_segments(struct kexec_info *info, char* mod_cmdline,
+ unsigned long UNUSED(max_addr),
+ unsigned long UNUSED(min_base))
+{
+ void *tmp;
+ unsigned long sz, elfcorehdr;
+ int nr_ranges, align = 1024;
+ struct memory_range *mem_range;
+ crash_create_elf_headers_func crash_create = crash_create_elf32_headers;
+ struct crash_elf_info *elf_info = &elf_info32;
+ unsigned long start_offset = 0x80000000UL;
+
+ if (patch_elf_info())
+ return -1;
+
+ if (arch_options.core_header_type == CORE_TYPE_ELF64) {
+ elf_info = &elf_info64;
+ crash_create = crash_create_elf64_headers;
+ start_offset = (unsigned long)0xffffffff80000000UL;
+ }
+
+ if (get_kernel_paddr(elf_info))
+ return -1;
+
+ if (get_kernel_vaddr_and_size(elf_info, start_offset))
+ return -1;
+
+ if (get_crash_memory_ranges(&mem_range, &nr_ranges) < 0)
+ return -1;
+
+ info->backup_src_start = BACKUP_SRC_START;
+ info->backup_src_size = BACKUP_SRC_SIZE;
+ /* Create a backup region segment to store backup data*/
+ sz = _ALIGN(BACKUP_SRC_SIZE, align);
+ tmp = xmalloc(sz);
+ memset(tmp, 0, sz);
+ info->backup_start = add_buffer(info, tmp, sz, sz, align,
+ crash_reserved_mem.start,
+ crash_reserved_mem.end, -1);
+
+ if (crash_create(info, elf_info, crash_memory_range, nr_ranges,
+ &tmp, &sz, ELF_CORE_HEADER_ALIGN) < 0) {
+ free(tmp);
+ return -1;
+ }
+
+ elfcorehdr = add_buffer(info, tmp, sz, sz, align,
+ crash_reserved_mem.start,
+ crash_reserved_mem.end, -1);
+
+ /*
+ * backup segment is after elfcorehdr, so use elfcorehdr as top of
+ * kernel's available memory
+ */
+ cmdline_add_mem(mod_cmdline, crash_reserved_mem.start,
+ crash_reserved_mem.end - crash_reserved_mem.start + 1);
+ cmdline_add_elfcorehdr(mod_cmdline, elfcorehdr);
+
+ dbgprintf("CRASH MEMORY RANGES:\n");
+ dbgprintf("%016Lx-%016Lx\n", crash_reserved_mem.start,
+ crash_reserved_mem.end);
+ return 0;
+}
+
+int is_crashkernel_mem_reserved(void)
+{
+ uint64_t start, end;
+
+ return parse_iomem_single("Crash kernel\n", &start, &end) == 0 ?
+ (start != end) : 0;
+}
+
+int get_crash_kernel_load_range(uint64_t *start, uint64_t *end)
+{
+ return parse_iomem_single("Crash kernel\n", start, end);
+}
diff --git a/kexec/arch/mips/crashdump-mips.h b/kexec/arch/mips/crashdump-mips.h
new file mode 100644
index 0000000..55c9925
--- /dev/null
+++ b/kexec/arch/mips/crashdump-mips.h
@@ -0,0 +1,30 @@
+#ifndef CRASHDUMP_MIPS_H
+#define CRASHDUMP_MIPS_H
+
+struct kexec_info;
+int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline,
+ unsigned long max_addr, unsigned long min_base);
+#ifdef __mips64
+#define PAGE_OFFSET 0xa800000000000000ULL
+#define MAXMEM 0
+#else
+#define PAGE_OFFSET 0x80000000
+#define MAXMEM 0x80000000
+#endif
+#define __pa(x) ((unsigned long)(X) & 0x7fffffff)
+
+#define LOONGSON_PAGE_OFFSET 0xffffffff80000000ULL
+#define OCTEON_PAGE_OFFSET 0x8000000000000000ULL
+
+#define CRASH_MAX_MEMMAP_NR (KEXEC_MAX_SEGMENTS + 1)
+#define CRASH_MAX_MEMORY_RANGES (MAX_MEMORY_RANGES + 2)
+
+#define COMMAND_LINE_SIZE 512
+
+/* Backup Region, First 1M of System RAM. */
+#define BACKUP_SRC_START 0x00000000
+#define BACKUP_SRC_END 0x000fffff
+#define BACKUP_SRC_SIZE (BACKUP_SRC_END - BACKUP_SRC_START + 1)
+
+extern struct arch_options_t arch_options;
+#endif /* CRASHDUMP_MIPS_H */
diff --git a/kexec/arch/mips/include/arch/options.h b/kexec/arch/mips/include/arch/options.h
new file mode 100644
index 0000000..ba2f346
--- /dev/null
+++ b/kexec/arch/mips/include/arch/options.h
@@ -0,0 +1,43 @@
+#ifndef KEXEC_ARCH_MIPS_OPTIONS_H
+#define KEXEC_ARCH_MIPS_OPTIONS_H
+
+#define OPT_ARCH_MAX (OPT_MAX+0)
+#define OPT_APPEND (OPT_ARCH_MAX+0)
+#define OPT_DTB (OPT_ARCH_MAX+1)
+#define OPT_RAMDISK (OPT_ARCH_MAX+2)
+#define OPT_REUSE_CMDLINE (OPT_ARCH_MAX+3)
+
+/* Options relevant to the architecture (excluding loader-specific ones),
+ * in this case none:
+ */
+#define KEXEC_ARCH_OPTIONS \
+ KEXEC_OPTIONS \
+ {"command-line", 1, 0, OPT_APPEND}, \
+ {"append", 1, 0, OPT_APPEND}, \
+ {"dtb", 1, 0, OPT_DTB }, \
+ {"initrd", 1, 0, OPT_RAMDISK }, \
+ { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE },
+
+
+#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
+
+/* The following two #defines list ALL of the options added by all of the
+ * architecture's loaders.
+ * o main() uses this complete list to scan for its options, ignoring
+ * arch-specific/loader-specific ones.
+ * o Then, arch_process_options() uses this complete list to scan for its
+ * options, ignoring general/loader-specific ones.
+ * o Then, the file_type[n].load re-scans for options, using
+ * KEXEC_ARCH_OPTIONS plus its loader-specific options subset.
+ * Any unrecognised options cause an error here.
+ *
+ * This is done so that main()'s/arch_process_options()'s getopt_long() calls
+ * don't choose a kernel filename from random arguments to options they don't
+ * recognise -- as they now recognise (if not act upon) all possible options.
+ */
+#define KEXEC_ALL_OPTIONS \
+ KEXEC_ARCH_OPTIONS
+
+#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR
+
+#endif /* KEXEC_ARCH_MIPS_OPTIONS_H */
diff --git a/kexec/arch/mips/kexec-elf-mips.c b/kexec/arch/mips/kexec-elf-mips.c
new file mode 100644
index 0000000..230d806
--- /dev/null
+++ b/kexec/arch/mips/kexec-elf-mips.c
@@ -0,0 +1,242 @@
+/*
+ * kexec-elf-mips.c - kexec Elf loader for mips
+ * Copyright (C) 2007 Francesco Chiechi, Alessandro Rubini
+ * Copyright (C) 2007 Tvblob s.r.l.
+ *
+ * derived from ../ppc/kexec-elf-ppc.c
+ * Copyright (C) 2004 Albert Herranz
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+*/
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <elf.h>
+#include <boot/elf_boot.h>
+#include <ip_checksum.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-syscall.h"
+#include "kexec-mips.h"
+#include "crashdump-mips.h"
+#include <arch/options.h>
+#include "../../fs2dt.h"
+#include "../../dt-ops.h"
+
+static const int probe_debug = 0;
+
+#define BOOTLOADER "kexec"
+#define UPSZ(X) _ALIGN_UP(sizeof(X), 4)
+
+#define CMDLINE_PREFIX "kexec "
+static char cmdline_buf[COMMAND_LINE_SIZE] = CMDLINE_PREFIX;
+
+/* Adds initrd parameters to command line. */
+static int cmdline_add_initrd(char *cmdline, unsigned long addr, char *new_para)
+{
+ int cmdlen, len;
+ char str[30], *ptr;
+
+ ptr = str;
+ strcpy(str, new_para);
+ ptr += strlen(str);
+ ultoa(addr, ptr);
+ len = strlen(str);
+ cmdlen = strlen(cmdline) + len;
+ if (cmdlen > (COMMAND_LINE_SIZE - 1))
+ die("Command line overflow\n");
+ strcat(cmdline, str);
+
+ return 0;
+}
+
+/* add initrd to cmdline to compatible with previous platforms. */
+static int patch_initrd_info(char *cmdline, unsigned long base,
+ unsigned long size)
+{
+ const char cpuinfo[] = "/proc/cpuinfo";
+ char line[MAX_LINE];
+ FILE *fp;
+ unsigned long page_offset = PAGE_OFFSET;
+
+ fp = fopen(cpuinfo, "r");
+ if (!fp) {
+ fprintf(stderr, "Cannot open %s: %s\n",
+ cpuinfo, strerror(errno));
+ return -1;
+ }
+ while (fgets(line, sizeof(line), fp) != 0) {
+ if (strncmp(line, "cpu model", 9) == 0) {
+ if (strstr(line, "Loongson")) {
+ /* LOONGSON64 uses a different page_offset. */
+ if (arch_options.core_header_type ==
+ CORE_TYPE_ELF64)
+ page_offset = LOONGSON_PAGE_OFFSET;
+ cmdline_add_initrd(cmdline,
+ page_offset + base, " rd_start=");
+ cmdline_add_initrd(cmdline, size, " rd_size=");
+ break;
+ }
+ }
+ }
+ fclose(fp);
+ return 0;
+}
+
+int elf_mips_probe(const char *buf, off_t len)
+{
+ struct mem_ehdr ehdr;
+ int result;
+ result = build_elf_exec_info(buf, len, &ehdr, 0);
+ if (result < 0) {
+ goto out;
+ }
+
+ /* Verify the architecuture specific bits */
+ if (ehdr.e_machine != EM_MIPS) {
+ /* for a different architecture */
+ if (probe_debug) {
+ fprintf(stderr, "Not for this architecture.\n");
+ }
+ result = -1;
+ goto out;
+ }
+ result = 0;
+ out:
+ free_elf_info(&ehdr);
+ return result;
+}
+
+void elf_mips_usage(void)
+{
+}
+
+int elf_mips_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ struct mem_ehdr ehdr;
+ int command_line_len = 0;
+ char *crash_cmdline;
+ int result;
+ unsigned long cmdline_addr;
+ size_t i;
+ off_t dtb_length;
+ char *dtb_buf;
+ char *initrd_buf = NULL;
+ unsigned long long kernel_addr = 0, kernel_size = 0;
+ unsigned long pagesize = getpagesize();
+
+ /* Need to append some command line parameters internally in case of
+ * taking crash dumps.
+ */
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ crash_cmdline = xmalloc(COMMAND_LINE_SIZE);
+ memset((void *)crash_cmdline, 0, COMMAND_LINE_SIZE);
+ } else
+ crash_cmdline = NULL;
+
+ result = build_elf_exec_info(buf, len, &ehdr, 0);
+ if (result < 0)
+ die("ELF exec parse failed\n");
+
+ /* Read in the PT_LOAD segments and remove CKSEG0 mask from address*/
+ for (i = 0; i < ehdr.e_phnum; i++) {
+ struct mem_phdr *phdr;
+ phdr = &ehdr.e_phdr[i];
+ if (phdr->p_type == PT_LOAD) {
+ phdr->p_paddr = virt_to_phys(phdr->p_paddr);
+ kernel_addr = phdr->p_paddr;
+ kernel_size = phdr->p_memsz;
+ }
+ }
+
+ /* Load the Elf data */
+ result = elf_exec_load(&ehdr, info);
+ if (result < 0)
+ die("ELF exec load failed\n");
+
+ info->entry = (void *)virt_to_phys(ehdr.e_entry);
+
+ if (arch_options.command_line)
+ command_line_len = strlen(arch_options.command_line) + 1;
+
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ result = load_crashdump_segments(info, crash_cmdline,
+ 0, 0);
+ if (result < 0) {
+ free(crash_cmdline);
+ return -1;
+ }
+ }
+
+ if (arch_options.command_line)
+ strncat(cmdline_buf, arch_options.command_line, command_line_len);
+ if (crash_cmdline)
+ {
+ strncat(cmdline_buf, crash_cmdline,
+ sizeof(crash_cmdline) -
+ strlen(crash_cmdline) - 1);
+ free(crash_cmdline);
+ }
+
+ if (info->kexec_flags & KEXEC_ON_CRASH)
+ /* In case of crashdump segment[0] is kernel.
+ * Put cmdline just after it. */
+ cmdline_addr = (unsigned long)info->segment[0].mem +
+ info->segment[0].memsz;
+ else
+ cmdline_addr = 0;
+
+ /* MIPS systems that have been converted to use device tree
+ * passed through UHI will use commandline in the DTB and
+ * the DTB passed as a separate buffer. Note that
+ * CMDLINE_PREFIX is skipped here intentionally, as it is
+ * used only in the legacy method */
+
+ if (arch_options.dtb_file) {
+ dtb_buf = slurp_file(arch_options.dtb_file, &dtb_length);
+ } else {
+ create_flatten_tree(&dtb_buf, &dtb_length, cmdline_buf + strlen(CMDLINE_PREFIX));
+ }
+
+ if (arch_options.initrd_file) {
+ initrd_buf = slurp_file(arch_options.initrd_file, &initrd_size);
+
+ /* Create initrd entries in dtb - although at this time
+ * they would not point to the correct location */
+ dtb_set_initrd(&dtb_buf, &dtb_length, (off_t)initrd_buf, (off_t)initrd_buf + initrd_size);
+
+ initrd_base = add_buffer(info, initrd_buf, initrd_size,
+ initrd_size, sizeof(void *),
+ _ALIGN_UP(kernel_addr + kernel_size + dtb_length,
+ pagesize), 0x0fffffff, 1);
+
+ /* Now that the buffer for initrd is prepared, update the dtb
+ * with an appropriate location */
+ dtb_set_initrd(&dtb_buf, &dtb_length, initrd_base, initrd_base + initrd_size);
+
+ /* Add the initrd parameters to cmdline */
+ patch_initrd_info(cmdline_buf, initrd_base, initrd_size);
+ }
+ /* This is a legacy method for commandline passing used
+ * currently by Octeon CPUs only */
+ add_buffer(info, cmdline_buf, sizeof(cmdline_buf),
+ sizeof(cmdline_buf), sizeof(void *),
+ cmdline_addr, 0x0fffffff, 1);
+
+ add_buffer(info, dtb_buf, dtb_length, dtb_length, 0,
+ _ALIGN_UP(kernel_addr + kernel_size, pagesize),
+ 0x0fffffff, 1);
+
+ return 0;
+}
+
diff --git a/kexec/arch/mips/kexec-elf-rel-mips.c b/kexec/arch/mips/kexec-elf-rel-mips.c
new file mode 100644
index 0000000..5bc22d5
--- /dev/null
+++ b/kexec/arch/mips/kexec-elf-rel-mips.c
@@ -0,0 +1,46 @@
+/*
+ * kexec-elf-rel-mips.c - kexec Elf relocation routines
+ * Copyright (C) 2007 Francesco Chiechi, Alessandro Rubini
+ * Copyright (C) 2007 Tvblob s.r.l.
+ *
+ * derived from ../ppc/kexec-elf-rel-ppc.c
+ * Copyright (C) 2004 Albert Herranz
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+*/
+
+#include <stdio.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+
+int machine_verify_elf_rel(struct mem_ehdr *ehdr)
+{
+ if (ehdr->ei_data != ELFDATA2MSB) {
+ return 0;
+ }
+ if (ehdr->ei_class != ELFCLASS32) {
+ return 0;
+ }
+ if (ehdr->e_machine != EM_MIPS) {
+ return 0;
+ }
+ return 1;
+}
+
+void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr),
+ struct mem_sym *UNUSED(sym),
+ unsigned long r_type,
+ void *UNUSED(location),
+ unsigned long UNUSED(address),
+ unsigned long UNUSED(value))
+{
+ switch(r_type) {
+
+ default:
+ die("Unknown rela relocation: %lu\n", r_type);
+ break;
+ }
+ return;
+}
diff --git a/kexec/arch/mips/kexec-mips.c b/kexec/arch/mips/kexec-mips.c
new file mode 100644
index 0000000..d8cbea8
--- /dev/null
+++ b/kexec/arch/mips/kexec-mips.c
@@ -0,0 +1,191 @@
+/*
+ * kexec-mips.c - kexec for mips
+ * Copyright (C) 2007 Francesco Chiechi, Alessandro Rubini
+ * Copyright (C) 2007 Tvblob s.r.l.
+ *
+ * derived from ../ppc/kexec-ppc.c
+ * Copyright (C) 2004, 2005 Albert Herranz
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "kexec-mips.h"
+#include <arch/options.h>
+
+/* Currently not used but required by top-level fs2dt code */
+off_t initrd_base = 0;
+off_t initrd_size = 0;
+
+static struct memory_range memory_range[MAX_MEMORY_RANGES];
+
+/* Return a sorted list of memory ranges. */
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long UNUSED(kexec_flags))
+{
+ int memory_ranges = 0;
+
+ const char iomem[] = "/proc/iomem";
+ char line[MAX_LINE];
+ FILE *fp;
+ unsigned long long start, end;
+ char *str;
+ int type, consumed, count;
+
+ fp = fopen(iomem, "r");
+ if (!fp) {
+ fprintf(stderr, "Cannot open %s: %s\n", iomem, strerror(errno));
+ return -1;
+ }
+ while (fgets(line, sizeof(line), fp) != 0) {
+ if (memory_ranges >= MAX_MEMORY_RANGES)
+ break;
+ count = sscanf(line, "%llx-%llx : %n", &start, &end, &consumed);
+ if (count != 2)
+ continue;
+ str = line + consumed;
+ end = end + 1;
+ if (memcmp(str, "System RAM\n", 11) == 0) {
+ type = RANGE_RAM;
+ } else if (memcmp(str, "reserved\n", 9) == 0) {
+ type = RANGE_RESERVED;
+ } else {
+ continue;
+ }
+ if (memory_ranges > 0 &&
+ memory_range[memory_ranges - 1].end == start &&
+ memory_range[memory_ranges - 1].type == type) {
+ memory_range[memory_ranges - 1].end = end;
+ } else {
+ memory_range[memory_ranges].start = start;
+ memory_range[memory_ranges].end = end;
+ memory_range[memory_ranges].type = type;
+ memory_ranges++;
+ }
+ }
+ fclose(fp);
+ *range = memory_range;
+ *ranges = memory_ranges;
+ return 0;
+}
+
+struct file_type file_type[] = {
+ {"elf-mips", elf_mips_probe, elf_mips_load, elf_mips_usage},
+};
+int file_types = sizeof(file_type) / sizeof(file_type[0]);
+
+void arch_usage(void)
+{
+ printf(
+ " --command-line=STRING Set the kernel command line to STRING.\n"
+ " --append=STRING Set the kernel command line to STRING.\n"
+ " --dtb=FILE Use FILE as the device tree blob.\n"
+ " --initrd=FILE Use FILE as initial ramdisk.\n"
+ " --reuse-cmdline Use kernel command line from running system.\n"
+ );
+}
+
+struct arch_options_t arch_options = {
+#ifdef __mips64
+ .core_header_type = CORE_TYPE_ELF64,
+#else
+ .core_header_type = CORE_TYPE_ELF32,
+#endif
+};
+
+int arch_process_options(int argc, char **argv)
+{
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { 0 },
+ };
+ static const char short_options[] = KEXEC_ARCH_OPT_STR;
+ int opt;
+ char *cmdline = NULL;
+ const char *append = NULL;
+
+ while ((opt = getopt_long(argc, argv, short_options,
+ options, 0)) != -1) {
+ switch (opt) {
+ case OPT_APPEND:
+ append = optarg;
+ break;
+ case OPT_REUSE_CMDLINE:
+ cmdline = get_command_line();
+ break;
+ case OPT_DTB:
+ arch_options.dtb_file = optarg;
+ break;
+ case OPT_RAMDISK:
+ arch_options.initrd_file = optarg;
+ break;
+ default:
+ break;
+ }
+ }
+
+ arch_options.command_line = concat_cmdline(cmdline, append);
+
+ dbgprintf("%s:%d: command_line: %s\n", __func__, __LINE__,
+ arch_options.command_line);
+ dbgprintf("%s:%d: initrd: %s\n", __func__, __LINE__,
+ arch_options.initrd_file);
+ dbgprintf("%s:%d: dtb: %s\n", __func__, __LINE__,
+ arch_options.dtb_file);
+
+ return 0;
+}
+
+const struct arch_map_entry arches[] = {
+ /* For compatibility with older patches
+ * use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_MIPS here.
+ */
+ { "mips", KEXEC_ARCH_MIPS },
+ { "mips64", KEXEC_ARCH_MIPS },
+ { NULL, 0 },
+};
+
+int arch_compat_trampoline(struct kexec_info *UNUSED(info))
+{
+
+ return 0;
+}
+
+void arch_update_purgatory(struct kexec_info *UNUSED(info))
+{
+}
+
+unsigned long virt_to_phys(unsigned long addr)
+{
+ return addr & 0x7fffffff;
+}
+
+/*
+ * add_segment() should convert base to a physical address on mips,
+ * while the default is just to work with base as is */
+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, virt_to_phys(base), memsz, 1);
+}
+
+/*
+ * add_buffer() should convert base to a physical address on mips,
+ * while the default is just to work with base as is */
+unsigned long add_buffer(struct kexec_info *info, const void *buf,
+ unsigned long bufsz, unsigned long memsz,
+ unsigned long buf_align, unsigned long buf_min,
+ unsigned long buf_max, int buf_end)
+{
+ return add_buffer_phys_virt(info, buf, bufsz, memsz, buf_align,
+ buf_min, buf_max, buf_end, 1);
+}
+
diff --git a/kexec/arch/mips/kexec-mips.h b/kexec/arch/mips/kexec-mips.h
new file mode 100644
index 0000000..222c815
--- /dev/null
+++ b/kexec/arch/mips/kexec-mips.h
@@ -0,0 +1,30 @@
+#ifndef KEXEC_MIPS_H
+#define KEXEC_MIPS_H
+
+#include <sys/types.h>
+
+#define BOOT_BLOCK_VERSION 17
+#define BOOT_BLOCK_LAST_COMP_VERSION 16
+
+#define MAX_MEMORY_RANGES 64
+#define MAX_LINE 160
+
+#define CORE_TYPE_ELF32 1
+#define CORE_TYPE_ELF64 2
+
+int elf_mips_probe(const char *buf, off_t len);
+int elf_mips_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void elf_mips_usage(void);
+
+struct arch_options_t {
+ char *command_line;
+ char *dtb_file;
+ char *initrd_file;
+ int core_header_type;
+};
+
+extern struct memory_ranges usablemem_rgns;
+extern off_t initrd_base, initrd_size;
+
+#endif /* KEXEC_MIPS_H */
diff --git a/kexec/arch/ppc/Makefile b/kexec/arch/ppc/Makefile
new file mode 100644
index 0000000..71871f1
--- /dev/null
+++ b/kexec/arch/ppc/Makefile
@@ -0,0 +1,34 @@
+#
+# kexec ppc (linux booting linux)
+#
+include $(srcdir)/kexec/libfdt/Makefile.libfdt
+
+ppc_KEXEC_SRCS = kexec/arch/ppc/kexec-ppc.c
+ppc_KEXEC_SRCS += kexec/arch/ppc/kexec-elf-ppc.c
+ppc_KEXEC_SRCS += kexec/arch/ppc/kexec-elf-rel-ppc.c
+ppc_KEXEC_SRCS += kexec/arch/ppc/kexec-dol-ppc.c
+ppc_KEXEC_SRCS += kexec/arch/ppc/kexec-uImage-ppc.c
+ppc_KEXEC_SRCS += kexec/arch/ppc/ppc-setup-simple.S
+ppc_KEXEC_SRCS += kexec/arch/ppc/ppc-setup-dol.S
+ppc_KEXEC_SRCS += kexec/arch/ppc/fixup_dtb.c
+ppc_KEXEC_SRCS += kexec/arch/ppc/fs2dt.c
+ppc_KEXEC_SRCS += kexec/arch/ppc/crashdump-powerpc.c
+
+ppc_UIMAGE = kexec/kexec-uImage.c
+
+ppc_libfdt_SRCS = kexec/arch/ppc/libfdt-wrapper.c
+libfdt_SRCS += $(LIBFDT_SRCS:%=kexec/libfdt/%)
+ppc_ARCH_REUSE_INITRD =
+
+ppc_CPPFLAGS = -I$(srcdir)/kexec/libfdt
+
+ppc_KEXEC_SRCS += $(libfdt_SRCS) $(ppc_libfdt_SRCS)
+
+ppc_ASFLAGS = -Wa,--noexecstack
+
+dist += kexec/arch/ppc/Makefile $(ppc_KEXEC_SRCS) \
+ kexec/arch/ppc/crashdump-powerpc.h kexec/arch/ppc/fixup_dtb.h \
+ kexec/arch/ppc/kexec-ppc.h kexec/arch/ppc/ops.h \
+ kexec/arch/ppc/ppc_asm.h \
+ kexec/arch/ppc/include/page.h kexec/arch/ppc/include/types.h \
+ kexec/arch/ppc/include/arch/options.h
diff --git a/kexec/arch/ppc/crashdump-powerpc.c b/kexec/arch/ppc/crashdump-powerpc.c
new file mode 100644
index 0000000..15e8531
--- /dev/null
+++ b/kexec/arch/ppc/crashdump-powerpc.c
@@ -0,0 +1,433 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <elf.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-syscall.h"
+#include "../../crashdump.h"
+#include "kexec-ppc.h"
+#include "crashdump-powerpc.h"
+
+#define DEVTREE_CRASHKERNEL_BASE "/proc/device-tree/chosen/linux,crashkernel-base"
+#define DEVTREE_CRASHKERNEL_SIZE "/proc/device-tree/chosen/linux,crashkernel-size"
+
+#ifdef CONFIG_PPC64
+static struct crash_elf_info elf_info64 = {
+class: ELFCLASS64,
+data: ELFDATA2MSB,
+machine: EM_PPC64,
+page_offset: PAGE_OFFSET,
+lowmem_limit: MAXMEM,
+};
+#endif
+static struct crash_elf_info elf_info32 = {
+class: ELFCLASS32,
+data: ELFDATA2MSB,
+#ifdef CONFIG_PPC64
+machine: EM_PPC64,
+#else
+machine: EM_PPC,
+#endif
+page_offset: PAGE_OFFSET,
+lowmem_limit: MAXMEM,
+};
+
+/* Stores a sorted list of RAM memory ranges for which to create elf headers.
+ * A separate program header is created for backup region
+ */
+static struct memory_range *crash_memory_range;
+static int crash_nr_memory_ranges;
+
+/* Define a variable to replace the CRASH_MAX_MEMORY_RANGES macro */
+static int crash_max_memory_ranges;
+
+/*
+ * Used to save various memory ranges/regions needed for the captured
+ * kernel to boot. (lime memmap= option in other archs)
+ */
+mem_rgns_t usablemem_rgns = {0, NULL};
+
+/* Append a segment to crash_memory_range, splitting it into two if
+ * it contains both lowmem and highmem */
+static void add_crash_memory_range(unsigned long long start,
+ unsigned long long end)
+{
+#ifndef CONFIG_PPC64
+ if (start < elf_info32.lowmem_limit && end > elf_info32.lowmem_limit) {
+ add_crash_memory_range(start, elf_info32.lowmem_limit);
+ add_crash_memory_range(elf_info32.lowmem_limit, end);
+ return;
+ }
+#endif
+
+ if (crash_nr_memory_ranges < crash_max_memory_ranges) {
+ crash_memory_range[crash_nr_memory_ranges].start = start;
+ crash_memory_range[crash_nr_memory_ranges].end = end;
+ crash_memory_range[crash_nr_memory_ranges].type = RANGE_RAM;
+ }
+
+ crash_nr_memory_ranges++;
+}
+
+
+/* Reads the appropriate file and retrieves the SYSTEM RAM regions for whom to
+ * create Elf headers. Keeping it separate from get_memory_ranges() as
+ * requirements are different in the case of normal kexec and crashdumps.
+ *
+ * Normal kexec needs to look at all of available physical memory irrespective
+ * of the fact how much of it is being used by currently running kernel.
+ * Crashdumps need to have access to memory regions actually being used by
+ * running kernel. Expecting a different file/data structure than /proc/iomem
+ * to look into down the line. May be something like /proc/kernelmem or may
+ * be zone data structures exported from kernel.
+ */
+static int get_crash_memory_ranges(struct memory_range **range, int *ranges)
+{
+
+ char device_tree[256] = "/proc/device-tree/";
+ char fname[256];
+ DIR *dir, *dmem;
+ int fd;
+ struct dirent *dentry, *mentry;
+ int n, crash_rng_len = 0;
+ unsigned long long start, end, cstart, cend;
+
+ crash_max_memory_ranges = max_memory_ranges + 6;
+ crash_rng_len = sizeof(struct memory_range) * crash_max_memory_ranges;
+
+ crash_memory_range = (struct memory_range *) malloc(crash_rng_len);
+ if (!crash_memory_range) {
+ fprintf(stderr, "Allocation for crash memory range failed\n");
+ return -1;
+ }
+ memset(crash_memory_range, 0, crash_rng_len);
+ crash_nr_memory_ranges = 0;
+
+#ifndef CONFIG_BOOKE
+ /* create a separate program header for the backup region */
+ add_crash_memory_range(BACKUP_SRC_START, BACKUP_SRC_END + 1);
+#endif
+
+ dir = opendir(device_tree);
+ if (!dir) {
+ perror(device_tree);
+ goto err;
+ }
+ while ((dentry = readdir(dir)) != NULL) {
+ if (strncmp(dentry->d_name, "memory@", 7)
+ && strcmp(dentry->d_name, "memory"))
+ continue;
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ dmem = opendir(fname);
+ if (!dmem) {
+ perror(fname);
+ closedir(dir);
+ goto err;
+ }
+ while ((mentry = readdir(dmem)) != NULL) {
+ if (strcmp(mentry->d_name, "reg"))
+ continue;
+ strcat(fname, "/reg");
+ fd = open(fname, O_RDONLY);
+ if (fd < 0) {
+ perror(fname);
+ closedir(dmem);
+ closedir(dir);
+ goto err;
+ }
+ n = read_memory_region_limits(fd, &start, &end);
+ /* We are done with fd, close it. */
+ close(fd);
+ if (n != 0) {
+ closedir(dmem);
+ closedir(dir);
+ goto err;
+ }
+#ifndef CONFIG_BOOKE
+ if (start == 0 && end >= (BACKUP_SRC_END + 1))
+ start = BACKUP_SRC_END + 1;
+#endif
+
+ /*
+ * Exclude the region that lies within crashkernel.
+ * If memory limit is set then exclude memory region
+ * above it.
+ */
+
+ if (memory_limit) {
+ if (start >= memory_limit)
+ continue;
+ if (end > memory_limit)
+ end = memory_limit;
+ }
+
+ /*
+ * Exclure region used by crash kernel
+ */
+ cstart = crash_base;
+ cend = crash_base + crash_size;
+
+ if (cstart >= end || cend <= start)
+ add_crash_memory_range(start, end);
+ else {
+ if (start < cstart)
+ add_crash_memory_range(start, cstart);
+ if (cend < end)
+ add_crash_memory_range(cend, end);
+ }
+ }
+ closedir(dmem);
+ }
+ closedir(dir);
+
+ /*
+ * If RTAS region is overlapped with crashkernel, need to create ELF
+ * Program header for the overlapped memory.
+ */
+ if (crash_base < rtas_base + rtas_size &&
+ rtas_base < crash_base + crash_size) {
+ cstart = rtas_base;
+ cend = rtas_base + rtas_size;
+ if (cstart < crash_base)
+ cstart = crash_base;
+ if (cend > crash_base + crash_size)
+ cend = crash_base + crash_size;
+ add_crash_memory_range(cstart, cend);
+ }
+
+ if (crash_nr_memory_ranges >= crash_max_memory_ranges) {
+ fprintf(stderr,
+ "Error: Number of crash memory ranges"
+ " excedeed the max limit\n");
+ goto err;
+ }
+
+ *range = crash_memory_range;
+ *ranges = crash_nr_memory_ranges;
+
+ int j;
+ dbgprintf("CRASH MEMORY RANGES\n");
+ for (j = 0; j < *ranges; j++) {
+ start = crash_memory_range[j].start;
+ end = crash_memory_range[j].end;
+ dbgprintf("%016Lx-%016Lx\n", start, end);
+ }
+
+ return 0;
+
+err:
+ if (crash_memory_range)
+ free(crash_memory_range);
+ return -1;
+}
+
+/* Converts unsigned long to ascii string. */
+static void ulltoa(unsigned long long i, char *str)
+{
+ int j = 0, k;
+ char tmp;
+
+ do {
+ str[j++] = i % 10 + '0';
+ } while ((i /= 10) > 0);
+ str[j] = '\0';
+
+ /* Reverse the string. */
+ for (j = 0, k = strlen(str) - 1; j < k; j++, k--) {
+ tmp = str[k];
+ str[k] = str[j];
+ str[j] = tmp;
+ }
+}
+
+/* Append str to cmdline */
+static void add_cmdline(char *cmdline, char *str)
+{
+ int cmdline_size;
+ int cmdlen = strlen(cmdline) + strlen(str);
+
+ cmdline_size = COMMAND_LINE_SIZE;
+ if (cmdlen > (cmdline_size - 1))
+ die("Command line overflow\n");
+ strcat(cmdline, str);
+}
+
+static int add_cmdline_param(char *cmdline, unsigned long long addr,
+ char *cmdstr, char *byte)
+{
+ int align = 1024;
+ char str[COMMAND_LINE_SIZE], *ptr;
+
+ /* Passing in =xxxK / =xxxM format. Saves space required in cmdline.*/
+ switch (byte[0]) {
+ case 'K':
+ if (addr%align)
+ return -1;
+ addr = addr/align;
+ break;
+ case 'M':
+ addr = addr/(align *align);
+ break;
+ }
+ ptr = str;
+ strcpy(str, cmdstr);
+ ptr += strlen(str);
+ ulltoa(addr, ptr);
+ strcat(str, byte);
+
+ add_cmdline(cmdline, str);
+
+ dbgprintf("Command line after adding elfcorehdr: %s\n", cmdline);
+
+ return 0;
+}
+
+/* Loads additional segments in case of a panic kernel is being loaded.
+ * One segment for backup region, another segment for storing elf headers
+ * for crash memory image.
+ */
+int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline,
+ unsigned long max_addr, unsigned long min_base)
+{
+ void *tmp;
+ unsigned long sz, elfcorehdr;
+ int nr_ranges, align = 1024, i;
+ unsigned long long end;
+ struct memory_range *mem_range;
+
+ if (get_crash_memory_ranges(&mem_range, &nr_ranges) < 0)
+ return -1;
+
+ info->backup_src_start = BACKUP_SRC_START;
+ info->backup_src_size = BACKUP_SRC_SIZE;
+#ifndef CONFIG_BOOKE
+ /* Create a backup region segment to store backup data*/
+ sz = _ALIGN(BACKUP_SRC_SIZE, align);
+ tmp = xmalloc(sz);
+ memset(tmp, 0, sz);
+ info->backup_start = add_buffer(info, tmp, sz, sz, align,
+ 0, max_addr, 1);
+ reserve(info->backup_start, sz);
+#endif
+
+ /* On powerpc memory ranges in device-tree is denoted as start
+ * and size rather than start and end, as is the case with
+ * other architectures like i386 . Because of this when loading
+ * the memory ranges in crashdump-elf.c the filesz calculation
+ * [ end - start + 1 ] goes for a toss.
+ *
+ * To be in sync with other archs adjust the end value for
+ * every crash memory range before calling the generic function
+ */
+
+ for (i = 0; i < nr_ranges; i++) {
+ end = crash_memory_range[i].end - 1;
+ crash_memory_range[i].end = end;
+ }
+
+
+#ifdef CONFIG_PPC64
+ /* Create elf header segment and store crash image data. */
+ if (arch_options.core_header_type == CORE_TYPE_ELF64) {
+ if (crash_create_elf64_headers(info, &elf_info64,
+ crash_memory_range, nr_ranges, &tmp,
+ &sz, ELF_CORE_HEADER_ALIGN) < 0)
+ return -1;
+ } else if (crash_create_elf32_headers(info, &elf_info32,
+ crash_memory_range, nr_ranges, &tmp, &sz,
+ ELF_CORE_HEADER_ALIGN) < 0)
+ return -1;
+#else
+ if (crash_create_elf32_headers(info, &elf_info32, crash_memory_range,
+ nr_ranges, &tmp, &sz, ELF_CORE_HEADER_ALIGN)
+ < 0)
+ return -1;
+#endif
+
+ elfcorehdr = add_buffer(info, tmp, sz, sz, align,
+ min_base, max_addr, 1);
+ reserve(elfcorehdr, sz);
+ /* modify and store the cmdline in a global array. This is later
+ * read by flatten_device_tree and modified if required
+ */
+ add_cmdline_param(mod_cmdline, elfcorehdr, " elfcorehdr=", "K");
+ add_cmdline(mod_cmdline, " maxcpus=1");
+ return 0;
+}
+
+/*
+ * Used to save various memory regions needed for the captured kernel.
+ */
+
+void add_usable_mem_rgns(unsigned long long base, unsigned long long size)
+{
+ int i;
+ unsigned long long end = base + size;
+ unsigned long long ustart, uend;
+
+ base = _ALIGN_DOWN(base, getpagesize());
+ end = _ALIGN_UP(end, getpagesize());
+
+ for (i = 0; i < usablemem_rgns.size; i++) {
+ ustart = usablemem_rgns.ranges[i].start;
+ uend = usablemem_rgns.ranges[i].end;
+ if (base < uend && end > ustart) {
+ if ((base >= ustart) && (end <= uend))
+ return;
+ if (base < ustart && end > uend) {
+ usablemem_rgns.ranges[i].start = base;
+ usablemem_rgns.ranges[i].end = end;
+ return;
+ } else if (base < ustart) {
+ usablemem_rgns.ranges[i].start = base;
+ return;
+ } else if (end > uend) {
+ usablemem_rgns.ranges[i].end = end;
+ return;
+ }
+ }
+ }
+ usablemem_rgns.ranges[usablemem_rgns.size].start = base;
+ usablemem_rgns.ranges[usablemem_rgns.size++].end = end;
+
+ dbgprintf("usable memory rgns size:%u base:%llx size:%llx\n",
+ usablemem_rgns.size, base, size);
+}
+
+int get_crash_kernel_load_range(uint64_t *start, uint64_t *end)
+{
+ unsigned long long value;
+
+ if (!get_devtree_value(DEVTREE_CRASHKERNEL_BASE, &value))
+ *start = value;
+ else
+ return -1;
+
+ if (!get_devtree_value(DEVTREE_CRASHKERNEL_SIZE, &value))
+ *end = *start + value - 1;
+ else
+ return -1;
+
+ return 0;
+}
+
+int is_crashkernel_mem_reserved(void)
+{
+ int fd;
+
+ fd = open(DEVTREE_CRASHKERNEL_BASE, O_RDONLY);
+ if (fd < 0)
+ return 0;
+ close(fd);
+ return 1;
+}
+
diff --git a/kexec/arch/ppc/crashdump-powerpc.h b/kexec/arch/ppc/crashdump-powerpc.h
new file mode 100644
index 0000000..97b5095
--- /dev/null
+++ b/kexec/arch/ppc/crashdump-powerpc.h
@@ -0,0 +1,45 @@
+#ifndef CRASHDUMP_POWERPC_H
+#define CRASHDUMP_POWERPC_H
+
+struct kexec_info;
+int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline,
+ unsigned long max_addr, unsigned long min_base);
+void add_usable_mem_rgns(unsigned long long base, unsigned long long size);
+
+extern struct arch_options_t arch_options;
+
+#ifdef CONFIG_PPC64
+#define PAGE_OFFSET 0xC000000000000000UL
+#define VMALLOCBASE 0xD000000000000000UL
+#define MAXMEM (-KERNELBASE-VMALLOCBASE)
+#else
+#define PAGE_OFFSET 0xC0000000
+#define MAXMEM 0x30000000 /* Use CONFIG_LOWMEM_SIZE from kernel */
+#endif
+
+#define KERNELBASE PAGE_OFFSET
+#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
+
+#ifdef CONFIG_BOOKE
+/* We don't need backup region in Book E */
+#define BACKUP_SRC_START 0x0000
+#define BACKUP_SRC_END 0x0000
+#define BACKUP_SRC_SIZE 0x0000
+#else
+/* Backup Region, First 64K of System RAM. */
+#define BACKUP_SRC_START 0x0000
+#define BACKUP_SRC_END 0xffff
+#define BACKUP_SRC_SIZE (BACKUP_SRC_END - BACKUP_SRC_START + 1)
+#endif
+
+#define KDUMP_BACKUP_LIMIT BACKUP_SRC_SIZE
+
+extern unsigned long long crash_base;
+extern unsigned long long crash_size;
+extern unsigned int rtas_base;
+extern unsigned int rtas_size;
+extern uint64_t opal_base;
+extern uint64_t opal_size;
+extern uint64_t memory_limit;
+
+#endif /* CRASHDUMP_POWERPC_H */
diff --git a/kexec/arch/ppc/fixup_dtb.c b/kexec/arch/ppc/fixup_dtb.c
new file mode 100644
index 0000000..92a0bfd
--- /dev/null
+++ b/kexec/arch/ppc/fixup_dtb.c
@@ -0,0 +1,408 @@
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include <libfdt.h>
+#include "ops.h"
+#include "page.h"
+#include "fixup_dtb.h"
+#include "kexec-ppc.h"
+
+const char proc_dts[] = "/proc/device-tree";
+
+static void print_fdt_reserve_regions(char *blob_buf)
+{
+ int i, num;
+
+ if (!kexec_debug)
+ return;
+ /* Print out a summary of the final reserve regions */
+ num = fdt_num_mem_rsv(blob_buf);
+ dbgprintf ("reserve regions: %d\n", num);
+ for (i = 0; i < num; i++) {
+ uint64_t offset, size;
+
+ if (fdt_get_mem_rsv(blob_buf, i, &offset, &size) == 0) {
+ dbgprintf("%d: offset: %llx, size: %llx\n", i, offset, size);
+ } else {
+ dbgprintf("Error retreiving reserved region\n");
+ }
+ }
+}
+
+
+static void fixup_nodes(char *nodes[])
+{
+ int index = 0;
+ char *fname;
+ char *prop_name;
+ char *node_name;
+ void *node;
+ int len;
+ char *content;
+ off_t content_size;
+ int ret;
+
+ while (nodes[index]) {
+
+ len = asprintf(&fname, "%s%s", proc_dts, nodes[index]);
+ if (len < 0)
+ die("asprintf() failed\n");
+
+ content = slurp_file(fname, &content_size);
+ if (!content) {
+ die("Can't open %s: %s\n", fname, strerror(errno));
+ }
+
+ prop_name = fname + len;
+ while (*prop_name != '/')
+ prop_name--;
+
+ *prop_name = '\0';
+ prop_name++;
+
+ node_name = fname + sizeof(proc_dts) - 1;
+
+ node = finddevice(node_name);
+ if (!node)
+ node = create_node(NULL, node_name + 1);
+
+ ret = setprop(node, prop_name, content, content_size);
+ if (ret < 0)
+ die("setprop of %s/%s size: %ld failed: %s\n",
+ node_name, prop_name, content_size,
+ fdt_strerror(ret));
+
+ free(content);
+ free(fname);
+ index++;
+ };
+}
+
+/*
+ * command line priority:
+ * - use the supplied command line
+ * - if none available use the command line from .dtb
+ * - if not available use the current command line
+ */
+static void fixup_cmdline(const char *cmdline)
+{
+ void *chosen;
+ char *fixup_cmd_node[] = {
+ "/chosen/bootargs",
+ NULL,
+ };
+
+ chosen = finddevice("/chosen");
+
+ if (!cmdline) {
+ if (!chosen)
+ fixup_nodes(fixup_cmd_node);
+ } else {
+ if (!chosen)
+ chosen = create_node(NULL, "chosen");
+ setprop_str(chosen, "bootargs", cmdline);
+ }
+ return;
+}
+
+#define EXPAND_GRANULARITY 1024
+
+static char *expand_buf(int minexpand, char *blob_buf, off_t *blob_size)
+{
+ int size = fdt_totalsize(blob_buf);
+ int rc;
+
+ size = _ALIGN(size + minexpand, EXPAND_GRANULARITY);
+ blob_buf = realloc(blob_buf, size);
+ if (!blob_buf)
+ die("Couldn't find %d bytes to expand device tree\n\r", size);
+ rc = fdt_open_into(blob_buf, blob_buf, size);
+ if (rc != 0)
+ die("Couldn't expand fdt into new buffer: %s\n\r",
+ fdt_strerror(rc));
+
+ *blob_size = fdt_totalsize(blob_buf);
+
+ return blob_buf;
+}
+
+static void fixup_reserve_regions(struct kexec_info *info, char *blob_buf)
+{
+ int ret, i;
+ int nodeoffset;
+ u64 val = 0;
+
+ /* If this is a KEXEC kernel we add all regions since they will
+ * all need to be saved */
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ for (i = 0; i < info->nr_segments; i++) {
+ uint64_t address = (unsigned long)info->segment[i].mem;
+ uint64_t size = info->segment[i].memsz;
+
+ while ((i+1) < info->nr_segments &&
+ (address + size == (unsigned long)info->segment[i+1].mem)) {
+ size += info->segment[++i].memsz;
+ }
+
+ ret = fdt_add_mem_rsv(blob_buf, address, size);
+ if (ret) {
+ printf("%s: Error adding memory range to memreserve!\n",
+ fdt_strerror(ret));
+ goto out;
+ }
+ }
+ } else if (ramdisk || reuse_initrd) {
+ /* Otherwise we just add back the ramdisk and the device tree
+ * is already in the list */
+ ret = fdt_add_mem_rsv(blob_buf, ramdisk_base, ramdisk_size);
+ if (ret) {
+ printf("%s: Unable to add new reserved memory for initrd flat device tree\n",
+ fdt_strerror(ret));
+ goto out;
+ }
+ }
+
+#if 0
+ /* XXX: Do not reserve spin-table for CPUs. */
+
+ /* Add reserve regions for cpu-release-addr */
+ nodeoffset = fdt_node_offset_by_prop_value(blob_buf, -1, "device_type", "cpu", 4);
+ while (nodeoffset != -FDT_ERR_NOTFOUND) {
+ const void *buf;
+ int sz, ret;
+ u64 tmp;
+
+ buf = fdt_getprop(blob_buf, nodeoffset, "cpu-release-addr", &sz);
+
+ if (buf) {
+ if (sz == 4) {
+ tmp = *(u32 *)buf;
+ } else if (sz == 8) {
+ tmp = *(u64 *)buf;
+ }
+
+ /* crude check to see if last value is repeated */
+ if (_ALIGN_DOWN(tmp, PAGE_SIZE) != _ALIGN_DOWN(val, PAGE_SIZE)) {
+ val = tmp;
+ ret = fdt_add_mem_rsv(blob_buf, _ALIGN_DOWN(val, PAGE_SIZE), PAGE_SIZE);
+ if (ret)
+ printf("%s: Unable to add reserve for cpu-release-addr!\n",
+ fdt_strerror(ret));
+ }
+ }
+
+ nodeoffset = fdt_node_offset_by_prop_value(blob_buf, nodeoffset,
+ "device_type", "cpu", 4);
+ }
+#endif
+
+out:
+ print_fdt_reserve_regions(blob_buf);
+}
+
+static void fixup_memory(struct kexec_info *info, char *blob_buf)
+{
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ int nodeoffset, len = 0;
+ u8 tmp[16];
+ const unsigned long *addrcell, *sizecell;
+
+ nodeoffset = fdt_path_offset(blob_buf, "/memory");
+
+ if (nodeoffset < 0) {
+ printf("Error searching for memory node!\n");
+ return;
+ }
+
+ addrcell = fdt_getprop(blob_buf, 0, "#address-cells", NULL);
+ /* use shifts and mask to ensure endianness */
+ if ((addrcell) && (*addrcell == 2)) {
+ tmp[0] = (crash_base >> 56) & 0xff;
+ tmp[1] = (crash_base >> 48) & 0xff;
+ tmp[2] = (crash_base >> 40) & 0xff;
+ tmp[3] = (crash_base >> 32) & 0xff;
+ tmp[4] = (crash_base >> 24) & 0xff;
+ tmp[5] = (crash_base >> 16) & 0xff;
+ tmp[6] = (crash_base >> 8) & 0xff;
+ tmp[7] = (crash_base ) & 0xff;
+ len = 8;
+ } else {
+ tmp[0] = (crash_base >> 24) & 0xff;
+ tmp[1] = (crash_base >> 16) & 0xff;
+ tmp[2] = (crash_base >> 8) & 0xff;
+ tmp[3] = (crash_base ) & 0xff;
+ len = 4;
+ }
+
+ sizecell = fdt_getprop(blob_buf, 0, "#size-cells", NULL);
+ /* use shifts and mask to ensure endianness */
+ if ((sizecell) && (*sizecell == 2)) {
+ tmp[0+len] = (crash_size >> 56) & 0xff;
+ tmp[1+len] = (crash_size >> 48) & 0xff;
+ tmp[2+len] = (crash_size >> 40) & 0xff;
+ tmp[3+len] = (crash_size >> 32) & 0xff;
+ tmp[4+len] = (crash_size >> 24) & 0xff;
+ tmp[5+len] = (crash_size >> 16) & 0xff;
+ tmp[6+len] = (crash_size >> 8) & 0xff;
+ tmp[7+len] = (crash_size ) & 0xff;
+ len += 8;
+ } else {
+ tmp[0+len] = (crash_size >> 24) & 0xff;
+ tmp[1+len] = (crash_size >> 16) & 0xff;
+ tmp[2+len] = (crash_size >> 8) & 0xff;
+ tmp[3+len] = (crash_size ) & 0xff;
+ len += 4;
+ }
+
+ if (fdt_setprop(blob_buf, nodeoffset, "reg", tmp, len) != 0) {
+ printf ("Error setting memory node!\n");
+ }
+
+ fdt_delprop(blob_buf, nodeoffset, "linux,usable-memory");
+ }
+}
+
+/* removes crashkernel nodes if they exist and we are *rebooting*
+ * into a crashkernel. These nodes should not exist after we
+ * crash and reboot into a new kernel
+ */
+static void fixup_crashkernel(struct kexec_info *info, char *blob_buf)
+{
+ int nodeoffset;
+
+ nodeoffset = fdt_path_offset(blob_buf, "/chosen");
+
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ if (nodeoffset < 0) {
+ printf("fdt_crashkernel: %s\n", fdt_strerror(nodeoffset));
+ return;
+ }
+
+ fdt_delprop(blob_buf, nodeoffset, "linux,crashkernel-base");
+ fdt_delprop(blob_buf, nodeoffset, "linux,crashkernel-size");
+ }
+}
+/* remove the old chosen nodes if they exist and add correct chosen
+ * nodes if we have an initd
+ */
+static void fixup_initrd(char *blob_buf)
+{
+ int err, nodeoffset;
+ unsigned long tmp;
+
+ nodeoffset = fdt_path_offset(blob_buf, "/chosen");
+
+ if (nodeoffset < 0) {
+ printf("fdt_initrd: %s\n", fdt_strerror(nodeoffset));
+ return;
+ }
+
+ fdt_delprop(blob_buf, nodeoffset, "linux,initrd-start");
+ fdt_delprop(blob_buf, nodeoffset, "linux,initrd-end");
+
+ if ((reuse_initrd || ramdisk) &&
+ ((ramdisk_base != 0) && (ramdisk_size != 0))) {
+ tmp = ramdisk_base;
+ err = fdt_setprop(blob_buf, nodeoffset,
+ "linux,initrd-start", &tmp, sizeof(tmp));
+ if (err < 0) {
+ printf("WARNING: "
+ "could not set linux,initrd-start %s.\n",
+ fdt_strerror(err));
+ return;
+ }
+
+ tmp = ramdisk_base + ramdisk_size;
+ err = fdt_setprop(blob_buf, nodeoffset,
+ "linux,initrd-end", &tmp, sizeof(tmp));
+ if (err < 0) {
+ printf("WARNING: could not set linux,initrd-end %s.\n",
+ fdt_strerror(err));
+ return;
+ }
+ }
+}
+
+char *fixup_dtb_init(struct kexec_info *info, char *blob_buf, off_t *blob_size,
+ unsigned long hole_addr, unsigned long *dtb_addr)
+{
+ int ret, i, num = fdt_num_mem_rsv(blob_buf);
+
+ fdt_init(blob_buf);
+
+ /* Remove the existing reserve regions as they will no longer
+ * be valid after we reboot */
+ for (i = num - 1; i >= 0; i--) {
+ ret = fdt_del_mem_rsv(blob_buf, i);
+ if (ret) {
+ printf("%s: Error deleting memory reserve region %d from device tree!\n",
+ fdt_strerror(ret), i);
+ }
+ }
+
+ /* Pack the FDT first, so we don't grow excessively if there is already free space */
+ ret = fdt_pack(blob_buf);
+ if (ret)
+ printf("%s: Unable to pack flat device tree\n", fdt_strerror(ret));
+
+ /* info->nr_segments just a guide, will grow by at least EXPAND_GRANULARITY */
+ blob_buf = expand_buf(info->nr_segments * sizeof(struct fdt_reserve_entry),
+ blob_buf, blob_size);
+
+ /* add reserve region for *THIS* fdt */
+ *dtb_addr = locate_hole(info, *blob_size, 0,
+ hole_addr, hole_addr+KERNEL_ACCESS_TOP, -1);
+ ret = fdt_add_mem_rsv(blob_buf, *dtb_addr, PAGE_ALIGN(*blob_size));
+ if (ret) {
+ printf("%s: Unable to add new reserved memory for the flat device tree\n",
+ fdt_strerror(ret));
+ }
+
+ return blob_buf;
+}
+
+static void save_fixed_up_dtb(char *blob_buf, off_t blob_size)
+{
+ FILE *fp;
+
+ if (!kexec_debug)
+ return;
+ fp = fopen("debug.dtb", "w");
+ if (fp) {
+ if ( blob_size == fwrite(blob_buf, sizeof(char), blob_size, fp)) {
+ dbgprintf("debug.dtb written\n");
+ } else {
+ dbgprintf("Unable to write debug.dtb\n");
+ }
+
+ fclose(fp);
+ } else {
+ dbgprintf("Unable to dump flat device tree to debug.dtb\n");
+ }
+}
+
+char *fixup_dtb_finalize(struct kexec_info *info, char *blob_buf, off_t *blob_size,
+ char *nodes[], char *cmdline)
+{
+ fixup_nodes(nodes);
+ fixup_cmdline(cmdline);
+ fixup_reserve_regions(info, blob_buf);
+ fixup_memory(info, blob_buf);
+ fixup_initrd(blob_buf);
+ fixup_crashkernel(info, blob_buf);
+
+ blob_buf = (char *)dt_ops.finalize();
+ *blob_size = fdt_totalsize(blob_buf);
+
+ save_fixed_up_dtb(blob_buf, *blob_size);
+
+ return blob_buf;
+}
diff --git a/kexec/arch/ppc/fixup_dtb.h b/kexec/arch/ppc/fixup_dtb.h
new file mode 100644
index 0000000..b706a5a
--- /dev/null
+++ b/kexec/arch/ppc/fixup_dtb.h
@@ -0,0 +1,10 @@
+#ifndef __FIXUP_DTB_H
+#define __FIXUP_DTB_H
+
+char *fixup_dtb_init(struct kexec_info *info, char *blob_buf, off_t *blob_size,
+ unsigned long hole_addr, unsigned long *dtb_addr);
+
+char *fixup_dtb_finalize(struct kexec_info *info, char *blob_buf, off_t *blob_size,
+ char *nodes[], char *cmdline);
+
+#endif
diff --git a/kexec/arch/ppc/fs2dt.c b/kexec/arch/ppc/fs2dt.c
new file mode 100644
index 0000000..fed499b
--- /dev/null
+++ b/kexec/arch/ppc/fs2dt.c
@@ -0,0 +1,471 @@
+/*
+ * fs2dt: creates a flattened device-tree
+ *
+ * Copyright (C) 2004,2005 Milton D Miller II, IBM Corporation
+ * Copyright (C) 2005 R Sharada (sharada@in.ibm.com), IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include "../../kexec.h"
+#include "kexec-ppc.h"
+#include "types.h"
+
+#define MAXPATH 1024 /* max path name length */
+#define NAMESPACE 16384 /* max bytes for property names */
+#define TREEWORDS 65536 /* max 32 bit words for properties */
+#define MEMRESERVE 256 /* max number of reserved memory blks */
+#define MAX_MEMORY_RANGES 1024
+
+static char pathname[MAXPATH];
+static char propnames[NAMESPACE] = { 0 };
+static unsigned dtstruct[TREEWORDS], *dt;
+static unsigned long long mem_rsrv[2*MEMRESERVE] = { 0, 0 };
+
+static int crash_param;
+static char local_cmdline[COMMAND_LINE_SIZE] = { "" };
+static unsigned *dt_len; /* changed len of modified cmdline
+ in flat device-tree */
+static struct bootblock bb[1];
+
+void reserve(unsigned long long where, unsigned long long length)
+{
+ size_t offset;
+
+ for (offset = 0; mem_rsrv[offset + 1]; offset += 2)
+ ;
+
+ if (offset + 4 >= 2 * MEMRESERVE)
+ die("unrecoverable error: exhasuted reservation meta data\n");
+
+ mem_rsrv[offset] = where;
+ mem_rsrv[offset + 1] = length;
+ mem_rsrv[offset + 3] = 0; /* N.B: don't care about offset + 2 */
+}
+
+/* look for properties we need to reserve memory space for */
+static void checkprop(char *name, unsigned *data, int len)
+{
+ static unsigned long long base, size, end;
+
+ if ((data == NULL) && (base || size || end))
+ die("unrecoverable error: no property data");
+ else if (!strcmp(name, "linux,rtas-base"))
+ base = *data;
+ else if (!strcmp(name, "linux,tce-base"))
+ base = *(unsigned long long *) data;
+ else if (!strcmp(name, "rtas-size") ||
+ !strcmp(name, "linux,tce-size"))
+ size = *data;
+ else if (reuse_initrd && !strcmp(name, "linux,initrd-start"))
+ if (len == 8)
+ base = *(unsigned long long *) data;
+ else
+ base = *data;
+ else if (reuse_initrd && !strcmp(name, "linux,initrd-end"))
+ end = *(unsigned long long *) data;
+
+ if (size && end)
+ die("unrecoverable error: size and end set at same time\n");
+ if (base && size) {
+ reserve(base, size);
+ base = 0;
+ size = 0;
+ }
+ if (base && end) {
+ reserve(base, end-base);
+ base = 0;
+ end = 0;
+ }
+}
+
+/*
+ * return the property index for a property name, creating a new one
+ * if needed.
+ */
+static unsigned propnum(const char *name)
+{
+ unsigned offset = 0;
+
+ while (propnames[offset])
+ if (strcmp(name, propnames+offset))
+ offset += strlen(propnames+offset)+1;
+ else
+ return offset;
+
+ if (NAMESPACE - offset < strlen(name) + 1)
+ die("unrecoverable error: propnames overrun\n");
+
+ strcpy(propnames+offset, name);
+
+ return offset;
+}
+
+static void add_usable_mem_property(int fd, int len)
+{
+ char fname[MAXPATH], *bname;
+ unsigned long buf[2];
+ unsigned long ranges[2*MAX_MEMORY_RANGES];
+ unsigned long long base, end, loc_base, loc_end;
+ int range, rlen = 0;
+
+ strcpy(fname, pathname);
+ bname = strrchr(fname, '/');
+ bname[0] = '\0';
+ bname = strrchr(fname, '/');
+ if (strncmp(bname, "/memory@", 8) && strcmp(bname, "/memory"))
+ return;
+
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ die("unrecoverable error: error seeking in \"%s\": %s\n",
+ pathname, strerror(errno));
+ if (read_memory_region_limits(fd, &base, &end) != 0)
+ die("unrecoverable error: error parsing memory/reg limits\n");
+
+ for (range = 0; range < usablemem_rgns.size; range++) {
+ loc_base = usablemem_rgns.ranges[range].start;
+ loc_end = usablemem_rgns.ranges[range].end;
+ if (loc_base >= base && loc_end <= end) {
+ ranges[rlen++] = loc_base;
+ ranges[rlen++] = loc_end - loc_base;
+ } else if (base < loc_end && end > loc_base) {
+ if (loc_base < base)
+ loc_base = base;
+ if (loc_end > end)
+ loc_end = end;
+ ranges[rlen++] = loc_base;
+ ranges[rlen++] = loc_end - loc_base;
+ }
+ }
+
+ if (!rlen) {
+ /*
+ * User did not pass any ranges for thsi region. Hence, write
+ * (0,0) duple in linux,usable-memory property such that
+ * this region will be ignored.
+ */
+ ranges[rlen++] = 0;
+ ranges[rlen++] = 0;
+ }
+
+ rlen = rlen * sizeof(unsigned long);
+ /*
+ * No add linux,usable-memory property.
+ */
+ *dt++ = 3;
+ *dt++ = rlen;
+ *dt++ = propnum("linux,usable-memory");
+ memcpy(dt, &ranges, rlen);
+ dt += (rlen + 3)/4;
+}
+
+/* put all properties (files) in the property structure */
+static void putprops(char *fn, struct dirent **nlist, int numlist)
+{
+ struct dirent *dp;
+ int i = 0, fd, len;
+ struct stat statbuf;
+
+ for (i = 0; i < numlist; i++) {
+ dp = nlist[i];
+ strcpy(fn, dp->d_name);
+
+ if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
+ continue;
+
+ if (lstat(pathname, &statbuf))
+ die("unrecoverable error: could not stat \"%s\": %s\n",
+ pathname, strerror(errno));
+
+ if (!crash_param && !strcmp(fn, "linux,crashkernel-base"))
+ continue;
+
+ if (!crash_param && !strcmp(fn, "linux,crashkernel-size"))
+ continue;
+
+ /*
+ * This property will be created for each node during kexec
+ * boot. So, ignore it.
+ */
+ if (!strcmp(dp->d_name, "linux,pci-domain") ||
+ !strcmp(dp->d_name, "linux,htab-base") ||
+ !strcmp(dp->d_name, "linux,htab-size") ||
+ !strcmp(dp->d_name, "linux,kernel-end") ||
+ !strcmp(dp->d_name, "linux,usable-memory"))
+ continue;
+
+ /* This property will be created/modified later in putnode()
+ * So ignore it, unless we are reusing the initrd.
+ */
+ if ((!strcmp(dp->d_name, "linux,initrd-start") ||
+ !strcmp(dp->d_name, "linux,initrd-end")) &&
+ !reuse_initrd)
+ continue;
+
+ if (!S_ISREG(statbuf.st_mode))
+ continue;
+
+ len = statbuf.st_size;
+
+ *dt++ = 3;
+ dt_len = dt;
+ *dt++ = len;
+ *dt++ = propnum(fn);
+
+ fd = open(pathname, O_RDONLY);
+ if (fd == -1)
+ die("unrecoverable error: could not open \"%s\": %s\n",
+ pathname, strerror(errno));
+
+ if (read(fd, dt, len) != len)
+ die("unrecoverable error: could not read \"%s\": %s\n",
+ pathname, strerror(errno));
+
+ checkprop(fn, dt, len);
+
+ /* Get the cmdline from the device-tree and modify it */
+ if (!strcmp(dp->d_name, "bootargs")) {
+ int cmd_len;
+ char temp_cmdline[COMMAND_LINE_SIZE] = { "" };
+ char *param = NULL;
+ cmd_len = strlen(local_cmdline);
+ if (cmd_len != 0) {
+ param = strstr(local_cmdline, "crashkernel=");
+ if (param)
+ crash_param = 1;
+ param = strstr(local_cmdline, "root=");
+ }
+ if (!param) {
+ char *old_param;
+ memcpy(temp_cmdline, dt, len);
+ param = strstr(temp_cmdline, "root=");
+ if (param) {
+ old_param = strtok(param, " ");
+ if (cmd_len != 0)
+ strcat(local_cmdline, " ");
+ strcat(local_cmdline, old_param);
+ }
+ }
+ strcat(local_cmdline, " ");
+ cmd_len = strlen(local_cmdline);
+ cmd_len = cmd_len + 1;
+ memcpy(dt, local_cmdline, cmd_len);
+ len = cmd_len;
+ *dt_len = cmd_len;
+
+ dbgprintf("Modified cmdline:%s\n", local_cmdline);
+
+ }
+
+ dt += (len + 3)/4;
+ if (!strcmp(dp->d_name, "reg") && usablemem_rgns.size)
+ add_usable_mem_property(fd, len);
+ close(fd);
+ }
+
+ fn[0] = '\0';
+ checkprop(pathname, NULL, 0);
+}
+
+/*
+ * Compare function used to sort the device-tree directories
+ * This function will be passed to scandir.
+ */
+static int comparefunc(const void *dentry1, const void *dentry2)
+{
+ char *str1 = (*(struct dirent **)dentry1)->d_name;
+ char *str2 = (*(struct dirent **)dentry2)->d_name;
+ char *p1, *p2;
+ int res = 0, max_len;
+
+ /*
+ * strcmp scans from left to right and fails to idetify for some
+ * strings such as memory@10000000 and memory@f000000.
+ * Therefore, we get the wrong sorted order like memory@10000000 and
+ * memory@f000000.
+ */
+ if ((p1 = strchr(str1, '@')) && (p2 = strchr(str2, '@'))) {
+ max_len = max(p1 - str1, p2 - str2);
+ if ((res = strncmp(str1, str2, max_len)) == 0) {
+ /* prefix is equal - compare part after '@' by length */
+ p1++; p2++;
+ res = strlen(p1) - strlen(p2);
+ if (res == 0)
+ /* equal length, compare by strcmp() */
+ res = strcmp(p1,p2);
+ }
+ } else {
+ res = strcmp(str1, str2);
+ }
+
+ return res;
+}
+
+/*
+ * put a node (directory) in the property structure. first properties
+ * then children.
+ */
+static void putnode(void)
+{
+ char *dn;
+ struct dirent *dp;
+ char *basename;
+ struct dirent **namelist;
+ int numlist, i;
+ struct stat statbuf;
+
+ numlist = scandir(pathname, &namelist, 0, comparefunc);
+ if (numlist < 0)
+ die("unrecoverable error: could not scan \"%s\": %s\n",
+ pathname, strerror(errno));
+ if (numlist == 0)
+ die("unrecoverable error: no directory entries in \"%s\"",
+ pathname);
+
+ basename = strrchr(pathname, '/') + 1;
+
+ *dt++ = 1;
+ strcpy((void *)dt, *basename ? basename : "");
+ dt += strlen((void *)dt) / sizeof(unsigned) + 1;
+
+ strcat(pathname, "/");
+ dn = pathname + strlen(pathname);
+
+ putprops(dn, namelist, numlist);
+
+ /*
+ * Add initrd entries to the second kernel
+ * if
+ * a) a ramdisk is specified in cmdline
+ * OR
+ * b) reuseinitrd is specified and a initrd is
+ * used by the kernel.
+ *
+ */
+ if ((ramdisk || (initrd_base && reuse_initrd))
+ && !strcmp(basename, "chosen/")) {
+ int len = 8;
+ unsigned long long initrd_end;
+ *dt++ = 3;
+ *dt++ = len;
+ *dt++ = propnum("linux,initrd-start");
+
+ memcpy(dt, &initrd_base, len);
+ dt += (len + 3)/4;
+
+ len = 8;
+ *dt++ = 3;
+ *dt++ = len;
+ *dt++ = propnum("linux,initrd-end");
+
+ initrd_end = initrd_base + initrd_size;
+
+ memcpy(dt, &initrd_end, len);
+ dt += (len + 3)/4;
+ /* reserve the existing initrd image in case of reuse_initrd */
+ if (initrd_base && initrd_size && reuse_initrd)
+ reserve(initrd_base, initrd_size);
+ }
+
+ for (i = 0; i < numlist; i++) {
+ dp = namelist[i];
+ strcpy(dn, dp->d_name);
+ free(namelist[i]);
+
+ if (!strcmp(dn, ".") || !strcmp(dn, ".."))
+ continue;
+
+ if (lstat(pathname, &statbuf))
+ die("unrecoverable error: could not stat \"%s\": %s\n",
+ pathname, strerror(errno));
+
+ if (S_ISDIR(statbuf.st_mode))
+ putnode();
+ }
+
+ *dt++ = 2;
+ dn[-1] = '\0';
+ free(namelist);
+}
+
+int create_flatten_tree(struct kexec_info *info, unsigned char **bufp,
+ unsigned long *sizep, char *cmdline)
+{
+ unsigned long len;
+ unsigned long tlen;
+ unsigned char *buf;
+ unsigned long me;
+
+ me = 0;
+
+ strcpy(pathname, "/proc/device-tree/");
+
+ dt = dtstruct;
+
+ if (cmdline)
+ strcpy(local_cmdline, cmdline);
+
+ putnode();
+ *dt++ = 9;
+
+ len = _ALIGN(sizeof(bb[0]), 8);
+
+ bb->off_mem_rsvmap = len;
+
+ for (len = 1; mem_rsrv[len]; len += 2)
+ ;
+ len += 3;
+ len *= sizeof(mem_rsrv[0]);
+
+ bb->off_dt_struct = bb->off_mem_rsvmap + len;
+
+ len = dt - dtstruct;
+ len *= sizeof(unsigned);
+ bb->dt_struct_size = len;
+ bb->off_dt_strings = bb->off_dt_struct + len;
+
+ len = propnum("");
+ bb->dt_strings_size = len;
+ len = _ALIGN(len, 4);
+ bb->totalsize = bb->off_dt_strings + len;
+
+ bb->magic = 0xd00dfeed;
+ bb->version = 17;
+ bb->last_comp_version = 16;
+
+ reserve(me, bb->totalsize); /* patched later in kexec_load */
+
+ buf = (unsigned char *) malloc(bb->totalsize);
+ *bufp = buf;
+ memcpy(buf, bb, bb->off_mem_rsvmap);
+ tlen = bb->off_mem_rsvmap;
+ memcpy(buf+tlen, mem_rsrv, bb->off_dt_struct - bb->off_mem_rsvmap);
+ tlen = tlen + (bb->off_dt_struct - bb->off_mem_rsvmap);
+ memcpy(buf+tlen, dtstruct, bb->off_dt_strings - bb->off_dt_struct);
+ tlen = tlen + (bb->off_dt_strings - bb->off_dt_struct);
+ memcpy(buf+tlen, propnames, bb->totalsize - bb->off_dt_strings);
+ tlen = tlen + bb->totalsize - bb->off_dt_strings;
+ *sizep = tlen;
+ return 0;
+}
diff --git a/kexec/arch/ppc/include/arch/options.h b/kexec/arch/ppc/include/arch/options.h
new file mode 100644
index 0000000..b2176ab
--- /dev/null
+++ b/kexec/arch/ppc/include/arch/options.h
@@ -0,0 +1,47 @@
+#ifndef KEXEC_ARCH_PPC_OPTIONS_H
+#define KEXEC_ARCH_PPC_OPTIONS_H
+
+#define OPT_ARCH_MAX (OPT_MAX+0)
+
+/* All 'local' loader options: */
+#define OPT_APPEND (OPT_ARCH_MAX+0)
+#define OPT_GAMECUBE (OPT_ARCH_MAX+1)
+#define OPT_DTB (OPT_ARCH_MAX+2)
+#define OPT_NODES (OPT_ARCH_MAX+3)
+#define OPT_RAMDISK (OPT_ARCH_MAX+4)
+
+/* Options relevant to the architecture (excluding loader-specific ones),
+ * in this case none:
+ */
+#define KEXEC_ARCH_OPTIONS \
+ KEXEC_OPTIONS \
+
+#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
+
+/* The following two #defines list ALL of the options added by all of the
+ * architecture's loaders.
+ * o main() uses this complete list to scan for its options, ignoring
+ * arch-specific/loader-specific ones.
+ * o Then, arch_process_options() uses this complete list to scan for its
+ * options, ignoring general/loader-specific ones.
+ * o Then, the file_type[n].load re-scans for options, using
+ * KEXEC_ARCH_OPTIONS plus its loader-specific options subset.
+ * Any unrecognised options cause an error here.
+ *
+ * This is done so that main()'s/arch_process_options()'s getopt_long() calls
+ * don't choose a kernel filename from random arguments to options they don't
+ * recognise -- as they now recognise (if not act upon) all possible options.
+ */
+#define KEXEC_ALL_OPTIONS \
+ KEXEC_ARCH_OPTIONS \
+ {"command-line", 1, 0, OPT_APPEND},\
+ {"append", 1, 0, OPT_APPEND},\
+ {"ramdisk", 1, 0, OPT_APPEND},\
+ {"initrd", 1, 0, OPT_APPEND},\
+ {"gamecube", 1, 0, OPT_GAMECUBE},\
+ {"dtb", 1, 0, OPT_DTB},\
+ {"reuse-node", 1, 0, OPT_NODES},
+
+#define KEXEC_ALL_OPT_STR KEXEC_OPT_STR
+
+#endif /* KEXEC_ARCH_PPC_OPTIONS_H */
diff --git a/kexec/arch/ppc/include/page.h b/kexec/arch/ppc/include/page.h
new file mode 100644
index 0000000..65877bc
--- /dev/null
+++ b/kexec/arch/ppc/include/page.h
@@ -0,0 +1,27 @@
+#ifndef _PPC_BOOT_PAGE_H
+#define _PPC_BOOT_PAGE_H
+/*
+ * Copyright (C) 2001 PPC64 Team, IBM Corp
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifdef __ASSEMBLY__
+#define ASM_CONST(x) x
+#else
+#define __ASM_CONST(x) x##UL
+#define ASM_CONST(x) __ASM_CONST(x)
+#endif
+
+/* PAGE_SHIFT determines the page size */
+#define PAGE_SHIFT 12
+#define PAGE_SIZE (ASM_CONST(1) << PAGE_SHIFT)
+#define PAGE_MASK (~(PAGE_SIZE-1))
+
+/* to align the pointer to the (next) page boundary */
+#define PAGE_ALIGN(addr) (((addr)+((PAGE_SIZE)-1))&(~((PAGE_SIZE)-1)))
+
+#endif /* _PPC_BOOT_PAGE_H */
diff --git a/kexec/arch/ppc/include/types.h b/kexec/arch/ppc/include/types.h
new file mode 100644
index 0000000..31393d1
--- /dev/null
+++ b/kexec/arch/ppc/include/types.h
@@ -0,0 +1,27 @@
+#ifndef _TYPES_H_
+#define _TYPES_H_
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+typedef unsigned long long u64;
+typedef signed char s8;
+typedef short s16;
+typedef int s32;
+typedef long long s64;
+
+#define min(x,y) ({ \
+ typeof(x) _x = (x); \
+ typeof(y) _y = (y); \
+ (void) (&_x == &_y); \
+ _x < _y ? _x : _y; })
+
+#define max(x,y) ({ \
+ typeof(x) _x = (x); \
+ typeof(y) _y = (y); \
+ (void) (&_x == &_y); \
+ _x > _y ? _x : _y; })
+
+#endif /* _TYPES_H_ */
diff --git a/kexec/arch/ppc/kexec-dol-ppc.c b/kexec/arch/ppc/kexec-dol-ppc.c
new file mode 100644
index 0000000..800c072
--- /dev/null
+++ b/kexec/arch/ppc/kexec-dol-ppc.c
@@ -0,0 +1,468 @@
+/*
+ * kexec-dol-ppc.c - kexec DOL executable loader for the PowerPC
+ * Copyright (C) 2004 Albert Herranz
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <elf.h>
+#include <boot/elf_boot.h>
+#include <ip_checksum.h>
+#include "../../kexec.h"
+#include "kexec-ppc.h"
+#include <arch/options.h>
+
+static int debug = 0;
+
+/*
+ * I've found out there DOLs with unaligned and/or overlapping sections.
+ * I assume that sizes of sections can be wrong on these DOLs so I trust
+ * better start of sections.
+ * In order to load DOLs, I first extend sections to page aligned boundaries
+ * and then merge overlapping sections starting from lower addresses.
+ * -- Albert Herranz
+ */
+
+/* DOL related stuff */
+
+#define DOL_HEADER_SIZE 0x100
+
+#define DOL_SECT_MAX_TEXT 7 /* text sections */
+#define DOL_SECT_MAX_DATA 11 /* data sections */
+#define DOL_MAX_SECT (DOL_SECT_MAX_TEXT+DOL_SECT_MAX_DATA)
+
+/* this is the DOL executable header */
+typedef struct {
+ uint32_t offset_text[DOL_SECT_MAX_TEXT]; /* in the file */
+ uint32_t offset_data[DOL_SECT_MAX_DATA];
+ uint32_t address_text[DOL_SECT_MAX_TEXT]; /* in memory */
+ uint32_t address_data[DOL_SECT_MAX_DATA];
+ uint32_t size_text[DOL_SECT_MAX_TEXT];
+ uint32_t size_data[DOL_SECT_MAX_DATA];
+ uint32_t address_bss;
+ uint32_t size_bss;
+ uint32_t entry_point;
+} dol_header;
+
+#define dol_sect_offset(hptr, index) \
+ ((index >= DOL_SECT_MAX_TEXT)? \
+ hptr->offset_data[index - DOL_SECT_MAX_TEXT] \
+ :hptr->offset_text[index])
+#define dol_sect_address(hptr, index) \
+ ((index >= DOL_SECT_MAX_TEXT)? \
+ hptr->address_data[index - DOL_SECT_MAX_TEXT] \
+ :hptr->address_text[index])
+#define dol_sect_size(hptr, index) \
+ ((index >= DOL_SECT_MAX_TEXT)? \
+ hptr->size_data[index - DOL_SECT_MAX_TEXT] \
+ :hptr->size_text[index])
+#define dol_sect_type(index) \
+ ((index >= DOL_SECT_MAX_TEXT) ? "data" : "text")
+
+typedef struct {
+ uint32_t sects_bitmap;
+ uint32_t start;
+ uint32_t size;
+} dol_segment;
+
+#define dol_seg_end(s1) \
+ (s1->start + s1->size)
+#define dol_seg_after_sect(s1, s2) \
+ (s1->start >= dol_seg_end(s2))
+#define dol_seg_overlaps(s1, s2) \
+ (!(dol_seg_after_sect(s1,s2) || dol_seg_after_sect(s2,s1)))
+
+/* same as in asm/page.h */
+#define PAGE_SHIFT 12
+#define PAGE_SIZE (1UL << PAGE_SHIFT)
+#define PAGE_MASK (~((1 << PAGE_SHIFT) - 1))
+#define PAGE_ALIGN(addr) _ALIGN(addr, PAGE_SIZE)
+
+#define MAX_COMMAND_LINE 256
+
+#define UPSZ(X) _ALIGN_UP(sizeof(X), 4)
+static struct boot_notes {
+ Elf_Bhdr hdr;
+ Elf_Nhdr bl_hdr;
+ unsigned char bl_desc[UPSZ(BOOTLOADER)];
+ Elf_Nhdr blv_hdr;
+ unsigned char blv_desc[UPSZ(BOOTLOADER_VERSION)];
+ Elf_Nhdr cmd_hdr;
+ unsigned char command_line[0];
+} elf_boot_notes = {
+ .hdr = {
+ .b_signature = 0x0E1FB007,
+ .b_size = sizeof(elf_boot_notes),
+ .b_checksum = 0,
+ .b_records = 3,
+ },
+ .bl_hdr = {
+ .n_namesz = 0,
+ .n_descsz = sizeof(BOOTLOADER),
+ .n_type = EBN_BOOTLOADER_NAME,
+ },
+ .bl_desc = BOOTLOADER,
+ .blv_hdr = {
+ .n_namesz = 0,
+ .n_descsz = sizeof(BOOTLOADER_VERSION),
+ .n_type = EBN_BOOTLOADER_VERSION,
+ },
+ .blv_desc = BOOTLOADER_VERSION,
+ .cmd_hdr = {
+ .n_namesz = 0,
+ .n_descsz = 0,
+ .n_type = EBN_COMMAND_LINE,
+ },
+};
+
+void print_sects_bitmap(dol_segment * seg)
+{
+ int i, first_seen;
+
+ printf("\t" "sects_bitmap");
+ first_seen = 0;
+ for (i = 0; i < DOL_MAX_SECT; i++) {
+ if ((seg->sects_bitmap & (1 << i)) == 0)
+ continue;
+ printf("%c%d", (first_seen ? ',' : '='), i);
+ first_seen = 1;
+ }
+ printf("\n");
+}
+
+void print_dol_segment(dol_segment * seg)
+{
+ printf("dol segment:\n");
+ printf("\t" "start=%08lx, size=%ld (%08lx)\n",
+ (unsigned long)seg->start, (unsigned long)seg->size,
+ (unsigned long)seg->size);
+ printf("\t" "end=%08lx\n", (unsigned long)dol_seg_end(seg));
+ print_sects_bitmap(seg);
+}
+
+int load_dol_segments(dol_segment * seg, int max_segs, dol_header * h)
+{
+ int i, n, remaining;
+ unsigned int start, size;
+ unsigned long adj1, adj2, end1;
+
+ n = 0;
+ remaining = max_segs;
+ for (i = 0; i < DOL_MAX_SECT && remaining > 0; i++) {
+ /* zero here means the section is not in use */
+ if (dol_sect_size(h, i) == 0)
+ continue;
+
+ /* we initially map 1 seg to 1 sect */
+ seg->sects_bitmap = (1 << i);
+
+ start = dol_sect_address(h, i);
+ size = dol_sect_size(h, i);
+
+ /* page align the segment */
+ seg->start = start & PAGE_MASK;
+ end1 = start + size;
+ adj1 = start - seg->start;
+ adj2 = PAGE_ALIGN(end1) - end1;
+ seg->size = adj1 + size + adj2;
+
+ //print_dol_segment(seg);
+
+ seg++;
+ remaining--;
+ n++;
+ }
+ return n;
+}
+
+void fix_dol_segments_overlaps(dol_segment * seg, int max_segs)
+{
+ int i, j;
+ dol_segment *p, *pp;
+ long extra_length;
+
+ /* look for overlapping segments and fix them */
+ for (i = 0; i < max_segs; i++) {
+ p = seg + i; /* segment p */
+
+ /* not really a segment */
+ if (p->size == 0)
+ continue;
+
+ /* check if overlaps any previous segments */
+ for (j = 0; j < i; j++) {
+ pp = seg + j; /* segment pp */
+
+ /* not a segment or no overlap */
+ if (pp->size == 0 || !dol_seg_overlaps(p, pp))
+ continue;
+
+ /* merge the two segments */
+ if (pp->start < p->start) {
+ /* extend pp to include p and delete p */
+ extra_length = dol_seg_end(p) - dol_seg_end(pp);
+ if (extra_length > 0) {
+ pp->size += extra_length;
+ }
+ pp->sects_bitmap |= p->sects_bitmap;
+ p->size = p->start = p->sects_bitmap = 0;
+
+ /* restart the loop because p was deleted */
+ i = 0;
+ break;
+ } else {
+ /* extend p to include pp and delete pp */
+ extra_length = dol_seg_end(pp) - dol_seg_end(p);
+ if (extra_length > 0) {
+ p->size += extra_length;
+ }
+ p->sects_bitmap |= pp->sects_bitmap;
+ pp->size = pp->start = pp->sects_bitmap = 0;
+ }
+ }
+ }
+}
+
+int dol_ppc_probe(const char *buf, off_t dol_length)
+{
+ dol_header header, *h;
+ int i, valid = 0;
+
+ /* the DOL file should be at least as long as the DOL header */
+ if (dol_length < DOL_HEADER_SIZE) {
+ if (debug) {
+ fprintf(stderr, "Not a DOL file, too short.\n");
+ }
+ return -1;
+ }
+
+ /* read the DOL header */
+ memcpy(&header, buf, sizeof(header));
+ h = &header;
+
+ /* now perform some sanity checks */
+ for (i = 0; i < DOL_MAX_SECT; i++) {
+ /* DOL segment MAY NOT be physically stored in the header */
+ if ((dol_sect_offset(h, i) != 0)
+ && (dol_sect_offset(h, i) < DOL_HEADER_SIZE)) {
+ if (debug) {
+ fprintf(stderr,
+ "%s segment offset within DOL header\n",
+ dol_sect_type(i));
+ }
+ return -1;
+ }
+
+ /* end of physical storage must be within file */
+ if ((uintmax_t)(dol_sect_offset(h, i) + dol_sect_size(h, i)) >
+ (uintmax_t)dol_length) {
+ if (debug) {
+ fprintf(stderr,
+ "%s segment past DOL file size\n",
+ dol_sect_type(i));
+ }
+ return -1;
+ }
+
+ /* we only should accept DOLs with segments above 2GB */
+ if (dol_sect_address(h, i) != 0
+ && !(dol_sect_address(h, i) & 0x80000000)) {
+ fprintf(stderr, "warning, %s segment below 2GB\n",
+ dol_sect_type(i));
+ }
+
+ if (i < DOL_SECT_MAX_TEXT) {
+ /* remember that entrypoint was in a code segment */
+ if (h->entry_point >= dol_sect_address(h, i)
+ && h->entry_point < dol_sect_address(h, i) +
+ dol_sect_size(h, i))
+ valid = 1;
+ }
+ }
+
+ /* if there is a BSS segment it must^H^H^H^Hshould be above 2GB, too */
+ if (h->address_bss != 0 && !(h->address_bss & 0x80000000)) {
+ fprintf(stderr, "warning, BSS segment below 2GB\n");
+ }
+
+ /* if entrypoint is not within a code segment reject this file */
+ if (!valid) {
+ if (debug) {
+ fprintf(stderr, "Entry point out of text segment\n");
+ }
+ return -1;
+ }
+
+ /* I've got a dol */
+ return 0;
+}
+
+void dol_ppc_usage(void)
+{
+ printf
+ (" --command-line=STRING Set the kernel command line to STRING.\n"
+ " --append=STRING Set the kernel command line to STRING.\n");
+
+}
+
+int dol_ppc_load(int argc, char **argv, const char *buf, off_t UNUSED(len),
+ struct kexec_info *info)
+{
+ dol_header header, *h;
+ unsigned long entry;
+ char *arg_buf;
+ size_t arg_bytes;
+ unsigned long arg_base;
+ struct boot_notes *notes;
+ size_t note_bytes;
+ const char *command_line;
+ int command_line_len;
+ unsigned long mstart;
+ dol_segment dol_segs[DOL_MAX_SECT];
+ unsigned int sects_bitmap;
+ unsigned long lowest_start;
+ int i, j, k;
+ int opt;
+
+ /* See options.h -- add any more there, too. */
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ {"command-line", 1, 0, OPT_APPEND},
+ {"append", 1, 0, OPT_APPEND},
+ {0, 0, 0, 0},
+ };
+ static const char short_options[] = KEXEC_ARCH_OPT_STR;
+
+ /*
+ * Parse the command line arguments
+ */
+ command_line = 0;
+ while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch (opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ case OPT_APPEND:
+ command_line = optarg;
+ break;
+ }
+ }
+ command_line_len = 0;
+ if (command_line) {
+ command_line_len = strlen(command_line) + 1;
+ }
+
+ /* read the DOL header */
+ memcpy(&header, buf, sizeof(header));
+ h = &header;
+
+ /* set entry point */
+ entry = h->entry_point;
+
+ /* convert the DOL sections into suitable page aligned segments */
+ memset(dol_segs, 0, sizeof(dol_segs));
+
+ load_dol_segments(dol_segs, DOL_MAX_SECT, h);
+ fix_dol_segments_overlaps(dol_segs, DOL_MAX_SECT);
+
+ /* load rest of segments */
+ for (i = 0; i < DOL_MAX_SECT; i++) {
+ unsigned char *seg_buf;
+ /* not a segment */
+ if (dol_segs[i].size == 0)
+ continue;
+
+ //print_dol_segment(&dol_segs[i]);
+
+ /* prepare segment */
+ seg_buf = xmalloc(dol_segs[i].size);
+ mstart = dol_segs[i].start;
+ if (mstart & 0xf0000000) {
+ /*
+ * GameCube DOLs expect memory mapped this way:
+ *
+ * 80000000 - 817fffff 24MB RAM, cached
+ * c0000000 - c17fffff 24MB RAM, not cached
+ *
+ * kexec, instead, needs physical memory layout, so
+ * we clear the upper bits of the address.
+ * (2 bits should be enough, indeed)
+ */
+ mstart &= ~0xf0000000; /* clear bits 0-3, ibm syntax */
+ }
+ add_segment(info,
+ seg_buf, dol_segs[i].size,
+ mstart, dol_segs[i].size);
+
+
+ /* load sections into segment memory, according to bitmap */
+ sects_bitmap = 0;
+ while (sects_bitmap != dol_segs[i].sects_bitmap) {
+ unsigned char *sec_buf;
+ /* find lowest start address for section */
+ lowest_start = 0xffffffff;
+ for (j = -1, k = 0; k < DOL_MAX_SECT; k++) {
+ /* continue if section is already done */
+ if ((sects_bitmap & (1 << k)) != 0)
+ continue;
+ /* do nothing for non sections */
+ if ((dol_segs[i].sects_bitmap & (1 << k)) == 0)
+ continue;
+ /* found new candidate */
+ if (dol_sect_address(h, k) < lowest_start) {
+ lowest_start = dol_sect_address(h, k);
+ j = k;
+ }
+ }
+ /* mark section as being loaded */
+ sects_bitmap |= (1 << j);
+
+ /* read it from file to the right place */
+ sec_buf = seg_buf +
+ (dol_sect_address(h, j) - dol_segs[i].start);
+ memcpy(sec_buf, buf + dol_sect_offset(h, j),
+ dol_sect_size(h, j));
+ }
+ }
+
+ /* build the setup glue and argument segment (segment 0) */
+ note_bytes = sizeof(elf_boot_notes) + _ALIGN(command_line_len, 4);
+ arg_bytes = note_bytes + _ALIGN(setup_dol_size, 4);
+
+ arg_buf = xmalloc(arg_bytes);
+ arg_base = add_buffer(info,
+ arg_buf, arg_bytes, arg_bytes, 4, 0, 0xFFFFFFFFUL, 1);
+
+ notes = (struct boot_notes *)(arg_buf + _ALIGN(setup_dol_size, 4));
+
+ notes->hdr.b_size = note_bytes;
+ notes->cmd_hdr.n_descsz = command_line_len;
+ notes->hdr.b_checksum = compute_ip_checksum(notes, note_bytes);
+
+ setup_dol_regs.spr8 = entry; /* Link Register */
+
+ memcpy(arg_buf, setup_dol_start, setup_dol_size);
+ memcpy(notes, &elf_boot_notes, sizeof(elf_boot_notes));
+ memcpy(notes->command_line, command_line, command_line_len);
+
+ if (debug) {
+ fprintf(stdout, "entry = %p\n", (void *)arg_base);
+ print_segments(stdout, info);
+ }
+
+ info->entry = (void *)arg_base;
+ return 0;
+}
diff --git a/kexec/arch/ppc/kexec-elf-ppc.c b/kexec/arch/ppc/kexec-elf-ppc.c
new file mode 100644
index 0000000..4a4886e
--- /dev/null
+++ b/kexec/arch/ppc/kexec-elf-ppc.c
@@ -0,0 +1,458 @@
+/*
+ * kexec-elf-ppc.c - kexec Elf loader for the PowerPC
+ * Copyright (C) 2004 Albert Herranz
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <elf.h>
+#include <boot/elf_boot.h>
+#include <ip_checksum.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "kexec-ppc.h"
+#include <arch/options.h>
+#include "../../kexec-syscall.h"
+#include "crashdump-powerpc.h"
+
+#include "config.h"
+#include "fixup_dtb.h"
+
+static const int probe_debug = 0;
+
+unsigned char reuse_initrd;
+int create_flatten_tree(struct kexec_info *, unsigned char **, unsigned long *,
+ char *);
+
+#define UPSZ(X) _ALIGN_UP(sizeof(X), 4);
+#ifdef WITH_GAMECUBE
+static struct boot_notes {
+ Elf_Bhdr hdr;
+ Elf_Nhdr bl_hdr;
+ unsigned char bl_desc[UPSZ(BOOTLOADER)];
+ Elf_Nhdr blv_hdr;
+ unsigned char blv_desc[UPSZ(BOOTLOADER_VERSION)];
+ Elf_Nhdr cmd_hdr;
+ unsigned char command_line[0];
+} elf_boot_notes = {
+ .hdr = {
+ .b_signature = 0x0E1FB007,
+ .b_size = sizeof(elf_boot_notes),
+ .b_checksum = 0,
+ .b_records = 3,
+ },
+ .bl_hdr = {
+ .n_namesz = 0,
+ .n_descsz = sizeof(BOOTLOADER),
+ .n_type = EBN_BOOTLOADER_NAME,
+ },
+ .bl_desc = BOOTLOADER,
+ .blv_hdr = {
+ .n_namesz = 0,
+ .n_descsz = sizeof(BOOTLOADER_VERSION),
+ .n_type = EBN_BOOTLOADER_VERSION,
+ },
+ .blv_desc = BOOTLOADER_VERSION,
+ .cmd_hdr = {
+ .n_namesz = 0,
+ .n_descsz = 0,
+ .n_type = EBN_COMMAND_LINE,
+ },
+};
+#endif
+
+int elf_ppc_probe(const char *buf, off_t len)
+{
+
+ struct mem_ehdr ehdr;
+ int result;
+ result = build_elf_exec_info(buf, len, &ehdr, 0);
+ if (result < 0) {
+ goto out;
+ }
+
+ /* Verify the architecuture specific bits */
+ if (ehdr.e_machine != EM_PPC) {
+ /* for a different architecture */
+ if (probe_debug) {
+ fprintf(stderr, "Not for this architecture.\n");
+ }
+ result = -1;
+ goto out;
+ }
+ result = 0;
+ out:
+ free_elf_info(&ehdr);
+ return result;
+}
+
+#ifdef WITH_GAMECUBE
+static void gamecube_hack_addresses(struct mem_ehdr *ehdr)
+{
+ struct mem_phdr *phdr, *phdr_end;
+ phdr_end = ehdr->e_phdr + ehdr->e_phnum;
+ for(phdr = ehdr->e_phdr; phdr != phdr_end; phdr++) {
+ /*
+ * GameCube ELF kernel is linked with memory mapped
+ * this way (to easily transform it into a DOL
+ * suitable for being loaded with psoload):
+ *
+ * 80000000 - 817fffff 24MB RAM, cached
+ * c0000000 - c17fffff 24MB RAM, not cached
+ *
+ * kexec, instead, needs physical memory layout, so
+ * we clear the upper bits of the address.
+ * (2 bits should be enough, indeed)
+ */
+ phdr->p_paddr &= ~0xf0000000; /* clear bits 0-3, ibm syntax */
+ }
+}
+#endif
+
+/* See options.h -- add any more there, too. */
+static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ {"command-line", 1, 0, OPT_APPEND},
+ {"append", 1, 0, OPT_APPEND},
+ {"ramdisk", 1, 0, OPT_RAMDISK},
+ {"initrd", 1, 0, OPT_RAMDISK},
+ {"gamecube", 1, 0, OPT_GAMECUBE},
+ {"dtb", 1, 0, OPT_DTB},
+ {"reuse-node", 1, 0, OPT_NODES},
+ {0, 0, 0, 0},
+};
+static const char short_options[] = KEXEC_ARCH_OPT_STR;
+
+void elf_ppc_usage(void)
+{
+ printf(
+ " --command-line=STRING Set the kernel command line to STRING.\n"
+ " --append=STRING Set the kernel command line to STRING.\n"
+ " --ramdisk=<filename> Initial RAM disk.\n"
+ " --initrd=<filename> same as --ramdisk\n"
+ " --gamecube=1|0 Enable/disable support for ELFs with changed\n"
+ " addresses suitable for the GameCube.\n"
+ " --dtb=<filename> Specify device tree blob file.\n"
+ " --reuse-node=node Specify nodes which should be taken from /proc/device-tree.\n"
+ " Can be set multiple times.\n"
+ );
+}
+
+int elf_ppc_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ struct mem_ehdr ehdr;
+ char *command_line, *crash_cmdline, *cmdline_buf;
+ char *tmp_cmdline;
+ int command_line_len, crash_cmdline_len;
+ char *dtb;
+ int result;
+ char *error_msg;
+ unsigned long max_addr, hole_addr;
+ struct mem_phdr *phdr;
+ size_t size;
+#ifdef CONFIG_PPC64
+ unsigned long toc_addr;
+#endif
+#ifdef WITH_GAMECUBE
+ int target_is_gamecube = 1;
+ char *arg_buf;
+ size_t arg_bytes;
+ unsigned long arg_base;
+ struct boot_notes *notes;
+ size_t note_bytes;
+ unsigned char *setup_start;
+ uint32_t setup_size;
+#else
+ char *seg_buf = NULL;
+ off_t seg_size = 0;
+ int target_is_gamecube = 0;
+ unsigned int addr;
+ unsigned long dtb_addr;
+ unsigned long dtb_addr_actual;
+#endif
+ unsigned long kernel_addr;
+#define FIXUP_ENTRYS (20)
+ char *fixup_nodes[FIXUP_ENTRYS + 1];
+ int cur_fixup = 0;
+ int opt;
+ char *blob_buf = NULL;
+ off_t blob_size = 0;
+
+ command_line = tmp_cmdline = NULL;
+ dtb = NULL;
+ max_addr = LONG_MAX;
+ hole_addr = 0;
+ kernel_addr = 0;
+ ramdisk = 0;
+ result = 0;
+ error_msg = NULL;
+
+ while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch (opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ case OPT_APPEND:
+ tmp_cmdline = optarg;
+ break;
+ case OPT_RAMDISK:
+ ramdisk = optarg;
+ break;
+ case OPT_GAMECUBE:
+ target_is_gamecube = atoi(optarg);
+ break;
+
+ case OPT_DTB:
+ dtb = optarg;
+ break;
+
+ case OPT_NODES:
+ if (cur_fixup >= FIXUP_ENTRYS) {
+ die("The number of entries for the fixup is too large\n");
+ }
+ fixup_nodes[cur_fixup] = optarg;
+ cur_fixup++;
+ break;
+ }
+ }
+
+ if (ramdisk && reuse_initrd)
+ die("Can't specify --ramdisk or --initrd with --reuseinitrd\n");
+
+ command_line_len = 0;
+ if (tmp_cmdline) {
+ command_line = tmp_cmdline;
+ } else {
+ command_line = get_command_line();
+ }
+ command_line_len = strlen(command_line);
+
+ fixup_nodes[cur_fixup] = NULL;
+
+ /* Parse the Elf file */
+ result = build_elf_exec_info(buf, len, &ehdr, 0);
+ if (result < 0) {
+ goto out;
+ }
+
+#ifdef WITH_GAMECUBE
+ if (target_is_gamecube) {
+ gamecube_hack_addresses(&ehdr);
+ }
+#endif
+
+ /* Load the Elf data. Physical load addresses in elf64 header do not
+ * show up correctly. Use user supplied address for now to patch the
+ * elf header
+ */
+
+ phdr = &ehdr.e_phdr[0];
+ size = phdr->p_filesz;
+ if (size > phdr->p_memsz)
+ size = phdr->p_memsz;
+
+ kernel_addr = locate_hole(info, size, 0, 0, max_addr, 1);
+#ifdef CONFIG_PPC64
+ ehdr.e_phdr[0].p_paddr = (Elf64_Addr)kernel_addr;
+#else
+ ehdr.e_phdr[0].p_paddr = kernel_addr;
+#endif
+
+ /* Load the Elf data */
+ result = elf_exec_load(&ehdr, info);
+ if (result < 0) {
+ goto out;
+ }
+
+ /*
+ * Need to append some command line parameters internally in case of
+ * taking crash dumps. Additional segments need to be created.
+ */
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ crash_cmdline = xmalloc(COMMAND_LINE_SIZE);
+ memset((void *)crash_cmdline, 0, COMMAND_LINE_SIZE);
+ result = load_crashdump_segments(info, crash_cmdline,
+ max_addr, 0);
+ if (result < 0) {
+ result = -1;
+ goto out;
+ }
+ crash_cmdline_len = strlen(crash_cmdline);
+ } else {
+ crash_cmdline = NULL;
+ crash_cmdline_len = 0;
+ }
+
+ /*
+ * In case of a toy we take the hardcoded things and an easy setup via
+ * one of the assembly startups. Every thing else should be grown up
+ * and go through the purgatory.
+ */
+#ifdef WITH_GAMECUBE
+ if (target_is_gamecube) {
+ setup_start = setup_dol_start;
+ setup_size = setup_dol_size;
+ setup_dol_regs.spr8 = ehdr.e_entry; /* Link Register */
+ } else {
+ setup_start = setup_simple_start;
+ setup_size = setup_simple_size;
+ setup_simple_regs.spr8 = ehdr.e_entry; /* Link Register */
+ }
+ note_bytes = sizeof(elf_boot_notes) + _ALIGN(command_line_len, 4);
+ arg_bytes = note_bytes + _ALIGN(setup_size, 4);
+
+ arg_buf = xmalloc(arg_bytes);
+ arg_base = add_buffer(info,
+ arg_buf, arg_bytes, arg_bytes, 4, 0, elf_max_addr(&ehdr), 1);
+
+ notes = (struct boot_notes *)(arg_buf + _ALIGN(setup_size, 4));
+
+ memcpy(arg_buf, setup_start, setup_size);
+ memcpy(notes, &elf_boot_notes, sizeof(elf_boot_notes));
+ memcpy(notes->command_line, command_line, command_line_len);
+ notes->hdr.b_size = note_bytes;
+ notes->cmd_hdr.n_descsz = command_line_len;
+ notes->hdr.b_checksum = compute_ip_checksum(notes, note_bytes);
+
+ info->entry = (void *)arg_base;
+#else
+ if (crash_cmdline_len + command_line_len + 1 > COMMAND_LINE_SIZE) {
+ printf("Kernel command line exceeds size\n");
+ return -1;
+ }
+
+ cmdline_buf = xmalloc(COMMAND_LINE_SIZE);
+ memset((void *)cmdline_buf, 0, COMMAND_LINE_SIZE);
+ if (command_line)
+ strncat(cmdline_buf, command_line, command_line_len);
+ if (crash_cmdline)
+ strncat(cmdline_buf, crash_cmdline,
+ sizeof(crash_cmdline) -
+ strlen(crash_cmdline) - 1);
+
+ elf_rel_build_load(info, &info->rhdr, (const char *)purgatory,
+ purgatory_size, 0, elf_max_addr(&ehdr), 1, 0);
+
+ /* Here we need to initialize the device tree, and find out where
+ * it is going to live so we can place it directly after the
+ * kernel image */
+ if (dtb) {
+ /* Grab device tree from buffer */
+ blob_buf = slurp_file(dtb, &blob_size);
+ } else {
+ create_flatten_tree(info, (unsigned char **)&blob_buf,
+ (unsigned long *)&blob_size, cmdline_buf);
+ }
+ if (!blob_buf || !blob_size) {
+ error_msg = "Device tree seems to be an empty file.\n";
+ goto out2;
+ }
+
+ /* initial fixup for device tree */
+ blob_buf = fixup_dtb_init(info, blob_buf, &blob_size, kernel_addr, &dtb_addr);
+
+ if (ramdisk) {
+ seg_buf = slurp_ramdisk_ppc(ramdisk, &seg_size);
+ /* load the ramdisk *above* the device tree */
+ hole_addr = add_buffer(info, seg_buf, seg_size, seg_size,
+ 0, dtb_addr + blob_size + 1, max_addr, -1);
+ ramdisk_base = hole_addr;
+ ramdisk_size = seg_size;
+ }
+ if (reuse_initrd) {
+ ramdisk_base = initrd_base;
+ ramdisk_size = initrd_size;
+ }
+
+ if (info->kexec_flags & KEXEC_ON_CRASH && ramdisk_base != 0) {
+ if ( (ramdisk_base < crash_base) ||
+ (ramdisk_base > crash_base + crash_size) ) {
+ printf("WARNING: ramdisk is above crashkernel region!\n");
+ }
+ else if (ramdisk_base + ramdisk_size > crash_base + crash_size) {
+ printf("WARNING: ramdisk overflows crashkernel region!\n");
+ }
+ }
+
+ /* Perform final fixup on devie tree, i.e. everything beside what
+ * was done above */
+ fixup_dtb_finalize(info, blob_buf, &blob_size, fixup_nodes,
+ cmdline_buf);
+ dtb_addr_actual = add_buffer(info, blob_buf, blob_size, blob_size, 0, dtb_addr,
+ kernel_addr + KERNEL_ACCESS_TOP, 1);
+ if (dtb_addr_actual != dtb_addr) {
+ error_msg = "Error device tree not loadded to address it was expecting to be loaded too!\n";
+ goto out2;
+ }
+
+ /*
+ * set various variables for the purgatory.
+ * ehdr.e_entry is a virtual address. we know physical start
+ * address of the kernel (kernel_addr). Find the offset of
+ * e_entry from the virtual start address(e_phdr[0].p_vaddr)
+ * and calculate the actual physical address of the 'kernel entry'.
+ */
+ addr = kernel_addr + (ehdr.e_entry - ehdr.e_phdr[0].p_vaddr);
+ elf_rel_set_symbol(&info->rhdr, "kernel", &addr, sizeof(addr));
+
+ addr = dtb_addr;
+ elf_rel_set_symbol(&info->rhdr, "dt_offset",
+ &addr, sizeof(addr));
+
+#define PUL_STACK_SIZE (16 * 1024)
+ addr = locate_hole(info, PUL_STACK_SIZE, 0, 0,
+ elf_max_addr(&ehdr), 1);
+ addr += PUL_STACK_SIZE;
+ elf_rel_set_symbol(&info->rhdr, "stack", &addr, sizeof(addr));
+#undef PUL_STACK_SIZE
+
+ /*
+ * Fixup ThreadPointer(r2) for purgatory.
+ * PPC32 ELF ABI expects :
+ * ThreadPointer (TP) = TCB + 0x7000
+ * We manually allocate a TCB space and set the TP
+ * accordingly.
+ */
+#define TCB_SIZE 1024
+#define TCB_TP_OFFSET 0x7000 /* PPC32 ELF ABI */
+
+ addr = locate_hole(info, TCB_SIZE, 0, 0,
+ ((unsigned long)elf_max_addr(&ehdr) - TCB_TP_OFFSET),
+ 1);
+ addr += TCB_SIZE + TCB_TP_OFFSET;
+ elf_rel_set_symbol(&info->rhdr, "my_thread_ptr", &addr, sizeof(addr));
+
+#undef TCB_SIZE
+#undef TCB_TP_OFFSET
+
+ addr = elf_rel_get_addr(&info->rhdr, "purgatory_start");
+ info->entry = (void *)addr;
+
+out2:
+ free(cmdline_buf);
+#endif
+out:
+ free_elf_info(&ehdr);
+ free(crash_cmdline);
+ if (!tmp_cmdline)
+ free(command_line);
+ if (error_msg)
+ die("%s", error_msg);
+
+ return result;
+}
diff --git a/kexec/arch/ppc/kexec-elf-rel-ppc.c b/kexec/arch/ppc/kexec-elf-rel-ppc.c
new file mode 100644
index 0000000..1acbd86
--- /dev/null
+++ b/kexec/arch/ppc/kexec-elf-rel-ppc.c
@@ -0,0 +1,69 @@
+#include <stdio.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+
+int machine_verify_elf_rel(struct mem_ehdr *ehdr)
+{
+ if (ehdr->ei_data != ELFDATA2MSB) {
+ return 0;
+ }
+ if (ehdr->ei_class != ELFCLASS32) {
+ return 0;
+ }
+ if (ehdr->e_machine != EM_PPC) {
+ return 0;
+ }
+ return 1;
+}
+
+void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr),
+ struct mem_sym *UNUSED(sym), unsigned long r_type, void *location,
+ unsigned long address, unsigned long value)
+{
+ switch(r_type) {
+ case R_PPC_ADDR32:
+ /* Simply set it */
+ *(uint32_t *)location = value;
+ break;
+
+ case R_PPC_ADDR16_LO:
+ /* Low half of the symbol */
+ *(uint16_t *)location = value;
+ break;
+
+ case R_PPC_ADDR16_HI:
+ *(uint16_t *)location = (value>>16) & 0xffff;
+ break;
+
+ case R_PPC_ADDR16_HA:
+ /* Sign-adjusted lower 16 bits: PPC ELF ABI says:
+ (((x >> 16) + ((x & 0x8000) ? 1 : 0))) & 0xFFFF.
+ This is the same, only sane.
+ */
+ *(uint16_t *)location = (value + 0x8000) >> 16;
+ break;
+
+ case R_PPC_REL24:
+ if ((int)(value - address) < -0x02000000
+ || (int)(value - address) >= 0x02000000)
+ {
+ die("Symbol more than 16MiB away");
+ }
+ /* Only replace bits 2 through 26 */
+ *(uint32_t *)location
+ = (*(uint32_t *)location & ~0x03fffffc)
+ | ((value - address)
+ & 0x03fffffc);
+ break;
+
+ case R_PPC_REL32:
+ /* 32-bit relative jump. */
+ *(uint32_t *)location = value - address;
+ break;
+ default:
+ die("Unknown rela relocation: %lu\n", r_type);
+ break;
+ }
+ return;
+}
diff --git a/kexec/arch/ppc/kexec-ppc.c b/kexec/arch/ppc/kexec-ppc.c
new file mode 100644
index 0000000..03bec36
--- /dev/null
+++ b/kexec/arch/ppc/kexec-ppc.c
@@ -0,0 +1,968 @@
+/*
+ * kexec-ppc.c - kexec for the PowerPC
+ * Copyright (C) 2004, 2005 Albert Herranz
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "kexec-ppc.h"
+#include "crashdump-powerpc.h"
+#include <arch/options.h>
+
+#include "config.h"
+
+unsigned long dt_address_cells = 0, dt_size_cells = 0;
+uint64_t rmo_top;
+uint64_t memory_limit;
+unsigned long long crash_base = 0, crash_size = 0;
+unsigned long long initrd_base = 0, initrd_size = 0;
+unsigned long long ramdisk_base = 0, ramdisk_size = 0;
+unsigned int rtas_base, rtas_size;
+int max_memory_ranges;
+const char *ramdisk;
+
+/*
+ * Reads the #address-cells and #size-cells on this platform.
+ * This is used to parse the memory/reg info from the device-tree
+ */
+int init_memory_region_info()
+{
+ size_t res = 0;
+ int fd;
+ char *file;
+
+ file = "/proc/device-tree/#address-cells";
+ fd = open(file, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Unable to open %s\n", file);
+ return -1;
+ }
+
+ res = read(fd, &dt_address_cells, sizeof(dt_address_cells));
+ if (res != sizeof(dt_address_cells)) {
+ fprintf(stderr, "Error reading %s\n", file);
+ return -1;
+ }
+ close(fd);
+
+ file = "/proc/device-tree/#size-cells";
+ fd = open(file, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Unable to open %s\n", file);
+ return -1;
+ }
+
+ res = read(fd, &dt_size_cells, sizeof(dt_size_cells));
+ if (res != sizeof(dt_size_cells)) {
+ fprintf(stderr, "Error reading %s\n", file);
+ return -1;
+ }
+ close(fd);
+
+ /* Convert the sizes into bytes */
+ dt_size_cells *= sizeof(unsigned long);
+ dt_address_cells *= sizeof(unsigned long);
+
+ return 0;
+}
+
+#define MAXBYTES 128
+/*
+ * Reads the memory region info from the device-tree node pointed
+ * by @fd and fills the *start, *end with the boundaries of the region
+ */
+int read_memory_region_limits(int fd, unsigned long long *start,
+ unsigned long long *end)
+{
+ char buf[MAXBYTES];
+ unsigned long *p;
+ unsigned long nbytes = dt_address_cells + dt_size_cells;
+
+ if (lseek(fd, 0, SEEK_SET) == -1) {
+ fprintf(stderr, "Error in file seek\n");
+ return -1;
+ }
+ if (read(fd, buf, nbytes) != nbytes) {
+ fprintf(stderr, "Error reading the memory region info\n");
+ return -1;
+ }
+
+ p = (unsigned long*)buf;
+ if (dt_address_cells == sizeof(unsigned long)) {
+ *start = p[0];
+ p++;
+ } else if (dt_address_cells == sizeof(unsigned long long)) {
+ *start = ((unsigned long long *)p)[0];
+ p = (unsigned long long *)p + 1;
+ } else {
+ fprintf(stderr, "Unsupported value for #address-cells : %ld\n",
+ dt_address_cells);
+ return -1;
+ }
+
+ if (dt_size_cells == sizeof(unsigned long))
+ *end = *start + p[0];
+ else if (dt_size_cells == sizeof(unsigned long long))
+ *end = *start + ((unsigned long long *)p)[0];
+ else {
+ fprintf(stderr, "Unsupported value for #size-cells : %ld\n",
+ dt_size_cells);
+ return -1;
+ }
+
+ return 0;
+}
+
+void arch_reuse_initrd(void)
+{
+ reuse_initrd = 1;
+}
+
+#ifdef WITH_GAMECUBE
+#define MAX_MEMORY_RANGES 64
+static struct memory_range memory_range[MAX_MEMORY_RANGES];
+
+static int get_memory_ranges_gc(struct memory_range **range, int *ranges,
+ unsigned long UNUSED(kexec_flags))
+{
+ int memory_ranges = 0;
+
+ /* RAM - lowmem used by DOLs - framebuffer */
+ memory_range[memory_ranges].start = 0x00003000;
+ memory_range[memory_ranges].end = 0x0174bfff;
+ memory_range[memory_ranges].type = RANGE_RAM;
+ memory_ranges++;
+ *range = memory_range;
+ *ranges = memory_ranges;
+ return 0;
+}
+#else
+static int use_new_dtb;
+static int nr_memory_ranges, nr_exclude_ranges;
+static struct memory_range *exclude_range;
+static struct memory_range *memory_range;
+static struct memory_range *base_memory_range;
+static uint64_t memory_max;
+
+/*
+ * Count the memory nodes under /proc/device-tree and populate the
+ * max_memory_ranges variable. This variable replaces MAX_MEMORY_RANGES
+ * macro used earlier.
+ */
+static int count_memory_ranges(void)
+{
+ char device_tree[256] = "/proc/device-tree/";
+ struct dirent *dentry;
+ DIR *dir;
+
+ if ((dir = opendir(device_tree)) == NULL) {
+ perror(device_tree);
+ return -1;
+ }
+
+ while ((dentry = readdir(dir)) != NULL) {
+ if (strncmp(dentry->d_name, "memory@", 7) &&
+ strcmp(dentry->d_name, "memory"))
+ continue;
+ max_memory_ranges++;
+ }
+
+ /* need to add extra region for retained initrd */
+ if (use_new_dtb) {
+ max_memory_ranges++;
+ }
+
+ closedir(dir);
+ return 0;
+
+}
+
+ static void cleanup_memory_ranges(void)
+ {
+ free(memory_range);
+ free(base_memory_range);
+ free(exclude_range);
+ }
+
+/*
+ * Allocate memory for various data structures used to hold
+ * values of different memory ranges
+ */
+static int alloc_memory_ranges(void)
+{
+ int memory_range_len;
+
+ memory_range_len = sizeof(struct memory_range) * max_memory_ranges;
+
+ memory_range = malloc(memory_range_len);
+ if (!memory_range)
+ return -1;
+
+ base_memory_range = malloc(memory_range_len);
+ if (!base_memory_range)
+ goto err1;
+
+ exclude_range = malloc(memory_range_len);
+ if (!exclude_range)
+ goto err1;
+
+ memset(memory_range, 0, memory_range_len);
+ memset(base_memory_range, 0, memory_range_len);
+ memset(exclude_range, 0, memory_range_len);
+ return 0;
+
+err1:
+ fprintf(stderr, "memory range structure allocation failure\n");
+ cleanup_memory_ranges();
+ return -1;
+}
+
+/* Sort the exclude ranges in memory */
+static int sort_ranges(void)
+{
+ int i, j;
+ uint64_t tstart, tend;
+ for (i = 0; i < nr_exclude_ranges - 1; i++) {
+ for (j = 0; j < nr_exclude_ranges - i - 1; j++) {
+ if (exclude_range[j].start > exclude_range[j+1].start) {
+ tstart = exclude_range[j].start;
+ tend = exclude_range[j].end;
+ exclude_range[j].start = exclude_range[j+1].start;
+ exclude_range[j].end = exclude_range[j+1].end;
+ exclude_range[j+1].start = tstart;
+ exclude_range[j+1].end = tend;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Sort the base ranges in memory - this is useful for ensuring that our
+ * ranges are in ascending order, even if device-tree read of memory nodes
+ * is done differently. Also, could be used for other range coalescing later
+ */
+static int sort_base_ranges(void)
+{
+ int i, j;
+ unsigned long long tstart, tend;
+
+ for (i = 0; i < nr_memory_ranges - 1; i++) {
+ for (j = 0; j < nr_memory_ranges - i - 1; j++) {
+ if (base_memory_range[j].start > base_memory_range[j+1].start) {
+ tstart = base_memory_range[j].start;
+ tend = base_memory_range[j].end;
+ base_memory_range[j].start = base_memory_range[j+1].start;
+ base_memory_range[j].end = base_memory_range[j+1].end;
+ base_memory_range[j+1].start = tstart;
+ base_memory_range[j+1].end = tend;
+ }
+ }
+ }
+ return 0;
+}
+
+static int realloc_memory_ranges(void)
+{
+ size_t memory_range_len;
+
+ max_memory_ranges++;
+ memory_range_len = sizeof(struct memory_range) * max_memory_ranges;
+
+ memory_range = (struct memory_range *) realloc(memory_range,
+ memory_range_len);
+ if (!memory_range)
+ goto err;
+
+ base_memory_range = (struct memory_range *) realloc(base_memory_range,
+ memory_range_len);
+ if (!base_memory_range)
+ goto err;
+
+ exclude_range = (struct memory_range *) realloc(exclude_range,
+ memory_range_len);
+ if (!exclude_range)
+ goto err;
+
+ usablemem_rgns.ranges = (struct memory_range *)
+ realloc(usablemem_rgns.ranges,
+ memory_range_len);
+ if (!(usablemem_rgns.ranges))
+ goto err;
+
+ return 0;
+
+err:
+ fprintf(stderr, "memory range structure re-allocation failure\n");
+ return -1;
+}
+
+/* Get base memory ranges */
+static int get_base_ranges(void)
+{
+ int local_memory_ranges = 0;
+ char device_tree[256] = "/proc/device-tree/";
+ char fname[256];
+ char buf[MAXBYTES];
+ DIR *dir, *dmem;
+ struct dirent *dentry, *mentry;
+ int n, fd;
+
+ if ((dir = opendir(device_tree)) == NULL) {
+ perror(device_tree);
+ return -1;
+ }
+ while ((dentry = readdir(dir)) != NULL) {
+ if (strncmp(dentry->d_name, "memory@", 7) &&
+ strcmp(dentry->d_name, "memory"))
+ continue;
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ if ((dmem = opendir(fname)) == NULL) {
+ perror(fname);
+ closedir(dir);
+ return -1;
+ }
+ while ((mentry = readdir(dmem)) != NULL) {
+ unsigned long long start, end;
+
+ if (strcmp(mentry->d_name, "reg"))
+ continue;
+ strcat(fname, "/reg");
+ if ((fd = open(fname, O_RDONLY)) < 0) {
+ perror(fname);
+ closedir(dmem);
+ closedir(dir);
+ return -1;
+ }
+ if (read_memory_region_limits(fd, &start, &end) != 0) {
+ close(fd);
+ closedir(dmem);
+ closedir(dir);
+ return -1;
+ }
+ if (local_memory_ranges >= max_memory_ranges) {
+ if (realloc_memory_ranges() < 0){
+ close(fd);
+ break;
+ }
+ }
+
+ base_memory_range[local_memory_ranges].start = start;
+ base_memory_range[local_memory_ranges].end = end;
+ base_memory_range[local_memory_ranges].type = RANGE_RAM;
+ local_memory_ranges++;
+ dbgprintf("%016llx-%016llx : %x\n",
+ base_memory_range[local_memory_ranges-1].start,
+ base_memory_range[local_memory_ranges-1].end,
+ base_memory_range[local_memory_ranges-1].type);
+ close(fd);
+ }
+ closedir(dmem);
+ }
+ closedir(dir);
+ nr_memory_ranges = local_memory_ranges;
+ sort_base_ranges();
+ memory_max = base_memory_range[nr_memory_ranges - 1].end;
+
+ dbgprintf("get base memory ranges:%d\n", nr_memory_ranges);
+
+ return 0;
+}
+
+static int read_kernel_memory_limit(char *fname, char *buf)
+{
+ FILE *file;
+ int n;
+
+ if (!fname || !buf)
+ return -1;
+
+ file = fopen(fname, "r");
+ if (file == NULL) {
+ if (errno != ENOENT) {
+ perror(fname);
+ return -1;
+ }
+ errno = 0;
+ /*
+ * fall through. On older kernel this file
+ * is not present. Hence return success.
+ */
+ } else {
+ /* Memory limit property is of u64 type. */
+ if ((n = fread(&memory_limit, 1, sizeof(uint64_t), file)) < 0) {
+ perror(fname);
+ goto err_out;
+ }
+ if (n != sizeof(uint64_t)) {
+ fprintf(stderr, "%s node has invalid size: %d\n",
+ fname, n);
+ goto err_out;
+ }
+ fclose(file);
+ }
+ return 0;
+err_out:
+ fclose(file);
+ return -1;
+}
+
+/* Return 0 if fname/value valid, -1 otherwise */
+int get_devtree_value(const char *fname, unsigned long long *value)
+{
+ FILE *file;
+ char buf[MAXBYTES];
+ int n = -1;
+
+ if ((file = fopen(fname, "r"))) {
+ n = fread(buf, 1, MAXBYTES, file);
+ fclose(file);
+ }
+
+ if (n == sizeof(uint32_t))
+ *value = ((uint32_t *)buf)[0];
+ else if (n == sizeof(uint64_t))
+ *value = ((uint64_t *)buf)[0];
+ else {
+ fprintf(stderr, "%s node has invalid size: %d\n", fname, n);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Get devtree details and create exclude_range array
+ * Also create usablemem_ranges for KEXEC_ON_CRASH
+ */
+static int get_devtree_details(unsigned long kexec_flags)
+{
+ uint64_t rmo_base;
+ unsigned long long tce_base;
+ unsigned int tce_size;
+ unsigned long long htab_base, htab_size;
+ unsigned long long kernel_end;
+ unsigned long long initrd_start, initrd_end;
+ char buf[MAXBYTES];
+ char device_tree[256] = "/proc/device-tree/";
+ char fname[256];
+ DIR *dir, *cdir;
+ FILE *file;
+ struct dirent *dentry;
+ int n, i = 0;
+
+ if ((dir = opendir(device_tree)) == NULL) {
+ perror(device_tree);
+ return -1;
+ }
+
+ while ((dentry = readdir(dir)) != NULL) {
+ if (strncmp(dentry->d_name, "chosen", 6) &&
+ strncmp(dentry->d_name, "memory@", 7) &&
+ strncmp(dentry->d_name, "memory", 6) &&
+ strncmp(dentry->d_name, "pci@", 4) &&
+ strncmp(dentry->d_name, "rtas", 4))
+ continue;
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ if ((cdir = opendir(fname)) == NULL) {
+ perror(fname);
+ goto error_opendir;
+ }
+
+ if (strncmp(dentry->d_name, "chosen", 6) == 0) {
+ /* only reserve kernel region if we are doing a crash kernel */
+ if (kexec_flags & KEXEC_ON_CRASH) {
+ strcat(fname, "/linux,kernel-end");
+ file = fopen(fname, "r");
+ if (!file) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if ((n = fread(buf, 1, MAXBYTES, file)) < 0) {
+ perror(fname);
+ goto error_openfile;
+ }
+ if (n == sizeof(uint32_t)) {
+ kernel_end = ((uint32_t *)buf)[0];
+ } else if (n == sizeof(uint64_t)) {
+ kernel_end = ((uint64_t *)buf)[0];
+ } else {
+ fprintf(stderr, "%s node has invalid size: %d\n", fname, n);
+ goto error_openfile;
+ }
+ fclose(file);
+
+ /* Add kernel memory to exclude_range */
+ exclude_range[i].start = 0x0UL;
+ exclude_range[i].end = kernel_end;
+ i++;
+ if (i >= max_memory_ranges)
+ realloc_memory_ranges();
+ memset(fname, 0, sizeof(fname));
+ sprintf(fname, "%s%s%s",
+ device_tree, dentry->d_name,
+ "/linux,crashkernel-base");
+ file = fopen(fname, "r");
+ if (!file) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if ((n = fread(buf, 1, MAXBYTES, file)) < 0) {
+ perror(fname);
+ goto error_openfile;
+ }
+ if (n == sizeof(uint32_t)) {
+ crash_base = ((uint32_t *)buf)[0];
+ } else if (n == sizeof(uint64_t)) {
+ crash_base = ((uint64_t *)buf)[0];
+ } else {
+ fprintf(stderr, "%s node has invalid size: %d\n", fname, n);
+ goto error_openfile;
+ }
+ fclose(file);
+
+ memset(fname, 0, sizeof(fname));
+ sprintf(fname, "%s%s%s",
+ device_tree, dentry->d_name,
+ "/linux,crashkernel-size");
+ file = fopen(fname, "r");
+ if (!file) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if ((n = fread(buf, 1, MAXBYTES, file)) < 0) {
+ perror(fname);
+ goto error_openfile;
+ }
+ if (n == sizeof(uint32_t)) {
+ crash_size = ((uint32_t *)buf)[0];
+ } else if (n == sizeof(uint64_t)) {
+ crash_size = ((uint64_t *)buf)[0];
+ } else {
+ fprintf(stderr, "%s node has invalid size: %d\n", fname, n);
+ goto error_openfile;
+ }
+ fclose(file);
+
+ if (crash_base > mem_min)
+ mem_min = crash_base;
+ if (crash_base + crash_size < mem_max)
+ mem_max = crash_base + crash_size;
+
+#ifndef CONFIG_BOOKE
+ add_usable_mem_rgns(0, crash_base + crash_size);
+ /* Reserve the region (KDUMP_BACKUP_LIMIT,crash_base) */
+ reserve(KDUMP_BACKUP_LIMIT,
+ crash_base-KDUMP_BACKUP_LIMIT);
+#else
+ add_usable_mem_rgns(crash_base, crash_size);
+#endif
+ }
+ /*
+ * Read the first kernel's memory limit.
+ * If the first kernel is booted with mem= option then
+ * it would export "linux,memory-limit" file
+ * reflecting value for the same.
+ */
+ memset(fname, 0, sizeof(fname));
+ snprintf(fname, sizeof(fname), "%s%s%s", device_tree,
+ dentry->d_name, "/linux,memory-limit");
+ if (read_kernel_memory_limit(fname, buf) < 0)
+ goto error_opencdir;
+
+ /* reserve the initrd_start and end locations. */
+ memset(fname, 0, sizeof(fname));
+ sprintf(fname, "%s%s%s",
+ device_tree, dentry->d_name,
+ "/linux,initrd-start");
+ file = fopen(fname, "r");
+ if (!file) {
+ errno = 0;
+ initrd_start = 0;
+ } else {
+ if ((n = fread(buf, 1, MAXBYTES, file)) < 0) {
+ perror(fname);
+ goto error_openfile;
+ }
+ if (n == sizeof(uint32_t)) {
+ initrd_start = ((uint32_t *)buf)[0];
+ } else if (n == sizeof(uint64_t)) {
+ initrd_start = ((uint64_t *)buf)[0];
+ } else {
+ fprintf(stderr, "%s node has invalid size: %d\n", fname, n);
+ goto error_openfile;
+ }
+ fclose(file);
+ }
+
+ memset(fname, 0, sizeof(fname));
+ sprintf(fname, "%s%s%s",
+ device_tree, dentry->d_name,
+ "/linux,initrd-end");
+ file = fopen(fname, "r");
+ if (!file) {
+ errno = 0;
+ initrd_end = 0;
+ } else {
+ if ((n = fread(buf, 1, MAXBYTES, file)) < 0) {
+ perror(fname);
+ goto error_openfile;
+ }
+ if (n == sizeof(uint32_t)) {
+ initrd_end = ((uint32_t *)buf)[0];
+ } else if (n == sizeof(uint64_t)) {
+ initrd_end = ((uint64_t *)buf)[0];
+ } else {
+ fprintf(stderr, "%s node has invalid size: %d\n", fname, n);
+ goto error_openfile;
+ }
+ fclose(file);
+ }
+
+ if ((initrd_end - initrd_start) != 0 ) {
+ initrd_base = initrd_start;
+ initrd_size = initrd_end - initrd_start;
+ }
+
+ if (reuse_initrd) {
+ /* Add initrd address to exclude_range */
+ exclude_range[i].start = initrd_start;
+ exclude_range[i].end = initrd_end;
+ i++;
+ if (i >= max_memory_ranges)
+ realloc_memory_ranges();
+ }
+
+ /* HTAB */
+ memset(fname, 0, sizeof(fname));
+ sprintf(fname, "%s%s%s",
+ device_tree, dentry->d_name,
+ "/linux,htab-base");
+ file = fopen(fname, "r");
+ if (!file) {
+ closedir(cdir);
+ if (errno == ENOENT) {
+ /* Non LPAR */
+ errno = 0;
+ continue;
+ }
+ perror(fname);
+ goto error_opendir;
+ }
+ if (fread(&htab_base, sizeof(unsigned long), 1, file)
+ != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ memset(fname, 0, sizeof(fname));
+ sprintf(fname, "%s%s%s",
+ device_tree, dentry->d_name,
+ "/linux,htab-size");
+ file = fopen(fname, "r");
+ if (!file) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if (fread(&htab_size, sizeof(unsigned long), 1, file)
+ != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ /* Add htab address to exclude_range - NON-LPAR only */
+ exclude_range[i].start = htab_base;
+ exclude_range[i].end = htab_base + htab_size;
+ i++;
+ if (i >= max_memory_ranges)
+ realloc_memory_ranges();
+
+ } /* chosen */
+ if (strncmp(dentry->d_name, "rtas", 4) == 0) {
+ strcat(fname, "/linux,rtas-base");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if (fread(&rtas_base, sizeof(unsigned int), 1, file)
+ != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ memset(fname, 0, sizeof(fname));
+ sprintf(fname, "%s%s%s",
+ device_tree, dentry->d_name,
+ "/linux,rtas-size");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if (fread(&rtas_size, sizeof(unsigned int), 1, file)
+ != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ closedir(cdir);
+ /* Add rtas to exclude_range */
+ exclude_range[i].start = rtas_base;
+ exclude_range[i].end = rtas_base + rtas_size;
+ i++;
+ if (kexec_flags & KEXEC_ON_CRASH)
+ add_usable_mem_rgns(rtas_base, rtas_size);
+ } /* rtas */
+
+ if (!strncmp(dentry->d_name, "memory@", 7) ||
+ !strcmp(dentry->d_name, "memory")) {
+ int fd;
+ strcat(fname, "/reg");
+ if ((fd = open(fname, O_RDONLY)) < 0) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if (read_memory_region_limits(fd, &rmo_base, &rmo_top) != 0)
+ goto error_openfile;
+
+ if (rmo_top > 0x30000000UL)
+ rmo_top = 0x30000000UL;
+
+ close(fd);
+ closedir(cdir);
+ } /* memory */
+
+ if (strncmp(dentry->d_name, "pci@", 4) == 0) {
+ strcat(fname, "/linux,tce-base");
+ file = fopen(fname, "r");
+ if (!file) {
+ closedir(cdir);
+ if (errno == ENOENT) {
+ /* Non LPAR */
+ errno = 0;
+ continue;
+ }
+ perror(fname);
+ goto error_opendir;
+ }
+ if (fread(&tce_base, sizeof(unsigned long), 1, file)
+ != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ memset(fname, 0, sizeof(fname));
+ sprintf(fname, "%s%s%s",
+ device_tree, dentry->d_name,
+ "/linux,tce-size");
+ file = fopen(fname, "r");
+ if (!file) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if (fread(&tce_size, sizeof(unsigned int), 1, file)
+ != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ /* Add tce to exclude_range - NON-LPAR only */
+ exclude_range[i].start = tce_base;
+ exclude_range[i].end = tce_base + tce_size;
+ i++;
+ if (kexec_flags & KEXEC_ON_CRASH)
+ add_usable_mem_rgns(tce_base, tce_size);
+ closedir(cdir);
+ } /* pci */
+ }
+ closedir(dir);
+
+ nr_exclude_ranges = i;
+
+ sort_ranges();
+
+
+ int k;
+ for (k = 0; k < i; k++)
+ dbgprintf("exclude_range sorted exclude_range[%d] "
+ "start:%llx, end:%llx\n", k, exclude_range[k].start,
+ exclude_range[k].end);
+
+ return 0;
+
+error_openfile:
+ fclose(file);
+error_opencdir:
+ closedir(cdir);
+error_opendir:
+ closedir(dir);
+ return -1;
+}
+
+
+/* Setup a sorted list of memory ranges. */
+static int setup_memory_ranges(unsigned long kexec_flags)
+{
+ int i, j = 0;
+
+ /* Get the base list of memory ranges from /proc/device-tree/memory
+ * nodes. Build list of ranges to be excluded from valid memory
+ */
+
+ if (get_base_ranges())
+ goto out;
+ if (get_devtree_details(kexec_flags))
+ goto out;
+
+ for (i = 0; i < nr_exclude_ranges; i++) {
+ /* If first exclude range does not start with 0, include the
+ * first hole of valid memory from 0 - exclude_range[0].start
+ */
+ if (i == 0) {
+ if (exclude_range[i].start != 0) {
+ memory_range[j].start = 0;
+ memory_range[j].end = exclude_range[i].start - 1;
+ memory_range[j].type = RANGE_RAM;
+ j++;
+ }
+ } /* i == 0 */
+ /* If the last exclude range does not end at memory_max, include
+ * the last hole of valid memory from exclude_range[last].end -
+ * memory_max
+ */
+ if (i == nr_exclude_ranges - 1) {
+ if (exclude_range[i].end < memory_max) {
+ memory_range[j].start = exclude_range[i].end + 1;
+ memory_range[j].end = memory_max;
+ memory_range[j].type = RANGE_RAM;
+ j++;
+ /* Limit the end to rmo_top */
+ if (memory_range[j-1].start >= rmo_top) {
+ j--;
+ break;
+ }
+ if ((memory_range[j-1].start < rmo_top) &&
+ (memory_range[j-1].end >= rmo_top)) {
+ memory_range[j-1].end = rmo_top;
+ break;
+ }
+ continue;
+ }
+ } /* i == nr_exclude_ranges - 1 */
+ /* contiguous exclude ranges - skip */
+ if (exclude_range[i+1].start == exclude_range[i].end + 1)
+ continue;
+ memory_range[j].start = exclude_range[i].end + 1;
+ memory_range[j].end = exclude_range[i+1].start - 1;
+ memory_range[j].type = RANGE_RAM;
+ j++;
+ /* Limit range to rmo_top */
+ if (memory_range[j-1].start >= rmo_top) {
+ j--;
+ break;
+ }
+ if ((memory_range[j-1].start < rmo_top) &&
+ (memory_range[j-1].end >= rmo_top)) {
+ memory_range[j-1].end = rmo_top;
+ break;
+ }
+ }
+
+ /* fixup in case we have no exclude regions */
+ if (!j) {
+ memory_range[0].start = base_memory_range[0].start;
+ memory_range[0].end = rmo_top;
+ memory_range[0].type = RANGE_RAM;
+ nr_memory_ranges = 1;
+ } else
+ nr_memory_ranges = j;
+
+
+ int k;
+ for (k = 0; k < j; k++)
+ dbgprintf("setup_memory_ranges memory_range[%d] "
+ "start:%llx, end:%llx\n", k, memory_range[k].start,
+ memory_range[k].end);
+ return 0;
+
+out:
+ cleanup_memory_ranges();
+ return -1;
+}
+
+
+/* Return a list of valid memory ranges */
+int get_memory_ranges_dt(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags)
+{
+ if (count_memory_ranges())
+ return -1;
+ if (alloc_memory_ranges())
+ return -1;
+ if (setup_memory_ranges(kexec_flags))
+ return -1;
+
+ *range = memory_range;
+ *ranges = nr_memory_ranges;
+ return 0;
+}
+#endif
+
+/* Return a sorted list of memory ranges. */
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags)
+{
+ int res = 0;
+
+ res = init_memory_region_info();
+ if (res != 0)
+ return res;
+#ifdef WITH_GAMECUBE
+ return get_memory_ranges_gc(range, ranges, kexec_flags);
+#else
+ return get_memory_ranges_dt(range, ranges, kexec_flags);
+#endif
+}
+
+struct file_type file_type[] = {
+ {"elf-ppc", elf_ppc_probe, elf_ppc_load, elf_ppc_usage},
+ {"dol-ppc", dol_ppc_probe, dol_ppc_load, dol_ppc_usage},
+ {"uImage-ppc", uImage_ppc_probe, uImage_ppc_load, uImage_ppc_usage },
+};
+int file_types = sizeof(file_type) / sizeof(file_type[0]);
+
+void arch_usage(void)
+{
+}
+
+int arch_process_options(int argc, char **argv)
+{
+ return 0;
+}
+
+const struct arch_map_entry arches[] = {
+ /* For compatibility with older patches
+ * use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_PPC here.
+ */
+ { "ppc", KEXEC_ARCH_DEFAULT },
+ { NULL, 0 },
+};
+
+int arch_compat_trampoline(struct kexec_info *UNUSED(info))
+{
+ return 0;
+}
+
+void arch_update_purgatory(struct kexec_info *UNUSED(info))
+{
+}
+
diff --git a/kexec/arch/ppc/kexec-ppc.h b/kexec/arch/ppc/kexec-ppc.h
new file mode 100644
index 0000000..04e728e
--- /dev/null
+++ b/kexec/arch/ppc/kexec-ppc.h
@@ -0,0 +1,86 @@
+#ifndef KEXEC_PPC_H
+#define KEXEC_PPC_H
+
+#define MAXBYTES 128
+#define MAX_LINE 160
+#define CORE_TYPE_ELF32 1
+#define CORE_TYPE_ELF64 2
+
+#define COMMAND_LINE_SIZE 2048 /* from kernel */
+
+extern unsigned char setup_simple_start[];
+extern uint32_t setup_simple_size;
+
+extern struct {
+ uint32_t spr8;
+} setup_simple_regs;
+
+extern unsigned char setup_dol_start[];
+extern uint32_t setup_dol_size;
+extern uint64_t rmo_top;
+
+extern struct {
+ uint32_t spr8;
+} setup_dol_regs;
+
+#define SIZE_16M (16*1024*1024UL)
+
+int elf_ppc_probe(const char *buf, off_t len);
+int elf_ppc_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void elf_ppc_usage(void);
+
+int uImage_ppc_probe(const char *buf, off_t len);
+int uImage_ppc_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void uImage_ppc_usage(void);
+
+int dol_ppc_probe(const char *buf, off_t len);
+int dol_ppc_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void dol_ppc_usage(void);
+
+/*
+ * During inital setup the kernel does not map the whole memory but a part of
+ * it. On Book-E that is 64MiB, 601 24MiB or 256MiB (if possible).
+ */
+#define KERNEL_ACCESS_TOP (24 * 1024 * 1024)
+
+/* boot block version 17 as defined by the linux kernel */
+struct bootblock {
+ unsigned magic,
+ totalsize,
+ off_dt_struct,
+ off_dt_strings,
+ off_mem_rsvmap,
+ version,
+ last_comp_version,
+ boot_physid,
+ dt_strings_size,
+ dt_struct_size;
+};
+
+typedef struct mem_rgns {
+ unsigned int size;
+ struct memory_range *ranges;
+} mem_rgns_t;
+extern mem_rgns_t usablemem_rgns;
+extern int max_memory_ranges;
+extern unsigned long long crash_base, crash_size;
+extern unsigned long long initrd_base, initrd_size;
+extern unsigned long long ramdisk_base, ramdisk_size;
+extern unsigned char reuse_initrd;
+extern const char *ramdisk;
+
+/* Method to parse the memory/reg nodes in device-tree */
+extern unsigned long dt_address_cells, dt_size_cells;
+extern int init_memory_region_info(void);
+extern int read_memory_region_limits(int fd, unsigned long long *start,
+ unsigned long long *end);
+extern int get_devtree_value(const char *fname, unsigned long long *pvalue);
+/*fs2dt*/
+void reserve(unsigned long long where, unsigned long long length);
+
+/* Defined kexec-uImage-ppc.c */
+extern char* slurp_ramdisk_ppc(const char *filename, off_t *r_size);
+#endif /* KEXEC_PPC_H */
diff --git a/kexec/arch/ppc/kexec-uImage-ppc.c b/kexec/arch/ppc/kexec-uImage-ppc.c
new file mode 100644
index 0000000..e8f7adc
--- /dev/null
+++ b/kexec/arch/ppc/kexec-uImage-ppc.c
@@ -0,0 +1,325 @@
+/*
+ * uImage support for PowerPC
+ */
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <image.h>
+#include <getopt.h>
+#include <arch/options.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "kexec-ppc.h"
+#include "fixup_dtb.h"
+#include <kexec-uImage.h>
+#include "crashdump-powerpc.h"
+#include <limits.h>
+
+int create_flatten_tree(struct kexec_info *, unsigned char **, unsigned long *,
+ char *);
+
+/* See options.h -- add any more there, too. */
+static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ {"command-line", 1, 0, OPT_APPEND},
+ {"append", 1, 0, OPT_APPEND},
+ {"ramdisk", 1, 0, OPT_RAMDISK},
+ {"initrd", 1, 0, OPT_RAMDISK},
+ {"dtb", 1, 0, OPT_DTB},
+ {"reuse-node", 1, 0, OPT_NODES},
+ {0, 0, 0, 0},
+};
+static const char short_options[] = KEXEC_ARCH_OPT_STR;
+
+void uImage_ppc_usage(void)
+{
+ printf(
+ " --command-line=STRING Set the kernel command line to STRING.\n"
+ " --append=STRING Set the kernel command line to STRING.\n"
+ " --ramdisk=<filename> Initial RAM disk.\n"
+ " --initrd=<filename> same as --ramdisk\n"
+ " --dtb=<filename> Specify device tree blob file.\n"
+ " --reuse-node=node Specify nodes which should be taken from /proc/device-tree.\n"
+ " Can be set multiple times.\n"
+ );
+}
+
+/*
+ * Load the ramdisk into buffer.
+ * If the supplied image is in uImage format use
+ * uImage_load() to read the payload from the image.
+ */
+char *slurp_ramdisk_ppc(const char *filename, off_t *r_size)
+{
+ struct Image_info img;
+ off_t size;
+ const char *buf = slurp_file(filename, &size);
+ int rc;
+
+ /* Check if this is a uImage RAMDisk */
+ if (!buf)
+ return buf;
+ rc = uImage_probe_ramdisk(buf, size, IH_ARCH_PPC);
+ if (rc < 0)
+ die("uImage: Corrupted ramdisk file %s\n", filename);
+ else if (rc == 0) {
+ if (uImage_load(buf, size, &img) != 0)
+ die("uImage: Reading %ld bytes from %s failed\n",
+ size, filename);
+ buf = img.buf;
+ size = img.len;
+ }
+
+ *r_size = size;
+ return buf;
+}
+
+int uImage_ppc_probe(const char *buf, off_t len)
+{
+ return uImage_probe_kernel(buf, len, IH_ARCH_PPC);
+}
+
+static int ppc_load_bare_bits(int argc, char **argv, const char *buf,
+ off_t len, struct kexec_info *info, unsigned int load_addr,
+ unsigned int ep)
+{
+ char *command_line, *cmdline_buf, *crash_cmdline;
+ char *tmp_cmdline;
+ int command_line_len, crash_cmdline_len;
+ char *dtb;
+ unsigned int addr;
+ unsigned long dtb_addr;
+ unsigned long dtb_addr_actual;
+#define FIXUP_ENTRYS (20)
+ char *fixup_nodes[FIXUP_ENTRYS + 1];
+ int cur_fixup = 0;
+ int opt;
+ int ret = 0;
+ char *seg_buf = NULL;
+ off_t seg_size = 0;
+ unsigned long long hole_addr;
+ unsigned long max_addr;
+ char *blob_buf = NULL;
+ off_t blob_size = 0;
+ char *error_msg = NULL;
+
+ cmdline_buf = NULL;
+ command_line = NULL;
+ tmp_cmdline = NULL;
+ dtb = NULL;
+ max_addr = LONG_MAX;
+
+ while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch (opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ case OPT_APPEND:
+ tmp_cmdline = optarg;
+ break;
+
+ case OPT_RAMDISK:
+ ramdisk = optarg;
+ break;
+
+ case OPT_DTB:
+ dtb = optarg;
+ break;
+
+ case OPT_NODES:
+ if (cur_fixup >= FIXUP_ENTRYS) {
+ die("The number of entries for the fixup is too large\n");
+ }
+ fixup_nodes[cur_fixup] = optarg;
+ cur_fixup++;
+ break;
+ }
+ }
+
+ if (ramdisk && reuse_initrd)
+ die("Can't specify --ramdisk or --initrd with --reuseinitrd\n");
+
+ command_line_len = 0;
+ if (tmp_cmdline) {
+ command_line = tmp_cmdline;
+ } else {
+ command_line = get_command_line();
+ }
+ command_line_len = strlen(command_line) + 1;
+
+ fixup_nodes[cur_fixup] = NULL;
+
+ /*
+ * len contains the length of the whole kernel image except the bss
+ * section. The 1 MiB should cover it. The purgatory and the dtb are
+ * allocated from memtop down towards zero so we should never get too
+ * close to the bss :)
+ */
+#define _1MiB (1 * 1024 * 1024)
+
+ /*
+ * If the provided load_addr cannot be allocated, find a new
+ * area. Rebase the entry point based on the new load_addr.
+ */
+ if (!valid_memory_range(info, load_addr, load_addr + (len + _1MiB))) {
+ int ep_offset = ep - load_addr;
+
+ load_addr = locate_hole(info, len + _1MiB, 0, 0, max_addr, 1);
+ if (load_addr == ULONG_MAX) {
+ printf("Can't allocate memory for kernel of len %ld\n",
+ len + _1MiB);
+ return -1;
+ }
+
+ ep = load_addr + ep_offset;
+ }
+
+ add_segment(info, buf, len, load_addr, len + _1MiB);
+
+
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ crash_cmdline = xmalloc(COMMAND_LINE_SIZE);
+ memset((void *)crash_cmdline, 0, COMMAND_LINE_SIZE);
+ ret = load_crashdump_segments(info, crash_cmdline,
+ max_addr, 0);
+ if (ret < 0) {
+ ret = -1;
+ goto out;
+ }
+ crash_cmdline_len = strlen(crash_cmdline);
+ } else {
+ crash_cmdline = NULL;
+ crash_cmdline_len = 0;
+ }
+
+ if (crash_cmdline_len + command_line_len + 1 > COMMAND_LINE_SIZE) {
+ printf("Kernel command line exceeds maximum possible length\n");
+ return -1;
+ }
+
+ cmdline_buf = xmalloc(COMMAND_LINE_SIZE);
+ memset((void *)cmdline_buf, 0, COMMAND_LINE_SIZE);
+
+ if (command_line)
+ strcpy(cmdline_buf, command_line);
+ if (crash_cmdline)
+ strncat(cmdline_buf, crash_cmdline, crash_cmdline_len);
+
+ elf_rel_build_load(info, &info->rhdr, (const char *)purgatory,
+ purgatory_size, 0, -1, -1, 0);
+
+ /* Here we need to initialize the device tree, and find out where
+ * it is going to live so we can place it directly after the
+ * kernel image */
+ if (dtb) {
+ /* Grab device tree from buffer */
+ blob_buf = slurp_file(dtb, &blob_size);
+ } else {
+ create_flatten_tree(info, (unsigned char **)&blob_buf,
+ (unsigned long *)&blob_size, cmdline_buf);
+ }
+ if (!blob_buf || !blob_size) {
+ error_msg = "Device tree seems to be an empty file.\n";
+ goto out2;
+ }
+
+ /* initial fixup for device tree */
+ blob_buf = fixup_dtb_init(info, blob_buf, &blob_size, load_addr, &dtb_addr);
+
+ if (ramdisk) {
+ seg_buf = slurp_ramdisk_ppc(ramdisk, &seg_size);
+ /* Load ramdisk at top of memory */
+ hole_addr = add_buffer(info, seg_buf, seg_size, seg_size,
+ 0, dtb_addr + blob_size, max_addr, -1);
+ ramdisk_base = hole_addr;
+ ramdisk_size = seg_size;
+ }
+ if (reuse_initrd) {
+ ramdisk_base = initrd_base;
+ ramdisk_size = initrd_size;
+ }
+
+ if (info->kexec_flags & KEXEC_ON_CRASH && ramdisk_base != 0) {
+ if ( (ramdisk_base < crash_base) ||
+ (ramdisk_base > crash_base + crash_size) ) {
+ printf("WARNING: ramdisk is above crashkernel region!\n");
+ }
+ else if (ramdisk_base + ramdisk_size > crash_base + crash_size) {
+ printf("WARNING: ramdisk overflows crashkernel region!\n");
+ }
+ }
+
+ /* Perform final fixup on devie tree, i.e. everything beside what
+ * was done above */
+ fixup_dtb_finalize(info, blob_buf, &blob_size, fixup_nodes,
+ cmdline_buf);
+ dtb_addr_actual = add_buffer(info, blob_buf, blob_size, blob_size, 0, dtb_addr,
+ load_addr + KERNEL_ACCESS_TOP, 1);
+ if (dtb_addr_actual != dtb_addr) {
+ printf("dtb_addr_actual: %lx, dtb_addr: %lx\n", dtb_addr_actual, dtb_addr);
+ error_msg = "Error device tree not loadded to address it was expecting to be loaded too!\n";
+ goto out2;
+ }
+
+ /* set various variables for the purgatory */
+ addr = ep;
+ elf_rel_set_symbol(&info->rhdr, "kernel", &addr, sizeof(addr));
+
+ addr = dtb_addr;
+ elf_rel_set_symbol(&info->rhdr, "dt_offset", &addr, sizeof(addr));
+
+#define PUL_STACK_SIZE (16 * 1024)
+ addr = locate_hole(info, PUL_STACK_SIZE, 0, 0, -1, 1);
+ addr += PUL_STACK_SIZE;
+ elf_rel_set_symbol(&info->rhdr, "stack", &addr, sizeof(addr));
+ /* No allocation past here in order not to overwrite the stack */
+#undef PUL_STACK_SIZE
+
+ /*
+ * Fixup ThreadPointer(r2) for purgatory.
+ * PPC32 ELF ABI expects :
+ * ThreadPointer (TP) = TCB + 0x7000
+ * We manually allocate a TCB space and set the TP
+ * accordingly.
+ */
+#define TCB_SIZE 1024
+#define TCB_TP_OFFSET 0x7000 /* PPC32 ELF ABI */
+ addr = locate_hole(info, TCB_SIZE, 0, 0,
+ ((unsigned long)-1 - TCB_TP_OFFSET),
+ 1);
+ addr += TCB_SIZE + TCB_TP_OFFSET;
+ elf_rel_set_symbol(&info->rhdr, "my_thread_ptr", &addr, sizeof(addr));
+#undef TCB_TP_OFFSET
+#undef TCB_SIZE
+
+ addr = elf_rel_get_addr(&info->rhdr, "purgatory_start");
+ info->entry = (void *)addr;
+
+out2:
+ free(cmdline_buf);
+out:
+ free(crash_cmdline);
+ if (!tmp_cmdline)
+ free(command_line);
+ if (error_msg)
+ die("%s", error_msg);
+ return ret;
+}
+
+int uImage_ppc_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ struct Image_info img;
+ int ret;
+
+ ret = uImage_load(buf, len, &img);
+ if (ret)
+ return ret;
+
+ return ppc_load_bare_bits(argc, argv, img.buf, img.len, info,
+ img.base, img.ep);
+}
diff --git a/kexec/arch/ppc/libfdt-wrapper.c b/kexec/arch/ppc/libfdt-wrapper.c
new file mode 100644
index 0000000..ef355d0
--- /dev/null
+++ b/kexec/arch/ppc/libfdt-wrapper.c
@@ -0,0 +1,189 @@
+/*
+ * This file does the necessary interface mapping between the bootwrapper
+ * device tree operations and the interface provided by shared source
+ * files flatdevicetree.[ch].
+ *
+ * Copyright 2007 David Gibson, IBM Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <page.h>
+#include <libfdt.h>
+#include "ops.h"
+#include "../../kexec.h"
+
+#define BAD_ERROR(err) (((err) < 0) \
+ && ((err) != -FDT_ERR_NOTFOUND) \
+ && ((err) != -FDT_ERR_EXISTS))
+
+#define check_err(err) \
+ ({ \
+ if (BAD_ERROR(err) || ((err < 0) && kexec_debug)) \
+ printf("%s():%d %s\n\r", __func__, __LINE__, \
+ fdt_strerror(err)); \
+ if (BAD_ERROR(err)) \
+ exit(1); \
+ (err < 0) ? -1 : 0; \
+ })
+
+#define offset_devp(off) \
+ ({ \
+ int _offset = (off); \
+ check_err(_offset) ? NULL : (void *)(_offset+1); \
+ })
+
+#define devp_offset_find(devp) (((int)(devp))-1)
+#define devp_offset(devp) (devp ? ((int)(devp))-1 : 0)
+
+static void *fdt;
+static void *buf; /* = NULL */
+struct dt_ops dt_ops;
+
+#define EXPAND_GRANULARITY 1024
+
+static void expand_buf(int minexpand)
+{
+ int size = fdt_totalsize(fdt);
+ int rc;
+
+ size = _ALIGN(size + minexpand, EXPAND_GRANULARITY);
+ buf = realloc(buf, size);
+ if (!buf)
+ die("Couldn't find %d bytes to expand device tree\n\r", size);
+ rc = fdt_open_into(fdt, buf, size);
+ if (rc != 0)
+ die("Couldn't expand fdt into new buffer: %s\n\r",
+ fdt_strerror(rc));
+
+ fdt = buf;
+}
+
+static void *fdt_wrapper_finddevice(const char *path)
+{
+ return offset_devp(fdt_path_offset(fdt, path));
+}
+
+static int fdt_wrapper_getprop(const void *devp, const char *name,
+ void *buf, const int buflen)
+{
+ const void *p;
+ int len;
+
+ p = fdt_getprop(fdt, devp_offset(devp), name, &len);
+ if (!p)
+ return check_err(len);
+ memcpy(buf, p, min(len, buflen));
+ return len;
+}
+
+static int fdt_wrapper_setprop(const void *devp, const char *name,
+ const void *buf, const int len)
+{
+ int rc;
+
+ rc = fdt_setprop(fdt, devp_offset(devp), name, buf, len);
+ if (rc == -FDT_ERR_NOSPACE) {
+ expand_buf(len + 16);
+ rc = fdt_setprop(fdt, devp_offset(devp), name, buf, len);
+ }
+
+ return check_err(rc);
+}
+
+static void *fdt_wrapper_get_parent(const void *devp)
+{
+ return offset_devp(fdt_parent_offset(fdt, devp_offset(devp)));
+}
+
+static void *fdt_wrapper_create_node(const void *devp, const char *name)
+{
+ int offset;
+
+ offset = fdt_add_subnode(fdt, devp_offset(devp), name);
+ if (offset == -FDT_ERR_NOSPACE) {
+ expand_buf(strlen(name) + 16);
+ offset = fdt_add_subnode(fdt, devp_offset(devp), name);
+ }
+
+ return offset_devp(offset);
+}
+
+static void *fdt_wrapper_find_node_by_prop_value(const void *prev,
+ const char *name,
+ const char *val,
+ int len)
+{
+ int offset = fdt_node_offset_by_prop_value(fdt, devp_offset_find(prev),
+ name, val, len);
+ return offset_devp(offset);
+}
+
+static void *fdt_wrapper_find_node_by_compatible(const void *prev,
+ const char *val)
+{
+ int offset = fdt_node_offset_by_compatible(fdt, devp_offset_find(prev),
+ val);
+ return offset_devp(offset);
+}
+
+static char *fdt_wrapper_get_path(const void *devp, char *buf, int len)
+{
+ int rc;
+
+ rc = fdt_get_path(fdt, devp_offset(devp), buf, len);
+ if (check_err(rc))
+ return NULL;
+ return buf;
+}
+
+static unsigned long fdt_wrapper_finalize(void)
+{
+ int rc;
+
+ rc = fdt_pack(fdt);
+ if (rc != 0)
+ die("Couldn't pack flat tree: %s\n\r",
+ fdt_strerror(rc));
+ return (unsigned long)fdt;
+}
+
+void fdt_init(void *blob)
+{
+ int err;
+ int bufsize;
+
+ dt_ops.finddevice = fdt_wrapper_finddevice;
+ dt_ops.getprop = fdt_wrapper_getprop;
+ dt_ops.setprop = fdt_wrapper_setprop;
+ dt_ops.get_parent = fdt_wrapper_get_parent;
+ dt_ops.create_node = fdt_wrapper_create_node;
+ dt_ops.find_node_by_prop_value = fdt_wrapper_find_node_by_prop_value;
+ dt_ops.find_node_by_compatible = fdt_wrapper_find_node_by_compatible;
+ dt_ops.get_path = fdt_wrapper_get_path;
+ dt_ops.finalize = fdt_wrapper_finalize;
+
+ /* Make sure the dt blob is the right version and so forth */
+ fdt = blob;
+ bufsize = fdt_totalsize(fdt);
+
+ err = fdt_open_into(fdt, fdt, bufsize);
+ if (err != 0)
+ die("fdt_init(): %s\n\r", fdt_strerror(err));
+}
diff --git a/kexec/arch/ppc/ops.h b/kexec/arch/ppc/ops.h
new file mode 100644
index 0000000..5e7a070
--- /dev/null
+++ b/kexec/arch/ppc/ops.h
@@ -0,0 +1,147 @@
+/*
+ * Global definition of all the bootwrapper operations.
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2006 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#ifndef _PPC_BOOT_OPS_H_
+#define _PPC_BOOT_OPS_H_
+#include "types.h"
+
+#define MAX_PATH_LEN 256
+#define MAX_PROP_LEN 256 /* What should this be? */
+
+typedef void (*kernel_entry_t)(unsigned long r3, unsigned long r4, void *r5);
+
+/* Device Tree operations */
+struct dt_ops {
+ void * (*finddevice)(const char *name);
+ int (*getprop)(const void *phandle, const char *name, void *buf,
+ const int buflen);
+ int (*setprop)(const void *phandle, const char *name,
+ const void *buf, const int buflen);
+ void *(*get_parent)(const void *phandle);
+ /* The node must not already exist. */
+ void *(*create_node)(const void *parent, const char *name);
+ void *(*find_node_by_prop_value)(const void *prev,
+ const char *propname,
+ const char *propval, int proplen);
+ void *(*find_node_by_compatible)(const void *prev,
+ const char *compat);
+ unsigned long (*finalize)(void);
+ char *(*get_path)(const void *phandle, char *buf, int len);
+};
+extern struct dt_ops dt_ops;
+
+void fdt_init(void *blob);
+extern void flush_cache(void *, unsigned long);
+int dt_xlate_reg(void *node, int res, unsigned long *addr, unsigned long *size);
+int dt_xlate_addr(void *node, u32 *buf, int buflen, unsigned long *xlated_addr);
+int dt_is_compatible(void *node, const char *compat);
+void dt_get_reg_format(void *node, u32 *naddr, u32 *nsize);
+int dt_get_virtual_reg(void *node, void **addr, int nres);
+
+static inline void *finddevice(const char *name)
+{
+ return (dt_ops.finddevice) ? dt_ops.finddevice(name) : NULL;
+}
+
+static inline int getprop(void *devp, const char *name, void *buf, int buflen)
+{
+ return (dt_ops.getprop) ? dt_ops.getprop(devp, name, buf, buflen) : -1;
+}
+
+static inline int setprop(void *devp, const char *name,
+ const void *buf, int buflen)
+{
+ return (dt_ops.setprop) ? dt_ops.setprop(devp, name, buf, buflen) : -1;
+}
+#define setprop_val(devp, name, val) \
+ do { \
+ typeof(val) x = (val); \
+ setprop((devp), (name), &x, sizeof(x)); \
+ } while (0)
+
+static inline int setprop_str(void *devp, const char *name, const char *buf)
+{
+ if (dt_ops.setprop)
+ return dt_ops.setprop(devp, name, buf, strlen(buf) + 1);
+
+ return -1;
+}
+
+static inline void *get_parent(const char *devp)
+{
+ return dt_ops.get_parent ? dt_ops.get_parent(devp) : NULL;
+}
+
+static inline void *create_node(const void *parent, const char *name)
+{
+ return dt_ops.create_node ? dt_ops.create_node(parent, name) : NULL;
+}
+
+
+static inline void *find_node_by_prop_value(const void *prev,
+ const char *propname,
+ const char *propval, int proplen)
+{
+ if (dt_ops.find_node_by_prop_value)
+ return dt_ops.find_node_by_prop_value(prev, propname,
+ propval, proplen);
+
+ return NULL;
+}
+
+static inline void *find_node_by_prop_value_str(const void *prev,
+ const char *propname,
+ const char *propval)
+{
+ return find_node_by_prop_value(prev, propname, propval,
+ strlen(propval) + 1);
+}
+
+static inline void *find_node_by_devtype(const void *prev,
+ const char *type)
+{
+ return find_node_by_prop_value_str(prev, "device_type", type);
+}
+
+static inline void *find_node_by_alias(const char *alias)
+{
+ void *devp = finddevice("/aliases");
+
+ if (devp) {
+ char path[MAX_PATH_LEN];
+ if (getprop(devp, alias, path, MAX_PATH_LEN) > 0)
+ return finddevice(path);
+ }
+
+ return NULL;
+}
+
+static inline void *find_node_by_compatible(const void *prev,
+ const char *compat)
+{
+ if (dt_ops.find_node_by_compatible)
+ return dt_ops.find_node_by_compatible(prev, compat);
+
+ return NULL;
+}
+
+#define dt_fixup_mac_addresses(...) \
+ __dt_fixup_mac_addresses(0, __VA_ARGS__, NULL)
+
+
+static inline char *get_path(const void *phandle, char *buf, int len)
+{
+ if (dt_ops.get_path)
+ return dt_ops.get_path(phandle, buf, len);
+
+ return NULL;
+}
+
+#endif /* _PPC_BOOT_OPS_H_ */
diff --git a/kexec/arch/ppc/ppc-setup-dol.S b/kexec/arch/ppc/ppc-setup-dol.S
new file mode 100644
index 0000000..17169bd
--- /dev/null
+++ b/kexec/arch/ppc/ppc-setup-dol.S
@@ -0,0 +1,174 @@
+/*
+ * ppc-setup-dol.S - setup glue for Nintendo's GameCube
+ * Copyright (C) 2004 Albert Herranz
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include "ppc_asm.h"
+
+ .data
+ .globl setup_dol_start
+setup_dol_start:
+
+ /* Try to reproduce the GameCube "native" environment */
+
+ /* Setup BATs */
+ isync
+ li r8, 0
+ mtspr DBAT0U, r8
+ mtspr DBAT0L, r8
+ mtspr DBAT1U, r8
+ mtspr DBAT1L, r8
+ mtspr DBAT2U, r8
+ mtspr DBAT2L, r8
+ mtspr DBAT3U, r8
+ mtspr DBAT3L, r8
+ mtspr IBAT0U, r8
+ mtspr IBAT0L, r8
+ mtspr IBAT1U, r8
+ mtspr IBAT1L, r8
+ mtspr IBAT2U, r8
+ mtspr IBAT2L, r8
+ mtspr IBAT3U, r8
+ mtspr IBAT3L, r8
+
+ /*
+ * Memory Map
+ * start end size description
+ * 0x80000000 0x817fffff 24MB RAM, uncached
+ * 0xc0000000 0xc17fffff 24MB RAM, cached
+ * 0xc8000000 0xc81fffff 2MB Embedded Framebuffer
+ * 0xcc000000 Hardware registers
+ * 0xe0000000 Layer 2 transfer cache ??? 256KB
+ *
+ */
+
+ isync
+ lis r8, 0x8000 /* IBAT0,DBAT0 for first 16Mbytes */
+ ori r8, r8, 0x01ff /* 16MB */
+ mtspr IBAT0U, r8
+ mtspr DBAT0U, r8
+ li r8, 0x0002 /* rw */
+ mtspr IBAT0L, r8
+ mtspr DBAT0L, r8
+
+ lis r8, 0xc000 /* DBAT1 for IO mem */
+ ori r8, r8, 0x1fff /* 256MB */
+ mtspr DBAT1U, r8
+ li r8, 0x002a /* uncached, guarded ,rw */
+ mtspr DBAT1L, r8
+
+ lis r8, 0x8100 /* IBAT2,DBAT2 for next 8Mbytes */
+ ori r8, r8, 0x00ff /* 8MB */
+ mtspr IBAT2U, r8
+ mtspr DBAT2U, r8
+ lis r8, 0x0100
+ ori r8, r8, 0x0002 /* rw */
+ mtspr IBAT2L, r8
+ mtspr DBAT2L, r8
+
+ lis r8, 0xe000 /* DBAT3 for layer 2 transfer cache ??? */
+ ori r8, r8, 0x01fe /* 16MB ??? */
+ mtspr DBAT3U, r8
+ lis r8, 0xe000
+ ori r8, r8, 0x0002 /* rw */
+ mtspr DBAT3L, r8
+
+ sync
+ isync
+
+/* AFAIK, this is not strictly needed, although seems sane */
+#if 1
+ li r9, 0
+
+ /* page table pointer */
+ sync
+ mtspr SDR1, r9
+
+ /* segment registers */
+ li r8, 16
+ mtctr r8
+ li r8, 0
+1: mtsrin r9, r8 /* zero */
+ sync
+ addis r8,r8,0x1000 /* next register */
+ bdnz 1b
+#endif
+
+ /* switch MMU on and continue */
+ RELOC_SYM(1f)
+ mfmsr r0
+ ori r0, r0, MSR_RI|MSR_ME|MSR_DR|MSR_IR
+ mtspr SRR1, r0
+ oris r3, r3, 0x8000 /* adjust text address */
+ mtspr SRR0, r3
+ oris r1, r1, 0x8000 /* adjust stack */
+ sync
+ rfi
+
+1:
+ /* from now on we run in a DOL-like environment */
+
+
+ /* first, sanitize the hardware a little bit */
+ /* although seems to be not needed in the general case */
+
+#if 1
+ /* audio */
+ lis r8, 0xcc00 /* io mem */
+ li r9, 0
+ sth r9, 0x5036(r8) /* stop audio sample */
+ stw r9, 0x6c00(r8) /* stop streaming */
+ stw r9, 0x6c04(r8) /* mute */
+
+ /* video */
+ mfspr r8, 920 /* spr920 = HID2 */
+ rlwinm r8, r8, 0, 4, 2 /* stop GX FIFO, and more */
+ mtspr 920, r8
+
+ /* exi */
+ lis r8, 0xcc00 /* io mem */
+1: lwz r9,0x680c(r8) /* wait for dma transfer to complete */
+ andi. r9,r9,1
+ bne+ 1b
+ stw r9,0x6800(r8) /* disable exi interrupts */
+ addi r8,r8,0x14 /* next channel */
+ andi. r9,r8,0x40 /* XXX 4 channels? */
+ beq+ 1b
+
+ /* pic */
+ lis r8, 0xcc00 /* io mem */
+ li r9, 0
+ stw r9, 0x3004(r8) /* mask all interrupts */
+ stw r9, 0x3000(r8) /* clear interrupt cause */
+
+ /* invalidate L1 data and instructions caches */
+ mfspr r8, HID0
+ ori r8, r8, HID0_ICFI|HID0_DCI
+ mtspr HID0, r8
+#endif
+
+ /* jump to our entry point */
+ RELOC_SYM(setup_dol_regs)
+ mr r9, r3
+ lwz r5, spr8 - setup_dol_regs(r9)
+
+ mtlr r5
+ blr
+
+ .balign 4
+ .globl setup_dol_regs
+setup_dol_regs:
+spr8: .long 0x00000000
+
+ .balign 4
+//#include "isobel_reloc_debug_console.s"
+
+setup_dol_end:
+
+ .globl setup_dol_size
+setup_dol_size:
+ .long setup_dol_end - setup_dol_start
+
diff --git a/kexec/arch/ppc/ppc-setup-simple.S b/kexec/arch/ppc/ppc-setup-simple.S
new file mode 100644
index 0000000..1317a8d
--- /dev/null
+++ b/kexec/arch/ppc/ppc-setup-simple.S
@@ -0,0 +1,39 @@
+/*
+ * ppc-setup-simple.S - (hopefully) setup for simple embedded platforms
+ * Copyright (C) 2004 Albert Herranz
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+/*
+ * Only suitable for platforms booting with MMU turned off.
+ * -- Albert Herranz
+ */
+
+#include "ppc_asm.h"
+
+ .data
+ .globl setup_simple_start
+setup_simple_start:
+
+ /* should perform here any required setup */
+
+ RELOC_SYM(setup_simple_regs)
+ mr r9, r3
+ lwz r5, spr8 - setup_simple_regs(r9)
+
+ mtlr r5
+ blr
+
+ .balign 4
+ .globl setup_simple_regs
+setup_simple_regs:
+spr8: .long 0x00000000
+
+setup_simple_end:
+
+ .globl setup_simple_size
+setup_simple_size:
+ .long setup_simple_end - setup_simple_start
+
diff --git a/kexec/arch/ppc/ppc_asm.h b/kexec/arch/ppc/ppc_asm.h
new file mode 100644
index 0000000..36503a9
--- /dev/null
+++ b/kexec/arch/ppc/ppc_asm.h
@@ -0,0 +1,506 @@
+/*
+ * ppc_asm.h - mainly bits stolen from Linux kernel asm/reg.h and asm/ppc_asm.h
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+/* Condition Register Bit Fields */
+
+#define cr0 0
+#define cr1 1
+#define cr2 2
+#define cr3 3
+#define cr4 4
+#define cr5 5
+#define cr6 6
+#define cr7 7
+
+
+/* General Purpose Registers (GPRs) */
+
+#define r0 0
+#define r1 1
+#define r2 2
+#define r3 3
+#define r4 4
+#define r5 5
+#define r6 6
+#define r7 7
+#define r8 8
+#define r9 9
+#define r10 10
+#define r11 11
+#define r12 12
+#define r13 13
+#define r14 14
+#define r15 15
+#define r16 16
+#define r17 17
+#define r18 18
+#define r19 19
+#define r20 20
+#define r21 21
+#define r22 22
+#define r23 23
+#define r24 24
+#define r25 25
+#define r26 26
+#define r27 27
+#define r28 28
+#define r29 29
+#define r30 30
+#define r31 31
+
+/* Machine State Register (MSR) Fields */
+#define MSR_SF (1<<63)
+#define MSR_ISF (1<<61)
+#define MSR_VEC (1<<25) /* Enable AltiVec */
+#define MSR_POW (1<<18) /* Enable Power Management */
+#define MSR_WE (1<<18) /* Wait State Enable */
+#define MSR_TGPR (1<<17) /* TLB Update registers in use */
+#define MSR_CE (1<<17) /* Critical Interrupt Enable */
+#define MSR_ILE (1<<16) /* Interrupt Little Endian */
+#define MSR_EE (1<<15) /* External Interrupt Enable */
+#define MSR_PR (1<<14) /* Problem State / Privilege Level */
+#define MSR_FP (1<<13) /* Floating Point enable */
+#define MSR_ME (1<<12) /* Machine Check Enable */
+#define MSR_FE0 (1<<11) /* Floating Exception mode 0 */
+#define MSR_SE (1<<10) /* Single Step */
+#define MSR_BE (1<<9) /* Branch Trace */
+#define MSR_DE (1<<9) /* Debug Exception Enable */
+#define MSR_FE1 (1<<8) /* Floating Exception mode 1 */
+#define MSR_IP (1<<6) /* Exception prefix 0x000/0xFFF */
+#define MSR_IR (1<<5) /* Instruction Relocate */
+#define MSR_DR (1<<4) /* Data Relocate */
+#define MSR_PE (1<<3) /* Protection Enable */
+#define MSR_PX (1<<2) /* Protection Exclusive Mode */
+#define MSR_RI (1<<1) /* Recoverable Exception */
+#define MSR_LE (1<<0) /* Little Endian */
+
+/* Special Purpose Registers (SPRNs)*/
+#define SPRN_CTR 0x009 /* Count Register */
+#define SPRN_DABR 0x3F5 /* Data Address Breakpoint Register */
+#define SPRN_DAR 0x013 /* Data Address Register */
+#define SPRN_TBRL 0x10C /* Time Base Read Lower Register (user, R/O) */
+#define SPRN_TBRU 0x10D /* Time Base Read Upper Register (user, R/O) */
+#define SPRN_TBWL 0x11C /* Time Base Lower Register (super, R/W) */
+#define SPRN_TBWU 0x11D /* Time Base Upper Register (super, R/W) */
+#define SPRN_HIOR 0x137 /* 970 Hypervisor interrupt offset */
+#define SPRN_DBAT0L 0x219 /* Data BAT 0 Lower Register */
+#define SPRN_DBAT0U 0x218 /* Data BAT 0 Upper Register */
+#define SPRN_DBAT1L 0x21B /* Data BAT 1 Lower Register */
+#define SPRN_DBAT1U 0x21A /* Data BAT 1 Upper Register */
+#define SPRN_DBAT2L 0x21D /* Data BAT 2 Lower Register */
+#define SPRN_DBAT2U 0x21C /* Data BAT 2 Upper Register */
+#define SPRN_DBAT3L 0x21F /* Data BAT 3 Lower Register */
+#define SPRN_DBAT3U 0x21E /* Data BAT 3 Upper Register */
+#define SPRN_DBAT4L 0x239 /* Data BAT 4 Lower Register */
+#define SPRN_DBAT4U 0x238 /* Data BAT 4 Upper Register */
+#define SPRN_DBAT5L 0x23B /* Data BAT 5 Lower Register */
+#define SPRN_DBAT5U 0x23A /* Data BAT 5 Upper Register */
+#define SPRN_DBAT6L 0x23D /* Data BAT 6 Lower Register */
+#define SPRN_DBAT6U 0x23C /* Data BAT 6 Upper Register */
+#define SPRN_DBAT7L 0x23F /* Data BAT 7 Lower Register */
+#define SPRN_DBAT7U 0x23E /* Data BAT 7 Upper Register */
+
+#define SPRN_DEC 0x016 /* Decrement Register */
+#define SPRN_DER 0x095 /* Debug Enable Regsiter */
+#define DER_RSTE 0x40000000 /* Reset Interrupt */
+#define DER_CHSTPE 0x20000000 /* Check Stop */
+#define DER_MCIE 0x10000000 /* Machine Check Interrupt */
+#define DER_EXTIE 0x02000000 /* External Interrupt */
+#define DER_ALIE 0x01000000 /* Alignment Interrupt */
+#define DER_PRIE 0x00800000 /* Program Interrupt */
+#define DER_FPUVIE 0x00400000 /* FP Unavailable Interrupt */
+#define DER_DECIE 0x00200000 /* Decrementer Interrupt */
+#define DER_SYSIE 0x00040000 /* System Call Interrupt */
+#define DER_TRE 0x00020000 /* Trace Interrupt */
+#define DER_SEIE 0x00004000 /* FP SW Emulation Interrupt */
+#define DER_ITLBMSE 0x00002000 /* Imp. Spec. Instruction TLB Miss */
+#define DER_ITLBERE 0x00001000 /* Imp. Spec. Instruction TLB Error */
+#define DER_DTLBMSE 0x00000800 /* Imp. Spec. Data TLB Miss */
+#define DER_DTLBERE 0x00000400 /* Imp. Spec. Data TLB Error */
+#define DER_LBRKE 0x00000008 /* Load/Store Breakpoint Interrupt */
+#define DER_IBRKE 0x00000004 /* Instruction Breakpoint Interrupt */
+#define DER_EBRKE 0x00000002 /* External Breakpoint Interrupt */
+#define DER_DPIE 0x00000001 /* Dev. Port Nonmaskable Request */
+#define SPRN_DMISS 0x3D0 /* Data TLB Miss Register */
+#define SPRN_DSISR 0x012 /* Data Storage Interrupt Status Register */
+#define SPRN_EAR 0x11A /* External Address Register */
+#define SPRN_HASH1 0x3D2 /* Primary Hash Address Register */
+#define SPRN_HASH2 0x3D3 /* Secondary Hash Address Resgister */
+#define SPRN_HID0 0x3F0 /* Hardware Implementation Register 0 */
+#define HID0_EMCP (1<<31) /* Enable Machine Check pin */
+#define HID0_EBA (1<<29) /* Enable Bus Address Parity */
+#define HID0_EBD (1<<28) /* Enable Bus Data Parity */
+#define HID0_SBCLK (1<<27)
+#define HID0_EICE (1<<26)
+#define HID0_TBEN (1<<26) /* Timebase enable - 745x */
+#define HID0_ECLK (1<<25)
+#define HID0_PAR (1<<24)
+#define HID0_STEN (1<<24) /* Software table search enable - 745x */
+#define HID0_HIGH_BAT (1<<23) /* Enable high BATs - 7455 */
+#define HID0_DOZE (1<<23)
+#define HID0_NAP (1<<22)
+#define HID0_SLEEP (1<<21)
+#define HID0_DPM (1<<20)
+#define HID0_BHTCLR (1<<18) /* Clear branch history table - 7450 */
+#define HID0_XAEN (1<<17) /* Extended addressing enable - 7450 */
+#define HID0_NHR (1<<16) /* Not hard reset (software bit-7450)*/
+#define HID0_ICE (1<<15) /* Instruction Cache Enable */
+#define HID0_DCE (1<<14) /* Data Cache Enable */
+#define HID0_ILOCK (1<<13) /* Instruction Cache Lock */
+#define HID0_DLOCK (1<<12) /* Data Cache Lock */
+#define HID0_ICFI (1<<11) /* Instr. Cache Flash Invalidate */
+#define HID0_DCI (1<<10) /* Data Cache Invalidate */
+#define HID0_SPD (1<<9) /* Speculative disable */
+#define HID0_SGE (1<<7) /* Store Gathering Enable */
+#define HID0_SIED (1<<7) /* Serial Instr. Execution [Disable] */
+#define HID0_DFCA (1<<6) /* Data Cache Flush Assist */
+#define HID0_LRSTK (1<<4) /* Link register stack - 745x */
+#define HID0_BTIC (1<<5) /* Branch Target Instr Cache Enable */
+#define HID0_ABE (1<<3) /* Address Broadcast Enable */
+#define HID0_FOLD (1<<3) /* Branch Folding enable - 745x */
+#define HID0_BHTE (1<<2) /* Branch History Table Enable */
+#define HID0_BTCD (1<<1) /* Branch target cache disable */
+#define HID0_NOPDST (1<<1) /* No-op dst, dstt, etc. instr. */
+#define HID0_NOPTI (1<<0) /* No-op dcbt and dcbst instr. */
+
+#define SPRN_HID1 0x3F1 /* Hardware Implementation Register 1 */
+#define HID1_EMCP (1<<31) /* 7450 Machine Check Pin Enable */
+#define HID1_PC0 (1<<16) /* 7450 PLL_CFG[0] */
+#define HID1_PC1 (1<<15) /* 7450 PLL_CFG[1] */
+#define HID1_PC2 (1<<14) /* 7450 PLL_CFG[2] */
+#define HID1_PC3 (1<<13) /* 7450 PLL_CFG[3] */
+#define HID1_SYNCBE (1<<11) /* 7450 ABE for sync, eieio */
+#define HID1_ABE (1<<10) /* 7450 Address Broadcast Enable */
+#define SPRN_HID2 0x3F8 /* Hardware Implementation Register 2 */
+#define SPRN_IABR 0x3F2 /* Instruction Address Breakpoint Register */
+#define SPRN_HID4 0x3F4 /* 970 HID4 */
+#define SPRN_HID5 0x3F6 /* 970 HID5 */
+#if !defined(SPRN_IAC1) && !defined(SPRN_IAC2)
+#define SPRN_IAC1 0x3F4 /* Instruction Address Compare 1 */
+#define SPRN_IAC2 0x3F5 /* Instruction Address Compare 2 */
+#endif
+#define SPRN_IBAT0L 0x211 /* Instruction BAT 0 Lower Register */
+#define SPRN_IBAT0U 0x210 /* Instruction BAT 0 Upper Register */
+#define SPRN_IBAT1L 0x213 /* Instruction BAT 1 Lower Register */
+#define SPRN_IBAT1U 0x212 /* Instruction BAT 1 Upper Register */
+#define SPRN_IBAT2L 0x215 /* Instruction BAT 2 Lower Register */
+#define SPRN_IBAT2U 0x214 /* Instruction BAT 2 Upper Register */
+#define SPRN_IBAT3L 0x217 /* Instruction BAT 3 Lower Register */
+#define SPRN_IBAT3U 0x216 /* Instruction BAT 3 Upper Register */
+#define SPRN_IBAT4L 0x231 /* Instruction BAT 4 Lower Register */
+#define SPRN_IBAT4U 0x230 /* Instruction BAT 4 Upper Register */
+#define SPRN_IBAT5L 0x233 /* Instruction BAT 5 Lower Register */
+#define SPRN_IBAT5U 0x232 /* Instruction BAT 5 Upper Register */
+#define SPRN_IBAT6L 0x235 /* Instruction BAT 6 Lower Register */
+#define SPRN_IBAT6U 0x234 /* Instruction BAT 6 Upper Register */
+#define SPRN_IBAT7L 0x237 /* Instruction BAT 7 Lower Register */
+#define SPRN_IBAT7U 0x236 /* Instruction BAT 7 Upper Register */
+#define SPRN_ICMP 0x3D5 /* Instruction TLB Compare Register */
+#define SPRN_ICTC 0x3FB /* Instruction Cache Throttling Control Reg */
+#define SPRN_ICTRL 0x3F3 /* 1011 7450 icache and interrupt ctrl */
+#define ICTRL_EICE 0x08000000 /* enable icache parity errs */
+#define ICTRL_EDC 0x04000000 /* enable dcache parity errs */
+#define ICTRL_EICP 0x00000100 /* enable icache par. check */
+#define SPRN_IMISS 0x3D4 /* Instruction TLB Miss Register */
+#define SPRN_IMMR 0x27E /* Internal Memory Map Register */
+#define SPRN_L2CR 0x3F9 /* Level 2 Cache Control Regsiter */
+#define SPRN_L2CR2 0x3f8
+#define L2CR_L2E 0x80000000 /* L2 enable */
+#define L2CR_L2PE 0x40000000 /* L2 parity enable */
+#define L2CR_L2SIZ_MASK 0x30000000 /* L2 size mask */
+#define L2CR_L2SIZ_256KB 0x10000000 /* L2 size 256KB */
+#define L2CR_L2SIZ_512KB 0x20000000 /* L2 size 512KB */
+#define L2CR_L2SIZ_1MB 0x30000000 /* L2 size 1MB */
+#define L2CR_L2CLK_MASK 0x0e000000 /* L2 clock mask */
+#define L2CR_L2CLK_DISABLED 0x00000000 /* L2 clock disabled */
+#define L2CR_L2CLK_DIV1 0x02000000 /* L2 clock / 1 */
+#define L2CR_L2CLK_DIV1_5 0x04000000 /* L2 clock / 1.5 */
+#define L2CR_L2CLK_DIV2 0x08000000 /* L2 clock / 2 */
+#define L2CR_L2CLK_DIV2_5 0x0a000000 /* L2 clock / 2.5 */
+#define L2CR_L2CLK_DIV3 0x0c000000 /* L2 clock / 3 */
+#define L2CR_L2RAM_MASK 0x01800000 /* L2 RAM type mask */
+#define L2CR_L2RAM_FLOW 0x00000000 /* L2 RAM flow through */
+#define L2CR_L2RAM_PIPE 0x01000000 /* L2 RAM pipelined */
+#define L2CR_L2RAM_PIPE_LW 0x01800000 /* L2 RAM pipelined latewr */
+#define L2CR_L2DO 0x00400000 /* L2 data only */
+#define L2CR_L2I 0x00200000 /* L2 global invalidate */
+#define L2CR_L2CTL 0x00100000 /* L2 RAM control */
+#define L2CR_L2WT 0x00080000 /* L2 write-through */
+#define L2CR_L2TS 0x00040000 /* L2 test support */
+#define L2CR_L2OH_MASK 0x00030000 /* L2 output hold mask */
+#define L2CR_L2OH_0_5 0x00000000 /* L2 output hold 0.5 ns */
+#define L2CR_L2OH_1_0 0x00010000 /* L2 output hold 1.0 ns */
+#define L2CR_L2SL 0x00008000 /* L2 DLL slow */
+#define L2CR_L2DF 0x00004000 /* L2 differential clock */
+#define L2CR_L2BYP 0x00002000 /* L2 DLL bypass */
+#define L2CR_L2IP 0x00000001 /* L2 GI in progress */
+#define SPRN_L3CR 0x3FA /* Level 3 Cache Control Regsiter */
+#define L3CR_L3E 0x80000000 /* L3 enable */
+#define L3CR_L3PE 0x40000000 /* L3 data parity enable */
+#define L3CR_L3APE 0x20000000 /* L3 addr parity enable */
+#define L3CR_L3SIZ 0x10000000 /* L3 size */
+#define L3CR_L3CLKEN 0x08000000 /* L3 clock enable */
+#define L3CR_L3RES 0x04000000 /* L3 special reserved bit */
+#define L3CR_L3CLKDIV 0x03800000 /* L3 clock divisor */
+#define L3CR_L3IO 0x00400000 /* L3 instruction only */
+#define L3CR_L3SPO 0x00040000 /* L3 sample point override */
+#define L3CR_L3CKSP 0x00030000 /* L3 clock sample point */
+#define L3CR_L3PSP 0x0000e000 /* L3 P-clock sample point */
+#define L3CR_L3REP 0x00001000 /* L3 replacement algorithm */
+#define L3CR_L3HWF 0x00000800 /* L3 hardware flush */
+#define L3CR_L3I 0x00000400 /* L3 global invalidate */
+#define L3CR_L3RT 0x00000300 /* L3 SRAM type */
+#define L3CR_L3NIRCA 0x00000080 /* L3 non-integer ratio clock adj. */
+#define L3CR_L3DO 0x00000040 /* L3 data only mode */
+#define L3CR_PMEN 0x00000004 /* L3 private memory enable */
+#define L3CR_PMSIZ 0x00000001 /* L3 private memory size */
+#define SPRN_MSSCR0 0x3f6 /* Memory Subsystem Control Register 0 */
+#define SPRN_MSSSR0 0x3f7 /* Memory Subsystem Status Register 1 */
+#define SPRN_LDSTCR 0x3f8 /* Load/Store control register */
+#define SPRN_LDSTDB 0x3f4 /* */
+#define SPRN_LR 0x008 /* Link Register */
+#define SPRN_MMCR0 0x3B8 /* Monitor Mode Control Register 0 */
+#define SPRN_MMCR1 0x3BC /* Monitor Mode Control Register 1 */
+#ifndef SPRN_PIR
+#define SPRN_PIR 0x3FF /* Processor Identification Register */
+#endif
+#define SPRN_PMC1 0x3B9 /* Performance Counter Register 1 */
+#define SPRN_PMC2 0x3BA /* Performance Counter Register 2 */
+#define SPRN_PMC3 0x3BD /* Performance Counter Register 3 */
+#define SPRN_PMC4 0x3BE /* Performance Counter Register 4 */
+#define SPRN_PTEHI 0x3D5 /* 981 7450 PTE HI word (S/W TLB load) */
+#define SPRN_PTELO 0x3D6 /* 982 7450 PTE LO word (S/W TLB load) */
+#define SPRN_PVR 0x11F /* Processor Version Register */
+#define SPRN_RPA 0x3D6 /* Required Physical Address Register */
+#define SPRN_SDA 0x3BF /* Sampled Data Address Register */
+#define SPRN_SDR1 0x019 /* MMU Hash Base Register */
+#define SPRN_SIA 0x3BB /* Sampled Instruction Address Register */
+#define SPRN_SPRG0 0x110 /* Special Purpose Register General 0 */
+#define SPRN_SPRG1 0x111 /* Special Purpose Register General 1 */
+#define SPRN_SPRG2 0x112 /* Special Purpose Register General 2 */
+#define SPRN_SPRG3 0x113 /* Special Purpose Register General 3 */
+#define SPRN_SPRG4 0x114 /* Special Purpose Register General 4 */
+#define SPRN_SPRG5 0x115 /* Special Purpose Register General 5 */
+#define SPRN_SPRG6 0x116 /* Special Purpose Register General 6 */
+#define SPRN_SPRG7 0x117 /* Special Purpose Register General 7 */
+#define SPRN_SRR0 0x01A /* Save/Restore Register 0 */
+#define SPRN_SRR1 0x01B /* Save/Restore Register 1 */
+#define SPRN_THRM1 0x3FC /* Thermal Management Register 1 */
+/* these bits were defined in inverted endian sense originally, ugh, confusing */
+#define THRM1_TIN (1 << 31)
+#define THRM1_TIV (1 << 30)
+#define THRM1_THRES(x) ((x&0x7f)<<23)
+#define THRM3_SITV(x) ((x&0x3fff)<<1)
+#define THRM1_TID (1<<2)
+#define THRM1_TIE (1<<1)
+#define THRM1_V (1<<0)
+#define SPRN_THRM2 0x3FD /* Thermal Management Register 2 */
+#define SPRN_THRM3 0x3FE /* Thermal Management Register 3 */
+#define THRM3_E (1<<0)
+#define SPRN_TLBMISS 0x3D4 /* 980 7450 TLB Miss Register */
+#define SPRN_UMMCR0 0x3A8 /* User Monitor Mode Control Register 0 */
+#define SPRN_UMMCR1 0x3AC /* User Monitor Mode Control Register 0 */
+#define SPRN_UPMC1 0x3A9 /* User Performance Counter Register 1 */
+#define SPRN_UPMC2 0x3AA /* User Performance Counter Register 2 */
+#define SPRN_UPMC3 0x3AD /* User Performance Counter Register 3 */
+#define SPRN_UPMC4 0x3AE /* User Performance Counter Register 4 */
+#define SPRN_USIA 0x3AB /* User Sampled Instruction Address Register */
+#define SPRN_VRSAVE 0x100 /* Vector Register Save Register */
+#define SPRN_XER 0x001 /* Fixed Point Exception Register */
+
+/* Bit definitions for MMCR0 and PMC1 / PMC2. */
+#define MMCR0_PMC1_CYCLES (1 << 7)
+#define MMCR0_PMC1_ICACHEMISS (5 << 7)
+#define MMCR0_PMC1_DTLB (6 << 7)
+#define MMCR0_PMC2_DCACHEMISS 0x6
+#define MMCR0_PMC2_CYCLES 0x1
+#define MMCR0_PMC2_ITLB 0x7
+#define MMCR0_PMC2_LOADMISSTIME 0x5
+
+/* Short-hand versions for a number of the above SPRNs */
+#define CTR SPRN_CTR /* Counter Register */
+#define DAR SPRN_DAR /* Data Address Register */
+#define DABR SPRN_DABR /* Data Address Breakpoint Register */
+#define DBAT0L SPRN_DBAT0L /* Data BAT 0 Lower Register */
+#define DBAT0U SPRN_DBAT0U /* Data BAT 0 Upper Register */
+#define DBAT1L SPRN_DBAT1L /* Data BAT 1 Lower Register */
+#define DBAT1U SPRN_DBAT1U /* Data BAT 1 Upper Register */
+#define DBAT2L SPRN_DBAT2L /* Data BAT 2 Lower Register */
+#define DBAT2U SPRN_DBAT2U /* Data BAT 2 Upper Register */
+#define DBAT3L SPRN_DBAT3L /* Data BAT 3 Lower Register */
+#define DBAT3U SPRN_DBAT3U /* Data BAT 3 Upper Register */
+#define DBAT4L SPRN_DBAT4L /* Data BAT 4 Lower Register */
+#define DBAT4U SPRN_DBAT4U /* Data BAT 4 Upper Register */
+#define DBAT5L SPRN_DBAT5L /* Data BAT 5 Lower Register */
+#define DBAT5U SPRN_DBAT5U /* Data BAT 5 Upper Register */
+#define DBAT6L SPRN_DBAT6L /* Data BAT 6 Lower Register */
+#define DBAT6U SPRN_DBAT6U /* Data BAT 6 Upper Register */
+#define DBAT7L SPRN_DBAT7L /* Data BAT 7 Lower Register */
+#define DBAT7U SPRN_DBAT7U /* Data BAT 7 Upper Register */
+#define DEC SPRN_DEC /* Decrement Register */
+#define DMISS SPRN_DMISS /* Data TLB Miss Register */
+#define DSISR SPRN_DSISR /* Data Storage Interrupt Status Register */
+#define EAR SPRN_EAR /* External Address Register */
+#define HASH1 SPRN_HASH1 /* Primary Hash Address Register */
+#define HASH2 SPRN_HASH2 /* Secondary Hash Address Register */
+#define HID0 SPRN_HID0 /* Hardware Implementation Register 0 */
+#define HID1 SPRN_HID1 /* Hardware Implementation Register 1 */
+#define IABR SPRN_IABR /* Instruction Address Breakpoint Register */
+#define IBAT0L SPRN_IBAT0L /* Instruction BAT 0 Lower Register */
+#define IBAT0U SPRN_IBAT0U /* Instruction BAT 0 Upper Register */
+#define IBAT1L SPRN_IBAT1L /* Instruction BAT 1 Lower Register */
+#define IBAT1U SPRN_IBAT1U /* Instruction BAT 1 Upper Register */
+#define IBAT2L SPRN_IBAT2L /* Instruction BAT 2 Lower Register */
+#define IBAT2U SPRN_IBAT2U /* Instruction BAT 2 Upper Register */
+#define IBAT3L SPRN_IBAT3L /* Instruction BAT 3 Lower Register */
+#define IBAT3U SPRN_IBAT3U /* Instruction BAT 3 Upper Register */
+#define IBAT4L SPRN_IBAT4L /* Instruction BAT 4 Lower Register */
+#define IBAT4U SPRN_IBAT4U /* Instruction BAT 4 Upper Register */
+#define IBAT5L SPRN_IBAT5L /* Instruction BAT 5 Lower Register */
+#define IBAT5U SPRN_IBAT5U /* Instruction BAT 5 Upper Register */
+#define IBAT6L SPRN_IBAT6L /* Instruction BAT 6 Lower Register */
+#define IBAT6U SPRN_IBAT6U /* Instruction BAT 6 Upper Register */
+#define IBAT7L SPRN_IBAT7L /* Instruction BAT 7 Lower Register */
+#define IBAT7U SPRN_IBAT7U /* Instruction BAT 7 Upper Register */
+#define ICMP SPRN_ICMP /* Instruction TLB Compare Register */
+#define IMISS SPRN_IMISS /* Instruction TLB Miss Register */
+#define IMMR SPRN_IMMR /* PPC 860/821 Internal Memory Map Register */
+#define L2CR SPRN_L2CR /* Classic PPC L2 cache control register */
+#define L3CR SPRN_L3CR /* PPC 745x L3 cache control register */
+#define LR SPRN_LR
+#define PVR SPRN_PVR /* Processor Version */
+#define RPA SPRN_RPA /* Required Physical Address Register */
+#define SDR1 SPRN_SDR1 /* MMU hash base register */
+#define SPR0 SPRN_SPRG0 /* Supervisor Private Registers */
+#define SPR1 SPRN_SPRG1
+#define SPR2 SPRN_SPRG2
+#define SPR3 SPRN_SPRG3
+#define SPR4 SPRN_SPRG4
+#define SPR5 SPRN_SPRG5
+#define SPR6 SPRN_SPRG6
+#define SPR7 SPRN_SPRG7
+#define SPRG0 SPRN_SPRG0
+#define SPRG1 SPRN_SPRG1
+#define SPRG2 SPRN_SPRG2
+#define SPRG3 SPRN_SPRG3
+#define SPRG4 SPRN_SPRG4
+#define SPRG5 SPRN_SPRG5
+#define SPRG6 SPRN_SPRG6
+#define SPRG7 SPRN_SPRG7
+#define SRR0 SPRN_SRR0 /* Save and Restore Register 0 */
+#define SRR1 SPRN_SRR1 /* Save and Restore Register 1 */
+#define SRR2 SPRN_SRR2 /* Save and Restore Register 2 */
+#define SRR3 SPRN_SRR3 /* Save and Restore Register 3 */
+#define ICTC SPRN_ICTC /* Instruction Cache Throttling Control Reg */
+#define THRM1 SPRN_THRM1 /* Thermal Management Register 1 */
+#define THRM2 SPRN_THRM2 /* Thermal Management Register 2 */
+#define THRM3 SPRN_THRM3 /* Thermal Management Register 3 */
+#define XER SPRN_XER
+#define TBRL SPRN_TBRL /* Time Base Read Lower Register */
+#define TBRU SPRN_TBRU /* Time Base Read Upper Register */
+#define TBWL SPRN_TBWL /* Time Base Write Lower Register */
+#define TBWU SPRN_TBWU /* Time Base Write Upper Register */
+
+/* Processor Version Register */
+
+/* Processor Version Register (PVR) field extraction */
+
+#define PVR_VER(pvr) (((pvr) >> 16) & 0xFFFF) /* Version field */
+#define PVR_REV(pvr) (((pvr) >> 0) & 0xFFFF) /* Revison field */
+
+/*
+ * IBM has further subdivided the standard PowerPC 16-bit version and
+ * revision subfields of the PVR for the PowerPC 403s into the following:
+ */
+
+#define PVR_FAM(pvr) (((pvr) >> 20) & 0xFFF) /* Family field */
+#define PVR_MEM(pvr) (((pvr) >> 16) & 0xF) /* Member field */
+#define PVR_CORE(pvr) (((pvr) >> 12) & 0xF) /* Core field */
+#define PVR_CFG(pvr) (((pvr) >> 8) & 0xF) /* Configuration field */
+#define PVR_MAJ(pvr) (((pvr) >> 4) & 0xF) /* Major revision field */
+#define PVR_MIN(pvr) (((pvr) >> 0) & 0xF) /* Minor revision field */
+
+/* Processor Version Numbers */
+
+#define PVR_403GA 0x00200000
+#define PVR_403GB 0x00200100
+#define PVR_403GC 0x00200200
+#define PVR_403GCX 0x00201400
+#define PVR_405GP 0x40110000
+#define PVR_STB03XXX 0x40310000
+#define PVR_NP405H 0x41410000
+#define PVR_NP405L 0x41610000
+#define PVR_440GP_RB 0x40120440
+#define PVR_440GP_RC1 0x40120481
+#define PVR_440GP_RC2 0x40200481
+#define PVR_440GX_RA 0x51b21850
+#define PVR_440GX_RB 0x51b21851
+#define PVR_440GX_RB1 0x51b21852
+#define PVR_601 0x00010000
+#define PVR_602 0x00050000
+#define PVR_603 0x00030000
+#define PVR_603e 0x00060000
+#define PVR_603ev 0x00070000
+#define PVR_603r 0x00071000
+#define PVR_604 0x00040000
+#define PVR_604e 0x00090000
+#define PVR_604r 0x000A0000
+#define PVR_620 0x00140000
+#define PVR_740 0x00080000
+#define PVR_750 PVR_740
+#define PVR_740P 0x10080000
+#define PVR_750P PVR_740P
+#define PVR_7400 0x000C0000
+#define PVR_7410 0x800C0000
+#define PVR_7450 0x80000000
+/*
+ * For the 8xx processors, all of them report the same PVR family for
+ * the PowerPC core. The various versions of these processors must be
+ * differentiated by the version number in the Communication Processor
+ * Module (CPM).
+ */
+#define PVR_821 0x00500000
+#define PVR_823 PVR_821
+#define PVR_850 PVR_821
+#define PVR_860 PVR_821
+#define PVR_8240 0x00810100
+#define PVR_8245 0x80811014
+#define PVR_8260 PVR_8240
+
+/* Segment Registers */
+#define SR0 0
+#define SR1 1
+#define SR2 2
+#define SR3 3
+#define SR4 4
+#define SR5 5
+#define SR6 6
+#define SR7 7
+#define SR8 8
+#define SR9 9
+#define SR10 10
+#define SR11 11
+#define SR12 12
+#define SR13 13
+#define SR14 14
+#define SR15 15
+
+
+/* returns r3 = relocated address of sym */
+/* modifies r0 */
+#define RELOC_SYM(sym) \
+ mflr r3; \
+ bl 1f; \
+1: mflr r0; \
+ mtlr r3; \
+ lis r3, 1b@ha; \
+ ori r3, r3, 1b@l; \
+ subf r0, r3, r0; \
+ lis r3, sym@ha; \
+ ori r3, r3, sym@l; \
+ add r3, r3, r0
+
diff --git a/kexec/arch/ppc64/Makefile b/kexec/arch/ppc64/Makefile
new file mode 100644
index 0000000..9caf501
--- /dev/null
+++ b/kexec/arch/ppc64/Makefile
@@ -0,0 +1,26 @@
+#
+# kexec ppc64 (linux booting linux)
+#
+include $(srcdir)/kexec/libfdt/Makefile.libfdt
+
+ppc64_KEXEC_SRCS = kexec/arch/ppc64/kexec-elf-rel-ppc64.c
+ppc64_KEXEC_SRCS += kexec/arch/ppc64/kexec-zImage-ppc64.c
+ppc64_KEXEC_SRCS += kexec/arch/ppc64/kexec-elf-ppc64.c
+ppc64_KEXEC_SRCS += kexec/arch/ppc64/kexec-ppc64.c
+ppc64_KEXEC_SRCS += kexec/arch/ppc64/crashdump-ppc64.c
+ppc64_KEXEC_SRCS += kexec/arch/ppc64/fdt.c
+ppc64_KEXEC_SRCS += $(LIBFDT_SRCS:%=kexec/libfdt/%)
+
+ppc64_ARCH_REUSE_INITRD =
+
+ppc64_FS2DT = kexec/fs2dt.c
+ppc64_FS2DT_INCLUDE = -include $(srcdir)/kexec/arch/ppc64/crashdump-ppc64.h \
+ -include $(srcdir)/kexec/arch/ppc64/kexec-ppc64.h
+
+ppc64_CPPFLAGS = -I$(srcdir)/kexec/libfdt
+
+dist += kexec/arch/ppc64/Makefile $(ppc64_KEXEC_SRCS) \
+ kexec/arch/ppc64/kexec-ppc64.h kexec/arch/ppc64/crashdump-ppc64.h \
+ kexec/arch/ppc64/include/arch/fdt.h \
+ kexec/arch/ppc64/include/arch/options.h
+
diff --git a/kexec/arch/ppc64/crashdump-ppc64.c b/kexec/arch/ppc64/crashdump-ppc64.c
new file mode 100644
index 0000000..6d47898
--- /dev/null
+++ b/kexec/arch/ppc64/crashdump-ppc64.c
@@ -0,0 +1,644 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Created by: R Sharada (sharada@in.ibm.com)
+ * Copyright (C) IBM Corporation, 2005. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <elf.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-syscall.h"
+#include "../../crashdump.h"
+#include "kexec-ppc64.h"
+#include "../../fs2dt.h"
+#include "crashdump-ppc64.h"
+
+#define DEVTREE_CRASHKERNEL_BASE "/proc/device-tree/chosen/linux,crashkernel-base"
+#define DEVTREE_CRASHKERNEL_SIZE "/proc/device-tree/chosen/linux,crashkernel-size"
+
+unsigned int num_of_lmb_sets;
+unsigned int is_dyn_mem_v2;
+uint64_t lmb_size;
+
+static struct crash_elf_info elf_info64 =
+{
+ class: ELFCLASS64,
+#if BYTE_ORDER == LITTLE_ENDIAN
+ data: ELFDATA2LSB,
+#else
+ data: ELFDATA2MSB,
+#endif
+ machine: EM_PPC64,
+ page_offset: PAGE_OFFSET,
+ lowmem_limit: MAXMEM,
+};
+
+static struct crash_elf_info elf_info32 =
+{
+ class: ELFCLASS32,
+ data: ELFDATA2MSB,
+ machine: EM_PPC64,
+ page_offset: PAGE_OFFSET,
+ lowmem_limit: MAXMEM,
+};
+
+extern struct arch_options_t arch_options;
+
+/* Stores a sorted list of RAM memory ranges for which to create elf headers.
+ * A separate program header is created for backup region
+ */
+static struct memory_range *crash_memory_range = NULL;
+
+/* Define a variable to replace the CRASH_MAX_MEMORY_RANGES macro */
+static int crash_max_memory_ranges;
+
+/*
+ * Used to save various memory ranges/regions needed for the captured
+ * kernel to boot. (lime memmap= option in other archs)
+ */
+mem_rgns_t usablemem_rgns = {0, NULL};
+
+static unsigned long long cstart, cend;
+static int memory_ranges;
+
+/*
+ * Exclude the region that lies within crashkernel and above the memory
+ * limit which is reflected by mem= kernel option.
+ */
+static void exclude_crash_region(uint64_t start, uint64_t end)
+{
+ /* If memory_limit is set then exclude the memory region above it. */
+ if (memory_limit) {
+ if (start >= memory_limit)
+ return;
+ if (end > memory_limit)
+ end = memory_limit;
+ }
+
+ if (cstart < end && cend > start) {
+ if (start < cstart && end > cend) {
+ crash_memory_range[memory_ranges].start = start;
+ crash_memory_range[memory_ranges].end = cstart;
+ crash_memory_range[memory_ranges].type = RANGE_RAM;
+ memory_ranges++;
+ crash_memory_range[memory_ranges].start = cend;
+ crash_memory_range[memory_ranges].end = end;
+ crash_memory_range[memory_ranges].type = RANGE_RAM;
+ memory_ranges++;
+ } else if (start < cstart) {
+ crash_memory_range[memory_ranges].start = start;
+ crash_memory_range[memory_ranges].end = cstart;
+ crash_memory_range[memory_ranges].type = RANGE_RAM;
+ memory_ranges++;
+ } else if (end > cend) {
+ crash_memory_range[memory_ranges].start = cend;
+ crash_memory_range[memory_ranges].end = end;
+ crash_memory_range[memory_ranges].type = RANGE_RAM;
+ memory_ranges++;
+ }
+ } else {
+ crash_memory_range[memory_ranges].start = start;
+ crash_memory_range[memory_ranges].end = end;
+ crash_memory_range[memory_ranges].type = RANGE_RAM;
+ memory_ranges++;
+ }
+}
+
+static int get_dyn_reconf_crash_memory_ranges(void)
+{
+ uint64_t start, end;
+ uint64_t startrange, endrange;
+ uint64_t size;
+ char fname[128], buf[32];
+ FILE *file;
+ unsigned int i;
+ int n;
+ uint32_t flags;
+
+ strcpy(fname, "/proc/device-tree/");
+ strcat(fname, "ibm,dynamic-reconfiguration-memory/ibm,dynamic-memory");
+ if (is_dyn_mem_v2)
+ strcat(fname, "-v2");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ return -1;
+ }
+
+ fseek(file, 4, SEEK_SET);
+ startrange = endrange = 0;
+ size = lmb_size;
+ for (i = 0; i < num_of_lmb_sets; i++) {
+ if ((n = fread(buf, 1, LMB_ENTRY_SIZE, file)) < 0) {
+ perror(fname);
+ fclose(file);
+ return -1;
+ }
+ if (memory_ranges >= (max_memory_ranges + 1)) {
+ /* No space to insert another element. */
+ fprintf(stderr,
+ "Error: Number of crash memory ranges"
+ " excedeed the max limit\n");
+ fclose(file);
+ return -1;
+ }
+
+ /*
+ * If the property is ibm,dynamic-memory-v2, the first 4 bytes
+ * tell the number of sequential LMBs in this entry.
+ */
+ if (is_dyn_mem_v2)
+ size = be32_to_cpu(((unsigned int *)buf)[0]) * lmb_size;
+
+ start = be64_to_cpu(*((uint64_t *)&buf[DRCONF_ADDR]));
+ end = start + size;
+ if (start == 0 && end >= (BACKUP_SRC_END + 1))
+ start = BACKUP_SRC_END + 1;
+
+ flags = be32_to_cpu((*((uint32_t *)&buf[DRCONF_FLAGS])));
+ /* skip this block if the reserved bit is set in flags (0x80)
+ or if the block is not assigned to this partition (0x8) */
+ if ((flags & 0x80) || !(flags & 0x8))
+ continue;
+
+ if (start != endrange) {
+ if (startrange != endrange)
+ exclude_crash_region(startrange, endrange);
+ startrange = start;
+ }
+ endrange = end;
+ }
+ if (startrange != endrange)
+ exclude_crash_region(startrange, endrange);
+
+ fclose(file);
+ return 0;
+}
+
+/*
+ * For a given memory node, check if it is mapped to system RAM or
+ * to onboard memory on accelerator device like GPU card or such.
+ */
+static int is_coherent_device_mem(const char *fname)
+{
+ char fpath[PATH_LEN];
+ char buf[32];
+ DIR *dmem;
+ FILE *file;
+ struct dirent *mentry;
+ int cnt, ret = 0;
+
+ strcpy(fpath, fname);
+ if ((dmem = opendir(fpath)) == NULL) {
+ perror(fpath);
+ return -1;
+ }
+
+ while ((mentry = readdir(dmem)) != NULL) {
+ if (strcmp(mentry->d_name, "compatible"))
+ continue;
+
+ strcat(fpath, "/compatible");
+ if ((file = fopen(fpath, "r")) == NULL) {
+ perror(fpath);
+ ret = -1;
+ break;
+ }
+ if ((cnt = fread(buf, 1, 32, file)) < 0) {
+ perror(fpath);
+ fclose(file);
+ ret = -1;
+ break;
+ }
+ if (!strncmp(buf, "ibm,coherent-device-memory", 26)) {
+ fclose(file);
+ ret = 1;
+ break;
+ }
+ fclose(file);
+ }
+
+ closedir(dmem);
+ return ret;
+}
+
+
+/* Reads the appropriate file and retrieves the SYSTEM RAM regions for whom to
+ * create Elf headers. Keeping it separate from get_memory_ranges() as
+ * requirements are different in the case of normal kexec and crashdumps.
+ *
+ * Normal kexec needs to look at all of available physical memory irrespective
+ * of the fact how much of it is being used by currently running kernel.
+ * Crashdumps need to have access to memory regions actually being used by
+ * running kernel. Expecting a different file/data structure than /proc/iomem
+ * to look into down the line. May be something like /proc/kernelmem or may
+ * be zone data structures exported from kernel.
+ */
+static int get_crash_memory_ranges(struct memory_range **range, int *ranges)
+{
+
+ char device_tree[256] = "/proc/device-tree/";
+ char fname[PATH_LEN];
+ char buf[MAXBYTES];
+ DIR *dir, *dmem;
+ FILE *file;
+ struct dirent *dentry, *mentry;
+ int n, ret, crash_rng_len = 0;
+ unsigned long long start, end;
+ int page_size;
+
+ crash_max_memory_ranges = max_memory_ranges + 6;
+ crash_rng_len = sizeof(struct memory_range) * crash_max_memory_ranges;
+
+ crash_memory_range = (struct memory_range *) malloc(crash_rng_len);
+ if (!crash_memory_range) {
+ fprintf(stderr, "Allocation for crash memory range failed\n");
+ return -1;
+ }
+ memset(crash_memory_range, 0, crash_rng_len);
+
+ /* create a separate program header for the backup region */
+ crash_memory_range[0].start = BACKUP_SRC_START;
+ crash_memory_range[0].end = BACKUP_SRC_END + 1;
+ crash_memory_range[0].type = RANGE_RAM;
+ memory_ranges++;
+
+ if ((dir = opendir(device_tree)) == NULL) {
+ perror(device_tree);
+ goto err;
+ }
+
+ cstart = crash_base;
+ cend = crash_base + crash_size;
+
+ while ((dentry = readdir(dir)) != NULL) {
+ if (!strncmp(dentry->d_name,
+ "ibm,dynamic-reconfiguration-memory", 35)){
+ get_dyn_reconf_crash_memory_ranges();
+ continue;
+ }
+ if (strncmp(dentry->d_name, "memory@", 7) &&
+ strcmp(dentry->d_name, "memory"))
+ continue;
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+
+ ret = is_coherent_device_mem(fname);
+ if (ret == -1) {
+ closedir(dir);
+ goto err;
+ } else if (ret == 1) {
+ /*
+ * Avoid adding this memory region as it is not
+ * mapped to system RAM.
+ */
+ continue;
+ }
+
+ if ((dmem = opendir(fname)) == NULL) {
+ perror(fname);
+ closedir(dir);
+ goto err;
+ }
+ while ((mentry = readdir(dmem)) != NULL) {
+ if (strcmp(mentry->d_name, "reg"))
+ continue;
+ strcat(fname, "/reg");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(dmem);
+ closedir(dir);
+ goto err;
+ }
+ if ((n = fread(buf, 1, MAXBYTES, file)) < 0) {
+ perror(fname);
+ fclose(file);
+ closedir(dmem);
+ closedir(dir);
+ goto err;
+ }
+ if (memory_ranges >= (max_memory_ranges + 1)) {
+ /* No space to insert another element. */
+ fprintf(stderr,
+ "Error: Number of crash memory ranges"
+ " excedeed the max limit\n");
+ goto err;
+ }
+
+ start = be64_to_cpu(((unsigned long long *)buf)[0]);
+ end = start +
+ be64_to_cpu(((unsigned long long *)buf)[1]);
+ if (start == 0 && end >= (BACKUP_SRC_END + 1))
+ start = BACKUP_SRC_END + 1;
+
+ exclude_crash_region(start, end);
+ fclose(file);
+ }
+ closedir(dmem);
+ }
+ closedir(dir);
+
+ /*
+ * If RTAS region is overlapped with crashkernel, need to create ELF
+ * Program header for the overlapped memory.
+ */
+ if (crash_base < rtas_base + rtas_size &&
+ rtas_base < crash_base + crash_size) {
+ page_size = getpagesize();
+ cstart = rtas_base;
+ cend = rtas_base + rtas_size;
+ if (cstart < crash_base)
+ cstart = crash_base;
+ if (cend > crash_base + crash_size)
+ cend = crash_base + crash_size;
+ /*
+ * The rtas section created here is formed by reading rtas-base
+ * and rtas-size from /proc/device-tree/rtas. Unfortunately
+ * rtas-size is not required to be a multiple of PAGE_SIZE
+ * The remainder of the page it ends on is just garbage, and is
+ * safe to read, its just not accounted in rtas-size. Since
+ * we're creating an elf section here though, lets round it up
+ * to the next page size boundary though, so makedumpfile can
+ * read it safely without going south on us.
+ */
+ cend = _ALIGN(cend, page_size);
+
+ crash_memory_range[memory_ranges].start = cstart;
+ crash_memory_range[memory_ranges++].end = cend;
+ }
+
+ /*
+ * If OPAL region is overlapped with crashkernel, need to create ELF
+ * Program header for the overlapped memory.
+ */
+ if (crash_base < opal_base + opal_size &&
+ opal_base < crash_base + crash_size) {
+ page_size = getpagesize();
+ cstart = opal_base;
+ cend = opal_base + opal_size;
+ if (cstart < crash_base)
+ cstart = crash_base;
+ if (cend > crash_base + crash_size)
+ cend = crash_base + crash_size;
+ /*
+ * The opal section created here is formed by reading opal-base
+ * and opal-size from /proc/device-tree/ibm,opal. Unfortunately
+ * opal-size is not required to be a multiple of PAGE_SIZE
+ * The remainder of the page it ends on is just garbage, and is
+ * safe to read, its just not accounted in opal-size. Since
+ * we're creating an elf section here though, lets round it up
+ * to the next page size boundary though, so makedumpfile can
+ * read it safely without going south on us.
+ */
+ cend = _ALIGN(cend, page_size);
+
+ crash_memory_range[memory_ranges].start = cstart;
+ crash_memory_range[memory_ranges++].end = cend;
+ }
+ *range = crash_memory_range;
+ *ranges = memory_ranges;
+
+ int j;
+ dbgprintf("CRASH MEMORY RANGES\n");
+ for(j = 0; j < *ranges; j++) {
+ start = crash_memory_range[j].start;
+ end = crash_memory_range[j].end;
+ dbgprintf("%016Lx-%016Lx\n", start, end);
+ }
+
+ return 0;
+
+err:
+ if (crash_memory_range)
+ free(crash_memory_range);
+ return -1;
+}
+
+static int add_cmdline_param(char *cmdline, uint64_t addr, char *cmdstr,
+ char *byte)
+{
+ int cmdline_size, cmdlen, len, align = 1024;
+ char str[COMMAND_LINE_SIZE], *ptr;
+
+ /* Passing in =xxxK / =xxxM format. Saves space required in cmdline.*/
+ switch (byte[0]) {
+ case 'K':
+ if (addr%align)
+ return -1;
+ addr = addr/align;
+ break;
+ case 'M':
+ addr = addr/(align *align);
+ break;
+ }
+ ptr = str;
+ strcpy(str, cmdstr);
+ ptr += strlen(str);
+ ultoa(addr, ptr);
+ strcat(str, byte);
+ len = strlen(str);
+ cmdlen = strlen(cmdline) + len;
+ cmdline_size = COMMAND_LINE_SIZE;
+ if (cmdlen > (cmdline_size - 1))
+ die("Command line overflow\n");
+ strcat(cmdline, str);
+ dbgprintf("Command line after adding elfcorehdr: %s\n", cmdline);
+ return 0;
+}
+
+/* Loads additional segments in case of a panic kernel is being loaded.
+ * One segment for backup region, another segment for storing elf headers
+ * for crash memory image.
+ */
+int load_crashdump_segments(struct kexec_info *info, char* mod_cmdline,
+ uint64_t max_addr, unsigned long min_base)
+{
+ void *tmp;
+ unsigned long sz;
+ uint64_t elfcorehdr;
+ int nr_ranges, align = 1024, i;
+ unsigned long long end;
+ struct memory_range *mem_range;
+
+ if (get_crash_memory_ranges(&mem_range, &nr_ranges) < 0)
+ return -1;
+
+ info->backup_src_start = BACKUP_SRC_START;
+ info->backup_src_size = BACKUP_SRC_SIZE;
+ /* Create a backup region segment to store backup data*/
+ sz = _ALIGN(BACKUP_SRC_SIZE, align);
+ tmp = xmalloc(sz);
+ memset(tmp, 0, sz);
+ info->backup_start = add_buffer(info, tmp, sz, sz, align,
+ 0, max_addr, 1);
+ reserve(info->backup_start, sz);
+
+ /* On ppc64 memory ranges in device-tree is denoted as start
+ * and size rather than start and end, as is the case with
+ * other architectures like i386 . Because of this when loading
+ * the memory ranges in crashdump-elf.c the filesz calculation
+ * [ end - start + 1 ] goes for a toss.
+ *
+ * To be in sync with other archs adjust the end value for
+ * every crash memory range before calling the generic function
+ */
+
+ for (i = 0; i < nr_ranges; i++) {
+ end = crash_memory_range[i].end - 1;
+ crash_memory_range[i].end = end;
+ }
+
+
+ /* Create elf header segment and store crash image data. */
+ if (arch_options.core_header_type == CORE_TYPE_ELF64) {
+ if (crash_create_elf64_headers(info, &elf_info64,
+ crash_memory_range, nr_ranges,
+ &tmp, &sz,
+ ELF_CORE_HEADER_ALIGN) < 0) {
+ free (tmp);
+ return -1;
+ }
+ }
+ else {
+ if (crash_create_elf32_headers(info, &elf_info32,
+ crash_memory_range, nr_ranges,
+ &tmp, &sz,
+ ELF_CORE_HEADER_ALIGN) < 0) {
+ free(tmp);
+ return -1;
+ }
+ }
+
+ elfcorehdr = add_buffer(info, tmp, sz, sz, align, min_base,
+ max_addr, 1);
+ reserve(elfcorehdr, sz);
+ /* modify and store the cmdline in a global array. This is later
+ * read by flatten_device_tree and modified if required
+ */
+ add_cmdline_param(mod_cmdline, elfcorehdr, " elfcorehdr=", "K");
+ return 0;
+}
+
+/*
+ * Used to save various memory regions needed for the captured kernel.
+ */
+
+void add_usable_mem_rgns(unsigned long long base, unsigned long long size)
+{
+ unsigned int i;
+ unsigned long long end = base + size;
+ unsigned long long ustart, uend;
+
+ base = _ALIGN_DOWN(base, getpagesize());
+ end = _ALIGN_UP(end, getpagesize());
+
+ for (i=0; i < usablemem_rgns.size; i++) {
+ ustart = usablemem_rgns.ranges[i].start;
+ uend = usablemem_rgns.ranges[i].end;
+ if (base < uend && end > ustart) {
+ if ((base >= ustart) && (end <= uend))
+ return;
+ if (base < ustart && end > uend) {
+ usablemem_rgns.ranges[i].start = base;
+ usablemem_rgns.ranges[i].end = end;
+#ifdef DEBUG
+ fprintf(stderr, "usable memory rgn %u: new base:%llx new size:%llx\n",
+ i, base, size);
+#endif
+ return;
+ } else if (base < ustart) {
+ usablemem_rgns.ranges[i].start = base;
+#ifdef DEBUG
+ fprintf(stderr, "usable memory rgn %u: new base:%llx new size:%llx",
+ i, base, usablemem_rgns.ranges[i].end - base);
+#endif
+ return;
+ } else if (end > uend){
+ usablemem_rgns.ranges[i].end = end;
+#ifdef DEBUG
+ fprintf(stderr, "usable memory rgn %u: new end:%llx, new size:%llx",
+ i, end, end - usablemem_rgns.ranges[i].start);
+#endif
+ return;
+ }
+ }
+ }
+ usablemem_rgns.ranges[usablemem_rgns.size].start = base;
+ usablemem_rgns.ranges[usablemem_rgns.size++].end = end;
+
+ dbgprintf("usable memory rgns size:%u base:%llx size:%llx\n",
+ usablemem_rgns.size, base, size);
+}
+
+int get_crash_kernel_load_range(uint64_t *start, uint64_t *end)
+{
+ unsigned long long value;
+
+ if (!get_devtree_value(DEVTREE_CRASHKERNEL_BASE, &value))
+ *start = be64_to_cpu(value);
+ else
+ return -1;
+
+ if (!get_devtree_value(DEVTREE_CRASHKERNEL_SIZE, &value))
+ *end = *start + be64_to_cpu(value) - 1;
+ else
+ return -1;
+
+ return 0;
+}
+
+int is_crashkernel_mem_reserved(void)
+{
+ int fd;
+
+ fd = open(DEVTREE_CRASHKERNEL_BASE, O_RDONLY);
+ if (fd < 0)
+ return 0;
+ close(fd);
+ return 1;
+}
+
+#if 0
+static int sort_regions(mem_rgns_t *rgn)
+{
+ int i, j;
+ unsigned long long tstart, tend;
+ for (i = 0; i < rgn->size; i++) {
+ for (j = 0; j < rgn->size - i - 1; j++) {
+ if (rgn->ranges[j].start > rgn->ranges[j+1].start) {
+ tstart = rgn->ranges[j].start;
+ tend = rgn->ranges[j].end;
+ rgn->ranges[j].start = rgn->ranges[j+1].start;
+ rgn->ranges[j].end = rgn->ranges[j+1].end;
+ rgn->ranges[j+1].start = tstart;
+ rgn->ranges[j+1].end = tend;
+ }
+ }
+ }
+ return 0;
+
+}
+#endif
+
diff --git a/kexec/arch/ppc64/crashdump-ppc64.h b/kexec/arch/ppc64/crashdump-ppc64.h
new file mode 100644
index 0000000..b0cba8a
--- /dev/null
+++ b/kexec/arch/ppc64/crashdump-ppc64.h
@@ -0,0 +1,51 @@
+#ifndef CRASHDUMP_PPC64_H
+#define CRASHDUMP_PPC64_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+struct kexec_info;
+int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline,
+ uint64_t max_addr, unsigned long min_base);
+void add_usable_mem_rgns(unsigned long long base, unsigned long long size);
+
+#define PAGE_OFFSET 0xC000000000000000ULL
+#define KERNELBASE PAGE_OFFSET
+#define VMALLOCBASE 0xD000000000000000ULL
+
+#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
+#define MAXMEM (-(unsigned long)(KERNELBASE-VMALLOCBASE))
+
+#define COMMAND_LINE_SIZE 2048 /* from kernel */
+/* Backup Region, First 64K of System RAM. */
+#define BACKUP_SRC_START 0x0000
+#define BACKUP_SRC_END 0xffff
+#define BACKUP_SRC_SIZE (BACKUP_SRC_END - BACKUP_SRC_START + 1)
+
+#define KDUMP_BACKUP_LIMIT BACKUP_SRC_SIZE
+
+#define KERNEL_RUN_AT_ZERO_MAGIC 0x72756e30 /* "run0" */
+
+extern uint64_t crash_base;
+extern uint64_t crash_size;
+extern uint64_t memory_limit;
+extern unsigned int rtas_base;
+extern unsigned int rtas_size;
+extern uint64_t opal_base;
+extern uint64_t opal_size;
+
+/*
+ * In case of ibm,dynamic-memory-v2 property, this is the number of LMB
+ * sets where each set represents a group of sequential LMB entries. In
+ * case of ibm,dynamic-memory property, the number of LMB sets is nothing
+ * but the total number of LMB entries.
+ */
+extern unsigned int num_of_lmb_sets;
+extern unsigned int is_dyn_mem_v2;
+extern uint64_t lmb_size;
+
+#define LMB_ENTRY_SIZE 24
+#define DRCONF_ADDR (is_dyn_mem_v2 ? 4 : 0)
+#define DRCONF_FLAGS 20
+
+#endif /* CRASHDUMP_PPC64_H */
diff --git a/kexec/arch/ppc64/fdt.c b/kexec/arch/ppc64/fdt.c
new file mode 100644
index 0000000..8bc6d2d
--- /dev/null
+++ b/kexec/arch/ppc64/fdt.c
@@ -0,0 +1,78 @@
+/*
+ * ppc64 fdt fixups
+ *
+ * Copyright 2015 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <arch/fdt.h>
+#include <libfdt.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/*
+ * Let the kernel know it booted from kexec, as some things (e.g.
+ * secondary CPU release) may work differently.
+ */
+static int fixup_kexec_prop(void *fdt)
+{
+ int err, nodeoffset;
+
+ nodeoffset = fdt_subnode_offset(fdt, 0, "chosen");
+ if (nodeoffset < 0)
+ nodeoffset = fdt_add_subnode(fdt, 0, "chosen");
+ if (nodeoffset < 0) {
+ printf("%s: add /chosen %s\n", __func__,
+ fdt_strerror(nodeoffset));
+ return -1;
+ }
+
+ err = fdt_setprop(fdt, nodeoffset, "linux,booted-from-kexec",
+ NULL, 0);
+ if (err < 0) {
+ printf("%s: couldn't write linux,booted-from-kexec: %s\n",
+ __func__, fdt_strerror(err));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * For now, assume that the added content fits in the file.
+ * This should be the case when flattening from /proc/device-tree,
+ * and when passing in a dtb, dtc can be told to add padding.
+ */
+int fixup_dt(char **fdt, off_t *size)
+{
+ int ret;
+
+ *size += 4096;
+ *fdt = realloc(*fdt, *size);
+ if (!*fdt) {
+ fprintf(stderr, "%s: out of memory\n", __func__);
+ return -1;
+ }
+
+ ret = fdt_open_into(*fdt, *fdt, *size);
+ if (ret < 0) {
+ fprintf(stderr, "%s: fdt_open_into: %s\n", __func__,
+ fdt_strerror(ret));
+ return -1;
+ }
+
+ ret = fixup_kexec_prop(*fdt);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
diff --git a/kexec/arch/ppc64/include/arch/fdt.h b/kexec/arch/ppc64/include/arch/fdt.h
new file mode 100644
index 0000000..b19f185
--- /dev/null
+++ b/kexec/arch/ppc64/include/arch/fdt.h
@@ -0,0 +1,8 @@
+#ifndef KEXEC_ARCH_PPC64_FDT
+#define KEXEC_ARCH_PPC64_FDT
+
+#include <sys/types.h>
+
+int fixup_dt(char **fdt, off_t *size);
+
+#endif
diff --git a/kexec/arch/ppc64/include/arch/options.h b/kexec/arch/ppc64/include/arch/options.h
new file mode 100644
index 0000000..2bca96a
--- /dev/null
+++ b/kexec/arch/ppc64/include/arch/options.h
@@ -0,0 +1,51 @@
+#ifndef KEXEC_ARCH_PPC64_OPTIONS_H
+#define KEXEC_ARCH_PPC64_OPTIONS_H
+
+#define OPT_ELF64_CORE (OPT_MAX+0)
+#define OPT_DT_NO_OLD_ROOT (OPT_MAX+1)
+#define OPT_ARCH_MAX (OPT_MAX+2)
+
+/* All 'local' loader options: */
+#define OPT_APPEND (OPT_ARCH_MAX+0)
+#define OPT_RAMDISK (OPT_ARCH_MAX+1)
+#define OPT_DEVICETREEBLOB (OPT_ARCH_MAX+2)
+#define OPT_ARGS_IGNORE (OPT_ARCH_MAX+3)
+#define OPT_REUSE_CMDLINE (OPT_ARCH_MAX+4)
+
+/* Options relevant to the architecture (excluding loader-specific ones): */
+#define KEXEC_ARCH_OPTIONS \
+ KEXEC_OPTIONS \
+ { "elf64-core-headers", 0, 0, OPT_ELF64_CORE }, \
+ { "dt-no-old-root", 0, 0, OPT_DT_NO_OLD_ROOT }, \
+
+#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
+
+/* The following two #defines list ALL of the options added by all of the
+ * architecture's loaders.
+ * o main() uses this complete list to scan for its options, ignoring
+ * arch-specific/loader-specific ones.
+ * o Then, arch_process_options() uses this complete list to scan for its
+ * options, ignoring general/loader-specific ones.
+ * o Then, the file_type[n].load re-scans for options, using
+ * KEXEC_ARCH_OPTIONS plus its loader-specific options subset.
+ * Any unrecognised options cause an error here.
+ *
+ * This is done so that main()'s/arch_process_options()'s getopt_long() calls
+ * don't choose a kernel filename from random arguments to options they don't
+ * recognise -- as they now recognise (if not act upon) all possible options.
+ */
+#define KEXEC_ALL_OPTIONS \
+ KEXEC_ARCH_OPTIONS \
+ { "command-line", 1, NULL, OPT_APPEND }, \
+ { "append", 1, NULL, OPT_APPEND }, \
+ { "ramdisk", 1, NULL, OPT_RAMDISK }, \
+ { "initrd", 1, NULL, OPT_RAMDISK }, \
+ { "devicetreeblob", 1, NULL, OPT_DEVICETREEBLOB }, \
+ { "dtb", 1, NULL, OPT_DEVICETREEBLOB }, \
+ { "args-linux", 0, NULL, OPT_ARGS_IGNORE }, \
+ { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE },
+
+#define KEXEC_ALL_OPT_STR KEXEC_OPT_STR
+
+
+#endif /* KEXEC_ARCH_PPC64_OPTIONS_H */
diff --git a/kexec/arch/ppc64/kexec-elf-ppc64.c b/kexec/arch/ppc64/kexec-elf-ppc64.c
new file mode 100644
index 0000000..01d045f
--- /dev/null
+++ b/kexec/arch/ppc64/kexec-elf-ppc64.c
@@ -0,0 +1,496 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2004 Adam Litke (agl@us.ibm.com)
+ * Copyright (C) 2004 IBM Corp.
+ * Copyright (C) 2005 R Sharada (sharada@in.ibm.com)
+ * Copyright (C) 2006 Mohan Kumar M (mohan@in.ibm.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <linux/elf.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-syscall.h"
+#include "kexec-ppc64.h"
+#include "../../fs2dt.h"
+#include "crashdump-ppc64.h"
+#include <libfdt.h>
+#include <arch/fdt.h>
+#include <arch/options.h>
+
+uint64_t initrd_base, initrd_size;
+unsigned char reuse_initrd = 0;
+const char *ramdisk;
+
+int elf_ppc64_probe(const char *buf, off_t len)
+{
+ struct mem_ehdr ehdr;
+ int result;
+ result = build_elf_exec_info(buf, len, &ehdr, 0);
+ if (result < 0) {
+ goto out;
+ }
+
+ /* Verify the architecuture specific bits */
+ if ((ehdr.e_machine != EM_PPC64) && (ehdr.e_machine != EM_PPC)) {
+ /* for a different architecture */
+ result = -1;
+ goto out;
+ }
+ result = 0;
+ out:
+ free_elf_info(&ehdr);
+ return result;
+}
+
+void arch_reuse_initrd(void)
+{
+ reuse_initrd = 1;
+}
+
+static int read_prop(char *name, void *value, size_t len)
+{
+ int fd;
+ size_t rlen;
+
+ fd = open(name, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rlen = read(fd, value, len);
+ if (rlen < 0)
+ fprintf(stderr, "Warning : Can't read %s : %s",
+ name, strerror(errno));
+ else if (rlen != len)
+ fprintf(stderr, "Warning : short read from %s", name);
+
+ close(fd);
+ return 0;
+}
+
+static int elf_ppc64_load_file(int argc, char **argv, struct kexec_info *info)
+{
+ int ret = 0;
+ char *cmdline, *dtb;
+ char *append_cmdline = NULL;
+ char *reuse_cmdline = NULL;
+ int opt, cmdline_len = 0;
+
+ /* See options.h -- add any more there, too. */
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { "command-line", 1, NULL, OPT_APPEND },
+ { "append", 1, NULL, OPT_APPEND },
+ { "ramdisk", 1, NULL, OPT_RAMDISK },
+ { "initrd", 1, NULL, OPT_RAMDISK },
+ { "devicetreeblob", 1, NULL, OPT_DEVICETREEBLOB },
+ { "dtb", 1, NULL, OPT_DEVICETREEBLOB },
+ { "args-linux", 0, NULL, OPT_ARGS_IGNORE },
+ { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE},
+ { 0, 0, NULL, 0 },
+ };
+
+ static const char short_options[] = KEXEC_OPT_STR "";
+
+ /* Parse command line arguments */
+ cmdline = 0;
+ dtb = 0;
+ ramdisk = 0;
+
+ while ((opt = getopt_long(argc, argv, short_options,
+ options, 0)) != -1) {
+ switch (opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX)
+ break;
+ case OPT_APPEND:
+ append_cmdline = optarg;
+ break;
+ case OPT_RAMDISK:
+ ramdisk = optarg;
+ break;
+ case OPT_DEVICETREEBLOB:
+ dtb = optarg;
+ break;
+ case OPT_ARGS_IGNORE:
+ break;
+ case OPT_REUSE_CMDLINE:
+ reuse_cmdline = get_command_line();
+ break;
+ }
+ }
+
+ if (dtb)
+ die("--dtb not supported while using --kexec-file-syscall.\n");
+
+ if (reuse_initrd)
+ die("--reuseinitrd not supported with --kexec-file-syscall.\n");
+
+ cmdline = concat_cmdline(reuse_cmdline, append_cmdline);
+ if (!reuse_cmdline)
+ free(reuse_cmdline);
+
+ if (cmdline) {
+ cmdline_len = strlen(cmdline) + 1;
+ } else {
+ cmdline = strdup("\0");
+ cmdline_len = 1;
+ }
+
+ if (ramdisk) {
+ info->initrd_fd = open(ramdisk, O_RDONLY);
+ if (info->initrd_fd == -1) {
+ fprintf(stderr, "Could not open initrd file %s:%s\n",
+ ramdisk, strerror(errno));
+ ret = -1;
+ goto out;
+ }
+ }
+
+ info->command_line = cmdline;
+ info->command_line_len = cmdline_len;
+ return ret;
+out:
+ if (cmdline_len == 1)
+ free(cmdline);
+ return ret;
+}
+
+int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ struct mem_ehdr ehdr;
+ char *cmdline, *modified_cmdline = NULL;
+ char *reuse_cmdline = NULL;
+ char *append_cmdline = NULL;
+ const char *devicetreeblob;
+ uint64_t max_addr, hole_addr;
+ char *seg_buf = NULL;
+ off_t seg_size = 0;
+ struct mem_phdr *phdr;
+ size_t size;
+#ifdef NEED_RESERVE_DTB
+ uint64_t *rsvmap_ptr;
+ struct bootblock *bb_ptr;
+#endif
+ int result, opt;
+ uint64_t my_kernel, my_dt_offset;
+ uint64_t my_opal_base = 0, my_opal_entry = 0;
+ unsigned int my_panic_kernel;
+ uint64_t my_stack, my_backup_start;
+ uint64_t toc_addr;
+ uint32_t my_run_at_load;
+ unsigned int slave_code[256/sizeof (unsigned int)], master_entry;
+
+ /* See options.h -- add any more there, too. */
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { "command-line", 1, NULL, OPT_APPEND },
+ { "append", 1, NULL, OPT_APPEND },
+ { "ramdisk", 1, NULL, OPT_RAMDISK },
+ { "initrd", 1, NULL, OPT_RAMDISK },
+ { "devicetreeblob", 1, NULL, OPT_DEVICETREEBLOB },
+ { "dtb", 1, NULL, OPT_DEVICETREEBLOB },
+ { "args-linux", 0, NULL, OPT_ARGS_IGNORE },
+ { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE},
+ { 0, 0, NULL, 0 },
+ };
+
+ static const char short_options[] = KEXEC_OPT_STR "";
+
+ if (info->file_mode)
+ return elf_ppc64_load_file(argc, argv, info);
+
+ /* Parse command line arguments */
+ initrd_base = 0;
+ initrd_size = 0;
+ cmdline = 0;
+ ramdisk = 0;
+ devicetreeblob = 0;
+ max_addr = 0xFFFFFFFFFFFFFFFFULL;
+ hole_addr = 0;
+
+ while ((opt = getopt_long(argc, argv, short_options,
+ options, 0)) != -1) {
+ switch (opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX)
+ break;
+ case OPT_APPEND:
+ append_cmdline = optarg;
+ break;
+ case OPT_RAMDISK:
+ ramdisk = optarg;
+ break;
+ case OPT_DEVICETREEBLOB:
+ devicetreeblob = optarg;
+ break;
+ case OPT_ARGS_IGNORE:
+ break;
+ case OPT_REUSE_CMDLINE:
+ reuse_cmdline = get_command_line();
+ break;
+ }
+ }
+
+ cmdline = concat_cmdline(reuse_cmdline, append_cmdline);
+ if (!reuse_cmdline)
+ free(reuse_cmdline);
+
+ if (!cmdline)
+ fprintf(stdout, "Warning: append= option is not passed. Using the first kernel root partition\n");
+
+ if (ramdisk && reuse_initrd)
+ die("Can't specify --ramdisk or --initrd with --reuseinitrd\n");
+
+ /* Need to append some command line parameters internally in case of
+ * taking crash dumps.
+ */
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ modified_cmdline = xmalloc(COMMAND_LINE_SIZE);
+ memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE);
+ if (cmdline) {
+ strncpy(modified_cmdline, cmdline, COMMAND_LINE_SIZE);
+ modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
+ }
+ }
+
+ /* Parse the Elf file */
+ result = build_elf_exec_info(buf, len, &ehdr, 0);
+ if (result < 0) {
+ free_elf_info(&ehdr);
+ return result;
+ }
+
+ /* Load the Elf data. Physical load addresses in elf64 header do not
+ * show up correctly. Use user supplied address for now to patch the
+ * elf header
+ */
+
+ phdr = &ehdr.e_phdr[0];
+ size = phdr->p_filesz;
+ if (size > phdr->p_memsz)
+ size = phdr->p_memsz;
+
+ my_kernel = hole_addr = locate_hole(info, size, 0, 0, max_addr, 1);
+ ehdr.e_phdr[0].p_paddr = hole_addr;
+ result = elf_exec_load(&ehdr, info);
+ if (result < 0) {
+ free_elf_info(&ehdr);
+ return result;
+ }
+
+ /* If panic kernel is being loaded, additional segments need
+ * to be created.
+ */
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ result = load_crashdump_segments(info, modified_cmdline,
+ max_addr, 0);
+ if (result < 0)
+ return -1;
+ /* Use new command line. */
+ cmdline = modified_cmdline;
+ }
+
+ /* Add v2wrap to the current image */
+ elf_rel_build_load(info, &info->rhdr, purgatory,
+ purgatory_size, 0, max_addr, 1, 0);
+
+ /* Add a ram-disk to the current image
+ * Note: Add the ramdisk after elf_rel_build_load
+ */
+ if (ramdisk) {
+ if (devicetreeblob) {
+ fprintf(stderr,
+ "Can't use ramdisk with device tree blob input\n");
+ return -1;
+ }
+ seg_buf = slurp_file(ramdisk, &seg_size);
+ hole_addr = add_buffer(info, seg_buf, seg_size, seg_size,
+ 0, 0, max_addr, 1);
+ initrd_base = hole_addr;
+ initrd_size = seg_size;
+ } /* ramdisk */
+
+ if (devicetreeblob) {
+ /* Grab device tree from buffer */
+ seg_buf = slurp_file(devicetreeblob, &seg_size);
+ } else {
+ /* create from fs2dt */
+ create_flatten_tree(&seg_buf, &seg_size, cmdline);
+ }
+
+ result = fixup_dt(&seg_buf, &seg_size);
+ if (result < 0)
+ return result;
+
+ my_dt_offset = add_buffer(info, seg_buf, seg_size, seg_size,
+ 0, 0, max_addr, -1);
+
+#ifdef NEED_RESERVE_DTB
+ /* patch reserve map address for flattened device-tree
+ * find last entry (both 0) in the reserve mem list. Assume DT
+ * entry is before this one
+ */
+ bb_ptr = (struct bootblock *)(seg_buf);
+ rsvmap_ptr = (uint64_t *)(seg_buf + be32_to_cpu(bb_ptr->off_mem_rsvmap));
+ while (*rsvmap_ptr || *(rsvmap_ptr+1))
+ rsvmap_ptr += 2;
+ rsvmap_ptr -= 2;
+ *rsvmap_ptr = cpu_to_be64(my_dt_offset);
+ rsvmap_ptr++;
+ *rsvmap_ptr = cpu_to_be64((uint64_t)be32_to_cpu(bb_ptr->totalsize));
+#endif
+
+ if (read_prop("/proc/device-tree/ibm,opal/opal-base-address",
+ &my_opal_base, sizeof(my_opal_base)) == 0) {
+ my_opal_base = be64_to_cpu(my_opal_base);
+ elf_rel_set_symbol(&info->rhdr, "opal_base",
+ &my_opal_base, sizeof(my_opal_base));
+ }
+
+ if (read_prop("/proc/device-tree/ibm,opal/opal-entry-address",
+ &my_opal_entry, sizeof(my_opal_entry)) == 0) {
+ my_opal_entry = be64_to_cpu(my_opal_entry);
+ elf_rel_set_symbol(&info->rhdr, "opal_entry",
+ &my_opal_entry, sizeof(my_opal_entry));
+ }
+
+ /* Set kernel */
+ elf_rel_set_symbol(&info->rhdr, "kernel", &my_kernel, sizeof(my_kernel));
+
+ /* Set dt_offset */
+ elf_rel_set_symbol(&info->rhdr, "dt_offset", &my_dt_offset,
+ sizeof(my_dt_offset));
+
+ /* get slave code from new kernel, put in purgatory */
+ elf_rel_get_symbol(&info->rhdr, "purgatory_start", slave_code,
+ sizeof(slave_code));
+ master_entry = slave_code[0];
+ memcpy(slave_code, phdr->p_data, sizeof(slave_code));
+ slave_code[0] = master_entry;
+ elf_rel_set_symbol(&info->rhdr, "purgatory_start", slave_code,
+ sizeof(slave_code));
+
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ my_panic_kernel = 1;
+ /* Set panic flag */
+ elf_rel_set_symbol(&info->rhdr, "panic_kernel",
+ &my_panic_kernel, sizeof(my_panic_kernel));
+
+ /* Set backup address */
+ my_backup_start = info->backup_start;
+ elf_rel_set_symbol(&info->rhdr, "backup_start",
+ &my_backup_start, sizeof(my_backup_start));
+
+ /* Tell relocatable kernel to run at load address
+ * via word before slave code in purgatory
+ */
+
+ elf_rel_get_symbol(&info->rhdr, "run_at_load", &my_run_at_load,
+ sizeof(my_run_at_load));
+ if (my_run_at_load == KERNEL_RUN_AT_ZERO_MAGIC)
+ my_run_at_load = 1;
+ /* else it should be a fixed offset image */
+ elf_rel_set_symbol(&info->rhdr, "run_at_load", &my_run_at_load,
+ sizeof(my_run_at_load));
+ }
+
+ /* Set stack address */
+ my_stack = locate_hole(info, 16*1024, 0, 0, max_addr, 1);
+ my_stack += 16*1024;
+ elf_rel_set_symbol(&info->rhdr, "stack", &my_stack, sizeof(my_stack));
+
+ /* Set toc */
+ toc_addr = my_r2(&info->rhdr);
+ elf_rel_set_symbol(&info->rhdr, "my_toc", &toc_addr, sizeof(toc_addr));
+
+ /* Set debug */
+ elf_rel_set_symbol(&info->rhdr, "debug", &my_debug, sizeof(my_debug));
+
+ my_kernel = 0;
+ my_dt_offset = 0;
+ my_panic_kernel = 0;
+ my_backup_start = 0;
+ my_stack = 0;
+ toc_addr = 0;
+ my_run_at_load = 0;
+ my_debug = 0;
+ my_opal_base = 0;
+ my_opal_entry = 0;
+
+ elf_rel_get_symbol(&info->rhdr, "opal_base", &my_opal_base,
+ sizeof(my_opal_base));
+ elf_rel_get_symbol(&info->rhdr, "opal_entry", &my_opal_entry,
+ sizeof(my_opal_entry));
+ elf_rel_get_symbol(&info->rhdr, "kernel", &my_kernel, sizeof(my_kernel));
+ elf_rel_get_symbol(&info->rhdr, "dt_offset", &my_dt_offset,
+ sizeof(my_dt_offset));
+ elf_rel_get_symbol(&info->rhdr, "run_at_load", &my_run_at_load,
+ sizeof(my_run_at_load));
+ elf_rel_get_symbol(&info->rhdr, "panic_kernel", &my_panic_kernel,
+ sizeof(my_panic_kernel));
+ elf_rel_get_symbol(&info->rhdr, "backup_start", &my_backup_start,
+ sizeof(my_backup_start));
+ elf_rel_get_symbol(&info->rhdr, "stack", &my_stack, sizeof(my_stack));
+ elf_rel_get_symbol(&info->rhdr, "my_toc", &toc_addr,
+ sizeof(toc_addr));
+ elf_rel_get_symbol(&info->rhdr, "debug", &my_debug, sizeof(my_debug));
+
+ dbgprintf("info->entry is %p\n", info->entry);
+ dbgprintf("kernel is %llx\n", (unsigned long long)my_kernel);
+ dbgprintf("dt_offset is %llx\n",
+ (unsigned long long)my_dt_offset);
+ dbgprintf("run_at_load flag is %x\n", my_run_at_load);
+ dbgprintf("panic_kernel is %x\n", my_panic_kernel);
+ dbgprintf("backup_start is %llx\n",
+ (unsigned long long)my_backup_start);
+ dbgprintf("stack is %llx\n", (unsigned long long)my_stack);
+ dbgprintf("toc_addr is %llx\n", (unsigned long long)toc_addr);
+ dbgprintf("purgatory size is %zu\n", purgatory_size);
+ dbgprintf("debug is %d\n", my_debug);
+ dbgprintf("opal_base is %llx\n", (unsigned long long) my_opal_base);
+ dbgprintf("opal_entry is %llx\n", (unsigned long long) my_opal_entry);
+
+ return 0;
+}
+
+void elf_ppc64_usage(void)
+{
+ fprintf(stderr, " --command-line=<Command line> command line to append.\n");
+ fprintf(stderr, " --append=<Command line> same as --command-line.\n");
+ fprintf(stderr, " --ramdisk=<filename> Initial RAM disk.\n");
+ fprintf(stderr, " --initrd=<filename> same as --ramdisk.\n");
+ fprintf(stderr, " --devicetreeblob=<filename> Specify device tree blob file.\n");
+ fprintf(stderr, " ");
+ fprintf(stderr, "Not applicable while using --kexec-file-syscall.\n");
+ fprintf(stderr, " --reuse-cmdline Use kernel command line from running system.\n");
+ fprintf(stderr, " --dtb=<filename> same as --devicetreeblob.\n");
+
+ fprintf(stderr, "elf support is still broken\n");
+}
diff --git a/kexec/arch/ppc64/kexec-elf-rel-ppc64.c b/kexec/arch/ppc64/kexec-elf-rel-ppc64.c
new file mode 100644
index 0000000..51b1354
--- /dev/null
+++ b/kexec/arch/ppc64/kexec-elf-rel-ppc64.c
@@ -0,0 +1,204 @@
+#include <stdio.h>
+#include <elf.h>
+#include <string.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "kexec-ppc64.h"
+
+#if defined(_CALL_ELF) && _CALL_ELF == 2
+#define STO_PPC64_LOCAL_BIT 5
+#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT)
+#define PPC64_LOCAL_ENTRY_OFFSET(other) \
+ (((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) >> 2) << 2)
+
+static unsigned int local_entry_offset(struct mem_sym *sym)
+{
+ /* If this symbol has a local entry point, use it. */
+ return PPC64_LOCAL_ENTRY_OFFSET(sym->st_other);
+}
+#else
+static unsigned int local_entry_offset(struct mem_sym *UNUSED(sym))
+{
+ return 0;
+}
+#endif
+
+static struct mem_shdr *toc_section(const struct mem_ehdr *ehdr)
+{
+ struct mem_shdr *shdr, *shdr_end;
+ unsigned char *strtab;
+
+ strtab = (unsigned char *)ehdr->e_shdr[ehdr->e_shstrndx].sh_data;
+ shdr_end = &ehdr->e_shdr[ehdr->e_shnum];
+ for (shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) {
+ if (shdr->sh_size &&
+ strcmp((char *)&strtab[shdr->sh_name], ".toc") == 0) {
+ return shdr;
+ }
+ }
+
+ return NULL;
+}
+
+int machine_verify_elf_rel(struct mem_ehdr *ehdr)
+{
+ struct mem_shdr *toc;
+
+ if (ehdr->ei_class != ELFCLASS64) {
+ return 0;
+ }
+ if (ehdr->e_machine != EM_PPC64) {
+ return 0;
+ }
+
+ /* Ensure .toc is sufficiently aligned. */
+ toc = toc_section(ehdr);
+ if (toc && toc->sh_addralign < 256)
+ toc->sh_addralign = 256;
+ return 1;
+}
+
+/* r2 is the TOC pointer: it actually points 0x8000 into the TOC (this
+ gives the value maximum span in an instruction which uses a signed
+ offset) */
+unsigned long my_r2(const struct mem_ehdr *ehdr)
+{
+ struct mem_shdr *shdr;
+
+ shdr = toc_section(ehdr);
+ if (!shdr) {
+ die("TOC reloc without a toc section?");
+ }
+
+ return shdr->sh_addr + 0x8000;
+}
+
+static void do_relative_toc(unsigned long value, uint16_t *location,
+ unsigned long mask, int complain_signed)
+{
+ if (complain_signed && (value + 0x8000 > 0xffff)) {
+ die("TOC16 relocation overflows (%lu)\n", value);
+ }
+
+ if ((~mask & 0xffff) & value) {
+ die("bad TOC16 relocation (%lu)\n", value);
+ }
+
+ *location = (*location & ~mask) | (value & mask);
+}
+
+void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *sym,
+ unsigned long r_type, void *location, unsigned long address,
+ unsigned long value)
+{
+ switch(r_type) {
+ case R_PPC64_ADDR32:
+ /* Simply set it */
+ *(uint32_t *)location = value;
+ break;
+
+ case R_PPC64_ADDR64:
+ case R_PPC64_REL64:
+ /* Simply set it */
+ *(uint64_t *)location = value;
+ break;
+
+ case R_PPC64_REL32:
+ *(uint32_t *)location = value - (uint32_t)location;
+ break;
+
+ case R_PPC64_TOC:
+ *(uint64_t *)location = my_r2(ehdr);
+ break;
+
+ case R_PPC64_TOC16:
+ do_relative_toc(value - my_r2(ehdr), location, 0xffff, 1);
+ break;
+
+ case R_PPC64_TOC16_DS:
+ do_relative_toc(value - my_r2(ehdr), location, 0xfffc, 1);
+ break;
+
+ case R_PPC64_TOC16_LO:
+ do_relative_toc(value - my_r2(ehdr), location, 0xffff, 0);
+ break;
+
+ case R_PPC64_TOC16_LO_DS:
+ do_relative_toc(value - my_r2(ehdr), location, 0xfffc, 0);
+ break;
+
+ case R_PPC64_TOC16_HI:
+ do_relative_toc((value - my_r2(ehdr)) >> 16, location,
+ 0xffff, 0);
+ break;
+
+ case R_PPC64_TOC16_HA:
+ do_relative_toc((value - my_r2(ehdr) + 0x8000) >> 16, location,
+ 0xffff, 0);
+ break;
+
+ case R_PPC64_REL24:
+ value += local_entry_offset(sym);
+ /* Convert value to relative */
+ value -= address;
+ if (value + 0x2000000 > 0x3ffffff || (value & 3) != 0) {
+ die("REL24 %li out of range!\n", (long int)value);
+ }
+
+ /* Only replace bits 2 through 26 */
+ *(uint32_t *)location = (*(uint32_t *)location & ~0x03fffffc) |
+ (value & 0x03fffffc);
+ break;
+
+ case R_PPC64_ADDR16_LO:
+ *(uint16_t *)location = value & 0xffff;
+ break;
+
+ case R_PPC64_ADDR16_HI:
+ *(uint16_t *)location = (value >> 16) & 0xffff;
+ break;
+
+ case R_PPC64_ADDR16_HA:
+ *(uint16_t *)location = (((value + 0x8000) >> 16) & 0xffff);
+ break;
+
+ case R_PPC64_ADDR16_HIGHER:
+ *(uint16_t *)location = (((uint64_t)value >> 32) & 0xffff);
+ break;
+
+ case R_PPC64_ADDR16_HIGHEST:
+ *(uint16_t *)location = (((uint64_t)value >> 48) & 0xffff);
+ break;
+
+ /* R_PPC64_REL16_HA and R_PPC64_REL16_LO are handled to support
+ * ABIv2 r2 assignment based on r12 for PIC executable.
+ * Here address is know so replace
+ * 0: addis 2,12,.TOC.-0b@ha
+ * addi 2,2,.TOC.-0b@l
+ * by
+ * lis 2,.TOC.@ha
+ * addi 2,2,.TOC.@l
+ */
+ case R_PPC64_REL16_HA:
+ /* check that we are dealing with the addis 2,12 instruction */
+ if (((*(uint32_t*)location) & 0xffff0000) != 0x3c4c0000)
+ die("Unexpected instruction for R_PPC64_REL16_HA");
+ value += my_r2(ehdr);
+ /* replacing by lis 2 */
+ *(uint32_t *)location = 0x3c400000 + ((value >> 16) & 0xffff);
+ break;
+
+ case R_PPC64_REL16_LO:
+ /* check that we are dealing with the addi 2,2 instruction */
+ if (((*(uint32_t*)location) & 0xffff0000) != 0x38420000)
+ die("Unexpected instruction for R_PPC64_REL16_LO");
+
+ value += my_r2(ehdr) - 4;
+ *(uint16_t *)location = value & 0xffff;
+ break;
+
+ default:
+ die("Unknown rela relocation: %lu\n", r_type);
+ break;
+ }
+}
diff --git a/kexec/arch/ppc64/kexec-ppc64.c b/kexec/arch/ppc64/kexec-ppc64.c
new file mode 100644
index 0000000..611809f
--- /dev/null
+++ b/kexec/arch/ppc64/kexec-ppc64.c
@@ -0,0 +1,969 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com)
+ * Copyright (C) 2005 R Sharada (sharada@in.ibm.com), IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <getopt.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "kexec-ppc64.h"
+#include "../../fs2dt.h"
+#include "crashdump-ppc64.h"
+#include <arch/options.h>
+
+static struct memory_range *exclude_range = NULL;
+static struct memory_range *memory_range = NULL;
+static struct memory_range *base_memory_range = NULL;
+static uint64_t rma_top;
+uint64_t memory_max = 0;
+uint64_t memory_limit;
+static int nr_memory_ranges, nr_exclude_ranges;
+uint64_t crash_base, crash_size;
+unsigned int rtas_base, rtas_size;
+uint64_t opal_base, opal_size;
+int max_memory_ranges;
+
+static void cleanup_memory_ranges(void)
+{
+ if (memory_range)
+ free(memory_range);
+ if (base_memory_range)
+ free(base_memory_range);
+ if (exclude_range)
+ free(exclude_range);
+ if (usablemem_rgns.ranges)
+ free(usablemem_rgns.ranges);
+}
+
+/*
+ * Allocate memory for various data structures used to hold
+ * values of different memory ranges
+ */
+static int alloc_memory_ranges(void)
+{
+ int memory_range_len;
+
+ memory_range_len = sizeof(struct memory_range) * max_memory_ranges;
+
+ memory_range = (struct memory_range *) malloc(memory_range_len);
+ if (!memory_range)
+ return -1;
+
+ base_memory_range = (struct memory_range *) malloc(memory_range_len);
+ if (!base_memory_range)
+ goto err1;
+
+ exclude_range = (struct memory_range *) malloc(memory_range_len);
+ if (!exclude_range)
+ goto err1;
+
+ usablemem_rgns.ranges = (struct memory_range *)
+ malloc(memory_range_len);
+ if (!(usablemem_rgns.ranges))
+ goto err1;
+
+ memset(memory_range, 0, memory_range_len);
+ memset(base_memory_range, 0, memory_range_len);
+ memset(exclude_range, 0, memory_range_len);
+ memset(usablemem_rgns.ranges, 0, memory_range_len);
+ return 0;
+
+err1:
+ fprintf(stderr, "memory range structure allocation failure\n");
+ cleanup_memory_ranges();
+ return -1;
+
+}
+
+static int realloc_memory_ranges(void)
+{
+ size_t memory_range_len;
+
+ max_memory_ranges++;
+ memory_range_len = sizeof(struct memory_range) * max_memory_ranges;
+
+ memory_range = (struct memory_range *) realloc(memory_range, memory_range_len);
+ if (!memory_range)
+ goto err;
+
+ base_memory_range = (struct memory_range *) realloc(base_memory_range, memory_range_len);
+ if (!base_memory_range)
+ goto err;
+
+ exclude_range = (struct memory_range *) realloc(exclude_range, memory_range_len);
+ if (!exclude_range)
+ goto err;
+
+ usablemem_rgns.ranges = (struct memory_range *)
+ realloc(usablemem_rgns.ranges, memory_range_len);
+ if (!(usablemem_rgns.ranges))
+ goto err;
+
+ return 0;
+
+err:
+ fprintf(stderr, "memory range structure re-allocation failure\n");
+ return -1;
+}
+
+
+static void add_base_memory_range(uint64_t start, uint64_t end)
+{
+ base_memory_range[nr_memory_ranges].start = start;
+ base_memory_range[nr_memory_ranges].end = end;
+ base_memory_range[nr_memory_ranges].type = RANGE_RAM;
+ nr_memory_ranges++;
+ if (nr_memory_ranges >= max_memory_ranges)
+ realloc_memory_ranges();
+
+ dbgprintf("%016llx-%016llx : %x\n",
+ base_memory_range[nr_memory_ranges-1].start,
+ base_memory_range[nr_memory_ranges-1].end,
+ base_memory_range[nr_memory_ranges-1].type);
+}
+
+static int get_dyn_reconf_base_ranges(void)
+{
+ uint64_t start, end;
+ uint64_t size;
+ char fname[128], buf[32];
+ FILE *file;
+ unsigned int i;
+ int n;
+
+ strcpy(fname, "/proc/device-tree/");
+ strcat(fname, "ibm,dynamic-reconfiguration-memory/ibm,lmb-size");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ return -1;
+ }
+ if (fread(buf, 1, 8, file) != 8) {
+ perror(fname);
+ fclose(file);
+ return -1;
+ }
+ /*
+ * lmb_size, num_of_lmb_sets(global variables) are
+ * initialized once here.
+ */
+ size = lmb_size = be64_to_cpu(((uint64_t *)buf)[0]);
+ fclose(file);
+
+ strcpy(fname, "/proc/device-tree/");
+ strcat(fname,
+ "ibm,dynamic-reconfiguration-memory/ibm,dynamic-memory");
+ if ((file = fopen(fname, "r")) == NULL) {
+ strcat(fname, "-v2");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ return -1;
+ }
+
+ is_dyn_mem_v2 = 1;
+ }
+
+ /* first 4 bytes tell the number of lmb set entries */
+ if (fread(buf, 1, 4, file) != 4) {
+ perror(fname);
+ fclose(file);
+ return -1;
+ }
+ num_of_lmb_sets = be32_to_cpu(((unsigned int *)buf)[0]);
+
+ for (i = 0; i < num_of_lmb_sets; i++) {
+ if ((n = fread(buf, 1, LMB_ENTRY_SIZE, file)) < 0) {
+ perror(fname);
+ fclose(file);
+ return -1;
+ }
+ if (nr_memory_ranges >= max_memory_ranges) {
+ fclose(file);
+ return -1;
+ }
+
+ /*
+ * If the property is ibm,dynamic-memory-v2, the first 4 bytes
+ * tell the number of sequential LMBs in this entry.
+ */
+ if (is_dyn_mem_v2)
+ size = be32_to_cpu(((unsigned int *)buf)[0]) * lmb_size;
+
+ start = be64_to_cpu(*((uint64_t *)&buf[DRCONF_ADDR]));
+ end = start + size;
+ add_base_memory_range(start, end);
+ }
+ fclose(file);
+ return 0;
+}
+
+/* Sort the base ranges in memory - this is useful for ensuring that our
+ * ranges are in ascending order, even if device-tree read of memory nodes
+ * is done differently. Also, could be used for other range coalescing later
+ */
+static int sort_base_ranges(void)
+{
+ int i, j;
+ unsigned long long tstart, tend;
+
+ for (i = 0; i < nr_memory_ranges - 1; i++) {
+ for (j = 0; j < nr_memory_ranges - i - 1; j++) {
+ if (base_memory_range[j].start > base_memory_range[j+1].start) {
+ tstart = base_memory_range[j].start;
+ tend = base_memory_range[j].end;
+ base_memory_range[j].start = base_memory_range[j+1].start;
+ base_memory_range[j].end = base_memory_range[j+1].end;
+ base_memory_range[j+1].start = tstart;
+ base_memory_range[j+1].end = tend;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Get base memory ranges */
+static int get_base_ranges(void)
+{
+ uint64_t start, end;
+ char device_tree[256] = "/proc/device-tree/";
+ char fname[256];
+ char buf[MAXBYTES];
+ DIR *dir, *dmem;
+ FILE *file;
+ struct dirent *dentry, *mentry;
+ int n;
+
+ if ((dir = opendir(device_tree)) == NULL) {
+ perror(device_tree);
+ return -1;
+ }
+ while ((dentry = readdir(dir)) != NULL) {
+ if (!strncmp(dentry->d_name,
+ "ibm,dynamic-reconfiguration-memory", 35)) {
+ get_dyn_reconf_base_ranges();
+ continue;
+ }
+ if (strncmp(dentry->d_name, "memory@", 7) &&
+ strcmp(dentry->d_name, "memory"))
+ continue;
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ if ((dmem = opendir(fname)) == NULL) {
+ perror(fname);
+ closedir(dir);
+ return -1;
+ }
+ while ((mentry = readdir(dmem)) != NULL) {
+ if (strcmp(mentry->d_name, "reg"))
+ continue;
+ strcat(fname, "/reg");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(dmem);
+ closedir(dir);
+ return -1;
+ }
+ if ((n = fread(buf, 1, MAXBYTES, file)) < 0) {
+ perror(fname);
+ fclose(file);
+ closedir(dmem);
+ closedir(dir);
+ return -1;
+ }
+ if (nr_memory_ranges >= max_memory_ranges) {
+ if (realloc_memory_ranges() < 0)
+ break;
+ }
+ start = be64_to_cpu(((uint64_t *)buf)[0]);
+ end = start + be64_to_cpu(((uint64_t *)buf)[1]);
+ add_base_memory_range(start, end);
+ fclose(file);
+ }
+ closedir(dmem);
+ }
+ closedir(dir);
+ sort_base_ranges();
+ memory_max = base_memory_range[nr_memory_ranges - 1].end;
+ dbgprintf("get base memory ranges:%d\n", nr_memory_ranges);
+
+ return 0;
+}
+
+/* Sort the exclude ranges in memory */
+static int sort_ranges(void)
+{
+ int i, j;
+ uint64_t tstart, tend;
+ for (i = 0; i < nr_exclude_ranges - 1; i++) {
+ for (j = 0; j < nr_exclude_ranges - i - 1; j++) {
+ if (exclude_range[j].start > exclude_range[j+1].start) {
+ tstart = exclude_range[j].start;
+ tend = exclude_range[j].end;
+ exclude_range[j].start = exclude_range[j+1].start;
+ exclude_range[j].end = exclude_range[j+1].end;
+ exclude_range[j+1].start = tstart;
+ exclude_range[j+1].end = tend;
+ }
+ }
+ }
+ return 0;
+}
+
+void scan_reserved_ranges(unsigned long kexec_flags, int *range_index)
+{
+ char fname[256], buf[16];
+ FILE *file;
+ int i = *range_index;
+
+ strcpy(fname, "/proc/device-tree/reserved-ranges");
+
+ file = fopen(fname, "r");
+ if (file == NULL) {
+ if (errno != ENOENT) {
+ perror(fname);
+ return;
+ }
+ errno = 0;
+ /* File not present. Non PowerKVM system. */
+ return;
+ }
+
+ /*
+ * Each reserved range is an (address,size) pair, 2 cells each,
+ * totalling 4 cells per range.
+ */
+ while (fread(buf, sizeof(uint64_t) * 2, 1, file) == 1) {
+ uint64_t base, size;
+
+ base = be64_to_cpu(((uint64_t *)buf)[0]);
+ size = be64_to_cpu(((uint64_t *)buf)[1]);
+
+ exclude_range[i].start = base;
+ exclude_range[i].end = base + size;
+ i++;
+ if (i >= max_memory_ranges)
+ realloc_memory_ranges();
+
+ reserve(base, size);
+ }
+ fclose(file);
+ *range_index = i;
+}
+
+/* Return 0 if fname/value valid, -1 otherwise */
+int get_devtree_value(const char *fname, unsigned long long *value)
+{
+ FILE *file;
+ char buf[MAXBYTES];
+ int n = -1;
+
+ if ((file = fopen(fname, "r"))) {
+ n = fread(buf, 1, MAXBYTES, file);
+ fclose(file);
+ }
+
+ if (n == sizeof(uint32_t))
+ *value = ((uint32_t *)buf)[0];
+ else if (n == sizeof(uint64_t))
+ *value = ((uint64_t *)buf)[0];
+ else {
+ fprintf(stderr, "%s node has invalid size: %d\n", fname, n);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Get devtree details and create exclude_range array
+ * Also create usablemem_ranges for KEXEC_ON_CRASH
+ */
+static int get_devtree_details(unsigned long kexec_flags)
+{
+ uint64_t rma_base = -1, base;
+ uint64_t tce_base;
+ unsigned int tce_size;
+ uint64_t htab_base, htab_size;
+ uint64_t kernel_end;
+ uint64_t initrd_start, initrd_end;
+ char buf[MAXBYTES];
+ char device_tree[256] = "/proc/device-tree/";
+ char fname[256];
+ DIR *dir, *cdir;
+ FILE *file;
+ struct dirent *dentry;
+ struct stat fstat;
+ int n, i = 0;
+
+ if ((dir = opendir(device_tree)) == NULL) {
+ perror(device_tree);
+ return -1;
+ }
+
+ scan_reserved_ranges(kexec_flags, &i);
+
+ while ((dentry = readdir(dir)) != NULL) {
+ if (strncmp(dentry->d_name, "chosen", 6) &&
+ strncmp(dentry->d_name, "memory@", 7) &&
+ strcmp(dentry->d_name, "memory") &&
+ strncmp(dentry->d_name, "pci@", 4) &&
+ strncmp(dentry->d_name, "rtas", 4) &&
+ strncmp(dentry->d_name, "ibm,opal", 8))
+ continue;
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ if ((cdir = opendir(fname)) == NULL) {
+ perror(fname);
+ goto error_opendir;
+ }
+
+ if (strncmp(dentry->d_name, "chosen", 6) == 0) {
+ strcat(fname, "/linux,kernel-end");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if (fread(&kernel_end, sizeof(uint64_t), 1, file) != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ fclose(file);
+ kernel_end = be64_to_cpu(kernel_end);
+
+ /* Add kernel memory to exclude_range */
+ exclude_range[i].start = 0x0UL;
+ exclude_range[i].end = kernel_end;
+ i++;
+ if (i >= max_memory_ranges)
+ realloc_memory_ranges();
+
+ if (kexec_flags & KEXEC_ON_CRASH) {
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/linux,crashkernel-base");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if (fread(&crash_base, sizeof(uint64_t), 1,
+ file) != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ fclose(file);
+ crash_base = be64_to_cpu(crash_base);
+
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/linux,crashkernel-size");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if (fread(&crash_size, sizeof(uint64_t), 1,
+ file) != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ fclose(file);
+ crash_size = be64_to_cpu(crash_size);
+
+ if (crash_base > mem_min)
+ mem_min = crash_base;
+ if (crash_base + crash_size < mem_max)
+ mem_max = crash_base + crash_size;
+
+ add_usable_mem_rgns(0, crash_base + crash_size);
+ reserve(KDUMP_BACKUP_LIMIT, crash_base-KDUMP_BACKUP_LIMIT);
+ }
+ /*
+ * Read the first kernel's memory limit.
+ * If the first kernel is booted with mem= option then
+ * it would export "linux,memory-limit" file
+ * reflecting value for the same.
+ */
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/linux,memory-limit");
+ if ((file = fopen(fname, "r")) == NULL) {
+ if (errno != ENOENT) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ errno = 0;
+ /*
+ * File not present.
+ * fall through. On older kernel this file
+ * is not present.
+ */
+ } else {
+ if (fread(&memory_limit, sizeof(uint64_t), 1,
+ file) != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ fclose(file);
+ memory_limit = be64_to_cpu(memory_limit);
+ }
+
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/linux,htab-base");
+ if ((file = fopen(fname, "r")) == NULL) {
+ closedir(cdir);
+ if (errno == ENOENT) {
+ /* Non LPAR */
+ errno = 0;
+ continue;
+ }
+ perror(fname);
+ goto error_opendir;
+ }
+ if (fread(&htab_base, sizeof(uint64_t), 1, file) != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ fclose(file);
+ htab_base = be64_to_cpu(htab_base);
+
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/linux,htab-size");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if (fread(&htab_size, sizeof(uint64_t), 1, file) != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ fclose(file);
+ htab_size = be64_to_cpu(htab_size);
+
+ /* Add htab address to exclude_range - NON-LPAR only */
+ exclude_range[i].start = htab_base;
+ exclude_range[i].end = htab_base + htab_size;
+ i++;
+ if (i >= max_memory_ranges)
+ realloc_memory_ranges();
+
+ /* reserve the initrd_start and end locations. */
+ if (reuse_initrd) {
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/linux,initrd-start");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ /* check for 4 and 8 byte initrd offset sizes */
+ if (stat(fname, &fstat) != 0) {
+ perror(fname);
+ goto error_openfile;
+ }
+ if (fread(&initrd_start, fstat.st_size, 1, file) != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ initrd_start = be64_to_cpu(initrd_start);
+ fclose(file);
+
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/linux,initrd-end");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ /* check for 4 and 8 byte initrd offset sizes */
+ if (stat(fname, &fstat) != 0) {
+ perror(fname);
+ goto error_openfile;
+ }
+ if (fread(&initrd_end, fstat.st_size, 1, file) != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ initrd_end = be64_to_cpu(initrd_end);
+ fclose(file);
+
+ /* Add initrd address to exclude_range */
+ exclude_range[i].start = initrd_start;
+ exclude_range[i].end = initrd_end;
+ i++;
+ if (i >= max_memory_ranges)
+ realloc_memory_ranges();
+ }
+ } /* chosen */
+
+ if (strncmp(dentry->d_name, "rtas", 4) == 0) {
+ strcat(fname, "/linux,rtas-base");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if (fread(&rtas_base, sizeof(unsigned int), 1, file) != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ fclose(file);
+ rtas_base = be32_to_cpu(rtas_base);
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/rtas-size");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if (fread(&rtas_size, sizeof(unsigned int), 1, file) != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ fclose(file);
+ closedir(cdir);
+ rtas_size = be32_to_cpu(rtas_size);
+ /* Add rtas to exclude_range */
+ exclude_range[i].start = rtas_base;
+ exclude_range[i].end = rtas_base + rtas_size;
+ i++;
+ if (i >= max_memory_ranges)
+ realloc_memory_ranges();
+ if (kexec_flags & KEXEC_ON_CRASH)
+ add_usable_mem_rgns(rtas_base, rtas_size);
+ } /* rtas */
+
+ if (strncmp(dentry->d_name, "ibm,opal", 8) == 0) {
+ strcat(fname, "/opal-base-address");
+ file = fopen(fname, "r");
+ if (file == NULL) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if (fread(&opal_base, sizeof(uint64_t), 1, file) != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ opal_base = be64_to_cpu(opal_base);
+ fclose(file);
+
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/opal-runtime-size");
+ file = fopen(fname, "r");
+ if (file == NULL) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if (fread(&opal_size, sizeof(uint64_t), 1, file) != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ fclose(file);
+ closedir(cdir);
+ opal_size = be64_to_cpu(opal_size);
+ /* Add OPAL to exclude_range */
+ exclude_range[i].start = opal_base;
+ exclude_range[i].end = opal_base + opal_size;
+ i++;
+ if (i >= max_memory_ranges)
+ realloc_memory_ranges();
+ if (kexec_flags & KEXEC_ON_CRASH)
+ add_usable_mem_rgns(opal_base, opal_size);
+ } /* ibm,opal */
+
+ if (!strncmp(dentry->d_name, "memory@", 7) ||
+ !strcmp(dentry->d_name, "memory")) {
+ strcat(fname, "/reg");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if ((n = fread(buf, 1, MAXBYTES, file)) < 0) {
+ perror(fname);
+ goto error_openfile;
+ }
+ base = be64_to_cpu(((uint64_t *)buf)[0]);
+ if (base < rma_base) {
+ rma_base = base;
+ rma_top = base + be64_to_cpu(((uint64_t *)buf)[1]);
+ }
+
+ fclose(file);
+ closedir(cdir);
+ } /* memory */
+
+ if (strncmp(dentry->d_name, "pci@", 4) == 0) {
+ strcat(fname, "/linux,tce-base");
+ if ((file = fopen(fname, "r")) == NULL) {
+ closedir(cdir);
+ if (errno == ENOENT) {
+ /* Non LPAR */
+ errno = 0;
+ continue;
+ }
+ perror(fname);
+ goto error_opendir;
+ }
+ if (fread(&tce_base, sizeof(uint64_t), 1, file) != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ fclose(file);
+ tce_base = be64_to_cpu(tce_base);
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/linux,tce-size");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if (fread(&tce_size, sizeof(unsigned int), 1, file) != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ fclose(file);
+ tce_size = be32_to_cpu(tce_size);
+ /* Add tce to exclude_range - NON-LPAR only */
+ exclude_range[i].start = tce_base;
+ exclude_range[i].end = tce_base + tce_size;
+ i++;
+ if (i >= max_memory_ranges)
+ realloc_memory_ranges();
+ if (kexec_flags & KEXEC_ON_CRASH)
+ add_usable_mem_rgns(tce_base, tce_size);
+ closedir(cdir);
+ } /* pci */
+ }
+ closedir(dir);
+
+ nr_exclude_ranges = i;
+
+ sort_ranges();
+
+ int k;
+ for (k = 0; k < i; k++)
+ dbgprintf("exclude_range sorted exclude_range[%d] "
+ "start:%llx, end:%llx\n", k, exclude_range[k].start,
+ exclude_range[k].end);
+
+ return 0;
+
+error_openfile:
+ fclose(file);
+error_opencdir:
+ closedir(cdir);
+error_opendir:
+ closedir(dir);
+ return -1;
+}
+
+/* Setup a sorted list of memory ranges. */
+int setup_memory_ranges(unsigned long kexec_flags)
+{
+ int i, j = 0;
+
+ /* Get the base list of memory ranges from /proc/device-tree/memory
+ * nodes. Build list of ranges to be excluded from valid memory
+ */
+
+ if (get_base_ranges())
+ goto out;
+ if (get_devtree_details(kexec_flags))
+ goto out;
+
+ for (i = 0; i < nr_exclude_ranges; i++) {
+ /* If first exclude range does not start with 0, include the
+ * first hole of valid memory from 0 - exclude_range[0].start
+ */
+ if (i == 0) {
+ if (exclude_range[i].start != 0) {
+ memory_range[j].start = 0;
+ memory_range[j].end = exclude_range[i].start - 1;
+ memory_range[j].type = RANGE_RAM;
+ j++;
+ if (j >= max_memory_ranges)
+ realloc_memory_ranges();
+ }
+ } /* i == 0 */
+ /* If the last exclude range does not end at memory_max, include
+ * the last hole of valid memory from exclude_range[last].end -
+ * memory_max
+ */
+ if (i == nr_exclude_ranges - 1) {
+ if (exclude_range[i].end < memory_max) {
+ memory_range[j].start = exclude_range[i].end + 1;
+ memory_range[j].end = memory_max;
+ memory_range[j].type = RANGE_RAM;
+ j++;
+ if (j >= max_memory_ranges)
+ realloc_memory_ranges();
+ /* Limit the end to rma_top */
+ if (memory_range[j-1].start >= rma_top) {
+ j--;
+ break;
+ }
+ if ((memory_range[j-1].start < rma_top) &&
+ (memory_range[j-1].end >= rma_top)) {
+ memory_range[j-1].end = rma_top;
+ break;
+ }
+ continue;
+ }
+ } /* i == nr_exclude_ranges - 1 */
+ /* contiguous exclude ranges - skip */
+ if (exclude_range[i+1].start == exclude_range[i].end + 1)
+ continue;
+ memory_range[j].start = exclude_range[i].end + 1;
+ memory_range[j].end = exclude_range[i+1].start - 1;
+ memory_range[j].type = RANGE_RAM;
+ j++;
+ if (j >= max_memory_ranges)
+ realloc_memory_ranges();
+ /* Limit range to rma_top */
+ if (memory_range[j-1].start >= rma_top) {
+ j--;
+ break;
+ }
+ if ((memory_range[j-1].start < rma_top) &&
+ (memory_range[j-1].end >= rma_top)) {
+ memory_range[j-1].end = rma_top;
+ break;
+ }
+ }
+ nr_memory_ranges = j;
+
+ int k;
+ for (k = 0; k < j; k++)
+ dbgprintf("setup_memory_ranges memory_range[%d] "
+ "start:%llx, end:%llx\n", k, memory_range[k].start,
+ memory_range[k].end);
+ return 0;
+
+out:
+ cleanup_memory_ranges();
+ return -1;
+}
+
+/* Return a list of valid memory ranges */
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags)
+{
+ /* allocate memory_range dynamically */
+ max_memory_ranges = 1;
+
+ if (alloc_memory_ranges())
+ return -1;
+ if (setup_memory_ranges(kexec_flags))
+ return -1;
+
+ /*
+ * copy the memory here, another realloc_memory_ranges might
+ * corrupt the old memory
+ */
+ *range = calloc(sizeof(struct memory_range), nr_memory_ranges);
+ if (*range == NULL)
+ return -1;
+ memmove(*range, memory_range,
+ sizeof(struct memory_range) * nr_memory_ranges);
+
+ *ranges = nr_memory_ranges;
+ dbgprintf("get memory ranges:%d\n", nr_memory_ranges);
+ return 0;
+}
+
+struct file_type file_type[] = {
+ { "elf-ppc64", elf_ppc64_probe, elf_ppc64_load, elf_ppc64_usage },
+};
+int file_types = sizeof(file_type) / sizeof(file_type[0]);
+
+void arch_usage(void)
+{
+ fprintf(stderr, " --elf64-core-headers Prepare core headers in ELF64 format\n");
+ fprintf(stderr, " --dt-no-old-root Do not reuse old kernel root= param.\n" \
+ " while creating flatten device tree.\n");
+}
+
+struct arch_options_t arch_options = {
+ .core_header_type = CORE_TYPE_ELF64,
+};
+
+int arch_process_options(int argc, char **argv)
+{
+ /* We look for all options so getopt_long doesn't start reordering
+ * argv[] before file_type[n].load() gets a look in.
+ */
+ static const struct option options[] = {
+ KEXEC_ALL_OPTIONS
+ { 0, 0, NULL, 0 },
+ };
+ static const char short_options[] = KEXEC_ALL_OPT_STR;
+ int opt;
+
+ opterr = 0; /* Don't complain about unrecognized options here */
+ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch(opt) {
+ default:
+ break;
+ case OPT_ELF64_CORE:
+ arch_options.core_header_type = CORE_TYPE_ELF64;
+ break;
+ case OPT_DT_NO_OLD_ROOT:
+ dt_no_old_root = 1;
+ break;
+ }
+ }
+ /* Reset getopt for the next pass; called in other source modules */
+ opterr = 1;
+ optind = 1;
+ return 0;
+}
+
+const struct arch_map_entry arches[] = {
+ /* We are running a 32-bit kexec-tools on 64-bit ppc64.
+ * So pass KEXEC_ARCH_PPC64 here
+ */
+ { "ppc64", KEXEC_ARCH_PPC64 },
+ { "ppc64le", KEXEC_ARCH_PPC64 },
+ { NULL, 0 },
+};
+
+int arch_compat_trampoline(struct kexec_info *UNUSED(info))
+{
+ return 0;
+}
+
+void arch_update_purgatory(struct kexec_info *UNUSED(info))
+{
+}
diff --git a/kexec/arch/ppc64/kexec-ppc64.h b/kexec/arch/ppc64/kexec-ppc64.h
new file mode 100644
index 0000000..434b4bf
--- /dev/null
+++ b/kexec/arch/ppc64/kexec-ppc64.h
@@ -0,0 +1,45 @@
+#ifndef KEXEC_PPC64_H
+#define KEXEC_PPC64_H
+
+#define PATH_LEN 256
+#define MAXBYTES 128
+#define MAX_LINE 160
+#define CORE_TYPE_ELF32 1
+#define CORE_TYPE_ELF64 2
+
+#define BOOT_BLOCK_VERSION 17
+#define BOOT_BLOCK_LAST_COMP_VERSION 17
+#if (BOOT_BLOCK_VERSION < 16)
+# define NEED_STRUCTURE_BLOCK_EXTRA_PAD
+#endif
+#define HAVE_DYNAMIC_MEMORY
+#define NEED_RESERVE_DTB
+
+extern int get_devtree_value(const char *fname, unsigned long long *pvalue);
+
+int setup_memory_ranges(unsigned long kexec_flags);
+
+int elf_ppc64_probe(const char *buf, off_t len);
+int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void elf_ppc64_usage(void);
+
+struct mem_ehdr;
+unsigned long my_r2(const struct mem_ehdr *ehdr);
+
+extern uint64_t initrd_base, initrd_size;
+extern int max_memory_ranges;
+extern unsigned char reuse_initrd;
+
+struct arch_options_t {
+ int core_header_type;
+};
+
+typedef struct mem_rgns {
+ unsigned int size;
+ struct memory_range *ranges;
+} mem_rgns_t;
+
+extern mem_rgns_t usablemem_rgns;
+
+#endif /* KEXEC_PPC64_H */
diff --git a/kexec/arch/ppc64/kexec-zImage-ppc64.c b/kexec/arch/ppc64/kexec-zImage-ppc64.c
new file mode 100644
index 0000000..e946205
--- /dev/null
+++ b/kexec/arch/ppc64/kexec-zImage-ppc64.c
@@ -0,0 +1,184 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2004 Adam Litke (agl@us.ibm.com)
+ * Copyright (C) 2004 IBM Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <linux/elf.h>
+#include "../../kexec.h"
+
+#define MAX_HEADERS 32
+
+int zImage_ppc64_probe(FILE *file)
+{
+ Elf32_Ehdr elf;
+ int valid;
+
+ if (fseek(file, 0, SEEK_SET) < 0) {
+ fprintf(stderr, "seek error: %s\n",
+ strerror(errno));
+ return -1;
+ }
+ if (fread(&elf, sizeof(Elf32_Ehdr), 1, file) != 1) {
+ fprintf(stderr, "read error: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
+ if (elf.e_machine == EM_PPC64) {
+ fprintf(stderr, "Elf64 not supported\n");
+ return -1;
+ }
+
+ valid = (elf.e_ident[EI_MAG0] == ELFMAG0 &&
+ elf.e_ident[EI_MAG1] == ELFMAG1 &&
+ elf.e_ident[EI_MAG2] == ELFMAG2 &&
+ elf.e_ident[EI_MAG3] == ELFMAG3 &&
+ elf.e_ident[EI_CLASS] == ELFCLASS32 &&
+ elf.e_ident[EI_DATA] == ELFDATA2MSB &&
+ elf.e_type == ET_EXEC &&
+ elf.e_machine == EM_PPC);
+
+ return valid ? 0 : -1;
+}
+
+int zImage_ppc64_load(FILE *file, int UNUSED(argc), char **UNUSED(argv),
+ void **ret_entry, struct kexec_segment **ret_segments,
+ int *ret_nr_segments)
+{
+ Elf32_Ehdr elf;
+ Elf32_Phdr *p, *ph;
+ struct kexec_segment *segment;
+ int i;
+ unsigned long memsize, filesize, offset, load_loc = 0;
+
+ /* Parse command line arguments */
+
+ /* Read in the Elf32 header */
+ if (fseek(file, 0, SEEK_SET) < 0) {
+ perror("seek error:");
+ return -1;
+ }
+ if (fread(&elf, sizeof(Elf32_Ehdr), 1, file) != 1) {
+ perror("read error: ");
+ return -1;
+ }
+ if (elf.e_phnum > MAX_HEADERS) {
+ fprintf(stderr,
+ "Only kernels with %i program headers are supported\n",
+ MAX_HEADERS);
+ return -1;
+ }
+
+ /* Read the section header */
+ ph = (Elf32_Phdr *)malloc(sizeof(Elf32_Phdr) * elf.e_phnum);
+ if (ph == 0) {
+ perror("malloc failed: ");
+ return -1;
+ }
+ if (fseek(file, elf.e_phoff, SEEK_SET) < 0) {
+ perror("seek failed: ");
+ free(ph);
+ return -1;
+ }
+ if (fread(ph, sizeof(Elf32_Phdr) * elf.e_phnum, 1, file) != 1) {
+ perror("read error: ");
+ free(ph);
+ return -1;
+ }
+
+ *ret_segments = malloc(elf.e_phnum * sizeof(struct kexec_segment));
+ if (*ret_segments == 0) {
+ fprintf(stderr, "malloc failed: %s\n",
+ strerror(errno));
+ free(ph);
+ return -1;
+ }
+ segment = ret_segments[0];
+
+ /* Scan through the program header */
+ memsize = filesize = offset = 0;
+ p = ph;
+ for (i = 0; i < elf.e_phnum; ++i, ++p) {
+ if (p->p_type != PT_LOAD || p->p_offset == 0)
+ continue;
+ if (memsize == 0) {
+ offset = p->p_offset;
+ memsize = p->p_memsz;
+ filesize = p->p_filesz;
+ load_loc = p->p_vaddr;
+ } else {
+ memsize = p->p_offset + p->p_memsz - offset;
+ filesize = p->p_offset + p->p_filesz - offset;
+ }
+ }
+ if (memsize == 0) {
+ fprintf(stderr, "Can't find a loadable segment.\n");
+ free(ph);
+ return -1;
+ }
+
+ /* Load program segments */
+ p = ph;
+ segment->buf = malloc(filesize);
+ if (segment->buf == 0) {
+ perror("malloc failed: ");
+ free(ph);
+ return -1;
+ }
+ for (i = 0; i < elf.e_phnum; ++i, ++p) {
+ unsigned long mem_offset;
+ if (p->p_type != PT_LOAD || p->p_offset == 0)
+ continue;
+
+ /* skip to the actual image */
+ if (fseek(file, p->p_offset, SEEK_SET) < 0) {
+ perror("seek error: ");
+ free(ph);
+ return -1;
+ }
+ mem_offset = p->p_vaddr - load_loc;
+ if (fread((void *)segment->buf+mem_offset, p->p_filesz, 1,
+ file) != 1) {
+ perror("read error: ");
+ free(ph);
+ return -1;
+ }
+ }
+ segment->mem = (void *) load_loc;
+ segment->memsz = memsize;
+ segment->bufsz = filesize;
+ *ret_entry = (void *)(uintptr_t)elf.e_entry;
+ *ret_nr_segments = i - 1;
+ free(ph);
+ return 0;
+}
+
+void zImage_ppc64_usage(void)
+{
+ fprintf(stderr, "zImage support is still broken\n");
+}
diff --git a/kexec/arch/s390/Makefile b/kexec/arch/s390/Makefile
new file mode 100644
index 0000000..fab3e68
--- /dev/null
+++ b/kexec/arch/s390/Makefile
@@ -0,0 +1,11 @@
+#
+# kexec s390 (linux booting linux)
+#
+s390_KEXEC_SRCS = kexec/arch/s390/kexec-s390.c
+s390_KEXEC_SRCS += kexec/arch/s390/kexec-image.c
+s390_KEXEC_SRCS += kexec/arch/s390/kexec-elf-rel-s390.c
+s390_KEXEC_SRCS += kexec/arch/s390/crashdump-s390.c
+
+dist += kexec/arch/s390/Makefile $(s390_KEXEC_SRCS) \
+ kexec/arch/s390/kexec-s390.h \
+ kexec/arch/s390/include/arch/options.h
diff --git a/kexec/arch/s390/crashdump-s390.c b/kexec/arch/s390/crashdump-s390.c
new file mode 100644
index 0000000..3bd9efe
--- /dev/null
+++ b/kexec/arch/s390/crashdump-s390.c
@@ -0,0 +1,90 @@
+/*
+ * kexec/arch/s390/crashdump-s390.c
+ *
+ * Copyright IBM Corp. 2011
+ *
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
+ */
+
+#ifdef __s390x__
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <elf.h>
+#include <limits.h>
+#include <string.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "../../kexec/crashdump.h"
+#include "kexec-s390.h"
+
+/*
+ * Create ELF core header
+ */
+static int create_elf_header(struct kexec_info *info, unsigned long crash_base,
+ unsigned long crash_end)
+{
+#ifdef WITH_ELF_HEADER
+ static struct memory_range crash_memory_range[MAX_MEMORY_RANGES];
+ unsigned long elfcorehdr, elfcorehdr_size, bufsz;
+ struct crash_elf_info elf_info;
+ char str[COMMAND_LINESIZE];
+ int ranges;
+ void *tmp;
+
+ memset(&elf_info, 0, sizeof(elf_info));
+
+ elf_info.data = ELFDATA2MSB;
+ elf_info.machine = EM_S390;
+ elf_info.class = ELFCLASS64;
+ elf_info.get_note_info = get_crash_notes_per_cpu;
+
+ if (get_memory_ranges_s390(crash_memory_range, &ranges, 0))
+ return -1;
+
+ if (crash_create_elf64_headers(info, &elf_info, crash_memory_range,
+ ranges, &tmp, &bufsz,
+ ELF_CORE_HEADER_ALIGN))
+ return -1;
+
+ elfcorehdr = add_buffer(info, tmp, bufsz, bufsz, 1024,
+ crash_base, crash_end, -1);
+ elfcorehdr_size = bufsz;
+ snprintf(str, sizeof(str), " elfcorehdr=%ld@%ldK\n",
+ elfcorehdr_size, elfcorehdr / 1024);
+ if (command_line_add(info, str))
+ return -1;
+#endif
+ return 0;
+}
+
+/*
+ * Load additional segments for kdump kernel
+ */
+int load_crashdump_segments(struct kexec_info *info, unsigned long crash_base,
+ unsigned long crash_end)
+{
+ unsigned long crash_size = crash_size = crash_end - crash_base + 1;
+
+ if (create_elf_header(info, crash_base, crash_end))
+ return -1;
+ elf_rel_build_load(info, &info->rhdr, (const char *) purgatory,
+ purgatory_size, crash_base + 0x2000,
+ crash_base + 0x10000, -1, 0);
+ elf_rel_set_symbol(&info->rhdr, "crash_base", &crash_base,
+ sizeof(crash_base));
+ elf_rel_set_symbol(&info->rhdr, "crash_size", &crash_size,
+ sizeof(crash_size));
+ info->entry = (void *) elf_rel_get_addr(&info->rhdr, "purgatory_start");
+ return 0;
+}
+#else
+/*
+ * kdump is not available for s390
+ */
+int load_crashdump_segments(struct kexec_info *info, unsigned long crash_base,
+ unsigned long crash_end)
+{
+ return -1;
+}
+#endif
diff --git a/kexec/arch/s390/include/arch/options.h b/kexec/arch/s390/include/arch/options.h
new file mode 100644
index 0000000..c150244
--- /dev/null
+++ b/kexec/arch/s390/include/arch/options.h
@@ -0,0 +1,40 @@
+#ifndef KEXEC_ARCH_S390_OPTIONS_H
+#define KEXEC_ARCH_S390_OPTIONS_H
+
+#define OPT_ARCH_MAX (OPT_MAX+0)
+#define OPT_APPEND (OPT_MAX+0)
+#define OPT_RAMDISK (OPT_MAX+1)
+#define OPT_REUSE_CMDLINE (OPT_MAX+2)
+
+/* Options relevant to the architecture (excluding loader-specific ones),
+ * in this case none:
+ */
+#define KEXEC_ARCH_OPTIONS \
+ KEXEC_OPTIONS \
+
+#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
+
+/* The following two #defines list ALL of the options added by all of the
+ * architecture's loaders.
+ * o main() uses this complete list to scan for its options, ignoring
+ * arch-specific/loader-specific ones.
+ * o Then, arch_process_options() uses this complete list to scan for its
+ * options, ignoring general/loader-specific ones.
+ * o Then, the file_type[n].load re-scans for options, using
+ * KEXEC_ARCH_OPTIONS plus its loader-specific options subset.
+ * Any unrecognised options cause an error here.
+ *
+ * This is done so that main()'s/arch_process_options()'s getopt_long() calls
+ * don't choose a kernel filename from random arguments to options they don't
+ * recognise -- as they now recognise (if not act upon) all possible options.
+ */
+#define KEXEC_ALL_OPTIONS \
+ KEXEC_ARCH_OPTIONS \
+ {"command-line", 1, 0, OPT_APPEND}, \
+ {"append", 1, 0, OPT_APPEND}, \
+ {"initrd", 1, 0, OPT_RAMDISK}, \
+ {"reuse-cmdline", 0, 0, OPT_REUSE_CMDLINE },
+
+#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR
+
+#endif /* KEXEC_ARCH_S390_OPTIONS_H */
diff --git a/kexec/arch/s390/kexec-elf-rel-s390.c b/kexec/arch/s390/kexec-elf-rel-s390.c
new file mode 100644
index 0000000..91ba86a
--- /dev/null
+++ b/kexec/arch/s390/kexec-elf-rel-s390.c
@@ -0,0 +1,78 @@
+/*
+ * kexec/arch/s390/kexec-elf-rel-s390.c
+ *
+ * Copyright IBM Corp. 2005,2011
+ *
+ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
+ *
+ */
+
+#include <stdio.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+
+int machine_verify_elf_rel(struct mem_ehdr *ehdr)
+{
+ if (ehdr->ei_data != ELFDATA2MSB)
+ return 0;
+ if (ehdr->ei_class != ELFCLASS64)
+ return 0;
+ if (ehdr->e_machine != EM_S390)
+ return 0;
+ return 1;
+}
+
+void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr),
+ struct mem_sym *UNUSED(sym),
+ unsigned long r_type,
+ void *loc,
+ unsigned long address,
+ unsigned long val)
+{
+ switch (r_type) {
+ case R_390_8: /* Direct 8 bit. */
+ case R_390_12: /* Direct 12 bit. */
+ case R_390_16: /* Direct 16 bit. */
+ case R_390_20: /* Direct 20 bit. */
+ case R_390_32: /* Direct 32 bit. */
+ case R_390_64: /* Direct 64 bit. */
+ if (r_type == R_390_8)
+ *(unsigned char *) loc = val;
+ else if (r_type == R_390_12)
+ *(unsigned short *) loc = (val & 0xfff) |
+ (*(unsigned short *) loc & 0xf000);
+ else if (r_type == R_390_16)
+ *(unsigned short *) loc = val;
+ else if (r_type == R_390_20)
+ *(unsigned int *) loc =
+ (*(unsigned int *) loc & 0xf00000ff) |
+ (val & 0xfff) << 16 | (val & 0xff000) >> 4;
+ else if (r_type == R_390_32)
+ *(unsigned int *) loc = val;
+ else if (r_type == R_390_64)
+ *(unsigned long *) loc = val;
+ break;
+ case R_390_PC16: /* PC relative 16 bit. */
+ case R_390_PC16DBL: /* PC relative 16 bit shifted by 1. */
+ case R_390_PC32DBL: /* PC relative 32 bit shifted by 1. */
+ case R_390_PLT32DBL: /* 32 bit PC rel. PLT shifted by 1. */
+ case R_390_PC32: /* PC relative 32 bit. */
+ case R_390_PC64: /* PC relative 64 bit. */
+ val -= address;
+ if (r_type == R_390_PC16)
+ *(unsigned short *) loc = val;
+ else if (r_type == R_390_PC16DBL)
+ *(unsigned short *) loc = val >> 1;
+ else if (r_type == R_390_PC32DBL || r_type == R_390_PLT32DBL)
+ *(unsigned int *) loc = val >> 1;
+ else if (r_type == R_390_PC32)
+ *(unsigned int *) loc = val;
+ else if (r_type == R_390_PC64)
+ *(unsigned long *) loc = val;
+ break;
+ default:
+ die("Unknown rela relocation: 0x%lx 0x%lx\n", r_type, address);
+ break;
+ }
+}
diff --git a/kexec/arch/s390/kexec-image.c b/kexec/arch/s390/kexec-image.c
new file mode 100644
index 0000000..69aaf96
--- /dev/null
+++ b/kexec/arch/s390/kexec-image.c
@@ -0,0 +1,236 @@
+/*
+ * kexec/arch/s390/kexec-image.c
+ *
+ * (C) Copyright IBM Corp. 2005
+ *
+ * Author(s): Rolf Adelsberger <adelsberger@de.ibm.com>
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <getopt.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "../../kexec/crashdump.h"
+#include "kexec-s390.h"
+#include <arch/options.h>
+#include <fcntl.h>
+
+static uint64_t crash_base, crash_end;
+
+static void add_segment_check(struct kexec_info *info, const void *buf,
+ size_t bufsz, unsigned long base, size_t memsz)
+{
+ if (info->kexec_flags & KEXEC_ON_CRASH)
+ if (base + memsz > crash_end - crash_base)
+ die("Not enough crashkernel memory to load segments\n");
+ add_segment(info, buf, bufsz, crash_base + base, memsz);
+}
+
+int command_line_add(struct kexec_info *info, const char *str)
+{
+ char *tmp = NULL;
+
+ tmp = concat_cmdline(info->command_line, str);
+ if (!tmp) {
+ fprintf(stderr, "out of memory\n");
+ return -1;
+ }
+
+ free(info->command_line);
+ info->command_line = tmp;
+ return 0;
+}
+
+int image_s390_load_file(int argc, char **argv, struct kexec_info *info)
+{
+ const char *ramdisk = NULL;
+ int opt;
+
+ static const struct option options[] =
+ {
+ KEXEC_ALL_OPTIONS
+ {0, 0, 0, 0},
+ };
+ static const char short_options[] = KEXEC_OPT_STR "";
+
+ while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch(opt) {
+ case OPT_APPEND:
+ if (command_line_add(info, optarg))
+ return -1;
+ break;
+ case OPT_RAMDISK:
+ ramdisk = optarg;
+ break;
+ case OPT_REUSE_CMDLINE:
+ free(info->command_line);
+ info->command_line = get_command_line();
+ break;
+ }
+ }
+
+ if (ramdisk) {
+ info->initrd_fd = open(ramdisk, O_RDONLY);
+ if (info->initrd_fd == -1) {
+ fprintf(stderr, "Could not open initrd file %s:%s\n",
+ ramdisk, strerror(errno));
+ free(info->command_line);
+ info->command_line = NULL;
+ return -1;
+ }
+ }
+
+ if (info->command_line)
+ info->command_line_len = strlen(info->command_line) + 1;
+ else
+ info->command_line_len = 0;
+ return 0;
+}
+
+int
+image_s390_load(int argc, char **argv, const char *kernel_buf,
+ off_t kernel_size, struct kexec_info *info)
+{
+ void *krnl_buffer;
+ char *rd_buffer;
+ const char *ramdisk;
+ off_t ramdisk_len;
+ unsigned int ramdisk_origin;
+ int opt, ret = -1;
+
+ if (info->file_mode)
+ return image_s390_load_file(argc, argv, info);
+
+ static const struct option options[] =
+ {
+ KEXEC_ALL_OPTIONS
+ {0, 0, 0, 0},
+ };
+ static const char short_options[] = KEXEC_OPT_STR "";
+
+ ramdisk = NULL;
+ ramdisk_len = 0;
+ ramdisk_origin = 0;
+
+ while ((opt = getopt_long(argc,argv,short_options,options,0)) != -1) {
+ switch(opt) {
+ case OPT_APPEND:
+ if (command_line_add(info, optarg))
+ return -1;
+ break;
+ case OPT_REUSE_CMDLINE:
+ free(info->command_line);
+ info->command_line = get_command_line();
+ break;
+ case OPT_RAMDISK:
+ ramdisk = optarg;
+ break;
+ }
+ }
+
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ if (parse_iomem_single("Crash kernel\n", &crash_base,
+ &crash_end))
+ goto out;
+ }
+
+ /* Add kernel segment */
+ add_segment_check(info, kernel_buf + IMAGE_READ_OFFSET,
+ kernel_size - IMAGE_READ_OFFSET, IMAGE_READ_OFFSET,
+ kernel_size - IMAGE_READ_OFFSET);
+
+ /* We do want to change the kernel image */
+ krnl_buffer = (void *) kernel_buf + IMAGE_READ_OFFSET;
+
+ /*
+ * Load ramdisk if present: If image is larger than RAMDISK_ORIGIN_ADDR,
+ * we load the ramdisk directly behind the image with 1 MiB alignment.
+ */
+ if (ramdisk) {
+ rd_buffer = slurp_file_mmap(ramdisk, &ramdisk_len);
+ if (rd_buffer == NULL) {
+ fprintf(stderr, "Could not read ramdisk.\n");
+ goto out;
+ }
+ ramdisk_origin = MAX(RAMDISK_ORIGIN_ADDR, kernel_size);
+ ramdisk_origin = _ALIGN_UP(ramdisk_origin, 0x100000);
+ add_segment_check(info, rd_buffer, ramdisk_len,
+ ramdisk_origin, ramdisk_len);
+ }
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ if (load_crashdump_segments(info, crash_base, crash_end))
+ goto out;
+ } else {
+ info->entry = (void *) IMAGE_READ_OFFSET;
+ }
+
+ /* Register the ramdisk and crashkernel memory in the kernel. */
+ {
+ unsigned long long *tmp;
+
+ tmp = krnl_buffer + INITRD_START_OFFS;
+ *tmp = (unsigned long long) ramdisk_origin;
+
+ tmp = krnl_buffer + INITRD_SIZE_OFFS;
+ *tmp = (unsigned long long) ramdisk_len;
+
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ tmp = krnl_buffer + OLDMEM_BASE_OFFS;
+ *tmp = crash_base;
+
+ tmp = krnl_buffer + OLDMEM_SIZE_OFFS;
+ *tmp = crash_end - crash_base + 1;
+ }
+ }
+
+ if (info->command_line) {
+ unsigned long maxsize;
+ char *dest = krnl_buffer + COMMAND_LINE_OFFS;
+
+ maxsize = *(unsigned long *)(krnl_buffer + MAX_COMMAND_LINESIZE_OFFS);
+ if (!maxsize)
+ maxsize = LEGACY_COMMAND_LINESIZE;
+
+ if (strlen(info->command_line) > maxsize-1) {
+ fprintf(stderr, "command line too long, maximum allowed size %ld\n",
+ maxsize-1);
+ goto out;
+ }
+ strncpy(dest, info->command_line, maxsize-1);
+ dest[maxsize-1] = '\0';
+ }
+ ret = 0;
+out:
+ free(info->command_line);
+ info->command_line = NULL;
+ return ret;
+}
+
+int
+image_s390_probe(const char *UNUSED(kernel_buf), off_t UNUSED(kernel_size))
+{
+ /*
+ * Can't reliably tell if an image is valid,
+ * therefore everything is valid.
+ */
+ return 0;
+}
+
+void
+image_s390_usage(void)
+{
+ printf("--command-line=STRING Set the kernel command line to STRING.\n"
+ "--append=STRING Set the kernel command line to STRING.\n"
+ "--initrd=FILENAME Use the file FILENAME as a ramdisk.\n"
+ "--reuse-cmdline Use kernel command line from running system.\n"
+ );
+}
diff --git a/kexec/arch/s390/kexec-s390.c b/kexec/arch/s390/kexec-s390.c
new file mode 100644
index 0000000..33ba6b9
--- /dev/null
+++ b/kexec/arch/s390/kexec-s390.c
@@ -0,0 +1,269 @@
+/*
+ * kexec/arch/s390/kexec-s390.c
+ *
+ * Copyright IBM Corp. 2005,2011
+ *
+ * Author(s): Rolf Adelsberger <adelsberger@de.ibm.com>
+ * Michael Holzheu <holzheu@linux.vnet.ibm.com>
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "kexec-s390.h"
+#include <arch/options.h>
+
+static struct memory_range memory_range[MAX_MEMORY_RANGES];
+
+/*
+ * Read string from file
+ */
+static void read_str(char *string, const char *path, size_t len)
+{
+ size_t rc;
+ FILE *fh;
+
+ fh = fopen(path, "rb");
+ if (fh == NULL)
+ die("Could not open \"%s\"", path);
+ rc = fread(string, 1, len - 1, fh);
+ if (rc == 0 && ferror(fh))
+ die("Could not read \"%s\"", path);
+ fclose(fh);
+ string[rc] = 0;
+ if (string[strlen(string) - 1] == '\n')
+ string[strlen(string) - 1] = 0;
+}
+
+/*
+ * Return number of memory chunks
+ */
+static int memory_range_cnt(struct memory_range chunks[])
+{
+ int i;
+
+ for (i = 0; i < MAX_MEMORY_RANGES; i++) {
+ if (chunks[i].end == 0)
+ break;
+ }
+ return i;
+}
+
+/*
+ * Create memory hole with given address and size
+ *
+ * lh = local hole
+ */
+static void add_mem_hole(struct memory_range chunks[], unsigned long addr,
+ unsigned long size)
+{
+ unsigned long lh_start, lh_end, lh_size, chunk_cnt;
+ int i;
+
+ chunk_cnt = memory_range_cnt(chunks);
+
+ for (i = 0; i < chunk_cnt; i++) {
+ if (addr + size <= chunks[i].start)
+ break;
+ if (addr > chunks[i].end)
+ continue;
+ lh_start = MAX(addr, chunks[i].start);
+ lh_end = MIN(addr + size - 1, chunks[i].end);
+ lh_size = lh_end - lh_start + 1;
+ if (lh_start == chunks[i].start && lh_end == chunks[i].end) {
+ /* Remove chunk */
+ memmove(&chunks[i], &chunks[i + 1],
+ sizeof(struct memory_range) *
+ (MAX_MEMORY_RANGES - (i + 1)));
+ memset(&chunks[MAX_MEMORY_RANGES - 1], 0,
+ sizeof(struct memory_range));
+ chunk_cnt--;
+ i--;
+ } else if (lh_start == chunks[i].start) {
+ /* Make chunk smaller at start */
+ chunks[i].start = chunks[i].start + lh_size;
+ break;
+ } else if (lh_end == chunks[i].end) {
+ /* Make chunk smaller at end */
+ chunks[i].end = lh_start - 1;
+ } else {
+ /* Split chunk into two */
+ if (chunk_cnt >= MAX_MEMORY_RANGES)
+ die("Unable to create memory hole: %i", i);
+ memmove(&chunks[i + 1], &chunks[i],
+ sizeof(struct memory_range) *
+ (MAX_MEMORY_RANGES - (i + 1)));
+ chunks[i + 1].start = lh_start + lh_size;
+ chunks[i].end = lh_start - 1;
+ break;
+ }
+ }
+}
+
+/*
+ * Remove offline memory from memory chunks
+ */
+static void remove_offline_memory(struct memory_range memory_range[])
+{
+ unsigned long block_size, chunk_nr;
+ struct dirent *dirent;
+ char path[PATH_MAX];
+ char str[64];
+ DIR *dir;
+
+ read_str(str, "/sys/devices/system/memory/block_size_bytes",
+ sizeof(str));
+ sscanf(str, "%lx", &block_size);
+
+ dir = opendir("/sys/devices/system/memory");
+ if (!dir)
+ die("Could not read \"/sys/devices/system/memory\"");
+ while ((dirent = readdir(dir))) {
+ if (sscanf(dirent->d_name, "memory%ld\n", &chunk_nr) != 1)
+ continue;
+ sprintf(path, "/sys/devices/system/memory/%s/state",
+ dirent->d_name);
+ read_str(str, path, sizeof(str));
+ if (strncmp(str, "offline", 6) != 0)
+ continue;
+ add_mem_hole(memory_range, chunk_nr * block_size, block_size);
+ }
+ closedir(dir);
+}
+
+/*
+ * Get memory ranges of type "System RAM" from /proc/iomem. If with_crashk=1
+ * then also type "Crash kernel" is added.
+ */
+int get_memory_ranges_s390(struct memory_range memory_range[], int *ranges,
+ int with_crashk)
+{
+ char crash_kernel[] = "Crash kernel\n";
+ char sys_ram[] = "System RAM\n";
+ const char *iomem = proc_iomem();
+ FILE *fp;
+ char line[80];
+ int current_range = 0;
+
+ fp = fopen(iomem,"r");
+ if(fp == 0) {
+ fprintf(stderr,"Unable to open %s: %s\n",iomem,strerror(errno));
+ return -1;
+ }
+
+ /* Setup the compare string properly. */
+ while (fgets(line, sizeof(line), fp) != 0) {
+ unsigned long long start, end;
+ int cons;
+ char *str;
+
+ if (current_range == MAX_MEMORY_RANGES)
+ break;
+
+ sscanf(line,"%llx-%llx : %n", &start, &end, &cons);
+ str = line+cons;
+ if ((memcmp(str, sys_ram, strlen(sys_ram)) == 0) ||
+ ((memcmp(str, crash_kernel, strlen(crash_kernel)) == 0) &&
+ with_crashk)) {
+ memory_range[current_range].start = start;
+ memory_range[current_range].end = end;
+ memory_range[current_range].type = RANGE_RAM;
+ current_range++;
+ }
+ else {
+ continue;
+ }
+ }
+ fclose(fp);
+ remove_offline_memory(memory_range);
+ *ranges = memory_range_cnt(memory_range);
+ return 0;
+}
+
+/*
+ * get_memory_ranges:
+ * Return a list of memory ranges by parsing the file returned by
+ * proc_iomem()
+ *
+ * INPUT:
+ * - Pointer to an array of memory_range structures.
+ * - Pointer to an integer with holds the number of memory ranges.
+ *
+ * RETURN:
+ * - 0 on normal execution.
+ * - (-1) if something went wrong.
+ */
+
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long flags)
+{
+ uint64_t start, end;
+
+ if (get_memory_ranges_s390(memory_range, ranges,
+ flags & KEXEC_ON_CRASH))
+ return -1;
+ *range = memory_range;
+ if ((flags & KEXEC_ON_CRASH) && !(flags & KEXEC_PRESERVE_CONTEXT)) {
+ if (parse_iomem_single("Crash kernel\n", &start, &end))
+ return -1;
+ if (start > mem_min)
+ mem_min = start;
+ if (end < mem_max)
+ mem_max = end;
+ }
+ return 0;
+}
+
+/* Supported file types and callbacks */
+struct file_type file_type[] = {
+ { "image", image_s390_probe, image_s390_load, image_s390_usage},
+};
+int file_types = sizeof(file_type) / sizeof(file_type[0]);
+
+
+void arch_usage(void)
+{
+}
+
+int arch_process_options(int UNUSED(argc), char **UNUSED(argv))
+{
+ return 0;
+}
+
+const struct arch_map_entry arches[] = {
+ { "s390", KEXEC_ARCH_S390 },
+ { "s390x", KEXEC_ARCH_S390 },
+ { NULL, 0 },
+};
+
+int arch_compat_trampoline(struct kexec_info *UNUSED(info))
+{
+ return 0;
+}
+
+void arch_update_purgatory(struct kexec_info *UNUSED(info))
+{
+}
+
+int is_crashkernel_mem_reserved(void)
+{
+ uint64_t start, end;
+
+ return parse_iomem_single("Crash kernel\n", &start, &end) == 0 ?
+ (start != end) : 0;
+}
+
+int get_crash_kernel_load_range(uint64_t *start, uint64_t *end)
+{
+ return parse_iomem_single("Crash kernel\n", start, end);
+}
diff --git a/kexec/arch/s390/kexec-s390.h b/kexec/arch/s390/kexec-s390.h
new file mode 100644
index 0000000..6a99518
--- /dev/null
+++ b/kexec/arch/s390/kexec-s390.h
@@ -0,0 +1,38 @@
+/*
+ * kexec/arch/s390/kexec-s390.h
+ *
+ * (C) Copyright IBM Corp. 2005
+ *
+ * Author(s): Rolf Adelsberger <adelsberger@de.ibm.com>
+ *
+ */
+
+#ifndef KEXEC_S390_H
+#define KEXEC_S390_H
+
+#define IMAGE_READ_OFFSET 0x10000
+
+#define RAMDISK_ORIGIN_ADDR 0x800000
+#define INITRD_START_OFFS 0x408
+#define INITRD_SIZE_OFFS 0x410
+#define OLDMEM_BASE_OFFS 0x418
+#define OLDMEM_SIZE_OFFS 0x420
+#define MAX_COMMAND_LINESIZE_OFFS 0x430
+#define COMMAND_LINE_OFFS 0x480
+#define LEGACY_COMMAND_LINESIZE 896
+#define MAX_MEMORY_RANGES 1024
+
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+
+extern int image_s390_load(int, char **, const char *, off_t, struct kexec_info *);
+extern int image_s390_probe(const char *, off_t);
+extern void image_s390_usage(void);
+extern int load_crashdump_segments(struct kexec_info *info,
+ unsigned long crash_base,
+ unsigned long crash_end);
+extern int get_memory_ranges_s390(struct memory_range range[], int *ranges,
+ int with_crashk);
+extern int command_line_add(struct kexec_info *info, const char *str);
+
+#endif /* KEXEC_S390_H */
diff --git a/kexec/arch/sh/Makefile b/kexec/arch/sh/Makefile
new file mode 100644
index 0000000..7cf40ae
--- /dev/null
+++ b/kexec/arch/sh/Makefile
@@ -0,0 +1,22 @@
+#
+# kexec sh (linux booting linux)
+#
+sh_KEXEC_SRCS += kexec/arch/sh/kexec-sh.c
+sh_KEXEC_SRCS += kexec/arch/sh/kexec-uImage-sh.c
+sh_KEXEC_SRCS += kexec/arch/sh/kexec-zImage-sh.c
+sh_KEXEC_SRCS += kexec/arch/sh/kexec-netbsd-sh.c
+sh_KEXEC_SRCS += kexec/arch/sh/kexec-elf-sh.c
+sh_KEXEC_SRCS += kexec/arch/sh/kexec-elf-rel-sh.c
+sh_KEXEC_SRCS += kexec/arch/sh/netbsd_booter.S
+sh_KEXEC_SRCS += kexec/arch/sh/crashdump-sh.c
+
+sh_UIMAGE = kexec/kexec-uImage.c
+
+sh_ADD_BUFFER =
+sh_ADD_SEGMENT =
+sh_VIRT_TO_PHYS =
+
+dist += kexec/arch/sh/Makefile $(sh_KEXEC_SRCS) \
+ kexec/arch/sh/kexec-sh.h \
+ kexec/arch/sh/crashdump-sh.h \
+ kexec/arch/sh/include/arch/options.h
diff --git a/kexec/arch/sh/crashdump-sh.c b/kexec/arch/sh/crashdump-sh.c
new file mode 100644
index 0000000..36e9aaf
--- /dev/null
+++ b/kexec/arch/sh/crashdump-sh.c
@@ -0,0 +1,166 @@
+/*
+ * crashdump-sh.c - crashdump for SuperH
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * Based on x86 and ppc64 implementation, written by
+ * Vivek Goyal (vgoyal@in.ibm.com), R Sharada (sharada@in.ibm.com)
+ * Copyright (C) IBM Corporation, 2005. All rights reserved
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#define _GNU_SOURCE
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-elf-boot.h"
+#include "../../kexec-syscall.h"
+#include "../../crashdump.h"
+#include "kexec-sh.h"
+#include "crashdump-sh.h"
+#include <arch/options.h>
+
+#define CRASH_MAX_MEMORY_RANGES 64
+static struct memory_range crash_memory_range[CRASH_MAX_MEMORY_RANGES];
+
+static int crash_sh_range_nr;
+static int crash_sh_memory_range_callback(void *UNUSED(data), int UNUSED(nr),
+ char *str,
+ unsigned long long base,
+ unsigned long long length)
+{
+
+ struct memory_range *range = crash_memory_range;
+ struct memory_range *range2 = crash_memory_range;
+
+ range += crash_sh_range_nr;
+
+ if (crash_sh_range_nr >= CRASH_MAX_MEMORY_RANGES) {
+ return 1;
+ }
+
+ if (strncmp(str, "System RAM\n", 11) == 0) {
+ range->start = base;
+ range->end = base + length - 1;
+ range->type = RANGE_RAM;
+ crash_sh_range_nr++;
+ }
+
+ if (strncmp(str, "Crash kernel\n", 13) == 0) {
+ if (!crash_sh_range_nr)
+ die("Unsupported /proc/iomem format\n");
+
+ range2 = range - 1;
+ if ((base + length - 1) < range2->end) {
+ range->start = base + length;
+ range->end = range2->end;
+ range->type = RANGE_RAM;
+ crash_sh_range_nr++;
+ }
+ range2->end = base - 1;
+ }
+
+ return 0;
+}
+
+/* Return a sorted list of available memory ranges. */
+static int crash_get_memory_ranges(struct memory_range **range, int *ranges)
+{
+ crash_sh_range_nr = 0;
+
+ kexec_iomem_for_each_line(NULL, crash_sh_memory_range_callback, NULL);
+ *range = crash_memory_range;
+ *ranges = crash_sh_range_nr;
+ return 0;
+}
+
+static struct crash_elf_info elf_info32 =
+{
+ class: ELFCLASS32,
+ data: ELFDATA2LSB,
+ machine: EM_SH,
+ page_offset: PAGE_OFFSET,
+};
+
+static int add_cmdline_param(char *cmdline, uint64_t addr, char *cmdstr,
+ char *byte)
+{
+ int cmdlen, len, align = 1024;
+ char str[COMMAND_LINE_SIZE], *ptr;
+
+ /* Passing in =xxxK / =xxxM format. Saves space required in cmdline.*/
+ switch (byte[0]) {
+ case 'K':
+ if (addr%align)
+ return -1;
+ addr = addr/align;
+ break;
+ case 'M':
+ addr = addr/(align *align);
+ break;
+ }
+ ptr = str;
+ strcpy(str, cmdstr);
+ ptr += strlen(str);
+ ultoa(addr, ptr);
+ strcat(str, byte);
+ len = strlen(str);
+ cmdlen = strlen(cmdline) + len;
+ if (cmdlen > (COMMAND_LINE_SIZE - 1))
+ die("Command line overflow\n");
+ strcat(cmdline, str);
+
+ dbgprintf("Command line after adding elfcorehdr: %s\n", cmdline);
+
+ return 0;
+}
+
+/* Loads additional segments in case of a panic kernel is being loaded.
+ * One segment for storing elf headers for crash memory image.
+ */
+int load_crashdump_segments(struct kexec_info *info, char* mod_cmdline)
+{
+ void *tmp;
+ unsigned long sz, elfcorehdr;
+ int nr_ranges;
+ struct memory_range *mem_range;
+
+ if (crash_get_memory_ranges(&mem_range, &nr_ranges) < 0)
+ return -1;
+
+ if (crash_create_elf32_headers(info, &elf_info32,
+ mem_range, nr_ranges,
+ &tmp, &sz,
+ ELF_CORE_HEADER_ALIGN) < 0)
+ return -1;
+
+ elfcorehdr = add_buffer_phys_virt(info, tmp, sz, sz, 1024,
+ 0, 0xffffffff, -1, 0);
+
+ dbgprintf("Created elf header segment at 0x%lx\n", elfcorehdr);
+ add_cmdline_param(mod_cmdline, elfcorehdr, " elfcorehdr=", "K");
+ add_cmdline_param(mod_cmdline, elfcorehdr - mem_min, " mem=", "K");
+
+ return 0;
+}
+
+int is_crashkernel_mem_reserved(void)
+{
+ uint64_t start, end;
+
+ return parse_iomem_single("Crash kernel\n", &start, &end) == 0 ?
+ (start != end) : 0;
+}
+
+int get_crash_kernel_load_range(uint64_t *start, uint64_t *end)
+{
+ return parse_iomem_single("Crash kernel\n", start, end);
+}
diff --git a/kexec/arch/sh/crashdump-sh.h b/kexec/arch/sh/crashdump-sh.h
new file mode 100644
index 0000000..c5d4102
--- /dev/null
+++ b/kexec/arch/sh/crashdump-sh.h
@@ -0,0 +1,9 @@
+#ifndef CRASHDUMP_SH_H
+#define CRASHDUMP_SH_H
+
+struct kexec_info;
+int load_crashdump_segments(struct kexec_info *info, char* mod_cmdline);
+
+#define PAGE_OFFSET 0x80000000
+
+#endif /* CRASHDUMP_SH_H */
diff --git a/kexec/arch/sh/include/arch/options.h b/kexec/arch/sh/include/arch/options.h
new file mode 100644
index 0000000..f923eb4
--- /dev/null
+++ b/kexec/arch/sh/include/arch/options.h
@@ -0,0 +1,42 @@
+#ifndef KEXEC_ARCH_SH_OPTIONS_H
+#define KEXEC_ARCH_SH_OPTIONS_H
+
+#define OPT_ARCH_MAX (OPT_MAX+0)
+#define OPT_APPEND (OPT_ARCH_MAX+1)
+#define OPT_EMPTYZERO (OPT_ARCH_MAX+2)
+#define OPT_NBSD_HOWTO (OPT_ARCH_MAX+3)
+#define OPT_NBSD_MROOT (OPT_ARCH_MAX+4)
+
+/* Options relevant to the architecture (excluding loader-specific ones): */
+#define KEXEC_ARCH_OPTIONS \
+ KEXEC_OPTIONS \
+ {"command-line", 1, 0, OPT_APPEND}, \
+ {"append", 1, 0, OPT_APPEND}, \
+ {"empty-zero", 1, 0, OPT_APPEND}, \
+ {"howto", 1, 0, OPT_NBSD_HOWTO}, \
+ {"miniroot", 1, 0, OPT_NBSD_MROOT},
+/* These options seem to be loader-specific rather than superh-specific, so
+ * ought to be moved to KEXEC_ALL_OPTIONS below and parsed in the relevant
+ * loader, e.g. kexec-netbsd-sh.c
+ */
+
+#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
+
+/* The following two #defines list ALL of the options added by all of the
+ * architecture's loaders.
+ * o main() uses this complete list to scan for its options, ignoring
+ * arch-specific/loader-specific ones.
+ * o Then, arch_process_options() uses this complete list to scan for its
+ * options, ignoring general/loader-specific ones.
+ * o Then, the file_type[n].load re-scans for options, using
+ * KEXEC_ARCH_OPTIONS plus its loader-specific options subset.
+ * Any unrecognised options cause an error here.
+ *
+ * This is done so that main()'s/arch_process_options()'s getopt_long() calls
+ * don't choose a kernel filename from random arguments to options they don't
+ * recognise -- as they now recognise (if not act upon) all possible options.
+ */
+#define KEXEC_ALL_OPTIONS KEXEC_ARCH_OPTIONS
+#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR
+
+#endif /* KEXEC_ARCH_SH_OPTIONS_H */
diff --git a/kexec/arch/sh/kexec-elf-rel-sh.c b/kexec/arch/sh/kexec-elf-rel-sh.c
new file mode 100644
index 0000000..3993ee8
--- /dev/null
+++ b/kexec/arch/sh/kexec-elf-rel-sh.c
@@ -0,0 +1,54 @@
+/*
+ * kexec-elf-rel-sh.c - ELF relocations for SuperH
+ * Copyright (C) 2008 Paul Mundt
+ *
+ * Based on the SHcompact module loader (arch/sh/kernel/module.c) in the
+ * Linux kernel, which is written by:
+ *
+ * Copyright (C) 2003 - 2008 Kaz Kojima & Paul Mundt
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+#include <stdio.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+
+int machine_verify_elf_rel(struct mem_ehdr *ehdr)
+{
+ /* Intentionally don't bother with endianness validation, it's
+ * configurable */
+
+ if (ehdr->ei_class != ELFCLASS32)
+ return 0;
+ if (ehdr->e_machine != EM_SH)
+ return 0;
+
+ return 1;
+}
+
+void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr),
+ struct mem_sym *UNUSED(sym), unsigned long r_type, void *orig_loc,
+ unsigned long UNUSED(address), unsigned long relocation)
+{
+ uint32_t *location = orig_loc;
+ uint32_t value;
+
+ switch (r_type) {
+ case R_SH_DIR32:
+ value = get_unaligned(location);
+ value += relocation;
+ put_unaligned(value, location);
+ break;
+ case R_SH_REL32:
+ relocation = (relocation - (uint32_t)location);
+ value = get_unaligned(location);
+ value += relocation;
+ put_unaligned(value, location);
+ break;
+ default:
+ die("Unknown rela relocation: %lu\n", r_type);
+ break;
+ }
+}
diff --git a/kexec/arch/sh/kexec-elf-sh.c b/kexec/arch/sh/kexec-elf-sh.c
new file mode 100644
index 0000000..897552c
--- /dev/null
+++ b/kexec/arch/sh/kexec-elf-sh.c
@@ -0,0 +1,136 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * Based on x86 implementation,
+ * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-elf-boot.h"
+#include <arch/options.h>
+#include "crashdump-sh.h"
+#include "kexec-sh.h"
+
+int elf_sh_probe(const char *buf, off_t len)
+{
+ struct mem_ehdr ehdr;
+ int result;
+ result = build_elf_exec_info(buf, len, &ehdr, 0);
+ if (result < 0)
+ goto out;
+
+ /* Verify the architecuture specific bits */
+ if (ehdr.e_machine != EM_SH) {
+ result = -1;
+ goto out;
+ }
+
+ result = 0;
+ out:
+ free_elf_info(&ehdr);
+ return result;
+}
+
+void elf_sh_usage(void)
+{
+ printf(" --append=STRING Set the kernel command line to STRING\n"
+ );
+}
+
+int elf_sh_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ struct mem_ehdr ehdr;
+ char *command_line;
+ char *modified_cmdline;
+ struct mem_sym sym;
+ int opt, rc;
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { 0, 0, 0, 0 },
+ };
+
+ static const char short_options[] = KEXEC_OPT_STR "";
+
+ /*
+ * Parse the command line arguments
+ */
+ command_line = modified_cmdline = 0;
+ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch(opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ case OPT_APPEND:
+ command_line = optarg;
+ break;
+ }
+ }
+
+ /* Need to append some command line parameters internally in case of
+ * taking crash dumps.
+ */
+ if (info->kexec_flags & (KEXEC_ON_CRASH | KEXEC_PRESERVE_CONTEXT)) {
+ modified_cmdline = xmalloc(COMMAND_LINE_SIZE);
+ memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE);
+ if (command_line) {
+ strncpy(modified_cmdline, command_line,
+ COMMAND_LINE_SIZE);
+ modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
+ }
+ }
+
+ /* Load the ELF executable */
+ elf_exec_build_load(info, &ehdr, buf, len, 0);
+ info->entry = (void *)virt_to_phys(ehdr.e_entry);
+
+ /* If panic kernel is being loaded, additional segments need
+ * to be created. */
+ if (info->kexec_flags & (KEXEC_ON_CRASH | KEXEC_PRESERVE_CONTEXT)) {
+ rc = load_crashdump_segments(info, modified_cmdline);
+ if (rc < 0)
+ return -1;
+ /* Use new command line. */
+ command_line = modified_cmdline;
+ }
+
+ /* If we're booting a vmlinux then fill in empty_zero_page */
+ if (elf_rel_find_symbol(&ehdr, "empty_zero_page", &sym) == 0) {
+ char *zp = (void *)ehdr.e_shdr[sym.st_shndx].sh_data;
+
+ kexec_sh_setup_zero_page(zp, 4096, command_line);
+ }
+
+ return 0;
+}
diff --git a/kexec/arch/sh/kexec-netbsd-sh.c b/kexec/arch/sh/kexec-netbsd-sh.c
new file mode 100644
index 0000000..ed93759
--- /dev/null
+++ b/kexec/arch/sh/kexec-netbsd-sh.c
@@ -0,0 +1,149 @@
+/*
+ * kexec-netbsd-sh.c - kexec netbsd loader for the SH
+ * Copyright (C) 2005 kogiidena@eggplant.ddo.jp
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#define _GNU_SOURCE
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <elf.h>
+#include <boot/elf_boot.h>
+#include <ip_checksum.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include <arch/options.h>
+
+static const int probe_debug = 0;
+extern const unsigned char netbsd_booter[];
+
+/*
+ * netbsd_sh_probe - sanity check the elf image
+ *
+ * Make sure that the file image has a reasonable chance of working.
+ */
+int netbsd_sh_probe(const char *buf, off_t UNUSED(len))
+{
+ Elf32_Ehdr *ehdr;
+
+ ehdr = (Elf32_Ehdr *)buf;
+ if(memcmp(buf, ELFMAG, SELFMAG) != 0){
+ return -1;
+ }
+ if (ehdr->e_machine != EM_SH) {
+ return -1;
+ }
+ return 0;
+}
+
+void netbsd_sh_usage(void)
+{
+ printf(
+ " --howto=VALUE NetBSD kernel boot option.\n"
+ " --miniroot=FILE NetBSD miniroot ramdisk.\n\n");
+}
+
+int netbsd_sh_load(int argc, char **argv, const char *buf, off_t UNUSED(len),
+ struct kexec_info *info)
+{
+ const char *howto, *miniroot;
+ unsigned long entry, start, size, psz;
+ char *miniroot_buf;
+ off_t miniroot_length;
+ unsigned int howto_value;
+ unsigned char *param;
+ unsigned long *paraml;
+ unsigned char *img;
+
+ int opt;
+
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ {0, 0, 0, 0},
+ };
+
+ static const char short_options[] = KEXEC_ARCH_OPT_STR "";
+
+ howto = miniroot = 0;
+ while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch (opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ case OPT_NBSD_HOWTO:
+ howto = optarg;
+ break;
+ case OPT_NBSD_MROOT:
+ miniroot = optarg;
+ break;
+ }
+ }
+
+ /* howto */
+ howto_value = 0;
+ if(howto){
+ howto_value = strtol(howto, NULL, 0);
+ }
+
+ psz = getpagesize();
+
+ /* Parse the Elf file */
+ {
+ Elf32_Ehdr *ehdr;
+ Elf32_Phdr *phdr;
+ unsigned long bbs;
+ ehdr = (Elf32_Ehdr *)buf;
+ phdr = (Elf32_Phdr *)&buf[ehdr->e_phoff];
+
+ entry = ehdr->e_entry;
+ img = (unsigned char *)&buf[phdr->p_offset];
+ start = (phdr->p_paddr) & 0x1fffffff;
+ bbs = phdr->p_filesz;
+ size = phdr->p_memsz;
+
+ if(size < bbs){
+ size = bbs;
+ }
+
+ size = _ALIGN(size, psz);
+ memset(&img[bbs], 0, size-bbs);
+ add_segment(info, img, size, start, size);
+ start += size;
+ }
+
+ /* miniroot file */
+ miniroot_buf = 0;
+ if (miniroot) {
+ miniroot_buf = slurp_file(miniroot, &miniroot_length);
+ howto_value |= 0x200;
+ size = _ALIGN(miniroot_length, psz);
+ add_segment(info, miniroot_buf, size, start, size);
+ start += size;
+ }
+
+ /* howto & bootinfo */
+ param = xmalloc(4096);
+ memset(param, 0, 4096);
+ paraml = (unsigned long *) &param[256];
+ memcpy(param, netbsd_booter, 256);
+ paraml[0] = entry;
+ paraml[1] = howto_value;
+ add_segment(info, param, 4096, start, 4096);
+
+ /* For now we don't have arguments to pass :( */
+ info->entry = (void *) (start | 0xa0000000);
+ return 0;
+}
diff --git a/kexec/arch/sh/kexec-sh.c b/kexec/arch/sh/kexec-sh.c
new file mode 100644
index 0000000..ce341c8
--- /dev/null
+++ b/kexec/arch/sh/kexec-sh.c
@@ -0,0 +1,259 @@
+/*
+ * kexec-sh.c - kexec for the SH
+ * Copyright (C) 2004 kogiidena@eggplant.ddo.jp
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#define _GNU_SOURCE
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "kexec-sh.h"
+#include <arch/options.h>
+
+#define MAX_MEMORY_RANGES 64
+static struct memory_range memory_range[MAX_MEMORY_RANGES];
+
+static int kexec_sh_memory_range_callback(void *UNUSED(data), int nr,
+ char *UNUSED(str),
+ unsigned long long base,
+ unsigned long long length)
+{
+ if (nr < MAX_MEMORY_RANGES) {
+ memory_range[nr].start = base;
+ memory_range[nr].end = base + length - 1;
+ memory_range[nr].type = RANGE_RAM;
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Return a sorted list of available memory ranges. */
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags)
+{
+ int nr, ret;
+ nr = kexec_iomem_for_each_line("System RAM\n",
+ kexec_sh_memory_range_callback, NULL);
+ *range = memory_range;
+ *ranges = nr;
+
+ /*
+ * Redefine the memory region boundaries if kernel
+ * exports the limits and if it is panic kernel.
+ * Override user values only if kernel exported values are
+ * subset of user defined values.
+ */
+ if (kexec_flags & KEXEC_ON_CRASH) {
+ unsigned long long start, end;
+
+ ret = parse_iomem_single("Crash kernel\n", &start, &end);
+ if (ret != 0) {
+ fprintf(stderr, "parse_iomem_single failed.\n");
+ return -1;
+ }
+
+ if (start > mem_min)
+ mem_min = start;
+ if (end < mem_max)
+ mem_max = end;
+ }
+
+ return 0;
+}
+
+/* Supported file types and callbacks */
+struct file_type file_type[] = {
+ /* uImage is probed before zImage because the latter also accepts
+ uncompressed images. */
+ { "uImage-sh", uImage_sh_probe, uImage_sh_load, zImage_sh_usage },
+ { "zImage-sh", zImage_sh_probe, zImage_sh_load, zImage_sh_usage },
+ { "elf-sh", elf_sh_probe, elf_sh_load, elf_sh_usage },
+ { "netbsd-sh", netbsd_sh_probe, netbsd_sh_load, netbsd_sh_usage },
+};
+int file_types = sizeof(file_type) / sizeof(file_type[0]);
+
+
+void arch_usage(void)
+{
+
+ printf(
+ " none\n\n"
+ "Default options:\n"
+ " --append=\"%s\"\n"
+ " STRING of --append is set from /proc/cmdline as default.\n"
+ ,get_append());
+
+}
+
+int arch_process_options(int argc, char **argv)
+{
+ /* The common options amongst loaders (e.g. --append) should be read
+ * here, and the loader-specific options (e.g. NetBSD stuff) should
+ * then be re-parsed in the loader.
+ * (e.g. in kexec-netbsd-sh.c, for example.)
+ */
+ static const struct option options[] = {
+ KEXEC_ALL_OPTIONS
+ { 0, 0, NULL, 0 },
+ };
+ static const char short_options[] = KEXEC_ARCH_OPT_STR;
+ int opt;
+
+ opterr = 0; /* Don't complain about unrecognized options here */
+ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch(opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_MAX) {
+ break;
+ }
+ case OPT_APPEND:
+ case OPT_NBSD_HOWTO:
+ case OPT_NBSD_MROOT:
+ ;
+ }
+ }
+ /* Reset getopt for the next pass; called in other source modules */
+ opterr = 1;
+ optind = 1;
+ return 0;
+}
+
+const struct arch_map_entry arches[] = {
+ /* For compatibility with older patches
+ * use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_SH here.
+ */
+ { "sh3", KEXEC_ARCH_DEFAULT },
+ { "sh4", KEXEC_ARCH_DEFAULT },
+ { "sh4a", KEXEC_ARCH_DEFAULT },
+ { "sh4al-dsp", KEXEC_ARCH_DEFAULT },
+ { NULL, 0 },
+};
+
+int arch_compat_trampoline(struct kexec_info *UNUSED(info))
+{
+ return 0;
+}
+
+void arch_update_purgatory(struct kexec_info *UNUSED(info))
+{
+}
+
+char append_buf[256];
+
+char *get_append(void)
+{
+ FILE *fp;
+ int len;
+ if((fp = fopen("/proc/cmdline", "r")) == NULL){
+ die("/proc/cmdline file open error !!\n");
+ }
+ fgets(append_buf, 256, fp);
+ len = strlen(append_buf);
+ append_buf[len-1] = 0;
+ fclose(fp);
+ return append_buf;
+}
+
+void kexec_sh_setup_zero_page(char *zero_page_buf, size_t zero_page_size,
+ char *cmd_line)
+{
+ size_t n = zero_page_size - 0x100;
+
+ memset(zero_page_buf, 0, zero_page_size);
+
+ if (cmd_line) {
+ if (n > strlen(cmd_line))
+ n = strlen(cmd_line);
+
+ memcpy(zero_page_buf + 0x100, cmd_line, n);
+ zero_page_buf[0x100 + n] = '\0';
+ }
+}
+
+static int is_32bit(void)
+{
+ const char *cpuinfo = "/proc/cpuinfo";
+ char line[MAX_LINE];
+ FILE *fp;
+ int status = 0;
+
+ fp = fopen(cpuinfo, "r");
+ if (!fp)
+ die("Cannot open %s\n", cpuinfo);
+
+ while(fgets(line, sizeof(line), fp) != 0) {
+ const char *key = "address sizes";
+ const char *value = " 32 bits physical";
+ char *p;
+ if (strncmp(line, key, strlen(key)))
+ continue;
+ p = strchr(line + strlen(key), ':');
+ if (!p)
+ continue;
+ if (!strncmp(p + 1, value, strlen(value)))
+ status = 1;
+ break;
+ }
+
+ fclose(fp);
+
+ return status;
+}
+
+unsigned long virt_to_phys(unsigned long addr)
+{
+ unsigned long seg = addr & 0xe0000000;
+ unsigned long long start = 0;
+ int have_32bit = is_32bit();
+
+ if (seg != 0x80000000 && (have_32bit || seg != 0xc0000000))
+ die("Virtual address %p is not in P1%s\n", (void *)addr,
+ have_32bit ? "" : " or P2");
+
+ /* If 32bit addressing is used then the base of system RAM
+ * is an offset into physical memory. */
+ if (have_32bit) {
+ unsigned long long end;
+ int ret;
+
+ /* Assume there is only one "System RAM" region */
+ ret = parse_iomem_single("System RAM\n", &start, &end);
+ if (ret)
+ die("Could not parse System RAM region "
+ "in /proc/iomem\n");
+ }
+
+ return addr - seg + start;
+}
+
+/*
+ * add_segment() should convert base to a physical address on superh,
+ * while the default is just to work with base as is */
+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);
+}
+
+/*
+ * add_buffer() should convert base to a physical address on superh,
+ * while the default is just to work with base as is */
+unsigned long add_buffer(struct kexec_info *info, const void *buf,
+ unsigned long bufsz, unsigned long memsz,
+ unsigned long buf_align, unsigned long buf_min,
+ unsigned long buf_max, int buf_end)
+{
+ return add_buffer_phys_virt(info, buf, bufsz, memsz, buf_align,
+ buf_min, buf_max, buf_end, 1);
+}
diff --git a/kexec/arch/sh/kexec-sh.h b/kexec/arch/sh/kexec-sh.h
new file mode 100644
index 0000000..7d28ade
--- /dev/null
+++ b/kexec/arch/sh/kexec-sh.h
@@ -0,0 +1,29 @@
+#ifndef KEXEC_SH_H
+#define KEXEC_SH_H
+
+#define COMMAND_LINE_SIZE 2048
+
+int uImage_sh_probe(const char *buf, off_t len);
+int uImage_sh_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+
+int zImage_sh_probe(const char *buf, off_t len);
+int zImage_sh_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void zImage_sh_usage(void);
+
+int elf_sh_probe(const char *buf, off_t len);
+int elf_sh_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void elf_sh_usage(void);
+
+int netbsd_sh_probe(const char *buf, off_t len);
+int netbsd_sh_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void netbsd_sh_usage(void);
+
+char *get_append(void);
+void kexec_sh_setup_zero_page(char *zero_page_buf, size_t zero_page_size,
+ char *cmd_line);
+
+#endif /* KEXEC_SH_H */
diff --git a/kexec/arch/sh/kexec-uImage-sh.c b/kexec/arch/sh/kexec-uImage-sh.c
new file mode 100644
index 0000000..130f12c
--- /dev/null
+++ b/kexec/arch/sh/kexec-uImage-sh.c
@@ -0,0 +1,24 @@
+/*
+ * uImage support added by Marc Andre Tanner <mat@brain-dump.org>
+ *
+ * Cloned from ARM by Paul Mundt, 2009.
+ */
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <image.h>
+#include <kexec-uImage.h>
+#include "../../kexec.h"
+#include "kexec-sh.h"
+
+int uImage_sh_probe(const char *buf, off_t len)
+{
+ return uImage_probe_kernel(buf, len, IH_ARCH_SH);
+}
+
+int uImage_sh_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ return zImage_sh_load(argc, argv, buf + sizeof(struct image_header),
+ len - sizeof(struct image_header), info);
+}
diff --git a/kexec/arch/sh/kexec-zImage-sh.c b/kexec/arch/sh/kexec-zImage-sh.c
new file mode 100644
index 0000000..6dc2c13
--- /dev/null
+++ b/kexec/arch/sh/kexec-zImage-sh.c
@@ -0,0 +1,142 @@
+/*
+ * kexec-zImage-sh.c - kexec zImage loader for the SH
+ * Copyright (C) 2005 kogiidena@eggplant.ddo.jp
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#define _GNU_SOURCE
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <elf.h>
+#include <boot/elf_boot.h>
+#include <ip_checksum.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include <arch/options.h>
+#include "kexec-sh.h"
+
+static const int probe_debug = 0;
+
+#define HEAD32_KERNEL_START_ADDR 0
+#define HEAD32_DECOMPRESS_KERNEL_ADDR 1
+#define HEAD32_INIT_STACK_ADDR 2
+#define HEAD32_INIT_SR 3
+#define HEAD32_INIT_SR_VALUE 0x400000F0
+
+static unsigned long zImage_head32(const char *buf, int offs)
+{
+ unsigned long *values = (void *)buf;
+ int k;
+
+ for (k = (0x200 / 4) - 1; k > 0; k--)
+ if (values[k] != 0x00090009) /* not nop + nop padding*/
+ return values[k - offs];
+
+ return 0;
+}
+
+/*
+ * zImage_sh_probe - sanity check the elf image
+ *
+ * Make sure that the file image has a reasonable chance of working.
+ */
+int zImage_sh_probe(const char *buf, off_t UNUSED(len))
+{
+ if (memcmp(&buf[0x202], "HdrS", 4) != 0)
+ return -1;
+
+ if (zImage_head32(buf, HEAD32_INIT_SR) != HEAD32_INIT_SR_VALUE)
+ return -1;
+
+ return 0;
+}
+
+void zImage_sh_usage(void)
+{
+ printf(
+ " --append=STRING Set the kernel command line to STRING.\n"
+ " --empty-zero=ADDRESS Set the kernel top ADDRESS. \n\n");
+
+}
+
+int zImage_sh_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ char *command_line;
+ int opt;
+ unsigned long empty_zero, zero_page_base, zero_page_size, k;
+ unsigned long image_base;
+ char *param;
+
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ {0, 0, 0, 0},
+ };
+
+ static const char short_options[] = KEXEC_ARCH_OPT_STR "";
+
+ command_line = 0;
+ while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch (opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ case OPT_APPEND:
+ command_line = optarg;
+ break;
+ }
+ }
+
+ if (!command_line)
+ command_line = get_append();
+
+ /* assume the zero page is the page before the vmlinux entry point.
+ * we don't know the page size though, but 64k seems to be max.
+ * put several 4k zero page copies before the entry point to cover
+ * all combinations.
+ */
+
+ empty_zero = zImage_head32(buf, HEAD32_KERNEL_START_ADDR);
+
+ zero_page_size = 0x10000;
+ zero_page_base = virt_to_phys(empty_zero - zero_page_size);
+
+ while (!valid_memory_range(info, zero_page_base,
+ zero_page_base + zero_page_size - 1)) {
+ zero_page_base += 0x1000;
+ zero_page_size -= 0x1000;
+ if (zero_page_size == 0)
+ die("Unable to determine zero page size from %p \n",
+ (void *)empty_zero);
+ }
+
+ param = xmalloc(zero_page_size);
+ for (k = 0; k < (zero_page_size / 0x1000); k++)
+ kexec_sh_setup_zero_page(param + (k * 0x1000), 0x1000,
+ command_line);
+
+ add_segment(info, param, zero_page_size,
+ 0x80000000 | zero_page_base, zero_page_size);
+
+ /* load image a bit above the zero page, round up to 64k
+ * the zImage will relocate itself, but only up seems supported.
+ */
+
+ image_base = _ALIGN(empty_zero, 0x10000);
+ add_segment(info, buf, len, image_base, len);
+ info->entry = (void *)virt_to_phys(image_base);
+ return 0;
+}
diff --git a/kexec/arch/sh/netbsd_booter.S b/kexec/arch/sh/netbsd_booter.S
new file mode 100644
index 0000000..d4d16df
--- /dev/null
+++ b/kexec/arch/sh/netbsd_booter.S
@@ -0,0 +1,47 @@
+ .globl netbsd_booter
+netbsd_booter:
+ mov.l ccr,r0
+ mov #0,r1
+ mov.l r1,@r0
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ mova netbsd_start,r0
+ mov.l @r0,r1
+ add #4,r0
+ mov.l @r0,r4 ! howto
+ add #4,r0
+ mov r0,r5 ! bootinfo
+ jmp @r1
+ nop
+ nop
+ nop
+ .align 4
+ccr: .long 0xff00001c
+
+ .align 8
+netbsd_start:
+ .long 0x8c001000
+ .long 0x200 ! howto
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+
diff --git a/kexec/arch/x86_64/Makefile b/kexec/arch/x86_64/Makefile
new file mode 100644
index 0000000..275add5
--- /dev/null
+++ b/kexec/arch/x86_64/Makefile
@@ -0,0 +1,24 @@
+#
+# kexec x86_64 (linux booting linux)
+#
+x86_64_KEXEC_SRCS = kexec/arch/i386/kexec-elf-x86.c
+x86_64_KEXEC_SRCS += kexec/arch/i386/kexec-bzImage.c
+x86_64_KEXEC_SRCS += kexec/arch/i386/kexec-multiboot-x86.c
+x86_64_KEXEC_SRCS += kexec/arch/i386/kexec-mb2-x86.c
+x86_64_KEXEC_SRCS += kexec/arch/i386/kexec-beoboot-x86.c
+x86_64_KEXEC_SRCS += kexec/arch/i386/kexec-nbi.c
+x86_64_KEXEC_SRCS += kexec/arch/i386/x86-linux-setup.c
+x86_64_KEXEC_SRCS += kexec/arch/i386/kexec-x86-common.c
+x86_64_KEXEC_SRCS += kexec/arch/i386/crashdump-x86.c
+
+x86_64_KEXEC_SRCS_native = kexec/arch/x86_64/kexec-x86_64.c
+x86_64_KEXEC_SRCS_native += kexec/arch/x86_64/kexec-elf-x86_64.c
+x86_64_KEXEC_SRCS_native += kexec/arch/x86_64/kexec-elf-rel-x86_64.c
+x86_64_KEXEC_SRCS_native += kexec/arch/x86_64/kexec-bzImage64.c
+
+x86_64_KEXEC_SRCS += $(x86_64_KEXEC_SRCS_native)
+
+# Don't add sources in i386/ to dist, as i386/Makefile adds them
+dist += kexec/arch/x86_64/Makefile $(x86_64_KEXEC_SRCS_native) \
+ kexec/arch/x86_64/kexec-x86_64.h \
+ kexec/arch/x86_64/include/arch/options.h
diff --git a/kexec/arch/x86_64/include/arch/options.h b/kexec/arch/x86_64/include/arch/options.h
new file mode 120000
index 0000000..047b0f9
--- /dev/null
+++ b/kexec/arch/x86_64/include/arch/options.h
@@ -0,0 +1 @@
+../../../i386/include/arch/options.h \ No newline at end of file
diff --git a/kexec/arch/x86_64/kexec-bzImage64.c b/kexec/arch/x86_64/kexec-bzImage64.c
new file mode 100644
index 0000000..aba4e3b
--- /dev/null
+++ b/kexec/arch/x86_64/kexec-bzImage64.c
@@ -0,0 +1,397 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2003-2010 Eric Biederman (ebiederm@xmission.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define _GNU_SOURCE
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <elf.h>
+#include <boot/elf_boot.h>
+#include <ip_checksum.h>
+#include <x86/x86-linux.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-syscall.h"
+#include "kexec-x86_64.h"
+#include "../i386/x86-linux-setup.h"
+#include "../i386/crashdump-x86.h"
+#include <arch/options.h>
+
+static const int probe_debug = 0;
+
+int bzImage64_probe(const char *buf, off_t len)
+{
+ const struct x86_linux_header *header;
+
+ if ((uintmax_t)len < (uintmax_t)(2 * 512)) {
+ if (probe_debug)
+ fprintf(stderr, "File is too short to be a bzImage!\n");
+ return -1;
+ }
+ header = (const struct x86_linux_header *)buf;
+ if (memcmp(header->header_magic, "HdrS", 4) != 0) {
+ if (probe_debug)
+ fprintf(stderr, "Not a bzImage\n");
+ return -1;
+ }
+ if (header->boot_sector_magic != 0xAA55) {
+ if (probe_debug)
+ fprintf(stderr, "No x86 boot sector present\n");
+ /* No x86 boot sector present */
+ return -1;
+ }
+ if (header->protocol_version < 0x020C) {
+ if (probe_debug)
+ fprintf(stderr, "Must be at least protocol version 2.12\n");
+ /* Must be at least protocol version 2.12 */
+ return -1;
+ }
+ if ((header->loadflags & 1) == 0) {
+ if (probe_debug)
+ fprintf(stderr, "zImage not a bzImage\n");
+ /* Not a bzImage */
+ return -1;
+ }
+ if ((header->xloadflags & 3) != 3) {
+ if (probe_debug)
+ fprintf(stderr, "Not a relocatable bzImage64\n");
+ /* Must be KERNEL_64 and CAN_BE_LOADED_ABOVE_4G */
+ return -1;
+ }
+
+#define XLF_EFI_KEXEC (1 << 4)
+ if ((header->xloadflags & XLF_EFI_KEXEC) == XLF_EFI_KEXEC)
+ bzImage_support_efi_boot = 1;
+
+ /* I've got a relocatable bzImage64 */
+ if (probe_debug)
+ fprintf(stderr, "It's a relocatable bzImage64\n");
+ return 0;
+}
+
+void bzImage64_usage(void)
+{
+ printf( " --entry-32bit Use the kernels 32bit entry point.\n"
+ " --real-mode Use the kernels real mode entry point.\n"
+ " --command-line=STRING Set the kernel command line to STRING.\n"
+ " --append=STRING Set the kernel command line to STRING.\n"
+ " --reuse-cmdline Use kernel command line from running system.\n"
+ " --initrd=FILE Use FILE as the kernel's initial ramdisk.\n"
+ " --ramdisk=FILE Use FILE as the kernel's initial ramdisk.\n"
+ );
+}
+
+static int do_bzImage64_load(struct kexec_info *info,
+ const char *kernel, off_t kernel_len,
+ const char *command_line, off_t command_line_len,
+ const char *initrd, off_t initrd_len)
+{
+ struct x86_linux_header setup_header;
+ struct x86_linux_param_header *real_mode;
+ int setup_sects;
+ size_t size;
+ int kern16_size;
+ unsigned long setup_base, setup_size, setup_header_size;
+ struct entry64_regs regs64;
+ char *modified_cmdline;
+ unsigned long cmdline_end;
+ unsigned long align, addr, k_size;
+ unsigned kern16_size_needed;
+
+ /*
+ * Find out about the file I am about to load.
+ */
+ if ((uintmax_t)kernel_len < (uintmax_t)(2 * 512))
+ return -1;
+
+ memcpy(&setup_header, kernel, sizeof(setup_header));
+ setup_sects = setup_header.setup_sects;
+ if (setup_sects == 0)
+ setup_sects = 4;
+ kern16_size = (setup_sects + 1) * 512;
+ if (kernel_len < kern16_size) {
+ fprintf(stderr, "BzImage truncated?\n");
+ return -1;
+ }
+
+ if ((uintmax_t)command_line_len > (uintmax_t)setup_header.cmdline_size) {
+ dbgprintf("Kernel command line too long for kernel!\n");
+ return -1;
+ }
+
+ /* Need to append some command line parameters internally in case of
+ * taking crash dumps.
+ */
+ if (info->kexec_flags & (KEXEC_ON_CRASH | KEXEC_PRESERVE_CONTEXT)) {
+ modified_cmdline = xmalloc(COMMAND_LINE_SIZE);
+ memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE);
+ if (command_line) {
+ strncpy(modified_cmdline, command_line,
+ COMMAND_LINE_SIZE);
+ modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
+ }
+
+ /* If panic kernel is being loaded, additional segments need
+ * to be created. load_crashdump_segments will take care of
+ * loading the segments as high in memory as possible, hence
+ * in turn as away as possible from kernel to avoid being
+ * stomped by the kernel.
+ */
+ if (load_crashdump_segments(info, modified_cmdline, -1, 0) < 0)
+ return -1;
+
+ /* Use new command line buffer */
+ command_line = modified_cmdline;
+ command_line_len = strlen(command_line) + 1;
+ }
+
+ /* x86_64 purgatory could be anywhere */
+ elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
+ 0x3000, -1, -1, 0);
+ dbgprintf("Loaded purgatory at addr 0x%lx\n", info->rhdr.rel_addr);
+ /* The argument/parameter segment */
+ kern16_size_needed = kern16_size;
+ if (kern16_size_needed < 4096)
+ kern16_size_needed = 4096;
+ setup_size = kern16_size_needed + command_line_len +
+ PURGATORY_CMDLINE_SIZE;
+ real_mode = xmalloc(setup_size);
+ memset(real_mode, 0, setup_size);
+
+ /* only copy setup_header */
+ setup_header_size = kernel[0x201] + 0x202 - 0x1f1;
+ if (setup_header_size > 0x7f)
+ setup_header_size = 0x7f;
+ memcpy((unsigned char *)real_mode + 0x1f1, kernel + 0x1f1,
+ setup_header_size);
+
+ /* No real mode code will be executing. setup segment can be loaded
+ * anywhere as we will be just reading command line.
+ */
+ setup_base = add_buffer(info, real_mode, setup_size, setup_size,
+ 16, 0x3000, -1, -1);
+
+ dbgprintf("Loaded real_mode_data and command line at 0x%lx\n",
+ setup_base);
+
+ /* The main kernel segment */
+ k_size = kernel_len - kern16_size;
+ /* need to use run-time size for buffer searching */
+ dbgprintf("kernel init_size 0x%x\n", real_mode->init_size);
+ size = _ALIGN(real_mode->init_size, 4096);
+ align = real_mode->kernel_alignment;
+ addr = add_buffer(info, kernel + kern16_size, k_size,
+ size, align, 0x100000, -1, -1);
+ if (addr == ULONG_MAX)
+ die("can not load bzImage64");
+ dbgprintf("Loaded 64bit kernel at 0x%lx\n", addr);
+
+ /* Tell the kernel what is going on */
+ setup_linux_bootloader_parameters_high(info, real_mode, setup_base,
+ kern16_size_needed, command_line, command_line_len,
+ initrd, initrd_len, 1); /* put initrd high too */
+
+ elf_rel_get_symbol(&info->rhdr, "entry64_regs", &regs64,
+ sizeof(regs64));
+ regs64.rbx = 0; /* Bootstrap processor */
+ regs64.rsi = setup_base; /* Pointer to the parameters */
+ regs64.rip = addr + 0x200; /* the entry point for startup_64 */
+ regs64.rsp = elf_rel_get_addr(&info->rhdr, "stack_end"); /* Stack, unused */
+ elf_rel_set_symbol(&info->rhdr, "entry64_regs", &regs64,
+ sizeof(regs64));
+
+ cmdline_end = setup_base + kern16_size_needed + command_line_len - 1;
+ elf_rel_set_symbol(&info->rhdr, "cmdline_end", &cmdline_end,
+ sizeof(unsigned long));
+
+ /* Fill in the information BIOS calls would normally provide. */
+ setup_linux_system_parameters(info, real_mode);
+
+ return 0;
+}
+
+/* This assumes file is being loaded using file based kexec syscall */
+int bzImage64_load_file(int argc, char **argv, struct kexec_info *info)
+{
+ int ret = 0;
+ char *command_line = NULL, *tmp_cmdline = NULL;
+ const char *ramdisk = NULL, *append = NULL;
+ int entry_16bit = 0, entry_32bit = 0;
+ int opt;
+ int command_line_len;
+
+ /* See options.h -- add any more there, too. */
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { "command-line", 1, 0, OPT_APPEND },
+ { "append", 1, 0, OPT_APPEND },
+ { "reuse-cmdline", 0, 0, OPT_REUSE_CMDLINE },
+ { "initrd", 1, 0, OPT_RAMDISK },
+ { "ramdisk", 1, 0, OPT_RAMDISK },
+ { "real-mode", 0, 0, OPT_REAL_MODE },
+ { "entry-32bit", 0, 0, OPT_ENTRY_32BIT },
+ { 0, 0, 0, 0 },
+ };
+ static const char short_options[] = KEXEC_ARCH_OPT_STR "d";
+
+ while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch (opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX)
+ break;
+ case OPT_APPEND:
+ append = optarg;
+ break;
+ case OPT_REUSE_CMDLINE:
+ tmp_cmdline = get_command_line();
+ break;
+ case OPT_RAMDISK:
+ ramdisk = optarg;
+ break;
+ case OPT_REAL_MODE:
+ entry_16bit = 1;
+ break;
+ case OPT_ENTRY_32BIT:
+ entry_32bit = 1;
+ break;
+ }
+ }
+ command_line = concat_cmdline(tmp_cmdline, append);
+ if (tmp_cmdline)
+ free(tmp_cmdline);
+ command_line_len = 0;
+ if (command_line) {
+ command_line_len = strlen(command_line) + 1;
+ } else {
+ command_line = strdup("\0");
+ command_line_len = 1;
+ }
+
+ if (entry_16bit || entry_32bit) {
+ fprintf(stderr, "Kexec2 syscall does not support 16bit"
+ " or 32bit entry yet\n");
+ ret = -1;
+ goto out;
+ }
+
+ if (ramdisk) {
+ info->initrd_fd = open(ramdisk, O_RDONLY);
+ if (info->initrd_fd == -1) {
+ fprintf(stderr, "Could not open initrd file %s:%s\n",
+ ramdisk, strerror(errno));
+ ret = -1;
+ goto out;
+ }
+ }
+
+ info->command_line = command_line;
+ info->command_line_len = command_line_len;
+ return ret;
+out:
+ free(command_line);
+ return ret;
+}
+
+int bzImage64_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ char *command_line = NULL, *tmp_cmdline = NULL;
+ const char *ramdisk = NULL, *append = NULL;
+ char *ramdisk_buf;
+ off_t ramdisk_length = 0;
+ int command_line_len;
+ int entry_16bit = 0, entry_32bit = 0;
+ int opt;
+ int result;
+
+ if (info->file_mode)
+ return bzImage64_load_file(argc, argv, info);
+
+ /* See options.h -- add any more there, too. */
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { "command-line", 1, 0, OPT_APPEND },
+ { "append", 1, 0, OPT_APPEND },
+ { "reuse-cmdline", 0, 0, OPT_REUSE_CMDLINE },
+ { "initrd", 1, 0, OPT_RAMDISK },
+ { "ramdisk", 1, 0, OPT_RAMDISK },
+ { "real-mode", 0, 0, OPT_REAL_MODE },
+ { "entry-32bit", 0, 0, OPT_ENTRY_32BIT },
+ { 0, 0, 0, 0 },
+ };
+ static const char short_options[] = KEXEC_ARCH_OPT_STR "d";
+
+ while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch (opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX)
+ break;
+ case OPT_APPEND:
+ append = optarg;
+ break;
+ case OPT_REUSE_CMDLINE:
+ tmp_cmdline = get_command_line();
+ break;
+ case OPT_RAMDISK:
+ ramdisk = optarg;
+ break;
+ case OPT_REAL_MODE:
+ entry_16bit = 1;
+ break;
+ case OPT_ENTRY_32BIT:
+ entry_32bit = 1;
+ break;
+ }
+ }
+ command_line = concat_cmdline(tmp_cmdline, append);
+ if (tmp_cmdline)
+ free(tmp_cmdline);
+ command_line_len = 0;
+ if (command_line) {
+ command_line_len = strlen(command_line) + 1;
+ } else {
+ command_line = strdup("\0");
+ command_line_len = 1;
+ }
+ ramdisk_buf = 0;
+ if (ramdisk)
+ ramdisk_buf = slurp_file(ramdisk, &ramdisk_length);
+
+ if (entry_16bit || entry_32bit)
+ result = do_bzImage_load(info, buf, len, command_line,
+ command_line_len, ramdisk_buf,
+ ramdisk_length, 0, 0, entry_16bit);
+ else
+ result = do_bzImage64_load(info, buf, len, command_line,
+ command_line_len, ramdisk_buf,
+ ramdisk_length);
+
+ free(command_line);
+ return result;
+}
diff --git a/kexec/arch/x86_64/kexec-elf-rel-x86_64.c b/kexec/arch/x86_64/kexec-elf-rel-x86_64.c
new file mode 100644
index 0000000..0d22f3b
--- /dev/null
+++ b/kexec/arch/x86_64/kexec-elf-rel-x86_64.c
@@ -0,0 +1,94 @@
+#include <stdio.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+
+int machine_verify_elf_rel(struct mem_ehdr *ehdr)
+{
+ if (ehdr->ei_data != ELFDATA2LSB) {
+ return 0;
+ }
+ if (ehdr->ei_class != ELFCLASS64 &&
+ ehdr->ei_class != ELFCLASS32) { /* x32 */
+ return 0;
+ }
+ if (ehdr->e_machine != EM_X86_64) {
+ return 0;
+ }
+ return 1;
+}
+
+static const char *reloc_name(unsigned long r_type)
+{
+ static const char *r_name[] = {
+ "R_X86_64_NONE",
+ "R_X86_64_64",
+ "R_X86_64_PC32",
+ "R_X86_64_GOT32",
+ "R_X86_64_PLT32",
+ "R_X86_64_COPY",
+ "R_X86_64_GLOB_DAT",
+ "R_X86_64_JUMP_SLOT",
+ "R_X86_64_RELATIVE",
+ "R_X86_64_GOTPCREL",
+ "R_X86_64_32",
+ "R_X86_64_32S",
+ "R_X86_64_16",
+ "R_X86_64_PC16",
+ "R_X86_64_8",
+ "R_X86_64_PC8",
+ "R_X86_64_DTPMOD64",
+ "R_X86_64_DTPOFF64",
+ "R_X86_64_TPOFF64",
+ "R_X86_64_TLSGD",
+ "R_X86_64_TLSLD",
+ "R_X86_64_DTPOFF32",
+ "R_X86_64_GOTTPOFF",
+ "R_X86_64_TPOFF32",
+ };
+ static char buf[100];
+ const char *name;
+ if (r_type < (sizeof(r_name)/sizeof(r_name[0]))){
+ name = r_name[r_type];
+ }
+ else {
+ sprintf(buf, "R_X86_64_%lu", r_type);
+ name = buf;
+ }
+ return name;
+}
+
+void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr),
+ struct mem_sym *UNUSED(sym), unsigned long r_type, void *location,
+ unsigned long address, unsigned long value)
+{
+ dbgprintf("%s\n", reloc_name(r_type));
+ switch(r_type) {
+ case R_X86_64_NONE:
+ break;
+ case R_X86_64_64:
+ *(uint64_t *)location = value;
+ break;
+ case R_X86_64_32:
+ *(uint32_t *)location = value;
+ if (value != *(uint32_t *)location)
+ goto overflow;
+ break;
+ case R_X86_64_32S:
+ *(uint32_t *)location = value;
+ if ((int64_t)value != *(int32_t *)location)
+ goto overflow;
+ break;
+ case R_X86_64_PC32:
+ case R_X86_64_PLT32:
+ *(uint32_t *)location = value - address;
+ break;
+ default:
+ die("Unhandled rela relocation: %s\n", reloc_name(r_type));
+ break;
+ }
+ return;
+ overflow:
+ die("overflow in relocation type %s val %lx\n",
+ reloc_name(r_type), value);
+}
diff --git a/kexec/arch/x86_64/kexec-elf-x86_64.c b/kexec/arch/x86_64/kexec-elf-x86_64.c
new file mode 100644
index 0000000..7f9540a
--- /dev/null
+++ b/kexec/arch/x86_64/kexec-elf-x86_64.c
@@ -0,0 +1,257 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define _GNU_SOURCE
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <elf.h>
+#include <x86/x86-linux.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-elf-boot.h"
+#include "../i386/x86-linux-setup.h"
+#include "kexec-x86_64.h"
+#include "../i386/kexec-x86.h"
+#include "../i386/crashdump-x86.h"
+#include <arch/options.h>
+
+int elf_x86_64_probe(const char *buf, off_t len)
+{
+ return elf_x86_any_probe(buf, len, CORE_TYPE_ELF64);
+}
+
+void elf_x86_64_usage(void)
+{
+ printf( " --command-line=STRING Set the kernel command line to STRING\n"
+ " --append=STRING Set the kernel command line to STRING\n"
+ " --reuse-cmdline Use kernel command line from running system.\n"
+ " --initrd=FILE Use FILE as the kernel's initial ramdisk.\n"
+ " --ramdisk=FILE Use FILE as the kernel's initial ramdisk.\n"
+ " --args-linux Pass linux kernel style options\n"
+ " --args-elf Pass elf boot notes\n"
+ " --args-none Jump directly from the kernel\n"
+ );
+}
+
+int elf_x86_64_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ struct mem_ehdr ehdr;
+ const char *append = NULL;
+ char *command_line = NULL, *modified_cmdline;
+ char *tmp_cmdline = NULL;
+ int command_line_len;
+ const char *ramdisk;
+ unsigned long entry, max_addr;
+ int arg_style;
+#define ARG_STYLE_ELF 0
+#define ARG_STYLE_LINUX 1
+#define ARG_STYLE_NONE 2
+ int opt;
+ int result = 0;
+ const char *error_msg = NULL;
+
+ /* See options.h and add any new options there too! */
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { "command-line", 1, NULL, OPT_APPEND },
+ { "append", 1, NULL, OPT_APPEND },
+ { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE },
+ { "initrd", 1, NULL, OPT_RAMDISK },
+ { "ramdisk", 1, NULL, OPT_RAMDISK },
+ { "args-elf", 0, NULL, OPT_ARGS_ELF },
+ { "args-linux", 0, NULL, OPT_ARGS_LINUX },
+ { "args-none", 0, NULL, OPT_ARGS_NONE },
+ { 0, 0, NULL, 0 },
+ };
+
+ static const char short_options[] = KEXEC_ARCH_OPT_STR "";
+
+ /*
+ * Parse the command line arguments
+ */
+ arg_style = ARG_STYLE_ELF;
+ modified_cmdline = 0;
+ ramdisk = 0;
+ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch(opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ fprintf(stderr, "Unknown option: opt: %d\n", opt);
+ case OPT_APPEND:
+ append = optarg;
+ break;
+ case OPT_REUSE_CMDLINE:
+ tmp_cmdline = get_command_line();
+ break;
+ case OPT_RAMDISK:
+ ramdisk = optarg;
+ break;
+ case OPT_ARGS_ELF:
+ arg_style = ARG_STYLE_ELF;
+ break;
+ case OPT_ARGS_LINUX:
+ arg_style = ARG_STYLE_LINUX;
+ break;
+ case OPT_ARGS_NONE:
+#ifdef __x86_64__
+ arg_style = ARG_STYLE_NONE;
+#else
+ die("--args-none only works on arch x86_64\n");
+#endif
+ break;
+ }
+ }
+ command_line = concat_cmdline(tmp_cmdline, append);
+ if (tmp_cmdline)
+ free(tmp_cmdline);
+ command_line_len = 0;
+ if (command_line) {
+ command_line_len = strlen(command_line) +1;
+ }
+
+ /* Need to append some command line parameters internally in case of
+ * taking crash dumps.
+ */
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ modified_cmdline = xmalloc(COMMAND_LINE_SIZE);
+ memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE);
+ if (command_line) {
+ strncpy(modified_cmdline, command_line,
+ COMMAND_LINE_SIZE);
+ modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
+ }
+ }
+
+ /* Load the ELF executable */
+ elf_exec_build_load(info, &ehdr, buf, len, 0);
+
+ entry = ehdr.e_entry;
+ max_addr = elf_max_addr(&ehdr);
+
+ /* Do we want arguments? */
+ if (arg_style != ARG_STYLE_NONE) {
+ /* Load the setup code */
+ elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
+ 0, ULONG_MAX, 1, 0);
+ }
+ if (arg_style == ARG_STYLE_NONE) {
+ info->entry = (void *)entry;
+
+ }
+ else if (arg_style == ARG_STYLE_ELF) {
+ unsigned long note_base;
+ struct entry64_regs regs;
+
+ /* Setup the ELF boot notes */
+ note_base = elf_boot_notes(info, max_addr, command_line, command_line_len);
+
+ /* Initialize the registers */
+ elf_rel_get_symbol(&info->rhdr, "entry64_regs", &regs, sizeof(regs));
+ regs.rdi = note_base; /* The notes (arg1) */
+ regs.rip = entry; /* The entry point */
+ regs.rsp = elf_rel_get_addr(&info->rhdr, "stack_end"); /* Stack, unused */
+ elf_rel_set_symbol(&info->rhdr, "entry64_regs", &regs, sizeof(regs));
+
+ if (ramdisk) {
+ error_msg = "Ramdisks not supported with generic elf arguments";
+ goto out;
+ }
+ }
+ else if (arg_style == ARG_STYLE_LINUX) {
+ struct x86_linux_faked_param_header *hdr;
+ unsigned long param_base;
+ char *ramdisk_buf;
+ off_t ramdisk_length;
+ struct entry64_regs regs;
+ int rc=0;
+
+ /* Get the linux parameter header */
+ hdr = xmalloc(sizeof(*hdr));
+ param_base = add_buffer(info, hdr, sizeof(*hdr), sizeof(*hdr),
+ 16, 0, max_addr, 1);
+
+ /* Initialize the parameter header */
+ memset(hdr, 0, sizeof(*hdr));
+ init_linux_parameters(&hdr->hdr);
+
+ /* Add a ramdisk to the current image */
+ ramdisk_buf = 0;
+ ramdisk_length = 0;
+ if (ramdisk) {
+ ramdisk_buf = slurp_file(ramdisk, &ramdisk_length);
+ }
+
+ /* If panic kernel is being loaded, additional segments need
+ * to be created. */
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ rc = load_crashdump_segments(info, modified_cmdline,
+ max_addr, 0);
+ if (rc < 0) {
+ result = -1;
+ goto out;
+ }
+ /* Use new command line. */
+ free(command_line);
+ command_line = modified_cmdline;
+ command_line_len = strlen(modified_cmdline) + 1;
+ modified_cmdline = NULL;
+ }
+
+ /* Tell the kernel what is going on */
+ setup_linux_bootloader_parameters(info, &hdr->hdr, param_base,
+ offsetof(struct x86_linux_faked_param_header, command_line),
+ command_line, command_line_len,
+ ramdisk_buf, ramdisk_length);
+
+ /* Fill in the information bios calls would usually provide */
+ setup_linux_system_parameters(info, &hdr->hdr);
+
+ /* Initialize the registers */
+ elf_rel_get_symbol(&info->rhdr, "entry64_regs", &regs, sizeof(regs));
+ regs.rbx = 0; /* Bootstrap processor */
+ regs.rsi = param_base; /* Pointer to the parameters */
+ regs.rip = entry; /* the entry point */
+ regs.rsp = elf_rel_get_addr(&info->rhdr, "stack_end"); /* Stack, unused */
+ elf_rel_set_symbol(&info->rhdr, "entry64_regs", &regs, sizeof(regs));
+ }
+ else {
+ error_msg = "Unknown argument style\n";
+ }
+
+out:
+ free(command_line);
+ free(modified_cmdline);
+ if (error_msg)
+ die("%s", error_msg);
+ return result;
+}
diff --git a/kexec/arch/x86_64/kexec-x86_64.c b/kexec/arch/x86_64/kexec-x86_64.c
new file mode 100644
index 0000000..ffd84f0
--- /dev/null
+++ b/kexec/arch/x86_64/kexec-x86_64.c
@@ -0,0 +1,190 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-syscall.h"
+#include "kexec-x86_64.h"
+#include "../i386/crashdump-x86.h"
+#include <arch/options.h>
+
+struct file_type file_type[] = {
+ { "multiboot2-x86", multiboot2_x86_probe, multiboot2_x86_load,
+ multiboot2_x86_usage },
+ { "elf-x86_64", elf_x86_64_probe, elf_x86_64_load, elf_x86_64_usage },
+ { "multiboot-x86", multiboot_x86_probe, multiboot_x86_load,
+ multiboot_x86_usage },
+ { "elf-x86", elf_x86_probe, elf_x86_load, elf_x86_usage },
+ { "bzImage64", bzImage64_probe, bzImage64_load, bzImage64_usage },
+ { "bzImage", bzImage_probe, bzImage_load, bzImage_usage },
+ { "beoboot-x86", beoboot_probe, beoboot_load, beoboot_usage },
+ { "nbi-x86", nbi_probe, nbi_load, nbi_usage },
+};
+int file_types = sizeof(file_type)/sizeof(file_type[0]);
+
+
+void arch_usage(void)
+{
+ printf(
+ " --reset-vga Attempt to reset a standard vga device\n"
+ " --serial=<port> Specify the serial port for debug output\n"
+ " --serial-baud=<baud_rate> Specify the serial port baud rate\n"
+ " --console-vga Enable the vga console\n"
+ " --console-serial Enable the serial console\n"
+ " --pass-memmap-cmdline Pass memory map via command line in kexec on panic case\n"
+ " --noefi Disable efi support\n"
+ " --reuse-video-type Reuse old boot time video type blindly\n"
+ );
+}
+
+struct arch_options_t arch_options = {
+ .reset_vga = 0,
+ .serial_base = 0x3f8,
+ .serial_baud = 0,
+ .console_vga = 0,
+ .console_serial = 0,
+ .core_header_type = CORE_TYPE_ELF64,
+ .pass_memmap_cmdline = 0,
+ .noefi = 0,
+ .reuse_video_type = 0,
+};
+
+int arch_process_options(int argc, char **argv)
+{
+ static const struct option options[] = {
+ KEXEC_ALL_OPTIONS
+ { 0, 0, NULL, 0 },
+ };
+ static const char short_options[] = KEXEC_ALL_OPT_STR;
+ int opt;
+ unsigned long value;
+ char *end;
+
+ opterr = 0; /* Don't complain about unrecognized options here */
+ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch(opt) {
+ default:
+ break;
+ case OPT_RESET_VGA:
+ arch_options.reset_vga = 1;
+ break;
+ case OPT_CONSOLE_VGA:
+ arch_options.console_vga = 1;
+ break;
+ case OPT_CONSOLE_SERIAL:
+ arch_options.console_serial = 1;
+ break;
+ case OPT_SERIAL:
+ value = ULONG_MAX;
+ if (strcmp(optarg, "ttyS0") == 0) {
+ value = 0x3f8;
+ }
+ else if (strcmp(optarg, "ttyS1") == 0) {
+ value = 0x2f8;
+ }
+ else if (strncmp(optarg, "0x", 2) == 0) {
+ value = strtoul(optarg +2, &end, 16);
+ if (*end != '\0') {
+ value = ULONG_MAX;
+ }
+ }
+ if (value >= 65536) {
+ fprintf(stderr, "Bad serial port base '%s'\n",
+ optarg);
+ usage();
+ return -1;
+
+ }
+ arch_options.serial_base = value;
+ break;
+ case OPT_SERIAL_BAUD:
+ value = strtoul(optarg, &end, 0);
+ if ((value > 115200) || ((115200 %value) != 0) ||
+ (value < 9600) || (*end))
+ {
+ fprintf(stderr, "Bad serial port baud rate '%s'\n",
+ optarg);
+ usage();
+ return -1;
+
+ }
+ arch_options.serial_baud = value;
+ break;
+ case OPT_PASS_MEMMAP_CMDLINE:
+ arch_options.pass_memmap_cmdline = 1;
+ break;
+ case OPT_NOEFI:
+ arch_options.noefi = 1;
+ break;
+ case OPT_REUSE_VIDEO_TYPE:
+ arch_options.reuse_video_type = 1;
+ break;
+ }
+ }
+ /* Reset getopt for the next pass; called in other source modules */
+ opterr = 1;
+ optind = 1;
+ return 0;
+}
+
+const struct arch_map_entry arches[] = {
+ { "x86_64", KEXEC_ARCH_X86_64 },
+ { NULL, 0 },
+};
+
+int arch_compat_trampoline(struct kexec_info *UNUSED(info))
+{
+ return 0;
+}
+
+void arch_update_purgatory(struct kexec_info *info)
+{
+ uint8_t panic_kernel = 0;
+
+ elf_rel_set_symbol(&info->rhdr, "reset_vga",
+ &arch_options.reset_vga, sizeof(arch_options.reset_vga));
+ elf_rel_set_symbol(&info->rhdr, "serial_base",
+ &arch_options.serial_base, sizeof(arch_options.serial_base));
+ elf_rel_set_symbol(&info->rhdr, "serial_baud",
+ &arch_options.serial_baud, sizeof(arch_options.serial_baud));
+ elf_rel_set_symbol(&info->rhdr, "console_vga",
+ &arch_options.console_vga, sizeof(arch_options.console_vga));
+ elf_rel_set_symbol(&info->rhdr, "console_serial",
+ &arch_options.console_serial, sizeof(arch_options.console_serial));
+ elf_rel_set_symbol(&info->rhdr, "backup_src_start",
+ &info->backup_src_start, sizeof(info->backup_src_start));
+ elf_rel_set_symbol(&info->rhdr, "backup_src_size",
+ &info->backup_src_size, sizeof(info->backup_src_size));
+
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ panic_kernel = 1;
+ elf_rel_set_symbol(&info->rhdr, "backup_start",
+ &info->backup_start, sizeof(info->backup_start));
+ }
+ elf_rel_set_symbol(&info->rhdr, "panic_kernel",
+ &panic_kernel, sizeof(panic_kernel));
+}
diff --git a/kexec/arch/x86_64/kexec-x86_64.h b/kexec/arch/x86_64/kexec-x86_64.h
new file mode 100644
index 0000000..21c3a73
--- /dev/null
+++ b/kexec/arch/x86_64/kexec-x86_64.h
@@ -0,0 +1,41 @@
+#ifndef KEXEC_X86_64_H
+#define KEXEC_X86_64_H
+
+#include "../i386/kexec-x86.h"
+
+struct entry64_regs {
+ uint64_t rax;
+ uint64_t rbx;
+ uint64_t rcx;
+ uint64_t rdx;
+ uint64_t rsi;
+ uint64_t rdi;
+ uint64_t rsp;
+ uint64_t rbp;
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t rip;
+};
+
+int elf_x86_64_probe(const char *buf, off_t len);
+int elf_x86_64_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void elf_x86_64_usage(void);
+
+int bzImage64_probe(const char *buf, off_t len);
+int bzImage64_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void bzImage64_usage(void);
+
+int multiboot2_x86_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void multiboot2_x86_usage(void);
+int multiboot2_x86_probe(const char *buf, off_t buf_len);
+
+#endif /* KEXEC_X86_64_H */