]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libkvm/kvm_minidump_mips.c
libkvm: add kvm_walk_pages API.
[FreeBSD/FreeBSD.git] / lib / libkvm / kvm_minidump_mips.c
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         int             pte_size;
56 };
57
58 static int
59 _mips_minidump_probe(kvm_t *kd)
60 {
61
62         if (kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS32 &&
63             kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS64)
64                 return (0);
65         if (kd->nlehdr.e_machine != EM_MIPS)
66                 return (0);
67         return (_kvm_is_minidump(kd));
68 }
69
70 static void
71 _mips_minidump_freevtop(kvm_t *kd)
72 {
73         struct vmstate *vm = kd->vmst;
74
75         free(vm);
76         kd->vmst = NULL;
77 }
78
79 static int
80 _mips_minidump_initvtop(kvm_t *kd)
81 {
82         struct vmstate *vmst;
83         off_t off, sparse_off;
84
85         vmst = _kvm_malloc(kd, sizeof(*vmst));
86         if (vmst == NULL) {
87                 _kvm_err(kd, kd->program, "cannot allocate vm");
88                 return (-1);
89         }
90
91         kd->vmst = vmst;
92
93         if (kd->nlehdr.e_ident[EI_CLASS] == ELFCLASS64 ||
94             kd->nlehdr.e_flags & EF_MIPS_ABI2)
95                 vmst->pte_size = 64;
96         else
97                 vmst->pte_size = 32;
98
99         if (pread(kd->pmfd, &vmst->hdr,
100             sizeof(vmst->hdr), 0) != sizeof(vmst->hdr)) {
101                 _kvm_err(kd, kd->program, "cannot read dump header");
102                 return (-1);
103         }
104
105         if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic,
106             sizeof(vmst->hdr.magic)) != 0) {
107                 _kvm_err(kd, kd->program, "not a minidump for this platform");
108                 return (-1);
109         }
110         vmst->hdr.version = _kvm32toh(kd, vmst->hdr.version);
111         if (vmst->hdr.version != MINIDUMP_VERSION) {
112                 _kvm_err(kd, kd->program, "wrong minidump version. "
113                     "Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version);
114                 return (-1);
115         }
116         vmst->hdr.msgbufsize = _kvm32toh(kd, vmst->hdr.msgbufsize);
117         vmst->hdr.bitmapsize = _kvm32toh(kd, vmst->hdr.bitmapsize);
118         vmst->hdr.ptesize = _kvm32toh(kd, vmst->hdr.ptesize);
119         vmst->hdr.kernbase = _kvm64toh(kd, vmst->hdr.kernbase);
120         vmst->hdr.dmapbase = _kvm64toh(kd, vmst->hdr.dmapbase);
121         vmst->hdr.dmapend = _kvm64toh(kd, vmst->hdr.dmapend);
122
123         /* Skip header and msgbuf */
124         off = MIPS_PAGE_SIZE + mips_round_page(vmst->hdr.msgbufsize);
125
126         sparse_off = off + mips_round_page(vmst->hdr.bitmapsize) +
127             mips_round_page(vmst->hdr.ptesize);
128         if (_kvm_pt_init(kd, vmst->hdr.bitmapsize, off, sparse_off,
129             MIPS_PAGE_SIZE, sizeof(uint32_t)) == -1) {
130                 return (-1);
131         }
132         off += mips_round_page(vmst->hdr.bitmapsize);
133
134         if (_kvm_pmap_init(kd, vmst->hdr.ptesize, off) == -1) {
135                 return (-1);
136         }
137         off += mips_round_page(vmst->hdr.ptesize);
138
139         return (0);
140 }
141
142 static int
143 _mips_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
144 {
145         struct vmstate *vm;
146         mips_physaddr_t offset, a;
147         kvaddr_t pteindex;
148         u_long valid;
149         off_t ofs;
150         mips32_pte_t pte32;
151         mips64_pte_t pte64;
152
153         if (ISALIVE(kd)) {
154                 _kvm_err(kd, 0, "_mips_minidump_kvatop called in live kernel!");
155                 return (0);
156         }
157
158         offset = va & MIPS_PAGE_MASK;
159         /* Operate with page-aligned address */
160         va &= ~MIPS_PAGE_MASK;
161
162         vm = kd->vmst;
163         if (kd->nlehdr.e_ident[EI_CLASS] == ELFCLASS64) {
164                 if (va >= MIPS_XKPHYS_START && va < MIPS_XKPHYS_END) {
165                         a = va & MIPS_XKPHYS_PHYS_MASK;
166                         goto found;
167                 }
168                 if (va >= MIPS64_KSEG0_START && va < MIPS64_KSEG0_END) {
169                         a = va & MIPS_KSEG0_PHYS_MASK;
170                         goto found;
171                 }
172                 if (va >= MIPS64_KSEG1_START && va < MIPS64_KSEG1_END) {
173                         a = va & MIPS_KSEG0_PHYS_MASK;
174                         goto found;
175                 }
176         } else {
177                 if (va >= MIPS32_KSEG0_START && va < MIPS32_KSEG0_END) {
178                         a = va & MIPS_KSEG0_PHYS_MASK;
179                         goto found;
180                 }
181                 if (va >= MIPS32_KSEG1_START && va < MIPS32_KSEG1_END) {
182                         a = va & MIPS_KSEG0_PHYS_MASK;
183                         goto found;
184                 }
185         }
186         if (va >= vm->hdr.kernbase) {
187                 pteindex = (va - vm->hdr.kernbase) >> MIPS_PAGE_SHIFT;
188                 if (vm->pte_size == 64) {
189                         valid = pteindex < vm->hdr.ptesize / sizeof(pte64);
190                         if (pteindex >= vm->hdr.ptesize / sizeof(pte64))
191                                 goto invalid;
192                         pte64 = _mips64_pte_get(kd, pteindex);
193                         valid = pte64 & MIPS_PTE_V;
194                         if (valid)
195                                 a = MIPS64_PTE_TO_PA(pte64);
196                 } else {
197                         if (pteindex >= vm->hdr.ptesize / sizeof(pte32))
198                                 goto invalid;
199                         pte32 = _mips32_pte_get(kd, pteindex);
200                         valid = pte32 & MIPS_PTE_V;
201                         if (valid)
202                                 a = MIPS32_PTE_TO_PA(pte32);
203                 }
204                 if (!valid) {
205                         _kvm_err(kd, kd->program, "_mips_minidump_kvatop: pte "
206                             "not valid");
207                         goto invalid;
208                 }
209         } else {
210                 _kvm_err(kd, kd->program, "_mips_minidump_kvatop: virtual "
211                     "address 0x%jx not minidumped", (uintmax_t)va);
212                 return (0);
213         }
214
215 found:
216         ofs = _kvm_pt_find(kd, a, MIPS_PAGE_SIZE);
217         if (ofs == -1) {
218                 _kvm_err(kd, kd->program, "_mips_minidump_kvatop: physical "
219                     "address 0x%jx not in minidump", (uintmax_t)a);
220                 goto invalid;
221         }
222
223         *pa = ofs + offset;
224         return (MIPS_PAGE_SIZE - offset);
225
226
227 invalid:
228         _kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
229         return (0);
230 }
231
232 static int
233 #ifdef __mips__
234 _mips_native(kvm_t *kd)
235 #else
236 _mips_native(kvm_t *kd __unused)
237 #endif
238 {
239
240 #ifdef __mips__
241 #ifdef __mips_n64
242         if (kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS64)
243                 return (0);
244 #else
245         if (kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS32)
246                 return (0);
247 #ifdef __mips_n32
248         if (!(kd->nlehdr.e_flags & EF_MIPS_ABI2))
249                 return (0);
250 #else
251         if (kd->nlehdr.e_flags & EF_MIPS_ABI2)
252                 return (0);
253 #endif
254 #endif
255 #if _BYTE_ORDER == _LITTLE_ENDIAN
256         return (kd->nlehdr.e_ident[EI_DATA] == ELFDATA2LSB);
257 #else
258         return (kd->nlehdr.e_ident[EI_DATA] == ELFDATA2MSB);
259 #endif
260 #else
261         return (0);
262 #endif
263 }
264
265 struct mips_iter {
266         kvm_t *kd;
267         u_long nptes;
268         u_long pteindex;
269 };
270
271 static void
272 _mips_iterator_init(struct mips_iter *it, kvm_t *kd)
273 {
274         struct vmstate *vm = kd->vmst;
275
276         it->kd = kd;
277         it->pteindex = 0;
278         if (vm->pte_size == 64)
279                 it->nptes = vm->hdr.ptesize / sizeof(mips64_pte_t);
280         else
281                 it->nptes = vm->hdr.ptesize / sizeof(mips32_pte_t);
282         return;
283 }
284
285 static int
286 _mips_iterator_next(struct mips_iter *it, u_long *pa, u_long *va, u_long *dva,
287     vm_prot_t *prot)
288 {
289         struct vmstate *vm = it->kd->vmst;
290         int found = 0;
291         mips64_pte_t pte64;
292         mips32_pte_t pte32;
293
294         /*
295          * mips/mips/pmap.c: init_pte_prot / pmap_protect indicate that all
296          * pages are R|X at least.
297          */
298         *prot = VM_PROT_READ | VM_PROT_EXECUTE;
299         *dva = 0;
300         for (;it->pteindex < it->nptes && found == 0; it->pteindex++) {
301                 if (vm->pte_size == 64) {
302                         pte64 = _mips64_pte_get(it->kd, it->pteindex);
303                         if ((pte64 & MIPS_PTE_V) == 0)
304                                 continue;
305                         if ((pte64 & MIPS64_PTE_RO) == 0)
306                                 *prot |= VM_PROT_WRITE;
307                         *pa = MIPS64_PTE_TO_PA(pte64);
308                 } else {
309                         pte32 = _mips32_pte_get(it->kd, it->pteindex);
310                         if ((pte32 & MIPS_PTE_V) == 0)
311                                 continue;
312                         if ((pte32 & MIPS32_PTE_RO) == 0)
313                                 *prot |= VM_PROT_WRITE;
314                         *pa = MIPS32_PTE_TO_PA(pte32);
315                 }
316                 *va = vm->hdr.kernbase + (it->pteindex << MIPS_PAGE_SHIFT);
317                 found = 1;
318                 /* advance pteindex regardless */
319         }
320
321         return found;
322 }
323
324 static int
325 _mips_minidump_walk_pages(kvm_t *kd, kvm_walk_pages_cb_t *cb, void *arg)
326 {
327         struct mips_iter it;
328         u_long dva, pa, va;
329         vm_prot_t prot;
330
331         /* Generate direct mapped entries; need page entries for prot etc? */
332         if (kd->nlehdr.e_ident[EI_CLASS] == ELFCLASS64) {
333                 /* MIPS_XKPHYS_START..MIPS_XKPHYS_END */
334                 /* MIPS64_KSEG0_START..MIPS64_KSEG0_END */
335                 /* MIPS64_KSEG1_START..MIPS64_KSEG1_START */
336         } else {
337                 /* MIPS32_KSEG0_START..MIPS32_KSEG0_END */
338                 /* MIPS32_KSEG1_START..MIPS32_KSEG1_END */
339         }
340
341         _mips_iterator_init(&it, kd);
342         while (_mips_iterator_next(&it, &pa, &va, &dva, &prot)) {
343                 if (!_kvm_visit_cb(kd, cb, arg, pa, va, dva,
344                     prot, MIPS_PAGE_SIZE, 0)) {
345                         return (0);
346                 }
347         }
348         return (1);
349 }
350
351 static struct kvm_arch kvm_mips_minidump = {
352         .ka_probe = _mips_minidump_probe,
353         .ka_initvtop = _mips_minidump_initvtop,
354         .ka_freevtop = _mips_minidump_freevtop,
355         .ka_kvatop = _mips_minidump_kvatop,
356         .ka_native = _mips_native,
357         .ka_walk_pages = _mips_minidump_walk_pages,
358 };
359
360 KVM_ARCH(kvm_mips_minidump);