svn commit: r184728 - in head: lib/libkvm sys/arm/arm sys/arm/include sys/conf sys/vm

Rafal Jaworowski raj at FreeBSD.org
Thu Nov 6 08:20:27 PST 2008


Author: raj
Date: Thu Nov  6 16:20:27 2008
New Revision: 184728
URL: http://svn.freebsd.org/changeset/base/184728

Log:
  Support kernel crash mini dumps on ARM architecture.
  
  Obtained from:	Juniper Networks, Semihalf

Added:
  head/lib/libkvm/kvm_minidump_arm.c   (contents, props changed)
  head/sys/arm/arm/minidump_machdep.c   (contents, props changed)
  head/sys/arm/include/minidump.h   (contents, props changed)
Modified:
  head/lib/libkvm/Makefile
  head/lib/libkvm/kvm_arm.c
  head/lib/libkvm/kvm_private.h
  head/sys/arm/arm/dump_machdep.c
  head/sys/arm/arm/pmap.c
  head/sys/arm/include/md_var.h
  head/sys/arm/include/pmap.h
  head/sys/conf/files.arm
  head/sys/vm/vm_page.c

Modified: head/lib/libkvm/Makefile
==============================================================================
--- head/lib/libkvm/Makefile	Thu Nov  6 15:32:02 2008	(r184727)
+++ head/lib/libkvm/Makefile	Thu Nov  6 16:20:27 2008	(r184728)
@@ -11,7 +11,7 @@ CFLAGS+=-DSUN4V
 
 SRCS=	kvm.c kvm_${MACHINE_ARCH}.c kvm_cptime.c kvm_file.c kvm_getloadavg.c \
 	kvm_getswapinfo.c kvm_pcpu.c kvm_proc.c
-.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "i386"
+.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH} == "arm"
 SRCS+=	kvm_minidump_${MACHINE_ARCH}.c
 .endif
 INCS=	kvm.h

Modified: head/lib/libkvm/kvm_arm.c
==============================================================================
--- head/lib/libkvm/kvm_arm.c	Thu Nov  6 15:32:02 2008	(r184727)
+++ head/lib/libkvm/kvm_arm.c	Thu Nov  6 16:20:27 2008	(r184728)
@@ -52,10 +52,13 @@ __FBSDID("$FreeBSD$");
 #include <limits.h>
 #include <kvm.h>
 #include <stdlib.h>
+#include <unistd.h>
 
 #include "kvm_private.h"
 
