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
56157911Speter#define PG_FRAME_PAE	(~((uint64_t)PAGE_MASK))
57157911Speter
58157911Speterstruct hpte {
59157911Speter	struct hpte *next;
60157911Speter	uint64_t pa;
61157911Speter	int64_t off;
62157911Speter};
63157911Speter
64157911Speter#define HPT_SIZE 1024
65157911Speter
66157911Speter/* minidump must be the first item! */
67157911Speterstruct vmstate {
68157911Speter	int minidump;		/* 1 = minidump mode */
69157911Speter	struct minidumphdr hdr;
70157911Speter	void *hpt_head[HPT_SIZE];
71157911Speter	uint32_t *bitmap;
72157911Speter	void *ptemap;
73157911Speter};
74157911Speter
75157911Speterstatic void
76157911Speterhpt_insert(kvm_t *kd, uint64_t pa, int64_t off)
77157911Speter{
78157911Speter	struct hpte *hpte;
79157911Speter	uint32_t fnv = FNV1_32_INIT;
80157911Speter
81157911Speter	fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
82157911Speter	fnv &= (HPT_SIZE - 1);
83157911Speter	hpte = malloc(sizeof(*hpte));
84157911Speter	hpte->pa = pa;
85157911Speter	hpte->off = off;
86157911Speter	hpte->next = kd->vmst->hpt_head[fnv];
87157911Speter	kd->vmst->hpt_head[fnv] = hpte;
88157911Speter}
89157911Speter
90157911Speterstatic int64_t
91157911Speterhpt_find(kvm_t *kd, uint64_t pa)
92157911Speter{
93157911Speter	struct hpte *hpte;
94157911Speter	uint32_t fnv = FNV1_32_INIT;
95157911Speter
96157911Speter	fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
97157911Speter	fnv &= (HPT_SIZE - 1);
98157911Speter	for (hpte = kd->vmst->hpt_head[fnv]; hpte != NULL; hpte = hpte->next) {
99157911Speter		if (pa == hpte->pa)
100157911Speter			return (hpte->off);
101157911Speter	}
102157911Speter	return (-1);
103157911Speter}
104157911Speter
105157911Speterstatic int
106157911Speterinithash(kvm_t *kd, uint32_t *base, int len, off_t off)
107157911Speter{
108157911Speter	uint64_t idx;
109157911Speter	uint32_t bit, bits;
110157911Speter	uint64_t pa;
111157911Speter
112157911Speter	for (idx = 0; idx < len / sizeof(*base); idx++) {
113157911Speter		bits = base[idx];
114157911Speter		while (bits) {
115157911Speter			bit = bsfl(bits);
116157911Speter			bits &= ~(1ul << bit);
117157911Speter			pa = (idx * sizeof(*base) * NBBY + bit) * PAGE_SIZE;
118157911Speter			hpt_insert(kd, pa, off);
119157911Speter			off += PAGE_SIZE;
120157911Speter		}
121157911Speter	}
122157911Speter	return (off);
123157911Speter}
124157911Speter
125157911Spetervoid
126157911Speter_kvm_minidump_freevtop(kvm_t *kd)
127157911Speter{
128157911Speter	struct vmstate *vm = kd->vmst;
129157911Speter
130157911Speter	if (vm->bitmap)
131157911Speter		free(vm->bitmap);
132157911Speter	if (vm->ptemap)
133157911Speter		free(vm->ptemap);
134157911Speter	free(vm);
135157911Speter	kd->vmst = NULL;
136157911Speter}
137157911Speter
138157911Speterint
139157911Speter_kvm_minidump_initvtop(kvm_t *kd)
140157911Speter{
141157911Speter	struct vmstate *vmst;
142157911Speter	off_t off;
143157911Speter
144157911Speter	vmst = _kvm_malloc(kd, sizeof(*vmst));
145157911Speter	if (vmst == 0) {
146157911Speter		_kvm_err(kd, kd->program, "cannot allocate vm");
147157911Speter		return (-1);
148157911Speter	}
149157911Speter	kd->vmst = vmst;
150157911Speter	vmst->minidump = 1;
151157911Speter	if (pread(kd->pmfd, &vmst->hdr, sizeof(vmst->hdr), 0) !=
152157911Speter	    sizeof(vmst->hdr)) {
153157911Speter		_kvm_err(kd, kd->program, "cannot read dump header");
154157911Speter		return (-1);
155157911Speter	}
156157911Speter	if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic, sizeof(vmst->hdr.magic)) != 0) {
157157911Speter		_kvm_err(kd, kd->program, "not a minidump for this platform");
158157911Speter		return (-1);
159157911Speter	}
160157911Speter	if (vmst->hdr.version != MINIDUMP_VERSION) {
161157911Speter		_kvm_err(kd, kd->program, "wrong minidump version. expected %d got %d",
162157911Speter		    MINIDUMP_VERSION, vmst->hdr.version);
163157911Speter		return (-1);
164157911Speter	}
165157911Speter
166157911Speter	/* Skip header and msgbuf */
167157911Speter	off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize);
168157911Speter
169157911Speter	vmst->bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
170157911Speter	if (vmst->bitmap == NULL) {
171157911Speter		_kvm_err(kd, kd->program, "cannot allocate %d bytes for bitmap", vmst->hdr.bitmapsize);
172157911Speter		return (-1);
173157911Speter	}
174157911Speter	if (pread(kd->pmfd, vmst->bitmap, vmst->hdr.bitmapsize, off) !=
175217744Suqs	    (ssize_t)vmst->hdr.bitmapsize) {
176157911Speter		_kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap", vmst->hdr.bitmapsize);
177157911Speter		return (-1);
178157911Speter	}
179157911Speter	off += round_page(vmst->hdr.bitmapsize);
180157911Speter
181157911Speter	vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize);
182157911Speter	if (vmst->ptemap == NULL) {
183157911Speter		_kvm_err(kd, kd->program, "cannot allocate %d bytes for ptemap", vmst->hdr.ptesize);
184157911Speter		return (-1);
185157911Speter	}
186157911Speter	if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) !=
187217744Suqs	    (ssize_t)vmst->hdr.ptesize) {
188157911Speter		_kvm_err(kd, kd->program, "cannot read %d bytes for ptemap", vmst->hdr.ptesize);
189157911Speter		return (-1);
190157911Speter	}
191157911Speter	off += vmst->hdr.ptesize;
192157911Speter
193157911Speter	/* build physical address hash table for sparse pages */
194157911Speter	inithash(kd, vmst->bitmap, vmst->hdr.bitmapsize, off);
195157911Speter
196157911Speter	return (0);
197157911Speter}
198157911Speter
199157911Speterstatic int
200157911Speter_kvm_minidump_vatop_pae(kvm_t *kd, u_long va, off_t *pa)
201157911Speter{
202157911Speter	struct vmstate *vm;
203157911Speter	uint64_t offset;
204157911Speter	uint64_t pte;
205157911Speter	u_long pteindex;
206157911Speter	uint64_t a;
207157911Speter	off_t ofs;
208157911Speter	uint64_t *ptemap;
209157911Speter
210157911Speter	vm = kd->vmst;
211157911Speter	ptemap = vm->ptemap;
212157911Speter	offset = va & (PAGE_SIZE - 1);
213157911Speter
214157911Speter	if (va >= vm->hdr.kernbase) {
215157911Speter		pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
216157911Speter		pte = ptemap[pteindex];
217157911Speter		if ((pte & PG_V) == 0) {
218157911Speter			_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
219157911Speter			goto invalid;
220157911Speter		}
221157911Speter		a = pte & PG_FRAME_PAE;
222157911Speter		ofs = hpt_find(kd, a);
223157911Speter		if (ofs == -1) {
224157911Speter			_kvm_err(kd, kd->program, "_kvm_vatop: physical address 0x%llx not in minidump", a);
225157911Speter			goto invalid;
226157911Speter		}
227157911Speter		*pa = ofs + offset;
228157911Speter		return (PAGE_SIZE - offset);
229157911Speter	} else {
230157911Speter		_kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va);
231157911Speter		goto invalid;
232157911Speter	}
233157911Speter
234157911Speterinvalid:
235157911Speter	_kvm_err(kd, 0, "invalid address (0x%lx)", va);
236157911Speter	return (0);
237157911Speter}
238157911Speter
239157911Speterstatic int
240157911Speter_kvm_minidump_vatop(kvm_t *kd, u_long va, off_t *pa)
241157911Speter{
242157911Speter	struct vmstate *vm;
243157911Speter	u_long offset;
244157911Speter	pt_entry_t pte;
245157911Speter	u_long pteindex;
246157911Speter	u_long a;
247157911Speter	off_t ofs;
248157911Speter	uint32_t *ptemap;
249157911Speter
250157911Speter	vm = kd->vmst;
251157911Speter	ptemap = vm->ptemap;
252157911Speter	offset = va & (PAGE_SIZE - 1);
253157911Speter
254157911Speter	if (va >= vm->hdr.kernbase) {
255157911Speter		pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
256157911Speter		pte = ptemap[pteindex];
257157911Speter		if ((pte & PG_V) == 0) {
258157911Speter			_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
259157911Speter			goto invalid;
260157911Speter		}
261157911Speter		a = pte & PG_FRAME;
262157911Speter		ofs = hpt_find(kd, a);
263157911Speter		if (ofs == -1) {
264157911Speter			_kvm_err(kd, kd->program, "_kvm_vatop: physical address 0x%lx not in minidump", a);
265157911Speter			goto invalid;
266157911Speter		}
267157911Speter		*pa = ofs + offset;
268157911Speter		return (PAGE_SIZE - offset);
269157911Speter	} else {
270157911Speter		_kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va);
271157911Speter		goto invalid;
272157911Speter	}
273157911Speter
274157911Speterinvalid:
275157911Speter	_kvm_err(kd, 0, "invalid address (0x%lx)", va);
276157911Speter	return (0);
277157911Speter}
278157911Speter
279157911Speterint
280157911Speter_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
281157911Speter{
282157911Speter
283157911Speter	if (ISALIVE(kd)) {
284157911Speter		_kvm_err(kd, 0, "kvm_kvatop called in live kernel!");
285157911Speter		return (0);
286157911Speter	}
287157911Speter	if (kd->vmst->hdr.paemode)
288157911Speter		return (_kvm_minidump_vatop_pae(kd, va, pa));
289157911Speter	else
290157911Speter		return (_kvm_minidump_vatop(kd, va, pa));
291157911Speter}
292