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$"); 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 struct vmstate *vmst; 142157911Speter off_t off; 143157911Speter 144157911Speter vmst = _kvm_malloc(kd, sizeof(*vmst)); 145157911Speter if (vmst == 0) { 146157911Speter _kvm_err(kd, kd->program, "cannot allocate vm"); 147157911Speter return (-1); 148157911Speter } 149157911Speter kd->vmst = vmst; 150157911Speter vmst->minidump = 1; 151157911Speter if (pread(kd->pmfd, &vmst->hdr, sizeof(vmst->hdr), 0) != 152157911Speter sizeof(vmst->hdr)) { 153157911Speter _kvm_err(kd, kd->program, "cannot read dump header"); 154157911Speter return (-1); 155157911Speter } 156157911Speter if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic, sizeof(vmst->hdr.magic)) != 0) { 157157911Speter _kvm_err(kd, kd->program, "not a minidump for this platform"); 158157911Speter return (-1); 159157911Speter } 160157911Speter if (vmst->hdr.version != MINIDUMP_VERSION) { 161157911Speter _kvm_err(kd, kd->program, "wrong minidump version. expected %d got %d", 162157911Speter MINIDUMP_VERSION, vmst->hdr.version); 163157911Speter return (-1); 164157911Speter } 165157911Speter 166157911Speter /* Skip header and msgbuf */ 167157911Speter off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize); 168157911Speter 169157911Speter vmst->bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize); 170157911Speter if (vmst->bitmap == NULL) { 171157911Speter _kvm_err(kd, kd->program, "cannot allocate %d bytes for bitmap", vmst->hdr.bitmapsize); 172157911Speter return (-1); 173157911Speter } 174157911Speter if (pread(kd->pmfd, vmst->bitmap, vmst->hdr.bitmapsize, off) != 175217744Suqs (ssize_t)vmst->hdr.bitmapsize) { 176157911Speter _kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap", vmst->hdr.bitmapsize); 177157911Speter return (-1); 178157911Speter } 179157911Speter off += round_page(vmst->hdr.bitmapsize); 180157911Speter 181157911Speter vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize); 182157911Speter if (vmst->ptemap == NULL) { 183157911Speter _kvm_err(kd, kd->program, "cannot allocate %d bytes for ptemap", vmst->hdr.ptesize); 184157911Speter return (-1); 185157911Speter } 186157911Speter if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) != 187217744Suqs (ssize_t)vmst->hdr.ptesize) { 188157911Speter _kvm_err(kd, kd->program, "cannot read %d bytes for ptemap", vmst->hdr.ptesize); 189157911Speter return (-1); 190157911Speter } 191157911Speter off += vmst->hdr.ptesize; 192157911Speter 193157911Speter /* build physical address hash table for sparse pages */ 194157911Speter inithash(kd, vmst->bitmap, vmst->hdr.bitmapsize, off); 195157911Speter 196157911Speter return (0); 197157911Speter} 198157911Speter 199157911Speterstatic int 200157911Speter_kvm_minidump_vatop_pae(kvm_t *kd, u_long va, off_t *pa) 201157911Speter{ 202157911Speter struct vmstate *vm; 203157911Speter uint64_t offset; 204157911Speter uint64_t pte; 205157911Speter u_long pteindex; 206157911Speter uint64_t a; 207157911Speter off_t ofs; 208157911Speter uint64_t *ptemap; 209157911Speter 210157911Speter vm = kd->vmst; 211157911Speter ptemap = vm->ptemap; 212157911Speter offset = va & (PAGE_SIZE - 1); 213157911Speter 214157911Speter if (va >= vm->hdr.kernbase) { 215157911Speter pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT; 216157911Speter pte = ptemap[pteindex]; 217157911Speter if ((pte & PG_V) == 0) { 218157911Speter _kvm_err(kd, kd->program, "_kvm_vatop: pte not valid"); 219157911Speter goto invalid; 220157911Speter } 221157911Speter a = pte & PG_FRAME_PAE; 222157911Speter ofs = hpt_find(kd, a); 223157911Speter if (ofs == -1) { 224157911Speter _kvm_err(kd, kd->program, "_kvm_vatop: physical address 0x%llx not in minidump", a); 225157911Speter goto invalid; 226157911Speter } 227157911Speter *pa = ofs + offset; 228157911Speter return (PAGE_SIZE - offset); 229157911Speter } else { 230157911Speter _kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va); 231157911Speter goto invalid; 232157911Speter } 233157911Speter 234157911Speterinvalid: 235157911Speter _kvm_err(kd, 0, "invalid address (0x%lx)", va); 236157911Speter return (0); 237157911Speter} 238157911Speter 239157911Speterstatic int 240157911Speter_kvm_minidump_vatop(kvm_t *kd, u_long va, off_t *pa) 241157911Speter{ 242157911Speter struct vmstate *vm; 243157911Speter u_long offset; 244157911Speter pt_entry_t pte; 245157911Speter u_long pteindex; 246157911Speter u_long a; 247157911Speter off_t ofs; 248157911Speter uint32_t *ptemap; 249157911Speter 250157911Speter vm = kd->vmst; 251157911Speter ptemap = vm->ptemap; 252157911Speter offset = va & (PAGE_SIZE - 1); 253157911Speter 254157911Speter if (va >= vm->hdr.kernbase) { 255157911Speter pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT; 256157911Speter pte = ptemap[pteindex]; 257157911Speter if ((pte & PG_V) == 0) { 258157911Speter _kvm_err(kd, kd->program, "_kvm_vatop: pte not valid"); 259157911Speter goto invalid; 260157911Speter } 261157911Speter a = pte & PG_FRAME; 262157911Speter ofs = hpt_find(kd, a); 263157911Speter if (ofs == -1) { 264157911Speter _kvm_err(kd, kd->program, "_kvm_vatop: physical address 0x%lx not in minidump", a); 265157911Speter goto invalid; 266157911Speter } 267157911Speter *pa = ofs + offset; 268157911Speter return (PAGE_SIZE - offset); 269157911Speter } else { 270157911Speter _kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va); 271157911Speter goto invalid; 272157911Speter } 273157911Speter 274157911Speterinvalid: 275157911Speter _kvm_err(kd, 0, "invalid address (0x%lx)", va); 276157911Speter return (0); 277157911Speter} 278157911Speter 279157911Speterint 280157911Speter_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa) 281157911Speter{ 282157911Speter 283157911Speter if (ISALIVE(kd)) { 284157911Speter _kvm_err(kd, 0, "kvm_kvatop called in live kernel!"); 285157911Speter return (0); 286157911Speter } 287157911Speter if (kd->vmst->hdr.paemode) 288157911Speter return (_kvm_minidump_vatop_pae(kd, va, pa)); 289157911Speter else 290157911Speter return (_kvm_minidump_vatop(kd, va, pa)); 291157911Speter} 292