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
ipmi_fru_read(ipmi_handle_t * ihp,ipmi_sdr_fru_locator_t * fru_loc,char ** buf)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 char *tmp;
59
60 devid = fru_loc->_devid_or_slaveaddr._logical._is_fl_devid;
61 /*
62 * First we issue a command to retrieve the size of the specified FRU's
63 * inventory area
64 */
65 cmd.ic_netfn = IPMI_NETFN_STORAGE;
66 cmd.ic_cmd = IPMI_CMD_GET_FRU_INV_AREA;
67 cmd.ic_data = &devid;
68 cmd.ic_dlen = sizeof (uint8_t);
69 cmd.ic_lun = 0;
70
71 if ((resp = ipmi_send(ihp, &cmd)) == NULL)
72 return (-1);
73
74 if (resp->ic_dlen != 3) {
75 (void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL);
76 return (-1);
77 }
78
79 (void) memcpy(&sz, resp->ic_data, sizeof (uint16_t));
80 if ((tmp = malloc(sz)) == NULL) {
81 (void) ipmi_set_error(ihp, EIPMI_NOMEM, NULL);
82 return (-1);
83 }
84
85 while (offset < sz) {
86 cmd_data_in.ifr_devid = devid;
87 cmd_data_in.ifr_offset_lsb = BITX(offset, 7, 0);
88 cmd_data_in.ifr_offset_msb = BITX(offset, 15, 8);
89 if ((sz - offset) < 128)
90 cmd_data_in.ifr_count = sz - offset;
91 else
92 cmd_data_in.ifr_count = 128;
93
94 cmd.ic_netfn = IPMI_NETFN_STORAGE;
95 cmd.ic_cmd = IPMI_CMD_READ_FRU_DATA;
96 cmd.ic_data = &cmd_data_in;
97 cmd.ic_dlen = sizeof (ipmi_fru_read_t);
98 cmd.ic_lun = 0;
99
100 if ((resp = ipmi_send(ihp, &cmd)) == NULL) {
101 free(tmp);
102 return (-1);
103 }
104
105 (void) memcpy(&count, resp->ic_data, sizeof (uint8_t));
106 if (count != cmd_data_in.ifr_count) {
107 (void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH,
108 NULL);
109 free(tmp);
110 return (-1);
111 }
112 (void) memcpy(tmp+offset, (char *)(resp->ic_data)+1, count);
113 offset += count;
114 }
115 *buf = tmp;
116 return (sz);
117 }
118
119 int
ipmi_fru_parse_product(ipmi_handle_t * ihp,char * fru_area,ipmi_fru_prod_info_t * buf)120 ipmi_fru_parse_product(ipmi_handle_t *ihp, char *fru_area,
121 ipmi_fru_prod_info_t *buf)
122 {
123 ipmi_fru_hdr_t fru_hdr;
124 char *tmp;
125 uint8_t len, typelen;
126
127 (void) memcpy(&fru_hdr, fru_area, sizeof (ipmi_fru_hdr_t));
128
129 /*
130 * We get the offset to the product info area from the FRU common
131 * header which is at the start of the FRU inventory area.
132 *
133 * The product info area is optional, so if the offset is NULL,
134 * indicating that it doesn't exist, then we return an error.
135 */
136 if (!fru_hdr.ifh_product_info_off) {
137 (void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
138 return (-1);
139 }
140
141 tmp = fru_area + (fru_hdr.ifh_product_info_off * 8) + 3;
142
143 (void) memcpy(&typelen, tmp, sizeof (uint8_t));
144 len = BITX(typelen, 5, 0);
145 ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_manuf_name);
146 tmp += len + 1;
147
148 (void) memcpy(&typelen, tmp, sizeof (uint8_t));
149 len = BITX(typelen, 5, 0);
150 ipmi_decode_string((typelen >> 6), len, tmp+1,
151 buf->ifpi_product_name);
152 tmp += len + 1;
153
154 (void) memcpy(&typelen, tmp, sizeof (uint8_t));
155 len = BITX(typelen, 5, 0);
156 ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_part_number);
157 tmp += len + 1;
158
159 (void) memcpy(&typelen, tmp, sizeof (uint8_t));
160 len = BITX(typelen, 5, 0);
161 ipmi_decode_string((typelen >> 6), len, tmp+1,
162 buf->ifpi_product_version);
163 tmp += len + 1;
164
165 (void) memcpy(&typelen, tmp, sizeof (uint8_t));
166 len = BITX(typelen, 5, 0);
167 ipmi_decode_string((typelen >> 6), len, tmp+1,
168 buf->ifpi_product_serial);
169 tmp += len + 1;
170
171 (void) memcpy(&typelen, tmp, sizeof (uint8_t));
172 len = BITX(typelen, 5, 0);
173 ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_asset_tag);
174
175 return (0);
176 }
177
178
179 /*
180 * The Board Info area is described in Sect 11 of the IPMI Platform Management
181 * FRU Information Storage Definition (v1.1).
182 */
183 int
ipmi_fru_parse_board(ipmi_handle_t * ihp,char * fru_area,ipmi_fru_brd_info_t * buf)184 ipmi_fru_parse_board(ipmi_handle_t *ihp, char *fru_area,
185 ipmi_fru_brd_info_t *buf)
186 {
187 ipmi_fru_hdr_t fru_hdr;
188 char *tmp;
189 uint8_t len, typelen;
190
191 (void) memcpy(&fru_hdr, fru_area, sizeof (ipmi_fru_hdr_t));
192
193 /*
194 * We get the offset to the board info area from the FRU common
195 * header which is at the start of the FRU inventory area.
196 *
197 * The board info area is optional, so if the offset is NULL,
198 * indicating that it doesn't exist, then we return an error.
199 */
200 if (!fru_hdr.ifh_board_info_off) {
201 (void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
202 return (-1);
203 }
204 tmp = fru_area + (fru_hdr.ifh_board_info_off * 8) + 3;
205
206 (void) memcpy(buf->ifbi_manuf_date, tmp, 3);
207 tmp += 3;
208
209 (void) memcpy(&typelen, tmp, sizeof (uint8_t));
210 len = BITX(typelen, 5, 0);
211 ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_manuf_name);
212 tmp += len + 1;
213
214 (void) memcpy(&typelen, tmp, sizeof (uint8_t));
215 len = BITX(typelen, 5, 0);
216 ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_board_name);
217 tmp += len + 1;
218
219 (void) memcpy(&typelen, tmp, sizeof (uint8_t));
220 len = BITX(typelen, 5, 0);
221 ipmi_decode_string((typelen >> 6), len, tmp+1,
222 buf->ifbi_product_serial);
223 tmp += len + 1;
224
225 (void) memcpy(&typelen, tmp, sizeof (uint8_t));
226 len = BITX(typelen, 5, 0);
227 ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_part_number);
228
229 return (0);
230 }
231