1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2006 Peter Wemm
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
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 #include <sys/watchdog.h>
42 #include <vm/vm.h>
43 #include <vm/vm_param.h>
44 #include <vm/vm_page.h>
45 #include <vm/vm_phys.h>
46 #include <vm/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 
53 CTASSERT(sizeof(struct kerneldumpheader) == 512);
54 
55 #define	MD_ALIGN(x)	(((off_t)(x) + PAGE_MASK) & ~PAGE_MASK)
56 #define	DEV_ALIGN(x)	roundup2((off_t)(x), DEV_BSIZE)
57 
58 static struct kerneldumpheader kdh;
59 
60 /* Handle chunked writes. */
61 static size_t fragsz;
62 static void *dump_va;
63 static uint64_t counter, progress;
64 
65 CTASSERT(sizeof(*vm_page_dump) == 4);
66 
67 static int
68 is_dumpable(vm_paddr_t pa)
69 {
70 	int i;
71 
72 	for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) {
73 		if (pa >= dump_avail[i] && pa < dump_avail[i + 1])
74 			return (1);
75 	}
76 	return (0);
77 }
78 
79 #define PG2MB(pgs) (((pgs) + (1 << 8) - 1) >> 8)
80 
81 static int
82 blk_flush(struct dumperinfo *di)
83 {
84 	int error;
85 
86 	if (fragsz == 0)
87 		return (0);
88 
89 	error = dump_append(di, dump_va, 0, fragsz);
90 	fragsz = 0;
91 	return (error);
92 }
93 
94 static int
95 blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz)
96 {
97 	size_t len;
98 	int error, i, c;
99 	u_int maxdumpsz;
100 
101 	maxdumpsz = min(di->maxiosize, MAXDUMPPGS * PAGE_SIZE);
102 	if (maxdumpsz == 0)	/* seatbelt */
103 		maxdumpsz = PAGE_SIZE;
104 	error = 0;
105 	if ((sz % PAGE_SIZE) != 0) {
106 		printf("size not page aligned\n");
107 		return (EINVAL);
108 	}
109 	if (ptr != NULL && pa != 0) {
110 		printf("cant have both va and pa!\n");
111 		return (EINVAL);
112 	}
113 	if (pa != 0 && (((uintptr_t)ptr) % PAGE_SIZE) != 0) {
114 		printf("address not page aligned\n");
115 		return (EINVAL);
116 	}
117 	if (ptr != NULL) {
118 		/* If we're doing a virtual dump, flush any pre-existing pa pages */
119 		error = blk_flush(di);
120 		if (error)
121 			return (error);
122 	}
123 	while (sz) {
124 		len = maxdumpsz - fragsz;
125 		if (len > sz)
126 			len = sz;
127 		counter += len;
128 		progress -= len;
129 		if (counter >> 24) {
130 			printf(" %lld", PG2MB(progress >> PAGE_SHIFT));
131 			counter &= (1<<24) - 1;
132 		}
133 
134 		wdog_kern_pat(WD_LASTVAL);
135 
136 		if (ptr) {
137 			error = dump_append(di, ptr, 0, len);
138 			if (error)
139 				return (error);
140 			ptr += len;
141 			sz -= len;
142 		} else {
143 			for (i = 0; i < len; i += PAGE_SIZE)
144 				dump_va = pmap_kenter_temporary(pa + i, (i + fragsz) >> PAGE_SHIFT);
145 			fragsz += len;
146 			pa += len;
147 			sz -= len;
148 			if (fragsz == maxdumpsz) {
149 				error = blk_flush(di);
150 				if (error)
151 					return (error);
152 			}
153 		}
154 
155 		/* Check for user abort. */
156 		c = cncheckc();
157 		if (c == 0x03)
158 			return (ECANCELED);
159 		if (c != -1)
160 			printf(" (CTRL-C to abort) ");
161 	}
162 
163 	return (0);
164 }
165 
166 /* A fake page table page, to avoid having to handle both 4K and 2M pages */
167 static pt_entry_t fakept[NPTEPG];
168 
169 #ifdef PMAP_PAE_COMP
170 #define	minidumpsys	minidumpsys_pae
171 #define	IdlePTD		IdlePTD_pae
172 #else
173 #define	minidumpsys	minidumpsys_nopae
174 #define	IdlePTD		IdlePTD_nopae
175 #endif
176 
177 int
178 minidumpsys(struct dumperinfo *di)
179 {
180 	uint64_t dumpsize;
181 	uint32_t ptesize;
182 	vm_offset_t va;
183 	int error;
184 	uint32_t bits;
185 	uint64_t pa;
186 	pd_entry_t *pd;
187 	pt_entry_t *pt;
188 	int i, j, k, bit;
189 	struct minidumphdr mdhdr;
190 
191 	counter = 0;
192 	/* Walk page table pages, set bits in vm_page_dump */
193 	ptesize = 0;
194 	for (va = KERNBASE; va < kernel_vm_end; va += NBPDR) {
195 		/*
196 		 * We always write a page, even if it is zero. Each
197 		 * page written corresponds to 2MB of space
198 		 */
199 		ptesize += PAGE_SIZE;
200 		pd = IdlePTD;	/* always mapped! */
201 		j = va >> PDRSHIFT;
202 		if ((pd[j] & (PG_PS | PG_V)) == (PG_PS | PG_V))  {
203 			/* This is an entire 2M page. */
204 			pa = pd[j] & PG_PS_FRAME;
205 			for (k = 0; k < NPTEPG; k++) {
206 				if (is_dumpable(pa))
207 					dump_add_page(pa);
208 				pa += PAGE_SIZE;
209 			}
210 			continue;
211 		}
212 		if ((pd[j] & PG_V) == PG_V) {
213 			/* set bit for each valid page in this 2MB block */
214 			pt = pmap_kenter_temporary(pd[j] & PG_FRAME, 0);
215 			for (k = 0; k < NPTEPG; k++) {
216 				if ((pt[k] & PG_V) == PG_V) {
217 					pa = pt[k] & PG_FRAME;
218 					if (is_dumpable(pa))
219 						dump_add_page(pa);
220 				}
221 			}
222 		} else {
223 			/* nothing, we're going to dump a null page */
224 		}
225 	}
226 
227 	/* Calculate dump size. */
228 	dumpsize = ptesize;
229 	dumpsize += round_page(msgbufp->msg_size);
230 	dumpsize += round_page(vm_page_dump_size);
231 	for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) {
232 		bits = vm_page_dump[i];
233 		while (bits) {
234 			bit = bsfl(bits);
235 			pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + bit) * PAGE_SIZE;
236 			/* Clear out undumpable pages now if needed */
237 			if (is_dumpable(pa)) {
238 				dumpsize += PAGE_SIZE;
239 			} else {
240 				dump_drop_page(pa);
241 			}
242 			bits &= ~(1ul << bit);
243 		}
244 	}
245 	dumpsize += PAGE_SIZE;
246 
247 	progress = dumpsize;
248 
249 	/* Initialize mdhdr */
250 	bzero(&mdhdr, sizeof(mdhdr));
251 	strcpy(mdhdr.magic, MINIDUMP_MAGIC);
252 	mdhdr.version = MINIDUMP_VERSION;
253 	mdhdr.msgbufsize = msgbufp->msg_size;
254 	mdhdr.bitmapsize = vm_page_dump_size;
255 	mdhdr.ptesize = ptesize;
256 	mdhdr.kernbase = KERNBASE;
257 	mdhdr.paemode = pae_mode;
258 
259 	dump_init_header(di, &kdh, KERNELDUMPMAGIC, KERNELDUMP_I386_VERSION,
260 	    dumpsize);
261 
262 	error = dump_start(di, &kdh);
263 	if (error != 0)
264 		goto fail;
265 
266 	printf("Physical memory: %ju MB\n", ptoa((uintmax_t)physmem) / 1048576);
267 	printf("Dumping %llu MB:", (long long)dumpsize >> 20);
268 
269 	/* Dump my header */
270 	bzero(&fakept, sizeof(fakept));
271 	bcopy(&mdhdr, &fakept, sizeof(mdhdr));
272 	error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE);
273 	if (error)
274 		goto fail;
275 
276 	/* Dump msgbuf up front */
277 	error = blk_write(di, (char *)msgbufp->msg_ptr, 0, round_page(msgbufp->msg_size));
278 	if (error)
279 		goto fail;
280 
281 	/* Dump bitmap */
282 	error = blk_write(di, (char *)vm_page_dump, 0, round_page(vm_page_dump_size));
283 	if (error)
284 		goto fail;
285 
286 	/* Dump kernel page table pages */
287 	for (va = KERNBASE; va < kernel_vm_end; va += NBPDR) {
288 		/* We always write a page, even if it is zero */
289 		pd = IdlePTD;	/* always mapped! */
290 		j = va >> PDRSHIFT;
291 		if ((pd[j] & (PG_PS | PG_V)) == (PG_PS | PG_V))  {
292 			/* This is a single 2M block. Generate a fake PTP */
293 			pa = pd[j] & PG_PS_FRAME;
294 			for (k = 0; k < NPTEPG; k++) {
295 				fakept[k] = (pa + (k * PAGE_SIZE)) | PG_V | PG_RW | PG_A | PG_M;
296 			}
297 			error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE);
298 			if (error)
299 				goto fail;
300 			/* flush, in case we reuse fakept in the same block */
301 			error = blk_flush(di);
302 			if (error)
303 				goto fail;
304 			continue;
305 		}
306 		if ((pd[j] & PG_V) == PG_V) {
307 			pa = pd[j] & PG_FRAME;
308 			error = blk_write(di, 0, pa, PAGE_SIZE);
309 			if (error)
310 				goto fail;
311 		} else {
312 			bzero(fakept, sizeof(fakept));
313 			error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE);
314 			if (error)
315 				goto fail;
316 			/* flush, in case we reuse fakept in the same block */
317 			error = blk_flush(di);
318 			if (error)
319 				goto fail;
320 		}
321 	}
322 
323 	/* Dump memory chunks */
324 	/* XXX cluster it up and use blk_dump() */
325 	for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) {
326 		bits = vm_page_dump[i];
327 		while (bits) {
328 			bit = bsfl(bits);
329 			pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + bit) * PAGE_SIZE;
330 			error = blk_write(di, 0, pa, PAGE_SIZE);
331 			if (error)
332 				goto fail;
333 			bits &= ~(1ul << bit);
334 		}
335 	}
336 
337 	error = blk_flush(di);
338 	if (error)
339 		goto fail;
340 
341 	error = dump_finish(di, &kdh);
342 	if (error != 0)
343 		goto fail;
344 
345 	printf("\nDump complete\n");
346 	return (0);
347 
348  fail:
349 	if (error < 0)
350 		error = -error;
351 
352 	if (error == ECANCELED)
353 		printf("\nDump aborted\n");
354 	else if (error == E2BIG || error == ENOSPC) {
355 		printf("\nDump failed. Partition too small (about %lluMB were "
356 		    "needed this time).\n", (long long)dumpsize >> 20);
357 	} else
358 		printf("\n** DUMP FAILED (ERROR %d) **\n", error);
359 	return (error);
360 }
361