]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libkvm/kvm_i386.c
MFV r326007: less v529.
[FreeBSD/FreeBSD.git] / lib / libkvm / kvm_i386.c
1 /*-
2  * Copyright (c) 1989, 1992, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software developed by the Computer Systems
6  * Engineering group at Lawrence Berkeley Laboratory under DARPA contract
7  * BG 91-66 and contributed to Berkeley.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36
37 #if defined(LIBC_SCCS) && !defined(lint)
38 #if 0
39 static char sccsid[] = "@(#)kvm_hp300.c 8.1 (Berkeley) 6/4/93";
40 #endif
41 #endif /* LIBC_SCCS and not lint */
42
43 /*
44  * i386 machine dependent routines for kvm.  Hopefully, the forthcoming
45  * vm code will one day obsolete this module.
46  */
47
48 #include <sys/param.h>
49 #include <sys/endian.h>
50 #include <stdint.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include <vm/vm.h>
55 #include <kvm.h>
56
57 #ifdef __i386__
58 #include <machine/vmparam.h>            /* For KERNBASE. */
59 #endif
60
61 #include <limits.h>
62
63 #include "kvm_private.h"
64 #include "kvm_i386.h"
65
66 struct vmstate {
67         void            *PTD;
68         int             pae;
69         size_t          phnum;
70         GElf_Phdr       *phdr;
71 };
72
73 /*
74  * Translate a physical memory address to a file-offset in the crash-dump.
75  */
76 static size_t
77 _kvm_pa2off(kvm_t *kd, uint64_t pa, off_t *ofs)
78 {
79         struct vmstate *vm = kd->vmst;
80         GElf_Phdr *p;
81         size_t n;
82
83         if (kd->rawdump) {
84                 *ofs = pa;
85                 return (I386_PAGE_SIZE - (pa & I386_PAGE_MASK));
86         }
87
88         p = vm->phdr;
89         n = vm->phnum;
90         while (n && (pa < p->p_paddr || pa >= p->p_paddr + p->p_memsz))
91                 p++, n--;
92         if (n == 0)
93                 return (0);
94         *ofs = (pa - p->p_paddr) + p->p_offset;
95         return (I386_PAGE_SIZE - (pa & I386_PAGE_MASK));
96 }
97
98 static void
99 _i386_freevtop(kvm_t *kd)
100 {
101         struct vmstate *vm = kd->vmst;
102
103         if (vm->PTD)
104                 free(vm->PTD);
105         free(vm->phdr);
106         free(vm);
107         kd->vmst = NULL;
108 }
109
110 static int
111 _i386_probe(kvm_t *kd)
112 {
113
114         return (_kvm_probe_elf_kernel(kd, ELFCLASS32, EM_386) &&
115             !_kvm_is_minidump(kd));
116 }
117
118 static int
119 _i386_initvtop(kvm_t *kd)
120 {
121         struct kvm_nlist nl[2];
122         i386_physaddr_t pa;
123         kvaddr_t kernbase;
124         char            *PTD;
125         int             i;
126
127         kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(struct vmstate));
128         if (kd->vmst == NULL) {
129                 _kvm_err(kd, kd->program, "cannot allocate vm");
130                 return (-1);
131         }
132         kd->vmst->PTD = 0;
133
134         if (kd->rawdump == 0) {
135                 if (_kvm_read_core_phdrs(kd, &kd->vmst->phnum,
136                     &kd->vmst->phdr) == -1)
137                         return (-1);
138         }
139
140         nl[0].n_name = "kernbase";
141         nl[1].n_name = 0;
142
143         if (kvm_nlist2(kd, nl) != 0) {
144 #ifdef __i386__
145                 kernbase = KERNBASE;    /* for old kernels */
146 #else
147                 _kvm_err(kd, kd->program, "cannot resolve kernbase");
148                 return (-1);
149 #endif
150         } else
151                 kernbase = nl[0].n_value;
152
153         nl[0].n_name = "IdlePDPT";
154         nl[1].n_name = 0;
155
156         if (kvm_nlist2(kd, nl) == 0) {
157                 i386_physaddr_pae_t pa64;
158
159                 if (kvm_read2(kd, (nl[0].n_value - kernbase), &pa,
160                     sizeof(pa)) != sizeof(pa)) {
161                         _kvm_err(kd, kd->program, "cannot read IdlePDPT");
162                         return (-1);
163                 }
164                 pa = le32toh(pa);
165                 PTD = _kvm_malloc(kd, 4 * I386_PAGE_SIZE);
166                 if (PTD == NULL) {
167                         _kvm_err(kd, kd->program, "cannot allocate PTD");
168                         return (-1);
169                 }
170                 for (i = 0; i < 4; i++) {
171                         if (kvm_read2(kd, pa + (i * sizeof(pa64)), &pa64,
172                             sizeof(pa64)) != sizeof(pa64)) {
173                                 _kvm_err(kd, kd->program, "Cannot read PDPT");
174                                 free(PTD);
175                                 return (-1);
176                         }
177                         pa64 = le64toh(pa64);
178                         if (kvm_read2(kd, pa64 & I386_PG_FRAME_PAE,
179                             PTD + (i * I386_PAGE_SIZE), I386_PAGE_SIZE) !=
180                             I386_PAGE_SIZE) {
181                                 _kvm_err(kd, kd->program, "cannot read PDPT");
182                                 free(PTD);
183                                 return (-1);
184                         }
185                 }
186                 kd->vmst->PTD = PTD;
187                 kd->vmst->pae = 1;
188         } else {
189                 nl[0].n_name = "IdlePTD";
190                 nl[1].n_name = 0;
191
192                 if (kvm_nlist2(kd, nl) != 0) {
193                         _kvm_err(kd, kd->program, "bad namelist");
194                         return (-1);
195                 }
196                 if (kvm_read2(kd, (nl[0].n_value - kernbase), &pa,
197                     sizeof(pa)) != sizeof(pa)) {
198                         _kvm_err(kd, kd->program, "cannot read IdlePTD");
199                         return (-1);
200                 }
201                 pa = le32toh(pa);
202                 PTD = _kvm_malloc(kd, I386_PAGE_SIZE);
203                 if (PTD == NULL) {
204                         _kvm_err(kd, kd->program, "cannot allocate PTD");
205                         return (-1);
206                 }
207                 if (kvm_read2(kd, pa, PTD, I386_PAGE_SIZE) != I386_PAGE_SIZE) {
208                         _kvm_err(kd, kd->program, "cannot read PTD");
209                         return (-1);
210                 }
211                 kd->vmst->PTD = PTD;
212                 kd->vmst->pae = 0;
213         }
214         return (0);
215 }
216
217 static int
218 _i386_vatop(kvm_t *kd, kvaddr_t va, off_t *pa)
219 {
220         struct vmstate *vm;
221         i386_physaddr_t offset;
222         i386_physaddr_t pte_pa;
223         i386_pde_t pde;
224         i386_pte_t pte;
225         kvaddr_t pdeindex;
226         kvaddr_t pteindex;
227         size_t s;
228         i386_physaddr_t a;
229         off_t ofs;
230         i386_pde_t *PTD;
231
232         vm = kd->vmst;
233         PTD = (i386_pde_t *)vm->PTD;
234         offset = va & I386_PAGE_MASK;
235
236         /*
237          * If we are initializing (kernel page table descriptor pointer
238          * not yet set) then return pa == va to avoid infinite recursion.
239          */
240         if (PTD == NULL) {
241                 s = _kvm_pa2off(kd, va, pa);
242                 if (s == 0) {
243                         _kvm_err(kd, kd->program,
244                             "_i386_vatop: bootstrap data not in dump");
245                         goto invalid;
246                 } else
247                         return (I386_PAGE_SIZE - offset);
248         }
249
250         pdeindex = va >> I386_PDRSHIFT;
251         pde = le32toh(PTD[pdeindex]);
252         if ((pde & I386_PG_V) == 0) {
253                 _kvm_err(kd, kd->program, "_i386_vatop: pde not valid");
254                 goto invalid;
255         }
256
257         if (pde & I386_PG_PS) {
258                 /*
259                  * No second-level page table; ptd describes one 4MB
260                  * page.  (We assume that the kernel wouldn't set
261                  * PG_PS without enabling it cr0).
262                  */
263                 offset = va & I386_PAGE_PS_MASK;
264                 a = (pde & I386_PG_PS_FRAME) + offset;
265                 s = _kvm_pa2off(kd, a, pa);
266                 if (s == 0) {
267                         _kvm_err(kd, kd->program,
268                             "_i386_vatop: 4MB page address not in dump");
269                         goto invalid;
270                 }
271                 return (I386_NBPDR - offset);
272         }
273
274         pteindex = (va >> I386_PAGE_SHIFT) & (I386_NPTEPG - 1);
275         pte_pa = (pde & I386_PG_FRAME) + (pteindex * sizeof(pte));
276
277         s = _kvm_pa2off(kd, pte_pa, &ofs);
278         if (s < sizeof(pte)) {
279                 _kvm_err(kd, kd->program, "_i386_vatop: pte_pa not found");
280                 goto invalid;
281         }
282
283         /* XXX This has to be a physical address read, kvm_read is virtual */
284         if (pread(kd->pmfd, &pte, sizeof(pte), ofs) != sizeof(pte)) {
285                 _kvm_syserr(kd, kd->program, "_i386_vatop: pread");
286                 goto invalid;
287         }
288         pte = le32toh(pte);
289         if ((pte & I386_PG_V) == 0) {
290                 _kvm_err(kd, kd->program, "_kvm_kvatop: pte not valid");
291                 goto invalid;
292         }
293
294         a = (pte & I386_PG_FRAME) + offset;
295         s = _kvm_pa2off(kd, a, pa);
296         if (s == 0) {
297                 _kvm_err(kd, kd->program, "_i386_vatop: address not in dump");
298                 goto invalid;
299         } else
300                 return (I386_PAGE_SIZE - offset);
301
302 invalid:
303         _kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
304         return (0);
305 }
306
307 static int
308 _i386_vatop_pae(kvm_t *kd, kvaddr_t va, off_t *pa)
309 {
310         struct vmstate *vm;
311         i386_physaddr_pae_t offset;
312         i386_physaddr_pae_t pte_pa;
313         i386_pde_pae_t pde;
314         i386_pte_pae_t pte;
315         kvaddr_t pdeindex;
316         kvaddr_t pteindex;
317         size_t s;
318         i386_physaddr_pae_t a;
319         off_t ofs;
320         i386_pde_pae_t *PTD;
321
322         vm = kd->vmst;
323         PTD = (i386_pde_pae_t *)vm->PTD;
324         offset = va & I386_PAGE_MASK;
325
326         /*
327          * If we are initializing (kernel page table descriptor pointer
328          * not yet set) then return pa == va to avoid infinite recursion.
329          */
330         if (PTD == NULL) {
331                 s = _kvm_pa2off(kd, va, pa);
332                 if (s == 0) {
333                         _kvm_err(kd, kd->program,
334                             "_i386_vatop_pae: bootstrap data not in dump");
335                         goto invalid;
336                 } else
337                         return (I386_PAGE_SIZE - offset);
338         }
339
340         pdeindex = va >> I386_PDRSHIFT_PAE;
341         pde = le64toh(PTD[pdeindex]);
342         if ((pde & I386_PG_V) == 0) {
343                 _kvm_err(kd, kd->program, "_kvm_kvatop_pae: pde not valid");
344                 goto invalid;
345         }
346
347         if (pde & I386_PG_PS) {
348                 /*
349                  * No second-level page table; ptd describes one 2MB
350                  * page.  (We assume that the kernel wouldn't set
351                  * PG_PS without enabling it cr0).
352                  */
353                 offset = va & I386_PAGE_PS_MASK_PAE;
354                 a = (pde & I386_PG_PS_FRAME_PAE) + offset;
355                 s = _kvm_pa2off(kd, a, pa);
356                 if (s == 0) {
357                         _kvm_err(kd, kd->program,
358                             "_i386_vatop: 2MB page address not in dump");
359                         goto invalid;
360                 }
361                 return (I386_NBPDR_PAE - offset);
362         }
363
364         pteindex = (va >> I386_PAGE_SHIFT) & (I386_NPTEPG_PAE - 1);
365         pte_pa = (pde & I386_PG_FRAME_PAE) + (pteindex * sizeof(pde));
366
367         s = _kvm_pa2off(kd, pte_pa, &ofs);
368         if (s < sizeof(pte)) {
369                 _kvm_err(kd, kd->program, "_i386_vatop_pae: pdpe_pa not found");
370                 goto invalid;
371         }
372
373         /* XXX This has to be a physical address read, kvm_read is virtual */
374         if (pread(kd->pmfd, &pte, sizeof(pte), ofs) != sizeof(pte)) {
375                 _kvm_syserr(kd, kd->program, "_i386_vatop_pae: read");
376                 goto invalid;
377         }
378         pte = le64toh(pte);
379         if ((pte & I386_PG_V) == 0) {
380                 _kvm_err(kd, kd->program, "_i386_vatop_pae: pte not valid");
381                 goto invalid;
382         }
383
384         a = (pte & I386_PG_FRAME_PAE) + offset;
385         s = _kvm_pa2off(kd, a, pa);
386         if (s == 0) {
387                 _kvm_err(kd, kd->program,
388                     "_i386_vatop_pae: address not in dump");
389                 goto invalid;
390         } else
391                 return (I386_PAGE_SIZE - offset);
392
393 invalid:
394         _kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
395         return (0);
396 }
397
398 static int
399 _i386_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
400 {
401
402         if (ISALIVE(kd)) {
403                 _kvm_err(kd, 0, "vatop called in live kernel!");
404                 return (0);
405         }
406         if (kd->vmst->pae)
407                 return (_i386_vatop_pae(kd, va, pa));
408         else
409                 return (_i386_vatop(kd, va, pa));
410 }
411
412 int
413 _i386_native(kvm_t *kd __unused)
414 {
415
416 #ifdef __i386__
417         return (1);
418 #else
419         return (0);
420 #endif
421 }
422
423 static struct kvm_arch kvm_i386 = {
424         .ka_probe = _i386_probe,
425         .ka_initvtop = _i386_initvtop,
426         .ka_freevtop = _i386_freevtop,
427         .ka_kvatop = _i386_kvatop,
428         .ka_native = _i386_native,
429 };
430
431 KVM_ARCH(kvm_i386);