14557a2a1Srobj /*
24557a2a1Srobj * CDDL HEADER START
34557a2a1Srobj *
44557a2a1Srobj * The contents of this file are subject to the terms of the
54557a2a1Srobj * Common Development and Distribution License (the "License").
64557a2a1Srobj * You may not use this file except in compliance with the License.
74557a2a1Srobj *
84557a2a1Srobj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94557a2a1Srobj * or http://www.opensolaris.org/os/licensing.
104557a2a1Srobj * See the License for the specific language governing permissions
114557a2a1Srobj * and limitations under the License.
124557a2a1Srobj *
134557a2a1Srobj * When distributing Covered Code, include this CDDL HEADER in each
144557a2a1Srobj * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154557a2a1Srobj * If applicable, add the following below this CDDL HEADER, with the
164557a2a1Srobj * fields enclosed by brackets "[]" replaced with your own identifying
174557a2a1Srobj * information: Portions Copyright [yyyy] [name of copyright owner]
184557a2a1Srobj *
194557a2a1Srobj * CDDL HEADER END
204557a2a1Srobj */
214557a2a1Srobj /*
222eeaed14Srobj * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
234557a2a1Srobj * Use is subject to license terms.
244557a2a1Srobj */
254557a2a1Srobj
264557a2a1Srobj #pragma ident "%Z%%M% %I% %E% SMI"
274557a2a1Srobj
284557a2a1Srobj #include <libipmi.h>
294557a2a1Srobj #include <string.h>
304557a2a1Srobj
314557a2a1Srobj #include "ipmi_impl.h"
324557a2a1Srobj
334557a2a1Srobj /*
344557a2a1Srobj * Extracts bits between index h (high, inclusive) and l (low, exclusive) from
354557a2a1Srobj * u, which must be an unsigned integer.
364557a2a1Srobj */
374557a2a1Srobj #define BITX(u, h, l) (((u) >> (l)) & ((1LU << ((h) - (l) + 1LU)) - 1LU))
384557a2a1Srobj
394557a2a1Srobj typedef struct ipmi_fru_read
404557a2a1Srobj {
414557a2a1Srobj uint8_t ifr_devid;
424557a2a1Srobj uint8_t ifr_offset_lsb;
434557a2a1Srobj uint8_t ifr_offset_msb;
444557a2a1Srobj uint8_t ifr_count;
454557a2a1Srobj } ipmi_fru_read_t;
464557a2a1Srobj
474557a2a1Srobj /*
484557a2a1Srobj * returns: size of FRU inventory data in bytes, on success
494557a2a1Srobj * -1, otherwise
504557a2a1Srobj */
514557a2a1Srobj int
ipmi_fru_read(ipmi_handle_t * ihp,ipmi_sdr_fru_locator_t * fru_loc,char ** buf)524557a2a1Srobj ipmi_fru_read(ipmi_handle_t *ihp, ipmi_sdr_fru_locator_t *fru_loc, char **buf)
534557a2a1Srobj {
544557a2a1Srobj ipmi_cmd_t cmd, *resp;
554557a2a1Srobj uint8_t count, devid;
564557a2a1Srobj uint16_t sz, offset = 0;
574557a2a1Srobj ipmi_fru_read_t cmd_data_in;
58*2cb5535aSrobj char *tmp;
594557a2a1Srobj
604557a2a1Srobj devid = fru_loc->_devid_or_slaveaddr._logical._is_fl_devid;
614557a2a1Srobj /*
624557a2a1Srobj * First we issue a command to retrieve the size of the specified FRU's
634557a2a1Srobj * inventory area
644557a2a1Srobj */
654557a2a1Srobj cmd.ic_netfn = IPMI_NETFN_STORAGE;
664557a2a1Srobj cmd.ic_cmd = IPMI_CMD_GET_FRU_INV_AREA;
674557a2a1Srobj cmd.ic_data = &devid;
684557a2a1Srobj cmd.ic_dlen = sizeof (uint8_t);
694557a2a1Srobj cmd.ic_lun = 0;
704557a2a1Srobj
714557a2a1Srobj if ((resp = ipmi_send(ihp, &cmd)) == NULL)
724557a2a1Srobj return (-1);
734557a2a1Srobj
744557a2a1Srobj if (resp->ic_dlen != 3) {
754557a2a1Srobj (void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL);
764557a2a1Srobj return (-1);
774557a2a1Srobj }
784557a2a1Srobj
794557a2a1Srobj (void) memcpy(&sz, resp->ic_data, sizeof (uint16_t));
80*2cb5535aSrobj if ((tmp = malloc(sz)) == NULL) {
814557a2a1Srobj (void) ipmi_set_error(ihp, EIPMI_NOMEM, NULL);
824557a2a1Srobj return (-1);
834557a2a1Srobj }
844557a2a1Srobj
854557a2a1Srobj while (offset < sz) {
864557a2a1Srobj cmd_data_in.ifr_devid = devid;
874557a2a1Srobj cmd_data_in.ifr_offset_lsb = BITX(offset, 7, 0);
884557a2a1Srobj cmd_data_in.ifr_offset_msb = BITX(offset, 15, 8);
894557a2a1Srobj if ((sz - offset) < 128)
904557a2a1Srobj cmd_data_in.ifr_count = sz - offset;
914557a2a1Srobj else
924557a2a1Srobj cmd_data_in.ifr_count = 128;
934557a2a1Srobj
944557a2a1Srobj cmd.ic_netfn = IPMI_NETFN_STORAGE;
954557a2a1Srobj cmd.ic_cmd = IPMI_CMD_READ_FRU_DATA;
964557a2a1Srobj cmd.ic_data = &cmd_data_in;
974557a2a1Srobj cmd.ic_dlen = sizeof (ipmi_fru_read_t);
984557a2a1Srobj cmd.ic_lun = 0;
994557a2a1Srobj
100*2cb5535aSrobj if ((resp = ipmi_send(ihp, &cmd)) == NULL) {
101*2cb5535aSrobj free(tmp);
1024557a2a1Srobj return (-1);
103*2cb5535aSrobj }
1044557a2a1Srobj
1054557a2a1Srobj (void) memcpy(&count, resp->ic_data, sizeof (uint8_t));
1064557a2a1Srobj if (count != cmd_data_in.ifr_count) {
1074557a2a1Srobj (void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH,
1084557a2a1Srobj NULL);
109*2cb5535aSrobj free(tmp);
1104557a2a1Srobj return (-1);
1114557a2a1Srobj }
112*2cb5535aSrobj (void) memcpy(tmp+offset, (char *)(resp->ic_data)+1, count);
1134557a2a1Srobj offset += count;
1144557a2a1Srobj }
115*2cb5535aSrobj *buf = tmp;
1164557a2a1Srobj return (sz);
1174557a2a1Srobj }
1184557a2a1Srobj
1194557a2a1Srobj int
ipmi_fru_parse_product(ipmi_handle_t * ihp,char * fru_area,ipmi_fru_prod_info_t * buf)1204557a2a1Srobj ipmi_fru_parse_product(ipmi_handle_t *ihp, char *fru_area,
1214557a2a1Srobj ipmi_fru_prod_info_t *buf)
1224557a2a1Srobj {
1234557a2a1Srobj ipmi_fru_hdr_t fru_hdr;
1244557a2a1Srobj char *tmp;
1254557a2a1Srobj uint8_t len, typelen;
1264557a2a1Srobj
1274557a2a1Srobj (void) memcpy(&fru_hdr, fru_area, sizeof (ipmi_fru_hdr_t));
1284557a2a1Srobj
1294557a2a1Srobj /*
1304557a2a1Srobj * We get the offset to the product info area from the FRU common
1314557a2a1Srobj * header which is at the start of the FRU inventory area.
1324557a2a1Srobj *
1334557a2a1Srobj * The product info area is optional, so if the offset is NULL,
1344557a2a1Srobj * indicating that it doesn't exist, then we return an error.
1354557a2a1Srobj */
1364557a2a1Srobj if (!fru_hdr.ifh_product_info_off) {
1374557a2a1Srobj (void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
1384557a2a1Srobj return (-1);
1394557a2a1Srobj }
1404557a2a1Srobj
1414557a2a1Srobj tmp = fru_area + (fru_hdr.ifh_product_info_off * 8) + 3;
1424557a2a1Srobj
1434557a2a1Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
144*2cb5535aSrobj len = BITX(typelen, 5, 0);
1452eeaed14Srobj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_manuf_name);
1464557a2a1Srobj tmp += len + 1;
1474557a2a1Srobj
1484557a2a1Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
149*2cb5535aSrobj len = BITX(typelen, 5, 0);
1502eeaed14Srobj ipmi_decode_string((typelen >> 6), len, tmp+1,
1512eeaed14Srobj buf->ifpi_product_name);
1524557a2a1Srobj tmp += len + 1;
1534557a2a1Srobj
1544557a2a1Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
155*2cb5535aSrobj len = BITX(typelen, 5, 0);
1562eeaed14Srobj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_part_number);
1574557a2a1Srobj tmp += len + 1;
1584557a2a1Srobj
1594557a2a1Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
160*2cb5535aSrobj len = BITX(typelen, 5, 0);
1612eeaed14Srobj ipmi_decode_string((typelen >> 6), len, tmp+1,
1622eeaed14Srobj buf->ifpi_product_version);
1634557a2a1Srobj tmp += len + 1;
1644557a2a1Srobj
1654557a2a1Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
166*2cb5535aSrobj len = BITX(typelen, 5, 0);
1672eeaed14Srobj ipmi_decode_string((typelen >> 6), len, tmp+1,
1682eeaed14Srobj buf->ifpi_product_serial);
1694557a2a1Srobj tmp += len + 1;
1704557a2a1Srobj
1714557a2a1Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
172*2cb5535aSrobj len = BITX(typelen, 5, 0);
1732eeaed14Srobj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_asset_tag);
1744557a2a1Srobj
1754557a2a1Srobj return (0);
1764557a2a1Srobj }
1774557a2a1Srobj
1784557a2a1Srobj
1794557a2a1Srobj /*
1804557a2a1Srobj * The Board Info area is described in Sect 11 of the IPMI Platform Management
1814557a2a1Srobj * FRU Information Storage Definition (v1.1).
1824557a2a1Srobj */
1834557a2a1Srobj int
ipmi_fru_parse_board(ipmi_handle_t * ihp,char * fru_area,ipmi_fru_brd_info_t * buf)1844557a2a1Srobj ipmi_fru_parse_board(ipmi_handle_t *ihp, char *fru_area,
1854557a2a1Srobj ipmi_fru_brd_info_t *buf)
1864557a2a1Srobj {
1874557a2a1Srobj ipmi_fru_hdr_t fru_hdr;
1884557a2a1Srobj char *tmp;
1894557a2a1Srobj uint8_t len, typelen;
1904557a2a1Srobj
1914557a2a1Srobj (void) memcpy(&fru_hdr, fru_area, sizeof (ipmi_fru_hdr_t));
1924557a2a1Srobj
1934557a2a1Srobj /*
1944557a2a1Srobj * We get the offset to the board info area from the FRU common
1954557a2a1Srobj * header which is at the start of the FRU inventory area.
1964557a2a1Srobj *
1974557a2a1Srobj * The board info area is optional, so if the offset is NULL,
1984557a2a1Srobj * indicating that it doesn't exist, then we return an error.
1994557a2a1Srobj */
2004557a2a1Srobj if (!fru_hdr.ifh_board_info_off) {
2014557a2a1Srobj (void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
2024557a2a1Srobj return (-1);
2034557a2a1Srobj }
2044557a2a1Srobj tmp = fru_area + (fru_hdr.ifh_board_info_off * 8) + 3;
2054557a2a1Srobj
2064557a2a1Srobj (void) memcpy(buf->ifbi_manuf_date, tmp, 3);
2074557a2a1Srobj tmp += 3;
2084557a2a1Srobj
2094557a2a1Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
210*2cb5535aSrobj len = BITX(typelen, 5, 0);
2112eeaed14Srobj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_manuf_name);
2124557a2a1Srobj tmp += len + 1;
2134557a2a1Srobj
2144557a2a1Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
215*2cb5535aSrobj len = BITX(typelen, 5, 0);
2162eeaed14Srobj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_board_name);
2174557a2a1Srobj tmp += len + 1;
2184557a2a1Srobj
2194557a2a1Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
220*2cb5535aSrobj len = BITX(typelen, 5, 0);
2212eeaed14Srobj ipmi_decode_string((typelen >> 6), len, tmp+1,
2222eeaed14Srobj buf->ifbi_product_serial);
2234557a2a1Srobj tmp += len + 1;
2244557a2a1Srobj
2254557a2a1Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
226*2cb5535aSrobj len = BITX(typelen, 5, 0);
2272eeaed14Srobj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_part_number);
2284557a2a1Srobj
2294557a2a1Srobj return (0);
2304557a2a1Srobj }
231