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

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