kvm_minidump_mips.c revision 291372
1239310Sdim/*- 2239310Sdim * Copyright (c) 2010 Oleksandr Tymoshenko 3239310Sdim * Copyright (c) 2008 Semihalf, Grzegorz Bernacki 4239310Sdim * Copyright (c) 2006 Peter Wemm 5239310Sdim * 6239310Sdim * Redistribution and use in source and binary forms, with or without 7239310Sdim * modification, are permitted provided that the following conditions 8239310Sdim * are met: 9239310Sdim * 1. Redistributions of source code must retain the above copyright 10239310Sdim * notice, this list of conditions and the following disclaimer. 11239310Sdim * 2. Redistributions in binary form must reproduce the above copyright 12239310Sdim * notice, this list of conditions and the following disclaimer in the 13239310Sdim * documentation and/or other materials provided with the distribution. 14239310Sdim * 15239310Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16239310Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17239310Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18239310Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19239310Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20239310Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21239310Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22239310Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23239310Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24239310Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25239310Sdim * SUCH DAMAGE. 26239310Sdim * 27239310Sdim * From: FreeBSD: src/lib/libkvm/kvm_minidump_arm.c r214223 28239310Sdim */ 29239310Sdim 30239310Sdim#include <sys/cdefs.h> 31239310Sdim__FBSDID("$FreeBSD: head/lib/libkvm/kvm_minidump_mips.c 291372 2015-11-26 19:42:10Z jhb $"); 32239310Sdim 33239310Sdim/* 34239310Sdim * MIPS machine dependent routines for kvm and minidumps. 35239310Sdim */ 36239310Sdim 37239310Sdim#include <sys/param.h> 38239310Sdim#include <sys/user.h> 39239310Sdim#include <sys/proc.h> 40239310Sdim#include <sys/stat.h> 41239310Sdim#include <sys/mman.h> 42239310Sdim#include <sys/fnv_hash.h> 43239310Sdim#include <stdlib.h> 44239310Sdim#include <string.h> 45239310Sdim#include <unistd.h> 46239310Sdim#include <nlist.h> 47239310Sdim#include <kvm.h> 48239310Sdim 49239310Sdim#include <vm/vm.h> 50239310Sdim#include <vm/vm_param.h> 51239310Sdim 52239310Sdim#include <machine/elf.h> 53239310Sdim#include <machine/cpufunc.h> 54252723Sdim#include <machine/minidump.h> 55252723Sdim 56252723Sdim#include <limits.h> 57252723Sdim 58252723Sdim#include "kvm_private.h" 59252723Sdim 60252723Sdimstruct hpte { 61252723Sdim struct hpte *next; 62252723Sdim uint64_t pa; 63252723Sdim int64_t off; 64252723Sdim}; 65252723Sdim 66252723Sdim#define HPT_SIZE 1024 67252723Sdim 68252723Sdim/* minidump must be the first field */ 69239310Sdimstruct vmstate { 70252723Sdim int minidump; /* 1 = minidump mode */ 71252723Sdim struct minidumphdr hdr; 72239310Sdim void *hpt_head[HPT_SIZE]; 73239310Sdim uint32_t *bitmap; 74239310Sdim void *ptemap; 75239310Sdim}; 76239310Sdim 77239310Sdimstatic void 78239310Sdimhpt_insert(kvm_t *kd, uint64_t pa, int64_t off) 79239310Sdim{ 80239310Sdim struct hpte *hpte; 81252723Sdim uint32_t fnv = FNV1_32_INIT; 82239310Sdim 83239310Sdim fnv = fnv_32_buf(&pa, sizeof(pa), fnv); 84239310Sdim fnv &= (HPT_SIZE - 1); 85239310Sdim 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