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