1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1989, 1992, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * This code is derived from software developed by the Computer Systems
8 * Engineering group at Lawrence Berkeley Laboratory under DARPA contract
9 * BG 91-66 and contributed to Berkeley.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36
37/*
38 * AMD64 machine dependent routines for kvm.  Hopefully, the forthcoming
39 * vm code will one day obsolete this module.
40 */
41
42#include <sys/param.h>
43#include <sys/endian.h>
44#include <stdint.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48#include <vm/vm.h>
49#include <kvm.h>
50
51#include <limits.h>
52
53#include "kvm_private.h"
54#include "kvm_amd64.h"
55
56struct vmstate {
57	size_t		phnum;
58	GElf_Phdr	*phdr;
59	amd64_pml4e_t	*PML4;
60};
61
62/*
63 * Translate a physical memory address to a file-offset in the crash-dump.
64 */
65static size_t
66_kvm_pa2off(kvm_t *kd, uint64_t pa, off_t *ofs)
67{
68	struct vmstate *vm = kd->vmst;
69	GElf_Phdr *p;
70	size_t n;
71
72	if (kd->rawdump) {
73		*ofs = pa;
74		return (AMD64_PAGE_SIZE - (pa & AMD64_PAGE_MASK));
75	}
76
77	p = vm->phdr;
78	n = vm->phnum;
79	while (n && (pa < p->p_paddr || pa >= p->p_paddr + p->p_memsz))
80		p++, n--;
81	if (n == 0)
82		return (0);
83	*ofs = (pa - p->p_paddr) + p->p_offset;
84	return (AMD64_PAGE_SIZE - (pa & AMD64_PAGE_MASK));
85}
86
87static void
88_amd64_freevtop(kvm_t *kd)
89{
90	struct vmstate *vm = kd->vmst;
91
92	if (vm->PML4)
93		free(vm->PML4);
94	free(vm->phdr);
95	free(vm);
96	kd->vmst = NULL;
97}
98
99static int
100_amd64_probe(kvm_t *kd)
101{
102
103	return (_kvm_probe_elf_kernel(kd, ELFCLASS64, EM_X86_64) &&
104	    !_kvm_is_minidump(kd));
105}
106
107static int
108_amd64_initvtop(kvm_t *kd)
109{
110	struct kvm_nlist nl[2];
111	amd64_physaddr_t pa;
112	kvaddr_t kernbase;
113	amd64_pml4e_t *PML4;
114
115	kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(*kd->vmst));
116	if (kd->vmst == NULL) {
117		_kvm_err(kd, kd->program, "cannot allocate vm");
118		return (-1);
119	}
120	kd->vmst->PML4 = 0;
121
122	if (kd->rawdump == 0) {
123		if (_kvm_read_core_phdrs(kd, &kd->vmst->phnum,
124		    &kd->vmst->phdr) == -1)
125			return (-1);
126	}
127
128	nl[0].n_name = "kernbase";
129	nl[1].n_name = 0;
130
131	if (kvm_nlist2(kd, nl) != 0) {
132		_kvm_err(kd, kd->program, "bad namelist - no kernbase");
133		return (-1);
134	}
135	kernbase = nl[0].n_value;
136
137	nl[0].n_name = "KPML4phys";
138	nl[1].n_name = 0;
139
140	if (kvm_nlist2(kd, nl) != 0) {
141		_kvm_err(kd, kd->program, "bad namelist - no KPML4phys");
142		return (-1);
143	}
144	if (kvm_read2(kd, (nl[0].n_value - kernbase), &pa, sizeof(pa)) !=
145	    sizeof(pa)) {
146		_kvm_err(kd, kd->program, "cannot read KPML4phys");
147		return (-1);
148	}
149	pa = le64toh(pa);
150	PML4 = _kvm_malloc(kd, AMD64_PAGE_SIZE);
151	if (PML4 == NULL) {
152		_kvm_err(kd, kd->program, "cannot allocate PML4");
153		return (-1);
154	}
155	if (kvm_read2(kd, pa, PML4, AMD64_PAGE_SIZE) != AMD64_PAGE_SIZE) {
156		_kvm_err(kd, kd->program, "cannot read KPML4phys");
157		free(PML4);
158		return (-1);
159	}
160	kd->vmst->PML4 = PML4;
161	return (0);
162}
163
164static int
165_amd64_vatop(kvm_t *kd, kvaddr_t va, off_t *pa)
166{
167	struct vmstate *vm;
168	amd64_physaddr_t offset;
169	amd64_physaddr_t pdpe_pa;
170	amd64_physaddr_t pde_pa;
171	amd64_physaddr_t pte_pa;
172	amd64_pml4e_t pml4e;
173	amd64_pdpe_t pdpe;
174	amd64_pde_t pde;
175	amd64_pte_t pte;
176	kvaddr_t pml4eindex;
177	kvaddr_t pdpeindex;
178	kvaddr_t pdeindex;
179	kvaddr_t pteindex;
180	amd64_physaddr_t a;
181	off_t ofs;
182	size_t s;
183
184	vm = kd->vmst;
185	offset = va & AMD64_PAGE_MASK;
186
187	/*
188	 * If we are initializing (kernel page table descriptor pointer
189	 * not yet set) then return pa == va to avoid infinite recursion.
190	 */
191	if (vm->PML4 == NULL) {
192		s = _kvm_pa2off(kd, va, pa);
193		if (s == 0) {
194			_kvm_err(kd, kd->program,
195			    "_amd64_vatop: bootstrap data not in dump");
196			goto invalid;
197		} else
198			return (AMD64_PAGE_SIZE - offset);
199	}
200
201	pml4eindex = (va >> AMD64_PML4SHIFT) & (AMD64_NPML4EPG - 1);
202	pml4e = le64toh(vm->PML4[pml4eindex]);
203	if ((pml4e & AMD64_PG_V) == 0) {
204		_kvm_err(kd, kd->program, "_amd64_vatop: pml4e not valid");
205		goto invalid;
206	}
207
208	pdpeindex = (va >> AMD64_PDPSHIFT) & (AMD64_NPDPEPG - 1);
209	pdpe_pa = (pml4e & AMD64_PG_FRAME) + (pdpeindex * sizeof(amd64_pdpe_t));
210
211	s = _kvm_pa2off(kd, pdpe_pa, &ofs);
212	if (s < sizeof(pdpe)) {
213		_kvm_err(kd, kd->program, "_amd64_vatop: pdpe_pa not found");
214		goto invalid;
215	}
216	if (pread(kd->pmfd, &pdpe, sizeof(pdpe), ofs) != sizeof(pdpe)) {
217		_kvm_syserr(kd, kd->program, "_amd64_vatop: read pdpe");
218		goto invalid;
219	}
220	pdpe = le64toh(pdpe);
221	if ((pdpe & AMD64_PG_V) == 0) {
222		_kvm_err(kd, kd->program, "_amd64_vatop: pdpe not valid");
223		goto invalid;
224	}
225
226	if (pdpe & AMD64_PG_PS) {
227		/*
228		 * No next-level page table; pdpe describes one 1GB page.
229		 */
230		a = (pdpe & AMD64_PG_1GB_FRAME) + (va & AMD64_PDPMASK);
231		s = _kvm_pa2off(kd, a, pa);
232		if (s == 0) {
233			_kvm_err(kd, kd->program,
234			    "_amd64_vatop: 1GB page address not in dump");
235			goto invalid;
236		} else
237			return (AMD64_NBPDP - (va & AMD64_PDPMASK));
238	}
239
240	pdeindex = (va >> AMD64_PDRSHIFT) & (AMD64_NPDEPG - 1);
241	pde_pa = (pdpe & AMD64_PG_FRAME) + (pdeindex * sizeof(amd64_pde_t));
242
243	s = _kvm_pa2off(kd, pde_pa, &ofs);
244	if (s < sizeof(pde)) {
245		_kvm_syserr(kd, kd->program, "_amd64_vatop: pde_pa not found");
246		goto invalid;
247	}
248	if (pread(kd->pmfd, &pde, sizeof(pde), ofs) != sizeof(pde)) {
249		_kvm_syserr(kd, kd->program, "_amd64_vatop: read pde");
250		goto invalid;
251	}
252	pde = le64toh(pde);
253	if ((pde & AMD64_PG_V) == 0) {
254		_kvm_err(kd, kd->program, "_amd64_vatop: pde not valid");
255		goto invalid;
256	}
257
258	if (pde & AMD64_PG_PS) {
259		/*
260		 * No final-level page table; pde describes one 2MB page.
261		 */
262		a = (pde & AMD64_PG_PS_FRAME) + (va & AMD64_PDRMASK);
263		s = _kvm_pa2off(kd, a, pa);
264		if (s == 0) {
265			_kvm_err(kd, kd->program,
266			    "_amd64_vatop: 2MB page address not in dump");
267			goto invalid;
268		} else
269			return (AMD64_NBPDR - (va & AMD64_PDRMASK));
270	}
271
272	pteindex = (va >> AMD64_PAGE_SHIFT) & (AMD64_NPTEPG - 1);
273	pte_pa = (pde & AMD64_PG_FRAME) + (pteindex * sizeof(amd64_pte_t));
274
275	s = _kvm_pa2off(kd, pte_pa, &ofs);
276	if (s < sizeof(pte)) {
277		_kvm_err(kd, kd->program, "_amd64_vatop: pte_pa not found");
278		goto invalid;
279	}
280	if (pread(kd->pmfd, &pte, sizeof(pte), ofs) != sizeof(pte)) {
281		_kvm_syserr(kd, kd->program, "_amd64_vatop: read");
282		goto invalid;
283	}
284	if ((pte & AMD64_PG_V) == 0) {
285		_kvm_err(kd, kd->program, "_amd64_vatop: pte not valid");
286		goto invalid;
287	}
288
289	a = (pte & AMD64_PG_FRAME) + offset;
290	s = _kvm_pa2off(kd, a, pa);
291	if (s == 0) {
292		_kvm_err(kd, kd->program, "_amd64_vatop: address not in dump");
293		goto invalid;
294	} else
295		return (AMD64_PAGE_SIZE - offset);
296
297invalid:
298	_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
299	return (0);
300}
301
302static int
303_amd64_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
304{
305
306	if (ISALIVE(kd)) {
307		_kvm_err(kd, 0, "kvm_kvatop called in live kernel!");
308		return (0);
309	}
310	return (_amd64_vatop(kd, va, pa));
311}
312
313int
314_amd64_native(kvm_t *kd __unused)
315{
316
317#ifdef __amd64__
318	return (1);
319#else
320	return (0);
321#endif
322}
323
324static struct kvm_arch kvm_amd64 = {
325	.ka_probe = _amd64_probe,
326	.ka_initvtop = _amd64_initvtop,
327	.ka_freevtop = _amd64_freevtop,
328	.ka_kvatop = _amd64_kvatop,
329	.ka_native = _amd64_native,
330};
331
332KVM_ARCH(kvm_amd64);
333