kvm_minidump_i386.c revision 194186
1157911Speter/*- 2157911Speter * Copyright (c) 2006 Peter Wemm 3157911Speter * 4157911Speter * Redistribution and use in source and binary forms, with or without 5157911Speter * modification, are permitted provided that the following conditions 6157911Speter * are met: 7157911Speter * 1. Redistributions of source code must retain the above copyright 8157911Speter * notice, this list of conditions and the following disclaimer. 9157911Speter * 2. Redistributions in binary form must reproduce the above copyright 10157911Speter * notice, this list of conditions and the following disclaimer in the 11157911Speter * documentation and/or other materials provided with the distribution. 12157911Speter * 13157911Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14157911Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15157911Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16157911Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17157911Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18157911Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19157911Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20157911Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21157911Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22157911Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23157911Speter * SUCH DAMAGE. 24157911Speter */ 25157911Speter 26157911Speter#include <sys/cdefs.h> 27157911Speter__FBSDID("$FreeBSD: head/lib/libkvm/kvm_minidump_i386.c 194186 2009-06-14 12:42:06Z ed $"); 28157911Speter 29157911Speter/* 30157911Speter * AMD64 machine dependent routines for kvm and minidumps. 31157911Speter */ 32157911Speter 33157911Speter#include <sys/param.h> 34157911Speter#include <sys/user.h> 35157911Speter#include <sys/proc.h> 36157911Speter#include <sys/stat.h> 37157911Speter#include <sys/mman.h> 38157911Speter#include <sys/fnv_hash.h> 39157911Speter#include <stdlib.h> 40194186Sed#include <string.h> 41157911Speter#include <unistd.h> 42157911Speter#include <nlist.h> 43157911Speter#include <kvm.h> 44157911Speter 45157911Speter#include <vm/vm.h> 46157911Speter#include <vm/vm_param.h> 47157911Speter 48157911Speter#include <machine/elf.h> 49157911Speter#include <machine/cpufunc.h> 50157911Speter#include <machine/minidump.h> 51157911Speter 52157911Speter#include <limits.h> 53157911Speter 54157911Speter#include "kvm_private.h" 55157911Speter 56157911Speter#define PG_FRAME_PAE (~((uint64_t)PAGE_MASK)) 57157911Speter 58157911Speterstruct hpte { 59157911Speter struct hpte *next; 60157911Speter uint64_t pa; 61157911Speter int64_t off; 62157911Speter}; 63157911Speter 64157911Speter#define HPT_SIZE 1024 65157911Speter 66157911Speter/* minidump must be the first item! */ 67157911Speterstruct vmstate { 68157911Speter int minidump; /* 1 = minidump mode */ 69157911Speter struct minidumphdr hdr; 70157911Speter void *hpt_head[HPT_SIZE]; 71157911Speter uint32_t *bitmap; 72157911Speter void *ptemap; 73157911Speter}; 74157911Speter 75157911Speterstatic void 76157911Speterhpt_insert(kvm_t *kd, uint64_t pa, int64_t off) 77157911Speter{ 78157911Speter struct hpte *hpte; 79157911Speter uint32_t fnv = FNV1_32_INIT; 80157911Speter 81157911Speter fnv = fnv_32_buf(&pa, sizeof(pa), fnv); 82157911Speter fnv &= (HPT_SIZE - 1); 83157911Speter hpte = malloc(sizeof(*hpte)); 84157911Speter hpte->pa = pa; 85157911Speter hpte->off = off; 86157911Speter hpte->next = kd->vmst->hpt_head[fnv]; 87157911Speter kd->vmst->hpt_head[fnv] = hpte; 88157911Speter} 89157911Speter 90157911Speterstatic int64_t 91157911Speterhpt_find(kvm_t *kd, uint64_t pa) 92157911Speter{ 93157911Speter struct hpte *hpte; 94157911Speter uint32_t fnv = FNV1_32_INIT; 95157911Speter 96157911Speter fnv = fnv_32_buf(&pa, sizeof(pa), fnv); 97157911Speter fnv &= (HPT_SIZE - 1); 98157911Speter for (hpte = kd->vmst->hpt_head[fnv]; hpte != NULL; hpte = hpte->next) { 99157911Speter if (pa == hpte->pa) 100157911Speter return (hpte->off); 101157911Speter } 102157911Speter return (-1); 103157911Speter} 104157911Speter 105157911Speterstatic int 106157911Speterinithash(kvm_t *kd, uint32_t *base, int len, off_t off) 107157911Speter{ 108157911Speter uint64_t idx; 109157911Speter uint32_t bit, bits; 110157911Speter uint64_t pa; 111157911Speter 112157911Speter for (idx = 0; idx < len / sizeof(*base); idx++) { 113157911Speter bits = base[idx]; 114157911Speter while (bits) { 115157911Speter bit = bsfl(bits); 116157911Speter bits &= ~(1ul << bit); 117157911Speter pa = (idx * sizeof(*base) * NBBY + bit) * PAGE_SIZE; 118157911Speter hpt_insert(kd, pa, off); 119157911Speter off += PAGE_SIZE; 120157911Speter } 121157911Speter } 122157911Speter return (off); 123157911Speter} 124157911Speter 125157911Spetervoid 126157911Speter_kvm_minidump_freevtop(kvm_t *kd) 127157911Speter{ 128157911Speter struct vmstate *vm = kd->vmst; 129157911Speter 130157911Speter if (vm->bitmap) 131157911Speter free(vm->bitmap); 132157911Speter if (vm->ptemap) 133157911Speter free(vm->ptemap); 134157911Speter free(vm); 135157911Speter kd->vmst = NULL; 136157911Speter} 137157911Speter 138157911Speterint 139157911Speter_kvm_minidump_initvtop(kvm_t *kd) 140157911Speter{ 141157911Speter u_long pa; 142157911Speter struct vmstate *vmst; 143157911Speter off_t off; 144157911Speter 145157911Speter vmst = _kvm_malloc(kd, sizeof(*vmst)); 146157911Speter if (vmst == 0) { 147157911Speter _kvm_err(kd, kd->program, "cannot allocate vm"); 148157911Speter return (-1); 149157911Speter } 150157911Speter kd->vmst = vmst; 151157911Speter vmst->minidump = 1; 152157911Speter if (pread(kd->pmfd, &vmst->hdr, sizeof(vmst->hdr), 0) != 153157911Speter sizeof(vmst->hdr)) { 154157911Speter _kvm_err(kd, kd->program, "cannot read dump header"); 155157911Speter return (-1); 156157911Speter } 157157911Speter if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic, sizeof(vmst->hdr.magic)) != 0) { 158157911Speter _kvm_err(kd, kd->program, "not a minidump for this platform"); 159157911Speter return (-1); 160157911Speter } 161157911Speter if (vmst->hdr.version != MINIDUMP_VERSION) { 162157911Speter _kvm_err(kd, kd->program, "wrong minidump version. expected %d got %d", 163157911Speter MINIDUMP_VERSION, vmst->hdr.version); 164157911Speter return (-1); 165157911Speter } 166157911Speter 167157911Speter /* Skip header and msgbuf */ 168157911Speter off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize); 169157911Speter 170157911Speter vmst->bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize); 171157911Speter if (vmst->bitmap == NULL) { 172157911Speter _kvm_err(kd, kd->program, "cannot allocate %d bytes for bitmap", vmst->hdr.bitmapsize); 173157911Speter return (-1); 174157911Speter } 175157911Speter if (pread(kd->pmfd, vmst->bitmap, vmst->hdr.bitmapsize, off) != 176157911Speter vmst->hdr.bitmapsize) { 177157911Speter _kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap", vmst->hdr.bitmapsize); 178157911Speter return (-1); 179157911Speter } 180157911Speter off += round_page(vmst->hdr.bitmapsize); 181157911Speter 182157911Speter vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize); 183157911Speter if (vmst->ptemap == NULL) { 184157911Speter _kvm_err(kd, kd->program, "cannot allocate %d bytes for ptemap", vmst->hdr.ptesize); 185157911Speter return (-1); 186157911Speter } 187157911Speter if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) != 188157911Speter vmst->hdr.ptesize) { 189157911Speter _kvm_err(kd, kd->program, "cannot read %d bytes for ptemap", vmst->hdr.ptesize); 190157911Speter return (-1); 191157911Speter } 192157911Speter off += vmst->hdr.ptesize; 193157911Speter 194157911Speter /* build physical address hash table for sparse pages */ 195157911Speter inithash(kd, vmst->bitmap, vmst->hdr.bitmapsize, off); 196157911Speter 197157911Speter return (0); 198157911Speter} 199157911Speter 200157911Speterstatic int 201157911Speter_kvm_minidump_vatop_pae(kvm_t *kd, u_long va, off_t *pa) 202157911Speter{ 203157911Speter struct vmstate *vm; 204157911Speter uint64_t offset; 205157911Speter uint64_t pte; 206157911Speter u_long pteindex; 207157911Speter int i; 208157911Speter uint64_t a; 209157911Speter off_t ofs; 210157911Speter uint64_t *ptemap; 211157911Speter 212157911Speter vm = kd->vmst; 213157911Speter ptemap = vm->ptemap; 214157911Speter offset = va & (PAGE_SIZE - 1); 215157911Speter 216157911Speter if (va >= vm->hdr.kernbase) { 217157911Speter pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT; 218157911Speter pte = ptemap[pteindex]; 219157911Speter if ((pte & PG_V) == 0) { 220157911Speter _kvm_err(kd, kd->program, "_kvm_vatop: pte not valid"); 221157911Speter goto invalid; 222157911Speter } 223157911Speter a = pte & PG_FRAME_PAE; 224157911Speter ofs = hpt_find(kd, a); 225157911Speter if (ofs == -1) { 226157911Speter _kvm_err(kd, kd->program, "_kvm_vatop: physical address 0x%llx not in minidump", a); 227157911Speter goto invalid; 228157911Speter } 229157911Speter *pa = ofs + offset; 230157911Speter return (PAGE_SIZE - offset); 231157911Speter } else { 232157911Speter _kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va); 233157911Speter goto invalid; 234157911Speter } 235157911Speter 236157911Speterinvalid: 237157911Speter _kvm_err(kd, 0, "invalid address (0x%lx)", va); 238157911Speter return (0); 239157911Speter} 240157911Speter 241157911Speterstatic int 242157911Speter_kvm_minidump_vatop(kvm_t *kd, u_long va, off_t *pa) 243157911Speter{ 244157911Speter struct vmstate *vm; 245157911Speter u_long offset; 246157911Speter pt_entry_t pte; 247157911Speter u_long pteindex; 248157911Speter int i; 249157911Speter u_long a; 250157911Speter off_t ofs; 251157911Speter uint32_t *ptemap; 252157911Speter 253157911Speter vm = kd->vmst; 254157911Speter ptemap = vm->ptemap; 255157911Speter offset = va & (PAGE_SIZE - 1); 256157911Speter 257157911Speter if (va >= vm->hdr.kernbase) { 258157911Speter pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT; 259157911Speter pte = ptemap[pteindex]; 260157911Speter if ((pte & PG_V) == 0) { 261157911Speter _kvm_err(kd, kd->program, "_kvm_vatop: pte not valid"); 262157911Speter goto invalid; 263157911Speter } 264157911Speter a = pte & PG_FRAME; 265157911Speter ofs = hpt_find(kd, a); 266157911Speter if (ofs == -1) { 267157911Speter _kvm_err(kd, kd->program, "_kvm_vatop: physical address 0x%lx not in minidump", a); 268157911Speter goto invalid; 269157911Speter } 270157911Speter *pa = ofs + offset; 271157911Speter return (PAGE_SIZE - offset); 272157911Speter } else { 273157911Speter _kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va); 274157911Speter goto invalid; 275157911Speter } 276157911Speter 277157911Speterinvalid: 278157911Speter _kvm_err(kd, 0, "invalid address (0x%lx)", va); 279157911Speter return (0); 280157911Speter} 281157911Speter 282157911Speterint 283157911Speter_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa) 284157911Speter{ 285157911Speter 286157911Speter if (ISALIVE(kd)) { 287157911Speter _kvm_err(kd, 0, "kvm_kvatop called in live kernel!"); 288157911Speter return (0); 289157911Speter } 290157911Speter if (kd->vmst->hdr.paemode) 291157911Speter return (_kvm_minidump_vatop_pae(kd, va, pa)); 292157911Speter else 293157911Speter return (_kvm_minidump_vatop(kd, va, pa)); 294157911Speter} 295