qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v5 0/16] Add an IPMI device to QEMU
@ 2015-12-17 18:50 minyard
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 01/16] Add a base IPMI interface minyard
                   ` (15 more replies)
  0 siblings, 16 replies; 17+ messages in thread
From: minyard @ 2015-12-17 18:50 UTC (permalink / raw)
  To: qemu-devel, Paolo Bonzini, Michael S. Tsirkin, Igor Mammedov

This is based on the just-released 2.5.0 release, I believe I have
fixed/answered all questions on this.

Thanks!

-corey

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 01/16] Add a base IPMI interface
  2015-12-17 18:50 [Qemu-devel] [PATCH v5 0/16] Add an IPMI device to QEMU minyard
@ 2015-12-17 18:50 ` minyard
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 02/16] ipmi: Add a local BMC simulation minyard
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: minyard @ 2015-12-17 18:50 UTC (permalink / raw)
  To: qemu-devel, Paolo Bonzini, Michael S. Tsirkin, Igor Mammedov
  Cc: Corey Minyard

From: Corey Minyard <cminyard@mvista.com>

Add the basic IPMI types and infrastructure to QEMU.  Low-level
interfaces and simulation interfaces will register with this; it's
kind of the go-between to tie them together.

Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
 default-configs/i386-softmmu.mak   |   1 +
 default-configs/x86_64-softmmu.mak |   1 +
 hw/Makefile.objs                   |   1 +
 hw/ipmi/Makefile.objs              |   1 +
 hw/ipmi/ipmi.c                     | 125 ++++++++++++++++++++++++++
 include/hw/ipmi/ipmi.h             | 178 +++++++++++++++++++++++++++++++++++++
 qemu-doc.texi                      |   2 +
 7 files changed, 309 insertions(+)
 create mode 100644 hw/ipmi/Makefile.objs
 create mode 100644 hw/ipmi/ipmi.c
 create mode 100644 include/hw/ipmi/ipmi.h

diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index 43c96d1..8fa751a 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -9,6 +9,7 @@ CONFIG_VGA_CIRRUS=y
 CONFIG_VMWARE_VGA=y
 CONFIG_VIRTIO_VGA=y
 CONFIG_VMMOUSE=y
+CONFIG_IPMI=y
 CONFIG_SERIAL=y
 CONFIG_PARALLEL=y
 CONFIG_I8254=y
diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index dfb8095..6767f4f 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -9,6 +9,7 @@ CONFIG_VGA_CIRRUS=y
 CONFIG_VMWARE_VGA=y
 CONFIG_VIRTIO_VGA=y
 CONFIG_VMMOUSE=y
+CONFIG_IPMI=y
 CONFIG_SERIAL=y
 CONFIG_PARALLEL=y
 CONFIG_I8254=y
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 7e7c241..4a07ed4 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -13,6 +13,7 @@ devices-dirs-$(CONFIG_SOFTMMU) += ide/
 devices-dirs-$(CONFIG_SOFTMMU) += input/
 devices-dirs-$(CONFIG_SOFTMMU) += intc/
 devices-dirs-$(CONFIG_IPACK) += ipack/
+devices-dirs-$(CONFIG_IPMI) += ipmi/
 devices-dirs-$(CONFIG_SOFTMMU) += isa/
 devices-dirs-$(CONFIG_SOFTMMU) += misc/
 devices-dirs-$(CONFIG_SOFTMMU) += net/
