xref: /onnv-gate/usr/src/lib/libipmi/common/ipmi_sdr.c (revision 11756:3ae81edcfbf7)
13798Seschrock /*
23798Seschrock  * CDDL HEADER START
33798Seschrock  *
43798Seschrock  * The contents of this file are subject to the terms of the
53798Seschrock  * Common Development and Distribution License (the "License").
63798Seschrock  * You may not use this file except in compliance with the License.
73798Seschrock  *
83798Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93798Seschrock  * or http://www.opensolaris.org/os/licensing.
103798Seschrock  * See the License for the specific language governing permissions
113798Seschrock  * and limitations under the License.
123798Seschrock  *
133798Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
143798Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153798Seschrock  * If applicable, add the following below this CDDL HEADER, with the
163798Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
173798Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
183798Seschrock  *
193798Seschrock  * CDDL HEADER END
203798Seschrock  */
213798Seschrock 
223798Seschrock /*
23*11756SRobert.Johnston@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
243798Seschrock  * Use is subject to license terms.
253798Seschrock  */
263798Seschrock 
273798Seschrock #include <libipmi.h>
283798Seschrock #include <stddef.h>
293798Seschrock #include <string.h>
306070Srobj #include <strings.h>
317243Srobj #include <math.h>
323798Seschrock 
333798Seschrock #include "ipmi_impl.h"
343798Seschrock 
357243Srobj /*
367243Srobj  * This macros are used by ipmi_sdr_conv_reading.  They were taken verbatim from
377243Srobj  * the source for ipmitool (v1.88)
387243Srobj  */
397243Srobj #define	tos32(val, bits)	((val & ((1<<((bits)-1)))) ? (-((val) & \
407243Srobj 				(1<<((bits)-1))) | (val)) : (val))
417243Srobj 
427243Srobj #define	__TO_TOL(mtol)	(uint16_t)(BSWAP_16(mtol) & 0x3f)
437243Srobj 
447243Srobj #define	__TO_M(mtol)	(int16_t)(tos32((((BSWAP_16(mtol) & 0xff00) >> 8) | \
457243Srobj 				((BSWAP_16(mtol) & 0xc0) << 2)), 10))
467243Srobj 
477243Srobj #define	__TO_B(bacc)	(int32_t)(tos32((((BSWAP_32(bacc) & \
487243Srobj 				0xff000000) >> 24) | \
497243Srobj 				((BSWAP_32(bacc) & 0xc00000) >> 14)), 10))
507243Srobj 
517243Srobj #define	__TO_ACC(bacc)	(uint32_t)(((BSWAP_32(bacc) & 0x3f0000) >> 16) | \
527243Srobj 				((BSWAP_32(bacc) & 0xf000) >> 6))
537243Srobj 
547243Srobj #define	__TO_ACC_EXP(bacc)	(uint32_t)((BSWAP_32(bacc) & 0xc00) >> 10)
557243Srobj #define	__TO_R_EXP(bacc)	(int32_t)(tos32(((BSWAP_32(bacc) & 0xf0) >> 4),\
567243Srobj 				4))
577243Srobj #define	__TO_B_EXP(bacc)	(int32_t)(tos32((BSWAP_32(bacc) & 0xf), 4))
587243Srobj 
597243Srobj #define	SDR_SENSOR_L_LINEAR	0x00
607243Srobj #define	SDR_SENSOR_L_LN		0x01
617243Srobj #define	SDR_SENSOR_L_LOG10	0x02
627243Srobj #define	SDR_SENSOR_L_LOG2	0x03
637243Srobj #define	SDR_SENSOR_L_E		0x04
647243Srobj #define	SDR_SENSOR_L_EXP10	0x05
657243Srobj #define	SDR_SENSOR_L_EXP2	0x06
667243Srobj #define	SDR_SENSOR_L_1_X	0x07
677243Srobj #define	SDR_SENSOR_L_SQR	0x08
687243Srobj #define	SDR_SENSOR_L_CUBE	0x09
697243Srobj #define	SDR_SENSOR_L_SQRT	0x0a
707243Srobj #define	SDR_SENSOR_L_CUBERT	0x0b
717243Srobj #define	SDR_SENSOR_L_NONLINEAR	0x70
727243Srobj 
737243Srobj /*
747243Srobj  * Analog sensor reading data formats
757243Srobj  *
767243Srobj  * See Section 43.1
777243Srobj  */
787243Srobj #define	IPMI_DATA_FMT_UNSIGNED	0
797243Srobj #define	IPMI_DATA_FMT_ONESCOMP	1
807243Srobj #define	IPMI_DATA_FMT_TWOSCOMP	2
817243Srobj 
828526SRobert.Johnston@Sun.COM #define	IPMI_SDR_HDR_SZ		offsetof(ipmi_sdr_t, is_record)
838526SRobert.Johnston@Sun.COM 
846070Srobj typedef struct ipmi_sdr_cache_ent {
856070Srobj 	char				*isc_name;
866070Srobj 	struct ipmi_sdr			*isc_sdr;
876070Srobj 	ipmi_hash_link_t		isc_link;
886070Srobj } ipmi_sdr_cache_ent_t;
896070Srobj 
903798Seschrock typedef struct ipmi_cmd_get_sdr {
913798Seschrock 	uint16_t	ic_gs_resid;
923798Seschrock 	uint16_t	ic_gs_recid;
933798Seschrock 	uint8_t		ic_gs_offset;
943798Seschrock 	uint8_t		ic_gs_len;
953798Seschrock } ipmi_cmd_get_sdr_t;
963798Seschrock 
973798Seschrock typedef struct ipmi_rsp_get_sdr {
983798Seschrock 	uint16_t	ir_gs_next;
993798Seschrock 	uint8_t		ir_gs_record[1];
1003798Seschrock } ipmi_rsp_get_sdr_t;
1013798Seschrock 
1023798Seschrock /*
1036070Srobj  * "Get SDR Repostiory Info" command.
1046070Srobj  */
1056070Srobj ipmi_sdr_info_t *
ipmi_sdr_get_info(ipmi_handle_t * ihp)1066070Srobj ipmi_sdr_get_info(ipmi_handle_t *ihp)
1076070Srobj {
1086070Srobj 	ipmi_cmd_t cmd, *rsp;
1096070Srobj 	ipmi_sdr_info_t *sip;
1106726Srobj 	uint16_t tmp16;
1116726Srobj 	uint32_t tmp32;
1126070Srobj 
1136070Srobj 	cmd.ic_netfn = IPMI_NETFN_STORAGE;
1146070Srobj 	cmd.ic_lun = 0;
1156070Srobj 	cmd.ic_cmd = IPMI_CMD_GET_SDR_INFO;
1166070Srobj 	cmd.ic_dlen = 0;
1176070Srobj 	cmd.ic_data = NULL;
1186070Srobj 
1196070Srobj 	if ((rsp = ipmi_send(ihp, &cmd)) == NULL)
1206070Srobj 		return (NULL);
1216070Srobj 
1226070Srobj 	sip = rsp->ic_data;
1236070Srobj 
1246726Srobj 	tmp16 = LE_IN16(&sip->isi_record_count);
1256726Srobj 	(void) memcpy(&sip->isi_record_count, &tmp16, sizeof (tmp16));
1266726Srobj 
1276726Srobj 	tmp16 = LE_IN16(&sip->isi_free_space);
1286726Srobj 	(void) memcpy(&sip->isi_free_space, &tmp16, sizeof (tmp16));
1296726Srobj 
1306726Srobj 	tmp32 = LE_IN32(&sip->isi_add_ts);
1316726Srobj 	(void) memcpy(&sip->isi_add_ts, &tmp32, sizeof (tmp32));
1326726Srobj 
1336726Srobj 	tmp32 = LE_IN32(&sip->isi_erase_ts);
1346726Srobj 	(void) memcpy(&sip->isi_erase_ts, &tmp32, sizeof (tmp32));
1356070Srobj 
1366070Srobj 	return (sip);
1376070Srobj }
1386070Srobj 
1396070Srobj /*
1403798Seschrock  * Issue the "Reserve SDR Repository" command.
1413798Seschrock  */
1423798Seschrock static int
ipmi_sdr_reserve_repository(ipmi_handle_t * ihp)1433798Seschrock ipmi_sdr_reserve_repository(ipmi_handle_t *ihp)
1443798Seschrock {
1453798Seschrock 	ipmi_cmd_t cmd, *rsp;
1463798Seschrock 
1473798Seschrock 	cmd.ic_netfn = IPMI_NETFN_STORAGE;
1483798Seschrock 	cmd.ic_lun = 0;
1493798Seschrock 	cmd.ic_cmd = IPMI_CMD_RESERVE_SDR_REPOSITORY;
1503798Seschrock 	cmd.ic_dlen = 0;
1513798Seschrock 	cmd.ic_data = NULL;
1523798Seschrock 
1533798Seschrock 	if ((rsp = ipmi_send(ihp, &cmd)) == NULL)
1543798Seschrock 		return (-1);
1553798Seschrock 
1563798Seschrock 	ihp->ih_reservation = *((uint16_t *)rsp->ic_data);
1573798Seschrock 	return (0);
1583798Seschrock }
1593798Seschrock 
1603798Seschrock /*
1616070Srobj  * Returns B_TRUE if the repository has changed since the cached copy was last
1626070Srobj  * referenced.
1636070Srobj  */
1646070Srobj boolean_t
ipmi_sdr_changed(ipmi_handle_t * ihp)1656070Srobj ipmi_sdr_changed(ipmi_handle_t *ihp)
1666070Srobj {
1676070Srobj 	ipmi_sdr_info_t *sip;
1686070Srobj 
1696070Srobj 	if ((sip = ipmi_sdr_get_info(ihp)) == NULL)
1706070Srobj 		return (B_TRUE);
1716070Srobj 
1726070Srobj 	return (sip->isi_add_ts > ihp->ih_sdr_ts ||
1736070Srobj 	    sip->isi_erase_ts > ihp->ih_sdr_ts ||
1746070Srobj 	    ipmi_hash_first(ihp->ih_sdr_cache) == NULL);
1756070Srobj }
1766070Srobj 
1776070Srobj /*
1783798Seschrock  * Refresh the cache of sensor data records.
1793798Seschrock  */
1806070Srobj int
ipmi_sdr_refresh(ipmi_handle_t * ihp)1813798Seschrock ipmi_sdr_refresh(ipmi_handle_t *ihp)
1823798Seschrock {
1833798Seschrock 	uint16_t id;
1843798Seschrock 	ipmi_sdr_t *sdr;
1853798Seschrock 	ipmi_sdr_cache_ent_t *ent;
1868526SRobert.Johnston@Sun.COM 	size_t namelen;
1876070Srobj 	uint8_t type;
1886070Srobj 	char *name;
1896070Srobj 	ipmi_sdr_info_t *sip;
190*11756SRobert.Johnston@Sun.COM 	uint32_t isi_add_ts, isi_erase_ts;
1916070Srobj 
1926070Srobj 	if ((sip = ipmi_sdr_get_info(ihp)) == NULL)
1936070Srobj 		return (-1);
1946070Srobj 
195*11756SRobert.Johnston@Sun.COM 	(void) memcpy(&isi_add_ts, &sip->isi_add_ts, sizeof (uint32_t));
196*11756SRobert.Johnston@Sun.COM 	(void) memcpy(&isi_erase_ts, &sip->isi_erase_ts, sizeof (uint32_t));
197*11756SRobert.Johnston@Sun.COM 	if (isi_add_ts <= ihp->ih_sdr_ts &&
198*11756SRobert.Johnston@Sun.COM 	    isi_erase_ts <= ihp->ih_sdr_ts &&
1996070Srobj 	    ipmi_hash_first(ihp->ih_sdr_cache) != NULL)
2006070Srobj 		return (0);
2013798Seschrock 
2025017Seschrock 	ipmi_sdr_clear(ihp);
2036070Srobj 	ipmi_entity_clear(ihp);
204*11756SRobert.Johnston@Sun.COM 	ihp->ih_sdr_ts = MAX(isi_add_ts, isi_erase_ts);
2053798Seschrock 
2063798Seschrock 	/*
2073798Seschrock 	 * Iterate over all existing SDRs and add them to the cache.
2083798Seschrock 	 */
2093798Seschrock 	id = IPMI_SDR_FIRST;
2103798Seschrock 	while (id != IPMI_SDR_LAST) {
2113798Seschrock 		if ((sdr = ipmi_sdr_get(ihp, id, &id)) == NULL)
2128339SEric.Schrock@Sun.COM 			goto error;
2133798Seschrock 
2143798Seschrock 		/*
2156070Srobj 		 * Extract the name from the record-specific data.
2163798Seschrock 		 */
2173798Seschrock 		switch (sdr->is_type) {
2183798Seschrock 		case IPMI_SDR_TYPE_GENERIC_LOCATOR:
2196070Srobj 			{
2206070Srobj 				ipmi_sdr_generic_locator_t *glp =
2216070Srobj 				    (ipmi_sdr_generic_locator_t *)
2226070Srobj 				    sdr->is_record;
2236070Srobj 				namelen = glp->is_gl_idlen;
2246070Srobj 				type = glp->is_gl_idtype;
2256070Srobj 				name = glp->is_gl_idstring;
2266070Srobj 				break;
2276070Srobj 			}
2283798Seschrock 
2293798Seschrock 		case IPMI_SDR_TYPE_FRU_LOCATOR:
2306070Srobj 			{
2316070Srobj 				ipmi_sdr_fru_locator_t *flp =
2326070Srobj 				    (ipmi_sdr_fru_locator_t *)
2336070Srobj 				    sdr->is_record;
2346070Srobj 				namelen = flp->is_fl_idlen;
2356070Srobj 				name = flp->is_fl_idstring;
2366070Srobj 				type = flp->is_fl_idtype;
2376070Srobj 				break;
2386070Srobj 			}
2396070Srobj 
2406070Srobj 		case IPMI_SDR_TYPE_COMPACT_SENSOR:
2416070Srobj 			{
2426070Srobj 				ipmi_sdr_compact_sensor_t *csp =
2436070Srobj 				    (ipmi_sdr_compact_sensor_t *)
2446070Srobj 				    sdr->is_record;
2456726Srobj 				uint16_t tmp;
2466726Srobj 
2476070Srobj 				namelen = csp->is_cs_idlen;
2486070Srobj 				type = csp->is_cs_idtype;
2496070Srobj 				name = csp->is_cs_idstring;
2506070Srobj 
2516726Srobj 				tmp = LE_IN16(&csp->is_cs_assert_mask);
2526726Srobj 				(void) memcpy(&csp->is_cs_assert_mask, &tmp,
2536726Srobj 				    sizeof (tmp));
2546726Srobj 
2556726Srobj 				tmp = LE_IN16(&csp->is_cs_deassert_mask);
2566726Srobj 				(void) memcpy(&csp->is_cs_deassert_mask, &tmp,
2576726Srobj 				    sizeof (tmp));
2586726Srobj 
2596726Srobj 				tmp = LE_IN16(&csp->is_cs_reading_mask);
2606726Srobj 				(void) memcpy(&csp->is_cs_reading_mask, &tmp,
2616726Srobj 				    sizeof (tmp));
2626070Srobj 				break;
2636070Srobj 			}
2646070Srobj 
2656070Srobj 		case IPMI_SDR_TYPE_FULL_SENSOR:
2666070Srobj 			{
2676726Srobj 				ipmi_sdr_full_sensor_t *fsp =
2686070Srobj 				    (ipmi_sdr_full_sensor_t *)
2696070Srobj 				    sdr->is_record;
2706726Srobj 				uint16_t tmp;
2716726Srobj 
2726726Srobj 				namelen = fsp->is_fs_idlen;
2736726Srobj 				type = fsp->is_fs_idtype;
2746726Srobj 				name = fsp->is_fs_idstring;
2756070Srobj 
2766726Srobj 				tmp = LE_IN16(&fsp->is_fs_assert_mask);
2776726Srobj 				(void) memcpy(&fsp->is_fs_assert_mask, &tmp,
2786726Srobj 				    sizeof (tmp));
2796726Srobj 
2806726Srobj 				tmp = LE_IN16(&fsp->is_fs_deassert_mask);
2816726Srobj 				(void) memcpy(&fsp->is_fs_deassert_mask, &tmp,
2826726Srobj 				    sizeof (tmp));
2836726Srobj 
2846726Srobj 				tmp = LE_IN16(&fsp->is_fs_reading_mask);
2856726Srobj 				(void) memcpy(&fsp->is_fs_reading_mask, &tmp,
2866726Srobj 				    sizeof (tmp));
2876070Srobj 				break;
2886070Srobj 			}
2896070Srobj 
2906070Srobj 		case IPMI_SDR_TYPE_EVENT_ONLY:
2916070Srobj 			{
2926070Srobj 				ipmi_sdr_event_only_t *esp =
2936070Srobj 				    (ipmi_sdr_event_only_t *)
2946070Srobj 				    sdr->is_record;
2956070Srobj 				namelen = esp->is_eo_idlen;
2966070Srobj 				type = esp->is_eo_idtype;
2976070Srobj 				name = esp->is_eo_idstring;
2986070Srobj 				break;
2996070Srobj 			}
3006070Srobj 
3016070Srobj 		case IPMI_SDR_TYPE_MANAGEMENT_LOCATOR:
3026070Srobj 			{
3036070Srobj 				ipmi_sdr_management_locator_t *msp =
3046070Srobj 				    (ipmi_sdr_management_locator_t *)
3056070Srobj 				    sdr->is_record;
3066070Srobj 				namelen = msp->is_ml_idlen;
3076070Srobj 				type = msp->is_ml_idtype;
3086070Srobj 				name = msp->is_ml_idstring;
3096070Srobj 				break;
3106070Srobj 			}
3116070Srobj 
3126070Srobj 		case IPMI_SDR_TYPE_MANAGEMENT_CONFIRMATION:
3136070Srobj 			{
3146070Srobj 				ipmi_sdr_management_confirmation_t *mcp =
3156070Srobj 				    (ipmi_sdr_management_confirmation_t *)
3166070Srobj 				    sdr->is_record;
3176726Srobj 				uint16_t tmp;
3186726Srobj 
3196070Srobj 				name = NULL;
3206726Srobj 				tmp = LE_IN16(&mcp->is_mc_product);
3216726Srobj 				(void) memcpy(&mcp->is_mc_product, &tmp,
3226726Srobj 				    sizeof (tmp));
3236070Srobj 				break;
3246070Srobj 			}
3256070Srobj 
3266070Srobj 		default:
3276070Srobj 			name = NULL;
3283798Seschrock 		}
3293798Seschrock 
3306070Srobj 		if ((ent = ipmi_zalloc(ihp,
3318526SRobert.Johnston@Sun.COM 		    sizeof (ipmi_sdr_cache_ent_t))) == NULL) {
3328526SRobert.Johnston@Sun.COM 			free(sdr);
3338339SEric.Schrock@Sun.COM 			goto error;
3343798Seschrock 		}
3358526SRobert.Johnston@Sun.COM 
3368526SRobert.Johnston@Sun.COM 		ent->isc_sdr = sdr;
3376070Srobj 
3386070Srobj 		if (name != NULL) {
3396070Srobj 			if ((ent->isc_name = ipmi_alloc(ihp, namelen + 1)) ==
3406070Srobj 			    NULL) {
3416070Srobj 				ipmi_free(ihp, ent->isc_sdr);
3426070Srobj 				ipmi_free(ihp, ent);
3438339SEric.Schrock@Sun.COM 				goto error;
3446070Srobj 			}
3456070Srobj 
3466070Srobj 			ipmi_decode_string(type, namelen, name, ent->isc_name);
3476070Srobj 		}
3483798Seschrock 
3496343Seschrock 		/*
3506343Seschrock 		 * This should never happen.  It means that the SP has returned
3516343Seschrock 		 * a SDR record twice, with the same name and ID.  This has
3526343Seschrock 		 * been observed on service processors that don't correctly
3536343Seschrock 		 * return SDR_LAST during iteration, so assume we've looped in
3546343Seschrock 		 * the SDR and return gracefully.
3556343Seschrock 		 */
3566343Seschrock 		if (ipmi_hash_lookup(ihp->ih_sdr_cache, ent) != NULL) {
3576343Seschrock 			ipmi_free(ihp, ent->isc_sdr);
3586343Seschrock 			ipmi_free(ihp, ent->isc_name);
3596343Seschrock 			ipmi_free(ihp, ent);
3606343Seschrock 			break;
3616343Seschrock 		}
3626343Seschrock 
3636070Srobj 		ipmi_hash_insert(ihp->ih_sdr_cache, ent);
3643798Seschrock 	}
3653798Seschrock 
3663798Seschrock 	return (0);
3678339SEric.Schrock@Sun.COM 
3688339SEric.Schrock@Sun.COM error:
3698339SEric.Schrock@Sun.COM 	ipmi_sdr_clear(ihp);
3708339SEric.Schrock@Sun.COM 	ipmi_entity_clear(ihp);
3718339SEric.Schrock@Sun.COM 	return (-1);
3723798Seschrock }
3733798Seschrock 
3746070Srobj /*
3756070Srobj  * Hash routines.  We allow lookup by name, but since not all entries have
3766070Srobj  * names, we fall back to the entry pointer, which is guaranteed to be unique.
3776070Srobj  * The end result is that entities without names cannot be looked up, but will
3786070Srobj  * show up during iteration.
3796070Srobj  */
3806070Srobj static const void *
ipmi_sdr_hash_convert(const void * p)3816070Srobj ipmi_sdr_hash_convert(const void *p)
3826070Srobj {
3836070Srobj 	return (p);
3846070Srobj }
3856070Srobj 
3866070Srobj static ulong_t
ipmi_sdr_hash_compute(const void * p)3876070Srobj ipmi_sdr_hash_compute(const void *p)
3886070Srobj {
3896070Srobj 	const ipmi_sdr_cache_ent_t *ep = p;
3906070Srobj 
3916070Srobj 	if (ep->isc_name)
3926070Srobj 		return (ipmi_hash_strhash(ep->isc_name));
3936070Srobj 	else
3946070Srobj 		return (ipmi_hash_ptrhash(ep));
3956070Srobj }
3966070Srobj 
3976070Srobj static int
ipmi_sdr_hash_compare(const void * a,const void * b)3986070Srobj ipmi_sdr_hash_compare(const void *a, const void *b)
3996070Srobj {
4006070Srobj 	const ipmi_sdr_cache_ent_t *ap = a;
4016070Srobj 	const ipmi_sdr_cache_ent_t *bp = b;
4026070Srobj 
4036070Srobj 	if (ap->isc_name == NULL || bp->isc_name == NULL)
4046070Srobj 		return (-1);
4056070Srobj 
4066343Seschrock 	if (strcmp(ap->isc_name, bp->isc_name) != 0)
4076343Seschrock 		return (-1);
4086343Seschrock 
4096343Seschrock 	/*
4106343Seschrock 	 * While it is strange for a service processor to report multiple
4116343Seschrock 	 * entries with the same name, we allow it by treating the (name, id)
4126343Seschrock 	 * as the unique identifier.  When looking up by name, the SDR pointer
4136343Seschrock 	 * is NULL, and we return the first matching name.
4146343Seschrock 	 */
4156343Seschrock 	if (ap->isc_sdr == NULL || bp->isc_sdr == NULL)
4166343Seschrock 		return (0);
4176343Seschrock 
4186343Seschrock 	if (ap->isc_sdr->is_id == bp->isc_sdr->is_id)
4196343Seschrock 		return (0);
4206343Seschrock 	else
4216343Seschrock 		return (-1);
4226070Srobj }
4236070Srobj 
4246070Srobj int
ipmi_sdr_init(ipmi_handle_t * ihp)4256070Srobj ipmi_sdr_init(ipmi_handle_t *ihp)
4266070Srobj {
4276070Srobj 	if ((ihp->ih_sdr_cache = ipmi_hash_create(ihp,
4286070Srobj 	    offsetof(ipmi_sdr_cache_ent_t, isc_link),
4296070Srobj 	    ipmi_sdr_hash_convert, ipmi_sdr_hash_compute,
4306070Srobj 	    ipmi_sdr_hash_compare)) == NULL)
4316070Srobj 		return (-1);
4326070Srobj 
4336070Srobj 	return (0);
4346070Srobj }
4356070Srobj 
4365017Seschrock void
ipmi_sdr_clear(ipmi_handle_t * ihp)4375017Seschrock ipmi_sdr_clear(ipmi_handle_t *ihp)
4385017Seschrock {
4396070Srobj 	ipmi_sdr_cache_ent_t *ent;
4405017Seschrock 
4416070Srobj 	while ((ent = ipmi_hash_first(ihp->ih_sdr_cache)) != NULL) {
4426070Srobj 		ipmi_hash_remove(ihp->ih_sdr_cache, ent);
4436070Srobj 		ipmi_free(ihp, ent->isc_sdr);
4446070Srobj 		ipmi_free(ihp, ent->isc_name);
4455017Seschrock 		ipmi_free(ihp, ent);
4466070Srobj 	}
4476070Srobj }
4486070Srobj 
4496070Srobj void
ipmi_sdr_fini(ipmi_handle_t * ihp)4506070Srobj ipmi_sdr_fini(ipmi_handle_t *ihp)
4516070Srobj {
4526070Srobj 	if (ihp->ih_sdr_cache != NULL) {
4536070Srobj 		ipmi_sdr_clear(ihp);
4546070Srobj 		ipmi_hash_destroy(ihp->ih_sdr_cache);
4555017Seschrock 	}
4565017Seschrock }
4575017Seschrock 
4583798Seschrock ipmi_sdr_t *
ipmi_sdr_get(ipmi_handle_t * ihp,uint16_t id,uint16_t * next)4593798Seschrock ipmi_sdr_get(ipmi_handle_t *ihp, uint16_t id, uint16_t *next)
4603798Seschrock {
4618526SRobert.Johnston@Sun.COM 	uint8_t offset = IPMI_SDR_HDR_SZ, count = 0, chunksz = 16, sdr_sz;
4623798Seschrock 	ipmi_cmd_t cmd, *rsp;
4633798Seschrock 	ipmi_cmd_get_sdr_t req;
4648526SRobert.Johnston@Sun.COM 	ipmi_sdr_t *sdr;
4658526SRobert.Johnston@Sun.COM 	int i = 0;
4668526SRobert.Johnston@Sun.COM 	char *buf;
4673798Seschrock 
4683798Seschrock 	req.ic_gs_resid = ihp->ih_reservation;
4693798Seschrock 	req.ic_gs_recid = id;
4703798Seschrock 
4713798Seschrock 	cmd.ic_netfn = IPMI_NETFN_STORAGE;
4723798Seschrock 	cmd.ic_lun = 0;
4733798Seschrock 	cmd.ic_cmd = IPMI_CMD_GET_SDR;
4743798Seschrock 	cmd.ic_dlen = sizeof (req);
4753798Seschrock 	cmd.ic_data = &req;
4763798Seschrock 
4778526SRobert.Johnston@Sun.COM 	/*
4788526SRobert.Johnston@Sun.COM 	 * The size of the SDR is contained in the 5th byte of the SDR header,
4798526SRobert.Johnston@Sun.COM 	 * so we'll read the first 5 bytes to get the size, so we know how big
4808526SRobert.Johnston@Sun.COM 	 * to make the buffer.
4818526SRobert.Johnston@Sun.COM 	 */
4828526SRobert.Johnston@Sun.COM 	req.ic_gs_offset = 0;
4838526SRobert.Johnston@Sun.COM 	req.ic_gs_len = IPMI_SDR_HDR_SZ;
4843798Seschrock 	for (i = 0; i < ihp->ih_retries; i++) {
4856070Srobj 		if ((rsp = ipmi_send(ihp, &cmd)) != NULL)
4866070Srobj 			break;
4873798Seschrock 
4886070Srobj 		if (ipmi_errno(ihp) != EIPMI_INVALID_RESERVATION)
4896070Srobj 			return (NULL);
4906070Srobj 
4916070Srobj 		if (ipmi_sdr_reserve_repository(ihp) != 0)
4926070Srobj 			return (NULL);
4936070Srobj 		req.ic_gs_resid = ihp->ih_reservation;
4943798Seschrock 	}
4953798Seschrock 	if (rsp == NULL)
4963798Seschrock 		return (NULL);
4973798Seschrock 
4988526SRobert.Johnston@Sun.COM 	sdr = (ipmi_sdr_t *)((ipmi_rsp_get_sdr_t *)rsp->ic_data)->ir_gs_record;
4998526SRobert.Johnston@Sun.COM 	sdr_sz = sdr->is_length;
5008526SRobert.Johnston@Sun.COM 
5018526SRobert.Johnston@Sun.COM 	if ((buf = ipmi_zalloc(ihp, sdr_sz + IPMI_SDR_HDR_SZ)) == NULL) {
5028526SRobert.Johnston@Sun.COM 		(void) ipmi_set_error(ihp, EIPMI_NOMEM, NULL);
5033798Seschrock 		return (NULL);
5043798Seschrock 	}
5058526SRobert.Johnston@Sun.COM 	(void) memcpy(buf, (void *)sdr, IPMI_SDR_HDR_SZ);
5063798Seschrock 
5078526SRobert.Johnston@Sun.COM 	/*
5088526SRobert.Johnston@Sun.COM 	 * Some SDRs can be bigger than the buffer sizes for a given bmc
5098526SRobert.Johnston@Sun.COM 	 * interface.  Therefore we break up the process of reading in an entire
5108526SRobert.Johnston@Sun.COM 	 * SDR into multiple smaller reads.
5118526SRobert.Johnston@Sun.COM 	 */
5128840SRobert.Johnston@Sun.COM 	while (count < sdr_sz) {
5138526SRobert.Johnston@Sun.COM 		req.ic_gs_offset = offset;
5148526SRobert.Johnston@Sun.COM 		if (chunksz > (sdr_sz - count))
5158526SRobert.Johnston@Sun.COM 			chunksz = sdr_sz - count;
5168526SRobert.Johnston@Sun.COM 		req.ic_gs_len = chunksz;
5178526SRobert.Johnston@Sun.COM 		rsp = ipmi_send(ihp, &cmd);
5183798Seschrock 
5198526SRobert.Johnston@Sun.COM 		if (rsp != NULL) {
5208526SRobert.Johnston@Sun.COM 			count += chunksz;
5218526SRobert.Johnston@Sun.COM 			sdr = (ipmi_sdr_t *)
5228526SRobert.Johnston@Sun.COM 			    ((ipmi_rsp_get_sdr_t *)rsp->ic_data)->ir_gs_record;
5238526SRobert.Johnston@Sun.COM 			(void) memcpy(buf+offset, (void *)sdr, chunksz);
5248526SRobert.Johnston@Sun.COM 			offset += chunksz;
5258526SRobert.Johnston@Sun.COM 			i = 0;
5268526SRobert.Johnston@Sun.COM 		} else if (ipmi_errno(ihp) == EIPMI_INVALID_RESERVATION) {
5278840SRobert.Johnston@Sun.COM 			if (i >= ihp->ih_retries ||
5288840SRobert.Johnston@Sun.COM 			    ipmi_sdr_reserve_repository(ihp) != 0) {
5298526SRobert.Johnston@Sun.COM 				free(buf);
5308526SRobert.Johnston@Sun.COM 				return (NULL);
5318526SRobert.Johnston@Sun.COM 			}
5328526SRobert.Johnston@Sun.COM 			req.ic_gs_resid = ihp->ih_reservation;
5338526SRobert.Johnston@Sun.COM 			i++;
5348526SRobert.Johnston@Sun.COM 		} else {
5358526SRobert.Johnston@Sun.COM 			free(buf);
5368526SRobert.Johnston@Sun.COM 			return (NULL);
5378526SRobert.Johnston@Sun.COM 		}
5388526SRobert.Johnston@Sun.COM 	}
5398526SRobert.Johnston@Sun.COM 	*next = ((ipmi_rsp_get_sdr_t *)rsp->ic_data)->ir_gs_next;
5408526SRobert.Johnston@Sun.COM 
5418526SRobert.Johnston@Sun.COM 	return ((ipmi_sdr_t *)buf);
5423798Seschrock }
5433798Seschrock 
5446070Srobj int
ipmi_sdr_iter(ipmi_handle_t * ihp,int (* func)(ipmi_handle_t *,const char *,ipmi_sdr_t *,void *),void * data)5456070Srobj ipmi_sdr_iter(ipmi_handle_t *ihp, int (*func)(ipmi_handle_t *,
5466070Srobj     const char *, ipmi_sdr_t *, void *), void *data)
5473798Seschrock {
5483798Seschrock 	ipmi_sdr_cache_ent_t *ent;
5496070Srobj 	int ret;
5503798Seschrock 
5516070Srobj 	if (ipmi_hash_first(ihp->ih_sdr_cache) == NULL &&
5526070Srobj 	    ipmi_sdr_refresh(ihp) != 0)
5536070Srobj 		return (-1);
5546070Srobj 
5556070Srobj 	for (ent = ipmi_hash_first(ihp->ih_sdr_cache); ent != NULL;
5566070Srobj 	    ent = ipmi_hash_next(ihp->ih_sdr_cache, ent)) {
5576070Srobj 		if ((ret = func(ihp, ent->isc_name, ent->isc_sdr, data)) != 0)
5586070Srobj 			return (ret);
5596070Srobj 	}
5606070Srobj 
5616070Srobj 	return (0);
5626070Srobj }
5636070Srobj 
5646070Srobj ipmi_sdr_t *
ipmi_sdr_lookup(ipmi_handle_t * ihp,const char * idstr)5656070Srobj ipmi_sdr_lookup(ipmi_handle_t *ihp, const char *idstr)
5666070Srobj {
5676070Srobj 	ipmi_sdr_cache_ent_t *ent, search;
5686070Srobj 
5696070Srobj 	if (ipmi_hash_first(ihp->ih_sdr_cache) == NULL &&
5703798Seschrock 	    ipmi_sdr_refresh(ihp) != 0)
5713798Seschrock 		return (NULL);
5723798Seschrock 
5736070Srobj 	search.isc_name = (char *)idstr;
5746343Seschrock 	search.isc_sdr = NULL;
5756070Srobj 	if ((ent = ipmi_hash_lookup(ihp->ih_sdr_cache, &search)) == NULL) {
5766070Srobj 		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
5776070Srobj 		return (NULL);
5783798Seschrock 	}
5793798Seschrock 
5806070Srobj 	return (ent->isc_sdr);
5816070Srobj }
5826070Srobj 
5836070Srobj static void *
ipmi_sdr_lookup_common(ipmi_handle_t * ihp,const char * idstr,uint8_t type)5846070Srobj ipmi_sdr_lookup_common(ipmi_handle_t *ihp, const char *idstr,
5856070Srobj     uint8_t type)
5866070Srobj {
5876070Srobj 	ipmi_sdr_t *sdrp;
5886070Srobj 
5896070Srobj 	if ((sdrp = ipmi_sdr_lookup(ihp, idstr)) == NULL)
5906070Srobj 		return (NULL);
5916070Srobj 
5926070Srobj 	if (sdrp->is_type != type) {
5936070Srobj 		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
5946070Srobj 		return (NULL);
5956070Srobj 	}
5966070Srobj 
5976070Srobj 	return (sdrp->is_record);
5986070Srobj }
5996070Srobj 
6006070Srobj ipmi_sdr_fru_locator_t *
ipmi_sdr_lookup_fru(ipmi_handle_t * ihp,const char * idstr)6016070Srobj ipmi_sdr_lookup_fru(ipmi_handle_t *ihp, const char *idstr)
6026070Srobj {
6036070Srobj 	return (ipmi_sdr_lookup_common(ihp, idstr,
6046070Srobj 	    IPMI_SDR_TYPE_FRU_LOCATOR));
6053798Seschrock }
6063798Seschrock 
6073798Seschrock ipmi_sdr_generic_locator_t *
ipmi_sdr_lookup_generic(ipmi_handle_t * ihp,const char * idstr)6083798Seschrock ipmi_sdr_lookup_generic(ipmi_handle_t *ihp, const char *idstr)
6093798Seschrock {
6106070Srobj 	return (ipmi_sdr_lookup_common(ihp, idstr,
6116070Srobj 	    IPMI_SDR_TYPE_GENERIC_LOCATOR));
6126070Srobj }
6133798Seschrock 
6146070Srobj ipmi_sdr_compact_sensor_t *
ipmi_sdr_lookup_compact_sensor(ipmi_handle_t * ihp,const char * idstr)6156070Srobj ipmi_sdr_lookup_compact_sensor(ipmi_handle_t *ihp, const char *idstr)
6166070Srobj {
6176070Srobj 	return (ipmi_sdr_lookup_common(ihp, idstr,
6186070Srobj 	    IPMI_SDR_TYPE_COMPACT_SENSOR));
6196070Srobj }
6203798Seschrock 
6216070Srobj ipmi_sdr_full_sensor_t *
ipmi_sdr_lookup_full_sensor(ipmi_handle_t * ihp,const char * idstr)6226070Srobj ipmi_sdr_lookup_full_sensor(ipmi_handle_t *ihp, const char *idstr)
6236070Srobj {
6246070Srobj 	return (ipmi_sdr_lookup_common(ihp, idstr,
6256070Srobj 	    IPMI_SDR_TYPE_FULL_SENSOR));
6263798Seschrock }
6277243Srobj 
6287243Srobj /*
6297243Srobj  * Mostly taken from ipmitool source v1.88
6307243Srobj  *
6317243Srobj  * This function converts the raw sensor reading returned by
6327243Srobj  * ipmi_get_sensor_reading to a unit-based value of type double.
6337243Srobj  */
6347243Srobj int
ipmi_sdr_conv_reading(ipmi_sdr_full_sensor_t * sensor,uint8_t val,double * result)6357243Srobj ipmi_sdr_conv_reading(ipmi_sdr_full_sensor_t *sensor, uint8_t val,
6367243Srobj     double *result)
6377243Srobj {
6387243Srobj 	int m, b, k1, k2;
6397243Srobj 
6407243Srobj 	m = __TO_M(sensor->is_fs_mtol);
6417243Srobj 	b = __TO_B(sensor->is_fs_bacc);
6427243Srobj 	k1 = __TO_B_EXP(sensor->is_fs_bacc);
6437243Srobj 	k2 = __TO_R_EXP(sensor->is_fs_bacc);
6447243Srobj 
6457243Srobj 	switch (sensor->is_fs_analog_fmt) {
6467243Srobj 	case IPMI_DATA_FMT_UNSIGNED:
6477243Srobj 		*result = (double)(((m * val) +
6487243Srobj 		    (b * pow(10, k1))) * pow(10, k2));
6497243Srobj 		break;
6507243Srobj 	case IPMI_DATA_FMT_ONESCOMP:
6517243Srobj 		if (val & 0x80)
6527243Srobj 			val++;
6537243Srobj 		/* FALLTHRU */
6547243Srobj 	case IPMI_DATA_FMT_TWOSCOMP:
6557243Srobj 		*result = (double)(((m * (int8_t)val) +
6567243Srobj 		    (b * pow(10, k1))) * pow(10, k2));
6577243Srobj 		break;
6587243Srobj 	default:
6597243Srobj 		/* This sensor does not return a numeric reading */
6607243Srobj 		return (-1);
6617243Srobj 	}
6627243Srobj 
6637243Srobj 	switch (sensor->is_fs_sensor_linear_type) {
6647243Srobj 	case SDR_SENSOR_L_LN:
6657243Srobj 		*result = log(*result);
6667243Srobj 		break;
6677243Srobj 	case SDR_SENSOR_L_LOG10:
6687243Srobj 		*result = log10(*result);
6697243Srobj 		break;
6707243Srobj 	case SDR_SENSOR_L_LOG2:
6717243Srobj 		*result = (double)(log(*result) / log(2.0));
6727243Srobj 		break;
6737243Srobj 	case SDR_SENSOR_L_E:
6747243Srobj 		*result = exp(*result);
6757243Srobj 		break;
6767243Srobj 	case SDR_SENSOR_L_EXP10:
6777243Srobj 		*result = pow(10.0, *result);
6787243Srobj 		break;
6797243Srobj 	case SDR_SENSOR_L_EXP2:
6807243Srobj 		*result = pow(2.0, *result);
6817243Srobj 		break;
6827243Srobj 	case SDR_SENSOR_L_1_X:
6837243Srobj 		*result = pow(*result, -1.0);	/* 1/x w/o exception */
6847243Srobj 		break;
6857243Srobj 	case SDR_SENSOR_L_SQR:
6867243Srobj 		*result = pow(*result, 2.0);
6877243Srobj 		break;
6887243Srobj 	case SDR_SENSOR_L_CUBE:
6897243Srobj 		*result = pow(*result, 3.0);
6907243Srobj 		break;
6917243Srobj 	case SDR_SENSOR_L_SQRT:
6927243Srobj 		*result = sqrt(*result);
6937243Srobj 		break;
6947243Srobj 	case SDR_SENSOR_L_CUBERT:
6957243Srobj 		*result = cbrt(*result);
6967243Srobj 		break;
6977243Srobj 	case SDR_SENSOR_L_LINEAR:
6987243Srobj 	default:
6997243Srobj 		break;
7007243Srobj 	}
7017243Srobj 	return (0);
7027243Srobj }
703