xref: /illumos-gate/usr/src/cmd/mdb/intel/modules/sata/sata.c (revision f5f2d263454d943a366844932bdb677530ba733b)
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