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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * IPMI entities are a strange beast. A reasonable assumption for those 28 * unfamiliar with the spec would be that there was a command to iterate over 29 * all entities, and a command to iterate over sensors associated with each 30 * entity. Instead, the entire IPMI world is derived from the SDR repository. 31 * Entities only exist in the sense that they are referenced by a SDR record. 32 * 33 * In addition, entities can be associated into groups, and determining entity 34 * presence is quite complicated. The IPMI spec dedicates an entire chapter 35 * (40) to the process of handling sensor associations. 36 * 37 * The above logic is implemented via the ipmi_entity_present() function. We 38 * make a first pass over the SDR repository to discover entities, creating 39 * entity groups and associating SDR records with the each. 40 * 41 * We don't currently support device-relative entities. 42 */ 43 44 #include <libipmi.h> 45 #include <ipmi_impl.h> 46 #include <stddef.h> 47 48 typedef struct ipmi_entity_sdr { 49 ipmi_list_t ies_list; 50 const char *ies_name; 51 ipmi_sdr_t *ies_sdr; 52 } ipmi_entity_sdr_t; 53 54 typedef struct ipmi_entity_impl { 55 ipmi_list_t ie_list; 56 ipmi_entity_t ie_entity; 57 struct ipmi_entity_impl *ie_parent; 58 ipmi_hash_link_t ie_link; 59 ipmi_list_t ie_child_list; 60 ipmi_list_t ie_sdr_list; 61 } ipmi_entity_impl_t; 62 63 #define ENTITY_TO_IMPL(ep) \ 64 ((ipmi_entity_impl_t *)((char *)(ep) - \ 65 offsetof(ipmi_entity_impl_t, ie_entity))) 66 67 static int 68 ipmi_entity_add_assoc(ipmi_handle_t *ihp, ipmi_entity_impl_t *eip, 69 uint8_t id, uint8_t instance) 70 { 71 ipmi_entity_impl_t *cp; 72 ipmi_entity_t search; 73 74 search.ie_type = id; 75 search.ie_instance = instance; 76 77 if ((cp = ipmi_hash_lookup(ihp->ih_entities, &search)) == NULL) { 78 if ((cp = ipmi_zalloc(ihp, 79 sizeof (ipmi_entity_impl_t))) == NULL) 80 return (-1); 81 82 cp->ie_entity.ie_type = id; 83 cp->ie_entity.ie_instance = instance; 84 85 ipmi_hash_insert(ihp->ih_entities, cp); 86 } 87 88 if (cp->ie_parent != NULL) { 89 /* 90 * This should never happen. However, we want to be tolerant of 91 * pathologically broken IPMI implementations, so we ignore this 92 * error, and the first parent wins. 93 */ 94 return (0); 95 } 96 97 cp->ie_parent = eip; 98 ipmi_list_append(&eip->ie_child_list, cp); 99 eip->ie_entity.ie_children++; 100 101 return (0); 102 } 103 104 static int 105 ipmi_entity_sdr_parse(ipmi_sdr_t *sdrp, uint8_t *id, uint8_t *instance, 106 boolean_t *logical) 107 { 108 switch (sdrp->is_type) { 109 case IPMI_SDR_TYPE_FULL_SENSOR: 110 { 111 ipmi_sdr_full_sensor_t *fsp = 112 (ipmi_sdr_full_sensor_t *)sdrp->is_record; 113 *id = fsp->is_fs_entity_id; 114 *instance = fsp->is_fs_entity_instance; 115 *logical = fsp->is_fs_entity_logical; 116 break; 117 } 118 119 case IPMI_SDR_TYPE_COMPACT_SENSOR: 120 { 121 ipmi_sdr_compact_sensor_t *csp = 122 (ipmi_sdr_compact_sensor_t *)sdrp->is_record; 123 *id = csp->is_cs_entity_id; 124 *instance = csp->is_cs_entity_instance; 125 *logical = csp->is_cs_entity_logical; 126 break; 127 } 128 129 case IPMI_SDR_TYPE_EVENT_ONLY: 130 { 131 ipmi_sdr_event_only_t *eop = 132 (ipmi_sdr_event_only_t *)sdrp->is_record; 133 *id = eop->is_eo_entity_id; 134 *instance = eop->is_eo_entity_instance; 135 *logical = eop->is_eo_entity_logical; 136 break; 137 } 138 139 case IPMI_SDR_TYPE_ENTITY_ASSOCIATION: 140 { 141 ipmi_sdr_entity_association_t *eap = 142 (ipmi_sdr_entity_association_t *)sdrp->is_record; 143 *id = eap->is_ea_entity_id; 144 *instance = eap->is_ea_entity_instance; 145 *logical = B_TRUE; 146 break; 147 } 148 149 case IPMI_SDR_TYPE_GENERIC_LOCATOR: 150 { 151 ipmi_sdr_generic_locator_t *glp = 152 (ipmi_sdr_generic_locator_t *)sdrp->is_record; 153 *id = glp->is_gl_entity; 154 *instance = glp->is_gl_instance; 155 *logical = B_FALSE; 156 break; 157 } 158 159 case IPMI_SDR_TYPE_FRU_LOCATOR: 160 { 161 ipmi_sdr_fru_locator_t *flp = 162 (ipmi_sdr_fru_locator_t *)sdrp->is_record; 163 *id = flp->is_fl_entity; 164 *instance = flp->is_fl_instance; 165 *logical = B_FALSE; 166 break; 167 } 168 169 case IPMI_SDR_TYPE_MANAGEMENT_LOCATOR: 170 { 171 ipmi_sdr_management_locator_t *mlp = 172 (ipmi_sdr_management_locator_t *)sdrp->is_record; 173 *id = mlp->is_ml_entity_id; 174 *instance = mlp->is_ml_entity_instance; 175 *logical = B_FALSE; 176 break; 177 } 178 179 default: 180 return (-1); 181 } 182 183 return (0); 184 } 185 186 /* 187 * This function is responsible for gathering all entities, inserting them into 188 * the global hash, and establishing any associations. 189 */ 190 /*ARGSUSED*/ 191 static int 192 ipmi_entity_visit(ipmi_handle_t *ihp, const char *name, ipmi_sdr_t *sdrp, 193 void *unused) 194 { 195 uint8_t id, instance; 196 boolean_t logical; 197 ipmi_entity_t search; 198 ipmi_entity_impl_t *eip; 199 ipmi_entity_sdr_t *esp; 200 201 if (ipmi_entity_sdr_parse(sdrp, &id, &instance, &logical) != 0) 202 return (0); 203 204 search.ie_type = id; 205 search.ie_instance = instance; 206 207 if ((eip = ipmi_hash_lookup(ihp->ih_entities, &search)) == NULL) { 208 if ((eip = ipmi_zalloc(ihp, 209 sizeof (ipmi_entity_impl_t))) == NULL) 210 return (-1); 211 212 eip->ie_entity.ie_type = id; 213 eip->ie_entity.ie_instance = instance; 214 215 ipmi_hash_insert(ihp->ih_entities, eip); 216 } 217 218 eip->ie_entity.ie_logical |= logical; 219 220 if (sdrp->is_type == IPMI_SDR_TYPE_ENTITY_ASSOCIATION) { 221 uint8_t start, end; 222 uint8_t i, type; 223 224 ipmi_sdr_entity_association_t *eap = 225 (ipmi_sdr_entity_association_t *)sdrp->is_record; 226 227 if (eap->is_ea_range) { 228 229 type = eap->is_ea_sub[0].is_ea_sub_id; 230 start = eap->is_ea_sub[0].is_ea_sub_instance; 231 end = eap->is_ea_sub[1].is_ea_sub_instance; 232 233 if (type != 0) { 234 for (i = start; i <= end; i++) { 235 if (ipmi_entity_add_assoc(ihp, eip, 236 type, i) != 0) 237 return (-1); 238 } 239 } 240 241 type = eap->is_ea_sub[2].is_ea_sub_id; 242 start = eap->is_ea_sub[2].is_ea_sub_instance; 243 end = eap->is_ea_sub[3].is_ea_sub_instance; 244 245 if (type != 0) { 246 for (i = start; i <= end; i++) { 247 if (ipmi_entity_add_assoc(ihp, eip, 248 type, i) != 0) 249 return (-1); 250 } 251 } 252 } else { 253 for (i = 0; i < 4; i++) { 254 type = eap->is_ea_sub[i].is_ea_sub_id; 255 instance = eap->is_ea_sub[i].is_ea_sub_instance; 256 257 if (type == 0) 258 continue; 259 260 if (ipmi_entity_add_assoc(ihp, eip, type, 261 instance) != 0) 262 return (-1); 263 } 264 } 265 } else { 266 if ((esp = ipmi_zalloc(ihp, 267 sizeof (ipmi_entity_sdr_t))) == NULL) 268 return (-1); 269 270 esp->ies_sdr = sdrp; 271 esp->ies_name = name; 272 ipmi_list_append(&eip->ie_sdr_list, esp); 273 } 274 275 return (0); 276 } 277 278 /* 279 * Given a SDR record, return boolean values indicating whether the sensor 280 * indicates explicit presence. 281 * 282 * XXX this should really share code with entity_present() 283 */ 284 int 285 ipmi_entity_present_sdr(ipmi_handle_t *ihp, ipmi_sdr_t *sdrp, 286 boolean_t *valp) 287 { 288 uint16_t mask; 289 uint8_t number, sensor_type, reading_type; 290 ipmi_sdr_compact_sensor_t *csp; 291 ipmi_sdr_full_sensor_t *fsp; 292 ipmi_sensor_reading_t *srp; 293 294 switch (sdrp->is_type) { 295 case IPMI_SDR_TYPE_COMPACT_SENSOR: 296 csp = (ipmi_sdr_compact_sensor_t *)sdrp->is_record; 297 number = csp->is_cs_number; 298 sensor_type = csp->is_cs_type; 299 reading_type = csp->is_cs_reading_type; 300 break; 301 302 case IPMI_SDR_TYPE_FULL_SENSOR: 303 fsp = (ipmi_sdr_full_sensor_t *)sdrp->is_record; 304 number = fsp->is_fs_number; 305 sensor_type = fsp->is_fs_type; 306 reading_type = fsp->is_fs_reading_type; 307 break; 308 309 default: 310 *valp = B_FALSE; 311 return (0); 312 } 313 314 switch (reading_type) { 315 case IPMI_RT_PRESENT: 316 mask = IPMI_SR_PRESENT_ASSERT; 317 break; 318 319 case IPMI_RT_SPECIFIC: 320 switch (sensor_type) { 321 case IPMI_ST_PROCESSOR: 322 mask = IPMI_EV_PROCESSOR_PRESENT; 323 break; 324 325 case IPMI_ST_POWER_SUPPLY: 326 mask = IPMI_EV_POWER_SUPPLY_PRESENT; 327 break; 328 329 case IPMI_ST_MEMORY: 330 mask = IPMI_EV_MEMORY_PRESENT; 331 break; 332 333 case IPMI_ST_BAY: 334 mask = IPMI_EV_BAY_PRESENT; 335 break; 336 337 default: 338 *valp = B_FALSE; 339 return (0); 340 } 341 break; 342 343 default: 344 *valp = B_FALSE; 345 return (0); 346 } 347 348 /* 349 * If we've reached here, then we have a dedicated sensor that 350 * indicates presence. 351 */ 352 if ((srp = ipmi_get_sensor_reading(ihp, number)) == NULL) { 353 if (ipmi_errno(ihp) == EIPMI_NOT_PRESENT) { 354 *valp = B_FALSE; 355 return (0); 356 } 357 358 return (-1); 359 } 360 361 *valp = (srp->isr_state & mask) != 0; 362 return (0); 363 } 364 365 /* 366 * This function follows the procedure documented in section 40 of the spec. 367 * To quote the conclusion from section 40.2: 368 * 369 * Thus, the steps to detecting an Entity are: 370 * 371 * a) Scan the SDRs for sensors associated with the entity. 372 * 373 * b) If there is an active sensor that includes a presence bit, or the 374 * entity has an active Entity Presence sensor, use the sensor to 375 * determine the presence of the entity. 376 * 377 * c) Otherwise, check to see that there is at least one active sensor 378 * associated with the entity. Do this by doing 'Get Sensor Readings' 379 * to the sensors associated with the entity until a scanning sensor is 380 * found. 381 * 382 * d) If there are no active sensors directly associated with the entity, 383 * check the SDRs to see if the entity is a container entity in an 384 * entity-association. If so, check to see if any of the contained 385 * entities are present, if so, assume the container entity exists. 386 * Note that this may need to be iterative, since it's possible to have 387 * multi-level entity associations. 388 * 389 * e) If there are no active sensors for the entity, and the entity is not 390 * the container entity in an active entity-assocation, then the entity 391 * is present if (sic) there there is a FRU device for the entity, and 392 * the FRU device is present. 393 * 394 * It should not be considered an error if a FRU device locator record is 395 * present for a FRU device, but the FRU device is not there. 396 * 397 */ 398 int 399 ipmi_entity_present(ipmi_handle_t *ihp, ipmi_entity_t *ep, boolean_t *valp) 400 { 401 /* LINTED - alignment */ 402 ipmi_entity_impl_t *eip = ENTITY_TO_IMPL(ep); 403 ipmi_entity_impl_t *cp; 404 ipmi_entity_sdr_t *esp; 405 ipmi_sdr_t *sdrp; 406 uint16_t mask; 407 uint8_t number, sensor_type, reading_type; 408 ipmi_sensor_reading_t *srp; 409 ipmi_sdr_compact_sensor_t *csp; 410 ipmi_sdr_full_sensor_t *fsp; 411 ipmi_sdr_fru_locator_t *frup; 412 char *frudata; 413 414 /* 415 * Search the sensors for a present sensor or a discrete sensor that 416 * indicates presence. 417 */ 418 for (esp = ipmi_list_next(&eip->ie_sdr_list); esp != NULL; 419 esp = ipmi_list_next(esp)) { 420 sdrp = esp->ies_sdr; 421 switch (sdrp->is_type) { 422 case IPMI_SDR_TYPE_COMPACT_SENSOR: 423 csp = (ipmi_sdr_compact_sensor_t *)sdrp->is_record; 424 number = csp->is_cs_number; 425 sensor_type = csp->is_cs_type; 426 reading_type = csp->is_cs_reading_type; 427 break; 428 429 case IPMI_SDR_TYPE_FULL_SENSOR: 430 fsp = (ipmi_sdr_full_sensor_t *)sdrp->is_record; 431 number = fsp->is_fs_number; 432 sensor_type = fsp->is_fs_type; 433 reading_type = fsp->is_fs_reading_type; 434 break; 435 436 default: 437 continue; 438 } 439 440 switch (reading_type) { 441 case IPMI_RT_PRESENT: 442 mask = IPMI_SR_PRESENT_ASSERT; 443 break; 444 445 case IPMI_RT_SPECIFIC: 446 switch (sensor_type) { 447 case IPMI_ST_PROCESSOR: 448 mask = IPMI_EV_PROCESSOR_PRESENT; 449 break; 450 451 case IPMI_ST_POWER_SUPPLY: 452 mask = IPMI_EV_POWER_SUPPLY_PRESENT; 453 break; 454 455 case IPMI_ST_MEMORY: 456 mask = IPMI_EV_MEMORY_PRESENT; 457 break; 458 459 case IPMI_ST_BAY: 460 mask = IPMI_EV_BAY_PRESENT; 461 break; 462 463 default: 464 continue; 465 } 466 break; 467 468 default: 469 continue; 470 } 471 472 /* 473 * If we've reached here, then we have a dedicated sensor that 474 * indicates presence. 475 */ 476 if ((srp = ipmi_get_sensor_reading(ihp, number)) == NULL) { 477 if (ipmi_errno(ihp) == EIPMI_NOT_PRESENT) { 478 *valp = B_FALSE; 479 return (0); 480 } 481 482 return (-1); 483 } 484 485 *valp = (srp->isr_state & mask) != 0; 486 return (0); 487 } 488 489 /* 490 * No explicit presence sensor was found. See if there is at least one 491 * active sensor associated with the entity. 492 */ 493 for (esp = ipmi_list_next(&eip->ie_sdr_list); esp != NULL; 494 esp = ipmi_list_next(esp)) { 495 sdrp = esp->ies_sdr; 496 switch (sdrp->is_type) { 497 case IPMI_SDR_TYPE_COMPACT_SENSOR: 498 csp = (ipmi_sdr_compact_sensor_t *)sdrp->is_record; 499 number = csp->is_cs_number; 500 break; 501 502 case IPMI_SDR_TYPE_FULL_SENSOR: 503 fsp = (ipmi_sdr_full_sensor_t *)sdrp->is_record; 504 number = fsp->is_fs_number; 505 break; 506 507 default: 508 continue; 509 } 510 511 if ((srp = ipmi_get_sensor_reading(ihp, number)) == NULL) { 512 if (ipmi_errno(ihp) == EIPMI_NOT_PRESENT) 513 continue; 514 515 return (-1); 516 } 517 518 if (srp->isr_scanning_enabled) { 519 *valp = B_TRUE; 520 return (0); 521 } 522 } 523 524 /* 525 * If this entity has children, then it is present if any of its 526 * children are present. 527 */ 528 for (cp = ipmi_list_next(&eip->ie_child_list); cp != NULL; 529 cp = ipmi_list_next(cp)) { 530 if (ipmi_entity_present(ihp, &cp->ie_entity, valp) != 0) 531 return (-1); 532 533 if (*valp) 534 return (0); 535 } 536 537 /* 538 * If the FRU device is present, then the entity is present. 539 */ 540 for (esp = ipmi_list_next(&eip->ie_sdr_list); esp != NULL; 541 esp = ipmi_list_next(esp)) { 542 sdrp = esp->ies_sdr; 543 if (sdrp->is_type != IPMI_SDR_TYPE_FRU_LOCATOR) 544 continue; 545 546 frup = (ipmi_sdr_fru_locator_t *)sdrp->is_record; 547 if (ipmi_fru_read(ihp, frup, &frudata) >= 0) { 548 ipmi_free(ihp, frudata); 549 *valp = B_TRUE; 550 return (0); 551 } 552 553 if (ipmi_errno(ihp) != EIPMI_NOT_PRESENT) 554 return (-1); 555 } 556 557 *valp = B_FALSE; 558 return (0); 559 } 560 561 static int 562 ipmi_entity_refresh(ipmi_handle_t *ihp) 563 { 564 if (ipmi_hash_first(ihp->ih_entities) != NULL && 565 !ipmi_sdr_changed(ihp)) 566 return (0); 567 568 if (ipmi_sdr_iter(ihp, ipmi_entity_visit, NULL) != 0) 569 return (-1); 570 571 return (0); 572 } 573 574 int 575 ipmi_entity_iter(ipmi_handle_t *ihp, int (*func)(ipmi_handle_t *, 576 ipmi_entity_t *, void *), void *data) 577 { 578 ipmi_entity_impl_t *eip; 579 int ret; 580 581 if (ipmi_entity_refresh(ihp) != 0) 582 return (-1); 583 584 for (eip = ipmi_hash_first(ihp->ih_entities); eip != NULL; 585 eip = ipmi_hash_next(ihp->ih_entities, eip)) { 586 if (eip->ie_parent != NULL) 587 continue; 588 589 if ((ret = func(ihp, &eip->ie_entity, data)) != 0) 590 return (ret); 591 } 592 593 return (0); 594 } 595 596 int 597 ipmi_entity_iter_sdr(ipmi_handle_t *ihp, ipmi_entity_t *ep, 598 int (*func)(ipmi_handle_t *, ipmi_entity_t *, const char *, ipmi_sdr_t *, 599 void *), void *data) 600 { 601 /* LINTED - alignment */ 602 ipmi_entity_impl_t *eip = ENTITY_TO_IMPL(ep); 603 ipmi_entity_sdr_t *isp; 604 int ret; 605 606 for (isp = ipmi_list_next(&eip->ie_sdr_list); isp != NULL; 607 isp = ipmi_list_next(isp)) { 608 if ((ret = func(ihp, ep, isp->ies_name, 609 isp->ies_sdr, data)) != 0) 610 return (ret); 611 } 612 613 return (0); 614 } 615 616 int 617 ipmi_entity_iter_children(ipmi_handle_t *ihp, ipmi_entity_t *ep, 618 int (*func)(ipmi_handle_t *, ipmi_entity_t *, void *), void *data) 619 { 620 /* LINTED - alignment */ 621 ipmi_entity_impl_t *eip = ENTITY_TO_IMPL(ep); 622 ipmi_entity_impl_t *cp; 623 int ret; 624 625 for (cp = ipmi_list_next(&eip->ie_child_list); cp != NULL; 626 cp = ipmi_list_next(cp)) { 627 if ((ret = func(ihp, &cp->ie_entity, data)) != 0) 628 return (ret); 629 } 630 631 return (0); 632 } 633 634 ipmi_entity_t * 635 ipmi_entity_parent(ipmi_handle_t *ihp, ipmi_entity_t *ep) 636 { 637 /* LINTED - alignment */ 638 ipmi_entity_impl_t *eip = ENTITY_TO_IMPL(ep); 639 640 if (eip->ie_parent == NULL) { 641 (void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL); 642 return (NULL); 643 } 644 645 return (&eip->ie_parent->ie_entity); 646 } 647 648 ipmi_entity_t * 649 ipmi_entity_lookup(ipmi_handle_t *ihp, uint8_t type, uint8_t instance) 650 { 651 ipmi_entity_t search; 652 ipmi_entity_impl_t *eip; 653 654 if (ipmi_entity_refresh(ihp) != 0) 655 return (NULL); 656 657 search.ie_type = type; 658 search.ie_instance = instance; 659 660 if ((eip = ipmi_hash_lookup(ihp->ih_entities, &search)) == NULL) { 661 (void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL); 662 return (NULL); 663 } 664 665 return (&eip->ie_entity); 666 } 667 668 ipmi_entity_t * 669 ipmi_entity_lookup_sdr(ipmi_handle_t *ihp, const char *name) 670 { 671 ipmi_sdr_t *sdrp; 672 uint8_t id, instance; 673 boolean_t logical; 674 675 if ((sdrp = ipmi_sdr_lookup(ihp, name)) == NULL) 676 return (NULL); 677 678 if (ipmi_entity_sdr_parse(sdrp, &id, &instance, &logical) != 0) { 679 (void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, 680 "SDR record %s has no associated entity", name); 681 return (NULL); 682 } 683 684 return (ipmi_entity_lookup(ihp, id, instance)); 685 } 686 687 static const void * 688 ipmi_entity_hash_convert(const void *p) 689 { 690 const ipmi_entity_impl_t *eip = p; 691 692 return (&eip->ie_entity); 693 } 694 695 static ulong_t 696 ipmi_entity_hash_compute(const void *p) 697 { 698 const ipmi_entity_t *ep = p; 699 700 return ((ep->ie_type << 8) | ep->ie_instance); 701 } 702 703 static int 704 ipmi_entity_hash_compare(const void *a, const void *b) 705 { 706 const ipmi_entity_t *ea = a; 707 const ipmi_entity_t *eb = b; 708 709 if (ea->ie_type == eb->ie_type && 710 ea->ie_instance == eb->ie_instance) 711 return (0); 712 else 713 return (-1); 714 } 715 716 int 717 ipmi_entity_init(ipmi_handle_t *ihp) 718 { 719 if ((ihp->ih_entities = ipmi_hash_create(ihp, 720 offsetof(ipmi_entity_impl_t, ie_link), 721 ipmi_entity_hash_convert, 722 ipmi_entity_hash_compute, 723 ipmi_entity_hash_compare)) == NULL) 724 return (-1); 725 726 return (0); 727 } 728 729 void 730 ipmi_entity_clear(ipmi_handle_t *ihp) 731 { 732 ipmi_entity_impl_t *eip; 733 ipmi_entity_sdr_t *esp; 734 735 while ((eip = ipmi_hash_first(ihp->ih_entities)) != NULL) { 736 while ((esp = ipmi_list_next(&eip->ie_sdr_list)) != NULL) { 737 ipmi_list_delete(&eip->ie_sdr_list, esp); 738 ipmi_free(ihp, esp); 739 } 740 ipmi_hash_remove(ihp->ih_entities, eip); 741 ipmi_free(ihp, eip); 742 } 743 } 744 745 void 746 ipmi_entity_fini(ipmi_handle_t *ihp) 747 { 748 if (ihp->ih_entities != NULL) { 749 ipmi_entity_clear(ihp); 750 ipmi_hash_destroy(ihp->ih_entities); 751 } 752 } 753