From: Hariprasad Nellitheertha <hari@in.ibm.com>

This patch contains the code that provides an ELF format interface to the
previous kernel's memory post kexec reboot.

Signed off by Hariprasad Nellitheertha <hari@in.ibm.com>

Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/fs/proc/Makefile           |    1 
 25-akpm/fs/proc/kcore.c            |   10 -
 25-akpm/fs/proc/proc_misc.c        |    1 
 25-akpm/fs/proc/vmcore.c           |  239 +++++++++++++++++++++++++++++++++++++
 25-akpm/include/linux/crash_dump.h |   10 +
 25-akpm/kernel/crash.c             |   16 ++
 6 files changed, 272 insertions(+), 5 deletions(-)

diff -puN fs/proc/kcore.c~crashdump-elf-format-dump-file-access fs/proc/kcore.c
--- 25/fs/proc/kcore.c~crashdump-elf-format-dump-file-access	2004-09-21 23:21:33.885426248 -0700
+++ 25-akpm/fs/proc/kcore.c	2004-09-21 23:21:33.896424576 -0700
@@ -114,7 +114,7 @@ static size_t get_kcore_size(int *nphdr,
 /*
  * determine size of ELF note
  */
-static int notesize(struct memelfnote *en)
+int notesize(struct memelfnote *en)
 {
 	int sz;
 
@@ -129,7 +129,7 @@ static int notesize(struct memelfnote *e
 /*
  * store a note in the header buffer
  */
-static char *storenote(struct memelfnote *men, char *bufp)
+char *storenote(struct memelfnote *men, char *bufp)
 {
 	struct elf_note en;
 
@@ -156,7 +156,7 @@ static char *storenote(struct memelfnote
  * store an ELF coredump header in the supplied buffer
  * nphdr is the number of elf_phdr to insert
  */
-static void elf_kcore_store_hdr(char *bufp, int nphdr, int dataoff)
+void elf_kcore_store_hdr(char *bufp, int nphdr, int dataoff, struct kcore_list *clist)
 {
 	struct elf_prstatus prstatus;	/* NT_PRSTATUS */
 	struct elf_prpsinfo prpsinfo;	/* NT_PRPSINFO */
@@ -208,7 +208,7 @@ static void elf_kcore_store_hdr(char *bu
 	nhdr->p_align	= 0;
 
 	/* setup ELF PT_LOAD program header for every area */
-	for (m=kclist; m; m=m->next) {
+	for (m=clist; m; m=m->next) {
 		phdr = (struct elf_phdr *) bufp;
 		bufp += sizeof(struct elf_phdr);
 		offset += sizeof(struct elf_phdr);
@@ -305,7 +305,7 @@ read_kcore(struct file *file, char __use
 			return -ENOMEM;
 		}
 		memset(elf_buf, 0, elf_buflen);
-		elf_kcore_store_hdr(elf_buf, nphdr, elf_buflen);
+		elf_kcore_store_hdr(elf_buf, nphdr, elf_buflen, kclist);
 		read_unlock(&kclist_lock);
 		if (copy_to_user(buffer, elf_buf + *fpos, tsz)) {
 			kfree(elf_buf);
diff -puN fs/proc/Makefile~crashdump-elf-format-dump-file-access fs/proc/Makefile
--- 25/fs/proc/Makefile~crashdump-elf-format-dump-file-access	2004-09-21 23:21:33.886426096 -0700
+++ 25-akpm/fs/proc/Makefile	2004-09-21 23:21:33.895424728 -0700
@@ -11,4 +11,5 @@ proc-y       += inode.o root.o base.o ge
 		kmsg.o proc_tty.o proc_misc.o
 
 proc-$(CONFIG_PROC_KCORE)	+= kcore.o
+proc-$(CONFIG_CRASH_DUMP)	+= vmcore.o
 proc-$(CONFIG_PROC_DEVICETREE)	+= proc_devtree.o
diff -puN fs/proc/proc_misc.c~crashdump-elf-format-dump-file-access fs/proc/proc_misc.c
--- 25/fs/proc/proc_misc.c~crashdump-elf-format-dump-file-access	2004-09-21 23:21:33.888425792 -0700
+++ 25-akpm/fs/proc/proc_misc.c	2004-09-21 23:21:33.897424424 -0700
@@ -694,6 +694,7 @@ void __init proc_misc_init(void)
 				(size_t)high_memory - PAGE_OFFSET + PAGE_SIZE;
 	}
 #endif
+	crash_create_proc_entry();
 #ifdef CONFIG_MAGIC_SYSRQ
 	entry = create_proc_entry("sysrq-trigger", S_IWUSR, NULL);
 	if (entry)
diff -puN /dev/null fs/proc/vmcore.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/fs/proc/vmcore.c	2004-09-21 23:21:33.898424272 -0700
@@ -0,0 +1,239 @@
+/*
+ *	fs/proc/vmcore.c Interface for accessing the crash
+ * 				 dump from the system's previous life.
+ * 	Heavily borrowed from fs/proc/kcore.c
+ *	Created by: Hariprasad Nellitheertha (hari@in.ibm.com)
+ *	Copyright (C) IBM Corporation, 2004. All rights reserved
+ */
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/elf.h>
+#include <linux/elfcore.h>
+#include <linux/vmalloc.h>
+#include <linux/proc_fs.h>
+#include <linux/highmem.h>
+#include <linux/bootmem.h>
+#include <linux/init.h>
+#include <linux/crash_dump.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+/* This is to re-use the kcore header creation code */
+static struct kcore_list vmcore_mem;
+
+static int open_vmcore(struct inode * inode, struct file * filp)
+{
+	return 0;
+}
+
+static ssize_t read_vmcore(struct file *,char __user *,size_t, loff_t *);
+
+#define BACKUP_START CRASH_BACKUP_BASE
+#define BACKUP_END CRASH_BACKUP_BASE + CRASH_BACKUP_SIZE
+#define REG_SIZE sizeof(elf_gregset_t)
+
+struct file_operations proc_vmcore_operations = {
+	.read		= read_vmcore,
+	.open		= open_vmcore,
+};
+
+struct proc_dir_entry *proc_vmcore;
+
+struct memelfnote
+{
+	const char *name;
+	int type;
+	unsigned int datasz;
+	void *data;
+};
+
+static size_t get_vmcore_size(int *nphdr, size_t *elf_buflen)
+{
+	size_t size;
+
+	/* We need 1 PT_LOAD segment headers
+	 * In addition, we need one PT_NOTE header
+	 */
+	*nphdr = 2;
+	size = (size_t)(saved_max_pfn << PAGE_SHIFT);
+
+	*elf_buflen =	sizeof(struct elfhdr) +
+			(*nphdr + 2)*sizeof(struct elf_phdr) +
+			3 * sizeof(struct memelfnote) +
+			sizeof(struct elf_prstatus) +
+			sizeof(struct elf_prpsinfo) +
+			sizeof(struct task_struct);
+	*elf_buflen = PAGE_ALIGN(*elf_buflen);
+	return size + *elf_buflen;
+}
+
+/*
+ * Reads a page from the oldmem device from given offset.
+ */
+static ssize_t read_from_oldmem(char *buf, size_t count,
+			     loff_t *ppos, int userbuf)
+{
+	unsigned long pfn;
+	size_t read = 0;
+
+	pfn = (unsigned long)(*ppos / PAGE_SIZE);
+
+	if (pfn > saved_max_pfn) {
+		read = -EINVAL;
+		goto done;
+	}
+
+	count = (count > PAGE_SIZE) ? PAGE_SIZE : count;
+
+	if (copy_oldmem_page(pfn, buf, count, userbuf)) {
+		read = -EFAULT;
+		goto done;
+	}
+
+	*ppos += count;
+done:
+	return read;
+}
+
+/*
+ * store an ELF crash dump header in the supplied buffer
+ * nphdr is the number of elf_phdr to insert
+ */
+static void elf_vmcore_store_hdr(char *bufp, int nphdr, int dataoff)
+{
+	struct elf_prstatus prstatus;	/* NT_PRSTATUS */
+	struct memelfnote notes[1];
+	char reg_buf[REG_SIZE];
+	loff_t reg_ppos;
+	char *buf = bufp;
+
+	vmcore_mem.addr = (unsigned long)__va(0);
+	vmcore_mem.size = saved_max_pfn << PAGE_SHIFT;
+	vmcore_mem.next = NULL;
+
+	/* Re-use the kcore code */
+	elf_kcore_store_hdr(bufp, nphdr, dataoff, &vmcore_mem);
+	buf += sizeof(struct elfhdr) + 2*sizeof(struct elf_phdr);
+
+	/* set up the process status */
+	notes[0].name = "CORE";
+	notes[0].type = NT_PRSTATUS;
+	notes[0].datasz = sizeof(struct elf_prstatus);
+	notes[0].data = &prstatus;
+
+	memset(&prstatus, 0, sizeof(struct elf_prstatus));
+
+	/* 1 - Get the registers from the reserved memory area */
+	reg_ppos = BACKUP_END + CRASH_RELOCATE_SIZE;
+	read_from_oldmem(reg_buf, REG_SIZE, &reg_ppos, 0);
+	elf_core_copy_regs(&prstatus.pr_reg, (struct pt_regs *)reg_buf);
+	buf = storenote(&notes[0], buf);
+}
+
+/*
+ * read from the ELF header and then the crash dump
+ */
+static ssize_t read_vmcore(
+struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
+{
+	ssize_t acc = 0;
+	size_t size, tsz;
+	size_t elf_buflen;
+	int nphdr;
+	unsigned long start;
+
+	tsz =  get_vmcore_size(&nphdr, &elf_buflen);
+	proc_vmcore->size = size = tsz + elf_buflen;
+	if (buflen == 0 || *fpos >= size) {
+		goto done;
+	}
+
+	/* trim buflen to not go beyond EOF */
+	if (buflen > size - *fpos)
+		buflen = size - *fpos;
+
+	/* construct an ELF core header if we'll need some of it */
+	if (*fpos < elf_buflen) {
+		char * elf_buf;
+
+		tsz = elf_buflen - *fpos;
+		if (buflen < tsz)
+			tsz = buflen;
+		elf_buf = kmalloc(elf_buflen, GFP_ATOMIC);
+		if (!elf_buf) {
+			acc = -ENOMEM;
+			goto done;
+		}
+		memset(elf_buf, 0, elf_buflen);
+		elf_vmcore_store_hdr(elf_buf, nphdr, elf_buflen);
+		if (copy_to_user(buffer, elf_buf + *fpos, tsz)) {
+			kfree(elf_buf);
+			acc = -EFAULT;
+			goto done;
+		}
+		kfree(elf_buf);
+		buflen -= tsz;
+		*fpos += tsz;
+		buffer += tsz;
+		acc += tsz;
+
+		/* leave now if filled buffer already */
+		if (buflen == 0) {
+			goto done;
+		}
+	}
+
+	start = *fpos - elf_buflen;
+	if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen)
+		tsz = buflen;
+
+	while (buflen) {
+		unsigned long p;
+		loff_t pdup;
+
+		if ((start < 0) || (start >= size))
+			if (clear_user(buffer, tsz)) {
+				acc = -EFAULT;
+				goto done;
+			}
+
+		/* tsz contains actual len of dump to be read.
+		 * buflen is the total len that was requested.
+		 * This may contain part of ELF header. start
+		 * is the fpos for the oldmem region
+		 * If the file position corresponds to the second
+		 * kernel's memory, we just return zeroes
+		 */
+		p = start;
+		if ((p >= BACKUP_START) && (p < BACKUP_END)) {
+			if (clear_user(buffer, tsz)) {
+				acc = -EFAULT;
+				goto done;
+			}
+
+			goto read_done;
+		} else if (p < CRASH_RELOCATE_SIZE)
+			p += BACKUP_END;
+
+		pdup = p;
+		if (read_from_oldmem(buffer, tsz, &pdup, 1)) {
+			acc = -EINVAL;
+			goto done;
+		}
+
+read_done:
+		buflen -= tsz;
+		*fpos += tsz;
+		buffer += tsz;
+		acc += tsz;
+		start += tsz;
+		tsz = (buflen > PAGE_SIZE ? PAGE_SIZE : buflen);
+	}
+
+done:
+	return acc;
+}
diff -puN include/linux/crash_dump.h~crashdump-elf-format-dump-file-access include/linux/crash_dump.h
--- 25/include/linux/crash_dump.h~crashdump-elf-format-dump-file-access	2004-09-21 23:21:33.890425488 -0700
+++ 25-akpm/include/linux/crash_dump.h	2004-09-21 23:21:33.899424120 -0700
@@ -1,13 +1,22 @@
 #include <linux/kexec.h>
 #include <linux/smp_lock.h>
 #include <linux/device.h>
+#include <linux/proc_fs.h>
 #ifdef CONFIG_CRASH_DUMP
 #include <asm/crash_dump.h>
 #endif
 
+extern unsigned long saved_max_pfn;
+extern struct memelfnote memelfnote;
+extern int notesize(struct memelfnote *);
+extern char *storenote(struct memelfnote *, char *);
+extern void elf_kcore_store_hdr(char *, int, int, struct kcore_list *);
+
 #ifdef CONFIG_CRASH_DUMP
+extern ssize_t copy_oldmem_page(unsigned long, char *, size_t, int);
 extern void __crash_machine_kexec(void);
 extern void crash_enable_by_proc(void);
+extern void crash_create_proc_entry(void);
 extern int crash_dump_on;
 static inline void crash_machine_kexec(void)
 {
@@ -15,5 +24,6 @@ static inline void crash_machine_kexec(v
 }
 #else
 #define crash_enable_by_proc() do { } while(0)
+#define crash_create_proc_entry() do { } while(0)
 #define crash_machine_kexec()	do { } while(0)
 #endif
diff -puN kernel/crash.c~crashdump-elf-format-dump-file-access kernel/crash.c
--- 25/kernel/crash.c~crashdump-elf-format-dump-file-access	2004-09-21 23:21:33.891425336 -0700
+++ 25-akpm/kernel/crash.c	2004-09-21 23:22:05.246658616 -0700
@@ -9,6 +9,7 @@
 #include <linux/kexec.h>
 #include <linux/errno.h>
 #include <linux/proc_fs.h>
+#include <linux/bootmem.h>
 #include <linux/highmem.h>
 #include <linux/crash_dump.h>
 
@@ -32,6 +33,9 @@ static struct file_operations proc_crash
 	.write          = write_crash_dump_on,
 };
 
+extern struct file_operations proc_vmcore_operations;
+extern struct proc_dir_entry *proc_vmcore;
+
 void crash_enable_by_proc(void)
 {
 	struct proc_dir_entry *entry;
@@ -41,6 +45,18 @@ void crash_enable_by_proc(void)
 		entry->proc_fops = &proc_crash_dump_on_operations;
 }
 
+void crash_create_proc_entry(void)
+{
+	if (dump_enabled) {
+		proc_vmcore = create_proc_entry("vmcore", S_IRUSR, NULL);
+		if (proc_vmcore) {
+			proc_vmcore->proc_fops = &proc_vmcore_operations;
+			proc_vmcore->size =
+			(size_t)(saved_max_pfn << PAGE_SHIFT);
+		}
+	}
+}
+
 void __crash_machine_kexec(void)
 {
 	struct kimage *image;
_