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 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 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 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 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 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 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 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 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 * 637 _mdb_init(void) 638 { 639 return (&modinfo); 640 }