Subject: port-i386/34932: Patches to port the ipmi(4) driver back to NetBSD/i386 3.x
To: None <port-i386-maintainer@netbsd.org, gnats-admin@netbsd.org,>
From: Brian Buhrow <buhrow@lothlorien.nfbcal.org>
List: netbsd-bugs
Date: 10/28/2006 17:25:01
>Number: 34932
>Category: port-i386
>Synopsis: Make the ipmi(4) driver work under NetBSD/i386 3.x
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: port-i386-maintainer
>State: open
>Class: change-request
>Submitter-Id: net
>Arrival-Date: Sat Oct 28 17:25:00 +0000 2006
>Originator: Brian Buhrow
>Release: NetBSD 3.0_STABLE
>Organization:
>Environment:
System: NetBSD nfbcal.org 3.0_STABLE NetBSD 3.0_STABLE (NFBNETBSD) #0: Tue Jan 31 14:45:08 PST 2006 buhrow@lothlorien.nfbcal.org:/usr/src/sys/arch/i386/compile/NFBNETBSD i386
Architecture: i386
Machine: i386
>Description:
I took the ipmi(4) driver from -current and back ported it to the
NetBSD-3 branch. It compiles fine, and the kernels produced run. However,
as of this writing, I don't have an ipmi enabled machine on which to test
things out. It occurred to me, however, that ipmi is the kind of thing
people want on their production boxes, and if other folks are like me,
they're still running 3.x code on their servers. So, I thought I'd provide
this stuff so that it wouldn't get lost, and so that anyone who needs it
might be able to use it.
I'll update this report when I have a chance to actually run this
stuff.
As with the -current code, this works only on I386 at this time. I
may have patches to make it fly under amd64 at a later time.
-Brian
>How-To-Repeat:
>Fix:
Here are the diffs. Also, be sure to add the following lines to your
kernel configuration file.
Also, this should compile against all versions of 3.x code, i.e. 3.0, 3.0.1
and the netbsd-3 branch from any arbitrary date.
#Add ipmi support (BB 10/26/2006)
ipmi* at ipmibus?
Index: sys/arch/i386/conf/files.i386
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/conf/files.i386,v
retrieving revision 1.262
diff -u -r1.262 files.i386
--- sys/arch/i386/conf/files.i386 14 Sep 2004 16:51:57 -0000 1.262
+++ sys/arch/i386/conf/files.i386 28 Oct 2006 16:27:08 -0000
@@ -143,7 +143,7 @@
# XXX BIOS32 only if something that uses it is configured!
device mainbus: isabus, eisabus, mcabus, pcibus, bios32, acpibus,
- cpubus, apmbus, pnpbiosbus, vesabiosbus
+ cpubus, apmbus, pnpbiosbus, vesabiosbus, ipmibus
attach mainbus at root
file arch/i386/i386/mainbus.c mainbus
Index: sys/arch/i386/i386/bios32.c
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/i386/bios32.c,v
retrieving revision 1.7
diff -u -r1.7 bios32.c
--- sys/arch/i386/i386/bios32.c 1 Oct 2002 12:56:48 -0000 1.7
+++ sys/arch/i386/i386/bios32.c 28 Oct 2006 16:27:08 -0000
@@ -80,11 +80,21 @@
#include <machine/segments.h>
#include <machine/bios32.h>
+#include "ipmi.h"
+
+#if NIPMI > 0
+#include <x86/smbiosvar.h>
+#include <uvm/uvm.h>
+#endif
+
#define BIOS32_START 0xe0000
#define BIOS32_SIZE 0x20000
#define BIOS32_END (BIOS32_START + BIOS32_SIZE - 0x10)
struct bios32_entry bios32_entry;
+#if NIPMI > 0
+struct smbios_entry smbios_entry;
+#endif
/*
* Initialize the BIOS32 interface.
@@ -130,6 +140,56 @@
bios32_entry.offset = (caddr_t)ISA_HOLE_VADDR(entry);
bios32_entry.segment = GSEL(GCODE_SEL, SEL_KPL);
}
+
+#if NIPMI > 0
+ /* see if we have SMBIOS extentions */
+ for (p = ISA_HOLE_VADDR(SMBIOS_START);
+ p < (caddr_t)ISA_HOLE_VADDR(SMBIOS_END); p+= 16) {
+ struct smbhdr * sh = (struct smbhdr *)p;
+ u_int8_t chksum;
+ vaddr_t eva;
+ paddr_t pa, end;
+
+ if (sh->sig != BIOS32_MAKESIG('_', 'S', 'M', '_'))
+ continue;
+ i = sh->len;
+ for (chksum = 0; i--; chksum += p[i])
+ ;
+ if (chksum != 0)
+ continue;
+ p += 0x10;
+ if (p[0] != '_' && p[1] != 'D' && p[2] != 'M' &&
+ p[3] != 'I' && p[4] != '_')
+ continue;
+ for (chksum = 0, i = 0xf; i--; chksum += p[i]);
+ ;
+ if (chksum != 0)
+ continue;
+
+ pa = trunc_page(sh->addr);
+ end = round_page(sh->addr + sh->size);
+ eva = uvm_km_alloc(kernel_map, end - pa);
+ if (eva == 0)
+ break;
+
+ smbios_entry.addr = (u_int8_t *)(eva +
+ (sh->addr & PGOFSET));
+ smbios_entry.len = sh->size;
+ smbios_entry.mjr = sh->majrev;
+ smbios_entry.min = sh->minrev;
+ smbios_entry.count = sh->count;
+
+ for (; pa < end; pa+= NBPG, eva+= NBPG)
+ pmap_kenter_pa(eva, pa, VM_PROT_READ);
+
+ printf("SMBIOS rev. %d.%d @ 0x%lx (%d entries)\n",
+ sh->majrev, sh->minrev, (u_long)sh->addr,
+ sh->count);
+
+ break;
+ }
+#endif
+
}
/*
@@ -176,3 +236,90 @@
return (1);
}
+
+
+#if NIPMI > 0
+/*
+ * smbios_find_table() takes a caller supplied smbios struct type and
+ * a pointer to a handle (struct smbtable) returning one if the structure
+ * is sucessfully located and zero otherwise. Callers should take care
+ * to initilize the cookie field of the smbtable structure to zero before
+ * the first invocation of this function.
+ * Multiple tables of the same type can be located by repeadtly calling
+ * smbios_find_table with the same arguments.
+ */
+int
+smbios_find_table(u_int8_t type, struct smbtable *st)
+{
+ u_int8_t *va, *end;
+ struct smbtblhdr *hdr;
+ int ret = 0, tcount = 1;
+
+ va = smbios_entry.addr;
+ end = va + smbios_entry.len;
+
+ /*
+ * The cookie field of the smtable structure is used to locate
+ * multiple instances of a table of an arbitrary type. Following the
+ * sucessful location of a table, the type is encoded as bits 0:7 of
+ * the cookie value, the offset in terms of the number of structures
+ * preceding that referenced by the handle is encoded in bits 15:31.
+ */
+ if ((st->cookie & 0xfff) == type && st->cookie >> 16) {
+ if ((u_int8_t *)st->hdr >= va && (u_int8_t *)st->hdr < end) {
+ hdr = st->hdr;
+ if (hdr->type == type) {
+ va = (u_int8_t *)hdr + hdr->size;
+ for (; va + 1 < end; va++)
+ if (*va == 0 && *(va + 1) == 0)
+ break;
+ va+= 2;
+ tcount = st->cookie >> 16;
+ }
+ }
+ }
+ for (; va + sizeof(struct smbtblhdr) < end && tcount <=
+ smbios_entry.count; tcount++) {
+ hdr = (struct smbtblhdr *)va;
+ if (hdr->type == type) {
+ ret = 1;
+ st->hdr = hdr;
+ st->tblhdr = va + sizeof(struct smbtblhdr);
+ st->cookie = (tcount + 1) << 16 | type;
+ break;
+ }
+ if (hdr->type == SMBIOS_TYPE_EOT)
+ break;
+ va+= hdr->size;
+ for (; va + 1 < end; va++)
+ if (*va == 0 && *(va + 1) == 0)
+ break;
+ va+=2;
+ }
+
+ return ret;
+}
+
+char *
+smbios_get_string(struct smbtable *st, u_int8_t indx, char *dest, size_t len)
+{
+ u_int8_t *va, *end;
+ char *ret = NULL;
+ int i;
+
+ va = (u_int8_t *)st->hdr + st->hdr->size;
+ end = smbios_entry.addr + smbios_entry.len;
+ for (i = 1; va < end && i < indx && *va; i++)
+ while (*va++)
+ ;
+ if (i == indx) {
+ if (va + len < end) {
+ ret = dest;
+ bcopy(va, ret, len);
+ ret[len - 1] = '\0';
+ }
+ }
+
+ return ret;
+}
+#endif
Index: sys/arch/x86/conf/files.x86
===================================================================
RCS file: /cvsroot/src/sys/arch/x86/conf/files.x86,v
retrieving revision 1.12
diff -u -r1.12 files.x86
--- sys/arch/x86/conf/files.x86 20 Oct 2004 04:20:05 -0000 1.12
+++ sys/arch/x86/conf/files.x86 28 Oct 2006 16:27:52 -0000
@@ -8,6 +8,7 @@
defflag MTRR
define cpubus { [apid = -1] }
+define ipmibus {}
file arch/x86/x86/apic.c ioapic | lapic
file arch/x86/x86/bus_dma.c
@@ -42,3 +43,8 @@
file arch/x86/isa/isa_machdep.c isa
file arch/x86/pci/pciide_machdep.c pciide_common
+
+#IPMI device
+device ipmi : sysmon_envsys, sysmon_wdog
+attach ipmi at ipmibus
+file arch/x86/x86/ipmi.c ipmi needs-flag
--- /dev/null 2006-10-28 10:00:08.000000000 -0700
+++ sys/arch/x86/include/ipmivar.h 2006-10-01 11:37:55.000000000 -0700
@@ -0,0 +1,308 @@
+/* $NetBSD: ipmivar.h,v 1.1 2006/10/01 18:37:55 bouyer 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 <dev/sysmon/sysmonvar.h>
+
+#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_thread;
+struct ipmi_softc;
+
+struct ipmi_bmc_args{
+ int offset;
+ u_int8_t mask;
+ u_int8_t value;
+ volatile u_int8_t *v;
+};
+
+struct ipmi_attach_args {
+ 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;
+
+ struct proc *sc_kthread;
+
+ struct callout sc_callout;
+ int sc_max_retries;
+ int sc_retries;
+ int sc_wakeup;
+
+ struct lock sc_lock;
+
+ struct ipmi_bmc_args *sc_iowait_args;
+
+ struct ipmi_sensor *current_sensor;
+ volatile int sc_thread_running;
+ struct sysmon_wdog sc_wdog;
+ struct sysmon_envsys sc_envsys;
+#define sc_ranges sc_envsys.sme_ranges
+#define sc_sensor_info sc_envsys.sme_sensor_info
+#define sc_sensor_data sc_envsys.sme_sensor_data
+ int sc_nsensors; /* total number of sensors */
+ int sc_nsensors_typ[ENVSYS_NSENSORS]; /* number per type */
+};
+
+struct ipmi_thread {
+ struct ipmi_softc *sc;
+ volatile int running;
+};
+
+#define IPMI_WDOG_USE_NLOG 0x80
+#define IPMI_WDOG_USE_NSTOP 0x40
+#define IPMI_WDOG_USE_USE_MASK 0x07
+#define IPMI_WDOG_USE_USE_FRB2 0x01
+#define IPMI_WDOG_USE_USE_POST 0x02
+#define IPMI_WDOG_USE_USE_OSLOAD 0x03
+#define IPMI_WDOG_USE_USE_OS 0x04
+#define IPMI_WDOG_USE_USE_EOM 0x05
+
+#define IPMI_WDOG_ACT_MASK 0x07
+#define IPMI_WDOG_ACT_DISABLED 0x00
+#define IPMI_WDOG_ACT_RESET 0x01
+#define IPMI_WDOG_ACT_PWROFF 0x02
+#define IPMI_WDOG_ACT_PWRCYCLE 0x03
+
+#define IPMI_WDOG_ACT_PRE_MASK 0x70
+#define IPMI_WDOG_ACT_PRE_DISABLED 0x00
+#define IPMI_WDOG_ACT_PRE_SMI 0x10
+#define IPMI_WDOG_ACT_PRE_NMI 0x20
+#define IPMI_WDOG_ACT_PRE_INTERRUPT 0x30
+
+struct ipmi_set_watchdog {
+ u_int8_t wdog_use;
+ u_int8_t wdog_action;
+ u_int8_t wdog_pretimeout;
+ u_int8_t wdog_flags;
+ u_int16_t wdog_timeout;
+} __packed;
+
+struct ipmi_get_watchdog {
+ u_int8_t wdog_use;
+ u_int8_t wdog_action;
+ u_int8_t wdog_pretimeout;
+ u_int8_t wdog_flags;
+ u_int16_t wdog_timeout;
+ u_int16_t wdog_countdown;
+} __packed;
+
+void ipmi_create_thread(void *);
+void ipmi_poll_thread(void *);
+
+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 *);
+
+struct dmd_ipmi {
+ 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 */
+} __packed;
+
+
+#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
+
+struct sdrhdr {
+ 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 */
+} __packed;
+
+/* SDR: Record Type 1 */
+struct sdrtype1 {
+ struct sdrhdr 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];
+} __packed;
+
+/* SDR: Record Type 2 */
+struct sdrtype2 {
+ struct sdrhdr 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];
+} __packed;
+
+int ipmi_probe(struct ipmi_attach_args *);
+
+#endif /* _IPMIVAR_H_ */
--- /dev/null 2006-10-28 10:00:27.000000000 -0700
+++ sys/arch/x86/include/smbiosvar.h 2006-10-01 11:37:55.000000000 -0700
@@ -0,0 +1,207 @@
+/* $NetBSD: smbiosvar.h,v 1.1 2006/10/01 18:37:55 bouyer Exp $ */
+/*
+ * Copyright (c) 2006 Gordon Willem Klok <gklok@cogeco.ca>
+ * 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 _I386_SMBIOSVAR_
+#define _I386_SMBIOSVAR_
+
+#define SMBIOS_START 0xf0000
+#define SMBIOS_END 0xfffff
+
+#define SMBIOS_UUID_NPRESENT 0x1
+#define SMBIOS_UUID_NSET 0x2
+
+/*
+ * Section 3.5 of "UUIDs and GUIDs" found at
+ * http://www.opengroup.org/dce/info/draft-leach-uuids-guids-01.txt
+ * specifies the string repersentation of a UUID.
+ */
+#define SMBIOS_UUID_REP "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x"
+#define SMBIOS_UUID_REPLEN 37 /* 16 zero padded values, 4 hyphens, 1 null */
+
+struct smbios_entry {
+ u_int8_t mjr;
+ u_int8_t min;
+ u_int8_t *addr;
+ u_int16_t len;
+ u_int16_t count;
+};
+
+struct smbhdr {
+ u_int32_t sig; /* "_SM_" */
+ u_int8_t checksum; /* Entry point checksum */
+ u_int8_t len; /* Entry point structure length */
+ u_int8_t majrev; /* Specification major revision */
+ u_int8_t minrev; /* Specification minor revision */
+ u_int16_t mss; /* Maximum Structure Size */
+ u_int8_t epr; /* Entry Point Revision */
+ u_int8_t fa[5]; /* value determined by EPR */
+ u_int8_t sasig[5]; /* Secondary Anchor "_DMI_" */
+ u_int8_t sachecksum; /* Secondary Checksum */
+ u_int16_t size; /* Length of structure table in bytes */
+ u_int32_t addr; /* Structure table address */
+ u_int16_t count; /* Number of SMBIOS structures */
+ u_int8_t rev; /* BCD revision */
+} __packed;
+
+struct smbtblhdr {
+ u_int8_t type;
+ u_int8_t size;
+ u_int16_t handle;
+} __packed;
+
+struct smbtable {
+ struct smbtblhdr *hdr;
+ void *tblhdr;
+ u_int32_t cookie;
+};
+
+#define SMBIOS_TYPE_BIOS 0
+#define SMBIOS_TYPE_SYSTEM 1
+#define SMBIOS_TYPE_BASEBOARD 2
+#define SMBIOS_TYPE_ENCLOSURE 3
+#define SMBIOS_TYPE_PROCESSOR 4
+#define SMBIOS_TYPE_MEMCTRL 5
+#define SMBIOS_TYPE_MEMMOD 6
+#define SMBIOS_TYPE_CACHE 7
+#define SMBIOS_TYPE_PORT 8
+#define SMBIOS_TYPE_SLOTS 9
+#define SMBIOS_TYPE_OBD 10
+#define SMBIOS_TYPE_OEM 11
+#define SMBIOS_TYPE_SYSCONFOPT 12
+#define SMBIOS_TYPE_BIOSLANG 13
+#define SMBIOS_TYPE_GROUPASSOC 14
+#define SMBIOS_TYPE_SYSEVENTLOG 15
+#define SMBIOS_TYPE_PHYMEM 16
+#define SMBIOS_TYPE_MEMDEV 17
+#define SMBIOS_TYPE_ECCINFO32 18
+#define SMBIOS_TYPE_MEMMAPARRAYADDR 19
+#define SMBIOS_TYPE_MEMMAPDEVADDR 20
+#define SMBIOS_TYPE_INBUILTPOINT 21
+#define SMBIOS_TYPE_PORTBATT 22
+#define SMBIOS_TYPE_SYSRESET 23
+#define SMBIOS_TYPE_HWSECUIRTY 24
+#define SMBIOS_TYPE_PWRCTRL 25
+#define SMBIOS_TYPE_VOLTPROBE 26
+#define SMBIOS_TYPE_COOLING 27
+#define SMBIOS_TYPE_TEMPPROBE 28
+#define SMBIOS_TYPE_CURRENTPROBE 29
+#define SMBIOS_TYPE_OOB_REMOTEACCESS 30
+#define SMBIOS_TYPE_BIS 31
+#define SMBIOS_TYPE_SBI 32
+#define SMBIOS_TYPE_ECCINFO64 33
+#define SMBIOS_TYPE_MGMTDEV 34
+#define SMBIOS_TYPE_MGTDEVCOMP 35
+#define SMBIOS_TYPE_MGTDEVTHRESH 36
+#define SMBIOS_TYPE_MEMCHANNEL 37
+#define SMBIOS_TYPE_IPMIDEV 38
+#define SMBIOS_TYPE_SPS 39
+#define SMBIOS_TYPE_INACTIVE 126
+#define SMBIOS_TYPE_EOT 127
+
+/*
+ * SMBIOS Structure Type 0 "BIOS Information"
+ * DMTF Specification DSP0134 Section: 3.3.1 p.g. 34
+ */
+struct smbios_struct_bios {
+ u_int8_t vendor; /* string */
+ u_int8_t version; /* string */
+ u_int16_t startaddr;
+ u_int8_t release; /* string */
+ u_int8_t romsize;
+ u_int64_t characteristics;
+ u_int32_t charext;
+ u_int8_t major_rel;
+ u_int8_t minor_rel;
+ u_int8_t ecf_mjr_rel; /* embedded controler firmware */
+ u_int8_t ecf_min_rel; /* embedded controler firmware */
+} __packed;
+
+/*
+ * SMBIOS Structure Type 1 "System Information"
+ * DMTF Specification DSP0134 Section 3.3.2 p.g. 35
+ */
+
+struct smbios_sys {
+/* SMBIOS spec 2.0+ */
+ u_int8_t vendor; /* string */
+ u_int8_t product; /* string */
+ u_int8_t version; /* string */
+ u_int8_t serial; /* string */
+/* SMBIOS spec 2.1+ */
+ u_int8_t uuid[16];
+ u_int8_t wakeup;
+/* SMBIOS spec 2.4+ */
+ u_int8_t sku; /* string */
+ u_int8_t family; /* string */
+} __packed;
+
+/*
+ * SMBIOS Structure Type 2 "Base Board (Module) Information"
+ * DMTF Specification DSP0134 Section 3.3.3 p.g. 37
+ */
+struct smbios_board {
+ u_int8_t vendor; /* string */
+ u_int8_t product; /* string */
+ u_int8_t version; /* string */
+ u_int8_t serial; /* string */
+ u_int8_t asset; /* stirng */
+ u_int8_t feature; /* feature flags */
+ u_int8_t location; /* location in chassis */
+ u_int16_t handle; /* chassis handle */
+ u_int8_t type; /* board type */
+ u_int8_t noc; /* number of contained objects */
+} __packed;
+
+/*
+ * SMBIOS Structure Type 38 "IPMI Information"
+ * DMTF Specification DSP0134 Section 3.3.39 p.g. 91
+ */
+struct smbios_ipmi {
+ 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:
+ * bit 7:6 : register spacing
+ * 00 = byte
+ * 01 = dword
+ * 02 = word
+ * bit 4 : Lower bit BAR
+ * bit 3 : IRQ valid
+ * bit 2 : N/A
+ * bit 1 : Interrupt polarity
+ * bit 0 : Interrupt trigger */
+ u_int8_t smipmi_irq; /* IRQ if applicable */
+} __packed;
+
+int smbios_find_table(u_int8_t, struct smbtable *);
+char *smbios_get_string(struct smbtable *, u_int8_t, char *, size_t);
+
+#endif
--- /dev/null 2006-10-28 10:00:27.000000000 -0700
+++ sys/arch/x86/x86/ipmi.c 2006-10-26 10:17:32.000000000 -0700
@@ -0,0 +1,1929 @@
+/* $NetBSD: ipmi.c,v 1.2 2006/10/12 01:30:44 christos Exp $ */
+/*
+ * Copyright (c) 2006 Manuel Bouyer.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Manuel Bouyer.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+ *
+ */
+
+/*
+ * 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/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: ipmi.c,v 1.2 2006/10/12 01:30:44 christos Exp $");
+
+#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/callout.h>
+#include <sys/lock.h>
+#include <sys/envsys.h>
+#include <sys/malloc.h>
+#include <sys/kthread.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <x86/smbiosvar.h>
+
+#include <dev/isa/isareg.h>
+#include <dev/isa/isavar.h>
+
+#include <x86/ipmivar.h>
+
+#include <uvm/uvm_extern.h>
+
+struct ipmi_sensor {
+ u_int8_t *i_sdr;
+ int i_num;
+ int i_stype;
+ int i_etype;
+ char i_envdesc[64];
+ int i_envtype; /* envsys compatible type */
+ int i_envnum; /* envsys index */
+ SLIST_ENTRY(ipmi_sensor) i_list;
+};
+
+int ipmi_nintr;
+int ipmi_dbg = 0;
+int ipmi_poll = 1;
+int ipmi_enabled = 0;
+
+#define SENSOR_REFRESH_RATE (5 * hz)
+
+#define SMBIOS_TYPE_IPMI 0x26
+
+#define DEVNAME(s) ((s)->sc_dev.dv_xname)
+
+/*
+ * Format of SMBIOS IPMI Flags
+ *
+ * bit0: interrupt trigger mode (1=level, 0=edge)
+ * bit1: interrupt polarity (1=active high, 0=active low)
+ * bit2: reserved
+ * bit3: address LSB (1=odd,0=even)
+ * bit4: interrupt (1=specified, 0=not specified)
+ * bit5: reserved
+ * bit6/7: register spacing (1,4,2,err)
+ */
+#define SMIPMI_FLAG_IRQLVL (1L << 0)
+#define SMIPMI_FLAG_IRQEN (1L << 3)
+#define SMIPMI_FLAG_ODDOFFSET (1L << 4)
+#define SMIPMI_FLAG_IFSPACING(x) (((x)>>6)&0x3)
+#define IPMI_IOSPACING_BYTE 0
+#define IPMI_IOSPACING_WORD 2
+#define IPMI_IOSPACING_DWORD 1
+
+#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_DATASND 4
+#define IPMI_BTMSG_DATARCV 5
+
+#define IPMI_MSG_NFLN 0
+#define IPMI_MSG_CMD 1
+#define IPMI_MSG_CCODE 2
+#define IPMI_MSG_DATASND 2
+#define IPMI_MSG_DATARCV 3
+
+#define IPMI_SENSOR_TYPE_TEMP 0x0101
+#define IPMI_SENSOR_TYPE_VOLT 0x0102
+#define IPMI_SENSOR_TYPE_FAN 0x0104
+#define IPMI_SENSOR_TYPE_INTRUSION 0x6F05
+#define IPMI_SENSOR_TYPE_PWRSUPPLY 0x6F08
+
+#define IPMI_NAME_UNICODE 0x00
+#define IPMI_NAME_BCDPLUS 0x01
+#define IPMI_NAME_ASCII6BIT 0x02
+#define IPMI_NAME_ASCII8BIT 0x03
+
+#define IPMI_ENTITY_PWRSUPPLY 0x0A
+
+#define IPMI_INVALID_SENSOR (1L << 5)
+
+#define IPMI_SDR_TYPEFULL 1
+#define IPMI_SDR_TYPECOMPACT 2
+
+#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));
+
+long signextend(unsigned long, int);
+
+SLIST_HEAD(ipmi_sensors_head, ipmi_sensor);
+struct ipmi_sensors_head ipmi_sensor_list =
+ SLIST_HEAD_INITIALIZER(&ipmi_sensor_list);
+
+void dumpb(const char *, int, const u_int8_t *);
+
+int read_sensor(struct ipmi_softc *, struct ipmi_sensor *);
+int ipmi_gtredata(struct sysmon_envsys *, struct envsys_tre_data *);
+int ipmi_streinfo(struct sysmon_envsys *, struct envsys_basic_info *);
+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 *);
+void ipmi_delay(struct ipmi_softc *, int);
+
+int ipmi_watchdog_setmode(struct sysmon_wdog *);
+int ipmi_watchdog_tickle(struct sysmon_wdog *);
+
+int ipmi_intr(void *);
+int ipmi_match(struct device *, struct cfdata *, void *);
+void ipmi_attach(struct device *, struct device *, void *);
+
+long ipow(long, int);
+long ipmi_convert(u_int8_t, struct sdrtype1 *, long);
+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 *);
+
+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 ipmi_sensor_type(int, int, int);
+
+void ipmi_smbios_probe(struct smbios_ipmi *, struct ipmi_attach_args *);
+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);
+
+void *scan_sig(long, long, int, int, const void *);
+
+int ipmi_test_threshold(u_int8_t, u_int8_t, u_int8_t, u_int8_t);
+int ipmi_sensor_status(struct ipmi_softc *, struct ipmi_sensor *,
+ struct envsys_tre_data *, u_int8_t *);
+
+int add_child_sensors(struct ipmi_softc *, u_int8_t *, int, int, int,
+ int, int, int, const char *);
+
+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
+ */
+u_int8_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, u_int8_t val)
+{
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh,
+ 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;
+ }
+
+ callout_schedule(&sc->sc_callout, 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)
+{
+ volatile u_int8_t v;
+ int count = 5000000; /* == 5s XXX can be shorter */
+
+ while (count--) {
+ v = bmc_read(sc, offset);
+ if ((v & mask) == 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);
+ return (-1);
+
+}
+
+#define NETFN_LUN(nf,ln) (((nf) << 2) | ((ln) & 0x3))
+
+/*
+ * BT interface
+ */
+#define _BT_CTRL_REG 0
+#define BT_CLR_WR_PTR (1L << 0)
+#define BT_CLR_RD_PTR (1L << 1)
+#define BT_HOST2BMC_ATN (1L << 2)
+#define BT_BMC2HOST_ATN (1L << 3)
+#define BT_EVT_ATN (1L << 4)
+#define BT_HOST_BUSY (1L << 6)
+#define BT_BMC_BUSY (1L << 7)
+
+#define BT_READY (BT_HOST_BUSY|BT_HOST2BMC_ATN|BT_BMC2HOST_ATN)
+
+#define _BT_DATAIN_REG 1
+#define _BT_DATAOUT_REG 1
+
+#define _BT_INTMASK_REG 2
+#define BT_IM_HIRQ_PEND (1L << 1)
+#define BT_IM_SCI_EN (1L << 2)
+#define BT_IM_SMI_EN (1L << 3)
+#define BT_IM_NMI2SMI (1L << 4)
+
+int bt_read(struct ipmi_softc *, int);
+int bt_write(struct ipmi_softc *, int, uint8_t);
+
+int
+bt_read(struct ipmi_softc *sc, int reg)
+{
+ return bmc_read(sc, 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)
+ return (-1);
+
+ bmc_write(sc, reg, data);
+ return (0);
+}
+
+int
+bt_sendmsg(struct ipmi_softc *sc, int len, const u_int8_t *data)
+{
+ int i;
+
+ bt_write(sc, _BT_CTRL_REG, BT_CLR_WR_PTR);
+ for (i = 0; i < len; i++)
+ bt_write(sc, _BT_DATAOUT_REG, data[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)
+ return (-1);
+
+ return (0);
+}
+
+int
+bt_recvmsg(struct ipmi_softc *sc, int maxlen __unused, int *rxlen,
+ u_int8_t *data)
+{
+ u_int8_t len, v, i;
+
+ if (bmc_io_wait(sc, _BT_CTRL_REG, BT_BMC2HOST_ATN, BT_BMC2HOST_ATN,
+ "bt_recvwait") < 0)
+ return (-1);
+
+ bt_write(sc, _BT_CTRL_REG, BT_HOST_BUSY);
+ bt_write(sc, _BT_CTRL_REG, BT_BMC2HOST_ATN);
+ bt_write(sc, _BT_CTRL_REG, BT_CLR_RD_PTR);
+ len = bt_read(sc, _BT_DATAIN_REG);
+ for (i = IPMI_BTMSG_NFLN; i <= len; i++) {
+ v = bt_read(sc, _BT_DATAIN_REG);
+ if (i != IPMI_BTMSG_SEQ)
+ *(data++) = v;
+ }
+ bt_write(sc, _BT_CTRL_REG, BT_HOST_BUSY);
+ *rxlen = len - 1;
+
+ return (0);
+}
+
+int
+bt_reset(struct ipmi_softc *sc __unused)
+{
+ return (-1);
+}
+
+int
+bt_probe(struct ipmi_softc *sc)
+{
+ u_int8_t rv;
+
+ rv = bmc_read(sc, _BT_CTRL_REG);
+ rv &= BT_HOST_BUSY;
+ rv |= BT_CLR_WR_PTR|BT_CLR_RD_PTR|BT_BMC2HOST_ATN|BT_HOST2BMC_ATN;
+ bmc_write(sc, _BT_CTRL_REG, rv);
+
+ rv = bmc_read(sc, _BT_INTMASK_REG);
+ rv &= BT_IM_SCI_EN|BT_IM_SMI_EN|BT_IM_NMI2SMI;
+ rv |= BT_IM_HIRQ_PEND;
+ bmc_write(sc, _BT_INTMASK_REG, rv);
+
+#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 (0);
+}
+
+/*
+ * SMIC interface
+ */
+#define _SMIC_DATAIN_REG 0
+#define _SMIC_DATAOUT_REG 0
+
+#define _SMIC_CTRL_REG 1
+#define SMS_CC_GET_STATUS 0x40
+#define SMS_CC_START_TRANSFER 0x41
+#define SMS_CC_NEXT_TRANSFER 0x42
+#define SMS_CC_END_TRANSFER 0x43
+#define SMS_CC_START_RECEIVE 0x44
+#define SMS_CC_NEXT_RECEIVE 0x45
+#define SMS_CC_END_RECEIVE 0x46
+#define SMS_CC_TRANSFER_ABORT 0x47
+
+#define SMS_SC_READY 0xc0
+#define SMS_SC_WRITE_START 0xc1
+#define SMS_SC_WRITE_NEXT 0xc2
+#define SMS_SC_WRITE_END 0xc3
+#define SMS_SC_READ_START 0xc4
+#define SMS_SC_READ_NEXT 0xc5
+#define SMS_SC_READ_END 0xc6
+
+#define _SMIC_FLAG_REG 2
+#define SMIC_BUSY (1L << 0)
+#define SMIC_SMS_ATN (1L << 2)
+#define SMIC_EVT_ATN (1L << 3)
+#define SMIC_SMI (1L << 4)
+#define SMIC_TX_DATA_RDY (1L << 6)
+#define SMIC_RX_DATA_RDY (1L << 7)
+
+int smic_wait(struct ipmi_softc *, u_int8_t, u_int8_t, const char *);
+int smic_write_cmd_data(struct ipmi_softc *, u_int8_t, const u_int8_t *);
+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 __unused)
+{
+ int v;
+
+ /* Wait for expected flag bits */
+ v = bmc_io_wait(sc, _SMIC_FLAG_REG, mask, val, "smicwait");
+ if (v < 0)
+ return (-1);
+
+ /* Return current status */
+ v = bmc_read(sc, _SMIC_CTRL_REG);
+ dbg_printf(99, "smic_wait = %.2x\n", v);
+ return (v);
+}
+
+int
+smic_write_cmd_data(struct ipmi_softc *sc, u_int8_t cmd, const u_int8_t *data)
+{
+ int sts, v;
+
+ dbg_printf(50, "smic_wcd: %.2x %.2x\n", cmd, data ? *data : -1);
+ 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);
+
+ /* Toggle BUSY bit, then wait for busy bit to clear */
+ 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)
+{
+ int sts;
+
+ sts = smic_wait(sc, SMIC_RX_DATA_RDY | SMIC_BUSY, SMIC_RX_DATA_RDY,
+ "smic_read_data");
+ if (sts >= 0) {
+ *data = bmc_read(sc, _SMIC_DATAIN_REG);
+ dbg_printf(50, "smic_readdata: %.2x\n", *data);
+ }
+ return (sts);
+}
+
+#define ErrStat(a,b) if (a) printf(b);
+
+int
+smic_sendmsg(struct ipmi_softc *sc, int len, const u_int8_t *data)
+{
+ int sts, idx;
+
+ sts = smic_write_cmd_data(sc, SMS_CC_START_TRANSFER, &data[0]);
+ ErrStat(sts != SMS_SC_WRITE_START, "wstart");
+ for (idx = 1; idx < len - 1; idx++) {
+ sts = smic_write_cmd_data(sc, SMS_CC_NEXT_TRANSFER,
+ &data[idx]);
+ ErrStat(sts != SMS_SC_WRITE_NEXT, "write");
+ }
+ sts = smic_write_cmd_data(sc, SMS_CC_END_TRANSFER, &data[idx]);
+ if (sts != SMS_SC_WRITE_END) {
+ dbg_printf(50, "smic_sendmsg %d/%d = %.2x\n", idx, len, sts);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+smic_recvmsg(struct ipmi_softc *sc, int maxlen, int *len, u_int8_t *data)
+{
+ int sts, idx;
+
+ *len = 0;
+ sts = smic_wait(sc, SMIC_RX_DATA_RDY, SMIC_RX_DATA_RDY, "smic_recvmsg");
+ if (sts < 0)
+ return (-1);
+
+ sts = smic_write_cmd_data(sc, SMS_CC_START_RECEIVE, NULL);
+ ErrStat(sts != SMS_SC_READ_START, "rstart");
+ for (idx = 0;; ) {
+ sts = smic_read_data(sc, &data[idx++]);
+ if (sts != SMS_SC_READ_START && sts != SMS_SC_READ_NEXT)
+ break;
+ smic_write_cmd_data(sc, SMS_CC_NEXT_RECEIVE, NULL);
+ }
+ ErrStat(sts != SMS_SC_READ_END, "rend");
+
+ *len = idx;
+
+ sts = smic_write_cmd_data(sc, SMS_CC_END_RECEIVE, NULL);
+ if (sts != SMS_SC_READY) {
+ dbg_printf(50, "smic_recvmsg %d/%d = %.2x\n", idx, maxlen, sts);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+smic_reset(struct ipmi_softc *sc __unused)
+{
+ return (-1);
+}
+
+int
+smic_probe(struct ipmi_softc *sc)
+{
+ /* Flag register should not be 0xFF on a good system */
+ if (bmc_read(sc, _SMIC_FLAG_REG) == 0xFF)
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * 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 (1L << 0)
+#define KCS_IBF (1L << 1)
+#define KCS_SMS_ATN (1L << 2)
+#define KCS_CD (1L << 3)
+#define KCS_OEM1 (1L << 4)
+#define KCS_OEM2 (1L << 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, lbl);
+ if (v < 0)
+ return (v);
+
+ /* Check if output buffer full, read dummy byte */
+ if ((v & (KCS_OBF | KCS_STATE_MASK)) == (KCS_OBF | KCS_WRITE_STATE))
+ 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)
+ ;
+ aprint_error("%s: error code: %x\n", DEVNAME(sc),
+ 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(50, "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(50, "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(50, "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) {
+ dbg_printf(1, "kcs sendmsg = %d/%d <%.2x>\n", idx, len, sts);
+ dbg_dump(1, "kcs_sendmsg", len, data);
+ return (-1);
+ }
+
+ return (0);
+}
+
+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) {
+ dbg_printf(1, "kcs read = %d/%d <%.2x>\n", idx, maxlen, sts);
+ return (-1);
+ }
+
+ dbg_dump(50, "kcs recvmsg", idx, data);
+
+ return (0);
+}
+
+int
+kcs_reset(struct ipmi_softc *sc __unused)
+{
+ 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
+
+struct ipmi_request {
+ 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;
+};
+
+struct ipmi_response {
+ u_int8_t cCode;
+ u_int8_t data_len;
+ u_int8_t *data;
+};
+
+struct ipmi_bmc_request {
+ u_int8_t bmc_nfLn;
+ u_int8_t bmc_cmd;
+ u_int8_t bmc_data_len;
+ u_int8_t bmc_data[1];
+};
+
+struct ipmi_bmc_response {
+ 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];
+};
+
+
+CFATTACH_DECL(ipmi, sizeof(struct ipmi_softc),
+ ipmi_match, ipmi_attach, NULL, NULL);
+
+/* 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);
+}
+
+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
+ipmi_smbios_probe(struct smbios_ipmi *pipmi, struct ipmi_attach_args *ia)
+{
+
+ dbg_printf(1, "ipmi_smbios_probe: %02x %02x %02x %02x %08llx %02x "
+ "%02x\n",
+ pipmi->smipmi_if_type,
+ pipmi->smipmi_if_rev,
+ pipmi->smipmi_i2c_address,
+ pipmi->smipmi_nvram_address,
+ pipmi->smipmi_base_address,
+ pipmi->smipmi_base_flags,
+ pipmi->smipmi_irq);
+
+ ia->iaa_if_type = pipmi->smipmi_if_type;
+ ia->iaa_if_rev = pipmi->smipmi_if_rev;
+ ia->iaa_if_irq = (pipmi->smipmi_base_flags & SMIPMI_FLAG_IRQEN) ?
+ pipmi->smipmi_irq : -1;
+ ia->iaa_if_irqlvl = (pipmi->smipmi_base_flags & SMIPMI_FLAG_IRQLVL) ?
+ IST_LEVEL : IST_EDGE;
+
+ switch (SMIPMI_FLAG_IFSPACING(pipmi->smipmi_base_flags)) {
+ case IPMI_IOSPACING_BYTE:
+ ia->iaa_if_iospacing = 1;
+ break;
+
+ case IPMI_IOSPACING_DWORD:
+ ia->iaa_if_iospacing = 4;
+ break;
+
+ case IPMI_IOSPACING_WORD:
+ ia->iaa_if_iospacing = 2;
+ break;
+
+ default:
+ ia->iaa_if_iospacing = 1;
+ aprint_error("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 & SMIPMI_FLAG_ODDOFFSET)
+ ia->iaa_if_iobase++;
+
+ if (pipmi->smipmi_base_flags == 0x7f) {
+ /* IBM 325 eServer workaround */
+ ia->iaa_if_iospacing = 1;
+ ia->iaa_if_iobase = pipmi->smipmi_base_address;
+ ia->iaa_if_iotype = 'i';
+ return;
+ }
+}
+
+/*
+ * 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 nfLun, 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_NOWAIT|M_CANFAIL);
+ if (buf == NULL)
+ return (NULL);
+
+ buf[IPMI_BTMSG_LEN] = len + 3;
+ buf[IPMI_BTMSG_NFLN] = nfLun;
+ buf[IPMI_BTMSG_SEQ] = sc->sc_btseq++;
+ buf[IPMI_BTMSG_CMD] = cmd;
+ if (len && data)
+ memcpy(buf + IPMI_BTMSG_DATASND, 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 __unused, 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_NOWAIT|M_CANFAIL);
+ if (buf == NULL)
+ return (NULL);
+
+ buf[IPMI_MSG_NFLN] = nfLun;
+ buf[IPMI_MSG_CMD] = cmd;
+ if (len && data)
+ memcpy(buf + IPMI_MSG_DATASND, 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 = -1;
+
+ dbg_printf(50, "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
+ goto done;
+ } else
+ buf = sc->sc_if->buildmsg(sc, NETFN_LUN(netfn, rslun), cmd,
+ txlen, data, &txlen);
+
+ if (buf == NULL) {
+ printf("%s: sendcmd malloc fails\n", DEVNAME(sc));
+ goto done;
+ }
+ rc = sc->sc_if->sendmsg(sc, txlen, buf);
+ free(buf, M_DEVBUF);
+
+ ipmi_delay(sc, 5); /* give bmc chance to digest command */
+
+done:
+ 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_NOWAIT|M_CANFAIL);
+ if (buf == NULL) {
+ printf("%s: ipmi_recvcmd: malloc fails\n", DEVNAME(sc));
+ return (-1);
+ }
+ /* Receive message from interface, copy out result data */
+ if (sc->sc_if->recvmsg(sc, maxlen + 3, &rawlen, buf))
+ return (-1);
+
+ *rxlen = rawlen - IPMI_MSG_DATARCV;
+ if (*rxlen > 0 && data)
+ memcpy(data, buf + IPMI_MSG_DATARCV, *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(50, "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);
+}
+
+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);
+}
+
+/* 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("%s: sendcmd fails\n", DEVNAME(sc));
+ return (-1);
+ }
+ if (ipmi_recvcmd(sc, 8 + length, &len, cmd)) {
+ printf("%s: getSdrPartial: recvcmd fails\n", DEVNAME(sc));
+ 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 = 0;
+ int len, sdrlen, offset;
+ u_int8_t *psdr;
+ struct sdrhdr shdr;
+
+ /* Reserve SDR */
+ if (ipmi_sendcmd(sc, BMC_SA, 0, STORAGE_NETFN, STORAGE_RESERVE_SDR,
+ 0, NULL)) {
+ printf("%s: reserve send fails\n", DEVNAME(sc));
+ return (-1);
+ }
+ if (ipmi_recvcmd(sc, sizeof(resid), &len, &resid)) {
+ printf("%s: reserve recv fails\n", DEVNAME(sc));
+ return (-1);
+ }
+ /* Get SDR Header */
+ if (get_sdr_partial(sc, recid, resid, 0, sizeof shdr, &shdr, nxtrec)) {
+ printf("%s: get header fails\n", DEVNAME(sc));
+ 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_NOWAIT|M_CANFAIL);
+ 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("%s: get chunk : %d,%d fails\n", DEVNAME(sc),
+ 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 >> 6) {
+ case IPMI_NAME_UNICODE:
+ //unicode
+ break;
+
+ case IPMI_NAME_BCDPLUS:
+ /* Characters are encoded in 4-bit BCDPLUS */
+ if (len < slen * 2 + 1)
+ slen = (len >> 1) - 1;
+ for (i = 0; i < slen; i++) {
+ *(name++) = bcdplus[bits[i] >> 4];
+ *(name++) = bcdplus[bits[i] & 0xF];
+ }
+ break;
+
+ case IPMI_NAME_ASCII6BIT:
+ /* Characters are encoded in 6-bit ASCII
+ * 0x00 - 0x3F maps to 0x20 - 0x5F */
+ /* XXX: need to calculate max len: slen = 3/4 * len */
+ if (len < slen + 1)
+ slen = len - 1;
+ for (i = 0; i < slen * 8; i += 6)
+ *(name++) = getbits(bits, i, 6) + ' ';
+ break;
+
+ case IPMI_NAME_ASCII8BIT:
+ /* Characters are 8-bit ascii */
+ if (len < slen + 1)
+ slen = len - 1;
+ 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);
+}
+
+/* Sign extend a n-bit value */
+long
+signextend(unsigned long val, int bits)
+{
+ long msk = (1L << (bits-1))-1;
+
+ return (-(val & ~msk) | val);
+}
+
+/* Convert IPMI reading from sensor factors */
+long
+ipmi_convert(u_int8_t v, struct sdrtype1 *s1, long adj)
+{
+ short M, B;
+ char K1, K2;
+ long val;
+
+ /* Calculate linear reading variables */
+ M = signextend((((short)(s1->m_tolerance & 0xC0)) << 2) + s1->m, 10);
+ B = signextend((((short)(s1->b_accuracy & 0xC0)) << 2) + s1->b, 10);
+ K1 = signextend(s1->rbexp & 0xF, 4);
+ K2 = signextend(s1->rbexp >> 4, 4);
+
+ /* 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
+ipmi_test_threshold(u_int8_t v, u_int8_t valid, u_int8_t hi, u_int8_t lo)
+{
+ dbg_printf(10, "thresh: %.2x %.2x %.2x %d\n", v, lo, hi,valid);
+ return ((valid & 1 && lo != 0x00 && v <= lo) ||
+ (valid & 8 && hi != 0xFF && v >= hi));
+}
+
+int
+ipmi_sensor_status(struct ipmi_softc *sc, struct ipmi_sensor *psensor,
+ struct envsys_tre_data *sdata, u_int8_t *reading)
+{
+ u_int8_t data[32];
+ struct sdrtype1 *s1 = (struct sdrtype1 *)psensor->i_sdr;
+ int rxlen, etype;
+ /* Get reading of sensor */
+ switch (sdata->units) {
+ case ENVSYS_STEMP:
+ sdata->cur.data_s = ipmi_convert(reading[0], s1, 6);
+ sdata->cur.data_s += 273150000;
+ break;
+
+ case ENVSYS_SVOLTS_DC:
+ sdata->cur.data_us = ipmi_convert(reading[0], s1, 6);
+ break;
+
+ case ENVSYS_SFANRPM:
+ sdata->cur.data_us = ipmi_convert(reading[0], s1, 0);
+ if (((s1->units1>>3)&0x7) == 0x3)
+ sdata->cur.data_us *= 60; /* RPS -> RPM */
+ break;
+ default:
+ break;
+ }
+
+ /* Return Sensor Status */
+ etype = (psensor->i_etype << 8) + psensor->i_stype;
+ switch (etype) {
+ case IPMI_SENSOR_TYPE_TEMP:
+ case IPMI_SENSOR_TYPE_VOLT:
+ case IPMI_SENSOR_TYPE_FAN:
+ data[0] = psensor->i_num;
+ if (ipmi_sendcmd(sc, s1->owner_id, s1->owner_lun,
+ SE_NETFN, SE_GET_SENSOR_THRESHOLD, 1, data) ||
+ ipmi_recvcmd(sc, sizeof(data), &rxlen, data))
+ return (ENVSYS_WARN_OK);
+
+ dbg_printf(25, "recvdata: %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n",
+ data[0], data[1], data[2], data[3], data[4], data[5],
+ data[6]);
+
+ if (ipmi_test_threshold(*reading, data[0] >> 2 ,
+ data[6], data[3]))
+ return (ENVSYS_WARN_CRITOVER);
+
+ if (ipmi_test_threshold(*reading, data[0] >> 1,
+ data[5], data[2]))
+ return (ENVSYS_WARN_CRITOVER);
+
+ if (ipmi_test_threshold(*reading, data[0] ,
+ data[4], data[1]))
+ return (ENVSYS_WARN_OVER);
+
+ break;
+
+ case IPMI_SENSOR_TYPE_INTRUSION:
+ sdata->cur.data_s = (reading[2] & 1) ? 0 : 1;
+ if (reading[2] & 0x1)
+ return (ENVSYS_WARN_CRITOVER);
+ break;
+
+ case IPMI_SENSOR_TYPE_PWRSUPPLY:
+ /* Reading: 1 = present+powered, 0 = otherwise */
+ sdata->cur.data_s = (reading[2] & 1) ? 0 : 1;
+ if (reading[2] & 0x10) {
+ /* XXX: Need envsys type for Power Supply types
+ * ok: power supply installed && powered
+ * warn: power supply installed && !powered
+ * crit: power supply !installed
+ */
+ return (ENVSYS_WARN_CRITOVER);
+ }
+ if (reading[2] & 0x08) {
+ /* Power supply AC lost */
+ return (ENVSYS_WARN_OVER);
+ }
+ break;
+ }
+
+ return (ENVSYS_WARN_OK);
+}
+
+int
+read_sensor(struct ipmi_softc *sc, struct ipmi_sensor *psensor)
+{
+ struct sdrtype1 *s1 = (struct sdrtype1 *) psensor->i_sdr;
+ u_int8_t data[8];
+ int rxlen, rv = -1;
+ struct envsys_tre_data *sdata = &sc->sc_sensor_data[psensor->i_envnum];
+
+ if (!cold) {
+ rv = lockmgr(&sc->sc_lock, LK_EXCLUSIVE, NULL);
+ if (rv)
+ return rv;
+ }
+
+ 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))
+ goto done;
+
+ if (ipmi_recvcmd(sc, sizeof(data), &rxlen, data))
+ goto done;
+ dbg_printf(10, "values=%.2x %.2x %.2x %.2x %s\n",
+ data[0],data[1],data[2],data[3],
+ sc->sc_sensor_info[psensor->i_envnum].desc);
+ if (data[1] & IPMI_INVALID_SENSOR) {
+ /* Check if sensor is valid */
+ sdata->validflags &= ~ENVSYS_FCURVALID;
+ } else {
+ sdata->validflags |= ENVSYS_FCURVALID;
+ }
+ sdata->warnflags = ipmi_sensor_status(sc, psensor, sdata, data);
+ rv = 0;
+done:
+ if (!cold)
+ (void) lockmgr(&sc->sc_lock, LK_RELEASE, NULL);
+ return (rv);
+}
+
+int
+ipmi_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred)
+{
+ struct ipmi_softc *sc = sme->sme_cookie;
+ int rv;
+
+ rv = lockmgr(&sc->sc_lock, LK_EXCLUSIVE, NULL);
+ if (rv)
+ return rv;
+ *tred = sc->sc_sensor_data[tred->sensor];
+ (void) lockmgr(&sc->sc_lock, LK_RELEASE, NULL);
+ return 0;
+}
+
+int
+ipmi_streinfo(struct sysmon_envsys *sme __unused,
+ struct envsys_basic_info *binfo)
+{
+ /* XXX Not implemented */
+ binfo->validflags = 0;
+
+ return (0);
+}
+
+int
+ipmi_sensor_type(int type, int ext_type, int entity)
+{
+ switch (ext_type << 8L | type) {
+ case IPMI_SENSOR_TYPE_TEMP:
+ return (ENVSYS_STEMP);
+
+ case IPMI_SENSOR_TYPE_VOLT:
+ return (ENVSYS_SVOLTS_DC);
+
+ case IPMI_SENSOR_TYPE_FAN:
+ return (ENVSYS_SFANRPM);
+
+ case IPMI_SENSOR_TYPE_PWRSUPPLY:
+ if (entity == IPMI_ENTITY_PWRSUPPLY)
+ return (ENVSYS_INDICATOR);
+ break;
+
+ case IPMI_SENSOR_TYPE_INTRUSION:
+ return (ENVSYS_INDICATOR);
+ }
+
+ return (-1);
+}
+
+/* Add Sensor to BSD Sysctl interface */
+int
+add_sdr_sensor(struct ipmi_softc *sc, u_int8_t *psdr)
+{
+ int rc;
+ struct sdrtype1 *s1 = (struct sdrtype1 *)psdr;
+ struct sdrtype2 *s2 = (struct sdrtype2 *)psdr;
+ char name[64];
+
+ switch (s1->sdrhdr.record_type) {
+ case IPMI_SDR_TYPEFULL:
+ ipmi_sensor_name(name, sizeof(name), s1->typelen, s1->name);
+ rc = add_child_sensors(sc, psdr, 1, s1->sensor_num,
+ s1->sensor_type, s1->event_code, 0, s1->entity_id, name);
+ break;
+
+ case IPMI_SDR_TYPECOMPACT:
+ ipmi_sensor_name(name, sizeof(name), s2->typelen, s2->name);
+ rc = add_child_sensors(sc, psdr, s2->share1 & 0xF,
+ s2->sensor_num, s2->sensor_type, s2->event_code,
+ s2->share2 & 0x7F, s2->entity_id, name);
+ break;
+
+ default:
+ return (0);
+ }
+
+ return rc;
+}
+
+int
+add_child_sensors(struct ipmi_softc *sc, u_int8_t *psdr, int count,
+ int sensor_num, int sensor_type, int ext_type, int sensor_base,
+ int entity, const char *name)
+{
+ int typ, idx;
+ struct ipmi_sensor *psensor;
+ struct sdrtype1 *s1 = (struct sdrtype1 *)psdr;
+
+ typ = ipmi_sensor_type(sensor_type, ext_type, entity);
+ if (typ == -1) {
+ dbg_printf(5, "Unknown sensor type:%.2x et:%.2x sn:%.2x "
+ "name:%s\n", sensor_type, ext_type, sensor_num, name);
+ return 0;
+ }
+ sc->sc_nsensors += count;
+ sc->sc_nsensors_typ[typ] += count;
+ for (idx = 0; idx < count; idx++) {
+ psensor = malloc(sizeof(struct ipmi_sensor), M_DEVBUF,
+ M_NOWAIT|M_CANFAIL);
+ if (psensor == NULL)
+ break;
+
+ memset(psensor, 0, sizeof(struct ipmi_sensor));
+
+ /* Initialize BSD Sensor info */
+ psensor->i_sdr = psdr;
+ psensor->i_num = sensor_num + idx;
+ psensor->i_stype = sensor_type;
+ psensor->i_etype = ext_type;
+ psensor->i_envtype = typ;
+ if (count > 1)
+ snprintf(psensor->i_envdesc,
+ sizeof(psensor->i_envdesc),
+ "%s - %d", name, sensor_base + idx);
+ else
+ strlcpy(psensor->i_envdesc, name,
+ sizeof(psensor->i_envdesc));
+
+ dbg_printf(5, "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_envdesc);
+ SLIST_INSERT_HEAD(&ipmi_sensor_list, psensor, i_list);
+ }
+
+ 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)
+{
+
+ if (!ipmi_poll)
+ return;
+
+ if (SLIST_EMPTY(&ipmi_sensor_list))
+ return;
+
+ sc->current_sensor = SLIST_NEXT(sc->current_sensor, i_list);
+ if (sc->current_sensor == NULL)
+ sc->current_sensor = SLIST_FIRST(&ipmi_sensor_list);
+
+ if (read_sensor(sc, sc->current_sensor)) {
+ dbg_printf(1, "%s: error reading\n", DEVNAME(sc));
+ }
+}
+
+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;
+ if (bus_space_map(sc->sc_iot, ia->iaa_if_iobase,
+ sc->sc_if->nregs * sc->sc_if_iospacing,
+ 0, &sc->sc_ioh)) {
+ printf("%s: bus_space_map(%x %x %x 0 %p) failed\n",
+ DEVNAME(sc),
+ sc->sc_iot, ia->iaa_if_iobase,
+ 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);
+}
+
+void
+ipmi_unmap_regs(struct ipmi_softc *sc, struct ipmi_attach_args *ia __unused)
+{
+ bus_space_unmap(sc->sc_iot, sc->sc_ioh,
+ sc->sc_if->nregs * sc->sc_if_iospacing);
+}
+
+void
+ipmi_poll_thread(void *arg)
+{
+ struct ipmi_softc *sc = arg;
+
+ while (sc->sc_thread_running) {
+ ipmi_refresh_sensors(sc);
+ tsleep((void *)&sc->sc_thread_running, PWAIT, "ipmi_poll",
+ SENSOR_REFRESH_RATE);
+ }
+ kthread_exit(0);
+}
+
+void
+ipmi_create_thread(void *arg)
+{
+ struct ipmi_softc *sc = arg;
+
+ if (kthread_create1(ipmi_poll_thread, sc, &sc->sc_kthread,
+ DEVNAME(sc)) != 0) {
+ printf("%s: unable to create polling thread, ipmi disabled\n",
+ DEVNAME(sc));
+ return;
+ }
+}
+
+int
+ipmi_probe(struct ipmi_attach_args *ia)
+{
+ struct dmd_ipmi *pipmi;
+ struct smbtable tbl;
+
+ tbl.cookie = 0;
+
+ if (smbios_find_table(SMBIOS_TYPE_IPMIDEV, &tbl))
+ ipmi_smbios_probe(tbl.tblhdr, ia);
+ else {
+ pipmi = (struct dmd_ipmi *)scan_sig(0xC0000L, 0xFFFFFL, 16, 4,
+ "IPMI");
+ /* XXX hack to find Dell PowerEdge 8450 */
+ if (pipmi == NULL) {
+ /* no IPMI found */
+ return (0);
+ }
+
+ /* we have an IPMI signature, fill in attach arg structure */
+ ia->iaa_if_type = pipmi->dmd_if_type;
+ ia->iaa_if_rev = pipmi->dmd_if_rev;
+ }
+
+ return (1);
+}
+
+int
+ipmi_match(struct device *parent __unused, struct cfdata *cf __unused,
+ void *aux)
+{
+ struct ipmi_softc sc;
+ struct ipmi_attach_args *ia = aux;
+ u_int8_t cmd[32];
+ int len;
+ int rv = 0;
+
+ /* Map registers */
+ if (ipmi_map_regs(&sc, ia) == 0) {
+ sc.sc_if->probe(&sc);
+
+ /* Identify BMC device early to detect lying bios */
+ if (ipmi_sendcmd(&sc, BMC_SA, 0, APP_NETFN, APP_GET_DEVICE_ID,
+ 0, NULL)) {
+ dbg_printf(1, ": unable to send get device id "
+ "command\n");
+ goto unmap;
+ }
+ if (ipmi_recvcmd(&sc, sizeof(cmd), &len, cmd)) {
+ dbg_printf(1, ": unable to retrieve device id\n");
+ goto unmap;
+ }
+
+ dbg_dump(1, "bmc data", len, cmd);
+unmap:
+ rv = 1; /* GETID worked, we got IPMI */
+ ipmi_unmap_regs(&sc, ia);
+ }
+
+ return (rv);
+}
+
+void
+ipmi_attach(struct device *parent __unused, struct device *self, void *aux)
+{
+ struct ipmi_softc *sc = (void *) self;
+ struct ipmi_attach_args *ia = aux;
+ u_int16_t rec;
+ struct ipmi_sensor *ipmi_s;
+ int i;
+ int current_index_typ[ENVSYS_NSENSORS];
+ int nrange, currange;
+ struct envsys_range *env_ranges;
+
+
+ sc->sc_thread_running = 1;
+
+ /* Map registers */
+ ipmi_map_regs(sc, ia);
+
+ /* Scan SDRs, add sensors to list */
+ for (rec = 0; rec != 0xFFFF;)
+ if (get_sdr(sc, rec, &rec))
+ break;
+
+ /* fill in sensor infos: */
+ /* get indexes for each unit, and number of units */
+ current_index_typ[0] = 0;
+ nrange = sc->sc_nsensors_typ[0] ? 1 : 0;
+ for (i = 1; i < ENVSYS_NSENSORS; i++) {
+ current_index_typ[i] =
+ current_index_typ[i-1] + sc->sc_nsensors_typ[i - 1];
+ if (sc->sc_nsensors_typ[i])
+ nrange += 1;
+ }
+ /* allocate range array, and fill in */
+ env_ranges = malloc(sizeof(struct envsys_range) * (nrange + 1),
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (env_ranges == NULL) {
+ aprint_error("%s: can't allocate envsys_range\n", DEVNAME(sc));
+ return;
+ }
+ currange = 0;
+ for (i = 0 ; i < ENVSYS_NSENSORS; i++) {
+ if (sc->sc_nsensors_typ[i]) {
+ env_ranges[currange].low = current_index_typ[i];
+ env_ranges[currange].high =
+ current_index_typ[i] + sc->sc_nsensors_typ[i];
+ env_ranges[currange].units = i;
+ currange++;
+ }
+ }
+ env_ranges[currange].low = 1;
+ env_ranges[currange].high = 0; /* end */
+ KASSERT(currange == nrange);
+ /* allocate and fill sensor arrays */
+ sc->sc_sensor_data =
+ malloc(sizeof(struct envsys_tre_data) * sc->sc_nsensors,
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (sc->sc_sensor_data == NULL) {
+ aprint_error("%s: can't allocate envsys_tre_data\n",
+ DEVNAME(sc));
+ return;
+ }
+ sc->sc_sensor_info =
+ malloc(sizeof(struct envsys_basic_info) * sc->sc_nsensors,
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (sc->sc_sensor_info == NULL) {
+ aprint_error("%s: can't allocate envsys_basic_info\n",
+ DEVNAME(sc));
+ return;
+ }
+ SLIST_FOREACH(ipmi_s, &ipmi_sensor_list, i_list) {
+ i = current_index_typ[ipmi_s->i_envtype];
+ current_index_typ[ipmi_s->i_envtype]++;
+ ipmi_s->i_envnum = i;
+ sc->sc_sensor_data[i].sensor = i;
+ sc->sc_sensor_data[i].units = ipmi_s->i_envtype;
+ /* ENVSYS_FCURVALID set by read_sensor() */
+ sc->sc_sensor_data[i].validflags = ENVSYS_FVALID;
+ sc->sc_sensor_data[i].warnflags = ENVSYS_WARN_OK;
+ sc->sc_sensor_info[i].sensor = i;
+ sc->sc_sensor_info[i].units = ipmi_s->i_envtype;
+ sc->sc_sensor_info[i].validflags = ENVSYS_FVALID;
+ strcpy(sc->sc_sensor_info[i].desc, ipmi_s->i_envdesc);
+ }
+ sc->sc_ranges = env_ranges;
+ sc->sc_envsys.sme_cookie = sc;
+ sc->sc_envsys.sme_gtredata = ipmi_gtredata;
+ sc->sc_envsys.sme_streinfo = ipmi_streinfo;
+ sc->sc_envsys.sme_nsensors = sc->sc_nsensors;
+ sc->sc_envsys.sme_envsys_version = 1000;
+
+ if (sysmon_envsys_register(&sc->sc_envsys))
+ printf("%s: unable to register with sysmon\n", DEVNAME(sc));
+
+ /* initialize sensor list for thread */
+ if (!SLIST_EMPTY(&ipmi_sensor_list))
+ sc->current_sensor = SLIST_FIRST(&ipmi_sensor_list);
+
+ /* Setup threads */
+ kthread_create(ipmi_create_thread, sc);
+
+ aprint_normal(": version %d.%d interface %s %sbase 0x%x/%x spacing %d",
+ ia->iaa_if_rev >> 4, ia->iaa_if_rev & 0xF, sc->sc_if->name,
+ ia->iaa_if_iotype == 'i' ? "io" : "mem", ia->iaa_if_iobase,
+ ia->iaa_if_iospacing * sc->sc_if->nregs, ia->iaa_if_iospacing);
+ if (ia->iaa_if_irq != -1)
+ aprint_normal(" irq %d", ia->iaa_if_irq);
+ aprint_normal("\n");
+
+ /* setup flag to exclude iic */
+ ipmi_enabled = 1;
+
+ /* Setup Watchdog timer */
+ sc->sc_wdog.smw_name = DEVNAME(sc);
+ sc->sc_wdog.smw_cookie = sc;
+ sc->sc_wdog.smw_setmode = ipmi_watchdog_setmode;
+ sc->sc_wdog.smw_tickle = ipmi_watchdog_tickle;
+ sysmon_wdog_register(&sc->sc_wdog);
+
+ /* lock around read_sensor so that no one messes with the bmc regs */
+ lockinit(&sc->sc_lock, PRIBIO, "ipmilk", 0, 0);
+
+ /* setup ticker */
+ sc->sc_retries = 0;
+ sc->sc_wakeup = 0;
+ sc->sc_max_retries = 50; /* 50 * 1/100 = 0.5 seconds max */
+ callout_init(&sc->sc_callout);
+ callout_setfunc(&sc->sc_callout, _bmc_io_wait, sc);
+}
+
+int
+ipmi_watchdog_setmode(struct sysmon_wdog *smwdog)
+{
+ struct ipmi_softc *sc = smwdog->smw_cookie;
+ struct ipmi_get_watchdog gwdog;
+ struct ipmi_set_watchdog swdog;
+ int s, rc, len;
+
+ if (smwdog->smw_period < 10)
+ return EINVAL;
+ if (smwdog->smw_period == WDOG_PERIOD_DEFAULT)
+ sc->sc_wdog.smw_period = 10;
+ else
+ sc->sc_wdog.smw_period = smwdog->smw_period;
+
+ s = splsoftclock();
+ /* see if we can properly task to the watchdog */
+ rc = ipmi_sendcmd(sc, BMC_SA, BMC_LUN, APP_NETFN,
+ APP_GET_WATCHDOG_TIMER, 0, NULL);
+ rc = ipmi_recvcmd(sc, sizeof(gwdog), &len, &gwdog);
+ if (rc) {
+ printf("%s: APP_GET_WATCHDOG_TIMER returned 0x%x\n",
+ DEVNAME(sc), rc);
+ splx(s);
+ return EIO;
+ }
+
+ memset(&swdog, 0, sizeof(swdog));
+ /* Period is 10ths/sec */
+ swdog.wdog_timeout = htole32(sc->sc_wdog.smw_period * 10);
+ swdog.wdog_action = 0;
+ if ((smwdog->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
+ swdog.wdog_action |= IPMI_WDOG_ACT_DISABLED;
+ } else {
+ swdog.wdog_action |= IPMI_WDOG_ACT_RESET;
+ }
+ swdog.wdog_use = IPMI_WDOG_USE_USE_OS;
+
+ rc = ipmi_sendcmd(sc, BMC_SA, BMC_LUN, APP_NETFN,
+ APP_SET_WATCHDOG_TIMER, sizeof(swdog), &swdog);
+ rc = ipmi_recvcmd(sc, 0, &len, NULL);
+ splx(s);
+ if (rc) {
+ printf("%s: APP_SET_WATCHDOG_TIMER returned 0x%x\n",
+ DEVNAME(sc), rc);
+ return EIO;
+ }
+
+ return (0);
+}
+
+int
+ipmi_watchdog_tickle(struct sysmon_wdog *smwdog)
+{
+ struct ipmi_softc *sc = smwdog->smw_cookie;
+ int s, rc, len;
+
+ s = splsoftclock();
+ /* tickle the watchdog */
+ rc = ipmi_sendcmd(sc, BMC_SA, BMC_LUN, APP_NETFN,
+ APP_RESET_WATCHDOG, 0, NULL);
+ rc = ipmi_recvcmd(sc, 0, &len, NULL);
+ splx(s);
+ if (rc) {
+ printf("%s: watchdog tickle returned 0x%x\n", DEVNAME(sc), rc);
+ return EIO;
+ }
+ return (0);
+}
>Unformatted: