Index: head/lib/libkvm/kvm_arm.h =================================================================== --- head/lib/libkvm/kvm_arm.h (revision 294097) +++ head/lib/libkvm/kvm_arm.h (revision 294098) @@ -1,108 +1,114 @@ /*- * Copyright (c) 2015 John H. Baldwin * 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 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. * * $FreeBSD$ */ #ifndef __KVM_ARM_H__ #define __KVM_ARM_H__ #ifdef __arm__ #include #endif typedef uint32_t arm_physaddr_t; typedef uint32_t arm_pd_entry_t; typedef uint32_t arm_pt_entry_t; #define ARM_PAGE_SHIFT 12 #define ARM_PAGE_SIZE (1 << ARM_PAGE_SHIFT) /* Page size */ #define ARM_PAGE_MASK (ARM_PAGE_SIZE - 1) #define ARM_L1_TABLE_SIZE 0x4000 /* 16K */ #define ARM_L1_S_SIZE 0x00100000 /* 1M */ #define ARM_L1_S_OFFSET (ARM_L1_S_SIZE - 1) #define ARM_L1_S_FRAME (~ARM_L1_S_OFFSET) #define ARM_L1_S_SHIFT 20 #define ARM_L2_L_SIZE 0x00010000 /* 64K */ #define ARM_L2_L_OFFSET (ARM_L2_L_SIZE - 1) #define ARM_L2_L_FRAME (~ARM_L2_L_OFFSET) #define ARM_L2_L_SHIFT 16 #define ARM_L2_S_SIZE 0x00001000 /* 4K */ #define ARM_L2_S_OFFSET (ARM_L2_S_SIZE - 1) #define ARM_L2_S_FRAME (~ARM_L2_S_OFFSET) #define ARM_L2_S_SHIFT 12 #define ARM_L1_TYPE_INV 0x00 /* Invalid (fault) */ #define ARM_L1_TYPE_C 0x01 /* Coarse L2 */ #define ARM_L1_TYPE_S 0x02 /* Section */ #define ARM_L1_TYPE_MASK 0x03 /* Mask of type bits */ #define ARM_L1_S_ADDR_MASK 0xfff00000 /* phys address of section */ #define ARM_L1_C_ADDR_MASK 0xfffffc00 /* phys address of L2 Table */ #define ARM_L2_TYPE_INV 0x00 /* Invalid (fault) */ -#define ARM_L2_TYPE_L 0x01 /* Large Page - 64k - not used yet*/ -#define ARM_L2_TYPE_S 0x02 /* Small Page - 4 */ +#define ARM_L2_TYPE_L 0x01 /* Large Page - 64k */ +#define ARM_L2_TYPE_S 0x02 /* Small Page - 4k */ +#define ARM_L2_TYPE_T 0x03 /* Tiny Page - 1k - not used */ #define ARM_L2_TYPE_MASK 0x03 #define ARM_L2_ADDR_BITS 0x000ff000 /* L2 PTE address bits */ #ifdef __arm__ +#include + _Static_assert(PAGE_SHIFT == ARM_PAGE_SHIFT, "PAGE_SHIFT mismatch"); _Static_assert(PAGE_SIZE == ARM_PAGE_SIZE, "PAGE_SIZE mismatch"); _Static_assert(PAGE_MASK == ARM_PAGE_MASK, "PAGE_MASK mismatch"); _Static_assert(L1_TABLE_SIZE == ARM_L1_TABLE_SIZE, "L1_TABLE_SIZE mismatch"); _Static_assert(L1_S_SIZE == ARM_L1_S_SIZE, "L1_S_SIZE mismatch"); _Static_assert(L1_S_OFFSET == ARM_L1_S_OFFSET, "L1_S_OFFSET mismatch"); _Static_assert(L1_S_FRAME == ARM_L1_S_FRAME, "L1_S_FRAME mismatch"); _Static_assert(L1_S_SHIFT == ARM_L1_S_SHIFT, "L1_S_SHIFT mismatch"); _Static_assert(L2_L_SIZE == ARM_L2_L_SIZE, "L2_L_SIZE mismatch"); _Static_assert(L2_L_OFFSET == ARM_L2_L_OFFSET, "L2_L_OFFSET mismatch"); _Static_assert(L2_L_FRAME == ARM_L2_L_FRAME, "L2_L_FRAME mismatch"); _Static_assert(L2_L_SHIFT == ARM_L2_L_SHIFT, "L2_L_SHIFT mismatch"); _Static_assert(L2_S_SIZE == ARM_L2_S_SIZE, "L2_S_SIZE mismatch"); _Static_assert(L2_S_OFFSET == ARM_L2_S_OFFSET, "L2_S_OFFSET mismatch"); _Static_assert(L2_S_FRAME == ARM_L2_S_FRAME, "L2_S_FRAME mismatch"); _Static_assert(L2_S_SHIFT == ARM_L2_S_SHIFT, "L2_S_SHIFT mismatch"); _Static_assert(L1_TYPE_INV == ARM_L1_TYPE_INV, "L1_TYPE_INV mismatch"); _Static_assert(L1_TYPE_C == ARM_L1_TYPE_C, "L1_TYPE_C mismatch"); _Static_assert(L1_TYPE_S == ARM_L1_TYPE_S, "L1_TYPE_S mismatch"); _Static_assert(L1_TYPE_MASK == ARM_L1_TYPE_MASK, "L1_TYPE_MASK mismatch"); _Static_assert(L1_S_ADDR_MASK == ARM_L1_S_ADDR_MASK, "L1_S_ADDR_MASK mismatch"); _Static_assert(L1_C_ADDR_MASK == ARM_L1_C_ADDR_MASK, "L1_C_ADDR_MASK mismatch"); _Static_assert(L2_TYPE_INV == ARM_L2_TYPE_INV, "L2_TYPE_INV mismatch"); _Static_assert(L2_TYPE_L == ARM_L2_TYPE_L, "L2_TYPE_L mismatch"); _Static_assert(L2_TYPE_S == ARM_L2_TYPE_S, "L2_TYPE_S mismatch"); +#if __ARM_ARCH < 6 +_Static_assert(L2_TYPE_T == ARM_L2_TYPE_T, "L2_TYPE_T mismatch"); +#endif _Static_assert(L2_TYPE_MASK == ARM_L2_TYPE_MASK, "L2_TYPE_MASK mismatch"); _Static_assert(L2_ADDR_BITS == ARM_L2_ADDR_BITS, "L2_ADDR_BITS mismatch"); #endif int _arm_native(kvm_t *); #endif /* !__KVM_ARM_H__ */ Index: head/lib/libkvm/kvm_minidump_arm.c =================================================================== --- head/lib/libkvm/kvm_minidump_arm.c (revision 294097) +++ head/lib/libkvm/kvm_minidump_arm.c (revision 294098) @@ -1,224 +1,237 @@ /*- * Copyright (c) 2008 Semihalf, Grzegorz Bernacki * Copyright (c) 2006 Peter Wemm * * 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 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. * * From: FreeBSD: src/lib/libkvm/kvm_minidump_i386.c,v 1.2 2006/06/05 08:51:14 */ #include __FBSDID("$FreeBSD$"); /* * ARM machine dependent routines for kvm and minidumps. */ #include #include #include #include #include #include #include #include #include "../../sys/arm/include/minidump.h" #include "kvm_private.h" #include "kvm_arm.h" #define arm_round_page(x) roundup2((kvaddr_t)(x), ARM_PAGE_SIZE) struct vmstate { struct minidumphdr hdr; struct hpt hpt; void *ptemap; unsigned char ei_data; }; static int _arm_minidump_probe(kvm_t *kd) { return (_kvm_probe_elf_kernel(kd, ELFCLASS32, EM_ARM) && _kvm_is_minidump(kd)); } static void _arm_minidump_freevtop(kvm_t *kd) { struct vmstate *vm = kd->vmst; _kvm_hpt_free(&vm->hpt); if (vm->ptemap) free(vm->ptemap); free(vm); kd->vmst = NULL; } static int _arm_minidump_initvtop(kvm_t *kd) { struct vmstate *vmst; uint32_t *bitmap; off_t off; vmst = _kvm_malloc(kd, sizeof(*vmst)); if (vmst == 0) { _kvm_err(kd, kd->program, "cannot allocate vm"); return (-1); } kd->vmst = vmst; if (pread(kd->pmfd, &vmst->hdr, sizeof(vmst->hdr), 0) != sizeof(vmst->hdr)) { _kvm_err(kd, kd->program, "cannot read dump header"); return (-1); } if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic, sizeof(vmst->hdr.magic)) != 0) { _kvm_err(kd, kd->program, "not a minidump for this platform"); return (-1); } vmst->hdr.version = _kvm32toh(kd, vmst->hdr.version); if (vmst->hdr.version != MINIDUMP_VERSION) { _kvm_err(kd, kd->program, "wrong minidump version. " "Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version); return (-1); } vmst->hdr.msgbufsize = _kvm32toh(kd, vmst->hdr.msgbufsize); vmst->hdr.bitmapsize = _kvm32toh(kd, vmst->hdr.bitmapsize); vmst->hdr.ptesize = _kvm32toh(kd, vmst->hdr.ptesize); vmst->hdr.kernbase = _kvm32toh(kd, vmst->hdr.kernbase); + vmst->hdr.arch = _kvm32toh(kd, vmst->hdr.arch); + vmst->hdr.mmuformat = _kvm32toh(kd, vmst->hdr.mmuformat); + if (vmst->hdr.mmuformat == MINIDUMP_MMU_FORMAT_UNKNOWN) { + /* This is a safe default as 1K pages are not used. */ + vmst->hdr.mmuformat = MINIDUMP_MMU_FORMAT_V6; + } /* Skip header and msgbuf */ off = ARM_PAGE_SIZE + arm_round_page(vmst->hdr.msgbufsize); bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize); if (bitmap == NULL) { _kvm_err(kd, kd->program, "cannot allocate %d bytes for " "bitmap", vmst->hdr.bitmapsize); return (-1); } if (pread(kd->pmfd, bitmap, vmst->hdr.bitmapsize, off) != (ssize_t)vmst->hdr.bitmapsize) { _kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap", vmst->hdr.bitmapsize); free(bitmap); return (-1); } off += arm_round_page(vmst->hdr.bitmapsize); vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize); if (vmst->ptemap == NULL) { _kvm_err(kd, kd->program, "cannot allocate %d bytes for " "ptemap", vmst->hdr.ptesize); free(bitmap); return (-1); } if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) != (ssize_t)vmst->hdr.ptesize) { _kvm_err(kd, kd->program, "cannot read %d bytes for ptemap", vmst->hdr.ptesize); free(bitmap); return (-1); } off += vmst->hdr.ptesize; /* Build physical address hash table for sparse pages */ _kvm_hpt_init(kd, &vmst->hpt, bitmap, vmst->hdr.bitmapsize, off, ARM_PAGE_SIZE, sizeof(*bitmap)); free(bitmap); return (0); } static int _arm_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa) { struct vmstate *vm; arm_pt_entry_t pte; arm_physaddr_t offset, a; kvaddr_t pteindex; off_t ofs; arm_pt_entry_t *ptemap; if (ISALIVE(kd)) { _kvm_err(kd, 0, "_arm_minidump_kvatop called in live kernel!"); return (0); } vm = kd->vmst; ptemap = vm->ptemap; if (va >= vm->hdr.kernbase) { pteindex = (va - vm->hdr.kernbase) >> ARM_PAGE_SHIFT; pte = _kvm32toh(kd, ptemap[pteindex]); - if (!pte) { + if ((pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_INV) { _kvm_err(kd, kd->program, "_arm_minidump_kvatop: pte not valid"); goto invalid; } if ((pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_L) { - offset = va & ARM_L2_L_OFFSET; - a = pte & ARM_L2_L_FRAME; - } else if ((pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_S) { + /* 64K page -> convert to be like 4K page */ offset = va & ARM_L2_S_OFFSET; + a = (pte & ARM_L2_L_FRAME) + + (va & ARM_L2_L_OFFSET & ARM_L2_S_FRAME); + } else { + if (kd->vmst->hdr.mmuformat == MINIDUMP_MMU_FORMAT_V4 && + (pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_T) { + _kvm_err(kd, kd->program, + "_arm_minidump_kvatop: pte not supported"); + goto invalid; + } + /* 4K page */ + offset = va & ARM_L2_S_OFFSET; a = pte & ARM_L2_S_FRAME; - } else - goto invalid; + } ofs = _kvm_hpt_find(&vm->hpt, a); if (ofs == -1) { _kvm_err(kd, kd->program, "_arm_minidump_kvatop: " "physical address 0x%jx not in minidump", (uintmax_t)a); goto invalid; } *pa = ofs + offset; return (ARM_PAGE_SIZE - offset); - } else _kvm_err(kd, kd->program, "_arm_minidump_kvatop: virtual " "address 0x%jx not minidumped", (uintmax_t)va); invalid: _kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va); return (0); } struct kvm_arch kvm_arm_minidump = { .ka_probe = _arm_minidump_probe, .ka_initvtop = _arm_minidump_initvtop, .ka_freevtop = _arm_minidump_freevtop, .ka_kvatop = _arm_minidump_kvatop, .ka_native = _arm_native, }; KVM_ARCH(kvm_arm_minidump); Index: head/sys/arm/arm/minidump_machdep.c =================================================================== --- head/sys/arm/arm/minidump_machdep.c (revision 294097) +++ head/sys/arm/arm/minidump_machdep.c (revision 294098) @@ -1,513 +1,518 @@ /*- * 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/i386/i386/minidump_machdep.c,v 1.6 2008/08/17 23:27:27 */ #include __FBSDID("$FreeBSD$"); #include "opt_watchdog.h" #include #include #include #include #include #include #include #ifdef SW_WATCHDOG #include #endif #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; #ifndef ARM_NEW_PMAP static struct kerneldumpheader kdh; static off_t dumplo; /* Handle chunked writes. */ static size_t fragsz, offset; static void *dump_va; static uint64_t counter, progress; 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); } #define PG2MB(pgs) (((pgs) + (1 << 8) - 1) >> 8) static int blk_flush(struct dumperinfo *di) { int error; if (fragsz == 0) return (0); error = dump_write(di, (char*)dump_va + offset, 0, dumplo, fragsz - offset); dumplo += (fragsz - offset); fragsz = 0; offset = 0; return (error); } static int blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz) { size_t len; int error, i, c; u_int maxdumpsz; maxdumpsz = di->maxiosize; if (maxdumpsz == 0) /* seatbelt */ maxdumpsz = PAGE_SIZE; error = 0; if (ptr != NULL && pa != 0) { printf("cant have both va and pa!\n"); return (EINVAL); } if (ptr != NULL) { /* If we're doing a virtual dump, flush any pre-existing pa pages */ error = blk_flush(di); if (error) return (error); } while (sz) { if (fragsz == 0) { offset = pa & PAGE_MASK; fragsz += offset; } len = maxdumpsz - fragsz; if (len > sz) len = sz; counter += len; progress -= len; if (counter >> 22) { printf(" %lld", PG2MB(progress >> PAGE_SHIFT)); counter &= (1<<22) - 1; } #ifdef SW_WATCHDOG wdog_kern_pat(WD_LASTVAL); #endif if (ptr) { error = dump_write(di, ptr, 0, dumplo, len); if (error) return (error); dumplo += len; ptr += len; sz -= len; } else { for (i = 0; i < len; i += PAGE_SIZE) dump_va = pmap_kenter_temporary(pa + i, (i + fragsz) >> PAGE_SHIFT); fragsz += len; pa += len; sz -= len; if (fragsz == maxdumpsz) { error = blk_flush(di); if (error) return (error); } } /* Check for user abort. */ c = cncheckc(); if (c == 0x03) return (ECANCELED); if (c != -1) printf(" (CTRL-C to abort) "); } return (0); } static int blk_write_cont(struct dumperinfo *di, vm_paddr_t pa, size_t sz) { int error; error = blk_write(di, 0, pa, sz); if (error) return (error); error = blk_flush(di); if (error) return (error); return (0); } /* A fake page table page, to avoid having to handle both 4K and 2M pages */ static pt_entry_t fakept[NPTEPG]; int minidumpsys(struct dumperinfo *di) { struct minidumphdr mdhdr; uint64_t dumpsize; uint32_t ptesize; uint32_t bits; uint32_t pa, prev_pa = 0, count = 0; vm_offset_t va; pd_entry_t *pdp; pt_entry_t *pt, *ptp; int i, k, bit, error; char *addr; /* * Flush caches. Note that in the SMP case this operates only on the * current CPU's L1 cache. Before we reach this point, code in either * the system shutdown or kernel debugger has called stop_cpus() to stop * all cores other than this one. Part of the ARM handling of * stop_cpus() is to call wbinv_all() on that core's local L1 cache. So * by time we get to here, all that remains is to flush the L1 for the * current CPU, then the L2. */ cpu_idcache_wbinv_all(); cpu_l2cache_wbinv_all(); counter = 0; /* Walk page table pages, set bits in vm_page_dump */ ptesize = 0; for (va = KERNBASE; va < kernel_vm_end; va += NBPDR) { /* * We always write a page, even if it is zero. Each * page written corresponds to 2MB of space */ ptesize += L2_TABLE_SIZE_REAL; pmap_get_pde_pte(pmap_kernel(), va, &pdp, &ptp); if (pmap_pde_v(pdp) && pmap_pde_section(pdp)) { /* This is a section mapping 1M page. */ pa = (*pdp & L1_S_ADDR_MASK) | (va & ~L1_S_ADDR_MASK); for (k = 0; k < (L1_S_SIZE / PAGE_SIZE); k++) { if (is_dumpable(pa)) dump_add_page(pa); pa += PAGE_SIZE; } continue; } if (pmap_pde_v(pdp) && pmap_pde_page(pdp)) { /* Set bit for each valid page in this 1MB block */ addr = pmap_kenter_temporary(*pdp & L1_C_ADDR_MASK, 0); pt = (pt_entry_t*)(addr + (((uint32_t)*pdp & L1_C_ADDR_MASK) & PAGE_MASK)); for (k = 0; k < 256; k++) { if ((pt[k] & L2_TYPE_MASK) == L2_TYPE_L) { pa = (pt[k] & L2_L_FRAME) | (va & L2_L_OFFSET); for (i = 0; i < 16; i++) { if (is_dumpable(pa)) dump_add_page(pa); k++; pa += PAGE_SIZE; } } else if ((pt[k] & L2_TYPE_MASK) == L2_TYPE_S) { pa = (pt[k] & L2_S_FRAME) | (va & L2_S_OFFSET); if (is_dumpable(pa)) dump_add_page(pa); } } } else { /* Nothing, we're going to dump a null page */ } } /* 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; } 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 = KERNBASE; - + mdhdr.arch = __ARM_ARCH; +#if __ARM_ARCH >= 6 + mdhdr.mmuformat = MINIDUMP_MMU_FORMAT_V6; +#else + mdhdr.mmuformat = MINIDUMP_MMU_FORMAT_V4; +#endif mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_ARM_VERSION, dumpsize, di->blocksize); printf("Physical memory: %u MB\n", 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(&fakept, sizeof(fakept)); bcopy(&mdhdr, &fakept, sizeof(mdhdr)); error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE); if (error) goto fail; /* Dump msgbuf up front */ error = blk_write(di, (char *)msgbufp->msg_ptr, 0, round_page(msgbufp->msg_size)); if (error) goto fail; /* Dump bitmap */ error = blk_write(di, (char *)vm_page_dump, 0, round_page(vm_page_dump_size)); if (error) goto fail; /* Dump kernel page table pages */ for (va = KERNBASE; va < kernel_vm_end; va += NBPDR) { /* We always write a page, even if it is zero */ pmap_get_pde_pte(pmap_kernel(), va, &pdp, &ptp); if (pmap_pde_v(pdp) && pmap_pde_section(pdp)) { if (count) { error = blk_write_cont(di, prev_pa, count * L2_TABLE_SIZE_REAL); if (error) goto fail; count = 0; prev_pa = 0; } /* This is a single 2M block. Generate a fake PTP */ pa = (*pdp & L1_S_ADDR_MASK) | (va & ~L1_S_ADDR_MASK); for (k = 0; k < (L1_S_SIZE / PAGE_SIZE); k++) { fakept[k] = L2_S_PROTO | (pa + (k * PAGE_SIZE)) | L2_S_PROT(PTE_KERNEL, VM_PROT_READ | VM_PROT_WRITE); } error = blk_write(di, (char *)&fakept, 0, L2_TABLE_SIZE_REAL); if (error) goto fail; /* Flush, in case we reuse fakept in the same block */ error = blk_flush(di); if (error) goto fail; continue; } if (pmap_pde_v(pdp) && pmap_pde_page(pdp)) { pa = *pdp & L1_C_ADDR_MASK; if (!count) { prev_pa = pa; count++; } else { if (pa == (prev_pa + count * L2_TABLE_SIZE_REAL)) count++; else { error = blk_write_cont(di, prev_pa, count * L2_TABLE_SIZE_REAL); if (error) goto fail; count = 1; prev_pa = pa; } } } else { if (count) { error = blk_write_cont(di, prev_pa, count * L2_TABLE_SIZE_REAL); if (error) goto fail; count = 0; prev_pa = 0; } bzero(fakept, sizeof(fakept)); error = blk_write(di, (char *)&fakept, 0, L2_TABLE_SIZE_REAL); if (error) goto fail; /* Flush, in case we reuse fakept in the same block */ error = blk_flush(di); if (error) goto fail; } } if (count) { error = blk_write_cont(di, prev_pa, count * L2_TABLE_SIZE_REAL); if (error) goto fail; count = 0; prev_pa = 0; } /* Dump memory chunks */ 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; if (!count) { prev_pa = pa; count++; } else { if (pa == (prev_pa + count * PAGE_SIZE)) count++; else { error = blk_write_cont(di, prev_pa, count * PAGE_SIZE); if (error) goto fail; count = 1; prev_pa = pa; } } bits &= ~(1ul << bit); } } if (count) { error = blk_write_cont(di, prev_pa, count * PAGE_SIZE); if (error) goto fail; count = 0; prev_pa = 0; } /* 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 (0); 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); return (error); return (0); } #else /* ARM_NEW_PMAP */ int minidumpsys(struct dumperinfo *di) { return (0); } #endif 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); } 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); } Index: head/sys/arm/include/minidump.h =================================================================== --- head/sys/arm/include/minidump.h (revision 294097) +++ head/sys/arm/include/minidump.h (revision 294098) @@ -1,45 +1,59 @@ /*- * Copyright (c) 2006 Peter Wemm * 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/i386/include/minidump.h,v 1.1 2006/04/21 04:28:43 * $FreeBSD$ */ #ifndef _MACHINE_MINIDUMP_H_ -#define _MACHINE_MINIDUMP_H_ 1 +#define _MACHINE_MINIDUMP_H_ #define MINIDUMP_MAGIC "minidump FreeBSD/arm" #define MINIDUMP_VERSION 1 +/* + * The first page of vmcore is dedicated to the following header. + * As the rest of the page is zeroed, any header extension can be + * done without version bumping. It should be taken into account + * only that new entries will be zero in old vmcores. + */ + struct minidumphdr { char magic[24]; uint32_t version; uint32_t msgbufsize; uint32_t bitmapsize; uint32_t ptesize; uint32_t kernbase; + uint32_t arch; + uint32_t mmuformat; }; + +#define MINIDUMP_MMU_FORMAT_UNKNOWN 0 +#define MINIDUMP_MMU_FORMAT_V4 1 +#define MINIDUMP_MMU_FORMAT_V6 2 +#define MINIDUMP_MMU_FORMAT_V6_LPAE 3 #endif /* _MACHINE_MINIDUMP_H_ */