Blame SOURCES/kexec-tools-2.0.8-arm64-kdump-create-elf-core-header.patch

a6d77e
From 9cd9f0ab98ce6ddbdbb9a045ed2887e0dd857afe Mon Sep 17 00:00:00 2001
a6d77e
Message-Id: <9cd9f0ab98ce6ddbdbb9a045ed2887e0dd857afe.1431592766.git.panand@redhat.com>
a6d77e
In-Reply-To: <1fb6841aa15407dbf371589d7abca7bc2d35815c.1431592766.git.panand@redhat.com>
a6d77e
References: <1fb6841aa15407dbf371589d7abca7bc2d35815c.1431592766.git.panand@redhat.com>
a6d77e
From: AKASHI Takahiro <takahiro.akashi@linaro.org>
a6d77e
Date: Tue, 17 Feb 2015 16:06:55 +0900
a6d77e
Subject: [PATCH 08/17] arm64: kdump: create elf core header
a6d77e
a6d77e
---
a6d77e
 kexec/arch/arm64/crashdump-arm64.c | 295 ++++++++++++++++++++++++++++++++++++-
a6d77e
 kexec/arch/arm64/crashdump-arm64.h |  17 ++-
a6d77e
 kexec/arch/arm64/kexec-arm64.c     |   4 +-
a6d77e
 kexec/arch/arm64/kexec-elf-arm64.c |  31 +++-
a6d77e
 4 files changed, 332 insertions(+), 15 deletions(-)
a6d77e
a6d77e
diff --git a/kexec/arch/arm64/crashdump-arm64.c b/kexec/arch/arm64/crashdump-arm64.c
a6d77e
index d2272c8124d0..ec83cf0c0f38 100644
a6d77e
--- a/kexec/arch/arm64/crashdump-arm64.c
a6d77e
+++ b/kexec/arch/arm64/crashdump-arm64.c
a6d77e
@@ -1,11 +1,28 @@
a6d77e
 /*
a6d77e
- * ARM64 crashdump.
a6d77e
+ * crashdump for arm64
a6d77e
+ *
a6d77e
+ * Copyright (C) Nokia Corporation, 2010.
a6d77e
+ * Author: Mika Westerberg
a6d77e
+ *
a6d77e
+ * Based on x86 implementation
a6d77e
+ * Copyright (C) IBM Corporation, 2005. All rights reserved
a6d77e
+ *
a6d77e
+ * Copyright (c) 2014 Linaro Limited
a6d77e
+ * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
a6d77e
+ *
a6d77e
+ * This program is free software; you can redistribute it and/or modify
a6d77e
+ * it under the terms of the GNU General Public License version 2 as
a6d77e
+ * published by the Free Software Foundation.
a6d77e
  */
a6d77e
 
a6d77e
-#define _GNU_SOURCE
a6d77e
+#define _GNU_SOURCE /* for asprintf */
a6d77e
 
a6d77e
+#include <assert.h>
a6d77e
+#include <elf.h>
a6d77e
 #include <errno.h>
a6d77e
-#include <linux/elf.h>
a6d77e
+#include <stdio.h>
a6d77e
+#include <stdlib.h>
a6d77e
+#include <unistd.h>
a6d77e
 
a6d77e
 #include "kexec.h"
a6d77e
 #include "crashdump.h"
a6d77e
@@ -13,9 +30,279 @@
a6d77e
 #include "kexec-arm64.h"
a6d77e
 #include "kexec-elf.h"
a6d77e
 
