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 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 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 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 != 0) { 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 == 0) { 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 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 != 0) { 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 != 0) { 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 == 0) { 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 == 0) { 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 450 uhci_td_walk_init(mdb_walk_state_t *wsp) 451 { 452 if (wsp->walk_addr == 0) { 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 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 == 0 || 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 == 0) 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 529 uhci_qh_walk_init(mdb_walk_state_t *wsp) 530 { 531 if (wsp->walk_addr == 0) 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 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 == 0) /* 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 == 0 || 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 == 0) 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 * 635 _mdb_init(void) 636 { 637 return (&modinfo); 638 } 639