1e9ca6fe4SPeter Wemm /*- 2e9ca6fe4SPeter Wemm * Copyright (c) 2006 Peter Wemm 3e9ca6fe4SPeter Wemm * 4e9ca6fe4SPeter Wemm * Redistribution and use in source and binary forms, with or without 5e9ca6fe4SPeter Wemm * modification, are permitted provided that the following conditions 6e9ca6fe4SPeter Wemm * are met: 7e9ca6fe4SPeter Wemm * 1. Redistributions of source code must retain the above copyright 8e9ca6fe4SPeter Wemm * notice, this list of conditions and the following disclaimer. 9e9ca6fe4SPeter Wemm * 2. Redistributions in binary form must reproduce the above copyright 10e9ca6fe4SPeter Wemm * notice, this list of conditions and the following disclaimer in the 11e9ca6fe4SPeter Wemm * documentation and/or other materials provided with the distribution. 12e9ca6fe4SPeter Wemm * 13e9ca6fe4SPeter Wemm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14e9ca6fe4SPeter Wemm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15e9ca6fe4SPeter Wemm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16e9ca6fe4SPeter Wemm * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17e9ca6fe4SPeter Wemm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18e9ca6fe4SPeter Wemm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19e9ca6fe4SPeter Wemm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20e9ca6fe4SPeter Wemm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21e9ca6fe4SPeter Wemm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22e9ca6fe4SPeter Wemm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23e9ca6fe4SPeter Wemm * SUCH DAMAGE. 24e9ca6fe4SPeter Wemm */ 25e9ca6fe4SPeter Wemm 26e9ca6fe4SPeter Wemm #include <sys/cdefs.h> 27e9ca6fe4SPeter Wemm __FBSDID("$FreeBSD$"); 28e9ca6fe4SPeter Wemm 29e9ca6fe4SPeter Wemm /* 30e9ca6fe4SPeter Wemm * AMD64 machine dependent routines for kvm and minidumps. 31e9ca6fe4SPeter Wemm */ 32e9ca6fe4SPeter Wemm 33e9ca6fe4SPeter Wemm #include <sys/param.h> 34e9ca6fe4SPeter Wemm #include <sys/user.h> 35e9ca6fe4SPeter Wemm #include <sys/proc.h> 36e9ca6fe4SPeter Wemm #include <sys/stat.h> 37e9ca6fe4SPeter Wemm #include <sys/mman.h> 38e9ca6fe4SPeter Wemm #include <sys/fnv_hash.h> 39e9ca6fe4SPeter Wemm #include <stdlib.h> 40e9ca6fe4SPeter Wemm #include <unistd.h> 41e9ca6fe4SPeter Wemm #include <nlist.h> 42e9ca6fe4SPeter Wemm #include <kvm.h> 43e9ca6fe4SPeter Wemm 44e9ca6fe4SPeter Wemm #include <vm/vm.h> 45e9ca6fe4SPeter Wemm #include <vm/vm_param.h> 46e9ca6fe4SPeter Wemm 47e9ca6fe4SPeter Wemm #include <machine/elf.h> 48e9ca6fe4SPeter Wemm #include <machine/cpufunc.h> 49e9ca6fe4SPeter Wemm #include <machine/minidump.h> 50e9ca6fe4SPeter Wemm 51e9ca6fe4SPeter Wemm #include <limits.h> 52e9ca6fe4SPeter Wemm 53e9ca6fe4SPeter Wemm #include "kvm_private.h" 54e9ca6fe4SPeter Wemm 55e9ca6fe4SPeter Wemm #define PG_FRAME_PAE (~((uint64_t)PAGE_MASK)) 56e9ca6fe4SPeter Wemm 57e9ca6fe4SPeter Wemm struct hpte { 58e9ca6fe4SPeter Wemm struct hpte *next; 59e9ca6fe4SPeter Wemm uint64_t pa; 60e9ca6fe4SPeter Wemm int64_t off; 61e9ca6fe4SPeter Wemm }; 62e9ca6fe4SPeter Wemm 63e9ca6fe4SPeter Wemm #define HPT_SIZE 1024 64e9ca6fe4SPeter Wemm 65e9ca6fe4SPeter Wemm /* minidump must be the first item! */ 66e9ca6fe4SPeter Wemm struct vmstate { 67e9ca6fe4SPeter Wemm int minidump; /* 1 = minidump mode */ 68e9ca6fe4SPeter Wemm struct minidumphdr hdr; 69e9ca6fe4SPeter Wemm void *hpt_head[HPT_SIZE]; 70e9ca6fe4SPeter Wemm uint32_t *bitmap; 71e9ca6fe4SPeter Wemm void *ptemap; 72e9ca6fe4SPeter Wemm }; 73e9ca6fe4SPeter Wemm 74e9ca6fe4SPeter Wemm static void 75e9ca6fe4SPeter Wemm hpt_insert(kvm_t *kd, uint64_t pa, int64_t off) 76e9ca6fe4SPeter Wemm { 77e9ca6fe4SPeter Wemm struct hpte *hpte; 78e9ca6fe4SPeter Wemm uint32_t fnv = FNV1_32_INIT; 79e9ca6fe4SPeter Wemm 80e9ca6fe4SPeter Wemm fnv = fnv_32_buf(&pa, sizeof(pa), fnv); 81e9ca6fe4SPeter Wemm fnv &= (HPT_SIZE - 1); 82e9ca6fe4SPeter Wemm hpte = malloc(sizeof(*hpte)); 83e9ca6fe4SPeter Wemm hpte->pa = pa; 84e9ca6fe4SPeter Wemm hpte->off = off; 85e9ca6fe4SPeter Wemm hpte->next = kd->vmst->hpt_head[fnv]; 86e9ca6fe4SPeter Wemm kd->vmst->hpt_head[fnv] = hpte; 87e9ca6fe4SPeter Wemm } 88e9ca6fe4SPeter Wemm 89e9ca6fe4SPeter Wemm static int64_t 90e9ca6fe4SPeter Wemm hpt_find(kvm_t *kd, uint64_t pa) 91e9ca6fe4SPeter Wemm { 92e9ca6fe4SPeter Wemm struct hpte *hpte; 93e9ca6fe4SPeter Wemm uint32_t fnv = FNV1_32_INIT; 94e9ca6fe4SPeter Wemm 95e9ca6fe4SPeter Wemm fnv = fnv_32_buf(&pa, sizeof(pa), fnv); 96e9ca6fe4SPeter Wemm fnv &= (HPT_SIZE - 1); 97e9ca6fe4SPeter Wemm for (hpte = kd->vmst->hpt_head[fnv]; hpte != NULL; hpte = hpte->next) { 98e9ca6fe4SPeter Wemm if (pa == hpte->pa) 99e9ca6fe4SPeter Wemm return (hpte->off); 100e9ca6fe4SPeter Wemm } 101e9ca6fe4SPeter Wemm return (-1); 102e9ca6fe4SPeter Wemm } 103e9ca6fe4SPeter Wemm 104e9ca6fe4SPeter Wemm static int 105e9ca6fe4SPeter Wemm inithash(kvm_t *kd, uint32_t *base, int len, off_t off) 106e9ca6fe4SPeter Wemm { 107e9ca6fe4SPeter Wemm uint64_t idx; 108e9ca6fe4SPeter Wemm uint32_t bit, bits; 109e9ca6fe4SPeter Wemm uint64_t pa; 110e9ca6fe4SPeter Wemm 111e9ca6fe4SPeter Wemm for (idx = 0; idx < len / sizeof(*base); idx++) { 112e9ca6fe4SPeter Wemm bits = base[idx]; 113e9ca6fe4SPeter Wemm while (bits) { 114e9ca6fe4SPeter Wemm bit = bsfl(bits); 115e9ca6fe4SPeter Wemm bits &= ~(1ul << bit); 116e9ca6fe4SPeter Wemm pa = (idx * sizeof(*base) * NBBY + bit) * PAGE_SIZE; 117e9ca6fe4SPeter Wemm hpt_insert(kd, pa, off); 118e9ca6fe4SPeter Wemm off += PAGE_SIZE; 119e9ca6fe4SPeter Wemm } 120e9ca6fe4SPeter Wemm } 121e9ca6fe4SPeter Wemm return (off); 122e9ca6fe4SPeter Wemm } 123e9ca6fe4SPeter Wemm 124e9ca6fe4SPeter Wemm void 125e9ca6fe4SPeter Wemm _kvm_minidump_freevtop(kvm_t *kd) 126e9ca6fe4SPeter Wemm { 127e9ca6fe4SPeter Wemm struct vmstate *vm = kd->vmst; 128e9ca6fe4SPeter Wemm 129e9ca6fe4SPeter Wemm if (vm->bitmap) 130e9ca6fe4SPeter Wemm free(vm->bitmap); 131e9ca6fe4SPeter Wemm if (vm->ptemap) 132e9ca6fe4SPeter Wemm free(vm->ptemap); 133e9ca6fe4SPeter Wemm free(vm); 134e9ca6fe4SPeter Wemm kd->vmst = NULL; 135e9ca6fe4SPeter Wemm } 136e9ca6fe4SPeter Wemm 137e9ca6fe4SPeter Wemm int 138e9ca6fe4SPeter Wemm _kvm_minidump_initvtop(kvm_t *kd) 139e9ca6fe4SPeter Wemm { 140e9ca6fe4SPeter Wemm u_long pa; 141e9ca6fe4SPeter Wemm struct vmstate *vmst; 142e9ca6fe4SPeter Wemm off_t off; 143e9ca6fe4SPeter Wemm 144e9ca6fe4SPeter Wemm vmst = _kvm_malloc(kd, sizeof(*vmst)); 145e9ca6fe4SPeter Wemm if (vmst == 0) { 146e9ca6fe4SPeter Wemm _kvm_err(kd, kd->program, "cannot allocate vm"); 147e9ca6fe4SPeter Wemm return (-1); 148e9ca6fe4SPeter Wemm } 149e9ca6fe4SPeter Wemm kd->vmst = vmst; 150e9ca6fe4SPeter Wemm bzero(vmst, sizeof(*vmst)); 151e9ca6fe4SPeter Wemm vmst->minidump = 1; 152e9ca6fe4SPeter Wemm if (pread(kd->pmfd, &vmst->hdr, sizeof(vmst->hdr), 0) != 153e9ca6fe4SPeter Wemm sizeof(vmst->hdr)) { 154e9ca6fe4SPeter Wemm _kvm_err(kd, kd->program, "cannot read dump header"); 155e9ca6fe4SPeter Wemm return (-1); 156e9ca6fe4SPeter Wemm } 157e9ca6fe4SPeter Wemm if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic, sizeof(vmst->hdr.magic)) != 0) { 158e9ca6fe4SPeter Wemm _kvm_err(kd, kd->program, "not a minidump for this platform"); 159e9ca6fe4SPeter Wemm return (-1); 160e9ca6fe4SPeter Wemm } 161e9ca6fe4SPeter Wemm if (vmst->hdr.version != MINIDUMP_VERSION) { 162e9ca6fe4SPeter Wemm _kvm_err(kd, kd->program, "wrong minidump version. expected %d got %d", 163e9ca6fe4SPeter Wemm MINIDUMP_VERSION, vmst->hdr.version); 164e9ca6fe4SPeter Wemm return (-1); 165e9ca6fe4SPeter Wemm } 166e9ca6fe4SPeter Wemm 167e9ca6fe4SPeter Wemm /* Skip header and msgbuf */ 168e9ca6fe4SPeter Wemm off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize); 169e9ca6fe4SPeter Wemm 170e9ca6fe4SPeter Wemm vmst->bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize); 171e9ca6fe4SPeter Wemm if (vmst->bitmap == NULL) { 172e9ca6fe4SPeter Wemm _kvm_err(kd, kd->program, "cannot allocate %d bytes for bitmap", vmst->hdr.bitmapsize); 173e9ca6fe4SPeter Wemm return (-1); 174e9ca6fe4SPeter Wemm } 175e9ca6fe4SPeter Wemm if (pread(kd->pmfd, vmst->bitmap, vmst->hdr.bitmapsize, off) != 176e9ca6fe4SPeter Wemm vmst->hdr.bitmapsize) { 177e9ca6fe4SPeter Wemm _kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap", vmst->hdr.bitmapsize); 178e9ca6fe4SPeter Wemm return (-1); 179e9ca6fe4SPeter Wemm } 180e9ca6fe4SPeter Wemm off += round_page(vmst->hdr.bitmapsize); 181e9ca6fe4SPeter Wemm 182e9ca6fe4SPeter Wemm vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize); 183e9ca6fe4SPeter Wemm if (vmst->ptemap == NULL) { 184e9ca6fe4SPeter Wemm _kvm_err(kd, kd->program, "cannot allocate %d bytes for ptemap", vmst->hdr.ptesize); 185e9ca6fe4SPeter Wemm return (-1); 186e9ca6fe4SPeter Wemm } 187e9ca6fe4SPeter Wemm if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) != 188e9ca6fe4SPeter Wemm vmst->hdr.ptesize) { 189e9ca6fe4SPeter Wemm _kvm_err(kd, kd->program, "cannot read %d bytes for ptemap", vmst->hdr.ptesize); 190e9ca6fe4SPeter Wemm return (-1); 191e9ca6fe4SPeter Wemm } 192e9ca6fe4SPeter Wemm off += vmst->hdr.ptesize; 193e9ca6fe4SPeter Wemm 194e9ca6fe4SPeter Wemm /* build physical address hash table for sparse pages */ 195e9ca6fe4SPeter Wemm inithash(kd, vmst->bitmap, vmst->hdr.bitmapsize, off); 196e9ca6fe4SPeter Wemm 197e9ca6fe4SPeter Wemm return (0); 198e9ca6fe4SPeter Wemm } 199e9ca6fe4SPeter Wemm 200e9ca6fe4SPeter Wemm static int 201e9ca6fe4SPeter Wemm _kvm_minidump_vatop_pae(kvm_t *kd, u_long va, off_t *pa) 202e9ca6fe4SPeter Wemm { 203e9ca6fe4SPeter Wemm struct vmstate *vm; 204e9ca6fe4SPeter Wemm uint64_t offset; 205e9ca6fe4SPeter Wemm uint64_t pte; 206e9ca6fe4SPeter Wemm u_long pteindex; 207e9ca6fe4SPeter Wemm int i; 208e9ca6fe4SPeter Wemm uint64_t a; 209e9ca6fe4SPeter Wemm off_t ofs; 210e9ca6fe4SPeter Wemm uint64_t *ptemap; 211e9ca6fe4SPeter Wemm 212e9ca6fe4SPeter Wemm vm = kd->vmst; 213e9ca6fe4SPeter Wemm ptemap = vm->ptemap; 214e9ca6fe4SPeter Wemm offset = va & (PAGE_SIZE - 1); 215e9ca6fe4SPeter Wemm 216e9ca6fe4SPeter Wemm if (va >= vm->hdr.kernbase) { 217e9ca6fe4SPeter Wemm pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT; 218e9ca6fe4SPeter Wemm pte = ptemap[pteindex]; 219e9ca6fe4SPeter Wemm if ((pte & PG_V) == 0) { 220e9ca6fe4SPeter Wemm _kvm_err(kd, kd->program, "_kvm_vatop: pte not valid"); 221e9ca6fe4SPeter Wemm goto invalid; 222e9ca6fe4SPeter Wemm } 223e9ca6fe4SPeter Wemm a = pte & PG_FRAME_PAE; 224e9ca6fe4SPeter Wemm ofs = hpt_find(kd, a); 225e9ca6fe4SPeter Wemm if (ofs == -1) { 226e9ca6fe4SPeter Wemm _kvm_err(kd, kd->program, "_kvm_vatop: physical address 0x%llx not in minidump", a); 227e9ca6fe4SPeter Wemm goto invalid; 228e9ca6fe4SPeter Wemm } 229e9ca6fe4SPeter Wemm *pa = ofs + offset; 230e9ca6fe4SPeter Wemm return (PAGE_SIZE - offset); 231e9ca6fe4SPeter Wemm } else { 232e9ca6fe4SPeter Wemm _kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va); 233e9ca6fe4SPeter Wemm goto invalid; 234e9ca6fe4SPeter Wemm } 235e9ca6fe4SPeter Wemm 236e9ca6fe4SPeter Wemm invalid: 237e9ca6fe4SPeter Wemm _kvm_err(kd, 0, "invalid address (0x%lx)", va); 238e9ca6fe4SPeter Wemm return (0); 239e9ca6fe4SPeter Wemm } 240e9ca6fe4SPeter Wemm 241e9ca6fe4SPeter Wemm static int 242e9ca6fe4SPeter Wemm _kvm_minidump_vatop(kvm_t *kd, u_long va, off_t *pa) 243e9ca6fe4SPeter Wemm { 244e9ca6fe4SPeter Wemm struct vmstate *vm; 245e9ca6fe4SPeter Wemm u_long offset; 246e9ca6fe4SPeter Wemm pt_entry_t pte; 247e9ca6fe4SPeter Wemm u_long pteindex; 248e9ca6fe4SPeter Wemm int i; 249e9ca6fe4SPeter Wemm u_long a; 250e9ca6fe4SPeter Wemm off_t ofs; 251e9ca6fe4SPeter Wemm uint32_t *ptemap; 252e9ca6fe4SPeter Wemm 253e9ca6fe4SPeter Wemm vm = kd->vmst; 254e9ca6fe4SPeter Wemm ptemap = vm->ptemap; 255e9ca6fe4SPeter Wemm offset = va & (PAGE_SIZE - 1); 256e9ca6fe4SPeter Wemm 257e9ca6fe4SPeter Wemm if (va >= vm->hdr.kernbase) { 258e9ca6fe4SPeter Wemm pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT; 259e9ca6fe4SPeter Wemm pte = ptemap[pteindex]; 260e9ca6fe4SPeter Wemm if ((pte & PG_V) == 0) { 261e9ca6fe4SPeter Wemm _kvm_err(kd, kd->program, "_kvm_vatop: pte not valid"); 262e9ca6fe4SPeter Wemm goto invalid; 263e9ca6fe4SPeter Wemm } 264e9ca6fe4SPeter Wemm a = pte & PG_FRAME; 265e9ca6fe4SPeter Wemm ofs = hpt_find(kd, a); 266e9ca6fe4SPeter Wemm if (ofs == -1) { 267e9ca6fe4SPeter Wemm _kvm_err(kd, kd->program, "_kvm_vatop: physical address 0x%lx not in minidump", a); 268e9ca6fe4SPeter Wemm goto invalid; 269e9ca6fe4SPeter Wemm } 270e9ca6fe4SPeter Wemm *pa = ofs + offset; 271e9ca6fe4SPeter Wemm return (PAGE_SIZE - offset); 272e9ca6fe4SPeter Wemm } else { 273e9ca6fe4SPeter Wemm _kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va); 274e9ca6fe4SPeter Wemm goto invalid; 275e9ca6fe4SPeter Wemm } 276e9ca6fe4SPeter Wemm 277e9ca6fe4SPeter Wemm invalid: 278e9ca6fe4SPeter Wemm _kvm_err(kd, 0, "invalid address (0x%lx)", va); 279e9ca6fe4SPeter Wemm return (0); 280e9ca6fe4SPeter Wemm } 281e9ca6fe4SPeter Wemm 282e9ca6fe4SPeter Wemm int 283e9ca6fe4SPeter Wemm _kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa) 284e9ca6fe4SPeter Wemm { 285e9ca6fe4SPeter Wemm 286e9ca6fe4SPeter Wemm if (ISALIVE(kd)) { 287e9ca6fe4SPeter Wemm _kvm_err(kd, 0, "kvm_kvatop called in live kernel!"); 288e9ca6fe4SPeter Wemm return (0); 289e9ca6fe4SPeter Wemm } 290e9ca6fe4SPeter Wemm if (kd->vmst->hdr.paemode) 291e9ca6fe4SPeter Wemm return (_kvm_minidump_vatop_pae(kd, va, pa)); 292e9ca6fe4SPeter Wemm else 293e9ca6fe4SPeter Wemm return (_kvm_minidump_vatop(kd, va, pa)); 294e9ca6fe4SPeter Wemm } 295