xref: /freebsd/lib/libkvm/kvm_i386.c (revision 8a16b7a1)
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 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
38 
39 #if defined(LIBC_SCCS) && !defined(lint)
40 #if 0
41 static char sccsid[] = "@(#)kvm_hp300.c	8.1 (Berkeley) 6/4/93";
42 #endif
43 #endif /* LIBC_SCCS and not lint */
44 
45 /*
46  * i386 machine dependent routines for kvm.  Hopefully, the forthcoming
47  * vm code will one day obsolete this module.
48  */
49 
50 #include <sys/param.h>
51 #include <sys/endian.h>
52 #include <stdint.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56 #include <vm/vm.h>
57 #include <kvm.h>
58 
59 #ifdef __i386__
60 #include <machine/vmparam.h>		/* For KERNBASE. */
61 #endif
62 
63 #include <limits.h>
64 
65 #include "kvm_private.h"
66 #include "kvm_i386.h"
67 
68 struct vmstate {
69 	void		*PTD;
70 	int		pae;
71 	size_t		phnum;
72 	GElf_Phdr	*phdr;
73 };
74 
75 /*
76  * Translate a physical memory address to a file-offset in the crash-dump.
77  */
78 static size_t
79 _kvm_pa2off(kvm_t *kd, uint64_t pa, off_t *ofs)
80 {
81 	struct vmstate *vm = kd->vmst;
82 	GElf_Phdr *p;
83 	size_t n;
84 
85 	if (kd->rawdump) {
86 		*ofs = pa;
87 		return (I386_PAGE_SIZE - (pa & I386_PAGE_MASK));
88 	}
89 
90 	p = vm->phdr;
91 	n = vm->phnum;
92 	while (n && (pa < p->p_paddr || pa >= p->p_paddr + p->p_memsz))
93 		p++, n--;
94 	if (n == 0)
95 		return (0);
96 	*ofs = (pa - p->p_paddr) + p->p_offset;
97 	return (I386_PAGE_SIZE - (pa & I386_PAGE_MASK));
98 }
99 
100 static void
101 _i386_freevtop(kvm_t *kd)
102 {
103 	struct vmstate *vm = kd->vmst;
104 
105 	if (vm->PTD)
106 		free(vm->PTD);
107 	free(vm->phdr);
108 	free(vm);
109 	kd->vmst = NULL;
110 }
111 
112 static int
113 _i386_probe(kvm_t *kd)
114 {
115 
116 	return (_kvm_probe_elf_kernel(kd, ELFCLASS32, EM_386) &&
117 	    !_kvm_is_minidump(kd));
118 }
119 
120 static int
121 _i386_initvtop(kvm_t *kd)
122 {
123 	struct kvm_nlist nl[2];
124 	i386_physaddr_t pa;
125 	kvaddr_t kernbase;
126 	char		*PTD;
127 	int		i;
128 
129 	kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(struct vmstate));
130 	if (kd->vmst == NULL) {
131 		_kvm_err(kd, kd->program, "cannot allocate vm");
132 		return (-1);
133 	}
134 	kd->vmst->PTD = 0;
135 
136 	if (kd->rawdump == 0) {
137 		if (_kvm_read_core_phdrs(kd, &kd->vmst->phnum,
138 		    &kd->vmst->phdr) == -1)
139 			return (-1);
140 	}
141 
142 	nl[0].n_name = "kernbase";
143 	nl[1].n_name = 0;
144 
145 	if (kvm_nlist2(kd, nl) != 0) {
146 #ifdef __i386__
147 		kernbase = KERNBASE;	/* for old kernels */
148 #else
149 		_kvm_err(kd, kd->program, "cannot resolve kernbase");
150 		return (-1);
151 #endif
152 	} else
153 		kernbase = nl[0].n_value;
154 
155 	nl[0].n_name = "IdlePDPT";
156 	nl[1].n_name = 0;
157 
158 	if (kvm_nlist2(kd, nl) == 0) {
159 		i386_physaddr_pae_t pa64;
160 
161 		if (kvm_read2(kd, (nl[0].n_value - kernbase), &pa,
162 		    sizeof(pa)) != sizeof(pa)) {
163 			_kvm_err(kd, kd->program, "cannot read IdlePDPT");
164 			return (-1);
165 		}
166 		pa = le32toh(pa);
167 		PTD = _kvm_malloc(kd, 4 * I386_PAGE_SIZE);
168 		if (PTD == NULL) {
169 			_kvm_err(kd, kd->program, "cannot allocate PTD");
170 			return (-1);
171 		}
172 		for (i = 0; i < 4; i++) {
173 			if (kvm_read2(kd, pa + (i * sizeof(pa64)), &pa64,
174 			    sizeof(pa64)) != sizeof(pa64)) {
175 				_kvm_err(kd, kd->program, "Cannot read PDPT");
176 				free(PTD);
177 				return (-1);
178 			}
179 			pa64 = le64toh(pa64);
180 			if (kvm_read2(kd, pa64 & I386_PG_FRAME_PAE,
181 			    PTD + (i * I386_PAGE_SIZE), I386_PAGE_SIZE) !=
182 			    I386_PAGE_SIZE) {
183 				_kvm_err(kd, kd->program, "cannot read PDPT");
184 				free(PTD);
185 				return (-1);
186 			}
187 		}
188 		kd->vmst->PTD = PTD;
189 		kd->vmst->pae = 1;
190 	} else {
191 		nl[0].n_name = "IdlePTD";
192 		nl[1].n_name = 0;
193 
194 		if (kvm_nlist2(kd, nl) != 0) {
195 			_kvm_err(kd, kd->program, "bad namelist");
196 			return (-1);
197 		}
198 		if (kvm_read2(kd, (nl[0].n_value - kernbase), &pa,
199 		    sizeof(pa)) != sizeof(pa)) {
200 			_kvm_err(kd, kd->program, "cannot read IdlePTD");
201 			return (-1);
202 		}
203 		pa = le32toh(pa);
204 		PTD = _kvm_malloc(kd, I386_PAGE_SIZE);
205 		if (PTD == NULL) {
206 			_kvm_err(kd, kd->program, "cannot allocate PTD");
207 			return (-1);
208 		}
209 		if (kvm_read2(kd, pa, PTD, I386_PAGE_SIZE) != I386_PAGE_SIZE) {
210 			_kvm_err(kd, kd->program, "cannot read PTD");
211 			return (-1);
212 		}
213 		kd->vmst->PTD = PTD;
214 		kd->vmst->pae = 0;
215 	}
216 	return (0);
217 }
218 
219 static int
220 _i386_vatop(kvm_t *kd, kvaddr_t va, off_t *pa)
221 {
222 	struct vmstate *vm;
223 	i386_physaddr_t offset;
224 	i386_physaddr_t pte_pa;
225 	i386_pde_t pde;
226 	i386_pte_t pte;
227 	kvaddr_t pdeindex;
228 	kvaddr_t pteindex;
229 	size_t s;
230 	i386_physaddr_t a;
231 	off_t ofs;
232 	i386_pde_t *PTD;
233 
234 	vm = kd->vmst;
235 	PTD = (i386_pde_t *)vm->PTD;
236 	offset = va & I386_PAGE_MASK;
237 
238 	/*
239 	 * If we are initializing (kernel page table descriptor pointer
240 	 * not yet set) then return pa == va to avoid infinite recursion.
241 	 */
242 	if (PTD == NULL) {
243 		s = _kvm_pa2off(kd, va, pa);
244 		if (s == 0) {
245 			_kvm_err(kd, kd->program,
246 			    "_i386_vatop: bootstrap data not in dump");
247 			goto invalid;
248 		} else
249 			return (I386_PAGE_SIZE - offset);
250 	}
251 
252 	pdeindex = va >> I386_PDRSHIFT;
253 	pde = le32toh(PTD[pdeindex]);
254 	if ((pde & I386_PG_V) == 0) {
255 		_kvm_err(kd, kd->program, "_i386_vatop: pde not valid");
256 		goto invalid;
257 	}
258 
259 	if (pde & I386_PG_PS) {
260 		/*
261 		 * No second-level page table; ptd describes one 4MB
262 		 * page.  (We assume that the kernel wouldn't set
263 		 * PG_PS without enabling it cr0).
264 		 */
265 		offset = va & I386_PAGE_PS_MASK;
266 		a = (pde & I386_PG_PS_FRAME) + offset;
267 		s = _kvm_pa2off(kd, a, pa);
268 		if (s == 0) {
269 			_kvm_err(kd, kd->program,
270 			    "_i386_vatop: 4MB page address not in dump");
271 			goto invalid;
272 		}
273 		return (I386_NBPDR - offset);
274 	}
275 
276 	pteindex = (va >> I386_PAGE_SHIFT) & (I386_NPTEPG - 1);
277 	pte_pa = (pde & I386_PG_FRAME) + (pteindex * sizeof(pte));
278 
279 	s = _kvm_pa2off(kd, pte_pa, &ofs);
280 	if (s < sizeof(pte)) {
281 		_kvm_err(kd, kd->program, "_i386_vatop: pte_pa not found");
282 		goto invalid;
283 	}
284 
285 	/* XXX This has to be a physical address read, kvm_read is virtual */
286 	if (pread(kd->pmfd, &pte, sizeof(pte), ofs) != sizeof(pte)) {
287 		_kvm_syserr(kd, kd->program, "_i386_vatop: pread");
288 		goto invalid;
289 	}
290 	pte = le32toh(pte);
291 	if ((pte & I386_PG_V) == 0) {
292 		_kvm_err(kd, kd->program, "_kvm_kvatop: pte not valid");
293 		goto invalid;
294 	}
295 
296 	a = (pte & I386_PG_FRAME) + offset;
297 	s = _kvm_pa2off(kd, a, pa);
298 	if (s == 0) {
299 		_kvm_err(kd, kd->program, "_i386_vatop: address not in dump");
300 		goto invalid;
301 	} else
302 		return (I386_PAGE_SIZE - offset);
303 
304 invalid:
305 	_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
306 	return (0);
307 }
308 
309 static int
310 _i386_vatop_pae(kvm_t *kd, kvaddr_t va, off_t *pa)
311 {
312 	struct vmstate *vm;
313 	i386_physaddr_pae_t offset;
314 	i386_physaddr_pae_t pte_pa;
315 	i386_pde_pae_t pde;
316 	i386_pte_pae_t pte;
317 	kvaddr_t pdeindex;
318 	kvaddr_t pteindex;
319 	size_t s;
320 	i386_physaddr_pae_t a;
321 	off_t ofs;
322 	i386_pde_pae_t *PTD;
323 
324 	vm = kd->vmst;
325 	PTD = (i386_pde_pae_t *)vm->PTD;
326 	offset = va & I386_PAGE_MASK;
327 
328 	/*
329 	 * If we are initializing (kernel page table descriptor pointer
330 	 * not yet set) then return pa == va to avoid infinite recursion.
331 	 */
332 	if (PTD == NULL) {
333 		s = _kvm_pa2off(kd, va, pa);
334 		if (s == 0) {
335 			_kvm_err(kd, kd->program,
336 			    "_i386_vatop_pae: bootstrap data not in dump");
337 			goto invalid;
338 		} else
339 			return (I386_PAGE_SIZE - offset);
340 	}
341 
342 	pdeindex = va >> I386_PDRSHIFT_PAE;
343 	pde = le64toh(PTD[pdeindex]);
344 	if ((pde & I386_PG_V) == 0) {
345 		_kvm_err(kd, kd->program, "_kvm_kvatop_pae: pde not valid");
346 		goto invalid;
347 	}
348 
349 	if (pde & I386_PG_PS) {
350 		/*
351 		 * No second-level page table; ptd describes one 2MB
352 		 * page.  (We assume that the kernel wouldn't set
353 		 * PG_PS without enabling it cr0).
354 		 */
355 		offset = va & I386_PAGE_PS_MASK_PAE;
356 		a = (pde & I386_PG_PS_FRAME_PAE) + offset;
357 		s = _kvm_pa2off(kd, a, pa);
358 		if (s == 0) {
359 			_kvm_err(kd, kd->program,
360 			    "_i386_vatop: 2MB page address not in dump");
361 			goto invalid;
362 		}
363 		return (I386_NBPDR_PAE - offset);
364 	}
365 
366 	pteindex = (va >> I386_PAGE_SHIFT) & (I386_NPTEPG_PAE - 1);
367 	pte_pa = (pde & I386_PG_FRAME_PAE) + (pteindex * sizeof(pde));
368 
369 	s = _kvm_pa2off(kd, pte_pa, &ofs);
370 	if (s < sizeof(pte)) {
371 		_kvm_err(kd, kd->program, "_i386_vatop_pae: pdpe_pa not found");
372 		goto invalid;
373 	}
374 
375 	/* XXX This has to be a physical address read, kvm_read is virtual */
376 	if (pread(kd->pmfd, &pte, sizeof(pte), ofs) != sizeof(pte)) {
377 		_kvm_syserr(kd, kd->program, "_i386_vatop_pae: read");
378 		goto invalid;
379 	}
380 	pte = le64toh(pte);
381 	if ((pte & I386_PG_V) == 0) {
382 		_kvm_err(kd, kd->program, "_i386_vatop_pae: pte not valid");
383 		goto invalid;
384 	}
385 
386 	a = (pte & I386_PG_FRAME_PAE) + offset;
387 	s = _kvm_pa2off(kd, a, pa);
388 	if (s == 0) {
389 		_kvm_err(kd, kd->program,
390 		    "_i386_vatop_pae: address not in dump");
391 		goto invalid;
392 	} else
393 		return (I386_PAGE_SIZE - offset);
394 
395 invalid:
396 	_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
397 	return (0);
398 }
399 
400 static int
401 _i386_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
402 {
403 
404 	if (ISALIVE(kd)) {
405 		_kvm_err(kd, 0, "vatop called in live kernel!");
406 		return (0);
407 	}
408 	if (kd->vmst->pae)
409 		return (_i386_vatop_pae(kd, va, pa));
410 	else
411 		return (_i386_vatop(kd, va, pa));
412 }
413 
414 int
415 _i386_native(kvm_t *kd __unused)
416 {
417 
418 #ifdef __i386__
419 	return (1);
420 #else
421 	return (0);
422 #endif
423 }
424 
425 static struct kvm_arch kvm_i386 = {
426 	.ka_probe = _i386_probe,
427 	.ka_initvtop = _i386_initvtop,
428 	.ka_freevtop = _i386_freevtop,
429 	.ka_kvatop = _i386_kvatop,
430 	.ka_native = _i386_native,
431 };
432 
433 KVM_ARCH(kvm_i386);
434