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 }