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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 * Copyright (c) 2017, Joyent, Inc.
31 #include "ipmi_impl.h"
34 * Extracts bits between index h (high, inclusive) and l (low, exclusive) from
35 * u, which must be an unsigned integer.
37 #define BITX(u, h, l) (((u) >> (l)) & ((1LU << ((h) - (l) + 1LU)) - 1LU))
40 * The default and minimum size in bytes that will be used when reading
41 * the FRU inventory area.
43 #define DEF_CHUNK_SZ 128
44 #define MIN_CHUNK_SZ 16
46 typedef struct ipmi_fru_read
49 uint8_t ifr_offset_lsb
;
50 uint8_t ifr_offset_msb
;
55 * returns: size of FRU inventory data in bytes, on success
59 ipmi_fru_read(ipmi_handle_t
*ihp
, ipmi_sdr_fru_locator_t
*fru_loc
, char **buf
)
61 ipmi_cmd_t cmd
, *resp
;
63 uint8_t count
, devid
, chunksz
;
64 uint16_t sz
, offset
= 0;
65 ipmi_fru_read_t cmd_data_in
;
68 devid
= fru_loc
->_devid_or_slaveaddr
._logical
._is_fl_devid
;
70 * First we issue a command to retrieve the size of the specified FRU's
73 cmd
.ic_netfn
= IPMI_NETFN_STORAGE
;
74 cmd
.ic_cmd
= IPMI_CMD_GET_FRU_INV_AREA
;
76 cmd
.ic_dlen
= sizeof (uint8_t);
79 if ((resp
= ipmi_send(ihp
, &cmd
)) == NULL
)
82 if (resp
->ic_dlen
!= 3) {
83 (void) ipmi_set_error(ihp
, EIPMI_BAD_RESPONSE_LENGTH
, NULL
);
87 (void) memcpy(&sz
, resp
->ic_data
, sizeof (uint16_t));
88 if ((tmp
= malloc(sz
)) == NULL
) {
89 (void) ipmi_set_error(ihp
, EIPMI_NOMEM
, NULL
);
93 chunksz
= DEF_CHUNK_SZ
;
95 cmd_data_in
.ifr_devid
= devid
;
96 cmd_data_in
.ifr_offset_lsb
= BITX(offset
, 7, 0);
97 cmd_data_in
.ifr_offset_msb
= BITX(offset
, 15, 8);
98 if ((sz
- offset
) < chunksz
)
99 cmd_data_in
.ifr_count
= sz
- offset
;
101 cmd_data_in
.ifr_count
= chunksz
;
103 cmd
.ic_netfn
= IPMI_NETFN_STORAGE
;
104 cmd
.ic_cmd
= IPMI_CMD_READ_FRU_DATA
;
105 cmd
.ic_data
= &cmd_data_in
;
106 cmd
.ic_dlen
= sizeof (ipmi_fru_read_t
);
110 * The FRU area must be read in chunks as its total size will
111 * be larger than what would fit in a single message. The
112 * maximum size of a message can vary between platforms so
113 * if while attempting to read a chunk we receive an error code
114 * indicating that the requested chunk size is invalid, we will
115 * perform a reverse exponential backoff of the chunk size until
116 * either the read succeeds or we hit bottom, at which point
117 * we'll fail the operation.
119 if ((resp
= ipmi_send(ihp
, &cmd
)) == NULL
) {
120 ierrno
= ipmi_errno(ihp
);
121 if (chunksz
> MIN_CHUNK_SZ
&&
122 (ierrno
== EIPMI_DATA_LENGTH_EXCEEDED
||
123 ierrno
== EIPMI_INVALID_REQUEST
)) {
124 chunksz
= chunksz
>> 1;
131 (void) memcpy(&count
, resp
->ic_data
, sizeof (uint8_t));
132 if (count
!= cmd_data_in
.ifr_count
) {
133 (void) ipmi_set_error(ihp
, EIPMI_BAD_RESPONSE_LENGTH
,
138 (void) memcpy(tmp
+offset
, (char *)(resp
->ic_data
)+1, count
);
146 ipmi_fru_parse_product(ipmi_handle_t
*ihp
, char *fru_area
,
147 ipmi_fru_prod_info_t
*buf
)
149 ipmi_fru_hdr_t fru_hdr
;
151 uint8_t len
, typelen
;
153 (void) memcpy(&fru_hdr
, fru_area
, sizeof (ipmi_fru_hdr_t
));
156 * We get the offset to the product info area from the FRU common
157 * header which is at the start of the FRU inventory area.
159 * The product info area is optional, so if the offset is NULL,
160 * indicating that it doesn't exist, then we return an error.
162 if (!fru_hdr
.ifh_product_info_off
) {
163 (void) ipmi_set_error(ihp
, EIPMI_NOT_PRESENT
, NULL
);
167 tmp
= fru_area
+ (fru_hdr
.ifh_product_info_off
* 8) + 3;
169 (void) memcpy(&typelen
, tmp
, sizeof (uint8_t));
170 len
= BITX(typelen
, 5, 0);
171 ipmi_decode_string((typelen
>> 6), len
, tmp
+1, buf
->ifpi_manuf_name
);
174 (void) memcpy(&typelen
, tmp
, sizeof (uint8_t));
175 len
= BITX(typelen
, 5, 0);
176 ipmi_decode_string((typelen
>> 6), len
, tmp
+1,
177 buf
->ifpi_product_name
);
180 (void) memcpy(&typelen
, tmp
, sizeof (uint8_t));
181 len
= BITX(typelen
, 5, 0);
182 ipmi_decode_string((typelen
>> 6), len
, tmp
+1, buf
->ifpi_part_number
);
185 (void) memcpy(&typelen
, tmp
, sizeof (uint8_t));
186 len
= BITX(typelen
, 5, 0);
187 ipmi_decode_string((typelen
>> 6), len
, tmp
+1,
188 buf
->ifpi_product_version
);
191 (void) memcpy(&typelen
, tmp
, sizeof (uint8_t));
192 len
= BITX(typelen
, 5, 0);
193 ipmi_decode_string((typelen
>> 6), len
, tmp
+1,
194 buf
->ifpi_product_serial
);
197 (void) memcpy(&typelen
, tmp
, sizeof (uint8_t));
198 len
= BITX(typelen
, 5, 0);
199 ipmi_decode_string((typelen
>> 6), len
, tmp
+1, buf
->ifpi_asset_tag
);
206 * The Board Info area is described in Sect 11 of the IPMI Platform Management
207 * FRU Information Storage Definition (v1.1).
210 ipmi_fru_parse_board(ipmi_handle_t
*ihp
, char *fru_area
,
211 ipmi_fru_brd_info_t
*buf
)
213 ipmi_fru_hdr_t fru_hdr
;
215 uint8_t len
, typelen
;
217 (void) memcpy(&fru_hdr
, fru_area
, sizeof (ipmi_fru_hdr_t
));
220 * We get the offset to the board info area from the FRU common
221 * header which is at the start of the FRU inventory area.
223 * The board info area is optional, so if the offset is NULL,
224 * indicating that it doesn't exist, then we return an error.
226 if (!fru_hdr
.ifh_board_info_off
) {
227 (void) ipmi_set_error(ihp
, EIPMI_NOT_PRESENT
, NULL
);
230 tmp
= fru_area
+ (fru_hdr
.ifh_board_info_off
* 8) + 3;
232 (void) memcpy(buf
->ifbi_manuf_date
, tmp
, 3);
235 (void) memcpy(&typelen
, tmp
, sizeof (uint8_t));
236 len
= BITX(typelen
, 5, 0);
237 ipmi_decode_string((typelen
>> 6), len
, tmp
+1, buf
->ifbi_manuf_name
);
240 (void) memcpy(&typelen
, tmp
, sizeof (uint8_t));
241 len
= BITX(typelen
, 5, 0);
242 ipmi_decode_string((typelen
>> 6), len
, tmp
+1, buf
->ifbi_board_name
);
245 (void) memcpy(&typelen
, tmp
, sizeof (uint8_t));
246 len
= BITX(typelen
, 5, 0);
247 ipmi_decode_string((typelen
>> 6), len
, tmp
+1,
248 buf
->ifbi_product_serial
);
251 (void) memcpy(&typelen
, tmp
, sizeof (uint8_t));
252 len
= BITX(typelen
, 5, 0);
253 ipmi_decode_string((typelen
>> 6), len
, tmp
+1, buf
->ifbi_part_number
);