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(...)
72 extern vm_offset_t __startkernel, __endkernel;
74 int vm_page_dump_size;
75 uint64_t *vm_page_dump;
77 static int dump_retry_count = 5;
78 SYSCTL_INT(_machdep, OID_AUTO, dump_retry_count, CTLFLAG_RWTUN,
80 "Number of times dump has to retry before bailing out");
82 static struct kerneldumpheader kdh;
83 static char pgbuf[PAGE_SIZE];
89 } progress_track[10] = {
102 static size_t counter, dumpsize, progress;
104 /* Handle chunked writes. */
105 static size_t fragsz;
108 dump_add_page(vm_paddr_t pa)
113 idx = pa >> 6; /* 2^6 = 64 */
115 atomic_set_long(&vm_page_dump[idx], 1ul << bit);
119 dump_drop_page(vm_paddr_t pa)
124 idx = pa >> 6; /* 2^6 = 64 */
126 atomic_clear_long(&vm_page_dump[idx], 1ul << bit);
130 is_dumpable(vm_paddr_t pa)
135 if ((m = vm_phys_paddr_to_vm_page(pa)) != NULL)
136 return ((m->flags & PG_NODUMP) == 0);
137 for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) {
138 if (pa >= dump_avail[i] && pa < dump_avail[i + 1])
145 pmap_kenter_temporary(vm_offset_t va, vm_paddr_t pa)
152 report_progress(void)
156 sofar = 100 - ((progress * 100) / dumpsize);
157 for (i = 0; i < nitems(progress_track); i++) {
158 if (sofar < progress_track[i].min_per ||
159 sofar > progress_track[i].max_per)
161 if (progress_track[i].visited)
163 progress_track[i].visited = 1;
164 printf("..%d%%", sofar);
170 blk_flush(struct dumperinfo *di)
177 error = dump_append(di, crashdumpmap, 0, fragsz);
178 DBG(dumptotal += fragsz;)
184 blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz)
186 size_t len, maxdumpsz;
189 maxdumpsz = MIN(di->maxiosize, MAXDUMPPGS * PAGE_SIZE);
190 if (maxdumpsz == 0) /* seatbelt */
191 maxdumpsz = PAGE_SIZE;
193 if ((sz % PAGE_SIZE) != 0) {
194 printf("Size not page aligned\n");
197 if (ptr != NULL && pa != 0) {
198 printf("Can't have both va and pa!\n");
201 if ((pa % PAGE_SIZE) != 0) {
202 printf("Address not page aligned 0x%lx\n", pa);
207 * If we're doing a virtual dump, flush any pre-existing
210 error = blk_flush(di);
215 len = maxdumpsz - fragsz;
222 counter &= (1<<20) - 1;
226 error = dump_append(di, ptr, 0, len);
229 DBG(dumptotal += len;)
232 for (i = 0; i < len; i += PAGE_SIZE)
233 pmap_kenter_temporary(
234 (vm_offset_t)crashdumpmap + fragsz + i,
239 if (fragsz == maxdumpsz) {
240 error = blk_flush(di);
247 /* Check for user abort. */
252 printf(" (CTRL-C to abort) ");
259 dump_pmap(struct dumperinfo *di)
266 ctx = dumpsys_dump_pmap_init(sizeof(pgbuf) / PAGE_SIZE);
269 buf = dumpsys_dump_pmap(ctx, pgbuf, &nbytes);
272 error = blk_write(di, buf, 0, nbytes);
281 minidumpsys(struct dumperinfo *di)
284 int bit, error, i, retry_count;
287 struct minidumphdr mdhdr;
293 DBG(total = dumptotal = 0;)
297 for (i = 0; i < nitems(progress_track); i++)
298 progress_track[i].visited = 0;
300 /* Build set of dumpable pages from kernel pmap */
301 pmapsize = dumpsys_scan_pmap();
302 if (pmapsize % PAGE_SIZE != 0) {
303 printf("pmapsize not page aligned: 0x%x\n", pmapsize);
307 /* Calculate dump size */
308 dumpsize = PAGE_SIZE; /* header */
309 dumpsize += round_page(msgbufp->msg_size);
310 dumpsize += round_page(vm_page_dump_size);
311 dumpsize += pmapsize;
312 for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) {
313 bits = vm_page_dump[i];
314 /* TODO optimize with bit manipulation instructions */
317 for (bit = 0; bit < 64; bit++) {
318 if ((bits & (1ul<<bit)) == 0)
321 pa = BTOP(vm_page_dump, i, bit);
322 /* Clear out undumpable pages now if needed */
324 dumpsize += PAGE_SIZE;
331 /* Initialize mdhdr */
332 bzero(&mdhdr, sizeof(mdhdr));
333 strcpy(mdhdr.magic, MINIDUMP_MAGIC);
334 strncpy(mdhdr.mmu_name, pmap_mmu_name(), sizeof(mdhdr.mmu_name) - 1);
335 mdhdr.version = MINIDUMP_VERSION;
336 mdhdr.msgbufsize = msgbufp->msg_size;
337 mdhdr.bitmapsize = vm_page_dump_size;
338 mdhdr.pmapsize = pmapsize;
339 mdhdr.kernbase = VM_MIN_KERNEL_ADDRESS;
340 mdhdr.kernend = VM_MAX_SAFE_KERNEL_ADDRESS;
341 mdhdr.dmapbase = DMAP_BASE_ADDRESS;
342 mdhdr.dmapend = DMAP_MAX_ADDRESS;
343 mdhdr.hw_direct_map = hw_direct_map;
344 mdhdr.startkernel = __startkernel;
345 mdhdr.endkernel = __endkernel;
347 dump_init_header(di, &kdh, KERNELDUMPMAGIC, KERNELDUMP_POWERPC_VERSION,
350 error = dump_start(di, &kdh);
354 printf("Dumping %lu out of %ju MB:", dumpsize >> 20,
355 ptoa((uintmax_t)physmem) / 1048576);
357 /* Dump minidump header */
358 bzero(pgbuf, sizeof(pgbuf));
359 memcpy(pgbuf, &mdhdr, sizeof(mdhdr));
360 error = blk_write(di, pgbuf, 0, PAGE_SIZE);
363 dump_total("header", PAGE_SIZE);
365 /* Dump msgbuf up front */
366 error = blk_write(di, (char *)msgbufp->msg_ptr, 0,
367 round_page(msgbufp->msg_size));
368 dump_total("msgbuf", round_page(msgbufp->msg_size));
371 error = blk_write(di, (char *)vm_page_dump, 0,
372 round_page(vm_page_dump_size));
375 dump_total("bitmap", round_page(vm_page_dump_size));
377 /* Dump kernel page directory pages */
378 error = dump_pmap(di);
381 dump_total("pmap", pmapsize);
383 /* Dump memory chunks */
384 /* XXX cluster it up and use blk_dump() */
385 for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) {
386 bits = vm_page_dump[i];
387 /* TODO optimize with bit manipulation instructions */
390 for (bit = 0; bit < 64; bit++) {
391 if ((bits & (1ul<<bit)) == 0)
394 pa = BTOP(vm_page_dump, i, bit);
395 error = blk_write(di, 0, pa, PAGE_SIZE);
401 error = blk_flush(di);
404 dump_total("mem_chunks", dumpsize - total);
406 error = dump_finish(di, &kdh);
410 printf("\nDump complete\n");
418 if (error == ENOSPC) {
419 printf("Dump map grown while dumping. ");
420 if (retry_count < dump_retry_count) {
421 printf("Retrying...\n");
424 printf("Dump failed.\n");
425 } else if (error == ECANCELED)
426 printf("Dump aborted\n");
427 else if (error == E2BIG)
428 printf("Dump failed. Partition too small.\n");
430 printf("** DUMP FAILED (ERROR %d) **\n", error);
436 dump_total(const char *id, size_t sz)
439 dprintf("\n%s=%08lx/%08lx/%08lx\n",
440 id, sz, total, dumptotal);