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 */ 26 27 #include <sys/types.h> 28 #include <sys/param.h> 29 30 #include <sys/cons.h> 31 #include <sys/kerneldump.h> 32 #include <sys/msgbuf.h> 33 #include <sys/proc.h> 34 #include <sys/sysctl.h> 35 36 #include <vm/vm.h> 37 #include <vm/vm_param.h> 38 #include <vm/vm_page.h> 39 #include <vm/vm_phys.h> 40 #include <vm/vm_dumpset.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 /* Debugging stuff */ 49 #define MINIDUMP_DEBUG 0 50 #if MINIDUMP_DEBUG 51 #define dprintf(fmt, ...) printf(fmt, ## __VA_ARGS__) 52 #define DBG(...) __VA_ARGS__ 53 static size_t total, dumptotal; 54 static void dump_total(const char *id, size_t sz); 55 #else 56 #define dprintf(fmt, ...) 57 #define DBG(...) 58 #define dump_total(...) 59 #endif 60 61 extern vm_offset_t __startkernel, __endkernel; 62 63 static int dump_retry_count = 5; 64 SYSCTL_INT(_machdep, OID_AUTO, dump_retry_count, CTLFLAG_RWTUN, 65 &dump_retry_count, 0, 66 "Number of times dump has to retry before bailing out"); 67 68 static struct kerneldumpheader kdh; 69 static char pgbuf[PAGE_SIZE]; 70 71 static size_t dumpsize; 72 73 /* Handle chunked writes. */ 74 static size_t fragsz; 75 76 static void 77 pmap_kenter_temporary(vm_offset_t va, vm_paddr_t pa) 78 { 79 pmap_kremove(va); 80 pmap_kenter(va, pa); 81 } 82 83 static int 84 blk_flush(struct dumperinfo *di) 85 { 86 int error; 87 88 if (fragsz == 0) 89 return (0); 90 91 error = dump_append(di, crashdumpmap, fragsz); 92 DBG(dumptotal += 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, maxdumpsz; 101 int error, i, c; 102 103 maxdumpsz = MIN(di->maxiosize, MAXDUMPPGS * PAGE_SIZE); 104 if (maxdumpsz == 0) /* seatbelt */ 105 maxdumpsz = PAGE_SIZE; 106 error = 0; 107 if ((sz % PAGE_SIZE) != 0) { 108 printf("Size not page aligned\n"); 109 return (EINVAL); 110 } 111 if (ptr != NULL && pa != 0) { 112 printf("Can't have both va and pa!\n"); 113 return (EINVAL); 114 } 115 if ((pa % PAGE_SIZE) != 0) { 116 printf("Address not page aligned 0x%lx\n", pa); 117 return (EINVAL); 118 } 119 if (ptr != NULL) { 120 /* 121 * If we're doing a virtual dump, flush any pre-existing 122 * pa pages 123 */ 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 133 dumpsys_pb_progress(len); 134 135 if (ptr) { 136 error = dump_append(di, ptr, len); 137 if (error) 138 return (error); 139 DBG(dumptotal += len;) 140 ptr += len; 141 } else { 142 for (i = 0; i < len; i += PAGE_SIZE) 143 pmap_kenter_temporary( 144 (vm_offset_t)crashdumpmap + fragsz + i, 145 pa + i); 146 147 fragsz += len; 148 pa += len; 149 if (fragsz == maxdumpsz) { 150 error = blk_flush(di); 151 if (error) 152 return (error); 153 } 154 } 155 sz -= len; 156 157 /* Check for user abort. */ 158 c = cncheckc(); 159 if (c == 0x03) 160 return (ECANCELED); 161 if (c != -1) 162 printf(" (CTRL-C to abort) "); 163 } 164 165 return (0); 166 } 167 168 static int 169 dump_pmap(struct dumperinfo *di) 170 { 171 void *ctx; 172 char *buf; 173 u_long nbytes; 174 int error; 175 176 ctx = dumpsys_dump_pmap_init(sizeof(pgbuf) / PAGE_SIZE); 177 178 for (;;) { 179 buf = dumpsys_dump_pmap(ctx, pgbuf, &nbytes); 180 if (buf == NULL) 181 break; 182 error = blk_write(di, buf, 0, nbytes); 183 if (error) 184 return (error); 185 } 186 187 return (0); 188 } 189 190 int 191 cpu_minidumpsys(struct dumperinfo *di, const struct minidumpstate *state) 192 { 193 vm_paddr_t pa; 194 int error, retry_count; 195 uint32_t pmapsize; 196 struct minidumphdr mdhdr; 197 struct msgbuf *mbp; 198 199 retry_count = 0; 200 retry: 201 retry_count++; 202 fragsz = 0; 203 DBG(total = dumptotal = 0;) 204 205 /* Build set of dumpable pages from kernel pmap */ 206 pmapsize = dumpsys_scan_pmap(state->dump_bitset); 207 if (pmapsize % PAGE_SIZE != 0) { 208 printf("pmapsize not page aligned: 0x%x\n", pmapsize); 209 return (EINVAL); 210 } 211 212 /* Calculate dump size */ 213 mbp = state->msgbufp; 214 dumpsize = PAGE_SIZE; /* header */ 215 dumpsize += round_page(mbp->msg_size); 216 dumpsize += round_page(sizeof(dump_avail)); 217 dumpsize += round_page(BITSET_SIZE(vm_page_dump_pages)); 218 dumpsize += pmapsize; 219 VM_PAGE_DUMP_FOREACH(state->dump_bitset, pa) { 220 /* Clear out undumpable pages now if needed */ 221 if (vm_phys_is_dumpable(pa)) 222 dumpsize += PAGE_SIZE; 223 else 224 vm_page_dump_drop(state->dump_bitset, pa); 225 } 226 dumpsys_pb_init(dumpsize); 227 228 /* Initialize mdhdr */ 229 bzero(&mdhdr, sizeof(mdhdr)); 230 strcpy(mdhdr.magic, MINIDUMP_MAGIC); 231 strncpy(mdhdr.mmu_name, pmap_mmu_name(), sizeof(mdhdr.mmu_name) - 1); 232 mdhdr.version = MINIDUMP_VERSION; 233 mdhdr.msgbufsize = mbp->msg_size; 234 mdhdr.bitmapsize = round_page(BITSET_SIZE(vm_page_dump_pages)); 235 mdhdr.pmapsize = pmapsize; 236 mdhdr.kernbase = VM_MIN_KERNEL_ADDRESS; 237 mdhdr.kernend = VM_MAX_SAFE_KERNEL_ADDRESS; 238 mdhdr.dmapbase = DMAP_BASE_ADDRESS; 239 mdhdr.dmapend = DMAP_MAX_ADDRESS; 240 mdhdr.hw_direct_map = hw_direct_map; 241 mdhdr.startkernel = __startkernel; 242 mdhdr.endkernel = __endkernel; 243 mdhdr.dumpavailsize = round_page(sizeof(dump_avail)); 244 245 dump_init_header(di, &kdh, KERNELDUMPMAGIC, KERNELDUMP_POWERPC_VERSION, 246 dumpsize); 247 248 error = dump_start(di, &kdh); 249 if (error) 250 goto fail; 251 252 printf("Dumping %lu out of %ju MB:", dumpsize >> 20, 253 ptoa((uintmax_t)physmem) / 1048576); 254 255 /* Dump minidump header */ 256 bzero(pgbuf, sizeof(pgbuf)); 257 memcpy(pgbuf, &mdhdr, sizeof(mdhdr)); 258 error = blk_write(di, pgbuf, 0, PAGE_SIZE); 259 if (error) 260 goto fail; 261 dump_total("header", PAGE_SIZE); 262 263 /* Dump msgbuf up front */ 264 error = blk_write(di, mbp->msg_ptr, 0, round_page(mbp->msg_size)); 265 dump_total("msgbuf", round_page(mbp->msg_size)); 266 267 /* Dump dump_avail */ 268 _Static_assert(sizeof(dump_avail) <= sizeof(pgbuf), 269 "Large dump_avail not handled"); 270 bzero(pgbuf, sizeof(mdhdr)); 271 memcpy(pgbuf, dump_avail, sizeof(dump_avail)); 272 error = blk_write(di, pgbuf, 0, PAGE_SIZE); 273 if (error) 274 goto fail; 275 dump_total("dump_avail", round_page(sizeof(dump_avail))); 276 277 /* Dump bitmap */ 278 error = blk_write(di, (char *)vm_page_dump, 0, 279 round_page(BITSET_SIZE(vm_page_dump_pages))); 280 if (error) 281 goto fail; 282 dump_total("bitmap", round_page(BITSET_SIZE(vm_page_dump_pages))); 283 284 /* Dump kernel page directory pages */ 285 error = dump_pmap(di); 286 if (error) 287 goto fail; 288 dump_total("pmap", pmapsize); 289 290 /* Dump memory chunks */ 291 VM_PAGE_DUMP_FOREACH(state->dump_bitset, pa) { 292 error = blk_write(di, 0, pa, PAGE_SIZE); 293 if (error) 294 goto fail; 295 } 296 297 error = blk_flush(di); 298 if (error) 299 goto fail; 300 dump_total("mem_chunks", dumpsize - total); 301 302 error = dump_finish(di, &kdh); 303 if (error) 304 goto fail; 305 306 printf("\nDump complete\n"); 307 return (0); 308 309 fail: 310 if (error < 0) 311 error = -error; 312 313 printf("\n"); 314 if (error == ENOSPC) { 315 printf("Dump map grown while dumping. "); 316 if (retry_count < dump_retry_count) { 317 printf("Retrying...\n"); 318 goto retry; 319 } 320 printf("Dump failed.\n"); 321 } else if (error == ECANCELED) 322 printf("Dump aborted\n"); 323 else if (error == E2BIG) 324 printf("Dump failed. Partition too small.\n"); 325 else 326 printf("** DUMP FAILED (ERROR %d) **\n", error); 327 return (error); 328 } 329 330 #if MINIDUMP_DEBUG 331 static void 332 dump_total(const char *id, size_t sz) 333 { 334 total += sz; 335 dprintf("\n%s=%08lx/%08lx/%08lx\n", 336 id, sz, total, dumptotal); 337 } 338 #endif 339