]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libkvm/kvm_amd64.c
This commit was generated by cvs2svn to compensate for changes in r164146,
[FreeBSD/FreeBSD.git] / lib / libkvm / kvm_amd64.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. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed by the University of
20  *      California, Berkeley and its contributors.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
40
41 #if defined(LIBC_SCCS) && !defined(lint)
42 #if 0
43 static char sccsid[] = "@(#)kvm_hp300.c 8.1 (Berkeley) 6/4/93";
44 #endif
45 #endif /* LIBC_SCCS and not lint */
46
47 /*
48  * AMD64 machine dependent routines for kvm.  Hopefully, the forthcoming
49  * vm code will one day obsolete this module.
50  */
51
52 #include <sys/param.h>
53 #include <sys/user.h>
54 #include <sys/proc.h>
55 #include <sys/stat.h>
56 #include <sys/mman.h>
57 #include <stdlib.h>
58 #include <unistd.h>
59 #include <nlist.h>
60 #include <kvm.h>
61
62 #include <vm/vm.h>
63 #include <vm/vm_param.h>
64
65 #include <machine/elf.h>
66
67 #include <limits.h>
68
69 #include "kvm_private.h"
70
71 #ifndef btop
72 #define btop(x)         (amd64_btop(x))
73 #define ptob(x)         (amd64_ptob(x))
74 #endif
75
76 /* minidump must be the first item! */
77 struct vmstate {
78         int             minidump;       /* 1 = minidump mode */
79         void            *mmapbase;
80         size_t          mmapsize;
81         pml4_entry_t    *PML4;
82 };
83
84 /*
85  * Map the ELF headers into the process' address space. We do this in two
86  * steps: first the ELF header itself and using that information the whole
87  * set of headers. (Taken from kvm_ia64.c)
88  */
89 static int
90 _kvm_maphdrs(kvm_t *kd, size_t sz)
91 {
92         struct vmstate *vm = kd->vmst;
93
94         /* munmap() previous mmap(). */
95         if (vm->mmapbase != NULL) {
96                 munmap(vm->mmapbase, vm->mmapsize);
97                 vm->mmapbase = NULL;
98         }
99
100         vm->mmapsize = sz;
101         vm->mmapbase = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, kd->pmfd, 0);
102         if (vm->mmapbase == MAP_FAILED) {
103                 _kvm_err(kd, kd->program, "cannot mmap corefile");
104                 return (-1);
105         }
106         return (0);
107 }
108
109 /*
110  * Translate a physical memory address to a file-offset in the crash-dump.
111  * (Taken from kvm_ia64.c)
112  */
113 static size_t
114 _kvm_pa2off(kvm_t *kd, uint64_t pa, off_t *ofs)
115 {
116         Elf_Ehdr *e = kd->vmst->mmapbase;
117         Elf_Phdr *p = (Elf_Phdr*)((char*)e + e->e_phoff);
118         int n = e->e_phnum;
119
120         while (n && (pa < p->p_paddr || pa >= p->p_paddr + p->p_memsz))
121                 p++, n--;
122         if (n == 0)
123                 return (0);
124         *ofs = (pa - p->p_paddr) + p->p_offset;
125         return (PAGE_SIZE - ((size_t)pa & PAGE_MASK));
126 }
127
128 void
129 _kvm_freevtop(kvm_t *kd)
130 {
131         struct vmstate *vm = kd->vmst;
132
133         if (kd->vmst->minidump)
134                 return (_kvm_minidump_freevtop(kd));
135         if (vm->mmapbase != NULL)
136                 munmap(vm->mmapbase, vm->mmapsize);
137         if (vm->PML4)
138                 free(vm->PML4);
139         free(vm);
140         kd->vmst = NULL;
141 }
142
143 int
144 _kvm_initvtop(kvm_t *kd)
145 {
146         struct nlist nlist[2];
147         u_long pa;
148         u_long kernbase;
149         pml4_entry_t    *PML4;
150         Elf_Ehdr *ehdr;
151         size_t hdrsz;
152         char minihdr[8];
153
154         if (pread(kd->pmfd, &minihdr, 8, 0) == 8)
155                 if (memcmp(&minihdr, "minidump", 8) == 0)
156                         return (_kvm_minidump_initvtop(kd));
157
158         kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(*kd->vmst));
159         if (kd->vmst == 0) {
160                 _kvm_err(kd, kd->program, "cannot allocate vm");
161                 return (-1);
162         }
163         kd->vmst->PML4 = 0;
164
165         if (_kvm_maphdrs(kd, sizeof(Elf_Ehdr)) == -1)
166                 return (-1);
167
168         ehdr = kd->vmst->mmapbase;
169         hdrsz = ehdr->e_phoff + ehdr->e_phentsize * ehdr->e_phnum;
170         if (_kvm_maphdrs(kd, hdrsz) == -1)
171                 return (-1);
172
173         nlist[0].n_name = "kernbase";
174         nlist[1].n_name = 0;
175
176         if (kvm_nlist(kd, nlist) != 0) {
177                 _kvm_err(kd, kd->program, "bad namelist - no kernbase");
178                 return (-1);
179         }
180         kernbase = nlist[0].n_value;
181
182         nlist[0].n_name = "KPML4phys";
183         nlist[1].n_name = 0;
184
185         if (kvm_nlist(kd, nlist) != 0) {
186                 _kvm_err(kd, kd->program, "bad namelist - no KPML4phys");
187                 return (-1);
188         }
189         if (kvm_read(kd, (nlist[0].n_value - kernbase), &pa, sizeof(pa)) !=
190             sizeof(pa)) {
191                 _kvm_err(kd, kd->program, "cannot read KPML4phys");
192                 return (-1);
193         }
194         PML4 = _kvm_malloc(kd, PAGE_SIZE);
195         if (kvm_read(kd, pa, PML4, PAGE_SIZE) != PAGE_SIZE) {
196                 _kvm_err(kd, kd->program, "cannot read KPML4phys");
197                 return (-1);
198         }
199         kd->vmst->PML4 = PML4;
200         return (0);
201 }
202
203 static int
204 _kvm_vatop(kvm_t *kd, u_long va, off_t *pa)
205 {
206         struct vmstate *vm;
207         u_long offset;
208         u_long pdpe_pa;
209         u_long pde_pa;
210         u_long pte_pa;
211         pml4_entry_t pml4e;
212         pdp_entry_t pdpe;
213         pd_entry_t pde;
214         pt_entry_t pte;
215         u_long pml4eindex;
216         u_long pdpeindex;
217         u_long pdeindex;
218         u_long pteindex;
219         int i;
220         u_long a;
221         off_t ofs;
222         size_t s;
223
224         vm = kd->vmst;
225         offset = va & (PAGE_SIZE - 1);
226
227         /*
228          * If we are initializing (kernel page table descriptor pointer
229          * not yet set) then return pa == va to avoid infinite recursion.
230          */
231         if (vm->PML4 == 0) {
232                 s = _kvm_pa2off(kd, va, pa);
233                 if (s == 0) {
234                         _kvm_err(kd, kd->program,
235                             "_kvm_vatop: bootstrap data not in dump");
236                         goto invalid;
237                 } else
238                         return (PAGE_SIZE - offset);
239         }
240
241         pml4eindex = (va >> PML4SHIFT) & (NPML4EPG - 1);
242         pml4e = vm->PML4[pml4eindex];
243         if (((u_long)pml4e & PG_V) == 0) {
244                 _kvm_err(kd, kd->program, "_kvm_vatop: pml4e not valid");
245                 goto invalid;
246         }
247
248         pdpeindex = (va >> PDPSHIFT) & (NPDPEPG-1);
249         pdpe_pa = ((u_long)pml4e & PG_FRAME) +
250             (pdpeindex * sizeof(pdp_entry_t));
251
252         s = _kvm_pa2off(kd, pdpe_pa, &ofs);
253         if (s < sizeof pdpe) {
254                 _kvm_err(kd, kd->program, "_kvm_vatop: pdpe_pa not found");
255                 goto invalid;
256         }
257         if (lseek(kd->pmfd, ofs, 0) == -1) {
258                 _kvm_syserr(kd, kd->program, "_kvm_vatop: lseek pdpe_pa");
259                 goto invalid;
260         }
261         if (read(kd->pmfd, &pdpe, sizeof pdpe) != sizeof pdpe) {
262                 _kvm_syserr(kd, kd->program, "_kvm_vatop: read pdpe");
263                 goto invalid;
264         }
265         if (((u_long)pdpe & PG_V) == 0) {
266                 _kvm_err(kd, kd->program, "_kvm_vatop: pdpe not valid");
267                 goto invalid;
268         }
269
270         pdeindex = (va >> PDRSHIFT) & (NPDEPG-1);
271         pde_pa = ((u_long)pdpe & PG_FRAME) + (pdeindex * sizeof(pd_entry_t));
272
273         s = _kvm_pa2off(kd, pde_pa, &ofs);
274         if (s < sizeof pde) {
275                 _kvm_syserr(kd, kd->program, "_kvm_vatop: pde_pa not found");
276                 goto invalid;
277         }
278         if (lseek(kd->pmfd, ofs, 0) == -1) {
279                 _kvm_err(kd, kd->program, "_kvm_vatop: lseek pde_pa");
280                 goto invalid;
281         }
282         if (read(kd->pmfd, &pde, sizeof pde) != sizeof pde) {
283                 _kvm_syserr(kd, kd->program, "_kvm_vatop: read pde");
284                 goto invalid;
285         }
286         if (((u_long)pde & PG_V) == 0) {
287                 _kvm_err(kd, kd->program, "_kvm_vatop: pde not valid");
288                 goto invalid;
289         }
290
291         if ((u_long)pde & PG_PS) {
292               /*
293                * No final-level page table; ptd describes one 2MB page.
294                */
295 #define PAGE2M_MASK     (NBPDR - 1)
296 #define PG_FRAME2M      (~PAGE2M_MASK)
297                 a = ((u_long)pde & PG_FRAME2M) + (va & PAGE2M_MASK);
298                 s = _kvm_pa2off(kd, a, pa);
299                 if (s == 0) {
300                         _kvm_err(kd, kd->program,
301                             "_kvm_vatop: 2MB page address not in dump");
302                         goto invalid;
303                 } else
304                         return (NBPDR - (va & PAGE2M_MASK));
305         }
306
307         pteindex = (va >> PAGE_SHIFT) & (NPTEPG-1);
308         pte_pa = ((u_long)pde & PG_FRAME) + (pteindex * sizeof(pt_entry_t));
309
310         s = _kvm_pa2off(kd, pte_pa, &ofs);
311         if (s < sizeof pte) {
312                 _kvm_err(kd, kd->program, "_kvm_vatop: pte_pa not found");
313                 goto invalid;
314         }
315         if (lseek(kd->pmfd, ofs, 0) == -1) {
316                 _kvm_syserr(kd, kd->program, "_kvm_vatop: lseek");
317                 goto invalid;
318         }
319         if (read(kd->pmfd, &pte, sizeof pte) != sizeof pte) {
320                 _kvm_syserr(kd, kd->program, "_kvm_vatop: read");
321                 goto invalid;
322         }
323         if (((u_long)pte & PG_V) == 0) {
324                 _kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
325                 goto invalid;
326         }
327
328         a = ((u_long)pte & PG_FRAME) + offset;
329         s = _kvm_pa2off(kd, a, pa);
330         if (s == 0) {
331                 _kvm_err(kd, kd->program, "_kvm_vatop: address not in dump");
332                 goto invalid;
333         } else
334                 return (PAGE_SIZE - offset);
335
336 invalid:
337         _kvm_err(kd, 0, "invalid address (0x%lx)", va);
338         return (0);
339 }
340
341 int
342 _kvm_kvatop(kvm_t *kd, u_long va, off_t *pa)
343 {
344
345         if (kd->vmst->minidump)
346                 return (_kvm_minidump_kvatop(kd, va, pa));
347         if (ISALIVE(kd)) {
348                 _kvm_err(kd, 0, "kvm_kvatop called in live kernel!");
349                 return (0);
350         }
351         return (_kvm_vatop(kd, va, pa));
352 }