From c359d73311b2a848ff9cc2eaecb6f96cb0b6aa12 Mon Sep 17 00:00:00 2001 From: Conrad Meyer Date: Sun, 21 Sep 2014 17:29:32 -0700 Subject: [PATCH] Unify dumpsys() under generic kern_dump.c. (To varying degrees by arch.) x86, ARM, and MIPS are all relatively similar and straightforward. Some MD-specific methods are left in dump_machdep.c in each arch to provide mach-dependent implementations. (Map a region temporarily for dumping, unmap a region, iterate physical memory segments, flush WB caches.) Sparc and PowerPC are weirder. PowerPC had a merged dump/minidump path that used a different md_pa structure, pmap_md, plumbed through its MMU interface. So, that was ripped out and replaced with the standard path. Sparc uses its own non-ELF dumping header and that makes its dumpsys different enough that unification wasn't an improvement. However, some logic shared with other archs (blk_dump == cb_dumpdata) was refactored away. Patch build-tested against: - ARMv6 / CHROMEBOOK - AMD64 / GENERIC - i386 / GENERIC - MIPS / WZR-300HP - MIPS64 / SWARM64_SMP - PPC / MPC85XX (cpu=booke) - PPC / GENERIC (cpu=aim) - PPC64 / GENERIC64 (cpu=aim64) - Sparc64 / GENERIC Sponsored by: EMC / Isilon storage division --- sys/amd64/include/md_var.h | 5 + sys/arm/arm/dump_machdep.c | 325 +---------------------------- sys/arm/include/md_var.h | 7 + sys/conf/files | 1 + sys/conf/files.powerpc | 1 - sys/i386/include/md_var.h | 5 + sys/kern/kern_dump.c | 402 ++++++++++++++++++++++++++++++++++++ sys/mips/include/md_var.h | 8 + sys/mips/mips/dump_machdep.c | 318 +--------------------------- sys/powerpc/aim/mmu_oea.c | 167 +++++++-------- sys/powerpc/aim/mmu_oea64.c | 164 +++++++-------- sys/powerpc/booke/pmap.c | 228 ++++++++++---------- sys/powerpc/include/md_var.h | 10 + sys/powerpc/include/pmap.h | 13 +- sys/powerpc/powerpc/dump_machdep.c | 315 ---------------------------- sys/powerpc/powerpc/mmu_if.m | 41 ++-- sys/powerpc/powerpc/pmap_dispatch.c | 28 ++- sys/sparc64/include/md_var.h | 5 + sys/sparc64/sparc64/dump_machdep.c | 115 +++-------- sys/sys/conf.h | 12 ++ sys/x86/x86/dump_machdep.c | 347 +------------------------------ 21 files changed, 820 insertions(+), 1697 deletions(-) create mode 100644 sys/kern/kern_dump.c delete mode 100644 sys/powerpc/powerpc/dump_machdep.c diff --git a/sys/amd64/include/md_var.h b/sys/amd64/include/md_var.h index c7b89a6..e741d79 100644 --- a/sys/amd64/include/md_var.h +++ b/sys/amd64/include/md_var.h @@ -122,6 +122,11 @@ void minidumpsys(struct dumperinfo *); struct savefpu *get_pcb_user_save_td(struct thread *td); struct savefpu *get_pcb_user_save_pcb(struct pcb *pcb); struct pcb *get_pcb_td(struct thread *td); void amd64_db_resume_dbreg(void); +#define KERNELDUMP_VERSION KERNELDUMP_AMD64_VERSION +#define EM_VALUE EM_X86_64 +/* 20 phys_avail entry pairs correspond to 10 md_pa's */ +#define DUMPSYS_MD_PA_NPAIRS 10 + #endif /* !_MACHINE_MD_VAR_H_ */ diff --git a/sys/arm/arm/dump_machdep.c b/sys/arm/arm/dump_machdep.c index b5a37f2..1df6086 100644 --- a/sys/arm/arm/dump_machdep.c +++ b/sys/arm/arm/dump_machdep.c @@ -45,136 +45,14 @@ __FBSDID("$FreeBSD$"); #include #include #include #include -CTASSERT(sizeof(struct kerneldumpheader) == 512); - -int do_minidump = 1; -SYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RWTUN, &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) +void +dumpsys_wbinv_all(void) { - 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 data. Note that in the SMP case this * only operates on the L1 cache of the current CPU, but all other CPUs * have already been stopped, and their flush/invalidate was done as * part of stopping. @@ -182,203 +60,18 @@ cb_dumpdata(struct md_pa *mdp, int seqnr, void *arg) cpu_idcache_wbinv_all(); cpu_l2cache_wbinv_all(); #ifdef __XSCALE__ xscale_cache_clean_minidata(); #endif - 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; - } - if (pa == (pa & L1_ADDR_BITS)) { - pmap_kenter_section(0, pa & L1_ADDR_BITS, 0); - cpu_tlb_flushID_SE(0); - cpu_cpwait(); - } -#ifdef SW_WATCHDOG - wdog_kern_pat(WD_LASTVAL); -#endif - error = dump_write(di, - (void *)(pa - (pa & L1_ADDR_BITS)),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) +void +dumpsys_map_chunk(vm_paddr_t pa, size_t chunk __unused, void **va) { - 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); + if (pa == (pa & L1_ADDR_BITS)) { + pmap_kenter_section(0, pa & L1_ADDR_BITS, 0); + cpu_tlb_flushID_SE(0); + cpu_cpwait(); } - return (seqnr); -} - -int -dumpsys(struct dumperinfo *di) -{ - Elf_Ehdr ehdr; - uint32_t dumpsize; - off_t hdrgap; - size_t hdrsz; - int error; - - if (do_minidump) { - minidumpsys(di); - return (0); - } - - 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_ARM; - 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_ARM_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 (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); + *va = (void *)(pa - (pa & L1_ADDR_BITS)); } diff --git a/sys/arm/include/md_var.h b/sys/arm/include/md_var.h index 7337b33..f3ba2e1 100644 --- a/sys/arm/include/md_var.h +++ b/sys/arm/include/md_var.h @@ -68,6 +68,13 @@ 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 *); +#define KERNELDUMP_VERSION KERNELDUMP_ARM_VERSION +#define EM_VALUE EM_ARM +/* XXX: I suppose 20 should be enough. */ +#define DUMPSYS_MD_PA_NPAIRS 20 +#define DUMPSYS_HAS_MD_WBINV 1 +void dumpsys_wbinv_all(void); + #endif /* !_MACHINE_MD_VAR_H_ */ diff --git a/sys/conf/files b/sys/conf/files index 7a98f9c..55331bb 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -2927,10 +2927,11 @@ kern/kern_cons.c standard kern/kern_cpu.c standard kern/kern_cpuset.c standard kern/kern_context.c standard kern/kern_descrip.c standard kern/kern_dtrace.c optional kdtrace_hooks +kern/kern_dump.c standard kern/kern_environment.c standard kern/kern_et.c standard kern/kern_event.c standard kern/kern_exec.c standard kern/kern_exit.c standard diff --git a/sys/conf/files.powerpc b/sys/conf/files.powerpc index 10515a5..ced3115 100644 --- a/sys/conf/files.powerpc +++ b/sys/conf/files.powerpc @@ -181,11 +181,10 @@ powerpc/powerpc/copystr.c standard powerpc/powerpc/cpu.c standard 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/elf32_machdep.c optional powerpc | compat_freebsd32 powerpc/powerpc/elf64_machdep.c optional powerpc64 powerpc/powerpc/exec_machdep.c standard powerpc/powerpc/fpu.c standard powerpc/powerpc/fuswintr.c standard diff --git a/sys/i386/include/md_var.h b/sys/i386/include/md_var.h index 950fa1f..e02a706 100644 --- a/sys/i386/include/md_var.h +++ b/sys/i386/include/md_var.h @@ -113,6 +113,11 @@ void ppro_reenable_apic(void); void printcpuinfo(void); void setidt(int idx, alias_for_inthand_t *func, int typ, int dpl, int selec); int user_dbreg_trap(void); void minidumpsys(struct dumperinfo *); +#define KERNELDUMP_VERSION KERNELDUMP_I386_VERSION +#define EM_VALUE EM_386 +/* 20 phys_avail entry pairs correspond to 10 md_pa's */ +#define DUMPSYS_MD_PA_NPAIRS 10 + #endif /* !_MACHINE_MD_VAR_H_ */ diff --git a/sys/kern/kern_dump.c b/sys/kern/kern_dump.c new file mode 100644 index 0000000..f48a02a --- /dev/null +++ b/sys/kern/kern_dump.c @@ -0,0 +1,402 @@ +/*- + * 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 +__FBSDID("$FreeBSD$"); + +#include "opt_watchdog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef SW_WATCHDOG +#include +#endif +#include +#include +#include +#include +#include +#include + +CTASSERT(sizeof(struct kerneldumpheader) == 512); + +#ifndef __sparc__ +int do_minidump = 1; +SYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RWTUN, &do_minidump, 0, + "Enable mini crash dumps"); +#endif + +/* + * 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)) + +off_t dumplo; + +/* Handle buffered writes. */ +static char buffer[DEV_BSIZE]; +static size_t fragsz; + +struct md_pa dump_map[DUMPSYS_MD_PA_NPAIRS]; + +#ifndef DUMPSYS_HAS_MD_PA_INIT +static void +dumpsys_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]; + } +} +#endif + +#ifndef DUMPSYS_HAS_MD_PA_NEXT +static struct md_pa * +dumpsys_md_pa_next(struct md_pa *mdp) +{ + + if (mdp == NULL) + return (&dump_map[0]); + + mdp++; + if (mdp->md_size == 0) + mdp = NULL; + return (mdp); +} +#endif + +#ifndef DUMPSYS_HAS_MD_WBINV +static void +dumpsys_wbinv_all(void) +{ + + /* nop */; +} +#endif + +#ifndef DUMPSYS_HAS_MD_UNMAP +static void +dumpsys_unmap_chunk(vm_paddr_t pa __unused, size_t chunk __unused, + void *va __unused) +{ + + /* nop */; +} +#endif + +int +dumpsys_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); +} + +int +dumpsys_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); +} + +CTASSERT(PAGE_SHIFT < 20); +#define PG2MB(pgs) ((pgs + (1 << (20 - PAGE_SHIFT)) - 1) >> (20 - PAGE_SHIFT)) + +int +dumpsys_cb_dumpdata(struct md_pa *mdp, int seqnr, void *arg) +{ + struct dumperinfo *di = (struct dumperinfo*)arg; + vm_paddr_t pa; + void *va; + uint64_t pgs; + size_t counter, sz, chunk; + int c, error, twiddle; + u_int maxdumppgs; + + error = 0; /* catch case in which chunk size is 0 */ + counter = 0; /* Update twiddle every 16MB */ + twiddle = 0; + va = 0; + pgs = mdp->md_size / PAGE_SIZE; + pa = mdp->md_start; + maxdumppgs = min(di->maxiosize / PAGE_SIZE, MAXDUMPPGS); + if (maxdumppgs == 0) /* seatbelt */ + maxdumppgs = 1; + + printf(" chunk %d: %juMB (%ju pages)", seqnr, (uintmax_t)PG2MB(pgs), + (uintmax_t)pgs); + + dumpsys_wbinv_all(); + while (pgs) { + chunk = pgs; + if (chunk > maxdumppgs) + chunk = maxdumppgs; + sz = chunk << PAGE_SHIFT; + counter += sz; + if (counter >> 24) { + printf(" %ju", (uintmax_t)PG2MB(pgs)); + counter &= (1<<24) - 1; + } + + dumpsys_map_chunk(pa, chunk, &va); +#ifdef SW_WATCHDOG + wdog_kern_pat(WD_LASTVAL); +#endif + + error = dump_write(di, va, 0, dumplo, sz); + dumpsys_unmap_chunk(pa, chunk, va); + 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); +} + +int +dumpsys_foreach_chunk(dumpsys_callback_t cb, void *arg) +{ + struct md_pa *mdp; + int error, seqnr; + + seqnr = 0; + mdp = dumpsys_md_pa_next(NULL); + while (mdp != NULL) { + error = (*cb)(mdp, seqnr++, arg); + if (error) + return (-error); + mdp = dumpsys_md_pa_next(mdp); + } + return (seqnr); +} + +#ifndef DUMPSYS_HAS_MD_DUMPSYS +static off_t fileofs; + +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; +#ifdef __powerpc__ + phdr.p_vaddr = (do_minidump? mdp->md_start : ~0L); + phdr.p_paddr = (do_minidump? ~0L : mdp->md_start); +#else + phdr.p_vaddr = mdp->md_start; + phdr.p_paddr = mdp->md_start; +#endif + phdr.p_filesz = size; + phdr.p_memsz = size; + phdr.p_align = PAGE_SIZE; + + error = dumpsys_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) +{ + uint64_t *sz = (uint64_t*)arg; + + *sz += (uint64_t)mdp->md_size; + return (0); +} + +int +dumpsys(struct dumperinfo *di) +{ + struct kerneldumpheader kdh; + Elf_Ehdr ehdr; + uint64_t dumpsize; + off_t hdrgap; + size_t hdrsz; + int error; + +#ifndef __powerpc__ + if (do_minidump) { + minidumpsys(di); + return (0); + } +#endif + 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_VALUE; + 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); + + dumpsys_md_pa_init(); + + /* Calculate dump size. */ + dumpsize = 0L; + ehdr.e_phnum = dumpsys_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_VERSION, dumpsize, + di->blocksize); + + printf("Dumping %ju MB (%d chunks)\n", (uintmax_t)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 = dumpsys_buf_write(di, (char*)&ehdr, sizeof(ehdr)); + if (error) + goto fail; + + /* Dump program headers */ + error = dumpsys_foreach_chunk(cb_dumphdr, di); + if (error < 0) + goto fail; + dumpsys_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 = dumpsys_foreach_chunk(dumpsys_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 (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); +} +#endif /* !DUMPSYS_HAS_MD_DUMPSYS */ diff --git a/sys/mips/include/md_var.h b/sys/mips/include/md_var.h index 71a78e7..51a55d1 100644 --- a/sys/mips/include/md_var.h +++ b/sys/mips/include/md_var.h @@ -78,6 +78,14 @@ void busdma_swi(void); struct dumperinfo; void dump_add_page(vm_paddr_t); void dump_drop_page(vm_paddr_t); void minidumpsys(struct dumperinfo *); + +#define KERNELDUMP_VERSION KERNELDUMP_MIPS_VERSION +#define EM_VALUE EM_MIPS +/* XXX: I suppose 20 should be enough. */ +#define DUMPSYS_MD_PA_NPAIRS 20 +#define DUMPSYS_HAS_MD_WBINV 1 +void dumpsys_wbinv_all(void); + #endif /* !_MACHINE_MD_VAR_H_ */ diff --git a/sys/mips/mips/dump_machdep.c b/sys/mips/mips/dump_machdep.c index 26f93ff..bf1462d 100644 --- a/sys/mips/mips/dump_machdep.c +++ b/sys/mips/mips/dump_machdep.c @@ -45,326 +45,20 @@ __FBSDID("$FreeBSD$"); #include #include #include #include -CTASSERT(sizeof(struct kerneldumpheader) == 512); - -int do_minidump = 1; -SYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RWTUN, &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) +void +dumpsys_wbinv_all(void) { - 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; - } - -#ifdef SW_WATCHDOG - wdog_kern_pat(WD_LASTVAL); -#endif - error = dump_write(di, (void *)(intptr_t)(pa),0, dumplo, sz); /* XXX fix PA */ - 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); } -int -dumpsys(struct dumperinfo *di) +void +dumpsys_map_chunk(vm_paddr_t pa, size_t chunk __unused, void **va) { - Elf_Ehdr ehdr; - uint32_t dumpsize; - off_t hdrgap; - size_t hdrsz; - int error; - - if (do_minidump) { - minidumpsys(di); - return (0); - } - - 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 (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); + /* XXX fix PA */ + *va = (void*)(intptr_t)pa; } diff --git a/sys/powerpc/aim/mmu_oea.c b/sys/powerpc/aim/mmu_oea.c index 742dd70..834f695 100644 --- a/sys/powerpc/aim/mmu_oea.c +++ b/sys/powerpc/aim/mmu_oea.c @@ -104,10 +104,11 @@ __FBSDID("$FreeBSD$"); #include "opt_kstack_pages.h" #include #include +#include #include #include #include #include #include @@ -161,12 +162,10 @@ struct ofw_map { }; extern unsigned char _etext[]; extern unsigned char _end[]; -extern int dumpsys_minidump; - /* * Map of physical memory regions. */ static struct mem_region *regions; static struct mem_region *pregions; @@ -312,13 +311,13 @@ vm_paddr_t moea_kextract(mmu_t, vm_offset_t); void moea_kenter_attr(mmu_t, vm_offset_t, vm_offset_t, vm_memattr_t); void moea_kenter(mmu_t, vm_offset_t, vm_paddr_t); void moea_page_set_memattr(mmu_t mmu, vm_page_t m, vm_memattr_t ma); boolean_t moea_dev_direct_mapped(mmu_t, vm_paddr_t, vm_size_t); static void moea_sync_icache(mmu_t, pmap_t, vm_offset_t, vm_size_t); -vm_offset_t moea_dumpsys_map(mmu_t mmu, struct pmap_md *md, vm_size_t ofs, - vm_size_t *sz); -struct pmap_md * moea_scan_md(mmu_t mmu, struct pmap_md *prev); +void moea_dumpsys_map(mmu_t mmu, vm_paddr_t pa, size_t sz, void **va); +void moea_scan_init(mmu_t mmu); +struct md_pa * moea_scan_md(mmu_t mmu, struct md_pa *prev); static mmu_method_t moea_methods[] = { MMUMETHOD(mmu_clear_modify, moea_clear_modify), MMUMETHOD(mmu_copy_page, moea_copy_page), MMUMETHOD(mmu_copy_pages, moea_copy_pages), @@ -361,10 +360,11 @@ static mmu_method_t moea_methods[] = { MMUMETHOD(mmu_unmapdev, moea_unmapdev), MMUMETHOD(mmu_kextract, moea_kextract), MMUMETHOD(mmu_kenter, moea_kenter), MMUMETHOD(mmu_kenter_attr, moea_kenter_attr), MMUMETHOD(mmu_dev_direct_mapped,moea_dev_direct_mapped), + MMUMETHOD(mmu_scan_init, moea_scan_init), MMUMETHOD(mmu_scan_md, moea_scan_md), MMUMETHOD(mmu_dumpsys_map, moea_dumpsys_map), { 0, 0 } }; @@ -2626,102 +2626,91 @@ moea_sync_icache(mmu_t mmu, pmap_t pm, vm_offset_t va, vm_size_t sz) sz -= len; } PMAP_UNLOCK(pm); } -vm_offset_t -moea_dumpsys_map(mmu_t mmu, struct pmap_md *md, vm_size_t ofs, - vm_size_t *sz) +void +moea_dumpsys_map(mmu_t mmu, vm_paddr_t pa, size_t sz, void **va) { - if (md->md_vaddr == ~0UL) - return (md->md_paddr + ofs); - else - return (md->md_vaddr + ofs); + + *va = (void *)pa; } -struct pmap_md * -moea_scan_md(mmu_t mmu, struct pmap_md *prev) +extern struct md_pa dump_map[PHYS_AVAIL_SZ + 1]; +static struct md_pa dump_vmap[4]; + +void +moea_scan_init(mmu_t mmu) { - static struct pmap_md md; struct pvo_entry *pvo; vm_offset_t va; - - if (dumpsys_minidump) { - md.md_paddr = ~0UL; /* Minidumps use virtual addresses. */ - if (prev == NULL) { - /* 1st: kernel .data and .bss. */ - md.md_index = 1; - md.md_vaddr = trunc_page((uintptr_t)_etext); - md.md_size = round_page((uintptr_t)_end) - md.md_vaddr; - return (&md); + int i; + + /* Initialize phys. segments for dumpsys(). */ + memset(&dump_map, 0, sizeof(dump_map)); + mem_regions(&pregions, &pregions_sz, ®ions, ®ions_sz); + for (i = 0; i < pregions_sz; i++) { + dump_map[i].md_start = pregions[i].mr_start; + dump_map[i].md_size = pregions[i].mr_size; + } + + /* Virtual segments for minidumps: */ + memset(&dump_vmap, 0, sizeof(dump_vmap)); + + /* 1st: kernel .data and .bss. */ + dump_vmap[0].md_start = trunc_page((uintptr_t)_etext); + dump_vmap[0].md_size = round_page((uintptr_t)_end) - dump_vmap[0].md_start; + + /* 2nd: msgbuf and tables (see pmap_bootstrap()). */ + dump_vmap[1].md_start = (vm_paddr_t)msgbufp->msg_ptr; + dump_vmap[1].md_size = round_page(msgbufp->msg_size); + + /* 3rd: kernel VM. */ + va = dump_vmap[1].md_start + dump_vmap[1].md_size; + /* Find start of next chunk (from va). */ + while (va < virtual_end) { + /* Don't dump the buffer cache. */ + if (va >= kmi.buffer_sva && va < kmi.buffer_eva) { + va = kmi.buffer_eva; + continue; } - switch (prev->md_index) { - case 1: - /* 2nd: msgbuf and tables (see pmap_bootstrap()). */ - md.md_index = 2; - md.md_vaddr = (vm_offset_t)msgbufp->msg_ptr; - md.md_size = round_page(msgbufp->msg_size); + pvo = moea_pvo_find_va(kernel_pmap, va & ~ADDR_POFF, NULL); + if (pvo != NULL && (pvo->pvo_pte.pte.pte_hi & PTE_VALID)) break; - case 2: - /* 3rd: kernel VM. */ - va = prev->md_vaddr + prev->md_size; - /* Find start of next chunk (from va). */ - while (va < virtual_end) { - /* Don't dump the buffer cache. */ - if (va >= kmi.buffer_sva && - va < kmi.buffer_eva) { - va = kmi.buffer_eva; - continue; - } - pvo = moea_pvo_find_va(kernel_pmap, - va & ~ADDR_POFF, NULL); - if (pvo != NULL && - (pvo->pvo_pte.pte.pte_hi & PTE_VALID)) - break; - va += PAGE_SIZE; - } - if (va < virtual_end) { - md.md_vaddr = va; - va += PAGE_SIZE; - /* Find last page in chunk. */ - while (va < virtual_end) { - /* Don't run into the buffer cache. */ - if (va == kmi.buffer_sva) - break; - pvo = moea_pvo_find_va(kernel_pmap, - va & ~ADDR_POFF, NULL); - if (pvo == NULL || - !(pvo->pvo_pte.pte.pte_hi & PTE_VALID)) - break; - va += PAGE_SIZE; - } - md.md_size = va - md.md_vaddr; + va += PAGE_SIZE; + } + if (va < virtual_end) { + dump_vmap[2].md_start = va; + va += PAGE_SIZE; + /* Find last page in chunk. */ + while (va < virtual_end) { + /* Don't run into the buffer cache. */ + if (va == kmi.buffer_sva) break; - } - md.md_index = 3; - /* FALLTHROUGH */ - default: - return (NULL); - } - } else { /* minidumps */ - mem_regions(&pregions, &pregions_sz, - ®ions, ®ions_sz); - - if (prev == NULL) { - /* first physical chunk. */ - md.md_paddr = pregions[0].mr_start; - md.md_size = pregions[0].mr_size; - md.md_vaddr = ~0UL; - md.md_index = 1; - } else if (md.md_index < pregions_sz) { - md.md_paddr = pregions[md.md_index].mr_start; - md.md_size = pregions[md.md_index].mr_size; - md.md_vaddr = ~0UL; - md.md_index++; - } else { - /* There's no next physical chunk. */ - return (NULL); + pvo = moea_pvo_find_va(kernel_pmap, va & ~ADDR_POFF, + NULL); + if (pvo == NULL || + !(pvo->pvo_pte.pte.pte_hi & PTE_VALID)) + break; + va += PAGE_SIZE; } + dump_vmap[2].md_size = va - dump_vmap[2].md_start; + } +} + +struct md_pa * +moea_scan_md(mmu_t mmu, struct md_pa *prev) +{ + + if (prev == NULL) { + if (do_minidump) + return (&dump_vmap[0]); + else + return (&dump_map[0]); } - return (&md); + prev++; + /* There's no next chunk. */ + if (prev->md_size == 0) + return (NULL); + return (prev); } diff --git a/sys/powerpc/aim/mmu_oea64.c b/sys/powerpc/aim/mmu_oea64.c index 2db7fcb..18d3c1a 100644 --- a/sys/powerpc/aim/mmu_oea64.c +++ b/sys/powerpc/aim/mmu_oea64.c @@ -105,10 +105,11 @@ __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_kstack_pages.h" #include #include +#include #include #include #include #include #include @@ -185,12 +186,10 @@ struct ofw_map { }; extern unsigned char _etext[]; extern unsigned char _end[]; -extern int dumpsys_minidump; - /* * Map of physical memory regions. */ static struct mem_region *regions; static struct mem_region *pregions; @@ -325,13 +324,14 @@ vm_paddr_t moea64_kextract(mmu_t, vm_offset_t); void moea64_page_set_memattr(mmu_t, vm_page_t m, vm_memattr_t ma); void moea64_kenter_attr(mmu_t, vm_offset_t, vm_offset_t, vm_memattr_t ma); void moea64_kenter(mmu_t, vm_offset_t, vm_paddr_t); boolean_t moea64_dev_direct_mapped(mmu_t, vm_paddr_t, vm_size_t); static void moea64_sync_icache(mmu_t, pmap_t, vm_offset_t, vm_size_t); -vm_offset_t moea64_dumpsys_map(mmu_t mmu, struct pmap_md *md, vm_size_t ofs, - vm_size_t *sz); -struct pmap_md * moea64_scan_md(mmu_t mmu, struct pmap_md *prev); +void moea64_dumpsys_map(mmu_t mmu, vm_paddr_t pa, size_t sz, + void **va); +struct md_pa * moea64_scan_md(mmu_t mmu, struct md_pa *prev); +void moea64_scan_init(mmu_t mmu); static mmu_method_t moea64_methods[] = { MMUMETHOD(mmu_clear_modify, moea64_clear_modify), MMUMETHOD(mmu_copy_page, moea64_copy_page), MMUMETHOD(mmu_copy_pages, moea64_copy_pages), @@ -373,10 +373,11 @@ static mmu_method_t moea64_methods[] = { MMUMETHOD(mmu_unmapdev, moea64_unmapdev), MMUMETHOD(mmu_kextract, moea64_kextract), MMUMETHOD(mmu_kenter, moea64_kenter), MMUMETHOD(mmu_kenter_attr, moea64_kenter_attr), MMUMETHOD(mmu_dev_direct_mapped,moea64_dev_direct_mapped), + MMUMETHOD(mmu_scan_init, moea64_scan_init), MMUMETHOD(mmu_scan_md, moea64_scan_md), MMUMETHOD(mmu_dumpsys_map, moea64_dumpsys_map), { 0, 0 } }; @@ -2610,99 +2611,90 @@ moea64_sync_icache(mmu_t mmu, pmap_t pm, vm_offset_t va, vm_size_t sz) sz -= len; } PMAP_UNLOCK(pm); } -vm_offset_t -moea64_dumpsys_map(mmu_t mmu, struct pmap_md *md, vm_size_t ofs, - vm_size_t *sz) +void +moea64_dumpsys_map(mmu_t mmu, vm_paddr_t pa, size_t sz, void **va) { - if (md->md_vaddr == ~0UL) - return (md->md_paddr + ofs); - else - return (md->md_vaddr + ofs); + + *va = (void *)pa; } -struct pmap_md * -moea64_scan_md(mmu_t mmu, struct pmap_md *prev) +extern struct md_pa dump_map[PHYS_AVAIL_SZ + 1]; +static struct md_pa dump_vmap[4]; + +void +moea64_scan_init(mmu_t mmu) { - static struct pmap_md md; struct pvo_entry *pvo; vm_offset_t va; - - if (dumpsys_minidump) { - md.md_paddr = ~0UL; /* Minidumps use virtual addresses. */ - if (prev == NULL) { - /* 1st: kernel .data and .bss. */ - md.md_index = 1; - md.md_vaddr = trunc_page((uintptr_t)_etext); - md.md_size = round_page((uintptr_t)_end) - md.md_vaddr; - return (&md); + int i; + + /* Initialize phys. segments for dumpsys(). */ + memset(&dump_map, 0, sizeof(dump_map)); + mem_regions(&pregions, &pregions_sz, ®ions, ®ions_sz); + for (i = 0; i < pregions_sz; i++) { + dump_map[i].md_start = pregions[i].mr_start; + dump_map[i].md_size = pregions[i].mr_size; + } + + /* Virtual segments for minidumps: */ + memset(&dump_vmap, 0, sizeof(dump_vmap)); + + /* 1st: kernel .data and .bss. */ + dump_vmap[0].md_start = trunc_page((uintptr_t)_etext); + dump_vmap[0].md_size = round_page((uintptr_t)_end) - dump_vmap[0].md_start; + + /* 2nd: msgbuf and tables (see pmap_bootstrap()). */ + dump_vmap[1].md_start = (vm_paddr_t)msgbufp->msg_ptr; + dump_vmap[1].md_size = round_page(msgbufp->msg_size); + + /* 3rd: kernel VM. */ + va = dump_vmap[1].md_start + dump_vmap[1].md_size; + /* Find start of next chunk (from va). */ + while (va < virtual_end) { + /* Don't dump the buffer cache. */ + if (va >= kmi.buffer_sva && va < kmi.buffer_eva) { + va = kmi.buffer_eva; + continue; } - switch (prev->md_index) { - case 1: - /* 2nd: msgbuf and tables (see pmap_bootstrap()). */ - md.md_index = 2; - md.md_vaddr = (vm_offset_t)msgbufp->msg_ptr; - md.md_size = round_page(msgbufp->msg_size); + pvo = moea64_pvo_find_va(kernel_pmap, va & ~ADDR_POFF); + if (pvo != NULL && (pvo->pvo_pte.lpte.pte_hi & LPTE_VALID)) break; - case 2: - /* 3rd: kernel VM. */ - va = prev->md_vaddr + prev->md_size; - /* Find start of next chunk (from va). */ - while (va < virtual_end) { - /* Don't dump the buffer cache. */ - if (va >= kmi.buffer_sva && - va < kmi.buffer_eva) { - va = kmi.buffer_eva; - continue; - } - pvo = moea64_pvo_find_va(kernel_pmap, - va & ~ADDR_POFF); - if (pvo != NULL && - (pvo->pvo_pte.lpte.pte_hi & LPTE_VALID)) - break; - va += PAGE_SIZE; - } - if (va < virtual_end) { - md.md_vaddr = va; - va += PAGE_SIZE; - /* Find last page in chunk. */ - while (va < virtual_end) { - /* Don't run into the buffer cache. */ - if (va == kmi.buffer_sva) - break; - pvo = moea64_pvo_find_va(kernel_pmap, - va & ~ADDR_POFF); - if (pvo == NULL || - !(pvo->pvo_pte.lpte.pte_hi & LPTE_VALID)) - break; - va += PAGE_SIZE; - } - md.md_size = va - md.md_vaddr; + va += PAGE_SIZE; + } + if (va < virtual_end) { + dump_vmap[2].md_start = va; + va += PAGE_SIZE; + /* Find last page in chunk. */ + while (va < virtual_end) { + /* Don't run into the buffer cache. */ + if (va == kmi.buffer_sva) break; - } - md.md_index = 3; - /* FALLTHROUGH */ - default: - return (NULL); - } - } else { /* minidumps */ - if (prev == NULL) { - /* first physical chunk. */ - md.md_paddr = pregions[0].mr_start; - md.md_size = pregions[0].mr_size; - md.md_vaddr = ~0UL; - md.md_index = 1; - } else if (md.md_index < pregions_sz) { - md.md_paddr = pregions[md.md_index].mr_start; - md.md_size = pregions[md.md_index].mr_size; - md.md_vaddr = ~0UL; - md.md_index++; - } else { - /* There's no next physical chunk. */ - return (NULL); + pvo = moea64_pvo_find_va(kernel_pmap, va & ~ADDR_POFF); + if (pvo == NULL || + !(pvo->pvo_pte.lpte.pte_hi & LPTE_VALID)) + break; + va += PAGE_SIZE; } + dump_vmap[2].md_size = va - dump_vmap[2].md_start; + } +} + +struct md_pa * +moea64_scan_md(mmu_t mmu, struct md_pa *prev) +{ + + if (prev == NULL) { + if (do_minidump) + return (&dump_vmap[0]); + else + return (&dump_map[0]); } - return (&md); + prev++; + /* There's no next chunk. */ + if (prev->md_size == 0) + return (NULL); + return (prev); } diff --git a/sys/powerpc/booke/pmap.c b/sys/powerpc/booke/pmap.c index 942ad12..977d010 100644 --- a/sys/powerpc/booke/pmap.c +++ b/sys/powerpc/booke/pmap.c @@ -50,10 +50,11 @@ #include __FBSDID("$FreeBSD$"); #include +#include #include #include #include #include #include @@ -98,12 +99,10 @@ __FBSDID("$FreeBSD$"); #define debugf(fmt, args...) #endif #define TODO panic("%s: not implemented", __func__); -extern int dumpsys_minidump; - extern unsigned char _etext[]; extern unsigned char _end[]; extern uint32_t *bootinfo; @@ -320,15 +319,16 @@ static void mmu_booke_kenter(mmu_t, vm_offset_t, vm_paddr_t); static void mmu_booke_kenter_attr(mmu_t, vm_offset_t, vm_paddr_t, vm_memattr_t); static void mmu_booke_kremove(mmu_t, vm_offset_t); static boolean_t mmu_booke_dev_direct_mapped(mmu_t, vm_paddr_t, vm_size_t); static void mmu_booke_sync_icache(mmu_t, pmap_t, vm_offset_t, vm_size_t); -static vm_offset_t mmu_booke_dumpsys_map(mmu_t, struct pmap_md *, - vm_size_t, vm_size_t *); -static void mmu_booke_dumpsys_unmap(mmu_t, struct pmap_md *, - vm_size_t, vm_offset_t); -static struct pmap_md *mmu_booke_scan_md(mmu_t, struct pmap_md *); +static void mmu_booke_dumpsys_map(mmu_t, vm_paddr_t pa, size_t, + void **); +static void mmu_booke_dumpsys_unmap(mmu_t, vm_paddr_t pa, size_t, + void *); +static void mmu_booke_scan_init(mmu_t); +static struct md_pa *mmu_booke_scan_md(mmu_t, struct md_pa *); static mmu_method_t mmu_booke_methods[] = { /* pmap dispatcher interface */ MMUMETHOD(mmu_clear_modify, mmu_booke_clear_modify), MMUMETHOD(mmu_copy, mmu_booke_copy), @@ -379,10 +379,11 @@ static mmu_method_t mmu_booke_methods[] = { MMUMETHOD(mmu_unmapdev, mmu_booke_unmapdev), /* dumpsys() support */ MMUMETHOD(mmu_dumpsys_map, mmu_booke_dumpsys_map), MMUMETHOD(mmu_dumpsys_unmap, mmu_booke_dumpsys_unmap), + MMUMETHOD(mmu_scan_init, mmu_booke_scan_init), MMUMETHOD(mmu_scan_md, mmu_booke_scan_md), { 0, 0 } }; @@ -2532,143 +2533,144 @@ mmu_booke_dev_direct_mapped(mmu_t mmu, vm_paddr_t pa, vm_size_t size) } return (EFAULT); } -vm_offset_t -mmu_booke_dumpsys_map(mmu_t mmu, struct pmap_md *md, vm_size_t ofs, - vm_size_t *sz) +void +mmu_booke_dumpsys_map(mmu_t mmu, vm_paddr_t pa, size_t sz, void **va) { - vm_paddr_t pa, ppa; - vm_offset_t va; + vm_paddr_t ppa; + vm_offset_t ofs; vm_size_t gran; - /* Raw physical memory dumps don't have a virtual address. */ - if (md->md_vaddr == ~0UL) { - /* We always map a 256MB page at 256M. */ - gran = 256 * 1024 * 1024; - pa = md->md_paddr + ofs; - ppa = pa & ~(gran - 1); - ofs = pa - ppa; - va = gran; - tlb1_set_entry(va, ppa, gran, _TLB_ENTRY_IO); - if (*sz > (gran - ofs)) - *sz = gran - ofs; - return (va + ofs); - } - /* Minidumps are based on virtual memory addresses. */ - va = md->md_vaddr + ofs; - if (va >= kernstart + kernsize) { - gran = PAGE_SIZE - (va & PAGE_MASK); - if (*sz > gran) - *sz = gran; + if (do_minidump) { + *va = (void *)pa; + return; } - return (va); + + /* Raw physical memory dumps don't have a virtual address. */ + /* We always map a 256MB page at 256M. */ + gran = 256 * 1024 * 1024; + ppa = pa & ~(gran - 1); + ofs = pa - ppa; + *va = (void *)gran; + tlb1_set_entry((vm_offset_t)va, ppa, gran, _TLB_ENTRY_IO); + + if (sz > (gran - ofs)) + tlb1_set_entry((vm_offset_t)(va + gran), ppa + gran, gran, + _TLB_ENTRY_IO); } void -mmu_booke_dumpsys_unmap(mmu_t mmu, struct pmap_md *md, vm_size_t ofs, - vm_offset_t va) +mmu_booke_dumpsys_unmap(mmu_t mmu, vm_paddr_t pa, size_t sz, void *va) { + vm_paddr_t ppa; + vm_offset_t ofs; + vm_size_t gran; + + /* Minidumps are based on virtual memory addresses. */ + /* Nothing to do... */ + if (do_minidump) + return; /* Raw physical memory dumps don't have a virtual address. */ - if (md->md_vaddr == ~0UL) { + tlb1_idx--; + tlb1[tlb1_idx].mas1 = 0; + tlb1[tlb1_idx].mas2 = 0; + tlb1[tlb1_idx].mas3 = 0; + tlb1_write_entry(tlb1_idx); + + gran = 256 * 1024 * 1024; + ppa = pa & ~(gran - 1); + ofs = pa - ppa; + if (sz > (gran - ofs)) { tlb1_idx--; tlb1[tlb1_idx].mas1 = 0; tlb1[tlb1_idx].mas2 = 0; tlb1[tlb1_idx].mas3 = 0; tlb1_write_entry(tlb1_idx); - return; } - - /* Minidumps are based on virtual memory addresses. */ - /* Nothing to do... */ } -struct pmap_md * -mmu_booke_scan_md(mmu_t mmu, struct pmap_md *prev) +extern struct md_pa dump_map[PHYS_AVAIL_SZ + 1]; +static struct md_pa dump_vmap[4]; + +void +mmu_booke_scan_init(mmu_t mmu) { - static struct pmap_md md; - pte_t *pte; vm_offset_t va; - - if (dumpsys_minidump) { - md.md_paddr = ~0UL; /* Minidumps use virtual addresses. */ - if (prev == NULL) { - /* 1st: kernel .data and .bss. */ - md.md_index = 1; - md.md_vaddr = trunc_page((uintptr_t)_etext); - md.md_size = round_page((uintptr_t)_end) - md.md_vaddr; - return (&md); + pte_t *pte; + int i; + + /* Initialize phys. segments for dumpsys(). */ + memset(&dump_map, 0, sizeof(dump_map)); + mem_regions(&physmem_regions, &physmem_regions_sz, &availmem_regions, + &availmem_regions_sz); + for (i = 0; i < physmem_regions_sz; i++) { + dump_map[i].md_start = physmem_regions[i].mr_start; + dump_map[i].md_size = physmem_regions[i].mr_size; + } + + /* Virtual segments for minidumps: */ + memset(&dump_vmap, 0, sizeof(dump_vmap)); + + /* 1st: kernel .data and .bss. */ + dump_vmap[0].md_start = trunc_page((uintptr_t)_etext); + dump_vmap[0].md_size = round_page((uintptr_t)_end) - dump_vmap[0].md_start; + + /* 2nd: msgbuf and tables (see pmap_bootstrap()). */ + dump_vmap[1].md_start = data_start; + dump_vmap[1].md_size = data_end - data_start; + + /* 3rd: kernel VM. */ + va = dump_vmap[1].md_start + dump_vmap[1].md_size; + /* Find start of next chunk (from va). */ + while (va < virtual_end) { + /* Don't dump the buffer cache. */ + if (va >= kmi.buffer_sva && va < kmi.buffer_eva) { + va = kmi.buffer_eva; + continue; } - switch (prev->md_index) { - case 1: - /* 2nd: msgbuf and tables (see pmap_bootstrap()). */ - md.md_index = 2; - md.md_vaddr = data_start; - md.md_size = data_end - data_start; + pte = pte_find(mmu, kernel_pmap, va); + if (pte != NULL && PTE_ISVALID(pte)) break; - case 2: - /* 3rd: kernel VM. */ - va = prev->md_vaddr + prev->md_size; - /* Find start of next chunk (from va). */ - while (va < virtual_end) { - /* Don't dump the buffer cache. */ - if (va >= kmi.buffer_sva && - va < kmi.buffer_eva) { - va = kmi.buffer_eva; - continue; - } - pte = pte_find(mmu, kernel_pmap, va); - if (pte != NULL && PTE_ISVALID(pte)) - break; - va += PAGE_SIZE; - } - if (va < virtual_end) { - md.md_vaddr = va; - va += PAGE_SIZE; - /* Find last page in chunk. */ - while (va < virtual_end) { - /* Don't run into the buffer cache. */ - if (va == kmi.buffer_sva) - break; - pte = pte_find(mmu, kernel_pmap, va); - if (pte == NULL || !PTE_ISVALID(pte)) - break; - va += PAGE_SIZE; - } - md.md_size = va - md.md_vaddr; + va += PAGE_SIZE; + } + if (va < virtual_end) { + dump_vmap[2].md_start = va; + va += PAGE_SIZE; + /* Find last page in chunk. */ + while (va < virtual_end) { + /* Don't run into the buffer cache. */ + if (va == kmi.buffer_sva) break; - } - md.md_index = 3; - /* FALLTHROUGH */ - default: - return (NULL); - } - } else { /* minidumps */ - mem_regions(&physmem_regions, &physmem_regions_sz, - &availmem_regions, &availmem_regions_sz); - - if (prev == NULL) { - /* first physical chunk. */ - md.md_paddr = physmem_regions[0].mr_start; - md.md_size = physmem_regions[0].mr_size; - md.md_vaddr = ~0UL; - md.md_index = 1; - } else if (md.md_index < physmem_regions_sz) { - md.md_paddr = physmem_regions[md.md_index].mr_start; - md.md_size = physmem_regions[md.md_index].mr_size; - md.md_vaddr = ~0UL; - md.md_index++; - } else { - /* There's no next physical chunk. */ - return (NULL); + pte = pte_find(mmu, kernel_pmap, va); + if (pte == NULL || !PTE_ISVALID(pte)) + break; + va += PAGE_SIZE; } + dump_vmap[2].md_size = va - dump_vmap[2].md_start; + } +} + +struct md_pa * +mmu_booke_scan_md(mmu_t mmu, struct md_pa *prev) +{ + + if (prev == NULL) { + if (do_minidump) + return (&dump_vmap[0]); + else + return (&dump_map[0]); } - return (&md); + prev++; + /* There's no next chunk. */ + if (prev->md_size == 0) + return (NULL); + return (prev); } /* * Map a set of physical memory pages into the kernel virtual address space. * Return a pointer to where it is mapped. This routine is intended to be used diff --git a/sys/powerpc/include/md_var.h b/sys/powerpc/include/md_var.h index a90967a..ac189c2 100644 --- a/sys/powerpc/include/md_var.h +++ b/sys/powerpc/include/md_var.h @@ -64,6 +64,16 @@ void decr_tc_init(void); void cpu_setup(u_int); struct trapframe; void powerpc_interrupt(struct trapframe *); +#define KERNELDUMP_VERSION KERNELDUMP_POWERPC_VERSION +#define EM_VALUE ELF_ARCH /* Defined in powerpc/include/elf.h */ + +#define DUMPSYS_HAS_MD_PA_INIT 1 +#define DUMPSYS_HAS_MD_PA_NEXT 1 +#define DUMPSYS_HAS_MD_UNMAP 1 +void dumpsys_unmap_chunk(vm_paddr_t, size_t, void *); + +#define DUMPSYS_MD_PA_NPAIRS (PHYS_AVAIL_SZ + 1) + #endif /* !_MACHINE_MD_VAR_H_ */ diff --git a/sys/powerpc/include/pmap.h b/sys/powerpc/include/pmap.h index 663cd1a..242ab24 100644 --- a/sys/powerpc/include/pmap.h +++ b/sys/powerpc/include/pmap.h @@ -72,17 +72,10 @@ #include #include #include #include -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) #define NPMAPS 32768 #endif /* !defined(NPMAPS) */ @@ -250,14 +243,12 @@ extern vm_offset_t virtual_end; 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 *); +extern void dumpsys_md_pa_init(void); +extern struct md_pa *dumpsys_md_pa_next(struct md_pa *); vm_offset_t pmap_early_io_map(vm_paddr_t pa, vm_size_t size); #endif diff --git a/sys/powerpc/powerpc/dump_machdep.c b/sys/powerpc/powerpc/dump_machdep.c deleted file mode 100644 index 14e2f0f..0000000 --- a/sys/powerpc/powerpc/dump_machdep.c +++ /dev/null @@ -1,315 +0,0 @@ -/*- - * 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 -__FBSDID("$FreeBSD$"); - -#include "opt_watchdog.h" - -#include -#include -#include -#include -#include -#include -#include -#ifdef SW_WATCHDOG -#include -#endif -#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) - -#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, maxsz; - 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; - maxsz = min(DFLTPHYS, di->maxiosize); - - printf(" chunk %d: %lu bytes ", seqnr, (u_long)resid); - - while (resid) { - sz = min(resid, maxsz); - va = pmap_dumpsys_map(md, ofs, &sz); - counter += sz; - if (counter >> 24) { - printf("%c\b", "|/-\\"[twiddle++ & 3]); - counter &= (1<<24) - 1; - } -#ifdef SW_WATCHDOG - wdog_kern_pat(WD_LASTVAL); -#endif - 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; - Elf_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); -} - -int -dumpsys(struct dumperinfo *di) -{ - Elf_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] = ELF_TARG_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 = ELF_ARCH; /* Defined in powerpc/include/elf.h */ - ehdr.e_phoff = sizeof(ehdr); - ehdr.e_ehsize = sizeof(ehdr); - ehdr.e_phentsize = sizeof(Elf_Phdr); - ehdr.e_shentsize = sizeof(Elf_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 = 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 (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); -} diff --git a/sys/powerpc/powerpc/mmu_if.m b/sys/powerpc/powerpc/mmu_if.m index 5c44b71..1be60b9 100644 --- a/sys/powerpc/powerpc/mmu_if.m +++ b/sys/powerpc/powerpc/mmu_if.m @@ -903,48 +903,51 @@ METHOD void sync_icache { /** * @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 _pa The physical page to map. * @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. + * @param _va The virtual address of the mapping. */ -METHOD vm_offset_t dumpsys_map { +METHOD void dumpsys_map { mmu_t _mmu; - struct pmap_md *_md; - vm_size_t _ofs; - vm_size_t *_sz; + vm_paddr_t _pa; + size_t _sz; + void **_va; }; /** * @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 _pa The physical page to map. + * @param _sz The requested size 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; + vm_paddr_t _pa; + size_t _sz; + void *_va; }; /** * @brief Scan/iterate memory chunks. * * @param _prev The previously returned chunk or NULL. * - * @retval The next (or first when _prev is NULL) chunk. + * @retval The next (or first when _prev is NULL) chunk, or NULL (EOF). */ -METHOD struct pmap_md * scan_md { +METHOD struct md_pa * scan_md { mmu_t _mmu; - struct pmap_md *_prev; + struct md_pa *_prev; } DEFAULT mmu_null_scan_md; + + +/** + * @brief Initialize memory chunks for dumpsys. + */ +METHOD void scan_init { + mmu_t _mmu; +}; diff --git a/sys/powerpc/powerpc/pmap_dispatch.c b/sys/powerpc/powerpc/pmap_dispatch.c index 7f3f913..bcc88fb 100644 --- a/sys/powerpc/powerpc/pmap_dispatch.c +++ b/sys/powerpc/powerpc/pmap_dispatch.c @@ -41,18 +41,20 @@ __FBSDID("$FreeBSD$"); * as the PMAP layer makes extensive use of mutexes. */ #include #include +#include #include #include #include #include #include #include +#include #include #include #include "mmu_if.h" @@ -520,28 +522,36 @@ pmap_sync_icache(pmap_t pm, vm_offset_t va, vm_size_t sz) CTR4(KTR_PMAP, "%s(%p, %#x, %#x)", __func__, pm, va, sz); return (MMU_SYNC_ICACHE(mmu_obj, pm, va, sz)); } -vm_offset_t -pmap_dumpsys_map(struct pmap_md *md, vm_size_t ofs, vm_size_t *sz) +void +dumpsys_map_chunk(vm_paddr_t pa, size_t sz, void **va) +{ + + CTR4(KTR_PMAP, "%s(%#jx, %#zx, %p)", __func__, (uintmax_t)pa, sz, va); + return (MMU_DUMPSYS_MAP(mmu_obj, pa, sz, va)); +} + +void +dumpsys_unmap_chunk(vm_paddr_t pa, size_t sz, void *va) { - CTR4(KTR_PMAP, "%s(%p, %#x, %#x)", __func__, md, ofs, *sz); - return (MMU_DUMPSYS_MAP(mmu_obj, md, ofs, sz)); + CTR4(KTR_PMAP, "%s(%#jx, %#zx, %p)", __func__, (uintmax_t)pa, sz, va); + return (MMU_DUMPSYS_UNMAP(mmu_obj, pa, sz, va)); } void -pmap_dumpsys_unmap(struct pmap_md *md, vm_size_t ofs, vm_offset_t va) +dumpsys_md_pa_init(void) { - CTR4(KTR_PMAP, "%s(%p, %#x, %#x)", __func__, md, ofs, va); - return (MMU_DUMPSYS_UNMAP(mmu_obj, md, ofs, va)); + CTR1(KTR_PMAP, "%s()", __func__); + return (MMU_SCAN_INIT(mmu_obj)); } -struct pmap_md * -pmap_scan_md(struct pmap_md *prev) +struct md_pa * +dumpsys_md_pa_next(struct md_pa *prev) { CTR2(KTR_PMAP, "%s(%p)", __func__, prev); return (MMU_SCAN_MD(mmu_obj, prev)); } diff --git a/sys/sparc64/include/md_var.h b/sys/sparc64/include/md_var.h index 6a0a2f6..7234a33 100644 --- a/sys/sparc64/include/md_var.h +++ b/sys/sparc64/include/md_var.h @@ -73,6 +73,11 @@ int scsi_da_bios_params(struct ccb_calc_geometry *ccg); struct disk; void sparc64_ata_disk_firmware_geom_adjust(struct disk *disk); #define ata_disk_firmware_geom_adjust(disk) \ sparc64_ata_disk_firmware_geom_adjust(disk) +#define DUMPSYS_MD_PA_NPAIRS 128 +#define DUMPSYS_HAS_MD_DUMPSYS 1 +#define DUMPSYS_HAS_MD_PA_INIT 1 +void dumpsys_md_pa_init(void); + #endif /* !_MACHINE_MD_VAR_H_ */ diff --git a/sys/sparc64/sparc64/dump_machdep.c b/sys/sparc64/sparc64/dump_machdep.c index 5af21cc..7bb0f9e 100644 --- a/sys/sparc64/sparc64/dump_machdep.c +++ b/sys/sparc64/sparc64/dump_machdep.c @@ -38,115 +38,56 @@ __FBSDID("$FreeBSD$"); #include #include #include #include +#include #include #include #include #include -CTASSERT(sizeof(struct kerneldumpheader) == DEV_BSIZE); +static off_t fileofs; -static struct kerneldumpheader kdh; -static off_t dumplo, dumppos; +extern off_t dumplo; +extern struct md_pa dump_map[DUMPSYS_MD_PA_NPAIRS]; -/* Handle buffered writes. */ -static char buffer[DEV_BSIZE]; -static vm_size_t fragsz; - -#define MAXDUMPSZ (MAXDUMPPGS << PAGE_SHIFT) - -static int -buf_write(struct dumperinfo *di, char *ptr, size_t sz) +void +dumpsys_md_pa_init(void) { - 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; - } - } + int i; - return (0); + memset(dump_map, 0, sizeof(dump_map)); + for (i = 0; i < sparc64_nmemreg; i++) { + dump_map[i].md_start = sparc64_memreg[i].mr_start; + dump_map[i].md_size = sparc64_memreg[i].mr_size; + } } -static int -buf_flush(struct dumperinfo *di) +void +dumpsys_map_chunk(vm_paddr_t pa, size_t chunk __unused, void **va) { - int error; - - if (fragsz == 0) - return (0); - error = dump_write(di, buffer, 0, dumplo, DEV_BSIZE); - dumplo += DEV_BSIZE; - fragsz = 0; - return (error); + *va = (void*)TLB_PHYS_TO_DIRECT(pa); } static int reg_write(struct dumperinfo *di, vm_paddr_t pa, vm_size_t size) { struct sparc64_dump_reg r; r.dr_pa = pa; r.dr_size = size; - r.dr_offs = dumppos; - dumppos += size; - return (buf_write(di, (char *)&r, sizeof(r))); -} - -static int -blk_dump(struct dumperinfo *di, vm_paddr_t pa, vm_size_t size) -{ - vm_size_t pos, rsz; - vm_offset_t va; - int c, counter, error, twiddle; - - printf(" chunk at %#lx: %ld bytes ", (u_long)pa, (long)size); - - va = 0L; - error = counter = twiddle = 0; - for (pos = 0; pos < size; pos += MAXDUMPSZ, counter++) { - if (counter % 128 == 0) - printf("%c\b", "|/-\\"[twiddle++ & 3]); - rsz = size - pos; - rsz = (rsz > MAXDUMPSZ) ? MAXDUMPSZ : rsz; - va = TLB_PHYS_TO_DIRECT(pa + pos); - error = dump_write(di, (void *)va, 0, dumplo, rsz); - if (error) - break; - dumplo += rsz; - - /* 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); + r.dr_offs = fileofs; + fileofs += size; + return (dumpsys_buf_write(di, (char *)&r, sizeof(r))); } int dumpsys(struct dumperinfo *di) { + struct kerneldumpheader kdh; struct sparc64_dump_hdr hdr; vm_size_t size, totsize, hdrsize; int error, i, nreg; /* Calculate dump size. */ @@ -187,30 +128,27 @@ dumpsys(struct dumperinfo *di) hdr.dh_tsb_pa = tsb_kernel_phys; hdr.dh_tsb_size = tsb_kernel_size; hdr.dh_tsb_mask = tsb_kernel_mask; hdr.dh_nregions = nreg; - if (buf_write(di, (char *)&hdr, sizeof(hdr)) != 0) + if (dumpsys_buf_write(di, (char *)&hdr, sizeof(hdr)) != 0) goto fail; - dumppos = hdrsize; + fileofs = hdrsize; /* Now, write out the region descriptors. */ for (i = 0; i < sparc64_nmemreg; i++) { error = reg_write(di, sparc64_memreg[i].mr_start, sparc64_memreg[i].mr_size); if (error != 0) goto fail; } - buf_flush(di); + dumpsys_buf_flush(di); /* Dump memory chunks. */ - for (i = 0; i < sparc64_nmemreg; i++) { - error = blk_dump(di, sparc64_memreg[i].mr_start, - sparc64_memreg[i].mr_size); - if (error != 0) - goto fail; - } + error = dumpsys_foreach_chunk(dumpsys_cb_dumpdata, di); + if (error < 0) + goto fail; /* Dump trailer */ error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); if (error) goto fail; @@ -219,9 +157,12 @@ dumpsys(struct dumperinfo *di) dump_write(di, NULL, 0, 0, 0); printf("\nDump complete\n"); return (0); fail: + if (error < 0) + error = -error; + /* XXX It should look more like VMS :-) */ printf("** DUMP FAILED (ERROR %d) **\n", error); return (error); } diff --git a/sys/sys/conf.h b/sys/sys/conf.h index f8a9a3c..63a2a05 100644 --- a/sys/sys/conf.h +++ b/sys/sys/conf.h @@ -332,14 +332,26 @@ struct dumperinfo { u_int maxiosize; /* Max size allowed for an individual I/O */ off_t mediaoffset; /* Initial offset in bytes. */ off_t mediasize; /* Space available in bytes. */ }; +struct md_pa { + vm_paddr_t md_start; + vm_paddr_t md_size; +}; + int set_dumper(struct dumperinfo *, const char *_devname); int dump_write(struct dumperinfo *, void *, vm_offset_t, off_t, size_t); int dumpsys(struct dumperinfo *); int doadump(boolean_t); +void dumpsys_map_chunk(vm_paddr_t, size_t, void **); +typedef int dumpsys_callback_t(struct md_pa *, int, void *); +int dumpsys_foreach_chunk(dumpsys_callback_t, void *); +int dumpsys_cb_dumpdata(struct md_pa *, int, void *); +int dumpsys_buf_write(struct dumperinfo *, char *, size_t); +int dumpsys_buf_flush(struct dumperinfo *); extern int dumping; /* system is dumping */ +extern int do_minidump; #endif /* _KERNEL */ #endif /* !_SYS_CONF_H_ */ diff --git a/sys/x86/x86/dump_machdep.c b/sys/x86/x86/dump_machdep.c index 940c519..601557f 100644 --- a/sys/x86/x86/dump_machdep.c +++ b/sys/x86/x86/dump_machdep.c @@ -28,352 +28,21 @@ __FBSDID("$FreeBSD$"); #include "opt_watchdog.h" #include -#include #include -#include -#include -#include -#include -#include +#include #include #include -#include -#include - -#ifdef __amd64__ -#define KERNELDUMP_VERSION KERNELDUMP_AMD64_VERSION -#define EM_VALUE EM_X86_64 -#else -#define KERNELDUMP_VERSION KERNELDUMP_I386_VERSION -#define EM_VALUE EM_386 -#endif - -CTASSERT(sizeof(struct kerneldumpheader) == 512); - -int do_minidump = 1; -SYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RWTUN, &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)) - -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; - -/* 20 phys_avail entry pairs correspond to 10 md_pa's */ -static struct md_pa dump_map[10]; - -static void -md_pa_init(void) +void +dumpsys_map_chunk(vm_paddr_t pa, size_t chunk, void **va) { - int n, idx; + int i; + vm_paddr_t a; - 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]; + for (i = 0; i < chunk; i++) { + a = pa + i * PAGE_SIZE; + *va = pmap_kenter_temporary(trunc_page(a), i); } } - -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); -} - -#define PG2MB(pgs) ((pgs + (1 << 8) - 1) >> 8) - -static int -cb_dumpdata(struct md_pa *mdp, int seqnr, void *arg) -{ - struct dumperinfo *di = (struct dumperinfo*)arg; - vm_paddr_t a, pa; - void *va; - uint64_t pgs; - size_t counter, sz, chunk; - int i, c, error, twiddle; - u_int maxdumppgs; - - error = 0; /* catch case in which chunk size is 0 */ - counter = 0; /* Update twiddle every 16MB */ - twiddle = 0; - va = 0; - pgs = mdp->md_size / PAGE_SIZE; - pa = mdp->md_start; - maxdumppgs = min(di->maxiosize / PAGE_SIZE, MAXDUMPPGS); - if (maxdumppgs == 0) /* seatbelt */ - maxdumppgs = 1; - - printf(" chunk %d: %juMB (%ju pages)", seqnr, (uintmax_t)PG2MB(pgs), - (uintmax_t)pgs); - - while (pgs) { - chunk = pgs; - if (chunk > maxdumppgs) - chunk = maxdumppgs; - sz = chunk << PAGE_SHIFT; - counter += sz; - if (counter >> 24) { - printf(" %ju", (uintmax_t)PG2MB(pgs)); - counter &= (1<<24) - 1; - } - for (i = 0; i < chunk; i++) { - a = pa + i * PAGE_SIZE; - va = pmap_kenter_temporary(trunc_page(a), i); - } - - wdog_kern_pat(WD_LASTVAL); - - error = dump_write(di, va, 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) -{ - uint64_t *sz = (uint64_t*)arg; - - *sz += (uint64_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); -} - -int -dumpsys(struct dumperinfo *di) -{ - Elf_Ehdr ehdr; - uint64_t dumpsize; - off_t hdrgap; - size_t hdrsz; - int error; - - if (do_minidump) { - minidumpsys(di); - return (0); - } - 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_VALUE; - 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_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 (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); -} -- 1.9.3