diff --git a/hw/ipmi/Makefile.objs b/hw/ipmi/Makefile.objs
new file mode 100644
index 0000000..65bde11
--- /dev/null
+++ b/hw/ipmi/Makefile.objs
@@ -0,0 +1 @@
+common-obj-$(CONFIG_IPMI) += ipmi.o
diff --git a/hw/ipmi/ipmi.c b/hw/ipmi/ipmi.c
new file mode 100644
index 0000000..7d17469
--- /dev/null
+++ b/hw/ipmi/ipmi.c
@@ -0,0 +1,125 @@
+/*
+ * QEMU IPMI emulation
+ *
+ * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/hw.h"
+#include "hw/ipmi/ipmi.h"
+#include "sysemu/sysemu.h"
+#include "qmp-commands.h"
+#include "qom/object_interfaces.h"
+#include "qapi/visitor.h"
+
+static int ipmi_do_hw_op(IPMIInterface *s, enum ipmi_op op, int checkonly)
+{
+    switch (op) {
+    case IPMI_RESET_CHASSIS:
+        if (checkonly) {
+            return 0;
+        }
+        qemu_system_reset_request();
+        return 0;
+
+    case IPMI_POWEROFF_CHASSIS:
+        if (checkonly) {
+            return 0;
+        }
+        qemu_system_powerdown_request();
+        return 0;
+
+    case IPMI_SEND_NMI:
+        if (checkonly) {
+            return 0;
+        }
+        qemu_mutex_lock_iothread();
+        qmp_inject_nmi(NULL);
+        qemu_mutex_unlock_iothread();
+        return 0;
+
+    case IPMI_POWERCYCLE_CHASSIS:
+    case IPMI_PULSE_DIAG_IRQ:
+    case IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP:
+    case IPMI_POWERON_CHASSIS:
+    default:
+        return IPMI_CC_COMMAND_NOT_SUPPORTED;
+    }
+}
+
+static void ipmi_interface_class_init(ObjectClass *class, void *data)
+{
+    IPMIInterfaceClass *ik = IPMI_INTERFACE_CLASS(class);
+
+    ik->do_hw_op = ipmi_do_hw_op;
+}
+
+static TypeInfo ipmi_interface_type_info = {
+    .name = TYPE_IPMI_INTERFACE,
+    .parent = TYPE_INTERFACE,
+    .class_size = sizeof(IPMIInterfaceClass),
+    .class_init = ipmi_interface_class_init,
+};
+
+static void isa_ipmi_bmc_check(Object *obj, const char *name,
+                               Object *val, Error **errp)
+{
+    IPMIBmc *bmc = IPMI_BMC(val);
+
+    if (bmc->intf)
+        error_setg(errp, "BMC object is already in use");
+}
+
+void ipmi_bmc_find_and_link(Object *obj, Object **bmc)
+{
+    object_property_add_link(obj, "bmc", TYPE_IPMI_BMC, bmc,
+                             isa_ipmi_bmc_check,
+                             OBJ_PROP_LINK_UNREF_ON_RELEASE,
+                             &error_abort);
+}
+
+static Property ipmi_bmc_properties[] = {
+    DEFINE_PROP_UINT8("slave_addr",  IPMIBmc, slave_addr, 0x20),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void bmc_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    dc->props = ipmi_bmc_properties;
+}
+
+static TypeInfo ipmi_bmc_type_info = {
+    .name = TYPE_IPMI_BMC,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(IPMIBmc),
+    .abstract = true,
+    .class_size = sizeof(IPMIBmcClass),
+    .class_init = bmc_class_init,
+};
+
+static void ipmi_register_types(void)
+{
+    type_register_static(&ipmi_interface_type_info);
+    type_register_static(&ipmi_bmc_type_info);
+}
+
+type_init(ipmi_register_types)
diff --git a/include/hw/ipmi/ipmi.h b/include/hw/ipmi/ipmi.h
new file mode 100644
index 0000000..e4f7738
--- /dev/null
+++ b/include/hw/ipmi/ipmi.h
@@ -0,0 +1,178 @@
+/*
+ * IPMI base class
+ *
+ * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef HW_IPMI_H
+#define HW_IPMI_H
+
+#include "exec/memory.h"
+#include "qemu-common.h"
+#include "hw/qdev.h"
+
+#define MAX_IPMI_MSG_SIZE 300
+
+enum ipmi_op {
+    IPMI_RESET_CHASSIS,
+    IPMI_POWEROFF_CHASSIS,
+    IPMI_POWERON_CHASSIS,
+    IPMI_POWERCYCLE_CHASSIS,
+    IPMI_PULSE_DIAG_IRQ,
+    IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP,
+    IPMI_SEND_NMI
+};
+
+#define IPMI_CC_INVALID_CMD                              0xc1
+#define IPMI_CC_COMMAND_INVALID_FOR_LUN                  0xc2
+#define IPMI_CC_TIMEOUT                                  0xc3
+#define IPMI_CC_OUT_OF_SPACE                             0xc4
+#define IPMI_CC_INVALID_RESERVATION                      0xc5
+#define IPMI_CC_REQUEST_DATA_TRUNCATED                   0xc6
+#define IPMI_CC_REQUEST_DATA_LENGTH_INVALID              0xc7
+#define IPMI_CC_PARM_OUT_OF_RANGE                        0xc9
+#define IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES              0xca
+#define IPMI_CC_REQ_ENTRY_NOT_PRESENT                    0xcb
+#define IPMI_CC_INVALID_DATA_FIELD                       0xcc
+#define IPMI_CC_BMC_INIT_IN_PROGRESS                     0xd2
+#define IPMI_CC_COMMAND_NOT_SUPPORTED                    0xd5
+
+#define IPMI_NETFN_APP                0x06
+
+#define IPMI_DEBUG 1
+
+/* Specified in the SMBIOS spec. */
+#define IPMI_SMBIOS_KCS         0x01
+#define IPMI_SMBIOS_SMIC        0x02
+#define IPMI_SMBIOS_BT          0x03
+#define IPMI_SMBIOS_SSIF        0x04
+
+/* IPMI Interface types (KCS, SMIC, BT) are prefixed with this */
+#define TYPE_IPMI_INTERFACE_PREFIX "ipmi-interface-"
+
+/*
+ * An IPMI Interface, the interface for talking between the target
+ * and the BMC.
+ */
+#define TYPE_IPMI_INTERFACE "ipmi-interface"
+#define IPMI_INTERFACE(obj) \
+     INTERFACE_CHECK(IPMIInterface, (obj), TYPE_IPMI_INTERFACE)
+#define IPMI_INTERFACE_CLASS(class) \
+     OBJECT_CLASS_CHECK(IPMIInterfaceClass, (class), TYPE_IPMI_INTERFACE)
+#define IPMI_INTERFACE_GET_CLASS(class) \
+     OBJECT_GET_CLASS(IPMIInterfaceClass, (class), TYPE_IPMI_INTERFACE)
+
+typedef struct IPMIInterface {
+    Object parent;
+} IPMIInterface;
+
+typedef struct IPMIInterfaceClass {
+    InterfaceClass parent;
+
+    void (*init)(struct IPMIInterface *s, Error **errp);
+
+    /*
+     * Perform various operations on the hardware.  If checkonly is
+     * true, it will return if the operation can be performed, but it
+     * will not do the operation.
+     */
+    int (*do_hw_op)(struct IPMIInterface *s, enum ipmi_op op, int checkonly);
+
+    /*
+     * Enable/disable irqs on the interface when the BMC requests this.
+     */
+    void (*set_irq_enable)(struct IPMIInterface *s, int val);
+
+    /*
+     * Handle an event that occurred on the interface, generally the.
+     * target writing to a register.
+     */
+    void (*handle_if_event)(struct IPMIInterface *s);
+
+    /*
+     * The interfaces use this to perform certain ops
+     */
+    void (*set_atn)(struct IPMIInterface *s, int val, int irq);
+
+    /*
+     * Got an IPMI warm/cold reset.
+     */
+    void (*reset)(struct IPMIInterface *s, bool is_cold);
+
+    /*
+     * Handle a response from the bmc.
+     */
+    void (*handle_rsp)(struct IPMIInterface *s, uint8_t msg_id,
+                       unsigned char *rsp, unsigned int rsp_len);
+
+    /*
+     * Set by the owner to hold the backend data for the interface.
+     */
+    void *(*get_backend_data)(struct IPMIInterface *s);
+} IPMIInterfaceClass;
+
+/*
+ * Define a BMC simulator (or perhaps a connection to a real BMC)
+ */
+#define TYPE_IPMI_BMC "ipmi-bmc"
+#define IPMI_BMC(obj) \
+     OBJECT_CHECK(IPMIBmc, (obj), TYPE_IPMI_BMC)
+#define IPMI_BMC_CLASS(obj_class) \
+     OBJECT_CLASS_CHECK(IPMIBmcClass, (obj_class), TYPE_IPMI_BMC)
+#define IPMI_BMC_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(IPMIBmcClass, (obj), TYPE_IPMI_BMC)
+
+typedef struct IPMIBmc {
+    DeviceState parent;
+
+    uint8_t slave_addr;
+
+    IPMIInterface *intf;
+} IPMIBmc;
+
+typedef struct IPMIBmcClass {
+    DeviceClass parent;
+
+    /* Called when the system resets to report to the bmc. */
+    void (*handle_reset)(struct IPMIBmc *s);
+
+    /*
+     * Handle a command to the bmc.
+     */
+    void (*handle_command)(struct IPMIBmc *s,
+                           uint8_t *cmd, unsigned int cmd_len,
+                           unsigned int max_cmd_len,
+                           uint8_t msg_id);
+} IPMIBmcClass;
+
+/*
+ * Add a link property to obj that points to a BMC.
+ */
+void ipmi_bmc_find_and_link(Object *obj, Object **bmc);
+
+#ifdef IPMI_DEBUG
+#define ipmi_debug(fs, ...) \
+    fprintf(stderr, "IPMI (%s): " fs, __func__, ##__VA_ARGS__)
+#else
+#define ipmi_debug(fs, ...)
+#endif
+
+#endif
diff --git a/qemu-doc.texi b/qemu-doc.texi
index ffc3e50..7bc3882 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -195,6 +195,8 @@ PCI and ISA network adapters
 @item
 Serial ports
 @item
+IPMI BMC, either and internal or external one
+@item
 Creative SoundBlaster 16 sound card
 @item
 ENSONIQ AudioPCI ES1370 sound card
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 02/16] ipmi: Add a local BMC simulation
  2015-12-17 18:50 [Qemu-devel] [PATCH v5 0/16] Add an IPMI device to QEMU minyard
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 01/16] Add a base IPMI interface minyard
@ 2015-12-17 18:50 ` minyard
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 03/16] ipmi: Add an external connection simulation interface minyard
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: minyard @ 2015-12-17 18:50 UTC (permalink / raw)
  To: qemu-devel, Paolo Bonzini, Michael S. Tsirkin, Igor Mammedov
  Cc: Corey Minyard

From: Corey Minyard <cminyard@mvista.com>

This provides a minimal local BMC, basically enough to comply with the
spec and provide a complete watchdog timer (including a sensor, SDR,
and event).

Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
 default-configs/i386-softmmu.mak   |    1 +
 default-configs/x86_64-softmmu.mak |    1 +
 hw/ipmi/Makefile.objs              |    1 +
 hw/ipmi/ipmi_bmc_sim.c             | 1726 ++++++++++++++++++++++++++++++++++++
 4 files changed, 1729 insertions(+)
 create mode 100644 hw/ipmi/ipmi_bmc_sim.c

diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index 8fa751a..00a0346 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -10,6 +10,7 @@ CONFIG_VMWARE_VGA=y
 CONFIG_VIRTIO_VGA=y
 CONFIG_VMMOUSE=y
 CONFIG_IPMI=y
+CONFIG_IPMI_LOCAL=y
 CONFIG_SERIAL=y
 CONFIG_PARALLEL=y
 CONFIG_I8254=y
diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index 6767f4f..39a619f 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -10,6 +10,7 @@ CONFIG_VMWARE_VGA=y
 CONFIG_VIRTIO_VGA=y
 CONFIG_VMMOUSE=y
 CONFIG_IPMI=y
+CONFIG_IPMI_LOCAL=y
 CONFIG_SERIAL=y
 CONFIG_PARALLEL=y
 CONFIG_I8254=y
diff --git a/hw/ipmi/Makefile.objs b/hw/ipmi/Makefile.objs
index 65bde11..875271c 100644
--- a/hw/ipmi/Makefile.objs
+++ b/hw/ipmi/Makefile.objs
@@ -1 +1,2 @@
 common-obj-$(CONFIG_IPMI) += ipmi.o
+common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_sim.o
diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c
new file mode 100644
index 0000000..af01e1a
--- /dev/null
+++ b/hw/ipmi/ipmi_bmc_sim.c
@@ -0,0 +1,1726 @@
+/*
+ * IPMI BMC emulation
+ *
+ * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include "qemu/timer.h"
+#include "hw/ipmi/ipmi.h"
+#include "qemu/error-report.h"
+
+#define IPMI_NETFN_CHASSIS            0x00
+#define IPMI_NETFN_CHASSIS_MAXCMD         0x03
+
+#define IPMI_CMD_GET_CHASSIS_CAPABILITIES 0x00
+#define IPMI_CMD_GET_CHASSIS_STATUS       0x01
+#define IPMI_CMD_CHASSIS_CONTROL          0x02
+
+#define IPMI_NETFN_SENSOR_EVENT       0x04
+#define IPMI_NETFN_SENSOR_EVENT_MAXCMD    0x2e
+
+#define IPMI_CMD_SET_SENSOR_EVT_ENABLE    0x28
+#define IPMI_CMD_GET_SENSOR_EVT_ENABLE    0x29
+#define IPMI_CMD_REARM_SENSOR_EVTS        0x2a
+#define IPMI_CMD_GET_SENSOR_EVT_STATUS    0x2b
+#define IPMI_CMD_GET_SENSOR_READING       0x2d
+
+/* #define IPMI_NETFN_APP             0x06 In ipmi.h */
+#define IPMI_NETFN_APP_MAXCMD             0x36
+
+#define IPMI_CMD_GET_DEVICE_ID            0x01
+#define IPMI_CMD_COLD_RESET               0x02
+#define IPMI_CMD_WARM_RESET               0x03
+#define IPMI_CMD_RESET_WATCHDOG_TIMER     0x22
+#define IPMI_CMD_SET_WATCHDOG_TIMER       0x24
+#define IPMI_CMD_GET_WATCHDOG_TIMER       0x25
+#define IPMI_CMD_SET_BMC_GLOBAL_ENABLES   0x2e
+#define IPMI_CMD_GET_BMC_GLOBAL_ENABLES   0x2f
+#define IPMI_CMD_CLR_MSG_FLAGS            0x30
+#define IPMI_CMD_GET_MSG_FLAGS            0x31
+#define IPMI_CMD_GET_MSG                  0x33
+#define IPMI_CMD_SEND_MSG                 0x34
+#define IPMI_CMD_READ_EVT_MSG_BUF         0x35
+
+#define IPMI_NETFN_STORAGE            0x0a
+#define IPMI_NETFN_STORAGE_MAXCMD         0x4a
+
+#define IPMI_CMD_GET_SDR_REP_INFO         0x20
+#define IPMI_CMD_GET_SDR_REP_ALLOC_INFO   0x21
+#define IPMI_CMD_RESERVE_SDR_REP          0x22
+#define IPMI_CMD_GET_SDR                  0x23
+#define IPMI_CMD_ADD_SDR                  0x24
+#define IPMI_CMD_PARTIAL_ADD_SDR          0x25
+#define IPMI_CMD_DELETE_SDR               0x26
+#define IPMI_CMD_CLEAR_SDR_REP            0x27
+#define IPMI_CMD_GET_SDR_REP_TIME         0x28
+#define IPMI_CMD_SET_SDR_REP_TIME         0x29
+#define IPMI_CMD_ENTER_SDR_REP_UPD_MODE   0x2A
+#define IPMI_CMD_EXIT_SDR_REP_UPD_MODE    0x2B
+#define IPMI_CMD_RUN_INIT_AGENT           0x2C
+#define IPMI_CMD_GET_SEL_INFO             0x40
+#define IPMI_CMD_GET_SEL_ALLOC_INFO       0x41
+#define IPMI_CMD_RESERVE_SEL              0x42
+#define IPMI_CMD_GET_SEL_ENTRY            0x43
+#define IPMI_CMD_ADD_SEL_ENTRY            0x44
+#define IPMI_CMD_PARTIAL_ADD_SEL_ENTRY    0x45
+#define IPMI_CMD_DELETE_SEL_ENTRY         0x46
+#define IPMI_CMD_CLEAR_SEL                0x47
+#define IPMI_CMD_GET_SEL_TIME             0x48
+#define IPMI_CMD_SET_SEL_TIME             0x49
+
+
+/* Same as a timespec struct. */
+struct ipmi_time {
+    long tv_sec;
+    long tv_nsec;
+};
+
+#define MAX_SEL_SIZE 128
+
+typedef struct IPMISel {
+    uint8_t sel[MAX_SEL_SIZE][16];
+    unsigned int next_free;
+    long time_offset;
+    uint16_t reservation;
+    uint8_t last_addition[4];
+    uint8_t last_clear[4];
+    uint8_t overflow;
+} IPMISel;
+
+#define MAX_SDR_SIZE 16384
+
+typedef struct IPMISdr {
+    uint8_t sdr[MAX_SDR_SIZE];
+    unsigned int next_free;
+    uint16_t next_rec_id;
+    uint16_t reservation;
+    uint8_t last_addition[4];
+    uint8_t last_clear[4];
+    uint8_t overflow;
+} IPMISdr;
+
+typedef struct IPMISensor {
+    uint8_t status;
+    uint8_t reading;
+    uint16_t states_suppt;
+    uint16_t assert_suppt;
+    uint16_t deassert_suppt;
+    uint16_t states;
+    uint16_t assert_states;
+    uint16_t deassert_states;
+    uint16_t assert_enable;
+    uint16_t deassert_enable;
+    uint8_t  sensor_type;
+    uint8_t  evt_reading_type_code;
+} IPMISensor;
+#define IPMI_SENSOR_GET_PRESENT(s)       ((s)->status & 0x01)
+#define IPMI_SENSOR_SET_PRESENT(s, v)    ((s)->status = (s->status & ~0x01) | \
+                                             !!(v))
+#define IPMI_SENSOR_GET_SCAN_ON(s)       ((s)->status & 0x40)
+#define IPMI_SENSOR_SET_SCAN_ON(s, v)    ((s)->status = (s->status & ~0x40) | \
+                                             ((!!(v)) << 6))
+#define IPMI_SENSOR_GET_EVENTS_ON(s)     ((s)->status & 0x80)
+#define IPMI_SENSOR_SET_EVENTS_ON(s, v)  ((s)->status = (s->status & ~0x80) | \
+                                             ((!!(v)) << 7))
+#define IPMI_SENSOR_GET_RET_STATUS(s)    ((s)->status & 0xc0)
+#define IPMI_SENSOR_SET_RET_STATUS(s, v) ((s)->status = (s->status & ~0xc0) | \
+                                             (v & 0xc0))
+#define IPMI_SENSOR_IS_DISCRETE(s) ((s)->evt_reading_type_code != 1)
+
+#define MAX_SENSORS 20
+#define IPMI_WATCHDOG_SENSOR 0
+
+typedef struct IPMIBmcSim IPMIBmcSim;
+
+#define MAX_NETFNS 64
+typedef void (*IPMICmdHandler)(IPMIBmcSim *s,
+                               uint8_t *cmd, unsigned int cmd_len,
+                               uint8_t *rsp, unsigned int *rsp_len,
+                               unsigned int max_rsp_len);
+typedef struct IPMINetfn {
+    unsigned int cmd_nums;
+    const IPMICmdHandler *cmd_handlers;
+} IPMINetfn;
+
+typedef struct IPMIRcvBufEntry {
+    QTAILQ_ENTRY(IPMIRcvBufEntry) entry;
+    uint8_t len;
+    uint8_t buf[MAX_IPMI_MSG_SIZE];
+} IPMIRcvBufEntry;
+
+#define TYPE_IPMI_BMC_SIMULATOR "ipmi-bmc-sim"
+#define IPMI_BMC_SIMULATOR(obj) OBJECT_CHECK(IPMIBmcSim, (obj), \
+                                        TYPE_IPMI_BMC_SIMULATOR)
+struct IPMIBmcSim {
+    IPMIBmc parent;
+
+    QEMUTimer *timer;
+
+    uint8_t bmc_global_enables;
+    uint8_t msg_flags;
+
+    bool     watchdog_initialized;
+    uint8_t  watchdog_use;
+    uint8_t  watchdog_action;
+    uint8_t  watchdog_pretimeout; /* In seconds */
+    bool     watchdog_expired;
+    uint16_t watchdog_timeout; /* in 100's of milliseconds */
+
+    bool     watchdog_running;
+    bool     watchdog_preaction_ran;
+    int64_t  watchdog_expiry;
+
+    uint8_t device_id;
+    uint8_t ipmi_version;
+    uint8_t device_rev;
+    uint8_t fwrev1;
+    uint8_t fwrev2;
+    uint8_t mfg_id[3];
+    uint8_t product_id[2];
+
+    IPMISel sel;
+    IPMISdr sdr;
+    IPMISensor sensors[MAX_SENSORS];
+
+    /* Odd netfns are for responses, so we only need the even ones. */
+    const IPMINetfn *netfns[MAX_NETFNS / 2];
+
+    QemuMutex lock;
+    /* We allow one event in the buffer */
+    uint8_t evtbuf[16];
+
+    QTAILQ_HEAD(, IPMIRcvBufEntry) rcvbufs;
+};
+
+#define IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK        (1 << 3)
+#define IPMI_BMC_MSG_FLAG_EVT_BUF_FULL                 (1 << 1)
+#define IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE                (1 << 0)
+#define IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK_SET(s) \
+    (IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK & (s)->msg_flags)
+#define IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(s) \
+    (IPMI_BMC_MSG_FLAG_EVT_BUF_FULL & (s)->msg_flags)
+#define IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE_SET(s) \
+    (IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE & (s)->msg_flags)
+
+#define IPMI_BMC_RCV_MSG_QUEUE_INT_BIT    0
+#define IPMI_BMC_EVBUF_FULL_INT_BIT       1
+#define IPMI_BMC_EVENT_MSG_BUF_BIT        2
+#define IPMI_BMC_EVENT_LOG_BIT            3
+#define IPMI_BMC_MSG_INTS_ON(s) ((s)->bmc_global_enables & \
+                                 (1 << IPMI_BMC_RCV_MSG_QUEUE_INT_BIT))
+#define IPMI_BMC_EVBUF_FULL_INT_ENABLED(s) ((s)->bmc_global_enables & \
+                                        (1 << IPMI_BMC_EVBUF_FULL_INT_BIT))
+#define IPMI_BMC_EVENT_LOG_ENABLED(s) ((s)->bmc_global_enables & \
+                                       (1 << IPMI_BMC_EVENT_LOG_BIT))
+#define IPMI_BMC_EVENT_MSG_BUF_ENABLED(s) ((s)->bmc_global_enables & \
+                                           (1 << IPMI_BMC_EVENT_MSG_BUF_BIT))
+
+#define IPMI_BMC_WATCHDOG_USE_MASK 0xc7
+#define IPMI_BMC_WATCHDOG_ACTION_MASK 0x77
+#define IPMI_BMC_WATCHDOG_GET_USE(s) ((s)->watchdog_use & 0x7)
+#define IPMI_BMC_WATCHDOG_GET_DONT_LOG(s) (((s)->watchdog_use >> 7) & 0x1)
+#define IPMI_BMC_WATCHDOG_GET_DONT_STOP(s) (((s)->watchdog_use >> 6) & 0x1)
+#define IPMI_BMC_WATCHDOG_GET_PRE_ACTION(s) (((s)->watchdog_action >> 4) & 0x7)
+#define IPMI_BMC_WATCHDOG_PRE_NONE               0
+#define IPMI_BMC_WATCHDOG_PRE_SMI                1
+#define IPMI_BMC_WATCHDOG_PRE_NMI                2
+#define IPMI_BMC_WATCHDOG_PRE_MSG_INT            3
+#define IPMI_BMC_WATCHDOG_GET_ACTION(s) ((s)->watchdog_action & 0x7)
+#define IPMI_BMC_WATCHDOG_ACTION_NONE            0
+#define IPMI_BMC_WATCHDOG_ACTION_RESET           1
+#define IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN      2
+#define IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE     3
+
+
+/* Add a byte to the response. */
+#define IPMI_ADD_RSP_DATA(b) \
+    do {                                                   \
+        if (*rsp_len >= max_rsp_len) {                     \
+            rsp[2] = IPMI_CC_REQUEST_DATA_TRUNCATED;       \
+            goto out;                                      \
+        }                                                  \
+        rsp[(*rsp_len)++] = (b);                           \
+    } while (0)
+
+/* Verify that the received command is a certain length. */
+#define IPMI_CHECK_CMD_LEN(l) \
+    if (cmd_len < l) {                                     \
+        rsp[2] = IPMI_CC_REQUEST_DATA_LENGTH_INVALID;      \
+        goto out; \
+    }
+
+/* Check that the reservation in the command is valid. */
+#define IPMI_CHECK_RESERVATION(off, r) \
+    do {                                                   \
+        if ((cmd[off] | (cmd[off + 1] << 8)) != r) {       \
+            rsp[2] = IPMI_CC_INVALID_RESERVATION;          \
+            goto out;                                      \
+        }                                                  \
+    } while (0)
+
+
+static void ipmi_sim_handle_timeout(IPMIBmcSim *ibs);
+
+static void ipmi_gettime(struct ipmi_time *time)
+{
+    int64_t stime;
+
+    stime = qemu_clock_get_ns(QEMU_CLOCK_HOST);
+    time->tv_sec = stime / 1000000000LL;
+    time->tv_nsec = stime % 1000000000LL;
+}
+
+static int64_t ipmi_getmonotime(void)
+{
+    return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+}
+
+static void ipmi_timeout(void *opaque)
+{
+    IPMIBmcSim *ibs = opaque;
+
+    ipmi_sim_handle_timeout(ibs);
+}
+
+static void set_timestamp(IPMIBmcSim *ibs, uint8_t *ts)
+{
+    unsigned int val;
+    struct ipmi_time now;
+
+    ipmi_gettime(&now);
+    val = now.tv_sec + ibs->sel.time_offset;
+    ts[0] = val & 0xff;
+    ts[1] = (val >> 8) & 0xff;
+    ts[2] = (val >> 16) & 0xff;
+    ts[3] = (val >> 24) & 0xff;
+}
+
+static void sdr_inc_reservation(IPMISdr *sdr)
+{
+    sdr->reservation++;
+    if (sdr->reservation == 0) {
+        sdr->reservation = 1;
+    }
+}
+
+static int sdr_add_entry(IPMIBmcSim *ibs, const uint8_t *entry,
+                         unsigned int len, uint16_t *recid)
+{
+    if ((len < 5) || (len > 255)) {
+        return 1;
+    }
+
+    if (entry[4] != len - 5) {
+        return 1;
+    }
+
+    if (ibs->sdr.next_free + len > MAX_SDR_SIZE) {
+        ibs->sdr.overflow = 1;
+        return 1;
+    }
+
+    memcpy(ibs->sdr.sdr + ibs->sdr.next_free, entry, len);
+    ibs->sdr.sdr[ibs->sdr.next_free] = ibs->sdr.next_rec_id & 0xff;
+    ibs->sdr.sdr[ibs->sdr.next_free+1] = (ibs->sdr.next_rec_id >> 8) & 0xff;
+    ibs->sdr.sdr[ibs->sdr.next_free+2] = 0x51; /* Conform to IPMI 1.5 spec */
+
+    if (recid) {
+        *recid = ibs->sdr.next_rec_id;
+    }
+    ibs->sdr.next_rec_id++;
+    set_timestamp(ibs, ibs->sdr.last_addition);
+    ibs->sdr.next_free += len;
+    sdr_inc_reservation(&ibs->sdr);
+    return 0;
+}
+
+static int sdr_find_entry(IPMISdr *sdr, uint16_t recid,
+                          unsigned int *retpos, uint16_t *nextrec)
+{
+    unsigned int pos = *retpos;
+
+    while (pos < sdr->next_free) {
+        uint16_t trec = sdr->sdr[pos] | (sdr->sdr[pos + 1] << 8);
+        unsigned int nextpos = pos + sdr->sdr[pos + 4];
+
+        if (trec == recid) {
+            if (nextrec) {
+                if (nextpos >= sdr->next_free) {
+                    *nextrec = 0xffff;
+                } else {
+                    *nextrec = (sdr->sdr[nextpos] |
+                                (sdr->sdr[nextpos + 1] << 8));
+                }
+            }
+            *retpos = pos;
+            return 0;
+        }
+        pos = nextpos;
+    }
+    return 1;
+}
+
+static void sel_inc_reservation(IPMISel *sel)
+{
+    sel->reservation++;
+    if (sel->reservation == 0) {
+        sel->reservation = 1;
+    }
+}
+
+/* Returns 1 if the SEL is full and can't hold the event. */
+static int sel_add_event(IPMIBmcSim *ibs, uint8_t *event)
+{
+    event[0] = 0xff;
+    event[1] = 0xff;
+    set_timestamp(ibs, event + 3);
+    if (ibs->sel.next_free == MAX_SEL_SIZE) {
+        ibs->sel.overflow = 1;
+        return 1;
+    }
+    event[0] = ibs->sel.next_free & 0xff;
+    event[1] = (ibs->sel.next_free >> 8) & 0xff;
+    memcpy(ibs->sel.last_addition, event + 3, 4);
+    memcpy(ibs->sel.sel[ibs->sel.next_free], event, 16);
+    ibs->sel.next_free++;
+    sel_inc_reservation(&ibs->sel);
+    return 0;
+}
+
+static int attn_set(IPMIBmcSim *ibs)
+{
+    return IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE_SET(ibs)
+        || IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(ibs)
+        || IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK_SET(ibs);
+}
+
+static int attn_irq_enabled(IPMIBmcSim *ibs)
+{
+    return (IPMI_BMC_MSG_INTS_ON(ibs) && IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE_SET(ibs))
+        || (IPMI_BMC_EVBUF_FULL_INT_ENABLED(ibs) &&
+            IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(ibs));
+}
+
+static void gen_event(IPMIBmcSim *ibs, unsigned int sens_num, uint8_t deassert,
+                      uint8_t evd1, uint8_t evd2, uint8_t evd3)
+{
+    IPMIInterface *s = ibs->parent.intf;
+    IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+    uint8_t evt[16];
+    IPMISensor *sens = ibs->sensors + sens_num;
+
+    if (!IPMI_BMC_EVENT_MSG_BUF_ENABLED(ibs)) {
+        return;
+    }
+    if (!IPMI_SENSOR_GET_EVENTS_ON(sens)) {
+        return;
+    }
+
+    evt[2] = 0x2; /* System event record */
+    evt[7] = ibs->parent.slave_addr;
+    evt[8] = 0;
+    evt[9] = 0x04; /* Format version */
+    evt[10] = sens->sensor_type;
+    evt[11] = sens_num;
+    evt[12] = sens->evt_reading_type_code | (!!deassert << 7);
+    evt[13] = evd1;
+    evt[14] = evd2;
+    evt[15] = evd3;
+
+    if (IPMI_BMC_EVENT_LOG_ENABLED(ibs)) {
+        sel_add_event(ibs, evt);
+    }
+
+    if (ibs->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL) {
+        goto out;
+    }
+
+    memcpy(ibs->evtbuf, evt, 16);
+    ibs->msg_flags |= IPMI_BMC_MSG_FLAG_EVT_BUF_FULL;
+    k->set_atn(s, 1, attn_irq_enabled(ibs));
+ out:
+    return;
+}
+
+static void sensor_set_discrete_bit(IPMIBmcSim *ibs, unsigned int sensor,
+                                    unsigned int bit, unsigned int val,
+                                    uint8_t evd1, uint8_t evd2, uint8_t evd3)
+{
+    IPMISensor *sens;
+    uint16_t mask;
+
+    if (sensor >= MAX_SENSORS) {
+        return;
+    }
+    if (bit >= 16) {
+        return;
+    }
+
+    mask = (1 << bit);
+    sens = ibs->sensors + sensor;
+    if (val) {
+        sens->states |= mask & sens->states_suppt;
+        if (sens->assert_states & mask) {
+            return; /* Already asserted */
+        }
+        sens->assert_states |= mask & sens->assert_suppt;
+        if (sens->assert_enable & mask & sens->assert_states) {
+            /* Send an event on assert */
+            gen_event(ibs, sensor, 0, evd1, evd2, evd3);
+        }
+    } else {
+        sens->states &= ~(mask & sens->states_suppt);
+        if (sens->deassert_states & mask) {
+            return; /* Already deasserted */
+        }
+        sens->deassert_states |= mask & sens->deassert_suppt;
+        if (sens->deassert_enable & mask & sens->deassert_states) {
+            /* Send an event on deassert */
+            gen_event(ibs, sensor, 1, evd1, evd2, evd3);
+        }
+    }
+}
+
+static void ipmi_init_sensors_from_sdrs(IPMIBmcSim *s)
+{
+    unsigned int i, pos;
+    IPMISensor *sens;
+
+    for (i = 0; i < MAX_SENSORS; i++) {
+        memset(s->sensors + i, 0, sizeof(*sens));
+    }
+
+    pos = 0;
+    for (i = 0; !sdr_find_entry(&s->sdr, i, &pos, NULL); i++) {
+        uint8_t *sdr = s->sdr.sdr + pos;
+        unsigned int len = sdr[4];
+
+        if (len < 20) {
+            continue;
+        }
+        if ((sdr[3] < 1) || (sdr[3] > 2)) {
+            continue; /* Not a sensor SDR we set from */
+        }
+
+        if (sdr[7] > MAX_SENSORS) {
+            continue;
+        }
+        sens = s->sensors + sdr[7];
+
+        IPMI_SENSOR_SET_PRESENT(sens, 1);
+        IPMI_SENSOR_SET_SCAN_ON(sens, (sdr[10] >> 6) & 1);
+        IPMI_SENSOR_SET_EVENTS_ON(sens, (sdr[10] >> 5) & 1);
+        sens->assert_suppt = sdr[14] | (sdr[15] << 8);
+        sens->deassert_suppt = sdr[16] | (sdr[17] << 8);
+        sens->states_suppt = sdr[18] | (sdr[19] << 8);
+        sens->sensor_type = sdr[12];
+        sens->evt_reading_type_code = sdr[13] & 0x7f;
+
+        /* Enable all the events that are supported. */
+        sens->assert_enable = sens->assert_suppt;
+        sens->deassert_enable = sens->deassert_suppt;
+    }
+}
+
+static int ipmi_register_netfn(IPMIBmcSim *s, unsigned int netfn,
+                               const IPMINetfn *netfnd)
+{
+    if ((netfn & 1) || (netfn > MAX_NETFNS) || (s->netfns[netfn / 2])) {
+        return -1;
+    }
+    s->netfns[netfn / 2] = netfnd;
+    return 0;
+}
+
+static void next_timeout(IPMIBmcSim *ibs)
+{
+    int64_t next;
+    if (ibs->watchdog_running) {
+        next = ibs->watchdog_expiry;
+    } else {
+        /* Wait a minute */
+        next = ipmi_getmonotime() + 60 * 1000000000LL;
+    }
+    timer_mod_ns(ibs->timer, next);
+}
+
+static void ipmi_sim_handle_command(IPMIBmc *b,
+                                    uint8_t *cmd, unsigned int cmd_len,
+                                    unsigned int max_cmd_len,
+                                    uint8_t msg_id)
+{
+    IPMIBmcSim *ibs = IPMI_BMC_SIMULATOR(b);
+    IPMIInterface *s = ibs->parent.intf;
+    IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+    unsigned int netfn;
+    uint8_t rsp[MAX_IPMI_MSG_SIZE];
+    unsigned int rsp_len_holder = 0;
+    unsigned int *rsp_len = &rsp_len_holder;
+    unsigned int max_rsp_len = sizeof(rsp);
+
+    /* Set up the response, set the low bit of NETFN. */
+    /* Note that max_rsp_len must be at least 3 */
+    IPMI_ADD_RSP_DATA(cmd[0] | 0x04);
+    IPMI_ADD_RSP_DATA(cmd[1]);
+    IPMI_ADD_RSP_DATA(0); /* Assume success */
+
+    /* If it's too short or it was truncated, return an error. */
+    if (cmd_len < 2) {
+        rsp[2] = IPMI_CC_REQUEST_DATA_LENGTH_INVALID;
+        goto out;
+    }
+    if (cmd_len > max_cmd_len) {
+        rsp[2] = IPMI_CC_REQUEST_DATA_TRUNCATED;
+        goto out;
+    }
+
+    if ((cmd[0] & 0x03) != 0) {
+        /* Only have stuff on LUN 0 */
+        rsp[2] = IPMI_CC_COMMAND_INVALID_FOR_LUN;
+        goto out;
+    }
+
+    netfn = cmd[0] >> 2;
+
+    /* Odd netfns are not valid, make sure the command is registered */
+    if ((netfn & 1) || !ibs->netfns[netfn / 2] ||
+                        (cmd[1] >= ibs->netfns[netfn / 2]->cmd_nums) ||
+                        (!ibs->netfns[netfn / 2]->cmd_handlers[cmd[1]])) {
+        rsp[2] = IPMI_CC_INVALID_CMD;
+        goto out;
+    }
+
+    ibs->netfns[netfn / 2]->cmd_handlers[cmd[1]](ibs, cmd, cmd_len, rsp, rsp_len,
+                                                max_rsp_len);
+
+ out:
+    k->handle_rsp(s, msg_id, rsp, *rsp_len);
+
+    next_timeout(ibs);
+}
+
+static void ipmi_sim_handle_timeout(IPMIBmcSim *ibs)
+{
+    IPMIInterface *s = ibs->parent.intf;
+    IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+
+    if (!ibs->watchdog_running) {
+        goto out;
+    }
+
+    if (!ibs->watchdog_preaction_ran) {
+        switch (IPMI_BMC_WATCHDOG_GET_PRE_ACTION(ibs)) {
+        case IPMI_BMC_WATCHDOG_PRE_NMI:
+            ibs->msg_flags |= IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK;
+            k->do_hw_op(s, IPMI_SEND_NMI, 0);
+            sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 8, 1,
+                                    0xc8, (2 << 4) | 0xf, 0xff);
+            break;
+
+        case IPMI_BMC_WATCHDOG_PRE_MSG_INT:
+            ibs->msg_flags |= IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK;
+            k->set_atn(s, 1, attn_irq_enabled(ibs));
+            sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 8, 1,
+                                    0xc8, (3 << 4) | 0xf, 0xff);
+            break;
+
+        default:
+            goto do_full_expiry;
+        }
+
+        ibs->watchdog_preaction_ran = 1;
+        /* Issued the pretimeout, do the rest of the timeout now. */
+        ibs->watchdog_expiry = ipmi_getmonotime();
+        ibs->watchdog_expiry += ibs->watchdog_pretimeout * 1000000000LL;
+        goto out;
+    }
+
+ do_full_expiry:
+    ibs->watchdog_running = 0; /* Stop the watchdog on a timeout */
+    ibs->watchdog_expired |= (1 << IPMI_BMC_WATCHDOG_GET_USE(ibs));
+    switch (IPMI_BMC_WATCHDOG_GET_ACTION(ibs)) {
+    case IPMI_BMC_WATCHDOG_ACTION_NONE:
+        sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 0, 1,
+                                0xc0, ibs->watchdog_use & 0xf, 0xff);
+        break;
+
+    case IPMI_BMC_WATCHDOG_ACTION_RESET:
+        sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 1, 1,
+                                0xc1, ibs->watchdog_use & 0xf, 0xff);
+        k->do_hw_op(s, IPMI_RESET_CHASSIS, 0);
+        break;
+
+    case IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN:
+        sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 2, 1,
+                                0xc2, ibs->watchdog_use & 0xf, 0xff);
+        k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0);
+        break;
+
+    case IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE:
+        sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 2, 1,
+                                0xc3, ibs->watchdog_use & 0xf, 0xff);
+        k->do_hw_op(s, IPMI_POWERCYCLE_CHASSIS, 0);
+        break;
+    }
+
+ out:
+    next_timeout(ibs);
+}
+
+static void chassis_capabilities(IPMIBmcSim *ibs,
+                                 uint8_t *cmd, unsigned int cmd_len,
+                                 uint8_t *rsp, unsigned int *rsp_len,
+                                 unsigned int max_rsp_len)
+{
+    IPMI_ADD_RSP_DATA(0);
+    IPMI_ADD_RSP_DATA(ibs->parent.slave_addr);
+    IPMI_ADD_RSP_DATA(ibs->parent.slave_addr);
+    IPMI_ADD_RSP_DATA(ibs->parent.slave_addr);
+    IPMI_ADD_RSP_DATA(ibs->parent.slave_addr);
+ out:
+    return;
+}
+
+static void chassis_status(IPMIBmcSim *ibs,
+                           uint8_t *cmd, unsigned int cmd_len,
+                           uint8_t *rsp, unsigned int *rsp_len,
+                           unsigned int max_rsp_len)
+{
+    IPMI_ADD_RSP_DATA(0x61); /* Unknown power restore, power is on */
+    IPMI_ADD_RSP_DATA(0);
+    IPMI_ADD_RSP_DATA(0);
+    IPMI_ADD_RSP_DATA(0);
+ out:
+    return;
+}
+
+static void chassis_control(IPMIBmcSim *ibs,
+                            uint8_t *cmd, unsigned int cmd_len,
+                            uint8_t *rsp, unsigned int *rsp_len,
+                            unsigned int max_rsp_len)
+{
+    IPMIInterface *s = ibs->parent.intf;
+    IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+
+    IPMI_CHECK_CMD_LEN(3);
+    switch (cmd[2] & 0xf) {
+    case 0: /* power down */
+        rsp[2] = k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0);
+        break;
+    case 1: /* power up */
+        rsp[2] = k->do_hw_op(s, IPMI_POWERON_CHASSIS, 0);
+        break;
+    case 2: /* power cycle */
+        rsp[2] = k->do_hw_op(s, IPMI_POWERCYCLE_CHASSIS, 0);
+        break;
+    case 3: /* hard reset */
+        rsp[2] = k->do_hw_op(s, IPMI_RESET_CHASSIS, 0);
+        break;
+    case 4: /* pulse diagnostic interrupt */
+        rsp[2] = k->do_hw_op(s, IPMI_PULSE_DIAG_IRQ, 0);
+        break;
+    case 5: /* soft shutdown via ACPI by overtemp emulation */
+        rsp[2] = k->do_hw_op(s,
+                             IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP, 0);
+        break;
+    default:
+        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+        goto out;
+    }
+ out:
+    return;
+}
+
+static void get_device_id(IPMIBmcSim *ibs,
+                          uint8_t *cmd, unsigned int cmd_len,
+                          uint8_t *rsp, unsigned int *rsp_len,
+                          unsigned int max_rsp_len)
+{
+    IPMI_ADD_RSP_DATA(ibs->device_id);
+    IPMI_ADD_RSP_DATA(ibs->device_rev & 0xf);
+    IPMI_ADD_RSP_DATA(ibs->fwrev1 & 0x7f);
+    IPMI_ADD_RSP_DATA(ibs->fwrev2);
+    IPMI_ADD_RSP_DATA(ibs->ipmi_version);
+    IPMI_ADD_RSP_DATA(0x07); /* sensor, SDR, and SEL. */
+    IPMI_ADD_RSP_DATA(ibs->mfg_id[0]);
+    IPMI_ADD_RSP_DATA(ibs->mfg_id[1]);
+    IPMI_ADD_RSP_DATA(ibs->mfg_id[2]);
+    IPMI_ADD_RSP_DATA(ibs->product_id[0]);
+    IPMI_ADD_RSP_DATA(ibs->product_id[1]);
+ out:
+    return;
+}
+
+static void set_global_enables(IPMIBmcSim *ibs, uint8_t val)
+{
+    IPMIInterface *s = ibs->parent.intf;
+    IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+    bool irqs_on;
+
+    ibs->bmc_global_enables = val;
+
+    irqs_on = val & (IPMI_BMC_EVBUF_FULL_INT_BIT |
+                     IPMI_BMC_RCV_MSG_QUEUE_INT_BIT);
+
+    k->set_irq_enable(s, irqs_on);
+}
+
+static void cold_reset(IPMIBmcSim *ibs,
+                       uint8_t *cmd, unsigned int cmd_len,
+                       uint8_t *rsp, unsigned int *rsp_len,
+                       unsigned int max_rsp_len)
+{
+    IPMIInterface *s = ibs->parent.intf;
+    IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+
+    /* Disable all interrupts */
+    set_global_enables(ibs, 1 << IPMI_BMC_EVENT_LOG_BIT);
+
+    if (k->reset) {
+        k->reset(s, true);
+    }
+}
+
+static void warm_reset(IPMIBmcSim *ibs,
+                       uint8_t *cmd, unsigned int cmd_len,
+                       uint8_t *rsp, unsigned int *rsp_len,
+                       unsigned int max_rsp_len)
+{
+    IPMIInterface *s = ibs->parent.intf;
+    IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+
+    if (k->reset) {
+        k->reset(s, false);
+    }
+}
+
+static void set_bmc_global_enables(IPMIBmcSim *ibs,
+                                   uint8_t *cmd, unsigned int cmd_len,
+                                   uint8_t *rsp, unsigned int *rsp_len,
+                                   unsigned int max_rsp_len)
+{
+    IPMI_CHECK_CMD_LEN(3);
+    set_global_enables(ibs, cmd[2]);
+ out:
+    return;
+}
+
+static void get_bmc_global_enables(IPMIBmcSim *ibs,
+                                   uint8_t *cmd, unsigned int cmd_len,
+                                   uint8_t *rsp, unsigned int *rsp_len,
+                                   unsigned int max_rsp_len)
+{
+    IPMI_ADD_RSP_DATA(ibs->bmc_global_enables);
+ out:
+    return;
+}
+
+static void clr_msg_flags(IPMIBmcSim *ibs,
+                          uint8_t *cmd, unsigned int cmd_len,
+                          uint8_t *rsp, unsigned int *rsp_len,
+                          unsigned int max_rsp_len)
+{
+    IPMIInterface *s = ibs->parent.intf;
+    IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+
+    IPMI_CHECK_CMD_LEN(3);
+    ibs->msg_flags &= ~cmd[2];
+    k->set_atn(s, attn_set(ibs), attn_irq_enabled(ibs));
+ out:
+    return;
+}
+
+static void get_msg_flags(IPMIBmcSim *ibs,
+                          uint8_t *cmd, unsigned int cmd_len,
+                          uint8_t *rsp, unsigned int *rsp_len,
+                          unsigned int max_rsp_len)
+{
+    IPMI_ADD_RSP_DATA(ibs->msg_flags);
+ out:
+    return;
+}
+
+static void read_evt_msg_buf(IPMIBmcSim *ibs,
+                             uint8_t *cmd, unsigned int cmd_len,
+                             uint8_t *rsp, unsigned int *rsp_len,
+                            unsigned int max_rsp_len)
+{
+    IPMIInterface *s = ibs->parent.intf;
+    IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+    unsigned int i;
+
+    if (!(ibs->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL)) {
+        rsp[2] = 0x80;
+        goto out;
+    }
+    for (i = 0; i < 16; i++) {
+        IPMI_ADD_RSP_DATA(ibs->evtbuf[i]);
+    }
+    ibs->msg_flags &= ~IPMI_BMC_MSG_FLAG_EVT_BUF_FULL;
+    k->set_atn(s, attn_set(ibs), attn_irq_enabled(ibs));
+ out:
+    return;
+}
+
+static void get_msg(IPMIBmcSim *ibs,
+                    uint8_t *cmd, unsigned int cmd_len,
+                    uint8_t *rsp, unsigned int *rsp_len,
+                    unsigned int max_rsp_len)
+{
+    IPMIRcvBufEntry *msg;
+
+    qemu_mutex_lock(&ibs->lock);
+    if (QTAILQ_EMPTY(&ibs->rcvbufs)) {
+        rsp[2] = 0x80; /* Queue empty */
+        goto out;
+    }
+    rsp[3] = 0; /* Channel 0 */
+    *rsp_len += 1;
+    msg = QTAILQ_FIRST(&ibs->rcvbufs);
+    memcpy(rsp + 4, msg->buf, msg->len);
+    *rsp_len += msg->len;
+    QTAILQ_REMOVE(&ibs->rcvbufs, msg, entry);
+    g_free(msg);
+
+    if (QTAILQ_EMPTY(&ibs->rcvbufs)) {
+        IPMIInterface *s = ibs->parent.intf;
+        IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+
+        ibs->msg_flags &= ~IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE;
+        k->set_atn(s, attn_set(ibs), attn_irq_enabled(ibs));
+    }
+
+ out:
+    qemu_mutex_unlock(&ibs->lock);
+    return;
+}
+
+static unsigned char
+ipmb_checksum(unsigned char *data, int size, unsigned char csum)
+{
+    for (; size > 0; size--, data++) {
+            csum += *data;
+    }
+
+    return -csum;
+}
+
+static void send_msg(IPMIBmcSim *ibs,
+                     uint8_t *cmd, unsigned int cmd_len,
+                     uint8_t *rsp, unsigned int *rsp_len,
+                     unsigned int max_rsp_len)
+{
+    IPMIInterface *s = ibs->parent.intf;
+    IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+    IPMIRcvBufEntry *msg;
+    uint8_t *buf;
+    uint8_t netfn, rqLun, rsLun, rqSeq;
+
+    IPMI_CHECK_CMD_LEN(3);
+
+    if (cmd[2] != 0) {
+        /* We only handle channel 0 with no options */
+        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+        goto out;
+    }
+
+    IPMI_CHECK_CMD_LEN(10);
+    if (cmd[3] != 0x40) {
+        /* We only emulate a MC at address 0x40. */
+        rsp[2] = 0x83; /* NAK on write */
+        goto out;
+    }
+
+    cmd += 3; /* Skip the header. */
+    cmd_len -= 3;
+
+    /*
+     * At this point we "send" the message successfully.  Any error will
+     * be returned in the response.
+     */
+    if (ipmb_checksum(cmd, cmd_len, 0) != 0 ||
+        cmd[3] != 0x20) { /* Improper response address */
+        goto out; /* No response */
+    }
+
+    netfn = cmd[1] >> 2;
+    rqLun = cmd[4] & 0x3;
+    rsLun = cmd[1] & 0x3;
+    rqSeq = cmd[4] >> 2;
+
+    if (rqLun != 2) {
+        /* We only support LUN 2 coming back to us. */
+        goto out;
+    }
+
+    msg = g_malloc(sizeof(*msg));
+    msg->buf[0] = ((netfn | 1) << 2) | rqLun; /* NetFN, and make a response */
+    msg->buf[1] = ipmb_checksum(msg->buf, 1, 0);
+    msg->buf[2] = cmd[0]; /* rsSA */
+    msg->buf[3] = (rqSeq << 2) | rsLun;
+    msg->buf[4] = cmd[5]; /* Cmd */
+    msg->buf[5] = 0; /* Completion Code */
+    msg->len = 6;
+
+    if ((cmd[1] >> 2) != IPMI_NETFN_APP || cmd[5] != IPMI_CMD_GET_DEVICE_ID) {
+        /* Not a command we handle. */
+        msg->buf[5] = IPMI_CC_INVALID_CMD;
+        goto end_msg;
+    }
+
+    buf = msg->buf + msg->len; /* After the CC */
+    buf[0] = 0;
+    buf[1] = 0;
+    buf[2] = 0;
+    buf[3] = 0;
+    buf[4] = 0x51;
+    buf[5] = 0;
+    buf[6] = 0;
+    buf[7] = 0;
+    buf[8] = 0;
+    buf[9] = 0;
+    buf[10] = 0;
+    msg->len += 11;
+
+ end_msg:
+    msg->buf[msg->len] = ipmb_checksum(msg->buf, msg->len, 0);
+    msg->len++;
+    qemu_mutex_lock(&ibs->lock);
+    QTAILQ_INSERT_TAIL(&ibs->rcvbufs, msg, entry);
+    ibs->msg_flags |= IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE;
+    k->set_atn(s, 1, attn_irq_enabled(ibs));
+    qemu_mutex_unlock(&ibs->lock);
+
+ out:
+    return;
+}
+
+static void do_watchdog_reset(IPMIBmcSim *ibs)
+{
+    if (IPMI_BMC_WATCHDOG_GET_ACTION(ibs) ==
+        IPMI_BMC_WATCHDOG_ACTION_NONE) {
+        ibs->watchdog_running = 0;
+        return;
+    }
+    ibs->watchdog_preaction_ran = 0;
+
+
+    /* Timeout is in tenths of a second, offset is in seconds */
+    ibs->watchdog_expiry = ipmi_getmonotime();
+    ibs->watchdog_expiry += ibs->watchdog_timeout * 100000000LL;
+    if (IPMI_BMC_WATCHDOG_GET_PRE_ACTION(ibs) != IPMI_BMC_WATCHDOG_PRE_NONE) {
+        ibs->watchdog_expiry -= ibs->watchdog_pretimeout * 1000000000LL;
+    }
+    ibs->watchdog_running = 1;
+}
+
+static void reset_watchdog_timer(IPMIBmcSim *ibs,
+                                 uint8_t *cmd, unsigned int cmd_len,
+                                 uint8_t *rsp, unsigned int *rsp_len,
+                                 unsigned int max_rsp_len)
+{
+    if (!ibs->watchdog_initialized) {
+        rsp[2] = 0x80;
+        goto out;
+    }
+    do_watchdog_reset(ibs);
+ out:
+    return;
+}
+
+static void set_watchdog_timer(IPMIBmcSim *ibs,
+                               uint8_t *cmd, unsigned int cmd_len,
+                               uint8_t *rsp, unsigned int *rsp_len,
+                               unsigned int max_rsp_len)
+{
+    IPMIInterface *s = ibs->parent.intf;
+    IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+    unsigned int val;
+
+    IPMI_CHECK_CMD_LEN(8);
+    val = cmd[2] & 0x7; /* Validate use */
+    if (val == 0 || val > 5) {
+        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+        goto out;
+    }
+    val = cmd[3] & 0x7; /* Validate action */
+    switch (val) {
+    case IPMI_BMC_WATCHDOG_ACTION_NONE:
+        break;
+
+    case IPMI_BMC_WATCHDOG_ACTION_RESET:
+        rsp[2] = k->do_hw_op(s, IPMI_RESET_CHASSIS, 1);
+        break;
+
+    case IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN:
+        rsp[2] = k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 1);
+        break;
+
+    case IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE:
+        rsp[2] = k->do_hw_op(s, IPMI_POWERCYCLE_CHASSIS, 1);
+        break;
+
+    default:
+        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+    }
+    if (rsp[2]) {
+        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+        goto out;
+    }
+
+    val = (cmd[3] >> 4) & 0x7; /* Validate preaction */
+    switch (val) {
+    case IPMI_BMC_WATCHDOG_PRE_MSG_INT:
+    case IPMI_BMC_WATCHDOG_PRE_NONE:
+        break;
+
+    case IPMI_BMC_WATCHDOG_PRE_NMI:
+        if (!k->do_hw_op(s, IPMI_SEND_NMI, 1)) {
+            /* NMI not supported. */
+            rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+            goto out;
+        }
+    default:
+        /* We don't support PRE_SMI */
+        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+        goto out;
+    }
+
+    ibs->watchdog_initialized = 1;
+    ibs->watchdog_use = cmd[2] & IPMI_BMC_WATCHDOG_USE_MASK;
+    ibs->watchdog_action = cmd[3] & IPMI_BMC_WATCHDOG_ACTION_MASK;
+    ibs->watchdog_pretimeout = cmd[4];
+    ibs->watchdog_expired &= ~cmd[5];
+    ibs->watchdog_timeout = cmd[6] | (((uint16_t) cmd[7]) << 8);
+    if (ibs->watchdog_running & IPMI_BMC_WATCHDOG_GET_DONT_STOP(ibs)) {
+        do_watchdog_reset(ibs);
+    } else {
+        ibs->watchdog_running = 0;
+    }
+ out:
+    return;
+}
+
+static void get_watchdog_timer(IPMIBmcSim *ibs,
+                               uint8_t *cmd, unsigned int cmd_len,
+                               uint8_t *rsp, unsigned int *rsp_len,
+                               unsigned int max_rsp_len)
+{
+    IPMI_ADD_RSP_DATA(ibs->watchdog_use);
+    IPMI_ADD_RSP_DATA(ibs->watchdog_action);
+    IPMI_ADD_RSP_DATA(ibs->watchdog_pretimeout);
+    IPMI_ADD_RSP_DATA(ibs->watchdog_expired);
+    if (ibs->watchdog_running) {
+        long timeout;
+        timeout = ((ibs->watchdog_expiry - ipmi_getmonotime() + 50000000)
+                   / 100000000);
+        IPMI_ADD_RSP_DATA(timeout & 0xff);
+        IPMI_ADD_RSP_DATA((timeout >> 8) & 0xff);
+    } else {
+        IPMI_ADD_RSP_DATA(0);
+        IPMI_ADD_RSP_DATA(0);
+    }
+ out:
+    return;
+}
+
+static void get_sdr_rep_info(IPMIBmcSim *ibs,
+                             uint8_t *cmd, unsigned int cmd_len,
+                             uint8_t *rsp, unsigned int *rsp_len,
+                             unsigned int max_rsp_len)
+{
+    unsigned int i;
+
+    IPMI_ADD_RSP_DATA(0x51); /* Conform to IPMI 1.5 spec */
+    IPMI_ADD_RSP_DATA(ibs->sdr.next_rec_id & 0xff);
+    IPMI_ADD_RSP_DATA((ibs->sdr.next_rec_id >> 8) & 0xff);
+    IPMI_ADD_RSP_DATA((MAX_SDR_SIZE - ibs->sdr.next_free) & 0xff);
+    IPMI_ADD_RSP_DATA(((MAX_SDR_SIZE - ibs->sdr.next_free) >> 8) & 0xff);
+    for (i = 0; i < 4; i++) {
+        IPMI_ADD_RSP_DATA(ibs->sdr.last_addition[i]);
+    }
+    for (i = 0; i < 4; i++) {
+        IPMI_ADD_RSP_DATA(ibs->sdr.last_clear[i]);
+    }
+    /* Only modal support, reserve supported */
+    IPMI_ADD_RSP_DATA((ibs->sdr.overflow << 7) | 0x22);
+ out:
+    return;
+}
+
+static void reserve_sdr_rep(IPMIBmcSim *ibs,
+                            uint8_t *cmd, unsigned int cmd_len,
+                            uint8_t *rsp, unsigned int *rsp_len,
+                            unsigned int max_rsp_len)
+{
+    IPMI_ADD_RSP_DATA(ibs->sdr.reservation & 0xff);
+    IPMI_ADD_RSP_DATA((ibs->sdr.reservation >> 8) & 0xff);
+ out:
+    return;
+}
+
+static void get_sdr(IPMIBmcSim *ibs,
+                    uint8_t *cmd, unsigned int cmd_len,
+                    uint8_t *rsp, unsigned int *rsp_len,
+                    unsigned int max_rsp_len)
+{
+    unsigned int pos;
+    uint16_t nextrec;
+
+    IPMI_CHECK_CMD_LEN(8);
+    if (cmd[6]) {
+        IPMI_CHECK_RESERVATION(2, ibs->sdr.reservation);
+    }
+    pos = 0;
+    if (sdr_find_entry(&ibs->sdr, cmd[4] | (cmd[5] << 8),
+                       &pos, &nextrec)) {
+        rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
+        goto out;
+    }
+    if (cmd[6] > (ibs->sdr.sdr[pos + 4])) {
+        rsp[2] = IPMI_CC_PARM_OUT_OF_RANGE;
+        goto out;
+    }
+
+    IPMI_ADD_RSP_DATA(nextrec & 0xff);
+    IPMI_ADD_RSP_DATA((nextrec >> 8) & 0xff);
+
+    if (cmd[7] == 0xff) {
+        cmd[7] = ibs->sdr.sdr[pos + 4] - cmd[6];
+    }
+
+    if ((cmd[7] + *rsp_len) > max_rsp_len) {
+        rsp[2] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES;
+        goto out;
+    }
+    memcpy(rsp + *rsp_len, ibs->sdr.sdr + pos + cmd[6], cmd[7]);
+    *rsp_len += cmd[7];
+ out:
+    return;
+}
+
+static void add_sdr(IPMIBmcSim *ibs,
+                    uint8_t *cmd, unsigned int cmd_len,
+                    uint8_t *rsp, unsigned int *rsp_len,
+                    unsigned int max_rsp_len)
+{
+    uint16_t recid;
+
+    if (sdr_add_entry(ibs, cmd + 2, cmd_len - 2, &recid)) {
+        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+        goto out;
+    }
+    IPMI_ADD_RSP_DATA(recid & 0xff);
+    IPMI_ADD_RSP_DATA((recid >> 8) & 0xff);
+ out:
+    return;
+}
+
+static void clear_sdr_rep(IPMIBmcSim *ibs,
+                          uint8_t *cmd, unsigned int cmd_len,
+                          uint8_t *rsp, unsigned int *rsp_len,
+                          unsigned int max_rsp_len)
+{
+    IPMI_CHECK_CMD_LEN(8);
+    IPMI_CHECK_RESERVATION(2, ibs->sdr.reservation);
+    if (cmd[4] != 'C' || cmd[5] != 'L' || cmd[6] != 'R') {
+        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+        goto out;
+    }
+    if (cmd[7] == 0xaa) {
+        ibs->sdr.next_free = 0;
+        ibs->sdr.overflow = 0;
+        set_timestamp(ibs, ibs->sdr.last_clear);
+        IPMI_ADD_RSP_DATA(1); /* Erasure complete */
+        sdr_inc_reservation(&ibs->sdr);
+    } else if (cmd[7] == 0) {
+        IPMI_ADD_RSP_DATA(1); /* Erasure complete */
+    } else {
+        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+        goto out;
+    }
+ out:
+    return;
+}
+
+static void get_sel_info(IPMIBmcSim *ibs,
+                         uint8_t *cmd, unsigned int cmd_len,
+                         uint8_t *rsp, unsigned int *rsp_len,
+                         unsigned int max_rsp_len)
+{
+    unsigned int i, val;
+
+    IPMI_ADD_RSP_DATA(0x51); /* Conform to IPMI 1.5 */
+    IPMI_ADD_RSP_DATA(ibs->sel.next_free & 0xff);
+    IPMI_ADD_RSP_DATA((ibs->sel.next_free >> 8) & 0xff);
+    val = (MAX_SEL_SIZE - ibs->sel.next_free) * 16;
+    IPMI_ADD_RSP_DATA(val & 0xff);
+    IPMI_ADD_RSP_DATA((val >> 8) & 0xff);
+    for (i = 0; i < 4; i++) {
+        IPMI_ADD_RSP_DATA(ibs->sel.last_addition[i]);
+    }
+    for (i = 0; i < 4; i++) {
+        IPMI_ADD_RSP_DATA(ibs->sel.last_clear[i]);
+    }
+    /* Only support Reserve SEL */
+    IPMI_ADD_RSP_DATA((ibs->sel.overflow << 7) | 0x02);
+ out:
+    return;
+}
+
+static void reserve_sel(IPMIBmcSim *ibs,
+                        uint8_t *cmd, unsigned int cmd_len,
+                        uint8_t *rsp, unsigned int *rsp_len,
+                        unsigned int max_rsp_len)
+{
+    IPMI_ADD_RSP_DATA(ibs->sel.reservation & 0xff);
+    IPMI_ADD_RSP_DATA((ibs->sel.reservation >> 8) & 0xff);
+ out:
+    return;
+}
+
+static void get_sel_entry(IPMIBmcSim *ibs,
+                          uint8_t *cmd, unsigned int cmd_len,
+                          uint8_t *rsp, unsigned int *rsp_len,
+                          unsigned int max_rsp_len)
+{
+    unsigned int val;
+
+    IPMI_CHECK_CMD_LEN(8);
+    if (cmd[6]) {
+        IPMI_CHECK_RESERVATION(2, ibs->sel.reservation);
+    }
+    if (ibs->sel.next_free == 0) {
+        rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
+        goto out;
+    }
+    if (cmd[6] > 15) {
+        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+        goto out;
+    }
+    if (cmd[7] == 0xff) {
+        cmd[7] = 16;
+    } else if ((cmd[7] + cmd[6]) > 16) {
+        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+        goto out;
+    } else {
+        cmd[7] += cmd[6];
+    }
+
+    val = cmd[4] | (cmd[5] << 8);
+    if (val == 0xffff) {
+        val = ibs->sel.next_free - 1;
+    } else if (val >= ibs->sel.next_free) {
+        rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
+        goto out;
+    }
+    if ((val + 1) == ibs->sel.next_free) {
+        IPMI_ADD_RSP_DATA(0xff);
+        IPMI_ADD_RSP_DATA(0xff);
+    } else {
+        IPMI_ADD_RSP_DATA((val + 1) & 0xff);
+        IPMI_ADD_RSP_DATA(((val + 1) >> 8) & 0xff);
+    }
+    for (; cmd[6] < cmd[7]; cmd[6]++) {
+        IPMI_ADD_RSP_DATA(ibs->sel.sel[val][cmd[6]]);
+    }
+ out:
+    return;
+}
+
+static void add_sel_entry(IPMIBmcSim *ibs,
+                          uint8_t *cmd, unsigned int cmd_len,
+                          uint8_t *rsp, unsigned int *rsp_len,
+                          unsigned int max_rsp_len)
+{
+    IPMI_CHECK_CMD_LEN(18);
+    if (sel_add_event(ibs, cmd + 2)) {
+        rsp[2] = IPMI_CC_OUT_OF_SPACE;
+        goto out;
+    }
+    /* sel_add_event fills in the record number. */
+    IPMI_ADD_RSP_DATA(cmd[2]);
+    IPMI_ADD_RSP_DATA(cmd[3]);
+ out:
+    return;
+}
+
+static void clear_sel(IPMIBmcSim *ibs,
+                      uint8_t *cmd, unsigned int cmd_len,
+                      uint8_t *rsp, unsigned int *rsp_len,
+                      unsigned int max_rsp_len)
+{
+    IPMI_CHECK_CMD_LEN(8);
+    IPMI_CHECK_RESERVATION(2, ibs->sel.reservation);
+    if (cmd[4] != 'C' || cmd[5] != 'L' || cmd[6] != 'R') {
+        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+        goto out;
+    }
+    if (cmd[7] == 0xaa) {
+        ibs->sel.next_free = 0;
+        ibs->sel.overflow = 0;
+        set_timestamp(ibs, ibs->sdr.last_clear);
+        IPMI_ADD_RSP_DATA(1); /* Erasure complete */
+        sel_inc_reservation(&ibs->sel);
+    } else if (cmd[7] == 0) {
+        IPMI_ADD_RSP_DATA(1); /* Erasure complete */
+    } else {
+        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+        goto out;
+    }
+ out:
+    return;
+}
+
+static void get_sel_time(IPMIBmcSim *ibs,
+                         uint8_t *cmd, unsigned int cmd_len,
+                         uint8_t *rsp, unsigned int *rsp_len,
+                         unsigned int max_rsp_len)
+{
+    uint32_t val;
+    struct ipmi_time now;
+
+    ipmi_gettime(&now);
+    val = now.tv_sec + ibs->sel.time_offset;
+    IPMI_ADD_RSP_DATA(val & 0xff);
+    IPMI_ADD_RSP_DATA((val >> 8) & 0xff);
+    IPMI_ADD_RSP_DATA((val >> 16) & 0xff);
+    IPMI_ADD_RSP_DATA((val >> 24) & 0xff);
+ out:
+    return;
+}
+
+static void set_sel_time(IPMIBmcSim *ibs,
+                         uint8_t *cmd, unsigned int cmd_len,
+                         uint8_t *rsp, unsigned int *rsp_len,
+                         unsigned int max_rsp_len)
+{
+    uint32_t val;
+    struct ipmi_time now;
+
+    IPMI_CHECK_CMD_LEN(6);
+    val = cmd[2] | (cmd[3] << 8) | (cmd[4] << 16) | (cmd[5] << 24);
+    ipmi_gettime(&now);
+    ibs->sel.time_offset = now.tv_sec - ((long) val);
+ out:
+    return;
+}
+
+static void set_sensor_evt_enable(IPMIBmcSim *ibs,
+                                  uint8_t *cmd, unsigned int cmd_len,
+                                  uint8_t *rsp, unsigned int *rsp_len,
+                                  unsigned int max_rsp_len)
+{
+    IPMISensor *sens;
+
+    IPMI_CHECK_CMD_LEN(4);
+    if ((cmd[2] > MAX_SENSORS) ||
+            !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
+        rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
+        goto out;
+    }
+    sens = ibs->sensors + cmd[2];
+    switch ((cmd[3] >> 4) & 0x3) {
+    case 0: /* Do not change */
+        break;
+    case 1: /* Enable bits */
+        if (cmd_len > 4) {
+            sens->assert_enable |= cmd[4];
+        }
+        if (cmd_len > 5) {
+            sens->assert_enable |= cmd[5] << 8;
+        }
+        if (cmd_len > 6) {
+            sens->deassert_enable |= cmd[6];
+        }
+        if (cmd_len > 7) {
+            sens->deassert_enable |= cmd[7] << 8;
+        }
+        break;
+    case 2: /* Disable bits */
+        if (cmd_len > 4) {
+            sens->assert_enable &= ~cmd[4];
+        }
+        if (cmd_len > 5) {
+            sens->assert_enable &= ~(cmd[5] << 8);
+        }
+        if (cmd_len > 6) {
+            sens->deassert_enable &= ~cmd[6];
+        }
+        if (cmd_len > 7) {
+            sens->deassert_enable &= ~(cmd[7] << 8);
+        }
+        break;
+    case 3:
+        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+        goto out;
+    }
+    IPMI_SENSOR_SET_RET_STATUS(sens, cmd[3]);
+ out:
+    return;
+}
+
+static void get_sensor_evt_enable(IPMIBmcSim *ibs,
+                                  uint8_t *cmd, unsigned int cmd_len,
+                                  uint8_t *rsp, unsigned int *rsp_len,
+                                  unsigned int max_rsp_len)
+{
+    IPMISensor *sens;
+
+    IPMI_CHECK_CMD_LEN(3);
+    if ((cmd[2] > MAX_SENSORS) ||
+        !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
+        rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
+        goto out;
+    }
+    sens = ibs->sensors + cmd[2];
+    IPMI_ADD_RSP_DATA(IPMI_SENSOR_GET_RET_STATUS(sens));
+    IPMI_ADD_RSP_DATA(sens->assert_enable & 0xff);
+    IPMI_ADD_RSP_DATA((sens->assert_enable >> 8) & 0xff);
+    IPMI_ADD_RSP_DATA(sens->deassert_enable & 0xff);
+    IPMI_ADD_RSP_DATA((sens->deassert_enable >> 8) & 0xff);
+ out:
+    return;
+}
+
+static void rearm_sensor_evts(IPMIBmcSim *ibs,
+                              uint8_t *cmd, unsigned int cmd_len,
+                              uint8_t *rsp, unsigned int *rsp_len,
+                              unsigned int max_rsp_len)
+{
+    IPMISensor *sens;
+
+    IPMI_CHECK_CMD_LEN(4);
+    if ((cmd[2] > MAX_SENSORS) ||
+        !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
+        rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
+        goto out;
+    }
+    sens = ibs->sensors + cmd[2];
+
+    if ((cmd[3] & 0x80) == 0) {
+        /* Just clear everything */
+        sens->states = 0;
+        goto out;
+    }
+ out:
+    return;
+}
+
+static void get_sensor_evt_status(IPMIBmcSim *ibs,
+                                  uint8_t *cmd, unsigned int cmd_len,
+                                  uint8_t *rsp, unsigned int *rsp_len,
+                                  unsigned int max_rsp_len)
+{
+    IPMISensor *sens;
+
+    IPMI_CHECK_CMD_LEN(3);
+    if ((cmd[2] > MAX_SENSORS) ||
+        !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
+        rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
+        goto out;
+    }
+    sens = ibs->sensors + cmd[2];
+    IPMI_ADD_RSP_DATA(sens->reading);
+    IPMI_ADD_RSP_DATA(IPMI_SENSOR_GET_RET_STATUS(sens));
+    IPMI_ADD_RSP_DATA(sens->assert_states & 0xff);
+    IPMI_ADD_RSP_DATA((sens->assert_states >> 8) & 0xff);
+    IPMI_ADD_RSP_DATA(sens->deassert_states & 0xff);
+    IPMI_ADD_RSP_DATA((sens->deassert_states >> 8) & 0xff);
+ out:
+    return;
+}
+
+static void get_sensor_reading(IPMIBmcSim *ibs,
+                               uint8_t *cmd, unsigned int cmd_len,
+                               uint8_t *rsp, unsigned int *rsp_len,
+                               unsigned int max_rsp_len)
+{
+    IPMISensor *sens;
+
+    IPMI_CHECK_CMD_LEN(3);
+    if ((cmd[2] > MAX_SENSORS) ||
+            !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
+        rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
+        goto out;
+    }
+    sens = ibs->sensors + cmd[2];
+    IPMI_ADD_RSP_DATA(sens->reading);
+    IPMI_ADD_RSP_DATA(IPMI_SENSOR_GET_RET_STATUS(sens));
+    IPMI_ADD_RSP_DATA(sens->states & 0xff);
+    if (IPMI_SENSOR_IS_DISCRETE(sens)) {
+        IPMI_ADD_RSP_DATA((sens->states >> 8) & 0xff);
+    }
+ out:
+    return;
+}
+
+static const IPMICmdHandler chassis_cmds[IPMI_NETFN_CHASSIS_MAXCMD] = {
+    [IPMI_CMD_GET_CHASSIS_CAPABILITIES] = chassis_capabilities,
+    [IPMI_CMD_GET_CHASSIS_STATUS] = chassis_status,
+    [IPMI_CMD_CHASSIS_CONTROL] = chassis_control
+};
+static const IPMINetfn chassis_netfn = {
+    .cmd_nums = IPMI_NETFN_CHASSIS_MAXCMD,
+    .cmd_handlers = chassis_cmds
+};
+
+static const IPMICmdHandler
+sensor_event_cmds[IPMI_NETFN_SENSOR_EVENT_MAXCMD] = {
+    [IPMI_CMD_SET_SENSOR_EVT_ENABLE] = set_sensor_evt_enable,
+    [IPMI_CMD_GET_SENSOR_EVT_ENABLE] = get_sensor_evt_enable,
+    [IPMI_CMD_REARM_SENSOR_EVTS] = rearm_sensor_evts,
+    [IPMI_CMD_GET_SENSOR_EVT_STATUS] = get_sensor_evt_status,
+    [IPMI_CMD_GET_SENSOR_READING] = get_sensor_reading
+};
+static const IPMINetfn sensor_event_netfn = {
+    .cmd_nums = IPMI_NETFN_SENSOR_EVENT_MAXCMD,
+    .cmd_handlers = sensor_event_cmds
+};
+
+static const IPMICmdHandler app_cmds[IPMI_NETFN_APP_MAXCMD] = {
+    [IPMI_CMD_GET_DEVICE_ID] = get_device_id,
+    [IPMI_CMD_COLD_RESET] = cold_reset,
+    [IPMI_CMD_WARM_RESET] = warm_reset,
+    [IPMI_CMD_SET_BMC_GLOBAL_ENABLES] = set_bmc_global_enables,
+    [IPMI_CMD_GET_BMC_GLOBAL_ENABLES] = get_bmc_global_enables,
+    [IPMI_CMD_CLR_MSG_FLAGS] = clr_msg_flags,
+    [IPMI_CMD_GET_MSG_FLAGS] = get_msg_flags,
+    [IPMI_CMD_GET_MSG] = get_msg,
+    [IPMI_CMD_SEND_MSG] = send_msg,
+    [IPMI_CMD_READ_EVT_MSG_BUF] = read_evt_msg_buf,
+    [IPMI_CMD_RESET_WATCHDOG_TIMER] = reset_watchdog_timer,
+    [IPMI_CMD_SET_WATCHDOG_TIMER] = set_watchdog_timer,
+    [IPMI_CMD_GET_WATCHDOG_TIMER] = get_watchdog_timer,
+};
+static const IPMINetfn app_netfn = {
+    .cmd_nums = IPMI_NETFN_APP_MAXCMD,
+    .cmd_handlers = app_cmds
+};
+
+static const IPMICmdHandler storage_cmds[IPMI_NETFN_STORAGE_MAXCMD] = {
+    [IPMI_CMD_GET_SDR_REP_INFO] = get_sdr_rep_info,
+    [IPMI_CMD_RESERVE_SDR_REP] = reserve_sdr_rep,
+    [IPMI_CMD_GET_SDR] = get_sdr,
+    [IPMI_CMD_ADD_SDR] = add_sdr,
+    [IPMI_CMD_CLEAR_SDR_REP] = clear_sdr_rep,
+    [IPMI_CMD_GET_SEL_INFO] = get_sel_info,
+    [IPMI_CMD_RESERVE_SEL] = reserve_sel,
+    [IPMI_CMD_GET_SEL_ENTRY] = get_sel_entry,
+    [IPMI_CMD_ADD_SEL_ENTRY] = add_sel_entry,
+    [IPMI_CMD_CLEAR_SEL] = clear_sel,
+    [IPMI_CMD_GET_SEL_TIME] = get_sel_time,
+    [IPMI_CMD_SET_SEL_TIME] = set_sel_time,
+};
+
+static const IPMINetfn storage_netfn = {
+    .cmd_nums = IPMI_NETFN_STORAGE_MAXCMD,
+    .cmd_handlers = storage_cmds
+};
+
+static void register_cmds(IPMIBmcSim *s)
+{
+    ipmi_register_netfn(s, IPMI_NETFN_CHASSIS, &chassis_netfn);
+    ipmi_register_netfn(s, IPMI_NETFN_SENSOR_EVENT, &sensor_event_netfn);
+    ipmi_register_netfn(s, IPMI_NETFN_APP, &app_netfn);
+    ipmi_register_netfn(s, IPMI_NETFN_STORAGE, &storage_netfn);
+}
+
+static const uint8_t init_sdrs[] = {
+    /* Watchdog device */
+    0x00, 0x00, 0x51, 0x02,   35, 0x20, 0x00, 0x00,
+    0x23, 0x01, 0x63, 0x00, 0x23, 0x6f, 0x0f, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8,
+    'W',  'a',  't',  'c',  'h',  'd',  'o',  'g',
+    /* End */
+    0xff, 0xff, 0x00, 0x00, 0x00
+};
+
+static void ipmi_sim_init(Object *obj)
+{
+    IPMIBmc *b = IPMI_BMC(obj);
+    unsigned int i;
+    unsigned int recid;
+    IPMIBmcSim *ibs = IPMI_BMC_SIMULATOR(b);
+
+    qemu_mutex_init(&ibs->lock);
+    QTAILQ_INIT(&ibs->rcvbufs);
+
+    ibs->bmc_global_enables = (1 << IPMI_BMC_EVENT_LOG_BIT);
+    ibs->device_id = 0x20;
+    ibs->ipmi_version = 0x02; /* IPMI 2.0 */
+    for (i = 0; i < 4; i++) {
+        ibs->sel.last_addition[i] = 0xff;
+        ibs->sel.last_clear[i] = 0xff;
+        ibs->sdr.last_addition[i] = 0xff;
+        ibs->sdr.last_clear[i] = 0xff;
+    }
+
+    for (i = 0;;) {
+        int len;
+        if ((i + 5) > sizeof(init_sdrs)) {
+            error_report("Problem with recid 0x%4.4x: \n", i);
+            return;
+        }
+        len = init_sdrs[i + 4];
+        recid = init_sdrs[i] | (init_sdrs[i + 1] << 8);
+        if (recid == 0xffff) {
+            break;
+        }
+        if ((i + len + 5) > sizeof(init_sdrs)) {
+            error_report("Problem with recid 0x%4.4x\n", i);
+            return;
+        }
+        sdr_add_entry(ibs, init_sdrs + i, len, NULL);
+        i += len + 5;
+    }
+
+    ipmi_init_sensors_from_sdrs(ibs);
+    register_cmds(ibs);
+
+    ibs->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ipmi_timeout, ibs);
+}
+
+static void ipmi_sim_class_init(ObjectClass *oc, void *data)
+{
+    IPMIBmcClass *bk = IPMI_BMC_CLASS(oc);
+
+    bk->handle_command = ipmi_sim_handle_command;
+}
+
+static const TypeInfo ipmi_sim_type = {
+    .name          = TYPE_IPMI_BMC_SIMULATOR,
+    .parent        = TYPE_IPMI_BMC,
+    .instance_size = sizeof(IPMIBmcSim),
+    .instance_init = ipmi_sim_init,
+    .class_init    = ipmi_sim_class_init,
+};
+
+static void ipmi_sim_register_types(void)
+{
+    type_register_static(&ipmi_sim_type);
+}
+
+type_init(ipmi_sim_register_types)
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 03/16] ipmi: Add an external connection simulation interface
  2015-12-17 18:50 [Qemu-devel] [PATCH v5 0/16] Add an IPMI device to QEMU minyard
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 01/16] Add a base IPMI interface minyard
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 02/16] ipmi: Add a local BMC simulation minyard
@ 2015-12-17 18:50 ` minyard
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 04/16] ipmi: Add an ISA KCS low-level interface minyard
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: minyard @ 2015-12-17 18:50 UTC (permalink / raw)
  To: qemu-devel, Paolo Bonzini, Michael S. Tsirkin, Igor Mammedov
  Cc: Corey Minyard

