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