diff options
| author | John Baldwin <jhb@FreeBSD.org> | 2006-09-29 21:21:53 +0000 |
|---|---|---|
| committer | John Baldwin <jhb@FreeBSD.org> | 2006-09-29 21:21:53 +0000 |
| commit | 5cda389395c4b7e6ddd7518540113d908601c54e (patch) | |
| tree | f4adf15509903d57db73728dd2ef55ee0cf20cef /sys/dev/ipmi | |
| parent | 730195649de6b3bfe879688220820303d221df97 (diff) | |
Notes
Diffstat (limited to 'sys/dev/ipmi')
| -rw-r--r-- | sys/dev/ipmi/ipmi.c | 1427 | ||||
| -rw-r--r-- | sys/dev/ipmi/ipmi_pci.c | 414 | ||||
| -rw-r--r-- | sys/dev/ipmi/ipmi_smbios.c | 504 | ||||
| -rw-r--r-- | sys/dev/ipmi/ipmivars.h | 210 |
4 files changed, 1174 insertions, 1381 deletions
diff --git a/sys/dev/ipmi/ipmi.c b/sys/dev/ipmi/ipmi.c index 38e7a218a53c..fb1da53bad2f 100644 --- a/sys/dev/ipmi/ipmi.c +++ b/sys/dev/ipmi/ipmi.c @@ -29,20 +29,17 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/bus.h> +#include <sys/condvar.h> +#include <sys/conf.h> #include <sys/kernel.h> #include <sys/malloc.h> -#include <sys/poll.h> -#include <sys/selinfo.h> - -#include <sys/disk.h> #include <sys/module.h> -#include <sys/bus.h> - -#include <machine/bus.h> -#include <machine/resource.h> +#include <sys/poll.h> #include <sys/rman.h> -#include <sys/watchdog.h> +#include <sys/selinfo.h> #include <sys/sysctl.h> +#include <sys/watchdog.h> #ifdef LOCAL_MODULE #include <ipmi.h> @@ -52,29 +49,7 @@ __FBSDID("$FreeBSD$"); #include <dev/ipmi/ipmivars.h> #endif -struct ipmi_done_list { - u_char *data; - int channel; - int msgid; - int len; - TAILQ_ENTRY(ipmi_done_list) list; -}; - -#define MAX_TIMEOUT 3 * hz - -static int ipmi_wait_for_ibf(device_t, int); -static int ipmi_wait_for_obf(device_t, int); -static void ipmi_clear_obf(device_t, int); -static void ipmi_error(device_t); -static void ipmi_check_read(device_t); -static int ipmi_write(device_t, u_char *, int); -static void ipmi_wait_for_tx_okay(device_t); -static void ipmi_wait_for_rx_okay(device_t); -static void ipmi_wait_for_not_busy(device_t); -static void ipmi_set_busy(device_t); -static int ipmi_ready_to_read(device_t); #ifdef IPMB -static int ipmi_handle_attn(device_t dev); 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) @@ -92,11 +67,10 @@ int ipmi_attached = 0; static int on = 1; SYSCTL_NODE(_hw, OID_AUTO, ipmi, CTLFLAG_RD, 0, "IPMI driver parameters"); SYSCTL_INT(_hw_ipmi, OID_AUTO, on, CTLFLAG_RW, - &on, 0, ""); + &on, 0, ""); static struct cdevsw ipmi_cdevsw = { .d_version = D_VERSION, - .d_flags = D_NEEDGIANT, .d_open = ipmi_open, .d_close = ipmi_close, .d_ioctl = ipmi_ioctl, @@ -106,60 +80,126 @@ static struct cdevsw ipmi_cdevsw = { MALLOC_DEFINE(M_IPMI, "ipmi", "ipmi"); -static int -ipmi_open(struct cdev *dev, int flags, int fmt, struct thread *td) +static int +ipmi_open(struct cdev *cdev, int flags, int fmt, struct thread *td) { + struct ipmi_device *dev; struct ipmi_softc *sc; if (!on) - return ENOENT; + return (ENOENT); - sc = dev->si_drv1; - if (sc->ipmi_refcnt) { - return EBUSY; + dev = cdev->si_drv1; + sc = dev->ipmi_softc; + IPMI_LOCK(sc); + if (dev->ipmi_open) { + IPMI_UNLOCK(sc); + return (EBUSY); } - sc->ipmi_refcnt = 1; + dev->ipmi_open = 1; + IPMI_UNLOCK(sc); - return 0; + return (0); } -static int -ipmi_poll(struct cdev *dev, int poll_events, struct thread *td) +static int +ipmi_poll(struct cdev *cdev, int poll_events, struct thread *td) { + struct ipmi_device *dev; struct ipmi_softc *sc; int revents = 0; - sc = dev->si_drv1; - - ipmi_check_read(sc->ipmi_dev); + dev = cdev->si_drv1; + sc = dev->ipmi_softc; + IPMI_LOCK(sc); if (poll_events & (POLLIN | POLLRDNORM)) { - if (!TAILQ_EMPTY(&sc->ipmi_done)) + if (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) revents |= poll_events & (POLLIN | POLLRDNORM); - if (TAILQ_EMPTY(&sc->ipmi_done) && sc->ipmi_requests == 0) { + if (dev->ipmi_requests == 0) revents |= POLLERR; - } } if (revents == 0) { if (poll_events & (POLLIN | POLLRDNORM)) - selrecord(td, &sc->ipmi_select); + selrecord(td, &dev->ipmi_select); } + IPMI_UNLOCK(sc); + + return (revents); +} - return revents; +static void +ipmi_purge_completed_requests(struct ipmi_device *dev) +{ + struct ipmi_request *req; + + while (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) { + req = TAILQ_FIRST(&dev->ipmi_completed_requests); + TAILQ_REMOVE(&dev->ipmi_completed_requests, req, ir_link); + dev->ipmi_requests--; + ipmi_free_request(req); + } } -static int -ipmi_close(struct cdev *dev, int flags, int fmt, struct thread *td) +static int +ipmi_close(struct cdev *cdev, int flags, int fmt, struct thread *td) { + struct ipmi_request *req, *nreq; + struct ipmi_device *dev; struct ipmi_softc *sc; - int error = 0; +#ifdef CLONING + int bit; +#endif - sc = dev->si_drv1; + dev = cdev->si_drv1; + sc = dev->ipmi_softc; - sc->ipmi_refcnt = 0; + IPMI_LOCK(sc); + if (dev->ipmi_requests) { + /* Throw away any pending requests for this device. */ + TAILQ_FOREACH_SAFE(req, &sc->ipmi_pending_requests, ir_link, + nreq) { + if (req->ir_owner == dev) { + TAILQ_REMOVE(&sc->ipmi_pending_requests, req, + ir_link); + dev->ipmi_requests--; + ipmi_free_request(req); + } + } - return error; + /* Throw away any pending completed requests for this device. */ + ipmi_purge_completed_requests(dev); + + /* + * If we still have outstanding requests, they must be stuck + * in an interface driver, so wait for those to drain. + */ + dev->ipmi_closing = 1; + while (dev->ipmi_requests > 0) { + msleep(&dev->ipmi_requests, &sc->ipmi_lock, PWAIT, + "ipmidrain", 0); + ipmi_purge_completed_requests(dev); + } + } + +#ifdef CLONING + /* Detach this sub-device from the main driver. */ + bit = minor(cdev) % 32; + sc->ipmi_cdev_mask &= ~(1 << bit); + TAILQ_REMOVE(&sc->ipmi_cdevs, dev, ipmi_link); + IPMI_UNLOCK(sc); + + /* Cleanup. */ + cdev->si_drv1 = NULL; + free(dev, M_IPMI); + destroy_dev(cdev); +#else + dev->ipmi_open = 0; + IPMI_UNLOCK(sc); +#endif + + return (0); } #ifdef IPMB @@ -171,707 +211,384 @@ ipmi_ipmb_checksum(u_char *data, int len) for (; len; len--) { sum += *data++; } - return -sum; + 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) { - u_char *temp; struct ipmi_softc *sc = device_get_softc(dev); - int error; + struct ipmi_request *req; u_char slave_addr = 0x52; + int error; - temp = malloc(data_len + 10, M_IPMI, M_WAITOK); - bzero(temp, data_len + 10); - temp[0] = IPMI_APP_REQUEST << 2; - temp[1] = IPMI_SEND_MSG; - temp[2] = channel; - temp[3] = slave_addr; - temp[4] = netfn << 2; - temp[5] = ipmi_ipmb_check_sum(&temp[3], 2); - temp[6] = sc->ipmi_address; - temp[7] = seq << 2 | sc->ipmi_lun; - temp[8] = command; + req = ipmi_alloc_driver_request(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, &temp[9], data_len); - temp[data_len + 9] = ipmi_ipmb_check(&temp[6], data_len + 3); - ipmi_write(sc->ipmi_dev, temp, data_len + 9); - free(temp, M_IPMI); + bcopy(data, &req->ir_request[7], data_len); + temp[data_len + 7] = ipmi_ipmb_checksum(&req->ir_request[4], + data_len + 3); - while (!ipmi_ready_to_read(dev)) - DELAY(1000); - temp = malloc(IPMI_MAX_RX, M_IPMI, M_WAITOK); - bzero(temp, IPMI_MAX_RX); - error = ipmi_read(dev, temp, IPMI_MAX_RX); - free(temp, M_IPMI); + ipmi_submit_driver_request(sc, req); + error = req->ir_error; + ipmi_free_request(req); - return error; + return (error); } static int -ipmi_handle_attn(device_t dev) +ipmi_handle_attn(struct ipmi_softc *sc) { - u_char temp[IPMI_MAX_RX]; - struct ipmi_softc *sc = device_get_softc(dev); + struct ipmi_request *req; int error; device_printf(sc->ipmi_dev, "BMC has a message\n"); - temp[0] = IPMI_APP_REQUEST << 2; - temp[1] = IPMI_GET_MSG_FLAGS; - ipmi_write(sc->ipmi_dev, temp, 2); - while (!ipmi_ready_to_read(dev)) - DELAY(1000); - bzero(temp, IPMI_MAX_RX); - error = ipmi_read(dev, temp, IPMI_MAX_RX); + req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), + IPMI_GET_MSG_FLAGS, 0, 1); + + ipmi_submit_driver_request(sc, req); - if (temp[2] == 0) { - if (temp[3] & IPMI_MSG_BUFFER_FULL) { + 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 (temp[3] & IPMI_WDT_PRE_TIMEOUT) { + if (req->ir_reply[0] & IPMI_WDT_PRE_TIMEOUT) { device_printf(sc->ipmi_dev, "watchdog about to go off"); } - if (temp[3] & IPMI_MSG_AVAILABLE) { - temp[0] = IPMI_APP_REQUEST << 2; - temp[1] = IPMI_GET_MSG; - ipmi_write(sc->ipmi_dev, temp, 2); - while (!ipmi_ready_to_read(dev)) - DELAY(1000); - bzero(temp, IPMI_MAX_RX); - error = ipmi_read(dev, temp, IPMI_MAX_RX); + if (req->ir_reply[0] & IPMI_MSG_AVAILABLE) { + ipmi_free_request(req); + + req = ipmi_alloc_driver_request( + IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_MSG, 0, + 16); device_printf(sc->ipmi_dev, "throw out message "); dump_buf(temp, 16); } - } else - return -1; - return error; -} -#endif - -static int -ipmi_ready_to_read(device_t dev) -{ - struct ipmi_softc *sc = device_get_softc(dev); - int status, flags; - - if (sc->ipmi_bios_info.smic_mode) { - flags = INB(sc, sc->ipmi_smic_flags); -#ifdef IPMB - if (flags & SMIC_STATUS_SMS_ATN) { - ipmi_handle_attn(dev); - return 0; - } -#endif - if (flags & SMIC_STATUS_RX_RDY) - return 1; - } else if (sc->ipmi_bios_info.kcs_mode) { - status = INB(sc, sc->ipmi_kcs_status_reg); -#ifdef IPMB - if (status & KCS_STATUS_SMS_ATN) { - ipmi_handle_attn(dev); - return 0; - } -#endif - if (status & KCS_STATUS_OBF) - return 1; - } else { - device_printf(dev,"Unsupported mode\n"); } + error = req->ir_error; + ipmi_free_request(req); - return 0; -} - -void -ipmi_intr(void *arg) { - device_t dev = arg; - - ipmi_check_read(dev); + return (error); } +#endif -static void -ipmi_check_read(device_t dev){ - struct ipmi_softc *sc = device_get_softc(dev); - struct ipmi_done_list *item; - int status; - u_char *temp; - - if (!sc->ipmi_requests) - return; - - untimeout((timeout_t *)ipmi_check_read, dev, sc->ipmi_timeout_handle); - - if(ipmi_ready_to_read(dev)) { - sc->ipmi_requests--; - temp = malloc(IPMI_MAX_RX, M_IPMI, M_WAITOK); - bzero(temp, IPMI_MAX_RX); - status = ipmi_read(dev, temp, IPMI_MAX_RX); - item = malloc(sizeof(struct ipmi_done_list), M_IPMI, M_WAITOK); - bzero(item, sizeof(struct ipmi_done_list)); - item->data = temp; - item->len = status; - if (ticks - sc->ipmi_timestamp > MAX_TIMEOUT) { - device_printf(dev, "read timeout when ready\n"); - TAILQ_INSERT_TAIL(&sc->ipmi_done, item, list); - selwakeup(&sc->ipmi_select); - } else if (status) { - TAILQ_INSERT_TAIL(&sc->ipmi_done, item, list); - selwakeup(&sc->ipmi_select); - } - } else { - if (ticks - sc->ipmi_timestamp > MAX_TIMEOUT) { - sc->ipmi_requests--; - device_printf(dev, "read timeout when not ready\n"); - temp = malloc(IPMI_MAX_RX, M_IPMI, M_WAITOK); - bzero(temp, IPMI_MAX_RX); - sc->ipmi_busy = 0; - wakeup(&sc->ipmi_busy); - status = -1; - item = malloc(sizeof(struct ipmi_done_list), - M_IPMI, M_WAITOK); - bzero(item, sizeof(struct ipmi_done_list)); - item->data = temp; - item->len = status; - TAILQ_INSERT_TAIL(&sc->ipmi_done, item, list); - selwakeup(&sc->ipmi_select); - } - } - if (sc->ipmi_requests) - sc->ipmi_timeout_handle - = timeout((timeout_t *)ipmi_check_read, dev, hz/30); -} +#ifdef IPMICTL_SEND_COMMAND_32 +#define PTRIN(p) ((void *)(uintptr_t)(p)) +#define PTROUT(p) ((uintptr_t)(p)) +#endif static int -ipmi_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data, +ipmi_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int flags, struct thread *td) { struct ipmi_softc *sc; + struct ipmi_device *dev; + struct ipmi_request *kreq; struct ipmi_req *req = (struct ipmi_req *)data; struct ipmi_recv *recv = (struct ipmi_recv *)data; struct ipmi_addr addr; - struct ipmi_done_list *item; - u_char *temp; +#ifdef IPMICTL_SEND_COMMAND_32 + struct ipmi_req32 *req32 = (struct ipmi_req32 *)data; + struct ipmi_recv32 *recv32 = (struct ipmi_recv32 *)data; + union { + struct ipmi_req req; + struct ipmi_recv recv; + } thunk32; +#endif int error, len; - sc = dev->si_drv1; + dev = cdev->si_drv1; + sc = dev->ipmi_softc; + +#ifdef IPMICTL_SEND_COMMAND_32 + /* Convert 32-bit structures to native. */ + switch (cmd) { + case IPMICTL_SEND_COMMAND_32: + req = &thunk32.req; + req->addr = PTRIN(req32->addr); + req->addr_len = req32->addr_len; + req->msgid = req32->msgid; + req->msg.netfn = req32->msg.netfn; + req->msg.cmd = req32->msg.cmd; + req->msg.data_len = req32->msg.data_len; + req->msg.data = PTRIN(req32->msg.data); + break; + case IPMICTL_RECEIVE_MSG_TRUNC_32: + case IPMICTL_RECEIVE_MSG_32: + recv = &thunk32.recv; + recv->addr = PTRIN(recv32->addr); + recv->addr_len = recv32->addr_len; + recv->msg.data_len = recv32->msg.data_len; + recv->msg.data = PTRIN(recv32->msg.data); + break; + } +#endif switch (cmd) { +#ifdef IPMICTL_SEND_COMMAND_32 + 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 */ - while((item = TAILQ_FIRST(&sc->ipmi_done))) { - TAILQ_REMOVE(&sc->ipmi_done, item, list); - free(item->data, M_IPMI); - free(item, M_IPMI); + /* XXX: This seems odd. */ + while ((kreq = TAILQ_FIRST(&dev->ipmi_completed_requests))) { + TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, + ir_link); + dev->ipmi_requests--; + ipmi_free_request(kreq); } + IPMI_UNLOCK(sc); - error = copyin(req->addr, &addr, sizeof(addr)); - temp = malloc(req->msg.data_len + 2, M_IPMI, M_WAITOK); - if (temp == NULL) { - return ENOMEM; - } - temp[0] = req->msg.netfn << 2; - temp[1] = req->msg.cmd; - error = copyin(req->msg.data, &temp[2], + 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 != 0) { - free(temp, M_IPMI); - return error; + if (error) { + ipmi_free_request(kreq); + return (error); } - error = ipmi_write(sc->ipmi_dev, - temp, req->msg.data_len + 2); - free(temp, M_IPMI); - - if (error != 1) - return EIO; - sc->ipmi_requests++; - sc->ipmi_timestamp = ticks; - ipmi_check_read(sc->ipmi_dev); - - return 0; + IPMI_LOCK(sc); + dev->ipmi_requests++; + error = sc->ipmi_enqueue_request(sc, kreq); + IPMI_UNLOCK(sc); + if (error) + return (error); + break; +#ifdef IPMICTL_SEND_COMMAND_32 + case IPMICTL_RECEIVE_MSG_TRUNC_32: + case IPMICTL_RECEIVE_MSG_32: +#endif case IPMICTL_RECEIVE_MSG_TRUNC: case IPMICTL_RECEIVE_MSG: - item = TAILQ_FIRST(&sc->ipmi_done); - if (!item) { - return EAGAIN; - } - error = copyin(recv->addr, &addr, sizeof(addr)); - if (error != 0) - return error; - TAILQ_REMOVE(&sc->ipmi_done, item, list); + if (error) + return (error); + + IPMI_LOCK(sc); + kreq = TAILQ_FIRST(&dev->ipmi_completed_requests); + if (kreq == NULL) { + IPMI_UNLOCK(sc); + return (EAGAIN); + } addr.channel = IPMI_BMC_CHANNEL; + /* XXX */ recv->recv_type = IPMI_RESPONSE_RECV_TYPE; - recv->msgid = item->msgid; - recv->msg.netfn = item->data[0] >> 2; - recv->msg.cmd = item->data[1]; - error = len = item->len; - len -= 2; - if (len < 0) - len = 1; - if (recv->msg.data_len < len && cmd == IPMICTL_RECEIVE_MSG) { - TAILQ_INSERT_HEAD(&sc->ipmi_done, item, list); - return EMSGSIZE; + 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) { + TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, + ir_link); + dev->ipmi_requests--; + IPMI_UNLOCK(sc); + ipmi_free_request(kreq); + return (error); } + len = kreq->ir_replylen + 1; + if (recv->msg.data_len < len && + (cmd == IPMICTL_RECEIVE_MSG +#ifdef IPMICTL_RECEIVE_MSG_32 + || cmd == IPMICTL_RECEIVE_MSG +#endif + )) { + IPMI_UNLOCK(sc); + return (EMSGSIZE); + } + TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link); + dev->ipmi_requests--; + IPMI_UNLOCK(sc); len = min(recv->msg.data_len, len); recv->msg.data_len = len; error = copyout(&addr, recv->addr,sizeof(addr)); if (error == 0) - error = copyout(&item->data[2], recv->msg.data, len); - free(item->data, M_IPMI); - free(item, M_IPMI); - - if (error != 0) - return error; - return 0; + error = copyout(&kreq->ir_compcode, recv->msg.data, 1); + if (error == 0) + error = copyout(kreq->ir_reply, recv->msg.data + 1, + len - 1); + ipmi_free_request(kreq); + if (error) + return (error); + break; case IPMICTL_SET_MY_ADDRESS_CMD: - sc->ipmi_address = *(int*)data; - return 0; + IPMI_LOCK(sc); + dev->ipmi_address = *(int*)data; + IPMI_UNLOCK(sc); + break; case IPMICTL_GET_MY_ADDRESS_CMD: - *(int*)data = sc->ipmi_address; - return 0; + IPMI_LOCK(sc); + *(int*)data = dev->ipmi_address; + IPMI_UNLOCK(sc); + break; case IPMICTL_SET_MY_LUN_CMD: - sc->ipmi_lun = *(int*)data & 0x3; - return 0; + IPMI_LOCK(sc); + dev->ipmi_lun = *(int*)data & 0x3; + IPMI_UNLOCK(sc); + break; case IPMICTL_GET_MY_LUN_CMD: - *(int*)data = sc->ipmi_lun; - return 0; + IPMI_LOCK(sc); + *(int*)data = dev->ipmi_lun; + IPMI_UNLOCK(sc); + break; case IPMICTL_SET_GETS_EVENTS_CMD: /* device_printf(sc->ipmi_dev, "IPMICTL_SET_GETS_EVENTS_CMD NA\n"); */ - return 0; + break; case IPMICTL_REGISTER_FOR_CMD: case IPMICTL_UNREGISTER_FOR_CMD: - return EOPNOTSUPP; + return (EOPNOTSUPP); + default: + device_printf(sc->ipmi_dev, "Unknown IOCTL %lX\n", cmd); + return (ENOIOCTL); } - device_printf(sc->ipmi_dev, "Unknown IOCTL %lX\n", cmd); - - return ENOIOCTL; -} - -static int -ipmi_wait_for_ibf(device_t dev, int state) { - struct ipmi_softc *sc = device_get_softc(dev); - int status, start = ticks; - int first = 1; - - if (state == 0) { - /* WAIT FOR IBF = 0 */ - do { - if (first) - first =0; - else - DELAY(100); - status = INB(sc, sc->ipmi_kcs_status_reg); - } while (ticks - start < MAX_TIMEOUT - && status & KCS_STATUS_IBF); - } else { - /* WAIT FOR IBF = 1 */ - do { - if (first) - first =0; - else - DELAY(100); - status = INB(sc, sc->ipmi_kcs_status_reg); - } while (ticks - start < MAX_TIMEOUT - && !(status & KCS_STATUS_IBF)); +#ifdef IPMICTL_SEND_COMMAND_32 + /* Update changed fields in 32-bit structures. */ + switch (cmd) { + case IPMICTL_RECEIVE_MSG_TRUNC_32: + case IPMICTL_RECEIVE_MSG_32: + recv32->recv_type = recv->recv_type; + recv32->msgid = recv->msgid; + recv32->msg.netfn = recv->msg.netfn; + recv32->msg.cmd = recv->msg.cmd; + recv32->msg.data_len = recv->msg.data_len; + break; } - return status; +#endif + return (0); } -static int -ipmi_wait_for_obf(device_t dev, int state) { - struct ipmi_softc *sc = device_get_softc(dev); - int status, start = ticks; - int first = 1; - - if (state == 0) { - /* WAIT FOR OBF = 0 */ - do { - if (first) - first = 0; - else - DELAY(100); - status = INB(sc, sc->ipmi_kcs_status_reg); - } while (ticks - start < MAX_TIMEOUT - && status & KCS_STATUS_OBF); - } else { - /* WAIT FOR OBF = 1 */ - do { - if (first) - first =0; - else - DELAY(100); - status = INB(sc, sc->ipmi_kcs_status_reg); - } while (ticks - start < MAX_TIMEOUT - && !(status & KCS_STATUS_OBF)); - } - return status; -} +/* + * Request management. + */ -static void -ipmi_clear_obf(device_t dev, int status) { - struct ipmi_softc *sc = device_get_softc(dev); - int data; +/* Allocate a new request with request and reply buffers. */ +struct ipmi_request * +ipmi_alloc_request(struct ipmi_device *dev, long msgid, uint8_t addr, + uint8_t command, size_t requestlen, size_t replylen) +{ + struct ipmi_request *req; - /* Clear OBF */ - if (status & KCS_STATUS_OBF) { - data = INB(sc, sc->ipmi_kcs_data_out_reg); + req = malloc(sizeof(struct ipmi_request) + requestlen + replylen, + M_IPMI, M_WAITOK | M_ZERO); + req->ir_owner = dev; + req->ir_msgid = msgid; + req->ir_addr = addr; + req->ir_command = command; + if (requestlen) { + req->ir_request = (char *)&req[1]; + req->ir_requestlen = requestlen; } -} - -static void -ipmi_error(device_t dev) { - struct ipmi_softc *sc = device_get_softc(dev); - int status, data = 0; - int retry = 0; - - for(;;){ - status = ipmi_wait_for_ibf(dev, 0); - - /* ABORT */ - OUTB(sc, sc->ipmi_kcs_command_reg, - KCS_CONTROL_GET_STATUS_ABORT); - - /* Wait for IBF = 0 */ - status = ipmi_wait_for_ibf(dev, 0); - - /* Clear OBF */ - ipmi_clear_obf(dev, status); - - if (status & KCS_STATUS_OBF) { - data = INB(sc, sc->ipmi_kcs_data_out_reg); - device_printf(dev, "Data %x\n", data); - } - - /* 0x00 to DATA_IN */ - OUTB(sc, sc->ipmi_kcs_data_in_reg, 0x00); - - /* Wait for IBF = 0 */ - status = ipmi_wait_for_ibf(dev, 0); - - if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_READ) { - - /* Wait for OBF = 1 */ - status = ipmi_wait_for_obf(dev, 1); - - /* Read error status */ - data = INB(sc, sc->ipmi_kcs_data_out_reg); - - /* Write READ into Data_in */ - OUTB(sc, sc->ipmi_kcs_data_in_reg, KCS_DATA_IN_READ); - - /* Wait for IBF = 0 */ - status = ipmi_wait_for_ibf(dev, 0); - } - - /* IDLE STATE */ - if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_IDLE) { - /* Wait for OBF = 1 */ - status = ipmi_wait_for_obf(dev, 1); - - /* Clear OBF */ - ipmi_clear_obf(dev, status); - break; - } - - retry++; - if (retry > 2) { - device_printf(dev, "Retry exhausted %x\n", retry); - break; - } + if (replylen) { + req->ir_reply = (char *)&req[1] + requestlen; + req->ir_replybuflen = replylen; } + return (req); } -static void -ipmi_wait_for_tx_okay(device_t dev) { - struct ipmi_softc *sc = device_get_softc(dev); - int flags; - - do { - flags = INB(sc, sc->ipmi_smic_flags); - } while(!flags & SMIC_STATUS_TX_RDY); -} - -static void -ipmi_wait_for_rx_okay(device_t dev) { - struct ipmi_softc *sc = device_get_softc(dev); - int flags; +/* Free a request no longer in use. */ +void +ipmi_free_request(struct ipmi_request *req) +{ - do { - flags = INB(sc, sc->ipmi_smic_flags); - } while(!flags & SMIC_STATUS_RX_RDY); + free(req, M_IPMI); } -static void -ipmi_wait_for_not_busy(device_t dev) { - struct ipmi_softc *sc = device_get_softc(dev); - int flags; - - do { - flags = INB(sc, sc->ipmi_smic_flags); - } while(flags & SMIC_STATUS_BUSY); -} +/* Store a processed request on the appropriate completion queue. */ +void +ipmi_complete_request(struct ipmi_softc *sc, struct ipmi_request *req) +{ + struct ipmi_device *dev; -static void -ipmi_set_busy(device_t dev) { - struct ipmi_softc *sc = device_get_softc(dev); - int flags; + IPMI_LOCK_ASSERT(sc); - flags = INB(sc, sc->ipmi_smic_flags); - flags |= SMIC_STATUS_BUSY; - OUTB(sc, sc->ipmi_smic_flags, flags); + /* + * Anonymous requests (from inside the driver) always have a + * waiter that we awaken. + */ + if (req->ir_owner == NULL) + wakeup(req); + else { + dev = req->ir_owner; + TAILQ_INSERT_TAIL(&dev->ipmi_completed_requests, req, ir_link); + selwakeup(&dev->ipmi_select); + if (dev->ipmi_closing) + wakeup(&dev->ipmi_requests); + } } +/* Enqueue an internal driver request and wait until it is completed. */ int -ipmi_read(device_t dev, u_char *bytes, int len){ - struct ipmi_softc *sc = device_get_softc(dev); - int status, flags, data, i = -1, error; - - bzero(bytes, len); - if (sc->ipmi_bios_info.smic_mode) { - ipmi_wait_for_not_busy(dev); - do { - flags = INB(sc, sc->ipmi_smic_flags); - } while(!flags & SMIC_STATUS_RX_RDY); - - OUTB(sc, sc->ipmi_smic_ctl_sts, SMIC_CC_SMS_RD_START); - ipmi_wait_for_rx_okay(dev); - ipmi_set_busy(dev); - ipmi_wait_for_not_busy(dev); - status = INB(sc, sc->ipmi_smic_ctl_sts); - if (status != SMIC_SC_SMS_RD_START) { - error = INB(sc, sc->ipmi_smic_data); - device_printf(dev, "Read did not start %x %x\n", - status, error); - sc->ipmi_busy = 0; - return -1; - } - for (i = -1; ; len--) { - i++; - data = INB(sc, sc->ipmi_smic_data); - if (len > 0) - *bytes++ = data; - else { - device_printf(dev, "Read short %x\n", data); - break; - } - do { - flags = INB(sc, sc->ipmi_smic_flags); - } while(!flags & SMIC_STATUS_RX_RDY); - - OUTB(sc, sc->ipmi_smic_ctl_sts, SMIC_CC_SMS_RD_NEXT); - ipmi_wait_for_rx_okay(dev); - ipmi_set_busy(dev); - ipmi_wait_for_not_busy(dev); - status = INB(sc, sc->ipmi_smic_ctl_sts); - if (status == SMIC_SC_SMS_RD_NEXT) { - continue; - } else if (status == SMIC_SC_SMS_RD_END) { - break; - } else { - device_printf(dev, "Read did not next %x\n", - status); - } - } - i++; - data = INB(sc, sc->ipmi_smic_data); - if (len > 0) - *bytes++ = data; - else - device_printf(dev, "Read short %x\n", data); - - OUTB(sc, sc->ipmi_smic_ctl_sts, SMIC_CC_SMS_RD_END); - i++; - - } else if (sc->ipmi_bios_info.kcs_mode) { - for (i = -1; ; len--) { - /* Wait for IBF = 0 */ - status = ipmi_wait_for_ibf(dev, 0); - - /* Read State */ - if (KCS_STATUS_STATE(status) - == KCS_STATUS_STATE_READ) { - i++; - - /* Wait for OBF = 1 */ - status = ipmi_wait_for_obf(dev, 1); - - /* Read Data_out */ - data = INB(sc, sc->ipmi_kcs_data_out_reg); - if (len > 0) - *bytes++ = data; - else { - device_printf(dev, "Read short %x byte %d\n", data, i); - break; - } - - /* Write READ into Data_in */ - OUTB(sc, sc->ipmi_kcs_data_in_reg, - KCS_DATA_IN_READ); - - /* Idle State */ - } else if (KCS_STATUS_STATE(status) - == KCS_STATUS_STATE_IDLE) { - i++; - - /* Wait for OBF = 1*/ - status = ipmi_wait_for_obf(dev, 1); - - /* Read Dummy */ - data = INB(sc, sc->ipmi_kcs_data_out_reg); - break; - - /* error state */ - } else { - device_printf(dev, - "read status error %x byte %d\n", - status, i); - sc->ipmi_busy = 0; - ipmi_error(dev); - return -1; - } - } - } else { - device_printf(dev, "Unsupported mode\n"); - } - sc->ipmi_busy = 0; - wakeup(&sc->ipmi_busy); +ipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, + int timo) +{ + int error; - return i; + IPMI_LOCK(sc); + error = sc->ipmi_enqueue_request(sc, req); + if (error == 0) + error = msleep(req, &sc->ipmi_lock, 0, "ipmireq", timo); + if (error == 0) + error = req->ir_error; + IPMI_UNLOCK(sc); + return (error); } +/* + * Helper routine for polled system interfaces that use + * ipmi_polled_enqueue_request() to queue requests. This request + * waits until there is a pending request and then returns the first + * request. If the driver is shutting down, it returns NULL. + */ +struct ipmi_request * +ipmi_dequeue_request(struct ipmi_softc *sc) +{ + struct ipmi_request *req; -static int -ipmi_write(device_t dev, u_char *bytes, int len){ - struct ipmi_softc *sc = device_get_softc(dev); - int status, flags, retry; - - while(sc->ipmi_busy){ - status = tsleep(&sc->ipmi_busy, PCATCH, "ipmi", 0); - if (status) - return status; - } - sc->ipmi_busy = 1; - if (sc->ipmi_bios_info.smic_mode) { - ipmi_wait_for_not_busy(dev); - - OUTB(sc, sc->ipmi_smic_ctl_sts, SMIC_CC_SMS_WR_START); - ipmi_wait_for_tx_okay(dev); - OUTB(sc, sc->ipmi_smic_data, *bytes++); - len--; - ipmi_set_busy(dev); - ipmi_wait_for_not_busy(dev); - status = INB(sc, sc->ipmi_smic_ctl_sts); - if (status != SMIC_SC_SMS_WR_START) { - device_printf(dev, "Write did not start %x\n",status); - sc->ipmi_busy = 0; - return -1; - } - for(len--; len; len--) { - OUTB(sc, sc->ipmi_smic_ctl_sts, SMIC_CC_SMS_WR_NEXT); - ipmi_wait_for_tx_okay(dev); - OUTB(sc, sc->ipmi_smic_data, *bytes++); - ipmi_set_busy(dev); - ipmi_wait_for_not_busy(dev); - status = INB(sc, sc->ipmi_smic_ctl_sts); - if (status != SMIC_SC_SMS_WR_NEXT) { - device_printf(dev, "Write did not next %x\n", - status); - sc->ipmi_busy = 0; - return -1; - } - } - do { - flags = INB(sc, sc->ipmi_smic_flags); - } while(!flags & SMIC_STATUS_TX_RDY); - OUTB(sc, sc->ipmi_smic_ctl_sts, SMIC_CC_SMS_WR_END); - ipmi_wait_for_tx_okay(dev); - OUTB(sc, sc->ipmi_smic_data, *bytes); - ipmi_set_busy(dev); - ipmi_wait_for_not_busy(dev); - status = INB(sc, sc->ipmi_smic_ctl_sts); - if (status != SMIC_SC_SMS_WR_END) { - device_printf(dev, "Write did not end %x\n",status); - return -1; - } - } else if (sc->ipmi_bios_info.kcs_mode) { - for (retry = 0; retry < 10; retry++) { - /* Wait for IBF = 0 */ - status = ipmi_wait_for_ibf(dev, 0); - - /* Clear OBF */ - ipmi_clear_obf(dev, status); - - /* Write start to command */ - OUTB(sc, sc->ipmi_kcs_command_reg, - KCS_CONTROL_WRITE_START); - - /* Wait for IBF = 0 */ - status = ipmi_wait_for_ibf(dev, 0); - if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_WRITE) - break; - DELAY(1000000); - } - - for(len--; len; len--) { - if (KCS_STATUS_STATE(status) - != KCS_STATUS_STATE_WRITE) { - /* error state */ - device_printf(dev, "status error %x\n",status); - ipmi_error(dev); - sc->ipmi_busy = 0; - return -1; - break; - } else { - /* Clear OBF */ - ipmi_clear_obf(dev, status); - - /* Data to Data */ - OUTB(sc, sc->ipmi_kcs_data_out_reg, *bytes++); + IPMI_LOCK_ASSERT(sc); - /* Wait for IBF = 0 */ - status = ipmi_wait_for_ibf(dev, 0); + while (!sc->ipmi_detaching && TAILQ_EMPTY(&sc->ipmi_pending_requests)) + cv_wait(&sc->ipmi_request_added, &sc->ipmi_lock); + if (sc->ipmi_detaching) + return (NULL); - if (KCS_STATUS_STATE(status) - != KCS_STATUS_STATE_WRITE) { - device_printf(dev, "status error %x\n" - ,status); - ipmi_error(dev); - return -1; - } else { - /* Clear OBF */ - ipmi_clear_obf(dev, status); - } - } - } - /* Write end to command */ - OUTB(sc, sc->ipmi_kcs_command_reg, KCS_CONTROL_WRITE_END); + req = TAILQ_FIRST(&sc->ipmi_pending_requests); + TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); + return (req); +} - /* Wait for IBF = 0 */ - status = ipmi_wait_for_ibf(dev, 0); +/* Default implementation of ipmi_enqueue_request() for polled interfaces. */ +int +ipmi_polled_enqueue_request(struct ipmi_softc *sc, struct ipmi_request *req) +{ - if (KCS_STATUS_STATE(status) != KCS_STATUS_STATE_WRITE) { - /* error state */ - device_printf(dev, "status error %x\n",status); - ipmi_error(dev); - sc->ipmi_busy = 0; - return -1; - } else { - /* Clear OBF */ - ipmi_clear_obf(dev, status); - OUTB(sc, sc->ipmi_kcs_data_out_reg, *bytes++); - } - } else { - device_printf(dev, "Unsupported mode\n"); - } - sc->ipmi_busy = 2; - return 1; + IPMI_LOCK(sc); + TAILQ_INSERT_TAIL(&sc->ipmi_pending_requests, req, ir_link); + IPMI_UNLOCK(sc); + cv_signal(&sc->ipmi_request_added); + return (0); } /* @@ -879,56 +596,50 @@ ipmi_write(device_t dev, u_char *bytes, int len){ */ static void -ipmi_set_watchdog(device_t dev, int sec) { - u_char *temp; - int s; +ipmi_set_watchdog(struct ipmi_softc *sc, int sec) +{ + struct ipmi_request *req; + int error; - temp = malloc(IPMI_MAX_RX, M_IPMI, M_WAITOK); + req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), + IPMI_SET_WDOG, 6, 0); - temp[0] = IPMI_APP_REQUEST << 2; if (sec) { - temp[1] = IPMI_SET_WDOG; - temp[2] = IPMI_SET_WD_TIMER_DONT_STOP + req->ir_request[0] = IPMI_SET_WD_TIMER_DONT_STOP | IPMI_SET_WD_TIMER_SMS_OS; - temp[3] = IPMI_SET_WD_ACTION_RESET; - temp[4] = 0; - temp[5] = 0; /* Timer use */ - temp[6] = (sec * 10) & 0xff; - temp[7] = (sec * 10) / 2550; + req->ir_request[1] = IPMI_SET_WD_ACTION_RESET; + req->ir_request[2] = 0; + req->ir_request[3] = 0; /* Timer use */ + req->ir_request[4] = (sec * 10) & 0xff; + req->ir_request[5] = (sec * 10) / 2550; } else { - temp[1] = IPMI_SET_WDOG; - temp[2] = IPMI_SET_WD_TIMER_SMS_OS; - temp[3] = 0; - temp[4] = 0; - temp[5] = 0; /* Timer use */ - temp[6] = 0; - temp[7] = 0; + req->ir_request[0] = IPMI_SET_WD_TIMER_SMS_OS; + req->ir_request[1] = 0; + req->ir_request[2] = 0; + req->ir_request[3] = 0; /* Timer use */ + req->ir_request[4] = 0; + req->ir_request[5] = 0; } - s = splhigh(); - ipmi_write(dev, temp, 8); + error = ipmi_submit_driver_request(sc, req, 0); + if (error) + device_printf(sc->ipmi_dev, "Failed to set watchdog\n"); - while (!ipmi_ready_to_read(dev)) - DELAY(1000); - bzero(temp, IPMI_MAX_RX); - ipmi_read(dev, temp, IPMI_MAX_RX); + if (error == 0 && sec) { + ipmi_free_request(req); - if (sec) { - temp[0] = IPMI_APP_REQUEST << 2; - temp[1] = IPMI_RESET_WDOG; + req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), + IPMI_RESET_WDOG, 0, 0); - ipmi_write(dev, temp, 2); - - while (!ipmi_ready_to_read(dev)) - DELAY(1000); - bzero(temp, IPMI_MAX_RX); - ipmi_read(dev, temp, IPMI_MAX_RX); + error = ipmi_submit_driver_request(sc, req, 0); + if (error) + device_printf(sc->ipmi_dev, + "Failed to reset watchdog\n"); } - splx(s); - free(temp, M_IPMI); + ipmi_free_request(req); /* - dump_watchdog(dev); + dump_watchdog(sc); */ } @@ -940,7 +651,7 @@ ipmi_wd_event(void *arg, unsigned int cmd, int *error) /* disable / enable */ if (!(cmd & WD_ACTIVE)) { - ipmi_set_watchdog(sc->ipmi_dev, 0); + ipmi_set_watchdog(sc, 0); *error = 0; return; } @@ -954,92 +665,208 @@ ipmi_wd_event(void *arg, unsigned int cmd, int *error) timeout = ((uint64_t)1 << cmd) / 1800000000; /* reload */ - ipmi_set_watchdog(sc->ipmi_dev, timeout); + ipmi_set_watchdog(sc, timeout); *error = 0; } -int -ipmi_attach(device_t dev) +#ifdef CLONING +static void +ipmi_clone(void *arg, struct ucred *cred, char *name, int namelen, + struct cdev **cdev) { - struct ipmi_softc *sc = device_get_softc(dev); - u_char temp[1024]; - int i; - int status; - int unit; + struct ipmi_softc *sc = arg; + struct ipmi_device *dev; + int minor, unit; + + if (*cdev != NULL) + return; + + if (strcmp(name, device_get_nameunit(sc->ipmi_dev)) != 0) + return; - TAILQ_INIT(&sc->ipmi_done); - sc->ipmi_address = IPMI_BMC_SLAVE_ADDR; - sc->ipmi_lun = IPMI_BMC_SMS_LUN; - temp[0] = IPMI_APP_REQUEST << 2; - temp[1] = IPMI_GET_DEVICE_ID; - ipmi_write(dev, temp, 2); + dev = malloc(sizeof(struct ipmi_device), M_IPMI, M_WAITOK | M_ZERO); + + /* Reserve a sub-device. */ + IPMI_LOCK(sc); + minor = ffs(~(sc->ipmi_cdev_mask & 0xffff)); + if (minor == 0 || !sc->ipmi_cloning) { + IPMI_UNLOCK(sc); + free(dev, M_IPMI); + return; + } + minor--; + sc->ipmi_cdev_mask |= (1 << minor); + TAILQ_INSERT_TAIL(&sc->ipmi_cdevs, dev, ipmi_link); + IPMI_UNLOCK(sc); + + /* Initialize the device. */ + TAILQ_INIT(&dev->ipmi_completed_requests); + dev->ipmi_softc = sc; + dev->ipmi_address = IPMI_BMC_SLAVE_ADDR; + dev->ipmi_lun = IPMI_BMC_SMS_LUN; + unit = device_get_unit(sc->ipmi_dev); + dev->ipmi_cdev = make_dev_cred(&ipmi_cdevsw, unit * 32 + minor, cred, + UID_ROOT, GID_OPERATOR, 0660, "ipmi%d.%d", unit, minor); + if (dev->ipmi_cdev == NULL) { + IPMI_LOCK(sc); + sc->ipmi_cdev_mask &= ~(1 << minor); + TAILQ_REMOVE(&sc->ipmi_cdevs, dev, ipmi_link); + IPMI_UNLOCK(sc); + free(dev, M_IPMI); + return; + } + dev->ipmi_cdev->si_drv1 = dev; + *cdev = dev->ipmi_cdev; + dev_ref(*cdev); +} +#endif + +static void +ipmi_startup(void *arg) +{ + struct ipmi_softc *sc = arg; + struct ipmi_request *req; + device_t dev; + int error, i; + + config_intrhook_disestablish(&sc->ipmi_ich); + dev = sc->ipmi_dev; + + /* Initialize interface-independent state. */ + mtx_init(&sc->ipmi_lock, device_get_nameunit(dev), "ipmi", MTX_DEF); + cv_init(&sc->ipmi_request_added, "ipmireq"); + TAILQ_INIT(&sc->ipmi_pending_requests); +#ifdef CLONING + TAILQ_INIT(&sc->ipmi_cdevs); +#endif + + /* Initialize interface-dependent state. */ + error = sc->ipmi_startup(sc); + if (error) { + device_printf(dev, "Failed to initialize interface: %d\n", + error); + return; + } + + /* Send a GET_DEVICE_ID request. */ + req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), + IPMI_GET_DEVICE_ID, 0, 15); + + error = ipmi_submit_driver_request(sc, req, MAX_TIMEOUT); + if (error == EWOULDBLOCK) { + device_printf(dev, "Timed out waiting for GET_DEVICE_ID\n"); + return; + } else if (error) { + device_printf(dev, "Failed GET_DEVICE_ID: %d\n", error); + return; + } else if (req->ir_compcode != 0) { + device_printf(dev, + "Bad completion code for GET_DEVICE_ID: %d\n", + req->ir_compcode); + return; + } else if (req->ir_replylen < 5) { + device_printf(dev, "Short reply for GET_DEVICE_ID: %d\n", + req->ir_replylen); + return; + } - while (!ipmi_ready_to_read(dev)) - DELAY(1000); - bzero(temp, sizeof(temp)); - ipmi_read(dev, temp, sizeof(temp)); device_printf(dev, "IPMI device rev. %d, firmware rev. %d.%d, " "version %d.%d\n", - temp[4] & 0x0f, - temp[5] & 0x0f, temp[7], - temp[7] & 0x0f, temp[7] >> 4); + req->ir_reply[1] & 0x0f, + req->ir_reply[2] & 0x0f, req->ir_reply[4], + req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4); + + ipmi_free_request(req); + + req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), + IPMI_CLEAR_FLAGS, 1, 0); - temp[0] = IPMI_APP_REQUEST << 2; - temp[1] = IPMI_CLEAR_FLAGS; - temp[2] = 8; - ipmi_write(dev, temp, 3); + ipmi_submit_driver_request(sc, req, 0); - while (!ipmi_ready_to_read(dev)) - DELAY(1000); - bzero(temp, sizeof(temp)); - ipmi_read(dev, temp, sizeof(temp)); - if (temp[2] == 0xc0) { + /* XXX: Magic numbers */ + if (req->ir_compcode == 0xc0) { device_printf(dev, "Clear flags is busy\n"); } - if (temp[2] == 0xc1) { + if (req->ir_compcode == 0xc1) { device_printf(dev, "Clear flags illegal\n"); } + ipmi_free_request(req); - for(i = 0; i < 8; i++){ - temp[0] = IPMI_APP_REQUEST << 2; - temp[1] = IPMI_GET_CHANNEL_INFO; - temp[2] = i; - ipmi_write(dev, temp, 3); - while (!ipmi_ready_to_read(dev)) - DELAY(1000); - bzero(temp, sizeof(temp)); - ipmi_read(dev, temp, sizeof(temp)); - if (temp[2]) { + for (i = 0; i < 8; i++) { + req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), + IPMI_GET_CHANNEL_INFO, 1, 0); + req->ir_request[0] = i; + + ipmi_submit_driver_request(sc, req, 0); + + if (req->ir_compcode != 0) { + ipmi_free_request(req); break; } + ipmi_free_request(req); } device_printf(dev, "Number of channels %d\n", i); /* probe for watchdog */ - bzero(temp, sizeof(temp)); - temp[0] = IPMI_APP_REQUEST << 2; - temp[1] = IPMI_GET_WDOG; - status = ipmi_write(dev, temp, 2); - while (!ipmi_ready_to_read(dev)) - DELAY(1000); - bzero(temp, sizeof(temp)); - ipmi_read(dev, temp, sizeof(temp)); - if (temp[0] == 0x1c && temp[2] == 0x00) { + req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), + IPMI_GET_WDOG, 0, 0); + + ipmi_submit_driver_request(sc, req, 0); + + if (req->ir_compcode == 0x00) { device_printf(dev, "Attached watchdog\n"); /* register the watchdog event handler */ - sc->ipmi_ev_tag = EVENTHANDLER_REGISTER(watchdog_list, - ipmi_wd_event, sc, 0); + sc->ipmi_watchdog_tag = EVENTHANDLER_REGISTER(watchdog_list, + ipmi_wd_event, sc, 0); } - unit = device_get_unit(sc->ipmi_dev); - /* force device to be ipmi0 since that is what ipmitool expects */ - sc->ipmi_dev_t = make_dev(&ipmi_cdevsw, unit, UID_ROOT, GID_OPERATOR, - 0660, "ipmi%d", 0); - sc->ipmi_dev_t->si_drv1 = sc; + ipmi_free_request(req); - ipmi_attached = 1; +#ifdef CLONING + sc->ipmi_cloning = 1; + sc->ipmi_clone_tag = EVENTHANDLER_REGISTER(dev_clone, ipmi_clone, sc, + 1000); +#else + /* Initialize the device. */ + TAILQ_INIT(&sc->ipmi_idev.ipmi_completed_requests); + sc->ipmi_idev.ipmi_softc = sc; + sc->ipmi_idev.ipmi_address = IPMI_BMC_SLAVE_ADDR; + sc->ipmi_idev.ipmi_lun = IPMI_BMC_SMS_LUN; + sc->ipmi_idev.ipmi_cdev = make_dev(&ipmi_cdevsw, device_get_unit(dev), + UID_ROOT, GID_OPERATOR, 0660, "ipmi%d", device_get_unit(dev)); + if (sc->ipmi_idev.ipmi_cdev == NULL) { + device_printf(dev, "Failed to create cdev\n"); + return; + } + sc->ipmi_idev.ipmi_cdev->si_drv1 = &sc->ipmi_idev; +#endif +} - return 0; +int +ipmi_attach(device_t dev) +{ + struct ipmi_softc *sc = device_get_softc(dev); + int error; + + if (sc->ipmi_irq_res != NULL && sc->ipmi_intr != NULL) { + error = bus_setup_intr(dev, sc->ipmi_irq_res, INTR_TYPE_MISC, + sc->ipmi_intr, sc, &sc->ipmi_irq); + if (error) { + device_printf(dev, "can't set up interrupt\n"); + return (error); + } + } + + bzero(&sc->ipmi_ich, sizeof(struct intr_config_hook)); + sc->ipmi_ich.ich_func = ipmi_startup; + sc->ipmi_ich.ich_arg = sc; + if (config_intrhook_establish(&sc->ipmi_ich) != 0) { + device_printf(dev, "can't establish configuration hook\n"); + return (ENOMEM); + } + + ipmi_attached = 1; + return (0); } int @@ -1048,16 +875,92 @@ ipmi_detach(device_t dev) struct ipmi_softc *sc; sc = device_get_softc(dev); - if (sc->ipmi_requests) - untimeout((timeout_t *)ipmi_check_read, dev, - sc->ipmi_timeout_handle); - destroy_dev(sc->ipmi_dev_t); - return 0; + + /* Fail if there are any open handles. */ + IPMI_LOCK(sc); +#ifdef CLONING + if (!TAILQ_EMPTY(&sc->ipmi_cdevs)) { + IPMI_UNLOCK(sc); + return (EBUSY); + } + + /* Turn off cloning. */ + sc->ipmi_cloning = 0; + IPMI_UNLOCK(sc); + + EVENTHANDLER_DEREGISTER(dev_clone, sc->ipmi_clone_tag); +#else + if (sc->ipmi_idev.ipmi_open) { + IPMI_UNLOCK(sc); + return (EBUSY); + } + IPMI_UNLOCK(sc); + destroy_dev(sc->ipmi_idev.ipmi_cdev); +#endif + + /* Detach from watchdog handling and turn off watchdog. */ + if (sc->ipmi_watchdog_tag) { + EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_watchdog_tag); + ipmi_set_watchdog(sc, 0); + } + + /* XXX: should use shutdown callout I think. */ + /* If the backend uses a kthread, shut it down. */ + IPMI_LOCK(sc); + sc->ipmi_detaching = 1; + if (sc->ipmi_kthread) { + cv_broadcast(&sc->ipmi_request_added); + msleep(sc->ipmi_kthread, &sc->ipmi_lock, 0, "ipmi_wait", 0); + } + IPMI_UNLOCK(sc); + if (sc->ipmi_irq) + bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq); + + ipmi_release_resources(dev); + mtx_destroy(&sc->ipmi_lock); + return (0); +} + +void +ipmi_release_resources(device_t dev) +{ + struct ipmi_softc *sc; + int i; + + sc = device_get_softc(dev); + if (sc->ipmi_irq) + bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq); + if (sc->ipmi_irq_res) + bus_release_resource(dev, SYS_RES_IRQ, sc->ipmi_irq_rid, + sc->ipmi_irq_res); + for (i = 0; i < MAX_RES; i++) + if (sc->ipmi_io_res[i]) + bus_release_resource(dev, sc->ipmi_io_type, + sc->ipmi_io_rid + i, sc->ipmi_io_res[i]); } +devclass_t ipmi_devclass; + +/* XXX: Why? */ +static void +ipmi_unload(void *arg) +{ + device_t * devs; + int count; + int i; + + if (devclass_get_devices(ipmi_devclass, &devs, &count) != 0) + return; + for (i = 0; i < count; i++) + device_delete_child(device_get_parent(devs[i]), devs[i]); + free(devs, M_TEMP); +} +SYSUNINIT(ipmi_unload, SI_SUB_DRIVERS, SI_ORDER_FIRST, ipmi_unload, NULL); + #ifdef IMPI_DEBUG static void -dump_buf(u_char *data, int len){ +dump_buf(u_char *data, int len) +{ char buf[20]; char line[1024]; char temp[30]; diff --git a/sys/dev/ipmi/ipmi_pci.c b/sys/dev/ipmi/ipmi_pci.c index b91891dfc9bc..def00c9947ed 100644 --- a/sys/dev/ipmi/ipmi_pci.c +++ b/sys/dev/ipmi/ipmi_pci.c @@ -28,18 +28,14 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> -#include <sys/malloc.h> #include <sys/systm.h> +#include <sys/bus.h> +#include <sys/condvar.h> +#include <sys/eventhandler.h> #include <sys/kernel.h> #include <sys/module.h> -#include <sys/selinfo.h> - -#include <sys/bus.h> -#include <sys/conf.h> - -#include <machine/bus.h> -#include <machine/resource.h> #include <sys/rman.h> +#include <sys/selinfo.h> #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> @@ -52,17 +48,8 @@ __FBSDID("$FreeBSD$"); static int ipmi_pci_probe(device_t dev); static int ipmi_pci_attach(device_t dev); -static int ipmi_pci_detach(device_t dev); - -static device_method_t ipmi_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, ipmi_pci_probe), - DEVMETHOD(device_attach, ipmi_pci_attach), - DEVMETHOD(device_detach, ipmi_pci_detach), - { 0, 0 } -}; -struct ipmi_ident +static struct ipmi_ident { u_int16_t vendor; u_int16_t device; @@ -72,238 +59,237 @@ struct ipmi_ident {0, 0, 0} }; -static int -ipmi_pci_probe(device_t dev) { +const char * +ipmi_pci_match(uint16_t vendor, uint16_t device) +{ struct ipmi_ident *m; + for (m = ipmi_identifiers; m->vendor != 0; m++) + if (m->vendor == vendor && m->device == device) + return (m->desc); + return (NULL); +} + +static int +ipmi_pci_probe(device_t dev) +{ + const char *desc; + if (ipmi_attached) - return ENXIO; + return (ENXIO); - for (m = ipmi_identifiers; m->vendor != 0; m++) { - if ((m->vendor == pci_get_vendor(dev)) && - (m->device == pci_get_device(dev))) { - device_set_desc(dev, m->desc); - return (BUS_PROBE_DEFAULT); - } + desc = ipmi_pci_match(pci_get_vendor(dev), pci_get_device(dev)); + if (desc != NULL) { + device_set_desc(dev, desc); + return (BUS_PROBE_DEFAULT); } - return ENXIO; + return (ENXIO); } static int -ipmi_pci_attach(device_t dev) { +ipmi_pci_attach(device_t dev) +{ struct ipmi_softc *sc = device_get_softc(dev); - device_t parent, smbios_attach_dev = NULL; - devclass_t dc; - int status, flags; - int error; + struct ipmi_get_info info; + const char *mode; + int error, type; + /* Look for an IPMI entry in the SMBIOS table. */ + if (!ipmi_smbios_identify(&info)) + return (ENXIO); - /* - * We need to attach to something that can address the BIOS/ - * SMBIOS memory range. This is usually the isa bus however - * during a static kernel boot the isa bus is not available - * so we run up the tree to the nexus bus. A module load - * will use the isa bus attachment. If neither work bail - * since the SMBIOS defines stuff we need to know to attach to - * this device. - */ - dc = devclass_find("isa"); - if (dc != NULL) { - smbios_attach_dev = devclass_get_device(dc, 0); - } + sc->ipmi_dev = dev; - if (smbios_attach_dev == NULL) { - smbios_attach_dev = dev; - for (;;) { - parent = device_get_parent(smbios_attach_dev); - if (parent == NULL) - break; - if (strcmp(device_get_name(smbios_attach_dev), - "nexus") == 0) - break; - smbios_attach_dev = parent; - } - } - - if (smbios_attach_dev == NULL) { - device_printf(dev, "Couldn't find isa/nexus device\n"); - goto bad; + switch (info.iface_type) { + case KCS_MODE: + mode = "KCS"; + break; + case SMIC_MODE: + mode = "SMIC"; + break; + case BT_MODE: + device_printf(dev, "BT mode is unsupported\n"); + return (ENXIO); + default: + device_printf(dev, "No IPMI interface found\n"); + return (ENXIO); } - sc->ipmi_smbios_dev = ipmi_smbios_identify(NULL, smbios_attach_dev); - if (sc->ipmi_smbios_dev == NULL) { - device_printf(dev, "Couldn't find isa device\n"); - goto bad; + + device_printf(dev, "%s mode found at %s 0x%jx alignment 0x%x on %s\n", + mode, info.io_mode ? "io" : "mem", + (uintmax_t)info.address, info.offset, + device_get_name(device_get_parent(dev))); + if (info.io_mode) + type = SYS_RES_IOPORT; + else + type = SYS_RES_MEMORY; + + sc->ipmi_io_rid = PCIR_BAR(0); + sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type, + &sc->ipmi_io_rid, RF_ACTIVE); + sc->ipmi_io_type = type; + sc->ipmi_io_spacing = info.offset; + + if (sc->ipmi_io_res[0] == NULL) { + device_printf(dev, "couldn't configure pci io res\n"); + return (ENXIO); } - error = ipmi_smbios_probe(sc->ipmi_smbios_dev); - if (error != 0) { - goto bad; + + sc->ipmi_irq_rid = 0; + sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE); + + switch (info.iface_type) { + case KCS_MODE: + error = ipmi_kcs_attach(sc); + if (error) + goto bad; + break; + case SMIC_MODE: + error = ipmi_smic_attach(sc); + if (error) + goto bad; + break; } - sc->ipmi_dev = dev; - error = ipmi_smbios_query(dev); - device_delete_child(dev, sc->ipmi_smbios_dev); - if (error != 0) + error = ipmi_attach(dev); + if (error) goto bad; - /* Now we know about the IPMI attachment info. */ - if (sc->ipmi_bios_info.kcs_mode) { - if (sc->ipmi_bios_info.io_mode) - device_printf(dev, "KCS mode found at io 0x%llx " - "alignment 0x%x on %s\n", - (long long)sc->ipmi_bios_info.address, - sc->ipmi_bios_info.offset, - device_get_name(device_get_parent(sc->ipmi_dev))); - else - device_printf(dev, "KCS mode found at mem 0x%llx " - "alignment 0x%x on %s\n", - (long long)sc->ipmi_bios_info.address, - sc->ipmi_bios_info.offset, - device_get_name(device_get_parent(sc->ipmi_dev))); + return (0); +bad: + ipmi_release_resources(dev); + return (error); +} + +static device_method_t ipmi_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ipmi_pci_probe), + DEVMETHOD(device_attach, ipmi_pci_attach), + DEVMETHOD(device_detach, ipmi_detach), + { 0, 0 } +}; + +static driver_t ipmi_pci_driver = { + "ipmi", + ipmi_methods, + sizeof(struct ipmi_softc) +}; - sc->ipmi_kcs_status_reg = sc->ipmi_bios_info.offset; - sc->ipmi_kcs_command_reg = sc->ipmi_bios_info.offset; - sc->ipmi_kcs_data_out_reg = 0; - sc->ipmi_kcs_data_in_reg = 0; +DRIVER_MODULE(ipmi_pci, pci, ipmi_pci_driver, ipmi_devclass, 0, 0); - if (sc->ipmi_bios_info.io_mode) { - sc->ipmi_io_rid = PCIR_BAR(0); - sc->ipmi_io_res = bus_alloc_resource(dev, - SYS_RES_IOPORT, &sc->ipmi_io_rid, - sc->ipmi_bios_info.address, - sc->ipmi_bios_info.address + - (sc->ipmi_bios_info.offset * 2), - sc->ipmi_bios_info.offset * 2, - RF_ACTIVE); - } else { - sc->ipmi_mem_rid = PCIR_BAR(0); - sc->ipmi_mem_res = bus_alloc_resource(dev, - SYS_RES_MEMORY, &sc->ipmi_mem_rid, - sc->ipmi_bios_info.address, - sc->ipmi_bios_info.address + - (sc->ipmi_bios_info.offset * 2), - sc->ipmi_bios_info.offset * 2, - RF_ACTIVE); - } +/* Native IPMI on PCI driver. */ - if (!sc->ipmi_io_res){ - device_printf(dev, "couldn't configure pci io res\n"); - goto bad; - } +static int +ipmi2_pci_probe(device_t dev) +{ - status = INB(sc, sc->ipmi_kcs_status_reg); - if (status == 0xff) { - device_printf(dev, "couldn't find it\n"); - goto bad; - } - if(status & KCS_STATUS_OBF){ - ipmi_read(dev, NULL, 0); - } - } else if (sc->ipmi_bios_info.smic_mode) { - if (sc->ipmi_bios_info.io_mode) - device_printf(dev, "SMIC mode found at io 0x%llx " - "alignment 0x%x on %s\n", - (long long)sc->ipmi_bios_info.address, - sc->ipmi_bios_info.offset, - device_get_name(device_get_parent(sc->ipmi_dev))); - else - device_printf(dev, "SMIC mode found at mem 0x%llx " - "alignment 0x%x on %s\n", - (long long)sc->ipmi_bios_info.address, - sc->ipmi_bios_info.offset, - device_get_name(device_get_parent(sc->ipmi_dev))); + if (pci_get_class(dev) == PCIC_SERIALBUS && + pci_get_subclass(dev) == 0x07) { + device_set_desc(dev, "IPMI System Interface"); + return (BUS_PROBE_GENERIC); + } - sc->ipmi_smic_data = 0; - sc->ipmi_smic_ctl_sts = sc->ipmi_bios_info.offset; - sc->ipmi_smic_flags = sc->ipmi_bios_info.offset * 2; + return (ENXIO); +} - if (sc->ipmi_bios_info.io_mode) { - sc->ipmi_io_rid = PCIR_BAR(0); - sc->ipmi_io_res = bus_alloc_resource(dev, - SYS_RES_IOPORT, &sc->ipmi_io_rid, - sc->ipmi_bios_info.address, - sc->ipmi_bios_info.address + - (sc->ipmi_bios_info.offset * 3), - sc->ipmi_bios_info.offset * 3, - RF_ACTIVE); - } else { - sc->ipmi_mem_rid = PCIR_BAR(0); - sc->ipmi_mem_res = bus_alloc_resource(dev, - SYS_RES_MEMORY, &sc->ipmi_mem_rid, - sc->ipmi_bios_info.address, - sc->ipmi_bios_info.address + - (sc->ipmi_bios_info.offset * 2), - sc->ipmi_bios_info.offset * 2, - RF_ACTIVE); - } +static int +ipmi2_pci_attach(device_t dev) +{ + struct ipmi_softc *sc; + int error, iface, type; - if (!sc->ipmi_io_res && !sc->ipmi_mem_res){ - device_printf(dev, "couldn't configure pci res\n"); - goto bad; - } + sc = device_get_softc(dev); + sc->ipmi_dev = dev; - flags = INB(sc, sc->ipmi_smic_flags); - if (flags == 0xff) { - device_printf(dev, "couldn't find it\n"); - goto bad; - } - if ((flags & SMIC_STATUS_SMS_ATN) - && (flags & SMIC_STATUS_RX_RDY)){ - ipmi_read(dev, NULL, 0); - } - } else { - device_printf(dev, "No IPMI interface found\n"); - goto bad; + /* Interface is determined by progif. */ + switch (pci_get_progif(dev)) { + case 0: + iface = SMIC_MODE; + break; + case 1: + iface = KCS_MODE; + break; + case 2: + iface = BT_MODE; + device_printf(dev, "BT interface unsupported\n"); + return (ENXIO); + default: + device_printf(dev, "Unsupported interface: %d\n", + pci_get_progif(dev)); + return (ENXIO); + } + + /* + * Bottom bit of bar indicates resouce type. There should be + * constants in pcireg.h for fields in a BAR. + */ + sc->ipmi_io_rid = PCIR_BAR(0); + if (pci_read_config(dev, PCIR_BAR(0), 4) & 0x1) + type = SYS_RES_IOPORT; + else + type = SYS_RES_MEMORY; + sc->ipmi_io_type = type; + sc->ipmi_io_spacing = 1; + sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type, + &sc->ipmi_io_rid, RF_ACTIVE); + if (sc->ipmi_io_res[0] == NULL) { + device_printf(dev, "couldn't map ports/memory\n"); + return (ENXIO); } - ipmi_attach(dev); sc->ipmi_irq_rid = 0; - sc->ipmi_irq_res = bus_alloc_resource_any(sc->ipmi_dev, SYS_RES_IRQ, + sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE); - if (sc->ipmi_irq_res == NULL) { - device_printf(sc->ipmi_dev, "can't allocate interrupt\n"); - } else { - if (bus_setup_intr(sc->ipmi_dev, sc->ipmi_irq_res, - INTR_TYPE_MISC, ipmi_intr, - sc->ipmi_dev, &sc->ipmi_irq)) { - device_printf(sc->ipmi_dev, - "can't set up interrupt\n"); - return (EINVAL); - } - } - return 0; -bad: - return ENXIO; -} + switch (iface) { + case KCS_MODE: + device_printf(dev, "using KSC interface\n"); -static int ipmi_pci_detach(device_t dev) { - struct ipmi_softc *sc; + /* + * We have to examine the resource directly to determine the + * alignment. + */ + if (!ipmi_kcs_probe_align(sc)) { + device_printf(dev, "Unable to determine alignment\n"); + error = ENXIO; + goto bad; + } - sc = device_get_softc(dev); - ipmi_detach(dev); - if (sc->ipmi_ev_tag) - EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_ev_tag); + error = ipmi_kcs_attach(sc); + if (error) + goto bad; + break; + case SMIC_MODE: + device_printf(dev, "using SMIC interface\n"); - if (sc->ipmi_mem_res) - bus_release_resource(dev, SYS_RES_MEMORY, sc->ipmi_mem_rid, - sc->ipmi_mem_res); - if (sc->ipmi_io_res) - bus_release_resource(dev, SYS_RES_IOPORT, sc->ipmi_io_rid, - sc->ipmi_io_res); - if (sc->ipmi_irq) - bus_teardown_intr(sc->ipmi_dev, sc->ipmi_irq_res, - sc->ipmi_irq); - if (sc->ipmi_irq_res) - bus_release_resource(sc->ipmi_dev, SYS_RES_IRQ, - sc->ipmi_irq_rid, sc->ipmi_irq_res); + error = ipmi_smic_attach(sc); + if (error) + goto bad; + break; + } + error = ipmi_attach(dev); + if (error) + goto bad; - return 0; + return (0); +bad: + ipmi_release_resources(dev); + return (error); } -static driver_t ipmi_pci_driver = { - "ipmi", - ipmi_methods, - sizeof(struct ipmi_softc) +static device_method_t ipmi2_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ipmi2_pci_probe), + DEVMETHOD(device_attach, ipmi2_pci_attach), + DEVMETHOD(device_detach, ipmi_detach), + { 0, 0 } +}; + +static driver_t ipmi2_pci_driver = { + "ipmi", + ipmi2_methods, + sizeof(struct ipmi_softc) }; -DRIVER_MODULE(ipmi_foo, pci, ipmi_pci_driver, ipmi_devclass, 0, 0); +DRIVER_MODULE(ipmi2_pci, pci, ipmi2_pci_driver, ipmi_devclass, 0, 0); diff --git a/sys/dev/ipmi/ipmi_smbios.c b/sys/dev/ipmi/ipmi_smbios.c index de14dfb13ede..dbc11ed5c34e 100644 --- a/sys/dev/ipmi/ipmi_smbios.c +++ b/sys/dev/ipmi/ipmi_smbios.c @@ -29,20 +29,13 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/bus.h> +#include <sys/condvar.h> +#include <sys/eventhandler.h> #include <sys/kernel.h> -#include <sys/poll.h> #include <sys/selinfo.h> -#include <sys/module.h> -#include <sys/bus.h> - -#include <machine/bus.h> -#include <machine/resource.h> -#include <sys/rman.h> -#include <sys/watchdog.h> - #include <vm/vm.h> -#include <vm/vm_param.h> #include <vm/pmap.h> #include <machine/pc/bios.h> @@ -54,6 +47,11 @@ __FBSDID("$FreeBSD$"); #include <dev/ipmi/ipmivars.h> #endif +#if __FreeBSD_version < 700020 +#define pmap_mapbios pmap_mapdev +#define pmap_unmapbios pmap_unmapdev +#endif + struct smbios_table_entry { uint8_t anchor_string[4]; uint8_t checksum; @@ -77,7 +75,7 @@ struct structure_header { uint16_t handle; }; -struct ipmi_device { +struct ipmi_entry { uint8_t type; uint8_t length; uint16_t handle; @@ -90,50 +88,88 @@ struct ipmi_device { uint8_t interrupt_number; }; +/* Fields in the base_address field of an IPMI entry. */ +#define IPMI_BAR_MODE(ba) ((ba) & 0x0000000000000001) +#define IPMI_BAR_ADDR(ba) ((ba) & 0xfffffffffffffffe) + +/* Fields in the base_address_modifier field of an IPMI entry. */ +#define IPMI_BAM_IRQ_TRIGGER 0x01 +#define IPMI_BAM_IRQ_POLARITY 0x02 +#define IPMI_BAM_IRQ_VALID 0x08 +#define IPMI_BAM_ADDR_LSB(bam) (((bam) & 0x10) >> 4) +#define IPMI_BAM_REG_SPACING(bam) (((bam) & 0xc0) >> 6) +#define SPACING_8 0x0 +#define SPACING_32 0x1 +#define SPACING_16 0x2 + #define SMBIOS_START 0xf0000 #define SMBIOS_STEP 0x10 #define SMBIOS_OFF 0 #define SMBIOS_LEN 4 #define SMBIOS_SIG "_SM_" -devclass_t ipmi_devclass; typedef void (*dispatchproc_t)(uint8_t *p, char **table, struct ipmi_get_info *info); -static void smbios_run_table(uint8_t *, int, dispatchproc_t *, - struct ipmi_get_info *); -static char *get_strings(char *, char **); -static int smbios_cksum (struct smbios_table_entry *); -static void smbios_t38_proc_info(uint8_t *, char **, struct ipmi_get_info *); -static int ipmi_smbios_attach (device_t); -static int ipmi_smbios_modevent(module_t, int, void *); +static struct ipmi_get_info ipmi_info; +static int ipmi_probed; +static struct mtx ipmi_info_mtx; +MTX_SYSINIT(ipmi_info, &ipmi_info_mtx, "ipmi info", MTX_DEF); + +static char *get_strings(char *, char **); +static void ipmi_smbios_probe(struct ipmi_get_info *); +static int smbios_cksum (struct smbios_table_entry *); +static void smbios_run_table(uint8_t *, int, dispatchproc_t *, + struct ipmi_get_info *); +static void smbios_t38_proc_info(uint8_t *, char **, + struct ipmi_get_info *); static void smbios_t38_proc_info(uint8_t *p, char **table, struct ipmi_get_info *info) { - struct ipmi_device *s = (struct ipmi_device *) p; + struct ipmi_entry *s = (struct ipmi_entry *)p; bzero(info, sizeof(struct ipmi_get_info)); - if (s->interface_type == 0x01) - info->kcs_mode = 1; - if (s->interface_type == 0x02) - info->smic_mode = 1; - info->address = s->base_address & ~1; - switch (s->base_address_modifier >> 6) { - case 0x00: - info->offset = 1; - break; - case 0x01: - info->offset = 4; - break; - case 0x10: - info->offset = 2; + switch (s->interface_type) { + case KCS_MODE: + case SMIC_MODE: + info->address = IPMI_BAR_ADDR(s->base_address) | + IPMI_BAM_ADDR_LSB(s->base_address_modifier); + info->io_mode = IPMI_BAR_MODE(s->base_address); + switch (IPMI_BAM_REG_SPACING(s->base_address_modifier)) { + case SPACING_8: + info->offset = 1; + break; + case SPACING_32: + info->offset = 4; + break; + case SPACING_16: + info->offset = 2; + break; + default: + printf("SMBIOS: Invalid register spacing\n"); + return; + } break; - case 0x11: - info->offset = 0; + case SSIF_MODE: + if ((s->base_address & 0xffffffffffffff00) != 0) { + printf("SMBIOS: Invalid SSIF SMBus address, using BMC I2C slave address instead\n"); + info->address = s->i2c_slave_address >> 1; + break; + } + info->address = IPMI_BAR_ADDR(s->base_address) >> 1; break; + default: + return; + } + if (s->length > offsetof(struct ipmi_entry, interrupt_number)) { + if (s->interrupt_number > 15) + printf("SMBIOS: Non-ISA IRQ %d for IPMI\n", + s->interrupt_number); + else + info->irq = s->interrupt_number; } - info->io_mode = s->base_address & 1; + info->iface_type = s->interface_type; } static char * @@ -147,7 +183,7 @@ get_strings(char *p, char **table) *table = 0; /* Skip past terminating null byte */ - return p + 1; + return (p + 1); } @@ -177,282 +213,88 @@ smbios_run_table(uint8_t *p, int entries, dispatchproc_t *dispatchstatus, } } -device_t -ipmi_smbios_identify (driver_t *driver, device_t parent) +/* + * Walk the SMBIOS table looking for an IPMI (type 38) entry. If we find + * one, return the parsed data in the passed in ipmi_get_info structure and + * return true. If we don't find one, return false. + */ +static void +ipmi_smbios_probe(struct ipmi_get_info *info) { - device_t child = NULL; + dispatchproc_t dispatch_smbios_ipmi[256]; + struct smbios_table_entry *header; + void *table; u_int32_t addr; - int length; - int rid1, rid2; + bzero(info, sizeof(struct ipmi_get_info)); + + /* Find the SMBIOS table header. */ addr = bios_sigsearch(SMBIOS_START, SMBIOS_SIG, SMBIOS_LEN, SMBIOS_STEP, SMBIOS_OFF); - if (addr != 0) { - rid1 = 0; - length = ((struct smbios_table_entry *)BIOS_PADDRTOVADDR(addr)) - ->length; - - child = BUS_ADD_CHILD(parent, 0, "ipmi", -1); - if (driver != NULL) - device_set_driver(child, driver); - bus_set_resource(child, SYS_RES_MEMORY, rid1, addr, length); - rid2 = 1; - length = ((struct smbios_table_entry *)BIOS_PADDRTOVADDR(addr)) - ->structure_table_length; - addr = ((struct smbios_table_entry *)BIOS_PADDRTOVADDR(addr)) - ->structure_table_address; - bus_set_resource(child, SYS_RES_MEMORY, rid2, addr, length); - device_set_desc(child, "System Management BIOS"); - } else { - device_printf(parent, "Failed to find SMBIOS signature\n"); - } + if (addr == 0) + return; - return child; -} - -int -ipmi_smbios_probe(device_t dev) -{ - struct resource *res1 = NULL, *res2 = NULL; - int rid1, rid2; - int error; - - if (ipmi_attached) - return(EBUSY); - - error = 0; - rid1 = 0; - rid2 = 1; - res1 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid1, - 0ul, ~0ul, 1, RF_ACTIVE | RF_SHAREABLE); - - if (res1 == NULL) { - device_printf(dev, "Unable to allocate memory resource.\n"); - error = ENOMEM; - goto bad; - } - res2 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid2, - 0ul, ~0ul, 1, RF_ACTIVE | RF_SHAREABLE); - if (res2 == NULL) { - device_printf(dev, "Unable to allocate memory resource.\n"); - error = ENOMEM; - goto bad; - } - - if (smbios_cksum( - (struct smbios_table_entry *)rman_get_virtual(res1))) { - device_printf(dev, "SMBIOS checksum failed.\n"); - error = ENXIO; - goto bad; - } - -bad: - if (res1) - bus_release_resource(dev, SYS_RES_MEMORY, rid1, res1); - if (res2) - bus_release_resource(dev, SYS_RES_MEMORY, rid2, res2); - return error; -} - - -int -ipmi_smbios_query(device_t dev) -{ - struct ipmi_softc *sc = device_get_softc(dev); - dispatchproc_t dispatch_smbios_ipmi[256]; - struct resource *res = NULL , *res2 = NULL; - int rid, rid2; - int error; - - error = 0; - - rid = 0; - res = bus_alloc_resource(sc->ipmi_smbios_dev, SYS_RES_MEMORY, &rid, - 0ul, ~0ul, 1, RF_ACTIVE | RF_SHAREABLE ); - if (res == NULL) { - device_printf(dev, "Unable to allocate memory resource.\n"); - error = ENOMEM; - goto bad; - } - rid2 = 1; - res2 = bus_alloc_resource(sc->ipmi_smbios_dev, SYS_RES_MEMORY, &rid2, - 0ul, ~0ul, 1, RF_ACTIVE | RF_SHAREABLE); - if (res2 == NULL) { - device_printf(dev, "Unable to allocate memory resource.\n"); - error = ENOMEM; - goto bad; + /* + * Map the header. We first map a fixed size to get the actual + * length and then map it a second time with the actual length so + * we can verify the checksum. + */ + header = pmap_mapbios(addr, sizeof(struct smbios_table_entry)); + table = pmap_mapbios(addr, header->length); + pmap_unmapbios((vm_offset_t)header, sizeof(struct smbios_table_entry)); + header = table; + if (smbios_cksum(header) != 0) { + pmap_unmapbios((vm_offset_t)header, header->length); + return; } - sc->ipmi_smbios = - (struct smbios_table_entry *)rman_get_virtual(res); - - sc->ipmi_busy = 0; - - device_printf(sc->ipmi_smbios_dev, "SMBIOS Version: %d.%02d", - sc->ipmi_smbios->major_version, - sc->ipmi_smbios->minor_version); - if (bcd2bin(sc->ipmi_smbios->BCD_revision)) - printf(", revision: %d.%02d", - bcd2bin(sc->ipmi_smbios->BCD_revision >> 4), - bcd2bin(sc->ipmi_smbios->BCD_revision & 0x0f)); - printf("\n"); - - bzero(&sc->ipmi_bios_info, sizeof(sc->ipmi_bios_info)); - + /* Now map the actual table and walk it looking for an IPMI entry. */ + table = pmap_mapbios(header->structure_table_address, + header->structure_table_length); bzero((void *)dispatch_smbios_ipmi, sizeof(dispatch_smbios_ipmi)); dispatch_smbios_ipmi[38] = (void *)smbios_t38_proc_info; - smbios_run_table( - (void *)rman_get_virtual(res2), - sc->ipmi_smbios->number_structures, - dispatch_smbios_ipmi, - (void*)&sc->ipmi_bios_info); + smbios_run_table(table, header->number_structures, dispatch_smbios_ipmi, + info); -bad: - if (res) - bus_release_resource(sc->ipmi_smbios_dev, SYS_RES_MEMORY, - rid, res); - res = NULL; - if (res2) - bus_release_resource(sc->ipmi_smbios_dev, SYS_RES_MEMORY, - rid2, res2); - res2 = NULL; - return 0; + /* Unmap everything. */ + pmap_unmapbios((vm_offset_t)table, header->structure_table_length); + pmap_unmapbios((vm_offset_t)header, header->length); } -static int -ipmi_smbios_attach(device_t dev) +/* + * Return the SMBIOS IPMI table entry info to the caller. If we haven't + * searched the IPMI table yet, search it. Otherwise, return a cached + * copy of the data. + */ +int +ipmi_smbios_identify(struct ipmi_get_info *info) { - struct ipmi_softc *sc = device_get_softc(dev); - int error; - int status, flags; - - error = 0; - sc->ipmi_smbios_dev = dev; - sc->ipmi_dev = dev; - ipmi_smbios_query(dev); - - if (sc->ipmi_bios_info.kcs_mode) { - if (sc->ipmi_bios_info.io_mode) - device_printf(dev, "KCS mode found at io 0x%llx " - "alignment 0x%x on %s\n", - (long long)sc->ipmi_bios_info.address, - sc->ipmi_bios_info.offset, - device_get_name(device_get_parent(sc->ipmi_dev))); - else - device_printf(dev, "KCS mode found at mem 0x%llx " - "alignment 0x%x on %s\n", - (long long)sc->ipmi_bios_info.address, - sc->ipmi_bios_info.offset, - device_get_name(device_get_parent(sc->ipmi_dev))); - - sc->ipmi_kcs_status_reg = sc->ipmi_bios_info.offset; - sc->ipmi_kcs_command_reg = sc->ipmi_bios_info.offset; - sc->ipmi_kcs_data_out_reg = 0; - sc->ipmi_kcs_data_in_reg = 0; - - if (sc->ipmi_bios_info.io_mode) { - sc->ipmi_io_rid = 2; - sc->ipmi_io_res = bus_alloc_resource(dev, - SYS_RES_IOPORT, &sc->ipmi_io_rid, - sc->ipmi_bios_info.address, - sc->ipmi_bios_info.address + - (sc->ipmi_bios_info.offset * 2), - sc->ipmi_bios_info.offset * 2, - RF_ACTIVE); - } else { - sc->ipmi_mem_rid = 2; - sc->ipmi_mem_res = bus_alloc_resource(dev, - SYS_RES_MEMORY, &sc->ipmi_mem_rid, - sc->ipmi_bios_info.address, - sc->ipmi_bios_info.address + - (sc->ipmi_bios_info.offset * 2), - sc->ipmi_bios_info.offset * 2, - RF_ACTIVE); - } - - if (!sc->ipmi_io_res){ - device_printf(dev, - "couldn't configure smbios io res\n"); - goto bad; - } - - status = INB(sc, sc->ipmi_kcs_status_reg); - if (status == 0xff) { - device_printf(dev, "couldn't find it\n"); - error = ENXIO; - goto bad; - } - if(status & KCS_STATUS_OBF){ - ipmi_read(dev, NULL, 0); - } - } else if (sc->ipmi_bios_info.smic_mode) { - if (sc->ipmi_bios_info.io_mode) - device_printf(dev, "SMIC mode found at io 0x%llx " - "alignment 0x%x on %s\n", - (long long)sc->ipmi_bios_info.address, - sc->ipmi_bios_info.offset, - device_get_name(device_get_parent(sc->ipmi_dev))); - else - device_printf(dev, "SMIC mode found at mem 0x%llx " - "alignment 0x%x on %s\n", - (long long)sc->ipmi_bios_info.address, - sc->ipmi_bios_info.offset, - device_get_name(device_get_parent(sc->ipmi_dev))); - sc->ipmi_smic_data = 0; - sc->ipmi_smic_ctl_sts = sc->ipmi_bios_info.offset; - sc->ipmi_smic_flags = sc->ipmi_bios_info.offset * 2; - - if (sc->ipmi_bios_info.io_mode) { - sc->ipmi_io_rid = 2; - sc->ipmi_io_res = bus_alloc_resource(dev, - SYS_RES_IOPORT, &sc->ipmi_io_rid, - sc->ipmi_bios_info.address, - sc->ipmi_bios_info.address + - (sc->ipmi_bios_info.offset * 3), - sc->ipmi_bios_info.offset * 3, - RF_ACTIVE); - } else { - sc->ipmi_mem_res = bus_alloc_resource(dev, - SYS_RES_MEMORY, &sc->ipmi_mem_rid, - sc->ipmi_bios_info.address, - sc->ipmi_bios_info.address + - (sc->ipmi_bios_info.offset * 2), - sc->ipmi_bios_info.offset * 2, - RF_ACTIVE); - } - - if (!sc->ipmi_io_res && !sc->ipmi_mem_res){ - device_printf(dev, "couldn't configure smbios res\n"); - error = ENXIO; - goto bad; - } - - flags = INB(sc, sc->ipmi_smic_flags); - if (flags == 0xff) { - device_printf(dev, "couldn't find it\n"); - error = ENXIO; - goto bad; - } - if ((flags & SMIC_STATUS_SMS_ATN) - && (flags & SMIC_STATUS_RX_RDY)){ - /* skip in smbio mode - ipmi_read(dev, NULL, 0); - */ - } - } else { - device_printf(dev, "No IPMI interface found\n"); - error = ENXIO; - goto bad; + mtx_lock(&ipmi_info_mtx); + switch (ipmi_probed) { + case 0: + /* Need to probe the SMBIOS table. */ + ipmi_probed++; + mtx_unlock(&ipmi_info_mtx); + ipmi_smbios_probe(&ipmi_info); + mtx_lock(&ipmi_info_mtx); + ipmi_probed++; + wakeup(&ipmi_info); + break; + case 1: + /* Another thread is currently probing the table, so wait. */ + while (ipmi_probed == 1) + msleep(&ipmi_info, &ipmi_info_mtx, 0, "ipmi info", 0); + break; + default: + /* The cached data is available. */ + break; } - ipmi_attach(dev); - return 0; -bad: - /* - device_delete_child(device_get_parent(dev), dev); - */ - return error; + bcopy(&ipmi_info, info, sizeof(ipmi_info)); + mtx_unlock(&ipmi_info_mtx); + + return (info->iface_type != 0); } static int @@ -468,71 +310,5 @@ smbios_cksum (struct smbios_table_entry *e) cksum += ptr[i]; } - return cksum; + return (cksum); } - - -static int -ipmi_smbios_detach (device_t dev) -{ - struct ipmi_softc *sc; - - sc = device_get_softc(dev); - ipmi_detach(dev); - if (sc->ipmi_ev_tag) - EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_ev_tag); - - if (sc->ipmi_mem_res) - bus_release_resource(dev, SYS_RES_MEMORY, sc->ipmi_mem_rid, - sc->ipmi_mem_res); - if (sc->ipmi_io_res) - bus_release_resource(dev, SYS_RES_IOPORT, sc->ipmi_io_rid, - sc->ipmi_io_res); - return 0; -} - - -static device_method_t ipmi_methods[] = { - /* Device interface */ - DEVMETHOD(device_identify, ipmi_smbios_identify), - DEVMETHOD(device_probe, ipmi_smbios_probe), - DEVMETHOD(device_attach, ipmi_smbios_attach), - DEVMETHOD(device_detach, ipmi_smbios_detach), - { 0, 0 } -}; - -static driver_t ipmi_smbios_driver = { - "ipmi", - ipmi_methods, - sizeof(struct ipmi_softc), -}; - -static int -ipmi_smbios_modevent (mod, what, arg) - module_t mod; - int what; - void * arg; -{ - device_t * devs; - int count; - int i; - - switch (what) { - case MOD_LOAD: - return 0; - case MOD_UNLOAD: - devclass_get_devices(ipmi_devclass, &devs, &count); - for (i = 0; i < count; i++) { - device_delete_child(device_get_parent(devs[i]), - devs[i]); - } - break; - default: - break; - } - - return 0; -} - -DRIVER_MODULE(ipmi, isa, ipmi_smbios_driver, ipmi_devclass, - ipmi_smbios_modevent, 0); diff --git a/sys/dev/ipmi/ipmivars.h b/sys/dev/ipmi/ipmivars.h index c793eab2486d..c56fb88c3849 100644 --- a/sys/dev/ipmi/ipmivars.h +++ b/sys/dev/ipmi/ipmivars.h @@ -26,50 +26,116 @@ * $FreeBSD$ */ +#ifndef __IPMIVARS_H__ +#define __IPMIVARS_H__ + struct ipmi_get_info { - int kcs_mode; - int smic_mode; + int iface_type; uint64_t address; int offset; int io_mode; + int irq; +}; + +struct ipmi_device; + +struct ipmi_request { + TAILQ_ENTRY(ipmi_request) ir_link; + struct ipmi_device *ir_owner; /* Driver uses NULL. */ + u_char *ir_request; /* Request is data to send to BMC. */ + size_t ir_requestlen; + u_char *ir_reply; /* Reply is data read from BMC. */ + size_t ir_replybuflen; /* Length of ir_reply[] buffer. */ + int ir_replylen; /* Length of reply from BMC. */ + int ir_error; + long ir_msgid; + uint8_t ir_addr; + uint8_t ir_command; + uint8_t ir_compcode; +}; + +#define MAX_RES 3 +#define KCS_DATA 0 +#define KCS_CTL_STS 1 +#define SMIC_DATA 0 +#define SMIC_CTL_STS 1 +#define SMIC_FLAGS 2 + +struct ipmi_softc; + +/* Per struct cdev data. */ +struct ipmi_device { + TAILQ_ENTRY(ipmi_device) ipmi_link; + TAILQ_HEAD(,ipmi_request) ipmi_completed_requests; + struct selinfo ipmi_select; + struct ipmi_softc *ipmi_softc; + struct cdev *ipmi_cdev; + int ipmi_open; + int ipmi_closing; + int ipmi_requests; + u_char ipmi_address; /* IPMB address. */ + u_char ipmi_lun; +}; + +struct ipmi_kcs { +}; + +struct ipmi_smic { +}; + +struct ipmi_ssif { + device_t smbus; + int smbus_address; }; struct ipmi_softc { device_t ipmi_dev; - device_t ipmi_smbios_dev; - struct cdev *ipmi_dev_t; - int ipmi_refcnt; - struct smbios_table_entry *ipmi_smbios; - struct ipmi_get_info ipmi_bios_info; - int ipmi_kcs_status_reg; - int ipmi_kcs_command_reg; - int ipmi_kcs_data_out_reg; - int ipmi_kcs_data_in_reg; - int ipmi_smic_data; - int ipmi_smic_ctl_sts; - int ipmi_smic_flags; + union { + struct ipmi_kcs kcs; + struct ipmi_smic smic; + struct ipmi_ssif ssif; + } _iface; int ipmi_io_rid; - struct resource * ipmi_io_res; - int ipmi_mem_rid; - struct resource * ipmi_mem_res; + int ipmi_io_type; + struct resource *ipmi_io_res[MAX_RES]; + int ipmi_io_spacing; int ipmi_irq_rid; - struct resource * ipmi_irq_res; + struct resource *ipmi_irq_res; void *ipmi_irq; - u_char ipmi_address; - u_char ipmi_lun; - int ipmi_busy; - struct selinfo ipmi_select; - int ipmi_timestamp; - int ipmi_requests; - struct callout_handle ipmi_timeout_handle; - TAILQ_HEAD(,ipmi_done_list) ipmi_done; - eventhandler_tag ipmi_ev_tag; + int ipmi_detaching; +#ifdef CLONING + int ipmi_cloning; + u_int ipmi_cdev_mask; + TAILQ_HEAD(,ipmi_device) ipmi_cdevs; +#else + struct ipmi_device ipmi_idev; +#endif + TAILQ_HEAD(,ipmi_request) ipmi_pending_requests; +#ifdef CLONING + eventhandler_tag ipmi_clone_tag; +#endif + eventhandler_tag ipmi_watchdog_tag; + struct intr_config_hook ipmi_ich; + struct mtx ipmi_lock; + struct cv ipmi_request_added; + struct proc *ipmi_kthread; + driver_intr_t *ipmi_intr; + int (*ipmi_startup)(struct ipmi_softc *); + int (*ipmi_enqueue_request)(struct ipmi_softc *, struct ipmi_request *); }; +#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 +#define SSIF_MODE 0x04 + /* KCS status flags */ #define KCS_STATUS_OBF 0x01 /* Data Out ready from BMC */ #define KCS_STATUS_IBF 0x02 /* Data In from System */ @@ -84,11 +150,13 @@ struct ipmi_ipmb { #define KCS_STATUS_STATE_READ 0x1 #define KCS_STATUS_STATE_WRITE 0x2 #define KCS_STATUS_STATE_ERROR 0x3 +#define KCS_IFACE_STATUS_OK 0x00 #define KCS_IFACE_STATUS_ABORT 0x01 #define KCS_IFACE_STATUS_ILLEGAL 0x02 #define KCS_IFACE_STATUS_LENGTH_ERR 0x06 +#define KCS_IFACE_STATUS_UNKNOWN_ERR 0xff -/* KCD control codes */ +/* KCS control codes */ #define KCS_CONTROL_GET_STATUS_ABORT 0x60 #define KCS_CONTROL_WRITE_START 0x61 #define KCS_CONTROL_WRITE_END 0x62 @@ -98,9 +166,10 @@ struct ipmi_ipmb { #define SMIC_STATUS_BUSY 0x01 /* System set and BMC clears it */ #define SMIC_STATUS_SMS_ATN 0x04 /* BMC has a message */ #define SMIC_STATUS_EVT_ATN 0x08 /* Event has been RX */ -#define SMIC_STATUS_SMI 0x08 /* asserted SMI */ +#define SMIC_STATUS_SMI 0x10 /* asserted SMI */ #define SMIC_STATUS_TX_RDY 0x40 /* Ready to accept WRITE */ #define SMIC_STATUS_RX_RDY 0x80 /* Ready to read */ +#define SMIC_STATUS_RESERVED 0x22 /* SMIC control codes */ #define SMIC_CC_SMS_GET_STATUS 0x40 @@ -120,19 +189,78 @@ struct ipmi_ipmb { #define SMIC_SC_SMS_RD_NEXT 0xc5 #define SMIC_SC_SMS_RD_END 0xc6 -#define RES(x) (x)->ipmi_io_res ? (x)->ipmi_io_res : (x)->ipmi_mem_res -#define INB(sc, x) bus_space_read_1(rman_get_bustag(RES(sc)), \ - rman_get_bushandle(RES(sc)), (x)) -#define OUTB(sc, x, value) bus_space_write_1(rman_get_bustag(RES(sc)), \ - rman_get_bushandle(RES(sc)), (x), value) +#define IPMI_ADDR(netfn, lun) ((netfn) << 2 | (lun)) +#define IPMI_REPLY_ADDR(addr) ((addr) + 0x4) + +#define IPMI_LOCK(sc) mtx_lock(&(sc)->ipmi_lock) +#define IPMI_UNLOCK(sc) mtx_unlock(&(sc)->ipmi_lock) +#define IPMI_LOCK_ASSERT(sc) mtx_assert(&(sc)->ipmi_lock, MA_OWNED) + +#define ipmi_alloc_driver_request(addr, cmd, reqlen, replylen) \ + ipmi_alloc_request(NULL, 0, (addr), (cmd), (reqlen), (replylen)) -int ipmi_attach(device_t); -int ipmi_detach(device_t); -int ipmi_smbios_query(device_t); -int ipmi_smbios_probe(device_t); -int ipmi_read(device_t, u_char *, int); -void ipmi_intr(void *); +#if __FreeBSD_version < 601105 +#define bus_read_1(r, o) \ + bus_space_read_1(rman_get_bustag(r), rman_get_bushandle(r), (o)) +#define bus_write_1(r, o, v) \ + bus_space_write_1(rman_get_bustag(r), rman_get_bushandle(r), (o), (v)) +#endif + +/* I/O to a single I/O resource. */ +#define INB_SINGLE(sc, x) \ + bus_read_1((sc)->ipmi_io_res[0], (sc)->ipmi_io_spacing * (x)) +#define OUTB_SINGLE(sc, x, value) \ + bus_write_1((sc)->ipmi_io_res[0], (sc)->ipmi_io_spacing * (x), value) + +/* I/O with each register in its in I/O resource. */ +#define INB_MULTIPLE(sc, x) \ + bus_read_1((sc)->ipmi_io_res[(x)], 0) +#define OUTB_MULTIPLE(sc, x, value) \ + bus_write_1((sc)->ipmi_io_res[(x)], 0, value) + +/* + * Determine I/O method based on whether or not we have more than one I/O + * resource. + */ +#define INB(sc, x) \ + ((sc)->ipmi_io_res[1] != NULL ? INB_MULTIPLE(sc, x) : INB_SINGLE(sc, x)) +#define OUTB(sc, x, value) \ + ((sc)->ipmi_io_res[1] != NULL ? OUTB_MULTIPLE(sc, x, value) : \ + OUTB_SINGLE(sc, x, value)) + +#define MAX_TIMEOUT 3 * hz + +int ipmi_attach(device_t); +int ipmi_detach(device_t); +void ipmi_release_resources(device_t); + +/* Manage requests. */ +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 *); +struct ipmi_request *ipmi_dequeue_request(struct ipmi_softc *); +void ipmi_free_request(struct ipmi_request *); +int ipmi_polled_enqueue_request(struct ipmi_softc *, struct ipmi_request *); +int ipmi_submit_driver_request(struct ipmi_softc *, struct ipmi_request *, + int); + +/* Identify BMC interface via SMBIOS. */ +int ipmi_smbios_identify(struct ipmi_get_info *); + +/* Match BMC PCI device listed in SMBIOS. */ +const char *ipmi_pci_match(uint16_t, uint16_t); + +/* Interface attach routines. */ +int ipmi_kcs_attach(struct ipmi_softc *); +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 -device_t ipmi_smbios_identify (driver_t *driver, device_t parent); extern devclass_t ipmi_devclass; extern int ipmi_attached; + +#endif /* !__IPMIVARS_H__ */ |
