/*- * 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: head/sys/dev/ipmi/ipmi.c 158651 2006-05-16 14:37:58Z phk $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef LOCAL_MODULE #include #include #else #include #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) #endif static d_ioctl_t ipmi_ioctl; static d_poll_t ipmi_poll; static d_open_t ipmi_open; static d_close_t ipmi_close; int ipmi_attached = 0; #define IPMI_MINOR 0 static int on = 1; SYSCTL_NODE(_hw, OID_AUTO, ipmi, CTLFLAG_RD, 0, "IPMI driver parameters"); SYSCTL_INT(_hw_ipmi, OID_AUTO, on, CTLFLAG_RW, &on, 0, ""); 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, .d_poll = ipmi_poll, .d_name = "ipmi", }; MALLOC_DEFINE(M_IPMI, "ipmi", "ipmi"); static int ipmi_open(struct cdev *dev, int flags, int fmt, struct thread *td) { struct ipmi_softc *sc; if (!on) return ENOENT; sc = dev->si_drv1; if (sc->ipmi_refcnt) { return EBUSY; } sc->ipmi_refcnt = 1; return 0; } static int ipmi_poll(struct cdev *dev, int poll_events, struct thread *td) { struct ipmi_softc *sc; int revents = 0; sc = dev->si_drv1; ipmi_check_read(sc->ipmi_dev); if (poll_events & (POLLIN | POLLRDNORM)) { if (!TAILQ_EMPTY(&sc->ipmi_done)) revents |= poll_events & (POLLIN | POLLRDNORM); if (TAILQ_EMPTY(&sc->ipmi_done) && sc->ipmi_requests == 0) { revents |= POLLERR; } } if (revents == 0) { if (poll_events & (POLLIN | POLLRDNORM)) selrecord(td, &sc->ipmi_select); } return revents; } static int ipmi_close(struct cdev *dev, int flags, int fmt, struct thread *td) { struct ipmi_softc *sc; int error = 0; sc = dev->si_drv1; sc->ipmi_refcnt = 0; return error; } #ifdef IPMB static int ipmi_ipmb_checksum(u_char *data, int len) { u_char sum = 0; for (; len; len--) { sum += *data++; } return -sum; } 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; u_char slave_addr = 0x52; 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); return error; } static int ipmi_handle_attn(device_t dev) { u_char temp[IPMI_MAX_RX]; struct ipmi_softc *sc = device_get_softc(dev); 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); if (temp[2] == 0) { if (temp[3] & IPMI_MSG_BUFFER_FULL) { device_printf(sc->ipmi_dev, "message buffer full"); } if (temp[3] & 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); 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"); } return 0; } 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); } static int ipmi_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data, int flags, struct thread *td) { struct ipmi_softc *sc; 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; int error, len; sc = dev->si_drv1; 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); } 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); return 0; 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); addr.channel = IPMI_BMC_CHANNEL; 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; } 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; case IPMICTL_SET_MY_ADDRESS_CMD: sc->ipmi_address = *(int*)data; return 0; case IPMICTL_GET_MY_ADDRESS_CMD: *(int*)data = sc->ipmi_address; return 0; case IPMICTL_SET_MY_LUN_CMD: sc->ipmi_lun = *(int*)data & 0x3; return 0; case IPMICTL_GET_MY_LUN_CMD: *(int*)data = sc->ipmi_lun; return 0; case IPMICTL_SET_GETS_EVENTS_CMD: /* device_printf(sc->ipmi_dev, "IPMICTL_SET_GETS_EVENTS_CMD NA\n"); */ return 0; case IPMICTL_REGISTER_FOR_CMD: case IPMICTL_UNREGISTER_FOR_CMD: return EOPNOTSUPP; } 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)); } return status; } static int ipmi_wait_for_obf(device_t dev, int state) { struct ipmi_softc *sc = device_get_softc(dev); int status, start = ticks; int first = 1; if (state == 0) { /* WAIT FOR OBF = 0 */ do { if (first) first = 0; else DELAY(100); status = INB(sc, sc->ipmi_kcs_status_reg); } while (ticks - start < MAX_TIMEOUT && status & KCS_STATUS_OBF); } else { /* WAIT FOR OBF = 1 */ do { if (first) first =0; else DELAY(100); status = INB(sc, sc->ipmi_kcs_status_reg); } while (ticks - start < MAX_TIMEOUT && !(status & KCS_STATUS_OBF)); } return status; } static void ipmi_clear_obf(device_t dev, int status) { struct ipmi_softc *sc = device_get_softc(dev); int data; /* Clear OBF */ if (status & KCS_STATUS_OBF) { data = INB(sc, sc->ipmi_kcs_data_out_reg); } } static void ipmi_error(device_t dev) { struct ipmi_softc *sc = device_get_softc(dev); int status, data = 0; int retry = 0; for(;;){ status = ipmi_wait_for_ibf(dev, 0); /* ABORT */ OUTB(sc, sc->ipmi_kcs_command_reg, KCS_CONTROL_GET_STATUS_ABORT); /* Wait for IBF = 0 */ status = ipmi_wait_for_ibf(dev, 0); /* Clear OBF */ ipmi_clear_obf(dev, status); if (status & KCS_STATUS_OBF) { data = INB(sc, sc->ipmi_kcs_data_out_reg); device_printf(dev, "Data %x\n", data); } /* 0x00 to DATA_IN */ OUTB(sc, sc->ipmi_kcs_data_in_reg, 0x00); /* Wait for IBF = 0 */ status = ipmi_wait_for_ibf(dev, 0); if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_READ) { /* Wait for OBF = 1 */ status = ipmi_wait_for_obf(dev, 1); /* Read error status */ data = INB(sc, sc->ipmi_kcs_data_out_reg); /* Write READ into Data_in */ OUTB(sc, sc->ipmi_kcs_data_in_reg, KCS_DATA_IN_READ); /* Wait for IBF = 0 */ status = ipmi_wait_for_ibf(dev, 0); } /* IDLE STATE */ if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_IDLE) { /* Wait for OBF = 1 */ status = ipmi_wait_for_obf(dev, 1); /* Clear OBF */ ipmi_clear_obf(dev, status); break; } retry++; if (retry > 2) { device_printf(dev, "Retry exhausted %x\n", retry); break; } } } 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; do { flags = INB(sc, sc->ipmi_smic_flags); } while(!flags & SMIC_STATUS_RX_RDY); } static void ipmi_wait_for_not_busy(device_t dev) { struct ipmi_softc *sc = device_get_softc(dev); int flags; do { flags = INB(sc, sc->ipmi_smic_flags); } while(flags & SMIC_STATUS_BUSY); } static void ipmi_set_busy(device_t dev) { struct ipmi_softc *sc = device_get_softc(dev); int flags; flags = INB(sc, sc->ipmi_smic_flags); flags |= SMIC_STATUS_BUSY; OUTB(sc, sc->ipmi_smic_flags, flags); } 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); 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; } /* * Watchdog event handler. */ static void ipmi_set_watchdog(device_t dev, int sec) { u_char *temp; int s; temp = malloc(IPMI_MAX_RX, M_IPMI, M_WAITOK); temp[0] = IPMI_APP_REQUEST << 2; if (sec) { temp[1] = IPMI_SET_WDOG; temp[2] = 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; } 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); if (sec) { temp[0] = IPMI_APP_REQUEST << 2; temp[1] = IPMI_RESET_WDOG; ipmi_write(dev, temp, 2); while (!ipmi_ready_to_read(dev)) DELAY(1000); bzero(temp, IPMI_MAX_RX); ipmi_read(dev, temp, IPMI_MAX_RX); } splx(s); free(temp, M_IPMI); /* dump_watchdog(dev); */ } static void ipmi_wd_event(void *arg, unsigned int cmd, int *error) { struct ipmi_softc *sc = arg; unsigned int timeout; /* disable / enable */ if (!(cmd & WD_ACTIVE)) { ipmi_set_watchdog(sc->ipmi_dev, 0); *error = 0; return; } cmd &= WD_INTERVAL; /* convert from power-of-to-ns to WDT ticks */ if (cmd >= 64) { *error = EINVAL; return; } timeout = ((uint64_t)1 << cmd) / 1800000000; /* reload */ ipmi_set_watchdog(sc->ipmi_dev, timeout); *error = 0; } int ipmi_attach(device_t dev) { struct ipmi_softc *sc = device_get_softc(dev); u_char temp[1024]; int i; int status; int unit; 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) { device_printf(dev, "Clear flags is busy\n"); } if (temp[2] == 0xc1) { device_printf(dev, "Clear flags illegal\n"); } 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]) { break; } } 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) { device_printf(dev, "Attached watchdog\n"); /* register the watchdog event handler */ sc->ipmi_ev_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_attached = 1; return 0; } int ipmi_detach(device_t dev) { struct ipmi_softc *sc; sc = device_get_softc(dev); if (sc->ipmi_requests) untimeout((timeout_t *)ipmi_check_read, dev, sc->ipmi_timeout_handle); destroy_dev(sc->ipmi_dev_t); return 0; } #ifdef IMPI_DEBUG static void dump_buf(u_char *data, int len){ char buf[20]; char line[1024]; char temp[30]; int count = 0; int i=0; printf("Address %p len %d\n", data, len); if (len > 256) len = 256; line[0] = '\000'; for (; len > 0; len--, data++) { sprintf(temp, "%02x ", *data); strcat(line, temp); if (*data >= ' ' && *data <= '~') buf[count] = *data; else if (*data >= 'A' && *data <= 'Z') buf[count] = *data; else buf[count] = '.'; if (++count == 16) { buf[count] = '\000'; count = 0; printf(" %3x %s %s\n", i, line, buf); i+=16; line[0] = '\000'; } } buf[count] = '\000'; for (; count != 16; count++) { strcat(line, " "); } printf(" %3x %s %s\n", i, line, buf); } #endif