minidump_machdep.c revision 214903
1166060Smarius/*- 2166060Smarius * Copyright (c) 2010 Oleksandr Tymoshenko <gonzo@freebsd.org> 3166060Smarius * Copyright (c) 2008 Semihalf, Grzegorz Bernacki 4166060Smarius * All rights reserved. 5166060Smarius * 6166060Smarius * Redistribution and use in source and binary forms, with or without 7166060Smarius * modification, are permitted provided that the following conditions 8166060Smarius * are met: 9166060Smarius * 10166060Smarius * 1. Redistributions of source code must retain the above copyright 11166060Smarius * notice, this list of conditions and the following disclaimer. 12166060Smarius * 2. Redistributions in binary form must reproduce the above copyright 13166060Smarius * notice, this list of conditions and the following disclaimer in the 14166060Smarius * documentation and/or other materials provided with the distribution. 15166060Smarius * 16166060Smarius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17166060Smarius * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18166060Smarius * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19166060Smarius * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20166060Smarius * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21166060Smarius * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22166060Smarius * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23166060Smarius * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24166060Smarius * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25166060Smarius * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26166060Smarius * 27166060Smarius * from: FreeBSD: src/sys/arm/arm/minidump_machdep.c v214223 28166060Smarius */ 29166060Smarius 30166060Smarius#include <sys/cdefs.h> 31166060Smarius__FBSDID("$FreeBSD: head/sys/mips/mips/minidump_machdep.c 214903 2010-11-07 03:09:02Z gonzo $"); 32166060Smarius 33166060Smarius#include <sys/param.h> 34166060Smarius#include <sys/systm.h> 35166060Smarius#include <sys/conf.h> 36166060Smarius#include <sys/cons.h> 37166060Smarius#include <sys/kernel.h> 38166060Smarius#include <sys/kerneldump.h> 39166060Smarius#include <sys/msgbuf.h> 40166060Smarius#include <vm/vm.h> 41166060Smarius#include <vm/pmap.h> 42166060Smarius#include <machine/pmap.h> 43166060Smarius#include <machine/atomic.h> 44178858Smarius#include <machine/elf.h> 45166060Smarius#include <machine/md_var.h> 46166060Smarius#include <machine/vmparam.h> 47166060Smarius#include <machine/minidump.h> 48166060Smarius#include <machine/cache.h> 49166060Smarius 50166060SmariusCTASSERT(sizeof(struct kerneldumpheader) == 512); 51166060Smarius 52166060Smarius/* 53166060Smarius * Don't touch the first SIZEOF_METADATA bytes on the dump device. This 54166060Smarius * is to protect us from metadata and to protect metadata from us. 55166060Smarius */ 56166060Smarius#define SIZEOF_METADATA (64*1024) 57166060Smarius 58166060Smariusuint32_t *vm_page_dump; 59166060Smariusint vm_page_dump_size; 60166060Smarius 61166060Smariusstatic struct kerneldumpheader kdh; 62166060Smariusstatic off_t dumplo; 63166060Smariusstatic off_t origdumplo; 64166060Smarius 65166060Smarius/* Handle chunked writes. */ 66172066Smariusstatic uint64_t counter, progress; 67166060Smarius/* Just auxiliary bufffer */ 68166060Smariusstatic char tmpbuffer[PAGE_SIZE]; 69166060Smarius 70166060Smariusextern pd_entry_t *kernel_segmap; 71166060Smarius 72166060SmariusCTASSERT(sizeof(*vm_page_dump) == 4); 73166060Smarius 74166060Smariusstatic int 75166060Smariusis_dumpable(vm_paddr_t pa) 76166060Smarius{ 77166060Smarius int i; 78166060Smarius 79166060Smarius for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) { 80166060Smarius if (pa >= dump_avail[i] && pa < dump_avail[i + 1]) 81166060Smarius return (1); 82166060Smarius } 83166060Smarius return (0); 84166060Smarius} 85166060Smarius 86166060Smariusstatic void 87166060Smariusdump_add_page(vm_paddr_t pa) 88166060Smarius{ 89166060Smarius int idx, bit; 90166060Smarius 91166060Smarius pa >>= PAGE_SHIFT; 92166060Smarius idx = pa >> 5; /* 2^5 = 32 */ 93166060Smarius bit = pa & 31; 94166060Smarius atomic_set_int(&vm_page_dump[idx], 1ul << bit); 95166060Smarius} 96166060Smarius 97166060Smariusstatic void 98166060Smariusdump_drop_page(vm_paddr_t pa) 99166060Smarius{ 100166060Smarius int idx, bit; 101166060Smarius 102166060Smarius pa >>= PAGE_SHIFT; 103166060Smarius idx = pa >> 5; /* 2^5 = 32 */ 104166060Smarius bit = pa & 31; 105166060Smarius atomic_clear_int(&vm_page_dump[idx], 1ul << bit); 106166060Smarius} 107166060Smarius 108166060Smarius#define PG2MB(pgs) (((pgs) + (1 << 8) - 1) >> 8) 109172066Smarius 110172066Smariusstatic int 111178443Smariuswrite_buffer(struct dumperinfo *di, char *ptr, size_t sz) 112166060Smarius{ 113166060Smarius size_t len; 114166060Smarius int error, c; 115166060Smarius u_int maxdumpsz; 116166060Smarius 117166060Smarius maxdumpsz = di->maxiosize; 118166060Smarius 119166060Smarius if (maxdumpsz == 0) /* seatbelt */ 120166060Smarius maxdumpsz = PAGE_SIZE; 121166060Smarius 122166060Smarius error = 0; 123166060Smarius 124166060Smarius while (sz) { 125166060Smarius len = min(maxdumpsz, sz); 126166060Smarius counter += len; 127166060Smarius progress -= len; 128166060Smarius 129166060Smarius if (counter >> 22) { 130166060Smarius printf(" %jd", PG2MB(progress >> PAGE_SHIFT)); 131166060Smarius counter &= (1<<22) - 1; 132166060Smarius } 133166060Smarius 134166060Smarius if (ptr) { 135166060Smarius error = dump_write(di, ptr, 0, dumplo, len); 136166060Smarius if (error) 137166060Smarius return (error); 138190114Smarius dumplo += len; 139166060Smarius ptr += len; 140166060Smarius sz -= len; 141166060Smarius } else { 142166060Smarius panic("pa is not supported"); 143166060Smarius } 144166060Smarius 145166060Smarius /* Check for user abort. */ 146166060Smarius c = cncheckc(); 147166060Smarius if (c == 0x03) 148190098Smarius return (ECANCELED); 149166060Smarius if (c != -1) 150166060Smarius printf(" (CTRL-C to abort) "); 151166060Smarius } 152166060Smarius 153166060Smarius return (0); 154166060Smarius} 155166060Smarius 156172066Smariusvoid 157172066Smariusminidumpsys(struct dumperinfo *di) 158172066Smarius{ 159178443Smarius struct minidumphdr mdhdr; 160172066Smarius uint64_t dumpsize; 161172066Smarius uint32_t ptesize; 162172066Smarius uint32_t bits; 163172066Smarius vm_paddr_t pa; 164172066Smarius vm_offset_t prev_pte = 0; 165172066Smarius uint32_t count = 0; 166172066Smarius vm_offset_t va; 167172066Smarius pt_entry_t *pte; 168172066Smarius int i, bit, error; 169166060Smarius void *dump_va; 170166060Smarius 171166060Smarius /* Flush cache */ 172166060Smarius mips_dcache_wbinv_all(); 173166060Smarius 174166060Smarius counter = 0; 175166060Smarius /* Walk page table pages, set bits in vm_page_dump */ 176166060Smarius ptesize = 0; 177166060Smarius 178166060Smarius for (va = VM_MIN_KERNEL_ADDRESS; va < kernel_vm_end; va += NBPDR) { 179166060Smarius ptesize += PAGE_SIZE; 180166060Smarius pte = pmap_pte(kernel_pmap, va); 181166060Smarius KASSERT(pte != NULL, ("pte for %jx is NULL", (uintmax_t)va)); 182166060Smarius for (i = 0; i < NPTEPG; i++) { 183166060Smarius if (pte_test(&pte[i], PTE_V)) { 184166060Smarius pa = TLBLO_PTE_TO_PA(pte[i]); 185166060Smarius if (is_dumpable(pa)) 186166060Smarius dump_add_page(pa); 187172066Smarius } 188166060Smarius } 189166060Smarius } 190166060Smarius 191166060Smarius /* 192172066Smarius * Now mark pages from 0 to phys_avail[0], that's where kernel 193166060Smarius * and pages allocated by pmap_steal reside 194169175Smarius */ 195169175Smarius for (pa = 0; pa < phys_avail[0]; pa += PAGE_SIZE) { 196172066Smarius if (is_dumpable(pa)) 197166060Smarius dump_add_page(pa); 198166060Smarius } 199166060Smarius 200166060Smarius /* Calculate dump size. */ 201166060Smarius dumpsize = ptesize; 202166060Smarius dumpsize += round_page(msgbufp->msg_size); 203169175Smarius dumpsize += round_page(vm_page_dump_size); 204169175Smarius 205169175Smarius for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { 206169175Smarius bits = vm_page_dump[i]; 207166060Smarius while (bits) { 208166060Smarius bit = ffs(bits) - 1; 209169175Smarius pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + 210169175Smarius bit) * PAGE_SIZE; 211166060Smarius /* Clear out undumpable pages now if needed */ 212166060Smarius if (is_dumpable(pa)) 213166060Smarius dumpsize += PAGE_SIZE; 214166060Smarius else 215166060Smarius dump_drop_page(pa); 216166060Smarius bits &= ~(1ul << bit); 217166060Smarius } 218166060Smarius } 219166060Smarius 220166060Smarius dumpsize += PAGE_SIZE; 221166060Smarius 222166060Smarius /* Determine dump offset on device. */ 223169175Smarius if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { 224169175Smarius error = ENOSPC; 225169175Smarius goto fail; 226169175Smarius } 227169175Smarius 228169175Smarius origdumplo = dumplo = di->mediaoffset + di->mediasize - dumpsize; 229169175Smarius dumplo -= sizeof(kdh) * 2; 230166060Smarius progress = dumpsize; 231166060Smarius 232166060Smarius /* Initialize mdhdr */ 233166060Smarius bzero(&mdhdr, sizeof(mdhdr)); 234166060Smarius strcpy(mdhdr.magic, MINIDUMP_MAGIC); 235169175Smarius mdhdr.version = MINIDUMP_VERSION; 236166060Smarius mdhdr.msgbufsize = msgbufp->msg_size; 237169175Smarius mdhdr.bitmapsize = vm_page_dump_size; 238169175Smarius mdhdr.ptesize = ptesize; 239166060Smarius mdhdr.kernbase = VM_MIN_KERNEL_ADDRESS; 240169175Smarius 241169175Smarius mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_MIPS_VERSION, dumpsize, 242166060Smarius di->blocksize); 243169175Smarius 244166060Smarius printf("Physical memory: %ju MB\n", 245190098Smarius (uintmax_t)ptoa((uintmax_t)physmem) / 1048576); 246166060Smarius printf("Dumping %llu MB:", (long long)dumpsize >> 20); 247166060Smarius 248166060Smarius /* Dump leader */ 249166060Smarius error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); 250166060Smarius if (error) 251166060Smarius goto fail; 252166060Smarius dumplo += sizeof(kdh); 253166060Smarius 254169175Smarius /* Dump my header */ 255169175Smarius bzero(tmpbuffer, sizeof(tmpbuffer)); 256166060Smarius bcopy(&mdhdr, tmpbuffer, sizeof(mdhdr)); 257169175Smarius error = write_buffer(di, tmpbuffer, PAGE_SIZE); 258166060Smarius if (error) 259166060Smarius goto fail; 260169175Smarius 261169175Smarius /* Dump msgbuf up front */ 262169175Smarius error = write_buffer(di, (char *)msgbufp->msg_ptr, 263166060Smarius round_page(msgbufp->msg_size)); 264166060Smarius if (error) 265166060Smarius goto fail; 266166060Smarius 267166060Smarius /* Dump bitmap */ 268166060Smarius error = write_buffer(di, (char *)vm_page_dump, 269166060Smarius round_page(vm_page_dump_size)); 270166060Smarius if (error) 271166060Smarius goto fail; 272166060Smarius 273169175Smarius /* Dump kernel page table pages */ 274166060Smarius for (va = VM_MIN_KERNEL_ADDRESS; va < kernel_vm_end; va += NBPDR) { 275169175Smarius pte = pmap_pte(kernel_pmap, va); 276166060Smarius KASSERT(pte != NULL, ("pte for %jx is NULL", (uintmax_t)va)); 277166060Smarius if (!count) { 278166060Smarius prev_pte = (vm_offset_t)pte; 279172066Smarius count++; 280172066Smarius } 281166060Smarius else { 282166060Smarius if ((vm_offset_t)pte == (prev_pte + count * PAGE_SIZE)) 283166060Smarius count++; 284166060Smarius else { 285166060Smarius error = write_buffer(di, (char*)prev_pte, 286166060Smarius count * PAGE_SIZE); 287166060Smarius if (error) 288166060Smarius goto fail; 289166060Smarius count = 1; 290166060Smarius prev_pte = (vm_offset_t)pte; 291190098Smarius } 292172066Smarius } 293172066Smarius } 294190098Smarius 295172066Smarius if (count) { 296172066Smarius error = write_buffer(di, (char*)prev_pte, count * PAGE_SIZE); 297172066Smarius if (error) 298172066Smarius goto fail; 299172066Smarius count = 0; 300172066Smarius prev_pte = 0; 301172066Smarius } 302172066Smarius 303172066Smarius /* Dump memory chunks page by page*/ 304172066Smarius for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { 305172066Smarius bits = vm_page_dump[i]; 306172066Smarius while (bits) { 307172066Smarius bit = ffs(bits) - 1; 308172066Smarius pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + 309172066Smarius bit) * PAGE_SIZE; 310172066Smarius dump_va = pmap_kenter_temporary(pa, 0); 311172066Smarius error = write_buffer(di, dump_va, PAGE_SIZE); 312172066Smarius if (error) 313172066Smarius goto fail; 314172066Smarius pmap_kenter_temporary_free(pa); 315172066Smarius bits &= ~(1ul << bit); 316172066Smarius } 317190098Smarius } 318190098Smarius 319190098Smarius /* Dump trailer */ 320190098Smarius error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); 321190098Smarius if (error) 322172066Smarius goto fail; 323172066Smarius dumplo += sizeof(kdh); 324166060Smarius 325166060Smarius /* Signal completion, signoff and exit stage left. */ 326166060Smarius dump_write(di, NULL, 0, 0, 0); 327166060Smarius printf("\nDump complete\n"); 328166060Smarius return; 329166060Smarius 330166060Smariusfail: 331166060Smarius if (error < 0) 332166060Smarius error = -error; 333166060Smarius 334166060Smarius if (error == ECANCELED) 335166060Smarius printf("\nDump aborted\n"); 336166060Smarius else if (error == ENOSPC) 337166060Smarius printf("\nDump failed. Partition too small.\n"); 338166060Smarius else 339166060Smarius printf("\n** DUMP FAILED (ERROR %d) **\n", error); 340166060Smarius} 341172066Smarius