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>
41 #include <vm/vm_dumpset.h>
44 #include <machine/atomic.h>
45 #include <machine/dump.h>
46 #include <machine/md_var.h>
47 #include <machine/minidump.h>
50 #define MINIDUMP_DEBUG 0
52 #define dprintf(fmt, ...) printf(fmt, ## __VA_ARGS__)
53 #define DBG(...) __VA_ARGS__
54 static size_t total, dumptotal;
55 static void dump_total(const char *id, size_t sz);
57 #define dprintf(fmt, ...)
59 #define dump_total(...)
62 extern vm_offset_t __startkernel, __endkernel;
64 static int dump_retry_count = 5;
65 SYSCTL_INT(_machdep, OID_AUTO, dump_retry_count, CTLFLAG_RWTUN,
67 "Number of times dump has to retry before bailing out");
69 static struct kerneldumpheader kdh;
70 static char pgbuf[PAGE_SIZE];
76 } progress_track[10] = {
89 static size_t counter, dumpsize, progress;
91 /* Handle chunked writes. */
95 is_dumpable(vm_paddr_t pa)
100 if ((m = vm_phys_paddr_to_vm_page(pa)) != NULL)
101 return ((m->flags & PG_NODUMP) == 0);
102 for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) {
103 if (pa >= dump_avail[i] && pa < dump_avail[i + 1])
110 pmap_kenter_temporary(vm_offset_t va, vm_paddr_t pa)
117 report_progress(void)
121 sofar = 100 - ((progress * 100) / dumpsize);
122 for (i = 0; i < nitems(progress_track); i++) {
123 if (sofar < progress_track[i].min_per ||
124 sofar > progress_track[i].max_per)
126 if (progress_track[i].visited)
128 progress_track[i].visited = 1;
129 printf("..%d%%", sofar);
135 blk_flush(struct dumperinfo *di)
142 error = dump_append(di, crashdumpmap, 0, fragsz);
143 DBG(dumptotal += fragsz;)
149 blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz)
151 size_t len, maxdumpsz;
154 maxdumpsz = MIN(di->maxiosize, MAXDUMPPGS * PAGE_SIZE);
155 if (maxdumpsz == 0) /* seatbelt */
156 maxdumpsz = PAGE_SIZE;
158 if ((sz % PAGE_SIZE) != 0) {
159 printf("Size not page aligned\n");
162 if (ptr != NULL && pa != 0) {
163 printf("Can't have both va and pa!\n");
166 if ((pa % PAGE_SIZE) != 0) {
167 printf("Address not page aligned 0x%lx\n", pa);
172 * If we're doing a virtual dump, flush any pre-existing
175 error = blk_flush(di);
180 len = maxdumpsz - fragsz;
187 counter &= (1<<20) - 1;
191 error = dump_append(di, ptr, 0, len);
194 DBG(dumptotal += len;)
197 for (i = 0; i < len; i += PAGE_SIZE)
198 pmap_kenter_temporary(
199 (vm_offset_t)crashdumpmap + fragsz + i,
204 if (fragsz == maxdumpsz) {
205 error = blk_flush(di);
212 /* Check for user abort. */
217 printf(" (CTRL-C to abort) ");
224 dump_pmap(struct dumperinfo *di)
231 ctx = dumpsys_dump_pmap_init(sizeof(pgbuf) / PAGE_SIZE);
234 buf = dumpsys_dump_pmap(ctx, pgbuf, &nbytes);
237 error = blk_write(di, buf, 0, nbytes);
246 minidumpsys(struct dumperinfo *di)
249 int error, i, retry_count;
251 struct minidumphdr mdhdr;
257 DBG(total = dumptotal = 0;)
261 for (i = 0; i < nitems(progress_track); i++)
262 progress_track[i].visited = 0;
264 /* Build set of dumpable pages from kernel pmap */
265 pmapsize = dumpsys_scan_pmap();
266 if (pmapsize % PAGE_SIZE != 0) {
267 printf("pmapsize not page aligned: 0x%x\n", pmapsize);
271 /* Calculate dump size */
272 dumpsize = PAGE_SIZE; /* header */
273 dumpsize += round_page(msgbufp->msg_size);
274 dumpsize += round_page(sizeof(dump_avail));
275 dumpsize += round_page(BITSET_SIZE(vm_page_dump_pages));
276 dumpsize += pmapsize;
277 VM_PAGE_DUMP_FOREACH(pa) {
278 /* Clear out undumpable pages now if needed */
280 dumpsize += PAGE_SIZE;
286 /* Initialize mdhdr */
287 bzero(&mdhdr, sizeof(mdhdr));
288 strcpy(mdhdr.magic, MINIDUMP_MAGIC);
289 strncpy(mdhdr.mmu_name, pmap_mmu_name(), sizeof(mdhdr.mmu_name) - 1);
290 mdhdr.version = MINIDUMP_VERSION;
291 mdhdr.msgbufsize = msgbufp->msg_size;
292 mdhdr.bitmapsize = round_page(BITSET_SIZE(vm_page_dump_pages));
293 mdhdr.pmapsize = pmapsize;
294 mdhdr.kernbase = VM_MIN_KERNEL_ADDRESS;
295 mdhdr.kernend = VM_MAX_SAFE_KERNEL_ADDRESS;
296 mdhdr.dmapbase = DMAP_BASE_ADDRESS;
297 mdhdr.dmapend = DMAP_MAX_ADDRESS;
298 mdhdr.hw_direct_map = hw_direct_map;
299 mdhdr.startkernel = __startkernel;
300 mdhdr.endkernel = __endkernel;
301 mdhdr.dumpavailsize = round_page(sizeof(dump_avail));
303 dump_init_header(di, &kdh, KERNELDUMPMAGIC, KERNELDUMP_POWERPC_VERSION,
306 error = dump_start(di, &kdh);
310 printf("Dumping %lu out of %ju MB:", dumpsize >> 20,
311 ptoa((uintmax_t)physmem) / 1048576);
313 /* Dump minidump header */
314 bzero(pgbuf, sizeof(pgbuf));
315 memcpy(pgbuf, &mdhdr, sizeof(mdhdr));
316 error = blk_write(di, pgbuf, 0, PAGE_SIZE);
319 dump_total("header", PAGE_SIZE);
321 /* Dump msgbuf up front */
322 error = blk_write(di, (char *)msgbufp->msg_ptr, 0,
323 round_page(msgbufp->msg_size));
324 dump_total("msgbuf", round_page(msgbufp->msg_size));
326 /* Dump dump_avail */
327 _Static_assert(sizeof(dump_avail) <= sizeof(pgbuf),
328 "Large dump_avail not handled");
329 bzero(pgbuf, sizeof(mdhdr));
330 memcpy(pgbuf, dump_avail, sizeof(dump_avail));
331 error = blk_write(di, pgbuf, 0, PAGE_SIZE);
334 dump_total("dump_avail", round_page(sizeof(dump_avail)));
337 error = blk_write(di, (char *)vm_page_dump, 0,
338 round_page(BITSET_SIZE(vm_page_dump_pages)));
341 dump_total("bitmap", round_page(BITSET_SIZE(vm_page_dump_pages)));
343 /* Dump kernel page directory pages */
344 error = dump_pmap(di);
347 dump_total("pmap", pmapsize);
349 /* Dump memory chunks */
350 VM_PAGE_DUMP_FOREACH(pa) {
351 error = blk_write(di, 0, pa, PAGE_SIZE);
356 error = blk_flush(di);
359 dump_total("mem_chunks", dumpsize - total);
361 error = dump_finish(di, &kdh);
365 printf("\nDump complete\n");
373 if (error == ENOSPC) {
374 printf("Dump map grown while dumping. ");
375 if (retry_count < dump_retry_count) {
376 printf("Retrying...\n");
379 printf("Dump failed.\n");
380 } else if (error == ECANCELED)
381 printf("Dump aborted\n");
382 else if (error == E2BIG)
383 printf("Dump failed. Partition too small.\n");
385 printf("** DUMP FAILED (ERROR %d) **\n", error);
391 dump_total(const char *id, size_t sz)
394 dprintf("\n%s=%08lx/%08lx/%08lx\n",
395 id, sz, total, dumptotal);