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