153a374c1SSascha Wildner /*-
253a374c1SSascha Wildner * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
353a374c1SSascha Wildner * All rights reserved.
453a374c1SSascha Wildner *
553a374c1SSascha Wildner * Redistribution and use in source and binary forms, with or without
653a374c1SSascha Wildner * modification, are permitted provided that the following conditions
753a374c1SSascha Wildner * are met:
853a374c1SSascha Wildner * 1. Redistributions of source code must retain the above copyright
953a374c1SSascha Wildner * notice, this list of conditions and the following disclaimer.
1053a374c1SSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright
1153a374c1SSascha Wildner * notice, this list of conditions and the following disclaimer in the
1253a374c1SSascha Wildner * documentation and/or other materials provided with the distribution.
1353a374c1SSascha Wildner *
1453a374c1SSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1553a374c1SSascha Wildner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1653a374c1SSascha Wildner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1753a374c1SSascha Wildner * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1853a374c1SSascha Wildner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1953a374c1SSascha Wildner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2053a374c1SSascha Wildner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2153a374c1SSascha Wildner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2253a374c1SSascha Wildner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2353a374c1SSascha Wildner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2453a374c1SSascha Wildner * SUCH DAMAGE.
2553a374c1SSascha Wildner *
2653a374c1SSascha Wildner * $FreeBSD: head/sys/dev/ipmi/ipmi.c 257421 2013-10-31 05:13:53Z glebius $
2753a374c1SSascha Wildner */
2853a374c1SSascha Wildner
2953a374c1SSascha Wildner #include <sys/param.h>
3053a374c1SSascha Wildner #include <sys/systm.h>
3153a374c1SSascha Wildner #include <sys/bus.h>
3253a374c1SSascha Wildner #include <sys/condvar.h>
3353a374c1SSascha Wildner #include <sys/conf.h>
3453a374c1SSascha Wildner #include <sys/kernel.h>
3553a374c1SSascha Wildner #include <sys/malloc.h>
3653a374c1SSascha Wildner #include <sys/module.h>
3753a374c1SSascha Wildner #include <sys/rman.h>
3853a374c1SSascha Wildner #include <sys/sysctl.h>
3953a374c1SSascha Wildner #include <sys/wdog.h>
4053a374c1SSascha Wildner #include <sys/device.h>
4153a374c1SSascha Wildner #include <sys/devfs.h>
4253a374c1SSascha Wildner
4353a374c1SSascha Wildner #ifdef LOCAL_MODULE
4453a374c1SSascha Wildner #include <ipmi.h>
4553a374c1SSascha Wildner #include <ipmivars.h>
4653a374c1SSascha Wildner #else
4753a374c1SSascha Wildner #include <sys/ipmi.h>
4853a374c1SSascha Wildner #include <dev/misc/ipmi/ipmivars.h>
4953a374c1SSascha Wildner #endif
5053a374c1SSascha Wildner
5153a374c1SSascha Wildner #ifdef IPMB
5253a374c1SSascha Wildner static int ipmi_ipmb_checksum(u_char, int);
5353a374c1SSascha Wildner static int ipmi_ipmb_send_message(device_t, u_char, u_char, u_char,
5453a374c1SSascha Wildner u_char, u_char, int)
5553a374c1SSascha Wildner #endif
5653a374c1SSascha Wildner
5753a374c1SSascha Wildner static d_ioctl_t ipmi_ioctl;
5853a374c1SSascha Wildner static d_kqfilter_t ipmi_kqfilter;
5953a374c1SSascha Wildner static d_open_t ipmi_open;
60*d56bec7aSAaron LI static d_priv_dtor_t ipmi_dtor;
6153a374c1SSascha Wildner
6253a374c1SSascha Wildner static void ipmi_filter_detach(struct knote *);
6353a374c1SSascha Wildner static int ipmi_filter_read(struct knote *, long);
6453a374c1SSascha Wildner
6553a374c1SSascha Wildner int ipmi_attached = 0;
6653a374c1SSascha Wildner
6753a374c1SSascha Wildner static int on = 1;
6853a374c1SSascha Wildner static SYSCTL_NODE(_hw, OID_AUTO, ipmi, CTLFLAG_RD, 0,
6953a374c1SSascha Wildner "IPMI driver parameters");
7053a374c1SSascha Wildner SYSCTL_INT(_hw_ipmi, OID_AUTO, on, CTLFLAG_RW,
7153a374c1SSascha Wildner &on, 0, "");
7253a374c1SSascha Wildner
7353a374c1SSascha Wildner static struct dev_ops ipmi_ops = {
7453a374c1SSascha Wildner { "ipmi", 0, D_MPSAFE },
7553a374c1SSascha Wildner .d_open = ipmi_open,
7653a374c1SSascha Wildner .d_ioctl = ipmi_ioctl,
7753a374c1SSascha Wildner .d_kqfilter = ipmi_kqfilter,
7853a374c1SSascha Wildner };
7953a374c1SSascha Wildner
807670f87fSMarkus Pfeiffer static int ipmi_watchdog_sysctl_enable(SYSCTL_HANDLER_ARGS);
817670f87fSMarkus Pfeiffer static int ipmi_watchdog_sysctl_period(SYSCTL_HANDLER_ARGS);
8253a374c1SSascha Wildner
8353a374c1SSascha Wildner static MALLOC_DEFINE(M_IPMI, "ipmi", "ipmi");
8453a374c1SSascha Wildner
8553a374c1SSascha Wildner static int
ipmi_open(struct dev_open_args * ap)8653a374c1SSascha Wildner ipmi_open(struct dev_open_args *ap)
8753a374c1SSascha Wildner {
885bd45597SMatthew Dillon struct file *fp = ap->a_fpp ? *ap->a_fpp : NULL;
8953a374c1SSascha Wildner cdev_t cdev = ap->a_head.a_dev;
9053a374c1SSascha Wildner struct ipmi_device *dev;
9153a374c1SSascha Wildner struct ipmi_softc *sc;
9253a374c1SSascha Wildner int error;
9353a374c1SSascha Wildner
9453a374c1SSascha Wildner if (!on)
9553a374c1SSascha Wildner return (ENOENT);
9653a374c1SSascha Wildner
9753a374c1SSascha Wildner /* Initialize the per file descriptor data. */
9853a374c1SSascha Wildner dev = kmalloc(sizeof(struct ipmi_device), M_IPMI, M_WAITOK | M_ZERO);
9953a374c1SSascha Wildner error = devfs_set_cdevpriv(fp, dev, ipmi_dtor);
10053a374c1SSascha Wildner if (error) {
10153a374c1SSascha Wildner kfree(dev, M_IPMI);
10253a374c1SSascha Wildner return (error);
10353a374c1SSascha Wildner }
10453a374c1SSascha Wildner
10553a374c1SSascha Wildner sc = cdev->si_drv1;
10653a374c1SSascha Wildner TAILQ_INIT(&dev->ipmi_completed_requests);
10753a374c1SSascha Wildner dev->ipmi_address = IPMI_BMC_SLAVE_ADDR;
10853a374c1SSascha Wildner dev->ipmi_lun = IPMI_BMC_SMS_LUN;
10953a374c1SSascha Wildner dev->ipmi_softc = sc;
11053a374c1SSascha Wildner IPMI_LOCK(sc);
11153a374c1SSascha Wildner sc->ipmi_opened++;
11253a374c1SSascha Wildner IPMI_UNLOCK(sc);
11353a374c1SSascha Wildner
11453a374c1SSascha Wildner return (0);
11553a374c1SSascha Wildner }
11653a374c1SSascha Wildner
11753a374c1SSascha Wildner static struct filterops ipmi_filterops = {
11853a374c1SSascha Wildner FILTEROP_ISFD | FILTEROP_MPSAFE,
11953a374c1SSascha Wildner NULL,
12053a374c1SSascha Wildner ipmi_filter_detach,
12153a374c1SSascha Wildner ipmi_filter_read
12253a374c1SSascha Wildner };
12353a374c1SSascha Wildner
12453a374c1SSascha Wildner static int
ipmi_kqfilter(struct dev_kqfilter_args * ap)12553a374c1SSascha Wildner ipmi_kqfilter(struct dev_kqfilter_args *ap)
12653a374c1SSascha Wildner {
12753a374c1SSascha Wildner cdev_t cdev = ap->a_head.a_dev;
12853a374c1SSascha Wildner struct file *fp = ap->a_fp;
12953a374c1SSascha Wildner struct knote *kn = ap->a_kn;
13053a374c1SSascha Wildner struct ipmi_softc *sc = cdev->si_drv1;
13153a374c1SSascha Wildner struct ipmi_device *dev;
13253a374c1SSascha Wildner struct klist *klist;
13353a374c1SSascha Wildner
13453a374c1SSascha Wildner ap->a_result = 0;
13553a374c1SSascha Wildner
13653a374c1SSascha Wildner switch(kn->kn_filter) {
13753a374c1SSascha Wildner case EVFILT_READ:
13853a374c1SSascha Wildner if (devfs_get_cdevpriv(fp, (void **)&dev))
13953a374c1SSascha Wildner return EOPNOTSUPP;
14053a374c1SSascha Wildner kn->kn_fop = &ipmi_filterops;
14153a374c1SSascha Wildner kn->kn_hook = (caddr_t)dev;
14253a374c1SSascha Wildner break;
14353a374c1SSascha Wildner default:
14453a374c1SSascha Wildner ap->a_result = EOPNOTSUPP;
14553a374c1SSascha Wildner return (0);
14653a374c1SSascha Wildner }
14753a374c1SSascha Wildner
14853a374c1SSascha Wildner klist = &sc->ipmi_kq.ki_note;
14953a374c1SSascha Wildner knote_insert(klist, kn);
15053a374c1SSascha Wildner
15153a374c1SSascha Wildner return (0);
15253a374c1SSascha Wildner }
15353a374c1SSascha Wildner
15453a374c1SSascha Wildner static void
ipmi_filter_detach(struct knote * kn)15553a374c1SSascha Wildner ipmi_filter_detach(struct knote *kn)
15653a374c1SSascha Wildner {
15753a374c1SSascha Wildner struct ipmi_device *dev = (struct ipmi_device *)kn->kn_hook;
15853a374c1SSascha Wildner struct ipmi_softc *sc = dev->ipmi_softc;
15953a374c1SSascha Wildner struct klist *klist;
16053a374c1SSascha Wildner
16153a374c1SSascha Wildner klist = &sc->ipmi_kq.ki_note;
16253a374c1SSascha Wildner knote_remove(klist, kn);
16353a374c1SSascha Wildner }
16453a374c1SSascha Wildner
16553a374c1SSascha Wildner static int
ipmi_filter_read(struct knote * kn,long hint)16653a374c1SSascha Wildner ipmi_filter_read(struct knote *kn, long hint)
16753a374c1SSascha Wildner {
16853a374c1SSascha Wildner struct ipmi_device *dev = (struct ipmi_device *)kn->kn_hook;
16953a374c1SSascha Wildner struct ipmi_softc *sc = dev->ipmi_softc;
17053a374c1SSascha Wildner int ret = 0;
17153a374c1SSascha Wildner
17253a374c1SSascha Wildner IPMI_LOCK(sc);
17353a374c1SSascha Wildner if (!TAILQ_EMPTY(&dev->ipmi_completed_requests))
17453a374c1SSascha Wildner ret = 1;
17553a374c1SSascha Wildner if (dev->ipmi_requests == 0)
17653a374c1SSascha Wildner kn->kn_flags |= EV_ERROR;
17753a374c1SSascha Wildner IPMI_UNLOCK(sc);
17853a374c1SSascha Wildner
17953a374c1SSascha Wildner return (ret);
18053a374c1SSascha Wildner }
18153a374c1SSascha Wildner
18253a374c1SSascha Wildner static void
ipmi_purge_completed_requests(struct ipmi_device * dev)18353a374c1SSascha Wildner ipmi_purge_completed_requests(struct ipmi_device *dev)
18453a374c1SSascha Wildner {
18553a374c1SSascha Wildner struct ipmi_request *req;
18653a374c1SSascha Wildner
18753a374c1SSascha Wildner while (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) {
18853a374c1SSascha Wildner req = TAILQ_FIRST(&dev->ipmi_completed_requests);
18953a374c1SSascha Wildner TAILQ_REMOVE(&dev->ipmi_completed_requests, req, ir_link);
19053a374c1SSascha Wildner dev->ipmi_requests--;
19153a374c1SSascha Wildner ipmi_free_request(req);
19253a374c1SSascha Wildner }
19353a374c1SSascha Wildner }
19453a374c1SSascha Wildner
19553a374c1SSascha Wildner static void
ipmi_dtor(void * arg)19653a374c1SSascha Wildner ipmi_dtor(void *arg)
19753a374c1SSascha Wildner {
19853a374c1SSascha Wildner struct ipmi_request *req, *nreq;
19953a374c1SSascha Wildner struct ipmi_device *dev;
20053a374c1SSascha Wildner struct ipmi_softc *sc;
20153a374c1SSascha Wildner
20253a374c1SSascha Wildner dev = arg;
20353a374c1SSascha Wildner sc = dev->ipmi_softc;
20453a374c1SSascha Wildner
20553a374c1SSascha Wildner IPMI_LOCK(sc);
20653a374c1SSascha Wildner if (dev->ipmi_requests) {
20753a374c1SSascha Wildner /* Throw away any pending requests for this device. */
20853a374c1SSascha Wildner TAILQ_FOREACH_MUTABLE(req, &sc->ipmi_pending_requests, ir_link,
20953a374c1SSascha Wildner nreq) {
21053a374c1SSascha Wildner if (req->ir_owner == dev) {
21153a374c1SSascha Wildner TAILQ_REMOVE(&sc->ipmi_pending_requests, req,
21253a374c1SSascha Wildner ir_link);
21353a374c1SSascha Wildner dev->ipmi_requests--;
21453a374c1SSascha Wildner ipmi_free_request(req);
21553a374c1SSascha Wildner }
21653a374c1SSascha Wildner }
21753a374c1SSascha Wildner
21853a374c1SSascha Wildner /* Throw away any pending completed requests for this device. */
21953a374c1SSascha Wildner ipmi_purge_completed_requests(dev);
22053a374c1SSascha Wildner
22153a374c1SSascha Wildner /*
22253a374c1SSascha Wildner * If we still have outstanding requests, they must be stuck
22353a374c1SSascha Wildner * in an interface driver, so wait for those to drain.
22453a374c1SSascha Wildner */
22553a374c1SSascha Wildner dev->ipmi_closing = 1;
22653a374c1SSascha Wildner while (dev->ipmi_requests > 0) {
22753a374c1SSascha Wildner lksleep(&dev->ipmi_requests, &sc->ipmi_lock, 0,
22853a374c1SSascha Wildner "ipmidrain", 0);
22953a374c1SSascha Wildner ipmi_purge_completed_requests(dev);
23053a374c1SSascha Wildner }
23153a374c1SSascha Wildner }
23253a374c1SSascha Wildner sc->ipmi_opened--;
23353a374c1SSascha Wildner IPMI_UNLOCK(sc);
23453a374c1SSascha Wildner
23553a374c1SSascha Wildner /* Cleanup. */
23653a374c1SSascha Wildner kfree(dev, M_IPMI);
23753a374c1SSascha Wildner }
23853a374c1SSascha Wildner
23953a374c1SSascha Wildner #ifdef IPMB
24053a374c1SSascha Wildner static int
ipmi_ipmb_checksum(u_char * data,int len)24153a374c1SSascha Wildner ipmi_ipmb_checksum(u_char *data, int len)
24253a374c1SSascha Wildner {
24353a374c1SSascha Wildner u_char sum = 0;
24453a374c1SSascha Wildner
24553a374c1SSascha Wildner for (; len; len--) {
24653a374c1SSascha Wildner sum += *data++;
24753a374c1SSascha Wildner }
24853a374c1SSascha Wildner return (-sum);
24953a374c1SSascha Wildner }
25053a374c1SSascha Wildner
25153a374c1SSascha Wildner /* XXX: Needs work */
25253a374c1SSascha Wildner 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)25353a374c1SSascha Wildner ipmi_ipmb_send_message(device_t dev, u_char channel, u_char netfn,
25453a374c1SSascha Wildner u_char command, u_char seq, u_char *data, int data_len)
25553a374c1SSascha Wildner {
25653a374c1SSascha Wildner struct ipmi_softc *sc = device_get_softc(dev);
25753a374c1SSascha Wildner struct ipmi_request *req;
25853a374c1SSascha Wildner u_char slave_addr = 0x52;
25953a374c1SSascha Wildner int error;
26053a374c1SSascha Wildner
26153a374c1SSascha Wildner req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
26253a374c1SSascha Wildner IPMI_SEND_MSG, data_len + 8, 0);
26353a374c1SSascha Wildner req->ir_request[0] = channel;
26453a374c1SSascha Wildner req->ir_request[1] = slave_addr;
26553a374c1SSascha Wildner req->ir_request[2] = IPMI_ADDR(netfn, 0);
26653a374c1SSascha Wildner req->ir_request[3] = ipmi_ipmb_checksum(&req->ir_request[1], 2);
26753a374c1SSascha Wildner req->ir_request[4] = sc->ipmi_address;
26853a374c1SSascha Wildner req->ir_request[5] = IPMI_ADDR(seq, sc->ipmi_lun);
26953a374c1SSascha Wildner req->ir_request[6] = command;
27053a374c1SSascha Wildner
27153a374c1SSascha Wildner bcopy(data, &req->ir_request[7], data_len);
27253a374c1SSascha Wildner temp[data_len + 7] = ipmi_ipmb_checksum(&req->ir_request[4],
27353a374c1SSascha Wildner data_len + 3);
27453a374c1SSascha Wildner
27553a374c1SSascha Wildner ipmi_submit_driver_request(sc, req);
27653a374c1SSascha Wildner error = req->ir_error;
27753a374c1SSascha Wildner ipmi_free_request(req);
27853a374c1SSascha Wildner
27953a374c1SSascha Wildner return (error);
28053a374c1SSascha Wildner }
28153a374c1SSascha Wildner
28253a374c1SSascha Wildner static int
ipmi_handle_attn(struct ipmi_softc * sc)28353a374c1SSascha Wildner ipmi_handle_attn(struct ipmi_softc *sc)
28453a374c1SSascha Wildner {
28553a374c1SSascha Wildner struct ipmi_request *req;
28653a374c1SSascha Wildner int error;
28753a374c1SSascha Wildner
28853a374c1SSascha Wildner device_printf(sc->ipmi_dev, "BMC has a message\n");
28953a374c1SSascha Wildner req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
29053a374c1SSascha Wildner IPMI_GET_MSG_FLAGS, 0, 1);
29153a374c1SSascha Wildner
29253a374c1SSascha Wildner ipmi_submit_driver_request(sc, req);
29353a374c1SSascha Wildner
29453a374c1SSascha Wildner if (req->ir_error == 0 && req->ir_compcode == 0) {
29553a374c1SSascha Wildner if (req->ir_reply[0] & IPMI_MSG_BUFFER_FULL) {
29653a374c1SSascha Wildner device_printf(sc->ipmi_dev, "message buffer full");
29753a374c1SSascha Wildner }
29853a374c1SSascha Wildner if (req->ir_reply[0] & IPMI_WDT_PRE_TIMEOUT) {
29953a374c1SSascha Wildner device_printf(sc->ipmi_dev,
30053a374c1SSascha Wildner "watchdog about to go off");
30153a374c1SSascha Wildner }
30253a374c1SSascha Wildner if (req->ir_reply[0] & IPMI_MSG_AVAILABLE) {
30353a374c1SSascha Wildner ipmi_free_request(req);
30453a374c1SSascha Wildner
30553a374c1SSascha Wildner req = ipmi_alloc_driver_request(
30653a374c1SSascha Wildner IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_MSG, 0,
30753a374c1SSascha Wildner 16);
30853a374c1SSascha Wildner
30953a374c1SSascha Wildner device_printf(sc->ipmi_dev, "throw out message ");
31053a374c1SSascha Wildner dump_buf(temp, 16);
31153a374c1SSascha Wildner }
31253a374c1SSascha Wildner }
31353a374c1SSascha Wildner error = req->ir_error;
31453a374c1SSascha Wildner ipmi_free_request(req);
31553a374c1SSascha Wildner
31653a374c1SSascha Wildner return (error);
31753a374c1SSascha Wildner }
31853a374c1SSascha Wildner #endif
31953a374c1SSascha Wildner
32053a374c1SSascha Wildner #ifdef IPMICTL_SEND_COMMAND_32
32153a374c1SSascha Wildner #define PTRIN(p) ((void *)(uintptr_t)(p))
32253a374c1SSascha Wildner #define PTROUT(p) ((uintptr_t)(p))
32353a374c1SSascha Wildner #endif
32453a374c1SSascha Wildner
32553a374c1SSascha Wildner static int
ipmi_ioctl(struct dev_ioctl_args * ap)32653a374c1SSascha Wildner ipmi_ioctl(struct dev_ioctl_args *ap)
32753a374c1SSascha Wildner {
32853a374c1SSascha Wildner struct file *fp = ap->a_fp;
32953a374c1SSascha Wildner cdev_t cdev = ap->a_head.a_dev;
33053a374c1SSascha Wildner u_long cmd = ap->a_cmd;
33153a374c1SSascha Wildner caddr_t data = ap->a_data;
33253a374c1SSascha Wildner struct ipmi_softc *sc;
33353a374c1SSascha Wildner struct ipmi_device *dev;
33453a374c1SSascha Wildner struct ipmi_request *kreq;
33553a374c1SSascha Wildner struct ipmi_req *req = (struct ipmi_req *)data;
33653a374c1SSascha Wildner struct ipmi_recv *recv = (struct ipmi_recv *)data;
33753a374c1SSascha Wildner struct ipmi_addr addr;
33853a374c1SSascha Wildner #ifdef IPMICTL_SEND_COMMAND_32
33953a374c1SSascha Wildner struct ipmi_req32 *req32 = (struct ipmi_req32 *)data;
34053a374c1SSascha Wildner struct ipmi_recv32 *recv32 = (struct ipmi_recv32 *)data;
34153a374c1SSascha Wildner union {
34253a374c1SSascha Wildner struct ipmi_req req;
34353a374c1SSascha Wildner struct ipmi_recv recv;
34453a374c1SSascha Wildner } thunk32;
34553a374c1SSascha Wildner #endif
34653a374c1SSascha Wildner int error, len;
34753a374c1SSascha Wildner
34853a374c1SSascha Wildner error = devfs_get_cdevpriv(fp, (void **)&dev);
34953a374c1SSascha Wildner if (error)
35053a374c1SSascha Wildner return (error);
35153a374c1SSascha Wildner
35253a374c1SSascha Wildner sc = cdev->si_drv1;
35353a374c1SSascha Wildner
35453a374c1SSascha Wildner #ifdef IPMICTL_SEND_COMMAND_32
35553a374c1SSascha Wildner /* Convert 32-bit structures to native. */
35653a374c1SSascha Wildner switch (cmd) {
35753a374c1SSascha Wildner case IPMICTL_SEND_COMMAND_32:
35853a374c1SSascha Wildner req = &thunk32.req;
35953a374c1SSascha Wildner req->addr = PTRIN(req32->addr);
36053a374c1SSascha Wildner req->addr_len = req32->addr_len;
36153a374c1SSascha Wildner req->msgid = req32->msgid;
36253a374c1SSascha Wildner req->msg.netfn = req32->msg.netfn;
36353a374c1SSascha Wildner req->msg.cmd = req32->msg.cmd;
36453a374c1SSascha Wildner req->msg.data_len = req32->msg.data_len;
36553a374c1SSascha Wildner req->msg.data = PTRIN(req32->msg.data);
36653a374c1SSascha Wildner break;
36753a374c1SSascha Wildner case IPMICTL_RECEIVE_MSG_TRUNC_32:
36853a374c1SSascha Wildner case IPMICTL_RECEIVE_MSG_32:
36953a374c1SSascha Wildner recv = &thunk32.recv;
37053a374c1SSascha Wildner recv->addr = PTRIN(recv32->addr);
37153a374c1SSascha Wildner recv->addr_len = recv32->addr_len;
37253a374c1SSascha Wildner recv->msg.data_len = recv32->msg.data_len;
37353a374c1SSascha Wildner recv->msg.data = PTRIN(recv32->msg.data);
37453a374c1SSascha Wildner break;
37553a374c1SSascha Wildner }
37653a374c1SSascha Wildner #endif
37753a374c1SSascha Wildner
37853a374c1SSascha Wildner switch (cmd) {
37953a374c1SSascha Wildner #ifdef IPMICTL_SEND_COMMAND_32
38053a374c1SSascha Wildner case IPMICTL_SEND_COMMAND_32:
38153a374c1SSascha Wildner #endif
38253a374c1SSascha Wildner case IPMICTL_SEND_COMMAND:
38353a374c1SSascha Wildner /*
38453a374c1SSascha Wildner * XXX: Need to add proper handling of this.
38553a374c1SSascha Wildner */
38653a374c1SSascha Wildner error = copyin(req->addr, &addr, sizeof(addr));
38753a374c1SSascha Wildner if (error)
38853a374c1SSascha Wildner return (error);
38953a374c1SSascha Wildner
39053a374c1SSascha Wildner IPMI_LOCK(sc);
39153a374c1SSascha Wildner /* clear out old stuff in queue of stuff done */
39253a374c1SSascha Wildner /* XXX: This seems odd. */
39353a374c1SSascha Wildner while ((kreq = TAILQ_FIRST(&dev->ipmi_completed_requests))) {
39453a374c1SSascha Wildner TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq,
39553a374c1SSascha Wildner ir_link);
39653a374c1SSascha Wildner dev->ipmi_requests--;
39753a374c1SSascha Wildner ipmi_free_request(kreq);
39853a374c1SSascha Wildner }
39953a374c1SSascha Wildner IPMI_UNLOCK(sc);
40053a374c1SSascha Wildner
40153a374c1SSascha Wildner kreq = ipmi_alloc_request(dev, req->msgid,
40253a374c1SSascha Wildner IPMI_ADDR(req->msg.netfn, 0), req->msg.cmd,
40353a374c1SSascha Wildner req->msg.data_len, IPMI_MAX_RX);
40453a374c1SSascha Wildner error = copyin(req->msg.data, kreq->ir_request,
40553a374c1SSascha Wildner req->msg.data_len);
40653a374c1SSascha Wildner if (error) {
40753a374c1SSascha Wildner ipmi_free_request(kreq);
40853a374c1SSascha Wildner return (error);
40953a374c1SSascha Wildner }
41053a374c1SSascha Wildner IPMI_LOCK(sc);
41153a374c1SSascha Wildner dev->ipmi_requests++;
41253a374c1SSascha Wildner error = sc->ipmi_enqueue_request(sc, kreq);
41353a374c1SSascha Wildner IPMI_UNLOCK(sc);
41453a374c1SSascha Wildner if (error)
41553a374c1SSascha Wildner return (error);
41653a374c1SSascha Wildner break;
41753a374c1SSascha Wildner #ifdef IPMICTL_SEND_COMMAND_32
41853a374c1SSascha Wildner case IPMICTL_RECEIVE_MSG_TRUNC_32:
41953a374c1SSascha Wildner case IPMICTL_RECEIVE_MSG_32:
42053a374c1SSascha Wildner #endif
42153a374c1SSascha Wildner case IPMICTL_RECEIVE_MSG_TRUNC:
42253a374c1SSascha Wildner case IPMICTL_RECEIVE_MSG:
42353a374c1SSascha Wildner error = copyin(recv->addr, &addr, sizeof(addr));
42453a374c1SSascha Wildner if (error)
42553a374c1SSascha Wildner return (error);
42653a374c1SSascha Wildner
42753a374c1SSascha Wildner IPMI_LOCK(sc);
42853a374c1SSascha Wildner kreq = TAILQ_FIRST(&dev->ipmi_completed_requests);
42953a374c1SSascha Wildner if (kreq == NULL) {
43053a374c1SSascha Wildner IPMI_UNLOCK(sc);
43153a374c1SSascha Wildner return (EAGAIN);
43253a374c1SSascha Wildner }
43353a374c1SSascha Wildner addr.channel = IPMI_BMC_CHANNEL;
43453a374c1SSascha Wildner /* XXX */
43553a374c1SSascha Wildner recv->recv_type = IPMI_RESPONSE_RECV_TYPE;
43653a374c1SSascha Wildner recv->msgid = kreq->ir_msgid;
43753a374c1SSascha Wildner recv->msg.netfn = IPMI_REPLY_ADDR(kreq->ir_addr) >> 2;
43853a374c1SSascha Wildner recv->msg.cmd = kreq->ir_command;
43953a374c1SSascha Wildner error = kreq->ir_error;
44053a374c1SSascha Wildner if (error) {
44153a374c1SSascha Wildner TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq,
44253a374c1SSascha Wildner ir_link);
44353a374c1SSascha Wildner dev->ipmi_requests--;
44453a374c1SSascha Wildner IPMI_UNLOCK(sc);
44553a374c1SSascha Wildner ipmi_free_request(kreq);
44653a374c1SSascha Wildner return (error);
44753a374c1SSascha Wildner }
44853a374c1SSascha Wildner len = kreq->ir_replylen + 1;
44953a374c1SSascha Wildner if (recv->msg.data_len < len &&
45053a374c1SSascha Wildner (cmd == IPMICTL_RECEIVE_MSG
45153a374c1SSascha Wildner #ifdef IPMICTL_RECEIVE_MSG_32
45253a374c1SSascha Wildner || cmd == IPMICTL_RECEIVE_MSG_32
45353a374c1SSascha Wildner #endif
45453a374c1SSascha Wildner )) {
45553a374c1SSascha Wildner IPMI_UNLOCK(sc);
45653a374c1SSascha Wildner return (EMSGSIZE);
45753a374c1SSascha Wildner }
45853a374c1SSascha Wildner TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link);
45953a374c1SSascha Wildner dev->ipmi_requests--;
46053a374c1SSascha Wildner IPMI_UNLOCK(sc);
46153a374c1SSascha Wildner len = min(recv->msg.data_len, len);
46253a374c1SSascha Wildner recv->msg.data_len = len;
46353a374c1SSascha Wildner error = copyout(&addr, recv->addr,sizeof(addr));
46453a374c1SSascha Wildner if (error == 0)
46553a374c1SSascha Wildner error = copyout(&kreq->ir_compcode, recv->msg.data, 1);
46653a374c1SSascha Wildner if (error == 0)
46753a374c1SSascha Wildner error = copyout(kreq->ir_reply, recv->msg.data + 1,
46853a374c1SSascha Wildner len - 1);
46953a374c1SSascha Wildner ipmi_free_request(kreq);
47053a374c1SSascha Wildner if (error)
47153a374c1SSascha Wildner return (error);
47253a374c1SSascha Wildner break;
47353a374c1SSascha Wildner case IPMICTL_SET_MY_ADDRESS_CMD:
47453a374c1SSascha Wildner IPMI_LOCK(sc);
47553a374c1SSascha Wildner dev->ipmi_address = *(int*)data;
47653a374c1SSascha Wildner IPMI_UNLOCK(sc);
47753a374c1SSascha Wildner break;
47853a374c1SSascha Wildner case IPMICTL_GET_MY_ADDRESS_CMD:
47953a374c1SSascha Wildner IPMI_LOCK(sc);
48053a374c1SSascha Wildner *(int*)data = dev->ipmi_address;
48153a374c1SSascha Wildner IPMI_UNLOCK(sc);
48253a374c1SSascha Wildner break;
48353a374c1SSascha Wildner case IPMICTL_SET_MY_LUN_CMD:
48453a374c1SSascha Wildner IPMI_LOCK(sc);
48553a374c1SSascha Wildner dev->ipmi_lun = *(int*)data & 0x3;
48653a374c1SSascha Wildner IPMI_UNLOCK(sc);
48753a374c1SSascha Wildner break;
48853a374c1SSascha Wildner case IPMICTL_GET_MY_LUN_CMD:
48953a374c1SSascha Wildner IPMI_LOCK(sc);
49053a374c1SSascha Wildner *(int*)data = dev->ipmi_lun;
49153a374c1SSascha Wildner IPMI_UNLOCK(sc);
49253a374c1SSascha Wildner break;
49353a374c1SSascha Wildner case IPMICTL_SET_GETS_EVENTS_CMD:
49453a374c1SSascha Wildner /*
49553a374c1SSascha Wildner device_printf(sc->ipmi_dev,
49653a374c1SSascha Wildner "IPMICTL_SET_GETS_EVENTS_CMD NA\n");
49753a374c1SSascha Wildner */
49853a374c1SSascha Wildner break;
49953a374c1SSascha Wildner case IPMICTL_REGISTER_FOR_CMD:
50053a374c1SSascha Wildner case IPMICTL_UNREGISTER_FOR_CMD:
50153a374c1SSascha Wildner return (EOPNOTSUPP);
50253a374c1SSascha Wildner default:
50353a374c1SSascha Wildner device_printf(sc->ipmi_dev, "Unknown IOCTL %lX\n", cmd);
50453a374c1SSascha Wildner return (ENOIOCTL);
50553a374c1SSascha Wildner }
50653a374c1SSascha Wildner
50753a374c1SSascha Wildner #ifdef IPMICTL_SEND_COMMAND_32
50853a374c1SSascha Wildner /* Update changed fields in 32-bit structures. */
50953a374c1SSascha Wildner switch (cmd) {
51053a374c1SSascha Wildner case IPMICTL_RECEIVE_MSG_TRUNC_32:
51153a374c1SSascha Wildner case IPMICTL_RECEIVE_MSG_32:
51253a374c1SSascha Wildner recv32->recv_type = recv->recv_type;
51353a374c1SSascha Wildner recv32->msgid = recv->msgid;
51453a374c1SSascha Wildner recv32->msg.netfn = recv->msg.netfn;
51553a374c1SSascha Wildner recv32->msg.cmd = recv->msg.cmd;
51653a374c1SSascha Wildner recv32->msg.data_len = recv->msg.data_len;
51753a374c1SSascha Wildner break;
51853a374c1SSascha Wildner }
51953a374c1SSascha Wildner #endif
52053a374c1SSascha Wildner return (0);
52153a374c1SSascha Wildner }
52253a374c1SSascha Wildner
52353a374c1SSascha Wildner /*
52453a374c1SSascha Wildner * Request management.
52553a374c1SSascha Wildner */
52653a374c1SSascha Wildner
52753a374c1SSascha Wildner /* Allocate a new request with request and reply buffers. */
52853a374c1SSascha Wildner struct ipmi_request *
ipmi_alloc_request(struct ipmi_device * dev,long msgid,uint8_t addr,uint8_t command,size_t requestlen,size_t replylen)52953a374c1SSascha Wildner ipmi_alloc_request(struct ipmi_device *dev, long msgid, uint8_t addr,
53053a374c1SSascha Wildner uint8_t command, size_t requestlen, size_t replylen)
53153a374c1SSascha Wildner {
53253a374c1SSascha Wildner struct ipmi_request *req;
53353a374c1SSascha Wildner
53453a374c1SSascha Wildner req = kmalloc(sizeof(struct ipmi_request) + requestlen + replylen,
53553a374c1SSascha Wildner M_IPMI, M_WAITOK | M_ZERO);
53653a374c1SSascha Wildner req->ir_owner = dev;
53753a374c1SSascha Wildner req->ir_msgid = msgid;
53853a374c1SSascha Wildner req->ir_addr = addr;
53953a374c1SSascha Wildner req->ir_command = command;
54053a374c1SSascha Wildner if (requestlen) {
54153a374c1SSascha Wildner req->ir_request = (char *)&req[1];
54253a374c1SSascha Wildner req->ir_requestlen = requestlen;
54353a374c1SSascha Wildner }
54453a374c1SSascha Wildner if (replylen) {
54553a374c1SSascha Wildner req->ir_reply = (char *)&req[1] + requestlen;
54653a374c1SSascha Wildner req->ir_replybuflen = replylen;
54753a374c1SSascha Wildner }
54853a374c1SSascha Wildner return (req);
54953a374c1SSascha Wildner }
55053a374c1SSascha Wildner
55153a374c1SSascha Wildner /* Free a request no longer in use. */
55253a374c1SSascha Wildner void
ipmi_free_request(struct ipmi_request * req)55353a374c1SSascha Wildner ipmi_free_request(struct ipmi_request *req)
55453a374c1SSascha Wildner {
55553a374c1SSascha Wildner
55653a374c1SSascha Wildner kfree(req, M_IPMI);
55753a374c1SSascha Wildner }
55853a374c1SSascha Wildner
55953a374c1SSascha Wildner /* Store a processed request on the appropriate completion queue. */
56053a374c1SSascha Wildner void
ipmi_complete_request(struct ipmi_softc * sc,struct ipmi_request * req)56153a374c1SSascha Wildner ipmi_complete_request(struct ipmi_softc *sc, struct ipmi_request *req)
56253a374c1SSascha Wildner {
56353a374c1SSascha Wildner struct ipmi_device *dev;
56453a374c1SSascha Wildner
56553a374c1SSascha Wildner IPMI_LOCK_ASSERT(sc);
56653a374c1SSascha Wildner
56753a374c1SSascha Wildner /*
56853a374c1SSascha Wildner * Anonymous requests (from inside the driver) always have a
56953a374c1SSascha Wildner * waiter that we awaken.
57053a374c1SSascha Wildner */
57153a374c1SSascha Wildner if (req->ir_owner == NULL)
57253a374c1SSascha Wildner wakeup(req);
57353a374c1SSascha Wildner else {
57453a374c1SSascha Wildner dev = req->ir_owner;
57553a374c1SSascha Wildner TAILQ_INSERT_TAIL(&dev->ipmi_completed_requests, req, ir_link);
57653a374c1SSascha Wildner KNOTE(&sc->ipmi_kq.ki_note, 0);
57753a374c1SSascha Wildner if (dev->ipmi_closing)
57853a374c1SSascha Wildner wakeup(&dev->ipmi_requests);
57953a374c1SSascha Wildner }
58053a374c1SSascha Wildner }
58153a374c1SSascha Wildner
58253a374c1SSascha Wildner /* Enqueue an internal driver request and wait until it is completed. */
58353a374c1SSascha Wildner int
ipmi_submit_driver_request(struct ipmi_softc * sc,struct ipmi_request * req,int timo)58453a374c1SSascha Wildner ipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request *req,
58553a374c1SSascha Wildner int timo)
58653a374c1SSascha Wildner {
58753a374c1SSascha Wildner int error;
58853a374c1SSascha Wildner
58953a374c1SSascha Wildner IPMI_LOCK(sc);
59053a374c1SSascha Wildner error = sc->ipmi_enqueue_request(sc, req);
59153a374c1SSascha Wildner if (error == 0)
59253a374c1SSascha Wildner error = lksleep(req, &sc->ipmi_lock, 0, "ipmireq", timo);
59353a374c1SSascha Wildner if (error == 0)
59453a374c1SSascha Wildner error = req->ir_error;
59553a374c1SSascha Wildner IPMI_UNLOCK(sc);
59653a374c1SSascha Wildner return (error);
59753a374c1SSascha Wildner }
59853a374c1SSascha Wildner
59953a374c1SSascha Wildner /*
60053a374c1SSascha Wildner * Helper routine for polled system interfaces that use
60153a374c1SSascha Wildner * ipmi_polled_enqueue_request() to queue requests. This request
60253a374c1SSascha Wildner * waits until there is a pending request and then returns the first
60353a374c1SSascha Wildner * request. If the driver is shutting down, it returns NULL.
60453a374c1SSascha Wildner */
60553a374c1SSascha Wildner struct ipmi_request *
ipmi_dequeue_request(struct ipmi_softc * sc)60653a374c1SSascha Wildner ipmi_dequeue_request(struct ipmi_softc *sc)
60753a374c1SSascha Wildner {
60853a374c1SSascha Wildner struct ipmi_request *req;
60953a374c1SSascha Wildner
61053a374c1SSascha Wildner IPMI_LOCK_ASSERT(sc);
61153a374c1SSascha Wildner
61253a374c1SSascha Wildner while (!sc->ipmi_detaching && TAILQ_EMPTY(&sc->ipmi_pending_requests))
61353a374c1SSascha Wildner cv_wait(&sc->ipmi_request_added, &sc->ipmi_lock);
61453a374c1SSascha Wildner if (sc->ipmi_detaching)
61553a374c1SSascha Wildner return (NULL);
61653a374c1SSascha Wildner
61753a374c1SSascha Wildner req = TAILQ_FIRST(&sc->ipmi_pending_requests);
61853a374c1SSascha Wildner TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link);
61953a374c1SSascha Wildner return (req);
62053a374c1SSascha Wildner }
62153a374c1SSascha Wildner
62253a374c1SSascha Wildner /* Default implementation of ipmi_enqueue_request() for polled interfaces. */
62353a374c1SSascha Wildner int
ipmi_polled_enqueue_request(struct ipmi_softc * sc,struct ipmi_request * req)62453a374c1SSascha Wildner ipmi_polled_enqueue_request(struct ipmi_softc *sc, struct ipmi_request *req)
62553a374c1SSascha Wildner {
62653a374c1SSascha Wildner IPMI_LOCK_ASSERT(sc);
62753a374c1SSascha Wildner
62853a374c1SSascha Wildner TAILQ_INSERT_TAIL(&sc->ipmi_pending_requests, req, ir_link);
6297670f87fSMarkus Pfeiffer
63053a374c1SSascha Wildner cv_signal(&sc->ipmi_request_added);
63153a374c1SSascha Wildner return (0);
63253a374c1SSascha Wildner }
63353a374c1SSascha Wildner
63453a374c1SSascha Wildner /*
63553a374c1SSascha Wildner * Watchdog event handler.
63653a374c1SSascha Wildner */
63753a374c1SSascha Wildner static int
ipmi_set_watchdog(struct ipmi_softc * sc,unsigned int sec)63853a374c1SSascha Wildner ipmi_set_watchdog(struct ipmi_softc *sc, unsigned int sec)
63953a374c1SSascha Wildner {
64053a374c1SSascha Wildner struct ipmi_request *req;
64153a374c1SSascha Wildner int error;
64253a374c1SSascha Wildner
64353a374c1SSascha Wildner if (sec > 0xffff / 10)
64453a374c1SSascha Wildner return (EINVAL);
64553a374c1SSascha Wildner
64653a374c1SSascha Wildner req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
64753a374c1SSascha Wildner IPMI_SET_WDOG, 6, 0);
64853a374c1SSascha Wildner
64953a374c1SSascha Wildner if (sec) {
65053a374c1SSascha Wildner req->ir_request[0] = IPMI_SET_WD_TIMER_DONT_STOP
65153a374c1SSascha Wildner | IPMI_SET_WD_TIMER_SMS_OS;
65253a374c1SSascha Wildner req->ir_request[1] = IPMI_SET_WD_ACTION_RESET;
65353a374c1SSascha Wildner req->ir_request[2] = 0;
65453a374c1SSascha Wildner req->ir_request[3] = 0; /* Timer use */
65553a374c1SSascha Wildner req->ir_request[4] = (sec * 10) & 0xff;
65653a374c1SSascha Wildner req->ir_request[5] = (sec * 10) >> 8;
65753a374c1SSascha Wildner } else {
65853a374c1SSascha Wildner req->ir_request[0] = IPMI_SET_WD_TIMER_SMS_OS;
65953a374c1SSascha Wildner req->ir_request[1] = 0;
66053a374c1SSascha Wildner req->ir_request[2] = 0;
66153a374c1SSascha Wildner req->ir_request[3] = 0; /* Timer use */
66253a374c1SSascha Wildner req->ir_request[4] = 0;
66353a374c1SSascha Wildner req->ir_request[5] = 0;
66453a374c1SSascha Wildner }
66553a374c1SSascha Wildner error = ipmi_submit_driver_request(sc, req, 0);
66653a374c1SSascha Wildner if (error)
66753a374c1SSascha Wildner device_printf(sc->ipmi_dev, "Failed to set watchdog\n");
66853a374c1SSascha Wildner else if (sec) {
66953a374c1SSascha Wildner ipmi_free_request(req);
67053a374c1SSascha Wildner
67153a374c1SSascha Wildner req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
67253a374c1SSascha Wildner IPMI_RESET_WDOG, 0, 0);
67353a374c1SSascha Wildner
67453a374c1SSascha Wildner error = ipmi_submit_driver_request(sc, req, 0);
67553a374c1SSascha Wildner if (error)
67653a374c1SSascha Wildner device_printf(sc->ipmi_dev,
67753a374c1SSascha Wildner "Failed to reset watchdog\n");
67853a374c1SSascha Wildner }
67953a374c1SSascha Wildner
68053a374c1SSascha Wildner ipmi_free_request(req);
68153a374c1SSascha Wildner return (error);
68253a374c1SSascha Wildner /*
68353a374c1SSascha Wildner dump_watchdog(sc);
68453a374c1SSascha Wildner */
68553a374c1SSascha Wildner }
68653a374c1SSascha Wildner
6877670f87fSMarkus Pfeiffer static void
ipmi_watchdog(void * arg)6887670f87fSMarkus Pfeiffer ipmi_watchdog(void *arg)
68953a374c1SSascha Wildner {
6907670f87fSMarkus Pfeiffer struct ipmi_softc *sc = (struct ipmi_softc *)arg;
69153a374c1SSascha Wildner int e;
69253a374c1SSascha Wildner
6937670f87fSMarkus Pfeiffer if(sc->ipmi_wdog_period) {
6947670f87fSMarkus Pfeiffer e = ipmi_set_watchdog(sc, sc->ipmi_wdog_period + 1);
6957670f87fSMarkus Pfeiffer
69653a374c1SSascha Wildner if (e == 0)
69753a374c1SSascha Wildner sc->ipmi_watchdog_active = 1;
69853a374c1SSascha Wildner else
69953a374c1SSascha Wildner ipmi_set_watchdog(sc, 0);
7007670f87fSMarkus Pfeiffer
7017670f87fSMarkus Pfeiffer callout_reset(&sc->ipmi_watchdog,
7027670f87fSMarkus Pfeiffer sc->ipmi_wdog_period * hz,
7037670f87fSMarkus Pfeiffer &ipmi_watchdog, (void *)sc);
70453a374c1SSascha Wildner }
7057670f87fSMarkus Pfeiffer }
70653a374c1SSascha Wildner
70753a374c1SSascha Wildner static void
ipmi_startup(void * arg)70853a374c1SSascha Wildner ipmi_startup(void *arg)
70953a374c1SSascha Wildner {
71053a374c1SSascha Wildner struct ipmi_softc *sc = arg;
71153a374c1SSascha Wildner struct ipmi_request *req;
71253a374c1SSascha Wildner device_t dev;
71353a374c1SSascha Wildner int error, i;
71453a374c1SSascha Wildner
71553a374c1SSascha Wildner config_intrhook_disestablish(&sc->ipmi_ich);
71653a374c1SSascha Wildner dev = sc->ipmi_dev;
71753a374c1SSascha Wildner
71853a374c1SSascha Wildner /* Initialize interface-independent state. */
71953a374c1SSascha Wildner lockinit(&sc->ipmi_lock, device_get_nameunit(dev), 0, LK_CANRECURSE);
72053a374c1SSascha Wildner cv_init(&sc->ipmi_request_added, "ipmireq");
72153a374c1SSascha Wildner TAILQ_INIT(&sc->ipmi_pending_requests);
72253a374c1SSascha Wildner
72353a374c1SSascha Wildner /* Initialize interface-dependent state. */
72453a374c1SSascha Wildner error = sc->ipmi_startup(sc);
72553a374c1SSascha Wildner if (error) {
72653a374c1SSascha Wildner device_printf(dev, "Failed to initialize interface: %d\n",
72753a374c1SSascha Wildner error);
72853a374c1SSascha Wildner return;
72953a374c1SSascha Wildner }
73053a374c1SSascha Wildner
73153a374c1SSascha Wildner /* Send a GET_DEVICE_ID request. */
73253a374c1SSascha Wildner req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
73353a374c1SSascha Wildner IPMI_GET_DEVICE_ID, 0, 15);
73453a374c1SSascha Wildner
73553a374c1SSascha Wildner error = ipmi_submit_driver_request(sc, req, MAX_TIMEOUT);
73653a374c1SSascha Wildner if (error == EWOULDBLOCK) {
73753a374c1SSascha Wildner device_printf(dev, "Timed out waiting for GET_DEVICE_ID\n");
73853a374c1SSascha Wildner ipmi_free_request(req);
73953a374c1SSascha Wildner return;
74053a374c1SSascha Wildner } else if (error) {
74153a374c1SSascha Wildner device_printf(dev, "Failed GET_DEVICE_ID: %d\n", error);
74253a374c1SSascha Wildner ipmi_free_request(req);
74353a374c1SSascha Wildner return;
74453a374c1SSascha Wildner } else if (req->ir_compcode != 0) {
74553a374c1SSascha Wildner device_printf(dev,
74653a374c1SSascha Wildner "Bad completion code for GET_DEVICE_ID: %d\n",
74753a374c1SSascha Wildner req->ir_compcode);
74853a374c1SSascha Wildner ipmi_free_request(req);
74953a374c1SSascha Wildner return;
75053a374c1SSascha Wildner } else if (req->ir_replylen < 5) {
75153a374c1SSascha Wildner device_printf(dev, "Short reply for GET_DEVICE_ID: %d\n",
75253a374c1SSascha Wildner req->ir_replylen);
75353a374c1SSascha Wildner ipmi_free_request(req);
75453a374c1SSascha Wildner return;
75553a374c1SSascha Wildner }
75653a374c1SSascha Wildner
75753a374c1SSascha Wildner device_printf(dev, "IPMI device rev. %d, firmware rev. %d.%d%d, "
75853a374c1SSascha Wildner "version %d.%d\n",
75953a374c1SSascha Wildner req->ir_reply[1] & 0x0f,
76053a374c1SSascha Wildner req->ir_reply[2] & 0x7f, req->ir_reply[3] >> 4, req->ir_reply[3] & 0x0f,
76153a374c1SSascha Wildner req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4);
76253a374c1SSascha Wildner
76353a374c1SSascha Wildner ipmi_free_request(req);
76453a374c1SSascha Wildner
76553a374c1SSascha Wildner req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
76653a374c1SSascha Wildner IPMI_CLEAR_FLAGS, 1, 0);
76753a374c1SSascha Wildner
76853a374c1SSascha Wildner ipmi_submit_driver_request(sc, req, 0);
76953a374c1SSascha Wildner
77053a374c1SSascha Wildner /* XXX: Magic numbers */
77153a374c1SSascha Wildner if (req->ir_compcode == 0xc0) {
77253a374c1SSascha Wildner device_printf(dev, "Clear flags is busy\n");
77353a374c1SSascha Wildner }
77453a374c1SSascha Wildner if (req->ir_compcode == 0xc1) {
77553a374c1SSascha Wildner device_printf(dev, "Clear flags illegal\n");
77653a374c1SSascha Wildner }
77753a374c1SSascha Wildner ipmi_free_request(req);
77853a374c1SSascha Wildner
77953a374c1SSascha Wildner for (i = 0; i < 8; i++) {
78053a374c1SSascha Wildner req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
78153a374c1SSascha Wildner IPMI_GET_CHANNEL_INFO, 1, 0);
78253a374c1SSascha Wildner req->ir_request[0] = i;
78353a374c1SSascha Wildner
78453a374c1SSascha Wildner ipmi_submit_driver_request(sc, req, 0);
78553a374c1SSascha Wildner
78653a374c1SSascha Wildner if (req->ir_compcode != 0) {
78753a374c1SSascha Wildner ipmi_free_request(req);
78853a374c1SSascha Wildner break;
78953a374c1SSascha Wildner }
79053a374c1SSascha Wildner ipmi_free_request(req);
79153a374c1SSascha Wildner }
79253a374c1SSascha Wildner device_printf(dev, "Number of channels %d\n", i);
79353a374c1SSascha Wildner
79453a374c1SSascha Wildner /* probe for watchdog */
79553a374c1SSascha Wildner req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
79653a374c1SSascha Wildner IPMI_GET_WDOG, 0, 0);
79753a374c1SSascha Wildner
79853a374c1SSascha Wildner ipmi_submit_driver_request(sc, req, 0);
79953a374c1SSascha Wildner
80053a374c1SSascha Wildner if (req->ir_compcode == 0x00) {
8017670f87fSMarkus Pfeiffer struct sysctl_ctx_list *ctx;
8027670f87fSMarkus Pfeiffer struct sysctl_oid *tree;
8037670f87fSMarkus Pfeiffer struct sysctl_oid_list *child;
8047670f87fSMarkus Pfeiffer
80553a374c1SSascha Wildner device_printf(dev, "Attached watchdog\n");
80653a374c1SSascha Wildner /* register the watchdog event handler */
8077670f87fSMarkus Pfeiffer /* XXX profmakx: our wdog driver holds a spinlock while
8087670f87fSMarkus Pfeiffer running the watchdog function, but since the ipmi watchdog
8097670f87fSMarkus Pfeiffer function sleeps, this doesn't work. Hack something with
8107670f87fSMarkus Pfeiffer a callout */
8117670f87fSMarkus Pfeiffer callout_init(&sc->ipmi_watchdog);
8127670f87fSMarkus Pfeiffer sc->ipmi_wdog_enable = 0;
8137670f87fSMarkus Pfeiffer sc->ipmi_wdog_period = 30;
8147670f87fSMarkus Pfeiffer
8157670f87fSMarkus Pfeiffer ctx = device_get_sysctl_ctx(dev);
8167670f87fSMarkus Pfeiffer tree = device_get_sysctl_tree(dev);
8177670f87fSMarkus Pfeiffer child = SYSCTL_CHILDREN(tree);
8187670f87fSMarkus Pfeiffer
8197670f87fSMarkus Pfeiffer SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "watchdog_enable",
8207670f87fSMarkus Pfeiffer CTLTYPE_INT | CTLFLAG_RW, sc, 0,
8217670f87fSMarkus Pfeiffer ipmi_watchdog_sysctl_enable, "I",
8227670f87fSMarkus Pfeiffer "ipmi watchdog enable");
8237670f87fSMarkus Pfeiffer SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "watchdog_period",
8247670f87fSMarkus Pfeiffer CTLTYPE_INT | CTLFLAG_RW, sc, 0,
8257670f87fSMarkus Pfeiffer ipmi_watchdog_sysctl_period, "I",
8267670f87fSMarkus Pfeiffer "ipmi watchdog period");
8277670f87fSMarkus Pfeiffer
82853a374c1SSascha Wildner }
82953a374c1SSascha Wildner ipmi_free_request(req);
83053a374c1SSascha Wildner
83153a374c1SSascha Wildner sc->ipmi_cdev = make_dev(&ipmi_ops, device_get_unit(dev),
83253a374c1SSascha Wildner UID_ROOT, GID_OPERATOR, 0660, "ipmi%d", device_get_unit(dev));
83353a374c1SSascha Wildner if (sc->ipmi_cdev == NULL) {
83453a374c1SSascha Wildner device_printf(dev, "Failed to create cdev\n");
83553a374c1SSascha Wildner return;
83653a374c1SSascha Wildner }
83753a374c1SSascha Wildner sc->ipmi_cdev->si_drv1 = sc;
83853a374c1SSascha Wildner }
83953a374c1SSascha Wildner
84053a374c1SSascha Wildner int
ipmi_attach(device_t dev)84153a374c1SSascha Wildner ipmi_attach(device_t dev)
84253a374c1SSascha Wildner {
84353a374c1SSascha Wildner struct ipmi_softc *sc = device_get_softc(dev);
84453a374c1SSascha Wildner int error;
84553a374c1SSascha Wildner
84653a374c1SSascha Wildner if (sc->ipmi_irq_res != NULL && sc->ipmi_intr != NULL) {
84753a374c1SSascha Wildner error = bus_setup_intr(dev, sc->ipmi_irq_res, 0,
84853a374c1SSascha Wildner sc->ipmi_intr, sc, &sc->ipmi_irq, NULL);
84953a374c1SSascha Wildner if (error) {
85053a374c1SSascha Wildner device_printf(dev, "can't set up interrupt\n");
85153a374c1SSascha Wildner return (error);
85253a374c1SSascha Wildner }
85353a374c1SSascha Wildner }
85453a374c1SSascha Wildner
85553a374c1SSascha Wildner bzero(&sc->ipmi_ich, sizeof(struct intr_config_hook));
85653a374c1SSascha Wildner sc->ipmi_ich.ich_func = ipmi_startup;
85753a374c1SSascha Wildner sc->ipmi_ich.ich_arg = sc;
85853a374c1SSascha Wildner sc->ipmi_ich.ich_desc = "ipmi";
85953a374c1SSascha Wildner if (config_intrhook_establish(&sc->ipmi_ich) != 0) {
86053a374c1SSascha Wildner device_printf(dev, "can't establish configuration hook\n");
86153a374c1SSascha Wildner return (ENOMEM);
86253a374c1SSascha Wildner }
86353a374c1SSascha Wildner
86453a374c1SSascha Wildner ipmi_attached = 1;
86553a374c1SSascha Wildner return (0);
86653a374c1SSascha Wildner }
86753a374c1SSascha Wildner
86853a374c1SSascha Wildner int
ipmi_detach(device_t dev)86953a374c1SSascha Wildner ipmi_detach(device_t dev)
87053a374c1SSascha Wildner {
87153a374c1SSascha Wildner struct ipmi_softc *sc;
87253a374c1SSascha Wildner
87353a374c1SSascha Wildner sc = device_get_softc(dev);
87453a374c1SSascha Wildner
87553a374c1SSascha Wildner /* Fail if there are any open handles. */
87653a374c1SSascha Wildner IPMI_LOCK(sc);
87753a374c1SSascha Wildner if (sc->ipmi_opened) {
87853a374c1SSascha Wildner IPMI_UNLOCK(sc);
87953a374c1SSascha Wildner return (EBUSY);
88053a374c1SSascha Wildner }
88153a374c1SSascha Wildner IPMI_UNLOCK(sc);
88253a374c1SSascha Wildner if (sc->ipmi_cdev)
88353a374c1SSascha Wildner destroy_dev(sc->ipmi_cdev);
88453a374c1SSascha Wildner
88553a374c1SSascha Wildner /* Detach from watchdog handling and turn off watchdog. */
886eb67213aSMatthew Dillon callout_cancel(&sc->ipmi_watchdog);
88753a374c1SSascha Wildner ipmi_set_watchdog(sc, 0);
88853a374c1SSascha Wildner
88953a374c1SSascha Wildner /* XXX: should use shutdown callout I think. */
89053a374c1SSascha Wildner /* If the backend uses a kthread, shut it down. */
89153a374c1SSascha Wildner IPMI_LOCK(sc);
89253a374c1SSascha Wildner sc->ipmi_detaching = 1;
89353a374c1SSascha Wildner if (sc->ipmi_kthread) {
89453a374c1SSascha Wildner cv_broadcast(&sc->ipmi_request_added);
89553a374c1SSascha Wildner lksleep(sc->ipmi_kthread, &sc->ipmi_lock, 0, "ipmi_wait", 0);
89653a374c1SSascha Wildner }
89753a374c1SSascha Wildner IPMI_UNLOCK(sc);
89853a374c1SSascha Wildner if (sc->ipmi_irq)
89953a374c1SSascha Wildner bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq);
90053a374c1SSascha Wildner
90153a374c1SSascha Wildner ipmi_release_resources(dev);
90253a374c1SSascha Wildner lockuninit(&sc->ipmi_lock);
90353a374c1SSascha Wildner return (0);
90453a374c1SSascha Wildner }
90553a374c1SSascha Wildner
90653a374c1SSascha Wildner void
ipmi_release_resources(device_t dev)90753a374c1SSascha Wildner ipmi_release_resources(device_t dev)
90853a374c1SSascha Wildner {
90953a374c1SSascha Wildner struct ipmi_softc *sc;
91053a374c1SSascha Wildner int i;
91153a374c1SSascha Wildner
91253a374c1SSascha Wildner sc = device_get_softc(dev);
91353a374c1SSascha Wildner if (sc->ipmi_irq)
91453a374c1SSascha Wildner bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq);
91553a374c1SSascha Wildner if (sc->ipmi_irq_res)
91653a374c1SSascha Wildner bus_release_resource(dev, SYS_RES_IRQ, sc->ipmi_irq_rid,
91753a374c1SSascha Wildner sc->ipmi_irq_res);
91853a374c1SSascha Wildner for (i = 0; i < MAX_RES; i++)
91953a374c1SSascha Wildner if (sc->ipmi_io_res[i])
92053a374c1SSascha Wildner bus_release_resource(dev, sc->ipmi_io_type,
92153a374c1SSascha Wildner sc->ipmi_io_rid + i, sc->ipmi_io_res[i]);
92253a374c1SSascha Wildner }
92353a374c1SSascha Wildner
92453a374c1SSascha Wildner devclass_t ipmi_devclass;
92553a374c1SSascha Wildner
92653a374c1SSascha Wildner /* XXX: Why? */
92753a374c1SSascha Wildner static void
ipmi_unload(void * arg)92853a374c1SSascha Wildner ipmi_unload(void *arg)
92953a374c1SSascha Wildner {
93053a374c1SSascha Wildner device_t * devs;
93153a374c1SSascha Wildner int count;
93253a374c1SSascha Wildner int i;
93353a374c1SSascha Wildner
93453a374c1SSascha Wildner if (devclass_get_devices(ipmi_devclass, &devs, &count) != 0)
93553a374c1SSascha Wildner return;
93653a374c1SSascha Wildner for (i = 0; i < count; i++)
93753a374c1SSascha Wildner device_delete_child(device_get_parent(devs[i]), devs[i]);
93853a374c1SSascha Wildner kfree(devs, M_TEMP);
93953a374c1SSascha Wildner }
94053a374c1SSascha Wildner SYSUNINIT(ipmi_unload, SI_SUB_DRIVERS, SI_ORDER_FIRST, ipmi_unload, NULL);
94153a374c1SSascha Wildner
9427670f87fSMarkus Pfeiffer static int
ipmi_watchdog_sysctl_enable(SYSCTL_HANDLER_ARGS)9437670f87fSMarkus Pfeiffer ipmi_watchdog_sysctl_enable(SYSCTL_HANDLER_ARGS)
9447670f87fSMarkus Pfeiffer {
9457670f87fSMarkus Pfeiffer struct ipmi_softc *sc;
9467670f87fSMarkus Pfeiffer int enable;
9477670f87fSMarkus Pfeiffer int error;
9487670f87fSMarkus Pfeiffer
9497670f87fSMarkus Pfeiffer sc = oidp->oid_arg1;
9507670f87fSMarkus Pfeiffer
9517670f87fSMarkus Pfeiffer IPMI_LOCK(sc);
9527670f87fSMarkus Pfeiffer enable = sc->ipmi_wdog_enable;
9537670f87fSMarkus Pfeiffer IPMI_UNLOCK(sc);
9547670f87fSMarkus Pfeiffer
9557670f87fSMarkus Pfeiffer error = sysctl_handle_int(oidp, &enable, 0, req);
9567670f87fSMarkus Pfeiffer if(error || req->newptr == NULL)
9577670f87fSMarkus Pfeiffer return error;
9587670f87fSMarkus Pfeiffer
9597670f87fSMarkus Pfeiffer IPMI_LOCK(sc);
9607670f87fSMarkus Pfeiffer sc->ipmi_wdog_enable = enable;
9617670f87fSMarkus Pfeiffer IPMI_UNLOCK(sc);
9627670f87fSMarkus Pfeiffer
9637670f87fSMarkus Pfeiffer if (sc->ipmi_wdog_enable==0) {
9647670f87fSMarkus Pfeiffer callout_stop(&sc->ipmi_watchdog);
9657670f87fSMarkus Pfeiffer ipmi_set_watchdog(sc, 0);
9667670f87fSMarkus Pfeiffer } else {
9677670f87fSMarkus Pfeiffer callout_reset(&sc->ipmi_watchdog,
9687670f87fSMarkus Pfeiffer sc->ipmi_wdog_period * hz,
9697670f87fSMarkus Pfeiffer &ipmi_watchdog, (void *)sc);
9707670f87fSMarkus Pfeiffer ipmi_set_watchdog(sc, sc->ipmi_wdog_period + 1);
9717670f87fSMarkus Pfeiffer }
9727670f87fSMarkus Pfeiffer return 0;
9737670f87fSMarkus Pfeiffer }
9747670f87fSMarkus Pfeiffer
9757670f87fSMarkus Pfeiffer static int
ipmi_watchdog_sysctl_period(SYSCTL_HANDLER_ARGS)9767670f87fSMarkus Pfeiffer ipmi_watchdog_sysctl_period(SYSCTL_HANDLER_ARGS)
9777670f87fSMarkus Pfeiffer {
9787670f87fSMarkus Pfeiffer struct ipmi_softc *sc;
9797670f87fSMarkus Pfeiffer int error;
9807670f87fSMarkus Pfeiffer int period;
9817670f87fSMarkus Pfeiffer
9827670f87fSMarkus Pfeiffer sc = oidp->oid_arg1;
9837670f87fSMarkus Pfeiffer
9847670f87fSMarkus Pfeiffer IPMI_LOCK(sc);
9857670f87fSMarkus Pfeiffer period = sc->ipmi_wdog_period;
9867670f87fSMarkus Pfeiffer IPMI_UNLOCK(sc);
9877670f87fSMarkus Pfeiffer
9887670f87fSMarkus Pfeiffer error = sysctl_handle_int(oidp, &period, 30, req);
9897670f87fSMarkus Pfeiffer
9907670f87fSMarkus Pfeiffer if (error || req->newptr == NULL)
9917670f87fSMarkus Pfeiffer return error;
9927670f87fSMarkus Pfeiffer
9937670f87fSMarkus Pfeiffer IPMI_LOCK(sc);
9947670f87fSMarkus Pfeiffer sc->ipmi_wdog_period = period;
9957670f87fSMarkus Pfeiffer IPMI_UNLOCK(sc);
9967670f87fSMarkus Pfeiffer
9977670f87fSMarkus Pfeiffer return 0;
9987670f87fSMarkus Pfeiffer }
9997670f87fSMarkus Pfeiffer
100053a374c1SSascha Wildner #ifdef IMPI_DEBUG
100153a374c1SSascha Wildner static void
dump_buf(u_char * data,int len)100253a374c1SSascha Wildner dump_buf(u_char *data, int len)
100353a374c1SSascha Wildner {
100453a374c1SSascha Wildner char buf[20];
100553a374c1SSascha Wildner char line[1024];
100653a374c1SSascha Wildner char temp[30];
100753a374c1SSascha Wildner int count = 0;
100853a374c1SSascha Wildner int i=0;
100953a374c1SSascha Wildner
101053a374c1SSascha Wildner printf("Address %p len %d\n", data, len);
101153a374c1SSascha Wildner if (len > 256)
101253a374c1SSascha Wildner len = 256;
101353a374c1SSascha Wildner line[0] = '\000';
101453a374c1SSascha Wildner for (; len > 0; len--, data++) {
101553a374c1SSascha Wildner sprintf(temp, "%02x ", *data);
101653a374c1SSascha Wildner strcat(line, temp);
101753a374c1SSascha Wildner if (*data >= ' ' && *data <= '~')
101853a374c1SSascha Wildner buf[count] = *data;
101953a374c1SSascha Wildner else if (*data >= 'A' && *data <= 'Z')
102053a374c1SSascha Wildner buf[count] = *data;
102153a374c1SSascha Wildner else
102253a374c1SSascha Wildner buf[count] = '.';
102353a374c1SSascha Wildner if (++count == 16) {
102453a374c1SSascha Wildner buf[count] = '\000';
102553a374c1SSascha Wildner count = 0;
102653a374c1SSascha Wildner printf(" %3x %s %s\n", i, line, buf);
102753a374c1SSascha Wildner i+=16;
102853a374c1SSascha Wildner line[0] = '\000';
102953a374c1SSascha Wildner }
103053a374c1SSascha Wildner }
103153a374c1SSascha Wildner buf[count] = '\000';
103253a374c1SSascha Wildner
103353a374c1SSascha Wildner for (; count != 16; count++) {
103453a374c1SSascha Wildner strcat(line, " ");
103553a374c1SSascha Wildner }
103653a374c1SSascha Wildner printf(" %3x %s %s\n", i, line, buf);
103753a374c1SSascha Wildner }
103853a374c1SSascha Wildner #endif
1039