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