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/kernel.h> 37 #include <sys/proc.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/vm_param.h> 44 #include <vm/pmap.h> 45 #include <machine/dump.h> 46 #include <machine/elf.h> 47 #include <machine/md_var.h> 48 #include <machine/pcb.h> 49 50 CTASSERT(sizeof(struct kerneldumpheader) == 512); 51 52 /* 53 * Don't touch the first SIZEOF_METADATA bytes on the dump device. This 54 * is to protect us from metadata and to protect metadata from us. 55 */ 56 #define SIZEOF_METADATA (64*1024) 57 58 #define MD_ALIGN(x) roundup2((off_t)(x), PAGE_SIZE) 59 60 off_t dumplo; 61 62 /* Handle buffered writes. */ 63 static size_t fragsz; 64 65 struct dump_pa dump_map[DUMPSYS_MD_PA_NPAIRS]; 66 67 #if !defined(__powerpc__) && !defined(__sparc__) 68 void 69 dumpsys_gen_pa_init(void) 70 { 71 int n, idx; 72 73 bzero(dump_map, sizeof(dump_map)); 74 for (n = 0; n < nitems(dump_map); n++) { 75 idx = n * 2; 76 if (dump_avail[idx] == 0 && dump_avail[idx + 1] == 0) 77 break; 78 dump_map[n].pa_start = dump_avail[idx]; 79 dump_map[n].pa_size = dump_avail[idx + 1] - dump_avail[idx]; 80 } 81 } 82 #endif 83 84 struct dump_pa * 85 dumpsys_gen_pa_next(struct dump_pa *mdp) 86 { 87 88 if (mdp == NULL) 89 return (&dump_map[0]); 90 91 mdp++; 92 if (mdp->pa_size == 0) 93 mdp = NULL; 94 return (mdp); 95 } 96 97 void 98 dumpsys_gen_wbinv_all(void) 99 { 100 101 } 102 103 void 104 dumpsys_gen_unmap_chunk(vm_paddr_t pa __unused, size_t chunk __unused, 105 void *va __unused) 106 { 107 108 } 109 110 #if !defined(__sparc__) 111 int 112 dumpsys_gen_write_aux_headers(struct dumperinfo *di) 113 { 114 115 return (0); 116 } 117 #endif 118 119 int 120 dumpsys_buf_seek(struct dumperinfo *di, size_t sz) 121 { 122 static uint8_t buf[DEV_BSIZE]; 123 size_t nbytes; 124 int error; 125 126 bzero(buf, sizeof(buf)); 127 128 while (sz > 0) { 129 nbytes = MIN(sz, sizeof(buf)); 130 131 error = dump_write(di, buf, 0, dumplo, nbytes); 132 if (error) 133 return (error); 134 dumplo += nbytes; 135 136 sz -= nbytes; 137 } 138 139 return (0); 140 } 141 142 int 143 dumpsys_buf_write(struct dumperinfo *di, char *ptr, size_t sz) 144 { 145 size_t len; 146 int error; 147 148 while (sz) { 149 len = di->blocksize - fragsz; 150 if (len > sz) 151 len = sz; 152 memcpy((char *)di->blockbuf + fragsz, ptr, len); 153 fragsz += len; 154 ptr += len; 155 sz -= len; 156 if (fragsz == di->blocksize) { 157 error = dump_write(di, di->blockbuf, 0, dumplo, 158 di->blocksize); 159 if (error) 160 return (error); 161 dumplo += di->blocksize; 162 fragsz = 0; 163 } 164 } 165 return (0); 166 } 167 168 int 169 dumpsys_buf_flush(struct dumperinfo *di) 170 { 171 int error; 172 173 if (fragsz == 0) 174 return (0); 175 176 error = dump_write(di, di->blockbuf, 0, dumplo, di->blocksize); 177 dumplo += di->blocksize; 178 fragsz = 0; 179 return (error); 180 } 181 182 CTASSERT(PAGE_SHIFT < 20); 183 #define PG2MB(pgs) ((pgs + (1 << (20 - PAGE_SHIFT)) - 1) >> (20 - PAGE_SHIFT)) 184 185 int 186 dumpsys_cb_dumpdata(struct dump_pa *mdp, int seqnr, void *arg) 187 { 188 struct dumperinfo *di = (struct dumperinfo*)arg; 189 vm_paddr_t pa; 190 void *va; 191 uint64_t pgs; 192 size_t counter, sz, chunk; 193 int c, error; 194 u_int maxdumppgs; 195 196 error = 0; /* catch case in which chunk size is 0 */ 197 counter = 0; /* Update twiddle every 16MB */ 198 va = NULL; 199 pgs = mdp->pa_size / PAGE_SIZE; 200 pa = mdp->pa_start; 201 maxdumppgs = min(di->maxiosize / PAGE_SIZE, MAXDUMPPGS); 202 if (maxdumppgs == 0) /* seatbelt */ 203 maxdumppgs = 1; 204 205 printf(" chunk %d: %juMB (%ju pages)", seqnr, (uintmax_t)PG2MB(pgs), 206 (uintmax_t)pgs); 207 208 dumpsys_wbinv_all(); 209 while (pgs) { 210 chunk = pgs; 211 if (chunk > maxdumppgs) 212 chunk = maxdumppgs; 213 sz = chunk << PAGE_SHIFT; 214 counter += sz; 215 if (counter >> 24) { 216 printf(" %ju", (uintmax_t)PG2MB(pgs)); 217 counter &= (1 << 24) - 1; 218 } 219 220 dumpsys_map_chunk(pa, chunk, &va); 221 #ifdef SW_WATCHDOG 222 wdog_kern_pat(WD_LASTVAL); 223 #endif 224 225 error = dump_write(di, va, 0, dumplo, sz); 226 dumpsys_unmap_chunk(pa, chunk, va); 227 if (error) 228 break; 229 dumplo += sz; 230 pgs -= chunk; 231 pa += sz; 232 233 /* Check for user abort. */ 234 c = cncheckc(); 235 if (c == 0x03) 236 return (ECANCELED); 237 if (c != -1) 238 printf(" (CTRL-C to abort) "); 239 } 240 printf(" ... %s\n", (error) ? "fail" : "ok"); 241 return (error); 242 } 243 244 int 245 dumpsys_foreach_chunk(dumpsys_callback_t cb, void *arg) 246 { 247 struct dump_pa *mdp; 248 int error, seqnr; 249 250 seqnr = 0; 251 mdp = dumpsys_pa_next(NULL); 252 while (mdp != NULL) { 253 error = (*cb)(mdp, seqnr++, arg); 254 if (error) 255 return (-error); 256 mdp = dumpsys_pa_next(mdp); 257 } 258 return (seqnr); 259 } 260 261 #if !defined(__sparc__) 262 static off_t fileofs; 263 264 static int 265 cb_dumphdr(struct dump_pa *mdp, int seqnr, void *arg) 266 { 267 struct dumperinfo *di = (struct dumperinfo*)arg; 268 Elf_Phdr phdr; 269 uint64_t size; 270 int error; 271 272 size = mdp->pa_size; 273 bzero(&phdr, sizeof(phdr)); 274 phdr.p_type = PT_LOAD; 275 phdr.p_flags = PF_R; /* XXX */ 276 phdr.p_offset = fileofs; 277 #ifdef __powerpc__ 278 phdr.p_vaddr = (do_minidump? mdp->pa_start : ~0L); 279 phdr.p_paddr = (do_minidump? ~0L : mdp->pa_start); 280 #else 281 phdr.p_vaddr = mdp->pa_start; 282 phdr.p_paddr = mdp->pa_start; 283 #endif 284 phdr.p_filesz = size; 285 phdr.p_memsz = size; 286 phdr.p_align = PAGE_SIZE; 287 288 error = dumpsys_buf_write(di, (char*)&phdr, sizeof(phdr)); 289 fileofs += phdr.p_filesz; 290 return (error); 291 } 292 293 static int 294 cb_size(struct dump_pa *mdp, int seqnr, void *arg) 295 { 296 uint64_t *sz; 297 298 sz = (uint64_t *)arg; 299 *sz += (uint64_t)mdp->pa_size; 300 return (0); 301 } 302 303 int 304 dumpsys_generic(struct dumperinfo *di) 305 { 306 static struct kerneldumpheader kdh; 307 Elf_Ehdr ehdr; 308 uint64_t dumpsize; 309 off_t hdrgap; 310 size_t hdrsz; 311 int error; 312 313 #ifndef __powerpc__ 314 if (do_minidump) 315 return (minidumpsys(di)); 316 #endif 317 318 bzero(&ehdr, sizeof(ehdr)); 319 ehdr.e_ident[EI_MAG0] = ELFMAG0; 320 ehdr.e_ident[EI_MAG1] = ELFMAG1; 321 ehdr.e_ident[EI_MAG2] = ELFMAG2; 322 ehdr.e_ident[EI_MAG3] = ELFMAG3; 323 ehdr.e_ident[EI_CLASS] = ELF_CLASS; 324 #if BYTE_ORDER == LITTLE_ENDIAN 325 ehdr.e_ident[EI_DATA] = ELFDATA2LSB; 326 #else 327 ehdr.e_ident[EI_DATA] = ELFDATA2MSB; 328 #endif 329 ehdr.e_ident[EI_VERSION] = EV_CURRENT; 330 ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE; /* XXX big picture? */ 331 ehdr.e_type = ET_CORE; 332 ehdr.e_machine = EM_VALUE; 333 ehdr.e_phoff = sizeof(ehdr); 334 ehdr.e_flags = 0; 335 ehdr.e_ehsize = sizeof(ehdr); 336 ehdr.e_phentsize = sizeof(Elf_Phdr); 337 ehdr.e_shentsize = sizeof(Elf_Shdr); 338 339 dumpsys_pa_init(); 340 341 /* Calculate dump size. */ 342 dumpsize = 0L; 343 ehdr.e_phnum = dumpsys_foreach_chunk(cb_size, &dumpsize) + 344 DUMPSYS_NUM_AUX_HDRS; 345 hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize; 346 fileofs = MD_ALIGN(hdrsz); 347 dumpsize += fileofs; 348 hdrgap = fileofs - roundup2((off_t)hdrsz, di->blocksize); 349 350 /* Determine dump offset on device. */ 351 if (di->mediasize < SIZEOF_METADATA + dumpsize + di->blocksize * 2 + 352 kerneldumpcrypto_dumpkeysize(di->kdc)) { 353 error = ENOSPC; 354 goto fail; 355 } 356 dumplo = di->mediaoffset + di->mediasize - dumpsize; 357 dumplo -= di->blocksize * 2; 358 dumplo -= kerneldumpcrypto_dumpkeysize(di->kdc); 359 360 /* Initialize kernel dump crypto. */ 361 error = kerneldumpcrypto_init(di->kdc); 362 if (error) 363 goto fail; 364 365 mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_ARCH_VERSION, dumpsize, 366 kerneldumpcrypto_dumpkeysize(di->kdc), di->blocksize); 367 368 printf("Dumping %ju MB (%d chunks)\n", (uintmax_t)dumpsize >> 20, 369 ehdr.e_phnum - DUMPSYS_NUM_AUX_HDRS); 370 371 /* Dump leader */ 372 error = dump_write_header(di, &kdh, 0, dumplo); 373 if (error) 374 goto fail; 375 dumplo += di->blocksize; 376 377 /* Dump key */ 378 error = dump_write_key(di, 0, dumplo); 379 if (error) 380 goto fail; 381 dumplo += kerneldumpcrypto_dumpkeysize(di->kdc); 382 383 /* Dump ELF header */ 384 error = dumpsys_buf_write(di, (char*)&ehdr, sizeof(ehdr)); 385 if (error) 386 goto fail; 387 388 /* Dump program headers */ 389 error = dumpsys_foreach_chunk(cb_dumphdr, di); 390 if (error < 0) 391 goto fail; 392 error = dumpsys_write_aux_headers(di); 393 if (error < 0) 394 goto fail; 395 dumpsys_buf_flush(di); 396 397 /* 398 * All headers are written using blocked I/O, so we know the 399 * current offset is (still) block aligned. Skip the alignement 400 * in the file to have the segment contents aligned at page 401 * boundary. We cannot use MD_ALIGN on dumplo, because we don't 402 * care and may very well be unaligned within the dump device. 403 */ 404 error = dumpsys_buf_seek(di, (size_t)hdrgap); 405 if (error) 406 goto fail; 407 408 /* Dump memory chunks (updates dumplo) */ 409 error = dumpsys_foreach_chunk(dumpsys_cb_dumpdata, di); 410 if (error < 0) 411 goto fail; 412 413 /* Dump trailer */ 414 error = dump_write_header(di, &kdh, 0, dumplo); 415 if (error) 416 goto fail; 417 dumplo += di->blocksize; 418 419 /* Signal completion, signoff and exit stage left. */ 420 dump_write(di, NULL, 0, 0, 0); 421 printf("\nDump complete\n"); 422 return (0); 423 424 fail: 425 if (error < 0) 426 error = -error; 427 428 if (error == ECANCELED) 429 printf("\nDump aborted\n"); 430 else if (error == ENOSPC) 431 printf("\nDump failed. Partition too small.\n"); 432 else 433 printf("\n** DUMP FAILED (ERROR %d) **\n", error); 434 return (error); 435 } 436 #endif 437