minidump_machdep.c revision 216148
1214903Sgonzo/*- 2214903Sgonzo * Copyright (c) 2010 Oleksandr Tymoshenko <gonzo@freebsd.org> 3214903Sgonzo * Copyright (c) 2008 Semihalf, Grzegorz Bernacki 4214903Sgonzo * All rights reserved. 5214903Sgonzo * 6214903Sgonzo * Redistribution and use in source and binary forms, with or without 7214903Sgonzo * modification, are permitted provided that the following conditions 8214903Sgonzo * are met: 9214903Sgonzo * 10214903Sgonzo * 1. Redistributions of source code must retain the above copyright 11214903Sgonzo * notice, this list of conditions and the following disclaimer. 12214903Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 13214903Sgonzo * notice, this list of conditions and the following disclaimer in the 14214903Sgonzo * documentation and/or other materials provided with the distribution. 15214903Sgonzo * 16214903Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17214903Sgonzo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18214903Sgonzo * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19214903Sgonzo * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20214903Sgonzo * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21214903Sgonzo * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22214903Sgonzo * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23214903Sgonzo * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24214903Sgonzo * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25214903Sgonzo * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26214903Sgonzo * 27214903Sgonzo * from: FreeBSD: src/sys/arm/arm/minidump_machdep.c v214223 28214903Sgonzo */ 29214903Sgonzo 30214903Sgonzo#include <sys/cdefs.h> 31214903Sgonzo__FBSDID("$FreeBSD: head/sys/mips/mips/minidump_machdep.c 216148 2010-12-03 14:20:20Z jchandra $"); 32214903Sgonzo 33214903Sgonzo#include <sys/param.h> 34214903Sgonzo#include <sys/systm.h> 35214903Sgonzo#include <sys/conf.h> 36214903Sgonzo#include <sys/cons.h> 37214903Sgonzo#include <sys/kernel.h> 38214903Sgonzo#include <sys/kerneldump.h> 39214903Sgonzo#include <sys/msgbuf.h> 40214903Sgonzo#include <vm/vm.h> 41214903Sgonzo#include <vm/pmap.h> 42214903Sgonzo#include <machine/pmap.h> 43214903Sgonzo#include <machine/atomic.h> 44214903Sgonzo#include <machine/elf.h> 45214903Sgonzo#include <machine/md_var.h> 46214903Sgonzo#include <machine/vmparam.h> 47214903Sgonzo#include <machine/minidump.h> 48214903Sgonzo#include <machine/cache.h> 49214903Sgonzo 50214903SgonzoCTASSERT(sizeof(struct kerneldumpheader) == 512); 51214903Sgonzo 52214903Sgonzo/* 53214903Sgonzo * Don't touch the first SIZEOF_METADATA bytes on the dump device. This 54214903Sgonzo * is to protect us from metadata and to protect metadata from us. 55214903Sgonzo */ 56214903Sgonzo#define SIZEOF_METADATA (64*1024) 57214903Sgonzo 58214903Sgonzouint32_t *vm_page_dump; 59214903Sgonzoint vm_page_dump_size; 60214903Sgonzo 61214903Sgonzostatic struct kerneldumpheader kdh; 62214903Sgonzostatic off_t dumplo; 63214903Sgonzostatic off_t origdumplo; 64214903Sgonzo 65214903Sgonzo/* Handle chunked writes. */ 66214903Sgonzostatic uint64_t counter, progress; 67214903Sgonzo/* Just auxiliary bufffer */ 68214903Sgonzostatic char tmpbuffer[PAGE_SIZE]; 69214903Sgonzo 70214903Sgonzoextern pd_entry_t *kernel_segmap; 71214903Sgonzo 72214903SgonzoCTASSERT(sizeof(*vm_page_dump) == 4); 73214903Sgonzo 74214903Sgonzostatic int 75214903Sgonzois_dumpable(vm_paddr_t pa) 76214903Sgonzo{ 77214903Sgonzo int i; 78214903Sgonzo 79214903Sgonzo for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) { 80214903Sgonzo if (pa >= dump_avail[i] && pa < dump_avail[i + 1]) 81214903Sgonzo return (1); 82214903Sgonzo } 83214903Sgonzo return (0); 84214903Sgonzo} 85214903Sgonzo 86216148Sjchandravoid 87214903Sgonzodump_add_page(vm_paddr_t pa) 88214903Sgonzo{ 89214903Sgonzo int idx, bit; 90214903Sgonzo 91214903Sgonzo pa >>= PAGE_SHIFT; 92214903Sgonzo idx = pa >> 5; /* 2^5 = 32 */ 93214903Sgonzo bit = pa & 31; 94214903Sgonzo atomic_set_int(&vm_page_dump[idx], 1ul << bit); 95214903Sgonzo} 96214903Sgonzo 97216148Sjchandravoid 98214903Sgonzodump_drop_page(vm_paddr_t pa) 99214903Sgonzo{ 100214903Sgonzo int idx, bit; 101214903Sgonzo 102214903Sgonzo pa >>= PAGE_SHIFT; 103214903Sgonzo idx = pa >> 5; /* 2^5 = 32 */ 104214903Sgonzo bit = pa & 31; 105214903Sgonzo atomic_clear_int(&vm_page_dump[idx], 1ul << bit); 106214903Sgonzo} 107214903Sgonzo 108214903Sgonzo#define PG2MB(pgs) (((pgs) + (1 << 8) - 1) >> 8) 109214903Sgonzo 110214903Sgonzostatic int 111214903Sgonzowrite_buffer(struct dumperinfo *di, char *ptr, size_t sz) 112214903Sgonzo{ 113214903Sgonzo size_t len; 114214903Sgonzo int error, c; 115214903Sgonzo u_int maxdumpsz; 116214903Sgonzo 117214903Sgonzo maxdumpsz = di->maxiosize; 118214903Sgonzo 119214903Sgonzo if (maxdumpsz == 0) /* seatbelt */ 120214903Sgonzo maxdumpsz = PAGE_SIZE; 121214903Sgonzo 122214903Sgonzo error = 0; 123214903Sgonzo 124214903Sgonzo while (sz) { 125214903Sgonzo len = min(maxdumpsz, sz); 126214903Sgonzo counter += len; 127214903Sgonzo progress -= len; 128214903Sgonzo 129214903Sgonzo if (counter >> 22) { 130214903Sgonzo printf(" %jd", PG2MB(progress >> PAGE_SHIFT)); 131214903Sgonzo counter &= (1<<22) - 1; 132214903Sgonzo } 133214903Sgonzo 134214903Sgonzo if (ptr) { 135214903Sgonzo error = dump_write(di, ptr, 0, dumplo, len); 136214903Sgonzo if (error) 137214903Sgonzo return (error); 138214903Sgonzo dumplo += len; 139214903Sgonzo ptr += len; 140214903Sgonzo sz -= len; 141214903Sgonzo } else { 142214903Sgonzo panic("pa is not supported"); 143214903Sgonzo } 144214903Sgonzo 145214903Sgonzo /* Check for user abort. */ 146214903Sgonzo c = cncheckc(); 147214903Sgonzo if (c == 0x03) 148214903Sgonzo return (ECANCELED); 149214903Sgonzo if (c != -1) 150214903Sgonzo printf(" (CTRL-C to abort) "); 151214903Sgonzo } 152214903Sgonzo 153214903Sgonzo return (0); 154214903Sgonzo} 155214903Sgonzo 156214903Sgonzovoid 157214903Sgonzominidumpsys(struct dumperinfo *di) 158214903Sgonzo{ 159214903Sgonzo struct minidumphdr mdhdr; 160214903Sgonzo uint64_t dumpsize; 161214903Sgonzo uint32_t ptesize; 162214903Sgonzo uint32_t bits; 163214903Sgonzo vm_paddr_t pa; 164214903Sgonzo vm_offset_t prev_pte = 0; 165214903Sgonzo uint32_t count = 0; 166214903Sgonzo vm_offset_t va; 167214903Sgonzo pt_entry_t *pte; 168214903Sgonzo int i, bit, error; 169214903Sgonzo void *dump_va; 170214903Sgonzo 171214903Sgonzo /* Flush cache */ 172214903Sgonzo mips_dcache_wbinv_all(); 173214903Sgonzo 174214903Sgonzo counter = 0; 175214903Sgonzo /* Walk page table pages, set bits in vm_page_dump */ 176214903Sgonzo ptesize = 0; 177214903Sgonzo 178214903Sgonzo for (va = VM_MIN_KERNEL_ADDRESS; va < kernel_vm_end; va += NBPDR) { 179214903Sgonzo ptesize += PAGE_SIZE; 180214903Sgonzo pte = pmap_pte(kernel_pmap, va); 181214903Sgonzo KASSERT(pte != NULL, ("pte for %jx is NULL", (uintmax_t)va)); 182214903Sgonzo for (i = 0; i < NPTEPG; i++) { 183214903Sgonzo if (pte_test(&pte[i], PTE_V)) { 184214903Sgonzo pa = TLBLO_PTE_TO_PA(pte[i]); 185214903Sgonzo if (is_dumpable(pa)) 186214903Sgonzo dump_add_page(pa); 187214903Sgonzo } 188214903Sgonzo } 189214903Sgonzo } 190214903Sgonzo 191214903Sgonzo /* 192214903Sgonzo * Now mark pages from 0 to phys_avail[0], that's where kernel 193214903Sgonzo * and pages allocated by pmap_steal reside 194214903Sgonzo */ 195214903Sgonzo for (pa = 0; pa < phys_avail[0]; pa += PAGE_SIZE) { 196214903Sgonzo if (is_dumpable(pa)) 197214903Sgonzo dump_add_page(pa); 198214903Sgonzo } 199214903Sgonzo 200214903Sgonzo /* Calculate dump size. */ 201214903Sgonzo dumpsize = ptesize; 202214903Sgonzo dumpsize += round_page(msgbufp->msg_size); 203214903Sgonzo dumpsize += round_page(vm_page_dump_size); 204214903Sgonzo 205214903Sgonzo for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { 206214903Sgonzo bits = vm_page_dump[i]; 207214903Sgonzo while (bits) { 208214903Sgonzo bit = ffs(bits) - 1; 209214903Sgonzo pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + 210214903Sgonzo bit) * PAGE_SIZE; 211214903Sgonzo /* Clear out undumpable pages now if needed */ 212214903Sgonzo if (is_dumpable(pa)) 213214903Sgonzo dumpsize += PAGE_SIZE; 214214903Sgonzo else 215214903Sgonzo dump_drop_page(pa); 216214903Sgonzo bits &= ~(1ul << bit); 217214903Sgonzo } 218214903Sgonzo } 219214903Sgonzo 220214903Sgonzo dumpsize += PAGE_SIZE; 221214903Sgonzo 222214903Sgonzo /* Determine dump offset on device. */ 223214903Sgonzo if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { 224214903Sgonzo error = ENOSPC; 225214903Sgonzo goto fail; 226214903Sgonzo } 227214903Sgonzo 228214903Sgonzo origdumplo = dumplo = di->mediaoffset + di->mediasize - dumpsize; 229214903Sgonzo dumplo -= sizeof(kdh) * 2; 230214903Sgonzo progress = dumpsize; 231214903Sgonzo 232214903Sgonzo /* Initialize mdhdr */ 233214903Sgonzo bzero(&mdhdr, sizeof(mdhdr)); 234214903Sgonzo strcpy(mdhdr.magic, MINIDUMP_MAGIC); 235214903Sgonzo mdhdr.version = MINIDUMP_VERSION; 236214903Sgonzo mdhdr.msgbufsize = msgbufp->msg_size; 237214903Sgonzo mdhdr.bitmapsize = vm_page_dump_size; 238214903Sgonzo mdhdr.ptesize = ptesize; 239214903Sgonzo mdhdr.kernbase = VM_MIN_KERNEL_ADDRESS; 240214903Sgonzo 241214903Sgonzo mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_MIPS_VERSION, dumpsize, 242214903Sgonzo di->blocksize); 243214903Sgonzo 244214903Sgonzo printf("Physical memory: %ju MB\n", 245214903Sgonzo (uintmax_t)ptoa((uintmax_t)physmem) / 1048576); 246214903Sgonzo printf("Dumping %llu MB:", (long long)dumpsize >> 20); 247214903Sgonzo 248214903Sgonzo /* Dump leader */ 249214903Sgonzo error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); 250214903Sgonzo if (error) 251214903Sgonzo goto fail; 252214903Sgonzo dumplo += sizeof(kdh); 253214903Sgonzo 254214903Sgonzo /* Dump my header */ 255214903Sgonzo bzero(tmpbuffer, sizeof(tmpbuffer)); 256214903Sgonzo bcopy(&mdhdr, tmpbuffer, sizeof(mdhdr)); 257214903Sgonzo error = write_buffer(di, tmpbuffer, PAGE_SIZE); 258214903Sgonzo if (error) 259214903Sgonzo goto fail; 260214903Sgonzo 261214903Sgonzo /* Dump msgbuf up front */ 262214903Sgonzo error = write_buffer(di, (char *)msgbufp->msg_ptr, 263214903Sgonzo round_page(msgbufp->msg_size)); 264214903Sgonzo if (error) 265214903Sgonzo goto fail; 266214903Sgonzo 267214903Sgonzo /* Dump bitmap */ 268214903Sgonzo error = write_buffer(di, (char *)vm_page_dump, 269214903Sgonzo round_page(vm_page_dump_size)); 270214903Sgonzo if (error) 271214903Sgonzo goto fail; 272214903Sgonzo 273214903Sgonzo /* Dump kernel page table pages */ 274214903Sgonzo for (va = VM_MIN_KERNEL_ADDRESS; va < kernel_vm_end; va += NBPDR) { 275214903Sgonzo pte = pmap_pte(kernel_pmap, va); 276214903Sgonzo KASSERT(pte != NULL, ("pte for %jx is NULL", (uintmax_t)va)); 277214903Sgonzo if (!count) { 278214903Sgonzo prev_pte = (vm_offset_t)pte; 279214903Sgonzo count++; 280214903Sgonzo } 281214903Sgonzo else { 282214903Sgonzo if ((vm_offset_t)pte == (prev_pte + count * PAGE_SIZE)) 283214903Sgonzo count++; 284214903Sgonzo else { 285214903Sgonzo error = write_buffer(di, (char*)prev_pte, 286214903Sgonzo count * PAGE_SIZE); 287214903Sgonzo if (error) 288214903Sgonzo goto fail; 289214903Sgonzo count = 1; 290214903Sgonzo prev_pte = (vm_offset_t)pte; 291214903Sgonzo } 292214903Sgonzo } 293214903Sgonzo } 294214903Sgonzo 295214903Sgonzo if (count) { 296214903Sgonzo error = write_buffer(di, (char*)prev_pte, count * PAGE_SIZE); 297214903Sgonzo if (error) 298214903Sgonzo goto fail; 299214903Sgonzo count = 0; 300214903Sgonzo prev_pte = 0; 301214903Sgonzo } 302214903Sgonzo 303214903Sgonzo /* Dump memory chunks page by page*/ 304214903Sgonzo for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { 305214903Sgonzo bits = vm_page_dump[i]; 306214903Sgonzo while (bits) { 307214903Sgonzo bit = ffs(bits) - 1; 308214903Sgonzo pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + 309214903Sgonzo bit) * PAGE_SIZE; 310214903Sgonzo dump_va = pmap_kenter_temporary(pa, 0); 311214903Sgonzo error = write_buffer(di, dump_va, PAGE_SIZE); 312214903Sgonzo if (error) 313214903Sgonzo goto fail; 314214903Sgonzo pmap_kenter_temporary_free(pa); 315214903Sgonzo bits &= ~(1ul << bit); 316214903Sgonzo } 317214903Sgonzo } 318214903Sgonzo 319214903Sgonzo /* Dump trailer */ 320214903Sgonzo error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); 321214903Sgonzo if (error) 322214903Sgonzo goto fail; 323214903Sgonzo dumplo += sizeof(kdh); 324214903Sgonzo 325214903Sgonzo /* Signal completion, signoff and exit stage left. */ 326214903Sgonzo dump_write(di, NULL, 0, 0, 0); 327214903Sgonzo printf("\nDump complete\n"); 328214903Sgonzo return; 329214903Sgonzo 330214903Sgonzofail: 331214903Sgonzo if (error < 0) 332214903Sgonzo error = -error; 333214903Sgonzo 334214903Sgonzo if (error == ECANCELED) 335214903Sgonzo printf("\nDump aborted\n"); 336214903Sgonzo else if (error == ENOSPC) 337214903Sgonzo printf("\nDump failed. Partition too small.\n"); 338214903Sgonzo else 339214903Sgonzo printf("\n** DUMP FAILED (ERROR %d) **\n", error); 340214903Sgonzo} 341