185361Sdfr/* $FreeBSD$ */
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