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 #include <gelf.h>
30
31 #include <sys/mdb_modapi.h>
32 #include <mdb/mdb_ks.h>
33
34 #include <sys/usb/usba.h>
35 #include <sys/usb/usba/usba_types.h>
36
37 #include <sys/usb/hcd/uhci/uhci.h>
38 #include <sys/usb/hcd/uhci/uhcid.h>
39 #include <sys/usb/hcd/uhci/uhciutil.h>
40
41
42 #define UHCI_TD 0
43 #define UHCI_QH 1
44
45
46 /* Prototypes */
47
48 int uhci_td(uintptr_t, uint_t, int, const mdb_arg_t *);
49 int uhci_qh(uintptr_t, uint_t, int, const mdb_arg_t *);
50 int uhci_td_walk_init(mdb_walk_state_t *);
51 int uhci_td_walk_step(mdb_walk_state_t *);
52 int uhci_qh_walk_init(mdb_walk_state_t *);
53 int uhci_qh_walk_step(mdb_walk_state_t *);
54
55
56 /*
57 * Callback for find_uhci_statep (called back from walk "softstate" in
58 * find_uhci_statep).
59 *
60 * - uhci_instancep is the value of the current pointer in the array of soft
61 * state instance pointers (see i_ddi_soft_state in ddi_impldefs.h)
62 * - local_ss is a pointer to the copy of the i_ddi_soft_state in local space
63 * - cb_arg is a pointer to the cb arg (an instance of state_find_data).
64 *
65 * For the current uchi_state_t*, see if the td address is in its pool.
66 *
67 * Returns WALK_NEXT on success (match not found yet), WALK_ERR on errors.
68 *
69 * WALK_DONE is returned, cb_data.found is set to TRUE, and
70 * *cb_data.fic_uhci_statep is filled in with the contents of the state
71 * struct in core. This forces the walk to terminate.
72 */
73 typedef struct find_instance_struct {
74 void *fic_td_qh; /* td/qh we want uhci instance for */
75 boolean_t fic_td_or_qh; /* which one td_qh points to */
76 boolean_t fic_found;
77 uhci_state_t *fic_uhci_statep; /* buffer uhci_state's written into */
78 } find_instance_cb_t;
79
80 /*ARGSUSED*/
81 static int
find_uhci_instance(uintptr_t uhci_instancep,const void * local_ss,void * cb_arg)82 find_uhci_instance(uintptr_t uhci_instancep, const void *local_ss, void *cb_arg)
83 {
84 int td_pool_size, qh_pool_size;
85 find_instance_cb_t *cb_data = (find_instance_cb_t *)cb_arg;
86 uhci_state_t *uhcip = cb_data->fic_uhci_statep;
87
88
89 if (mdb_vread(cb_data->fic_uhci_statep, sizeof (uhci_state_t),
90 uhci_instancep) == -1) {
91 mdb_warn("failed to read uhci_state at %p", uhci_instancep);
92 return (-1);
93 }
94
95 if (mdb_readsym(&td_pool_size, sizeof (int), "uhci_td_pool_size") ==
96 -1) {
97 mdb_warn("failed to read uhci_td_pool_size");
98 return (-1);
99 }
100
101 if (mdb_readsym(&qh_pool_size, sizeof (int), "uhci_qh_pool_size") ==
102 -1) {
103 mdb_warn("failed to read uhci_td_pool_size");
104 return (-1);
105 }
106
107 /*
108 * See if the addr is within the appropriate pool for this instance.
109 */
110 if ((cb_data->fic_td_or_qh == UHCI_TD &&
111
112 ((uhci_td_t *)cb_data->fic_td_qh >= uhcip->uhci_td_pool_addr &&
113 (uhci_td_t *)cb_data->fic_td_qh <= (uhcip->uhci_td_pool_addr +
114 td_pool_size - sizeof (uhci_td_t)))) ||
115
116 (cb_data->fic_td_or_qh == UHCI_QH &&
117
118 ((queue_head_t *)cb_data->fic_td_qh >= uhcip->uhci_qh_pool_addr &&
119 (queue_head_t *)cb_data->fic_td_qh <= (uhcip->uhci_qh_pool_addr +
120 qh_pool_size - sizeof (queue_head_t))))) {
121
122 /* td/qh address is within pool for this instance of uhci. */
123 cb_data->fic_found = TRUE;
124 return (WALK_DONE);
125 }
126
127 return (WALK_NEXT);
128 }
129
130 /*
131 * Figure out which instance of uhci owns a td/qh.
132 *
133 * - td_qh: a pointer to a uhci td or qh
134 * - td_or_qh: a flag indicating which it is (td/qh),
135 * - uhci_statep, pointer to a uhci_state_t, to be filled in with data from
136 * the found instance of uhci_state_t.
137 *
138 * Only works for Cntl/Interrupt tds/qhs; others are dynamically allocated
139 * and so cannot be found with this method.
140 *
141 * Returns 0 on success (no match found), 1 on success (match found),
142 * -1 on errors.
143 */
144 static int
find_uhci_statep(void * td_qh,boolean_t td_or_qh,uhci_state_t * uhci_statep)145 find_uhci_statep(void *td_qh, boolean_t td_or_qh, uhci_state_t *uhci_statep)
146 {
147 find_instance_cb_t cb_data;
148 uintptr_t uhci_ss;
149
150
151 if (uhci_statep == NULL) {
152 mdb_warn("failed to find uhci statep: "
153 "NULL uhci_statep param\n");
154 return (-1);
155 }
156
157 cb_data.fic_td_qh = td_qh;
158 cb_data.fic_td_or_qh = td_or_qh;
159 cb_data.fic_found = FALSE;
160 cb_data.fic_uhci_statep = uhci_statep;
161
162
163 if (mdb_readsym(&uhci_ss, sizeof (uhci_statep),
164 "uhci_statep") == -1) {
165 mdb_warn("failed to read uhci_statep");
166 return (-1);
167 }
168
169
170 /*
171 * Walk all instances of uhci.
172 * The callback func checks if td_qh belongs to a given instance
173 * of uhci.
174 */
175 if (mdb_pwalk("softstate", find_uhci_instance, &cb_data,
176 uhci_ss) != 0) {
177 mdb_warn("failed to walk softstate");
178 return (-1);
179 }
180
181 if (cb_data.fic_found == TRUE) {
182 return (1);
183 }
184
185 return (0);
186 }
187
188 /*
189 * Dump a UHCI TD (transaction descriptor);
190 * or (-d) the chain of TDs starting with the one specified.
191 */
192 int
uhci_td(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)193 uhci_td(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
194 {
195 uint_t depth_flag = FALSE;
196 uhci_state_t uhci_state, *uhcip = &uhci_state;
197 uhci_td_t td;
198
199
200 if (!(flags & DCMD_ADDRSPEC))
201 return (DCMD_USAGE);
202
203 if (addr & ~QH_LINK_PTR_MASK) {
204 mdb_warn("address must be on a 16-byte boundary.\n");
205 return (DCMD_ERR);
206 }
207
208 if (mdb_getopts(argc, argv,
209 'd', MDB_OPT_SETBITS, TRUE, &depth_flag,
210 NULL) != argc) {
211 return (DCMD_USAGE);
212 }
213
214
215 if (depth_flag) {
216 if (mdb_pwalk_dcmd("uhci_td", "uhci_td", 0, NULL, addr) == -1) {
217 mdb_warn("failed to walk 'uhci_td'");
218 return (DCMD_ERR);
219 }
220 return (DCMD_OK);
221 }
222
223
224 if (find_uhci_statep((void *)addr, UHCI_TD, uhcip) != 1) {
225 mdb_warn("failed to find uhci_statep");
226 return (DCMD_ERR);
227 }
228
229 if (mdb_vread(&td, sizeof (td), addr) != sizeof (td)) {
230 mdb_warn("failed to read td at vaddr %p", addr);
231 return (DCMD_ERR);
232 }
233
234 mdb_printf("\n UHCI td struct at (vaddr) %08x:\n", addr);
235
236 if (!(td.link_ptr & HC_END_OF_LIST) && td.link_ptr != NULL) {
237 mdb_printf(" link_ptr (paddr) : %-8x "
238 "(vaddr) : %p\n",
239 td.link_ptr,
240 /* Note: uhcip needed by TD_VADDR macro */
241 TD_VADDR(td.link_ptr & QH_LINK_PTR_MASK));
242 } else {
243 mdb_printf(" link_ptr (paddr) : %-8x\n",
244 td.link_ptr);
245 }
246 mdb_printf(" td_dword2 : %08x\n", td.dw2);
247 mdb_printf(" td_dword3 : %08x\n", td.dw3);
248 mdb_printf(" buffer_address : %08x\n", td.buffer_address);
249 mdb_printf(" qh_td_prev : %?p "
250 "tw_td_next : %?p\n",
251 td.qh_td_prev, td.tw_td_next);
252 mdb_printf(" outst_td_prev : %?p "
253 "outst_td_next : %?p\n",
254 td.outst_td_prev, td.outst_td_next);
255 mdb_printf(" tw : %?p "
256 "flag : %02x\n", td.tw, td.flag);
257 mdb_printf(" isoc_next : %?p "
258 "isoc_prev : %0x\n", td.isoc_next, td.isoc_prev);
259 mdb_printf(" isoc_pkt_index : %0x "
260 "startingframe: %0x\n", td.isoc_pkt_index, td.starting_frame);
261
262
263 if (td.link_ptr == NULL) {
264 mdb_printf(" --> Link pointer = NULL\n");
265 return (DCMD_ERR);
266 } else {
267
268 /* Inform user if link is to a TD or QH. */
269 if (td.link_ptr & HC_END_OF_LIST) {
270 mdb_printf(" "
271 "--> Link pointer invalid (terminate bit set).\n");
272 } else {
273 if ((td.link_ptr & HC_QUEUE_HEAD) == HC_QUEUE_HEAD) {
274 mdb_printf(" "
275 "--> Link pointer points to a QH.\n");
276 } else {
277 mdb_printf(" "
278 "--> Link pointer points to a TD.\n");
279 }
280 }
281 }
282
283 return (DCMD_OK);
284 }
285
286 /*
287 * Dump a UHCI QH (queue head).
288 * -b walk/dump the chian of QHs starting with the one specified.
289 * -d also dump the chain of TDs starting with the one specified.
290 */
291 int
uhci_qh(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)292 uhci_qh(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
293 {
294 uint_t breadth_flag = FALSE, depth_flag = FALSE;
295 uhci_state_t uhci_state, *uhcip = &uhci_state;
296 queue_head_t qh;
297
298
299 if (!(flags & DCMD_ADDRSPEC))
300 return (DCMD_USAGE);
301
302 if (addr & ~QH_LINK_PTR_MASK) {
303 mdb_warn("address must be on a 16-byte boundary.\n");
304 return (DCMD_ERR);
305 }
306
307 if (mdb_getopts(argc, argv,
308 'b', MDB_OPT_SETBITS, TRUE, &breadth_flag,
309 'd', MDB_OPT_SETBITS, TRUE, &depth_flag,
310 NULL) != argc) {
311 return (DCMD_USAGE);
312 }
313
314
315 if (breadth_flag) {
316 uint_t new_argc = 0;
317 mdb_arg_t new_argv[1];
318
319
320 if (depth_flag) {
321 new_argc = 1;
322 new_argv[0].a_type = MDB_TYPE_STRING;
323 new_argv[0].a_un.a_str = "-d";
324 }
325
326 if ((mdb_pwalk_dcmd("uhci_qh", "uhci_qh", new_argc, new_argv,
327 addr)) != 0) {
328 mdb_warn("failed to walk 'uhci_qh'");
329 return (DCMD_ERR);
330 }
331 return (DCMD_OK);
332 }
333
334
335 if (find_uhci_statep((void *)addr, UHCI_QH, uhcip) != 1) {
336 mdb_warn("failed to find uhci_statep");
337 return (DCMD_ERR);
338 }
339
340
341 if (mdb_vread(&qh, sizeof (qh), addr) != sizeof (qh)) {
342 mdb_warn("failed to read qh at vaddr %p", addr);
343 return (DCMD_ERR);
344 }
345
346 mdb_printf("\n UHCI qh struct at (vaddr) %08x:\n", addr);
347
348 if (!(qh.link_ptr & HC_END_OF_LIST) && qh.link_ptr != NULL) {
349 mdb_printf(" link_ptr (paddr) : %08x "
350 "(vaddr) : %p\n",
351 qh.link_ptr,
352 /* Note: uhcip needed by QH_VADDR macro */
353 QH_VADDR(qh.link_ptr & QH_LINK_PTR_MASK));
354 } else {
355 mdb_printf(
356 " link_ptr (paddr) : %08x\n",
357 qh.link_ptr);
358 }
359
360 if (!(qh.element_ptr & HC_END_OF_LIST) && qh.element_ptr != NULL) {
361 mdb_printf(" element_ptr (paddr) : %08x "
362 "(vaddr) : %p\n",
363 qh.element_ptr,
364 /* Note: uhcip needed by TD_VADDR macro */
365 TD_VADDR(qh.element_ptr & QH_LINK_PTR_MASK));
366 } else {
367 mdb_printf(
368 " element_ptr (paddr) : %08x\n", qh.element_ptr);
369 }
370
371 mdb_printf(" node : %04x "
372 "flag : %04x\n",
373 qh.node, qh.qh_flag);
374 mdb_printf(" prev_qh : %?p "
375 "td_tailp : %?p\n",
376 qh.prev_qh, qh.td_tailp);
377 mdb_printf(" bulk_xfer_isoc_info : %?p\n", qh.bulk_xfer_info);
378
379
380 if (qh.link_ptr == NULL) {
381 mdb_printf(" --> Link pointer = NULL\n");
382 return (DCMD_ERR);
383 } else {
384
385 /* Inform user if next link is a TD or QH. */
386 if (qh.link_ptr & HC_END_OF_LIST) {
387 mdb_printf(" "
388 "--> Link pointer invalid (terminate bit set).\n");
389 } else {
390 if ((qh.link_ptr & HC_QUEUE_HEAD) == HC_QUEUE_HEAD) {
391 mdb_printf(" "
392 "--> Link pointer points to a QH.\n");
393 } else {
394 /* Should never happen. */
395 mdb_warn(" "
396 "--> Link pointer points to a TD.\n");
397 return (DCMD_ERR);
398 }
399 }
400 }
401
402
403 if (qh.element_ptr == NULL) {
404 mdb_printf(" element_ptr = NULL\n");
405 return (DCMD_ERR);
406 } else {
407
408 /* Inform user if next element is a TD or QH. */
409 if (qh.element_ptr & HC_END_OF_LIST) {
410 mdb_printf(" "
411 "-->Element pointer invalid (terminate bit set)."
412 "\n");
413 return (DCMD_OK);
414 } else {
415 if ((qh.element_ptr & HC_QUEUE_HEAD) == HC_QUEUE_HEAD) {
416 mdb_printf(" "
417 "--> Element pointer points to a QH.\n");
418 /* Should never happen in UHCI implementation */
419 return (DCMD_ERR);
420 } else {
421 mdb_printf(" "
422 "--> Element pointer points to a TD.\n");
423 }
424 }
425 }
426
427 /*
428 * If the user specified the -d (depth) option,
429 * dump all TDs linked to this TD via the element_ptr.
430 */
431 if (depth_flag) {
432
433 /* Traverse and display all the TDs in the chain */
434 if (mdb_pwalk_dcmd("uhci_td", "uhci_td", argc, argv,
435 (uintptr_t)(TD_VADDR(qh.element_ptr &
436 QH_LINK_PTR_MASK))) == -1) {
437 mdb_warn("failed to walk 'uhci_td'");
438 return (DCMD_ERR);
439 }
440 }
441
442 return (DCMD_OK);
443 }
444
445 /*
446 * Walk a list of UHCI Transaction Descriptors (td's).
447 * Stop at the end of the list, or if the next element in the list is a
448 * queue head (qh).
449 * User must specify the address of the first td to look at.
450 */
451 int
uhci_td_walk_init(mdb_walk_state_t * wsp)452 uhci_td_walk_init(mdb_walk_state_t *wsp)
453 {
454 if (wsp->walk_addr == NULL) {
455 return (DCMD_USAGE);
456 }
457
458 wsp->walk_data = mdb_alloc(sizeof (uhci_td_t), UM_SLEEP | UM_GC);
459 wsp->walk_arg = mdb_alloc(sizeof (uhci_state_t), UM_SLEEP | UM_GC);
460
461
462 /*
463 * Read the uhci_state_t for the instance of uhci
464 * using this td address into buf pointed to by walk_arg.
465 */
466 if (find_uhci_statep((void *)wsp->walk_addr, UHCI_TD,
467 wsp->walk_arg) != 1) {
468 mdb_warn("failed to find uhci_statep");
469 return (WALK_ERR);
470 }
471
472 return (WALK_NEXT);
473 }
474
475 /*
476 * At each step, read a TD into our private storage, and then invoke
477 * the callback function. We terminate when we reach a QH, or
478 * link_ptr is NULL.
479 */
480 int
uhci_td_walk_step(mdb_walk_state_t * wsp)481 uhci_td_walk_step(mdb_walk_state_t *wsp)
482 {
483 int status;
484 uhci_state_t *uhcip = (uhci_state_t *)wsp->walk_arg;
485
486
487 if (mdb_vread(wsp->walk_data, sizeof (uhci_td_t), wsp->walk_addr)
488 == -1) {
489 mdb_warn("failed to read td at %p", wsp->walk_addr);
490 return (WALK_DONE);
491 }
492
493 status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
494 wsp->walk_cbdata);
495
496 /* Next td. */
497 wsp->walk_addr = ((uhci_td_t *)wsp->walk_data)->link_ptr;
498
499 /* Check if we're at the last element */
500 if (wsp->walk_addr == NULL || wsp->walk_addr & HC_END_OF_LIST)
501 return (WALK_DONE);
502
503 /* Make sure next element is a TD. If a QH, stop. */
504 if (((((uhci_td_t *)wsp->walk_data)->link_ptr) & HC_QUEUE_HEAD)
505 == HC_QUEUE_HEAD) {
506 return (WALK_DONE);
507 }
508
509 /* Strip terminate etc. bits. */
510 wsp->walk_addr &= QH_LINK_PTR_MASK; /* there is no TD_LINK_PTR_MASK */
511
512 if (wsp->walk_addr == NULL)
513 return (WALK_DONE);
514
515 /*
516 * Convert link_ptr paddr to vaddr
517 * Note: uhcip needed by TD_VADDR macro
518 */
519 wsp->walk_addr = (uintptr_t)TD_VADDR(wsp->walk_addr);
520
521 return (status);
522 }
523
524 /*
525 * Walk a list of UHCI Queue Heads (qh's).
526 * Stop at the end of the list, or if the next element in the list is a
527 * Transaction Descriptor (td).
528 * User must specify the address of the first qh to look at.
529 */
530 int
uhci_qh_walk_init(mdb_walk_state_t * wsp)531 uhci_qh_walk_init(mdb_walk_state_t *wsp)
532 {
533 if (wsp->walk_addr == NULL)
534 return (DCMD_USAGE);
535
536 wsp->walk_data = mdb_alloc(sizeof (queue_head_t), UM_SLEEP | UM_GC);
537 wsp->walk_arg = mdb_alloc(sizeof (uhci_state_t), UM_SLEEP | UM_GC);
538
539
540 /*
541 * Read the uhci_state_t for the instance of uhci
542 * using this td address into buf pointed to by walk_arg.
543 */
544 if (find_uhci_statep((void *)wsp->walk_addr, UHCI_QH,
545 (uhci_state_t *)wsp->walk_arg) != 1) {
546 mdb_warn("failed to find uhci_statep");
547 return (WALK_ERR);
548 }
549
550 return (WALK_NEXT);
551 }
552
553 /*
554 * At each step, read a QH into our private storage, and then invoke
555 * the callback function. We terminate when we reach a QH, or
556 * link_ptr is NULL.
557 */
558 int
uhci_qh_walk_step(mdb_walk_state_t * wsp)559 uhci_qh_walk_step(mdb_walk_state_t *wsp)
560 {
561 int status;
562 uhci_state_t *uhcip = (uhci_state_t *)wsp->walk_arg;
563
564
565 if (wsp->walk_addr == NULL) /* Should never occur */
566 return (WALK_DONE);
567
568 if (mdb_vread(wsp->walk_data, sizeof (queue_head_t), wsp->walk_addr)
569 == -1) {
570 mdb_warn("failure reading qh at %p", wsp->walk_addr);
571 return (WALK_DONE);
572 }
573
574 status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
575 wsp->walk_cbdata);
576
577 /* Next QH. */
578 wsp->walk_addr = ((queue_head_t *)wsp->walk_data)->link_ptr;
579
580
581 /* Check if we're at the last element */
582 if (wsp->walk_addr == NULL || wsp->walk_addr & HC_END_OF_LIST) {
583 return (WALK_DONE);
584 }
585
586 /* Make sure next element is a QH. If a TD, stop. */
587 if (! ((((queue_head_t *)wsp->walk_data)->link_ptr) & HC_QUEUE_HEAD)
588 == HC_QUEUE_HEAD) {
589 return (WALK_DONE);
590 }
591
592 /* Strip terminate etc. bits. */
593 wsp->walk_addr &= QH_LINK_PTR_MASK;
594
595 if (wsp->walk_addr == NULL)
596 return (WALK_DONE);
597
598 /*
599 * Convert link_ptr paddr to vaddr
600 * Note: uhcip needed by QH_VADDR macro
601 */
602 wsp->walk_addr = (uintptr_t)QH_VADDR(wsp->walk_addr);
603
604 return (status);
605 }
606
607 /*
608 * MDB module linkage information:
609 *
610 * We declare a list of structures describing our dcmds, and a function
611 * named _mdb_init to return a pointer to our module information.
612 */
613
614 static const mdb_dcmd_t dcmds[] = {
615 { "uhci_td", ": [-d]", "print UHCI TD", uhci_td, NULL },
616 { "uhci_qh", ": [-bd]", "print UHCI QH", uhci_qh, NULL},
617 { NULL }
618 };
619
620
621 static const mdb_walker_t walkers[] = {
622 { "uhci_td", "walk list of UHCI TD structures",
623 uhci_td_walk_init, uhci_td_walk_step, NULL,
624 NULL },
625 { "uhci_qh", "walk list of UHCI QH structures",
626 uhci_qh_walk_init, uhci_qh_walk_step, NULL,
627 NULL },
628 { NULL }
629 };
630
631 static const mdb_modinfo_t modinfo = {
632 MDB_API_VERSION, dcmds, walkers
633 };
634
635
636 const mdb_modinfo_t *
_mdb_init(void)637 _mdb_init(void)
638 {
639 return (&modinfo);
640 }
641