minidump_machdep.c revision 225736
1/*-
2 * Copyright (c) 2008 Semihalf, Grzegorz Bernacki
3 * All rights reserved.
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 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * from: FreeBSD: src/sys/i386/i386/minidump_machdep.c,v 1.6 2008/08/17 23:27:27
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: stable/9/sys/arm/arm/minidump_machdep.c 221173 2011-04-28 16:02:05Z attilio $");
31
32#include "opt_watchdog.h"
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/conf.h>
37#include <sys/cons.h>
38#include <sys/kernel.h>
39#include <sys/kerneldump.h>
40#include <sys/msgbuf.h>
41#ifdef SW_WATCHDOG
42#include <sys/watchdog.h>
43#endif
44#include <vm/vm.h>
45#include <vm/pmap.h>
46#include <machine/pmap.h>
47#include <machine/atomic.h>
48#include <machine/elf.h>
49#include <machine/md_var.h>
50#include <machine/vmparam.h>
51#include <machine/minidump.h>
52#include <machine/cpufunc.h>
53
54CTASSERT(sizeof(struct kerneldumpheader) == 512);
55
56/*
57 * Don't touch the first SIZEOF_METADATA bytes on the dump device. This
58 * is to protect us from metadata and to protect metadata from us.
59 */
60#define	SIZEOF_METADATA		(64*1024)
61
62uint32_t *vm_page_dump;
63int vm_page_dump_size;
64
65static struct kerneldumpheader kdh;
66static off_t dumplo;
67
68/* Handle chunked writes. */
69static size_t fragsz, offset;
70static void *dump_va;
71static uint64_t counter, progress;
72
73CTASSERT(sizeof(*vm_page_dump) == 4);
74
75static int
76is_dumpable(vm_paddr_t pa)
77{
78	int i;
79
80	for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) {
81		if (pa >= dump_avail[i] && pa < dump_avail[i + 1])
82			return (1);
83	}
84	return (0);
85}
86
87#define PG2MB(pgs) (((pgs) + (1 << 8) - 1) >> 8)
88
89static int
90blk_flush(struct dumperinfo *di)
91{
92	int error;
93
94	if (fragsz == 0)
95		return (0);
96
97	error = dump_write(di, (char*)dump_va + offset, 0, dumplo, fragsz - offset);
98	dumplo += (fragsz - offset);
99	fragsz = 0;
100	offset = 0;
101	return (error);
102}
103
104static int
105blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz)
106{
107	size_t len;
108	int error, i, c;
109	u_int maxdumpsz;
110
111	maxdumpsz = di->maxiosize;
112
113	if (maxdumpsz == 0)	/* seatbelt */
114		maxdumpsz = PAGE_SIZE;
115
116	error = 0;
117
118	if (ptr != NULL && pa != 0) {
119		printf("cant have both va and pa!\n");
120		return (EINVAL);
121	}
122
123	if (ptr != NULL) {
124		/* If we're doing a virtual dump, flush any pre-existing pa pages */
125		error = blk_flush(di);
126		if (error)
127			return (error);
128	}
129
130	while (sz) {
131		if (fragsz == 0) {
132			offset = pa & PAGE_MASK;
133			fragsz += offset;
134		}
135		len = maxdumpsz - fragsz;
136		if (len > sz)
137			len = sz;
138		counter += len;
139		progress -= len;
140
141		if (counter >> 22) {
142			printf(" %lld", PG2MB(progress >> PAGE_SHIFT));
143			counter &= (1<<22) - 1;
144		}
145
146#ifdef SW_WATCHDOG
147		wdog_kern_pat(WD_LASTVAL);
148#endif
149		if (ptr) {
150			error = dump_write(di, ptr, 0, dumplo, len);
151			if (error)
152				return (error);
153			dumplo += len;
154			ptr += len;
155			sz -= len;
156		} else {
157			for (i = 0; i < len; i += PAGE_SIZE)
158				dump_va = pmap_kenter_temp(pa + i,
159				    (i + fragsz) >> PAGE_SHIFT);
160			fragsz += len;
161			pa += len;
162			sz -= len;
163			if (fragsz == maxdumpsz) {
164				error = blk_flush(di);
165				if (error)
166					return (error);
167			}
168		}
169
170		/* Check for user abort. */
171		c = cncheckc();
172		if (c == 0x03)
173			return (ECANCELED);
174		if (c != -1)
175			printf(" (CTRL-C to abort) ");
176	}
177
178	return (0);
179}
180
181static int
182blk_write_cont(struct dumperinfo *di, vm_paddr_t pa, size_t sz)
183{
184	int error;
185
186	error = blk_write(di, 0, pa, sz);
187	if (error)
188		return (error);
189
190	error = blk_flush(di);
191	if (error)
192		return (error);
193
194	return (0);
195}
196
197/* A fake page table page, to avoid having to handle both 4K and 2M pages */
198static pt_entry_t fakept[NPTEPG];
199
200void
201minidumpsys(struct dumperinfo *di)
202{
203	struct minidumphdr mdhdr;
204	uint64_t dumpsize;
205	uint32_t ptesize;
206	uint32_t bits;
207	uint32_t pa, prev_pa = 0, count = 0;
208	vm_offset_t va;
209	pd_entry_t *pdp;
210	pt_entry_t *pt, *ptp;
211	int i, k, bit, error;
212	char *addr;
213
214	/* Flush cache */
215	cpu_idcache_wbinv_all();
216	cpu_l2cache_wbinv_all();
217
218	counter = 0;
219	/* Walk page table pages, set bits in vm_page_dump */
220	ptesize = 0;
221	for (va = KERNBASE; va < kernel_vm_end; va += NBPDR) {
222		/*
223		 * We always write a page, even if it is zero. Each
224		 * page written corresponds to 2MB of space
225		 */
226		ptesize += L2_TABLE_SIZE_REAL;
227		pmap_get_pde_pte(pmap_kernel(), va, &pdp, &ptp);
228		if (pmap_pde_v(pdp) && pmap_pde_section(pdp)) {
229			/* This is a section mapping 1M page. */
230			pa = (*pdp & L1_S_ADDR_MASK) | (va & ~L1_S_ADDR_MASK);
231			for (k = 0; k < (L1_S_SIZE / PAGE_SIZE); k++) {
232				if (is_dumpable(pa))
233					dump_add_page(pa);
234				pa += PAGE_SIZE;
235			}
236			continue;
237		}
238		if (pmap_pde_v(pdp) && pmap_pde_page(pdp)) {
239			/* Set bit for each valid page in this 1MB block */
240			addr = pmap_kenter_temp(*pdp & L1_C_ADDR_MASK, 0);
241			pt = (pt_entry_t*)(addr +
242			    (((uint32_t)*pdp  & L1_C_ADDR_MASK) & PAGE_MASK));
243			for (k = 0; k < 256; k++) {
244				if ((pt[k] & L2_TYPE_MASK) == L2_TYPE_L) {
245					pa = (pt[k] & L2_L_FRAME) |
246					    (va & L2_L_OFFSET);
247					for (i = 0; i < 16; i++) {
248						if (is_dumpable(pa))
249							dump_add_page(pa);
250						k++;
251						pa += PAGE_SIZE;
252					}
253				} else if ((pt[k] & L2_TYPE_MASK) == L2_TYPE_S) {
254					pa = (pt[k] & L2_S_FRAME) |
255					    (va & L2_S_OFFSET);
256					if (is_dumpable(pa))
257						dump_add_page(pa);
258				}
259			}
260		} else {
261			/* Nothing, we're going to dump a null page */
262		}
263	}
264
265	/* Calculate dump size. */
266	dumpsize = ptesize;
267	dumpsize += round_page(msgbufp->msg_size);
268	dumpsize += round_page(vm_page_dump_size);
269
270	for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) {
271		bits = vm_page_dump[i];
272		while (bits) {
273			bit = ffs(bits) - 1;
274			pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) +
275			    bit) * PAGE_SIZE;
276			/* Clear out undumpable pages now if needed */
277			if (is_dumpable(pa))
278				dumpsize += PAGE_SIZE;
279			else
280				dump_drop_page(pa);
281			bits &= ~(1ul << bit);
282		}
283	}
284
285	dumpsize += PAGE_SIZE;
286
287	/* Determine dump offset on device. */
288	if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
289		error = ENOSPC;
290		goto fail;
291	}
292
293	dumplo = di->mediaoffset + di->mediasize - dumpsize;
294	dumplo -= sizeof(kdh) * 2;
295	progress = dumpsize;
296
297	/* Initialize mdhdr */
298	bzero(&mdhdr, sizeof(mdhdr));
299	strcpy(mdhdr.magic, MINIDUMP_MAGIC);
300	mdhdr.version = MINIDUMP_VERSION;
301	mdhdr.msgbufsize = msgbufp->msg_size;
302	mdhdr.bitmapsize = vm_page_dump_size;
303	mdhdr.ptesize = ptesize;
304	mdhdr.kernbase = KERNBASE;
305
306	mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_ARM_VERSION, dumpsize,
307	    di->blocksize);
308
309	printf("Physical memory: %u MB\n", ptoa((uintmax_t)physmem) / 1048576);
310	printf("Dumping %llu MB:", (long long)dumpsize >> 20);
311
312	/* Dump leader */
313	error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
314	if (error)
315		goto fail;
316	dumplo += sizeof(kdh);
317
318	/* Dump my header */
319	bzero(&fakept, sizeof(fakept));
320	bcopy(&mdhdr, &fakept, sizeof(mdhdr));
321	error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE);
322	if (error)
323		goto fail;
324
325	/* Dump msgbuf up front */
326	error = blk_write(di, (char *)msgbufp->msg_ptr, 0, round_page(msgbufp->msg_size));
327	if (error)
328		goto fail;
329
330	/* Dump bitmap */
331	error = blk_write(di, (char *)vm_page_dump, 0,
332	    round_page(vm_page_dump_size));
333	if (error)
334		goto fail;
335
336	/* Dump kernel page table pages */
337	for (va = KERNBASE; va < kernel_vm_end; va += NBPDR) {
338		/* We always write a page, even if it is zero */
339		pmap_get_pde_pte(pmap_kernel(), va, &pdp, &ptp);
340
341		if (pmap_pde_v(pdp) && pmap_pde_section(pdp))  {
342			if (count) {
343				error = blk_write_cont(di, prev_pa,
344				    count * L2_TABLE_SIZE_REAL);
345				if (error)
346					goto fail;
347				count = 0;
348				prev_pa = 0;
349			}
350			/* This is a single 2M block. Generate a fake PTP */
351			pa = (*pdp & L1_S_ADDR_MASK) | (va & ~L1_S_ADDR_MASK);
352			for (k = 0; k < (L1_S_SIZE / PAGE_SIZE); k++) {
353				fakept[k] = L2_S_PROTO | (pa + (k * PAGE_SIZE)) |
354				    L2_S_PROT(PTE_KERNEL,
355				    VM_PROT_READ | VM_PROT_WRITE);
356			}
357			error = blk_write(di, (char *)&fakept, 0,
358			    L2_TABLE_SIZE_REAL);
359			if (error)
360				goto fail;
361			/* Flush, in case we reuse fakept in the same block */
362			error = blk_flush(di);
363			if (error)
364				goto fail;
365			continue;
366		}
367		if (pmap_pde_v(pdp) && pmap_pde_page(pdp)) {
368			pa = *pdp & L1_C_ADDR_MASK;
369			if (!count) {
370				prev_pa = pa;
371				count++;
372			}
373			else {
374				if (pa == (prev_pa + count * L2_TABLE_SIZE_REAL))
375					count++;
376				else {
377					error = blk_write_cont(di, prev_pa,
378					    count * L2_TABLE_SIZE_REAL);
379					if (error)
380						goto fail;
381					count = 1;
382					prev_pa = pa;
383				}
384			}
385		} else {
386			if (count) {
387				error = blk_write_cont(di, prev_pa,
388				    count * L2_TABLE_SIZE_REAL);
389				if (error)
390					goto fail;
391				count = 0;
392				prev_pa = 0;
393			}
394			bzero(fakept, sizeof(fakept));
395			error = blk_write(di, (char *)&fakept, 0,
396			    L2_TABLE_SIZE_REAL);
397			if (error)
398				goto fail;
399			/* Flush, in case we reuse fakept in the same block */
400			error = blk_flush(di);
401			if (error)
402				goto fail;
403		}
404	}
405
406	if (count) {
407		error = blk_write_cont(di, prev_pa, count * L2_TABLE_SIZE_REAL);
408		if (error)
409			goto fail;
410		count = 0;
411		prev_pa = 0;
412	}
413
414	/* Dump memory chunks */
415	for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) {
416		bits = vm_page_dump[i];
417		while (bits) {
418			bit = ffs(bits) - 1;
419			pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) +
420			    bit) * PAGE_SIZE;
421			if (!count) {
422				prev_pa = pa;
423				count++;
424			} else {
425				if (pa == (prev_pa + count * PAGE_SIZE))
426					count++;
427				else {
428					error = blk_write_cont(di, prev_pa,
429					    count * PAGE_SIZE);
430					if (error)
431						goto fail;
432					count = 1;
433					prev_pa = pa;
434				}
435			}
436			bits &= ~(1ul << bit);
437		}
438	}
439	if (count) {
440		error = blk_write_cont(di, prev_pa, count * PAGE_SIZE);
441		if (error)
442			goto fail;
443		count = 0;
444		prev_pa = 0;
445	}
446
447	/* Dump trailer */
448	error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
449	if (error)
450		goto fail;
451	dumplo += sizeof(kdh);
452
453	/* Signal completion, signoff and exit stage left. */
454	dump_write(di, NULL, 0, 0, 0);
455	printf("\nDump complete\n");
456	return;
457
458fail:
459	if (error < 0)
460		error = -error;
461
462	if (error == ECANCELED)
463		printf("\nDump aborted\n");
464	else if (error == ENOSPC)
465		printf("\nDump failed. Partition too small.\n");
466	else
467		printf("\n** DUMP FAILED (ERROR %d) **\n", error);
468}
469
470void
471dump_add_page(vm_paddr_t pa)
472{
473	int idx, bit;
474
475	pa >>= PAGE_SHIFT;
476	idx = pa >> 5;		/* 2^5 = 32 */
477	bit = pa & 31;
478	atomic_set_int(&vm_page_dump[idx], 1ul << bit);
479}
480
481void
482dump_drop_page(vm_paddr_t pa)
483{
484	int idx, bit;
485
486	pa >>= PAGE_SHIFT;
487	idx = pa >> 5;		/* 2^5 = 32 */
488	bit = pa & 31;
489	atomic_clear_int(&vm_page_dump[idx], 1ul << bit);
490}
491