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: releng/11.0/sys/kern/kern_dump.c 298310 2016-04-19 23:48:27Z pfg $"); 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 58298076Scem#define MD_ALIGN(x) roundup2((off_t)(x), PAGE_SIZE) 59276772Smarkj 60276772Smarkjoff_t dumplo; 61276772Smarkj 62276772Smarkj/* Handle buffered writes. */ 63276772Smarkjstatic size_t fragsz; 64276772Smarkj 65276772Smarkjstruct dump_pa dump_map[DUMPSYS_MD_PA_NPAIRS]; 66276772Smarkj 67290957Smarius#if !defined(__powerpc__) && !defined(__sparc__) 68276772Smarkjvoid 69276772Smarkjdumpsys_gen_pa_init(void) 70276772Smarkj{ 71276772Smarkj int n, idx; 72276772Smarkj 73276772Smarkj bzero(dump_map, sizeof(dump_map)); 74298310Spfg for (n = 0; n < nitems(dump_map); n++) { 75276772Smarkj idx = n * 2; 76276772Smarkj if (dump_avail[idx] == 0 && dump_avail[idx + 1] == 0) 77276772Smarkj break; 78276772Smarkj dump_map[n].pa_start = dump_avail[idx]; 79276772Smarkj dump_map[n].pa_size = dump_avail[idx + 1] - dump_avail[idx]; 80276772Smarkj } 81290957Smarius} 82276772Smarkj#endif 83276772Smarkj 84276772Smarkjstruct dump_pa * 85276772Smarkjdumpsys_gen_pa_next(struct dump_pa *mdp) 86276772Smarkj{ 87276772Smarkj 88276772Smarkj if (mdp == NULL) 89276772Smarkj return (&dump_map[0]); 90276772Smarkj 91276772Smarkj mdp++; 92276772Smarkj if (mdp->pa_size == 0) 93276772Smarkj mdp = NULL; 94276772Smarkj return (mdp); 95276772Smarkj} 96276772Smarkj 97276772Smarkjvoid 98276772Smarkjdumpsys_gen_wbinv_all(void) 99276772Smarkj{ 100290957Smarius 101276772Smarkj} 102276772Smarkj 103276772Smarkjvoid 104276772Smarkjdumpsys_gen_unmap_chunk(vm_paddr_t pa __unused, size_t chunk __unused, 105276772Smarkj void *va __unused) 106276772Smarkj{ 107290957Smarius 108276772Smarkj} 109276772Smarkj 110290957Smarius#if !defined(__sparc__) 111276772Smarkjint 112276772Smarkjdumpsys_gen_write_aux_headers(struct dumperinfo *di) 113276772Smarkj{ 114276772Smarkj 115276772Smarkj return (0); 116276772Smarkj} 117290957Smarius#endif 118276772Smarkj 119276772Smarkjint 120276772Smarkjdumpsys_buf_write(struct dumperinfo *di, char *ptr, size_t sz) 121276772Smarkj{ 122276772Smarkj size_t len; 123276772Smarkj int error; 124276772Smarkj 125276772Smarkj while (sz) { 126298076Scem len = di->blocksize - fragsz; 127276772Smarkj if (len > sz) 128276772Smarkj len = sz; 129298076Scem memcpy((char *)di->blockbuf + fragsz, ptr, len); 130276772Smarkj fragsz += len; 131276772Smarkj ptr += len; 132276772Smarkj sz -= len; 133298076Scem if (fragsz == di->blocksize) { 134298076Scem error = dump_write(di, di->blockbuf, 0, dumplo, 135298076Scem di->blocksize); 136276772Smarkj if (error) 137276772Smarkj return (error); 138298076Scem dumplo += di->blocksize; 139276772Smarkj fragsz = 0; 140276772Smarkj } 141276772Smarkj } 142276772Smarkj return (0); 143276772Smarkj} 144276772Smarkj 145276772Smarkjint 146276772Smarkjdumpsys_buf_flush(struct dumperinfo *di) 147276772Smarkj{ 148276772Smarkj int error; 149276772Smarkj 150276772Smarkj if (fragsz == 0) 151276772Smarkj return (0); 152276772Smarkj 153298076Scem error = dump_write(di, di->blockbuf, 0, dumplo, di->blocksize); 154298076Scem dumplo += di->blocksize; 155276772Smarkj fragsz = 0; 156276772Smarkj return (error); 157276772Smarkj} 158276772Smarkj 159276772SmarkjCTASSERT(PAGE_SHIFT < 20); 160276772Smarkj#define PG2MB(pgs) ((pgs + (1 << (20 - PAGE_SHIFT)) - 1) >> (20 - PAGE_SHIFT)) 161276772Smarkj 162276772Smarkjint 163276772Smarkjdumpsys_cb_dumpdata(struct dump_pa *mdp, int seqnr, void *arg) 164276772Smarkj{ 165276772Smarkj struct dumperinfo *di = (struct dumperinfo*)arg; 166276772Smarkj vm_paddr_t pa; 167276772Smarkj void *va; 168276772Smarkj uint64_t pgs; 169276772Smarkj size_t counter, sz, chunk; 170276772Smarkj int c, error; 171276772Smarkj u_int maxdumppgs; 172276772Smarkj 173276772Smarkj error = 0; /* catch case in which chunk size is 0 */ 174276772Smarkj counter = 0; /* Update twiddle every 16MB */ 175298069Spfg va = NULL; 176276772Smarkj pgs = mdp->pa_size / PAGE_SIZE; 177276772Smarkj pa = mdp->pa_start; 178276772Smarkj maxdumppgs = min(di->maxiosize / PAGE_SIZE, MAXDUMPPGS); 179276772Smarkj if (maxdumppgs == 0) /* seatbelt */ 180276772Smarkj maxdumppgs = 1; 181276772Smarkj 182276772Smarkj printf(" chunk %d: %juMB (%ju pages)", seqnr, (uintmax_t)PG2MB(pgs), 183276772Smarkj (uintmax_t)pgs); 184276772Smarkj 185276772Smarkj dumpsys_wbinv_all(); 186276772Smarkj while (pgs) { 187276772Smarkj chunk = pgs; 188276772Smarkj if (chunk > maxdumppgs) 189276772Smarkj chunk = maxdumppgs; 190276772Smarkj sz = chunk << PAGE_SHIFT; 191276772Smarkj counter += sz; 192276772Smarkj if (counter >> 24) { 193276772Smarkj printf(" %ju", (uintmax_t)PG2MB(pgs)); 194276772Smarkj counter &= (1 << 24) - 1; 195276772Smarkj } 196276772Smarkj 197276772Smarkj dumpsys_map_chunk(pa, chunk, &va); 198276772Smarkj#ifdef SW_WATCHDOG 199276772Smarkj wdog_kern_pat(WD_LASTVAL); 200276772Smarkj#endif 201276772Smarkj 202276772Smarkj error = dump_write(di, va, 0, dumplo, sz); 203276772Smarkj dumpsys_unmap_chunk(pa, chunk, va); 204276772Smarkj if (error) 205276772Smarkj break; 206276772Smarkj dumplo += sz; 207276772Smarkj pgs -= chunk; 208276772Smarkj pa += sz; 209276772Smarkj 210276772Smarkj /* Check for user abort. */ 211276772Smarkj c = cncheckc(); 212276772Smarkj if (c == 0x03) 213276772Smarkj return (ECANCELED); 214276772Smarkj if (c != -1) 215276772Smarkj printf(" (CTRL-C to abort) "); 216276772Smarkj } 217276772Smarkj printf(" ... %s\n", (error) ? "fail" : "ok"); 218276772Smarkj return (error); 219276772Smarkj} 220276772Smarkj 221276772Smarkjint 222276772Smarkjdumpsys_foreach_chunk(dumpsys_callback_t cb, void *arg) 223276772Smarkj{ 224276772Smarkj struct dump_pa *mdp; 225276772Smarkj int error, seqnr; 226276772Smarkj 227276772Smarkj seqnr = 0; 228276772Smarkj mdp = dumpsys_pa_next(NULL); 229276772Smarkj while (mdp != NULL) { 230276772Smarkj error = (*cb)(mdp, seqnr++, arg); 231276772Smarkj if (error) 232276772Smarkj return (-error); 233276772Smarkj mdp = dumpsys_pa_next(mdp); 234276772Smarkj } 235276772Smarkj return (seqnr); 236276772Smarkj} 237276772Smarkj 238290957Smarius#if !defined(__sparc__) 239276772Smarkjstatic off_t fileofs; 240276772Smarkj 241276772Smarkjstatic int 242276772Smarkjcb_dumphdr(struct dump_pa *mdp, int seqnr, void *arg) 243276772Smarkj{ 244276772Smarkj struct dumperinfo *di = (struct dumperinfo*)arg; 245276772Smarkj Elf_Phdr phdr; 246276772Smarkj uint64_t size; 247276772Smarkj int error; 248276772Smarkj 249276772Smarkj size = mdp->pa_size; 250276772Smarkj bzero(&phdr, sizeof(phdr)); 251276772Smarkj phdr.p_type = PT_LOAD; 252276772Smarkj phdr.p_flags = PF_R; /* XXX */ 253276772Smarkj phdr.p_offset = fileofs; 254276772Smarkj#ifdef __powerpc__ 255276772Smarkj phdr.p_vaddr = (do_minidump? mdp->pa_start : ~0L); 256276772Smarkj phdr.p_paddr = (do_minidump? ~0L : mdp->pa_start); 257276772Smarkj#else 258276772Smarkj phdr.p_vaddr = mdp->pa_start; 259276772Smarkj phdr.p_paddr = mdp->pa_start; 260276772Smarkj#endif 261276772Smarkj phdr.p_filesz = size; 262276772Smarkj phdr.p_memsz = size; 263276772Smarkj phdr.p_align = PAGE_SIZE; 264276772Smarkj 265276772Smarkj error = dumpsys_buf_write(di, (char*)&phdr, sizeof(phdr)); 266276772Smarkj fileofs += phdr.p_filesz; 267276772Smarkj return (error); 268276772Smarkj} 269276772Smarkj 270276772Smarkjstatic int 271276772Smarkjcb_size(struct dump_pa *mdp, int seqnr, void *arg) 272276772Smarkj{ 273276772Smarkj uint64_t *sz; 274276772Smarkj 275276772Smarkj sz = (uint64_t *)arg; 276276772Smarkj *sz += (uint64_t)mdp->pa_size; 277276772Smarkj return (0); 278276772Smarkj} 279276772Smarkj 280276772Smarkjint 281276772Smarkjdumpsys_generic(struct dumperinfo *di) 282276772Smarkj{ 283276772Smarkj static struct kerneldumpheader kdh; 284276772Smarkj Elf_Ehdr ehdr; 285276772Smarkj uint64_t dumpsize; 286276772Smarkj off_t hdrgap; 287298076Scem size_t hdrsz, size; 288276772Smarkj int error; 289276772Smarkj 290276772Smarkj#ifndef __powerpc__ 291276772Smarkj if (do_minidump) 292276772Smarkj return (minidumpsys(di)); 293276772Smarkj#endif 294276772Smarkj 295276772Smarkj bzero(&ehdr, sizeof(ehdr)); 296276772Smarkj ehdr.e_ident[EI_MAG0] = ELFMAG0; 297276772Smarkj ehdr.e_ident[EI_MAG1] = ELFMAG1; 298276772Smarkj ehdr.e_ident[EI_MAG2] = ELFMAG2; 299276772Smarkj ehdr.e_ident[EI_MAG3] = ELFMAG3; 300276772Smarkj ehdr.e_ident[EI_CLASS] = ELF_CLASS; 301276772Smarkj#if BYTE_ORDER == LITTLE_ENDIAN 302276772Smarkj ehdr.e_ident[EI_DATA] = ELFDATA2LSB; 303276772Smarkj#else 304276772Smarkj ehdr.e_ident[EI_DATA] = ELFDATA2MSB; 305276772Smarkj#endif 306276772Smarkj ehdr.e_ident[EI_VERSION] = EV_CURRENT; 307276772Smarkj ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE; /* XXX big picture? */ 308276772Smarkj ehdr.e_type = ET_CORE; 309276772Smarkj ehdr.e_machine = EM_VALUE; 310276772Smarkj ehdr.e_phoff = sizeof(ehdr); 311276772Smarkj ehdr.e_flags = 0; 312276772Smarkj ehdr.e_ehsize = sizeof(ehdr); 313276772Smarkj ehdr.e_phentsize = sizeof(Elf_Phdr); 314276772Smarkj ehdr.e_shentsize = sizeof(Elf_Shdr); 315276772Smarkj 316276772Smarkj dumpsys_pa_init(); 317276772Smarkj 318276772Smarkj /* Calculate dump size. */ 319276772Smarkj dumpsize = 0L; 320276772Smarkj ehdr.e_phnum = dumpsys_foreach_chunk(cb_size, &dumpsize) + 321276772Smarkj DUMPSYS_NUM_AUX_HDRS; 322276772Smarkj hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize; 323276772Smarkj fileofs = MD_ALIGN(hdrsz); 324276772Smarkj dumpsize += fileofs; 325298076Scem hdrgap = fileofs - roundup2((off_t)hdrsz, di->blocksize); 326276772Smarkj 327276772Smarkj /* Determine dump offset on device. */ 328298076Scem if (di->mediasize < SIZEOF_METADATA + dumpsize + di->blocksize * 2) { 329276772Smarkj error = ENOSPC; 330276772Smarkj goto fail; 331276772Smarkj } 332276772Smarkj dumplo = di->mediaoffset + di->mediasize - dumpsize; 333298076Scem dumplo -= di->blocksize * 2; 334276772Smarkj 335276772Smarkj mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_ARCH_VERSION, dumpsize, 336276772Smarkj di->blocksize); 337276772Smarkj 338276772Smarkj printf("Dumping %ju MB (%d chunks)\n", (uintmax_t)dumpsize >> 20, 339276772Smarkj ehdr.e_phnum - DUMPSYS_NUM_AUX_HDRS); 340276772Smarkj 341276772Smarkj /* Dump leader */ 342298076Scem error = dump_write_pad(di, &kdh, 0, dumplo, sizeof(kdh), &size); 343276772Smarkj if (error) 344276772Smarkj goto fail; 345298076Scem dumplo += size; 346276772Smarkj 347276772Smarkj /* Dump ELF header */ 348276772Smarkj error = dumpsys_buf_write(di, (char*)&ehdr, sizeof(ehdr)); 349276772Smarkj if (error) 350276772Smarkj goto fail; 351276772Smarkj 352276772Smarkj /* Dump program headers */ 353276772Smarkj error = dumpsys_foreach_chunk(cb_dumphdr, di); 354276772Smarkj if (error < 0) 355276772Smarkj goto fail; 356276772Smarkj error = dumpsys_write_aux_headers(di); 357276772Smarkj if (error < 0) 358276772Smarkj goto fail; 359276772Smarkj dumpsys_buf_flush(di); 360276772Smarkj 361276772Smarkj /* 362276772Smarkj * All headers are written using blocked I/O, so we know the 363276772Smarkj * current offset is (still) block aligned. Skip the alignement 364276772Smarkj * in the file to have the segment contents aligned at page 365276772Smarkj * boundary. We cannot use MD_ALIGN on dumplo, because we don't 366276772Smarkj * care and may very well be unaligned within the dump device. 367276772Smarkj */ 368276772Smarkj dumplo += hdrgap; 369276772Smarkj 370276772Smarkj /* Dump memory chunks (updates dumplo) */ 371276772Smarkj error = dumpsys_foreach_chunk(dumpsys_cb_dumpdata, di); 372276772Smarkj if (error < 0) 373276772Smarkj goto fail; 374276772Smarkj 375276772Smarkj /* Dump trailer */ 376298076Scem error = dump_write_pad(di, &kdh, 0, dumplo, sizeof(kdh), &size); 377276772Smarkj if (error) 378276772Smarkj goto fail; 379276772Smarkj 380276772Smarkj /* Signal completion, signoff and exit stage left. */ 381276772Smarkj dump_write(di, NULL, 0, 0, 0); 382276772Smarkj printf("\nDump complete\n"); 383276772Smarkj return (0); 384276772Smarkj 385276772Smarkj fail: 386276772Smarkj if (error < 0) 387276772Smarkj error = -error; 388276772Smarkj 389276772Smarkj if (error == ECANCELED) 390276772Smarkj printf("\nDump aborted\n"); 391276772Smarkj else if (error == ENOSPC) 392276772Smarkj printf("\nDump failed. Partition too small.\n"); 393276772Smarkj else 394276772Smarkj printf("\n** DUMP FAILED (ERROR %d) **\n", error); 395276772Smarkj return (error); 396276772Smarkj} 397290957Smarius#endif 398