Index: amd64/conf/NOTES =================================================================== RCS file: /usr/cvs/src/sys/amd64/conf/NOTES,v retrieving revision 1.58 diff -u -r1.58 NOTES --- amd64/conf/NOTES 29 Jul 2006 18:38:53 -0000 1.58 +++ amd64/conf/NOTES 13 Sep 2006 21:13:26 -0000 @@ -346,6 +346,7 @@ # # Miscellaneous hardware: # +# ipmi: Intelligent Platform Management Interface # smbios: DMI/SMBIOS entry point # vpd: Vital Product Data kernel interface # cy: Cyclades serial driver @@ -368,6 +369,7 @@ device digi_Xe device digi_Xem device digi_Xr +device ipmi # Parallel (8255 PPI) basic I/O (mode 0) port (e.g. Advantech PCL-724) device pbio hint.pbio.0.at="isa" Index: conf/files.amd64 =================================================================== RCS file: /usr/cvs/src/sys/conf/files.amd64,v retrieving revision 1.93 diff -u -r1.93 files.amd64 --- conf/files.amd64 5 Sep 2006 16:55:12 -0000 1.93 +++ conf/files.amd64 8 Sep 2006 21:18:20 -0000 @@ -161,7 +161,13 @@ dev/if_ndis/if_ndis_usb.c optional ndis usb dev/io/iodev.c optional io dev/ipmi/ipmi.c optional ipmi -dev/ipmi/ipmi_smbios.c optional ipmi isa +dev/ipmi/ipmi_acpi.c optional ipmi acpi +dev/ipmi/ipmi_isa.c optional ipmi isa +dev/ipmi/ipmi_kcs.c optional ipmi +dev/ipmi/ipmi_smic.c optional ipmi +dev/ipmi/ipmi_smbus.c optional ipmi smbus +dev/ipmi/ipmi_smbios.c optional ipmi +dev/ipmi/ipmi_ssif.c optional ipmi smbus dev/ipmi/ipmi_pci.c optional ipmi pci dev/fdc/fdc.c optional fdc dev/fdc/fdc_acpi.c optional fdc Index: conf/files.i386 =================================================================== RCS file: /usr/cvs/src/sys/conf/files.i386,v retrieving revision 1.565 diff -u -r1.565 files.i386 --- conf/files.i386 15 Aug 2006 12:54:28 -0000 1.565 +++ conf/files.i386 8 Sep 2006 21:18:20 -0000 @@ -196,7 +196,13 @@ dev/if_ndis/if_ndis_usb.c optional ndis usb dev/io/iodev.c optional io dev/ipmi/ipmi.c optional ipmi -dev/ipmi/ipmi_smbios.c optional ipmi isa +dev/ipmi/ipmi_acpi.c optional ipmi acpi +dev/ipmi/ipmi_isa.c optional ipmi isa +dev/ipmi/ipmi_kcs.c optional ipmi +dev/ipmi/ipmi_smic.c optional ipmi +dev/ipmi/ipmi_smbus.c optional ipmi smbus +dev/ipmi/ipmi_smbios.c optional ipmi +dev/ipmi/ipmi_ssif.c optional ipmi smbus dev/ipmi/ipmi_pci.c optional ipmi pci dev/kbd/kbd.c optional atkbd | sc | ukbd | vt dev/le/if_le_isa.c optional le isa Index: dev/ipmi/ipmi.c =================================================================== RCS file: /usr/cvs/src/sys/dev/ipmi/ipmi.c,v retrieving revision 1.3 diff -u -r1.3 ipmi.c --- dev/ipmi/ipmi.c 16 May 2006 14:36:26 -0000 1.3 +++ dev/ipmi/ipmi.c 18 Sep 2006 20:33:31 -0000 @@ -25,24 +25,21 @@ */ #include -__FBSDID("$FreeBSD: src/sys/dev/ipmi/ipmi.c,v 1.3 2006/05/16 14:36:26 phk Exp $"); +__FBSDID("$FreeBSD: src/sys/dev/ipmi/ipmi.c,v 1.3.2.1 2006/06/15 21:12:15 ambrisko Exp $"); #include #include +#include +#include +#include #include #include -#include -#include - -#include #include -#include - -#include -#include +#include #include -#include +#include #include +#include #ifdef LOCAL_MODULE #include @@ -52,29 +49,7 @@ #include #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 @@ 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 @@ 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 int -ipmi_close(struct cdev *dev, int flags, int fmt, struct thread *td) +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 *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); + } + } + + /* 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 error; + return (0); } #ifdef IPMB @@ -171,707 +211,384 @@ 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; - - 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); - - 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); + 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, &req->ir_request[7], data_len); + temp[data_len + 7] = ipmi_ipmb_checksum(&req->ir_request[4], + data_len + 3); + + ipmi_submit_driver_request(sc, req); + error = req->ir_error; + 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); - if (temp[2] == 0) { - if (temp[3] & IPMI_MSG_BUFFER_FULL) { + ipmi_submit_driver_request(sc, req); + + if (req->ir_error == 0 && req->ir_compcode == 0) { + if (req->ir_reply[0] & IPMI_MSG_BUFFER_FULL) { device_printf(sc->ipmi_dev, "message buffer full"); } - if (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; + return (error); } +#endif -void -ipmi_intr(void *arg) { - device_t dev = arg; - - ipmi_check_read(dev); -} - -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: - /* 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); - } + 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)); - 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], - req->msg.data_len); - if (error != 0) { - free(temp, M_IPMI); - 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); + if (error) + return (error); - return 0; + IPMI_LOCK(sc); + /* clear out old stuff in queue of stuff done */ + /* XXX: This seems odd. */ + while ((kreq = TAILQ_FIRST(&dev->ipmi_completed_requests))) { + TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, + ir_link); + dev->ipmi_requests--; + ipmi_free_request(kreq); + } + IPMI_UNLOCK(sc); + + kreq = ipmi_alloc_request(dev, req->msgid, + IPMI_ADDR(req->msg.netfn, 0), req->msg.cmd, + req->msg.data_len, IPMI_MAX_RX); + error = copyin(req->msg.data, kreq->ir_request, + req->msg.data_len); + if (error) { + ipmi_free_request(kreq); + return (error); + } + IPMI_LOCK(sc); + dev->ipmi_requests++; + error = sc->ipmi_enqueue_request(sc, kreq); + IPMI_UNLOCK(sc); + if (error) + return (error); + break; +#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; +/* + * Request management. + */ - 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)); +/* 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; + + 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; + } + if (replylen) { + req->ir_reply = (char *)&req[1] + requestlen; + req->ir_replybuflen = replylen; } - return status; + return (req); } -static void -ipmi_clear_obf(device_t dev, int status) { - struct ipmi_softc *sc = device_get_softc(dev); - int data; +/* Free a request no longer in use. */ +void +ipmi_free_request(struct ipmi_request *req) +{ - /* Clear OBF */ - if (status & KCS_STATUS_OBF) { - data = INB(sc, sc->ipmi_kcs_data_out_reg); - } + free(req, M_IPMI); } -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); +/* 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; - /* Clear OBF */ - ipmi_clear_obf(dev, status); - break; - } + IPMI_LOCK_ASSERT(sc); - retry++; - if (retry > 2) { - device_printf(dev, "Retry exhausted %x\n", retry); - break; - } + /* + * 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); } } -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; +/* Enqueue an internal driver request and wait until it is completed. */ +int +ipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, + int timo) +{ + int error; - do { - flags = INB(sc, sc->ipmi_smic_flags); - } while(!flags & SMIC_STATUS_RX_RDY); + 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); } -static void -ipmi_wait_for_not_busy(device_t dev) { - struct ipmi_softc *sc = device_get_softc(dev); - int flags; +/* + * 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; - do { - flags = INB(sc, sc->ipmi_smic_flags); - } while(flags & SMIC_STATUS_BUSY); -} + IPMI_LOCK_ASSERT(sc); -static void -ipmi_set_busy(device_t dev) { - struct ipmi_softc *sc = device_get_softc(dev); - int flags; + 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); - flags = INB(sc, sc->ipmi_smic_flags); - flags |= SMIC_STATUS_BUSY; - OUTB(sc, sc->ipmi_smic_flags, flags); + req = TAILQ_FIRST(&sc->ipmi_pending_requests); + TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); + return (req); } +/* Default implementation of ipmi_enqueue_request() for polled interfaces. */ 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); - - return i; -} - - -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++); - - /* Wait for IBF = 0 */ - status = ipmi_wait_for_ibf(dev, 0); - - 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); - - /* Wait for IBF = 0 */ - status = ipmi_wait_for_ibf(dev, 0); +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 @@ */ 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; - } - - s = splhigh(); - ipmi_write(dev, temp, 8); - - while (!ipmi_ready_to_read(dev)) - DELAY(1000); - bzero(temp, IPMI_MAX_RX); - ipmi_read(dev, temp, IPMI_MAX_RX); + 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; + } + + error = ipmi_submit_driver_request(sc, req, 0); + if (error) + device_printf(sc->ipmi_dev, "Failed to set watchdog\n"); - if (sec) { - temp[0] = IPMI_APP_REQUEST << 2; - temp[1] = IPMI_RESET_WDOG; + if (error == 0 && sec) { + ipmi_free_request(req); - ipmi_write(dev, temp, 2); + req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), + IPMI_RESET_WDOG, 0, 0); - 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 @@ /* 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 @@ 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; + + 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; + } - 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); - - 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); - - temp[0] = IPMI_APP_REQUEST << 2; - temp[1] = IPMI_CLEAR_FLAGS; - temp[2] = 8; - 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] == 0xc0) { + 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); + + ipmi_submit_driver_request(sc, req, 0); + + /* 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 +} + +int +ipmi_attach(device_t dev) +{ + struct ipmi_softc *sc = device_get_softc(dev); + int error; - return 0; + 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,90 @@ 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; + + devclass_get_devices(ipmi_devclass, &devs, &count); + for (i = 0; i < count; i++) + device_delete_child(device_get_parent(devs[i]), devs[i]); } +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]; Index: dev/ipmi/ipmi_acpi.c =================================================================== RCS file: dev/ipmi/ipmi_acpi.c diff -N dev/ipmi/ipmi_acpi.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ dev/ipmi/ipmi_acpi.c 18 Sep 2006 20:33:31 -0000 @@ -0,0 +1,210 @@ +/*- + * Copyright (c) 2006 IronPort Systems Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Hooks for the ACPI CA debugging infrastructure */ +#define _COMPONENT ACPI_BUTTON +ACPI_MODULE_NAME("IPMI") + +#ifdef LOCAL_MODULE +#include +#include +#else +#include +#include +#endif + +static int ipmi_acpi_probe(device_t); +static int ipmi_acpi_attach(device_t); + +int +ipmi_acpi_probe(device_t dev) +{ + static char *ipmi_ids[] = {"IPI0001", NULL}; + + if (ipmi_attached) + return (EBUSY); + + if (acpi_disabled("ipmi") || + ACPI_ID_PROBE(device_get_parent(dev), dev, ipmi_ids) == NULL) + return (ENXIO); + + device_set_desc(dev, "IPMI System Interface"); + + return (0); +} + +static int +ipmi_acpi_attach(device_t dev) +{ + ACPI_HANDLE devh; + const char *mode; + struct ipmi_get_info info; + struct ipmi_softc *sc = device_get_softc(dev); + int count, error, flags, i, type; + int interface_type = 0, interface_version = 0; + + error = 0; + devh = acpi_get_handle(dev); + if (ACPI_FAILURE(acpi_GetInteger(devh, "_IFT", &interface_type))) + return (ENXIO); + + if (ACPI_FAILURE(acpi_GetInteger(devh, "_SRV", &interface_version))) + return (ENXIO); + + switch (interface_type) { + case KCS_MODE: + count = 2; + mode = "KCS"; + break; + case SMIC_MODE: + count = 3; + mode = "SMIC"; + break; + case BT_MODE: + device_printf(dev, "BT interface not supported\n"); + return (ENXIO); + case SSIF_MODE: + if (ACPI_FAILURE(acpi_GetInteger(devh, "_ADR", &flags))) + return (ENXIO); + info.address = flags >> 1; + device_printf(dev, "SSIF interface not supported on ACPI\n"); + return (0); + default: + return (ENXIO); + } + + if (bus_get_resource(dev, SYS_RES_IOPORT, 0, NULL, NULL) == 0) + type = SYS_RES_IOPORT; + else if (bus_get_resource(dev, SYS_RES_MEMORY, 0, NULL, NULL) == 0) + type = SYS_RES_MEMORY; + else { + device_printf(dev, "unknown resource type\n"); + return (ENXIO); + } + + sc->ipmi_io_rid = 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 = 1; + if (sc->ipmi_io_res[0] == NULL) { + device_printf(dev, "couldn't configure I/O resource\n"); + return (ENXIO); + } + + /* If we have multiple resources, allocate up to MAX_RES. */ + for (i = 1; i < MAX_RES; i++) { + sc->ipmi_io_rid = i; + sc->ipmi_io_res[i] = bus_alloc_resource_any(dev, type, + &sc->ipmi_io_rid, RF_ACTIVE); + if (sc->ipmi_io_res[i] == NULL) + break; + } + sc->ipmi_io_rid = 0; + + /* If we have multiple resources, make sure we have enough of them. */ + if (sc->ipmi_io_res[1] != NULL && sc->ipmi_io_res[count - 1] == NULL) { + device_printf(dev, "too few I/O resources\n"); + error = ENXIO; + goto bad; + } + + device_printf(dev, "%s mode found at %s 0x%jx on %s\n", + mode, type == SYS_RES_IOPORT ? "io" : "mem", + (uintmax_t)rman_get_start(sc->ipmi_io_res[0]), + device_get_name(device_get_parent(dev))); + + sc->ipmi_dev = dev; + + /* + * Setup an interrupt if we have an interrupt resource. We + * don't support GPE interrupts via _GPE yet. + */ + 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); + + /* Warn if _GPE exists. */ + if (ACPI_SUCCESS(AcpiEvaluateObject(devh, "_GPE", NULL, NULL))) + device_printf(dev, "_GPE support not implemented\n"); + + /* + * We assume an alignment of 1 byte as currently the IPMI spec + * doesn't provide any way to determine the alignment via ACPI. + */ + switch (interface_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; + } + error = ipmi_attach(dev); + if (error) + goto bad; + + return (0); +bad: + ipmi_release_resources(dev); + return (error); +} + +static device_method_t ipmi_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ipmi_acpi_probe), + DEVMETHOD(device_attach, ipmi_acpi_attach), + DEVMETHOD(device_detach, ipmi_detach), + { 0, 0 } +}; + +static driver_t ipmi_acpi_driver = { + "ipmi", + ipmi_methods, + sizeof(struct ipmi_softc), +}; + +DRIVER_MODULE(ipmi_acpi, acpi, ipmi_acpi_driver, ipmi_devclass, 0, 0); +MODULE_DEPEND(ipmi_acpi, acpi, 1, 1, 1); Index: dev/ipmi/ipmi_isa.c =================================================================== RCS file: dev/ipmi/ipmi_isa.c diff -N dev/ipmi/ipmi_isa.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ dev/ipmi/ipmi_isa.c 18 Sep 2006 21:49:23 -0000 @@ -0,0 +1,209 @@ +/*- + * Copyright (c) 2006 IronPort Systems Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#ifdef LOCAL_MODULE +#include +#include +#else +#include +#include +#endif + +static void +ipmi_isa_identify(driver_t *driver, device_t parent) +{ + struct ipmi_get_info info; + uint32_t devid; + + if (ipmi_smbios_identify(&info) && info.iface_type != SSIF_MODE && + device_find_child(parent, "ipmi", -1) == NULL) { + /* + * XXX: Hack alert. On some broken systems, the IPMI + * interface is described via SMBIOS, but the actual + * IO resource is in a PCI device BAR, so we have to let + * the PCI device attach ipmi instead. In that case don't + * create an isa ipmi device. For now we hardcode the list + * of bus, device, function tuples. + */ + devid = pci_cfgregread(0, 4, 2, PCIR_DEVVENDOR, 4); + if (devid != 0xffffffff && + ipmi_pci_match(devid & 0xffff, devid >> 16) != NULL) + return; + BUS_ADD_CHILD(parent, 0, "ipmi", -1); + } +} + +static int +ipmi_isa_probe(device_t dev) +{ + + /* Skip any PNP devices. */ + if (isa_get_logicalid(dev) != 0) + return (ENXIO); + + device_set_desc(dev, "IPMI System Interface"); + return (BUS_PROBE_DEFAULT); +} + +static int +ipmi_isa_attach(device_t dev) +{ + struct ipmi_softc *sc = device_get_softc(dev); + struct ipmi_get_info info; + const char *mode; + int count, error, i, type; + + /* This should never fail. */ + if (!ipmi_smbios_identify(&info)) + return (ENXIO); + + /* + * Give other drivers precedence. Unfortunately, this doesn't + * work if we have an SMBIOS table that duplicates a PCI device + * that's later on the bus than the PCI-ISA bridge. + */ + if (ipmi_attached) + return (EBUSY); + + switch (info.iface_type) { + case KCS_MODE: + count = 2; + mode = "KCS"; + break; + case SMIC_MODE: + count = 3; + mode = "SMIC"; + break; + case BT_MODE: + device_printf(dev, "BT mode is unsupported\n"); + return (ENXIO); + default: + return (ENXIO); + } + error = 0; + sc->ipmi_dev = dev; + + 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_type = type; + sc->ipmi_io_spacing = info.offset; + if (info.offset == 1) { + sc->ipmi_io_rid = 0; + sc->ipmi_io_res[0] = bus_alloc_resource(dev, type, + &sc->ipmi_io_rid, info.address, info.address + count - 1, + count, RF_ACTIVE); + if (sc->ipmi_io_res[0] == NULL) { + device_printf(dev, "couldn't configure I/O resource\n"); + return (ENXIO); + } + } else { + for (i = 0; i < count; i++) { + sc->ipmi_io_rid = i; + sc->ipmi_io_res[i] = bus_alloc_resource(dev, type, + &sc->ipmi_io_rid, info.address + i * info.offset, + info.address + i * info.offset, 1, RF_ACTIVE); + if (sc->ipmi_io_res[i] == NULL) { + device_printf(dev, + "couldn't configure I/O resource\n"); + error = ENXIO; + sc->ipmi_io_rid = 0; + goto bad; + } + } + sc->ipmi_io_rid = 0; + } + + if (info.irq != 0) { + sc->ipmi_irq_rid = 0; + sc->ipmi_irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, + &sc->ipmi_irq_rid, info.irq, info.irq, 1, + 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; + } + + error = ipmi_attach(dev); + if (error) + goto bad; + + return (0); +bad: + ipmi_release_resources(dev); + return (error); +} + +static device_method_t ipmi_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, ipmi_isa_identify), + DEVMETHOD(device_probe, ipmi_isa_probe), + DEVMETHOD(device_attach, ipmi_isa_attach), + DEVMETHOD(device_detach, ipmi_detach), + { 0, 0 } +}; + +static driver_t ipmi_isa_driver = { + "ipmi", + ipmi_methods, + sizeof(struct ipmi_softc), +}; + +DRIVER_MODULE(ipmi_isa, isa, ipmi_isa_driver, ipmi_devclass, 0, 0); Index: dev/ipmi/ipmi_kcs.c =================================================================== RCS file: dev/ipmi/ipmi_kcs.c diff -N dev/ipmi/ipmi_kcs.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ dev/ipmi/ipmi_kcs.c 18 Sep 2006 20:33:31 -0000 @@ -0,0 +1,607 @@ +/*- + * Copyright (c) 2006 IronPort Systems Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef LOCAL_MODULE +#include +#include +#else +#include +#include +#endif + +static void kcs_clear_obf(struct ipmi_softc *, int); +static void kcs_error(struct ipmi_softc *); +static int kcs_wait_for_ibf(struct ipmi_softc *, int); +static int kcs_wait_for_obf(struct ipmi_softc *, int); + +static int +kcs_wait_for_ibf(struct ipmi_softc *sc, int state) +{ + int status, start = ticks; + + status = INB(sc, KCS_CTL_STS); + if (state == 0) { + /* WAIT FOR IBF = 0 */ + while (ticks - start < MAX_TIMEOUT && status & KCS_STATUS_IBF) { + DELAY(100); + status = INB(sc, KCS_CTL_STS); + } + } else { + /* WAIT FOR IBF = 1 */ + while (ticks - start < MAX_TIMEOUT && + !(status & KCS_STATUS_IBF)) { + DELAY(100); + status = INB(sc, KCS_CTL_STS); + } + } + return (status); +} + +static int +kcs_wait_for_obf(struct ipmi_softc *sc, int state) +{ + int status, start = ticks; + + status = INB(sc, KCS_CTL_STS); + if (state == 0) { + /* WAIT FOR OBF = 0 */ + while (ticks - start < MAX_TIMEOUT && status & KCS_STATUS_OBF) { + DELAY(100); + status = INB(sc, KCS_CTL_STS); + } + } else { + /* WAIT FOR OBF = 1 */ + while (ticks - start < MAX_TIMEOUT && + !(status & KCS_STATUS_OBF)) { + DELAY(100); + status = INB(sc, KCS_CTL_STS); + } + } + return (status); +} + +static void +kcs_clear_obf(struct ipmi_softc *sc, int status) +{ + int data; + + /* Clear OBF */ + if (status & KCS_STATUS_OBF) { + data = INB(sc, KCS_DATA); + } +} + +static void +kcs_error(struct ipmi_softc *sc) +{ + int retry, status; + u_char data; + + for (retry = 0; retry < 2; retry++) { + + /* Wait for IBF = 0 */ + status = kcs_wait_for_ibf(sc, 0); + + /* ABORT */ + OUTB(sc, KCS_CTL_STS, KCS_CONTROL_GET_STATUS_ABORT); + + /* Wait for IBF = 0 */ + status = kcs_wait_for_ibf(sc, 0); + + /* Clear OBF */ + kcs_clear_obf(sc, status); + + if (status & KCS_STATUS_OBF) { + data = INB(sc, KCS_DATA); + if (data != 0) + device_printf(sc->ipmi_dev, + "KCS Error Data %02x\n", data); + } + + /* 0x00 to DATA_IN */ + OUTB(sc, KCS_DATA, 0x00); + + /* Wait for IBF = 0 */ + status = kcs_wait_for_ibf(sc, 0); + + if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_READ) { + + /* Wait for OBF = 1 */ + status = kcs_wait_for_obf(sc, 1); + + /* Read error status */ + data = INB(sc, KCS_DATA); + if (data != 0) + device_printf(sc->ipmi_dev, "KCS error: %02x\n", + data); + + /* Write READ into Data_in */ + OUTB(sc, KCS_DATA, KCS_DATA_IN_READ); + + /* Wait for IBF = 0 */ + status = kcs_wait_for_ibf(sc, 0); + } + + /* IDLE STATE */ + if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_IDLE) { + /* Wait for OBF = 1 */ + status = kcs_wait_for_obf(sc, 1); + + /* Clear OBF */ + kcs_clear_obf(sc, status); + return; + } + } + device_printf(sc->ipmi_dev, "KCS Error retry exhausted\n"); +} + +/* + * Start to write a request. Waits for IBF to clear and then sends the + * WR_START command. + */ +static int +kcs_start_write(struct ipmi_softc *sc) +{ + int retry, status; + + for (retry = 0; retry < 10; retry++) { + /* Wait for IBF = 0 */ + status = kcs_wait_for_ibf(sc, 0); + + /* Clear OBF */ + kcs_clear_obf(sc, status); + + /* Write start to command */ + OUTB(sc, KCS_CTL_STS, KCS_CONTROL_WRITE_START); + + /* Wait for IBF = 0 */ + status = kcs_wait_for_ibf(sc, 0); + if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_WRITE) + break; + DELAY(1000000); + } + + if (KCS_STATUS_STATE(status) != KCS_STATUS_STATE_WRITE) + /* error state */ + return (0); + + /* Clear OBF */ + kcs_clear_obf(sc, status); + + return (1); +} + +/* + * Write a byte of the request message, excluding the last byte of the + * message which requires special handling. + */ +static int +kcs_write_byte(struct ipmi_softc *sc, u_char data) +{ + int status; + + /* Data to Data */ + OUTB(sc, KCS_DATA, data); + + /* Wait for IBF = 0 */ + status = kcs_wait_for_ibf(sc, 0); + + if (KCS_STATUS_STATE(status) != KCS_STATUS_STATE_WRITE) + return (0); + + /* Clear OBF */ + kcs_clear_obf(sc, status); + return (1); +} + +/* + * Write the last byte of a request message. + */ +static int +kcs_write_last_byte(struct ipmi_softc *sc, u_char data) +{ + int status; + + /* Write end to command */ + OUTB(sc, KCS_CTL_STS, KCS_CONTROL_WRITE_END); + + /* Wait for IBF = 0 */ + status = kcs_wait_for_ibf(sc, 0); + + if (KCS_STATUS_STATE(status) != KCS_STATUS_STATE_WRITE) + /* error state */ + return (0); + + /* Clear OBF */ + kcs_clear_obf(sc, status); + + /* Send data byte to DATA. */ + OUTB(sc, KCS_DATA, data); + return (1); +} + +/* + * Read one byte of the reply message. + */ +static int +kcs_read_byte(struct ipmi_softc *sc, u_char *data) +{ + int status; + u_char dummy; + + /* Wait for IBF = 0 */ + status = kcs_wait_for_ibf(sc, 0); + + /* Read State */ + if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_READ) { + + /* Wait for OBF = 1 */ + status = kcs_wait_for_obf(sc, 1); + + /* Read Data_out */ + *data = INB(sc, KCS_DATA); + + /* Write READ into Data_in */ + OUTB(sc, KCS_DATA, KCS_DATA_IN_READ); + return (1); + } + + /* Idle State */ + if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_IDLE) { + + /* Wait for OBF = 1*/ + status = kcs_wait_for_obf(sc, 1); + + /* Read Dummy */ + dummy = INB(sc, KCS_DATA); + return (2); + } + + /* Error State */ + return (0); +} + +/* + * Send a request message and collect the reply. Returns true if we + * succeed. + */ +static int +kcs_polled_request(struct ipmi_softc *sc, struct ipmi_request *req) +{ + u_char *cp, data; + int i, state; + + /* Send the request. */ + if (!kcs_start_write(sc)) { + device_printf(sc->ipmi_dev, "KCS: Failed to start write\n"); + goto fail; + } +#ifdef KCS_DEBUG + device_printf(sc->ipmi_dev, "KCS: WRITE_START... ok\n"); +#endif + + if (!kcs_write_byte(sc, req->ir_addr)) { + device_printf(sc->ipmi_dev, "KCS: Failed to write address\n"); + goto fail; + } +#ifdef KCS_DEBUG + device_printf(sc->ipmi_dev, "KCS: Wrote address: %02x\n", req->ir_addr); +#endif + + if (req->ir_requestlen == 0) { + if (!kcs_write_last_byte(sc, req->ir_command)) { + device_printf(sc->ipmi_dev, + "KCS: Failed to write command\n"); + goto fail; + } +#ifdef KCS_DEBUG + device_printf(sc->ipmi_dev, "KCS: Wrote command: %02x\n", + req->ir_command); +#endif + } else { + if (!kcs_write_byte(sc, req->ir_command)) { + device_printf(sc->ipmi_dev, + "KCS: Failed to write command\n"); + goto fail; + } +#ifdef KCS_DEBUG + device_printf(sc->ipmi_dev, "KCS: Wrote command: %02x\n", + req->ir_command); +#endif + + cp = req->ir_request; + for (i = 0; i < req->ir_requestlen - 1; i++) { + if (!kcs_write_byte(sc, *cp++)) { + device_printf(sc->ipmi_dev, + "KCS: Failed to write data byte %d\n", + i + 1); + goto fail; + } +#ifdef KCS_DEBUG + device_printf(sc->ipmi_dev, "KCS: Wrote data: %02x\n", + cp[-1]); +#endif + } + + if (!kcs_write_last_byte(sc, *cp)) { + device_printf(sc->ipmi_dev, + "KCS: Failed to write last dta byte\n"); + goto fail; + } +#ifdef KCS_DEBUG + device_printf(sc->ipmi_dev, "KCS: Wrote last data: %02x\n", + *cp); +#endif + } + + /* Read the reply. First, read the NetFn/LUN. */ + if (kcs_read_byte(sc, &data) != 1) { + device_printf(sc->ipmi_dev, "KCS: Failed to read address\n"); + goto fail; + } +#ifdef KCS_DEBUG + device_printf(sc->ipmi_dev, "KCS: Read address: %02x\n", data); +#endif + if (data != IPMI_REPLY_ADDR(req->ir_addr)) { + device_printf(sc->ipmi_dev, "KCS: Reply address mismatch\n"); + goto fail; + } + + /* Next we read the command. */ + if (kcs_read_byte(sc, &data) != 1) { + device_printf(sc->ipmi_dev, "KCS: Failed to read command\n"); + goto fail; + } +#ifdef KCS_DEBUG + device_printf(sc->ipmi_dev, "KCS: Read command: %02x\n", data); +#endif + if (data != req->ir_command) { + device_printf(sc->ipmi_dev, "KCS: Command mismatch\n"); + goto fail; + } + + /* Next we read the completion code. */ + if (kcs_read_byte(sc, &req->ir_compcode) != 1) { + device_printf(sc->ipmi_dev, + "KCS: Failed to read completion code\n"); + goto fail; + } +#ifdef KCS_DEBUG + device_printf(sc->ipmi_dev, "KCS: Read completion code: %02x\n", + req->ir_compcode); +#endif + + /* Finally, read the reply from the BMC. */ + i = 0; + for (;;) { + state = kcs_read_byte(sc, &data); + if (state == 0) { + device_printf(sc->ipmi_dev, + "KCS: Read failed on byte %d\n", i + 1); + goto fail; + } + if (state == 2) + break; + if (i < req->ir_replybuflen) { + req->ir_reply[i] = data; +#ifdef KCS_DEBUG + device_printf(sc->ipmi_dev, "KCS: Read data %02x\n", + data); + } else { + device_printf(sc->ipmi_dev, + "KCS: Read short %02x byte %d\n", data, i + 1); +#endif + } + i++; + } + req->ir_replylen = i; +#ifdef KCS_DEBUG + device_printf(sc->ipmi_dev, "KCS: READ finished (%d bytes)\n", i); + if (req->ir_replybuflen < i) +#else + if (req->ir_replybuflen < i && req->ir_replybuflen != 0) +#endif + device_printf(sc->ipmi_dev, + "KCS: Read short: %zd buffer, %d actual\n", + req->ir_replybuflen, i); + return (1); +fail: + kcs_error(sc); + return (0); +} + +static void +kcs_loop(void *arg) +{ + struct ipmi_softc *sc = arg; + struct ipmi_request *req; + int i, ok; + + IPMI_LOCK(sc); + while ((req = ipmi_dequeue_request(sc)) != NULL) { + ok = 0; + for (i = 0; i < 3 && !ok; i++) + ok = kcs_polled_request(sc, req); + if (ok) + req->ir_error = 0; + else + req->ir_error = EIO; + ipmi_complete_request(sc, req); + } + IPMI_UNLOCK(sc); + kthread_exit(0); +} + +static int +kcs_startup(struct ipmi_softc *sc) +{ + + return (kthread_create(kcs_loop, sc, &sc->ipmi_kthread, 0, 0, "%s: kcs", + device_get_nameunit(sc->ipmi_dev))); +} + +int +ipmi_kcs_attach(struct ipmi_softc *sc) +{ + int status; + + /* Setup function pointers. */ + sc->ipmi_startup = kcs_startup; + sc->ipmi_enqueue_request = ipmi_polled_enqueue_request; + + /* See if we can talk to the controller. */ + status = INB(sc, KCS_CTL_STS); + if (status == 0xff) { + device_printf(sc->ipmi_dev, "couldn't find it\n"); + return (ENXIO); + } + +#ifdef KCS_DEBUG + device_printf(sc->ipmi_dev, "KCS: initial state: %02x\n", status); +#endif + if (status & KCS_STATUS_OBF || + KCS_STATUS_STATE(status) != KCS_STATUS_STATE_IDLE) + kcs_error(sc); + + return (0); +} + +/* + * Determine the alignment automatically for a PCI attachment. In this case, + * any unused bytes will return 0x00 when read. We make use of the C/D bit + * in the CTL_STS register to try to start a GET_STATUS transaction. When + * we write the command, that bit should be set, so we should get a non-zero + * value back when we read CTL_STS if the offset we are testing is the CTL_STS + * register. + */ +int +ipmi_kcs_probe_align(struct ipmi_softc *sc) +{ + int data, status; + + sc->ipmi_io_spacing = 1; +retry: +#ifdef KCS_DEBUG + device_printf(sc->ipmi_dev, "Trying KCS align %d... ", sc->ipmi_io_spacing); +#endif + + /* Wait for IBF = 0 */ + status = INB(sc, KCS_CTL_STS); + while (status & KCS_STATUS_IBF) { + DELAY(100); + status = INB(sc, KCS_CTL_STS); + } + + OUTB(sc, KCS_CTL_STS, KCS_CONTROL_GET_STATUS_ABORT); + + /* Wait for IBF = 0 */ + status = INB(sc, KCS_CTL_STS); + while (status & KCS_STATUS_IBF) { + DELAY(100); + status = INB(sc, KCS_CTL_STS); + } + + /* If we got 0x00 back, then this must not be the CTL_STS register. */ + if (status == 0) { +#ifdef KCS_DEBUG + printf("failed\n"); +#endif + sc->ipmi_io_spacing <<= 1; + if (sc->ipmi_io_spacing > 4) + return (0); + goto retry; + } +#ifdef KCS_DEBUG + printf("ok\n"); +#endif + + /* Finish out the transaction. */ + + /* Clear OBF */ + if (status && KCS_STATUS_OBF) + data = INB(sc, KCS_DATA); + + /* 0x00 to DATA_IN */ + OUTB(sc, KCS_DATA, 0); + + /* Wait for IBF = 0 */ + status = INB(sc, KCS_CTL_STS); + while (status & KCS_STATUS_IBF) { + DELAY(100); + status = INB(sc, KCS_CTL_STS); + } + + if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_READ) { + /* Wait for IBF = 1 */ + while (!(status & KCS_STATUS_OBF)) { + DELAY(100); + status = INB(sc, KCS_CTL_STS); + } + + /* Read error status. */ + data = INB(sc, KCS_DATA); + + /* Write dummy READ to DATA_IN. */ + OUTB(sc, KCS_DATA, KCS_DATA_IN_READ); + + /* Wait for IBF = 0 */ + status = INB(sc, KCS_CTL_STS); + while (status & KCS_STATUS_IBF) { + DELAY(100); + status = INB(sc, KCS_CTL_STS); + } + } + + if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_IDLE) { + /* Wait for IBF = 1 */ + while (!(status & KCS_STATUS_OBF)) { + DELAY(100); + status = INB(sc, KCS_CTL_STS); + } + + /* Clear OBF */ + if (status && KCS_STATUS_OBF) + data = INB(sc, KCS_DATA); + } else + device_printf(sc->ipmi_dev, "KCS probe: end state %x\n", + KCS_STATUS_STATE(status)); + + return (1); +} Index: dev/ipmi/ipmi_pci.c =================================================================== RCS file: /usr/cvs/src/sys/dev/ipmi/ipmi_pci.c,v retrieving revision 1.1 diff -u -r1.1 ipmi_pci.c --- dev/ipmi/ipmi_pci.c 10 Feb 2006 20:51:35 -0000 1.1 +++ dev/ipmi/ipmi_pci.c 18 Sep 2006 20:33:31 -0000 @@ -25,21 +25,17 @@ */ #include -__FBSDID("$FreeBSD: src/sys/dev/ipmi/ipmi_pci.c,v 1.1 2006/02/10 20:51:35 ambrisko Exp $"); +__FBSDID("$FreeBSD: src/sys/dev/ipmi/ipmi_pci.c,v 1.1.2.1 2006/06/15 21:12:15 ambrisko Exp $"); #include -#include #include +#include +#include +#include #include #include -#include - -#include -#include - -#include -#include #include +#include #include #include @@ -52,17 +48,8 @@ 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 @@ {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); + sc->ipmi_dev = dev; - /* - * 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); + 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); } - 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; + 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); } - 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; + + 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; } - error = ipmi_smbios_probe(sc->ipmi_smbios_dev); - if (error != 0) { + error = ipmi_attach(dev); + if (error) goto bad; + + 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) +}; + +DRIVER_MODULE(ipmi_pci, pci, ipmi_pci_driver, ipmi_devclass, 0, 0); + +/* Native IPMI on PCI driver. */ + +static int +ipmi2_pci_probe(device_t 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_dev = dev; - error = ipmi_smbios_query(dev); - device_delete_child(dev, sc->ipmi_smbios_dev); - if (error != 0) - 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))); - - 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 = 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); - } + return (ENXIO); +} - if (!sc->ipmi_io_res){ - device_printf(dev, "couldn't configure pci io res\n"); - goto bad; - } +static int +ipmi2_pci_attach(device_t dev) +{ + struct ipmi_softc *sc; + int error, iface, type; - 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))); - - 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 = 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); - } + sc = device_get_softc(dev); + sc->ipmi_dev = dev; - if (!sc->ipmi_io_res && !sc->ipmi_mem_res){ - device_printf(dev, "couldn't configure pci res\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); + } - 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; + /* + * 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); + + switch (iface) { + case KCS_MODE: + device_printf(dev, "using KSC interface\n"); + + /* + * 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; } + + error = ipmi_kcs_attach(sc); + if (error) + goto bad; + break; + case SMIC_MODE: + device_printf(dev, "using SMIC interface\n"); + + error = ipmi_smic_attach(sc); + if (error) + goto bad; + break; } + error = ipmi_attach(dev); + if (error) + goto bad; - return 0; + return (0); bad: - return ENXIO; + ipmi_release_resources(dev); + return (error); } -static int ipmi_pci_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); - 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); - - return 0; -} +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 ipmi_pci_driver = { - "ipmi", - ipmi_methods, - sizeof(struct ipmi_softc) +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); Index: dev/ipmi/ipmi_smbios.c =================================================================== RCS file: /usr/cvs/src/sys/dev/ipmi/ipmi_smbios.c,v retrieving revision 1.1 diff -u -r1.1 ipmi_smbios.c --- dev/ipmi/ipmi_smbios.c 10 Feb 2006 20:51:35 -0000 1.1 +++ dev/ipmi/ipmi_smbios.c 18 Sep 2006 20:33:31 -0000 @@ -25,24 +25,17 @@ */ #include -__FBSDID("$FreeBSD: src/sys/dev/ipmi/ipmi_smbios.c,v 1.1 2006/02/10 20:51:35 ambrisko Exp $"); +__FBSDID("$FreeBSD: src/sys/dev/ipmi/ipmi_smbios.c,v 1.1.2.1 2006/06/15 21:12:15 ambrisko Exp $"); #include #include +#include +#include +#include #include -#include #include -#include -#include - -#include -#include -#include -#include - #include -#include #include #include @@ -77,7 +70,7 @@ uint16_t handle; }; -struct ipmi_device { +struct ipmi_entry { uint8_t type; uint8_t length; uint16_t handle; @@ -90,50 +83,88 @@ 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; } - info->io_mode = s->base_address & 1; + 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->iface_type = s->interface_type; } static char * @@ -147,7 +178,7 @@ *table = 0; /* Skip past terminating null byte */ - return p + 1; + return (p + 1); } @@ -177,282 +208,88 @@ } } -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"); - } - - 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; - } - - 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)); + if (addr == 0) + return; + /* + * 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_mapdev(addr, sizeof(struct smbios_table_entry)); + table = pmap_mapdev(addr, header->length); + pmap_unmapdev((vm_offset_t)header, sizeof(struct smbios_table_entry)); + header = table; + if (smbios_cksum(header) != 0) { + pmap_unmapdev((vm_offset_t)header, header->length); + return; + } + + /* Now map the actual table and walk it looking for an IPMI entry. */ + table = pmap_mapdev(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); - -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; + smbios_run_table(table, header->number_structures, dispatch_smbios_ipmi, + info); + + /* Unmap everything. */ + pmap_unmapdev((vm_offset_t)table, header->structure_table_length); + pmap_unmapdev((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 +305,5 @@ cksum += ptr[i]; } - 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; + return (cksum); } - -DRIVER_MODULE(ipmi, isa, ipmi_smbios_driver, ipmi_devclass, - ipmi_smbios_modevent, 0); Index: dev/ipmi/ipmi_smbus.c =================================================================== RCS file: dev/ipmi/ipmi_smbus.c diff -N dev/ipmi/ipmi_smbus.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ dev/ipmi/ipmi_smbus.c 18 Sep 2006 20:33:31 -0000 @@ -0,0 +1,131 @@ +/*- + * Copyright (c) 2006 IronPort Systems Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "smbus_if.h" + +#ifdef LOCAL_MODULE +#include +#else +#include +#endif + +static void ipmi_smbus_identify(driver_t *driver, device_t parent); +static int ipmi_smbus_probe(device_t dev); +static int ipmi_smbus_attach(device_t dev); + +static void +ipmi_smbus_identify(driver_t *driver, device_t parent) +{ + struct ipmi_get_info info; + + if (ipmi_smbios_identify(&info) && info.iface_type == SSIF_MODE && + device_find_child(parent, "ipmi", -1) == NULL) + BUS_ADD_CHILD(parent, 0, "ipmi", -1); +} + +static int +ipmi_smbus_probe(device_t dev) +{ + + device_set_desc(dev, "IPMI System Interface"); + return (BUS_PROBE_DEFAULT); +} + +static int +ipmi_smbus_attach(device_t dev) +{ + struct ipmi_softc *sc = device_get_softc(dev); + struct ipmi_get_info info; + int error; + + /* This should never fail. */ + if (!ipmi_smbios_identify(&info)) + return (ENXIO); + + if (info.iface_type != SSIF_MODE) { + device_printf(dev, "No SSIF IPMI interface found\n"); + return (ENXIO); + } + + sc->ipmi_dev = dev; + + if (info.irq != 0) { + sc->ipmi_irq_rid = 0; + sc->ipmi_irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, + &sc->ipmi_irq_rid, info.irq, info.irq, 1, + RF_SHAREABLE | RF_ACTIVE); + } + + device_printf(dev, "SSIF mode found at address 0x%llx on %s\n", + (long long)info.address, device_get_name(device_get_parent(dev))); + error = ipmi_ssif_attach(sc, device_get_parent(dev), info.address); + if (error) + goto bad; + + error = ipmi_attach(dev); + if (error) + goto bad; + + return (0); +bad: + ipmi_release_resources(dev); + return (error); +} + +static device_method_t ipmi_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, ipmi_smbus_identify), + DEVMETHOD(device_probe, ipmi_smbus_probe), + DEVMETHOD(device_attach, ipmi_smbus_attach), + DEVMETHOD(device_detach, ipmi_detach), + { 0, 0 } +}; + +static driver_t ipmi_smbus_driver = { + "ipmi", + ipmi_methods, + sizeof(struct ipmi_softc) +}; + +DRIVER_MODULE(ipmi_smbus, smbus, ipmi_smbus_driver, ipmi_devclass, 0, 0); +MODULE_DEPEND(ipmi_smbus, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); Index: dev/ipmi/ipmi_smic.c =================================================================== RCS file: dev/ipmi/ipmi_smic.c diff -N dev/ipmi/ipmi_smic.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ dev/ipmi/ipmi_smic.c 18 Sep 2006 20:33:31 -0000 @@ -0,0 +1,361 @@ +/*- + * Copyright (c) 2006 IronPort Systems Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef LOCAL_MODULE +#include +#include +#else +#include +#include +#endif + +static void smic_wait_for_tx_okay(struct ipmi_softc *); +static void smic_wait_for_rx_okay(struct ipmi_softc *); +static void smic_wait_for_not_busy(struct ipmi_softc *); +static void smic_set_busy(struct ipmi_softc *); + +static void +smic_wait_for_tx_okay(struct ipmi_softc *sc) +{ + int flags; + + do { + flags = INB(sc, SMIC_FLAGS); + } while (!(flags & SMIC_STATUS_TX_RDY)); +} + +static void +smic_wait_for_rx_okay(struct ipmi_softc *sc) +{ + int flags; + + do { + flags = INB(sc, SMIC_FLAGS); + } while (!(flags & SMIC_STATUS_RX_RDY)); +} + +static void +smic_wait_for_not_busy(struct ipmi_softc *sc) +{ + int flags; + + do { + flags = INB(sc, SMIC_FLAGS); + } while (flags & SMIC_STATUS_BUSY); +} + +static void +smic_set_busy(struct ipmi_softc *sc) +{ + int flags; + + flags = INB(sc, SMIC_FLAGS); + flags |= SMIC_STATUS_BUSY; + flags &= ~SMIC_STATUS_RESERVED; + OUTB(sc, SMIC_FLAGS, flags); +} + +/* + * Start a transfer with a WR_START transaction that sends the NetFn/LUN + * address. + */ +static int +smic_start_write(struct ipmi_softc *sc, u_char data) +{ + u_char error, status; + + smic_wait_for_not_busy(sc); + + OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_START); + OUTB(sc, SMIC_DATA, data); + smic_set_busy(sc); + smic_wait_for_not_busy(sc); + status = INB(sc, SMIC_CTL_STS); + if (status != SMIC_SC_SMS_WR_START) { + error = INB(sc, SMIC_DATA); + device_printf(sc->ipmi_dev, "SMIC: Write did not start %02x\n", + error); + return (0); + } + return (1); +} + +/* + * Write a byte in the middle of the message (either the command or one of + * the data bytes) using a WR_NEXT transaction. + */ +static int +smic_write_next(struct ipmi_softc *sc, u_char data) +{ + u_char error, status; + + OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_NEXT); + smic_wait_for_tx_okay(sc); + OUTB(sc, SMIC_DATA, data); + smic_set_busy(sc); + smic_wait_for_not_busy(sc); + status = INB(sc, SMIC_CTL_STS); + if (status != SMIC_SC_SMS_WR_NEXT) { + error = INB(sc, SMIC_DATA); + device_printf(sc->ipmi_dev, "SMIC: Write did not next %02x\n", + error); + return (0); + } + return (1); +} + +/* + * Write the last byte of a transfer to end the write phase via a WR_END + * transaction. + */ +static int +smic_write_last(struct ipmi_softc *sc, u_char data) +{ + u_char error, status; + + OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_END); + smic_wait_for_tx_okay(sc); + OUTB(sc, SMIC_DATA, data); + smic_set_busy(sc); + smic_wait_for_not_busy(sc); + status = INB(sc, SMIC_CTL_STS); + if (status != SMIC_SC_SMS_WR_END) { + error = INB(sc, SMIC_DATA); + device_printf(sc->ipmi_dev, "SMIC: Write did not end %02x\n", + error); + return (0); + } + return (1); +} + +/* + * Start the read phase of a transfer with a RD_START transaction. + */ +static int +smic_start_read(struct ipmi_softc *sc, u_char *data) +{ + u_char error, status; + + smic_wait_for_not_busy(sc); + + OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_START); + smic_wait_for_rx_okay(sc); + smic_set_busy(sc); + smic_wait_for_not_busy(sc); + status = INB(sc, SMIC_CTL_STS); + if (status != SMIC_SC_SMS_RD_START) { + error = INB(sc, SMIC_DATA); + device_printf(sc->ipmi_dev, "SMIC: Read did not start %02x\n", + error); + return (0); + } + *data = INB(sc, SMIC_DATA); + return (1); +} + +/* + * Read a byte via a RD_NEXT transaction. If this was the last byte, return + * 2 rather than 1. + */ +static int +smic_read_byte(struct ipmi_softc *sc, u_char *data) +{ + u_char error, status; + + OUTB(sc, SMIC_CTL_STS, SMIC_SC_SMS_RD_NEXT); + smic_wait_for_rx_okay(sc); + smic_set_busy(sc); + smic_wait_for_not_busy(sc); + status = INB(sc, SMIC_CTL_STS); + if (status != SMIC_SC_SMS_RD_NEXT && + status != SMIC_SC_SMS_RD_END) { + error = INB(sc, SMIC_DATA); + device_printf(sc->ipmi_dev, "SMIC: Read did not next %02x\n", + error); + return (0); + } + *data = INB(sc, SMIC_DATA); + if (status == SMIC_SC_SMS_RD_NEXT) + return (1); + else + return (2); +} + +/* Complete a transfer via a RD_END transaction after reading the last byte. */ +static int +smic_read_end(struct ipmi_softc *sc) +{ + u_char error, status; + + OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_END); + smic_set_busy(sc); + smic_wait_for_not_busy(sc); + status = INB(sc, SMIC_CTL_STS); + if (status != SMIC_SC_SMS_RDY) { + error = INB(sc, SMIC_DATA); + device_printf(sc->ipmi_dev, "SMIC: Read did not end %02x\n", + error); + return (0); + } + return (1); +} + +static int +smic_polled_request(struct ipmi_softc *sc, struct ipmi_request *req) +{ + u_char *cp, data; + int i, state; + + /* First, start the message with the address. */ + if (!smic_start_write(sc, req->ir_addr)) + return (0); + + if (req->ir_requestlen == 0) { + /* Send the command as the last byte. */ + if (!smic_write_last(sc, req->ir_command)) + return (0); + } else { + /* Send the command. */ + if (!smic_write_next(sc, req->ir_command)) + return (0); + + /* Send the payload. */ + cp = req->ir_request; + for (i = 0; i < req->ir_requestlen - 1; i++) + if (!smic_write_next(sc, *cp++)) + return (0); + if (!smic_write_last(sc, *cp)) + return (0); + } + + /* Start the read phase by reading the NetFn/LUN. */ + if (smic_start_read(sc, &data) != 1) + return (0); + if (data != IPMI_REPLY_ADDR(req->ir_addr)) { + device_printf(sc->ipmi_dev, "SMIC: Reply address mismatch\n"); + return (0); + } + + /* Read the command. */ + if (smic_read_byte(sc, &data) != 1) + return (0); + if (data != req->ir_command) { + device_printf(sc->ipmi_dev, "SMIC: Command mismatch\n"); + return (0); + } + + /* Read the completion code. */ + state = smic_read_byte(sc, &req->ir_compcode); + if (state == 0) + return (0); + + /* Finally, read the reply from the BMC. */ + i = 0; + while (state == 1) { + state = smic_read_byte(sc, &data); + if (state == 0) + return (0); + if (state == 2) + break; + if (i < req->ir_replybuflen) + req->ir_reply[i] = data; + i++; + } + + /* Terminate the transfer. */ + if (!smic_read_end(sc)) + return (0); + req->ir_replylen = i; + if (req->ir_replybuflen < i && req->ir_replybuflen != 0) + device_printf(sc->ipmi_dev, + "SMIC: Read short: %zd buffer, %d actual\n", + req->ir_replybuflen, i); + return (1); +} + +static void +smic_loop(void *arg) +{ + struct ipmi_softc *sc = arg; + struct ipmi_request *req; + int i, ok; + + IPMI_LOCK(sc); + while ((req = ipmi_dequeue_request(sc)) != NULL) { + ok = 0; + for (i = 0; i < 3 && !ok; i++) + ok = smic_polled_request(sc, req); + if (ok) + req->ir_error = 0; + else + req->ir_error = EIO; + ipmi_complete_request(sc, req); + } + IPMI_UNLOCK(sc); + kthread_exit(0); +} + +static int +smic_startup(struct ipmi_softc *sc) +{ + + return (kthread_create(smic_loop, sc, &sc->ipmi_kthread, 0, 0, + "%s: smic", device_get_nameunit(sc->ipmi_dev))); +} + +int +ipmi_smic_attach(struct ipmi_softc *sc) +{ + int flags; + + /* Setup function pointers. */ + sc->ipmi_startup = smic_startup; + sc->ipmi_enqueue_request = ipmi_polled_enqueue_request; + + /* See if we can talk to the controller. */ + flags = INB(sc, SMIC_FLAGS); + if (flags == 0xff) { + device_printf(sc->ipmi_dev, "couldn't find it\n"); + return (ENXIO); + } + + return (0); +} Index: dev/ipmi/ipmi_ssif.c =================================================================== RCS file: dev/ipmi/ipmi_ssif.c diff -N dev/ipmi/ipmi_ssif.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ dev/ipmi/ipmi_ssif.c 18 Sep 2006 20:33:31 -0000 @@ -0,0 +1,375 @@ +/*- + * Copyright (c) 2006 IronPort Systems Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "smbus_if.h" + +#ifdef LOCAL_MODULE +#include +#else +#include +#endif + +#define SMBUS_WRITE_SINGLE 0x02 +#define SMBUS_WRITE_START 0x06 +#define SMBUS_WRITE_CONT 0x07 +#define SMBUS_READ_START 0x03 +#define SMBUS_READ_CONT 0x09 +#define SMBUS_DATA_SIZE 32 + +#ifdef SSIF_DEBUG +static void +dump_buffer(device_t dev, const char *msg, u_char *bytes, int len) +{ + int i; + + device_printf(dev, "%s:", msg); + for (i = 0; i < len; i++) + printf(" %02x", bytes[i]); + printf("\n"); +} +#endif + +static int +ssif_polled_request(struct ipmi_softc *sc, struct ipmi_request *req) +{ + u_char ssif_buf[SMBUS_DATA_SIZE]; + device_t dev = sc->ipmi_dev; + device_t smbus = sc->ipmi_ssif_smbus; + u_char *cp, block, count, offset; + size_t len; + int error; + + /* Acquire the bus while we send the request. */ + if (smbus_request_bus(smbus, dev, SMB_WAIT) != 0) + return (0); + + /* + * First, send out the request. Begin by filling out the first + * packet which includes the NetFn/LUN and command. + */ + ssif_buf[0] = req->ir_addr; + ssif_buf[1] = req->ir_command; + if (req->ir_requestlen > 0) + bcopy(req->ir_request, &ssif_buf[2], + min(req->ir_requestlen, SMBUS_DATA_SIZE - 2)); + + /* Small requests are sent with a single command. */ + if (req->ir_requestlen <= 30) { +#ifdef SSIF_DEBUG + dump_buffer(dev, "WRITE_SINGLE", ssif_buf, + req->ir_requestlen + 2); +#endif + error = smbus_error(smbus_bwrite(smbus, + sc->ipmi_ssif_smbus_address, SMBUS_WRITE_SINGLE, + req->ir_requestlen + 2, ssif_buf)); + if (error) { +#ifdef SSIF_ERROR_DEBUG + device_printf(dev, "SSIF: WRITE_SINGLE error %d\n", + error); +#endif + goto fail; + } + } else { + /* Longer requests are sent out in 32-byte messages. */ +#ifdef SSIF_DEBUG + dump_buffer(dev, "WRITE_START", ssif_buf, SMBUS_DATA_SIZE); +#endif + error = smbus_error(smbus_bwrite(smbus, + sc->ipmi_ssif_smbus_address, SMBUS_WRITE_START, + SMBUS_DATA_SIZE, ssif_buf)); + if (error) { +#ifdef SSIF_ERROR_DEBUG + device_printf(dev, "SSIF: WRITE_START error %d\n", + error); +#endif + goto fail; + } + + len = req->ir_requestlen - (SMBUS_DATA_SIZE - 2); + cp = req->ir_request + (SMBUS_DATA_SIZE - 2); + while (len > 0) { +#ifdef SSIF_DEBUG + dump_buffer(dev, "WRITE_CONT", cp, + min(len, SMBUS_DATA_SIZE)); +#endif + error = smbus_error(smbus_bwrite(smbus, + sc->ipmi_ssif_smbus_address, SMBUS_WRITE_CONT, + min(len, SMBUS_DATA_SIZE), cp)); + if (error) { +#ifdef SSIF_ERROR_DEBUG + device_printf(dev, "SSIF: WRITE_CONT error %d\n", + error); +#endif + goto fail; + } + cp += SMBUS_DATA_SIZE; + len -= SMBUS_DATA_SIZE; + } + + /* + * The final WRITE_CONT transaction has to have a non-zero + * length that is also not SMBUS_DATA_SIZE. If our last + * WRITE_CONT transaction in the loop sent SMBUS_DATA_SIZE + * bytes, then len will be 0, and we send an extra 0x00 byte + * to terminate the transaction. + */ + if (len == 0) { + char c = 0; + +#ifdef SSIF_DEBUG + dump_buffer(dev, "WRITE_CONT", &c, 1); +#endif + error = smbus_error(smbus_bwrite(smbus, + sc->ipmi_ssif_smbus_address, SMBUS_WRITE_CONT, + 1, &c)); + if (error) { +#ifdef SSIF_ERROR_DEBUG + device_printf(dev, "SSIF: WRITE_CONT error %d\n", + error); +#endif + goto fail; + } + } + } + + /* Release the bus. */ + smbus_release_bus(smbus, dev); + + /* Give the BMC 100ms to chew on the request. */ + tsleep(&error, 0, "ssifwt", hz / 10); + + /* Try to read the first packet. */ +read_start: + if (smbus_request_bus(smbus, dev, SMB_WAIT) != 0) + return (0); + count = SMBUS_DATA_SIZE; + error = smbus_error(smbus_bread(smbus, + sc->ipmi_ssif_smbus_address, SMBUS_READ_START, &count, ssif_buf)); + if (error == ENXIO || error == EBUSY) { + smbus_release_bus(smbus, dev); +#ifdef SSIF_DEBUG + device_printf(dev, "SSIF: READ_START retry\n"); +#endif + /* Give the BMC another 10ms. */ + tsleep(&error, 0, "ssifwt", hz / 100); + goto read_start; + } + if (error) { +#ifdef SSIF_ERROR_DEBUG + device_printf(dev, "SSIF: READ_START failed: %d\n", error); +#endif + goto fail; + } +#ifdef SSIF_DEBUG + device_printf("SSIF: READ_START: ok\n"); +#endif + + /* + * If this is the first part of a multi-part read, then we need to + * skip the first two bytes. + */ + if (count == SMBUS_DATA_SIZE && ssif_buf[0] == 0 && ssif_buf[1] == 1) + offset = 2; + else + offset = 0; + + /* We had better get the reply header. */ + if (count < 3) { + device_printf(dev, "SSIF: Short reply packet\n"); + goto fail; + } + + /* Verify the NetFn/LUN. */ + if (ssif_buf[offset] != IPMI_REPLY_ADDR(req->ir_addr)) { + device_printf(dev, "SSIF: Reply address mismatch\n"); + goto fail; + } + + /* Verify the command. */ + if (ssif_buf[offset + 1] != req->ir_command) { + device_printf(dev, "SMIC: Command mismatch\n"); + goto fail; + } + + /* Read the completion code. */ + req->ir_compcode = ssif_buf[offset + 2]; + + /* If this is a single read, just copy the data and return. */ + if (offset == 0) { +#ifdef SSIF_DEBUG + dump_buffer(dev, "READ_SINGLE", ssif_buf, count); +#endif + len = count - 3; + bcopy(&ssif_buf[3], req->ir_reply, + min(req->ir_replybuflen, len)); + goto done; + } + + /* + * This is the first part of a multi-read transaction, so copy + * out the payload and start looping. + */ +#ifdef SSIF_DEBUG + dump_buffer(dev, "READ_START", ssif_buf + 2, count - 2); +#endif + bcopy(&ssif_buf[5], req->ir_reply, min(req->ir_replybuflen, count - 5)); + len = count - 5; + block = 1; + + for (;;) { + /* Read another packet via READ_CONT. */ + count = SMBUS_DATA_SIZE; + error = smbus_error(smbus_bread(smbus, + sc->ipmi_ssif_smbus_address, SMBUS_READ_CONT, &count, + ssif_buf)); + if (error) { +#ifdef SSIF_ERROR_DEBUG + printf("SSIF: READ_CONT failed: %d\n", error); +#endif + goto fail; + } +#ifdef SSIF_DEBUG + device_printf(dev, "SSIF: READ_CONT... ok\n"); +#endif + + /* Verify the block number. 0xff marks the last block. */ + if (ssif_buf[0] != 0xff && ssif_buf[0] != block) { + device_printf(dev, "SSIF: Read wrong block %d %d\n", + ssif_buf[0], block); + goto fail; + } + if (ssif_buf[0] != 0xff && count < SMBUS_DATA_SIZE) { + device_printf(dev, + "SSIF: Read short middle block, length %d\n", + count); + goto fail; + } +#ifdef SSIF_DEBUG + if (ssif_buf[0] == 0xff) + dump_buffer(dev, "READ_END", ssif_buf + 1, count - 1); + else + dump_buffer(dev, "READ_CONT", ssif_buf + 1, count - 1); +#endif + if (len < req->ir_replybuflen) + bcopy(&ssif_buf[1], &req->ir_reply[len], + min(req->ir_replybuflen - len, count - 1)); + len += count - 1; + + /* If this was the last block we are done. */ + if (ssif_buf[0] != 0xff) + break; + block++; + } + +done: + /* Save the total length and return success. */ + req->ir_replylen = len; + smbus_release_bus(smbus, dev); + return (1); + +fail: + smbus_release_bus(smbus, dev); + return (0); +} + +static void +ssif_loop(void *arg) +{ + struct ipmi_softc *sc = arg; + struct ipmi_request *req; + int i, ok; + + IPMI_LOCK(sc); + while ((req = ipmi_dequeue_request(sc)) != NULL) { + IPMI_UNLOCK(sc); + ok = 0; + for (i = 0; i < 5; i++) { + ok = ssif_polled_request(sc, req); + if (ok) + break; + + /* Wait 60 ms between retries. */ + tsleep(&ok, 0, "retry", 60 * hz / 1000); +#ifdef SSIF_RETRY_DEBUG + device_printf(sc->ipmi_dev, + "SSIF: Retrying request (%d)\n", i + 1); +#endif + } + if (ok) + req->ir_error = 0; + else + req->ir_error = EIO; + IPMI_LOCK(sc); + ipmi_complete_request(sc, req); + IPMI_UNLOCK(sc); + + /* Enforce 10ms between requests. */ + tsleep(&ok, 0, "delay", hz / 100); + + IPMI_LOCK(sc); + } + IPMI_UNLOCK(sc); + kthread_exit(0); +} + +static int +ssif_startup(struct ipmi_softc *sc) +{ + + return (kthread_create(ssif_loop, sc, &sc->ipmi_kthread, 0, 0, + "%s: ssif", device_get_nameunit(sc->ipmi_dev))); +} + +int +ipmi_ssif_attach(struct ipmi_softc *sc, device_t smbus, int smbus_address) +{ + + /* Setup smbus address. */ + sc->ipmi_ssif_smbus = smbus; + sc->ipmi_ssif_smbus_address = smbus_address; + + /* Setup function pointers. */ + sc->ipmi_startup = ssif_startup; + sc->ipmi_enqueue_request = ipmi_polled_enqueue_request; + + return (0); +} Index: dev/ipmi/ipmivars.h =================================================================== RCS file: /usr/cvs/src/sys/dev/ipmi/ipmivars.h,v retrieving revision 1.1 diff -u -r1.1 ipmivars.h --- dev/ipmi/ipmivars.h 10 Feb 2006 20:51:35 -0000 1.1 +++ dev/ipmi/ipmivars.h 18 Sep 2006 20:33:31 -0000 @@ -23,53 +23,119 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/ipmi/ipmivars.h,v 1.1 2006/02/10 20:51:35 ambrisko Exp $ + * $FreeBSD: src/sys/dev/ipmi/ipmivars.h,v 1.1.2.1 2006/06/15 21:12:15 ambrisko Exp $ */ +#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 @@ #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 @@ #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 @@ #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) - -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 *); +#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)) + +#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__ */ Index: i386/conf/NOTES =================================================================== RCS file: /usr/cvs/src/sys/i386/conf/NOTES,v retrieving revision 1.1231 diff -u -r1.1231 NOTES --- i386/conf/NOTES 13 Sep 2006 01:23:08 -0000 1.1231 +++ i386/conf/NOTES 13 Sep 2006 21:21:47 -0000 @@ -698,6 +698,7 @@ # Miscellaneous hardware: # # apm: Laptop Advanced Power Management (experimental) +# ipmi: Intelligent Platform Management Interface # pmtimer: Timer device driver for power management events (APM or ACPI) # smapi: System Management Application Program Interface driver # smbios: DMI/SMBIOS entry point @@ -730,6 +731,7 @@ device apm hint.apm.0.flags="0x20" +device ipmi device smapi device smbios device vpd Index: modules/ipmi/Makefile =================================================================== RCS file: /usr/cvs/src/sys/modules/ipmi/Makefile,v retrieving revision 1.1 diff -u -r1.1 Makefile --- modules/ipmi/Makefile 10 Feb 2006 20:51:35 -0000 1.1 +++ modules/ipmi/Makefile 13 Sep 2006 21:18:29 -0000 @@ -2,8 +2,12 @@ .PATH: ${.CURDIR}/../../dev/ipmi +# XXX - ipmi_smbus and ipmi_ssif depend on smbus +# XXX - ipmi_acpi depends on acpi KMOD= ipmi -SRCS= ipmi.c ipmi_pci.c ipmi_smbios.c -SRCS+= bus_if.h device_if.h pci_if.h vnode_if.h +SRCS= ipmi.c ipmi_kcs.c ipmi_smic.c ipmi_smbios.c ipmi_ssif.c +SRCS+= ipmi_acpi.c ipmi_isa.c ipmi_pci.c ipmi_smbus.c +SRCS+= opt_acpi.h +SRCS+= acpi_if.h bus_if.h device_if.h isa_if.h pci_if.h smbus_if.h .include Index: sys/ipmi.h =================================================================== RCS file: /usr/cvs/src/sys/sys/ipmi.h,v retrieving revision 1.1 diff -u -r1.1 ipmi.h --- sys/ipmi.h 10 Feb 2006 20:51:35 -0000 1.1 +++ sys/ipmi.h 8 Sep 2006 21:18:20 -0000 @@ -26,6 +26,9 @@ * $FreeBSD: src/sys/sys/ipmi.h,v 1.1 2006/02/10 20:51:35 ambrisko Exp $ */ +#ifndef __SYS_IPMI_H__ +#define __SYS_IPMI_H__ + #define IPMI_MAX_ADDR_SIZE 0x20 #define IPMI_MAX_RX 1024 #define IPMI_BMC_SLAVE_ADDR 0x20 /* Linux Default slave address */ @@ -117,3 +120,36 @@ unsigned char slave_addr; unsigned char lun; }; + +#if defined(__amd64__) +/* Compatiblity with 32-bit binaries. */ + +#define IPMICTL_RECEIVE_MSG_TRUNC_32 _IOWR(IPMI_IOC_MAGIC, 11, struct ipmi_recv32) +#define IPMICTL_RECEIVE_MSG_32 _IOWR(IPMI_IOC_MAGIC, 12, struct ipmi_recv32) +#define IPMICTL_SEND_COMMAND_32 _IOW(IPMI_IOC_MAGIC, 13, struct ipmi_req32) + +struct ipmi_msg32 { + unsigned char netfn; + unsigned char cmd; + unsigned short data_len; + uint32_t data; +}; + +struct ipmi_req32 { + uint32_t addr; + unsigned int addr_len; + int32_t msgid; + struct ipmi_msg32 msg; +}; + +struct ipmi_recv32 { + int recv_type; + uint32_t addr; + unsigned int addr_len; + int32_t msgid; + struct ipmi_msg32 msg; +}; + +#endif + +#endif /* !__SYS_IPMI_H__ */