xref: /illumos-gate/usr/src/cmd/mdb/intel/modules/xhci/xhci.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2019 Joyent, Inc.
14  */
15 
16 #include <sys/mdb_modapi.h>
17 #include <sys/usb/hcd/xhci/xhci.h>
18 
19 #define	XHCI_MDB_TRB_INDENT	4
20 
21 static const char *xhci_mdb_epctx_eptypes[] = {
22 	"Not Valid",
23 	"ISOCH OUT",
24 	"BULK OUT",
25 	"INTR OUT",
26 	"CONTROL",
27 	"ISOCH IN",
28 	"BULK IN",
29 	"INTR IN"
30 };
31 
32 static const char *xhci_mdb_epctx_states[] = {
33 	"Disabled",
34 	"Running",
35 	"Halted",
36 	"Stopped",
37 	"Error",
38 	"<Unknown>",
39 	"<Unknown>",
40 	"<Unknown>"
41 };
42 
43 static const mdb_bitmask_t xhci_mdb_trb_flags[] = {
44 	{ "C", XHCI_TRB_CYCLE, XHCI_TRB_CYCLE },
45 	{ "ENT", XHCI_TRB_ENT, XHCI_TRB_ENT },
46 	{ "ISP", XHCI_TRB_ISP, XHCI_TRB_ISP },
47 	{ "NS", XHCI_TRB_NOSNOOP, XHCI_TRB_NOSNOOP },
48 	{ "CH", XHCI_TRB_CHAIN, XHCI_TRB_CHAIN },
49 	{ "IOC", XHCI_TRB_IOC, XHCI_TRB_IOC },
50 	{ "IDT", XHCI_TRB_IDT, XHCI_TRB_IDT },
51 	{ "BEI", XHCI_TRB_BEI, XHCI_TRB_BEI },
52 	{ NULL, 0, 0 }
53 };
54 
55 typedef struct xhci_mdb_walk_endpoint {
56 	xhci_device_t	xmwe_device;
57 	uint_t		xmwe_ep;
58 } xhci_mdb_walk_endpoint_t;
59 
60 static const char *
61 xhci_mdb_trb_code_to_str(int code)
62 {
63 	switch (code) {
64 	case XHCI_CODE_INVALID:
65 		return ("Invalid");
66 	case XHCI_CODE_SUCCESS:
67 		return ("Success");
68 	case XHCI_CODE_DATA_BUF:
69 		return ("Data Overrun or Underrun");
70 	case XHCI_CODE_BABBLE:
71 		return ("Babble");
72 	case XHCI_CODE_TXERR:
73 		return ("Transaction Error");
74 	case XHCI_CODE_TRB:
75 		return ("Invalid TRB");
76 	case XHCI_CODE_STALL:
77 		return ("Stall");
78 	case XHCI_CODE_RESOURCE:
79 		return ("No Resources Available");
80 	case XHCI_CODE_BANDWIDTH:
81 		return ("No Bandwidth Available");
82 	case XHCI_CODE_NO_SLOTS:
83 		return ("No Slots Available");
84 	case XHCI_CODE_STREAM_TYPE:
85 		return ("Stream Context Type Detected");
86 	case XHCI_CODE_SLOT_NOT_ON:
87 		return ("Slot disabled");
88 	case XHCI_CODE_ENDP_NOT_ON:
89 		return ("Endpoint disabled");
90 	case XHCI_CODE_SHORT_XFER:
91 		return ("Short Transfer");
92 	case XHCI_CODE_RING_UNDERRUN:
93 		return ("Isoch. Ring Underrun");
94 	case XHCI_CODE_RING_OVERRUN:
95 		return ("Isoch. Ring Overrun");
96 	case XHCI_CODE_VF_RING_FULL:
97 		return ("VF Ring Full");
98 	case XHCI_CODE_PARAMETER:
99 		return ("Invalid Context Parameter");
100 	case XHCI_CODE_BW_OVERRUN:
101 		return ("Bandwidth Overrun");
102 	case XHCI_CODE_CONTEXT_STATE:
103 		return ("Illegal Context Transition");
104 	case XHCI_CODE_NO_PING_RESP:
105 		return ("Failed to Complete Periodic Transfer");
106 	case XHCI_CODE_EV_RING_FULL:
107 		return ("Event Ring Full");
108 	case XHCI_CODE_INCOMPAT_DEV:
109 		return ("Incompatible Device");
110 	case XHCI_CODE_MISSED_SRV:
111 		return ("Missed Isoch. Service Window");
112 	case XHCI_CODE_CMD_RING_STOP:
113 		return ("Command Ring Stop");
114 	case XHCI_CODE_CMD_ABORTED:
115 		return ("Command Aborted");
116 	case XHCI_CODE_XFER_STOPPED:
117 		return ("Transfer Stopped");
118 	case XHCI_CODE_XFER_STOPINV:
119 		return ("Invalid Transfer Length");
120 	case XHCI_CODE_XFER_STOPSHORT:
121 		return ("Stopped before End of Transfer Descriptor");
122 	case XHCI_CODE_MELAT:
123 		return ("Max Exit Latency too large");
124 	case XHCI_CODE_RESERVED:
125 		return ("Reserved");
126 	case XHCI_CODE_ISOC_OVERRUN:
127 		return ("Isochronus Overrun");
128 	case XHCI_CODE_EVENT_LOST:
129 		return ("Event Lost");
130 	case XHCI_CODE_UNDEFINED:
131 		return ("Undefined Fatal Error");
132 	case XHCI_CODE_INVALID_SID:
133 		return ("Invalid Stream ID");
134 	case XHCI_CODE_SEC_BW:
135 		return ("Secondary Bandwith Allocation Failure");
136 	case XHCI_CODE_SPLITERR:
137 		return ("USB2 Split Transaction Error");
138 	default:
139 		break;
140 	}
141 
142 	if (code >= 192 && code <= 223)
143 		return ("Vendor Defined Error");
144 	if (code >= 224 && code <= 255)
145 		return ("Vendor Defined Info");
146 
147 	return ("Reserved");
148 }
149 
150 static const char *
151 xhci_mdb_trb_type_to_str(int code)
152 {
153 	/*
154 	 * The macros for the types are all already shifted over based on their
155 	 * place in the TRB, so shift there again ourselves.
156 	 */
157 	switch (code << 10) {
158 	case XHCI_TRB_TYPE_NORMAL:
159 		return ("Normal");
160 	case XHCI_TRB_TYPE_SETUP:
161 		return ("Setup");
162 	case XHCI_TRB_TYPE_DATA:
163 		return ("Data");
164 	case XHCI_TRB_TYPE_STATUS:
165 		return ("Status");
166 	case XHCI_TRB_TYPE_LINK:
167 		return ("Link");
168 	case XHCI_TRB_TYPE_EVENT:
169 		return ("Event");
170 	case XHCI_TRB_TYPE_NOOP:
171 		return ("No-Op");
172 	case XHCI_CMD_ENABLE_SLOT:
173 		return ("Enable Slot");
174 	case XHCI_CMD_DISABLE_SLOT:
175 		return ("Disable Slot");
176 	case XHCI_CMD_ADDRESS_DEVICE:
177 		return ("Address Device");
178 	case XHCI_CMD_CONFIG_EP:
179 		return ("Configure Endpoint");
180 	case XHCI_CMD_EVAL_CTX:
181 		return ("Evaluate Context");
182 	case XHCI_CMD_RESET_EP:
183 		return ("Reset Endpoint");
184 	case XHCI_CMD_STOP_EP:
185 		return ("Stop Endpoint");
186 	case XHCI_CMD_SET_TR_DEQ:
187 		return ("Set Transfer Ring Dequeue Pointer");
188 	case XHCI_CMD_RESET_DEV:
189 		return ("Reset Device");
190 	case XHCI_CMD_FEVENT:
191 		return ("Force Event");
192 	case XHCI_CMD_NEG_BW:
193 		return ("Negotiate Bandwidth");
194 	case XHCI_CMD_SET_LT:
195 		return ("Set Latency Tolerance");
196 	case XHCI_CMD_GET_BW:
197 		return ("Get Bandwidth");
198 	case XHCI_CMD_FHEADER:
199 		return ("Force Header");
200 	case XHCI_CMD_NOOP:
201 		return ("No-Op Command");
202 	case XHCI_EVT_XFER:
203 		return ("Transfer Event");
204 	case XHCI_EVT_CMD_COMPLETE:
205 		return ("Command Completion Event");
206 	case XHCI_EVT_PORT_CHANGE:
207 		return ("Port Status Change Event");
208 	case XHCI_EVT_BW_REQUEST:
209 		return ("Bandwidth Request Event");
210 	case XHCI_EVT_DOORBELL:
211 		return ("Doorbell Event");
212 	case XHCI_EVT_HOST_CTRL:
213 		return ("Host Controller Event");
214 	case XHCI_EVT_DEVICE_NOTIFY:
215 		return ("Device Notification Event");
216 	case XHCI_EVT_MFINDEX_WRAP:
217 		return ("MFINDEX Wrap Event");
218 	default:
219 		break;
220 	}
221 
222 	if (code >= 43 && code <= 63)
223 		return ("Vendor Defiend");
224 	return ("Reserved");
225 }
226 
227 /* ARGSUSED */
228 static int
229 xhci_mdb_print_epctx(uintptr_t addr, uint_t flags, int argc,
230     const mdb_arg_t *argv)
231 {
232 	uint32_t info, info2, txinfo;
233 	xhci_endpoint_context_t epctx;
234 
235 	if (!(flags & DCMD_ADDRSPEC)) {
236 		mdb_warn("::xhci_epctx requires an address\n");
237 		return (DCMD_USAGE);
238 	}
239 
240 	if (mdb_vread(&epctx, sizeof (epctx), addr) != sizeof (epctx)) {
241 		mdb_warn("failed to read xhci_endpoint_context_t at %p", addr);
242 		return (DCMD_ERR);
243 	}
244 
245 	info = LE_32(epctx.xec_info);
246 	info2 = LE_32(epctx.xec_info2);
247 	txinfo = LE_32(epctx.xec_txinfo);
248 
249 	mdb_printf("Endpoint State: %s (%d)\n",
250 	    xhci_mdb_epctx_states[XHCI_EPCTX_STATE(info)],
251 	    XHCI_EPCTX_STATE(info));
252 
253 	mdb_printf("Mult: %d\n", XHCI_EPCTX_GET_MULT(info));
254 	mdb_printf("Max Streams: %d\n", XHCI_EPCTX_GET_MAXP_STREAMS(info));
255 	mdb_printf("LSA: %d\n", XHCI_EPCTX_GET_LSA(info));
256 	mdb_printf("Interval: %d\n", XHCI_EPCTX_GET_IVAL(info));
257 	mdb_printf("Max ESIT Hi: %d\n", XHCI_EPCTX_GET_MAX_ESIT_HI(info));
258 
259 	mdb_printf("CErr: %d\n", XHCI_EPCTX_GET_CERR(info2));
260 	mdb_printf("EP Type: %s (%d)\n",
261 	    xhci_mdb_epctx_eptypes[XHCI_EPCTX_GET_EPTYPE(info2)],
262 	    XHCI_EPCTX_GET_EPTYPE(info2));
263 	mdb_printf("Host Initiate Disable: %d\n", XHCI_EPCTX_GET_HID(info2));
264 	mdb_printf("Max Burst: %d\n", XHCI_EPCTX_GET_MAXB(info2));
265 	mdb_printf("Max Packet Size: %d\n", XHCI_EPCTX_GET_MPS(info2));
266 
267 	mdb_printf("Ring DCS: %d\n", LE_64(epctx.xec_dequeue) & 0x1);
268 	mdb_printf("Ring PA: 0x%lx\n", LE_64(epctx.xec_dequeue) & ~0xf);
269 
270 	mdb_printf("Average TRB Length: %d\n", XHCI_EPCTX_AVG_TRB_LEN(txinfo));
271 	mdb_printf("Max ESIT: %d\n", XHCI_EPCTX_GET_MAX_ESIT_PAYLOAD(txinfo));
272 
273 	return (DCMD_OK);
274 }
275 
276 /* ARGSUSED */
277 static int
278 xhci_mdb_print_slotctx(uintptr_t addr, uint_t flags, int argc,
279     const mdb_arg_t *argv)
280 {
281 	uint32_t info, info2, tt, state;
282 	xhci_slot_context_t sctx;
283 
284 	if (!(flags & DCMD_ADDRSPEC)) {
285 		mdb_warn("::xhci_slotctx requires an address\n");
286 		return (DCMD_USAGE);
287 	}
288 
289 	if (mdb_vread(&sctx, sizeof (sctx), addr) != sizeof (sctx)) {
290 		mdb_warn("failed to read xhci_slot_context_t at %p", addr);
291 		return (DCMD_ERR);
292 	}
293 
294 	info = LE_32(sctx.xsc_info);
295 	info2 = LE_32(sctx.xsc_info2);
296 	tt = LE_32(sctx.xsc_tt);
297 	state = LE_32(sctx.xsc_state);
298 
299 	mdb_printf("Route: 0x%x\n", XHCI_SCTX_GET_ROUTE(info));
300 
301 	mdb_printf("Slot Speed: ");
302 	switch (XHCI_SCTX_GET_SPEED(info)) {
303 	case XHCI_SPEED_FULL:
304 		mdb_printf("Full");
305 		break;
306 	case XHCI_SPEED_LOW:
307 		mdb_printf("Low");
308 		break;
309 	case XHCI_SPEED_HIGH:
310 		mdb_printf("High");
311 		break;
312 	case XHCI_SPEED_SUPER:
313 		mdb_printf("Super");
314 		break;
315 	default:
316 		mdb_printf("Unknown");
317 		break;
318 	}
319 	mdb_printf(" (%d)\n", XHCI_SCTX_GET_SPEED(info));
320 
321 
322 	mdb_printf("MTT: %d\n", XHCI_SCTX_GET_MTT(info));
323 	mdb_printf("HUB: %d\n", XHCI_SCTX_GET_HUB(info));
324 	mdb_printf("DCI: %d\n", XHCI_SCTX_GET_DCI(info));
325 
326 	mdb_printf("Max Exit Latency: %d\n", XHCI_SCTX_GET_MAX_EL(info2));
327 	mdb_printf("Root Hub Port: %d\n", XHCI_SCTX_GET_RHPORT(info2));
328 	mdb_printf("Hub Number of Ports: %d\n", XHCI_SCTX_GET_NPORTS(info2));
329 
330 	mdb_printf("TT Hub Slot id: %d\n", XHCI_SCTX_GET_TT_HUB_SID(tt));
331 	mdb_printf("TT Port Number: %d\n", XHCI_SCTX_GET_TT_PORT_NUM(tt));
332 	mdb_printf("TT Think Time: %d\n", XHCI_SCTX_GET_TT_THINK_TIME(tt));
333 	mdb_printf("IRQ Target: %d\n", XHCI_SCTX_GET_IRQ_TARGET(tt));
334 
335 	mdb_printf("Device Address: 0x%x\n", XHCI_SCTX_GET_DEV_ADDR(state));
336 	mdb_printf("Slot State: ");
337 	switch (XHCI_SCTX_GET_SLOT_STATE(state)) {
338 	case XHCI_SLOT_DIS_ENAB:
339 		mdb_printf("Disabled/Enabled");
340 		break;
341 	case XHCI_SLOT_DEFAULT:
342 		mdb_printf("Default");
343 		break;
344 	case XHCI_SLOT_ADDRESSED:
345 		mdb_printf("Addressed");
346 		break;
347 	case XHCI_SLOT_CONFIGURED:
348 		mdb_printf("Configured");
349 		break;
350 	default:
351 		mdb_printf("Unknown");
352 		break;
353 	}
354 	mdb_printf(" (%d)\n", XHCI_SCTX_GET_SLOT_STATE(state));
355 
356 	return (DCMD_OK);
357 }
358 
359 static int
360 xhci_mdb_print_transfer_event(uint64_t pa, uint32_t status, uint32_t flags)
361 {
362 	mdb_printf("TRB Address: 0x%lx\n", pa);
363 	mdb_printf("Transfer Length (Remain): %d\n", XHCI_TRB_REMAIN(status));
364 	mdb_printf("Completion Code: %s (%d)\n",
365 	    xhci_mdb_trb_code_to_str(XHCI_TRB_GET_CODE(status)),
366 	    XHCI_TRB_GET_CODE(status));
367 
368 	mdb_printf("Cycle: %d\n", XHCI_TRB_GET_CYCLE(flags));
369 	mdb_printf("Event Data: %d\n", XHCI_TRB_GET_ED(flags));
370 	mdb_printf("Endpoint ID: %d\n", XHCI_TRB_GET_EP(flags));
371 	mdb_printf("Slot ID: %d\n", XHCI_TRB_GET_SLOT(flags));
372 	mdb_dec_indent(XHCI_MDB_TRB_INDENT);
373 
374 	return (DCMD_OK);
375 }
376 
377 static int
378 xhci_mdb_print_command_event(uint64_t pa, uint32_t status, uint32_t flags)
379 {
380 	mdb_printf("TRB Address: 0x%lx\n", pa);
381 	mdb_printf("Command Param: 0x%x\n", XHCI_TRB_REMAIN(status));
382 	mdb_printf("Completion Code: %s (%d)\n",
383 	    xhci_mdb_trb_code_to_str(XHCI_TRB_GET_CODE(status)),
384 	    XHCI_TRB_GET_CODE(status));
385 
386 	mdb_printf("Cycle: %d\n", XHCI_TRB_GET_CYCLE(flags));
387 	/* Skip VF ID as we don't support VFs */
388 	mdb_printf("Slot ID: %d\n", XHCI_TRB_GET_SLOT(flags));
389 	mdb_dec_indent(XHCI_MDB_TRB_INDENT);
390 
391 	return (DCMD_OK);
392 }
393 
394 /* ARGSUSED */
395 static int
396 xhci_mdb_print_psc(uint64_t pa, uint32_t status, uint32_t flags)
397 {
398 	mdb_printf("Port: %d\n", XHCI_TRB_PORTID(pa));
399 	mdb_printf("Completion Code: %s (%d)\n",
400 	    xhci_mdb_trb_code_to_str(XHCI_TRB_GET_CODE(status)),
401 	    XHCI_TRB_GET_CODE(status));
402 	mdb_dec_indent(XHCI_MDB_TRB_INDENT);
403 	return (DCMD_OK);
404 }
405 
406 static int
407 xhci_mdb_print_normal_trb(uint64_t pa, uint32_t status, uint32_t flags)
408 {
409 	mdb_printf("TRB Address: 0x%lx\n", pa);
410 	mdb_printf("TRB Length: %d bytes\n", XHCI_TRB_LEN(status));
411 	mdb_printf("TRB TD Size: %d packets\n", XHCI_TRB_GET_TDREM(status));
412 	mdb_printf("TRB Interrupt: %d\n", XHCI_TRB_GET_INTR(status));
413 	mdb_printf("TRB Flags: %b (0x%x)\n", flags, xhci_mdb_trb_flags,
414 	    XHCI_TRB_GET_FLAGS(flags));
415 	mdb_dec_indent(XHCI_MDB_TRB_INDENT);
416 
417 	return (DCMD_OK);
418 }
419 
420 /* ARGSUSED */
421 static int
422 xhci_mdb_print_trb(uintptr_t addr, uint_t flags, int argc,
423     const mdb_arg_t *argv)
424 {
425 	xhci_trb_t trb;
426 	uint64_t pa;
427 	uint32_t status, trbflags, type;
428 
429 	if (!(flags & DCMD_ADDRSPEC)) {
430 		mdb_warn("::xhci_trb expects an address\n");
431 		return (DCMD_USAGE);
432 	}
433 
434 	if (mdb_vread(&trb, sizeof (trb), addr) != sizeof (trb)) {
435 		mdb_warn("failed to read xhci_trb_t at 0x%x", addr);
436 		return (DCMD_ERR);
437 	}
438 
439 	pa = LE_64(trb.trb_addr);
440 	status = LE_32(trb.trb_status);
441 	trbflags = LE_32(trb.trb_flags);
442 
443 	type = XHCI_TRB_GET_TYPE(trbflags);
444 
445 	if ((flags & DCMD_LOOP) && !(flags & DCMD_LOOPFIRST))
446 		mdb_printf("\n");
447 
448 	mdb_set_dot(addr + sizeof (xhci_trb_t));
449 	mdb_printf("%s TRB (%d)\n", xhci_mdb_trb_type_to_str(type), type);
450 	mdb_inc_indent(XHCI_MDB_TRB_INDENT);
451 
452 	switch (XHCI_RING_TYPE_SHIFT(type)) {
453 	case XHCI_EVT_XFER:
454 		return (xhci_mdb_print_transfer_event(pa, status, trbflags));
455 	case XHCI_EVT_CMD_COMPLETE:
456 		return (xhci_mdb_print_command_event(pa, status, trbflags));
457 	case XHCI_EVT_PORT_CHANGE:
458 		return (xhci_mdb_print_psc(pa, status, trbflags));
459 	case XHCI_TRB_TYPE_NORMAL:
460 		return (xhci_mdb_print_normal_trb(pa, status, trbflags));
461 	}
462 
463 	/*
464 	 * Just print generic information if we don't have a specific printer
465 	 * for that TRB type.
466 	 */
467 	mdb_printf("TRB Address: 0x%lx\n", pa);
468 	mdb_printf("TRB Status: 0x%x\n", status);
469 	mdb_printf("TRB Flags: 0x%x\n", trbflags);
470 	mdb_dec_indent(XHCI_MDB_TRB_INDENT);
471 
472 	return (DCMD_OK);
473 }
474 
475 static int
476 xhci_mdb_walk_xhci_init(mdb_walk_state_t *wsp)
477 {
478 	GElf_Sym sym;
479 	uintptr_t addr;
480 
481 	if (wsp->walk_addr != 0) {
482 		mdb_warn("::walk xhci only supports global walks\n");
483 		return (WALK_ERR);
484 	}
485 
486 	if (mdb_lookup_by_obj("xhci", "xhci_soft_state", &sym) != 0) {
487 		mdb_warn("failed to find xhci_soft_state symbol");
488 		return (WALK_ERR);
489 	}
490 
491 	if (mdb_vread(&addr, sizeof (addr), sym.st_value) != sizeof (addr)) {
492 		mdb_warn("failed to read xhci_soft_state at %p", addr);
493 		return (WALK_ERR);
494 	}
495 
496 	wsp->walk_addr = addr;
497 	if (mdb_layered_walk("softstate", wsp) != 0) {
498 		mdb_warn("failed to walk softstate");
499 		return (WALK_ERR);
500 	}
501 
502 	return (WALK_NEXT);
503 }
504 
505 static int
506 xhci_mdb_walk_xhci_step(mdb_walk_state_t *wsp)
507 {
508 	xhci_t xhci;
509 
510 	if (mdb_vread(&xhci, sizeof (xhci), wsp->walk_addr) != sizeof (xhci)) {
511 		mdb_warn("failed to read xhci_t at %p", wsp->walk_addr);
512 		return (WALK_ERR);
513 	}
514 
515 	return (wsp->walk_callback(wsp->walk_addr, &xhci, wsp->walk_cbdata));
516 }
517 
518 static int
519 xhci_mdb_walk_xhci_device_init(mdb_walk_state_t *wsp)
520 {
521 	uintptr_t addr;
522 
523 	if (wsp->walk_addr == 0) {
524 		mdb_warn("::walk xhci_device requires an xhci_t\n");
525 		return (WALK_ERR);
526 	}
527 
528 	addr = wsp->walk_addr;
529 	addr += offsetof(xhci_t, xhci_usba);
530 	addr += offsetof(xhci_usba_t, xa_devices);
531 	wsp->walk_addr = (uintptr_t)addr;
532 	if (mdb_layered_walk("list", wsp) != 0) {
533 		mdb_warn("failed to walk list");
534 		return (WALK_ERR);
535 	}
536 
537 	return (WALK_NEXT);
538 }
539 
540 static int
541 xhci_mdb_walk_xhci_device_step(mdb_walk_state_t *wsp)
542 {
543 	xhci_device_t xd;
544 
545 	if (mdb_vread(&xd, sizeof (xd), wsp->walk_addr) != sizeof (xd)) {
546 		mdb_warn("failed to read xhci_device_t at %p", wsp->walk_addr);
547 		return (WALK_ERR);
548 	}
549 
550 	return (wsp->walk_callback(wsp->walk_addr, &xd, wsp->walk_cbdata));
551 }
552 
553 static int
554 xhci_mdb_walk_xhci_endpoint_init(mdb_walk_state_t *wsp)
555 {
556 	xhci_mdb_walk_endpoint_t *xm;
557 	xhci_device_t *xd;
558 
559 	if (wsp->walk_addr == 0) {
560 		mdb_warn("::walk xhci_endpoint requires an xhci_device_t\n");
561 		return (WALK_ERR);
562 	}
563 
564 	xm = mdb_alloc(sizeof (xhci_mdb_walk_endpoint_t), UM_SLEEP | UM_GC);
565 	xm->xmwe_ep = 0;
566 	xd = &xm->xmwe_device;
567 	if (mdb_vread(xd, sizeof (*xd), wsp->walk_addr) != sizeof (*xd)) {
568 		mdb_warn("failed to read xhci_endpoint_t at %p",
569 		    wsp->walk_addr);
570 		return (WALK_ERR);
571 	}
572 	wsp->walk_data = xm;
573 
574 	return (WALK_NEXT);
575 }
576 
577 static int
578 xhci_mdb_walk_xhci_endpoint_step(mdb_walk_state_t *wsp)
579 {
580 	int ret;
581 	uintptr_t addr;
582 	xhci_mdb_walk_endpoint_t *xm = wsp->walk_data;
583 
584 	if (xm->xmwe_ep >= XHCI_NUM_ENDPOINTS)
585 		return (WALK_DONE);
586 
587 	addr = (uintptr_t)xm->xmwe_device.xd_endpoints[xm->xmwe_ep];
588 	if (addr != 0) {
589 		xhci_endpoint_t xe;
590 
591 		if (mdb_vread(&xe, sizeof (xe), addr) != sizeof (xe)) {
592 			mdb_warn("failed to read xhci_endpoint_t at %p",
593 			    xm->xmwe_device.xd_endpoints[xm->xmwe_ep]);
594 			return (WALK_ERR);
595 		}
596 
597 		ret = wsp->walk_callback(addr, &xe, wsp->walk_cbdata);
598 	} else {
599 		ret = WALK_NEXT;
600 	}
601 	xm->xmwe_ep++;
602 
603 	return (ret);
604 }
605 
606 typedef struct xhci_mdb_find {
607 	int		xmf_slot;
608 	int		xmf_ep;
609 	uintptr_t	xmf_addr;
610 } xhci_mdb_find_t;
611 
612 static int
613 xhci_mdb_find_endpoint_cb(uintptr_t addr, const void *data, void *arg)
614 {
615 	const xhci_endpoint_t *xep = data;
616 	xhci_mdb_find_t *xmf = arg;
617 
618 	/*
619 	 * The endpoints that are presented here are off by one from the actual
620 	 * endpoint ID in the xhci_endpoint_t, as we're really displaying the
621 	 * index into the device input context.
622 	 */
623 	if (xep->xep_num + 1 == xmf->xmf_ep) {
624 		xmf->xmf_addr = addr;
625 		return (WALK_DONE);
626 	}
627 
628 	return (WALK_NEXT);
629 }
630 
631 static int
632 xhci_mdb_find_device_cb(uintptr_t addr, const void *data, void *arg)
633 {
634 	const xhci_device_t *xd = data;
635 	xhci_mdb_find_t *xmf = arg;
636 
637 	if (xd->xd_slot == xmf->xmf_slot) {
638 		if (xmf->xmf_ep == -1) {
639 			xmf->xmf_addr = addr;
640 			return (WALK_DONE);
641 		}
642 
643 		if (mdb_pwalk("xhci`xhci_endpoint", xhci_mdb_find_endpoint_cb,
644 		    xmf, addr) == -1) {
645 			mdb_warn("failed to walk xhci_endpoint at %p", addr);
646 			return (WALK_ERR);
647 		}
648 
649 		return (WALK_DONE);
650 	}
651 
652 	return (WALK_NEXT);
653 }
654 
655 static int
656 xhci_mdb_find(uintptr_t addr, uint_t flags, int argc,
657     const mdb_arg_t *argv)
658 {
659 	uintptr_t ep, slot;
660 	boolean_t ep_set, slot_set;
661 	xhci_mdb_find_t xmf;
662 
663 	if ((flags & DCMD_ADDRSPEC) == 0)
664 		return (DCMD_USAGE);
665 
666 	ep_set = slot_set = B_FALSE;
667 	if (mdb_getopts(argc, argv, 'e', MDB_OPT_UINTPTR_SET, &ep_set, &ep,
668 	    's', MDB_OPT_UINTPTR_SET, &slot_set, &slot, NULL) != argc)
669 		return (DCMD_USAGE);
670 
671 	if (!slot_set) {
672 		mdb_warn("-s is required\n");
673 		return (DCMD_USAGE);
674 	}
675 
676 	xmf.xmf_slot = (int)slot;
677 	if (ep_set)
678 		xmf.xmf_ep = (int)ep;
679 	else
680 		xmf.xmf_ep = -1;
681 	xmf.xmf_addr = 0;
682 
683 	if (mdb_pwalk("xhci`xhci_device", xhci_mdb_find_device_cb,
684 	    &xmf, addr) == -1) {
685 		mdb_warn("failed to walk xhci_device at %p", addr);
686 		return (DCMD_ERR);
687 	}
688 
689 	if (xmf.xmf_addr == 0) {
690 		if (ep_set) {
691 			mdb_warn("failed to find xhci_endpoint_t for slot %d "
692 			    "and endpoint %d\n", slot, ep);
693 		} else {
694 			mdb_warn("failed to find xhci_device_t for slot %d\n",
695 			    slot);
696 		}
697 		return (DCMD_ERR);
698 	}
699 
700 	mdb_printf("%p\n", xmf.xmf_addr);
701 	return (DCMD_OK);
702 }
703 
704 /* ARGSUSED */
705 static int
706 xhci_mdb_endpoint_count(uintptr_t addr, const void *ep, void *arg)
707 {
708 	int *countp = arg;
709 
710 	*countp += 1;
711 	return (WALK_NEXT);
712 }
713 
714 /* ARGSUSED */
715 static int
716 xhci_mdb_print_endpoint_summary(uintptr_t addr, const void *ep, void *arg)
717 {
718 	const xhci_device_t *xd = arg;
719 	const xhci_endpoint_t *xep = ep;
720 	const char *type;
721 	const char *state;
722 	xhci_endpoint_context_t epctx;
723 	int eptype;
724 
725 	if (mdb_vread(&epctx, sizeof (epctx),
726 	    (uintptr_t)xd->xd_endout[xep->xep_num]) != sizeof (epctx)) {
727 		mdb_warn("failed to read endpoint context at %p",
728 		    xd->xd_endout[xep->xep_num]);
729 		return (WALK_ERR);
730 	}
731 
732 	eptype = XHCI_EPCTX_GET_EPTYPE(LE_32(epctx.xec_info2));
733 	type = xhci_mdb_epctx_eptypes[eptype];
734 	state = xhci_mdb_epctx_states[XHCI_EPCTX_STATE(LE_32(epctx.xec_info))];
735 
736 	mdb_printf("%-4d %-10s %-10s 0x%-04x 0x%-04x\n", xep->xep_num, type,
737 	    state, xep->xep_ring.xr_head, xep->xep_ring.xr_tail);
738 
739 	return (WALK_NEXT);
740 }
741 
742 /* ARGSUSED */
743 static int
744 xhci_mdb_print_device(uintptr_t addr, uint_t flags, int argc,
745     const mdb_arg_t *argv)
746 {
747 	int count;
748 	xhci_device_t xd;
749 	usba_device_t ud;
750 	char product[256], mfg[256];
751 
752 	if (!(flags & DCMD_ADDRSPEC)) {
753 		return (mdb_eval("::walk xhci`xhci | ::walk xhci`xhci_device | "
754 		    "::xhci_device"));
755 	}
756 
757 	if (mdb_vread(&xd, sizeof (xd), addr) != sizeof (xd)) {
758 		mdb_warn("failed to read xhci_device_t at 0x%x", addr);
759 		return (DCMD_ERR);
760 	}
761 
762 	if (mdb_vread(&ud, sizeof (ud), (uintptr_t)xd.xd_usbdev) !=
763 	    sizeof (ud)) {
764 		mdb_warn("failed to read usba_device_t at %p\n", xd.xd_usbdev);
765 		return (DCMD_ERR);
766 	}
767 
768 	if (ud.usb_mfg_str == NULL || mdb_readstr(mfg, sizeof (mfg),
769 	    (uintptr_t)ud.usb_mfg_str) <= 0) {
770 		(void) strlcpy(mfg, "Unknown Manufacturer", sizeof (mfg));
771 	}
772 
773 	if (ud.usb_product_str == NULL || mdb_readstr(product, sizeof (product),
774 	    (uintptr_t)ud.usb_product_str) <= 0) {
775 		(void) strlcpy(product, "Unknown Product", sizeof (product));
776 	}
777 
778 	mdb_printf("%<b>%s - %s%</b>\n", mfg, product);
779 
780 	count = 0;
781 	if (mdb_pwalk("xhci`xhci_endpoint", xhci_mdb_endpoint_count, &count,
782 	    addr) == -1) {
783 		mdb_warn("failed to walk xhci_endpoint rooted at 0x%x", addr);
784 		return (DCMD_ERR);
785 	}
786 
787 	mdb_printf("Port %02d | Slot %02d | # Endpoints %02d\n", xd.xd_port,
788 	    xd.xd_slot, count);
789 	mdb_printf("%<u>%-4s %-10s %-10s %-6s %-6s%</u>\n", "EP", "Type",
790 	    "State", "Head", "Tail");
791 
792 	if (mdb_pwalk("xhci`xhci_endpoint", xhci_mdb_print_endpoint_summary,
793 	    &xd, addr) == -1) {
794 		mdb_warn("failed to walk xhci_endpoint rooted at 0x%x", addr);
795 		return (DCMD_ERR);
796 	}
797 
798 
799 	mdb_printf("\n");
800 
801 	return (DCMD_OK);
802 }
803 
804 static int
805 xhci_mdb_find_trb(uintptr_t addr, uint_t flags, int argc,
806     const mdb_arg_t *argv)
807 {
808 	xhci_ring_t xr;
809 	uint64_t base, max, target;
810 
811 	if (!(flags & DCMD_ADDRSPEC)) {
812 		mdb_warn("missing required xhci_ring_t\n");
813 		return (DCMD_USAGE);
814 	}
815 
816 	if (argc == 0) {
817 		mdb_warn("missing required PA of ring\n");
818 		return (DCMD_USAGE);
819 	}
820 
821 	if (argc > 1) {
822 		mdb_warn("too many arguments\n");
823 		return (DCMD_USAGE);
824 	}
825 
826 	if (mdb_vread(&xr, sizeof (xr), addr) != sizeof (xr)) {
827 		mdb_warn("failed to read xhci_ring_t at %p", addr);
828 		return (DCMD_USAGE);
829 	}
830 
831 	if (argv[0].a_type == MDB_TYPE_IMMEDIATE) {
832 		target = argv[0].a_un.a_val;
833 	} else if (argv[0].a_type == MDB_TYPE_STRING) {
834 		target = mdb_strtoull(argv[0].a_un.a_str);
835 	} else {
836 		mdb_warn("argument is an unknown supported type: %d\n",
837 		    argv[0].a_type);
838 		return (DCMD_USAGE);
839 	}
840 	target = roundup(target, sizeof (xhci_trb_t));
841 
842 	base = xr.xr_dma.xdb_cookies[0].dmac_laddress;
843 	max = base + xr.xr_ntrb * sizeof (xhci_trb_t);
844 
845 	if (target < base || target > max) {
846 		mdb_warn("target address %p is outside the range of PAs for "
847 		    "TRBs in the ring [%p, %p)", target, base, max);
848 		return (DCMD_ERR);
849 	}
850 	target -= base;
851 	mdb_printf("0x%" PRIx64 "\n", target + (uintptr_t)xr.xr_trb);
852 
853 	return (DCMD_OK);
854 }
855 
856 static const mdb_dcmd_t xhci_dcmds[] = {
857 	{ "xhci_epctx", ":", "print endpoint context",
858 	    xhci_mdb_print_epctx, NULL },
859 	{ "xhci_slotctx", ":", "print slot context",
860 	    xhci_mdb_print_slotctx, NULL },
861 	{ "xhci_trb", ":", "print TRB",
862 	    xhci_mdb_print_trb, NULL },
863 	{ "xhci_find", ": -s slot [-e endpiont]",
864 	    "find given xhci slot or endpoint",
865 	    xhci_mdb_find, NULL },
866 	{ "xhci_device", ":", "device summary",
867 	    xhci_mdb_print_device, NULL },
868 	{ "xhci_find_trb", ": pa", "find trb with PA in ring",
869 	    xhci_mdb_find_trb, NULL },
870 	{ NULL }
871 };
872 
873 static const mdb_walker_t xhci_walkers[] = {
874 	{ "xhci", "walk list of xhci_t structures",
875 	    xhci_mdb_walk_xhci_init, xhci_mdb_walk_xhci_step, NULL },
876 	{ "xhci_device", "walk list of xhci_device_t structures",
877 	    xhci_mdb_walk_xhci_device_init, xhci_mdb_walk_xhci_device_step,
878 	    NULL },
879 	{ "xhci_endpoint", "walk list of xhci_endpoint_t structures",
880 	    xhci_mdb_walk_xhci_endpoint_init, xhci_mdb_walk_xhci_endpoint_step,
881 	    NULL },
882 	{ NULL }
883 };
884 
885 static const mdb_modinfo_t xhci_modinfo = {
886 	MDB_API_VERSION, xhci_dcmds, xhci_walkers
887 };
888 
889 const mdb_modinfo_t *
890 _mdb_init(void)
891 {
892 	return (&xhci_modinfo);
893 }
894