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