@@ -51,6 +51,7 @@ CONFIG_APIC=y
CONFIG_IOAPIC=y
CONFIG_PVPANIC=y
CONFIG_MEM_HOTPLUG=y
+CONFIG_VMGENID=y
CONFIG_NVDIMM=y
CONFIG_ACPI_NVDIMM=y
CONFIG_XIO3130=y
@@ -51,6 +51,7 @@ CONFIG_APIC=y
CONFIG_IOAPIC=y
CONFIG_PVPANIC=y
CONFIG_MEM_HOTPLUG=y
+CONFIG_VMGENID=y
CONFIG_NVDIMM=y
CONFIG_ACPI_NVDIMM=y
CONFIG_XIO3130=y
@@ -47,6 +47,7 @@ PCI devices (other than virtio):
1b36:0005 PCI test device (docs/specs/pci-testdev.txt)
1b36:0006 PCI Rocker Ethernet switch device
1b36:0007 PCI SD Card Host Controller Interface (SDHCI)
+1b36:0009 PCI VM-Generation device
1b36:000a PCI-PCI bridge (multiseat)
All these devices are documented in docs/specs.
@@ -44,6 +44,7 @@
#include "hw/acpi/tpm.h"
#include "sysemu/tpm_backend.h"
#include "hw/timer/mc146818rtc_regs.h"
+#include "hw/misc/vmgenid.h"
/* Supported chipsets: */
#include "hw/acpi/piix4.h"
@@ -237,6 +238,40 @@ static void acpi_get_misc_info(AcpiMiscInfo *info)
info->applesmc_io_base = applesmc_port();
}
+static Aml *build_vmgenid_device(uint64_t buf_paddr)
+{
+ Aml *dev, *pkg, *crs;
+
+ dev = aml_device("VGEN");
+ aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0003")));
+ aml_append(dev, aml_name_decl("_CID", aml_string("VM_Gen_Counter")));
+ aml_append(dev, aml_name_decl("_DDN", aml_string("VM_Gen_Counter")));
+
+ pkg = aml_package(2);
+ /* low 32 bits of UUID buffer addr */
+ aml_append(pkg, aml_int(buf_paddr & 0xFFFFFFFFUL));
+ /* high 32 bits of UUID buffer addr */
+ aml_append(pkg, aml_int(buf_paddr >> 32));
+ aml_append(dev, aml_name_decl("ADDR", pkg));
+
+ /*
+ * VMGEN device has class_id PCI_CLASS_MEMORY_RAM and Windows
+ * displays it as "PCI RAM controller" which is marked as NO_DRV
+ * so Windows ignores VMGEN device completely and doesn't check
+ * for resource conflicts which during PCI rebalancing can lead
+ * to another PCI device claiming ignored BARs. To prevent this
+ * statically reserve resources used by VM_Gen_Counter.
+ * For more verbose comment see this commit message.
+ */
+ crs = aml_resource_template();
+ aml_append(crs, aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED,
+ AML_MAX_FIXED, AML_CACHEABLE, AML_READ_WRITE, 0,
+ buf_paddr, buf_paddr + VMGENID_VMGID_BUF_SIZE - 1, 0,
+ VMGENID_VMGID_BUF_SIZE));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+ return dev;
+}
+
/*
* Because of the PXB hosts we cannot simply query TYPE_PCI_HOST_BRIDGE.
* On i386 arch we only have two pci hosts, so we can look only for them.
@@ -2171,6 +2206,7 @@ build_ssdt(GArray *table_data, GArray *linker,
}
if (bus) {
+ Object *vmgen;
Aml *scope = aml_scope("PCI0");
/* Scan all PCI buses. Generate tables to support hotplug. */
build_append_pci_bus_devices(scope, bus, pm->pcihp_bridge_en);
@@ -2187,6 +2223,24 @@ build_ssdt(GArray *table_data, GArray *linker,
aml_append(scope, dev);
}
+ vmgen = find_vmgneid_dev(NULL);
+ if (vmgen) {
+ PCIDevice *pdev = PCI_DEVICE(vmgen);
+ uint64_t buf_paddr =
+ pci_get_bar_addr(pdev, VMGENID_VMGID_BUF_BAR);
+
+ if (buf_paddr != PCI_BAR_UNMAPPED) {
+ aml_append(scope, build_vmgenid_device(buf_paddr));
+
+ method = aml_method("\\_GPE._E00", 0,
+ AML_NOTSERIALIZED);
+ aml_append(method,
+ aml_notify(aml_name("\\_SB.PCI0.VGEN"),
+ aml_int(0x80)));
+ aml_append(ssdt, method);
+ }
+ }
+
aml_append(sb_scope, scope);
}
}
@@ -2489,8 +2543,6 @@ build_dsdt(GArray *table_data, GArray *linker,
{
aml_append(scope, aml_name_decl("_HID", aml_string("ACPI0006")));
- aml_append(scope, aml_method("_L00", 0, AML_NOTSERIALIZED));
-
if (misc->is_piix4) {
method = aml_method("_E01", 0, AML_NOTSERIALIZED);
aml_append(method,
@@ -43,4 +43,5 @@ obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o
obj-$(CONFIG_PVPANIC) += pvpanic.o
obj-$(CONFIG_EDU) += edu.o
+obj-$(CONFIG_VMGENID) += vmgenid.o
obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
new file mode 100644
@@ -0,0 +1,154 @@
+/*
+ * Virtual Machine Generation ID Device
+ *
+ * Copyright (C) 2016 Red Hat Inc.
+ *
+ * Authors: Gal Hammer <ghammer@redhat.com>
+ * Igor Mammedov <imammedo@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "hw/i386/pc.h"
+#include "hw/pci/pci.h"
+#include "hw/misc/vmgenid.h"
+#include "hw/acpi/acpi.h"
+#include "qapi/visitor.h"
+
+#define VMGENID(obj) OBJECT_CHECK(VmGenIdState, (obj), VMGENID_DEVICE)
+
+typedef struct VmGenIdState {
+ PCIDevice parent_obj;
+ MemoryRegion iomem;
+ union {
+ uint8_t guid[16];
+ uint8_t guid_page[VMGENID_VMGID_BUF_SIZE];
+ };
+ bool guid_set;
+} VmGenIdState;
+
+Object *find_vmgneid_dev(Error **errp)
+{
+ Object *obj = object_resolve_path_type("", VMGENID_DEVICE, NULL);
+ if (!obj) {
+ error_setg(errp, VMGENID_DEVICE " is not found");
+ }
+ return obj;
+}
+
+static void vmgenid_update_guest(VmGenIdState *s)
+{
+ Object *acpi_obj;
+ void *ptr = memory_region_get_ram_ptr(&s->iomem);
+
+ memcpy(ptr, &s->guid, sizeof(s->guid));
+ memory_region_set_dirty(&s->iomem, 0, sizeof(s->guid));
+
+ acpi_obj = object_resolve_path_type("", TYPE_ACPI_DEVICE_IF, NULL);
+ if (acpi_obj) {
+ AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(acpi_obj);
+ AcpiDeviceIf *adev = ACPI_DEVICE_IF(acpi_obj);
+ ACPIREGS *acpi_regs = adevc->regs(adev);
+
+ acpi_regs->gpe.sts[0] |= 1; /* _GPE.E00 handler */
+ acpi_update_sci(acpi_regs, adevc->sci(adev));
+ }
+}
+
+static void vmgenid_set_guid(Object *obj, const char *value, Error **errp)
+{
+ VmGenIdState *s = VMGENID(obj);
+
+ if (qemu_uuid_parse(value, s->guid) < 0) {
+ error_setg(errp, "'%s." VMGENID_GUID
+ "': Failed to parse GUID string: %s",
+ object_get_typename(OBJECT(s)),
+ value);
+ return;
+ }
+
+ s->guid_set = true;
+ vmgenid_update_guest(s);
+}
+
+static void vmgenid_get_vmgid_addr(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ int64_t value = pci_get_bar_addr(PCI_DEVICE(obj), 0);
+
+ if (value == PCI_BAR_UNMAPPED) {
+ error_setg(errp, "'%s." VMGENID_VMGID_BUF_ADDR "': not initialized",
+ object_get_typename(OBJECT(obj)));
+ return;
+ }
+ visit_type_int(v, &value, name, errp);
+}
+
+static void vmgenid_initfn(Object *obj)
+{
+ VmGenIdState *s = VMGENID(obj);
+
+ memory_region_init_ram(&s->iomem, obj, "vgid.bar", sizeof(s->guid_page),
+ &error_abort);
+
+ object_property_add_str(obj, VMGENID_GUID, NULL, vmgenid_set_guid, NULL);
+ object_property_add(obj, VMGENID_VMGID_BUF_ADDR, "int",
+ vmgenid_get_vmgid_addr, NULL, NULL, NULL, NULL);
+}
+
+
+static void vmgenid_realize(PCIDevice *dev, Error **errp)
+{
+ VmGenIdState *s = VMGENID(dev);
+ bool ambiguous = false;
+
+ object_resolve_path_type("", VMGENID_DEVICE, &ambiguous);
+ if (ambiguous) {
+ error_setg(errp, "no more than one " VMGENID_DEVICE
+ " device is permitted");
+ return;
+ }
+
+ if (!s->guid_set) {
+ error_setg(errp, "'%s." VMGENID_GUID "' property is not set",
+ object_get_typename(OBJECT(s)));
+ return;
+ }
+
+ vmstate_register_ram(&s->iomem, DEVICE(s));
+ pci_register_bar(PCI_DEVICE(s), VMGENID_VMGID_BUF_BAR,
+ PCI_BASE_ADDRESS_MEM_PREFETCH |
+ PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64,
+ &s->iomem);
+ return;
+}
+
+static void vmgenid_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->hotpluggable = false;
+ k->realize = vmgenid_realize;
+ k->vendor_id = PCI_VENDOR_ID_REDHAT;
+ k->device_id = PCI_DEVICE_ID_REDHAT_VMGENID;
+ k->class_id = PCI_CLASS_MEMORY_RAM;
+}
+
+static const TypeInfo vmgenid_device_info = {
+ .name = VMGENID_DEVICE,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(VmGenIdState),
+ .instance_init = vmgenid_initfn,
+ .class_init = vmgenid_class_init,
+};
+
+static void vmgenid_register_types(void)
+{
+ type_register_static(&vmgenid_device_info);
+}
+
+type_init(vmgenid_register_types)
new file mode 100644
@@ -0,0 +1,27 @@
+/*
+ * Virtual Machine Generation ID Device
+ *
+ * Copyright (C) 2016 Red Hat Inc.
+ *
+ * Authors: Gal Hammer <ghammer@redhat.com>
+ * Igor Mammedov <imammedo@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef HW_MISC_VMGENID_H
+#define HW_MISC_VMGENID_H
+
+#include "qom/object.h"
+
+#define VMGENID_DEVICE "vmgenid"
+#define VMGENID_GUID "guid"
+#define VMGENID_VMGID_BUF_ADDR "vmgid-addr"
+#define VMGENID_VMGID_BUF_SIZE 0x1000
+#define VMGENID_VMGID_BUF_BAR 0
+
+Object *find_vmgneid_dev(Error **errp);
+
+#endif
@@ -94,6 +94,7 @@
#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_VMGENID 0x000c
#define PCI_DEVICE_ID_REDHAT_QXL 0x0100
#define FMT_PCIBUS PRIx64