2 * Copyright (c) 2006 Peter Wemm
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
26 * $FreeBSD: src/sys/i386/i386/minidump_machdep.c,v 1.9 2009/05/29 21:27:12 jamie Exp $
29 #include <sys/param.h>
30 #include <sys/systm.h>
33 #include <sys/device.h>
34 #include <sys/globaldata.h>
35 #include <sys/kernel.h>
36 #include <sys/kerneldump.h>
37 #include <sys/msgbuf.h>
39 #include <vm/vm_kern.h>
41 #include <machine/atomic.h>
42 #include <machine/elf.h>
43 #include <machine/globaldata.h>
44 #include <machine/md_var.h>
45 #include <machine/vmparam.h>
46 #include <machine/minidump.h>
48 CTASSERT(sizeof(struct kerneldumpheader
) == 512);
51 * Don't touch the first SIZEOF_METADATA bytes on the dump device. This
52 * is to protect us from metadata and to protect metadata from us.
54 #define SIZEOF_METADATA (64*1024)
56 #define MD_ALIGN(x) (((off_t)(x) + PAGE_MASK) & ~PAGE_MASK)
57 #define DEV_ALIGN(x) (((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1))
59 uint32_t *vm_page_dump
;
60 int vm_page_dump_size
;
62 static struct kerneldumpheader kdh
;
65 /* Handle chunked writes. */
68 static uint64_t counter
, progress
;
70 CTASSERT(sizeof(*vm_page_dump
) == 4);
73 is_dumpable(vm_paddr_t pa
)
77 for (i
= 0; dump_avail
[i
] != 0 || dump_avail
[i
+ 1] != 0; i
+= 2) {
78 if (pa
>= dump_avail
[i
] && pa
< dump_avail
[i
+ 1])
84 #define PG2MB(pgs) (((pgs) + (1 << 8) - 1) >> 8)
87 blk_flush(struct dumperinfo
*di
)
94 error
= dev_ddump(di
->priv
, dump_va
, 0, dumplo
, fragsz
);
101 blk_write(struct dumperinfo
*di
, char *ptr
, vm_paddr_t pa
, size_t sz
)
107 if ((sz
% PAGE_SIZE
) != 0) {
108 kprintf("size not page aligned\n");
111 if (ptr
!= NULL
&& pa
!= 0) {
112 kprintf("cant have both va and pa!\n");
115 if (pa
!= 0 && (((uintptr_t)ptr
) % PAGE_SIZE
) != 0) {
116 kprintf("address not page aligned\n");
120 /* If we're doing a virtual dump, flush any pre-existing pa pages */
121 error
= blk_flush(di
);
126 len
= (MAXDUMPPGS
* PAGE_SIZE
) - fragsz
;
132 kprintf(" %lld", PG2MB(progress
>> PAGE_SHIFT
));
133 counter
&= (1<<24) - 1;
136 error
= dev_ddump(di
->priv
, ptr
, 0, dumplo
, len
);
143 for (i
= 0; i
< len
; i
+= PAGE_SIZE
)
144 dump_va
= pmap_kenter_temporary(pa
+ i
, (i
+ fragsz
) >> PAGE_SHIFT
);
148 if (fragsz
== (MAXDUMPPGS
* PAGE_SIZE
)) {
149 error
= blk_flush(di
);
155 /* Check for user abort. */
160 kprintf(" (CTRL-C to abort) ");
166 /* A fake page table page, to avoid having to handle both 4K and 2M pages */
167 static pt_entry_t fakept
[NPTEPG
];
170 minidumpsys(struct dumperinfo
*di
)
175 vm_offset_t kern_end
;
182 struct minidumphdr mdhdr
;
183 struct mdglobaldata
*md
;
188 md
= (struct mdglobaldata
*)globaldata_find(0);
190 kern_end
= kernel_vm_end
;
191 if (kern_end
< (vm_offset_t
)&(md
[ncpus
]))
192 kern_end
= (vm_offset_t
)&(md
[ncpus
]);
194 kern_end
= 0xFFFFF000;
197 /* Walk page table pages, set bits in vm_page_dump */
198 for (va
= KERNBASE
; va
< kern_end
; va
+= NBPDR
) {
200 * We always write a page, even if it is zero. Each
201 * page written corresponds to 2MB of space
203 ptesize
+= PAGE_SIZE
;
204 pd
= (pd_entry_t
*)((uintptr_t)IdlePTD
+ KERNBASE
); /* always mapped! */
206 if ((pd
[j
] & (PG_PS
| PG_V
)) == (PG_PS
| PG_V
)) {
207 /* This is an entire 2M page. */
208 pa
= pd
[j
] & PG_FRAME
& ~PDRMASK
;
209 for (k
= 0; k
< NPTEPG
; k
++) {
216 if ((pd
[j
] & PG_V
) == PG_V
) {
217 /* set bit for each valid page in this 2MB block */
218 pt
= pmap_kenter_temporary(pd
[j
] & PG_FRAME
, 0);
219 for (k
= 0; k
< NPTEPG
; k
++) {
220 if ((pt
[k
] & PG_V
) == PG_V
) {
221 pa
= pt
[k
] & PG_FRAME
;
227 /* nothing, we're going to dump a null page */
231 /* Calculate dump size. */
233 dumpsize
+= round_page(msgbufp
->msg_size
);
234 dumpsize
+= round_page(vm_page_dump_size
);
235 for (i
= 0; i
< vm_page_dump_size
/ sizeof(*vm_page_dump
); i
++) {
236 bits
= vm_page_dump
[i
];
239 pa
= (((uint64_t)i
* sizeof(*vm_page_dump
) * NBBY
) + bit
) * PAGE_SIZE
;
240 /* Clear out undumpable pages now if needed */
241 if (is_dumpable(pa
)) {
242 dumpsize
+= PAGE_SIZE
;
246 bits
&= ~(1ul << bit
);
249 dumpsize
+= PAGE_SIZE
;
251 /* Determine dump offset on device. */
252 if (di
->mediasize
< SIZEOF_METADATA
+ dumpsize
+ sizeof(kdh
) * 2) {
256 dumplo
= di
->mediaoffset
+ di
->mediasize
- dumpsize
;
257 dumplo
-= sizeof(kdh
) * 2;
260 /* Initialize mdhdr */
261 bzero(&mdhdr
, sizeof(mdhdr
));
262 strcpy(mdhdr
.magic
, MINIDUMP_MAGIC
);
263 mdhdr
.version
= MINIDUMP_VERSION
;
264 mdhdr
.msgbufsize
= msgbufp
->msg_size
;
265 mdhdr
.bitmapsize
= vm_page_dump_size
;
266 mdhdr
.ptesize
= ptesize
;
267 mdhdr
.kernbase
= KERNBASE
;
269 mkdumpheader(&kdh
, KERNELDUMPMAGIC
, KERNELDUMP_I386_VERSION
,
270 dumpsize
, di
->blocksize
);
272 kprintf("Physical memory: %jd MB\n", (intmax_t)ptoa(physmem
) / 1048576);
273 kprintf("Dumping %jd MB:", (intmax_t)dumpsize
>> 20);
276 error
= dev_ddump(di
->priv
, &kdh
, 0, dumplo
, sizeof(kdh
));
279 dumplo
+= sizeof(kdh
);
282 bzero(&fakept
, sizeof(fakept
));
283 bcopy(&mdhdr
, &fakept
, sizeof(mdhdr
));
284 error
= blk_write(di
, (char *)&fakept
, 0, PAGE_SIZE
);
288 /* Dump msgbuf up front */
289 error
= blk_write(di
, (char *)msgbufp
->msg_ptr
, 0, round_page(msgbufp
->msg_size
));
294 error
= blk_write(di
, (char *)vm_page_dump
, 0, round_page(vm_page_dump_size
));
298 /* Dump kernel page table pages */
299 for (va
= KERNBASE
; va
< kern_end
; va
+= NBPDR
) {
300 /* We always write a page, even if it is zero */
301 pd
= (pd_entry_t
*)((uintptr_t)IdlePTD
+ KERNBASE
); /* always mapped! */
303 if ((pd
[j
] & (PG_PS
| PG_V
)) == (PG_PS
| PG_V
)) {
304 /* This is a single 2M block. Generate a fake PTP */
305 pa
= pd
[j
] & PG_FRAME
& ~PDRMASK
;
306 for (k
= 0; k
< NPTEPG
; k
++) {
307 fakept
[k
] = (pa
+ (k
* PAGE_SIZE
)) | PG_V
| PG_RW
| PG_A
| PG_M
;
309 error
= blk_write(di
, (char *)&fakept
, 0, PAGE_SIZE
);
312 /* flush, in case we reuse fakept in the same block */
313 error
= blk_flush(di
);
318 if ((pd
[j
] & PG_V
) == PG_V
) {
319 pa
= pd
[j
] & PG_FRAME
;
320 error
= blk_write(di
, 0, pa
, PAGE_SIZE
);
324 bzero(fakept
, sizeof(fakept
));
325 error
= blk_write(di
, (char *)&fakept
, 0, PAGE_SIZE
);
328 /* flush, in case we reuse fakept in the same block */
329 error
= blk_flush(di
);
335 /* Dump memory chunks */
336 /* XXX cluster it up and use blk_dump() */
337 for (i
= 0; i
< vm_page_dump_size
/ sizeof(*vm_page_dump
); i
++) {
338 bits
= vm_page_dump
[i
];
341 pa
= (((uint64_t)i
* sizeof(*vm_page_dump
) * NBBY
) + bit
) * PAGE_SIZE
;
342 error
= blk_write(di
, 0, pa
, PAGE_SIZE
);
345 bits
&= ~(1ul << bit
);
349 error
= blk_flush(di
);
354 error
= dev_ddump(di
->priv
, &kdh
, 0, dumplo
, sizeof(kdh
));
357 dumplo
+= sizeof(kdh
);
359 /* Signal completion, signoff and exit stage left. */
360 dev_ddump(di
->priv
, NULL
, 0, 0, 0);
361 kprintf("\nDump complete\n");
368 if (error
== ECANCELED
)
369 kprintf("\nDump aborted\n");
370 else if (error
== ENOSPC
)
371 kprintf("\nDump failed. Partition too small.\n");
373 kprintf("\n** DUMP FAILED (ERROR %d) **\n", error
);
377 dump_add_page(vm_paddr_t pa
)
382 idx
= pa
>> 5; /* 2^5 = 32 */
384 atomic_set_int(&vm_page_dump
[idx
], 1ul << bit
);
388 dump_drop_page(vm_paddr_t pa
)
393 idx
= pa
>> 5; /* 2^5 = 32 */
395 atomic_clear_int(&vm_page_dump
[idx
], 1ul << bit
);