These two patches implement ipmitool-compatible IOCTL in ipmi(4).
IPMI BMCs are usually accessed via ethernet ports. This provides BMC
access from within host for some situations where you can't use ethernet.
The idea is to provide generic command interface for userland via IOCTL,
while keeping minimal, critical parts (sensor reading and watchdog)
being implemented in kernel. For example, you have to use ipmitool to
configure sensor threashold values set in BMC.
I'm checking in this in a few days, if no one objects.
(Jonathan Gray kindly did thorough review and helped to polish this patch.)
diff --git a/sys/arch/amd64/amd64/conf.c b/sys/arch/amd64/amd64/conf.c
index d69887b..e6a0913 100644
--- a/sys/arch/amd64/amd64/conf.c
+++ b/sys/arch/amd64/amd64/conf.c
@@ -183,6 +183,7 @@ cdev_decl(pci);
#include "pppx.h"
#include "fuse.h"
#include "pvbus.h"
+#include "ipmi.h"
struct cdevsw cdevsw[] =
{
@@ -294,6 +295,7 @@ struct cdevsw cdevsw[] =
cdev_tun_init(NTUN,tap), /* 93: Ethernet network tunnel */
cdev_tty_init(NVIOCON,viocon), /* 94: virtio console */
cdev_pvbus_init(NPVBUS,pvbus), /* 95: pvbus(4) control interface */
+ cdev_ipmi_init(NIPMI,ipmi), /* 96: ipmi */
};
int nchrdev = nitems(cdevsw);
diff --git a/sys/arch/i386/i386/conf.c b/sys/arch/i386/i386/conf.c
index 668e137..05e82bf 100644
--- a/sys/arch/i386/i386/conf.c
+++ b/sys/arch/i386/i386/conf.c
@@ -177,6 +177,7 @@ cdev_decl(pci);
#include "pppx.h"
#include "fuse.h"
#include "pvbus.h"
+#include "ipmi.h"
struct cdevsw cdevsw[] =
{
@@ -288,6 +289,7 @@ struct cdevsw cdevsw[] =
cdev_fuse_init(NFUSE,fuse), /* 93: fuse */
cdev_tun_init(NTUN,tap), /* 94: Ethernet network tunnel */
cdev_pvbus_init(NPVBUS,pvbus), /* 95: pvbus(4) control interface */
+ cdev_ipmi_init(NIPMI,ipmi), /* 96: ipmi */
};
int nchrdev = nitems(cdevsw);
diff --git a/sys/dev/ipmi.c b/sys/dev/ipmi.c
index 5812388..a1fc350 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 $ */
/*
+ * 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,151 @@ 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);
+ if (rc != 0)
+ goto reset;
+ break;
+ 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 +1902,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 +1914,6 @@ ipmi_watchdog_tickle(void *arg)
c.c_rxlen = 0;
c.c_data = NULL;
ipmi_cmd(&c);
-
- splx(s);
}
void
@@ -1851,11 +1921,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 +1951,4 @@ ipmi_watchdog_set(void *arg)
c.c_rxlen = 0;
c.c_data = wdog;
ipmi_cmd(&c);
-
- splx(s);
}
diff --git a/sys/dev/ipmi.h b/sys/dev/ipmi.h
new file mode 100644
index 0000000..81d6aa3
--- /dev/null
+++ b/sys/dev/ipmi.h
@@ -0,0 +1,123 @@
+/*-
+ * Copyright (c) 2006 IronPort Systems Inc. <[email protected]>
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _IPMI_H_
+#define _IPMI_H_
+
+#define IPMI_MAX_ADDR_SIZE 0x20
+#define IPMI_MAX_RX 1024
+#define IPMI_BMC_SLAVE_ADDR 0x20 /* Linux Default slave address */
+#define IPMI_BMC_CHANNEL 0x0f /* Linux BMC channel */
+
+#define IPMI_BMC_SMS_LUN 0x02
+
+#define IPMI_SYSTEM_INTERFACE_ADDR_TYPE 0x0c
+#define IPMI_IPMB_ADDR_TYPE 0x01
+#define IPMI_IPMB_BROADCAST_ADDR_TYPE 0x41
+
+#define IPMI_IOC_MAGIC 'i'
+#define IPMICTL_RECEIVE_MSG_TRUNC _IOWR(IPMI_IOC_MAGIC, 11, struct
ipmi_recv)
+#define IPMICTL_RECEIVE_MSG _IOWR(IPMI_IOC_MAGIC, 12, struct
ipmi_recv)
+#define IPMICTL_SEND_COMMAND _IOW(IPMI_IOC_MAGIC, 13, struct
ipmi_req)
+#define IPMICTL_REGISTER_FOR_CMD _IOW(IPMI_IOC_MAGIC, 14, struct
ipmi_cmdspec)
+#define IPMICTL_UNREGISTER_FOR_CMD _IOW(IPMI_IOC_MAGIC, 15, struct
ipmi_cmdspec)
+#define IPMICTL_SET_GETS_EVENTS_CMD _IOW(IPMI_IOC_MAGIC, 16, int)
+#define IPMICTL_SET_MY_ADDRESS_CMD _IOW(IPMI_IOC_MAGIC, 17, unsigned int)
+#define IPMICTL_GET_MY_ADDRESS_CMD _IOR(IPMI_IOC_MAGIC, 18, unsigned int)
+#define IPMICTL_SET_MY_LUN_CMD _IOW(IPMI_IOC_MAGIC, 19, unsigned int)
+#define IPMICTL_GET_MY_LUN_CMD _IOR(IPMI_IOC_MAGIC, 20, unsigned int)
+
+#define IPMI_RESPONSE_RECV_TYPE 1
+#define IPMI_ASYNC_EVENT_RECV_TYPE 2
+#define IPMI_CMD_RECV_TYPE 3
+
+#define IPMI_APP_REQUEST 0x06
+#define IPMI_GET_DEVICE_ID 0x01
+#define IPMI_CLEAR_FLAGS 0x30
+#define IPMI_GET_MSG_FLAGS 0x31
+# define IPMI_MSG_AVAILABLE 0x01
+# define IPMI_MSG_BUFFER_FULL 0x02
+# define IPMI_WDT_PRE_TIMEOUT 0x08
+#define IPMI_GET_MSG 0x33
+#define IPMI_SEND_MSG 0x34
+#define IPMI_GET_CHANNEL_INFO 0x42
+#define IPMI_RESET_WDOG 0x22
+#define IPMI_SET_WDOG 0x24
+#define IPMI_GET_WDOG 0x25
+
+#define IPMI_SET_WD_TIMER_SMS_OS 0x04
+#define IPMI_SET_WD_TIMER_DONT_STOP 0x40
+#define IPMI_SET_WD_ACTION_RESET 0x01
+
+struct ipmi_msg {
+ unsigned char netfn;
+ unsigned char cmd;
+ unsigned short data_len;
+ unsigned char *data;
+};
+
+struct ipmi_req {
+ unsigned char *addr;
+ unsigned int addr_len;
+ long msgid;
+ struct ipmi_msg msg;
+};
+
+struct ipmi_recv {
+ int recv_type;
+ unsigned char *addr;
+ unsigned int addr_len;
+ long msgid;
+ struct ipmi_msg msg;
+};
+
+struct ipmi_cmdspec {
+ unsigned char netfn;
+ unsigned char cmd;
+};
+
+struct ipmi_addr {
+ int addr_type;
+ short channel;
+ unsigned char data[IPMI_MAX_ADDR_SIZE];
+};
+
+struct ipmi_system_interface_addr {
+ int addr_type;
+ short channel;
+ unsigned char lun;
+};
+
+struct ipmi_ipmb_addr {
+ int addr_type;
+ short channel;
+ unsigned char slave_addr;
+ unsigned char lun;
+};
+
+#endif /* !_IPMI_H_ */
diff --git a/sys/dev/ipmivar.h b/sys/dev/ipmivar.h
index 0e2bc93..a707eaa 100644
--- a/sys/dev/ipmivar.h
+++ b/sys/dev/ipmivar.h
@@ -30,11 +30,12 @@
#ifndef _IPMIVAR_H_
#define _IPMIVAR_H_
-#include <sys/timeout.h>
#include <sys/rwlock.h>
#include <sys/sensors.h>
#include <sys/task.h>
+#include <dev/ipmi.h>
+
#define IPMI_IF_KCS 1
#define IPMI_IF_SMIC 2
#define IPMI_IF_BT 3
@@ -43,17 +44,16 @@
#define IPMI_IF_SMIC_NREGS 3
#define IPMI_IF_BT_NREGS 3
-#define IPMI_MAX_RX 1024 /* XXX ipmi_linux.h */
-
struct ipmi_thread;
struct ipmi_softc;
struct ipmi_cmd;
-struct ipmi_bmc_args{
+struct ipmi_iowait {
int offset;
u_int8_t mask;
u_int8_t value;
volatile u_int8_t *v;
+ const char *lbl;
};
struct ipmi_attach_args {
@@ -111,24 +111,21 @@ struct ipmi_softc {
int sc_btseq;
u_int8_t sc_buf[IPMI_MAX_RX + 16];
- struct ipmi_cmd *sc_cmd;
struct taskq *sc_cmd_taskq;
struct mutex sc_cmd_mtx;
+ struct ipmi_ioctl {
+ struct rwlock lock;
+ struct ipmi_req req;
+ struct ipmi_cmd cmd;
+ uint8_t buf[IPMI_MAX_RX];
+ } sc_ioctl;
+
int sc_wdog_period;
struct task sc_wdog_tickle_task;
struct ipmi_thread *sc_thread;
- struct timeout sc_timeout;
- int sc_max_retries;
- int sc_retries;
- int sc_wakeup;
-
- struct rwlock sc_lock;
-
- struct ipmi_bmc_args *sc_iowait_args;
-
struct ipmi_sensor *current_sensor;
struct ksensordev sc_sensordev;
};
diff --git a/sys/sys/conf.h b/sys/sys/conf.h
index 95212cb..a037ad8 100644
--- a/sys/sys/conf.h
+++ b/sys/sys/conf.h
@@ -475,6 +475,13 @@ extern struct cdevsw cdevsw[];
#endif
+/* open, close, read, write, poll, ioctl, nokqfilter */
+#define cdev_ipmi_init(c,n) { \
+ dev_init(c,n,open), dev_init(c,n,close), (dev_type_read((*))) enodev, \
+ (dev_type_write((*))) enodev, dev_init(c,n,ioctl), \
+ (dev_type_stop((*))) enodev, 0, (dev_type_poll((*))) enodev, \
+ (dev_type_mmap((*))) enodev, 0 }
+
/*
* Line discipline switch table
*/
@@ -606,6 +613,7 @@ cdev_decl(gpio);
cdev_decl(amdmsr);
cdev_decl(fuse);
cdev_decl(pvbus);
+cdev_decl(ipmi);
#endif
diff --git a/etc/MAKEDEV.common b/etc/MAKEDEV.common
index 4071eac..ebc13ed 100644
--- a/etc/MAKEDEV.common
+++ b/etc/MAKEDEV.common
@@ -524,6 +524,8 @@ __devitem(gpr, gpr*, GPR400 smartcard reader)dnl
_mcdev(gpr, gpr*, gpr, {-major_gpr_c-})dnl
__devitem(hotplug, hotplug, devices hot plugging)dnl
_mkdev(hotplug, hotplug, {-M hotplug c major_hotplug_c $U 400-})dnl
+__devitem(ipmi, ipmi*, IPMI BMC access)dnl
+_mkdev(ipmi, ipmi*, {-M ipmi$U c major_ipmi_c $U 600-})dnl
__devitem(gpio, gpio*, General Purpose Input/Output)dnl
_mcdev(gpio, gpio*, gpio, {-major_gpio_c-}, 600)dnl
__devitem(vmm, vmm, Virtual Machine Monitor)dnl
diff --git a/etc/etc.amd64/MAKEDEV b/etc/etc.amd64/MAKEDEV
index 0305adf..c389ac6 100644
--- a/etc/etc.amd64/MAKEDEV
+++ b/etc/etc.amd64/MAKEDEV
@@ -77,6 +77,7 @@
# gpio* General Purpose Input/Output
# gpr* GPR400 smartcard reader
# hotplug devices hot plugging
+# ipmi* IPMI BMC access
# nvram NVRAM access
# pci* PCI bus devices
# pctr* PC Performance Tuning Register access device
@@ -325,6 +326,10 @@ nvram)
M nvram c 85 0 440 kmem
;;
+ipmi*)
+ M ipmi$U c 96 $U 600
+ ;;
+
hotplug)
M hotplug c 82 $U 400
;;
@@ -561,19 +566,20 @@ local)
;;
all)
- R ttyVI00 ttyVI10 ttyVI20 ttyVI30 ttyVI40 drm0 drm1 drm2 drm3
- R nvram gpio0 gpio1 gpio2 bktr0 vnd0 vnd1 vnd2 vnd3 sd0 sd1
- R sd2 sd3 sd4 sd5 sd6 sd7 sd8 sd9 cd0 cd1 rd0 tap0 tap1 tap2
- R tap3 tun0 tun1 tun2 tun3 bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6
- R bpf7 bpf8 bpf9 bio pty0 fd1 fd1B fd1C fd1D fd1E fd1F fd1G
- R fd1H fd0 fd0B fd0C fd0D fd0E fd0F fd0G fd0H diskmap vscsi0
- R ch0 audio0 audio1 audio2 pvbus0 vmm fuse pppx hotplug ptm
- R gpr0 local wscons pci0 pci1 pci2 pci3 uall rmidi0 rmidi1
- R rmidi2 rmidi3 rmidi4 rmidi5 rmidi6 rmidi7 tuner0 radio0
- R speaker video0 video1 uk0 random lpa0 lpa1 lpa2 lpt0 lpt1
- R lpt2 tty00 tty01 tty02 tty03 tty04 tty05 tty06 tty07 tty08
- R tty09 tty0a tty0b ttyc0 ttyc1 ttyc2 ttyc3 ttyc4 ttyc5 ttyc6
- R ttyc7 apm pf pctr systrace wd0 wd1 wd2 wd3 std st0 st1 fd
+ R ipmi0 ttyVI00 ttyVI10 ttyVI20 ttyVI30 ttyVI40 drm0 drm1
+ R drm2 drm3 nvram gpio0 gpio1 gpio2 bktr0 vnd0 vnd1 vnd2 vnd3
+ R sd0 sd1 sd2 sd3 sd4 sd5 sd6 sd7 sd8 sd9 cd0 cd1 rd0 tap0
+ R tap1 tap2 tap3 tun0 tun1 tun2 tun3 bpf0 bpf1 bpf2 bpf3 bpf4
+ R bpf5 bpf6 bpf7 bpf8 bpf9 bio pty0 fd1 fd1B fd1C fd1D fd1E
+ R fd1F fd1G fd1H fd0 fd0B fd0C fd0D fd0E fd0F fd0G fd0H
+ R diskmap vscsi0 ch0 audio0 audio1 audio2 pvbus0 vmm fuse pppx
+ R hotplug ptm gpr0 local wscons pci0 pci1 pci2 pci3 uall
+ R rmidi0 rmidi1 rmidi2 rmidi3 rmidi4 rmidi5 rmidi6 rmidi7
+ R tuner0 radio0 speaker video0 video1 uk0 random lpa0 lpa1
+ R lpa2 lpt0 lpt1 lpt2 tty00 tty01 tty02 tty03 tty04 tty05
+ R tty06 tty07 tty08 tty09 tty0a tty0b ttyc0 ttyc1 ttyc2 ttyc3
+ R ttyc4 ttyc5 ttyc6 ttyc7 apm pf pctr systrace wd0 wd1 wd2 wd3
+ R std st0 st1 fd
;;
wd*|sd*)
diff --git a/etc/etc.amd64/MAKEDEV.md b/etc/etc.amd64/MAKEDEV.md
index 9f06e00..9236f2d 100644
--- a/etc/etc.amd64/MAKEDEV.md
+++ b/etc/etc.amd64/MAKEDEV.md
@@ -74,6 +74,7 @@ _DEV(fuse, 92)
_DEV(gpio, 88)
_DEV(gpr, 80)
_DEV(hotplug, 82)
+_DEV(ipmi, 96)
dnl _DEV(joy, 26)
_DEV(nvram, 85)
_DEV(pci, 72)
@@ -133,5 +134,6 @@ target(all, gpio, 0, 1, 2)dnl
target(all, nvram)dnl
target(all, drm, 0, 1, 2, 3)dnl
target(all, ttyVI, 00, 10, 20, 30, 40)dnl
+target(all, ipmi, 0)dnl
twrget(ramd, wsdisp, ttyC, 0)dnl
target(ramd, fd, 0)dnl
diff --git a/etc/etc.i386/MAKEDEV b/etc/etc.i386/MAKEDEV
index f8c641d..c5338f4 100644
--- a/etc/etc.i386/MAKEDEV
+++ b/etc/etc.i386/MAKEDEV
@@ -77,6 +77,7 @@
# gpio* General Purpose Input/Output
# gpr* GPR400 smartcard reader
# hotplug devices hot plugging
+# ipmi* IPMI BMC access
# joy* Joystick driver
# nvram NVRAM access
# pci* PCI bus devices
@@ -330,6 +331,10 @@ joy*)
M joy$U c 26 $U 666
;;
+ipmi*)
+ M ipmi$U c 96 $U 600
+ ;;
+
hotplug)
M hotplug c 82 $U 400
;;
@@ -566,19 +571,19 @@ local)
;;
all)
- R amdmsr drm0 drm1 drm2 drm3 nvram gpio0 gpio1 gpio2 bktr0
- R vnd0 vnd1 vnd2 vnd3 sd0 sd1 sd2 sd3 sd4 sd5 sd6 sd7 sd8 sd9
- R cd0 cd1 rd0 tap0 tap1 tap2 tap3 tun0 tun1 tun2 tun3 bio bpf0
- R bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 bpf8 bpf9 pty0 fd1 fd1B
- R fd1C fd1D fd1E fd1F fd1G fd1H fd0 fd0B fd0C fd0D fd0E fd0F
- R fd0G fd0H diskmap vscsi0 ch0 audio0 audio1 audio2 pvbus0 vmm
- R fuse pppx hotplug ptm gpr0 local wscons pci0 pci1 pci2 pci3
- R uall rmidi0 rmidi1 rmidi2 rmidi3 rmidi4 rmidi5 rmidi6 rmidi7
- R tuner0 radio0 speaker video0 video1 uk0 random joy0 joy1
- R lpa0 lpa1 lpa2 lpt0 lpt1 lpt2 tty00 tty01 tty02 tty03 tty04
- R tty05 tty06 tty07 tty08 tty09 tty0a tty0b ttyc0 ttyc1 ttyc2
- R ttyc3 ttyc4 ttyc5 ttyc6 ttyc7 apm pf pctr systrace wd0 wd1
- R wd2 wd3 std st0 st1 fd
+ R ipmi0 amdmsr drm0 drm1 drm2 drm3 nvram gpio0 gpio1 gpio2
+ R bktr0 vnd0 vnd1 vnd2 vnd3 sd0 sd1 sd2 sd3 sd4 sd5 sd6 sd7
+ R sd8 sd9 cd0 cd1 rd0 tap0 tap1 tap2 tap3 tun0 tun1 tun2 tun3
+ R bio bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 bpf8 bpf9 pty0
+ R fd1 fd1B fd1C fd1D fd1E fd1F fd1G fd1H fd0 fd0B fd0C fd0D
+ R fd0E fd0F fd0G fd0H diskmap vscsi0 ch0 audio0 audio1 audio2
+ R pvbus0 vmm fuse pppx hotplug ptm gpr0 local wscons pci0 pci1
+ R pci2 pci3 uall rmidi0 rmidi1 rmidi2 rmidi3 rmidi4 rmidi5
+ R rmidi6 rmidi7 tuner0 radio0 speaker video0 video1 uk0 random
+ R joy0 joy1 lpa0 lpa1 lpa2 lpt0 lpt1 lpt2 tty00 tty01 tty02
+ R tty03 tty04 tty05 tty06 tty07 tty08 tty09 tty0a tty0b ttyc0
+ R ttyc1 ttyc2 ttyc3 ttyc4 ttyc5 ttyc6 ttyc7 apm pf pctr
+ R systrace wd0 wd1 wd2 wd3 std st0 st1 fd
;;
wd*|sd*)
diff --git a/etc/etc.i386/MAKEDEV.md b/etc/etc.i386/MAKEDEV.md
index d044384..931c1d9 100644
--- a/etc/etc.i386/MAKEDEV.md
+++ b/etc/etc.i386/MAKEDEV.md
@@ -76,6 +76,7 @@ _DEV(fuse, 93)
_DEV(gpio, 83)
_DEV(gpr, 80)
_DEV(hotplug, 82)
+_DEV(ipmi, 96)
_DEV(joy, 26)
_DEV(nvram, 84)
_DEV(pci, 72)
@@ -135,4 +136,5 @@ target(all, gpio, 0, 1, 2)dnl
target(all, nvram)dnl
target(all, drm, 0, 1, 2, 3)dnl
target(all, amdmsr)dnl
+target(all, ipmi, 0)dnl
twrget(ramd, wsdisp, ttyC, 0)dnl
diff --git a/share/man/man8/man8.amd64/MAKEDEV.8
b/share/man/man8/man8.amd64/MAKEDEV.8
index 5cd7d51..76d12ae 100644
--- a/share/man/man8/man8.amd64/MAKEDEV.8
+++ b/share/man/man8/man8.amd64/MAKEDEV.8
@@ -1,10 +1,10 @@
-.\" $OpenBSD: MAKEDEV.8,v 1.76 2016/01/27 09:12:16 reyk Exp $
+.\" $OpenBSD$
.\"
.\" THIS FILE AUTOMATICALLY GENERATED. DO NOT EDIT.
.\" generated from:
.\"
-.\" OpenBSD: etc.amd64/MAKEDEV.md,v 1.62 2015/12/21 22:15:53 sf Exp
-.\" OpenBSD: MAKEDEV.common,v 1.83 2015/12/21 22:15:53 sf Exp
+.\" OpenBSD: etc.amd64/MAKEDEV.md,v 1.63 2016/01/27 09:09:29 reyk Exp
+.\" OpenBSD: MAKEDEV.common,v 1.84 2016/01/27 09:09:28 reyk Exp
.\" OpenBSD: MAKEDEV.man,v 1.7 2009/03/18 17:34:25 sobrado Exp
.\" OpenBSD: MAKEDEV.mansub,v 1.2 2004/02/20 19:13:01 miod Exp
.\"
@@ -23,7 +23,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: January 27 2016 $
+.Dd $Mdocdate: March 18 2009 $
.Dt MAKEDEV 8 amd64
.Os
.Sh NAME
@@ -226,6 +226,9 @@ GPR400 smartcard reader, see
.It Ar hotplug
devices hot plugging, see
.Xr hotplug 4 .
+.It Ar ipmi*
+IPMI BMC access, see
+.Xr ipmi 4 .
.It Ar nvram
NVRAM access, see
.Xr nvram 4 .
diff --git a/share/man/man8/man8.i386/MAKEDEV.8
b/share/man/man8/man8.i386/MAKEDEV.8
index 652c3db..c1d053e 100644
--- a/share/man/man8/man8.i386/MAKEDEV.8
+++ b/share/man/man8/man8.i386/MAKEDEV.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: MAKEDEV.8,v 1.101 2016/01/27 09:12:16 reyk Exp $
+.\" $OpenBSD$
.\"
.\" THIS FILE AUTOMATICALLY GENERATED. DO NOT EDIT.
.\" generated from:
@@ -23,7 +23,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: January 27 2016 $
+.Dd $Mdocdate: March 18 2009 $
.Dt MAKEDEV 8 i386
.Os
.Sh NAME
@@ -226,6 +226,9 @@ GPR400 smartcard reader, see
.It Ar hotplug
devices hot plugging, see
.Xr hotplug 4 .
+.It Ar ipmi*
+IPMI BMC access, see
+.Xr ipmi 4 .
.It Ar joy*
Joystick driver, see
.Xr joy 4 .