1/*- 2 * Copyright (c) 2002 Marcel Moolenaar 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD$"); 29 30#include "opt_watchdog.h" 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/conf.h> 35#include <sys/cons.h> 36#include <sys/efi.h> 37#include <sys/kernel.h> 38#include <sys/kerneldump.h> 39#ifdef SW_WATCHDOG 40#include <sys/watchdog.h> 41#endif 42#include <vm/vm.h> 43#include <vm/pmap.h> 44#include <machine/bootinfo.h> 45#include <machine/elf.h> 46#include <machine/md_var.h> 47 48CTASSERT(sizeof(struct kerneldumpheader) == 512); 49 50/* 51 * Don't touch the first SIZEOF_METADATA bytes on the dump device. This 52 * is to protect us from metadata and to protect metadata from us. 53 */ 54#define SIZEOF_METADATA (64*1024) 55 56#define MD_ALIGN(x) (((off_t)(x) + EFI_PAGE_MASK) & ~EFI_PAGE_MASK) 57#define DEV_ALIGN(x) (((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1)) 58 59static int minidump = 0; 60TUNABLE_INT("debug.minidump", &minidump); 61SYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RW, &minidump, 0, 62 "Enable mini crash dumps"); 63 64static struct kerneldumpheader kdh; 65static off_t dumplo, fileofs; 66 67/* Handle buffered writes. */ 68static char buffer[DEV_BSIZE]; 69static size_t fragsz; 70 71static int 72buf_write(struct dumperinfo *di, char *ptr, size_t sz) 73{ 74 size_t len; 75 int error; 76 77 while (sz) { 78 len = DEV_BSIZE - fragsz; 79 if (len > sz) 80 len = sz; 81 bcopy(ptr, buffer + fragsz, len); 82 fragsz += len; 83 ptr += len; 84 sz -= len; 85 if (fragsz == DEV_BSIZE) { 86 error = dump_write(di, buffer, 0, dumplo, 87 DEV_BSIZE); 88 if (error) 89 return (error); 90 dumplo += DEV_BSIZE; 91 fragsz = 0; 92 } 93 } 94 95 return (0); 96} 97 98static int 99buf_flush(struct dumperinfo *di) 100{ 101 int error; 102 103 if (fragsz == 0) 104 return (0); 105 106 error = dump_write(di, buffer, 0, dumplo, DEV_BSIZE); 107 dumplo += DEV_BSIZE; 108 fragsz = 0; 109 return (error); 110} 111 112/* 113 * Physical dump support 114 */ 115 116typedef int phys_callback_t(struct efi_md*, int, void*); 117 118static int 119phys_cb_dumpdata(struct efi_md *mdp, int seqnr, void *arg) 120{ 121 struct dumperinfo *di = (struct dumperinfo*)arg; 122 vm_offset_t pa; 123 uint64_t pgs; 124 size_t counter, sz; 125 int c, error, twiddle; 126 127 error = 0; /* catch case in which mdp->md_pages is 0 */ 128 counter = 0; /* Update twiddle every 16MB */ 129 twiddle = 0; 130 pgs = mdp->md_pages; 131 pa = IA64_PHYS_TO_RR7(mdp->md_phys); 132 133 printf(" chunk %d: %ld pages ", seqnr, (long)pgs); 134 135 while (pgs) { 136 sz = (pgs > (DFLTPHYS >> EFI_PAGE_SHIFT)) 137 ? DFLTPHYS : pgs << EFI_PAGE_SHIFT; 138 counter += sz; 139 if (counter >> 24) { 140 printf("%c\b", "|/-\\"[twiddle++ & 3]); 141 counter &= (1<<24) - 1; 142 } 143#ifdef SW_WATCHDOG 144 wdog_kern_pat(WD_LASTVAL); 145#endif 146 error = dump_write(di, (void*)pa, 0, dumplo, sz); 147 if (error) 148 break; 149 dumplo += sz; 150 pgs -= sz >> EFI_PAGE_SHIFT; 151 pa += sz; 152 153 /* Check for user abort. */ 154 c = cncheckc(); 155 if (c == 0x03) 156 return (ECANCELED); 157 if (c != -1) 158 printf("(CTRL-C to abort) "); 159 } 160 printf("... %s\n", (error) ? "fail" : "ok"); 161 return (error); 162} 163 164static int 165phys_cb_dumphdr(struct efi_md *mdp, int seqnr, void *arg) 166{ 167 struct dumperinfo *di = (struct dumperinfo*)arg; 168 Elf64_Phdr phdr; 169 int error; 170 171 bzero(&phdr, sizeof(phdr)); 172 phdr.p_type = PT_LOAD; 173 phdr.p_flags = PF_R; /* XXX */ 174 phdr.p_offset = fileofs; 175 phdr.p_vaddr = (uintptr_t)mdp->md_virt; /* XXX probably bogus. */ 176 phdr.p_paddr = mdp->md_phys; 177 phdr.p_filesz = mdp->md_pages << EFI_PAGE_SHIFT; 178 phdr.p_memsz = mdp->md_pages << EFI_PAGE_SHIFT; 179 phdr.p_align = EFI_PAGE_SIZE; 180 181 error = buf_write(di, (char*)&phdr, sizeof(phdr)); 182 fileofs += phdr.p_filesz; 183 return (error); 184} 185 186static int 187phys_cb_size(struct efi_md *mdp, int seqnr, void *arg) 188{ 189 uint64_t *sz = (uint64_t*)arg; 190 191 *sz += (uint64_t)mdp->md_pages << EFI_PAGE_SHIFT; 192 return (0); 193} 194 195static int 196phys_foreach(phys_callback_t cb, void *arg) 197{ 198 struct efi_md *mdp; 199 int error, seqnr; 200 201 seqnr = 0; 202 mdp = efi_md_first(); 203 while (mdp != NULL) { 204 if (mdp->md_type == EFI_MD_TYPE_FREE || 205 mdp->md_type == EFI_MD_TYPE_DATA || 206 mdp->md_type == EFI_MD_TYPE_CODE || 207 mdp->md_type == EFI_MD_TYPE_BS_DATA || 208 mdp->md_type == EFI_MD_TYPE_BS_CODE) { 209 error = (*cb)(mdp, seqnr++, arg); 210 if (error) 211 return (-error); 212 } 213 mdp = efi_md_next(mdp); 214 } 215 return (seqnr); 216} 217 218/* 219 * Virtual dump (aka minidump) support 220 */ 221 222typedef int virt_callback_t(vm_offset_t, vm_size_t, int, void*); 223 224static int 225virt_cb_size(vm_offset_t va, vm_size_t sz, int seqnr, void *arg) 226{ 227 uint64_t *dumpsize = (uint64_t *)arg; 228 229 *dumpsize += sz; 230 return (0); 231} 232 233static int 234virt_cb_dumphdr(vm_offset_t va, vm_size_t sz, int seqnr, void *arg) 235{ 236 struct dumperinfo *di = (struct dumperinfo *)arg; 237 Elf64_Phdr phdr; 238 int error; 239 240 bzero(&phdr, sizeof(phdr)); 241 phdr.p_type = PT_LOAD; 242 phdr.p_flags = PF_R; /* XXX */ 243 phdr.p_offset = fileofs; 244 phdr.p_vaddr = va; 245 phdr.p_paddr = ~0UL; 246 phdr.p_filesz = sz; 247 phdr.p_memsz = sz; 248 phdr.p_align = PAGE_SIZE; 249 250 error = buf_write(di, (char*)&phdr, sizeof(phdr)); 251 fileofs += phdr.p_filesz; 252 return (error); 253} 254 255static int 256virt_cb_dumpdata(vm_offset_t va, vm_size_t sz, int seqnr, void *arg) 257{ 258 struct dumperinfo *di = (struct dumperinfo *)arg; 259 size_t counter, iosz; 260 int c, error, twiddle; 261 262 error = 0; /* catch case in which pgs is 0 */ 263 counter = 0; /* Update twiddle every 16MB */ 264 twiddle = 0; 265 266 printf(" chunk %d: %ld pages ", seqnr, atop(sz)); 267 268 while (sz) { 269 iosz = (sz > DFLTPHYS) ? DFLTPHYS : sz; 270 counter += iosz; 271 if (counter >> 24) { 272 printf("%c\b", "|/-\\"[twiddle++ & 3]); 273 counter &= (1<<24) - 1; 274 } 275#ifdef SW_WATCHDOG 276 wdog_kern_pat(WD_LASTVAL); 277#endif 278 error = dump_write(di, (void*)va, 0, dumplo, iosz); 279 if (error) 280 break; 281 dumplo += iosz; 282 sz -= iosz; 283 va += iosz; 284 285 /* Check for user abort. */ 286 c = cncheckc(); 287 if (c == 0x03) 288 return (ECANCELED); 289 if (c != -1) 290 printf("(CTRL-C to abort) "); 291 } 292 printf("... %s\n", (error) ? "fail" : "ok"); 293 return (error); 294} 295 296static int 297virt_foreach(virt_callback_t cb, void *arg) 298{ 299 vm_offset_t va; 300 vm_size_t sz; 301 int error, seqnr; 302 303 seqnr = 0; 304 while (1) { 305 switch (seqnr) { 306 case 0: 307 va = IA64_PBVM_BASE; 308 sz = round_page(bootinfo->bi_kernend) - va; 309 break; 310 default: 311 va = 0; 312 sz = 0; 313 break; 314 } 315 if (va == 0 && sz == 0) 316 break; 317 error = (*cb)(va, sz, seqnr, arg); 318 if (error) 319 return (-error); 320 seqnr++; 321 } 322 return (seqnr); 323} 324 325/* 326 * main entry point. 327 */ 328 329void 330dumpsys(struct dumperinfo *di) 331{ 332 Elf64_Ehdr ehdr; 333 uint64_t dumpsize; 334 off_t hdrgap; 335 size_t hdrsz; 336 int error, status; 337 338 bzero(&ehdr, sizeof(ehdr)); 339 ehdr.e_ident[EI_MAG0] = ELFMAG0; 340 ehdr.e_ident[EI_MAG1] = ELFMAG1; 341 ehdr.e_ident[EI_MAG2] = ELFMAG2; 342 ehdr.e_ident[EI_MAG3] = ELFMAG3; 343 ehdr.e_ident[EI_CLASS] = ELFCLASS64; 344#if BYTE_ORDER == LITTLE_ENDIAN 345 ehdr.e_ident[EI_DATA] = ELFDATA2LSB; 346#else 347 ehdr.e_ident[EI_DATA] = ELFDATA2MSB; 348#endif 349 ehdr.e_ident[EI_VERSION] = EV_CURRENT; 350 ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE; /* XXX big picture? */ 351 ehdr.e_type = ET_CORE; 352 ehdr.e_machine = EM_IA_64; 353 ehdr.e_entry = (minidump) ? (uintptr_t)bootinfo : 354 ia64_tpa((uintptr_t)bootinfo); 355 ehdr.e_phoff = sizeof(ehdr); 356 ehdr.e_flags = (minidump) ? 0 : EF_IA_64_ABSOLUTE; /* XXX misuse? */ 357 ehdr.e_ehsize = sizeof(ehdr); 358 ehdr.e_phentsize = sizeof(Elf64_Phdr); 359 ehdr.e_shentsize = sizeof(Elf64_Shdr); 360 361 /* Calculate dump size. */ 362 dumpsize = 0L; 363 status = (minidump) ? virt_foreach(virt_cb_size, &dumpsize) : 364 phys_foreach(phys_cb_size, &dumpsize); 365 if (status < 0) { 366 error = -status; 367 goto fail; 368 } 369 ehdr.e_phnum = status; 370 hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize; 371 fileofs = (minidump) ? round_page(hdrsz) : MD_ALIGN(hdrsz); 372 dumpsize += fileofs; 373 hdrgap = fileofs - DEV_ALIGN(hdrsz); 374 375 /* Determine dump offset on device. */ 376 if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { 377 error = ENOSPC; 378 goto fail; 379 } 380 dumplo = di->mediaoffset + di->mediasize - dumpsize; 381 dumplo -= sizeof(kdh) * 2; 382 383 mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_IA64_VERSION, dumpsize, di->blocksize); 384 385 printf("Dumping %llu MB (%d chunks)\n", (long long)dumpsize >> 20, 386 ehdr.e_phnum); 387 388 /* Dump leader */ 389 error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); 390 if (error) 391 goto fail; 392 dumplo += sizeof(kdh); 393 394 /* Dump ELF header */ 395 error = buf_write(di, (char*)&ehdr, sizeof(ehdr)); 396 if (error) 397 goto fail; 398 399 /* Dump program headers */ 400 status = (minidump) ? virt_foreach(virt_cb_dumphdr, di) : 401 phys_foreach(phys_cb_dumphdr, di); 402 if (status < 0) { 403 error = -status; 404 goto fail; 405 } 406 buf_flush(di); 407 408 /* 409 * All headers are written using blocked I/O, so we know the 410 * current offset is (still) block aligned. Skip the alignment 411 * in the file to have the segment contents aligned at page 412 * boundary. For physical dumps, it's the EFI page size (= 4K). 413 * For minidumps it's the kernel's page size (= 8K). 414 */ 415 dumplo += hdrgap; 416 417 /* Dump memory chunks (updates dumplo) */ 418 status = (minidump) ? virt_foreach(virt_cb_dumpdata, di) : 419 phys_foreach(phys_cb_dumpdata, di); 420 if (status < 0) { 421 error = -status; 422 goto fail; 423 } 424 425 /* Dump trailer */ 426 error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); 427 if (error) 428 goto fail; 429 430 /* Signal completion, signoff and exit stage left. */ 431 dump_write(di, NULL, 0, 0, 0); 432 printf("\nDump complete\n"); 433 return; 434 435 fail: 436 if (error == ECANCELED) 437 printf("\nDump aborted\n"); 438 else 439 printf("\n** DUMP FAILED (ERROR %d) **\n", error); 440} 441