]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/powerpc/powerpc/dump_machdep.c
Update elftoolchain to upstream rev 3130
[FreeBSD/FreeBSD.git] / sys / powerpc / powerpc / dump_machdep.c
1 /*-
2  * Copyright (c) 2002 Marcel Moolenaar
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include "opt_watchdog.h"
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/conf.h>
35 #include <sys/cons.h>
36 #include <sys/kernel.h>
37 #include <sys/kerneldump.h>
38 #include <sys/sysctl.h>
39 #ifdef SW_WATCHDOG
40 #include <sys/watchdog.h>
41 #endif
42 #include <vm/vm.h>
43 #include <vm/pmap.h>
44 #include <machine/elf.h>
45 #include <machine/md_var.h>
46
47 CTASSERT(sizeof(struct kerneldumpheader) == 512);
48
49 /*
50  * Don't touch the first SIZEOF_METADATA bytes on the dump device. This
51  * is to protect us from metadata and to protect metadata from us.
52  */
53 #define SIZEOF_METADATA         (64*1024)
54
55 #define MD_ALIGN(x)     (((off_t)(x) + PAGE_MASK) & ~PAGE_MASK)
56 #define DEV_ALIGN(x)    (((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1))
57
58 typedef int callback_t(struct pmap_md *, int, void *);
59
60 static struct kerneldumpheader kdh;
61 static off_t dumplo, fileofs;
62
63 /* Handle buffered writes. */
64 static char buffer[DEV_BSIZE];
65 static size_t fragsz;
66
67 int dumpsys_minidump = 1;
68 SYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RD, &dumpsys_minidump, 0,
69     "Kernel makes compressed crash dumps");
70
71 static int
72 buf_write(struct dumperinfo *di, char *ptr, size_t sz)
73 {
74         size_t len;
75         int error;
76
77         while (sz) {
78                 len = DEV_BSIZE - fragsz;
79                 if (len > sz)
80                         len = sz;
81                 bcopy(ptr, buffer + fragsz, len);
82                 fragsz += len;
83                 ptr += len;
84                 sz -= len;
85                 if (fragsz == DEV_BSIZE) {
86                         error = di->dumper(di->priv, buffer, 0, dumplo,
87                             DEV_BSIZE);
88                         if (error)
89                                 return error;
90                         dumplo += DEV_BSIZE;
91                         fragsz = 0;
92                 }
93         }
94
95         return (0);
96 }
97
98 static int
99 buf_flush(struct dumperinfo *di)
100 {
101         int error;
102
103         if (fragsz == 0)
104                 return (0);
105
106         error = di->dumper(di->priv, buffer, 0, dumplo, DEV_BSIZE);
107         dumplo += DEV_BSIZE;
108         fragsz = 0;
109         return (error);
110 }
111
112 static int
113 cb_dumpdata(struct pmap_md *md, int seqnr, void *arg)
114 {
115         struct dumperinfo *di = (struct dumperinfo*)arg;
116         vm_offset_t va;
117         size_t counter, ofs, resid, sz, maxsz;
118         int c, error, twiddle;
119
120         error = 0;
121         counter = 0;    /* Update twiddle every 16MB */
122         twiddle = 0;
123
124         ofs = 0;        /* Logical offset within the chunk */
125         resid = md->md_size;
126         maxsz = min(DFLTPHYS, di->maxiosize);
127
128         printf("  chunk %d: %lu bytes ", seqnr, (u_long)resid);
129
130         while (resid) {
131                 sz = min(resid, maxsz);
132                 va = pmap_dumpsys_map(md, ofs, &sz);
133                 counter += sz;
134                 if (counter >> 24) {
135                         printf("%c\b", "|/-\\"[twiddle++ & 3]);
136                         counter &= (1<<24) - 1;
137                 }
138 #ifdef SW_WATCHDOG
139                 wdog_kern_pat(WD_LASTVAL);
140 #endif
141                 error = di->dumper(di->priv, (void*)va, 0, dumplo, sz);
142                 pmap_dumpsys_unmap(md, ofs, va);
143                 if (error)
144                         break;
145                 dumplo += sz;
146                 resid -= sz;
147                 ofs += sz;
148
149                 /* Check for user abort. */
150                 c = cncheckc();
151                 if (c == 0x03)
152                         return (ECANCELED);
153                 if (c != -1)
154                         printf("(CTRL-C to abort)  ");
155         }
156         printf("... %s\n", (error) ? "fail" : "ok");
157         return (error);
158 }
159
160 static int
161 cb_dumphdr(struct pmap_md *md, int seqnr, void *arg)
162 {
163         struct dumperinfo *di = (struct dumperinfo*)arg;
164         Elf_Phdr phdr;
165         int error;
166
167         bzero(&phdr, sizeof(phdr));
168         phdr.p_type = PT_LOAD;
169         phdr.p_flags = PF_R;                    /* XXX */
170         phdr.p_offset = fileofs;
171         phdr.p_vaddr = md->md_vaddr;
172         phdr.p_paddr = md->md_paddr;
173         phdr.p_filesz = md->md_size;
174         phdr.p_memsz = md->md_size;
175         phdr.p_align = PAGE_SIZE;
176
177         error = buf_write(di, (char*)&phdr, sizeof(phdr));
178         fileofs += phdr.p_filesz;
179         return (error);
180 }
181
182 static int
183 cb_size(struct pmap_md *md, int seqnr, void *arg)
184 {
185         uint32_t *sz = (uint32_t*)arg;
186
187         *sz += md->md_size;
188         return (0);
189 }
190
191 static int
192 foreach_chunk(callback_t cb, void *arg)
193 {
194         struct pmap_md *md;
195         int error, seqnr;
196
197         seqnr = 0;
198         md = pmap_scan_md(NULL);
199         while (md != NULL) {
200                 error = (*cb)(md, seqnr++, arg);
201                 if (error)
202                         return (-error);
203                 md = pmap_scan_md(md);
204         }
205         return (seqnr);
206 }
207
208 int
209 dumpsys(struct dumperinfo *di)
210 {
211         Elf_Ehdr ehdr;
212         uint32_t dumpsize;
213         off_t hdrgap;
214         size_t hdrsz;
215         int error;
216
217         bzero(&ehdr, sizeof(ehdr));
218         ehdr.e_ident[EI_MAG0] = ELFMAG0;
219         ehdr.e_ident[EI_MAG1] = ELFMAG1;
220         ehdr.e_ident[EI_MAG2] = ELFMAG2;
221         ehdr.e_ident[EI_MAG3] = ELFMAG3;
222         ehdr.e_ident[EI_CLASS] = ELF_TARG_CLASS;
223 #if BYTE_ORDER == LITTLE_ENDIAN
224         ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
225 #else
226         ehdr.e_ident[EI_DATA] = ELFDATA2MSB;
227 #endif
228         ehdr.e_ident[EI_VERSION] = EV_CURRENT;
229         ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE;   /* XXX big picture? */
230         ehdr.e_type = ET_CORE;
231         ehdr.e_machine = ELF_ARCH;      /* Defined in powerpc/include/elf.h */
232         ehdr.e_phoff = sizeof(ehdr);
233         ehdr.e_ehsize = sizeof(ehdr);
234         ehdr.e_phentsize = sizeof(Elf_Phdr);
235         ehdr.e_shentsize = sizeof(Elf_Shdr);
236
237         /* Calculate dump size. */
238         dumpsize = 0L;
239         ehdr.e_phnum = foreach_chunk(cb_size, &dumpsize);
240         hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize;
241         fileofs = MD_ALIGN(hdrsz);
242         dumpsize += fileofs;
243         hdrgap = fileofs - DEV_ALIGN(hdrsz);
244
245         /* For block devices, determine the dump offset on the device. */
246         if (di->mediasize > 0) {
247                 if (di->mediasize <
248                     SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
249                         error = ENOSPC;
250                         goto fail;
251                 }
252                 dumplo = di->mediaoffset + di->mediasize - dumpsize;
253                 dumplo -= sizeof(kdh) * 2;
254         } else
255                 dumplo = 0;
256
257         mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_POWERPC_VERSION, dumpsize,
258             di->blocksize);
259
260         printf("Dumping %u MB (%d chunks)\n", dumpsize >> 20,
261             ehdr.e_phnum);
262
263         /* Dump leader */
264         error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
265         if (error)
266                 goto fail;
267         dumplo += sizeof(kdh);
268
269         /* Dump ELF header */
270         error = buf_write(di, (char*)&ehdr, sizeof(ehdr));
271         if (error)
272                 goto fail;
273
274         /* Dump program headers */
275         error = foreach_chunk(cb_dumphdr, di);
276         if (error < 0)
277                 goto fail;
278         buf_flush(di);
279
280         /*
281          * All headers are written using blocked I/O, so we know the
282          * current offset is (still) block aligned. Skip the alignement
283          * in the file to have the segment contents aligned at page
284          * boundary. We cannot use MD_ALIGN on dumplo, because we don't
285          * care and may very well be unaligned within the dump device.
286          */
287         dumplo += hdrgap;
288
289         /* Dump memory chunks (updates dumplo) */
290         error = foreach_chunk(cb_dumpdata, di);
291         if (error < 0)
292                 goto fail;
293
294         /* Dump trailer */
295         error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
296         if (error)
297                 goto fail;
298
299         /* Signal completion, signoff and exit stage left. */
300         dump_write(di, NULL, 0, 0, 0);
301         printf("\nDump complete\n");
302         return (0);
303
304  fail:
305         if (error < 0)
306                 error = -error;
307
308         if (error == ECANCELED)
309                 printf("\nDump aborted\n");
310         else if (error == ENOSPC)
311                 printf("\nDump failed. Partition too small.\n");
312         else
313                 printf("\n** DUMP FAILED (ERROR %d) **\n", error);
314         return (error);
315 }