xref: /freebsd/lib/libkvm/kvm_minidump_arm.c (revision 6574b8ed19b093f0af09501d2c9676c28993cb97)
1 /*-
2  * Copyright (c) 2008 Semihalf, Grzegorz Bernacki
3  * Copyright (c) 2006 Peter Wemm
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * From: FreeBSD: src/lib/libkvm/kvm_minidump_i386.c,v 1.2 2006/06/05 08:51:14
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 /*
33  * ARM machine dependent routines for kvm and minidumps.
34  */
35 
36 #include <sys/param.h>
37 #ifndef CROSS_LIBKVM
38 #include <sys/user.h>
39 #endif
40 #include <sys/proc.h>
41 #include <sys/stat.h>
42 #include <sys/mman.h>
43 #include <sys/fnv_hash.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <nlist.h>
48 #include <kvm.h>
49 
50 #ifndef CROSS_LIBKVM
51 #include <vm/vm.h>
52 #include <vm/vm_param.h>
53 
54 #include <machine/elf.h>
55 #include <machine/cpufunc.h>
56 #include <machine/minidump.h>
57 #else
58 #include "../../sys/arm/include/pte.h"
59 #include "../../sys/arm/include/vmparam.h"
60 #include "../../sys/arm/include/minidump.h"
61 #endif
62 
63 #include <limits.h>
64 
65 #include "kvm_private.h"
66 
67 struct hpte {
68 	struct hpte	*next;
69 	uint64_t	pa;
70 	int64_t		off;
71 };
72 
73 #define HPT_SIZE 1024
74 
75 /* minidump must be the first field */
76 struct vmstate {
77 	int		minidump;		/* 1 = minidump mode */
78 	struct		minidumphdr hdr;
79 	void		*hpt_head[HPT_SIZE];
80 	uint32_t	*bitmap;
81 	void		*ptemap;
82 };
83 
84 static void
85 hpt_insert(kvm_t *kd, uint64_t pa, int64_t off)
86 {
87 	struct hpte *hpte;
88 	uint32_t fnv = FNV1_32_INIT;
89 
90 	fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
91 	fnv &= (HPT_SIZE - 1);
92 	hpte = malloc(sizeof(*hpte));
93 	hpte->pa = pa;
94 	hpte->off = off;
95 	hpte->next = kd->vmst->hpt_head[fnv];
96 	kd->vmst->hpt_head[fnv] = hpte;
97 }
98 
99 static int64_t
100 hpt_find(kvm_t *kd, uint64_t pa)
101 {
102 	struct hpte *hpte;
103 	uint32_t fnv = FNV1_32_INIT;
104 
105 	fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
106 	fnv &= (HPT_SIZE - 1);
107 	for (hpte = kd->vmst->hpt_head[fnv]; hpte != NULL; hpte = hpte->next)
108 		if (pa == hpte->pa)
109 			return (hpte->off);
110 
111 	return (-1);
112 }
113 
114 static int
115 inithash(kvm_t *kd, uint32_t *base, int len, off_t off)
116 {
117 	uint64_t idx, pa;
118 	uint32_t bit, bits;
119 
120 	for (idx = 0; idx < len / sizeof(*base); idx++) {
121 		bits = base[idx];
122 		while (bits) {
123 			bit = ffs(bits) - 1;
124 			bits &= ~(1ul << bit);
125 			pa = (idx * sizeof(*base) * NBBY + bit) * PAGE_SIZE;
126 			hpt_insert(kd, pa, off);
127 			off += PAGE_SIZE;
128 		}
129 	}
130 	return (off);
131 }
132 
133 void
134 _kvm_minidump_freevtop(kvm_t *kd)
135 {
136 	struct vmstate *vm = kd->vmst;
137 
138 	if (vm->bitmap)
139 		free(vm->bitmap);
140 	if (vm->ptemap)
141 		free(vm->ptemap);
142 	free(vm);
143 	kd->vmst = NULL;
144 }
145 
146 int
147 _kvm_minidump_initvtop(kvm_t *kd)
148 {
149 	struct vmstate *vmst;
150 	off_t off;
151 
152 	vmst = _kvm_malloc(kd, sizeof(*vmst));
153 	if (vmst == 0) {
154 		_kvm_err(kd, kd->program, "cannot allocate vm");
155 		return (-1);
156 	}
157 
158 	kd->vmst = vmst;
159 	vmst->minidump = 1;
160 
161 	if (pread(kd->pmfd, &vmst->hdr,
162 	    sizeof(vmst->hdr), 0) != sizeof(vmst->hdr)) {
163 		_kvm_err(kd, kd->program, "cannot read dump header");
164 		return (-1);
165 	}
166 
167 	if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic,
168 	    sizeof(vmst->hdr.magic)) != 0) {
169 		_kvm_err(kd, kd->program, "not a minidump for this platform");
170 		return (-1);
171 	}
172 	if (vmst->hdr.version != MINIDUMP_VERSION) {
173 		_kvm_err(kd, kd->program, "wrong minidump version. "
174 		    "Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version);
175 		return (-1);
176 	}
177 
178 	/* Skip header and msgbuf */
179 	off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize);
180 
181 	vmst->bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
182 	if (vmst->bitmap == NULL) {
183 		_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
184 		    "bitmap", vmst->hdr.bitmapsize);
185 		return (-1);
186 	}
187 
188 	if (pread(kd->pmfd, vmst->bitmap, vmst->hdr.bitmapsize, off) !=
189 	    (ssize_t)vmst->hdr.bitmapsize) {
190 		_kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap",
191 		    vmst->hdr.bitmapsize);
192 		return (-1);
193 	}
194 	off += round_page(vmst->hdr.bitmapsize);
195 
196 	vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize);
197 	if (vmst->ptemap == NULL) {
198 		_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
199 		    "ptemap", vmst->hdr.ptesize);
200 		return (-1);
201 	}
202 
203 	if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) !=
204 	    (ssize_t)vmst->hdr.ptesize) {
205 		_kvm_err(kd, kd->program, "cannot read %d bytes for ptemap",
206 		    vmst->hdr.ptesize);
207 		return (-1);
208 	}
209 
210 	off += vmst->hdr.ptesize;
211 
212 	/* Build physical address hash table for sparse pages */
213 	inithash(kd, vmst->bitmap, vmst->hdr.bitmapsize, off);
214 
215 	return (0);
216 }
217 
218 int
219 _kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
220 {
221 	struct vmstate *vm;
222 	pt_entry_t pte;
223 	u_long offset, pteindex, a;
224 	off_t ofs;
225 	uint32_t *ptemap;
226 
227 	if (ISALIVE(kd)) {
228 		_kvm_err(kd, 0, "kvm_kvatop called in live kernel!");
229 		return (0);
230 	}
231 
232 	vm = kd->vmst;
233 	ptemap = vm->ptemap;
234 
235 	if (va >= vm->hdr.kernbase) {
236 		pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
237 		pte = ptemap[pteindex];
238 		if (!pte) {
239 			_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
240 			goto invalid;
241 		}
242 		if ((pte & L2_TYPE_MASK) == L2_TYPE_L) {
243 			offset = va & L2_L_OFFSET;
244 			a = pte & L2_L_FRAME;
245 		} else if ((pte & L2_TYPE_MASK) == L2_TYPE_S) {
246 			offset = va & L2_S_OFFSET;
247 			a = pte & L2_S_FRAME;
248 		} else
249 			goto invalid;
250 
251 		ofs = hpt_find(kd, a);
252 		if (ofs == -1) {
253 			_kvm_err(kd, kd->program, "_kvm_vatop: physical "
254 			    "address 0x%lx not in minidump", a);
255 			goto invalid;
256 		}
257 
258 		*pa = ofs + offset;
259 		return (PAGE_SIZE - offset);
260 
261 	} else
262 		_kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx "
263 		    "not minidumped", va);
264 
265 invalid:
266 	_kvm_err(kd, 0, "invalid address (0x%lx)", va);
267 	return (0);
268 }
269