Date: Fri, 24 Jun 2005 15:52:04 GMT From: Paul Saab <ps@FreeBSD.org> To: Perforce Change Reviews <perforce@freebsd.org> Subject: PERFORCE change 78910 for review Message-ID: <200506241552.j5OFq4Wu055845@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=78910 Change 78910 by ps@butter.corp on 2005/06/24 15:51:48 Follow amd64's lead and convert over to ELF crashdumps. Obtained from: peter via amd64 Affected files ... .. //depot/projects/hammer/sys/i386/i386/dump_machdep.c#6 edit .. //depot/projects/hammer/sys/i386/i386/machdep.c#52 edit .. //depot/projects/hammer/sys/i386/include/pmap.h#21 edit Differences ... ==== //depot/projects/hammer/sys/i386/i386/dump_machdep.c#6 (text+ko) ==== @@ -1,40 +1,31 @@ /*- - * Copyright (c) 2002 Poul-Henning Kamp - * Copyright (c) 2002 Networks Associates Technology, Inc. + * Copyright (c) 2002 Marcel Moolenaar * All rights reserved. * - * This software was developed for the FreeBSD Project by Poul-Henning Kamp - * and NAI Labs, the Security Research Division of Network Associates, Inc. - * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the - * DARPA CHATS research program. - * * 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. - * 3. The names of the authors may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * 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: src/sys/i386/i386/dump_machdep.c,v 1.9 2003/08/25 09:48:46 obrien Exp $"); +__FBSDID("$FreeBSD: src/sys/ia64/ia64/dump_machdep.c,v 1.12 2005/01/06 22:18:22 imp Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -44,92 +35,353 @@ #include <sys/kerneldump.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)) + +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; -void -dumpsys(struct dumperinfo *di) +/* 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_avail[10]; + +static void +md_pa_init(void) +{ + int n, idx; + + bzero(&dump_avail, sizeof(dump_avail)); + for (n = 0; n < sizeof(dump_avail) / sizeof(dump_avail[0]); n++) { + idx = n * 2; + if (phys_avail[idx] == 0 && phys_avail[idx + 1] == 0) + break; + dump_avail[n].md_start = phys_avail[idx]; + dump_avail[n].md_size = phys_avail[idx + 1] - phys_avail[idx]; + if (dump_avail[n].md_start == kernphys[1]) { + /* Include the kernel, that would be helpful! */ + dump_avail[n].md_start = kernphys[0]; + dump_avail[n].md_size += kernphys[1] - kernphys[0]; + } + if (dump_avail[n].md_start == PAGE_SIZE) { + /* Include page zero */ + dump_avail[n].md_start = 0; + dump_avail[n].md_size += PAGE_SIZE; + } + if (dump_avail[n].md_start == 0 && dump_avail[n].md_size >= 0x98000) { + /* Include mpboot code and/or bios EBDA */ + dump_avail[n].md_size = 0xA0000; + } + if (phys_avail[idx + 1] == avail_end) { + /* Include msgbuf, that would be helpful! */ + dump_avail[n].md_size += (Maxmem << PAGE_SHIFT) - avail_end; + } + } +} + +static struct md_pa * +md_pa_first(void) +{ + + return (&dump_avail[0]); +} + +static struct md_pa * +md_pa_next(struct md_pa *mdp) { - off_t dumplo; - vm_offset_t a, addr; - u_int count, left, u; - void *va; - int i, mb; - int c; + + mdp++; + if (mdp->md_size == 0) + mdp = NULL; + return (mdp); +} - printf("Dumping %ld MB\n", Maxmem / (1024*1024 / PAGE_SIZE)); +/* XXX should be MI */ +static void +mkdumpheader(struct kerneldumpheader *kdh, uint32_t archver, uint64_t dumplen, + uint32_t blksz) +{ - /* Fill in the kernel dump header */ - strcpy(kdh.magic, KERNELDUMPMAGIC); - strcpy(kdh.architecture, "i386"); - kdh.version = htod32(KERNELDUMPVERSION); - kdh.architectureversion = htod32(KERNELDUMP_I386_VERSION); - kdh.dumplength = htod64(Maxmem * (off_t)PAGE_SIZE); - kdh.dumptime = htod64(time_second); - kdh.blocksize = htod32(di->blocksize); - strncpy(kdh.hostname, hostname, sizeof kdh.hostname); - strncpy(kdh.versionstring, version, sizeof kdh.versionstring); + bzero(kdh, sizeof(*kdh)); + strncpy(kdh->magic, KERNELDUMPMAGIC, sizeof(kdh->magic)); + strncpy(kdh->architecture, MACHINE_ARCH, sizeof(kdh->architecture)); + kdh->version = htod32(KERNELDUMPVERSION); + kdh->architectureversion = htod32(archver); + kdh->dumplength = htod64(dumplen); + kdh->dumptime = htod64(time_second); + kdh->blocksize = htod32(blksz); + strncpy(kdh->hostname, hostname, sizeof(kdh->hostname)); + strncpy(kdh->versionstring, version, sizeof(kdh->versionstring)); if (panicstr != NULL) - strncpy(kdh.panicstring, panicstr, sizeof kdh.panicstring); - kdh.parity = kerneldump_parity(&kdh); + strncpy(kdh->panicstring, panicstr, sizeof(kdh->panicstring)); + kdh->parity = kerneldump_parity(kdh); +} + +static int +buf_write(struct dumperinfo *di, char *ptr, size_t sz) +{ + size_t len; + int error; - /* - * Check if we will have enough room to save the coredump. - * The partition size needed is the sum of: - * Memory to save + header + trailer + Room to leave untouched - * at partition head. (an arbitrary amount). - */ - if (di->mediasize < - Maxmem * (off_t)PAGE_SIZE + sizeof kdh * 2 + 64*1024) { - printf("\nDump failed. Partition too small.\n"); - return; + 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; + } } - dumplo = di->mediaoffset + di->mediasize - Maxmem * (off_t)PAGE_SIZE; - dumplo -= sizeof kdh * 2; - i = di->dumper(di->priv, &kdh, 0, dumplo, sizeof kdh); - if (i) - printf("\nDump failed writing header (%d)\n", i); - dumplo += sizeof kdh; - i = 0; - addr = 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; + 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; + + error = 0; /* catch case in which chunk size is 0 */ + counter = 0; /* Update twiddle every 16MB */ + twiddle = 0; va = 0; - mb = 0; - for (count = 0; count < Maxmem;) { - left = Maxmem - count; - if (left > MAXDUMPPGS) - left = MAXDUMPPGS; - for (u = 0; u < left; u++) { - a = addr + u * PAGE_SIZE; - if (!is_physical_memory(a)) - a = 0; - va = pmap_kenter_temporary(trunc_page(a), u); + pgs = mdp->md_size / PAGE_SIZE; + pa = mdp->md_start; + + printf(" chunk %d: %lldMB (%lld pages)", seqnr, PG2MB(pgs), pgs); + + while (pgs) { + chunk = pgs; + if (chunk > MAXDUMPPGS) + chunk = MAXDUMPPGS; + sz = chunk << PAGE_SHIFT; + counter += sz; + if (counter >> 24) { + printf(" %lld", PG2MB(pgs)); + counter &= (1<<24) - 1; } - i = count / (16*1024*1024 / PAGE_SIZE); - if (i != mb) { - printf(" %d", count / (1024 * 1024 / PAGE_SIZE)); - mb = i; + for (i = 0; i < chunk; i++) { + a = pa + i * PAGE_SIZE; + va = pmap_kenter_temporary(trunc_page(a), i); } - i = di->dumper(di->priv, va, 0, dumplo, left * PAGE_SIZE); - if (i) + error = di->dumper(di->priv, va, 0, dumplo, sz); + if (error) break; - count += left; - dumplo += left * PAGE_SIZE; - addr += left * PAGE_SIZE; - if ((c = cncheckc()) == 0x03) { - printf("\nDump aborted.\n"); - return; - } else if (c != -1) - printf("[CTRL-C to abort] "); + 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); +} + +void +dumpsys(struct dumperinfo *di) +{ + Elf_Ehdr ehdr; + uint64_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_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_386; + 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; } - if (i) - printf("\nDump failed writing data (%d)\n", i); - i = di->dumper(di->priv, &kdh, 0, dumplo, sizeof kdh); - if (i) - printf("\nDump failed writing trailer (%d)\n", i); - di->dumper(di->priv, NULL, 0, 0, 0); /* tell them we are done */ + dumplo = di->mediaoffset + di->mediasize - dumpsize; + dumplo -= sizeof(kdh) * 2; + + mkdumpheader(&kdh, KERNELDUMP_I386_VERSION, dumpsize, di->blocksize); + + printf("Dumping %llu MB (%d chunks)\n", (long long)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 if (error == ENOSPC) + printf("\nDump failed. Partition too small.\n"); + else + printf("\n** DUMP FAILED (ERROR %d) **\n", error); } ==== //depot/projects/hammer/sys/i386/i386/machdep.c#52 (text+ko) ==== @@ -185,6 +185,7 @@ long realmem = 0; vm_paddr_t phys_avail[10]; +vm_paddr_t kernphys[2]; /* must be 2 less so 0 0 can signal end of chunks */ #define PHYS_AVAIL_ARRAY_END ((sizeof(phys_avail) / sizeof(vm_offset_t)) - 2) @@ -1879,6 +1880,12 @@ getenv_quad("dcons.size", &dcons_size) == 0) dcons_addr = 0; + /* + * Keep track of where the kernel lives for crash dumps. + */ + kernphys[0] = KERNLOAD; + kernphys[1] = first; + /* * physmap is in bytes, so when converting to page boundaries, * round up the start address and round down the end address. @@ -1896,7 +1903,7 @@ /* * block out kernel memory as not available. */ - if (pa >= KERNLOAD && pa < first) + if (pa >= kernphys[0] && pa < kernphys[1]) continue; /* ==== //depot/projects/hammer/sys/i386/include/pmap.h#21 (text+ko) ==== @@ -346,6 +346,7 @@ extern pt_entry_t *CMAP1; extern vm_paddr_t avail_end; extern vm_paddr_t phys_avail[]; +extern vm_paddr_t kernphys[]; extern int pseflag; extern int pgeflag; extern char *ptvmmap; /* poor name! */
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200506241552.j5OFq4Wu055845>