kvm_minidump_i386.c revision 194186
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 194186 2009-06-14 12:42:06Z ed $");
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	u_long pa;
142	struct vmstate *vmst;
143	off_t off;
144
145	vmst = _kvm_malloc(kd, sizeof(*vmst));
146	if (vmst == 0) {
147		_kvm_err(kd, kd->program, "cannot allocate vm");
148		return (-1);
149	}
150	kd->vmst = vmst;
151	vmst->minidump = 1;
152	if (pread(kd->pmfd, &vmst->hdr, sizeof(vmst->hdr), 0) !=
153	    sizeof(vmst->hdr)) {
154		_kvm_err(kd, kd->program, "cannot read dump header");
155		return (-1);
156	}
157	if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic, sizeof(vmst->hdr.magic)) != 0) {
158		_kvm_err(kd, kd->program, "not a minidump for this platform");
159		return (-1);
160	}
161	if (vmst->hdr.version != MINIDUMP_VERSION) {
162		_kvm_err(kd, kd->program, "wrong minidump version. expected %d got %d",
163		    MINIDUMP_VERSION, vmst->hdr.version);
164		return (-1);
165	}
166
167	/* Skip header and msgbuf */
168	off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize);
169
170	vmst->bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
171	if (vmst->bitmap == NULL) {
172		_kvm_err(kd, kd->program, "cannot allocate %d bytes for bitmap", vmst->hdr.bitmapsize);
173		return (-1);
174	}
175	if (pread(kd->pmfd, vmst->bitmap, vmst->hdr.bitmapsize, off) !=
176	    vmst->hdr.bitmapsize) {
177		_kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap", vmst->hdr.bitmapsize);
178		return (-1);
179	}
180	off += round_page(vmst->hdr.bitmapsize);
181
182	vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize);
183	if (vmst->ptemap == NULL) {
184		_kvm_err(kd, kd->program, "cannot allocate %d bytes for ptemap", vmst->hdr.ptesize);
185		return (-1);
186	}
187	if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) !=
188	    vmst->hdr.ptesize) {
189		_kvm_err(kd, kd->program, "cannot read %d bytes for ptemap", vmst->hdr.ptesize);
190		return (-1);
191	}
192	off += vmst->hdr.ptesize;
193
194	/* build physical address hash table for sparse pages */
195	inithash(kd, vmst->bitmap, vmst->hdr.bitmapsize, off);
196
197	return (0);
198}
199
200static int
201_kvm_minidump_vatop_pae(kvm_t *kd, u_long va, off_t *pa)
202{
203	struct vmstate *vm;
204	uint64_t offset;
205	uint64_t pte;
206	u_long pteindex;
207	int i;
208	uint64_t a;
209	off_t ofs;
210	uint64_t *ptemap;
211
212	vm = kd->vmst;
213	ptemap = vm->ptemap;
214	offset = va & (PAGE_SIZE - 1);
215
216	if (va >= vm->hdr.kernbase) {
217		pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
218		pte = ptemap[pteindex];
219		if ((pte & PG_V) == 0) {
220			_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
221			goto invalid;
222		}
223		a = pte & PG_FRAME_PAE;
224		ofs = hpt_find(kd, a);
225		if (ofs == -1) {
226			_kvm_err(kd, kd->program, "_kvm_vatop: physical address 0x%llx not in minidump", a);
227			goto invalid;
228		}
229		*pa = ofs + offset;
230		return (PAGE_SIZE - offset);
231	} else {
232		_kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va);
233		goto invalid;
234	}
235
236invalid:
237	_kvm_err(kd, 0, "invalid address (0x%lx)", va);
238	return (0);
239}
240
241static int
242_kvm_minidump_vatop(kvm_t *kd, u_long va, off_t *pa)
243{
244	struct vmstate *vm;
245	u_long offset;
246	pt_entry_t pte;
247	u_long pteindex;
248	int i;
249	u_long a;
250	off_t ofs;
251	uint32_t *ptemap;
252
253	vm = kd->vmst;
254	ptemap = vm->ptemap;
255	offset = va & (PAGE_SIZE - 1);
256
257	if (va >= vm->hdr.kernbase) {
258		pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
259		pte = ptemap[pteindex];
260		if ((pte & PG_V) == 0) {
261			_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
262			goto invalid;
263		}
264		a = pte & PG_FRAME;
265		ofs = hpt_find(kd, a);
266		if (ofs == -1) {
267			_kvm_err(kd, kd->program, "_kvm_vatop: physical address 0x%lx not in minidump", a);
268			goto invalid;
269		}
270		*pa = ofs + offset;
271		return (PAGE_SIZE - offset);
272	} else {
273		_kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va);
274		goto invalid;
275	}
276
277invalid:
278	_kvm_err(kd, 0, "invalid address (0x%lx)", va);
279	return (0);
280}
281
282int
283_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
284{
285
286	if (ISALIVE(kd)) {
287		_kvm_err(kd, 0, "kvm_kvatop called in live kernel!");
288		return (0);
289	}
290	if (kd->vmst->hdr.paemode)
291		return (_kvm_minidump_vatop_pae(kd, va, pa));
292	else
293		return (_kvm_minidump_vatop(kd, va, pa));
294}
295