1be66ad11SAlex Hornung /*-
2be66ad11SAlex Hornung  * Copyright (c) 2006 Peter Wemm
3be66ad11SAlex Hornung  * All rights reserved.
4be66ad11SAlex Hornung  *
5be66ad11SAlex Hornung  * Redistribution and use in source and binary forms, with or without
6be66ad11SAlex Hornung  * modification, are permitted provided that the following conditions
7be66ad11SAlex Hornung  * are met:
8be66ad11SAlex Hornung  *
9be66ad11SAlex Hornung  * 1. Redistributions of source code must retain the above copyright
10be66ad11SAlex Hornung  *    notice, this list of conditions and the following disclaimer.
11be66ad11SAlex Hornung  * 2. Redistributions in binary form must reproduce the above copyright
12be66ad11SAlex Hornung  *    notice, this list of conditions and the following disclaimer in the
13be66ad11SAlex Hornung  *    documentation and/or other materials provided with the distribution.
14be66ad11SAlex Hornung  *
15be66ad11SAlex Hornung  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16be66ad11SAlex Hornung  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17be66ad11SAlex Hornung  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18be66ad11SAlex Hornung  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19be66ad11SAlex Hornung  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20be66ad11SAlex Hornung  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21be66ad11SAlex Hornung  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22be66ad11SAlex Hornung  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23be66ad11SAlex Hornung  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24be66ad11SAlex Hornung  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2532af04f7SSascha Wildner  *
2632af04f7SSascha Wildner  * $FreeBSD: src/sys/amd64/amd64/minidump_machdep.c,v 1.10 2009/05/29 21:27:12 jamie Exp $
27be66ad11SAlex Hornung  */
28be66ad11SAlex Hornung 
29be66ad11SAlex Hornung #include <sys/param.h>
30be66ad11SAlex Hornung #include <sys/systm.h>
31be66ad11SAlex Hornung #include <sys/conf.h>
32be66ad11SAlex Hornung #include <sys/cons.h>
33be66ad11SAlex Hornung #include <sys/device.h>
34be66ad11SAlex Hornung #include <sys/globaldata.h>
35be66ad11SAlex Hornung #include <sys/kernel.h>
36be66ad11SAlex Hornung #include <sys/kerneldump.h>
37be66ad11SAlex Hornung #include <sys/msgbuf.h>
38ce7866b8SMatthew Dillon #include <sys/kbio.h>
39be66ad11SAlex Hornung #include <vm/vm.h>
40be66ad11SAlex Hornung #include <vm/vm_kern.h>
41be66ad11SAlex Hornung #include <vm/pmap.h>
42be66ad11SAlex Hornung #include <machine/atomic.h>
43be66ad11SAlex Hornung #include <machine/elf.h>
44be66ad11SAlex Hornung #include <machine/globaldata.h>
45be66ad11SAlex Hornung #include <machine/md_var.h>
46be66ad11SAlex Hornung #include <machine/vmparam.h>
47be66ad11SAlex Hornung #include <machine/minidump.h>
48be66ad11SAlex Hornung 
49be66ad11SAlex Hornung CTASSERT(sizeof(struct kerneldumpheader) == 512);
50be66ad11SAlex Hornung 
51be66ad11SAlex Hornung /*
52be66ad11SAlex Hornung  * Don't touch the first SIZEOF_METADATA bytes on the dump device. This
53be66ad11SAlex Hornung  * is to protect us from metadata and to protect metadata from us.
54be66ad11SAlex Hornung  */
55be66ad11SAlex Hornung #define	SIZEOF_METADATA		(64*1024)
56be66ad11SAlex Hornung 
57be66ad11SAlex Hornung #define	MD_ALIGN(x)	(((off_t)(x) + PAGE_MASK) & ~PAGE_MASK)
58965b839fSSascha Wildner #define	DEV_ALIGN(x)	roundup2((off_t)(x), DEV_BSIZE)
59be66ad11SAlex Hornung 
60be66ad11SAlex Hornung uint64_t *vm_page_dump;
61d30a28ddSMatthew Dillon vm_offset_t vm_page_dump_size;
62be66ad11SAlex Hornung 
63be66ad11SAlex Hornung static struct kerneldumpheader kdh;
64be66ad11SAlex Hornung static off_t dumplo;
65be66ad11SAlex Hornung 
66be66ad11SAlex Hornung /* Handle chunked writes. */
67be66ad11SAlex Hornung static size_t fragsz;
68be66ad11SAlex Hornung static void *dump_va;
69be66ad11SAlex Hornung static size_t counter, progress;
70be66ad11SAlex Hornung 
71be66ad11SAlex Hornung CTASSERT(sizeof(*vm_page_dump) == 8);
72be66ad11SAlex Hornung 
73be66ad11SAlex Hornung static int
is_dumpable(vm_paddr_t pa)74be66ad11SAlex Hornung is_dumpable(vm_paddr_t pa)
75be66ad11SAlex Hornung {
76be66ad11SAlex Hornung 	int i;
77be66ad11SAlex Hornung 
7877c48adbSMatthew Dillon 	for (i = 0; dump_avail[i].phys_beg || dump_avail[i].phys_end; ++i) {
7977c48adbSMatthew Dillon 		if (pa >= dump_avail[i].phys_beg && pa < dump_avail[i].phys_end)
80be66ad11SAlex Hornung 			return (1);
81be66ad11SAlex Hornung 	}
82be66ad11SAlex Hornung 	return (0);
83be66ad11SAlex Hornung }
84be66ad11SAlex Hornung 
85be66ad11SAlex Hornung #define PG2MB(pgs) (((pgs) + (1 << 8) - 1) >> 8)
86be66ad11SAlex Hornung 
87be66ad11SAlex Hornung static int
blk_flush(struct dumperinfo * di)88be66ad11SAlex Hornung blk_flush(struct dumperinfo *di)
89be66ad11SAlex Hornung {
90be66ad11SAlex Hornung 	int error;
91be66ad11SAlex Hornung 
92be66ad11SAlex Hornung 	if (fragsz == 0)
93be66ad11SAlex Hornung 		return (0);
94be66ad11SAlex Hornung 
95be66ad11SAlex Hornung 	error = dev_ddump(di->priv, dump_va, 0, dumplo, fragsz);
96be66ad11SAlex Hornung 	dumplo += fragsz;
97be66ad11SAlex Hornung 	fragsz = 0;
98be66ad11SAlex Hornung 	return (error);
99be66ad11SAlex Hornung }
100be66ad11SAlex Hornung 
101be66ad11SAlex Hornung static int
blk_write(struct dumperinfo * di,char * ptr,vm_paddr_t pa,size_t sz)102be66ad11SAlex Hornung blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz)
103be66ad11SAlex Hornung {
104be66ad11SAlex Hornung 	size_t len;
105be66ad11SAlex Hornung 	int error, i, c;
106c6de20f8SFrançois Tigeot 	int max_iosize;
107be66ad11SAlex Hornung 
108be66ad11SAlex Hornung 	error = 0;
109fb8345e6SMatthew Dillon 	if ((sz & PAGE_MASK)) {
110be66ad11SAlex Hornung 		kprintf("size not page aligned\n");
111be66ad11SAlex Hornung 		return (EINVAL);
112be66ad11SAlex Hornung 	}
113be66ad11SAlex Hornung 	if (ptr != NULL && pa != 0) {
1143da63b93SSascha Wildner 		kprintf("can't have both va and pa!\n");
115be66ad11SAlex Hornung 		return (EINVAL);
116be66ad11SAlex Hornung 	}
117fb8345e6SMatthew Dillon 	if (pa != 0 && (((uintptr_t)pa) & PAGE_MASK) != 0) {
118be66ad11SAlex Hornung 		kprintf("address not page aligned\n");
119be66ad11SAlex Hornung 		return (EINVAL);
120be66ad11SAlex Hornung 	}
121be66ad11SAlex Hornung 	if (ptr != NULL) {
122fb8345e6SMatthew Dillon 		/*
123fb8345e6SMatthew Dillon 		 * If we're doing a virtual dump, flush any
124fb8345e6SMatthew Dillon 		 * pre-existing pa pages
125fb8345e6SMatthew Dillon 		 */
126be66ad11SAlex Hornung 		error = blk_flush(di);
127be66ad11SAlex Hornung 		if (error)
128be66ad11SAlex Hornung 			return (error);
129be66ad11SAlex Hornung 	}
130c6de20f8SFrançois Tigeot 	max_iosize = min(MAXPHYS, di->maxiosize);
131be66ad11SAlex Hornung 	while (sz) {
132c6de20f8SFrançois Tigeot 		len = max_iosize - fragsz;
133be66ad11SAlex Hornung 		if (len > sz)
134be66ad11SAlex Hornung 			len = sz;
135be66ad11SAlex Hornung 		counter += len;
136be66ad11SAlex Hornung 		progress -= len;
137be66ad11SAlex Hornung 		if (counter >> 24) {
138be66ad11SAlex Hornung 			kprintf(" %ld", PG2MB(progress >> PAGE_SHIFT));
139be66ad11SAlex Hornung 			counter &= (1<<24) - 1;
140be66ad11SAlex Hornung 		}
141be66ad11SAlex Hornung 		if (ptr) {
142fb8345e6SMatthew Dillon 			/*kprintf("s");*/
143be66ad11SAlex Hornung 			error = dev_ddump(di->priv, ptr, 0, dumplo, len);
144fb8345e6SMatthew Dillon 			/* kprintf("t");*/
145be66ad11SAlex Hornung 			if (error)
146be66ad11SAlex Hornung 				return (error);
147be66ad11SAlex Hornung 			dumplo += len;
148be66ad11SAlex Hornung 			ptr += len;
149be66ad11SAlex Hornung 			sz -= len;
150be66ad11SAlex Hornung 		} else {
151fb8345e6SMatthew Dillon 			for (i = 0; i < len; i += PAGE_SIZE) {
152fb8345e6SMatthew Dillon 				dump_va = pmap_kenter_temporary(pa + i,
153fb8345e6SMatthew Dillon 						(i + fragsz) >> PAGE_SHIFT);
154fb8345e6SMatthew Dillon 			}
155fb8345e6SMatthew Dillon 			smp_invltlb();
156be66ad11SAlex Hornung 			fragsz += len;
157be66ad11SAlex Hornung 			pa += len;
158be66ad11SAlex Hornung 			sz -= len;
159c6de20f8SFrançois Tigeot 			if (fragsz == max_iosize) {
160be66ad11SAlex Hornung 				error = blk_flush(di);
161be66ad11SAlex Hornung 				if (error)
162be66ad11SAlex Hornung 					return (error);
163be66ad11SAlex Hornung 			}
164be66ad11SAlex Hornung 		}
165fb8345e6SMatthew Dillon 	}
166be66ad11SAlex Hornung 
167be66ad11SAlex Hornung 	/* Check for user abort. */
168be66ad11SAlex Hornung 	c = cncheckc();
169be66ad11SAlex Hornung 	if (c == 0x03)
170be66ad11SAlex Hornung 		return (ECANCELED);
171ce7866b8SMatthew Dillon 	if (c != -1 && c != NOKEY)
172be66ad11SAlex Hornung 		kprintf(" (CTRL-C to abort) ");
173be66ad11SAlex Hornung 
174be66ad11SAlex Hornung 	return (0);
175be66ad11SAlex Hornung }
176be66ad11SAlex Hornung 
177be66ad11SAlex Hornung /* A fake page table page, to avoid having to handle both 4K and 2M pages */
178be66ad11SAlex Hornung static pt_entry_t fakept[NPTEPG];
179be66ad11SAlex Hornung 
180be66ad11SAlex Hornung void
minidumpsys(struct dumperinfo * di)181be66ad11SAlex Hornung minidumpsys(struct dumperinfo *di)
182be66ad11SAlex Hornung {
183be66ad11SAlex Hornung 	uint64_t dumpsize;
184d30a28ddSMatthew Dillon 	uint64_t ptesize;
185be66ad11SAlex Hornung 	vm_offset_t va;
186be66ad11SAlex Hornung 	vm_offset_t kern_end;
187be66ad11SAlex Hornung 	int error;
188be66ad11SAlex Hornung 	uint64_t bits;
189be66ad11SAlex Hornung 	uint64_t *pdp, *pd, *pt, pa;
190be66ad11SAlex Hornung 	int i, j, k, bit;
191d30a28ddSMatthew Dillon 	int kpdp, klo, khi;
192d30a28ddSMatthew Dillon 	int lpdp = -1;
193d30a28ddSMatthew Dillon 	long lpdpttl = 0;
194d30a28ddSMatthew Dillon 	struct minidumphdr2 mdhdr;
195be66ad11SAlex Hornung 	struct mdglobaldata *md;
196be66ad11SAlex Hornung 
197ce7866b8SMatthew Dillon 	cnpoll(TRUE);
198be66ad11SAlex Hornung 	counter = 0;
199d30a28ddSMatthew Dillon 
200d30a28ddSMatthew Dillon 	/*
201d30a28ddSMatthew Dillon 	 * minidump page table format is an array of PD entries (1GB pte's),
202d30a28ddSMatthew Dillon 	 * representing the entire user and kernel virtual address space
203d30a28ddSMatthew Dillon 	 * (256TB).
204d30a28ddSMatthew Dillon 	 *
205d30a28ddSMatthew Dillon 	 * However, we will only dump the KVM portion of this space.  And we
206d30a28ddSMatthew Dillon 	 * only copy the PDP pages for direct access, the PD and PT pages
207d30a28ddSMatthew Dillon 	 * will be included in the dump as part of the physical map.
208d30a28ddSMatthew Dillon 	 */
209d30a28ddSMatthew Dillon 	ptesize = NPML4EPG * NPDPEPG * 8;
210d30a28ddSMatthew Dillon 
211791c6551SMatthew Dillon 	/*
212791c6551SMatthew Dillon 	 * Walk page table pages, set bits in vm_page_dump.
213791c6551SMatthew Dillon 	 *
214791c6551SMatthew Dillon 	 * NOTE: kernel_vm_end can actually be below KERNBASE.
2157b7a555bSMatthew Dillon 	 * 	 Just use KvaEnd.  Also note that loops which go
2167b7a555bSMatthew Dillon 	 *	 all the way to the end of the address space might
2177b7a555bSMatthew Dillon 	 *	 overflow the loop variable.
218791c6551SMatthew Dillon 	 */
219be66ad11SAlex Hornung 	md = (struct mdglobaldata *)globaldata_find(0);
220be66ad11SAlex Hornung 
221791c6551SMatthew Dillon 	kern_end = KvaEnd;
222be66ad11SAlex Hornung 	if (kern_end < (vm_offset_t)&(md[ncpus]))
223be66ad11SAlex Hornung 		kern_end = (vm_offset_t)&(md[ncpus]);
224be66ad11SAlex Hornung 
225be66ad11SAlex Hornung 	pdp = (uint64_t *)PHYS_TO_DMAP(KPDPphys);
226791c6551SMatthew Dillon 	for (va = VM_MIN_KERNEL_ADDRESS; va < kern_end; va += NBPDR) {
2277b7a555bSMatthew Dillon 		/*
2287b7a555bSMatthew Dillon 		 * The loop probably overflows a 64-bit int due to NBPDR.
2297b7a555bSMatthew Dillon 		 */
2307b7a555bSMatthew Dillon 		if (va < VM_MIN_KERNEL_ADDRESS)
2317b7a555bSMatthew Dillon 			break;
2327b7a555bSMatthew Dillon 
233be66ad11SAlex Hornung 		/*
234d30a28ddSMatthew Dillon 		 * KPDPphys[] is relative to VM_MIN_KERNEL_ADDRESS. It
235d30a28ddSMatthew Dillon 		 * contains NKPML4E PDP pages (so we can get to all kernel
236d30a28ddSMatthew Dillon 		 * PD entries from this array).
237be66ad11SAlex Hornung 		 */
238d30a28ddSMatthew Dillon 		i = ((va - VM_MIN_KERNEL_ADDRESS) >> PDPSHIFT) &
239d30a28ddSMatthew Dillon 		    (NPML4EPG * NPDPEPG - 1);
240d30a28ddSMatthew Dillon 		if (i != lpdp) {
241d30a28ddSMatthew Dillon 			lpdp = i;
242d30a28ddSMatthew Dillon 			lpdpttl = 0;
243d30a28ddSMatthew Dillon 		}
244d30a28ddSMatthew Dillon 
245d30a28ddSMatthew Dillon 		/*
246d30a28ddSMatthew Dillon 		 * Calculate the PD index in the PDP.  Each PD represents 1GB.
247d30a28ddSMatthew Dillon 		 * KVA space can cover multiple PDP pages.  The PDP array
248d30a28ddSMatthew Dillon 		 * has been initialized for the entire kernel address space.
249d30a28ddSMatthew Dillon 		 *
250d30a28ddSMatthew Dillon 		 * We include the PD entries in the PDP in the dump
251d30a28ddSMatthew Dillon 		 */
252d30a28ddSMatthew Dillon 		i = ((va - VM_MIN_KERNEL_ADDRESS) >> PDPSHIFT) &
253d30a28ddSMatthew Dillon 		    (NPML4EPG * NPDPEPG - 1);
254*c713db65SAaron LI 		if ((pdp[i] & kernel_pmap->pmap_bits[PG_V_IDX]) == 0)
255be66ad11SAlex Hornung 			continue;
256d30a28ddSMatthew Dillon 
257d30a28ddSMatthew Dillon 		/*
258d30a28ddSMatthew Dillon 		 * Add the PD page from the PDP to the dump
259d30a28ddSMatthew Dillon 		 */
260d30a28ddSMatthew Dillon 		dump_add_page(pdp[i] & PG_FRAME);
261d30a28ddSMatthew Dillon 		lpdpttl += PAGE_SIZE;
262d30a28ddSMatthew Dillon 
263be66ad11SAlex Hornung 		pd = (uint64_t *)PHYS_TO_DMAP(pdp[i] & PG_FRAME);
264be66ad11SAlex Hornung 		j = ((va >> PDRSHIFT) & ((1ul << NPDEPGSHIFT) - 1));
265*c713db65SAaron LI 		if ((pd[j] & (kernel_pmap->pmap_bits[PG_PS_IDX] | kernel_pmap->pmap_bits[PG_V_IDX])) ==
266*c713db65SAaron LI 		    (kernel_pmap->pmap_bits[PG_PS_IDX] | kernel_pmap->pmap_bits[PG_V_IDX]))  {
267be66ad11SAlex Hornung 			/* This is an entire 2M page. */
268d30a28ddSMatthew Dillon 			lpdpttl += PAGE_SIZE * NPTEPG;
269be66ad11SAlex Hornung 			pa = pd[j] & PG_PS_FRAME;
270be66ad11SAlex Hornung 			for (k = 0; k < NPTEPG; k++) {
271be66ad11SAlex Hornung 				if (is_dumpable(pa))
272be66ad11SAlex Hornung 					dump_add_page(pa);
273be66ad11SAlex Hornung 				pa += PAGE_SIZE;
274be66ad11SAlex Hornung 			}
275*c713db65SAaron LI 		} else if ((pd[j] & kernel_pmap->pmap_bits[PG_V_IDX]) ==
276*c713db65SAaron LI 			   kernel_pmap->pmap_bits[PG_V_IDX]) {
277d30a28ddSMatthew Dillon 			/*
278d30a28ddSMatthew Dillon 			 * Add the PT page from the PD to the dump (it is no
279d30a28ddSMatthew Dillon 			 * longer included in the ptemap.
280d30a28ddSMatthew Dillon 			 */
281d30a28ddSMatthew Dillon 			dump_add_page(pd[j] & PG_FRAME);
282d30a28ddSMatthew Dillon 			lpdpttl += PAGE_SIZE;
283d30a28ddSMatthew Dillon 
284be66ad11SAlex Hornung 			/* set bit for each valid page in this 2MB block */
285be66ad11SAlex Hornung 			pt = (uint64_t *)PHYS_TO_DMAP(pd[j] & PG_FRAME);
286be66ad11SAlex Hornung 			for (k = 0; k < NPTEPG; k++) {
287*c713db65SAaron LI 				if ((pt[k] & kernel_pmap->pmap_bits[PG_V_IDX])
288*c713db65SAaron LI 				    == kernel_pmap->pmap_bits[PG_V_IDX]) {
289be66ad11SAlex Hornung 					pa = pt[k] & PG_FRAME;
290d30a28ddSMatthew Dillon 					lpdpttl += PAGE_SIZE;
291be66ad11SAlex Hornung 					if (is_dumpable(pa))
292be66ad11SAlex Hornung 						dump_add_page(pa);
293be66ad11SAlex Hornung 				}
294be66ad11SAlex Hornung 			}
295be66ad11SAlex Hornung 		} else {
296be66ad11SAlex Hornung 			/* nothing, we're going to dump a null page */
297be66ad11SAlex Hornung 		}
298be66ad11SAlex Hornung 	}
299be66ad11SAlex Hornung 
300be66ad11SAlex Hornung 	/* Calculate dump size. */
301be66ad11SAlex Hornung 	dumpsize = ptesize;
302be66ad11SAlex Hornung 	dumpsize += round_page(msgbufp->msg_size);
303be66ad11SAlex Hornung 	dumpsize += round_page(vm_page_dump_size);
304d30a28ddSMatthew Dillon 
305be66ad11SAlex Hornung 	for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) {
306be66ad11SAlex Hornung 		bits = vm_page_dump[i];
307be66ad11SAlex Hornung 		while (bits) {
308be66ad11SAlex Hornung 			bit = bsfq(bits);
309be66ad11SAlex Hornung 			pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + bit) * PAGE_SIZE;
310be66ad11SAlex Hornung 			/* Clear out undumpable pages now if needed */
311be66ad11SAlex Hornung 			if (is_dumpable(pa)) {
312be66ad11SAlex Hornung 				dumpsize += PAGE_SIZE;
313be66ad11SAlex Hornung 			} else {
314be66ad11SAlex Hornung 				dump_drop_page(pa);
315be66ad11SAlex Hornung 			}
316be66ad11SAlex Hornung 			bits &= ~(1ul << bit);
317be66ad11SAlex Hornung 		}
318be66ad11SAlex Hornung 	}
319be66ad11SAlex Hornung 	dumpsize += PAGE_SIZE;
320be66ad11SAlex Hornung 
321be66ad11SAlex Hornung 	/* Determine dump offset on device. */
322be66ad11SAlex Hornung 	if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
323be66ad11SAlex Hornung 		error = ENOSPC;
324be66ad11SAlex Hornung 		goto fail;
325be66ad11SAlex Hornung 	}
326be66ad11SAlex Hornung 	dumplo = di->mediaoffset + di->mediasize - dumpsize;
327be66ad11SAlex Hornung 	dumplo -= sizeof(kdh) * 2;
328be66ad11SAlex Hornung 	progress = dumpsize;
329be66ad11SAlex Hornung 
330be66ad11SAlex Hornung 	/* Initialize mdhdr */
331be66ad11SAlex Hornung 	bzero(&mdhdr, sizeof(mdhdr));
332d30a28ddSMatthew Dillon 	strcpy(mdhdr.magic, MINIDUMP2_MAGIC);
333d30a28ddSMatthew Dillon 	mdhdr.version = MINIDUMP2_VERSION;
334be66ad11SAlex Hornung 	mdhdr.msgbufsize = msgbufp->msg_size;
335be66ad11SAlex Hornung 	mdhdr.bitmapsize = vm_page_dump_size;
336be66ad11SAlex Hornung 	mdhdr.ptesize = ptesize;
337791c6551SMatthew Dillon 	mdhdr.kernbase = VM_MIN_KERNEL_ADDRESS;
338be66ad11SAlex Hornung 	mdhdr.dmapbase = DMAP_MIN_ADDRESS;
339be66ad11SAlex Hornung 	mdhdr.dmapend = DMAP_MAX_ADDRESS;
340be66ad11SAlex Hornung 
341be66ad11SAlex Hornung 	mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_AMD64_VERSION,
342be66ad11SAlex Hornung 		     dumpsize, di->blocksize);
343be66ad11SAlex Hornung 
344cfda4c25SMatthew Dillon 	kprintf("Physical memory: %jd MB\n", (intmax_t)ptoa(physmem) / 1048576);
345cfda4c25SMatthew Dillon 	kprintf("Dumping %jd MB:", (intmax_t)dumpsize >> 20);
346be66ad11SAlex Hornung 
347be66ad11SAlex Hornung 	/* Dump leader */
348be66ad11SAlex Hornung 	error = dev_ddump(di->priv, &kdh, 0, dumplo, sizeof(kdh));
349be66ad11SAlex Hornung 	if (error)
350be66ad11SAlex Hornung 		goto fail;
351be66ad11SAlex Hornung 	dumplo += sizeof(kdh);
352be66ad11SAlex Hornung 
353be66ad11SAlex Hornung 	/* Dump my header */
354d30a28ddSMatthew Dillon 	bzero(fakept, sizeof(fakept));
355d30a28ddSMatthew Dillon 	bcopy(&mdhdr, fakept, sizeof(mdhdr));
356d30a28ddSMatthew Dillon 	error = blk_write(di, (char *)fakept, 0, PAGE_SIZE);
357be66ad11SAlex Hornung 	if (error)
358be66ad11SAlex Hornung 		goto fail;
359be66ad11SAlex Hornung 
360be66ad11SAlex Hornung 	/* Dump msgbuf up front */
361be66ad11SAlex Hornung 	error = blk_write(di, (char *)msgbufp->msg_ptr, 0, round_page(msgbufp->msg_size));
362be66ad11SAlex Hornung 	if (error)
363be66ad11SAlex Hornung 		goto fail;
364be66ad11SAlex Hornung 
365be66ad11SAlex Hornung 	/* Dump bitmap */
366be66ad11SAlex Hornung 	error = blk_write(di, (char *)vm_page_dump, 0, round_page(vm_page_dump_size));
367be66ad11SAlex Hornung 	if (error)
368be66ad11SAlex Hornung 		goto fail;
369be66ad11SAlex Hornung 
370d30a28ddSMatthew Dillon 	/*
371d30a28ddSMatthew Dillon 	 * Dump a full PDP array for the entire KVM space, user and kernel.
372d30a28ddSMatthew Dillon 	 * This is 512*512 1G PD entries (512*512*8 = 2MB).
373d30a28ddSMatthew Dillon 	 *
374d30a28ddSMatthew Dillon 	 * The minidump only dumps PD entries related to KVA space.  Also
375d30a28ddSMatthew Dillon 	 * note that pdp[] (aka KPDPphys[]) only covers VM_MIN_KERNEL_ADDRESS
376d30a28ddSMatthew Dillon 	 * to VM_MAX_KERNEL_ADDRESS.
377d30a28ddSMatthew Dillon 	 *
378d30a28ddSMatthew Dillon 	 * The actual KPDPphys[] array covers a KVA space starting at KVA
379d30a28ddSMatthew Dillon 	 * KPDPPHYS_KVA.
380d30a28ddSMatthew Dillon 	 *
381d30a28ddSMatthew Dillon 	 * By dumping a PDP[] array of PDs representing the entire virtual
382d30a28ddSMatthew Dillon 	 * address space we can expand what we dump in the future.
383d30a28ddSMatthew Dillon 	 */
384be66ad11SAlex Hornung 	pdp = (uint64_t *)PHYS_TO_DMAP(KPDPphys);
385d30a28ddSMatthew Dillon 	kpdp = (KPDPPHYS_KVA >> PDPSHIFT) &
386d30a28ddSMatthew Dillon 		    (NPML4EPG * NPDPEPG - 1);
387d30a28ddSMatthew Dillon 	klo = (int)(VM_MIN_KERNEL_ADDRESS >> PDPSHIFT) &
388d30a28ddSMatthew Dillon 		    (NPML4EPG * NPDPEPG - 1);
389d30a28ddSMatthew Dillon 	khi = (int)(VM_MAX_KERNEL_ADDRESS >> PDPSHIFT) &
390d30a28ddSMatthew Dillon 		    (NPML4EPG * NPDPEPG - 1);
3917b7a555bSMatthew Dillon 
392d30a28ddSMatthew Dillon 	for (i = 0; i < NPML4EPG * NPDPEPG; ++i) {
393d30a28ddSMatthew Dillon 		if (i < klo || i > khi) {
394d30a28ddSMatthew Dillon 			fakept[i & (NPDPEPG - 1)] = 0;
395be66ad11SAlex Hornung 		} else {
396d30a28ddSMatthew Dillon 			fakept[i & (NPDPEPG - 1)] = pdp[i - kpdp];
397d30a28ddSMatthew Dillon 		}
398d30a28ddSMatthew Dillon 		if ((i & (NPDPEPG - 1)) == (NPDPEPG - 1)) {
399d30a28ddSMatthew Dillon 			error = blk_write(di, (char *)fakept, 0, PAGE_SIZE);
400be66ad11SAlex Hornung 			if (error)
401be66ad11SAlex Hornung 				goto fail;
402be66ad11SAlex Hornung 			error = blk_flush(di);
403be66ad11SAlex Hornung 			if (error)
404be66ad11SAlex Hornung 				goto fail;
405be66ad11SAlex Hornung 		}
406be66ad11SAlex Hornung 	}
407be66ad11SAlex Hornung 
408be66ad11SAlex Hornung 	/* Dump memory chunks */
409be66ad11SAlex Hornung 	/* XXX cluster it up and use blk_dump() */
410be66ad11SAlex Hornung 	for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) {
411be66ad11SAlex Hornung 		bits = vm_page_dump[i];
412be66ad11SAlex Hornung 		while (bits) {
413be66ad11SAlex Hornung 			bit = bsfq(bits);
414be66ad11SAlex Hornung 			pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + bit) * PAGE_SIZE;
415be66ad11SAlex Hornung 			error = blk_write(di, 0, pa, PAGE_SIZE);
416be66ad11SAlex Hornung 			if (error)
417be66ad11SAlex Hornung 				goto fail;
418be66ad11SAlex Hornung 			bits &= ~(1ul << bit);
419be66ad11SAlex Hornung 		}
420be66ad11SAlex Hornung 	}
421be66ad11SAlex Hornung 
422be66ad11SAlex Hornung 	error = blk_flush(di);
423be66ad11SAlex Hornung 	if (error)
424be66ad11SAlex Hornung 		goto fail;
425be66ad11SAlex Hornung 
426be66ad11SAlex Hornung 	/* Dump trailer */
427be66ad11SAlex Hornung 	error = dev_ddump(di->priv, &kdh, 0, dumplo, sizeof(kdh));
428be66ad11SAlex Hornung 	if (error)
429be66ad11SAlex Hornung 		goto fail;
430be66ad11SAlex Hornung 	dumplo += sizeof(kdh);
431be66ad11SAlex Hornung 
432be66ad11SAlex Hornung 	/* Signal completion, signoff and exit stage left. */
433be66ad11SAlex Hornung 	dev_ddump(di->priv, NULL, 0, 0, 0);
434be66ad11SAlex Hornung 	kprintf("\nDump complete\n");
435ce7866b8SMatthew Dillon 	cnpoll(FALSE);
436be66ad11SAlex Hornung 	return;
437be66ad11SAlex Hornung 
438be66ad11SAlex Hornung  fail:
439ce7866b8SMatthew Dillon 	cnpoll(FALSE);
440be66ad11SAlex Hornung 	if (error < 0)
441be66ad11SAlex Hornung 		error = -error;
442be66ad11SAlex Hornung 
443be66ad11SAlex Hornung 	if (error == ECANCELED)
444be66ad11SAlex Hornung 		kprintf("\nDump aborted\n");
445be66ad11SAlex Hornung 	else if (error == ENOSPC)
446be66ad11SAlex Hornung 		kprintf("\nDump failed. Partition too small.\n");
447be66ad11SAlex Hornung 	else
448be66ad11SAlex Hornung 		kprintf("\n** DUMP FAILED (ERROR %d) **\n", error);
449be66ad11SAlex Hornung }
450be66ad11SAlex Hornung 
451be66ad11SAlex Hornung void
dump_add_page(vm_paddr_t pa)452be66ad11SAlex Hornung dump_add_page(vm_paddr_t pa)
453be66ad11SAlex Hornung {
454be66ad11SAlex Hornung 	int idx, bit;
455be66ad11SAlex Hornung 
456be66ad11SAlex Hornung 	pa >>= PAGE_SHIFT;
457be66ad11SAlex Hornung 	idx = pa >> 6;		/* 2^6 = 64 */
458be66ad11SAlex Hornung 	bit = pa & 63;
459be66ad11SAlex Hornung 	atomic_set_long(&vm_page_dump[idx], 1ul << bit);
460be66ad11SAlex Hornung }
461be66ad11SAlex Hornung 
462be66ad11SAlex Hornung void
dump_drop_page(vm_paddr_t pa)463be66ad11SAlex Hornung dump_drop_page(vm_paddr_t pa)
464be66ad11SAlex Hornung {
465be66ad11SAlex Hornung 	int idx, bit;
466be66ad11SAlex Hornung 
467be66ad11SAlex Hornung 	pa >>= PAGE_SHIFT;
468be66ad11SAlex Hornung 	idx = pa >> 6;		/* 2^6 = 64 */
469be66ad11SAlex Hornung 	bit = pa & 63;
470be66ad11SAlex Hornung 	atomic_clear_long(&vm_page_dump[idx], 1ul << bit);
471be66ad11SAlex Hornung }
472