xref: /openbmc/ipmitool/src/plugins/open/open.c (revision 2d79e69f)
1 /*
2  * Copyright (c) 2003 Sun Microsystems, Inc.  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 Sun Microsystems, Inc. or 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  * SUN MICROSYSTEMS, INC. ("SUN") 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  * SUN 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 SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
31  */
32 #define _POSIX_SOURCE
33 
34 #include <stdio.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <sys/ioctl.h>
38 #include <errno.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/types.h>
42 #include <sys/select.h>
43 #include <sys/stat.h>
44 
45 #include <ipmitool/ipmi.h>
46 #include <ipmitool/ipmi_intf.h>
47 #include <ipmitool/helper.h>
48 #include <ipmitool/log.h>
49 
50 #if defined(HAVE_CONFIG_H)
51 # include <config.h>
52 #endif
53 
54 #if defined(HAVE_SYS_IOCCOM_H)
55 # include <sys/ioccom.h>
56 #endif
57 
58 #if defined(HAVE_OPENIPMI_H)
59 # if defined(HAVE_LINUX_COMPILER_H)
60 #  include <linux/compiler.h>
61 # endif
62 # include <linux/ipmi.h>
63 #elif defined(HAVE_FREEBSD_IPMI_H)
64 /* FreeBSD OpenIPMI-compatible header */
65 # include <sys/ipmi.h>
66 #else
67 # include "open.h"
68 #endif
69 
70 /**
71  * Maximum input message size for KCS/SMIC is 40 with 2 utility bytes and
72  * 38 bytes of data.
73  * Maximum input message size for BT is 42 with 4 utility bytes and
74  * 38 bytes of data.
75  */
76 #define IPMI_OPENIPMI_MAX_RQ_DATA_SIZE 38
77 
78 /**
79  * Maximum output message size for KCS/SMIC is 38 with 2 utility bytes, a byte
80  * for completion code and 35 bytes of data.
81  * Maximum output message size for BT is 40 with 4 utility bytes, a byte
82  * for completion code and 35 bytes of data.
83  */
84 #define IPMI_OPENIPMI_MAX_RS_DATA_SIZE 35
85 
86 extern int verbose;
87 
88 static int
ipmi_openipmi_open(struct ipmi_intf * intf)89 ipmi_openipmi_open(struct ipmi_intf * intf)
90 {
91 	int i = 0;
92 
93 	char ipmi_dev[16];
94 	char ipmi_devfs[16];
95 	char ipmi_devfs2[16];
96 	int devnum = 0;
97 
98 	devnum = intf->devnum;
99 
100 	sprintf(ipmi_dev, "/dev/ipmi%d", devnum);
101 	sprintf(ipmi_devfs, "/dev/ipmi/%d", devnum);
102 	sprintf(ipmi_devfs2, "/dev/ipmidev/%d", devnum);
103 	lprintf(LOG_DEBUG, "Using ipmi device %d", devnum);
104 
105 	intf->fd = open(ipmi_dev, O_RDWR);
106 
107 	if (intf->fd < 0) {
108 		intf->fd = open(ipmi_devfs, O_RDWR);
109 		if (intf->fd < 0) {
110 			intf->fd = open(ipmi_devfs2, O_RDWR);
111 		}
112 		if (intf->fd < 0) {
113 			lperror(LOG_ERR, "Could not open device at %s or %s or %s",
114 			ipmi_dev, ipmi_devfs , ipmi_devfs2);
115 			return -1;
116 		}
117 	}
118 
119 	if (ioctl(intf->fd, IPMICTL_SET_GETS_EVENTS_CMD, &i) < 0) {
120 		lperror(LOG_ERR, "Could not enable event receiver");
121 		return -1;
122 	}
123 
124 	intf->opened = 1;
125 
126    /* This is never set to 0, the default is IPMI_BMC_SLAVE_ADDR */
127 	if (intf->my_addr != 0) {
128 		if (intf->set_my_addr(intf, intf->my_addr) < 0) {
129 			lperror(LOG_ERR, "Could not set IPMB address");
130 			return -1;
131 		}
132 		lprintf(LOG_DEBUG, "Set IPMB address to 0x%x",
133 			intf->my_addr );
134 	}
135 
136 	intf->manufacturer_id = ipmi_get_oem(intf);
137 	return intf->fd;
138 }
139 static int
ipmi_openipmi_set_my_addr(struct ipmi_intf * intf,uint8_t addr)140 ipmi_openipmi_set_my_addr(struct ipmi_intf *intf, uint8_t addr)
141 {
142 	unsigned int a = addr;
143 	if (ioctl(intf->fd, IPMICTL_SET_MY_ADDRESS_CMD, &a) < 0) {
144 		lperror(LOG_ERR, "Could not set IPMB address");
145 		return -1;
146 	}
147 	intf->my_addr = addr;
148 	return 0;
149 }
150 
151 static void
ipmi_openipmi_close(struct ipmi_intf * intf)152 ipmi_openipmi_close(struct ipmi_intf * intf)
153 {
154 	if (intf->fd >= 0) {
155 		close(intf->fd);
156 		intf->fd = -1;
157 	}
158 
159 	intf->opened = 0;
160 	intf->manufacturer_id = IPMI_OEM_UNKNOWN;
161 }
162 
163 static struct ipmi_rs *
ipmi_openipmi_send_cmd(struct ipmi_intf * intf,struct ipmi_rq * req)164 ipmi_openipmi_send_cmd(struct ipmi_intf * intf, struct ipmi_rq * req)
165 {
166 	struct ipmi_recv recv;
167 	struct ipmi_addr addr;
168 	struct ipmi_system_interface_addr bmc_addr = {
169 		.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE,
170 		.channel = IPMI_BMC_CHANNEL,
171 	};
172 	struct ipmi_ipmb_addr ipmb_addr = {
173 		.addr_type = IPMI_IPMB_ADDR_TYPE,
174 	};
175 	struct ipmi_req _req;
176 	static struct ipmi_rs rsp;
177 	static int curr_seq = 0;
178 	fd_set rset;
179 
180 	uint8_t * data = NULL;
181 	int data_len = 0;
182 
183 
184 	if (intf == NULL || req == NULL)
185 		return NULL;
186 
187 	ipmb_addr.channel = intf->target_channel & 0x0f;
188 
189 	if (intf->opened == 0 && intf->open != NULL)
190 		if (intf->open(intf) < 0)
191 			return NULL;
192 
193 	if (verbose > 2) {
194 		fprintf(stderr, "OpenIPMI Request Message Header:\n");
195 		fprintf(stderr, "  netfn     = 0x%x\n",  req->msg.netfn );
196 		fprintf(stderr, "  cmd       = 0x%x\n", req->msg.cmd);
197 		printbuf(req->msg.data, req->msg.data_len, "OpenIPMI Request Message Data");
198 	}
199 
200 
201 
202 	/*
203 	 * setup and send message
204 	 */
205 
206 	memset(&_req, 0, sizeof(struct ipmi_req));
207 
208 	if (intf->target_addr != 0 &&
209 	    intf->target_addr != intf->my_addr) {
210 		/* use IPMB address if needed */
211 		ipmb_addr.slave_addr = intf->target_addr;
212 		ipmb_addr.lun = req->msg.lun;
213 		lprintf(LOG_DEBUG, "Sending request 0x%x to "
214 			"IPMB target @ 0x%x:0x%x (from 0x%x)",
215 			req->msg.cmd,
216 			intf->target_addr,intf->target_channel, intf->my_addr);
217 
218 		if(intf->transit_addr != 0 && intf->transit_addr != intf->my_addr) {
219 		   uint8_t index = 0;
220 
221 		   lprintf(LOG_DEBUG, "Encapsulating data sent to "
222 			   "end target [0x%02x,0x%02x] using transit [0x%02x,0x%02x] from 0x%x ",
223 			   (0x40 | intf->target_channel),
224 			   intf->target_addr,
225 			   intf->transit_channel,
226 			   intf->transit_addr,
227 			   intf->my_addr
228 			   );
229 
230 		   /* Convert Message to 'Send Message' */
231 		   /* Supplied req : req , internal req : _req  */
232 
233 		   if (verbose > 4) {
234 		      fprintf(stderr, "Converting message:\n");
235 		      fprintf(stderr, "  netfn     = 0x%x\n",  req->msg.netfn );
236 		      fprintf(stderr, "  cmd       = 0x%x\n", req->msg.cmd);
237 		      if (req->msg.data && req->msg.data_len) {
238 			 fprintf(stderr, "  data_len  = %d\n", req->msg.data_len);
239 			 fprintf(stderr, "  data      = %s\n",
240 				 buf2str(req->msg.data,req->msg.data_len));
241 		      }
242 		   }
243 
244 		   /* Modify target address to use 'transit' instead */
245 		   ipmb_addr.slave_addr = intf->transit_addr;
246 		   ipmb_addr.channel    = intf->transit_channel;
247 
248 		   /* FIXME backup "My address" */
249 		   data_len = req->msg.data_len + 8;
250 		   data = malloc(data_len);
251 		   if (data == NULL) {
252 		      lprintf(LOG_ERR, "ipmitool: malloc failure");
253 		      return NULL;
254 		   }
255 
256 		   memset(data, 0, data_len);
257 
258 		   data[index++] = (0x40|intf->target_channel);
259 		   data[index++] = intf->target_addr;
260 		   data[index++] = (  req->msg.netfn << 2 ) |  req->msg.lun ;
261 		   data[index++] = ipmi_csum(data+1, 2);
262 		   data[index++] = 0xFF;    /* normally 0x20 , overwritten by IPMC  */
263 		   data[index++] = ( (0)  << 2) | 0 ;           /* FIXME */
264 		   data[index++] = req->msg.cmd;
265 		   memcpy( (data+index) , req->msg.data, req->msg.data_len);
266 		   index += req->msg.data_len;
267 		   data[index++] = ipmi_csum( (data+4),(req->msg.data_len + 3)  );
268 
269 		   if (verbose > 4) {
270 		      fprintf(stderr, "Encapsulated message:\n");
271 		      fprintf(stderr, "  netfn     = 0x%x\n", IPMI_NETFN_APP  );
272 		      fprintf(stderr, "  cmd       = 0x%x\n", 0x34 );
273 		      if (data && data_len) {
274 			 fprintf(stderr, "  data_len  = %d\n", data_len);
275 			 fprintf(stderr, "  data      = %s\n",
276 				 buf2str(data,data_len));
277 		      }
278 		   }
279 		}
280 		_req.addr = (unsigned char *) &ipmb_addr;
281 		_req.addr_len = sizeof(ipmb_addr);
282 	} else {
283 	   /* otherwise use system interface */
284 	   lprintf(LOG_DEBUG+2, "Sending request 0x%x to "
285 		   "System Interface", req->msg.cmd);
286 	   bmc_addr.lun = req->msg.lun;
287 	   _req.addr = (unsigned char *) &bmc_addr;
288 	   _req.addr_len = sizeof(bmc_addr);
289 	}
290 
291 	_req.msgid = curr_seq++;
292 
293 	/* In case of a bridge request */
294 	if( data != NULL && data_len != 0 ) {
295 	   _req.msg.data = data;
296 	   _req.msg.data_len = data_len;
297 	   _req.msg.netfn = IPMI_NETFN_APP;
298 	   _req.msg.cmd   = 0x34;
299 
300 	} else {
301 	   _req.msg.data = req->msg.data;
302 	   _req.msg.data_len = req->msg.data_len;
303 	   _req.msg.netfn = req->msg.netfn;
304 	   _req.msg.cmd = req->msg.cmd;
305 	}
306 
307 	if (ioctl(intf->fd, IPMICTL_SEND_COMMAND, &_req) < 0) {
308 	   lperror(LOG_ERR, "Unable to send command");
309 	   if (data != NULL) {
310 	      free(data);
311 				data = NULL;
312 		 }
313 	   return NULL;
314 	}
315 
316 	/*
317 	 * wait for and retrieve response
318 	 */
319 
320 	if (intf->noanswer) {
321 	   if (data != NULL) {
322 	      free(data);
323 				data = NULL;
324 		 }
325 	   return NULL;
326 	}
327 
328 	FD_ZERO(&rset);
329 	FD_SET(intf->fd, &rset);
330 
331 	if (select(intf->fd+1, &rset, NULL, NULL, NULL) < 0) {
332 	   lperror(LOG_ERR, "I/O Error");
333 	   if (data != NULL) {
334 	      free(data);
335 				data = NULL;
336 		 }
337 	   return NULL;
338 	}
339 	if (FD_ISSET(intf->fd, &rset) == 0) {
340 	   lprintf(LOG_ERR, "No data available");
341 	   if (data != NULL) {
342 	      free(data);
343 				data = NULL;
344 		 }
345 	   return NULL;
346 	}
347 
348 	recv.addr = (unsigned char *) &addr;
349 	recv.addr_len = sizeof(addr);
350 	recv.msg.data = rsp.data;
351 	recv.msg.data_len = sizeof(rsp.data);
352 
353 	/* get data */
354 	if (ioctl(intf->fd, IPMICTL_RECEIVE_MSG_TRUNC, &recv) < 0) {
355 	   lperror(LOG_ERR, "Error receiving message");
356 	   if (errno != EMSGSIZE) {
357 	      if (data != NULL) {
358 					free(data);
359 					data = NULL;
360 				}
361 	      return NULL;
362 	   }
363 	}
364 
365 	if (verbose > 4) {
366 	   fprintf(stderr, "Got message:");
367 	   fprintf(stderr, "  type      = %d\n", recv.recv_type);
368 	   fprintf(stderr, "  channel   = 0x%x\n", addr.channel);
369 	   fprintf(stderr, "  msgid     = %ld\n", recv.msgid);
370 	   fprintf(stderr, "  netfn     = 0x%x\n", recv.msg.netfn);
371 	   fprintf(stderr, "  cmd       = 0x%x\n", recv.msg.cmd);
372 	   if (recv.msg.data && recv.msg.data_len) {
373 	      fprintf(stderr, "  data_len  = %d\n", recv.msg.data_len);
374 	      fprintf(stderr, "  data      = %s\n",
375 		      buf2str(recv.msg.data, recv.msg.data_len));
376 	   }
377 	}
378 
379 	if(intf->transit_addr != 0 && intf->transit_addr != intf->my_addr) {
380 	   /* ipmb_addr.transit_slave_addr = intf->transit_addr; */
381 	   lprintf(LOG_DEBUG, "Decapsulating data received from transit "
382 		   "IPMB target @ 0x%x", intf->transit_addr);
383 
384 	   /* comp code */
385 	   /* Check data */
386 
387 	   if( recv.msg.data[0] == 0 ) {
388 	      recv.msg.netfn = recv.msg.data[2] >> 2;
389 	      recv.msg.cmd   = recv.msg.data[6];
390 
391 	      recv.msg.data = memmove(recv.msg.data ,recv.msg.data+7 , recv.msg.data_len - 7);
392 	      recv.msg.data_len -=8;
393 
394 	      if (verbose > 4) {
395 		 fprintf(stderr, "Decapsulated  message:\n");
396 		 fprintf(stderr, "  netfn     = 0x%x\n",   recv.msg.netfn );
397 		 fprintf(stderr, "  cmd       = 0x%x\n",  recv.msg.cmd);
398 		 if (recv.msg.data && recv.msg.data_len) {
399 		    fprintf(stderr, "  data_len  = %d\n",  recv.msg.data_len);
400 		    fprintf(stderr, "  data      = %s\n",
401 			    buf2str(recv.msg.data,recv.msg.data_len));
402 		 }
403 	      }
404 	   }
405 	}
406 
407 	/* save completion code */
408 	rsp.ccode = recv.msg.data[0];
409 	rsp.data_len = recv.msg.data_len - 1;
410 
411 	/* save response data for caller */
412 	if (rsp.ccode == 0 && rsp.data_len > 0) {
413 	   memmove(rsp.data, rsp.data + 1, rsp.data_len);
414 	   rsp.data[rsp.data_len] = 0;
415 	}
416 
417 	if (data != NULL) {
418 	   free(data);
419 		 data = NULL;
420 	}
421 
422 	return &rsp;
423 }
424 
ipmi_openipmi_setup(struct ipmi_intf * intf)425 int ipmi_openipmi_setup(struct ipmi_intf * intf)
426 {
427 	/* set default payload size */
428 	intf->max_request_data_size = IPMI_OPENIPMI_MAX_RQ_DATA_SIZE;
429 	intf->max_response_data_size = IPMI_OPENIPMI_MAX_RS_DATA_SIZE;
430 
431 	return 0;
432 }
433 
434 struct ipmi_intf ipmi_open_intf = {
435 	.name = "open",
436 	.desc = "Linux OpenIPMI Interface",
437 	.setup = ipmi_openipmi_setup,
438 	.open = ipmi_openipmi_open,
439 	.close = ipmi_openipmi_close,
440 	.sendrecv = ipmi_openipmi_send_cmd,
441 	.set_my_addr = ipmi_openipmi_set_my_addr,
442 	.my_addr = IPMI_BMC_SLAVE_ADDR,
443 	.target_addr = 0, /* init so -m local_addr does not cause bridging */
444 };
445