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 /* 30 * Generic memory walker, used by both the genunix and libumem dmods. 31 */ 32 33 #include <mdb/mdb_modapi.h> 34 #include <sys/sysmacros.h> 35 36 #include "kgrep.h" 37 38 #define KGREP_FULL_MASK (~(uintmax_t)0) 39 40 typedef struct kgrep_data { 41 uintmax_t kg_pattern; 42 uintmax_t kg_mask; /* fancy only */ 43 uintmax_t kg_dist; /* fancy only */ 44 uintptr_t kg_minaddr; /* fancy only */ 45 uintptr_t kg_maxaddr; /* fancy only */ 46 void *kg_page; 47 size_t kg_pagesize; 48 char kg_cbtype; 49 char kg_seen; 50 } kgrep_data_t; 51 52 #define KG_BASE 0 53 #define KG_VERBOSE 1 54 #define KG_PIPE 2 55 56 static void 57 kgrep_cb(uintptr_t addr, uintmax_t *val, int type) 58 { 59 switch (type) { 60 case KG_BASE: 61 default: 62 mdb_printf("%p\n", addr); 63 break; 64 case KG_VERBOSE: 65 mdb_printf("%p:\t%llx\n", addr, *val); 66 break; 67 case KG_PIPE: 68 mdb_printf("%#lr\n", addr); 69 break; 70 } 71 } 72 73 static int 74 kgrep_range_basic(uintptr_t base, uintptr_t lim, void *kg_arg) 75 { 76 kgrep_data_t *kg = kg_arg; 77 size_t pagesize = kg->kg_pagesize; 78 uintptr_t pattern = kg->kg_pattern; 79 uintptr_t *page = kg->kg_page; 80 uintptr_t *page_end = &page[pagesize / sizeof (uintptr_t)]; 81 uintptr_t *pos; 82 83 uintptr_t addr, offset; 84 int seen = 0; 85 86 /* 87 * page-align everything, to simplify the loop 88 */ 89 base = P2ALIGN(base, pagesize); 90 lim = P2ROUNDUP(lim, pagesize); 91 92 for (addr = base; addr < lim; addr += pagesize) { 93 if (mdb_vread(page, pagesize, addr) == -1) 94 continue; 95 seen = 1; 96 97 for (pos = page; pos < page_end; pos++) { 98 if (*pos != pattern) 99 continue; 100 101 offset = (caddr_t)pos - (caddr_t)page; 102 kgrep_cb(addr + offset, NULL, kg->kg_cbtype); 103 } 104 } 105 if (seen) 106 kg->kg_seen = 1; 107 108 return (WALK_NEXT); 109 } 110 111 /* 112 * Full-service template -- instantiated for each supported size. We support 113 * the following options: 114 * 115 * addr in [minaddr, maxaddr), and 116 * value in [pattern, pattern + dist) OR 117 * mask matching: (value & mask) == (pattern & mask) 118 */ 119 #define KGREP_FANCY_TEMPLATE(kgrep_range_fancybits, uintbits_t) \ 120 static int \ 121 kgrep_range_fancybits(uintptr_t base, uintptr_t lim, void *kg_arg) \ 122 { \ 123 kgrep_data_t *kg = kg_arg; \ 124 \ 125 uintbits_t pattern = kg->kg_pattern; \ 126 uintbits_t dist = kg->kg_dist; \ 127 uintbits_t mask = kg->kg_mask; \ 128 uintptr_t minaddr = kg->kg_minaddr; \ 129 uintptr_t maxaddr = kg->kg_maxaddr; \ 130 size_t pagesize = kg->kg_pagesize; \ 131 uintbits_t *page = (uintbits_t *)kg->kg_page; \ 132 uintbits_t *page_end; \ 133 uintbits_t *pos; \ 134 uintbits_t cur; \ 135 uintmax_t out; \ 136 \ 137 uintptr_t addr, size, offset; \ 138 int seen = 0; \ 139 \ 140 base = P2ROUNDUP(MAX(base, minaddr), sizeof (uintbits_t)); \ 141 \ 142 if (maxaddr != 0 && lim > maxaddr) \ 143 lim = maxaddr; \ 144 \ 145 for (addr = base; addr < lim; addr += size) { \ 146 /* P2END(...) computes the next page boundry */ \ 147 size = MIN(lim, P2END(addr, pagesize)) - addr; \ 148 \ 149 if (mdb_vread(page, size, addr) == -1) \ 150 continue; \ 151 \ 152 seen = 1; \ 153 \ 154 page_end = &page[size / sizeof (uintbits_t)]; \ 155 for (pos = page; pos < page_end; pos++) { \ 156 cur = *pos; \ 157 \ 158 if (((cur ^ pattern) & mask) != 0 && \ 159 (cur - pattern) >= dist) \ 160 continue; \ 161 \ 162 out = cur; \ 163 offset = (caddr_t)pos - (caddr_t)page; \ 164 kgrep_cb(addr + offset, &out, kg->kg_cbtype); \ 165 } \ 166 } \ 167 if (seen) \ 168 kg->kg_seen = 1; \ 169 \ 170 return (WALK_NEXT); \ 171 } 172 173 KGREP_FANCY_TEMPLATE(kgrep_range_fancy8, uint8_t) 174 KGREP_FANCY_TEMPLATE(kgrep_range_fancy16, uint16_t) 175 KGREP_FANCY_TEMPLATE(kgrep_range_fancy32, uint32_t) 176 KGREP_FANCY_TEMPLATE(kgrep_range_fancy64, uint64_t) 177 178 #undef KGREP_FANCY_TEMPLATE 179 180 /*ARGSUSED*/ 181 int 182 kgrep(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 183 { 184 uintmax_t pattern = mdb_get_dot(); 185 uintmax_t mask = KGREP_FULL_MASK; 186 uintmax_t invmask = 0; 187 uintmax_t dist = 0; 188 uintptr_t size = sizeof (uintptr_t); 189 uintptr_t minaddr = 0; 190 uintptr_t maxaddr = 0; 191 size_t pagesize = kgrep_subr_pagesize(); 192 int verbose = 0; 193 int ret; 194 int args = 0; 195 196 kgrep_cb_func *func; 197 kgrep_data_t kg; 198 199 uintmax_t size_mask; 200 201 if (mdb_getopts(argc, argv, 202 'a', MDB_OPT_UINTPTR, &minaddr, 203 'A', MDB_OPT_UINTPTR, &maxaddr, 204 'd', MDB_OPT_UINT64, &dist, 205 'm', MDB_OPT_UINT64, &mask, 206 'M', MDB_OPT_UINT64, &invmask, 207 's', MDB_OPT_UINTPTR, &size, 208 'v', MDB_OPT_SETBITS, B_TRUE, &verbose, NULL) != argc) 209 return (DCMD_USAGE); 210 211 if (invmask != 0) 212 args++; 213 if (mask != KGREP_FULL_MASK) 214 args++; 215 if (dist != 0) 216 args++; 217 218 if (args > 1) { 219 mdb_warn("only one of -d, -m and -M may be specified\n"); 220 return (DCMD_USAGE); 221 } 222 223 if (!(flags & DCMD_ADDRSPEC)) 224 return (DCMD_USAGE); 225 226 if (invmask != 0) 227 mask = ~invmask; 228 229 if (pattern & ~mask) 230 mdb_warn("warning: pattern does not match mask\n"); 231 232 if (size > sizeof (uintmax_t)) { 233 mdb_warn("sizes greater than %d not supported\n", 234 sizeof (uintmax_t)); 235 return (DCMD_ERR); 236 } 237 238 if (size == 0 || (size & (size - 1)) != 0) { 239 mdb_warn("size must be a power of 2\n"); 240 return (DCMD_ERR); 241 } 242 243 if (size == sizeof (uintmax_t)) 244 size_mask = KGREP_FULL_MASK; 245 else 246 size_mask = (1ULL << (size * NBBY)) - 1ULL; 247 248 if (pattern & ~size_mask) 249 mdb_warn("warning: pattern %llx overflows requested size " 250 "%d (max: %llx)\n", 251 pattern, size, size_mask); 252 253 if (dist > 0 && 254 ((dist & ~size_mask) || size_mask + 1 - dist < pattern)) { 255 mdb_warn("pattern %llx + distance %llx overflows size\n" 256 "%d (max: %llx)\n", pattern, dist, size, size_mask); 257 return (DCMD_ERR); 258 } 259 260 /* 261 * All arguments have now been validated. 262 */ 263 264 (void) memset(&kg, '\0', sizeof (kg)); 265 kg.kg_page = mdb_alloc(pagesize, UM_SLEEP | UM_GC); 266 kg.kg_pagesize = pagesize; 267 kg.kg_pattern = pattern; 268 kg.kg_mask = mask; 269 kg.kg_dist = dist; 270 kg.kg_minaddr = minaddr; 271 kg.kg_maxaddr = maxaddr; 272 273 if (flags & DCMD_PIPE_OUT) { 274 verbose = 0; 275 kg.kg_cbtype = KG_PIPE; 276 } else if (verbose) { 277 kg.kg_cbtype = KG_VERBOSE; 278 } else { 279 kg.kg_cbtype = KG_BASE; 280 } 281 282 /* 283 * kgrep_range_basic handles the common case (no arguments) 284 * with dispatch. 285 */ 286 if (size == sizeof (uintptr_t) && !verbose && mask == KGREP_FULL_MASK && 287 dist == 0 && minaddr == 0 && maxaddr == 0) 288 func = kgrep_range_basic; 289 else { 290 switch (size) { 291 case 1: 292 func = kgrep_range_fancy8; 293 break; 294 case 2: 295 func = kgrep_range_fancy16; 296 break; 297 case 4: 298 func = kgrep_range_fancy32; 299 break; 300 case 8: 301 func = kgrep_range_fancy64; 302 break; 303 default: 304 mdb_warn("can't happen: non-recognized kgrep size\n"); 305 return (DCMD_ERR); 306 } 307 } 308 309 /* 310 * Invoke the target, which should invoke func(start, end, &kg) for 311 * every range [start, end) of vaddrs which might have backing. 312 * Both start and end must be multiples of kgrep_subr_pagesize(). 313 */ 314 ret = kgrep_subr(func, &kg); 315 316 if (ret == DCMD_OK && !kg.kg_seen) 317 mdb_warn("warning: nothing searched\n"); 318 319 return (ret); 320 } 321