Skip to content

Commit 059747c

Browse files
committed
ipmi: Add support for IPMB direct messages
An application has come up that has a device sitting right on the IPMB that would like to communicate with the BMC on the IPMB using normal IPMI commands. Sending these commands and handling the responses is easy enough, no modifications are needed to the IPMI infrastructure. But if this is an application that also needs to receive IPMB commands and respond, some way is needed to handle these incoming commands and send the responses. Currently, the IPMI message handler only sends commands to the interface and only receives responses from interface. This change extends the interface to receive commands/responses and send commands/responses. These are formatted differently in support of receiving/sending IPMB messages directly. Signed-off-by: Corey Minyard <[email protected]> Tested-by: Andrew Manley <[email protected]> Reviewed-by: Andrew Manley <[email protected]>
1 parent 1e4071f commit 059747c

File tree

3 files changed

+328
-33
lines changed

3 files changed

+328
-33
lines changed

drivers/char/ipmi/ipmi_msghandler.c

Lines changed: 255 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,11 @@ static int is_ipmb_bcast_addr(struct ipmi_addr *addr)
653653
return addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE;
654654
}
655655

656+
static int is_ipmb_direct_addr(struct ipmi_addr *addr)
657+
{
658+
return addr->addr_type == IPMI_IPMB_DIRECT_ADDR_TYPE;
659+
}
660+
656661
static void free_recv_msg_list(struct list_head *q)
657662
{
658663
struct ipmi_recv_msg *msg, *msg2;
@@ -805,6 +810,17 @@ ipmi_addr_equal(struct ipmi_addr *addr1, struct ipmi_addr *addr2)
805810
&& (ipmb_addr1->lun == ipmb_addr2->lun));
806811
}
807812

813+
if (is_ipmb_direct_addr(addr1)) {
814+
struct ipmi_ipmb_direct_addr *daddr1
815+
= (struct ipmi_ipmb_direct_addr *) addr1;
816+
struct ipmi_ipmb_direct_addr *daddr2
817+
= (struct ipmi_ipmb_direct_addr *) addr2;
818+
819+
return daddr1->slave_addr == daddr2->slave_addr &&
820+
daddr1->rq_lun == daddr2->rq_lun &&
821+
daddr1->rs_lun == daddr2->rs_lun;
822+
}
823+
808824
if (is_lan_addr(addr1)) {
809825
struct ipmi_lan_addr *lan_addr1
810826
= (struct ipmi_lan_addr *) addr1;
@@ -843,6 +859,23 @@ int ipmi_validate_addr(struct ipmi_addr *addr, int len)
843859
return 0;
844860
}
845861

862+
if (is_ipmb_direct_addr(addr)) {
863+
struct ipmi_ipmb_direct_addr *daddr = (void *) addr;
864+
865+
if (addr->channel != 0)
866+
return -EINVAL;
867+
if (len < sizeof(struct ipmi_ipmb_direct_addr))
868+
return -EINVAL;
869+
870+
if (daddr->slave_addr & 0x01)
871+
return -EINVAL;
872+
if (daddr->rq_lun >= 4)
873+
return -EINVAL;
874+
if (daddr->rs_lun >= 4)
875+
return -EINVAL;
876+
return 0;
877+
}
878+
846879
if (is_lan_addr(addr)) {
847880
if (len < sizeof(struct ipmi_lan_addr))
848881
return -EINVAL;
@@ -862,6 +895,9 @@ unsigned int ipmi_addr_length(int addr_type)
862895
|| (addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE))
863896
return sizeof(struct ipmi_ipmb_addr);
864897

898+
if (addr_type == IPMI_IPMB_DIRECT_ADDR_TYPE)
899+
return sizeof(struct ipmi_ipmb_direct_addr);
900+
865901
if (addr_type == IPMI_LAN_ADDR_TYPE)
866902
return sizeof(struct ipmi_lan_addr);
867903

