mirror of https://gitee.com/openkylin/qemu.git
acpi, pc features
pxb support for q35 nvdimm support most of ipmi support part of DSDT rewrite Signed-off-by: Michael S. Tsirkin <mst@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJWeX6WAAoJECgfDbjSjVRp08kH/0YwnLl23evSIst67O5AthCm IjTu7tbANtpadMq9YxU7Is+6QEi27C26pqy4CWgoirhsrjiY2jC5LVCZqhIhBEeJ JFyAiYglJDMraHWyKG373lLEeQvNmEmFsxmULmV6cX2j1tsgyXCqdQs5DmRGnHno L02W0p3EnEfUwYvnuyeuKmdw+ykiCxKt5zBYodSU6yPkFI34Ex/VBiwqt6nJ0hU1 xDNbmudN0GI/ckUezq0ko6AXPnmouiq0QPGVf0EXRaFQxwjbyb+f9zK+xmo/v2k3 5Mz3+otQ8i5cyunY9ziQf58WKMXY/CmerXXv4/gnfwz6EfdZZGsNc1niJcsXxDs= =D7er -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/mst/tags/for_upstream' into staging acpi, pc features pxb support for q35 nvdimm support most of ipmi support part of DSDT rewrite Signed-off-by: Michael S. Tsirkin <mst@redhat.com> # gpg: Signature made Tue 22 Dec 2015 16:47:18 GMT using RSA key ID D28D5469 # gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>" # gpg: aka "Michael S. Tsirkin <mst@redhat.com>" * remotes/mst/tags/for_upstream: (55 commits) acpi: extend aml_and() to accept target argument acpi: extend aml_or() to accept target argument acpi add aml_dma() acpi: add aml_to_buffer() acpi: add aml_to_hexstring() acpi: extend aml_field() to support LockRule acpi: add aml_lgreater() acpi: add aml_lor() acpi: add aml_sleep() acpi: add aml_alias() acpi: extend aml_shiftright() to accept target argument acpi: add aml_to_integer() acpi: add aml_call0() helper acpi: add aml_decrement() and aml_subtract() acpi: extend aml_add() to accept target argument acpi: aml: add helper for Opcode Arg2 Arg2 [Dst] AML pattern acpi: add aml_create_qword_field() acpi: add aml_mutex(), aml_acquire(), aml_release() acpi: add aml_lgreater_equal() acpi: add aml_sizeof ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
5fbba56073
|
@ -941,6 +941,13 @@ M: Jiri Pirko <jiri@resnulli.us>
|
|||
S: Maintained
|
||||
F: hw/net/rocker/
|
||||
|
||||
NVDIMM
|
||||
M: Xiao Guangrong <guangrong.xiao@linux.intel.com>
|
||||
S: Maintained
|
||||
F: hw/acpi/nvdimm.c
|
||||
F: hw/mem/nvdimm.c
|
||||
F: include/hw/mem/nvdimm.h
|
||||
|
||||
Subsystems
|
||||
----------
|
||||
Audio
|
||||
|
|
|
@ -9,6 +9,11 @@ CONFIG_VGA_CIRRUS=y
|
|||
CONFIG_VMWARE_VGA=y
|
||||
CONFIG_VIRTIO_VGA=y
|
||||
CONFIG_VMMOUSE=y
|
||||
CONFIG_IPMI=y
|
||||
CONFIG_IPMI_LOCAL=y
|
||||
CONFIG_IPMI_EXTERN=y
|
||||
CONFIG_ISA_IPMI_KCS=y
|
||||
CONFIG_ISA_IPMI_BT=y
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_PARALLEL=y
|
||||
CONFIG_I8254=y
|
||||
|
@ -46,6 +51,8 @@ CONFIG_APIC=y
|
|||
CONFIG_IOAPIC=y
|
||||
CONFIG_PVPANIC=y
|
||||
CONFIG_MEM_HOTPLUG=y
|
||||
CONFIG_NVDIMM=y
|
||||
CONFIG_ACPI_NVDIMM=y
|
||||
CONFIG_XIO3130=y
|
||||
CONFIG_IOH3420=y
|
||||
CONFIG_I82801B11=y
|
||||
|
|
|
@ -9,6 +9,11 @@ CONFIG_VGA_CIRRUS=y
|
|||
CONFIG_VMWARE_VGA=y
|
||||
CONFIG_VIRTIO_VGA=y
|
||||
CONFIG_VMMOUSE=y
|
||||
CONFIG_IPMI=y
|
||||
CONFIG_IPMI_LOCAL=y
|
||||
CONFIG_IPMI_EXTERN=y
|
||||
CONFIG_ISA_IPMI_KCS=y
|
||||
CONFIG_ISA_IPMI_BT=y
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_PARALLEL=y
|
||||
CONFIG_I8254=y
|
||||
|
@ -46,6 +51,8 @@ CONFIG_APIC=y
|
|||
CONFIG_IOAPIC=y
|
||||
CONFIG_PVPANIC=y
|
||||
CONFIG_MEM_HOTPLUG=y
|
||||
CONFIG_NVDIMM=y
|
||||
CONFIG_ACPI_NVDIMM=y
|
||||
CONFIG_XIO3130=y
|
||||
CONFIG_IOH3420=y
|
||||
CONFIG_I82801B11=y
|
||||
|
|
|
@ -23,9 +23,9 @@ A detailed command line would be:
|
|||
-m 2G
|
||||
-object memory-backend-ram,size=1024M,policy=bind,host-nodes=0,id=ram-node0 -numa node,nodeid=0,cpus=0,memdev=ram-node0
|
||||
-object memory-backend-ram,size=1024M,policy=bind,host-nodes=1,id=ram-node1 -numa node,nodeid=1,cpus=1,memdev=ram-node1
|
||||
-device pxb,id=bridge1,bus=pci.0,numa_node=1,bus_nr=4 -netdev user,id=nd-device e1000,bus=bridge1,addr=0x4,netdev=nd
|
||||
-device pxb,id=bridge2,bus=pci.0,numa_node=0,bus_nr=8,bus=pci.0 -device e1000,bus=bridge2,addr=0x3
|
||||
-device pxb,id=bridge3,bus=pci.0,bus_nr=40,bus=pci.0 -drive if=none,id=drive0,file=[img] -device virtio-blk-pci,drive=drive0,scsi=off,bus=bridge3,addr=1
|
||||
-device pxb,id=bridge1,bus=pci.0,numa_node=1,bus_nr=4 -netdev user,id=nd -device e1000,bus=bridge1,addr=0x4,netdev=nd
|
||||
-device pxb,id=bridge2,bus=pci.0,numa_node=0,bus_nr=8, -device e1000,bus=bridge2,addr=0x3
|
||||
-device pxb,id=bridge3,bus=pci.0,bus_nr=40, -drive if=none,id=drive0,file=[img] -device virtio-blk-pci,drive=drive0,scsi=off,bus=bridge3,addr=1
|
||||
|
||||
Here you have:
|
||||
- 2 NUMA nodes for the guest, 0 and 1. (both mapped to the same NUMA node in host, but you can and should put it in different host NUMA nodes)
|
||||
|
|
|
@ -13,6 +13,7 @@ devices-dirs-$(CONFIG_SOFTMMU) += ide/
|
|||
devices-dirs-$(CONFIG_SOFTMMU) += input/
|
||||
devices-dirs-$(CONFIG_SOFTMMU) += intc/
|
||||
devices-dirs-$(CONFIG_IPACK) += ipack/
|
||||
devices-dirs-$(CONFIG_IPMI) += ipmi/
|
||||
devices-dirs-$(CONFIG_SOFTMMU) += isa/
|
||||
devices-dirs-$(CONFIG_SOFTMMU) += misc/
|
||||
devices-dirs-$(CONFIG_SOFTMMU) += net/
|
||||
|
|
|
@ -2,6 +2,7 @@ common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o pcihp.o
|
|||
common-obj-$(CONFIG_ACPI_X86_ICH) += ich9.o tco.o
|
||||
common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o
|
||||
common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
|
||||
common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
|
||||
common-obj-$(CONFIG_ACPI) += acpi_interface.o
|
||||
common-obj-$(CONFIG_ACPI) += bios-linker-loader.o
|
||||
common-obj-$(CONFIG_ACPI) += aml-build.o
|
||||
|
|
|
@ -427,6 +427,41 @@ Aml *aml_arg(int pos)
|
|||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToInteger */
|
||||
Aml *aml_to_integer(Aml *arg)
|
||||
{
|
||||
Aml *var = aml_opcode(0x99 /* ToIntegerOp */);
|
||||
aml_append(var, arg);
|
||||
build_append_byte(var->buf, 0x00 /* NullNameOp */);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToHexString */
|
||||
Aml *aml_to_hexstring(Aml *src, Aml *dst)
|
||||
{
|
||||
Aml *var = aml_opcode(0x98 /* ToHexStringOp */);
|
||||
aml_append(var, src);
|
||||
if (dst) {
|
||||
aml_append(var, dst);
|
||||
} else {
|
||||
build_append_byte(var->buf, 0x00 /* NullNameOp */);
|
||||
}
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToBuffer */
|
||||
Aml *aml_to_buffer(Aml *src, Aml *dst)
|
||||
{
|
||||
Aml *var = aml_opcode(0x96 /* ToBufferOp */);
|
||||
aml_append(var, src);
|
||||
if (dst) {
|
||||
aml_append(var, dst);
|
||||
} else {
|
||||
build_append_byte(var->buf, 0x00 /* NullNameOp */);
|
||||
}
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefStore */
|
||||
Aml *aml_store(Aml *val, Aml *target)
|
||||
{
|
||||
|
@ -436,44 +471,64 @@ Aml *aml_store(Aml *val, Aml *target)
|
|||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAnd */
|
||||
Aml *aml_and(Aml *arg1, Aml *arg2)
|
||||
/**
|
||||
* build_opcode_2arg_dst:
|
||||
* @op: 1-byte opcode
|
||||
* @arg1: 1st operand
|
||||
* @arg2: 2nd operand
|
||||
* @dst: optional target to store to, set to NULL if it's not required
|
||||
*
|
||||
* An internal helper to compose AML terms that have
|
||||
* "Op Operand Operand Target"
|
||||
* pattern.
|
||||
*
|
||||
* Returns: The newly allocated and composed according to patter Aml object.
|
||||
*/
|
||||
static Aml *
|
||||
build_opcode_2arg_dst(uint8_t op, Aml *arg1, Aml *arg2, Aml *dst)
|
||||
{
|
||||
Aml *var = aml_opcode(0x7B /* AndOp */);
|
||||
Aml *var = aml_opcode(op);
|
||||
aml_append(var, arg1);
|
||||
aml_append(var, arg2);
|
||||
build_append_byte(var->buf, 0x00 /* NullNameOp */);
|
||||
if (dst) {
|
||||
aml_append(var, dst);
|
||||
} else {
|
||||
build_append_byte(var->buf, 0x00 /* NullNameOp */);
|
||||
}
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefOr */
|
||||
Aml *aml_or(Aml *arg1, Aml *arg2)
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAnd */
|
||||
Aml *aml_and(Aml *arg1, Aml *arg2, Aml *dst)
|
||||
{
|
||||
Aml *var = aml_opcode(0x7D /* OrOp */);
|
||||
return build_opcode_2arg_dst(0x7B /* AndOp */, arg1, arg2, dst);
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefOr */
|
||||
Aml *aml_or(Aml *arg1, Aml *arg2, Aml *dst)
|
||||
{
|
||||
return build_opcode_2arg_dst(0x7D /* OrOp */, arg1, arg2, dst);
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLOr */
|
||||
Aml *aml_lor(Aml *arg1, Aml *arg2)
|
||||
{
|
||||
Aml *var = aml_opcode(0x91 /* LOrOp */);
|
||||
aml_append(var, arg1);
|
||||
aml_append(var, arg2);
|
||||
build_append_byte(var->buf, 0x00 /* NullNameOp */);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefShiftLeft */
|
||||
Aml *aml_shiftleft(Aml *arg1, Aml *count)
|
||||
{
|
||||
Aml *var = aml_opcode(0x79 /* ShiftLeftOp */);
|
||||
aml_append(var, arg1);
|
||||
aml_append(var, count);
|
||||
build_append_byte(var->buf, 0x00); /* NullNameOp */
|
||||
return var;
|
||||
return build_opcode_2arg_dst(0x79 /* ShiftLeftOp */, arg1, count, NULL);
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefShiftRight */
|
||||
Aml *aml_shiftright(Aml *arg1, Aml *count)
|
||||
Aml *aml_shiftright(Aml *arg1, Aml *count, Aml *dst)
|
||||
{
|
||||
Aml *var = aml_opcode(0x7A /* ShiftRightOp */);
|
||||
aml_append(var, arg1);
|
||||
aml_append(var, count);
|
||||
build_append_byte(var->buf, 0x00); /* NullNameOp */
|
||||
return var;
|
||||
return build_opcode_2arg_dst(0x7A /* ShiftRightOp */, arg1, count, dst);
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLLess */
|
||||
|
@ -486,13 +541,15 @@ Aml *aml_lless(Aml *arg1, Aml *arg2)
|
|||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAdd */
|
||||
Aml *aml_add(Aml *arg1, Aml *arg2)
|
||||
Aml *aml_add(Aml *arg1, Aml *arg2, Aml *dst)
|
||||
{
|
||||
Aml *var = aml_opcode(0x72 /* AddOp */);
|
||||
aml_append(var, arg1);
|
||||
aml_append(var, arg2);
|
||||
build_append_byte(var->buf, 0x00 /* NullNameOp */);
|
||||
return var;
|
||||
return build_opcode_2arg_dst(0x72 /* AddOp */, arg1, arg2, dst);
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefSubtract */
|
||||
Aml *aml_subtract(Aml *arg1, Aml *arg2, Aml *dst)
|
||||
{
|
||||
return build_opcode_2arg_dst(0x74 /* SubtractOp */, arg1, arg2, dst);
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefIncrement */
|
||||
|
@ -503,14 +560,18 @@ Aml *aml_increment(Aml *arg)
|
|||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefDecrement */
|
||||
Aml *aml_decrement(Aml *arg)
|
||||
{
|
||||
Aml *var = aml_opcode(0x76 /* DecrementOp */);
|
||||
aml_append(var, arg);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefIndex */
|
||||
Aml *aml_index(Aml *arg1, Aml *idx)
|
||||
{
|
||||
Aml *var = aml_opcode(0x88 /* IndexOp */);
|
||||
aml_append(var, arg1);
|
||||
aml_append(var, idx);
|
||||
build_append_byte(var->buf, 0x00 /* NullNameOp */);
|
||||
return var;
|
||||
return build_opcode_2arg_dst(0x88 /* IndexOp */, arg1, idx, NULL);
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefNotify */
|
||||
|
@ -522,6 +583,14 @@ Aml *aml_notify(Aml *arg1, Aml *arg2)
|
|||
return var;
|
||||
}
|
||||
|
||||
/* helper to call method with 1 argument */
|
||||
Aml *aml_call0(const char *method)
|
||||
{
|
||||
Aml *var = aml_alloc();
|
||||
build_append_namestring(var->buf, "%s", method);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* helper to call method with 1 argument */
|
||||
Aml *aml_call1(const char *method, Aml *arg1)
|
||||
{
|
||||
|
@ -764,6 +833,26 @@ Aml *aml_equal(Aml *arg1, Aml *arg2)
|
|||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLGreater */
|
||||
Aml *aml_lgreater(Aml *arg1, Aml *arg2)
|
||||
{
|
||||
Aml *var = aml_opcode(0x94 /* LGreaterOp */);
|
||||
aml_append(var, arg1);
|
||||
aml_append(var, arg2);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLGreaterEqual */
|
||||
Aml *aml_lgreater_equal(Aml *arg1, Aml *arg2)
|
||||
{
|
||||
/* LGreaterEqualOp := LNotOp LLessOp */
|
||||
Aml *var = aml_opcode(0x92 /* LNotOp */);
|
||||
build_append_byte(var->buf, 0x95 /* LLessOp */);
|
||||
aml_append(var, arg1);
|
||||
aml_append(var, arg2);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefIfElse */
|
||||
Aml *aml_if(Aml *predicate)
|
||||
{
|
||||
|
@ -889,27 +978,43 @@ Aml *aml_reserved_field(unsigned length)
|
|||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefField */
|
||||
Aml *aml_field(const char *name, AmlAccessType type, AmlUpdateRule rule)
|
||||
Aml *aml_field(const char *name, AmlAccessType type, AmlLockRule lock,
|
||||
AmlUpdateRule rule)
|
||||
{
|
||||
Aml *var = aml_bundle(0x81 /* FieldOp */, AML_EXT_PACKAGE);
|
||||
uint8_t flags = rule << 5 | type;
|
||||
|
||||
flags |= lock << 4; /* LockRule at 4 bit offset */
|
||||
|
||||
build_append_namestring(var->buf, "%s", name);
|
||||
build_append_byte(var->buf, flags);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefCreateDWordField */
|
||||
Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name)
|
||||
static
|
||||
Aml *create_field_common(int opcode, Aml *srcbuf, Aml *index, const char *name)
|
||||
{
|
||||
Aml *var = aml_alloc();
|
||||
build_append_byte(var->buf, 0x8A); /* CreateDWordFieldOp */
|
||||
Aml *var = aml_opcode(opcode);
|
||||
aml_append(var, srcbuf);
|
||||
aml_append(var, index);
|
||||
build_append_namestring(var->buf, "%s", name);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefCreateDWordField */
|
||||
Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name)
|
||||
{
|
||||
return create_field_common(0x8A /* CreateDWordFieldOp */,
|
||||
srcbuf, index, name);
|
||||
}
|
||||
|
||||
/* ACPI 2.0a: 17.2.4.2 Named Objects Encoding: DefCreateQWordField */
|
||||
Aml *aml_create_qword_field(Aml *srcbuf, Aml *index, const char *name)
|
||||
{
|
||||
return create_field_common(0x8F /* CreateQWordFieldOp */,
|
||||
srcbuf, index, name);
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.3 Data Objects Encoding: String */
|
||||
Aml *aml_string(const char *name_format, ...)
|
||||
{
|
||||
|
@ -1170,6 +1275,30 @@ Aml *aml_qword_memory(AmlDecode dec, AmlMinFixed min_fixed,
|
|||
addr_trans, len, flags);
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 6.4.2.2 DMA Format/6.4.2.2.1 ASL Macro for DMA Descriptor */
|
||||
Aml *aml_dma(AmlDmaType typ, AmlDmaBusMaster bm, AmlTransferSize sz,
|
||||
uint8_t channel)
|
||||
{
|
||||
Aml *var = aml_alloc();
|
||||
uint8_t flags = sz | bm << 2 | typ << 5;
|
||||
|
||||
assert(channel < 8);
|
||||
build_append_byte(var->buf, 0x2A); /* Byte 0: DMA Descriptor */
|
||||
build_append_byte(var->buf, 1U << channel); /* Byte 1: _DMA - DmaChannel */
|
||||
build_append_byte(var->buf, flags); /* Byte 2 */
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefSleep */
|
||||
Aml *aml_sleep(uint64_t msec)
|
||||
{
|
||||
Aml *var = aml_alloc();
|
||||
build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
|
||||
build_append_byte(var->buf, 0x22); /* SleepOp */
|
||||
aml_append(var, aml_int(msec));
|
||||
return var;
|
||||
}
|
||||
|
||||
static uint8_t Hex2Byte(const char *src)
|
||||
{
|
||||
int hi, lo;
|
||||
|
@ -1240,16 +1369,81 @@ Aml *aml_unicode(const char *str)
|
|||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefDerefOf */
|
||||
Aml *aml_derefof(Aml *arg)
|
||||
{
|
||||
Aml *var = aml_opcode(0x83 /* DerefOfOp */);
|
||||
aml_append(var, arg);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefSizeOf */
|
||||
Aml *aml_sizeof(Aml *arg)
|
||||
{
|
||||
Aml *var = aml_opcode(0x87 /* SizeOfOp */);
|
||||
aml_append(var, arg);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefMutex */
|
||||
Aml *aml_mutex(const char *name, uint8_t sync_level)
|
||||
{
|
||||
Aml *var = aml_alloc();
|
||||
build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
|
||||
build_append_byte(var->buf, 0x01); /* MutexOp */
|
||||
build_append_namestring(var->buf, "%s", name);
|
||||
assert(!(sync_level & 0xF0));
|
||||
build_append_byte(var->buf, sync_level);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAcquire */
|
||||
Aml *aml_acquire(Aml *mutex, uint16_t timeout)
|
||||
{
|
||||
Aml *var = aml_alloc();
|
||||
build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
|
||||
build_append_byte(var->buf, 0x23); /* AcquireOp */
|
||||
aml_append(var, mutex);
|
||||
build_append_int_noprefix(var->buf, timeout, sizeof(timeout));
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefRelease */
|
||||
Aml *aml_release(Aml *mutex)
|
||||
{
|
||||
Aml *var = aml_alloc();
|
||||
build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
|
||||
build_append_byte(var->buf, 0x27); /* ReleaseOp */
|
||||
aml_append(var, mutex);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.1 Name Space Modifier Objects Encoding: DefAlias */
|
||||
Aml *aml_alias(const char *source_object, const char *alias_object)
|
||||
{
|
||||
Aml *var = aml_opcode(0x06 /* AliasOp */);
|
||||
aml_append(var, aml_name("%s", source_object));
|
||||
aml_append(var, aml_name("%s", alias_object));
|
||||
return var;
|
||||
}
|
||||
|
||||
void
|
||||
build_header(GArray *linker, GArray *table_data,
|
||||
AcpiTableHeader *h, const char *sig, int len, uint8_t rev)
|
||||
AcpiTableHeader *h, const char *sig, int len, uint8_t rev,
|
||||
const char *oem_table_id)
|
||||
{
|
||||
memcpy(&h->signature, sig, 4);
|
||||
h->length = cpu_to_le32(len);
|
||||
h->revision = rev;
|
||||
memcpy(h->oem_id, ACPI_BUILD_APPNAME6, 6);
|
||||
memcpy(h->oem_table_id, ACPI_BUILD_APPNAME4, 4);
|
||||
memcpy(h->oem_table_id + 4, sig, 4);
|
||||
|
||||
if (oem_table_id) {
|
||||
strncpy((char *)h->oem_table_id, oem_table_id, sizeof(h->oem_table_id));
|
||||
} else {
|
||||
memcpy(h->oem_table_id, ACPI_BUILD_APPNAME4, 4);
|
||||
memcpy(h->oem_table_id + 4, sig, 4);
|
||||
}
|
||||
|
||||
h->oem_revision = cpu_to_le32(1);
|
||||
memcpy(h->asl_compiler_id, ACPI_BUILD_APPNAME4, 4);
|
||||
h->asl_compiler_revision = cpu_to_le32(1);
|
||||
|
@ -1316,5 +1510,5 @@ build_rsdt(GArray *table_data, GArray *linker, GArray *table_offsets)
|
|||
sizeof(uint32_t));
|
||||
}
|
||||
build_header(linker, table_data,
|
||||
(void *)rsdt, "RSDT", rsdt_len, 1);
|
||||
(void *)rsdt, "RSDT", rsdt_len, 1, NULL);
|
||||
}
|
||||
|
|
|
@ -231,6 +231,11 @@ void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st,
|
|||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
MemStatus *mdev;
|
||||
DeviceClass *dc = DEVICE_GET_CLASS(dev);
|
||||
|
||||
if (!dc->hotpluggable) {
|
||||
return;
|
||||
}
|
||||
|
||||
mdev = acpi_memory_slot_status(mem_st, dev, errp);
|
||||
if (!mdev) {
|
||||
|
|
|
@ -0,0 +1,488 @@
|
|||
/*
|
||||
* NVDIMM ACPI Implementation
|
||||
*
|
||||
* Copyright(C) 2015 Intel Corporation.
|
||||
*
|
||||
* Author:
|
||||
* Xiao Guangrong <guangrong.xiao@linux.intel.com>
|
||||
*
|
||||
* NFIT is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT)
|
||||
* and the DSM specification can be found at:
|
||||
* http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf
|
||||
*
|
||||
* Currently, it only supports PMEM Virtualization.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "hw/acpi/acpi.h"
|
||||
#include "hw/acpi/aml-build.h"
|
||||
#include "hw/mem/nvdimm.h"
|
||||
|
||||
static int nvdimm_plugged_device_list(Object *obj, void *opaque)
|
||||
{
|
||||
GSList **list = opaque;
|
||||
|
||||
if (object_dynamic_cast(obj, TYPE_NVDIMM)) {
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
|
||||
if (dev->realized) { /* only realized NVDIMMs matter */
|
||||
*list = g_slist_append(*list, DEVICE(obj));
|
||||
}
|
||||
}
|
||||
|
||||
object_child_foreach(obj, nvdimm_plugged_device_list, opaque);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* inquire plugged NVDIMM devices and link them into the list which is
|
||||
* returned to the caller.
|
||||
*
|
||||
* Note: it is the caller's responsibility to free the list to avoid
|
||||
* memory leak.
|
||||
*/
|
||||
static GSList *nvdimm_get_plugged_device_list(void)
|
||||
{
|
||||
GSList *list = NULL;
|
||||
|
||||
object_child_foreach(qdev_get_machine(), nvdimm_plugged_device_list,
|
||||
&list);
|
||||
return list;
|
||||
}
|
||||
|
||||
#define NVDIMM_UUID_LE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \
|
||||
{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \
|
||||
(b) & 0xff, ((b) >> 8) & 0xff, (c) & 0xff, ((c) >> 8) & 0xff, \
|
||||
(d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }
|
||||
|
||||
/*
|
||||
* define Byte Addressable Persistent Memory (PM) Region according to
|
||||
* ACPI 6.0: 5.2.25.1 System Physical Address Range Structure.
|
||||
*/
|
||||
static const uint8_t nvdimm_nfit_spa_uuid[] =
|
||||
NVDIMM_UUID_LE(0x66f0d379, 0xb4f3, 0x4074, 0xac, 0x43, 0x0d, 0x33,
|
||||
0x18, 0xb7, 0x8c, 0xdb);
|
||||
|
||||
/*
|
||||
* NVDIMM Firmware Interface Table
|
||||
* @signature: "NFIT"
|
||||
*
|
||||
* It provides information that allows OSPM to enumerate NVDIMM present in
|
||||
* the platform and associate system physical address ranges created by the
|
||||
* NVDIMMs.
|
||||
*
|
||||
* It is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT)
|
||||
*/
|
||||
struct NvdimmNfitHeader {
|
||||
ACPI_TABLE_HEADER_DEF
|
||||
uint32_t reserved;
|
||||
} QEMU_PACKED;
|
||||
typedef struct NvdimmNfitHeader NvdimmNfitHeader;
|
||||
|
||||
/*
|
||||
* define NFIT structures according to ACPI 6.0: 5.2.25 NVDIMM Firmware
|
||||
* Interface Table (NFIT).
|
||||
*/
|
||||
|
||||
/*
|
||||
* System Physical Address Range Structure
|
||||
*
|
||||
* It describes the system physical address ranges occupied by NVDIMMs and
|
||||
* the types of the regions.
|
||||
*/
|
||||
struct NvdimmNfitSpa {
|
||||
uint16_t type;
|
||||
uint16_t length;
|
||||
uint16_t spa_index;
|
||||
uint16_t flags;
|
||||
uint32_t reserved;
|
||||
uint32_t proximity_domain;
|
||||
uint8_t type_guid[16];
|
||||
uint64_t spa_base;
|
||||
uint64_t spa_length;
|
||||
uint64_t mem_attr;
|
||||
} QEMU_PACKED;
|
||||
typedef struct NvdimmNfitSpa NvdimmNfitSpa;
|
||||
|
||||
/*
|
||||
* Memory Device to System Physical Address Range Mapping Structure
|
||||
*
|
||||
* It enables identifying each NVDIMM region and the corresponding SPA
|
||||
* describing the memory interleave
|
||||
*/
|
||||
struct NvdimmNfitMemDev {
|
||||
uint16_t type;
|
||||
uint16_t length;
|
||||
uint32_t nfit_handle;
|
||||
uint16_t phys_id;
|
||||
uint16_t region_id;
|
||||
uint16_t spa_index;
|
||||
uint16_t dcr_index;
|
||||
uint64_t region_len;
|
||||
uint64_t region_offset;
|
||||
uint64_t region_dpa;
|
||||
uint16_t interleave_index;
|
||||
uint16_t interleave_ways;
|
||||
uint16_t flags;
|
||||
uint16_t reserved;
|
||||
} QEMU_PACKED;
|
||||
typedef struct NvdimmNfitMemDev NvdimmNfitMemDev;
|
||||
|
||||
/*
|
||||
* NVDIMM Control Region Structure
|
||||
*
|
||||
* It describes the NVDIMM and if applicable, Block Control Window.
|
||||
*/
|
||||
struct NvdimmNfitControlRegion {
|
||||
uint16_t type;
|
||||
uint16_t length;
|
||||
uint16_t dcr_index;
|
||||
uint16_t vendor_id;
|
||||
uint16_t device_id;
|
||||
uint16_t revision_id;
|
||||
uint16_t sub_vendor_id;
|
||||
uint16_t sub_device_id;
|
||||
uint16_t sub_revision_id;
|
||||
uint8_t reserved[6];
|
||||
uint32_t serial_number;
|
||||
uint16_t fic;
|
||||
uint16_t num_bcw;
|
||||
uint64_t bcw_size;
|
||||
uint64_t cmd_offset;
|
||||
uint64_t cmd_size;
|
||||
uint64_t status_offset;
|
||||
uint64_t status_size;
|
||||
uint16_t flags;
|
||||
uint8_t reserved2[6];
|
||||
} QEMU_PACKED;
|
||||
typedef struct NvdimmNfitControlRegion NvdimmNfitControlRegion;
|
||||
|
||||
/*
|
||||
* Module serial number is a unique number for each device. We use the
|
||||
* slot id of NVDIMM device to generate this number so that each device
|
||||
* associates with a different number.
|
||||
*
|
||||
* 0x123456 is a magic number we arbitrarily chose.
|
||||
*/
|
||||
static uint32_t nvdimm_slot_to_sn(int slot)
|
||||
{
|
||||
return 0x123456 + slot;
|
||||
}
|
||||
|
||||
/*
|
||||
* handle is used to uniquely associate nfit_memdev structure with NVDIMM
|
||||
* ACPI device - nfit_memdev.nfit_handle matches with the value returned
|
||||
* by ACPI device _ADR method.
|
||||
*
|
||||
* We generate the handle with the slot id of NVDIMM device and reserve
|
||||
* 0 for NVDIMM root device.
|
||||
*/
|
||||
static uint32_t nvdimm_slot_to_handle(int slot)
|
||||
{
|
||||
return slot + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* index uniquely identifies the structure, 0 is reserved which indicates
|
||||
* that the structure is not valid or the associated structure is not
|
||||
* present.
|
||||
*
|
||||
* Each NVDIMM device needs two indexes, one for nfit_spa and another for
|
||||
* nfit_dc which are generated by the slot id of NVDIMM device.
|
||||
*/
|
||||
static uint16_t nvdimm_slot_to_spa_index(int slot)
|
||||
{
|
||||
return (slot + 1) << 1;
|
||||
}
|
||||
|
||||
/* See the comments of nvdimm_slot_to_spa_index(). */
|
||||
static uint32_t nvdimm_slot_to_dcr_index(int slot)
|
||||
{
|
||||
return nvdimm_slot_to_spa_index(slot) + 1;
|
||||
}
|
||||
|
||||
/* ACPI 6.0: 5.2.25.1 System Physical Address Range Structure */
|
||||
static void
|
||||
nvdimm_build_structure_spa(GArray *structures, DeviceState *dev)
|
||||
{
|
||||
NvdimmNfitSpa *nfit_spa;
|
||||
uint64_t addr = object_property_get_int(OBJECT(dev), PC_DIMM_ADDR_PROP,
|
||||
NULL);
|
||||
uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP,
|
||||
NULL);
|
||||
uint32_t node = object_property_get_int(OBJECT(dev), PC_DIMM_NODE_PROP,
|
||||
NULL);
|
||||
int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP,
|
||||
NULL);
|
||||
|
||||
nfit_spa = acpi_data_push(structures, sizeof(*nfit_spa));
|
||||
|
||||
nfit_spa->type = cpu_to_le16(0 /* System Physical Address Range
|
||||
Structure */);
|
||||
nfit_spa->length = cpu_to_le16(sizeof(*nfit_spa));
|
||||
nfit_spa->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot));
|
||||
|
||||
/*
|
||||
* Control region is strict as all the device info, such as SN, index,
|
||||
* is associated with slot id.
|
||||
*/
|
||||
nfit_spa->flags = cpu_to_le16(1 /* Control region is strictly for
|
||||
management during hot add/online
|
||||
operation */ |
|
||||
2 /* Data in Proximity Domain field is
|
||||
valid*/);
|
||||
|
||||
/* NUMA node. */
|
||||
nfit_spa->proximity_domain = cpu_to_le32(node);
|
||||
/* the region reported as PMEM. */
|
||||
memcpy(nfit_spa->type_guid, nvdimm_nfit_spa_uuid,
|
||||
sizeof(nvdimm_nfit_spa_uuid));
|
||||
|
||||
nfit_spa->spa_base = cpu_to_le64(addr);
|
||||
nfit_spa->spa_length = cpu_to_le64(size);
|
||||
|
||||
/* It is the PMEM and can be cached as writeback. */
|
||||
nfit_spa->mem_attr = cpu_to_le64(0x8ULL /* EFI_MEMORY_WB */ |
|
||||
0x8000ULL /* EFI_MEMORY_NV */);
|
||||
}
|
||||
|
||||
/*
|
||||
* ACPI 6.0: 5.2.25.2 Memory Device to System Physical Address Range Mapping
|
||||
* Structure
|
||||
*/
|
||||
static void
|
||||
nvdimm_build_structure_memdev(GArray *structures, DeviceState *dev)
|
||||
{
|
||||
NvdimmNfitMemDev *nfit_memdev;
|
||||
uint64_t addr = object_property_get_int(OBJECT(dev), PC_DIMM_ADDR_PROP,
|
||||
NULL);
|
||||
uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP,
|
||||
NULL);
|
||||
int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP,
|
||||
NULL);
|
||||
uint32_t handle = nvdimm_slot_to_handle(slot);
|
||||
|
||||
nfit_memdev = acpi_data_push(structures, sizeof(*nfit_memdev));
|
||||
|
||||
nfit_memdev->type = cpu_to_le16(1 /* Memory Device to System Address
|
||||
Range Map Structure*/);
|
||||
nfit_memdev->length = cpu_to_le16(sizeof(*nfit_memdev));
|
||||
nfit_memdev->nfit_handle = cpu_to_le32(handle);
|
||||
|
||||
/*
|
||||
* associate memory device with System Physical Address Range
|
||||
* Structure.
|
||||
*/
|
||||
nfit_memdev->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot));
|
||||
/* associate memory device with Control Region Structure. */
|
||||
nfit_memdev->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot));
|
||||
|
||||
/* The memory region on the device. */
|
||||
nfit_memdev->region_len = cpu_to_le64(size);
|
||||
nfit_memdev->region_dpa = cpu_to_le64(addr);
|
||||
|
||||
/* Only one interleave for PMEM. */
|
||||
nfit_memdev->interleave_ways = cpu_to_le16(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* ACPI 6.0: 5.2.25.5 NVDIMM Control Region Structure.
|
||||
*/
|
||||
static void nvdimm_build_structure_dcr(GArray *structures, DeviceState *dev)
|
||||
{
|
||||
NvdimmNfitControlRegion *nfit_dcr;
|
||||
int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP,
|
||||
NULL);
|
||||
uint32_t sn = nvdimm_slot_to_sn(slot);
|
||||
|
||||
nfit_dcr = acpi_data_push(structures, sizeof(*nfit_dcr));
|
||||
|
||||
nfit_dcr->type = cpu_to_le16(4 /* NVDIMM Control Region Structure */);
|
||||
nfit_dcr->length = cpu_to_le16(sizeof(*nfit_dcr));
|
||||
nfit_dcr->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot));
|
||||
|
||||
/* vendor: Intel. */
|
||||
nfit_dcr->vendor_id = cpu_to_le16(0x8086);
|
||||
nfit_dcr->device_id = cpu_to_le16(1);
|
||||
|
||||
/* The _DSM method is following Intel's DSM specification. */
|
||||
nfit_dcr->revision_id = cpu_to_le16(1 /* Current Revision supported
|
||||
in ACPI 6.0 is 1. */);
|
||||
nfit_dcr->serial_number = cpu_to_le32(sn);
|
||||
nfit_dcr->fic = cpu_to_le16(0x201 /* Format Interface Code. See Chapter
|
||||
2: NVDIMM Device Specific Method
|
||||
(DSM) in DSM Spec Rev1.*/);
|
||||
}
|
||||
|
||||
static GArray *nvdimm_build_device_structure(GSList *device_list)
|
||||
{
|
||||
GArray *structures = g_array_new(false, true /* clear */, 1);
|
||||
|
||||
for (; device_list; device_list = device_list->next) {
|
||||
DeviceState *dev = device_list->data;
|
||||
|
||||
/* build System Physical Address Range Structure. */
|
||||
nvdimm_build_structure_spa(structures, dev);
|
||||
|
||||
/*
|
||||
* build Memory Device to System Physical Address Range Mapping
|
||||
* Structure.
|
||||
*/
|
||||
nvdimm_build_structure_memdev(structures, dev);
|
||||
|
||||
/* build NVDIMM Control Region Structure. */
|
||||
nvdimm_build_structure_dcr(structures, dev);
|
||||
}
|
||||
|
||||
return structures;
|
||||
}
|
||||
|
||||
static void nvdimm_build_nfit(GSList *device_list, GArray *table_offsets,
|
||||
GArray *table_data, GArray *linker)
|
||||
{
|
||||
GArray *structures = nvdimm_build_device_structure(device_list);
|
||||
void *header;
|
||||
|
||||
acpi_add_table(table_offsets, table_data);
|
||||
|
||||
/* NFIT header. */
|
||||
header = acpi_data_push(table_data, sizeof(NvdimmNfitHeader));
|
||||
/* NVDIMM device structures. */
|
||||
g_array_append_vals(table_data, structures->data, structures->len);
|
||||
|
||||
build_header(linker, table_data, header, "NFIT",
|
||||
sizeof(NvdimmNfitHeader) + structures->len, 1, NULL);
|
||||
g_array_free(structures, true);
|
||||
}
|
||||
|
||||
#define NVDIMM_COMMON_DSM "NCAL"
|
||||
|
||||
static void nvdimm_build_common_dsm(Aml *dev)
|
||||
{
|
||||
Aml *method, *ifctx, *function;
|
||||
uint8_t byte_list[1];
|
||||
|
||||
method = aml_method(NVDIMM_COMMON_DSM, 4, AML_NOTSERIALIZED);
|
||||
function = aml_arg(2);
|
||||
|
||||
/*
|
||||
* function 0 is called to inquire what functions are supported by
|
||||
* OSPM
|
||||
*/
|
||||
ifctx = aml_if(aml_equal(function, aml_int(0)));
|
||||
byte_list[0] = 0 /* No function Supported */;
|
||||
aml_append(ifctx, aml_return(aml_buffer(1, byte_list)));
|
||||
aml_append(method, ifctx);
|
||||
|
||||
/* No function is supported yet. */
|
||||
byte_list[0] = 1 /* Not Supported */;
|
||||
aml_append(method, aml_return(aml_buffer(1, byte_list)));
|
||||
|
||||
aml_append(dev, method);
|
||||
}
|
||||
|
||||
static void nvdimm_build_device_dsm(Aml *dev)
|
||||
{
|
||||
Aml *method;
|
||||
|
||||
method = aml_method("_DSM", 4, AML_NOTSERIALIZED);
|
||||
aml_append(method, aml_return(aml_call4(NVDIMM_COMMON_DSM, aml_arg(0),
|
||||
aml_arg(1), aml_arg(2), aml_arg(3))));
|
||||
aml_append(dev, method);
|
||||
}
|
||||
|
||||
static void nvdimm_build_nvdimm_devices(GSList *device_list, Aml *root_dev)
|
||||
{
|
||||
for (; device_list; device_list = device_list->next) {
|
||||
DeviceState *dev = device_list->data;
|
||||
int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP,
|
||||
NULL);
|
||||
uint32_t handle = nvdimm_slot_to_handle(slot);
|
||||
Aml *nvdimm_dev;
|
||||
|
||||
nvdimm_dev = aml_device("NV%02X", slot);
|
||||
|
||||
/*
|
||||
* ACPI 6.0: 9.20 NVDIMM Devices:
|
||||
*
|
||||
* _ADR object that is used to supply OSPM with unique address
|
||||
* of the NVDIMM device. This is done by returning the NFIT Device
|
||||
* handle that is used to identify the associated entries in ACPI
|
||||
* table NFIT or _FIT.
|
||||
*/
|
||||
aml_append(nvdimm_dev, aml_name_decl("_ADR", aml_int(handle)));
|
||||
|
||||
nvdimm_build_device_dsm(nvdimm_dev);
|
||||
aml_append(root_dev, nvdimm_dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void nvdimm_build_ssdt(GSList *device_list, GArray *table_offsets,
|
||||
GArray *table_data, GArray *linker)
|
||||
{
|
||||
Aml *ssdt, *sb_scope, *dev;
|
||||
|
||||
acpi_add_table(table_offsets, table_data);
|
||||
|
||||
ssdt = init_aml_allocator();
|
||||
acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader));
|
||||
|
||||
sb_scope = aml_scope("\\_SB");
|
||||
|
||||
dev = aml_device("NVDR");
|
||||
|
||||
/*
|
||||
* ACPI 6.0: 9.20 NVDIMM Devices:
|
||||
*
|
||||
* The ACPI Name Space device uses _HID of ACPI0012 to identify the root
|
||||
* NVDIMM interface device. Platform firmware is required to contain one
|
||||
* such device in _SB scope if NVDIMMs support is exposed by platform to
|
||||
* OSPM.
|
||||
* For each NVDIMM present or intended to be supported by platform,
|
||||
* platform firmware also exposes an ACPI Namespace Device under the
|
||||
* root device.
|
||||
*/
|
||||
aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0012")));
|
||||
|
||||
nvdimm_build_common_dsm(dev);
|
||||
nvdimm_build_device_dsm(dev);
|
||||
|
||||
nvdimm_build_nvdimm_devices(device_list, dev);
|
||||
|
||||
aml_append(sb_scope, dev);
|
||||
|
||||
aml_append(ssdt, sb_scope);
|
||||
/* copy AML table into ACPI tables blob and patch header there */
|
||||
g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len);
|
||||
build_header(linker, table_data,
|
||||
(void *)(table_data->data + table_data->len - ssdt->buf->len),
|
||||
"SSDT", ssdt->buf->len, 1, "NVDIMM");
|
||||
free_aml_allocator();
|
||||
}
|
||||
|
||||
void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data,
|
||||
GArray *linker)
|
||||
{
|
||||
GSList *device_list;
|
||||
|
||||
/* no NVDIMM device is plugged. */
|
||||
device_list = nvdimm_get_plugged_device_list();
|
||||
if (!device_list) {
|
||||
return;
|
||||
}
|
||||
nvdimm_build_nfit(device_list, table_offsets, table_data, linker);
|
||||
nvdimm_build_ssdt(device_list, table_offsets, table_data, linker);
|
||||
g_slist_free(device_list);
|
||||
}
|
|
@ -274,16 +274,16 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap,
|
|||
aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3"));
|
||||
aml_append(ifctx, aml_store(aml_name("CDW2"), aml_name("SUPP")));
|
||||
aml_append(ifctx, aml_store(aml_name("CDW3"), aml_name("CTRL")));
|
||||
aml_append(ifctx, aml_store(aml_and(aml_name("CTRL"), aml_int(0x1D)),
|
||||
aml_append(ifctx, aml_store(aml_and(aml_name("CTRL"), aml_int(0x1D), NULL),
|
||||
aml_name("CTRL")));
|
||||
|
||||
ifctx1 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(0x1))));
|
||||
aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x08)),
|
||||
aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x08), NULL),
|
||||
aml_name("CDW1")));
|
||||
aml_append(ifctx, ifctx1);
|
||||
|
||||
ifctx1 = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), aml_name("CTRL"))));
|
||||
aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x10)),
|
||||
aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x10), NULL),
|
||||
aml_name("CDW1")));
|
||||
aml_append(ifctx, ifctx1);
|
||||
|
||||
|
@ -292,7 +292,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap,
|
|||
aml_append(method, ifctx);
|
||||
|
||||
elsectx = aml_else();
|
||||
aml_append(elsectx, aml_store(aml_or(aml_name("CDW1"), aml_int(4)),
|
||||
aml_append(elsectx, aml_store(aml_or(aml_name("CDW1"), aml_int(4), NULL),
|
||||
aml_name("CDW1")));
|
||||
aml_append(elsectx, aml_return(aml_arg(3)));
|
||||
aml_append(method, elsectx);
|
||||
|
@ -423,7 +423,8 @@ build_spcr(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
|
|||
spcr->pci_device_id = 0xffff; /* PCI Device ID: not a PCI device */
|
||||
spcr->pci_vendor_id = 0xffff; /* PCI Vendor ID: not a PCI device */
|
||||
|
||||
build_header(linker, table_data, (void *)spcr, "SPCR", sizeof(*spcr), 2);
|
||||
build_header(linker, table_data, (void *)spcr, "SPCR", sizeof(*spcr), 2,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -442,7 +443,7 @@ build_mcfg(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
|
|||
mcfg->allocation[0].end_bus_number = (memmap[VIRT_PCIE_ECAM].size
|
||||
/ PCIE_MMCFG_SIZE_MIN) - 1;
|
||||
|
||||
build_header(linker, table_data, (void *)mcfg, "MCFG", len, 1);
|
||||
build_header(linker, table_data, (void *)mcfg, "MCFG", len, 1, NULL);
|
||||
}
|
||||
|
||||
/* GTDT */
|
||||
|
@ -468,7 +469,7 @@ build_gtdt(GArray *table_data, GArray *linker)
|
|||
|
||||
build_header(linker, table_data,
|
||||
(void *)(table_data->data + gtdt_start), "GTDT",
|
||||
table_data->len - gtdt_start, 2);
|
||||
table_data->len - gtdt_start, 2, NULL);
|
||||
}
|
||||
|
||||
/* MADT */
|
||||
|
@ -530,7 +531,7 @@ build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info,
|
|||
|
||||
build_header(linker, table_data,
|
||||
(void *)(table_data->data + madt_start), "APIC",
|
||||
table_data->len - madt_start, 3);
|
||||
table_data->len - madt_start, 3, NULL);
|
||||
}
|
||||
|
||||
/* FADT */
|
||||
|
@ -555,7 +556,7 @@ build_fadt(GArray *table_data, GArray *linker, unsigned dsdt)
|
|||
sizeof fadt->dsdt);
|
||||
|
||||
build_header(linker, table_data,
|
||||
(void *)fadt, "FACP", sizeof(*fadt), 5);
|
||||
(void *)fadt, "FACP", sizeof(*fadt), 5, NULL);
|
||||
}
|
||||
|
||||
/* DSDT */
|
||||
|
@ -591,7 +592,7 @@ build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
|
|||
g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
|
||||
build_header(linker, table_data,
|
||||
(void *)(table_data->data + table_data->len - dsdt->buf->len),
|
||||
"DSDT", dsdt->buf->len, 2);
|
||||
"DSDT", dsdt->buf->len, 2, NULL);
|
||||
free_aml_allocator();
|
||||
}
|
||||
|
||||
|
|
|
@ -51,12 +51,10 @@
|
|||
#include "hw/nvram/fw_cfg.h"
|
||||
#include "exec/memory.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/boards.h"
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
bool option_rom_has_mr = false;
|
||||
bool rom_file_has_mr = true;
|
||||
|
||||
static int roms_loaded;
|
||||
|
||||
/* return the size or -1 if error */
|
||||
|
@ -754,6 +752,7 @@ int rom_add_file(const char *file, const char *fw_dir,
|
|||
hwaddr addr, int32_t bootindex,
|
||||
bool option_rom)
|
||||
{
|
||||
MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
|
||||
Rom *rom;
|
||||
int rc, fd = -1;
|
||||
char devpath[100];
|
||||
|
@ -810,7 +809,7 @@ int rom_add_file(const char *file, const char *fw_dir,
|
|||
basename);
|
||||
snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name);
|
||||
|
||||
if ((!option_rom || option_rom_has_mr) && rom_file_has_mr) {
|
||||
if ((!option_rom || mc->option_rom_has_mr) && mc->rom_file_has_mr) {
|
||||
data = rom_set_mr(rom, OBJECT(fw_cfg), devpath);
|
||||
} else {
|
||||
data = rom->data;
|
||||
|
@ -838,6 +837,7 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len,
|
|||
size_t max_len, hwaddr addr, const char *fw_file_name,
|
||||
FWCfgReadCallback fw_callback, void *callback_opaque)
|
||||
{
|
||||
MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
|
||||
Rom *rom;
|
||||
MemoryRegion *mr = NULL;
|
||||
|
||||
|
@ -855,7 +855,7 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len,
|
|||
|
||||
snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name);
|
||||
|
||||
if (rom_file_has_mr) {
|
||||
if (mc->rom_file_has_mr) {
|
||||
data = rom_set_mr(rom, OBJECT(fw_cfg), devpath);
|
||||
mr = rom->mr;
|
||||
} else {
|
||||
|
|
|
@ -342,6 +342,7 @@ static void machine_class_init(ObjectClass *oc, void *data)
|
|||
|
||||
/* Default 128 MB as guest ram size */
|
||||
mc->default_ram_size = 128 * M_BYTE;
|
||||
mc->rom_file_has_mr = true;
|
||||
}
|
||||
|
||||
static void machine_class_base_init(ObjectClass *oc, void *data)
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "hw/loader.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/acpi/memory_hotplug.h"
|
||||
#include "hw/mem/nvdimm.h"
|
||||
#include "sysemu/tpm.h"
|
||||
#include "hw/acpi/tpm.h"
|
||||
#include "sysemu/tpm_backend.h"
|
||||
|
@ -361,7 +362,7 @@ build_fadt(GArray *table_data, GArray *linker, AcpiPmInfo *pm,
|
|||
fadt_setup(fadt, pm);
|
||||
|
||||
build_header(linker, table_data,
|
||||
(void *)fadt, "FACP", sizeof(*fadt), 1);
|
||||
(void *)fadt, "FACP", sizeof(*fadt), 1, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -431,7 +432,7 @@ build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu,
|
|||
|
||||
build_header(linker, table_data,
|
||||
(void *)(table_data->data + madt_start), "APIC",
|
||||
table_data->len - madt_start, 1);
|
||||
table_data->len - madt_start, 1, NULL);
|
||||
}
|
||||
|
||||
/* Assign BSEL property to all buses. In the future, this can be changed
|
||||
|
@ -469,7 +470,7 @@ static void build_append_pcihp_notify_entry(Aml *method, int slot)
|
|||
Aml *if_ctx;
|
||||
int32_t devfn = PCI_DEVFN(slot, 0);
|
||||
|
||||
if_ctx = aml_if(aml_and(aml_arg(0), aml_int(0x1U << slot)));
|
||||
if_ctx = aml_if(aml_and(aml_arg(0), aml_int(0x1U << slot), NULL));
|
||||
aml_append(if_ctx, aml_notify(aml_name("S%.02X", devfn), aml_arg(1)));
|
||||
aml_append(method, if_ctx);
|
||||
}
|
||||
|
@ -666,10 +667,11 @@ static Aml *build_prt(void)
|
|||
|
||||
/* slot = pin >> 2 */
|
||||
aml_append(while_ctx,
|
||||
aml_store(aml_shiftright(pin, aml_int(2)), slot));
|
||||
aml_store(aml_shiftright(pin, aml_int(2), NULL), slot));
|
||||
/* lnk_idx = (slot + pin) & 3 */
|
||||
aml_append(while_ctx,
|
||||
aml_store(aml_and(aml_add(pin, slot), aml_int(3)), lnk_idx));
|
||||
aml_store(aml_and(aml_add(pin, slot, NULL), aml_int(3), NULL),
|
||||
lnk_idx));
|
||||
|
||||
/* route[2] = "LNK[D|A|B|C]", selection based on pin % 3 */
|
||||
aml_append(while_ctx, initialize_route(route, "LNKD", lnk_idx, 0));
|
||||
|
@ -679,11 +681,13 @@ static Aml *build_prt(void)
|
|||
|
||||
/* route[0] = 0x[slot]FFFF */
|
||||
aml_append(while_ctx,
|
||||
aml_store(aml_or(aml_shiftleft(slot, aml_int(16)), aml_int(0xFFFF)),
|
||||
aml_store(aml_or(aml_shiftleft(slot, aml_int(16)), aml_int(0xFFFF),
|
||||
NULL),
|
||||
aml_index(route, aml_int(0))));
|
||||
/* route[1] = pin & 3 */
|
||||
aml_append(while_ctx,
|
||||
aml_store(aml_and(pin, aml_int(3)), aml_index(route, aml_int(1))));
|
||||
aml_store(aml_and(pin, aml_int(3), NULL),
|
||||
aml_index(route, aml_int(1))));
|
||||
/* res[pin] = route */
|
||||
aml_append(while_ctx, aml_store(route, aml_index(res, pin)));
|
||||
/* pin++ */
|
||||
|
@ -762,16 +766,59 @@ static void crs_replace_with_free_ranges(GPtrArray *ranges,
|
|||
g_ptr_array_free(free_ranges, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* crs_range_merge - merges adjacent ranges in the given array.
|
||||
* Array elements are deleted and replaced with the merged ranges.
|
||||
*/
|
||||
static void crs_range_merge(GPtrArray *range)
|
||||
{
|
||||
GPtrArray *tmp = g_ptr_array_new_with_free_func(crs_range_free);
|
||||
CrsRangeEntry *entry;
|
||||
uint64_t range_base, range_limit;
|
||||
int i;
|
||||
|
||||
if (!range->len) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_ptr_array_sort(range, crs_range_compare);
|
||||
|
||||
entry = g_ptr_array_index(range, 0);
|
||||
range_base = entry->base;
|
||||
range_limit = entry->limit;
|
||||
for (i = 1; i < range->len; i++) {
|
||||
entry = g_ptr_array_index(range, i);
|
||||
if (entry->base - 1 == range_limit) {
|
||||
range_limit = entry->limit;
|
||||
} else {
|
||||
crs_range_insert(tmp, range_base, range_limit);
|
||||
range_base = entry->base;
|
||||
range_limit = entry->limit;
|
||||
}
|
||||
}
|
||||
crs_range_insert(tmp, range_base, range_limit);
|
||||
|
||||
g_ptr_array_set_size(range, 0);
|
||||
for (i = 0; i < tmp->len; i++) {
|
||||
entry = g_ptr_array_index(tmp, i);
|
||||
crs_range_insert(range, entry->base, entry->limit);
|
||||
}
|
||||
g_ptr_array_free(tmp, true);
|
||||
}
|
||||
|
||||
static Aml *build_crs(PCIHostState *host,
|
||||
GPtrArray *io_ranges, GPtrArray *mem_ranges)
|
||||
{
|
||||
Aml *crs = aml_resource_template();
|
||||
GPtrArray *host_io_ranges = g_ptr_array_new_with_free_func(crs_range_free);
|
||||
GPtrArray *host_mem_ranges = g_ptr_array_new_with_free_func(crs_range_free);
|
||||
CrsRangeEntry *entry;
|
||||
uint8_t max_bus = pci_bus_num(host->bus);
|
||||
uint8_t type;
|
||||
int devfn;
|
||||
int i;
|
||||
|
||||
for (devfn = 0; devfn < ARRAY_SIZE(host->bus->devices); devfn++) {
|
||||
int i;
|
||||
uint64_t range_base, range_limit;
|
||||
PCIDevice *dev = host->bus->devices[devfn];
|
||||
|
||||
|
@ -794,26 +841,9 @@ static Aml *build_crs(PCIHostState *host,
|
|||
}
|
||||
|
||||
if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
|
||||
aml_append(crs,
|
||||
aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
|
||||
AML_POS_DECODE, AML_ENTIRE_RANGE,
|
||||
0,
|
||||
range_base,
|
||||
range_limit,
|
||||
0,
|
||||
range_limit - range_base + 1));
|
||||
crs_range_insert(io_ranges, range_base, range_limit);
|
||||
crs_range_insert(host_io_ranges, range_base, range_limit);
|
||||
} else { /* "memory" */
|
||||
aml_append(crs,
|
||||
aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
|
||||
AML_MAX_FIXED, AML_NON_CACHEABLE,
|
||||
AML_READ_WRITE,
|
||||
0,
|
||||
range_base,
|
||||
range_limit,
|
||||
0,
|
||||
range_limit - range_base + 1));
|
||||
crs_range_insert(mem_ranges, range_base, range_limit);
|
||||
crs_range_insert(host_mem_ranges, range_base, range_limit);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -832,15 +862,7 @@ static Aml *build_crs(PCIHostState *host,
|
|||
* that do not support multiple root buses
|
||||
*/
|
||||
if (range_base && range_base <= range_limit) {
|
||||
aml_append(crs,
|
||||
aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
|
||||
AML_POS_DECODE, AML_ENTIRE_RANGE,
|
||||
0,
|
||||
range_base,
|
||||
range_limit,
|
||||
0,
|
||||
range_limit - range_base + 1));
|
||||
crs_range_insert(io_ranges, range_base, range_limit);
|
||||
crs_range_insert(host_io_ranges, range_base, range_limit);
|
||||
}
|
||||
|
||||
range_base =
|
||||
|
@ -853,16 +875,7 @@ static Aml *build_crs(PCIHostState *host,
|
|||
* that do not support multiple root buses
|
||||
*/
|
||||
if (range_base && range_base <= range_limit) {
|
||||
aml_append(crs,
|
||||
aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
|
||||
AML_MAX_FIXED, AML_NON_CACHEABLE,
|
||||
AML_READ_WRITE,
|
||||
0,
|
||||
range_base,
|
||||
range_limit,
|
||||
0,
|
||||
range_limit - range_base + 1));
|
||||
crs_range_insert(mem_ranges, range_base, range_limit);
|
||||
crs_range_insert(host_mem_ranges, range_base, range_limit);
|
||||
}
|
||||
|
||||
range_base =
|
||||
|
@ -875,20 +888,36 @@ static Aml *build_crs(PCIHostState *host,
|
|||
* that do not support multiple root buses
|
||||
*/
|
||||
if (range_base && range_base <= range_limit) {
|
||||
aml_append(crs,
|
||||
aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
|
||||
AML_MAX_FIXED, AML_NON_CACHEABLE,
|
||||
AML_READ_WRITE,
|
||||
0,
|
||||
range_base,
|
||||
range_limit,
|
||||
0,
|
||||
range_limit - range_base + 1));
|
||||
crs_range_insert(mem_ranges, range_base, range_limit);
|
||||
crs_range_insert(host_mem_ranges, range_base, range_limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crs_range_merge(host_io_ranges);
|
||||
for (i = 0; i < host_io_ranges->len; i++) {
|
||||
entry = g_ptr_array_index(host_io_ranges, i);
|
||||
aml_append(crs,
|
||||
aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
|
||||
AML_POS_DECODE, AML_ENTIRE_RANGE,
|
||||
0, entry->base, entry->limit, 0,
|
||||
entry->limit - entry->base + 1));
|
||||
crs_range_insert(io_ranges, entry->base, entry->limit);
|
||||
}
|
||||
g_ptr_array_free(host_io_ranges, true);
|
||||
|
||||
crs_range_merge(host_mem_ranges);
|
||||
for (i = 0; i < host_mem_ranges->len; i++) {
|
||||
entry = g_ptr_array_index(host_mem_ranges, i);
|
||||
aml_append(crs,
|
||||
aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
|
||||
AML_MAX_FIXED, AML_NON_CACHEABLE,
|
||||
AML_READ_WRITE,
|
||||
0, entry->base, entry->limit, 0,
|
||||
entry->limit - entry->base + 1));
|
||||
crs_range_insert(mem_ranges, entry->base, entry->limit);
|
||||
}
|
||||
g_ptr_array_free(host_mem_ranges, true);
|
||||
|
||||
aml_append(crs,
|
||||
aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE,
|
||||
0,
|
||||
|
@ -925,8 +954,7 @@ build_ssdt(GArray *table_data, GArray *linker,
|
|||
/* Reserve space for header */
|
||||
acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader));
|
||||
|
||||
/* Extra PCI root buses are implemented only for i440fx */
|
||||
bus = find_i440fx();
|
||||
bus = PC_MACHINE(machine)->bus;
|
||||
if (bus) {
|
||||
QLIST_FOREACH(bus, &bus->child, sibling) {
|
||||
uint8_t bus_num = pci_bus_num(bus);
|
||||
|
@ -1105,7 +1133,7 @@ build_ssdt(GArray *table_data, GArray *linker,
|
|||
|
||||
aml_append(dev, aml_operation_region("PEOR", AML_SYSTEM_IO,
|
||||
misc->pvpanic_port, 1));
|
||||
field = aml_field("PEOR", AML_BYTE_ACC, AML_PRESERVE);
|
||||
field = aml_field("PEOR", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||
aml_append(field, aml_named_field("PEPT", 8));
|
||||
aml_append(dev, field);
|
||||
|
||||
|
@ -1145,7 +1173,7 @@ build_ssdt(GArray *table_data, GArray *linker,
|
|||
/* declare CPU hotplug MMIO region and PRS field to access it */
|
||||
aml_append(sb_scope, aml_operation_region(
|
||||
"PRST", AML_SYSTEM_IO, pm->cpu_hp_io_base, pm->cpu_hp_io_len));
|
||||
field = aml_field("PRST", AML_BYTE_ACC, AML_PRESERVE);
|
||||
field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||
aml_append(field, aml_named_field("PRS", 256));
|
||||
aml_append(sb_scope, field);
|
||||
|
||||
|
@ -1220,7 +1248,7 @@ build_ssdt(GArray *table_data, GArray *linker,
|
|||
);
|
||||
|
||||
field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), AML_DWORD_ACC,
|
||||
AML_PRESERVE);
|
||||
AML_NOLOCK, AML_PRESERVE);
|
||||
aml_append(field, /* read only */
|
||||
aml_named_field(stringify(MEMORY_SLOT_ADDR_LOW), 32));
|
||||
aml_append(field, /* read only */
|
||||
|
@ -1234,7 +1262,7 @@ build_ssdt(GArray *table_data, GArray *linker,
|
|||
aml_append(scope, field);
|
||||
|
||||
field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), AML_BYTE_ACC,
|
||||
AML_WRITE_AS_ZEROS);
|
||||
AML_NOLOCK, AML_WRITE_AS_ZEROS);
|
||||
aml_append(field, aml_reserved_field(160 /* bits, Offset(20) */));
|
||||
aml_append(field, /* 1 if enabled, read only */
|
||||
aml_named_field(stringify(MEMORY_SLOT_ENABLED), 1));
|
||||
|
@ -1250,7 +1278,7 @@ build_ssdt(GArray *table_data, GArray *linker,
|
|||
aml_append(scope, field);
|
||||
|
||||
field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), AML_DWORD_ACC,
|
||||
AML_PRESERVE);
|
||||
AML_NOLOCK, AML_PRESERVE);
|
||||
aml_append(field, /* DIMM selector, write only */
|
||||
aml_named_field(stringify(MEMORY_SLOT_SLECTOR), 32));
|
||||
aml_append(field, /* _OST event code, write only */
|
||||
|
@ -1350,7 +1378,7 @@ build_ssdt(GArray *table_data, GArray *linker,
|
|||
g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len);
|
||||
build_header(linker, table_data,
|
||||
(void *)(table_data->data + table_data->len - ssdt->buf->len),
|
||||
"SSDT", ssdt->buf->len, 1);
|
||||
"SSDT", ssdt->buf->len, 1, NULL);
|
||||
free_aml_allocator();
|
||||
}
|
||||
|
||||
|
@ -1366,7 +1394,7 @@ build_hpet(GArray *table_data, GArray *linker)
|
|||
hpet->timer_block_id = cpu_to_le32(0x8086a201);
|
||||
hpet->addr.address = cpu_to_le64(HPET_BASE);
|
||||
build_header(linker, table_data,
|
||||
(void *)hpet, "HPET", sizeof(*hpet), 1);
|
||||
(void *)hpet, "HPET", sizeof(*hpet), 1, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1389,7 +1417,7 @@ build_tpm_tcpa(GArray *table_data, GArray *linker, GArray *tcpalog)
|
|||
sizeof(tcpa->log_area_start_address));
|
||||
|
||||
build_header(linker, table_data,
|
||||
(void *)tcpa, "TCPA", sizeof(*tcpa), 2);
|
||||
(void *)tcpa, "TCPA", sizeof(*tcpa), 2, NULL);
|
||||
|
||||
acpi_data_push(tcpalog, TPM_LOG_AREA_MINIMUM_SIZE);
|
||||
}
|
||||
|
@ -1406,7 +1434,7 @@ build_tpm2(GArray *table_data, GArray *linker)
|
|||
tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_MMIO);
|
||||
|
||||
build_header(linker, table_data,
|
||||
(void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4);
|
||||
(void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL);
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
|
@ -1520,7 +1548,7 @@ build_srat(GArray *table_data, GArray *linker, PcGuestInfo *guest_info)
|
|||
build_header(linker, table_data,
|
||||
(void *)(table_data->data + srat_start),
|
||||
"SRAT",
|
||||
table_data->len - srat_start, 1);
|
||||
table_data->len - srat_start, 1, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1549,7 +1577,7 @@ build_mcfg_q35(GArray *table_data, GArray *linker, AcpiMcfgInfo *info)
|
|||
} else {
|
||||
sig = "MCFG";
|
||||
}
|
||||
build_header(linker, table_data, (void *)mcfg, sig, len, 1);
|
||||
build_header(linker, table_data, (void *)mcfg, sig, len, 1, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1573,7 +1601,7 @@ build_dmar_q35(GArray *table_data, GArray *linker)
|
|||
drhd->address = cpu_to_le64(Q35_HOST_BRIDGE_IOMMU_ADDR);
|
||||
|
||||
build_header(linker, table_data, (void *)(table_data->data + dmar_start),
|
||||
"DMAR", table_data->len - dmar_start, 1);
|
||||
"DMAR", table_data->len - dmar_start, 1, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1588,7 +1616,7 @@ build_dsdt(GArray *table_data, GArray *linker, AcpiMiscInfo *misc)
|
|||
|
||||
memset(dsdt, 0, sizeof *dsdt);
|
||||
build_header(linker, table_data, dsdt, "DSDT",
|
||||
misc->dsdt_size, 1);
|
||||
misc->dsdt_size, 1, NULL);
|
||||
}
|
||||
|
||||
static GArray *
|
||||
|
@ -1659,6 +1687,13 @@ static bool acpi_has_iommu(void)
|
|||
return intel_iommu && !ambiguous;
|
||||
}
|
||||
|
||||
static bool acpi_has_nvdimm(void)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
|
||||
|
||||
return pcms->nvdimm;
|
||||
}
|
||||
|
||||
static
|
||||
void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
|
||||
{
|
||||
|
@ -1743,6 +1778,10 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
|
|||
build_dmar_q35(tables_blob, tables->linker);
|
||||
}
|
||||
|
||||
if (acpi_has_nvdimm()) {
|
||||
nvdimm_build_acpi(table_offsets, tables_blob, tables->linker);
|
||||
}
|
||||
|
||||
/* Add tables supplied by user (if any) */
|
||||
for (u = acpi_table_first(); u; u = acpi_table_next(u)) {
|
||||
unsigned len = acpi_table_len(u);
|
||||
|
|
53
hw/i386/pc.c
53
hw/i386/pc.c
|
@ -77,15 +77,6 @@
|
|||
#define DPRINTF(fmt, ...)
|
||||
#endif
|
||||
|
||||
/* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables
|
||||
* (128K) and other BIOS datastructures (less than 4K reported to be used at
|
||||
* the moment, 32K should be enough for a while). */
|
||||
static unsigned acpi_data_size = 0x20000 + 0x8000;
|
||||
void pc_set_legacy_acpi_data_size(void)
|
||||
{
|
||||
acpi_data_size = 0x10000;
|
||||
}
|
||||
|
||||
#define BIOS_CFG_IOPORT 0x510
|
||||
#define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0)
|
||||
#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1)
|
||||
|
@ -841,6 +832,7 @@ static void load_linux(PCMachineState *pcms,
|
|||
FILE *f;
|
||||
char *vmode;
|
||||
MachineState *machine = MACHINE(pcms);
|
||||
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
||||
const char *kernel_filename = machine->kernel_filename;
|
||||
const char *initrd_filename = machine->initrd_filename;
|
||||
const char *kernel_cmdline = machine->kernel_cmdline;
|
||||
|
@ -908,8 +900,8 @@ static void load_linux(PCMachineState *pcms,
|
|||
initrd_max = 0x37ffffff;
|
||||
}
|
||||
|
||||
if (initrd_max >= pcms->below_4g_mem_size - acpi_data_size) {
|
||||
initrd_max = pcms->below_4g_mem_size - acpi_data_size - 1;
|
||||
if (initrd_max >= pcms->below_4g_mem_size - pcmc->acpi_data_size) {
|
||||
initrd_max = pcms->below_4g_mem_size - pcmc->acpi_data_size - 1;
|
||||
}
|
||||
|
||||
fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr);
|
||||
|
@ -1175,7 +1167,7 @@ void pc_guest_info_machine_done(Notifier *notifier, void *data)
|
|||
PcGuestInfoState *guest_info_state = container_of(notifier,
|
||||
PcGuestInfoState,
|
||||
machine_done);
|
||||
PCIBus *bus = find_i440fx();
|
||||
PCIBus *bus = PC_MACHINE(qdev_get_machine())->bus;
|
||||
|
||||
if (bus) {
|
||||
int extra_hosts = 0;
|
||||
|
@ -1303,6 +1295,7 @@ FWCfgState *pc_memory_init(PCMachineState *pcms,
|
|||
MemoryRegion *ram_below_4g, *ram_above_4g;
|
||||
FWCfgState *fw_cfg;
|
||||
MachineState *machine = MACHINE(pcms);
|
||||
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
||||
|
||||
assert(machine->ram_size == pcms->below_4g_mem_size +
|
||||
pcms->above_4g_mem_size);
|
||||
|
@ -1364,7 +1357,7 @@ FWCfgState *pc_memory_init(PCMachineState *pcms,
|
|||
pcms->hotplug_memory.base =
|
||||
ROUND_UP(0x100000000ULL + pcms->above_4g_mem_size, 1ULL << 30);
|
||||
|
||||
if (pcms->enforce_aligned_dimm) {
|
||||
if (pcmc->enforce_aligned_dimm) {
|
||||
/* size hotplug region assuming 1G page max alignment per slot */
|
||||
hotplug_mem_size += (1ULL << 30) * machine->ram_slots;
|
||||
}
|
||||
|
@ -1617,12 +1610,13 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev,
|
|||
HotplugHandlerClass *hhc;
|
||||
Error *local_err = NULL;
|
||||
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
|
||||
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
||||
PCDIMMDevice *dimm = PC_DIMM(dev);
|
||||
PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
|
||||
MemoryRegion *mr = ddc->get_memory_region(dimm);
|
||||
uint64_t align = TARGET_PAGE_SIZE;
|
||||
|
||||
if (memory_region_get_alignment(mr) && pcms->enforce_aligned_dimm) {
|
||||
if (memory_region_get_alignment(mr) && pcmc->enforce_aligned_dimm) {
|
||||
align = memory_region_get_alignment(mr);
|
||||
}
|
||||
|
||||
|
@ -1871,11 +1865,18 @@ static void pc_machine_set_smm(Object *obj, Visitor *v, void *opaque,
|
|||
visit_type_OnOffAuto(v, &pcms->smm, name, errp);
|
||||
}
|
||||
|
||||
static bool pc_machine_get_aligned_dimm(Object *obj, Error **errp)
|
||||
static bool pc_machine_get_nvdimm(Object *obj, Error **errp)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(obj);
|
||||
|
||||
return pcms->enforce_aligned_dimm;
|
||||
return pcms->nvdimm;
|
||||
}
|
||||
|
||||
static void pc_machine_set_nvdimm(Object *obj, bool value, Error **errp)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(obj);
|
||||
|
||||
pcms->nvdimm = value;
|
||||
}
|
||||
|
||||
static void pc_machine_initfn(Object *obj)
|
||||
|
@ -1913,10 +1914,10 @@ static void pc_machine_initfn(Object *obj)
|
|||
"Enable vmport (pc & q35)",
|
||||
&error_abort);
|
||||
|
||||
pcms->enforce_aligned_dimm = true;
|
||||
object_property_add_bool(obj, PC_MACHINE_ENFORCE_ALIGNED_DIMM,
|
||||
pc_machine_get_aligned_dimm,
|
||||
NULL, &error_abort);
|
||||
/* nvdimm is disabled on default. */
|
||||
pcms->nvdimm = false;
|
||||
object_property_add_bool(obj, PC_MACHINE_NVDIMM, pc_machine_get_nvdimm,
|
||||
pc_machine_set_nvdimm, &error_abort);
|
||||
}
|
||||
|
||||
static void pc_machine_reset(void)
|
||||
|
@ -1953,6 +1954,18 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
|
|||
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
|
||||
|
||||
pcmc->get_hotplug_handler = mc->get_hotplug_handler;
|
||||
pcmc->pci_enabled = true;
|
||||
pcmc->has_acpi_build = true;
|
||||
pcmc->rsdp_in_ram = true;
|
||||
pcmc->smbios_defaults = true;
|
||||
pcmc->smbios_uuid_encoded = true;
|
||||
pcmc->gigabyte_align = true;
|
||||
pcmc->has_reserved_memory = true;
|
||||
pcmc->kvmclock_enabled = true;
|
||||
pcmc->enforce_aligned_dimm = true;
|
||||
/* BIOS ACPI tables: 128K. Other BIOS datastructures: less than 4K reported
|
||||
* to be used at the moment, 32K should be enough for a while. */
|
||||
pcmc->acpi_data_size = 0x20000 + 0x8000;
|
||||
mc->get_hotplug_handler = pc_get_hotpug_handler;
|
||||
mc->cpu_index_to_socket_id = pc_cpu_index_to_socket_id;
|
||||
mc->default_boot_order = "cad";
|
||||
|
|
|
@ -61,26 +61,12 @@ static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 };
|
|||
static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 };
|
||||
static const int ide_irq[MAX_IDE_BUS] = { 14, 15 };
|
||||
|
||||
static bool pci_enabled = true;
|
||||
static bool has_acpi_build = true;
|
||||
static bool rsdp_in_ram = true;
|
||||
static int legacy_acpi_table_size;
|
||||
static bool smbios_defaults = true;
|
||||
static bool smbios_legacy_mode;
|
||||
static bool smbios_uuid_encoded = true;
|
||||
/* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to
|
||||
* host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte
|
||||
* pages in the host.
|
||||
*/
|
||||
static bool gigabyte_align = true;
|
||||
static bool has_reserved_memory = true;
|
||||
static bool kvmclock_enabled = true;
|
||||
|
||||
/* PC hardware initialisation */
|
||||
static void pc_init1(MachineState *machine,
|
||||
const char *host_type, const char *pci_type)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(machine);
|
||||
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
MemoryRegion *system_io = get_system_io();
|
||||
int i;
|
||||
|
@ -109,7 +95,7 @@ static void pc_init1(MachineState *machine,
|
|||
* breaking migration.
|
||||
*/
|
||||
if (machine->ram_size >= 0xe0000000) {
|
||||
lowmem = gigabyte_align ? 0xc0000000 : 0xe0000000;
|
||||
lowmem = pcmc->gigabyte_align ? 0xc0000000 : 0xe0000000;
|
||||
} else {
|
||||
lowmem = 0xe0000000;
|
||||
}
|
||||
|
@ -142,11 +128,11 @@ static void pc_init1(MachineState *machine,
|
|||
|
||||
pc_cpus_init(pcms);
|
||||
|
||||
if (kvm_enabled() && kvmclock_enabled) {
|
||||
if (kvm_enabled() && pcmc->kvmclock_enabled) {
|
||||
kvmclock_create();
|
||||
}
|
||||
|
||||
if (pci_enabled) {
|
||||
if (pcmc->pci_enabled) {
|
||||
pci_memory = g_new(MemoryRegion, 1);
|
||||
memory_region_init(pci_memory, NULL, "pci", UINT64_MAX);
|
||||
rom_memory = pci_memory;
|
||||
|
@ -157,18 +143,19 @@ static void pc_init1(MachineState *machine,
|
|||
|
||||
guest_info = pc_guest_info_init(pcms);
|
||||
|
||||
guest_info->has_acpi_build = has_acpi_build;
|
||||
guest_info->legacy_acpi_table_size = legacy_acpi_table_size;
|
||||
guest_info->has_acpi_build = pcmc->has_acpi_build;
|
||||
guest_info->legacy_acpi_table_size = pcmc->legacy_acpi_table_size;
|
||||
|
||||
guest_info->isapc_ram_fw = !pci_enabled;
|
||||
guest_info->has_reserved_memory = has_reserved_memory;
|
||||
guest_info->rsdp_in_ram = rsdp_in_ram;
|
||||
guest_info->isapc_ram_fw = !pcmc->pci_enabled;
|
||||
guest_info->has_reserved_memory = pcmc->has_reserved_memory;
|
||||
guest_info->rsdp_in_ram = pcmc->rsdp_in_ram;
|
||||
|
||||
if (smbios_defaults) {
|
||||
if (pcmc->smbios_defaults) {
|
||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||
/* These values are guest ABI, do not change */
|
||||
smbios_set_defaults("QEMU", "Standard PC (i440FX + PIIX, 1996)",
|
||||
mc->name, smbios_legacy_mode, smbios_uuid_encoded,
|
||||
mc->name, pcmc->smbios_legacy_mode,
|
||||
pcmc->smbios_uuid_encoded,
|
||||
SMBIOS_ENTRY_POINT_21);
|
||||
}
|
||||
|
||||
|
@ -183,14 +170,14 @@ static void pc_init1(MachineState *machine,
|
|||
|
||||
gsi_state = g_malloc0(sizeof(*gsi_state));
|
||||
if (kvm_ioapic_in_kernel()) {
|
||||
kvm_pc_setup_irq_routing(pci_enabled);
|
||||
kvm_pc_setup_irq_routing(pcmc->pci_enabled);
|
||||
gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state,
|
||||
GSI_NUM_PINS);
|
||||
} else {
|
||||
gsi = qemu_allocate_irqs(gsi_handler, gsi_state, GSI_NUM_PINS);
|
||||
}
|
||||
|
||||
if (pci_enabled) {
|
||||
if (pcmc->pci_enabled) {
|
||||
pci_bus = i440fx_init(host_type,
|
||||
pci_type,
|
||||
&i440fx_state, &piix3_devfn, &isa_bus, gsi,
|
||||
|
@ -198,6 +185,7 @@ static void pc_init1(MachineState *machine,
|
|||
pcms->below_4g_mem_size,
|
||||
pcms->above_4g_mem_size,
|
||||
pci_memory, ram_memory);
|
||||
pcms->bus = pci_bus;
|
||||
} else {
|
||||
pci_bus = NULL;
|
||||
i440fx_state = NULL;
|
||||
|
@ -218,13 +206,13 @@ static void pc_init1(MachineState *machine,
|
|||
gsi_state->i8259_irq[i] = i8259[i];
|
||||
}
|
||||
g_free(i8259);
|
||||
if (pci_enabled) {
|
||||
if (pcmc->pci_enabled) {
|
||||
ioapic_init_gsi(gsi_state, "i440fx");
|
||||
}
|
||||
|
||||
pc_register_ferr_irq(gsi[13]);
|
||||
|
||||
pc_vga_init(isa_bus, pci_enabled ? pci_bus : NULL);
|
||||
pc_vga_init(isa_bus, pcmc->pci_enabled ? pci_bus : NULL);
|
||||
|
||||
assert(pcms->vmport != ON_OFF_AUTO__MAX);
|
||||
if (pcms->vmport == ON_OFF_AUTO_AUTO) {
|
||||
|
@ -238,7 +226,7 @@ static void pc_init1(MachineState *machine,
|
|||
pc_nic_init(isa_bus, pci_bus);
|
||||
|
||||
ide_drive_get(hd, ARRAY_SIZE(hd));
|
||||
if (pci_enabled) {
|
||||
if (pcmc->pci_enabled) {
|
||||
PCIDevice *dev;
|
||||
if (xen_enabled()) {
|
||||
dev = pci_piix3_xen_ide_init(pci_bus, hd, piix3_devfn + 1);
|
||||
|
@ -265,11 +253,11 @@ static void pc_init1(MachineState *machine,
|
|||
|
||||
pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state);
|
||||
|
||||
if (pci_enabled && usb_enabled()) {
|
||||
if (pcmc->pci_enabled && usb_enabled()) {
|
||||
pci_create_simple(pci_bus, piix3_devfn + 2, "piix3-usb-uhci");
|
||||
}
|
||||
|
||||
if (pci_enabled && acpi_enabled) {
|
||||
if (pcmc->pci_enabled && acpi_enabled) {
|
||||
DeviceState *piix4_pm;
|
||||
I2CBus *smbus;
|
||||
|
||||
|
@ -290,7 +278,7 @@ static void pc_init1(MachineState *machine,
|
|||
PC_MACHINE_ACPI_DEVICE_PROP, &error_abort);
|
||||
}
|
||||
|
||||
if (pci_enabled) {
|
||||
if (pcmc->pci_enabled) {
|
||||
pc_pci_device_init(pci_bus);
|
||||
}
|
||||
}
|
||||
|
@ -316,60 +304,29 @@ static void pc_compat_2_3(MachineState *machine)
|
|||
static void pc_compat_2_2(MachineState *machine)
|
||||
{
|
||||
pc_compat_2_3(machine);
|
||||
rsdp_in_ram = false;
|
||||
machine->suppress_vmdesc = true;
|
||||
}
|
||||
|
||||
static void pc_compat_2_1(MachineState *machine)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(machine);
|
||||
|
||||
pc_compat_2_2(machine);
|
||||
smbios_uuid_encoded = false;
|
||||
x86_cpu_change_kvm_default("svm", NULL);
|
||||
pcms->enforce_aligned_dimm = false;
|
||||
}
|
||||
|
||||
static void pc_compat_2_0(MachineState *machine)
|
||||
{
|
||||
pc_compat_2_1(machine);
|
||||
/* This value depends on the actual DSDT and SSDT compiled into
|
||||
* the source QEMU; unfortunately it depends on the binary and
|
||||
* not on the machine type, so we cannot make pc-i440fx-1.7 work on
|
||||
* both QEMU 1.7 and QEMU 2.0.
|
||||
*
|
||||
* Large variations cause migration to fail for more than one
|
||||
* consecutive value of the "-smp" maxcpus option.
|
||||
*
|
||||
* For small variations of the kind caused by different iasl versions,
|
||||
* the 4k rounding usually leaves slack. However, there could be still
|
||||
* one or two values that break. For QEMU 1.7 and QEMU 2.0 the
|
||||
* slack is only ~10 bytes before one "-smp maxcpus" value breaks!
|
||||
*
|
||||
* 6652 is valid for QEMU 2.0, the right value for pc-i440fx-1.7 on
|
||||
* QEMU 1.7 it is 6414. For RHEL/CentOS 7.0 it is 6418.
|
||||
*/
|
||||
legacy_acpi_table_size = 6652;
|
||||
smbios_legacy_mode = true;
|
||||
has_reserved_memory = false;
|
||||
pc_set_legacy_acpi_data_size();
|
||||
}
|
||||
|
||||
static void pc_compat_1_7(MachineState *machine)
|
||||
{
|
||||
pc_compat_2_0(machine);
|
||||
smbios_defaults = false;
|
||||
gigabyte_align = false;
|
||||
option_rom_has_mr = true;
|
||||
legacy_acpi_table_size = 6414;
|
||||
x86_cpu_change_kvm_default("x2apic", NULL);
|
||||
}
|
||||
|
||||
static void pc_compat_1_6(MachineState *machine)
|
||||
{
|
||||
pc_compat_1_7(machine);
|
||||
rom_file_has_mr = false;
|
||||
has_acpi_build = false;
|
||||
}
|
||||
|
||||
static void pc_compat_1_5(MachineState *machine)
|
||||
|
@ -399,19 +356,10 @@ static void pc_compat_1_2(MachineState *machine)
|
|||
static void pc_compat_0_13(MachineState *machine)
|
||||
{
|
||||
pc_compat_1_2(machine);
|
||||
kvmclock_enabled = false;
|
||||
}
|
||||
|
||||
static void pc_init_isa(MachineState *machine)
|
||||
{
|
||||
pci_enabled = false;
|
||||
has_acpi_build = false;
|
||||
smbios_defaults = false;
|
||||
gigabyte_align = false;
|
||||
smbios_legacy_mode = true;
|
||||
has_reserved_memory = false;
|
||||
option_rom_has_mr = true;
|
||||
rom_file_has_mr = false;
|
||||
if (!machine->cpu_model) {
|
||||
machine->cpu_model = "486";
|
||||
}
|
||||
|
@ -470,13 +418,25 @@ static void pc_i440fx_machine_options(MachineClass *m)
|
|||
m->default_display = "std";
|
||||
}
|
||||
|
||||
static void pc_i440fx_2_5_machine_options(MachineClass *m)
|
||||
static void pc_i440fx_2_6_machine_options(MachineClass *m)
|
||||
{
|
||||
pc_i440fx_machine_options(m);
|
||||
m->alias = "pc";
|
||||
m->is_default = 1;
|
||||
}
|
||||
|
||||
DEFINE_I440FX_MACHINE(v2_6, "pc-i440fx-2.6", NULL,
|
||||
pc_i440fx_2_6_machine_options);
|
||||
|
||||
|
||||
static void pc_i440fx_2_5_machine_options(MachineClass *m)
|
||||
{
|
||||
pc_i440fx_2_6_machine_options(m);
|
||||
m->alias = NULL;
|
||||
m->is_default = 0;
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_5);
|
||||
}
|
||||
|
||||
DEFINE_I440FX_MACHINE(v2_5, "pc-i440fx-2.5", NULL,
|
||||
pc_i440fx_2_5_machine_options);
|
||||
|
||||
|
@ -486,8 +446,6 @@ static void pc_i440fx_2_4_machine_options(MachineClass *m)
|
|||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_i440fx_2_5_machine_options(m);
|
||||
m->hw_version = "2.4.0";
|
||||
m->alias = NULL;
|
||||
m->is_default = 0;
|
||||
pcmc->broken_reserved_end = true;
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_4);
|
||||
}
|
||||
|
@ -500,8 +458,6 @@ static void pc_i440fx_2_3_machine_options(MachineClass *m)
|
|||
{
|
||||
pc_i440fx_2_4_machine_options(m);
|
||||
m->hw_version = "2.3.0";
|
||||
m->alias = NULL;
|
||||
m->is_default = 0;
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_3);
|
||||
}
|
||||
|
||||
|
@ -511,9 +467,11 @@ DEFINE_I440FX_MACHINE(v2_3, "pc-i440fx-2.3", pc_compat_2_3,
|
|||
|
||||
static void pc_i440fx_2_2_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_i440fx_2_3_machine_options(m);
|
||||
m->hw_version = "2.2.0";
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_2);
|
||||
pcmc->rsdp_in_ram = false;
|
||||
}
|
||||
|
||||
DEFINE_I440FX_MACHINE(v2_2, "pc-i440fx-2.2", pc_compat_2_2,
|
||||
|
@ -522,10 +480,13 @@ DEFINE_I440FX_MACHINE(v2_2, "pc-i440fx-2.2", pc_compat_2_2,
|
|||
|
||||
static void pc_i440fx_2_1_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_i440fx_2_2_machine_options(m);
|
||||
m->hw_version = "2.1.0";
|
||||
m->default_display = NULL;
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_1);
|
||||
pcmc->smbios_uuid_encoded = false;
|
||||
pcmc->enforce_aligned_dimm = false;
|
||||
}
|
||||
|
||||
DEFINE_I440FX_MACHINE(v2_1, "pc-i440fx-2.1", pc_compat_2_1,
|
||||
|
@ -535,9 +496,30 @@ DEFINE_I440FX_MACHINE(v2_1, "pc-i440fx-2.1", pc_compat_2_1,
|
|||
|
||||
static void pc_i440fx_2_0_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_i440fx_2_1_machine_options(m);
|
||||
m->hw_version = "2.0.0";
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_0);
|
||||
pcmc->smbios_legacy_mode = true;
|
||||
pcmc->has_reserved_memory = false;
|
||||
/* This value depends on the actual DSDT and SSDT compiled into
|
||||
* the source QEMU; unfortunately it depends on the binary and
|
||||
* not on the machine type, so we cannot make pc-i440fx-1.7 work on
|
||||
* both QEMU 1.7 and QEMU 2.0.
|
||||
*
|
||||
* Large variations cause migration to fail for more than one
|
||||
* consecutive value of the "-smp" maxcpus option.
|
||||
*
|
||||
* For small variations of the kind caused by different iasl versions,
|
||||
* the 4k rounding usually leaves slack. However, there could be still
|
||||
* one or two values that break. For QEMU 1.7 and QEMU 2.0 the
|
||||
* slack is only ~10 bytes before one "-smp maxcpus" value breaks!
|
||||
*
|
||||
* 6652 is valid for QEMU 2.0, the right value for pc-i440fx-1.7 on
|
||||
* QEMU 1.7 it is 6414. For RHEL/CentOS 7.0 it is 6418.
|
||||
*/
|
||||
pcmc->legacy_acpi_table_size = 6652;
|
||||
pcmc->acpi_data_size = 0x10000;
|
||||
}
|
||||
|
||||
DEFINE_I440FX_MACHINE(v2_0, "pc-i440fx-2.0", pc_compat_2_0,
|
||||
|
@ -546,10 +528,15 @@ DEFINE_I440FX_MACHINE(v2_0, "pc-i440fx-2.0", pc_compat_2_0,
|
|||
|
||||
static void pc_i440fx_1_7_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_i440fx_2_0_machine_options(m);
|
||||
m->hw_version = "1.7.0";
|
||||
m->default_machine_opts = NULL;
|
||||
m->option_rom_has_mr = true;
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_1_7);
|
||||
pcmc->smbios_defaults = false;
|
||||
pcmc->gigabyte_align = false;
|
||||
pcmc->legacy_acpi_table_size = 6414;
|
||||
}
|
||||
|
||||
DEFINE_I440FX_MACHINE(v1_7, "pc-i440fx-1.7", pc_compat_1_7,
|
||||
|
@ -558,9 +545,12 @@ DEFINE_I440FX_MACHINE(v1_7, "pc-i440fx-1.7", pc_compat_1_7,
|
|||
|
||||
static void pc_i440fx_1_6_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_i440fx_1_7_machine_options(m);
|
||||
m->hw_version = "1.6.0";
|
||||
m->rom_file_has_mr = false;
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_1_6);
|
||||
pcmc->has_acpi_build = false;
|
||||
}
|
||||
|
||||
DEFINE_I440FX_MACHINE(v1_6, "pc-i440fx-1.6", pc_compat_1_6,
|
||||
|
@ -814,9 +804,11 @@ DEFINE_I440FX_MACHINE(v0_14, "pc-0.14", pc_compat_1_2,
|
|||
|
||||
static void pc_i440fx_0_13_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_i440fx_0_14_machine_options(m);
|
||||
m->hw_version = "0.13";
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_0_13);
|
||||
pcmc->kvmclock_enabled = false;
|
||||
}
|
||||
|
||||
DEFINE_I440FX_MACHINE(v0_13, "pc-0.13", pc_compat_0_13,
|
||||
|
@ -1038,8 +1030,17 @@ void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id)
|
|||
|
||||
static void isapc_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
m->desc = "ISA-only PC";
|
||||
m->max_cpus = 1;
|
||||
m->option_rom_has_mr = true;
|
||||
m->rom_file_has_mr = false;
|
||||
pcmc->pci_enabled = false;
|
||||
pcmc->has_acpi_build = false;
|
||||
pcmc->smbios_defaults = false;
|
||||
pcmc->gigabyte_align = false;
|
||||
pcmc->smbios_legacy_mode = true;
|
||||
pcmc->has_reserved_memory = false;
|
||||
}
|
||||
|
||||
DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa,
|
||||
|
|
|
@ -49,22 +49,11 @@
|
|||
/* ICH9 AHCI has 6 ports */
|
||||
#define MAX_SATA_PORTS 6
|
||||
|
||||
static bool has_acpi_build = true;
|
||||
static bool rsdp_in_ram = true;
|
||||
static bool smbios_defaults = true;
|
||||
static bool smbios_legacy_mode;
|
||||
static bool smbios_uuid_encoded = true;
|
||||
/* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to
|
||||
* host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte
|
||||
* pages in the host.
|
||||
*/
|
||||
static bool gigabyte_align = true;
|
||||
static bool has_reserved_memory = true;
|
||||
|
||||
/* PC hardware initialisation */
|
||||
static void pc_q35_init(MachineState *machine)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(machine);
|
||||
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
||||
Q35PCIHost *q35_host;
|
||||
PCIHostState *phb;
|
||||
PCIBus *host_bus;
|
||||
|
@ -76,7 +65,6 @@ static void pc_q35_init(MachineState *machine)
|
|||
MemoryRegion *ram_memory;
|
||||
GSIState *gsi_state;
|
||||
ISABus *isa_bus;
|
||||
int pci_enabled = 1;
|
||||
qemu_irq *gsi;
|
||||
qemu_irq *i8259;
|
||||
int i;
|
||||
|
@ -97,7 +85,7 @@ static void pc_q35_init(MachineState *machine)
|
|||
* breaking migration.
|
||||
*/
|
||||
if (machine->ram_size >= 0xb0000000) {
|
||||
lowmem = gigabyte_align ? 0x80000000 : 0xb0000000;
|
||||
lowmem = pcmc->gigabyte_align ? 0x80000000 : 0xb0000000;
|
||||
} else {
|
||||
lowmem = 0xb0000000;
|
||||
}
|
||||
|
@ -129,12 +117,15 @@ static void pc_q35_init(MachineState *machine)
|
|||
}
|
||||
|
||||
pc_cpus_init(pcms);
|
||||
pc_acpi_init("q35-acpi-dsdt.aml");
|
||||
if (!pcmc->has_acpi_build) {
|
||||
/* only machine types 1.7 & older need this */
|
||||
pc_acpi_init("q35-acpi-dsdt.aml");
|
||||
}
|
||||
|
||||
kvmclock_create();
|
||||
|
||||
/* pci enabled */
|
||||
if (pci_enabled) {
|
||||
if (pcmc->pci_enabled) {
|
||||
pci_memory = g_new(MemoryRegion, 1);
|
||||
memory_region_init(pci_memory, NULL, "pci", UINT64_MAX);
|
||||
rom_memory = pci_memory;
|
||||
|
@ -145,19 +136,20 @@ static void pc_q35_init(MachineState *machine)
|
|||
|
||||
guest_info = pc_guest_info_init(pcms);
|
||||
guest_info->isapc_ram_fw = false;
|
||||
guest_info->has_acpi_build = has_acpi_build;
|
||||
guest_info->has_reserved_memory = has_reserved_memory;
|
||||
guest_info->rsdp_in_ram = rsdp_in_ram;
|
||||
guest_info->has_acpi_build = pcmc->has_acpi_build;
|
||||
guest_info->has_reserved_memory = pcmc->has_reserved_memory;
|
||||
guest_info->rsdp_in_ram = pcmc->rsdp_in_ram;
|
||||
|
||||
/* Migration was not supported in 2.0 for Q35, so do not bother
|
||||
* with this hack (see hw/i386/acpi-build.c).
|
||||
*/
|
||||
guest_info->legacy_acpi_table_size = 0;
|
||||
|
||||
if (smbios_defaults) {
|
||||
if (pcmc->smbios_defaults) {
|
||||
/* These values are guest ABI, do not change */
|
||||
smbios_set_defaults("QEMU", "Standard PC (Q35 + ICH9, 2009)",
|
||||
mc->name, smbios_legacy_mode, smbios_uuid_encoded,
|
||||
mc->name, pcmc->smbios_legacy_mode,
|
||||
pcmc->smbios_uuid_encoded,
|
||||
SMBIOS_ENTRY_POINT_21);
|
||||
}
|
||||
|
||||
|
@ -170,7 +162,7 @@ static void pc_q35_init(MachineState *machine)
|
|||
/* irq lines */
|
||||
gsi_state = g_malloc0(sizeof(*gsi_state));
|
||||
if (kvm_irqchip_in_kernel()) {
|
||||
kvm_pc_setup_irq_routing(pci_enabled);
|
||||
kvm_pc_setup_irq_routing(pcmc->pci_enabled);
|
||||
gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state,
|
||||
GSI_NUM_PINS);
|
||||
} else {
|
||||
|
@ -187,11 +179,11 @@ static void pc_q35_init(MachineState *machine)
|
|||
q35_host->mch.address_space_io = get_system_io();
|
||||
q35_host->mch.below_4g_mem_size = pcms->below_4g_mem_size;
|
||||
q35_host->mch.above_4g_mem_size = pcms->above_4g_mem_size;
|
||||
q35_host->mch.guest_info = guest_info;
|
||||
/* pci */
|
||||
qdev_init_nofail(DEVICE(q35_host));
|
||||
phb = PCI_HOST_BRIDGE(q35_host);
|
||||
host_bus = phb->bus;
|
||||
pcms->bus = phb->bus;
|
||||
/* create ISA bus */
|
||||
lpc = pci_create_simple_multifunction(host_bus, PCI_DEVFN(ICH9_LPC_DEV,
|
||||
ICH9_LPC_FUNC), true,
|
||||
|
@ -227,7 +219,7 @@ static void pc_q35_init(MachineState *machine)
|
|||
for (i = 0; i < ISA_NUM_IRQS; i++) {
|
||||
gsi_state->i8259_irq[i] = i8259[i];
|
||||
}
|
||||
if (pci_enabled) {
|
||||
if (pcmc->pci_enabled) {
|
||||
ioapic_init_gsi(gsi_state, "q35");
|
||||
}
|
||||
|
||||
|
@ -272,7 +264,7 @@ static void pc_q35_init(MachineState *machine)
|
|||
/* the rest devices to which pci devfn is automatically assigned */
|
||||
pc_vga_init(isa_bus, host_bus);
|
||||
pc_nic_init(isa_bus, host_bus);
|
||||
if (pci_enabled) {
|
||||
if (pcmc->pci_enabled) {
|
||||
pc_pci_device_init(host_bus);
|
||||
}
|
||||
}
|
||||
|
@ -298,42 +290,29 @@ static void pc_compat_2_3(MachineState *machine)
|
|||
static void pc_compat_2_2(MachineState *machine)
|
||||
{
|
||||
pc_compat_2_3(machine);
|
||||
rsdp_in_ram = false;
|
||||
machine->suppress_vmdesc = true;
|
||||
}
|
||||
|
||||
static void pc_compat_2_1(MachineState *machine)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(machine);
|
||||
|
||||
pc_compat_2_2(machine);
|
||||
pcms->enforce_aligned_dimm = false;
|
||||
smbios_uuid_encoded = false;
|
||||
x86_cpu_change_kvm_default("svm", NULL);
|
||||
}
|
||||
|
||||
static void pc_compat_2_0(MachineState *machine)
|
||||
{
|
||||
pc_compat_2_1(machine);
|
||||
smbios_legacy_mode = true;
|
||||
has_reserved_memory = false;
|
||||
pc_set_legacy_acpi_data_size();
|
||||
}
|
||||
|
||||
static void pc_compat_1_7(MachineState *machine)
|
||||
{
|
||||
pc_compat_2_0(machine);
|
||||
smbios_defaults = false;
|
||||
gigabyte_align = false;
|
||||
option_rom_has_mr = true;
|
||||
x86_cpu_change_kvm_default("x2apic", NULL);
|
||||
}
|
||||
|
||||
static void pc_compat_1_6(MachineState *machine)
|
||||
{
|
||||
pc_compat_1_7(machine);
|
||||
rom_file_has_mr = false;
|
||||
has_acpi_build = false;
|
||||
}
|
||||
|
||||
static void pc_compat_1_5(MachineState *machine)
|
||||
|
@ -370,12 +349,22 @@ static void pc_q35_machine_options(MachineClass *m)
|
|||
m->no_tco = 0;
|
||||
}
|
||||
|
||||
static void pc_q35_2_5_machine_options(MachineClass *m)
|
||||
static void pc_q35_2_6_machine_options(MachineClass *m)
|
||||
{
|
||||
pc_q35_machine_options(m);
|
||||
m->alias = "q35";
|
||||
}
|
||||
|
||||
DEFINE_Q35_MACHINE(v2_6, "pc-q35-2.6", NULL,
|
||||
pc_q35_2_6_machine_options);
|
||||
|
||||
static void pc_q35_2_5_machine_options(MachineClass *m)
|
||||
{
|
||||
pc_q35_2_6_machine_options(m);
|
||||
m->alias = NULL;
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_5);
|
||||
}
|
||||
|
||||
DEFINE_Q35_MACHINE(v2_5, "pc-q35-2.5", NULL,
|
||||
pc_q35_2_5_machine_options);
|
||||
|
||||
|
@ -384,7 +373,6 @@ static void pc_q35_2_4_machine_options(MachineClass *m)
|
|||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_q35_2_5_machine_options(m);
|
||||
m->hw_version = "2.4.0";
|
||||
m->alias = NULL;
|
||||
pcmc->broken_reserved_end = true;
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_4);
|
||||
}
|
||||
|
@ -399,7 +387,6 @@ static void pc_q35_2_3_machine_options(MachineClass *m)
|
|||
m->hw_version = "2.3.0";
|
||||
m->no_floppy = 0;
|
||||
m->no_tco = 1;
|
||||
m->alias = NULL;
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_3);
|
||||
}
|
||||
|
||||
|
@ -409,9 +396,11 @@ DEFINE_Q35_MACHINE(v2_3, "pc-q35-2.3", pc_compat_2_3,
|
|||
|
||||
static void pc_q35_2_2_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_q35_2_3_machine_options(m);
|
||||
m->hw_version = "2.2.0";
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_2);
|
||||
pcmc->rsdp_in_ram = false;
|
||||
}
|
||||
|
||||
DEFINE_Q35_MACHINE(v2_2, "pc-q35-2.2", pc_compat_2_2,
|
||||
|
@ -420,10 +409,13 @@ DEFINE_Q35_MACHINE(v2_2, "pc-q35-2.2", pc_compat_2_2,
|
|||
|
||||
static void pc_q35_2_1_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_q35_2_2_machine_options(m);
|
||||
m->hw_version = "2.1.0";
|
||||
m->default_display = NULL;
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_1);
|
||||
pcmc->smbios_uuid_encoded = false;
|
||||
pcmc->enforce_aligned_dimm = false;
|
||||
}
|
||||
|
||||
DEFINE_Q35_MACHINE(v2_1, "pc-q35-2.1", pc_compat_2_1,
|
||||
|
@ -432,9 +424,13 @@ DEFINE_Q35_MACHINE(v2_1, "pc-q35-2.1", pc_compat_2_1,
|
|||
|
||||
static void pc_q35_2_0_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_q35_2_1_machine_options(m);
|
||||
m->hw_version = "2.0.0";
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_0);
|
||||
pcmc->has_reserved_memory = false;
|
||||
pcmc->smbios_legacy_mode = true;
|
||||
pcmc->acpi_data_size = 0x10000;
|
||||
}
|
||||
|
||||
DEFINE_Q35_MACHINE(v2_0, "pc-q35-2.0", pc_compat_2_0,
|
||||
|
@ -443,10 +439,14 @@ DEFINE_Q35_MACHINE(v2_0, "pc-q35-2.0", pc_compat_2_0,
|
|||
|
||||
static void pc_q35_1_7_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_q35_2_0_machine_options(m);
|
||||
m->hw_version = "1.7.0";
|
||||
m->default_machine_opts = NULL;
|
||||
m->option_rom_has_mr = true;
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_1_7);
|
||||
pcmc->smbios_defaults = false;
|
||||
pcmc->gigabyte_align = false;
|
||||
}
|
||||
|
||||
DEFINE_Q35_MACHINE(v1_7, "pc-q35-1.7", pc_compat_1_7,
|
||||
|
@ -455,9 +455,12 @@ DEFINE_Q35_MACHINE(v1_7, "pc-q35-1.7", pc_compat_1_7,
|
|||
|
||||
static void pc_q35_1_6_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_q35_machine_options(m);
|
||||
m->hw_version = "1.6.0";
|
||||
m->rom_file_has_mr = false;
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_1_6);
|
||||
pcmc->has_acpi_build = false;
|
||||
}
|
||||
|
||||
DEFINE_Q35_MACHINE(v1_6, "pc-q35-1.6", pc_compat_1_6,
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
common-obj-$(CONFIG_IPMI) += ipmi.o
|
||||
common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_sim.o
|
||||
common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_extern.o
|
||||
common-obj-$(CONFIG_ISA_IPMI_KCS) += isa_ipmi_kcs.o
|
||||
common-obj-$(CONFIG_ISA_IPMI_BT) += isa_ipmi_bt.o
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* QEMU IPMI emulation
|
||||
*
|
||||
* Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "hw/ipmi/ipmi.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "qapi/visitor.h"
|
||||
|
||||
static int ipmi_do_hw_op(IPMIInterface *s, enum ipmi_op op, int checkonly)
|
||||
{
|
||||
switch (op) {
|
||||
case IPMI_RESET_CHASSIS:
|
||||
if (checkonly) {
|
||||
return 0;
|
||||
}
|
||||
qemu_system_reset_request();
|
||||
return 0;
|
||||
|
||||
case IPMI_POWEROFF_CHASSIS:
|
||||
if (checkonly) {
|
||||
return 0;
|
||||
}
|
||||
qemu_system_powerdown_request();
|
||||
return 0;
|
||||
|
||||
case IPMI_SEND_NMI:
|
||||
if (checkonly) {
|
||||
return 0;
|
||||
}
|
||||
qemu_mutex_lock_iothread();
|
||||
qmp_inject_nmi(NULL);
|
||||
qemu_mutex_unlock_iothread();
|
||||
return 0;
|
||||
|
||||
case IPMI_POWERCYCLE_CHASSIS:
|
||||
case IPMI_PULSE_DIAG_IRQ:
|
||||
case IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP:
|
||||
case IPMI_POWERON_CHASSIS:
|
||||
default:
|
||||
return IPMI_CC_COMMAND_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
static void ipmi_interface_class_init(ObjectClass *class, void *data)
|
||||
{
|
||||
IPMIInterfaceClass *ik = IPMI_INTERFACE_CLASS(class);
|
||||
|
||||
ik->do_hw_op = ipmi_do_hw_op;
|
||||
}
|
||||
|
||||
static TypeInfo ipmi_interface_type_info = {
|
||||
.name = TYPE_IPMI_INTERFACE,
|
||||
.parent = TYPE_INTERFACE,
|
||||
.class_size = sizeof(IPMIInterfaceClass),
|
||||
.class_init = ipmi_interface_class_init,
|
||||
};
|
||||
|
||||
static void isa_ipmi_bmc_check(Object *obj, const char *name,
|
||||
Object *val, Error **errp)
|
||||
{
|
||||
IPMIBmc *bmc = IPMI_BMC(val);
|
||||
|
||||
if (bmc->intf)
|
||||
error_setg(errp, "BMC object is already in use");
|
||||
}
|
||||
|
||||
void ipmi_bmc_find_and_link(Object *obj, Object **bmc)
|
||||
{
|
||||
object_property_add_link(obj, "bmc", TYPE_IPMI_BMC, bmc,
|
||||
isa_ipmi_bmc_check,
|
||||
OBJ_PROP_LINK_UNREF_ON_RELEASE,
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
static Property ipmi_bmc_properties[] = {
|
||||
DEFINE_PROP_UINT8("slave_addr", IPMIBmc, slave_addr, 0x20),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void bmc_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->props = ipmi_bmc_properties;
|
||||
}
|
||||
|
||||
static TypeInfo ipmi_bmc_type_info = {
|
||||
.name = TYPE_IPMI_BMC,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(IPMIBmc),
|
||||
.abstract = true,
|
||||
.class_size = sizeof(IPMIBmcClass),
|
||||
.class_init = bmc_class_init,
|
||||
};
|
||||
|
||||
static void ipmi_register_types(void)
|
||||
{
|
||||
type_register_static(&ipmi_interface_type_info);
|
||||
type_register_static(&ipmi_bmc_type_info);
|
||||
}
|
||||
|
||||
type_init(ipmi_register_types)
|
||||
|
||||
static IPMIFwInfo *ipmi_fw_info;
|
||||
static unsigned int ipmi_fw_info_len;
|
||||
|
||||
static uint32_t current_uuid = 1;
|
||||
|
||||
void ipmi_add_fwinfo(IPMIFwInfo *info, Error **errp)
|
||||
{
|
||||
info->uuid = current_uuid++;
|
||||
ipmi_fw_info = g_realloc(ipmi_fw_info,
|
||||
sizeof(*ipmi_fw_info) * (ipmi_fw_info_len + 1));
|
||||
ipmi_fw_info[ipmi_fw_info_len] = *info;
|
||||
}
|
||||
|
||||
IPMIFwInfo *ipmi_first_fwinfo(void)
|
||||
{
|
||||
return ipmi_fw_info;
|
||||
}
|
||||
|
||||
IPMIFwInfo *ipmi_next_fwinfo(IPMIFwInfo *current)
|
||||
{
|
||||
current++;
|
||||
if (current >= &ipmi_fw_info[ipmi_fw_info_len]) {
|
||||
return NULL;
|
||||
}
|
||||
return current;
|
||||
}
|
|
@ -0,0 +1,518 @@
|
|||
/*
|
||||
* IPMI BMC external connection
|
||||
*
|
||||
* Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is designed to connect with OpenIPMI's lanserv serial interface
|
||||
* using the "VM" connection type. See that for details.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "qemu/timer.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/ipmi/ipmi.h"
|
||||
|
||||
#define VM_MSG_CHAR 0xA0 /* Marks end of message */
|
||||
#define VM_CMD_CHAR 0xA1 /* Marks end of a command */
|
||||
#define VM_ESCAPE_CHAR 0xAA /* Set bit 4 from the next byte to 0 */
|
||||
|
||||
#define VM_PROTOCOL_VERSION 1
|
||||
#define VM_CMD_VERSION 0xff /* A version number byte follows */
|
||||
#define VM_CMD_NOATTN 0x00
|
||||
#define VM_CMD_ATTN 0x01
|
||||
#define VM_CMD_ATTN_IRQ 0x02
|
||||
#define VM_CMD_POWEROFF 0x03
|
||||
#define VM_CMD_RESET 0x04
|
||||
#define VM_CMD_ENABLE_IRQ 0x05 /* Enable/disable the messaging irq */
|
||||
#define VM_CMD_DISABLE_IRQ 0x06
|
||||
#define VM_CMD_SEND_NMI 0x07
|
||||
#define VM_CMD_CAPABILITIES 0x08
|
||||
#define VM_CAPABILITIES_POWER 0x01
|
||||
#define VM_CAPABILITIES_RESET 0x02
|
||||
#define VM_CAPABILITIES_IRQ 0x04
|
||||
#define VM_CAPABILITIES_NMI 0x08
|
||||
#define VM_CAPABILITIES_ATTN 0x10
|
||||
#define VM_CMD_FORCEOFF 0x09
|
||||
|
||||
#define TYPE_IPMI_BMC_EXTERN "ipmi-bmc-extern"
|
||||
#define IPMI_BMC_EXTERN(obj) OBJECT_CHECK(IPMIBmcExtern, (obj), \
|
||||
TYPE_IPMI_BMC_EXTERN)
|
||||
typedef struct IPMIBmcExtern {
|
||||
IPMIBmc parent;
|
||||
|
||||
CharDriverState *chr;
|
||||
|
||||
bool connected;
|
||||
|
||||
unsigned char inbuf[MAX_IPMI_MSG_SIZE + 2];
|
||||
unsigned int inpos;
|
||||
bool in_escape;
|
||||
bool in_too_many;
|
||||
bool waiting_rsp;
|
||||
bool sending_cmd;
|
||||
|
||||
unsigned char outbuf[(MAX_IPMI_MSG_SIZE + 2) * 2 + 1];
|
||||
unsigned int outpos;
|
||||
unsigned int outlen;
|
||||
|
||||
struct QEMUTimer *extern_timer;
|
||||
|
||||
/* A reset event is pending to be sent upstream. */
|
||||
bool send_reset;
|
||||
} IPMIBmcExtern;
|
||||
|
||||
static int can_receive(void *opaque);
|
||||
static void receive(void *opaque, const uint8_t *buf, int size);
|
||||
static void chr_event(void *opaque, int event);
|
||||
|
||||
static unsigned char
|
||||
ipmb_checksum(const unsigned char *data, int size, unsigned char start)
|
||||
{
|
||||
unsigned char csum = start;
|
||||
|
||||
for (; size > 0; size--, data++) {
|
||||
csum += *data;
|
||||
}
|
||||
return csum;
|
||||
}
|
||||
|
||||
static void continue_send(IPMIBmcExtern *ibe)
|
||||
{
|
||||
if (ibe->outlen == 0) {
|
||||
goto check_reset;
|
||||
}
|
||||
send:
|
||||
ibe->outpos += qemu_chr_fe_write(ibe->chr, ibe->outbuf + ibe->outpos,
|
||||
ibe->outlen - ibe->outpos);
|
||||
if (ibe->outpos < ibe->outlen) {
|
||||
/* Not fully transmitted, try again in a 10ms */
|
||||
timer_mod_ns(ibe->extern_timer,
|
||||
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 10000000);
|
||||
} else {
|
||||
/* Sent */
|
||||
ibe->outlen = 0;
|
||||
ibe->outpos = 0;
|
||||
if (!ibe->sending_cmd) {
|
||||
ibe->waiting_rsp = true;
|
||||
} else {
|
||||
ibe->sending_cmd = false;
|
||||
}
|
||||
check_reset:
|
||||
if (ibe->connected && ibe->send_reset) {
|
||||
/* Send the reset */
|
||||
ibe->outbuf[0] = VM_CMD_RESET;
|
||||
ibe->outbuf[1] = VM_CMD_CHAR;
|
||||
ibe->outlen = 2;
|
||||
ibe->outpos = 0;
|
||||
ibe->send_reset = false;
|
||||
ibe->sending_cmd = true;
|
||||
goto send;
|
||||
}
|
||||
|
||||
if (ibe->waiting_rsp) {
|
||||
/* Make sure we get a response within 4 seconds. */
|
||||
timer_mod_ns(ibe->extern_timer,
|
||||
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 4000000000ULL);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void extern_timeout(void *opaque)
|
||||
{
|
||||
IPMIBmcExtern *ibe = opaque;
|
||||
IPMIInterface *s = ibe->parent.intf;
|
||||
|
||||
if (ibe->connected) {
|
||||
if (ibe->waiting_rsp && (ibe->outlen == 0)) {
|
||||
IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
|
||||
/* The message response timed out, return an error. */
|
||||
ibe->waiting_rsp = false;
|
||||
ibe->inbuf[1] = ibe->outbuf[1] | 0x04;
|
||||
ibe->inbuf[2] = ibe->outbuf[2];
|
||||
ibe->inbuf[3] = IPMI_CC_TIMEOUT;
|
||||
k->handle_rsp(s, ibe->outbuf[0], ibe->inbuf + 1, 3);
|
||||
} else {
|
||||
continue_send(ibe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void addchar(IPMIBmcExtern *ibe, unsigned char ch)
|
||||
{
|
||||
switch (ch) {
|
||||
case VM_MSG_CHAR:
|
||||
case VM_CMD_CHAR:
|
||||
case VM_ESCAPE_CHAR:
|
||||
ibe->outbuf[ibe->outlen] = VM_ESCAPE_CHAR;
|
||||
ibe->outlen++;
|
||||
ch |= 0x10;
|
||||
/* No break */
|
||||
|
||||
default:
|
||||
ibe->outbuf[ibe->outlen] = ch;
|
||||
ibe->outlen++;
|
||||
}
|
||||
}
|
||||
|
||||
static void ipmi_bmc_extern_handle_command(IPMIBmc *b,
|
||||
uint8_t *cmd, unsigned int cmd_len,
|
||||
unsigned int max_cmd_len,
|
||||
uint8_t msg_id)
|
||||
{
|
||||
IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b);
|
||||
IPMIInterface *s = ibe->parent.intf;
|
||||
uint8_t err = 0, csum;
|
||||
unsigned int i;
|
||||
|
||||
if (ibe->outlen) {
|
||||
/* We already have a command queued. Shouldn't ever happen. */
|
||||
fprintf(stderr, "IPMI KCS: Got command when not finished with the"
|
||||
" previous commmand\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
/* If it's too short or it was truncated, return an error. */
|
||||
if (cmd_len < 2) {
|
||||
err = IPMI_CC_REQUEST_DATA_LENGTH_INVALID;
|
||||
} else if ((cmd_len > max_cmd_len) || (cmd_len > MAX_IPMI_MSG_SIZE)) {
|
||||
err = IPMI_CC_REQUEST_DATA_TRUNCATED;
|
||||
} else if (!ibe->connected) {
|
||||
err = IPMI_CC_BMC_INIT_IN_PROGRESS;
|
||||
}
|
||||
if (err) {
|
||||
IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
|
||||
unsigned char rsp[3];
|
||||
rsp[0] = cmd[0] | 0x04;
|
||||
rsp[1] = cmd[1];
|
||||
rsp[2] = err;
|
||||
ibe->waiting_rsp = false;
|
||||
k->handle_rsp(s, msg_id, rsp, 3);
|
||||
goto out;
|
||||
}
|
||||
|
||||
addchar(ibe, msg_id);
|
||||
for (i = 0; i < cmd_len; i++) {
|
||||
addchar(ibe, cmd[i]);
|
||||
}
|
||||
csum = ipmb_checksum(&msg_id, 1, 0);
|
||||
addchar(ibe, -ipmb_checksum(cmd, cmd_len, csum));
|
||||
|
||||
ibe->outbuf[ibe->outlen] = VM_MSG_CHAR;
|
||||
ibe->outlen++;
|
||||
|
||||
/* Start the transmit */
|
||||
continue_send(ibe);
|
||||
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
static void handle_hw_op(IPMIBmcExtern *ibe, unsigned char hw_op)
|
||||
{
|
||||
IPMIInterface *s = ibe->parent.intf;
|
||||
IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
|
||||
|
||||
switch (hw_op) {
|
||||
case VM_CMD_VERSION:
|
||||
/* We only support one version at this time. */
|
||||
break;
|
||||
|
||||
case VM_CMD_NOATTN:
|
||||
k->set_atn(s, 0, 0);
|
||||
break;
|
||||
|
||||
case VM_CMD_ATTN:
|
||||
k->set_atn(s, 1, 0);
|
||||
break;
|
||||
|
||||
case VM_CMD_ATTN_IRQ:
|
||||
k->set_atn(s, 1, 1);
|
||||
break;
|
||||
|
||||
case VM_CMD_POWEROFF:
|
||||
k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0);
|
||||
break;
|
||||
|
||||
case VM_CMD_RESET:
|
||||
k->do_hw_op(s, IPMI_RESET_CHASSIS, 0);
|
||||
break;
|
||||
|
||||
case VM_CMD_ENABLE_IRQ:
|
||||
k->set_irq_enable(s, 1);
|
||||
break;
|
||||
|
||||
case VM_CMD_DISABLE_IRQ:
|
||||
k->set_irq_enable(s, 0);
|
||||
break;
|
||||
|
||||
case VM_CMD_SEND_NMI:
|
||||
k->do_hw_op(s, IPMI_SEND_NMI, 0);
|
||||
break;
|
||||
|
||||
case VM_CMD_FORCEOFF:
|
||||
qemu_system_shutdown_request();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_msg(IPMIBmcExtern *ibe)
|
||||
{
|
||||
IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(ibe->parent.intf);
|
||||
|
||||
if (ibe->in_escape) {
|
||||
ipmi_debug("msg escape not ended\n");
|
||||
return;
|
||||
}
|
||||
if (ibe->inpos < 5) {
|
||||
ipmi_debug("msg too short\n");
|
||||
return;
|
||||
}
|
||||
if (ibe->in_too_many) {
|
||||
ibe->inbuf[3] = IPMI_CC_REQUEST_DATA_TRUNCATED;
|
||||
ibe->inpos = 4;
|
||||
} else if (ipmb_checksum(ibe->inbuf, ibe->inpos, 0) != 0) {
|
||||
ipmi_debug("msg checksum failure\n");
|
||||
return;
|
||||
} else {
|
||||
ibe->inpos--; /* Remove checkum */
|
||||
}
|
||||
|
||||
timer_del(ibe->extern_timer);
|
||||
ibe->waiting_rsp = false;
|
||||
k->handle_rsp(ibe->parent.intf, ibe->inbuf[0], ibe->inbuf + 1, ibe->inpos - 1);
|
||||
}
|
||||
|
||||
static int can_receive(void *opaque)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void receive(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
IPMIBmcExtern *ibe = opaque;
|
||||
int i;
|
||||
unsigned char hw_op;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
unsigned char ch = buf[i];
|
||||
|
||||
switch (ch) {
|
||||
case VM_MSG_CHAR:
|
||||
handle_msg(ibe);
|
||||
ibe->in_too_many = false;
|
||||
ibe->inpos = 0;
|
||||
break;
|
||||
|
||||
case VM_CMD_CHAR:
|
||||
if (ibe->in_too_many) {
|
||||
ipmi_debug("cmd in too many\n");
|
||||
ibe->in_too_many = false;
|
||||
ibe->inpos = 0;
|
||||
break;
|
||||
}
|
||||
if (ibe->in_escape) {
|
||||
ipmi_debug("cmd in escape\n");
|
||||
ibe->in_too_many = false;
|
||||
ibe->inpos = 0;
|
||||
ibe->in_escape = false;
|
||||
break;
|
||||
}
|
||||
ibe->in_too_many = false;
|
||||
if (ibe->inpos < 1) {
|
||||
break;
|
||||
}
|
||||
hw_op = ibe->inbuf[0];
|
||||
ibe->inpos = 0;
|
||||
goto out_hw_op;
|
||||
break;
|
||||
|
||||
case VM_ESCAPE_CHAR:
|
||||
ibe->in_escape = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (ibe->in_escape) {
|
||||
ch &= ~0x10;
|
||||
ibe->in_escape = false;
|
||||
}
|
||||
if (ibe->in_too_many) {
|
||||
break;
|
||||
}
|
||||
if (ibe->inpos >= sizeof(ibe->inbuf)) {
|
||||
ibe->in_too_many = true;
|
||||
break;
|
||||
}
|
||||
ibe->inbuf[ibe->inpos] = ch;
|
||||
ibe->inpos++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
out_hw_op:
|
||||
handle_hw_op(ibe, hw_op);
|
||||
}
|
||||
|
||||
static void chr_event(void *opaque, int event)
|
||||
{
|
||||
IPMIBmcExtern *ibe = opaque;
|
||||
IPMIInterface *s = ibe->parent.intf;
|
||||
IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
|
||||
unsigned char v;
|
||||
|
||||
switch (event) {
|
||||
case CHR_EVENT_OPENED:
|
||||
ibe->connected = true;
|
||||
ibe->outpos = 0;
|
||||
ibe->outlen = 0;
|
||||
addchar(ibe, VM_CMD_VERSION);
|
||||
addchar(ibe, VM_PROTOCOL_VERSION);
|
||||
ibe->outbuf[ibe->outlen] = VM_CMD_CHAR;
|
||||
ibe->outlen++;
|
||||
addchar(ibe, VM_CMD_CAPABILITIES);
|
||||
v = VM_CAPABILITIES_IRQ | VM_CAPABILITIES_ATTN;
|
||||
if (k->do_hw_op(ibe->parent.intf, IPMI_POWEROFF_CHASSIS, 1) == 0) {
|
||||
v |= VM_CAPABILITIES_POWER;
|
||||
}
|
||||
if (k->do_hw_op(ibe->parent.intf, IPMI_RESET_CHASSIS, 1) == 0) {
|
||||
v |= VM_CAPABILITIES_RESET;
|
||||
}
|
||||
if (k->do_hw_op(ibe->parent.intf, IPMI_SEND_NMI, 1) == 0) {
|
||||
v |= VM_CAPABILITIES_NMI;
|
||||
}
|
||||
addchar(ibe, v);
|
||||
ibe->outbuf[ibe->outlen] = VM_CMD_CHAR;
|
||||
ibe->outlen++;
|
||||
ibe->sending_cmd = false;
|
||||
continue_send(ibe);
|
||||
break;
|
||||
|
||||
case CHR_EVENT_CLOSED:
|
||||
if (!ibe->connected) {
|
||||
return;
|
||||
}
|
||||
ibe->connected = false;
|
||||
if (ibe->waiting_rsp) {
|
||||
ibe->waiting_rsp = false;
|
||||
ibe->inbuf[1] = ibe->outbuf[1] | 0x04;
|
||||
ibe->inbuf[2] = ibe->outbuf[2];
|
||||
ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS;
|
||||
k->handle_rsp(s, ibe->outbuf[0], ibe->inbuf + 1, 3);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ipmi_bmc_extern_handle_reset(IPMIBmc *b)
|
||||
{
|
||||
IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b);
|
||||
|
||||
ibe->send_reset = true;
|
||||
continue_send(ibe);
|
||||
}
|
||||
|
||||
static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev);
|
||||
|
||||
if (!ibe->chr) {
|
||||
error_setg(errp, "IPMI external bmc requires chardev attribute");
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_chr_add_handlers(ibe->chr, can_receive, receive, chr_event, ibe);
|
||||
}
|
||||
|
||||
static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id)
|
||||
{
|
||||
IPMIBmcExtern *ibe = opaque;
|
||||
|
||||
/*
|
||||
* We don't directly restore waiting_rsp, Instead, we return an
|
||||
* error on the interface if a response was being waited for.
|
||||
*/
|
||||
if (ibe->waiting_rsp) {
|
||||
IPMIInterface *ii = ibe->parent.intf;
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
|
||||
ibe->waiting_rsp = false;
|
||||
ibe->inbuf[1] = ibe->outbuf[1] | 0x04;
|
||||
ibe->inbuf[2] = ibe->outbuf[2];
|
||||
ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS;
|
||||
iic->handle_rsp(ii, ibe->outbuf[0], ibe->inbuf + 1, 3);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_ipmi_bmc_extern = {
|
||||
.name = TYPE_IPMI_BMC_EXTERN,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.post_load = ipmi_bmc_extern_post_migrate,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_BOOL(send_reset, IPMIBmcExtern),
|
||||
VMSTATE_BOOL(waiting_rsp, IPMIBmcExtern),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void ipmi_bmc_extern_init(Object *obj)
|
||||
{
|
||||
IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj);
|
||||
|
||||
ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe);
|
||||
vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe);
|
||||
}
|
||||
|
||||
static Property ipmi_bmc_extern_properties[] = {
|
||||
DEFINE_PROP_CHR("chardev", IPMIBmcExtern, chr),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void ipmi_bmc_extern_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
IPMIBmcClass *bk = IPMI_BMC_CLASS(oc);
|
||||
|
||||
bk->handle_command = ipmi_bmc_extern_handle_command;
|
||||
bk->handle_reset = ipmi_bmc_extern_handle_reset;
|
||||
dc->realize = ipmi_bmc_extern_realize;
|
||||
dc->props = ipmi_bmc_extern_properties;
|
||||
}
|
||||
|
||||
static const TypeInfo ipmi_bmc_extern_type = {
|
||||
.name = TYPE_IPMI_BMC_EXTERN,
|
||||
.parent = TYPE_IPMI_BMC,
|
||||
.instance_size = sizeof(IPMIBmcExtern),
|
||||
.instance_init = ipmi_bmc_extern_init,
|
||||
.class_init = ipmi_bmc_extern_class_init,
|
||||
};
|
||||
|
||||
static void ipmi_bmc_extern_register_types(void)
|
||||
{
|
||||
type_register_static(&ipmi_bmc_extern_type);
|
||||
}
|
||||
|
||||
type_init(ipmi_bmc_extern_register_types)
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,528 @@
|
|||
/*
|
||||
* QEMU ISA IPMI BT emulation
|
||||
*
|
||||
* Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "hw/hw.h"
|
||||
#include "hw/ipmi/ipmi.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/i386/pc.h"
|
||||
|
||||
/* Control register */
|
||||
#define IPMI_BT_CLR_WR_BIT 0
|
||||
#define IPMI_BT_CLR_RD_BIT 1
|
||||
#define IPMI_BT_H2B_ATN_BIT 2
|
||||
#define IPMI_BT_B2H_ATN_BIT 3
|
||||
#define IPMI_BT_SMS_ATN_BIT 4
|
||||
#define IPMI_BT_HBUSY_BIT 6
|
||||
#define IPMI_BT_BBUSY_BIT 7
|
||||
|
||||
#define IPMI_BT_CLR_WR_MASK (1 << IPMI_BT_CLR_WR_BIT)
|
||||
#define IPMI_BT_GET_CLR_WR(d) (((d) >> IPMI_BT_CLR_WR_BIT) & 0x1)
|
||||
#define IPMI_BT_SET_CLR_WR(d, v) (d) = (((d) & ~IPMI_BT_CLR_WR_MASK) | \
|
||||
(((v & 1) << IPMI_BT_CLR_WR_BIT)))
|
||||
|
||||
#define IPMI_BT_CLR_RD_MASK (1 << IPMI_BT_CLR_RD_BIT)
|
||||
#define IPMI_BT_GET_CLR_RD(d) (((d) >> IPMI_BT_CLR_RD_BIT) & 0x1)
|
||||
#define IPMI_BT_SET_CLR_RD(d, v) (d) = (((d) & ~IPMI_BT_CLR_RD_MASK) | \
|
||||
(((v & 1) << IPMI_BT_CLR_RD_BIT)))
|
||||
|
||||
#define IPMI_BT_H2B_ATN_MASK (1 << IPMI_BT_H2B_ATN_BIT)
|
||||
#define IPMI_BT_GET_H2B_ATN(d) (((d) >> IPMI_BT_H2B_ATN_BIT) & 0x1)
|
||||
#define IPMI_BT_SET_H2B_ATN(d, v) (d) = (((d) & ~IPMI_BT_H2B_ATN_MASK) | \
|
||||
(((v & 1) << IPMI_BT_H2B_ATN_BIT)))
|
||||
|
||||
#define IPMI_BT_B2H_ATN_MASK (1 << IPMI_BT_B2H_ATN_BIT)
|
||||
#define IPMI_BT_GET_B2H_ATN(d) (((d) >> IPMI_BT_B2H_ATN_BIT) & 0x1)
|
||||
#define IPMI_BT_SET_B2H_ATN(d, v) (d) = (((d) & ~IPMI_BT_B2H_ATN_MASK) | \
|
||||
(((v & 1) << IPMI_BT_B2H_ATN_BIT)))
|
||||
|
||||
#define IPMI_BT_SMS_ATN_MASK (1 << IPMI_BT_SMS_ATN_BIT)
|
||||
#define IPMI_BT_GET_SMS_ATN(d) (((d) >> IPMI_BT_SMS_ATN_BIT) & 0x1)
|
||||
#define IPMI_BT_SET_SMS_ATN(d, v) (d) = (((d) & ~IPMI_BT_SMS_ATN_MASK) | \
|
||||
(((v & 1) << IPMI_BT_SMS_ATN_BIT)))
|
||||
|
||||
#define IPMI_BT_HBUSY_MASK (1 << IPMI_BT_HBUSY_BIT)
|
||||
#define IPMI_BT_GET_HBUSY(d) (((d) >> IPMI_BT_HBUSY_BIT) & 0x1)
|
||||
#define IPMI_BT_SET_HBUSY(d, v) (d) = (((d) & ~IPMI_BT_HBUSY_MASK) | \
|
||||
(((v & 1) << IPMI_BT_HBUSY_BIT)))
|
||||
|
||||
#define IPMI_BT_BBUSY_MASK (1 << IPMI_BT_BBUSY_BIT)
|
||||
#define IPMI_BT_GET_BBUSY(d) (((d) >> IPMI_BT_BBUSY_BIT) & 0x1)
|
||||
#define IPMI_BT_SET_BBUSY(d, v) (d) = (((d) & ~IPMI_BT_BBUSY_MASK) | \
|
||||
(((v & 1) << IPMI_BT_BBUSY_BIT)))
|
||||
|
||||
|
||||
/* Mask register */
|
||||
#define IPMI_BT_B2H_IRQ_EN_BIT 0
|
||||
#define IPMI_BT_B2H_IRQ_BIT 1
|
||||
|
||||
#define IPMI_BT_B2H_IRQ_EN_MASK (1 << IPMI_BT_B2H_IRQ_EN_BIT)
|
||||
#define IPMI_BT_GET_B2H_IRQ_EN(d) (((d) >> IPMI_BT_B2H_IRQ_EN_BIT) & 0x1)
|
||||
#define IPMI_BT_SET_B2H_IRQ_EN(d, v) (d) = (((d) & ~IPMI_BT_B2H_IRQ_EN_MASK) | \
|
||||
(((v & 1) << IPMI_BT_B2H_IRQ_EN_BIT)))
|
||||
|
||||
#define IPMI_BT_B2H_IRQ_MASK (1 << IPMI_BT_B2H_IRQ_BIT)
|
||||
#define IPMI_BT_GET_B2H_IRQ(d) (((d) >> IPMI_BT_B2H_IRQ_BIT) & 0x1)
|
||||
#define IPMI_BT_SET_B2H_IRQ(d, v) (d) = (((d) & ~IPMI_BT_B2H_IRQ_MASK) | \
|
||||
(((v & 1) << IPMI_BT_B2H_IRQ_BIT)))
|
||||
|
||||
typedef struct IPMIBT {
|
||||
IPMIBmc *bmc;
|
||||
|
||||
bool do_wake;
|
||||
|
||||
qemu_irq irq;
|
||||
|
||||
uint32_t io_base;
|
||||
unsigned long io_length;
|
||||
MemoryRegion io;
|
||||
|
||||
bool obf_irq_set;
|
||||
bool atn_irq_set;
|
||||
bool use_irq;
|
||||
bool irqs_enabled;
|
||||
|
||||
uint8_t outmsg[MAX_IPMI_MSG_SIZE];
|
||||
uint32_t outpos;
|
||||
uint32_t outlen;
|
||||
|
||||
uint8_t inmsg[MAX_IPMI_MSG_SIZE];
|
||||
uint32_t inlen;
|
||||
|
||||
uint8_t control_reg;
|
||||
uint8_t mask_reg;
|
||||
|
||||
/*
|
||||
* This is a response number that we send with the command to make
|
||||
* sure that the response matches the command.
|
||||
*/
|
||||
uint8_t waiting_rsp;
|
||||
uint8_t waiting_seq;
|
||||
} IPMIBT;
|
||||
|
||||
#define IPMI_CMD_GET_BT_INTF_CAP 0x36
|
||||
|
||||
static void ipmi_bt_handle_event(IPMIInterface *ii)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIBT *ib = iic->get_backend_data(ii);
|
||||
|
||||
if (ib->inlen < 4) {
|
||||
goto out;
|
||||
}
|
||||
/* Note that overruns are handled by handle_command */
|
||||
if (ib->inmsg[0] != (ib->inlen - 1)) {
|
||||
/* Length mismatch, just ignore. */
|
||||
IPMI_BT_SET_BBUSY(ib->control_reg, 1);
|
||||
ib->inlen = 0;
|
||||
goto out;
|
||||
}
|
||||
if ((ib->inmsg[1] == (IPMI_NETFN_APP << 2)) &&
|
||||
(ib->inmsg[3] == IPMI_CMD_GET_BT_INTF_CAP)) {
|
||||
/* We handle this one ourselves. */
|
||||
ib->outmsg[0] = 9;
|
||||
ib->outmsg[1] = ib->inmsg[1] | 0x04;
|
||||
ib->outmsg[2] = ib->inmsg[2];
|
||||
ib->outmsg[3] = ib->inmsg[3];
|
||||
ib->outmsg[4] = 0;
|
||||
ib->outmsg[5] = 1; /* Only support 1 outstanding request. */
|
||||
if (sizeof(ib->inmsg) > 0xff) { /* Input buffer size */
|
||||
ib->outmsg[6] = 0xff;
|
||||
} else {
|
||||
ib->outmsg[6] = (unsigned char) sizeof(ib->inmsg);
|
||||
}
|
||||
if (sizeof(ib->outmsg) > 0xff) { /* Output buffer size */
|
||||
ib->outmsg[7] = 0xff;
|
||||
} else {
|
||||
ib->outmsg[7] = (unsigned char) sizeof(ib->outmsg);
|
||||
}
|
||||
ib->outmsg[8] = 10; /* Max request to response time */
|
||||
ib->outmsg[9] = 0; /* Don't recommend retries */
|
||||
ib->outlen = 10;
|
||||
IPMI_BT_SET_BBUSY(ib->control_reg, 0);
|
||||
IPMI_BT_SET_B2H_ATN(ib->control_reg, 1);
|
||||
if (ib->use_irq && ib->irqs_enabled &&
|
||||
!IPMI_BT_GET_B2H_IRQ(ib->mask_reg) &&
|
||||
IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
|
||||
IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
|
||||
qemu_irq_raise(ib->irq);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
ib->waiting_seq = ib->inmsg[2];
|
||||
ib->inmsg[2] = ib->inmsg[1];
|
||||
{
|
||||
IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(ib->bmc);
|
||||
bk->handle_command(ib->bmc, ib->inmsg + 2, ib->inlen - 2,
|
||||
sizeof(ib->inmsg), ib->waiting_rsp);
|
||||
}
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
static void ipmi_bt_handle_rsp(IPMIInterface *ii, uint8_t msg_id,
|
||||
unsigned char *rsp, unsigned int rsp_len)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIBT *ib = iic->get_backend_data(ii);
|
||||
|
||||
if (ib->waiting_rsp == msg_id) {
|
||||
ib->waiting_rsp++;
|
||||
if (rsp_len > (sizeof(ib->outmsg) - 2)) {
|
||||
ib->outmsg[0] = 4;
|
||||
ib->outmsg[1] = rsp[0];
|
||||
ib->outmsg[2] = ib->waiting_seq;
|
||||
ib->outmsg[3] = rsp[1];
|
||||
ib->outmsg[4] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES;
|
||||
ib->outlen = 5;
|
||||
} else {
|
||||
ib->outmsg[0] = rsp_len + 1;
|
||||
ib->outmsg[1] = rsp[0];
|
||||
ib->outmsg[2] = ib->waiting_seq;
|
||||
memcpy(ib->outmsg + 3, rsp + 1, rsp_len - 1);
|
||||
ib->outlen = rsp_len + 2;
|
||||
}
|
||||
IPMI_BT_SET_BBUSY(ib->control_reg, 0);
|
||||
IPMI_BT_SET_B2H_ATN(ib->control_reg, 1);
|
||||
if (ib->use_irq && ib->irqs_enabled &&
|
||||
!IPMI_BT_GET_B2H_IRQ(ib->mask_reg) &&
|
||||
IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
|
||||
IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
|
||||
qemu_irq_raise(ib->irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static uint64_t ipmi_bt_ioport_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
IPMIInterface *ii = opaque;
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIBT *ib = iic->get_backend_data(ii);
|
||||
uint32_t ret = 0xff;
|
||||
|
||||
switch (addr & 3) {
|
||||
case 0:
|
||||
ret = ib->control_reg;
|
||||
break;
|
||||
case 1:
|
||||
if (ib->outpos < ib->outlen) {
|
||||
ret = ib->outmsg[ib->outpos];
|
||||
ib->outpos++;
|
||||
if (ib->outpos == ib->outlen) {
|
||||
ib->outpos = 0;
|
||||
ib->outlen = 0;
|
||||
}
|
||||
} else {
|
||||
ret = 0xff;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
ret = ib->mask_reg;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ipmi_bt_signal(IPMIBT *ib, IPMIInterface *ii)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
|
||||
ib->do_wake = 1;
|
||||
while (ib->do_wake) {
|
||||
ib->do_wake = 0;
|
||||
iic->handle_if_event(ii);
|
||||
}
|
||||
}
|
||||
|
||||
static void ipmi_bt_ioport_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
unsigned size)
|
||||
{
|
||||
IPMIInterface *ii = opaque;
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIBT *ib = iic->get_backend_data(ii);
|
||||
|
||||
switch (addr & 3) {
|
||||
case 0:
|
||||
if (IPMI_BT_GET_CLR_WR(val)) {
|
||||
ib->inlen = 0;
|
||||
}
|
||||
if (IPMI_BT_GET_CLR_RD(val)) {
|
||||
ib->outpos = 0;
|
||||
}
|
||||
if (IPMI_BT_GET_B2H_ATN(val)) {
|
||||
IPMI_BT_SET_B2H_ATN(ib->control_reg, 0);
|
||||
}
|
||||
if (IPMI_BT_GET_SMS_ATN(val)) {
|
||||
IPMI_BT_SET_SMS_ATN(ib->control_reg, 0);
|
||||
}
|
||||
if (IPMI_BT_GET_HBUSY(val)) {
|
||||
/* Toggle */
|
||||
IPMI_BT_SET_HBUSY(ib->control_reg,
|
||||
!IPMI_BT_GET_HBUSY(ib->control_reg));
|
||||
}
|
||||
if (IPMI_BT_GET_H2B_ATN(val)) {
|
||||
IPMI_BT_SET_BBUSY(ib->control_reg, 1);
|
||||
ipmi_bt_signal(ib, ii);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (ib->inlen < sizeof(ib->inmsg)) {
|
||||
ib->inmsg[ib->inlen] = val;
|
||||
}
|
||||
ib->inlen++;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (IPMI_BT_GET_B2H_IRQ_EN(val) !=
|
||||
IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
|
||||
if (IPMI_BT_GET_B2H_IRQ_EN(val)) {
|
||||
if (IPMI_BT_GET_B2H_ATN(ib->control_reg) ||
|
||||
IPMI_BT_GET_SMS_ATN(ib->control_reg)) {
|
||||
IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
|
||||
qemu_irq_raise(ib->irq);
|
||||
}
|
||||
IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 1);
|
||||
} else {
|
||||
if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
|
||||
IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
|
||||
qemu_irq_lower(ib->irq);
|
||||
}
|
||||
IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0);
|
||||
}
|
||||
}
|
||||
if (IPMI_BT_GET_B2H_IRQ(val) && IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
|
||||
IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
|
||||
qemu_irq_lower(ib->irq);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipmi_bt_io_ops = {
|
||||
.read = ipmi_bt_ioport_read,
|
||||
.write = ipmi_bt_ioport_write,
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 1,
|
||||
},
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipmi_bt_set_atn(IPMIInterface *ii, int val, int irq)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIBT *ib = iic->get_backend_data(ii);
|
||||
|
||||
if (!!val == IPMI_BT_GET_SMS_ATN(ib->control_reg)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IPMI_BT_SET_SMS_ATN(ib->control_reg, val);
|
||||
if (val) {
|
||||
if (irq && ib->use_irq && ib->irqs_enabled &&
|
||||
!IPMI_BT_GET_B2H_ATN(ib->control_reg) &&
|
||||
IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
|
||||
IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
|
||||
qemu_irq_raise(ib->irq);
|
||||
}
|
||||
} else {
|
||||
if (!IPMI_BT_GET_B2H_ATN(ib->control_reg) &&
|
||||
IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
|
||||
IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
|
||||
qemu_irq_lower(ib->irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ipmi_bt_handle_reset(IPMIInterface *ii, bool is_cold)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIBT *ib = iic->get_backend_data(ii);
|
||||
|
||||
if (is_cold) {
|
||||
/* Disable the BT interrupt on reset */
|
||||
if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
|
||||
IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
|
||||
qemu_irq_lower(ib->irq);
|
||||
}
|
||||
IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void ipmi_bt_set_irq_enable(IPMIInterface *ii, int val)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIBT *ib = iic->get_backend_data(ii);
|
||||
|
||||
ib->irqs_enabled = val;
|
||||
}
|
||||
|
||||
static void ipmi_bt_init(IPMIInterface *ii, Error **errp)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIBT *ib = iic->get_backend_data(ii);
|
||||
|
||||
ib->io_length = 3;
|
||||
|
||||
memory_region_init_io(&ib->io, NULL, &ipmi_bt_io_ops, ii, "ipmi-bt", 3);
|
||||
}
|
||||
|
||||
static void ipmi_bt_class_init(IPMIInterfaceClass *iic)
|
||||
{
|
||||
iic->init = ipmi_bt_init;
|
||||
iic->set_atn = ipmi_bt_set_atn;
|
||||
iic->handle_rsp = ipmi_bt_handle_rsp;
|
||||
iic->handle_if_event = ipmi_bt_handle_event;
|
||||
iic->set_irq_enable = ipmi_bt_set_irq_enable;
|
||||
iic->reset = ipmi_bt_handle_reset;
|
||||
}
|
||||
|
||||
|
||||
#define TYPE_ISA_IPMI_BT "isa-ipmi-bt"
|
||||
#define ISA_IPMI_BT(obj) OBJECT_CHECK(ISAIPMIBTDevice, (obj), \
|
||||
TYPE_ISA_IPMI_BT)
|
||||
|
||||
typedef struct ISAIPMIBTDevice {
|
||||
ISADevice dev;
|
||||
int32 isairq;
|
||||
IPMIBT bt;
|
||||
IPMIFwInfo fwinfo;
|
||||
} ISAIPMIBTDevice;
|
||||
|
||||
static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
ISADevice *isadev = ISA_DEVICE(dev);
|
||||
ISAIPMIBTDevice *iib = ISA_IPMI_BT(dev);
|
||||
IPMIInterface *ii = IPMI_INTERFACE(dev);
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
|
||||
if (!iib->bt.bmc) {
|
||||
error_setg(errp, "IPMI device requires a bmc attribute to be set");
|
||||
return;
|
||||
}
|
||||
|
||||
iib->bt.bmc->intf = ii;
|
||||
|
||||
iic->init(ii, errp);
|
||||
if (*errp)
|
||||
return;
|
||||
|
||||
if (iib->isairq > 0) {
|
||||
isa_init_irq(isadev, &iib->bt.irq, iib->isairq);
|
||||
iib->bt.use_irq = 1;
|
||||
}
|
||||
|
||||
qdev_set_legacy_instance_id(dev, iib->bt.io_base, iib->bt.io_length);
|
||||
|
||||
isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base);
|
||||
|
||||
iib->fwinfo.interface_name = "bt";
|
||||
iib->fwinfo.interface_type = IPMI_SMBIOS_BT;
|
||||
iib->fwinfo.ipmi_spec_major_revision = 2;
|
||||
iib->fwinfo.ipmi_spec_minor_revision = 0;
|
||||
iib->fwinfo.base_address = iib->bt.io_base;
|
||||
iib->fwinfo.register_length = iib->bt.io_length;
|
||||
iib->fwinfo.register_spacing = 1;
|
||||
iib->fwinfo.memspace = IPMI_MEMSPACE_IO;
|
||||
iib->fwinfo.irq_type = IPMI_LEVEL_IRQ;
|
||||
iib->fwinfo.interrupt_number = iib->isairq;
|
||||
iib->fwinfo.acpi_parent = "\\_SB.PCI0.ISA";
|
||||
iib->fwinfo.i2c_slave_address = iib->bt.bmc->slave_addr;
|
||||
ipmi_add_fwinfo(&iib->fwinfo, errp);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_ISAIPMIBTDevice = {
|
||||
.name = TYPE_IPMI_INTERFACE,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_BOOL(bt.obf_irq_set, ISAIPMIBTDevice),
|
||||
VMSTATE_BOOL(bt.atn_irq_set, ISAIPMIBTDevice),
|
||||
VMSTATE_BOOL(bt.use_irq, ISAIPMIBTDevice),
|
||||
VMSTATE_BOOL(bt.irqs_enabled, ISAIPMIBTDevice),
|
||||
VMSTATE_UINT32(bt.outpos, ISAIPMIBTDevice),
|
||||
VMSTATE_VBUFFER_UINT32(bt.outmsg, ISAIPMIBTDevice, 1, NULL, 0,
|
||||
bt.outlen),
|
||||
VMSTATE_VBUFFER_UINT32(bt.inmsg, ISAIPMIBTDevice, 1, NULL, 0,
|
||||
bt.inlen),
|
||||
VMSTATE_UINT8(bt.control_reg, ISAIPMIBTDevice),
|
||||
VMSTATE_UINT8(bt.mask_reg, ISAIPMIBTDevice),
|
||||
VMSTATE_UINT8(bt.waiting_rsp, ISAIPMIBTDevice),
|
||||
VMSTATE_UINT8(bt.waiting_seq, ISAIPMIBTDevice),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void isa_ipmi_bt_init(Object *obj)
|
||||
{
|
||||
ISAIPMIBTDevice *iib = ISA_IPMI_BT(obj);
|
||||
|
||||
ipmi_bmc_find_and_link(obj, (Object **) &iib->bt.bmc);
|
||||
|
||||
vmstate_register(NULL, 0, &vmstate_ISAIPMIBTDevice, iib);
|
||||
}
|
||||
|
||||
static void *isa_ipmi_bt_get_backend_data(IPMIInterface *ii)
|
||||
{
|
||||
ISAIPMIBTDevice *iib = ISA_IPMI_BT(ii);
|
||||
|
||||
return &iib->bt;
|
||||
}
|
||||
|
||||
static Property ipmi_isa_properties[] = {
|
||||
DEFINE_PROP_UINT32("ioport", ISAIPMIBTDevice, bt.io_base, 0xe4),
|
||||
DEFINE_PROP_INT32("irq", ISAIPMIBTDevice, isairq, 5),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void isa_ipmi_bt_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc);
|
||||
|
||||
dc->realize = isa_ipmi_bt_realize;
|
||||
dc->props = ipmi_isa_properties;
|
||||
|
||||
iic->get_backend_data = isa_ipmi_bt_get_backend_data;
|
||||
ipmi_bt_class_init(iic);
|
||||
}
|
||||
|
||||
static const TypeInfo isa_ipmi_bt_info = {
|
||||
.name = TYPE_ISA_IPMI_BT,
|
||||
.parent = TYPE_ISA_DEVICE,
|
||||
.instance_size = sizeof(ISAIPMIBTDevice),
|
||||
.instance_init = isa_ipmi_bt_init,
|
||||
.class_init = isa_ipmi_bt_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_IPMI_INTERFACE },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
static void ipmi_register_types(void)
|
||||
{
|
||||
type_register_static(&isa_ipmi_bt_info);
|
||||
}
|
||||
|
||||
type_init(ipmi_register_types)
|
|
@ -0,0 +1,493 @@
|
|||
/*
|
||||
* QEMU ISA IPMI KCS emulation
|
||||
*
|
||||
* Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "hw/hw.h"
|
||||
#include "hw/ipmi/ipmi.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/i386/pc.h"
|
||||
|
||||
#define IPMI_KCS_OBF_BIT 0
|
||||
#define IPMI_KCS_IBF_BIT 1
|
||||
#define IPMI_KCS_SMS_ATN_BIT 2
|
||||
#define IPMI_KCS_CD_BIT 3
|
||||
|
||||
#define IPMI_KCS_OBF_MASK (1 << IPMI_KCS_OBF_BIT)
|
||||
#define IPMI_KCS_GET_OBF(d) (((d) >> IPMI_KCS_OBF_BIT) & 0x1)
|
||||
#define IPMI_KCS_SET_OBF(d, v) (d) = (((d) & ~IPMI_KCS_OBF_MASK) | \
|
||||
(((v) & 1) << IPMI_KCS_OBF_BIT))
|
||||
#define IPMI_KCS_IBF_MASK (1 << IPMI_KCS_IBF_BIT)
|
||||
#define IPMI_KCS_GET_IBF(d) (((d) >> IPMI_KCS_IBF_BIT) & 0x1)
|
||||
#define IPMI_KCS_SET_IBF(d, v) (d) = (((d) & ~IPMI_KCS_IBF_MASK) | \
|
||||
(((v) & 1) << IPMI_KCS_IBF_BIT))
|
||||
#define IPMI_KCS_SMS_ATN_MASK (1 << IPMI_KCS_SMS_ATN_BIT)
|
||||
#define IPMI_KCS_GET_SMS_ATN(d) (((d) >> IPMI_KCS_SMS_ATN_BIT) & 0x1)
|
||||
#define IPMI_KCS_SET_SMS_ATN(d, v) (d) = (((d) & ~IPMI_KCS_SMS_ATN_MASK) | \
|
||||
(((v) & 1) << IPMI_KCS_SMS_ATN_BIT))
|
||||
#define IPMI_KCS_CD_MASK (1 << IPMI_KCS_CD_BIT)
|
||||
#define IPMI_KCS_GET_CD(d) (((d) >> IPMI_KCS_CD_BIT) & 0x1)
|
||||
#define IPMI_KCS_SET_CD(d, v) (d) = (((d) & ~IPMI_KCS_CD_MASK) | \
|
||||
(((v) & 1) << IPMI_KCS_CD_BIT))
|
||||
|
||||
#define IPMI_KCS_IDLE_STATE 0
|
||||
#define IPMI_KCS_READ_STATE 1
|
||||
#define IPMI_KCS_WRITE_STATE 2
|
||||
#define IPMI_KCS_ERROR_STATE 3
|
||||
|
||||
#define IPMI_KCS_GET_STATE(d) (((d) >> 6) & 0x3)
|
||||
#define IPMI_KCS_SET_STATE(d, v) ((d) = ((d) & ~0xc0) | (((v) & 0x3) << 6))
|
||||
|
||||
#define IPMI_KCS_ABORT_STATUS_CMD 0x60
|
||||
#define IPMI_KCS_WRITE_START_CMD 0x61
|
||||
#define IPMI_KCS_WRITE_END_CMD 0x62
|
||||
#define IPMI_KCS_READ_CMD 0x68
|
||||
|
||||
#define IPMI_KCS_STATUS_NO_ERR 0x00
|
||||
#define IPMI_KCS_STATUS_ABORTED_ERR 0x01
|
||||
#define IPMI_KCS_STATUS_BAD_CC_ERR 0x02
|
||||
#define IPMI_KCS_STATUS_LENGTH_ERR 0x06
|
||||
|
||||
typedef struct IPMIKCS {
|
||||
IPMIBmc *bmc;
|
||||
|
||||
bool do_wake;
|
||||
|
||||
qemu_irq irq;
|
||||
|
||||
uint32_t io_base;
|
||||
unsigned long io_length;
|
||||
MemoryRegion io;
|
||||
|
||||
bool obf_irq_set;
|
||||
bool atn_irq_set;
|
||||
bool use_irq;
|
||||
bool irqs_enabled;
|
||||
|
||||
uint8_t outmsg[MAX_IPMI_MSG_SIZE];
|
||||
uint32_t outpos;
|
||||
uint32_t outlen;
|
||||
|
||||
uint8_t inmsg[MAX_IPMI_MSG_SIZE];
|
||||
uint32_t inlen;
|
||||
bool write_end;
|
||||
|
||||
uint8_t status_reg;
|
||||
uint8_t data_out_reg;
|
||||
|
||||
int16_t data_in_reg; /* -1 means not written */
|
||||
int16_t cmd_reg;
|
||||
|
||||
/*
|
||||
* This is a response number that we send with the command to make
|
||||
* sure that the response matches the command.
|
||||
*/
|
||||
uint8_t waiting_rsp;
|
||||
} IPMIKCS;
|
||||
|
||||
#define SET_OBF() \
|
||||
do { \
|
||||
IPMI_KCS_SET_OBF(ik->status_reg, 1); \
|
||||
if (ik->use_irq && ik->irqs_enabled && !ik->obf_irq_set) { \
|
||||
ik->obf_irq_set = 1; \
|
||||
if (!ik->atn_irq_set) { \
|
||||
qemu_irq_raise(ik->irq); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static void ipmi_kcs_signal(IPMIKCS *ik, IPMIInterface *ii)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
|
||||
ik->do_wake = 1;
|
||||
while (ik->do_wake) {
|
||||
ik->do_wake = 0;
|
||||
iic->handle_if_event(ii);
|
||||
}
|
||||
}
|
||||
|
||||
static void ipmi_kcs_handle_event(IPMIInterface *ii)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIKCS *ik = iic->get_backend_data(ii);
|
||||
|
||||
if (ik->cmd_reg == IPMI_KCS_ABORT_STATUS_CMD) {
|
||||
if (IPMI_KCS_GET_STATE(ik->status_reg) != IPMI_KCS_ERROR_STATE) {
|
||||
ik->waiting_rsp++; /* Invalidate the message */
|
||||
ik->outmsg[0] = IPMI_KCS_STATUS_ABORTED_ERR;
|
||||
ik->outlen = 1;
|
||||
ik->outpos = 0;
|
||||
IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE);
|
||||
SET_OBF();
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (IPMI_KCS_GET_STATE(ik->status_reg)) {
|
||||
case IPMI_KCS_IDLE_STATE:
|
||||
if (ik->cmd_reg == IPMI_KCS_WRITE_START_CMD) {
|
||||
IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_WRITE_STATE);
|
||||
ik->cmd_reg = -1;
|
||||
ik->write_end = 0;
|
||||
ik->inlen = 0;
|
||||
SET_OBF();
|
||||
}
|
||||
break;
|
||||
|
||||
case IPMI_KCS_READ_STATE:
|
||||
handle_read:
|
||||
if (ik->outpos >= ik->outlen) {
|
||||
IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_IDLE_STATE);
|
||||
SET_OBF();
|
||||
} else if (ik->data_in_reg == IPMI_KCS_READ_CMD) {
|
||||
ik->data_out_reg = ik->outmsg[ik->outpos];
|
||||
ik->outpos++;
|
||||
SET_OBF();
|
||||
} else {
|
||||
ik->outmsg[0] = IPMI_KCS_STATUS_BAD_CC_ERR;
|
||||
ik->outlen = 1;
|
||||
ik->outpos = 0;
|
||||
IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE);
|
||||
SET_OBF();
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case IPMI_KCS_WRITE_STATE:
|
||||
if (ik->data_in_reg != -1) {
|
||||
/*
|
||||
* Don't worry about input overrun here, that will be
|
||||
* handled in the BMC.
|
||||
*/
|
||||
if (ik->inlen < sizeof(ik->inmsg)) {
|
||||
ik->inmsg[ik->inlen] = ik->data_in_reg;
|
||||
}
|
||||
ik->inlen++;
|
||||
}
|
||||
if (ik->write_end) {
|
||||
IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(ik->bmc);
|
||||
ik->outlen = 0;
|
||||
ik->write_end = 0;
|
||||
ik->outpos = 0;
|
||||
bk->handle_command(ik->bmc, ik->inmsg, ik->inlen, sizeof(ik->inmsg),
|
||||
ik->waiting_rsp);
|
||||
goto out_noibf;
|
||||
} else if (ik->cmd_reg == IPMI_KCS_WRITE_END_CMD) {
|
||||
ik->cmd_reg = -1;
|
||||
ik->write_end = 1;
|
||||
}
|
||||
SET_OBF();
|
||||
break;
|
||||
|
||||
case IPMI_KCS_ERROR_STATE:
|
||||
if (ik->data_in_reg != -1) {
|
||||
IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_READ_STATE);
|
||||
ik->data_in_reg = IPMI_KCS_READ_CMD;
|
||||
goto handle_read;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (ik->cmd_reg != -1) {
|
||||
/* Got an invalid command */
|
||||
ik->outmsg[0] = IPMI_KCS_STATUS_BAD_CC_ERR;
|
||||
ik->outlen = 1;
|
||||
ik->outpos = 0;
|
||||
IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE);
|
||||
}
|
||||
|
||||
out:
|
||||
ik->cmd_reg = -1;
|
||||
ik->data_in_reg = -1;
|
||||
IPMI_KCS_SET_IBF(ik->status_reg, 0);
|
||||
out_noibf:
|
||||
return;
|
||||
}
|
||||
|
||||
static void ipmi_kcs_handle_rsp(IPMIInterface *ii, uint8_t msg_id,
|
||||
unsigned char *rsp, unsigned int rsp_len)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIKCS *ik = iic->get_backend_data(ii);
|
||||
|
||||
if (ik->waiting_rsp == msg_id) {
|
||||
ik->waiting_rsp++;
|
||||
if (rsp_len > sizeof(ik->outmsg)) {
|
||||
ik->outmsg[0] = rsp[0];
|
||||
ik->outmsg[1] = rsp[1];
|
||||
ik->outmsg[2] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES;
|
||||
ik->outlen = 3;
|
||||
} else {
|
||||
memcpy(ik->outmsg, rsp, rsp_len);
|
||||
ik->outlen = rsp_len;
|
||||
}
|
||||
IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_READ_STATE);
|
||||
ik->data_in_reg = IPMI_KCS_READ_CMD;
|
||||
ipmi_kcs_signal(ik, ii);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static uint64_t ipmi_kcs_ioport_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
IPMIInterface *ii = opaque;
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIKCS *ik = iic->get_backend_data(ii);
|
||||
uint32_t ret;
|
||||
|
||||
switch (addr & 1) {
|
||||
case 0:
|
||||
ret = ik->data_out_reg;
|
||||
IPMI_KCS_SET_OBF(ik->status_reg, 0);
|
||||
if (ik->obf_irq_set) {
|
||||
ik->obf_irq_set = 0;
|
||||
if (!ik->atn_irq_set) {
|
||||
qemu_irq_lower(ik->irq);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
ret = ik->status_reg;
|
||||
if (ik->atn_irq_set) {
|
||||
ik->atn_irq_set = 0;
|
||||
if (!ik->obf_irq_set) {
|
||||
qemu_irq_lower(ik->irq);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ipmi_kcs_ioport_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
unsigned size)
|
||||
{
|
||||
IPMIInterface *ii = opaque;
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIKCS *ik = iic->get_backend_data(ii);
|
||||
|
||||
if (IPMI_KCS_GET_IBF(ik->status_reg)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (addr & 1) {
|
||||
case 0:
|
||||
ik->data_in_reg = val;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
ik->cmd_reg = val;
|
||||
break;
|
||||
}
|
||||
IPMI_KCS_SET_IBF(ik->status_reg, 1);
|
||||
ipmi_kcs_signal(ik, ii);
|
||||
}
|
||||
|
||||
const MemoryRegionOps ipmi_kcs_io_ops = {
|
||||
.read = ipmi_kcs_ioport_read,
|
||||
.write = ipmi_kcs_ioport_write,
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 1,
|
||||
},
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipmi_kcs_set_atn(IPMIInterface *ii, int val, int irq)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIKCS *ik = iic->get_backend_data(ii);
|
||||
|
||||
IPMI_KCS_SET_SMS_ATN(ik->status_reg, val);
|
||||
if (val) {
|
||||
if (irq && !ik->atn_irq_set && ik->use_irq && ik->irqs_enabled) {
|
||||
ik->atn_irq_set = 1;
|
||||
if (!ik->obf_irq_set) {
|
||||
qemu_irq_raise(ik->irq);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (ik->atn_irq_set) {
|
||||
ik->atn_irq_set = 0;
|
||||
if (!ik->obf_irq_set) {
|
||||
qemu_irq_lower(ik->irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ipmi_kcs_set_irq_enable(IPMIInterface *ii, int val)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIKCS *ik = iic->get_backend_data(ii);
|
||||
|
||||
ik->irqs_enabled = val;
|
||||
}
|
||||
|
||||
static void ipmi_kcs_init(IPMIInterface *ii, Error **errp)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIKCS *ik = iic->get_backend_data(ii);
|
||||
|
||||
ik->io_length = 2;
|
||||
memory_region_init_io(&ik->io, NULL, &ipmi_kcs_io_ops, ii, "ipmi-kcs", 2);
|
||||
}
|
||||
|
||||
static void ipmi_kcs_class_init(IPMIInterfaceClass *iic)
|
||||
{
|
||||
iic->init = ipmi_kcs_init;
|
||||
iic->set_atn = ipmi_kcs_set_atn;
|
||||
iic->handle_rsp = ipmi_kcs_handle_rsp;
|
||||
iic->handle_if_event = ipmi_kcs_handle_event;
|
||||
iic->set_irq_enable = ipmi_kcs_set_irq_enable;
|
||||
}
|
||||
|
||||
|
||||
#define TYPE_ISA_IPMI_KCS "isa-ipmi-kcs"
|
||||
#define ISA_IPMI_KCS(obj) OBJECT_CHECK(ISAIPMIKCSDevice, (obj), \
|
||||
TYPE_ISA_IPMI_KCS)
|
||||
|
||||
typedef struct ISAIPMIKCSDevice {
|
||||
ISADevice dev;
|
||||
int32 isairq;
|
||||
IPMIKCS kcs;
|
||||
IPMIFwInfo fwinfo;
|
||||
} ISAIPMIKCSDevice;
|
||||
|
||||
static void ipmi_isa_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
ISADevice *isadev = ISA_DEVICE(dev);
|
||||
ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(dev);
|
||||
IPMIInterface *ii = IPMI_INTERFACE(dev);
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
|
||||
if (!iik->kcs.bmc) {
|
||||
error_setg(errp, "IPMI device requires a bmc attribute to be set");
|
||||
return;
|
||||
}
|
||||
|
||||
iik->kcs.bmc->intf = ii;
|
||||
|
||||
iic->init(ii, errp);
|
||||
if (*errp)
|
||||
return;
|
||||
|
||||
if (iik->isairq > 0) {
|
||||
isa_init_irq(isadev, &iik->kcs.irq, iik->isairq);
|
||||
iik->kcs.use_irq = 1;
|
||||
}
|
||||
|
||||
qdev_set_legacy_instance_id(dev, iik->kcs.io_base, iik->kcs.io_length);
|
||||
|
||||
isa_register_ioport(isadev, &iik->kcs.io, iik->kcs.io_base);
|
||||
|
||||
iik->fwinfo.interface_name = "kcs";
|
||||
iik->fwinfo.interface_type = IPMI_SMBIOS_KCS;
|
||||
iik->fwinfo.ipmi_spec_major_revision = 2;
|
||||
iik->fwinfo.ipmi_spec_minor_revision = 0;
|
||||
iik->fwinfo.base_address = iik->kcs.io_base;
|
||||
iik->fwinfo.i2c_slave_address = iik->kcs.bmc->slave_addr;
|
||||
iik->fwinfo.register_length = iik->kcs.io_length;
|
||||
iik->fwinfo.register_spacing = 1;
|
||||
iik->fwinfo.memspace = IPMI_MEMSPACE_IO;
|
||||
iik->fwinfo.irq_type = IPMI_LEVEL_IRQ;
|
||||
iik->fwinfo.interrupt_number = iik->isairq;
|
||||
iik->fwinfo.acpi_parent = "\\_SB.PCI0.ISA";
|
||||
ipmi_add_fwinfo(&iik->fwinfo, errp);
|
||||
}
|
||||
|
||||
const VMStateDescription vmstate_ISAIPMIKCSDevice = {
|
||||
.name = TYPE_IPMI_INTERFACE,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_BOOL(kcs.obf_irq_set, ISAIPMIKCSDevice),
|
||||
VMSTATE_BOOL(kcs.atn_irq_set, ISAIPMIKCSDevice),
|
||||
VMSTATE_BOOL(kcs.use_irq, ISAIPMIKCSDevice),
|
||||
VMSTATE_BOOL(kcs.irqs_enabled, ISAIPMIKCSDevice),
|
||||
VMSTATE_UINT32(kcs.outpos, ISAIPMIKCSDevice),
|
||||
VMSTATE_VBUFFER_UINT32(kcs.outmsg, ISAIPMIKCSDevice, 1, NULL, 0,
|
||||
kcs.outlen),
|
||||
VMSTATE_VBUFFER_UINT32(kcs.inmsg, ISAIPMIKCSDevice, 1, NULL, 0,
|
||||
kcs.inlen),
|
||||
VMSTATE_BOOL(kcs.write_end, ISAIPMIKCSDevice),
|
||||
VMSTATE_UINT8(kcs.status_reg, ISAIPMIKCSDevice),
|
||||
VMSTATE_UINT8(kcs.data_out_reg, ISAIPMIKCSDevice),
|
||||
VMSTATE_INT16(kcs.data_in_reg, ISAIPMIKCSDevice),
|
||||
VMSTATE_INT16(kcs.cmd_reg, ISAIPMIKCSDevice),
|
||||
VMSTATE_UINT8(kcs.waiting_rsp, ISAIPMIKCSDevice),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void isa_ipmi_kcs_init(Object *obj)
|
||||
{
|
||||
ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(obj);
|
||||
|
||||
ipmi_bmc_find_and_link(obj, (Object **) &iik->kcs.bmc);
|
||||
|
||||
vmstate_register(NULL, 0, &vmstate_ISAIPMIKCSDevice, iik);
|
||||
}
|
||||
|
||||
static void *isa_ipmi_kcs_get_backend_data(IPMIInterface *ii)
|
||||
{
|
||||
ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(ii);
|
||||
|
||||
return &iik->kcs;
|
||||
}
|
||||
|
||||
static Property ipmi_isa_properties[] = {
|
||||
DEFINE_PROP_UINT32("ioport", ISAIPMIKCSDevice, kcs.io_base, 0xca2),
|
||||
DEFINE_PROP_INT32("irq", ISAIPMIKCSDevice, isairq, 5),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void isa_ipmi_kcs_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc);
|
||||
|
||||
dc->realize = ipmi_isa_realize;
|
||||
dc->props = ipmi_isa_properties;
|
||||
|
||||
iic->get_backend_data = isa_ipmi_kcs_get_backend_data;
|
||||
ipmi_kcs_class_init(iic);
|
||||
}
|
||||
|
||||
static const TypeInfo isa_ipmi_kcs_info = {
|
||||
.name = TYPE_ISA_IPMI_KCS,
|
||||
.parent = TYPE_ISA_DEVICE,
|
||||
.instance_size = sizeof(ISAIPMIKCSDevice),
|
||||
.instance_init = isa_ipmi_kcs_init,
|
||||
.class_init = isa_ipmi_kcs_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_IPMI_INTERFACE },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
static void ipmi_register_types(void)
|
||||
{
|
||||
type_register_static(&isa_ipmi_kcs_info);
|
||||
}
|
||||
|
||||
type_init(ipmi_register_types)
|
|
@ -1 +1,2 @@
|
|||
common-obj-$(CONFIG_MEM_HOTPLUG) += pc-dimm.o
|
||||
common-obj-$(CONFIG_NVDIMM) += nvdimm.o
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Non-Volatile Dual In-line Memory Module Virtualization Implementation
|
||||
*
|
||||
* Copyright(C) 2015 Intel Corporation.
|
||||
*
|
||||
* Author:
|
||||
* Xiao Guangrong <guangrong.xiao@linux.intel.com>
|
||||
*
|
||||
* Currently, it only supports PMEM Virtualization.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "hw/mem/nvdimm.h"
|
||||
|
||||
static void nvdimm_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
/* nvdimm hotplug has not been supported yet. */
|
||||
dc->hotpluggable = false;
|
||||
}
|
||||
|
||||
static TypeInfo nvdimm_info = {
|
||||
.name = TYPE_NVDIMM,
|
||||
.parent = TYPE_PC_DIMM,
|
||||
.class_init = nvdimm_class_init,
|
||||
};
|
||||
|
||||
static void nvdimm_register_types(void)
|
||||
{
|
||||
type_register_static(&nvdimm_info);
|
||||
}
|
||||
|
||||
type_init(nvdimm_register_types)
|
|
@ -23,6 +23,9 @@
|
|||
#define TYPE_PXB_BUS "pxb-bus"
|
||||
#define PXB_BUS(obj) OBJECT_CHECK(PXBBus, (obj), TYPE_PXB_BUS)
|
||||
|
||||
#define TYPE_PXB_PCIE_BUS "pxb-pcie-bus"
|
||||
#define PXB_PCIE_BUS(obj) OBJECT_CHECK(PXBBus, (obj), TYPE_PXB_PCIE_BUS)
|
||||
|
||||
typedef struct PXBBus {
|
||||
/*< private >*/
|
||||
PCIBus parent_obj;
|
||||
|
@ -34,6 +37,9 @@ typedef struct PXBBus {
|
|||
#define TYPE_PXB_DEVICE "pxb"
|
||||
#define PXB_DEV(obj) OBJECT_CHECK(PXBDev, (obj), TYPE_PXB_DEVICE)
|
||||
|
||||
#define TYPE_PXB_PCIE_DEVICE "pxb-pcie"
|
||||
#define PXB_PCIE_DEV(obj) OBJECT_CHECK(PXBDev, (obj), TYPE_PXB_PCIE_DEVICE)
|
||||
|
||||
typedef struct PXBDev {
|
||||
/*< private >*/
|
||||
PCIDevice parent_obj;
|
||||
|
@ -43,13 +49,18 @@ typedef struct PXBDev {
|
|||
uint16_t numa_node;
|
||||
} PXBDev;
|
||||
|
||||
static PXBDev *convert_to_pxb(PCIDevice *dev)
|
||||
{
|
||||
return pci_bus_is_express(dev->bus) ? PXB_PCIE_DEV(dev) : PXB_DEV(dev);
|
||||
}
|
||||
|
||||
static GList *pxb_dev_list;
|
||||
|
||||
#define TYPE_PXB_HOST "pxb-host"
|
||||
|
||||
static int pxb_bus_num(PCIBus *bus)
|
||||
{
|
||||
PXBDev *pxb = PXB_DEV(bus->parent_dev);
|
||||
PXBDev *pxb = convert_to_pxb(bus->parent_dev);
|
||||
|
||||
return pxb->bus_nr;
|
||||
}
|
||||
|
@ -61,7 +72,7 @@ static bool pxb_is_root(PCIBus *bus)
|
|||
|
||||
static uint16_t pxb_bus_numa_node(PCIBus *bus)
|
||||
{
|
||||
PXBDev *pxb = PXB_DEV(bus->parent_dev);
|
||||
PXBDev *pxb = convert_to_pxb(bus->parent_dev);
|
||||
|
||||
return pxb->numa_node;
|
||||
}
|
||||
|
@ -82,10 +93,18 @@ static const TypeInfo pxb_bus_info = {
|
|||
.class_init = pxb_bus_class_init,
|
||||
};
|
||||
|
||||
static const TypeInfo pxb_pcie_bus_info = {
|
||||
.name = TYPE_PXB_PCIE_BUS,
|
||||
.parent = TYPE_PCIE_BUS,
|
||||
.instance_size = sizeof(PXBBus),
|
||||
.class_init = pxb_bus_class_init,
|
||||
};
|
||||
|
||||
static const char *pxb_host_root_bus_path(PCIHostState *host_bridge,
|
||||
PCIBus *rootbus)
|
||||
{
|
||||
PXBBus *bus = PXB_BUS(rootbus);
|
||||
PXBBus *bus = pci_bus_is_express(rootbus) ?
|
||||
PXB_PCIE_BUS(rootbus) : PXB_BUS(rootbus);
|
||||
|
||||
snprintf(bus->bus_path, 8, "0000:%02x", pxb_bus_num(rootbus));
|
||||
return bus->bus_path;
|
||||
|
@ -103,7 +122,7 @@ static char *pxb_host_ofw_unit_address(const SysBusDevice *dev)
|
|||
|
||||
pxb_host = PCI_HOST_BRIDGE(dev);
|
||||
pxb_bus = pxb_host->bus;
|
||||
pxb_dev = PXB_DEV(pxb_bus->parent_dev);
|
||||
pxb_dev = convert_to_pxb(pxb_bus->parent_dev);
|
||||
position = g_list_index(pxb_dev_list, pxb_dev);
|
||||
assert(position >= 0);
|
||||
|
||||
|
@ -193,10 +212,10 @@ static gint pxb_compare(gconstpointer a, gconstpointer b)
|
|||
0;
|
||||
}
|
||||
|
||||
static int pxb_dev_initfn(PCIDevice *dev)
|
||||
static int pxb_dev_init_common(PCIDevice *dev, bool pcie)
|
||||
{
|
||||
PXBDev *pxb = PXB_DEV(dev);
|
||||
DeviceState *ds, *bds;
|
||||
PXBDev *pxb = convert_to_pxb(dev);
|
||||
DeviceState *ds, *bds = NULL;
|
||||
PCIBus *bus;
|
||||
const char *dev_name = NULL;
|
||||
|
||||
|
@ -211,18 +230,21 @@ static int pxb_dev_initfn(PCIDevice *dev)
|
|||
}
|
||||
|
||||
ds = qdev_create(NULL, TYPE_PXB_HOST);
|
||||
bus = pci_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS);
|
||||
if (pcie) {
|
||||
bus = pci_bus_new(ds, dev_name, NULL, NULL, 0, TYPE_PXB_PCIE_BUS);
|
||||
} else {
|
||||
bus = pci_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS);
|
||||
bds = qdev_create(BUS(bus), "pci-bridge");
|
||||
bds->id = dev_name;
|
||||
qdev_prop_set_uint8(bds, PCI_BRIDGE_DEV_PROP_CHASSIS_NR, pxb->bus_nr);
|
||||
qdev_prop_set_bit(bds, PCI_BRIDGE_DEV_PROP_SHPC, false);
|
||||
}
|
||||
|
||||
bus->parent_dev = dev;
|
||||
bus->address_space_mem = dev->bus->address_space_mem;
|
||||
bus->address_space_io = dev->bus->address_space_io;
|
||||
bus->map_irq = pxb_map_irq_fn;
|
||||
|
||||
bds = qdev_create(BUS(bus), "pci-bridge");
|
||||
bds->id = dev_name;
|
||||
qdev_prop_set_uint8(bds, PCI_BRIDGE_DEV_PROP_CHASSIS_NR, pxb->bus_nr);
|
||||
qdev_prop_set_bit(bds, PCI_BRIDGE_DEV_PROP_SHPC, false);
|
||||
|
||||
PCI_HOST_BRIDGE(ds)->bus = bus;
|
||||
|
||||
if (pxb_register_bus(dev, bus)) {
|
||||
|
@ -230,7 +252,9 @@ static int pxb_dev_initfn(PCIDevice *dev)
|
|||
}
|
||||
|
||||
qdev_init_nofail(ds);
|
||||
qdev_init_nofail(bds);
|
||||
if (bds) {
|
||||
qdev_init_nofail(bds);
|
||||
}
|
||||
|
||||
pci_word_test_and_set_mask(dev->config + PCI_STATUS,
|
||||
PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
|
||||
|
@ -240,9 +264,19 @@ static int pxb_dev_initfn(PCIDevice *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pxb_dev_initfn(PCIDevice *dev)
|
||||
{
|
||||
if (pci_bus_is_express(dev->bus)) {
|
||||
error_report("pxb devices cannot reside on a PCIe bus!");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return pxb_dev_init_common(dev, false);
|
||||
}
|
||||
|
||||
static void pxb_dev_exitfn(PCIDevice *pci_dev)
|
||||
{
|
||||
PXBDev *pxb = PXB_DEV(pci_dev);
|
||||
PXBDev *pxb = convert_to_pxb(pci_dev);
|
||||
|
||||
pxb_dev_list = g_list_remove(pxb_dev_list, pxb);
|
||||
}
|
||||
|
@ -276,11 +310,45 @@ static const TypeInfo pxb_dev_info = {
|
|||
.class_init = pxb_dev_class_init,
|
||||
};
|
||||
|
||||
static int pxb_pcie_dev_initfn(PCIDevice *dev)
|
||||
{
|
||||
if (!pci_bus_is_express(dev->bus)) {
|
||||
error_report("pxb-pcie devices cannot reside on a PCI bus!");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return pxb_dev_init_common(dev, true);
|
||||
}
|
||||
|
||||
static void pxb_pcie_dev_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = pxb_pcie_dev_initfn;
|
||||
k->exit = pxb_dev_exitfn;
|
||||
k->vendor_id = PCI_VENDOR_ID_REDHAT;
|
||||
k->device_id = PCI_DEVICE_ID_REDHAT_PXB_PCIE;
|
||||
k->class_id = PCI_CLASS_BRIDGE_HOST;
|
||||
|
||||
dc->desc = "PCI Express Expander Bridge";
|
||||
dc->props = pxb_dev_properties;
|
||||
}
|
||||
|
||||
static const TypeInfo pxb_pcie_dev_info = {
|
||||
.name = TYPE_PXB_PCIE_DEVICE,
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(PXBDev),
|
||||
.class_init = pxb_pcie_dev_class_init,
|
||||
};
|
||||
|
||||
static void pxb_register_types(void)
|
||||
{
|
||||
type_register_static(&pxb_bus_info);
|
||||
type_register_static(&pxb_pcie_bus_info);
|
||||
type_register_static(&pxb_host_info);
|
||||
type_register_static(&pxb_dev_info);
|
||||
type_register_static(&pxb_pcie_dev_info);
|
||||
}
|
||||
|
||||
type_init(pxb_register_types)
|
||||
|
|
|
@ -34,6 +34,24 @@ struct Aml {
|
|||
};
|
||||
typedef struct Aml Aml;
|
||||
|
||||
typedef enum {
|
||||
AML_COMPATIBILITY = 0,
|
||||
AML_TYPEA = 1,
|
||||
AML_TYPEB = 2,
|
||||
AML_TYPEF = 3,
|
||||
} AmlDmaType;
|
||||
|
||||
typedef enum {
|
||||
AML_NOTBUSMASTER = 0,
|
||||
AML_BUSMASTER = 1,
|
||||
} AmlDmaBusMaster;
|
||||
|
||||
typedef enum {
|
||||
AML_TRANSFER8 = 0,
|
||||
AML_TRANSFER8_16 = 1,
|
||||
AML_TRANSFER16 = 2,
|
||||
} AmlTransferSize;
|
||||
|
||||
typedef enum {
|
||||
AML_DECODE10 = 0,
|
||||
AML_DECODE16 = 1,
|
||||
|
@ -48,6 +66,11 @@ typedef enum {
|
|||
AML_BUFFER_ACC = 5,
|
||||
} AmlAccessType;
|
||||
|
||||
typedef enum {
|
||||
AML_NOLOCK = 0,
|
||||
AML_LOCK = 1,
|
||||
} AmlLockRule;
|
||||
|
||||
typedef enum {
|
||||
AML_PRESERVE = 0,
|
||||
AML_WRITE_AS_ONES = 1,
|
||||
|
@ -224,16 +247,23 @@ Aml *aml_name_decl(const char *name, Aml *val);
|
|||
Aml *aml_return(Aml *val);
|
||||
Aml *aml_int(const uint64_t val);
|
||||
Aml *aml_arg(int pos);
|
||||
Aml *aml_to_integer(Aml *arg);
|
||||
Aml *aml_to_hexstring(Aml *src, Aml *dst);
|
||||
Aml *aml_to_buffer(Aml *src, Aml *dst);
|
||||
Aml *aml_store(Aml *val, Aml *target);
|
||||
Aml *aml_and(Aml *arg1, Aml *arg2);
|
||||
Aml *aml_or(Aml *arg1, Aml *arg2);
|
||||
Aml *aml_and(Aml *arg1, Aml *arg2, Aml *dst);
|
||||
Aml *aml_or(Aml *arg1, Aml *arg2, Aml *dst);
|
||||
Aml *aml_lor(Aml *arg1, Aml *arg2);
|
||||
Aml *aml_shiftleft(Aml *arg1, Aml *count);
|
||||
Aml *aml_shiftright(Aml *arg1, Aml *count);
|
||||
Aml *aml_shiftright(Aml *arg1, Aml *count, Aml *dst);
|
||||
Aml *aml_lless(Aml *arg1, Aml *arg2);
|
||||
Aml *aml_add(Aml *arg1, Aml *arg2);
|
||||
Aml *aml_add(Aml *arg1, Aml *arg2, Aml *dst);
|
||||
Aml *aml_subtract(Aml *arg1, Aml *arg2, Aml *dst);
|
||||
Aml *aml_increment(Aml *arg);
|
||||
Aml *aml_decrement(Aml *arg);
|
||||
Aml *aml_index(Aml *arg1, Aml *idx);
|
||||
Aml *aml_notify(Aml *arg1, Aml *arg2);
|
||||
Aml *aml_call0(const char *method);
|
||||
Aml *aml_call1(const char *method, Aml *arg1);
|
||||
Aml *aml_call2(const char *method, Aml *arg1, Aml *arg2);
|
||||
Aml *aml_call3(const char *method, Aml *arg1, Aml *arg2, Aml *arg3);
|
||||
|
@ -262,6 +292,8 @@ Aml *aml_local(int num);
|
|||
Aml *aml_string(const char *name_format, ...) GCC_FMT_ATTR(1, 2);
|
||||
Aml *aml_lnot(Aml *arg);
|
||||
Aml *aml_equal(Aml *arg1, Aml *arg2);
|
||||
Aml *aml_lgreater(Aml *arg1, Aml *arg2);
|
||||
Aml *aml_lgreater_equal(Aml *arg1, Aml *arg2);
|
||||
Aml *aml_processor(uint8_t proc_id, uint32_t pblk_addr, uint8_t pblk_len,
|
||||
const char *name_format, ...) GCC_FMT_ATTR(4, 5);
|
||||
Aml *aml_eisaid(const char *str);
|
||||
|
@ -291,6 +323,9 @@ Aml *aml_qword_memory(AmlDecode dec, AmlMinFixed min_fixed,
|
|||
uint64_t addr_gran, uint64_t addr_min,
|
||||
uint64_t addr_max, uint64_t addr_trans,
|
||||
uint64_t len);
|
||||
Aml *aml_dma(AmlDmaType typ, AmlDmaBusMaster bm, AmlTransferSize sz,
|
||||
uint8_t channel);
|
||||
Aml *aml_sleep(uint64_t msec);
|
||||
|
||||
/* Block AML object primitives */
|
||||
Aml *aml_scope(const char *name_format, ...) GCC_FMT_ATTR(1, 2);
|
||||
|
@ -302,15 +337,24 @@ Aml *aml_while(Aml *predicate);
|
|||
Aml *aml_package(uint8_t num_elements);
|
||||
Aml *aml_buffer(int buffer_size, uint8_t *byte_list);
|
||||
Aml *aml_resource_template(void);
|
||||
Aml *aml_field(const char *name, AmlAccessType type, AmlUpdateRule rule);
|
||||
Aml *aml_field(const char *name, AmlAccessType type, AmlLockRule lock,
|
||||
AmlUpdateRule rule);
|
||||
Aml *aml_mutex(const char *name, uint8_t sync_level);
|
||||
Aml *aml_acquire(Aml *mutex, uint16_t timeout);
|
||||
Aml *aml_release(Aml *mutex);
|
||||
Aml *aml_alias(const char *source_object, const char *alias_object);
|
||||
Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name);
|
||||
Aml *aml_create_qword_field(Aml *srcbuf, Aml *index, const char *name);
|
||||
Aml *aml_varpackage(uint32_t num_elements);
|
||||
Aml *aml_touuid(const char *uuid);
|
||||
Aml *aml_unicode(const char *str);
|
||||
Aml *aml_derefof(Aml *arg);
|
||||
Aml *aml_sizeof(Aml *arg);
|
||||
|
||||
void
|
||||
build_header(GArray *linker, GArray *table_data,
|
||||
AcpiTableHeader *h, const char *sig, int len, uint8_t rev);
|
||||
AcpiTableHeader *h, const char *sig, int len, uint8_t rev,
|
||||
const char *oem_table_id);
|
||||
void *acpi_data_push(GArray *table_data, unsigned size);
|
||||
unsigned acpi_data_len(GArray *table);
|
||||
void acpi_add_table(GArray *table_offsets, GArray *table_data);
|
||||
|
|
|
@ -93,6 +93,8 @@ struct MachineClass {
|
|||
GlobalProperty *compat_props;
|
||||
const char *hw_version;
|
||||
ram_addr_t default_ram_size;
|
||||
bool option_rom_has_mr;
|
||||
bool rom_file_has_mr;
|
||||
|
||||
HotplugHandler *(*get_hotplug_handler)(MachineState *machine,
|
||||
DeviceState *dev);
|
||||
|
|
|
@ -1,88 +1,91 @@
|
|||
#ifndef HW_COMPAT_H
|
||||
#define HW_COMPAT_H
|
||||
|
||||
#define HW_COMPAT_2_5 \
|
||||
/* empty */
|
||||
|
||||
#define HW_COMPAT_2_4 \
|
||||
{\
|
||||
.driver = "virtio-blk-device",\
|
||||
.property = "scsi",\
|
||||
.value = "true",\
|
||||
},{\
|
||||
.driver = "pvscsi",\
|
||||
.property = "x-old-pci-configuration",\
|
||||
.value = "on",\
|
||||
},{\
|
||||
.driver = "pvscsi",\
|
||||
.property = "x-disable-pcie",\
|
||||
.value = "on",\
|
||||
},{\
|
||||
.driver = "e1000",\
|
||||
.property = "extra_mac_registers",\
|
||||
.value = "off",\
|
||||
},{\
|
||||
.driver = "virtio-pci",\
|
||||
.property = "x-disable-pcie",\
|
||||
.value = "on",\
|
||||
},{\
|
||||
.driver = "virtio-pci",\
|
||||
.property = "migrate-extra",\
|
||||
.value = "off",\
|
||||
},
|
||||
{\
|
||||
.driver = "virtio-blk-device",\
|
||||
.property = "scsi",\
|
||||
.value = "true",\
|
||||
},{\
|
||||
.driver = "pvscsi",\
|
||||
.property = "x-old-pci-configuration",\
|
||||
.value = "on",\
|
||||
},{\
|
||||
.driver = "pvscsi",\
|
||||
.property = "x-disable-pcie",\
|
||||
.value = "on",\
|
||||
},{\
|
||||
.driver = "e1000",\
|
||||
.property = "extra_mac_registers",\
|
||||
.value = "off",\
|
||||
},{\
|
||||
.driver = "virtio-pci",\
|
||||
.property = "x-disable-pcie",\
|
||||
.value = "on",\
|
||||
},{\
|
||||
.driver = "virtio-pci",\
|
||||
.property = "migrate-extra",\
|
||||
.value = "off",\
|
||||
},
|
||||
|
||||
#define HW_COMPAT_2_3 \
|
||||
{\
|
||||
.driver = "virtio-blk-pci",\
|
||||
.property = "any_layout",\
|
||||
.value = "off",\
|
||||
},{\
|
||||
.driver = "virtio-balloon-pci",\
|
||||
.property = "any_layout",\
|
||||
.value = "off",\
|
||||
},{\
|
||||
.driver = "virtio-serial-pci",\
|
||||
.property = "any_layout",\
|
||||
.value = "off",\
|
||||
},{\
|
||||
.driver = "virtio-9p-pci",\
|
||||
.property = "any_layout",\
|
||||
.value = "off",\
|
||||
},{\
|
||||
.driver = "virtio-rng-pci",\
|
||||
.property = "any_layout",\
|
||||
.value = "off",\
|
||||
},
|
||||
{\
|
||||
.driver = "virtio-blk-pci",\
|
||||
.property = "any_layout",\
|
||||
.value = "off",\
|
||||
},{\
|
||||
.driver = "virtio-balloon-pci",\
|
||||
.property = "any_layout",\
|
||||
.value = "off",\
|
||||
},{\
|
||||
.driver = "virtio-serial-pci",\
|
||||
.property = "any_layout",\
|
||||
.value = "off",\
|
||||
},{\
|
||||
.driver = "virtio-9p-pci",\
|
||||
.property = "any_layout",\
|
||||
.value = "off",\
|
||||
},{\
|
||||
.driver = "virtio-rng-pci",\
|
||||
.property = "any_layout",\
|
||||
.value = "off",\
|
||||
},
|
||||
|
||||
#define HW_COMPAT_2_2 \
|
||||
/* empty */
|
||||
/* empty */
|
||||
|
||||
#define HW_COMPAT_2_1 \
|
||||
{\
|
||||
.driver = "intel-hda",\
|
||||
.property = "old_msi_addr",\
|
||||
.value = "on",\
|
||||
},{\
|
||||
.driver = "VGA",\
|
||||
.property = "qemu-extended-regs",\
|
||||
.value = "off",\
|
||||
},{\
|
||||
.driver = "secondary-vga",\
|
||||
.property = "qemu-extended-regs",\
|
||||
.value = "off",\
|
||||
},{\
|
||||
.driver = "virtio-scsi-pci",\
|
||||
.property = "any_layout",\
|
||||
.value = "off",\
|
||||
},{\
|
||||
.driver = "usb-mouse",\
|
||||
.property = "usb_version",\
|
||||
.value = stringify(1),\
|
||||
},{\
|
||||
.driver = "usb-kbd",\
|
||||
.property = "usb_version",\
|
||||
.value = stringify(1),\
|
||||
},{\
|
||||
.driver = "virtio-pci",\
|
||||
.property = "virtio-pci-bus-master-bug-migration",\
|
||||
.value = "on",\
|
||||
},
|
||||
{\
|
||||
.driver = "intel-hda",\
|
||||
.property = "old_msi_addr",\
|
||||
.value = "on",\
|
||||
},{\
|
||||
.driver = "VGA",\
|
||||
.property = "qemu-extended-regs",\
|
||||
.value = "off",\
|
||||
},{\
|
||||
.driver = "secondary-vga",\
|
||||
.property = "qemu-extended-regs",\
|
||||
.value = "off",\
|
||||
},{\
|
||||
.driver = "virtio-scsi-pci",\
|
||||
.property = "any_layout",\
|
||||
.value = "off",\
|
||||
},{\
|
||||
.driver = "usb-mouse",\
|
||||
.property = "usb_version",\
|
||||
.value = stringify(1),\
|
||||
},{\
|
||||
.driver = "usb-kbd",\
|
||||
.property = "usb_version",\
|
||||
.value = stringify(1),\
|
||||
},{\
|
||||
.driver = "virtio-pci",\
|
||||
.property = "virtio-pci-bus-master-bug-migration",\
|
||||
.value = "on",\
|
||||
},
|
||||
|
||||
#endif /* HW_COMPAT_H */
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* IPMI base class
|
||||
*
|
||||
* Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef HW_IPMI_H
|
||||
#define HW_IPMI_H
|
||||
|
||||
#include "exec/memory.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/qdev.h"
|
||||
|
||||
#define MAX_IPMI_MSG_SIZE 300
|
||||
|
||||
enum ipmi_op {
|
||||
IPMI_RESET_CHASSIS,
|
||||
IPMI_POWEROFF_CHASSIS,
|
||||
IPMI_POWERON_CHASSIS,
|
||||
IPMI_POWERCYCLE_CHASSIS,
|
||||
IPMI_PULSE_DIAG_IRQ,
|
||||
IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP,
|
||||
IPMI_SEND_NMI
|
||||
};
|
||||
|
||||
#define IPMI_CC_INVALID_CMD 0xc1
|
||||
#define IPMI_CC_COMMAND_INVALID_FOR_LUN 0xc2
|
||||
#define IPMI_CC_TIMEOUT 0xc3
|
||||
#define IPMI_CC_OUT_OF_SPACE 0xc4
|
||||
#define IPMI_CC_INVALID_RESERVATION 0xc5
|
||||
#define IPMI_CC_REQUEST_DATA_TRUNCATED 0xc6
|
||||
#define IPMI_CC_REQUEST_DATA_LENGTH_INVALID 0xc7
|
||||
#define IPMI_CC_PARM_OUT_OF_RANGE 0xc9
|
||||
#define IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES 0xca
|
||||
#define IPMI_CC_REQ_ENTRY_NOT_PRESENT 0xcb
|
||||
#define IPMI_CC_INVALID_DATA_FIELD 0xcc
|
||||
#define IPMI_CC_BMC_INIT_IN_PROGRESS 0xd2
|
||||
#define IPMI_CC_COMMAND_NOT_SUPPORTED 0xd5
|
||||
|
||||
#define IPMI_NETFN_APP 0x06
|
||||
|
||||
#define IPMI_DEBUG 1
|
||||
|
||||
/* Specified in the SMBIOS spec. */
|
||||
#define IPMI_SMBIOS_KCS 0x01
|
||||
#define IPMI_SMBIOS_SMIC 0x02
|
||||
#define IPMI_SMBIOS_BT 0x03
|
||||
#define IPMI_SMBIOS_SSIF 0x04
|
||||
|
||||
/* IPMI Interface types (KCS, SMIC, BT) are prefixed with this */
|
||||
#define TYPE_IPMI_INTERFACE_PREFIX "ipmi-interface-"
|
||||
|
||||
/*
|
||||
* An IPMI Interface, the interface for talking between the target
|
||||
* and the BMC.
|
||||
*/
|
||||
#define TYPE_IPMI_INTERFACE "ipmi-interface"
|
||||
#define IPMI_INTERFACE(obj) \
|
||||
INTERFACE_CHECK(IPMIInterface, (obj), TYPE_IPMI_INTERFACE)
|
||||
#define IPMI_INTERFACE_CLASS(class) \
|
||||
OBJECT_CLASS_CHECK(IPMIInterfaceClass, (class), TYPE_IPMI_INTERFACE)
|
||||
#define IPMI_INTERFACE_GET_CLASS(class) \
|
||||
OBJECT_GET_CLASS(IPMIInterfaceClass, (class), TYPE_IPMI_INTERFACE)
|
||||
|
||||
typedef struct IPMIInterface {
|
||||
Object parent;
|
||||
} IPMIInterface;
|
||||
|
||||
typedef struct IPMIInterfaceClass {
|
||||
InterfaceClass parent;
|
||||
|
||||
void (*init)(struct IPMIInterface *s, Error **errp);
|
||||
|
||||
/*
|
||||
* Perform various operations on the hardware. If checkonly is
|
||||
* true, it will return if the operation can be performed, but it
|
||||
* will not do the operation.
|
||||
*/
|
||||
int (*do_hw_op)(struct IPMIInterface *s, enum ipmi_op op, int checkonly);
|
||||
|
||||
/*
|
||||
* Enable/disable irqs on the interface when the BMC requests this.
|
||||
*/
|
||||
void (*set_irq_enable)(struct IPMIInterface *s, int val);
|
||||
|
||||
/*
|
||||
* Handle an event that occurred on the interface, generally the.
|
||||
* target writing to a register.
|
||||
*/
|
||||
void (*handle_if_event)(struct IPMIInterface *s);
|
||||
|
||||
/*
|
||||
* The interfaces use this to perform certain ops
|
||||
*/
|
||||
void (*set_atn)(struct IPMIInterface *s, int val, int irq);
|
||||
|
||||
/*
|
||||
* Got an IPMI warm/cold reset.
|
||||
*/
|
||||
void (*reset)(struct IPMIInterface *s, bool is_cold);
|
||||
|
||||
/*
|
||||
* Handle a response from the bmc.
|
||||
*/
|
||||
void (*handle_rsp)(struct IPMIInterface *s, uint8_t msg_id,
|
||||
unsigned char *rsp, unsigned int rsp_len);
|
||||
|
||||
/*
|
||||
* Set by the owner to hold the backend data for the interface.
|
||||
*/
|
||||
void *(*get_backend_data)(struct IPMIInterface *s);
|
||||
} IPMIInterfaceClass;
|
||||
|
||||
/*
|
||||
* Define a BMC simulator (or perhaps a connection to a real BMC)
|
||||
*/
|
||||
#define TYPE_IPMI_BMC "ipmi-bmc"
|
||||
#define IPMI_BMC(obj) \
|
||||
OBJECT_CHECK(IPMIBmc, (obj), TYPE_IPMI_BMC)
|
||||
#define IPMI_BMC_CLASS(obj_class) \
|
||||
OBJECT_CLASS_CHECK(IPMIBmcClass, (obj_class), TYPE_IPMI_BMC)
|
||||
#define IPMI_BMC_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(IPMIBmcClass, (obj), TYPE_IPMI_BMC)
|
||||
|
||||
typedef struct IPMIBmc {
|
||||
DeviceState parent;
|
||||
|
||||
uint8_t slave_addr;
|
||||
|
||||
IPMIInterface *intf;
|
||||
} IPMIBmc;
|
||||
|
||||
typedef struct IPMIBmcClass {
|
||||
DeviceClass parent;
|
||||
|
||||
/* Called when the system resets to report to the bmc. */
|
||||
void (*handle_reset)(struct IPMIBmc *s);
|
||||
|
||||
/*
|
||||
* Handle a command to the bmc.
|
||||
*/
|
||||
void (*handle_command)(struct IPMIBmc *s,
|
||||
uint8_t *cmd, unsigned int cmd_len,
|
||||
unsigned int max_cmd_len,
|
||||
uint8_t msg_id);
|
||||
} IPMIBmcClass;
|
||||
|
||||
/*
|
||||
* Add a link property to obj that points to a BMC.
|
||||
*/
|
||||
void ipmi_bmc_find_and_link(Object *obj, Object **bmc);
|
||||
|
||||
/*
|
||||
* Used for transferring information to interfaces that add
|
||||
* entries to firmware tables.
|
||||
*/
|
||||
typedef struct IPMIFwInfo {
|
||||
const char *interface_name;
|
||||
int interface_type;
|
||||
uint8_t ipmi_spec_major_revision;
|
||||
uint8_t ipmi_spec_minor_revision;
|
||||
uint8_t i2c_slave_address;
|
||||
uint32_t uuid;
|
||||
|
||||
uint64_t base_address;
|
||||
uint64_t register_length;
|
||||
uint8_t register_spacing;
|
||||
enum {
|
||||
IPMI_MEMSPACE_IO,
|
||||
IPMI_MEMSPACE_MEM32,
|
||||
IPMI_MEMSPACE_MEM64,
|
||||
IPMI_MEMSPACE_SMBUS
|
||||
} memspace;
|
||||
|
||||
int interrupt_number;
|
||||
enum {
|
||||
IPMI_LEVEL_IRQ,
|
||||
IPMI_EDGE_IRQ
|
||||
} irq_type;
|
||||
|
||||
const char *acpi_parent;
|
||||
} IPMIFwInfo;
|
||||
|
||||
void ipmi_add_fwinfo(IPMIFwInfo *info, Error **errp);
|
||||
IPMIFwInfo *ipmi_first_fwinfo(void);
|
||||
IPMIFwInfo *ipmi_next_fwinfo(IPMIFwInfo *current);
|
||||
|
||||
#ifdef IPMI_DEBUG
|
||||
#define ipmi_debug(fs, ...) \
|
||||
fprintf(stderr, "IPMI (%s): " fs, __func__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define ipmi_debug(fs, ...)
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Non-Volatile Dual In-line Memory Module Virtualization Implementation
|
||||
*
|
||||
* Copyright(C) 2015 Intel Corporation.
|
||||
*
|
||||
* Author:
|
||||
* Xiao Guangrong <guangrong.xiao@linux.intel.com>
|
||||
*
|
||||
* NVDIMM specifications and some documents can be found at:
|
||||
* NVDIMM ACPI device and NFIT are introduced in ACPI 6:
|
||||
* http://www.uefi.org/sites/default/files/resources/ACPI_6.0.pdf
|
||||
* NVDIMM Namespace specification:
|
||||
* http://pmem.io/documents/NVDIMM_Namespace_Spec.pdf
|
||||
* DSM Interface Example:
|
||||
* http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf
|
||||
* Driver Writer's Guide:
|
||||
* http://pmem.io/documents/NVDIMM_Driver_Writers_Guide.pdf
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef QEMU_NVDIMM_H
|
||||
#define QEMU_NVDIMM_H
|
||||
|
||||
#include "hw/mem/pc-dimm.h"
|
||||
|
||||
#define TYPE_NVDIMM "nvdimm"
|
||||
|
||||
void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data,
|
||||
GArray *linker);
|
||||
#endif
|
|
@ -59,7 +59,6 @@ typedef struct MCHPCIState {
|
|||
ram_addr_t below_4g_mem_size;
|
||||
ram_addr_t above_4g_mem_size;
|
||||
uint64_t pci_hole64_size;
|
||||
PcGuestInfo *guest_info;
|
||||
uint32_t short_root_bus;
|
||||
IntelIOMMUState *iommu;
|
||||
} MCHPCIState;
|
||||
|
|
|
@ -93,6 +93,7 @@
|
|||
#define PCI_DEVICE_ID_REDHAT_PCIE_HOST 0x0008
|
||||
#define PCI_DEVICE_ID_REDHAT_PXB 0x0009
|
||||
#define PCI_DEVICE_ID_REDHAT_BRIDGE_SEAT 0x000a
|
||||
#define PCI_DEVICE_ID_REDHAT_PXB_PCIE 0x000b
|
||||
#define PCI_DEVICE_ID_REDHAT_QXL 0x0100
|
||||
|
||||
#define FMT_PCIBUS PRIx64
|
||||
|
|
|
@ -195,6 +195,8 @@ PCI and ISA network adapters
|
|||
@item
|
||||
Serial ports
|
||||
@item
|
||||
IPMI BMC, either and internal or external one
|
||||
@item
|
||||
Creative SoundBlaster 16 sound card
|
||||
@item
|
||||
ENSONIQ AudioPCI ES1370 sound card
|
||||
|
|
|
@ -42,7 +42,8 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \
|
|||
" igd-passthru=on|off controls IGD GFX passthrough support (default=off)\n"
|
||||
" aes-key-wrap=on|off controls support for AES key wrapping (default=on)\n"
|
||||
" dea-key-wrap=on|off controls support for DEA key wrapping (default=on)\n"
|
||||
" suppress-vmdesc=on|off disables self-describing migration (default=off)\n",
|
||||
" suppress-vmdesc=on|off disables self-describing migration (default=off)\n"
|
||||
" nvdimm=on|off controls NVDIMM support (default=off)\n",
|
||||
QEMU_ARCH_ALL)
|
||||
STEXI
|
||||
@item -machine [type=]@var{name}[,prop=@var{value}[,...]]
|
||||
|
@ -81,6 +82,8 @@ execution of AES cryptographic functions. The default is on.
|
|||
Enables or disables DEA key wrapping support on s390-ccw hosts. This feature
|
||||
controls whether DEA wrapping keys will be created to allow
|
||||
execution of DEA cryptographic functions. The default is on.
|
||||
@item nvdimm=on|off
|
||||
Enables or disables NVDIMM support. The default is off.
|
||||
@end table
|
||||
ETEXI
|
||||
|
||||
|
@ -382,6 +385,58 @@ Add device @var{driver}. @var{prop}=@var{value} sets driver
|
|||
properties. Valid properties depend on the driver. To get help on
|
||||
possible drivers and properties, use @code{-device help} and
|
||||
@code{-device @var{driver},help}.
|
||||
|
||||
Some drivers are:
|
||||
@item -device ipmi-bmc-sim,id=@var{id}[,slave_addr=@var{val}]
|
||||
|
||||
Add an IPMI BMC. This is a simulation of a hardware management
|
||||
interface processor that normally sits on a system. It provides
|
||||
a watchdog and the ability to reset and power control the system.
|
||||
You need to connect this to an IPMI interface to make it useful
|
||||
|
||||
The IPMI slave address to use for the BMC. The default is 0x20.
|
||||
This address is the BMC's address on the I2C network of management
|
||||
controllers. If you don't know what this means, it is safe to ignore
|
||||
it.
|
||||
|
||||
@item -device ipmi-bmc-extern,id=@var{id},chardev=@var{id}[,slave_addr=@var{val}]
|
||||
|
||||
Add a connection to an external IPMI BMC simulator. Instead of
|
||||
locally emulating the BMC like the above item, instead connect
|
||||
to an external entity that provides the IPMI services.
|
||||
|
||||
A connection is made to an external BMC simulator. If you do this, it
|
||||
is strongly recommended that you use the "reconnect=" chardev option
|
||||
to reconnect to the simulator if the connection is lost. Note that if
|
||||
this is not used carefully, it can be a security issue, as the
|
||||
interface has the ability to send resets, NMIs, and power off the VM.
|
||||
It's best if QEMU makes a connection to an external simulator running
|
||||
on a secure port on localhost, so neither the simulator nor QEMU is
|
||||
exposed to any outside network.
|
||||
|
||||
See the "lanserv/README.vm" file in the OpenIPMI library for more
|
||||
details on the external interface.
|
||||
|
||||
@item -device isa-ipmi-kcs,bmc=@var{id}[,ioport=@var{val}][,irq=@var{val}]
|
||||
|
||||
Add a KCS IPMI interafce on the ISA bus. This also adds a
|
||||
corresponding ACPI and SMBIOS entries, if appropriate.
|
||||
|
||||
@table @option
|
||||
@item bmc=@var{id}
|
||||
The BMC to connect to, one of ipmi-bmc-sim or ipmi-bmc-extern above.
|
||||
@item ioport=@var{val}
|
||||
Define the I/O address of the interface. The default is 0xca0 for KCS.
|
||||
@item irq=@var{val}
|
||||
Define the interrupt to use. The default is 5. To disable interrupts,
|
||||
set this to 0.
|
||||
@end table
|
||||
|
||||
@item -device isa-ipmi-bt,bmc=@var{id}[,ioport=@var{val}][,irq=@var{val}]
|
||||
|
||||
Like the KCS interface, but defines a BT interface. The default port is
|
||||
0xe4 and the default interrupt is 5.
|
||||
|
||||
ETEXI
|
||||
|
||||
DEF("name", HAS_ARG, QEMU_OPTION_name,
|
||||
|
|
|
@ -174,6 +174,8 @@ gcov-files-i386-y += hw/block/hd-geometry.c
|
|||
check-qtest-i386-y += tests/boot-order-test$(EXESUF)
|
||||
check-qtest-i386-y += tests/bios-tables-test$(EXESUF)
|
||||
check-qtest-i386-y += tests/rtc-test$(EXESUF)
|
||||
check-qtest-i386-y += tests/ipmi-kcs-test$(EXESUF)
|
||||
check-qtest-i386-y += tests/ipmi-bt-test$(EXESUF)
|
||||
check-qtest-i386-y += tests/i440fx-test$(EXESUF)
|
||||
check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
|
||||
check-qtest-i386-y += tests/drive_del-test$(EXESUF)
|
||||
|
@ -512,6 +514,8 @@ tests/spapr-phb-test$(EXESUF): tests/spapr-phb-test.o $(libqos-obj-y)
|
|||
tests/fdc-test$(EXESUF): tests/fdc-test.o
|
||||
tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y)
|
||||
tests/ahci-test$(EXESUF): tests/ahci-test.o $(libqos-pc-obj-y)
|
||||
tests/ipmi-kcs-test$(EXESUF): tests/ipmi-kcs-test.o
|
||||
tests/ipmi-bt-test$(EXESUF): tests/ipmi-bt-test.o
|
||||
tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o
|
||||
tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y)
|
||||
tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o $(libqos-obj-y)
|
||||
|
|
|
@ -0,0 +1,436 @@
|
|||
/*
|
||||
* IPMI BT test cases, using the external interface for checking
|
||||
*
|
||||
* Copyright (c) 2012 Corey Minyard <cminyard@mvista.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "libqtest.h"
|
||||
#include "qemu-common.h"
|
||||
|
||||
#define IPMI_IRQ 5
|
||||
|
||||
#define IPMI_BT_BASE 0xe4
|
||||
|
||||
#define IPMI_BT_CTLREG_CLR_WR_PTR 0
|
||||
#define IPMI_BT_CTLREG_CLR_RD_PTR 1
|
||||
#define IPMI_BT_CTLREG_H2B_ATN 2
|
||||
#define IPMI_BT_CTLREG_B2H_ATN 3
|
||||
#define IPMI_BT_CTLREG_SMS_ATN 4
|
||||
#define IPMI_BT_CTLREG_H_BUSY 6
|
||||
#define IPMI_BT_CTLREG_B_BUSY 7
|
||||
|
||||
#define IPMI_BT_CTLREG_GET(b) ((bt_get_ctrlreg() >> (b)) & 1)
|
||||
#define IPMI_BT_CTLREG_GET_H2B_ATN() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_H2B_ATN)
|
||||
#define IPMI_BT_CTLREG_GET_B2H_ATN() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_B2H_ATN)
|
||||
#define IPMI_BT_CTLREG_GET_SMS_ATN() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_SMS_ATN)
|
||||
#define IPMI_BT_CTLREG_GET_H_BUSY() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_H_BUSY)
|
||||
#define IPMI_BT_CTLREG_GET_B_BUSY() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_B_BUSY)
|
||||
|
||||
#define IPMI_BT_CTLREG_SET(b) bt_write_ctrlreg(1 << (b))
|
||||
#define IPMI_BT_CTLREG_SET_CLR_WR_PTR() IPMI_BT_CTLREG_SET( \
|
||||
IPMI_BT_CTLREG_CLR_WR_PTR)
|
||||
#define IPMI_BT_CTLREG_SET_CLR_RD_PTR() IPMI_BT_CTLREG_SET( \
|
||||
IPMI_BT_CTLREG_CLR_RD_PTR)
|
||||
#define IPMI_BT_CTLREG_SET_H2B_ATN() IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_H2B_ATN)
|
||||
#define IPMI_BT_CTLREG_SET_B2H_ATN() IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_B2H_ATN)
|
||||
#define IPMI_BT_CTLREG_SET_SMS_ATN() IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_SMS_ATN)
|
||||
#define IPMI_BT_CTLREG_SET_H_BUSY() IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_H_BUSY)
|
||||
|
||||
static int bt_ints_enabled;
|
||||
|
||||
static uint8_t bt_get_ctrlreg(void)
|
||||
{
|
||||
return inb(IPMI_BT_BASE);
|
||||
}
|
||||
|
||||
static void bt_write_ctrlreg(uint8_t val)
|
||||
{
|
||||
outb(IPMI_BT_BASE, val);
|
||||
}
|
||||
|
||||
static uint8_t bt_get_buf(void)
|
||||
{
|
||||
return inb(IPMI_BT_BASE + 1);
|
||||
}
|
||||
|
||||
static void bt_write_buf(uint8_t val)
|
||||
{
|
||||
outb(IPMI_BT_BASE + 1, val);
|
||||
}
|
||||
|
||||
static uint8_t bt_get_irqreg(void)
|
||||
{
|
||||
return inb(IPMI_BT_BASE + 2);
|
||||
}
|
||||
|
||||
static void bt_write_irqreg(uint8_t val)
|
||||
{
|
||||
outb(IPMI_BT_BASE + 2, val);
|
||||
}
|
||||
|
||||
static void bt_wait_b_busy(void)
|
||||
{
|
||||
unsigned int count = 1000;
|
||||
while (IPMI_BT_CTLREG_GET_B_BUSY() != 0) {
|
||||
g_assert(--count != 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_wait_b2h_atn(void)
|
||||
{
|
||||
unsigned int count = 1000;
|
||||
while (IPMI_BT_CTLREG_GET_B2H_ATN() == 0) {
|
||||
g_assert(--count != 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int emu_lfd;
|
||||
static int emu_fd;
|
||||
static in_port_t emu_port;
|
||||
static uint8_t inbuf[100];
|
||||
static unsigned int inbuf_len;
|
||||
static unsigned int inbuf_pos;
|
||||
static int last_was_aa;
|
||||
|
||||
static void read_emu_data(void)
|
||||
{
|
||||
fd_set readfds;
|
||||
int rv;
|
||||
struct timeval tv;
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(emu_fd, &readfds);
|
||||
tv.tv_sec = 10;
|
||||
tv.tv_usec = 0;
|
||||
rv = select(emu_fd + 1, &readfds, NULL, NULL, &tv);
|
||||
if (rv == -1) {
|
||||
perror("select");
|
||||
}
|
||||
g_assert(rv == 1);
|
||||
rv = read(emu_fd, inbuf, sizeof(inbuf));
|
||||
if (rv == -1) {
|
||||
perror("read");
|
||||
}
|
||||
g_assert(rv > 0);
|
||||
inbuf_len = rv;
|
||||
inbuf_pos = 0;
|
||||
}
|
||||
|
||||
static void write_emu_msg(uint8_t *msg, unsigned int len)
|
||||
{
|
||||
int rv;
|
||||
|
||||
#ifdef DEBUG_TEST
|
||||
{
|
||||
unsigned int i;
|
||||
printf("sending:");
|
||||
for (i = 0; i < len; i++) {
|
||||
printf(" %2.2x", msg[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
rv = write(emu_fd, msg, len);
|
||||
g_assert(rv == len);
|
||||
}
|
||||
|
||||
static void get_emu_msg(uint8_t *msg, unsigned int *len)
|
||||
{
|
||||
unsigned int outpos = 0;
|
||||
|
||||
for (;;) {
|
||||
while (inbuf_pos < inbuf_len) {
|
||||
uint8_t ch = inbuf[inbuf_pos++];
|
||||
|
||||
g_assert(outpos < *len);
|
||||
if (last_was_aa) {
|
||||
assert(ch & 0x10);
|
||||
msg[outpos++] = ch & ~0x10;
|
||||
last_was_aa = 0;
|
||||
} else if (ch == 0xaa) {
|
||||
last_was_aa = 1;
|
||||
} else {
|
||||
msg[outpos++] = ch;
|
||||
if ((ch == 0xa0) || (ch == 0xa1)) {
|
||||
/* Message complete */
|
||||
*len = outpos;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
read_emu_data();
|
||||
}
|
||||
done:
|
||||
#ifdef DEBUG_TEST
|
||||
{
|
||||
unsigned int i;
|
||||
printf("Msg:");
|
||||
for (i = 0; i < outpos; i++) {
|
||||
printf(" %2.2x", msg[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
ipmb_checksum(const unsigned char *data, int size, unsigned char start)
|
||||
{
|
||||
unsigned char csum = start;
|
||||
|
||||
for (; size > 0; size--, data++) {
|
||||
csum += *data;
|
||||
}
|
||||
return csum;
|
||||
}
|
||||
|
||||
static uint8_t get_dev_id_cmd[] = { 0x18, 0x01 };
|
||||
static uint8_t get_dev_id_rsp[] = { 0x1c, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
|
||||
0x02, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
static uint8_t set_bmc_globals_cmd[] = { 0x18, 0x2e, 0x0f };
|
||||
static uint8_t set_bmc_globals_rsp[] = { 0x1c, 0x2e, 0x00 };
|
||||
static uint8_t enable_irq_cmd[] = { 0x05, 0xa1 };
|
||||
|
||||
static void emu_msg_handler(void)
|
||||
{
|
||||
uint8_t msg[100];
|
||||
unsigned int msg_len = sizeof(msg);
|
||||
|
||||
get_emu_msg(msg, &msg_len);
|
||||
g_assert(msg_len >= 5);
|
||||
g_assert(msg[msg_len - 1] == 0xa0);
|
||||
msg_len--;
|
||||
g_assert(ipmb_checksum(msg, msg_len, 0) == 0);
|
||||
msg_len--;
|
||||
if ((msg[1] == get_dev_id_cmd[0]) && (msg[2] == get_dev_id_cmd[1])) {
|
||||
memcpy(msg + 1, get_dev_id_rsp, sizeof(get_dev_id_rsp));
|
||||
msg_len = sizeof(get_dev_id_rsp) + 1;
|
||||
msg[msg_len] = -ipmb_checksum(msg, msg_len, 0);
|
||||
msg_len++;
|
||||
msg[msg_len++] = 0xa0;
|
||||
write_emu_msg(msg, msg_len);
|
||||
} else if ((msg[1] == set_bmc_globals_cmd[0]) &&
|
||||
(msg[2] == set_bmc_globals_cmd[1])) {
|
||||
memcpy(msg + 1, set_bmc_globals_rsp, sizeof(set_bmc_globals_rsp));
|
||||
msg_len = sizeof(set_bmc_globals_rsp) + 1;
|
||||
msg[msg_len] = -ipmb_checksum(msg, msg_len, 0);
|
||||
msg_len++;
|
||||
msg[msg_len++] = 0xa0;
|
||||
write_emu_msg(msg, msg_len);
|
||||
write_emu_msg(enable_irq_cmd, sizeof(enable_irq_cmd));
|
||||
} else {
|
||||
g_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_cmd(uint8_t *cmd, unsigned int cmd_len,
|
||||
uint8_t *rsp, unsigned int *rsp_len)
|
||||
{
|
||||
unsigned int i, len, j = 0;
|
||||
uint8_t seq = 5;
|
||||
|
||||
/* Should be idle */
|
||||
g_assert(bt_get_ctrlreg() == 0);
|
||||
|
||||
bt_wait_b_busy();
|
||||
IPMI_BT_CTLREG_SET_CLR_WR_PTR();
|
||||
bt_write_buf(cmd_len + 1);
|
||||
bt_write_buf(cmd[0]);
|
||||
bt_write_buf(seq);
|
||||
for (i = 1; i < cmd_len; i++) {
|
||||
bt_write_buf(cmd[i]);
|
||||
}
|
||||
IPMI_BT_CTLREG_SET_H2B_ATN();
|
||||
|
||||
emu_msg_handler(); /* We should get a message on the socket here. */
|
||||
|
||||
bt_wait_b2h_atn();
|
||||
if (bt_ints_enabled) {
|
||||
g_assert((bt_get_irqreg() & 0x02) == 0x02);
|
||||
g_assert(get_irq(IPMI_IRQ));
|
||||
bt_write_irqreg(0x03);
|
||||
} else {
|
||||
g_assert(!get_irq(IPMI_IRQ));
|
||||
}
|
||||
IPMI_BT_CTLREG_SET_H_BUSY();
|
||||
IPMI_BT_CTLREG_SET_B2H_ATN();
|
||||
IPMI_BT_CTLREG_SET_CLR_RD_PTR();
|
||||
len = bt_get_buf();
|
||||
g_assert(len >= 4);
|
||||
rsp[0] = bt_get_buf();
|
||||
assert(bt_get_buf() == seq);
|
||||
len--;
|
||||
for (j = 1; j < len; j++) {
|
||||
rsp[j] = bt_get_buf();
|
||||
}
|
||||
IPMI_BT_CTLREG_SET_H_BUSY();
|
||||
*rsp_len = j;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* We should get a connect request and a short message with capabilities.
|
||||
*/
|
||||
static void test_connect(void)
|
||||
{
|
||||
fd_set readfds;
|
||||
int rv;
|
||||
int val;
|
||||
struct timeval tv;
|
||||
uint8_t msg[100];
|
||||
unsigned int msglen;
|
||||
static uint8_t exp1[] = { 0xff, 0x01, 0xa1 }; /* A protocol version */
|
||||
static uint8_t exp2[] = { 0x08, 0x1f, 0xa1 }; /* A capabilities cmd */
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(emu_lfd, &readfds);
|
||||
tv.tv_sec = 10;
|
||||
tv.tv_usec = 0;
|
||||
rv = select(emu_lfd + 1, &readfds, NULL, NULL, &tv);
|
||||
g_assert(rv == 1);
|
||||
emu_fd = accept(emu_lfd, NULL, 0);
|
||||
if (emu_fd < 0) {
|
||||
perror("accept");
|
||||
}
|
||||
g_assert(emu_fd >= 0);
|
||||
|
||||
val = 1;
|
||||
rv = setsockopt(emu_fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
|
||||
g_assert(rv != -1);
|
||||
|
||||
/* Report our version */
|
||||
write_emu_msg(exp1, sizeof(exp1));
|
||||
|
||||
/* Validate that we get the info we expect. */
|
||||
msglen = sizeof(msg);
|
||||
get_emu_msg(msg, &msglen);
|
||||
g_assert(msglen == sizeof(exp1));
|
||||
g_assert(memcmp(msg, exp1, msglen) == 0);
|
||||
msglen = sizeof(msg);
|
||||
get_emu_msg(msg, &msglen);
|
||||
g_assert(msglen == sizeof(exp2));
|
||||
g_assert(memcmp(msg, exp2, msglen) == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a get_device_id to do a basic test.
|
||||
*/
|
||||
static void test_bt_base(void)
|
||||
{
|
||||
uint8_t rsp[20];
|
||||
unsigned int rsplen = sizeof(rsp);
|
||||
|
||||
bt_cmd(get_dev_id_cmd, sizeof(get_dev_id_cmd), rsp, &rsplen);
|
||||
g_assert(rsplen == sizeof(get_dev_id_rsp));
|
||||
g_assert(memcmp(get_dev_id_rsp, rsp, rsplen) == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable IRQs for the interface.
|
||||
*/
|
||||
static void test_enable_irq(void)
|
||||
{
|
||||
uint8_t rsp[20];
|
||||
unsigned int rsplen = sizeof(rsp);
|
||||
|
||||
bt_cmd(set_bmc_globals_cmd, sizeof(set_bmc_globals_cmd), rsp, &rsplen);
|
||||
g_assert(rsplen == sizeof(set_bmc_globals_rsp));
|
||||
g_assert(memcmp(set_bmc_globals_rsp, rsp, rsplen) == 0);
|
||||
bt_write_irqreg(0x01);
|
||||
bt_ints_enabled = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a local TCP socket with any port, then save off the port we got.
|
||||
*/
|
||||
static void open_socket(void)
|
||||
{
|
||||
struct sockaddr_in myaddr;
|
||||
socklen_t addrlen;
|
||||
|
||||
myaddr.sin_family = AF_INET;
|
||||
myaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
myaddr.sin_port = 0;
|
||||
emu_lfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (emu_lfd == -1) {
|
||||
perror("socket");
|
||||
exit(1);
|
||||
}
|
||||
if (bind(emu_lfd, (struct sockaddr *) &myaddr, sizeof(myaddr)) == -1) {
|
||||
perror("bind");
|
||||
exit(1);
|
||||
}
|
||||
addrlen = sizeof(myaddr);
|
||||
if (getsockname(emu_lfd, (struct sockaddr *) &myaddr , &addrlen) == -1) {
|
||||
perror("getsockname");
|
||||
exit(1);
|
||||
}
|
||||
emu_port = ntohs(myaddr.sin_port);
|
||||
assert(listen(emu_lfd, 1) != -1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *arch = qtest_get_arch();
|
||||
char *cmdline;
|
||||
int ret;
|
||||
|
||||
/* Check architecture */
|
||||
if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) {
|
||||
g_test_message("Skipping test for non-x86\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
open_socket();
|
||||
|
||||
/* Run the tests */
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
cmdline = g_strdup_printf("-vnc none"
|
||||
" -chardev socket,id=ipmi0,host=localhost,port=%d,reconnect=10"
|
||||
" -device ipmi-bmc-extern,chardev=ipmi0,id=bmc0"
|
||||
" -device isa-ipmi-bt,bmc=bmc0", emu_port);
|
||||
qtest_start(cmdline);
|
||||
qtest_irq_intercept_in(global_qtest, "ioapic");
|
||||
qtest_add_func("/ipmi/extern/connect", test_connect);
|
||||
qtest_add_func("/ipmi/extern/bt_base", test_bt_base);
|
||||
qtest_add_func("/ipmi/extern/bt_enable_irq", test_enable_irq);
|
||||
qtest_add_func("/ipmi/extern/bt_base_irq", test_bt_base);
|
||||
ret = g_test_run();
|
||||
qtest_quit(global_qtest);
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
* IPMI KCS test cases, using the local interface.
|
||||
*
|
||||
* Copyright (c) 2012 Corey Minyard <cminyard@mvista.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "libqtest.h"
|
||||
|
||||
#define IPMI_IRQ 5
|
||||
|
||||
#define IPMI_KCS_BASE 0xca2
|
||||
|
||||
#define IPMI_KCS_STATUS_ABORT 0x60
|
||||
#define IPMI_KCS_CMD_WRITE_START 0x61
|
||||
#define IPMI_KCS_CMD_WRITE_END 0x62
|
||||
#define IPMI_KCS_CMD_READ 0x68
|
||||
|
||||
#define IPMI_KCS_ABORTED_BY_CMD 0x01
|
||||
|
||||
#define IPMI_KCS_CMDREG_GET_STATE() ((kcs_get_cmdreg() >> 6) & 3)
|
||||
#define IPMI_KCS_STATE_IDLE 0
|
||||
#define IPMI_KCS_STATE_READ 1
|
||||
#define IPMI_KCS_STATE_WRITE 2
|
||||
#define IPMI_KCS_STATE_ERROR 3
|
||||
#define IPMI_KCS_CMDREG_GET_CD() ((kcs_get_cmdreg() >> 3) & 1)
|
||||
#define IPMI_KCS_CMDREG_GET_ATN() ((kcs_get_cmdreg() >> 2) & 1)
|
||||
#define IPMI_KCS_CMDREG_GET_IBF() ((kcs_get_cmdreg() >> 1) & 1)
|
||||
#define IPMI_KCS_CMDREG_GET_OBF() ((kcs_get_cmdreg() >> 0) & 1)
|
||||
|
||||
static int kcs_ints_enabled;
|
||||
|
||||
static uint8_t kcs_get_cmdreg(void)
|
||||
{
|
||||
return inb(IPMI_KCS_BASE + 1);
|
||||
}
|
||||
|
||||
static void kcs_write_cmdreg(uint8_t val)
|
||||
{
|
||||
outb(IPMI_KCS_BASE + 1, val);
|
||||
}
|
||||
|
||||
static uint8_t kcs_get_datareg(void)
|
||||
{
|
||||
return inb(IPMI_KCS_BASE);
|
||||
}
|
||||
|
||||
static void kcs_write_datareg(uint8_t val)
|
||||
{
|
||||
outb(IPMI_KCS_BASE, val);
|
||||
}
|
||||
|
||||
static void kcs_wait_ibf(void)
|
||||
{
|
||||
unsigned int count = 1000;
|
||||
while (IPMI_KCS_CMDREG_GET_IBF() != 0) {
|
||||
g_assert(--count != 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void kcs_wait_obf(void)
|
||||
{
|
||||
unsigned int count = 1000;
|
||||
while (IPMI_KCS_CMDREG_GET_OBF() == 0) {
|
||||
g_assert(--count != 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void kcs_clear_obf(void)
|
||||
{
|
||||
if (kcs_ints_enabled) {
|
||||
g_assert(get_irq(IPMI_IRQ));
|
||||
} else {
|
||||
g_assert(!get_irq(IPMI_IRQ));
|
||||
}
|
||||
g_assert(IPMI_KCS_CMDREG_GET_OBF() == 1);
|
||||
kcs_get_datareg();
|
||||
g_assert(IPMI_KCS_CMDREG_GET_OBF() == 0);
|
||||
g_assert(!get_irq(IPMI_IRQ));
|
||||
}
|
||||
|
||||
static void kcs_check_state(uint8_t state)
|
||||
{
|
||||
g_assert(IPMI_KCS_CMDREG_GET_STATE() == state);
|
||||
}
|
||||
|
||||
static void kcs_cmd(uint8_t *cmd, unsigned int cmd_len,
|
||||
uint8_t *rsp, unsigned int *rsp_len)
|
||||
{
|
||||
unsigned int i, j = 0;
|
||||
|
||||
/* Should be idle */
|
||||
g_assert(kcs_get_cmdreg() == 0);
|
||||
|
||||
kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_START);
|
||||
kcs_wait_ibf();
|
||||
kcs_check_state(IPMI_KCS_STATE_WRITE);
|
||||
kcs_clear_obf();
|
||||
for (i = 0; i < cmd_len; i++) {
|
||||
kcs_write_datareg(cmd[i]);
|
||||
kcs_wait_ibf();
|
||||
kcs_check_state(IPMI_KCS_STATE_WRITE);
|
||||
kcs_clear_obf();
|
||||
}
|
||||
kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_END);
|
||||
kcs_wait_ibf();
|
||||
kcs_check_state(IPMI_KCS_STATE_WRITE);
|
||||
kcs_clear_obf();
|
||||
kcs_write_datareg(0);
|
||||
next_read_byte:
|
||||
kcs_wait_ibf();
|
||||
switch (IPMI_KCS_CMDREG_GET_STATE()) {
|
||||
case IPMI_KCS_STATE_READ:
|
||||
kcs_wait_obf();
|
||||
g_assert(j < *rsp_len);
|
||||
rsp[j++] = kcs_get_datareg();
|
||||
kcs_write_datareg(IPMI_KCS_CMD_READ);
|
||||
goto next_read_byte;
|
||||
break;
|
||||
|
||||
case IPMI_KCS_STATE_IDLE:
|
||||
kcs_wait_obf();
|
||||
kcs_get_datareg();
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert(0);
|
||||
}
|
||||
*rsp_len = j;
|
||||
}
|
||||
|
||||
static void kcs_abort(uint8_t *cmd, unsigned int cmd_len,
|
||||
uint8_t *rsp, unsigned int *rsp_len)
|
||||
{
|
||||
unsigned int i, j = 0;
|
||||
unsigned int retries = 4;
|
||||
|
||||
/* Should be idle */
|
||||
g_assert(kcs_get_cmdreg() == 0);
|
||||
|
||||
kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_START);
|
||||
kcs_wait_ibf();
|
||||
kcs_check_state(IPMI_KCS_STATE_WRITE);
|
||||
kcs_clear_obf();
|
||||
for (i = 0; i < cmd_len; i++) {
|
||||
kcs_write_datareg(cmd[i]);
|
||||
kcs_wait_ibf();
|
||||
kcs_check_state(IPMI_KCS_STATE_WRITE);
|
||||
kcs_clear_obf();
|
||||
}
|
||||
kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_END);
|
||||
kcs_wait_ibf();
|
||||
kcs_check_state(IPMI_KCS_STATE_WRITE);
|
||||
kcs_clear_obf();
|
||||
kcs_write_datareg(0);
|
||||
kcs_wait_ibf();
|
||||
switch (IPMI_KCS_CMDREG_GET_STATE()) {
|
||||
case IPMI_KCS_STATE_READ:
|
||||
kcs_wait_obf();
|
||||
g_assert(j < *rsp_len);
|
||||
rsp[j++] = kcs_get_datareg();
|
||||
kcs_write_datareg(IPMI_KCS_CMD_READ);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert(0);
|
||||
}
|
||||
|
||||
/* Start the abort here */
|
||||
retry_abort:
|
||||
g_assert(retries > 0);
|
||||
|
||||
kcs_wait_ibf();
|
||||
kcs_write_cmdreg(IPMI_KCS_STATUS_ABORT);
|
||||
kcs_wait_ibf();
|
||||
kcs_clear_obf();
|
||||
kcs_write_datareg(0);
|
||||
kcs_wait_ibf();
|
||||
if (IPMI_KCS_CMDREG_GET_STATE() != IPMI_KCS_STATE_READ) {
|
||||
retries--;
|
||||
goto retry_abort;
|
||||
}
|
||||
kcs_wait_obf();
|
||||
rsp[0] = kcs_get_datareg();
|
||||
kcs_write_datareg(IPMI_KCS_CMD_READ);
|
||||
kcs_wait_ibf();
|
||||
if (IPMI_KCS_CMDREG_GET_STATE() != IPMI_KCS_STATE_IDLE) {
|
||||
retries--;
|
||||
goto retry_abort;
|
||||
}
|
||||
kcs_wait_obf();
|
||||
kcs_clear_obf();
|
||||
|
||||
*rsp_len = j;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t get_dev_id_cmd[] = { 0x18, 0x01 };
|
||||
static uint8_t get_dev_id_rsp[] = { 0x1c, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
|
||||
0x02, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
/*
|
||||
* Send a get_device_id to do a basic test.
|
||||
*/
|
||||
static void test_kcs_base(void)
|
||||
{
|
||||
uint8_t rsp[20];
|
||||
unsigned int rsplen = sizeof(rsp);
|
||||
|
||||
kcs_cmd(get_dev_id_cmd, sizeof(get_dev_id_cmd), rsp, &rsplen);
|
||||
g_assert(rsplen == sizeof(get_dev_id_rsp));
|
||||
g_assert(memcmp(get_dev_id_rsp, rsp, rsplen) == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Abort a kcs operation while reading
|
||||
*/
|
||||
static void test_kcs_abort(void)
|
||||
{
|
||||
uint8_t rsp[20];
|
||||
unsigned int rsplen = sizeof(rsp);
|
||||
|
||||
kcs_abort(get_dev_id_cmd, sizeof(get_dev_id_cmd), rsp, &rsplen);
|
||||
g_assert(rsp[0] == IPMI_KCS_ABORTED_BY_CMD);
|
||||
}
|
||||
|
||||
static uint8_t set_bmc_globals_cmd[] = { 0x18, 0x2e, 0x0f };
|
||||
static uint8_t set_bmc_globals_rsp[] = { 0x1c, 0x2e, 0x00 };
|
||||
|
||||
/*
|
||||
* Enable interrupts
|
||||
*/
|
||||
static void test_enable_irq(void)
|
||||
{
|
||||
uint8_t rsp[20];
|
||||
unsigned int rsplen = sizeof(rsp);
|
||||
|
||||
kcs_cmd(set_bmc_globals_cmd, sizeof(set_bmc_globals_cmd), rsp, &rsplen);
|
||||
g_assert(rsplen == sizeof(set_bmc_globals_rsp));
|
||||
g_assert(memcmp(set_bmc_globals_rsp, rsp, rsplen) == 0);
|
||||
kcs_ints_enabled = 1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *arch = qtest_get_arch();
|
||||
char *cmdline;
|
||||
int ret;
|
||||
|
||||
/* Check architecture */
|
||||
if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) {
|
||||
g_test_message("Skipping test for non-x86\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Run the tests */
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
cmdline = g_strdup_printf("-vnc none -device ipmi-bmc-sim,id=bmc0"
|
||||
" -device isa-ipmi-kcs,bmc=bmc0");
|
||||
qtest_start(cmdline);
|
||||
qtest_irq_intercept_in(global_qtest, "ioapic");
|
||||
qtest_add_func("/ipmi/local/kcs_base", test_kcs_base);
|
||||
qtest_add_func("/ipmi/local/kcs_abort", test_kcs_abort);
|
||||
qtest_add_func("/ipmi/local/kcs_enable_irq", test_enable_irq);
|
||||
qtest_add_func("/ipmi/local/kcs_base_irq", test_kcs_base);
|
||||
qtest_add_func("/ipmi/local/kcs_abort_irq", test_kcs_abort);
|
||||
ret = g_test_run();
|
||||
qtest_quit(global_qtest);
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -50,10 +50,11 @@ void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared)
|
|||
#if defined(__powerpc64__) && defined(__linux__)
|
||||
/* On ppc64 mappings in the same segment (aka slice) must share the same
|
||||
* page size. Since we will be re-allocating part of this segment
|
||||
* from the supplied fd, we should make sure to use the same page size,
|
||||
* unless we are using the system page size, in which case anonymous memory
|
||||
* is OK. Use align as a hint for the page size.
|
||||
* In this case, set MAP_NORESERVE to avoid allocating backing store memory.
|
||||
* from the supplied fd, we should make sure to use the same page size, to
|
||||
* this end we mmap the supplied fd. In this case, set MAP_NORESERVE to
|
||||
* avoid allocating backing store memory.
|
||||
* We do this unless we are using the system page size, in which case
|
||||
* anonymous memory is OK.
|
||||
*/
|
||||
int anonfd = fd == -1 || qemu_fd_getpagesize(fd) == getpagesize() ? -1 : fd;
|
||||
int flags = anonfd == -1 ? MAP_ANONYMOUS : MAP_NORESERVE;
|
||||
|
|
Loading…
Reference in New Issue