]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/powerpc/powerpc/minidump_machdep.c
Import riscv DTS files
[FreeBSD/FreeBSD.git] / sys / powerpc / powerpc / minidump_machdep.c
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/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 /*
49  * bit to physical address
50  *
51  * bm - bitmap
52  * i - bitmap entry index
53  * bit - bit number
54  */
55 #define BTOP(bm, i, bit) \
56         (((uint64_t)(i) * sizeof(*(bm)) * NBBY + (bit)) * PAGE_SIZE)
57
58 /* Debugging stuff */
59 #define MINIDUMP_DEBUG  0
60 #if     MINIDUMP_DEBUG
61 #define dprintf(fmt, ...)       printf(fmt, ## __VA_ARGS__)
62 #define DBG(...)        __VA_ARGS__
63 static size_t total, dumptotal;
64 static void dump_total(const char *id, size_t sz);
65 #else
66 #define dprintf(fmt, ...)
67 #define DBG(...)
68 #define dump_total(...)
69 #endif
70
71
72 extern vm_offset_t __startkernel, __endkernel;
73
74 int vm_page_dump_size;
75 uint64_t *vm_page_dump;
76
77 static int dump_retry_count = 5;
78 SYSCTL_INT(_machdep, OID_AUTO, dump_retry_count, CTLFLAG_RWTUN,
79     &dump_retry_count, 0,
80     "Number of times dump has to retry before bailing out");
81
82 static struct kerneldumpheader kdh;
83 static char pgbuf[PAGE_SIZE];
84
85 static struct {
86         int min_per;
87         int max_per;
88         int visited;
89 } progress_track[10] = {
90         {  0,  10, 0},
91         { 10,  20, 0},
92         { 20,  30, 0},
93         { 30,  40, 0},
94         { 40,  50, 0},
95         { 50,  60, 0},
96         { 60,  70, 0},
97         { 70,  80, 0},
98         { 80,  90, 0},
99         { 90, 100, 0}
100 };
101
102 static size_t counter, dumpsize, progress;
103
104 /* Handle chunked writes. */
105 static size_t fragsz;
106
107 void
108 dump_add_page(vm_paddr_t pa)
109 {
110         int idx, bit;
111
112         pa >>= PAGE_SHIFT;
113         idx = pa >> 6;          /* 2^6 = 64 */
114         bit = pa & 63;
115         atomic_set_long(&vm_page_dump[idx], 1ul << bit);
116 }
117
118 void
119 dump_drop_page(vm_paddr_t pa)
120 {
121         int idx, bit;
122
123         pa >>= PAGE_SHIFT;
124         idx = pa >> 6;          /* 2^6 = 64 */
125         bit = pa & 63;
126         atomic_clear_long(&vm_page_dump[idx], 1ul << bit);
127 }
128
129 int
130 is_dumpable(vm_paddr_t pa)
131 {
132         vm_page_t m;
133         int i;
134
135         if ((m = vm_phys_paddr_to_vm_page(pa)) != NULL)
136                 return ((m->flags & PG_NODUMP) == 0);
137         for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) {
138                 if (pa >= dump_avail[i] && pa < dump_avail[i + 1])
139                         return (1);
140         }
141         return (0);
142 }
143
144 static void
145 pmap_kenter_temporary(vm_offset_t va, vm_paddr_t pa)
146 {
147         pmap_kremove(va);
148         pmap_kenter(va, pa);
149 }
150
151 static void
152 report_progress(void)
153 {
154         int sofar, i;
155
156         sofar = 100 - ((progress * 100) / dumpsize);
157         for (i = 0; i < nitems(progress_track); i++) {
158                 if (sofar < progress_track[i].min_per ||
159                     sofar > progress_track[i].max_per)
160                         continue;
161                 if (progress_track[i].visited)
162                         return;
163                 progress_track[i].visited = 1;
164                 printf("..%d%%", sofar);
165                 return;
166         }
167 }
168
169 static int
170 blk_flush(struct dumperinfo *di)
171 {
172         int error;
173
174         if (fragsz == 0)
175                 return (0);
176
177         error = dump_append(di, crashdumpmap, 0, fragsz);
178         DBG(dumptotal += fragsz;)
179         fragsz = 0;
180         return (error);
181 }
182
183 static int
184 blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz)
185 {
186         size_t len, maxdumpsz;
187         int error, i, c;
188
189         maxdumpsz = MIN(di->maxiosize, MAXDUMPPGS * PAGE_SIZE);
190         if (maxdumpsz == 0)     /* seatbelt */
191                 maxdumpsz = PAGE_SIZE;
192         error = 0;
193         if ((sz % PAGE_SIZE) != 0) {
194                 printf("Size not page aligned\n");
195                 return (EINVAL);
196         }
197         if (ptr != NULL && pa != 0) {
198                 printf("Can't have both va and pa!\n");
199                 return (EINVAL);
200         }
201         if ((pa % PAGE_SIZE) != 0) {
202                 printf("Address not page aligned 0x%lx\n", pa);
203                 return (EINVAL);
204         }
205         if (ptr != NULL) {
206                 /*
207                  * If we're doing a virtual dump, flush any pre-existing
208                  * pa pages
209                  */
210                 error = blk_flush(di);
211                 if (error)
212                         return (error);
213         }
214         while (sz) {
215                 len = maxdumpsz - fragsz;
216                 if (len > sz)
217                         len = sz;
218                 counter += len;
219                 progress -= len;
220                 if (counter >> 20) {
221                         report_progress();
222                         counter &= (1<<20) - 1;
223                 }
224
225                 if (ptr) {
226                         error = dump_append(di, ptr, 0, len);
227                         if (error)
228                                 return (error);
229                         DBG(dumptotal += len;)
230                         ptr += len;
231                 } else {
232                         for (i = 0; i < len; i += PAGE_SIZE)
233                                 pmap_kenter_temporary(
234                                     (vm_offset_t)crashdumpmap + fragsz + i,
235                                     pa + i);
236
237                         fragsz += len;
238                         pa += len;
239                         if (fragsz == maxdumpsz) {
240                                 error = blk_flush(di);
241                                 if (error)
242                                         return (error);
243                         }
244                 }
245                 sz -= len;
246
247                 /* Check for user abort. */
248                 c = cncheckc();
249                 if (c == 0x03)
250                         return (ECANCELED);
251                 if (c != -1)
252                         printf(" (CTRL-C to abort) ");
253         }
254
255         return (0);
256 }
257
258 static int
259 dump_pmap(struct dumperinfo *di)
260 {
261         void *ctx;
262         char *buf;
263         u_long nbytes;
264         int error;
265
266         ctx = dumpsys_dump_pmap_init(sizeof(pgbuf) / PAGE_SIZE);
267
268         for (;;) {
269                 buf = dumpsys_dump_pmap(ctx, pgbuf, &nbytes);
270                 if (buf == NULL)
271                         break;
272                 error = blk_write(di, buf, 0, nbytes);
273                 if (error)
274                         return (error);
275         }
276
277         return (0);
278 }
279
280 int
281 minidumpsys(struct dumperinfo *di)
282 {
283         vm_paddr_t pa;
284         int bit, error, i, retry_count;
285         uint32_t pmapsize;
286         uint64_t bits;
287         struct minidumphdr mdhdr;
288
289         retry_count = 0;
290 retry:
291         retry_count++;
292         fragsz = 0;
293         DBG(total = dumptotal = 0;)
294
295         /* Reset progress */
296         counter = 0;
297         for (i = 0; i < nitems(progress_track); i++)
298                 progress_track[i].visited = 0;
299
300         /* Build set of dumpable pages from kernel pmap */
301         pmapsize = dumpsys_scan_pmap();
302         if (pmapsize % PAGE_SIZE != 0) {
303                 printf("pmapsize not page aligned: 0x%x\n", pmapsize);
304                 return (EINVAL);
305         }
306
307         /* Calculate dump size */
308         dumpsize = PAGE_SIZE;                           /* header */
309         dumpsize += round_page(msgbufp->msg_size);
310         dumpsize += round_page(vm_page_dump_size);
311         dumpsize += pmapsize;
312         for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) {
313                 bits = vm_page_dump[i];
314                 /* TODO optimize with bit manipulation instructions */
315                 if (bits == 0)
316                         continue;
317                 for (bit = 0; bit < 64; bit++) {
318                         if ((bits & (1ul<<bit)) == 0)
319                                 continue;
320
321                         pa = BTOP(vm_page_dump, i, bit);
322                         /* Clear out undumpable pages now if needed */
323                         if (is_dumpable(pa))
324                                 dumpsize += PAGE_SIZE;
325                         else
326                                 dump_drop_page(pa);
327                 }
328         }
329         progress = dumpsize;
330
331         /* Initialize mdhdr */
332         bzero(&mdhdr, sizeof(mdhdr));
333         strcpy(mdhdr.magic, MINIDUMP_MAGIC);
334         strncpy(mdhdr.mmu_name, pmap_mmu_name(), sizeof(mdhdr.mmu_name) - 1);
335         mdhdr.version = MINIDUMP_VERSION;
336         mdhdr.msgbufsize = msgbufp->msg_size;
337         mdhdr.bitmapsize = vm_page_dump_size;
338         mdhdr.pmapsize = pmapsize;
339         mdhdr.kernbase = VM_MIN_KERNEL_ADDRESS;
340         mdhdr.kernend = VM_MAX_SAFE_KERNEL_ADDRESS;
341         mdhdr.dmapbase = DMAP_BASE_ADDRESS;
342         mdhdr.dmapend = DMAP_MAX_ADDRESS;
343         mdhdr.hw_direct_map = hw_direct_map;
344         mdhdr.startkernel = __startkernel;
345         mdhdr.endkernel = __endkernel;
346
347         dump_init_header(di, &kdh, KERNELDUMPMAGIC, KERNELDUMP_POWERPC_VERSION,
348             dumpsize);
349
350         error = dump_start(di, &kdh);
351         if (error)
352                 goto fail;
353
354         printf("Dumping %lu out of %ju MB:", dumpsize >> 20,
355             ptoa((uintmax_t)physmem) / 1048576);
356
357         /* Dump minidump header */
358         bzero(pgbuf, sizeof(pgbuf));
359         memcpy(pgbuf, &mdhdr, sizeof(mdhdr));
360         error = blk_write(di, pgbuf, 0, PAGE_SIZE);
361         if (error)
362                 goto fail;
363         dump_total("header", PAGE_SIZE);
364
365         /* Dump msgbuf up front */
366         error = blk_write(di, (char *)msgbufp->msg_ptr, 0,
367             round_page(msgbufp->msg_size));
368         dump_total("msgbuf", round_page(msgbufp->msg_size));
369
370         /* Dump bitmap */
371         error = blk_write(di, (char *)vm_page_dump, 0,
372             round_page(vm_page_dump_size));
373         if (error)
374                 goto fail;
375         dump_total("bitmap", round_page(vm_page_dump_size));
376
377         /* Dump kernel page directory pages */
378         error = dump_pmap(di);
379         if (error)
380                 goto fail;
381         dump_total("pmap", pmapsize);
382
383         /* Dump memory chunks */
384         /* XXX cluster it up and use blk_dump() */
385         for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) {
386                 bits = vm_page_dump[i];
387                 /* TODO optimize with bit manipulation instructions */
388                 if (bits == 0)
389                         continue;
390                 for (bit = 0; bit < 64; bit++) {
391                         if ((bits & (1ul<<bit)) == 0)
392                                 continue;
393
394                         pa = BTOP(vm_page_dump, i, bit);
395                         error = blk_write(di, 0, pa, PAGE_SIZE);
396                         if (error)
397                                 goto fail;
398                 }
399         }
400
401         error = blk_flush(di);
402         if (error)
403                 goto fail;
404         dump_total("mem_chunks", dumpsize - total);
405
406         error = dump_finish(di, &kdh);
407         if (error)
408                 goto fail;
409
410         printf("\nDump complete\n");
411         return (0);
412
413 fail:
414         if (error < 0)
415                 error = -error;
416
417         printf("\n");
418         if (error == ENOSPC) {
419                 printf("Dump map grown while dumping. ");
420                 if (retry_count < dump_retry_count) {
421                         printf("Retrying...\n");
422                         goto retry;
423                 }
424                 printf("Dump failed.\n");
425         } else if (error == ECANCELED)
426                 printf("Dump aborted\n");
427         else if (error == E2BIG)
428                 printf("Dump failed. Partition too small.\n");
429         else
430                 printf("** DUMP FAILED (ERROR %d) **\n", error);
431         return (error);
432 }
433
434 #if     MINIDUMP_DEBUG
435 static void
436 dump_total(const char *id, size_t sz)
437 {
438         total += sz;
439         dprintf("\n%s=%08lx/%08lx/%08lx\n",
440                 id, sz, total, dumptotal);
441 }
442 #endif