2 * Copyright (c) 2019 Leandro Lupori
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
28 #include <sys/types.h>
29 #include <sys/param.h>
32 #include <sys/kerneldump.h>
33 #include <sys/msgbuf.h>
35 #include <sys/sysctl.h>
38 #include <vm/vm_param.h>
39 #include <vm/vm_page.h>
40 #include <vm/vm_phys.h>
43 #include <machine/atomic.h>
44 #include <machine/dump.h>
45 #include <machine/md_var.h>
46 #include <machine/minidump.h>
49 * bit to physical address
52 * i - bitmap entry index
55 #define BTOP(bm, i, bit) \
56 (((uint64_t)(i) * sizeof(*(bm)) * NBBY + (bit)) * PAGE_SIZE)
59 #define MINIDUMP_DEBUG 0
61 #define dprintf(fmt, ...) printf(fmt, ## __VA_ARGS__)
62 #define DBG(...) __VA_ARGS__
63 static size_t total, dumptotal;
64 static void dump_total(const char *id, size_t sz);
66 #define dprintf(fmt, ...)
68 #define dump_total(...)
71 extern vm_offset_t __startkernel, __endkernel;
73 int vm_page_dump_size;
74 uint64_t *vm_page_dump;
76 static int dump_retry_count = 5;
77 SYSCTL_INT(_machdep, OID_AUTO, dump_retry_count, CTLFLAG_RWTUN,
79 "Number of times dump has to retry before bailing out");
81 static struct kerneldumpheader kdh;
82 static char pgbuf[PAGE_SIZE];
88 } progress_track[10] = {
101 static size_t counter, dumpsize, progress;
103 /* Handle chunked writes. */
104 static size_t fragsz;
107 dump_add_page(vm_paddr_t pa)
112 idx = pa >> 6; /* 2^6 = 64 */
114 atomic_set_long(&vm_page_dump[idx], 1ul << bit);
118 dump_drop_page(vm_paddr_t pa)
123 idx = pa >> 6; /* 2^6 = 64 */
125 atomic_clear_long(&vm_page_dump[idx], 1ul << bit);
129 is_dumpable(vm_paddr_t pa)
134 if ((m = vm_phys_paddr_to_vm_page(pa)) != NULL)
135 return ((m->flags & PG_NODUMP) == 0);
136 for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) {
137 if (pa >= dump_avail[i] && pa < dump_avail[i + 1])
144 pmap_kenter_temporary(vm_offset_t va, vm_paddr_t pa)
151 report_progress(void)
155 sofar = 100 - ((progress * 100) / dumpsize);
156 for (i = 0; i < nitems(progress_track); i++) {
157 if (sofar < progress_track[i].min_per ||
158 sofar > progress_track[i].max_per)
160 if (progress_track[i].visited)
162 progress_track[i].visited = 1;
163 printf("..%d%%", sofar);
169 blk_flush(struct dumperinfo *di)
176 error = dump_append(di, crashdumpmap, 0, fragsz);
177 DBG(dumptotal += fragsz;)
183 blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz)
185 size_t len, maxdumpsz;
188 maxdumpsz = MIN(di->maxiosize, MAXDUMPPGS * PAGE_SIZE);
189 if (maxdumpsz == 0) /* seatbelt */
190 maxdumpsz = PAGE_SIZE;
192 if ((sz % PAGE_SIZE) != 0) {
193 printf("Size not page aligned\n");
196 if (ptr != NULL && pa != 0) {
197 printf("Can't have both va and pa!\n");
200 if ((pa % PAGE_SIZE) != 0) {
201 printf("Address not page aligned 0x%lx\n", pa);
206 * If we're doing a virtual dump, flush any pre-existing
209 error = blk_flush(di);
214 len = maxdumpsz - fragsz;
221 counter &= (1<<20) - 1;
225 error = dump_append(di, ptr, 0, len);
228 DBG(dumptotal += len;)
231 for (i = 0; i < len; i += PAGE_SIZE)
232 pmap_kenter_temporary(
233 (vm_offset_t)crashdumpmap + fragsz + i,
238 if (fragsz == maxdumpsz) {
239 error = blk_flush(di);
246 /* Check for user abort. */
251 printf(" (CTRL-C to abort) ");
258 dump_pmap(struct dumperinfo *di)
265 ctx = dumpsys_dump_pmap_init(sizeof(pgbuf) / PAGE_SIZE);
268 buf = dumpsys_dump_pmap(ctx, pgbuf, &nbytes);
271 error = blk_write(di, buf, 0, nbytes);
280 minidumpsys(struct dumperinfo *di)
283 int bit, error, i, retry_count;
286 struct minidumphdr mdhdr;
292 DBG(total = dumptotal = 0;)
296 for (i = 0; i < nitems(progress_track); i++)
297 progress_track[i].visited = 0;
299 /* Build set of dumpable pages from kernel pmap */
300 pmapsize = dumpsys_scan_pmap();
301 if (pmapsize % PAGE_SIZE != 0) {
302 printf("pmapsize not page aligned: 0x%x\n", pmapsize);
306 /* Calculate dump size */
307 dumpsize = PAGE_SIZE; /* header */
308 dumpsize += round_page(msgbufp->msg_size);
309 dumpsize += round_page(vm_page_dump_size);
310 dumpsize += pmapsize;
311 for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) {
312 bits = vm_page_dump[i];
313 /* TODO optimize with bit manipulation instructions */
316 for (bit = 0; bit < 64; bit++) {
317 if ((bits & (1ul<<bit)) == 0)
320 pa = BTOP(vm_page_dump, i, bit);
321 /* Clear out undumpable pages now if needed */
323 dumpsize += PAGE_SIZE;
330 /* Initialize mdhdr */
331 bzero(&mdhdr, sizeof(mdhdr));
332 strcpy(mdhdr.magic, MINIDUMP_MAGIC);
333 strncpy(mdhdr.mmu_name, pmap_mmu_name(), sizeof(mdhdr.mmu_name) - 1);
334 mdhdr.version = MINIDUMP_VERSION;
335 mdhdr.msgbufsize = msgbufp->msg_size;
336 mdhdr.bitmapsize = vm_page_dump_size;
337 mdhdr.pmapsize = pmapsize;
338 mdhdr.kernbase = VM_MIN_KERNEL_ADDRESS;
339 mdhdr.kernend = VM_MAX_SAFE_KERNEL_ADDRESS;
340 mdhdr.dmapbase = DMAP_BASE_ADDRESS;
341 mdhdr.dmapend = DMAP_MAX_ADDRESS;
342 mdhdr.hw_direct_map = hw_direct_map;
343 mdhdr.startkernel = __startkernel;
344 mdhdr.endkernel = __endkernel;
346 dump_init_header(di, &kdh, KERNELDUMPMAGIC, KERNELDUMP_POWERPC_VERSION,
349 error = dump_start(di, &kdh);
353 printf("Dumping %lu out of %ju MB:", dumpsize >> 20,
354 ptoa((uintmax_t)physmem) / 1048576);
356 /* Dump minidump header */
357 bzero(pgbuf, sizeof(pgbuf));
358 memcpy(pgbuf, &mdhdr, sizeof(mdhdr));
359 error = blk_write(di, pgbuf, 0, PAGE_SIZE);
362 dump_total("header", PAGE_SIZE);
364 /* Dump msgbuf up front */
365 error = blk_write(di, (char *)msgbufp->msg_ptr, 0,
366 round_page(msgbufp->msg_size));
367 dump_total("msgbuf", round_page(msgbufp->msg_size));
370 error = blk_write(di, (char *)vm_page_dump, 0,
371 round_page(vm_page_dump_size));
374 dump_total("bitmap", round_page(vm_page_dump_size));
376 /* Dump kernel page directory pages */
377 error = dump_pmap(di);
380 dump_total("pmap", pmapsize);
382 /* Dump memory chunks */
383 for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) {
384 bits = vm_page_dump[i];
385 /* TODO optimize with bit manipulation instructions */
388 for (bit = 0; bit < 64; bit++) {
389 if ((bits & (1ul<<bit)) == 0)
392 pa = BTOP(vm_page_dump, i, bit);
393 error = blk_write(di, 0, pa, PAGE_SIZE);
399 error = blk_flush(di);
402 dump_total("mem_chunks", dumpsize - total);
404 error = dump_finish(di, &kdh);
408 printf("\nDump complete\n");
416 if (error == ENOSPC) {
417 printf("Dump map grown while dumping. ");
418 if (retry_count < dump_retry_count) {
419 printf("Retrying...\n");
422 printf("Dump failed.\n");
423 } else if (error == ECANCELED)
424 printf("Dump aborted\n");
425 else if (error == E2BIG)
426 printf("Dump failed. Partition too small.\n");
428 printf("** DUMP FAILED (ERROR %d) **\n", error);
434 dump_total(const char *id, size_t sz)
437 dprintf("\n%s=%08lx/%08lx/%08lx\n",
438 id, sz, total, dumptotal);