]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libkvm/kvm_minidump_mips.c
MFC r326601:
[FreeBSD/FreeBSD.git] / lib / libkvm / kvm_minidump_mips.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2010 Oleksandr Tymoshenko
5  * Copyright (c) 2008 Semihalf, Grzegorz Bernacki
6  * Copyright (c) 2006 Peter Wemm
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * From: FreeBSD: src/lib/libkvm/kvm_minidump_arm.c r214223
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 /*
36  * MIPS machine dependent routines for kvm and minidumps.
37  */
38
39 #include <sys/param.h>
40 #include <kvm.h>
41 #include <limits.h>
42 #include <stdint.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 #include "../../sys/mips/include/cpuregs.h"
48 #include "../../sys/mips/include/minidump.h"
49
50 #include "kvm_private.h"
51 #include "kvm_mips.h"
52
53 #define mips_round_page(x)      roundup2((kvaddr_t)(x), MIPS_PAGE_SIZE)
54
55 struct vmstate {
56         struct          minidumphdr hdr;
57         struct          hpt hpt;
58         void            *ptemap;
59         int             pte_size;
60 };
61
62 static int
63 _mips_minidump_probe(kvm_t *kd)
64 {
65
66         if (kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS32 &&
67             kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS64)
68                 return (0);
69         if (kd->nlehdr.e_machine != EM_MIPS)
70                 return (0);
71         return (_kvm_is_minidump(kd));
72 }
73
74 static void
75 _mips_minidump_freevtop(kvm_t *kd)
76 {
77         struct vmstate *vm = kd->vmst;
78
79         _kvm_hpt_free(&vm->hpt);
80         if (vm->ptemap)
81                 free(vm->ptemap);
82         free(vm);
83         kd->vmst = NULL;
84 }
85
86 static int
87 _mips_minidump_initvtop(kvm_t *kd)
88 {
89         struct vmstate *vmst;
90         uint32_t *bitmap;
91         off_t off;
92
93         vmst = _kvm_malloc(kd, sizeof(*vmst));
94         if (vmst == NULL) {
95                 _kvm_err(kd, kd->program, "cannot allocate vm");
96                 return (-1);
97         }
98
99         kd->vmst = vmst;
100
101         if (kd->nlehdr.e_ident[EI_CLASS] == ELFCLASS64 ||
102             kd->nlehdr.e_flags & EF_MIPS_ABI2)
103                 vmst->pte_size = 64;
104         else
105                 vmst->pte_size = 32;
106
107         if (pread(kd->pmfd, &vmst->hdr,
108             sizeof(vmst->hdr), 0) != sizeof(vmst->hdr)) {
109                 _kvm_err(kd, kd->program, "cannot read dump header");
110                 return (-1);
111         }
112
113         if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic,
114             sizeof(vmst->hdr.magic)) != 0) {
115                 _kvm_err(kd, kd->program, "not a minidump for this platform");
116                 return (-1);
117         }
118         vmst->hdr.version = _kvm32toh(kd, vmst->hdr.version);
119         if (vmst->hdr.version != MINIDUMP_VERSION) {
120                 _kvm_err(kd, kd->program, "wrong minidump version. "
121                     "Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version);
122                 return (-1);
123         }
124         vmst->hdr.msgbufsize = _kvm32toh(kd, vmst->hdr.msgbufsize);
125         vmst->hdr.bitmapsize = _kvm32toh(kd, vmst->hdr.bitmapsize);
126         vmst->hdr.ptesize = _kvm32toh(kd, vmst->hdr.ptesize);
127         vmst->hdr.kernbase = _kvm64toh(kd, vmst->hdr.kernbase);
128         vmst->hdr.dmapbase = _kvm64toh(kd, vmst->hdr.dmapbase);
129         vmst->hdr.dmapend = _kvm64toh(kd, vmst->hdr.dmapend);
130
131         /* Skip header and msgbuf */
132         off = MIPS_PAGE_SIZE + mips_round_page(vmst->hdr.msgbufsize);
133
134         bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
135         if (bitmap == NULL) {
136                 _kvm_err(kd, kd->program, "cannot allocate %d bytes for "
137                     "bitmap", vmst->hdr.bitmapsize);
138                 return (-1);
139         }
140
141         if (pread(kd->pmfd, bitmap, vmst->hdr.bitmapsize, off) !=
142             (ssize_t)vmst->hdr.bitmapsize) {
143                 _kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap",
144                     vmst->hdr.bitmapsize);
145                 free(bitmap);
146                 return (-1);
147         }
148         off += mips_round_page(vmst->hdr.bitmapsize);
149
150         vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize);
151         if (vmst->ptemap == NULL) {
152                 _kvm_err(kd, kd->program, "cannot allocate %d bytes for "
153                     "ptemap", vmst->hdr.ptesize);
154                 free(bitmap);
155                 return (-1);
156         }
157
158         if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) !=
159             (ssize_t)vmst->hdr.ptesize) {
160                 _kvm_err(kd, kd->program, "cannot read %d bytes for ptemap",
161                     vmst->hdr.ptesize);
162                 free(bitmap);
163                 return (-1);
164         }
165
166         off += vmst->hdr.ptesize;
167
168         /* Build physical address hash table for sparse pages */
169         _kvm_hpt_init(kd, &vmst->hpt, bitmap, vmst->hdr.bitmapsize, off,
170             MIPS_PAGE_SIZE, sizeof(*bitmap));
171         free(bitmap);
172
173         return (0);
174 }
175
176 static int
177 _mips_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
178 {
179         struct vmstate *vm;
180         uint64_t pte;
181         mips_physaddr_t offset, a;
182         kvaddr_t pteindex;
183         off_t ofs;
184         uint32_t *ptemap32;
185         uint64_t *ptemap64;
186
187         if (ISALIVE(kd)) {
188                 _kvm_err(kd, 0, "_mips_minidump_kvatop called in live kernel!");
189                 return (0);
190         }
191
192         offset = va & MIPS_PAGE_MASK;
193         /* Operate with page-aligned address */
194         va &= ~MIPS_PAGE_MASK;
195
196         vm = kd->vmst;
197         ptemap32 = vm->ptemap;
198         ptemap64 = vm->ptemap;
199
200         if (kd->nlehdr.e_ident[EI_CLASS] == ELFCLASS64) {
201                 if (va >= MIPS_XKPHYS_START && va < MIPS_XKPHYS_END) {
202                         a = va & MIPS_XKPHYS_PHYS_MASK;
203                         goto found;
204                 }
205                 if (va >= MIPS64_KSEG0_START && va < MIPS64_KSEG0_END) {
206                         a = va & MIPS_KSEG0_PHYS_MASK;
207                         goto found;
208                 }
209                 if (va >= MIPS64_KSEG1_START && va < MIPS64_KSEG1_END) {
210                         a = va & MIPS_KSEG0_PHYS_MASK;
211                         goto found;
212                 }
213         } else {
214                 if (va >= MIPS32_KSEG0_START && va < MIPS32_KSEG0_END) {
215                         a = va & MIPS_KSEG0_PHYS_MASK;
216                         goto found;
217                 }
218                 if (va >= MIPS32_KSEG1_START && va < MIPS32_KSEG1_END) {
219                         a = va & MIPS_KSEG0_PHYS_MASK;
220                         goto found;
221                 }
222         }
223         if (va >= vm->hdr.kernbase) {
224                 pteindex = (va - vm->hdr.kernbase) >> MIPS_PAGE_SHIFT;
225                 if (vm->pte_size == 64) {
226                         pte = _kvm64toh(kd, ptemap64[pteindex]);
227                         a = MIPS64_PTE_TO_PA(pte);
228                 } else {
229                         pte = _kvm32toh(kd, ptemap32[pteindex]);
230                         a = MIPS32_PTE_TO_PA(pte);
231                 }
232                 if (!pte) {
233                         _kvm_err(kd, kd->program, "_mips_minidump_kvatop: pte "
234                             "not valid");
235                         goto invalid;
236                 }
237         } else {
238                 _kvm_err(kd, kd->program, "_mips_minidump_kvatop: virtual "
239                     "address 0x%jx not minidumped", (uintmax_t)va);
240                 return (0);
241         }
242
243 found:
244         ofs = _kvm_hpt_find(&vm->hpt, a);
245         if (ofs == -1) {
246                 _kvm_err(kd, kd->program, "_mips_minidump_kvatop: physical "
247                     "address 0x%jx not in minidump", (uintmax_t)a);
248                 goto invalid;
249         }
250
251         *pa = ofs + offset;
252         return (MIPS_PAGE_SIZE - offset);
253
254
255 invalid:
256         _kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
257         return (0);
258 }
259
260 static int
261 #ifdef __mips__
262 _mips_native(kvm_t *kd)
263 #else
264 _mips_native(kvm_t *kd __unused)
265 #endif
266 {
267
268 #ifdef __mips__
269 #ifdef __mips_n64
270         if (kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS64)
271                 return (0);
272 #else
273         if (kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS32)
274                 return (0);
275 #ifdef __mips_n32
276         if (!(kd->nlehdr.e_flags & EF_MIPS_ABI2))
277                 return (0);
278 #else
279         if (kd->nlehdr.e_flags & EF_MIPS_ABI2)
280                 return (0);
281 #endif
282 #endif
283 #if _BYTE_ORDER == _LITTLE_ENDIAN
284         return (kd->nlehdr.e_ident[EI_DATA] == ELFDATA2LSB);
285 #else
286         return (kd->nlehdr.e_ident[EI_DATA] == ELFDATA2MSB);
287 #endif
288 #else
289         return (0);
290 #endif
291 }
292
293 static struct kvm_arch kvm_mips_minidump = {
294         .ka_probe = _mips_minidump_probe,
295         .ka_initvtop = _mips_minidump_initvtop,
296         .ka_freevtop = _mips_minidump_freevtop,
297         .ka_kvatop = _mips_minidump_kvatop,
298         .ka_native = _mips_native,
299 };
300
301 KVM_ARCH(kvm_mips_minidump);