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