]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/i386/i386/minidump_machdep_base.c
MFV r357163:
[FreeBSD/FreeBSD.git] / sys / i386 / i386 / minidump_machdep_base.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2006 Peter Wemm
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include "opt_watchdog.h"
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/conf.h>
37 #include <sys/cons.h>
38 #include <sys/kernel.h>
39 #include <sys/kerneldump.h>
40 #include <sys/msgbuf.h>
41 #include <sys/watchdog.h>
42 #include <vm/vm.h>
43 #include <vm/vm_param.h>
44 #include <vm/vm_page.h>
45 #include <vm/vm_phys.h>
46 #include <vm/pmap.h>
47 #include <machine/atomic.h>
48 #include <machine/elf.h>
49 #include <machine/md_var.h>
50 #include <machine/vmparam.h>
51 #include <machine/minidump.h>
52
53 CTASSERT(sizeof(struct kerneldumpheader) == 512);
54
55 #define MD_ALIGN(x)     (((off_t)(x) + PAGE_MASK) & ~PAGE_MASK)
56 #define DEV_ALIGN(x)    roundup2((off_t)(x), DEV_BSIZE)
57
58 static struct kerneldumpheader kdh;
59
60 /* Handle chunked writes. */
61 static size_t fragsz;
62 static void *dump_va;
63 static uint64_t counter, progress;
64
65 CTASSERT(sizeof(*vm_page_dump) == 4);
66
67 static int
68 is_dumpable(vm_paddr_t pa)
69 {
70         int i;
71
72         for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) {
73                 if (pa >= dump_avail[i] && pa < dump_avail[i + 1])
74                         return (1);
75         }
76         return (0);
77 }
78
79 #define PG2MB(pgs) (((pgs) + (1 << 8) - 1) >> 8)
80
81 static int
82 blk_flush(struct dumperinfo *di)
83 {
84         int error;
85
86         if (fragsz == 0)
87                 return (0);
88
89         error = dump_append(di, dump_va, 0, fragsz);
90         fragsz = 0;
91         return (error);
92 }
93
94 static int
95 blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz)
96 {
97         size_t len;
98         int error, i, c;
99         u_int maxdumpsz;
100
101         maxdumpsz = min(di->maxiosize, MAXDUMPPGS * PAGE_SIZE);
102         if (maxdumpsz == 0)     /* seatbelt */
103                 maxdumpsz = PAGE_SIZE;
104         error = 0;
105         if ((sz % PAGE_SIZE) != 0) {
106                 printf("size not page aligned\n");
107                 return (EINVAL);
108         }
109         if (ptr != NULL && pa != 0) {
110                 printf("cant have both va and pa!\n");
111                 return (EINVAL);
112         }
113         if (pa != 0 && (((uintptr_t)ptr) % PAGE_SIZE) != 0) {
114                 printf("address not page aligned\n");
115                 return (EINVAL);
116         }
117         if (ptr != NULL) {
118                 /* If we're doing a virtual dump, flush any pre-existing pa pages */
119                 error = blk_flush(di);
120                 if (error)
121                         return (error);
122         }
123         while (sz) {
124                 len = maxdumpsz - fragsz;
125                 if (len > sz)
126                         len = sz;
127                 counter += len;
128                 progress -= len;
129                 if (counter >> 24) {
130                         printf(" %lld", PG2MB(progress >> PAGE_SHIFT));
131                         counter &= (1<<24) - 1;
132                 }
133
134                 wdog_kern_pat(WD_LASTVAL);
135
136                 if (ptr) {
137                         error = dump_append(di, ptr, 0, len);
138                         if (error)
139                                 return (error);
140                         ptr += len;
141                         sz -= len;
142                 } else {
143                         for (i = 0; i < len; i += PAGE_SIZE)
144                                 dump_va = pmap_kenter_temporary(pa + i, (i + fragsz) >> PAGE_SHIFT);
145                         fragsz += len;
146                         pa += len;
147                         sz -= len;
148                         if (fragsz == maxdumpsz) {
149                                 error = blk_flush(di);
150                                 if (error)
151                                         return (error);
152                         }
153                 }
154
155                 /* Check for user abort. */
156                 c = cncheckc();
157                 if (c == 0x03)
158                         return (ECANCELED);
159                 if (c != -1)
160                         printf(" (CTRL-C to abort) ");
161         }
162
163         return (0);
164 }
165
166 /* A fake page table page, to avoid having to handle both 4K and 2M pages */
167 static pt_entry_t fakept[NPTEPG];
168
169 #ifdef PMAP_PAE_COMP
170 #define minidumpsys     minidumpsys_pae
171 #define IdlePTD         IdlePTD_pae
172 #else
173 #define minidumpsys     minidumpsys_nopae
174 #define IdlePTD         IdlePTD_nopae
175 #endif
176
177 int
178 minidumpsys(struct dumperinfo *di)
179 {
180         uint64_t dumpsize;
181         uint32_t ptesize;
182         vm_offset_t va;
183         int error;
184         uint32_t bits;
185         uint64_t pa;
186         pd_entry_t *pd;
187         pt_entry_t *pt;
188         int i, j, k, bit;
189         struct minidumphdr mdhdr;
190
191         counter = 0;
192         /* Walk page table pages, set bits in vm_page_dump */
193         ptesize = 0;
194         for (va = KERNBASE; va < kernel_vm_end; va += NBPDR) {
195                 /*
196                  * We always write a page, even if it is zero. Each
197                  * page written corresponds to 2MB of space
198                  */
199                 ptesize += PAGE_SIZE;
200                 pd = IdlePTD;   /* always mapped! */
201                 j = va >> PDRSHIFT;
202                 if ((pd[j] & (PG_PS | PG_V)) == (PG_PS | PG_V))  {
203                         /* This is an entire 2M page. */
204                         pa = pd[j] & PG_PS_FRAME;
205                         for (k = 0; k < NPTEPG; k++) {
206                                 if (is_dumpable(pa))
207                                         dump_add_page(pa);
208                                 pa += PAGE_SIZE;
209                         }
210                         continue;
211                 }
212                 if ((pd[j] & PG_V) == PG_V) {
213                         /* set bit for each valid page in this 2MB block */
214                         pt = pmap_kenter_temporary(pd[j] & PG_FRAME, 0);
215                         for (k = 0; k < NPTEPG; k++) {
216                                 if ((pt[k] & PG_V) == PG_V) {
217                                         pa = pt[k] & PG_FRAME;
218                                         if (is_dumpable(pa))
219                                                 dump_add_page(pa);
220                                 }
221                         }
222                 } else {
223                         /* nothing, we're going to dump a null page */
224                 }
225         }
226
227         /* Calculate dump size. */
228         dumpsize = ptesize;
229         dumpsize += round_page(msgbufp->msg_size);
230         dumpsize += round_page(vm_page_dump_size);
231         for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) {
232                 bits = vm_page_dump[i];
233                 while (bits) {
234                         bit = bsfl(bits);
235                         pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + bit) * PAGE_SIZE;
236                         /* Clear out undumpable pages now if needed */
237                         if (is_dumpable(pa)) {
238                                 dumpsize += PAGE_SIZE;
239                         } else {
240                                 dump_drop_page(pa);
241                         }
242                         bits &= ~(1ul << bit);
243                 }
244         }
245         dumpsize += PAGE_SIZE;
246
247         progress = dumpsize;
248
249         /* Initialize mdhdr */
250         bzero(&mdhdr, sizeof(mdhdr));
251         strcpy(mdhdr.magic, MINIDUMP_MAGIC);
252         mdhdr.version = MINIDUMP_VERSION;
253         mdhdr.msgbufsize = msgbufp->msg_size;
254         mdhdr.bitmapsize = vm_page_dump_size;
255         mdhdr.ptesize = ptesize;
256         mdhdr.kernbase = KERNBASE;
257         mdhdr.paemode = pae_mode;
258
259         dump_init_header(di, &kdh, KERNELDUMPMAGIC, KERNELDUMP_I386_VERSION,
260             dumpsize);
261
262         error = dump_start(di, &kdh);
263         if (error != 0)
264                 goto fail;
265
266         printf("Physical memory: %ju MB\n", ptoa((uintmax_t)physmem) / 1048576);
267         printf("Dumping %llu MB:", (long long)dumpsize >> 20);
268
269         /* Dump my header */
270         bzero(&fakept, sizeof(fakept));
271         bcopy(&mdhdr, &fakept, sizeof(mdhdr));
272         error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE);
273         if (error)
274                 goto fail;
275
276         /* Dump msgbuf up front */
277         error = blk_write(di, (char *)msgbufp->msg_ptr, 0, round_page(msgbufp->msg_size));
278         if (error)
279                 goto fail;
280
281         /* Dump bitmap */
282         error = blk_write(di, (char *)vm_page_dump, 0, round_page(vm_page_dump_size));
283         if (error)
284                 goto fail;
285
286         /* Dump kernel page table pages */
287         for (va = KERNBASE; va < kernel_vm_end; va += NBPDR) {
288                 /* We always write a page, even if it is zero */
289                 pd = IdlePTD;   /* always mapped! */
290                 j = va >> PDRSHIFT;
291                 if ((pd[j] & (PG_PS | PG_V)) == (PG_PS | PG_V))  {
292                         /* This is a single 2M block. Generate a fake PTP */
293                         pa = pd[j] & PG_PS_FRAME;
294                         for (k = 0; k < NPTEPG; k++) {
295                                 fakept[k] = (pa + (k * PAGE_SIZE)) | PG_V | PG_RW | PG_A | PG_M;
296                         }
297                         error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE);
298                         if (error)
299                                 goto fail;
300                         /* flush, in case we reuse fakept in the same block */
301                         error = blk_flush(di);
302                         if (error)
303                                 goto fail;
304                         continue;
305                 }
306                 if ((pd[j] & PG_V) == PG_V) {
307                         pa = pd[j] & PG_FRAME;
308                         error = blk_write(di, 0, pa, PAGE_SIZE);
309                         if (error)
310                                 goto fail;
311                 } else {
312                         bzero(fakept, sizeof(fakept));
313                         error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE);
314                         if (error)
315                                 goto fail;
316                         /* flush, in case we reuse fakept in the same block */
317                         error = blk_flush(di);
318                         if (error)
319                                 goto fail;
320                 }
321         }
322
323         /* Dump memory chunks */
324         /* XXX cluster it up and use blk_dump() */
325         for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) {
326                 bits = vm_page_dump[i];
327                 while (bits) {
328                         bit = bsfl(bits);
329                         pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + bit) * PAGE_SIZE;
330                         error = blk_write(di, 0, pa, PAGE_SIZE);
331                         if (error)
332                                 goto fail;
333                         bits &= ~(1ul << bit);
334                 }
335         }
336
337         error = blk_flush(di);
338         if (error)
339                 goto fail;
340
341         error = dump_finish(di, &kdh);
342         if (error != 0)
343                 goto fail;
344
345         printf("\nDump complete\n");
346         return (0);
347
348  fail:
349         if (error < 0)
350                 error = -error;
351
352         if (error == ECANCELED)
353                 printf("\nDump aborted\n");
354         else if (error == E2BIG || error == ENOSPC) {
355                 printf("\nDump failed. Partition too small (about %lluMB were "
356                     "needed this time).\n", (long long)dumpsize >> 20);
357         } else
358                 printf("\n** DUMP FAILED (ERROR %d) **\n", error);
359         return (error);
360 }