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 <mdb/mdb_dump.h>
30 #include <mdb/mdb_modapi.h>
31 #include <mdb/mdb_nv.h>
32 #include <mdb/mdb_err.h>
33 #include <mdb/mdb.h>
34 #include <limits.h>
35
36 #define DUMP_PARAGRAPH 16
37 #define DUMP_WIDTH(x) (DUMP_PARAGRAPH * ((((x) >> 16) & 0xf) + 1))
38 #define DUMP_GROUP(x) ((((x) >> 20) & 0xff) + 1)
39 #define DUMP_MAXWIDTH DUMP_WIDTH(MDB_DUMP_WIDTH(0x10))
40
41 /*
42 * This is the implementation of mdb's generic hexdump facility (though
43 * not named such in case we decide to add support for other radices).
44 * While it is possible to call mdb_dump_internal directly, it is
45 * recommended that you use mdb_dumpptr or mdb_dump64 instead.
46 */
47
48
49 /*
50 * Output the header for the dump. pad is the width of the address
51 * field, and offset is the index of the byte that we want highlighted.
52 * If the output isn't MDB_DUMP_ALIGNed, we use offset to adjust the
53 * labels to reflect the true least significant address nibble.
54 */
55
56 static void
mdb_dump_header(int flags,int pad,int offset)57 mdb_dump_header(int flags, int pad, int offset)
58 {
59 int nalign = !(flags & MDB_DUMP_ALIGN);
60 int group = DUMP_GROUP(flags);
61 int width = DUMP_WIDTH(flags);
62 int i;
63
64 mdb_printf("%*s ", pad, "");
65 for (i = 0; i < width; i++) {
66 if (!(i % group))
67 mdb_printf((group == 1 && i && !(i % 8)) ? " " : " ");
68 if (i == offset && !nalign)
69 mdb_printf("\\/");
70 else
71 mdb_printf("%2x", (i + (nalign * offset)) & 0xf);
72 }
73
74 if (flags & MDB_DUMP_ASCII) {
75 mdb_printf(" ");
76 for (i = 0; i < width; i++) {
77 if (i == offset && !nalign)
78 mdb_printf("v");
79 else
80 mdb_printf("%x", (i + (nalign * offset)) & 0xf);
81 }
82 }
83
84 mdb_printf("\n");
85 }
86
87
88 /*
89 * Output a line of data. pad is as defined above. A non-zero lmargin
90 * and/or rmargin indicate a set of bytes that shouldn't be printed.
91 */
92
93 static void
mdb_dump_data(uint64_t addr,uchar_t * buf,int flags,int pad,int lmargin,int rmargin)94 mdb_dump_data(uint64_t addr, uchar_t *buf, int flags, int pad,
95 int lmargin, int rmargin)
96 {
97 uchar_t abuf[DUMP_MAXWIDTH + 1];
98 int group = DUMP_GROUP(flags);
99 int width = DUMP_WIDTH(flags);
100 int i;
101 #ifdef _LITTLE_ENDIAN
102 int flip = FALSE;
103
104 if (flags & MDB_DUMP_ENDIAN)
105 flip = TRUE;
106 #endif
107
108 mdb_printf("%0*llx: ", pad, addr);
109
110 for (i = 0; i < width; i++) {
111 if (!(i % group))
112 mdb_printf((group == 1 && i && !(i % 8)) ? " " : " ");
113 if (i < lmargin || (width - i) <= rmargin) {
114 mdb_printf(" ");
115 #ifdef _LITTLE_ENDIAN
116 } else if (flip) {
117 int j = group * ((i / group) + 1) - (i % group) - 1;
118 mdb_printf("%02x", buf[j]);
119 #endif
120 } else {
121 mdb_printf("%02x", buf[i]);
122 }
123 }
124
125 if (flags & MDB_DUMP_ASCII) {
126 for (i = 0; i < width; i++)
127 if (i < lmargin || (width - i) <= rmargin)
128 abuf[i] = ' ';
129 else if (buf[i] < ' ' || buf[i] > '~')
130 abuf[i] = '.';
131 else
132 abuf[i] = buf[i];
133 abuf[width] = '\0';
134 mdb_printf(" %s", abuf);
135 }
136
137 mdb_printf("\n");
138 }
139
140
141 /*
142 * Given an address and a length, compute the number of characters
143 * needed to display addresses within that range.
144 */
145
146 static int
mdb_dump_pad(uint64_t addr,uint64_t len,int flags,int bytes)147 mdb_dump_pad(uint64_t addr, uint64_t len, int flags, int bytes)
148 {
149 uint64_t x;
150 int bits;
151
152 if (flags & MDB_DUMP_PEDANT) {
153 /*
154 * Assume full width pointers
155 */
156 bits = NBBY * bytes;
157 } else {
158 /*
159 * Vary width based on address and length, but first
160 * check to see if the address is relevant.
161 */
162 if (len > 1 || (addr && len == 1))
163 len--;
164 if (flags & MDB_DUMP_RELATIVE)
165 x = len;
166 else
167 x = len + addr;
168
169 bits = 0;
170 while (x) {
171 bits++;
172 x >>= 1;
173 }
174 }
175
176 return ((bits + 3) / 4);
177 }
178
179
180 /*
181 * The main dump routine, called by mdb_dump64 and (indirectly) by
182 * mdb_dumpptr. Arguments:
183 * addr - the address to start dumping at
184 * len - the amount of data to dump
185 * flags - to tune operation (see mdb_modapi.h)
186 * func - callback function used to obtain data
187 * arg - argument to pass to callback function
188 * bytes - size of pointer type
189 */
190
191 int
mdb_dump_internal(uint64_t addr,uint64_t len,int flags,mdb_dump64_cb_t func,void * arg,int bytes)192 mdb_dump_internal(uint64_t addr, uint64_t len, int flags, mdb_dump64_cb_t func,
193 void *arg, int bytes)
194 {
195 uchar_t buffers[2][DUMP_MAXWIDTH];
196 uchar_t *buf, *pbuf;
197 uint64_t i;
198 ssize_t j;
199 uint64_t addrmax;
200 uint64_t offset; /* bytes between first position and addr */
201 uint64_t reqlen = len; /* requested length */
202 int l, r; /* left and right margins */
203 int pskip; /* previous line was skipped */
204 int pvalid; /* previous line was valid (we may skip) */
205 int bread, bwanted; /* used to handle partial reads */
206 int pad, n;
207 int group, width;
208 int err = 0;
209
210 addrmax = (1LL << (bytes * NBBY - 1)) - 1 + (1LL << (bytes * NBBY - 1));
211
212 /*
213 * Ensure that len doesn't wrap around the end of addressable
214 * memory. Note that because we take an address and a length,
215 * it isn't possible to dump from 0 to UINT64_MAX if
216 * MDB_DUMP_TRIM is set.
217 */
218 if (len && (len - 1 > addrmax - addr)) {
219 len = addrmax - addr;
220 if (addr || (addrmax < UINT64_MAX))
221 len++;
222 }
223
224 /*
225 * If a) the grouping isn't a power of two, or
226 * b) the display width is not evenly divisible by the grouping
227 * we ignore the specified grouping (and default to 4).
228 */
229 group = DUMP_GROUP(flags);
230 width = DUMP_WIDTH(flags);
231 if (((group - 1) & group) || (width % group)) {
232 group = 4;
233 flags = (flags & 0xfffff) | MDB_DUMP_GROUP(group);
234 }
235
236 /*
237 * If we are reordering bytes to adjust for endianness, turn
238 * off text output, headers, and alignment to cut down on the
239 * number of special cases (and confusing output). For
240 * correctness, we will continue to observe MDB_DUMP_TRIM, but
241 * will truncate output if the specified length isn't a
242 * multiple of the grouping.
243 */
244 if (flags & MDB_DUMP_ENDIAN) {
245 flags &= ~(MDB_DUMP_ALIGN | MDB_DUMP_HEADER | MDB_DUMP_ASCII);
246 if (flags & MDB_DUMP_TRIM)
247 len -= len % group;
248 }
249
250 /*
251 * If we are interested in seeing the data indexed relative to
252 * the starting location, paragraph alignment is irrelevant.
253 * The left margin will always be 0.
254 */
255 if (flags & MDB_DUMP_RELATIVE) {
256 flags &= ~MDB_DUMP_ALIGN;
257 l = 0;
258 } else {
259 l = addr % DUMP_PARAGRAPH;
260 }
261
262 /*
263 * Compute the width of our addresses, and adjust our starting
264 * point based on the address and the state of the alignment
265 * flag.
266 */
267 pad = mdb_dump_pad(addr, len, flags, bytes);
268 if (flags & MDB_DUMP_ALIGN) {
269 len += l;
270 addr -= l;
271 offset = l;
272 } else {
273 offset = 0;
274 }
275
276 /*
277 * Display the header (if appropriate), using the left margin
278 * to determine what our column header offset should be.
279 */
280 if (flags & MDB_DUMP_HEADER)
281 mdb_dump_header(flags, pad, l);
282
283 /*
284 * If we aren't trimming and aligning the output, the left
285 * margin is now irrelevant and should be zeroed.
286 */
287 if (!(flags & MDB_DUMP_TRIM) || !(flags & MDB_DUMP_ALIGN))
288 l = 0;
289
290 /*
291 * We haven't skipped the previous line, it isn't valid to skip
292 * the current line, and we use buffer 0 first. lint doesn't
293 * realize that this implies pbuf won't be accessed until after
294 * it is set, so we explicitly initialize that here, too.
295 */
296 pskip = pvalid = FALSE;
297 pbuf = NULL;
298 n = 0;
299 r = 0;
300
301 for (i = 0; i < len && r == 0; i += width) {
302 /*
303 * Select the current buffer.
304 */
305 buf = buffers[n];
306
307 /*
308 * We have a right margin only if we are on the last
309 * line and either (1) MDB_DUMP_TRIM is set or (2) our
310 * untrimmed output would require reading past the end
311 * of addressable memory. In either case, we clear
312 * pvalid since we don't want to skip the last line.
313 */
314 if ((uint64_t)width >= len - i) {
315 pvalid = FALSE;
316 if (flags & MDB_DUMP_TRIM)
317 r = width - (len - i);
318 if ((uint64_t)width - 1 > addrmax - (addr + i)) {
319 int nr = width - (addrmax - (addr + i)) - 1;
320 r = MAX(r, nr);
321 }
322 }
323
324 /*
325 * Read data into the current buffer, obeying the left
326 * and right margins.
327 *
328 * We handle read(2)-style partial results by
329 * repeatedly calling the callback until we fill the
330 * buffer, we get a 0 (end of file), or we get a -1
331 * (error). We take care to never read the same data
332 * twice, though.
333 *
334 * mdb(1)-style partial results (i.e. EMDB_PARTIAL) are
335 * treated like any other error. If more exotic
336 * handling is desired, the caller is free to wrap
337 * their callback with an auxiliary function. See
338 * mdb_dumpptr and mdb_dump64 for examples of this.
339 */
340 bread = l;
341 bwanted = width - r;
342 while (bread < bwanted) {
343 j = func(buf + bread, bwanted - bread,
344 addr + i + bread, arg);
345 if (j <= 0) {
346 if (i + bread < offset) {
347 l++;
348 j = 1;
349 } else {
350 r += bwanted - bread;
351 pvalid = FALSE;
352 if (j == -1)
353 err = errno;
354 if (bread == l) {
355 i += width;
356 goto out;
357 }
358 break;
359 }
360 }
361 bread += j;
362 }
363
364 /*
365 * If we are eliminating repeated lines, AND it is
366 * valid to eliminate this line, AND the current line
367 * is the same as the previous line, don't print the
368 * current line. If we didn't skip the previous line,
369 * print an asterisk and set the previous-line-skipped
370 * flag.
371 *
372 * Otherwise, print the line and clear the
373 * previous-line-skipped flag.
374 */
375 if ((flags & MDB_DUMP_SQUISH) && pvalid &&
376 (memcmp(buf, pbuf, width) == 0)) {
377 if (!pskip) {
378 mdb_printf("*\n");
379 pskip = TRUE;
380 }
381 } else {
382 if (flags & MDB_DUMP_RELATIVE)
383 mdb_dump_data(i, buf, flags, pad, l, r);
384 else
385 mdb_dump_data(addr + i, buf, flags, pad, l, r);
386 pskip = FALSE;
387 }
388
389 /*
390 * If we have a non-zero left margin then we don't have
391 * a full buffer of data and we shouldn't try to skip
392 * the next line. It doesn't matter if the right
393 * margin is non-zero since we'll fall out of the loop.
394 */
395 if (!l)
396 pvalid = TRUE;
397
398 /*
399 * Swap buffers, and zero the left margin.
400 */
401 n = (n + 1) % 2;
402 pbuf = buf;
403 l = 0;
404 }
405
406 out:
407 /*
408 * If we successfully dumped everything, update . to be the
409 * address following that of the last byte requested.
410 */
411 if (i - r - offset >= reqlen) {
412 if (flags & MDB_DUMP_NEWDOT)
413 mdb_set_dot(addr + offset + reqlen);
414 } else if (err) {
415 errno = err;
416 mdb_warn("failed to read data at %#llx", addr + i - r);
417 return (-1);
418 }
419
420 return (0);
421 }
422