ipmi: correctly handle ipmb requests

Handle IPMB requests using SEND_MSG (sent as driver request as we do not
need to return anything back to userland for this) and GET_MSG (sent as
usual request so we can return the data for RECEIVE_MSG ioctl) pair.

This fixes fetching complete sensor data from boards (e.g. HP ProLiant
DL380 Gen10).

Reviewed by:	philip
MFC after:	1 week
Differential Revision: https://reviews.freebsd.org/D35605
This commit is contained in:
Yuri 2022-07-04 12:55:18 +08:00 committed by Philip Paeps
parent a4d6d3b891
commit 18db96dbfd
3 changed files with 86 additions and 118 deletions

View File

@ -76,12 +76,6 @@ __FBSDID("$FreeBSD$");
IPMI_INIT_DRIVER_REQUEST((req), (addr), (cmd), (reqlen), \
(replylen))
#ifdef IPMB
static int ipmi_ipmb_checksum(u_char, int);
static int ipmi_ipmb_send_message(device_t, u_char, u_char, u_char,
u_char, u_char, int)
#endif
static d_ioctl_t ipmi_ioctl;
static d_poll_t ipmi_poll;
static d_open_t ipmi_open;
@ -245,83 +239,16 @@ ipmi_dtor(void *arg)
free(dev, M_IPMI);
}
#ifdef IPMB
static int
static u_char
ipmi_ipmb_checksum(u_char *data, int len)
{
u_char sum = 0;
for (; len; len--) {
for (; len; len--)
sum += *data++;
}
return (-sum);
}
/* XXX: Needs work */
static int
ipmi_ipmb_send_message(device_t dev, u_char channel, u_char netfn,
u_char command, u_char seq, u_char *data, int data_len)
{
struct ipmi_softc *sc = device_get_softc(dev);
struct ipmi_request *req;
u_char slave_addr = 0x52;
int error;
IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0),
IPMI_SEND_MSG, data_len + 8, 0);
req->ir_request[0] = channel;
req->ir_request[1] = slave_addr;
req->ir_request[2] = IPMI_ADDR(netfn, 0);
req->ir_request[3] = ipmi_ipmb_checksum(&req->ir_request[1], 2);
req->ir_request[4] = sc->ipmi_address;
req->ir_request[5] = IPMI_ADDR(seq, sc->ipmi_lun);
req->ir_request[6] = command;
bcopy(data, &req->ir_request[7], data_len);
temp[data_len + 7] = ipmi_ipmb_checksum(&req->ir_request[4],
data_len + 3);
ipmi_submit_driver_request(sc, req);
error = req->ir_error;
return (error);
}
static int
ipmi_handle_attn(struct ipmi_softc *sc)
{
struct ipmi_request *req;
int error;
device_printf(sc->ipmi_dev, "BMC has a message\n");
IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0),
IPMI_GET_MSG_FLAGS, 0, 1);
ipmi_submit_driver_request(sc, req);
if (req->ir_error == 0 && req->ir_compcode == 0) {
if (req->ir_reply[0] & IPMI_MSG_BUFFER_FULL) {
device_printf(sc->ipmi_dev, "message buffer full");
}
if (req->ir_reply[0] & IPMI_WDT_PRE_TIMEOUT) {
device_printf(sc->ipmi_dev,
"watchdog about to go off");
}
if (req->ir_reply[0] & IPMI_MSG_AVAILABLE) {
IPMI_ALLOC_DRIVER_REQUEST(req,
IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_MSG, 0,
16);
device_printf(sc->ipmi_dev, "throw out message ");
dump_buf(temp, 16);
}
}
error = req->ir_error;
return (error);
}
#endif
static int
ipmi_ioctl(struct cdev *cdev, u_long cmd, caddr_t data,
int flags, struct thread *td)
@ -377,38 +304,68 @@ ipmi_ioctl(struct cdev *cdev, u_long cmd, caddr_t data,
case IPMICTL_SEND_COMMAND_32:
#endif
case IPMICTL_SEND_COMMAND:
/*
* XXX: Need to add proper handling of this.
*/
error = copyin(req->addr, &addr, sizeof(addr));
if (error)
return (error);
IPMI_LOCK(sc);
/* clear out old stuff in queue of stuff done */
/* XXX: This seems odd. */
while ((kreq = TAILQ_FIRST(&dev->ipmi_completed_requests))) {
TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq,
ir_link);
dev->ipmi_requests--;
ipmi_free_request(kreq);
if (addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {
kreq = ipmi_alloc_request(dev, req->msgid,
IPMI_ADDR(req->msg.netfn, 0), req->msg.cmd,
req->msg.data_len, IPMI_MAX_RX);
error = copyin(req->msg.data, kreq->ir_request,
req->msg.data_len);
if (error) {
ipmi_free_request(kreq);
return (error);
}
IPMI_LOCK(sc);
dev->ipmi_requests++;
error = sc->ipmi_enqueue_request(sc, kreq);
IPMI_UNLOCK(sc);
if (error)
return (error);
break;
}
IPMI_UNLOCK(sc);
/* Special processing for IPMB commands */
struct ipmi_ipmb_addr *iaddr = (struct ipmi_ipmb_addr *)&addr;
IPMI_ALLOC_DRIVER_REQUEST(kreq, IPMI_ADDR(IPMI_APP_REQUEST, 0),
IPMI_SEND_MSG, req->msg.data_len + 8, IPMI_MAX_RX);
/* Construct the SEND MSG header */
kreq->ir_request[0] = iaddr->channel;
kreq->ir_request[1] = iaddr->slave_addr;
kreq->ir_request[2] = IPMI_ADDR(req->msg.netfn, iaddr->lun);
kreq->ir_request[3] =
ipmi_ipmb_checksum(&kreq->ir_request[1], 2);
kreq->ir_request[4] = dev->ipmi_address;
kreq->ir_request[5] = IPMI_ADDR(0, dev->ipmi_lun);
kreq->ir_request[6] = req->msg.cmd;
/* Copy the message data */
if (req->msg.data_len > 0) {
error = copyin(req->msg.data, &kreq->ir_request[7],
req->msg.data_len);
if (error != 0)
return (error);
}
kreq->ir_request[req->msg.data_len + 7] =
ipmi_ipmb_checksum(&kreq->ir_request[4],
req->msg.data_len + 3);
error = ipmi_submit_driver_request(sc, kreq, MAX_TIMEOUT);
if (error != 0)
return (error);
kreq = ipmi_alloc_request(dev, req->msgid,
IPMI_ADDR(req->msg.netfn, 0), req->msg.cmd,
req->msg.data_len, IPMI_MAX_RX);
error = copyin(req->msg.data, kreq->ir_request,
req->msg.data_len);
if (error) {
ipmi_free_request(kreq);
return (error);
}
IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_MSG,
0, IPMI_MAX_RX);
kreq->ir_ipmb = true;
kreq->ir_ipmb_addr = IPMI_ADDR(req->msg.netfn, 0);
kreq->ir_ipmb_command = req->msg.cmd;
IPMI_LOCK(sc);
dev->ipmi_requests++;
error = sc->ipmi_enqueue_request(sc, kreq);
IPMI_UNLOCK(sc);
if (error)
if (error != 0)
return (error);
break;
#ifdef IPMICTL_SEND_COMMAND_32
@ -427,26 +384,38 @@ ipmi_ioctl(struct cdev *cdev, u_long cmd, caddr_t data,
IPMI_UNLOCK(sc);
return (EAGAIN);
}
addr.channel = IPMI_BMC_CHANNEL;
/* XXX */
recv->recv_type = IPMI_RESPONSE_RECV_TYPE;
recv->msgid = kreq->ir_msgid;
recv->msg.netfn = IPMI_REPLY_ADDR(kreq->ir_addr) >> 2;
recv->msg.cmd = kreq->ir_command;
error = kreq->ir_error;
if (error) {
if (kreq->ir_error != 0) {
TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq,
ir_link);
dev->ipmi_requests--;
IPMI_UNLOCK(sc);
ipmi_free_request(kreq);
return (error);
return (kreq->ir_error);
}
len = kreq->ir_replylen + 1;
recv->recv_type = IPMI_RESPONSE_RECV_TYPE;
recv->msgid = kreq->ir_msgid;
if (kreq->ir_ipmb) {
addr.channel = IPMI_IPMB_CHANNEL;
recv->msg.netfn =
IPMI_REPLY_ADDR(kreq->ir_ipmb_addr) >> 2;
recv->msg.cmd = kreq->ir_ipmb_command;
/* Get the compcode of response */
kreq->ir_compcode = kreq->ir_reply[6];
/* Move the reply head past response header */
kreq->ir_reply += 7;
len = kreq->ir_replylen - 7;
} else {
addr.channel = IPMI_BMC_CHANNEL;
recv->msg.netfn = IPMI_REPLY_ADDR(kreq->ir_addr) >> 2;
recv->msg.cmd = kreq->ir_command;
len = kreq->ir_replylen + 1;
}
if (recv->msg.data_len < len &&
(cmd == IPMICTL_RECEIVE_MSG
#ifdef IPMICTL_RECEIVE_MSG_32
|| cmd == IPMICTL_RECEIVE_MSG_32
|| cmd == IPMICTL_RECEIVE_MSG_32
#endif
)) {
IPMI_UNLOCK(sc);
@ -521,7 +490,7 @@ ipmi_ioctl(struct cdev *cdev, u_long cmd, caddr_t data,
* Request management.
*/
static __inline void
__inline void
ipmi_init_request(struct ipmi_request *req, struct ipmi_device *dev, long msgid,
uint8_t addr, uint8_t command, size_t requestlen, size_t replylen)
{

View File

@ -54,6 +54,9 @@ struct ipmi_request {
uint8_t ir_addr;
uint8_t ir_command;
uint8_t ir_compcode;
bool ir_ipmb;
uint8_t ir_ipmb_addr;
uint8_t ir_ipmb_command;
};
#define MAX_RES 3
@ -128,10 +131,6 @@ struct ipmi_softc {
#define ipmi_ssif_smbus_address _iface.ssif.smbus_address
#define ipmi_ssif_smbus _iface.ssif.smbus
struct ipmi_ipmb {
u_char foo;
};
#define KCS_MODE 0x01
#define SMIC_MODE 0x02
#define BT_MODE 0x03
@ -230,6 +229,8 @@ int ipmi_detach(device_t);
void ipmi_release_resources(device_t);
/* Manage requests. */
void ipmi_init_request(struct ipmi_request *, struct ipmi_device *, long,
uint8_t, uint8_t, size_t, size_t);
struct ipmi_request *ipmi_alloc_request(struct ipmi_device *, long, uint8_t,
uint8_t, size_t, size_t);
void ipmi_complete_request(struct ipmi_softc *, struct ipmi_request *);
@ -251,10 +252,6 @@ int ipmi_kcs_probe_align(struct ipmi_softc *);
int ipmi_smic_attach(struct ipmi_softc *);
int ipmi_ssif_attach(struct ipmi_softc *, device_t, int);
#ifdef IPMB
int ipmi_handle_attn(struct ipmi_softc *);
#endif
extern int ipmi_attached;
#endif /* !__IPMIVARS_H__ */

View File

@ -33,9 +33,11 @@
#define IPMI_MAX_ADDR_SIZE 0x20
#define IPMI_MAX_RX 1024
#define IPMI_BMC_SLAVE_ADDR 0x20 /* Linux Default slave address */
#define IPMI_BMC_CHANNEL 0x0f /* Linux BMC channel */
#define IPMI_BMC_CHANNEL 0x0f /* Linux BMC channel */
#define IPMI_IPMB_CHANNEL 0x00
#define IPMI_BMC_SLAVE_ADDR 0x20 /* Linux Default slave address */
#define IPMI_BMC_SMS_LUN 0x02
#define IPMI_SYSTEM_INTERFACE_ADDR_TYPE 0x0c