a6d77e
-struct memory_ranges usablemem_rgns = {};
a6d77e
+
a6d77e
+/*
a6d77e
+ * Used to save various memory ranges/regions needed for the captured
a6d77e
+ * kernel to boot. (like memmap= option in other archs)
a6d77e
+ */
a6d77e
+static struct memory_range crash_memory_ranges[CRASH_MAX_MEMORY_RANGES];
a6d77e
+struct memory_ranges crashmem_rgns = {
a6d77e
+	.size = 0,
a6d77e
+	.ranges = crash_memory_ranges,
a6d77e
+};
a6d77e
+
a6d77e
+/* memory range reserved for crashkernel */
a6d77e
+struct memory_range crash_reserved_mem;
a6d77e
+struct memory_ranges usablemem_rgns = {
a6d77e
+	.size = 0,
a6d77e
+	.ranges = &crash_reserved_mem,
a6d77e
+};
a6d77e
+
a6d77e
+static struct crash_elf_info elf_info = {
a6d77e
+	.class		= ELFCLASS64,
a6d77e
+#if (__BYTE_ORDER == __LITTLE_ENDIAN)
a6d77e
+	.data		= ELFDATA2LSB,
a6d77e
+#else
a6d77e
+	.data		= ELFDATA2MSB,
a6d77e
+#endif
a6d77e
+	.machine	= EM_AARCH64,
a6d77e
+};
a6d77e
 
a6d77e
 int is_crashkernel_mem_reserved(void)
