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