]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libkvm/kvm_i386.c
zfs: merge openzfs/zfs@043c6ee3b
[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 __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);