1178172Simp/*- 2214903Sgonzo * Copyright (c) 2002 Marcel Moolenaar 3178172Simp * All rights reserved. 4178172Simp * 5178172Simp * Redistribution and use in source and binary forms, with or without 6178172Simp * modification, are permitted provided that the following conditions 7178172Simp * are met: 8214903Sgonzo * 9178172Simp * 1. Redistributions of source code must retain the above copyright 10214903Sgonzo * notice, this list of conditions and the following disclaimer. 11214903Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 12214903Sgonzo * notice, this list of conditions and the following disclaimer in the 13214903Sgonzo * documentation and/or other materials provided with the distribution. 14178172Simp * 15214903Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16214903Sgonzo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17214903Sgonzo * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18214903Sgonzo * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19214903Sgonzo * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20214903Sgonzo * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21214903Sgonzo * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22214903Sgonzo * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23214903Sgonzo * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24214903Sgonzo * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25178172Simp */ 26178172Simp 27178172Simp#include <sys/cdefs.h> 28178172Simp__FBSDID("$FreeBSD$"); 29178172Simp 30221173Sattilio#include "opt_watchdog.h" 31221173Sattilio 32214903Sgonzo#include <sys/param.h> 33214903Sgonzo#include <sys/systm.h> 34214903Sgonzo#include <sys/conf.h> 35214903Sgonzo#include <sys/cons.h> 36214903Sgonzo#include <sys/sysctl.h> 37214903Sgonzo#include <sys/kernel.h> 38214903Sgonzo#include <sys/proc.h> 39214903Sgonzo#include <sys/kerneldump.h> 40221173Sattilio#ifdef SW_WATCHDOG 41221173Sattilio#include <sys/watchdog.h> 42221173Sattilio#endif 43214903Sgonzo#include <vm/vm.h> 44214903Sgonzo#include <vm/pmap.h> 45214903Sgonzo#include <machine/elf.h> 46214903Sgonzo#include <machine/md_var.h> 47214903Sgonzo#include <machine/pcb.h> 48214903Sgonzo#include <machine/cache.h> 49214903Sgonzo 50214903SgonzoCTASSERT(sizeof(struct kerneldumpheader) == 512); 51214903Sgonzo 52214903Sgonzoint do_minidump = 1; 53214903SgonzoTUNABLE_INT("debug.minidump", &do_minidump); 54214903SgonzoSYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RW, &do_minidump, 0, 55214903Sgonzo "Enable mini crash dumps"); 56214903Sgonzo 57214903Sgonzo/* 58214903Sgonzo * Don't touch the first SIZEOF_METADATA bytes on the dump device. This 59214903Sgonzo * is to protect us from metadata and to protect metadata from us. 60178172Simp */ 61214903Sgonzo#define SIZEOF_METADATA (64*1024) 62214903Sgonzo 63214903Sgonzo#define MD_ALIGN(x) (((off_t)(x) + PAGE_MASK) & ~PAGE_MASK) 64214903Sgonzo#define DEV_ALIGN(x) (((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1)) 65214903Sgonzoextern struct pcb dumppcb; 66214903Sgonzo 67214903Sgonzostruct md_pa { 68214903Sgonzo vm_paddr_t md_start; 69214903Sgonzo vm_paddr_t md_size; 70214903Sgonzo}; 71214903Sgonzo 72214903Sgonzotypedef int callback_t(struct md_pa *, int, void *); 73214903Sgonzo 74214903Sgonzostatic struct kerneldumpheader kdh; 75214903Sgonzostatic off_t dumplo, fileofs; 76214903Sgonzo 77214903Sgonzo/* Handle buffered writes. */ 78214903Sgonzostatic char buffer[DEV_BSIZE]; 79214903Sgonzostatic size_t fragsz; 80214903Sgonzo 81214903Sgonzo/* XXX: I suppose 20 should be enough. */ 82214903Sgonzostatic struct md_pa dump_map[20]; 83214903Sgonzo 84214903Sgonzostatic void 85214903Sgonzomd_pa_init(void) 86214903Sgonzo{ 87214903Sgonzo int n, idx; 88214903Sgonzo 89214903Sgonzo bzero(dump_map, sizeof(dump_map)); 90214903Sgonzo for (n = 0; n < sizeof(dump_map) / sizeof(dump_map[0]); n++) { 91214903Sgonzo idx = n * 2; 92214903Sgonzo if (dump_avail[idx] == 0 && dump_avail[idx + 1] == 0) 93214903Sgonzo break; 94214903Sgonzo dump_map[n].md_start = dump_avail[idx]; 95214903Sgonzo dump_map[n].md_size = dump_avail[idx + 1] - dump_avail[idx]; 96214903Sgonzo } 97214903Sgonzo} 98214903Sgonzo 99214903Sgonzostatic struct md_pa * 100214903Sgonzomd_pa_first(void) 101214903Sgonzo{ 102214903Sgonzo 103214903Sgonzo return (&dump_map[0]); 104214903Sgonzo} 105214903Sgonzo 106214903Sgonzostatic struct md_pa * 107214903Sgonzomd_pa_next(struct md_pa *mdp) 108214903Sgonzo{ 109214903Sgonzo 110214903Sgonzo mdp++; 111214903Sgonzo if (mdp->md_size == 0) 112214903Sgonzo mdp = NULL; 113214903Sgonzo return (mdp); 114214903Sgonzo} 115214903Sgonzo 116214903Sgonzostatic int 117214903Sgonzobuf_write(struct dumperinfo *di, char *ptr, size_t sz) 118214903Sgonzo{ 119214903Sgonzo size_t len; 120214903Sgonzo int error; 121214903Sgonzo 122214903Sgonzo while (sz) { 123214903Sgonzo len = DEV_BSIZE - fragsz; 124214903Sgonzo if (len > sz) 125214903Sgonzo len = sz; 126214903Sgonzo bcopy(ptr, buffer + fragsz, len); 127214903Sgonzo fragsz += len; 128214903Sgonzo ptr += len; 129214903Sgonzo sz -= len; 130214903Sgonzo if (fragsz == DEV_BSIZE) { 131214903Sgonzo error = dump_write(di, buffer, 0, dumplo, 132214903Sgonzo DEV_BSIZE); 133214903Sgonzo if (error) 134214903Sgonzo return error; 135214903Sgonzo dumplo += DEV_BSIZE; 136214903Sgonzo fragsz = 0; 137214903Sgonzo } 138214903Sgonzo } 139214903Sgonzo 140214903Sgonzo return (0); 141214903Sgonzo} 142214903Sgonzo 143214903Sgonzostatic int 144214903Sgonzobuf_flush(struct dumperinfo *di) 145214903Sgonzo{ 146214903Sgonzo int error; 147214903Sgonzo 148214903Sgonzo if (fragsz == 0) 149214903Sgonzo return (0); 150214903Sgonzo 151214903Sgonzo error = dump_write(di, buffer, 0, dumplo, DEV_BSIZE); 152214903Sgonzo dumplo += DEV_BSIZE; 153214903Sgonzo fragsz = 0; 154214903Sgonzo return (error); 155214903Sgonzo} 156214903Sgonzo 157214903Sgonzoextern vm_offset_t kernel_l1kva; 158214903Sgonzoextern char *pouet2; 159214903Sgonzo 160214903Sgonzostatic int 161214903Sgonzocb_dumpdata(struct md_pa *mdp, int seqnr, void *arg) 162214903Sgonzo{ 163214903Sgonzo struct dumperinfo *di = (struct dumperinfo*)arg; 164214903Sgonzo vm_paddr_t pa; 165214903Sgonzo uint32_t pgs; 166214903Sgonzo size_t counter, sz, chunk; 167214903Sgonzo int c, error; 168214903Sgonzo 169214903Sgonzo error = 0; /* catch case in which chunk size is 0 */ 170214903Sgonzo counter = 0; 171214903Sgonzo pgs = mdp->md_size / PAGE_SIZE; 172214903Sgonzo pa = mdp->md_start; 173214903Sgonzo 174214903Sgonzo printf(" chunk %d: %dMB (%d pages)", seqnr, pgs * PAGE_SIZE / ( 175214903Sgonzo 1024*1024), pgs); 176214903Sgonzo 177214903Sgonzo /* Make sure we write coherent datas. */ 178214903Sgonzo mips_dcache_wbinv_all(); 179214903Sgonzo while (pgs) { 180214903Sgonzo chunk = pgs; 181214903Sgonzo if (chunk > MAXDUMPPGS) 182214903Sgonzo chunk = MAXDUMPPGS; 183214903Sgonzo sz = chunk << PAGE_SHIFT; 184214903Sgonzo counter += sz; 185214903Sgonzo if (counter >> 24) { 186214903Sgonzo printf(" %d", pgs * PAGE_SIZE); 187214903Sgonzo counter &= (1<<24) - 1; 188214903Sgonzo } 189214903Sgonzo 190221173Sattilio#ifdef SW_WATCHDOG 191224845Sattilio wdog_kern_pat(WD_LASTVAL); 192221173Sattilio#endif 193217354Sjchandra error = dump_write(di, (void *)(intptr_t)(pa),0, dumplo, sz); /* XXX fix PA */ 194214903Sgonzo if (error) 195214903Sgonzo break; 196214903Sgonzo dumplo += sz; 197214903Sgonzo pgs -= chunk; 198214903Sgonzo pa += sz; 199214903Sgonzo 200214903Sgonzo /* Check for user abort. */ 201214903Sgonzo c = cncheckc(); 202214903Sgonzo if (c == 0x03) 203214903Sgonzo return (ECANCELED); 204214903Sgonzo if (c != -1) 205214903Sgonzo printf(" (CTRL-C to abort) "); 206214903Sgonzo } 207214903Sgonzo printf(" ... %s\n", (error) ? "fail" : "ok"); 208214903Sgonzo return (error); 209214903Sgonzo} 210214903Sgonzo 211214903Sgonzostatic int 212214903Sgonzocb_dumphdr(struct md_pa *mdp, int seqnr, void *arg) 213214903Sgonzo{ 214214903Sgonzo struct dumperinfo *di = (struct dumperinfo*)arg; 215214903Sgonzo Elf_Phdr phdr; 216214903Sgonzo uint64_t size; 217214903Sgonzo int error; 218214903Sgonzo 219214903Sgonzo size = mdp->md_size; 220214903Sgonzo bzero(&phdr, sizeof(phdr)); 221214903Sgonzo phdr.p_type = PT_LOAD; 222214903Sgonzo phdr.p_flags = PF_R; /* XXX */ 223214903Sgonzo phdr.p_offset = fileofs; 224214903Sgonzo phdr.p_vaddr = mdp->md_start; 225214903Sgonzo phdr.p_paddr = mdp->md_start; 226214903Sgonzo phdr.p_filesz = size; 227214903Sgonzo phdr.p_memsz = size; 228214903Sgonzo phdr.p_align = PAGE_SIZE; 229214903Sgonzo 230214903Sgonzo error = buf_write(di, (char*)&phdr, sizeof(phdr)); 231214903Sgonzo fileofs += phdr.p_filesz; 232214903Sgonzo return (error); 233214903Sgonzo} 234214903Sgonzo 235214903Sgonzostatic int 236214903Sgonzocb_size(struct md_pa *mdp, int seqnr, void *arg) 237214903Sgonzo{ 238214903Sgonzo uint32_t *sz = (uint32_t*)arg; 239214903Sgonzo 240214903Sgonzo *sz += (uint32_t)mdp->md_size; 241214903Sgonzo return (0); 242214903Sgonzo} 243214903Sgonzo 244214903Sgonzostatic int 245214903Sgonzoforeach_chunk(callback_t cb, void *arg) 246214903Sgonzo{ 247214903Sgonzo struct md_pa *mdp; 248214903Sgonzo int error, seqnr; 249214903Sgonzo 250214903Sgonzo seqnr = 0; 251214903Sgonzo mdp = md_pa_first(); 252214903Sgonzo while (mdp != NULL) { 253214903Sgonzo error = (*cb)(mdp, seqnr++, arg); 254214903Sgonzo if (error) 255214903Sgonzo return (-error); 256214903Sgonzo mdp = md_pa_next(mdp); 257214903Sgonzo } 258214903Sgonzo return (seqnr); 259214903Sgonzo} 260214903Sgonzo 261214903Sgonzovoid 262214903Sgonzodumpsys(struct dumperinfo *di) 263214903Sgonzo{ 264214903Sgonzo Elf_Ehdr ehdr; 265214903Sgonzo uint32_t dumpsize; 266214903Sgonzo off_t hdrgap; 267214903Sgonzo size_t hdrsz; 268214903Sgonzo int error; 269214903Sgonzo 270214903Sgonzo if (do_minidump) { 271214903Sgonzo minidumpsys(di); 272214903Sgonzo return; 273214903Sgonzo } 274214903Sgonzo 275214903Sgonzo bzero(&ehdr, sizeof(ehdr)); 276214903Sgonzo ehdr.e_ident[EI_MAG0] = ELFMAG0; 277214903Sgonzo ehdr.e_ident[EI_MAG1] = ELFMAG1; 278214903Sgonzo ehdr.e_ident[EI_MAG2] = ELFMAG2; 279214903Sgonzo ehdr.e_ident[EI_MAG3] = ELFMAG3; 280214903Sgonzo ehdr.e_ident[EI_CLASS] = ELF_CLASS; 281214903Sgonzo#if BYTE_ORDER == LITTLE_ENDIAN 282214903Sgonzo ehdr.e_ident[EI_DATA] = ELFDATA2LSB; 283214903Sgonzo#else 284214903Sgonzo ehdr.e_ident[EI_DATA] = ELFDATA2MSB; 285214903Sgonzo#endif 286214903Sgonzo ehdr.e_ident[EI_VERSION] = EV_CURRENT; 287214903Sgonzo ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE; /* XXX big picture? */ 288214903Sgonzo ehdr.e_type = ET_CORE; 289214903Sgonzo ehdr.e_machine = EM_MIPS; 290214903Sgonzo ehdr.e_phoff = sizeof(ehdr); 291214903Sgonzo ehdr.e_flags = 0; 292214903Sgonzo ehdr.e_ehsize = sizeof(ehdr); 293214903Sgonzo ehdr.e_phentsize = sizeof(Elf_Phdr); 294214903Sgonzo ehdr.e_shentsize = sizeof(Elf_Shdr); 295214903Sgonzo 296214903Sgonzo md_pa_init(); 297214903Sgonzo 298214903Sgonzo /* Calculate dump size. */ 299214903Sgonzo dumpsize = 0L; 300214903Sgonzo ehdr.e_phnum = foreach_chunk(cb_size, &dumpsize); 301214903Sgonzo hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize; 302214903Sgonzo fileofs = MD_ALIGN(hdrsz); 303214903Sgonzo dumpsize += fileofs; 304214903Sgonzo hdrgap = fileofs - DEV_ALIGN(hdrsz); 305214903Sgonzo 306214903Sgonzo /* Determine dump offset on device. */ 307214903Sgonzo if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { 308214903Sgonzo error = ENOSPC; 309214903Sgonzo goto fail; 310214903Sgonzo } 311214903Sgonzo dumplo = di->mediaoffset + di->mediasize - dumpsize; 312214903Sgonzo dumplo -= sizeof(kdh) * 2; 313214903Sgonzo 314214903Sgonzo mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_MIPS_VERSION, dumpsize, di->blocksize); 315214903Sgonzo 316214903Sgonzo printf("Dumping %llu MB (%d chunks)\n", (long long)dumpsize >> 20, 317214903Sgonzo ehdr.e_phnum); 318214903Sgonzo 319214903Sgonzo /* Dump leader */ 320214903Sgonzo error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); 321214903Sgonzo if (error) 322214903Sgonzo goto fail; 323214903Sgonzo dumplo += sizeof(kdh); 324214903Sgonzo 325214903Sgonzo /* Dump ELF header */ 326214903Sgonzo error = buf_write(di, (char*)&ehdr, sizeof(ehdr)); 327214903Sgonzo if (error) 328214903Sgonzo goto fail; 329214903Sgonzo 330214903Sgonzo /* Dump program headers */ 331214903Sgonzo error = foreach_chunk(cb_dumphdr, di); 332214903Sgonzo if (error < 0) 333214903Sgonzo goto fail; 334214903Sgonzo buf_flush(di); 335214903Sgonzo 336214903Sgonzo /* 337214903Sgonzo * All headers are written using blocked I/O, so we know the 338214903Sgonzo * current offset is (still) block aligned. Skip the alignement 339214903Sgonzo * in the file to have the segment contents aligned at page 340214903Sgonzo * boundary. We cannot use MD_ALIGN on dumplo, because we don't 341214903Sgonzo * care and may very well be unaligned within the dump device. 342214903Sgonzo */ 343214903Sgonzo dumplo += hdrgap; 344214903Sgonzo 345214903Sgonzo /* Dump memory chunks (updates dumplo) */ 346214903Sgonzo error = foreach_chunk(cb_dumpdata, di); 347214903Sgonzo if (error < 0) 348214903Sgonzo goto fail; 349214903Sgonzo 350214903Sgonzo /* Dump trailer */ 351214903Sgonzo error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); 352214903Sgonzo if (error) 353214903Sgonzo goto fail; 354214903Sgonzo 355214903Sgonzo /* Signal completion, signoff and exit stage left. */ 356214903Sgonzo dump_write(di, NULL, 0, 0, 0); 357214903Sgonzo printf("\nDump complete\n"); 358214903Sgonzo return; 359214903Sgonzo 360214903Sgonzo fail: 361214903Sgonzo if (error < 0) 362214903Sgonzo error = -error; 363214903Sgonzo 364214903Sgonzo if (error == ECANCELED) 365214903Sgonzo printf("\nDump aborted\n"); 366214903Sgonzo else if (error == ENOSPC) 367214903Sgonzo printf("\nDump failed. Partition too small.\n"); 368214903Sgonzo else 369214903Sgonzo printf("\n** DUMP FAILED (ERROR %d) **\n", error); 370214903Sgonzo} 371