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 2007 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 /* 115 * See Sect 12 of the IPMI Platform Management FRU Information Storage 116 * Definition (v1.1). 117 * 118 * The FRU Product Info Area contains a number of fields which encode 119 * both the type and length of various name fields into a single byte. 120 * The byte is a bitfield broken down as follows: 121 * 122 * bits descr 123 * ---- ----- 124 * 7:6 encoding: 125 * 11b = 8-bit ascii 126 * 10b = 6-bit packed ascii 127 * 5:0 length of data in bytes 128 * 129 * This function extracts the type and length and then copies the data into the 130 * supplied buffer. If the type is 6-bit packed ASCII then it first converts 131 * the string to an 8-bit ASCII string 132 * 133 * The function returns the length of the data. 134 */ 135 static int 136 ipmi_fru_decode_string(uint8_t typelen, char *data, char *buf) 137 { 138 int i, j = 0, chunks, leftovers; 139 uint8_t tmp, lo, type, len; 140 141 type = typelen >> 6; 142 len = BITX(typelen, 5, 0); 143 144 if (len == 0) { 145 *buf = '\0'; 146 return (len); 147 } 148 /* 149 * If the type is 8-bit ASCII, we can simply copy the string and return 150 */ 151 if (type == 0x3) { 152 (void) strncpy(buf, data, len); 153 *(buf+len) = '\0'; 154 return (len); 155 } else if (type == 0x1 || type == 0x0) { 156 /* 157 * Yuck - they either used BCD plus encoding, which we don't 158 * currently handle, or they used an unspecified encoding type. 159 * In these cases we'll set buf to an empty string. We still 160 * need to return the length so that we can get to the next 161 * record. 162 */ 163 *buf = '\0'; 164 return (len); 165 } 166 167 /* 168 * Otherwise, it's 6-bit packed ASCII, so we have to convert the 169 * data first 170 */ 171 chunks = len / 3; 172 leftovers = len % 3; 173 174 /* 175 * First we decode the 6-bit string in chunks of 3 bytes as far as 176 * possible 177 */ 178 for (i = 0; i < chunks; i++) { 179 tmp = BITX(*(data+j), 5, 0); 180 *buf++ = (char)(tmp + 32); 181 182 lo = BITX(*(data+j++), 7, 6); 183 tmp = BITX(*(data+j), 3, 0); 184 tmp = (tmp << 2) | lo; 185 *buf++ = (char)(tmp + 32); 186 187 lo = BITX(*(data+j++), 7, 4); 188 tmp = BITX(*(data+j), 1, 0); 189 tmp = (tmp << 4) | lo; 190 *buf++ = (char)(tmp + 32); 191 192 tmp = BITX(*(data+j++), 7, 2); 193 *buf++ = (char)(tmp + 32); 194 } 195 switch (leftovers) { 196 case 1: 197 tmp = BITX(*(data+j), 5, 0); 198 *buf++ = (char)(tmp + 32); 199 break; 200 case 2: 201 tmp = BITX(*(data+j), 5, 0); 202 *buf++ = (char)(tmp + 32); 203 204 lo = BITX(*(data+j++), 7, 6); 205 tmp = BITX(*(data+j), 3, 0); 206 tmp = (tmp << 2) | lo; 207 *buf++ = (char)(tmp + 32); 208 break; 209 } 210 *buf = '\0'; 211 return (len); 212 } 213 214 int 215 ipmi_fru_parse_product(ipmi_handle_t *ihp, char *fru_area, 216 ipmi_fru_prod_info_t *buf) 217 { 218 ipmi_fru_hdr_t fru_hdr; 219 char *tmp; 220 uint8_t len, typelen; 221 222 (void) memcpy(&fru_hdr, fru_area, sizeof (ipmi_fru_hdr_t)); 223 224 /* 225 * We get the offset to the product info area from the FRU common 226 * header which is at the start of the FRU inventory area. 227 * 228 * The product info area is optional, so if the offset is NULL, 229 * indicating that it doesn't exist, then we return an error. 230 */ 231 if (!fru_hdr.ifh_product_info_off) { 232 (void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL); 233 return (-1); 234 } 235 236 tmp = fru_area + (fru_hdr.ifh_product_info_off * 8) + 3; 237 238 (void) memcpy(&typelen, tmp, sizeof (uint8_t)); 239 len = ipmi_fru_decode_string(typelen, tmp+1, buf->ifpi_manuf_name); 240 tmp += len + 1; 241 242 (void) memcpy(&typelen, tmp, sizeof (uint8_t)); 243 len = ipmi_fru_decode_string(typelen, tmp+1, buf->ifpi_product_name); 244 tmp += len + 1; 245 246 (void) memcpy(&typelen, tmp, sizeof (uint8_t)); 247 len = ipmi_fru_decode_string(typelen, tmp+1, buf->ifpi_part_number); 248 tmp += len + 1; 249 250 (void) memcpy(&typelen, tmp, sizeof (uint8_t)); 251 len = ipmi_fru_decode_string(typelen, tmp+1, buf->ifpi_product_version); 252 tmp += len + 1; 253 254 (void) memcpy(&typelen, tmp, sizeof (uint8_t)); 255 len = ipmi_fru_decode_string(typelen, tmp+1, buf->ifpi_product_serial); 256 tmp += len + 1; 257 258 (void) memcpy(&typelen, tmp, sizeof (uint8_t)); 259 (void) ipmi_fru_decode_string(typelen, tmp+1, buf->ifpi_asset_tag); 260 261 return (0); 262 } 263 264 265 /* 266 * The Board Info area is described in Sect 11 of the IPMI Platform Management 267 * FRU Information Storage Definition (v1.1). 268 */ 269 int 270 ipmi_fru_parse_board(ipmi_handle_t *ihp, char *fru_area, 271 ipmi_fru_brd_info_t *buf) 272 { 273 ipmi_fru_hdr_t fru_hdr; 274 char *tmp; 275 uint8_t len, typelen; 276 277 (void) memcpy(&fru_hdr, fru_area, sizeof (ipmi_fru_hdr_t)); 278 279 /* 280 * We get the offset to the board info area from the FRU common 281 * header which is at the start of the FRU inventory area. 282 * 283 * The board info area is optional, so if the offset is NULL, 284 * indicating that it doesn't exist, then we return an error. 285 */ 286 if (!fru_hdr.ifh_board_info_off) { 287 (void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL); 288 return (-1); 289 } 290 tmp = fru_area + (fru_hdr.ifh_board_info_off * 8) + 3; 291 292 (void) memcpy(buf->ifbi_manuf_date, tmp, 3); 293 tmp += 3; 294 295 (void) memcpy(&typelen, tmp, sizeof (uint8_t)); 296 len = ipmi_fru_decode_string(typelen, tmp+1, buf->ifbi_manuf_name); 297 tmp += len + 1; 298 299 (void) memcpy(&typelen, tmp, sizeof (uint8_t)); 300 len = ipmi_fru_decode_string(typelen, tmp+1, buf->ifbi_board_name); 301 tmp += len + 1; 302 303 (void) memcpy(&typelen, tmp, sizeof (uint8_t)); 304 len = ipmi_fru_decode_string(typelen, tmp+1, buf->ifbi_product_serial); 305 tmp += len + 1; 306 307 (void) memcpy(&typelen, tmp, sizeof (uint8_t)); 308 len = ipmi_fru_decode_string(typelen, tmp+1, buf->ifbi_part_number); 309 310 return (0); 311 } 312