]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libkvm/kvm_minidump_arm.c
zfs: merge openzfs/zfs@043c6ee3b
[FreeBSD/FreeBSD.git] / lib / libkvm / kvm_minidump_arm.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2008 Semihalf, Grzegorz Bernacki
5  * Copyright (c) 2006 Peter Wemm
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * From: FreeBSD: src/lib/libkvm/kvm_minidump_i386.c,v 1.2 2006/06/05 08:51:14
29  */
30
31 #include <sys/cdefs.h>
32 /*
33  * ARM machine dependent routines for kvm and minidumps.
34  */
35
36 #include <sys/endian.h>
37 #include <sys/param.h>
38 #include <vm/vm.h>
39 #include <kvm.h>
40 #include <limits.h>
41 #include <stdint.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45
46 #include "../../sys/arm/include/minidump.h"
47
48 #include "kvm_private.h"
49 #include "kvm_arm.h"
50
51 #define arm_round_page(x)       roundup2((kvaddr_t)(x), ARM_PAGE_SIZE)
52
53 struct vmstate {
54         struct          minidumphdr hdr;
55         unsigned char   ei_data;
56 };
57
58 static arm_pt_entry_t
59 _arm_pte_get(kvm_t *kd, u_long pteindex)
60 {
61         arm_pt_entry_t *pte = _kvm_pmap_get(kd, pteindex, sizeof(*pte));
62
63         return _kvm32toh(kd, *pte);
64 }
65
66 static int
67 _arm_minidump_probe(kvm_t *kd)
68 {
69
70         return (_kvm_probe_elf_kernel(kd, ELFCLASS32, EM_ARM) &&
71             _kvm_is_minidump(kd));
72 }
73
74 static void
75 _arm_minidump_freevtop(kvm_t *kd)
76 {
77         struct vmstate *vm = kd->vmst;
78
79         free(vm);
80         kd->vmst = NULL;
81 }
82
83 static int
84 _arm_minidump_initvtop(kvm_t *kd)
85 {
86         struct vmstate *vmst;
87         off_t off, dump_avail_off, sparse_off;
88
89         vmst = _kvm_malloc(kd, sizeof(*vmst));
90         if (vmst == NULL) {
91                 _kvm_err(kd, kd->program, "cannot allocate vm");
92                 return (-1);
93         }
94
95         kd->vmst = vmst;
96
97         if (pread(kd->pmfd, &vmst->hdr,
98             sizeof(vmst->hdr), 0) != sizeof(vmst->hdr)) {
99                 _kvm_err(kd, kd->program, "cannot read dump header");
100                 return (-1);
101         }
102
103         if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic,
104             sizeof(vmst->hdr.magic)) != 0) {
105                 _kvm_err(kd, kd->program, "not a minidump for this platform");
106                 return (-1);
107         }
108         vmst->hdr.version = _kvm32toh(kd, vmst->hdr.version);
109         if (vmst->hdr.version != MINIDUMP_VERSION && vmst->hdr.version != 1) {
110                 _kvm_err(kd, kd->program, "wrong minidump version. "
111                     "Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version);
112                 return (-1);
113         }
114         vmst->hdr.msgbufsize = _kvm32toh(kd, vmst->hdr.msgbufsize);
115         vmst->hdr.bitmapsize = _kvm32toh(kd, vmst->hdr.bitmapsize);
116         vmst->hdr.ptesize = _kvm32toh(kd, vmst->hdr.ptesize);
117         vmst->hdr.kernbase = _kvm32toh(kd, vmst->hdr.kernbase);
118         vmst->hdr.arch = _kvm32toh(kd, vmst->hdr.arch);
119         vmst->hdr.mmuformat = _kvm32toh(kd, vmst->hdr.mmuformat);
120         if (vmst->hdr.mmuformat == MINIDUMP_MMU_FORMAT_UNKNOWN) {
121                 /* This is a safe default as 1K pages are not used. */
122                 vmst->hdr.mmuformat = MINIDUMP_MMU_FORMAT_V6;
123         }
124         vmst->hdr.dumpavailsize = vmst->hdr.version == MINIDUMP_VERSION ?
125             _kvm32toh(kd, vmst->hdr.dumpavailsize) : 0;
126
127         /* Skip header and msgbuf */
128         dump_avail_off = ARM_PAGE_SIZE + arm_round_page(vmst->hdr.msgbufsize);
129
130         /* Skip dump_avail */
131         off = dump_avail_off + arm_round_page(vmst->hdr.dumpavailsize);
132
133         sparse_off = off + arm_round_page(vmst->hdr.bitmapsize) +
134             arm_round_page(vmst->hdr.ptesize);
135         if (_kvm_pt_init(kd, vmst->hdr.dumpavailsize, dump_avail_off,
136             vmst->hdr.bitmapsize, off, sparse_off, ARM_PAGE_SIZE) == -1) {
137                 return (-1);
138         }
139         off += arm_round_page(vmst->hdr.bitmapsize);
140
141         if (_kvm_pmap_init(kd, vmst->hdr.ptesize, off) == -1) {
142                 return (-1);
143         }
144         off += arm_round_page(vmst->hdr.ptesize);
145
146         return (0);
147 }
148
149 static int
150 _arm_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
151 {
152         struct vmstate *vm;
153         arm_pt_entry_t pte;
154         arm_physaddr_t offset, a;
155         kvaddr_t pteindex;
156         off_t ofs;
157
158         if (ISALIVE(kd)) {
159                 _kvm_err(kd, 0, "_arm_minidump_kvatop called in live kernel!");
160                 return (0);
161         }
162
163         vm = kd->vmst;
164
165         if (va >= vm->hdr.kernbase) {
166                 pteindex = (va - vm->hdr.kernbase) >> ARM_PAGE_SHIFT;
167                 if (pteindex >= vm->hdr.ptesize / sizeof(pte))
168                         goto invalid;
169                 pte = _arm_pte_get(kd, pteindex);
170                 if ((pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_INV) {
171                         _kvm_err(kd, kd->program,
172                             "_arm_minidump_kvatop: pte not valid");
173                         goto invalid;
174                 }
175                 if ((pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_L) {
176                         /* 64K page -> convert to be like 4K page */
177                         offset = va & ARM_L2_S_OFFSET;
178                         a = (pte & ARM_L2_L_FRAME) +
179                             (va & ARM_L2_L_OFFSET & ARM_L2_S_FRAME);
180                 } else {
181                         if (kd->vmst->hdr.mmuformat == MINIDUMP_MMU_FORMAT_V4 &&
182                             (pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_T) {
183                                 _kvm_err(kd, kd->program,
184                                     "_arm_minidump_kvatop: pte not supported");
185                                 goto invalid;
186                         }
187                         /* 4K page */
188                         offset = va & ARM_L2_S_OFFSET;
189                         a = pte & ARM_L2_S_FRAME;
190                 }
191
192                 ofs = _kvm_pt_find(kd, a, ARM_PAGE_SIZE);
193                 if (ofs == -1) {
194                         _kvm_err(kd, kd->program, "_arm_minidump_kvatop: "
195                             "physical address 0x%jx not in minidump",
196                             (uintmax_t)a);
197                         goto invalid;
198                 }
199
200                 *pa = ofs + offset;
201                 return (ARM_PAGE_SIZE - offset);
202         } else
203                 _kvm_err(kd, kd->program, "_arm_minidump_kvatop: virtual "
204                     "address 0x%jx not minidumped", (uintmax_t)va);
205
206 invalid:
207         _kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
208         return (0);
209 }
210
211 static vm_prot_t
212 _arm_entry_to_prot(kvm_t *kd, arm_pt_entry_t pte)
213 {
214         struct vmstate *vm = kd->vmst;
215         vm_prot_t prot = VM_PROT_READ;
216
217         /* Source: arm/arm/pmap-v4.c:pmap_fault_fixup() */
218         if (vm->hdr.mmuformat == MINIDUMP_MMU_FORMAT_V4) {
219                 if (pte & ARM_L2_S_PROT_W)
220                         prot |= VM_PROT_WRITE;
221                 return prot;
222         }
223
224         /* Source: arm/arm/pmap-v6.c:pmap_protect() */
225         if ((pte & ARM_PTE2_RO) == 0)
226                 prot |= VM_PROT_WRITE;
227         if ((pte & ARM_PTE2_NX) == 0)
228                 prot |= VM_PROT_EXECUTE;
229         return prot;
230 }
231
232 static int
233 _arm_minidump_walk_pages(kvm_t *kd, kvm_walk_pages_cb_t *cb, void *arg)
234 {
235         struct vmstate *vm = kd->vmst;
236         u_long nptes = vm->hdr.ptesize / sizeof(arm_pt_entry_t);
237         u_long dva, pa, pteindex, va;
238
239         for (pteindex = 0; pteindex < nptes; pteindex++) {
240                 arm_pt_entry_t pte = _arm_pte_get(kd, pteindex);
241
242                 if ((pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_INV)
243                         continue;
244
245                 va = vm->hdr.kernbase + (pteindex << ARM_PAGE_SHIFT);
246                 if ((pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_L) {
247                         /* 64K page */
248                         pa = (pte & ARM_L2_L_FRAME) +
249                             (va & ARM_L2_L_OFFSET & ARM_L2_S_FRAME);
250                 } else {
251                         if (vm->hdr.mmuformat == MINIDUMP_MMU_FORMAT_V4 &&
252                             (pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_T) {
253                                 continue;
254                         }
255                         /* 4K page */
256                         pa = pte & ARM_L2_S_FRAME;
257                 }
258
259                 dva = 0; /* no direct map on this platform */
260                 if (!_kvm_visit_cb(kd, cb, arg, pa, va, dva,
261                     _arm_entry_to_prot(kd, pte), ARM_PAGE_SIZE, 0))
262                         return (0);
263         }
264         return (1);
265 }
266
267 static struct kvm_arch kvm_arm_minidump = {
268         .ka_probe = _arm_minidump_probe,
269         .ka_initvtop = _arm_minidump_initvtop,
270         .ka_freevtop = _arm_minidump_freevtop,
271         .ka_kvatop = _arm_minidump_kvatop,
272         .ka_native = _arm_native,
273         .ka_walk_pages = _arm_minidump_walk_pages,
274 };
275
276 KVM_ARCH(kvm_arm_minidump);