xref: /freebsd/lib/libkvm/kvm_minidump_i386.c (revision c9057838)
1 /*-
2  * Copyright (c) 2006 Peter Wemm
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28 
29 /*
30  * i386 machine dependent routines for kvm and minidumps.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/endian.h>
35 #include <stdint.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <kvm.h>
40 
41 #include "../../sys/i386/include/minidump.h"
42 
43 #include <limits.h>
44 
45 #include "kvm_private.h"
46 #include "kvm_i386.h"
47 
48 #define	i386_round_page(x)	roundup2((kvaddr_t)(x), I386_PAGE_SIZE)
49 
50 struct vmstate {
51 	struct minidumphdr hdr;
52 };
53 
54 static i386_pte_pae_t
55 _i386_pte_pae_get(kvm_t *kd, u_long pteindex)
56 {
57 	i386_pte_pae_t *pte = _kvm_pmap_get(kd, pteindex, sizeof(*pte));
58 
59 	return le64toh(*pte);
60 }
61 
62 static i386_pte_t
63 _i386_pte_get(kvm_t *kd, u_long pteindex)
64 {
65 	i386_pte_t *pte = _kvm_pmap_get(kd, pteindex, sizeof(*pte));
66 
67 	return le32toh(*pte);
68 }
69 
70 static int
71 _i386_minidump_probe(kvm_t *kd)
72 {
73 
74 	return (_kvm_probe_elf_kernel(kd, ELFCLASS32, EM_386) &&
75 	    _kvm_is_minidump(kd));
76 }
77 
78 static void
79 _i386_minidump_freevtop(kvm_t *kd)
80 {
81 	struct vmstate *vm = kd->vmst;
82 
83 	free(vm);
84 	kd->vmst = NULL;
85 }
86 
87 static int
88 _i386_minidump_initvtop(kvm_t *kd)
89 {
90 	struct vmstate *vmst;
91 	off_t off, sparse_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 	kd->vmst = vmst;
99 	if (pread(kd->pmfd, &vmst->hdr, sizeof(vmst->hdr), 0) !=
100 	    sizeof(vmst->hdr)) {
101 		_kvm_err(kd, kd->program, "cannot read dump header");
102 		return (-1);
103 	}
104 	if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic, 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 = le32toh(vmst->hdr.version);
109 	if (vmst->hdr.version != MINIDUMP_VERSION) {
110 		_kvm_err(kd, kd->program, "wrong minidump version. expected %d got %d",
111 		    MINIDUMP_VERSION, vmst->hdr.version);
112 		return (-1);
113 	}
114 	vmst->hdr.msgbufsize = le32toh(vmst->hdr.msgbufsize);
115 	vmst->hdr.bitmapsize = le32toh(vmst->hdr.bitmapsize);
116 	vmst->hdr.ptesize = le32toh(vmst->hdr.ptesize);
117 	vmst->hdr.kernbase = le32toh(vmst->hdr.kernbase);
118 	vmst->hdr.paemode = le32toh(vmst->hdr.paemode);
119 
120 	/* Skip header and msgbuf */
121 	off = I386_PAGE_SIZE + i386_round_page(vmst->hdr.msgbufsize);
122 
123 	sparse_off = off + i386_round_page(vmst->hdr.bitmapsize) +
124 	    i386_round_page(vmst->hdr.ptesize);
125 	if (_kvm_pt_init(kd, vmst->hdr.bitmapsize, off, sparse_off,
126 	    I386_PAGE_SIZE, sizeof(uint32_t)) == -1) {
127 		return (-1);
128 	}
129 	off += i386_round_page(vmst->hdr.bitmapsize);
130 
131 	if (_kvm_pmap_init(kd, vmst->hdr.ptesize, off) == -1) {
132 		return (-1);
133 	}
134 	off += i386_round_page(vmst->hdr.ptesize);
135 
136 	return (0);
137 }
138 
139 static int
140 _i386_minidump_vatop_pae(kvm_t *kd, kvaddr_t va, off_t *pa)
141 {
142 	struct vmstate *vm;
143 	i386_physaddr_pae_t offset;
144 	i386_pte_pae_t pte;
145 	kvaddr_t pteindex;
146 	i386_physaddr_pae_t a;
147 	off_t ofs;
148 
149 	vm = kd->vmst;
150 	offset = va & I386_PAGE_MASK;
151 
152 	if (va >= vm->hdr.kernbase) {
153 		pteindex = (va - vm->hdr.kernbase) >> I386_PAGE_SHIFT;
154 		if (pteindex >= vm->hdr.ptesize / sizeof(pte))
155 			goto invalid;
156 		pte = _i386_pte_pae_get(kd, pteindex);
157 		if ((pte & I386_PG_V) == 0) {
158 			_kvm_err(kd, kd->program,
159 			    "_i386_minidump_vatop_pae: pte not valid");
160 			goto invalid;
161 		}
162 		a = pte & I386_PG_FRAME_PAE;
163 		ofs = _kvm_pt_find(kd, a, I386_PAGE_SIZE);
164 		if (ofs == -1) {
165 			_kvm_err(kd, kd->program,
166 	    "_i386_minidump_vatop_pae: physical address 0x%jx not in minidump",
167 			    (uintmax_t)a);
168 			goto invalid;
169 		}
170 		*pa = ofs + offset;
171 		return (I386_PAGE_SIZE - offset);
172 	} else {
173 		_kvm_err(kd, kd->program,
174 	    "_i386_minidump_vatop_pae: virtual address 0x%jx not minidumped",
175 		    (uintmax_t)va);
176 		goto invalid;
177 	}
178 
179 invalid:
180 	_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
181 	return (0);
182 }
183 
184 static int
185 _i386_minidump_vatop(kvm_t *kd, kvaddr_t va, off_t *pa)
186 {
187 	struct vmstate *vm;
188 	i386_physaddr_t offset;
189 	i386_pte_t pte;
190 	kvaddr_t pteindex;
191 	i386_physaddr_t a;
192 	off_t ofs;
193 
194 	vm = kd->vmst;
195 	offset = va & I386_PAGE_MASK;
196 
197 	if (va >= vm->hdr.kernbase) {
198 		pteindex = (va - vm->hdr.kernbase) >> I386_PAGE_SHIFT;
199 		if (pteindex >= vm->hdr.ptesize / sizeof(pte))
200 			goto invalid;
201 		pte = _i386_pte_get(kd, pteindex);
202 		if ((pte & I386_PG_V) == 0) {
203 			_kvm_err(kd, kd->program,
204 			    "_i386_minidump_vatop: pte not valid");
205 			goto invalid;
206 		}
207 		a = pte & I386_PG_FRAME;
208 		ofs = _kvm_pt_find(kd, a, I386_PAGE_SIZE);
209 		if (ofs == -1) {
210 			_kvm_err(kd, kd->program,
211 	    "_i386_minidump_vatop: physical address 0x%jx not in minidump",
212 			    (uintmax_t)a);
213 			goto invalid;
214 		}
215 		*pa = ofs + offset;
216 		return (I386_PAGE_SIZE - offset);
217 	} else {
218 		_kvm_err(kd, kd->program,
219 	    "_i386_minidump_vatop: virtual address 0x%jx not minidumped",
220 		    (uintmax_t)va);
221 		goto invalid;
222 	}
223 
224 invalid:
225 	_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
226 	return (0);
227 }
228 
229 static int
230 _i386_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
231 {
232 
233 	if (ISALIVE(kd)) {
234 		_kvm_err(kd, 0, "_i386_minidump_kvatop called in live kernel!");
235 		return (0);
236 	}
237 	if (kd->vmst->hdr.paemode)
238 		return (_i386_minidump_vatop_pae(kd, va, pa));
239 	else
240 		return (_i386_minidump_vatop(kd, va, pa));
241 }
242 
243 static vm_prot_t
244 _i386_entry_to_prot(uint64_t pte)
245 {
246 	vm_prot_t prot = VM_PROT_READ;
247 
248 	/* Source: i386/pmap.c:pmap_protect() */
249 	if (pte & I386_PG_RW)
250 		prot |= VM_PROT_WRITE;
251 	if ((pte & I386_PG_NX) == 0)
252 		prot |= VM_PROT_EXECUTE;
253 
254 	return prot;
255 }
256 
257 struct i386_iter {
258 	kvm_t *kd;
259 	u_long nptes;
260 	u_long pteindex;
261 };
262 
263 static void
264 _i386_iterator_init(struct i386_iter *it, kvm_t *kd)
265 {
266 	struct vmstate *vm = kd->vmst;
267 
268 	it->kd = kd;
269 	it->pteindex = 0;
270 	if (vm->hdr.paemode) {
271 		it->nptes = vm->hdr.ptesize / sizeof(i386_pte_pae_t);
272 	} else {
273 		it->nptes = vm->hdr.ptesize / sizeof(i386_pte_t);
274 	}
275 	return;
276 }
277 
278 static int
279 _i386_iterator_next(struct i386_iter *it, u_long *pa, u_long *va, u_long *dva,
280     vm_prot_t *prot)
281 {
282 	struct vmstate *vm = it->kd->vmst;
283 	i386_pte_t pte32;
284 	i386_pte_pae_t pte64;
285 	int found = 0;
286 
287 	*dva = 0;
288 	for (; it->pteindex < it->nptes && found == 0; it->pteindex++) {
289 		if (vm->hdr.paemode) {
290 			pte64 = _i386_pte_pae_get(it->kd, it->pteindex);
291 			if ((pte64 & I386_PG_V) == 0)
292 				continue;
293 			*prot = _i386_entry_to_prot(pte64);
294 			*pa = pte64 & I386_PG_FRAME_PAE;
295 		} else {
296 			pte32 = _i386_pte_get(it->kd, it->pteindex);
297 			if ((pte32 & I386_PG_V) == 0)
298 				continue;
299 			*prot = _i386_entry_to_prot(pte32);
300 			*pa = pte32 & I386_PG_FRAME;
301 		}
302 		*va = vm->hdr.kernbase + (it->pteindex << I386_PAGE_SHIFT);
303 		found = 1;
304 	}
305 	return found;
306 }
307 
308 static int
309 _i386_minidump_walk_pages(kvm_t *kd, kvm_walk_pages_cb_t *cb, void *arg)
310 {
311 	struct i386_iter it;
312 	u_long dva, pa, va;
313 	vm_prot_t prot;
314 
315 	_i386_iterator_init(&it, kd);
316 	while (_i386_iterator_next(&it, &pa, &va, &dva, &prot)) {
317 		if (!_kvm_visit_cb(kd, cb, arg, pa, va, dva,
318 		    prot, I386_PAGE_SIZE, 0)) {
319 			return (0);
320 		}
321 	}
322 	return (1);
323 }
324 
325 static struct kvm_arch kvm_i386_minidump = {
326 	.ka_probe = _i386_minidump_probe,
327 	.ka_initvtop = _i386_minidump_initvtop,
328 	.ka_freevtop = _i386_minidump_freevtop,
329 	.ka_kvatop = _i386_minidump_kvatop,
330 	.ka_native = _i386_native,
331 	.ka_walk_pages = _i386_minidump_walk_pages,
332 };
333 
334 KVM_ARCH(kvm_i386_minidump);
335