kern_dump.c revision 276772
1276772Smarkj/*- 2276772Smarkj * Copyright (c) 2002 Marcel Moolenaar 3276772Smarkj * All rights reserved. 4276772Smarkj * 5276772Smarkj * Redistribution and use in source and binary forms, with or without 6276772Smarkj * modification, are permitted provided that the following conditions 7276772Smarkj * are met: 8276772Smarkj * 9276772Smarkj * 1. Redistributions of source code must retain the above copyright 10276772Smarkj * notice, this list of conditions and the following disclaimer. 11276772Smarkj * 2. Redistributions in binary form must reproduce the above copyright 12276772Smarkj * notice, this list of conditions and the following disclaimer in the 13276772Smarkj * documentation and/or other materials provided with the distribution. 14276772Smarkj * 15276772Smarkj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16276772Smarkj * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17276772Smarkj * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18276772Smarkj * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19276772Smarkj * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20276772Smarkj * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21276772Smarkj * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22276772Smarkj * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23276772Smarkj * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24276772Smarkj * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25276772Smarkj */ 26276772Smarkj 27276772Smarkj#include <sys/cdefs.h> 28276772Smarkj__FBSDID("$FreeBSD: head/sys/kern/kern_dump.c 276772 2015-01-07 01:01:39Z markj $"); 29276772Smarkj 30276772Smarkj#include "opt_watchdog.h" 31276772Smarkj 32276772Smarkj#include <sys/param.h> 33276772Smarkj#include <sys/systm.h> 34276772Smarkj#include <sys/conf.h> 35276772Smarkj#include <sys/cons.h> 36276772Smarkj#include <sys/kernel.h> 37276772Smarkj#include <sys/proc.h> 38276772Smarkj#include <sys/kerneldump.h> 39276772Smarkj#ifdef SW_WATCHDOG 40276772Smarkj#include <sys/watchdog.h> 41276772Smarkj#endif 42276772Smarkj#include <vm/vm.h> 43276772Smarkj#include <vm/vm_param.h> 44276772Smarkj#include <vm/pmap.h> 45276772Smarkj#include <machine/dump.h> 46276772Smarkj#include <machine/elf.h> 47276772Smarkj#include <machine/md_var.h> 48276772Smarkj#include <machine/pcb.h> 49276772Smarkj 50276772SmarkjCTASSERT(sizeof(struct kerneldumpheader) == 512); 51276772Smarkj 52276772Smarkj/* 53276772Smarkj * Don't touch the first SIZEOF_METADATA bytes on the dump device. This 54276772Smarkj * is to protect us from metadata and to protect metadata from us. 55276772Smarkj */ 56276772Smarkj#define SIZEOF_METADATA (64*1024) 57276772Smarkj 58276772Smarkj#define MD_ALIGN(x) (((off_t)(x) + PAGE_MASK) & ~PAGE_MASK) 59276772Smarkj#define DEV_ALIGN(x) (((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1)) 60276772Smarkj 61276772Smarkjoff_t dumplo; 62276772Smarkj 63276772Smarkj/* Handle buffered writes. */ 64276772Smarkjstatic char buffer[DEV_BSIZE]; 65276772Smarkjstatic size_t fragsz; 66276772Smarkj 67276772Smarkjstruct dump_pa dump_map[DUMPSYS_MD_PA_NPAIRS]; 68276772Smarkj 69276772Smarkjvoid 70276772Smarkjdumpsys_gen_pa_init(void) 71276772Smarkj{ 72276772Smarkj#if !defined(__sparc__) && !defined(__powerpc__) 73276772Smarkj int n, idx; 74276772Smarkj 75276772Smarkj bzero(dump_map, sizeof(dump_map)); 76276772Smarkj for (n = 0; n < sizeof(dump_map) / sizeof(dump_map[0]); n++) { 77276772Smarkj idx = n * 2; 78276772Smarkj if (dump_avail[idx] == 0 && dump_avail[idx + 1] == 0) 79276772Smarkj break; 80276772Smarkj dump_map[n].pa_start = dump_avail[idx]; 81276772Smarkj dump_map[n].pa_size = dump_avail[idx + 1] - dump_avail[idx]; 82276772Smarkj } 83276772Smarkj#endif 84276772Smarkj} 85276772Smarkj 86276772Smarkjstruct dump_pa * 87276772Smarkjdumpsys_gen_pa_next(struct dump_pa *mdp) 88276772Smarkj{ 89276772Smarkj 90276772Smarkj if (mdp == NULL) 91276772Smarkj return (&dump_map[0]); 92276772Smarkj 93276772Smarkj mdp++; 94276772Smarkj if (mdp->pa_size == 0) 95276772Smarkj mdp = NULL; 96276772Smarkj return (mdp); 97276772Smarkj} 98276772Smarkj 99276772Smarkjvoid 100276772Smarkjdumpsys_gen_wbinv_all(void) 101276772Smarkj{ 102276772Smarkj} 103276772Smarkj 104276772Smarkjvoid 105276772Smarkjdumpsys_gen_unmap_chunk(vm_paddr_t pa __unused, size_t chunk __unused, 106276772Smarkj void *va __unused) 107276772Smarkj{ 108276772Smarkj} 109276772Smarkj 110276772Smarkjint 111276772Smarkjdumpsys_gen_write_aux_headers(struct dumperinfo *di) 112276772Smarkj{ 113276772Smarkj 114276772Smarkj return (0); 115276772Smarkj} 116276772Smarkj 117276772Smarkjint 118276772Smarkjdumpsys_buf_write(struct dumperinfo *di, char *ptr, size_t sz) 119276772Smarkj{ 120276772Smarkj size_t len; 121276772Smarkj int error; 122276772Smarkj 123276772Smarkj while (sz) { 124276772Smarkj len = DEV_BSIZE - fragsz; 125276772Smarkj if (len > sz) 126276772Smarkj len = sz; 127276772Smarkj bcopy(ptr, buffer + fragsz, len); 128276772Smarkj fragsz += len; 129276772Smarkj ptr += len; 130276772Smarkj sz -= len; 131276772Smarkj if (fragsz == DEV_BSIZE) { 132276772Smarkj error = dump_write(di, buffer, 0, dumplo, 133276772Smarkj DEV_BSIZE); 134276772Smarkj if (error) 135276772Smarkj return (error); 136276772Smarkj dumplo += DEV_BSIZE; 137276772Smarkj fragsz = 0; 138276772Smarkj } 139276772Smarkj } 140276772Smarkj return (0); 141276772Smarkj} 142276772Smarkj 143276772Smarkjint 144276772Smarkjdumpsys_buf_flush(struct dumperinfo *di) 145276772Smarkj{ 146276772Smarkj int error; 147276772Smarkj 148276772Smarkj if (fragsz == 0) 149276772Smarkj return (0); 150276772Smarkj 151276772Smarkj error = dump_write(di, buffer, 0, dumplo, DEV_BSIZE); 152276772Smarkj dumplo += DEV_BSIZE; 153276772Smarkj fragsz = 0; 154276772Smarkj return (error); 155276772Smarkj} 156276772Smarkj 157276772SmarkjCTASSERT(PAGE_SHIFT < 20); 158276772Smarkj#define PG2MB(pgs) ((pgs + (1 << (20 - PAGE_SHIFT)) - 1) >> (20 - PAGE_SHIFT)) 159276772Smarkj 160276772Smarkjint 161276772Smarkjdumpsys_cb_dumpdata(struct dump_pa *mdp, int seqnr, void *arg) 162276772Smarkj{ 163276772Smarkj struct dumperinfo *di = (struct dumperinfo*)arg; 164276772Smarkj vm_paddr_t pa; 165276772Smarkj void *va; 166276772Smarkj uint64_t pgs; 167276772Smarkj size_t counter, sz, chunk; 168276772Smarkj int c, error; 169276772Smarkj u_int maxdumppgs; 170276772Smarkj 171276772Smarkj error = 0; /* catch case in which chunk size is 0 */ 172276772Smarkj counter = 0; /* Update twiddle every 16MB */ 173276772Smarkj va = 0; 174276772Smarkj pgs = mdp->pa_size / PAGE_SIZE; 175276772Smarkj pa = mdp->pa_start; 176276772Smarkj maxdumppgs = min(di->maxiosize / PAGE_SIZE, MAXDUMPPGS); 177276772Smarkj if (maxdumppgs == 0) /* seatbelt */ 178276772Smarkj maxdumppgs = 1; 179276772Smarkj 180276772Smarkj printf(" chunk %d: %juMB (%ju pages)", seqnr, (uintmax_t)PG2MB(pgs), 181276772Smarkj (uintmax_t)pgs); 182276772Smarkj 183276772Smarkj dumpsys_wbinv_all(); 184276772Smarkj while (pgs) { 185276772Smarkj chunk = pgs; 186276772Smarkj if (chunk > maxdumppgs) 187276772Smarkj chunk = maxdumppgs; 188276772Smarkj sz = chunk << PAGE_SHIFT; 189276772Smarkj counter += sz; 190276772Smarkj if (counter >> 24) { 191276772Smarkj printf(" %ju", (uintmax_t)PG2MB(pgs)); 192276772Smarkj counter &= (1 << 24) - 1; 193276772Smarkj } 194276772Smarkj 195276772Smarkj dumpsys_map_chunk(pa, chunk, &va); 196276772Smarkj#ifdef SW_WATCHDOG 197276772Smarkj wdog_kern_pat(WD_LASTVAL); 198276772Smarkj#endif 199276772Smarkj 200276772Smarkj error = dump_write(di, va, 0, dumplo, sz); 201276772Smarkj dumpsys_unmap_chunk(pa, chunk, va); 202276772Smarkj if (error) 203276772Smarkj break; 204276772Smarkj dumplo += sz; 205276772Smarkj pgs -= chunk; 206276772Smarkj pa += sz; 207276772Smarkj 208276772Smarkj /* Check for user abort. */ 209276772Smarkj c = cncheckc(); 210276772Smarkj if (c == 0x03) 211276772Smarkj return (ECANCELED); 212276772Smarkj if (c != -1) 213276772Smarkj printf(" (CTRL-C to abort) "); 214276772Smarkj } 215276772Smarkj printf(" ... %s\n", (error) ? "fail" : "ok"); 216276772Smarkj return (error); 217276772Smarkj} 218276772Smarkj 219276772Smarkjint 220276772Smarkjdumpsys_foreach_chunk(dumpsys_callback_t cb, void *arg) 221276772Smarkj{ 222276772Smarkj struct dump_pa *mdp; 223276772Smarkj int error, seqnr; 224276772Smarkj 225276772Smarkj seqnr = 0; 226276772Smarkj mdp = dumpsys_pa_next(NULL); 227276772Smarkj while (mdp != NULL) { 228276772Smarkj error = (*cb)(mdp, seqnr++, arg); 229276772Smarkj if (error) 230276772Smarkj return (-error); 231276772Smarkj mdp = dumpsys_pa_next(mdp); 232276772Smarkj } 233276772Smarkj return (seqnr); 234276772Smarkj} 235276772Smarkj 236276772Smarkjstatic off_t fileofs; 237276772Smarkj 238276772Smarkjstatic int 239276772Smarkjcb_dumphdr(struct dump_pa *mdp, int seqnr, void *arg) 240276772Smarkj{ 241276772Smarkj struct dumperinfo *di = (struct dumperinfo*)arg; 242276772Smarkj Elf_Phdr phdr; 243276772Smarkj uint64_t size; 244276772Smarkj int error; 245276772Smarkj 246276772Smarkj size = mdp->pa_size; 247276772Smarkj bzero(&phdr, sizeof(phdr)); 248276772Smarkj phdr.p_type = PT_LOAD; 249276772Smarkj phdr.p_flags = PF_R; /* XXX */ 250276772Smarkj phdr.p_offset = fileofs; 251276772Smarkj#ifdef __powerpc__ 252276772Smarkj phdr.p_vaddr = (do_minidump? mdp->pa_start : ~0L); 253276772Smarkj phdr.p_paddr = (do_minidump? ~0L : mdp->pa_start); 254276772Smarkj#else 255276772Smarkj phdr.p_vaddr = mdp->pa_start; 256276772Smarkj phdr.p_paddr = mdp->pa_start; 257276772Smarkj#endif 258276772Smarkj phdr.p_filesz = size; 259276772Smarkj phdr.p_memsz = size; 260276772Smarkj phdr.p_align = PAGE_SIZE; 261276772Smarkj 262276772Smarkj error = dumpsys_buf_write(di, (char*)&phdr, sizeof(phdr)); 263276772Smarkj fileofs += phdr.p_filesz; 264276772Smarkj return (error); 265276772Smarkj} 266276772Smarkj 267276772Smarkjstatic int 268276772Smarkjcb_size(struct dump_pa *mdp, int seqnr, void *arg) 269276772Smarkj{ 270276772Smarkj uint64_t *sz; 271276772Smarkj 272276772Smarkj sz = (uint64_t *)arg; 273276772Smarkj *sz += (uint64_t)mdp->pa_size; 274276772Smarkj return (0); 275276772Smarkj} 276276772Smarkj 277276772Smarkjint 278276772Smarkjdumpsys_generic(struct dumperinfo *di) 279276772Smarkj{ 280276772Smarkj static struct kerneldumpheader kdh; 281276772Smarkj Elf_Ehdr ehdr; 282276772Smarkj uint64_t dumpsize; 283276772Smarkj off_t hdrgap; 284276772Smarkj size_t hdrsz; 285276772Smarkj int error; 286276772Smarkj 287276772Smarkj#ifndef __powerpc__ 288276772Smarkj if (do_minidump) 289276772Smarkj return (minidumpsys(di)); 290276772Smarkj#endif 291276772Smarkj 292276772Smarkj bzero(&ehdr, sizeof(ehdr)); 293276772Smarkj ehdr.e_ident[EI_MAG0] = ELFMAG0; 294276772Smarkj ehdr.e_ident[EI_MAG1] = ELFMAG1; 295276772Smarkj ehdr.e_ident[EI_MAG2] = ELFMAG2; 296276772Smarkj ehdr.e_ident[EI_MAG3] = ELFMAG3; 297276772Smarkj ehdr.e_ident[EI_CLASS] = ELF_CLASS; 298276772Smarkj#if BYTE_ORDER == LITTLE_ENDIAN 299276772Smarkj ehdr.e_ident[EI_DATA] = ELFDATA2LSB; 300276772Smarkj#else 301276772Smarkj ehdr.e_ident[EI_DATA] = ELFDATA2MSB; 302276772Smarkj#endif 303276772Smarkj ehdr.e_ident[EI_VERSION] = EV_CURRENT; 304276772Smarkj ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE; /* XXX big picture? */ 305276772Smarkj ehdr.e_type = ET_CORE; 306276772Smarkj ehdr.e_machine = EM_VALUE; 307276772Smarkj ehdr.e_phoff = sizeof(ehdr); 308276772Smarkj ehdr.e_flags = 0; 309276772Smarkj ehdr.e_ehsize = sizeof(ehdr); 310276772Smarkj ehdr.e_phentsize = sizeof(Elf_Phdr); 311276772Smarkj ehdr.e_shentsize = sizeof(Elf_Shdr); 312276772Smarkj 313276772Smarkj dumpsys_pa_init(); 314276772Smarkj 315276772Smarkj /* Calculate dump size. */ 316276772Smarkj dumpsize = 0L; 317276772Smarkj ehdr.e_phnum = dumpsys_foreach_chunk(cb_size, &dumpsize) + 318276772Smarkj DUMPSYS_NUM_AUX_HDRS; 319276772Smarkj hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize; 320276772Smarkj fileofs = MD_ALIGN(hdrsz); 321276772Smarkj dumpsize += fileofs; 322276772Smarkj hdrgap = fileofs - DEV_ALIGN(hdrsz); 323276772Smarkj 324276772Smarkj /* Determine dump offset on device. */ 325276772Smarkj if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { 326276772Smarkj error = ENOSPC; 327276772Smarkj goto fail; 328276772Smarkj } 329276772Smarkj dumplo = di->mediaoffset + di->mediasize - dumpsize; 330276772Smarkj dumplo -= sizeof(kdh) * 2; 331276772Smarkj 332276772Smarkj mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_ARCH_VERSION, dumpsize, 333276772Smarkj di->blocksize); 334276772Smarkj 335276772Smarkj printf("Dumping %ju MB (%d chunks)\n", (uintmax_t)dumpsize >> 20, 336276772Smarkj ehdr.e_phnum - DUMPSYS_NUM_AUX_HDRS); 337276772Smarkj 338276772Smarkj /* Dump leader */ 339276772Smarkj error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); 340276772Smarkj if (error) 341276772Smarkj goto fail; 342276772Smarkj dumplo += sizeof(kdh); 343276772Smarkj 344276772Smarkj /* Dump ELF header */ 345276772Smarkj error = dumpsys_buf_write(di, (char*)&ehdr, sizeof(ehdr)); 346276772Smarkj if (error) 347276772Smarkj goto fail; 348276772Smarkj 349276772Smarkj /* Dump program headers */ 350276772Smarkj error = dumpsys_foreach_chunk(cb_dumphdr, di); 351276772Smarkj if (error < 0) 352276772Smarkj goto fail; 353276772Smarkj error = dumpsys_write_aux_headers(di); 354276772Smarkj if (error < 0) 355276772Smarkj goto fail; 356276772Smarkj dumpsys_buf_flush(di); 357276772Smarkj 358276772Smarkj /* 359276772Smarkj * All headers are written using blocked I/O, so we know the 360276772Smarkj * current offset is (still) block aligned. Skip the alignement 361276772Smarkj * in the file to have the segment contents aligned at page 362276772Smarkj * boundary. We cannot use MD_ALIGN on dumplo, because we don't 363276772Smarkj * care and may very well be unaligned within the dump device. 364276772Smarkj */ 365276772Smarkj dumplo += hdrgap; 366276772Smarkj 367276772Smarkj /* Dump memory chunks (updates dumplo) */ 368276772Smarkj error = dumpsys_foreach_chunk(dumpsys_cb_dumpdata, di); 369276772Smarkj if (error < 0) 370276772Smarkj goto fail; 371276772Smarkj 372276772Smarkj /* Dump trailer */ 373276772Smarkj error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); 374276772Smarkj if (error) 375276772Smarkj goto fail; 376276772Smarkj 377276772Smarkj /* Signal completion, signoff and exit stage left. */ 378276772Smarkj dump_write(di, NULL, 0, 0, 0); 379276772Smarkj printf("\nDump complete\n"); 380276772Smarkj return (0); 381276772Smarkj 382276772Smarkj fail: 383276772Smarkj if (error < 0) 384276772Smarkj error = -error; 385276772Smarkj 386276772Smarkj if (error == ECANCELED) 387276772Smarkj printf("\nDump aborted\n"); 388276772Smarkj else if (error == ENOSPC) 389276772Smarkj printf("\nDump failed. Partition too small.\n"); 390276772Smarkj else 391276772Smarkj printf("\n** DUMP FAILED (ERROR %d) **\n", error); 392276772Smarkj return (error); 393276772Smarkj} 394