xref: /freebsd/sys/arm64/arm64/minidump_machdep.c (revision b0b1dbdd)
1 /*-
2  * Copyright (c) 2006 Peter Wemm
3  * Copyright (c) 2015 The FreeBSD Foundation
4  * All rights reserved.
5  *
6  * This software was developed by Andrew Turner under
7  * sponsorship from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 
35 #include "opt_watchdog.h"
36 
37 #include "opt_watchdog.h"
38 
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/conf.h>
42 #include <sys/cons.h>
43 #include <sys/kernel.h>
44 #include <sys/kerneldump.h>
45 #include <sys/msgbuf.h>
46 #include <sys/watchdog.h>
47 
48 #include <vm/vm.h>
49 #include <vm/vm_param.h>
50 #include <vm/vm_page.h>
51 #include <vm/vm_phys.h>
52 #include <vm/pmap.h>
53 
54 #include <machine/md_var.h>
55 #include <machine/pte.h>
56 #include <machine/minidump.h>
57 
58 CTASSERT(sizeof(struct kerneldumpheader) == 512);
59 
60 /*
61  * Don't touch the first SIZEOF_METADATA bytes on the dump device. This
62  * is to protect us from metadata and to protect metadata from us.
63  */
64 #define	SIZEOF_METADATA		(64*1024)
65 
66 uint64_t *vm_page_dump;
67 int vm_page_dump_size;
68 
69 static struct kerneldumpheader kdh;
70 static off_t dumplo;
71 
72 /* Handle chunked writes. */
73 static size_t fragsz;
74 static void *dump_va;
75 static size_t counter, progress, dumpsize;
76 
77 static uint64_t tmpbuffer[PAGE_SIZE / sizeof(uint64_t)];
78 
79 CTASSERT(sizeof(*vm_page_dump) == 8);
80 
81 static int
82 is_dumpable(vm_paddr_t pa)
83 {
84 	vm_page_t m;
85 	int i;
86 
87 	if ((m = vm_phys_paddr_to_vm_page(pa)) != NULL)
88 		return ((m->flags & PG_NODUMP) == 0);
89 	for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) {
90 		if (pa >= dump_avail[i] && pa < dump_avail[i + 1])
91 			return (1);
92 	}
93 	return (0);
94 }
95 
96 static int
97 blk_flush(struct dumperinfo *di)
98 {
99 	int error;
100 
101 	if (fragsz == 0)
102 		return (0);
103 
104 	error = dump_write(di, dump_va, 0, dumplo, fragsz);
105 	dumplo += fragsz;
106 	fragsz = 0;
107 	return (error);
108 }
109 
110 static struct {
111 	int min_per;
112 	int max_per;
113 	int visited;
114 } progress_track[10] = {
115 	{  0,  10, 0},
116 	{ 10,  20, 0},
117 	{ 20,  30, 0},
118 	{ 30,  40, 0},
119 	{ 40,  50, 0},
120 	{ 50,  60, 0},
121 	{ 60,  70, 0},
122 	{ 70,  80, 0},
123 	{ 80,  90, 0},
124 	{ 90, 100, 0}
125 };
126 
127 static void
128 report_progress(size_t progress, size_t dumpsize)
129 {
130 	int sofar, i;
131 
132 	sofar = 100 - ((progress * 100) / dumpsize);
133 	for (i = 0; i < nitems(progress_track); i++) {
134 		if (sofar < progress_track[i].min_per ||
135 		    sofar > progress_track[i].max_per)
136 			continue;
137 		if (progress_track[i].visited)
138 			return;
139 		progress_track[i].visited = 1;
140 		printf("..%d%%", sofar);
141 		return;
142 	}
143 }
144 
145 static int
146 blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz)
147 {
148 	size_t len;
149 	int error, c;
150 	u_int maxdumpsz;
151 
152 	maxdumpsz = min(di->maxiosize, MAXDUMPPGS * PAGE_SIZE);
153 	if (maxdumpsz == 0)	/* seatbelt */
154 		maxdumpsz = PAGE_SIZE;
155 	error = 0;
156 	if ((sz % PAGE_SIZE) != 0) {
157 		printf("size not page aligned\n");
158 		return (EINVAL);
159 	}
160 	if (ptr != NULL && pa != 0) {
161 		printf("cant have both va and pa!\n");
162 		return (EINVAL);
163 	}
164 	if ((((uintptr_t)pa) % PAGE_SIZE) != 0) {
165 		printf("address not page aligned %p\n", ptr);
166 		return (EINVAL);
167 	}
168 	if (ptr != NULL) {
169 		/*
170 		 * If we're doing a virtual dump, flush any
171 		 * pre-existing pa pages.
172 		 */
173 		error = blk_flush(di);
174 		if (error)
175 			return (error);
176 	}
177 	while (sz) {
178 		len = maxdumpsz - fragsz;
179 		if (len > sz)
180 			len = sz;
181 		counter += len;
182 		progress -= len;
183 		if (counter >> 22) {
184 			report_progress(progress, dumpsize);
185 			counter &= (1 << 22) - 1;
186 		}
187 
188 		wdog_kern_pat(WD_LASTVAL);
189 
190 		if (ptr) {
191 			error = dump_write(di, ptr, 0, dumplo, len);
192 			if (error)
193 				return (error);
194 			dumplo += len;
195 			ptr += len;
196 			sz -= len;
197 		} else {
198 			dump_va = (void *)PHYS_TO_DMAP(pa);
199 			fragsz += len;
200 			pa += len;
201 			sz -= len;
202 			error = blk_flush(di);
203 			if (error)
204 				return (error);
205 		}
206 
207 		/* Check for user abort. */
208 		c = cncheckc();
209 		if (c == 0x03)
210 			return (ECANCELED);
211 		if (c != -1)
212 			printf(" (CTRL-C to abort) ");
213 	}
214 
215 	return (0);
216 }
217 
218 int
219 minidumpsys(struct dumperinfo *di)
220 {
221 	pd_entry_t *l0, *l1, *l2;
222 	pt_entry_t *l3;
223 	uint32_t pmapsize;
224 	vm_offset_t va;
225 	vm_paddr_t pa;
226 	int error;
227 	uint64_t bits;
228 	int i, bit;
229 	int retry_count;
230 	struct minidumphdr mdhdr;
231 
232 	retry_count = 0;
233  retry:
234 	retry_count++;
235 	error = 0;
236 	pmapsize = 0;
237 	for (va = VM_MIN_KERNEL_ADDRESS; va < kernel_vm_end; va += L2_SIZE) {
238 		pmapsize += PAGE_SIZE;
239 		if (!pmap_get_tables(pmap_kernel(), va, &l0, &l1, &l2, &l3))
240 			continue;
241 
242 		/* We should always be using the l2 table for kvm */
243 		if (l2 == NULL)
244 			continue;
245 
246 		if ((*l2 & ATTR_DESCR_MASK) == L2_BLOCK) {
247 			pa = *l2 & ~ATTR_MASK;
248 			for (i = 0; i < Ln_ENTRIES; i++, pa += PAGE_SIZE) {
249 				if (is_dumpable(pa))
250 					dump_add_page(pa);
251 			}
252 		} else if ((*l2 & ATTR_DESCR_MASK) == L2_TABLE) {
253 			for (i = 0; i < Ln_ENTRIES; i++) {
254 				if ((l3[i] & ATTR_DESCR_MASK) != L3_PAGE)
255 					continue;
256 				pa = l3[i] & ~ATTR_MASK;
257 				if (is_dumpable(pa))
258 					dump_add_page(pa);
259 			}
260 		}
261 	}
262 
263 	/* Calculate dump size. */
264 	dumpsize = pmapsize;
265 	dumpsize += round_page(msgbufp->msg_size);
266 	dumpsize += round_page(vm_page_dump_size);
267 	for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) {
268 		bits = vm_page_dump[i];
269 		while (bits) {
270 			bit = ffsl(bits) - 1;
271 			pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) +
272 			    bit) * PAGE_SIZE;
273 			/* Clear out undumpable pages now if needed */
274 			if (is_dumpable(pa))
275 				dumpsize += PAGE_SIZE;
276 			else
277 				dump_drop_page(pa);
278 			bits &= ~(1ul << bit);
279 		}
280 	}
281 	dumpsize += PAGE_SIZE;
282 
283 	/* Determine dump offset on device. */
284 	if (di->mediasize < SIZEOF_METADATA + dumpsize + di->blocksize * 2 +
285 	    kerneldumpcrypto_dumpkeysize(di->kdc)) {
286 		error = E2BIG;
287 		goto fail;
288 	}
289 	dumplo = di->mediaoffset + di->mediasize - dumpsize;
290 	dumplo -= di->blocksize * 2;
291 	dumplo -= kerneldumpcrypto_dumpkeysize(di->kdc);
292 	progress = dumpsize;
293 
294 	/* Initialize kernel dump crypto. */
295 	error = kerneldumpcrypto_init(di->kdc);
296 	if (error)
297 		goto fail;
298 
299 	/* Initialize mdhdr */
300 	bzero(&mdhdr, sizeof(mdhdr));
301 	strcpy(mdhdr.magic, MINIDUMP_MAGIC);
302 	mdhdr.version = MINIDUMP_VERSION;
303 	mdhdr.msgbufsize = msgbufp->msg_size;
304 	mdhdr.bitmapsize = vm_page_dump_size;
305 	mdhdr.pmapsize = pmapsize;
306 	mdhdr.kernbase = VM_MIN_KERNEL_ADDRESS;
307 	mdhdr.dmapphys = DMAP_MIN_PHYSADDR;
308 	mdhdr.dmapbase = DMAP_MIN_ADDRESS;
309 	mdhdr.dmapend = DMAP_MAX_ADDRESS;
310 
311 	mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_AARCH64_VERSION,
312 	    dumpsize, kerneldumpcrypto_dumpkeysize(di->kdc), di->blocksize);
313 
314 	printf("Dumping %llu out of %ju MB:", (long long)dumpsize >> 20,
315 	    ptoa((uintmax_t)physmem) / 1048576);
316 
317 	/* Dump leader */
318 	error = dump_write_header(di, &kdh, 0, dumplo);
319 	if (error)
320 		goto fail;
321 	dumplo += di->blocksize;
322 
323 	/* Dump key */
324 	error = dump_write_key(di, 0, dumplo);
325 	if (error)
326 		goto fail;
327 	dumplo += kerneldumpcrypto_dumpkeysize(di->kdc);
328 
329 	/* Dump my header */
330 	bzero(&tmpbuffer, sizeof(tmpbuffer));
331 	bcopy(&mdhdr, &tmpbuffer, sizeof(mdhdr));
332 	error = blk_write(di, (char *)&tmpbuffer, 0, PAGE_SIZE);
333 	if (error)
334 		goto fail;
335 
336 	/* Dump msgbuf up front */
337 	error = blk_write(di, (char *)msgbufp->msg_ptr, 0,
338 	    round_page(msgbufp->msg_size));
339 	if (error)
340 		goto fail;
341 
342 	/* Dump bitmap */
343 	error = blk_write(di, (char *)vm_page_dump, 0,
344 	    round_page(vm_page_dump_size));
345 	if (error)
346 		goto fail;
347 
348 	/* Dump kernel page directory pages */
349 	bzero(&tmpbuffer, sizeof(tmpbuffer));
350 	for (va = VM_MIN_KERNEL_ADDRESS; va < kernel_vm_end; va += L2_SIZE) {
351 		if (!pmap_get_tables(pmap_kernel(), va, &l0, &l1, &l2, &l3)) {
352 			/* We always write a page, even if it is zero */
353 			error = blk_write(di, (char *)&tmpbuffer, 0, PAGE_SIZE);
354 			if (error)
355 				goto fail;
356 			/* flush, in case we reuse tmpbuffer in the same block*/
357 			error = blk_flush(di);
358 			if (error)
359 				goto fail;
360 		} else if (l2 == NULL) {
361 			pa = (*l1 & ~ATTR_MASK) | (va & L1_OFFSET);
362 
363 			/* Generate fake l3 entries based upon the l1 entry */
364 			for (i = 0; i < Ln_ENTRIES; i++) {
365 				tmpbuffer[i] = pa + (i * PAGE_SIZE) |
366 				    ATTR_DEFAULT | L3_PAGE;
367 			}
368 			/* We always write a page, even if it is zero */
369 			error = blk_write(di, (char *)&tmpbuffer, 0, PAGE_SIZE);
370 			if (error)
371 				goto fail;
372 			/* flush, in case we reuse tmpbuffer in the same block*/
373 			error = blk_flush(di);
374 			if (error)
375 				goto fail;
376 			bzero(&tmpbuffer, sizeof(tmpbuffer));
377 		} else if ((*l2 & ATTR_DESCR_MASK) == L2_BLOCK) {
378 			/* TODO: Handle an invalid L2 entry */
379 			pa = (*l2 & ~ATTR_MASK) | (va & L2_OFFSET);
380 
381 			/* Generate fake l3 entries based upon the l1 entry */
382 			for (i = 0; i < Ln_ENTRIES; i++) {
383 				tmpbuffer[i] = pa + (i * PAGE_SIZE) |
384 				    ATTR_DEFAULT | L3_PAGE;
385 			}
386 			/* We always write a page, even if it is zero */
387 			error = blk_write(di, (char *)&tmpbuffer, 0, PAGE_SIZE);
388 			if (error)
389 				goto fail;
390 			/* flush, in case we reuse fakepd in the same block */
391 			error = blk_flush(di);
392 			if (error)
393 				goto fail;
394 			bzero(&tmpbuffer, sizeof(tmpbuffer));
395 			continue;
396 		} else {
397 			pa = *l2 & ~ATTR_MASK;
398 
399 			/* We always write a page, even if it is zero */
400 			error = blk_write(di, NULL, pa, PAGE_SIZE);
401 			if (error)
402 				goto fail;
403 		}
404 	}
405 
406 	/* Dump memory chunks */
407 	/* XXX cluster it up and use blk_dump() */
408 	for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) {
409 		bits = vm_page_dump[i];
410 		while (bits) {
411 			bit = ffsl(bits) - 1;
412 			pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) +
413 			    bit) * PAGE_SIZE;
414 			error = blk_write(di, 0, pa, PAGE_SIZE);
415 			if (error)
416 				goto fail;
417 			bits &= ~(1ul << bit);
418 		}
419 	}
420 
421 	error = blk_flush(di);
422 	if (error)
423 		goto fail;
424 
425 	/* Dump trailer */
426 	error = dump_write_header(di, &kdh, 0, dumplo);
427 	if (error)
428 		goto fail;
429 	dumplo += di->blocksize;
430 
431 	/* Signal completion, signoff and exit stage left. */
432 	dump_write(di, NULL, 0, 0, 0);
433 	printf("\nDump complete\n");
434 	return (0);
435 
436  fail:
437 	if (error < 0)
438 		error = -error;
439 
440 	printf("\n");
441 	if (error == ENOSPC) {
442 		printf("Dump map grown while dumping. ");
443 		if (retry_count < 5) {
444 			printf("Retrying...\n");
445 			goto retry;
446 		}
447 		printf("Dump failed.\n");
448 	}
449 	else if (error == ECANCELED)
450 		printf("Dump aborted\n");
451 	else if (error == E2BIG)
452 		printf("Dump failed. Partition too small.\n");
453 	else
454 		printf("** DUMP FAILED (ERROR %d) **\n", error);
455 	return (error);
456 }
457 
458 void
459 dump_add_page(vm_paddr_t pa)
460 {
461 	int idx, bit;
462 
463 	pa >>= PAGE_SHIFT;
464 	idx = pa >> 6;		/* 2^6 = 64 */
465 	bit = pa & 63;
466 	atomic_set_long(&vm_page_dump[idx], 1ul << bit);
467 }
468 
469 void
470 dump_drop_page(vm_paddr_t pa)
471 {
472 	int idx, bit;
473 
474 	pa >>= PAGE_SHIFT;
475 	idx = pa >> 6;		/* 2^6 = 64 */
476 	bit = pa & 63;
477 	atomic_clear_long(&vm_page_dump[idx], 1ul << bit);
478 }
479