summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/ipmi.c1376
-rw-r--r--sys/dev/ipmivar.h317
2 files changed, 1693 insertions, 0 deletions
diff --git a/sys/dev/ipmi.c b/sys/dev/ipmi.c
new file mode 100644
index 00000000000..be825e278f5
--- /dev/null
+++ b/sys/dev/ipmi.c
@@ -0,0 +1,1376 @@
+/* $OpenBSD: ipmi.c,v 1.1 2005/10/04 21:59:41 marco Exp $ */
+
+/*
+ * Copyright (c) 2005 Jordan Hargrave
+ * 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 AUTHORS 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 AUTHORS 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 <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+#include <sys/extent.h>
+#include <sys/timeout.h>
+#include <sys/sensors.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/isa/isareg.h>
+#include <dev/isa/isavar.h>
+
+#include <dev/ipmivar.h>
+
+struct ipmi_sensor {
+ u_int8_t *i_sdr;
+ int i_num;
+ struct sensor i_sensor;
+ SLIST_ENTRY(ipmi_sensor) list;
+};
+
+int ipmi_nintr;
+int ipmi_dbg = 0;
+
+#define SENSOR_REFRESH_RATE (10 * hz)
+
+#define SMBIOS_TYPE_IPMI 0x26
+
+#define BIT(x) (1L << (x))
+
+#define IPMI_BTMSG_LEN 0
+#define IPMI_BTMSG_NFLN 1
+#define IPMI_BTMSG_SEQ 2
+#define IPMI_BTMSG_CMD 3
+#define IPMI_BTMSG_CCODE 4
+#define IPMI_BTMSG_DATAIN 4
+#define IPMI_BTMSG_DATAOUT 5
+
+#define IPMI_MSG_NFLN 0
+#define IPMI_MSG_CMD 1
+#define IPMI_MSG_CCODE 2
+#define IPMI_MSG_DATAIN 2
+#define IPMI_MSG_DATAOUT 3
+
+#define byteof(x) ((x) >> 3)
+#define bitof(x) (1L << ((x) & 0x7))
+#define TB(b,m) (data[2+byteof(b)] & bitof(b))
+
+#define dbg_printf(lvl,fmt...) if (ipmi_dbg >= lvl) { printf(fmt); }
+#define dbg_dump(lvl,msg,len,buf) if (len && ipmi_dbg >= lvl) { dumpb(msg,len,(const u_int8_t *)(buf)); }
+
+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 smbios_ipmi_probe(void *, void *);
+void dumpb(const char *, int, const u_int8_t *);
+
+int read_sensor(struct ipmi_softc *, struct ipmi_sensor *);
+int add_sdr_sensor(struct ipmi_softc *, u_int8_t *);
+int get_sdr_partial(struct ipmi_softc *, u_int16_t, u_int16_t,
+ u_int8_t, u_int8_t, void *, u_int16_t *);
+int get_sdr(struct ipmi_softc *, u_int16_t, u_int16_t *);
+
+int ipmi_sendcmd(struct ipmi_softc *, int, int, int, int, int, const void*);
+int ipmi_recvcmd(struct ipmi_softc *, int, int *, void *);
+
+int ipmi_intr(void *);
+int ipmi_probe(struct device *, void *, void *);
+void ipmi_attach(struct device *, struct device *, void *);
+
+long ipow(long, int);
+long ipmi_convert(u_int8_t, sdrtype1 *, long);
+void ipmi_sensor_name(char *, int, u_int8_t, u_int8_t *);
+
+/* BMC Helper Functions */
+uint8_t bmc_read(struct ipmi_softc *, int);
+void bmc_write(struct ipmi_softc *, int, uint8_t);
+int bmc_io_wait(struct ipmi_softc *, int, u_int8_t, u_int8_t, long,
+ const char *);
+
+void *bt_buildmsg(struct ipmi_softc *, int, int, int, const void *, int *);
+void *cmn_buildmsg(struct ipmi_softc *, int, int, int, const void *, int *);
+
+int getbits(u_int8_t *, int, int);
+int valid_sensor(int, int);
+
+void ipmi_refresh(void *arg);
+void ipmi_refresh_sensors(struct ipmi_softc * sc);
+int ipmi_map_regs(struct ipmi_softc * sc, struct ipmi_attach_args * ia);
+void ipmi_unmap_regs(struct ipmi_softc * sc, struct ipmi_attach_args * ia);
+
+struct ipmi_if kcs_if = {
+ "kcs",
+ IPMI_IF_KCS_NREGS,
+ cmn_buildmsg,
+ kcs_sendmsg,
+ kcs_recvmsg,
+ kcs_reset,
+ kcs_probe,
+};
+
+struct ipmi_if smic_if = {
+ "smic",
+ IPMI_IF_SMIC_NREGS,
+ cmn_buildmsg,
+ smic_sendmsg,
+ smic_recvmsg,
+ smic_reset,
+ smic_probe,
+};
+
+struct ipmi_if bt_if = {
+ "bt",
+ IPMI_IF_BT_NREGS,
+ bt_buildmsg,
+ bt_sendmsg,
+ bt_recvmsg,
+ bt_reset,
+ bt_probe,
+};
+
+struct ipmi_if *ipmi_get_if(int);
+struct ipmi_if *
+ipmi_get_if(int iftype)
+{
+ switch (iftype) {
+ case IPMI_IF_KCS:
+ return (&kcs_if);
+
+ case IPMI_IF_SMIC:
+ return (&smic_if);
+
+ case IPMI_IF_BT:
+ return (&bt_if);
+ }
+
+ return (NULL);
+}
+
+/*
+ * BMC Helper Functions
+ */
+uint8_t
+bmc_read(struct ipmi_softc * sc, int offset)
+{
+ return (bus_space_read_1(sc->sc_iot, sc->sc_ioh,
+ offset * sc->sc_if_iospacing));
+}
+
+void
+bmc_write(struct ipmi_softc * sc, int offset, uint8_t val)
+{
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh,
+ offset * sc->sc_if_iospacing, val);
+}
+
+int
+bmc_io_wait(struct ipmi_softc * sc, int offset, u_int8_t mask,
+ u_int8_t value, long count, const char *lbl)
+{
+ volatile u_int8_t v;
+
+ /* Spin loop (ugly) */
+ while (count--) {
+ v = bmc_read(sc, offset);
+ if ((v & mask) == value) {
+ return v;
+ }
+ }
+ printf("bmc_io_wait fails : v=%.2x m=%.2x b=%.2x %s\n",
+ v, mask, value, lbl);
+ return (-1);
+}
+
+#define NETFN_LUN(nf,ln) (((nf) << 2) | ((ln) & 0x3))
+
+/*
+ * BT interface
+ */
+#define _BT_CTRL_REG 0
+#define BT_CLR_WR_PTR BIT(0)
+#define BT_CLR_RD_PTR BIT(1)
+#define BT_HOST2BMC_ATN BIT(2)
+#define BT_BMC2HOST_ATN BIT(3)
+#define BT_EVT_ATN BIT(4)
+#define BT_HOST_BUSY BIT(6)
+#define BT_BMC_BUSY BIT(7)
+
+#define _BT_DATAIN_REG 1
+#define _BT_DATAOUT_REG 1
+#define _BT_INTMASK_REG 2
+
+int
+bt_sendmsg(struct ipmi_softc * sc, int len, const u_int8_t * data)
+{
+ int i;
+
+ bmc_write(sc, _BT_CTRL_REG, BT_CLR_WR_PTR);
+ for (i = 0; i < len; i++)
+ bmc_write(sc, _BT_DATAOUT_REG, data[i]);
+
+ bmc_write(sc, _BT_CTRL_REG, BT_HOST2BMC_ATN);
+ return (0);
+}
+
+int
+bt_recvmsg(struct ipmi_softc * sc, int maxlen, int *rxlen, u_int8_t * data)
+{
+ uint8_t len, v, i;
+
+ /* BT Result data: 0: len 1:nfln 2:seq 3:cmd 4:ccode
+ * 5:data... */
+ len = bmc_read(sc, _BT_DATAIN_REG);
+ for (i = IPMI_BTMSG_NFLN; i <= len; i++) {
+ /* Ignore sequence number */
+ v = bmc_read(sc, _BT_DATAIN_REG);
+ if (i != IPMI_BTMSG_SEQ)
+ *(data++) = v;
+ }
+ bmc_write(sc, _BT_CTRL_REG, BT_BMC2HOST_ATN | BT_HOST_BUSY);
+ *rxlen = len - 1;
+
+#if 0
+ data[IPMI_MSG_NFLN] = data[IPMI_BTMSG_
+#endif
+ return (0);
+}
+
+int
+bt_reset(struct ipmi_softc * sc)
+{
+ return (-1);
+}
+
+int
+bt_probe(struct ipmi_softc * sc)
+{
+ uint8_t v;
+
+ v = bmc_read(sc, _BT_CTRL_REG);
+#if 0
+ printf("bt_probe: %2x\n", v);
+ printf(" WR : %2x\n", v & BT_CLR_WR_PTR);
+ printf(" RD : %2x\n", v & BT_CLR_RD_PTR);
+ printf(" H2B : %2x\n", v & BT_HOST2BMC_ATN);
+ printf(" B2H : %2x\n", v & BT_BMC2HOST_ATN);
+ printf(" EVT : %2x\n", v & BT_EVT_ATN);
+ printf(" HBSY : %2x\n", v & BT_HOST_BUSY);
+ printf(" BBSY : %2x\n", v & BT_BMC_BUSY);
+#endif
+ return (-1);
+}
+
+/*
+ * SMIC interface
+ */
+#define _SMIC_DATAIN_REG 0
+#define _SMIC_DATAOUT_REG 0
+
+#define _SMIC_CTRL_REG 1
+
+#define _SMIC_FLAG_REG 2
+#define SMIC_BUSY BIT(0)
+#define SMIC_SMS_ATN BIT(2)
+#define SMIC_EVT_ATN BIT(3)
+#define SMIC_SMI BIT(4)
+#define SMIC_TX_DATA_RDY BIT(6)
+#define SMIC_RX_DATA_RDY BIT(7)
+
+#if 0
+int
+smic_wait(struct ipmi_softc * sc, u_int8_t mask, u_int8_t val, const char *lbl)
+{
+ return (inb(SMIC_CNTL_REGISTER(sc)));
+}
+
+int
+smic_write_cmd_data(struct ipmi_softc * sc, u_int8_t cmd, const u_int8_t * data)
+{
+ int sts;
+
+ sts = smic_wait(sc, SMIC_TX_DATA_RDY | SMIC_BUSY, SMIC_TX_DATA_RDY,
+ "smic_write_cmd_data ready");
+ if (sts != 0)
+ return (sts);
+
+ bmc_write(sc, _SMIC_CTRL_REG, cmd);
+ if (data)
+ bmc_write(sc, _SMIC_DATAOUT_REG, *data);
+
+ v = bmc_read(sc, _SMIC_FLAG_REG);
+ bmc_write(sc, _SMIC_FLAG_REG, v | SMIC_BUSY);
+
+ return (smic_wait(sc, SMIC_BUSY, 0, "smic_write_cmd_data busy"));
+}
+
+int
+smic_read_data(struct ipmi_softc * sc, u_int8_t * data)
+{
+ sts = smic_wait(sc, SMIC_RX_DATA_RDY | SMIC_BUSY, SMIC_RX_DATA_RDY,
+ "smic_read_data");
+ if (sts != SMIC_STATE_READ)
+ return (sts);
+
+ sts = bmc_read(sc, _SMIC_CNTL_REG);
+ *data = bmc_read(sc, _SMIC_DATAIN_REG);
+ return (sts);
+}
+#endif
+
+int
+smic_sendmsg(struct ipmi_softc * sc, int len, const u_int8_t * data)
+{
+#if 0
+ int sts, idx;
+
+ sts = smic_write_cmd_data(sc, SMS_CC_START_TRANSFER, &data[0]);
+ for (idx = 0; idx < len - 1; idx++)
+ sts = smic_write_cmd_data(sc, SMS_CC_NEXT_TRANSFER, &data[idx]);
+
+ sts = smic_write_cmd(sc, SMS_CC_END_TRANSFER, &data[idx]);
+#endif
+ return (-1);
+}
+
+int
+smic_recvmsg(struct ipmi_softc * sc, int maxlen, int *len, u_int8_t * data)
+{
+#if 0
+ int sts, idx;
+
+ sts = smic_write_cmd_data(sc, SMS_CC_START_RECEIVE, NULL);
+ for (idx = 0;; idx++) {
+ smic_read_data(sc, &data[idx]);
+ smic_write_cmd_data(sc, SMS_CC_NEXT_RECEIVE, NULL);
+ }
+ smic_write_cmd_data(sc, SMS_CC_END_RECEIVE, NULL);
+#endif
+ return (-1);
+}
+
+int
+smic_reset(struct ipmi_softc * sc)
+{
+ return (-1);
+}
+
+int
+smic_probe(struct ipmi_softc * sc)
+{
+ return (-1);
+}
+
+/*
+ * KCS interface
+ */
+#define _KCS_DATAIN_REGISTER 0
+#define _KCS_DATAOUT_REGISTER 0
+#define KCS_READ_NEXT 0x68
+
+#define _KCS_COMMAND_REGISTER 1
+#define KCS_GET_STATUS 0x60
+#define KCS_WRITE_START 0x61
+#define KCS_WRITE_END 0x62
+
+#define _KCS_STATUS_REGISTER 1
+#define KCS_OBF BIT(0)
+#define KCS_IBF BIT(1)
+#define KCS_SMS_ATN BIT(2)
+#define KCS_CD BIT(3)
+#define KCS_OEM1 BIT(4)
+#define KCS_OEM2 BIT(5)
+#define KCS_STATE_MASK 0xc0
+
+#define KCS_IDLE_STATE 0x00
+#define KCS_READ_STATE 0x40
+#define KCS_WRITE_STATE 0x80
+#define KCS_ERROR_STATE 0xC0
+
+int kcs_wait(struct ipmi_softc *, u_int8_t, u_int8_t, const char *);
+int kcs_write_cmd(struct ipmi_softc *, u_int8_t);
+int kcs_write_data(struct ipmi_softc *, u_int8_t);
+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)
+{
+ int v;
+
+ v = bmc_io_wait(sc, _KCS_STATUS_REGISTER, mask, value, 0xFFFFF, lbl);
+ if (v < 0)
+ return (v);
+
+ /* Check if output buffer full, read dummy byte */
+ if (value == 0 && (v & KCS_OBF))
+ bmc_read(sc, _KCS_DATAIN_REGISTER);
+
+ /* Check for error state */
+ if ((v & KCS_STATE_MASK) == KCS_ERROR_STATE) {
+ bmc_write(sc, _KCS_COMMAND_REGISTER, KCS_GET_STATUS);
+ while (bmc_read(sc, _KCS_STATUS_REGISTER) & KCS_IBF);
+ printf(" error code: %x\n", bmc_read(sc, _KCS_DATAIN_REGISTER));
+ }
+
+ return (v & KCS_STATE_MASK);
+}
+
+int
+kcs_write_cmd(struct ipmi_softc * sc, u_int8_t cmd)
+{
+ /* ASSERT: IBF and OBF are clear */
+ dbg_printf(99, "kcswritecmd: %.2x\n", cmd);
+ bmc_write(sc, _KCS_COMMAND_REGISTER, cmd);
+
+ return (kcs_wait(sc, KCS_IBF, 0, "write_cmd"));
+}
+
+int
+kcs_write_data(struct ipmi_softc * sc, u_int8_t data)
+{
+ /* ASSERT: IBF and OBF are clear */
+ dbg_printf(99, "kcswritedata: %.2x\n", data);
+ bmc_write(sc, _KCS_DATAOUT_REGISTER, data);
+
+ return (kcs_wait(sc, KCS_IBF, 0, "write_data"));
+}
+
+int
+kcs_read_data(struct ipmi_softc * sc, u_int8_t * data)
+{
+ int sts;
+
+ sts = kcs_wait(sc, KCS_IBF | KCS_OBF, KCS_OBF, "read_data");
+ if (sts != KCS_READ_STATE)
+ return (sts);
+
+ /* ASSERT: OBF is set read data, request next byte */
+ *data = bmc_read(sc, _KCS_DATAIN_REGISTER);
+ bmc_write(sc, _KCS_DATAOUT_REGISTER, KCS_READ_NEXT);
+
+ dbg_printf(99, "kcsreaddata: %.2x\n", *data);
+
+ return (sts);
+}
+
+/* Exported KCS functions */
+int
+kcs_sendmsg(struct ipmi_softc * sc, int len, const u_int8_t * data)
+{
+ int idx, sts;
+
+ /* ASSERT: IBF is clear */
+ dbg_dump(50, "kcs sendmsg", len, data);
+ sts = kcs_write_cmd(sc, KCS_WRITE_START);
+ for (idx = 0; idx < len; idx++) {
+ if (idx == len - 1)
+ sts = kcs_write_cmd(sc, KCS_WRITE_END);
+
+ if (sts != KCS_WRITE_STATE)
+ break;
+
+ sts = kcs_write_data(sc, data[idx]);
+ }
+ if (sts != KCS_READ_STATE) {
+ printf("kcs sendmsg = %d/%d <%.2x>\n", idx, len, sts);
+ dumpb("kcs_sendmsg", len, data);
+ }
+
+ return (sts != KCS_READ_STATE);
+}
+
+int
+kcs_recvmsg(struct ipmi_softc * sc, int maxlen, int *rxlen, u_int8_t * data)
+{
+ int idx, sts;
+
+ for (idx = 0; idx < maxlen; idx++) {
+ sts = kcs_read_data(sc, &data[idx]);
+ if (sts != KCS_READ_STATE)
+ break;
+ }
+ sts = kcs_wait(sc, KCS_IBF, 0, "recv");
+ *rxlen = idx;
+ if (sts != KCS_IDLE_STATE)
+ printf("kcs read = %d/%d <%.2x>\n", idx, maxlen, sts);
+
+ dbg_dump(50, "kcs recvmsg", idx, data);
+
+ return (sts != KCS_IDLE_STATE);
+}
+
+int
+kcs_reset(struct ipmi_softc * sc)
+{
+ return (-1);
+}
+
+int
+kcs_probe(struct ipmi_softc * sc)
+{
+ u_int8_t v;
+
+ v = bmc_read(sc, _KCS_STATUS_REGISTER);
+#if 0
+ printf("kcs_probe: %2x\n", v);
+ printf(" STS: %2x\n", v & KCS_STATE_MASK);
+ printf(" ATN: %2x\n", v & KCS_SMS_ATN);
+ printf(" C/D: %2x\n", v & KCS_CD);
+ printf(" IBF: %2x\n", v & KCS_IBF);
+ printf(" OBF: %2x\n", v & KCS_OBF);
+#endif
+ return (0);
+}
+
+/*
+ * IPMI code
+ */
+#define READ_SMS_BUFFER 0x37
+#define WRITE_I2C 0x50
+
+#define GET_MESSAGE_CMD 0x33
+#define SEND_MESSAGE_CMD 0x34
+
+#define IPMB_CHANNEL_NUMBER 0
+
+#define PUBLIC_BUS 0
+
+#define MIN_I2C_PACKET_SIZE 3
+#define MIN_IMB_PACKET_SIZE 7 /* one byte for cksum */
+
+#define MIN_BTBMC_REQ_SIZE 4
+#define MIN_BTBMC_RSP_SIZE 5
+#define MIN_BMC_REQ_SIZE 2
+#define MIN_BMC_RSP_SIZE 3
+
+#define BMC_SA 0x20 /* BMC/ESM3 */
+#define FPC_SA 0x22 /* front panel */
+#define BP_SA 0xC0 /* Primary Backplane */
+#define BP2_SA 0xC2 /* Secondary Backplane */
+#define PBP_SA 0xC4 /* Peripheral Backplane */
+#define DRAC_SA 0x28 /* DRAC-III */
+#define DRAC3_SA 0x30 /* DRAC-III */
+#define BMC_LUN 0
+#define SMS_LUN 2
+
+typedef struct {
+ u_int8_t rsSa;
+ u_int8_t rsLun;
+ u_int8_t netFn;
+ u_int8_t cmd;
+ u_int8_t data_len;
+ u_int8_t *data;
+} ipmi_request_;
+
+typedef struct {
+ u_int8_t cCode;
+ u_int8_t data_len;
+ u_int8_t *data;
+} ipmi_response_t;
+
+typedef struct {
+ u_int8_t bmc_nfLn;
+ u_int8_t bmc_cmd;
+ u_int8_t bmc_data_len;
+ u_int8_t bmc_data[1];
+} ipmi_bmc_request_t;
+
+typedef struct {
+ u_int8_t bmc_nfLn;
+ u_int8_t bmc_cmd;
+ u_int8_t bmc_cCode;
+ u_int8_t bmc_data_len;
+ u_int8_t bmc_data[1];
+} ipmi_bmc_response_t;
+
+struct cfattach ipmi_ca = {
+ sizeof(struct ipmi_softc), ipmi_probe, ipmi_attach
+};
+
+struct cfdriver ipmi_cd = {
+ NULL, "ipmi", DV_DULL
+};
+
+void *scan_sig(long, long, int, int, const void *);
+int scan_smbios(u_int8_t, void (*)(void *, void *), void *);
+
+/* Scan memory for signature */
+void *
+scan_sig(long start, long end, int skip, int len, const void *data)
+{
+ void *va;
+
+ while (start < end) {
+ va = ISA_HOLE_VADDR(start);
+ if (memcmp(va, data, len) == 0)
+ return (va);
+
+ start += skip;
+ }
+
+ return (NULL);
+}
+
+/* Scan SMBIOS for table type */
+int
+scan_smbios(u_int8_t mtype, void (*smcb) (void *base, void *arg), void *arg) {
+ smbiosanchor_t *romhdr;
+ smhdr_t *smhdr;
+ u_int8_t *offset;
+ int nmatch, num;
+
+ /* Scan for SMBIOS Table Signature */
+ romhdr = (smbiosanchor_t *) scan_sig(0xF0000, 0xFFFFF, 16, 4, "_SM_");
+ if (romhdr == NULL)
+ return (-1);
+
+#if 0
+ printf("Found SMBIOS Version %d.%d at 0x%lx, %d entries\n",
+ romhdr->smr_smbios_majver,
+ romhdr->smr_smbios_minver,
+ romhdr->smr_table_address,
+ romhdr->smr_count);
+#endif
+ /* XXX: Need to handle correctly if SMBIOS in high memory Get offset
+ * of SMBIOS Table entries */
+ nmatch = 0;
+ offset = ISA_HOLE_VADDR(romhdr->smr_table_address);
+ for (num = 0; num < romhdr->smr_count; num++) {
+ smhdr = (smhdr_t *) offset;
+ if (smhdr->smh_type == SMBIOS_TYPE_END ||
+ smhdr->smh_length == 0)
+ break;
+
+ /* found a match here */
+ if (smhdr->smh_type == mtype) {
+ smcb(&smhdr[1], arg);
+ nmatch++;
+ }
+ /* Search for end of string table, marked by '\0\0' */
+ offset += smhdr->smh_length;
+ while (offset[0] || offset[1])
+ offset++;
+
+ offset += 2;
+ }
+
+ return (nmatch);
+}
+
+void
+dumpb(const char *lbl, int len, const u_int8_t * data)
+{
+ int idx;
+
+ printf("%s: ", lbl);
+ for (idx = 0; idx < len; idx++)
+ printf("%.2x ", data[idx]);
+
+ printf("\n");
+}
+
+void
+smbios_ipmi_probe(void *ptr, void *arg)
+{
+ struct ipmi_attach_args *ia = arg;
+ smbios_ipmi_t *pipmi = (smbios_ipmi_t *)ptr;
+
+ ia->iaa_if_type = pipmi->smipmi_if_type;
+ ia->iaa_if_rev = pipmi->smipmi_if_rev;
+ ia->iaa_if_irq = (pipmi->smipmi_base_flags & BIT(3)) ?
+ pipmi->smipmi_irq : -1;
+ ia->iaa_if_irqlvl = (pipmi->smipmi_base_flags & BIT(1)) ?
+ IST_LEVEL : IST_EDGE;
+
+ switch (pipmi->smipmi_base_flags >> 6) {
+ case 0:
+ ia->iaa_if_iospacing = 1;
+ break;
+
+ case 1:
+ ia->iaa_if_iospacing = 4;
+ break;
+
+ case 2:
+ ia->iaa_if_iospacing = 2;
+ break;
+
+ default:
+ ia->iaa_if_iospacing = 1;
+ printf("ipmi: unknown register spacing\n");
+ }
+
+ /* Calculate base address (PCI BAR format) */
+ if (pipmi->smipmi_base_address & 0x1) {
+ ia->iaa_if_iotype = 'i';
+ ia->iaa_if_iobase = pipmi->smipmi_base_address & ~0x1;
+ } else {
+ ia->iaa_if_iotype = 'm';
+ ia->iaa_if_iobase = pipmi->smipmi_base_address & ~0xF;
+ }
+ if (pipmi->smipmi_base_flags & BIT(4)) {
+ ia->iaa_if_iobase++;
+ }
+}
+
+/*
+ * bt_buildmsg builds an IPMI message from a nfLun, cmd, and data
+ * This is used by BT protocol
+ *
+ * Returns a buffer to an allocated message, txlen contains length
+ * of allocated message
+ */
+void *
+bt_buildmsg(struct ipmi_softc * sc, int netlun, int cmd, int len,
+ const void *data, int *txlen)
+{
+ u_int8_t *buf;
+
+ /* Block transfer needs 4 extra bytes: length/netfn/seq/cmd + data */
+ *txlen = len + 4;
+ buf = malloc(*txlen, M_DEVBUF, M_WAITOK);
+ if (buf == NULL)
+ return (NULL);
+
+ buf[IPMI_BTMSG_LEN] = len + 3;
+ buf[IPMI_BTMSG_NFLN] = netlun;
+ buf[IPMI_BTMSG_SEQ] = sc->sc_btseq++;
+ buf[IPMI_BTMSG_CMD] = cmd;
+ if (len && data)
+ memcpy(buf + IPMI_BTMSG_DATAIN, data, len);
+
+ return (buf);
+}
+
+/*
+ * cmn_buildmsg builds an IPMI message from a nfLun, cmd, and data
+ * This is used by both SMIC and KCS protocols
+ *
+ * Returns a buffer to an allocated message, txlen contains length
+ * of allocated message
+ */
+void *
+cmn_buildmsg(struct ipmi_softc * sc, int nfLun, int cmd, int len,
+ const void *data, int *txlen)
+{
+ u_int8_t *buf;
+
+ /* Common needs two extra bytes: nfLun/cmd + data */
+ *txlen = len + 2;
+ buf = malloc(*txlen, M_DEVBUF, M_WAITOK);
+ if (buf == NULL)
+ return (NULL);
+
+ buf[IPMI_MSG_NFLN] = nfLun;
+ buf[IPMI_MSG_CMD] = cmd;
+ if (len && data)
+ memcpy(buf + IPMI_MSG_DATAIN, data, len);
+
+ return (buf);
+}
+
+/* Send an IPMI command */
+int
+ipmi_sendcmd(struct ipmi_softc * sc, int rssa, int rslun, int netfn, int cmd,
+ int txlen, const void *data)
+{
+ u_int8_t *buf;
+ int rc;
+
+ dbg_printf(10, "ipmi_sendcmd: rssa=%.2x nfln=%.2x cmd=%.2x len=%.2x\n",
+ rssa, NETFN_LUN(netfn, rslun), cmd, txlen);
+ dbg_dump(10, " send", txlen, data);
+ if (rssa != BMC_SA) {
+#if 0
+ buf = sc->sc_if->buildmsg(sc, NETFN_LUN(APP_NETFN, BMC_LUN),
+ APP_SEND_MESSAGE, 7 + txlen, NULL, &txlen);
+ pI2C->bus = (sc->if_ver == 0x09) ?
+ PUBLIC_BUS :
+ IPMB_CHANNEL_NUMBER;
+
+ imbreq->rsSa = rssa;
+ imbreq->nfLn = NETFN_LUN(netfn, rslun);
+ imbreq->cSum1 = -(imbreq->rsSa + imbreq->nfLn);
+ imbreq->rqSa = BMC_SA;
+ imbreq->seqLn = NETFN_LUN(sc->imb_seq++, SMS_LUN);
+ imbreq->cmd = cmd;
+ if (txlen) {
+ memcpy(imbreq->data, data, txlen);
+ }
+ /* Set message checksum */
+ imbreq->data[txlen] = cksum8(&imbreq->rqSa, txlen + 3);
+#endif
+ return (-1);
+ } else
+ buf = sc->sc_if->buildmsg(sc, NETFN_LUN(netfn, rslun), cmd,
+ txlen, data, &txlen);
+
+ if (buf == NULL) {
+ printf("sendcmd malloc fails\n");
+ return (-1);
+ }
+ rc = sc->sc_if->sendmsg(sc, txlen, buf);
+ free(buf, M_DEVBUF);
+
+ return (rc);
+}
+
+int
+ipmi_recvcmd(struct ipmi_softc * sc, int maxlen, int *rxlen, void *data)
+{
+ u_int8_t *buf, rc = 0;
+ int rawlen;
+
+ /* Need three extra bytes: netfn/cmd/ccode + data */
+ buf = malloc(maxlen + 3, M_DEVBUF, M_WAITOK);
+ if (buf == NULL) {
+ printf("ipmi_recvcmd: malloc fails\n");
+ return -1;
+ }
+ /* Receive message from interface, copy out result data */
+ sc->sc_if->recvmsg(sc, maxlen + 3, &rawlen, buf);
+
+ *rxlen = rawlen - IPMI_MSG_DATAOUT;
+ if (*rxlen > 0 && data)
+ memcpy(data, buf + IPMI_MSG_DATAOUT, *rxlen);
+
+ if ((rc = buf[IPMI_MSG_CCODE]) != 0) {
+ dbg_printf(1, "ipmi_recvmsg: nfln=%.2x cmd=%.2x err=%.2x\n",
+ buf[IPMI_MSG_NFLN], buf[IPMI_MSG_CMD], buf[IPMI_MSG_CCODE]);
+ }
+ dbg_printf(10, "ipmi_recvcmd: nfln=%.2x cmd=%.2x err=%.2x len=%.2x\n",
+ buf[IPMI_MSG_NFLN], buf[IPMI_MSG_CMD], buf[IPMI_MSG_CCODE],
+ *rxlen);
+ dbg_dump(10, " recv", *rxlen, data);
+
+ free(buf, M_DEVBUF);
+
+ return (rc);
+}
+
+/* Read a partial SDR entry */
+int
+get_sdr_partial(struct ipmi_softc * sc, u_int16_t recordId, u_int16_t reserveId,
+ u_int8_t offset, u_int8_t length, void *buffer, u_int16_t * nxtRecordId)
+{
+ u_int8_t cmd[8 + length];
+ int len;
+
+ ((u_int16_t *) cmd)[0] = reserveId;
+ ((u_int16_t *) cmd)[1] = recordId;
+ cmd[4] = offset;
+ cmd[5] = length;
+ if (ipmi_sendcmd(sc, BMC_SA, 0, STORAGE_NETFN, STORAGE_GET_SDR, 6,
+ cmd)) {
+ printf("sendcmd fails\n");
+ return (-1);
+ }
+ if (ipmi_recvcmd(sc, 8 + length, &len, cmd)) {
+ printf("getSdrPartial: recvcmd fails\n");
+ return (-1);
+ }
+ if (nxtRecordId) {
+ *nxtRecordId = *(uint16_t *) cmd;
+ }
+ memcpy(buffer, cmd + 2, len - 2);
+
+ return (0);
+}
+
+int maxsdrlen = 0x10;
+
+/* Read an entire SDR; pass to add sensor */
+int
+get_sdr(struct ipmi_softc * sc, u_int16_t recid, u_int16_t * nxtrec)
+{
+ u_int16_t resid;
+ int len, sdrlen, offset;
+ u_int8_t *psdr;
+ sdrhdr_t shdr;
+
+ /* Reserve SDR */
+ if (ipmi_sendcmd(sc, BMC_SA, 0, STORAGE_NETFN, STORAGE_RESERVE_SDR,
+ 0, NULL)) {
+ printf("reserve send fails\n");
+ return (-1);
+ }
+ if (ipmi_recvcmd(sc, sizeof(resid), &len, &resid)) {
+ printf("reserve recv fails\n");
+ return (-1);
+ }
+ /* Get SDR Header */
+ if (get_sdr_partial(sc, recid, resid, 0, sizeof(sdrhdr_t), &shdr,
+ nxtrec)) {
+ printf("get header fails\n");
+ return (-1);
+ }
+ /* Allocate space for entire SDR Length of SDR in header does not
+ * include header length */
+ sdrlen = sizeof(shdr) + shdr.record_length;
+ psdr = malloc(sdrlen, M_DEVBUF, M_WAITOK);
+ if (psdr == NULL)
+ return -1;
+
+ memcpy(psdr, &shdr, sizeof(shdr));
+
+ /* Read SDR Data maxsdrlen bytes at a time */
+ for (offset = sizeof(shdr); offset < sdrlen; offset += maxsdrlen) {
+ len = sdrlen - offset;
+ if (len > maxsdrlen)
+ len = maxsdrlen;
+
+ if (get_sdr_partial(sc, recid, resid, offset, len,
+ psdr + offset, NULL)) {
+ printf("get chunk : %d,%d fails\n", offset, len);
+ return (-1);
+ }
+ }
+
+ /* Add SDR to sensor list, if not wanted, free buffer */
+ if (add_sdr_sensor(sc, psdr) == 0)
+ free(psdr, M_DEVBUF);
+
+ return (0);
+}
+
+int
+getbits(u_int8_t * bytes, int bitpos, int bitlen)
+{
+ int v;
+ int mask;
+
+ bitpos += bitlen - 1;
+ for (v = 0; bitlen--;) {
+ v <<= 1;
+ mask = 1L << (bitpos & 7);
+ if (bytes[bitpos >> 3] & mask) {
+ v |= 1;
+ }
+ bitpos--;
+ }
+
+ return (v);
+}
+
+/* Decode IPMI sensor name */
+void
+ipmi_sensor_name(char *name, int len, u_int8_t typelen, u_int8_t * bits)
+{
+ int i, slen;
+ char bcdplus[] = "0123456789 -.:,_";
+
+ slen = typelen & 0x1F;
+ switch (typelen & 0xC0) {
+ case 0x00:
+ //unicode
+ break;
+
+ case 0x40:
+ //bcdplus
+ /* Characters are encoded in 4-bit BCDPLUS */
+ for (i = 0; i < slen; i++) {
+ *(name++) = bcdplus[bits[i] >> 4];
+ *(name++) = bcdplus[bits[i] & 0xF];
+ }
+ break;
+
+ case 0x80:
+ //6 - bit ascii
+ /* Characters are encoded in 6-bit ASCII 0x00 - 0x3F maps to
+ * 0x20 - 0x5F */
+ for (i = 0; i < slen * 8; i += 6) {
+ *(name++) = getbits(bits, i, 6) + ' ';
+ }
+ break;
+
+ case 0xC0:
+ //8 - bit ascii
+ while (slen--)
+ *(name++) = *(bits++);
+ break;
+ }
+ *name = 0;
+}
+
+/* Calculate val * 10^exp */
+long
+ipow(long val, int exp)
+{
+ while (exp > 0) {
+ val *= 10;
+ exp--;
+ }
+
+ while (exp < 0) {
+ val /= 10;
+ exp++;
+ }
+
+ return (val);
+}
+
+/* Convert IPMI reading from sensor factors */
+long
+ipmi_convert(u_int8_t v, sdrtype1 * s1, long adj)
+{
+ short M, B;
+ char K1, K2;
+ long val;
+
+ /* M is 10-bit value: check if negative */
+ M = (((short)(s1->m_tolerance & 0xC0)) << 2) + s1->m;
+ if (M & 0x200)
+ M |= 0xFC00;
+
+ /* B is 10-bit value; check if negative */
+ B = (((short)(s1->b_accuracy & 0xC0)) << 2) + s1->b;
+ if (B & 0x200)
+ B |= 0xFC00;
+
+ /* K1/K2 are 4-bit values; check if negative */
+ K1 = s1->rbexp & 0xF;
+ if (K1 & 0x8)
+ K1 |= 0xF0;
+
+ K2 = s1->rbexp >> 4;
+ if (K2 & 0x8)
+ K2 |= 0xF0;
+
+ /* Calculate sensor reading: y = L((M * v + (B * 10^K1)) *
+ * 10^(K2+adj));
+ *
+ * This commutes out to: y = L(M*v * 10^(K2+adj) + B * 10^(K1+K2+adj)); */
+ val = ipow(M * v, K2 + adj) + ipow(B, K1 + K2 + adj);
+
+ /* Linearization function: y = f(x) 0 : y = x 1 : y = ln(x) 2 : y =
+ * log10(x) 3 : y = log2(x) 4 : y = e^x 5 : y = 10^x 6 : y = 2^x 7 : y
+ * = 1/x 8 : y = x^2 9 : y = x^3 10 : y = square root(x) 11 : y = cube
+ * root(x) */
+ return (val);
+}
+
+int
+read_sensor(struct ipmi_softc * sc, struct ipmi_sensor * psensor)
+{
+ sdrtype1 *s1 = (sdrtype1 *) psensor->i_sdr;
+ u_int8_t data[8];
+ int rxlen;
+
+ memset(data, 0, sizeof(data));
+ data[0] = psensor->i_num;
+ if (ipmi_sendcmd(sc, s1->owner_id, s1->owner_lun, SE_NETFN,
+ SE_GET_SENSOR_READING, 1, data))
+ return (-1);
+
+ if (ipmi_recvcmd(sc, sizeof(data), &rxlen, data))
+ return (-1);
+
+ psensor->i_sensor.flags &= ~SENSOR_FINVALID;
+ if (data[1] & BIT(5)) {
+ /* Check if sensor is valid */
+ psensor->i_sensor.flags |= SENSOR_FINVALID;
+ }
+ psensor->i_sensor.status = SENSOR_S_OK;
+ if (s1->sdrhdr.record_type == 2) {
+ /* Direct reading */
+ psensor->i_sensor.value = data[0];
+ return (0);
+ }
+
+ dbg_printf(1, "sensor state: %.02x %.02x %.02x %s\n",
+ rxlen, data[2], data[3], psensor->i_sensor.desc);
+ /* ..XX.XX. X......X */
+ if (data[2] & 0x36)
+ psensor->i_sensor.status = SENSOR_S_CRIT;
+ else
+ if (data[2] & 0x81)
+ psensor->i_sensor.status = SENSOR_S_WARN;
+
+ switch (psensor->i_sensor.type) {
+ case SENSOR_TEMP:
+ psensor->i_sensor.value = ipmi_convert(data[0], s1, 6);
+ psensor->i_sensor.value += 273150000;
+ break;
+
+ case SENSOR_VOLTS_DC:
+ psensor->i_sensor.value = ipmi_convert(data[0], s1, 6);
+ break;
+
+ default:
+ psensor->i_sensor.value = ipmi_convert(data[0], s1, 0);
+ }
+
+ return (0);
+}
+
+int
+valid_sensor(int type, int btype)
+{
+ switch (type << 8L | btype) {
+ case 0x0101:
+ return (SENSOR_TEMP);
+
+ case 0x0201:
+ return (SENSOR_VOLTS_DC);
+
+ case 0x0401:
+ return (SENSOR_FANRPM);
+
+ case 0x056F:
+ return (SENSOR_INDICATOR);
+ }
+
+ return (-1);
+}
+
+/* Add Sensor to BSD Sysctl interface */
+int
+add_sdr_sensor(struct ipmi_softc * sc, u_int8_t * psdr)
+{
+ struct ipmi_sensor *psensor;
+ int typ, base, icnt, snum, idx;
+ sdrtype1 *s1 = (sdrtype1 *) psdr;
+ sdrtype2 *s2 = (sdrtype2 *) psdr;
+ char name[64];
+
+ switch (s1->sdrhdr.record_type) {
+ case 1:
+ ipmi_sensor_name(name, sizeof(name), s1->typelen, s1->name);
+ icnt = 1;
+ snum = s1->sensor_num;
+ typ = valid_sensor(s1->sensor_type, s1->event_code);
+ if (typ == -1) {
+ dbg_printf(1, "Unknown sensor type1: st:%.02x "
+ "et:%.02x sn:%.02x %s\n",
+ s1->sensor_type, s1->event_code, snum, name);
+ return (0);
+ }
+ break;
+
+ case 2:
+ ipmi_sensor_name(name, sizeof(name), s2->typelen, s2->name);
+ base = s2->share2 & 0x7F;
+ icnt = s2->share1 & 0x0F;
+ snum = s2->sensor_num;
+ typ = valid_sensor(s2->sensor_type, s2->event_code);
+ if (typ == -1) {
+ dbg_printf(1, "Unknown sensor type2: st:%.02x "
+ "et:%.02x sn:%.02x %s\n",
+ s2->sensor_type, s2->event_code, snum, name);
+
+ return (0);
+ }
+ break;
+
+ default:
+ return (0);
+ }
+
+ for (idx = 0; idx < icnt; idx++) {
+
+ psensor = malloc(sizeof(struct ipmi_sensor), M_DEVBUF,
+ M_WAITOK);
+
+ /* XXX get rid of this */
+ if (psensor == NULL)
+ break;
+
+ memset(psensor, 0, sizeof(struct ipmi_sensor));
+
+ /* Initialize BSD Sensor info */
+ psensor->i_sdr = psdr;
+ psensor->i_num = snum + idx;
+ psensor->i_sensor.status = SENSOR_S_OK;
+ psensor->i_sensor.type = typ;
+ strlcpy(psensor->i_sensor.device, sc->sc_dev.dv_xname,
+ sizeof(psensor->i_sensor.device));
+ if (icnt > 1)
+ snprintf(psensor->i_sensor.desc,
+ sizeof(psensor->i_sensor.desc),
+ "%s - %d", name, base + idx);
+ else
+ strlcpy(psensor->i_sensor.desc, name,
+ sizeof(psensor->i_sensor.desc));
+
+ dbg_printf(1, "add sensor:%.4x %.2x:%d ent:%.2x:%.2x %s\n",
+ s1->sdrhdr.record_id, s1->sensor_type,
+ typ, s1->entity_id, s1->entity_instance,
+ psensor->i_sensor.desc);
+ if (read_sensor(sc, psensor) == 0) {
+ SLIST_INSERT_HEAD(&ipmi_sensor_list, psensor, list);
+ SENSOR_ADD(&psensor->i_sensor);
+ dbg_printf(1, " reading: %lld [%s]\n",
+ psensor->i_sensor.value,
+ psensor->i_sensor.desc);
+ }
+ }
+
+ 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)
+{
+ struct ipmi_sensor *psensor = NULL;
+
+ SLIST_FOREACH(psensor, &ipmi_sensor_list, list) {
+ if (read_sensor(sc, psensor))
+ printf("error reading: %s\n", psensor->i_sensor.desc);
+
+ }
+}
+
+void
+ipmi_refresh(void *arg)
+{
+ struct ipmi_softc *sc = (struct ipmi_softc *)arg;
+
+ ipmi_refresh_sensors(sc);
+ timeout_add(&ipmi_timeout, SENSOR_REFRESH_RATE);
+}
+
+int
+ipmi_map_regs(struct ipmi_softc * sc, struct ipmi_attach_args * ia)
+{
+ sc->sc_if = ipmi_get_if(ia->iaa_if_type);
+ if (sc->sc_if == NULL)
+ return (-1);
+
+ if (ia->iaa_if_iotype == 'i')
+ sc->sc_iot = ia->iaa_iot;
+ else
+ sc->sc_iot = ia->iaa_memt;
+
+ sc->sc_if_rev = ia->iaa_if_rev;
+ sc->sc_if_iospacing = ia->iaa_if_iospacing;
+ bus_space_map(sc->sc_iot, ia->iaa_if_iobase,
+ sc->sc_if->nregs * sc->sc_if_iospacing,
+ 0, &sc->sc_ioh);
+#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
+ sc->sc_dev.dv_xname);
+ }
+#endif
+ return (0);
+}
+
+void
+ipmi_unmap_regs(struct ipmi_softc * sc, struct ipmi_attach_args * ia)
+{
+ bus_space_unmap(sc->sc_iot, sc->sc_ioh,
+ sc->sc_if->nregs * sc->sc_if_iospacing);
+}
+
+int
+ipmi_probe(struct device * parent, void *match, void *aux)
+{
+ struct ipmi_softc sc;
+ struct ipmi_attach_args *ia = aux;
+ struct cfdata *cf = match;
+ int rc;
+
+ if (strcmp(ia->iaa_name, cf->cf_driver->cd_name))
+ return (0);
+
+ if (scan_smbios(SMBIOS_TYPE_IPMI, smbios_ipmi_probe, ia) == 0) {
+ dmd_ipmi_t *pipmi;
+
+ pipmi = (dmd_ipmi_t *)scan_sig(0xC0000L, 0xFFFFFL, 16, 4,
+ "IPMI");
+ if (pipmi == NULL)
+ return (0);
+
+ ia->iaa_if_type = pipmi->dmd_if_type;
+ ia->iaa_if_rev = pipmi->dmd_if_rev;
+ }
+ /* Map registers */
+ if (ipmi_map_regs(&sc, ia) != 0)
+ return (0);
+
+ rc = sc.sc_if->probe(&sc);
+ ipmi_unmap_regs(&sc, ia);
+
+ return (!rc);
+}
+
+void
+ipmi_attach(struct device * parent, struct device * self, void *aux)
+{
+ struct ipmi_softc *sc = (void *) self;
+ struct ipmi_attach_args *ia = aux;
+ u_int8_t cmd[32];
+ int len;
+ u_int16_t rec;
+
+ /* Map registers */
+ ipmi_map_regs(sc, ia);
+
+ /* Identify BMC device */
+ ipmi_sendcmd(sc, BMC_SA, 0, APP_NETFN, APP_GET_DEVICE_ID, 0, NULL);
+ ipmi_recvcmd(sc, sizeof(cmd), &len, cmd);
+ dbg_dump(1, "bmc data", len, cmd);
+
+ /* Scan SDRs, add sensors */
+ for (rec = 0; rec != 0xFFFF;)
+ if (get_sdr(sc, rec, &rec))
+ break;
+
+ /* Setup timeout */
+ timeout_set(&ipmi_timeout, ipmi_refresh, sc);
+ timeout_add(&ipmi_timeout, SENSOR_REFRESH_RATE);
+
+ printf("\n%s: ver=%d.%d iface=%s %cbase=0x%x/%x spacing=%d irq=%d\n",
+ sc->sc_dev.dv_xname,
+ ia->iaa_if_rev >> 4,
+ ia->iaa_if_rev & 0xF,
+ sc->sc_if->name,
+ ia->iaa_if_iotype,
+ ia->iaa_if_iobase,
+ ia->iaa_if_iospacing * sc->sc_if->nregs,
+ ia->iaa_if_iospacing,
+ ia->iaa_if_irq);
+}
diff --git a/sys/dev/ipmivar.h b/sys/dev/ipmivar.h
new file mode 100644
index 00000000000..722e072db36
--- /dev/null
+++ b/sys/dev/ipmivar.h
@@ -0,0 +1,317 @@
+/* $OpenBSD: ipmivar.h,v 1.1 2005/10/04 21:59:41 marco Exp $ */
+
+/*
+ * Copyright (c) 2005 Jordan Hargrave
+ * 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 AUTHORS 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 AUTHORS 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.
+ *
+ */
+
+#ifndef _IPMIVAR_H_
+#define _IPMIVAR_H_
+
+#define IPMI_IF_KCS 1
+#define IPMI_IF_SMIC 2
+#define IPMI_IF_BT 3
+
+#define IPMI_IF_KCS_NREGS 2
+#define IPMI_IF_SMIC_NREGS 3
+#define IPMI_IF_BT_NREGS 3
+
+struct ipmi_softc;
+
+struct ipmi_attach_args {
+ char *iaa_name;
+ bus_space_tag_t iaa_iot;
+ bus_space_tag_t iaa_memt;
+
+ int iaa_if_type;
+ int iaa_if_rev;
+ int iaa_if_iotype;
+ int iaa_if_iobase;
+ int iaa_if_iospacing;
+ int iaa_if_irq;
+ int iaa_if_irqlvl;
+};
+
+struct ipmi_if {
+ const char *name;
+ int nregs;
+ void *(*buildmsg)(struct ipmi_softc *, int, int, int,
+ const void *, int *);
+ int (*sendmsg)(struct ipmi_softc *, int, const u_int8_t *);
+ int (*recvmsg)(struct ipmi_softc *, int, int *, u_int8_t *);
+ int (*reset)(struct ipmi_softc *);
+ int (*probe)(struct ipmi_softc *);
+};
+
+struct ipmi_softc {
+ struct device sc_dev;
+
+ struct ipmi_if *sc_if; /* Interface layer */
+ int sc_if_iospacing; /* Spacing of I/O ports */
+ int sc_if_rev; /* IPMI Revision */
+
+ void *sc_ih; /* Interrupt/IO handles */
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+
+ int sc_btseq;
+};
+
+int kcs_probe(struct ipmi_softc *);
+int kcs_reset(struct ipmi_softc *);
+int kcs_sendmsg(struct ipmi_softc *, int, const u_int8_t *);
+int kcs_recvmsg(struct ipmi_softc *, int, int *len, u_int8_t *);
+
+int bt_probe(struct ipmi_softc *);
+int bt_reset(struct ipmi_softc *);
+int bt_sendmsg(struct ipmi_softc *, int, const u_int8_t *);
+int bt_recvmsg(struct ipmi_softc *, int, int *, u_int8_t *);
+
+int smic_probe(struct ipmi_softc *);
+int smic_reset(struct ipmi_softc *);
+int smic_sendmsg(struct ipmi_softc *, int, const u_int8_t *);
+int smic_recvmsg(struct ipmi_softc *, int, int *, u_int8_t *);
+
+#define SMBIOS_TYPE_IPMI 0x26
+#define SMBIOS_TYPE_END 0x7F
+
+typedef struct {
+ u_int8_t smr_smtag[4]; /* Signature '_SM_' */
+ u_int8_t smr_ep_cksum; /* Chcksum Entry Point struct */
+ u_int8_t smr_length; /* Length of Anchor structure */
+ u_int8_t smr_smbios_majver; /* SMBIOS Major Version */
+ u_int8_t smr_smbios_minver; /* SMBIOS Minor Version */
+ u_int16_t smr_maxsize; /* Max size of SMHDR entry */
+ u_int8_t smr_eprev; /* SMBIOS Entry Revision (00) */
+ u_int8_t smr_format[5]; /* Should be Zero */
+ u_int8_t smr_dmitag[5]; /* Signature '_DMI_' */
+ u_int8_t smr_iep_cksum; /* Chcksum of Intermediate Entry
+ * Point * structure */
+ u_int16_t smr_table_length; /* Length of SMBIOS Table */
+ u_int32_t smr_table_address; /* Phys addr of SMBIOS Table */
+ u_int16_t smr_count; /* # of entries in SMBIOS Tbl */
+ u_int8_t smr_bcdrev; /* BCD SMBIOS Revision */
+} __attribute__((packed)) smbiosanchor_t;
+
+typedef struct {
+ u_int8_t smh_type; /* SMBIOS Header Type */
+ u_int8_t smh_length; /* SMBIOS Header Length */
+ u_int16_t smh_handle; /* SMBIOS Header Handle */
+} smhdr_t;
+
+typedef struct {
+ u_int8_t smipmi_if_type; /* IPMI Interface Type */
+ u_int8_t smipmi_if_rev; /* BCD IPMI Revision */
+ u_int8_t smipmi_i2c_address; /* I2C address of BMC */
+ u_int8_t smipmi_nvram_address; /* I2C address of NVRAM
+ * storage */
+ u_int64_t smipmi_base_address; /* Base address of BMC (BAR
+ * format) */
+ u_int8_t smipmi_base_flags; /* Flags field:
+ * spacing/lsb/irq bit 7:6 :
+ * register spacing 00 = byte
+ * alignment 01 = dword
+ * alignment 02 = word
+ * alignment bit 4 : Lower bit
+ * of base address bit 3 : IRQ
+ * valid bit 1 : Interrupt
+ * polarity bit 0 : Interrupt
+ * trigger mode */
+ u_int8_t smipmi_irq; /* IRQ if applicable */
+}__attribute__((packed)) smbios_ipmi_t;
+
+typedef struct {
+ u_int8_t dmd_sig[4]; /* Signature 'IPMI' */
+ u_int8_t dmd_i2c_address; /* Address of BMC */
+ u_int8_t dmd_nvram_address; /* Address of NVRAM */
+ u_int8_t dmd_if_type; /* IPMI Interface Type */
+ u_int8_t dmd_if_rev; /* IPMI Interface Revision */
+}__attribute__((packed)) dmd_ipmi_t;
+
+
+#define APP_NETFN 0x06
+#define APP_GET_DEVICE_ID 0x01
+#define APP_RESET_WATCHDOG 0x22
+#define APP_SET_WATCHDOG_TIMER 0x24
+#define APP_GET_WATCHDOG_TIMER 0x25
+
+#define TRANSPORT_NETFN 0xC
+#define BRIDGE_NETFN 0x2
+
+#define STORAGE_NETFN 0x0A
+#define STORAGE_RESERVE_SDR 0x22
+#define STORAGE_GET_SDR 0x23
+#define STORAGE_ADD_SDR 0x24
+#define STORAGE_ADD_PARTIAL_SDR 0x25
+#define STORAGE_DELETE_SDR 0x26
+#define STORAGE_RESERVE_SEL 0x42
+#define STORAGE_GET_SEL 0x43
+#define STORAGE_ADD_SEL 0x44
+#define STORAGE_ADD_PARTIAL_SEL 0x45
+#define STORAGE_DELETE_SEL 0x46
+#define STORAGE_CLEAR_SEL 0x47
+
+#define SE_NETFN 0x04
+#define SE_GET_SDR_INFO 0x20
+#define SE_GET_SDR 0x21
+#define SE_RESERVE_SDR 0x22
+#define SE_GET_SENSOR_FACTOR 0x23
+#define SE_SET_SENSOR_HYSTERESIS 0x24
+#define SE_GET_SENSOR_HYSTERESIS 0x25
+#define SE_SET_SENSOR_THRESHOLD 0x26
+#define SE_GET_SENSOR_THRESHOLD 0x27
+#define SE_SET_SENSOR_EVENT_ENABLE 0x28
+#define SE_GET_SENSOR_EVENT_ENABLE 0x29
+#define SE_REARM_SENSOR_EVENTS 0x2A
+#define SE_GET_SENSOR_EVENT_STATUS 0x2B
+#define SE_GET_SENSOR_READING 0x2D
+#define SE_SET_SENSOR_TYPE 0x2E
+#define SE_GET_SENSOR_TYPE 0x2F
+
+#define APP_NETFN 0x06
+#define APP_GET_DEVICE_ID 0x01
+#define APP_RESET_WATCHDOG 0x22
+#define APP_SET_WATCHDOG_TIMER 0x24
+#define APP_GET_WATCHDOG_TIMER 0x25
+
+#define TRANSPORT_NETFN 0xC
+#define BRIDGE_NETFN 0x2
+
+#define STORAGE_NETFN 0x0A
+#define STORAGE_GET_FRU_INV_AREA 0x10
+#define STORAGE_READ_FRU_DATA 0x11
+#define STORAGE_RESERVE_SDR 0x22
+#define STORAGE_GET_SDR 0x23
+#define STORAGE_ADD_SDR 0x24
+#define STORAGE_ADD_PARTIAL_SDR 0x25
+#define STORAGE_DELETE_SDR 0x26
+#define STORAGE_RESERVE_SEL 0x42
+#define STORAGE_GET_SEL 0x43
+#define STORAGE_ADD_SEL 0x44
+#define STORAGE_ADD_PARTIAL_SEL 0x45
+#define STORAGE_DELETE_SEL 0x46
+
+#define SE_NETFN 0x04
+#define SE_GET_SDR_INFO 0x20
+#define SE_GET_SDR 0x21
+#define SE_RESERVE_SDR 0x22
+#define SE_GET_SENSOR_FACTOR 0x23
+#define SE_SET_SENSOR_HYSTERESIS 0x24
+#define SE_GET_SENSOR_HYSTERESIS 0x25
+#define SE_SET_SENSOR_THRESHOLD 0x26
+#define SE_GET_SENSOR_THRESHOLD 0x27
+#define SE_SET_SENSOR_EVENT_ENABLE 0x28
+#define SE_GET_SENSOR_EVENT_ENABLE 0x29
+#define SE_REARM_SENSOR_EVENTS 0x2A
+#define SE_GET_SENSOR_EVENT_STATUS 0x2B
+#define SE_GET_SENSOR_READING 0x2D
+#define SE_SET_SENSOR_TYPE 0x2E
+#define SE_GET_SENSOR_TYPE 0x2F
+
+typedef struct {
+ u_int16_t record_id; /* SDR Record ID */
+ u_int8_t sdr_version; /* SDR Version */
+ u_int8_t record_type; /* SDR Record Type */
+ u_int8_t record_length; /* SDR Record Length */
+}__attribute__((packed)) sdrhdr_t;
+
+/* SDR: Record Type 1 */
+typedef struct {
+ sdrhdr_t sdrhdr;
+
+ u_int8_t owner_id;
+ u_int8_t owner_lun;
+ u_int8_t sensor_num;
+
+ u_int8_t entity_id;
+ u_int8_t entity_instance;
+ u_int8_t sensor_init;
+ u_int8_t sensor_caps;
+ u_int8_t sensor_type;
+ u_int8_t event_code;
+ u_int16_t trigger_mask;
+ u_int16_t reading_mask;
+ u_int16_t settable_mask;
+ u_int8_t units1;
+ u_int8_t units2;
+ u_int8_t units3;
+ u_int8_t linear;
+ u_int8_t m;
+ u_int8_t m_tolerance;
+ u_int8_t b;
+ u_int8_t b_accuracy;
+ u_int8_t accuracyexp;
+ u_int8_t rbexp;
+ u_int8_t analogchars;
+ u_int8_t nominalreading;
+ u_int8_t normalmax;
+ u_int8_t normalmin;
+ u_int8_t sensormax;
+ u_int8_t sensormin;
+ u_int8_t uppernr;
+ u_int8_t upperc;
+ u_int8_t uppernc;
+ u_int8_t lowernr;
+ u_int8_t lowerc;
+ u_int8_t lowernc;
+ u_int8_t physt;
+ u_int8_t nhyst;
+ u_int8_t resvd[2];
+ u_int8_t oem;
+ u_int8_t typelen;
+ u_int8_t name[1];
+} __attribute__((packed)) sdrtype1;
+
+/* SDR: Record Type 2 */
+typedef struct {
+ sdrhdr_t sdrhdr;
+
+ u_int8_t owner_id;
+ u_int8_t owner_lun;
+ u_int8_t sensor_num;
+
+ u_int8_t entity_id;
+ u_int8_t entity_instance;
+ u_int8_t sensor_init;
+ u_int8_t sensor_caps;
+ u_int8_t sensor_type;
+ u_int8_t event_code;
+ u_int16_t trigger_mask;
+ u_int16_t reading_mask;
+ u_int16_t set_mask;
+ u_int8_t units1;
+ u_int8_t units2;
+ u_int8_t units3;
+ u_int8_t share1;
+ u_int8_t share2;
+ u_int8_t physt;
+ u_int8_t nhyst;
+ u_int8_t resvd[3];
+ u_int8_t oem;
+ u_int8_t typelen;
+ u_int8_t name[1];
+} __attribute__((packed)) sdrtype2;
+
+#endif /* _IPMIVAR_H_ */