kvm_minidump_i386.c revision 217744
1/*-
2 * Copyright (c) 2006 Peter Wemm
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#include <sys/cdefs.h>
27__FBSDID("$FreeBSD: head/lib/libkvm/kvm_minidump_i386.c 217744 2011-01-23 11:08:28Z uqs $");
28
29/*
30 * AMD64 machine dependent routines for kvm and minidumps.
31 */
32
33#include <sys/param.h>
34#include <sys/user.h>
35#include <sys/proc.h>
36#include <sys/stat.h>
37#include <sys/mman.h>
38#include <sys/fnv_hash.h>
39#include <stdlib.h>
40#include <string.h>
41#include <unistd.h>
42#include <nlist.h>
43#include <kvm.h>
44
45#include <vm/vm.h>
46#include <vm/vm_param.h>
47
48#include <machine/elf.h>
49#include <machine/cpufunc.h>
50#include <machine/minidump.h>
51
52#include <limits.h>
53
54#include "kvm_private.h"
55
56#define PG_FRAME_PAE	(~((uint64_t)PAGE_MASK))
57
58struct hpte {
59	struct hpte *next;
60	uint64_t pa;
61	int64_t off;
62};
63
64#define HPT_SIZE 1024
65
66/* minidump must be the first item! */
67struct vmstate {
68	int minidump;		/* 1 = minidump mode */
69	struct minidumphdr hdr;
70	void *hpt_head[HPT_SIZE];
71	uint32_t *bitmap;
72	void *ptemap;
73};
74
75static void
76hpt_insert(kvm_t *kd, uint64_t pa, int64_t off)
77{
78	struct hpte *hpte;
79	uint32_t fnv = FNV1_32_INIT;
80
81	fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
82	fnv &= (HPT_SIZE - 1);
83	hpte = malloc(sizeof(*hpte));
84	hpte->pa = pa;
85	hpte->off = off;
86	hpte->next = kd->vmst->hpt_head[fnv];
87	kd->vmst->hpt_head[fnv] = hpte;
88}
89
90static int64_t
91hpt_find(kvm_t *kd, uint64_t pa)
92{
93	struct hpte *hpte;
94	uint32_t fnv = FNV1_32_INIT;
95
96	fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
97	fnv &= (HPT_SIZE - 1);
98	for (hpte = kd->vmst->hpt_head[fnv]; hpte != NULL; hpte = hpte->next) {
99		if (pa == hpte->pa)
100			return (hpte->off);
101	}
102	return (-1);
103}
104
105static int
106inithash(kvm_t *kd, uint32_t *base, int len, off_t off)
107{
108	uint64_t idx;
109	uint32_t bit, bits;
110	uint64_t pa;
111
112	for (idx = 0; idx < len / sizeof(*base); idx++) {
113		bits = base[idx];
114		while (bits) {
115			bit = bsfl(bits);
116			bits &= ~(1ul << bit);
117			pa = (idx * sizeof(*base) * NBBY + bit) * PAGE_SIZE;
118			hpt_insert(kd, pa, off);
119			off += PAGE_SIZE;
120		}
121	}
122	return (off);
123}
124
125void
126_kvm_minidump_freevtop(kvm_t *kd)
127{
128	struct vmstate *vm = kd->vmst;
129
130	if (vm->bitmap)
131		free(vm->bitmap);
132	if (vm->ptemap)
133		free(vm->ptemap);
134	free(vm);
135	kd->vmst = NULL;
136}
137
138int
139_kvm_minidump_initvtop(kvm_t *kd)
140{
141	struct vmstate *vmst;
142	off_t off;
143
144	vmst = _kvm_malloc(kd, sizeof(*vmst));
145	if (vmst == 0) {
146		_kvm_err(kd, kd->program, "cannot allocate vm");
147		return (-1);
148	}
149	kd->vmst = vmst;
150	vmst->minidump = 1;
151	if (pread(kd->pmfd, &vmst->hdr, sizeof(vmst->hdr), 0) !=
152	    sizeof(vmst->hdr)) {
153		_kvm_err(kd, kd->program, "cannot read dump header");
154		return (-1);
155	}
156	if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic, sizeof(vmst->hdr.magic)) != 0) {
157		_kvm_err(kd, kd->program, "not a minidump for this platform");
158		return (-1);
159	}
160	if (vmst->hdr.version != MINIDUMP_VERSION) {
161		_kvm_err(kd, kd->program, "wrong minidump version. expected %d got %d",
162		    MINIDUMP_VERSION, vmst->hdr.version);
163		return (-1);
164	}
165
166	/* Skip header and msgbuf */
167	off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize);
168
169	vmst->bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
170	if (vmst->bitmap == NULL) {
171		_kvm_err(kd, kd->program, "cannot allocate %d bytes for bitmap", vmst->hdr.bitmapsize);
172		return (-1);
173	}
174	if (pread(kd->pmfd, vmst->bitmap, vmst->hdr.bitmapsize, off) !=
175	    (ssize_t)vmst->hdr.bitmapsize) {
176		_kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap", vmst->hdr.bitmapsize);
177		return (-1);
178	}
179	off += round_page(vmst->hdr.bitmapsize);
180
181	vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize);
182	if (vmst->ptemap == NULL) {
183		_kvm_err(kd, kd->program, "cannot allocate %d bytes for ptemap", vmst->hdr.ptesize);
184		return (-1);
185	}
186	if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) !=
187	    (ssize_t)vmst->hdr.ptesize) {
188		_kvm_err(kd, kd->program, "cannot read %d bytes for ptemap", vmst->hdr.ptesize);
189		return (-1);
190	}
191	off += vmst->hdr.ptesize;
192
193	/* build physical address hash table for sparse pages */
194	inithash(kd, vmst->bitmap, vmst->hdr.bitmapsize, off);
195
196	return (0);
197}
198
199static int
200_kvm_minidump_vatop_pae(kvm_t *kd, u_long va, off_t *pa)
201{
202	struct vmstate *vm;
203	uint64_t offset;
204	uint64_t pte;
205	u_long pteindex;
206	uint64_t a;
207	off_t ofs;
208	uint64_t *ptemap;
209
210	vm = kd->vmst;
211	ptemap = vm->ptemap;
212	offset = va & (PAGE_SIZE - 1);
213
214	if (va >= vm->hdr.kernbase) {
215		pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
216		pte = ptemap[pteindex];
217		if ((pte & PG_V) == 0) {
218			_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
219			goto invalid;
220		}
221		a = pte & PG_FRAME_PAE;
222		ofs = hpt_find(kd, a);
223		if (ofs == -1) {
224			_kvm_err(kd, kd->program, "_kvm_vatop: physical address 0x%llx not in minidump", a);
225			goto invalid;
226		}
227		*pa = ofs + offset;
228		return (PAGE_SIZE - offset);
229	} else {
230		_kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va);
231		goto invalid;
232	}
233
234invalid:
235	_kvm_err(kd, 0, "invalid address (0x%lx)", va);
236	return (0);
237}
238
239static int
240_kvm_minidump_vatop(kvm_t *kd, u_long va, off_t *pa)
241{
242	struct vmstate *vm;
243	u_long offset;
244	pt_entry_t pte;
245	u_long pteindex;
246	u_long a;
247	off_t ofs;
248	uint32_t *ptemap;
249
250	vm = kd->vmst;
251	ptemap = vm->ptemap;
252	offset = va & (PAGE_SIZE - 1);
253
254	if (va >= vm->hdr.kernbase) {
255		pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
256		pte = ptemap[pteindex];
257		if ((pte & PG_V) == 0) {
258			_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
259			goto invalid;
260		}
261		a = pte & PG_FRAME;
262		ofs = hpt_find(kd, a);
263		if (ofs == -1) {
264			_kvm_err(kd, kd->program, "_kvm_vatop: physical address 0x%lx not in minidump", a);
265			goto invalid;
266		}
267		*pa = ofs + offset;
268		return (PAGE_SIZE - offset);
269	} else {
270		_kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va);
271		goto invalid;
272	}
273
274invalid:
275	_kvm_err(kd, 0, "invalid address (0x%lx)", va);
276	return (0);
277}
278
279int
280_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
281{
282
283	if (ISALIVE(kd)) {
284		_kvm_err(kd, 0, "kvm_kvatop called in live kernel!");
285		return (0);
286	}
287	if (kd->vmst->hdr.paemode)
288		return (_kvm_minidump_vatop_pae(kd, va, pa));
289	else
290		return (_kvm_minidump_vatop(kd, va, pa));
291}
292