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