[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[RFC 1/1] hw/ipmi: add an aspeed ipmi iBT device model
From: |
Titus Rwantare |
Subject: |
[RFC 1/1] hw/ipmi: add an aspeed ipmi iBT device model |
Date: |
Tue, 28 Sep 2021 15:39:42 -0700 |
Modifies [PATCH] hw/misc: Add an iBT device model
posted by Cédric Le Goater, to use IPMIInterface.
Signed-off-by: Titus Rwantare <titusr@google.com>
---
hw/ipmi/ipmi_extern.h | 1 +
include/hw/arm/aspeed_soc.h | 2 +
include/hw/ipmi/aspeed_ibt.h | 77 +++++++++
hw/arm/aspeed_ast2600.c | 12 ++
hw/arm/aspeed_soc.c | 12 ++
hw/ipmi/aspeed_ibt.c | 311 +++++++++++++++++++++++++++++++++++
hw/ipmi/meson.build | 1 +
hw/ipmi/trace-events | 7 +
8 files changed, 423 insertions(+)
create mode 100644 include/hw/ipmi/aspeed_ibt.h
create mode 100644 hw/ipmi/aspeed_ibt.c
diff --git a/hw/ipmi/ipmi_extern.h b/hw/ipmi/ipmi_extern.h
index e4aa80a0f6..40157ab0b3 100644
--- a/hw/ipmi/ipmi_extern.h
+++ b/hw/ipmi/ipmi_extern.h
@@ -27,6 +27,7 @@
#include "qemu/osdep.h"
#include "hw/ipmi/ipmi.h"
+#include "chardev/char-fe.h"
#define VM_MSG_CHAR 0xA0 /* Marks end of message */
#define VM_CMD_CHAR 0xA1 /* Marks end of a command */
diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h
index 87d76c9259..f650ff83ae 100644
--- a/include/hw/arm/aspeed_soc.h
+++ b/include/hw/arm/aspeed_soc.h
@@ -30,6 +30,7 @@
#include "hw/usb/hcd-ehci.h"
#include "qom/object.h"
#include "hw/misc/aspeed_lpc.h"
+#include "hw/ipmi/aspeed_ibt.h"
#define ASPEED_SPIS_NUM 2
#define ASPEED_EHCIS_NUM 2
@@ -65,6 +66,7 @@ struct AspeedSoCState {
AspeedSDHCIState sdhci;
AspeedSDHCIState emmc;
AspeedLPCState lpc;
+ AspeedIBTState ibt;
uint32_t uart_default;
};
diff --git a/include/hw/ipmi/aspeed_ibt.h b/include/hw/ipmi/aspeed_ibt.h
new file mode 100644
index 0000000000..d90cc103d4
--- /dev/null
+++ b/include/hw/ipmi/aspeed_ibt.h
@@ -0,0 +1,77 @@
+/*
+ * ASPEED iBT Device
+ *
+ * Copyright (c) 2016-2021 Cédric Le Goater, IBM Corporation.
+ * Copyright 2021 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef ASPEED_IBT_H
+#define ASPEED_IBT_H
+
+#include "hw/ipmi/ipmi.h"
+#include "hw/sysbus.h"
+
+#define TYPE_ASPEED_IBT "aspeed.ibt"
+#define ASPEED_IBT(obj) OBJECT_CHECK(AspeedIBTState, (obj), TYPE_ASPEED_IBT)
+
+#define ASPEED_IBT_NR_REGS 7
+
+#define ASPEED_IBT_BUFFER_SIZE 64
+
+#define ASPEED_IBT_IO_REGION_SIZE 0x1C
+
+#define TO_REG(o) (o >> 2)
+
+/* from linux/char/ipmi/bt-bmc. */
+#define BT_CR0 0x0
+#define BT_CR0_IO_BASE 16
+#define BT_CR0_IRQ 12
+#define BT_CR0_EN_CLR_SLV_RDP 0x8
+#define BT_CR0_EN_CLR_SLV_WRP 0x4
+#define BT_CR0_ENABLE_IBT 0x1
+#define BT_CR1 0x4
+#define BT_CR1_IRQ_H2B 0x01
+#define BT_CR1_IRQ_HBUSY 0x40
+#define BT_CR2 0x8
+#define BT_CR2_IRQ_H2B 0x01
+#define BT_CR2_IRQ_HBUSY 0x40
+#define BT_CR3 0xc
+#define BT_CTRL 0x10
+#define BT_CTRL_B_BUSY BIT(7) /* BMC is busy */
+#define BT_CTRL_H_BUSY BIT(6) /* Host is busy */
+#define BT_CTRL_OEM0 BIT(5)
+#define BT_CTRL_SMS_ATN BIT(4) /* SMS/EVT Attention */
+#define BT_CTRL_B2H_ATN BIT(3) /* BMC to Host Attention */
+#define BT_CTRL_H2B_ATN BIT(2) /* Host to BMC Attention */
+#define BT_CTRL_CLR_RD_PTR BIT(1) /* Clear Read Ptr */
+#define BT_CTRL_CLR_WR_PTR BIT(0) /* Clear Write Ptr */
+#define BT_BMC2HOST 0x14
+#define BT_HOST2BMC 0x14
+#define BT_INTMASK 0x18
+#define BT_INTMASK_BMC_HWRST BIT(7)
+#define BT_INTMASK_B2H_IRQ BIT(1)
+#define BT_INTMASK_B2H_IRQEN BIT(0)
+
+typedef struct AspeedIBTState {
+ SysBusDevice parent;
+
+ IPMICore *handler;
+
+ uint8_t msg_id;
+ uint8_t recv_msg[ASPEED_IBT_BUFFER_SIZE];
+ uint8_t recv_msg_len;
+ int recv_msg_index;
+
+ uint8_t send_msg[ASPEED_IBT_BUFFER_SIZE];
+ uint8_t send_msg_len;
+
+ MemoryRegion iomem;
+ qemu_irq irq;
+
+ uint32_t regs[ASPEED_IBT_NR_REGS];
+} AspeedIBTState;
+
+
+#endif /* ASPEED_IBT_H */
diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c
index 9d70e8e060..f40fa69a53 100644
--- a/hw/arm/aspeed_ast2600.c
+++ b/hw/arm/aspeed_ast2600.c
@@ -216,6 +216,8 @@ static void aspeed_soc_ast2600_init(Object *obj)
snprintf(typename, sizeof(typename), "aspeed.hace-%s", socname);
object_initialize_child(obj, "hace", &s->hace, typename);
+
+ object_initialize_child(obj, "ibt", &s->ibt, TYPE_ASPEED_IBT);
}
/*
@@ -507,6 +509,16 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev,
Error **errp)
sysbus_mmio_map(SYS_BUS_DEVICE(&s->hace), 0, sc->memmap[ASPEED_DEV_HACE]);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0,
aspeed_soc_get_irq(s, ASPEED_DEV_HACE));
+
+ /* iBT */
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->ibt), errp)) {
+ return;
+ }
+ memory_region_add_subregion(&s->lpc.iomem,
+ sc->memmap[ASPEED_DEV_IBT] - sc->memmap[ASPEED_DEV_LPC],
+ &s->ibt.iomem);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->ibt), 0,
+ aspeed_soc_get_irq(s, ASPEED_DEV_IBT));
}
static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data)
diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c
index ed84502e23..13543d37c5 100644
--- a/hw/arm/aspeed_soc.c
+++ b/hw/arm/aspeed_soc.c
@@ -216,6 +216,8 @@ static void aspeed_soc_init(Object *obj)
snprintf(typename, sizeof(typename), "aspeed.hace-%s", socname);
object_initialize_child(obj, "hace", &s->hace, typename);
+
+ object_initialize_child(obj, "ibt", &s->ibt, TYPE_ASPEED_IBT);
}
static void aspeed_soc_realize(DeviceState *dev, Error **errp)
@@ -426,6 +428,16 @@ static void aspeed_soc_realize(DeviceState *dev, Error
**errp)
sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_4,
qdev_get_gpio_in(DEVICE(&s->lpc), aspeed_lpc_kcs_4));
+ /* iBT */
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->ibt), errp)) {
+ return;
+ }
+ memory_region_add_subregion(&s->lpc.iomem,
+ sc->memmap[ASPEED_DEV_IBT] - sc->memmap[ASPEED_DEV_LPC],
+ &s->ibt.iomem);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_ibt,
+ qdev_get_gpio_in(DEVICE(&s->lpc), aspeed_lpc_ibt));
+
/* HACE */
object_property_set_link(OBJECT(&s->hace), "dram", OBJECT(s->dram_mr),
&error_abort);
diff --git a/hw/ipmi/aspeed_ibt.c b/hw/ipmi/aspeed_ibt.c
new file mode 100644
index 0000000000..94503da1b0
--- /dev/null
+++ b/hw/ipmi/aspeed_ibt.c
@@ -0,0 +1,311 @@
+/*
+ * ASPEED iBT Device
+ *
+ * Copyright (c) 2016-2021 Cédric Le Goater, IBM Corporation.
+ * Copyright 2021 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-properties-system.h"
+#include "hw/ipmi/aspeed_ibt.h"
+#include "hw/ipmi/ipmi_extern.h"
+#include "hw/sysbus.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qemu/bitops.h"
+#include "qemu/error-report.h"
+#include "qemu/log.h"
+#include "sysemu/qtest.h"
+#include "sysemu/sysemu.h"
+#include "trace.h"
+
+static void aspeed_ibt_dump_msg(const char *func, unsigned char *msg,
+ unsigned int len)
+{
+ if (trace_event_get_state_backends(TRACE_ASPEED_IBT_CHR_DUMP_MSG)) {
+ int size = len * 3 + 1;
+ char tmp[size];
+ int i, n = 0;
+
+ for (i = 0; i < len; i++) {
+ n += snprintf(tmp + n, size - n, "%02x:", msg[i]);
+ }
+ tmp[size - 1] = 0;
+
+ trace_aspeed_ibt_chr_dump_msg(func, tmp, len);
+ }
+}
+
+/* convert and send IPMI message from BMC to remote host */
+static void aspeed_ibt_b2h_write(AspeedIBTState *ibt)
+{
+ IPMICoreClass *hk = IPMI_CORE_GET_CLASS(ibt->handler);
+
+ aspeed_ibt_dump_msg(__func__, ibt->send_msg, ibt->send_msg_len);
+ /* separate seq from raw contents */
+ ibt->msg_id = ibt->send_msg[2];
+ ibt->send_msg[2] = ibt->send_msg[1];
+ /* length and seq not included in vm message */
+ hk->handle_command(ibt->handler, ibt->send_msg + 2,
+ ibt->send_msg_len - 2, MAX_IPMI_MSG_SIZE,
+ ibt->msg_id);
+}
+
+
+/* BMC firmware writing to memory region */
+static void aspeed_ibt_write(void *opaque, hwaddr offset, uint64_t data,
+ uint32_t size)
+{
+ AspeedIBTState *ibt = ASPEED_IBT(opaque);
+
+ trace_aspeed_ibt_write(offset, data);
+ switch (offset) {
+ case BT_CTRL:
+ /* CLR_WR_PTR: cleared before a message is written */
+ if (data & BT_CTRL_CLR_WR_PTR) {
+ memset(ibt->send_msg, 0, sizeof(ibt->send_msg));
+ ibt->send_msg_len = 0;
+ trace_aspeed_ibt_event("CLR_WR_PTR");
+ }
+
+ /* CLR_RD_PTR: cleared before a message is read */
+ else if (data & BT_CTRL_CLR_RD_PTR) {
+ ibt->recv_msg_index = -1;
+ trace_aspeed_ibt_event("CLR_RD_PTR");
+ }
+
+ /*
+ * H2B_ATN: raised by host to end message, cleared by BMC
+ * before reading message
+ */
+ else if (data & BT_CTRL_H2B_ATN) {
+ ibt->regs[TO_REG(BT_CTRL)] &= ~BT_CTRL_H2B_ATN;
+ trace_aspeed_ibt_event("H2B_ATN");
+ }
+
+ /* B_BUSY: raised and cleared by BMC when message is read */
+ else if (data & BT_CTRL_B_BUSY) {
+ ibt->regs[TO_REG(BT_CTRL)] ^= BT_CTRL_B_BUSY;
+ trace_aspeed_ibt_event("B_BUSY");
+ }
+
+ /*
+ * B2H_ATN: raised by BMC and cleared by host
+ *
+ * Also simulate the host busy bit which is set while the host
+ * is reading the message from the BMC
+ */
+ else if (data & BT_CTRL_B2H_ATN) {
+ trace_aspeed_ibt_event("B2H_ATN");
+ ibt->regs[TO_REG(BT_CTRL)] |= (BT_CTRL_B2H_ATN | BT_CTRL_H_BUSY);
+
+ aspeed_ibt_b2h_write(ibt);
+
+ ibt->regs[TO_REG(BT_CTRL)] &= ~(BT_CTRL_B2H_ATN | BT_CTRL_H_BUSY);
+ }
+
+ /* Anything else is unexpected */
+ else {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected CTRL setting\n",
+ __func__);
+ }
+
+ /* Message was read by BMC. we can reset the receive state */
+ if (!(ibt->regs[TO_REG(BT_CTRL)] & BT_CTRL_B_BUSY)) {
+ trace_aspeed_ibt_event("B_BUSY cleared");
+ ibt->recv_msg_len = 0;
+ }
+ break;
+
+ case BT_BMC2HOST:
+ if (ibt->send_msg_len < sizeof(ibt->send_msg)) {
+ trace_aspeed_ibt_event("BMC2HOST");
+ ibt->send_msg[ibt->send_msg_len++] = data & 0xff;
+ }
+ break;
+
+ case BT_CR0: /* TODO: iBT config */
+ case BT_CR1: /* interrupt enable */
+ case BT_CR3: /* unused */
+ case BT_INTMASK:
+ ibt->regs[TO_REG(offset)] = (uint32_t) data;
+ break;
+
+ case BT_CR2: /* interrupt status. writing 1 clears. */
+ ibt->regs[TO_REG(offset)] ^= (uint32_t) data;
+ qemu_irq_lower(ibt->irq);
+ break;
+
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: not implemented 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ break;
+ }
+}
+
+/* BMC firmware reading from memory region */
+static uint64_t aspeed_ibt_read(void *opaque, hwaddr offset, unsigned size)
+{
+ AspeedIBTState *ibt = ASPEED_IBT(opaque);
+ uint64_t val = 0;
+
+ switch (offset) {
+ case BT_HOST2BMC: /* shares offset with B2H */
+ trace_aspeed_ibt_event("HOST2BMC");
+ /*
+ * The IPMI BT interface requires the first byte to be the
+ * length of the message
+ */
+ if (ibt->recv_msg_index == -1) {
+ val = ibt->recv_msg_len;
+ ibt->recv_msg_index++;
+ } else if (ibt->recv_msg_index < ibt->recv_msg_len) {
+ val = ibt->recv_msg[ibt->recv_msg_index++];
+ }
+ break;
+
+ case BT_CR0:
+ case BT_CR1:
+ case BT_CR2:
+ case BT_CR3:
+ case BT_CTRL:
+ case BT_INTMASK:
+ val = ibt->regs[TO_REG(offset)];
+ break;
+
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: not implemented 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ return 0;
+ }
+
+ trace_aspeed_ibt_read(offset, val);
+ return val;
+}
+
+/* send a request from a host to the BMC core */
+static void aspeed_ibt_handle_msg(IPMIInterface *ii, uint8_t msg_id,
+ unsigned char *req, unsigned int req_len)
+{
+ AspeedIBTState *ibt = ASPEED_IBT(ii);
+
+ if (req_len == 0) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: zero length request", __func__);
+ return;
+ }
+
+ if (req_len > ASPEED_IBT_BUFFER_SIZE - 1) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: request of %d bytes is too long",
+ __func__, req_len);
+ req_len = ASPEED_IBT_BUFFER_SIZE - 1;
+ }
+
+ /* include length and reuse msg_id as seq in message */
+ ibt->recv_msg[0] = req[0];
+ ibt->recv_msg[1] = msg_id;
+ memcpy(ibt->recv_msg + 2, req + 1, req_len - 1);
+ ibt->recv_msg_len = req_len + 1;
+
+
+ ibt->regs[TO_REG(BT_CTRL)] |= BT_CTRL_H2B_ATN;
+ aspeed_ibt_dump_msg(__func__, ibt->recv_msg, ibt->recv_msg_len);
+
+}
+
+static void *aspeed_ibt_backend_data(IPMIInterface *ii)
+{
+ return ii;
+}
+
+static void aspeed_ibt_set_ipmi_handler(IPMIInterface *ii, IPMICore *h)
+{
+ AspeedIBTState *ibt = ASPEED_IBT(ii);
+ ibt->handler = h;
+}
+
+static const MemoryRegionOps aspeed_ibt_ops = {
+ .read = aspeed_ibt_read,
+ .write = aspeed_ibt_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+};
+
+static void aspeed_ibt_enter_reset(Object *obj, ResetType type)
+{
+ AspeedIBTState *ibt = ASPEED_IBT(obj);
+
+ memset(ibt->regs, 0, sizeof(ibt->regs));
+
+ memset(ibt->recv_msg, 0, sizeof(ibt->recv_msg));
+ ibt->recv_msg_len = 0;
+ ibt->recv_msg_index = -1;
+
+ memset(ibt->send_msg, 0, sizeof(ibt->send_msg));
+ ibt->send_msg_len = 0;
+}
+
+
+static void aspeed_ibt_realize(DeviceState *dev, Error **errp)
+{
+ AspeedIBTState *ibt = ASPEED_IBT(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ IPMIInterface *ii = IPMI_INTERFACE(dev);
+
+ sysbus_init_irq(sbd, &ibt->irq);
+ memory_region_init_io(&ibt->iomem, OBJECT(ibt), &aspeed_ibt_ops, ibt,
+ TYPE_ASPEED_IBT, ASPEED_IBT_IO_REGION_SIZE);
+ sysbus_init_mmio(sbd, &ibt->iomem);
+
+
+ if (ibt->handler) {
+ ibt->handler->intf = ii;
+ }
+}
+
+static const VMStateDescription vmstate_aspeed_ibt = {
+ .name = TYPE_ASPEED_IBT,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, AspeedIBTState, ASPEED_IBT_NR_REGS),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void aspeed_ibt_class_init(ObjectClass *klass, void *data)
+{
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(klass);
+
+ dc->desc = "ASPEED IPMI BT Host Controller";
+ dc->vmsd = &vmstate_aspeed_ibt;
+ dc->realize = aspeed_ibt_realize;
+ rc->phases.enter = aspeed_ibt_enter_reset;
+
+ iic->handle_msg = aspeed_ibt_handle_msg;
+ iic->get_backend_data = aspeed_ibt_backend_data;
+ iic->set_ipmi_handler = aspeed_ibt_set_ipmi_handler;
+}
+
+static const TypeInfo aspeed_ibt_info[] = {
+ {
+ .name = TYPE_ASPEED_IBT,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AspeedIBTState),
+ .class_init = aspeed_ibt_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_IPMI_INTERFACE },
+ { },
+ },
+ },
+};
+
+DEFINE_TYPES(aspeed_ibt_info);
diff --git a/hw/ipmi/meson.build b/hw/ipmi/meson.build
index 3d8b030ba5..7e7bced38e 100644
--- a/hw/ipmi/meson.build
+++ b/hw/ipmi/meson.build
@@ -9,5 +9,6 @@ ipmi_ss.add(when: 'CONFIG_PCI_IPMI_BT', if_true:
files('pci_ipmi_bt.c'))
ipmi_ss.add(when: 'CONFIG_IPMI_SSIF', if_true: files('smbus_ipmi.c'))
ipmi_ss.add(when: 'CONFIG_IPMI_HOST', if_true: files('ipmi_host_extern.c'))
ipmi_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_kcs.c'))
+ipmi_ss.add(when: 'CONFIG_IPMI_HOST', if_true: files('aspeed_ibt.c'))
softmmu_ss.add_all(when: 'CONFIG_IPMI', if_true: ipmi_ss)
diff --git a/hw/ipmi/trace-events b/hw/ipmi/trace-events
index 66d40bccbc..efbedfdd62 100644
--- a/hw/ipmi/trace-events
+++ b/hw/ipmi/trace-events
@@ -6,3 +6,10 @@ npcm7xx_kcs_write(const char *id, int channel, int reg,
uint8_t value) " %s chan
npcm7xx_kcs_handle_event(const char *id, uint8_t status) " %s: %" PRIu8
npcm7xx_kcs_host_read_byte(const char *id, uint8_t value) " %s: value 0x%02"
PRIx8
npcm7xx_kcs_host_write_byte(const char *id, uint8_t value) " %s: value 0x%02"
PRIx8
+
+# aspeed_ibt.c
+aspeed_ibt_chr_dump_msg(const char *func, const char *buf, uint32_t len) "%s:
%s #%d bytes"
+aspeed_ibt_chr_event(bool connected) "connected:%d"
+aspeed_ibt_read(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 "
value:0x%" PRIx64
+aspeed_ibt_write(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 "
value:0x%" PRIx64
+aspeed_ibt_event(const char* event) "%s"
--
2.33.0.800.g4c38ced690-goog