kvm_minidump_i386.c revision 157911
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 157911 2006-04-21 04:32:51Z peter $");
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 <unistd.h>
41#include <nlist.h>
42#include <kvm.h>
43
44#include <vm/vm.h>
45#include <vm/vm_param.h>
46
47#include <machine/elf.h>
48#include <machine/cpufunc.h>
49#include <machine/minidump.h>
50
51#include <limits.h>
52
53#include "kvm_private.h"
54
55#define PG_FRAME_PAE	(~((uint64_t)PAGE_MASK))
56
57struct hpte {
58	struct hpte *next;
59	uint64_t pa;
60	int64_t off;
61};
62
63#define HPT_SIZE 1024
64
65/* minidump must be the first item! */
66struct vmstate {
67	int minidump;		/* 1 = minidump mode */
68	struct minidumphdr hdr;
69	void *hpt_head[HPT_SIZE];
70	uint32_t *bitmap;
71	void *ptemap;
72};
73
74static void
75hpt_insert(kvm_t *kd, uint64_t pa, int64_t off)
76{
77	struct hpte *hpte;
78	uint32_t fnv = FNV1_32_INIT;
79
80	fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
81	fnv &= (HPT_SIZE - 1);
82	hpte = malloc(sizeof(*hpte));
83	hpte->pa = pa;
84	hpte->off = off;
85	hpte->next = kd->vmst->hpt_head[fnv];
86	kd->vmst->hpt_head[fnv] = hpte;
87}
88
89static int64_t
90hpt_find(kvm_t *kd, uint64_t pa)
91{
92	struct hpte *hpte;
93	uint32_t fnv = FNV1_32_INIT;
94
95	fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
96	fnv &= (HPT_SIZE - 1);
97	for (hpte = kd->vmst->hpt_head[fnv]; hpte != NULL; hpte = hpte->next) {
98		if (pa == hpte->pa)
99			return (hpte->off);
100	}
101	return (-1);
102}
103
104static int
105inithash(kvm_t *kd, uint32_t *base, int len, off_t off)
106{
107	uint64_t idx;
108	uint32_t bit, bits;
109	uint64_t pa;
110
111	for (idx = 0; idx < len / sizeof(*base); idx++) {
112		bits = base[idx];
113		while (bits) {
114			bit = bsfl(bits);
115			bits &= ~(1ul << bit);
116			pa = (idx * sizeof(*base) * NBBY + bit) * PAGE_SIZE;
117			hpt_insert(kd, pa, off);
118			off += PAGE_SIZE;
119		}
120	}
121	return (off);
122}
123
124void
125_kvm_minidump_freevtop(kvm_t *kd)
126{
127	struct vmstate *vm = kd->vmst;
128
129	if (vm->bitmap)
130		free(vm->bitmap);
131	if (vm->ptemap)
132		free(vm->ptemap);
133	free(vm);
134	kd->vmst = NULL;
135}
136
137int
138_kvm_minidump_initvtop(kvm_t *kd)
139{
140	u_long pa;
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	bzero(vmst, sizeof(*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