1214904Sgonzo/*-
2291372Sjhb * 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: releng/11.0/lib/libkvm/kvm_minidump_mips.c 298485 2016-04-22 18:05:34Z ngie $");
32214904Sgonzo
33214904Sgonzo/*
34214904Sgonzo * MIPS machine dependent routines for kvm and minidumps.
35214904Sgonzo */
36214904Sgonzo
37214904Sgonzo#include <sys/param.h>
38291406Sjhb#include <kvm.h>
39291406Sjhb#include <limits.h>
40291406Sjhb#include <stdint.h>
41214904Sgonzo#include <stdlib.h>
42214904Sgonzo#include <string.h>
43214904Sgonzo#include <unistd.h>
44214904Sgonzo
45291406Sjhb#include "../../sys/mips/include/cpuregs.h"
46291406Sjhb#include "../../sys/mips/include/minidump.h"
47214904Sgonzo
48214904Sgonzo#include "kvm_private.h"
49291406Sjhb#include "kvm_mips.h"
50214904Sgonzo
51291406Sjhb#define	mips_round_page(x)	roundup2((kvaddr_t)(x), MIPS_PAGE_SIZE)
52214904Sgonzo
53214904Sgonzostruct vmstate {
54214904Sgonzo	struct		minidumphdr hdr;
55291406Sjhb	struct		hpt hpt;
56214904Sgonzo	void		*ptemap;
57291406Sjhb	int		pte_size;
58214904Sgonzo};
59214904Sgonzo
60214904Sgonzostatic int
61291406Sjhb_mips_minidump_probe(kvm_t *kd)
62214904Sgonzo{
63214904Sgonzo
64291406Sjhb	if (kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS32 &&
65291406Sjhb	    kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS64)
66291406Sjhb		return (0);
67291406Sjhb	if (kd->nlehdr.e_machine != EM_MIPS)
68291406Sjhb		return (0);
69291406Sjhb	return (_kvm_is_minidump(kd));
70214904Sgonzo}
71214904Sgonzo
72291406Sjhbstatic void
73291406Sjhb_mips_minidump_freevtop(kvm_t *kd)
74214904Sgonzo{
75214904Sgonzo	struct vmstate *vm = kd->vmst;
76214904Sgonzo
77291406Sjhb	_kvm_hpt_free(&vm->hpt);
78214904Sgonzo	if (vm->ptemap)
79214904Sgonzo		free(vm->ptemap);
80214904Sgonzo	free(vm);
81214904Sgonzo	kd->vmst = NULL;
82214904Sgonzo}
83214904Sgonzo
84291406Sjhbstatic int
85291406Sjhb_mips_minidump_initvtop(kvm_t *kd)
86214904Sgonzo{
87214904Sgonzo	struct vmstate *vmst;
88291406Sjhb	uint32_t *bitmap;
89214904Sgonzo	off_t off;
90214904Sgonzo
91214904Sgonzo	vmst = _kvm_malloc(kd, sizeof(*vmst));
92298485Sngie	if (vmst == NULL) {
93214904Sgonzo		_kvm_err(kd, kd->program, "cannot allocate vm");
94214904Sgonzo		return (-1);
95214904Sgonzo	}
96214904Sgonzo
97214904Sgonzo	kd->vmst = vmst;
98214904Sgonzo
99291406Sjhb	if (kd->nlehdr.e_ident[EI_CLASS] == ELFCLASS64 ||
100291406Sjhb	    kd->nlehdr.e_flags & EF_MIPS_ABI2)
101291406Sjhb		vmst->pte_size = 64;
102291406Sjhb	else
103291406Sjhb		vmst->pte_size = 32;
104291406Sjhb
105214904Sgonzo	if (pread(kd->pmfd, &vmst->hdr,
106214904Sgonzo	    sizeof(vmst->hdr), 0) != sizeof(vmst->hdr)) {
107214904Sgonzo		_kvm_err(kd, kd->program, "cannot read dump header");
108214904Sgonzo		return (-1);
109214904Sgonzo	}
110214904Sgonzo
111214904Sgonzo	if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic,
112214904Sgonzo	    sizeof(vmst->hdr.magic)) != 0) {
113214904Sgonzo		_kvm_err(kd, kd->program, "not a minidump for this platform");
114214904Sgonzo		return (-1);
115214904Sgonzo	}
116291406Sjhb	vmst->hdr.version = _kvm32toh(kd, vmst->hdr.version);
117214904Sgonzo	if (vmst->hdr.version != MINIDUMP_VERSION) {
118214904Sgonzo		_kvm_err(kd, kd->program, "wrong minidump version. "
119214904Sgonzo		    "Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version);
120214904Sgonzo		return (-1);
121214904Sgonzo	}
122291406Sjhb	vmst->hdr.msgbufsize = _kvm32toh(kd, vmst->hdr.msgbufsize);
123291406Sjhb	vmst->hdr.bitmapsize = _kvm32toh(kd, vmst->hdr.bitmapsize);
124291406Sjhb	vmst->hdr.ptesize = _kvm32toh(kd, vmst->hdr.ptesize);
125291406Sjhb	vmst->hdr.kernbase = _kvm64toh(kd, vmst->hdr.kernbase);
126291406Sjhb	vmst->hdr.dmapbase = _kvm64toh(kd, vmst->hdr.dmapbase);
127291406Sjhb	vmst->hdr.dmapend = _kvm64toh(kd, vmst->hdr.dmapend);
128214904Sgonzo
129214904Sgonzo	/* Skip header and msgbuf */
130291406Sjhb	off = MIPS_PAGE_SIZE + mips_round_page(vmst->hdr.msgbufsize);
131214904Sgonzo
132291406Sjhb	bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
133291406Sjhb	if (bitmap == NULL) {
134214904Sgonzo		_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
135214904Sgonzo		    "bitmap", vmst->hdr.bitmapsize);
136214904Sgonzo		return (-1);
137214904Sgonzo	}
138214904Sgonzo
139291406Sjhb	if (pread(kd->pmfd, bitmap, vmst->hdr.bitmapsize, off) !=
140217744Suqs	    (ssize_t)vmst->hdr.bitmapsize) {
141214904Sgonzo		_kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap",
142214904Sgonzo		    vmst->hdr.bitmapsize);
143291406Sjhb		free(bitmap);
144214904Sgonzo		return (-1);
145214904Sgonzo	}
146291406Sjhb	off += mips_round_page(vmst->hdr.bitmapsize);
147214904Sgonzo
148214904Sgonzo	vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize);
149214904Sgonzo	if (vmst->ptemap == NULL) {
150214904Sgonzo		_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
151214904Sgonzo		    "ptemap", vmst->hdr.ptesize);
152291406Sjhb		free(bitmap);
153214904Sgonzo		return (-1);
154214904Sgonzo	}
155214904Sgonzo
156214904Sgonzo	if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) !=
157217744Suqs	    (ssize_t)vmst->hdr.ptesize) {
158214904Sgonzo		_kvm_err(kd, kd->program, "cannot read %d bytes for ptemap",
159214904Sgonzo		    vmst->hdr.ptesize);
160291406Sjhb		free(bitmap);
161214904Sgonzo		return (-1);
162214904Sgonzo	}
163214904Sgonzo
164214904Sgonzo	off += vmst->hdr.ptesize;
165214904Sgonzo
166214904Sgonzo	/* Build physical address hash table for sparse pages */
167291406Sjhb	_kvm_hpt_init(kd, &vmst->hpt, bitmap, vmst->hdr.bitmapsize, off,
168291406Sjhb	    MIPS_PAGE_SIZE, sizeof(*bitmap));
169291406Sjhb	free(bitmap);
170214904Sgonzo
171214904Sgonzo	return (0);
172214904Sgonzo}
173214904Sgonzo
174291406Sjhbstatic int
175291406Sjhb_mips_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
176214904Sgonzo{
177214904Sgonzo	struct vmstate *vm;
178291406Sjhb	uint64_t pte;
179291406Sjhb	mips_physaddr_t offset, a;
180291406Sjhb	kvaddr_t pteindex;
181214904Sgonzo	off_t ofs;
182291406Sjhb	uint32_t *ptemap32;
183291406Sjhb	uint64_t *ptemap64;
184214904Sgonzo
185214904Sgonzo	if (ISALIVE(kd)) {
186291406Sjhb		_kvm_err(kd, 0, "_mips_minidump_kvatop called in live kernel!");
187214904Sgonzo		return (0);
188214904Sgonzo	}
189214904Sgonzo
190291406Sjhb	offset = va & MIPS_PAGE_MASK;
191214904Sgonzo	/* Operate with page-aligned address */
192291406Sjhb	va &= ~MIPS_PAGE_MASK;
193214904Sgonzo
194214904Sgonzo	vm = kd->vmst;
195291406Sjhb	ptemap32 = vm->ptemap;
196291406Sjhb	ptemap64 = vm->ptemap;
197214904Sgonzo
198291406Sjhb	if (kd->nlehdr.e_ident[EI_CLASS] == ELFCLASS64) {
199291406Sjhb		if (va >= MIPS_XKPHYS_START && va < MIPS_XKPHYS_END) {
200291406Sjhb			a = va & MIPS_XKPHYS_PHYS_MASK;
201291406Sjhb			goto found;
202291406Sjhb		}
203291406Sjhb		if (va >= MIPS64_KSEG0_START && va < MIPS64_KSEG0_END) {
204291406Sjhb			a = va & MIPS_KSEG0_PHYS_MASK;
205291406Sjhb			goto found;
206291406Sjhb		}
207291406Sjhb		if (va >= MIPS64_KSEG1_START && va < MIPS64_KSEG1_END) {
208291406Sjhb			a = va & MIPS_KSEG0_PHYS_MASK;
209291406Sjhb			goto found;
210291406Sjhb		}
211291406Sjhb	} else {
212291406Sjhb		if (va >= MIPS32_KSEG0_START && va < MIPS32_KSEG0_END) {
213291406Sjhb			a = va & MIPS_KSEG0_PHYS_MASK;
214291406Sjhb			goto found;
215291406Sjhb		}
216291406Sjhb		if (va >= MIPS32_KSEG1_START && va < MIPS32_KSEG1_END) {
217291406Sjhb			a = va & MIPS_KSEG0_PHYS_MASK;
218291406Sjhb			goto found;
219291406Sjhb		}
220291406Sjhb	}
221291406Sjhb	if (va >= vm->hdr.kernbase) {
222291406Sjhb		pteindex = (va - vm->hdr.kernbase) >> MIPS_PAGE_SHIFT;
223291406Sjhb		if (vm->pte_size == 64) {
224291406Sjhb			pte = _kvm64toh(kd, ptemap64[pteindex]);
225291406Sjhb			a = MIPS64_PTE_TO_PA(pte);
226291406Sjhb		} else {
227291406Sjhb			pte = _kvm32toh(kd, ptemap32[pteindex]);
228291406Sjhb			a = MIPS32_PTE_TO_PA(pte);
229291406Sjhb		}
230214904Sgonzo		if (!pte) {
231291406Sjhb			_kvm_err(kd, kd->program, "_mips_minidump_kvatop: pte "
232291406Sjhb			    "not valid");
233214904Sgonzo			goto invalid;
234214904Sgonzo		}
235214904Sgonzo	} else {
236291406Sjhb		_kvm_err(kd, kd->program, "_mips_minidump_kvatop: virtual "
237291406Sjhb		    "address 0x%jx not minidumped", (uintmax_t)va);
238214904Sgonzo		return (0);
239214904Sgonzo	}
240214904Sgonzo
241291406Sjhbfound:
242291406Sjhb	ofs = _kvm_hpt_find(&vm->hpt, a);
243214904Sgonzo	if (ofs == -1) {
244291406Sjhb		_kvm_err(kd, kd->program, "_mips_minidump_kvatop: physical "
245291406Sjhb		    "address 0x%jx not in minidump", (uintmax_t)a);
246214904Sgonzo		goto invalid;
247214904Sgonzo	}
248214904Sgonzo
249214904Sgonzo	*pa = ofs + offset;
250291406Sjhb	return (MIPS_PAGE_SIZE - offset);
251214904Sgonzo
252214904Sgonzo
253214904Sgonzoinvalid:
254291406Sjhb	_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
255214904Sgonzo	return (0);
256214904Sgonzo}
257291406Sjhb
258291406Sjhbstatic int
259291406Sjhb_mips_native(kvm_t *kd)
260291406Sjhb{
261291406Sjhb
262291406Sjhb#ifdef __mips__
263291406Sjhb#ifdef __mips_n64
264291406Sjhb	if (kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS64)
265291406Sjhb		return (0);
266291406Sjhb#else
267291406Sjhb	if (kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS32)
268291406Sjhb		return (0);
269291406Sjhb#ifdef __mips_n32
270291406Sjhb	if (!(kd->nlehdr.e_flags & EF_MIPS_ABI2))
271291406Sjhb		return (0);
272291406Sjhb#else
273291406Sjhb	if (kd->nlehdr.e_flags & EF_MIPS_ABI2)
274291406Sjhb		return (0);
275291406Sjhb#endif
276291406Sjhb#endif
277291406Sjhb#if _BYTE_ORDER == _LITTLE_ENDIAN
278291406Sjhb	return (kd->nlehdr.e_ident[EI_DATA] == ELFDATA2LSB);
279291406Sjhb#else
280291406Sjhb	return (kd->nlehdr.e_ident[EI_DATA] == ELFDATA2MSB);
281291406Sjhb#endif
282291406Sjhb#else
283291406Sjhb	return (0);
284291406Sjhb#endif
285291406Sjhb}
286291406Sjhb
287291406Sjhbstruct kvm_arch kvm_mips_minidump = {
288291406Sjhb	.ka_probe = _mips_minidump_probe,
289291406Sjhb	.ka_initvtop = _mips_minidump_initvtop,
290291406Sjhb	.ka_freevtop = _mips_minidump_freevtop,
291291406Sjhb	.ka_kvatop = _mips_minidump_kvatop,
292291406Sjhb	.ka_native = _mips_native,
293291406Sjhb};
294291406Sjhb
295291406SjhbKVM_ARCH(kvm_mips_minidump);
296