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:
parent
a4d6d3b891
commit
18db96dbfd
@ -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,24 +304,11 @@ 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);
|
||||
}
|
||||
IPMI_UNLOCK(sc);
|
||||
|
||||
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);
|
||||
@ -411,6 +325,49 @@ ipmi_ioctl(struct cdev *cdev, u_long cmd, caddr_t data,
|
||||
if (error)
|
||||
return (error);
|
||||
break;
|
||||
}
|
||||
|
||||
/* 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(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 != 0)
|
||||
return (error);
|
||||
break;
|
||||
#ifdef IPMICTL_SEND_COMMAND_32
|
||||
case IPMICTL_RECEIVE_MSG_TRUNC_32:
|
||||
case IPMICTL_RECEIVE_MSG_32:
|
||||
@ -427,22 +384,34 @@ 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);
|
||||
}
|
||||
|
||||
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
|
||||
@ -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)
|
||||
{
|
||||
|
@ -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__ */
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user