kvm_minidump_mips.c revision 214904
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: head/lib/libkvm/kvm_minidump_mips.c 214904 2010-11-07 03:26:22Z gonzo $"); 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 u_long pa; 144 struct vmstate *vmst; 145 off_t off; 146 147 vmst = _kvm_malloc(kd, sizeof(*vmst)); 148 if (vmst == 0) { 149 _kvm_err(kd, kd->program, "cannot allocate vm"); 150 return (-1); 151 } 152 153 kd->vmst = vmst; 154 vmst->minidump = 1; 155 156 off = lseek(kd->pmfd, 0, SEEK_CUR); 157 if (pread(kd->pmfd, &vmst->hdr, 158 sizeof(vmst->hdr), 0) != sizeof(vmst->hdr)) { 159 _kvm_err(kd, kd->program, "cannot read dump header"); 160 return (-1); 161 } 162 163 if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic, 164 sizeof(vmst->hdr.magic)) != 0) { 165 _kvm_err(kd, kd->program, "not a minidump for this platform"); 166 return (-1); 167 } 168 if (vmst->hdr.version != MINIDUMP_VERSION) { 169 _kvm_err(kd, kd->program, "wrong minidump version. " 170 "Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version); 171 return (-1); 172 } 173 174 /* Skip header and msgbuf */ 175 off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize); 176 177 vmst->bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize); 178 if (vmst->bitmap == NULL) { 179 _kvm_err(kd, kd->program, "cannot allocate %d bytes for " 180 "bitmap", vmst->hdr.bitmapsize); 181 return (-1); 182 } 183 184 if (pread(kd->pmfd, vmst->bitmap, vmst->hdr.bitmapsize, off) != 185 vmst->hdr.bitmapsize) { 186 _kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap", 187 vmst->hdr.bitmapsize); 188 return (-1); 189 } 190 off += round_page(vmst->hdr.bitmapsize); 191 192 vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize); 193 if (vmst->ptemap == NULL) { 194 _kvm_err(kd, kd->program, "cannot allocate %d bytes for " 195 "ptemap", vmst->hdr.ptesize); 196 return (-1); 197 } 198 199 if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) != 200 vmst->hdr.ptesize) { 201 _kvm_err(kd, kd->program, "cannot read %d bytes for ptemap", 202 vmst->hdr.ptesize); 203 return (-1); 204 } 205 206 off += vmst->hdr.ptesize; 207 208 /* Build physical address hash table for sparse pages */ 209 inithash(kd, vmst->bitmap, vmst->hdr.bitmapsize, off); 210 211 return (0); 212} 213 214int 215_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa) 216{ 217 struct vmstate *vm; 218 pt_entry_t pte; 219 u_long offset, pteindex, a; 220 off_t ofs; 221 pt_entry_t *ptemap; 222 int i; 223 224 if (ISALIVE(kd)) { 225 _kvm_err(kd, 0, "kvm_kvatop called in live kernel!"); 226 return (0); 227 } 228 229 offset = va & PAGE_MASK; 230 /* Operate with page-aligned address */ 231 va &= ~PAGE_MASK; 232 233 vm = kd->vmst; 234 ptemap = vm->ptemap; 235 236#if defined(__mips_n64) 237 if (va >= MIPS_XKPHYS_START && va < MIPS_XKPHYS_END) 238 a = (MIPS_XKPHYS_TO_PHYS(va)); 239 else 240#endif 241 if (va >= MIPS_KSEG0_START && va < MIPS_KSEG0_END) 242 a = (MIPS_KSEG0_TO_PHYS(va)); 243 else if (va >= MIPS_KSEG1_START && va < MIPS_KSEG1_END) 244 a = (MIPS_KSEG1_TO_PHYS(va)); 245 else if (va >= vm->hdr.kernbase) { 246 pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT; 247 pte = ptemap[pteindex]; 248 if (!pte) { 249 _kvm_err(kd, kd->program, "_kvm_vatop: pte not valid"); 250 goto invalid; 251 } 252 253 a = TLBLO_PTE_TO_PA(pte); 254 255 } else { 256 _kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx " 257 "not minidumped", va); 258 return (0); 259 } 260 261 ofs = hpt_find(kd, a); 262 if (ofs == -1) { 263 _kvm_err(kd, kd->program, "_kvm_vatop: physical " 264 "address 0x%lx not in minidump", a); 265 goto invalid; 266 } 267 268 *pa = ofs + offset; 269 return (PAGE_SIZE - offset); 270 271 272invalid: 273 _kvm_err(kd, 0, "invalid address (0x%lx)", va); 274 return (0); 275} 276