ipmi_fru.c revision 4557a2a1868181b517f5dfe61ba6eeba58edf4c0
1N/A/*
1N/A * CDDL HEADER START
1N/A *
1N/A * The contents of this file are subject to the terms of the
1N/A * Common Development and Distribution License (the "License").
1N/A * You may not use this file except in compliance with the License.
1N/A *
1N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1N/A * or http://www.opensolaris.org/os/licensing.
1N/A * See the License for the specific language governing permissions
1N/A * and limitations under the License.
1N/A *
1N/A * When distributing Covered Code, include this CDDL HEADER in each
1N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1N/A * If applicable, add the following below this CDDL HEADER, with the
1N/A * fields enclosed by brackets "[]" replaced with your own identifying
1N/A * information: Portions Copyright [yyyy] [name of copyright owner]
1N/A *
1N/A * CDDL HEADER END
1N/A */
1N/A/*
1N/A * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
1N/A * Use is subject to license terms.
1N/A */
1N/A
1N/A#pragma ident "%Z%%M% %I% %E% SMI"
1N/A
1N/A#include <libipmi.h>
1N/A#include <string.h>
1N/A
1N/A#include "ipmi_impl.h"
1N/A
1N/A/*
1N/A * Extracts bits between index h (high, inclusive) and l (low, exclusive) from
1N/A * u, which must be an unsigned integer.
1N/A */
1N/A#define BITX(u, h, l) (((u) >> (l)) & ((1LU << ((h) - (l) + 1LU)) - 1LU))
1N/A
1N/Atypedef struct ipmi_fru_read
1N/A{
1N/A uint8_t ifr_devid;
1N/A uint8_t ifr_offset_lsb;
1N/A uint8_t ifr_offset_msb;
1N/A uint8_t ifr_count;
1N/A} ipmi_fru_read_t;
1N/A
1N/A/*
1N/A * returns: size of FRU inventory data in bytes, on success
1N/A * -1, otherwise
1N/A */
1N/Aint
1N/Aipmi_fru_read(ipmi_handle_t *ihp, ipmi_sdr_fru_locator_t *fru_loc, char **buf)
1N/A{
1N/A ipmi_cmd_t cmd, *resp;
1N/A uint8_t count, devid;
1N/A uint16_t sz, offset = 0;
1N/A ipmi_fru_read_t cmd_data_in;
1N/A
1N/A devid = fru_loc->_devid_or_slaveaddr._logical._is_fl_devid;
1N/A /*
1N/A * First we issue a command to retrieve the size of the specified FRU's
1N/A * inventory area
1N/A */
1N/A cmd.ic_netfn = IPMI_NETFN_STORAGE;
1N/A cmd.ic_cmd = IPMI_CMD_GET_FRU_INV_AREA;
1N/A cmd.ic_data = &devid;
1N/A cmd.ic_dlen = sizeof (uint8_t);
1N/A cmd.ic_lun = 0;
1N/A
1N/A if ((resp = ipmi_send(ihp, &cmd)) == NULL)
1N/A return (-1);
1N/A
1N/A 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 ((*buf = 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)
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);
return (-1);
}
(void) memcpy((*buf)+offset, (char *)(resp->ic_data)+1, count);
offset += count;
}
return (sz);
}
/*
* See Sect 12 of the IPMI Platform Management FRU Information Storage
* Definition (v1.1).
*
* The FRU Product Info Area contains a number of fields which encode
* both the type and length of various name fields into a single byte.
* The byte is a bitfield broken down as follows:
*
* bits descr
* ---- -----
* 7:6 encoding:
* 11b = 8-bit ascii
* 10b = 6-bit packed ascii
* 5:0 length of data in bytes
*
* This function extracts the type and length and then copies the data into the
* supplied buffer. If the type is 6-bit packed ASCII then it first converts
* the string to an 8-bit ASCII string
*
* The function returns the length of the data.
*/
static int
ipmi_fru_decode_string(uint8_t typelen, char *data, char *buf)
{
int i, j = 0, chunks, leftovers;
uint8_t tmp, lo, type, len;
type = typelen >> 6;
len = BITX(typelen, 5, 0);
if (len == 0) {
*buf = '\0';
return (len);
}
/*
* If the type is 8-bit ASCII, we can simply copy the string and return
*/
if (type == 0x3) {
(void) strncpy(buf, data, len);
*(buf+len) = '\0';
return (len);
} else if (type == 0x1 || type == 0x0) {
/*
* Yuck - they either used BCD plus encoding, which we don't
* currently handle, or they used an unspecified encoding type.
* In these cases we'll set buf to an empty string. We still
* need to return the length so that we can get to the next
* record.
*/
*buf = '\0';
return (len);
}
/*
* Otherwise, it's 6-bit packed ASCII, so we have to convert the
* data first
*/
chunks = len / 3;
leftovers = len % 3;
/*
* First we decode the 6-bit string in chunks of 3 bytes as far as
* possible
*/
for (i = 0; i < chunks; i++) {
tmp = BITX(*(data+j), 5, 0);
*buf++ = (char)(tmp + 32);
lo = BITX(*(data+j++), 7, 6);
tmp = BITX(*(data+j), 3, 0);
tmp = (tmp << 2) | lo;
*buf++ = (char)(tmp + 32);
lo = BITX(*(data+j++), 7, 4);
tmp = BITX(*(data+j), 1, 0);
tmp = (tmp << 4) | lo;
*buf++ = (char)(tmp + 32);
tmp = BITX(*(data+j++), 7, 2);
*buf++ = (char)(tmp + 32);
}
switch (leftovers) {
case 1:
tmp = BITX(*(data+j), 5, 0);
*buf++ = (char)(tmp + 32);
break;
case 2:
tmp = BITX(*(data+j), 5, 0);
*buf++ = (char)(tmp + 32);
lo = BITX(*(data+j++), 7, 6);
tmp = BITX(*(data+j), 3, 0);
tmp = (tmp << 2) | lo;
*buf++ = (char)(tmp + 32);
break;
}
*buf = '\0';
return (len);
}
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 = ipmi_fru_decode_string(typelen, tmp+1, buf->ifpi_manuf_name);
tmp += len + 1;
(void) memcpy(&typelen, tmp, sizeof (uint8_t));
len = ipmi_fru_decode_string(typelen, tmp+1, buf->ifpi_product_name);
tmp += len + 1;
(void) memcpy(&typelen, tmp, sizeof (uint8_t));
len = ipmi_fru_decode_string(typelen, tmp+1, buf->ifpi_part_number);
tmp += len + 1;
(void) memcpy(&typelen, tmp, sizeof (uint8_t));
len = ipmi_fru_decode_string(typelen, tmp+1, buf->ifpi_product_version);
tmp += len + 1;
(void) memcpy(&typelen, tmp, sizeof (uint8_t));
len = ipmi_fru_decode_string(typelen, tmp+1, buf->ifpi_product_serial);
tmp += len + 1;
(void) memcpy(&typelen, tmp, sizeof (uint8_t));
(void) ipmi_fru_decode_string(typelen, 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 = ipmi_fru_decode_string(typelen, tmp+1, buf->ifbi_manuf_name);
tmp += len + 1;
(void) memcpy(&typelen, tmp, sizeof (uint8_t));
len = ipmi_fru_decode_string(typelen, tmp+1, buf->ifbi_board_name);
tmp += len + 1;
(void) memcpy(&typelen, tmp, sizeof (uint8_t));
len = ipmi_fru_decode_string(typelen, tmp+1, buf->ifbi_product_serial);
tmp += len + 1;
(void) memcpy(&typelen, tmp, sizeof (uint8_t));
len = ipmi_fru_decode_string(typelen, tmp+1, buf->ifbi_part_number);
return (0);
}