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