From: Corey Minyard <cminyard@mvista.com>

This adds an interface for IPMI that connects to a remote
BMC over a chardev (generally a TCP socket).  The OpenIPMI
lanserv simulator describes this interface, see that for
interface details.

Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
 default-configs/i386-softmmu.mak   |   1 +
 default-configs/x86_64-softmmu.mak |   1 +
 hw/ipmi/Makefile.objs              |   1 +
 hw/ipmi/ipmi_bmc_extern.c          | 478 +++++++++++++++++++++++++++++++++++++
 4 files changed, 481 insertions(+)
 create mode 100644 hw/ipmi/ipmi_bmc_extern.c

diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index 00a0346..077b3c4 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -11,6 +11,7 @@ CONFIG_VIRTIO_VGA=y
 CONFIG_VMMOUSE=y
 CONFIG_IPMI=y
 CONFIG_IPMI_LOCAL=y
+CONFIG_IPMI_EXTERN=y
 CONFIG_SERIAL=y
 CONFIG_PARALLEL=y
 CONFIG_I8254=y
diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index 39a619f..81b0cb0 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -11,6 +11,7 @@ CONFIG_VIRTIO_VGA=y
 CONFIG_VMMOUSE=y
 CONFIG_IPMI=y
 CONFIG_IPMI_LOCAL=y
+CONFIG_IPMI_EXTERN=y
 CONFIG_SERIAL=y
 CONFIG_PARALLEL=y
 CONFIG_I8254=y
diff --git a/hw/ipmi/Makefile.objs b/hw/ipmi/Makefile.objs
index 875271c..ddb15ec 100644
--- a/hw/ipmi/Makefile.objs
+++ b/hw/ipmi/Makefile.objs
@@ -1,2 +1,3 @@
 common-obj-$(CONFIG_IPMI) += ipmi.o
 common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_sim.o
