2 * fs/proc/vmcore.c Interface for accessing the crash
3 * dump from the system's previous life.
4 * Heavily borrowed from fs/proc/kcore.c
5 * Created by: Hariprasad Nellitheertha (hari@in.ibm.com)
6 * Copyright (C) IBM Corporation, 2004. All rights reserved
9 #include <linux/config.h>
11 #include <linux/proc_fs.h>
12 #include <linux/user.h>
13 #include <linux/a.out.h>
14 #include <linux/elf.h>
15 #include <linux/elfcore.h>
16 #include <linux/vmalloc.h>
17 #include <linux/proc_fs.h>
18 #include <linux/highmem.h>
19 #include <linux/bootmem.h>
20 #include <linux/init.h>
21 #include <linux/crash_dump.h>
22 #include <asm/uaccess.h>
25 /* This is to re-use the kcore header creation code */
26 static struct kcore_list vmcore_mem;
28 static int open_vmcore(struct inode * inode, struct file * filp)
33 static ssize_t read_vmcore(struct file *,char __user *,size_t, loff_t *);
35 #define BACKUP_START CRASH_BACKUP_BASE
36 #define BACKUP_END CRASH_BACKUP_BASE + CRASH_BACKUP_SIZE
37 #define REG_SIZE sizeof(elf_gregset_t)
39 struct file_operations proc_vmcore_operations = {
44 struct proc_dir_entry *proc_vmcore;
54 static size_t get_vmcore_size(int *nphdr, size_t *elf_buflen)
58 /* We need 1 PT_LOAD segment headers
59 * In addition, we need one PT_NOTE header
62 size = (size_t)(saved_max_pfn << PAGE_SHIFT);
64 *elf_buflen = sizeof(struct elfhdr) +
65 (*nphdr + 2)*sizeof(struct elf_phdr) +
66 3 * sizeof(struct memelfnote) +
67 sizeof(struct elf_prstatus) +
68 sizeof(struct elf_prpsinfo) +
69 sizeof(struct task_struct);
70 *elf_buflen = PAGE_ALIGN(*elf_buflen);
71 return size + *elf_buflen;
75 * Reads a page from the oldmem device from given offset.
77 static ssize_t read_from_oldmem(char *buf, size_t count,
78 loff_t *ppos, int userbuf)
83 pfn = (unsigned long)(*ppos / PAGE_SIZE);
85 if (pfn > saved_max_pfn) {
90 count = (count > PAGE_SIZE) ? PAGE_SIZE : count;
92 if (copy_oldmem_page(pfn, buf, count, userbuf)) {
103 * store an ELF crash dump header in the supplied buffer
104 * nphdr is the number of elf_phdr to insert
106 static void elf_vmcore_store_hdr(char *bufp, int nphdr, int dataoff)
108 struct elf_prstatus prstatus; /* NT_PRSTATUS */
109 struct memelfnote notes[1];
110 char reg_buf[REG_SIZE];
114 vmcore_mem.addr = (unsigned long)__va(0);
115 vmcore_mem.size = saved_max_pfn << PAGE_SHIFT;
116 vmcore_mem.next = NULL;
118 /* Re-use the kcore code */
119 elf_kcore_store_hdr(bufp, nphdr, dataoff, &vmcore_mem);
120 buf += sizeof(struct elfhdr) + 2*sizeof(struct elf_phdr);
122 /* set up the process status */
123 notes[0].name = "CORE";
124 notes[0].type = NT_PRSTATUS;
125 notes[0].datasz = sizeof(struct elf_prstatus);
126 notes[0].data = &prstatus;
128 memset(&prstatus, 0, sizeof(struct elf_prstatus));
130 /* 1 - Get the registers from the reserved memory area */
131 reg_ppos = BACKUP_END + CRASH_RELOCATE_SIZE;
132 read_from_oldmem(reg_buf, REG_SIZE, ®_ppos, 0);
133 elf_core_copy_regs(&prstatus.pr_reg, (struct pt_regs *)reg_buf);
134 buf = storenote(¬es[0], buf);
138 * read from the ELF header and then the crash dump
140 static ssize_t read_vmcore(
141 struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
149 tsz = get_vmcore_size(&nphdr, &elf_buflen);
150 proc_vmcore->size = size = tsz + elf_buflen;
151 if (buflen == 0 || *fpos >= size) {
155 /* trim buflen to not go beyond EOF */
156 if (buflen > size - *fpos)
157 buflen = size - *fpos;
159 /* construct an ELF core header if we'll need some of it */
160 if (*fpos < elf_buflen) {
163 tsz = elf_buflen - *fpos;
166 elf_buf = kmalloc(elf_buflen, GFP_ATOMIC);
171 memset(elf_buf, 0, elf_buflen);
172 elf_vmcore_store_hdr(elf_buf, nphdr, elf_buflen);
173 if (copy_to_user(buffer, elf_buf + *fpos, tsz)) {
184 /* leave now if filled buffer already */
190 start = *fpos - elf_buflen;
191 if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen)
198 if ((start < 0) || (start >= size))
199 if (clear_user(buffer, tsz)) {
204 /* tsz contains actual len of dump to be read.
205 * buflen is the total len that was requested.
206 * This may contain part of ELF header. start
207 * is the fpos for the oldmem region
208 * If the file position corresponds to the second
209 * kernel's memory, we just return zeroes
212 if ((p >= BACKUP_START) && (p < BACKUP_END)) {
213 if (clear_user(buffer, tsz)) {
219 } else if (p < CRASH_RELOCATE_SIZE)
223 if (read_from_oldmem(buffer, tsz, &pdup, 1)) {
234 tsz = (buflen > PAGE_SIZE ? PAGE_SIZE : buflen);