Page MenuHomeFreeBSD

D40421.diff
No OneTemporary

D40421.diff

diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64
--- a/sys/conf/files.arm64
+++ b/sys/conf/files.arm64
@@ -338,6 +338,7 @@
dev/ipmi/ipmi.c optional ipmi
dev/ipmi/ipmi_acpi.c optional ipmi acpi
+dev/ipmi/ipmi_bt.c optional ipmi
dev/ipmi/ipmi_kcs.c optional ipmi
dev/ipmi/ipmi_smic.c optional ipmi
diff --git a/sys/conf/files.x86 b/sys/conf/files.x86
--- a/sys/conf/files.x86
+++ b/sys/conf/files.x86
@@ -153,6 +153,7 @@
dev/ipmi/ipmi.c optional ipmi
dev/ipmi/ipmi_acpi.c optional ipmi acpi
dev/ipmi/ipmi_isa.c optional ipmi isa
+dev/ipmi/ipmi_bt.c optional ipmi
dev/ipmi/ipmi_kcs.c optional ipmi
dev/ipmi/ipmi_smic.c optional ipmi
dev/ipmi/ipmi_smbus.c optional ipmi smbus
diff --git a/sys/dev/ipmi/ipmi_acpi.c b/sys/dev/ipmi/ipmi_acpi.c
--- a/sys/dev/ipmi/ipmi_acpi.c
+++ b/sys/dev/ipmi/ipmi_acpi.c
@@ -94,16 +94,17 @@
switch (interface_type) {
case KCS_MODE:
- count = 2;
+ count = IPMI_IF_KCS_NRES;
mode = "KCS";
break;
case SMIC_MODE:
- count = 3;
+ count = IPMI_IF_SMIC_NRES;
mode = "SMIC";
break;
case BT_MODE:
- device_printf(dev, "BT interface not supported\n");
- return (ENXIO);
+ count = IPMI_IF_BT_NRES;
+ mode = "BT";
+ break;
case SSIF_MODE:
device_printf(dev, "SSIF interface not supported on ACPI\n");
return (0);
@@ -170,18 +171,20 @@
* We assume an alignment of 1 byte as currently the IPMI spec
* doesn't provide any way to determine the alignment via ACPI.
*/
+ error = ENXIO;
switch (interface_type) {
case KCS_MODE:
error = ipmi_kcs_attach(sc);
- if (error)
- goto bad;
break;
case SMIC_MODE:
error = ipmi_smic_attach(sc);
- if (error)
- goto bad;
+ break;
+ case BT_MODE:
+ error = ipmi_bt_attach(sc);
break;
}
+ if (error)
+ goto bad;
error = ipmi_attach(dev);
if (error)
goto bad;
diff --git a/sys/dev/ipmi/ipmi_bt.c b/sys/dev/ipmi/ipmi_bt.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/ipmi/ipmi_bt.c
@@ -0,0 +1,296 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Yandex LLC
+ * Copyright (c) 2023 Andrey V. Elsukov <ae@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/condvar.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/selinfo.h>
+#include <machine/bus.h>
+
+#include <sys/ipmi.h>
+#include <dev/ipmi/ipmivars.h>
+
+/*
+ * BT interface
+ */
+
+#define DMSG0(sc, fmt, ...) do { \
+ device_printf((sc)->ipmi_dev, "BT: %s: " fmt "\n", \
+ __func__, ## __VA_ARGS__); \
+} while (0)
+
+#define DMSGV(...) if (bootverbose) { \
+ DMSG0(__VA_ARGS__); \
+}
+
+#ifdef IPMI_BT_DEBUG
+#define DMSG(...) DMSG0(__VA_ARGS__)
+#else
+#define DMSG(...)
+#endif
+
+#define BT_IO_BASE 0xe4
+
+#define BT_CTRL_REG 0
+#define BT_C_CLR_WR_PTR (1L << 0)
+#define BT_C_CLR_RD_PTR (1L << 1)
+#define BT_C_H2B_ATN (1L << 2)
+#define BT_C_B2H_ATN (1L << 3)
+#define BT_C_SMS_ATN (1L << 4)
+#define BT_C_OEM0 (1L << 5)
+#define BT_C_H_BUSY (1L << 6)
+#define BT_C_B_BUSY (1L << 7)
+
+#define BT_CTRL_BITS "\20\01CLR_WR_PTR\02CLR_RD_PTR\03H2B_ATN\04B2H_ATN"\
+ "\05SMS_ATN\06OEM0\07H_BUSY\010B_BUSY"
+
+#define BT_DATA_REG 1
+#define BTMSG_REQLEN 3
+#define BTMSG_REPLEN 4
+
+#define BT_INTMASK_REG 2
+#define BT_IM_B2H_IRQ_EN (1L << 0)
+#define BT_IM_B2H_IRQ (1L << 1)
+#define BT_IM_BMC_HWRST (1L << 7)
+
+static int bt_polled_request(struct ipmi_softc *, struct ipmi_request *);
+static int bt_driver_request(struct ipmi_softc *, struct ipmi_request *, int);
+static int bt_wait(struct ipmi_softc *, uint8_t, uint8_t);
+static int bt_reset(struct ipmi_softc *);
+
+static void bt_loop(void *);
+static int bt_startup(struct ipmi_softc *);
+
+#define BT_DELAY_MIN 1
+#define BT_DELAY_MAX 256
+
+static int
+bt_wait(struct ipmi_softc *sc, uint8_t mask, uint8_t wanted)
+{
+ volatile uint8_t value;
+ int delay = BT_DELAY_MIN;
+ int count = 20000; /* about 5 seconds */
+
+ while (count--) {
+ value = INB(sc, BT_CTRL_REG);
+ if ((value & mask) == wanted)
+ return (value);
+ /*
+ * The wait delay is increased exponentially to avoid putting
+ * significant load on I/O bus.
+ */
+ DELAY(delay);
+ if (delay < BT_DELAY_MAX)
+ delay <<= 1;
+ }
+ DMSGV(sc, "failed: m=%b w=%b v=0x%02x\n",
+ mask, BT_CTRL_BITS, wanted, BT_CTRL_BITS, value);
+ return (-1);
+
+}
+
+static int
+bt_reset(struct ipmi_softc *sc)
+{
+ uint8_t v;
+
+ v = INB(sc, BT_CTRL_REG);
+ DMSG(sc, "ctrl: %b", v, BT_CTRL_BITS);
+ v &= BT_C_H_BUSY; /* clear H_BUSY iff it set */
+ v |= BT_C_CLR_WR_PTR | BT_C_CLR_RD_PTR | BT_C_B2H_ATN | BT_C_H2B_ATN;
+
+ bt_wait(sc, BT_C_B_BUSY, 0);
+ OUTB(sc, BT_CTRL_REG, v);
+
+ v = BT_IM_B2H_IRQ | BT_IM_BMC_HWRST;
+ OUTB(sc, BT_INTMASK_REG, v);
+
+ return (0);
+}
+
+/*
+ * Send a request message and collect the reply. Returns 1 if we
+ * succeed.
+ */
+static int
+bt_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
+{
+ uint8_t addr, cmd, seq, v;
+ int i;
+
+ IPMI_IO_LOCK(sc);
+
+ /*
+ * Send the request:
+ *
+ * Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5:N
+ * -------+-----------+--------+--------+---------
+ * Length | NetFn/LUN | Seq | Cmd | Data
+ */
+
+ if (bt_wait(sc, BT_C_B_BUSY | BT_C_H2B_ATN, 0) < 0) {
+ DMSG(sc, "failed to start write transfer");
+ goto fail;
+ }
+ DMSG(sc, "request: length=%d, addr=0x%02x, seq=%u, cmd=0x%02x",
+ (int)req->ir_requestlen, req->ir_addr, sc->ipmi_bt_seq, req->ir_command);
+ OUTB(sc, BT_CTRL_REG, BT_C_CLR_WR_PTR);
+ OUTB(sc, BT_DATA_REG, req->ir_requestlen + BTMSG_REQLEN);
+ OUTB(sc, BT_DATA_REG, req->ir_addr);
+ OUTB(sc, BT_DATA_REG, sc->ipmi_bt_seq);
+ OUTB(sc, BT_DATA_REG, req->ir_command);
+ for (i = 0; i < req->ir_requestlen; i++)
+ OUTB(sc, BT_DATA_REG, req->ir_request[i]);
+ OUTB(sc, BT_CTRL_REG, BT_C_H2B_ATN);
+
+ if (bt_wait(sc, BT_C_B_BUSY | BT_C_H2B_ATN, 0) < 0) {
+ DMSG(sc, "failed to finish write transfer");
+ goto fail;
+ }
+
+ /*
+ * Read the reply:
+ *
+ * Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6:N
+ * -------+-----------+--------+--------+-----------------+---------
+ * Length | NetFn/LUN | Seq | Cmd | Completion code | Data
+ */
+ if (bt_wait(sc, BT_C_B2H_ATN, BT_C_B2H_ATN) < 0) {
+ DMSG(sc, "got no reply from BMC");
+ goto fail;
+ }
+ OUTB(sc, BT_CTRL_REG, BT_C_H_BUSY);
+ OUTB(sc, BT_CTRL_REG, BT_C_B2H_ATN);
+ OUTB(sc, BT_CTRL_REG, BT_C_CLR_RD_PTR);
+
+ i = INB(sc, BT_DATA_REG);
+ if (i < BTMSG_REPLEN) {
+ DMSG(sc, "wrong data length: %d", i);
+ goto fail;
+ }
+ req->ir_replylen = i - BTMSG_REPLEN;
+ DMSG(sc, "data length: %d, frame length: %d", req->ir_replylen, i);
+
+ addr = INB(sc, BT_DATA_REG);
+ if (addr != IPMI_REPLY_ADDR(req->ir_addr)) {
+ DMSGV(sc, "address doesn't match: addr=0x%02x vs. 0x%02x",
+ req->ir_addr, addr);
+ }
+
+ seq = INB(sc, BT_DATA_REG);
+ if (seq != sc->ipmi_bt_seq) {
+ DMSGV(sc, "seq number doesn't match: seq=0x%02x vs. 0x%02x",
+ sc->ipmi_bt_seq, seq);
+ }
+
+ cmd = INB(sc, BT_DATA_REG);
+ if (cmd != req->ir_command) {
+ DMSGV(sc, "command doesn't match: cmd=0x%02x vs. 0x%02x",
+ req->ir_command, cmd);
+ }
+
+ req->ir_compcode = INB(sc, BT_DATA_REG);
+ for (i = 0; i < req->ir_replylen; i++) {
+ v = INB(sc, BT_DATA_REG);
+ if (i < req->ir_replybuflen)
+ req->ir_reply[i] = v;
+ }
+
+ OUTB(sc, BT_CTRL_REG, BT_C_H_BUSY);
+ IPMI_IO_UNLOCK(sc);
+ DMSG(sc, "reply: length=%d, addr=0x%02x, seq=%u, cmd=0x%02x, code=0x%02x",
+ (int)req->ir_replylen, addr, seq, req->ir_command, req->ir_compcode);
+ return (1);
+fail:
+ bt_reset(sc);
+ IPMI_IO_UNLOCK(sc);
+ return (0);
+}
+
+static void
+bt_loop(void *arg)
+{
+ struct ipmi_softc *sc = arg;
+ struct ipmi_request *req;
+
+ IPMI_LOCK(sc);
+ while ((req = ipmi_dequeue_request(sc)) != NULL) {
+ IPMI_UNLOCK(sc);
+ (void)bt_driver_request(sc, req, 0);
+ IPMI_LOCK(sc);
+ sc->ipmi_bt_seq++;
+ ipmi_complete_request(sc, req);
+ }
+ IPMI_UNLOCK(sc);
+ kproc_exit(0);
+}
+
+static int
+bt_startup(struct ipmi_softc *sc)
+{
+
+ return (kproc_create(bt_loop, sc, &sc->ipmi_kthread, 0, 0, "%s: bt",
+ device_get_nameunit(sc->ipmi_dev)));
+}
+
+static int
+bt_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, int timo __unused)
+{
+ int i, ok;
+
+ ok = 0;
+ for (i = 0; i < 3 && !ok; i++)
+ ok = bt_polled_request(sc, req);
+ if (ok)
+ req->ir_error = 0;
+ else
+ req->ir_error = EIO;
+ return (req->ir_error);
+}
+
+int
+ipmi_bt_attach(struct ipmi_softc *sc)
+{
+ /* Setup function pointers. */
+ sc->ipmi_startup = bt_startup;
+ sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
+ sc->ipmi_driver_request = bt_driver_request;
+ sc->ipmi_driver_requests_polled = 1;
+ sc->ipmi_bt_seq = 1;
+
+ return (bt_reset(sc));
+}
diff --git a/sys/dev/ipmi/ipmi_isa.c b/sys/dev/ipmi/ipmi_isa.c
--- a/sys/dev/ipmi/ipmi_isa.c
+++ b/sys/dev/ipmi/ipmi_isa.c
@@ -188,16 +188,17 @@
switch (info.iface_type) {
case KCS_MODE:
- count = 2;
+ count = IPMI_IF_KCS_NRES;
mode = "KCS";
break;
case SMIC_MODE:
- count = 3;
+ count = IPMI_IF_SMIC_NRES;
mode = "SMIC";
break;
case BT_MODE:
- device_printf(dev, "BT mode is unsupported\n");
- return (ENXIO);
+ count = IPMI_IF_BT_NRES;
+ mode = "BT";
+ break;
default:
return (ENXIO);
}
@@ -248,19 +249,21 @@
RF_SHAREABLE | RF_ACTIVE);
}
+ error = ENXIO;
switch (info.iface_type) {
case KCS_MODE:
error = ipmi_kcs_attach(sc);
- if (error)
- goto bad;
break;
case SMIC_MODE:
error = ipmi_smic_attach(sc);
- if (error)
- goto bad;
+ break;
+ case BT_MODE:
+ error = ipmi_bt_attach(sc);
break;
}
+ if (error)
+ goto bad;
error = ipmi_attach(dev);
if (error)
goto bad;
diff --git a/sys/dev/ipmi/ipmi_pci.c b/sys/dev/ipmi/ipmi_pci.c
--- a/sys/dev/ipmi/ipmi_pci.c
+++ b/sys/dev/ipmi/ipmi_pci.c
@@ -112,8 +112,8 @@
mode = "SMIC";
break;
case BT_MODE:
- device_printf(dev, "BT mode is unsupported\n");
- return (ENXIO);
+ mode = "BT";
+ break;
default:
device_printf(dev, "No IPMI interface found\n");
return (ENXIO);
@@ -143,18 +143,20 @@
sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
&sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
+ error = ENXIO;
switch (info.iface_type) {
case KCS_MODE:
error = ipmi_kcs_attach(sc);
- if (error)
- goto bad;
break;
case SMIC_MODE:
error = ipmi_smic_attach(sc);
- if (error)
- goto bad;
+ break;
+ case BT_MODE:
+ error = ipmi_bt_attach(sc);
break;
}
+ if (error)
+ goto bad;
error = ipmi_attach(dev);
if (error)
goto bad;
@@ -215,8 +217,7 @@
break;
case PCIP_SERIALBUS_IPMI_BT:
iface = BT_MODE;
- device_printf(dev, "BT interface unsupported\n");
- return (ENXIO);
+ break;
default:
device_printf(dev, "Unsupported interface: %d\n",
pci_get_progif(dev));
@@ -242,6 +243,7 @@
sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
&sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
+ error = ENXIO;
switch (iface) {
case KCS_MODE:
device_printf(dev, "using KSC interface\n");
@@ -252,7 +254,6 @@
*/
if (!ipmi_kcs_probe_align(sc)) {
device_printf(dev, "Unable to determine alignment\n");
- error = ENXIO;
goto bad;
}
@@ -262,12 +263,15 @@
break;
case SMIC_MODE:
device_printf(dev, "using SMIC interface\n");
-
error = ipmi_smic_attach(sc);
- if (error)
- goto bad;
+ break;
+ case BT_MODE:
+ device_printf(dev, "using BT interface\n");
+ error = ipmi_bt_attach(sc);
break;
}
+ if (error)
+ goto bad;
error = ipmi_attach(dev);
if (error)
goto bad;
diff --git a/sys/dev/ipmi/ipmi_smbios.c b/sys/dev/ipmi/ipmi_smbios.c
--- a/sys/dev/ipmi/ipmi_smbios.c
+++ b/sys/dev/ipmi/ipmi_smbios.c
@@ -104,6 +104,7 @@
switch (s->interface_type) {
case KCS_MODE:
case SMIC_MODE:
+ case BT_MODE:
info->address = IPMI_BAR_ADDR(s->base_address) |
IPMI_BAM_ADDR_LSB(s->base_address_modifier);
info->io_mode = IPMI_BAR_MODE(s->base_address);
diff --git a/sys/dev/ipmi/ipmivars.h b/sys/dev/ipmi/ipmivars.h
--- a/sys/dev/ipmi/ipmivars.h
+++ b/sys/dev/ipmi/ipmivars.h
@@ -59,6 +59,10 @@
uint8_t ir_ipmb_command;
};
+#define IPMI_IF_KCS_NRES 2
+#define IPMI_IF_SMIC_NRES 3
+#define IPMI_IF_BT_NRES 3
+
#define MAX_RES 3
#define KCS_DATA 0
#define KCS_CTL_STS 1
@@ -80,6 +84,10 @@
u_char ipmi_lun;
};
+struct ipmi_bt {
+ uint8_t seq;
+};
+
struct ipmi_kcs {
};
@@ -94,6 +102,7 @@
struct ipmi_softc {
device_t ipmi_dev;
union {
+ struct ipmi_bt bt;
struct ipmi_kcs kcs;
struct ipmi_smic smic;
struct ipmi_ssif ssif;
@@ -131,11 +140,12 @@
#define ipmi_ssif_smbus_address _iface.ssif.smbus_address
#define ipmi_ssif_smbus _iface.ssif.smbus
+#define ipmi_bt_seq _iface.bt.seq
-#define KCS_MODE 0x01
-#define SMIC_MODE 0x02
-#define BT_MODE 0x03
-#define SSIF_MODE 0x04
+#define KCS_MODE 0x01
+#define SMIC_MODE 0x02
+#define BT_MODE 0x03
+#define SSIF_MODE 0x04
/* KCS status flags */
#define KCS_STATUS_OBF 0x01 /* Data Out ready from BMC */
@@ -252,6 +262,7 @@
int ipmi_kcs_attach(struct ipmi_softc *);
int ipmi_kcs_probe_align(struct ipmi_softc *);
int ipmi_smic_attach(struct ipmi_softc *);
+int ipmi_bt_attach(struct ipmi_softc *);
int ipmi_ssif_attach(struct ipmi_softc *, device_t, int);
extern int ipmi_attached;
diff --git a/sys/modules/ipmi/Makefile b/sys/modules/ipmi/Makefile
--- a/sys/modules/ipmi/Makefile
+++ b/sys/modules/ipmi/Makefile
@@ -10,7 +10,7 @@
.if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64" || \
${MACHINE_CPUARCH} == "aarch64"
-SRCS+= ipmi_kcs.c ipmi_smic.c
+SRCS+= ipmi_kcs.c ipmi_smic.c ipmi_bt.c
SRCS+= ipmi_acpi.c
SRCS+= opt_acpi.h
SRCS+= acpi_if.h

File Metadata

Mime Type
text/plain
Expires
Wed, Dec 4, 8:01 AM (5 h, 36 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
9066705
Default Alt Text
D40421.diff (14 KB)

Event Timeline