+common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_extern.o
diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c
new file mode 100644
index 0000000..d4669f0
--- /dev/null
+++ b/hw/ipmi/ipmi_bmc_extern.c
@@ -0,0 +1,478 @@
+/*
+ * IPMI BMC external connection
+ *
+ * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * This is designed to connect with OpenIPMI's lanserv serial interface
+ * using the "VM" connection type.  See that for details.
+ */
+
+#include <stdint.h>
+#include "qemu/timer.h"
+#include "sysemu/char.h"
+#include "hw/ipmi/ipmi.h"
+
+#define VM_MSG_CHAR        0xA0 /* Marks end of message */
+#define VM_CMD_CHAR        0xA1 /* Marks end of a command */
+#define VM_ESCAPE_CHAR     0xAA /* Set bit 4 from the next byte to 0 */
+
+#define VM_PROTOCOL_VERSION        1
+#define VM_CMD_VERSION             0xff /* A version number byte follows */
+#define VM_CMD_NOATTN              0x00
+#define VM_CMD_ATTN                0x01
+#define VM_CMD_ATTN_IRQ            0x02
+#define VM_CMD_POWEROFF            0x03
+#define VM_CMD_RESET               0x04
+#define VM_CMD_ENABLE_IRQ          0x05 /* Enable/disable the messaging irq */
+#define VM_CMD_DISABLE_IRQ         0x06
+#define VM_CMD_SEND_NMI            0x07
+#define VM_CMD_CAPABILITIES        0x08
+#define   VM_CAPABILITIES_POWER    0x01
+#define   VM_CAPABILITIES_RESET    0x02
+#define   VM_CAPABILITIES_IRQ      0x04
+#define   VM_CAPABILITIES_NMI      0x08
+#define   VM_CAPABILITIES_ATTN     0x10
+
+#define TYPE_IPMI_BMC_EXTERN "ipmi-bmc-extern"
+#define IPMI_BMC_EXTERN(obj) OBJECT_CHECK(IPMIBmcExtern, (obj), \
+                                        TYPE_IPMI_BMC_EXTERN)
+typedef struct IPMIBmcExtern {
+    IPMIBmc parent;
+
+    CharDriverState *chr;
+
+    bool connected;
+
+    unsigned char inbuf[MAX_IPMI_MSG_SIZE + 2];
+    unsigned int inpos;
+    bool in_escape;
+    bool in_too_many;
+    bool waiting_rsp;
+    bool sending_cmd;
+
+    unsigned char outbuf[(MAX_IPMI_MSG_SIZE + 2) * 2 + 1];
+    unsigned int outpos;
+    unsigned int outlen;
+
+    struct QEMUTimer *extern_timer;
+
+    /* A reset event is pending to be sent upstream. */
+    bool send_reset;
+} IPMIBmcExtern;
+
+static int can_receive(void *opaque);
+static void receive(void *opaque, const uint8_t *buf, int size);
+static void chr_event(void *opaque, int event);
+
+static unsigned char
+ipmb_checksum(const unsigned char *data, int size, unsigned char start)
+{
+        unsigned char csum = start;
+
+        for (; size > 0; size--, data++) {
+                csum += *data;
+        }
+        return csum;
+}
+
+static void continue_send(IPMIBmcExtern *ibe)
+{
+    if (ibe->outlen == 0) {
+        goto check_reset;
+    }
+ send:
+    ibe->outpos += qemu_chr_fe_write(ibe->chr, ibe->outbuf + ibe->outpos,
+                                     ibe->outlen - ibe->outpos);
+    if (ibe->outpos < ibe->outlen) {
+        /* Not fully transmitted, try again in a 10ms */
+        timer_mod_ns(ibe->extern_timer,
+                     qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 10000000);
+    } else {
+        /* Sent */
+        ibe->outlen = 0;
+        ibe->outpos = 0;
+        if (!ibe->sending_cmd) {
+            ibe->waiting_rsp = true;
+        } else {
+            ibe->sending_cmd = false;
+        }
+    check_reset:
+        if (ibe->connected && ibe->send_reset) {
+            /* Send the reset */
+            ibe->outbuf[0] = VM_CMD_RESET;
+            ibe->outbuf[1] = VM_CMD_CHAR;
+            ibe->outlen = 2;
+            ibe->outpos = 0;
+            ibe->send_reset = false;
+            ibe->sending_cmd = true;
+            goto send;
+        }
+
+        if (ibe->waiting_rsp) {
+            /* Make sure we get a response within 4 seconds. */
+            timer_mod_ns(ibe->extern_timer,
+                         qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 4000000000ULL);
+        }
+    }
+    return;
+}
+
+static void extern_timeout(void *opaque)
+{
+    IPMIBmcExtern *ibe = opaque;
+    IPMIInterface *s = ibe->parent.intf;
+
+    if (ibe->connected) {
+        if (ibe->waiting_rsp && (ibe->outlen == 0)) {
+            IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+            /* The message response timed out, return an error. */
+            ibe->waiting_rsp = false;
+            ibe->inbuf[1] = ibe->outbuf[1] | 0x04;
+            ibe->inbuf[2] = ibe->outbuf[2];
+            ibe->inbuf[3] = IPMI_CC_TIMEOUT;
+            k->handle_rsp(s, ibe->outbuf[0], ibe->inbuf + 1, 3);
+        } else {
+            continue_send(ibe);
+        }
+    }
+}
+
+static void addchar(IPMIBmcExtern *ibe, unsigned char ch)
+{
+    switch (ch) {
+    case VM_MSG_CHAR:
+    case VM_CMD_CHAR:
+    case VM_ESCAPE_CHAR:
+        ibe->outbuf[ibe->outlen] = VM_ESCAPE_CHAR;
+        ibe->outlen++;
+        ch |= 0x10;
+        /* No break */
+
+    default:
+        ibe->outbuf[ibe->outlen] = ch;
+        ibe->outlen++;
+    }
+}
+
+static void ipmi_bmc_extern_handle_command(IPMIBmc *b,
+                                       uint8_t *cmd, unsigned int cmd_len,
+                                       unsigned int max_cmd_len,
+                                       uint8_t msg_id)
+{
+    IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b);
+    IPMIInterface *s = ibe->parent.intf;
+    uint8_t err = 0, csum;
+    unsigned int i;
+
+    if (ibe->outlen) {
+        /* We already have a command queued.  Shouldn't ever happen. */
+        fprintf(stderr, "IPMI KCS: Got command when not finished with the"
+                " previous commmand\n");
+        abort();
+    }
+
+    /* If it's too short or it was truncated, return an error. */
+    if (cmd_len < 2) {
+        err = IPMI_CC_REQUEST_DATA_LENGTH_INVALID;
+    } else if ((cmd_len > max_cmd_len) || (cmd_len > MAX_IPMI_MSG_SIZE)) {
+        err = IPMI_CC_REQUEST_DATA_TRUNCATED;
+    } else if (!ibe->connected) {
+        err = IPMI_CC_BMC_INIT_IN_PROGRESS;
+    }
+    if (err) {
+        IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+        unsigned char rsp[3];
+        rsp[0] = cmd[0] | 0x04;
+        rsp[1] = cmd[1];
+        rsp[2] = err;
+        ibe->waiting_rsp = false;
+        k->handle_rsp(s, msg_id, rsp, 3);
+        goto out;
+    }
+
+    addchar(ibe, msg_id);
+    for (i = 0; i < cmd_len; i++) {
+        addchar(ibe, cmd[i]);
+    }
+    csum = ipmb_checksum(&msg_id, 1, 0);
+    addchar(ibe, -ipmb_checksum(cmd, cmd_len, csum));
+
+    ibe->outbuf[ibe->outlen] = VM_MSG_CHAR;
+    ibe->outlen++;
+
+    /* Start the transmit */
+    continue_send(ibe);
+
+ out:
+    return;
+}
+
+static void handle_hw_op(IPMIBmcExtern *ibe, unsigned char hw_op)
+{
+    IPMIInterface *s = ibe->parent.intf;
+    IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+
+    switch (hw_op) {
+    case VM_CMD_VERSION:
+        /* We only support one version at this time. */
+        break;
+
+    case VM_CMD_NOATTN:
+        k->set_atn(s, 0, 0);
+        break;
+
+    case VM_CMD_ATTN:
+        k->set_atn(s, 1, 0);
+        break;
+
+    case VM_CMD_ATTN_IRQ:
+        k->set_atn(s, 1, 1);
+        break;
+
+    case VM_CMD_POWEROFF:
+        k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0);
+        break;
+
+    case VM_CMD_RESET:
+        k->do_hw_op(s, IPMI_RESET_CHASSIS, 0);
+        break;
+
+    case VM_CMD_ENABLE_IRQ:
+        k->set_irq_enable(s, 1);
+        break;
+
+    case VM_CMD_DISABLE_IRQ:
+        k->set_irq_enable(s, 0);
+        break;
+
+    case VM_CMD_SEND_NMI:
+        k->do_hw_op(s, IPMI_SEND_NMI, 0);
+        break;
+    }
+}
+
+static void handle_msg(IPMIBmcExtern *ibe)
+{
+    IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(ibe->parent.intf);
+
+    if (ibe->in_escape) {
+        ipmi_debug("msg escape not ended\n");
+        return;
+    }
+    if (ibe->inpos < 5) {
+        ipmi_debug("msg too short\n");
+        return;
+    }
+    if (ibe->in_too_many) {
+        ibe->inbuf[3] = IPMI_CC_REQUEST_DATA_TRUNCATED;
+        ibe->inpos = 4;
+    } else if (ipmb_checksum(ibe->inbuf, ibe->inpos, 0) != 0) {
+        ipmi_debug("msg checksum failure\n");
+        return;
+    } else {
+        ibe->inpos--; /* Remove checkum */
+    }
+
+    timer_del(ibe->extern_timer);
+    ibe->waiting_rsp = false;
+    k->handle_rsp(ibe->parent.intf, ibe->inbuf[0], ibe->inbuf + 1, ibe->inpos - 1);
+}
+
+static int can_receive(void *opaque)
+{
+    return 1;
+}
+
+static void receive(void *opaque, const uint8_t *buf, int size)
+{
+    IPMIBmcExtern *ibe = opaque;
+    int i;
+    unsigned char hw_op;
+
+    for (i = 0; i < size; i++) {
+        unsigned char ch = buf[i];
+
+        switch (ch) {
+        case VM_MSG_CHAR:
+            handle_msg(ibe);
+            ibe->in_too_many = false;
+            ibe->inpos = 0;
+            break;
+
+        case VM_CMD_CHAR:
+            if (ibe->in_too_many) {
+                ipmi_debug("cmd in too many\n");
+                ibe->in_too_many = false;
+                ibe->inpos = 0;
+                break;
+            }
+            if (ibe->in_escape) {
+                ipmi_debug("cmd in escape\n");
+                ibe->in_too_many = false;
+                ibe->inpos = 0;
+                ibe->in_escape = false;
+                break;
+            }
+            ibe->in_too_many = false;
+            if (ibe->inpos < 1) {
+                break;
+            }
+            hw_op = ibe->inbuf[0];
+            ibe->inpos = 0;
+            goto out_hw_op;
+            break;
+
+        case VM_ESCAPE_CHAR:
+            ibe->in_escape = true;
+            break;
+
+        default:
+            if (ibe->in_escape) {
+                ch &= ~0x10;
+                ibe->in_escape = false;
+            }
+            if (ibe->in_too_many) {
+                break;
+            }
+            if (ibe->inpos >= sizeof(ibe->inbuf)) {
+                ibe->in_too_many = true;
+                break;
+            }
+            ibe->inbuf[ibe->inpos] = ch;
+            ibe->inpos++;
+            break;
+        }
+    }
+    return;
+
+ out_hw_op:
+    handle_hw_op(ibe, hw_op);
+}
+
+static void chr_event(void *opaque, int event)
+{
+    IPMIBmcExtern *ibe = opaque;
+    IPMIInterface *s = ibe->parent.intf;
+    IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+    unsigned char v;
+
+    switch (event) {
+    case CHR_EVENT_OPENED:
+        ibe->connected = true;
+        ibe->outpos = 0;
+        ibe->outlen = 0;
+        addchar(ibe, VM_CMD_VERSION);
+        addchar(ibe, VM_PROTOCOL_VERSION);
+        ibe->outbuf[ibe->outlen] = VM_CMD_CHAR;
+        ibe->outlen++;
+        addchar(ibe, VM_CMD_CAPABILITIES);
+        v = VM_CAPABILITIES_IRQ | VM_CAPABILITIES_ATTN;
+        if (k->do_hw_op(ibe->parent.intf, IPMI_POWEROFF_CHASSIS, 1) == 0) {
+            v |= VM_CAPABILITIES_POWER;
+        }
+        if (k->do_hw_op(ibe->parent.intf, IPMI_RESET_CHASSIS, 1) == 0) {
+            v |= VM_CAPABILITIES_RESET;
+        }
+        if (k->do_hw_op(ibe->parent.intf, IPMI_SEND_NMI, 1) == 0) {
+            v |= VM_CAPABILITIES_NMI;
+        }
+        addchar(ibe, v);
+        ibe->outbuf[ibe->outlen] = VM_CMD_CHAR;
+        ibe->outlen++;
+        ibe->sending_cmd = false;
+        continue_send(ibe);
+        break;
+
+    case CHR_EVENT_CLOSED:
+        if (!ibe->connected) {
+            return;
+        }
+        ibe->connected = false;
+        if (ibe->waiting_rsp) {
+            ibe->waiting_rsp = false;
+            ibe->inbuf[1] = ibe->outbuf[1] | 0x04;
+            ibe->inbuf[2] = ibe->outbuf[2];
+            ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS;
+            k->handle_rsp(s, ibe->outbuf[0], ibe->inbuf + 1, 3);
+        }
+        break;
+    }
+}
+
+static void ipmi_bmc_extern_handle_reset(IPMIBmc *b)
+{
+    IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b);
+
+    ibe->send_reset = true;
+    continue_send(ibe);
+}
+
+static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp)
+{
+    IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev);
+
+    if (!ibe->chr) {
+        error_setg(errp, "IPMI external bmc requires chardev attribute");
+        return;
+    }
+
+    qemu_chr_add_handlers(ibe->chr, can_receive, receive, chr_event, ibe);
+}
+
+static void ipmi_bmc_extern_init(Object *obj)
+{
+    IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj);
+
+    ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe);
+}
+
+static Property ipmi_bmc_extern_properties[] = {
+    DEFINE_PROP_CHR("chardev", IPMIBmcExtern, chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ipmi_bmc_extern_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    IPMIBmcClass *bk = IPMI_BMC_CLASS(oc);
+
+    bk->handle_command = ipmi_bmc_extern_handle_command;
+    bk->handle_reset = ipmi_bmc_extern_handle_reset;
+    dc->realize = ipmi_bmc_extern_realize;
+    dc->props = ipmi_bmc_extern_properties;
+}
+
+static const TypeInfo ipmi_bmc_extern_type = {
+    .name          = TYPE_IPMI_BMC_EXTERN,
+    .parent        = TYPE_IPMI_BMC,
+    .instance_size = sizeof(IPMIBmcExtern),
+    .instance_init = ipmi_bmc_extern_init,
+    .class_init    = ipmi_bmc_extern_class_init,
+ };
+
+static void ipmi_bmc_extern_register_types(void)
+{
+    type_register_static(&ipmi_bmc_extern_type);
+}
+
+type_init(ipmi_bmc_extern_register_types)
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 04/16] ipmi: Add an ISA KCS low-level interface
  2015-12-17 18:50 [Qemu-devel] [PATCH v5 0/16] Add an IPMI device to QEMU minyard
                   ` (2 preceding siblings ...)
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 03/16] ipmi: Add an external connection simulation interface minyard
@ 2015-12-17 18:50 ` minyard
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 05/16] ipmi: Add a BT " minyard
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: minyard @ 2015-12-17 18:50 UTC (permalink / raw)
  To: qemu-devel, Paolo Bonzini, Michael S. Tsirkin, Igor Mammedov
  Cc: Corey Minyard

From: Corey Minyard <cminyard@mvista.com>

This provides the simulation of the KCS hardware interface.

Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
 default-configs/i386-softmmu.mak   |   1 +
 default-configs/x86_64-softmmu.mak |   1 +
 hw/ipmi/Makefile.objs              |   1 +
 hw/ipmi/isa_ipmi_kcs.c             | 452 +++++++++++++++++++++++++++++++++++++
 4 files changed, 455 insertions(+)
 create mode 100644 hw/ipmi/isa_ipmi_kcs.c

diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index 077b3c4..1c24aab 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -12,6 +12,7 @@ CONFIG_VMMOUSE=y
 CONFIG_IPMI=y
 CONFIG_IPMI_LOCAL=y
 CONFIG_IPMI_EXTERN=y
+CONFIG_ISA_IPMI_KCS=y
 CONFIG_SERIAL=y
 CONFIG_PARALLEL=y
 CONFIG_I8254=y
diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index 81b0cb0..9d1f934 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -12,6 +12,7 @@ CONFIG_VMMOUSE=y
 CONFIG_IPMI=y
 CONFIG_IPMI_LOCAL=y
 CONFIG_IPMI_EXTERN=y
+CONFIG_ISA_IPMI_KCS=y
 CONFIG_SERIAL=y
 CONFIG_PARALLEL=y
 CONFIG_I8254=y
diff --git a/hw/ipmi/Makefile.objs b/hw/ipmi/Makefile.objs
index ddb15ec..28618c5 100644
--- a/hw/ipmi/Makefile.objs
+++ b/hw/ipmi/Makefile.objs
@@ -1,3 +1,4 @@
 common-obj-$(CONFIG_IPMI) += ipmi.o
 common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_sim.o
 common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_extern.o
+common-obj-$(CONFIG_ISA_IPMI_KCS) += isa_ipmi_kcs.o
diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c
new file mode 100644
index 0000000..750a725
--- /dev/null
+++ b/hw/ipmi/isa_ipmi_kcs.c
@@ -0,0 +1,452 @@
+/*
+ * QEMU ISA IPMI KCS emulation
+ *
+ * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/ipmi/ipmi.h"
+#include "hw/isa/isa.h"
+#include "hw/i386/pc.h"
+
+#define IPMI_KCS_OBF_BIT        0
+#define IPMI_KCS_IBF_BIT        1
+#define IPMI_KCS_SMS_ATN_BIT    2
+#define IPMI_KCS_CD_BIT         3
+
+#define IPMI_KCS_OBF_MASK          (1 << IPMI_KCS_OBF_BIT)
+#define IPMI_KCS_GET_OBF(d)        (((d) >> IPMI_KCS_OBF_BIT) & 0x1)
+#define IPMI_KCS_SET_OBF(d, v)     (d) = (((d) & ~IPMI_KCS_OBF_MASK) | \
+                                       (((v) & 1) << IPMI_KCS_OBF_BIT))
+#define IPMI_KCS_IBF_MASK          (1 << IPMI_KCS_IBF_BIT)
+#define IPMI_KCS_GET_IBF(d)        (((d) >> IPMI_KCS_IBF_BIT) & 0x1)
+#define IPMI_KCS_SET_IBF(d, v)     (d) = (((d) & ~IPMI_KCS_IBF_MASK) | \
+                                       (((v) & 1) << IPMI_KCS_IBF_BIT))
+#define IPMI_KCS_SMS_ATN_MASK      (1 << IPMI_KCS_SMS_ATN_BIT)
+#define IPMI_KCS_GET_SMS_ATN(d)    (((d) >> IPMI_KCS_SMS_ATN_BIT) & 0x1)
+#define IPMI_KCS_SET_SMS_ATN(d, v) (d) = (((d) & ~IPMI_KCS_SMS_ATN_MASK) | \
+                                       (((v) & 1) << IPMI_KCS_SMS_ATN_BIT))
+#define IPMI_KCS_CD_MASK           (1 << IPMI_KCS_CD_BIT)
+#define IPMI_KCS_GET_CD(d)         (((d) >> IPMI_KCS_CD_BIT) & 0x1)
+#define IPMI_KCS_SET_CD(d, v)      (d) = (((d) & ~IPMI_KCS_CD_MASK) | \
+                                       (((v) & 1) << IPMI_KCS_CD_BIT))
+
+#define IPMI_KCS_IDLE_STATE        0
+#define IPMI_KCS_READ_STATE        1
+#define IPMI_KCS_WRITE_STATE       2
+#define IPMI_KCS_ERROR_STATE       3
+
+#define IPMI_KCS_GET_STATE(d)    (((d) >> 6) & 0x3)
+#define IPMI_KCS_SET_STATE(d, v) ((d) = ((d) & ~0xc0) | (((v) & 0x3) << 6))
+
+#define IPMI_KCS_ABORT_STATUS_CMD       0x60
+#define IPMI_KCS_WRITE_START_CMD        0x61
+#define IPMI_KCS_WRITE_END_CMD          0x62
+#define IPMI_KCS_READ_CMD               0x68
+
+#define IPMI_KCS_STATUS_NO_ERR          0x00
+#define IPMI_KCS_STATUS_ABORTED_ERR     0x01
+#define IPMI_KCS_STATUS_BAD_CC_ERR      0x02
+#define IPMI_KCS_STATUS_LENGTH_ERR      0x06
+
+typedef struct IPMIKCS {
+    IPMIBmc *bmc;
+
+    bool do_wake;
+
+    qemu_irq irq;
+
+    uint32_t io_base;
+    unsigned long io_length;
+    MemoryRegion io;
+
+    bool obf_irq_set;
+    bool atn_irq_set;
+    bool use_irq;
+    bool irqs_enabled;
+
+    uint8_t outmsg[MAX_IPMI_MSG_SIZE];
+    uint32_t outpos;
+    uint32_t outlen;
+
+    uint8_t inmsg[MAX_IPMI_MSG_SIZE];
+    uint32_t inlen;
+    bool write_end;
+
+    uint8_t status_reg;
+    uint8_t data_out_reg;
+
+    int16_t data_in_reg; /* -1 means not written */
+    int16_t cmd_reg;
+
+    /*
+     * This is a response number that we send with the command to make
+     * sure that the response matches the command.
+     */
+    uint8_t waiting_rsp;
+} IPMIKCS;
+
+#define SET_OBF() \
+    do {                                                                      \
+        IPMI_KCS_SET_OBF(ik->status_reg, 1);                                  \
+        if (ik->use_irq && ik->irqs_enabled && !ik->obf_irq_set) {            \
+            ik->obf_irq_set = 1;                                              \
+            if (!ik->atn_irq_set) {                                           \
+                qemu_irq_raise(ik->irq);                                      \
+            }                                                                 \
+        }                                                                     \
+    } while (0)
+
+static void ipmi_kcs_signal(IPMIKCS *ik, IPMIInterface *ii)
+{
+    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+
+    ik->do_wake = 1;
+    while (ik->do_wake) {
+        ik->do_wake = 0;
+        iic->handle_if_event(ii);
+    }
+}
+
+static void ipmi_kcs_handle_event(IPMIInterface *ii)
+{
+    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+    IPMIKCS *ik = iic->get_backend_data(ii);
+
+    if (ik->cmd_reg == IPMI_KCS_ABORT_STATUS_CMD) {
+        if (IPMI_KCS_GET_STATE(ik->status_reg) != IPMI_KCS_ERROR_STATE) {
+            ik->waiting_rsp++; /* Invalidate the message */
+            ik->outmsg[0] = IPMI_KCS_STATUS_ABORTED_ERR;
+            ik->outlen = 1;
+            ik->outpos = 0;
+            IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE);
+            SET_OBF();
+        }
+        goto out;
+    }
+
+    switch (IPMI_KCS_GET_STATE(ik->status_reg)) {
+    case IPMI_KCS_IDLE_STATE:
+        if (ik->cmd_reg == IPMI_KCS_WRITE_START_CMD) {
+            IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_WRITE_STATE);
+            ik->cmd_reg = -1;
+            ik->write_end = 0;
+            ik->inlen = 0;
+            SET_OBF();
+        }
+        break;
+
+    case IPMI_KCS_READ_STATE:
+    handle_read:
+        if (ik->outpos >= ik->outlen) {
+            IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_IDLE_STATE);
+            SET_OBF();
+        } else if (ik->data_in_reg == IPMI_KCS_READ_CMD) {
+            ik->data_out_reg = ik->outmsg[ik->outpos];
+            ik->outpos++;
+            SET_OBF();
+        } else {
+            ik->outmsg[0] = IPMI_KCS_STATUS_BAD_CC_ERR;
+            ik->outlen = 1;
+            ik->outpos = 0;
+            IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE);
+            SET_OBF();
+            goto out;
+        }
+        break;
+
+    case IPMI_KCS_WRITE_STATE:
+        if (ik->data_in_reg != -1) {
+            /*
+             * Don't worry about input overrun here, that will be
+             * handled in the BMC.
+             */
+            if (ik->inlen < sizeof(ik->inmsg)) {
+                ik->inmsg[ik->inlen] = ik->data_in_reg;
+            }
+            ik->inlen++;
+        }
+        if (ik->write_end) {
+            IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(ik->bmc);
+            ik->outlen = 0;
+            ik->write_end = 0;
+            ik->outpos = 0;
+            bk->handle_command(ik->bmc, ik->inmsg, ik->inlen, sizeof(ik->inmsg),
+                               ik->waiting_rsp);
+            goto out_noibf;
+        } else if (ik->cmd_reg == IPMI_KCS_WRITE_END_CMD) {
+            ik->cmd_reg = -1;
+            ik->write_end = 1;
+        }
+        SET_OBF();
+        break;
+
+    case IPMI_KCS_ERROR_STATE:
+        if (ik->data_in_reg != -1) {
+            IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_READ_STATE);
+            ik->data_in_reg = IPMI_KCS_READ_CMD;
+            goto handle_read;
+        }
+        break;
+    }
+
+    if (ik->cmd_reg != -1) {
+        /* Got an invalid command */
+        ik->outmsg[0] = IPMI_KCS_STATUS_BAD_CC_ERR;
+        ik->outlen = 1;
+        ik->outpos = 0;
+        IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE);
+    }
+
+ out:
+    ik->cmd_reg = -1;
+    ik->data_in_reg = -1;
+    IPMI_KCS_SET_IBF(ik->status_reg, 0);
+ out_noibf:
+    return;
+}
+
+static void ipmi_kcs_handle_rsp(IPMIInterface *ii, uint8_t msg_id,
+                                unsigned char *rsp, unsigned int rsp_len)
+{
+    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+    IPMIKCS *ik = iic->get_backend_data(ii);
+
+    if (ik->waiting_rsp == msg_id) {
+        ik->waiting_rsp++;
+        if (rsp_len > sizeof(ik->outmsg)) {
+            ik->outmsg[0] = rsp[0];
+            ik->outmsg[1] = rsp[1];
+            ik->outmsg[2] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES;
+            ik->outlen = 3;
+        } else {
+            memcpy(ik->outmsg, rsp, rsp_len);
+            ik->outlen = rsp_len;
+        }
+        IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_READ_STATE);
+        ik->data_in_reg = IPMI_KCS_READ_CMD;
+        ipmi_kcs_signal(ik, ii);
+    }
+}
+
+
+static uint64_t ipmi_kcs_ioport_read(void *opaque, hwaddr addr, unsigned size)
+{
+    IPMIInterface *ii = opaque;
+    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+    IPMIKCS *ik = iic->get_backend_data(ii);
+    uint32_t ret;
+
+    switch (addr & 1) {
+    case 0:
+        ret = ik->data_out_reg;
+        IPMI_KCS_SET_OBF(ik->status_reg, 0);
+        if (ik->obf_irq_set) {
+            ik->obf_irq_set = 0;
+            if (!ik->atn_irq_set) {
+                qemu_irq_lower(ik->irq);
+            }
+        }
+        break;
+    case 1:
+        ret = ik->status_reg;
+        if (ik->atn_irq_set) {
+            ik->atn_irq_set = 0;
+            if (!ik->obf_irq_set) {
+                qemu_irq_lower(ik->irq);
+            }
+        }
+        break;
+    }
+    return ret;
+}
+
+static void ipmi_kcs_ioport_write(void *opaque, hwaddr addr, uint64_t val,
+                                  unsigned size)
+{
+    IPMIInterface *ii = opaque;
+    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+    IPMIKCS *ik = iic->get_backend_data(ii);
+
+    if (IPMI_KCS_GET_IBF(ik->status_reg)) {
+        return;
+    }
+
+    switch (addr & 1) {
+    case 0:
+        ik->data_in_reg = val;
+        break;
+
+    case 1:
+        ik->cmd_reg = val;
+        break;
+    }
+    IPMI_KCS_SET_IBF(ik->status_reg, 1);
+    ipmi_kcs_signal(ik, ii);
+}
+
+const MemoryRegionOps ipmi_kcs_io_ops = {
+    .read = ipmi_kcs_ioport_read,
+    .write = ipmi_kcs_ioport_write,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void ipmi_kcs_set_atn(IPMIInterface *ii, int val, int irq)
+{
+    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+    IPMIKCS *ik = iic->get_backend_data(ii);
+
+    IPMI_KCS_SET_SMS_ATN(ik->status_reg, val);
+    if (val) {
+        if (irq && !ik->atn_irq_set && ik->use_irq && ik->irqs_enabled) {
+            ik->atn_irq_set = 1;
+            if (!ik->obf_irq_set) {
+                qemu_irq_raise(ik->irq);
+            }
+        }
+    } else {
+        if (ik->atn_irq_set) {
+            ik->atn_irq_set = 0;
+            if (!ik->obf_irq_set) {
+                qemu_irq_lower(ik->irq);
+            }
+        }
+    }
+}
+
+static void ipmi_kcs_set_irq_enable(IPMIInterface *ii, int val)
+{
+    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+    IPMIKCS *ik = iic->get_backend_data(ii);
+
+    ik->irqs_enabled = val;
+}
+
+static void ipmi_kcs_init(IPMIInterface *ii, Error **errp)
+{
+    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+    IPMIKCS *ik = iic->get_backend_data(ii);
+
+    ik->io_length = 2;
+    memory_region_init_io(&ik->io, NULL, &ipmi_kcs_io_ops, ii, "ipmi-kcs", 2);
+}
+
+static void ipmi_kcs_class_init(IPMIInterfaceClass *iic)
+{
+    iic->init = ipmi_kcs_init;
+    iic->set_atn = ipmi_kcs_set_atn;
+    iic->handle_rsp = ipmi_kcs_handle_rsp;
+    iic->handle_if_event = ipmi_kcs_handle_event;
+    iic->set_irq_enable = ipmi_kcs_set_irq_enable;
+}
+
+
+#define TYPE_ISA_IPMI_KCS "isa-ipmi-kcs"
+#define ISA_IPMI_KCS(obj) OBJECT_CHECK(ISAIPMIKCSDevice, (obj), \
+                                       TYPE_ISA_IPMI_KCS)
+
+typedef struct ISAIPMIKCSDevice {
+    ISADevice dev;
+    int32 isairq;
+    IPMIKCS kcs;
+} ISAIPMIKCSDevice;
+
+static void ipmi_isa_realize(DeviceState *dev, Error **errp)
+{
+    ISADevice *isadev = ISA_DEVICE(dev);
+    ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(dev);
+    IPMIInterface *ii = IPMI_INTERFACE(dev);
+    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+
+    if (!iik->kcs.bmc) {
+        error_setg(errp, "IPMI device requires a bmc attribute to be set");
+        return;
+    }
+
+    iik->kcs.bmc->intf = ii;
+
+    iic->init(ii, errp);
+    if (*errp)
+        return;
+
+    if (iik->isairq > 0) {
+        isa_init_irq(isadev, &iik->kcs.irq, iik->isairq);
+        iik->kcs.use_irq = 1;
+    }
+
+    qdev_set_legacy_instance_id(dev, iik->kcs.io_base, iik->kcs.io_length);
+
+    isa_register_ioport(isadev, &iik->kcs.io, iik->kcs.io_base);
+}
+
+static void isa_ipmi_kcs_init(Object *obj)
+{
+    ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(obj);
+
+    ipmi_bmc_find_and_link(obj, (Object **) &iik->kcs.bmc);
+}
+
+static void *isa_ipmi_kcs_get_backend_data(IPMIInterface *ii)
+{
+    ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(ii);
+
+    return &iik->kcs;
+}
+
+static Property ipmi_isa_properties[] = {
+    DEFINE_PROP_UINT32("ioport", ISAIPMIKCSDevice, kcs.io_base,  0xca2),
+    DEFINE_PROP_INT32("irq",   ISAIPMIKCSDevice, isairq,  5),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void isa_ipmi_kcs_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc);
+
+    dc->realize = ipmi_isa_realize;
+    dc->props = ipmi_isa_properties;
+
+    iic->get_backend_data = isa_ipmi_kcs_get_backend_data;
+    ipmi_kcs_class_init(iic);
+}
+
+static const TypeInfo isa_ipmi_kcs_info = {
+    .name          = TYPE_ISA_IPMI_KCS,
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(ISAIPMIKCSDevice),
+    .instance_init = isa_ipmi_kcs_init,
+    .class_init    = isa_ipmi_kcs_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_IPMI_INTERFACE },
+        { }
+    }
+};
+
+static void ipmi_register_types(void)
+{
+    type_register_static(&isa_ipmi_kcs_info);
+}
+
+type_init(ipmi_register_types)
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 05/16] ipmi: Add a BT low-level interface
  2015-12-17 18:50 [Qemu-devel] [PATCH v5 0/16] Add an IPMI device to QEMU minyard
                   ` (3 preceding siblings ...)
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 04/16] ipmi: Add an ISA KCS low-level interface minyard
@ 2015-12-17 18:50 ` minyard
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 06/16] ipmi: Add tests minyard
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: minyard @ 2015-12-17 18:50 UTC (permalink / raw)
  To: qemu-devel, Paolo Bonzini, Michael S. Tsirkin, Igor Mammedov
  Cc: Corey Minyard

