1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2006 Peter Wemm 5 * Copyright (c) 2008 Semihalf, Grzegorz Bernacki 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 * from: FreeBSD: src/sys/i386/i386/minidump_machdep.c,v 1.6 2008/08/17 23:27:27 30 */ 31 32 #include <sys/cdefs.h> 33 __FBSDID("$FreeBSD$"); 34 35 #include "opt_watchdog.h" 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/conf.h> 40 #include <sys/cons.h> 41 #include <sys/kernel.h> 42 #include <sys/kerneldump.h> 43 #include <sys/msgbuf.h> 44 #ifdef SW_WATCHDOG 45 #include <sys/watchdog.h> 46 #endif 47 #include <vm/vm.h> 48 #include <vm/pmap.h> 49 #include <machine/atomic.h> 50 #include <machine/cpu.h> 51 #include <machine/elf.h> 52 #include <machine/md_var.h> 53 #include <machine/minidump.h> 54 #include <machine/vmparam.h> 55 56 CTASSERT(sizeof(struct kerneldumpheader) == 512); 57 58 uint32_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 uint64_t counter, progress; 67 68 CTASSERT(sizeof(*vm_page_dump) == 4); 69 70 static int 71 is_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 84 static int 85 blk_flush(struct dumperinfo *di) 86 { 87 int error; 88 89 if (fragsz == 0) 90 return (0); 91 92 error = dump_append(di, dump_va, 0, fragsz); 93 fragsz = 0; 94 return (error); 95 } 96 97 static int 98 blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz) 99 { 100 size_t len; 101 int error, i, c; 102 u_int maxdumpsz; 103 104 maxdumpsz = min(di->maxiosize, MAXDUMPPGS * PAGE_SIZE); 105 if (maxdumpsz == 0) /* seatbelt */ 106 maxdumpsz = PAGE_SIZE; 107 error = 0; 108 if (ptr != NULL && pa != 0) { 109 printf("cant have both va and pa!\n"); 110 return (EINVAL); 111 } 112 if (pa != 0) { 113 if ((sz % PAGE_SIZE) != 0) { 114 printf("size not page aligned\n"); 115 return (EINVAL); 116 } 117 if ((pa & PAGE_MASK) != 0) { 118 printf("address not page aligned\n"); 119 return (EINVAL); 120 } 121 } 122 if (ptr != NULL) { 123 /* Flush any pre-existing pa pages before a virtual dump. */ 124 error = blk_flush(di); 125 if (error) 126 return (error); 127 } 128 while (sz) { 129 len = maxdumpsz - fragsz; 130 if (len > sz) 131 len = sz; 132 counter += len; 133 progress -= len; 134 if (counter >> 22) { 135 printf(" %lld", PG2MB(progress >> PAGE_SHIFT)); 136 counter &= (1<<22) - 1; 137 } 138 139 #ifdef SW_WATCHDOG 140 wdog_kern_pat(WD_LASTVAL); 141 #endif 142 if (ptr) { 143 error = dump_append(di, ptr, 0, len); 144 if (error) 145 return (error); 146 ptr += len; 147 sz -= len; 148 } else { 149 for (i = 0; i < len; i += PAGE_SIZE) 150 dump_va = pmap_kenter_temporary(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 173 /* A buffer for general use. Its size must be one page at least. */ 174 static char dumpbuf[PAGE_SIZE]; 175 CTASSERT(sizeof(dumpbuf) % sizeof(pt2_entry_t) == 0); 176 177 int 178 minidumpsys(struct dumperinfo *di) 179 { 180 struct minidumphdr mdhdr; 181 uint64_t dumpsize; 182 uint32_t ptesize; 183 uint32_t bits; 184 uint32_t pa, prev_pa = 0, count = 0; 185 vm_offset_t va; 186 int i, bit, error; 187 char *addr; 188 189 /* 190 * Flush caches. Note that in the SMP case this operates only on the 191 * current CPU's L1 cache. Before we reach this point, code in either 192 * the system shutdown or kernel debugger has called stop_cpus() to stop 193 * all cores other than this one. Part of the ARM handling of 194 * stop_cpus() is to call wbinv_all() on that core's local L1 cache. So 195 * by time we get to here, all that remains is to flush the L1 for the 196 * current CPU, then the L2. 197 */ 198 dcache_wbinv_poc_all(); 199 200 counter = 0; 201 /* Walk page table pages, set bits in vm_page_dump */ 202 ptesize = 0; 203 for (va = KERNBASE; va < kernel_vm_end; va += PAGE_SIZE) { 204 pa = pmap_dump_kextract(va, NULL); 205 if (pa != 0 && is_dumpable(pa)) 206 dump_add_page(pa); 207 ptesize += sizeof(pt2_entry_t); 208 } 209 210 /* Calculate dump size. */ 211 dumpsize = ptesize; 212 dumpsize += round_page(msgbufp->msg_size); 213 dumpsize += round_page(vm_page_dump_size); 214 for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { 215 bits = vm_page_dump[i]; 216 while (bits) { 217 bit = ffs(bits) - 1; 218 pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + 219 bit) * PAGE_SIZE; 220 /* Clear out undumpable pages now if needed */ 221 if (is_dumpable(pa)) 222 dumpsize += PAGE_SIZE; 223 else 224 dump_drop_page(pa); 225 bits &= ~(1ul << bit); 226 } 227 } 228 dumpsize += PAGE_SIZE; 229 230 progress = dumpsize; 231 232 /* Initialize mdhdr */ 233 bzero(&mdhdr, sizeof(mdhdr)); 234 strcpy(mdhdr.magic, MINIDUMP_MAGIC); 235 mdhdr.version = MINIDUMP_VERSION; 236 mdhdr.msgbufsize = msgbufp->msg_size; 237 mdhdr.bitmapsize = vm_page_dump_size; 238 mdhdr.ptesize = ptesize; 239 mdhdr.kernbase = KERNBASE; 240 mdhdr.arch = __ARM_ARCH; 241 #if __ARM_ARCH >= 6 242 mdhdr.mmuformat = MINIDUMP_MMU_FORMAT_V6; 243 #else 244 mdhdr.mmuformat = MINIDUMP_MMU_FORMAT_V4; 245 #endif 246 dump_init_header(di, &kdh, KERNELDUMPMAGIC, KERNELDUMP_ARM_VERSION, 247 dumpsize); 248 249 error = dump_start(di, &kdh); 250 if (error != 0) 251 goto fail; 252 253 printf("Physical memory: %u MB\n", ptoa((uintmax_t)physmem) / 1048576); 254 printf("Dumping %llu MB:", (long long)dumpsize >> 20); 255 256 /* Dump my header */ 257 bzero(dumpbuf, sizeof(dumpbuf)); 258 bcopy(&mdhdr, dumpbuf, sizeof(mdhdr)); 259 error = blk_write(di, dumpbuf, 0, PAGE_SIZE); 260 if (error) 261 goto fail; 262 263 /* Dump msgbuf up front */ 264 error = blk_write(di, (char *)msgbufp->msg_ptr, 0, 265 round_page(msgbufp->msg_size)); 266 if (error) 267 goto fail; 268 269 /* Dump bitmap */ 270 error = blk_write(di, (char *)vm_page_dump, 0, 271 round_page(vm_page_dump_size)); 272 if (error) 273 goto fail; 274 275 /* Dump kernel page table pages */ 276 addr = dumpbuf; 277 for (va = KERNBASE; va < kernel_vm_end; va += PAGE_SIZE) { 278 pmap_dump_kextract(va, (pt2_entry_t *)addr); 279 addr += sizeof(pt2_entry_t); 280 if (addr == dumpbuf + sizeof(dumpbuf)) { 281 error = blk_write(di, dumpbuf, 0, sizeof(dumpbuf)); 282 if (error != 0) 283 goto fail; 284 addr = dumpbuf; 285 } 286 } 287 if (addr != dumpbuf) { 288 error = blk_write(di, dumpbuf, 0, addr - dumpbuf); 289 if (error != 0) 290 goto fail; 291 } 292 293 /* Dump memory chunks */ 294 for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { 295 bits = vm_page_dump[i]; 296 while (bits) { 297 bit = ffs(bits) - 1; 298 pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + 299 bit) * PAGE_SIZE; 300 if (!count) { 301 prev_pa = pa; 302 count++; 303 } else { 304 if (pa == (prev_pa + count * PAGE_SIZE)) 305 count++; 306 else { 307 error = blk_write(di, NULL, prev_pa, 308 count * PAGE_SIZE); 309 if (error) 310 goto fail; 311 count = 1; 312 prev_pa = pa; 313 } 314 } 315 bits &= ~(1ul << bit); 316 } 317 } 318 if (count) { 319 error = blk_write(di, NULL, prev_pa, count * PAGE_SIZE); 320 if (error) 321 goto fail; 322 count = 0; 323 prev_pa = 0; 324 } 325 326 error = blk_flush(di); 327 if (error) 328 goto fail; 329 330 error = dump_finish(di, &kdh); 331 if (error != 0) 332 goto fail; 333 334 printf("\nDump complete\n"); 335 return (0); 336 337 fail: 338 if (error < 0) 339 error = -error; 340 341 if (error == ECANCELED) 342 printf("\nDump aborted\n"); 343 else if (error == E2BIG || error == ENOSPC) 344 printf("\nDump failed. Partition too small.\n"); 345 else 346 printf("\n** DUMP FAILED (ERROR %d) **\n", error); 347 return (error); 348 } 349 350 void 351 dump_add_page(vm_paddr_t pa) 352 { 353 int idx, bit; 354 355 pa >>= PAGE_SHIFT; 356 idx = pa >> 5; /* 2^5 = 32 */ 357 bit = pa & 31; 358 atomic_set_int(&vm_page_dump[idx], 1ul << bit); 359 } 360 361 void 362 dump_drop_page(vm_paddr_t pa) 363 { 364 int idx, bit; 365 366 pa >>= PAGE_SHIFT; 367 idx = pa >> 5; /* 2^5 = 32 */ 368 bit = pa & 31; 369 atomic_clear_int(&vm_page_dump[idx], 1ul << bit); 370 } 371