a6d77e
 {
a6d77e
+	uint64_t start, end;
a6d77e
+
a6d77e
+	if (parse_iomem_single("Crash kernel\n", &start, &end) == 0)
a6d77e
+		return start != end;
a6d77e
+
a6d77e
+	return 0;
a6d77e
+}
a6d77e
+
a6d77e
+/*
a6d77e
+ * crash_range_callback() - callback called for each iomem region
a6d77e
+ * @data: not used
a6d77e
+ * @nr: not used
a6d77e
+ * @str: name of the memory region
a6d77e
+ * @base: start address of the memory region
a6d77e
+ * @length: size of the memory region
a6d77e
+ *
a6d77e
+ * This function is called once for each memory region found in /proc/iomem.
a6d77e
+ * It locates system RAM and crashkernel reserved memory and places these to
a6d77e
+ * variables: @crash_memory_ranges and @crash_reserved_mem. Number of memory
a6d77e
+ * regions is placed in @crash_memory_nr_ranges.
a6d77e
+ */
a6d77e
+
a6d77e
+static int crash_range_callback(void *UNUSED(data), int UNUSED(nr),
a6d77e
+				char *str, unsigned long long base,
a6d77e
+				unsigned long long length)
a6d77e
+{
a6d77e
+	struct memory_range *range;
a6d77e
+
a6d77e
+	assert(arm64_mem.memstart);
a6d77e
+
a6d77e
+	if (crashmem_rgns.size >= CRASH_MAX_MEMORY_RANGES)
a6d77e
+		return 1;
a6d77e
+
a6d77e
+	range = crashmem_rgns.ranges + crashmem_rgns.size;
a6d77e
+
a6d77e
+	if (strncmp(str, "System RAM\n", 11) == 0) {
a6d77e
+		range->start = base;
a6d77e
+		range->end = base + length - 1;
a6d77e
+		range->type = RANGE_RAM;
a6d77e
+		crashmem_rgns.size++;
a6d77e
+	} else if (strncmp(str, "Crash kernel\n", 13) == 0) {
a6d77e
+		if (base < arm64_mem.memstart)
a6d77e
+			base += arm64_mem.memstart;
a6d77e
+		crash_reserved_mem.start = base;
a6d77e
+		crash_reserved_mem.end = base + length - 1;
a6d77e
+		crash_reserved_mem.type = RANGE_RAM;
a6d77e
+		usablemem_rgns.size++;
a6d77e
+	}
a6d77e
+
a6d77e
+	return 0;
a6d77e
+}
a6d77e
+
a6d77e
+/*
a6d77e
+ * crash_exclude_range() - excludes memory region reserved for crashkernel
a6d77e
+ *
a6d77e
+ * Function locates where crashkernel reserved memory is and removes that
a6d77e
+ * region from the available memory regions.
a6d77e
+ */
a6d77e
+static void crash_exclude_range(void)
a6d77e
+{
a6d77e
+	const struct memory_range *range = &crash_reserved_mem;
a6d77e
+	int i;
a6d77e
+
a6d77e
+	for (i = 0; i < crashmem_rgns.size; i++) {
a6d77e
+		struct memory_range *r = crashmem_rgns.ranges + i;
a6d77e
+
a6d77e
+		/*
a6d77e
+		 * We assume that crash area is fully contained in
a6d77e
+		 * some larger memory area.
a6d77e
+		 */
a6d77e
+		if (r->start <= range->start && r->end >= range->end) {
a6d77e
+			struct memory_range *new;
a6d77e
+
a6d77e
+			if (r->start == range->start) {
a6d77e
+				if (r->end == range->end) {
a6d77e
+					memcpy(r, r + 1,
a6d77e
+						sizeof(r)
a6d77e
+						* (crashmem_rgns.size - i - 1));
a6d77e
+					crashmem_rgns.size--;
a6d77e
+				} else {
a6d77e
+					r->start = range->end + 1;
a6d77e
+				}
a6d77e
+				break;
a6d77e
+			}
a6d77e
+			if (r->end == range->end) {
a6d77e
+				r->end = range->start - 1;
a6d77e
+				break;
a6d77e
+			}
a6d77e
+
a6d77e
+			/*
a6d77e
+			 * Let's split this area into 2 smaller ones and
a6d77e
+			 * remove excluded range from between. First create
a6d77e
+			 * new entry for the remaining area.
a6d77e
+			 */
a6d77e
+			new = crashmem_rgns.ranges + crashmem_rgns.size;
a6d77e
+			new->start = range->end + 1;
a6d77e
+			new->end = r->end;
a6d77e
+			crashmem_rgns.size++;
a6d77e
+			/*
a6d77e
+			 * Next update this area to end before excluded range.
a6d77e
+			 */
a6d77e
+			r->end = range->start - 1;
a6d77e
+			break;
a6d77e
+		}
a6d77e
+	}
a6d77e
+}
a6d77e
+
a6d77e
+/*
a6d77e
+ * crash_get_memory_ranges() - read system physical memory
a6d77e
+ *
a6d77e
+ * Function reads through system physical memory and stores found memory
a6d77e
+ * regions in @crash_memory_ranges. Number of memory regions found is placed
a6d77e
+ * in @crash_memory_nr_ranges. Regions are sorted in ascending order.
a6d77e
+ *
a6d77e
+ * Returns %0 in case of success and %-1 otherwise (errno is set).
a6d77e
+ */
a6d77e
+static int crash_get_memory_ranges(void)
a6d77e
+{
a6d77e
+	/*
a6d77e
+	 * First read all memory regions that can be considered as
a6d77e
+	 * system memory including the crash area.
a6d77e
+	 */
a6d77e
+	kexec_iomem_for_each_line(NULL, crash_range_callback, NULL);
a6d77e
+
a6d77e
+	if (usablemem_rgns.size != 1) {
a6d77e
+		errno = EINVAL;
a6d77e
+		return -1;
a6d77e
+	}
a6d77e
+
a6d77e
+	/*
a6d77e
+	 * Exclude memory reserved for crashkernel (this may result int
a6d77e
+	 * split memory regions).
a6d77e
+	 */
a6d77e
+	/*
a6d77e
+	 * FIXME:
a6d77e
+	 * Do we have to check crashkernel is within main memory?
a6d77e
+	 */
a6d77e
+	crash_exclude_range();
a6d77e
+
a6d77e
+	return 0;
a6d77e
+}
a6d77e
+
a6d77e
+/*
a6d77e
+ * range_size - Return range size in MiB.
a6d77e
+ */
a6d77e
+
a6d77e
+static unsigned long range_size(const struct memory_range *r)
a6d77e
+{
a6d77e
+	return (r->end - r->start + 1) >> 20;
a6d77e
+}
a6d77e
+
a6d77e
+static void dump_crash_ranges(void)
a6d77e
+{
a6d77e
+	int i;
a6d77e
+
a6d77e
+	if (!kexec_debug)
a6d77e
+		return;
a6d77e
+
a6d77e
+	dbgprintf("%s: kernel: %016llx - %016llx (%ld MiB)\n", __func__,
a6d77e
+		  crash_reserved_mem.start, crash_reserved_mem.end,
a6d77e
+		  range_size(&crash_reserved_mem));
a6d77e
+
a6d77e
+	for (i = 0; i < crashmem_rgns.size; i++) {
a6d77e
+		struct memory_range *r = crashmem_rgns.ranges + i;
a6d77e
+		dbgprintf("%s: RAM:    %016llx - %016llx (%ld MiB)\n", __func__,
a6d77e
+			  r->start, r->end, range_size(r));
a6d77e
+	}
a6d77e
+}
a6d77e
+
a6d77e
+/*
a6d77e
+ * load_crashdump_segments() - create elf core header for /proc/vmcore
a6d77e
+ * @info: kexec info structure
a6d77e
+ * @option: To be appended to kernel command line
a6d77e
+ *
a6d77e
+ * This function loads additional segments which are needed for the dump
a6d77e
+ * capture kernel. It also updates kernel command line passed in
a6d77e
+ * @command_line_extra to have the correct parameters for the dump capture
a6d77e
+ * kernel.
a6d77e
+ *
a6d77e
+ * Return %0 in case of success and %-1 in case of error.
a6d77e
+ */
a6d77e
+
a6d77e
+int load_crashdump_segments(struct kexec_info *info, char **option)
a6d77e
+{
a6d77e
+	unsigned long elfcorehdr;
a6d77e
+	unsigned long bufsz;
a6d77e
+	void *buf;
a6d77e
+	int err;
a6d77e
+
a6d77e
+	/*
a6d77e
+	 * First fetch all the memory (RAM) ranges that we are going to
a6d77e
+	 * pass to the crashdump kernel during panic.
a6d77e
+	 */
a6d77e
+
a6d77e
+	err = crash_get_memory_ranges();
a6d77e
+
a6d77e
+	if (err)
a6d77e
+		return err;
a6d77e
+
a6d77e
+	dump_crash_ranges();
a6d77e
+
a6d77e
+	elf_info.page_offset = arm64_mem.page_offset;
a6d77e
+
a6d77e
+	err = crash_create_elf64_headers(info, &elf_info, crashmem_rgns.ranges,
a6d77e
+		crashmem_rgns.size, &buf, &bufsz, ELF_CORE_HEADER_ALIGN);
a6d77e
+
a6d77e
+	if (err)
a6d77e
+		return err;
a6d77e
+
a6d77e
+	/*
a6d77e
+	 * FIXME: check if we need 128KB alignment here as stated in arm.
a6d77e
+	 */
a6d77e
+	elfcorehdr = add_buffer_phys_virt(info, buf, bufsz, bufsz, 0,
a6d77e
+		crash_reserved_mem.start, crash_reserved_mem.end,
a6d77e
+		-1, 0);
a6d77e
+
a6d77e
+	err = asprintf(option, " elfcorehdr=%#lx@%#lx", bufsz, elfcorehdr);
a6d77e
+
a6d77e
+	if (err == -1)
a6d77e
+		return err;
a6d77e
+
a6d77e
+	dbgprintf("%s:%s\n", __func__, *option);
a6d77e
+
a6d77e
 	return 0;
a6d77e
 }
