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