1214904Sgonzo/*- 2214904Sgonzo * Copyright (c) 2010 Oleksandr Tymoshenko 3214904Sgonzo * Copyright (c) 2008 Semihalf, Grzegorz Bernacki 4214904Sgonzo * Copyright (c) 2006 Peter Wemm 5214904Sgonzo * 6214904Sgonzo * Redistribution and use in source and binary forms, with or without 7214904Sgonzo * modification, are permitted provided that the following conditions 8214904Sgonzo * are met: 9214904Sgonzo * 1. Redistributions of source code must retain the above copyright 10214904Sgonzo * notice, this list of conditions and the following disclaimer. 11214904Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 12214904Sgonzo * notice, this list of conditions and the following disclaimer in the 13214904Sgonzo * documentation and/or other materials provided with the distribution. 14214904Sgonzo * 15214904Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16214904Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17214904Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18214904Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19214904Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20214904Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21214904Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22214904Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23214904Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24214904Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25214904Sgonzo * SUCH DAMAGE. 26214904Sgonzo * 27214904Sgonzo * From: FreeBSD: src/lib/libkvm/kvm_minidump_arm.c r214223 28214904Sgonzo */ 29214904Sgonzo 30214904Sgonzo#include <sys/cdefs.h> 31214904Sgonzo__FBSDID("$FreeBSD: releng/10.2/lib/libkvm/kvm_minidump_mips.c 217744 2011-01-23 11:08:28Z uqs $"); 32214904Sgonzo 33214904Sgonzo/* 34214904Sgonzo * MIPS machine dependent routines for kvm and minidumps. 35214904Sgonzo */ 36214904Sgonzo 37214904Sgonzo#include <sys/param.h> 38214904Sgonzo#include <sys/user.h> 39214904Sgonzo#include <sys/proc.h> 40214904Sgonzo#include <sys/stat.h> 41214904Sgonzo#include <sys/mman.h> 42214904Sgonzo#include <sys/fnv_hash.h> 43214904Sgonzo#include <stdlib.h> 44214904Sgonzo#include <string.h> 45214904Sgonzo#include <unistd.h> 46214904Sgonzo#include <nlist.h> 47214904Sgonzo#include <kvm.h> 48214904Sgonzo 49214904Sgonzo#include <vm/vm.h> 50214904Sgonzo#include <vm/vm_param.h> 51214904Sgonzo 52214904Sgonzo#include <machine/elf.h> 53214904Sgonzo#include <machine/cpufunc.h> 54214904Sgonzo#include <machine/minidump.h> 55214904Sgonzo 56214904Sgonzo#include <limits.h> 57214904Sgonzo 58214904Sgonzo#include "kvm_private.h" 59214904Sgonzo 60214904Sgonzostruct hpte { 61214904Sgonzo struct hpte *next; 62214904Sgonzo uint64_t pa; 63214904Sgonzo int64_t off; 64214904Sgonzo}; 65214904Sgonzo 66214904Sgonzo#define HPT_SIZE 1024 67214904Sgonzo 68214904Sgonzo/* minidump must be the first field */ 69214904Sgonzostruct vmstate { 70214904Sgonzo int minidump; /* 1 = minidump mode */ 71214904Sgonzo struct minidumphdr hdr; 72214904Sgonzo void *hpt_head[HPT_SIZE]; 73214904Sgonzo uint32_t *bitmap; 74214904Sgonzo void *ptemap; 75214904Sgonzo}; 76214904Sgonzo 77214904Sgonzostatic void 78214904Sgonzohpt_insert(kvm_t *kd, uint64_t pa, int64_t off) 79214904Sgonzo{ 80214904Sgonzo struct hpte *hpte; 81214904Sgonzo uint32_t fnv = FNV1_32_INIT; 82214904Sgonzo 83214904Sgonzo fnv = fnv_32_buf(&pa, sizeof(pa), fnv); 84214904Sgonzo fnv &= (HPT_SIZE - 1); 85214904Sgonzo hpte = malloc(sizeof(*hpte)); 86214904Sgonzo hpte->pa = pa; 87214904Sgonzo hpte->off = off; 88214904Sgonzo hpte->next = kd->vmst->hpt_head[fnv]; 89214904Sgonzo kd->vmst->hpt_head[fnv] = hpte; 90214904Sgonzo} 91214904Sgonzo 92214904Sgonzostatic int64_t 93214904Sgonzohpt_find(kvm_t *kd, uint64_t pa) 94214904Sgonzo{ 95214904Sgonzo struct hpte *hpte; 96214904Sgonzo uint32_t fnv = FNV1_32_INIT; 97214904Sgonzo 98214904Sgonzo fnv = fnv_32_buf(&pa, sizeof(pa), fnv); 99214904Sgonzo fnv &= (HPT_SIZE - 1); 100214904Sgonzo for (hpte = kd->vmst->hpt_head[fnv]; hpte != NULL; hpte = hpte->next) 101214904Sgonzo if (pa == hpte->pa) 102214904Sgonzo return (hpte->off); 103214904Sgonzo 104214904Sgonzo return (-1); 105214904Sgonzo} 106214904Sgonzo 107214904Sgonzostatic int 108214904Sgonzoinithash(kvm_t *kd, uint32_t *base, int len, off_t off) 109214904Sgonzo{ 110214904Sgonzo uint64_t idx, pa; 111214904Sgonzo uint32_t bit, bits; 112214904Sgonzo 113214904Sgonzo for (idx = 0; idx < len / sizeof(*base); idx++) { 114214904Sgonzo bits = base[idx]; 115214904Sgonzo while (bits) { 116214904Sgonzo bit = ffs(bits) - 1; 117214904Sgonzo bits &= ~(1ul << bit); 118214904Sgonzo pa = (idx * sizeof(*base) * NBBY + bit) * PAGE_SIZE; 119214904Sgonzo hpt_insert(kd, pa, off); 120214904Sgonzo off += PAGE_SIZE; 121214904Sgonzo } 122214904Sgonzo } 123214904Sgonzo 124214904Sgonzo return (off); 125214904Sgonzo} 126214904Sgonzo 127214904Sgonzovoid 128214904Sgonzo_kvm_minidump_freevtop(kvm_t *kd) 129214904Sgonzo{ 130214904Sgonzo struct vmstate *vm = kd->vmst; 131214904Sgonzo 132214904Sgonzo if (vm->bitmap) 133214904Sgonzo free(vm->bitmap); 134214904Sgonzo if (vm->ptemap) 135214904Sgonzo free(vm->ptemap); 136214904Sgonzo free(vm); 137214904Sgonzo kd->vmst = NULL; 138214904Sgonzo} 139214904Sgonzo 140214904Sgonzoint 141214904Sgonzo_kvm_minidump_initvtop(kvm_t *kd) 142214904Sgonzo{ 143214904Sgonzo struct vmstate *vmst; 144214904Sgonzo off_t off; 145214904Sgonzo 146214904Sgonzo vmst = _kvm_malloc(kd, sizeof(*vmst)); 147214904Sgonzo if (vmst == 0) { 148214904Sgonzo _kvm_err(kd, kd->program, "cannot allocate vm"); 149214904Sgonzo return (-1); 150214904Sgonzo } 151214904Sgonzo 152214904Sgonzo kd->vmst = vmst; 153214904Sgonzo vmst->minidump = 1; 154214904Sgonzo 155214904Sgonzo off = lseek(kd->pmfd, 0, SEEK_CUR); 156214904Sgonzo if (pread(kd->pmfd, &vmst->hdr, 157214904Sgonzo sizeof(vmst->hdr), 0) != sizeof(vmst->hdr)) { 158214904Sgonzo _kvm_err(kd, kd->program, "cannot read dump header"); 159214904Sgonzo return (-1); 160214904Sgonzo } 161214904Sgonzo 162214904Sgonzo if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic, 163214904Sgonzo sizeof(vmst->hdr.magic)) != 0) { 164214904Sgonzo _kvm_err(kd, kd->program, "not a minidump for this platform"); 165214904Sgonzo return (-1); 166214904Sgonzo } 167214904Sgonzo if (vmst->hdr.version != MINIDUMP_VERSION) { 168214904Sgonzo _kvm_err(kd, kd->program, "wrong minidump version. " 169214904Sgonzo "Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version); 170214904Sgonzo return (-1); 171214904Sgonzo } 172214904Sgonzo 173214904Sgonzo /* Skip header and msgbuf */ 174214904Sgonzo off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize); 175214904Sgonzo 176214904Sgonzo vmst->bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize); 177214904Sgonzo if (vmst->bitmap == NULL) { 178214904Sgonzo _kvm_err(kd, kd->program, "cannot allocate %d bytes for " 179214904Sgonzo "bitmap", vmst->hdr.bitmapsize); 180214904Sgonzo return (-1); 181214904Sgonzo } 182214904Sgonzo 183214904Sgonzo if (pread(kd->pmfd, vmst->bitmap, vmst->hdr.bitmapsize, off) != 184217744Suqs (ssize_t)vmst->hdr.bitmapsize) { 185214904Sgonzo _kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap", 186214904Sgonzo vmst->hdr.bitmapsize); 187214904Sgonzo return (-1); 188214904Sgonzo } 189214904Sgonzo off += round_page(vmst->hdr.bitmapsize); 190214904Sgonzo 191214904Sgonzo vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize); 192214904Sgonzo if (vmst->ptemap == NULL) { 193214904Sgonzo _kvm_err(kd, kd->program, "cannot allocate %d bytes for " 194214904Sgonzo "ptemap", vmst->hdr.ptesize); 195214904Sgonzo return (-1); 196214904Sgonzo } 197214904Sgonzo 198214904Sgonzo if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) != 199217744Suqs (ssize_t)vmst->hdr.ptesize) { 200214904Sgonzo _kvm_err(kd, kd->program, "cannot read %d bytes for ptemap", 201214904Sgonzo vmst->hdr.ptesize); 202214904Sgonzo return (-1); 203214904Sgonzo } 204214904Sgonzo 205214904Sgonzo off += vmst->hdr.ptesize; 206214904Sgonzo 207214904Sgonzo /* Build physical address hash table for sparse pages */ 208214904Sgonzo inithash(kd, vmst->bitmap, vmst->hdr.bitmapsize, off); 209214904Sgonzo 210214904Sgonzo return (0); 211214904Sgonzo} 212214904Sgonzo 213214904Sgonzoint 214214904Sgonzo_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa) 215214904Sgonzo{ 216214904Sgonzo struct vmstate *vm; 217214904Sgonzo pt_entry_t pte; 218214904Sgonzo u_long offset, pteindex, a; 219214904Sgonzo off_t ofs; 220214904Sgonzo pt_entry_t *ptemap; 221214904Sgonzo 222214904Sgonzo if (ISALIVE(kd)) { 223214904Sgonzo _kvm_err(kd, 0, "kvm_kvatop called in live kernel!"); 224214904Sgonzo return (0); 225214904Sgonzo } 226214904Sgonzo 227214904Sgonzo offset = va & PAGE_MASK; 228214904Sgonzo /* Operate with page-aligned address */ 229214904Sgonzo va &= ~PAGE_MASK; 230214904Sgonzo 231214904Sgonzo vm = kd->vmst; 232214904Sgonzo ptemap = vm->ptemap; 233214904Sgonzo 234214904Sgonzo#if defined(__mips_n64) 235214904Sgonzo if (va >= MIPS_XKPHYS_START && va < MIPS_XKPHYS_END) 236214904Sgonzo a = (MIPS_XKPHYS_TO_PHYS(va)); 237214904Sgonzo else 238214904Sgonzo#endif 239217744Suqs if (va >= (u_long)MIPS_KSEG0_START && va < (u_long)MIPS_KSEG0_END) 240214904Sgonzo a = (MIPS_KSEG0_TO_PHYS(va)); 241217744Suqs else if (va >= (u_long)MIPS_KSEG1_START && va < (u_long)MIPS_KSEG1_END) 242214904Sgonzo a = (MIPS_KSEG1_TO_PHYS(va)); 243214904Sgonzo else if (va >= vm->hdr.kernbase) { 244214904Sgonzo pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT; 245214904Sgonzo pte = ptemap[pteindex]; 246214904Sgonzo if (!pte) { 247214904Sgonzo _kvm_err(kd, kd->program, "_kvm_vatop: pte not valid"); 248214904Sgonzo goto invalid; 249214904Sgonzo } 250214904Sgonzo 251214904Sgonzo a = TLBLO_PTE_TO_PA(pte); 252214904Sgonzo 253214904Sgonzo } else { 254214904Sgonzo _kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx " 255214904Sgonzo "not minidumped", va); 256214904Sgonzo return (0); 257214904Sgonzo } 258214904Sgonzo 259214904Sgonzo ofs = hpt_find(kd, a); 260214904Sgonzo if (ofs == -1) { 261214904Sgonzo _kvm_err(kd, kd->program, "_kvm_vatop: physical " 262214904Sgonzo "address 0x%lx not in minidump", a); 263214904Sgonzo goto invalid; 264214904Sgonzo } 265214904Sgonzo 266214904Sgonzo *pa = ofs + offset; 267214904Sgonzo return (PAGE_SIZE - offset); 268214904Sgonzo 269214904Sgonzo 270214904Sgonzoinvalid: 271214904Sgonzo _kvm_err(kd, 0, "invalid address (0x%lx)", va); 272214904Sgonzo return (0); 273214904Sgonzo} 274