From: Hariprasad Nellitheertha 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 Signed-off-by: Andrew Morton --- 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-11-10 19:08:15.413170824 -0800 +++ 25-akpm/fs/proc/kcore.c 2004-11-10 19:08:15.421169608 -0800 @@ -113,7 +113,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; @@ -128,7 +128,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; @@ -155,7 +155,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 */ @@ -207,7 +207,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); @@ -304,7 +304,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-11-10 19:08:15.414170672 -0800 +++ 25-akpm/fs/proc/Makefile 2004-11-10 19:08:15.422169456 -0800 @@ -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-11-10 19:08:15.415170520 -0800 +++ 25-akpm/fs/proc/proc_misc.c 2004-11-10 19:08:15.423169304 -0800 @@ -596,6 +596,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-11-10 19:08:15.424169152 -0800 @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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, ®_ppos, 0); + elf_core_copy_regs(&prstatus.pr_reg, (struct pt_regs *)reg_buf); + buf = storenote(¬es[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-11-10 19:08:15.417170216 -0800 +++ 25-akpm/include/linux/crash_dump.h 2004-11-10 19:08:15.424169152 -0800 @@ -1,13 +1,22 @@ #include #include #include +#include #ifdef CONFIG_CRASH_DUMP #include #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-11-10 19:08:15.418170064 -0800 +++ 25-akpm/kernel/crash.c 2004-11-10 19:08:15.425169000 -0800 @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -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; _