aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--default-configs/i386-softmmu.mak3
-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/intc/s390_flic_kvm.c6
-rw-r--r--hw/intc/trace-events1
-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
-rw-r--r--include/hw/acpi/aml-build.h18
-rw-r--r--include/hw/acpi/ipmi.h2
-rw-r--r--include/hw/i386/pc.h2
-rw-r--r--include/hw/ipmi/ipmi.h7
-rw-r--r--include/hw/ipmi/ipmi_bt.h73
-rw-r--r--include/hw/ipmi/ipmi_kcs.h76
-rw-r--r--include/hw/pci/pci.h1
-rw-r--r--include/hw/qdev-properties.h7
-rw-r--r--pc-bios/s390-ccw/main.c2
-rw-r--r--pc-bios/s390-ccw/netmain.c1
-rw-r--r--pc-bios/s390-netboot.imgbin67232 -> 67232 bytes
-rw-r--r--qemu-options.hx10
-rw-r--r--target/s390x/cpu.h4
-rw-r--r--target/s390x/cpu_models.c2
-rw-r--r--target/s390x/helper.h2
-rw-r--r--target/s390x/insn-data.def2
-rw-r--r--target/s390x/kvm.c7
-rw-r--r--target/s390x/mem_helper.c749
-rw-r--r--target/s390x/translate.c12
-rw-r--r--tests/Makefile.include3
-rw-r--r--tests/acceptance/linux_ssh_mips_malta.py159
-rw-r--r--tests/acceptance/machine_m68k_nextcube.py2
-rw-r--r--tests/acceptance/x86_cpu_model_versions.py22
-rw-r--r--tests/data/acpi/q35/DSDTbin7841 -> 7879 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.bridgebin7858 -> 7896 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.cphpbin8304 -> 8342 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.dimmpxmbin9494 -> 9532 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.ipmibtbin7916 -> 7954 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.memhpbin9200 -> 9238 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.mmio64bin8971 -> 9009 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.numamembin7847 -> 7885 bytes
-rw-r--r--tests/ipmi-bt-test.c6
-rw-r--r--tests/requirements.txt3
-rw-r--r--tests/tcg/s390x/Makefile.target2
-rw-r--r--tests/tcg/s390x/mvc.c109
-rw-r--r--tests/tcg/s390x/mvo.c25
58 files changed, 2702 insertions, 1169 deletions
diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index ba3fb3ff50..4229900f57 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -10,6 +10,9 @@
#CONFIG_ISA_DEBUG=n
#CONFIG_ISA_IPMI_BT=n
#CONFIG_ISA_IPMI_KCS=n
+#CONFIG_PCI_IPMI_KCS=n
+#CONFIG_PCI_IPMI_BT=n
+#CONFIG_IPMI_SSIF=n
#CONFIG_PCI_DEVICES=n
#CONFIG_PVPANIC=n
#CONFIG_QXL=n
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/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c
index 819aa5e198..cedccba8a9 100644
--- a/hw/intc/s390_flic_kvm.c
+++ b/hw/intc/s390_flic_kvm.c
@@ -589,12 +589,6 @@ static void kvm_s390_flic_realize(DeviceState *dev, Error **errp)
goto fail;
}
flic_state->fd = -1;
- if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) {
- error_setg_errno(&errp_local, errno, "KVM is missing capability"
- " KVM_CAP_DEVICE_CTRL");
- trace_flic_no_device_api(errno);
- goto fail;
- }
cd.type = KVM_DEV_TYPE_FLIC;
ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd);
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index 90c9d07c1a..719f46b516 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -75,7 +75,6 @@ xics_ics_simple_eoi(int nr) "ics_eoi: irq 0x%x"
# s390_flic_kvm.c
flic_create_device(int err) "flic: create device failed %d"
-flic_no_device_api(int err) "flic: no Device Contral API support %d"
flic_reset_failed(int err) "flic: reset failed %d"
# s390_flic.c
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;
diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h
index 991cf05134..de4a406568 100644
--- a/include/hw/acpi/aml-build.h
+++ b/include/hw/acpi/aml-build.h
@@ -223,6 +223,23 @@ struct AcpiBuildTables {
BIOSLinker *linker;
} AcpiBuildTables;
+/*
+ * ACPI 5.0: 6.4.3.8.2 Serial Bus Connection Descriptors
+ * Serial Bus Type
+ */
+#define AML_SERIAL_BUS_TYPE_I2C 1
+#define AML_SERIAL_BUS_TYPE_SPI 2
+#define AML_SERIAL_BUS_TYPE_UART 3
+
+/*
+ * ACPI 5.0: 6.4.3.8.2 Serial Bus Connection Descriptors
+ * General Flags
+ */
+/* Slave Mode */
+#define AML_SERIAL_BUS_FLAG_MASTER_DEVICE (1 << 0)
+/* Consumer/Producer */
+#define AML_SERIAL_BUS_FLAG_CONSUME_ONLY (1 << 1)
+
/**
* init_aml_allocator:
*
@@ -347,6 +364,7 @@ Aml *aml_qword_memory(AmlDecode dec, AmlMinFixed min_fixed,
Aml *aml_dma(AmlDmaType typ, AmlDmaBusMaster bm, AmlTransferSize sz,
uint8_t channel);
Aml *aml_sleep(uint64_t msec);
+Aml *aml_i2c_serial_bus_device(uint16_t address, const char *resource_source);
/* Block AML object primitives */
Aml *aml_scope(const char *name_format, ...) GCC_FMT_ATTR(1, 2);
diff --git a/include/hw/acpi/ipmi.h b/include/hw/acpi/ipmi.h
index c38483565c..c14ad682ac 100644
--- a/include/hw/acpi/ipmi.h
+++ b/include/hw/acpi/ipmi.h
@@ -16,6 +16,6 @@
* bus matches the given bus. The resource is the ACPI resource that
* contains the IPMI device, this is required for the I2C CRS.
*/
-void build_acpi_ipmi_devices(Aml *table, BusState *bus);
+void build_acpi_ipmi_devices(Aml *table, BusState *bus, const char *resource);
#endif /* HW_ACPI_IPMI_H */
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 062feeb69e..6df4f4b6fb 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -38,6 +38,7 @@ struct PCMachineState {
HotplugHandler *acpi_dev;
ISADevice *rtc;
PCIBus *bus;
+ I2CBus *smbus;
FWCfgState *fw_cfg;
qemu_irq *gsi;
PFlashCFI01 *flash[2];
@@ -117,6 +118,7 @@ typedef struct PCMachineClass {
bool rsdp_in_ram;
int legacy_acpi_table_size;
unsigned acpi_data_size;
+ bool do_not_add_smb_acpi;
/* SMBIOS compat: */
bool smbios_defaults;
diff --git a/include/hw/ipmi/ipmi.h b/include/hw/ipmi/ipmi.h
index 70871da0a7..6f2413b39b 100644
--- a/include/hw/ipmi/ipmi.h
+++ b/include/hw/ipmi/ipmi.h
@@ -118,7 +118,12 @@ typedef struct IPMIInterface IPMIInterface;
typedef struct IPMIInterfaceClass {
InterfaceClass parent;
- void (*init)(struct IPMIInterface *s, Error **errp);
+ /*
+ * min_size is the requested I/O size and must be a power of 2.
+ * This is so PCI (or other busses) can request a bigger range.
+ * Use 0 for the default.
+ */
+ void (*init)(struct IPMIInterface *s, unsigned int min_size, Error **errp);
/*
* Perform various operations on the hardware. If checkonly is
diff --git a/include/hw/ipmi/ipmi_bt.h b/include/hw/ipmi/ipmi_bt.h
new file mode 100644
index 0000000000..8a4316ea7c
--- /dev/null
+++ b/include/hw/ipmi/ipmi_bt.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#ifndef HW_IPMI_BT_H
+#define HW_IPMI_BT_H
+
+#include "hw/ipmi/ipmi.h"
+
+typedef struct IPMIBT {
+ IPMIBmc *bmc;
+
+ bool do_wake;
+
+ bool obf_irq_set;
+ bool atn_irq_set;
+ 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;
+
+ uint32_t io_base;
+ unsigned long io_length;
+ MemoryRegion io;
+ unsigned long size_mask;
+
+ void (*raise_irq)(struct IPMIBT *ib);
+ void (*lower_irq)(struct IPMIBT *ib);
+ void *opaque;
+
+ bool use_irq;
+} IPMIBT;
+
+void ipmi_bt_get_fwinfo(IPMIBT *ik, IPMIFwInfo *info);
+void ipmi_bt_class_init(IPMIInterfaceClass *iic);
+extern const VMStateDescription vmstate_IPMIBT;
+int ipmi_bt_vmstate_post_load(void *opaque, int version);
+
+#endif /* HW_IPMI_BT_H */
diff --git a/include/hw/ipmi/ipmi_kcs.h b/include/hw/ipmi/ipmi_kcs.h
new file mode 100644
index 0000000000..6e6ef4c539
--- /dev/null
+++ b/include/hw/ipmi/ipmi_kcs.h
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#ifndef HW_IPMI_KCS_H
+#define HW_IPMI_KCS_H
+
+#include "hw/ipmi/ipmi.h"
+
+typedef struct IPMIKCS {
+ IPMIBmc *bmc;
+
+ bool do_wake;
+
+ bool obf_irq_set;
+ bool atn_irq_set;
+ 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;
+
+ uint32_t io_base;
+ unsigned long io_length;
+ MemoryRegion io;
+ unsigned long size_mask;
+
+ void (*raise_irq)(struct IPMIKCS *ik);
+ void (*lower_irq)(struct IPMIKCS *ik);
+ void *opaque;
+
+ bool use_irq;
+} IPMIKCS;
+
+void ipmi_kcs_get_fwinfo(IPMIKCS *ik, IPMIFwInfo *info);
+void ipmi_kcs_class_init(IPMIInterfaceClass *iic);
+extern const VMStateDescription vmstate_IPMIKCS;
+int ipmi_kcs_vmstate_post_load(void *opaque, int version);
+
+#endif /* HW_IPMI_KCS_H */
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index 1b840e61a2..f3f0ffd5fb 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -55,6 +55,7 @@ extern bool pci_available;
/* QEMU/Bochs VGA (0x1234) */
#define PCI_VENDOR_ID_QEMU 0x1234
#define PCI_DEVICE_ID_QEMU_VGA 0x1111
+#define PCI_DEVICE_ID_QEMU_IPMI 0x1112
/* VMWare (0x15ad) */
#define PCI_VENDOR_ID_VMWARE 0x15ad
diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h
index 2e98dd60db..c6a8cb5516 100644
--- a/include/hw/qdev-properties.h
+++ b/include/hw/qdev-properties.h
@@ -238,6 +238,13 @@ extern const PropertyInfo qdev_prop_pcie_link_width;
#define DEFINE_PROP_AUDIODEV(_n, _s, _f) \
DEFINE_PROP(_n, _s, _f, qdev_prop_audiodev, QEMUSoundCard)
+#define DEFINE_PROP_UUID_NODEFAULT(_name, _state, _field) { \
+ .name = (_name), \
+ .info = &qdev_prop_uuid, \
+ .offset = offsetof(_state, _field) \
+ + type_check(QemuUUID, typeof_field(_state, _field)), \
+ }
+
#define DEFINE_PROP_END_OF_LIST() \
{}
diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
index a69c73349e..a21b386280 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -17,7 +17,7 @@
char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE)));
static SubChannelId blk_schid = { .one = 1 };
-static char loadparm_str[LOADPARM_LEN + 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+static char loadparm_str[LOADPARM_LEN + 1];
QemuIplParameters qipl;
IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
static bool have_iplb;
diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c
index f3542cb2cf..f2dcc01e27 100644
--- a/pc-bios/s390-ccw/netmain.c
+++ b/pc-bios/s390-ccw/netmain.c
@@ -269,6 +269,7 @@ static const char *get_uuid(void)
: "d" (r0), "d" (r1), [addr] "a" (buf)
: "cc", "memory");
if (cc) {
+ free(mem);
return NULL;
}
diff --git a/pc-bios/s390-netboot.img b/pc-bios/s390-netboot.img
index aa90fbccb1..b984ad0da0 100644
--- a/pc-bios/s390-netboot.img
+++ b/pc-bios/s390-netboot.img
Binary files differ
diff --git a/qemu-options.hx b/qemu-options.hx
index 565d7af8c2..2a04ca6ac5 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -701,7 +701,7 @@ 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}][,sdrfile=@var{file}][,furareasize=@var{val}][,furdatafile=@var{file}]
+@item -device ipmi-bmc-sim,id=@var{id}[,slave_addr=@var{val}][,sdrfile=@var{file}][,furareasize=@var{val}][,furdatafile=@var{file}][,guid=@var{uuid}]
Add an IPMI BMC. This is a simulation of a hardware management
interface processor that normally sits on a system. It provides
@@ -714,8 +714,8 @@ controllers. If you don't know what this means, it is safe to ignore
it.
@table @option
-@item bmc=@var{id}
-The BMC to connect to, one of ipmi-bmc-sim or ipmi-bmc-extern above.
+@item id=@var{id}
+The BMC id for interfaces to use this device.
@item slave_addr=@var{val}
Define slave address to use for the BMC. The default is 0x20.
@item sdrfile=@var{file}
@@ -724,6 +724,10 @@ file containing raw Sensor Data Records (SDR) data. The default is none.
size of a Field Replaceable Unit (FRU) area. The default is 1024.
@item frudatafile=@var{file}
file containing raw Field Replaceable Unit (FRU) inventory data. The default is none.
+@item guid=@var{uuid}
+value for the GUID for the BMC, in standard UUID format. If this is set,
+get "Get GUID" command to the BMC will return it. Otherwise "Get GUID"
+will return an error.
@end table
@item -device ipmi-bmc-extern,id=@var{id},chardev=@var{id}[,slave_addr=@var{val}]
diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h
index 79202c0980..163dae13d7 100644
--- a/target/s390x/cpu.h
+++ b/target/s390x/cpu.h
@@ -328,6 +328,9 @@ extern const VMStateDescription vmstate_s390_cpu;
static inline int cpu_mmu_index(CPUS390XState *env, bool ifetch)
{
+#ifdef CONFIG_USER_ONLY
+ return MMU_USER_IDX;
+#else
if (!(env->psw.mask & PSW_MASK_DAT)) {
return MMU_REAL_IDX;
}
@@ -351,6 +354,7 @@ static inline int cpu_mmu_index(CPUS390XState *env, bool ifetch)
default:
abort();
}
+#endif
}
static inline void cpu_get_tb_cpu_state(CPUS390XState* env, target_ulong *pc,
diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
index 1d16d7d5e7..009afc38b9 100644
--- a/target/s390x/cpu_models.c
+++ b/target/s390x/cpu_models.c
@@ -84,7 +84,7 @@ static S390CPUDef s390_cpu_defs[] = {
CPUDEF_INIT(0x3906, 14, 1, 47, 0x08000000U, "z14", "IBM z14 GA1"),
CPUDEF_INIT(0x3906, 14, 2, 47, 0x08000000U, "z14.2", "IBM z14 GA2"),
CPUDEF_INIT(0x3907, 14, 1, 47, 0x08000000U, "z14ZR1", "IBM z14 Model ZR1 GA1"),
- CPUDEF_INIT(0x8561, 15, 1, 47, 0x08000000U, "gen15a", "IBM 8561 GA1"),
+ CPUDEF_INIT(0x8561, 15, 1, 47, 0x08000000U, "gen15a", "IBM z15 GA1"),
CPUDEF_INIT(0x8562, 15, 1, 47, 0x08000000U, "gen15b", "IBM 8562 GA1"),
};
diff --git a/target/s390x/helper.h b/target/s390x/helper.h
index e9aff83b05..56e8149866 100644
--- a/target/s390x/helper.h
+++ b/target/s390x/helper.h
@@ -20,7 +20,7 @@ DEF_HELPER_FLAGS_4(mvn, TCG_CALL_NO_WG, void, env, i32, i64, i64)
DEF_HELPER_FLAGS_4(mvo, TCG_CALL_NO_WG, void, env, i32, i64, i64)
DEF_HELPER_FLAGS_4(mvpg, TCG_CALL_NO_WG, i32, env, i64, i64, i64)
DEF_HELPER_FLAGS_4(mvz, TCG_CALL_NO_WG, void, env, i32, i64, i64)
-DEF_HELPER_4(mvst, i64, env, i64, i64, i64)
+DEF_HELPER_3(mvst, i32, env, i32, i32)
DEF_HELPER_4(ex, void, env, i32, i64, i64)
DEF_HELPER_FLAGS_4(stam, TCG_CALL_NO_WG, void, env, i32, i64, i32)
DEF_HELPER_FLAGS_4(lam, TCG_CALL_NO_WG, void, env, i32, i64, i32)
diff --git a/target/s390x/insn-data.def b/target/s390x/insn-data.def
index f421184fcd..449eee1662 100644
--- a/target/s390x/insn-data.def
+++ b/target/s390x/insn-data.def
@@ -637,7 +637,7 @@
/* MOVE PAGE */
C(0xb254, MVPG, RRE, Z, r1_o, r2_o, 0, 0, mvpg, 0)
/* MOVE STRING */
- C(0xb255, MVST, RRE, Z, r1_o, r2_o, 0, 0, mvst, 0)
+ C(0xb255, MVST, RRE, Z, 0, 0, 0, 0, mvst, 0)
/* MOVE WITH OPTIONAL SPECIFICATION */
C(0xc800, MVCOS, SSF, MVCOS, la1, a2, 0, 0, mvcos, 0)
/* MOVE WITH OFFSET */
diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c
index cea71ac7c3..97a662ad0e 100644
--- a/target/s390x/kvm.c
+++ b/target/s390x/kvm.c
@@ -316,6 +316,13 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
MachineClass *mc = MACHINE_GET_CLASS(ms);
mc->default_cpu_type = S390_CPU_TYPE_NAME("host");
+
+ if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) {
+ error_report("KVM is missing capability KVM_CAP_DEVICE_CTRL - "
+ "please use kernel 3.15 or newer");
+ return -1;
+ }
+
cap_sync_regs = kvm_check_extension(s, KVM_CAP_SYNC_REGS);
cap_async_pf = kvm_check_extension(s, KVM_CAP_ASYNC_PF);
cap_mem_op = kvm_check_extension(s, KVM_CAP_S390_MEM_OP);
diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c
index 29fcce426e..44e535856d 100644
--- a/target/s390x/mem_helper.c
+++ b/target/s390x/mem_helper.c
@@ -52,15 +52,17 @@ static inline bool psw_key_valid(CPUS390XState *env, uint8_t psw_key)
return true;
}
-/* Reduce the length so that addr + len doesn't cross a page boundary. */
-static inline uint32_t adj_len_to_page(uint32_t len, uint64_t addr)
+static bool is_destructive_overlap(CPUS390XState *env, uint64_t dest,
+ uint64_t src, uint32_t len)
{
-#ifndef CONFIG_USER_ONLY
- if ((addr & ~TARGET_PAGE_MASK) + len - 1 >= TARGET_PAGE_SIZE) {
- return -(addr | TARGET_PAGE_MASK);
+ if (!len || src == dest) {
+ return false;
}
-#endif
- return len;
+ /* Take care of wrapping at the end of address space. */
+ if (unlikely(wrap_address(env, src + len - 1) < src)) {
+ return dest > src || dest <= wrap_address(env, src + len - 1);
+ }
+ return dest > src && dest <= src + len - 1;
}
/* Trigger a SPECIFICATION exception if an address or a length is not
@@ -104,62 +106,207 @@ static inline void cpu_stsize_data_ra(CPUS390XState *env, uint64_t addr,
}
}
-static void fast_memset(CPUS390XState *env, uint64_t dest, uint8_t byte,
- uint32_t l, uintptr_t ra)
+/* An access covers at most 4096 bytes and therefore at most two pages. */
+typedef struct S390Access {
+ target_ulong vaddr1;
+ target_ulong vaddr2;
+ char *haddr1;
+ char *haddr2;
+ uint16_t size1;
+ uint16_t size2;
+ /*
+ * If we can't access the host page directly, we'll have to do I/O access
+ * via ld/st helpers. These are internal details, so we store the
+ * mmu idx to do the access here instead of passing it around in the
+ * helpers. Maybe, one day we can get rid of ld/st access - once we can
+ * handle TLB_NOTDIRTY differently. We don't expect these special accesses
+ * to trigger exceptions - only if we would have TLB_NOTDIRTY on LAP
+ * pages, we might trigger a new MMU translation - very unlikely that
+ * the mapping changes in between and we would trigger a fault.
+ */
+ int mmu_idx;
+} S390Access;
+
+static S390Access access_prepare(CPUS390XState *env, vaddr vaddr, int size,
+ MMUAccessType access_type, int mmu_idx,
+ uintptr_t ra)
{
- int mmu_idx = cpu_mmu_index(env, false);
+ S390Access access = {
+ .vaddr1 = vaddr,
+ .size1 = MIN(size, -(vaddr | TARGET_PAGE_MASK)),
+ .mmu_idx = mmu_idx,
+ };
- while (l > 0) {
- void *p = tlb_vaddr_to_host(env, dest, MMU_DATA_STORE, mmu_idx);
- if (p) {
- /* Access to the whole page in write mode granted. */
- uint32_t l_adj = adj_len_to_page(l, dest);
- memset(p, byte, l_adj);
- dest += l_adj;
- l -= l_adj;
- } else {
- /* We failed to get access to the whole page. The next write
- access will likely fill the QEMU TLB for the next iteration. */
- cpu_stb_data_ra(env, dest, byte, ra);
- dest++;
- l--;
- }
+ g_assert(size > 0 && size <= 4096);
+ access.haddr1 = probe_access(env, access.vaddr1, access.size1, access_type,
+ mmu_idx, ra);
+
+ if (unlikely(access.size1 != size)) {
+ /* The access crosses page boundaries. */
+ access.vaddr2 = wrap_address(env, vaddr + access.size1);
+ access.size2 = size - access.size1;
+ access.haddr2 = probe_access(env, access.vaddr2, access.size2,
+ access_type, mmu_idx, ra);
}
+ return access;
}
-#ifndef CONFIG_USER_ONLY
-static void fast_memmove_idx(CPUS390XState *env, uint64_t dest, uint64_t src,
- uint32_t len, int dest_idx, int src_idx,
+/* Helper to handle memset on a single page. */
+static void do_access_memset(CPUS390XState *env, vaddr vaddr, char *haddr,
+ uint8_t byte, uint16_t size, int mmu_idx,
uintptr_t ra)
{
- TCGMemOpIdx oi_dest = make_memop_idx(MO_UB, dest_idx);
- TCGMemOpIdx oi_src = make_memop_idx(MO_UB, src_idx);
- uint32_t len_adj;
- void *src_p;
- void *dest_p;
- uint8_t x;
-
- while (len > 0) {
- src = wrap_address(env, src);
- dest = wrap_address(env, dest);
- src_p = tlb_vaddr_to_host(env, src, MMU_DATA_LOAD, src_idx);
- dest_p = tlb_vaddr_to_host(env, dest, MMU_DATA_STORE, dest_idx);
-
- if (src_p && dest_p) {
- /* Access to both whole pages granted. */
- len_adj = adj_len_to_page(adj_len_to_page(len, src), dest);
- memmove(dest_p, src_p, len_adj);
+#ifdef CONFIG_USER_ONLY
+ g_assert(haddr);
+ memset(haddr, byte, size);
+#else
+ TCGMemOpIdx oi = make_memop_idx(MO_UB, mmu_idx);
+ int i;
+
+ if (likely(haddr)) {
+ memset(haddr, byte, size);
+ } else {
+ /*
+ * Do a single access and test if we can then get access to the
+ * page. This is especially relevant to speed up TLB_NOTDIRTY.
+ */
+ g_assert(size > 0);
+ helper_ret_stb_mmu(env, vaddr, byte, oi, ra);
+ haddr = tlb_vaddr_to_host(env, vaddr, MMU_DATA_STORE, mmu_idx);
+ if (likely(haddr)) {
+ memset(haddr + 1, byte, size - 1);
} else {
- /* We failed to get access to one or both whole pages. The next
- read or write access will likely fill the QEMU TLB for the
- next iteration. */
- len_adj = 1;
- x = helper_ret_ldub_mmu(env, src, oi_src, ra);
- helper_ret_stb_mmu(env, dest, x, oi_dest, ra);
+ for (i = 1; i < size; i++) {
+ helper_ret_stb_mmu(env, vaddr + i, byte, oi, ra);
+ }
+ }
+ }
+#endif
+}
+
+static void access_memset(CPUS390XState *env, S390Access *desta,
+ uint8_t byte, uintptr_t ra)
+{
+
+ do_access_memset(env, desta->vaddr1, desta->haddr1, byte, desta->size1,
+ desta->mmu_idx, ra);
+ if (likely(!desta->size2)) {
+ return;
+ }
+ do_access_memset(env, desta->vaddr2, desta->haddr2, byte, desta->size2,
+ desta->mmu_idx, ra);
+}
+
+static uint8_t do_access_get_byte(CPUS390XState *env, vaddr vaddr, char **haddr,
+ int offset, int mmu_idx, uintptr_t ra)
+{
+#ifdef CONFIG_USER_ONLY
+ return ldub_p(*haddr + offset);
+#else
+ TCGMemOpIdx oi = make_memop_idx(MO_UB, mmu_idx);
+ uint8_t byte;
+
+ if (likely(*haddr)) {
+ return ldub_p(*haddr + offset);
+ }
+ /*
+ * Do a single access and test if we can then get access to the
+ * page. This is especially relevant to speed up TLB_NOTDIRTY.
+ */
+ byte = helper_ret_ldub_mmu(env, vaddr + offset, oi, ra);
+ *haddr = tlb_vaddr_to_host(env, vaddr, MMU_DATA_LOAD, mmu_idx);
+ return byte;
+#endif
+}
+
+static uint8_t access_get_byte(CPUS390XState *env, S390Access *access,
+ int offset, uintptr_t ra)
+{
+ if (offset < access->size1) {
+ return do_access_get_byte(env, access->vaddr1, &access->haddr1,
+ offset, access->mmu_idx, ra);
+ }
+ return do_access_get_byte(env, access->vaddr2, &access->haddr2,
+ offset - access->size1, access->mmu_idx, ra);
+}
+
+static void do_access_set_byte(CPUS390XState *env, vaddr vaddr, char **haddr,
+ int offset, uint8_t byte, int mmu_idx,
+ uintptr_t ra)
+{
+#ifdef CONFIG_USER_ONLY
+ stb_p(*haddr + offset, byte);
+#else
+ TCGMemOpIdx oi = make_memop_idx(MO_UB, mmu_idx);
+
+ if (likely(*haddr)) {
+ stb_p(*haddr + offset, byte);
+ return;
+ }
+ /*
+ * Do a single access and test if we can then get access to the
+ * page. This is especially relevant to speed up TLB_NOTDIRTY.
+ */
+ helper_ret_stb_mmu(env, vaddr + offset, byte, oi, ra);
+ *haddr = tlb_vaddr_to_host(env, vaddr, MMU_DATA_STORE, mmu_idx);
+#endif
+}
+
+static void access_set_byte(CPUS390XState *env, S390Access *access,
+ int offset, uint8_t byte, uintptr_t ra)
+{
+ if (offset < access->size1) {
+ do_access_set_byte(env, access->vaddr1, &access->haddr1, offset, byte,
+ access->mmu_idx, ra);
+ } else {
+ do_access_set_byte(env, access->vaddr2, &access->haddr2,
+ offset - access->size1, byte, access->mmu_idx, ra);
+ }
+}
+
+/*
+ * Move data with the same semantics as memmove() in case ranges don't overlap
+ * or src > dest. Undefined behavior on destructive overlaps.
+ */
+static void access_memmove(CPUS390XState *env, S390Access *desta,
+ S390Access *srca, uintptr_t ra)
+{
+ int diff;
+
+ g_assert(desta->size1 + desta->size2 == srca->size1 + srca->size2);
+
+ /* Fallback to slow access in case we don't have access to all host pages */
+ if (unlikely(!desta->haddr1 || (desta->size2 && !desta->haddr2) ||
+ !srca->haddr1 || (srca->size2 && !srca->haddr2))) {
+ int i;
+
+ for (i = 0; i < desta->size1 + desta->size2; i++) {
+ uint8_t byte = access_get_byte(env, srca, i, ra);
+
+ access_set_byte(env, desta, i, byte, ra);
+ }
+ return;
+ }
+
+ if (srca->size1 == desta->size1) {
+ memmove(desta->haddr1, srca->haddr1, srca->size1);
+ if (unlikely(srca->size2)) {
+ memmove(desta->haddr2, srca->haddr2, srca->size2);
+ }
+ } else if (srca->size1 < desta->size1) {
+ diff = desta->size1 - srca->size1;
+ memmove(desta->haddr1, srca->haddr1, srca->size1);
+ memmove(desta->haddr1 + srca->size1, srca->haddr2, diff);
+ if (likely(desta->size2)) {
+ memmove(desta->haddr2, srca->haddr2 + diff, desta->size2);
+ }
+ } else {
+ diff = srca->size1 - desta->size1;
+ memmove(desta->haddr1, srca->haddr1, desta->size1);
+ memmove(desta->haddr2, srca->haddr1 + desta->size1, diff);
+ if (likely(srca->size2)) {
+ memmove(desta->haddr2 + diff, srca->haddr2, srca->size2);
}
- src += len_adj;
- dest += len_adj;
- len -= len_adj;
}
}
@@ -178,60 +325,30 @@ static int mmu_idx_from_as(uint8_t as)
}
}
-static void fast_memmove_as(CPUS390XState *env, uint64_t dest, uint64_t src,
- uint32_t len, uint8_t dest_as, uint8_t src_as,
- uintptr_t ra)
-{
- int src_idx = mmu_idx_from_as(src_as);
- int dest_idx = mmu_idx_from_as(dest_as);
-
- fast_memmove_idx(env, dest, src, len, dest_idx, src_idx, ra);
-}
-#endif
-
-static void fast_memmove(CPUS390XState *env, uint64_t dest, uint64_t src,
- uint32_t l, uintptr_t ra)
-{
- int mmu_idx = cpu_mmu_index(env, false);
-
- while (l > 0) {
- void *src_p = tlb_vaddr_to_host(env, src, MMU_DATA_LOAD, mmu_idx);
- void *dest_p = tlb_vaddr_to_host(env, dest, MMU_DATA_STORE, mmu_idx);
- if (src_p && dest_p) {
- /* Access to both whole pages granted. */
- uint32_t l_adj = adj_len_to_page(l, src);
- l_adj = adj_len_to_page(l_adj, dest);
- memmove(dest_p, src_p, l_adj);
- src += l_adj;
- dest += l_adj;
- l -= l_adj;
- } else {
- /* We failed to get access to one or both whole pages. The next
- read or write access will likely fill the QEMU TLB for the
- next iteration. */
- cpu_stb_data_ra(env, dest, cpu_ldub_data_ra(env, src, ra), ra);
- src++;
- dest++;
- l--;
- }
- }
-}
-
/* and on array */
static uint32_t do_helper_nc(CPUS390XState *env, uint32_t l, uint64_t dest,
uint64_t src, uintptr_t ra)
{
+ const int mmu_idx = cpu_mmu_index(env, false);
+ S390Access srca1, srca2, desta;
uint32_t i;
uint8_t c = 0;
HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
__func__, l, dest, src);
- for (i = 0; i <= l; i++) {
- uint8_t x = cpu_ldub_data_ra(env, src + i, ra);
- x &= cpu_ldub_data_ra(env, dest + i, ra);
+ /* NC always processes one more byte than specified - maximum is 256 */
+ l++;
+
+ srca1 = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra);
+ srca2 = access_prepare(env, dest, l, MMU_DATA_LOAD, mmu_idx, ra);
+ desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra);
+ for (i = 0; i < l; i++) {
+ const uint8_t x = access_get_byte(env, &srca1, i, ra) &
+ access_get_byte(env, &srca2, i, ra);
+
c |= x;
- cpu_stb_data_ra(env, dest + i, x, ra);
+ access_set_byte(env, &desta, i, x, ra);
}
return c != 0;
}
@@ -246,23 +363,33 @@ uint32_t HELPER(nc)(CPUS390XState *env, uint32_t l, uint64_t dest,
static uint32_t do_helper_xc(CPUS390XState *env, uint32_t l, uint64_t dest,
uint64_t src, uintptr_t ra)
{
+ const int mmu_idx = cpu_mmu_index(env, false);
+ S390Access srca1, srca2, desta;
uint32_t i;
uint8_t c = 0;
HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
__func__, l, dest, src);
+ /* XC always processes one more byte than specified - maximum is 256 */
+ l++;
+
+ srca1 = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra);
+ srca2 = access_prepare(env, dest, l, MMU_DATA_LOAD, mmu_idx, ra);
+ desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra);
+
/* xor with itself is the same as memset(0) */
if (src == dest) {
- fast_memset(env, dest, 0, l + 1, ra);
+ access_memset(env, &desta, 0, ra);
return 0;
}
- for (i = 0; i <= l; i++) {
- uint8_t x = cpu_ldub_data_ra(env, src + i, ra);
- x ^= cpu_ldub_data_ra(env, dest + i, ra);
+ for (i = 0; i < l; i++) {
+ const uint8_t x = access_get_byte(env, &srca1, i, ra) ^
+ access_get_byte(env, &srca2, i, ra);
+
c |= x;
- cpu_stb_data_ra(env, dest + i, x, ra);
+ access_set_byte(env, &desta, i, x, ra);
}
return c != 0;
}
@@ -277,17 +404,26 @@ uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest,
static uint32_t do_helper_oc(CPUS390XState *env, uint32_t l, uint64_t dest,
uint64_t src, uintptr_t ra)
{
+ const int mmu_idx = cpu_mmu_index(env, false);
+ S390Access srca1, srca2, desta;
uint32_t i;
uint8_t c = 0;
HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
__func__, l, dest, src);
- for (i = 0; i <= l; i++) {
- uint8_t x = cpu_ldub_data_ra(env, src + i, ra);
- x |= cpu_ldub_data_ra(env, dest + i, ra);
+ /* OC always processes one more byte than specified - maximum is 256 */
+ l++;
+
+ srca1 = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra);
+ srca2 = access_prepare(env, dest, l, MMU_DATA_LOAD, mmu_idx, ra);
+ desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra);
+ for (i = 0; i < l; i++) {
+ const uint8_t x = access_get_byte(env, &srca1, i, ra) |
+ access_get_byte(env, &srca2, i, ra);
+
c |= x;
- cpu_stb_data_ra(env, dest + i, x, ra);
+ access_set_byte(env, &desta, i, x, ra);
}
return c != 0;
}
@@ -302,23 +438,33 @@ uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest,
static uint32_t do_helper_mvc(CPUS390XState *env, uint32_t l, uint64_t dest,
uint64_t src, uintptr_t ra)
{
+ const int mmu_idx = cpu_mmu_index(env, false);
+ S390Access srca, desta;
uint32_t i;
HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
__func__, l, dest, src);
- /* mvc and memmove do not behave the same when areas overlap! */
- /* mvc with source pointing to the byte after the destination is the
- same as memset with the first source byte */
+ /* MVC always copies one more byte than specified - maximum is 256 */
+ l++;
+
+ srca = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra);
+ desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra);
+
+ /*
+ * "When the operands overlap, the result is obtained as if the operands
+ * were processed one byte at a time". Only non-destructive overlaps
+ * behave like memmove().
+ */
if (dest == src + 1) {
- fast_memset(env, dest, cpu_ldub_data_ra(env, src, ra), l + 1, ra);
- } else if (dest < src || src + l < dest) {
- fast_memmove(env, dest, src, l + 1, ra);
+ access_memset(env, &desta, access_get_byte(env, &srca, 0, ra), ra);
+ } else if (!is_destructive_overlap(env, dest, src, l)) {
+ access_memmove(env, &desta, &srca, ra);
} else {
- /* slow version with byte accesses which always work */
- for (i = 0; i <= l; i++) {
- uint8_t x = cpu_ldub_data_ra(env, src + i, ra);
- cpu_stb_data_ra(env, dest + i, x, ra);
+ for (i = 0; i < l; i++) {
+ uint8_t byte = access_get_byte(env, &srca, i, ra);
+
+ access_set_byte(env, &desta, i, byte, ra);
}
}
@@ -333,69 +479,99 @@ void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
/* move inverse */
void HELPER(mvcin)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
{
+ const int mmu_idx = cpu_mmu_index(env, false);
+ S390Access srca, desta;
uintptr_t ra = GETPC();
int i;
- for (i = 0; i <= l; i++) {
- uint8_t v = cpu_ldub_data_ra(env, src - i, ra);
- cpu_stb_data_ra(env, dest + i, v, ra);
+ /* MVCIN always copies one more byte than specified - maximum is 256 */
+ l++;
+
+ src = wrap_address(env, src - l + 1);
+ srca = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra);
+ desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra);
+ for (i = 0; i < l; i++) {
+ const uint8_t x = access_get_byte(env, &srca, l - i - 1, ra);
+
+ access_set_byte(env, &desta, i, x, ra);
}
}
/* move numerics */
void HELPER(mvn)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
{
+ const int mmu_idx = cpu_mmu_index(env, false);
+ S390Access srca1, srca2, desta;
uintptr_t ra = GETPC();
int i;
- for (i = 0; i <= l; i++) {
- uint8_t v = cpu_ldub_data_ra(env, dest + i, ra) & 0xf0;
- v |= cpu_ldub_data_ra(env, src + i, ra) & 0x0f;
- cpu_stb_data_ra(env, dest + i, v, ra);
+ /* MVN always copies one more byte than specified - maximum is 256 */
+ l++;
+
+ srca1 = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra);
+ srca2 = access_prepare(env, dest, l, MMU_DATA_LOAD, mmu_idx, ra);
+ desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra);
+ for (i = 0; i < l; i++) {
+ const uint8_t x = (access_get_byte(env, &srca1, i, ra) & 0x0f) |
+ (access_get_byte(env, &srca2, i, ra) & 0xf0);
+
+ access_set_byte(env, &desta, i, x, ra);
}
}
/* move with offset */
void HELPER(mvo)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
{
+ const int mmu_idx = cpu_mmu_index(env, false);
+ /* MVO always processes one more byte than specified - maximum is 16 */
+ const int len_dest = (l >> 4) + 1;
+ const int len_src = (l & 0xf) + 1;
uintptr_t ra = GETPC();
- int len_dest = l >> 4;
- int len_src = l & 0xf;
uint8_t byte_dest, byte_src;
- int i;
+ S390Access srca, desta;
+ int i, j;
- src += len_src;
- dest += len_dest;
+ srca = access_prepare(env, src, len_src, MMU_DATA_LOAD, mmu_idx, ra);
+ desta = access_prepare(env, dest, len_dest, MMU_DATA_STORE, mmu_idx, ra);
/* Handle rightmost byte */
- byte_src = cpu_ldub_data_ra(env, src, ra);
- byte_dest = cpu_ldub_data_ra(env, dest, ra);
+ byte_dest = cpu_ldub_data_ra(env, dest + len_dest - 1, ra);
+ byte_src = access_get_byte(env, &srca, len_src - 1, ra);
byte_dest = (byte_dest & 0x0f) | (byte_src << 4);
- cpu_stb_data_ra(env, dest, byte_dest, ra);
+ access_set_byte(env, &desta, len_dest - 1, byte_dest, ra);
/* Process remaining bytes from right to left */
- for (i = 1; i <= len_dest; i++) {
+ for (i = len_dest - 2, j = len_src - 2; i >= 0; i--, j--) {
byte_dest = byte_src >> 4;
- if (len_src - i >= 0) {
- byte_src = cpu_ldub_data_ra(env, src - i, ra);
+ if (j >= 0) {
+ byte_src = access_get_byte(env, &srca, j, ra);
} else {
byte_src = 0;
}
byte_dest |= byte_src << 4;
- cpu_stb_data_ra(env, dest - i, byte_dest, ra);
+ access_set_byte(env, &desta, i, byte_dest, ra);
}
}
/* move zones */
void HELPER(mvz)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
{
+ const int mmu_idx = cpu_mmu_index(env, false);
+ S390Access srca1, srca2, desta;
uintptr_t ra = GETPC();
int i;
- for (i = 0; i <= l; i++) {
- uint8_t b = cpu_ldub_data_ra(env, dest + i, ra) & 0x0f;
- b |= cpu_ldub_data_ra(env, src + i, ra) & 0xf0;
- cpu_stb_data_ra(env, dest + i, b, ra);
+ /* MVZ always copies one more byte than specified - maximum is 256 */
+ l++;
+
+ srca1 = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra);
+ srca2 = access_prepare(env, dest, l, MMU_DATA_LOAD, mmu_idx, ra);
+ desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra);
+ for (i = 0; i < l; i++) {
+ const uint8_t x = (access_get_byte(env, &srca1, i, ra) & 0xf0) |
+ (access_get_byte(env, &srca2, i, ra) & 0x0f);
+
+ access_set_byte(env, &desta, i, x, ra);
}
}
@@ -469,6 +645,25 @@ static inline uint64_t get_address(CPUS390XState *env, int reg)
return wrap_address(env, env->regs[reg]);
}
+/*
+ * Store the address to the given register, zeroing out unused leftmost
+ * bits in bit positions 32-63 (24-bit and 31-bit mode only).
+ */
+static inline void set_address_zero(CPUS390XState *env, int reg,
+ uint64_t address)
+{
+ if (env->psw.mask & PSW_MASK_64) {
+ env->regs[reg] = address;
+ } else {
+ if (!(env->psw.mask & PSW_MASK_32)) {
+ address &= 0x00ffffff;
+ } else {
+ address &= 0x7fffffff;
+ }
+ env->regs[reg] = deposit64(env->regs[reg], 0, 32, address);
+ }
+}
+
static inline void set_address(CPUS390XState *env, int reg, uint64_t address)
{
if (env->psw.mask & PSW_MASK_64) {
@@ -492,7 +687,15 @@ static inline void set_address(CPUS390XState *env, int reg, uint64_t address)
}
}
-static inline uint64_t wrap_length(CPUS390XState *env, uint64_t length)
+static inline uint64_t wrap_length32(CPUS390XState *env, uint64_t length)
+{
+ if (!(env->psw.mask & PSW_MASK_64)) {
+ return (uint32_t)length;
+ }
+ return length;
+}
+
+static inline uint64_t wrap_length31(CPUS390XState *env, uint64_t length)
{
if (!(env->psw.mask & PSW_MASK_64)) {
/* 24-Bit and 31-Bit mode */
@@ -503,7 +706,7 @@ static inline uint64_t wrap_length(CPUS390XState *env, uint64_t length)
static inline uint64_t get_length(CPUS390XState *env, int reg)
{
- return wrap_length(env, env->regs[reg]);
+ return wrap_length31(env, env->regs[reg]);
}
static inline void set_length(CPUS390XState *env, int reg, uint64_t length)
@@ -636,39 +839,69 @@ uint64_t HELPER(clst)(CPUS390XState *env, uint64_t c, uint64_t s1, uint64_t s2)
/* move page */
uint32_t HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2)
{
- /* ??? missing r0 handling, which includes access keys, but more
- importantly optional suppression of the exception! */
- fast_memmove(env, r1, r2, TARGET_PAGE_SIZE, GETPC());
+ const int mmu_idx = cpu_mmu_index(env, false);
+ const bool f = extract64(r0, 11, 1);
+ const bool s = extract64(r0, 10, 1);
+ uintptr_t ra = GETPC();
+ S390Access srca, desta;
+
+ if ((f && s) || extract64(r0, 12, 4)) {
+ s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC());
+ }
+
+ r1 = wrap_address(env, r1 & TARGET_PAGE_MASK);
+ r2 = wrap_address(env, r2 & TARGET_PAGE_MASK);
+
+ /*
+ * TODO:
+ * - Access key handling
+ * - CC-option with surpression of page-translation exceptions
+ * - Store r1/r2 register identifiers at real location 162
+ */
+ srca = access_prepare(env, r2, TARGET_PAGE_SIZE, MMU_DATA_LOAD, mmu_idx,
+ ra);
+ desta = access_prepare(env, r1, TARGET_PAGE_SIZE, MMU_DATA_STORE, mmu_idx,
+ ra);
+ access_memmove(env, &desta, &srca, ra);
return 0; /* data moved */
}
-/* string copy (c is string terminator) */
-uint64_t HELPER(mvst)(CPUS390XState *env, uint64_t c, uint64_t d, uint64_t s)
+/* string copy */
+uint32_t HELPER(mvst)(CPUS390XState *env, uint32_t r1, uint32_t r2)
{
+ const int mmu_idx = cpu_mmu_index(env, false);
+ const uint64_t d = get_address(env, r1);
+ const uint64_t s = get_address(env, r2);
+ const uint8_t c = env->regs[0];
+ const int len = MIN(-(d | TARGET_PAGE_MASK), -(s | TARGET_PAGE_MASK));
+ S390Access srca, desta;
uintptr_t ra = GETPC();
- uint32_t len;
+ int i;
- c = c & 0xff;
- d = wrap_address(env, d);
- s = wrap_address(env, s);
+ if (env->regs[0] & 0xffffff00ull) {
+ s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra);
+ }
- /* Lest we fail to service interrupts in a timely manner, limit the
- amount of work we're willing to do. For now, let's cap at 8k. */
- for (len = 0; len < 0x2000; ++len) {
- uint8_t v = cpu_ldub_data_ra(env, s + len, ra);
- cpu_stb_data_ra(env, d + len, v, ra);
+ /*
+ * Our access should not exceed single pages, as we must not report access
+ * exceptions exceeding the actually copied range (which we don't know at
+ * this point). We might over-indicate watchpoints within the pages
+ * (if we ever care, we have to limit processing to a single byte).
+ */
+ srca = access_prepare(env, s, len, MMU_DATA_LOAD, mmu_idx, ra);
+ desta = access_prepare(env, d, len, MMU_DATA_STORE, mmu_idx, ra);
+ for (i = 0; i < len; i++) {
+ const uint8_t v = access_get_byte(env, &srca, i, ra);
+
+ access_set_byte(env, &desta, i, v, ra);
if (v == c) {
- /* Complete. Set CC=1 and advance R1. */
- env->cc_op = 1;
- env->retxl = s;
- return d + len;
+ set_address_zero(env, r1, d + i);
+ return 1;
}
}
-
- /* Incomplete. Set CC=3 and signal to advance R1 and R2. */
- env->cc_op = 3;
- env->retxl = s + len;
- return d + len;
+ set_address_zero(env, r1, d + len);
+ set_address_zero(env, r2, s + len);
+ return 3;
}
/* load access registers r1 to r3 from memory at a2 */
@@ -718,8 +951,10 @@ static inline uint32_t do_mvcl(CPUS390XState *env,
uint64_t *src, uint64_t *srclen,
uint16_t pad, int wordsize, uintptr_t ra)
{
- uint64_t len = MIN(*srclen, *destlen);
- uint32_t cc;
+ const int mmu_idx = cpu_mmu_index(env, false);
+ int len = MIN(*destlen, -(*dest | TARGET_PAGE_MASK));
+ S390Access srca, desta;
+ int i, cc;
if (*destlen == *srclen) {
cc = 0;
@@ -729,52 +964,109 @@ static inline uint32_t do_mvcl(CPUS390XState *env,
cc = 2;
}
- /* Copy the src array */
- fast_memmove(env, *dest, *src, len, ra);
- *src += len;
- *srclen -= len;
- *dest += len;
- *destlen -= len;
+ if (!*destlen) {
+ return cc;
+ }
- /* Pad the remaining area */
- if (wordsize == 1) {
- fast_memset(env, *dest, pad, *destlen, ra);
- *dest += *destlen;
- *destlen = 0;
+ /*
+ * Only perform one type of type of operation (move/pad) at a time.
+ * Stay within single pages.
+ */
+ if (*srclen) {
+ /* Copy the src array */
+ len = MIN(MIN(*srclen, -(*src | TARGET_PAGE_MASK)), len);
+ *destlen -= len;
+ *srclen -= len;
+ srca = access_prepare(env, *src, len, MMU_DATA_LOAD, mmu_idx, ra);
+ desta = access_prepare(env, *dest, len, MMU_DATA_STORE, mmu_idx, ra);
+ access_memmove(env, &desta, &srca, ra);
+ *src = wrap_address(env, *src + len);
+ *dest = wrap_address(env, *dest + len);
+ } else if (wordsize == 1) {
+ /* Pad the remaining area */
+ *destlen -= len;
+ desta = access_prepare(env, *dest, len, MMU_DATA_STORE, mmu_idx, ra);
+ access_memset(env, &desta, pad, ra);
+ *dest = wrap_address(env, *dest + len);
} else {
- /* If remaining length is odd, pad with odd byte first. */
- if (*destlen & 1) {
- cpu_stb_data_ra(env, *dest, pad & 0xff, ra);
- *dest += 1;
- *destlen -= 1;
- }
- /* The remaining length is even, pad using words. */
- for (; *destlen; *dest += 2, *destlen -= 2) {
- cpu_stw_data_ra(env, *dest, pad, ra);
+ desta = access_prepare(env, *dest, len, MMU_DATA_STORE, mmu_idx, ra);
+
+ /* The remaining length selects the padding byte. */
+ for (i = 0; i < len; (*destlen)--, i++) {
+ if (*destlen & 1) {
+ access_set_byte(env, &desta, i, pad, ra);
+ } else {
+ access_set_byte(env, &desta, i, pad >> 8, ra);
+ }
}
+ *dest = wrap_address(env, *dest + len);
}
- return cc;
+ return *destlen ? 3 : cc;
}
/* move long */
uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
{
+ const int mmu_idx = cpu_mmu_index(env, false);
uintptr_t ra = GETPC();
uint64_t destlen = env->regs[r1 + 1] & 0xffffff;
uint64_t dest = get_address(env, r1);
uint64_t srclen = env->regs[r2 + 1] & 0xffffff;
uint64_t src = get_address(env, r2);
uint8_t pad = env->regs[r2 + 1] >> 24;
- uint32_t cc;
+ S390Access srca, desta;
+ uint32_t cc, cur_len;
- cc = do_mvcl(env, &dest, &destlen, &src, &srclen, pad, 1, ra);
+ if (is_destructive_overlap(env, dest, src, MIN(srclen, destlen))) {
+ cc = 3;
+ } else if (srclen == destlen) {
+ cc = 0;
+ } else if (destlen < srclen) {
+ cc = 1;
+ } else {
+ cc = 2;
+ }
- env->regs[r1 + 1] = deposit64(env->regs[r1 + 1], 0, 24, destlen);
- env->regs[r2 + 1] = deposit64(env->regs[r2 + 1], 0, 24, srclen);
- set_address(env, r1, dest);
- set_address(env, r2, src);
+ /* We might have to zero-out some bits even if there was no action. */
+ if (unlikely(!destlen || cc == 3)) {
+ set_address_zero(env, r2, src);
+ set_address_zero(env, r1, dest);
+ return cc;
+ } else if (!srclen) {
+ set_address_zero(env, r2, src);
+ }
+
+ /*
+ * Only perform one type of type of operation (move/pad) in one step.
+ * Stay within single pages.
+ */
+ while (destlen) {
+ cur_len = MIN(destlen, -(dest | TARGET_PAGE_MASK));
+ if (!srclen) {
+ desta = access_prepare(env, dest, cur_len, MMU_DATA_STORE, mmu_idx,
+ ra);
+ access_memset(env, &desta, pad, ra);
+ } else {
+ cur_len = MIN(MIN(srclen, -(src | TARGET_PAGE_MASK)), cur_len);
+ srca = access_prepare(env, src, cur_len, MMU_DATA_LOAD, mmu_idx,
+ ra);
+ desta = access_prepare(env, dest, cur_len, MMU_DATA_STORE, mmu_idx,
+ ra);
+ access_memmove(env, &desta, &srca, ra);
+ src = wrap_address(env, src + cur_len);
+ srclen -= cur_len;
+ env->regs[r2 + 1] = deposit64(env->regs[r2 + 1], 0, 24, srclen);
+ set_address_zero(env, r2, src);
+ }
+ dest = wrap_address(env, dest + cur_len);
+ destlen -= cur_len;
+ env->regs[r1 + 1] = deposit64(env->regs[r1 + 1], 0, 24, destlen);
+ set_address_zero(env, r1, dest);
+
+ /* TODO: Deliver interrupts. */
+ }
return cc;
}
@@ -1747,7 +2039,7 @@ uint32_t HELPER(tprot)(CPUS390XState *env, uint64_t a1, uint64_t a2)
if (env->int_pgm_code == PGM_PROTECTION) {
/* retry if reading is possible */
- cs->exception_index = 0;
+ cs->exception_index = -1;
if (!s390_cpu_virt_mem_check_read(cpu, a1, 0, 1)) {
/* Fetching permitted; storing not permitted */
return 1;
@@ -1757,7 +2049,7 @@ uint32_t HELPER(tprot)(CPUS390XState *env, uint64_t a1, uint64_t a2)
switch (env->int_pgm_code) {
case PGM_PROTECTION:
/* Fetching not permitted; storing not permitted */
- cs->exception_index = 0;
+ cs->exception_index = -1;
return 2;
case PGM_ADDRESSING:
case PGM_TRANS_SPEC:
@@ -1767,7 +2059,7 @@ uint32_t HELPER(tprot)(CPUS390XState *env, uint64_t a1, uint64_t a2)
}
/* Translation not available */
- cs->exception_index = 0;
+ cs->exception_index = -1;
return 3;
}
@@ -1866,47 +2158,63 @@ uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2)
uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
{
+ const uint8_t psw_as = (env->psw.mask & PSW_MASK_ASC) >> PSW_SHIFT_ASC;
+ S390Access srca, desta;
uintptr_t ra = GETPC();
- int cc = 0, i;
+ int cc = 0;
HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
__func__, l, a1, a2);
+ if (!(env->psw.mask & PSW_MASK_DAT) || !(env->cregs[0] & CR0_SECONDARY) ||
+ psw_as == AS_HOME || psw_as == AS_ACCREG) {
+ s390_program_interrupt(env, PGM_SPECIAL_OP, ILEN_AUTO, ra);
+ }
+
+ l = wrap_length32(env, l);
if (l > 256) {
/* max 256 */
l = 256;
cc = 3;
+ } else if (!l) {
+ return cc;
}
- /* XXX replace w/ memcpy */
- for (i = 0; i < l; i++) {
- uint8_t x = cpu_ldub_primary_ra(env, a2 + i, ra);
- cpu_stb_secondary_ra(env, a1 + i, x, ra);
- }
-
+ /* TODO: Access key handling */
+ srca = access_prepare(env, a2, l, MMU_DATA_LOAD, MMU_PRIMARY_IDX, ra);
+ desta = access_prepare(env, a1, l, MMU_DATA_STORE, MMU_SECONDARY_IDX, ra);
+ access_memmove(env, &desta, &srca, ra);
return cc;
}
uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
{
+ const uint8_t psw_as = (env->psw.mask & PSW_MASK_ASC) >> PSW_SHIFT_ASC;
+ S390Access srca, desta;
uintptr_t ra = GETPC();
- int cc = 0, i;
+ int cc = 0;
HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
__func__, l, a1, a2);
+ if (!(env->psw.mask & PSW_MASK_DAT) || !(env->cregs[0] & CR0_SECONDARY) ||
+ psw_as == AS_HOME || psw_as == AS_ACCREG) {
+ s390_program_interrupt(env, PGM_SPECIAL_OP, ILEN_AUTO, ra);
+ }
+
+ l = wrap_length32(env, l);
if (l > 256) {
/* max 256 */
l = 256;
cc = 3;
+ } else if (!l) {
+ return cc;
}
- /* XXX replace w/ memcpy */
- for (i = 0; i < l; i++) {
- uint8_t x = cpu_ldub_secondary_ra(env, a2 + i, ra);
- cpu_stb_primary_ra(env, a1 + i, x, ra);
- }
-
+ /* TODO: Access key handling */
+ srca = access_prepare(env, a2, l, MMU_DATA_LOAD, MMU_SECONDARY_IDX, ra);
+ desta = access_prepare(env, a1, l, MMU_DATA_STORE, MMU_PRIMARY_IDX, ra);
+ access_memmove(env, &desta, &srca, ra);
return cc;
}
@@ -2272,7 +2580,7 @@ uint32_t HELPER(mvcos)(CPUS390XState *env, uint64_t dest, uint64_t src,
s390_program_interrupt(env, PGM_PRIVILEGED, 6, ra);
}
- len = wrap_length(env, len);
+ len = wrap_length32(env, len);
if (len > 4096) {
cc = 3;
len = 4096;
@@ -2286,16 +2594,15 @@ uint32_t HELPER(mvcos)(CPUS390XState *env, uint64_t dest, uint64_t src,
s390_program_interrupt(env, PGM_ADDRESSING, 6, ra);
}
- /* FIXME: a) LAP
- * b) Access using correct keys
- * c) AR-mode
- */
-#ifdef CONFIG_USER_ONLY
- /* psw keys are never valid in user mode, we will never reach this */
- g_assert_not_reached();
-#else
- fast_memmove_as(env, dest, src, len, dest_as, src_as, ra);
-#endif
+ /* FIXME: Access using correct keys and AR-mode */
+ if (len) {
+ S390Access srca = access_prepare(env, src, len, MMU_DATA_LOAD,
+ mmu_idx_from_as(src_as), ra);
+ S390Access desta = access_prepare(env, dest, len, MMU_DATA_STORE,
+ mmu_idx_from_as(dest_as), ra);
+
+ access_memmove(env, &desta, &srca, ra);
+ }
return cc;
}
diff --git a/target/s390x/translate.c b/target/s390x/translate.c
index 2927247c82..a3e43ff9ec 100644
--- a/target/s390x/translate.c
+++ b/target/s390x/translate.c
@@ -318,6 +318,9 @@ static inline uint64_t ld_code4(CPUS390XState *env, uint64_t pc)
static int get_mem_index(DisasContext *s)
{
+#ifdef CONFIG_USER_ONLY
+ return MMU_USER_IDX;
+#else
if (!(s->base.tb->flags & FLAG_MASK_DAT)) {
return MMU_REAL_IDX;
}
@@ -333,6 +336,7 @@ static int get_mem_index(DisasContext *s)
tcg_abort();
break;
}
+#endif
}
static void gen_exception(int excp)
@@ -3488,9 +3492,13 @@ static DisasJumpType op_mvpg(DisasContext *s, DisasOps *o)
static DisasJumpType op_mvst(DisasContext *s, DisasOps *o)
{
- gen_helper_mvst(o->in1, cpu_env, regs[0], o->in1, o->in2);
+ TCGv_i32 t1 = tcg_const_i32(get_field(s->fields, r1));
+ TCGv_i32 t2 = tcg_const_i32(get_field(s->fields, r2));
+
+ gen_helper_mvst(cc_op, cpu_env, t1, t2);
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t2);
set_cc_static(s);
- return_low128(o->in2);
return DISAS_NEXT;
}
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 793632ca72..479664f899 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -170,8 +170,7 @@ check-qtest-i386-$(CONFIG_SGA) += tests/boot-serial-test$(EXESUF)
check-qtest-i386-$(CONFIG_SLIRP) += tests/pxe-test$(EXESUF)
check-qtest-i386-y += tests/rtc-test$(EXESUF)
check-qtest-i386-$(CONFIG_ISA_IPMI_KCS) += tests/ipmi-kcs-test$(EXESUF)
-# Disabled temporarily as it fails intermittently especially under NetBSD VM
-# check-qtest-i386-$(CONFIG_ISA_IPMI_BT) += tests/ipmi-bt-test$(EXESUF)
+check-qtest-i386-$(CONFIG_ISA_IPMI_BT) += 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/device-plug-test$(EXESUF)
diff --git a/tests/acceptance/linux_ssh_mips_malta.py b/tests/acceptance/linux_ssh_mips_malta.py
index 7200507a3a..25a1df5098 100644
--- a/tests/acceptance/linux_ssh_mips_malta.py
+++ b/tests/acceptance/linux_ssh_mips_malta.py
@@ -9,13 +9,13 @@ import os
import re
import base64
import logging
-import paramiko
import time
from avocado import skipUnless
from avocado_qemu import Test
from avocado.utils import process
from avocado.utils import archive
+from avocado.utils import ssh
class LinuxSSH(Test):
@@ -26,35 +26,19 @@ class LinuxSSH(Test):
VM_IP = '127.0.0.1'
IMAGE_INFO = {
- 'be': {
- 'image_url': 'https://people.debian.org/~aurel32/qemu/mips/'
- 'debian_wheezy_mips_standard.qcow2',
- 'image_hash': '8987a63270df67345b2135a6b7a4885a35e392d5',
- 'rsa_hostkey': b'AAAAB3NzaC1yc2EAAAADAQABAAABAQCca1VitiyLAdQOld'
- b'zT43IOEVJZ0wHD78GJi8wDAjMiYWUzNSSn0rXGQsINHuH5'
- b'IlF+kBZsHinb/FtKCAyS9a8uCHhQI4SuB4QhAb0+39MlUw'
- b'Mm0CLkctgM2eUUZ6MQMQvDlqnue6CCkxN62EZYbaxmby7j'
- b'CQa1125o1HRKBvdGm2zrJWxXAfA+f1v6jHLyE8Jnu83eQ+'
- b'BFY25G+Vzx1PVc3zQBwJ8r0NGTRqy2//oWQP0h+bMsgeFe'
- b'KH/J3RJM22vg6+I4JAdBFcxnK+l781h1FuRxOn4O/Xslbg'
- b'go6WtB4V4TOsw2E/KfxI5IZ/icxF+swVcnvF46Hf3uQc/0'
- b'BBqb',
- },
- 'le': {
- 'image_url': 'https://people.debian.org/~aurel32/qemu/mipsel/'
- 'debian_wheezy_mipsel_standard.qcow2',
- 'image_hash': '7866764d9de3ef536ffca24c9fb9f04ffdb45802',
- 'rsa_hostkey': b'AAAAB3NzaC1yc2EAAAADAQABAAABAQClXJlBT71HL5yKvv'
- b'gfC7jmxSWx5zSBCzET6CLZczwAafSIs7YKfNOy/dQTxhuk'
- b'yIGFUugZFoF3E9PzdhunuyvyTd56MPoNIqFbb5rGokwU5I'
- b'TOx3dBHZR0mClypL6MVrwe0bsiIb8GhF1zioNwcsaAZnAi'
- b'KfXStVDtXvn/kLLq+xLABYt48CC5KYWoFaCoICskLAY+qo'
- b'L+LWyAnQisj4jAH8VSaSKIImFpfkHWEXPhHcC4ZBlDKtnH'
- b'po9vhfCHgnfW3Pzrqmk8BI4HysqPFVmJWkJGlGUL+sGeg3'
- b'ZZolAYuDXGuBrw8ooPJq2v2dOH+z6dyD2q/ypmAbyPqj5C'
- b'rc8H',
- },
- }
+ 'be': {'image_url': ('https://people.debian.org/~aurel32/qemu/mips/'
+ 'debian_wheezy_mips_standard.qcow2'),
+ 'image_hash': '8987a63270df67345b2135a6b7a4885a35e392d5'},
+ 'le': {'image_url': ('https://people.debian.org/~aurel32/qemu/mipsel/'
+ 'debian_wheezy_mipsel_standard.qcow2'),
+ 'image_hash': '7866764d9de3ef536ffca24c9fb9f04ffdb45802'}
+ }
+
+
+ @skipUnless(ssh.SSH_CLIENT_BINARY, 'No SSH client available')
+ @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout')
+ def setUp(self):
+ super(LinuxSSH, self).setUp()
def wait_for_console_pattern(self, success_message,
failure_message='Oops'):
@@ -78,23 +62,14 @@ class LinuxSSH(Test):
self.log.debug("sshd listening on port:" + port)
return port
- def ssh_connect(self, username, password, rsa_hostkey_b64=None):
+ def ssh_connect(self, username, password):
self.ssh_logger = logging.getLogger('ssh')
- self.ssh_username = username
- self.ssh_ps1 = '# ' if username is 'root' else '$ '
- self.ssh_client = paramiko.SSHClient()
port = self.get_portfwd()
- if rsa_hostkey_b64:
- rsa_hostkey_bin = base64.b64decode(rsa_hostkey_b64)
- rsa_hostkey = paramiko.RSAKey(data = rsa_hostkey_bin)
- ipport = '[%s]:%s' % (self.VM_IP, port)
- self.ssh_logger.debug('ipport ' + ipport)
- self.ssh_client.get_host_keys().add(ipport, 'ssh-rsa', rsa_hostkey)
+ self.ssh_session = ssh.Session(self.VM_IP, port=int(port),
+ user=username, password=password)
for i in range(10):
try:
- self.ssh_client.connect(self.VM_IP, int(port),
- username, password, banner_timeout=90)
- self.ssh_logger.info("Entering interactive session.")
+ self.ssh_session.connect()
return
except:
time.sleep(4)
@@ -102,15 +77,15 @@ class LinuxSSH(Test):
self.fail("sshd timeout")
def ssh_disconnect_vm(self):
- self.ssh_client.close()
+ self.ssh_session.quit()
def ssh_command(self, command, is_root=True):
- self.ssh_logger.info(self.ssh_ps1 + command)
- stdin, stdout, stderr = self.ssh_client.exec_command(command)
- stdout_lines = [line.strip('\n') for line in stdout]
+ self.ssh_logger.info(command)
+ result = self.ssh_session.cmd(command)
+ stdout_lines = [line.rstrip() for line in result.stdout_text.splitlines()]
for line in stdout_lines:
self.ssh_logger.info(line)
- stderr_lines = [line.strip('\n') for line in stderr]
+ stderr_lines = [line.rstrip() for line in result.stderr_text.splitlines()]
for line in stderr_lines:
self.ssh_logger.warning(line)
return stdout_lines, stderr_lines
@@ -119,7 +94,6 @@ class LinuxSSH(Test):
image_url = self.IMAGE_INFO[endianess]['image_url']
image_hash = self.IMAGE_INFO[endianess]['image_hash']
image_path = self.fetch_asset(image_url, asset_hash=image_hash)
- rsa_hostkey_b64 = self.IMAGE_INFO[endianess]['rsa_hostkey']
self.vm.set_machine('malta')
self.vm.set_console()
@@ -138,40 +112,90 @@ class LinuxSSH(Test):
self.wait_for_console_pattern(console_pattern)
self.log.info('sshd ready')
- self.ssh_connect('root', 'root', rsa_hostkey_b64=rsa_hostkey_b64)
+ self.ssh_connect('root', 'root')
def shutdown_via_ssh(self):
self.ssh_command('poweroff')
self.ssh_disconnect_vm()
self.wait_for_console_pattern('Power down')
- def run_common_commands(self):
- stdout, stderr = self.ssh_command('lspci -d 11ab:4620')
- self.assertIn(True, ["GT-64120" in line for line in stdout])
-
- stdout, stderr = self.ssh_command('cat /sys/bus/i2c/devices/i2c-0/name')
- self.assertIn(True, ["SMBus PIIX4 adapter" in line
- for line in stdout])
-
- stdout, stderr = self.ssh_command('cat /proc/mtd')
- self.assertIn(True, ["YAMON" in line
- for line in stdout])
+ def ssh_command_output_contains(self, cmd, exp):
+ stdout, _ = self.ssh_command(cmd)
+ for line in stdout:
+ if exp in line:
+ break
+ else:
+ self.fail('"%s" output does not contain "%s"' % (cmd, exp))
+ def run_common_commands(self):
+ self.ssh_command_output_contains(
+ 'cat /proc/cpuinfo',
+ '24Kc')
+ self.ssh_command_output_contains(
+ 'uname -m',
+ 'mips')
+ self.ssh_command_output_contains(
+ 'uname -r',
+ '3.2.0-4-4kc-malta')
+ self.ssh_command_output_contains(
+ 'cat /proc/interrupts',
+ 'timer')
+ self.ssh_command_output_contains(
+ 'cat /proc/interrupts',
+ 'i8042')
+ self.ssh_command_output_contains(
+ 'cat /proc/interrupts',
+ 'serial')
+ self.ssh_command_output_contains(
+ 'cat /proc/interrupts',
+ 'ata_piix')
+ self.ssh_command_output_contains(
+ 'cat /proc/interrupts',
+ 'eth0')
+ self.ssh_command_output_contains(
+ 'cat /proc/interrupts',
+ 'eth0')
+ self.ssh_command_output_contains(
+ 'cat /proc/devices',
+ 'input')
+ self.ssh_command_output_contains(
+ 'cat /proc/devices',
+ 'usb')
+ self.ssh_command_output_contains(
+ 'cat /proc/devices',
+ 'fb')
+ self.ssh_command_output_contains(
+ 'cat /proc/ioports',
+ 'serial')
+ self.ssh_command_output_contains(
+ 'cat /proc/ioports',
+ 'ata_piix')
+ self.ssh_command_output_contains(
+ 'cat /proc/ioports',
+ 'piix4_smbus')
+ self.ssh_command_output_contains(
+ 'lspci -d 11ab:4620',
+ 'GT-64120')
+ self.ssh_command_output_contains(
+ 'cat /sys/bus/i2c/devices/i2c-0/name',
+ 'SMBus PIIX4 adapter')
+ self.ssh_command_output_contains(
+ 'cat /proc/mtd',
+ 'YAMON')
# Empty 'Board Config'
- stdout, stderr = self.ssh_command('md5sum /dev/mtd2ro')
- self.assertIn(True, ["0dfbe8aa4c20b52e1b8bf3cb6cbdf193" in line
- for line in stdout])
+ self.ssh_command_output_contains(
+ 'md5sum /dev/mtd2ro',
+ '0dfbe8aa4c20b52e1b8bf3cb6cbdf193')
def check_mips_malta(self, endianess, kernel_path, uname_m):
self.boot_debian_wheezy_image_and_ssh_login(endianess, kernel_path)
- stdout, stderr = self.ssh_command('uname -a')
+ stdout, _ = self.ssh_command('uname -a')
self.assertIn(True, [uname_m + " GNU/Linux" in line for line in stdout])
self.run_common_commands()
self.shutdown_via_ssh()
- @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout')
def test_mips_malta32eb_kernel3_2_0(self):
"""
:avocado: tags=arch:mips
@@ -186,7 +210,6 @@ class LinuxSSH(Test):
self.check_mips_malta('be', kernel_path, 'mips')
- @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout')
def test_mips_malta32el_kernel3_2_0(self):
"""
:avocado: tags=arch:mipsel
@@ -201,7 +224,6 @@ class LinuxSSH(Test):
self.check_mips_malta('le', kernel_path, 'mips')
- @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout')
def test_mips_malta64eb_kernel3_2_0(self):
"""
:avocado: tags=arch:mips64
@@ -215,7 +237,6 @@ class LinuxSSH(Test):
kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
self.check_mips_malta('be', kernel_path, 'mips64')
- @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout')
def test_mips_malta64el_kernel3_2_0(self):
"""
:avocado: tags=arch:mips64el
diff --git a/tests/acceptance/machine_m68k_nextcube.py b/tests/acceptance/machine_m68k_nextcube.py
index e09cab9f20..fcd2c58ee7 100644
--- a/tests/acceptance/machine_m68k_nextcube.py
+++ b/tests/acceptance/machine_m68k_nextcube.py
@@ -116,6 +116,6 @@ class NextCubeMachine(Test):
if len(line):
console_logger.debug(line)
self.assertIn('Testing the FPU, SCC', text)
- self.assertIn('System test failed. Error code 51', text)
+ self.assertIn('System test failed. Error code', text)
self.assertIn('Boot command', text)
self.assertIn('Next>', text)
diff --git a/tests/acceptance/x86_cpu_model_versions.py b/tests/acceptance/x86_cpu_model_versions.py
index 1c9fd6a56e..5fc9ca4bc6 100644
--- a/tests/acceptance/x86_cpu_model_versions.py
+++ b/tests/acceptance/x86_cpu_model_versions.py
@@ -234,7 +234,14 @@ class X86CPUModelAliases(avocado_qemu.Test):
self.validate_aliases(cpus)
- def test_Cascadelake_arch_capabilities_result(self):
+
+class CascadelakeArchCapabilities(avocado_qemu.Test):
+ """
+ Validation of Cascadelake arch-capabilities
+
+ :avocado: tags=arch:x86_64
+ """
+ def test_4_1(self):
# machine-type only:
vm = self.get_vm()
vm.add_args('-S')
@@ -244,6 +251,7 @@ class X86CPUModelAliases(avocado_qemu.Test):
self.assertFalse(get_cpu_prop(vm, 'arch-capabilities'),
'pc-i440fx-4.1 + Cascadelake-Server should not have arch-capabilities')
+ def test_4_0(self):
vm = self.get_vm()
vm.add_args('-S')
vm.set_machine('pc-i440fx-4.0')
@@ -252,6 +260,7 @@ class X86CPUModelAliases(avocado_qemu.Test):
self.assertFalse(get_cpu_prop(vm, 'arch-capabilities'),
'pc-i440fx-4.0 + Cascadelake-Server should not have arch-capabilities')
+ def test_set_4_0(self):
# command line must override machine-type if CPU model is not versioned:
vm = self.get_vm()
vm.add_args('-S')
@@ -261,6 +270,7 @@ class X86CPUModelAliases(avocado_qemu.Test):
self.assertTrue(get_cpu_prop(vm, 'arch-capabilities'),
'pc-i440fx-4.0 + Cascadelake-Server,+arch-capabilities should have arch-capabilities')
+ def test_unset_4_1(self):
vm = self.get_vm()
vm.add_args('-S')
vm.set_machine('pc-i440fx-4.1')
@@ -269,6 +279,7 @@ class X86CPUModelAliases(avocado_qemu.Test):
self.assertFalse(get_cpu_prop(vm, 'arch-capabilities'),
'pc-i440fx-4.1 + Cascadelake-Server,-arch-capabilities should not have arch-capabilities')
+ def test_v1_4_0(self):
# versioned CPU model overrides machine-type:
vm = self.get_vm()
vm.add_args('-S')
@@ -276,25 +287,28 @@ class X86CPUModelAliases(avocado_qemu.Test):
vm.add_args('-cpu', 'Cascadelake-Server-v1,x-force-features=on,check=off,enforce=off')
vm.launch()
self.assertFalse(get_cpu_prop(vm, 'arch-capabilities'),
- 'pc-i440fx-4.1 + Cascadelake-Server-v1 should not have arch-capabilities')
+ 'pc-i440fx-4.0 + Cascadelake-Server-v1 should not have arch-capabilities')
+ def test_v2_4_0(self):
vm = self.get_vm()
vm.add_args('-S')
vm.set_machine('pc-i440fx-4.0')
vm.add_args('-cpu', 'Cascadelake-Server-v2,x-force-features=on,check=off,enforce=off')
vm.launch()
self.assertTrue(get_cpu_prop(vm, 'arch-capabilities'),
- 'pc-i440fx-4.1 + Cascadelake-Server-v1 should have arch-capabilities')
+ 'pc-i440fx-4.0 + Cascadelake-Server-v2 should have arch-capabilities')
+ def test_v1_set_4_0(self):
# command line must override machine-type and versioned CPU model:
vm = self.get_vm()
vm.add_args('-S')
vm.set_machine('pc-i440fx-4.0')
- vm.add_args('-cpu', 'Cascadelake-Server,x-force-features=on,check=off,enforce=off,+arch-capabilities')
+ vm.add_args('-cpu', 'Cascadelake-Server-v1,x-force-features=on,check=off,enforce=off,+arch-capabilities')
vm.launch()
self.assertTrue(get_cpu_prop(vm, 'arch-capabilities'),
'pc-i440fx-4.0 + Cascadelake-Server-v1,+arch-capabilities should have arch-capabilities')
+ def test_v2_unset_4_1(self):
vm = self.get_vm()
vm.add_args('-S')
vm.set_machine('pc-i440fx-4.1')
diff --git a/tests/data/acpi/q35/DSDT b/tests/data/acpi/q35/DSDT
index f9f36d1645..77ea60ffed 100644
--- a/tests/data/acpi/q35/DSDT
+++ b/tests/data/acpi/q35/DSDT
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.bridge b/tests/data/acpi/q35/DSDT.bridge
index 29176832ca..fbc2d40000 100644
--- a/tests/data/acpi/q35/DSDT.bridge
+++ b/tests/data/acpi/q35/DSDT.bridge
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.cphp b/tests/data/acpi/q35/DSDT.cphp
index 19bdb5d210..6a896cb214 100644
--- a/tests/data/acpi/q35/DSDT.cphp
+++ b/tests/data/acpi/q35/DSDT.cphp
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.dimmpxm b/tests/data/acpi/q35/DSDT.dimmpxm
index 727fe489b4..23fdf5e60a 100644
--- a/tests/data/acpi/q35/DSDT.dimmpxm
+++ b/tests/data/acpi/q35/DSDT.dimmpxm
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.ipmibt b/tests/data/acpi/q35/DSDT.ipmibt
index 9634930e61..c3fca0a71e 100644
--- a/tests/data/acpi/q35/DSDT.ipmibt
+++ b/tests/data/acpi/q35/DSDT.ipmibt
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.memhp b/tests/data/acpi/q35/DSDT.memhp
index dad5dc8db2..2abd0e36cd 100644
--- a/tests/data/acpi/q35/DSDT.memhp
+++ b/tests/data/acpi/q35/DSDT.memhp
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.mmio64 b/tests/data/acpi/q35/DSDT.mmio64
index 20f627ed08..b32034a11c 100644
--- a/tests/data/acpi/q35/DSDT.mmio64
+++ b/tests/data/acpi/q35/DSDT.mmio64
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.numamem b/tests/data/acpi/q35/DSDT.numamem
index 7b96a97280..d8b2b47f8b 100644
--- a/tests/data/acpi/q35/DSDT.numamem
+++ b/tests/data/acpi/q35/DSDT.numamem
Binary files differ
diff --git a/tests/ipmi-bt-test.c b/tests/ipmi-bt-test.c
index fc4c83b5db..a42207d416 100644
--- a/tests/ipmi-bt-test.c
+++ b/tests/ipmi-bt-test.c
@@ -30,7 +30,7 @@
#include <netinet/tcp.h>
-#include "libqtest.h"
+#include "libqtest-single.h"
#include "qemu-common.h"
#define IPMI_IRQ 5
@@ -99,6 +99,7 @@ static void bt_wait_b_busy(void)
unsigned int count = 1000;
while (IPMI_BT_CTLREG_GET_B_BUSY() != 0) {
g_assert(--count != 0);
+ usleep(100);
}
}
@@ -107,6 +108,7 @@ static void bt_wait_b2h_atn(void)
unsigned int count = 1000;
while (IPMI_BT_CTLREG_GET_B2H_ATN() == 0) {
g_assert(--count != 0);
+ usleep(100);
}
}
@@ -240,13 +242,13 @@ static void emu_msg_handler(void)
write_emu_msg(msg, msg_len);
} else if ((msg[1] == set_bmc_globals_cmd[0]) &&
(msg[2] == set_bmc_globals_cmd[1])) {
+ write_emu_msg(enable_irq_cmd, sizeof(enable_irq_cmd));
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);
}
diff --git a/tests/requirements.txt b/tests/requirements.txt
index bd1f7590ed..a2a587223a 100644
--- a/tests/requirements.txt
+++ b/tests/requirements.txt
@@ -1,5 +1,4 @@
# Add Python module requirements, one per line, to be installed
# in the tests/venv Python virtual environment. For more info,
# refer to: https://pip.pypa.io/en/stable/user_guide/#id1
-avocado-framework==68.0
-paramiko==2.4.2
+avocado-framework==72.0
diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target
index 151dc075aa..241ef28f61 100644
--- a/tests/tcg/s390x/Makefile.target
+++ b/tests/tcg/s390x/Makefile.target
@@ -6,3 +6,5 @@ TESTS+=ipm
TESTS+=exrl-trt
TESTS+=exrl-trtr
TESTS+=pack
+TESTS+=mvo
+TESTS+=mvc
diff --git a/tests/tcg/s390x/mvc.c b/tests/tcg/s390x/mvc.c
new file mode 100644
index 0000000000..aa552d52e5
--- /dev/null
+++ b/tests/tcg/s390x/mvc.c
@@ -0,0 +1,109 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <signal.h>
+#include <setjmp.h>
+
+jmp_buf jmp_env;
+
+static void handle_sigsegv(int sig)
+{
+ siglongjmp(jmp_env, 1);
+}
+
+#define ALLOC_SIZE (2 * 4096)
+
+static inline void mvc_256(const char *dst, const char *src)
+{
+ asm volatile (
+ " mvc 0(256,%[dst]),0(%[src])\n"
+ :
+ : [dst] "d" (dst),
+ [src] "d" (src)
+ : "memory");
+}
+
+int main(void)
+{
+ char *src, *dst;
+ int i;
+
+ /* register the SIGSEGV handler */
+ if (signal(SIGSEGV, handle_sigsegv) == SIG_ERR) {
+ fprintf(stderr, "SIGSEGV not registered\n");
+ return 1;
+ }
+
+ /* prepare the buffers - two consecutive pages */
+ src = valloc(ALLOC_SIZE);
+ dst = valloc(ALLOC_SIZE);
+ memset(src, 0xff, ALLOC_SIZE);
+ memset(dst, 0x0, ALLOC_SIZE);
+
+ /* protect the second pages */
+ if (mprotect(src + 4096, 4096, PROT_NONE) ||
+ mprotect(dst + 4096, 4096, PROT_NONE)) {
+ fprintf(stderr, "mprotect failed\n");
+ return 1;
+ }
+
+ /* fault on second destination page */
+ if (sigsetjmp(jmp_env, 1) == 0) {
+ mvc_256(dst + 4096 - 128, src);
+ fprintf(stderr, "fault not triggered\n");
+ return 1;
+ }
+
+ /* fault on second source page */
+ if (sigsetjmp(jmp_env, 1) == 0) {
+ mvc_256(dst, src + 4096 - 128);
+ fprintf(stderr, "fault not triggered\n");
+ return 1;
+ }
+
+ /* fault on second source and second destination page */
+ if (sigsetjmp(jmp_env, 1) == 0) {
+ mvc_256(dst + 4096 - 128, src + 4096 - 128);
+ fprintf(stderr, "fault not triggered\n");
+ return 1;
+ }
+
+ /* restore permissions */
+ if (mprotect(src + 4096, 4096, PROT_READ | PROT_WRITE) ||
+ mprotect(dst + 4096, 4096, PROT_READ | PROT_WRITE)) {
+ fprintf(stderr, "mprotect failed\n");
+ return 1;
+ }
+
+ /* no data must be touched during the faults */
+ for (i = 0; i < ALLOC_SIZE; i++) {
+ if (src[i] != 0xff || dst[i]) {
+ fprintf(stderr, "data modified during a fault\n");
+ return 1;
+ }
+ }
+
+ /* test if MVC works now correctly accross page boundaries */
+ mvc_256(dst + 4096 - 128, src + 4096 - 128);
+ for (i = 0; i < ALLOC_SIZE; i++) {
+ if (src[i] != 0xff) {
+ fprintf(stderr, "src modified\n");
+ return 1;
+ }
+ if (i < 4096 - 128 || i >= 4096 + 128) {
+ if (dst[i]) {
+ fprintf(stderr, "wrong dst modified\n");
+ return 1;
+ }
+ } else {
+ if (dst[i] != 0xff) {
+ fprintf(stderr, "wrong data moved\n");
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/tests/tcg/s390x/mvo.c b/tests/tcg/s390x/mvo.c
new file mode 100644
index 0000000000..5546fe2a97
--- /dev/null
+++ b/tests/tcg/s390x/mvo.c
@@ -0,0 +1,25 @@
+#include <stdint.h>
+#include <stdio.h>
+
+int main(void)
+{
+ uint8_t dest[6] = {0xff, 0x77, 0x88, 0x99, 0x0c, 0xff};
+ uint8_t src[5] = {0xee, 0x12, 0x34, 0x56, 0xee};
+ uint8_t expected[6] = {0xff, 0x01, 0x23, 0x45, 0x6c, 0xff};
+ int i;
+
+ asm volatile (
+ " mvo 0(4,%[dest]),0(3,%[src])\n"
+ :
+ : [dest] "d" (dest + 1),
+ [src] "d" (src + 1)
+ : "memory");
+
+ for (i = 0; i < sizeof(expected); i++) {
+ if (dest[i] != expected[i]) {
+ fprintf(stderr, "bad data\n");
+ return 1;
+ }
+ }
+ return 0;
+}