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