minidump_machdep.c revision 330897
1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2006 Peter Wemm 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD: stable/11/sys/i386/i386/minidump_machdep.c 330897 2018-03-14 03:19:51Z eadler $"); 31 32#include "opt_watchdog.h" 33 34#include <sys/param.h> 35#include <sys/systm.h> 36#include <sys/conf.h> 37#include <sys/cons.h> 38#include <sys/kernel.h> 39#include <sys/kerneldump.h> 40#include <sys/msgbuf.h> 41#include <sys/watchdog.h> 42#include <vm/vm.h> 43#include <vm/pmap.h> 44#include <machine/atomic.h> 45#include <machine/elf.h> 46#include <machine/md_var.h> 47#include <machine/vmparam.h> 48#include <machine/minidump.h> 49 50CTASSERT(sizeof(struct kerneldumpheader) == 512); 51 52/* 53 * Don't touch the first SIZEOF_METADATA bytes on the dump device. This 54 * is to protect us from metadata and to protect metadata from us. 55 */ 56#define SIZEOF_METADATA (64*1024) 57 58#define MD_ALIGN(x) (((off_t)(x) + PAGE_MASK) & ~PAGE_MASK) 59#define DEV_ALIGN(x) roundup2((off_t)(x), DEV_BSIZE) 60 61uint32_t *vm_page_dump; 62int vm_page_dump_size; 63 64static struct kerneldumpheader kdh; 65static off_t dumplo; 66 67/* Handle chunked writes. */ 68static size_t fragsz; 69static void *dump_va; 70static uint64_t counter, progress; 71 72CTASSERT(sizeof(*vm_page_dump) == 4); 73 74 75static int 76is_dumpable(vm_paddr_t pa) 77{ 78 int i; 79 80 for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) { 81 if (pa >= dump_avail[i] && pa < dump_avail[i + 1]) 82 return (1); 83 } 84 return (0); 85} 86 87#define PG2MB(pgs) (((pgs) + (1 << 8) - 1) >> 8) 88 89static int 90blk_flush(struct dumperinfo *di) 91{ 92 int error; 93 94 if (fragsz == 0) 95 return (0); 96 97 error = dump_write(di, dump_va, 0, dumplo, fragsz); 98 dumplo += fragsz; 99 fragsz = 0; 100 return (error); 101} 102 103static int 104blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz) 105{ 106 size_t len; 107 int error, i, c; 108 u_int maxdumpsz; 109 110 maxdumpsz = min(di->maxiosize, MAXDUMPPGS * PAGE_SIZE); 111 if (maxdumpsz == 0) /* seatbelt */ 112 maxdumpsz = PAGE_SIZE; 113 error = 0; 114 if ((sz % PAGE_SIZE) != 0) { 115 printf("size not page aligned\n"); 116 return (EINVAL); 117 } 118 if (ptr != NULL && pa != 0) { 119 printf("cant have both va and pa!\n"); 120 return (EINVAL); 121 } 122 if (pa != 0 && (((uintptr_t)ptr) % PAGE_SIZE) != 0) { 123 printf("address not page aligned\n"); 124 return (EINVAL); 125 } 126 if (ptr != NULL) { 127 /* If we're doing a virtual dump, flush any pre-existing pa pages */ 128 error = blk_flush(di); 129 if (error) 130 return (error); 131 } 132 while (sz) { 133 len = maxdumpsz - fragsz; 134 if (len > sz) 135 len = sz; 136 counter += len; 137 progress -= len; 138 if (counter >> 24) { 139 printf(" %lld", PG2MB(progress >> PAGE_SHIFT)); 140 counter &= (1<<24) - 1; 141 } 142 143 wdog_kern_pat(WD_LASTVAL); 144 145 if (ptr) { 146 error = dump_write(di, ptr, 0, dumplo, len); 147 if (error) 148 return (error); 149 dumplo += len; 150 ptr += len; 151 sz -= len; 152 } else { 153 for (i = 0; i < len; i += PAGE_SIZE) 154 dump_va = pmap_kenter_temporary(pa + i, (i + fragsz) >> PAGE_SHIFT); 155 fragsz += len; 156 pa += len; 157 sz -= len; 158 if (fragsz == maxdumpsz) { 159 error = blk_flush(di); 160 if (error) 161 return (error); 162 } 163 } 164 165 /* Check for user abort. */ 166 c = cncheckc(); 167 if (c == 0x03) 168 return (ECANCELED); 169 if (c != -1) 170 printf(" (CTRL-C to abort) "); 171 } 172 173 return (0); 174} 175 176/* A fake page table page, to avoid having to handle both 4K and 2M pages */ 177static pt_entry_t fakept[NPTEPG]; 178 179int 180minidumpsys(struct dumperinfo *di) 181{ 182 uint64_t dumpsize; 183 uint32_t ptesize; 184 vm_offset_t va; 185 int error; 186 uint32_t bits; 187 uint64_t pa; 188 pd_entry_t *pd; 189 pt_entry_t *pt; 190 int i, j, k, bit; 191 struct minidumphdr mdhdr; 192 193 counter = 0; 194 /* Walk page table pages, set bits in vm_page_dump */ 195 ptesize = 0; 196 for (va = KERNBASE; va < kernel_vm_end; va += NBPDR) { 197 /* 198 * We always write a page, even if it is zero. Each 199 * page written corresponds to 2MB of space 200 */ 201 ptesize += PAGE_SIZE; 202 pd = (pd_entry_t *)((uintptr_t)IdlePTD + KERNBASE); /* always mapped! */ 203 j = va >> PDRSHIFT; 204 if ((pd[j] & (PG_PS | PG_V)) == (PG_PS | PG_V)) { 205 /* This is an entire 2M page. */ 206 pa = pd[j] & PG_PS_FRAME; 207 for (k = 0; k < NPTEPG; k++) { 208 if (is_dumpable(pa)) 209 dump_add_page(pa); 210 pa += PAGE_SIZE; 211 } 212 continue; 213 } 214 if ((pd[j] & PG_V) == PG_V) { 215 /* set bit for each valid page in this 2MB block */ 216 pt = pmap_kenter_temporary(pd[j] & PG_FRAME, 0); 217 for (k = 0; k < NPTEPG; k++) { 218 if ((pt[k] & PG_V) == PG_V) { 219 pa = pt[k] & PG_FRAME; 220 if (is_dumpable(pa)) 221 dump_add_page(pa); 222 } 223 } 224 } else { 225 /* nothing, we're going to dump a null page */ 226 } 227 } 228 229 /* Calculate dump size. */ 230 dumpsize = ptesize; 231 dumpsize += round_page(msgbufp->msg_size); 232 dumpsize += round_page(vm_page_dump_size); 233 for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { 234 bits = vm_page_dump[i]; 235 while (bits) { 236 bit = bsfl(bits); 237 pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + bit) * PAGE_SIZE; 238 /* Clear out undumpable pages now if needed */ 239 if (is_dumpable(pa)) { 240 dumpsize += PAGE_SIZE; 241 } else { 242 dump_drop_page(pa); 243 } 244 bits &= ~(1ul << bit); 245 } 246 } 247 dumpsize += PAGE_SIZE; 248 249 /* Determine dump offset on device. */ 250 if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { 251 error = ENOSPC; 252 goto fail; 253 } 254 dumplo = di->mediaoffset + di->mediasize - dumpsize; 255 dumplo -= sizeof(kdh) * 2; 256 progress = dumpsize; 257 258 /* Initialize mdhdr */ 259 bzero(&mdhdr, sizeof(mdhdr)); 260 strcpy(mdhdr.magic, MINIDUMP_MAGIC); 261 mdhdr.version = MINIDUMP_VERSION; 262 mdhdr.msgbufsize = msgbufp->msg_size; 263 mdhdr.bitmapsize = vm_page_dump_size; 264 mdhdr.ptesize = ptesize; 265 mdhdr.kernbase = KERNBASE; 266#if defined(PAE) || defined(PAE_TABLES) 267 mdhdr.paemode = 1; 268#endif 269 270 mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_I386_VERSION, dumpsize, di->blocksize); 271 272 printf("Physical memory: %ju MB\n", ptoa((uintmax_t)physmem) / 1048576); 273 printf("Dumping %llu MB:", (long long)dumpsize >> 20); 274 275 /* Dump leader */ 276 error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); 277 if (error) 278 goto fail; 279 dumplo += sizeof(kdh); 280 281 /* Dump my header */ 282 bzero(&fakept, sizeof(fakept)); 283 bcopy(&mdhdr, &fakept, sizeof(mdhdr)); 284 error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE); 285 if (error) 286 goto fail; 287 288 /* Dump msgbuf up front */ 289 error = blk_write(di, (char *)msgbufp->msg_ptr, 0, round_page(msgbufp->msg_size)); 290 if (error) 291 goto fail; 292 293 /* Dump bitmap */ 294 error = blk_write(di, (char *)vm_page_dump, 0, round_page(vm_page_dump_size)); 295 if (error) 296 goto fail; 297 298 /* Dump kernel page table pages */ 299 for (va = KERNBASE; va < kernel_vm_end; va += NBPDR) { 300 /* We always write a page, even if it is zero */ 301 pd = (pd_entry_t *)((uintptr_t)IdlePTD + KERNBASE); /* always mapped! */ 302 j = va >> PDRSHIFT; 303 if ((pd[j] & (PG_PS | PG_V)) == (PG_PS | PG_V)) { 304 /* This is a single 2M block. Generate a fake PTP */ 305 pa = pd[j] & PG_PS_FRAME; 306 for (k = 0; k < NPTEPG; k++) { 307 fakept[k] = (pa + (k * PAGE_SIZE)) | PG_V | PG_RW | PG_A | PG_M; 308 } 309 error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE); 310 if (error) 311 goto fail; 312 /* flush, in case we reuse fakept in the same block */ 313 error = blk_flush(di); 314 if (error) 315 goto fail; 316 continue; 317 } 318 if ((pd[j] & PG_V) == PG_V) { 319 pa = pd[j] & PG_FRAME; 320 error = blk_write(di, 0, pa, PAGE_SIZE); 321 if (error) 322 goto fail; 323 } else { 324 bzero(fakept, sizeof(fakept)); 325 error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE); 326 if (error) 327 goto fail; 328 /* flush, in case we reuse fakept in the same block */ 329 error = blk_flush(di); 330 if (error) 331 goto fail; 332 } 333 } 334 335 /* Dump memory chunks */ 336 /* XXX cluster it up and use blk_dump() */ 337 for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { 338 bits = vm_page_dump[i]; 339 while (bits) { 340 bit = bsfl(bits); 341 pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + bit) * PAGE_SIZE; 342 error = blk_write(di, 0, pa, PAGE_SIZE); 343 if (error) 344 goto fail; 345 bits &= ~(1ul << bit); 346 } 347 } 348 349 error = blk_flush(di); 350 if (error) 351 goto fail; 352 353 /* Dump trailer */ 354 error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); 355 if (error) 356 goto fail; 357 dumplo += sizeof(kdh); 358 359 /* Signal completion, signoff and exit stage left. */ 360 dump_write(di, NULL, 0, 0, 0); 361 printf("\nDump complete\n"); 362 return (0); 363 364 fail: 365 if (error < 0) 366 error = -error; 367 368 if (error == ECANCELED) 369 printf("\nDump aborted\n"); 370 else if (error == ENOSPC) 371 printf("\nDump failed. Partition too small.\n"); 372 else 373 printf("\n** DUMP FAILED (ERROR %d) **\n", error); 374 return (error); 375} 376 377void 378dump_add_page(vm_paddr_t pa) 379{ 380 int idx, bit; 381 382 pa >>= PAGE_SHIFT; 383 idx = pa >> 5; /* 2^5 = 32 */ 384 bit = pa & 31; 385 atomic_set_int(&vm_page_dump[idx], 1ul << bit); 386} 387 388void 389dump_drop_page(vm_paddr_t pa) 390{ 391 int idx, bit; 392 393 pa >>= PAGE_SHIFT; 394 idx = pa >> 5; /* 2^5 = 32 */ 395 bit = pa & 31; 396 atomic_clear_int(&vm_page_dump[idx], 1ul << bit); 397} 398 399