a6d77e
+
a6d77e
+void modify_ehdr_for_crashmem(struct mem_ehdr *ehdr)
a6d77e
+{
a6d77e
+	struct mem_phdr *phdr;
a6d77e
+	int i;
a6d77e
+
a6d77e
+	ehdr->e_entry += crash_reserved_mem.start;
a6d77e
+
a6d77e
+	for (i = 0; i < ehdr->e_phnum; i++) {
a6d77e
+		phdr = &ehdr->e_phdr[i];
a6d77e
+		if (phdr->p_type != PT_LOAD)
a6d77e
+			continue;
a6d77e
+		phdr->p_paddr += 
a6d77e
+			(-arm64_mem.memstart + crash_reserved_mem.start);
a6d77e
+	}
a6d77e
+}
a6d77e
+
a6d77e
+void set_crash_entry(struct mem_ehdr *ehdr, struct kexec_info *info)
a6d77e
+{
a6d77e
+	info->entry = (void *)crash_reserved_mem.start + arm64_mem.text_offset;
a6d77e
+}
a6d77e
diff --git a/kexec/arch/arm64/crashdump-arm64.h b/kexec/arch/arm64/crashdump-arm64.h
a6d77e
index f33c7a25b454..3f8b38350841 100644
a6d77e
--- a/kexec/arch/arm64/crashdump-arm64.h
a6d77e
+++ b/kexec/arch/arm64/crashdump-arm64.h
a6d77e
@@ -1,12 +1,25 @@
a6d77e
 /*
a6d77e
- * ARM64 crashdump.
a6d77e
+ * crashdump for arm64
a6d77e
+ *
a6d77e
+ * Copyright (c) 2014 Linaro Limited
a6d77e
+ * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
a6d77e
+ *
a6d77e
+ * This program is free software; you can redistribute it and/or modify
a6d77e
+ * it under the terms of the GNU General Public License version 2 as
a6d77e
+ * published by the Free Software Foundation.
a6d77e
  */
