From c38810a00a431ec61f42161464bc2fe8bf28b6a3 Mon Sep 17 00:00:00 2001
From: Vinayak Menon <vinayakm.list@gmail.com>
Date: Tue, 22 Jul 2014 20:21:24 +0530
Subject: [PATCH] raw binary RAM image support

Signed-off-by: Vinayak Menon <vinayakm.list@gmail.com>
---
 Makefile  |    9 ++-
 defs.h    |    7 ++
 help.c    |    5 +
 main.c    |   41 +++++++-
 ramdump.c |  317 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 372 insertions(+), 7 deletions(-)
 create mode 100644 ramdump.c

diff --git a/Makefile b/Makefile
index 41c0ece..6991d84 100644
--- a/Makefile
+++ b/Makefile
@@ -68,7 +68,8 @@ CFILES=main.c tools.c global_data.c memory.c filesys.c help.c task.c \
 	netdump.c diskdump.c makedumpfile.c xendump.c unwind.c unwind_decoder.c \
 	unwind_x86_32_64.c unwind_arm.c \
 	xen_hyper.c xen_hyper_command.c xen_hyper_global_data.c \
-	xen_hyper_dump_tables.c kvmdump.c qemu.c qemu-load.c sadump.c ipcs.c
+	xen_hyper_dump_tables.c kvmdump.c qemu.c qemu-load.c ramdump.c \
+	sadump.c ipcs.c
 
 SOURCE_FILES=${CFILES} ${GENERIC_HFILES} ${MCORE_HFILES} \
 	${REDHAT_CFILES} ${REDHAT_HFILES} ${UNWIND_HFILES} \
@@ -85,7 +86,8 @@ OBJECT_FILES=main.o tools.o global_data.o memory.o filesys.o help.o task.o \
 	lkcd_x86_trace.o unwind_v1.o unwind_v2.o unwind_v3.o \
 	unwind_x86_32_64.o unwind_arm.o \
 	xen_hyper.o xen_hyper_command.o xen_hyper_global_data.o \
-	xen_hyper_dump_tables.o kvmdump.o qemu.o qemu-load.o sadump.o ipcs.o
+	xen_hyper_dump_tables.o kvmdump.o qemu.o qemu-load.o ramdump.o \
+	sadump.o ipcs.o
 
 MEMORY_DRIVER_FILES=memory_driver/Makefile memory_driver/crash.c memory_driver/README
 
@@ -474,6 +476,9 @@ xen_hyper_global_data.o: ${GENERIC_HFILES} xen_hyper_global_data.c
 xen_hyper_dump_tables.o: ${GENERIC_HFILES} xen_hyper_dump_tables.c
 	${CC} -c ${CRASH_CFLAGS} xen_hyper_dump_tables.c ${WARNING_OPTIONS} ${WARNING_ERROR}
 
+ramdump.o: ${GENERIC_HFILES} ${REDHAT_HFILES} ramdump.c
+	${CC} -c ${CRASH_CFLAGS} ramdump.c ${WARNING_OPTIONS} ${WARNING_ERROR}
+
 ${PROGRAM}: force
 	@make --no-print-directory all
 
diff --git a/defs.h b/defs.h
index f8e1150..0698132 100644
--- a/defs.h
+++ b/defs.h
@@ -5417,6 +5417,13 @@ int kcore_memory_dump(FILE *);
 void dump_registers_for_qemu_mem_dump(void);
 void kdump_backup_region_init(void);
 
+struct ramdump_def {
+	char *path;
+	ulong start_paddr;
+	ulong size;
+};
+char *ramdump_to_elf(struct ramdump_def *ramdump, int nodes, char *elf);
+
 /*
  *  diskdump.c
  */
