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