Line 0
Link Here
|
|
|
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/sysctl.h> |
37 |
#include <sys/kernel.h> |
38 |
#include <sys/proc.h> |
39 |
#include <sys/kerneldump.h> |
40 |
#ifdef SW_WATCHDOG |
41 |
#include <sys/watchdog.h> |
42 |
#endif |
43 |
#include <vm/vm.h> |
44 |
#include <vm/vm_param.h> |
45 |
#include <vm/pmap.h> |
46 |
#include <machine/dump.h> |
47 |
#include <machine/elf.h> |
48 |
#include <machine/md_var.h> |
49 |
#include <machine/pcb.h> |
50 |
|
51 |
CTASSERT(sizeof(struct kerneldumpheader) == 512); |
52 |
|
53 |
#ifndef __sparc__ |
54 |
int do_minidump = 1; |
55 |
SYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RWTUN, &do_minidump, 0, |
56 |
"Enable mini crash dumps"); |
57 |
#endif |
58 |
|
59 |
/* |
60 |
* Don't touch the first SIZEOF_METADATA bytes on the dump device. This |
61 |
* is to protect us from metadata and to protect metadata from us. |
62 |
*/ |
63 |
#define SIZEOF_METADATA (64*1024) |
64 |
|
65 |
#define MD_ALIGN(x) (((off_t)(x) + PAGE_MASK) & ~PAGE_MASK) |
66 |
#define DEV_ALIGN(x) (((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1)) |
67 |
|
68 |
off_t dumplo; |
69 |
|
70 |
/* Handle buffered writes. */ |
71 |
static char buffer[DEV_BSIZE]; |
72 |
static size_t fragsz; |
73 |
|
74 |
struct md_pa dump_map[DUMPSYS_MD_PA_NPAIRS]; |
75 |
|
76 |
#if !defined(__sparc__) && !defined(__powerpc__) |
77 |
void |
78 |
dumpsys_gen_md_pa_init(void) |
79 |
{ |
80 |
int n, idx; |
81 |
|
82 |
bzero(dump_map, sizeof(dump_map)); |
83 |
for (n = 0; n < sizeof(dump_map) / sizeof(dump_map[0]); n++) { |
84 |
idx = n * 2; |
85 |
if (dump_avail[idx] == 0 && dump_avail[idx + 1] == 0) |
86 |
break; |
87 |
dump_map[n].md_start = dump_avail[idx]; |
88 |
dump_map[n].md_size = dump_avail[idx + 1] - dump_avail[idx]; |
89 |
} |
90 |
} |
91 |
#endif |
92 |
|
93 |
struct md_pa * |
94 |
dumpsys_gen_md_pa_next(struct md_pa *mdp) |
95 |
{ |
96 |
|
97 |
if (mdp == NULL) |
98 |
return (&dump_map[0]); |
99 |
|
100 |
mdp++; |
101 |
if (mdp->md_size == 0) |
102 |
mdp = NULL; |
103 |
return (mdp); |
104 |
} |
105 |
|
106 |
void |
107 |
dumpsys_gen_wbinv_all(void) |
108 |
{ |
109 |
|
110 |
/* nop */; |
111 |
} |
112 |
|
113 |
void |
114 |
dumpsys_gen_unmap_chunk(vm_paddr_t pa __unused, size_t chunk __unused, |
115 |
void *va __unused) |
116 |
{ |
117 |
|
118 |
/* nop */; |
119 |
} |
120 |
|
121 |
int |
122 |
dumpsys_buf_write(struct dumperinfo *di, char *ptr, size_t sz) |
123 |
{ |
124 |
size_t len; |
125 |
int error; |
126 |
|
127 |
while (sz) { |
128 |
len = DEV_BSIZE - fragsz; |
129 |
if (len > sz) |
130 |
len = sz; |
131 |
bcopy(ptr, buffer + fragsz, len); |
132 |
fragsz += len; |
133 |
ptr += len; |
134 |
sz -= len; |
135 |
if (fragsz == DEV_BSIZE) { |
136 |
error = dump_write(di, buffer, 0, dumplo, |
137 |
DEV_BSIZE); |
138 |
if (error) |
139 |
return error; |
140 |
dumplo += DEV_BSIZE; |
141 |
fragsz = 0; |
142 |
} |
143 |
} |
144 |
|
145 |
return (0); |
146 |
} |
147 |
|
148 |
int |
149 |
dumpsys_buf_flush(struct dumperinfo *di) |
150 |
{ |
151 |
int error; |
152 |
|
153 |
if (fragsz == 0) |
154 |
return (0); |
155 |
|
156 |
error = dump_write(di, buffer, 0, dumplo, DEV_BSIZE); |
157 |
dumplo += DEV_BSIZE; |
158 |
fragsz = 0; |
159 |
return (error); |
160 |
} |
161 |
|
162 |
CTASSERT(PAGE_SHIFT < 20); |
163 |
#define PG2MB(pgs) ((pgs + (1 << (20 - PAGE_SHIFT)) - 1) >> (20 - PAGE_SHIFT)) |
164 |
|
165 |
int |
166 |
dumpsys_cb_dumpdata(struct md_pa *mdp, int seqnr, void *arg) |
167 |
{ |
168 |
struct dumperinfo *di = (struct dumperinfo*)arg; |
169 |
vm_paddr_t pa; |
170 |
void *va; |
171 |
uint64_t pgs; |
172 |
size_t counter, sz, chunk; |
173 |
int c, error, twiddle; |
174 |
u_int maxdumppgs; |
175 |
|
176 |
error = 0; /* catch case in which chunk size is 0 */ |
177 |
counter = 0; /* Update twiddle every 16MB */ |
178 |
twiddle = 0; |
179 |
va = 0; |
180 |
pgs = mdp->md_size / PAGE_SIZE; |
181 |
pa = mdp->md_start; |
182 |
maxdumppgs = min(di->maxiosize / PAGE_SIZE, MAXDUMPPGS); |
183 |
if (maxdumppgs == 0) /* seatbelt */ |
184 |
maxdumppgs = 1; |
185 |
|
186 |
printf(" chunk %d: %juMB (%ju pages)", seqnr, (uintmax_t)PG2MB(pgs), |
187 |
(uintmax_t)pgs); |
188 |
|
189 |
dumpsys_wbinv_all(); |
190 |
while (pgs) { |
191 |
chunk = pgs; |
192 |
if (chunk > maxdumppgs) |
193 |
chunk = maxdumppgs; |
194 |
sz = chunk << PAGE_SHIFT; |
195 |
counter += sz; |
196 |
if (counter >> 24) { |
197 |
printf(" %ju", (uintmax_t)PG2MB(pgs)); |
198 |
counter &= (1<<24) - 1; |
199 |
} |
200 |
|
201 |
dumpsys_map_chunk(pa, chunk, &va); |
202 |
#ifdef SW_WATCHDOG |
203 |
wdog_kern_pat(WD_LASTVAL); |
204 |
#endif |
205 |
|
206 |
error = dump_write(di, va, 0, dumplo, sz); |
207 |
dumpsys_unmap_chunk(pa, chunk, va); |
208 |
if (error) |
209 |
break; |
210 |
dumplo += sz; |
211 |
pgs -= chunk; |
212 |
pa += sz; |
213 |
|
214 |
/* Check for user abort. */ |
215 |
c = cncheckc(); |
216 |
if (c == 0x03) |
217 |
return (ECANCELED); |
218 |
if (c != -1) |
219 |
printf(" (CTRL-C to abort) "); |
220 |
} |
221 |
printf(" ... %s\n", (error) ? "fail" : "ok"); |
222 |
return (error); |
223 |
} |
224 |
|
225 |
int |
226 |
dumpsys_foreach_chunk(dumpsys_callback_t cb, void *arg) |
227 |
{ |
228 |
struct md_pa *mdp; |
229 |
int error, seqnr; |
230 |
|
231 |
seqnr = 0; |
232 |
mdp = dumpsys_md_pa_next(NULL); |
233 |
while (mdp != NULL) { |
234 |
error = (*cb)(mdp, seqnr++, arg); |
235 |
if (error) |
236 |
return (-error); |
237 |
mdp = dumpsys_md_pa_next(mdp); |
238 |
} |
239 |
return (seqnr); |
240 |
} |
241 |
|
242 |
#ifndef DUMPSYS_HAS_MD_DUMPSYS |
243 |
static off_t fileofs; |
244 |
|
245 |
static int |
246 |
cb_dumphdr(struct md_pa *mdp, int seqnr, void *arg) |
247 |
{ |
248 |
struct dumperinfo *di = (struct dumperinfo*)arg; |
249 |
Elf_Phdr phdr; |
250 |
uint64_t size; |
251 |
int error; |
252 |
|
253 |
size = mdp->md_size; |
254 |
bzero(&phdr, sizeof(phdr)); |
255 |
phdr.p_type = PT_LOAD; |
256 |
phdr.p_flags = PF_R; /* XXX */ |
257 |
phdr.p_offset = fileofs; |
258 |
#ifdef __powerpc__ |
259 |
phdr.p_vaddr = (do_minidump? mdp->md_start : ~0L); |
260 |
phdr.p_paddr = (do_minidump? ~0L : mdp->md_start); |
261 |
#else |
262 |
phdr.p_vaddr = mdp->md_start; |
263 |
phdr.p_paddr = mdp->md_start; |
264 |
#endif |
265 |
phdr.p_filesz = size; |
266 |
phdr.p_memsz = size; |
267 |
phdr.p_align = PAGE_SIZE; |
268 |
|
269 |
error = dumpsys_buf_write(di, (char*)&phdr, sizeof(phdr)); |
270 |
fileofs += phdr.p_filesz; |
271 |
return (error); |
272 |
} |
273 |
|
274 |
static int |
275 |
cb_size(struct md_pa *mdp, int seqnr, void *arg) |
276 |
{ |
277 |
uint64_t *sz = (uint64_t*)arg; |
278 |
|
279 |
*sz += (uint64_t)mdp->md_size; |
280 |
return (0); |
281 |
} |
282 |
|
283 |
int |
284 |
dumpsys(struct dumperinfo *di) |
285 |
{ |
286 |
struct kerneldumpheader kdh; |
287 |
Elf_Ehdr ehdr; |
288 |
uint64_t dumpsize; |
289 |
off_t hdrgap; |
290 |
size_t hdrsz; |
291 |
int error; |
292 |
|
293 |
#ifndef __powerpc__ |
294 |
if (do_minidump) |
295 |
return (minidumpsys(di)); |
296 |
#endif |
297 |
bzero(&ehdr, sizeof(ehdr)); |
298 |
ehdr.e_ident[EI_MAG0] = ELFMAG0; |
299 |
ehdr.e_ident[EI_MAG1] = ELFMAG1; |
300 |
ehdr.e_ident[EI_MAG2] = ELFMAG2; |
301 |
ehdr.e_ident[EI_MAG3] = ELFMAG3; |
302 |
ehdr.e_ident[EI_CLASS] = ELF_CLASS; |
303 |
#if BYTE_ORDER == LITTLE_ENDIAN |
304 |
ehdr.e_ident[EI_DATA] = ELFDATA2LSB; |
305 |
#else |
306 |
ehdr.e_ident[EI_DATA] = ELFDATA2MSB; |
307 |
#endif |
308 |
ehdr.e_ident[EI_VERSION] = EV_CURRENT; |
309 |
ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE; /* XXX big picture? */ |
310 |
ehdr.e_type = ET_CORE; |
311 |
ehdr.e_machine = EM_VALUE; |
312 |
ehdr.e_phoff = sizeof(ehdr); |
313 |
ehdr.e_flags = 0; |
314 |
ehdr.e_ehsize = sizeof(ehdr); |
315 |
ehdr.e_phentsize = sizeof(Elf_Phdr); |
316 |
ehdr.e_shentsize = sizeof(Elf_Shdr); |
317 |
|
318 |
dumpsys_md_pa_init(); |
319 |
|
320 |
/* Calculate dump size. */ |
321 |
dumpsize = 0L; |
322 |
ehdr.e_phnum = dumpsys_foreach_chunk(cb_size, &dumpsize); |
323 |
hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize; |
324 |
fileofs = MD_ALIGN(hdrsz); |
325 |
dumpsize += fileofs; |
326 |
hdrgap = fileofs - DEV_ALIGN(hdrsz); |
327 |
|
328 |
/* Determine dump offset on device. */ |
329 |
if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { |
330 |
error = ENOSPC; |
331 |
goto fail; |
332 |
} |
333 |
dumplo = di->mediaoffset + di->mediasize - dumpsize; |
334 |
dumplo -= sizeof(kdh) * 2; |
335 |
|
336 |
mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_VERSION, dumpsize, |
337 |
di->blocksize); |
338 |
|
339 |
printf("Dumping %ju MB (%d chunks)\n", (uintmax_t)dumpsize >> 20, |
340 |
ehdr.e_phnum); |
341 |
|
342 |
/* Dump leader */ |
343 |
error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); |
344 |
if (error) |
345 |
goto fail; |
346 |
dumplo += sizeof(kdh); |
347 |
|
348 |
/* Dump ELF header */ |
349 |
error = dumpsys_buf_write(di, (char*)&ehdr, sizeof(ehdr)); |
350 |
if (error) |
351 |
goto fail; |
352 |
|
353 |
/* Dump program headers */ |
354 |
error = dumpsys_foreach_chunk(cb_dumphdr, di); |
355 |
if (error < 0) |
356 |
goto fail; |
357 |
dumpsys_buf_flush(di); |
358 |
|
359 |
/* |
360 |
* All headers are written using blocked I/O, so we know the |
361 |
* current offset is (still) block aligned. Skip the alignement |
362 |
* in the file to have the segment contents aligned at page |
363 |
* boundary. We cannot use MD_ALIGN on dumplo, because we don't |
364 |
* care and may very well be unaligned within the dump device. |
365 |
*/ |
366 |
dumplo += hdrgap; |
367 |
|
368 |
/* Dump memory chunks (updates dumplo) */ |
369 |
error = dumpsys_foreach_chunk(dumpsys_cb_dumpdata, di); |
370 |
if (error < 0) |
371 |
goto fail; |
372 |
|
373 |
/* Dump trailer */ |
374 |
error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); |
375 |
if (error) |
376 |
goto fail; |
377 |
|
378 |
/* Signal completion, signoff and exit stage left. */ |
379 |
dump_write(di, NULL, 0, 0, 0); |
380 |
printf("\nDump complete\n"); |
381 |
return (0); |
382 |
|
383 |
fail: |
384 |
if (error < 0) |
385 |
error = -error; |
386 |
|
387 |
if (error == ECANCELED) |
388 |
printf("\nDump aborted\n"); |
389 |
else if (error == ENOSPC) |
390 |
printf("\nDump failed. Partition too small.\n"); |
391 |
else |
392 |
printf("\n** DUMP FAILED (ERROR %d) **\n", error); |
393 |
return (error); |
394 |
} |
395 |
#endif /* !DUMPSYS_HAS_MD_DUMPSYS */ |