19a527560SKonstantin Belousov /*- 29a527560SKonstantin Belousov * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 39a527560SKonstantin Belousov * 49a527560SKonstantin Belousov * Copyright (c) 2006 Peter Wemm 59a527560SKonstantin Belousov * All rights reserved. 69a527560SKonstantin Belousov * 79a527560SKonstantin Belousov * Redistribution and use in source and binary forms, with or without 89a527560SKonstantin Belousov * modification, are permitted provided that the following conditions 99a527560SKonstantin Belousov * are met: 109a527560SKonstantin Belousov * 119a527560SKonstantin Belousov * 1. Redistributions of source code must retain the above copyright 129a527560SKonstantin Belousov * notice, this list of conditions and the following disclaimer. 139a527560SKonstantin Belousov * 2. Redistributions in binary form must reproduce the above copyright 149a527560SKonstantin Belousov * notice, this list of conditions and the following disclaimer in the 159a527560SKonstantin Belousov * documentation and/or other materials provided with the distribution. 169a527560SKonstantin Belousov * 179a527560SKonstantin Belousov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 189a527560SKonstantin Belousov * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 199a527560SKonstantin Belousov * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 209a527560SKonstantin Belousov * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 219a527560SKonstantin Belousov * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 229a527560SKonstantin Belousov * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 239a527560SKonstantin Belousov * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 249a527560SKonstantin Belousov * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 259a527560SKonstantin Belousov * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 269a527560SKonstantin Belousov * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 279a527560SKonstantin Belousov */ 289a527560SKonstantin Belousov 299a527560SKonstantin Belousov #include <sys/cdefs.h> 309a527560SKonstantin Belousov __FBSDID("$FreeBSD$"); 319a527560SKonstantin Belousov 329a527560SKonstantin Belousov #include "opt_watchdog.h" 339a527560SKonstantin Belousov 349a527560SKonstantin Belousov #include <sys/param.h> 359a527560SKonstantin Belousov #include <sys/systm.h> 369a527560SKonstantin Belousov #include <sys/conf.h> 379a527560SKonstantin Belousov #include <sys/cons.h> 389a527560SKonstantin Belousov #include <sys/kernel.h> 399a527560SKonstantin Belousov #include <sys/kerneldump.h> 409a527560SKonstantin Belousov #include <sys/msgbuf.h> 419a527560SKonstantin Belousov #include <sys/watchdog.h> 429a527560SKonstantin Belousov #include <vm/vm.h> 4321943937SJeff Roberson #include <vm/vm_param.h> 4421943937SJeff Roberson #include <vm/vm_page.h> 4521943937SJeff Roberson #include <vm/vm_phys.h> 466f3b523cSKonstantin Belousov #include <vm/vm_dumpset.h> 479a527560SKonstantin Belousov #include <vm/pmap.h> 489a527560SKonstantin Belousov #include <machine/atomic.h> 499a527560SKonstantin Belousov #include <machine/elf.h> 509a527560SKonstantin Belousov #include <machine/md_var.h> 519a527560SKonstantin Belousov #include <machine/minidump.h> 529a527560SKonstantin Belousov 539a527560SKonstantin Belousov CTASSERT(sizeof(struct kerneldumpheader) == 512); 549a527560SKonstantin Belousov 559a527560SKonstantin Belousov #define MD_ALIGN(x) (((off_t)(x) + PAGE_MASK) & ~PAGE_MASK) 569a527560SKonstantin Belousov #define DEV_ALIGN(x) roundup2((off_t)(x), DEV_BSIZE) 579a527560SKonstantin Belousov 589a527560SKonstantin Belousov static struct kerneldumpheader kdh; 599a527560SKonstantin Belousov 609a527560SKonstantin Belousov /* Handle chunked writes. */ 619a527560SKonstantin Belousov static size_t fragsz; 629a527560SKonstantin Belousov static void *dump_va; 639a527560SKonstantin Belousov 649a527560SKonstantin Belousov static int 659a527560SKonstantin Belousov blk_flush(struct dumperinfo *di) 669a527560SKonstantin Belousov { 679a527560SKonstantin Belousov int error; 689a527560SKonstantin Belousov 699a527560SKonstantin Belousov if (fragsz == 0) 709a527560SKonstantin Belousov return (0); 719a527560SKonstantin Belousov 729a527560SKonstantin Belousov error = dump_append(di, dump_va, 0, fragsz); 739a527560SKonstantin Belousov fragsz = 0; 749a527560SKonstantin Belousov return (error); 759a527560SKonstantin Belousov } 769a527560SKonstantin Belousov 779a527560SKonstantin Belousov static int 789a527560SKonstantin Belousov blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz) 799a527560SKonstantin Belousov { 809a527560SKonstantin Belousov size_t len; 819a527560SKonstantin Belousov int error, i, c; 829a527560SKonstantin Belousov u_int maxdumpsz; 839a527560SKonstantin Belousov 849a527560SKonstantin Belousov maxdumpsz = min(di->maxiosize, MAXDUMPPGS * PAGE_SIZE); 859a527560SKonstantin Belousov if (maxdumpsz == 0) /* seatbelt */ 869a527560SKonstantin Belousov maxdumpsz = PAGE_SIZE; 879a527560SKonstantin Belousov error = 0; 889a527560SKonstantin Belousov if ((sz % PAGE_SIZE) != 0) { 899a527560SKonstantin Belousov printf("size not page aligned\n"); 909a527560SKonstantin Belousov return (EINVAL); 919a527560SKonstantin Belousov } 929a527560SKonstantin Belousov if (ptr != NULL && pa != 0) { 939a527560SKonstantin Belousov printf("cant have both va and pa!\n"); 949a527560SKonstantin Belousov return (EINVAL); 959a527560SKonstantin Belousov } 969a527560SKonstantin Belousov if (pa != 0 && (((uintptr_t)ptr) % PAGE_SIZE) != 0) { 979a527560SKonstantin Belousov printf("address not page aligned\n"); 989a527560SKonstantin Belousov return (EINVAL); 999a527560SKonstantin Belousov } 1009a527560SKonstantin Belousov if (ptr != NULL) { 1019a527560SKonstantin Belousov /* If we're doing a virtual dump, flush any pre-existing pa pages */ 1029a527560SKonstantin Belousov error = blk_flush(di); 1039a527560SKonstantin Belousov if (error) 1049a527560SKonstantin Belousov return (error); 1059a527560SKonstantin Belousov } 1069a527560SKonstantin Belousov while (sz) { 1079a527560SKonstantin Belousov len = maxdumpsz - fragsz; 1089a527560SKonstantin Belousov if (len > sz) 1099a527560SKonstantin Belousov len = sz; 1109a527560SKonstantin Belousov 111ab4ed843SMitchell Horne dumpsys_pb_progress(len); 1129a527560SKonstantin Belousov wdog_kern_pat(WD_LASTVAL); 1139a527560SKonstantin Belousov 1149a527560SKonstantin Belousov if (ptr) { 1159a527560SKonstantin Belousov error = dump_append(di, ptr, 0, len); 1169a527560SKonstantin Belousov if (error) 1179a527560SKonstantin Belousov return (error); 1189a527560SKonstantin Belousov ptr += len; 1199a527560SKonstantin Belousov sz -= len; 1209a527560SKonstantin Belousov } else { 1219a527560SKonstantin Belousov for (i = 0; i < len; i += PAGE_SIZE) 1229a527560SKonstantin Belousov dump_va = pmap_kenter_temporary(pa + i, (i + fragsz) >> PAGE_SHIFT); 1239a527560SKonstantin Belousov fragsz += len; 1249a527560SKonstantin Belousov pa += len; 1259a527560SKonstantin Belousov sz -= len; 1269a527560SKonstantin Belousov if (fragsz == maxdumpsz) { 1279a527560SKonstantin Belousov error = blk_flush(di); 1289a527560SKonstantin Belousov if (error) 1299a527560SKonstantin Belousov return (error); 1309a527560SKonstantin Belousov } 1319a527560SKonstantin Belousov } 1329a527560SKonstantin Belousov 1339a527560SKonstantin Belousov /* Check for user abort. */ 1349a527560SKonstantin Belousov c = cncheckc(); 1359a527560SKonstantin Belousov if (c == 0x03) 1369a527560SKonstantin Belousov return (ECANCELED); 1379a527560SKonstantin Belousov if (c != -1) 1389a527560SKonstantin Belousov printf(" (CTRL-C to abort) "); 1399a527560SKonstantin Belousov } 1409a527560SKonstantin Belousov 1419a527560SKonstantin Belousov return (0); 1429a527560SKonstantin Belousov } 1439a527560SKonstantin Belousov 1449a527560SKonstantin Belousov /* A fake page table page, to avoid having to handle both 4K and 2M pages */ 1459a527560SKonstantin Belousov static pt_entry_t fakept[NPTEPG]; 1469a527560SKonstantin Belousov 1479a527560SKonstantin Belousov #ifdef PMAP_PAE_COMP 1481adebe3cSMitchell Horne #define cpu_minidumpsys cpu_minidumpsys_pae 1499a527560SKonstantin Belousov #define IdlePTD IdlePTD_pae 1509a527560SKonstantin Belousov #else 1511adebe3cSMitchell Horne #define cpu_minidumpsys cpu_minidumpsys_nopae 1529a527560SKonstantin Belousov #define IdlePTD IdlePTD_nopae 1539a527560SKonstantin Belousov #endif 1549a527560SKonstantin Belousov 1559a527560SKonstantin Belousov int 1561adebe3cSMitchell Horne cpu_minidumpsys(struct dumperinfo *di, const struct minidumpstate *state) 1579a527560SKonstantin Belousov { 1589a527560SKonstantin Belousov uint64_t dumpsize; 1599a527560SKonstantin Belousov uint32_t ptesize; 160681bd710SMitchell Horne vm_offset_t va, kva_end; 1619a527560SKonstantin Belousov int error; 1629a527560SKonstantin Belousov uint64_t pa; 163681bd710SMitchell Horne pd_entry_t *pd, pde; 164681bd710SMitchell Horne pt_entry_t *pt, pte; 165ab041f71SD Scott Phillips int j, k; 1669a527560SKonstantin Belousov struct minidumphdr mdhdr; 1671d2d1418SMitchell Horne struct msgbuf *mbp; 1689a527560SKonstantin Belousov 169681bd710SMitchell Horne /* Snapshot the KVA upper bound in case it grows. */ 170681bd710SMitchell Horne kva_end = kernel_vm_end; 171681bd710SMitchell Horne 172681bd710SMitchell Horne /* 173681bd710SMitchell Horne * Walk the kernel page table pages, setting the active entries in the 174681bd710SMitchell Horne * dump bitmap. 175681bd710SMitchell Horne * 176681bd710SMitchell Horne * NB: for a live dump, we may be racing with updates to the page 177681bd710SMitchell Horne * tables, so care must be taken to read each entry only once. 178681bd710SMitchell Horne */ 1799a527560SKonstantin Belousov ptesize = 0; 180681bd710SMitchell Horne for (va = KERNBASE; va < kva_end; va += NBPDR) { 1819a527560SKonstantin Belousov /* 1829a527560SKonstantin Belousov * We always write a page, even if it is zero. Each 1839a527560SKonstantin Belousov * page written corresponds to 2MB of space 1849a527560SKonstantin Belousov */ 1859a527560SKonstantin Belousov ptesize += PAGE_SIZE; 1869a527560SKonstantin Belousov pd = IdlePTD; /* always mapped! */ 1879a527560SKonstantin Belousov j = va >> PDRSHIFT; 188681bd710SMitchell Horne pde = pte_load(&pd[va >> PDRSHIFT]); 189681bd710SMitchell Horne if ((pde & (PG_PS | PG_V)) == (PG_PS | PG_V)) { 1909a527560SKonstantin Belousov /* This is an entire 2M page. */ 191681bd710SMitchell Horne pa = pde & PG_PS_FRAME; 1929a527560SKonstantin Belousov for (k = 0; k < NPTEPG; k++) { 19331991a5aSMitchell Horne if (vm_phys_is_dumpable(pa)) 1949a527560SKonstantin Belousov dump_add_page(pa); 1959a527560SKonstantin Belousov pa += PAGE_SIZE; 1969a527560SKonstantin Belousov } 1979a527560SKonstantin Belousov continue; 1989a527560SKonstantin Belousov } 199681bd710SMitchell Horne if ((pde & PG_V) == PG_V) { 2009a527560SKonstantin Belousov /* set bit for each valid page in this 2MB block */ 201681bd710SMitchell Horne pt = pmap_kenter_temporary(pde & PG_FRAME, 0); 2029a527560SKonstantin Belousov for (k = 0; k < NPTEPG; k++) { 203681bd710SMitchell Horne pte = pte_load(&pt[k]); 204681bd710SMitchell Horne if ((pte & PG_V) == PG_V) { 205681bd710SMitchell Horne pa = pte & PG_FRAME; 20631991a5aSMitchell Horne if (vm_phys_is_dumpable(pa)) 2079a527560SKonstantin Belousov dump_add_page(pa); 2089a527560SKonstantin Belousov } 2099a527560SKonstantin Belousov } 2109a527560SKonstantin Belousov } else { 2119a527560SKonstantin Belousov /* nothing, we're going to dump a null page */ 2129a527560SKonstantin Belousov } 2139a527560SKonstantin Belousov } 2149a527560SKonstantin Belousov 2159a527560SKonstantin Belousov /* Calculate dump size. */ 2161d2d1418SMitchell Horne mbp = state->msgbufp; 2179a527560SKonstantin Belousov dumpsize = ptesize; 2181d2d1418SMitchell Horne dumpsize += round_page(mbp->msg_size); 21900e66147SD Scott Phillips dumpsize += round_page(sizeof(dump_avail)); 220ab041f71SD Scott Phillips dumpsize += round_page(BITSET_SIZE(vm_page_dump_pages)); 221ab041f71SD Scott Phillips VM_PAGE_DUMP_FOREACH(pa) { 2229a527560SKonstantin Belousov /* Clear out undumpable pages now if needed */ 22331991a5aSMitchell Horne if (vm_phys_is_dumpable(pa)) { 2249a527560SKonstantin Belousov dumpsize += PAGE_SIZE; 2259a527560SKonstantin Belousov } else { 2269a527560SKonstantin Belousov dump_drop_page(pa); 2279a527560SKonstantin Belousov } 2289a527560SKonstantin Belousov } 2299a527560SKonstantin Belousov dumpsize += PAGE_SIZE; 2309a527560SKonstantin Belousov 231ab4ed843SMitchell Horne dumpsys_pb_init(dumpsize); 2329a527560SKonstantin Belousov 2339a527560SKonstantin Belousov /* Initialize mdhdr */ 2349a527560SKonstantin Belousov bzero(&mdhdr, sizeof(mdhdr)); 2359a527560SKonstantin Belousov strcpy(mdhdr.magic, MINIDUMP_MAGIC); 2369a527560SKonstantin Belousov mdhdr.version = MINIDUMP_VERSION; 2371d2d1418SMitchell Horne mdhdr.msgbufsize = mbp->msg_size; 238ab041f71SD Scott Phillips mdhdr.bitmapsize = round_page(BITSET_SIZE(vm_page_dump_pages)); 2399a527560SKonstantin Belousov mdhdr.ptesize = ptesize; 2409a527560SKonstantin Belousov mdhdr.kernbase = KERNBASE; 2419a527560SKonstantin Belousov mdhdr.paemode = pae_mode; 24200e66147SD Scott Phillips mdhdr.dumpavailsize = round_page(sizeof(dump_avail)); 2439a527560SKonstantin Belousov 2449a527560SKonstantin Belousov dump_init_header(di, &kdh, KERNELDUMPMAGIC, KERNELDUMP_I386_VERSION, 2459a527560SKonstantin Belousov dumpsize); 2469a527560SKonstantin Belousov 2479a527560SKonstantin Belousov error = dump_start(di, &kdh); 2489a527560SKonstantin Belousov if (error != 0) 2499a527560SKonstantin Belousov goto fail; 2509a527560SKonstantin Belousov 2519a527560SKonstantin Belousov printf("Physical memory: %ju MB\n", ptoa((uintmax_t)physmem) / 1048576); 2529a527560SKonstantin Belousov printf("Dumping %llu MB:", (long long)dumpsize >> 20); 2539a527560SKonstantin Belousov 2549a527560SKonstantin Belousov /* Dump my header */ 2559a527560SKonstantin Belousov bzero(&fakept, sizeof(fakept)); 2569a527560SKonstantin Belousov bcopy(&mdhdr, &fakept, sizeof(mdhdr)); 2579a527560SKonstantin Belousov error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE); 2589a527560SKonstantin Belousov if (error) 2599a527560SKonstantin Belousov goto fail; 2609a527560SKonstantin Belousov 2619a527560SKonstantin Belousov /* Dump msgbuf up front */ 2621d2d1418SMitchell Horne error = blk_write(di, (char *)mbp->msg_ptr, 0, 2631d2d1418SMitchell Horne round_page(mbp->msg_size)); 2649a527560SKonstantin Belousov if (error) 2659a527560SKonstantin Belousov goto fail; 2669a527560SKonstantin Belousov 26700e66147SD Scott Phillips /* Dump dump_avail */ 26800e66147SD Scott Phillips _Static_assert(sizeof(dump_avail) <= sizeof(fakept), 26900e66147SD Scott Phillips "Large dump_avail not handled"); 27000e66147SD Scott Phillips bzero(fakept, sizeof(fakept)); 27100e66147SD Scott Phillips memcpy(fakept, dump_avail, sizeof(dump_avail)); 27200e66147SD Scott Phillips error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE); 27300e66147SD Scott Phillips if (error) 27400e66147SD Scott Phillips goto fail; 27500e66147SD Scott Phillips 2769a527560SKonstantin Belousov /* Dump bitmap */ 277ab041f71SD Scott Phillips error = blk_write(di, (char *)vm_page_dump, 0, 278ab041f71SD Scott Phillips round_page(BITSET_SIZE(vm_page_dump_pages))); 2799a527560SKonstantin Belousov if (error) 2809a527560SKonstantin Belousov goto fail; 2819a527560SKonstantin Belousov 2829a527560SKonstantin Belousov /* Dump kernel page table pages */ 283681bd710SMitchell Horne for (va = KERNBASE; va < kva_end; va += NBPDR) { 2849a527560SKonstantin Belousov /* We always write a page, even if it is zero */ 2859a527560SKonstantin Belousov pd = IdlePTD; /* always mapped! */ 286681bd710SMitchell Horne pde = pte_load(&pd[va >> PDRSHIFT]); 287681bd710SMitchell Horne if ((pde & (PG_PS | PG_V)) == (PG_PS | PG_V)) { 2889a527560SKonstantin Belousov /* This is a single 2M block. Generate a fake PTP */ 289681bd710SMitchell Horne pa = pde & PG_PS_FRAME; 2909a527560SKonstantin Belousov for (k = 0; k < NPTEPG; k++) { 2919a527560SKonstantin Belousov fakept[k] = (pa + (k * PAGE_SIZE)) | PG_V | PG_RW | PG_A | PG_M; 2929a527560SKonstantin Belousov } 2939a527560SKonstantin Belousov error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE); 2949a527560SKonstantin Belousov if (error) 2959a527560SKonstantin Belousov goto fail; 2969a527560SKonstantin Belousov /* flush, in case we reuse fakept in the same block */ 2979a527560SKonstantin Belousov error = blk_flush(di); 2989a527560SKonstantin Belousov if (error) 2999a527560SKonstantin Belousov goto fail; 3009a527560SKonstantin Belousov continue; 3019a527560SKonstantin Belousov } 302681bd710SMitchell Horne if ((pde & PG_V) == PG_V) { 303681bd710SMitchell Horne pa = pde & PG_FRAME; 3049a527560SKonstantin Belousov error = blk_write(di, 0, pa, PAGE_SIZE); 3059a527560SKonstantin Belousov if (error) 3069a527560SKonstantin Belousov goto fail; 3079a527560SKonstantin Belousov } else { 3089a527560SKonstantin Belousov bzero(fakept, sizeof(fakept)); 3099a527560SKonstantin Belousov error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE); 3109a527560SKonstantin Belousov if (error) 3119a527560SKonstantin Belousov goto fail; 3129a527560SKonstantin Belousov /* flush, in case we reuse fakept in the same block */ 3139a527560SKonstantin Belousov error = blk_flush(di); 3149a527560SKonstantin Belousov if (error) 3159a527560SKonstantin Belousov goto fail; 3169a527560SKonstantin Belousov } 3179a527560SKonstantin Belousov } 3189a527560SKonstantin Belousov 3199a527560SKonstantin Belousov /* Dump memory chunks */ 320ab041f71SD Scott Phillips VM_PAGE_DUMP_FOREACH(pa) { 3219a527560SKonstantin Belousov error = blk_write(di, 0, pa, PAGE_SIZE); 3229a527560SKonstantin Belousov if (error) 3239a527560SKonstantin Belousov goto fail; 3249a527560SKonstantin Belousov } 3259a527560SKonstantin Belousov 3269a527560SKonstantin Belousov error = blk_flush(di); 3279a527560SKonstantin Belousov if (error) 3289a527560SKonstantin Belousov goto fail; 3299a527560SKonstantin Belousov 3309a527560SKonstantin Belousov error = dump_finish(di, &kdh); 3319a527560SKonstantin Belousov if (error != 0) 3329a527560SKonstantin Belousov goto fail; 3339a527560SKonstantin Belousov 3349a527560SKonstantin Belousov printf("\nDump complete\n"); 3359a527560SKonstantin Belousov return (0); 3369a527560SKonstantin Belousov 3379a527560SKonstantin Belousov fail: 3389a527560SKonstantin Belousov if (error < 0) 3399a527560SKonstantin Belousov error = -error; 3409a527560SKonstantin Belousov 3419a527560SKonstantin Belousov if (error == ECANCELED) 3429a527560SKonstantin Belousov printf("\nDump aborted\n"); 34357f317e6SNavdeep Parhar else if (error == E2BIG || error == ENOSPC) { 34457f317e6SNavdeep Parhar printf("\nDump failed. Partition too small (about %lluMB were " 34557f317e6SNavdeep Parhar "needed this time).\n", (long long)dumpsize >> 20); 34657f317e6SNavdeep Parhar } else 3479a527560SKonstantin Belousov printf("\n** DUMP FAILED (ERROR %d) **\n", error); 3489a527560SKonstantin Belousov return (error); 3499a527560SKonstantin Belousov } 350