git: 18db96dbfd4a - main - ipmi: correctly handle ipmb requests

From: Philip Paeps <philip_at_FreeBSD.org>
Date: Mon, 04 Jul 2022 05:33:19 UTC
The branch main has been updated by philip:

URL: https://cgit.FreeBSD.org/src/commit/?id=18db96dbfd4a09063a0abcefd51fa8d2aeb115d6

commit 18db96dbfd4a09063a0abcefd51fa8d2aeb115d6
Author:     Yuri <yuri@aetern.org>
AuthorDate: 2022-07-04 04:55:18 +0000
Commit:     Philip Paeps <philip@FreeBSD.org>
CommitDate: 2022-07-04 05:00:42 +0000

    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
---
 sys/dev/ipmi/ipmi.c     | 185 ++++++++++++++++++++----------------------------
 sys/dev/ipmi/ipmivars.h |  13 ++--
 sys/sys/ipmi.h          |   4 +-
 3 files changed, 85 insertions(+), 117 deletions(-)

diff --git a/sys/dev/ipmi/ipmi.c b/sys/dev/ipmi/ipmi.c
index c4fb804987c0..775734d3e1aa 100644
--- a/sys/dev/ipmi/ipmi.c
+++ b/sys/dev/ipmi/ipmi.c
@@ -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);
 
-		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);
+		/* 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)
+		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)
 {
diff --git a/sys/dev/ipmi/ipmivars.h b/sys/dev/ipmi/ipmivars.h
index a7ab23336ff5..b0548ee3d7c3 100644
--- a/sys/dev/ipmi/ipmivars.h
+++ b/sys/dev/ipmi/ipmivars.h
@@ -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__ */
diff --git a/sys/sys/ipmi.h b/sys/sys/ipmi.h
index 3c805bac099d..9b46d1869101 100644
--- a/sys/sys/ipmi.h
+++ b/sys/sys/ipmi.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_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