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