1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <libipmi.h> 29 #include <string.h> 30 31 #include "ipmi_impl.h" 32 33 /* 34 * Extracts bits between index h (high, inclusive) and l (low, exclusive) from 35 * u, which must be an unsigned integer. 36 */ 37 #define BITX(u, h, l) (((u) >> (l)) & ((1LU << ((h) - (l) + 1LU)) - 1LU)) 38 39 typedef struct ipmi_fru_read 40 { 41 uint8_t ifr_devid; 42 uint8_t ifr_offset_lsb; 43 uint8_t ifr_offset_msb; 44 uint8_t ifr_count; 45 } ipmi_fru_read_t; 46 47 /* 48 * returns: size of FRU inventory data in bytes, on success 49 * -1, otherwise 50 */ 51 int 52 ipmi_fru_read(ipmi_handle_t *ihp, ipmi_sdr_fru_locator_t *fru_loc, char **buf) 53 { 54 ipmi_cmd_t cmd, *resp; 55 uint8_t count, devid; 56 uint16_t sz, offset = 0; 57 ipmi_fru_read_t cmd_data_in; 58 59 devid = fru_loc->_devid_or_slaveaddr._logical._is_fl_devid; 60 /* 61 * First we issue a command to retrieve the size of the specified FRU's 62 * inventory area 63 */ 64 cmd.ic_netfn = IPMI_NETFN_STORAGE; 65 cmd.ic_cmd = IPMI_CMD_GET_FRU_INV_AREA; 66 cmd.ic_data = &devid; 67 cmd.ic_dlen = sizeof (uint8_t); 68 cmd.ic_lun = 0; 69 70 if ((resp = ipmi_send(ihp, &cmd)) == NULL) 71 return (-1); 72 73 if (resp->ic_dlen != 3) { 74 (void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL); 75 return (-1); 76 } 77 78 (void) memcpy(&sz, resp->ic_data, sizeof (uint16_t)); 79 if ((*buf = malloc(sz)) == NULL) { 80 (void) ipmi_set_error(ihp, EIPMI_NOMEM, NULL); 81 return (-1); 82 } 83 84 while (offset < sz) { 85 cmd_data_in.ifr_devid = devid; 86 cmd_data_in.ifr_offset_lsb = BITX(offset, 7, 0); 87 cmd_data_in.ifr_offset_msb = BITX(offset, 15, 8); 88 if ((sz - offset) < 128) 89 cmd_data_in.ifr_count = sz - offset; 90 else 91 cmd_data_in.ifr_count = 128; 92 93 cmd.ic_netfn = IPMI_NETFN_STORAGE; 94 cmd.ic_cmd = IPMI_CMD_READ_FRU_DATA; 95 cmd.ic_data = &cmd_data_in; 96 cmd.ic_dlen = sizeof (ipmi_fru_read_t); 97 cmd.ic_lun = 0; 98 99 if ((resp = ipmi_send(ihp, &cmd)) == NULL) 100 return (-1); 101 102 (void) memcpy(&count, resp->ic_data, sizeof (uint8_t)); 103 if (count != cmd_data_in.ifr_count) { 104 (void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, 105 NULL); 106 return (-1); 107 } 108 (void) memcpy((*buf)+offset, (char *)(resp->ic_data)+1, count); 109 offset += count; 110 } 111 return (sz); 112 } 113 114 int 115 ipmi_fru_parse_product(ipmi_handle_t *ihp, char *fru_area, 116 ipmi_fru_prod_info_t *buf) 117 { 118 ipmi_fru_hdr_t fru_hdr; 119 char *tmp; 120 uint8_t len, typelen; 121 122 (void) memcpy(&fru_hdr, fru_area, sizeof (ipmi_fru_hdr_t)); 123 124 /* 125 * We get the offset to the product info area from the FRU common 126 * header which is at the start of the FRU inventory area. 127 * 128 * The product info area is optional, so if the offset is NULL, 129 * indicating that it doesn't exist, then we return an error. 130 */ 131 if (!fru_hdr.ifh_product_info_off) { 132 (void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL); 133 return (-1); 134 } 135 136 tmp = fru_area + (fru_hdr.ifh_product_info_off * 8) + 3; 137 138 (void) memcpy(&typelen, tmp, sizeof (uint8_t)); 139 len = BITX(typelen, 4, 0); 140 ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_manuf_name); 141 tmp += len + 1; 142 143 (void) memcpy(&typelen, tmp, sizeof (uint8_t)); 144 len = BITX(typelen, 4, 0); 145 ipmi_decode_string((typelen >> 6), len, tmp+1, 146 buf->ifpi_product_name); 147 tmp += len + 1; 148 149 (void) memcpy(&typelen, tmp, sizeof (uint8_t)); 150 len = BITX(typelen, 4, 0); 151 ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_part_number); 152 tmp += len + 1; 153 154 (void) memcpy(&typelen, tmp, sizeof (uint8_t)); 155 len = BITX(typelen, 4, 0); 156 ipmi_decode_string((typelen >> 6), len, tmp+1, 157 buf->ifpi_product_version); 158 tmp += len + 1; 159 160 (void) memcpy(&typelen, tmp, sizeof (uint8_t)); 161 len = BITX(typelen, 4, 0); 162 ipmi_decode_string((typelen >> 6), len, tmp+1, 163 buf->ifpi_product_serial); 164 tmp += len + 1; 165 166 (void) memcpy(&typelen, tmp, sizeof (uint8_t)); 167 len = BITX(typelen, 4, 0); 168 ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_asset_tag); 169 170 return (0); 171 } 172 173 174 /* 175 * The Board Info area is described in Sect 11 of the IPMI Platform Management 176 * FRU Information Storage Definition (v1.1). 177 */ 178 int 179 ipmi_fru_parse_board(ipmi_handle_t *ihp, char *fru_area, 180 ipmi_fru_brd_info_t *buf) 181 { 182 ipmi_fru_hdr_t fru_hdr; 183 char *tmp; 184 uint8_t len, typelen; 185 186 (void) memcpy(&fru_hdr, fru_area, sizeof (ipmi_fru_hdr_t)); 187 188 /* 189 * We get the offset to the board info area from the FRU common 190 * header which is at the start of the FRU inventory area. 191 * 192 * The board info area is optional, so if the offset is NULL, 193 * indicating that it doesn't exist, then we return an error. 194 */ 195 if (!fru_hdr.ifh_board_info_off) { 196 (void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL); 197 return (-1); 198 } 199 tmp = fru_area + (fru_hdr.ifh_board_info_off * 8) + 3; 200 201 (void) memcpy(buf->ifbi_manuf_date, tmp, 3); 202 tmp += 3; 203 204 (void) memcpy(&typelen, tmp, sizeof (uint8_t)); 205 len = BITX(typelen, 4, 0); 206 ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_manuf_name); 207 tmp += len + 1; 208 209 (void) memcpy(&typelen, tmp, sizeof (uint8_t)); 210 len = BITX(typelen, 4, 0); 211 ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_board_name); 212 tmp += len + 1; 213 214 (void) memcpy(&typelen, tmp, sizeof (uint8_t)); 215 len = BITX(typelen, 4, 0); 216 ipmi_decode_string((typelen >> 6), len, tmp+1, 217 buf->ifbi_product_serial); 218 tmp += len + 1; 219 220 (void) memcpy(&typelen, tmp, sizeof (uint8_t)); 221 len = BITX(typelen, 4, 0); 222 ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_part_number); 223 224 return (0); 225 } 226