1184728Sraj/*- 2184728Sraj * Copyright (c) 2008 Semihalf, Grzegorz Bernacki 3184728Sraj * Copyright (c) 2006 Peter Wemm 4184728Sraj * 5184728Sraj * Redistribution and use in source and binary forms, with or without 6184728Sraj * modification, are permitted provided that the following conditions 7184728Sraj * are met: 8184728Sraj * 1. Redistributions of source code must retain the above copyright 9184728Sraj * notice, this list of conditions and the following disclaimer. 10184728Sraj * 2. Redistributions in binary form must reproduce the above copyright 11184728Sraj * notice, this list of conditions and the following disclaimer in the 12184728Sraj * documentation and/or other materials provided with the distribution. 13184728Sraj * 14184728Sraj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15184728Sraj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16184728Sraj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17184728Sraj * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18184728Sraj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19184728Sraj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20184728Sraj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21184728Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22184728Sraj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23184728Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24184728Sraj * SUCH DAMAGE. 25184728Sraj * 26184728Sraj * From: FreeBSD: src/lib/libkvm/kvm_minidump_i386.c,v 1.2 2006/06/05 08:51:14 27184728Sraj */ 28184728Sraj 29184728Sraj#include <sys/cdefs.h> 30184728Sraj__FBSDID("$FreeBSD$"); 31184728Sraj 32184728Sraj/* 33184728Sraj * ARM machine dependent routines for kvm and minidumps. 34184728Sraj */ 35184728Sraj 36184728Sraj#include <sys/param.h> 37184728Sraj#include <sys/user.h> 38184728Sraj#include <sys/proc.h> 39184728Sraj#include <sys/stat.h> 40184728Sraj#include <sys/mman.h> 41184728Sraj#include <sys/fnv_hash.h> 42184728Sraj#include <stdlib.h> 43184728Sraj#include <string.h> 44184728Sraj#include <unistd.h> 45184728Sraj#include <nlist.h> 46184728Sraj#include <kvm.h> 47184728Sraj 48184728Sraj#include <vm/vm.h> 49184728Sraj#include <vm/vm_param.h> 50184728Sraj 51184728Sraj#include <machine/elf.h> 52184728Sraj#include <machine/cpufunc.h> 53184728Sraj#include <machine/minidump.h> 54184728Sraj 55184728Sraj#include <limits.h> 56184728Sraj 57184728Sraj#include "kvm_private.h" 58184728Sraj 59184728Srajstruct hpte { 60184728Sraj struct hpte *next; 61184728Sraj uint64_t pa; 62184728Sraj int64_t off; 63184728Sraj}; 64184728Sraj 65184728Sraj#define HPT_SIZE 1024 66184728Sraj 67184728Sraj/* minidump must be the first field */ 68184728Srajstruct vmstate { 69184728Sraj int minidump; /* 1 = minidump mode */ 70184728Sraj struct minidumphdr hdr; 71184728Sraj void *hpt_head[HPT_SIZE]; 72184728Sraj uint32_t *bitmap; 73184728Sraj void *ptemap; 74184728Sraj}; 75184728Sraj 76184728Srajstatic void 77184728Srajhpt_insert(kvm_t *kd, uint64_t pa, int64_t off) 78184728Sraj{ 79184728Sraj struct hpte *hpte; 80184728Sraj uint32_t fnv = FNV1_32_INIT; 81184728Sraj 82184728Sraj fnv = fnv_32_buf(&pa, sizeof(pa), fnv); 83184728Sraj fnv &= (HPT_SIZE - 1); 84184728Sraj hpte = malloc(sizeof(*hpte)); 85184728Sraj hpte->pa = pa; 86184728Sraj hpte->off = off; 87184728Sraj hpte->next = kd->vmst->hpt_head[fnv]; 88184728Sraj kd->vmst->hpt_head[fnv] = hpte; 89184728Sraj} 90184728Sraj 91184728Srajstatic int64_t 92184728Srajhpt_find(kvm_t *kd, uint64_t pa) 93184728Sraj{ 94184728Sraj struct hpte *hpte; 95184728Sraj uint32_t fnv = FNV1_32_INIT; 96184728Sraj 97184728Sraj fnv = fnv_32_buf(&pa, sizeof(pa), fnv); 98184728Sraj fnv &= (HPT_SIZE - 1); 99184728Sraj for (hpte = kd->vmst->hpt_head[fnv]; hpte != NULL; hpte = hpte->next) 100184728Sraj if (pa == hpte->pa) 101184728Sraj return (hpte->off); 102184728Sraj 103184728Sraj return (-1); 104184728Sraj} 105184728Sraj 106184728Srajstatic int 107184728Srajinithash(kvm_t *kd, uint32_t *base, int len, off_t off) 108184728Sraj{ 109184728Sraj uint64_t idx, pa; 110184728Sraj uint32_t bit, bits; 111184728Sraj 112184728Sraj for (idx = 0; idx < len / sizeof(*base); idx++) { 113184728Sraj bits = base[idx]; 114184728Sraj while (bits) { 115184728Sraj bit = ffs(bits) - 1; 116184728Sraj bits &= ~(1ul << bit); 117184728Sraj pa = (idx * sizeof(*base) * NBBY + bit) * PAGE_SIZE; 118184728Sraj hpt_insert(kd, pa, off); 119184728Sraj off += PAGE_SIZE; 120184728Sraj } 121184728Sraj } 122184728Sraj return (off); 123184728Sraj} 124184728Sraj 125184728Srajvoid 126184728Sraj_kvm_minidump_freevtop(kvm_t *kd) 127184728Sraj{ 128184728Sraj struct vmstate *vm = kd->vmst; 129184728Sraj 130184728Sraj if (vm->bitmap) 131184728Sraj free(vm->bitmap); 132184728Sraj if (vm->ptemap) 133184728Sraj free(vm->ptemap); 134184728Sraj free(vm); 135184728Sraj kd->vmst = NULL; 136184728Sraj} 137184728Sraj 138184728Srajint 139184728Sraj_kvm_minidump_initvtop(kvm_t *kd) 140184728Sraj{ 141184728Sraj struct vmstate *vmst; 142184728Sraj off_t off; 143184728Sraj 144184728Sraj vmst = _kvm_malloc(kd, sizeof(*vmst)); 145184728Sraj if (vmst == 0) { 146184728Sraj _kvm_err(kd, kd->program, "cannot allocate vm"); 147184728Sraj return (-1); 148184728Sraj } 149184728Sraj 150184728Sraj kd->vmst = vmst; 151184728Sraj vmst->minidump = 1; 152184728Sraj 153184728Sraj if (pread(kd->pmfd, &vmst->hdr, 154184728Sraj sizeof(vmst->hdr), 0) != sizeof(vmst->hdr)) { 155184728Sraj _kvm_err(kd, kd->program, "cannot read dump header"); 156184728Sraj return (-1); 157184728Sraj } 158184728Sraj 159184728Sraj if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic, 160184728Sraj sizeof(vmst->hdr.magic)) != 0) { 161184728Sraj _kvm_err(kd, kd->program, "not a minidump for this platform"); 162184728Sraj return (-1); 163184728Sraj } 164184728Sraj if (vmst->hdr.version != MINIDUMP_VERSION) { 165184728Sraj _kvm_err(kd, kd->program, "wrong minidump version. " 166184728Sraj "Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version); 167184728Sraj return (-1); 168184728Sraj } 169184728Sraj 170184728Sraj /* Skip header and msgbuf */ 171184728Sraj off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize); 172184728Sraj 173184728Sraj vmst->bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize); 174184728Sraj if (vmst->bitmap == NULL) { 175184728Sraj _kvm_err(kd, kd->program, "cannot allocate %d bytes for " 176184728Sraj "bitmap", vmst->hdr.bitmapsize); 177184728Sraj return (-1); 178184728Sraj } 179184728Sraj 180184728Sraj if (pread(kd->pmfd, vmst->bitmap, vmst->hdr.bitmapsize, off) != 181217744Suqs (ssize_t)vmst->hdr.bitmapsize) { 182184728Sraj _kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap", 183184728Sraj vmst->hdr.bitmapsize); 184184728Sraj return (-1); 185184728Sraj } 186184728Sraj off += round_page(vmst->hdr.bitmapsize); 187184728Sraj 188184728Sraj vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize); 189184728Sraj if (vmst->ptemap == NULL) { 190184728Sraj _kvm_err(kd, kd->program, "cannot allocate %d bytes for " 191184728Sraj "ptemap", vmst->hdr.ptesize); 192184728Sraj return (-1); 193184728Sraj } 194184728Sraj 195184728Sraj if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) != 196217744Suqs (ssize_t)vmst->hdr.ptesize) { 197184728Sraj _kvm_err(kd, kd->program, "cannot read %d bytes for ptemap", 198184728Sraj vmst->hdr.ptesize); 199184728Sraj return (-1); 200184728Sraj } 201184728Sraj 202184728Sraj off += vmst->hdr.ptesize; 203184728Sraj 204184728Sraj /* Build physical address hash table for sparse pages */ 205184728Sraj inithash(kd, vmst->bitmap, vmst->hdr.bitmapsize, off); 206184728Sraj 207184728Sraj return (0); 208184728Sraj} 209184728Sraj 210184728Srajint 211184728Sraj_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa) 212184728Sraj{ 213184728Sraj struct vmstate *vm; 214184728Sraj pt_entry_t pte; 215184728Sraj u_long offset, pteindex, a; 216184728Sraj off_t ofs; 217184728Sraj uint32_t *ptemap; 218184728Sraj 219184728Sraj if (ISALIVE(kd)) { 220184728Sraj _kvm_err(kd, 0, "kvm_kvatop called in live kernel!"); 221184728Sraj return (0); 222184728Sraj } 223184728Sraj 224184728Sraj vm = kd->vmst; 225184728Sraj ptemap = vm->ptemap; 226184728Sraj 227184728Sraj if (va >= vm->hdr.kernbase) { 228184728Sraj pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT; 229184728Sraj pte = ptemap[pteindex]; 230184728Sraj if (!pte) { 231184728Sraj _kvm_err(kd, kd->program, "_kvm_vatop: pte not valid"); 232184728Sraj goto invalid; 233184728Sraj } 234184728Sraj if ((pte & L2_TYPE_MASK) == L2_TYPE_L) { 235184728Sraj offset = va & L2_L_OFFSET; 236184728Sraj a = pte & L2_L_FRAME; 237184728Sraj } else if ((pte & L2_TYPE_MASK) == L2_TYPE_S) { 238184728Sraj offset = va & L2_S_OFFSET; 239184728Sraj a = pte & L2_S_FRAME; 240184728Sraj } else 241184728Sraj goto invalid; 242184728Sraj 243184728Sraj ofs = hpt_find(kd, a); 244184728Sraj if (ofs == -1) { 245184728Sraj _kvm_err(kd, kd->program, "_kvm_vatop: physical " 246184728Sraj "address 0x%lx not in minidump", a); 247184728Sraj goto invalid; 248184728Sraj } 249184728Sraj 250184728Sraj *pa = ofs + offset; 251184728Sraj return (PAGE_SIZE - offset); 252184728Sraj 253184728Sraj } else 254184728Sraj _kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx " 255184728Sraj "not minidumped", va); 256184728Sraj 257184728Srajinvalid: 258184728Sraj _kvm_err(kd, 0, "invalid address (0x%lx)", va); 259184728Sraj return (0); 260184728Sraj} 261