aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/acpi/aml-build.c40
-rw-r--r--hw/acpi/ipmi-stub.c2
-rw-r--r--hw/acpi/ipmi.c13
-rw-r--r--hw/i386/Kconfig3
-rw-r--r--hw/i386/acpi-build.c17
-rw-r--r--hw/i386/pc_piix.c12
-rw-r--r--hw/i386/pc_q35.c9
-rw-r--r--hw/ipmi/Kconfig15
-rw-r--r--hw/ipmi/Makefile.objs5
-rw-r--r--hw/ipmi/ipmi.c6
-rw-r--r--hw/ipmi/ipmi_bmc_sim.c30
-rw-r--r--hw/ipmi/ipmi_bt.c437
-rw-r--r--hw/ipmi/ipmi_kcs.c423
-rw-r--r--hw/ipmi/isa_ipmi_bt.c443
-rw-r--r--hw/ipmi/isa_ipmi_kcs.c419
-rw-r--r--hw/ipmi/pci_ipmi_bt.c146
-rw-r--r--hw/ipmi/pci_ipmi_kcs.c146
-rw-r--r--hw/ipmi/smbus_ipmi.c384
-rw-r--r--hw/smbios/smbios_type_38.c3
19 files changed, 1703 insertions, 850 deletions
diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c
index 78aee1a2f9..2c3702b882 100644
--- a/hw/acpi/aml-build.c
+++ b/hw/acpi/aml-build.c
@@ -1874,3 +1874,43 @@ build_hdr:
build_header(linker, tbl, (void *)(tbl->data + fadt_start),
"FACP", tbl->len - fadt_start, f->rev, oem_id, oem_table_id);
}
+
+/* ACPI 5.0: 6.4.3.8.2 Serial Bus Connection Descriptors */
+static Aml *aml_serial_bus_device(uint8_t serial_bus_type, uint8_t flags,
+ uint16_t type_flags,
+ uint8_t revid, uint16_t data_length,
+ uint16_t resource_source_len)
+{
+ Aml *var = aml_alloc();
+ uint16_t length = data_length + resource_source_len + 9;
+
+ build_append_byte(var->buf, 0x8e); /* Serial Bus Connection Descriptor */
+ build_append_int_noprefix(var->buf, length, sizeof(length));
+ build_append_byte(var->buf, 1); /* Revision ID */
+ build_append_byte(var->buf, 0); /* Resource Source Index */
+ build_append_byte(var->buf, serial_bus_type); /* Serial Bus Type */
+ build_append_byte(var->buf, flags); /* General Flags */
+ build_append_int_noprefix(var->buf, type_flags, /* Type Specific Flags */
+ sizeof(type_flags));
+ build_append_byte(var->buf, revid); /* Type Specification Revision ID */
+ build_append_int_noprefix(var->buf, data_length, sizeof(data_length));
+
+ return var;
+}
+
+/* ACPI 5.0: 6.4.3.8.2.1 I2C Serial Bus Connection Resource Descriptor */
+Aml *aml_i2c_serial_bus_device(uint16_t address, const char *resource_source)
+{
+ uint16_t resource_source_len = strlen(resource_source) + 1;
+ Aml *var = aml_serial_bus_device(AML_SERIAL_BUS_TYPE_I2C, 0, 0, 1,
+ 6, resource_source_len);
+
+ /* Connection Speed. Just set to 100K for now, it doesn't really matter. */
+ build_append_int_noprefix(var->buf, 100000, 4);
+ build_append_int_noprefix(var->buf, address, sizeof(address));
+
+ /* This is a string, not a name, so just copy it directly in. */
+ g_array_append_vals(var->buf, resource_source, resource_source_len);
+
+ return var;
+}
diff --git a/hw/acpi/ipmi-stub.c b/hw/acpi/ipmi-stub.c
index f525f71c2d..8634fb325c 100644
--- a/hw/acpi/ipmi-stub.c
+++ b/hw/acpi/ipmi-stub.c
@@ -10,6 +10,6 @@
#include "qemu/osdep.h"
#include "hw/acpi/ipmi.h"
-void build_acpi_ipmi_devices(Aml *table, BusState *bus)
+void build_acpi_ipmi_devices(Aml *table, BusState *bus, const char *resource)
{
}
diff --git a/hw/acpi/ipmi.c b/hw/acpi/ipmi.c
index 651e2e94ea..96e48eba15 100644
--- a/hw/acpi/ipmi.c
+++ b/hw/acpi/ipmi.c
@@ -13,7 +13,7 @@
#include "hw/acpi/acpi.h"
#include "hw/acpi/ipmi.h"
-static Aml *aml_ipmi_crs(IPMIFwInfo *info)
+static Aml *aml_ipmi_crs(IPMIFwInfo *info, const char *resource)
{
Aml *crs = aml_resource_template();
@@ -48,7 +48,8 @@ static Aml *aml_ipmi_crs(IPMIFwInfo *info)
info->register_spacing, info->register_length));
break;
case IPMI_MEMSPACE_SMBUS:
- aml_append(crs, aml_return(aml_int(info->base_address)));
+ aml_append(crs, aml_i2c_serial_bus_device(info->base_address,
+ resource));
break;
default:
abort();
@@ -61,7 +62,7 @@ static Aml *aml_ipmi_crs(IPMIFwInfo *info)
return crs;
}
-static Aml *aml_ipmi_device(IPMIFwInfo *info)
+static Aml *aml_ipmi_device(IPMIFwInfo *info, const char *resource)
{
Aml *dev;
uint16_t version = ((info->ipmi_spec_major_revision << 8)
@@ -74,14 +75,14 @@ static Aml *aml_ipmi_device(IPMIFwInfo *info)
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)));
+ aml_append(dev, aml_name_decl("_CRS", aml_ipmi_crs(info, resource)));
aml_append(dev, aml_name_decl("_IFT", aml_int(info->interface_type)));
aml_append(dev, aml_name_decl("_SRV", aml_int(version)));
return dev;
}
-void build_acpi_ipmi_devices(Aml *scope, BusState *bus)
+void build_acpi_ipmi_devices(Aml *scope, BusState *bus, const char *resource)
{
BusChild *kid;
@@ -101,6 +102,6 @@ void build_acpi_ipmi_devices(Aml *scope, BusState *bus)
iic = IPMI_INTERFACE_GET_CLASS(obj);
memset(&info, 0, sizeof(info));
iic->get_fwinfo(ii, &info);
- aml_append(scope, aml_ipmi_device(&info));
+ aml_append(scope, aml_ipmi_device(&info, resource));
}
}
diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig
index c7a9d6315c..c5c9d4900e 100644
--- a/hw/i386/Kconfig
+++ b/hw/i386/Kconfig
@@ -8,6 +8,9 @@ config PC
imply HYPERV
imply ISA_IPMI_KCS
imply ISA_IPMI_BT
+ imply PCI_IPMI_KCS
+ imply PCI_IPMI_BT
+ imply IPMI_SSIF
imply ISA_DEBUG
imply PARALLEL
imply PCI_DEVICES
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index e54e571a75..4e0f9f425a 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -1290,7 +1290,7 @@ static void build_isa_devices_aml(Aml *table)
} else if (!obj) {
error_report("No ISA bus, unable to define IPMI ACPI data");
} else {
- build_acpi_ipmi_devices(scope, BUS(obj));
+ build_acpi_ipmi_devices(scope, BUS(obj), "\\_SB.PCI0.ISA");
}
aml_append(table, scope);
@@ -1809,6 +1809,18 @@ static Aml *build_q35_osc_method(void)
return method;
}
+static void build_smb0(Aml *table, I2CBus *smbus, int devnr, int func)
+{
+ Aml *scope = aml_scope("_SB.PCI0");
+ Aml *dev = aml_device("SMB0");
+
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("APP0005")));
+ aml_append(dev, aml_name_decl("_ADR", aml_int(devnr << 16 | func)));
+ build_acpi_ipmi_devices(dev, BUS(smbus), "\\_SB.PCI0.SMB0");
+ aml_append(scope, dev);
+ aml_append(table, scope);
+}
+
static void
build_dsdt(GArray *table_data, BIOSLinker *linker,
AcpiPmInfo *pm, AcpiMiscInfo *misc,
@@ -1862,6 +1874,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
build_q35_isa_bridge(dsdt);
build_isa_devices_aml(dsdt);
build_q35_pci0_int(dsdt);
+ if (pcms->smbus && !pcmc->do_not_add_smb_acpi) {
+ build_smb0(dsdt, pcms->smbus, ICH9_SMB_DEV, ICH9_SMB_FUNC);
+ }
}
if (pcmc->legacy_cpu_hotplug) {
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 2362675149..6824b72124 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -283,15 +283,14 @@ else {
if (pcmc->pci_enabled && acpi_enabled) {
DeviceState *piix4_pm;
- I2CBus *smbus;
smi_irq = qemu_allocate_irq(pc_acpi_smi_interrupt, first_cpu, 0);
/* TODO: Populate SPD eeprom data. */
- smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100,
- pcms->gsi[9], smi_irq,
- pc_machine_is_smm_enabled(pcms),
- &piix4_pm);
- smbus_eeprom_init(smbus, 8, NULL, 0);
+ pcms->smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100,
+ pcms->gsi[9], smi_irq,
+ pc_machine_is_smm_enabled(pcms),
+ &piix4_pm);
+ smbus_eeprom_init(pcms->smbus, 8, NULL, 0);
object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP,
TYPE_HOTPLUG_HANDLER,
@@ -476,6 +475,7 @@ static void pc_i440fx_3_1_machine_options(MachineClass *m)
pc_i440fx_4_0_machine_options(m);
m->is_default = 0;
+ pcmc->do_not_add_smb_acpi = true;
m->smbus_no_migration_support = true;
m->alias = NULL;
pcmc->pvh_enabled = false;
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index d4e8a1cb9f..8fad20f314 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -316,10 +316,10 @@ static void pc_q35_init(MachineState *machine)
if (pcms->smbus_enabled) {
/* TODO: Populate SPD eeprom data. */
- smbus_eeprom_init(ich9_smb_init(host_bus,
- PCI_DEVFN(ICH9_SMB_DEV, ICH9_SMB_FUNC),
- 0xb100),
- 8, NULL, 0);
+ pcms->smbus = ich9_smb_init(host_bus,
+ PCI_DEVFN(ICH9_SMB_DEV, ICH9_SMB_FUNC),
+ 0xb100);
+ smbus_eeprom_init(pcms->smbus, 8, NULL, 0);
}
pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state);
@@ -421,6 +421,7 @@ static void pc_q35_3_1_machine_options(MachineClass *m)
pc_q35_4_0_machine_options(m);
m->default_kernel_irqchip_split = false;
+ pcmc->do_not_add_smb_acpi = true;
m->smbus_no_migration_support = true;
m->alias = NULL;
pcmc->pvh_enabled = false;
diff --git a/hw/ipmi/Kconfig b/hw/ipmi/Kconfig
index b944fae100..9befd4f422 100644
--- a/hw/ipmi/Kconfig
+++ b/hw/ipmi/Kconfig
@@ -20,3 +20,18 @@ config ISA_IPMI_BT
bool
depends on ISA_BUS
select IPMI
+
+config PCI_IPMI_KCS
+ bool
+ depends on PCI
+ select IPMI
+
+config PCI_IPMI_BT
+ bool
+ depends on PCI
+ select IPMI
+
+config IPMI_SSIF
+ bool
+ depends on I2C
+ select IPMI
diff --git a/hw/ipmi/Makefile.objs b/hw/ipmi/Makefile.objs
index 1b422bbee0..3cca10bc50 100644
--- a/hw/ipmi/Makefile.objs
+++ b/hw/ipmi/Makefile.objs
@@ -1,5 +1,8 @@
-common-obj-$(CONFIG_IPMI) += ipmi.o
+common-obj-$(CONFIG_IPMI) += ipmi.o ipmi_kcs.o ipmi_bt.o
common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_sim.o
common-obj-$(CONFIG_IPMI_EXTERN) += ipmi_bmc_extern.o
common-obj-$(CONFIG_ISA_IPMI_KCS) += isa_ipmi_kcs.o
+common-obj-$(CONFIG_PCI_IPMI_KCS) += pci_ipmi_kcs.o
common-obj-$(CONFIG_ISA_IPMI_BT) += isa_ipmi_bt.o
+common-obj-$(CONFIG_PCI_IPMI_BT) += pci_ipmi_bt.o
+common-obj-$(CONFIG_IPMI_SSIF) += smbus_ipmi.o
diff --git a/hw/ipmi/ipmi.c b/hw/ipmi/ipmi.c
index 136c86b7a7..cbe158f815 100644
--- a/hw/ipmi/ipmi.c
+++ b/hw/ipmi/ipmi.c
@@ -28,9 +28,8 @@
#include "qom/object_interfaces.h"
#include "sysemu/runstate.h"
#include "qapi/error.h"
-#include "qapi/qapi-commands-misc.h"
-#include "qapi/visitor.h"
#include "qemu/module.h"
+#include "hw/nmi.h"
static uint32_t ipmi_current_uuid = 1;
@@ -60,7 +59,8 @@ static int ipmi_do_hw_op(IPMIInterface *s, enum ipmi_op op, int checkonly)
if (checkonly) {
return 0;
}
- qmp_inject_nmi(NULL);
+ /* We don't care what CPU we use. */
+ nmi_monitor_handle(0, NULL);
return 0;
case IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP:
diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c
index 246a6d390c..71e56f3b13 100644
--- a/hw/ipmi/ipmi_bmc_sim.c
+++ b/hw/ipmi/ipmi_bmc_sim.c
@@ -223,7 +223,7 @@ struct IPMIBmcSim {
uint8_t restart_cause;
uint8_t acpi_power_state[2];
- uint8_t uuid[16];
+ QemuUUID uuid;
IPMISel sel;
IPMISdr sdr;
@@ -477,7 +477,9 @@ static int attn_set(IPMIBmcSim *ibs)
static int attn_irq_enabled(IPMIBmcSim *ibs)
{
- return (IPMI_BMC_MSG_INTS_ON(ibs) && IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE_SET(ibs))
+ return (IPMI_BMC_MSG_INTS_ON(ibs) &&
+ (IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE_SET(ibs) ||
+ IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK_SET(ibs)))
|| (IPMI_BMC_EVBUF_FULL_INT_ENABLED(ibs) &&
IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(ibs));
}
@@ -939,8 +941,19 @@ static void get_device_guid(IPMIBmcSim *ibs,
{
unsigned int i;
+ /* An uninitialized uuid is all zeros, use that to know if it is set. */
for (i = 0; i < 16; i++) {
- rsp_buffer_push(rsp, ibs->uuid[i]);
+ if (ibs->uuid.data[i]) {
+ goto uuid_set;
+ }
+ }
+ /* No uuid is set, return an error. */
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_CMD);
+ return;
+
+ uuid_set:
+ for (i = 0; i < 16; i++) {
+ rsp_buffer_push(rsp, ibs->uuid.data[i]);
}
}
@@ -1194,7 +1207,7 @@ static void set_watchdog_timer(IPMIBmcSim *ibs,
break;
case IPMI_BMC_WATCHDOG_PRE_NMI:
- if (!k->do_hw_op(s, IPMI_SEND_NMI, 1)) {
+ if (k->do_hw_op(s, IPMI_SEND_NMI, 1)) {
/* NMI not supported. */
rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
return;
@@ -1228,6 +1241,8 @@ static void get_watchdog_timer(IPMIBmcSim *ibs,
rsp_buffer_push(rsp, ibs->watchdog_action);
rsp_buffer_push(rsp, ibs->watchdog_pretimeout);
rsp_buffer_push(rsp, ibs->watchdog_expired);
+ rsp_buffer_push(rsp, ibs->watchdog_timeout & 0xff);
+ rsp_buffer_push(rsp, (ibs->watchdog_timeout >> 8) & 0xff);
if (ibs->watchdog_running) {
long timeout;
timeout = ((ibs->watchdog_expiry - ipmi_getmonotime() + 50000000)
@@ -1982,12 +1997,6 @@ static void ipmi_sim_realize(DeviceState *dev, Error **errp)
ibs->acpi_power_state[0] = 0;
ibs->acpi_power_state[1] = 0;
- if (qemu_uuid_set) {
- memcpy(&ibs->uuid, &qemu_uuid, 16);
- } else {
- memset(&ibs->uuid, 0, 16);
- }
-
ipmi_init_sensors_from_sdrs(ibs);
register_cmds(ibs);
@@ -2007,6 +2016,7 @@ static Property ipmi_sim_properties[] = {
DEFINE_PROP_UINT8("fwrev2", IPMIBmcSim, fwrev2, 0),
DEFINE_PROP_UINT32("mfg_id", IPMIBmcSim, mfg_id, 0),
DEFINE_PROP_UINT16("product_id", IPMIBmcSim, product_id, 0),
+ DEFINE_PROP_UUID_NODEFAULT("guid", IPMIBmcSim, uuid),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/ipmi/ipmi_bt.c b/hw/ipmi/ipmi_bt.c
new file mode 100644
index 0000000000..22f94fb98d
--- /dev/null
+++ b/hw/ipmi/ipmi_bt.c
@@ -0,0 +1,437 @@
+/*
+ * QEMU 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 "qemu/osdep.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "hw/ipmi/ipmi_bt.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_GET_CLR_WR(d) (((d) >> IPMI_BT_CLR_WR_BIT) & 0x1)
+
+#define IPMI_BT_GET_CLR_RD(d) (((d) >> IPMI_BT_CLR_RD_BIT) & 0x1)
+
+#define IPMI_BT_GET_H2B_ATN(d) (((d) >> IPMI_BT_H2B_ATN_BIT) & 0x1)
+
+#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) << 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) << 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) << IPMI_BT_HBUSY_BIT)))
+
+#define IPMI_BT_BBUSY_MASK (1 << IPMI_BT_BBUSY_BIT)
+#define IPMI_BT_SET_BBUSY(d, v) ((d) = (((d) & ~IPMI_BT_BBUSY_MASK) | \
+ (!!(v) << 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) << 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) << IPMI_BT_B2H_IRQ_BIT)))
+
+#define IPMI_CMD_GET_BT_INTF_CAP 0x36
+
+static void ipmi_bt_raise_irq(IPMIBT *ib)
+{
+ if (ib->use_irq && ib->irqs_enabled && ib->raise_irq) {
+ ib->raise_irq(ib);
+ }
+}
+
+static void ipmi_bt_lower_irq(IPMIBT *ib)
+{
+ if (ib->lower_irq) {
+ ib->lower_irq(ib);
+ }
+}
+
+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 (!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);
+ ipmi_bt_raise_irq(ib);
+ }
+ 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 (!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);
+ ipmi_bt_raise_irq(ib);
+ }
+ }
+}
+
+
+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 & ib->size_mask) {
+ 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;
+ default:
+ ret = 0xff;
+ 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 & ib->size_mask) {
+ 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);
+ ipmi_bt_raise_irq(ib);
+ }
+ 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);
+ ipmi_bt_lower_irq(ib);
+ }
+ 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);
+ ipmi_bt_lower_irq(ib);
+ }
+ break;
+ default:
+ /* Ignore. */
+ 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 && !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);
+ ipmi_bt_raise_irq(ib);
+ }
+ } 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);
+ ipmi_bt_lower_irq(ib);
+ }
+ }
+}
+
+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);
+ ipmi_bt_lower_irq(ib);
+ }
+ 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, unsigned int min_size, Error **errp)
+{
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+ IPMIBT *ib = iic->get_backend_data(ii);
+
+ if (min_size == 0) {
+ min_size = 4;
+ }
+ ib->size_mask = min_size - 1;
+ ib->io_length = 3;
+
+ memory_region_init_io(&ib->io, NULL, &ipmi_bt_io_ops, ii, "ipmi-bt",
+ min_size);
+}
+
+int ipmi_bt_vmstate_post_load(void *opaque, int version)
+{
+ IPMIBT *ib = opaque;
+
+ /* Make sure all the values are sane. */
+ if (ib->outpos >= MAX_IPMI_MSG_SIZE || ib->outlen >= MAX_IPMI_MSG_SIZE ||
+ ib->outpos >= ib->outlen) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "ipmi:bt: vmstate transfer received bad out values: %d %d\n",
+ ib->outpos, ib->outlen);
+ ib->outpos = 0;
+ ib->outlen = 0;
+ }
+
+ if (ib->inlen >= MAX_IPMI_MSG_SIZE) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "ipmi:bt: vmstate transfer received bad in value: %d\n",
+ ib->inlen);
+ ib->inlen = 0;
+ }
+
+ return 0;
+}
+
+const VMStateDescription vmstate_IPMIBT = {
+ .name = TYPE_IPMI_INTERFACE_PREFIX "bt",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .post_load = ipmi_bt_vmstate_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(obf_irq_set, IPMIBT),
+ VMSTATE_BOOL(atn_irq_set, IPMIBT),
+ VMSTATE_BOOL(irqs_enabled, IPMIBT),
+ VMSTATE_UINT32(outpos, IPMIBT),
+ VMSTATE_UINT32(outlen, IPMIBT),
+ VMSTATE_UINT8_ARRAY(outmsg, IPMIBT, MAX_IPMI_MSG_SIZE),
+ VMSTATE_UINT32(inlen, IPMIBT),
+ VMSTATE_UINT8_ARRAY(inmsg, IPMIBT, MAX_IPMI_MSG_SIZE),
+ VMSTATE_UINT8(control_reg, IPMIBT),
+ VMSTATE_UINT8(mask_reg, IPMIBT),
+ VMSTATE_UINT8(waiting_rsp, IPMIBT),
+ VMSTATE_UINT8(waiting_seq, IPMIBT),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+void ipmi_bt_get_fwinfo(struct IPMIBT *ib, IPMIFwInfo *info)
+{
+ info->interface_name = "bt";
+ info->interface_type = IPMI_SMBIOS_BT;
+ info->ipmi_spec_major_revision = 2;
+ info->ipmi_spec_minor_revision = 0;
+ info->base_address = ib->io_base;
+ info->register_length = ib->io_length;
+ info->register_spacing = 1;
+ info->memspace = IPMI_MEMSPACE_IO;
+ info->irq_type = IPMI_LEVEL_IRQ;
+}
+
+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;
+}
diff --git a/hw/ipmi/ipmi_kcs.c b/hw/ipmi/ipmi_kcs.c
new file mode 100644
index 0000000000..a77612946a
--- /dev/null
+++ b/hw/ipmi/ipmi_kcs.c
@@ -0,0 +1,423 @@
+/*
+ * QEMU IPMI KCS emulation
+ *
+ * Copyright (c) 2015,2017 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 "qemu/osdep.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "hw/ipmi/ipmi_kcs.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
+
+static void ipmi_kcs_raise_irq(IPMIKCS *ik)
+{
+ if (ik->use_irq && ik->irqs_enabled && ik->raise_irq) {
+ ik->raise_irq(ik);
+ }
+}
+
+static void ipmi_kcs_lower_irq(IPMIKCS *ik)
+{
+ if (ik->lower_irq) {
+ ik->lower_irq(ik);
+ }
+}
+
+#define SET_OBF() \
+ do { \
+ IPMI_KCS_SET_OBF(ik->status_reg, 1); \
+ if (!ik->obf_irq_set) { \
+ ik->obf_irq_set = 1; \
+ if (!ik->atn_irq_set) { \
+ ipmi_kcs_raise_irq(ik); \
+ } \
+ } \
+ } 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 & ik->size_mask) {
+ 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) {
+ ipmi_kcs_lower_irq(ik);
+ }
+ }
+ break;
+
+ case 1:
+ ret = ik->status_reg;
+ if (ik->atn_irq_set) {
+ ik->atn_irq_set = 0;
+ if (!ik->obf_irq_set) {
+ ipmi_kcs_lower_irq(ik);
+ }
+ }
+ break;
+
+ default:
+ ret = 0xff;
+ }
+ 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 & ik->size_mask) {
+ case 0:
+ ik->data_in_reg = val;
+ break;
+
+ case 1:
+ ik->cmd_reg = val;
+ break;
+
+ default:
+ /* Ignore. */
+ 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->atn_irq_set = 1;
+ if (!ik->obf_irq_set) {
+ ipmi_kcs_raise_irq(ik);
+ }
+ }
+ } else {
+ if (ik->atn_irq_set) {
+ ik->atn_irq_set = 0;
+ if (!ik->obf_irq_set) {
+ ipmi_kcs_lower_irq(ik);
+ }
+ }
+ }
+}
+
+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;
+}
+
+/* min_size must be a power of 2. */
+static void ipmi_kcs_init(IPMIInterface *ii, unsigned int min_size,
+ Error **errp)
+{
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+ IPMIKCS *ik = iic->get_backend_data(ii);
+
+ if (min_size == 0) {
+ min_size = 2;
+ }
+ ik->size_mask = min_size - 1;
+ ik->io_length = 2;
+ memory_region_init_io(&ik->io, NULL, &ipmi_kcs_io_ops, ii, "ipmi-kcs",
+ min_size);
+}
+
+int ipmi_kcs_vmstate_post_load(void *opaque, int version)
+{
+ IPMIKCS *ik = opaque;
+
+ /* Make sure all the values are sane. */
+ if (ik->outpos >= MAX_IPMI_MSG_SIZE || ik->outlen >= MAX_IPMI_MSG_SIZE ||
+ ik->outpos >= ik->outlen) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "ipmi:kcs: vmstate transfer received bad out values: %d %d\n",
+ ik->outpos, ik->outlen);
+ ik->outpos = 0;
+ ik->outlen = 0;
+ }
+
+ if (ik->inlen >= MAX_IPMI_MSG_SIZE) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "ipmi:kcs: vmstate transfer received bad in value: %d\n",
+ ik->inlen);
+ ik->inlen = 0;
+ }
+
+ return 0;
+}
+
+static bool vmstate_kcs_before_version2(void *opaque, int version)
+{
+ return version <= 1;
+}
+
+const VMStateDescription vmstate_IPMIKCS = {
+ .name = TYPE_IPMI_INTERFACE_PREFIX "kcs",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .post_load = ipmi_kcs_vmstate_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(obf_irq_set, IPMIKCS),
+ VMSTATE_BOOL(atn_irq_set, IPMIKCS),
+ VMSTATE_UNUSED_TEST(vmstate_kcs_before_version2, 1), /* Was use_irq */
+ VMSTATE_BOOL(irqs_enabled, IPMIKCS),
+ VMSTATE_UINT32(outpos, IPMIKCS),
+ VMSTATE_UINT32_V(outlen, IPMIKCS, 2),
+ VMSTATE_UINT8_ARRAY(outmsg, IPMIKCS, MAX_IPMI_MSG_SIZE),
+ VMSTATE_UINT32_V(inlen, IPMIKCS, 2),
+ VMSTATE_UINT8_ARRAY(inmsg, IPMIKCS, MAX_IPMI_MSG_SIZE),
+ VMSTATE_BOOL(write_end, IPMIKCS),
+ VMSTATE_UINT8(status_reg, IPMIKCS),
+ VMSTATE_UINT8(data_out_reg, IPMIKCS),
+ VMSTATE_INT16(data_in_reg, IPMIKCS),
+ VMSTATE_INT16(cmd_reg, IPMIKCS),
+ VMSTATE_UINT8(waiting_rsp, IPMIKCS),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+void ipmi_kcs_get_fwinfo(IPMIKCS *ik, IPMIFwInfo *info)
+{
+ info->interface_name = "kcs";
+ info->interface_type = IPMI_SMBIOS_KCS;
+ info->ipmi_spec_major_revision = 2;
+ info->ipmi_spec_minor_revision = 0;
+ info->base_address = ik->io_base;
+ info->i2c_slave_address = ik->bmc->slave_addr;
+ info->register_length = ik->io_length;
+ info->register_spacing = 1;
+ info->memspace = IPMI_MEMSPACE_IO;
+ info->irq_type = IPMI_LEVEL_IRQ;
+}
+
+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;
+}
diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c
index a696096cbb..9a87ffd3f0 100644
--- a/hw/ipmi/isa_ipmi_bt.c
+++ b/hw/ipmi/isa_ipmi_bt.c
@@ -26,403 +26,46 @@
#include "qemu/log.h"
#include "qemu/module.h"
#include "qapi/error.h"
-#include "hw/ipmi/ipmi.h"
#include "hw/irq.h"
+#include "hw/ipmi/ipmi_bt.h"
#include "hw/isa/isa.h"
#include "hw/qdev-properties.h"
#include "migration/vmstate.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_GET_CLR_WR(d) (((d) >> IPMI_BT_CLR_WR_BIT) & 0x1)
-
-#define IPMI_BT_GET_CLR_RD(d) (((d) >> IPMI_BT_CLR_RD_BIT) & 0x1)
-
-#define IPMI_BT_GET_H2B_ATN(d) (((d) >> IPMI_BT_H2B_ATN_BIT) & 0x1)
-
-#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) << 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) << 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) << IPMI_BT_HBUSY_BIT)))
-
-#define IPMI_BT_BBUSY_MASK (1 << IPMI_BT_BBUSY_BIT)
-#define IPMI_BT_SET_BBUSY(d, v) ((d) = (((d) & ~IPMI_BT_BBUSY_MASK) | \
- (!!(v) << 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) << 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) << 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);
-}
-
-
#define TYPE_ISA_IPMI_BT "isa-ipmi-bt"
#define ISA_IPMI_BT(obj) OBJECT_CHECK(ISAIPMIBTDevice, (obj), \
- TYPE_ISA_IPMI_BT)
+ TYPE_ISA_IPMI_BT)
typedef struct ISAIPMIBTDevice {
ISADevice dev;
int32_t isairq;
+ qemu_irq irq;
IPMIBT bt;
uint32_t uuid;
} ISAIPMIBTDevice;
-static void ipmi_bt_get_fwinfo(struct IPMIInterface *ii, IPMIFwInfo *info)
+static void isa_ipmi_bt_get_fwinfo(struct IPMIInterface *ii, IPMIFwInfo *info)
{
ISAIPMIBTDevice *iib = ISA_IPMI_BT(ii);
- info->interface_name = "bt";
- info->interface_type = IPMI_SMBIOS_BT;
- info->ipmi_spec_major_revision = 2;
- info->ipmi_spec_minor_revision = 0;
- info->base_address = iib->bt.io_base;
- info->register_length = iib->bt.io_length;
- info->register_spacing = 1;
- info->memspace = IPMI_MEMSPACE_IO;
- info->irq_type = IPMI_LEVEL_IRQ;
+ ipmi_bt_get_fwinfo(&iib->bt, info);
info->interrupt_number = iib->isairq;
info->i2c_slave_address = iib->bt.bmc->slave_addr;
info->uuid = iib->uuid;
}
-static void ipmi_bt_class_init(IPMIInterfaceClass *iic)
+static void isa_ipmi_bt_raise_irq(IPMIBT *ib)
{
- 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;
- iic->get_fwinfo = ipmi_bt_get_fwinfo;
+ ISAIPMIBTDevice *iib = ib->opaque;
+
+ qemu_irq_raise(iib->irq);
+}
+
+static void isa_ipmi_bt_lower_irq(IPMIBT *ib)
+{
+ ISAIPMIBTDevice *iib = ib->opaque;
+
+ qemu_irq_lower(iib->irq);
}
static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp)
@@ -440,14 +83,17 @@ static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp)
iib->uuid = ipmi_next_uuid();
iib->bt.bmc->intf = ii;
+ iib->bt.opaque = iib;
- iic->init(ii, errp);
+ iic->init(ii, 0, errp);
if (*errp)
return;
if (iib->isairq > 0) {
- isa_init_irq(isadev, &iib->bt.irq, iib->isairq);
+ isa_init_irq(isadev, &iib->irq, iib->isairq);
iib->bt.use_irq = 1;
+ iib->bt.raise_irq = isa_ipmi_bt_raise_irq;
+ iib->bt.lower_irq = isa_ipmi_bt_lower_irq;
}
qdev_set_legacy_instance_id(dev, iib->bt.io_base, iib->bt.io_length);
@@ -455,52 +101,6 @@ static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp)
isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base);
}
-static int ipmi_bt_vmstate_post_load(void *opaque, int version)
-{
- IPMIBT *ib = opaque;
-
- /* Make sure all the values are sane. */
- if (ib->outpos >= MAX_IPMI_MSG_SIZE || ib->outlen >= MAX_IPMI_MSG_SIZE ||
- ib->outpos >= ib->outlen) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "ipmi:bt: vmstate transfer received bad out values: %d %d\n",
- ib->outpos, ib->outlen);
- ib->outpos = 0;
- ib->outlen = 0;
- }
-
- if (ib->inlen >= MAX_IPMI_MSG_SIZE) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "ipmi:bt: vmstate transfer received bad in value: %d\n",
- ib->inlen);
- ib->inlen = 0;
- }
-
- return 0;
-}
-
-const VMStateDescription vmstate_IPMIBT = {
- .name = TYPE_IPMI_INTERFACE_PREFIX "bt",
- .version_id = 1,
- .minimum_version_id = 1,
- .post_load = ipmi_bt_vmstate_post_load,
- .fields = (VMStateField[]) {
- VMSTATE_BOOL(obf_irq_set, IPMIBT),
- VMSTATE_BOOL(atn_irq_set, IPMIBT),
- VMSTATE_BOOL(irqs_enabled, IPMIBT),
- VMSTATE_UINT32(outpos, IPMIBT),
- VMSTATE_UINT32(outlen, IPMIBT),
- VMSTATE_UINT8_ARRAY(outmsg, IPMIBT, MAX_IPMI_MSG_SIZE),
- VMSTATE_UINT32(inlen, IPMIBT),
- VMSTATE_UINT8_ARRAY(inmsg, IPMIBT, MAX_IPMI_MSG_SIZE),
- VMSTATE_UINT8(control_reg, IPMIBT),
- VMSTATE_UINT8(mask_reg, IPMIBT),
- VMSTATE_UINT8(waiting_rsp, IPMIBT),
- VMSTATE_UINT8(waiting_seq, IPMIBT),
- VMSTATE_END_OF_LIST()
- }
-};
-
static const VMStateDescription vmstate_ISAIPMIBTDevice = {
.name = TYPE_IPMI_INTERFACE_PREFIX "isa-bt",
.version_id = 2,
@@ -548,6 +148,7 @@ static void isa_ipmi_bt_class_init(ObjectClass *oc, void *data)
iic->get_backend_data = isa_ipmi_bt_get_backend_data;
ipmi_bt_class_init(iic);
+ iic->get_fwinfo = isa_ipmi_bt_get_fwinfo;
}
static const TypeInfo isa_ipmi_bt_info = {
diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c
index 374b2a0709..ca3ea36a3f 100644
--- a/hw/ipmi/isa_ipmi_kcs.c
+++ b/hw/ipmi/isa_ipmi_kcs.c
@@ -1,7 +1,7 @@
/*
* QEMU ISA IPMI KCS emulation
*
- * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
+ * Copyright (c) 2015,2017 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
@@ -26,338 +26,12 @@
#include "qemu/log.h"
#include "qemu/module.h"
#include "qapi/error.h"
-#include "hw/ipmi/ipmi.h"
#include "hw/irq.h"
+#include "hw/ipmi/ipmi_kcs.h"
#include "hw/isa/isa.h"
#include "hw/qdev-properties.h"
#include "migration/vmstate.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);
-}
-
#define TYPE_ISA_IPMI_KCS "isa-ipmi-kcs"
#define ISA_IPMI_KCS(obj) OBJECT_CHECK(ISAIPMIKCSDevice, (obj), \
TYPE_ISA_IPMI_KCS)
@@ -365,36 +39,32 @@ static void ipmi_kcs_init(IPMIInterface *ii, Error **errp)
typedef struct ISAIPMIKCSDevice {
ISADevice dev;
int32_t isairq;
+ qemu_irq irq;
IPMIKCS kcs;
uint32_t uuid;
} ISAIPMIKCSDevice;
-static void ipmi_kcs_get_fwinfo(IPMIInterface *ii, IPMIFwInfo *info)
+static void isa_ipmi_kcs_get_fwinfo(IPMIInterface *ii, IPMIFwInfo *info)
{
ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(ii);
- info->interface_name = "kcs";
- info->interface_type = IPMI_SMBIOS_KCS;
- info->ipmi_spec_major_revision = 2;
- info->ipmi_spec_minor_revision = 0;
- info->base_address = iik->kcs.io_base;
- info->i2c_slave_address = iik->kcs.bmc->slave_addr;
- info->register_length = iik->kcs.io_length;
- info->register_spacing = 1;
- info->memspace = IPMI_MEMSPACE_IO;
- info->irq_type = IPMI_LEVEL_IRQ;
+ ipmi_kcs_get_fwinfo(&iik->kcs, info);
info->interrupt_number = iik->isairq;
info->uuid = iik->uuid;
}
-static void ipmi_kcs_class_init(IPMIInterfaceClass *iic)
+static void isa_ipmi_kcs_raise_irq(IPMIKCS *ik)
{
- 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;
- iic->get_fwinfo = ipmi_kcs_get_fwinfo;
+ ISAIPMIKCSDevice *iik = ik->opaque;
+
+ qemu_irq_raise(iik->irq);
+}
+
+static void isa_ipmi_kcs_lower_irq(IPMIKCS *ik)
+{
+ ISAIPMIKCSDevice *iik = ik->opaque;
+
+ qemu_irq_lower(iik->irq);
}
static void ipmi_isa_realize(DeviceState *dev, Error **errp)
@@ -412,14 +82,17 @@ static void ipmi_isa_realize(DeviceState *dev, Error **errp)
iik->uuid = ipmi_next_uuid();
iik->kcs.bmc->intf = ii;
+ iik->kcs.opaque = iik;
- iic->init(ii, errp);
+ iic->init(ii, 0, errp);
if (*errp)
return;
if (iik->isairq > 0) {
- isa_init_irq(isadev, &iik->kcs.irq, iik->isairq);
+ isa_init_irq(isadev, &iik->irq, iik->isairq);
iik->kcs.use_irq = 1;
+ iik->kcs.raise_irq = isa_ipmi_kcs_raise_irq;
+ iik->kcs.lower_irq = isa_ipmi_kcs_lower_irq;
}
qdev_set_legacy_instance_id(dev, iik->kcs.io_base, iik->kcs.io_length);
@@ -427,60 +100,11 @@ static void ipmi_isa_realize(DeviceState *dev, Error **errp)
isa_register_ioport(isadev, &iik->kcs.io, iik->kcs.io_base);
}
-static int ipmi_kcs_vmstate_post_load(void *opaque, int version)
-{
- IPMIKCS *ik = opaque;
-
- /* Make sure all the values are sane. */
- if (ik->outpos >= MAX_IPMI_MSG_SIZE || ik->outlen >= MAX_IPMI_MSG_SIZE ||
- ik->outpos >= ik->outlen) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "ipmi:kcs: vmstate transfer received bad out values: %d %d\n",
- ik->outpos, ik->outlen);
- ik->outpos = 0;
- ik->outlen = 0;
- }
-
- if (ik->inlen >= MAX_IPMI_MSG_SIZE) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "ipmi:kcs: vmstate transfer received bad in value: %d\n",
- ik->inlen);
- ik->inlen = 0;
- }
-
- return 0;
-}
-
static bool vmstate_kcs_before_version2(void *opaque, int version)
{
return version <= 1;
}
-static const VMStateDescription vmstate_IPMIKCS = {
- .name = TYPE_IPMI_INTERFACE_PREFIX "kcs",
- .version_id = 2,
- .minimum_version_id = 1,
- .post_load = ipmi_kcs_vmstate_post_load,
- .fields = (VMStateField[]) {
- VMSTATE_BOOL(obf_irq_set, IPMIKCS),
- VMSTATE_BOOL(atn_irq_set, IPMIKCS),
- VMSTATE_UNUSED_TEST(vmstate_kcs_before_version2, 1), /* Was use_irq */
- VMSTATE_BOOL(irqs_enabled, IPMIKCS),
- VMSTATE_UINT32(outpos, IPMIKCS),
- VMSTATE_UINT32_V(outlen, IPMIKCS, 2),
- VMSTATE_UINT8_ARRAY(outmsg, IPMIKCS, MAX_IPMI_MSG_SIZE),
- VMSTATE_UINT32_V(inlen, IPMIKCS, 2),
- VMSTATE_UINT8_ARRAY(inmsg, IPMIKCS, MAX_IPMI_MSG_SIZE),
- VMSTATE_BOOL(write_end, IPMIKCS),
- VMSTATE_UINT8(status_reg, IPMIKCS),
- VMSTATE_UINT8(data_out_reg, IPMIKCS),
- VMSTATE_INT16(data_in_reg, IPMIKCS),
- VMSTATE_INT16(cmd_reg, IPMIKCS),
- VMSTATE_UINT8(waiting_rsp, IPMIKCS),
- VMSTATE_END_OF_LIST()
- }
-};
-
static const VMStateDescription vmstate_ISAIPMIKCSDevice = {
.name = TYPE_IPMI_INTERFACE,
.version_id = 2,
@@ -531,6 +155,7 @@ static void isa_ipmi_kcs_class_init(ObjectClass *oc, void *data)
iic->get_backend_data = isa_ipmi_kcs_get_backend_data;
ipmi_kcs_class_init(iic);
+ iic->get_fwinfo = isa_ipmi_kcs_get_fwinfo;
}
static const TypeInfo isa_ipmi_kcs_info = {
diff --git a/hw/ipmi/pci_ipmi_bt.c b/hw/ipmi/pci_ipmi_bt.c
new file mode 100644
index 0000000000..6ed925a665
--- /dev/null
+++ b/hw/ipmi/pci_ipmi_bt.c
@@ -0,0 +1,146 @@
+/*
+ * QEMU PCI IPMI BT emulation
+ *
+ * Copyright (c) 2017 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 "qemu/osdep.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "hw/ipmi/ipmi_bt.h"
+#include "hw/pci/pci.h"
+
+#define TYPE_PCI_IPMI_BT "pci-ipmi-bt"
+#define PCI_IPMI_BT(obj) OBJECT_CHECK(PCIIPMIBTDevice, (obj), \
+ TYPE_PCI_IPMI_BT)
+
+typedef struct PCIIPMIBTDevice {
+ PCIDevice dev;
+ IPMIBT bt;
+ bool irq_enabled;
+ uint32_t uuid;
+} PCIIPMIBTDevice;
+
+static void pci_ipmi_raise_irq(IPMIBT *ik)
+{
+ PCIIPMIBTDevice *pik = ik->opaque;
+
+ pci_set_irq(&pik->dev, true);
+}
+
+static void pci_ipmi_lower_irq(IPMIBT *ik)
+{
+ PCIIPMIBTDevice *pik = ik->opaque;
+
+ pci_set_irq(&pik->dev, false);
+}
+
+static void pci_ipmi_bt_realize(PCIDevice *pd, Error **errp)
+{
+ PCIIPMIBTDevice *pik = PCI_IPMI_BT(pd);
+ IPMIInterface *ii = IPMI_INTERFACE(pd);
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+
+ if (!pik->bt.bmc) {
+ error_setg(errp, "IPMI device requires a bmc attribute to be set");
+ return;
+ }
+
+ pik->uuid = ipmi_next_uuid();
+
+ pik->bt.bmc->intf = ii;
+ pik->bt.opaque = pik;
+
+ pci_config_set_prog_interface(pd->config, 0x02); /* BT */
+ pci_config_set_interrupt_pin(pd->config, 0x01);
+ pik->bt.use_irq = 1;
+ pik->bt.raise_irq = pci_ipmi_raise_irq;
+ pik->bt.lower_irq = pci_ipmi_lower_irq;
+
+ iic->init(ii, 8, errp);
+ if (*errp) {
+ return;
+ }
+ pci_register_bar(pd, 0, PCI_BASE_ADDRESS_SPACE_IO, &pik->bt.io);
+}
+
+const VMStateDescription vmstate_PCIIPMIBTDevice = {
+ .name = TYPE_IPMI_INTERFACE_PREFIX "pci-bt",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(dev, PCIIPMIBTDevice),
+ VMSTATE_STRUCT(bt, PCIIPMIBTDevice, 1, vmstate_IPMIBT, IPMIBT),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void pci_ipmi_bt_instance_init(Object *obj)
+{
+ PCIIPMIBTDevice *pik = PCI_IPMI_BT(obj);
+
+ ipmi_bmc_find_and_link(obj, (Object **) &pik->bt.bmc);
+}
+
+static void *pci_ipmi_bt_get_backend_data(IPMIInterface *ii)
+{
+ PCIIPMIBTDevice *pik = PCI_IPMI_BT(ii);
+
+ return &pik->bt;
+}
+
+static void pci_ipmi_bt_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc);
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc);
+
+ pdc->vendor_id = PCI_VENDOR_ID_QEMU;
+ pdc->device_id = PCI_DEVICE_ID_QEMU_IPMI;
+ pdc->revision = 1;
+ pdc->class_id = PCI_CLASS_SERIAL_IPMI;
+
+ dc->vmsd = &vmstate_PCIIPMIBTDevice;
+ dc->desc = "PCI IPMI BT";
+ pdc->realize = pci_ipmi_bt_realize;
+
+ iic->get_backend_data = pci_ipmi_bt_get_backend_data;
+ ipmi_bt_class_init(iic);
+}
+
+static const TypeInfo pci_ipmi_bt_info = {
+ .name = TYPE_PCI_IPMI_BT,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PCIIPMIBTDevice),
+ .instance_init = pci_ipmi_bt_instance_init,
+ .class_init = pci_ipmi_bt_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_IPMI_INTERFACE },
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { }
+ }
+};
+
+static void pci_ipmi_bt_register_types(void)
+{
+ type_register_static(&pci_ipmi_bt_info);
+}
+
+type_init(pci_ipmi_bt_register_types)
diff --git a/hw/ipmi/pci_ipmi_kcs.c b/hw/ipmi/pci_ipmi_kcs.c
new file mode 100644
index 0000000000..eeba63baa4
--- /dev/null
+++ b/hw/ipmi/pci_ipmi_kcs.c
@@ -0,0 +1,146 @@
+/*
+ * QEMU PCI IPMI KCS emulation
+ *
+ * Copyright (c) 2017 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 "qemu/osdep.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "hw/ipmi/ipmi_kcs.h"
+#include "hw/pci/pci.h"
+
+#define TYPE_PCI_IPMI_KCS "pci-ipmi-kcs"
+#define PCI_IPMI_KCS(obj) OBJECT_CHECK(PCIIPMIKCSDevice, (obj), \
+ TYPE_PCI_IPMI_KCS)
+
+typedef struct PCIIPMIKCSDevice {
+ PCIDevice dev;
+ IPMIKCS kcs;
+ bool irq_enabled;
+ uint32_t uuid;
+} PCIIPMIKCSDevice;
+
+static void pci_ipmi_raise_irq(IPMIKCS *ik)
+{
+ PCIIPMIKCSDevice *pik = ik->opaque;
+
+ pci_set_irq(&pik->dev, true);
+}
+
+static void pci_ipmi_lower_irq(IPMIKCS *ik)
+{
+ PCIIPMIKCSDevice *pik = ik->opaque;
+
+ pci_set_irq(&pik->dev, false);
+}
+
+static void pci_ipmi_kcs_realize(PCIDevice *pd, Error **errp)
+{
+ PCIIPMIKCSDevice *pik = PCI_IPMI_KCS(pd);
+ IPMIInterface *ii = IPMI_INTERFACE(pd);
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+
+ if (!pik->kcs.bmc) {
+ error_setg(errp, "IPMI device requires a bmc attribute to be set");
+ return;
+ }
+
+ pik->uuid = ipmi_next_uuid();
+
+ pik->kcs.bmc->intf = ii;
+ pik->kcs.opaque = pik;
+
+ pci_config_set_prog_interface(pd->config, 0x01); /* KCS */
+ pci_config_set_interrupt_pin(pd->config, 0x01);
+ pik->kcs.use_irq = 1;
+ pik->kcs.raise_irq = pci_ipmi_raise_irq;
+ pik->kcs.lower_irq = pci_ipmi_lower_irq;
+
+ iic->init(ii, 8, errp);
+ if (*errp) {
+ return;
+ }
+ pci_register_bar(pd, 0, PCI_BASE_ADDRESS_SPACE_IO, &pik->kcs.io);
+}
+
+const VMStateDescription vmstate_PCIIPMIKCSDevice = {
+ .name = TYPE_IPMI_INTERFACE_PREFIX "pci-kcs",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(dev, PCIIPMIKCSDevice),
+ VMSTATE_STRUCT(kcs, PCIIPMIKCSDevice, 1, vmstate_IPMIKCS, IPMIKCS),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void pci_ipmi_kcs_instance_init(Object *obj)
+{
+ PCIIPMIKCSDevice *pik = PCI_IPMI_KCS(obj);
+
+ ipmi_bmc_find_and_link(obj, (Object **) &pik->kcs.bmc);
+}
+
+static void *pci_ipmi_kcs_get_backend_data(IPMIInterface *ii)
+{
+ PCIIPMIKCSDevice *pik = PCI_IPMI_KCS(ii);
+
+ return &pik->kcs;
+}
+
+static void pci_ipmi_kcs_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc);
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc);
+
+ pdc->vendor_id = PCI_VENDOR_ID_QEMU;
+ pdc->device_id = PCI_DEVICE_ID_QEMU_IPMI;
+ pdc->revision = 1;
+ pdc->class_id = PCI_CLASS_SERIAL_IPMI;
+
+ dc->vmsd = &vmstate_PCIIPMIKCSDevice;
+ dc->desc = "PCI IPMI KCS";
+ pdc->realize = pci_ipmi_kcs_realize;
+
+ iic->get_backend_data = pci_ipmi_kcs_get_backend_data;
+ ipmi_kcs_class_init(iic);
+}
+
+static const TypeInfo pci_ipmi_kcs_info = {
+ .name = TYPE_PCI_IPMI_KCS,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PCIIPMIKCSDevice),
+ .instance_init = pci_ipmi_kcs_instance_init,
+ .class_init = pci_ipmi_kcs_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_IPMI_INTERFACE },
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { }
+ }
+};
+
+static void pci_ipmi_kcs_register_types(void)
+{
+ type_register_static(&pci_ipmi_kcs_info);
+}
+
+type_init(pci_ipmi_kcs_register_types)
diff --git a/hw/ipmi/smbus_ipmi.c b/hw/ipmi/smbus_ipmi.c
new file mode 100644
index 0000000000..2a9470d9df
--- /dev/null
+++ b/hw/ipmi/smbus_ipmi.c
@@ -0,0 +1,384 @@
+/*
+ * QEMU IPMI SMBus (SSIF) emulation
+ *
+ * Copyright (c) 2015,2016 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 "qemu/osdep.h"
+#include "migration/vmstate.h"
+#include "hw/i2c/smbus_slave.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "hw/ipmi/ipmi.h"
+
+#define TYPE_SMBUS_IPMI "smbus-ipmi"
+#define SMBUS_IPMI(obj) OBJECT_CHECK(SMBusIPMIDevice, (obj), TYPE_SMBUS_IPMI)
+
+#define SSIF_IPMI_REQUEST 2
+#define SSIF_IPMI_MULTI_PART_REQUEST_START 6
+#define SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE 7
+#define SSIF_IPMI_MULTI_PART_REQUEST_END 8
+#define SSIF_IPMI_RESPONSE 3
+#define SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE 9
+#define SSIF_IPMI_MULTI_PART_RETRY 0xa
+
+#define MAX_SSIF_IPMI_MSG_SIZE 255
+#define MAX_SSIF_IPMI_MSG_CHUNK 32
+
+#define IPMI_GET_SYS_INTF_CAP_CMD 0x57
+
+typedef struct SMBusIPMIDevice {
+ SMBusDevice parent;
+
+ IPMIBmc *bmc;
+
+ uint8_t outmsg[MAX_SSIF_IPMI_MSG_SIZE];
+ uint32_t outlen;
+ uint32_t currblk;
+
+ /* Holds the SMBUS message currently being sent to the host. */
+ uint8_t outbuf[MAX_SSIF_IPMI_MSG_CHUNK + 1]; /* len + message. */
+ uint32_t outpos;
+
+ uint8_t inmsg[MAX_SSIF_IPMI_MSG_SIZE];
+ uint32_t inlen;
+
+ /*
+ * This is a response number that we send with the command to make
+ * sure that the response matches the command.
+ */
+ uint8_t waiting_rsp;
+
+ uint32_t uuid;
+} SMBusIPMIDevice;
+
+static void smbus_ipmi_handle_event(IPMIInterface *ii)
+{
+ /* No interrupts, so nothing to do here. */
+}
+
+static void smbus_ipmi_handle_rsp(IPMIInterface *ii, uint8_t msg_id,
+ unsigned char *rsp, unsigned int rsp_len)
+{
+ SMBusIPMIDevice *sid = SMBUS_IPMI(ii);
+
+ if (sid->waiting_rsp == msg_id) {
+ sid->waiting_rsp++;
+
+ if (rsp_len > MAX_SSIF_IPMI_MSG_SIZE) {
+ rsp[2] = IPMI_CC_REQUEST_DATA_TRUNCATED;
+ rsp_len = MAX_SSIF_IPMI_MSG_SIZE;
+ }
+ memcpy(sid->outmsg, rsp, rsp_len);
+ sid->outlen = rsp_len;
+ sid->outpos = 0;
+ sid->currblk = 0;
+ }
+}
+
+static void smbus_ipmi_set_atn(IPMIInterface *ii, int val, int irq)
+{
+ /* This is where PEC would go. */
+}
+
+static void smbus_ipmi_set_irq_enable(IPMIInterface *ii, int val)
+{
+}
+
+static void smbus_ipmi_send_msg(SMBusIPMIDevice *sid)
+{
+ uint8_t *msg = sid->inmsg;
+ uint32_t len = sid->inlen;
+ IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(sid->bmc);
+
+ sid->outlen = 0;
+ sid->outpos = 0;
+ sid->currblk = 0;
+
+ if (msg[0] == (IPMI_NETFN_APP << 2) && msg[1] == IPMI_GET_SYS_INTF_CAP_CMD)
+ {
+ /* We handle this ourself. */
+ sid->outmsg[0] = (IPMI_NETFN_APP + 1) << 2;
+ sid->outmsg[1] = msg[1];
+ if (len < 3) {
+ sid->outmsg[2] = IPMI_CC_REQUEST_DATA_LENGTH_INVALID;
+ sid->outlen = 3;
+ } else if ((msg[2] & 0x0f) != 0) {
+ sid->outmsg[2] = IPMI_CC_INVALID_DATA_FIELD;
+ sid->outlen = 3;
+ } else {
+ sid->outmsg[2] = 0;
+ sid->outmsg[3] = 0;
+ sid->outmsg[4] = (2 << 6); /* Multi-part supported. */
+ sid->outmsg[5] = MAX_SSIF_IPMI_MSG_SIZE;
+ sid->outmsg[6] = MAX_SSIF_IPMI_MSG_SIZE;
+ sid->outlen = 7;
+ }
+ return;
+ }
+
+ bk->handle_command(sid->bmc, sid->inmsg, sid->inlen, sizeof(sid->inmsg),
+ sid->waiting_rsp);
+}
+
+static uint8_t ipmi_receive_byte(SMBusDevice *dev)
+{
+ SMBusIPMIDevice *sid = SMBUS_IPMI(dev);
+
+ if (sid->outpos >= sizeof(sid->outbuf)) {
+ return 0xff;
+ }
+
+ return sid->outbuf[sid->outpos++];
+}
+
+static int ipmi_load_readbuf(SMBusIPMIDevice *sid)
+{
+ unsigned int block = sid->currblk, pos, len;
+
+ if (sid->outlen == 0) {
+ return -1;
+ }
+
+ if (sid->outlen <= 32) {
+ if (block != 0) {
+ return -1;
+ }
+ sid->outbuf[0] = sid->outlen;
+ memcpy(sid->outbuf + 1, sid->outmsg, sid->outlen);
+ sid->outpos = 0;
+ return 0;
+ }
+
+ if (block == 0) {
+ sid->outbuf[0] = 32;
+ sid->outbuf[1] = 0;
+ sid->outbuf[2] = 1;
+ memcpy(sid->outbuf + 3, sid->outmsg, 30);
+ sid->outpos = 0;
+ return 0;
+ }
+
+ /*
+ * Calculate the position in outmsg. 30 for the first block, 31
+ * for the rest of the blocks.
+ */
+ pos = 30 + (block - 1) * 31;
+
+ if (pos >= sid->outlen) {
+ return -1;
+ }
+
+ len = sid->outlen - pos;
+ if (len > 31) {
+ /* More chunks after this. */
+ len = 31;
+ /* Blocks start at 0 for the first middle transaction. */
+ sid->outbuf[1] = block - 1;
+ } else {
+ sid->outbuf[1] = 0xff; /* End of message marker. */
+ }
+
+ sid->outbuf[0] = len + 1;
+ memcpy(sid->outbuf + 2, sid->outmsg + pos, len);
+ sid->outpos = 0;
+ return 0;
+}
+
+static int ipmi_write_data(SMBusDevice *dev, uint8_t *buf, uint8_t len)
+{
+ SMBusIPMIDevice *sid = SMBUS_IPMI(dev);
+ bool send = false;
+ uint8_t cmd;
+ int ret = 0;
+
+ /* length is guaranteed to be >= 1. */
+ cmd = *buf++;
+ len--;
+
+ /* Handle read request, which don't have any data in the write part. */
+ switch (cmd) {
+ case SSIF_IPMI_RESPONSE:
+ sid->currblk = 0;
+ ret = ipmi_load_readbuf(sid);
+ break;
+
+ case SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE:
+ sid->currblk++;
+ ret = ipmi_load_readbuf(sid);
+ break;
+
+ case SSIF_IPMI_MULTI_PART_RETRY:
+ if (len >= 1) {
+ sid->currblk = buf[0];
+ ret = ipmi_load_readbuf(sid);
+ } else {
+ ret = -1;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /* This should be a message write, make the length is there and correct. */
+ if (len >= 1) {
+ if (*buf != len - 1 || *buf > MAX_SSIF_IPMI_MSG_CHUNK) {
+ return -1; /* Bogus message */
+ }
+ buf++;
+ len--;
+ }
+
+ switch (cmd) {
+ case SSIF_IPMI_REQUEST:
+ send = true;
+ /* FALLTHRU */
+ case SSIF_IPMI_MULTI_PART_REQUEST_START:
+ if (len < 2) {
+ return -1; /* Bogus. */
+ }
+ memcpy(sid->inmsg, buf, len);
+ sid->inlen = len;
+ break;
+
+ case SSIF_IPMI_MULTI_PART_REQUEST_END:
+ send = true;
+ /* FALLTHRU */
+ case SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE:
+ if (!sid->inlen) {
+ return -1; /* Bogus. */
+ }
+ if (sid->inlen + len > MAX_SSIF_IPMI_MSG_SIZE) {
+ sid->inlen = 0; /* Discard the message. */
+ return -1; /* Bogus. */
+ }
+ if (len < 32) {
+ /*
+ * Special hack, a multi-part middle that is less than 32 bytes
+ * marks the end of a message. The specification is fairly
+ * confusing, so some systems to this, even sending a zero
+ * length end message to mark the end.
+ */
+ send = true;
+ }
+ memcpy(sid->inmsg + sid->inlen, buf, len);
+ sid->inlen += len;
+ break;
+ }
+
+ if (send && sid->inlen) {
+ smbus_ipmi_send_msg(sid);
+ }
+
+ return ret;
+}
+
+static const VMStateDescription vmstate_smbus_ipmi = {
+ .name = TYPE_SMBUS_IPMI,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_SMBUS_DEVICE(parent, SMBusIPMIDevice),
+ VMSTATE_UINT8(waiting_rsp, SMBusIPMIDevice),
+ VMSTATE_UINT32(outlen, SMBusIPMIDevice),
+ VMSTATE_UINT32(currblk, SMBusIPMIDevice),
+ VMSTATE_UINT8_ARRAY(outmsg, SMBusIPMIDevice, MAX_SSIF_IPMI_MSG_SIZE),
+ VMSTATE_UINT32(outpos, SMBusIPMIDevice),
+ VMSTATE_UINT8_ARRAY(outbuf, SMBusIPMIDevice,
+ MAX_SSIF_IPMI_MSG_CHUNK + 1),
+ VMSTATE_UINT32(inlen, SMBusIPMIDevice),
+ VMSTATE_UINT8_ARRAY(inmsg, SMBusIPMIDevice, MAX_SSIF_IPMI_MSG_SIZE),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void smbus_ipmi_realize(DeviceState *dev, Error **errp)
+{
+ SMBusIPMIDevice *sid = SMBUS_IPMI(dev);
+ IPMIInterface *ii = IPMI_INTERFACE(dev);
+
+ if (!sid->bmc) {
+ error_setg(errp, "IPMI device requires a bmc attribute to be set");
+ return;
+ }
+
+ sid->uuid = ipmi_next_uuid();
+
+ sid->bmc->intf = ii;
+}
+
+static void smbus_ipmi_init(Object *obj)
+{
+ SMBusIPMIDevice *sid = SMBUS_IPMI(obj);
+
+ ipmi_bmc_find_and_link(OBJECT(obj), (Object **) &sid->bmc);
+}
+
+static void smbus_ipmi_get_fwinfo(struct IPMIInterface *ii, IPMIFwInfo *info)
+{
+ SMBusIPMIDevice *sid = SMBUS_IPMI(ii);
+
+ info->interface_name = "smbus";
+ info->interface_type = IPMI_SMBIOS_SSIF;
+ info->ipmi_spec_major_revision = 2;
+ info->ipmi_spec_minor_revision = 0;
+ info->i2c_slave_address = sid->bmc->slave_addr;
+ info->base_address = sid->parent.i2c.address;
+ info->memspace = IPMI_MEMSPACE_SMBUS;
+ info->register_spacing = 1;
+ info->uuid = sid->uuid;
+}
+
+static void smbus_ipmi_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc);
+ SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(oc);
+
+ sc->receive_byte = ipmi_receive_byte;
+ sc->write_data = ipmi_write_data;
+ dc->vmsd = &vmstate_smbus_ipmi;
+ dc->realize = smbus_ipmi_realize;
+ iic->set_atn = smbus_ipmi_set_atn;
+ iic->handle_rsp = smbus_ipmi_handle_rsp;
+ iic->handle_if_event = smbus_ipmi_handle_event;
+ iic->set_irq_enable = smbus_ipmi_set_irq_enable;
+ iic->get_fwinfo = smbus_ipmi_get_fwinfo;
+}
+
+static const TypeInfo smbus_ipmi_info = {
+ .name = TYPE_SMBUS_IPMI,
+ .parent = TYPE_SMBUS_DEVICE,
+ .instance_size = sizeof(SMBusIPMIDevice),
+ .instance_init = smbus_ipmi_init,
+ .class_init = smbus_ipmi_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_IPMI_INTERFACE },
+ { }
+ }
+};
+
+static void smbus_ipmi_register_types(void)
+{
+ type_register_static(&smbus_ipmi_info);
+}
+
+type_init(smbus_ipmi_register_types)
diff --git a/hw/smbios/smbios_type_38.c b/hw/smbios/smbios_type_38.c
index 0c08f282de..168b886647 100644
--- a/hw/smbios/smbios_type_38.c
+++ b/hw/smbios/smbios_type_38.c
@@ -94,6 +94,9 @@ static void smbios_add_ipmi_devices(BusState *bus)
ii = IPMI_INTERFACE(obj);
iic = IPMI_INTERFACE_GET_CLASS(obj);
memset(&info, 0, sizeof(info));
+ if (!iic->get_fwinfo) {
+ continue;
+ }
iic->get_fwinfo(ii, &info);
smbios_build_one_type_38(&info);
continue;