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 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 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 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