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