1 /*- 2 * Copyright (c) 2006 Peter Wemm 3 * Copyright (c) 2015 The FreeBSD Foundation 4 * All rights reserved. 5 * 6 * This software was developed by Andrew Turner under 7 * sponsorship from the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 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 #include <sys/watchdog.h> 45 #include <sys/vmmeter.h> 46 47 #include <vm/vm.h> 48 #include <vm/vm_param.h> 49 #include <vm/vm_page.h> 50 #include <vm/vm_phys.h> 51 #include <vm/vm_dumpset.h> 52 #include <vm/pmap.h> 53 54 #include <machine/md_var.h> 55 #include <machine/pte.h> 56 #include <machine/minidump.h> 57 58 CTASSERT(sizeof(struct kerneldumpheader) == 512); 59 60 static struct kerneldumpheader kdh; 61 62 /* Handle chunked writes. */ 63 static size_t fragsz; 64 static void *dump_va; 65 static size_t dumpsize; 66 67 static uint64_t tmpbuffer[Ln_ENTRIES]; 68 69 static int 70 blk_flush(struct dumperinfo *di) 71 { 72 int error; 73 74 if (fragsz == 0) 75 return (0); 76 77 error = dump_append(di, dump_va, 0, fragsz); 78 fragsz = 0; 79 return (error); 80 } 81 82 static int 83 blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz) 84 { 85 size_t len; 86 int error, c; 87 u_int maxdumpsz; 88 89 maxdumpsz = min(di->maxiosize, MAXDUMPPGS * PAGE_SIZE); 90 if (maxdumpsz == 0) /* seatbelt */ 91 maxdumpsz = PAGE_SIZE; 92 error = 0; 93 if ((sz % PAGE_SIZE) != 0) { 94 printf("size not page aligned\n"); 95 return (EINVAL); 96 } 97 if (ptr != NULL && pa != 0) { 98 printf("cant have both va and pa!\n"); 99 return (EINVAL); 100 } 101 if ((((uintptr_t)pa) % PAGE_SIZE) != 0) { 102 printf("address not page aligned %p\n", ptr); 103 return (EINVAL); 104 } 105 if (ptr != NULL) { 106 /* 107 * If we're doing a virtual dump, flush any 108 * pre-existing pa pages. 109 */ 110 error = blk_flush(di); 111 if (error) 112 return (error); 113 } 114 while (sz) { 115 len = maxdumpsz - fragsz; 116 if (len > sz) 117 len = sz; 118 119 dumpsys_pb_progress(len); 120 wdog_kern_pat(WD_LASTVAL); 121 122 if (ptr) { 123 error = dump_append(di, ptr, 0, len); 124 if (error) 125 return (error); 126 ptr += len; 127 sz -= len; 128 } else { 129 dump_va = (void *)PHYS_TO_DMAP(pa); 130 fragsz += len; 131 pa += len; 132 sz -= len; 133 error = blk_flush(di); 134 if (error) 135 return (error); 136 } 137 138 /* Check for user abort. */ 139 c = cncheckc(); 140 if (c == 0x03) 141 return (ECANCELED); 142 if (c != -1) 143 printf(" (CTRL-C to abort) "); 144 } 145 146 return (0); 147 } 148 149 int 150 minidumpsys(struct dumperinfo *di) 151 { 152 struct minidumphdr mdhdr; 153 pd_entry_t *l0, *l1, *l2; 154 pt_entry_t *l3; 155 vm_offset_t va; 156 vm_paddr_t pa; 157 uint32_t pmapsize; 158 int error, i, j, retry_count; 159 160 retry_count = 0; 161 retry: 162 retry_count++; 163 error = 0; 164 pmapsize = 0; 165 for (va = VM_MIN_KERNEL_ADDRESS; va < kernel_vm_end; va += L2_SIZE) { 166 pmapsize += PAGE_SIZE; 167 if (!pmap_get_tables(pmap_kernel(), va, &l0, &l1, &l2, &l3)) 168 continue; 169 170 if ((*l1 & ATTR_DESCR_MASK) == L1_BLOCK) { 171 pa = *l1 & ~ATTR_MASK; 172 for (i = 0; i < Ln_ENTRIES * Ln_ENTRIES; 173 i++, pa += PAGE_SIZE) 174 if (vm_phys_is_dumpable(pa)) 175 dump_add_page(pa); 176 pmapsize += (Ln_ENTRIES - 1) * PAGE_SIZE; 177 va += L1_SIZE - L2_SIZE; 178 } else if ((*l2 & ATTR_DESCR_MASK) == L2_BLOCK) { 179 pa = *l2 & ~ATTR_MASK; 180 for (i = 0; i < Ln_ENTRIES; i++, pa += PAGE_SIZE) { 181 if (vm_phys_is_dumpable(pa)) 182 dump_add_page(pa); 183 } 184 } else if ((*l2 & ATTR_DESCR_MASK) == L2_TABLE) { 185 for (i = 0; i < Ln_ENTRIES; i++) { 186 if ((l3[i] & ATTR_DESCR_MASK) != L3_PAGE) 187 continue; 188 pa = l3[i] & ~ATTR_MASK; 189 if (vm_phys_is_dumpable(pa)) 190 dump_add_page(pa); 191 } 192 } 193 } 194 195 /* Calculate dump size. */ 196 dumpsize = pmapsize; 197 dumpsize += round_page(msgbufp->msg_size); 198 dumpsize += round_page(sizeof(dump_avail)); 199 dumpsize += round_page(BITSET_SIZE(vm_page_dump_pages)); 200 VM_PAGE_DUMP_FOREACH(pa) { 201 if (vm_phys_is_dumpable(pa)) 202 dumpsize += PAGE_SIZE; 203 else 204 dump_drop_page(pa); 205 } 206 dumpsize += PAGE_SIZE; 207 208 dumpsys_pb_init(dumpsize); 209 210 /* Initialize mdhdr */ 211 bzero(&mdhdr, sizeof(mdhdr)); 212 strcpy(mdhdr.magic, MINIDUMP_MAGIC); 213 mdhdr.version = MINIDUMP_VERSION; 214 mdhdr.msgbufsize = msgbufp->msg_size; 215 mdhdr.bitmapsize = round_page(BITSET_SIZE(vm_page_dump_pages)); 216 mdhdr.pmapsize = pmapsize; 217 mdhdr.kernbase = VM_MIN_KERNEL_ADDRESS; 218 mdhdr.dmapphys = DMAP_MIN_PHYSADDR; 219 mdhdr.dmapbase = DMAP_MIN_ADDRESS; 220 mdhdr.dmapend = DMAP_MAX_ADDRESS; 221 mdhdr.dumpavailsize = round_page(sizeof(dump_avail)); 222 223 dump_init_header(di, &kdh, KERNELDUMPMAGIC, KERNELDUMP_AARCH64_VERSION, 224 dumpsize); 225 226 error = dump_start(di, &kdh); 227 if (error != 0) 228 goto fail; 229 230 printf("Dumping %llu out of %ju MB:", (long long)dumpsize >> 20, 231 ptoa((uintmax_t)physmem) / 1048576); 232 233 /* Dump my header */ 234 bzero(&tmpbuffer, sizeof(tmpbuffer)); 235 bcopy(&mdhdr, &tmpbuffer, sizeof(mdhdr)); 236 error = blk_write(di, (char *)&tmpbuffer, 0, PAGE_SIZE); 237 if (error) 238 goto fail; 239 240 /* Dump msgbuf up front */ 241 error = blk_write(di, (char *)msgbufp->msg_ptr, 0, 242 round_page(msgbufp->msg_size)); 243 if (error) 244 goto fail; 245 246 /* Dump dump_avail */ 247 _Static_assert(sizeof(dump_avail) <= sizeof(tmpbuffer), 248 "Large dump_avail not handled"); 249 bzero(tmpbuffer, sizeof(tmpbuffer)); 250 memcpy(tmpbuffer, dump_avail, sizeof(dump_avail)); 251 error = blk_write(di, (char *)&tmpbuffer, 0, PAGE_SIZE); 252 if (error) 253 goto fail; 254 255 /* Dump bitmap */ 256 error = blk_write(di, (char *)vm_page_dump, 0, 257 round_page(BITSET_SIZE(vm_page_dump_pages))); 258 if (error) 259 goto fail; 260 261 /* Dump kernel page directory pages */ 262 bzero(&tmpbuffer, sizeof(tmpbuffer)); 263 for (va = VM_MIN_KERNEL_ADDRESS; va < kernel_vm_end; va += L2_SIZE) { 264 if (!pmap_get_tables(pmap_kernel(), va, &l0, &l1, &l2, &l3)) { 265 /* We always write a page, even if it is zero */ 266 error = blk_write(di, (char *)&tmpbuffer, 0, PAGE_SIZE); 267 if (error) 268 goto fail; 269 /* flush, in case we reuse tmpbuffer in the same block*/ 270 error = blk_flush(di); 271 if (error) 272 goto fail; 273 } else if ((*l1 & ATTR_DESCR_MASK) == L1_BLOCK) { 274 /* 275 * Handle a 1GB block mapping: write out 512 fake L2 276 * pages. 277 */ 278 pa = (*l1 & ~ATTR_MASK) | (va & L1_OFFSET); 279 280 for (i = 0; i < Ln_ENTRIES; i++) { 281 for (j = 0; j < Ln_ENTRIES; j++) { 282 tmpbuffer[j] = pa + i * L2_SIZE + 283 j * PAGE_SIZE | ATTR_DEFAULT | 284 L3_PAGE; 285 } 286 error = blk_write(di, (char *)&tmpbuffer, 0, 287 PAGE_SIZE); 288 if (error) 289 goto fail; 290 } 291 /* flush, in case we reuse tmpbuffer in the same block*/ 292 error = blk_flush(di); 293 if (error) 294 goto fail; 295 bzero(&tmpbuffer, sizeof(tmpbuffer)); 296 va += L1_SIZE - L2_SIZE; 297 } else if ((*l2 & ATTR_DESCR_MASK) == L2_BLOCK) { 298 pa = (*l2 & ~ATTR_MASK) | (va & L2_OFFSET); 299 300 /* Generate fake l3 entries based upon the l1 entry */ 301 for (i = 0; i < Ln_ENTRIES; i++) { 302 tmpbuffer[i] = pa + (i * PAGE_SIZE) | 303 ATTR_DEFAULT | L3_PAGE; 304 } 305 error = blk_write(di, (char *)&tmpbuffer, 0, PAGE_SIZE); 306 if (error) 307 goto fail; 308 /* flush, in case we reuse fakepd in the same block */ 309 error = blk_flush(di); 310 if (error) 311 goto fail; 312 bzero(&tmpbuffer, sizeof(tmpbuffer)); 313 continue; 314 } else { 315 pa = *l2 & ~ATTR_MASK; 316 317 error = blk_write(di, NULL, pa, PAGE_SIZE); 318 if (error) 319 goto fail; 320 } 321 } 322 323 /* Dump memory chunks */ 324 VM_PAGE_DUMP_FOREACH(pa) { 325 error = blk_write(di, 0, pa, PAGE_SIZE); 326 if (error) 327 goto fail; 328 } 329 330 error = blk_flush(di); 331 if (error) 332 goto fail; 333 334 error = dump_finish(di, &kdh); 335 if (error != 0) 336 goto fail; 337 338 printf("\nDump complete\n"); 339 return (0); 340 341 fail: 342 if (error < 0) 343 error = -error; 344 345 printf("\n"); 346 if (error == ENOSPC) { 347 printf("Dump map grown while dumping. "); 348 if (retry_count < 5) { 349 printf("Retrying...\n"); 350 goto retry; 351 } 352 printf("Dump failed.\n"); 353 } 354 else if (error == ECANCELED) 355 printf("Dump aborted\n"); 356 else if (error == E2BIG) { 357 printf("Dump failed. Partition too small (about %lluMB were " 358 "needed this time).\n", (long long)dumpsize >> 20); 359 } else 360 printf("** DUMP FAILED (ERROR %d) **\n", error); 361 return (error); 362 } 363