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