a6d77e
 
a6d77e
-#if !defined(CRASHDUMP_ARM64_H)
a6d77e
+#ifndef CRASHDUMP_ARM64_H
a6d77e
 #define CRASHDUMP_ARM64_H
a6d77e
 
a6d77e
 #include "kexec.h"
a6d77e
 
a6d77e
+#define CRASH_MAX_MEMORY_RANGES	32
a6d77e
+
a6d77e
 extern struct memory_ranges usablemem_rgns;
a6d77e
 
a6d77e
+int load_crashdump_segments(struct kexec_info *info, char **option);
a6d77e
+void modify_ehdr_for_crashmem(struct mem_ehdr *ehdr);
a6d77e
+void set_crash_entry(struct mem_ehdr *ehdr, struct kexec_info *info);
a6d77e
+
a6d77e
 #endif
a6d77e
diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c
a6d77e
index eb68b6b3d9e3..e83e83bdd6db 100644
a6d77e
--- a/kexec/arch/arm64/kexec-arm64.c
a6d77e
+++ b/kexec/arch/arm64/kexec-arm64.c
a6d77e
@@ -758,7 +758,7 @@ unsigned long phys_to_virt(struct crash_elf_info *UNUSED(elf_info),
a6d77e
 	v = p - arm64_mem.memstart + arm64_mem.page_offset;
a6d77e
 
a6d77e
 	dbgprintf("%s: %016lx -> %016lx\n", __func__, p, v);
a6d77e
-	return p;
a6d77e
+	return v;
a6d77e
 }
a6d77e
 
