kvm_minidump_amd64.c revision 159246
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_amd64.c 159246 2006-06-05 08:51:14Z delphij $");
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 <strings.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
56struct hpte {
57	struct hpte *next;
58	vm_paddr_t pa;
59	int64_t off;
60};
61
62#define HPT_SIZE 1024
63
64/* minidump must be the first item! */
65struct vmstate {
66	int minidump;		/* 1 = minidump mode */
67	struct minidumphdr hdr;
68	void *hpt_head[HPT_SIZE];
69	uint64_t *bitmap;
70	uint64_t *ptemap;
71};
72
73static void
74hpt_insert(kvm_t *kd, vm_paddr_t pa, int64_t off)
75{
76	struct hpte *hpte;
77	uint32_t fnv = FNV1_32_INIT;
78
79	fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
80	fnv &= (HPT_SIZE - 1);
81	hpte = malloc(sizeof(*hpte));
82	hpte->pa = pa;
83	hpte->off = off;
84	hpte->next = kd->vmst->hpt_head[fnv];
85	kd->vmst->hpt_head[fnv] = hpte;
86}
87
88static int64_t
89hpt_find(kvm_t *kd, vm_paddr_t pa)
90{
91	struct hpte *hpte;
92	uint32_t fnv = FNV1_32_INIT;
93
94	fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
95	fnv &= (HPT_SIZE - 1);
96	for (hpte = kd->vmst->hpt_head[fnv]; hpte != NULL; hpte = hpte->next) {
97		if (pa == hpte->pa)
98			return (hpte->off);
99	}
100	return (-1);
101}
102
103static int
104inithash(kvm_t *kd, uint64_t *base, int len, off_t off)
105{
106	uint64_t idx;
107	uint64_t bit, bits;
108	vm_paddr_t pa;
109
110	for (idx = 0; idx < len / sizeof(*base); idx++) {
111		bits = base[idx];
112		while (bits) {
113			bit = bsfq(bits);
114			bits &= ~(1ul << bit);
115			pa = (idx * sizeof(*base) * NBBY + bit) * PAGE_SIZE;
116			hpt_insert(kd, pa, off);
117			off += PAGE_SIZE;
118		}
119	}
120	return (off);
121}
122
123void
124_kvm_minidump_freevtop(kvm_t *kd)
125{
126	struct vmstate *vm = kd->vmst;
127
128	if (vm->bitmap)
129		free(vm->bitmap);
130	if (vm->ptemap)
131		free(vm->ptemap);
132	free(vm);
133	kd->vmst = NULL;
134}
135
136int
137_kvm_minidump_initvtop(kvm_t *kd)
138{
139	u_long pa;
140	struct vmstate *vmst;
141	off_t off;
142
143	vmst = _kvm_malloc(kd, sizeof(*vmst));
144	if (vmst == 0) {
145		_kvm_err(kd, kd->program, "cannot allocate vm");
146		return (-1);
147	}
148	kd->vmst = vmst;
149	bzero(vmst, sizeof(*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	    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	    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(kvm_t *kd, u_long va, off_t *pa)
201{
202	struct vmstate *vm;
203	u_long offset;
204	pt_entry_t pte;
205	u_long pteindex;
206	int i;
207	u_long a;
208	off_t ofs;
209
210	vm = kd->vmst;
211	offset = va & (PAGE_SIZE - 1);
212
213	if (va >= vm->hdr.kernbase) {
214		pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
215		pte = vm->ptemap[pteindex];
216		if (((u_long)pte & PG_V) == 0) {
217			_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
218			goto invalid;
219		}
220		a = pte & PG_FRAME;
221		ofs = hpt_find(kd, a);
222		if (ofs == -1) {
223			_kvm_err(kd, kd->program, "_kvm_vatop: physical address 0x%lx not in minidump", a);
224			goto invalid;
225		}
226		*pa = ofs + offset;
227		return (PAGE_SIZE - offset);
228	} else if (va >= vm->hdr.dmapbase && va < vm->hdr.dmapend) {
229		a = (va - vm->hdr.dmapbase) & ~PAGE_MASK;
230		ofs = hpt_find(kd, a);
231		if (ofs == -1) {
232			_kvm_err(kd, kd->program, "_kvm_vatop: direct map address 0x%lx not in minidump", va);
233			goto invalid;
234		}
235		*pa = ofs + offset;
236		return (PAGE_SIZE - offset);
237	} else {
238		_kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va);
239		goto invalid;
240	}
241
242invalid:
243	_kvm_err(kd, 0, "invalid address (0x%lx)", va);
244	return (0);
245}
246
247int
248_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
249{
250
251	if (ISALIVE(kd)) {
252		_kvm_err(kd, 0, "kvm_kvatop called in live kernel!");
253		return (0);
254	}
255	return (_kvm_minidump_vatop(kd, va, pa));
256}
257