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