From: Corey Minyard <cminyard@mvista.com>

This provides the simulation of the BT hardware interface for
IPMI.

Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
 default-configs/i386-softmmu.mak   |   1 +
 default-configs/x86_64-softmmu.mak |   1 +
 hw/ipmi/Makefile.objs              |   1 +
 hw/ipmi/isa_ipmi_bt.c              | 489 +++++++++++++++++++++++++++++++++++++
 4 files changed, 492 insertions(+)
 create mode 100644 hw/ipmi/isa_ipmi_bt.c

diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index 1c24aab..1730fbb 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -13,6 +13,7 @@ CONFIG_IPMI=y
 CONFIG_IPMI_LOCAL=y
 CONFIG_IPMI_EXTERN=y
 CONFIG_ISA_IPMI_KCS=y
+CONFIG_ISA_IPMI_BT=y
 CONFIG_SERIAL=y
 CONFIG_PARALLEL=y
 CONFIG_I8254=y
diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index 9d1f934..22a99a2 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -13,6 +13,7 @@ CONFIG_IPMI=y
 CONFIG_IPMI_LOCAL=y
 CONFIG_IPMI_EXTERN=y
 CONFIG_ISA_IPMI_KCS=y
+CONFIG_ISA_IPMI_BT=y
 CONFIG_SERIAL=y
 CONFIG_PARALLEL=y
 CONFIG_I8254=y
diff --git a/hw/ipmi/Makefile.objs b/hw/ipmi/Makefile.objs
index 28618c5..a90318d 100644
--- a/hw/ipmi/Makefile.objs
+++ b/hw/ipmi/Makefile.objs
@@ -2,3 +2,4 @@ common-obj-$(CONFIG_IPMI) += ipmi.o
 common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_sim.o
 common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_extern.o
 common-obj-$(CONFIG_ISA_IPMI_KCS) += isa_ipmi_kcs.o
+common-obj-$(CONFIG_ISA_IPMI_BT) += isa_ipmi_bt.o
diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c
new file mode 100644
index 0000000..70e2c0d
--- /dev/null
+++ b/hw/ipmi/isa_ipmi_bt.c
@@ -0,0 +1,489 @@
+/*
+ * QEMU ISA IPMI BT emulation
+ *
+ * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/ipmi/ipmi.h"
+#include "hw/isa/isa.h"
+#include "hw/i386/pc.h"
+
+/* Control register */
+#define IPMI_BT_CLR_WR_BIT         0
+#define IPMI_BT_CLR_RD_BIT         1
+#define IPMI_BT_H2B_ATN_BIT        2
+#define IPMI_BT_B2H_ATN_BIT        3
+#define IPMI_BT_SMS_ATN_BIT        4
+#define IPMI_BT_HBUSY_BIT          6
+#define IPMI_BT_BBUSY_BIT          7
+
+#define IPMI_BT_CLR_WR_MASK        (1 << IPMI_BT_CLR_WR_BIT)
+#define IPMI_BT_GET_CLR_WR(d)      (((d) >> IPMI_BT_CLR_WR_BIT) & 0x1)
+#define IPMI_BT_SET_CLR_WR(d, v)   (d) = (((d) & ~IPMI_BT_CLR_WR_MASK) | \
+                                       (((v & 1) << IPMI_BT_CLR_WR_BIT)))
+
+#define IPMI_BT_CLR_RD_MASK        (1 << IPMI_BT_CLR_RD_BIT)
+#define IPMI_BT_GET_CLR_RD(d)      (((d) >> IPMI_BT_CLR_RD_BIT) & 0x1)
+#define IPMI_BT_SET_CLR_RD(d, v)   (d) = (((d) & ~IPMI_BT_CLR_RD_MASK) | \
+                                       (((v & 1) << IPMI_BT_CLR_RD_BIT)))
+
+#define IPMI_BT_H2B_ATN_MASK       (1 << IPMI_BT_H2B_ATN_BIT)
+#define IPMI_BT_GET_H2B_ATN(d)     (((d) >> IPMI_BT_H2B_ATN_BIT) & 0x1)
+#define IPMI_BT_SET_H2B_ATN(d, v)  (d) = (((d) & ~IPMI_BT_H2B_ATN_MASK) | \
+                                        (((v & 1) << IPMI_BT_H2B_ATN_BIT)))
+
+#define IPMI_BT_B2H_ATN_MASK       (1 << IPMI_BT_B2H_ATN_BIT)
+#define IPMI_BT_GET_B2H_ATN(d)     (((d) >> IPMI_BT_B2H_ATN_BIT) & 0x1)
+#define IPMI_BT_SET_B2H_ATN(d, v)  (d) = (((d) & ~IPMI_BT_B2H_ATN_MASK) | \
+                                        (((v & 1) << IPMI_BT_B2H_ATN_BIT)))
+
+#define IPMI_BT_SMS_ATN_MASK       (1 << IPMI_BT_SMS_ATN_BIT)
+#define IPMI_BT_GET_SMS_ATN(d)     (((d) >> IPMI_BT_SMS_ATN_BIT) & 0x1)
+#define IPMI_BT_SET_SMS_ATN(d, v)  (d) = (((d) & ~IPMI_BT_SMS_ATN_MASK) | \
+                                        (((v & 1) << IPMI_BT_SMS_ATN_BIT)))
+
+#define IPMI_BT_HBUSY_MASK         (1 << IPMI_BT_HBUSY_BIT)
+#define IPMI_BT_GET_HBUSY(d)       (((d) >> IPMI_BT_HBUSY_BIT) & 0x1)
+#define IPMI_BT_SET_HBUSY(d, v)    (d) = (((d) & ~IPMI_BT_HBUSY_MASK) | \
+                                       (((v & 1) << IPMI_BT_HBUSY_BIT)))
+
+#define IPMI_BT_BBUSY_MASK         (1 << IPMI_BT_BBUSY_BIT)
+#define IPMI_BT_GET_BBUSY(d)       (((d) >> IPMI_BT_BBUSY_BIT) & 0x1)
+#define IPMI_BT_SET_BBUSY(d, v)    (d) = (((d) & ~IPMI_BT_BBUSY_MASK) | \
+                                       (((v & 1) << IPMI_BT_BBUSY_BIT)))
+
+
+/* Mask register */
+#define IPMI_BT_B2H_IRQ_EN_BIT     0
+#define IPMI_BT_B2H_IRQ_BIT        1
+
+#define IPMI_BT_B2H_IRQ_EN_MASK      (1 << IPMI_BT_B2H_IRQ_EN_BIT)
+#define IPMI_BT_GET_B2H_IRQ_EN(d)    (((d) >> IPMI_BT_B2H_IRQ_EN_BIT) & 0x1)
+#define IPMI_BT_SET_B2H_IRQ_EN(d, v) (d) = (((d) & ~IPMI_BT_B2H_IRQ_EN_MASK) | \
+                                        (((v & 1) << IPMI_BT_B2H_IRQ_EN_BIT)))
+
+#define IPMI_BT_B2H_IRQ_MASK         (1 << IPMI_BT_B2H_IRQ_BIT)
+#define IPMI_BT_GET_B2H_IRQ(d)       (((d) >> IPMI_BT_B2H_IRQ_BIT) & 0x1)
+#define IPMI_BT_SET_B2H_IRQ(d, v)    (d) = (((d) & ~IPMI_BT_B2H_IRQ_MASK) | \
+                                        (((v & 1) << IPMI_BT_B2H_IRQ_BIT)))
+
+typedef struct IPMIBT {
+    IPMIBmc *bmc;
+
+    bool do_wake;
+
+    qemu_irq irq;
+
+    uint32_t io_base;
+    unsigned long io_length;
+    MemoryRegion io;
+
+    bool obf_irq_set;
+    bool atn_irq_set;
+    bool use_irq;
+    bool irqs_enabled;
+
+    uint8_t outmsg[MAX_IPMI_MSG_SIZE];
+    uint32_t outpos;
+    uint32_t outlen;
+
+    uint8_t inmsg[MAX_IPMI_MSG_SIZE];
+    uint32_t inlen;
+
+    uint8_t control_reg;
+    uint8_t mask_reg;
+
+    /*
+     * This is a response number that we send with the command to make
+     * sure that the response matches the command.
+     */
+    uint8_t waiting_rsp;
+    uint8_t waiting_seq;
+} IPMIBT;
+
+#define IPMI_CMD_GET_BT_INTF_CAP        0x36
+
+static void ipmi_bt_handle_event(IPMIInterface *ii)
+{
+    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+    IPMIBT *ib = iic->get_backend_data(ii);
+
+    if (ib->inlen < 4) {
+        goto out;
+    }
+    /* Note that overruns are handled by handle_command */
+    if (ib->inmsg[0] != (ib->inlen - 1)) {
+        /* Length mismatch, just ignore. */
+        IPMI_BT_SET_BBUSY(ib->control_reg, 1);
+        ib->inlen = 0;
+        goto out;
+    }
+    if ((ib->inmsg[1] == (IPMI_NETFN_APP << 2)) &&
+                        (ib->inmsg[3] == IPMI_CMD_GET_BT_INTF_CAP)) {
+        /* We handle this one ourselves. */
+        ib->outmsg[0] = 9;
+        ib->outmsg[1] = ib->inmsg[1] | 0x04;
+        ib->outmsg[2] = ib->inmsg[2];
+        ib->outmsg[3] = ib->inmsg[3];
+        ib->outmsg[4] = 0;
+        ib->outmsg[5] = 1; /* Only support 1 outstanding request. */
+        if (sizeof(ib->inmsg) > 0xff) { /* Input buffer size */
+            ib->outmsg[6] = 0xff;
+        } else {
+            ib->outmsg[6] = (unsigned char) sizeof(ib->inmsg);
+        }
+        if (sizeof(ib->outmsg) > 0xff) { /* Output buffer size */
+            ib->outmsg[7] = 0xff;
+        } else {
+            ib->outmsg[7] = (unsigned char) sizeof(ib->outmsg);
+        }
+        ib->outmsg[8] = 10; /* Max request to response time */
+        ib->outmsg[9] = 0; /* Don't recommend retries */
+        ib->outlen = 10;
+        IPMI_BT_SET_BBUSY(ib->control_reg, 0);
+        IPMI_BT_SET_B2H_ATN(ib->control_reg, 1);
+        if (ib->use_irq && ib->irqs_enabled &&
+                !IPMI_BT_GET_B2H_IRQ(ib->mask_reg) &&
+                IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
+            IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
+            qemu_irq_raise(ib->irq);
+        }
+        goto out;
+    }
+    ib->waiting_seq = ib->inmsg[2];
+    ib->inmsg[2] = ib->inmsg[1];
+    {
+        IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(ib->bmc);
+        bk->handle_command(ib->bmc, ib->inmsg + 2, ib->inlen - 2,
+                           sizeof(ib->inmsg), ib->waiting_rsp);
+    }
+ out:
+    return;
+}
+
+static void ipmi_bt_handle_rsp(IPMIInterface *ii, uint8_t msg_id,
+                                unsigned char *rsp, unsigned int rsp_len)
+{
+    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+    IPMIBT *ib = iic->get_backend_data(ii);
+
+    if (ib->waiting_rsp == msg_id) {
+        ib->waiting_rsp++;
+        if (rsp_len > (sizeof(ib->outmsg) - 2)) {
+            ib->outmsg[0] = 4;
+            ib->outmsg[1] = rsp[0];
+            ib->outmsg[2] = ib->waiting_seq;
+            ib->outmsg[3] = rsp[1];
+            ib->outmsg[4] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES;
+            ib->outlen = 5;
+        } else {
+            ib->outmsg[0] = rsp_len + 1;
+            ib->outmsg[1] = rsp[0];
+            ib->outmsg[2] = ib->waiting_seq;
+            memcpy(ib->outmsg + 3, rsp + 1, rsp_len - 1);
+            ib->outlen = rsp_len + 2;
+        }
+        IPMI_BT_SET_BBUSY(ib->control_reg, 0);
+        IPMI_BT_SET_B2H_ATN(ib->control_reg, 1);
+        if (ib->use_irq && ib->irqs_enabled &&
+                !IPMI_BT_GET_B2H_IRQ(ib->mask_reg) &&
+                IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
+            IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
+            qemu_irq_raise(ib->irq);
+        }
+    }
+}
+
+
+static uint64_t ipmi_bt_ioport_read(void *opaque, hwaddr addr, unsigned size)
+{
+    IPMIInterface *ii = opaque;
+    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+    IPMIBT *ib = iic->get_backend_data(ii);
+    uint32_t ret = 0xff;
+
+    switch (addr & 3) {
+    case 0:
+        ret = ib->control_reg;
+        break;
+    case 1:
+        if (ib->outpos < ib->outlen) {
+            ret = ib->outmsg[ib->outpos];
+            ib->outpos++;
+            if (ib->outpos == ib->outlen) {
+                ib->outpos = 0;
+                ib->outlen = 0;
+            }
+        } else {
+            ret = 0xff;
+        }
+        break;
+    case 2:
+        ret = ib->mask_reg;
+        break;
+    }
+    return ret;
+}
+
+static void ipmi_bt_signal(IPMIBT *ib, IPMIInterface *ii)
+{
+    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+
+    ib->do_wake = 1;
+    while (ib->do_wake) {
+        ib->do_wake = 0;
+        iic->handle_if_event(ii);
+    }
+}
+
+static void ipmi_bt_ioport_write(void *opaque, hwaddr addr, uint64_t val,
+                                 unsigned size)
+{
+    IPMIInterface *ii = opaque;
+    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+    IPMIBT *ib = iic->get_backend_data(ii);
+
+    switch (addr & 3) {
+    case 0:
+        if (IPMI_BT_GET_CLR_WR(val)) {
+            ib->inlen = 0;
+        }
+        if (IPMI_BT_GET_CLR_RD(val)) {
+            ib->outpos = 0;
+        }
+        if (IPMI_BT_GET_B2H_ATN(val)) {
+            IPMI_BT_SET_B2H_ATN(ib->control_reg, 0);
+        }
+        if (IPMI_BT_GET_SMS_ATN(val)) {
+            IPMI_BT_SET_SMS_ATN(ib->control_reg, 0);
+        }
+        if (IPMI_BT_GET_HBUSY(val)) {
+            /* Toggle */
+            IPMI_BT_SET_HBUSY(ib->control_reg,
+                              !IPMI_BT_GET_HBUSY(ib->control_reg));
+        }
+        if (IPMI_BT_GET_H2B_ATN(val)) {
+            IPMI_BT_SET_BBUSY(ib->control_reg, 1);
+            ipmi_bt_signal(ib, ii);
+        }
+        break;
+
+    case 1:
+        if (ib->inlen < sizeof(ib->inmsg)) {
+            ib->inmsg[ib->inlen] = val;
+        }
+        ib->inlen++;
+        break;
+
+    case 2:
+        if (IPMI_BT_GET_B2H_IRQ_EN(val) !=
+                        IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
+            if (IPMI_BT_GET_B2H_IRQ_EN(val)) {
+                if (IPMI_BT_GET_B2H_ATN(ib->control_reg) ||
+                        IPMI_BT_GET_SMS_ATN(ib->control_reg)) {
+                    IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
+                    qemu_irq_raise(ib->irq);
+                }
+                IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 1);
+            } else {
+                if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
+                    IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
+                    qemu_irq_lower(ib->irq);
+                }
+                IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0);
+            }
+        }
+        if (IPMI_BT_GET_B2H_IRQ(val) && IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
+            IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
+            qemu_irq_lower(ib->irq);
+        }
+        break;
+    }
+}
+
+static const MemoryRegionOps ipmi_bt_io_ops = {
+    .read = ipmi_bt_ioport_read,
+    .write = ipmi_bt_ioport_write,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void ipmi_bt_set_atn(IPMIInterface *ii, int val, int irq)
+{
+    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+    IPMIBT *ib = iic->get_backend_data(ii);
+
+    if (!!val == IPMI_BT_GET_SMS_ATN(ib->control_reg)) {
+        return;
+    }
+
+    IPMI_BT_SET_SMS_ATN(ib->control_reg, val);
+    if (val) {
+        if (irq && ib->use_irq && ib->irqs_enabled &&
+                !IPMI_BT_GET_B2H_ATN(ib->control_reg) &&
+                IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
+            IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
+            qemu_irq_raise(ib->irq);
+        }
+    } else {
+        if (!IPMI_BT_GET_B2H_ATN(ib->control_reg) &&
+                IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
+            IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
+            qemu_irq_lower(ib->irq);
+        }
+    }
+}
+
+static void ipmi_bt_handle_reset(IPMIInterface *ii, bool is_cold)
+{
+    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+    IPMIBT *ib = iic->get_backend_data(ii);
+
+    if (is_cold) {
+        /* Disable the BT interrupt on reset */
+        if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
+            IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
+            qemu_irq_lower(ib->irq);
+        }
+        IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0);
+    }
+}
+
+static void ipmi_bt_set_irq_enable(IPMIInterface *ii, int val)
+{
+    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+    IPMIBT *ib = iic->get_backend_data(ii);
+
+    ib->irqs_enabled = val;
+}
+
+static void ipmi_bt_init(IPMIInterface *ii, Error **errp)
+{
+    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+    IPMIBT *ib = iic->get_backend_data(ii);
+
+    ib->io_length = 3;
+
+    memory_region_init_io(&ib->io, NULL, &ipmi_bt_io_ops, ii, "ipmi-bt", 3);
+}
+
+static void ipmi_bt_class_init(IPMIInterfaceClass *iic)
+{
+    iic->init = ipmi_bt_init;
+    iic->set_atn = ipmi_bt_set_atn;
+    iic->handle_rsp = ipmi_bt_handle_rsp;
+    iic->handle_if_event = ipmi_bt_handle_event;
+    iic->set_irq_enable = ipmi_bt_set_irq_enable;
+    iic->reset = ipmi_bt_handle_reset;
+}
+
+
+#define TYPE_ISA_IPMI_BT "isa-ipmi-bt"
+#define ISA_IPMI_BT(obj) OBJECT_CHECK(ISAIPMIBTDevice, (obj), \
+                                       TYPE_ISA_IPMI_BT)
+
+typedef struct ISAIPMIBTDevice {
+    ISADevice dev;
+    int32 isairq;
+    IPMIBT bt;
+} ISAIPMIBTDevice;
+
+static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp)
+{
+    ISADevice *isadev = ISA_DEVICE(dev);
+    ISAIPMIBTDevice *iib = ISA_IPMI_BT(dev);
+    IPMIInterface *ii = IPMI_INTERFACE(dev);
+    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+
+    if (!iib->bt.bmc) {
+        error_setg(errp, "IPMI device requires a bmc attribute to be set");
+        return;
+    }
+
+    iib->bt.bmc->intf = ii;
+
+    iic->init(ii, errp);
+    if (*errp)
+        return;
+
+    if (iib->isairq > 0) {
+        isa_init_irq(isadev, &iib->bt.irq, iib->isairq);
+        iib->bt.use_irq = 1;
+    }
+
+    qdev_set_legacy_instance_id(dev, iib->bt.io_base, iib->bt.io_length);
+
+    isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base);
+}
+
+static void isa_ipmi_bt_init(Object *obj)
+{
+    ISAIPMIBTDevice *iib = ISA_IPMI_BT(obj);
+
+    ipmi_bmc_find_and_link(obj, (Object **) &iib->bt.bmc);
+}
+
+static void *isa_ipmi_bt_get_backend_data(IPMIInterface *ii)
+{
+    ISAIPMIBTDevice *iib = ISA_IPMI_BT(ii);
+
+    return &iib->bt;
+}
+
+static Property ipmi_isa_properties[] = {
+    DEFINE_PROP_UINT32("ioport", ISAIPMIBTDevice, bt.io_base,  0xe4),
+    DEFINE_PROP_INT32("irq",   ISAIPMIBTDevice, isairq,  5),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void isa_ipmi_bt_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc);
+
+    dc->realize = isa_ipmi_bt_realize;
+    dc->props = ipmi_isa_properties;
+
+    iic->get_backend_data = isa_ipmi_bt_get_backend_data;
+    ipmi_bt_class_init(iic);
+}
+
+static const TypeInfo isa_ipmi_bt_info = {
+    .name          = TYPE_ISA_IPMI_BT,
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(ISAIPMIBTDevice),
+    .instance_init = isa_ipmi_bt_init,
+    .class_init    = isa_ipmi_bt_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_IPMI_INTERFACE },
+        { }
+    }
+};
+
+static void ipmi_register_types(void)
+{
+    type_register_static(&isa_ipmi_bt_info);
+}
+
+type_init(ipmi_register_types)
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 06/16] ipmi: Add tests
  2015-12-17 18:50 [Qemu-devel] [PATCH v5 0/16] Add an IPMI device to QEMU minyard
                   ` (4 preceding siblings ...)
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 05/16] ipmi: Add a BT " minyard
@ 2015-12-17 18:50 ` minyard
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 07/16] ipmi: Add documentation minyard
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: minyard @ 2015-12-17 18:50 UTC (permalink / raw)
  To: qemu-devel, Paolo Bonzini, Michael S. Tsirkin, Igor Mammedov
  Cc: Corey Minyard

From: Corey Minyard <cminyard@mvista.com>

Test the KCS interface with a local BMC and a BT interface with an
external BMC.

Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
 tests/Makefile        |   4 +
 tests/ipmi-bt-test.c  | 436 ++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/ipmi-kcs-test.c | 295 ++++++++++++++++++++++++++++++++++
 3 files changed, 735 insertions(+)
 create mode 100644 tests/ipmi-bt-test.c
 create mode 100644 tests/ipmi-kcs-test.c

diff --git a/tests/Makefile b/tests/Makefile
index 053c1ae..f41070b 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -166,6 +166,8 @@ gcov-files-i386-y += hw/block/hd-geometry.c
 check-qtest-i386-y += tests/boot-order-test$(EXESUF)
 check-qtest-i386-y += tests/bios-tables-test$(EXESUF)
 check-qtest-i386-y += tests/rtc-test$(EXESUF)
+check-qtest-i386-y += tests/ipmi-kcs-test$(EXESUF)
+check-qtest-i386-y += tests/ipmi-bt-test$(EXESUF)
 check-qtest-i386-y += tests/i440fx-test$(EXESUF)
 check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
 check-qtest-i386-y += tests/drive_del-test$(EXESUF)
@@ -488,6 +490,8 @@ tests/spapr-phb-test$(EXESUF): tests/spapr-phb-test.o $(libqos-obj-y)
 tests/fdc-test$(EXESUF): tests/fdc-test.o
 tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y)
 tests/ahci-test$(EXESUF): tests/ahci-test.o $(libqos-pc-obj-y)
+tests/ipmi-kcs-test$(EXESUF): tests/ipmi-kcs-test.o
+tests/ipmi-bt-test$(EXESUF): tests/ipmi-bt-test.o
 tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o
 tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y)
 tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o $(libqos-obj-y)
