/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * IPMI entities are a strange beast. A reasonable assumption for those * unfamiliar with the spec would be that there was a command to iterate over * all entities, and a command to iterate over sensors associated with each * entity. Instead, the entire IPMI world is derived from the SDR repository. * Entities only exist in the sense that they are referenced by a SDR record. * * In addition, entities can be associated into groups, and determining entity * presence is quite complicated. The IPMI spec dedicates an entire chapter * (40) to the process of handling sensor associations. * * The above logic is implemented via the ipmi_entity_present() function. We * make a first pass over the SDR repository to discover entities, creating * entity groups and associating SDR records with the each. * * We don't currently support device-relative entities. */ #include #include #include typedef struct ipmi_entity_sdr { ipmi_list_t ies_list; const char *ies_name; ipmi_sdr_t *ies_sdr; } ipmi_entity_sdr_t; typedef struct ipmi_entity_impl { ipmi_list_t ie_list; ipmi_entity_t ie_entity; struct ipmi_entity_impl *ie_parent; ipmi_hash_link_t ie_link; ipmi_list_t ie_child_list; ipmi_list_t ie_sdr_list; } ipmi_entity_impl_t; #define ENTITY_TO_IMPL(ep) \ ((ipmi_entity_impl_t *)((char *)(ep) - \ offsetof(ipmi_entity_impl_t, ie_entity))) static int ipmi_entity_add_assoc(ipmi_handle_t *ihp, ipmi_entity_impl_t *eip, uint8_t id, uint8_t instance) { ipmi_entity_impl_t *cp; ipmi_entity_t search; search.ie_type = id; search.ie_instance = instance; if ((cp = ipmi_hash_lookup(ihp->ih_entities, &search)) == NULL) { if ((cp = ipmi_zalloc(ihp, sizeof (ipmi_entity_impl_t))) == NULL) return (-1); cp->ie_entity.ie_type = id; cp->ie_entity.ie_instance = instance; ipmi_hash_insert(ihp->ih_entities, cp); } if (cp->ie_parent != NULL) { /* * This should never happen. However, we want to be tolerant of * pathologically broken IPMI implementations, so we ignore this * error, and the first parent wins. */ return (0); } cp->ie_parent = eip; ipmi_list_append(&eip->ie_child_list, cp); eip->ie_entity.ie_children++; return (0); } static int ipmi_entity_sdr_parse(ipmi_sdr_t *sdrp, uint8_t *id, uint8_t *instance, boolean_t *logical) { switch (sdrp->is_type) { case IPMI_SDR_TYPE_FULL_SENSOR: { ipmi_sdr_full_sensor_t *fsp = (ipmi_sdr_full_sensor_t *)sdrp->is_record; *id = fsp->is_fs_entity_id; *instance = fsp->is_fs_entity_instance; *logical = fsp->is_fs_entity_logical; break; } case IPMI_SDR_TYPE_COMPACT_SENSOR: { ipmi_sdr_compact_sensor_t *csp = (ipmi_sdr_compact_sensor_t *)sdrp->is_record; *id = csp->is_cs_entity_id; *instance = csp->is_cs_entity_instance; *logical = csp->is_cs_entity_logical; break; } case IPMI_SDR_TYPE_EVENT_ONLY: { ipmi_sdr_event_only_t *eop = (ipmi_sdr_event_only_t *)sdrp->is_record; *id = eop->is_eo_entity_id; *instance = eop->is_eo_entity_instance; *logical = eop->is_eo_entity_logical; break; } case IPMI_SDR_TYPE_ENTITY_ASSOCIATION: { ipmi_sdr_entity_association_t *eap = (ipmi_sdr_entity_association_t *)sdrp->is_record; *id = eap->is_ea_entity_id; *instance = eap->is_ea_entity_instance; *logical = B_TRUE; break; } case IPMI_SDR_TYPE_GENERIC_LOCATOR: { ipmi_sdr_generic_locator_t *glp = (ipmi_sdr_generic_locator_t *)sdrp->is_record; *id = glp->is_gl_entity; *instance = glp->is_gl_instance; *logical = B_FALSE; break; } case IPMI_SDR_TYPE_FRU_LOCATOR: { ipmi_sdr_fru_locator_t *flp = (ipmi_sdr_fru_locator_t *)sdrp->is_record; *id = flp->is_fl_entity; *instance = flp->is_fl_instance; *logical = B_FALSE; break; } case IPMI_SDR_TYPE_MANAGEMENT_LOCATOR: { ipmi_sdr_management_locator_t *mlp = (ipmi_sdr_management_locator_t *)sdrp->is_record; *id = mlp->is_ml_entity_id; *instance = mlp->is_ml_entity_instance; *logical = B_FALSE; break; } default: return (-1); } return (0); } /* * This function is responsible for gathering all entities, inserting them into * the global hash, and establishing any associations. */ /*ARGSUSED*/ static int ipmi_entity_visit(ipmi_handle_t *ihp, const char *name, ipmi_sdr_t *sdrp, void *unused) { uint8_t id, instance; boolean_t logical; ipmi_entity_t search; ipmi_entity_impl_t *eip; ipmi_entity_sdr_t *esp; if (ipmi_entity_sdr_parse(sdrp, &id, &instance, &logical) != 0) return (0); search.ie_type = id; search.ie_instance = instance; if ((eip = ipmi_hash_lookup(ihp->ih_entities, &search)) == NULL) { if ((eip = ipmi_zalloc(ihp, sizeof (ipmi_entity_impl_t))) == NULL) return (-1); eip->ie_entity.ie_type = id; eip->ie_entity.ie_instance = instance; ipmi_hash_insert(ihp->ih_entities, eip); } eip->ie_entity.ie_logical |= logical; if (sdrp->is_type == IPMI_SDR_TYPE_ENTITY_ASSOCIATION) { uint8_t start, end; uint8_t i, type; ipmi_sdr_entity_association_t *eap = (ipmi_sdr_entity_association_t *)sdrp->is_record; if (eap->is_ea_range) { type = eap->is_ea_sub[0].is_ea_sub_id; start = eap->is_ea_sub[0].is_ea_sub_instance; end = eap->is_ea_sub[1].is_ea_sub_instance; if (type != 0) { for (i = start; i <= end; i++) { if (ipmi_entity_add_assoc(ihp, eip, type, i) != 0) return (-1); } } type = eap->is_ea_sub[2].is_ea_sub_id; start = eap->is_ea_sub[2].is_ea_sub_instance; end = eap->is_ea_sub[3].is_ea_sub_instance; if (type != 0) { for (i = start; i <= end; i++) { if (ipmi_entity_add_assoc(ihp, eip, type, i) != 0) return (-1); } } } else { for (i = 0; i < 4; i++) { type = eap->is_ea_sub[i].is_ea_sub_id; instance = eap->is_ea_sub[i].is_ea_sub_instance; if (type == 0) continue; if (ipmi_entity_add_assoc(ihp, eip, type, instance) != 0) return (-1); } } } else { if ((esp = ipmi_zalloc(ihp, sizeof (ipmi_entity_sdr_t))) == NULL) return (-1); esp->ies_sdr = sdrp; esp->ies_name = name; ipmi_list_append(&eip->ie_sdr_list, esp); } return (0); } /* * Given a SDR record, return boolean values indicating whether the sensor * indicates explicit presence. * * XXX this should really share code with entity_present() */ int ipmi_entity_present_sdr(ipmi_handle_t *ihp, ipmi_sdr_t *sdrp, boolean_t *valp) { uint16_t mask; uint8_t number, sensor_type, reading_type; ipmi_sdr_compact_sensor_t *csp; ipmi_sdr_full_sensor_t *fsp; ipmi_sensor_reading_t *srp; switch (sdrp->is_type) { case IPMI_SDR_TYPE_COMPACT_SENSOR: csp = (ipmi_sdr_compact_sensor_t *)sdrp->is_record; number = csp->is_cs_number; sensor_type = csp->is_cs_type; reading_type = csp->is_cs_reading_type; break; case IPMI_SDR_TYPE_FULL_SENSOR: fsp = (ipmi_sdr_full_sensor_t *)sdrp->is_record; number = fsp->is_fs_number; sensor_type = fsp->is_fs_type; reading_type = fsp->is_fs_reading_type; break; default: *valp = B_FALSE; return (0); } switch (reading_type) { case IPMI_RT_PRESENT: mask = IPMI_SR_PRESENT_ASSERT; break; case IPMI_RT_SPECIFIC: switch (sensor_type) { case IPMI_ST_PROCESSOR: mask = IPMI_EV_PROCESSOR_PRESENT; break; case IPMI_ST_POWER_SUPPLY: mask = IPMI_EV_POWER_SUPPLY_PRESENT; break; case IPMI_ST_MEMORY: mask = IPMI_EV_MEMORY_PRESENT; break; case IPMI_ST_BAY: mask = IPMI_EV_BAY_PRESENT; break; default: *valp = B_FALSE; return (0); } break; default: *valp = B_FALSE; return (0); } /* * If we've reached here, then we have a dedicated sensor that * indicates presence. */ if ((srp = ipmi_get_sensor_reading(ihp, number)) == NULL) { if (ipmi_errno(ihp) == EIPMI_NOT_PRESENT) { *valp = B_FALSE; return (0); } return (-1); } *valp = (srp->isr_state & mask) != 0; return (0); } /* * This function follows the procedure documented in section 40 of the spec. * To quote the conclusion from section 40.2: * * Thus, the steps to detecting an Entity are: * * a) Scan the SDRs for sensors associated with the entity. * * b) If there is an active sensor that includes a presence bit, or the * entity has an active Entity Presence sensor, use the sensor to * determine the presence of the entity. * * c) Otherwise, check to see that there is at least one active sensor * associated with the entity. Do this by doing 'Get Sensor Readings' * to the sensors associated with the entity until a scanning sensor is * found. * * d) If there are no active sensors directly associated with the entity, * check the SDRs to see if the entity is a container entity in an * entity-association. If so, check to see if any of the contained * entities are present, if so, assume the container entity exists. * Note that this may need to be iterative, since it's possible to have * multi-level entity associations. * * e) If there are no active sensors for the entity, and the entity is not * the container entity in an active entity-assocation, then the entity * is present if (sic) there there is a FRU device for the entity, and * the FRU device is present. * * It should not be considered an error if a FRU device locator record is * present for a FRU device, but the FRU device is not there. * */ int ipmi_entity_present(ipmi_handle_t *ihp, ipmi_entity_t *ep, boolean_t *valp) { /* LINTED - alignment */ ipmi_entity_impl_t *eip = ENTITY_TO_IMPL(ep); ipmi_entity_impl_t *cp; ipmi_entity_sdr_t *esp; ipmi_sdr_t *sdrp; uint16_t mask; uint8_t number, sensor_type, reading_type; ipmi_sensor_reading_t *srp; ipmi_sdr_compact_sensor_t *csp; ipmi_sdr_full_sensor_t *fsp; ipmi_sdr_fru_locator_t *frup; char *frudata; /* * Search the sensors for a present sensor or a discrete sensor that * indicates presence. */ for (esp = ipmi_list_next(&eip->ie_sdr_list); esp != NULL; esp = ipmi_list_next(esp)) { sdrp = esp->ies_sdr; switch (sdrp->is_type) { case IPMI_SDR_TYPE_COMPACT_SENSOR: csp = (ipmi_sdr_compact_sensor_t *)sdrp->is_record; number = csp->is_cs_number; sensor_type = csp->is_cs_type; reading_type = csp->is_cs_reading_type; break; case IPMI_SDR_TYPE_FULL_SENSOR: fsp = (ipmi_sdr_full_sensor_t *)sdrp->is_record; number = fsp->is_fs_number; sensor_type = fsp->is_fs_type; reading_type = fsp->is_fs_reading_type; break; default: continue; } switch (reading_type) { case IPMI_RT_PRESENT: mask = IPMI_SR_PRESENT_ASSERT; break; case IPMI_RT_SPECIFIC: switch (sensor_type) { case IPMI_ST_PROCESSOR: mask = IPMI_EV_PROCESSOR_PRESENT; break; case IPMI_ST_POWER_SUPPLY: mask = IPMI_EV_POWER_SUPPLY_PRESENT; break; case IPMI_ST_MEMORY: mask = IPMI_EV_MEMORY_PRESENT; break; case IPMI_ST_BAY: mask = IPMI_EV_BAY_PRESENT; break; default: continue; } break; default: continue; } /* * If we've reached here, then we have a dedicated sensor that * indicates presence. */ if ((srp = ipmi_get_sensor_reading(ihp, number)) == NULL) { if (ipmi_errno(ihp) == EIPMI_NOT_PRESENT) { *valp = B_FALSE; return (0); } return (-1); } *valp = (srp->isr_state & mask) != 0; return (0); } /* * No explicit presence sensor was found. See if there is at least one * active sensor associated with the entity. */ for (esp = ipmi_list_next(&eip->ie_sdr_list); esp != NULL; esp = ipmi_list_next(esp)) { sdrp = esp->ies_sdr; switch (sdrp->is_type) { case IPMI_SDR_TYPE_COMPACT_SENSOR: csp = (ipmi_sdr_compact_sensor_t *)sdrp->is_record; number = csp->is_cs_number; break; case IPMI_SDR_TYPE_FULL_SENSOR: fsp = (ipmi_sdr_full_sensor_t *)sdrp->is_record; number = fsp->is_fs_number; break; default: continue; } if ((srp = ipmi_get_sensor_reading(ihp, number)) == NULL) { if (ipmi_errno(ihp) == EIPMI_NOT_PRESENT) continue; return (-1); } if (srp->isr_scanning_enabled) { *valp = B_TRUE; return (0); } } /* * If this entity has children, then it is present if any of its * children are present. */ for (cp = ipmi_list_next(&eip->ie_child_list); cp != NULL; cp = ipmi_list_next(cp)) { if (ipmi_entity_present(ihp, &cp->ie_entity, valp) != 0) return (-1); if (*valp) return (0); } /* * If the FRU device is present, then the entity is present. */ for (esp = ipmi_list_next(&eip->ie_sdr_list); esp != NULL; esp = ipmi_list_next(esp)) { sdrp = esp->ies_sdr; if (sdrp->is_type != IPMI_SDR_TYPE_FRU_LOCATOR) continue; frup = (ipmi_sdr_fru_locator_t *)sdrp->is_record; if (ipmi_fru_read(ihp, frup, &frudata) >= 0) { ipmi_free(ihp, frudata); *valp = B_TRUE; return (0); } if (ipmi_errno(ihp) != EIPMI_NOT_PRESENT) return (-1); } *valp = B_FALSE; return (0); } static int ipmi_entity_refresh(ipmi_handle_t *ihp) { if (ipmi_hash_first(ihp->ih_entities) != NULL && !ipmi_sdr_changed(ihp)) return (0); if (ipmi_sdr_iter(ihp, ipmi_entity_visit, NULL) != 0) return (-1); return (0); } int ipmi_entity_iter(ipmi_handle_t *ihp, int (*func)(ipmi_handle_t *, ipmi_entity_t *, void *), void *data) { ipmi_entity_impl_t *eip; int ret; if (ipmi_entity_refresh(ihp) != 0) return (-1); for (eip = ipmi_hash_first(ihp->ih_entities); eip != NULL; eip = ipmi_hash_next(ihp->ih_entities, eip)) { if (eip->ie_parent != NULL) continue; if ((ret = func(ihp, &eip->ie_entity, data)) != 0) return (ret); } return (0); } int ipmi_entity_iter_sdr(ipmi_handle_t *ihp, ipmi_entity_t *ep, int (*func)(ipmi_handle_t *, ipmi_entity_t *, const char *, ipmi_sdr_t *, void *), void *data) { /* LINTED - alignment */ ipmi_entity_impl_t *eip = ENTITY_TO_IMPL(ep); ipmi_entity_sdr_t *isp; int ret; for (isp = ipmi_list_next(&eip->ie_sdr_list); isp != NULL; isp = ipmi_list_next(isp)) { if ((ret = func(ihp, ep, isp->ies_name, isp->ies_sdr, data)) != 0) return (ret); } return (0); } int ipmi_entity_iter_children(ipmi_handle_t *ihp, ipmi_entity_t *ep, int (*func)(ipmi_handle_t *, ipmi_entity_t *, void *), void *data) { /* LINTED - alignment */ ipmi_entity_impl_t *eip = ENTITY_TO_IMPL(ep); ipmi_entity_impl_t *cp; int ret; for (cp = ipmi_list_next(&eip->ie_child_list); cp != NULL; cp = ipmi_list_next(cp)) { if ((ret = func(ihp, &cp->ie_entity, data)) != 0) return (ret); } return (0); } ipmi_entity_t * ipmi_entity_parent(ipmi_handle_t *ihp, ipmi_entity_t *ep) { /* LINTED - alignment */ ipmi_entity_impl_t *eip = ENTITY_TO_IMPL(ep); if (eip->ie_parent == NULL) { (void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL); return (NULL); } return (&eip->ie_parent->ie_entity); } ipmi_entity_t * ipmi_entity_lookup(ipmi_handle_t *ihp, uint8_t type, uint8_t instance) { ipmi_entity_t search; ipmi_entity_impl_t *eip; if (ipmi_entity_refresh(ihp) != 0) return (NULL); search.ie_type = type; search.ie_instance = instance; if ((eip = ipmi_hash_lookup(ihp->ih_entities, &search)) == NULL) { (void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL); return (NULL); } return (&eip->ie_entity); } ipmi_entity_t * ipmi_entity_lookup_sdr(ipmi_handle_t *ihp, const char *name) { ipmi_sdr_t *sdrp; uint8_t id, instance; boolean_t logical; if ((sdrp = ipmi_sdr_lookup(ihp, name)) == NULL) return (NULL); if (ipmi_entity_sdr_parse(sdrp, &id, &instance, &logical) != 0) { (void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, "SDR record %s has no associated entity", name); return (NULL); } return (ipmi_entity_lookup(ihp, id, instance)); } static const void * ipmi_entity_hash_convert(const void *p) { const ipmi_entity_impl_t *eip = p; return (&eip->ie_entity); } static ulong_t ipmi_entity_hash_compute(const void *p) { const ipmi_entity_t *ep = p; return ((ep->ie_type << 8) | ep->ie_instance); } static int ipmi_entity_hash_compare(const void *a, const void *b) { const ipmi_entity_t *ea = a; const ipmi_entity_t *eb = b; if (ea->ie_type == eb->ie_type && ea->ie_instance == eb->ie_instance) return (0); else return (-1); } int ipmi_entity_init(ipmi_handle_t *ihp) { if ((ihp->ih_entities = ipmi_hash_create(ihp, offsetof(ipmi_entity_impl_t, ie_link), ipmi_entity_hash_convert, ipmi_entity_hash_compute, ipmi_entity_hash_compare)) == NULL) return (-1); return (0); } void ipmi_entity_clear(ipmi_handle_t *ihp) { ipmi_entity_impl_t *eip; ipmi_entity_sdr_t *esp; while ((eip = ipmi_hash_first(ihp->ih_entities)) != NULL) { while ((esp = ipmi_list_next(&eip->ie_sdr_list)) != NULL) { ipmi_list_delete(&eip->ie_sdr_list, esp); ipmi_free(ihp, esp); } ipmi_hash_remove(ihp->ih_entities, eip); ipmi_free(ihp, eip); } } void ipmi_entity_fini(ipmi_handle_t *ihp) { if (ihp->ih_entities != NULL) { ipmi_entity_clear(ihp); ipmi_hash_destroy(ihp->ih_entities); } }