xref: /onnv-gate/usr/src/lib/libipmi/common/ipmi_fru.c (revision 6292:964ff070e569)
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