+/* minidump must be the first item! */
 struct vmstate {
+	int minidump;		/* 1 = minidump mode */
 	pd_entry_t *l1pt;
 	void *mmapbase;
 	size_t mmapsize;
@@ -107,6 +110,8 @@ void
 _kvm_freevtop(kvm_t *kd)
 {
 	if (kd->vmst != 0) {
+		if (kd->vmst->minidump)
+			return (_kvm_minidump_freevtop(kd));
 		if (kd->vmst->mmapbase != NULL)
 			munmap(kd->vmst->mmapbase, kd->vmst->mmapsize);
 		free(kd->vmst);
@@ -117,13 +122,25 @@ _kvm_freevtop(kvm_t *kd)
 int
 _kvm_initvtop(kvm_t *kd)
 {
-	struct vmstate *vm = _kvm_malloc(kd, sizeof(*vm));
+	struct vmstate *vm;
 	struct nlist nlist[2];
 	u_long kernbase, physaddr, pa;
 	pd_entry_t *l1pt;
 	Elf32_Ehdr *ehdr;
 	size_t hdrsz;
-	
+	char minihdr[8];
+
+	if (!kd->rawdump) {
+		if (pread(kd->pmfd, &minihdr, 8, 0) == 8) {
+			if (memcmp(&minihdr, "minidump", 8) == 0)
+				return (_kvm_minidump_initvtop(kd));
+		} else {
+			_kvm_err(kd, kd->program, "cannot read header");
+			return (-1);
+		}
+	}
+
+	vm = _kvm_malloc(kd, sizeof(*vm));
 	if (vm == 0) {
 		_kvm_err(kd, kd->program, "cannot allocate vm");
 		return (-1);
@@ -193,6 +210,9 @@ _kvm_kvatop(kvm_t *kd, u_long va, off_t 
 	pt_entry_t pte;
 	u_long pte_pa;
 
+	if (kd->vmst->minidump)
+		return (_kvm_minidump_kvatop(kd, va, pa));
+
 	if (vm->l1pt == NULL)
 		return (_kvm_pa2off(kd, va, pa, PAGE_SIZE));
 	pd = vm->l1pt[L1_IDX(va)];

Added: head/lib/libkvm/kvm_minidump_arm.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libkvm/kvm_minidump_arm.c	Thu Nov  6 16:20:27 2008	(r184728)
@@ -0,0 +1,262 @@
+/*-
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * ARM machine dependent routines for kvm and minidumps.
+ */
+
+#include <sys/param.h>
+#include <sys/user.h>
+#include <sys/proc.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/fnv_hash.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <nlist.h>
+#include <kvm.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+
+#include <machine/elf.h>
+#include <machine/cpufunc.h>
+#include <machine/minidump.h>
+
+#include <limits.h>
+
+#include "kvm_private.h"
+
+struct hpte {
+	struct hpte	*next;
+	uint64_t	pa;
+	int64_t		off;
+};
+
+#define HPT_SIZE 1024
+
+/* minidump must be the first field */
+struct vmstate {
+	int		minidump;		/* 1 = minidump mode */
+	struct		minidumphdr hdr;
+	void		*hpt_head[HPT_SIZE];
+	uint32_t	*bitmap;
+	void		*ptemap;
+};
+
+static void
+hpt_insert(kvm_t *kd, uint64_t pa, int64_t off)
+{
+	struct hpte *hpte;
+	uint32_t fnv = FNV1_32_INIT;
+
+	fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
+	fnv &= (HPT_SIZE - 1);
+	hpte = malloc(sizeof(*hpte));
+	hpte->pa = pa;
+	hpte->off = off;
+	hpte->next = kd->vmst->hpt_head[fnv];
+	kd->vmst->hpt_head[fnv] = hpte;
+}
+
+static int64_t
+hpt_find(kvm_t *kd, uint64_t pa)
+{
+	struct hpte *hpte;
+	uint32_t fnv = FNV1_32_INIT;
+
+	fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
+	fnv &= (HPT_SIZE - 1);
+	for (hpte = kd->vmst->hpt_head[fnv]; hpte != NULL; hpte = hpte->next)
+		if (pa == hpte->pa)
+			return (hpte->off);
+
+	return (-1);
+}
+
+static int
+inithash(kvm_t *kd, uint32_t *base, int len, off_t off)
+{
+	uint64_t idx, pa;
+	uint32_t bit, bits;
+
+	for (idx = 0; idx < len / sizeof(*base); idx++) {
+		bits = base[idx];
+		while (bits) {
+			bit = ffs(bits) - 1;
+			bits &= ~(1ul << bit);
+			pa = (idx * sizeof(*base) * NBBY + bit) * PAGE_SIZE;
+			hpt_insert(kd, pa, off);
+			off += PAGE_SIZE;
+		}
+	}
+	return (off);
+}
+
+void
+_kvm_minidump_freevtop(kvm_t *kd)
+{
+	struct vmstate *vm = kd->vmst;
+
+	if (vm->bitmap)
+		free(vm->bitmap);
+	if (vm->ptemap)
+		free(vm->ptemap);
+	free(vm);
+	kd->vmst = NULL;
+}
+
+int
+_kvm_minidump_initvtop(kvm_t *kd)
+{
+	u_long pa;
+	struct vmstate *vmst;
+	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;
+	vmst->minidump = 1;
+
+	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);
+	}
+	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);
+	}
+
+	/* Skip header and msgbuf */
+	off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize);
+
+	vmst->bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
+	if (vmst->bitmap == NULL) {
+		_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
+		    "bitmap", vmst->hdr.bitmapsize);
+		return (-1);
+	}
+
+	if (pread(kd->pmfd, vmst->bitmap, vmst->hdr.bitmapsize, off) !=
+	    vmst->hdr.bitmapsize) {
+		_kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap",
+		    vmst->hdr.bitmapsize);
+		return (-1);
+	}
+	off += 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);
+		return (-1);
+	}
+
+	if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) !=
+	    vmst->hdr.ptesize) {
+		_kvm_err(kd, kd->program, "cannot read %d bytes for ptemap",
+		    vmst->hdr.ptesize);
+		return (-1);
+	}
+
+	off += vmst->hdr.ptesize;
+
+	/* Build physical address hash table for sparse pages */
+	inithash(kd, vmst->bitmap, vmst->hdr.bitmapsize, off);
+
+	return (0);
+}
+
+int
+_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
+{
+	struct vmstate *vm;
+	pt_entry_t pte;
+	u_long offset, pteindex, a;
+	off_t ofs;
+	uint32_t *ptemap;
+	int i;
+
+	if (ISALIVE(kd)) {
+		_kvm_err(kd, 0, "kvm_kvatop called in live kernel!");
+		return (0);
+	}
+
+	vm = kd->vmst;
+	ptemap = vm->ptemap;
+
+	if (va >= vm->hdr.kernbase) {
+		pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
+		pte = ptemap[pteindex];
+		if (!pte) {
+			_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
+			goto invalid;
+		}
+		if ((pte & L2_TYPE_MASK) == L2_TYPE_L) {
+			offset = va & L2_L_OFFSET;
+			a = pte & L2_L_FRAME;
+		} else if ((pte & L2_TYPE_MASK) == L2_TYPE_S) {
+			offset = va & L2_S_OFFSET;
+			a = pte & L2_S_FRAME;
+		} else
+			goto invalid;
+
+		ofs = hpt_find(kd, a);
+		if (ofs == -1) {
+			_kvm_err(kd, kd->program, "_kvm_vatop: physical "
+			    "address 0x%lx not in minidump", a);
+			goto invalid;
+		}
+
+		*pa = ofs + offset;
+		return (PAGE_SIZE - offset);
+
+	} else
+		_kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx "
+		    "not minidumped", va);
+
+invalid:
+	_kvm_err(kd, 0, "invalid address (0x%lx)", va);
+	return (0);
+}

