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