| Commit | Line | Data |
|---|---|---|
| 17030342 MD |
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 | * @(#)kvm_hp300.c 8.1 (Berkeley) 6/4/93 | |
| 38 | * $FreeBSD: src/lib/libkvm/kvm_amd64.c,v 1.16 2003/04/30 21:05:33 peter Exp $ | |
| 17030342 MD |
39 | */ |
| 40 | ||
| 41 | /* | |
| b2b3ffcd | 42 | * x86_64 machine dependent routines for kvm. Hopefully, the forthcoming |
| 17030342 MD |
43 | * vm code will one day obsolete this module. |
| 44 | */ | |
| 45 | ||
| 9f17b787 | 46 | #include <sys/user.h> /* MUST BE FIRST */ |
| 17030342 | 47 | #include <sys/param.h> |
| 17030342 MD |
48 | #include <sys/proc.h> |
| 49 | #include <sys/stat.h> | |
| 78a7b07a AH |
50 | #include <sys/mman.h> |
| 51 | #include <sys/elf_common.h> | |
| 17030342 | 52 | #include <stdlib.h> |
| 78a7b07a | 53 | #include <string.h> |
| 17030342 MD |
54 | #include <unistd.h> |
| 55 | #include <nlist.h> | |
| 56 | #include <kvm.h> | |
| 57 | ||
| 58 | #include <vm/vm.h> | |
| 59 | #include <vm/vm_param.h> | |
| 60 | ||
| 78a7b07a AH |
61 | #include <machine/elf.h> |
| 62 | ||
| 17030342 MD |
63 | #include <limits.h> |
| 64 | ||
| 65 | #include "kvm_private.h" | |
| 66 | ||
| 67 | #ifndef btop | |
| b2b3ffcd SS |
68 | #define btop(x) (x86_64_btop(x)) |
| 69 | #define ptob(x) (x86_64_ptob(x)) | |
| 17030342 MD |
70 | #endif |
| 71 | ||
| 72 | struct vmstate { | |
| 78a7b07a AH |
73 | int minidump; /* 1 = minidump mode */ |
| 74 | void *mmapbase; | |
| 75 | size_t mmapsize; | |
| a60accf7 | 76 | pml4_entry_t *PML4; |
| 17030342 MD |
77 | }; |
| 78 | ||
| 78a7b07a AH |
79 | /* |
| 80 | * Map the ELF headers into the process' address space. We do this in two | |
| 81 | * steps: first the ELF header itself and using that information the whole | |
| 82 | * set of headers. (Taken from kvm_ia64.c) | |
| 83 | */ | |
| 84 | static int | |
| 85 | _kvm_maphdrs(kvm_t *kd, size_t sz) | |
| 86 | { | |
| 87 | struct vmstate *vm = kd->vmst; | |
| 88 | ||
| 89 | if (kd->vmst->minidump) { | |
| 90 | _kvm_minidump_freevtop(kd); | |
| 91 | return (0); | |
| 92 | } | |
| 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; | |
| 118 | int n; | |
| 119 | ||
| 120 | if (kd->rawdump) { | |
| 121 | *ofs = pa; | |
| 122 | return (PAGE_SIZE - ((size_t)pa & PAGE_MASK)); | |
| 123 | } | |
| 124 | ||
| 125 | p = (Elf_Phdr*)((char*)e + e->e_phoff); | |
| 126 | n = e->e_phnum; | |
| 127 | ||
| 128 | while (n && (pa < p->p_paddr || pa >= p->p_paddr + p->p_memsz)) | |
| 129 | p++, n--; | |
| 130 | if (n == 0) | |
| 131 | return (0); | |
| 132 | *ofs = (pa - p->p_paddr) + p->p_offset; | |
| 133 | return (PAGE_SIZE - ((size_t)pa & PAGE_MASK)); | |
| 134 | } | |
| 135 | ||
| 17030342 MD |
136 | void |
| 137 | _kvm_freevtop(kvm_t *kd) | |
| 138 | { | |
| 78a7b07a AH |
139 | struct vmstate *vm = kd->vmst; |
| 140 | ||
| 141 | if (kd->vmst->minidump) { | |
| 142 | _kvm_minidump_freevtop(kd); | |
| 143 | return; | |
| 17030342 | 144 | } |
| 78a7b07a AH |
145 | |
| 146 | if (vm->mmapbase != NULL) | |
| 147 | munmap(vm->mmapbase, vm->mmapsize); | |
| 148 | if (vm->PML4) | |
| 149 | free(vm->PML4); | |
| 150 | free(vm); | |
| 151 | kd->vmst = NULL; | |
| 17030342 MD |
152 | } |
| 153 | ||
| 154 | int | |
| 155 | _kvm_initvtop(kvm_t *kd) | |
| 156 | { | |
| 17030342 MD |
157 | struct nlist nlist[2]; |
| 158 | u_long pa; | |
| 159 | u_long kernbase; | |
| a60accf7 | 160 | pml4_entry_t *PML4; |
| 78a7b07a AH |
161 | Elf_Ehdr *ehdr; |
| 162 | size_t hdrsz; | |
| 163 | char minihdr[8]; | |
| 3f1be088 | 164 | struct pcb dumppcb; |
| 78a7b07a AH |
165 | |
| 166 | if (pread(kd->pmfd, &minihdr, 8, 0) == 8) | |
| 167 | if (memcmp(&minihdr, "minidump", 8) == 0) | |
| 168 | return (_kvm_minidump_initvtop(kd)); | |
| 17030342 | 169 | |
| 78a7b07a AH |
170 | kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(*kd->vmst)); |
| 171 | if (kd->vmst == 0) { | |
| 17030342 MD |
172 | _kvm_err(kd, kd->program, "cannot allocate vm"); |
| 173 | return (-1); | |
| 174 | } | |
| 78a7b07a AH |
175 | kd->vmst->PML4 = 0; |
| 176 | ||
| 177 | if (_kvm_maphdrs(kd, sizeof(Elf_Ehdr)) == -1) | |
| 178 | return (-1); | |
| 179 | /* | |
| 3f1be088 MD |
180 | * Check if this is indeed an ELF header. If not, assume old style |
| 181 | *dump or memory layout. | |
| 78a7b07a AH |
182 | */ |
| 183 | ehdr = kd->vmst->mmapbase; | |
| 184 | if (!IS_ELF(*ehdr)) { | |
| 185 | kd->rawdump = 1; | |
| 186 | munmap(kd->vmst->mmapbase, kd->vmst->mmapsize); | |
| 187 | kd->vmst->mmapbase = NULL; | |
| 188 | } else { | |
| 189 | hdrsz = ehdr->e_phoff + ehdr->e_phentsize * ehdr->e_phnum; | |
| 190 | if (_kvm_maphdrs(kd, hdrsz) == -1) | |
| 191 | return (-1); | |
| 192 | } | |
| 17030342 MD |
193 | |
| 194 | nlist[0].n_name = "kernbase"; | |
| 195 | nlist[1].n_name = 0; | |
| 196 | ||
| a60accf7 MD |
197 | if (kvm_nlist(kd, nlist) != 0) { |
| 198 | _kvm_err(kd, kd->program, "bad namelist - no kernbase"); | |
| 199 | return (-1); | |
| 200 | } | |
| 201 | kernbase = nlist[0].n_value; | |
| 17030342 | 202 | |
| 3f1be088 | 203 | nlist[0].n_name = "dumppcb"; |
| 17030342 MD |
204 | nlist[1].n_name = 0; |
| 205 | ||
| 206 | if (kvm_nlist(kd, nlist) != 0) { | |
| 3f1be088 | 207 | _kvm_err(kd, kd->program, "bad namelist - no dumppcb"); |
| 17030342 MD |
208 | return (-1); |
| 209 | } | |
| 3f1be088 MD |
210 | if (kvm_read(kd, (nlist[0].n_value - kernbase), &dumppcb, |
| 211 | sizeof(dumppcb)) != sizeof(dumppcb)) { | |
| 212 | _kvm_err(kd, kd->program, "cannot read dumppcb"); | |
| 17030342 MD |
213 | return (-1); |
| 214 | } | |
| 3f1be088 MD |
215 | pa = dumppcb.pcb_cr3 & PG_FRAME; |
| 216 | ||
| a60accf7 MD |
217 | PML4 = _kvm_malloc(kd, PAGE_SIZE); |
| 218 | if (kvm_read(kd, pa, PML4, PAGE_SIZE) != PAGE_SIZE) { | |
| 3f1be088 | 219 | _kvm_err(kd, kd->program, "cannot read dumppcb"); |
| 17030342 MD |
220 | return (-1); |
| 221 | } | |
| 78a7b07a | 222 | kd->vmst->PML4 = PML4; |
| 17030342 MD |
223 | return (0); |
| 224 | } | |
| 225 | ||
| 226 | static int | |
| 78a7b07a | 227 | _kvm_vatop(kvm_t *kd, u_long va, off_t *pa) |
| 17030342 MD |
228 | { |
| 229 | struct vmstate *vm; | |
| 230 | u_long offset; | |
| a60accf7 MD |
231 | u_long pdpe_pa; |
| 232 | u_long pde_pa; | |
| 17030342 | 233 | u_long pte_pa; |
| a60accf7 MD |
234 | pml4_entry_t pml4e; |
| 235 | pdp_entry_t pdpe; | |
| 17030342 MD |
236 | pd_entry_t pde; |
| 237 | pt_entry_t pte; | |
| a60accf7 MD |
238 | u_long pml4eindex; |
| 239 | u_long pdpeindex; | |
| 17030342 MD |
240 | u_long pdeindex; |
| 241 | u_long pteindex; | |
| 78a7b07a AH |
242 | u_long a; |
| 243 | off_t ofs; | |
| 244 | size_t s; | |
| 17030342 MD |
245 | |
| 246 | vm = kd->vmst; | |
| 247 | offset = va & (PAGE_SIZE - 1); | |
| 248 | ||
| 249 | /* | |
| 250 | * If we are initializing (kernel page table descriptor pointer | |
| 251 | * not yet set) then return pa == va to avoid infinite recursion. | |
| 252 | */ | |
| a60accf7 | 253 | if (vm->PML4 == 0) { |
| 78a7b07a AH |
254 | s = _kvm_pa2off(kd, va, pa); |
| 255 | if (s == 0) { | |
| 256 | _kvm_err(kd, kd->program, | |
| 257 | "_kvm_vatop: bootstrap data not in dump"); | |
| 258 | goto invalid; | |
| 259 | } else | |
| 260 | return (PAGE_SIZE - offset); | |
| 17030342 MD |
261 | } |
| 262 | ||
| a60accf7 MD |
263 | pml4eindex = (va >> PML4SHIFT) & (NPML4EPG - 1); |
| 264 | pml4e = vm->PML4[pml4eindex]; | |
| 78a7b07a AH |
265 | if (((u_long)pml4e & PG_V) == 0) { |
| 266 | _kvm_err(kd, kd->program, "_kvm_vatop: pml4e not valid"); | |
| a60accf7 | 267 | goto invalid; |
| 78a7b07a | 268 | } |
| a60accf7 MD |
269 | |
| 270 | pdpeindex = (va >> PDPSHIFT) & (NPDPEPG-1); | |
| 78a7b07a AH |
271 | pdpe_pa = ((u_long)pml4e & PG_FRAME) + |
| 272 | (pdpeindex * sizeof(pdp_entry_t)); | |
| a60accf7 | 273 | |
| 78a7b07a | 274 | s = _kvm_pa2off(kd, pdpe_pa, &ofs); |
| 3f1be088 | 275 | if (s < sizeof pdpe) { |
| 78a7b07a AH |
276 | _kvm_err(kd, kd->program, "_kvm_vatop: pdpe_pa not found"); |
| 277 | goto invalid; | |
| 278 | } | |
| 279 | if (lseek(kd->pmfd, ofs, 0) == -1) { | |
| a60accf7 MD |
280 | _kvm_syserr(kd, kd->program, "_kvm_vatop: lseek pdpe_pa"); |
| 281 | goto invalid; | |
| 282 | } | |
| 283 | if (read(kd->pmfd, &pdpe, sizeof pdpe) != sizeof pdpe) { | |
| 284 | _kvm_syserr(kd, kd->program, "_kvm_vatop: read pdpe"); | |
| 285 | goto invalid; | |
| 286 | } | |
| 78a7b07a AH |
287 | if (((u_long)pdpe & PG_V) == 0) { |
| 288 | _kvm_err(kd, kd->program, "_kvm_vatop: pdpe not valid"); | |
| a60accf7 | 289 | goto invalid; |
| 78a7b07a | 290 | } |
| a60accf7 | 291 | |
| a60accf7 MD |
292 | pdeindex = (va >> PDRSHIFT) & (NPDEPG-1); |
| 293 | pde_pa = ((u_long)pdpe & PG_FRAME) + (pdeindex * sizeof(pd_entry_t)); | |
| 294 | ||
| 78a7b07a | 295 | s = _kvm_pa2off(kd, pde_pa, &ofs); |
| 3f1be088 | 296 | if (s < sizeof pde) { |
| 78a7b07a AH |
297 | _kvm_syserr(kd, kd->program, "_kvm_vatop: pde_pa not found"); |
| 298 | goto invalid; | |
| 299 | } | |
| 300 | if (lseek(kd->pmfd, ofs, 0) == -1) { | |
| 301 | _kvm_err(kd, kd->program, "_kvm_vatop: lseek pde_pa"); | |
| a60accf7 MD |
302 | goto invalid; |
| 303 | } | |
| 304 | if (read(kd->pmfd, &pde, sizeof pde) != sizeof pde) { | |
| 305 | _kvm_syserr(kd, kd->program, "_kvm_vatop: read pde"); | |
| 306 | goto invalid; | |
| 307 | } | |
| 78a7b07a AH |
308 | if (((u_long)pde & PG_V) == 0) { |
| 309 | _kvm_err(kd, kd->program, "_kvm_vatop: pde not valid"); | |
| 17030342 | 310 | goto invalid; |
| 78a7b07a | 311 | } |
| 17030342 MD |
312 | |
| 313 | if ((u_long)pde & PG_PS) { | |
| 314 | /* | |
| a60accf7 | 315 | * No final-level page table; ptd describes one 2MB page. |
| 17030342 | 316 | */ |
| a60accf7 MD |
317 | #define PAGE2M_MASK (NBPDR - 1) |
| 318 | #define PG_FRAME2M (~PAGE2M_MASK) | |
| 78a7b07a AH |
319 | a = ((u_long)pde & PG_FRAME2M) + (va & PAGE2M_MASK); |
| 320 | s = _kvm_pa2off(kd, a, pa); | |
| 321 | if (s == 0) { | |
| 322 | _kvm_err(kd, kd->program, | |
| 323 | "_kvm_vatop: 2MB page address not in dump"); | |
| 324 | goto invalid; | |
| 325 | } else { | |
| 326 | return (NBPDR - (va & PAGE2M_MASK)); | |
| 327 | } | |
| 17030342 MD |
328 | } |
| 329 | ||
| 330 | pteindex = (va >> PAGE_SHIFT) & (NPTEPG-1); | |
| 331 | pte_pa = ((u_long)pde & PG_FRAME) + (pteindex * sizeof(pt_entry_t)); | |
| 332 | ||
| 78a7b07a | 333 | s = _kvm_pa2off(kd, pte_pa, &ofs); |
| 3f1be088 | 334 | if (s < sizeof pte) { |
| 78a7b07a AH |
335 | _kvm_err(kd, kd->program, "_kvm_vatop: pte_pa not found"); |
| 336 | goto invalid; | |
| 337 | } | |
| 338 | if (lseek(kd->pmfd, ofs, 0) == -1) { | |
| 17030342 MD |
339 | _kvm_syserr(kd, kd->program, "_kvm_vatop: lseek"); |
| 340 | goto invalid; | |
| 341 | } | |
| 342 | if (read(kd->pmfd, &pte, sizeof pte) != sizeof pte) { | |
| 343 | _kvm_syserr(kd, kd->program, "_kvm_vatop: read"); | |
| 344 | goto invalid; | |
| 345 | } | |
| 78a7b07a AH |
346 | if (((u_long)pte & PG_V) == 0) { |
| 347 | _kvm_err(kd, kd->program, "_kvm_vatop: pte not valid"); | |
| 17030342 | 348 | goto invalid; |
| 78a7b07a | 349 | } |
| 17030342 | 350 | |
| 78a7b07a AH |
351 | a = ((u_long)pte & PG_FRAME) + offset; |
| 352 | s = _kvm_pa2off(kd, a, pa); | |
| 353 | if (s == 0) { | |
| 354 | _kvm_err(kd, kd->program, "_kvm_vatop: address not in dump"); | |
| 355 | goto invalid; | |
| 356 | } else | |
| 357 | return (PAGE_SIZE - offset); | |
| 17030342 MD |
358 | |
| 359 | invalid: | |
| 78a7b07a | 360 | _kvm_err(kd, 0, "invalid address (0x%lx)", va); |
| 17030342 MD |
361 | return (0); |
| 362 | } | |
| 363 | ||
| 364 | int | |
| 78a7b07a | 365 | _kvm_kvatop(kvm_t *kd, u_long va, off_t *pa) |
| 17030342 | 366 | { |
| 78a7b07a AH |
367 | if (kd->vmst->minidump) |
| 368 | return (_kvm_minidump_kvatop(kd, va, pa)); | |
| 369 | ||
| 370 | if (kvm_ishost(kd)) { | |
| 371 | _kvm_err(kd, 0, "kvm_vatop called in live kernel!"); | |
| 372 | return((off_t)0); | |
| 373 | } | |
| 374 | ||
| 17030342 MD |
375 | return (_kvm_vatop(kd, va, pa)); |
| 376 | } |