1/*- 2 * Copyright (c) 2010 Oleksandr Tymoshenko 3 * Copyright (c) 2008 Semihalf, Grzegorz Bernacki 4 * Copyright (c) 2006 Peter Wemm 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 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * From: FreeBSD: src/lib/libkvm/kvm_minidump_arm.c r214223 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: releng/10.2/lib/libkvm/kvm_minidump_mips.c 217744 2011-01-23 11:08:28Z uqs $"); 32 33/* 34 * MIPS machine dependent routines for kvm and minidumps. 35 */ 36 37#include <sys/param.h> 38#include <sys/user.h> 39#include <sys/proc.h> 40#include <sys/stat.h> 41#include <sys/mman.h> 42#include <sys/fnv_hash.h> 43#include <stdlib.h> 44#include <string.h> 45#include <unistd.h> 46#include <nlist.h> 47#include <kvm.h> 48 49#include <vm/vm.h> 50#include <vm/vm_param.h> 51 52#include <machine/elf.h> 53#include <machine/cpufunc.h> 54#include <machine/minidump.h> 55 56#include <limits.h> 57 58#include "kvm_private.h" 59 60struct hpte { 61 struct hpte *next; 62 uint64_t pa; 63 int64_t off; 64}; 65 66#define HPT_SIZE 1024 67 68/* minidump must be the first field */ 69struct vmstate { 70 int minidump; /* 1 = minidump mode */ 71 struct minidumphdr hdr; 72 void *hpt_head[HPT_SIZE]; 73 uint32_t *bitmap; 74 void *ptemap; 75}; 76 77static void 78hpt_insert(kvm_t *kd, uint64_t pa, int64_t off) 79{ 80 struct hpte *hpte; 81 uint32_t fnv = FNV1_32_INIT; 82 83 fnv = fnv_32_buf(&pa, sizeof(pa), fnv); 84 fnv &= (HPT_SIZE - 1); 85 hpte = malloc(sizeof(*hpte)); 86 hpte->pa = pa; 87 hpte->off = off; 88 hpte->next = kd->vmst->hpt_head[fnv]; 89 kd->vmst->hpt_head[fnv] = hpte; 90} 91 92static int64_t 93hpt_find(kvm_t *kd, uint64_t pa) 94{ 95 struct hpte *hpte; 96 uint32_t fnv = FNV1_32_INIT; 97 98 fnv = fnv_32_buf(&pa, sizeof(pa), fnv); 99 fnv &= (HPT_SIZE - 1); 100 for (hpte = kd->vmst->hpt_head[fnv]; hpte != NULL; hpte = hpte->next) 101 if (pa == hpte->pa) 102 return (hpte->off); 103 104 return (-1); 105} 106 107static int 108inithash(kvm_t *kd, uint32_t *base, int len, off_t off) 109{ 110 uint64_t idx, pa; 111 uint32_t bit, bits; 112 113 for (idx = 0; idx < len / sizeof(*base); idx++) { 114 bits = base[idx]; 115 while (bits) { 116 bit = ffs(bits) - 1; 117 bits &= ~(1ul << bit); 118 pa = (idx * sizeof(*base) * NBBY + bit) * PAGE_SIZE; 119 hpt_insert(kd, pa, off); 120 off += PAGE_SIZE; 121 } 122 } 123 124 return (off); 125} 126 127void 128_kvm_minidump_freevtop(kvm_t *kd) 129{ 130 struct vmstate *vm = kd->vmst; 131 132 if (vm->bitmap) 133 free(vm->bitmap); 134 if (vm->ptemap) 135 free(vm->ptemap); 136 free(vm); 137 kd->vmst = NULL; 138} 139 140int 141_kvm_minidump_initvtop(kvm_t *kd) 142{ 143 struct vmstate *vmst; 144 off_t off; 145 146 vmst = _kvm_malloc(kd, sizeof(*vmst)); 147 if (vmst == 0) { 148 _kvm_err(kd, kd->program, "cannot allocate vm"); 149 return (-1); 150 } 151 152 kd->vmst = vmst; 153 vmst->minidump = 1; 154 155 off = lseek(kd->pmfd, 0, SEEK_CUR); 156 if (pread(kd->pmfd, &vmst->hdr, 157 sizeof(vmst->hdr), 0) != sizeof(vmst->hdr)) { 158 _kvm_err(kd, kd->program, "cannot read dump header"); 159 return (-1); 160 } 161 162 if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic, 163 sizeof(vmst->hdr.magic)) != 0) { 164 _kvm_err(kd, kd->program, "not a minidump for this platform"); 165 return (-1); 166 } 167 if (vmst->hdr.version != MINIDUMP_VERSION) { 168 _kvm_err(kd, kd->program, "wrong minidump version. " 169 "Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version); 170 return (-1); 171 } 172 173 /* Skip header and msgbuf */ 174 off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize); 175 176 vmst->bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize); 177 if (vmst->bitmap == NULL) { 178 _kvm_err(kd, kd->program, "cannot allocate %d bytes for " 179 "bitmap", vmst->hdr.bitmapsize); 180 return (-1); 181 } 182 183 if (pread(kd->pmfd, vmst->bitmap, vmst->hdr.bitmapsize, off) != 184 (ssize_t)vmst->hdr.bitmapsize) { 185 _kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap", 186 vmst->hdr.bitmapsize); 187 return (-1); 188 } 189 off += round_page(vmst->hdr.bitmapsize); 190 191 vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize); 192 if (vmst->ptemap == NULL) { 193 _kvm_err(kd, kd->program, "cannot allocate %d bytes for " 194 "ptemap", vmst->hdr.ptesize); 195 return (-1); 196 } 197 198 if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) != 199 (ssize_t)vmst->hdr.ptesize) { 200 _kvm_err(kd, kd->program, "cannot read %d bytes for ptemap", 201 vmst->hdr.ptesize); 202 return (-1); 203 } 204 205 off += vmst->hdr.ptesize; 206 207 /* Build physical address hash table for sparse pages */ 208 inithash(kd, vmst->bitmap, vmst->hdr.bitmapsize, off); 209 210 return (0); 211} 212 213int 214_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa) 215{ 216 struct vmstate *vm; 217 pt_entry_t pte; 218 u_long offset, pteindex, a; 219 off_t ofs; 220 pt_entry_t *ptemap; 221 222 if (ISALIVE(kd)) { 223 _kvm_err(kd, 0, "kvm_kvatop called in live kernel!"); 224 return (0); 225 } 226 227 offset = va & PAGE_MASK; 228 /* Operate with page-aligned address */ 229 va &= ~PAGE_MASK; 230 231 vm = kd->vmst; 232 ptemap = vm->ptemap; 233 234#if defined(__mips_n64) 235 if (va >= MIPS_XKPHYS_START && va < MIPS_XKPHYS_END) 236 a = (MIPS_XKPHYS_TO_PHYS(va)); 237 else 238#endif 239 if (va >= (u_long)MIPS_KSEG0_START && va < (u_long)MIPS_KSEG0_END) 240 a = (MIPS_KSEG0_TO_PHYS(va)); 241 else if (va >= (u_long)MIPS_KSEG1_START && va < (u_long)MIPS_KSEG1_END) 242 a = (MIPS_KSEG1_TO_PHYS(va)); 243 else if (va >= vm->hdr.kernbase) { 244 pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT; 245 pte = ptemap[pteindex]; 246 if (!pte) { 247 _kvm_err(kd, kd->program, "_kvm_vatop: pte not valid"); 248 goto invalid; 249 } 250 251 a = TLBLO_PTE_TO_PA(pte); 252 253 } else { 254 _kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx " 255 "not minidumped", va); 256 return (0); 257 } 258 259 ofs = hpt_find(kd, a); 260 if (ofs == -1) { 261 _kvm_err(kd, kd->program, "_kvm_vatop: physical " 262 "address 0x%lx not in minidump", a); 263 goto invalid; 264 } 265 266 *pa = ofs + offset; 267 return (PAGE_SIZE - offset); 268 269 270invalid: 271 _kvm_err(kd, 0, "invalid address (0x%lx)", va); 272 return (0); 273} 274