xref: /titanic_52/usr/src/lib/libipmi/common/ipmi_sdr.c (revision 81d9f076db88c1f40c85831ce1ebb444a209c5a8)
19113a79cSeschrock /*
29113a79cSeschrock  * CDDL HEADER START
39113a79cSeschrock  *
49113a79cSeschrock  * The contents of this file are subject to the terms of the
59113a79cSeschrock  * Common Development and Distribution License (the "License").
69113a79cSeschrock  * You may not use this file except in compliance with the License.
79113a79cSeschrock  *
89113a79cSeschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99113a79cSeschrock  * or http://www.opensolaris.org/os/licensing.
109113a79cSeschrock  * See the License for the specific language governing permissions
119113a79cSeschrock  * and limitations under the License.
129113a79cSeschrock  *
139113a79cSeschrock  * When distributing Covered Code, include this CDDL HEADER in each
149113a79cSeschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159113a79cSeschrock  * If applicable, add the following below this CDDL HEADER, with the
169113a79cSeschrock  * fields enclosed by brackets "[]" replaced with your own identifying
179113a79cSeschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
189113a79cSeschrock  *
199113a79cSeschrock  * CDDL HEADER END
209113a79cSeschrock  */
219113a79cSeschrock 
229113a79cSeschrock /*
23*81d9f076SRobert Johnston  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
249113a79cSeschrock  * Use is subject to license terms.
259113a79cSeschrock  */
269113a79cSeschrock 
279113a79cSeschrock #include <libipmi.h>
289113a79cSeschrock #include <stddef.h>
299113a79cSeschrock #include <string.h>
302eeaed14Srobj #include <strings.h>
31825ba0f2Srobj #include <math.h>
329113a79cSeschrock 
339113a79cSeschrock #include "ipmi_impl.h"
349113a79cSeschrock 
35825ba0f2Srobj /*
36825ba0f2Srobj  * This macros are used by ipmi_sdr_conv_reading.  They were taken verbatim from
37825ba0f2Srobj  * the source for ipmitool (v1.88)
38825ba0f2Srobj  */
39825ba0f2Srobj #define	tos32(val, bits)	((val & ((1<<((bits)-1)))) ? (-((val) & \
40825ba0f2Srobj 				(1<<((bits)-1))) | (val)) : (val))
41825ba0f2Srobj 
42825ba0f2Srobj #define	__TO_TOL(mtol)	(uint16_t)(BSWAP_16(mtol) & 0x3f)
43825ba0f2Srobj 
44825ba0f2Srobj #define	__TO_M(mtol)	(int16_t)(tos32((((BSWAP_16(mtol) & 0xff00) >> 8) | \
45825ba0f2Srobj 				((BSWAP_16(mtol) & 0xc0) << 2)), 10))
46825ba0f2Srobj 
47825ba0f2Srobj #define	__TO_B(bacc)	(int32_t)(tos32((((BSWAP_32(bacc) & \
48825ba0f2Srobj 				0xff000000) >> 24) | \
49825ba0f2Srobj 				((BSWAP_32(bacc) & 0xc00000) >> 14)), 10))
50825ba0f2Srobj 
51825ba0f2Srobj #define	__TO_ACC(bacc)	(uint32_t)(((BSWAP_32(bacc) & 0x3f0000) >> 16) | \
52825ba0f2Srobj 				((BSWAP_32(bacc) & 0xf000) >> 6))
53825ba0f2Srobj 
54825ba0f2Srobj #define	__TO_ACC_EXP(bacc)	(uint32_t)((BSWAP_32(bacc) & 0xc00) >> 10)
55825ba0f2Srobj #define	__TO_R_EXP(bacc)	(int32_t)(tos32(((BSWAP_32(bacc) & 0xf0) >> 4),\
56825ba0f2Srobj 				4))
57825ba0f2Srobj #define	__TO_B_EXP(bacc)	(int32_t)(tos32((BSWAP_32(bacc) & 0xf), 4))
58825ba0f2Srobj 
59825ba0f2Srobj #define	SDR_SENSOR_L_LINEAR	0x00
60825ba0f2Srobj #define	SDR_SENSOR_L_LN		0x01
61825ba0f2Srobj #define	SDR_SENSOR_L_LOG10	0x02
62825ba0f2Srobj #define	SDR_SENSOR_L_LOG2	0x03
63825ba0f2Srobj #define	SDR_SENSOR_L_E		0x04
64825ba0f2Srobj #define	SDR_SENSOR_L_EXP10	0x05
65825ba0f2Srobj #define	SDR_SENSOR_L_EXP2	0x06
66825ba0f2Srobj #define	SDR_SENSOR_L_1_X	0x07
67825ba0f2Srobj #define	SDR_SENSOR_L_SQR	0x08
68825ba0f2Srobj #define	SDR_SENSOR_L_CUBE	0x09
69825ba0f2Srobj #define	SDR_SENSOR_L_SQRT	0x0a
70825ba0f2Srobj #define	SDR_SENSOR_L_CUBERT	0x0b
71825ba0f2Srobj #define	SDR_SENSOR_L_NONLINEAR	0x70
72825ba0f2Srobj 
73825ba0f2Srobj /*
74825ba0f2Srobj  * Analog sensor reading data formats
75825ba0f2Srobj  *
76825ba0f2Srobj  * See Section 43.1
77825ba0f2Srobj  */
78825ba0f2Srobj #define	IPMI_DATA_FMT_UNSIGNED	0
79825ba0f2Srobj #define	IPMI_DATA_FMT_ONESCOMP	1
80825ba0f2Srobj #define	IPMI_DATA_FMT_TWOSCOMP	2
81825ba0f2Srobj 
82e5dcf7beSRobert Johnston #define	IPMI_SDR_HDR_SZ		offsetof(ipmi_sdr_t, is_record)
83e5dcf7beSRobert Johnston 
842eeaed14Srobj typedef struct ipmi_sdr_cache_ent {
852eeaed14Srobj 	char				*isc_name;
862eeaed14Srobj 	struct ipmi_sdr			*isc_sdr;
872eeaed14Srobj 	ipmi_hash_link_t		isc_link;
882eeaed14Srobj } ipmi_sdr_cache_ent_t;
892eeaed14Srobj 
909113a79cSeschrock typedef struct ipmi_cmd_get_sdr {
919113a79cSeschrock 	uint16_t	ic_gs_resid;
929113a79cSeschrock 	uint16_t	ic_gs_recid;
939113a79cSeschrock 	uint8_t		ic_gs_offset;
949113a79cSeschrock 	uint8_t		ic_gs_len;
959113a79cSeschrock } ipmi_cmd_get_sdr_t;
969113a79cSeschrock 
979113a79cSeschrock typedef struct ipmi_rsp_get_sdr {
989113a79cSeschrock 	uint16_t	ir_gs_next;
999113a79cSeschrock 	uint8_t		ir_gs_record[1];
1009113a79cSeschrock } ipmi_rsp_get_sdr_t;
1019113a79cSeschrock 
1029113a79cSeschrock /*
1032eeaed14Srobj  * "Get SDR Repostiory Info" command.
1042eeaed14Srobj  */
1052eeaed14Srobj ipmi_sdr_info_t *
1062eeaed14Srobj ipmi_sdr_get_info(ipmi_handle_t *ihp)
1072eeaed14Srobj {
1082eeaed14Srobj 	ipmi_cmd_t cmd, *rsp;
1092eeaed14Srobj 	ipmi_sdr_info_t *sip;
110e1a24155Srobj 	uint16_t tmp16;
111e1a24155Srobj 	uint32_t tmp32;
1122eeaed14Srobj 
1132eeaed14Srobj 	cmd.ic_netfn = IPMI_NETFN_STORAGE;
1142eeaed14Srobj 	cmd.ic_lun = 0;
1152eeaed14Srobj 	cmd.ic_cmd = IPMI_CMD_GET_SDR_INFO;
1162eeaed14Srobj 	cmd.ic_dlen = 0;
1172eeaed14Srobj 	cmd.ic_data = NULL;
1182eeaed14Srobj 
1192eeaed14Srobj 	if ((rsp = ipmi_send(ihp, &cmd)) == NULL)
1202eeaed14Srobj 		return (NULL);
1212eeaed14Srobj 
1222eeaed14Srobj 	sip = rsp->ic_data;
1232eeaed14Srobj 
124e1a24155Srobj 	tmp16 = LE_IN16(&sip->isi_record_count);
125e1a24155Srobj 	(void) memcpy(&sip->isi_record_count, &tmp16, sizeof (tmp16));
126e1a24155Srobj 
127e1a24155Srobj 	tmp16 = LE_IN16(&sip->isi_free_space);
128e1a24155Srobj 	(void) memcpy(&sip->isi_free_space, &tmp16, sizeof (tmp16));
129e1a24155Srobj 
130e1a24155Srobj 	tmp32 = LE_IN32(&sip->isi_add_ts);
131e1a24155Srobj 	(void) memcpy(&sip->isi_add_ts, &tmp32, sizeof (tmp32));
132e1a24155Srobj 
133e1a24155Srobj 	tmp32 = LE_IN32(&sip->isi_erase_ts);
134e1a24155Srobj 	(void) memcpy(&sip->isi_erase_ts, &tmp32, sizeof (tmp32));
1352eeaed14Srobj 
1362eeaed14Srobj 	return (sip);
1372eeaed14Srobj }
1382eeaed14Srobj 
1392eeaed14Srobj /*
1409113a79cSeschrock  * Issue the "Reserve SDR Repository" command.
1419113a79cSeschrock  */
1429113a79cSeschrock static int
1439113a79cSeschrock ipmi_sdr_reserve_repository(ipmi_handle_t *ihp)
1449113a79cSeschrock {
1459113a79cSeschrock 	ipmi_cmd_t cmd, *rsp;
1469113a79cSeschrock 
1479113a79cSeschrock 	cmd.ic_netfn = IPMI_NETFN_STORAGE;
1489113a79cSeschrock 	cmd.ic_lun = 0;
1499113a79cSeschrock 	cmd.ic_cmd = IPMI_CMD_RESERVE_SDR_REPOSITORY;
1509113a79cSeschrock 	cmd.ic_dlen = 0;
1519113a79cSeschrock 	cmd.ic_data = NULL;
1529113a79cSeschrock 
1539113a79cSeschrock 	if ((rsp = ipmi_send(ihp, &cmd)) == NULL)
1549113a79cSeschrock 		return (-1);
1559113a79cSeschrock 
1569113a79cSeschrock 	ihp->ih_reservation = *((uint16_t *)rsp->ic_data);
1579113a79cSeschrock 	return (0);
1589113a79cSeschrock }
1599113a79cSeschrock 
1609113a79cSeschrock /*
1612eeaed14Srobj  * Returns B_TRUE if the repository has changed since the cached copy was last
1622eeaed14Srobj  * referenced.
1632eeaed14Srobj  */
1642eeaed14Srobj boolean_t
1652eeaed14Srobj ipmi_sdr_changed(ipmi_handle_t *ihp)
1662eeaed14Srobj {
1672eeaed14Srobj 	ipmi_sdr_info_t *sip;
1682eeaed14Srobj 
1692eeaed14Srobj 	if ((sip = ipmi_sdr_get_info(ihp)) == NULL)
1702eeaed14Srobj 		return (B_TRUE);
1712eeaed14Srobj 
1722eeaed14Srobj 	return (sip->isi_add_ts > ihp->ih_sdr_ts ||
1732eeaed14Srobj 	    sip->isi_erase_ts > ihp->ih_sdr_ts ||
1742eeaed14Srobj 	    ipmi_hash_first(ihp->ih_sdr_cache) == NULL);
1752eeaed14Srobj }
1762eeaed14Srobj 
1772eeaed14Srobj /*
1789113a79cSeschrock  * Refresh the cache of sensor data records.
1799113a79cSeschrock  */
1802eeaed14Srobj int
1819113a79cSeschrock ipmi_sdr_refresh(ipmi_handle_t *ihp)
1829113a79cSeschrock {
1839113a79cSeschrock 	uint16_t id;
1849113a79cSeschrock 	ipmi_sdr_t *sdr;
1859113a79cSeschrock 	ipmi_sdr_cache_ent_t *ent;
186e5dcf7beSRobert Johnston 	size_t namelen;
1872eeaed14Srobj 	uint8_t type;
1882eeaed14Srobj 	char *name;
1892eeaed14Srobj 	ipmi_sdr_info_t *sip;
190*81d9f076SRobert Johnston 	uint32_t isi_add_ts, isi_erase_ts;
1912eeaed14Srobj 
1922eeaed14Srobj 	if ((sip = ipmi_sdr_get_info(ihp)) == NULL)
1932eeaed14Srobj 		return (-1);
1942eeaed14Srobj 
195*81d9f076SRobert Johnston 	(void) memcpy(&isi_add_ts, &sip->isi_add_ts, sizeof (uint32_t));
196*81d9f076SRobert Johnston 	(void) memcpy(&isi_erase_ts, &sip->isi_erase_ts, sizeof (uint32_t));
197*81d9f076SRobert Johnston 	if (isi_add_ts <= ihp->ih_sdr_ts &&
198*81d9f076SRobert Johnston 	    isi_erase_ts <= ihp->ih_sdr_ts &&
1992eeaed14Srobj 	    ipmi_hash_first(ihp->ih_sdr_cache) != NULL)
2002eeaed14Srobj 		return (0);
2019113a79cSeschrock 
20292ba7109Seschrock 	ipmi_sdr_clear(ihp);
2032eeaed14Srobj 	ipmi_entity_clear(ihp);
204*81d9f076SRobert Johnston 	ihp->ih_sdr_ts = MAX(isi_add_ts, isi_erase_ts);
2059113a79cSeschrock 
2069113a79cSeschrock 	/*
2079113a79cSeschrock 	 * Iterate over all existing SDRs and add them to the cache.
2089113a79cSeschrock 	 */
2099113a79cSeschrock 	id = IPMI_SDR_FIRST;
2109113a79cSeschrock 	while (id != IPMI_SDR_LAST) {
2119113a79cSeschrock 		if ((sdr = ipmi_sdr_get(ihp, id, &id)) == NULL)
212283bfb4dSEric Schrock 			goto error;
2139113a79cSeschrock 
2149113a79cSeschrock 		/*
2152eeaed14Srobj 		 * Extract the name from the record-specific data.
2169113a79cSeschrock 		 */
2179113a79cSeschrock 		switch (sdr->is_type) {
2189113a79cSeschrock 		case IPMI_SDR_TYPE_GENERIC_LOCATOR:
2192eeaed14Srobj 			{
2202eeaed14Srobj 				ipmi_sdr_generic_locator_t *glp =
2212eeaed14Srobj 				    (ipmi_sdr_generic_locator_t *)
2222eeaed14Srobj 				    sdr->is_record;
2232eeaed14Srobj 				namelen = glp->is_gl_idlen;
2242eeaed14Srobj 				type = glp->is_gl_idtype;
2252eeaed14Srobj 				name = glp->is_gl_idstring;
2269113a79cSeschrock 				break;
2272eeaed14Srobj 			}
2289113a79cSeschrock 
2299113a79cSeschrock 		case IPMI_SDR_TYPE_FRU_LOCATOR:
2302eeaed14Srobj 			{
2312eeaed14Srobj 				ipmi_sdr_fru_locator_t *flp =
2322eeaed14Srobj 				    (ipmi_sdr_fru_locator_t *)
2332eeaed14Srobj 				    sdr->is_record;
2342eeaed14Srobj 				namelen = flp->is_fl_idlen;
2352eeaed14Srobj 				name = flp->is_fl_idstring;
2362eeaed14Srobj 				type = flp->is_fl_idtype;
2379113a79cSeschrock 				break;
2389113a79cSeschrock 			}
2399113a79cSeschrock 
2402eeaed14Srobj 		case IPMI_SDR_TYPE_COMPACT_SENSOR:
2412eeaed14Srobj 			{
2422eeaed14Srobj 				ipmi_sdr_compact_sensor_t *csp =
2432eeaed14Srobj 				    (ipmi_sdr_compact_sensor_t *)
2442eeaed14Srobj 				    sdr->is_record;
245e1a24155Srobj 				uint16_t tmp;
246e1a24155Srobj 
2472eeaed14Srobj 				namelen = csp->is_cs_idlen;
2482eeaed14Srobj 				type = csp->is_cs_idtype;
2492eeaed14Srobj 				name = csp->is_cs_idstring;
2502eeaed14Srobj 
251e1a24155Srobj 				tmp = LE_IN16(&csp->is_cs_assert_mask);
252e1a24155Srobj 				(void) memcpy(&csp->is_cs_assert_mask, &tmp,
253e1a24155Srobj 				    sizeof (tmp));
254e1a24155Srobj 
255e1a24155Srobj 				tmp = LE_IN16(&csp->is_cs_deassert_mask);
256e1a24155Srobj 				(void) memcpy(&csp->is_cs_deassert_mask, &tmp,
257e1a24155Srobj 				    sizeof (tmp));
258e1a24155Srobj 
259e1a24155Srobj 				tmp = LE_IN16(&csp->is_cs_reading_mask);
260e1a24155Srobj 				(void) memcpy(&csp->is_cs_reading_mask, &tmp,
261e1a24155Srobj 				    sizeof (tmp));
2622eeaed14Srobj 				break;
2639113a79cSeschrock 			}
2649113a79cSeschrock 
2652eeaed14Srobj 		case IPMI_SDR_TYPE_FULL_SENSOR:
2662eeaed14Srobj 			{
267e1a24155Srobj 				ipmi_sdr_full_sensor_t *fsp =
2682eeaed14Srobj 				    (ipmi_sdr_full_sensor_t *)
2692eeaed14Srobj 				    sdr->is_record;
270e1a24155Srobj 				uint16_t tmp;
2712eeaed14Srobj 
272e1a24155Srobj 				namelen = fsp->is_fs_idlen;
273e1a24155Srobj 				type = fsp->is_fs_idtype;
274e1a24155Srobj 				name = fsp->is_fs_idstring;
275e1a24155Srobj 
276e1a24155Srobj 				tmp = LE_IN16(&fsp->is_fs_assert_mask);
277e1a24155Srobj 				(void) memcpy(&fsp->is_fs_assert_mask, &tmp,
278e1a24155Srobj 				    sizeof (tmp));
279e1a24155Srobj 
280e1a24155Srobj 				tmp = LE_IN16(&fsp->is_fs_deassert_mask);
281e1a24155Srobj 				(void) memcpy(&fsp->is_fs_deassert_mask, &tmp,
282e1a24155Srobj 				    sizeof (tmp));
283e1a24155Srobj 
284e1a24155Srobj 				tmp = LE_IN16(&fsp->is_fs_reading_mask);
285e1a24155Srobj 				(void) memcpy(&fsp->is_fs_reading_mask, &tmp,
286e1a24155Srobj 				    sizeof (tmp));
2872eeaed14Srobj 				break;
2889113a79cSeschrock 			}
2899113a79cSeschrock 
2902eeaed14Srobj 		case IPMI_SDR_TYPE_EVENT_ONLY:
2912eeaed14Srobj 			{
2922eeaed14Srobj 				ipmi_sdr_event_only_t *esp =
2932eeaed14Srobj 				    (ipmi_sdr_event_only_t *)
2942eeaed14Srobj 				    sdr->is_record;
2952eeaed14Srobj 				namelen = esp->is_eo_idlen;
2962eeaed14Srobj 				type = esp->is_eo_idtype;
2972eeaed14Srobj 				name = esp->is_eo_idstring;
2982eeaed14Srobj 				break;
2992eeaed14Srobj 			}
3002eeaed14Srobj 
3012eeaed14Srobj 		case IPMI_SDR_TYPE_MANAGEMENT_LOCATOR:
3022eeaed14Srobj 			{
3032eeaed14Srobj 				ipmi_sdr_management_locator_t *msp =
3042eeaed14Srobj 				    (ipmi_sdr_management_locator_t *)
3052eeaed14Srobj 				    sdr->is_record;
3062eeaed14Srobj 				namelen = msp->is_ml_idlen;
3072eeaed14Srobj 				type = msp->is_ml_idtype;
3082eeaed14Srobj 				name = msp->is_ml_idstring;
3092eeaed14Srobj 				break;
3102eeaed14Srobj 			}
3112eeaed14Srobj 
3122eeaed14Srobj 		case IPMI_SDR_TYPE_MANAGEMENT_CONFIRMATION:
3132eeaed14Srobj 			{
3142eeaed14Srobj 				ipmi_sdr_management_confirmation_t *mcp =
3152eeaed14Srobj 				    (ipmi_sdr_management_confirmation_t *)
3162eeaed14Srobj 				    sdr->is_record;
317e1a24155Srobj 				uint16_t tmp;
318e1a24155Srobj 
3192eeaed14Srobj 				name = NULL;
320e1a24155Srobj 				tmp = LE_IN16(&mcp->is_mc_product);
321e1a24155Srobj 				(void) memcpy(&mcp->is_mc_product, &tmp,
322e1a24155Srobj 				    sizeof (tmp));
3232eeaed14Srobj 				break;
3242eeaed14Srobj 			}
3252eeaed14Srobj 
3262eeaed14Srobj 		default:
3272eeaed14Srobj 			name = NULL;
3282eeaed14Srobj 		}
3292eeaed14Srobj 
3302eeaed14Srobj 		if ((ent = ipmi_zalloc(ihp,
331e5dcf7beSRobert Johnston 		    sizeof (ipmi_sdr_cache_ent_t))) == NULL) {
332e5dcf7beSRobert Johnston 			free(sdr);
333283bfb4dSEric Schrock 			goto error;
3342eeaed14Srobj 		}
335e5dcf7beSRobert Johnston 
336e5dcf7beSRobert Johnston 		ent->isc_sdr = sdr;
3372eeaed14Srobj 
3382eeaed14Srobj 		if (name != NULL) {
3392eeaed14Srobj 			if ((ent->isc_name = ipmi_alloc(ihp, namelen + 1)) ==
3402eeaed14Srobj 			    NULL) {
3412eeaed14Srobj 				ipmi_free(ihp, ent->isc_sdr);
3422eeaed14Srobj 				ipmi_free(ihp, ent);
343283bfb4dSEric Schrock 				goto error;
3442eeaed14Srobj 			}
3452eeaed14Srobj 
3462eeaed14Srobj 			ipmi_decode_string(type, namelen, name, ent->isc_name);
3472eeaed14Srobj 		}
3482eeaed14Srobj 
3492092e3b9Seschrock 		/*
3502092e3b9Seschrock 		 * This should never happen.  It means that the SP has returned
3512092e3b9Seschrock 		 * a SDR record twice, with the same name and ID.  This has
3522092e3b9Seschrock 		 * been observed on service processors that don't correctly
3532092e3b9Seschrock 		 * return SDR_LAST during iteration, so assume we've looped in
3542092e3b9Seschrock 		 * the SDR and return gracefully.
3552092e3b9Seschrock 		 */
3562092e3b9Seschrock 		if (ipmi_hash_lookup(ihp->ih_sdr_cache, ent) != NULL) {
3572092e3b9Seschrock 			ipmi_free(ihp, ent->isc_sdr);
3582092e3b9Seschrock 			ipmi_free(ihp, ent->isc_name);
3592092e3b9Seschrock 			ipmi_free(ihp, ent);
3602092e3b9Seschrock 			break;
3612092e3b9Seschrock 		}
3622092e3b9Seschrock 
3632eeaed14Srobj 		ipmi_hash_insert(ihp->ih_sdr_cache, ent);
3642eeaed14Srobj 	}
3652eeaed14Srobj 
3662eeaed14Srobj 	return (0);
367283bfb4dSEric Schrock 
368283bfb4dSEric Schrock error:
369283bfb4dSEric Schrock 	ipmi_sdr_clear(ihp);
370283bfb4dSEric Schrock 	ipmi_entity_clear(ihp);
371283bfb4dSEric Schrock 	return (-1);
3722eeaed14Srobj }
3732eeaed14Srobj 
3742eeaed14Srobj /*
3752eeaed14Srobj  * Hash routines.  We allow lookup by name, but since not all entries have
3762eeaed14Srobj  * names, we fall back to the entry pointer, which is guaranteed to be unique.
3772eeaed14Srobj  * The end result is that entities without names cannot be looked up, but will
3782eeaed14Srobj  * show up during iteration.
3792eeaed14Srobj  */
3802eeaed14Srobj static const void *
3812eeaed14Srobj ipmi_sdr_hash_convert(const void *p)
3822eeaed14Srobj {
3832eeaed14Srobj 	return (p);
3842eeaed14Srobj }
3852eeaed14Srobj 
3862eeaed14Srobj static ulong_t
3872eeaed14Srobj ipmi_sdr_hash_compute(const void *p)
3882eeaed14Srobj {
3892eeaed14Srobj 	const ipmi_sdr_cache_ent_t *ep = p;
3902eeaed14Srobj 
3912eeaed14Srobj 	if (ep->isc_name)
3922eeaed14Srobj 		return (ipmi_hash_strhash(ep->isc_name));
3932eeaed14Srobj 	else
3942eeaed14Srobj 		return (ipmi_hash_ptrhash(ep));
3952eeaed14Srobj }
3962eeaed14Srobj 
3972eeaed14Srobj static int
3982eeaed14Srobj ipmi_sdr_hash_compare(const void *a, const void *b)
3992eeaed14Srobj {
4002eeaed14Srobj 	const ipmi_sdr_cache_ent_t *ap = a;
4012eeaed14Srobj 	const ipmi_sdr_cache_ent_t *bp = b;
4022eeaed14Srobj 
4032eeaed14Srobj 	if (ap->isc_name == NULL || bp->isc_name == NULL)
4042eeaed14Srobj 		return (-1);
4052eeaed14Srobj 
4062092e3b9Seschrock 	if (strcmp(ap->isc_name, bp->isc_name) != 0)
4072092e3b9Seschrock 		return (-1);
4082092e3b9Seschrock 
4092092e3b9Seschrock 	/*
4102092e3b9Seschrock 	 * While it is strange for a service processor to report multiple
4112092e3b9Seschrock 	 * entries with the same name, we allow it by treating the (name, id)
4122092e3b9Seschrock 	 * as the unique identifier.  When looking up by name, the SDR pointer
4132092e3b9Seschrock 	 * is NULL, and we return the first matching name.
4142092e3b9Seschrock 	 */
4152092e3b9Seschrock 	if (ap->isc_sdr == NULL || bp->isc_sdr == NULL)
4162092e3b9Seschrock 		return (0);
4172092e3b9Seschrock 
4182092e3b9Seschrock 	if (ap->isc_sdr->is_id == bp->isc_sdr->is_id)
4192092e3b9Seschrock 		return (0);
4202092e3b9Seschrock 	else
4212092e3b9Seschrock 		return (-1);
4222eeaed14Srobj }
4232eeaed14Srobj 
4242eeaed14Srobj int
4252eeaed14Srobj ipmi_sdr_init(ipmi_handle_t *ihp)
4262eeaed14Srobj {
4272eeaed14Srobj 	if ((ihp->ih_sdr_cache = ipmi_hash_create(ihp,
4282eeaed14Srobj 	    offsetof(ipmi_sdr_cache_ent_t, isc_link),
4292eeaed14Srobj 	    ipmi_sdr_hash_convert, ipmi_sdr_hash_compute,
4302eeaed14Srobj 	    ipmi_sdr_hash_compare)) == NULL)
4312eeaed14Srobj 		return (-1);
4322eeaed14Srobj 
4339113a79cSeschrock 	return (0);
4349113a79cSeschrock }
4359113a79cSeschrock 
43692ba7109Seschrock void
43792ba7109Seschrock ipmi_sdr_clear(ipmi_handle_t *ihp)
43892ba7109Seschrock {
4392eeaed14Srobj 	ipmi_sdr_cache_ent_t *ent;
44092ba7109Seschrock 
4412eeaed14Srobj 	while ((ent = ipmi_hash_first(ihp->ih_sdr_cache)) != NULL) {
4422eeaed14Srobj 		ipmi_hash_remove(ihp->ih_sdr_cache, ent);
4432eeaed14Srobj 		ipmi_free(ihp, ent->isc_sdr);
4442eeaed14Srobj 		ipmi_free(ihp, ent->isc_name);
44592ba7109Seschrock 		ipmi_free(ihp, ent);
4462eeaed14Srobj 	}
4472eeaed14Srobj }
4482eeaed14Srobj 
4492eeaed14Srobj void
4502eeaed14Srobj ipmi_sdr_fini(ipmi_handle_t *ihp)
4512eeaed14Srobj {
4522eeaed14Srobj 	if (ihp->ih_sdr_cache != NULL) {
4532eeaed14Srobj 		ipmi_sdr_clear(ihp);
4542eeaed14Srobj 		ipmi_hash_destroy(ihp->ih_sdr_cache);
45592ba7109Seschrock 	}
45692ba7109Seschrock }
45792ba7109Seschrock 
4589113a79cSeschrock ipmi_sdr_t *
4599113a79cSeschrock ipmi_sdr_get(ipmi_handle_t *ihp, uint16_t id, uint16_t *next)
4609113a79cSeschrock {
461e5dcf7beSRobert Johnston 	uint8_t offset = IPMI_SDR_HDR_SZ, count = 0, chunksz = 16, sdr_sz;
4629113a79cSeschrock 	ipmi_cmd_t cmd, *rsp;
4639113a79cSeschrock 	ipmi_cmd_get_sdr_t req;
464e5dcf7beSRobert Johnston 	ipmi_sdr_t *sdr;
465e5dcf7beSRobert Johnston 	int i = 0;
466e5dcf7beSRobert Johnston 	char *buf;
4679113a79cSeschrock 
4689113a79cSeschrock 	req.ic_gs_resid = ihp->ih_reservation;
4699113a79cSeschrock 	req.ic_gs_recid = id;
4709113a79cSeschrock 
4719113a79cSeschrock 	cmd.ic_netfn = IPMI_NETFN_STORAGE;
4729113a79cSeschrock 	cmd.ic_lun = 0;
4739113a79cSeschrock 	cmd.ic_cmd = IPMI_CMD_GET_SDR;
4749113a79cSeschrock 	cmd.ic_dlen = sizeof (req);
4759113a79cSeschrock 	cmd.ic_data = &req;
4769113a79cSeschrock 
477e5dcf7beSRobert Johnston 	/*
478e5dcf7beSRobert Johnston 	 * The size of the SDR is contained in the 5th byte of the SDR header,
479e5dcf7beSRobert Johnston 	 * so we'll read the first 5 bytes to get the size, so we know how big
480e5dcf7beSRobert Johnston 	 * to make the buffer.
481e5dcf7beSRobert Johnston 	 */
482e5dcf7beSRobert Johnston 	req.ic_gs_offset = 0;
483e5dcf7beSRobert Johnston 	req.ic_gs_len = IPMI_SDR_HDR_SZ;
4849113a79cSeschrock 	for (i = 0; i < ihp->ih_retries; i++) {
4852eeaed14Srobj 		if ((rsp = ipmi_send(ihp, &cmd)) != NULL)
4862eeaed14Srobj 			break;
4872eeaed14Srobj 
4889113a79cSeschrock 		if (ipmi_errno(ihp) != EIPMI_INVALID_RESERVATION)
4899113a79cSeschrock 			return (NULL);
4909113a79cSeschrock 
4919113a79cSeschrock 		if (ipmi_sdr_reserve_repository(ihp) != 0)
4929113a79cSeschrock 			return (NULL);
4939113a79cSeschrock 		req.ic_gs_resid = ihp->ih_reservation;
4949113a79cSeschrock 	}
4959113a79cSeschrock 	if (rsp == NULL)
4969113a79cSeschrock 		return (NULL);
4979113a79cSeschrock 
498e5dcf7beSRobert Johnston 	sdr = (ipmi_sdr_t *)((ipmi_rsp_get_sdr_t *)rsp->ic_data)->ir_gs_record;
499e5dcf7beSRobert Johnston 	sdr_sz = sdr->is_length;
500e5dcf7beSRobert Johnston 
501e5dcf7beSRobert Johnston 	if ((buf = ipmi_zalloc(ihp, sdr_sz + IPMI_SDR_HDR_SZ)) == NULL) {
502e5dcf7beSRobert Johnston 		(void) ipmi_set_error(ihp, EIPMI_NOMEM, NULL);
5039113a79cSeschrock 		return (NULL);
5049113a79cSeschrock 	}
505e5dcf7beSRobert Johnston 	(void) memcpy(buf, (void *)sdr, IPMI_SDR_HDR_SZ);
5069113a79cSeschrock 
507e5dcf7beSRobert Johnston 	/*
508e5dcf7beSRobert Johnston 	 * Some SDRs can be bigger than the buffer sizes for a given bmc
509e5dcf7beSRobert Johnston 	 * interface.  Therefore we break up the process of reading in an entire
510e5dcf7beSRobert Johnston 	 * SDR into multiple smaller reads.
511e5dcf7beSRobert Johnston 	 */
512a759cb32SRobert Johnston 	while (count < sdr_sz) {
513e5dcf7beSRobert Johnston 		req.ic_gs_offset = offset;
514e5dcf7beSRobert Johnston 		if (chunksz > (sdr_sz - count))
515e5dcf7beSRobert Johnston 			chunksz = sdr_sz - count;
516e5dcf7beSRobert Johnston 		req.ic_gs_len = chunksz;
517e5dcf7beSRobert Johnston 		rsp = ipmi_send(ihp, &cmd);
5189113a79cSeschrock 
519e5dcf7beSRobert Johnston 		if (rsp != NULL) {
520e5dcf7beSRobert Johnston 			count += chunksz;
521e5dcf7beSRobert Johnston 			sdr = (ipmi_sdr_t *)
522e5dcf7beSRobert Johnston 			    ((ipmi_rsp_get_sdr_t *)rsp->ic_data)->ir_gs_record;
523e5dcf7beSRobert Johnston 			(void) memcpy(buf+offset, (void *)sdr, chunksz);
524e5dcf7beSRobert Johnston 			offset += chunksz;
525e5dcf7beSRobert Johnston 			i = 0;
526e5dcf7beSRobert Johnston 		} else if (ipmi_errno(ihp) == EIPMI_INVALID_RESERVATION) {
527a759cb32SRobert Johnston 			if (i >= ihp->ih_retries ||
528a759cb32SRobert Johnston 			    ipmi_sdr_reserve_repository(ihp) != 0) {
529e5dcf7beSRobert Johnston 				free(buf);
530e5dcf7beSRobert Johnston 				return (NULL);
531e5dcf7beSRobert Johnston 			}
532e5dcf7beSRobert Johnston 			req.ic_gs_resid = ihp->ih_reservation;
533e5dcf7beSRobert Johnston 			i++;
534e5dcf7beSRobert Johnston 		} else {
535e5dcf7beSRobert Johnston 			free(buf);
536e5dcf7beSRobert Johnston 			return (NULL);
537e5dcf7beSRobert Johnston 		}
538e5dcf7beSRobert Johnston 	}
539e5dcf7beSRobert Johnston 	*next = ((ipmi_rsp_get_sdr_t *)rsp->ic_data)->ir_gs_next;
540e5dcf7beSRobert Johnston 
541e5dcf7beSRobert Johnston 	return ((ipmi_sdr_t *)buf);
5429113a79cSeschrock }
5439113a79cSeschrock 
5442eeaed14Srobj int
5452eeaed14Srobj ipmi_sdr_iter(ipmi_handle_t *ihp, int (*func)(ipmi_handle_t *,
5462eeaed14Srobj     const char *, ipmi_sdr_t *, void *), void *data)
5479113a79cSeschrock {
5489113a79cSeschrock 	ipmi_sdr_cache_ent_t *ent;
5492eeaed14Srobj 	int ret;
5509113a79cSeschrock 
5512eeaed14Srobj 	if (ipmi_hash_first(ihp->ih_sdr_cache) == NULL &&
5522eeaed14Srobj 	    ipmi_sdr_refresh(ihp) != 0)
5532eeaed14Srobj 		return (-1);
5542eeaed14Srobj 
5552eeaed14Srobj 	for (ent = ipmi_hash_first(ihp->ih_sdr_cache); ent != NULL;
5562eeaed14Srobj 	    ent = ipmi_hash_next(ihp->ih_sdr_cache, ent)) {
5572eeaed14Srobj 		if ((ret = func(ihp, ent->isc_name, ent->isc_sdr, data)) != 0)
5582eeaed14Srobj 			return (ret);
5592eeaed14Srobj 	}
5602eeaed14Srobj 
5612eeaed14Srobj 	return (0);
5622eeaed14Srobj }
5632eeaed14Srobj 
5642eeaed14Srobj ipmi_sdr_t *
5652eeaed14Srobj ipmi_sdr_lookup(ipmi_handle_t *ihp, const char *idstr)
5662eeaed14Srobj {
5672eeaed14Srobj 	ipmi_sdr_cache_ent_t *ent, search;
5682eeaed14Srobj 
5692eeaed14Srobj 	if (ipmi_hash_first(ihp->ih_sdr_cache) == NULL &&
5709113a79cSeschrock 	    ipmi_sdr_refresh(ihp) != 0)
5719113a79cSeschrock 		return (NULL);
5729113a79cSeschrock 
5732eeaed14Srobj 	search.isc_name = (char *)idstr;
5742092e3b9Seschrock 	search.isc_sdr = NULL;
5752eeaed14Srobj 	if ((ent = ipmi_hash_lookup(ihp->ih_sdr_cache, &search)) == NULL) {
5769113a79cSeschrock 		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
5779113a79cSeschrock 		return (NULL);
5789113a79cSeschrock 	}
5799113a79cSeschrock 
5802eeaed14Srobj 	return (ent->isc_sdr);
5812eeaed14Srobj }
5822eeaed14Srobj 
5832eeaed14Srobj static void *
5842eeaed14Srobj ipmi_sdr_lookup_common(ipmi_handle_t *ihp, const char *idstr,
5852eeaed14Srobj     uint8_t type)
5862eeaed14Srobj {
5872eeaed14Srobj 	ipmi_sdr_t *sdrp;
5882eeaed14Srobj 
5892eeaed14Srobj 	if ((sdrp = ipmi_sdr_lookup(ihp, idstr)) == NULL)
5902eeaed14Srobj 		return (NULL);
5912eeaed14Srobj 
5922eeaed14Srobj 	if (sdrp->is_type != type) {
5932eeaed14Srobj 		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
5942eeaed14Srobj 		return (NULL);
5952eeaed14Srobj 	}
5962eeaed14Srobj 
5972eeaed14Srobj 	return (sdrp->is_record);
5982eeaed14Srobj }
5992eeaed14Srobj 
6002eeaed14Srobj ipmi_sdr_fru_locator_t *
6012eeaed14Srobj ipmi_sdr_lookup_fru(ipmi_handle_t *ihp, const char *idstr)
6022eeaed14Srobj {
6032eeaed14Srobj 	return (ipmi_sdr_lookup_common(ihp, idstr,
6042eeaed14Srobj 	    IPMI_SDR_TYPE_FRU_LOCATOR));
6052eeaed14Srobj }
6062eeaed14Srobj 
6079113a79cSeschrock ipmi_sdr_generic_locator_t *
6089113a79cSeschrock ipmi_sdr_lookup_generic(ipmi_handle_t *ihp, const char *idstr)
6099113a79cSeschrock {
6102eeaed14Srobj 	return (ipmi_sdr_lookup_common(ihp, idstr,
6112eeaed14Srobj 	    IPMI_SDR_TYPE_GENERIC_LOCATOR));
6129113a79cSeschrock }
6139113a79cSeschrock 
6142eeaed14Srobj ipmi_sdr_compact_sensor_t *
6152eeaed14Srobj ipmi_sdr_lookup_compact_sensor(ipmi_handle_t *ihp, const char *idstr)
6162eeaed14Srobj {
6172eeaed14Srobj 	return (ipmi_sdr_lookup_common(ihp, idstr,
6182eeaed14Srobj 	    IPMI_SDR_TYPE_COMPACT_SENSOR));
6192eeaed14Srobj }
6202eeaed14Srobj 
6212eeaed14Srobj ipmi_sdr_full_sensor_t *
6222eeaed14Srobj ipmi_sdr_lookup_full_sensor(ipmi_handle_t *ihp, const char *idstr)
6232eeaed14Srobj {
6242eeaed14Srobj 	return (ipmi_sdr_lookup_common(ihp, idstr,
6252eeaed14Srobj 	    IPMI_SDR_TYPE_FULL_SENSOR));
6269113a79cSeschrock }
627825ba0f2Srobj 
628825ba0f2Srobj /*
629825ba0f2Srobj  * Mostly taken from ipmitool source v1.88
630825ba0f2Srobj  *
631825ba0f2Srobj  * This function converts the raw sensor reading returned by
632825ba0f2Srobj  * ipmi_get_sensor_reading to a unit-based value of type double.
633825ba0f2Srobj  */
634825ba0f2Srobj int
635825ba0f2Srobj ipmi_sdr_conv_reading(ipmi_sdr_full_sensor_t *sensor, uint8_t val,
636825ba0f2Srobj     double *result)
637825ba0f2Srobj {
638825ba0f2Srobj 	int m, b, k1, k2;
639825ba0f2Srobj 
640825ba0f2Srobj 	m = __TO_M(sensor->is_fs_mtol);
641825ba0f2Srobj 	b = __TO_B(sensor->is_fs_bacc);
642825ba0f2Srobj 	k1 = __TO_B_EXP(sensor->is_fs_bacc);
643825ba0f2Srobj 	k2 = __TO_R_EXP(sensor->is_fs_bacc);
644825ba0f2Srobj 
645825ba0f2Srobj 	switch (sensor->is_fs_analog_fmt) {
646825ba0f2Srobj 	case IPMI_DATA_FMT_UNSIGNED:
647825ba0f2Srobj 		*result = (double)(((m * val) +
648825ba0f2Srobj 		    (b * pow(10, k1))) * pow(10, k2));
649825ba0f2Srobj 		break;
650825ba0f2Srobj 	case IPMI_DATA_FMT_ONESCOMP:
651825ba0f2Srobj 		if (val & 0x80)
652825ba0f2Srobj 			val++;
653825ba0f2Srobj 		/* FALLTHRU */
654825ba0f2Srobj 	case IPMI_DATA_FMT_TWOSCOMP:
655825ba0f2Srobj 		*result = (double)(((m * (int8_t)val) +
656825ba0f2Srobj 		    (b * pow(10, k1))) * pow(10, k2));
657825ba0f2Srobj 		break;
658825ba0f2Srobj 	default:
659825ba0f2Srobj 		/* This sensor does not return a numeric reading */
660825ba0f2Srobj 		return (-1);
661825ba0f2Srobj 	}
662825ba0f2Srobj 
663825ba0f2Srobj 	switch (sensor->is_fs_sensor_linear_type) {
664825ba0f2Srobj 	case SDR_SENSOR_L_LN:
665825ba0f2Srobj 		*result = log(*result);
666825ba0f2Srobj 		break;
667825ba0f2Srobj 	case SDR_SENSOR_L_LOG10:
668825ba0f2Srobj 		*result = log10(*result);
669825ba0f2Srobj 		break;
670825ba0f2Srobj 	case SDR_SENSOR_L_LOG2:
671825ba0f2Srobj 		*result = (double)(log(*result) / log(2.0));
672825ba0f2Srobj 		break;
673825ba0f2Srobj 	case SDR_SENSOR_L_E:
674825ba0f2Srobj 		*result = exp(*result);
675825ba0f2Srobj 		break;
676825ba0f2Srobj 	case SDR_SENSOR_L_EXP10:
677825ba0f2Srobj 		*result = pow(10.0, *result);
678825ba0f2Srobj 		break;
679825ba0f2Srobj 	case SDR_SENSOR_L_EXP2:
680825ba0f2Srobj 		*result = pow(2.0, *result);
681825ba0f2Srobj 		break;
682825ba0f2Srobj 	case SDR_SENSOR_L_1_X:
683825ba0f2Srobj 		*result = pow(*result, -1.0);	/* 1/x w/o exception */
684825ba0f2Srobj 		break;
685825ba0f2Srobj 	case SDR_SENSOR_L_SQR:
686825ba0f2Srobj 		*result = pow(*result, 2.0);
687825ba0f2Srobj 		break;
688825ba0f2Srobj 	case SDR_SENSOR_L_CUBE:
689825ba0f2Srobj 		*result = pow(*result, 3.0);
690825ba0f2Srobj 		break;
691825ba0f2Srobj 	case SDR_SENSOR_L_SQRT:
692825ba0f2Srobj 		*result = sqrt(*result);
693825ba0f2Srobj 		break;
694825ba0f2Srobj 	case SDR_SENSOR_L_CUBERT:
695825ba0f2Srobj 		*result = cbrt(*result);
696825ba0f2Srobj 		break;
697825ba0f2Srobj 	case SDR_SENSOR_L_LINEAR:
698825ba0f2Srobj 	default:
699825ba0f2Srobj 		break;
700825ba0f2Srobj 	}
701825ba0f2Srobj 	return (0);
702825ba0f2Srobj }
703