1 /*- 2 * Copyright (c) 2006 Peter Wemm 3 * Copyright (c) 2015 The FreeBSD Foundation 4 * All rights reserved. 5 * 6 * This software was developed by Andrew Turner under 7 * sponsorship from the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __FBSDID("$FreeBSD$"); 34 35 #include "opt_watchdog.h" 36 37 #include "opt_watchdog.h" 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/conf.h> 42 #include <sys/cons.h> 43 #include <sys/kernel.h> 44 #include <sys/kerneldump.h> 45 #include <sys/msgbuf.h> 46 #include <sys/watchdog.h> 47 #include <sys/vmmeter.h> 48 49 #include <vm/vm.h> 50 #include <vm/vm_param.h> 51 #include <vm/vm_page.h> 52 #include <vm/vm_phys.h> 53 #include <vm/pmap.h> 54 55 #include <machine/md_var.h> 56 #include <machine/pte.h> 57 #include <machine/minidump.h> 58 59 CTASSERT(sizeof(struct kerneldumpheader) == 512); 60 61 uint64_t *vm_page_dump; 62 int vm_page_dump_size; 63 64 static struct kerneldumpheader kdh; 65 66 /* Handle chunked writes. */ 67 static size_t fragsz; 68 static void *dump_va; 69 static size_t counter, progress, dumpsize; 70 71 static uint64_t tmpbuffer[PAGE_SIZE / sizeof(uint64_t)]; 72 73 CTASSERT(sizeof(*vm_page_dump) == 8); 74 75 static int 76 is_dumpable(vm_paddr_t pa) 77 { 78 vm_page_t m; 79 int i; 80 81 if ((m = vm_phys_paddr_to_vm_page(pa)) != NULL) 82 return ((m->flags & PG_NODUMP) == 0); 83 for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) { 84 if (pa >= dump_avail[i] && pa < dump_avail[i + 1]) 85 return (1); 86 } 87 return (0); 88 } 89 90 static int 91 blk_flush(struct dumperinfo *di) 92 { 93 int error; 94 95 if (fragsz == 0) 96 return (0); 97 98 error = dump_append(di, dump_va, 0, fragsz); 99 fragsz = 0; 100 return (error); 101 } 102 103 static struct { 104 int min_per; 105 int max_per; 106 int visited; 107 } progress_track[10] = { 108 { 0, 10, 0}, 109 { 10, 20, 0}, 110 { 20, 30, 0}, 111 { 30, 40, 0}, 112 { 40, 50, 0}, 113 { 50, 60, 0}, 114 { 60, 70, 0}, 115 { 70, 80, 0}, 116 { 80, 90, 0}, 117 { 90, 100, 0} 118 }; 119 120 static void 121 report_progress(size_t progress, size_t dumpsize) 122 { 123 int sofar, i; 124 125 sofar = 100 - ((progress * 100) / dumpsize); 126 for (i = 0; i < nitems(progress_track); i++) { 127 if (sofar < progress_track[i].min_per || 128 sofar > progress_track[i].max_per) 129 continue; 130 if (progress_track[i].visited) 131 return; 132 progress_track[i].visited = 1; 133 printf("..%d%%", sofar); 134 return; 135 } 136 } 137 138 static int 139 blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz) 140 { 141 size_t len; 142 int error, c; 143 u_int maxdumpsz; 144 145 maxdumpsz = min(di->maxiosize, MAXDUMPPGS * PAGE_SIZE); 146 if (maxdumpsz == 0) /* seatbelt */ 147 maxdumpsz = PAGE_SIZE; 148 error = 0; 149 if ((sz % PAGE_SIZE) != 0) { 150 printf("size not page aligned\n"); 151 return (EINVAL); 152 } 153 if (ptr != NULL && pa != 0) { 154 printf("cant have both va and pa!\n"); 155 return (EINVAL); 156 } 157 if ((((uintptr_t)pa) % PAGE_SIZE) != 0) { 158 printf("address not page aligned %p\n", ptr); 159 return (EINVAL); 160 } 161 if (ptr != NULL) { 162 /* 163 * If we're doing a virtual dump, flush any 164 * pre-existing pa pages. 165 */ 166 error = blk_flush(di); 167 if (error) 168 return (error); 169 } 170 while (sz) { 171 len = maxdumpsz - fragsz; 172 if (len > sz) 173 len = sz; 174 counter += len; 175 progress -= len; 176 if (counter >> 22) { 177 report_progress(progress, dumpsize); 178 counter &= (1 << 22) - 1; 179 } 180 181 wdog_kern_pat(WD_LASTVAL); 182 183 if (ptr) { 184 error = dump_append(di, ptr, 0, len); 185 if (error) 186 return (error); 187 ptr += len; 188 sz -= len; 189 } else { 190 dump_va = (void *)PHYS_TO_DMAP(pa); 191 fragsz += len; 192 pa += len; 193 sz -= len; 194 error = blk_flush(di); 195 if (error) 196 return (error); 197 } 198 199 /* Check for user abort. */ 200 c = cncheckc(); 201 if (c == 0x03) 202 return (ECANCELED); 203 if (c != -1) 204 printf(" (CTRL-C to abort) "); 205 } 206 207 return (0); 208 } 209 210 int 211 minidumpsys(struct dumperinfo *di) 212 { 213 pd_entry_t *l0, *l1, *l2; 214 pt_entry_t *l3; 215 uint32_t pmapsize; 216 vm_offset_t va; 217 vm_paddr_t pa; 218 int error; 219 uint64_t bits; 220 int i, bit; 221 int retry_count; 222 struct minidumphdr mdhdr; 223 224 retry_count = 0; 225 retry: 226 retry_count++; 227 error = 0; 228 pmapsize = 0; 229 for (va = VM_MIN_KERNEL_ADDRESS; va < kernel_vm_end; va += L2_SIZE) { 230 pmapsize += PAGE_SIZE; 231 if (!pmap_get_tables(pmap_kernel(), va, &l0, &l1, &l2, &l3)) 232 continue; 233 234 /* We should always be using the l2 table for kvm */ 235 if (l2 == NULL) 236 continue; 237 238 if ((*l2 & ATTR_DESCR_MASK) == L2_BLOCK) { 239 pa = *l2 & ~ATTR_MASK; 240 for (i = 0; i < Ln_ENTRIES; i++, pa += PAGE_SIZE) { 241 if (is_dumpable(pa)) 242 dump_add_page(pa); 243 } 244 } else if ((*l2 & ATTR_DESCR_MASK) == L2_TABLE) { 245 for (i = 0; i < Ln_ENTRIES; i++) { 246 if ((l3[i] & ATTR_DESCR_MASK) != L3_PAGE) 247 continue; 248 pa = l3[i] & ~ATTR_MASK; 249 if (is_dumpable(pa)) 250 dump_add_page(pa); 251 } 252 } 253 } 254 255 /* Calculate dump size. */ 256 dumpsize = pmapsize; 257 dumpsize += round_page(msgbufp->msg_size); 258 dumpsize += round_page(vm_page_dump_size); 259 for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { 260 bits = vm_page_dump[i]; 261 while (bits) { 262 bit = ffsl(bits) - 1; 263 pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + 264 bit) * PAGE_SIZE; 265 /* Clear out undumpable pages now if needed */ 266 if (is_dumpable(pa)) 267 dumpsize += PAGE_SIZE; 268 else 269 dump_drop_page(pa); 270 bits &= ~(1ul << bit); 271 } 272 } 273 dumpsize += PAGE_SIZE; 274 275 progress = dumpsize; 276 277 /* Initialize mdhdr */ 278 bzero(&mdhdr, sizeof(mdhdr)); 279 strcpy(mdhdr.magic, MINIDUMP_MAGIC); 280 mdhdr.version = MINIDUMP_VERSION; 281 mdhdr.msgbufsize = msgbufp->msg_size; 282 mdhdr.bitmapsize = vm_page_dump_size; 283 mdhdr.pmapsize = pmapsize; 284 mdhdr.kernbase = VM_MIN_KERNEL_ADDRESS; 285 mdhdr.dmapphys = DMAP_MIN_PHYSADDR; 286 mdhdr.dmapbase = DMAP_MIN_ADDRESS; 287 mdhdr.dmapend = DMAP_MAX_ADDRESS; 288 289 dump_init_header(di, &kdh, KERNELDUMPMAGIC, KERNELDUMP_AARCH64_VERSION, 290 dumpsize); 291 292 error = dump_start(di, &kdh); 293 if (error != 0) 294 goto fail; 295 296 printf("Dumping %llu out of %ju MB:", (long long)dumpsize >> 20, 297 ptoa((uintmax_t)physmem) / 1048576); 298 299 /* Dump my header */ 300 bzero(&tmpbuffer, sizeof(tmpbuffer)); 301 bcopy(&mdhdr, &tmpbuffer, sizeof(mdhdr)); 302 error = blk_write(di, (char *)&tmpbuffer, 0, PAGE_SIZE); 303 if (error) 304 goto fail; 305 306 /* Dump msgbuf up front */ 307 error = blk_write(di, (char *)msgbufp->msg_ptr, 0, 308 round_page(msgbufp->msg_size)); 309 if (error) 310 goto fail; 311 312 /* Dump bitmap */ 313 error = blk_write(di, (char *)vm_page_dump, 0, 314 round_page(vm_page_dump_size)); 315 if (error) 316 goto fail; 317 318 /* Dump kernel page directory pages */ 319 bzero(&tmpbuffer, sizeof(tmpbuffer)); 320 for (va = VM_MIN_KERNEL_ADDRESS; va < kernel_vm_end; va += L2_SIZE) { 321 if (!pmap_get_tables(pmap_kernel(), va, &l0, &l1, &l2, &l3)) { 322 /* We always write a page, even if it is zero */ 323 error = blk_write(di, (char *)&tmpbuffer, 0, PAGE_SIZE); 324 if (error) 325 goto fail; 326 /* flush, in case we reuse tmpbuffer in the same block*/ 327 error = blk_flush(di); 328 if (error) 329 goto fail; 330 } else if (l2 == NULL) { 331 pa = (*l1 & ~ATTR_MASK) | (va & L1_OFFSET); 332 333 /* Generate fake l3 entries based upon the l1 entry */ 334 for (i = 0; i < Ln_ENTRIES; i++) { 335 tmpbuffer[i] = pa + (i * PAGE_SIZE) | 336 ATTR_DEFAULT | L3_PAGE; 337 } 338 /* We always write a page, even if it is zero */ 339 error = blk_write(di, (char *)&tmpbuffer, 0, PAGE_SIZE); 340 if (error) 341 goto fail; 342 /* flush, in case we reuse tmpbuffer in the same block*/ 343 error = blk_flush(di); 344 if (error) 345 goto fail; 346 bzero(&tmpbuffer, sizeof(tmpbuffer)); 347 } else if ((*l2 & ATTR_DESCR_MASK) == L2_BLOCK) { 348 /* TODO: Handle an invalid L2 entry */ 349 pa = (*l2 & ~ATTR_MASK) | (va & L2_OFFSET); 350 351 /* Generate fake l3 entries based upon the l1 entry */ 352 for (i = 0; i < Ln_ENTRIES; i++) { 353 tmpbuffer[i] = pa + (i * PAGE_SIZE) | 354 ATTR_DEFAULT | L3_PAGE; 355 } 356 /* We always write a page, even if it is zero */ 357 error = blk_write(di, (char *)&tmpbuffer, 0, PAGE_SIZE); 358 if (error) 359 goto fail; 360 /* flush, in case we reuse fakepd in the same block */ 361 error = blk_flush(di); 362 if (error) 363 goto fail; 364 bzero(&tmpbuffer, sizeof(tmpbuffer)); 365 continue; 366 } else { 367 pa = *l2 & ~ATTR_MASK; 368 369 /* We always write a page, even if it is zero */ 370 error = blk_write(di, NULL, pa, PAGE_SIZE); 371 if (error) 372 goto fail; 373 } 374 } 375 376 /* Dump memory chunks */ 377 /* XXX cluster it up and use blk_dump() */ 378 for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { 379 bits = vm_page_dump[i]; 380 while (bits) { 381 bit = ffsl(bits) - 1; 382 pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + 383 bit) * PAGE_SIZE; 384 error = blk_write(di, 0, pa, PAGE_SIZE); 385 if (error) 386 goto fail; 387 bits &= ~(1ul << bit); 388 } 389 } 390 391 error = blk_flush(di); 392 if (error) 393 goto fail; 394 395 error = dump_finish(di, &kdh); 396 if (error != 0) 397 goto fail; 398 399 printf("\nDump complete\n"); 400 return (0); 401 402 fail: 403 if (error < 0) 404 error = -error; 405 406 printf("\n"); 407 if (error == ENOSPC) { 408 printf("Dump map grown while dumping. "); 409 if (retry_count < 5) { 410 printf("Retrying...\n"); 411 goto retry; 412 } 413 printf("Dump failed.\n"); 414 } 415 else if (error == ECANCELED) 416 printf("Dump aborted\n"); 417 else if (error == E2BIG) { 418 printf("Dump failed. Partition too small (about %lluMB were " 419 "needed this time).\n", (long long)dumpsize >> 20); 420 } else 421 printf("** DUMP FAILED (ERROR %d) **\n", error); 422 return (error); 423 } 424 425 void 426 dump_add_page(vm_paddr_t pa) 427 { 428 int idx, bit; 429 430 pa >>= PAGE_SHIFT; 431 idx = pa >> 6; /* 2^6 = 64 */ 432 bit = pa & 63; 433 atomic_set_long(&vm_page_dump[idx], 1ul << bit); 434 } 435 436 void 437 dump_drop_page(vm_paddr_t pa) 438 { 439 int idx, bit; 440 441 pa >>= PAGE_SHIFT; 442 idx = pa >> 6; /* 2^6 = 64 */ 443 bit = pa & 63; 444 atomic_clear_long(&vm_page_dump[idx], 1ul << bit); 445 } 446