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