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.
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.
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]
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
33 #include "ipmi_impl.h"
36 * This macros are used by ipmi_sdr_conv_reading. They were taken verbatim from
37 * the source for ipmitool (v1.88)
39 #define tos32(val, bits) ((val & ((1<<((bits)-1)))) ? (-((val) & \
40 (1<<((bits)-1))) | (val)) : (val))
42 #define __TO_TOL(mtol) (uint16_t)(BSWAP_16(mtol) & 0x3f)
44 #define __TO_M(mtol) (int16_t)(tos32((((BSWAP_16(mtol) & 0xff00) >> 8) | \
45 ((BSWAP_16(mtol) & 0xc0) << 2)), 10))
47 #define __TO_B(bacc) (int32_t)(tos32((((BSWAP_32(bacc) & \
48 0xff000000) >> 24) | \
49 ((BSWAP_32(bacc) & 0xc00000) >> 14)), 10))
51 #define __TO_ACC(bacc) (uint32_t)(((BSWAP_32(bacc) & 0x3f0000) >> 16) | \
52 ((BSWAP_32(bacc) & 0xf000) >> 6))
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),\
57 #define __TO_B_EXP(bacc) (int32_t)(tos32((BSWAP_32(bacc) & 0xf), 4))
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
74 * Analog sensor reading data formats
78 #define IPMI_DATA_FMT_UNSIGNED 0
79 #define IPMI_DATA_FMT_ONESCOMP 1
80 #define IPMI_DATA_FMT_TWOSCOMP 2
82 #define IPMI_SDR_HDR_SZ offsetof(ipmi_sdr_t, is_record)
84 typedef struct ipmi_sdr_cache_ent
{
86 struct ipmi_sdr
*isc_sdr
;
87 ipmi_hash_link_t isc_link
;
88 } ipmi_sdr_cache_ent_t
;
90 typedef struct ipmi_cmd_get_sdr
{
97 typedef struct ipmi_rsp_get_sdr
{
99 uint8_t ir_gs_record
[1];
100 } ipmi_rsp_get_sdr_t
;
103 * "Get SDR Repostiory Info" command.
106 ipmi_sdr_get_info(ipmi_handle_t
*ihp
)
108 ipmi_cmd_t cmd
, *rsp
;
109 ipmi_sdr_info_t
*sip
;
113 cmd
.ic_netfn
= IPMI_NETFN_STORAGE
;
115 cmd
.ic_cmd
= IPMI_CMD_GET_SDR_INFO
;
119 if ((rsp
= ipmi_send(ihp
, &cmd
)) == NULL
)
124 tmp16
= LE_IN16(&sip
->isi_record_count
);
125 (void) memcpy(&sip
->isi_record_count
, &tmp16
, sizeof (tmp16
));
127 tmp16
= LE_IN16(&sip
->isi_free_space
);
128 (void) memcpy(&sip
->isi_free_space
, &tmp16
, sizeof (tmp16
));
130 tmp32
= LE_IN32(&sip
->isi_add_ts
);
131 (void) memcpy(&sip
->isi_add_ts
, &tmp32
, sizeof (tmp32
));
133 tmp32
= LE_IN32(&sip
->isi_erase_ts
);
134 (void) memcpy(&sip
->isi_erase_ts
, &tmp32
, sizeof (tmp32
));
140 * Issue the "Reserve SDR Repository" command.
143 ipmi_sdr_reserve_repository(ipmi_handle_t
*ihp
)
145 ipmi_cmd_t cmd
, *rsp
;
147 cmd
.ic_netfn
= IPMI_NETFN_STORAGE
;
149 cmd
.ic_cmd
= IPMI_CMD_RESERVE_SDR_REPOSITORY
;
153 if ((rsp
= ipmi_send(ihp
, &cmd
)) == NULL
)
156 ihp
->ih_reservation
= *((uint16_t *)rsp
->ic_data
);
161 * Returns B_TRUE if the repository has changed since the cached copy was last
165 ipmi_sdr_changed(ipmi_handle_t
*ihp
)
167 ipmi_sdr_info_t
*sip
;
169 if ((sip
= ipmi_sdr_get_info(ihp
)) == NULL
)
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
);
178 * Refresh the cache of sensor data records.
181 ipmi_sdr_refresh(ipmi_handle_t
*ihp
)
185 ipmi_sdr_cache_ent_t
*ent
;
189 ipmi_sdr_info_t
*sip
;
190 uint32_t isi_add_ts
, isi_erase_ts
;
192 if ((sip
= ipmi_sdr_get_info(ihp
)) == NULL
)
195 (void) memcpy(&isi_add_ts
, &sip
->isi_add_ts
, sizeof (uint32_t));
196 (void) memcpy(&isi_erase_ts
, &sip
->isi_erase_ts
, sizeof (uint32_t));
197 if (isi_add_ts
<= ihp
->ih_sdr_ts
&&
198 isi_erase_ts
<= ihp
->ih_sdr_ts
&&
199 ipmi_hash_first(ihp
->ih_sdr_cache
) != NULL
)
203 ipmi_entity_clear(ihp
);
204 ihp
->ih_sdr_ts
= MAX(isi_add_ts
, isi_erase_ts
);
207 * Iterate over all existing SDRs and add them to the cache.
210 while (id
!= IPMI_SDR_LAST
) {
211 if ((sdr
= ipmi_sdr_get(ihp
, id
, &id
)) == NULL
)
215 * Extract the name from the record-specific data.
217 switch (sdr
->is_type
) {
218 case IPMI_SDR_TYPE_GENERIC_LOCATOR
:
220 ipmi_sdr_generic_locator_t
*glp
=
221 (ipmi_sdr_generic_locator_t
*)
223 namelen
= glp
->is_gl_idlen
;
224 type
= glp
->is_gl_idtype
;
225 name
= glp
->is_gl_idstring
;
229 case IPMI_SDR_TYPE_FRU_LOCATOR
:
231 ipmi_sdr_fru_locator_t
*flp
=
232 (ipmi_sdr_fru_locator_t
*)
234 namelen
= flp
->is_fl_idlen
;
235 name
= flp
->is_fl_idstring
;
236 type
= flp
->is_fl_idtype
;
240 case IPMI_SDR_TYPE_COMPACT_SENSOR
:
242 ipmi_sdr_compact_sensor_t
*csp
=
243 (ipmi_sdr_compact_sensor_t
*)
247 namelen
= csp
->is_cs_idlen
;
248 type
= csp
->is_cs_idtype
;
249 name
= csp
->is_cs_idstring
;
251 tmp
= LE_IN16(&csp
->is_cs_assert_mask
);
252 (void) memcpy(&csp
->is_cs_assert_mask
, &tmp
,
255 tmp
= LE_IN16(&csp
->is_cs_deassert_mask
);
256 (void) memcpy(&csp
->is_cs_deassert_mask
, &tmp
,
259 tmp
= LE_IN16(&csp
->is_cs_reading_mask
);
260 (void) memcpy(&csp
->is_cs_reading_mask
, &tmp
,
265 case IPMI_SDR_TYPE_FULL_SENSOR
:
267 ipmi_sdr_full_sensor_t
*fsp
=
268 (ipmi_sdr_full_sensor_t
*)
272 namelen
= fsp
->is_fs_idlen
;
273 type
= fsp
->is_fs_idtype
;
274 name
= fsp
->is_fs_idstring
;
276 tmp
= LE_IN16(&fsp
->is_fs_assert_mask
);
277 (void) memcpy(&fsp
->is_fs_assert_mask
, &tmp
,
280 tmp
= LE_IN16(&fsp
->is_fs_deassert_mask
);
281 (void) memcpy(&fsp
->is_fs_deassert_mask
, &tmp
,
284 tmp
= LE_IN16(&fsp
->is_fs_reading_mask
);
285 (void) memcpy(&fsp
->is_fs_reading_mask
, &tmp
,
290 case IPMI_SDR_TYPE_EVENT_ONLY
:
292 ipmi_sdr_event_only_t
*esp
=
293 (ipmi_sdr_event_only_t
*)
295 namelen
= esp
->is_eo_idlen
;
296 type
= esp
->is_eo_idtype
;
297 name
= esp
->is_eo_idstring
;
301 case IPMI_SDR_TYPE_MANAGEMENT_LOCATOR
:
303 ipmi_sdr_management_locator_t
*msp
=
304 (ipmi_sdr_management_locator_t
*)
306 namelen
= msp
->is_ml_idlen
;
307 type
= msp
->is_ml_idtype
;
308 name
= msp
->is_ml_idstring
;
312 case IPMI_SDR_TYPE_MANAGEMENT_CONFIRMATION
:
314 ipmi_sdr_management_confirmation_t
*mcp
=
315 (ipmi_sdr_management_confirmation_t
*)
320 tmp
= LE_IN16(&mcp
->is_mc_product
);
321 (void) memcpy(&mcp
->is_mc_product
, &tmp
,
330 if ((ent
= ipmi_zalloc(ihp
,
331 sizeof (ipmi_sdr_cache_ent_t
))) == NULL
) {
339 if ((ent
->isc_name
= ipmi_alloc(ihp
, namelen
+ 1)) ==
341 ipmi_free(ihp
, ent
->isc_sdr
);
346 ipmi_decode_string(type
, namelen
, name
, ent
->isc_name
);
350 * This should never happen. It means that the SP has returned
351 * a SDR record twice, with the same name and ID. This has
352 * been observed on service processors that don't correctly
353 * return SDR_LAST during iteration, so assume we've looped in
354 * the SDR and return gracefully.
356 if (ipmi_hash_lookup(ihp
->ih_sdr_cache
, ent
) != NULL
) {
357 ipmi_free(ihp
, ent
->isc_sdr
);
358 ipmi_free(ihp
, ent
->isc_name
);
363 ipmi_hash_insert(ihp
->ih_sdr_cache
, ent
);
370 ipmi_entity_clear(ihp
);
375 * Hash routines. We allow lookup by name, but since not all entries have
376 * names, we fall back to the entry pointer, which is guaranteed to be unique.
377 * The end result is that entities without names cannot be looked up, but will
378 * show up during iteration.
381 ipmi_sdr_hash_convert(const void *p
)
387 ipmi_sdr_hash_compute(const void *p
)
389 const ipmi_sdr_cache_ent_t
*ep
= p
;
392 return (ipmi_hash_strhash(ep
->isc_name
));
394 return (ipmi_hash_ptrhash(ep
));
398 ipmi_sdr_hash_compare(const void *a
, const void *b
)
400 const ipmi_sdr_cache_ent_t
*ap
= a
;
401 const ipmi_sdr_cache_ent_t
*bp
= b
;
403 if (ap
->isc_name
== NULL
|| bp
->isc_name
== NULL
)
406 if (strcmp(ap
->isc_name
, bp
->isc_name
) != 0)
410 * While it is strange for a service processor to report multiple
411 * entries with the same name, we allow it by treating the (name, id)
412 * as the unique identifier. When looking up by name, the SDR pointer
413 * is NULL, and we return the first matching name.
415 if (ap
->isc_sdr
== NULL
|| bp
->isc_sdr
== NULL
)
418 if (ap
->isc_sdr
->is_id
== bp
->isc_sdr
->is_id
)
425 ipmi_sdr_init(ipmi_handle_t
*ihp
)
427 if ((ihp
->ih_sdr_cache
= ipmi_hash_create(ihp
,
428 offsetof(ipmi_sdr_cache_ent_t
, isc_link
),
429 ipmi_sdr_hash_convert
, ipmi_sdr_hash_compute
,
430 ipmi_sdr_hash_compare
)) == NULL
)
437 ipmi_sdr_clear(ipmi_handle_t
*ihp
)
439 ipmi_sdr_cache_ent_t
*ent
;
441 while ((ent
= ipmi_hash_first(ihp
->ih_sdr_cache
)) != NULL
) {
442 ipmi_hash_remove(ihp
->ih_sdr_cache
, ent
);
443 ipmi_free(ihp
, ent
->isc_sdr
);
444 ipmi_free(ihp
, ent
->isc_name
);
450 ipmi_sdr_fini(ipmi_handle_t
*ihp
)
452 if (ihp
->ih_sdr_cache
!= NULL
) {
454 ipmi_hash_destroy(ihp
->ih_sdr_cache
);
459 ipmi_sdr_get(ipmi_handle_t
*ihp
, uint16_t id
, uint16_t *next
)
461 uint8_t offset
= IPMI_SDR_HDR_SZ
, count
= 0, chunksz
= 16, sdr_sz
;
462 ipmi_cmd_t cmd
, *rsp
;
463 ipmi_cmd_get_sdr_t req
;
468 req
.ic_gs_resid
= ihp
->ih_reservation
;
469 req
.ic_gs_recid
= id
;
471 cmd
.ic_netfn
= IPMI_NETFN_STORAGE
;
473 cmd
.ic_cmd
= IPMI_CMD_GET_SDR
;
474 cmd
.ic_dlen
= sizeof (req
);
478 * The size of the SDR is contained in the 5th byte of the SDR header,
479 * so we'll read the first 5 bytes to get the size, so we know how big
480 * to make the buffer.
482 req
.ic_gs_offset
= 0;
483 req
.ic_gs_len
= IPMI_SDR_HDR_SZ
;
484 for (i
= 0; i
< ihp
->ih_retries
; i
++) {
485 if ((rsp
= ipmi_send(ihp
, &cmd
)) != NULL
)
488 if (ipmi_errno(ihp
) != EIPMI_INVALID_RESERVATION
)
491 if (ipmi_sdr_reserve_repository(ihp
) != 0)
493 req
.ic_gs_resid
= ihp
->ih_reservation
;
498 sdr
= (ipmi_sdr_t
*)((ipmi_rsp_get_sdr_t
*)rsp
->ic_data
)->ir_gs_record
;
499 sdr_sz
= sdr
->is_length
;
501 if ((buf
= ipmi_zalloc(ihp
, sdr_sz
+ IPMI_SDR_HDR_SZ
)) == NULL
) {
502 (void) ipmi_set_error(ihp
, EIPMI_NOMEM
, NULL
);
505 (void) memcpy(buf
, (void *)sdr
, IPMI_SDR_HDR_SZ
);
508 * Some SDRs can be bigger than the buffer sizes for a given bmc
509 * interface. Therefore we break up the process of reading in an entire
510 * SDR into multiple smaller reads.
512 while (count
< sdr_sz
) {
513 req
.ic_gs_offset
= offset
;
514 if (chunksz
> (sdr_sz
- count
))
515 chunksz
= sdr_sz
- count
;
516 req
.ic_gs_len
= chunksz
;
517 rsp
= ipmi_send(ihp
, &cmd
);
522 ((ipmi_rsp_get_sdr_t
*)rsp
->ic_data
)->ir_gs_record
;
523 (void) memcpy(buf
+offset
, (void *)sdr
, chunksz
);
526 } else if (ipmi_errno(ihp
) == EIPMI_INVALID_RESERVATION
) {
527 if (i
>= ihp
->ih_retries
||
528 ipmi_sdr_reserve_repository(ihp
) != 0) {
532 req
.ic_gs_resid
= ihp
->ih_reservation
;
539 *next
= ((ipmi_rsp_get_sdr_t
*)rsp
->ic_data
)->ir_gs_next
;
541 return ((ipmi_sdr_t
*)buf
);
545 ipmi_sdr_iter(ipmi_handle_t
*ihp
, int (*func
)(ipmi_handle_t
*,
546 const char *, ipmi_sdr_t
*, void *), void *data
)
548 ipmi_sdr_cache_ent_t
*ent
;
551 if (ipmi_hash_first(ihp
->ih_sdr_cache
) == NULL
&&
552 ipmi_sdr_refresh(ihp
) != 0)
555 for (ent
= ipmi_hash_first(ihp
->ih_sdr_cache
); ent
!= NULL
;
556 ent
= ipmi_hash_next(ihp
->ih_sdr_cache
, ent
)) {
557 if ((ret
= func(ihp
, ent
->isc_name
, ent
->isc_sdr
, data
)) != 0)
565 ipmi_sdr_lookup(ipmi_handle_t
*ihp
, const char *idstr
)
567 ipmi_sdr_cache_ent_t
*ent
, search
;
569 if (ipmi_hash_first(ihp
->ih_sdr_cache
) == NULL
&&
570 ipmi_sdr_refresh(ihp
) != 0)
573 search
.isc_name
= (char *)idstr
;
574 search
.isc_sdr
= NULL
;
575 if ((ent
= ipmi_hash_lookup(ihp
->ih_sdr_cache
, &search
)) == NULL
) {
576 (void) ipmi_set_error(ihp
, EIPMI_NOT_PRESENT
, NULL
);
580 return (ent
->isc_sdr
);
584 ipmi_sdr_lookup_common(ipmi_handle_t
*ihp
, const char *idstr
,
589 if ((sdrp
= ipmi_sdr_lookup(ihp
, idstr
)) == NULL
)
592 if (sdrp
->is_type
!= type
) {
593 (void) ipmi_set_error(ihp
, EIPMI_NOT_PRESENT
, NULL
);
597 return (sdrp
->is_record
);
600 ipmi_sdr_fru_locator_t
*
601 ipmi_sdr_lookup_fru(ipmi_handle_t
*ihp
, const char *idstr
)
603 return (ipmi_sdr_lookup_common(ihp
, idstr
,
604 IPMI_SDR_TYPE_FRU_LOCATOR
));
607 ipmi_sdr_generic_locator_t
*
608 ipmi_sdr_lookup_generic(ipmi_handle_t
*ihp
, const char *idstr
)
610 return (ipmi_sdr_lookup_common(ihp
, idstr
,
611 IPMI_SDR_TYPE_GENERIC_LOCATOR
));
614 ipmi_sdr_compact_sensor_t
*
615 ipmi_sdr_lookup_compact_sensor(ipmi_handle_t
*ihp
, const char *idstr
)
617 return (ipmi_sdr_lookup_common(ihp
, idstr
,
618 IPMI_SDR_TYPE_COMPACT_SENSOR
));
621 ipmi_sdr_full_sensor_t
*
622 ipmi_sdr_lookup_full_sensor(ipmi_handle_t
*ihp
, const char *idstr
)
624 return (ipmi_sdr_lookup_common(ihp
, idstr
,
625 IPMI_SDR_TYPE_FULL_SENSOR
));
629 * Mostly taken from ipmitool source v1.88
631 * This function converts the raw sensor reading returned by
632 * ipmi_get_sensor_reading to a unit-based value of type double.
635 ipmi_sdr_conv_reading(ipmi_sdr_full_sensor_t
*sensor
, uint8_t val
,
640 m
= __TO_M(sensor
->is_fs_mtol
);
641 b
= __TO_B(sensor
->is_fs_bacc
);
642 k1
= __TO_B_EXP(sensor
->is_fs_bacc
);
643 k2
= __TO_R_EXP(sensor
->is_fs_bacc
);
645 switch (sensor
->is_fs_analog_fmt
) {
646 case IPMI_DATA_FMT_UNSIGNED
:
647 *result
= (double)(((m
* val
) +
648 (b
* pow(10, k1
))) * pow(10, k2
));
650 case IPMI_DATA_FMT_ONESCOMP
:
654 case IPMI_DATA_FMT_TWOSCOMP
:
655 *result
= (double)(((m
* (int8_t)val
) +
656 (b
* pow(10, k1
))) * pow(10, k2
));
659 /* This sensor does not return a numeric reading */
663 switch (sensor
->is_fs_sensor_linear_type
) {
664 case SDR_SENSOR_L_LN
:
665 *result
= log(*result
);
667 case SDR_SENSOR_L_LOG10
:
668 *result
= log10(*result
);
670 case SDR_SENSOR_L_LOG2
:
671 *result
= (double)(log(*result
) / log(2.0));
674 *result
= exp(*result
);
676 case SDR_SENSOR_L_EXP10
:
677 *result
= pow(10.0, *result
);
679 case SDR_SENSOR_L_EXP2
:
680 *result
= pow(2.0, *result
);
682 case SDR_SENSOR_L_1_X
:
683 *result
= pow(*result
, -1.0); /* 1/x w/o exception */
685 case SDR_SENSOR_L_SQR
:
686 *result
= pow(*result
, 2.0);
688 case SDR_SENSOR_L_CUBE
:
689 *result
= pow(*result
, 3.0);
691 case SDR_SENSOR_L_SQRT
:
692 *result
= sqrt(*result
);
694 case SDR_SENSOR_L_CUBERT
:
695 *result
= cbrt(*result
);
697 case SDR_SENSOR_L_LINEAR
: