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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 /* 28 * av1394 configuration ROM 29 */ 30 #include <sys/file.h> 31 #include <sys/stream.h> 32 #include <sys/strsun.h> 33 #include <sys/1394/targets/av1394/av1394_impl.h> 34 35 /* configROM parsing */ 36 static int av1394_cfgrom_parse_rom(av1394_inst_t *); 37 static void av1394_cfgrom_unparse_rom(av1394_inst_t *); 38 static int av1394_cfgrom_parse_dir(av1394_inst_t *, cmd1394_cmd_t *, 39 av1394_cfgrom_parse_arg_t *); 40 static void av1394_cfgrom_add_text_leaf(av1394_inst_t *, 41 av1394_cfgrom_parsed_dir_t *, uint64_t, uint32_t); 42 static int av1394_cfgrom_read_leaf(av1394_inst_t *, uint64_t, mblk_t **); 43 static void av1394_cfgrom_grow_parsed_dir(av1394_cfgrom_parsed_dir_t *, 44 int); 45 46 /* routines involving bus transactions */ 47 static int av1394_cfgrom_rq(av1394_inst_t *, cmd1394_cmd_t *, 48 uint64_t, uint32_t *); 49 50 /* the following macros emulate throwing an exception when read fails */ 51 #define AV1394_CFGROM_RQ(avp, cmd, addr, valp) \ 52 if ((ret = av1394_cfgrom_rq(avp, cmd, addr, valp)) != 0) { \ 53 goto catch; \ 54 } 55 56 #define AV1394_TNF_ENTER(func) \ 57 TNF_PROBE_0_DEBUG(func##_enter, AV1394_TNF_ASYNC_STACK, ""); 58 59 #define AV1394_TNF_EXIT(func) \ 60 TNF_PROBE_0_DEBUG(func##_exit, AV1394_TNF_ASYNC_STACK, ""); 61 62 int 63 av1394_cfgrom_init(av1394_inst_t *avp) 64 { 65 av1394_cfgrom_t *crp = &avp->av_a.a_cfgrom; 66 ddi_iblock_cookie_t ibc = avp->av_attachinfo.iblock_cookie; 67 68 AV1394_TNF_ENTER(av1394_cfgrom_init); 69 70 rw_init(&crp->cr_rwlock, NULL, RW_DRIVER, ibc); 71 72 AV1394_TNF_EXIT(av1394_cfgrom_init); 73 return (DDI_SUCCESS); 74 } 75 76 void 77 av1394_cfgrom_fini(av1394_inst_t *avp) 78 { 79 av1394_cfgrom_t *crp = &avp->av_a.a_cfgrom; 80 81 AV1394_TNF_ENTER(av1394_cfgrom_fini); 82 83 rw_destroy(&crp->cr_rwlock); 84 85 AV1394_TNF_EXIT(av1394_cfgrom_fini); 86 } 87 88 void 89 av1394_cfgrom_close(av1394_inst_t *avp) 90 { 91 av1394_cfgrom_t *crp = &avp->av_a.a_cfgrom; 92 93 AV1394_TNF_ENTER(av1394_cfgrom_close); 94 95 rw_enter(&crp->cr_rwlock, RW_WRITER); 96 if (crp->cr_parsed) { 97 av1394_cfgrom_unparse_rom(avp); 98 } 99 rw_exit(&crp->cr_rwlock); 100 101 AV1394_TNF_EXIT(av1394_cfgrom_close); 102 } 103 104 int 105 av1394_ioctl_node_get_bus_name(av1394_inst_t *avp, void *arg, int mode) 106 { 107 cmd1394_cmd_t *cmd; 108 uint32_t val; 109 int err; 110 int ret = 0; 111 112 AV1394_TNF_ENTER(av1394_ioctl_node_get_bus_name); 113 114 err = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd); 115 if (err != DDI_SUCCESS) { 116 AV1394_TNF_EXIT(av1394_ioctl_node_get_bus_name); 117 return (ENOMEM); 118 } 119 120 ret = av1394_cfgrom_rq(avp, cmd, AV1394_CFGROM_BUS_NAME_ADDR, &val); 121 if (ret == 0) { 122 if (ddi_copyout(&val, arg, sizeof (uint32_t), mode) != 0) { 123 ret = EFAULT; 124 } 125 } 126 127 err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd); 128 ASSERT(err == DDI_SUCCESS); 129 130 AV1394_TNF_EXIT(av1394_ioctl_node_get_bus_name); 131 return (ret); 132 } 133 134 int 135 av1394_ioctl_node_get_uid(av1394_inst_t *avp, void *arg, int mode) 136 { 137 cmd1394_cmd_t *cmd; 138 uint64_t eui64; 139 uint32_t hi, lo; 140 int err; 141 int ret = 0; 142 143 AV1394_TNF_ENTER(av1394_ioctl_node_get_uid); 144 145 err = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd); 146 if (err != DDI_SUCCESS) { 147 AV1394_TNF_EXIT(av1394_ioctl_node_get_uid); 148 return (ENOMEM); 149 } 150 151 AV1394_CFGROM_RQ(avp, cmd, AV1394_CFGROM_EUI64_HI_ADDR, &hi); 152 AV1394_CFGROM_RQ(avp, cmd, AV1394_CFGROM_EUI64_LO_ADDR, &lo); 153 154 eui64 = ((uint64_t)hi << 32) | lo; 155 if (ddi_copyout(&eui64, arg, sizeof (uint64_t), mode) != 0) { 156 ret = EFAULT; 157 } 158 159 catch: 160 err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd); 161 ASSERT(err == DDI_SUCCESS); 162 163 AV1394_TNF_EXIT(av1394_ioctl_node_get_uid); 164 return (ret); 165 } 166 167 int 168 av1394_ioctl_node_get_text_leaf(av1394_inst_t *avp, void *arg, int mode) 169 { 170 av1394_cfgrom_t *crp = &avp->av_a.a_cfgrom; 171 iec61883_node_text_leaf_t tl; 172 #ifdef _MULTI_DATAMODEL 173 iec61883_node_text_leaf32_t tl32; 174 #endif 175 int n; /* text leaf number requested */ 176 int parent; /* leaf parent */ 177 mblk_t *bp = NULL; 178 av1394_cfgrom_parsed_dir_t *pd; 179 int leaf_len; 180 uint32_t spec, lang_id, desc_entry; 181 int ret = 0; 182 183 AV1394_TNF_ENTER(av1394_ioctl_node_get_text_leaf); 184 185 /* copyin arguments */ 186 #ifdef _MULTI_DATAMODEL 187 if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) { 188 if (ddi_copyin(arg, &tl32, sizeof (tl32), mode) != 0) { 189 AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf); 190 return (EFAULT); 191 } 192 n = tl32.tl_num; 193 parent = tl32.tl_parent; 194 } else { 195 #endif 196 if (ddi_copyin(arg, &tl, sizeof (tl), mode) != 0) { 197 AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf); 198 return (EFAULT); 199 } 200 n = tl.tl_num; 201 parent = tl.tl_parent; 202 #ifdef _MULTI_DATAMODEL 203 } 204 #endif 205 /* verify arguments */ 206 if (((parent != IEC61883_ROM_ROOT) && (parent != IEC61883_ROM_UNIT)) || 207 (n < 0)) { 208 return (EINVAL); 209 } 210 211 /* parse ConfigROM if not already */ 212 rw_enter(&crp->cr_rwlock, RW_WRITER); 213 if (!crp->cr_parsed) { 214 ret = av1394_cfgrom_parse_rom(avp); 215 if (ret != 0) { 216 rw_exit(&crp->cr_rwlock); 217 AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf); 218 return (ret); 219 } 220 } 221 rw_downgrade(&crp->cr_rwlock); 222 223 /* get parsed leaf info */ 224 if (parent == IEC61883_ROM_ROOT) { 225 pd = &crp->cr_root_dir; 226 } else { 227 pd = &crp->cr_unit_dir; 228 } 229 230 if (n < pd->pd_tl_next) { 231 /* read the leaf */ 232 ret = av1394_cfgrom_read_leaf(avp, pd->pd_tl[n].tl_addr, &bp); 233 if (ret != 0) { 234 rw_exit(&crp->cr_rwlock); 235 AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf); 236 return (ret); 237 } 238 leaf_len = MBLKL(bp) / 4 - 2; 239 ASSERT(leaf_len > 0); 240 spec = *(uint32_t *)bp->b_rptr; 241 bp->b_rptr += 4; 242 lang_id = *(uint32_t *)bp->b_rptr; 243 bp->b_rptr += 4; 244 desc_entry = pd->pd_tl[n].tl_desc_entry; 245 } else { 246 /* return success anyway, but with tl_cnt < tl_num */ 247 spec = lang_id = desc_entry = 0; 248 leaf_len = 0; 249 } 250 251 /* copyout the results */ 252 #ifdef _MULTI_DATAMODEL 253 if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) { 254 tl32.tl_cnt = pd->pd_tl_next; 255 tl32.tl_desc_entry = desc_entry; 256 tl32.tl_rlen = leaf_len; 257 tl32.tl_spec = spec; 258 tl32.tl_lang_id = lang_id; 259 if (ddi_copyout(&tl32, arg, sizeof (tl32), mode) != 0) { 260 ret = EFAULT; 261 } else if (bp && ddi_copyout(bp->b_rptr, 262 (void *)(uintptr_t)tl32.tl_data, 263 4 * min(tl32.tl_len, tl32.tl_rlen), mode) != 0) { 264 ret = EFAULT; 265 } 266 } else { 267 #endif 268 tl.tl_cnt = pd->pd_tl_next; 269 tl.tl_desc_entry = desc_entry; 270 tl.tl_rlen = leaf_len; 271 tl.tl_spec = spec; 272 tl.tl_lang_id = lang_id; 273 if (ddi_copyout(&tl, arg, sizeof (tl), mode) != 0) { 274 ret = EFAULT; 275 } else if (bp && ddi_copyout(bp->b_rptr, tl.tl_data, 276 4 * min(tl.tl_len, tl.tl_rlen), mode) != 0) { 277 ret = EFAULT; 278 } 279 #ifdef _MULTI_DATAMODEL 280 } 281 #endif 282 rw_exit(&crp->cr_rwlock); 283 284 freemsg(bp); 285 286 AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf); 287 return (ret); 288 } 289 290 291 /* 292 * 293 * --- configROM parsing 294 * 295 * Parse entire configROM. Only extract information that interests us. 296 * ConfigROM integrity checks are only made to ensure correct parsing. 297 */ 298 static int 299 av1394_cfgrom_parse_rom(av1394_inst_t *avp) 300 { 301 av1394_cfgrom_t *crp = &avp->av_a.a_cfgrom; 302 cmd1394_cmd_t *cmd; 303 uint32_t val; 304 uint64_t root_addr; /* root dir address */ 305 uint16_t root_len; /* root dir length */ 306 av1394_cfgrom_parse_arg_t pa; 307 int err; 308 int ret; 309 310 ASSERT(crp->cr_parsed == B_FALSE); 311 312 err = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd); 313 if (err != DDI_SUCCESS) { 314 return (ENOMEM); 315 } 316 317 /* skip info_len quadlets to get root dir address and length */ 318 AV1394_CFGROM_RQ(avp, cmd, AV1394_CFGROM_INFO_LEN_ADDR, &val); 319 val = AV_SWAP32(val); 320 root_addr = IEEE1394_CONFIG_ROM_ADDR + 4 + (val >> 24) * 4; 321 AV1394_CFGROM_RQ(avp, cmd, root_addr, &val); 322 val = AV_SWAP32(val); 323 root_len = IEEE1212_DIR_LEN(val); 324 325 /* parse root dir and everything underneath */ 326 pa.pa_depth = 0; 327 pa.pa_desc_entry = 0; 328 pa.pa_parent_k = 0; 329 pa.pa_addr = root_addr + 4; 330 pa.pa_len = root_len; 331 pa.pa_dir = &crp->cr_root_dir; 332 333 ret = av1394_cfgrom_parse_dir(avp, cmd, &pa); 334 335 catch: 336 if (ret == 0) { 337 crp->cr_parsed = B_TRUE; 338 } else { 339 av1394_cfgrom_unparse_rom(avp); 340 } 341 err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd); 342 ASSERT(err == DDI_SUCCESS); 343 344 return (ret); 345 } 346 347 /* 348 * parse a directory 349 */ 350 static int 351 av1394_cfgrom_parse_dir(av1394_inst_t *avp, cmd1394_cmd_t *cmd, 352 av1394_cfgrom_parse_arg_t *pa) 353 { 354 av1394_cfgrom_t *crp = &avp->av_a.a_cfgrom; 355 int i; 356 uint64_t entry_addr; 357 uint32_t entry; 358 uint64_t leaf_addr; 359 uint64_t dir_addr; 360 uint16_t dir_len; 361 uint8_t t, k; 362 uint16_t v; 363 uint32_t val; 364 av1394_cfgrom_parse_arg_t this_pa; 365 int ret = 0; 366 367 /* safeguard against deep recursion */ 368 if (pa->pa_depth > AV1394_CFGROM_PARSE_MAX_DEPTH) { 369 return (ENOMEM); 370 } 371 372 /* initialize parse arguments */ 373 this_pa.pa_depth = pa->pa_depth + 1; 374 this_pa.pa_desc_entry = pa->pa_desc_entry; 375 376 /* walk dir entries */ 377 entry_addr = pa->pa_addr; 378 for (i = 0; i < pa->pa_len; i++) { 379 AV1394_CFGROM_RQ(avp, cmd, entry_addr, &entry); 380 entry = AV_SWAP32(entry); 381 382 CFGROM_TYPE_KEY_VALUE(entry, t, k, v); 383 if ((t == IEEE1212_LEAF_TYPE) && 384 (k == IEEE1212_TEXTUAL_DESCRIPTOR)) { 385 /* save this leaf */ 386 leaf_addr = entry_addr + 4 * v; 387 av1394_cfgrom_add_text_leaf(avp, pa->pa_dir, 388 leaf_addr, this_pa.pa_desc_entry); 389 } else if (t == IEEE1212_DIRECTORY_TYPE) { 390 dir_addr = entry_addr + 4 * v; 391 AV1394_CFGROM_RQ(avp, cmd, dir_addr, &val); 392 val = AV_SWAP32(val); 393 dir_len = IEEE1212_DIR_LEN(val); 394 395 /* parse this dir */ 396 this_pa.pa_parent_k = k; 397 this_pa.pa_addr = dir_addr + 4; 398 this_pa.pa_len = dir_len; 399 /* leaves will be added to either root or unit array */ 400 if (k == IEEE1212_UNIT_DIRECTORY) { 401 this_pa.pa_dir = &crp->cr_unit_dir; 402 } else { 403 this_pa.pa_dir = pa->pa_dir; 404 } 405 406 ret = av1394_cfgrom_parse_dir(avp, cmd, &this_pa); 407 if (ret != 0) { 408 goto catch; 409 } 410 } 411 412 /* 413 * if we're walking Textual_Descriptor directory, 414 * the described entry is the one preceding directory's entry, 415 * so we need to preserve what was passed in pa->pa_desc_entry 416 */ 417 if (pa->pa_parent_k != IEEE1212_TEXTUAL_DESCRIPTOR) { 418 this_pa.pa_desc_entry = entry; 419 } 420 entry_addr += 4; 421 } 422 423 catch: 424 return (ret); 425 } 426 427 /*ARGSUSED*/ 428 static void 429 av1394_cfgrom_add_text_leaf(av1394_inst_t *avp, av1394_cfgrom_parsed_dir_t *pd, 430 uint64_t addr, uint32_t desc_entry) 431 { 432 /* grow array of needed */ 433 if (pd->pd_tl_next >= pd->pd_tl_size) { 434 av1394_cfgrom_grow_parsed_dir(pd, 2); 435 } 436 pd->pd_tl[pd->pd_tl_next].tl_addr = addr; 437 pd->pd_tl[pd->pd_tl_next].tl_desc_entry = desc_entry; 438 pd->pd_tl_next++; 439 } 440 441 /* 442 * this routine cleans up after av1394_cfgrom_parse() 443 */ 444 static void 445 av1394_cfgrom_unparse_rom(av1394_inst_t *avp) 446 { 447 av1394_cfgrom_t *crp = &avp->av_a.a_cfgrom; 448 av1394_cfgrom_parsed_dir_t *pd; 449 450 pd = &crp->cr_root_dir; 451 if (pd->pd_tl) { 452 kmem_free(pd->pd_tl, pd->pd_tl_size * sizeof (*pd->pd_tl)); 453 bzero(pd, sizeof (*pd)); 454 } 455 pd = &crp->cr_unit_dir; 456 if (pd->pd_tl) { 457 kmem_free(pd->pd_tl, pd->pd_tl_size * sizeof (*pd->pd_tl)); 458 bzero(pd, sizeof (*pd)); 459 } 460 crp->cr_parsed = B_FALSE; 461 } 462 463 /* 464 * grow parsed dir leaf array by 'cnt' entries 465 */ 466 static void 467 av1394_cfgrom_grow_parsed_dir(av1394_cfgrom_parsed_dir_t *pd, int cnt) 468 { 469 int new_size; 470 void *new_tl; 471 472 ASSERT(cnt > 0); 473 474 new_size = (pd->pd_tl_size + cnt) * sizeof (av1394_cfgrom_text_leaf_t); 475 new_tl = kmem_zalloc(new_size, KM_SLEEP); 476 if (pd->pd_tl_size > 0) { 477 bcopy(pd->pd_tl, new_tl, pd->pd_tl_size * sizeof (*pd->pd_tl)); 478 kmem_free(pd->pd_tl, pd->pd_tl_size * sizeof (*pd->pd_tl)); 479 } 480 pd->pd_tl = new_tl; 481 pd->pd_tl_size += cnt; 482 } 483 484 static int 485 av1394_cfgrom_read_leaf(av1394_inst_t *avp, uint64_t leaf_addr, mblk_t **bpp) 486 { 487 cmd1394_cmd_t *cmd; 488 uint64_t addr; 489 uint32_t val; 490 int leaf_len; /* leaf length in quadlets */ 491 mblk_t *bp = NULL; 492 int i; 493 int err; 494 int ret = 0; 495 496 err = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd); 497 if (err != DDI_SUCCESS) { 498 AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf); 499 return (ENOMEM); 500 } 501 502 /* read leaf length */ 503 AV1394_CFGROM_RQ(avp, cmd, leaf_addr, &val); 504 val = AV_SWAP32(val); 505 leaf_len = IEEE1212_DIR_LEN(val); 506 507 if (leaf_len < 3) { 508 ret = EIO; 509 goto catch; 510 } 511 512 if ((bp = allocb(leaf_len * 4, BPRI_HI)) == NULL) { 513 TNF_PROBE_0(aav1394_cfgrom_read_leaf_error_allocb, 514 AV1394_TNF_ASYNC_ERROR, ""); 515 return (ENOMEM); 516 } 517 518 /* read leaf value */ 519 addr = leaf_addr + 4; 520 for (i = 0; i < leaf_len; i++) { 521 AV1394_CFGROM_RQ(avp, cmd, addr, (uint32_t *)bp->b_wptr); 522 bp->b_wptr += 4; 523 addr += 4; 524 } 525 526 catch: 527 if (ret == 0) { 528 *bpp = bp; 529 } else { 530 freemsg(bp); 531 } 532 err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd); 533 ASSERT(err == DDI_SUCCESS); 534 535 return (ret); 536 } 537 538 /* 539 * 540 * --- routines involving bus transactions 541 * 542 */ 543 static int 544 av1394_cfgrom_rq(av1394_inst_t *avp, cmd1394_cmd_t *cmd, uint64_t addr, 545 uint32_t *rval) 546 { 547 int err; 548 549 cmd->cmd_type = CMD1394_ASYNCH_RD_QUAD; 550 cmd->cmd_options = CMD1394_BLOCKING; 551 cmd->cmd_addr = addr; 552 553 err = t1394_read(avp->av_t1394_hdl, cmd); 554 if ((err == DDI_SUCCESS) && (cmd->cmd_result == CMD1394_CMDSUCCESS)) { 555 *rval = cmd->cmd_u.q.quadlet_data; 556 return (0); 557 } else { 558 TNF_PROBE_2(av1394_cfgrom_rq_error, 559 AV1394_TNF_ASYNC_ERROR, "", tnf_int, err, err, 560 tnf_int, result, cmd->cmd_result); 561 return (EIO); 562 } 563 } 564