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