a6d77e
 void add_segment(struct kexec_info *info, const void *buf, size_t bufsz,
a6d77e
@@ -924,7 +924,7 @@ static int get_memory_ranges_iomem(struct memory_range *array,
a6d77e
 
a6d77e
 		r.type = RANGE_RAM;
a6d77e
 
a6d77e
-		dbgprintf("%s:%d: RAM:  %016llx - %016llx : %s", __func__,
a6d77e
+		dbgprintf("%s:%d: RAM: %016llx - %016llx : %s", __func__,
a6d77e
 			__LINE__, r.start, r.end, str);
a6d77e
 
a6d77e
 		array[(*count)++] = r;
a6d77e
diff --git a/kexec/arch/arm64/kexec-elf-arm64.c b/kexec/arch/arm64/kexec-elf-arm64.c
a6d77e
index 8b336054a6ab..903602985eb4 100644
a6d77e
--- a/kexec/arch/arm64/kexec-elf-arm64.c
a6d77e
+++ b/kexec/arch/arm64/kexec-elf-arm64.c
a6d77e
@@ -9,9 +9,10 @@
a6d77e
 #include <errno.h>
a6d77e
 #include <getopt.h>
a6d77e
 #include <libfdt.h>
a6d77e
+#include <stdlib.h>
a6d77e
 
a6d77e
-#include "dt-ops.h"
a6d77e
 #include "crashdump-arm64.h"
a6d77e
+#include "dt-ops.h"
a6d77e
 #include "kexec-arm64.h"
a6d77e
 #include "fs2dt.h"
a6d77e
 #include "kexec-syscall.h"
a6d77e
@@ -45,16 +46,13 @@ on_exit:
a6d77e
 int elf_arm64_load(int argc, char **argv, const char *kernel_buf,
a6d77e
 	off_t kernel_size, struct kexec_info *info)
a6d77e
 {
a6d77e
+	char *header_option = NULL;
a6d77e
 	int result;
a6d77e
 	struct mem_ehdr ehdr;
a6d77e
 	bool found_header;
a6d77e
 	int i;
a6d77e
 
a6d77e
-	if (info->kexec_flags & KEXEC_ON_CRASH) {
a6d77e
-		fprintf(stderr, "kexec: kdump not yet supported on arm64\n");
a6d77e
-		return -EINVAL;
a6d77e
-	}
a6d77e
-
a6d77e
+	/* Parse the Elf file */
a6d77e
 	result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0);
a6d77e
 
a6d77e
 	if (result < 0) {
a6d77e
@@ -94,6 +92,18 @@ int elf_arm64_load(int argc, char **argv, const char *kernel_buf,
a6d77e
 		goto exit;
a6d77e
 	}
a6d77e
 
a6d77e
+	if (info->kexec_flags & KEXEC_ON_CRASH) {
a6d77e
+		result = load_crashdump_segments(info, &header_option);
a6d77e
+
a6d77e
+		if (result) {
a6d77e
+			fprintf(stderr, "kexec: creating eflcorehdr failed.\n");
a6d77e
+			goto exit;
a6d77e
+		}
a6d77e
+	}
a6d77e
+
a6d77e
+	if (info->kexec_flags & KEXEC_ON_CRASH)
a6d77e
+		modify_ehdr_for_crashmem(&ehdr);
a6d77e
+
a6d77e
 	result = elf_exec_load(&ehdr, info);
a6d77e
 
a6d77e
 	if (result) {
a6d77e
@@ -101,6 +111,11 @@ int elf_arm64_load(int argc, char **argv, const char *kernel_buf,
a6d77e
 		goto exit;
a6d77e
 	}
a6d77e
 
a6d77e
+	if (info->kexec_flags & KEXEC_ON_CRASH)
a6d77e
+		set_crash_entry(&ehdr, info);
a6d77e
+	else
a6d77e
+		info->entry = (void *)virt_to_phys(ehdr.e_entry);
a6d77e
+
a6d77e
 	dbgprintf("%s: text_offset: %016lx\n", __func__, arm64_mem.text_offset);
a6d77e
 	dbgprintf("%s: image_size:  %016lx\n", __func__, arm64_mem.image_size);
a6d77e
 	dbgprintf("%s: page_offset: %016lx\n", __func__, arm64_mem.page_offset);
a6d77e
@@ -108,9 +123,11 @@ int elf_arm64_load(int argc, char **argv, const char *kernel_buf,
a6d77e
 	dbgprintf("%s: e_entry:     %016llx -> %016lx\n", __func__,
a6d77e
 		ehdr.e_entry, virt_to_phys(ehdr.e_entry));
a6d77e
 
a6d77e
-	result = arm64_load_other_segments(info, virt_to_phys(ehdr.e_entry));
a6d77e
+	result = arm64_load_other_segments(info, (unsigned long)info->entry);
a6d77e
 exit:
a6d77e
 	free_elf_info(&ehdr);
a6d77e
+	if (header_option)
a6d77e
+		free(header_option);
a6d77e
 	return result;
a6d77e
 }
a6d77e
 
a6d77e
-- 
a6d77e
2.1.0
a6d77e