1 /*-
2  * Copyright (c) 2019 Leandro Lupori
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  * $FreeBSD$
26  */
27 
28 #include <sys/types.h>
29 #include <sys/param.h>
30 
31 #include <sys/cons.h>
32 #include <sys/kerneldump.h>
33 #include <sys/msgbuf.h>
34 #include <sys/proc.h>
35 #include <sys/sysctl.h>
36 
37 #include <vm/vm.h>
38 #include <vm/vm_param.h>
39 #include <vm/vm_page.h>
40 #include <vm/vm_phys.h>
41 #include <vm/vm_dumpset.h>
42 #include <vm/pmap.h>
43 
44 #include <machine/atomic.h>
45 #include <machine/dump.h>
46 #include <machine/md_var.h>
47 #include <machine/minidump.h>
48 
49 /* Debugging stuff */
50 #define	MINIDUMP_DEBUG	0
51 #if	MINIDUMP_DEBUG
52 #define dprintf(fmt, ...)	printf(fmt, ## __VA_ARGS__)
53 #define DBG(...)	__VA_ARGS__
54 static size_t total, dumptotal;
55 static void dump_total(const char *id, size_t sz);
56 #else
57 #define dprintf(fmt, ...)
58 #define DBG(...)
59 #define dump_total(...)
60 #endif
61 
62 extern vm_offset_t __startkernel, __endkernel;
63 
64 static int dump_retry_count = 5;
65 SYSCTL_INT(_machdep, OID_AUTO, dump_retry_count, CTLFLAG_RWTUN,
66     &dump_retry_count, 0,
67     "Number of times dump has to retry before bailing out");
68 
69 static struct kerneldumpheader kdh;
70 static char pgbuf[PAGE_SIZE];
71 
72 static size_t dumpsize;
73 
74 /* Handle chunked writes. */
75 static size_t fragsz;
76 
77 static void
78 pmap_kenter_temporary(vm_offset_t va, vm_paddr_t pa)
79 {
80 	pmap_kremove(va);
81 	pmap_kenter(va, pa);
82 }
83 
84 static int
85 blk_flush(struct dumperinfo *di)
86 {
87 	int error;
88 
89 	if (fragsz == 0)
90 		return (0);
91 
92 	error = dump_append(di, crashdumpmap, fragsz);
93 	DBG(dumptotal += fragsz;)
94 	fragsz = 0;
95 	return (error);
96 }
97 
98 static int
99 blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz)
100 {
101 	size_t len, maxdumpsz;
102 	int error, i, c;
103 
104 	maxdumpsz = MIN(di->maxiosize, MAXDUMPPGS * PAGE_SIZE);
105 	if (maxdumpsz == 0)	/* seatbelt */
106 		maxdumpsz = PAGE_SIZE;
107 	error = 0;
108 	if ((sz % PAGE_SIZE) != 0) {
109 		printf("Size not page aligned\n");
110 		return (EINVAL);
111 	}
112 	if (ptr != NULL && pa != 0) {
113 		printf("Can't have both va and pa!\n");
114 		return (EINVAL);
115 	}
116 	if ((pa % PAGE_SIZE) != 0) {
117 		printf("Address not page aligned 0x%lx\n", pa);
118 		return (EINVAL);
119 	}
120 	if (ptr != NULL) {
121 		/*
122 		 * If we're doing a virtual dump, flush any pre-existing
123 		 * pa pages
124 		 */
125 		error = blk_flush(di);
126 		if (error)
127 			return (error);
128 	}
129 	while (sz) {
130 		len = maxdumpsz - fragsz;
131 		if (len > sz)
132 			len = sz;
133 
134 		dumpsys_pb_progress(len);
135 
136 		if (ptr) {
137 			error = dump_append(di, ptr, len);
138 			if (error)
139 				return (error);
140 			DBG(dumptotal += len;)
141 			ptr += len;
142 		} else {
143 			for (i = 0; i < len; i += PAGE_SIZE)
144 				pmap_kenter_temporary(
145 				    (vm_offset_t)crashdumpmap + fragsz + i,
146 				    pa + i);
147 
148 			fragsz += len;
149 			pa += len;
150 			if (fragsz == maxdumpsz) {
151 				error = blk_flush(di);
152 				if (error)
153 					return (error);
154 			}
155 		}
156 		sz -= len;
157 
158 		/* Check for user abort. */
159 		c = cncheckc();
160 		if (c == 0x03)
161 			return (ECANCELED);
162 		if (c != -1)
163 			printf(" (CTRL-C to abort) ");
164 	}
165 
166 	return (0);
167 }
168 
169 static int
170 dump_pmap(struct dumperinfo *di)
171 {
172 	void *ctx;
173 	char *buf;
174 	u_long nbytes;
175 	int error;
176 
177 	ctx = dumpsys_dump_pmap_init(sizeof(pgbuf) / PAGE_SIZE);
178 
179 	for (;;) {
180 		buf = dumpsys_dump_pmap(ctx, pgbuf, &nbytes);
181 		if (buf == NULL)
182 			break;
183 		error = blk_write(di, buf, 0, nbytes);
184 		if (error)
185 			return (error);
186 	}
187 
188 	return (0);
189 }
190 
191 int
192 cpu_minidumpsys(struct dumperinfo *di, const struct minidumpstate *state)
193 {
194 	vm_paddr_t pa;
195 	int error, retry_count;
196 	uint32_t pmapsize;
197 	struct minidumphdr mdhdr;
198 	struct msgbuf *mbp;
199 
200 	retry_count = 0;
201 retry:
202 	retry_count++;
203 	fragsz = 0;
204 	DBG(total = dumptotal = 0;)
205 
206 	/* Build set of dumpable pages from kernel pmap */
207 	pmapsize = dumpsys_scan_pmap(state->dump_bitset);
208 	if (pmapsize % PAGE_SIZE != 0) {
209 		printf("pmapsize not page aligned: 0x%x\n", pmapsize);
210 		return (EINVAL);
211 	}
212 
213 	/* Calculate dump size */
214 	mbp = state->msgbufp;
215 	dumpsize = PAGE_SIZE;				/* header */
216 	dumpsize += round_page(mbp->msg_size);
217 	dumpsize += round_page(sizeof(dump_avail));
218 	dumpsize += round_page(BITSET_SIZE(vm_page_dump_pages));
219 	dumpsize += pmapsize;
220 	VM_PAGE_DUMP_FOREACH(state->dump_bitset, pa) {
221 		/* Clear out undumpable pages now if needed */
222 		if (vm_phys_is_dumpable(pa))
223 			dumpsize += PAGE_SIZE;
224 		else
225 			vm_page_dump_drop(state->dump_bitset, pa);
226 	}
227 	dumpsys_pb_init(dumpsize);
228 
229 	/* Initialize mdhdr */
230 	bzero(&mdhdr, sizeof(mdhdr));
231 	strcpy(mdhdr.magic, MINIDUMP_MAGIC);
232 	strncpy(mdhdr.mmu_name, pmap_mmu_name(), sizeof(mdhdr.mmu_name) - 1);
233 	mdhdr.version = MINIDUMP_VERSION;
234 	mdhdr.msgbufsize = mbp->msg_size;
235 	mdhdr.bitmapsize = round_page(BITSET_SIZE(vm_page_dump_pages));
236 	mdhdr.pmapsize = pmapsize;
237 	mdhdr.kernbase = VM_MIN_KERNEL_ADDRESS;
238 	mdhdr.kernend = VM_MAX_SAFE_KERNEL_ADDRESS;
239 	mdhdr.dmapbase = DMAP_BASE_ADDRESS;
240 	mdhdr.dmapend = DMAP_MAX_ADDRESS;
241 	mdhdr.hw_direct_map = hw_direct_map;
242 	mdhdr.startkernel = __startkernel;
243 	mdhdr.endkernel = __endkernel;
244 	mdhdr.dumpavailsize = round_page(sizeof(dump_avail));
245 
246 	dump_init_header(di, &kdh, KERNELDUMPMAGIC, KERNELDUMP_POWERPC_VERSION,
247 	    dumpsize);
248 
249 	error = dump_start(di, &kdh);
250 	if (error)
251 		goto fail;
252 
253 	printf("Dumping %lu out of %ju MB:", dumpsize >> 20,
254 	    ptoa((uintmax_t)physmem) / 1048576);
255 
256 	/* Dump minidump header */
257 	bzero(pgbuf, sizeof(pgbuf));
258 	memcpy(pgbuf, &mdhdr, sizeof(mdhdr));
259 	error = blk_write(di, pgbuf, 0, PAGE_SIZE);
260 	if (error)
261 		goto fail;
262 	dump_total("header", PAGE_SIZE);
263 
264 	/* Dump msgbuf up front */
265 	error = blk_write(di, mbp->msg_ptr, 0, round_page(mbp->msg_size));
266 	dump_total("msgbuf", round_page(mbp->msg_size));
267 
268 	/* Dump dump_avail */
269 	_Static_assert(sizeof(dump_avail) <= sizeof(pgbuf),
270 	    "Large dump_avail not handled");
271 	bzero(pgbuf, sizeof(mdhdr));
272 	memcpy(pgbuf, dump_avail, sizeof(dump_avail));
273 	error = blk_write(di, pgbuf, 0, PAGE_SIZE);
274 	if (error)
275 		goto fail;
276 	dump_total("dump_avail", round_page(sizeof(dump_avail)));
277 
278 	/* Dump bitmap */
279 	error = blk_write(di, (char *)vm_page_dump, 0,
280 	    round_page(BITSET_SIZE(vm_page_dump_pages)));
281 	if (error)
282 		goto fail;
283 	dump_total("bitmap", round_page(BITSET_SIZE(vm_page_dump_pages)));
284 
285 	/* Dump kernel page directory pages */
286 	error = dump_pmap(di);
287 	if (error)
288 		goto fail;
289 	dump_total("pmap", pmapsize);
290 
291 	/* Dump memory chunks */
292 	VM_PAGE_DUMP_FOREACH(state->dump_bitset, pa) {
293 		error = blk_write(di, 0, pa, PAGE_SIZE);
294 		if (error)
295 			goto fail;
296 	}
297 
298 	error = blk_flush(di);
299 	if (error)
300 		goto fail;
301 	dump_total("mem_chunks", dumpsize - total);
302 
303 	error = dump_finish(di, &kdh);
304 	if (error)
305 		goto fail;
306 
307 	printf("\nDump complete\n");
308 	return (0);
309 
310 fail:
311 	if (error < 0)
312 		error = -error;
313 
314 	printf("\n");
315 	if (error == ENOSPC) {
316 		printf("Dump map grown while dumping. ");
317 		if (retry_count < dump_retry_count) {
318 			printf("Retrying...\n");
319 			goto retry;
320 		}
321 		printf("Dump failed.\n");
322 	} else if (error == ECANCELED)
323 		printf("Dump aborted\n");
324 	else if (error == E2BIG)
325 		printf("Dump failed. Partition too small.\n");
326 	else
327 		printf("** DUMP FAILED (ERROR %d) **\n", error);
328 	return (error);
329 }
330 
331 #if	MINIDUMP_DEBUG
332 static void
333 dump_total(const char *id, size_t sz)
334 {
335 	total += sz;
336 	dprintf("\n%s=%08lx/%08lx/%08lx\n",
337 		id, sz, total, dumptotal);
338 }
339 #endif
340