1 /*
2  * Copyright (c) 2012 Pigeon Point Systems.  All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * Redistribution of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * Redistribution in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * Neither the name of Pigeon Point Systems nor the names of
16  * contributors may be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  *
19  * This software is provided "AS IS," without a warranty of any kind.
20  * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
21  * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
22  * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
23  * PIGEON POINT SYSTEMS ("PPS") AND ITS LICENSORS SHALL NOT BE LIABLE
24  * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
25  * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL
26  * PPS OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
27  * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
28  * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
29  * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
30  * EVEN IF PPS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
31  */
32 
33 #include <ipmitool/bswap.h>
34 #include <ipmitool/hpm2.h>
35 #include <ipmitool/ipmi_intf.h>
36 #include <ipmitool/log.h>
37 #include <ipmitool/bswap.h>
38 
39 /* From src/plugins/ipmi_intf.c: */
40 void
41 ipmi_intf_set_max_request_data_size(struct ipmi_intf * intf, uint16_t size);
42 void
43 ipmi_intf_set_max_response_data_size(struct ipmi_intf * intf, uint16_t size);
44 
45 #if HAVE_PRAGMA_PACK
46 # pragma pack(push, 1)
47 #endif
48 
49 /* HPM.x Get Capabilities request */
50 struct hpmx_cmd_get_capabilities_rq {
51 	uint8_t picmg_id;
52 	uint8_t hpmx_id;
53 } ATTRIBUTE_PACKING;
54 
55 /* HPM.2 Get Capabilities response */
56 struct hpm2_cmd_get_capabilities_rp {
57 	uint8_t picmg_id;
58 	struct hpm2_lan_attach_capabilities caps;
59 } ATTRIBUTE_PACKING;
60 
61 #if HAVE_PRAGMA_PACK
62 # pragma pack(pop)
63 #endif
64 
65 /* IPMI Get LAN Configuration Parameters command */
66 #define IPMI_LAN_GET_CONFIG	0x02
67 
hpm2_get_capabilities(struct ipmi_intf * intf,struct hpm2_lan_attach_capabilities * caps)68 int hpm2_get_capabilities(struct ipmi_intf * intf,
69 		struct hpm2_lan_attach_capabilities * caps)
70 {
71 	struct ipmi_rq req;
72 	struct ipmi_rs * rsp;
73 	struct hpmx_cmd_get_capabilities_rq rq;
74 
75 	/* reset result */
76 	memset(caps, 0, sizeof(struct hpm2_lan_attach_capabilities));
77 
78 	/* prepare request */
79 	rq.picmg_id = 0;
80 	rq.hpmx_id = 2;
81 
82 	/* prepare request */
83 	memset(&req, 0, sizeof(req));
84 	req.msg.netfn = IPMI_NETFN_PICMG;
85 	req.msg.cmd = HPM2_GET_LAN_ATTACH_CAPABILITIES;
86 	req.msg.data = (uint8_t *)&rq;
87 	req.msg.data_len = sizeof(rq);
88 
89 
90 	/* send */
91 	rsp = intf->sendrecv(intf, &req);
92 
93 	if (!rsp) {
94 		lprintf(LOG_NOTICE, "Error sending request.");
95 		return -1;
96 	}
97 
98 	if (rsp->ccode == 0xC1) {
99 		lprintf(LOG_DEBUG, "IPM Controller is not HPM.2 compatible");
100 		return rsp->ccode;
101 	} else if (rsp->ccode) {
102 		lprintf(LOG_NOTICE, "Get HPM.x Capabilities request failed,"
103 				" compcode = %x", rsp->ccode);
104 		return rsp->ccode;
105 	}
106 
107 	/* check response length */
108 	if (rsp->data_len < 2 || rsp->data_len > 10) {
109 		lprintf(LOG_NOTICE, "Bad response length, len=%d", rsp->data_len);
110 		return -1;
111 	}
112 
113 	/* check HPM.x identifier */
114 	if (rsp->data[1] != 2) {
115 		lprintf(LOG_NOTICE, "Bad HPM.x ID, id=%d", rsp->data[1]);
116 		return rsp->ccode;
117 	}
118 
119 	/*
120 	 * this hardly can happen, since completion code is already checked.
121 	 * but check for safety
122 	 */
123 	if (rsp->data_len < 4) {
124 		lprintf(LOG_NOTICE, "Bad response length, len=%d", rsp->data_len);
125 		return -1;
126 	}
127 
128 	/* copy HPM.2 capabilities */
129 	memcpy(caps, rsp->data + 2, rsp->data_len - 2);
130 
131 #if WORDS_BIGENDIAN
132 	/* swap bytes to convert from little-endian format */
133 	caps->lan_channel_mask = BSWAP_16(caps->lan_channel_mask);
134 #endif
135 
136 	/* check HPM.2 revision */
137 	if (caps->hpm2_revision_id == 0) {
138 		lprintf(LOG_NOTICE, "Bad HPM.2 revision, rev=%d",
139 				caps->hpm2_revision_id);
140 		return -1;
141 	}
142 
143 	if (!caps->lan_channel_mask) {
144 		return -1;
145 	}
146 
147 	/* check response length */
148 	if (rsp->data_len < 8) {
149 		lprintf(LOG_NOTICE, "Bad response length, len=%d", rsp->data_len);
150 		return -1;
151 	}
152 
153 	/* check HPM.2 LAN parameters start */
154 	if (caps->hpm2_lan_params_start < 0xC0) {
155 		lprintf(LOG_NOTICE, "Bad HPM.2 LAN params start, start=%x",
156 				caps->hpm2_lan_params_start);
157 		return -1;
158 	}
159 
160 	/* check HPM.2 LAN parameters revision */
161 	if (caps->hpm2_lan_params_rev != HPM2_LAN_PARAMS_REV) {
162 		lprintf(LOG_NOTICE, "Bad HPM.2 LAN params revision, rev=%d",
163 				caps->hpm2_lan_params_rev);
164 		return -1;
165 	}
166 
167 	/* check for HPM.2 SOL extension */
168 	if (!(caps->hpm2_caps & HPM2_CAPS_SOL_EXTENSION)) {
169 		/* no further checks */
170 		return 0;
171 	}
172 
173 	/* check response length */
174 	if (rsp->data_len < 10) {
175 		lprintf(LOG_NOTICE, "Bad response length, len=%d", rsp->data_len);
176 		return -1;
177 	}
178 
179 	/* check HPM.2 SOL parameters start */
180 	if (caps->hpm2_sol_params_start < 0xC0) {
181 		lprintf(LOG_NOTICE, "Bad HPM.2 SOL params start, start=%x",
182 				caps->hpm2_sol_params_start);
183 		return -1;
184 	}
185 
186 	/* check HPM.2 SOL parameters revision */
187 	if (caps->hpm2_sol_params_rev != HPM2_SOL_PARAMS_REV) {
188 		lprintf(LOG_NOTICE, "Bad HPM.2 SOL params revision, rev=%d",
189 				caps->hpm2_sol_params_rev);
190 		return -1;
191 	}
192 
193 	return 0;
194 }
195 
hpm2_get_lan_channel_capabilities(struct ipmi_intf * intf,uint8_t hpm2_lan_params_start,struct hpm2_lan_channel_capabilities * caps)196 int hpm2_get_lan_channel_capabilities(struct ipmi_intf * intf,
197 		uint8_t hpm2_lan_params_start,
198 		struct hpm2_lan_channel_capabilities * caps)
199 {
200 	struct ipmi_rq req;
201 	struct ipmi_rs * rsp;
202 	uint8_t rq[4];
203 
204 	/* reset result */
205 	memset(caps, 0, sizeof(struct hpm2_lan_channel_capabilities));
206 
207 	/* prepare request */
208 	memset(&req, 0, sizeof(req));
209 	req.msg.netfn = IPMI_NETFN_TRANSPORT;
210 	req.msg.cmd = IPMI_LAN_GET_CONFIG;
211 	req.msg.data = (uint8_t *)&rq;
212 	req.msg.data_len = sizeof(rq);
213 
214 	/* prepare request data */
215 	rq[0] = 0xE;					/* sending channel */
216 	rq[1] = hpm2_lan_params_start;	/* HPM.2 Channel Caps */
217 	rq[2] = rq[3] = 0;
218 
219 	/* send */
220 	rsp = intf->sendrecv(intf, &req);
221 
222 	if (!rsp) {
223 		lprintf(LOG_NOTICE, "Error sending request.");
224 		return -1;
225 	}
226 
227 	if (rsp->ccode == 0x80) {
228 		lprintf(LOG_DEBUG, "HPM.2 Channel Caps parameter is not supported");
229 		return rsp->ccode;
230 	} else if (rsp->ccode) {
231 		lprintf(LOG_NOTICE, "Get LAN Configuration Parameters request failed,"
232 				" compcode = %x", rsp->ccode);
233 		return rsp->ccode;
234 	}
235 
236 	/* check response length */
237 	if (rsp->data_len != sizeof (struct hpm2_lan_channel_capabilities) + 1) {
238 		lprintf(LOG_NOTICE, "Bad response length, len=%d", rsp->data_len);
239 		return -1;
240 	}
241 
242 	/* check parameter revision */
243 	if (rsp->data[0] !=
244 			LAN_PARAM_REV(HPM2_LAN_PARAMS_REV, HPM2_LAN_PARAMS_REV)) {
245 		lprintf(LOG_NOTICE, "Bad HPM.2 LAN parameter revision, rev=%d",
246 				rsp->data[0]);
247 		return -1;
248 	}
249 
250 	/* copy parameter data */
251 	memcpy(caps, &rsp->data[1], sizeof (struct hpm2_lan_channel_capabilities));
252 
253 #if WORDS_BIGENDIAN
254 	/* swap bytes to convert from little-endian format */
255 	caps->max_inbound_pld_size = BSWAP_16(caps->max_inbound_pld_size);
256 	caps->max_outbound_pld_size = BSWAP_16(caps->max_outbound_pld_size);
257 #endif
258 
259 	return 0;
260 }
261 
hpm2_detect_max_payload_size(struct ipmi_intf * intf)262 int hpm2_detect_max_payload_size(struct ipmi_intf * intf)
263 {
264 	struct hpm2_lan_attach_capabilities attach_caps;
265 	struct hpm2_lan_channel_capabilities channel_caps;
266 	int err;
267 
268 	/* query HPM.2 support */
269 	err = hpm2_get_capabilities(intf, &attach_caps);
270 
271 	/* check if HPM.2 is supported */
272 	if (err != 0 || !attach_caps.lan_channel_mask) {
273 		return err;
274 	}
275 
276 	/* query channel capabilities */
277 	err = hpm2_get_lan_channel_capabilities(intf,
278 			attach_caps.hpm2_lan_params_start, &channel_caps);
279 
280 	/* check if succeeded */
281 	if (err != 0) {
282 		return err;
283 	}
284 
285 	/* update request and response sizes */
286 	ipmi_intf_set_max_request_data_size(intf,
287 			channel_caps.max_inbound_pld_size - 7);
288 	ipmi_intf_set_max_response_data_size(intf,
289 			channel_caps.max_outbound_pld_size - 8);
290 
291 	/* print debug info */
292 	lprintf(LOG_DEBUG, "Set maximum request size to %d\n"
293 			"Set maximum response size to %d",
294 			intf->max_request_data_size, intf->max_response_data_size);
295 
296 	return 0;
297 }
298