1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2006 Peter Wemm 5 * All rights reserved. 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 <vm/vm.h> 43 #include <vm/pmap.h> 44 #include <machine/atomic.h> 45 #include <machine/elf.h> 46 #include <machine/md_var.h> 47 #include <machine/vmparam.h> 48 #include <machine/minidump.h> 49 50 CTASSERT(sizeof(struct kerneldumpheader) == 512); 51 52 #define MD_ALIGN(x) (((off_t)(x) + PAGE_MASK) & ~PAGE_MASK) 53 #define DEV_ALIGN(x) roundup2((off_t)(x), DEV_BSIZE) 54 55 uint32_t *vm_page_dump; 56 int vm_page_dump_size; 57 58 static struct kerneldumpheader kdh; 59 60 /* Handle chunked writes. */ 61 static size_t fragsz; 62 static void *dump_va; 63 static uint64_t counter, progress; 64 65 CTASSERT(sizeof(*vm_page_dump) == 4); 66 67 68 static int 69 is_dumpable(vm_paddr_t pa) 70 { 71 int i; 72 73 for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) { 74 if (pa >= dump_avail[i] && pa < dump_avail[i + 1]) 75 return (1); 76 } 77 return (0); 78 } 79 80 #define PG2MB(pgs) (((pgs) + (1 << 8) - 1) >> 8) 81 82 static int 83 blk_flush(struct dumperinfo *di) 84 { 85 int error; 86 87 if (fragsz == 0) 88 return (0); 89 90 error = dump_append(di, dump_va, 0, fragsz); 91 fragsz = 0; 92 return (error); 93 } 94 95 static int 96 blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz) 97 { 98 size_t len; 99 int error, i, c; 100 u_int maxdumpsz; 101 102 maxdumpsz = min(di->maxiosize, MAXDUMPPGS * PAGE_SIZE); 103 if (maxdumpsz == 0) /* seatbelt */ 104 maxdumpsz = PAGE_SIZE; 105 error = 0; 106 if ((sz % PAGE_SIZE) != 0) { 107 printf("size not page aligned\n"); 108 return (EINVAL); 109 } 110 if (ptr != NULL && pa != 0) { 111 printf("cant have both va and pa!\n"); 112 return (EINVAL); 113 } 114 if (pa != 0 && (((uintptr_t)ptr) % PAGE_SIZE) != 0) { 115 printf("address not page aligned\n"); 116 return (EINVAL); 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 while (sz) { 125 len = maxdumpsz - fragsz; 126 if (len > sz) 127 len = sz; 128 counter += len; 129 progress -= len; 130 if (counter >> 24) { 131 printf(" %lld", PG2MB(progress >> PAGE_SHIFT)); 132 counter &= (1<<24) - 1; 133 } 134 135 wdog_kern_pat(WD_LASTVAL); 136 137 if (ptr) { 138 error = dump_append(di, ptr, 0, len); 139 if (error) 140 return (error); 141 ptr += len; 142 sz -= len; 143 } else { 144 for (i = 0; i < len; i += PAGE_SIZE) 145 dump_va = pmap_kenter_temporary(pa + i, (i + fragsz) >> PAGE_SHIFT); 146 fragsz += len; 147 pa += len; 148 sz -= len; 149 if (fragsz == maxdumpsz) { 150 error = blk_flush(di); 151 if (error) 152 return (error); 153 } 154 } 155 156 /* Check for user abort. */ 157 c = cncheckc(); 158 if (c == 0x03) 159 return (ECANCELED); 160 if (c != -1) 161 printf(" (CTRL-C to abort) "); 162 } 163 164 return (0); 165 } 166 167 /* A fake page table page, to avoid having to handle both 4K and 2M pages */ 168 static pt_entry_t fakept[NPTEPG]; 169 170 int 171 minidumpsys(struct dumperinfo *di) 172 { 173 uint64_t dumpsize; 174 uint32_t ptesize; 175 vm_offset_t va; 176 int error; 177 uint32_t bits; 178 uint64_t pa; 179 pd_entry_t *pd; 180 pt_entry_t *pt; 181 int i, j, k, bit; 182 struct minidumphdr mdhdr; 183 184 counter = 0; 185 /* Walk page table pages, set bits in vm_page_dump */ 186 ptesize = 0; 187 for (va = KERNBASE; va < kernel_vm_end; va += NBPDR) { 188 /* 189 * We always write a page, even if it is zero. Each 190 * page written corresponds to 2MB of space 191 */ 192 ptesize += PAGE_SIZE; 193 pd = IdlePTD; /* always mapped! */ 194 j = va >> PDRSHIFT; 195 if ((pd[j] & (PG_PS | PG_V)) == (PG_PS | PG_V)) { 196 /* This is an entire 2M page. */ 197 pa = pd[j] & PG_PS_FRAME; 198 for (k = 0; k < NPTEPG; k++) { 199 if (is_dumpable(pa)) 200 dump_add_page(pa); 201 pa += PAGE_SIZE; 202 } 203 continue; 204 } 205 if ((pd[j] & PG_V) == PG_V) { 206 /* set bit for each valid page in this 2MB block */ 207 pt = pmap_kenter_temporary(pd[j] & PG_FRAME, 0); 208 for (k = 0; k < NPTEPG; k++) { 209 if ((pt[k] & PG_V) == PG_V) { 210 pa = pt[k] & PG_FRAME; 211 if (is_dumpable(pa)) 212 dump_add_page(pa); 213 } 214 } 215 } else { 216 /* nothing, we're going to dump a null page */ 217 } 218 } 219 220 /* Calculate dump size. */ 221 dumpsize = ptesize; 222 dumpsize += round_page(msgbufp->msg_size); 223 dumpsize += round_page(vm_page_dump_size); 224 for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { 225 bits = vm_page_dump[i]; 226 while (bits) { 227 bit = bsfl(bits); 228 pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + bit) * PAGE_SIZE; 229 /* Clear out undumpable pages now if needed */ 230 if (is_dumpable(pa)) { 231 dumpsize += PAGE_SIZE; 232 } else { 233 dump_drop_page(pa); 234 } 235 bits &= ~(1ul << bit); 236 } 237 } 238 dumpsize += PAGE_SIZE; 239 240 progress = dumpsize; 241 242 /* Initialize mdhdr */ 243 bzero(&mdhdr, sizeof(mdhdr)); 244 strcpy(mdhdr.magic, MINIDUMP_MAGIC); 245 mdhdr.version = MINIDUMP_VERSION; 246 mdhdr.msgbufsize = msgbufp->msg_size; 247 mdhdr.bitmapsize = vm_page_dump_size; 248 mdhdr.ptesize = ptesize; 249 mdhdr.kernbase = KERNBASE; 250 #if defined(PAE) || defined(PAE_TABLES) 251 mdhdr.paemode = 1; 252 #endif 253 254 dump_init_header(di, &kdh, KERNELDUMPMAGIC, KERNELDUMP_I386_VERSION, 255 dumpsize); 256 257 error = dump_start(di, &kdh); 258 if (error != 0) 259 goto fail; 260 261 printf("Physical memory: %ju MB\n", ptoa((uintmax_t)physmem) / 1048576); 262 printf("Dumping %llu MB:", (long long)dumpsize >> 20); 263 264 /* Dump my header */ 265 bzero(&fakept, sizeof(fakept)); 266 bcopy(&mdhdr, &fakept, sizeof(mdhdr)); 267 error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE); 268 if (error) 269 goto fail; 270 271 /* Dump msgbuf up front */ 272 error = blk_write(di, (char *)msgbufp->msg_ptr, 0, round_page(msgbufp->msg_size)); 273 if (error) 274 goto fail; 275 276 /* Dump bitmap */ 277 error = blk_write(di, (char *)vm_page_dump, 0, round_page(vm_page_dump_size)); 278 if (error) 279 goto fail; 280 281 /* Dump kernel page table pages */ 282 for (va = KERNBASE; va < kernel_vm_end; va += NBPDR) { 283 /* We always write a page, even if it is zero */ 284 pd = IdlePTD; /* always mapped! */ 285 j = va >> PDRSHIFT; 286 if ((pd[j] & (PG_PS | PG_V)) == (PG_PS | PG_V)) { 287 /* This is a single 2M block. Generate a fake PTP */ 288 pa = pd[j] & PG_PS_FRAME; 289 for (k = 0; k < NPTEPG; k++) { 290 fakept[k] = (pa + (k * PAGE_SIZE)) | PG_V | PG_RW | PG_A | PG_M; 291 } 292 error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE); 293 if (error) 294 goto fail; 295 /* flush, in case we reuse fakept in the same block */ 296 error = blk_flush(di); 297 if (error) 298 goto fail; 299 continue; 300 } 301 if ((pd[j] & PG_V) == PG_V) { 302 pa = pd[j] & PG_FRAME; 303 error = blk_write(di, 0, pa, PAGE_SIZE); 304 if (error) 305 goto fail; 306 } else { 307 bzero(fakept, sizeof(fakept)); 308 error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE); 309 if (error) 310 goto fail; 311 /* flush, in case we reuse fakept in the same block */ 312 error = blk_flush(di); 313 if (error) 314 goto fail; 315 } 316 } 317 318 /* Dump memory chunks */ 319 /* XXX cluster it up and use blk_dump() */ 320 for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { 321 bits = vm_page_dump[i]; 322 while (bits) { 323 bit = bsfl(bits); 324 pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + bit) * PAGE_SIZE; 325 error = blk_write(di, 0, pa, PAGE_SIZE); 326 if (error) 327 goto fail; 328 bits &= ~(1ul << bit); 329 } 330 } 331 332 error = blk_flush(di); 333 if (error) 334 goto fail; 335 336 error = dump_finish(di, &kdh); 337 if (error != 0) 338 goto fail; 339 340 printf("\nDump complete\n"); 341 return (0); 342 343 fail: 344 if (error < 0) 345 error = -error; 346 347 if (error == ECANCELED) 348 printf("\nDump aborted\n"); 349 else if (error == E2BIG || error == ENOSPC) 350 printf("\nDump failed. Partition too small.\n"); 351 else 352 printf("\n** DUMP FAILED (ERROR %d) **\n", error); 353 return (error); 354 } 355 356 void 357 dump_add_page(vm_paddr_t pa) 358 { 359 int idx, bit; 360 361 pa >>= PAGE_SHIFT; 362 idx = pa >> 5; /* 2^5 = 32 */ 363 bit = pa & 31; 364 atomic_set_int(&vm_page_dump[idx], 1ul << bit); 365 } 366 367 void 368 dump_drop_page(vm_paddr_t pa) 369 { 370 int idx, bit; 371 372 pa >>= PAGE_SHIFT; 373 idx = pa >> 5; /* 2^5 = 32 */ 374 bit = pa & 31; 375 atomic_clear_int(&vm_page_dump[idx], 1ul << bit); 376 } 377 378