kvm_ia64.c revision 217744
1137587Snik/* $FreeBSD: head/lib/libkvm/kvm_ia64.c 217744 2011-01-23 11:08:28Z uqs $ */ 2137587Snik/* $NetBSD: kvm_alpha.c,v 1.7.2.1 1997/11/02 20:34:26 mellon Exp $ */ 3137587Snik 4137587Snik/* 5137587Snik * Copyright (c) 1994, 1995 Carnegie-Mellon University. 6137587Snik * All rights reserved. 7137587Snik * 8137587Snik * Author: Chris G. Demetriou 9137587Snik * 10137587Snik * Permission to use, copy, modify and distribute this software and 11 * its documentation is hereby granted, provided that both the copyright 12 * notice and this permission notice appear in all copies of the 13 * software, derivative works or modified versions, and any portions 14 * thereof, and that both notices appear in supporting documentation. 15 * 16 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 17 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND 18 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 19 * 20 * Carnegie Mellon requests users of this software to return to 21 * 22 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 23 * School of Computer Science 24 * Carnegie Mellon University 25 * Pittsburgh PA 15213-3890 26 * 27 * any improvements or extensions that they make and grant Carnegie the 28 * rights to redistribute these changes. 29 */ 30 31#include <sys/types.h> 32#include <sys/elf64.h> 33#include <sys/mman.h> 34 35#include <machine/atomic.h> 36#include <machine/pte.h> 37 38#include <kvm.h> 39#include <limits.h> 40#include <stdlib.h> 41#include <unistd.h> 42 43#include "kvm_private.h" 44 45#define REGION_BASE(n) (((uint64_t)(n)) << 61) 46#define REGION_ADDR(x) ((x) & ((1LL<<61)-1LL)) 47 48#define NKPTEPG(ps) ((ps) / sizeof(struct ia64_lpte)) 49#define NKPTEDIR(ps) ((ps) >> 3) 50#define KPTE_PTE_INDEX(va,ps) (((va)/(ps)) % NKPTEPG(ps)) 51#define KPTE_DIR0_INDEX(va,ps) ((((va)/(ps)) / NKPTEPG(ps)) / NKPTEDIR(ps)) 52#define KPTE_DIR1_INDEX(va,ps) ((((va)/(ps)) / NKPTEPG(ps)) % NKPTEDIR(ps)) 53 54struct vmstate { 55 void *mmapbase; 56 size_t mmapsize; 57 size_t pagesize; 58 u_long kptdir; 59}; 60 61/* 62 * Map the ELF headers into the process' address space. We do this in two 63 * steps: first the ELF header itself and using that information the whole 64 * set of headers. 65 */ 66static int 67_kvm_maphdrs(kvm_t *kd, size_t sz) 68{ 69 struct vmstate *vm = kd->vmst; 70 71 /* munmap() previous mmap(). */ 72 if (vm->mmapbase != NULL) { 73 munmap(vm->mmapbase, vm->mmapsize); 74 vm->mmapbase = NULL; 75 } 76 77 vm->mmapsize = sz; 78 vm->mmapbase = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, kd->pmfd, 0); 79 if (vm->mmapbase == MAP_FAILED) { 80 _kvm_err(kd, kd->program, "cannot mmap corefile"); 81 return (-1); 82 } 83 84 return (0); 85} 86 87/* 88 * Translate a physical memory address to a file-offset in the crash-dump. 89 */ 90static size_t 91_kvm_pa2off(kvm_t *kd, uint64_t pa, off_t *ofs, size_t pgsz) 92{ 93 Elf64_Ehdr *e = kd->vmst->mmapbase; 94 Elf64_Phdr *p = (Elf64_Phdr*)((char*)e + e->e_phoff); 95 int n = e->e_phnum; 96 97 if (pa != REGION_ADDR(pa)) { 98 _kvm_err(kd, kd->program, "internal error"); 99 return (0); 100 } 101 102 while (n && (pa < p->p_paddr || pa >= p->p_paddr + p->p_memsz)) 103 p++, n--; 104 if (n == 0) 105 return (0); 106 107 *ofs = (pa - p->p_paddr) + p->p_offset; 108 if (pgsz == 0) 109 return (p->p_memsz - (pa - p->p_paddr)); 110 return (pgsz - ((size_t)pa & (pgsz - 1))); 111} 112 113void 114_kvm_freevtop(kvm_t *kd) 115{ 116 struct vmstate *vm = kd->vmst; 117 118 if (vm->mmapbase != NULL) 119 munmap(vm->mmapbase, vm->mmapsize); 120 free(vm); 121 kd->vmst = NULL; 122} 123 124int 125_kvm_initvtop(kvm_t *kd) 126{ 127 struct nlist nl[2]; 128 uint64_t va; 129 Elf64_Ehdr *ehdr; 130 size_t hdrsz; 131 132 kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(*kd->vmst)); 133 if (kd->vmst == NULL) { 134 _kvm_err(kd, kd->program, "cannot allocate vm"); 135 return (-1); 136 } 137 138 kd->vmst->pagesize = getpagesize(); 139 140 if (_kvm_maphdrs(kd, sizeof(Elf64_Ehdr)) == -1) 141 return (-1); 142 143 ehdr = kd->vmst->mmapbase; 144 hdrsz = ehdr->e_phoff + ehdr->e_phentsize * ehdr->e_phnum; 145 if (_kvm_maphdrs(kd, hdrsz) == -1) 146 return (-1); 147 148 /* 149 * At this point we've got enough information to use kvm_read() for 150 * direct mapped (ie region 6 and region 7) address, such as symbol 151 * addresses/values. 152 */ 153 154 nl[0].n_name = "ia64_kptdir"; 155 nl[1].n_name = 0; 156 157 if (kvm_nlist(kd, nl) != 0) { 158 _kvm_err(kd, kd->program, "bad namelist"); 159 return (-1); 160 } 161 162 if (kvm_read(kd, (nl[0].n_value), &va, sizeof(va)) != sizeof(va)) { 163 _kvm_err(kd, kd->program, "cannot read kptdir"); 164 return (-1); 165 } 166 167 if (va < REGION_BASE(6)) { 168 _kvm_err(kd, kd->program, "kptdir is itself virtual"); 169 return (-1); 170 } 171 172 kd->vmst->kptdir = va; 173 return (0); 174} 175 176int 177_kvm_kvatop(kvm_t *kd, u_long va, off_t *pa) 178{ 179 struct ia64_lpte pte; 180 uint64_t pgaddr, pt0addr, pt1addr; 181 size_t pgno, pgsz, pt0no, pt1no; 182 183 if (va >= REGION_BASE(6)) { 184 /* Regions 6 and 7: direct mapped. */ 185 return (_kvm_pa2off(kd, REGION_ADDR(va), pa, 0)); 186 } else if (va >= REGION_BASE(5)) { 187 /* Region 5: virtual. */ 188 va = REGION_ADDR(va); 189 pgsz = kd->vmst->pagesize; 190 pt0no = KPTE_DIR0_INDEX(va, pgsz); 191 pt1no = KPTE_DIR1_INDEX(va, pgsz); 192 pgno = KPTE_PTE_INDEX(va, pgsz); 193 if (pt0no >= NKPTEDIR(pgsz)) 194 goto fail; 195 pt0addr = kd->vmst->kptdir + (pt0no << 3); 196 if (kvm_read(kd, pt0addr, &pt1addr, 8) != 8) 197 goto fail; 198 if (pt1addr == 0) 199 goto fail; 200 pt1addr += pt1no << 3; 201 if (kvm_read(kd, pt1addr, &pgaddr, 8) != 8) 202 goto fail; 203 if (pgaddr == 0) 204 goto fail; 205 pgaddr += pgno * sizeof(pte); 206 if (kvm_read(kd, pgaddr, &pte, sizeof(pte)) != sizeof(pte)) 207 goto fail; 208 if (!(pte.pte & PTE_PRESENT)) 209 goto fail; 210 va = (pte.pte & PTE_PPN_MASK) + (va & (pgsz - 1)); 211 return (_kvm_pa2off(kd, va, pa, pgsz)); 212 } 213 214 fail: 215 _kvm_err(kd, kd->program, "invalid kernel virtual address"); 216 *pa = ~0UL; 217 return (0); 218} 219