185361Sdfr/* $FreeBSD: releng/10.2/lib/libkvm/kvm_ia64.c 269449 2014-08-02 22:25:24Z marcel $ */ 285361Sdfr/* $NetBSD: kvm_alpha.c,v 1.7.2.1 1997/11/02 20:34:26 mellon Exp $ */ 385361Sdfr 485361Sdfr/* 585361Sdfr * Copyright (c) 1994, 1995 Carnegie-Mellon University. 685361Sdfr * All rights reserved. 785361Sdfr * 885361Sdfr * Author: Chris G. Demetriou 985361Sdfr * 1085361Sdfr * Permission to use, copy, modify and distribute this software and 1185361Sdfr * its documentation is hereby granted, provided that both the copyright 1285361Sdfr * notice and this permission notice appear in all copies of the 1385361Sdfr * software, derivative works or modified versions, and any portions 1485361Sdfr * thereof, and that both notices appear in supporting documentation. 1585361Sdfr * 1685361Sdfr * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 1785361Sdfr * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND 1885361Sdfr * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 1985361Sdfr * 2085361Sdfr * Carnegie Mellon requests users of this software to return to 2185361Sdfr * 2285361Sdfr * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 2385361Sdfr * School of Computer Science 2485361Sdfr * Carnegie Mellon University 2585361Sdfr * Pittsburgh PA 15213-3890 2685361Sdfr * 2785361Sdfr * any improvements or extensions that they make and grant Carnegie the 2885361Sdfr * rights to redistribute these changes. 2985361Sdfr */ 3085361Sdfr 3185361Sdfr#include <sys/types.h> 32105607Smarcel#include <sys/elf64.h> 33105607Smarcel#include <sys/mman.h> 3485361Sdfr 35269449Smarcel#ifndef CROSS_LIBKVM 36217744Suqs#include <machine/atomic.h> 37224680Smarcel#include <machine/bootinfo.h> 38269449Smarcel#include <machine/elf.h> 39105607Smarcel#include <machine/pte.h> 40269449Smarcel#else 41269449Smarcel#include "../../sys/ia64/include/atomic.h" 42269449Smarcel#include "../../sys/ia64/include/bootinfo.h" 43269449Smarcel#include "../../sys/ia64/include/elf.h" 44269449Smarcel#include "../../sys/ia64/include/pte.h" 45269449Smarcel#endif 4685361Sdfr 47105607Smarcel#include <kvm.h> 4885361Sdfr#include <limits.h> 49269449Smarcel#include <stdint.h> 5085361Sdfr#include <stdlib.h> 51105607Smarcel#include <unistd.h> 52105607Smarcel 5385361Sdfr#include "kvm_private.h" 5485361Sdfr 55105607Smarcel#define REGION_BASE(n) (((uint64_t)(n)) << 61) 56105607Smarcel#define REGION_ADDR(x) ((x) & ((1LL<<61)-1LL)) 5785361Sdfr 58105607Smarcel#define NKPTEPG(ps) ((ps) / sizeof(struct ia64_lpte)) 59169760Smarcel#define NKPTEDIR(ps) ((ps) >> 3) 60105607Smarcel#define KPTE_PTE_INDEX(va,ps) (((va)/(ps)) % NKPTEPG(ps)) 61169760Smarcel#define KPTE_DIR0_INDEX(va,ps) ((((va)/(ps)) / NKPTEPG(ps)) / NKPTEDIR(ps)) 62169760Smarcel#define KPTE_DIR1_INDEX(va,ps) ((((va)/(ps)) / NKPTEPG(ps)) % NKPTEDIR(ps)) 63105607Smarcel 64224680Smarcel#define PBVM_BASE 0x9ffc000000000000UL 65224680Smarcel#define PBVM_PGSZ (64 * 1024) 66224680Smarcel 67269449Smarceltypedef size_t (a2p_f)(kvm_t *, uint64_t, off_t *); 68269449Smarcel 6985361Sdfrstruct vmstate { 70105607Smarcel void *mmapbase; 71105607Smarcel size_t mmapsize; 72105607Smarcel size_t pagesize; 73105607Smarcel u_long kptdir; 74224680Smarcel u_long *pbvm_pgtbl; 75224680Smarcel u_int pbvm_pgtblsz; 76269449Smarcel a2p_f *kvatop; 7785361Sdfr}; 7885361Sdfr 79105607Smarcel/* 80105607Smarcel * Map the ELF headers into the process' address space. We do this in two 81105607Smarcel * steps: first the ELF header itself and using that information the whole 82105607Smarcel * set of headers. 83105607Smarcel */ 84105607Smarcelstatic int 85269449Smarcelia64_maphdrs(kvm_t *kd, size_t sz) 86105607Smarcel{ 87105607Smarcel struct vmstate *vm = kd->vmst; 88105607Smarcel 89105607Smarcel /* munmap() previous mmap(). */ 90105607Smarcel if (vm->mmapbase != NULL) { 91105607Smarcel munmap(vm->mmapbase, vm->mmapsize); 92105607Smarcel vm->mmapbase = NULL; 93105607Smarcel } 94105607Smarcel 95105607Smarcel vm->mmapsize = sz; 96135585Smarcel vm->mmapbase = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, kd->pmfd, 0); 97105607Smarcel if (vm->mmapbase == MAP_FAILED) { 98105607Smarcel _kvm_err(kd, kd->program, "cannot mmap corefile"); 99105607Smarcel return (-1); 100105607Smarcel } 101105607Smarcel 102105607Smarcel return (0); 103105607Smarcel} 104105607Smarcel 105105607Smarcel/* 106269449Smarcel * Physical core support. 107105607Smarcel */ 108269449Smarcel 109105607Smarcelstatic size_t 110269449Smarcelphys_addr2off(kvm_t *kd, uint64_t pa, off_t *ofs, size_t pgsz) 111105607Smarcel{ 112269449Smarcel Elf64_Ehdr *e; 113269449Smarcel Elf64_Phdr *p; 114269449Smarcel int n; 115105607Smarcel 116269449Smarcel if (pa != REGION_ADDR(pa)) 117269449Smarcel goto fail; 118105607Smarcel 119269449Smarcel e = (Elf64_Ehdr *)(kd->vmst->mmapbase); 120269449Smarcel n = e->e_phnum; 121269449Smarcel p = (Elf64_Phdr *)(void *)((uintptr_t)(void *)e + e->e_phoff); 122105607Smarcel while (n && (pa < p->p_paddr || pa >= p->p_paddr + p->p_memsz)) 123105607Smarcel p++, n--; 124105607Smarcel if (n == 0) 125269449Smarcel goto fail; 126105607Smarcel 127105607Smarcel *ofs = (pa - p->p_paddr) + p->p_offset; 128105607Smarcel if (pgsz == 0) 129105607Smarcel return (p->p_memsz - (pa - p->p_paddr)); 130105607Smarcel return (pgsz - ((size_t)pa & (pgsz - 1))); 131269449Smarcel 132269449Smarcel fail: 133269449Smarcel _kvm_err(kd, kd->program, "invalid physical address %#jx", 134269449Smarcel (uintmax_t)pa); 135269449Smarcel return (0); 136105607Smarcel} 137105607Smarcel 138269449Smarcelstatic size_t 139269449Smarcelphys_kvatop(kvm_t *kd, uint64_t va, off_t *ofs) 140269449Smarcel{ 141269449Smarcel struct ia64_lpte pte; 142269449Smarcel uint64_t pa, pgaddr, pt0addr, pt1addr; 143269449Smarcel size_t pgno, pgsz, pt0no, pt1no; 144269449Smarcel 145269449Smarcel if (va >= REGION_BASE(6)) { 146269449Smarcel /* Regions 6 and 7: direct mapped. */ 147269449Smarcel pa = REGION_ADDR(va); 148269449Smarcel return (phys_addr2off(kd, pa, ofs, 0)); 149269449Smarcel } else if (va >= REGION_BASE(5)) { 150269449Smarcel /* Region 5: Kernel Virtual Memory. */ 151269449Smarcel va = REGION_ADDR(va); 152269449Smarcel pgsz = kd->vmst->pagesize; 153269449Smarcel pt0no = KPTE_DIR0_INDEX(va, pgsz); 154269449Smarcel pt1no = KPTE_DIR1_INDEX(va, pgsz); 155269449Smarcel pgno = KPTE_PTE_INDEX(va, pgsz); 156269449Smarcel if (pt0no >= NKPTEDIR(pgsz)) 157269449Smarcel goto fail; 158269449Smarcel pt0addr = kd->vmst->kptdir + (pt0no << 3); 159269449Smarcel if (kvm_read(kd, pt0addr, &pt1addr, 8) != 8) 160269449Smarcel goto fail; 161269449Smarcel if (pt1addr == 0) 162269449Smarcel goto fail; 163269449Smarcel pt1addr += pt1no << 3; 164269449Smarcel if (kvm_read(kd, pt1addr, &pgaddr, 8) != 8) 165269449Smarcel goto fail; 166269449Smarcel if (pgaddr == 0) 167269449Smarcel goto fail; 168269449Smarcel pgaddr += pgno * sizeof(pte); 169269449Smarcel if (kvm_read(kd, pgaddr, &pte, sizeof(pte)) != sizeof(pte)) 170269449Smarcel goto fail; 171269449Smarcel if (!(pte.pte & PTE_PRESENT)) 172269449Smarcel goto fail; 173269449Smarcel pa = (pte.pte & PTE_PPN_MASK) + (va & (pgsz - 1)); 174269449Smarcel return (phys_addr2off(kd, pa, ofs, pgsz)); 175269449Smarcel } else if (va >= PBVM_BASE) { 176269449Smarcel /* Region 4: Pre-Boot Virtual Memory (PBVM). */ 177269449Smarcel va -= PBVM_BASE; 178269449Smarcel pgsz = PBVM_PGSZ; 179269449Smarcel pt0no = va / pgsz; 180269449Smarcel if (pt0no >= (kd->vmst->pbvm_pgtblsz >> 3)) 181269449Smarcel goto fail; 182269449Smarcel pt0addr = kd->vmst->pbvm_pgtbl[pt0no]; 183269449Smarcel if (!(pt0addr & PTE_PRESENT)) 184269449Smarcel goto fail; 185269449Smarcel pa = (pt0addr & PTE_PPN_MASK) + va % pgsz; 186269449Smarcel return (phys_addr2off(kd, pa, ofs, pgsz)); 187269449Smarcel } 188269449Smarcel 189269449Smarcel fail: 190269449Smarcel _kvm_err(kd, kd->program, "invalid kernel virtual address %#jx", 191269449Smarcel (uintmax_t)va); 192269449Smarcel *ofs = -1; 193269449Smarcel return (0); 194269449Smarcel} 195269449Smarcel 196224680Smarcelstatic ssize_t 197269449Smarcelphys_read(kvm_t *kd, uint64_t pa, void *buf, size_t bufsz) 198224680Smarcel{ 199224680Smarcel off_t ofs; 200224680Smarcel size_t sz; 201224680Smarcel 202269449Smarcel sz = phys_addr2off(kd, pa, &ofs, 0); 203224680Smarcel if (sz < bufsz) 204224680Smarcel return ((ssize_t)sz); 205224680Smarcel 206224680Smarcel if (lseek(kd->pmfd, ofs, 0) == -1) 207224680Smarcel return (-1); 208224680Smarcel return (read(kd->pmfd, buf, bufsz)); 209224680Smarcel} 210224680Smarcel 211269449Smarcel/* 212269449Smarcel * Virtual core support (aka minidump). 213269449Smarcel */ 214269449Smarcel 215269449Smarcelstatic size_t 216269449Smarcelvirt_addr2off(kvm_t *kd, uint64_t va, off_t *ofs, size_t pgsz) 217269449Smarcel{ 218269449Smarcel Elf64_Ehdr *e; 219269449Smarcel Elf64_Phdr *p; 220269449Smarcel int n; 221269449Smarcel 222269449Smarcel if (va < REGION_BASE(4)) 223269449Smarcel goto fail; 224269449Smarcel 225269449Smarcel e = (Elf64_Ehdr *)(kd->vmst->mmapbase); 226269449Smarcel n = e->e_phnum; 227269449Smarcel p = (Elf64_Phdr *)(void *)((uintptr_t)(void *)e + e->e_phoff); 228269449Smarcel while (n && (va < p->p_vaddr || va >= p->p_vaddr + p->p_memsz)) 229269449Smarcel p++, n--; 230269449Smarcel if (n == 0) 231269449Smarcel goto fail; 232269449Smarcel 233269449Smarcel *ofs = (va - p->p_vaddr) + p->p_offset; 234269449Smarcel if (pgsz == 0) 235269449Smarcel return (p->p_memsz - (va - p->p_vaddr)); 236269449Smarcel return (pgsz - ((size_t)va & (pgsz - 1))); 237269449Smarcel 238269449Smarcel fail: 239269449Smarcel _kvm_err(kd, kd->program, "invalid virtual address %#jx", 240269449Smarcel (uintmax_t)va); 241269449Smarcel return (0); 242269449Smarcel} 243269449Smarcel 244269449Smarcelstatic size_t 245269449Smarcelvirt_kvatop(kvm_t *kd, uint64_t va, off_t *ofs) 246269449Smarcel{ 247269449Smarcel 248269449Smarcel return (virt_addr2off(kd, va, ofs, 0)); 249269449Smarcel} 250269449Smarcel 251269449Smarcel/* 252269449Smarcel * KVM architecture support functions. 253269449Smarcel */ 254269449Smarcel 25585361Sdfrvoid 25685478Sdfr_kvm_freevtop(kvm_t *kd) 25785361Sdfr{ 258105607Smarcel struct vmstate *vm = kd->vmst; 25985361Sdfr 260224680Smarcel if (vm->pbvm_pgtbl != NULL) 261224680Smarcel free(vm->pbvm_pgtbl); 262105607Smarcel if (vm->mmapbase != NULL) 263105607Smarcel munmap(vm->mmapbase, vm->mmapsize); 264105607Smarcel free(vm); 265105607Smarcel kd->vmst = NULL; 26685361Sdfr} 26785361Sdfr 26885361Sdfrint 26985478Sdfr_kvm_initvtop(kvm_t *kd) 27085361Sdfr{ 271224680Smarcel struct bootinfo bi; 272217744Suqs struct nlist nl[2]; 273105607Smarcel uint64_t va; 274105607Smarcel Elf64_Ehdr *ehdr; 275105607Smarcel size_t hdrsz; 276224680Smarcel ssize_t sz; 27785361Sdfr 278105607Smarcel kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(*kd->vmst)); 279105607Smarcel if (kd->vmst == NULL) { 28085361Sdfr _kvm_err(kd, kd->program, "cannot allocate vm"); 28185361Sdfr return (-1); 28285361Sdfr } 28385361Sdfr 284269449Smarcel#ifndef CROSS_LIBKVM 285105607Smarcel kd->vmst->pagesize = getpagesize(); 286269449Smarcel#else 287269449Smarcel kd->vmst->pagesize = 8192; 288269449Smarcel#endif 289105607Smarcel 290269449Smarcel if (ia64_maphdrs(kd, sizeof(Elf64_Ehdr)) == -1) 291105607Smarcel return (-1); 292105607Smarcel 293105607Smarcel ehdr = kd->vmst->mmapbase; 294105607Smarcel hdrsz = ehdr->e_phoff + ehdr->e_phentsize * ehdr->e_phnum; 295269449Smarcel if (ia64_maphdrs(kd, hdrsz) == -1) 296105607Smarcel return (-1); 297105607Smarcel 298269449Smarcel kd->vmst->kvatop = (ehdr->e_flags & EF_IA_64_ABSOLUTE) ? 299269449Smarcel phys_kvatop : virt_kvatop; 300269449Smarcel 301105607Smarcel /* 302224680Smarcel * Load the PBVM page table. We need this to resolve PBVM addresses. 303224680Smarcel * The PBVM page table is obtained from the bootinfo structure, of 304269449Smarcel * which the address is given to us in e_entry. If e_entry is 0, then 305269449Smarcel * this is assumed to be a pre-PBVM kernel. 306269449Smarcel * Note that the address of the bootinfo structure is either physical 307269449Smarcel * or virtual, depending on whether the core is physical or virtual. 308224680Smarcel */ 309269449Smarcel if (ehdr->e_entry != 0 && (ehdr->e_flags & EF_IA_64_ABSOLUTE) != 0) { 310269449Smarcel sz = phys_read(kd, ehdr->e_entry, &bi, sizeof(bi)); 311224680Smarcel if (sz != sizeof(bi)) { 312224680Smarcel _kvm_err(kd, kd->program, 313269449Smarcel "cannot read bootinfo at physical address %#jx", 314269449Smarcel (uintmax_t)ehdr->e_entry); 315224680Smarcel return (-1); 316224680Smarcel } 317224680Smarcel if (bi.bi_magic != BOOTINFO_MAGIC) { 318224680Smarcel _kvm_err(kd, kd->program, "invalid bootinfo"); 319224680Smarcel return (-1); 320224680Smarcel } 321224680Smarcel kd->vmst->pbvm_pgtbl = _kvm_malloc(kd, bi.bi_pbvm_pgtblsz); 322224680Smarcel if (kd->vmst->pbvm_pgtbl == NULL) { 323224680Smarcel _kvm_err(kd, kd->program, "cannot allocate page table"); 324224680Smarcel return (-1); 325224680Smarcel } 326224680Smarcel kd->vmst->pbvm_pgtblsz = bi.bi_pbvm_pgtblsz; 327269449Smarcel sz = phys_read(kd, bi.bi_pbvm_pgtbl, kd->vmst->pbvm_pgtbl, 328224680Smarcel bi.bi_pbvm_pgtblsz); 329224680Smarcel if (sz != bi.bi_pbvm_pgtblsz) { 330224680Smarcel _kvm_err(kd, kd->program, 331269449Smarcel "cannot read page table at physical address %#jx", 332269449Smarcel (uintmax_t)bi.bi_pbvm_pgtbl); 333224680Smarcel return (-1); 334224680Smarcel } 335224680Smarcel } else { 336224680Smarcel kd->vmst->pbvm_pgtbl = NULL; 337224680Smarcel kd->vmst->pbvm_pgtblsz = 0; 338224680Smarcel } 339224680Smarcel 340224680Smarcel /* 341105607Smarcel * At this point we've got enough information to use kvm_read() for 342105607Smarcel * direct mapped (ie region 6 and region 7) address, such as symbol 343105607Smarcel * addresses/values. 344105607Smarcel */ 345105607Smarcel 346217744Suqs nl[0].n_name = "ia64_kptdir"; 347217744Suqs nl[1].n_name = 0; 34885361Sdfr 349217744Suqs if (kvm_nlist(kd, nl) != 0) { 35085361Sdfr _kvm_err(kd, kd->program, "bad namelist"); 35185361Sdfr return (-1); 35285361Sdfr } 35385361Sdfr 354217744Suqs if (kvm_read(kd, (nl[0].n_value), &va, sizeof(va)) != sizeof(va)) { 355105607Smarcel _kvm_err(kd, kd->program, "cannot read kptdir"); 356105607Smarcel return (-1); 357105607Smarcel } 358105607Smarcel 359269449Smarcel if (va == REGION_BASE(5)) { 360105607Smarcel _kvm_err(kd, kd->program, "kptdir is itself virtual"); 361105607Smarcel return (-1); 362105607Smarcel } 363105607Smarcel 364105607Smarcel kd->vmst->kptdir = va; 36585361Sdfr return (0); 36685361Sdfr} 36785361Sdfr 36885361Sdfrint 369224680Smarcel_kvm_kvatop(kvm_t *kd, u_long va, off_t *ofs) 37085361Sdfr{ 371269449Smarcel size_t sz; 37285478Sdfr 373269449Smarcel sz = kd->vmst->kvatop(kd, va, ofs); 374269449Smarcel return ((sz > INT_MAX) ? INT_MAX : sz); 37585361Sdfr} 376