1 /*- 2 * Copyright (c) 2019 Leandro Lupori 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 * $FreeBSD$ 26 */ 27 28 #include <sys/types.h> 29 #include <sys/param.h> 30 31 #include <sys/cons.h> 32 #include <sys/kerneldump.h> 33 #include <sys/msgbuf.h> 34 #include <sys/proc.h> 35 #include <sys/sysctl.h> 36 37 #include <vm/vm.h> 38 #include <vm/vm_param.h> 39 #include <vm/vm_page.h> 40 #include <vm/vm_phys.h> 41 #include <vm/pmap.h> 42 43 #include <machine/atomic.h> 44 #include <machine/dump.h> 45 #include <machine/md_var.h> 46 #include <machine/minidump.h> 47 48 /* 49 * bit to physical address 50 * 51 * bm - bitmap 52 * i - bitmap entry index 53 * bit - bit number 54 */ 55 #define BTOP(bm, i, bit) \ 56 (((uint64_t)(i) * sizeof(*(bm)) * NBBY + (bit)) * PAGE_SIZE) 57 58 /* Debugging stuff */ 59 #define MINIDUMP_DEBUG 0 60 #if MINIDUMP_DEBUG 61 #define dprintf(fmt, ...) printf(fmt, ## __VA_ARGS__) 62 #define DBG(...) __VA_ARGS__ 63 static size_t total, dumptotal; 64 static void dump_total(const char *id, size_t sz); 65 #else 66 #define dprintf(fmt, ...) 67 #define DBG(...) 68 #define dump_total(...) 69 #endif 70 71 72 extern vm_offset_t __startkernel, __endkernel; 73 74 int vm_page_dump_size; 75 uint64_t *vm_page_dump; 76 77 static int dump_retry_count = 5; 78 SYSCTL_INT(_machdep, OID_AUTO, dump_retry_count, CTLFLAG_RWTUN, 79 &dump_retry_count, 0, 80 "Number of times dump has to retry before bailing out"); 81 82 static struct kerneldumpheader kdh; 83 static char pgbuf[PAGE_SIZE]; 84 85 static struct { 86 int min_per; 87 int max_per; 88 int visited; 89 } progress_track[10] = { 90 { 0, 10, 0}, 91 { 10, 20, 0}, 92 { 20, 30, 0}, 93 { 30, 40, 0}, 94 { 40, 50, 0}, 95 { 50, 60, 0}, 96 { 60, 70, 0}, 97 { 70, 80, 0}, 98 { 80, 90, 0}, 99 { 90, 100, 0} 100 }; 101 102 static size_t counter, dumpsize, progress; 103 104 /* Handle chunked writes. */ 105 static size_t fragsz; 106 107 void 108 dump_add_page(vm_paddr_t pa) 109 { 110 int idx, bit; 111 112 pa >>= PAGE_SHIFT; 113 idx = pa >> 6; /* 2^6 = 64 */ 114 bit = pa & 63; 115 atomic_set_long(&vm_page_dump[idx], 1ul << bit); 116 } 117 118 void 119 dump_drop_page(vm_paddr_t pa) 120 { 121 int idx, bit; 122 123 pa >>= PAGE_SHIFT; 124 idx = pa >> 6; /* 2^6 = 64 */ 125 bit = pa & 63; 126 atomic_clear_long(&vm_page_dump[idx], 1ul << bit); 127 } 128 129 int 130 is_dumpable(vm_paddr_t pa) 131 { 132 vm_page_t m; 133 int i; 134 135 if ((m = vm_phys_paddr_to_vm_page(pa)) != NULL) 136 return ((m->flags & PG_NODUMP) == 0); 137 for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) { 138 if (pa >= dump_avail[i] && pa < dump_avail[i + 1]) 139 return (1); 140 } 141 return (0); 142 } 143 144 static void 145 pmap_kenter_temporary(vm_offset_t va, vm_paddr_t pa) 146 { 147 pmap_kremove(va); 148 pmap_kenter(va, pa); 149 } 150 151 static void 152 report_progress(void) 153 { 154 int sofar, i; 155 156 sofar = 100 - ((progress * 100) / dumpsize); 157 for (i = 0; i < nitems(progress_track); i++) { 158 if (sofar < progress_track[i].min_per || 159 sofar > progress_track[i].max_per) 160 continue; 161 if (progress_track[i].visited) 162 return; 163 progress_track[i].visited = 1; 164 printf("..%d%%", sofar); 165 return; 166 } 167 } 168 169 static int 170 blk_flush(struct dumperinfo *di) 171 { 172 int error; 173 174 if (fragsz == 0) 175 return (0); 176 177 error = dump_append(di, crashdumpmap, 0, fragsz); 178 DBG(dumptotal += fragsz;) 179 fragsz = 0; 180 return (error); 181 } 182 183 static int 184 blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz) 185 { 186 size_t len, maxdumpsz; 187 int error, i, c; 188 189 maxdumpsz = MIN(di->maxiosize, MAXDUMPPGS * PAGE_SIZE); 190 if (maxdumpsz == 0) /* seatbelt */ 191 maxdumpsz = PAGE_SIZE; 192 error = 0; 193 if ((sz % PAGE_SIZE) != 0) { 194 printf("Size not page aligned\n"); 195 return (EINVAL); 196 } 197 if (ptr != NULL && pa != 0) { 198 printf("Can't have both va and pa!\n"); 199 return (EINVAL); 200 } 201 if ((pa % PAGE_SIZE) != 0) { 202 printf("Address not page aligned 0x%lx\n", pa); 203 return (EINVAL); 204 } 205 if (ptr != NULL) { 206 /* 207 * If we're doing a virtual dump, flush any pre-existing 208 * pa pages 209 */ 210 error = blk_flush(di); 211 if (error) 212 return (error); 213 } 214 while (sz) { 215 len = maxdumpsz - fragsz; 216 if (len > sz) 217 len = sz; 218 counter += len; 219 progress -= len; 220 if (counter >> 20) { 221 report_progress(); 222 counter &= (1<<20) - 1; 223 } 224 225 if (ptr) { 226 error = dump_append(di, ptr, 0, len); 227 if (error) 228 return (error); 229 DBG(dumptotal += len;) 230 ptr += len; 231 } else { 232 for (i = 0; i < len; i += PAGE_SIZE) 233 pmap_kenter_temporary( 234 (vm_offset_t)crashdumpmap + fragsz + i, 235 pa + i); 236 237 fragsz += len; 238 pa += len; 239 if (fragsz == maxdumpsz) { 240 error = blk_flush(di); 241 if (error) 242 return (error); 243 } 244 } 245 sz -= len; 246 247 /* Check for user abort. */ 248 c = cncheckc(); 249 if (c == 0x03) 250 return (ECANCELED); 251 if (c != -1) 252 printf(" (CTRL-C to abort) "); 253 } 254 255 return (0); 256 } 257 258 static int 259 dump_pmap(struct dumperinfo *di) 260 { 261 void *ctx; 262 char *buf; 263 u_long nbytes; 264 int error; 265 266 ctx = dumpsys_dump_pmap_init(sizeof(pgbuf) / PAGE_SIZE); 267 268 for (;;) { 269 buf = dumpsys_dump_pmap(ctx, pgbuf, &nbytes); 270 if (buf == NULL) 271 break; 272 error = blk_write(di, buf, 0, nbytes); 273 if (error) 274 return (error); 275 } 276 277 return (0); 278 } 279 280 int 281 minidumpsys(struct dumperinfo *di) 282 { 283 vm_paddr_t pa; 284 int bit, error, i, retry_count; 285 uint32_t pmapsize; 286 uint64_t bits; 287 struct minidumphdr mdhdr; 288 289 retry_count = 0; 290 retry: 291 retry_count++; 292 fragsz = 0; 293 DBG(total = dumptotal = 0;) 294 295 /* Reset progress */ 296 counter = 0; 297 for (i = 0; i < nitems(progress_track); i++) 298 progress_track[i].visited = 0; 299 300 /* Build set of dumpable pages from kernel pmap */ 301 pmapsize = dumpsys_scan_pmap(); 302 if (pmapsize % PAGE_SIZE != 0) { 303 printf("pmapsize not page aligned: 0x%x\n", pmapsize); 304 return (EINVAL); 305 } 306 307 /* Calculate dump size */ 308 dumpsize = PAGE_SIZE; /* header */ 309 dumpsize += round_page(msgbufp->msg_size); 310 dumpsize += round_page(vm_page_dump_size); 311 dumpsize += pmapsize; 312 for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { 313 bits = vm_page_dump[i]; 314 /* TODO optimize with bit manipulation instructions */ 315 if (bits == 0) 316 continue; 317 for (bit = 0; bit < 64; bit++) { 318 if ((bits & (1ul<<bit)) == 0) 319 continue; 320 321 pa = BTOP(vm_page_dump, i, bit); 322 /* Clear out undumpable pages now if needed */ 323 if (is_dumpable(pa)) 324 dumpsize += PAGE_SIZE; 325 else 326 dump_drop_page(pa); 327 } 328 } 329 progress = dumpsize; 330 331 /* Initialize mdhdr */ 332 bzero(&mdhdr, sizeof(mdhdr)); 333 strcpy(mdhdr.magic, MINIDUMP_MAGIC); 334 strncpy(mdhdr.mmu_name, pmap_mmu_name(), sizeof(mdhdr.mmu_name) - 1); 335 mdhdr.version = MINIDUMP_VERSION; 336 mdhdr.msgbufsize = msgbufp->msg_size; 337 mdhdr.bitmapsize = vm_page_dump_size; 338 mdhdr.pmapsize = pmapsize; 339 mdhdr.kernbase = VM_MIN_KERNEL_ADDRESS; 340 mdhdr.kernend = VM_MAX_SAFE_KERNEL_ADDRESS; 341 mdhdr.dmapbase = DMAP_BASE_ADDRESS; 342 mdhdr.dmapend = DMAP_MAX_ADDRESS; 343 mdhdr.hw_direct_map = hw_direct_map; 344 mdhdr.startkernel = __startkernel; 345 mdhdr.endkernel = __endkernel; 346 347 dump_init_header(di, &kdh, KERNELDUMPMAGIC, KERNELDUMP_POWERPC_VERSION, 348 dumpsize); 349 350 error = dump_start(di, &kdh); 351 if (error) 352 goto fail; 353 354 printf("Dumping %lu out of %ju MB:", dumpsize >> 20, 355 ptoa((uintmax_t)physmem) / 1048576); 356 357 /* Dump minidump header */ 358 bzero(pgbuf, sizeof(pgbuf)); 359 memcpy(pgbuf, &mdhdr, sizeof(mdhdr)); 360 error = blk_write(di, pgbuf, 0, PAGE_SIZE); 361 if (error) 362 goto fail; 363 dump_total("header", PAGE_SIZE); 364 365 /* Dump msgbuf up front */ 366 error = blk_write(di, (char *)msgbufp->msg_ptr, 0, 367 round_page(msgbufp->msg_size)); 368 dump_total("msgbuf", round_page(msgbufp->msg_size)); 369 370 /* Dump bitmap */ 371 error = blk_write(di, (char *)vm_page_dump, 0, 372 round_page(vm_page_dump_size)); 373 if (error) 374 goto fail; 375 dump_total("bitmap", round_page(vm_page_dump_size)); 376 377 /* Dump kernel page directory pages */ 378 error = dump_pmap(di); 379 if (error) 380 goto fail; 381 dump_total("pmap", pmapsize); 382 383 /* Dump memory chunks */ 384 for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { 385 bits = vm_page_dump[i]; 386 /* TODO optimize with bit manipulation instructions */ 387 if (bits == 0) 388 continue; 389 for (bit = 0; bit < 64; bit++) { 390 if ((bits & (1ul<<bit)) == 0) 391 continue; 392 393 pa = BTOP(vm_page_dump, i, bit); 394 error = blk_write(di, 0, pa, PAGE_SIZE); 395 if (error) 396 goto fail; 397 } 398 } 399 400 error = blk_flush(di); 401 if (error) 402 goto fail; 403 dump_total("mem_chunks", dumpsize - total); 404 405 error = dump_finish(di, &kdh); 406 if (error) 407 goto fail; 408 409 printf("\nDump complete\n"); 410 return (0); 411 412 fail: 413 if (error < 0) 414 error = -error; 415 416 printf("\n"); 417 if (error == ENOSPC) { 418 printf("Dump map grown while dumping. "); 419 if (retry_count < dump_retry_count) { 420 printf("Retrying...\n"); 421 goto retry; 422 } 423 printf("Dump failed.\n"); 424 } else if (error == ECANCELED) 425 printf("Dump aborted\n"); 426 else if (error == E2BIG) 427 printf("Dump failed. Partition too small.\n"); 428 else 429 printf("** DUMP FAILED (ERROR %d) **\n", error); 430 return (error); 431 } 432 433 #if MINIDUMP_DEBUG 434 static void 435 dump_total(const char *id, size_t sz) 436 { 437 total += sz; 438 dprintf("\n%s=%08lx/%08lx/%08lx\n", 439 id, sz, total, dumptotal); 440 } 441 #endif 442