diff --git a/tests/ipmi-bt-test.c b/tests/ipmi-bt-test.c
new file mode 100644
index 0000000..5b7eb73
--- /dev/null
+++ b/tests/ipmi-bt-test.c
@@ -0,0 +1,436 @@
+/*
+ * IPMI BT test cases, using the external interface for checking
+ *
+ * Copyright (c) 2012 Corey Minyard <cminyard@mvista.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#include <glib.h>
+
+#include "libqtest.h"
+#include "qemu-common.h"
+
+#define IPMI_IRQ        5
+
+#define IPMI_BT_BASE    0xe4
+
+#define IPMI_BT_CTLREG_CLR_WR_PTR  0
+#define IPMI_BT_CTLREG_CLR_RD_PTR  1
+#define IPMI_BT_CTLREG_H2B_ATN     2
+#define IPMI_BT_CTLREG_B2H_ATN     3
+#define IPMI_BT_CTLREG_SMS_ATN     4
+#define IPMI_BT_CTLREG_H_BUSY      6
+#define IPMI_BT_CTLREG_B_BUSY      7
+
+#define IPMI_BT_CTLREG_GET(b) ((bt_get_ctrlreg() >> (b)) & 1)
+#define IPMI_BT_CTLREG_GET_H2B_ATN() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_H2B_ATN)
+#define IPMI_BT_CTLREG_GET_B2H_ATN() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_B2H_ATN)
+#define IPMI_BT_CTLREG_GET_SMS_ATN() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_SMS_ATN)
+#define IPMI_BT_CTLREG_GET_H_BUSY()  IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_H_BUSY)
+#define IPMI_BT_CTLREG_GET_B_BUSY()  IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_B_BUSY)
+
+#define IPMI_BT_CTLREG_SET(b) bt_write_ctrlreg(1 << (b))
+#define IPMI_BT_CTLREG_SET_CLR_WR_PTR() IPMI_BT_CTLREG_SET( \
+                                                IPMI_BT_CTLREG_CLR_WR_PTR)
+#define IPMI_BT_CTLREG_SET_CLR_RD_PTR() IPMI_BT_CTLREG_SET( \
+                                                IPMI_BT_CTLREG_CLR_RD_PTR)
+#define IPMI_BT_CTLREG_SET_H2B_ATN()  IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_H2B_ATN)
+#define IPMI_BT_CTLREG_SET_B2H_ATN()  IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_B2H_ATN)
+#define IPMI_BT_CTLREG_SET_SMS_ATN()  IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_SMS_ATN)
+#define IPMI_BT_CTLREG_SET_H_BUSY()   IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_H_BUSY)
+
+static int bt_ints_enabled;
+
+static uint8_t bt_get_ctrlreg(void)
+{
+    return inb(IPMI_BT_BASE);
+}
+
+static void bt_write_ctrlreg(uint8_t val)
+{
+    outb(IPMI_BT_BASE, val);
+}
+
+static uint8_t bt_get_buf(void)
+{
+    return inb(IPMI_BT_BASE + 1);
+}
+
+static void bt_write_buf(uint8_t val)
+{
+    outb(IPMI_BT_BASE + 1, val);
+}
+
+static uint8_t bt_get_irqreg(void)
+{
+    return inb(IPMI_BT_BASE + 2);
+}
+
+static void bt_write_irqreg(uint8_t val)
+{
+    outb(IPMI_BT_BASE + 2, val);
+}
+
+static void bt_wait_b_busy(void)
+{
+    unsigned int count = 1000;
+    while (IPMI_BT_CTLREG_GET_B_BUSY() != 0) {
+        g_assert(--count != 0);
+    }
+}
+
+static void bt_wait_b2h_atn(void)
+{
+    unsigned int count = 1000;
+    while (IPMI_BT_CTLREG_GET_B2H_ATN() == 0) {
+        g_assert(--count != 0);
+    }
+}
+
+
+static int emu_lfd;
+static int emu_fd;
+static in_port_t emu_port;
+static uint8_t inbuf[100];
+static unsigned int inbuf_len;
+static unsigned int inbuf_pos;
+static int last_was_aa;
+
+static void read_emu_data(void)
+{
+    fd_set readfds;
+    int rv;
+    struct timeval tv;
+
+    FD_ZERO(&readfds);
+    FD_SET(emu_fd, &readfds);
+    tv.tv_sec = 10;
+    tv.tv_usec = 0;
+    rv = select(emu_fd + 1, &readfds, NULL, NULL, &tv);
+    if (rv == -1) {
+        perror("select");
+    }
+    g_assert(rv == 1);
+    rv = read(emu_fd, inbuf, sizeof(inbuf));
+    if (rv == -1) {
+        perror("read");
+    }
+    g_assert(rv > 0);
+    inbuf_len = rv;
+    inbuf_pos = 0;
+}
+
+static void write_emu_msg(uint8_t *msg, unsigned int len)
+{
+    int rv;
+
+#ifdef DEBUG_TEST
+    {
+        unsigned int i;
+        printf("sending:");
+        for (i = 0; i < len; i++) {
+            printf(" %2.2x", msg[i]);
+        }
+        printf("\n");
+    }
+#endif
+    rv = write(emu_fd, msg, len);
+    g_assert(rv == len);
+}
+
+static void get_emu_msg(uint8_t *msg, unsigned int *len)
+{
+    unsigned int outpos = 0;
+
+    for (;;) {
+        while (inbuf_pos < inbuf_len) {
+            uint8_t ch = inbuf[inbuf_pos++];
+
+            g_assert(outpos < *len);
+            if (last_was_aa) {
+                assert(ch & 0x10);
+                msg[outpos++] = ch & ~0x10;
+                last_was_aa = 0;
+            } else if (ch == 0xaa) {
+                last_was_aa = 1;
+            } else {
+                msg[outpos++] = ch;
+                if ((ch == 0xa0) || (ch == 0xa1)) {
+                    /* Message complete */
+                    *len = outpos;
+                    goto done;
+                }
+            }
+        }
+        read_emu_data();
+    }
+ done:
+#ifdef DEBUG_TEST
+    {
+        unsigned int i;
+        printf("Msg:");
+        for (i = 0; i < outpos; i++) {
+            printf(" %2.2x", msg[i]);
+        }
+        printf("\n");
+    }
+#endif
+    return;
+}
+
+static uint8_t
+ipmb_checksum(const unsigned char *data, int size, unsigned char start)
+{
+        unsigned char csum = start;
+
+        for (; size > 0; size--, data++) {
+                csum += *data;
+        }
+        return csum;
+}
+
+static uint8_t get_dev_id_cmd[] = { 0x18, 0x01 };
+static uint8_t get_dev_id_rsp[] = { 0x1c, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
+                                    0x02, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static uint8_t set_bmc_globals_cmd[] = { 0x18, 0x2e, 0x0f };
+static uint8_t set_bmc_globals_rsp[] = { 0x1c, 0x2e, 0x00 };
+static uint8_t enable_irq_cmd[] = { 0x05, 0xa1 };
+
+static void emu_msg_handler(void)
+{
+    uint8_t msg[100];
+    unsigned int msg_len = sizeof(msg);
+
+    get_emu_msg(msg, &msg_len);
+    g_assert(msg_len >= 5);
+    g_assert(msg[msg_len - 1] == 0xa0);
+    msg_len--;
+    g_assert(ipmb_checksum(msg, msg_len, 0) == 0);
+    msg_len--;
+    if ((msg[1] == get_dev_id_cmd[0]) && (msg[2] == get_dev_id_cmd[1])) {
+        memcpy(msg + 1, get_dev_id_rsp, sizeof(get_dev_id_rsp));
+        msg_len = sizeof(get_dev_id_rsp) + 1;
+        msg[msg_len] = -ipmb_checksum(msg, msg_len, 0);
+        msg_len++;
+        msg[msg_len++] = 0xa0;
+        write_emu_msg(msg, msg_len);
+    } else if ((msg[1] == set_bmc_globals_cmd[0]) &&
+               (msg[2] == set_bmc_globals_cmd[1])) {
+        memcpy(msg + 1, set_bmc_globals_rsp, sizeof(set_bmc_globals_rsp));
+        msg_len = sizeof(set_bmc_globals_rsp) + 1;
+        msg[msg_len] = -ipmb_checksum(msg, msg_len, 0);
+        msg_len++;
+        msg[msg_len++] = 0xa0;
+        write_emu_msg(msg, msg_len);
+        write_emu_msg(enable_irq_cmd, sizeof(enable_irq_cmd));
+    } else {
+        g_assert(0);
+    }
+}
+
+static void bt_cmd(uint8_t *cmd, unsigned int cmd_len,
+                    uint8_t *rsp, unsigned int *rsp_len)
+{
+    unsigned int i, len, j = 0;
+    uint8_t seq = 5;
+
+    /* Should be idle */
+    g_assert(bt_get_ctrlreg() == 0);
+
+    bt_wait_b_busy();
+    IPMI_BT_CTLREG_SET_CLR_WR_PTR();
+    bt_write_buf(cmd_len + 1);
+    bt_write_buf(cmd[0]);
+    bt_write_buf(seq);
+    for (i = 1; i < cmd_len; i++) {
+        bt_write_buf(cmd[i]);
+    }
+    IPMI_BT_CTLREG_SET_H2B_ATN();
+
+    emu_msg_handler(); /* We should get a message on the socket here. */
+
+    bt_wait_b2h_atn();
+    if (bt_ints_enabled) {
+        g_assert((bt_get_irqreg() & 0x02) == 0x02);
+        g_assert(get_irq(IPMI_IRQ));
+        bt_write_irqreg(0x03);
+    } else {
+        g_assert(!get_irq(IPMI_IRQ));
+    }
+    IPMI_BT_CTLREG_SET_H_BUSY();
+    IPMI_BT_CTLREG_SET_B2H_ATN();
+    IPMI_BT_CTLREG_SET_CLR_RD_PTR();
+    len = bt_get_buf();
+    g_assert(len >= 4);
+    rsp[0] = bt_get_buf();
+    assert(bt_get_buf() == seq);
+    len--;
+    for (j = 1; j < len; j++) {
+        rsp[j] = bt_get_buf();
+    }
+    IPMI_BT_CTLREG_SET_H_BUSY();
+    *rsp_len = j;
+}
+
+
+/*
+ * We should get a connect request and a short message with capabilities.
+ */
+static void test_connect(void)
+{
+    fd_set readfds;
+    int rv;
+    int val;
+    struct timeval tv;
+    uint8_t msg[100];
+    unsigned int msglen;
+    static uint8_t exp1[] = { 0xff, 0x01, 0xa1 }; /* A protocol version */
+    static uint8_t exp2[] = { 0x08, 0x1f, 0xa1 }; /* A capabilities cmd */
+
+    FD_ZERO(&readfds);
+    FD_SET(emu_lfd, &readfds);
+    tv.tv_sec = 10;
+    tv.tv_usec = 0;
+    rv = select(emu_lfd + 1, &readfds, NULL, NULL, &tv);
+    g_assert(rv == 1);
+    emu_fd = accept(emu_lfd, NULL, 0);
+    if (emu_fd < 0) {
+        perror("accept");
+    }
+    g_assert(emu_fd >= 0);
+
+    val = 1;
+    rv = setsockopt(emu_fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
+    g_assert(rv != -1);
+
+    /* Report our version */
+    write_emu_msg(exp1, sizeof(exp1));
+
+    /* Validate that we get the info we expect. */
+    msglen = sizeof(msg);
+    get_emu_msg(msg, &msglen);
+    g_assert(msglen == sizeof(exp1));
+    g_assert(memcmp(msg, exp1, msglen) == 0);
+    msglen = sizeof(msg);
+    get_emu_msg(msg, &msglen);
+    g_assert(msglen == sizeof(exp2));
+    g_assert(memcmp(msg, exp2, msglen) == 0);
+}
+
+/*
+ * Send a get_device_id to do a basic test.
+ */
+static void test_bt_base(void)
+{
+    uint8_t rsp[20];
+    unsigned int rsplen = sizeof(rsp);
+
+    bt_cmd(get_dev_id_cmd, sizeof(get_dev_id_cmd), rsp, &rsplen);
+    g_assert(rsplen == sizeof(get_dev_id_rsp));
+    g_assert(memcmp(get_dev_id_rsp, rsp, rsplen) == 0);
+}
+
+/*
+ * Enable IRQs for the interface.
+ */
+static void test_enable_irq(void)
+{
+    uint8_t rsp[20];
+    unsigned int rsplen = sizeof(rsp);
+
+    bt_cmd(set_bmc_globals_cmd, sizeof(set_bmc_globals_cmd), rsp, &rsplen);
+    g_assert(rsplen == sizeof(set_bmc_globals_rsp));
+    g_assert(memcmp(set_bmc_globals_rsp, rsp, rsplen) == 0);
+    bt_write_irqreg(0x01);
+    bt_ints_enabled = 1;
+}
+
+/*
+ * Create a local TCP socket with any port, then save off the port we got.
+ */
+static void open_socket(void)
+{
+    struct sockaddr_in myaddr;
+    socklen_t addrlen;
+
+    myaddr.sin_family = AF_INET;
+    myaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    myaddr.sin_port = 0;
+    emu_lfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if (emu_lfd == -1) {
+        perror("socket");
+        exit(1);
+    }
+    if (bind(emu_lfd, (struct sockaddr *) &myaddr, sizeof(myaddr)) == -1) {
+        perror("bind");
+        exit(1);
+    }
+    addrlen = sizeof(myaddr);
+    if (getsockname(emu_lfd, (struct sockaddr *) &myaddr , &addrlen) == -1) {
+        perror("getsockname");
+        exit(1);
+    }
+    emu_port = ntohs(myaddr.sin_port);
+    assert(listen(emu_lfd, 1) != -1);
+}
+
+int main(int argc, char **argv)
+{
+    const char *arch = qtest_get_arch();
+    char *cmdline;
+    int ret;
+
+    /* Check architecture */
+    if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) {
+        g_test_message("Skipping test for non-x86\n");
+        return 0;
+    }
+
+    open_socket();
+
+    /* Run the tests */
+    g_test_init(&argc, &argv, NULL);
+
+    cmdline = g_strdup_printf("-vnc none"
+          " -chardev socket,id=ipmi0,host=localhost,port=%d,reconnect=10"
+          " -device ipmi-bmc-extern,chardev=ipmi0,id=bmc0"
+          " -device isa-ipmi-bt,bmc=bmc0", emu_port);
+    qtest_start(cmdline);
+    qtest_irq_intercept_in(global_qtest, "ioapic");
+    qtest_add_func("/ipmi/extern/connect", test_connect);
+    qtest_add_func("/ipmi/extern/bt_base", test_bt_base);
+    qtest_add_func("/ipmi/extern/bt_enable_irq", test_enable_irq);
+    qtest_add_func("/ipmi/extern/bt_base_irq", test_bt_base);
+    ret = g_test_run();
+    qtest_quit(global_qtest);
+
+    return ret;
+}
diff --git a/tests/ipmi-kcs-test.c b/tests/ipmi-kcs-test.c
new file mode 100644
index 0000000..564c470
--- /dev/null
+++ b/tests/ipmi-kcs-test.c
@@ -0,0 +1,295 @@
+/*
+ * IPMI KCS test cases, using the local interface.
+ *
+ * Copyright (c) 2012 Corey Minyard <cminyard@mvista.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include "libqtest.h"
+
+#define IPMI_IRQ        5
+
+#define IPMI_KCS_BASE   0xca2
+
+#define IPMI_KCS_STATUS_ABORT           0x60
+#define IPMI_KCS_CMD_WRITE_START        0x61
+#define IPMI_KCS_CMD_WRITE_END          0x62
+#define IPMI_KCS_CMD_READ               0x68
+
+#define IPMI_KCS_ABORTED_BY_CMD         0x01
+
+#define IPMI_KCS_CMDREG_GET_STATE() ((kcs_get_cmdreg() >> 6) & 3)
+#define IPMI_KCS_STATE_IDLE     0
+#define IPMI_KCS_STATE_READ     1
+#define IPMI_KCS_STATE_WRITE    2
+#define IPMI_KCS_STATE_ERROR    3
+#define IPMI_KCS_CMDREG_GET_CD()    ((kcs_get_cmdreg() >> 3) & 1)
+#define IPMI_KCS_CMDREG_GET_ATN()   ((kcs_get_cmdreg() >> 2) & 1)
+#define IPMI_KCS_CMDREG_GET_IBF()   ((kcs_get_cmdreg() >> 1) & 1)
+#define IPMI_KCS_CMDREG_GET_OBF()   ((kcs_get_cmdreg() >> 0) & 1)
+
+static int kcs_ints_enabled;
+
+static uint8_t kcs_get_cmdreg(void)
+{
+    return inb(IPMI_KCS_BASE + 1);
+}
+
+static void kcs_write_cmdreg(uint8_t val)
+{
+    outb(IPMI_KCS_BASE + 1, val);
+}
+
+static uint8_t kcs_get_datareg(void)
+{
+    return inb(IPMI_KCS_BASE);
+}
+
+static void kcs_write_datareg(uint8_t val)
+{
+    outb(IPMI_KCS_BASE, val);
+}
+
+static void kcs_wait_ibf(void)
+{
+    unsigned int count = 1000;
+    while (IPMI_KCS_CMDREG_GET_IBF() != 0) {
+        g_assert(--count != 0);
+    }
+}
+
+static void kcs_wait_obf(void)
+{
+    unsigned int count = 1000;
+    while (IPMI_KCS_CMDREG_GET_OBF() == 0) {
+        g_assert(--count != 0);
+    }
+}
+
+static void kcs_clear_obf(void)
+{
+    if (kcs_ints_enabled) {
+        g_assert(get_irq(IPMI_IRQ));
+    } else {
+        g_assert(!get_irq(IPMI_IRQ));
+    }
+    g_assert(IPMI_KCS_CMDREG_GET_OBF() == 1);
+    kcs_get_datareg();
+    g_assert(IPMI_KCS_CMDREG_GET_OBF() == 0);
+    g_assert(!get_irq(IPMI_IRQ));
+}
+
+static void kcs_check_state(uint8_t state)
+{
+    g_assert(IPMI_KCS_CMDREG_GET_STATE() == state);
+}
+
+static void kcs_cmd(uint8_t *cmd, unsigned int cmd_len,
+                    uint8_t *rsp, unsigned int *rsp_len)
+{
+    unsigned int i, j = 0;
+
+    /* Should be idle */
+    g_assert(kcs_get_cmdreg() == 0);
+
+    kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_START);
+    kcs_wait_ibf();
+    kcs_check_state(IPMI_KCS_STATE_WRITE);
+    kcs_clear_obf();
+    for (i = 0; i < cmd_len; i++) {
+        kcs_write_datareg(cmd[i]);
+        kcs_wait_ibf();
+        kcs_check_state(IPMI_KCS_STATE_WRITE);
+        kcs_clear_obf();
+    }
+    kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_END);
+    kcs_wait_ibf();
+    kcs_check_state(IPMI_KCS_STATE_WRITE);
+    kcs_clear_obf();
+    kcs_write_datareg(0);
+ next_read_byte:
+    kcs_wait_ibf();
+    switch (IPMI_KCS_CMDREG_GET_STATE()) {
+    case IPMI_KCS_STATE_READ:
+        kcs_wait_obf();
+        g_assert(j < *rsp_len);
+        rsp[j++] = kcs_get_datareg();
+        kcs_write_datareg(IPMI_KCS_CMD_READ);
+        goto next_read_byte;
+        break;
+
+    case IPMI_KCS_STATE_IDLE:
+        kcs_wait_obf();
+        kcs_get_datareg();
+        break;
+
+    default:
+        g_assert(0);
+    }
+    *rsp_len = j;
+}
+
+static void kcs_abort(uint8_t *cmd, unsigned int cmd_len,
+                      uint8_t *rsp, unsigned int *rsp_len)
+{
+    unsigned int i, j = 0;
+    unsigned int retries = 4;
+
+    /* Should be idle */
+    g_assert(kcs_get_cmdreg() == 0);
+
+    kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_START);
+    kcs_wait_ibf();
+    kcs_check_state(IPMI_KCS_STATE_WRITE);
+    kcs_clear_obf();
+    for (i = 0; i < cmd_len; i++) {
+        kcs_write_datareg(cmd[i]);
+        kcs_wait_ibf();
+        kcs_check_state(IPMI_KCS_STATE_WRITE);
+        kcs_clear_obf();
+    }
+    kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_END);
+    kcs_wait_ibf();
+    kcs_check_state(IPMI_KCS_STATE_WRITE);
+    kcs_clear_obf();
+    kcs_write_datareg(0);
+    kcs_wait_ibf();
+    switch (IPMI_KCS_CMDREG_GET_STATE()) {
+    case IPMI_KCS_STATE_READ:
+        kcs_wait_obf();
+        g_assert(j < *rsp_len);
+        rsp[j++] = kcs_get_datareg();
+        kcs_write_datareg(IPMI_KCS_CMD_READ);
+        break;
+
+    default:
+        g_assert(0);
+    }
+
+    /* Start the abort here */
+ retry_abort:
+    g_assert(retries > 0);
+
+    kcs_wait_ibf();
+    kcs_write_cmdreg(IPMI_KCS_STATUS_ABORT);
+    kcs_wait_ibf();
+    kcs_clear_obf();
+    kcs_write_datareg(0);
+    kcs_wait_ibf();
+    if (IPMI_KCS_CMDREG_GET_STATE() != IPMI_KCS_STATE_READ) {
+        retries--;
+        goto retry_abort;
+    }
+    kcs_wait_obf();
+    rsp[0] = kcs_get_datareg();
+    kcs_write_datareg(IPMI_KCS_CMD_READ);
+    kcs_wait_ibf();
+    if (IPMI_KCS_CMDREG_GET_STATE() != IPMI_KCS_STATE_IDLE) {
+        retries--;
+        goto retry_abort;
+    }
+    kcs_wait_obf();
+    kcs_clear_obf();
+
+    *rsp_len = j;
+}
+
+
+static uint8_t get_dev_id_cmd[] = { 0x18, 0x01 };
+static uint8_t get_dev_id_rsp[] = { 0x1c, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
+                                    0x02, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+/*
+ * Send a get_device_id to do a basic test.
+ */
+static void test_kcs_base(void)
+{
+    uint8_t rsp[20];
+    unsigned int rsplen = sizeof(rsp);
+
+    kcs_cmd(get_dev_id_cmd, sizeof(get_dev_id_cmd), rsp, &rsplen);
+    g_assert(rsplen == sizeof(get_dev_id_rsp));
+    g_assert(memcmp(get_dev_id_rsp, rsp, rsplen) == 0);
+}
+
+/*
+ * Abort a kcs operation while reading
+ */
+static void test_kcs_abort(void)
+{
+    uint8_t rsp[20];
+    unsigned int rsplen = sizeof(rsp);
+
+    kcs_abort(get_dev_id_cmd, sizeof(get_dev_id_cmd), rsp, &rsplen);
+    g_assert(rsp[0] == IPMI_KCS_ABORTED_BY_CMD);
+}
+
+static uint8_t set_bmc_globals_cmd[] = { 0x18, 0x2e, 0x0f };
+static uint8_t set_bmc_globals_rsp[] = { 0x1c, 0x2e, 0x00 };
+
+/*
+ * Enable interrupts
+ */
+static void test_enable_irq(void)
+{
+    uint8_t rsp[20];
+    unsigned int rsplen = sizeof(rsp);
+
+    kcs_cmd(set_bmc_globals_cmd, sizeof(set_bmc_globals_cmd), rsp, &rsplen);
+    g_assert(rsplen == sizeof(set_bmc_globals_rsp));
+    g_assert(memcmp(set_bmc_globals_rsp, rsp, rsplen) == 0);
+    kcs_ints_enabled = 1;
+}
+
+int main(int argc, char **argv)
+{
+    const char *arch = qtest_get_arch();
+    char *cmdline;
+    int ret;
+
+    /* Check architecture */
+    if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) {
+        g_test_message("Skipping test for non-x86\n");
+        return 0;
+    }
+
+    /* Run the tests */
+    g_test_init(&argc, &argv, NULL);
+
+    cmdline = g_strdup_printf("-vnc none -device ipmi-bmc-sim,id=bmc0"
+                              " -device isa-ipmi-kcs,bmc=bmc0");
+    qtest_start(cmdline);
+    qtest_irq_intercept_in(global_qtest, "ioapic");
+    qtest_add_func("/ipmi/local/kcs_base", test_kcs_base);
+    qtest_add_func("/ipmi/local/kcs_abort", test_kcs_abort);
+    qtest_add_func("/ipmi/local/kcs_enable_irq", test_enable_irq);
+    qtest_add_func("/ipmi/local/kcs_base_irq", test_kcs_base);
+    qtest_add_func("/ipmi/local/kcs_abort_irq", test_kcs_abort);
+    ret = g_test_run();
+    qtest_quit(global_qtest);
+
+    return ret;
+}
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 07/16] ipmi: Add documentation
  2015-12-17 18:50 [Qemu-devel] [PATCH v5 0/16] Add an IPMI device to QEMU minyard
                   ` (5 preceding siblings ...)
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 06/16] ipmi: Add tests minyard
@ 2015-12-17 18:50 ` minyard
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 08/16] ipmi: Add migration capability to the IPMI devices minyard
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: minyard @ 2015-12-17 18:50 UTC (permalink / raw)
  To: qemu-devel, Paolo Bonzini, Michael S. Tsirkin, Igor Mammedov
  Cc: Corey Minyard

From: Corey Minyard <cminyard@mvista.com>

Add some basic documentation for the IPMI device.

Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
 qemu-options.hx | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/qemu-options.hx b/qemu-options.hx
index 0eea4ee..985d2ab 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -381,6 +381,58 @@ Add device @var{driver}.  @var{prop}=@var{value} sets driver
 properties.  Valid properties depend on the driver.  To get help on
 possible drivers and properties, use @code{-device help} and
 @code{-device @var{driver},help}.
+
+Some drivers are:
+@item -device ipmi-bmc-sim,id=@var{id}[,slave_addr=@var{val}]
+
+Add an IPMI BMC.  This is a simulation of a hardware management
+interface processor that normally sits on a system.  It provides
+a watchdog and the ability to reset and power control the system.
+You need to connect this to an IPMI interface to make it useful
+
+The IPMI slave address to use for the BMC.  The default is 0x20.
+This address is the BMC's address on the I2C network of management
+controllers.  If you don't know what this means, it is safe to ignore
+it.
+
+@item -device ipmi-bmc-extern,id=@var{id},chardev=@var{id}[,slave_addr=@var{val}]
+
+Add a connection to an external IPMI BMC simulator.  Instead of
+locally emulating the BMC like the above item, instead connect
+to an external entity that provides the IPMI services.
+
+A connection is made to an external BMC simulator.  If you do this, it
+is strongly recommended that you use the "reconnect=" chardev option
+to reconnect to the simulator if the connection is lost.  Note that if
+this is not used carefully, it can be a security issue, as the
+interface has the ability to send resets, NMIs, and power off the VM.
+It's best if QEMU makes a connection to an external simulator running
+on a secure port on localhost, so neither the simulator nor QEMU is
+exposed to any outside network.
+
+See the "lanserv/README.vm" file in the OpenIPMI library for more
+details on the external interface.
+
+@item -device isa-ipmi-kcs,bmc=@var{id}[,ioport=@var{val}][,irq=@var{val}]
+
+Add a KCS IPMI interafce on the ISA bus.  This also adds a
+corresponding ACPI and SMBIOS entries, if appropriate.
+
+@table @option
+@item bmc=@var{id}
+The BMC to connect to, one of ipmi-bmc-sim or ipmi-bmc-extern above.
+@item ioport=@var{val}
+Define the I/O address of the interface.  The default is 0xca0 for KCS.
+@item irq=@var{val}
+Define the interrupt to use.  The default is 5.  To disable interrupts,
+set this to 0.
+@end table
+
+@item -device isa-ipmi-bt,bmc=@var{id}[,ioport=@var{val}][,irq=@var{val}]
+
+Like the KCS interface, but defines a BT interface.  The default port is
+0xe4 and the default interrupt is 5.
+
 ETEXI
 
 DEF("name", HAS_ARG, QEMU_OPTION_name,
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 08/16] ipmi: Add migration capability to the IPMI devices.
  2015-12-17 18:50 [Qemu-devel] [PATCH v5 0/16] Add an IPMI device to QEMU minyard
                   ` (6 preceding siblings ...)
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 07/16] ipmi: Add documentation minyard
@ 2015-12-17 18:50 ` minyard
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 09/16] ipmi: Add a firmware configuration repository minyard
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: minyard @ 2015-12-17 18:50 UTC (permalink / raw)
  To: qemu-devel, Paolo Bonzini, Michael S. Tsirkin, Igor Mammedov
  Cc: Corey Minyard

From: Corey Minyard <cminyard@mvista.com>

Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
 hw/ipmi/ipmi_bmc_extern.c | 34 ++++++++++++++++++++++++++++++++++
 hw/ipmi/ipmi_bmc_sim.c    | 30 ++++++++++++++++++++++++++++++
 hw/ipmi/isa_ipmi_bt.c     | 24 ++++++++++++++++++++++++
 hw/ipmi/isa_ipmi_kcs.c    | 26 ++++++++++++++++++++++++++
 4 files changed, 114 insertions(+)

diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c
index d4669f0..05b9121 100644
--- a/hw/ipmi/ipmi_bmc_extern.c
+++ b/hw/ipmi/ipmi_bmc_extern.c
@@ -439,11 +439,45 @@ static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp)
     qemu_chr_add_handlers(ibe->chr, can_receive, receive, chr_event, ibe);
 }
 
