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