This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / fs / proc / vmcore.c
1 /*
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
7  */
8
9 #include <linux/config.h>
10 #include <linux/mm.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>
23 #include <asm/io.h>
24
25 /* This is to re-use the kcore header creation code */
26 static struct kcore_list vmcore_mem;
27
28 static int open_vmcore(struct inode * inode, struct file * filp)
29 {
30         return 0;
31 }
32
33 static ssize_t read_vmcore(struct file *,char __user *,size_t, loff_t *);
34
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)
38
39 struct file_operations proc_vmcore_operations = {
40         .read           = read_vmcore,
41         .open           = open_vmcore,
42 };
43
44 struct proc_dir_entry *proc_vmcore;
45
46 struct memelfnote
47 {
48         const char *name;
49         int type;
50         unsigned int datasz;
51         void *data;
52 };
53
54 static size_t get_vmcore_size(int *nphdr, size_t *elf_buflen)
55 {
56         size_t size;
57
58         /* We need 1 PT_LOAD segment headers
59          * In addition, we need one PT_NOTE header
60          */
61         *nphdr = 2;
62         size = (size_t)(saved_max_pfn << PAGE_SHIFT);
63
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;
72 }
73
74 /*
75  * Reads a page from the oldmem device from given offset.
76  */
77 static ssize_t read_from_oldmem(char *buf, size_t count,
78                              loff_t *ppos, int userbuf)
79 {
80         unsigned long pfn;
81         size_t read = 0;
82
83         pfn = (unsigned long)(*ppos / PAGE_SIZE);
84
85         if (pfn > saved_max_pfn) {
86                 read = -EINVAL;
87                 goto done;
88         }
89
90         count = (count > PAGE_SIZE) ? PAGE_SIZE : count;
91
92         if (copy_oldmem_page(pfn, buf, count, userbuf)) {
93                 read = -EFAULT;
94                 goto done;
95         }
96
97         *ppos += count;
98 done:
99         return read;
100 }
101
102 /*
103  * store an ELF crash dump header in the supplied buffer
104  * nphdr is the number of elf_phdr to insert
105  */
106 static void elf_vmcore_store_hdr(char *bufp, int nphdr, int dataoff)
107 {
108         struct elf_prstatus prstatus;   /* NT_PRSTATUS */
109         struct memelfnote notes[1];
110         char reg_buf[REG_SIZE];
111         loff_t reg_ppos;
112         char *buf = bufp;
113
114         vmcore_mem.addr = (unsigned long)__va(0);
115         vmcore_mem.size = saved_max_pfn << PAGE_SHIFT;
116         vmcore_mem.next = NULL;
117
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);
121
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;
127
128         memset(&prstatus, 0, sizeof(struct elf_prstatus));
129
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, &reg_ppos, 0);
133         elf_core_copy_regs(&prstatus.pr_reg, (struct pt_regs *)reg_buf);
134         buf = storenote(&notes[0], buf);
135 }
136
137 /*
138  * read from the ELF header and then the crash dump
139  */
140 static ssize_t read_vmcore(
141 struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
142 {
143         ssize_t acc = 0;
144         size_t size, tsz;
145         size_t elf_buflen;
146         int nphdr;
147         unsigned long start;
148
149         tsz =  get_vmcore_size(&nphdr, &elf_buflen);
150         proc_vmcore->size = size = tsz + elf_buflen;
151         if (buflen == 0 || *fpos >= size) {
152                 goto done;
153         }
154
155         /* trim buflen to not go beyond EOF */
156         if (buflen > size - *fpos)
157                 buflen = size - *fpos;
158
159         /* construct an ELF core header if we'll need some of it */
160         if (*fpos < elf_buflen) {
161                 char * elf_buf;
162
163                 tsz = elf_buflen - *fpos;
164                 if (buflen < tsz)
165                         tsz = buflen;
166                 elf_buf = kmalloc(elf_buflen, GFP_ATOMIC);
167                 if (!elf_buf) {
168                         acc = -ENOMEM;
169                         goto done;
170                 }
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)) {
174                         kfree(elf_buf);
175                         acc = -EFAULT;
176                         goto done;
177                 }
178                 kfree(elf_buf);
179                 buflen -= tsz;
180                 *fpos += tsz;
181                 buffer += tsz;
182                 acc += tsz;
183
184                 /* leave now if filled buffer already */
185                 if (buflen == 0) {
186                         goto done;
187                 }
188         }
189
190         start = *fpos - elf_buflen;
191         if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen)
192                 tsz = buflen;
193
194         while (buflen) {
195                 unsigned long p;
196                 loff_t pdup;
197
198                 if ((start < 0) || (start >= size))
199                         if (clear_user(buffer, tsz)) {
200                                 acc = -EFAULT;
201                                 goto done;
202                         }
203
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
210                  */
211                 p = start;
212                 if ((p >= BACKUP_START) && (p < BACKUP_END)) {
213                         if (clear_user(buffer, tsz)) {
214                                 acc = -EFAULT;
215                                 goto done;
216                         }
217
218                         goto read_done;
219                 } else if (p < CRASH_RELOCATE_SIZE)
220                         p += BACKUP_END;
221
222                 pdup = p;
223                 if (read_from_oldmem(buffer, tsz, &pdup, 1)) {
224                         acc = -EINVAL;
225                         goto done;
226                 }
227
228 read_done:
229                 buflen -= tsz;
230                 *fpos += tsz;
231                 buffer += tsz;
232                 acc += tsz;
233                 start += tsz;
234                 tsz = (buflen > PAGE_SIZE ? PAGE_SIZE : buflen);
235         }
236
237 done:
238         return acc;
239 }