xref: /illumos-gate/usr/src/lib/libipmi/common/ipmi_fru.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
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 	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
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
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