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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 #include <sys/mdb_modapi.h> 28 #include <mdb/mdb_ks.h> 29 #include <sys/modctl.h> 30 #include <note.h> 31 #include <sys/ddi_impldefs.h> 32 #include <sys/ddidmareq.h> 33 #include <sys/devops.h> 34 #include <time.h> 35 #include <sys/varargs.h> 36 #include <sys/sata/sata_hba.h> 37 38 /* 39 * SATA trace debug walker/dcmd code 40 */ 41 42 /* 43 * Initialize the sata_trace_dmsg_t walker by either using the given starting 44 * address, or reading the value of the kernel's sata_debug_rbuf pointer. 45 * We also allocate a sata_trace_dmsg_t for storage, and save this using the 46 * walk_data pointer. 47 */ 48 static int 49 sata_dmsg_walk_i(mdb_walk_state_t *wsp) 50 { 51 uintptr_t rbuf_addr; 52 sata_trace_rbuf_t rbuf; 53 54 if (wsp->walk_addr == NULL) { 55 if (mdb_readvar(&rbuf_addr, "sata_debug_rbuf") == -1) { 56 mdb_warn("failed to read 'sata_debug_rbuf'"); 57 return (WALK_ERR); 58 } 59 60 if (mdb_vread(&rbuf, sizeof (sata_trace_rbuf_t), rbuf_addr) 61 == -1) { 62 mdb_warn("failed to read sata_trace_rbuf_t at %p", 63 rbuf_addr); 64 return (WALK_ERR); 65 } 66 67 wsp->walk_addr = (uintptr_t)(sata_trace_dmsg_t *)rbuf.dmsgh; 68 } 69 70 /* 71 * Save ptr to head of ring buffer to prevent looping. 72 */ 73 wsp->walk_arg = (void *)wsp->walk_addr; 74 wsp->walk_data = mdb_alloc(sizeof (sata_trace_dmsg_t), UM_SLEEP); 75 return (WALK_NEXT); 76 } 77 78 /* 79 * At each step, read a sata_trace_dmsg_t into our private storage, and then 80 * invoke the callback function. We terminate when we reach a NULL next 81 * pointer. 82 */ 83 static int 84 sata_dmsg_walk_s(mdb_walk_state_t *wsp) 85 { 86 int status; 87 88 if (wsp->walk_addr == NULL) 89 return (WALK_DONE); 90 91 if (mdb_vread(wsp->walk_data, sizeof (sata_trace_dmsg_t), 92 wsp->walk_addr) == -1) { 93 mdb_warn("failed to read sata_trace_dmsg_t at %p", 94 wsp->walk_addr); 95 return (WALK_ERR); 96 } 97 98 status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data, 99 wsp->walk_cbdata); 100 101 wsp->walk_addr = 102 (uintptr_t)(((sata_trace_dmsg_t *)wsp->walk_data)->next); 103 104 /* 105 * If we've looped then we're done. 106 */ 107 if (wsp->walk_addr == (uintptr_t)wsp->walk_arg) 108 wsp->walk_addr = NULL; 109 110 return (status); 111 } 112 113 /* 114 * The walker's fini function is invoked at the end of each walk. Since we 115 * dynamically allocated a sata_trace_dmsg_t in sata_dmsg_walk_i, we must 116 * free it now. 117 */ 118 static void 119 sata_dmsg_walk_f(mdb_walk_state_t *wsp) 120 { 121 mdb_free(wsp->walk_data, sizeof (sata_trace_dmsg_t)); 122 } 123 124 /* 125 * This routine is used by the sata_dmsg_dump dcmd to dump content of 126 * SATA trace ring buffer. 127 */ 128 int 129 sata_dmsg_dump(sata_trace_dmsg_t *addr, int print_pathname, uint_t *printed) 130 { 131 sata_trace_dmsg_t dmsg, *dmsgh = addr; 132 struct dev_info dev; 133 char drivername[MODMAXNAMELEN]; 134 char pathname[MAXPATHLEN]; 135 char merge[1024]; 136 137 while (addr != NULL) { 138 if (mdb_vread(&dmsg, sizeof (dmsg), (uintptr_t)addr) != 139 sizeof (dmsg)) { 140 mdb_warn("failed to read message pointer in kernel"); 141 return (DCMD_ERR); 142 } 143 144 if (dmsg.dip != NULL) { 145 if ((mdb_vread(&dev, sizeof (struct dev_info), 146 (uintptr_t)dmsg.dip)) == -1) { 147 (void) mdb_snprintf(merge, sizeof (merge), 148 "[%Y:%03d:%03d:%03d] : %s", 149 dmsg.timestamp.tv_sec, 150 (int)dmsg.timestamp.tv_nsec/1000000, 151 (int)(dmsg.timestamp.tv_nsec/1000)%1000, 152 (int)dmsg.timestamp.tv_nsec%1000, 153 dmsg.buf); 154 } else { 155 (void) mdb_devinfo2driver((uintptr_t)dmsg.dip, 156 drivername, sizeof (drivername)); 157 (void) mdb_snprintf(merge, sizeof (merge), 158 "[%Y:%03d:%03d:%03d] %s%d: %s", 159 dmsg.timestamp.tv_sec, 160 (int)dmsg.timestamp.tv_nsec/1000000, 161 (int)(dmsg.timestamp.tv_nsec/1000)%1000, 162 (int)dmsg.timestamp.tv_nsec%1000, 163 drivername, 164 dev.devi_instance, 165 dmsg.buf); 166 167 if (print_pathname == TRUE) { 168 (void) mdb_ddi_pathname( 169 (uintptr_t)dmsg.dip, pathname, 170 sizeof (pathname)); 171 mdb_printf("\n[%s]", pathname); 172 } 173 } 174 } else { 175 (void) mdb_snprintf(merge, sizeof (merge), 176 "[%Y:%03d:%03d:%03d] : %s", 177 dmsg.timestamp.tv_sec, 178 (int)dmsg.timestamp.tv_nsec/1000000, 179 (int)(dmsg.timestamp.tv_nsec/1000)%1000, 180 (int)dmsg.timestamp.tv_nsec%1000, 181 dmsg.buf); 182 } 183 184 mdb_printf("%s", merge); 185 186 if (printed != NULL) { 187 (*printed)++; 188 } 189 190 if (((addr = dmsg.next) == NULL) || (dmsg.next == dmsgh)) { 191 break; 192 } 193 } 194 195 return (DCMD_OK); 196 } 197 198 /* 199 * 1. Process flag passed to sata_dmsg_dump dcmd. 200 * 2. Obtain SATA trace ring buffer pointer. 201 * 3. Pass SATA trace ring buffer pointer to sata_dmsg_dump() 202 * to dump content of SATA trace ring buffer. 203 */ 204 int 205 sata_rbuf_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 206 { 207 sata_trace_rbuf_t rbuf; 208 uint_t printed = 0; /* have we printed anything? */ 209 int print_pathname = FALSE; 210 int rval = DCMD_OK; 211 212 if (argc > 1) { 213 return (DCMD_USAGE); 214 } 215 216 if (mdb_getopts(argc, argv, 217 'a', MDB_OPT_SETBITS, TRUE, &print_pathname) != argc) { 218 return (DCMD_USAGE); 219 } 220 221 /* 222 * If ring buffer address not provided try to obtain 223 * it using sata_debug_rbuf global. 224 */ 225 if ((addr == NULL) || !(flags & DCMD_ADDRSPEC)) { 226 if (mdb_readvar(&addr, "sata_debug_rbuf") == -1) { 227 mdb_warn("Failed to read 'sata_debug_rbuf'."); 228 return (DCMD_ERR); 229 } 230 } 231 232 if (mdb_vread(&rbuf, sizeof (rbuf), addr) != sizeof (rbuf)) { 233 mdb_warn("Failed to read ring buffer in kernel."); 234 return (DCMD_ERR); 235 } 236 237 if (rbuf.dmsgh == NULL) { 238 mdb_printf("The sata trace ring buffer is empty.\n"); 239 return (DCMD_OK); 240 } 241 242 rval = sata_dmsg_dump((sata_trace_dmsg_t *)rbuf.dmsgh, 243 print_pathname, &printed); 244 245 if (rval != DCMD_OK) { 246 return (rval); 247 } 248 249 if (printed == 0) { 250 mdb_warn("Failed to read sata trace ring buffer."); 251 return (DCMD_ERR); 252 } 253 254 return (rval); 255 } 256 257 /* 258 * MDB module linkage information: 259 * 260 * We declare a list of structures describing our dcmds, a list of structures 261 * describing our walkers, and a function named _mdb_init to return a pointer 262 * to our module information. 263 */ 264 265 static const mdb_dcmd_t dcmds[] = { 266 { "sata_dmsg_dump", "[-a]", "Dump sata trace debug messages", 267 sata_rbuf_dump }, 268 { NULL } 269 }; 270 271 static const mdb_walker_t walkers[] = { 272 { "sata_dmsg", 273 "walk ring buffer containing sata trace debug messages", 274 sata_dmsg_walk_i, sata_dmsg_walk_s, sata_dmsg_walk_f }, 275 { NULL } 276 }; 277 278 static const mdb_modinfo_t modinfo = { 279 MDB_API_VERSION, dcmds, walkers 280 }; 281 282 const mdb_modinfo_t * 283 _mdb_init(void) 284 { 285 return (&modinfo); 286 } 287