1214904Sgonzo/*- 2291372Sjhb * Copyright (c) 2010 Oleksandr Tymoshenko 3214904Sgonzo * Copyright (c) 2008 Semihalf, Grzegorz Bernacki 4214904Sgonzo * Copyright (c) 2006 Peter Wemm 5214904Sgonzo * 6214904Sgonzo * Redistribution and use in source and binary forms, with or without 7214904Sgonzo * modification, are permitted provided that the following conditions 8214904Sgonzo * are met: 9214904Sgonzo * 1. Redistributions of source code must retain the above copyright 10214904Sgonzo * notice, this list of conditions and the following disclaimer. 11214904Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 12214904Sgonzo * notice, this list of conditions and the following disclaimer in the 13214904Sgonzo * documentation and/or other materials provided with the distribution. 14214904Sgonzo * 15214904Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16214904Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17214904Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18214904Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19214904Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20214904Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21214904Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22214904Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23214904Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24214904Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25214904Sgonzo * SUCH DAMAGE. 26214904Sgonzo * 27214904Sgonzo * From: FreeBSD: src/lib/libkvm/kvm_minidump_arm.c r214223 28214904Sgonzo */ 29214904Sgonzo 30214904Sgonzo#include <sys/cdefs.h> 31214904Sgonzo__FBSDID("$FreeBSD: releng/11.0/lib/libkvm/kvm_minidump_mips.c 298485 2016-04-22 18:05:34Z ngie $"); 32214904Sgonzo 33214904Sgonzo/* 34214904Sgonzo * MIPS machine dependent routines for kvm and minidumps. 35214904Sgonzo */ 36214904Sgonzo 37214904Sgonzo#include <sys/param.h> 38291406Sjhb#include <kvm.h> 39291406Sjhb#include <limits.h> 40291406Sjhb#include <stdint.h> 41214904Sgonzo#include <stdlib.h> 42214904Sgonzo#include <string.h> 43214904Sgonzo#include <unistd.h> 44214904Sgonzo 45291406Sjhb#include "../../sys/mips/include/cpuregs.h" 46291406Sjhb#include "../../sys/mips/include/minidump.h" 47214904Sgonzo 48214904Sgonzo#include "kvm_private.h" 49291406Sjhb#include "kvm_mips.h" 50214904Sgonzo 51291406Sjhb#define mips_round_page(x) roundup2((kvaddr_t)(x), MIPS_PAGE_SIZE) 52214904Sgonzo 53214904Sgonzostruct vmstate { 54214904Sgonzo struct minidumphdr hdr; 55291406Sjhb struct hpt hpt; 56214904Sgonzo void *ptemap; 57291406Sjhb int pte_size; 58214904Sgonzo}; 59214904Sgonzo 60214904Sgonzostatic int 61291406Sjhb_mips_minidump_probe(kvm_t *kd) 62214904Sgonzo{ 63214904Sgonzo 64291406Sjhb if (kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS32 && 65291406Sjhb kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS64) 66291406Sjhb return (0); 67291406Sjhb if (kd->nlehdr.e_machine != EM_MIPS) 68291406Sjhb return (0); 69291406Sjhb return (_kvm_is_minidump(kd)); 70214904Sgonzo} 71214904Sgonzo 72291406Sjhbstatic void 73291406Sjhb_mips_minidump_freevtop(kvm_t *kd) 74214904Sgonzo{ 75214904Sgonzo struct vmstate *vm = kd->vmst; 76214904Sgonzo 77291406Sjhb _kvm_hpt_free(&vm->hpt); 78214904Sgonzo if (vm->ptemap) 79214904Sgonzo free(vm->ptemap); 80214904Sgonzo free(vm); 81214904Sgonzo kd->vmst = NULL; 82214904Sgonzo} 83214904Sgonzo 84291406Sjhbstatic int 85291406Sjhb_mips_minidump_initvtop(kvm_t *kd) 86214904Sgonzo{ 87214904Sgonzo struct vmstate *vmst; 88291406Sjhb uint32_t *bitmap; 89214904Sgonzo off_t off; 90214904Sgonzo 91214904Sgonzo vmst = _kvm_malloc(kd, sizeof(*vmst)); 92298485Sngie if (vmst == NULL) { 93214904Sgonzo _kvm_err(kd, kd->program, "cannot allocate vm"); 94214904Sgonzo return (-1); 95214904Sgonzo } 96214904Sgonzo 97214904Sgonzo kd->vmst = vmst; 98214904Sgonzo 99291406Sjhb if (kd->nlehdr.e_ident[EI_CLASS] == ELFCLASS64 || 100291406Sjhb kd->nlehdr.e_flags & EF_MIPS_ABI2) 101291406Sjhb vmst->pte_size = 64; 102291406Sjhb else 103291406Sjhb vmst->pte_size = 32; 104291406Sjhb 105214904Sgonzo if (pread(kd->pmfd, &vmst->hdr, 106214904Sgonzo sizeof(vmst->hdr), 0) != sizeof(vmst->hdr)) { 107214904Sgonzo _kvm_err(kd, kd->program, "cannot read dump header"); 108214904Sgonzo return (-1); 109214904Sgonzo } 110214904Sgonzo 111214904Sgonzo if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic, 112214904Sgonzo sizeof(vmst->hdr.magic)) != 0) { 113214904Sgonzo _kvm_err(kd, kd->program, "not a minidump for this platform"); 114214904Sgonzo return (-1); 115214904Sgonzo } 116291406Sjhb vmst->hdr.version = _kvm32toh(kd, vmst->hdr.version); 117214904Sgonzo if (vmst->hdr.version != MINIDUMP_VERSION) { 118214904Sgonzo _kvm_err(kd, kd->program, "wrong minidump version. " 119214904Sgonzo "Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version); 120214904Sgonzo return (-1); 121214904Sgonzo } 122291406Sjhb vmst->hdr.msgbufsize = _kvm32toh(kd, vmst->hdr.msgbufsize); 123291406Sjhb vmst->hdr.bitmapsize = _kvm32toh(kd, vmst->hdr.bitmapsize); 124291406Sjhb vmst->hdr.ptesize = _kvm32toh(kd, vmst->hdr.ptesize); 125291406Sjhb vmst->hdr.kernbase = _kvm64toh(kd, vmst->hdr.kernbase); 126291406Sjhb vmst->hdr.dmapbase = _kvm64toh(kd, vmst->hdr.dmapbase); 127291406Sjhb vmst->hdr.dmapend = _kvm64toh(kd, vmst->hdr.dmapend); 128214904Sgonzo 129214904Sgonzo /* Skip header and msgbuf */ 130291406Sjhb off = MIPS_PAGE_SIZE + mips_round_page(vmst->hdr.msgbufsize); 131214904Sgonzo 132291406Sjhb bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize); 133291406Sjhb if (bitmap == NULL) { 134214904Sgonzo _kvm_err(kd, kd->program, "cannot allocate %d bytes for " 135214904Sgonzo "bitmap", vmst->hdr.bitmapsize); 136214904Sgonzo return (-1); 137214904Sgonzo } 138214904Sgonzo 139291406Sjhb if (pread(kd->pmfd, bitmap, vmst->hdr.bitmapsize, off) != 140217744Suqs (ssize_t)vmst->hdr.bitmapsize) { 141214904Sgonzo _kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap", 142214904Sgonzo vmst->hdr.bitmapsize); 143291406Sjhb free(bitmap); 144214904Sgonzo return (-1); 145214904Sgonzo } 146291406Sjhb off += mips_round_page(vmst->hdr.bitmapsize); 147214904Sgonzo 148214904Sgonzo vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize); 149214904Sgonzo if (vmst->ptemap == NULL) { 150214904Sgonzo _kvm_err(kd, kd->program, "cannot allocate %d bytes for " 151214904Sgonzo "ptemap", vmst->hdr.ptesize); 152291406Sjhb free(bitmap); 153214904Sgonzo return (-1); 154214904Sgonzo } 155214904Sgonzo 156214904Sgonzo if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) != 157217744Suqs (ssize_t)vmst->hdr.ptesize) { 158214904Sgonzo _kvm_err(kd, kd->program, "cannot read %d bytes for ptemap", 159214904Sgonzo vmst->hdr.ptesize); 160291406Sjhb free(bitmap); 161214904Sgonzo return (-1); 162214904Sgonzo } 163214904Sgonzo 164214904Sgonzo off += vmst->hdr.ptesize; 165214904Sgonzo 166214904Sgonzo /* Build physical address hash table for sparse pages */ 167291406Sjhb _kvm_hpt_init(kd, &vmst->hpt, bitmap, vmst->hdr.bitmapsize, off, 168291406Sjhb MIPS_PAGE_SIZE, sizeof(*bitmap)); 169291406Sjhb free(bitmap); 170214904Sgonzo 171214904Sgonzo return (0); 172214904Sgonzo} 173214904Sgonzo 174291406Sjhbstatic int 175291406Sjhb_mips_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa) 176214904Sgonzo{ 177214904Sgonzo struct vmstate *vm; 178291406Sjhb uint64_t pte; 179291406Sjhb mips_physaddr_t offset, a; 180291406Sjhb kvaddr_t pteindex; 181214904Sgonzo off_t ofs; 182291406Sjhb uint32_t *ptemap32; 183291406Sjhb uint64_t *ptemap64; 184214904Sgonzo 185214904Sgonzo if (ISALIVE(kd)) { 186291406Sjhb _kvm_err(kd, 0, "_mips_minidump_kvatop called in live kernel!"); 187214904Sgonzo return (0); 188214904Sgonzo } 189214904Sgonzo 190291406Sjhb offset = va & MIPS_PAGE_MASK; 191214904Sgonzo /* Operate with page-aligned address */ 192291406Sjhb va &= ~MIPS_PAGE_MASK; 193214904Sgonzo 194214904Sgonzo vm = kd->vmst; 195291406Sjhb ptemap32 = vm->ptemap; 196291406Sjhb ptemap64 = vm->ptemap; 197214904Sgonzo 198291406Sjhb if (kd->nlehdr.e_ident[EI_CLASS] == ELFCLASS64) { 199291406Sjhb if (va >= MIPS_XKPHYS_START && va < MIPS_XKPHYS_END) { 200291406Sjhb a = va & MIPS_XKPHYS_PHYS_MASK; 201291406Sjhb goto found; 202291406Sjhb } 203291406Sjhb if (va >= MIPS64_KSEG0_START && va < MIPS64_KSEG0_END) { 204291406Sjhb a = va & MIPS_KSEG0_PHYS_MASK; 205291406Sjhb goto found; 206291406Sjhb } 207291406Sjhb if (va >= MIPS64_KSEG1_START && va < MIPS64_KSEG1_END) { 208291406Sjhb a = va & MIPS_KSEG0_PHYS_MASK; 209291406Sjhb goto found; 210291406Sjhb } 211291406Sjhb } else { 212291406Sjhb if (va >= MIPS32_KSEG0_START && va < MIPS32_KSEG0_END) { 213291406Sjhb a = va & MIPS_KSEG0_PHYS_MASK; 214291406Sjhb goto found; 215291406Sjhb } 216291406Sjhb if (va >= MIPS32_KSEG1_START && va < MIPS32_KSEG1_END) { 217291406Sjhb a = va & MIPS_KSEG0_PHYS_MASK; 218291406Sjhb goto found; 219291406Sjhb } 220291406Sjhb } 221291406Sjhb if (va >= vm->hdr.kernbase) { 222291406Sjhb pteindex = (va - vm->hdr.kernbase) >> MIPS_PAGE_SHIFT; 223291406Sjhb if (vm->pte_size == 64) { 224291406Sjhb pte = _kvm64toh(kd, ptemap64[pteindex]); 225291406Sjhb a = MIPS64_PTE_TO_PA(pte); 226291406Sjhb } else { 227291406Sjhb pte = _kvm32toh(kd, ptemap32[pteindex]); 228291406Sjhb a = MIPS32_PTE_TO_PA(pte); 229291406Sjhb } 230214904Sgonzo if (!pte) { 231291406Sjhb _kvm_err(kd, kd->program, "_mips_minidump_kvatop: pte " 232291406Sjhb "not valid"); 233214904Sgonzo goto invalid; 234214904Sgonzo } 235214904Sgonzo } else { 236291406Sjhb _kvm_err(kd, kd->program, "_mips_minidump_kvatop: virtual " 237291406Sjhb "address 0x%jx not minidumped", (uintmax_t)va); 238214904Sgonzo return (0); 239214904Sgonzo } 240214904Sgonzo 241291406Sjhbfound: 242291406Sjhb ofs = _kvm_hpt_find(&vm->hpt, a); 243214904Sgonzo if (ofs == -1) { 244291406Sjhb _kvm_err(kd, kd->program, "_mips_minidump_kvatop: physical " 245291406Sjhb "address 0x%jx not in minidump", (uintmax_t)a); 246214904Sgonzo goto invalid; 247214904Sgonzo } 248214904Sgonzo 249214904Sgonzo *pa = ofs + offset; 250291406Sjhb return (MIPS_PAGE_SIZE - offset); 251214904Sgonzo 252214904Sgonzo 253214904Sgonzoinvalid: 254291406Sjhb _kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va); 255214904Sgonzo return (0); 256214904Sgonzo} 257291406Sjhb 258291406Sjhbstatic int 259291406Sjhb_mips_native(kvm_t *kd) 260291406Sjhb{ 261291406Sjhb 262291406Sjhb#ifdef __mips__ 263291406Sjhb#ifdef __mips_n64 264291406Sjhb if (kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS64) 265291406Sjhb return (0); 266291406Sjhb#else 267291406Sjhb if (kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS32) 268291406Sjhb return (0); 269291406Sjhb#ifdef __mips_n32 270291406Sjhb if (!(kd->nlehdr.e_flags & EF_MIPS_ABI2)) 271291406Sjhb return (0); 272291406Sjhb#else 273291406Sjhb if (kd->nlehdr.e_flags & EF_MIPS_ABI2) 274291406Sjhb return (0); 275291406Sjhb#endif 276291406Sjhb#endif 277291406Sjhb#if _BYTE_ORDER == _LITTLE_ENDIAN 278291406Sjhb return (kd->nlehdr.e_ident[EI_DATA] == ELFDATA2LSB); 279291406Sjhb#else 280291406Sjhb return (kd->nlehdr.e_ident[EI_DATA] == ELFDATA2MSB); 281291406Sjhb#endif 282291406Sjhb#else 283291406Sjhb return (0); 284291406Sjhb#endif 285291406Sjhb} 286291406Sjhb 287291406Sjhbstruct kvm_arch kvm_mips_minidump = { 288291406Sjhb .ka_probe = _mips_minidump_probe, 289291406Sjhb .ka_initvtop = _mips_minidump_initvtop, 290291406Sjhb .ka_freevtop = _mips_minidump_freevtop, 291291406Sjhb .ka_kvatop = _mips_minidump_kvatop, 292291406Sjhb .ka_native = _mips_native, 293291406Sjhb}; 294291406Sjhb 295291406SjhbKVM_ARCH(kvm_mips_minidump); 296