1190684Smarcel/*- 2190684Smarcel * Copyright (c) 2002 Marcel Moolenaar 3190684Smarcel * All rights reserved. 4190684Smarcel * 5190684Smarcel * Redistribution and use in source and binary forms, with or without 6190684Smarcel * modification, are permitted provided that the following conditions 7190684Smarcel * are met: 8190684Smarcel * 9190684Smarcel * 1. Redistributions of source code must retain the above copyright 10190684Smarcel * notice, this list of conditions and the following disclaimer. 11190684Smarcel * 2. Redistributions in binary form must reproduce the above copyright 12190684Smarcel * notice, this list of conditions and the following disclaimer in the 13190684Smarcel * documentation and/or other materials provided with the distribution. 14190684Smarcel * 15190684Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16190684Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17190684Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18190684Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19190684Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20190684Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21190684Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22190684Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23190684Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24190684Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25190684Smarcel */ 26190684Smarcel 27190684Smarcel#include <sys/cdefs.h> 28190684Smarcel__FBSDID("$FreeBSD$"); 29190684Smarcel 30221173Sattilio#include "opt_watchdog.h" 31221173Sattilio 32190684Smarcel#include <sys/param.h> 33190684Smarcel#include <sys/systm.h> 34190684Smarcel#include <sys/conf.h> 35190684Smarcel#include <sys/cons.h> 36190684Smarcel#include <sys/kernel.h> 37190684Smarcel#include <sys/kerneldump.h> 38190684Smarcel#include <sys/sysctl.h> 39221173Sattilio#ifdef SW_WATCHDOG 40221173Sattilio#include <sys/watchdog.h> 41221173Sattilio#endif 42190684Smarcel#include <vm/vm.h> 43190684Smarcel#include <vm/pmap.h> 44190684Smarcel#include <machine/elf.h> 45190684Smarcel#include <machine/md_var.h> 46190684Smarcel 47190684SmarcelCTASSERT(sizeof(struct kerneldumpheader) == 512); 48190684Smarcel 49190684Smarcel/* 50190684Smarcel * Don't touch the first SIZEOF_METADATA bytes on the dump device. This 51190684Smarcel * is to protect us from metadata and to protect metadata from us. 52190684Smarcel */ 53190684Smarcel#define SIZEOF_METADATA (64*1024) 54190684Smarcel 55190684Smarcel#define MD_ALIGN(x) (((off_t)(x) + PAGE_MASK) & ~PAGE_MASK) 56190684Smarcel#define DEV_ALIGN(x) (((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1)) 57190684Smarcel 58190684Smarceltypedef int callback_t(struct pmap_md *, int, void *); 59190684Smarcel 60190684Smarcelstatic struct kerneldumpheader kdh; 61190684Smarcelstatic off_t dumplo, fileofs; 62190684Smarcel 63190684Smarcel/* Handle buffered writes. */ 64190684Smarcelstatic char buffer[DEV_BSIZE]; 65190684Smarcelstatic size_t fragsz; 66190684Smarcel 67190684Smarcelint dumpsys_minidump = 1; 68190684SmarcelSYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RD, &dumpsys_minidump, 0, 69190684Smarcel "Kernel makes compressed crash dumps"); 70190684Smarcel 71190684Smarcelstatic int 72190684Smarcelbuf_write(struct dumperinfo *di, char *ptr, size_t sz) 73190684Smarcel{ 74190684Smarcel size_t len; 75190684Smarcel int error; 76190684Smarcel 77190684Smarcel while (sz) { 78190684Smarcel len = DEV_BSIZE - fragsz; 79190684Smarcel if (len > sz) 80190684Smarcel len = sz; 81190684Smarcel bcopy(ptr, buffer + fragsz, len); 82190684Smarcel fragsz += len; 83190684Smarcel ptr += len; 84190684Smarcel sz -= len; 85190684Smarcel if (fragsz == DEV_BSIZE) { 86190684Smarcel error = di->dumper(di->priv, buffer, 0, dumplo, 87190684Smarcel DEV_BSIZE); 88190684Smarcel if (error) 89190684Smarcel return error; 90190684Smarcel dumplo += DEV_BSIZE; 91190684Smarcel fragsz = 0; 92190684Smarcel } 93190684Smarcel } 94190684Smarcel 95190684Smarcel return (0); 96190684Smarcel} 97190684Smarcel 98190684Smarcelstatic int 99190684Smarcelbuf_flush(struct dumperinfo *di) 100190684Smarcel{ 101190684Smarcel int error; 102190684Smarcel 103190684Smarcel if (fragsz == 0) 104190684Smarcel return (0); 105190684Smarcel 106190684Smarcel error = di->dumper(di->priv, buffer, 0, dumplo, DEV_BSIZE); 107190684Smarcel dumplo += DEV_BSIZE; 108190684Smarcel fragsz = 0; 109190684Smarcel return (error); 110190684Smarcel} 111190684Smarcel 112190684Smarcelstatic int 113190684Smarcelcb_dumpdata(struct pmap_md *md, int seqnr, void *arg) 114190684Smarcel{ 115190684Smarcel struct dumperinfo *di = (struct dumperinfo*)arg; 116190684Smarcel vm_offset_t va; 117190684Smarcel size_t counter, ofs, resid, sz; 118190684Smarcel int c, error, twiddle; 119190684Smarcel 120190684Smarcel error = 0; 121190684Smarcel counter = 0; /* Update twiddle every 16MB */ 122190684Smarcel twiddle = 0; 123190684Smarcel 124190684Smarcel ofs = 0; /* Logical offset within the chunk */ 125190684Smarcel resid = md->md_size; 126190684Smarcel 127190684Smarcel printf(" chunk %d: %lu bytes ", seqnr, (u_long)resid); 128190684Smarcel 129190684Smarcel while (resid) { 130190684Smarcel sz = (resid > DFLTPHYS) ? DFLTPHYS : resid; 131190684Smarcel va = pmap_dumpsys_map(md, ofs, &sz); 132190684Smarcel counter += sz; 133190684Smarcel if (counter >> 24) { 134190684Smarcel printf("%c\b", "|/-\\"[twiddle++ & 3]); 135190684Smarcel counter &= (1<<24) - 1; 136190684Smarcel } 137221173Sattilio#ifdef SW_WATCHDOG 138221173Sattilio wdog_kern_pat(WD_LASTVAL); 139221173Sattilio#endif 140190684Smarcel error = di->dumper(di->priv, (void*)va, 0, dumplo, sz); 141190684Smarcel pmap_dumpsys_unmap(md, ofs, va); 142190684Smarcel if (error) 143190684Smarcel break; 144190684Smarcel dumplo += sz; 145190684Smarcel resid -= sz; 146190684Smarcel ofs += sz; 147190684Smarcel 148190684Smarcel /* Check for user abort. */ 149190684Smarcel c = cncheckc(); 150190684Smarcel if (c == 0x03) 151190684Smarcel return (ECANCELED); 152190684Smarcel if (c != -1) 153190684Smarcel printf("(CTRL-C to abort) "); 154190684Smarcel } 155190684Smarcel printf("... %s\n", (error) ? "fail" : "ok"); 156190684Smarcel return (error); 157190684Smarcel} 158190684Smarcel 159190684Smarcelstatic int 160190684Smarcelcb_dumphdr(struct pmap_md *md, int seqnr, void *arg) 161190684Smarcel{ 162190684Smarcel struct dumperinfo *di = (struct dumperinfo*)arg; 163190684Smarcel Elf32_Phdr phdr; 164190684Smarcel int error; 165190684Smarcel 166190684Smarcel bzero(&phdr, sizeof(phdr)); 167190684Smarcel phdr.p_type = PT_LOAD; 168190684Smarcel phdr.p_flags = PF_R; /* XXX */ 169190684Smarcel phdr.p_offset = fileofs; 170190684Smarcel phdr.p_vaddr = md->md_vaddr; 171190684Smarcel phdr.p_paddr = md->md_paddr; 172190684Smarcel phdr.p_filesz = md->md_size; 173190684Smarcel phdr.p_memsz = md->md_size; 174190684Smarcel phdr.p_align = PAGE_SIZE; 175190684Smarcel 176190684Smarcel error = buf_write(di, (char*)&phdr, sizeof(phdr)); 177190684Smarcel fileofs += phdr.p_filesz; 178190684Smarcel return (error); 179190684Smarcel} 180190684Smarcel 181190684Smarcelstatic int 182190684Smarcelcb_size(struct pmap_md *md, int seqnr, void *arg) 183190684Smarcel{ 184190684Smarcel uint32_t *sz = (uint32_t*)arg; 185190684Smarcel 186190684Smarcel *sz += md->md_size; 187190684Smarcel return (0); 188190684Smarcel} 189190684Smarcel 190190684Smarcelstatic int 191190684Smarcelforeach_chunk(callback_t cb, void *arg) 192190684Smarcel{ 193190684Smarcel struct pmap_md *md; 194190684Smarcel int error, seqnr; 195190684Smarcel 196190684Smarcel seqnr = 0; 197190684Smarcel md = pmap_scan_md(NULL); 198190684Smarcel while (md != NULL) { 199190684Smarcel error = (*cb)(md, seqnr++, arg); 200190684Smarcel if (error) 201190684Smarcel return (-error); 202190684Smarcel md = pmap_scan_md(md); 203190684Smarcel } 204190684Smarcel return (seqnr); 205190684Smarcel} 206190684Smarcel 207190684Smarcelvoid 208190684Smarceldumpsys(struct dumperinfo *di) 209190684Smarcel{ 210190684Smarcel Elf32_Ehdr ehdr; 211190684Smarcel uint32_t dumpsize; 212190684Smarcel off_t hdrgap; 213190684Smarcel size_t hdrsz; 214190684Smarcel int error; 215190684Smarcel 216190684Smarcel bzero(&ehdr, sizeof(ehdr)); 217190684Smarcel ehdr.e_ident[EI_MAG0] = ELFMAG0; 218190684Smarcel ehdr.e_ident[EI_MAG1] = ELFMAG1; 219190684Smarcel ehdr.e_ident[EI_MAG2] = ELFMAG2; 220190684Smarcel ehdr.e_ident[EI_MAG3] = ELFMAG3; 221190684Smarcel ehdr.e_ident[EI_CLASS] = ELFCLASS32; 222190684Smarcel#if BYTE_ORDER == LITTLE_ENDIAN 223190684Smarcel ehdr.e_ident[EI_DATA] = ELFDATA2LSB; 224190684Smarcel#else 225190684Smarcel ehdr.e_ident[EI_DATA] = ELFDATA2MSB; 226190684Smarcel#endif 227190684Smarcel ehdr.e_ident[EI_VERSION] = EV_CURRENT; 228190684Smarcel ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE; /* XXX big picture? */ 229190684Smarcel ehdr.e_type = ET_CORE; 230190684Smarcel ehdr.e_machine = EM_PPC; 231190684Smarcel ehdr.e_phoff = sizeof(ehdr); 232190684Smarcel ehdr.e_ehsize = sizeof(ehdr); 233190684Smarcel ehdr.e_phentsize = sizeof(Elf32_Phdr); 234190684Smarcel ehdr.e_shentsize = sizeof(Elf32_Shdr); 235190684Smarcel 236190684Smarcel /* Calculate dump size. */ 237190684Smarcel dumpsize = 0L; 238190684Smarcel ehdr.e_phnum = foreach_chunk(cb_size, &dumpsize); 239190684Smarcel hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize; 240190684Smarcel fileofs = MD_ALIGN(hdrsz); 241190684Smarcel dumpsize += fileofs; 242190684Smarcel hdrgap = fileofs - DEV_ALIGN(hdrsz); 243190684Smarcel 244190684Smarcel /* For block devices, determine the dump offset on the device. */ 245190684Smarcel if (di->mediasize > 0) { 246190684Smarcel if (di->mediasize < 247190684Smarcel SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { 248190684Smarcel error = ENOSPC; 249190684Smarcel goto fail; 250190684Smarcel } 251190684Smarcel dumplo = di->mediaoffset + di->mediasize - dumpsize; 252190684Smarcel dumplo -= sizeof(kdh) * 2; 253190684Smarcel } else 254190684Smarcel dumplo = 0; 255190684Smarcel 256190684Smarcel mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_POWERPC_VERSION, dumpsize, 257190684Smarcel di->blocksize); 258190684Smarcel 259190684Smarcel printf("Dumping %u MB (%d chunks)\n", dumpsize >> 20, 260190684Smarcel ehdr.e_phnum); 261190684Smarcel 262190684Smarcel /* Dump leader */ 263190684Smarcel error = di->dumper(di->priv, &kdh, 0, dumplo, sizeof(kdh)); 264190684Smarcel if (error) 265190684Smarcel goto fail; 266190684Smarcel dumplo += sizeof(kdh); 267190684Smarcel 268190684Smarcel /* Dump ELF header */ 269190684Smarcel error = buf_write(di, (char*)&ehdr, sizeof(ehdr)); 270190684Smarcel if (error) 271190684Smarcel goto fail; 272190684Smarcel 273190684Smarcel /* Dump program headers */ 274190684Smarcel error = foreach_chunk(cb_dumphdr, di); 275190684Smarcel if (error < 0) 276190684Smarcel goto fail; 277190684Smarcel buf_flush(di); 278190684Smarcel 279190684Smarcel /* 280190684Smarcel * All headers are written using blocked I/O, so we know the 281190684Smarcel * current offset is (still) block aligned. Skip the alignement 282190684Smarcel * in the file to have the segment contents aligned at page 283190684Smarcel * boundary. We cannot use MD_ALIGN on dumplo, because we don't 284190684Smarcel * care and may very well be unaligned within the dump device. 285190684Smarcel */ 286190684Smarcel dumplo += hdrgap; 287190684Smarcel 288190684Smarcel /* Dump memory chunks (updates dumplo) */ 289190684Smarcel error = foreach_chunk(cb_dumpdata, di); 290190684Smarcel if (error < 0) 291190684Smarcel goto fail; 292190684Smarcel 293190684Smarcel /* Dump trailer */ 294190684Smarcel error = di->dumper(di->priv, &kdh, 0, dumplo, sizeof(kdh)); 295190684Smarcel if (error) 296190684Smarcel goto fail; 297190684Smarcel 298190684Smarcel /* Signal completion, signoff and exit stage left. */ 299190684Smarcel di->dumper(di->priv, NULL, 0, 0, 0); 300190684Smarcel printf("\nDump complete\n"); 301190684Smarcel return; 302190684Smarcel 303190684Smarcel fail: 304190684Smarcel if (error < 0) 305190684Smarcel error = -error; 306190684Smarcel 307190684Smarcel if (error == ECANCELED) 308190684Smarcel printf("\nDump aborted\n"); 309190684Smarcel else 310190684Smarcel printf("\n** DUMP FAILED (ERROR %d) **\n", error); 311190684Smarcel} 312