xref: /NextBSD/lib/libkvm/kvm_minidump_mips.c (revision 4557fabb34e865d7f40be64b39c9e34fa41dbb60)
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$");
32 
33 /*
34  * MIPS machine dependent routines for kvm and minidumps.
35  */
36 
37 #include <sys/param.h>
38 #include <kvm.h>
39 #include <limits.h>
40 #include <stdint.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 
45 #include "../../sys/mips/include/cpuregs.h"
46 #include "../../sys/mips/include/minidump.h"
47 
48 #include "kvm_private.h"
49 #include "kvm_mips.h"
50 
51 #define	mips_round_page(x)	roundup2((kvaddr_t)(x), MIPS_PAGE_SIZE)
52 
53 struct vmstate {
54 	struct		minidumphdr hdr;
55 	struct		hpt hpt;
56 	void		*ptemap;
57 	int		pte_size;
58 };
59 
60 static int
_mips_minidump_probe(kvm_t * kd)61 _mips_minidump_probe(kvm_t *kd)
62 {
63 
64 	if (kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS32 &&
65 	    kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS64)
66 		return (0);
67 	if (kd->nlehdr.e_machine != EM_MIPS)
68 		return (0);
69 	return (_kvm_is_minidump(kd));
70 }
71 
72 static void
_mips_minidump_freevtop(kvm_t * kd)73 _mips_minidump_freevtop(kvm_t *kd)
74 {
75 	struct vmstate *vm = kd->vmst;
76 
77 	_kvm_hpt_free(&vm->hpt);
78 	if (vm->ptemap)
79 		free(vm->ptemap);
80 	free(vm);
81 	kd->vmst = NULL;
82 }
83 
84 static int
_mips_minidump_initvtop(kvm_t * kd)85 _mips_minidump_initvtop(kvm_t *kd)
86 {
87 	struct vmstate *vmst;
88 	uint32_t *bitmap;
89 	off_t off;
90 
91 	vmst = _kvm_malloc(kd, sizeof(*vmst));
92 	if (vmst == 0) {
93 		_kvm_err(kd, kd->program, "cannot allocate vm");
94 		return (-1);
95 	}
96 
97 	kd->vmst = vmst;
98 
99 	if (kd->nlehdr.e_ident[EI_CLASS] == ELFCLASS64 ||
100 	    kd->nlehdr.e_flags & EF_MIPS_ABI2)
101 		vmst->pte_size = 64;
102 	else
103 		vmst->pte_size = 32;
104 
105 	if (pread(kd->pmfd, &vmst->hdr,
106 	    sizeof(vmst->hdr), 0) != sizeof(vmst->hdr)) {
107 		_kvm_err(kd, kd->program, "cannot read dump header");
108 		return (-1);
109 	}
110 
111 	if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic,
112 	    sizeof(vmst->hdr.magic)) != 0) {
113 		_kvm_err(kd, kd->program, "not a minidump for this platform");
114 		return (-1);
115 	}
116 	vmst->hdr.version = _kvm32toh(kd, vmst->hdr.version);
117 	if (vmst->hdr.version != MINIDUMP_VERSION) {
118 		_kvm_err(kd, kd->program, "wrong minidump version. "
119 		    "Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version);
120 		return (-1);
121 	}
122 	vmst->hdr.msgbufsize = _kvm32toh(kd, vmst->hdr.msgbufsize);
123 	vmst->hdr.bitmapsize = _kvm32toh(kd, vmst->hdr.bitmapsize);
124 	vmst->hdr.ptesize = _kvm32toh(kd, vmst->hdr.ptesize);
125 	vmst->hdr.kernbase = _kvm64toh(kd, vmst->hdr.kernbase);
126 	vmst->hdr.dmapbase = _kvm64toh(kd, vmst->hdr.dmapbase);
127 	vmst->hdr.dmapend = _kvm64toh(kd, vmst->hdr.dmapend);
128 
129 	/* Skip header and msgbuf */
130 	off = MIPS_PAGE_SIZE + mips_round_page(vmst->hdr.msgbufsize);
131 
132 	bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
133 	if (bitmap == NULL) {
134 		_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
135 		    "bitmap", vmst->hdr.bitmapsize);
136 		return (-1);
137 	}
138 
139 	if (pread(kd->pmfd, bitmap, vmst->hdr.bitmapsize, off) !=
140 	    (ssize_t)vmst->hdr.bitmapsize) {
141 		_kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap",
142 		    vmst->hdr.bitmapsize);
143 		free(bitmap);
144 		return (-1);
145 	}
146 	off += mips_round_page(vmst->hdr.bitmapsize);
147 
148 	vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize);
149 	if (vmst->ptemap == NULL) {
150 		_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
151 		    "ptemap", vmst->hdr.ptesize);
152 		free(bitmap);
153 		return (-1);
154 	}
155 
156 	if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) !=
157 	    (ssize_t)vmst->hdr.ptesize) {
158 		_kvm_err(kd, kd->program, "cannot read %d bytes for ptemap",
159 		    vmst->hdr.ptesize);
160 		free(bitmap);
161 		return (-1);
162 	}
163 
164 	off += vmst->hdr.ptesize;
165 
166 	/* Build physical address hash table for sparse pages */
167 	_kvm_hpt_init(kd, &vmst->hpt, bitmap, vmst->hdr.bitmapsize, off,
168 	    MIPS_PAGE_SIZE, sizeof(*bitmap));
169 	free(bitmap);
170 
171 	return (0);
172 }
173 
174 static int
_mips_minidump_kvatop(kvm_t * kd,kvaddr_t va,off_t * pa)175 _mips_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
176 {
177 	struct vmstate *vm;
178 	uint64_t pte;
179 	mips_physaddr_t offset, a;
180 	kvaddr_t pteindex;
181 	off_t ofs;
182 	uint32_t *ptemap32;
183 	uint64_t *ptemap64;
184 
185 	if (ISALIVE(kd)) {
186 		_kvm_err(kd, 0, "_mips_minidump_kvatop called in live kernel!");
187 		return (0);
188 	}
189 
190 	offset = va & MIPS_PAGE_MASK;
191 	/* Operate with page-aligned address */
192 	va &= ~MIPS_PAGE_MASK;
193 
194 	vm = kd->vmst;
195 	ptemap32 = vm->ptemap;
196 	ptemap64 = vm->ptemap;
197 
198 	if (kd->nlehdr.e_ident[EI_CLASS] == ELFCLASS64) {
199 		if (va >= MIPS_XKPHYS_START && va < MIPS_XKPHYS_END) {
200 			a = va & MIPS_XKPHYS_PHYS_MASK;
201 			goto found;
202 		}
203 		if (va >= MIPS64_KSEG0_START && va < MIPS64_KSEG0_END) {
204 			a = va & MIPS_KSEG0_PHYS_MASK;
205 			goto found;
206 		}
207 		if (va >= MIPS64_KSEG1_START && va < MIPS64_KSEG1_END) {
208 			a = va & MIPS_KSEG0_PHYS_MASK;
209 			goto found;
210 		}
211 	} else {
212 		if (va >= MIPS32_KSEG0_START && va < MIPS32_KSEG0_END) {
213 			a = va & MIPS_KSEG0_PHYS_MASK;
214 			goto found;
215 		}
216 		if (va >= MIPS32_KSEG1_START && va < MIPS32_KSEG1_END) {
217 			a = va & MIPS_KSEG0_PHYS_MASK;
218 			goto found;
219 		}
220 	}
221 	if (va >= vm->hdr.kernbase) {
222 		pteindex = (va - vm->hdr.kernbase) >> MIPS_PAGE_SHIFT;
223 		if (vm->pte_size == 64) {
224 			pte = _kvm64toh(kd, ptemap64[pteindex]);
225 			a = MIPS64_PTE_TO_PA(pte);
226 		} else {
227 			pte = _kvm32toh(kd, ptemap32[pteindex]);
228 			a = MIPS32_PTE_TO_PA(pte);
229 		}
230 		if (!pte) {
231 			_kvm_err(kd, kd->program, "_mips_minidump_kvatop: pte "
232 			    "not valid");
233 			goto invalid;
234 		}
235 	} else {
236 		_kvm_err(kd, kd->program, "_mips_minidump_kvatop: virtual "
237 		    "address 0x%jx not minidumped", (uintmax_t)va);
238 		return (0);
239 	}
240 
241 found:
242 	ofs = _kvm_hpt_find(&vm->hpt, a);
243 	if (ofs == -1) {
244 		_kvm_err(kd, kd->program, "_mips_minidump_kvatop: physical "
245 		    "address 0x%jx not in minidump", (uintmax_t)a);
246 		goto invalid;
247 	}
248 
249 	*pa = ofs + offset;
250 	return (MIPS_PAGE_SIZE - offset);
251 
252 
253 invalid:
254 	_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
255 	return (0);
256 }
257 
258 static int
_mips_native(kvm_t * kd)259 _mips_native(kvm_t *kd)
260 {
261 
262 #ifdef __mips__
263 #ifdef __mips_n64
264 	if (kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS64)
265 		return (0);
266 #else
267 	if (kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS32)
268 		return (0);
269 #ifdef __mips_n32
270 	if (!(kd->nlehdr.e_flags & EF_MIPS_ABI2))
271 		return (0);
272 #else
273 	if (kd->nlehdr.e_flags & EF_MIPS_ABI2)
274 		return (0);
275 #endif
276 #endif
277 #if _BYTE_ORDER == _LITTLE_ENDIAN
278 	return (kd->nlehdr.e_ident[EI_DATA] == ELFDATA2LSB);
279 #else
280 	return (kd->nlehdr.e_ident[EI_DATA] == ELFDATA2MSB);
281 #endif
282 #else
283 	return (0);
284 #endif
285 }
286 
287 struct kvm_arch kvm_mips_minidump = {
288 	.ka_probe = _mips_minidump_probe,
289 	.ka_initvtop = _mips_minidump_initvtop,
290 	.ka_freevtop = _mips_minidump_freevtop,
291 	.ka_kvatop = _mips_minidump_kvatop,
292 	.ka_native = _mips_native,
293 };
294 
295 KVM_ARCH(kvm_mips_minidump);
296