OpenSolaris_b135/lib/libipmi/common/ipmi_fru.c

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <libipmi.h>
#include <string.h>

#include "ipmi_impl.h"

/*
 * Extracts bits between index h (high, inclusive) and l (low, exclusive) from
 * u, which must be an unsigned integer.
 */
#define	BITX(u, h, l)	(((u) >> (l)) & ((1LU << ((h) - (l) + 1LU)) - 1LU))

typedef struct ipmi_fru_read
{
	uint8_t		ifr_devid;
	uint8_t		ifr_offset_lsb;
	uint8_t		ifr_offset_msb;
	uint8_t		ifr_count;
} ipmi_fru_read_t;

/*
 * returns: size of FRU inventory data in bytes, on success
 *          -1, otherwise
 */
int
ipmi_fru_read(ipmi_handle_t *ihp, ipmi_sdr_fru_locator_t *fru_loc, char **buf)
{
	ipmi_cmd_t cmd, *resp;
	uint8_t count, devid;
	uint16_t sz, offset = 0;
	ipmi_fru_read_t cmd_data_in;
	char *tmp;

	devid = fru_loc->_devid_or_slaveaddr._logical._is_fl_devid;
	/*
	 * First we issue a command to retrieve the size of the specified FRU's
	 * inventory area
	 */
	cmd.ic_netfn = IPMI_NETFN_STORAGE;
	cmd.ic_cmd = IPMI_CMD_GET_FRU_INV_AREA;
	cmd.ic_data = &devid;
	cmd.ic_dlen = sizeof (uint8_t);
	cmd.ic_lun = 0;

	if ((resp = ipmi_send(ihp, &cmd)) == NULL)
		return (-1);

	if (resp->ic_dlen != 3) {
		(void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL);
		return (-1);
	}

	(void) memcpy(&sz, resp->ic_data, sizeof (uint16_t));
	if ((tmp = malloc(sz)) == NULL) {
		(void) ipmi_set_error(ihp, EIPMI_NOMEM, NULL);
		return (-1);
	}

	while (offset < sz) {
		cmd_data_in.ifr_devid = devid;
		cmd_data_in.ifr_offset_lsb = BITX(offset, 7, 0);
		cmd_data_in.ifr_offset_msb = BITX(offset, 15, 8);
		if ((sz - offset) < 128)
			cmd_data_in.ifr_count = sz - offset;
		else
			cmd_data_in.ifr_count = 128;

		cmd.ic_netfn = IPMI_NETFN_STORAGE;
		cmd.ic_cmd = IPMI_CMD_READ_FRU_DATA;
		cmd.ic_data = &cmd_data_in;
		cmd.ic_dlen = sizeof (ipmi_fru_read_t);
		cmd.ic_lun = 0;

		if ((resp = ipmi_send(ihp, &cmd)) == NULL) {
			free(tmp);
			return (-1);
		}

		(void) memcpy(&count, resp->ic_data, sizeof (uint8_t));
		if (count != cmd_data_in.ifr_count) {
			(void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH,
			    NULL);
			free(tmp);
			return (-1);
		}
		(void) memcpy(tmp+offset, (char *)(resp->ic_data)+1, count);
		offset += count;
	}
	*buf = tmp;
	return (sz);
}

int
ipmi_fru_parse_product(ipmi_handle_t *ihp, char *fru_area,
    ipmi_fru_prod_info_t *buf)
{
	ipmi_fru_hdr_t fru_hdr;
	char *tmp;
	uint8_t len, typelen;

	(void) memcpy(&fru_hdr, fru_area, sizeof (ipmi_fru_hdr_t));

	/*
	 * We get the offset to the product info area from the FRU common
	 * header which is at the start of the FRU inventory area.
	 *
	 * The product info area is optional, so if the offset is NULL,
	 * indicating that it doesn't exist, then we return an error.
	 */
	if (!fru_hdr.ifh_product_info_off) {
		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
		return (-1);
	}

	tmp = fru_area + (fru_hdr.ifh_product_info_off * 8) + 3;

	(void) memcpy(&typelen, tmp, sizeof (uint8_t));
	len = BITX(typelen, 5, 0);
	ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_manuf_name);
	tmp += len + 1;

	(void) memcpy(&typelen, tmp, sizeof (uint8_t));
	len = BITX(typelen, 5, 0);
	ipmi_decode_string((typelen >> 6), len, tmp+1,
	    buf->ifpi_product_name);
	tmp += len + 1;

	(void) memcpy(&typelen, tmp, sizeof (uint8_t));
	len = BITX(typelen, 5, 0);
	ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_part_number);
	tmp += len + 1;

	(void) memcpy(&typelen, tmp, sizeof (uint8_t));
	len = BITX(typelen, 5, 0);
	ipmi_decode_string((typelen >> 6), len, tmp+1,
	    buf->ifpi_product_version);
	tmp += len + 1;

	(void) memcpy(&typelen, tmp, sizeof (uint8_t));
	len = BITX(typelen, 5, 0);
	ipmi_decode_string((typelen >> 6), len, tmp+1,
	    buf->ifpi_product_serial);
	tmp += len + 1;

	(void) memcpy(&typelen, tmp, sizeof (uint8_t));
	len = BITX(typelen, 5, 0);
	ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_asset_tag);

	return (0);
}


/*
 * The Board Info area is described in Sect 11 of the IPMI Platform Management
 * FRU Information Storage Definition (v1.1).
 */
int
ipmi_fru_parse_board(ipmi_handle_t *ihp, char *fru_area,
    ipmi_fru_brd_info_t *buf)
{
	ipmi_fru_hdr_t fru_hdr;
	char *tmp;
	uint8_t len, typelen;

	(void) memcpy(&fru_hdr, fru_area, sizeof (ipmi_fru_hdr_t));

	/*
	 * We get the offset to the board info area from the FRU common
	 * header which is at the start of the FRU inventory area.
	 *
	 * The board info area is optional, so if the offset is NULL,
	 * indicating that it doesn't exist, then we return an error.
	 */
	if (!fru_hdr.ifh_board_info_off) {
		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
		return (-1);
	}
	tmp = fru_area + (fru_hdr.ifh_board_info_off * 8) + 3;

	(void) memcpy(buf->ifbi_manuf_date, tmp, 3);
	tmp += 3;

	(void) memcpy(&typelen, tmp, sizeof (uint8_t));
	len = BITX(typelen, 5, 0);
	ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_manuf_name);
	tmp += len + 1;

	(void) memcpy(&typelen, tmp, sizeof (uint8_t));
	len = BITX(typelen, 5, 0);
	ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_board_name);
	tmp += len + 1;

	(void) memcpy(&typelen, tmp, sizeof (uint8_t));
	len = BITX(typelen, 5, 0);
	ipmi_decode_string((typelen >> 6), len, tmp+1,
	    buf->ifbi_product_serial);
	tmp += len + 1;

	(void) memcpy(&typelen, tmp, sizeof (uint8_t));
	len = BITX(typelen, 5, 0);
	ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_part_number);

	return (0);
}