Added IPMB dual bridge support (no need for driver support)

This commit is contained in:
Francois Isabelle 2010-01-20 14:52:29 +00:00
parent 50c514e336
commit e41a6aacb3
4 changed files with 336 additions and 122 deletions

View File

@ -131,6 +131,109 @@ no problems. The IPMIv1.5 interface will attempt to use OpenSSL for MD5
hash function at compile time but if that is not found it will use an
internal library.
IPMB Dual Bridging in IPMITOOL
-------------------------------
IPMI offers a standard messaging interface.
The following concepts are related to this messaging interface:
Channel type : Communication channel type (SMS/KCS, IPMB, LAN)
Channel number : Channel descritor
Requester : Address of the requester
Responder : Address of the responder
NetFN : The logical function for the request/response.
Command : The command number
Sequence : An ID identifiying the request/response pair
Message tracking : The ability to match request/response pair.
When a communication is issued through any of the channels, an application
formats a request and expect a response.
Direct Command
--------------
The simplest form of communication is a "direct command" using SMS/KCS
Example:
ipmitool raw 6 4
55 00
This send raw command 4 (selftest) from netfn 6(application) to KCS, the driver
takes care of 'message tracking' and provides the answer.
Hopefuly, the application also includes a "human readable" instance of the API:
ipmitool mc selftest
Selftest: passed
Bridged Command
---------------
One slightly more complicated communication mode is the so-called
"bridged command" using IPMB.
Example:
ipmitool -m 0x94 -t 0x9a raw 6 4
55 00
or
ipmitool -m 0x94 -t 0x9a mc selftest
Selftest: passed
This still sends the same command 4 (selftest) from netfn 6(application) to
the target. However, to do so, the command is encapsulated (by the driver) and
sent using the command 0x34 (send message) from netfn 6(application) to KCS.
Then KCS is polled by the driver until a message has been received, then the
driver uses command 0x33 (get message). The driver also tracks the message
and makes sure the response matches the request. Then it decapsultates the
message and gives the response back to the application.
Dual Bridged Command
--------------------
Things get a little more ugly when the application needs to reach a management
controller sitting on an interface (or channel) not directly connected to the
BMC/IPMC. In the case the application must encapsulate its message itself and
request the IPMC to deal with message tracking itself.
Its been working well with IPMITOOL on the LAN interface with:
ipmitool -H <ip> -U <user> -P <password> -B 0 -T 0x8a -m 0x20 -t 0x7a -b 7
mc selftest
However, trying to dual bridge commands locally with :
ipmitool -B 0 -T 0x9a -m 0x94 -t 0x7a -b 7 mc selftest didn't work
(it returned the same data as ipmitool -m 0x20 -t 0x7a -b 7 mc selftest )
The reason was that the "openipmi" interface pluging didn't
encapsulate/decapsulate the message and didn't even detect the intent
to double bridge the request.
./src/ipmitool -B 0 -T 0x8a -m 0x94 -t 0x7a -b 7 mc selftest
-B 0 : transit channel for first bridge level (channel 0: IPMB-0)
-T 0x8a : transit destination address (remote IPMC address)
-m 0x94 : source address (local IPMC address on IPMB-0)
-t 0x7a : remote target (AMC IPMB-L address)
-b 7 : remote channel (channel 7: IPMB-L)
The transit source address (remote IPMC address on remote channel) is
automatically assigned by the remote IPMC.
Payload Size Limit
------------------
Vecause some commands return a lot of data (fru read/get sdr) and because 2
levels of encapsulation are used, some command will fail.
For instance this works.
ipmitool -H <ip> -U <user> -P <password> -B 0 -T 0x8a -m 0x94 -t 0x7a -b 7
mc selftest
but this does not:
ipmitool -H <ip> -U <user> -P <password> -B 0 -T 0x8a -m 0x94 -t 0x7a -b 7
fru print.
Usage
=====

View File