+static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id)
+{
+    IPMIBmcExtern *ibe = opaque;
+
+    /*
+     * We don't directly restore waiting_rsp, Instead, we return an
+     * error on the interface if a response was being waited for.
+     */
+    if (ibe->waiting_rsp) {
+        IPMIInterface *ii = ibe->parent.intf;
+        IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+
+        ibe->waiting_rsp = false;
+        ibe->inbuf[1] = ibe->outbuf[1] | 0x04;
+        ibe->inbuf[2] = ibe->outbuf[2];
+        ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS;
+        iic->handle_rsp(ii, ibe->outbuf[0], ibe->inbuf + 1, 3);
+    }
+    return 0;
+}
+
+static const VMStateDescription vmstate_ipmi_bmc_extern = {
+    .name = TYPE_IPMI_BMC_EXTERN,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .post_load = ipmi_bmc_extern_post_migrate,
+    .fields      = (VMStateField[]) {
+        VMSTATE_BOOL(send_reset, IPMIBmcExtern),
+        VMSTATE_BOOL(waiting_rsp, IPMIBmcExtern),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static void ipmi_bmc_extern_init(Object *obj)
 {
     IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj);
 
     ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe);
+    vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe);
 }
 
 static Property ipmi_bmc_extern_properties[] = {
diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c
index af01e1a..0a59e53 100644
--- a/hw/ipmi/ipmi_bmc_sim.c
+++ b/hw/ipmi/ipmi_bmc_sim.c
@@ -1658,6 +1658,34 @@ static const uint8_t init_sdrs[] = {
     0xff, 0xff, 0x00, 0x00, 0x00
 };
 
+static const VMStateDescription vmstate_ipmi_sim = {
+    .name = TYPE_IPMI_BMC_SIMULATOR,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT8(bmc_global_enables, IPMIBmcSim),
+        VMSTATE_UINT8(msg_flags, IPMIBmcSim),
+        VMSTATE_BOOL(watchdog_initialized, IPMIBmcSim),
+        VMSTATE_UINT8(watchdog_use, IPMIBmcSim),
+        VMSTATE_UINT8(watchdog_action, IPMIBmcSim),
+        VMSTATE_UINT8(watchdog_pretimeout, IPMIBmcSim),
+        VMSTATE_BOOL(watchdog_expired, IPMIBmcSim),
+        VMSTATE_UINT16(watchdog_timeout, IPMIBmcSim),
+        VMSTATE_BOOL(watchdog_running, IPMIBmcSim),
+        VMSTATE_BOOL(watchdog_preaction_ran, IPMIBmcSim),
+        VMSTATE_INT64(watchdog_expiry, IPMIBmcSim),
+        VMSTATE_UINT8_ARRAY(evtbuf, IPMIBmcSim, 16),
+        VMSTATE_UINT8(sensors[IPMI_WATCHDOG_SENSOR].status, IPMIBmcSim),
+        VMSTATE_UINT8(sensors[IPMI_WATCHDOG_SENSOR].reading, IPMIBmcSim),
+        VMSTATE_UINT16(sensors[IPMI_WATCHDOG_SENSOR].states, IPMIBmcSim),
+        VMSTATE_UINT16(sensors[IPMI_WATCHDOG_SENSOR].assert_states, IPMIBmcSim),
+        VMSTATE_UINT16(sensors[IPMI_WATCHDOG_SENSOR].deassert_states,
+                       IPMIBmcSim),
+        VMSTATE_UINT16(sensors[IPMI_WATCHDOG_SENSOR].assert_enable, IPMIBmcSim),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static void ipmi_sim_init(Object *obj)
 {
     IPMIBmc *b = IPMI_BMC(obj);
@@ -1701,6 +1729,8 @@ static void ipmi_sim_init(Object *obj)
     register_cmds(ibs);
 
     ibs->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ipmi_timeout, ibs);
+
+    vmstate_register(NULL, 0, &vmstate_ipmi_sim, ibs);
 }
 
 static void ipmi_sim_class_init(ObjectClass *oc, void *data)
diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c
index 70e2c0d..03eb9cc 100644
--- a/hw/ipmi/isa_ipmi_bt.c
+++ b/hw/ipmi/isa_ipmi_bt.c
@@ -437,11 +437,35 @@ static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp)
     isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base);
 }
 
+static const VMStateDescription vmstate_ISAIPMIBTDevice = {
+    .name = TYPE_IPMI_INTERFACE,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_BOOL(bt.obf_irq_set, ISAIPMIBTDevice),
+        VMSTATE_BOOL(bt.atn_irq_set, ISAIPMIBTDevice),
+        VMSTATE_BOOL(bt.use_irq, ISAIPMIBTDevice),
+        VMSTATE_BOOL(bt.irqs_enabled, ISAIPMIBTDevice),
+        VMSTATE_UINT32(bt.outpos, ISAIPMIBTDevice),
+        VMSTATE_VBUFFER_UINT32(bt.outmsg, ISAIPMIBTDevice, 1, NULL, 0,
+                               bt.outlen),
+        VMSTATE_VBUFFER_UINT32(bt.inmsg, ISAIPMIBTDevice, 1, NULL, 0,
+                               bt.inlen),
+        VMSTATE_UINT8(bt.control_reg, ISAIPMIBTDevice),
+        VMSTATE_UINT8(bt.mask_reg, ISAIPMIBTDevice),
+        VMSTATE_UINT8(bt.waiting_rsp, ISAIPMIBTDevice),
+        VMSTATE_UINT8(bt.waiting_seq, ISAIPMIBTDevice),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static void isa_ipmi_bt_init(Object *obj)
 {
     ISAIPMIBTDevice *iib = ISA_IPMI_BT(obj);
 
     ipmi_bmc_find_and_link(obj, (Object **) &iib->bt.bmc);
+
+    vmstate_register(NULL, 0, &vmstate_ISAIPMIBTDevice, iib);
 }
 
 static void *isa_ipmi_bt_get_backend_data(IPMIInterface *ii)
diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c
index 750a725..b588711 100644
--- a/hw/ipmi/isa_ipmi_kcs.c
+++ b/hw/ipmi/isa_ipmi_kcs.c
@@ -400,11 +400,37 @@ static void ipmi_isa_realize(DeviceState *dev, Error **errp)
     isa_register_ioport(isadev, &iik->kcs.io, iik->kcs.io_base);
 }
 
+const VMStateDescription vmstate_ISAIPMIKCSDevice = {
+    .name = TYPE_IPMI_INTERFACE,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_BOOL(kcs.obf_irq_set, ISAIPMIKCSDevice),
+        VMSTATE_BOOL(kcs.atn_irq_set, ISAIPMIKCSDevice),
+        VMSTATE_BOOL(kcs.use_irq, ISAIPMIKCSDevice),
+        VMSTATE_BOOL(kcs.irqs_enabled, ISAIPMIKCSDevice),
+        VMSTATE_UINT32(kcs.outpos, ISAIPMIKCSDevice),
+        VMSTATE_VBUFFER_UINT32(kcs.outmsg, ISAIPMIKCSDevice, 1, NULL, 0,
+                               kcs.outlen),
+        VMSTATE_VBUFFER_UINT32(kcs.inmsg, ISAIPMIKCSDevice, 1, NULL, 0,
+                               kcs.inlen),
+        VMSTATE_BOOL(kcs.write_end, ISAIPMIKCSDevice),
+        VMSTATE_UINT8(kcs.status_reg, ISAIPMIKCSDevice),
+        VMSTATE_UINT8(kcs.data_out_reg, ISAIPMIKCSDevice),
+        VMSTATE_INT16(kcs.data_in_reg, ISAIPMIKCSDevice),
+        VMSTATE_INT16(kcs.cmd_reg, ISAIPMIKCSDevice),
+        VMSTATE_UINT8(kcs.waiting_rsp, ISAIPMIKCSDevice),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static void isa_ipmi_kcs_init(Object *obj)
 {
     ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(obj);
 
     ipmi_bmc_find_and_link(obj, (Object **) &iik->kcs.bmc);
+
+    vmstate_register(NULL, 0, &vmstate_ISAIPMIKCSDevice, iik);
 }
 
 static void *isa_ipmi_kcs_get_backend_data(IPMIInterface *ii)
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 09/16] ipmi: Add a firmware configuration repository
  2015-12-17 18:50 [Qemu-devel] [PATCH v5 0/16] Add an IPMI device to QEMU minyard
                   ` (7 preceding siblings ...)
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 08/16] ipmi: Add migration capability to the IPMI devices minyard
@ 2015-12-17 18:50 ` minyard
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 10/16] ipmi: Add firmware registration to the ISA interface minyard
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: minyard @ 2015-12-17 18:50 UTC (permalink / raw)
  To: qemu-devel, Paolo Bonzini, Michael S. Tsirkin, Igor Mammedov
  Cc: Corey Minyard

From: Corey Minyard <cminyard@mvista.com>

Add a way for IPMI devices to register their firmware information
with the IPMI subsystem so that various firmware entities can pull
that information later for adding to firmware tables.

Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
 hw/ipmi/ipmi.c         | 27 +++++++++++++++++++++++++++
 include/hw/ipmi/ipmi.h | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 62 insertions(+)

diff --git a/hw/ipmi/ipmi.c b/hw/ipmi/ipmi.c
index 7d17469..52aba1e 100644
--- a/hw/ipmi/ipmi.c
+++ b/hw/ipmi/ipmi.c
@@ -123,3 +123,30 @@ static void ipmi_register_types(void)
 }
 
 type_init(ipmi_register_types)
+
+static IPMIFwInfo *ipmi_fw_info;
+static unsigned int ipmi_fw_info_len;
+
+static uint32_t current_uuid = 1;
+
+void ipmi_add_fwinfo(IPMIFwInfo *info, Error **errp)
+{
+    info->uuid = current_uuid++;
+    ipmi_fw_info = g_realloc(ipmi_fw_info,
+                             sizeof(*ipmi_fw_info) * (ipmi_fw_info_len + 1));
+    ipmi_fw_info[ipmi_fw_info_len] = *info;
+}
+
+IPMIFwInfo *ipmi_first_fwinfo(void)
+{
+    return ipmi_fw_info;
+}
+
+IPMIFwInfo *ipmi_next_fwinfo(IPMIFwInfo *current)
+{
+    current++;
+    if (current >= &ipmi_fw_info[ipmi_fw_info_len]) {
+        return NULL;
+    }
+    return current;
+}
diff --git a/include/hw/ipmi/ipmi.h b/include/hw/ipmi/ipmi.h
index e4f7738..74e84a0 100644
--- a/include/hw/ipmi/ipmi.h
+++ b/include/hw/ipmi/ipmi.h
@@ -168,6 +168,41 @@ typedef struct IPMIBmcClass {
  */
 void ipmi_bmc_find_and_link(Object *obj, Object **bmc);
 
+/*
+ * Used for transferring information to interfaces that add 
+ * entries to firmware tables.
+ */
+typedef struct IPMIFwInfo {
+    const char *interface_name;
+    int interface_type;
+    uint8_t ipmi_spec_major_revision;
+    uint8_t ipmi_spec_minor_revision;
+    uint8_t i2c_slave_address;
+    uint32_t uuid;
+
+    uint64_t base_address;
+    uint64_t register_length;
+    uint8_t register_spacing;
+    enum {
+        IPMI_MEMSPACE_IO,
+        IPMI_MEMSPACE_MEM32,
+        IPMI_MEMSPACE_MEM64,
+        IPMI_MEMSPACE_SMBUS
+    } memspace;
+
+    int interrupt_number;
+    enum {
+        IPMI_LEVEL_IRQ,
+        IPMI_EDGE_IRQ
+    } irq_type;
+
+    const char *acpi_parent;
+} IPMIFwInfo;
+
+void ipmi_add_fwinfo(IPMIFwInfo *info, Error **errp);
+IPMIFwInfo *ipmi_first_fwinfo(void);
+IPMIFwInfo *ipmi_next_fwinfo(IPMIFwInfo *current);
+
 #ifdef IPMI_DEBUG
 #define ipmi_debug(fs, ...) \
     fprintf(stderr, "IPMI (%s): " fs, __func__, ##__VA_ARGS__)
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 10/16] ipmi: Add firmware registration to the ISA interface
  2015-12-17 18:50 [Qemu-devel] [PATCH v5 0/16] Add an IPMI device to QEMU minyard
                   ` (8 preceding siblings ...)
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 09/16] ipmi: Add a firmware configuration repository minyard
@ 2015-12-17 18:50 ` minyard
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 11/16] smbios: Move table build tools into an include file minyard
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: minyard @ 2015-12-17 18:50 UTC (permalink / raw)
  To: qemu-devel, Paolo Bonzini, Michael S. Tsirkin, Igor Mammedov
  Cc: Corey Minyard

From: Corey Minyard <cminyard@mvista.com>

Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
 hw/ipmi/isa_ipmi_bt.c  | 15 +++++++++++++++
 hw/ipmi/isa_ipmi_kcs.c | 15 +++++++++++++++
 2 files changed, 30 insertions(+)

diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c
index 03eb9cc..21fa4a7 100644
--- a/hw/ipmi/isa_ipmi_bt.c
+++ b/hw/ipmi/isa_ipmi_bt.c
@@ -407,6 +407,7 @@ typedef struct ISAIPMIBTDevice {
     ISADevice dev;
     int32 isairq;
     IPMIBT bt;
+    IPMIFwInfo fwinfo;
 } ISAIPMIBTDevice;
 
 static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp)
@@ -435,6 +436,20 @@ static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp)
     qdev_set_legacy_instance_id(dev, iib->bt.io_base, iib->bt.io_length);
 
     isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base);
+
+    iib->fwinfo.interface_name = "bt";
+    iib->fwinfo.interface_type = IPMI_SMBIOS_BT;
+    iib->fwinfo.ipmi_spec_major_revision = 2;
+    iib->fwinfo.ipmi_spec_minor_revision = 0;
+    iib->fwinfo.base_address = iib->bt.io_base;
+    iib->fwinfo.register_length = iib->bt.io_length;
+    iib->fwinfo.register_spacing = 1;
+    iib->fwinfo.memspace = IPMI_MEMSPACE_IO;
+    iib->fwinfo.irq_type = IPMI_LEVEL_IRQ;
+    iib->fwinfo.interrupt_number = iib->isairq;
+    iib->fwinfo.acpi_parent = "\\_SB.PCI0.ISA";
+    iib->fwinfo.i2c_slave_address = iib->bt.bmc->slave_addr;
+    ipmi_add_fwinfo(&iib->fwinfo, errp);
 }
 
 static const VMStateDescription vmstate_ISAIPMIBTDevice = {
diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c
index b588711..c662aee 100644
--- a/hw/ipmi/isa_ipmi_kcs.c
+++ b/hw/ipmi/isa_ipmi_kcs.c
@@ -370,6 +370,7 @@ typedef struct ISAIPMIKCSDevice {
     ISADevice dev;
     int32 isairq;
     IPMIKCS kcs;
+    IPMIFwInfo fwinfo;
 } ISAIPMIKCSDevice;
 
 static void ipmi_isa_realize(DeviceState *dev, Error **errp)
@@ -398,6 +399,20 @@ static void ipmi_isa_realize(DeviceState *dev, Error **errp)
     qdev_set_legacy_instance_id(dev, iik->kcs.io_base, iik->kcs.io_length);
 
     isa_register_ioport(isadev, &iik->kcs.io, iik->kcs.io_base);
+
+    iik->fwinfo.interface_name = "kcs";
+    iik->fwinfo.interface_type = IPMI_SMBIOS_KCS;
+    iik->fwinfo.ipmi_spec_major_revision = 2;
+    iik->fwinfo.ipmi_spec_minor_revision = 0;
+    iik->fwinfo.base_address = iik->kcs.io_base;
+    iik->fwinfo.i2c_slave_address = iik->kcs.bmc->slave_addr;
+    iik->fwinfo.register_length = iik->kcs.io_length;
+    iik->fwinfo.register_spacing = 1;
+    iik->fwinfo.memspace = IPMI_MEMSPACE_IO;
+    iik->fwinfo.irq_type = IPMI_LEVEL_IRQ;
+    iik->fwinfo.interrupt_number = iik->isairq;
+    iik->fwinfo.acpi_parent = "\\_SB.PCI0.ISA";
+    ipmi_add_fwinfo(&iik->fwinfo, errp);
 }
 
 const VMStateDescription vmstate_ISAIPMIKCSDevice = {
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 11/16] smbios: Move table build tools into an include file.
  2015-12-17 18:50 [Qemu-devel] [PATCH v5 0/16] Add an IPMI device to QEMU minyard
                   ` (9 preceding siblings ...)
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 10/16] ipmi: Add firmware registration to the ISA interface minyard
@ 2015-12-17 18:50 ` minyard
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 12/16] pc: Postpone SMBIOS table installation to post machine init minyard
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: minyard @ 2015-12-17 18:50 UTC (permalink / raw)
  To: qemu-devel, Paolo Bonzini, Michael S. Tsirkin, Igor Mammedov
  Cc: Corey Minyard

From: Corey Minyard <cminyard@mvista.com>

This will let things in other files (like IPMI) build SMBIOS tables.

Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
 hw/smbios/smbios.c       | 70 ++++---------------------------------------
 hw/smbios/smbios_build.h | 77 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 83 insertions(+), 64 deletions(-)
 create mode 100644 hw/smbios/smbios_build.h

diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index b81a1d3..fe553f9 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -22,6 +22,7 @@
 #include "hw/smbios/smbios.h"
 #include "hw/loader.h"
 #include "exec/cpu-common.h"
+#include "smbios_build.h"
 
 /* legacy structures and constants for <= 2.0 machines */
 struct smbios_header {
@@ -51,10 +52,10 @@ static bool smbios_uuid_encoded = true;
 /* end: legacy structures & constants for <= 2.0 machines */
 
 
-static uint8_t *smbios_tables;
-static size_t smbios_tables_len;
-static unsigned smbios_table_max;
-static unsigned smbios_table_cnt;
+uint8_t *smbios_tables;
+size_t smbios_tables_len;
+unsigned smbios_table_max;
+unsigned smbios_table_cnt;
 static SmbiosEntryPointType smbios_ep_type = SMBIOS_ENTRY_POINT_21;
 
 static SmbiosEntryPoint ep;
@@ -427,7 +428,7 @@ uint8_t *smbios_get_table_legacy(size_t *length)
 /* end: legacy setup functions for <= 2.0 machines */
 
 
-static bool smbios_skip_table(uint8_t type, bool required_table)
+bool smbios_skip_table(uint8_t type, bool required_table)
 {
     if (test_bit(type, have_binfile_bitmap)) {
         return true; /* user provided their own binary blob(s) */
@@ -441,65 +442,6 @@ static bool smbios_skip_table(uint8_t type, bool required_table)
     return true;
 }
 
-#define SMBIOS_BUILD_TABLE_PRE(tbl_type, tbl_handle, tbl_required)        \
-    struct smbios_type_##tbl_type *t;                                     \
-    size_t t_off; /* table offset into smbios_tables */                   \
-    int str_index = 0;                                                    \
-    do {                                                                  \
-        /* should we skip building this table ? */                        \
-        if (smbios_skip_table(tbl_type, tbl_required)) {                  \
-            return;                                                       \
-        }                                                                 \
-                                                                          \
-        /* use offset of table t within smbios_tables */                  \
-        /* (pointer must be updated after each realloc) */                \
-        t_off = smbios_tables_len;                                        \
-        smbios_tables_len += sizeof(*t);                                  \
-        smbios_tables = g_realloc(smbios_tables, smbios_tables_len);      \
-        t = (struct smbios_type_##tbl_type *)(smbios_tables + t_off);     \
-                                                                          \
-        t->header.type = tbl_type;                                        \
-        t->header.length = sizeof(*t);                                    \
-        t->header.handle = cpu_to_le16(tbl_handle);                       \
-    } while (0)
-
-#define SMBIOS_TABLE_SET_STR(tbl_type, field, value)                      \
-    do {                                                                  \
-        int len = (value != NULL) ? strlen(value) + 1 : 0;                \
-        if (len > 1) {                                                    \
-            smbios_tables = g_realloc(smbios_tables,                      \
-                                      smbios_tables_len + len);           \
-            memcpy(smbios_tables + smbios_tables_len, value, len);        \
-            smbios_tables_len += len;                                     \
-            /* update pointer post-realloc */                             \
-            t = (struct smbios_type_##tbl_type *)(smbios_tables + t_off); \
-            t->field = ++str_index;                                       \
-        } else {                                                          \
-            t->field = 0;                                                 \
-        }                                                                 \
-    } while (0)
-
-#define SMBIOS_BUILD_TABLE_POST                                           \
-    do {                                                                  \
-        size_t term_cnt, t_size;                                          \
-                                                                          \
-        /* add '\0' terminator (add two if no strings defined) */         \
-        term_cnt = (str_index == 0) ? 2 : 1;                              \
-        smbios_tables = g_realloc(smbios_tables,                          \
-                                  smbios_tables_len + term_cnt);          \
-        memset(smbios_tables + smbios_tables_len, 0, term_cnt);           \
-        smbios_tables_len += term_cnt;                                    \
-                                                                          \
-        /* update smbios max. element size */                             \
-        t_size = smbios_tables_len - t_off;                               \
-        if (t_size > smbios_table_max) {                                  \
-            smbios_table_max = t_size;                                    \
-        }                                                                 \
-                                                                          \
-        /* update smbios element count */                                 \
-        smbios_table_cnt++;                                               \
-    } while (0)
-
 static void smbios_build_type_0_table(void)
 {
     SMBIOS_BUILD_TABLE_PRE(0, 0x000, false); /* optional, leave up to BIOS */
diff --git a/hw/smbios/smbios_build.h b/hw/smbios/smbios_build.h
new file mode 100644
index 0000000..2257721
--- /dev/null
+++ b/hw/smbios/smbios_build.h
@@ -0,0 +1,77 @@
+/*
+ * SMBIOS Support
+ *
+ * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
+ * Copyright (C) 2013 Red Hat, Inc.
+ */
+
+#ifndef QEMU_SMBIOS_BUILD_H
+#define QEMU_SMBIOS_BUILD_H
+
+bool smbios_skip_table(uint8_t type, bool required_table);
+
+extern uint8_t *smbios_tables;
+extern size_t smbios_tables_len;
+extern unsigned smbios_table_max;
+extern unsigned smbios_table_cnt;
+
+#define SMBIOS_BUILD_TABLE_PRE(tbl_type, tbl_handle, tbl_required)        \
+    struct smbios_type_##tbl_type *t;                                     \
+    size_t t_off; /* table offset into smbios_tables */                   \
+    int str_index = 0;                                                    \
+    do {                                                                  \
+        /* should we skip building this table ? */                        \
+        if (smbios_skip_table(tbl_type, tbl_required)) {                  \
+            return;                                                       \
+        }                                                                 \
+                                                                          \
+        /* use offset of table t within smbios_tables */                  \
+        /* (pointer must be updated after each realloc) */                \
+        t_off = smbios_tables_len;                                        \
+        smbios_tables_len += sizeof(*t);                                  \
+        smbios_tables = g_realloc(smbios_tables, smbios_tables_len);      \
+        t = (struct smbios_type_##tbl_type *)(smbios_tables + t_off);     \
+                                                                          \
+        t->header.type = tbl_type;                                        \
+        t->header.length = sizeof(*t);                                    \
+        t->header.handle = cpu_to_le16(tbl_handle);                       \
+    } while (0)
+
+#define SMBIOS_TABLE_SET_STR(tbl_type, field, value)                      \
+    do {                                                                  \
+        int len = (value != NULL) ? strlen(value) + 1 : 0;                \
+        if (len > 1) {                                                    \
+            smbios_tables = g_realloc(smbios_tables,                      \
+                                      smbios_tables_len + len);           \
+            memcpy(smbios_tables + smbios_tables_len, value, len);        \
+            smbios_tables_len += len;                                     \
+            /* update pointer post-realloc */                             \
+            t = (struct smbios_type_##tbl_type *)(smbios_tables + t_off); \
+            t->field = ++str_index;                                       \
+        } else {                                                          \
+            t->field = 0;                                                 \
+        }                                                                 \
+    } while (0)
+
+#define SMBIOS_BUILD_TABLE_POST                                           \
+    do {                                                                  \
+        size_t term_cnt, t_size;                                          \
+                                                                          \
+        /* add '\0' terminator (add two if no strings defined) */         \
+        term_cnt = (str_index == 0) ? 2 : 1;                              \
+        smbios_tables = g_realloc(smbios_tables,                          \
+                                  smbios_tables_len + term_cnt);          \
+        memset(smbios_tables + smbios_tables_len, 0, term_cnt);           \
+        smbios_tables_len += term_cnt;                                    \
+                                                                          \
+        /* update smbios max. element size */                             \
+        t_size = smbios_tables_len - t_off;                               \
+        if (t_size > smbios_table_max) {                                  \
+            smbios_table_max = t_size;                                    \
+        }                                                                 \
+                                                                          \
+        /* update smbios element count */                                 \
+        smbios_table_cnt++;                                               \
+    } while (0)
+
+#endif /* QEMU_SMBIOS_BUILD_H */
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 12/16] pc: Postpone SMBIOS table installation to post machine init
  2015-12-17 18:50 [Qemu-devel] [PATCH v5 0/16] Add an IPMI device to QEMU minyard
                   ` (10 preceding siblings ...)
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 11/16] smbios: Move table build tools into an include file minyard
@ 2015-12-17 18:50 ` minyard
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 13/16] ipmi: Add SMBIOS table entry minyard
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: minyard @ 2015-12-17 18:50 UTC (permalink / raw)
  To: qemu-devel, Paolo Bonzini, Michael S. Tsirkin, Igor Mammedov
  Cc: Corey Minyard

From: Corey Minyard <cminyard@mvista.com>

This is the same place that the ACPI SSDT table gets added, so that
devices can add themselves to the SMBIOS table.

Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
 hw/i386/pc.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 5e20e07..861f887 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -781,8 +781,6 @@ static FWCfgState *bochs_bios_init(AddressSpace *as)
                      acpi_tables, acpi_tables_len);
     fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, kvm_allows_irq0_override());
 
-    pc_build_smbios(fw_cfg);
-
     fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE,
                      &e820_reserve, sizeof(e820_reserve));
     fw_cfg_add_file(fw_cfg, "etc/e820", e820_table,
@@ -1174,6 +1172,7 @@ void pc_guest_info_machine_done(Notifier *notifier, void *data)
     PcGuestInfoState *guest_info_state = container_of(notifier,
                                                       PcGuestInfoState,
                                                       machine_done);
+    FWCfgState *fw_cfg = guest_info_state->info.fw_cfg;
     PCIBus *bus = find_i440fx();
 
     if (bus) {
@@ -1185,15 +1184,17 @@ void pc_guest_info_machine_done(Notifier *notifier, void *data)
                 extra_hosts++;
             }
         }
-        if (extra_hosts && guest_info_state->info.fw_cfg) {
+        if (extra_hosts && fw_cfg) {
             uint64_t *val = g_malloc(sizeof(*val));
             *val = cpu_to_le64(extra_hosts);
-            fw_cfg_add_file(guest_info_state->info.fw_cfg,
-                    "etc/extra-pci-roots", val, sizeof(*val));
+            fw_cfg_add_file(fw_cfg, "etc/extra-pci-roots", val, sizeof(*val));
         }
     }
 
     acpi_setup(&guest_info_state->info);
+    if (fw_cfg) {
+        pc_build_smbios(fw_cfg);
+    }
 }
 
 PcGuestInfo *pc_guest_info_init(PCMachineState *pcms)
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 13/16] ipmi: Add SMBIOS table entry
  2015-12-17 18:50 [Qemu-devel] [PATCH v5 0/16] Add an IPMI device to QEMU minyard
                   ` (11 preceding siblings ...)
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 12/16] pc: Postpone SMBIOS table installation to post machine init minyard
@ 2015-12-17 18:50 ` minyard
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 14/16] acpi: Add IPMI table entries minyard
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: minyard @ 2015-12-17 18:50 UTC (permalink / raw)
  To: qemu-devel, Paolo Bonzini, Michael S. Tsirkin, Igor Mammedov
  Cc: Corey Minyard

From: Corey Minyard <cminyard@mvista.com>

Add an IPMI table entry to the SMBIOS.

Signed-off-by: Corey Minyard <cminyard@mvista.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
---
 default-configs/i386-softmmu.mak   |  1 +
 default-configs/x86_64-softmmu.mak |  1 +
 hw/smbios/Makefile.objs            |  2 +
 hw/smbios/ipmi.c                   | 75 ++++++++++++++++++++++++++++++++++++++
 hw/smbios/noipmi.c                 | 14 +++++++
 hw/smbios/smbios.c                 |  2 +
 include/hw/smbios/ipmi.h           | 15 ++++++++
 7 files changed, 110 insertions(+)
 create mode 100644 hw/smbios/ipmi.c
 create mode 100644 hw/smbios/noipmi.c
 create mode 100644 include/hw/smbios/ipmi.h

diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index 1730fbb..b73bf3a 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -20,6 +20,7 @@ CONFIG_I8254=y
 CONFIG_PCSPK=y
 CONFIG_PCKBD=y
 CONFIG_FDC=y
+CONFIG_SMBIOS=y
 CONFIG_ACPI=y
 CONFIG_ACPI_X86=y
 CONFIG_ACPI_X86_ICH=y
diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index 22a99a2..baf07cc 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -20,6 +20,7 @@ CONFIG_I8254=y
 CONFIG_PCSPK=y
 CONFIG_PCKBD=y
 CONFIG_FDC=y
+CONFIG_SMBIOS=y
 CONFIG_ACPI=y
 CONFIG_ACPI_X86=y
 CONFIG_ACPI_X86_ICH=y
diff --git a/hw/smbios/Makefile.objs b/hw/smbios/Makefile.objs
index f69a92f..5578f51 100644
--- a/hw/smbios/Makefile.objs
+++ b/hw/smbios/Makefile.objs
@@ -1 +1,3 @@
 common-obj-$(CONFIG_SMBIOS) += smbios.o
