Home | History | Annotate | Download | only in common
      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 /*
     23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #include <libipmi.h>
     28 #include <stddef.h>
     29 #include <string.h>
     30 #include <strings.h>
     31 #include <math.h>
     32 
     33 #include "ipmi_impl.h"
     34 
     35 /*
     36  * This macros are used by ipmi_sdr_conv_reading.  They were taken verbatim from
     37  * the source for ipmitool (v1.88)
     38  */
     39 #define	tos32(val, bits)	((val & ((1<<((bits)-1)))) ? (-((val) & \
     40 				(1<<((bits)-1))) | (val)) : (val))
     41 
     42 #define	__TO_TOL(mtol)	(uint16_t)(BSWAP_16(mtol) & 0x3f)
     43 
     44 #define	__TO_M(mtol)	(int16_t)(tos32((((BSWAP_16(mtol) & 0xff00) >> 8) | \
     45 				((BSWAP_16(mtol) & 0xc0) << 2)), 10))
     46 
     47 #define	__TO_B(bacc)	(int32_t)(tos32((((BSWAP_32(bacc) & \
     48 				0xff000000) >> 24) | \
     49 				((BSWAP_32(bacc) & 0xc00000) >> 14)), 10))
     50 
     51 #define	__TO_ACC(bacc)	(uint32_t)(((BSWAP_32(bacc) & 0x3f0000) >> 16) | \
     52 				((BSWAP_32(bacc) & 0xf000) >> 6))
     53 
     54 #define	__TO_ACC_EXP(bacc)	(uint32_t)((BSWAP_32(bacc) & 0xc00) >> 10)
     55 #define	__TO_R_EXP(bacc)	(int32_t)(tos32(((BSWAP_32(bacc) & 0xf0) >> 4),\
     56 				4))
     57 #define	__TO_B_EXP(bacc)	(int32_t)(tos32((BSWAP_32(bacc) & 0xf), 4))
     58 
     59 #define	SDR_SENSOR_L_LINEAR	0x00
     60 #define	SDR_SENSOR_L_LN		0x01
     61 #define	SDR_SENSOR_L_LOG10	0x02
     62 #define	SDR_SENSOR_L_LOG2	0x03
     63 #define	SDR_SENSOR_L_E		0x04
     64 #define	SDR_SENSOR_L_EXP10	0x05
     65 #define	SDR_SENSOR_L_EXP2	0x06
     66 #define	SDR_SENSOR_L_1_X	0x07
     67 #define	SDR_SENSOR_L_SQR	0x08
     68 #define	SDR_SENSOR_L_CUBE	0x09
     69 #define	SDR_SENSOR_L_SQRT	0x0a
     70 #define	SDR_SENSOR_L_CUBERT	0x0b
     71 #define	SDR_SENSOR_L_NONLINEAR	0x70
     72 
     73 /*
     74  * Analog sensor reading data formats
     75  *
     76  * See Section 43.1
     77  */
     78 #define	IPMI_DATA_FMT_UNSIGNED	0
     79 #define	IPMI_DATA_FMT_ONESCOMP	1
     80 #define	IPMI_DATA_FMT_TWOSCOMP	2
     81 
     82 #define	IPMI_SDR_HDR_SZ		offsetof(ipmi_sdr_t, is_record)
     83 
     84 typedef struct ipmi_sdr_cache_ent {
     85 	char				*isc_name;
     86 	struct ipmi_sdr			*isc_sdr;
     87 	ipmi_hash_link_t		isc_link;
     88 } ipmi_sdr_cache_ent_t;
     89 
     90 typedef struct ipmi_cmd_get_sdr {
     91 	uint16_t	ic_gs_resid;
     92 	uint16_t	ic_gs_recid;
     93 	uint8_t		ic_gs_offset;
     94 	uint8_t		ic_gs_len;
     95 } ipmi_cmd_get_sdr_t;
     96 
     97 typedef struct ipmi_rsp_get_sdr {
     98 	uint16_t	ir_gs_next;
     99 	uint8_t		ir_gs_record[1];
    100 } ipmi_rsp_get_sdr_t;
    101 
    102 /*
    103  * "Get SDR Repostiory Info" command.
    104  */
    105 ipmi_sdr_info_t *
    106 ipmi_sdr_get_info(ipmi_handle_t *ihp)
    107 {
    108 	ipmi_cmd_t cmd, *rsp;
    109 	ipmi_sdr_info_t *sip;
    110 	uint16_t tmp16;
    111 	uint32_t tmp32;
    112 
    113 	cmd.ic_netfn = IPMI_NETFN_STORAGE;
    114 	cmd.ic_lun = 0;
    115 	cmd.ic_cmd = IPMI_CMD_GET_SDR_INFO;
    116 	cmd.ic_dlen = 0;
    117 	cmd.ic_data = NULL;
    118 
    119 	if ((rsp = ipmi_send(ihp, &cmd)) == NULL)
    120 		return (NULL);
    121 
    122 	sip = rsp->ic_data;
    123 
    124 	tmp16 = LE_IN16(&sip->isi_record_count);
    125 	(void) memcpy(&sip->isi_record_count, &tmp16, sizeof (tmp16));
    126 
    127 	tmp16 = LE_IN16(&sip->isi_free_space);
    128 	(void) memcpy(&sip->isi_free_space, &tmp16, sizeof (tmp16));
    129 
    130 	tmp32 = LE_IN32(&sip->isi_add_ts);
    131 	(void) memcpy(&sip->isi_add_ts, &tmp32, sizeof (tmp32));
    132 
    133 	tmp32 = LE_IN32(&sip->isi_erase_ts);
    134 	(void) memcpy(&sip->isi_erase_ts, &tmp32, sizeof (tmp32));
    135 
    136 	return (sip);
    137 }
    138 
    139 /*
    140  * Issue the "Reserve SDR Repository" command.
    141  */
    142 static int
    143 ipmi_sdr_reserve_repository(ipmi_handle_t *ihp)
    144 {
    145 	ipmi_cmd_t cmd, *rsp;
    146 
    147 	cmd.ic_netfn = IPMI_NETFN_STORAGE;
    148 	cmd.ic_lun = 0;
    149 	cmd.ic_cmd = IPMI_CMD_RESERVE_SDR_REPOSITORY;
    150 	cmd.ic_dlen = 0;
    151 	cmd.ic_data = NULL;
    152 
    153 	if ((rsp = ipmi_send(ihp, &cmd)) == NULL)
    154 		return (-1);
    155 
    156 	ihp->ih_reservation = *((uint16_t *)rsp->ic_data);
    157 	return (0);
    158 }
    159 
    160 /*
    161  * Returns B_TRUE if the repository has changed since the cached copy was last
    162  * referenced.
    163  */
    164 boolean_t
    165 ipmi_sdr_changed(ipmi_handle_t *ihp)
    166 {
    167 	ipmi_sdr_info_t *sip;
    168 
    169 	if ((sip = ipmi_sdr_get_info(ihp)) == NULL)
    170 		return (B_TRUE);
    171 
    172 	return (sip->isi_add_ts > ihp->ih_sdr_ts ||
    173 	    sip->isi_erase_ts > ihp->ih_sdr_ts ||
    174 	    ipmi_hash_first(ihp->ih_sdr_cache) == NULL);
    175 }
    176 
    177 /*
    178  * Refresh the cache of sensor data records.
    179  */
    180 int
    181 ipmi_sdr_refresh(ipmi_handle_t *ihp)
    182 {
    183 	uint16_t id;
    184 	ipmi_sdr_t *sdr;
    185 	ipmi_sdr_cache_ent_t *ent;
    186 	size_t namelen;
    187 	uint8_t type;
    188 	char *name;
    189 	ipmi_sdr_info_t *sip;
    190 
    191 	if ((sip = ipmi_sdr_get_info(ihp)) == NULL)
    192 		return (-1);
    193 
    194 	if (sip->isi_add_ts <= ihp->ih_sdr_ts &&
    195 	    sip->isi_erase_ts <= ihp->ih_sdr_ts &&
    196 	    ipmi_hash_first(ihp->ih_sdr_cache) != NULL)
    197 		return (0);
    198 
    199 	ipmi_sdr_clear(ihp);
    200 	ipmi_entity_clear(ihp);
    201 	ihp->ih_sdr_ts = MAX(sip->isi_add_ts, sip->isi_erase_ts);
    202 
    203 	/*
    204 	 * Iterate over all existing SDRs and add them to the cache.
    205 	 */
    206 	id = IPMI_SDR_FIRST;
    207 	while (id != IPMI_SDR_LAST) {
    208 		if ((sdr = ipmi_sdr_get(ihp, id, &id)) == NULL)
    209 			goto error;
    210 
    211 		/*
    212 		 * Extract the name from the record-specific data.
    213 		 */
    214 		switch (sdr->is_type) {
    215 		case IPMI_SDR_TYPE_GENERIC_LOCATOR:
    216 			{
    217 				ipmi_sdr_generic_locator_t *glp =
    218 				    (ipmi_sdr_generic_locator_t *)
    219 				    sdr->is_record;
    220 				namelen = glp->is_gl_idlen;
    221 				type = glp->is_gl_idtype;
    222 				name = glp->is_gl_idstring;
    223 				break;
    224 			}
    225 
    226 		case IPMI_SDR_TYPE_FRU_LOCATOR:
    227 			{
    228 				ipmi_sdr_fru_locator_t *flp =
    229 				    (ipmi_sdr_fru_locator_t *)
    230 				    sdr->is_record;
    231 				namelen = flp->is_fl_idlen;
    232 				name = flp->is_fl_idstring;
    233 				type = flp->is_fl_idtype;
    234 				break;
    235 			}
    236 
    237 		case IPMI_SDR_TYPE_COMPACT_SENSOR:
    238 			{
    239 				ipmi_sdr_compact_sensor_t *csp =
    240 				    (ipmi_sdr_compact_sensor_t *)
    241 				    sdr->is_record;
    242 				uint16_t tmp;
    243 
    244 				namelen = csp->is_cs_idlen;
    245 				type = csp->is_cs_idtype;
    246 				name = csp->is_cs_idstring;
    247 
    248 				tmp = LE_IN16(&csp->is_cs_assert_mask);
    249 				(void) memcpy(&csp->is_cs_assert_mask, &tmp,
    250 				    sizeof (tmp));
    251 
    252 				tmp = LE_IN16(&csp->is_cs_deassert_mask);
    253 				(void) memcpy(&csp->is_cs_deassert_mask, &tmp,
    254 				    sizeof (tmp));
    255 
    256 				tmp = LE_IN16(&csp->is_cs_reading_mask);
    257 				(void) memcpy(&csp->is_cs_reading_mask, &tmp,
    258 				    sizeof (tmp));
    259 				break;
    260 			}
    261 
    262 		case IPMI_SDR_TYPE_FULL_SENSOR:
    263 			{
    264 				ipmi_sdr_full_sensor_t *fsp =
    265 				    (ipmi_sdr_full_sensor_t *)
    266 				    sdr->is_record;
    267 				uint16_t tmp;
    268 
    269 				namelen = fsp->is_fs_idlen;
    270 				type = fsp->is_fs_idtype;
    271 				name = fsp->is_fs_idstring;
    272 
    273 				tmp = LE_IN16(&fsp->is_fs_assert_mask);
    274 				(void) memcpy(&fsp->is_fs_assert_mask, &tmp,
    275 				    sizeof (tmp));
    276 
    277 				tmp = LE_IN16(&fsp->is_fs_deassert_mask);
    278 				(void) memcpy(&fsp->is_fs_deassert_mask, &tmp,
    279 				    sizeof (tmp));
    280 
    281 				tmp = LE_IN16(&fsp->is_fs_reading_mask);
    282 				(void) memcpy(&fsp->is_fs_reading_mask, &tmp,
    283 				    sizeof (tmp));
    284 				break;
    285 			}
    286 
    287 		case IPMI_SDR_TYPE_EVENT_ONLY:
    288 			{
    289 				ipmi_sdr_event_only_t *esp =
    290 				    (ipmi_sdr_event_only_t *)
    291 				    sdr->is_record;
    292 				namelen = esp->is_eo_idlen;
    293 				type = esp->is_eo_idtype;
    294 				name = esp->is_eo_idstring;
    295 				break;
    296 			}
    297 
    298 		case IPMI_SDR_TYPE_MANAGEMENT_LOCATOR:
    299 			{
    300 				ipmi_sdr_management_locator_t *msp =
    301 				    (ipmi_sdr_management_locator_t *)
    302 				    sdr->is_record;
    303 				namelen = msp->is_ml_idlen;
    304 				type = msp->is_ml_idtype;
    305 				name = msp->is_ml_idstring;
    306 				break;
    307 			}
    308 
    309 		case IPMI_SDR_TYPE_MANAGEMENT_CONFIRMATION:
    310 			{
    311 				ipmi_sdr_management_confirmation_t *mcp =
    312 				    (ipmi_sdr_management_confirmation_t *)
    313 				    sdr->is_record;
    314 				uint16_t tmp;
    315 
    316 				name = NULL;
    317 				tmp = LE_IN16(&mcp->is_mc_product);
    318 				(void) memcpy(&mcp->is_mc_product, &tmp,
    319 				    sizeof (tmp));
    320 				break;
    321 			}
    322 
    323 		default:
    324 			name = NULL;
    325 		}
    326 
    327 		if ((ent = ipmi_zalloc(ihp,
    328 		    sizeof (ipmi_sdr_cache_ent_t))) == NULL) {
    329 			free(sdr);
    330 			goto error;
    331 		}
    332 
    333 		ent->isc_sdr = sdr;
    334 
    335 		if (name != NULL) {
    336 			if ((ent->isc_name = ipmi_alloc(ihp, namelen + 1)) ==
    337 			    NULL) {
    338 				ipmi_free(ihp, ent->isc_sdr);
    339 				ipmi_free(ihp, ent);
    340 				goto error;
    341 			}
    342 
    343 			ipmi_decode_string(type, namelen, name, ent->isc_name);
    344 		}
    345 
    346 		/*
    347 		 * This should never happen.  It means that the SP has returned
    348 		 * a SDR record twice, with the same name and ID.  This has
    349 		 * been observed on service processors that don't correctly
    350 		 * return SDR_LAST during iteration, so assume we've looped in
    351 		 * the SDR and return gracefully.
    352 		 */
    353 		if (ipmi_hash_lookup(ihp->ih_sdr_cache, ent) != NULL) {
    354 			ipmi_free(ihp, ent->isc_sdr);
    355 			ipmi_free(ihp, ent->isc_name);
    356 			ipmi_free(ihp, ent);
    357 			break;
    358 		}
    359 
    360 		ipmi_hash_insert(ihp->ih_sdr_cache, ent);
    361 	}
    362 
    363 	return (0);
    364 
    365 error:
    366 	ipmi_sdr_clear(ihp);
    367 	ipmi_entity_clear(ihp);
    368 	return (-1);
    369 }
    370 
    371 /*
    372  * Hash routines.  We allow lookup by name, but since not all entries have
    373  * names, we fall back to the entry pointer, which is guaranteed to be unique.
    374  * The end result is that entities without names cannot be looked up, but will
    375  * show up during iteration.
    376  */
    377 static const void *
    378 ipmi_sdr_hash_convert(const void *p)
    379 {
    380 	return (p);
    381 }
    382 
    383 static ulong_t
    384 ipmi_sdr_hash_compute(const void *p)
    385 {
    386 	const ipmi_sdr_cache_ent_t *ep = p;
    387 
    388 	if (ep->isc_name)
    389 		return (ipmi_hash_strhash(ep->isc_name));
    390 	else
    391 		return (ipmi_hash_ptrhash(ep));
    392 }
    393 
    394 static int
    395 ipmi_sdr_hash_compare(const void *a, const void *b)
    396 {
    397 	const ipmi_sdr_cache_ent_t *ap = a;
    398 	const ipmi_sdr_cache_ent_t *bp = b;
    399 
    400 	if (ap->isc_name == NULL || bp->isc_name == NULL)
    401 		return (-1);
    402 
    403 	if (strcmp(ap->isc_name, bp->isc_name) != 0)
    404 		return (-1);
    405 
    406 	/*
    407 	 * While it is strange for a service processor to report multiple
    408 	 * entries with the same name, we allow it by treating the (name, id)
    409 	 * as the unique identifier.  When looking up by name, the SDR pointer
    410 	 * is NULL, and we return the first matching name.
    411 	 */
    412 	if (ap->isc_sdr == NULL || bp->isc_sdr == NULL)
    413 		return (0);
    414 
    415 	if (ap->isc_sdr->is_id == bp->isc_sdr->is_id)
    416 		return (0);
    417 	else
    418 		return (-1);
    419 }
    420 
    421 int
    422 ipmi_sdr_init(ipmi_handle_t *ihp)
    423 {
    424 	if ((ihp->ih_sdr_cache = ipmi_hash_create(ihp,
    425 	    offsetof(ipmi_sdr_cache_ent_t, isc_link),
    426 	    ipmi_sdr_hash_convert, ipmi_sdr_hash_compute,
    427 	    ipmi_sdr_hash_compare)) == NULL)
    428 		return (-1);
    429 
    430 	return (0);
    431 }
    432 
    433 void
    434 ipmi_sdr_clear(ipmi_handle_t *ihp)
    435 {
    436 	ipmi_sdr_cache_ent_t *ent;
    437 
    438 	while ((ent = ipmi_hash_first(ihp->ih_sdr_cache)) != NULL) {
    439 		ipmi_hash_remove(ihp->ih_sdr_cache, ent);
    440 		ipmi_free(ihp, ent->isc_sdr);
    441 		ipmi_free(ihp, ent->isc_name);
    442 		ipmi_free(ihp, ent);
    443 	}
    444 }
    445 
    446 void
    447 ipmi_sdr_fini(ipmi_handle_t *ihp)
    448 {
    449 	if (ihp->ih_sdr_cache != NULL) {
    450 		ipmi_sdr_clear(ihp);
    451 		ipmi_hash_destroy(ihp->ih_sdr_cache);
    452 	}
    453 }
    454 
    455 ipmi_sdr_t *
    456 ipmi_sdr_get(ipmi_handle_t *ihp, uint16_t id, uint16_t *next)
    457 {
    458 	uint8_t offset = IPMI_SDR_HDR_SZ, count = 0, chunksz = 16, sdr_sz;
    459 	ipmi_cmd_t cmd, *rsp;
    460 	ipmi_cmd_get_sdr_t req;
    461 	ipmi_sdr_t *sdr;
    462 	int i = 0;
    463 	char *buf;
    464 
    465 	req.ic_gs_resid = ihp->ih_reservation;
    466 	req.ic_gs_recid = id;
    467 
    468 	cmd.ic_netfn = IPMI_NETFN_STORAGE;
    469 	cmd.ic_lun = 0;
    470 	cmd.ic_cmd = IPMI_CMD_GET_SDR;
    471 	cmd.ic_dlen = sizeof (req);
    472 	cmd.ic_data = &req;
    473 
    474 	/*
    475 	 * The size of the SDR is contained in the 5th byte of the SDR header,
    476 	 * so we'll read the first 5 bytes to get the size, so we know how big
    477 	 * to make the buffer.
    478 	 */
    479 	req.ic_gs_offset = 0;
    480 	req.ic_gs_len = IPMI_SDR_HDR_SZ;
    481 	for (i = 0; i < ihp->ih_retries; i++) {
    482 		if ((rsp = ipmi_send(ihp, &cmd)) != NULL)
    483 			break;
    484 
    485 		if (ipmi_errno(ihp) != EIPMI_INVALID_RESERVATION)
    486 			return (NULL);
    487 
    488 		if (ipmi_sdr_reserve_repository(ihp) != 0)
    489 			return (NULL);
    490 		req.ic_gs_resid = ihp->ih_reservation;
    491 	}
    492 	if (rsp == NULL)
    493 		return (NULL);
    494 
    495 	sdr = (ipmi_sdr_t *)((ipmi_rsp_get_sdr_t *)rsp->ic_data)->ir_gs_record;
    496 	sdr_sz = sdr->is_length;
    497 
    498 	if ((buf = ipmi_zalloc(ihp, sdr_sz + IPMI_SDR_HDR_SZ)) == NULL) {
    499 		(void) ipmi_set_error(ihp, EIPMI_NOMEM, NULL);
    500 		return (NULL);
    501 	}
    502 	(void) memcpy(buf, (void *)sdr, IPMI_SDR_HDR_SZ);
    503 
    504 	/*
    505 	 * Some SDRs can be bigger than the buffer sizes for a given bmc
    506 	 * interface.  Therefore we break up the process of reading in an entire
    507 	 * SDR into multiple smaller reads.
    508 	 */
    509 	while (count < sdr_sz) {
    510 		req.ic_gs_offset = offset;
    511 		if (chunksz > (sdr_sz - count))
    512 			chunksz = sdr_sz - count;
    513 		req.ic_gs_len = chunksz;
    514 		rsp = ipmi_send(ihp, &cmd);
    515 
    516 		if (rsp != NULL) {
    517 			count += chunksz;
    518 			sdr = (ipmi_sdr_t *)
    519 			    ((ipmi_rsp_get_sdr_t *)rsp->ic_data)->ir_gs_record;
    520 			(void) memcpy(buf+offset, (void *)sdr, chunksz);
    521 			offset += chunksz;
    522 			i = 0;
    523 		} else if (ipmi_errno(ihp) == EIPMI_INVALID_RESERVATION) {
    524 			if (i >= ihp->ih_retries ||
    525 			    ipmi_sdr_reserve_repository(ihp) != 0) {
    526 				free(buf);
    527 				return (NULL);
    528 			}
    529 			req.ic_gs_resid = ihp->ih_reservation;
    530 			i++;
    531 		} else {
    532 			free(buf);
    533 			return (NULL);
    534 		}
    535 	}
    536 	*next = ((ipmi_rsp_get_sdr_t *)rsp->ic_data)->ir_gs_next;
    537 
    538 	return ((ipmi_sdr_t *)buf);
    539 }
    540 
    541 int
    542 ipmi_sdr_iter(ipmi_handle_t *ihp, int (*func)(ipmi_handle_t *,
    543     const char *, ipmi_sdr_t *, void *), void *data)
    544 {
    545 	ipmi_sdr_cache_ent_t *ent;
    546 	int ret;
    547 
    548 	if (ipmi_hash_first(ihp->ih_sdr_cache) == NULL &&
    549 	    ipmi_sdr_refresh(ihp) != 0)
    550 		return (-1);
    551 
    552 	for (ent = ipmi_hash_first(ihp->ih_sdr_cache); ent != NULL;
    553 	    ent = ipmi_hash_next(ihp->ih_sdr_cache, ent)) {
    554 		if ((ret = func(ihp, ent->isc_name, ent->isc_sdr, data)) != 0)
    555 			return (ret);
    556 	}
    557 
    558 	return (0);
    559 }
    560 
    561 ipmi_sdr_t *
    562 ipmi_sdr_lookup(ipmi_handle_t *ihp, const char *idstr)
    563 {
    564 	ipmi_sdr_cache_ent_t *ent, search;
    565 
    566 	if (ipmi_hash_first(ihp->ih_sdr_cache) == NULL &&
    567 	    ipmi_sdr_refresh(ihp) != 0)
    568 		return (NULL);
    569 
    570 	search.isc_name = (char *)idstr;
    571 	search.isc_sdr = NULL;
    572 	if ((ent = ipmi_hash_lookup(ihp->ih_sdr_cache, &search)) == NULL) {
    573 		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
    574 		return (NULL);
    575 	}
    576 
    577 	return (ent->isc_sdr);
    578 }
    579 
    580 static void *
    581 ipmi_sdr_lookup_common(ipmi_handle_t *ihp, const char *idstr,
    582     uint8_t type)
    583 {
    584 	ipmi_sdr_t *sdrp;
    585 
    586 	if ((sdrp = ipmi_sdr_lookup(ihp, idstr)) == NULL)
    587 		return (NULL);
    588 
    589 	if (sdrp->is_type != type) {
    590 		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
    591 		return (NULL);
    592 	}
    593 
    594 	return (sdrp->is_record);
    595 }
    596 
    597 ipmi_sdr_fru_locator_t *
    598 ipmi_sdr_lookup_fru(ipmi_handle_t *ihp, const char *idstr)
    599 {
    600 	return (ipmi_sdr_lookup_common(ihp, idstr,
    601 	    IPMI_SDR_TYPE_FRU_LOCATOR));
    602 }
    603 
    604 ipmi_sdr_generic_locator_t *
    605 ipmi_sdr_lookup_generic(ipmi_handle_t *ihp, const char *idstr)
    606 {
    607 	return (ipmi_sdr_lookup_common(ihp, idstr,
    608 	    IPMI_SDR_TYPE_GENERIC_LOCATOR));
    609 }
    610 
    611 ipmi_sdr_compact_sensor_t *
    612 ipmi_sdr_lookup_compact_sensor(ipmi_handle_t *ihp, const char *idstr)
    613 {
    614 	return (ipmi_sdr_lookup_common(ihp, idstr,
    615 	    IPMI_SDR_TYPE_COMPACT_SENSOR));
    616 }
    617 
    618 ipmi_sdr_full_sensor_t *
    619 ipmi_sdr_lookup_full_sensor(ipmi_handle_t *ihp, const char *idstr)
    620 {
    621 	return (ipmi_sdr_lookup_common(ihp, idstr,
    622 	    IPMI_SDR_TYPE_FULL_SENSOR));
    623 }
    624 
    625 /*
    626  * Mostly taken from ipmitool source v1.88
    627  *
    628  * This function converts the raw sensor reading returned by
    629  * ipmi_get_sensor_reading to a unit-based value of type double.
    630  */
    631 int
    632 ipmi_sdr_conv_reading(ipmi_sdr_full_sensor_t *sensor, uint8_t val,
    633     double *result)
    634 {
    635 	int m, b, k1, k2;
    636 
    637 	m = __TO_M(sensor->is_fs_mtol);
    638 	b = __TO_B(sensor->is_fs_bacc);
    639 	k1 = __TO_B_EXP(sensor->is_fs_bacc);
    640 	k2 = __TO_R_EXP(sensor->is_fs_bacc);
    641 
    642 	switch (sensor->is_fs_analog_fmt) {
    643 	case IPMI_DATA_FMT_UNSIGNED:
    644 		*result = (double)(((m * val) +
    645 		    (b * pow(10, k1))) * pow(10, k2));
    646 		break;
    647 	case IPMI_DATA_FMT_ONESCOMP:
    648 		if (val & 0x80)
    649 			val++;
    650 		/* FALLTHRU */
    651 	case IPMI_DATA_FMT_TWOSCOMP:
    652 		*result = (double)(((m * (int8_t)val) +
    653 		    (b * pow(10, k1))) * pow(10, k2));
    654 		break;
    655 	default:
    656 		/* This sensor does not return a numeric reading */
    657 		return (-1);
    658 	}
    659 
    660 	switch (sensor->is_fs_sensor_linear_type) {
    661 	case SDR_SENSOR_L_LN:
    662 		*result = log(*result);
    663 		break;
    664 	case SDR_SENSOR_L_LOG10:
    665 		*result = log10(*result);
    666 		break;
    667 	case SDR_SENSOR_L_LOG2:
    668 		*result = (double)(log(*result) / log(2.0));
    669 		break;
    670 	case SDR_SENSOR_L_E:
    671 		*result = exp(*result);
    672 		break;
    673 	case SDR_SENSOR_L_EXP10:
    674 		*result = pow(10.0, *result);
    675 		break;
    676 	case SDR_SENSOR_L_EXP2:
    677 		*result = pow(2.0, *result);
    678 		break;
    679 	case SDR_SENSOR_L_1_X:
    680 		*result = pow(*result, -1.0);	/* 1/x w/o exception */
    681 		break;
    682 	case SDR_SENSOR_L_SQR:
    683 		*result = pow(*result, 2.0);
    684 		break;
    685 	case SDR_SENSOR_L_CUBE:
    686 		*result = pow(*result, 3.0);
    687 		break;
    688 	case SDR_SENSOR_L_SQRT:
    689 		*result = sqrt(*result);
    690 		break;
    691 	case SDR_SENSOR_L_CUBERT:
    692 		*result = cbrt(*result);
    693 		break;
    694 	case SDR_SENSOR_L_LINEAR:
    695 	default:
    696 		break;
    697 	}
    698 	return (0);
    699 }
    700