1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <unistd.h> 30 #include <stdlib.h> 31 #include <stdio.h> 32 #include <libintl.h> 33 #include <unistd.h> 34 #include <string.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <kvm.h> 38 #include <fcntl.h> 39 #include <sys/mman.h> 40 #include <sys/tnf.h> 41 #include <sys/tnf_com.h> 42 #include <nlist.h> 43 #include <errno.h> 44 45 #define TNFDEV "/dev/tnfmap" 46 47 #define MAXFWTRY 5 48 49 typedef struct { 50 boolean_t ever_read; 51 tnf_uint32_t generation; 52 tnf_uint16_t bytes_valid; 53 } BLOCK_STATUS; 54 55 static char *dumpfile; /* Dump file if extracting from crashdump */ 56 static char *namelist; /* Symbol tbl. if extracting from crashdump */ 57 static kvm_t *kvm_p; /* Handle for kvm_open, kvm_read, ... */ 58 59 static struct nlist kvm_syms[] = { 60 { "tnf_buf" }, 61 { "tnf_trace_file_size" }, 62 { NULL } 63 }; 64 65 static uintptr_t dump_bufaddr; 66 static size_t tnf_bufsize; 67 static char *program_name; 68 static int input_fd; 69 static int output_fd; 70 static tnf_file_header_t *tnf_header; 71 72 /* 73 * usage() - gives a description of the arguments, and exits 74 */ 75 76 static void 77 usage(char *argv[], const char *msg) 78 { 79 if (msg) 80 (void) fprintf(stderr, 81 gettext("%s: %s\n"), argv[0], msg); 82 83 (void) fprintf(stderr, gettext( 84 "usage: %s [-d <dumpfile> -n <symbolfile> ] " 85 "<output-filename>\n"), argv[0]); 86 exit(1); 87 } /* end usage */ 88 89 90 /* 91 * Write 'size' bytes at offset 'offset' from 'addr' 92 * to the output file. Bail out unceremoniously if anything goes wrong. 93 */ 94 static void 95 writeout(char *addr, int offset, int size) 96 97 { 98 if (lseek(output_fd, offset, SEEK_SET) < 0) { 99 perror("lseek"); 100 exit(1); 101 } 102 if (write(output_fd, addr, size) != size) { 103 perror("write"); 104 exit(1); 105 } 106 } 107 108 109 static void 110 dumpfile_init() 111 112 { 113 kvm_p = kvm_open(namelist, dumpfile, NULL, O_RDONLY, program_name); 114 if (kvm_p == NULL) { 115 /* kvm_open prints an error message */ 116 exit(1); 117 } 118 if (kvm_nlist(kvm_p, kvm_syms) != 0) { 119 (void) fprintf(stderr, gettext( 120 "Symbol lookup error in %s\n"), namelist); 121 exit(1); 122 } 123 if (kvm_read(kvm_p, kvm_syms[0].n_value, (char *) &dump_bufaddr, 124 sizeof (dump_bufaddr)) != sizeof (dump_bufaddr) || 125 kvm_read(kvm_p, kvm_syms[1].n_value, (char *) &tnf_bufsize, 126 sizeof (tnf_bufsize)) != sizeof (tnf_bufsize)) { 127 (void) fprintf(stderr, gettext( 128 "kvm_read error in %s\n"), dumpfile); 129 exit(1); 130 } 131 if (dump_bufaddr == NULL || tnf_bufsize == 0) { 132 (void) fprintf(stderr, gettext( 133 "No trace data available in the kernel.\n")); 134 exit(1); 135 } 136 } 137 138 static void 139 live_kernel_init() 140 141 { 142 tifiocstate_t tstate; 143 144 if ((input_fd = open(TNFDEV, O_RDWR)) < 0) { 145 perror(TNFDEV); 146 exit(1); 147 } 148 if (ioctl(input_fd, TIFIOCGSTATE, &tstate) < 0) { 149 perror(gettext("Error getting trace system state")); 150 exit(1); 151 } 152 if (tstate.buffer_state != TIFIOCBUF_OK) { 153 (void) fprintf(stderr, gettext( 154 "No trace data available in the kernel.\n")); 155 exit(1); 156 } 157 tnf_bufsize = tstate.buffer_size; 158 } 159 160 static void 161 read_tnf_header(char *addr) 162 163 { 164 if (dumpfile != NULL) { 165 if (kvm_read(kvm_p, dump_bufaddr, addr, 512) != 512) { 166 (void) fprintf(stderr, gettext( 167 "Error reading tnf header from dump file.\n")); 168 exit(1); 169 } 170 } else { 171 if (ioctl(input_fd, TIFIOCGHEADER, addr) != 0) { 172 perror(gettext("Error reading tnf header from kernel")); 173 exit(1); 174 } 175 } 176 } 177 178 static int 179 read_tnf_block(tnf_block_header_t *addr, int block_num) 180 181 { 182 int offset; 183 tifiocgblock_t ioctl_arg; 184 185 if (dumpfile != NULL) { 186 offset = tnf_header->directory_size + 187 block_num * tnf_header->block_size; 188 if (kvm_read(kvm_p, dump_bufaddr + offset, (char *) addr, 189 tnf_header->block_size) != tnf_header->block_size) { 190 (void) fprintf(stderr, gettext( 191 "Error reading tnf block.\n")); 192 exit(1); 193 } 194 } else { 195 ioctl_arg.dst_addr = (char *) addr; 196 ioctl_arg.block_num = block_num; 197 if (ioctl(input_fd, TIFIOCGBLOCK, &ioctl_arg) < 0) { 198 if (errno == EBUSY) 199 return (EBUSY); 200 perror(gettext("Error reading tnf block")); 201 exit(1); 202 } 203 } 204 return (0); 205 } 206 207 static void 208 read_tnf_fwzone(tnf_ref32_t *dest, int start, int slots) 209 210 { 211 int offset; 212 int len; 213 tifiocgfw_t ioctl_arg; 214 215 if (dumpfile != NULL) { 216 /* LINTED assignment of 64-bit integer to 32-bit integer */ 217 offset = tnf_header->block_size + start * sizeof (tnf_ref32_t); 218 /* LINTED assignment of 64-bit integer to 32-bit integer */ 219 len = slots * sizeof (tnf_ref32_t); 220 if (kvm_read(kvm_p, dump_bufaddr + offset, (char *) dest, 221 len) != len) { 222 (void) fprintf(stderr, gettext( 223 "Error reading tnf forwarding zone.\n")); 224 exit(1); 225 } 226 } else { 227 /* LINTED pointer cast may result in improper alignment */ 228 ioctl_arg.dst_addr = (long *) dest; 229 ioctl_arg.start = start; 230 ioctl_arg.slots = slots; 231 if (ioctl(input_fd, TIFIOCGFWZONE, &ioctl_arg) < 0) { 232 perror(gettext("Error reading tnf block")); 233 exit(1); 234 } 235 } 236 } 237 238 int 239 main(int argc, char *argv[]) 240 { 241 const char *optstr = "d:n:"; 242 const char *outfile; 243 char *local_buf; 244 int c; 245 tnf_uint32_t *magicp; 246 tnf_block_header_t *block_base, *blockp; 247 BLOCK_STATUS *block_stat, *bsp; 248 int block_num; 249 boolean_t any_unread, any_different, retry; 250 tnf_ref32_t *fwzone; 251 int fwzonesize; 252 int i; 253 int fwtries; 254 int block_count; 255 256 program_name = argv[0]; 257 while ((c = getopt(argc, argv, optstr)) != EOF) { 258 switch (c) { 259 case 'd': 260 dumpfile = optarg; 261 break; 262 case 'n': 263 namelist = optarg; 264 break; 265 case '?': 266 usage(argv, gettext("unrecognized argument")); 267 } 268 } 269 if (optind != argc - 1) { 270 usage(argv, gettext("too many or too few arguments")); 271 } else { 272 outfile = argv[optind]; 273 } 274 if ((dumpfile != NULL) ^ (namelist != NULL)) { 275 usage(argv, gettext("must specify both or neither of the " 276 "-d and -n options")); 277 } 278 279 output_fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0600); 280 if (output_fd < 0) { 281 perror(outfile); 282 exit(1); 283 } 284 if (dumpfile != NULL) 285 dumpfile_init(); 286 else 287 live_kernel_init(); 288 289 if ((local_buf = malloc(tnf_bufsize)) == NULL) { 290 (void) fprintf(stderr, 291 gettext("tnfxtract memory allocation failure\n")); 292 exit(1); 293 } 294 295 /* Read header, get block size, check for version mismatch */ 296 read_tnf_header(local_buf); 297 /*LINTED pointer cast may result in improper alignment*/ 298 magicp = (tnf_uint32_t *) local_buf; 299 /*LINTED pointer cast may result in improper alignment*/ 300 tnf_header = (tnf_file_header_t *)(local_buf + sizeof (*magicp)); 301 if (*magicp != TNF_MAGIC) { 302 (void) fprintf(stderr, gettext( 303 "Buffer is not in TNF format.\n")); 304 exit(1); 305 } 306 if (tnf_header->file_version != TNF_FILE_VERSION) { 307 (void) fprintf(stderr, 308 gettext("Version mismatch (tnfxtract: %d; buffer: %d)\n"), 309 TNF_FILE_VERSION, tnf_header->file_version); 310 exit(1); 311 } 312 writeout(local_buf, 0, tnf_header->block_size); 313 /* LINTED pointer cast may result in improper alignment */ 314 block_base = (tnf_block_header_t *) 315 (local_buf + tnf_header->directory_size); 316 block_count = tnf_header->block_count - 317 tnf_header->directory_size / tnf_header->block_size; 318 fwzonesize = tnf_header->directory_size - tnf_header->block_size; 319 320 block_stat = (BLOCK_STATUS *) 321 calloc(block_count, sizeof (BLOCK_STATUS)); 322 if (block_stat == NULL) { 323 (void) fprintf(stderr, 324 gettext("tnfxtract memory allocation failure\n")); 325 exit(1); 326 } 327 328 for (bsp = block_stat; bsp != block_stat + block_count; ++bsp) 329 bsp->ever_read = B_FALSE; 330 /* 331 * Make repeated passes until we've read every non-tag block. 332 */ 333 do { 334 any_unread = B_FALSE; 335 bsp = block_stat; 336 block_num = 0; 337 blockp = block_base; 338 while (block_num != block_count) { 339 if (!bsp->ever_read) { 340 if (read_tnf_block(blockp, block_num) != 0) 341 any_unread = B_TRUE; 342 else { 343 bsp->ever_read = B_TRUE; 344 bsp->generation = blockp->generation; 345 bsp->bytes_valid = blockp->bytes_valid; 346 writeout((char *) blockp, 347 /* LINTED cast 64 to 32 bit */ 348 (int)((char *) blockp - local_buf), 349 tnf_header->block_size); 350 } 351 } 352 ++bsp; 353 ++block_num; 354 /* LINTED pointer cast may result in improper alignment */ 355 blockp = (tnf_block_header_t *) 356 ((char *) blockp + tnf_header->block_size); 357 } 358 } while (any_unread); 359 360 /* 361 * Then read tag blocks only, until we have two consecutive, 362 * consistent reads. 363 */ 364 do { 365 any_different = B_FALSE; 366 bsp = block_stat; 367 block_num = 0; 368 blockp = block_base; 369 while (block_num != block_count) { 370 if (read_tnf_block(blockp, block_num) == 0 && 371 blockp->generation == TNF_TAG_GENERATION_NUM && 372 (bsp->generation != TNF_TAG_GENERATION_NUM || 373 bsp->bytes_valid != blockp->bytes_valid)) { 374 bsp->generation = TNF_TAG_GENERATION_NUM; 375 bsp->bytes_valid = blockp->bytes_valid; 376 writeout((char *) blockp, 377 /* LINTED cast 64bit to 32 bit */ 378 (int)((char *) blockp - local_buf), 379 tnf_header->block_size); 380 any_different = B_TRUE; 381 } 382 ++bsp; 383 ++block_num; 384 /* LINTED pointer cast may result in improper alignment */ 385 blockp = (tnf_block_header_t *) 386 ((char *) blockp + tnf_header->block_size); 387 } 388 } while (any_different); 389 390 /* 391 * Then read the forwarding pointers. If any are -1: 392 * sleep briefly, then make another pass. 393 */ 394 /*LINTED pointer cast may result in improper alignment*/ 395 fwzone = (tnf_ref32_t *)(local_buf + tnf_header->block_size); 396 397 read_tnf_fwzone(fwzone, 0, 398 /* LINTED cast from 64-bit integer to 32-bit integer */ 399 (int)(fwzonesize / sizeof (fwzone[0]))); 400 fwtries = 0; 401 while (fwtries != MAXFWTRY) { 402 retry = B_FALSE; 403 for (i = 0; i != fwzonesize / sizeof (fwzone[0]); ++i) { 404 if (fwzone[i] == -1) { 405 read_tnf_fwzone(&fwzone[i], i, 1); 406 if (!retry) { 407 retry = B_TRUE; 408 ++fwtries; 409 } 410 } 411 } 412 if (!retry) 413 break; 414 sleep(2); 415 } 416 if (fwtries == MAXFWTRY) { 417 (void) fprintf(stderr, gettext( 418 "Warning: forwarding pointers may " 419 "be invalid.\n")); 420 } 421 writeout((char *) fwzone, tnf_header->block_size, fwzonesize); 422 return (0); 423 }