15068Srobj /*
25068Srobj * CDDL HEADER START
35068Srobj *
45068Srobj * The contents of this file are subject to the terms of the
55068Srobj * Common Development and Distribution License (the "License").
65068Srobj * You may not use this file except in compliance with the License.
75068Srobj *
85068Srobj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95068Srobj * or http://www.opensolaris.org/os/licensing.
105068Srobj * See the License for the specific language governing permissions
115068Srobj * and limitations under the License.
125068Srobj *
135068Srobj * When distributing Covered Code, include this CDDL HEADER in each
145068Srobj * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155068Srobj * If applicable, add the following below this CDDL HEADER, with the
165068Srobj * fields enclosed by brackets "[]" replaced with your own identifying
175068Srobj * information: Portions Copyright [yyyy] [name of copyright owner]
185068Srobj *
195068Srobj * CDDL HEADER END
205068Srobj */
215068Srobj /*
226070Srobj * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
235068Srobj * Use is subject to license terms.
245068Srobj */
255068Srobj
265068Srobj #pragma ident "%Z%%M% %I% %E% SMI"
275068Srobj
285068Srobj #include <libipmi.h>
295068Srobj #include <string.h>
305068Srobj
315068Srobj #include "ipmi_impl.h"
325068Srobj
335068Srobj /*
345068Srobj * Extracts bits between index h (high, inclusive) and l (low, exclusive) from
355068Srobj * u, which must be an unsigned integer.
365068Srobj */
375068Srobj #define BITX(u, h, l) (((u) >> (l)) & ((1LU << ((h) - (l) + 1LU)) - 1LU))
385068Srobj
395068Srobj typedef struct ipmi_fru_read
405068Srobj {
415068Srobj uint8_t ifr_devid;
425068Srobj uint8_t ifr_offset_lsb;
435068Srobj uint8_t ifr_offset_msb;
445068Srobj uint8_t ifr_count;
455068Srobj } ipmi_fru_read_t;
465068Srobj
475068Srobj /*
485068Srobj * returns: size of FRU inventory data in bytes, on success
495068Srobj * -1, otherwise
505068Srobj */
515068Srobj int
ipmi_fru_read(ipmi_handle_t * ihp,ipmi_sdr_fru_locator_t * fru_loc,char ** buf)525068Srobj ipmi_fru_read(ipmi_handle_t *ihp, ipmi_sdr_fru_locator_t *fru_loc, char **buf)
535068Srobj {
545068Srobj ipmi_cmd_t cmd, *resp;
555068Srobj uint8_t count, devid;
565068Srobj uint16_t sz, offset = 0;
575068Srobj ipmi_fru_read_t cmd_data_in;
58*6292Srobj char *tmp;
595068Srobj
605068Srobj devid = fru_loc->_devid_or_slaveaddr._logical._is_fl_devid;
615068Srobj /*
625068Srobj * First we issue a command to retrieve the size of the specified FRU's
635068Srobj * inventory area
645068Srobj */
655068Srobj cmd.ic_netfn = IPMI_NETFN_STORAGE;
665068Srobj cmd.ic_cmd = IPMI_CMD_GET_FRU_INV_AREA;
675068Srobj cmd.ic_data = &devid;
685068Srobj cmd.ic_dlen = sizeof (uint8_t);
695068Srobj cmd.ic_lun = 0;
705068Srobj
715068Srobj if ((resp = ipmi_send(ihp, &cmd)) == NULL)
725068Srobj return (-1);
735068Srobj
745068Srobj if (resp->ic_dlen != 3) {
755068Srobj (void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL);
765068Srobj return (-1);
775068Srobj }
785068Srobj
795068Srobj (void) memcpy(&sz, resp->ic_data, sizeof (uint16_t));
80*6292Srobj if ((tmp = malloc(sz)) == NULL) {
815068Srobj (void) ipmi_set_error(ihp, EIPMI_NOMEM, NULL);
825068Srobj return (-1);
835068Srobj }
845068Srobj
855068Srobj while (offset < sz) {
865068Srobj cmd_data_in.ifr_devid = devid;
875068Srobj cmd_data_in.ifr_offset_lsb = BITX(offset, 7, 0);
885068Srobj cmd_data_in.ifr_offset_msb = BITX(offset, 15, 8);
895068Srobj if ((sz - offset) < 128)
905068Srobj cmd_data_in.ifr_count = sz - offset;
915068Srobj else
925068Srobj cmd_data_in.ifr_count = 128;
935068Srobj
945068Srobj cmd.ic_netfn = IPMI_NETFN_STORAGE;
955068Srobj cmd.ic_cmd = IPMI_CMD_READ_FRU_DATA;
965068Srobj cmd.ic_data = &cmd_data_in;
975068Srobj cmd.ic_dlen = sizeof (ipmi_fru_read_t);
985068Srobj cmd.ic_lun = 0;
995068Srobj
100*6292Srobj if ((resp = ipmi_send(ihp, &cmd)) == NULL) {
101*6292Srobj free(tmp);
1025068Srobj return (-1);
103*6292Srobj }
1045068Srobj
1055068Srobj (void) memcpy(&count, resp->ic_data, sizeof (uint8_t));
1065068Srobj if (count != cmd_data_in.ifr_count) {
1075068Srobj (void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH,
1085068Srobj NULL);
109*6292Srobj free(tmp);
1105068Srobj return (-1);
1115068Srobj }
112*6292Srobj (void) memcpy(tmp+offset, (char *)(resp->ic_data)+1, count);
1135068Srobj offset += count;
1145068Srobj }
115*6292Srobj *buf = tmp;
1165068Srobj return (sz);
1175068Srobj }
1185068Srobj
1195068Srobj int
ipmi_fru_parse_product(ipmi_handle_t * ihp,char * fru_area,ipmi_fru_prod_info_t * buf)1205068Srobj ipmi_fru_parse_product(ipmi_handle_t *ihp, char *fru_area,
1215068Srobj ipmi_fru_prod_info_t *buf)
1225068Srobj {
1235068Srobj ipmi_fru_hdr_t fru_hdr;
1245068Srobj char *tmp;
1255068Srobj uint8_t len, typelen;
1265068Srobj
1275068Srobj (void) memcpy(&fru_hdr, fru_area, sizeof (ipmi_fru_hdr_t));
1285068Srobj
1295068Srobj /*
1305068Srobj * We get the offset to the product info area from the FRU common
1315068Srobj * header which is at the start of the FRU inventory area.
1325068Srobj *
1335068Srobj * The product info area is optional, so if the offset is NULL,
1345068Srobj * indicating that it doesn't exist, then we return an error.
1355068Srobj */
1365068Srobj if (!fru_hdr.ifh_product_info_off) {
1375068Srobj (void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
1385068Srobj return (-1);
1395068Srobj }
1405068Srobj
1415068Srobj tmp = fru_area + (fru_hdr.ifh_product_info_off * 8) + 3;
1425068Srobj
1435068Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
144*6292Srobj len = BITX(typelen, 5, 0);
1456070Srobj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_manuf_name);
1465068Srobj tmp += len + 1;
1475068Srobj
1485068Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
149*6292Srobj len = BITX(typelen, 5, 0);
1506070Srobj ipmi_decode_string((typelen >> 6), len, tmp+1,
1516070Srobj buf->ifpi_product_name);
1525068Srobj tmp += len + 1;
1535068Srobj
1545068Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
155*6292Srobj len = BITX(typelen, 5, 0);
1566070Srobj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_part_number);
1575068Srobj tmp += len + 1;
1585068Srobj
1595068Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
160*6292Srobj len = BITX(typelen, 5, 0);
1616070Srobj ipmi_decode_string((typelen >> 6), len, tmp+1,
1626070Srobj buf->ifpi_product_version);
1635068Srobj tmp += len + 1;
1645068Srobj
1655068Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
166*6292Srobj len = BITX(typelen, 5, 0);
1676070Srobj ipmi_decode_string((typelen >> 6), len, tmp+1,
1686070Srobj buf->ifpi_product_serial);
1695068Srobj tmp += len + 1;
1705068Srobj
1715068Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
172*6292Srobj len = BITX(typelen, 5, 0);
1736070Srobj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_asset_tag);
1745068Srobj
1755068Srobj return (0);
1765068Srobj }
1775068Srobj
1785068Srobj
1795068Srobj /*
1805068Srobj * The Board Info area is described in Sect 11 of the IPMI Platform Management
1815068Srobj * FRU Information Storage Definition (v1.1).
1825068Srobj */
1835068Srobj int
ipmi_fru_parse_board(ipmi_handle_t * ihp,char * fru_area,ipmi_fru_brd_info_t * buf)1845068Srobj ipmi_fru_parse_board(ipmi_handle_t *ihp, char *fru_area,
1855068Srobj ipmi_fru_brd_info_t *buf)
1865068Srobj {
1875068Srobj ipmi_fru_hdr_t fru_hdr;
1885068Srobj char *tmp;
1895068Srobj uint8_t len, typelen;
1905068Srobj
1915068Srobj (void) memcpy(&fru_hdr, fru_area, sizeof (ipmi_fru_hdr_t));
1925068Srobj
1935068Srobj /*
1945068Srobj * We get the offset to the board info area from the FRU common
1955068Srobj * header which is at the start of the FRU inventory area.
1965068Srobj *
1975068Srobj * The board info area is optional, so if the offset is NULL,
1985068Srobj * indicating that it doesn't exist, then we return an error.
1995068Srobj */
2005068Srobj if (!fru_hdr.ifh_board_info_off) {
2015068Srobj (void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
2025068Srobj return (-1);
2035068Srobj }
2045068Srobj tmp = fru_area + (fru_hdr.ifh_board_info_off * 8) + 3;
2055068Srobj
2065068Srobj (void) memcpy(buf->ifbi_manuf_date, tmp, 3);
2075068Srobj tmp += 3;
2085068Srobj
2095068Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
210*6292Srobj len = BITX(typelen, 5, 0);
2116070Srobj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_manuf_name);
2125068Srobj tmp += len + 1;
2135068Srobj
2145068Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
215*6292Srobj len = BITX(typelen, 5, 0);
2166070Srobj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_board_name);
2175068Srobj tmp += len + 1;
2185068Srobj
2195068Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
220*6292Srobj len = BITX(typelen, 5, 0);
2216070Srobj ipmi_decode_string((typelen >> 6), len, tmp+1,
2226070Srobj buf->ifbi_product_serial);
2235068Srobj tmp += len + 1;
2245068Srobj
2255068Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
226*6292Srobj len = BITX(typelen, 5, 0);
2276070Srobj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_part_number);
2285068Srobj
2295068Srobj return (0);
2305068Srobj }
231