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 */
25*b10f758dSRob Johnston /*
26*b10f758dSRob Johnston * Copyright (c) 2017, Joyent, Inc.
27*b10f758dSRob Johnston */
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
39*b10f758dSRob Johnston /*
40*b10f758dSRob Johnston * The default and minimum size in bytes that will be used when reading
41*b10f758dSRob Johnston * the FRU inventory area.
42*b10f758dSRob Johnston */
43*b10f758dSRob Johnston #define DEF_CHUNK_SZ 128
44*b10f758dSRob Johnston #define MIN_CHUNK_SZ 16
45*b10f758dSRob Johnston
464557a2a1Srobj typedef struct ipmi_fru_read
474557a2a1Srobj {
484557a2a1Srobj uint8_t ifr_devid;
494557a2a1Srobj uint8_t ifr_offset_lsb;
504557a2a1Srobj uint8_t ifr_offset_msb;
514557a2a1Srobj uint8_t ifr_count;
524557a2a1Srobj } ipmi_fru_read_t;
534557a2a1Srobj
544557a2a1Srobj /*
554557a2a1Srobj * returns: size of FRU inventory data in bytes, on success
564557a2a1Srobj * -1, otherwise
574557a2a1Srobj */
584557a2a1Srobj int
ipmi_fru_read(ipmi_handle_t * ihp,ipmi_sdr_fru_locator_t * fru_loc,char ** buf)594557a2a1Srobj ipmi_fru_read(ipmi_handle_t *ihp, ipmi_sdr_fru_locator_t *fru_loc, char **buf)
604557a2a1Srobj {
614557a2a1Srobj ipmi_cmd_t cmd, *resp;
62*b10f758dSRob Johnston int ierrno;
63*b10f758dSRob Johnston uint8_t count, devid, chunksz;
644557a2a1Srobj uint16_t sz, offset = 0;
654557a2a1Srobj ipmi_fru_read_t cmd_data_in;
662cb5535aSrobj char *tmp;
674557a2a1Srobj
684557a2a1Srobj devid = fru_loc->_devid_or_slaveaddr._logical._is_fl_devid;
694557a2a1Srobj /*
704557a2a1Srobj * First we issue a command to retrieve the size of the specified FRU's
714557a2a1Srobj * inventory area
724557a2a1Srobj */
734557a2a1Srobj cmd.ic_netfn = IPMI_NETFN_STORAGE;
744557a2a1Srobj cmd.ic_cmd = IPMI_CMD_GET_FRU_INV_AREA;
754557a2a1Srobj cmd.ic_data = &devid;
764557a2a1Srobj cmd.ic_dlen = sizeof (uint8_t);
774557a2a1Srobj cmd.ic_lun = 0;
784557a2a1Srobj
794557a2a1Srobj if ((resp = ipmi_send(ihp, &cmd)) == NULL)
804557a2a1Srobj return (-1);
814557a2a1Srobj
824557a2a1Srobj if (resp->ic_dlen != 3) {
834557a2a1Srobj (void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL);
844557a2a1Srobj return (-1);
854557a2a1Srobj }
864557a2a1Srobj
874557a2a1Srobj (void) memcpy(&sz, resp->ic_data, sizeof (uint16_t));
882cb5535aSrobj if ((tmp = malloc(sz)) == NULL) {
894557a2a1Srobj (void) ipmi_set_error(ihp, EIPMI_NOMEM, NULL);
904557a2a1Srobj return (-1);
914557a2a1Srobj }
924557a2a1Srobj
93*b10f758dSRob Johnston chunksz = DEF_CHUNK_SZ;
944557a2a1Srobj while (offset < sz) {
954557a2a1Srobj cmd_data_in.ifr_devid = devid;
964557a2a1Srobj cmd_data_in.ifr_offset_lsb = BITX(offset, 7, 0);
974557a2a1Srobj cmd_data_in.ifr_offset_msb = BITX(offset, 15, 8);
98*b10f758dSRob Johnston if ((sz - offset) < chunksz)
994557a2a1Srobj cmd_data_in.ifr_count = sz - offset;
1004557a2a1Srobj else
101*b10f758dSRob Johnston cmd_data_in.ifr_count = chunksz;
1024557a2a1Srobj
1034557a2a1Srobj cmd.ic_netfn = IPMI_NETFN_STORAGE;
1044557a2a1Srobj cmd.ic_cmd = IPMI_CMD_READ_FRU_DATA;
1054557a2a1Srobj cmd.ic_data = &cmd_data_in;
1064557a2a1Srobj cmd.ic_dlen = sizeof (ipmi_fru_read_t);
1074557a2a1Srobj cmd.ic_lun = 0;
1084557a2a1Srobj
109*b10f758dSRob Johnston /*
110*b10f758dSRob Johnston * The FRU area must be read in chunks as its total size will
111*b10f758dSRob Johnston * be larger than what would fit in a single message. The
112*b10f758dSRob Johnston * maximum size of a message can vary between platforms so
113*b10f758dSRob Johnston * if while attempting to read a chunk we receive an error code
114*b10f758dSRob Johnston * indicating that the requested chunk size is invalid, we will
115*b10f758dSRob Johnston * perform a reverse exponential backoff of the chunk size until
116*b10f758dSRob Johnston * either the read succeeds or we hit bottom, at which point
117*b10f758dSRob Johnston * we'll fail the operation.
118*b10f758dSRob Johnston */
1192cb5535aSrobj if ((resp = ipmi_send(ihp, &cmd)) == NULL) {
120*b10f758dSRob Johnston ierrno = ipmi_errno(ihp);
121*b10f758dSRob Johnston if (chunksz > MIN_CHUNK_SZ &&
122*b10f758dSRob Johnston (ierrno == EIPMI_DATA_LENGTH_EXCEEDED ||
123*b10f758dSRob Johnston ierrno == EIPMI_INVALID_REQUEST)) {
124*b10f758dSRob Johnston chunksz = chunksz >> 1;
125*b10f758dSRob Johnston continue;
126*b10f758dSRob Johnston }
1272cb5535aSrobj free(tmp);
1284557a2a1Srobj return (-1);
1292cb5535aSrobj }
1304557a2a1Srobj
1314557a2a1Srobj (void) memcpy(&count, resp->ic_data, sizeof (uint8_t));
1324557a2a1Srobj if (count != cmd_data_in.ifr_count) {
1334557a2a1Srobj (void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH,
1344557a2a1Srobj NULL);
1352cb5535aSrobj free(tmp);
1364557a2a1Srobj return (-1);
1374557a2a1Srobj }
1382cb5535aSrobj (void) memcpy(tmp+offset, (char *)(resp->ic_data)+1, count);
1394557a2a1Srobj offset += count;
1404557a2a1Srobj }
1412cb5535aSrobj *buf = tmp;
1424557a2a1Srobj return (sz);
1434557a2a1Srobj }
1444557a2a1Srobj
1454557a2a1Srobj int
ipmi_fru_parse_product(ipmi_handle_t * ihp,char * fru_area,ipmi_fru_prod_info_t * buf)1464557a2a1Srobj ipmi_fru_parse_product(ipmi_handle_t *ihp, char *fru_area,
1474557a2a1Srobj ipmi_fru_prod_info_t *buf)
1484557a2a1Srobj {
1494557a2a1Srobj ipmi_fru_hdr_t fru_hdr;
1504557a2a1Srobj char *tmp;
1514557a2a1Srobj uint8_t len, typelen;
1524557a2a1Srobj
1534557a2a1Srobj (void) memcpy(&fru_hdr, fru_area, sizeof (ipmi_fru_hdr_t));
1544557a2a1Srobj
1554557a2a1Srobj /*
1564557a2a1Srobj * We get the offset to the product info area from the FRU common
1574557a2a1Srobj * header which is at the start of the FRU inventory area.
1584557a2a1Srobj *
1594557a2a1Srobj * The product info area is optional, so if the offset is NULL,
1604557a2a1Srobj * indicating that it doesn't exist, then we return an error.
1614557a2a1Srobj */
1624557a2a1Srobj if (!fru_hdr.ifh_product_info_off) {
1634557a2a1Srobj (void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
1644557a2a1Srobj return (-1);
1654557a2a1Srobj }
1664557a2a1Srobj
1674557a2a1Srobj tmp = fru_area + (fru_hdr.ifh_product_info_off * 8) + 3;
1684557a2a1Srobj
1694557a2a1Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
1702cb5535aSrobj len = BITX(typelen, 5, 0);
1712eeaed14Srobj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_manuf_name);
1724557a2a1Srobj tmp += len + 1;
1734557a2a1Srobj
1744557a2a1Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
1752cb5535aSrobj len = BITX(typelen, 5, 0);
1762eeaed14Srobj ipmi_decode_string((typelen >> 6), len, tmp+1,
1772eeaed14Srobj buf->ifpi_product_name);
1784557a2a1Srobj tmp += len + 1;
1794557a2a1Srobj
1804557a2a1Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
1812cb5535aSrobj len = BITX(typelen, 5, 0);
1822eeaed14Srobj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_part_number);
1834557a2a1Srobj tmp += len + 1;
1844557a2a1Srobj
1854557a2a1Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
1862cb5535aSrobj len = BITX(typelen, 5, 0);
1872eeaed14Srobj ipmi_decode_string((typelen >> 6), len, tmp+1,
1882eeaed14Srobj buf->ifpi_product_version);
1894557a2a1Srobj tmp += len + 1;
1904557a2a1Srobj
1914557a2a1Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
1922cb5535aSrobj len = BITX(typelen, 5, 0);
1932eeaed14Srobj ipmi_decode_string((typelen >> 6), len, tmp+1,
1942eeaed14Srobj buf->ifpi_product_serial);
1954557a2a1Srobj tmp += len + 1;
1964557a2a1Srobj
1974557a2a1Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
1982cb5535aSrobj len = BITX(typelen, 5, 0);
1992eeaed14Srobj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_asset_tag);
2004557a2a1Srobj
2014557a2a1Srobj return (0);
2024557a2a1Srobj }
2034557a2a1Srobj
2044557a2a1Srobj
2054557a2a1Srobj /*
2064557a2a1Srobj * The Board Info area is described in Sect 11 of the IPMI Platform Management
2074557a2a1Srobj * FRU Information Storage Definition (v1.1).
2084557a2a1Srobj */
2094557a2a1Srobj int
ipmi_fru_parse_board(ipmi_handle_t * ihp,char * fru_area,ipmi_fru_brd_info_t * buf)2104557a2a1Srobj ipmi_fru_parse_board(ipmi_handle_t *ihp, char *fru_area,
2114557a2a1Srobj ipmi_fru_brd_info_t *buf)
2124557a2a1Srobj {
2134557a2a1Srobj ipmi_fru_hdr_t fru_hdr;
2144557a2a1Srobj char *tmp;
2154557a2a1Srobj uint8_t len, typelen;
2164557a2a1Srobj
2174557a2a1Srobj (void) memcpy(&fru_hdr, fru_area, sizeof (ipmi_fru_hdr_t));
2184557a2a1Srobj
2194557a2a1Srobj /*
2204557a2a1Srobj * We get the offset to the board info area from the FRU common
2214557a2a1Srobj * header which is at the start of the FRU inventory area.
2224557a2a1Srobj *
2234557a2a1Srobj * The board info area is optional, so if the offset is NULL,
2244557a2a1Srobj * indicating that it doesn't exist, then we return an error.
2254557a2a1Srobj */
2264557a2a1Srobj if (!fru_hdr.ifh_board_info_off) {
2274557a2a1Srobj (void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
2284557a2a1Srobj return (-1);
2294557a2a1Srobj }
2304557a2a1Srobj tmp = fru_area + (fru_hdr.ifh_board_info_off * 8) + 3;
2314557a2a1Srobj
2324557a2a1Srobj (void) memcpy(buf->ifbi_manuf_date, tmp, 3);
2334557a2a1Srobj tmp += 3;
2344557a2a1Srobj
2354557a2a1Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
2362cb5535aSrobj len = BITX(typelen, 5, 0);
2372eeaed14Srobj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_manuf_name);
2384557a2a1Srobj tmp += len + 1;
2394557a2a1Srobj
2404557a2a1Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
2412cb5535aSrobj len = BITX(typelen, 5, 0);
2422eeaed14Srobj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_board_name);
2434557a2a1Srobj tmp += len + 1;
2444557a2a1Srobj
2454557a2a1Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
2462cb5535aSrobj len = BITX(typelen, 5, 0);
2472eeaed14Srobj ipmi_decode_string((typelen >> 6), len, tmp+1,
2482eeaed14Srobj buf->ifbi_product_serial);
2494557a2a1Srobj tmp += len + 1;
2504557a2a1Srobj
2514557a2a1Srobj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
2522cb5535aSrobj len = BITX(typelen, 5, 0);
2532eeaed14Srobj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_part_number);
2544557a2a1Srobj
2554557a2a1Srobj return (0);
2564557a2a1Srobj }
257