1214904Sgonzo/*-
2214904Sgonzo * Copyright (c) 2010 Oleksandr Tymoshenko
3214904Sgonzo * Copyright (c) 2008 Semihalf, Grzegorz Bernacki
4214904Sgonzo * Copyright (c) 2006 Peter Wemm
5214904Sgonzo *
6214904Sgonzo * Redistribution and use in source and binary forms, with or without
7214904Sgonzo * modification, are permitted provided that the following conditions
8214904Sgonzo * are met:
9214904Sgonzo * 1. Redistributions of source code must retain the above copyright
10214904Sgonzo *    notice, this list of conditions and the following disclaimer.
11214904Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
12214904Sgonzo *    notice, this list of conditions and the following disclaimer in the
13214904Sgonzo *    documentation and/or other materials provided with the distribution.
14214904Sgonzo *
15214904Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16214904Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17214904Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18214904Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19214904Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20214904Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21214904Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22214904Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23214904Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24214904Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25214904Sgonzo * SUCH DAMAGE.
26214904Sgonzo *
27214904Sgonzo * From: FreeBSD: src/lib/libkvm/kvm_minidump_arm.c r214223
28214904Sgonzo */
29214904Sgonzo
30214904Sgonzo#include <sys/cdefs.h>
31214904Sgonzo__FBSDID("$FreeBSD$");
32214904Sgonzo
33214904Sgonzo/*
34214904Sgonzo * MIPS machine dependent routines for kvm and minidumps.
35214904Sgonzo */
36214904Sgonzo
37214904Sgonzo#include <sys/param.h>
38214904Sgonzo#include <sys/user.h>
39214904Sgonzo#include <sys/proc.h>
40214904Sgonzo#include <sys/stat.h>
41214904Sgonzo#include <sys/mman.h>
42214904Sgonzo#include <sys/fnv_hash.h>
43214904Sgonzo#include <stdlib.h>
44214904Sgonzo#include <string.h>
45214904Sgonzo#include <unistd.h>
46214904Sgonzo#include <nlist.h>
47214904Sgonzo#include <kvm.h>
48214904Sgonzo
49214904Sgonzo#include <vm/vm.h>
50214904Sgonzo#include <vm/vm_param.h>
51214904Sgonzo
52214904Sgonzo#include <machine/elf.h>
53214904Sgonzo#include <machine/cpufunc.h>
54214904Sgonzo#include <machine/minidump.h>
55214904Sgonzo
56214904Sgonzo#include <limits.h>
57214904Sgonzo
58214904Sgonzo#include "kvm_private.h"
59214904Sgonzo
60214904Sgonzostruct hpte {
61214904Sgonzo	struct hpte	*next;
62214904Sgonzo	uint64_t	pa;
63214904Sgonzo	int64_t		off;
64214904Sgonzo};
65214904Sgonzo
66214904Sgonzo#define HPT_SIZE 1024
67214904Sgonzo
68214904Sgonzo/* minidump must be the first field */
69214904Sgonzostruct vmstate {
70214904Sgonzo	int		minidump;		/* 1 = minidump mode */
71214904Sgonzo	struct		minidumphdr hdr;
72214904Sgonzo	void		*hpt_head[HPT_SIZE];
73214904Sgonzo	uint32_t	*bitmap;
74214904Sgonzo	void		*ptemap;
75214904Sgonzo};
76214904Sgonzo
77214904Sgonzostatic void
78214904Sgonzohpt_insert(kvm_t *kd, uint64_t pa, int64_t off)
79214904Sgonzo{
80214904Sgonzo	struct hpte *hpte;
81214904Sgonzo	uint32_t fnv = FNV1_32_INIT;
82214904Sgonzo
83214904Sgonzo	fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
84214904Sgonzo	fnv &= (HPT_SIZE - 1);
85214904Sgonzo	hpte = malloc(sizeof(*hpte));
86214904Sgonzo	hpte->pa = pa;
87214904Sgonzo	hpte->off = off;
88214904Sgonzo	hpte->next = kd->vmst->hpt_head[fnv];
89214904Sgonzo	kd->vmst->hpt_head[fnv] = hpte;
90214904Sgonzo}
91214904Sgonzo
92214904Sgonzostatic int64_t
93214904Sgonzohpt_find(kvm_t *kd, uint64_t pa)
94214904Sgonzo{
95214904Sgonzo	struct hpte *hpte;
96214904Sgonzo	uint32_t fnv = FNV1_32_INIT;
97214904Sgonzo
98214904Sgonzo	fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
99214904Sgonzo	fnv &= (HPT_SIZE - 1);
100214904Sgonzo	for (hpte = kd->vmst->hpt_head[fnv]; hpte != NULL; hpte = hpte->next)
101214904Sgonzo		if (pa == hpte->pa)
102214904Sgonzo			return (hpte->off);
103214904Sgonzo
104214904Sgonzo	return (-1);
105214904Sgonzo}
106214904Sgonzo
107214904Sgonzostatic int
108214904Sgonzoinithash(kvm_t *kd, uint32_t *base, int len, off_t off)
109214904Sgonzo{
110214904Sgonzo	uint64_t idx, pa;
111214904Sgonzo	uint32_t bit, bits;
112214904Sgonzo
113214904Sgonzo	for (idx = 0; idx < len / sizeof(*base); idx++) {
114214904Sgonzo		bits = base[idx];
115214904Sgonzo		while (bits) {
116214904Sgonzo			bit = ffs(bits) - 1;
117214904Sgonzo			bits &= ~(1ul << bit);
118214904Sgonzo			pa = (idx * sizeof(*base) * NBBY + bit) * PAGE_SIZE;
119214904Sgonzo			hpt_insert(kd, pa, off);
120214904Sgonzo			off += PAGE_SIZE;
121214904Sgonzo		}
122214904Sgonzo	}
123214904Sgonzo
124214904Sgonzo	return (off);
125214904Sgonzo}
126214904Sgonzo
127214904Sgonzovoid
128214904Sgonzo_kvm_minidump_freevtop(kvm_t *kd)
129214904Sgonzo{
130214904Sgonzo	struct vmstate *vm = kd->vmst;
131214904Sgonzo
132214904Sgonzo	if (vm->bitmap)
133214904Sgonzo		free(vm->bitmap);
134214904Sgonzo	if (vm->ptemap)
135214904Sgonzo		free(vm->ptemap);
136214904Sgonzo	free(vm);
137214904Sgonzo	kd->vmst = NULL;
138214904Sgonzo}
139214904Sgonzo
140214904Sgonzoint
141214904Sgonzo_kvm_minidump_initvtop(kvm_t *kd)
142214904Sgonzo{
143214904Sgonzo	struct vmstate *vmst;
144214904Sgonzo	off_t off;
145214904Sgonzo
146214904Sgonzo	vmst = _kvm_malloc(kd, sizeof(*vmst));
147214904Sgonzo	if (vmst == 0) {
148214904Sgonzo		_kvm_err(kd, kd->program, "cannot allocate vm");
149214904Sgonzo		return (-1);
150214904Sgonzo	}
151214904Sgonzo
152214904Sgonzo	kd->vmst = vmst;
153214904Sgonzo	vmst->minidump = 1;
154214904Sgonzo
155214904Sgonzo	off = lseek(kd->pmfd, 0, SEEK_CUR);
156214904Sgonzo	if (pread(kd->pmfd, &vmst->hdr,
157214904Sgonzo	    sizeof(vmst->hdr), 0) != sizeof(vmst->hdr)) {
158214904Sgonzo		_kvm_err(kd, kd->program, "cannot read dump header");
159214904Sgonzo		return (-1);
160214904Sgonzo	}
161214904Sgonzo
162214904Sgonzo	if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic,
163214904Sgonzo	    sizeof(vmst->hdr.magic)) != 0) {
164214904Sgonzo		_kvm_err(kd, kd->program, "not a minidump for this platform");
165214904Sgonzo		return (-1);
166214904Sgonzo	}
167214904Sgonzo	if (vmst->hdr.version != MINIDUMP_VERSION) {
168214904Sgonzo		_kvm_err(kd, kd->program, "wrong minidump version. "
169214904Sgonzo		    "Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version);
170214904Sgonzo		return (-1);
171214904Sgonzo	}
172214904Sgonzo
173214904Sgonzo	/* Skip header and msgbuf */
174214904Sgonzo	off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize);
175214904Sgonzo
176214904Sgonzo	vmst->bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
177214904Sgonzo	if (vmst->bitmap == NULL) {
178214904Sgonzo		_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
179214904Sgonzo		    "bitmap", vmst->hdr.bitmapsize);
180214904Sgonzo		return (-1);
181214904Sgonzo	}
182214904Sgonzo
183214904Sgonzo	if (pread(kd->pmfd, vmst->bitmap, vmst->hdr.bitmapsize, off) !=
184217744Suqs	    (ssize_t)vmst->hdr.bitmapsize) {
185214904Sgonzo		_kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap",
186214904Sgonzo		    vmst->hdr.bitmapsize);
187214904Sgonzo		return (-1);
188214904Sgonzo	}
189214904Sgonzo	off += round_page(vmst->hdr.bitmapsize);
190214904Sgonzo
191214904Sgonzo	vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize);
192214904Sgonzo	if (vmst->ptemap == NULL) {
193214904Sgonzo		_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
194214904Sgonzo		    "ptemap", vmst->hdr.ptesize);
195214904Sgonzo		return (-1);
196214904Sgonzo	}
197214904Sgonzo
198214904Sgonzo	if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) !=
199217744Suqs	    (ssize_t)vmst->hdr.ptesize) {
200214904Sgonzo		_kvm_err(kd, kd->program, "cannot read %d bytes for ptemap",
201214904Sgonzo		    vmst->hdr.ptesize);
202214904Sgonzo		return (-1);
203214904Sgonzo	}
204214904Sgonzo
205214904Sgonzo	off += vmst->hdr.ptesize;
206214904Sgonzo
207214904Sgonzo	/* Build physical address hash table for sparse pages */
208214904Sgonzo	inithash(kd, vmst->bitmap, vmst->hdr.bitmapsize, off);
209214904Sgonzo
210214904Sgonzo	return (0);
211214904Sgonzo}
212214904Sgonzo
213214904Sgonzoint
214214904Sgonzo_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
215214904Sgonzo{
216214904Sgonzo	struct vmstate *vm;
217214904Sgonzo	pt_entry_t pte;
218214904Sgonzo	u_long offset, pteindex, a;
219214904Sgonzo	off_t ofs;
220214904Sgonzo	pt_entry_t *ptemap;
221214904Sgonzo
222214904Sgonzo	if (ISALIVE(kd)) {
223214904Sgonzo		_kvm_err(kd, 0, "kvm_kvatop called in live kernel!");
224214904Sgonzo		return (0);
225214904Sgonzo	}
226214904Sgonzo
227214904Sgonzo 	offset = va & PAGE_MASK;
228214904Sgonzo	/* Operate with page-aligned address */
229214904Sgonzo	va &= ~PAGE_MASK;
230214904Sgonzo
231214904Sgonzo	vm = kd->vmst;
232214904Sgonzo	ptemap = vm->ptemap;
233214904Sgonzo
234214904Sgonzo#if defined(__mips_n64)
235214904Sgonzo	if (va >= MIPS_XKPHYS_START && va < MIPS_XKPHYS_END)
236214904Sgonzo		a = (MIPS_XKPHYS_TO_PHYS(va));
237214904Sgonzo	else
238214904Sgonzo#endif
239217744Suqs	if (va >= (u_long)MIPS_KSEG0_START && va < (u_long)MIPS_KSEG0_END)
240214904Sgonzo		a = (MIPS_KSEG0_TO_PHYS(va));
241217744Suqs	else if (va >= (u_long)MIPS_KSEG1_START && va < (u_long)MIPS_KSEG1_END)
242214904Sgonzo		a = (MIPS_KSEG1_TO_PHYS(va));
243214904Sgonzo	else if (va >= vm->hdr.kernbase) {
244214904Sgonzo		pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
245214904Sgonzo		pte = ptemap[pteindex];
246214904Sgonzo		if (!pte) {
247214904Sgonzo			_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
248214904Sgonzo			goto invalid;
249214904Sgonzo		}
250214904Sgonzo
251214904Sgonzo		a = TLBLO_PTE_TO_PA(pte);
252214904Sgonzo
253214904Sgonzo	} else {
254214904Sgonzo		_kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx "
255214904Sgonzo		    "not minidumped", va);
256214904Sgonzo		return (0);
257214904Sgonzo	}
258214904Sgonzo
259214904Sgonzo	ofs = hpt_find(kd, a);
260214904Sgonzo	if (ofs == -1) {
261214904Sgonzo		_kvm_err(kd, kd->program, "_kvm_vatop: physical "
262214904Sgonzo		    "address 0x%lx not in minidump", a);
263214904Sgonzo		goto invalid;
264214904Sgonzo	}
265214904Sgonzo
266214904Sgonzo	*pa = ofs + offset;
267214904Sgonzo	return (PAGE_SIZE - offset);
268214904Sgonzo
269214904Sgonzo
270214904Sgonzoinvalid:
271214904Sgonzo	_kvm_err(kd, 0, "invalid address (0x%lx)", va);
272214904Sgonzo	return (0);
273214904Sgonzo}
274