1157911Speter/*-
2157911Speter * Copyright (c) 2006 Peter Wemm
3157911Speter *
4157911Speter * Redistribution and use in source and binary forms, with or without
5157911Speter * modification, are permitted provided that the following conditions
6157911Speter * are met:
7157911Speter * 1. Redistributions of source code must retain the above copyright
8157911Speter *    notice, this list of conditions and the following disclaimer.
9157911Speter * 2. Redistributions in binary form must reproduce the above copyright
10157911Speter *    notice, this list of conditions and the following disclaimer in the
11157911Speter *    documentation and/or other materials provided with the distribution.
12157911Speter *
13157911Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14157911Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15157911Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16157911Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17157911Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18157911Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19157911Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20157911Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21157911Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22157911Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23157911Speter * SUCH DAMAGE.
24157911Speter */
25157911Speter
26157911Speter#include <sys/cdefs.h>
27157911Speter__FBSDID("$FreeBSD$");
28157911Speter
29157911Speter/*
30157911Speter * AMD64 machine dependent routines for kvm and minidumps.
31157911Speter */
32157911Speter
33157911Speter#include <sys/param.h>
34157911Speter#include <sys/user.h>
35157911Speter#include <sys/proc.h>
36157911Speter#include <sys/stat.h>
37157911Speter#include <sys/mman.h>
38157911Speter#include <sys/fnv_hash.h>
39157911Speter#include <stdlib.h>
40194186Sed#include <string.h>
41157911Speter#include <unistd.h>
42157911Speter#include <nlist.h>
43157911Speter#include <kvm.h>
44157911Speter
45157911Speter#include <vm/vm.h>
46157911Speter#include <vm/vm_param.h>
47157911Speter
48157911Speter#include <machine/elf.h>
49157911Speter#include <machine/cpufunc.h>
50157911Speter#include <machine/minidump.h>
51157911Speter
52157911Speter#include <limits.h>
53157911Speter
54157911Speter#include "kvm_private.h"
55157911Speter
56157911Speterstruct hpte {
57157911Speter	struct hpte *next;
58157911Speter	vm_paddr_t pa;
59157911Speter	int64_t off;
60157911Speter};
61157911Speter
62157911Speter#define HPT_SIZE 1024
63157911Speter
64157911Speter/* minidump must be the first item! */
65157911Speterstruct vmstate {
66157911Speter	int minidump;		/* 1 = minidump mode */
67157911Speter	struct minidumphdr hdr;
68157911Speter	void *hpt_head[HPT_SIZE];
69157911Speter	uint64_t *bitmap;
70215133Savg	uint64_t *page_map;
71157911Speter};
72157911Speter
73157911Speterstatic void
74157911Speterhpt_insert(kvm_t *kd, vm_paddr_t pa, int64_t off)
75157911Speter{
76157911Speter	struct hpte *hpte;
77157911Speter	uint32_t fnv = FNV1_32_INIT;
78157911Speter
79157911Speter	fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
80157911Speter	fnv &= (HPT_SIZE - 1);
81157911Speter	hpte = malloc(sizeof(*hpte));
82157911Speter	hpte->pa = pa;
83157911Speter	hpte->off = off;
84157911Speter	hpte->next = kd->vmst->hpt_head[fnv];
85157911Speter	kd->vmst->hpt_head[fnv] = hpte;
86157911Speter}
87157911Speter
88157911Speterstatic int64_t
89157911Speterhpt_find(kvm_t *kd, vm_paddr_t pa)
90157911Speter{
91157911Speter	struct hpte *hpte;
92157911Speter	uint32_t fnv = FNV1_32_INIT;
93157911Speter
94157911Speter	fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
95157911Speter	fnv &= (HPT_SIZE - 1);
96157911Speter	for (hpte = kd->vmst->hpt_head[fnv]; hpte != NULL; hpte = hpte->next) {
97157911Speter		if (pa == hpte->pa)
98157911Speter			return (hpte->off);
99157911Speter	}
100157911Speter	return (-1);
101157911Speter}
102157911Speter
103157911Speterstatic int
104157911Speterinithash(kvm_t *kd, uint64_t *base, int len, off_t off)
105157911Speter{
106157911Speter	uint64_t idx;
107157911Speter	uint64_t bit, bits;
108157911Speter	vm_paddr_t pa;
109157911Speter
110157911Speter	for (idx = 0; idx < len / sizeof(*base); idx++) {
111157911Speter		bits = base[idx];
112157911Speter		while (bits) {
113157911Speter			bit = bsfq(bits);
114157911Speter			bits &= ~(1ul << bit);
115157911Speter			pa = (idx * sizeof(*base) * NBBY + bit) * PAGE_SIZE;
116157911Speter			hpt_insert(kd, pa, off);
117157911Speter			off += PAGE_SIZE;
118157911Speter		}
119157911Speter	}
120157911Speter	return (off);
121157911Speter}
122157911Speter
123157911Spetervoid
124157911Speter_kvm_minidump_freevtop(kvm_t *kd)
125157911Speter{
126157911Speter	struct vmstate *vm = kd->vmst;
127157911Speter
128157911Speter	if (vm->bitmap)
129157911Speter		free(vm->bitmap);
130215133Savg	if (vm->page_map)
131215133Savg		free(vm->page_map);
132157911Speter	free(vm);
133157911Speter	kd->vmst = NULL;
134157911Speter}
135157911Speter
136157911Speterint
137157911Speter_kvm_minidump_initvtop(kvm_t *kd)
138157911Speter{
139157911Speter	struct vmstate *vmst;
140157911Speter	off_t off;
141157911Speter
142157911Speter	vmst = _kvm_malloc(kd, sizeof(*vmst));
143157911Speter	if (vmst == 0) {
144157911Speter		_kvm_err(kd, kd->program, "cannot allocate vm");
145157911Speter		return (-1);
146157911Speter	}
147157911Speter	kd->vmst = vmst;
148157911Speter	vmst->minidump = 1;
149157911Speter	if (pread(kd->pmfd, &vmst->hdr, sizeof(vmst->hdr), 0) !=
150157911Speter	    sizeof(vmst->hdr)) {
151157911Speter		_kvm_err(kd, kd->program, "cannot read dump header");
152157911Speter		return (-1);
153157911Speter	}
154157911Speter	if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic, sizeof(vmst->hdr.magic)) != 0) {
155157911Speter		_kvm_err(kd, kd->program, "not a minidump for this platform");
156157911Speter		return (-1);
157157911Speter	}
158215133Savg
159215133Savg	/*
160215133Savg	 * NB: amd64 minidump header is binary compatible between version 1
161215133Savg	 * and version 2; this may not be the case for the future versions.
162215133Savg	 */
163215133Savg	if (vmst->hdr.version != MINIDUMP_VERSION && vmst->hdr.version != 1) {
164157911Speter		_kvm_err(kd, kd->program, "wrong minidump version. expected %d got %d",
165157911Speter		    MINIDUMP_VERSION, vmst->hdr.version);
166157911Speter		return (-1);
167157911Speter	}
168157911Speter
169157911Speter	/* Skip header and msgbuf */
170157911Speter	off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize);
171157911Speter
172157911Speter	vmst->bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
173157911Speter	if (vmst->bitmap == NULL) {
174157911Speter		_kvm_err(kd, kd->program, "cannot allocate %d bytes for bitmap", vmst->hdr.bitmapsize);
175157911Speter		return (-1);
176157911Speter	}
177157911Speter	if (pread(kd->pmfd, vmst->bitmap, vmst->hdr.bitmapsize, off) !=
178157911Speter	    vmst->hdr.bitmapsize) {
179157911Speter		_kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap", vmst->hdr.bitmapsize);
180157911Speter		return (-1);
181157911Speter	}
182157911Speter	off += round_page(vmst->hdr.bitmapsize);
183157911Speter
184215133Savg	vmst->page_map = _kvm_malloc(kd, vmst->hdr.pmapsize);
185215133Savg	if (vmst->page_map == NULL) {
186215133Savg		_kvm_err(kd, kd->program, "cannot allocate %d bytes for page_map", vmst->hdr.pmapsize);
187157911Speter		return (-1);
188157911Speter	}
189215133Savg	if (pread(kd->pmfd, vmst->page_map, vmst->hdr.pmapsize, off) !=
190215133Savg	    vmst->hdr.pmapsize) {
191215133Savg		_kvm_err(kd, kd->program, "cannot read %d bytes for page_map", vmst->hdr.pmapsize);
192157911Speter		return (-1);
193157911Speter	}
194215133Savg	off += vmst->hdr.pmapsize;
195157911Speter
196157911Speter	/* build physical address hash table for sparse pages */
197157911Speter	inithash(kd, vmst->bitmap, vmst->hdr.bitmapsize, off);
198157911Speter
199157911Speter	return (0);
200157911Speter}
201157911Speter
202157911Speterstatic int
203215133Savg_kvm_minidump_vatop_v1(kvm_t *kd, u_long va, off_t *pa)
204157911Speter{
205157911Speter	struct vmstate *vm;
206157911Speter	u_long offset;
207157911Speter	pt_entry_t pte;
208157911Speter	u_long pteindex;
209157911Speter	u_long a;
210157911Speter	off_t ofs;
211157911Speter
212157911Speter	vm = kd->vmst;
213157911Speter	offset = va & (PAGE_SIZE - 1);
214157911Speter
215157911Speter	if (va >= vm->hdr.kernbase) {
216157911Speter		pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
217269013Semaste		if (pteindex >= vm->hdr.pmapsize / sizeof(*vm->page_map))
218269013Semaste			goto invalid;
219215133Savg		pte = vm->page_map[pteindex];
220157911Speter		if (((u_long)pte & PG_V) == 0) {
221157911Speter			_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
222157911Speter			goto invalid;
223157911Speter		}
224157911Speter		a = pte & PG_FRAME;
225157911Speter		ofs = hpt_find(kd, a);
226157911Speter		if (ofs == -1) {
227157911Speter			_kvm_err(kd, kd->program, "_kvm_vatop: physical address 0x%lx not in minidump", a);
228157911Speter			goto invalid;
229157911Speter		}
230157911Speter		*pa = ofs + offset;
231157911Speter		return (PAGE_SIZE - offset);
232157911Speter	} else if (va >= vm->hdr.dmapbase && va < vm->hdr.dmapend) {
233157911Speter		a = (va - vm->hdr.dmapbase) & ~PAGE_MASK;
234157911Speter		ofs = hpt_find(kd, a);
235157911Speter		if (ofs == -1) {
236157911Speter			_kvm_err(kd, kd->program, "_kvm_vatop: direct map address 0x%lx not in minidump", va);
237157911Speter			goto invalid;
238157911Speter		}
239157911Speter		*pa = ofs + offset;
240157911Speter		return (PAGE_SIZE - offset);
241157911Speter	} else {
242157911Speter		_kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va);
243157911Speter		goto invalid;
244157911Speter	}
245157911Speter
246157911Speterinvalid:
247157911Speter	_kvm_err(kd, 0, "invalid address (0x%lx)", va);
248157911Speter	return (0);
249157911Speter}
250157911Speter
251215133Savgstatic int
252215133Savg_kvm_minidump_vatop(kvm_t *kd, u_long va, off_t *pa)
253215133Savg{
254215133Savg	pt_entry_t pt[NPTEPG];
255215133Savg	struct vmstate *vm;
256215133Savg	u_long offset;
257215133Savg	pd_entry_t pde;
258215133Savg	pd_entry_t pte;
259215133Savg	u_long pteindex;
260215133Savg	u_long pdeindex;
261215133Savg	u_long a;
262215133Savg	off_t ofs;
263215133Savg
264215133Savg	vm = kd->vmst;
265215133Savg	offset = va & PAGE_MASK;
266215133Savg
267215133Savg	if (va >= vm->hdr.kernbase) {
268215133Savg		pdeindex = (va - vm->hdr.kernbase) >> PDRSHIFT;
269269013Semaste		if (pdeindex >= vm->hdr.pmapsize / sizeof(*vm->page_map))
270269013Semaste			goto invalid;
271215133Savg		pde = vm->page_map[pdeindex];
272215133Savg		if (((u_long)pde & PG_V) == 0) {
273215133Savg			_kvm_err(kd, kd->program, "_kvm_vatop: pde not valid");
274215133Savg			goto invalid;
275215133Savg		}
276215133Savg		if ((pde & PG_PS) == 0) {
277215133Savg			a = pde & PG_FRAME;
278215133Savg			ofs = hpt_find(kd, a);
279215133Savg			if (ofs == -1) {
280215133Savg				_kvm_err(kd, kd->program, "_kvm_vatop: pt physical address 0x%lx not in minidump", a);
281215133Savg				goto invalid;
282215133Savg			}
283215133Savg			if (pread(kd->pmfd, &pt, PAGE_SIZE, ofs) != PAGE_SIZE) {
284215133Savg				_kvm_err(kd, kd->program, "cannot read %d bytes for pt", PAGE_SIZE);
285215133Savg				return (-1);
286215133Savg			}
287215133Savg			pteindex = (va >> PAGE_SHIFT) & ((1ul << NPTEPGSHIFT) - 1);
288215133Savg			pte = pt[pteindex];
289215133Savg			if (((u_long)pte & PG_V) == 0) {
290215133Savg				_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
291215133Savg				goto invalid;
292215133Savg			}
293215133Savg			a = pte & PG_FRAME;
294215133Savg		} else {
295215133Savg			a = pde & PG_PS_FRAME;
296215133Savg			a += (va & PDRMASK) ^ offset;
297215133Savg		}
298215133Savg		ofs = hpt_find(kd, a);
299215133Savg		if (ofs == -1) {
300215133Savg			_kvm_err(kd, kd->program, "_kvm_vatop: physical address 0x%lx not in minidump", a);
301215133Savg			goto invalid;
302215133Savg		}
303215133Savg		*pa = ofs + offset;
304215133Savg		return (PAGE_SIZE - offset);
305215133Savg	} else if (va >= vm->hdr.dmapbase && va < vm->hdr.dmapend) {
306215133Savg		a = (va - vm->hdr.dmapbase) & ~PAGE_MASK;
307215133Savg		ofs = hpt_find(kd, a);
308215133Savg		if (ofs == -1) {
309215133Savg			_kvm_err(kd, kd->program, "_kvm_vatop: direct map address 0x%lx not in minidump", va);
310215133Savg			goto invalid;
311215133Savg		}
312215133Savg		*pa = ofs + offset;
313215133Savg		return (PAGE_SIZE - offset);
314215133Savg	} else {
315215133Savg		_kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va);
316215133Savg		goto invalid;
317215133Savg	}
318215133Savg
319215133Savginvalid:
320215133Savg	_kvm_err(kd, 0, "invalid address (0x%lx)", va);
321215133Savg	return (0);
322215133Savg}
323215133Savg
324157911Speterint
325157911Speter_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
326157911Speter{
327157911Speter
328157911Speter	if (ISALIVE(kd)) {
329157911Speter		_kvm_err(kd, 0, "kvm_kvatop called in live kernel!");
330157911Speter		return (0);
331157911Speter	}
332215133Savg	if (((struct vmstate *)kd->vmst)->hdr.version == 1)
333215133Savg		return (_kvm_minidump_vatop_v1(kd, va, pa));
334215133Savg	else
335215133Savg		return (_kvm_minidump_vatop(kd, va, pa));
336157911Speter}
337