dump_machdep.c revision 190684
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: head/sys/powerpc/powerpc/dump_machdep.c 190684 2009-04-04 02:12:37Z marcel $"); 29 30#include <sys/param.h> 31#include <sys/systm.h> 32#include <sys/conf.h> 33#include <sys/cons.h> 34#include <sys/kernel.h> 35#include <sys/kerneldump.h> 36#include <sys/sysctl.h> 37#include <vm/vm.h> 38#include <vm/pmap.h> 39#include <machine/elf.h> 40#include <machine/md_var.h> 41 42CTASSERT(sizeof(struct kerneldumpheader) == 512); 43 44/* 45 * Don't touch the first SIZEOF_METADATA bytes on the dump device. This 46 * is to protect us from metadata and to protect metadata from us. 47 */ 48#define SIZEOF_METADATA (64*1024) 49 50#define MD_ALIGN(x) (((off_t)(x) + PAGE_MASK) & ~PAGE_MASK) 51#define DEV_ALIGN(x) (((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1)) 52 53typedef int callback_t(struct pmap_md *, int, void *); 54 55static struct kerneldumpheader kdh; 56static off_t dumplo, fileofs; 57 58/* Handle buffered writes. */ 59static char buffer[DEV_BSIZE]; 60static size_t fragsz; 61 62int dumpsys_minidump = 1; 63SYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RD, &dumpsys_minidump, 0, 64 "Kernel makes compressed crash dumps"); 65 66static int 67buf_write(struct dumperinfo *di, char *ptr, size_t sz) 68{ 69 size_t len; 70 int error; 71 72 while (sz) { 73 len = DEV_BSIZE - fragsz; 74 if (len > sz) 75 len = sz; 76 bcopy(ptr, buffer + fragsz, len); 77 fragsz += len; 78 ptr += len; 79 sz -= len; 80 if (fragsz == DEV_BSIZE) { 81 error = di->dumper(di->priv, buffer, 0, dumplo, 82 DEV_BSIZE); 83 if (error) 84 return error; 85 dumplo += DEV_BSIZE; 86 fragsz = 0; 87 } 88 } 89 90 return (0); 91} 92 93static int 94buf_flush(struct dumperinfo *di) 95{ 96 int error; 97 98 if (fragsz == 0) 99 return (0); 100 101 error = di->dumper(di->priv, buffer, 0, dumplo, DEV_BSIZE); 102 dumplo += DEV_BSIZE; 103 fragsz = 0; 104 return (error); 105} 106 107static int 108cb_dumpdata(struct pmap_md *md, int seqnr, void *arg) 109{ 110 struct dumperinfo *di = (struct dumperinfo*)arg; 111 vm_offset_t va; 112 size_t counter, ofs, resid, sz; 113 int c, error, twiddle; 114 115 error = 0; 116 counter = 0; /* Update twiddle every 16MB */ 117 twiddle = 0; 118 119 ofs = 0; /* Logical offset within the chunk */ 120 resid = md->md_size; 121 122 printf(" chunk %d: %lu bytes ", seqnr, (u_long)resid); 123 124 while (resid) { 125 sz = (resid > DFLTPHYS) ? DFLTPHYS : resid; 126 va = pmap_dumpsys_map(md, ofs, &sz); 127 counter += sz; 128 if (counter >> 24) { 129 printf("%c\b", "|/-\\"[twiddle++ & 3]); 130 counter &= (1<<24) - 1; 131 } 132 error = di->dumper(di->priv, (void*)va, 0, dumplo, sz); 133 pmap_dumpsys_unmap(md, ofs, va); 134 if (error) 135 break; 136 dumplo += sz; 137 resid -= sz; 138 ofs += sz; 139 140 /* Check for user abort. */ 141 c = cncheckc(); 142 if (c == 0x03) 143 return (ECANCELED); 144 if (c != -1) 145 printf("(CTRL-C to abort) "); 146 } 147 printf("... %s\n", (error) ? "fail" : "ok"); 148 return (error); 149} 150 151static int 152cb_dumphdr(struct pmap_md *md, int seqnr, void *arg) 153{ 154 struct dumperinfo *di = (struct dumperinfo*)arg; 155 Elf32_Phdr phdr; 156 int error; 157 158 bzero(&phdr, sizeof(phdr)); 159 phdr.p_type = PT_LOAD; 160 phdr.p_flags = PF_R; /* XXX */ 161 phdr.p_offset = fileofs; 162 phdr.p_vaddr = md->md_vaddr; 163 phdr.p_paddr = md->md_paddr; 164 phdr.p_filesz = md->md_size; 165 phdr.p_memsz = md->md_size; 166 phdr.p_align = PAGE_SIZE; 167 168 error = buf_write(di, (char*)&phdr, sizeof(phdr)); 169 fileofs += phdr.p_filesz; 170 return (error); 171} 172 173static int 174cb_size(struct pmap_md *md, int seqnr, void *arg) 175{ 176 uint32_t *sz = (uint32_t*)arg; 177 178 *sz += md->md_size; 179 return (0); 180} 181 182static int 183foreach_chunk(callback_t cb, void *arg) 184{ 185 struct pmap_md *md; 186 int error, seqnr; 187 188 seqnr = 0; 189 md = pmap_scan_md(NULL); 190 while (md != NULL) { 191 error = (*cb)(md, seqnr++, arg); 192 if (error) 193 return (-error); 194 md = pmap_scan_md(md); 195 } 196 return (seqnr); 197} 198 199void 200dumpsys(struct dumperinfo *di) 201{ 202 Elf32_Ehdr ehdr; 203 uint32_t dumpsize; 204 off_t hdrgap; 205 size_t hdrsz; 206 int error; 207 208 bzero(&ehdr, sizeof(ehdr)); 209 ehdr.e_ident[EI_MAG0] = ELFMAG0; 210 ehdr.e_ident[EI_MAG1] = ELFMAG1; 211 ehdr.e_ident[EI_MAG2] = ELFMAG2; 212 ehdr.e_ident[EI_MAG3] = ELFMAG3; 213 ehdr.e_ident[EI_CLASS] = ELFCLASS32; 214#if BYTE_ORDER == LITTLE_ENDIAN 215 ehdr.e_ident[EI_DATA] = ELFDATA2LSB; 216#else 217 ehdr.e_ident[EI_DATA] = ELFDATA2MSB; 218#endif 219 ehdr.e_ident[EI_VERSION] = EV_CURRENT; 220 ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE; /* XXX big picture? */ 221 ehdr.e_type = ET_CORE; 222 ehdr.e_machine = EM_PPC; 223 ehdr.e_phoff = sizeof(ehdr); 224 ehdr.e_ehsize = sizeof(ehdr); 225 ehdr.e_phentsize = sizeof(Elf32_Phdr); 226 ehdr.e_shentsize = sizeof(Elf32_Shdr); 227 228 /* Calculate dump size. */ 229 dumpsize = 0L; 230 ehdr.e_phnum = foreach_chunk(cb_size, &dumpsize); 231 hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize; 232 fileofs = MD_ALIGN(hdrsz); 233 dumpsize += fileofs; 234 hdrgap = fileofs - DEV_ALIGN(hdrsz); 235 236 /* For block devices, determine the dump offset on the device. */ 237 if (di->mediasize > 0) { 238 if (di->mediasize < 239 SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { 240 error = ENOSPC; 241 goto fail; 242 } 243 dumplo = di->mediaoffset + di->mediasize - dumpsize; 244 dumplo -= sizeof(kdh) * 2; 245 } else 246 dumplo = 0; 247 248 mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_POWERPC_VERSION, dumpsize, 249 di->blocksize); 250 251 printf("Dumping %u MB (%d chunks)\n", dumpsize >> 20, 252 ehdr.e_phnum); 253 254 /* Dump leader */ 255 error = di->dumper(di->priv, &kdh, 0, dumplo, sizeof(kdh)); 256 if (error) 257 goto fail; 258 dumplo += sizeof(kdh); 259 260 /* Dump ELF header */ 261 error = buf_write(di, (char*)&ehdr, sizeof(ehdr)); 262 if (error) 263 goto fail; 264 265 /* Dump program headers */ 266 error = foreach_chunk(cb_dumphdr, di); 267 if (error < 0) 268 goto fail; 269 buf_flush(di); 270 271 /* 272 * All headers are written using blocked I/O, so we know the 273 * current offset is (still) block aligned. Skip the alignement 274 * in the file to have the segment contents aligned at page 275 * boundary. We cannot use MD_ALIGN on dumplo, because we don't 276 * care and may very well be unaligned within the dump device. 277 */ 278 dumplo += hdrgap; 279 280 /* Dump memory chunks (updates dumplo) */ 281 error = foreach_chunk(cb_dumpdata, di); 282 if (error < 0) 283 goto fail; 284 285 /* Dump trailer */ 286 error = di->dumper(di->priv, &kdh, 0, dumplo, sizeof(kdh)); 287 if (error) 288 goto fail; 289 290 /* Signal completion, signoff and exit stage left. */ 291 di->dumper(di->priv, NULL, 0, 0, 0); 292 printf("\nDump complete\n"); 293 return; 294 295 fail: 296 if (error < 0) 297 error = -error; 298 299 if (error == ECANCELED) 300 printf("\nDump aborted\n"); 301 else 302 printf("\n** DUMP FAILED (ERROR %d) **\n", error); 303} 304