1/*- 2 * Copyright (c) 2008 Semihalf, Grzegorz Bernacki 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 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. 14 * 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. 25 * 26 * from: FreeBSD: src/sys/i386/i386/minidump_machdep.c,v 1.6 2008/08/17 23:27:27 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD$"); 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#ifdef SW_WATCHDOG 42#include <sys/watchdog.h> 43#endif 44#include <vm/vm.h> 45#include <vm/pmap.h> 46#include <machine/pmap.h> 47#include <machine/atomic.h> 48#include <machine/elf.h> 49#include <machine/md_var.h> 50#include <machine/vmparam.h> 51#include <machine/minidump.h> 52#include <machine/cpufunc.h> 53 54CTASSERT(sizeof(struct kerneldumpheader) == 512); 55 56/* 57 * Don't touch the first SIZEOF_METADATA bytes on the dump device. This 58 * is to protect us from metadata and to protect metadata from us. 59 */ 60#define SIZEOF_METADATA (64*1024) 61 62uint32_t *vm_page_dump; 63int vm_page_dump_size; 64 65static struct kerneldumpheader kdh; 66static off_t dumplo; 67 68/* Handle chunked writes. */ 69static size_t fragsz, offset; 70static void *dump_va; 71static uint64_t counter, progress; 72 73CTASSERT(sizeof(*vm_page_dump) == 4); 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, (char*)dump_va + offset, 0, dumplo, fragsz - offset); 98 dumplo += (fragsz - offset); 99 fragsz = 0; 100 offset = 0; 101 return (error); 102} 103 104static int 105blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz) 106{ 107 size_t len; 108 int error, i, c; 109 u_int maxdumpsz; 110 111 maxdumpsz = di->maxiosize; 112 113 if (maxdumpsz == 0) /* seatbelt */ 114 maxdumpsz = PAGE_SIZE; 115 116 error = 0; 117 118 if (ptr != NULL && pa != 0) { 119 printf("cant have both va and pa!\n"); 120 return (EINVAL); 121 } 122 123 if (ptr != NULL) { 124 /* If we're doing a virtual dump, flush any pre-existing pa pages */ 125 error = blk_flush(di); 126 if (error) 127 return (error); 128 } 129 130 while (sz) { 131 if (fragsz == 0) { 132 offset = pa & PAGE_MASK; 133 fragsz += offset; 134 } 135 len = maxdumpsz - fragsz; 136 if (len > sz) 137 len = sz; 138 counter += len; 139 progress -= len; 140 141 if (counter >> 22) { 142 printf(" %lld", PG2MB(progress >> PAGE_SHIFT)); 143 counter &= (1<<22) - 1; 144 } 145 146#ifdef SW_WATCHDOG 147 wdog_kern_pat(WD_LASTVAL); 148#endif 149 if (ptr) { 150 error = dump_write(di, ptr, 0, dumplo, len); 151 if (error) 152 return (error); 153 dumplo += len; 154 ptr += len; 155 sz -= len; 156 } else { 157 for (i = 0; i < len; i += PAGE_SIZE) 158 dump_va = pmap_kenter_temp(pa + i, 159 (i + fragsz) >> PAGE_SHIFT); 160 fragsz += len; 161 pa += len; 162 sz -= len; 163 if (fragsz == maxdumpsz) { 164 error = blk_flush(di); 165 if (error) 166 return (error); 167 } 168 } 169 170 /* Check for user abort. */ 171 c = cncheckc(); 172 if (c == 0x03) 173 return (ECANCELED); 174 if (c != -1) 175 printf(" (CTRL-C to abort) "); 176 } 177 178 return (0); 179} 180 181static int 182blk_write_cont(struct dumperinfo *di, vm_paddr_t pa, size_t sz) 183{ 184 int error; 185 186 error = blk_write(di, 0, pa, sz); 187 if (error) 188 return (error); 189 190 error = blk_flush(di); 191 if (error) 192 return (error); 193 194 return (0); 195} 196 197/* A fake page table page, to avoid having to handle both 4K and 2M pages */ 198static pt_entry_t fakept[NPTEPG]; 199 200void 201minidumpsys(struct dumperinfo *di) 202{ 203 struct minidumphdr mdhdr; 204 uint64_t dumpsize; 205 uint32_t ptesize; 206 uint32_t bits; 207 uint32_t pa, prev_pa = 0, count = 0; 208 vm_offset_t va; 209 pd_entry_t *pdp; 210 pt_entry_t *pt, *ptp; 211 int i, k, bit, error; 212 char *addr; 213 214 /* Flush cache */ 215 cpu_idcache_wbinv_all(); 216 cpu_l2cache_wbinv_all(); 217 218 counter = 0; 219 /* Walk page table pages, set bits in vm_page_dump */ 220 ptesize = 0; 221 for (va = KERNBASE; va < kernel_vm_end; va += NBPDR) { 222 /* 223 * We always write a page, even if it is zero. Each 224 * page written corresponds to 2MB of space 225 */ 226 ptesize += L2_TABLE_SIZE_REAL; 227 pmap_get_pde_pte(pmap_kernel(), va, &pdp, &ptp); 228 if (pmap_pde_v(pdp) && pmap_pde_section(pdp)) { 229 /* This is a section mapping 1M page. */ 230 pa = (*pdp & L1_S_ADDR_MASK) | (va & ~L1_S_ADDR_MASK); 231 for (k = 0; k < (L1_S_SIZE / PAGE_SIZE); k++) { 232 if (is_dumpable(pa)) 233 dump_add_page(pa); 234 pa += PAGE_SIZE; 235 } 236 continue; 237 } 238 if (pmap_pde_v(pdp) && pmap_pde_page(pdp)) { 239 /* Set bit for each valid page in this 1MB block */ 240 addr = pmap_kenter_temp(*pdp & L1_C_ADDR_MASK, 0); 241 pt = (pt_entry_t*)(addr + 242 (((uint32_t)*pdp & L1_C_ADDR_MASK) & PAGE_MASK)); 243 for (k = 0; k < 256; k++) { 244 if ((pt[k] & L2_TYPE_MASK) == L2_TYPE_L) { 245 pa = (pt[k] & L2_L_FRAME) | 246 (va & L2_L_OFFSET); 247 for (i = 0; i < 16; i++) { 248 if (is_dumpable(pa)) 249 dump_add_page(pa); 250 k++; 251 pa += PAGE_SIZE; 252 } 253 } else if ((pt[k] & L2_TYPE_MASK) == L2_TYPE_S) { 254 pa = (pt[k] & L2_S_FRAME) | 255 (va & L2_S_OFFSET); 256 if (is_dumpable(pa)) 257 dump_add_page(pa); 258 } 259 } 260 } else { 261 /* Nothing, we're going to dump a null page */ 262 } 263 } 264 265 /* Calculate dump size. */ 266 dumpsize = ptesize; 267 dumpsize += round_page(msgbufp->msg_size); 268 dumpsize += round_page(vm_page_dump_size); 269 270 for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { 271 bits = vm_page_dump[i]; 272 while (bits) { 273 bit = ffs(bits) - 1; 274 pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + 275 bit) * PAGE_SIZE; 276 /* Clear out undumpable pages now if needed */ 277 if (is_dumpable(pa)) 278 dumpsize += PAGE_SIZE; 279 else 280 dump_drop_page(pa); 281 bits &= ~(1ul << bit); 282 } 283 } 284 285 dumpsize += PAGE_SIZE; 286 287 /* Determine dump offset on device. */ 288 if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { 289 error = ENOSPC; 290 goto fail; 291 } 292 293 dumplo = di->mediaoffset + di->mediasize - dumpsize; 294 dumplo -= sizeof(kdh) * 2; 295 progress = dumpsize; 296 297 /* Initialize mdhdr */ 298 bzero(&mdhdr, sizeof(mdhdr)); 299 strcpy(mdhdr.magic, MINIDUMP_MAGIC); 300 mdhdr.version = MINIDUMP_VERSION; 301 mdhdr.msgbufsize = msgbufp->msg_size; 302 mdhdr.bitmapsize = vm_page_dump_size; 303 mdhdr.ptesize = ptesize; 304 mdhdr.kernbase = KERNBASE; 305 306 mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_ARM_VERSION, dumpsize, 307 di->blocksize); 308 309 printf("Physical memory: %u MB\n", ptoa((uintmax_t)physmem) / 1048576); 310 printf("Dumping %llu MB:", (long long)dumpsize >> 20); 311 312 /* Dump leader */ 313 error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); 314 if (error) 315 goto fail; 316 dumplo += sizeof(kdh); 317 318 /* Dump my header */ 319 bzero(&fakept, sizeof(fakept)); 320 bcopy(&mdhdr, &fakept, sizeof(mdhdr)); 321 error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE); 322 if (error) 323 goto fail; 324 325 /* Dump msgbuf up front */ 326 error = blk_write(di, (char *)msgbufp->msg_ptr, 0, round_page(msgbufp->msg_size)); 327 if (error) 328 goto fail; 329 330 /* Dump bitmap */ 331 error = blk_write(di, (char *)vm_page_dump, 0, 332 round_page(vm_page_dump_size)); 333 if (error) 334 goto fail; 335 336 /* Dump kernel page table pages */ 337 for (va = KERNBASE; va < kernel_vm_end; va += NBPDR) { 338 /* We always write a page, even if it is zero */ 339 pmap_get_pde_pte(pmap_kernel(), va, &pdp, &ptp); 340 341 if (pmap_pde_v(pdp) && pmap_pde_section(pdp)) { 342 if (count) { 343 error = blk_write_cont(di, prev_pa, 344 count * L2_TABLE_SIZE_REAL); 345 if (error) 346 goto fail; 347 count = 0; 348 prev_pa = 0; 349 } 350 /* This is a single 2M block. Generate a fake PTP */ 351 pa = (*pdp & L1_S_ADDR_MASK) | (va & ~L1_S_ADDR_MASK); 352 for (k = 0; k < (L1_S_SIZE / PAGE_SIZE); k++) { 353 fakept[k] = L2_S_PROTO | (pa + (k * PAGE_SIZE)) | 354 L2_S_PROT(PTE_KERNEL, 355 VM_PROT_READ | VM_PROT_WRITE); 356 } 357 error = blk_write(di, (char *)&fakept, 0, 358 L2_TABLE_SIZE_REAL); 359 if (error) 360 goto fail; 361 /* Flush, in case we reuse fakept in the same block */ 362 error = blk_flush(di); 363 if (error) 364 goto fail; 365 continue; 366 } 367 if (pmap_pde_v(pdp) && pmap_pde_page(pdp)) { 368 pa = *pdp & L1_C_ADDR_MASK; 369 if (!count) { 370 prev_pa = pa; 371 count++; 372 } 373 else { 374 if (pa == (prev_pa + count * L2_TABLE_SIZE_REAL)) 375 count++; 376 else { 377 error = blk_write_cont(di, prev_pa, 378 count * L2_TABLE_SIZE_REAL); 379 if (error) 380 goto fail; 381 count = 1; 382 prev_pa = pa; 383 } 384 } 385 } else { 386 if (count) { 387 error = blk_write_cont(di, prev_pa, 388 count * L2_TABLE_SIZE_REAL); 389 if (error) 390 goto fail; 391 count = 0; 392 prev_pa = 0; 393 } 394 bzero(fakept, sizeof(fakept)); 395 error = blk_write(di, (char *)&fakept, 0, 396 L2_TABLE_SIZE_REAL); 397 if (error) 398 goto fail; 399 /* Flush, in case we reuse fakept in the same block */ 400 error = blk_flush(di); 401 if (error) 402 goto fail; 403 } 404 } 405 406 if (count) { 407 error = blk_write_cont(di, prev_pa, count * L2_TABLE_SIZE_REAL); 408 if (error) 409 goto fail; 410 count = 0; 411 prev_pa = 0; 412 } 413 414 /* Dump memory chunks */ 415 for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { 416 bits = vm_page_dump[i]; 417 while (bits) { 418 bit = ffs(bits) - 1; 419 pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + 420 bit) * PAGE_SIZE; 421 if (!count) { 422 prev_pa = pa; 423 count++; 424 } else { 425 if (pa == (prev_pa + count * PAGE_SIZE)) 426 count++; 427 else { 428 error = blk_write_cont(di, prev_pa, 429 count * PAGE_SIZE); 430 if (error) 431 goto fail; 432 count = 1; 433 prev_pa = pa; 434 } 435 } 436 bits &= ~(1ul << bit); 437 } 438 } 439 if (count) { 440 error = blk_write_cont(di, prev_pa, count * PAGE_SIZE); 441 if (error) 442 goto fail; 443 count = 0; 444 prev_pa = 0; 445 } 446 447 /* Dump trailer */ 448 error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); 449 if (error) 450 goto fail; 451 dumplo += sizeof(kdh); 452 453 /* Signal completion, signoff and exit stage left. */ 454 dump_write(di, NULL, 0, 0, 0); 455 printf("\nDump complete\n"); 456 return; 457 458fail: 459 if (error < 0) 460 error = -error; 461 462 if (error == ECANCELED) 463 printf("\nDump aborted\n"); 464 else if (error == ENOSPC) 465 printf("\nDump failed. Partition too small.\n"); 466 else 467 printf("\n** DUMP FAILED (ERROR %d) **\n", error); 468} 469 470void 471dump_add_page(vm_paddr_t pa) 472{ 473 int idx, bit; 474 475 pa >>= PAGE_SHIFT; 476 idx = pa >> 5; /* 2^5 = 32 */ 477 bit = pa & 31; 478 atomic_set_int(&vm_page_dump[idx], 1ul << bit); 479} 480 481void 482dump_drop_page(vm_paddr_t pa) 483{ 484 int idx, bit; 485 486 pa >>= PAGE_SHIFT; 487 idx = pa >> 5; /* 2^5 = 32 */ 488 bit = pa & 31; 489 atomic_clear_int(&vm_page_dump[idx], 1ul << bit); 490} 491