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