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: releng/10.3/lib/libkvm/kvm_minidump_mips.c 217744 2011-01-23 11:08:28Z uqs $");
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	struct vmstate *vmst;
144	off_t off;
145
146	vmst = _kvm_malloc(kd, sizeof(*vmst));
147	if (vmst == 0) {
148		_kvm_err(kd, kd->program, "cannot allocate vm");
149		return (-1);
150	}
151
152	kd->vmst = vmst;
153	vmst->minidump = 1;
154
155	off = lseek(kd->pmfd, 0, SEEK_CUR);
156	if (pread(kd->pmfd, &vmst->hdr,
157	    sizeof(vmst->hdr), 0) != sizeof(vmst->hdr)) {
158		_kvm_err(kd, kd->program, "cannot read dump header");
159		return (-1);
160	}
161
162	if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic,
163	    sizeof(vmst->hdr.magic)) != 0) {
164		_kvm_err(kd, kd->program, "not a minidump for this platform");
165		return (-1);
166	}
167	if (vmst->hdr.version != MINIDUMP_VERSION) {
168		_kvm_err(kd, kd->program, "wrong minidump version. "
169		    "Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version);
170		return (-1);
171	}
172
173	/* Skip header and msgbuf */
174	off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize);
175
176	vmst->bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
177	if (vmst->bitmap == NULL) {
178		_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
179		    "bitmap", vmst->hdr.bitmapsize);
180		return (-1);
181	}
182
183	if (pread(kd->pmfd, vmst->bitmap, vmst->hdr.bitmapsize, off) !=
184	    (ssize_t)vmst->hdr.bitmapsize) {
185		_kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap",
186		    vmst->hdr.bitmapsize);
187		return (-1);
188	}
189	off += round_page(vmst->hdr.bitmapsize);
190
191	vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize);
192	if (vmst->ptemap == NULL) {
193		_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
194		    "ptemap", vmst->hdr.ptesize);
195		return (-1);
196	}
197
198	if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) !=
199	    (ssize_t)vmst->hdr.ptesize) {
200		_kvm_err(kd, kd->program, "cannot read %d bytes for ptemap",
201		    vmst->hdr.ptesize);
202		return (-1);
203	}
204
205	off += vmst->hdr.ptesize;
206
207	/* Build physical address hash table for sparse pages */
208	inithash(kd, vmst->bitmap, vmst->hdr.bitmapsize, off);
209
210	return (0);
211}
212
213int
214_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
215{
216	struct vmstate *vm;
217	pt_entry_t pte;
218	u_long offset, pteindex, a;
219	off_t ofs;
220	pt_entry_t *ptemap;
221
222	if (ISALIVE(kd)) {
223		_kvm_err(kd, 0, "kvm_kvatop called in live kernel!");
224		return (0);
225	}
226
227 	offset = va & PAGE_MASK;
228	/* Operate with page-aligned address */
229	va &= ~PAGE_MASK;
230
231	vm = kd->vmst;
232	ptemap = vm->ptemap;
233
234#if defined(__mips_n64)
235	if (va >= MIPS_XKPHYS_START && va < MIPS_XKPHYS_END)
236		a = (MIPS_XKPHYS_TO_PHYS(va));
237	else
238#endif
239	if (va >= (u_long)MIPS_KSEG0_START && va < (u_long)MIPS_KSEG0_END)
240		a = (MIPS_KSEG0_TO_PHYS(va));
241	else if (va >= (u_long)MIPS_KSEG1_START && va < (u_long)MIPS_KSEG1_END)
242		a = (MIPS_KSEG1_TO_PHYS(va));
243	else if (va >= vm->hdr.kernbase) {
244		pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
245		pte = ptemap[pteindex];
246		if (!pte) {
247			_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
248			goto invalid;
249		}
250
251		a = TLBLO_PTE_TO_PA(pte);
252
253	} else {
254		_kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx "
255		    "not minidumped", va);
256		return (0);
257	}
258
259	ofs = hpt_find(kd, a);
260	if (ofs == -1) {
261		_kvm_err(kd, kd->program, "_kvm_vatop: physical "
262		    "address 0x%lx not in minidump", a);
263		goto invalid;
264	}
265
266	*pa = ofs + offset;
267	return (PAGE_SIZE - offset);
268
269
270invalid:
271	_kvm_err(kd, 0, "invalid address (0x%lx)", va);
272	return (0);
273}
274