10ecc478bSLeandro Lupori /*-
20ecc478bSLeandro Lupori * Copyright (c) 2019 Leandro Lupori
30ecc478bSLeandro Lupori * All rights reserved.
40ecc478bSLeandro Lupori *
50ecc478bSLeandro Lupori * Redistribution and use in source and binary forms, with or without
60ecc478bSLeandro Lupori * modification, are permitted provided that the following conditions
70ecc478bSLeandro Lupori * are met:
80ecc478bSLeandro Lupori *
90ecc478bSLeandro Lupori * 1. Redistributions of source code must retain the above copyright
100ecc478bSLeandro Lupori * notice, this list of conditions and the following disclaimer.
110ecc478bSLeandro Lupori * 2. Redistributions in binary form must reproduce the above copyright
120ecc478bSLeandro Lupori * notice, this list of conditions and the following disclaimer in the
130ecc478bSLeandro Lupori * documentation and/or other materials provided with the distribution.
140ecc478bSLeandro Lupori *
150ecc478bSLeandro Lupori * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
160ecc478bSLeandro Lupori * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
170ecc478bSLeandro Lupori * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
180ecc478bSLeandro Lupori * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
190ecc478bSLeandro Lupori * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
200ecc478bSLeandro Lupori * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
210ecc478bSLeandro Lupori * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
220ecc478bSLeandro Lupori * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
230ecc478bSLeandro Lupori * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
240ecc478bSLeandro Lupori * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
250ecc478bSLeandro Lupori */
260ecc478bSLeandro Lupori
270ecc478bSLeandro Lupori #include <sys/types.h>
280ecc478bSLeandro Lupori #include <sys/param.h>
290ecc478bSLeandro Lupori
300ecc478bSLeandro Lupori #include <sys/cons.h>
310ecc478bSLeandro Lupori #include <sys/kerneldump.h>
320ecc478bSLeandro Lupori #include <sys/msgbuf.h>
330ecc478bSLeandro Lupori #include <sys/proc.h>
340ecc478bSLeandro Lupori #include <sys/sysctl.h>
350ecc478bSLeandro Lupori
360ecc478bSLeandro Lupori #include <vm/vm.h>
370ecc478bSLeandro Lupori #include <vm/vm_param.h>
380ecc478bSLeandro Lupori #include <vm/vm_page.h>
390ecc478bSLeandro Lupori #include <vm/vm_phys.h>
406f3b523cSKonstantin Belousov #include <vm/vm_dumpset.h>
410ecc478bSLeandro Lupori #include <vm/pmap.h>
420ecc478bSLeandro Lupori
430ecc478bSLeandro Lupori #include <machine/atomic.h>
440ecc478bSLeandro Lupori #include <machine/dump.h>
450ecc478bSLeandro Lupori #include <machine/md_var.h>
460ecc478bSLeandro Lupori #include <machine/minidump.h>
470ecc478bSLeandro Lupori
480ecc478bSLeandro Lupori /* Debugging stuff */
490ecc478bSLeandro Lupori #define MINIDUMP_DEBUG 0
500ecc478bSLeandro Lupori #if MINIDUMP_DEBUG
510ecc478bSLeandro Lupori #define dprintf(fmt, ...) printf(fmt, ## __VA_ARGS__)
520ecc478bSLeandro Lupori #define DBG(...) __VA_ARGS__
530ecc478bSLeandro Lupori static size_t total, dumptotal;
540ecc478bSLeandro Lupori static void dump_total(const char *id, size_t sz);
550ecc478bSLeandro Lupori #else
560ecc478bSLeandro Lupori #define dprintf(fmt, ...)
570ecc478bSLeandro Lupori #define DBG(...)
580ecc478bSLeandro Lupori #define dump_total(...)
590ecc478bSLeandro Lupori #endif
600ecc478bSLeandro Lupori
610ecc478bSLeandro Lupori extern vm_offset_t __startkernel, __endkernel;
620ecc478bSLeandro Lupori
630ecc478bSLeandro Lupori static int dump_retry_count = 5;
640ecc478bSLeandro Lupori SYSCTL_INT(_machdep, OID_AUTO, dump_retry_count, CTLFLAG_RWTUN,
650ecc478bSLeandro Lupori &dump_retry_count, 0,
660ecc478bSLeandro Lupori "Number of times dump has to retry before bailing out");
670ecc478bSLeandro Lupori
680ecc478bSLeandro Lupori static struct kerneldumpheader kdh;
690ecc478bSLeandro Lupori static char pgbuf[PAGE_SIZE];
700ecc478bSLeandro Lupori
71ab4ed843SMitchell Horne static size_t dumpsize;
720ecc478bSLeandro Lupori
730ecc478bSLeandro Lupori /* Handle chunked writes. */
740ecc478bSLeandro Lupori static size_t fragsz;
750ecc478bSLeandro Lupori
760ecc478bSLeandro Lupori static void
pmap_kenter_temporary(vm_offset_t va,vm_paddr_t pa)770ecc478bSLeandro Lupori pmap_kenter_temporary(vm_offset_t va, vm_paddr_t pa)
780ecc478bSLeandro Lupori {
790ecc478bSLeandro Lupori pmap_kremove(va);
800ecc478bSLeandro Lupori pmap_kenter(va, pa);
810ecc478bSLeandro Lupori }
820ecc478bSLeandro Lupori
830ecc478bSLeandro Lupori static int
blk_flush(struct dumperinfo * di)840ecc478bSLeandro Lupori blk_flush(struct dumperinfo *di)
850ecc478bSLeandro Lupori {
860ecc478bSLeandro Lupori int error;
870ecc478bSLeandro Lupori
880ecc478bSLeandro Lupori if (fragsz == 0)
890ecc478bSLeandro Lupori return (0);
900ecc478bSLeandro Lupori
91*db71383bSMitchell Horne error = dump_append(di, crashdumpmap, fragsz);
920ecc478bSLeandro Lupori DBG(dumptotal += fragsz;)
930ecc478bSLeandro Lupori fragsz = 0;
940ecc478bSLeandro Lupori return (error);
950ecc478bSLeandro Lupori }
960ecc478bSLeandro Lupori
970ecc478bSLeandro Lupori static int
blk_write(struct dumperinfo * di,char * ptr,vm_paddr_t pa,size_t sz)980ecc478bSLeandro Lupori blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz)
990ecc478bSLeandro Lupori {
1000ecc478bSLeandro Lupori size_t len, maxdumpsz;
1010ecc478bSLeandro Lupori int error, i, c;
1020ecc478bSLeandro Lupori
1030ecc478bSLeandro Lupori maxdumpsz = MIN(di->maxiosize, MAXDUMPPGS * PAGE_SIZE);
1040ecc478bSLeandro Lupori if (maxdumpsz == 0) /* seatbelt */
1050ecc478bSLeandro Lupori maxdumpsz = PAGE_SIZE;
1060ecc478bSLeandro Lupori error = 0;
1070ecc478bSLeandro Lupori if ((sz % PAGE_SIZE) != 0) {
1080ecc478bSLeandro Lupori printf("Size not page aligned\n");
1090ecc478bSLeandro Lupori return (EINVAL);
1100ecc478bSLeandro Lupori }
1110ecc478bSLeandro Lupori if (ptr != NULL && pa != 0) {
1120ecc478bSLeandro Lupori printf("Can't have both va and pa!\n");
1130ecc478bSLeandro Lupori return (EINVAL);
1140ecc478bSLeandro Lupori }
1150ecc478bSLeandro Lupori if ((pa % PAGE_SIZE) != 0) {
1160ecc478bSLeandro Lupori printf("Address not page aligned 0x%lx\n", pa);
1170ecc478bSLeandro Lupori return (EINVAL);
1180ecc478bSLeandro Lupori }
1190ecc478bSLeandro Lupori if (ptr != NULL) {
1200ecc478bSLeandro Lupori /*
1210ecc478bSLeandro Lupori * If we're doing a virtual dump, flush any pre-existing
1220ecc478bSLeandro Lupori * pa pages
1230ecc478bSLeandro Lupori */
1240ecc478bSLeandro Lupori error = blk_flush(di);
1250ecc478bSLeandro Lupori if (error)
1260ecc478bSLeandro Lupori return (error);
1270ecc478bSLeandro Lupori }
1280ecc478bSLeandro Lupori while (sz) {
1290ecc478bSLeandro Lupori len = maxdumpsz - fragsz;
1300ecc478bSLeandro Lupori if (len > sz)
1310ecc478bSLeandro Lupori len = sz;
132ab4ed843SMitchell Horne
133ab4ed843SMitchell Horne dumpsys_pb_progress(len);
1340ecc478bSLeandro Lupori
1350ecc478bSLeandro Lupori if (ptr) {
136*db71383bSMitchell Horne error = dump_append(di, ptr, len);
1370ecc478bSLeandro Lupori if (error)
1380ecc478bSLeandro Lupori return (error);
1390ecc478bSLeandro Lupori DBG(dumptotal += len;)
1400ecc478bSLeandro Lupori ptr += len;
1410ecc478bSLeandro Lupori } else {
1420ecc478bSLeandro Lupori for (i = 0; i < len; i += PAGE_SIZE)
1430ecc478bSLeandro Lupori pmap_kenter_temporary(
1440ecc478bSLeandro Lupori (vm_offset_t)crashdumpmap + fragsz + i,
1450ecc478bSLeandro Lupori pa + i);
1460ecc478bSLeandro Lupori
1470ecc478bSLeandro Lupori fragsz += len;
1480ecc478bSLeandro Lupori pa += len;
1490ecc478bSLeandro Lupori if (fragsz == maxdumpsz) {
1500ecc478bSLeandro Lupori error = blk_flush(di);
1510ecc478bSLeandro Lupori if (error)
1520ecc478bSLeandro Lupori return (error);
1530ecc478bSLeandro Lupori }
1540ecc478bSLeandro Lupori }
1550ecc478bSLeandro Lupori sz -= len;
1560ecc478bSLeandro Lupori
1570ecc478bSLeandro Lupori /* Check for user abort. */
1580ecc478bSLeandro Lupori c = cncheckc();
1590ecc478bSLeandro Lupori if (c == 0x03)
1600ecc478bSLeandro Lupori return (ECANCELED);
1610ecc478bSLeandro Lupori if (c != -1)
1620ecc478bSLeandro Lupori printf(" (CTRL-C to abort) ");
1630ecc478bSLeandro Lupori }
1640ecc478bSLeandro Lupori
1650ecc478bSLeandro Lupori return (0);
1660ecc478bSLeandro Lupori }
1670ecc478bSLeandro Lupori
1680ecc478bSLeandro Lupori static int
dump_pmap(struct dumperinfo * di)1690ecc478bSLeandro Lupori dump_pmap(struct dumperinfo *di)
1700ecc478bSLeandro Lupori {
1710ecc478bSLeandro Lupori void *ctx;
1720ecc478bSLeandro Lupori char *buf;
1730ecc478bSLeandro Lupori u_long nbytes;
1740ecc478bSLeandro Lupori int error;
1750ecc478bSLeandro Lupori
1760ecc478bSLeandro Lupori ctx = dumpsys_dump_pmap_init(sizeof(pgbuf) / PAGE_SIZE);
1770ecc478bSLeandro Lupori
1780ecc478bSLeandro Lupori for (;;) {
1790ecc478bSLeandro Lupori buf = dumpsys_dump_pmap(ctx, pgbuf, &nbytes);
1800ecc478bSLeandro Lupori if (buf == NULL)
1810ecc478bSLeandro Lupori break;
1820ecc478bSLeandro Lupori error = blk_write(di, buf, 0, nbytes);
1830ecc478bSLeandro Lupori if (error)
1840ecc478bSLeandro Lupori return (error);
1850ecc478bSLeandro Lupori }
1860ecc478bSLeandro Lupori
1870ecc478bSLeandro Lupori return (0);
1880ecc478bSLeandro Lupori }
1890ecc478bSLeandro Lupori
1900ecc478bSLeandro Lupori int
cpu_minidumpsys(struct dumperinfo * di,const struct minidumpstate * state)1911adebe3cSMitchell Horne cpu_minidumpsys(struct dumperinfo *di, const struct minidumpstate *state)
1920ecc478bSLeandro Lupori {
1930ecc478bSLeandro Lupori vm_paddr_t pa;
194ab4ed843SMitchell Horne int error, retry_count;
1950ecc478bSLeandro Lupori uint32_t pmapsize;
1960ecc478bSLeandro Lupori struct minidumphdr mdhdr;
1971d2d1418SMitchell Horne struct msgbuf *mbp;
1980ecc478bSLeandro Lupori
1990ecc478bSLeandro Lupori retry_count = 0;
2000ecc478bSLeandro Lupori retry:
2010ecc478bSLeandro Lupori retry_count++;
2020ecc478bSLeandro Lupori fragsz = 0;
2030ecc478bSLeandro Lupori DBG(total = dumptotal = 0;)
2040ecc478bSLeandro Lupori
2050ecc478bSLeandro Lupori /* Build set of dumpable pages from kernel pmap */
20610fe6f80SMitchell Horne pmapsize = dumpsys_scan_pmap(state->dump_bitset);
2070ecc478bSLeandro Lupori if (pmapsize % PAGE_SIZE != 0) {
2080ecc478bSLeandro Lupori printf("pmapsize not page aligned: 0x%x\n", pmapsize);
2090ecc478bSLeandro Lupori return (EINVAL);
2100ecc478bSLeandro Lupori }
2110ecc478bSLeandro Lupori
2120ecc478bSLeandro Lupori /* Calculate dump size */
2131d2d1418SMitchell Horne mbp = state->msgbufp;
2140ecc478bSLeandro Lupori dumpsize = PAGE_SIZE; /* header */
2151d2d1418SMitchell Horne dumpsize += round_page(mbp->msg_size);
21600e66147SD Scott Phillips dumpsize += round_page(sizeof(dump_avail));
217ab041f71SD Scott Phillips dumpsize += round_page(BITSET_SIZE(vm_page_dump_pages));
2180ecc478bSLeandro Lupori dumpsize += pmapsize;
21910fe6f80SMitchell Horne VM_PAGE_DUMP_FOREACH(state->dump_bitset, pa) {
2200ecc478bSLeandro Lupori /* Clear out undumpable pages now if needed */
22131991a5aSMitchell Horne if (vm_phys_is_dumpable(pa))
2220ecc478bSLeandro Lupori dumpsize += PAGE_SIZE;
2230ecc478bSLeandro Lupori else
22410fe6f80SMitchell Horne vm_page_dump_drop(state->dump_bitset, pa);
2250ecc478bSLeandro Lupori }
226ab4ed843SMitchell Horne dumpsys_pb_init(dumpsize);
2270ecc478bSLeandro Lupori
2280ecc478bSLeandro Lupori /* Initialize mdhdr */
2290ecc478bSLeandro Lupori bzero(&mdhdr, sizeof(mdhdr));
2300ecc478bSLeandro Lupori strcpy(mdhdr.magic, MINIDUMP_MAGIC);
2310ecc478bSLeandro Lupori strncpy(mdhdr.mmu_name, pmap_mmu_name(), sizeof(mdhdr.mmu_name) - 1);
2320ecc478bSLeandro Lupori mdhdr.version = MINIDUMP_VERSION;
2331d2d1418SMitchell Horne mdhdr.msgbufsize = mbp->msg_size;
234ab041f71SD Scott Phillips mdhdr.bitmapsize = round_page(BITSET_SIZE(vm_page_dump_pages));
2350ecc478bSLeandro Lupori mdhdr.pmapsize = pmapsize;
2360ecc478bSLeandro Lupori mdhdr.kernbase = VM_MIN_KERNEL_ADDRESS;
2370ecc478bSLeandro Lupori mdhdr.kernend = VM_MAX_SAFE_KERNEL_ADDRESS;
2380ecc478bSLeandro Lupori mdhdr.dmapbase = DMAP_BASE_ADDRESS;
2390ecc478bSLeandro Lupori mdhdr.dmapend = DMAP_MAX_ADDRESS;
2400ecc478bSLeandro Lupori mdhdr.hw_direct_map = hw_direct_map;
2410ecc478bSLeandro Lupori mdhdr.startkernel = __startkernel;
2420ecc478bSLeandro Lupori mdhdr.endkernel = __endkernel;
24300e66147SD Scott Phillips mdhdr.dumpavailsize = round_page(sizeof(dump_avail));
2440ecc478bSLeandro Lupori
2450ecc478bSLeandro Lupori dump_init_header(di, &kdh, KERNELDUMPMAGIC, KERNELDUMP_POWERPC_VERSION,
2460ecc478bSLeandro Lupori dumpsize);
2470ecc478bSLeandro Lupori
2480ecc478bSLeandro Lupori error = dump_start(di, &kdh);
2490ecc478bSLeandro Lupori if (error)
2500ecc478bSLeandro Lupori goto fail;
2510ecc478bSLeandro Lupori
2520ecc478bSLeandro Lupori printf("Dumping %lu out of %ju MB:", dumpsize >> 20,
2530ecc478bSLeandro Lupori ptoa((uintmax_t)physmem) / 1048576);
2540ecc478bSLeandro Lupori
2550ecc478bSLeandro Lupori /* Dump minidump header */
2560ecc478bSLeandro Lupori bzero(pgbuf, sizeof(pgbuf));
2570ecc478bSLeandro Lupori memcpy(pgbuf, &mdhdr, sizeof(mdhdr));
2580ecc478bSLeandro Lupori error = blk_write(di, pgbuf, 0, PAGE_SIZE);
2590ecc478bSLeandro Lupori if (error)
2600ecc478bSLeandro Lupori goto fail;
2610ecc478bSLeandro Lupori dump_total("header", PAGE_SIZE);
2620ecc478bSLeandro Lupori
2630ecc478bSLeandro Lupori /* Dump msgbuf up front */
2641d2d1418SMitchell Horne error = blk_write(di, mbp->msg_ptr, 0, round_page(mbp->msg_size));
2651d2d1418SMitchell Horne dump_total("msgbuf", round_page(mbp->msg_size));
2660ecc478bSLeandro Lupori
26700e66147SD Scott Phillips /* Dump dump_avail */
26800e66147SD Scott Phillips _Static_assert(sizeof(dump_avail) <= sizeof(pgbuf),
26900e66147SD Scott Phillips "Large dump_avail not handled");
27000e66147SD Scott Phillips bzero(pgbuf, sizeof(mdhdr));
27100e66147SD Scott Phillips memcpy(pgbuf, dump_avail, sizeof(dump_avail));
27200e66147SD Scott Phillips error = blk_write(di, pgbuf, 0, PAGE_SIZE);
27300e66147SD Scott Phillips if (error)
27400e66147SD Scott Phillips goto fail;
27500e66147SD Scott Phillips dump_total("dump_avail", round_page(sizeof(dump_avail)));
27600e66147SD Scott Phillips
2770ecc478bSLeandro Lupori /* Dump bitmap */
2780ecc478bSLeandro Lupori error = blk_write(di, (char *)vm_page_dump, 0,
279ab041f71SD Scott Phillips round_page(BITSET_SIZE(vm_page_dump_pages)));
2800ecc478bSLeandro Lupori if (error)
2810ecc478bSLeandro Lupori goto fail;
282ab041f71SD Scott Phillips dump_total("bitmap", round_page(BITSET_SIZE(vm_page_dump_pages)));
2830ecc478bSLeandro Lupori
2840ecc478bSLeandro Lupori /* Dump kernel page directory pages */
2850ecc478bSLeandro Lupori error = dump_pmap(di);
2860ecc478bSLeandro Lupori if (error)
2870ecc478bSLeandro Lupori goto fail;
2880ecc478bSLeandro Lupori dump_total("pmap", pmapsize);
2890ecc478bSLeandro Lupori
2900ecc478bSLeandro Lupori /* Dump memory chunks */
29110fe6f80SMitchell Horne VM_PAGE_DUMP_FOREACH(state->dump_bitset, pa) {
2920ecc478bSLeandro Lupori error = blk_write(di, 0, pa, PAGE_SIZE);
2930ecc478bSLeandro Lupori if (error)
2940ecc478bSLeandro Lupori goto fail;
2950ecc478bSLeandro Lupori }
2960ecc478bSLeandro Lupori
2970ecc478bSLeandro Lupori error = blk_flush(di);
2980ecc478bSLeandro Lupori if (error)
2990ecc478bSLeandro Lupori goto fail;
3000ecc478bSLeandro Lupori dump_total("mem_chunks", dumpsize - total);
3010ecc478bSLeandro Lupori
3020ecc478bSLeandro Lupori error = dump_finish(di, &kdh);
3030ecc478bSLeandro Lupori if (error)
3040ecc478bSLeandro Lupori goto fail;
3050ecc478bSLeandro Lupori
3060ecc478bSLeandro Lupori printf("\nDump complete\n");
3070ecc478bSLeandro Lupori return (0);
3080ecc478bSLeandro Lupori
3090ecc478bSLeandro Lupori fail:
3100ecc478bSLeandro Lupori if (error < 0)
3110ecc478bSLeandro Lupori error = -error;
3120ecc478bSLeandro Lupori
3130ecc478bSLeandro Lupori printf("\n");
3140ecc478bSLeandro Lupori if (error == ENOSPC) {
3150ecc478bSLeandro Lupori printf("Dump map grown while dumping. ");
3160ecc478bSLeandro Lupori if (retry_count < dump_retry_count) {
3170ecc478bSLeandro Lupori printf("Retrying...\n");
3180ecc478bSLeandro Lupori goto retry;
3190ecc478bSLeandro Lupori }
3200ecc478bSLeandro Lupori printf("Dump failed.\n");
3210ecc478bSLeandro Lupori } else if (error == ECANCELED)
3220ecc478bSLeandro Lupori printf("Dump aborted\n");
3230ecc478bSLeandro Lupori else if (error == E2BIG)
3240ecc478bSLeandro Lupori printf("Dump failed. Partition too small.\n");
3250ecc478bSLeandro Lupori else
3260ecc478bSLeandro Lupori printf("** DUMP FAILED (ERROR %d) **\n", error);
3270ecc478bSLeandro Lupori return (error);
3280ecc478bSLeandro Lupori }
3290ecc478bSLeandro Lupori
3300ecc478bSLeandro Lupori #if MINIDUMP_DEBUG
3310ecc478bSLeandro Lupori static void
dump_total(const char * id,size_t sz)3320ecc478bSLeandro Lupori dump_total(const char *id, size_t sz)
3330ecc478bSLeandro Lupori {
3340ecc478bSLeandro Lupori total += sz;
3350ecc478bSLeandro Lupori dprintf("\n%s=%08lx/%08lx/%08lx\n",
3360ecc478bSLeandro Lupori id, sz, total, dumptotal);
3370ecc478bSLeandro Lupori }
3380ecc478bSLeandro Lupori #endif
339