kvm_ia64.c revision 169760
185361Sdfr/* $FreeBSD: head/lib/libkvm/kvm_ia64.c 169760 2007-05-19 13:11:27Z 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
35105607Smarcel#include <machine/pte.h>
3685361Sdfr
37105607Smarcel#include <kvm.h>
3885361Sdfr#include <limits.h>
3985361Sdfr#include <stdlib.h>
40105607Smarcel#include <unistd.h>
41105607Smarcel
4285361Sdfr#include "kvm_private.h"
4385361Sdfr
44105607Smarcel#define	REGION_BASE(n)		(((uint64_t)(n)) << 61)
45105607Smarcel#define	REGION_ADDR(x)		((x) & ((1LL<<61)-1LL))
4685361Sdfr
47105607Smarcel#define	NKPTEPG(ps)		((ps) / sizeof(struct ia64_lpte))
48169760Smarcel#define	NKPTEDIR(ps)		((ps) >> 3)
49105607Smarcel#define	KPTE_PTE_INDEX(va,ps)	(((va)/(ps)) % NKPTEPG(ps))
50169760Smarcel#define	KPTE_DIR0_INDEX(va,ps)	((((va)/(ps)) / NKPTEPG(ps)) / NKPTEDIR(ps))
51169760Smarcel#define	KPTE_DIR1_INDEX(va,ps)	((((va)/(ps)) / NKPTEPG(ps)) % NKPTEDIR(ps))
52105607Smarcel
5385361Sdfrstruct vmstate {
54105607Smarcel	void	*mmapbase;
55105607Smarcel	size_t	mmapsize;
56105607Smarcel	size_t	pagesize;
57105607Smarcel	u_long	kptdir;
5885361Sdfr};
5985361Sdfr
60105607Smarcel/*
61105607Smarcel * Map the ELF headers into the process' address space. We do this in two
62105607Smarcel * steps: first the ELF header itself and using that information the whole
63105607Smarcel * set of headers.
64105607Smarcel */
65105607Smarcelstatic int
66105607Smarcel_kvm_maphdrs(kvm_t *kd, size_t sz)
67105607Smarcel{
68105607Smarcel	struct vmstate *vm = kd->vmst;
69105607Smarcel
70105607Smarcel	/* munmap() previous mmap(). */
71105607Smarcel	if (vm->mmapbase != NULL) {
72105607Smarcel		munmap(vm->mmapbase, vm->mmapsize);
73105607Smarcel		vm->mmapbase = NULL;
74105607Smarcel	}
75105607Smarcel
76105607Smarcel	vm->mmapsize = sz;
77135585Smarcel	vm->mmapbase = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, kd->pmfd, 0);
78105607Smarcel	if (vm->mmapbase == MAP_FAILED) {
79105607Smarcel		_kvm_err(kd, kd->program, "cannot mmap corefile");
80105607Smarcel		return (-1);
81105607Smarcel	}
82105607Smarcel
83105607Smarcel	return (0);
84105607Smarcel}
85105607Smarcel
86105607Smarcel/*
87105607Smarcel * Translate a physical memory address to a file-offset in the crash-dump.
88105607Smarcel */
89105607Smarcelstatic size_t
90147672Speter_kvm_pa2off(kvm_t *kd, uint64_t pa, off_t *ofs, size_t pgsz)
91105607Smarcel{
92105607Smarcel	Elf64_Ehdr *e = kd->vmst->mmapbase;
93105607Smarcel	Elf64_Phdr *p = (Elf64_Phdr*)((char*)e + e->e_phoff);
94105607Smarcel	int n = e->e_phnum;
95105607Smarcel
96105607Smarcel	if (pa != REGION_ADDR(pa)) {
97105607Smarcel		_kvm_err(kd, kd->program, "internal error");
98105607Smarcel		return (0);
99105607Smarcel	}
100105607Smarcel
101105607Smarcel	while (n && (pa < p->p_paddr || pa >= p->p_paddr + p->p_memsz))
102105607Smarcel		p++, n--;
103105607Smarcel	if (n == 0)
104105607Smarcel		return (0);
105105607Smarcel
106105607Smarcel	*ofs = (pa - p->p_paddr) + p->p_offset;
107105607Smarcel	if (pgsz == 0)
108105607Smarcel		return (p->p_memsz - (pa - p->p_paddr));
109105607Smarcel	return (pgsz - ((size_t)pa & (pgsz - 1)));
110105607Smarcel}
111105607Smarcel
11285361Sdfrvoid
11385478Sdfr_kvm_freevtop(kvm_t *kd)
11485361Sdfr{
115105607Smarcel	struct vmstate *vm = kd->vmst;
11685361Sdfr
117105607Smarcel	if (vm->mmapbase != NULL)
118105607Smarcel		munmap(vm->mmapbase, vm->mmapsize);
119105607Smarcel	free(vm);
120105607Smarcel	kd->vmst = NULL;
12185361Sdfr}
12285361Sdfr
12385361Sdfrint
12485478Sdfr_kvm_initvtop(kvm_t *kd)
12585361Sdfr{
12685361Sdfr	struct nlist nlist[2];
127105607Smarcel	uint64_t va;
128105607Smarcel	Elf64_Ehdr *ehdr;
129105607Smarcel	size_t hdrsz;
13085361Sdfr
131105607Smarcel	kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(*kd->vmst));
132105607Smarcel	if (kd->vmst == NULL) {
13385361Sdfr		_kvm_err(kd, kd->program, "cannot allocate vm");
13485361Sdfr		return (-1);
13585361Sdfr	}
13685361Sdfr
137105607Smarcel	kd->vmst->pagesize = getpagesize();
138105607Smarcel
139105607Smarcel	if (_kvm_maphdrs(kd, sizeof(Elf64_Ehdr)) == -1)
140105607Smarcel		return (-1);
141105607Smarcel
142105607Smarcel	ehdr = kd->vmst->mmapbase;
143105607Smarcel	hdrsz = ehdr->e_phoff + ehdr->e_phentsize * ehdr->e_phnum;
144105607Smarcel	if (_kvm_maphdrs(kd, hdrsz) == -1)
145105607Smarcel		return (-1);
146105607Smarcel
147105607Smarcel	/*
148105607Smarcel	 * At this point we've got enough information to use kvm_read() for
149105607Smarcel	 * direct mapped (ie region 6 and region 7) address, such as symbol
150105607Smarcel	 * addresses/values.
151105607Smarcel	 */
152105607Smarcel
153115084Smarcel	nlist[0].n_name = "ia64_kptdir";
15485361Sdfr	nlist[1].n_name = 0;
15585361Sdfr
15685361Sdfr	if (kvm_nlist(kd, nlist) != 0) {
15785361Sdfr		_kvm_err(kd, kd->program, "bad namelist");
15885361Sdfr		return (-1);
15985361Sdfr	}
16085361Sdfr
161105607Smarcel	if (kvm_read(kd, (nlist[0].n_value), &va, sizeof(va)) != sizeof(va)) {
162105607Smarcel		_kvm_err(kd, kd->program, "cannot read kptdir");
163105607Smarcel		return (-1);
164105607Smarcel	}
165105607Smarcel
166105607Smarcel	if (va < REGION_BASE(6)) {
167105607Smarcel		_kvm_err(kd, kd->program, "kptdir is itself virtual");
168105607Smarcel		return (-1);
169105607Smarcel	}
170105607Smarcel
171105607Smarcel	kd->vmst->kptdir = va;
17285361Sdfr	return (0);
17385361Sdfr}
17485361Sdfr
17585361Sdfrint
176147672Speter_kvm_kvatop(kvm_t *kd, u_long va, off_t *pa)
17785361Sdfr{
17885478Sdfr	struct ia64_lpte pte;
179169760Smarcel	uint64_t pgaddr, pt0addr, pt1addr;
180169760Smarcel	size_t pgno, pgsz, pt0no, pt1no;
18185478Sdfr
182105607Smarcel	if (va >= REGION_BASE(6)) {
183105607Smarcel		/* Regions 6 and 7: direct mapped. */
184105607Smarcel		return (_kvm_pa2off(kd, REGION_ADDR(va), pa, 0));
185105607Smarcel	} else if (va >= REGION_BASE(5)) {
186105607Smarcel		/* Region 5: virtual. */
187105607Smarcel		va = REGION_ADDR(va);
188105607Smarcel		pgsz = kd->vmst->pagesize;
189169760Smarcel		pt0no = KPTE_DIR0_INDEX(va, pgsz);
190169760Smarcel		pt1no = KPTE_DIR1_INDEX(va, pgsz);
191105607Smarcel		pgno = KPTE_PTE_INDEX(va, pgsz);
192169760Smarcel		if (pt0no >= NKPTEDIR(pgsz))
193105607Smarcel			goto fail;
194169760Smarcel		pt0addr = kd->vmst->kptdir + (pt0no << 3);
195169760Smarcel		if (kvm_read(kd, pt0addr, &pt1addr, 8) != 8)
196105607Smarcel			goto fail;
197169760Smarcel		if (pt1addr == 0)
198169760Smarcel			goto fail;
199169760Smarcel		pt1addr += pt1no << 3;
200169760Smarcel		if (kvm_read(kd, pt1addr, &pgaddr, 8) != 8)
201169760Smarcel			goto fail;
202105607Smarcel		if (pgaddr == 0)
203105607Smarcel			goto fail;
204169760Smarcel		pgaddr += pgno * sizeof(pte);
205105607Smarcel		if (kvm_read(kd, pgaddr, &pte, sizeof(pte)) != sizeof(pte))
206105607Smarcel			goto fail;
207135590Smarcel		if (!(pte.pte & PTE_PRESENT))
208105607Smarcel			goto fail;
209135590Smarcel		va = (pte.pte & PTE_PPN_MASK) + (va & (pgsz - 1));
210105607Smarcel		return (_kvm_pa2off(kd, va, pa, pgsz));
21185361Sdfr	}
21285361Sdfr
213105607Smarcel fail:
214105607Smarcel	_kvm_err(kd, kd->program, "invalid kernel virtual address");
215105607Smarcel	*pa = ~0UL;
216105607Smarcel	return (0);
21785361Sdfr}
218