+common-obj-$(call land,$(CONFIG_SMBIOS),$(CONFIG_IPMI)) += ipmi.o
+common-obj-$(call land,$(CONFIG_SMBIOS),$(call lnot,$(CONFIG_IPMI))) += noipmi.o
diff --git a/hw/smbios/ipmi.c b/hw/smbios/ipmi.c
new file mode 100644
index 0000000..9c0ce3c
--- /dev/null
+++ b/hw/smbios/ipmi.c
@@ -0,0 +1,75 @@
+/*
+ * IPMI SMBIOS firmware handling
+ *
+ * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "hw/ipmi/ipmi.h"
+#include "hw/smbios/ipmi.h"
+#include "hw/smbios/smbios.h"
+#include "qemu/error-report.h"
+#include "smbios_build.h"
+
+/* SMBIOS type 38 - IPMI */
+struct smbios_type_38 {
+    struct smbios_structure_header header;
+    uint8_t interface_type;
+    uint8_t ipmi_spec_revision;
+    uint8_t i2c_slave_address;
+    uint8_t nv_storage_device_address;
+    uint64_t base_address;
+    uint8_t base_address_modifier;
+    uint8_t interrupt_number;
+} QEMU_PACKED;
+
+static void ipmi_encode_one_smbios(IPMIFwInfo *info)
+{
+    uint64_t baseaddr = info->base_address;
+    SMBIOS_BUILD_TABLE_PRE(38, 0x3000, true);
+
+    t->interface_type = info->interface_type;
+    t->ipmi_spec_revision = ((info->ipmi_spec_major_revision << 4)
+                             | info->ipmi_spec_minor_revision);
+    t->i2c_slave_address = info->i2c_slave_address;
+    t->nv_storage_device_address = 0;
+
+    /* or 1 to set it to I/O space */
+    switch (info->memspace) {
+    case IPMI_MEMSPACE_IO: baseaddr |= 1; break;
+    case IPMI_MEMSPACE_MEM32: break;
+    case IPMI_MEMSPACE_MEM64: break;
+    case IPMI_MEMSPACE_SMBUS: baseaddr <<= 1; break;
+    }
+
+    t->base_address = cpu_to_le64(baseaddr);
+    
+    t->base_address_modifier = 0;
+    if (info->irq_type == IPMI_LEVEL_IRQ) {
+        t->base_address_modifier |= 1;
+    }
+    switch (info->register_spacing) {
+    case 1: break;
+    case 4: t->base_address_modifier |= 1 << 6; break;
+    case 16: t->base_address_modifier |= 2 << 6; break;
+    default:
+        error_report("IPMI register spacing %d is not compatible with"
+                     " SMBIOS, ignoring this entry.", info->register_spacing);
+        return;
+    }
+    t->interrupt_number = info->interrupt_number;
+
+    SMBIOS_BUILD_TABLE_POST;
+}
+
+void smbios_build_type_38_table(void)
+{
+    IPMIFwInfo *info = ipmi_first_fwinfo();
+
+    while (info) {
+        ipmi_encode_one_smbios(info);
+        info = ipmi_next_fwinfo(info);
+    }
+}
diff --git a/hw/smbios/noipmi.c b/hw/smbios/noipmi.c
new file mode 100644
index 0000000..ad669a4
--- /dev/null
+++ b/hw/smbios/noipmi.c
@@ -0,0 +1,14 @@
+/*
+ * IPMI SMBIOS firmware handling
+ *
+ * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "hw/smbios/ipmi.h"
+
+void smbios_build_type_38_table(void)
+{
+}
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index fe553f9..ebfb077 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -23,6 +23,7 @@
 #include "hw/loader.h"
 #include "exec/cpu-common.h"
 #include "smbios_build.h"
+#include "hw/smbios/ipmi.h"
 
 /* legacy structures and constants for <= 2.0 machines */
 struct smbios_header {
@@ -846,6 +847,7 @@ void smbios_get_tables(const struct smbios_phys_mem_area *mem_array,
         }
 
         smbios_build_type_32_table();
+        smbios_build_type_38_table();
         smbios_build_type_127_table();
 
         smbios_validate_table();
diff --git a/include/hw/smbios/ipmi.h b/include/hw/smbios/ipmi.h
new file mode 100644
index 0000000..fd53c96
--- /dev/null
+++ b/include/hw/smbios/ipmi.h
@@ -0,0 +1,15 @@
+/*
+ * IPMI SMBIOS firmware handling
+ *
+ * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef QEMU_SMBIOS_IPMI_H
+#define QEMU_SMBIOS_IPMI_H
+
+void smbios_build_type_38_table(void);
+
+#endif /* QEMU_SMBIOS_IPMI_H */
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 14/16] acpi: Add IPMI table entries
  2015-12-17 18:50 [Qemu-devel] [PATCH v5 0/16] Add an IPMI device to QEMU minyard
                   ` (12 preceding siblings ...)
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 13/16] ipmi: Add SMBIOS table entry minyard
@ 2015-12-17 18:50 ` minyard
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 15/16] bios: Add tests for the IPMI ACPI and SMBIOS entries minyard
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 16/16] ipmi: Add a force off function minyard
  15 siblings, 0 replies; 17+ messages in thread
From: minyard @ 2015-12-17 18:50 UTC (permalink / raw)
  To: qemu-devel, Paolo Bonzini, Michael S. Tsirkin, Igor Mammedov
  Cc: Corey Minyard

From: Corey Minyard <cminyard@mvista.com>

Use the new ACPI table construction tools to create an ACPI
entry for IPMI.  This adds a function called from build_ssdt
to add an SSDT entry for IPMI if IPMI is compiled in and has
registered firmware.  It also adds a dummy function if IPMI
is not compiled in.

This conforms to section "C3-2 Locating IPMI System Interfaces in
ACPI Name Space" in the IPMI 2.0 specification.

Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
 hw/acpi/Makefile.objs  |   2 +
 hw/acpi/ipmi.c         | 116 +++++++++++++++++++++++++++++++++++++++++++++++++
 hw/acpi/noipmi.c       |  14 ++++++
 hw/i386/acpi-build.c   |   4 ++
 include/hw/acpi/ipmi.h |  16 +++++++
 5 files changed, 152 insertions(+)
 create mode 100644 hw/acpi/ipmi.c
 create mode 100644 hw/acpi/noipmi.c
 create mode 100644 include/hw/acpi/ipmi.h

diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
index 7d3230c..f35ecfc 100644
--- a/hw/acpi/Makefile.objs
+++ b/hw/acpi/Makefile.objs
@@ -5,3 +5,5 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
 common-obj-$(CONFIG_ACPI) += acpi_interface.o
 common-obj-$(CONFIG_ACPI) += bios-linker-loader.o
 common-obj-$(CONFIG_ACPI) += aml-build.o
+common-obj-$(call land,$(CONFIG_ACPI),$(CONFIG_IPMI)) += ipmi.o
+common-obj-$(call land,$(CONFIG_ACPI),$(call lnot,$(CONFIG_IPMI))) += noipmi.o
diff --git a/hw/acpi/ipmi.c b/hw/acpi/ipmi.c
new file mode 100644
index 0000000..eacaa93
--- /dev/null
+++ b/hw/acpi/ipmi.c
@@ -0,0 +1,116 @@
+/*
+ * IPMI ACPI firmware handling
+ *
+ * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "hw/ipmi/ipmi.h"
+#include "hw/acpi/aml-build.h"
+#include "hw/acpi/acpi.h"
+#include "hw/acpi/ipmi.h"
+
+static Aml *aml_ipmi_crs(IPMIFwInfo *info)
+{
+    Aml *crs = aml_resource_template();
+    uint8_t regspacing = info->register_spacing;
+
+    /*
+     * The base address is fixed and cannot change.  That may be different
+     * if someone does PCI, but we aren't there yet.
+     */
+    switch (info->memspace) {
+    case IPMI_MEMSPACE_IO:
+        aml_append(crs, aml_io(AML_DECODE16, info->base_address,
+                               info->base_address + info->register_length - 1,
+                               regspacing, info->register_length));
+        break;
+    case IPMI_MEMSPACE_MEM32:
+        aml_append(crs,
+                   aml_dword_memory(AML_POS_DECODE,
+                            AML_MIN_FIXED, AML_MAX_FIXED,
+                            AML_NON_CACHEABLE, AML_READ_WRITE,
+                            0xffffffff,
+                            info->base_address,
+                            info->base_address + info->register_length - 1,
+                            regspacing, info->register_length));
+        break;
+    case IPMI_MEMSPACE_MEM64:
+        aml_append(crs,
+                   aml_qword_memory(AML_POS_DECODE,
+                            AML_MIN_FIXED, AML_MAX_FIXED,
+                            AML_NON_CACHEABLE, AML_READ_WRITE,
+                            0xffffffffffffffffULL,
+                            info->base_address,
+                            info->base_address + info->register_length - 1,
+                            regspacing, info->register_length));
+        break;
+    case IPMI_MEMSPACE_SMBUS:
+        aml_append(crs, aml_return(aml_int(info->base_address)));
+        break;
+    default:
+        abort();
+    }
+
+    if (info->interrupt_number) {
+        aml_append(crs, aml_irq_no_flags(info->interrupt_number));
+    }
+
+    return crs;
+}
+
+static void
+ipmi_encode_one_acpi(Aml *ssdt, IPMIFwInfo *info)
+{
+    Aml *scope, *dev, *method;
+    uint16_t version = ((info->ipmi_spec_major_revision << 8)
+                        | (info->ipmi_spec_minor_revision << 4));
+
+    /*
+     * The ACPI parent is a little bit of a pain.  It could be in
+     * different places depending on the device.  It could be an SMBus,
+     * it could be ISA, it could be PCI, etc.  Only the device really
+     * knows, so it has to pass it in.
+     */
+    if (!info->acpi_parent) {
+        ipmi_debug("device %s not compatible with ACPI, no parent given.",
+                   info->interface_name);
+        return;
+    }
+
+    scope = aml_scope("%s", info->acpi_parent);
+
+    dev = aml_device("MI0");
+    aml_append(dev, aml_name_decl("_HID", aml_eisaid("IPI0001")));
+    aml_append(dev, aml_name_decl("_STR", aml_string("ipmi_%s",
+                                                     info->interface_name)));
+    aml_append(dev, aml_name_decl("_UID", aml_int(info->uuid)));
+    aml_append(dev, aml_name_decl("_CRS", aml_ipmi_crs(info)));
+
+    /*
+     * The spec seems to require these to be methods.  All the examples
+     * show them this way and it doesn't seem to work if they are not.
+     */
+    method = aml_method("_IFT", 0, AML_NOTSERIALIZED);
+    aml_append(method, aml_return(aml_int(info->interface_type)));
+    aml_append(dev, method);
+    method = aml_method("_SRV", 0, AML_NOTSERIALIZED);
+    aml_append(method, aml_return(aml_int(version)));
+    aml_append(dev, method);
+
+    aml_append(scope, dev);
+
+    aml_append(ssdt, scope);
+}
+
+void acpi_add_ipmi(Aml *ssdt)
+{
+    IPMIFwInfo *info = ipmi_first_fwinfo();
+
+    while (info) {
+        ipmi_encode_one_acpi(ssdt, info);
+        info = ipmi_next_fwinfo(info);
+    }
+}
diff --git a/hw/acpi/noipmi.c b/hw/acpi/noipmi.c
new file mode 100644
index 0000000..4aae336
--- /dev/null
+++ b/hw/acpi/noipmi.c
@@ -0,0 +1,14 @@
+/*
+ * IPMI ACPI firmware handling
+ *
+ * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "hw/acpi/ipmi.h"
+
+void acpi_add_ipmi(Aml *ssdt)
+{
+}
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index fa866f6..81081a4 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -59,6 +59,8 @@
 #include "qapi/qmp/qint.h"
 #include "qom/qom-qobject.h"
 
+#include "hw/acpi/ipmi.h"
+
 /* These are used to size the ACPI tables for -M pc-i440fx-1.7 and
  * -M pc-i440fx-2.0.  Even if the actual amount of AML generated grows
  * a little bit, there should be plenty of free space since the DSDT
@@ -1346,6 +1348,8 @@ build_ssdt(GArray *table_data, GArray *linker,
         aml_append(ssdt, sb_scope);
     }
 
+    acpi_add_ipmi(ssdt);
+
     /* copy AML table into ACPI tables blob and patch header there */
     g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len);
     build_header(linker, table_data,
diff --git a/include/hw/acpi/ipmi.h b/include/hw/acpi/ipmi.h
new file mode 100644
index 0000000..313ce37
--- /dev/null
+++ b/include/hw/acpi/ipmi.h
@@ -0,0 +1,16 @@
+/*
+ * QEMU IPMI ACPI handling
+ *
+ * Copyright (c) 2015 Corey Minyard <cminyard@mvista.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#ifndef HW_ACPI_IPMI_H
+#define HW_ACPI_IPMI_H
+
+#include "hw/acpi/aml-build.h"
+
+void acpi_add_ipmi(Aml *ssdt);
+
+#endif /* HW_ACPI_IPMI_H */
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 15/16] bios: Add tests for the IPMI ACPI and SMBIOS entries
  2015-12-17 18:50 [Qemu-devel] [PATCH v5 0/16] Add an IPMI device to QEMU minyard
                   ` (13 preceding siblings ...)
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 14/16] acpi: Add IPMI table entries minyard
@ 2015-12-17 18:50 ` minyard
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 16/16] ipmi: Add a force off function minyard
  15 siblings, 0 replies; 17+ messages in thread
From: minyard @ 2015-12-17 18:50 UTC (permalink / raw)
  To: qemu-devel, Paolo Bonzini, Michael S. Tsirkin, Igor Mammedov
  Cc: Corey Minyard

From: Corey Minyard <cminyard@mvista.com>

Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
 tests/acpi-test-data/pc/SSDT.ipmikcs | Bin 0 -> 2582 bytes
 tests/acpi-test-data/q35/SSDT.ipmibt | Bin 0 -> 790 bytes
 tests/bios-tables-test.c             |  58 ++++++++++++++++++++++++++++++++---
 3 files changed, 54 insertions(+), 4 deletions(-)
 create mode 100644 tests/acpi-test-data/pc/SSDT.ipmikcs
 create mode 100644 tests/acpi-test-data/q35/SSDT.ipmibt

diff --git a/tests/acpi-test-data/pc/SSDT.ipmikcs b/tests/acpi-test-data/pc/SSDT.ipmikcs
new file mode 100644
index 0000000000000000000000000000000000000000..f1107c37c8fa3523546b1759e488a624ebbd896d
GIT binary patch
literal 2582
zcma);%Wm676o$`Gv`vqwm{RCV;yC82K(a{WICj#VD2leEm@>hUj1)4!MG#a*FE)~9
zVR&P^sM~#CAZWdg9-)1NK0@B1K<ic2b4VT8i%d~1sQLMS{AY%9qf(9Tf&>sR>bpTx
zdG(iZpzxjm04meJ6PEWMrK+CBL91$|L88j_8Tsu`B`|EX0Y-EY+v3W;vW12Bg<YUq
zf1`b6PjqQS;GaJ(0^~k`gwd!-DE<t{9IBCi)@bzGKY8)@zmB1Q9+=y4i1jyHjZ)l+
ztA2G8th9%U?t5=uzdd~S=GE)BcvE$KS<G8nl5$&o9DW=MlS0~N+>Db?g<$8r=HYio
zU%&hEKeONid=Tz2I1l!zruDQkZ3=8OmY=2-%an#RGPOYtb-?j|*r(UmKYw99qIVc4
zp~{+I`+vykybnR#`+wCBVJJN8%4wXat|-D#di^sy6L?8$PXGJ$Ag)&PSLfPk<agmR
zh$A0R;F}?#y5N@5uw4}>!Zr-ta;LlXh4EhgJb(*&{J;w0-A<(-gcDi&j|)8hQ8R8>
zQKOS!hiN8c?-$2W!V)#A8Ak~=O3J1F*(!-bl+<@TyfpE$L!})o#V=EA^$Gz#)MON@
zR79<g5>XQEi70HRqJK7@Kd(Z3YB$`&iwZn<I--U;(P1_l>*(UJ7FD&HTB+YiP&`y?
zV}bL}02fDQtpS%=;RzSksn9=vpw^z_gR0k&h3t8JqyTOudpB_qwy6TL34KDbgqn<+
zT9i|#7@g9g>zs-+0Xu*VnAW14I>YFU4t>h07NeFH<<wb5XLaZXr%Fbp7Ufi2h<Ue)
zI&_m$ZANV^%BdwrOFHx!r`}@pmKNpIIY#Gn=yOhW7<IHLr<NHl>(I|RwZdpci*o91
zMsMp-pHp!O<U`<UQBIv_bY6$nIdy^21ue>{i;OPn(125y7+un$oO*}RJ36$%sdpK@
zt3^3=nbBn(8glAAM(=4+PF-PiMTc&2>U~D<Yf(;JWpwpZsM}m_>~*@vd;4S{Ex4e)
z?jREm3f#?d;gMjch5~n_+&LvdPU&P&1c3^-m9a8NO*|77ZVqFQ>e`riR2eSuu}8IS
zPCTj(7wXuf`ks+DcQU|z5Kq+$g$s@~o~x*@eLP(+6fPb1txRA04ryEj?0cEM_G_eZ
zI<tS8>1#jhF-}bO@i4hi!402!jZ<vmQ@<NGeCjz)mWl6?zTwwU<FHTsQ_|?Wt8vij
z!V%Ovy@-Yo&$^x%7aFqQ>`vV{l3U8hZ;Kr>YS9eB&m;S@SrB+qb(7M;(KiR_SFh-!
i2)~r@=H$%&#s1Y6#xb3Wtu6dQ5UxyBvImz%F#ZLb|5+aZ

literal 0
HcmV?d00001

diff --git a/tests/acpi-test-data/q35/SSDT.ipmibt b/tests/acpi-test-data/q35/SSDT.ipmibt
new file mode 100644
index 0000000000000000000000000000000000000000..5c2078ce396decb418b4f1416fd541a7c9aaf902
GIT binary patch
literal 790
zcmaJ<y^hmB5S~pU<J+tyUu57Y5Ecaz6o(;zsL(l%lQ?9PmDd*?a-@|&CoU3jr~4_=
zkstv*4**5p10I1g4}f?93Q`ef91c<8V*GtGyF2sEjMmNUQW=1KYz(4~_8WUD(&%FV
zK$!k+B1YRrH;jssz&25;HyaoDtM?9+1cm`tWLTRm#ZoXE{Y>DK06#Ij6o0w1qJux)
zT?Sak0XVEyRXU1a35p11r0>gW_4(Hy_-F$8EUL6sjQKlB(@}jDguw%_%`P(X`^Wny
zhiAvn_D|5MvyfNUl{%canQ!?Pb0^^598t7xYF33|BE<Jh&93kgFBR-QMJTD;y)q)n
z`Rs9Jn{=x*)<td+e&yCWZ=`np&f)3Ni?iqdO}`Ke!F<&4TBSN;)rH`GJ{1K)V|Lv9
zO+8XUu$JoyBf|{lk&+=GK{Z5l2A*T$ZorVlT{!SI`&sKr=@b8O0B`B~*pAeoUn?`_
zN_%v>d_FVTQB7Mm`zZ>h=@mV}-gDTZqB=^ZtO}fO<<o0K$}(;Ad^8<2JwiQ1YR_QR
zSLr|qJ6y)vFq!l<lc^js8FvlKrx(}vb&R(L@eqxUe9w|psN1SgC-YfV2Z6Co*i3P(
zw{-AuO>kQncsP?gO_&$p?m4Izz*12^1Gi!Qqb<=P4rx%42|bCsA#Q8{qZLS<@zKk%
pd3uVfaZAug+FkCn**EwOtbE;v33wYOX<@T@+2cm1Lzq{g^c#SctpNZ4

literal 0
HcmV?d00001

diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c
index 6d37332..4b4c4e0 100644
--- a/tests/bios-tables-test.c
+++ b/tests/bios-tables-test.c
@@ -51,6 +51,8 @@ typedef struct {
     GArray *tables;
     uint32_t smbios_ep_addr;
     struct smbios_21_entry_point smbios_ep_table;
+    uint8_t *required_struct_types;
+    int required_struct_types_len;
 } test_data;
 
 #define LOW(x) ((x) & 0xff)
@@ -372,7 +374,7 @@ static void test_acpi_tables(test_data *data)
     for (i = 0; i < tables_nr; i++) {
         AcpiSdtTable ssdt_table;
 
-        memset(&ssdt_table, 0 , sizeof(ssdt_table));
+        memset(&ssdt_table, 0, sizeof(ssdt_table));
         uint32_t addr = data->rsdt_tables_addr[i + 1]; /* fadt is first */
         test_dst_table(&ssdt_table, addr);
         g_array_append_val(data->tables, ssdt_table);
@@ -675,7 +677,6 @@ static void test_smbios_structs(test_data *data)
     uint32_t addr = ep_table->structure_table_address;
     int i, len, max_len = 0;
     uint8_t type, prv, crt;
-    uint8_t required_struct_types[] = {0, 1, 3, 4, 16, 17, 19, 32, 127};
 
     /* walk the smbios tables */
     for (i = 0; i < ep_table->number_of_structures; i++) {
@@ -715,8 +716,8 @@ static void test_smbios_structs(test_data *data)
     g_assert_cmpuint(ep_table->max_structure_size, ==, max_len);
 
     /* required struct types must all be present */
-    for (i = 0; i < ARRAY_SIZE(required_struct_types); i++) {
-        g_assert(test_bit(required_struct_types[i], struct_bitmap));
+    for (i = 0; i < data->required_struct_types_len; i++) {
+        g_assert(test_bit(data->required_struct_types[i], struct_bitmap));
     }
 }
 
@@ -777,6 +778,9 @@ static void test_acpi_one(const char *params, test_data *data)
     g_free(args);
 }
 
+static uint8_t base_required_struct_types[] =
+    {0, 1, 3, 4, 16, 17, 19, 32, 127};
+
 static void test_acpi_piix4_tcg(void)
 {
     test_data data;
@@ -786,6 +790,8 @@ static void test_acpi_piix4_tcg(void)
      */
     memset(&data, 0, sizeof(data));
     data.machine = MACHINE_PC;
+    data.required_struct_types = base_required_struct_types;
+    data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types);
     test_acpi_one("-machine accel=tcg", &data);
     free_test_data(&data);
 }
@@ -797,6 +803,8 @@ static void test_acpi_piix4_tcg_bridge(void)
     memset(&data, 0, sizeof(data));
     data.machine = MACHINE_PC;
     data.variant = ".bridge";
+    data.required_struct_types = base_required_struct_types;
+    data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types);
     test_acpi_one("-machine accel=tcg -device pci-bridge,chassis_nr=1", &data);
     free_test_data(&data);
 }
@@ -807,6 +815,8 @@ static void test_acpi_q35_tcg(void)
 
     memset(&data, 0, sizeof(data));
     data.machine = MACHINE_Q35;
+    data.required_struct_types = base_required_struct_types;
+    data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types);
     test_acpi_one("-machine q35,accel=tcg", &data);
     free_test_data(&data);
 }
@@ -818,11 +828,49 @@ static void test_acpi_q35_tcg_bridge(void)
     memset(&data, 0, sizeof(data));
     data.machine = MACHINE_Q35;
     data.variant = ".bridge";
+    data.required_struct_types = base_required_struct_types;
+    data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types);
     test_acpi_one("-machine q35,accel=tcg -device pci-bridge,chassis_nr=1",
                   &data);
     free_test_data(&data);
 }
 
+static uint8_t ipmi_required_struct_types[] =
+    {0, 1, 3, 4, 16, 17, 19, 32, 38, 127};
+
+static void test_acpi_q35_tcg_ipmi(void)
+{
+    test_data data;
+
+    memset(&data, 0, sizeof(data));
+    data.machine = MACHINE_Q35;
+    data.variant = ".ipmibt";
+    data.required_struct_types = ipmi_required_struct_types;
+    data.required_struct_types_len = ARRAY_SIZE(ipmi_required_struct_types);
+    test_acpi_one("-machine q35,accel=tcg -device ipmi-bmc-sim,id=bmc0"
+                  " -device isa-ipmi-bt,bmc=bmc0",
+                  &data);
+    free_test_data(&data);
+}
+
+static void test_acpi_piix4_tcg_ipmi(void)
+{
+    test_data data;
+
+    /* Supplying -machine accel argument overrides the default (qtest).
+     * This is to make guest actually run.
+     */
+    memset(&data, 0, sizeof(data));
+    data.machine = MACHINE_PC;
+    data.variant = ".ipmikcs";
+    data.required_struct_types = ipmi_required_struct_types;
+    data.required_struct_types_len = ARRAY_SIZE(ipmi_required_struct_types);
+    test_acpi_one("-machine accel=tcg -device ipmi-bmc-sim,id=bmc0"
+                  " -device isa-ipmi-kcs,irq=0,bmc=bmc0",
+                  &data);
+    free_test_data(&data);
+}
+
 int main(int argc, char *argv[])
 {
     const char *arch = qtest_get_arch();
@@ -843,6 +891,8 @@ int main(int argc, char *argv[])
         qtest_add_func("acpi/piix4/tcg/bridge", test_acpi_piix4_tcg_bridge);
         qtest_add_func("acpi/q35/tcg", test_acpi_q35_tcg);
         qtest_add_func("acpi/q35/tcg/bridge", test_acpi_q35_tcg_bridge);
+        qtest_add_func("acpi/piix4/tcg/ipmi", test_acpi_piix4_tcg_ipmi);
+        qtest_add_func("acpi/q35/tcg/ipmi", test_acpi_q35_tcg_ipmi);
     }
     ret = g_test_run();
     unlink(disk);
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 16/16] ipmi: Add a force off function
  2015-12-17 18:50 [Qemu-devel] [PATCH v5 0/16] Add an IPMI device to QEMU minyard
                   ` (14 preceding siblings ...)
  2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 15/16] bios: Add tests for the IPMI ACPI and SMBIOS entries minyard
@ 2015-12-17 18:50 ` minyard
  15 siblings, 0 replies; 17+ messages in thread
From: minyard @ 2015-12-17 18:50 UTC (permalink / raw)
  To: qemu-devel, Paolo Bonzini, Michael S. Tsirkin, Igor Mammedov
  Cc: Corey Minyard

From: Corey Minyard <cminyard@mvista.com>

Allow the IPMI interface to request a forced power off.

Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
 hw/ipmi/ipmi_bmc_extern.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c
index 05b9121..56073b3 100644
--- a/hw/ipmi/ipmi_bmc_extern.c
+++ b/hw/ipmi/ipmi_bmc_extern.c
@@ -30,6 +30,7 @@
 #include <stdint.h>
 #include "qemu/timer.h"
 #include "sysemu/char.h"
+#include "sysemu/sysemu.h"
 #include "hw/ipmi/ipmi.h"
 
 #define VM_MSG_CHAR        0xA0 /* Marks end of message */
@@ -52,6 +53,7 @@
 #define   VM_CAPABILITIES_IRQ      0x04
 #define   VM_CAPABILITIES_NMI      0x08
 #define   VM_CAPABILITIES_ATTN     0x10
+#define VM_CMD_FORCEOFF            0x09
 
 #define TYPE_IPMI_BMC_EXTERN "ipmi-bmc-extern"
 #define IPMI_BMC_EXTERN(obj) OBJECT_CHECK(IPMIBmcExtern, (obj), \
@@ -268,6 +270,10 @@ static void handle_hw_op(IPMIBmcExtern *ibe, unsigned char hw_op)
     case VM_CMD_SEND_NMI:
         k->do_hw_op(s, IPMI_SEND_NMI, 0);
         break;
+
+    case VM_CMD_FORCEOFF:
+        qemu_system_shutdown_request();
+        break;
     }
 }
 
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2015-12-17 18:59 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-12-17 18:50 [Qemu-devel] [PATCH v5 0/16] Add an IPMI device to QEMU minyard
2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 01/16] Add a base IPMI interface minyard
2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 02/16] ipmi: Add a local BMC simulation minyard
2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 03/16] ipmi: Add an external connection simulation interface minyard
2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 04/16] ipmi: Add an ISA KCS low-level interface minyard
2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 05/16] ipmi: Add a BT " minyard
2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 06/16] ipmi: Add tests minyard
2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 07/16] ipmi: Add documentation minyard
2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 08/16] ipmi: Add migration capability to the IPMI devices minyard
2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 09/16] ipmi: Add a firmware configuration repository minyard
2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 10/16] ipmi: Add firmware registration to the ISA interface minyard
2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 11/16] smbios: Move table build tools into an include file minyard
2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 12/16] pc: Postpone SMBIOS table installation to post machine init minyard
2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 13/16] ipmi: Add SMBIOS table entry minyard
2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 14/16] acpi: Add IPMI table entries minyard
2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 15/16] bios: Add tests for the IPMI ACPI and SMBIOS entries minyard
2015-12-17 18:50 ` [Qemu-devel] [PATCH v5 16/16] ipmi: Add a force off function minyard

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).