ipmi.c revision 182322
190075Sobrien/*-
290075Sobrien * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
3169699Skan * All rights reserved.
490075Sobrien *
590075Sobrien * Redistribution and use in source and binary forms, with or without
690075Sobrien * modification, are permitted provided that the following conditions
790075Sobrien * are met:
890075Sobrien * 1. Redistributions of source code must retain the above copyright
990075Sobrien *    notice, this list of conditions and the following disclaimer.
1090075Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1190075Sobrien *    notice, this list of conditions and the following disclaimer in the
1290075Sobrien *    documentation and/or other materials provided with the distribution.
1390075Sobrien *
1490075Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1590075Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1690075Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1790075Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1890075Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19169699Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20169699Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2190075Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2290075Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2390075Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2490075Sobrien * SUCH DAMAGE.
2590075Sobrien */
2690075Sobrien
27132729Skan#include <sys/cdefs.h>
2890075Sobrien__FBSDID("$FreeBSD: head/sys/dev/ipmi/ipmi.c 182322 2008-08-28 02:13:53Z jhb $");
2990075Sobrien
30169699Skan#include <sys/param.h>
3190075Sobrien#include <sys/systm.h>
3290075Sobrien#include <sys/bus.h>
3390075Sobrien#include <sys/condvar.h>
34169699Skan#include <sys/conf.h>
35169699Skan#include <sys/kernel.h>
36169699Skan#include <sys/malloc.h>
37169699Skan#include <sys/module.h>
3890075Sobrien#include <sys/poll.h>
3990075Sobrien#include <sys/rman.h>
4090075Sobrien#include <sys/selinfo.h>
41117406Skan#include <sys/sysctl.h>
4290075Sobrien#include <sys/watchdog.h>
4390075Sobrien
4490075Sobrien#ifdef LOCAL_MODULE
4590075Sobrien#include <ipmi.h>
4690075Sobrien#include <ipmivars.h>
4790075Sobrien#else
4890075Sobrien#include <sys/ipmi.h>
4990075Sobrien#include <dev/ipmi/ipmivars.h>
5090075Sobrien#endif
5190075Sobrien
5290075Sobrien#ifdef IPMB
5390075Sobrienstatic int ipmi_ipmb_checksum(u_char, int);
5490075Sobrienstatic int ipmi_ipmb_send_message(device_t, u_char, u_char, u_char,
5590075Sobrien     u_char, u_char, int)
56132729Skan#endif
5790075Sobrien
5890075Sobrienstatic d_ioctl_t ipmi_ioctl;
5990075Sobrienstatic d_poll_t ipmi_poll;
6090075Sobrienstatic d_open_t ipmi_open;
6190075Sobrienstatic void ipmi_dtor(void *arg);
6290075Sobrien
6390075Sobrienint ipmi_attached = 0;
6490075Sobrien
6590075Sobrienstatic int on = 1;
6690075SobrienSYSCTL_NODE(_hw, OID_AUTO, ipmi, CTLFLAG_RD, 0, "IPMI driver parameters");
6790075SobrienSYSCTL_INT(_hw_ipmi, OID_AUTO, on, CTLFLAG_RW,
6890075Sobrien	&on, 0, "");
6990075Sobrien
7090075Sobrienstatic struct cdevsw ipmi_cdevsw = {
7190075Sobrien	.d_version =    D_VERSION,
7290075Sobrien	.d_open =	ipmi_open,
73169699Skan	.d_ioctl =	ipmi_ioctl,
74169699Skan	.d_poll =	ipmi_poll,
75169699Skan	.d_name =	"ipmi",
7690075Sobrien};
7790075Sobrien
7890075SobrienMALLOC_DEFINE(M_IPMI, "ipmi", "ipmi");
7990075Sobrien
8090075Sobrienstatic int
8190075Sobrienipmi_open(struct cdev *cdev, int flags, int fmt, struct thread *td)
8290075Sobrien{
8390075Sobrien	struct ipmi_device *dev;
8490075Sobrien	struct ipmi_softc *sc;
85169699Skan	int error;
86169699Skan
87169699Skan	if (!on)
88132729Skan		return (ENOENT);
8990075Sobrien
9090075Sobrien	/* Initialize the per file descriptor data. */
9190075Sobrien	dev = malloc(sizeof(struct ipmi_device), M_IPMI, M_WAITOK | M_ZERO);
9290075Sobrien	error = devfs_set_cdevpriv(dev, ipmi_dtor);
93117406Skan	if (error) {
94169699Skan		free(dev, M_IPMI);
9590075Sobrien		return (error);
9690075Sobrien	}
97169699Skan
98132729Skan	sc = cdev->si_drv1;
99132729Skan	TAILQ_INIT(&dev->ipmi_completed_requests);
100132729Skan	dev->ipmi_address = IPMI_BMC_SLAVE_ADDR;
10190075Sobrien	dev->ipmi_lun = IPMI_BMC_SMS_LUN;
10290075Sobrien	dev->ipmi_softc = sc;
10390075Sobrien	IPMI_LOCK(sc);
10490075Sobrien	sc->ipmi_opened++;
10590075Sobrien	IPMI_UNLOCK(sc);
10690075Sobrien
10790075Sobrien	return (0);
10890075Sobrien}
10990075Sobrien
11090075Sobrienstatic int
11190075Sobrienipmi_poll(struct cdev *cdev, int poll_events, struct thread *td)
11290075Sobrien{
11390075Sobrien	struct ipmi_device *dev;
11490075Sobrien	struct ipmi_softc *sc;
115169699Skan	int revents = 0;
116169699Skan
11790075Sobrien	if (devfs_get_cdevpriv((void **)&dev))
11890075Sobrien		return (0);
119169699Skan
120169699Skan	sc = cdev->si_drv1;
12190075Sobrien	IPMI_LOCK(sc);
12290075Sobrien	if (poll_events & (POLLIN | POLLRDNORM)) {
12390075Sobrien		if (!TAILQ_EMPTY(&dev->ipmi_completed_requests))
124169699Skan		    revents |= poll_events & (POLLIN | POLLRDNORM);
12590075Sobrien		if (dev->ipmi_requests == 0)
12690075Sobrien		    revents |= POLLERR;
12790075Sobrien	}
12890075Sobrien
12990075Sobrien	if (revents == 0) {
13090075Sobrien		if (poll_events & (POLLIN | POLLRDNORM))
13190075Sobrien			selrecord(td, &dev->ipmi_select);
13290075Sobrien	}
13390075Sobrien	IPMI_UNLOCK(sc);
13490075Sobrien
13590075Sobrien	return (revents);
13690075Sobrien}
13790075Sobrien
13890075Sobrienstatic void
13990075Sobrienipmi_purge_completed_requests(struct ipmi_device *dev)
14090075Sobrien{
14190075Sobrien	struct ipmi_request *req;
14290075Sobrien
14390075Sobrien	while (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) {
14490075Sobrien		req = TAILQ_FIRST(&dev->ipmi_completed_requests);
14590075Sobrien		TAILQ_REMOVE(&dev->ipmi_completed_requests, req, ir_link);
14690075Sobrien		dev->ipmi_requests--;
14790075Sobrien		ipmi_free_request(req);
148132729Skan	}
149132729Skan}
150132729Skan
151132729Skanstatic void
15290075Sobrienipmi_dtor(void *arg)
15390075Sobrien{
15490075Sobrien	struct ipmi_request *req, *nreq;
15590075Sobrien	struct ipmi_device *dev;
15690075Sobrien	struct ipmi_softc *sc;
15790075Sobrien
15890075Sobrien	dev = arg;
159169699Skan	sc = dev->ipmi_softc;
16090075Sobrien
16190075Sobrien	IPMI_LOCK(sc);
162169699Skan	if (dev->ipmi_requests) {
163169699Skan		/* Throw away any pending requests for this device. */
16490075Sobrien		TAILQ_FOREACH_SAFE(req, &sc->ipmi_pending_requests, ir_link,
16590075Sobrien		    nreq) {
16690075Sobrien			if (req->ir_owner == dev) {
16790075Sobrien				TAILQ_REMOVE(&sc->ipmi_pending_requests, req,
16890075Sobrien				    ir_link);
16990075Sobrien				dev->ipmi_requests--;
17090075Sobrien				ipmi_free_request(req);
171117406Skan			}
17290075Sobrien		}
17390075Sobrien
174132729Skan		/* Throw away any pending completed requests for this device. */
17590075Sobrien		ipmi_purge_completed_requests(dev);
17690075Sobrien
17790075Sobrien		/*
17890075Sobrien		 * If we still have outstanding requests, they must be stuck
17990075Sobrien		 * in an interface driver, so wait for those to drain.
18090075Sobrien		 */
18190075Sobrien		dev->ipmi_closing = 1;
18290075Sobrien		while (dev->ipmi_requests > 0) {
18390075Sobrien			msleep(&dev->ipmi_requests, &sc->ipmi_lock, PWAIT,
18490075Sobrien			    "ipmidrain", 0);
18590075Sobrien			ipmi_purge_completed_requests(dev);
18690075Sobrien		}
18790075Sobrien	}
188132729Skan	sc->ipmi_opened--;
189132729Skan	IPMI_UNLOCK(sc);
190132729Skan
19190075Sobrien	/* Cleanup. */
19290075Sobrien	free(dev, M_IPMI);
19390075Sobrien}
19490075Sobrien
19590075Sobrien#ifdef IPMB
19690075Sobrienstatic int
19790075Sobrienipmi_ipmb_checksum(u_char *data, int len)
19890075Sobrien{
19990075Sobrien	u_char sum = 0;
20090075Sobrien
20190075Sobrien	for (; len; len--) {
20290075Sobrien		sum += *data++;
20390075Sobrien	}
20490075Sobrien	return (-sum);
20590075Sobrien}
20690075Sobrien
20790075Sobrien/* XXX: Needs work */
208169699Skanstatic int
209169699Skanipmi_ipmb_send_message(device_t dev, u_char channel, u_char netfn,
210169699Skan    u_char command, u_char seq, u_char *data, int data_len)
211117406Skan{
21290075Sobrien	struct ipmi_softc *sc = device_get_softc(dev);
213132729Skan	struct ipmi_request *req;
214132729Skan	u_char slave_addr = 0x52;
215132729Skan	int error;
216132729Skan
217132729Skan	req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
218132729Skan	    IPMI_SEND_MSG, data_len + 8, 0);
219132729Skan	req->ir_request[0] = channel;
220132729Skan	req->ir_request[1] = slave_addr;
22190075Sobrien	req->ir_request[2] = IPMI_ADDR(netfn, 0);
22290075Sobrien	req->ir_request[3] = ipmi_ipmb_checksum(&req->ir_request[1], 2);
22390075Sobrien	req->ir_request[4] = sc->ipmi_address;
22490075Sobrien	req->ir_request[5] = IPMI_ADDR(seq, sc->ipmi_lun);
22590075Sobrien	req->ir_request[6] = command;
22690075Sobrien
227132729Skan	bcopy(data, &req->ir_request[7], data_len);
228132729Skan	temp[data_len + 7] = ipmi_ipmb_checksum(&req->ir_request[4],
229132729Skan	    data_len + 3);
230132729Skan
23190075Sobrien	ipmi_submit_driver_request(sc, req);
23290075Sobrien	error = req->ir_error;
23390075Sobrien	ipmi_free_request(req);
234132729Skan
235132729Skan	return (error);
236132729Skan}
237132729Skan
238132729Skanstatic int
239132729Skanipmi_handle_attn(struct ipmi_softc *sc)
240132729Skan{
24190075Sobrien	struct ipmi_request *req;
24290075Sobrien	int error;
243117406Skan
244169699Skan	device_printf(sc->ipmi_dev, "BMC has a message\n");
245169699Skan	req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
246169699Skan	    IPMI_GET_MSG_FLAGS, 0, 1);
247117406Skan
24890075Sobrien	ipmi_submit_driver_request(sc, req);
24990075Sobrien
25090075Sobrien	if (req->ir_error == 0 && req->ir_compcode == 0) {
25190075Sobrien		if (req->ir_reply[0] & IPMI_MSG_BUFFER_FULL) {
252117406Skan			device_printf(sc->ipmi_dev, "message buffer full");
25390075Sobrien		}
25490075Sobrien		if (req->ir_reply[0] & IPMI_WDT_PRE_TIMEOUT) {
25590075Sobrien			device_printf(sc->ipmi_dev,
25690075Sobrien			    "watchdog about to go off");
25790075Sobrien		}
25890075Sobrien		if (req->ir_reply[0] & IPMI_MSG_AVAILABLE) {
25990075Sobrien			ipmi_free_request(req);
26090075Sobrien
26190075Sobrien			req = ipmi_alloc_driver_request(
26290075Sobrien			    IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_MSG, 0,
26390075Sobrien			    16);
26490075Sobrien
26590075Sobrien			device_printf(sc->ipmi_dev, "throw out message ");
26690075Sobrien			dump_buf(temp, 16);
26790075Sobrien		}
26890075Sobrien	}
269117406Skan	error = req->ir_error;
27090075Sobrien	ipmi_free_request(req);
27190075Sobrien
27290075Sobrien	return (error);
27390075Sobrien}
27490075Sobrien#endif
275169699Skan
276169699Skan#ifdef IPMICTL_SEND_COMMAND_32
277169699Skan#define	PTRIN(p)	((void *)(uintptr_t)(p))
27890075Sobrien#define	PTROUT(p)	((uintptr_t)(p))
279169699Skan#endif
28090075Sobrien
28190075Sobrienstatic int
28290075Sobrienipmi_ioctl(struct cdev *cdev, u_long cmd, caddr_t data,
28390075Sobrien    int flags, struct thread *td)
28490075Sobrien{
285132729Skan	struct ipmi_softc *sc;
28690075Sobrien	struct ipmi_device *dev;
287169699Skan	struct ipmi_request *kreq;
288169699Skan	struct ipmi_req *req = (struct ipmi_req *)data;
289169699Skan	struct ipmi_recv *recv = (struct ipmi_recv *)data;
290169699Skan	struct ipmi_addr addr;
291169699Skan#ifdef IPMICTL_SEND_COMMAND_32
292169699Skan	struct ipmi_req32 *req32 = (struct ipmi_req32 *)data;
293132729Skan	struct ipmi_recv32 *recv32 = (struct ipmi_recv32 *)data;
294169699Skan	union {
295169699Skan		struct ipmi_req req;
296132729Skan		struct ipmi_recv recv;
297169699Skan	} thunk32;
298169699Skan#endif
299169699Skan	int error, len;
300169699Skan
301169699Skan	error = devfs_get_cdevpriv((void **)&dev);
30290075Sobrien	if (error)
303169699Skan		return (error);
30490075Sobrien
305132729Skan	sc = cdev->si_drv1;
306169699Skan
307169699Skan#ifdef IPMICTL_SEND_COMMAND_32
308169699Skan	/* Convert 32-bit structures to native. */
309132729Skan	switch (cmd) {
310132729Skan	case IPMICTL_SEND_COMMAND_32:
31190075Sobrien		req = &thunk32.req;
312132729Skan		req->addr = PTRIN(req32->addr);
31390075Sobrien		req->addr_len = req32->addr_len;
314132729Skan		req->msgid = req32->msgid;
31590075Sobrien		req->msg.netfn = req32->msg.netfn;
316132729Skan		req->msg.cmd = req32->msg.cmd;
317132729Skan		req->msg.data_len = req32->msg.data_len;
318132729Skan		req->msg.data = PTRIN(req32->msg.data);
319132729Skan		break;
32090075Sobrien	case IPMICTL_RECEIVE_MSG_TRUNC_32:
321132729Skan	case IPMICTL_RECEIVE_MSG_32:
322132729Skan		recv = &thunk32.recv;
323132729Skan		recv->addr = PTRIN(recv32->addr);
32490075Sobrien		recv->addr_len = recv32->addr_len;
325169699Skan		recv->msg.data_len = recv32->msg.data_len;
326169699Skan		recv->msg.data = PTRIN(recv32->msg.data);
327169699Skan		break;
328132729Skan	}
329132729Skan#endif
330132729Skan
331132729Skan	switch (cmd) {
33290075Sobrien#ifdef IPMICTL_SEND_COMMAND_32
333132729Skan	case IPMICTL_SEND_COMMAND_32:
334132729Skan#endif
335132729Skan	case IPMICTL_SEND_COMMAND:
336132729Skan		/*
33790075Sobrien		 * XXX: Need to add proper handling of this.
338132729Skan		 */
33990075Sobrien		error = copyin(req->addr, &addr, sizeof(addr));
340132729Skan		if (error)
34190075Sobrien			return (error);
342132729Skan
343132729Skan		IPMI_LOCK(sc);
344132729Skan		/* clear out old stuff in queue of stuff done */
345132729Skan		/* XXX: This seems odd. */
346132729Skan		while ((kreq = TAILQ_FIRST(&dev->ipmi_completed_requests))) {
347132729Skan			TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq,
348132729Skan			    ir_link);
349132729Skan			dev->ipmi_requests--;
350132729Skan			ipmi_free_request(kreq);
351132729Skan		}
352132729Skan		IPMI_UNLOCK(sc);
353132729Skan
354132729Skan		kreq = ipmi_alloc_request(dev, req->msgid,
355132729Skan		    IPMI_ADDR(req->msg.netfn, 0), req->msg.cmd,
356169699Skan		    req->msg.data_len, IPMI_MAX_RX);
357169699Skan		error = copyin(req->msg.data, kreq->ir_request,
358169699Skan		    req->msg.data_len);
359169699Skan		if (error) {
360132729Skan			ipmi_free_request(kreq);
361132729Skan			return (error);
362132729Skan		}
363132729Skan		IPMI_LOCK(sc);
364132729Skan		dev->ipmi_requests++;
365117406Skan		error = sc->ipmi_enqueue_request(sc, kreq);
366117406Skan		IPMI_UNLOCK(sc);
367132729Skan		if (error)
368117406Skan			return (error);
369117406Skan		break;
370117406Skan#ifdef IPMICTL_SEND_COMMAND_32
371117406Skan	case IPMICTL_RECEIVE_MSG_TRUNC_32:
372132729Skan	case IPMICTL_RECEIVE_MSG_32:
373117406Skan#endif
374117406Skan	case IPMICTL_RECEIVE_MSG_TRUNC:
375117406Skan	case IPMICTL_RECEIVE_MSG:
376117406Skan		error = copyin(recv->addr, &addr, sizeof(addr));
377117406Skan		if (error)
378117406Skan			return (error);
379117406Skan
380117406Skan		IPMI_LOCK(sc);
381117406Skan		kreq = TAILQ_FIRST(&dev->ipmi_completed_requests);
382117406Skan		if (kreq == NULL) {
383117406Skan			IPMI_UNLOCK(sc);
38490075Sobrien			return (EAGAIN);
38590075Sobrien		}
38690075Sobrien		addr.channel = IPMI_BMC_CHANNEL;
38790075Sobrien		/* XXX */
38890075Sobrien		recv->recv_type = IPMI_RESPONSE_RECV_TYPE;
38990075Sobrien		recv->msgid = kreq->ir_msgid;
39090075Sobrien		recv->msg.netfn = IPMI_REPLY_ADDR(kreq->ir_addr) >> 2;
39190075Sobrien		recv->msg.cmd = kreq->ir_command;
392117406Skan		error = kreq->ir_error;
393117406Skan		if (error) {
39490075Sobrien			TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq,
395117406Skan			    ir_link);
396117406Skan			dev->ipmi_requests--;
397117406Skan			IPMI_UNLOCK(sc);
398117406Skan			ipmi_free_request(kreq);
399117406Skan			return (error);
400117406Skan		}
401117406Skan		len = kreq->ir_replylen + 1;
402117406Skan		if (recv->msg.data_len < len &&
403117406Skan		    (cmd == IPMICTL_RECEIVE_MSG
404117406Skan#ifdef IPMICTL_RECEIVE_MSG_32
405117406Skan		     || cmd == IPMICTL_RECEIVE_MSG
406117406Skan#endif
407117406Skan		    )) {
408169699Skan			IPMI_UNLOCK(sc);
409117406Skan			return (EMSGSIZE);
410169699Skan		}
411117406Skan		TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link);
412117406Skan		dev->ipmi_requests--;
413117406Skan		IPMI_UNLOCK(sc);
414117406Skan		len = min(recv->msg.data_len, len);
41590075Sobrien		recv->msg.data_len = len;
41690075Sobrien		error = copyout(&addr, recv->addr,sizeof(addr));
41790075Sobrien		if (error == 0)
418117406Skan			error = copyout(&kreq->ir_compcode, recv->msg.data, 1);
41990075Sobrien		if (error == 0)
42090075Sobrien			error = copyout(kreq->ir_reply, recv->msg.data + 1,
421117406Skan			    len - 1);
422117406Skan		ipmi_free_request(kreq);
42396557Sobrien		if (error)
424117406Skan			return (error);
42590075Sobrien		break;
42690075Sobrien	case IPMICTL_SET_MY_ADDRESS_CMD:
42790075Sobrien		IPMI_LOCK(sc);
42890075Sobrien		dev->ipmi_address = *(int*)data;
42990075Sobrien		IPMI_UNLOCK(sc);
43090075Sobrien		break;
43190075Sobrien	case IPMICTL_GET_MY_ADDRESS_CMD:
43290075Sobrien		IPMI_LOCK(sc);
43390075Sobrien		*(int*)data = dev->ipmi_address;
434132729Skan		IPMI_UNLOCK(sc);
43590075Sobrien		break;
43690075Sobrien	case IPMICTL_SET_MY_LUN_CMD:
43790075Sobrien		IPMI_LOCK(sc);
438117406Skan		dev->ipmi_lun = *(int*)data & 0x3;
439117406Skan		IPMI_UNLOCK(sc);
440117406Skan		break;
441117406Skan	case IPMICTL_GET_MY_LUN_CMD:
442117406Skan		IPMI_LOCK(sc);
443117406Skan		*(int*)data = dev->ipmi_lun;
444117406Skan		IPMI_UNLOCK(sc);
445117406Skan		break;
446117406Skan	case IPMICTL_SET_GETS_EVENTS_CMD:
447117406Skan		/*
448117406Skan		device_printf(sc->ipmi_dev,
449117406Skan		    "IPMICTL_SET_GETS_EVENTS_CMD NA\n");
450117406Skan		*/
451117406Skan		break;
452117406Skan	case IPMICTL_REGISTER_FOR_CMD:
453117406Skan	case IPMICTL_UNREGISTER_FOR_CMD:
454117406Skan		return (EOPNOTSUPP);
455117406Skan	default:
456117406Skan		device_printf(sc->ipmi_dev, "Unknown IOCTL %lX\n", cmd);
457117406Skan		return (ENOIOCTL);
458117406Skan	}
459117406Skan
460117406Skan#ifdef IPMICTL_SEND_COMMAND_32
461117406Skan	/* Update changed fields in 32-bit structures. */
462117406Skan	switch (cmd) {
463117406Skan	case IPMICTL_RECEIVE_MSG_TRUNC_32:
464117406Skan	case IPMICTL_RECEIVE_MSG_32:
465117406Skan		recv32->recv_type = recv->recv_type;
466117406Skan		recv32->msgid = recv->msgid;
467117406Skan		recv32->msg.netfn = recv->msg.netfn;
468117406Skan		recv32->msg.cmd = recv->msg.cmd;
469117406Skan		recv32->msg.data_len = recv->msg.data_len;
470117406Skan		break;
471117406Skan	}
472117406Skan#endif
473117406Skan	return (0);
474117406Skan}
475117406Skan
476117406Skan/*
477117406Skan * Request management.
478117406Skan */
479117406Skan
480117406Skan/* Allocate a new request with request and reply buffers. */
481117406Skanstruct ipmi_request *
482117406Skanipmi_alloc_request(struct ipmi_device *dev, long msgid, uint8_t addr,
483117406Skan    uint8_t command, size_t requestlen, size_t replylen)
484117406Skan{
485117406Skan	struct ipmi_request *req;
486117406Skan
487117406Skan	req = malloc(sizeof(struct ipmi_request) + requestlen + replylen,
488117406Skan	    M_IPMI, M_WAITOK | M_ZERO);
489117406Skan	req->ir_owner = dev;
490117406Skan	req->ir_msgid = msgid;
491117406Skan	req->ir_addr = addr;
492117406Skan	req->ir_command = command;
493117406Skan	if (requestlen) {
494117406Skan		req->ir_request = (char *)&req[1];
495117406Skan		req->ir_requestlen = requestlen;
496117406Skan	}
497117406Skan	if (replylen) {
498117406Skan		req->ir_reply = (char *)&req[1] + requestlen;
499117406Skan		req->ir_replybuflen = replylen;
500117406Skan	}
501117406Skan	return (req);
502117406Skan}
503117406Skan
504117406Skan/* Free a request no longer in use. */
505117406Skanvoid
506117406Skanipmi_free_request(struct ipmi_request *req)
507117406Skan{
508117406Skan
509117406Skan	free(req, M_IPMI);
510117406Skan}
511117406Skan
512117406Skan/* Store a processed request on the appropriate completion queue. */
513117406Skanvoid
514117406Skanipmi_complete_request(struct ipmi_softc *sc, struct ipmi_request *req)
515117406Skan{
516117406Skan	struct ipmi_device *dev;
517117406Skan
518117406Skan	IPMI_LOCK_ASSERT(sc);
519117406Skan
520117406Skan	/*
521117406Skan	 * Anonymous requests (from inside the driver) always have a
522117406Skan	 * waiter that we awaken.
523117406Skan	 */
524117406Skan	if (req->ir_owner == NULL)
525117406Skan		wakeup(req);
526117406Skan	else {
527117406Skan		dev = req->ir_owner;
528117406Skan		TAILQ_INSERT_TAIL(&dev->ipmi_completed_requests, req, ir_link);
529117406Skan		selwakeup(&dev->ipmi_select);
530117406Skan		if (dev->ipmi_closing)
531117406Skan			wakeup(&dev->ipmi_requests);
532117406Skan	}
533117406Skan}
534117406Skan
535117406Skan/* Enqueue an internal driver request and wait until it is completed. */
536117406Skanint
537117406Skanipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request *req,
538117406Skan    int timo)
539132729Skan{
540132729Skan	int error;
541132729Skan
542132729Skan	IPMI_LOCK(sc);
543132729Skan	error = sc->ipmi_enqueue_request(sc, req);
544132729Skan	if (error == 0)
545132729Skan		error = msleep(req, &sc->ipmi_lock, 0, "ipmireq", timo);
546117406Skan	if (error == 0)
547117406Skan		error = req->ir_error;
548117406Skan	IPMI_UNLOCK(sc);
549117406Skan	return (error);
550117406Skan}
551169699Skan
552169699Skan/*
553169699Skan * Helper routine for polled system interfaces that use
554169699Skan * ipmi_polled_enqueue_request() to queue requests.  This request
555169699Skan * waits until there is a pending request and then returns the first
556117406Skan * request.  If the driver is shutting down, it returns NULL.
557117406Skan */
558117406Skanstruct ipmi_request *
559117406Skanipmi_dequeue_request(struct ipmi_softc *sc)
560117406Skan{
561117406Skan	struct ipmi_request *req;
562117406Skan
563117406Skan	IPMI_LOCK_ASSERT(sc);
564117406Skan
565117406Skan	while (!sc->ipmi_detaching && TAILQ_EMPTY(&sc->ipmi_pending_requests))
566117406Skan		cv_wait(&sc->ipmi_request_added, &sc->ipmi_lock);
567117406Skan	if (sc->ipmi_detaching)
568169699Skan		return (NULL);
569169699Skan
570117406Skan	req = TAILQ_FIRST(&sc->ipmi_pending_requests);
571169699Skan	TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link);
572117406Skan	return (req);
573117406Skan}
574117406Skan
575117406Skan/* Default implementation of ipmi_enqueue_request() for polled interfaces. */
576117406Skanint
577169699Skanipmi_polled_enqueue_request(struct ipmi_softc *sc, struct ipmi_request *req)
578169699Skan{
579169699Skan
580117406Skan	IPMI_LOCK_ASSERT(sc);
581169699Skan
582117406Skan	TAILQ_INSERT_TAIL(&sc->ipmi_pending_requests, req, ir_link);
583117406Skan	cv_signal(&sc->ipmi_request_added);
584117406Skan	return (0);
585117406Skan}
586117406Skan
587117406Skan/*
588117406Skan * Watchdog event handler.
589103452Skan */
590103452Skan
591103452Skanstatic void
592103452Skanipmi_set_watchdog(struct ipmi_softc *sc, int sec)
593103452Skan{
59490075Sobrien	struct ipmi_request *req;
59590075Sobrien	int error;
59690075Sobrien
59790075Sobrien	req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
59890075Sobrien	    IPMI_SET_WDOG, 6, 0);
59990075Sobrien
60090075Sobrien	if (sec) {
60190075Sobrien		req->ir_request[0] = IPMI_SET_WD_TIMER_DONT_STOP
60290075Sobrien		    | IPMI_SET_WD_TIMER_SMS_OS;
60390075Sobrien		req->ir_request[1] = IPMI_SET_WD_ACTION_RESET;
60490075Sobrien		req->ir_request[2] = 0;
60590075Sobrien		req->ir_request[3] = 0;	/* Timer use */
60690075Sobrien		req->ir_request[4] = (sec * 10) & 0xff;
60790075Sobrien		req->ir_request[5] = (sec * 10) / 2550;
60890075Sobrien	} else {
60990075Sobrien		req->ir_request[0] = IPMI_SET_WD_TIMER_SMS_OS;
610117406Skan		req->ir_request[1] = 0;
611117406Skan		req->ir_request[2] = 0;
612117406Skan		req->ir_request[3] = 0;	/* Timer use */
613117406Skan		req->ir_request[4] = 0;
61490075Sobrien		req->ir_request[5] = 0;
61590075Sobrien	}
61690075Sobrien
61790075Sobrien	error = ipmi_submit_driver_request(sc, req, 0);
61890075Sobrien	if (error)
619132729Skan		device_printf(sc->ipmi_dev, "Failed to set watchdog\n");
62090075Sobrien
621132729Skan	if (error == 0 && sec) {
622132729Skan		ipmi_free_request(req);
623132729Skan
624132729Skan		req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
625132729Skan		    IPMI_RESET_WDOG, 0, 0);
626132729Skan
627132729Skan		error = ipmi_submit_driver_request(sc, req, 0);
62890075Sobrien		if (error)
629169699Skan			device_printf(sc->ipmi_dev,
630132729Skan			    "Failed to reset watchdog\n");
631132729Skan	}
632132729Skan
633132729Skan	ipmi_free_request(req);
634132729Skan	/*
635169699Skan	dump_watchdog(sc);
636132729Skan	*/
637132729Skan}
638132729Skan
639132729Skanstatic void
640132729Skanipmi_wd_event(void *arg, unsigned int cmd, int *error)
641132729Skan{
642132729Skan	struct ipmi_softc *sc = arg;
643132729Skan	unsigned int timeout;
644132729Skan
645132729Skan	cmd &= WD_INTERVAL;
646169699Skan	if (cmd > 0 && cmd <= 63) {
647132729Skan		timeout = ((uint64_t)1 << cmd) / 1800000000;
648132729Skan		ipmi_set_watchdog(sc, timeout);
649169699Skan		*error = 0;
650132729Skan	} else {
65190075Sobrien		ipmi_set_watchdog(sc, 0);
65290075Sobrien	}
653132729Skan}
654132729Skan
655117406Skanstatic void
656132729Skanipmi_startup(void *arg)
657169699Skan{
658169699Skan	struct ipmi_softc *sc = arg;
659132729Skan	struct ipmi_request *req;
660132729Skan	device_t dev;
661169699Skan	int error, i;
662169699Skan
66390075Sobrien	config_intrhook_disestablish(&sc->ipmi_ich);
664169699Skan	dev = sc->ipmi_dev;
665169699Skan
66690075Sobrien	/* Initialize interface-independent state. */
66790075Sobrien	mtx_init(&sc->ipmi_lock, device_get_nameunit(dev), "ipmi", MTX_DEF);
66890075Sobrien	cv_init(&sc->ipmi_request_added, "ipmireq");
66990075Sobrien	TAILQ_INIT(&sc->ipmi_pending_requests);
670132729Skan
67190075Sobrien	/* Initialize interface-dependent state. */
672132729Skan	error = sc->ipmi_startup(sc);
673132729Skan	if (error) {
67490075Sobrien		device_printf(dev, "Failed to initialize interface: %d\n",
67590075Sobrien		    error);
676132729Skan		return;
67790075Sobrien	}
67890075Sobrien
67990075Sobrien	/* Send a GET_DEVICE_ID request. */
680132729Skan	req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
68190075Sobrien	    IPMI_GET_DEVICE_ID, 0, 15);
682169699Skan
683169699Skan	error = ipmi_submit_driver_request(sc, req, MAX_TIMEOUT);
684132729Skan	if (error == EWOULDBLOCK) {
68590075Sobrien		device_printf(dev, "Timed out waiting for GET_DEVICE_ID\n");
686169699Skan		ipmi_free_request(req);
687169699Skan		return;
688132729Skan	} else if (error) {
68990075Sobrien		device_printf(dev, "Failed GET_DEVICE_ID: %d\n", error);
690132729Skan		ipmi_free_request(req);
691132729Skan		return;
692132729Skan	} else if (req->ir_compcode != 0) {
693132729Skan		device_printf(dev,
694132729Skan		    "Bad completion code for GET_DEVICE_ID: %d\n",
695132729Skan		    req->ir_compcode);
696132729Skan		ipmi_free_request(req);
697132729Skan		return;
698132729Skan	} else if (req->ir_replylen < 5) {
699132729Skan		device_printf(dev, "Short reply for GET_DEVICE_ID: %d\n",
700132729Skan		    req->ir_replylen);
701169699Skan		ipmi_free_request(req);
70290075Sobrien		return;
703132729Skan	}
704132729Skan
705132729Skan	device_printf(dev, "IPMI device rev. %d, firmware rev. %d.%d, "
706132729Skan	    "version %d.%d\n",
707132729Skan	     req->ir_reply[1] & 0x0f,
708169699Skan	     req->ir_reply[2] & 0x0f, req->ir_reply[4],
70990075Sobrien	     req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4);
71090075Sobrien
711169699Skan	ipmi_free_request(req);
712169699Skan
713169699Skan	req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
714169699Skan	    IPMI_CLEAR_FLAGS, 1, 0);
71590075Sobrien
716169699Skan	ipmi_submit_driver_request(sc, req, 0);
717169699Skan
718169699Skan	/* XXX: Magic numbers */
71990075Sobrien	if (req->ir_compcode == 0xc0) {
72090075Sobrien		device_printf(dev, "Clear flags is busy\n");
72190075Sobrien	}
72290075Sobrien	if (req->ir_compcode == 0xc1) {
72390075Sobrien		device_printf(dev, "Clear flags illegal\n");
724169699Skan	}
72590075Sobrien	ipmi_free_request(req);
72690075Sobrien
72790075Sobrien	for (i = 0; i < 8; i++) {
72890075Sobrien		req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
72990075Sobrien		    IPMI_GET_CHANNEL_INFO, 1, 0);
73090075Sobrien		req->ir_request[0] = i;
73190075Sobrien
73290075Sobrien		ipmi_submit_driver_request(sc, req, 0);
73390075Sobrien
73490075Sobrien		if (req->ir_compcode != 0) {
73590075Sobrien			ipmi_free_request(req);
736132729Skan			break;
737132729Skan		}
73890075Sobrien		ipmi_free_request(req);
73990075Sobrien	}
74090075Sobrien	device_printf(dev, "Number of channels %d\n", i);
74190075Sobrien
74290075Sobrien	/* probe for watchdog */
74390075Sobrien	req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
74490075Sobrien	    IPMI_GET_WDOG, 0, 0);
74590075Sobrien
74690075Sobrien	ipmi_submit_driver_request(sc, req, 0);
74790075Sobrien
74890075Sobrien	if (req->ir_compcode == 0x00) {
74990075Sobrien		device_printf(dev, "Attached watchdog\n");
750132729Skan		/* register the watchdog event handler */
751132729Skan		sc->ipmi_watchdog_tag = EVENTHANDLER_REGISTER(watchdog_list,
752132729Skan		    ipmi_wd_event, sc, 0);
753132729Skan	}
75490075Sobrien	ipmi_free_request(req);
75590075Sobrien
75690075Sobrien	sc->ipmi_cdev = make_dev(&ipmi_cdevsw, device_get_unit(dev),
75790075Sobrien	    UID_ROOT, GID_OPERATOR, 0660, "ipmi%d", device_get_unit(dev));
758132729Skan	if (sc->ipmi_cdev == NULL) {
759132729Skan		device_printf(dev, "Failed to create cdev\n");
760169699Skan		return;
761169699Skan	}
762169699Skan	sc->ipmi_cdev->si_drv1 = sc;
763169699Skan}
764169699Skan
765132729Skanint
76690075Sobrienipmi_attach(device_t dev)
76790075Sobrien{
76890075Sobrien	struct ipmi_softc *sc = device_get_softc(dev);
76990075Sobrien	int error;
770132729Skan
77190075Sobrien	if (sc->ipmi_irq_res != NULL && sc->ipmi_intr != NULL) {
772132729Skan		error = bus_setup_intr(dev, sc->ipmi_irq_res, INTR_TYPE_MISC,
77390075Sobrien		    NULL, sc->ipmi_intr, sc, &sc->ipmi_irq);
77490075Sobrien		if (error) {
775132729Skan			device_printf(dev, "can't set up interrupt\n");
77690075Sobrien			return (error);
777132729Skan		}
77890075Sobrien	}
779169699Skan
78090075Sobrien	bzero(&sc->ipmi_ich, sizeof(struct intr_config_hook));
781169699Skan	sc->ipmi_ich.ich_func = ipmi_startup;
782169699Skan	sc->ipmi_ich.ich_arg = sc;
783132729Skan	if (config_intrhook_establish(&sc->ipmi_ich) != 0) {
78490075Sobrien		device_printf(dev, "can't establish configuration hook\n");
785169699Skan		return (ENOMEM);
786169699Skan	}
787132729Skan
78890075Sobrien	ipmi_attached = 1;
78990075Sobrien	return (0);
79090075Sobrien}
791132729Skan
792169699Skanint
79390075Sobrienipmi_detach(device_t dev)
794161660Skan{
795161660Skan	struct ipmi_softc *sc;
796132729Skan
79790075Sobrien	sc = device_get_softc(dev);
798169699Skan
79990075Sobrien	/* Fail if there are any open handles. */
800132729Skan	IPMI_LOCK(sc);
801117406Skan	if (sc->ipmi_opened) {
802132729Skan		IPMI_UNLOCK(sc);
803117406Skan		return (EBUSY);
804132729Skan	}
805132729Skan	IPMI_UNLOCK(sc);
806169699Skan	if (sc->ipmi_cdev)
807132729Skan		destroy_dev(sc->ipmi_cdev);
80890075Sobrien
80990075Sobrien	/* Detach from watchdog handling and turn off watchdog. */
81090075Sobrien	if (sc->ipmi_watchdog_tag) {
81190075Sobrien		EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_watchdog_tag);
81290075Sobrien		ipmi_set_watchdog(sc, 0);
813169699Skan	}
814169699Skan
815169699Skan	/* XXX: should use shutdown callout I think. */
816169699Skan	/* If the backend uses a kthread, shut it down. */
817169699Skan	IPMI_LOCK(sc);
818169699Skan	sc->ipmi_detaching = 1;
819169699Skan	if (sc->ipmi_kthread) {
820169699Skan		cv_broadcast(&sc->ipmi_request_added);
82190075Sobrien		msleep(sc->ipmi_kthread, &sc->ipmi_lock, 0, "ipmi_wait", 0);
82290075Sobrien	}
82390075Sobrien	IPMI_UNLOCK(sc);
824132729Skan	if (sc->ipmi_irq)
825132729Skan		bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq);
82690075Sobrien
827132729Skan	ipmi_release_resources(dev);
828117406Skan	mtx_destroy(&sc->ipmi_lock);
829169699Skan	return (0);
830169699Skan}
831169699Skan
832169699Skanvoid
833169699Skanipmi_release_resources(device_t dev)
834169699Skan{
835169699Skan	struct ipmi_softc *sc;
836169699Skan	int i;
837169699Skan
838169699Skan	sc = device_get_softc(dev);
839169699Skan	if (sc->ipmi_irq)
840169699Skan		bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq);
841169699Skan	if (sc->ipmi_irq_res)
842169699Skan		bus_release_resource(dev, SYS_RES_IRQ, sc->ipmi_irq_rid,
843169699Skan		    sc->ipmi_irq_res);
844169699Skan	for (i = 0; i < MAX_RES; i++)
845169699Skan		if (sc->ipmi_io_res[i])
846169699Skan			bus_release_resource(dev, sc->ipmi_io_type,
847169699Skan			    sc->ipmi_io_rid + i, sc->ipmi_io_res[i]);
848169699Skan}
849169699Skan
850169699Skandevclass_t ipmi_devclass;
851169699Skan
852169699Skan/* XXX: Why? */
853259268Spfgstatic void
854259268Spfgipmi_unload(void *arg)
855169699Skan{
856259268Spfg	device_t *	devs;
857169699Skan	int		count;
858169699Skan	int		i;
859169699Skan
860169699Skan	if (devclass_get_devices(ipmi_devclass, &devs, &count) != 0)
861169699Skan		return;
862169699Skan	for (i = 0; i < count; i++)
863132729Skan		device_delete_child(device_get_parent(devs[i]), devs[i]);
864132729Skan	free(devs, M_TEMP);
865132729Skan}
866132729SkanSYSUNINIT(ipmi_unload, SI_SUB_DRIVERS, SI_ORDER_FIRST, ipmi_unload, NULL);
867132729Skan
868132729Skan#ifdef IMPI_DEBUG
869169699Skanstatic void
870169699Skandump_buf(u_char *data, int len)
871169699Skan{
872169699Skan	char buf[20];
873169699Skan	char line[1024];
874169699Skan	char temp[30];
875132729Skan	int count = 0;
876132729Skan	int i=0;
877132729Skan
878132729Skan	printf("Address %p len %d\n", data, len);
879132729Skan	if (len > 256)
880169699Skan		len = 256;
881169699Skan	line[0] = '\000';
882169699Skan	for (; len > 0; len--, data++) {
883169699Skan		sprintf(temp, "%02x ", *data);
884169699Skan		strcat(line, temp);
885132729Skan		if (*data >= ' ' && *data <= '~')
886132729Skan			buf[count] = *data;
887169699Skan		else if (*data >= 'A' && *data <= 'Z')
888169699Skan			buf[count] = *data;
889169699Skan		else
890169699Skan			buf[count] = '.';
891169699Skan		if (++count == 16) {
892132729Skan			buf[count] = '\000';
893169699Skan			count = 0;
894169699Skan			printf("  %3x  %s %s\n", i, line, buf);
895169699Skan			i+=16;
896169699Skan			line[0] = '\000';
897169699Skan		}
898169699Skan	}
899169699Skan	buf[count] = '\000';
900169699Skan
901169699Skan	for (; count != 16; count++) {
902169699Skan		strcat(line, "   ");
903169699Skan	}
904169699Skan	printf("  %3x  %s %s\n", i, line, buf);
905169699Skan}
906169699Skan#endif
907169699Skan