From 499b87366bc9cc3dda8b9c2ed8fefbd17ce93c2f Mon Sep 17 00:00:00 2001
From: Vinayak Menon <vinayakm.list@gmail.com>
Date: Wed, 23 Jul 2014 11:30:40 +0530
Subject: [PATCH] raw RAM image support

Raw RAM image can be passed to crash utility in the
following format.

> crash [options] image@address... [namelist] [-o out_file]
image@address is an ordered pair where, image is the raw
RAM file representing a memory node, and address is the
physical start of the node. Multiple such ordered pairs can
be provided, which represent different memory nodes.

The raw dump is converted to kdump format.

Optionally -o can be used to specify the ELF output file
to be generated.

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

diff --git a/Makefile b/Makefile
index cf3ae44..64616fc 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 sadump.c ipcs.c \
+	ramdump.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 sadump.o ipcs.o \
+	ramdump.o
 
 MEMORY_DRIVER_FILES=memory_driver/Makefile memory_driver/crash.c memory_driver/README
 
@@ -485,6 +487,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 44df6ae..33d4e8b 100644
--- a/defs.h
+++ b/defs.h
@@ -5528,6 +5528,13 @@ void dump_registers_for_qemu_mem_dump(void);
 void kdump_backup_region_init(void);
 
 /*
+ * ramdump.c
+ */
+int is_ramdump(char *pattern);
+char *ramdump_to_elf(void);
+void ramdump_elf_output_file(char *opt);
+
+/*
  *  diskdump.c
  */
 int is_diskdump(char *);
diff --git a/help.c b/help.c
index e1f265a..00843aa 100644
--- a/help.c
+++ b/help.c
@@ -48,6 +48,7 @@ char *program_usage_info[] = {
     "USAGE:",
     "",
     "  crash [OPTION]... NAMELIST MEMORY-IMAGE  (dumpfile form)",
+    "  crash [OPTION]... NAMELIST MEMORY-IMAGE@ADDRESS...  (dumpfile form)",
     "  crash [OPTION]... [NAMELIST]             (live system form)",
     "",
     "OPTIONS:",
@@ -69,6 +70,10 @@ char *program_usage_info[] = {
     "    /dev/mem will be used; but if the kernel has been configured with ",
     "    CONFIG_STRICT_DEVMEM, then /proc/kcore will be used.  It is permissible",
     "    to explicitly enter /dev/crash, /dev/mem or /proc/kcore.",
+    "    @ADDRESS is used along with MEMORY-IMAGE when the dumpfile is a raw dump",
+    "    which needs a conversion to kernel core dump format. Multiple",
+    "    MEMORY-IMAGE@ADDRESS ordered pairs can be used, each representing a",
+    "    memory node. ADDRESS is the physical start address of the node",
     "",
     "  mapfile",
     "    If the NAMELIST file is not the same kernel that is running",
@@ -139,6 +144,10 @@ char *program_usage_info[] = {
     "    architecture-specific option/pairs should only be required in",
     "    very rare circumstances:",
     "",
+    "  -o file",
+    "  Can be used when the MEMORY-IMAGE@ADDRESS format is used, to specify",
+    "  the path and name of the kdump ELF generated.",
+    "",
     "    X86_64:",
     "      phys_base=<physical-address>",
     "      irq_eframe_link=<value>",
diff --git a/main.c b/main.c
index b0524d2..0bd6575 100644
--- a/main.c
+++ b/main.c
@@ -86,7 +86,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)
 		{
@@ -387,6 +387,10 @@ main(int argc, char **argv)
 			pc->flags |= PRELOAD_EXTENSIONS;
 			break;
 
+		case 'o':
+			ramdump_elf_output_file(optarg);
+			break;
+
 		default:
 			error(INFO, "invalid option: %s\n",
 				argv[optind-1]);
@@ -402,6 +406,26 @@ main(int argc, char **argv)
 	 */
 	while (argv[optind]) {
 
+		if (is_ramdump(argv[optind])) {
+			if (pc->flags & MEMORY_SOURCES) {
+				error(INFO,
+					"too many dumpfile arguments\n");
+					program_usage(SHORT_FORM);
+			}
+			pc->dumpfile = ramdump_to_elf();
+			if (is_kdump(pc->dumpfile, KDUMP_LOCAL)) {
+				pc->flags |= KDUMP;
+				pc->readmem = read_kdump;
+				pc->writemem = write_kdump;
+			} else {
+				error(INFO, "malformed ELF file: %s\n",
+					pc->dumpfile);
+				program_usage(SHORT_FORM);
+			}
+			optind++;
+			continue;
+		}
+
 		if (is_remote_daemon(argv[optind])) {
                 	if (pc->flags & DUMPFILE_TYPES) {
 				error(INFO, 
diff --git a/ramdump.c b/ramdump.c
new file mode 100644
index 0000000..a09af7b
--- /dev/null
+++ b/ramdump.c
@@ -0,0 +1,360 @@
+/*
+ * 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"
+
+struct ramdump_def {
+	char *path;
+	ulong start_paddr;
+};
+
+static struct ramdump_def *ramdump;
+static int nodes;
+static char *user_elf;
+static char *pattern;
+
+static void alloc_elf_header(char *e, ushort e_machine)
+{
+	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)
+{
+	unsigned int i;
+	struct stat64 st;
+	struct ramdump_def *r = ramdump;
+
+	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;
+			}
+			phdr[i].p_filesz = st.st_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;
+			}
+			phdr[i].p_filesz = st.st_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)
+{
+#define CPY_BUF_SZ 4096
+	int fd1, fd2, i, err = 1;
+	char *buf;
+	char *out_elf = ELF_DEFAULT_FILE;
+	size_t rd, len, offset;
+	struct ramdump_def *r = ramdump;
+
+	buf = (char *)GETBUF(CPY_BUF_SZ);
+
+	if (user_elf)
+		out_elf = user_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(void)
+{
+	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);
+
+	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))
+		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);
+end:
+	FREEBUF(e_head);
+	FREEBUF(pattern);
+	free(ramdump);
+	return e_file;
+}
+
+int is_ramdump(char *p)
+{
+	char *x = NULL, *y = NULL, *pat;
+	size_t len;
+
+	if (nodes || !strchr(p, '@'))
+		return 0;
+
+	len = strlen(p);
+	pattern = (char *)GETBUF(len + 1);
+	strlcpy(pattern, p, len + 1);
+
+	pat = pattern;
+	while ((pat = strtok_r(pat, ",", &x))) {
+		if ((pat = strtok_r(pat, "@", &y))) {
+			nodes++;
+			ramdump = realloc(ramdump, sizeof(struct ramdump_def) * nodes);
+			if (!ramdump)
+				error(FATAL, "realloc failure\n");
+			ramdump[nodes - 1].path = pat;
+			pat = strtok_r(NULL, "@", &y);
+			ramdump[nodes - 1].start_paddr = strtoul(pat, NULL, 0);
+		}
+		pat = NULL;
+	}
+
+	return nodes;
+}
+
+void ramdump_elf_output_file(char *opt)
+{
+	user_elf = opt;
+}
-- 
1.7.6