Modified: head/lib/libkvm/kvm_private.h
==============================================================================
--- head/lib/libkvm/kvm_private.h	Thu Nov  6 15:32:02 2008	(r184727)
+++ head/lib/libkvm/kvm_private.h	Thu Nov  6 16:20:27 2008	(r184728)
@@ -79,7 +79,7 @@ void	 _kvm_syserr (kvm_t *kd, const char
 	    __printflike(3, 4);
 int	 _kvm_uvatop(kvm_t *, const struct proc *, u_long, u_long *);
 
-#if defined(__amd64__) || defined(__i386__)
+#if defined(__amd64__) || defined(__i386__) || defined(__arm__)
 void	 _kvm_minidump_freevtop(kvm_t *);
 int	 _kvm_minidump_initvtop(kvm_t *);
 int	 _kvm_minidump_kvatop(kvm_t *, u_long, off_t *);

Modified: head/sys/arm/arm/dump_machdep.c
==============================================================================
--- head/sys/arm/arm/dump_machdep.c	Thu Nov  6 15:32:02 2008	(r184727)
+++ head/sys/arm/arm/dump_machdep.c	Thu Nov  6 16:20:27 2008	(r184728)
@@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/systm.h>
 #include <sys/conf.h>
 #include <sys/cons.h>
+#include <sys/sysctl.h>
 #include <sys/kernel.h>
 #include <sys/proc.h>
 #include <sys/kerneldump.h>
@@ -44,6 +45,11 @@ __FBSDID("$FreeBSD$");
 
 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.
@@ -155,11 +161,10 @@ cb_dumpdata(struct md_pa *mdp, int seqnr
 	vm_offset_t va;
 	uint32_t pgs;
 	size_t counter, sz, chunk;
-	int c, error, twiddle;
+	int c, error;
 
 	error = 0;	/* catch case in which chunk size is 0 */
-	counter = 0;	/* Update twiddle every 16MB */
-	twiddle = 0;
+	counter = 0;
 	va = 0;
 	pgs = mdp->md_size / PAGE_SIZE;
 	pa = mdp->md_start;
@@ -264,7 +269,12 @@ dumpsys(struct dumperinfo *di)
 	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;

Added: head/sys/arm/arm/minidump_machdep.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm/arm/minidump_machdep.c	Thu Nov  6 16:20:27 2008	(r184728)
@@ -0,0 +1,483 @@
+/*-
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/cons.h>
+#include <sys/kernel.h>
+#include <sys/kerneldump.h>
+#include <sys/msgbuf.h>
+#include <sys/vimage.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <machine/pmap.h>
+#include <machine/atomic.h>
+#include <machine/elf.h>
+#include <machine/md_var.h>
+#include <machine/vmparam.h>
+#include <machine/minidump.h>
+#include <machine/cpufunc.h>
+
+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;
+
+/* 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;
+		}
+
+		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_temp(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];
+
+void
+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 cache */
+	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_temp(*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;
+
+	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;
+
+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);
+}
+
+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);
+}

