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 <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 42 CTASSERT(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 53 typedef int callback_t(struct pmap_md *, int, void *); 54 55 static struct kerneldumpheader kdh; 56 static off_t dumplo, fileofs; 57 58 /* Handle buffered writes. */ 59 static char buffer[DEV_BSIZE]; 60 static size_t fragsz; 61 62 int dumpsys_minidump = 1; 63 SYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RD, &dumpsys_minidump, 0, 64 "Kernel makes compressed crash dumps"); 65 66 static int 67 buf_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 93 static int 94 buf_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 107 static int 108 cb_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 151 static int 152 cb_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 173 static int 174 cb_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 182 static int 183 foreach_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 199 void 200 dumpsys(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