]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - sys/ia64/ia64/dump_machdep.c
MFC r306478:
[FreeBSD/stable/8.git] / sys / ia64 / ia64 / 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 #ifdef SW_WATCHDOG
39 #include <sys/watchdog.h>
40 #endif
41 #include <vm/vm.h>
42 #include <vm/pmap.h>
43 #include <machine/efi.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) + EFI_PAGE_MASK) & ~EFI_PAGE_MASK)
56 #define DEV_ALIGN(x)    (((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1))
57
58 typedef int callback_t(struct efi_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 static int
68 buf_write(struct dumperinfo *di, char *ptr, size_t sz)
69 {
70         size_t len;
71         int error;
72
73         while (sz) {
74                 len = DEV_BSIZE - fragsz;
75                 if (len > sz)
76                         len = sz;
77                 bcopy(ptr, buffer + fragsz, len);
78                 fragsz += len;
79                 ptr += len;
80                 sz -= len;
81                 if (fragsz == DEV_BSIZE) {
82                         error = dump_write(di, buffer, 0, dumplo,
83                             DEV_BSIZE);
84                         if (error)
85                                 return error;
86                         dumplo += DEV_BSIZE;
87                         fragsz = 0;
88                 }
89         }
90
91         return (0);
92 }
93
94 static int
95 buf_flush(struct dumperinfo *di)
96 {
97         int error;
98
99         if (fragsz == 0)
100                 return (0);
101
102         error = dump_write(di, buffer, 0, dumplo, DEV_BSIZE);
103         dumplo += DEV_BSIZE;
104         fragsz = 0;
105         return (error);
106 }
107
108 static int
109 cb_dumpdata(struct efi_md *mdp, int seqnr, void *arg)
110 {
111         struct dumperinfo *di = (struct dumperinfo*)arg;
112         vm_offset_t pa;
113         uint64_t pgs;
114         size_t counter, sz;
115         int c, error, twiddle;
116
117         error = 0;      /* catch case in which mdp->md_pages is 0 */
118         counter = 0;    /* Update twiddle every 16MB */
119         twiddle = 0;
120         pgs = mdp->md_pages;
121         pa = IA64_PHYS_TO_RR7(mdp->md_phys);
122
123         printf("  chunk %d: %ld pages ", seqnr, (long)pgs);
124
125         while (pgs) {
126                 sz = (pgs > (DFLTPHYS >> EFI_PAGE_SHIFT))
127                     ? DFLTPHYS : pgs << EFI_PAGE_SHIFT;
128                 counter += sz;
129                 if (counter >> 24) {
130                         printf("%c\b", "|/-\\"[twiddle++ & 3]);
131                         counter &= (1<<24) - 1;
132                 }
133 #ifdef SW_WATCHDOG
134                 wdog_kern_pat(WD_LASTVAL);
135 #endif
136                 error = dump_write(di, (void*)pa, 0, dumplo, sz);
137                 if (error)
138                         break;
139                 dumplo += sz;
140                 pgs -= sz >> EFI_PAGE_SHIFT;
141                 pa += sz;
142
143                 /* Check for user abort. */
144                 c = cncheckc();
145                 if (c == 0x03)
146                         return (ECANCELED);
147                 if (c != -1)
148                         printf("(CTRL-C to abort)  ");
149         }
150         printf("... %s\n", (error) ? "fail" : "ok");
151         return (error);
152 }
153
154 static int
155 cb_dumphdr(struct efi_md *mdp, int seqnr, void *arg)
156 {
157         struct dumperinfo *di = (struct dumperinfo*)arg;
158         Elf64_Phdr phdr;
159         int error;
160
161         bzero(&phdr, sizeof(phdr));
162         phdr.p_type = PT_LOAD;
163         phdr.p_flags = PF_R;                    /* XXX */
164         phdr.p_offset = fileofs;
165         phdr.p_vaddr = (uintptr_t)mdp->md_virt; /* XXX probably bogus. */
166         phdr.p_paddr = mdp->md_phys;
167         phdr.p_filesz = mdp->md_pages << EFI_PAGE_SHIFT;
168         phdr.p_memsz = mdp->md_pages << EFI_PAGE_SHIFT;
169         phdr.p_align = EFI_PAGE_SIZE;
170
171         error = buf_write(di, (char*)&phdr, sizeof(phdr));
172         fileofs += phdr.p_filesz;
173         return (error);
174 }
175
176 static int
177 cb_size(struct efi_md *mdp, int seqnr, void *arg)
178 {
179         uint64_t *sz = (uint64_t*)arg;
180
181         *sz += (uint64_t)mdp->md_pages << EFI_PAGE_SHIFT;
182         return (0);
183 }
184
185 static int
186 foreach_chunk(callback_t cb, void *arg)
187 {
188         struct efi_md *mdp;
189         int error, seqnr;
190
191         seqnr = 0;
192         mdp = efi_md_first();
193         while (mdp != NULL) {
194                 if (mdp->md_type == EFI_MD_TYPE_FREE) {
195                         error = (*cb)(mdp, seqnr++, arg);
196                         if (error)
197                                 return (-error);
198                 }
199                 mdp = efi_md_next(mdp);
200         }
201         return (seqnr);
202 }
203
204 void
205 dumpsys(struct dumperinfo *di)
206 {
207         Elf64_Ehdr ehdr;
208         uint64_t dumpsize;
209         off_t hdrgap;
210         size_t hdrsz;
211         int error;
212
213         bzero(&ehdr, sizeof(ehdr));
214         ehdr.e_ident[EI_MAG0] = ELFMAG0;
215         ehdr.e_ident[EI_MAG1] = ELFMAG1;
216         ehdr.e_ident[EI_MAG2] = ELFMAG2;
217         ehdr.e_ident[EI_MAG3] = ELFMAG3;
218         ehdr.e_ident[EI_CLASS] = ELFCLASS64;
219 #if BYTE_ORDER == LITTLE_ENDIAN
220         ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
221 #else
222         ehdr.e_ident[EI_DATA] = ELFDATA2MSB;
223 #endif
224         ehdr.e_ident[EI_VERSION] = EV_CURRENT;
225         ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE;   /* XXX big picture? */
226         ehdr.e_type = ET_CORE;
227         ehdr.e_machine = EM_IA_64;
228         ehdr.e_phoff = sizeof(ehdr);
229         ehdr.e_flags = EF_IA_64_ABSOLUTE;               /* XXX misuse? */
230         ehdr.e_ehsize = sizeof(ehdr);
231         ehdr.e_phentsize = sizeof(Elf64_Phdr);
232         ehdr.e_shentsize = sizeof(Elf64_Shdr);
233
234         /* Calculate dump size. */
235         dumpsize = 0L;
236         ehdr.e_phnum = foreach_chunk(cb_size, &dumpsize);
237         hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize;
238         fileofs = MD_ALIGN(hdrsz);
239         dumpsize += fileofs;
240         hdrgap = fileofs - DEV_ALIGN(hdrsz);
241
242         /* Determine dump offset on device. */
243         if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
244                 error = ENOSPC;
245                 goto fail;
246         }
247         dumplo = di->mediaoffset + di->mediasize - dumpsize;
248         dumplo -= sizeof(kdh) * 2;
249
250         mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_IA64_VERSION, dumpsize, di->blocksize);
251
252         printf("Dumping %llu MB (%d chunks)\n", (long long)dumpsize >> 20,
253             ehdr.e_phnum);
254
255         /* Dump leader */
256         error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
257         if (error)
258                 goto fail;
259         dumplo += sizeof(kdh);
260
261         /* Dump ELF header */
262         error = buf_write(di, (char*)&ehdr, sizeof(ehdr));
263         if (error)
264                 goto fail;
265
266         /* Dump program headers */
267         error = foreach_chunk(cb_dumphdr, di);
268         if (error < 0)
269                 goto fail;
270         buf_flush(di);
271
272         /*
273          * All headers are written using blocked I/O, so we know the
274          * current offset is (still) block aligned. Skip the alignement
275          * in the file to have the segment contents aligned at page
276          * boundary. We cannot use MD_ALIGN on dumplo, because we don't
277          * care and may very well be unaligned within the dump device.
278          */
279         dumplo += hdrgap;
280
281         /* Dump memory chunks (updates dumplo) */
282         error = foreach_chunk(cb_dumpdata, di);
283         if (error < 0)
284                 goto fail;
285
286         /* Dump trailer */
287         error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
288         if (error)
289                 goto fail;
290
291         /* Signal completion, signoff and exit stage left. */
292         dump_write(di, NULL, 0, 0, 0);
293         printf("\nDump complete\n");
294         return;
295
296  fail:
297         if (error < 0)
298                 error = -error;
299
300         if (error == ECANCELED)
301                 printf("\nDump aborted\n");
302         else
303                 printf("\n** DUMP FAILED (ERROR %d) **\n", error);
304 }