@ -67,6 +67,7 @@
#include <ipmitool/ipmi_pef.h>
#include <ipmitool/ipmi_oem.h>
#include <ipmitool/ipmi_ekanalyzer.h>
#include <ipmitool/ipmi_picmg.h>
#ifdef HAVE_CONFIG_H
# include <config.h>
@ -242,6 +243,7 @@ ipmi_option_usage(const char * progname, struct ipmi_cmd * cmdlist, struct ipmi_
lprintf(LOG_NOTICE, " -m address Set local IPMB address");
lprintf(LOG_NOTICE, " -b channel Set destination channel for bridged request");
lprintf(LOG_NOTICE, " -t address Bridge request to remote target address");
lprintf(LOG_NOTICE, " -M address Set transit local address for bridge request(dual bridge)");
lprintf(LOG_NOTICE, " -B channel Set transit channel for bridged request (dual bridge)");
lprintf(LOG_NOTICE, " -T address Set transit address for bridge request (dual bridge)");
lprintf(LOG_NOTICE, " -l lun Set destination lun for raw commands");
@ -355,6 +357,7 @@ ipmi_main(int argc, char ** argv,
int privlvl = 0;
uint8_t target_addr = 0;
uint8_t target_channel = 0;
uint8_t transit_addr = 0;
uint8_t transit_channel = 0;
uint8_t target_lun = 0;
@ -722,8 +725,71 @@ ipmi_main(int argc, char ** argv,
ipmi_main_intf->devnum = devnum;
/* setup IPMB local and target address if given */
ipmi_main_intf->my_addr = my_addr ? : IPMI_BMC_SLAVE_ADDR;
if (target_addr > 0) {
if( my_addr ) {
ipmi_main_intf->my_addr = my_addr;
}
else {
/* Check if PICMG extension is available to use the function GetDeviceLocator
* to retreive i2c address PICMG hack to set right IPMB address,
* If extension is not supported, should not give any problems
* PICMG Extension Version 2.0 (PICMG 3.0 Revision 1.0 ATCA) to
* PICMG Extension Version 2.3 (PICMG 3.0 Revision 3.0 ATCA)
*/
/* First, check if PICMG extension is available and supported */
struct ipmi_rq req;
struct ipmi_rs *rsp;
char msg_data;
unsigned char version_accepted = 0;
lprintf(LOG_INFO, "Running PICMG GetDeviceLocator" );
memset(&req, 0, sizeof(req));
req.msg.netfn = IPMI_NETFN_PICMG;
req.msg.cmd = PICMG_GET_PICMG_PROPERTIES_CMD;
msg_data = 0x00;
req.msg.data = &msg_data;
req.msg.data_len = 1;
msg_data = 0;
rsp = ipmi_main_intf->sendrecv(ipmi_main_intf, &req);
if (rsp && !rsp->ccode) {
if
(
(rsp->data[0] == 0)
&&
((rsp->data[1] & 0x0F ) == PICMG_ATCA_MAJOR_VERSION )
){
version_accepted = 1;
lprintf(LOG_INFO, "Discovered PICMG Extension %d.%d",
(rsp->data[1] & 0x0f),(rsp->data[1] >> 4) );
}
}
if(version_accepted == 1){
lprintf(LOG_DEBUG, "Running PICMG GetDeviceLocator" );
memset(&req, 0, sizeof(req));
req.msg.netfn = IPMI_NETFN_PICMG;
req.msg.cmd = PICMG_GET_ADDRESS_INFO_CMD;
msg_data = 0x00;
req.msg.data = &msg_data;
req.msg.data_len = 1;
msg_data = 0;
rsp = ipmi_main_intf->sendrecv(ipmi_main_intf, &req);
if (rsp && !rsp->ccode) {
ipmi_main_intf->my_addr = rsp->data[2];
ipmi_main_intf->target_addr = ipmi_main_intf->my_addr;
lprintf(LOG_INFO, "Discovered IPMB address = 0x%x", ipmi_main_intf->my_addr);
}
}
else{
lprintf(LOG_INFO,
"No PICMG Extenstion discovered, keeping IPMB address 0x20");
}
}
if ( target_addr > 0 && (target_addr != my_addr) ) {
/* need to open the interface first */
if (ipmi_main_intf->open != NULL)
ipmi_main_intf->open(ipmi_main_intf);

View File

@ -44,7 +44,6 @@
#include <ipmitool/ipmi_intf.h>
#include <ipmitool/helper.h>
#include <ipmitool/log.h>
#include <ipmitool/ipmi_picmg.h>
#if defined(HAVE_CONFIG_H)
# include <config.h>
@ -68,9 +67,7 @@ static int
ipmi_openipmi_open(struct ipmi_intf * intf)
{
int i = 0;
struct ipmi_rq req;
struct ipmi_rs *rsp;
char msg_data;
char ipmi_dev[16];
char ipmi_devfs[16];
char ipmi_devfs2[16];
@ -102,83 +99,20 @@ ipmi_openipmi_open(struct ipmi_intf * intf)
return -1;
}
intf->opened = 1;
/* Check if PICMG extension is available to use the function GetDeviceLocator
* to retreive i2c address PICMG hack to set right IPMB address,
* If extension is not supported, should not give any problems
* PICMG Extension Version 2.0 (PICMG 3.0 Revision 1.0 ATCA) to
* PICMG Extension Version 2.3 (PICMG 3.0 Revision 3.0 ATCA)
*/
if (intf->my_addr == IPMI_BMC_SLAVE_ADDR) {
/* First, check if PICMG extension is available and supported */
unsigned char version_accepted = 0;
lprintf(LOG_DEBUG, "Running PICMG GetDeviceLocator" );
memset(&req, 0, sizeof(req));
req.msg.netfn = IPMI_NETFN_PICMG;
req.msg.cmd = PICMG_GET_PICMG_PROPERTIES_CMD;
msg_data = 0x00;
req.msg.data = &msg_data;
req.msg.data_len = 1;
msg_data = 0;
rsp = intf->sendrecv(intf, &req);
if (rsp && !rsp->ccode) {
if
(
(rsp->data[0] == 0)
&&
(
(rsp->data[1] == 0x02)
||
(rsp->data[1] == 0x12)
||
(rsp->data[1] == 0x22)
||
(rsp->data[1] == 0x32)
)
){
version_accepted = 1;
lprintf(LOG_DEBUG, "Discovered PICMG Extension %d.%d",
(rsp->data[1] & 0x0f),(rsp->data[1] >> 4) );
}
}
if(version_accepted == 1){
lprintf(LOG_DEBUG, "Running PICMG GetDeviceLocator" );
memset(&req, 0, sizeof(req));
req.msg.netfn = IPMI_NETFN_PICMG;
req.msg.cmd = PICMG_GET_ADDRESS_INFO_CMD;
msg_data = 0x00;
req.msg.data = &msg_data;
req.msg.data_len = 1;
msg_data = 0;
rsp = intf->sendrecv(intf, &req);
if (rsp && !rsp->ccode) {
intf->my_addr = rsp->data[2];
intf->target_addr = intf->my_addr;
lprintf(LOG_DEBUG, "Discovered IPMB address = 0x%x", intf->my_addr);
}
}
else{
lprintf(LOG_DEBUG,
"No PICMG Extenstion discovered, keeping IPMB address 0x20");
}
}
/* This is never set to 0, the default is IPMI_BMC_SLAVE_ADDR */
if (intf->my_addr != 0) {
unsigned int a = intf->my_addr;
if (ioctl(intf->fd, IPMICTL_SET_MY_ADDRESS_CMD, &a) < 0) {
lperror(LOG_ERR, "Could not set IPMB address");
return -1;
}
lprintf(LOG_DEBUG, "Set IPMB address to 0x%x",
intf->my_addr);
intf->my_addr );
}
return intf->fd;
}
@ -211,8 +145,13 @@ ipmi_openipmi_send_cmd(struct ipmi_intf * intf, struct ipmi_rq * req)
static int curr_seq = 0;
fd_set rset;
uint8_t * data = NULL;
int data_len = 0;
if (intf == NULL || req == NULL)
return NULL;
if (intf->opened == 0 && intf->open != NULL)
if (intf->open(intf) < 0)
return NULL;
@ -234,14 +173,69 @@ ipmi_openipmi_send_cmd(struct ipmi_intf * intf, struct ipmi_rq * req)
ipmb_addr.lun = req->msg.lun;
lprintf(LOG_DEBUG, "Sending request to "
"IPMB target @ 0x%x (from 0x%x)", intf->target_addr,intf->my_addr);
#ifdef ENABLE_INTF_OPEN_DUAL_BRIDGE
if(intf->transit_addr != 0 &&
intf->transit_addr != intf->my_addr) {
ipmb_addr.transit_slave_addr = intf->transit_addr;
lprintf(LOG_DEBUG, "Sending through transit "
"IPMB target @ 0x%x", intf->transit_addr);
if(intf->transit_addr != 0 && intf->transit_addr != intf->my_addr) {
uint8_t index = 0;
lprintf(LOG_DEBUG, "Encapsulating data sent to "
"end target [0x%02x,0x%02x] using transit [0x%02x,0x%02x] from 0x%x ",
(0x40 | intf->target_channel),
intf->target_addr,
intf->transit_channel,
intf->transit_addr,
intf->my_addr
);
/* Convert Message to 'Send Message' */
/* Supplied req : req , internal req : _req */
if (verbose > 4) {
fprintf(stderr, "Converting message:\n");
fprintf(stderr, " netfn = 0x%x\n", req->msg.netfn );
fprintf(stderr, " cmd = 0x%x\n", req->msg.cmd);
if (recv.msg.data && recv.msg.data_len) {
fprintf(stderr, " data_len = %d\n", req->msg.data_len);
fprintf(stderr, " data = %s\n",
buf2str(req->msg.data,req->msg.data_len));
}
}
/* Modify target address to use 'transit' instead */
ipmb_addr.slave_addr = intf->transit_addr;
ipmb_addr.channel = intf->transit_channel;
/* FIXME backup "My address" */
data_len = req->msg.data_len + 8;
data = malloc(data_len);
if (data == NULL) {
lprintf(LOG_ERR, "ipmitool: malloc failure");
return NULL;
}
memset(data, 0, data_len);
data[index++] = (0x40|intf->target_channel);
data[index++] = intf->target_addr;
data[index++] = ( req->msg.netfn << 2 ) | req->msg.lun ;
data[index++] = ipmi_csum(data+1, 2);
data[index++] = 0xFF; /* normally 0x20 , overwritten by IPMC */
data[index++] = ( (0) << 2) | 0 ; /* FIXME */
data[index++] = req->msg.cmd;
memcpy( (data+index) , req->msg.data, req->msg.data_len);
index += req->msg.data_len;
data[index++] = ipmi_csum( (data+4),(req->msg.data_len + 3) );
if (verbose > 4) {
fprintf(stderr, "Encapsulated message:\n");
fprintf(stderr, " netfn = 0x%x\n", IPMI_NETFN_APP );
fprintf(stderr, " cmd = 0x%x\n", 0x34 );
if (data && data_len) {
fprintf(stderr, " data_len = %d\n", data_len);
fprintf(stderr, " data = %s\n",
buf2str(data,data_len));
}
}
}
#endif
_req.addr = (unsigned char *) &ipmb_addr;
_req.addr_len = sizeof(ipmb_addr);
} else {
@ -254,13 +248,25 @@ ipmi_openipmi_send_cmd(struct ipmi_intf * intf, struct ipmi_rq * req)
}
_req.msgid = curr_seq++;
_req.msg.netfn = req->msg.netfn;
_req.msg.cmd = req->msg.cmd;
/* In case of a bridge request */
if( data != NULL && data_len != 0 ) {
_req.msg.data = data;
_req.msg.data_len = data_len;
_req.msg.netfn = IPMI_NETFN_APP;
_req.msg.cmd = 0x34;
} else {
_req.msg.data = req->msg.data;
_req.msg.data_len = req->msg.data_len;
_req.msg.netfn = req->msg.netfn;
_req.msg.cmd = req->msg.cmd;
}
if (ioctl(intf->fd, IPMICTL_SEND_COMMAND, &_req) < 0) {
lperror(LOG_ERR, "Unable to send command");
if (data != NULL)
free(data);
return NULL;
}
@ -268,18 +274,25 @@ ipmi_openipmi_send_cmd(struct ipmi_intf * intf, struct ipmi_rq * req)
* wait for and retrieve response
*/
if (intf->noanswer)
if (intf->noanswer) {
if (data != NULL)
free(data);
return NULL;
}
FD_ZERO(&rset);
FD_SET(intf->fd, &rset);
if (select(intf->fd+1, &rset, NULL, NULL, NULL) < 0) {
lperror(LOG_ERR, "I/O Error");
if (data != NULL)
free(data);
return NULL;
}
if (FD_ISSET(intf->fd, &rset) == 0) {
lprintf(LOG_ERR, "No data available");
if (data != NULL)
free(data);
return NULL;
}
@ -291,9 +304,12 @@ ipmi_openipmi_send_cmd(struct ipmi_intf * intf, struct ipmi_rq * req)
/* get data */
if (ioctl(intf->fd, IPMICTL_RECEIVE_MSG_TRUNC, &recv) < 0) {
lperror(LOG_ERR, "Error receiving message");
if (errno != EMSGSIZE)
if (errno != EMSGSIZE) {
if (data != NULL)
free(data);
return NULL;
}
}
if (verbose > 4) {
fprintf(stderr, "Got message:");
@ -309,6 +325,36 @@ ipmi_openipmi_send_cmd(struct ipmi_intf * intf, struct ipmi_rq * req)
}
}
if(intf->transit_addr != 0 && intf->transit_addr != intf->my_addr) {
uint8_t index = 0;
/* ipmb_addr.transit_slave_addr = intf->transit_addr; */
lprintf(LOG_DEBUG, "Decapsulating data received from transit "
"IPMB target @ 0x%x", intf->transit_addr);
/* comp code */
/* Check data */
if( recv.msg.data[0] == 0 ) {
recv.msg.netfn = recv.msg.data[2] >> 2;
recv.msg.cmd = recv.msg.data[6];
recv.msg.data = memmove(recv.msg.data ,recv.msg.data+7 , recv.msg.data_len - 7);
recv.msg.data_len -=8;
if (verbose > 4) {
fprintf(stderr, "Decapsulated message:\n");
fprintf(stderr, " netfn = 0x%x\n", recv.msg.netfn );
fprintf(stderr, " cmd = 0x%x\n", recv.msg.cmd);
if (recv.msg.data && recv.msg.data_len) {
fprintf(stderr, " data_len = %d\n", recv.msg.data_len);
fprintf(stderr, " data = %s\n",
buf2str(recv.msg.data,recv.msg.data_len));
}
}
}
}
/* save completion code */
rsp.ccode = recv.msg.data[0];
rsp.data_len = recv.msg.data_len - 1;
@ -319,6 +365,9 @@ ipmi_openipmi_send_cmd(struct ipmi_intf * intf, struct ipmi_rq * req)
rsp.data[recv.msg.data_len] = 0;
}
if (data != NULL)
free(data);
return &rsp;
}

View File

@ -89,10 +89,6 @@ struct ipmi_ipmb_addr {
short channel;
unsigned char slave_addr;
unsigned char lun;
#ifdef ENABLE_INTF_OPEN_DUAL_BRIDGE
short transit_channel;
unsigned char transit_slave_addr;
#endif
};
#define IPMI_IOC_MAGIC 'i'