From 903ba3da8635fc6c1e1b8a42c81d70d036241238 Mon Sep 17 00:00:00 2001 From: Oleksandr Tymoshenko Date: Sun, 7 Nov 2010 03:09:02 +0000 Subject: [PATCH] - Add minidump support for FreeBSD/mips --- sys/conf/files.mips | 1 + sys/mips/include/cpuregs.h | 3 + sys/mips/include/md_var.h | 5 + sys/mips/include/pmap.h | 2 + sys/mips/mips/dump_machdep.c | 369 +++++++++++++++++++++++++++++-- sys/mips/mips/machdep.c | 8 +- sys/mips/mips/minidump_machdep.c | 340 ++++++++++++++++++++++++++++ sys/sys/kerneldump.h | 1 + sys/vm/vm_page.c | 3 +- 9 files changed, 703 insertions(+), 29 deletions(-) create mode 100644 sys/mips/mips/minidump_machdep.c diff --git a/sys/conf/files.mips b/sys/conf/files.mips index 99a772358e01..61a929554509 100644 --- a/sys/conf/files.mips +++ b/sys/conf/files.mips @@ -55,6 +55,7 @@ mips/mips/db_trace.c optional ddb mips/mips/dump_machdep.c standard mips/mips/in_cksum.c optional inet mips/mips/locore.S standard no-obj +mips/mips/minidump_machdep.c standard mips/mips/mem.c optional mem mips/mips/nexus.c standard mips/mips/stack_machdep.c optional ddb | stack diff --git a/sys/mips/include/cpuregs.h b/sys/mips/include/cpuregs.h index bc55a7eb2e96..456c545ca05a 100644 --- a/sys/mips/include/cpuregs.h +++ b/sys/mips/include/cpuregs.h @@ -181,6 +181,9 @@ #define MIPS_XUSEG_END 0x0000010000000000 #define MIPS_XKSEG_START 0xc000000000000000 #define MIPS_XKSEG_END 0xc00000ff80000000 +#define MIPS_XKSEG_COMPAT32_START 0xffffffff80000000 +#define MIPS_XKSEG_COMPAT32_END 0xffffffffffffffff +#define MIPS_XKSEG_TO_COMPAT32(va) ((va) & 0xffffffff) #ifdef __mips_n64 #define MIPS_DIRECT_MAPPABLE(pa) 1 diff --git a/sys/mips/include/md_var.h b/sys/mips/include/md_var.h index fee2e44b31ef..3cebbdc56460 100644 --- a/sys/mips/include/md_var.h +++ b/sys/mips/include/md_var.h @@ -42,6 +42,8 @@ extern long Maxmem; extern char sigcode[]; extern int szsigcode, szosigcode; +extern uint32_t *vm_page_dump; +extern int vm_page_dump_size; extern vm_offset_t kstack0; extern vm_offset_t kernel_kseg0_end; @@ -74,4 +76,7 @@ void platform_identify(void); extern int busdma_swi_pending; void busdma_swi(void); + +struct dumperinfo; +void minidumpsys(struct dumperinfo *); #endif /* !_MACHINE_MD_VAR_H_ */ diff --git a/sys/mips/include/pmap.h b/sys/mips/include/pmap.h index 558a0182ce83..c082abb9a42a 100644 --- a/sys/mips/include/pmap.h +++ b/sys/mips/include/pmap.h @@ -144,6 +144,8 @@ extern vm_offset_t physmem_desc[PHYS_AVAIL_ENTRIES + 2]; extern vm_offset_t virtual_avail; extern vm_offset_t virtual_end; +extern vm_paddr_t dump_avail[PHYS_AVAIL_ENTRIES + 2]; + #define pmap_page_get_memattr(m) VM_MEMATTR_DEFAULT #define pmap_page_is_mapped(m) (!TAILQ_EMPTY(&(m)->md.pv_list)) #define pmap_page_set_memattr(m, ma) (void)0 diff --git a/sys/mips/mips/dump_machdep.c b/sys/mips/mips/dump_machdep.c index 370068969b31..78938de68e75 100644 --- a/sys/mips/mips/dump_machdep.c +++ b/sys/mips/mips/dump_machdep.c @@ -1,35 +1,362 @@ /*- - * Copyright (c) 2006 Oleksandr Tymoshenko + * Copyright (c) 2002 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification, immediately at the beginning of the file. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); -/* Note to writer, when using pmap_kenter_temporary() you must, - * after using the va to write out the page, call - * pmap_kenter_temporary_free(). You should probably also - * pin the dump thread to the CPU with sched_pin(). +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +CTASSERT(sizeof(struct kerneldumpheader) == 512); + +int do_minidump = 1; +TUNABLE_INT("debug.minidump", &do_minidump); +SYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RW, &do_minidump, 0, + "Enable mini crash dumps"); + +/* + * Don't touch the first SIZEOF_METADATA bytes on the dump device. This + * is to protect us from metadata and to protect metadata from us. */ +#define SIZEOF_METADATA (64*1024) + +#define MD_ALIGN(x) (((off_t)(x) + PAGE_MASK) & ~PAGE_MASK) +#define DEV_ALIGN(x) (((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1)) +extern struct pcb dumppcb; + +struct md_pa { + vm_paddr_t md_start; + vm_paddr_t md_size; +}; + +typedef int callback_t(struct md_pa *, int, void *); + +static struct kerneldumpheader kdh; +static off_t dumplo, fileofs; + +/* Handle buffered writes. */ +static char buffer[DEV_BSIZE]; +static size_t fragsz; + +/* XXX: I suppose 20 should be enough. */ +static struct md_pa dump_map[20]; + +static void +md_pa_init(void) +{ + int n, idx; + + bzero(dump_map, sizeof(dump_map)); + for (n = 0; n < sizeof(dump_map) / sizeof(dump_map[0]); n++) { + idx = n * 2; + if (dump_avail[idx] == 0 && dump_avail[idx + 1] == 0) + break; + dump_map[n].md_start = dump_avail[idx]; + dump_map[n].md_size = dump_avail[idx + 1] - dump_avail[idx]; + } +} + +static struct md_pa * +md_pa_first(void) +{ + + return (&dump_map[0]); +} + +static struct md_pa * +md_pa_next(struct md_pa *mdp) +{ + + mdp++; + if (mdp->md_size == 0) + mdp = NULL; + return (mdp); +} + +static int +buf_write(struct dumperinfo *di, char *ptr, size_t sz) +{ + size_t len; + int error; + + while (sz) { + len = DEV_BSIZE - fragsz; + if (len > sz) + len = sz; + bcopy(ptr, buffer + fragsz, len); + fragsz += len; + ptr += len; + sz -= len; + if (fragsz == DEV_BSIZE) { + error = dump_write(di, buffer, 0, dumplo, + DEV_BSIZE); + if (error) + return error; + dumplo += DEV_BSIZE; + fragsz = 0; + } + } + + return (0); +} + +static int +buf_flush(struct dumperinfo *di) +{ + int error; + + if (fragsz == 0) + return (0); + + error = dump_write(di, buffer, 0, dumplo, DEV_BSIZE); + dumplo += DEV_BSIZE; + fragsz = 0; + return (error); +} + +extern vm_offset_t kernel_l1kva; +extern char *pouet2; + +static int +cb_dumpdata(struct md_pa *mdp, int seqnr, void *arg) +{ + struct dumperinfo *di = (struct dumperinfo*)arg; + vm_paddr_t pa; + uint32_t pgs; + size_t counter, sz, chunk; + int c, error; + + error = 0; /* catch case in which chunk size is 0 */ + counter = 0; + pgs = mdp->md_size / PAGE_SIZE; + pa = mdp->md_start; + + printf(" chunk %d: %dMB (%d pages)", seqnr, pgs * PAGE_SIZE / ( + 1024*1024), pgs); + + /* Make sure we write coherent datas. */ + mips_dcache_wbinv_all(); + while (pgs) { + chunk = pgs; + if (chunk > MAXDUMPPGS) + chunk = MAXDUMPPGS; + sz = chunk << PAGE_SHIFT; + counter += sz; + if (counter >> 24) { + printf(" %d", pgs * PAGE_SIZE); + counter &= (1<<24) - 1; + } + + error = dump_write(di, (void *)(pa),0, dumplo, sz); + if (error) + break; + dumplo += sz; + pgs -= chunk; + pa += sz; + + /* Check for user abort. */ + c = cncheckc(); + if (c == 0x03) + return (ECANCELED); + if (c != -1) + printf(" (CTRL-C to abort) "); + } + printf(" ... %s\n", (error) ? "fail" : "ok"); + return (error); +} + +static int +cb_dumphdr(struct md_pa *mdp, int seqnr, void *arg) +{ + struct dumperinfo *di = (struct dumperinfo*)arg; + Elf_Phdr phdr; + uint64_t size; + int error; + + size = mdp->md_size; + bzero(&phdr, sizeof(phdr)); + phdr.p_type = PT_LOAD; + phdr.p_flags = PF_R; /* XXX */ + phdr.p_offset = fileofs; + phdr.p_vaddr = mdp->md_start; + phdr.p_paddr = mdp->md_start; + phdr.p_filesz = size; + phdr.p_memsz = size; + phdr.p_align = PAGE_SIZE; + + error = buf_write(di, (char*)&phdr, sizeof(phdr)); + fileofs += phdr.p_filesz; + return (error); +} + +static int +cb_size(struct md_pa *mdp, int seqnr, void *arg) +{ + uint32_t *sz = (uint32_t*)arg; + + *sz += (uint32_t)mdp->md_size; + return (0); +} + +static int +foreach_chunk(callback_t cb, void *arg) +{ + struct md_pa *mdp; + int error, seqnr; + + seqnr = 0; + mdp = md_pa_first(); + while (mdp != NULL) { + error = (*cb)(mdp, seqnr++, arg); + if (error) + return (-error); + mdp = md_pa_next(mdp); + } + return (seqnr); +} + +void +dumpsys(struct dumperinfo *di) +{ + Elf_Ehdr ehdr; + uint32_t dumpsize; + off_t hdrgap; + size_t hdrsz; + int error; + + if (do_minidump) { + minidumpsys(di); + return; + } + + bzero(&ehdr, sizeof(ehdr)); + ehdr.e_ident[EI_MAG0] = ELFMAG0; + ehdr.e_ident[EI_MAG1] = ELFMAG1; + ehdr.e_ident[EI_MAG2] = ELFMAG2; + ehdr.e_ident[EI_MAG3] = ELFMAG3; + ehdr.e_ident[EI_CLASS] = ELF_CLASS; +#if BYTE_ORDER == LITTLE_ENDIAN + ehdr.e_ident[EI_DATA] = ELFDATA2LSB; +#else + ehdr.e_ident[EI_DATA] = ELFDATA2MSB; +#endif + ehdr.e_ident[EI_VERSION] = EV_CURRENT; + ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE; /* XXX big picture? */ + ehdr.e_type = ET_CORE; + ehdr.e_machine = EM_MIPS; + ehdr.e_phoff = sizeof(ehdr); + ehdr.e_flags = 0; + ehdr.e_ehsize = sizeof(ehdr); + ehdr.e_phentsize = sizeof(Elf_Phdr); + ehdr.e_shentsize = sizeof(Elf_Shdr); + + md_pa_init(); + + /* Calculate dump size. */ + dumpsize = 0L; + ehdr.e_phnum = foreach_chunk(cb_size, &dumpsize); + hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize; + fileofs = MD_ALIGN(hdrsz); + dumpsize += fileofs; + hdrgap = fileofs - DEV_ALIGN(hdrsz); + + /* Determine dump offset on device. */ + if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { + error = ENOSPC; + goto fail; + } + dumplo = di->mediaoffset + di->mediasize - dumpsize; + dumplo -= sizeof(kdh) * 2; + + mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_MIPS_VERSION, dumpsize, di->blocksize); + + printf("Dumping %llu MB (%d chunks)\n", (long long)dumpsize >> 20, + ehdr.e_phnum); + + /* Dump leader */ + error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + if (error) + goto fail; + dumplo += sizeof(kdh); + + /* Dump ELF header */ + error = buf_write(di, (char*)&ehdr, sizeof(ehdr)); + if (error) + goto fail; + + /* Dump program headers */ + error = foreach_chunk(cb_dumphdr, di); + if (error < 0) + goto fail; + buf_flush(di); + + /* + * All headers are written using blocked I/O, so we know the + * current offset is (still) block aligned. Skip the alignement + * in the file to have the segment contents aligned at page + * boundary. We cannot use MD_ALIGN on dumplo, because we don't + * care and may very well be unaligned within the dump device. + */ + dumplo += hdrgap; + + /* Dump memory chunks (updates dumplo) */ + error = foreach_chunk(cb_dumpdata, di); + if (error < 0) + goto fail; + + /* Dump trailer */ + error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + if (error) + goto fail; + + /* Signal completion, signoff and exit stage left. */ + dump_write(di, NULL, 0, 0, 0); + printf("\nDump complete\n"); + return; + + fail: + if (error < 0) + error = -error; + + if (error == ECANCELED) + printf("\nDump aborted\n"); + else if (error == ENOSPC) + printf("\nDump failed. Partition too small.\n"); + else + printf("\n** DUMP FAILED (ERROR %d) **\n", error); +} diff --git a/sys/mips/mips/machdep.c b/sys/mips/mips/machdep.c index 314bfd121152..074b5245bd7d 100644 --- a/sys/mips/mips/machdep.c +++ b/sys/mips/mips/machdep.c @@ -138,6 +138,7 @@ struct pcpu *pcpup = (struct pcpu *)pcpu_space; vm_offset_t phys_avail[PHYS_AVAIL_ENTRIES + 2]; vm_offset_t physmem_desc[PHYS_AVAIL_ENTRIES + 2]; +vm_paddr_t dump_avail[PHYS_AVAIL_ENTRIES + 2]; #ifdef UNIMPLEMENTED struct platform platform; @@ -488,13 +489,6 @@ cpu_idle(int busy) panic("ints disabled in idleproc!"); } -void -dumpsys(struct dumperinfo *di __unused) -{ - - printf("Kernel dumps not implemented on this architecture\n"); -} - int cpu_idle_wakeup(int cpu) { diff --git a/sys/mips/mips/minidump_machdep.c b/sys/mips/mips/minidump_machdep.c new file mode 100644 index 000000000000..1ac384a8876c --- /dev/null +++ b/sys/mips/mips/minidump_machdep.c @@ -0,0 +1,340 @@ +/*- + * Copyright (c) 2010 Oleksandr Tymoshenko + * Copyright (c) 2008 Semihalf, Grzegorz Bernacki + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * from: FreeBSD: src/sys/arm/arm/minidump_machdep.c v214223 + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +CTASSERT(sizeof(struct kerneldumpheader) == 512); + +/* + * Don't touch the first SIZEOF_METADATA bytes on the dump device. This + * is to protect us from metadata and to protect metadata from us. + */ +#define SIZEOF_METADATA (64*1024) + +uint32_t *vm_page_dump; +int vm_page_dump_size; + +static struct kerneldumpheader kdh; +static off_t dumplo; +static off_t origdumplo; + +/* Handle chunked writes. */ +static uint64_t counter, progress; +/* Just auxiliary bufffer */ +static char tmpbuffer[PAGE_SIZE]; + +extern pd_entry_t *kernel_segmap; + +CTASSERT(sizeof(*vm_page_dump) == 4); + +static int +is_dumpable(vm_paddr_t pa) +{ + int i; + + for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) { + if (pa >= dump_avail[i] && pa < dump_avail[i + 1]) + return (1); + } + return (0); +} + +static void +dump_add_page(vm_paddr_t pa) +{ + int idx, bit; + + pa >>= PAGE_SHIFT; + idx = pa >> 5; /* 2^5 = 32 */ + bit = pa & 31; + atomic_set_int(&vm_page_dump[idx], 1ul << bit); +} + +static void +dump_drop_page(vm_paddr_t pa) +{ + int idx, bit; + + pa >>= PAGE_SHIFT; + idx = pa >> 5; /* 2^5 = 32 */ + bit = pa & 31; + atomic_clear_int(&vm_page_dump[idx], 1ul << bit); +} + +#define PG2MB(pgs) (((pgs) + (1 << 8) - 1) >> 8) + +static int +write_buffer(struct dumperinfo *di, char *ptr, size_t sz) +{ + size_t len; + int error, c; + u_int maxdumpsz; + + maxdumpsz = di->maxiosize; + + if (maxdumpsz == 0) /* seatbelt */ + maxdumpsz = PAGE_SIZE; + + error = 0; + + while (sz) { + len = min(maxdumpsz, sz); + counter += len; + progress -= len; + + if (counter >> 22) { + printf(" %jd", PG2MB(progress >> PAGE_SHIFT)); + counter &= (1<<22) - 1; + } + + if (ptr) { + error = dump_write(di, ptr, 0, dumplo, len); + if (error) + return (error); + dumplo += len; + ptr += len; + sz -= len; + } else { + panic("pa is not supported"); + } + + /* Check for user abort. */ + c = cncheckc(); + if (c == 0x03) + return (ECANCELED); + if (c != -1) + printf(" (CTRL-C to abort) "); + } + + return (0); +} + +void +minidumpsys(struct dumperinfo *di) +{ + struct minidumphdr mdhdr; + uint64_t dumpsize; + uint32_t ptesize; + uint32_t bits; + vm_paddr_t pa; + vm_offset_t prev_pte = 0; + uint32_t count = 0; + vm_offset_t va; + pt_entry_t *pte; + int i, bit, error; + void *dump_va; + + /* Flush cache */ + mips_dcache_wbinv_all(); + + counter = 0; + /* Walk page table pages, set bits in vm_page_dump */ + ptesize = 0; + + for (va = VM_MIN_KERNEL_ADDRESS; va < kernel_vm_end; va += NBPDR) { + ptesize += PAGE_SIZE; + pte = pmap_pte(kernel_pmap, va); + KASSERT(pte != NULL, ("pte for %jx is NULL", (uintmax_t)va)); + for (i = 0; i < NPTEPG; i++) { + if (pte_test(&pte[i], PTE_V)) { + pa = TLBLO_PTE_TO_PA(pte[i]); + if (is_dumpable(pa)) + dump_add_page(pa); + } + } + } + + /* + * Now mark pages from 0 to phys_avail[0], that's where kernel + * and pages allocated by pmap_steal reside + */ + for (pa = 0; pa < phys_avail[0]; pa += PAGE_SIZE) { + if (is_dumpable(pa)) + dump_add_page(pa); + } + + /* Calculate dump size. */ + dumpsize = ptesize; + dumpsize += round_page(msgbufp->msg_size); + dumpsize += round_page(vm_page_dump_size); + + for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { + bits = vm_page_dump[i]; + while (bits) { + bit = ffs(bits) - 1; + pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + + bit) * PAGE_SIZE; + /* Clear out undumpable pages now if needed */ + if (is_dumpable(pa)) + dumpsize += PAGE_SIZE; + else + dump_drop_page(pa); + bits &= ~(1ul << bit); + } + } + + dumpsize += PAGE_SIZE; + + /* Determine dump offset on device. */ + if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { + error = ENOSPC; + goto fail; + } + + origdumplo = dumplo = di->mediaoffset + di->mediasize - dumpsize; + dumplo -= sizeof(kdh) * 2; + progress = dumpsize; + + /* Initialize mdhdr */ + bzero(&mdhdr, sizeof(mdhdr)); + strcpy(mdhdr.magic, MINIDUMP_MAGIC); + mdhdr.version = MINIDUMP_VERSION; + mdhdr.msgbufsize = msgbufp->msg_size; + mdhdr.bitmapsize = vm_page_dump_size; + mdhdr.ptesize = ptesize; + mdhdr.kernbase = VM_MIN_KERNEL_ADDRESS; + + mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_MIPS_VERSION, dumpsize, + di->blocksize); + + printf("Physical memory: %ju MB\n", + (uintmax_t)ptoa((uintmax_t)physmem) / 1048576); + printf("Dumping %llu MB:", (long long)dumpsize >> 20); + + /* Dump leader */ + error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + if (error) + goto fail; + dumplo += sizeof(kdh); + + /* Dump my header */ + bzero(tmpbuffer, sizeof(tmpbuffer)); + bcopy(&mdhdr, tmpbuffer, sizeof(mdhdr)); + error = write_buffer(di, tmpbuffer, PAGE_SIZE); + if (error) + goto fail; + + /* Dump msgbuf up front */ + error = write_buffer(di, (char *)msgbufp->msg_ptr, + round_page(msgbufp->msg_size)); + if (error) + goto fail; + + /* Dump bitmap */ + error = write_buffer(di, (char *)vm_page_dump, + round_page(vm_page_dump_size)); + if (error) + goto fail; + + /* Dump kernel page table pages */ + for (va = VM_MIN_KERNEL_ADDRESS; va < kernel_vm_end; va += NBPDR) { + pte = pmap_pte(kernel_pmap, va); + KASSERT(pte != NULL, ("pte for %jx is NULL", (uintmax_t)va)); + if (!count) { + prev_pte = (vm_offset_t)pte; + count++; + } + else { + if ((vm_offset_t)pte == (prev_pte + count * PAGE_SIZE)) + count++; + else { + error = write_buffer(di, (char*)prev_pte, + count * PAGE_SIZE); + if (error) + goto fail; + count = 1; + prev_pte = (vm_offset_t)pte; + } + } + } + + if (count) { + error = write_buffer(di, (char*)prev_pte, count * PAGE_SIZE); + if (error) + goto fail; + count = 0; + prev_pte = 0; + } + + /* Dump memory chunks page by page*/ + for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { + bits = vm_page_dump[i]; + while (bits) { + bit = ffs(bits) - 1; + pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + + bit) * PAGE_SIZE; + dump_va = pmap_kenter_temporary(pa, 0); + error = write_buffer(di, dump_va, PAGE_SIZE); + if (error) + goto fail; + pmap_kenter_temporary_free(pa); + bits &= ~(1ul << bit); + } + } + + /* Dump trailer */ + error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + if (error) + goto fail; + dumplo += sizeof(kdh); + + /* Signal completion, signoff and exit stage left. */ + dump_write(di, NULL, 0, 0, 0); + printf("\nDump complete\n"); + return; + +fail: + if (error < 0) + error = -error; + + if (error == ECANCELED) + printf("\nDump aborted\n"); + else if (error == ENOSPC) + printf("\nDump failed. Partition too small.\n"); + else + printf("\n** DUMP FAILED (ERROR %d) **\n", error); +} diff --git a/sys/sys/kerneldump.h b/sys/sys/kerneldump.h index e3421de274f1..6342f7d67ff9 100644 --- a/sys/sys/kerneldump.h +++ b/sys/sys/kerneldump.h @@ -71,6 +71,7 @@ struct kerneldumpheader { #define KERNELDUMP_ARM_VERSION 1 #define KERNELDUMP_I386_VERSION 2 #define KERNELDUMP_IA64_VERSION 1 +#define KERNELDUMP_MIPS_VERSION 1 #define KERNELDUMP_POWERPC_VERSION 1 #define KERNELDUMP_SPARC64_VERSION 1 #define KERNELDUMP_TEXT_VERSION 1 diff --git a/sys/vm/vm_page.c b/sys/vm/vm_page.c index d778c992b9b4..32b90e632c26 100644 --- a/sys/vm/vm_page.c +++ b/sys/vm/vm_page.c @@ -359,7 +359,8 @@ vm_page_startup(vm_offset_t vaddr) bzero((void *)mapped, end - new_end); uma_startup((void *)mapped, boot_pages); -#if defined(__amd64__) || defined(__i386__) || defined(__arm__) +#if defined(__amd64__) || defined(__i386__) || defined(__arm__) || \ + defined(__mips__) /* * Allocate a bitmap to indicate that a random physical page * needs to be included in a minidump.