diff --git a/help.c b/help.c
index 91eea95..bf6b0c8 100644
--- a/help.c
+++ b/help.c
@@ -319,6 +319,11 @@ char *program_usage_info[] = {
     "  --kvmio <size>",
     "    override the automatically-calculated KVM guest I/O hole size.",
     "",
+    "  --ram_start <address>",
+    "    When using a raw RAM image as input, pass the start of each node image",
+    "    with this option. The number of node images passed much match with the",
+    "    number of times this option is passed",
+    "",
     "FILES:",
     "",
     "  .crashrc",
diff --git a/main.c b/main.c
index e2c76e0..0cb09d7 100644
--- a/main.c
+++ b/main.c
@@ -69,13 +69,16 @@ static struct option long_options[] = {
 	{"hex", 0, 0, 0},
 	{"dec", 0, 0, 0},
 	{"no_strip", 0, 0, 0},
+	{"ram_start", required_argument, 0, 0},
         {0, 0, 0, 0}
 };
 
 int
 main(int argc, char **argv)
 {
-	int i, c, option_index;
+	int i, c, option_index, nodes = 0, rams = 0;
+	struct ramdump_def *rd = NULL;
+	char *elf_out = NULL;
 	char *tmpname;
 
 	setup_environment(argc, argv);
@@ -85,7 +88,7 @@ main(int argc, char **argv)
 	 */
 	opterr = 0;
 	optind = 0;
-	while((c = getopt_long(argc, argv, "Lkgh::e:i:sSvc:d:tfp:m:x",
+	while((c = getopt_long(argc, argv, "Lkgh::e:i:sSvc:d:tfp:m:xo:",
        		long_options, &option_index)) != -1) {
 		switch (c)
 		{
@@ -273,7 +276,14 @@ main(int argc, char **argv)
 				pc->flags2 |= RADIX_OVERRIDE;
 				pc->output_radix = 10;
 			}
-			
+
+			else if (STREQ(long_options[option_index].name, "ram_start")) {
+				nodes++;
+				rd = realloc(rd, sizeof(struct ramdump_def) * nodes);
+				if (!rd)
+					error(FATAL, "realloc failure\n");
+				rd[nodes - 1].start_paddr = strtoul(optarg, NULL, 0);
+			}
 
 			else {
 				error(INFO, "internal error: option %s unhandled\n",
@@ -381,6 +391,10 @@ main(int argc, char **argv)
 			pc->flags |= PRELOAD_EXTENSIONS;
 			break;
 
+		case 'o':
+			elf_out = optarg;
+			break;
+
 		default:
 			error(INFO, "invalid option: %s\n",
 				argv[optind-1]);
@@ -453,7 +467,13 @@ main(int argc, char **argv)
 			if (is_flattened_format(argv[optind]))
 				pc->flags2 |= FLAT;
 
-			if (STREQ(argv[optind], "/dev/mem")) {
+			if (rd) {
+				if (rams < nodes)
+					rd[rams++].path = argv[optind];
+				else
+					error(INFO, "too many dumpfile arguments\n");
+
+			} else if (STREQ(argv[optind], "/dev/mem")) {
                         	if (pc->flags & MEMORY_SOURCES) {
                                 	error(INFO, 
                                             "too many dumpfile arguments\n");
@@ -605,7 +625,18 @@ main(int argc, char **argv)
 		}
 		optind++;
 	}
-	
+
+	if (rd) {
+		if (rams != nodes)
+			error(FATAL, "Number of nodes not matching with ram images\n");
+		pc->dumpfile = ramdump_to_elf(rd, nodes, elf_out);
+                if (is_kdump(pc->dumpfile, KDUMP_LOCAL)) {
+			pc->flags |= KDUMP;
+			pc->readmem = read_kdump;
+			pc->writemem = write_kdump;
+		}
+	}
+
 	check_xen_hyper();
 
         if (setjmp(pc->main_loop_env))
diff --git a/ramdump.c b/ramdump.c
new file mode 100644
index 0000000..3bc0d79
--- /dev/null
+++ b/ramdump.c
@@ -0,0 +1,317 @@
+/*
+ * ramdump.c - core analysis suite
+ *
+ * Copyright (c) 2014 Vinayak Menon <vinayakm.list@gmail.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.
+ *
+ * Author: Vinayak Menon <vinayakm.list@gmail.com>
+ */
+
+#define _LARGEFILE64_SOURCE 1  /* stat64() */
+#include "defs.h"
+#include <libelf.h>
+
+#define ELF_DEFAULT_FILE "ramdump_elf"
+
+static void alloc_elf_header(char *e, ushort e_machine, int nodes)
+{
+	switch (e_machine) {
+	case EM_ARM:
+	{
+		Elf32_Ehdr *ehdr = (Elf32_Ehdr *)e;
+		memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
+		ehdr->e_ident[EI_CLASS] = ELFCLASS32;
+		ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
+		ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+		ehdr->e_ident[EI_OSABI] = ELFOSABI_LINUX;
+		ehdr->e_ident[EI_ABIVERSION] = 0;
+		memset(ehdr->e_ident+EI_PAD,
+			0, EI_NIDENT-EI_PAD);
+		ehdr->e_type = ET_CORE;
+		ehdr->e_machine = EM_ARM;
+		ehdr->e_version = EV_CURRENT;
+		ehdr->e_shentsize = 0x0;
+		ehdr->e_shnum = 0x0;
+		ehdr->e_shoff = 0x0;
+		ehdr->e_shstrndx = 0x0;
+		ehdr->e_phnum = 1 + nodes;
+		ehdr->e_entry = 0;
+		ehdr->e_phoff = sizeof(Elf32_Ehdr);
+		ehdr->e_flags = 0;
+		ehdr->e_ehsize = sizeof(Elf32_Ehdr);
+		ehdr->e_phentsize = sizeof(Elf32_Phdr);
+		break;
+	}
+	case EM_AARCH64:
+	{
+		Elf64_Ehdr *ehdr = (Elf64_Ehdr *)e;
+		memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
+		ehdr->e_ident[EI_CLASS] = ELFCLASS64;
+		ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
+		ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+		ehdr->e_ident[EI_OSABI] = ELFOSABI_LINUX;
+		ehdr->e_ident[EI_ABIVERSION] = 0;
+		memset(ehdr->e_ident+EI_PAD, 0,
+			EI_NIDENT-EI_PAD);
+		ehdr->e_type = ET_CORE;
+		ehdr->e_machine = EM_AARCH64;
+		ehdr->e_version = EV_CURRENT;
+		ehdr->e_entry = 0;
+		ehdr->e_phoff = sizeof(Elf64_Ehdr);
+		ehdr->e_shoff = 0;
+		ehdr->e_flags = 0;
+		ehdr->e_ehsize = sizeof(Elf64_Ehdr);
+		ehdr->e_phentsize = sizeof(Elf64_Phdr);
+		ehdr->e_phnum = 1 + nodes;
+		ehdr->e_shentsize = 0;
+		ehdr->e_shnum = 0;
+		ehdr->e_shstrndx = 0;
+		break;
+	}
+	};
+}
+
+static int alloc_program_headers(char *e, ushort e_machine,
+	struct ramdump_def *r, int nodes)
+{
+	unsigned int i;
+	struct stat64 st;
+
+	switch (e_machine) {
+	case EM_ARM:
+	{
+		Elf32_Phdr *phdr = (Elf32_Phdr *)e;
+		for (i = 0; i < nodes; i++, phdr++) {
+			phdr[i].p_type = PT_LOAD;
+			if (0 > stat64(r[i].path, &st)) {
+				error(INFO, "ramdump stat failed\n");
+				return -1;
+			}
+			r[i].size = st.st_size;
+			phdr[i].p_filesz = r[i].size;
+			phdr[i].p_memsz = phdr[i].p_filesz;
+			/*
+			 * Yet to find a way to use PTOV,
+			 * as machdep_init is yet to be
+			 * invoked.
+			 */
+			phdr[i].p_vaddr = 0;
+			phdr[i].p_paddr = r[i].start_paddr;
+			phdr[i].p_flags = PF_R | PF_W | PF_X;
+			phdr[i].p_align = 0;
+		}
+		break;
+	}
+	case EM_AARCH64:
+	{
+		Elf64_Phdr *phdr = (Elf64_Phdr *)e;
+		for (i = 0; i < nodes; i++, phdr++) {
+			phdr[i].p_type = PT_LOAD;
+			if (0 > stat64(r[i].path, &st)) {
+				error(INFO, "ramdump stat failed\n");
+				return -1;
+			}
+			r[i].size = st.st_size;
+			phdr[i].p_filesz = r[i].size;
+			phdr[i].p_memsz = phdr[i].p_filesz;
+			phdr[i].p_vaddr = 0;
+			phdr[i].p_paddr = r[i].start_paddr;
+			phdr[i].p_flags = PF_R | PF_W | PF_X;
+			phdr[i].p_align = 0;
+		}
+		break;
+	}
+	};
+
+	return 0;
+}
+
+static char *write_elf(ushort e_machine, void *load, char *e_head,
+	size_t data_offset, struct ramdump_def *r,
+	char *elf, int nodes)
+{
+#define CPY_BUF_SZ 4096
+	int fd1, fd2, i, err = 1;
+	char *buf;
+	char *out_elf = ELF_DEFAULT_FILE;
+	size_t rd, len, offset;
+
+	buf = (char *)GETBUF(CPY_BUF_SZ);
+
+	if (elf)
+		out_elf = elf;
+
+	error(NOTE, "Creating kdump ELF %s\n", out_elf);
+
+	offset = data_offset;
+
+	fd2 = open(out_elf, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
+	if (!fd2) {
+		error(INFO, "%s open error\n", out_elf);
+		goto end1;
+	}
+
+	while (offset > 0) {
+		len = write(fd2, e_head + (data_offset - offset), offset);
+		if (len < 0) {
+			error(INFO, "ramdump write error\n");
+			goto end;
+		}
+		offset -= len;
+	}
+
+	for (i = 0; i < nodes; i++) {
+		if (e_machine == EM_ARM)
+			offset = ((Elf32_Phdr *)load)[i].p_offset;
+		else if (e_machine == EM_AARCH64)
+			offset = ((Elf64_Phdr *)load)[i].p_offset;
+
+		fd1 = open(r[i].path, O_RDONLY, S_IRUSR);
+		if (!fd1) {
+			error(INFO, "%s open error\n", r[i].path);
+			goto end;
+		}
+
+		lseek(fd2, (off_t)offset, SEEK_SET);
+		while ((rd = read(fd1, buf, CPY_BUF_SZ)) > 0) {
+			if (write(fd2, buf, rd) != rd) {
+				error(INFO, "%s write error\n", r[i].path);
+				close(fd1);
+				goto end;
+			}
+		}
+		close(fd1);
+	}
+
+	err = 0;
+end:
+	close(fd2);
+end1:
+	FREEBUF(buf);
+	return err ? NULL : out_elf;
+}
+
+static void alloc_notes(char *e_head, ushort e_machine)
+{
+	/* Nothing filled in as of now */
+	switch (e_machine) {
+	case EM_ARM:
+	{
+		Elf32_Phdr *notes = (Elf32_Phdr *)e_head;
+		notes->p_type = PT_NOTE;
+		notes->p_offset = 0;
+		notes->p_vaddr = 0;
+		notes->p_paddr = 0;
+		notes->p_filesz = 0;
+		notes->p_memsz = 0;
+		notes->p_flags = 0;
+		notes->p_align = 0;
+		break;
+	}
+	case EM_AARCH64:
+	{
+		Elf64_Phdr *notes = (Elf64_Phdr *)e_head;
+		notes->p_type = PT_NOTE;
+		notes->p_offset = 0;
+		notes->p_vaddr = 0;
+		notes->p_paddr = 0;
+		notes->p_filesz = 0;
+		notes->p_memsz = 0;
+		notes->p_flags = 0;
+		notes->p_align = 0;
+		break;
+	}
+	};
+}
+
+char *ramdump_to_elf(struct ramdump_def *ramdump, int nodes, char *elf)
+{
+	int i;
+	char *e_head, *ptr, *e_file = NULL;
+	ushort e_machine = 0;
+	size_t offset, data_offset;
+	size_t l_offset32, l_offset64;
+	size_t szelfhdr = 0, szpghdr = 0;
+	Elf64_Phdr *notes64, *load64;
+	Elf32_Phdr *load32, *notes32;
+
+	if (machine_type("ARM")) {
+		e_machine = EM_ARM;
+		szelfhdr = sizeof(Elf32_Ehdr);
+		szpghdr = sizeof(Elf32_Phdr);
+	} else if (machine_type("ARM64")) {
+		e_machine = EM_AARCH64;
+		szelfhdr = sizeof(Elf64_Ehdr);
+		szpghdr = sizeof(Elf64_Phdr);
+	} else {
+		error(INFO, "unknown machine type\n");
+		return NULL;
+	}
+
+	e_head = (char *)GETBUF(szelfhdr +
+		nodes * szpghdr + PAGESIZE() * 2);
+	ptr = e_head;
+	offset = 0;
+
+	error(NOTE, "Converting ramdump to kdump\n");
+	alloc_elf_header(ptr, e_machine, nodes);
+
+	ptr += szelfhdr;
+	offset += szelfhdr;
+
+	notes32 = (Elf32_Phdr *)ptr;
+	notes64 = (Elf64_Phdr *)ptr;
+
+	alloc_notes(ptr, e_machine);
+
+	offset += szpghdr;
+	ptr += szpghdr;
+
+	load32 = (Elf32_Phdr *)ptr;
+	load64 = (Elf64_Phdr *)ptr;
+
+	if (alloc_program_headers(ptr, e_machine, ramdump, nodes))
+		goto end;
+
+	offset += szpghdr * nodes;
+	ptr += szpghdr * nodes;
+
+	/* Empty note */
+	notes32->p_offset = offset;
+	notes64->p_offset = offset;
+
+	l_offset32 = offset;
+	l_offset64 = offset;
+
+	data_offset = offset;
+
+	switch (e_machine) {
+	case EM_ARM:
+		for (i = 0; i < nodes; i++) {
+			load32[i].p_offset = l_offset32;
+			l_offset32 += load32[i].p_filesz;
+		}
+		break;
+	case EM_AARCH64:
+		for (i = 0; i < nodes; i++) {
+			load64[i].p_offset = l_offset64;
+			l_offset64 += load64[i].p_filesz;
+		}
+		break;
+	}
+
+	e_file = write_elf(e_machine, load32, e_head, data_offset, ramdump, elf, nodes);
+end:
+	FREEBUF(e_head);
+	return e_file;
+}
+
-- 
1.7.6

