diff options
Diffstat (limited to 'sys/dev/ipmi.c')
-rw-r--r-- | sys/dev/ipmi.c | 357 |
1 files changed, 210 insertions, 147 deletions
diff --git a/sys/dev/ipmi.c b/sys/dev/ipmi.c index 5812388ed51..1afc935fffa 100644 --- a/sys/dev/ipmi.c +++ b/sys/dev/ipmi.c @@ -1,6 +1,7 @@ -/* $OpenBSD: ipmi.c,v 1.91 2016/01/25 06:36:47 uebayasi Exp $ */ +/* $OpenBSD: ipmi.c,v 1.92 2016/02/05 06:29:01 uebayasi Exp $ */ /* + * Copyright (c) 2015 Masao Uebayashi * Copyright (c) 2005 Jordan Hargrave * All rights reserved. * @@ -31,21 +32,21 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/device.h> +#include <sys/ioctl.h> #include <sys/extent.h> -#include <sys/timeout.h> #include <sys/sensors.h> #include <sys/malloc.h> #include <sys/kthread.h> #include <sys/task.h> #include <machine/bus.h> -#include <machine/intr.h> #include <machine/smbiosvar.h> #include <dev/isa/isareg.h> #include <dev/isa/isavar.h> #include <dev/ipmivar.h> +#include <dev/ipmi.h> struct ipmi_sensor { u_int8_t *i_sdr; @@ -56,7 +57,6 @@ struct ipmi_sensor { SLIST_ENTRY(ipmi_sensor) list; }; -int ipmi_nintr; int ipmi_enabled = 0; #define SENSOR_REFRESH_RATE (5 * hz) @@ -140,8 +140,6 @@ SLIST_HEAD(ipmi_sensors_head, ipmi_sensor); struct ipmi_sensors_head ipmi_sensor_list = SLIST_HEAD_INITIALIZER(ipmi_sensor_list); -struct timeout ipmi_timeout; - void dumpb(const char *, int, const u_int8_t *); int read_sensor(struct ipmi_softc *, struct ipmi_sensor *); @@ -152,7 +150,6 @@ int get_sdr(struct ipmi_softc *, u_int16_t, u_int16_t *); int ipmi_sendcmd(struct ipmi_cmd *); int ipmi_recvcmd(struct ipmi_cmd *); -void ipmi_delay(struct ipmi_softc *, int); void ipmi_cmd(struct ipmi_cmd *); void ipmi_cmd_poll(struct ipmi_cmd *); void ipmi_cmd_wait(struct ipmi_cmd *); @@ -162,10 +159,14 @@ int ipmi_watchdog(void *, int); void ipmi_watchdog_tickle(void *); void ipmi_watchdog_set(void *); -int ipmi_intr(void *); int ipmi_match(struct device *, void *, void *); void ipmi_attach(struct device *, struct device *, void *); int ipmi_activate(struct device *, int); +struct ipmi_softc *ipmilookup(dev_t dev); + +int ipmiopen(dev_t, int, int, struct proc *); +int ipmiclose(dev_t, int, int, struct proc *); +int ipmiioctl(dev_t, u_long, caddr_t, int, struct proc *); long ipow(long, int); long ipmi_convert(u_int8_t, struct sdrtype1 *, long); @@ -174,10 +175,7 @@ void ipmi_sensor_name(char *, int, u_int8_t, u_int8_t *); /* BMC Helper Functions */ u_int8_t bmc_read(struct ipmi_softc *, int); void bmc_write(struct ipmi_softc *, int, u_int8_t); -int bmc_io_wait(struct ipmi_softc *, int, u_int8_t, u_int8_t, const char *); -int bmc_io_wait_cold(struct ipmi_softc *, int, u_int8_t, u_int8_t, - const char *); -void _bmc_io_wait(void *); +int bmc_io_wait(struct ipmi_softc *, struct ipmi_iowait *); void bt_buildmsg(struct ipmi_cmd *); void cmn_buildmsg(struct ipmi_cmd *); @@ -268,83 +266,29 @@ bmc_write(struct ipmi_softc *sc, int offset, u_int8_t val) offset * sc->sc_if_iospacing, val); } -void -_bmc_io_wait(void *arg) -{ - struct ipmi_softc *sc = arg; - struct ipmi_bmc_args *a = sc->sc_iowait_args; - - *a->v = bmc_read(sc, a->offset); - if ((*a->v & a->mask) == a->value) { - sc->sc_wakeup = 0; - wakeup(sc); - return; - } - - if (++sc->sc_retries > sc->sc_max_retries) { - sc->sc_wakeup = 0; - wakeup(sc); - return; - } - - timeout_add(&sc->sc_timeout, 1); -} - int -bmc_io_wait(struct ipmi_softc *sc, int offset, u_int8_t mask, u_int8_t value, - const char *lbl) -{ - volatile u_int8_t v; - struct ipmi_bmc_args args; - - if (cold) - return (bmc_io_wait_cold(sc, offset, mask, value, lbl)); - - sc->sc_retries = 0; - sc->sc_wakeup = 1; - - args.offset = offset; - args.mask = mask; - args.value = value; - args.v = &v; - sc->sc_iowait_args = &args; - - _bmc_io_wait(sc); - - while (sc->sc_wakeup) - tsleep(sc, PWAIT, lbl, 0); - - if (sc->sc_retries > sc->sc_max_retries) { - dbg_printf(1, "%s: bmc_io_wait fails : v=%.2x m=%.2x " - "b=%.2x %s\n", DEVNAME(sc), v, mask, value, lbl); - return (-1); - } - - return (v); -} - -int -bmc_io_wait_cold(struct ipmi_softc *sc, int offset, u_int8_t mask, - u_int8_t value, const char *lbl) +bmc_io_wait(struct ipmi_softc *sc, struct ipmi_iowait *a) { volatile u_int8_t v; int count = 5000000; /* == 5s XXX can be shorter */ while (count--) { - v = bmc_read(sc, offset); - if ((v & mask) == value) + v = bmc_read(sc, a->offset); + if ((v & a->mask) == a->value) return v; delay(1); } - dbg_printf(1, "%s: bmc_io_wait_cold fails : *v=%.2x m=%.2x b=%.2x %s\n", - DEVNAME(sc), v, mask, value, lbl); + dbg_printf(1, "%s: bmc_io_wait fails : *v=%.2x m=%.2x b=%.2x %s\n", + DEVNAME(sc), v, a->mask, a->value, a->lbl); return (-1); } -#define NETFN_LUN(nf,ln) (((nf) << 2) | ((ln) & 0x3)) +#define RSSA_MASK 0xff +#define LUN_MASK 0x3 +#define NETFN_LUN(nf,ln) (((nf) << 2) | ((ln) & LUN_MASK)) /* * BT interface @@ -381,7 +325,13 @@ bt_read(struct ipmi_softc *sc, int reg) int bt_write(struct ipmi_softc *sc, int reg, uint8_t data) { - if (bmc_io_wait(sc, _BT_CTRL_REG, BT_BMC_BUSY, 0, "bt_write") < 0) + struct ipmi_iowait a; + + a.offset = _BT_CTRL_REG; + a.mask = BT_BMC_BUSY; + a.value = 0; + a.lbl = "bt_write"; + if (bmc_io_wait(sc, &a) < 0) return (-1); bmc_write(sc, reg, data); @@ -392,6 +342,7 @@ int bt_sendmsg(struct ipmi_cmd *c) { struct ipmi_softc *sc = c->c_sc; + struct ipmi_iowait a; int i; bt_write(sc, _BT_CTRL_REG, BT_CLR_WR_PTR); @@ -399,8 +350,11 @@ bt_sendmsg(struct ipmi_cmd *c) bt_write(sc, _BT_DATAOUT_REG, sc->sc_buf[i]); bt_write(sc, _BT_CTRL_REG, BT_HOST2BMC_ATN); - if (bmc_io_wait(sc, _BT_CTRL_REG, BT_HOST2BMC_ATN | BT_BMC_BUSY, 0, - "bt_sendwait") < 0) + a.offset = _BT_CTRL_REG; + a.mask = BT_HOST2BMC_ATN | BT_BMC_BUSY; + a.value = 0; + a.lbl = "bt_sendwait"; + if (bmc_io_wait(sc, &a) < 0) return (-1); return (0); @@ -410,10 +364,14 @@ int bt_recvmsg(struct ipmi_cmd *c) { struct ipmi_softc *sc = c->c_sc; + struct ipmi_iowait a; u_int8_t len, v, i, j; - if (bmc_io_wait(sc, _BT_CTRL_REG, BT_BMC2HOST_ATN, BT_BMC2HOST_ATN, - "bt_recvwait") < 0) + a.offset = _BT_CTRL_REG; + a.mask = BT_BMC2HOST_ATN; + a.value = BT_BMC2HOST_ATN; + a.lbl = "bt_recvwait"; + if (bmc_io_wait(sc, &a) < 0) return (-1); bt_write(sc, _BT_CTRL_REG, BT_HOST_BUSY); @@ -504,10 +462,15 @@ int smic_read_data(struct ipmi_softc *, u_int8_t *); int smic_wait(struct ipmi_softc *sc, u_int8_t mask, u_int8_t val, const char *lbl) { + struct ipmi_iowait a; int v; /* Wait for expected flag bits */ - v = bmc_io_wait(sc, _SMIC_FLAG_REG, mask, val, "smicwait"); + a.offset = _SMIC_FLAG_REG; + a.mask = mask; + a.value = val; + a.lbl = "smicwait"; + v = bmc_io_wait(sc, &a); if (v < 0) return (-1); @@ -658,9 +621,14 @@ int kcs_read_data(struct ipmi_softc *, u_int8_t *); int kcs_wait(struct ipmi_softc *sc, u_int8_t mask, u_int8_t value, const char *lbl) { + struct ipmi_iowait a; int v; - v = bmc_io_wait(sc, _KCS_STATUS_REGISTER, mask, value, lbl); + a.offset = _KCS_STATUS_REGISTER; + a.mask = mask; + a.value = value; + a.lbl = lbl; + v = bmc_io_wait(sc, &a); if (v < 0) return (v); @@ -781,6 +749,8 @@ kcs_probe(struct ipmi_softc *sc) u_int8_t v; v = bmc_read(sc, _KCS_STATUS_REGISTER); + if ((v & KCS_STATE_MASK) == KCS_ERROR_STATE) + return (1); #if 0 printf("kcs_probe: %2x\n", v); printf(" STS: %2x\n", v & KCS_STATE_MASK); @@ -1019,8 +989,6 @@ ipmi_sendcmd(struct ipmi_cmd *c) c->c_txlen += sc->sc_if->datasnd; rc = sc->sc_if->sendmsg(c); - ipmi_delay(sc, 5); /* give bmc chance to digest command */ - done: return (rc); } @@ -1055,23 +1023,10 @@ ipmi_recvcmd(struct ipmi_cmd *c) c->c_rxlen); dbg_dump(10, " recv", c->c_rxlen, c->c_data); - ipmi_delay(sc, 5); /* give bmc chance to digest command */ - return (rc); } void -ipmi_delay(struct ipmi_softc *sc, int period) -{ - /* period is in 10 ms increments */ - if (cold) - delay(period * 10000); - else - while (tsleep(sc, PWAIT, "ipmicmd", period) != EWOULDBLOCK) - continue; -} - -void ipmi_cmd(struct ipmi_cmd *c) { if (cold || panicstr != NULL) @@ -1085,12 +1040,10 @@ ipmi_cmd_poll(struct ipmi_cmd *c) { mtx_enter(&c->c_sc->sc_cmd_mtx); - c->c_sc->sc_cmd = c; if (ipmi_sendcmd(c)) { panic("%s: sendcmd fails", DEVNAME(c->c_sc)); } c->c_ccode = ipmi_recvcmd(c); - c->c_sc->sc_cmd = NULL; mtx_leave(&c->c_sc->sc_cmd_mtx); } @@ -1141,6 +1094,7 @@ get_sdr_partial(struct ipmi_softc *sc, u_int16_t recordId, u_int16_t reserveId, c.c_cmd = STORAGE_GET_SDR; c.c_txlen = IPMI_SET_WDOG_MAX; c.c_rxlen = 0; + c.c_data = cmd; ipmi_cmd(&c); len = c.c_rxlen; @@ -1406,9 +1360,6 @@ read_sensor(struct ipmi_softc *sc, struct ipmi_sensor *psensor) u_int8_t data[8]; int rv = -1; - if (!cold) - rw_enter_write(&sc->sc_lock); - memset(data, 0, sizeof(data)); data[0] = psensor->i_num; @@ -1432,8 +1383,6 @@ read_sensor(struct ipmi_softc *sc, struct ipmi_sensor *psensor) psensor->i_sensor.flags |= SENSOR_FINVALID; psensor->i_sensor.status = ipmi_sensor_status(sc, psensor, data); rv = 0; - if (!cold) - rw_exit_write(&sc->sc_lock); return (rv); } @@ -1544,20 +1493,6 @@ add_child_sensors(struct ipmi_softc *sc, u_int8_t *psdr, int count, return (1); } -/* Interrupt handler */ -int -ipmi_intr(void *arg) -{ - struct ipmi_softc *sc = (struct ipmi_softc *)arg; - int v; - - v = bmc_read(sc, _KCS_STATUS_REGISTER); - if (v & KCS_OBF) - ++ipmi_nintr; - - return (0); -} - /* Handle IPMI Timer - reread sensor values */ void ipmi_refresh_sensors(struct ipmi_softc *sc) @@ -1599,11 +1534,6 @@ ipmi_map_regs(struct ipmi_softc *sc, struct ipmi_attach_args *ia) sc->sc_if->nregs * sc->sc_if_iospacing, &sc->sc_ioh); return (-1); } -#if 0 - if (iaa->if_if_irq != -1) - sc->ih = isa_intr_establish(-1, iaa->if_if_irq, - iaa->if_irqlvl, IPL_BIO, ipmi_intr, sc, DEVNAME(sc)); -#endif return (0); } @@ -1694,7 +1624,7 @@ ipmi_probe(void *aux) int ipmi_match(struct device *parent, void *match, void *aux) { - struct ipmi_softc sc; + struct ipmi_softc *sc; struct ipmi_attach_args *ia = aux; struct cfdata *cf = match; u_int8_t cmd[32]; @@ -1704,16 +1634,17 @@ ipmi_match(struct device *parent, void *match, void *aux) return (0); /* XXX local softc is wrong wrong wrong */ - mtx_init(&sc.sc_cmd_mtx, IPL_NONE); - strlcpy(sc.sc_dev.dv_xname, "ipmi0", sizeof(sc.sc_dev.dv_xname)); + sc = malloc(sizeof(*sc), M_TEMP, M_NOWAIT | M_ZERO); + mtx_init(&sc->sc_cmd_mtx, IPL_NONE); + strlcpy(sc->sc_dev.dv_xname, "ipmi0", sizeof(sc->sc_dev.dv_xname)); /* Map registers */ - if (ipmi_map_regs(&sc, ia) == 0) { - sc.sc_if->probe(&sc); + if (ipmi_map_regs(sc, ia) == 0) { + sc->sc_if->probe(sc); /* Identify BMC device early to detect lying bios */ struct ipmi_cmd c; - c.c_sc = ≻ + c.c_sc = sc; c.c_rssa = BMC_SA; c.c_rslun = BMC_LUN; c.c_netfn = APP_NETFN; @@ -1726,9 +1657,11 @@ ipmi_match(struct device *parent, void *match, void *aux) dbg_dump(1, "bmc data", c.c_rxlen, cmd); rv = 1; /* GETID worked, we got IPMI */ - ipmi_unmap_regs(&sc); + ipmi_unmap_regs(sc); } + free(sc, M_TEMP, sizeof(*sc)); + return (rv); } @@ -1737,6 +1670,7 @@ ipmi_attach(struct device *parent, struct device *self, void *aux) { struct ipmi_softc *sc = (void *) self; struct ipmi_attach_args *ia = aux; + struct ipmi_cmd *c = &sc->sc_ioctl.cmd; /* Map registers */ ipmi_map_regs(sc, ia); @@ -1768,14 +1702,10 @@ ipmi_attach(struct device *parent, struct device *self, void *aux) task_set(&sc->sc_wdog_tickle_task, ipmi_watchdog_tickle, sc); wdog_register(ipmi_watchdog, sc); - /* lock around read_sensor so that no one messes with the bmc regs */ - rw_init(&sc->sc_lock, DEVNAME(sc)); - - /* setup ticker */ - sc->sc_retries = 0; - sc->sc_wakeup = 0; - sc->sc_max_retries = 50; /* 50 * 1/100 = 0.5 seconds max */ - timeout_set(&sc->sc_timeout, _bmc_io_wait, sc); + rw_init(&sc->sc_ioctl.lock, DEVNAME(sc)); + sc->sc_ioctl.req.msgid = -1; + c->c_sc = sc; + c->c_ccode = -1; sc->sc_cmd_taskq = taskq_create("ipmicmd", 1, IPL_NONE, TASKQ_MPSAFE); mtx_init(&sc->sc_cmd_mtx, IPL_NONE); @@ -1793,6 +1723,149 @@ ipmi_activate(struct device *self, int act) return (0); } +struct ipmi_softc * +ipmilookup(dev_t dev) +{ + return (struct ipmi_softc *)device_lookup(&ipmi_cd, minor(dev)); +} + +int +ipmiopen(dev_t dev, int flags, int mode, struct proc *p) +{ + struct ipmi_softc *sc = ipmilookup(dev); + + if (sc == NULL) + return (ENXIO); + return (0); +} + +int +ipmiclose(dev_t dev, int flags, int mode, struct proc *p) +{ + struct ipmi_softc *sc = ipmilookup(dev); + + if (sc == NULL) + return (ENXIO); + return (0); +} + +int +ipmiioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *proc) +{ + struct ipmi_softc *sc = ipmilookup(dev); + struct ipmi_req *req = (struct ipmi_req *)data; + struct ipmi_recv *recv = (struct ipmi_recv *)data; + struct ipmi_cmd *c = &sc->sc_ioctl.cmd; + int iv; + int len; + u_char ccode; + int rc = 0; + + if (sc == NULL) + return (ENXIO); + + rw_enter_write(&sc->sc_ioctl.lock); + + c->c_maxrxlen = sizeof(sc->sc_ioctl.buf); + c->c_data = sc->sc_ioctl.buf; + + switch (cmd) { + case IPMICTL_SEND_COMMAND: + if (req->msgid == -1) { + rc = EINVAL; + goto reset; + } + if (sc->sc_ioctl.req.msgid != -1) { + rc = EBUSY; + goto reset; + } + len = req->msg.data_len; + if (len < 0) { + rc = EINVAL; + goto reset; + } + if (len > c->c_maxrxlen) { + rc = E2BIG; + goto reset; + } + sc->sc_ioctl.req = *req; + c->c_ccode = -1; + rc = copyin(req->msg.data, c->c_data, len); + if (rc != 0) + goto reset; + KASSERT(c->c_ccode == -1); + + /* Execute a command synchronously. */ + c->c_netfn = req->msg.netfn; + c->c_cmd = req->msg.cmd; + c->c_txlen = req->msg.data_len; + c->c_rxlen = 0; + ipmi_cmd(c); + + KASSERT(c->c_ccode != -1); + break; + case IPMICTL_RECEIVE_MSG_TRUNC: + case IPMICTL_RECEIVE_MSG: + if (sc->sc_ioctl.req.msgid == -1) { + rc = EINVAL; + goto reset; + } + if (c->c_ccode == -1) { + rc = EAGAIN; + goto reset; + } + ccode = c->c_ccode & 0xff; + rc = copyout(&ccode, recv->msg.data, 1); + if (rc != 0) + goto reset; + + /* Return a command result. */ + recv->recv_type = IPMI_RESPONSE_RECV_TYPE; + recv->msgid = sc->sc_ioctl.req.msgid; + recv->msg.netfn = sc->sc_ioctl.req.msg.netfn; + recv->msg.cmd = sc->sc_ioctl.req.msg.cmd; + recv->msg.data_len = c->c_rxlen + 1; + + rc = copyout(c->c_data, recv->msg.data + 1, c->c_rxlen); + goto reset; + case IPMICTL_SET_MY_ADDRESS_CMD: + iv = *(int *)data; + if (iv < 0 || iv > RSSA_MASK) { + rc = EINVAL; + goto reset; + } + c->c_rssa = iv; + break; + case IPMICTL_GET_MY_ADDRESS_CMD: + *(int *)data = c->c_rssa; + break; + case IPMICTL_SET_MY_LUN_CMD: + iv = *(int *)data; + if (iv < 0 || iv > LUN_MASK) { + rc = EINVAL; + goto reset; + } + c->c_rslun = iv; + break; + case IPMICTL_GET_MY_LUN_CMD: + *(int *)data = c->c_rslun; + break; + case IPMICTL_SET_GETS_EVENTS_CMD: + break; + case IPMICTL_REGISTER_FOR_CMD: + case IPMICTL_UNREGISTER_FOR_CMD: + default: + break; + } +done: + rw_exit_write(&sc->sc_ioctl.lock); + return (rc); +reset: + sc->sc_ioctl.req.msgid = -1; + c->c_ccode = -1; + goto done; +} + #define MIN_PERIOD 10 int @@ -1827,11 +1900,8 @@ void ipmi_watchdog_tickle(void *arg) { struct ipmi_softc *sc = arg; - int s; struct ipmi_cmd c; - s = splsoftclock(); - c.c_sc = sc; c.c_rssa = BMC_SA; c.c_rslun = BMC_LUN; @@ -1842,8 +1912,6 @@ ipmi_watchdog_tickle(void *arg) c.c_rxlen = 0; c.c_data = NULL; ipmi_cmd(&c); - - splx(s); } void @@ -1851,11 +1919,8 @@ ipmi_watchdog_set(void *arg) { struct ipmi_softc *sc = arg; uint8_t wdog[IPMI_GET_WDOG_MAX]; - int s; struct ipmi_cmd c; - s = splsoftclock(); - c.c_sc = sc; c.c_rssa = BMC_SA; c.c_rslun = BMC_LUN; @@ -1884,6 +1949,4 @@ ipmi_watchdog_set(void *arg) c.c_rxlen = 0; c.c_data = wdog; ipmi_cmd(&c); - - splx(s); } |