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 56157911Speterstruct hpte { 57157911Speter struct hpte *next; 58157911Speter vm_paddr_t pa; 59157911Speter int64_t off; 60157911Speter}; 61157911Speter 62157911Speter#define HPT_SIZE 1024 63157911Speter 64157911Speter/* minidump must be the first item! */ 65157911Speterstruct vmstate { 66157911Speter int minidump; /* 1 = minidump mode */ 67157911Speter struct minidumphdr hdr; 68157911Speter void *hpt_head[HPT_SIZE]; 69157911Speter uint64_t *bitmap; 70215133Savg uint64_t *page_map; 71157911Speter}; 72157911Speter 73157911Speterstatic void 74157911Speterhpt_insert(kvm_t *kd, vm_paddr_t pa, int64_t off) 75157911Speter{ 76157911Speter struct hpte *hpte; 77157911Speter uint32_t fnv = FNV1_32_INIT; 78157911Speter 79157911Speter fnv = fnv_32_buf(&pa, sizeof(pa), fnv); 80157911Speter fnv &= (HPT_SIZE - 1); 81157911Speter hpte = malloc(sizeof(*hpte)); 82157911Speter hpte->pa = pa; 83157911Speter hpte->off = off; 84157911Speter hpte->next = kd->vmst->hpt_head[fnv]; 85157911Speter kd->vmst->hpt_head[fnv] = hpte; 86157911Speter} 87157911Speter 88157911Speterstatic int64_t 89157911Speterhpt_find(kvm_t *kd, vm_paddr_t pa) 90157911Speter{ 91157911Speter struct hpte *hpte; 92157911Speter uint32_t fnv = FNV1_32_INIT; 93157911Speter 94157911Speter fnv = fnv_32_buf(&pa, sizeof(pa), fnv); 95157911Speter fnv &= (HPT_SIZE - 1); 96157911Speter for (hpte = kd->vmst->hpt_head[fnv]; hpte != NULL; hpte = hpte->next) { 97157911Speter if (pa == hpte->pa) 98157911Speter return (hpte->off); 99157911Speter } 100157911Speter return (-1); 101157911Speter} 102157911Speter 103157911Speterstatic int 104157911Speterinithash(kvm_t *kd, uint64_t *base, int len, off_t off) 105157911Speter{ 106157911Speter uint64_t idx; 107157911Speter uint64_t bit, bits; 108157911Speter vm_paddr_t pa; 109157911Speter 110157911Speter for (idx = 0; idx < len / sizeof(*base); idx++) { 111157911Speter bits = base[idx]; 112157911Speter while (bits) { 113157911Speter bit = bsfq(bits); 114157911Speter bits &= ~(1ul << bit); 115157911Speter pa = (idx * sizeof(*base) * NBBY + bit) * PAGE_SIZE; 116157911Speter hpt_insert(kd, pa, off); 117157911Speter off += PAGE_SIZE; 118157911Speter } 119157911Speter } 120157911Speter return (off); 121157911Speter} 122157911Speter 123157911Spetervoid 124157911Speter_kvm_minidump_freevtop(kvm_t *kd) 125157911Speter{ 126157911Speter struct vmstate *vm = kd->vmst; 127157911Speter 128157911Speter if (vm->bitmap) 129157911Speter free(vm->bitmap); 130215133Savg if (vm->page_map) 131215133Savg free(vm->page_map); 132157911Speter free(vm); 133157911Speter kd->vmst = NULL; 134157911Speter} 135157911Speter 136157911Speterint 137157911Speter_kvm_minidump_initvtop(kvm_t *kd) 138157911Speter{ 139157911Speter struct vmstate *vmst; 140157911Speter off_t off; 141157911Speter 142157911Speter vmst = _kvm_malloc(kd, sizeof(*vmst)); 143157911Speter if (vmst == 0) { 144157911Speter _kvm_err(kd, kd->program, "cannot allocate vm"); 145157911Speter return (-1); 146157911Speter } 147157911Speter kd->vmst = vmst; 148157911Speter vmst->minidump = 1; 149157911Speter if (pread(kd->pmfd, &vmst->hdr, sizeof(vmst->hdr), 0) != 150157911Speter sizeof(vmst->hdr)) { 151157911Speter _kvm_err(kd, kd->program, "cannot read dump header"); 152157911Speter return (-1); 153157911Speter } 154157911Speter if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic, sizeof(vmst->hdr.magic)) != 0) { 155157911Speter _kvm_err(kd, kd->program, "not a minidump for this platform"); 156157911Speter return (-1); 157157911Speter } 158215133Savg 159215133Savg /* 160215133Savg * NB: amd64 minidump header is binary compatible between version 1 161215133Savg * and version 2; this may not be the case for the future versions. 162215133Savg */ 163215133Savg if (vmst->hdr.version != MINIDUMP_VERSION && vmst->hdr.version != 1) { 164157911Speter _kvm_err(kd, kd->program, "wrong minidump version. expected %d got %d", 165157911Speter MINIDUMP_VERSION, vmst->hdr.version); 166157911Speter return (-1); 167157911Speter } 168157911Speter 169157911Speter /* Skip header and msgbuf */ 170157911Speter off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize); 171157911Speter 172157911Speter vmst->bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize); 173157911Speter if (vmst->bitmap == NULL) { 174157911Speter _kvm_err(kd, kd->program, "cannot allocate %d bytes for bitmap", vmst->hdr.bitmapsize); 175157911Speter return (-1); 176157911Speter } 177157911Speter if (pread(kd->pmfd, vmst->bitmap, vmst->hdr.bitmapsize, off) != 178157911Speter vmst->hdr.bitmapsize) { 179157911Speter _kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap", vmst->hdr.bitmapsize); 180157911Speter return (-1); 181157911Speter } 182157911Speter off += round_page(vmst->hdr.bitmapsize); 183157911Speter 184215133Savg vmst->page_map = _kvm_malloc(kd, vmst->hdr.pmapsize); 185215133Savg if (vmst->page_map == NULL) { 186215133Savg _kvm_err(kd, kd->program, "cannot allocate %d bytes for page_map", vmst->hdr.pmapsize); 187157911Speter return (-1); 188157911Speter } 189215133Savg if (pread(kd->pmfd, vmst->page_map, vmst->hdr.pmapsize, off) != 190215133Savg vmst->hdr.pmapsize) { 191215133Savg _kvm_err(kd, kd->program, "cannot read %d bytes for page_map", vmst->hdr.pmapsize); 192157911Speter return (-1); 193157911Speter } 194215133Savg off += vmst->hdr.pmapsize; 195157911Speter 196157911Speter /* build physical address hash table for sparse pages */ 197157911Speter inithash(kd, vmst->bitmap, vmst->hdr.bitmapsize, off); 198157911Speter 199157911Speter return (0); 200157911Speter} 201157911Speter 202157911Speterstatic int 203215133Savg_kvm_minidump_vatop_v1(kvm_t *kd, u_long va, off_t *pa) 204157911Speter{ 205157911Speter struct vmstate *vm; 206157911Speter u_long offset; 207157911Speter pt_entry_t pte; 208157911Speter u_long pteindex; 209157911Speter u_long a; 210157911Speter off_t ofs; 211157911Speter 212157911Speter vm = kd->vmst; 213157911Speter offset = va & (PAGE_SIZE - 1); 214157911Speter 215157911Speter if (va >= vm->hdr.kernbase) { 216157911Speter pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT; 217269013Semaste if (pteindex >= vm->hdr.pmapsize / sizeof(*vm->page_map)) 218269013Semaste goto invalid; 219215133Savg pte = vm->page_map[pteindex]; 220157911Speter if (((u_long)pte & PG_V) == 0) { 221157911Speter _kvm_err(kd, kd->program, "_kvm_vatop: pte not valid"); 222157911Speter goto invalid; 223157911Speter } 224157911Speter a = pte & PG_FRAME; 225157911Speter ofs = hpt_find(kd, a); 226157911Speter if (ofs == -1) { 227157911Speter _kvm_err(kd, kd->program, "_kvm_vatop: physical address 0x%lx not in minidump", a); 228157911Speter goto invalid; 229157911Speter } 230157911Speter *pa = ofs + offset; 231157911Speter return (PAGE_SIZE - offset); 232157911Speter } else if (va >= vm->hdr.dmapbase && va < vm->hdr.dmapend) { 233157911Speter a = (va - vm->hdr.dmapbase) & ~PAGE_MASK; 234157911Speter ofs = hpt_find(kd, a); 235157911Speter if (ofs == -1) { 236157911Speter _kvm_err(kd, kd->program, "_kvm_vatop: direct map address 0x%lx not in minidump", va); 237157911Speter goto invalid; 238157911Speter } 239157911Speter *pa = ofs + offset; 240157911Speter return (PAGE_SIZE - offset); 241157911Speter } else { 242157911Speter _kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va); 243157911Speter goto invalid; 244157911Speter } 245157911Speter 246157911Speterinvalid: 247157911Speter _kvm_err(kd, 0, "invalid address (0x%lx)", va); 248157911Speter return (0); 249157911Speter} 250157911Speter 251215133Savgstatic int 252215133Savg_kvm_minidump_vatop(kvm_t *kd, u_long va, off_t *pa) 253215133Savg{ 254215133Savg pt_entry_t pt[NPTEPG]; 255215133Savg struct vmstate *vm; 256215133Savg u_long offset; 257215133Savg pd_entry_t pde; 258215133Savg pd_entry_t pte; 259215133Savg u_long pteindex; 260215133Savg u_long pdeindex; 261215133Savg u_long a; 262215133Savg off_t ofs; 263215133Savg 264215133Savg vm = kd->vmst; 265215133Savg offset = va & PAGE_MASK; 266215133Savg 267215133Savg if (va >= vm->hdr.kernbase) { 268215133Savg pdeindex = (va - vm->hdr.kernbase) >> PDRSHIFT; 269269013Semaste if (pdeindex >= vm->hdr.pmapsize / sizeof(*vm->page_map)) 270269013Semaste goto invalid; 271215133Savg pde = vm->page_map[pdeindex]; 272215133Savg if (((u_long)pde & PG_V) == 0) { 273215133Savg _kvm_err(kd, kd->program, "_kvm_vatop: pde not valid"); 274215133Savg goto invalid; 275215133Savg } 276215133Savg if ((pde & PG_PS) == 0) { 277215133Savg a = pde & PG_FRAME; 278215133Savg ofs = hpt_find(kd, a); 279215133Savg if (ofs == -1) { 280215133Savg _kvm_err(kd, kd->program, "_kvm_vatop: pt physical address 0x%lx not in minidump", a); 281215133Savg goto invalid; 282215133Savg } 283215133Savg if (pread(kd->pmfd, &pt, PAGE_SIZE, ofs) != PAGE_SIZE) { 284215133Savg _kvm_err(kd, kd->program, "cannot read %d bytes for pt", PAGE_SIZE); 285215133Savg return (-1); 286215133Savg } 287215133Savg pteindex = (va >> PAGE_SHIFT) & ((1ul << NPTEPGSHIFT) - 1); 288215133Savg pte = pt[pteindex]; 289215133Savg if (((u_long)pte & PG_V) == 0) { 290215133Savg _kvm_err(kd, kd->program, "_kvm_vatop: pte not valid"); 291215133Savg goto invalid; 292215133Savg } 293215133Savg a = pte & PG_FRAME; 294215133Savg } else { 295215133Savg a = pde & PG_PS_FRAME; 296215133Savg a += (va & PDRMASK) ^ offset; 297215133Savg } 298215133Savg ofs = hpt_find(kd, a); 299215133Savg if (ofs == -1) { 300215133Savg _kvm_err(kd, kd->program, "_kvm_vatop: physical address 0x%lx not in minidump", a); 301215133Savg goto invalid; 302215133Savg } 303215133Savg *pa = ofs + offset; 304215133Savg return (PAGE_SIZE - offset); 305215133Savg } else if (va >= vm->hdr.dmapbase && va < vm->hdr.dmapend) { 306215133Savg a = (va - vm->hdr.dmapbase) & ~PAGE_MASK; 307215133Savg ofs = hpt_find(kd, a); 308215133Savg if (ofs == -1) { 309215133Savg _kvm_err(kd, kd->program, "_kvm_vatop: direct map address 0x%lx not in minidump", va); 310215133Savg goto invalid; 311215133Savg } 312215133Savg *pa = ofs + offset; 313215133Savg return (PAGE_SIZE - offset); 314215133Savg } else { 315215133Savg _kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va); 316215133Savg goto invalid; 317215133Savg } 318215133Savg 319215133Savginvalid: 320215133Savg _kvm_err(kd, 0, "invalid address (0x%lx)", va); 321215133Savg return (0); 322215133Savg} 323215133Savg 324157911Speterint 325157911Speter_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa) 326157911Speter{ 327157911Speter 328157911Speter if (ISALIVE(kd)) { 329157911Speter _kvm_err(kd, 0, "kvm_kvatop called in live kernel!"); 330157911Speter return (0); 331157911Speter } 332215133Savg if (((struct vmstate *)kd->vmst)->hdr.version == 1) 333215133Savg return (_kvm_minidump_vatop_v1(kd, va, pa)); 334215133Savg else 335215133Savg return (_kvm_minidump_vatop(kd, va, pa)); 336157911Speter} 337