qemu-devel
[Top][All Lists]
Advanced

[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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]