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