11602Srgrimes/*-
21602Srgrimes * Copyright (c) 1989, 1992, 1993
31602Srgrimes *	The Regents of the University of California.  All rights reserved.
41602Srgrimes *
51602Srgrimes * This code is derived from software developed by the Computer Systems
61602Srgrimes * Engineering group at Lawrence Berkeley Laboratory under DARPA contract
71602Srgrimes * BG 91-66 and contributed to Berkeley.
81602Srgrimes *
91602Srgrimes * Redistribution and use in source and binary forms, with or without
101602Srgrimes * modification, are permitted provided that the following conditions
111602Srgrimes * are met:
121602Srgrimes * 1. Redistributions of source code must retain the above copyright
131602Srgrimes *    notice, this list of conditions and the following disclaimer.
141602Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
151602Srgrimes *    notice, this list of conditions and the following disclaimer in the
161602Srgrimes *    documentation and/or other materials provided with the distribution.
171602Srgrimes * 4. Neither the name of the University nor the names of its contributors
181602Srgrimes *    may be used to endorse or promote products derived from this software
191602Srgrimes *    without specific prior written permission.
201602Srgrimes *
211602Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221602Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231602Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241602Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251602Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261602Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271602Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281602Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291602Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301602Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311602Srgrimes * SUCH DAMAGE.
321602Srgrimes */
331602Srgrimes
3483551Sdillon#include <sys/cdefs.h>
3583551Sdillon__FBSDID("$FreeBSD: stable/11/lib/libkvm/kvm_amd64.c 316126 2017-03-29 07:30:59Z ngie $");
3683551Sdillon
371602Srgrimes#if defined(LIBC_SCCS) && !defined(lint)
3855127Speter#if 0
391602Srgrimesstatic char sccsid[] = "@(#)kvm_hp300.c	8.1 (Berkeley) 6/4/93";
4055127Speter#endif
411602Srgrimes#endif /* LIBC_SCCS and not lint */
421602Srgrimes
431602Srgrimes/*
44114330Speter * AMD64 machine dependent routines for kvm.  Hopefully, the forthcoming
451602Srgrimes * vm code will one day obsolete this module.
461602Srgrimes */
471602Srgrimes
481602Srgrimes#include <sys/param.h>
49291406Sjhb#include <sys/endian.h>
50291406Sjhb#include <stdint.h>
5117141Sjkh#include <stdlib.h>
52194186Sed#include <string.h>
531602Srgrimes#include <unistd.h>
541602Srgrimes#include <kvm.h>
551602Srgrimes
561602Srgrimes#include <limits.h>
571602Srgrimes
581602Srgrimes#include "kvm_private.h"
59291406Sjhb#include "kvm_amd64.h"
601602Srgrimes
611602Srgrimesstruct vmstate {
62291406Sjhb	size_t		phnum;
63291406Sjhb	GElf_Phdr	*phdr;
64291406Sjhb	amd64_pml4e_t	*PML4;
651602Srgrimes};
661602Srgrimes
67147672Speter/*
68147672Speter * Translate a physical memory address to a file-offset in the crash-dump.
69147672Speter */
70147672Speterstatic size_t
71147672Speter_kvm_pa2off(kvm_t *kd, uint64_t pa, off_t *ofs)
72147672Speter{
73291406Sjhb	struct vmstate *vm = kd->vmst;
74291406Sjhb	GElf_Phdr *p;
75291406Sjhb	size_t n;
76147672Speter
77170772Ssimokawa	if (kd->rawdump) {
78170772Ssimokawa		*ofs = pa;
79291406Sjhb		return (AMD64_PAGE_SIZE - (pa & AMD64_PAGE_MASK));
80170772Ssimokawa	}
81170772Ssimokawa
82291406Sjhb	p = vm->phdr;
83291406Sjhb	n = vm->phnum;
84147672Speter	while (n && (pa < p->p_paddr || pa >= p->p_paddr + p->p_memsz))
85147672Speter		p++, n--;
86147672Speter	if (n == 0)
87147672Speter		return (0);
88147672Speter	*ofs = (pa - p->p_paddr) + p->p_offset;
89291406Sjhb	return (AMD64_PAGE_SIZE - (pa & AMD64_PAGE_MASK));
90147672Speter}
91147672Speter
92291406Sjhbstatic void
93291406Sjhb_amd64_freevtop(kvm_t *kd)
9418798Speter{
95147672Speter	struct vmstate *vm = kd->vmst;
96147672Speter
97147672Speter	if (vm->PML4)
98147672Speter		free(vm->PML4);
99291406Sjhb	free(vm->phdr);
100147672Speter	free(vm);
101147672Speter	kd->vmst = NULL;
1021602Srgrimes}
1031602Srgrimes
104291406Sjhbstatic int
105291406Sjhb_amd64_probe(kvm_t *kd)
10618798Speter{
1071602Srgrimes
108291406Sjhb	return (_kvm_probe_elf_kernel(kd, ELFCLASS64, EM_X86_64) &&
109291406Sjhb	    !_kvm_is_minidump(kd));
110291406Sjhb}
111157911Speter
112291406Sjhbstatic int
113291406Sjhb_amd64_initvtop(kvm_t *kd)
114291406Sjhb{
115291406Sjhb	struct kvm_nlist nl[2];
116291406Sjhb	amd64_physaddr_t pa;
117291406Sjhb	kvaddr_t kernbase;
118291406Sjhb	amd64_pml4e_t *PML4;
119291406Sjhb
120147672Speter	kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(*kd->vmst));
121298485Sngie	if (kd->vmst == NULL) {
1221603Srgrimes		_kvm_err(kd, kd->program, "cannot allocate vm");
1231602Srgrimes		return (-1);
1241603Srgrimes	}
125147672Speter	kd->vmst->PML4 = 0;
1261602Srgrimes
127170772Ssimokawa	if (kd->rawdump == 0) {
128291406Sjhb		if (_kvm_read_core_phdrs(kd, &kd->vmst->phnum,
129291406Sjhb		    &kd->vmst->phdr) == -1)
130170772Ssimokawa			return (-1);
131170772Ssimokawa	}
132147672Speter
133217744Suqs	nl[0].n_name = "kernbase";
134217744Suqs	nl[1].n_name = 0;
1351602Srgrimes
136291406Sjhb	if (kvm_nlist2(kd, nl) != 0) {
137129452Speter		_kvm_err(kd, kd->program, "bad namelist - no kernbase");
138129452Speter		return (-1);
139129452Speter	}
140217744Suqs	kernbase = nl[0].n_value;
14182263Speter
142217744Suqs	nl[0].n_name = "KPML4phys";
143217744Suqs	nl[1].n_name = 0;
14482263Speter
145291406Sjhb	if (kvm_nlist2(kd, nl) != 0) {
146129452Speter		_kvm_err(kd, kd->program, "bad namelist - no KPML4phys");
1471602Srgrimes		return (-1);
1481602Srgrimes	}
149291406Sjhb	if (kvm_read2(kd, (nl[0].n_value - kernbase), &pa, sizeof(pa)) !=
15082263Speter	    sizeof(pa)) {
151129452Speter		_kvm_err(kd, kd->program, "cannot read KPML4phys");
1521602Srgrimes		return (-1);
1531602Srgrimes	}
154291406Sjhb	pa = le64toh(pa);
155291406Sjhb	PML4 = _kvm_malloc(kd, AMD64_PAGE_SIZE);
156298485Sngie	if (PML4 == NULL) {
157298485Sngie		_kvm_err(kd, kd->program, "cannot allocate PML4");
158298485Sngie		return (-1);
159298485Sngie	}
160291406Sjhb	if (kvm_read2(kd, pa, PML4, AMD64_PAGE_SIZE) != AMD64_PAGE_SIZE) {
161129452Speter		_kvm_err(kd, kd->program, "cannot read KPML4phys");
162298842Sngie		free(PML4);
1631602Srgrimes		return (-1);
1641602Srgrimes	}
165147672Speter	kd->vmst->PML4 = PML4;
1661602Srgrimes	return (0);
1671602Srgrimes}
1681602Srgrimes
1691602Srgrimesstatic int
170291406Sjhb_amd64_vatop(kvm_t *kd, kvaddr_t va, off_t *pa)
17118798Speter{
17218798Speter	struct vmstate *vm;
173291406Sjhb	amd64_physaddr_t offset;
174291406Sjhb	amd64_physaddr_t pdpe_pa;
175291406Sjhb	amd64_physaddr_t pde_pa;
176291406Sjhb	amd64_physaddr_t pte_pa;
177291406Sjhb	amd64_pml4e_t pml4e;
178291406Sjhb	amd64_pdpe_t pdpe;
179291406Sjhb	amd64_pde_t pde;
180291406Sjhb	amd64_pte_t pte;
181291406Sjhb	kvaddr_t pml4eindex;
182291406Sjhb	kvaddr_t pdpeindex;
183291406Sjhb	kvaddr_t pdeindex;
184291406Sjhb	kvaddr_t pteindex;
185291406Sjhb	amd64_physaddr_t a;
186147672Speter	off_t ofs;
187147672Speter	size_t s;
1881602Srgrimes
18918798Speter	vm = kd->vmst;
190291406Sjhb	offset = va & AMD64_PAGE_MASK;
19118798Speter
19218798Speter	/*
19318798Speter	 * If we are initializing (kernel page table descriptor pointer
19418798Speter	 * not yet set) then return pa == va to avoid infinite recursion.
19518798Speter	 */
196298485Sngie	if (vm->PML4 == NULL) {
197147672Speter		s = _kvm_pa2off(kd, va, pa);
198147672Speter		if (s == 0) {
199147672Speter			_kvm_err(kd, kd->program,
200291406Sjhb			    "_amd64_vatop: bootstrap data not in dump");
201147672Speter			goto invalid;
202147672Speter		} else
203291406Sjhb			return (AMD64_PAGE_SIZE - offset);
20418798Speter	}
20518798Speter
206291406Sjhb	pml4eindex = (va >> AMD64_PML4SHIFT) & (AMD64_NPML4EPG - 1);
207291406Sjhb	pml4e = le64toh(vm->PML4[pml4eindex]);
208291406Sjhb	if ((pml4e & AMD64_PG_V) == 0) {
209291406Sjhb		_kvm_err(kd, kd->program, "_amd64_vatop: pml4e not valid");
210129452Speter		goto invalid;
211147672Speter	}
212129452Speter
213291406Sjhb	pdpeindex = (va >> AMD64_PDPSHIFT) & (AMD64_NPDPEPG - 1);
214291406Sjhb	pdpe_pa = (pml4e & AMD64_PG_FRAME) + (pdpeindex * sizeof(amd64_pdpe_t));
215129452Speter
216147672Speter	s = _kvm_pa2off(kd, pdpe_pa, &ofs);
217291406Sjhb	if (s < sizeof(pdpe)) {
218291406Sjhb		_kvm_err(kd, kd->program, "_amd64_vatop: pdpe_pa not found");
219147672Speter		goto invalid;
220147672Speter	}
221291406Sjhb	if (pread(kd->pmfd, &pdpe, sizeof(pdpe), ofs) != sizeof(pdpe)) {
222291406Sjhb		_kvm_syserr(kd, kd->program, "_amd64_vatop: read pdpe");
223129452Speter		goto invalid;
224129452Speter	}
225291406Sjhb	pdpe = le64toh(pdpe);
226291406Sjhb	if ((pdpe & AMD64_PG_V) == 0) {
227291406Sjhb		_kvm_err(kd, kd->program, "_amd64_vatop: pdpe not valid");
228129452Speter		goto invalid;
229129452Speter	}
230291406Sjhb
231291406Sjhb	if (pdpe & AMD64_PG_PS) {
232291406Sjhb		/*
233291406Sjhb		 * No next-level page table; pdpe describes one 1GB page.
234291406Sjhb		 */
235297359Sjhb		a = (pdpe & AMD64_PG_1GB_FRAME) + (va & AMD64_PDPMASK);
236291406Sjhb		s = _kvm_pa2off(kd, a, pa);
237291406Sjhb		if (s == 0) {
238291406Sjhb			_kvm_err(kd, kd->program,
239291406Sjhb			    "_amd64_vatop: 1GB page address not in dump");
240291406Sjhb			goto invalid;
241291406Sjhb		} else
242291406Sjhb			return (AMD64_NBPDP - (va & AMD64_PDPMASK));
243147672Speter	}
244129452Speter
245291406Sjhb	pdeindex = (va >> AMD64_PDRSHIFT) & (AMD64_NPDEPG - 1);
246291406Sjhb	pde_pa = (pdpe & AMD64_PG_FRAME) + (pdeindex * sizeof(amd64_pde_t));
247129452Speter
248147672Speter	s = _kvm_pa2off(kd, pde_pa, &ofs);
249291406Sjhb	if (s < sizeof(pde)) {
250291406Sjhb		_kvm_syserr(kd, kd->program, "_amd64_vatop: pde_pa not found");
251129452Speter		goto invalid;
252129452Speter	}
253291406Sjhb	if (pread(kd->pmfd, &pde, sizeof(pde), ofs) != sizeof(pde)) {
254291406Sjhb		_kvm_syserr(kd, kd->program, "_amd64_vatop: read pde");
255147672Speter		goto invalid;
256147672Speter	}
257291406Sjhb	pde = le64toh(pde);
258291406Sjhb	if ((pde & AMD64_PG_V) == 0) {
259291406Sjhb		_kvm_err(kd, kd->program, "_amd64_vatop: pde not valid");
260129452Speter		goto invalid;
261129452Speter	}
26218798Speter
263291406Sjhb	if (pde & AMD64_PG_PS) {
264291406Sjhb		/*
265291406Sjhb		 * No final-level page table; pde describes one 2MB page.
266291406Sjhb		 */
267291406Sjhb		a = (pde & AMD64_PG_PS_FRAME) + (va & AMD64_PDRMASK);
268147672Speter		s = _kvm_pa2off(kd, a, pa);
269147672Speter		if (s == 0) {
270147672Speter			_kvm_err(kd, kd->program,
271291406Sjhb			    "_amd64_vatop: 2MB page address not in dump");
272147672Speter			goto invalid;
273147672Speter		} else
274291406Sjhb			return (AMD64_NBPDR - (va & AMD64_PDRMASK));
27528318Stegge	}
27628318Stegge
277291406Sjhb	pteindex = (va >> AMD64_PAGE_SHIFT) & (AMD64_NPTEPG - 1);
278291406Sjhb	pte_pa = (pde & AMD64_PG_FRAME) + (pteindex * sizeof(amd64_pte_t));
27918798Speter
280147672Speter	s = _kvm_pa2off(kd, pte_pa, &ofs);
281291406Sjhb	if (s < sizeof(pte)) {
282291406Sjhb		_kvm_err(kd, kd->program, "_amd64_vatop: pte_pa not found");
283147672Speter		goto invalid;
284147672Speter	}
285291406Sjhb	if (pread(kd->pmfd, &pte, sizeof(pte), ofs) != sizeof(pte)) {
286291406Sjhb		_kvm_syserr(kd, kd->program, "_amd64_vatop: read");
28718798Speter		goto invalid;
28818798Speter	}
289291406Sjhb	if ((pte & AMD64_PG_V) == 0) {
290291406Sjhb		_kvm_err(kd, kd->program, "_amd64_vatop: pte not valid");
29118798Speter		goto invalid;
29218798Speter	}
29318798Speter
294291406Sjhb	a = (pte & AMD64_PG_FRAME) + offset;
295147672Speter	s = _kvm_pa2off(kd, a, pa);
296147672Speter	if (s == 0) {
297291406Sjhb		_kvm_err(kd, kd->program, "_amd64_vatop: address not in dump");
298147672Speter		goto invalid;
299147672Speter	} else
300291406Sjhb		return (AMD64_PAGE_SIZE - offset);
30118798Speter
30218798Speterinvalid:
303291406Sjhb	_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
30418798Speter	return (0);
3051602Srgrimes}
3061602Srgrimes
307291406Sjhbstatic int
308291406Sjhb_amd64_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
30918798Speter{
310147672Speter
311147672Speter	if (ISALIVE(kd)) {
312147672Speter		_kvm_err(kd, 0, "kvm_kvatop called in live kernel!");
313147672Speter		return (0);
314147672Speter	}
315291406Sjhb	return (_amd64_vatop(kd, va, pa));
3161602Srgrimes}
317291406Sjhb
318291406Sjhbint
319316126Sngie_amd64_native(kvm_t *kd __unused)
320291406Sjhb{
321291406Sjhb
322291406Sjhb#ifdef __amd64__
323291406Sjhb	return (1);
324291406Sjhb#else
325291406Sjhb	return (0);
326291406Sjhb#endif
327291406Sjhb}
328291406Sjhb
329316126Sngiestatic struct kvm_arch kvm_amd64 = {
330291406Sjhb	.ka_probe = _amd64_probe,
331291406Sjhb	.ka_initvtop = _amd64_initvtop,
332291406Sjhb	.ka_freevtop = _amd64_freevtop,
333291406Sjhb	.ka_kvatop = _amd64_kvatop,
334291406Sjhb	.ka_native = _amd64_native,
335291406Sjhb};
336291406Sjhb
337291406SjhbKVM_ARCH(kvm_amd64);
338