xref: /illumos-gate/usr/src/lib/libipmi/common/ipmi_entity.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
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