minidump_machdep.c revision 256281
1/*- 2 * Copyright (c) 2010 Oleksandr Tymoshenko <gonzo@freebsd.org> 3 * Copyright (c) 2008 Semihalf, Grzegorz Bernacki 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 * 27 * from: FreeBSD: src/sys/arm/arm/minidump_machdep.c v214223 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: stable/10/sys/mips/mips/minidump_machdep.c 216148 2010-12-03 14:20:20Z jchandra $"); 32 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/conf.h> 36#include <sys/cons.h> 37#include <sys/kernel.h> 38#include <sys/kerneldump.h> 39#include <sys/msgbuf.h> 40#include <vm/vm.h> 41#include <vm/pmap.h> 42#include <machine/pmap.h> 43#include <machine/atomic.h> 44#include <machine/elf.h> 45#include <machine/md_var.h> 46#include <machine/vmparam.h> 47#include <machine/minidump.h> 48#include <machine/cache.h> 49 50CTASSERT(sizeof(struct kerneldumpheader) == 512); 51 52/* 53 * Don't touch the first SIZEOF_METADATA bytes on the dump device. This 54 * is to protect us from metadata and to protect metadata from us. 55 */ 56#define SIZEOF_METADATA (64*1024) 57 58uint32_t *vm_page_dump; 59int vm_page_dump_size; 60 61static struct kerneldumpheader kdh; 62static off_t dumplo; 63static off_t origdumplo; 64 65/* Handle chunked writes. */ 66static uint64_t counter, progress; 67/* Just auxiliary bufffer */ 68static char tmpbuffer[PAGE_SIZE]; 69 70extern pd_entry_t *kernel_segmap; 71 72CTASSERT(sizeof(*vm_page_dump) == 4); 73 74static int 75is_dumpable(vm_paddr_t pa) 76{ 77 int i; 78 79 for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) { 80 if (pa >= dump_avail[i] && pa < dump_avail[i + 1]) 81 return (1); 82 } 83 return (0); 84} 85 86void 87dump_add_page(vm_paddr_t pa) 88{ 89 int idx, bit; 90 91 pa >>= PAGE_SHIFT; 92 idx = pa >> 5; /* 2^5 = 32 */ 93 bit = pa & 31; 94 atomic_set_int(&vm_page_dump[idx], 1ul << bit); 95} 96 97void 98dump_drop_page(vm_paddr_t pa) 99{ 100 int idx, bit; 101 102 pa >>= PAGE_SHIFT; 103 idx = pa >> 5; /* 2^5 = 32 */ 104 bit = pa & 31; 105 atomic_clear_int(&vm_page_dump[idx], 1ul << bit); 106} 107 108#define PG2MB(pgs) (((pgs) + (1 << 8) - 1) >> 8) 109 110static int 111write_buffer(struct dumperinfo *di, char *ptr, size_t sz) 112{ 113 size_t len; 114 int error, c; 115 u_int maxdumpsz; 116 117 maxdumpsz = di->maxiosize; 118 119 if (maxdumpsz == 0) /* seatbelt */ 120 maxdumpsz = PAGE_SIZE; 121 122 error = 0; 123 124 while (sz) { 125 len = min(maxdumpsz, sz); 126 counter += len; 127 progress -= len; 128 129 if (counter >> 22) { 130 printf(" %jd", PG2MB(progress >> PAGE_SHIFT)); 131 counter &= (1<<22) - 1; 132 } 133 134 if (ptr) { 135 error = dump_write(di, ptr, 0, dumplo, len); 136 if (error) 137 return (error); 138 dumplo += len; 139 ptr += len; 140 sz -= len; 141 } else { 142 panic("pa is not supported"); 143 } 144 145 /* Check for user abort. */ 146 c = cncheckc(); 147 if (c == 0x03) 148 return (ECANCELED); 149 if (c != -1) 150 printf(" (CTRL-C to abort) "); 151 } 152 153 return (0); 154} 155 156void 157minidumpsys(struct dumperinfo *di) 158{ 159 struct minidumphdr mdhdr; 160 uint64_t dumpsize; 161 uint32_t ptesize; 162 uint32_t bits; 163 vm_paddr_t pa; 164 vm_offset_t prev_pte = 0; 165 uint32_t count = 0; 166 vm_offset_t va; 167 pt_entry_t *pte; 168 int i, bit, error; 169 void *dump_va; 170 171 /* Flush cache */ 172 mips_dcache_wbinv_all(); 173 174 counter = 0; 175 /* Walk page table pages, set bits in vm_page_dump */ 176 ptesize = 0; 177 178 for (va = VM_MIN_KERNEL_ADDRESS; va < kernel_vm_end; va += NBPDR) { 179 ptesize += PAGE_SIZE; 180 pte = pmap_pte(kernel_pmap, va); 181 KASSERT(pte != NULL, ("pte for %jx is NULL", (uintmax_t)va)); 182 for (i = 0; i < NPTEPG; i++) { 183 if (pte_test(&pte[i], PTE_V)) { 184 pa = TLBLO_PTE_TO_PA(pte[i]); 185 if (is_dumpable(pa)) 186 dump_add_page(pa); 187 } 188 } 189 } 190 191 /* 192 * Now mark pages from 0 to phys_avail[0], that's where kernel 193 * and pages allocated by pmap_steal reside 194 */ 195 for (pa = 0; pa < phys_avail[0]; pa += PAGE_SIZE) { 196 if (is_dumpable(pa)) 197 dump_add_page(pa); 198 } 199 200 /* Calculate dump size. */ 201 dumpsize = ptesize; 202 dumpsize += round_page(msgbufp->msg_size); 203 dumpsize += round_page(vm_page_dump_size); 204 205 for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { 206 bits = vm_page_dump[i]; 207 while (bits) { 208 bit = ffs(bits) - 1; 209 pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + 210 bit) * PAGE_SIZE; 211 /* Clear out undumpable pages now if needed */ 212 if (is_dumpable(pa)) 213 dumpsize += PAGE_SIZE; 214 else 215 dump_drop_page(pa); 216 bits &= ~(1ul << bit); 217 } 218 } 219 220 dumpsize += PAGE_SIZE; 221 222 /* Determine dump offset on device. */ 223 if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { 224 error = ENOSPC; 225 goto fail; 226 } 227 228 origdumplo = dumplo = di->mediaoffset + di->mediasize - dumpsize; 229 dumplo -= sizeof(kdh) * 2; 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 = VM_MIN_KERNEL_ADDRESS; 240 241 mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_MIPS_VERSION, dumpsize, 242 di->blocksize); 243 244 printf("Physical memory: %ju MB\n", 245 (uintmax_t)ptoa((uintmax_t)physmem) / 1048576); 246 printf("Dumping %llu MB:", (long long)dumpsize >> 20); 247 248 /* Dump leader */ 249 error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); 250 if (error) 251 goto fail; 252 dumplo += sizeof(kdh); 253 254 /* Dump my header */ 255 bzero(tmpbuffer, sizeof(tmpbuffer)); 256 bcopy(&mdhdr, tmpbuffer, sizeof(mdhdr)); 257 error = write_buffer(di, tmpbuffer, PAGE_SIZE); 258 if (error) 259 goto fail; 260 261 /* Dump msgbuf up front */ 262 error = write_buffer(di, (char *)msgbufp->msg_ptr, 263 round_page(msgbufp->msg_size)); 264 if (error) 265 goto fail; 266 267 /* Dump bitmap */ 268 error = write_buffer(di, (char *)vm_page_dump, 269 round_page(vm_page_dump_size)); 270 if (error) 271 goto fail; 272 273 /* Dump kernel page table pages */ 274 for (va = VM_MIN_KERNEL_ADDRESS; va < kernel_vm_end; va += NBPDR) { 275 pte = pmap_pte(kernel_pmap, va); 276 KASSERT(pte != NULL, ("pte for %jx is NULL", (uintmax_t)va)); 277 if (!count) { 278 prev_pte = (vm_offset_t)pte; 279 count++; 280 } 281 else { 282 if ((vm_offset_t)pte == (prev_pte + count * PAGE_SIZE)) 283 count++; 284 else { 285 error = write_buffer(di, (char*)prev_pte, 286 count * PAGE_SIZE); 287 if (error) 288 goto fail; 289 count = 1; 290 prev_pte = (vm_offset_t)pte; 291 } 292 } 293 } 294 295 if (count) { 296 error = write_buffer(di, (char*)prev_pte, count * PAGE_SIZE); 297 if (error) 298 goto fail; 299 count = 0; 300 prev_pte = 0; 301 } 302 303 /* Dump memory chunks page by page*/ 304 for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { 305 bits = vm_page_dump[i]; 306 while (bits) { 307 bit = ffs(bits) - 1; 308 pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + 309 bit) * PAGE_SIZE; 310 dump_va = pmap_kenter_temporary(pa, 0); 311 error = write_buffer(di, dump_va, PAGE_SIZE); 312 if (error) 313 goto fail; 314 pmap_kenter_temporary_free(pa); 315 bits &= ~(1ul << bit); 316 } 317 } 318 319 /* Dump trailer */ 320 error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); 321 if (error) 322 goto fail; 323 dumplo += sizeof(kdh); 324 325 /* Signal completion, signoff and exit stage left. */ 326 dump_write(di, NULL, 0, 0, 0); 327 printf("\nDump complete\n"); 328 return; 329 330fail: 331 if (error < 0) 332 error = -error; 333 334 if (error == ECANCELED) 335 printf("\nDump aborted\n"); 336 else if (error == ENOSPC) 337 printf("\nDump failed. Partition too small.\n"); 338 else 339 printf("\n** DUMP FAILED (ERROR %d) **\n", error); 340} 341