diff options
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/ipmi.c | 1376 | ||||
-rw-r--r-- | sys/dev/ipmivar.h | 317 |
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_ */ |