/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define TNFDEV "/dev/tnfmap" #define MAXFWTRY 5 typedef struct { boolean_t ever_read; tnf_uint32_t generation; tnf_uint16_t bytes_valid; } BLOCK_STATUS; static char *dumpfile; /* Dump file if extracting from crashdump */ static char *namelist; /* Symbol tbl. if extracting from crashdump */ static kvm_t *kvm_p; /* Handle for kvm_open, kvm_read, ... */ static struct nlist kvm_syms[] = { { "tnf_buf" }, { "tnf_trace_file_size" }, { NULL } }; static uintptr_t dump_bufaddr; static size_t tnf_bufsize; static char *program_name; static int input_fd; static int output_fd; static tnf_file_header_t *tnf_header; /* * usage() - gives a description of the arguments, and exits */ static void usage(char *argv[], const char *msg) { if (msg) (void) fprintf(stderr, gettext("%s: %s\n"), argv[0], msg); (void) fprintf(stderr, gettext( "usage: %s [-d -n ] " "\n"), argv[0]); exit(1); } /* end usage */ /* * Write 'size' bytes at offset 'offset' from 'addr' * to the output file. Bail out unceremoniously if anything goes wrong. */ static void writeout(char *addr, int offset, int size) { if (lseek(output_fd, offset, SEEK_SET) < 0) { perror("lseek"); exit(1); } if (write(output_fd, addr, size) != size) { perror("write"); exit(1); } } static void dumpfile_init() { kvm_p = kvm_open(namelist, dumpfile, NULL, O_RDONLY, program_name); if (kvm_p == NULL) { /* kvm_open prints an error message */ exit(1); } if (kvm_nlist(kvm_p, kvm_syms) != 0) { (void) fprintf(stderr, gettext( "Symbol lookup error in %s\n"), namelist); exit(1); } if (kvm_read(kvm_p, kvm_syms[0].n_value, (char *) &dump_bufaddr, sizeof (dump_bufaddr)) != sizeof (dump_bufaddr) || kvm_read(kvm_p, kvm_syms[1].n_value, (char *) &tnf_bufsize, sizeof (tnf_bufsize)) != sizeof (tnf_bufsize)) { (void) fprintf(stderr, gettext( "kvm_read error in %s\n"), dumpfile); exit(1); } if (dump_bufaddr == NULL || tnf_bufsize == 0) { (void) fprintf(stderr, gettext( "No trace data available in the kernel.\n")); exit(1); } } static void live_kernel_init() { tifiocstate_t tstate; if ((input_fd = open(TNFDEV, O_RDWR)) < 0) { perror(TNFDEV); exit(1); } if (ioctl(input_fd, TIFIOCGSTATE, &tstate) < 0) { perror(gettext("Error getting trace system state")); exit(1); } if (tstate.buffer_state != TIFIOCBUF_OK) { (void) fprintf(stderr, gettext( "No trace data available in the kernel.\n")); exit(1); } tnf_bufsize = tstate.buffer_size; } static void read_tnf_header(char *addr) { if (dumpfile != NULL) { if (kvm_read(kvm_p, dump_bufaddr, addr, 512) != 512) { (void) fprintf(stderr, gettext( "Error reading tnf header from dump file.\n")); exit(1); } } else { if (ioctl(input_fd, TIFIOCGHEADER, addr) != 0) { perror(gettext("Error reading tnf header from kernel")); exit(1); } } } static int read_tnf_block(tnf_block_header_t *addr, int block_num) { int offset; tifiocgblock_t ioctl_arg; if (dumpfile != NULL) { offset = tnf_header->directory_size + block_num * tnf_header->block_size; if (kvm_read(kvm_p, dump_bufaddr + offset, (char *) addr, tnf_header->block_size) != tnf_header->block_size) { (void) fprintf(stderr, gettext( "Error reading tnf block.\n")); exit(1); } } else { ioctl_arg.dst_addr = (char *) addr; ioctl_arg.block_num = block_num; if (ioctl(input_fd, TIFIOCGBLOCK, &ioctl_arg) < 0) { if (errno == EBUSY) return (EBUSY); perror(gettext("Error reading tnf block")); exit(1); } } return (0); } static void read_tnf_fwzone(tnf_ref32_t *dest, int start, int slots) { int offset; int len; tifiocgfw_t ioctl_arg; if (dumpfile != NULL) { /* LINTED assignment of 64-bit integer to 32-bit integer */ offset = tnf_header->block_size + start * sizeof (tnf_ref32_t); /* LINTED assignment of 64-bit integer to 32-bit integer */ len = slots * sizeof (tnf_ref32_t); if (kvm_read(kvm_p, dump_bufaddr + offset, (char *) dest, len) != len) { (void) fprintf(stderr, gettext( "Error reading tnf forwarding zone.\n")); exit(1); } } else { /* LINTED pointer cast may result in improper alignment */ ioctl_arg.dst_addr = (long *) dest; ioctl_arg.start = start; ioctl_arg.slots = slots; if (ioctl(input_fd, TIFIOCGFWZONE, &ioctl_arg) < 0) { perror(gettext("Error reading tnf block")); exit(1); } } } int main(int argc, char *argv[]) { const char *optstr = "d:n:"; const char *outfile; char *local_buf; int c; tnf_uint32_t *magicp; tnf_block_header_t *block_base, *blockp; BLOCK_STATUS *block_stat, *bsp; int block_num; boolean_t any_unread, any_different, retry; tnf_ref32_t *fwzone; int fwzonesize; int i; int fwtries; int block_count; program_name = argv[0]; while ((c = getopt(argc, argv, optstr)) != EOF) { switch (c) { case 'd': dumpfile = optarg; break; case 'n': namelist = optarg; break; case '?': usage(argv, gettext("unrecognized argument")); } } if (optind != argc - 1) { usage(argv, gettext("too many or too few arguments")); } else { outfile = argv[optind]; } if ((dumpfile != NULL) ^ (namelist != NULL)) { usage(argv, gettext("must specify both or neither of the " "-d and -n options")); } output_fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (output_fd < 0) { perror(outfile); exit(1); } if (dumpfile != NULL) dumpfile_init(); else live_kernel_init(); if ((local_buf = malloc(tnf_bufsize)) == NULL) { (void) fprintf(stderr, gettext("tnfxtract memory allocation failure\n")); exit(1); } /* Read header, get block size, check for version mismatch */ read_tnf_header(local_buf); /*LINTED pointer cast may result in improper alignment*/ magicp = (tnf_uint32_t *) local_buf; /*LINTED pointer cast may result in improper alignment*/ tnf_header = (tnf_file_header_t *)(local_buf + sizeof (*magicp)); if (*magicp != TNF_MAGIC) { (void) fprintf(stderr, gettext( "Buffer is not in TNF format.\n")); exit(1); } if (tnf_header->file_version != TNF_FILE_VERSION) { (void) fprintf(stderr, gettext("Version mismatch (tnfxtract: %d; buffer: %d)\n"), TNF_FILE_VERSION, tnf_header->file_version); exit(1); } writeout(local_buf, 0, tnf_header->block_size); /* LINTED pointer cast may result in improper alignment */ block_base = (tnf_block_header_t *) (local_buf + tnf_header->directory_size); block_count = tnf_header->block_count - tnf_header->directory_size / tnf_header->block_size; fwzonesize = tnf_header->directory_size - tnf_header->block_size; block_stat = (BLOCK_STATUS *) calloc(block_count, sizeof (BLOCK_STATUS)); if (block_stat == NULL) { (void) fprintf(stderr, gettext("tnfxtract memory allocation failure\n")); exit(1); } for (bsp = block_stat; bsp != block_stat + block_count; ++bsp) bsp->ever_read = B_FALSE; /* * Make repeated passes until we've read every non-tag block. */ do { any_unread = B_FALSE; bsp = block_stat; block_num = 0; blockp = block_base; while (block_num != block_count) { if (!bsp->ever_read) { if (read_tnf_block(blockp, block_num) != 0) any_unread = B_TRUE; else { bsp->ever_read = B_TRUE; bsp->generation = blockp->generation; bsp->bytes_valid = blockp->bytes_valid; writeout((char *) blockp, /* LINTED cast 64 to 32 bit */ (int)((char *) blockp - local_buf), tnf_header->block_size); } } ++bsp; ++block_num; /* LINTED pointer cast may result in improper alignment */ blockp = (tnf_block_header_t *) ((char *) blockp + tnf_header->block_size); } } while (any_unread); /* * Then read tag blocks only, until we have two consecutive, * consistent reads. */ do { any_different = B_FALSE; bsp = block_stat; block_num = 0; blockp = block_base; while (block_num != block_count) { if (read_tnf_block(blockp, block_num) == 0 && blockp->generation == TNF_TAG_GENERATION_NUM && (bsp->generation != TNF_TAG_GENERATION_NUM || bsp->bytes_valid != blockp->bytes_valid)) { bsp->generation = TNF_TAG_GENERATION_NUM; bsp->bytes_valid = blockp->bytes_valid; writeout((char *) blockp, /* LINTED cast 64bit to 32 bit */ (int)((char *) blockp - local_buf), tnf_header->block_size); any_different = B_TRUE; } ++bsp; ++block_num; /* LINTED pointer cast may result in improper alignment */ blockp = (tnf_block_header_t *) ((char *) blockp + tnf_header->block_size); } } while (any_different); /* * Then read the forwarding pointers. If any are -1: * sleep briefly, then make another pass. */ /*LINTED pointer cast may result in improper alignment*/ fwzone = (tnf_ref32_t *)(local_buf + tnf_header->block_size); read_tnf_fwzone(fwzone, 0, /* LINTED cast from 64-bit integer to 32-bit integer */ (int)(fwzonesize / sizeof (fwzone[0]))); fwtries = 0; while (fwtries != MAXFWTRY) { retry = B_FALSE; for (i = 0; i != fwzonesize / sizeof (fwzone[0]); ++i) { if (fwzone[i] == -1) { read_tnf_fwzone(&fwzone[i], i, 1); if (!retry) { retry = B_TRUE; ++fwtries; } } } if (!retry) break; sleep(2); } if (fwtries == MAXFWTRY) { (void) fprintf(stderr, gettext( "Warning: forwarding pointers may " "be invalid.\n")); } writeout((char *) fwzone, tnf_header->block_size, fwzonesize); return (0); }