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
usage(char * argv[],const char * msg)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
writeout(char * addr,int offset,int size)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
dumpfile_init()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
live_kernel_init()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
read_tnf_header(char * addr)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
read_tnf_block(tnf_block_header_t * addr,int block_num)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
read_tnf_fwzone(tnf_ref32_t * dest,int start,int slots)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
main(int argc,char * argv[])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 }
424