1139790Simp/*- 293647Smarcel * Copyright (c) 2002 Marcel Moolenaar 393647Smarcel * All rights reserved. 493647Smarcel * 593647Smarcel * Redistribution and use in source and binary forms, with or without 693647Smarcel * modification, are permitted provided that the following conditions 793647Smarcel * are met: 893647Smarcel * 993647Smarcel * 1. Redistributions of source code must retain the above copyright 1093647Smarcel * notice, this list of conditions and the following disclaimer. 1193647Smarcel * 2. Redistributions in binary form must reproduce the above copyright 1293647Smarcel * notice, this list of conditions and the following disclaimer in the 1393647Smarcel * documentation and/or other materials provided with the distribution. 1493647Smarcel * 1593647Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1693647Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1793647Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1893647Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1993647Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2093647Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2193647Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2293647Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2393647Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2493647Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2593647Smarcel */ 2693647Smarcel 27135453Smarcel#include <sys/cdefs.h> 28135453Smarcel__FBSDID("$FreeBSD$"); 29135453Smarcel 30221173Sattilio#include "opt_watchdog.h" 31221173Sattilio 3293647Smarcel#include <sys/param.h> 3393647Smarcel#include <sys/systm.h> 3493647Smarcel#include <sys/conf.h> 35105079Smarcel#include <sys/cons.h> 36270296Semaste#include <sys/efi.h> 3793647Smarcel#include <sys/kernel.h> 3893647Smarcel#include <sys/kerneldump.h> 39221173Sattilio#ifdef SW_WATCHDOG 40221173Sattilio#include <sys/watchdog.h> 41221173Sattilio#endif 4293647Smarcel#include <vm/vm.h> 4393647Smarcel#include <vm/pmap.h> 44224668Smarcel#include <machine/bootinfo.h> 4593647Smarcel#include <machine/elf.h> 4693647Smarcel#include <machine/md_var.h> 4793647Smarcel 4893717SmarcelCTASSERT(sizeof(struct kerneldumpheader) == 512); 4993717Smarcel 50107963Smarcel/* 51107963Smarcel * Don't touch the first SIZEOF_METADATA bytes on the dump device. This 52107963Smarcel * is to protect us from metadata and to protect metadata from us. 53107963Smarcel */ 54107963Smarcel#define SIZEOF_METADATA (64*1024) 55107963Smarcel 5693647Smarcel#define MD_ALIGN(x) (((off_t)(x) + EFI_PAGE_MASK) & ~EFI_PAGE_MASK) 5793933Smarcel#define DEV_ALIGN(x) (((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1)) 5893647Smarcel 59268192Smarcelstatic int minidump = 0; 60268192SmarcelTUNABLE_INT("debug.minidump", &minidump); 61268192SmarcelSYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RW, &minidump, 0, 62268192Smarcel "Enable mini crash dumps"); 6393647Smarcel 6493647Smarcelstatic struct kerneldumpheader kdh; 6593647Smarcelstatic off_t dumplo, fileofs; 6693647Smarcel 6793647Smarcel/* Handle buffered writes. */ 6893647Smarcelstatic char buffer[DEV_BSIZE]; 6993647Smarcelstatic size_t fragsz; 7093647Smarcel 7193647Smarcelstatic int 7293647Smarcelbuf_write(struct dumperinfo *di, char *ptr, size_t sz) 7393647Smarcel{ 7493647Smarcel size_t len; 7593647Smarcel int error; 7693647Smarcel 7793647Smarcel while (sz) { 7893647Smarcel len = DEV_BSIZE - fragsz; 7993647Smarcel if (len > sz) 8093647Smarcel len = sz; 8193647Smarcel bcopy(ptr, buffer + fragsz, len); 8293647Smarcel fragsz += len; 8393647Smarcel ptr += len; 8493647Smarcel sz -= len; 8593647Smarcel if (fragsz == DEV_BSIZE) { 86175768Sru error = dump_write(di, buffer, 0, dumplo, 8793647Smarcel DEV_BSIZE); 8893647Smarcel if (error) 89268192Smarcel return (error); 9093647Smarcel dumplo += DEV_BSIZE; 9193647Smarcel fragsz = 0; 9293647Smarcel } 9393647Smarcel } 9493647Smarcel 9593647Smarcel return (0); 9693647Smarcel} 9793647Smarcel 9893647Smarcelstatic int 9993647Smarcelbuf_flush(struct dumperinfo *di) 10093647Smarcel{ 10193647Smarcel int error; 10293647Smarcel 10393647Smarcel if (fragsz == 0) 10493647Smarcel return (0); 10593647Smarcel 106175768Sru error = dump_write(di, buffer, 0, dumplo, DEV_BSIZE); 10793647Smarcel dumplo += DEV_BSIZE; 108147740Smarcel fragsz = 0; 10993647Smarcel return (error); 11093647Smarcel} 11193647Smarcel 112268192Smarcel/* 113268192Smarcel * Physical dump support 114268192Smarcel */ 115268192Smarcel 116268192Smarceltypedef int phys_callback_t(struct efi_md*, int, void*); 117268192Smarcel 11893647Smarcelstatic int 119268192Smarcelphys_cb_dumpdata(struct efi_md *mdp, int seqnr, void *arg) 12093647Smarcel{ 12193647Smarcel struct dumperinfo *di = (struct dumperinfo*)arg; 12293647Smarcel vm_offset_t pa; 12393647Smarcel uint64_t pgs; 12494642Smarcel size_t counter, sz; 125105079Smarcel int c, error, twiddle; 12693647Smarcel 127135453Smarcel error = 0; /* catch case in which mdp->md_pages is 0 */ 12894642Smarcel counter = 0; /* Update twiddle every 16MB */ 12993712Smarcel twiddle = 0; 130135453Smarcel pgs = mdp->md_pages; 131135453Smarcel pa = IA64_PHYS_TO_RR7(mdp->md_phys); 13293712Smarcel 13394642Smarcel printf(" chunk %d: %ld pages ", seqnr, (long)pgs); 13493712Smarcel 13593647Smarcel while (pgs) { 13693647Smarcel sz = (pgs > (DFLTPHYS >> EFI_PAGE_SHIFT)) 13793647Smarcel ? DFLTPHYS : pgs << EFI_PAGE_SHIFT; 13894642Smarcel counter += sz; 13994642Smarcel if (counter >> 24) { 14094642Smarcel printf("%c\b", "|/-\\"[twiddle++ & 3]); 14194642Smarcel counter &= (1<<24) - 1; 14294642Smarcel } 143221173Sattilio#ifdef SW_WATCHDOG 144221173Sattilio wdog_kern_pat(WD_LASTVAL); 145221173Sattilio#endif 146175768Sru error = dump_write(di, (void*)pa, 0, dumplo, sz); 14793647Smarcel if (error) 14893647Smarcel break; 14993647Smarcel dumplo += sz; 15093647Smarcel pgs -= sz >> EFI_PAGE_SHIFT; 15193647Smarcel pa += sz; 152105079Smarcel 153105079Smarcel /* Check for user abort. */ 154105079Smarcel c = cncheckc(); 155105079Smarcel if (c == 0x03) 156105079Smarcel return (ECANCELED); 157105079Smarcel if (c != -1) 158105079Smarcel printf("(CTRL-C to abort) "); 15993647Smarcel } 16093712Smarcel printf("... %s\n", (error) ? "fail" : "ok"); 16193647Smarcel return (error); 16293647Smarcel} 16393647Smarcel 16493647Smarcelstatic int 165268192Smarcelphys_cb_dumphdr(struct efi_md *mdp, int seqnr, void *arg) 16693647Smarcel{ 16793647Smarcel struct dumperinfo *di = (struct dumperinfo*)arg; 16893647Smarcel Elf64_Phdr phdr; 16993647Smarcel int error; 17093647Smarcel 17193647Smarcel bzero(&phdr, sizeof(phdr)); 17293647Smarcel phdr.p_type = PT_LOAD; 17393647Smarcel phdr.p_flags = PF_R; /* XXX */ 17493647Smarcel phdr.p_offset = fileofs; 175135453Smarcel phdr.p_vaddr = (uintptr_t)mdp->md_virt; /* XXX probably bogus. */ 176135453Smarcel phdr.p_paddr = mdp->md_phys; 177135453Smarcel phdr.p_filesz = mdp->md_pages << EFI_PAGE_SHIFT; 178135453Smarcel phdr.p_memsz = mdp->md_pages << EFI_PAGE_SHIFT; 17993647Smarcel phdr.p_align = EFI_PAGE_SIZE; 18093647Smarcel 181105591Smarcel error = buf_write(di, (char*)&phdr, sizeof(phdr)); 18293647Smarcel fileofs += phdr.p_filesz; 18393647Smarcel return (error); 18493647Smarcel} 18593647Smarcel 18693647Smarcelstatic int 187268192Smarcelphys_cb_size(struct efi_md *mdp, int seqnr, void *arg) 18893647Smarcel{ 18993647Smarcel uint64_t *sz = (uint64_t*)arg; 19093647Smarcel 191135453Smarcel *sz += (uint64_t)mdp->md_pages << EFI_PAGE_SHIFT; 19293647Smarcel return (0); 19393647Smarcel} 19493647Smarcel 19593647Smarcelstatic int 196268192Smarcelphys_foreach(phys_callback_t cb, void *arg) 19793647Smarcel{ 198135453Smarcel struct efi_md *mdp; 199135453Smarcel int error, seqnr; 20093647Smarcel 201135453Smarcel seqnr = 0; 202135453Smarcel mdp = efi_md_first(); 203135453Smarcel while (mdp != NULL) { 204224668Smarcel if (mdp->md_type == EFI_MD_TYPE_FREE || 205246712Smarcel mdp->md_type == EFI_MD_TYPE_DATA || 206246712Smarcel mdp->md_type == EFI_MD_TYPE_CODE || 207246712Smarcel mdp->md_type == EFI_MD_TYPE_BS_DATA || 208246712Smarcel mdp->md_type == EFI_MD_TYPE_BS_CODE) { 20993647Smarcel error = (*cb)(mdp, seqnr++, arg); 21093647Smarcel if (error) 21193647Smarcel return (-error); 21293647Smarcel } 213135453Smarcel mdp = efi_md_next(mdp); 21493647Smarcel } 21593647Smarcel return (seqnr); 21693647Smarcel} 21793647Smarcel 218268192Smarcel/* 219268192Smarcel * Virtual dump (aka minidump) support 220268192Smarcel */ 221268192Smarcel 222268192Smarceltypedef int virt_callback_t(vm_offset_t, vm_size_t, int, void*); 223268192Smarcel 224268192Smarcelstatic int 225268192Smarcelvirt_cb_size(vm_offset_t va, vm_size_t sz, int seqnr, void *arg) 226268192Smarcel{ 227268192Smarcel uint64_t *dumpsize = (uint64_t *)arg; 228268192Smarcel 229268192Smarcel *dumpsize += sz; 230268192Smarcel return (0); 231268192Smarcel} 232268192Smarcel 233268192Smarcelstatic int 234268192Smarcelvirt_cb_dumphdr(vm_offset_t va, vm_size_t sz, int seqnr, void *arg) 235268192Smarcel{ 236268192Smarcel struct dumperinfo *di = (struct dumperinfo *)arg; 237268192Smarcel Elf64_Phdr phdr; 238268192Smarcel int error; 239268192Smarcel 240268192Smarcel bzero(&phdr, sizeof(phdr)); 241268192Smarcel phdr.p_type = PT_LOAD; 242268192Smarcel phdr.p_flags = PF_R; /* XXX */ 243268192Smarcel phdr.p_offset = fileofs; 244268192Smarcel phdr.p_vaddr = va; 245268192Smarcel phdr.p_paddr = ~0UL; 246268192Smarcel phdr.p_filesz = sz; 247268192Smarcel phdr.p_memsz = sz; 248268192Smarcel phdr.p_align = PAGE_SIZE; 249268192Smarcel 250268192Smarcel error = buf_write(di, (char*)&phdr, sizeof(phdr)); 251268192Smarcel fileofs += phdr.p_filesz; 252268192Smarcel return (error); 253268192Smarcel} 254268192Smarcel 255268192Smarcelstatic int 256268192Smarcelvirt_cb_dumpdata(vm_offset_t va, vm_size_t sz, int seqnr, void *arg) 257268192Smarcel{ 258268192Smarcel struct dumperinfo *di = (struct dumperinfo *)arg; 259268192Smarcel size_t counter, iosz; 260268192Smarcel int c, error, twiddle; 261268192Smarcel 262268192Smarcel error = 0; /* catch case in which pgs is 0 */ 263268192Smarcel counter = 0; /* Update twiddle every 16MB */ 264268192Smarcel twiddle = 0; 265268192Smarcel 266268192Smarcel printf(" chunk %d: %ld pages ", seqnr, atop(sz)); 267268192Smarcel 268268192Smarcel while (sz) { 269268192Smarcel iosz = (sz > DFLTPHYS) ? DFLTPHYS : sz; 270268192Smarcel counter += iosz; 271268192Smarcel if (counter >> 24) { 272268192Smarcel printf("%c\b", "|/-\\"[twiddle++ & 3]); 273268192Smarcel counter &= (1<<24) - 1; 274268192Smarcel } 275268192Smarcel#ifdef SW_WATCHDOG 276268192Smarcel wdog_kern_pat(WD_LASTVAL); 277268192Smarcel#endif 278268192Smarcel error = dump_write(di, (void*)va, 0, dumplo, iosz); 279268192Smarcel if (error) 280268192Smarcel break; 281268192Smarcel dumplo += iosz; 282268192Smarcel sz -= iosz; 283268192Smarcel va += iosz; 284268192Smarcel 285268192Smarcel /* Check for user abort. */ 286268192Smarcel c = cncheckc(); 287268192Smarcel if (c == 0x03) 288268192Smarcel return (ECANCELED); 289268192Smarcel if (c != -1) 290268192Smarcel printf("(CTRL-C to abort) "); 291268192Smarcel } 292268192Smarcel printf("... %s\n", (error) ? "fail" : "ok"); 293268192Smarcel return (error); 294268192Smarcel} 295268192Smarcel 296268192Smarcelstatic int 297268192Smarcelvirt_foreach(virt_callback_t cb, void *arg) 298268192Smarcel{ 299268192Smarcel vm_offset_t va; 300268192Smarcel vm_size_t sz; 301268192Smarcel int error, seqnr; 302268192Smarcel 303268192Smarcel seqnr = 0; 304268192Smarcel while (1) { 305268192Smarcel switch (seqnr) { 306268192Smarcel case 0: 307268192Smarcel va = IA64_PBVM_BASE; 308268192Smarcel sz = round_page(bootinfo->bi_kernend) - va; 309268192Smarcel break; 310268192Smarcel default: 311268192Smarcel va = 0; 312268192Smarcel sz = 0; 313268192Smarcel break; 314268192Smarcel } 315268192Smarcel if (va == 0 && sz == 0) 316268192Smarcel break; 317268192Smarcel error = (*cb)(va, sz, seqnr, arg); 318268192Smarcel if (error) 319268192Smarcel return (-error); 320268192Smarcel seqnr++; 321268192Smarcel } 322268192Smarcel return (seqnr); 323268192Smarcel} 324268192Smarcel 325268192Smarcel/* 326268192Smarcel * main entry point. 327268192Smarcel */ 328268192Smarcel 32993647Smarcelvoid 33093647Smarceldumpsys(struct dumperinfo *di) 33193647Smarcel{ 33293647Smarcel Elf64_Ehdr ehdr; 33393647Smarcel uint64_t dumpsize; 33493933Smarcel off_t hdrgap; 33593647Smarcel size_t hdrsz; 336268192Smarcel int error, status; 33793647Smarcel 33893647Smarcel bzero(&ehdr, sizeof(ehdr)); 33993647Smarcel ehdr.e_ident[EI_MAG0] = ELFMAG0; 34093647Smarcel ehdr.e_ident[EI_MAG1] = ELFMAG1; 34193647Smarcel ehdr.e_ident[EI_MAG2] = ELFMAG2; 34293647Smarcel ehdr.e_ident[EI_MAG3] = ELFMAG3; 34393647Smarcel ehdr.e_ident[EI_CLASS] = ELFCLASS64; 34493717Smarcel#if BYTE_ORDER == LITTLE_ENDIAN 34593647Smarcel ehdr.e_ident[EI_DATA] = ELFDATA2LSB; 34693717Smarcel#else 34793717Smarcel ehdr.e_ident[EI_DATA] = ELFDATA2MSB; 34893717Smarcel#endif 34993647Smarcel ehdr.e_ident[EI_VERSION] = EV_CURRENT; 35093647Smarcel ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE; /* XXX big picture? */ 35193647Smarcel ehdr.e_type = ET_CORE; 35293647Smarcel ehdr.e_machine = EM_IA_64; 353268192Smarcel ehdr.e_entry = (minidump) ? (uintptr_t)bootinfo : 354268192Smarcel ia64_tpa((uintptr_t)bootinfo); 35593647Smarcel ehdr.e_phoff = sizeof(ehdr); 356268192Smarcel ehdr.e_flags = (minidump) ? 0 : EF_IA_64_ABSOLUTE; /* XXX misuse? */ 35793647Smarcel ehdr.e_ehsize = sizeof(ehdr); 35893647Smarcel ehdr.e_phentsize = sizeof(Elf64_Phdr); 35993647Smarcel ehdr.e_shentsize = sizeof(Elf64_Shdr); 36093647Smarcel 36193647Smarcel /* Calculate dump size. */ 36293647Smarcel dumpsize = 0L; 363268192Smarcel status = (minidump) ? virt_foreach(virt_cb_size, &dumpsize) : 364268192Smarcel phys_foreach(phys_cb_size, &dumpsize); 365268192Smarcel if (status < 0) { 366268192Smarcel error = -status; 367268192Smarcel goto fail; 368268192Smarcel } 369268192Smarcel ehdr.e_phnum = status; 37093647Smarcel hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize; 371268192Smarcel fileofs = (minidump) ? round_page(hdrsz) : MD_ALIGN(hdrsz); 37293647Smarcel dumpsize += fileofs; 37393933Smarcel hdrgap = fileofs - DEV_ALIGN(hdrsz); 37493647Smarcel 37593647Smarcel /* Determine dump offset on device. */ 376107963Smarcel if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { 377107963Smarcel error = ENOSPC; 378107963Smarcel goto fail; 379107963Smarcel } 38093647Smarcel dumplo = di->mediaoffset + di->mediasize - dumpsize; 38193647Smarcel dumplo -= sizeof(kdh) * 2; 38293647Smarcel 383183527Speter mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_IA64_VERSION, dumpsize, di->blocksize); 38493647Smarcel 38596899Smarcel printf("Dumping %llu MB (%d chunks)\n", (long long)dumpsize >> 20, 38696899Smarcel ehdr.e_phnum); 38793647Smarcel 38893647Smarcel /* Dump leader */ 389175768Sru error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); 39093647Smarcel if (error) 39193647Smarcel goto fail; 39293647Smarcel dumplo += sizeof(kdh); 39393647Smarcel 39493647Smarcel /* Dump ELF header */ 39593647Smarcel error = buf_write(di, (char*)&ehdr, sizeof(ehdr)); 39693647Smarcel if (error) 39793647Smarcel goto fail; 39893647Smarcel 39993647Smarcel /* Dump program headers */ 400268192Smarcel status = (minidump) ? virt_foreach(virt_cb_dumphdr, di) : 401268192Smarcel phys_foreach(phys_cb_dumphdr, di); 402268192Smarcel if (status < 0) { 403268192Smarcel error = -status; 40493647Smarcel goto fail; 405268192Smarcel } 40693647Smarcel buf_flush(di); 40793647Smarcel 40893933Smarcel /* 40993933Smarcel * All headers are written using blocked I/O, so we know the 410268192Smarcel * current offset is (still) block aligned. Skip the alignment 41193933Smarcel * in the file to have the segment contents aligned at page 412268192Smarcel * boundary. For physical dumps, it's the EFI page size (= 4K). 413268192Smarcel * For minidumps it's the kernel's page size (= 8K). 41493933Smarcel */ 41593933Smarcel dumplo += hdrgap; 41693933Smarcel 41794642Smarcel /* Dump memory chunks (updates dumplo) */ 418268192Smarcel status = (minidump) ? virt_foreach(virt_cb_dumpdata, di) : 419268192Smarcel phys_foreach(phys_cb_dumpdata, di); 420268192Smarcel if (status < 0) { 421268192Smarcel error = -status; 42293647Smarcel goto fail; 423268192Smarcel } 42493647Smarcel 42593647Smarcel /* Dump trailer */ 426175768Sru error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); 42793647Smarcel if (error) 42893647Smarcel goto fail; 42993647Smarcel 43093647Smarcel /* Signal completion, signoff and exit stage left. */ 431175768Sru dump_write(di, NULL, 0, 0, 0); 43293647Smarcel printf("\nDump complete\n"); 43393647Smarcel return; 43493647Smarcel 43593647Smarcel fail: 436105079Smarcel if (error == ECANCELED) 437105079Smarcel printf("\nDump aborted\n"); 438105079Smarcel else 439105079Smarcel printf("\n** DUMP FAILED (ERROR %d) **\n", error); 44093647Smarcel} 441