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 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 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 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 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