]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libkvm/kvm_i386.c
sys/{x86,amd64}: remove one of doubled ;s
[FreeBSD/FreeBSD.git] / lib / libkvm / kvm_i386.c
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);