summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/conf/files.powerpc1
-rw-r--r--sys/kern/kern_shutdown.c9
-rw-r--r--sys/powerpc/include/pmap.h12
-rw-r--r--sys/powerpc/powerpc/dump_machdep.c303
-rw-r--r--sys/powerpc/powerpc/mmu_if.m52
-rw-r--r--sys/powerpc/powerpc/pmap_dispatch.c24
-rw-r--r--sys/sys/kerneldump.h11
7 files changed, 398 insertions, 14 deletions
diff --git a/sys/conf/files.powerpc b/sys/conf/files.powerpc
index 42b1beb..dd946fe 100644
--- a/sys/conf/files.powerpc
+++ b/sys/conf/files.powerpc
@@ -139,6 +139,7 @@ powerpc/powerpc/db_disasm.c optional ddb
powerpc/powerpc/db_hwwatch.c optional ddb
powerpc/powerpc/db_interface.c optional ddb
powerpc/powerpc/db_trace.c optional ddb
+powerpc/powerpc/dump_machdep.c standard
powerpc/powerpc/elf_machdep.c standard
powerpc/powerpc/fpu.c optional aim
powerpc/powerpc/fuswintr.c standard
diff --git a/sys/kern/kern_shutdown.c b/sys/kern/kern_shutdown.c
index 157da53..7702ad8 100644
--- a/sys/kern/kern_shutdown.c
+++ b/sys/kern/kern_shutdown.c
@@ -680,15 +680,6 @@ dump_write(struct dumperinfo *di, void *virtual, vm_offset_t physical,
return (di->dumper(di->priv, virtual, physical, offset, length));
}
-#if defined(__powerpc__)
-void
-dumpsys(struct dumperinfo *di __unused)
-{
-
- printf("Kernel dumps not implemented on this architecture\n");
-}
-#endif
-
void
mkdumpheader(struct kerneldumpheader *kdh, char *magic, uint32_t archver,
uint64_t dumplen, uint32_t blksz)
diff --git a/sys/powerpc/include/pmap.h b/sys/powerpc/include/pmap.h
index 2b2d6af..5e6d9c6 100644
--- a/sys/powerpc/include/pmap.h
+++ b/sys/powerpc/include/pmap.h
@@ -71,6 +71,13 @@
#include <machine/pte.h>
#include <machine/tlb.h>
+struct pmap_md {
+ u_int md_index;
+ vm_paddr_t md_paddr;
+ vm_offset_t md_vaddr;
+ vm_size_t md_size;
+};
+
#if defined(AIM)
#if !defined(NPMAPS)
@@ -179,6 +186,11 @@ extern vm_offset_t msgbuf_phys;
extern int pmap_bootstrapped;
+extern vm_offset_t pmap_dumpsys_map(struct pmap_md *, vm_size_t, vm_size_t *);
+extern void pmap_dumpsys_unmap(struct pmap_md *, vm_size_t, vm_offset_t);
+
+extern struct pmap_md *pmap_scan_md(struct pmap_md *);
+
#endif
#endif /* !_MACHINE_PMAP_H_ */
diff --git a/sys/powerpc/powerpc/dump_machdep.c b/sys/powerpc/powerpc/dump_machdep.c
new file mode 100644
index 0000000..acda560
--- /dev/null
+++ b/sys/powerpc/powerpc/dump_machdep.c
@@ -0,0 +1,303 @@
+/*-
+ * 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.
+ * 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 <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/sysctl.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <machine/elf.h>
+#include <machine/md_var.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)
+
+#define MD_ALIGN(x) (((off_t)(x) + PAGE_MASK) & ~PAGE_MASK)
+#define DEV_ALIGN(x) (((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1))
+
+typedef int callback_t(struct pmap_md *, int, void *);
+
+static struct kerneldumpheader kdh;
+static off_t dumplo, fileofs;
+
+/* Handle buffered writes. */
+static char buffer[DEV_BSIZE];
+static size_t fragsz;
+
+int dumpsys_minidump = 1;
+SYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RD, &dumpsys_minidump, 0,
+ "Kernel makes compressed crash dumps");
+
+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 = di->dumper(di->priv, 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 = di->dumper(di->priv, buffer, 0, dumplo, DEV_BSIZE);
+ dumplo += DEV_BSIZE;
+ fragsz = 0;
+ return (error);
+}
+
+static int
+cb_dumpdata(struct pmap_md *md, int seqnr, void *arg)
+{
+ struct dumperinfo *di = (struct dumperinfo*)arg;
+ vm_offset_t va;
+ size_t counter, ofs, resid, sz;
+ int c, error, twiddle;
+
+ error = 0;
+ counter = 0; /* Update twiddle every 16MB */
+ twiddle = 0;
+
+ ofs = 0; /* Logical offset within the chunk */
+ resid = md->md_size;
+
+ printf(" chunk %d: %lu bytes ", seqnr, (u_long)resid);
+
+ while (resid) {
+ sz = (resid > DFLTPHYS) ? DFLTPHYS : resid;
+ va = pmap_dumpsys_map(md, ofs, &sz);
+ counter += sz;
+ if (counter >> 24) {
+ printf("%c\b", "|/-\\"[twiddle++ & 3]);
+ counter &= (1<<24) - 1;
+ }
+ error = di->dumper(di->priv, (void*)va, 0, dumplo, sz);
+ pmap_dumpsys_unmap(md, ofs, va);
+ if (error)
+ break;
+ dumplo += sz;
+ resid -= sz;
+ ofs += 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 pmap_md *md, int seqnr, void *arg)
+{
+ struct dumperinfo *di = (struct dumperinfo*)arg;
+ Elf32_Phdr phdr;
+ int error;
+
+ bzero(&phdr, sizeof(phdr));
+ phdr.p_type = PT_LOAD;
+ phdr.p_flags = PF_R; /* XXX */
+ phdr.p_offset = fileofs;
+ phdr.p_vaddr = md->md_vaddr;
+ phdr.p_paddr = md->md_paddr;
+ phdr.p_filesz = md->md_size;
+ phdr.p_memsz = md->md_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 pmap_md *md, int seqnr, void *arg)
+{
+ uint32_t *sz = (uint32_t*)arg;
+
+ *sz += md->md_size;
+ return (0);
+}
+
+static int
+foreach_chunk(callback_t cb, void *arg)
+{
+ struct pmap_md *md;
+ int error, seqnr;
+
+ seqnr = 0;
+ md = pmap_scan_md(NULL);
+ while (md != NULL) {
+ error = (*cb)(md, seqnr++, arg);
+ if (error)
+ return (-error);
+ md = pmap_scan_md(md);
+ }
+ return (seqnr);
+}
+
+void
+dumpsys(struct dumperinfo *di)
+{
+ Elf32_Ehdr ehdr;
+ uint32_t dumpsize;
+ off_t hdrgap;
+ size_t hdrsz;
+ int error;
+
+ 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] = ELFCLASS32;
+#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_PPC;
+ ehdr.e_phoff = sizeof(ehdr);
+ ehdr.e_ehsize = sizeof(ehdr);
+ ehdr.e_phentsize = sizeof(Elf32_Phdr);
+ ehdr.e_shentsize = sizeof(Elf32_Shdr);
+
+ /* 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);
+
+ /* For block devices, determine the dump offset on the device. */
+ if (di->mediasize > 0) {
+ if (di->mediasize <
+ SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
+ error = ENOSPC;
+ goto fail;
+ }
+ dumplo = di->mediaoffset + di->mediasize - dumpsize;
+ dumplo -= sizeof(kdh) * 2;
+ } else
+ dumplo = 0;
+
+ mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_POWERPC_VERSION, dumpsize,
+ di->blocksize);
+
+ printf("Dumping %u MB (%d chunks)\n", dumpsize >> 20,
+ ehdr.e_phnum);
+
+ /* Dump leader */
+ error = di->dumper(di->priv, &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 = di->dumper(di->priv, &kdh, 0, dumplo, sizeof(kdh));
+ if (error)
+ goto fail;
+
+ /* Signal completion, signoff and exit stage left. */
+ di->dumper(di->priv, NULL, 0, 0, 0);
+ printf("\nDump complete\n");
+ return;
+
+ fail:
+ if (error < 0)
+ error = -error;
+
+ if (error == ECANCELED)
+ printf("\nDump aborted\n");
+ else
+ printf("\n** DUMP FAILED (ERROR %d) **\n", error);
+}
diff --git a/sys/powerpc/powerpc/mmu_if.m b/sys/powerpc/powerpc/mmu_if.m
index 9b7bdd2..4a5a37c 100644
--- a/sys/powerpc/powerpc/mmu_if.m
+++ b/sys/powerpc/powerpc/mmu_if.m
@@ -105,6 +105,11 @@ CODE {
{
return;
}
+
+ static struct pmap_md *mmu_null_scan_md(mmu_t mmu, struct pmap_md *p)
+ {
+ return (NULL);
+ }
};
@@ -795,3 +800,50 @@ METHOD boolean_t page_executable {
vm_page_t _pg;
};
+
+/**
+ * @brief Create temporary memory mapping for use by dumpsys().
+ *
+ * @param _md The memory chunk in which the mapping lies.
+ * @param _ofs The offset within the chunk of the mapping.
+ * @param _sz The requested size of the mapping.
+ *
+ * @retval vm_offset_t The virtual address of the mapping.
+ *
+ * The sz argument is modified to reflect the actual size of the
+ * mapping.
+ */
+METHOD vm_offset_t dumpsys_map {
+ mmu_t _mmu;
+ struct pmap_md *_md;
+ vm_size_t _ofs;
+ vm_size_t *_sz;
+};
+
+
+/**
+ * @brief Remove temporary dumpsys() mapping.
+ *
+ * @param _md The memory chunk in which the mapping lies.
+ * @param _ofs The offset within the chunk of the mapping.
+ * @param _va The virtual address of the mapping.
+ */
+METHOD void dumpsys_unmap {
+ mmu_t _mmu;
+ struct pmap_md *_md;
+ vm_size_t _ofs;
+ vm_offset_t _va;
+};
+
+
+/**
+ * @brief Scan/iterate memory chunks.
+ *
+ * @param _prev The previously returned chunk or NULL.
+ *
+ * @retval The next (or first when _prev is NULL) chunk.
+ */
+METHOD struct pmap_md * scan_md {
+ mmu_t _mmu;
+ struct pmap_md *_prev;
+} DEFAULT mmu_null_scan_md;
diff --git a/sys/powerpc/powerpc/pmap_dispatch.c b/sys/powerpc/powerpc/pmap_dispatch.c
index a60c076..af657d4 100644
--- a/sys/powerpc/powerpc/pmap_dispatch.c
+++ b/sys/powerpc/powerpc/pmap_dispatch.c
@@ -465,6 +465,30 @@ pmap_page_executable(vm_page_t pg)
return (MMU_PAGE_EXECUTABLE(mmu_obj, pg));
}
+vm_offset_t
+pmap_dumpsys_map(struct pmap_md *md, vm_size_t ofs, vm_size_t *sz)
+{
+
+ CTR4(KTR_PMAP, "%s(%p, %#x, %#x)", __func__, md, ofs, *sz);
+ return (MMU_DUMPSYS_MAP(mmu_obj, md, ofs, sz));
+}
+
+void
+pmap_dumpsys_unmap(struct pmap_md *md, vm_size_t ofs, vm_offset_t va)
+{
+
+ CTR4(KTR_PMAP, "%s(%p, %#x, %#x)", __func__, md, ofs, va);
+ return (MMU_DUMPSYS_UNMAP(mmu_obj, md, ofs, va));
+}
+
+struct pmap_md *
+pmap_scan_md(struct pmap_md *prev)
+{
+
+ CTR2(KTR_PMAP, "%s(%p)", __func__, prev);
+ return (MMU_SCAN_MD(mmu_obj, prev));
+}
+
/*
* MMU install routines. Highest priority wins, equal priority also
* overrides allowing last-set to win.
diff --git a/sys/sys/kerneldump.h b/sys/sys/kerneldump.h
index 3f3eacd..e3421de 100644
--- a/sys/sys/kerneldump.h
+++ b/sys/sys/kerneldump.h
@@ -67,12 +67,13 @@ struct kerneldumpheader {
#define KERNELDUMPVERSION 1
uint32_t architectureversion;
#define KERNELDUMP_ALPHA_VERSION 1
-#define KERNELDUMP_I386_VERSION 2
-#define KERNELDUMP_IA64_VERSION 1
-#define KERNELDUMP_SPARC64_VERSION 1
#define KERNELDUMP_AMD64_VERSION 2
-#define KERNELDUMP_ARM_VERSION 1
-#define KERNELDUMP_TEXT_VERSION 1
+#define KERNELDUMP_ARM_VERSION 1
+#define KERNELDUMP_I386_VERSION 2
+#define KERNELDUMP_IA64_VERSION 1
+#define KERNELDUMP_POWERPC_VERSION 1
+#define KERNELDUMP_SPARC64_VERSION 1
+#define KERNELDUMP_TEXT_VERSION 1
uint64_t dumplength; /* excl headers */
uint64_t dumptime;
uint32_t blocksize;
OpenPOWER on IntegriCloud