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