* [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).