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: stable/11/sys/kern/kern_dump.c 327920 2018-01-13 14:10:05Z karels $"); 29276772Smarkj 30276772Smarkj#include <sys/param.h> 31276772Smarkj#include <sys/systm.h> 32276772Smarkj#include <sys/conf.h> 33276772Smarkj#include <sys/cons.h> 34276772Smarkj#include <sys/kernel.h> 35276772Smarkj#include <sys/proc.h> 36276772Smarkj#include <sys/kerneldump.h> 37276772Smarkj#include <sys/watchdog.h> 38276772Smarkj#include <vm/vm.h> 39276772Smarkj#include <vm/vm_param.h> 40276772Smarkj#include <vm/pmap.h> 41276772Smarkj#include <machine/dump.h> 42276772Smarkj#include <machine/elf.h> 43276772Smarkj#include <machine/md_var.h> 44276772Smarkj#include <machine/pcb.h> 45276772Smarkj 46276772SmarkjCTASSERT(sizeof(struct kerneldumpheader) == 512); 47276772Smarkj 48276772Smarkj/* 49276772Smarkj * Don't touch the first SIZEOF_METADATA bytes on the dump device. This 50276772Smarkj * is to protect us from metadata and to protect metadata from us. 51276772Smarkj */ 52276772Smarkj#define SIZEOF_METADATA (64*1024) 53276772Smarkj 54298076Scem#define MD_ALIGN(x) roundup2((off_t)(x), PAGE_SIZE) 55276772Smarkj 56276772Smarkjoff_t dumplo; 57276772Smarkj 58276772Smarkj/* Handle buffered writes. */ 59276772Smarkjstatic size_t fragsz; 60276772Smarkj 61276772Smarkjstruct dump_pa dump_map[DUMPSYS_MD_PA_NPAIRS]; 62276772Smarkj 63290957Smarius#if !defined(__powerpc__) && !defined(__sparc__) 64276772Smarkjvoid 65276772Smarkjdumpsys_gen_pa_init(void) 66276772Smarkj{ 67276772Smarkj int n, idx; 68276772Smarkj 69276772Smarkj bzero(dump_map, sizeof(dump_map)); 70298310Spfg for (n = 0; n < nitems(dump_map); n++) { 71276772Smarkj idx = n * 2; 72276772Smarkj if (dump_avail[idx] == 0 && dump_avail[idx + 1] == 0) 73276772Smarkj break; 74276772Smarkj dump_map[n].pa_start = dump_avail[idx]; 75276772Smarkj dump_map[n].pa_size = dump_avail[idx + 1] - dump_avail[idx]; 76276772Smarkj } 77290957Smarius} 78276772Smarkj#endif 79276772Smarkj 80276772Smarkjstruct dump_pa * 81276772Smarkjdumpsys_gen_pa_next(struct dump_pa *mdp) 82276772Smarkj{ 83276772Smarkj 84276772Smarkj if (mdp == NULL) 85276772Smarkj return (&dump_map[0]); 86276772Smarkj 87276772Smarkj mdp++; 88276772Smarkj if (mdp->pa_size == 0) 89276772Smarkj mdp = NULL; 90276772Smarkj return (mdp); 91276772Smarkj} 92276772Smarkj 93276772Smarkjvoid 94276772Smarkjdumpsys_gen_wbinv_all(void) 95276772Smarkj{ 96290957Smarius 97276772Smarkj} 98276772Smarkj 99276772Smarkjvoid 100276772Smarkjdumpsys_gen_unmap_chunk(vm_paddr_t pa __unused, size_t chunk __unused, 101276772Smarkj void *va __unused) 102276772Smarkj{ 103290957Smarius 104276772Smarkj} 105276772Smarkj 106290957Smarius#if !defined(__sparc__) 107276772Smarkjint 108276772Smarkjdumpsys_gen_write_aux_headers(struct dumperinfo *di) 109276772Smarkj{ 110276772Smarkj 111276772Smarkj return (0); 112276772Smarkj} 113290957Smarius#endif 114276772Smarkj 115276772Smarkjint 116276772Smarkjdumpsys_buf_write(struct dumperinfo *di, char *ptr, size_t sz) 117276772Smarkj{ 118276772Smarkj size_t len; 119276772Smarkj int error; 120276772Smarkj 121276772Smarkj while (sz) { 122298076Scem len = di->blocksize - fragsz; 123276772Smarkj if (len > sz) 124276772Smarkj len = sz; 125298076Scem memcpy((char *)di->blockbuf + fragsz, ptr, len); 126276772Smarkj fragsz += len; 127276772Smarkj ptr += len; 128276772Smarkj sz -= len; 129298076Scem if (fragsz == di->blocksize) { 130298076Scem error = dump_write(di, di->blockbuf, 0, dumplo, 131298076Scem di->blocksize); 132276772Smarkj if (error) 133276772Smarkj return (error); 134298076Scem dumplo += di->blocksize; 135276772Smarkj fragsz = 0; 136276772Smarkj } 137276772Smarkj } 138276772Smarkj return (0); 139276772Smarkj} 140276772Smarkj 141276772Smarkjint 142276772Smarkjdumpsys_buf_flush(struct dumperinfo *di) 143276772Smarkj{ 144276772Smarkj int error; 145276772Smarkj 146276772Smarkj if (fragsz == 0) 147276772Smarkj return (0); 148276772Smarkj 149298076Scem error = dump_write(di, di->blockbuf, 0, dumplo, di->blocksize); 150298076Scem dumplo += di->blocksize; 151276772Smarkj fragsz = 0; 152276772Smarkj return (error); 153276772Smarkj} 154276772Smarkj 155276772SmarkjCTASSERT(PAGE_SHIFT < 20); 156276772Smarkj#define PG2MB(pgs) ((pgs + (1 << (20 - PAGE_SHIFT)) - 1) >> (20 - PAGE_SHIFT)) 157276772Smarkj 158276772Smarkjint 159276772Smarkjdumpsys_cb_dumpdata(struct dump_pa *mdp, int seqnr, void *arg) 160276772Smarkj{ 161276772Smarkj struct dumperinfo *di = (struct dumperinfo*)arg; 162276772Smarkj vm_paddr_t pa; 163276772Smarkj void *va; 164276772Smarkj uint64_t pgs; 165276772Smarkj size_t counter, sz, chunk; 166276772Smarkj int c, error; 167276772Smarkj u_int maxdumppgs; 168276772Smarkj 169276772Smarkj error = 0; /* catch case in which chunk size is 0 */ 170276772Smarkj counter = 0; /* Update twiddle every 16MB */ 171298069Spfg va = NULL; 172276772Smarkj pgs = mdp->pa_size / PAGE_SIZE; 173276772Smarkj pa = mdp->pa_start; 174276772Smarkj maxdumppgs = min(di->maxiosize / PAGE_SIZE, MAXDUMPPGS); 175276772Smarkj if (maxdumppgs == 0) /* seatbelt */ 176276772Smarkj maxdumppgs = 1; 177276772Smarkj 178276772Smarkj printf(" chunk %d: %juMB (%ju pages)", seqnr, (uintmax_t)PG2MB(pgs), 179276772Smarkj (uintmax_t)pgs); 180276772Smarkj 181276772Smarkj dumpsys_wbinv_all(); 182276772Smarkj while (pgs) { 183276772Smarkj chunk = pgs; 184276772Smarkj if (chunk > maxdumppgs) 185276772Smarkj chunk = maxdumppgs; 186276772Smarkj sz = chunk << PAGE_SHIFT; 187276772Smarkj counter += sz; 188276772Smarkj if (counter >> 24) { 189276772Smarkj printf(" %ju", (uintmax_t)PG2MB(pgs)); 190276772Smarkj counter &= (1 << 24) - 1; 191276772Smarkj } 192276772Smarkj 193276772Smarkj dumpsys_map_chunk(pa, chunk, &va); 194276772Smarkj wdog_kern_pat(WD_LASTVAL); 195276772Smarkj 196276772Smarkj error = dump_write(di, va, 0, dumplo, sz); 197276772Smarkj dumpsys_unmap_chunk(pa, chunk, va); 198276772Smarkj if (error) 199276772Smarkj break; 200276772Smarkj dumplo += sz; 201276772Smarkj pgs -= chunk; 202276772Smarkj pa += sz; 203276772Smarkj 204276772Smarkj /* Check for user abort. */ 205276772Smarkj c = cncheckc(); 206276772Smarkj if (c == 0x03) 207276772Smarkj return (ECANCELED); 208276772Smarkj if (c != -1) 209276772Smarkj printf(" (CTRL-C to abort) "); 210276772Smarkj } 211276772Smarkj printf(" ... %s\n", (error) ? "fail" : "ok"); 212276772Smarkj return (error); 213276772Smarkj} 214276772Smarkj 215276772Smarkjint 216276772Smarkjdumpsys_foreach_chunk(dumpsys_callback_t cb, void *arg) 217276772Smarkj{ 218276772Smarkj struct dump_pa *mdp; 219276772Smarkj int error, seqnr; 220276772Smarkj 221276772Smarkj seqnr = 0; 222276772Smarkj mdp = dumpsys_pa_next(NULL); 223276772Smarkj while (mdp != NULL) { 224276772Smarkj error = (*cb)(mdp, seqnr++, arg); 225276772Smarkj if (error) 226276772Smarkj return (-error); 227276772Smarkj mdp = dumpsys_pa_next(mdp); 228276772Smarkj } 229276772Smarkj return (seqnr); 230276772Smarkj} 231276772Smarkj 232290957Smarius#if !defined(__sparc__) 233276772Smarkjstatic off_t fileofs; 234276772Smarkj 235276772Smarkjstatic int 236276772Smarkjcb_dumphdr(struct dump_pa *mdp, int seqnr, void *arg) 237276772Smarkj{ 238276772Smarkj struct dumperinfo *di = (struct dumperinfo*)arg; 239276772Smarkj Elf_Phdr phdr; 240276772Smarkj uint64_t size; 241276772Smarkj int error; 242276772Smarkj 243276772Smarkj size = mdp->pa_size; 244276772Smarkj bzero(&phdr, sizeof(phdr)); 245276772Smarkj phdr.p_type = PT_LOAD; 246276772Smarkj phdr.p_flags = PF_R; /* XXX */ 247276772Smarkj phdr.p_offset = fileofs; 248276772Smarkj#ifdef __powerpc__ 249276772Smarkj phdr.p_vaddr = (do_minidump? mdp->pa_start : ~0L); 250276772Smarkj phdr.p_paddr = (do_minidump? ~0L : mdp->pa_start); 251276772Smarkj#else 252276772Smarkj phdr.p_vaddr = mdp->pa_start; 253276772Smarkj phdr.p_paddr = mdp->pa_start; 254276772Smarkj#endif 255276772Smarkj phdr.p_filesz = size; 256276772Smarkj phdr.p_memsz = size; 257276772Smarkj phdr.p_align = PAGE_SIZE; 258276772Smarkj 259276772Smarkj error = dumpsys_buf_write(di, (char*)&phdr, sizeof(phdr)); 260276772Smarkj fileofs += phdr.p_filesz; 261276772Smarkj return (error); 262276772Smarkj} 263276772Smarkj 264276772Smarkjstatic int 265276772Smarkjcb_size(struct dump_pa *mdp, int seqnr, void *arg) 266276772Smarkj{ 267276772Smarkj uint64_t *sz; 268276772Smarkj 269276772Smarkj sz = (uint64_t *)arg; 270276772Smarkj *sz += (uint64_t)mdp->pa_size; 271276772Smarkj return (0); 272276772Smarkj} 273276772Smarkj 274276772Smarkjint 275276772Smarkjdumpsys_generic(struct dumperinfo *di) 276276772Smarkj{ 277276772Smarkj static struct kerneldumpheader kdh; 278276772Smarkj Elf_Ehdr ehdr; 279276772Smarkj uint64_t dumpsize; 280276772Smarkj off_t hdrgap; 281298076Scem size_t hdrsz, size; 282276772Smarkj int error; 283276772Smarkj 284276772Smarkj#ifndef __powerpc__ 285276772Smarkj if (do_minidump) 286276772Smarkj return (minidumpsys(di)); 287276772Smarkj#endif 288276772Smarkj 289276772Smarkj bzero(&ehdr, sizeof(ehdr)); 290276772Smarkj ehdr.e_ident[EI_MAG0] = ELFMAG0; 291276772Smarkj ehdr.e_ident[EI_MAG1] = ELFMAG1; 292276772Smarkj ehdr.e_ident[EI_MAG2] = ELFMAG2; 293276772Smarkj ehdr.e_ident[EI_MAG3] = ELFMAG3; 294276772Smarkj ehdr.e_ident[EI_CLASS] = ELF_CLASS; 295276772Smarkj#if BYTE_ORDER == LITTLE_ENDIAN 296276772Smarkj ehdr.e_ident[EI_DATA] = ELFDATA2LSB; 297276772Smarkj#else 298276772Smarkj ehdr.e_ident[EI_DATA] = ELFDATA2MSB; 299276772Smarkj#endif 300276772Smarkj ehdr.e_ident[EI_VERSION] = EV_CURRENT; 301276772Smarkj ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE; /* XXX big picture? */ 302276772Smarkj ehdr.e_type = ET_CORE; 303276772Smarkj ehdr.e_machine = EM_VALUE; 304276772Smarkj ehdr.e_phoff = sizeof(ehdr); 305276772Smarkj ehdr.e_flags = 0; 306276772Smarkj ehdr.e_ehsize = sizeof(ehdr); 307276772Smarkj ehdr.e_phentsize = sizeof(Elf_Phdr); 308276772Smarkj ehdr.e_shentsize = sizeof(Elf_Shdr); 309276772Smarkj 310276772Smarkj dumpsys_pa_init(); 311276772Smarkj 312276772Smarkj /* Calculate dump size. */ 313276772Smarkj dumpsize = 0L; 314276772Smarkj ehdr.e_phnum = dumpsys_foreach_chunk(cb_size, &dumpsize) + 315276772Smarkj DUMPSYS_NUM_AUX_HDRS; 316276772Smarkj hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize; 317276772Smarkj fileofs = MD_ALIGN(hdrsz); 318276772Smarkj dumpsize += fileofs; 319298076Scem hdrgap = fileofs - roundup2((off_t)hdrsz, di->blocksize); 320276772Smarkj 321276772Smarkj /* Determine dump offset on device. */ 322298076Scem if (di->mediasize < SIZEOF_METADATA + dumpsize + di->blocksize * 2) { 323276772Smarkj error = ENOSPC; 324276772Smarkj goto fail; 325276772Smarkj } 326276772Smarkj dumplo = di->mediaoffset + di->mediasize - dumpsize; 327298076Scem dumplo -= di->blocksize * 2; 328276772Smarkj 329276772Smarkj mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_ARCH_VERSION, dumpsize, 330276772Smarkj di->blocksize); 331276772Smarkj 332276772Smarkj printf("Dumping %ju MB (%d chunks)\n", (uintmax_t)dumpsize >> 20, 333276772Smarkj ehdr.e_phnum - DUMPSYS_NUM_AUX_HDRS); 334276772Smarkj 335276772Smarkj /* Dump leader */ 336298076Scem error = dump_write_pad(di, &kdh, 0, dumplo, sizeof(kdh), &size); 337276772Smarkj if (error) 338276772Smarkj goto fail; 339298076Scem dumplo += size; 340276772Smarkj 341276772Smarkj /* Dump ELF header */ 342276772Smarkj error = dumpsys_buf_write(di, (char*)&ehdr, sizeof(ehdr)); 343276772Smarkj if (error) 344276772Smarkj goto fail; 345276772Smarkj 346276772Smarkj /* Dump program headers */ 347276772Smarkj error = dumpsys_foreach_chunk(cb_dumphdr, di); 348276772Smarkj if (error < 0) 349276772Smarkj goto fail; 350276772Smarkj error = dumpsys_write_aux_headers(di); 351276772Smarkj if (error < 0) 352276772Smarkj goto fail; 353276772Smarkj dumpsys_buf_flush(di); 354276772Smarkj 355276772Smarkj /* 356276772Smarkj * All headers are written using blocked I/O, so we know the 357276772Smarkj * current offset is (still) block aligned. Skip the alignement 358276772Smarkj * in the file to have the segment contents aligned at page 359276772Smarkj * boundary. We cannot use MD_ALIGN on dumplo, because we don't 360276772Smarkj * care and may very well be unaligned within the dump device. 361276772Smarkj */ 362276772Smarkj dumplo += hdrgap; 363276772Smarkj 364276772Smarkj /* Dump memory chunks (updates dumplo) */ 365276772Smarkj error = dumpsys_foreach_chunk(dumpsys_cb_dumpdata, di); 366276772Smarkj if (error < 0) 367276772Smarkj goto fail; 368276772Smarkj 369276772Smarkj /* Dump trailer */ 370298076Scem error = dump_write_pad(di, &kdh, 0, dumplo, sizeof(kdh), &size); 371276772Smarkj if (error) 372276772Smarkj goto fail; 373276772Smarkj 374276772Smarkj /* Signal completion, signoff and exit stage left. */ 375276772Smarkj dump_write(di, NULL, 0, 0, 0); 376276772Smarkj printf("\nDump complete\n"); 377276772Smarkj return (0); 378276772Smarkj 379276772Smarkj fail: 380276772Smarkj if (error < 0) 381276772Smarkj error = -error; 382276772Smarkj 383276772Smarkj if (error == ECANCELED) 384276772Smarkj printf("\nDump aborted\n"); 385276772Smarkj else if (error == ENOSPC) 386276772Smarkj printf("\nDump failed. Partition too small.\n"); 387276772Smarkj else 388276772Smarkj printf("\n** DUMP FAILED (ERROR %d) **\n", error); 389276772Smarkj return (error); 390276772Smarkj} 391290957Smarius#endif 392