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