Modified: head/sys/arm/arm/pmap.c
==============================================================================
--- head/sys/arm/arm/pmap.c	Thu Nov  6 15:32:02 2008	(r184727)
+++ head/sys/arm/arm/pmap.c	Thu Nov  6 16:20:27 2008	(r184728)
@@ -270,6 +270,11 @@ union pmap_cache_state *pmap_cache_state
 
 struct msgbuf *msgbufp = 0;
 
+/*
+ * Crashdump maps.
+ */
+static caddr_t crashdumpmap;
+
 extern void bcopy_page(vm_offset_t, vm_offset_t);
 extern void bzero_page(vm_offset_t);
 
@@ -2455,6 +2460,8 @@ pmap_bootstrap(vm_offset_t firstaddr, vm
 
 	pmap_alloc_specials(&virtual_avail,
 	    1, (vm_offset_t*)&_tmppt, NULL);
+	pmap_alloc_specials(&virtual_avail,
+	    MAXDUMPPGS, (vm_offset_t *)&crashdumpmap, NULL);
 	SLIST_INIT(&l1_list);
 	TAILQ_INIT(&l1_lru_list);
 	mtx_init(&l1_lru_lock, "l1 list lock", NULL, MTX_DEF);
@@ -2793,6 +2800,20 @@ pmap_kenter_section(vm_offset_t va, vm_o
 }
 
 /*
+ * Make a temporary mapping for a physical address.  This is only intended
+ * to be used for panic dumps.
+ */
+void *
+pmap_kenter_temp(vm_paddr_t pa, int i)
+{
+	vm_offset_t va;
+
+	va = (vm_offset_t)crashdumpmap + (i * PAGE_SIZE);
+	pmap_kenter(va, pa);
+	return ((void *)crashdumpmap);
+}
+
+/*
  * add a wired page to the kva
  * note that in order for the mapping to take effect -- you
  * should do a invltlb after doing the pmap_kenter...

Modified: head/sys/arm/include/md_var.h
==============================================================================
--- head/sys/arm/include/md_var.h	Thu Nov  6 15:32:02 2008	(r184727)
+++ head/sys/arm/include/md_var.h	Thu Nov  6 16:20:27 2008	(r184728)
@@ -35,6 +35,8 @@
 
 extern char sigcode[];
 extern int szsigcode;
+extern uint32_t *vm_page_dump;
+extern int vm_page_dump_size;
 
 extern int (*_arm_memcpy)(void *, void *, int, int);
 extern int (*_arm_bzero)(void *, int, int);
@@ -46,7 +48,11 @@ extern int _min_bzero_size;
 #define SRC_IS_USER	0x2
 #define IS_PHYSICAL	0x4
 
+struct dumperinfo;
 extern int busdma_swi_pending;
 void busdma_swi(void);
+void dump_add_page(vm_paddr_t);
+void dump_drop_page(vm_paddr_t);
+void minidumpsys(struct dumperinfo *);
 
 #endif /* !_MACHINE_MD_VAR_H_ */

Added: head/sys/arm/include/minidump.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm/include/minidump.h	Thu Nov  6 16:20:27 2008	(r184728)
@@ -0,0 +1,45 @@
+/*-
+ * 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	MINIDUMP_MAGIC		"minidump FreeBSD/arm"
+#define	MINIDUMP_VERSION	1
+
+struct minidumphdr {
+	char magic[24];
+	uint32_t version;
+	uint32_t msgbufsize;
+	uint32_t bitmapsize;
+	uint32_t ptesize;
+	uint32_t kernbase;
+};
+

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-head mailing list