@@ -2052,6 +2088,58 @@ static int i_ipmi_req_ipmb(struct ipmi_smi *intf,
20522088
return rv;
20532089
}
20542090

2091+
static int i_ipmi_req_ipmb_direct(struct ipmi_smi *intf,
2092+
struct ipmi_addr *addr,
2093+
long msgid,
2094+
struct kernel_ipmi_msg *msg,
2095+
struct ipmi_smi_msg *smi_msg,
2096+
struct ipmi_recv_msg *recv_msg,
2097+
unsigned char source_lun)
2098+
{
2099+
struct ipmi_ipmb_direct_addr *daddr;
2100+
bool is_cmd = !(recv_msg->msg.netfn & 0x1);
2101+
2102+
if (!(intf->handlers->flags & IPMI_SMI_CAN_HANDLE_IPMB_DIRECT))
2103+
return -EAFNOSUPPORT;
2104+
2105+
/* Responses must have a completion code. */
2106+
if (!is_cmd && msg->data_len < 1) {
2107+
ipmi_inc_stat(intf, sent_invalid_commands);
2108+
return -EINVAL;
2109+
}
2110+
2111+
if ((msg->data_len + 4) > IPMI_MAX_MSG_LENGTH) {
2112+
ipmi_inc_stat(intf, sent_invalid_commands);
2113+
return -EMSGSIZE;
2114+
}
2115+
2116+
daddr = (struct ipmi_ipmb_direct_addr *) addr;
2117+
if (daddr->rq_lun > 3 || daddr->rs_lun > 3) {
2118+
ipmi_inc_stat(intf, sent_invalid_commands);
2119+
return -EINVAL;
2120+
}
2121+
2122+
smi_msg->type = IPMI_SMI_MSG_TYPE_IPMB_DIRECT;
2123+
smi_msg->msgid = msgid;
2124+
2125+
if (is_cmd) {
2126+
smi_msg->data[0] = msg->netfn << 2 | daddr->rs_lun;
2127+
smi_msg->data[2] = recv_msg->msgid << 2 | daddr->rq_lun;
2128+
} else {
2129+
smi_msg->data[0] = msg->netfn << 2 | daddr->rq_lun;
2130+
smi_msg->data[2] = recv_msg->msgid << 2 | daddr->rs_lun;
2131+
}
2132+
smi_msg->data[1] = daddr->slave_addr;
2133+
smi_msg->data[3] = msg->cmd;
2134+
2135+
memcpy(smi_msg->data + 4, msg->data, msg->data_len);
2136+
smi_msg->data_size = msg->data_len + 4;
2137+
2138+
smi_msg->user_data = recv_msg;
2139+
2140+
return 0;
2141+
}
2142+
20552143
static int i_ipmi_req_lan(struct ipmi_smi *intf,
20562144
struct ipmi_addr *addr,
20572145
long msgid,
@@ -2241,6 +2329,9 @@ static int i_ipmi_request(struct ipmi_user *user,
22412329
rv = i_ipmi_req_ipmb(intf, addr, msgid, msg, smi_msg, recv_msg,
22422330
source_address, source_lun,
22432331
retries, retry_time_ms);
2332+
} else if (is_ipmb_direct_addr(addr)) {
2333+
rv = i_ipmi_req_ipmb_direct(intf, addr, msgid, msg, smi_msg,
2334+
recv_msg, source_lun);
22442335
} else if (is_lan_addr(addr)) {
22452336
rv = i_ipmi_req_lan(intf, addr, msgid, msg, smi_msg, recv_msg,
22462337
source_lun, retries, retry_time_ms);
@@ -3802,6 +3893,123 @@ static int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf,
38023893
return rv;
38033894
}
38043895

3896+
static int handle_ipmb_direct_rcv_cmd(struct ipmi_smi *intf,
3897+
struct ipmi_smi_msg *msg)
3898+
{
3899+
struct cmd_rcvr *rcvr;
3900+
int rv = 0;
3901+
struct ipmi_user *user = NULL;
3902+
struct ipmi_ipmb_direct_addr *daddr;
3903+
struct ipmi_recv_msg *recv_msg;
3904+
unsigned char netfn = msg->rsp[0] >> 2;
3905+
unsigned char cmd = msg->rsp[3];
3906+
3907+
rcu_read_lock();
3908+
/* We always use channel 0 for direct messages. */
3909+
rcvr = find_cmd_rcvr(intf, netfn, cmd, 0);
3910+
if (rcvr) {
3911+
user = rcvr->user;
3912+
kref_get(&user->refcount);
3913+
} else
3914+
user = NULL;
3915+
rcu_read_unlock();
3916+
3917+
if (user == NULL) {
3918+
/* We didn't find a user, deliver an error response. */
3919+
ipmi_inc_stat(intf, unhandled_commands);
3920+
3921+
msg->data[0] = ((netfn + 1) << 2) | (msg->rsp[4] & 0x3);
3922+
msg->data[1] = msg->rsp[2];
3923+
msg->data[2] = msg->rsp[4] & ~0x3;
3924+
msg->data[3] = cmd;
3925+
msg->data[4] = IPMI_INVALID_CMD_COMPLETION_CODE;
3926+
msg->data_size = 5;
3927+
3928+
rcu_read_lock();
3929+
if (!intf->in_shutdown) {
3930+
smi_send(intf, intf->handlers, msg, 0);
3931+
/*
3932+
* We used the message, so return the value
3933+
* that causes it to not be freed or
3934+
* queued.
3935+
*/
3936+
rv = -1;
3937+
}
3938+
rcu_read_unlock();
3939+
} else {
3940+
recv_msg = ipmi_alloc_recv_msg();
3941+
if (!recv_msg) {
3942+
/*
3943+
* We couldn't allocate memory for the
3944+
* message, so requeue it for handling
3945+
* later.
3946+
*/
3947+
rv = 1;
3948+
kref_put(&user->refcount, free_user);
3949+
} else {
3950+
/* Extract the source address from the data. */
3951+
daddr = (struct ipmi_ipmb_direct_addr *)&recv_msg->addr;
3952+
daddr->addr_type = IPMI_IPMB_DIRECT_ADDR_TYPE;
3953+
daddr->channel = 0;
3954+
daddr->slave_addr = msg->rsp[1];
3955+
daddr->rs_lun = msg->rsp[0] & 3;
3956+
daddr->rq_lun = msg->rsp[2] & 3;
3957+
3958+
/*
3959+
* Extract the rest of the message information
3960+
* from the IPMB header.
3961+
*/
3962+
recv_msg->user = user;
3963+
recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
3964+
recv_msg->msgid = (msg->rsp[2] >> 2);
3965+
recv_msg->msg.netfn = msg->rsp[0] >> 2;
3966+
recv_msg->msg.cmd = msg->rsp[3];
3967+
recv_msg->msg.data = recv_msg->msg_data;
3968+
3969+
recv_msg->msg.data_len = msg->rsp_size - 4;
3970+
memcpy(recv_msg->msg_data, msg->rsp + 4,
3971+
msg->rsp_size - 4);
3972+
if (deliver_response(intf, recv_msg))
3973+
ipmi_inc_stat(intf, unhandled_commands);
3974+
else
3975+
ipmi_inc_stat(intf, handled_commands);
3976+
}
3977+
}
3978+
3979+
return rv;
3980+
}
3981+
3982+
static int handle_ipmb_direct_rcv_rsp(struct ipmi_smi *intf,
3983+
struct ipmi_smi_msg *msg)
3984+
{
3985+
struct ipmi_recv_msg *recv_msg;
3986+
struct ipmi_ipmb_direct_addr *daddr;
3987+
3988+
recv_msg = (struct ipmi_recv_msg *) msg->user_data;
3989+
if (recv_msg == NULL) {
3990+
dev_warn(intf->si_dev,
3991+
"IPMI message received with no owner. This could be because of a malformed message, or because of a hardware error. Contact your hardware vendor for assistance.\n");
3992+
return 0;
3993+
}
3994+
3995+
recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
3996+
recv_msg->msgid = msg->msgid;
3997+
daddr = (struct ipmi_ipmb_direct_addr *) &recv_msg->addr;
3998+
daddr->addr_type = IPMI_IPMB_DIRECT_ADDR_TYPE;
3999+
daddr->channel = 0;
4000+
daddr->slave_addr = msg->rsp[1];
4001+
daddr->rq_lun = msg->rsp[0] & 3;
4002+
daddr->rs_lun = msg->rsp[2] & 3;
4003+
recv_msg->msg.netfn = msg->rsp[0] >> 2;
4004+
recv_msg->msg.cmd = msg->rsp[3];
4005+
memcpy(recv_msg->msg_data, &msg->rsp[4], msg->rsp_size - 4);
4006+
recv_msg->msg.data = recv_msg->msg_data;
4007+
recv_msg->msg.data_len = msg->rsp_size - 4;
4008+
deliver_local_response(intf, recv_msg);
4009+
4010+
return 0;
4011+
}
4012+
38054013
static int handle_lan_get_msg_rsp(struct ipmi_smi *intf,
38064014
struct ipmi_smi_msg *msg)
38074015
{
@@ -4227,18 +4435,40 @@ static int handle_bmc_rsp(struct ipmi_smi *intf,
42274435
static int handle_one_recv_msg(struct ipmi_smi *intf,
42284436
struct ipmi_smi_msg *msg)
42294437
{
4230-
int requeue;
4438+
int requeue = 0;
42314439
int chan;
4440+
unsigned char cc;
4441+
bool is_cmd = !((msg->rsp[0] >> 2) & 1);
42324442

42334443
pr_debug("Recv: %*ph\n", msg->rsp_size, msg->rsp);
42344444

4235-
if ((msg->data_size >= 2)
4445+
if (msg->rsp_size < 2) {
4446+
/* Message is too small to be correct. */
4447+
dev_warn(intf->si_dev,
4448+
"BMC returned too small a message for netfn %x cmd %x, got %d bytes\n",
4449+
(msg->data[0] >> 2) | 1, msg->data[1], msg->rsp_size);
4450+
4451+
return_unspecified:
4452+
/* Generate an error response for the message. */
4453+
msg->rsp[0] = msg->data[0] | (1 << 2);
4454+
msg->rsp[1] = msg->data[1];
4455+
msg->rsp[2] = IPMI_ERR_UNSPECIFIED;
4456+
msg->rsp_size = 3;
4457+
} else if (msg->type == IPMI_SMI_MSG_TYPE_IPMB_DIRECT) {
4458+
/* commands must have at least 3 bytes, responses 4. */
4459+
if (is_cmd && (msg->rsp_size < 3)) {
4460+
ipmi_inc_stat(intf, invalid_commands);
4461+
goto out;
4462+
}
4463+
if (!is_cmd && (msg->rsp_size < 4))
4464+
goto return_unspecified;
4465+
} else if ((msg->data_size >= 2)
42364466
&& (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2))
42374467
&& (msg->data[1] == IPMI_SEND_MSG_CMD)
42384468
&& (msg->user_data == NULL)) {
42394469

42404470
if (intf->in_shutdown)
4241-
goto free_msg;
4471+
goto out;
42424472

42434473
/*
42444474
* This is the local response to a command send, start
@@ -4273,21 +4503,6 @@ static int handle_one_recv_msg(struct ipmi_smi *intf,
42734503
} else
42744504
/* The message was sent, start the timer. */
42754505
intf_start_seq_timer(intf, msg->msgid);
4276-
free_msg:
4277-
requeue = 0;
4278-
goto out;
4279-
4280-
} else if (msg->rsp_size < 2) {
4281-
/* Message is too small to be correct. */
4282-
dev_warn(intf->si_dev,
4283-
"BMC returned too small a message for netfn %x cmd %x, got %d bytes\n",
4284-
(msg->data[0] >> 2) | 1, msg->data[1], msg->rsp_size);
4285-
4286-
/* Generate an error response for the message. */
4287-
msg->rsp[0] = msg->data[0] | (1 << 2);
4288-
msg->rsp[1] = msg->data[1];
4289-
msg->rsp[2] = IPMI_ERR_UNSPECIFIED;
4290-
msg->rsp_size = 3;
42914506
} else if (((msg->rsp[0] >> 2) != ((msg->data[0] >> 2) | 1))
42924507
|| (msg->rsp[1] != msg->data[1])) {
42934508
/*
@@ -4299,39 +4514,46 @@ static int handle_one_recv_msg(struct ipmi_smi *intf,
42994514
(msg->data[0] >> 2) | 1, msg->data[1],
43004515
msg->rsp[0] >> 2, msg->rsp[1]);
43014516

4302-
/* Generate an error response for the message. */
4303-
msg->rsp[0] = msg->data[0] | (1 << 2);
4304-
msg->rsp[1] = msg->data[1];
4305-
msg->rsp[2] = IPMI_ERR_UNSPECIFIED;
4306-
msg->rsp_size = 3;
4517+
goto return_unspecified;
43074518
}
43084519

4309-
if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
4310-
&& (msg->rsp[1] == IPMI_SEND_MSG_CMD)
4311-
&& (msg->user_data != NULL)) {
4520+
if (msg->type == IPMI_SMI_MSG_TYPE_IPMB_DIRECT) {
4521+
if ((msg->data[0] >> 2) & 1) {
4522+
/* It's a response to a sent response. */
4523+
chan = 0;
4524+
cc = msg->rsp[4];
4525+
goto process_response_response;
4526+
}
4527+
if (is_cmd)
4528+
requeue = handle_ipmb_direct_rcv_cmd(intf, msg);
4529+
else
4530+
requeue = handle_ipmb_direct_rcv_rsp(intf, msg);
4531+
} else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
4532+
&& (msg->rsp[1] == IPMI_SEND_MSG_CMD)
4533+
&& (msg->user_data != NULL)) {
43124534
/*
43134535
* It's a response to a response we sent. For this we
43144536
* deliver a send message response to the user.
43154537
*/
4316-
struct ipmi_recv_msg *recv_msg = msg->user_data;
4317-
4318-
requeue = 0;
4319-
if (msg->rsp_size < 2)
4320-
/* Message is too small to be correct. */
4321-
goto out;
4538+
struct ipmi_recv_msg *recv_msg;
43224539

43234540
chan = msg->data[2] & 0x0f;
43244541
if (chan >= IPMI_MAX_CHANNELS)
43254542
/* Invalid channel number */
43264543
goto out;
4544+
cc = msg->rsp[2];
43274545

4546+
process_response_response:
4547+
recv_msg = msg->user_data;
4548+
4549+
requeue = 0;
43284550
if (!recv_msg)
43294551
goto out;
43304552

43314553
recv_msg->recv_type = IPMI_RESPONSE_RESPONSE_TYPE;
43324554
recv_msg->msg.data = recv_msg->msg_data;
4555+
recv_msg->msg_data[0] = cc;
43334556
recv_msg->msg.data_len = 1;
4334-
recv_msg->msg_data[0] = msg->rsp[2];
43354557
deliver_local_response(intf, recv_msg);
43364558
} else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
43374559
&& (msg->rsp[1] == IPMI_GET_MSG_CMD)) {

0 commit comments

Comments
 (0)