[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH V16 3/4] i386: add a Virtual Machine Generation ID d
From: |
Gal Hammer |
Subject: |
[Qemu-devel] [PATCH V16 3/4] i386: add a Virtual Machine Generation ID device |
Date: |
Tue, 16 Jun 2015 17:11:02 +0300 |
Based on Microsoft's specifications (paper can be downloaded from
http://go.microsoft.com/fwlink/?LinkId=260709), add a device
description to the SSDT ACPI table and its implementation.
The GUID is set using a global "vmgenid.uuid" parameter.
Signed-off-by: Gal Hammer <address@hidden>
---
default-configs/i386-softmmu.mak | 1 +
default-configs/x86_64-softmmu.mak | 1 +
hw/i386/acpi-build.c | 32 +++++++++
hw/misc/Makefile.objs | 1 +
hw/misc/vmgenid.c | 140 +++++++++++++++++++++++++++++++++++++
include/hw/i386/pc.h | 3 +
6 files changed, 178 insertions(+)
create mode 100644 hw/misc/vmgenid.c
diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index 91d602c..3c2fda8 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -48,3 +48,4 @@ CONFIG_MEM_HOTPLUG=y
CONFIG_XIO3130=y
CONFIG_IOH3420=y
CONFIG_I82801B11=y
+CONFIG_VMGENID=y
diff --git a/default-configs/x86_64-softmmu.mak
b/default-configs/x86_64-softmmu.mak
index 62575eb..36e654d 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -49,3 +49,4 @@ CONFIG_MEM_HOTPLUG=y
CONFIG_XIO3130=y
CONFIG_IOH3420=y
CONFIG_I82801B11=y
+CONFIG_VMGENID=y
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index b71e942..784e23d 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -112,6 +112,7 @@ typedef struct AcpiMiscInfo {
unsigned dsdt_size;
uint16_t pvpanic_port;
uint16_t applesmc_io_base;
+ uint64_t vm_generation_addr;
} AcpiMiscInfo;
typedef struct AcpiBuildPciBusHotplugState {
@@ -238,6 +239,7 @@ static void acpi_get_misc_info(AcpiMiscInfo *info)
info->tpm_version = tpm_get_version();
info->pvpanic_port = pvpanic_port();
info->applesmc_io_base = applesmc_port();
+ info->vm_generation_addr = vm_generation_addr();
}
/*
@@ -1128,6 +1130,36 @@ build_ssdt(GArray *table_data, GArray *linker,
}
sb_scope = aml_scope("\\_SB");
+
+ if (misc->vm_generation_addr) {
+ dev = aml_device("VMGI");
+ aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0002")));
+ aml_append(dev,
+ aml_name_decl("_CID", aml_string("VM_Gen_Counter")));
+ aml_append(dev,
+ aml_name_decl("_DDN", aml_string("VM_Gen_Counter")));
+
+ method = aml_method("ADDR", 0);
+ pkg = aml_package(2);
+ aml_append(pkg, aml_int(misc->vm_generation_addr & 0xffffffff));
+ aml_append(pkg, aml_int(misc->vm_generation_addr >> 32));
+ aml_append(method, aml_store(pkg, aml_local(0)));
+ aml_append(method, aml_return(aml_local(0)));
+ aml_append(dev, method);
+
+ aml_append(sb_scope, dev);
+ aml_append(ssdt, sb_scope);
+
+ scope = aml_scope("\\_GPE");
+
+ method = aml_method("_E00", 0);
+ aml_append(method,
+ aml_notify(aml_name("\\_SB.VMGI"), aml_int(0x80)));
+
+ aml_append(scope, method);
+ aml_append(ssdt, scope);
+ }
+
{
/* create PCI0.PRES device and its _CRS to reserve CPU hotplug MMIO */
dev = aml_device("PCI0." stringify(CPU_HOTPLUG_RESOURCE_DEVICE));
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 4aa76ff..3db11de 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -40,3 +40,4 @@ obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o
obj-$(CONFIG_PVPANIC) += pvpanic.o
obj-$(CONFIG_EDU) += edu.o
+obj-$(CONFIG_VMGENID) += vmgenid.o
diff --git a/hw/misc/vmgenid.c b/hw/misc/vmgenid.c
new file mode 100644
index 0000000..4484952
--- /dev/null
+++ b/hw/misc/vmgenid.c
@@ -0,0 +1,140 @@
+/*
+ * Virtual Machine Generation ID Device
+ *
+ * Copyright (C) 2015 Red Hat Inc.
+ *
+ * Authors: Gal Hammer <address@hidden>
+ *
+ * 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/acpi/acpi_dev_interface.h"
+
+#define VMGENID_DEVICE "vmgenid"
+
+/* Use the memory address which follows HPET for no special reason. */
+#define VMGENID_DEFAULT_ADDRESS 0xfedf0000
+
+#define PROPERTY_ADDR "addr"
+#define PROPERTY_UUID "uuid"
+
+#define VMGENID(obj) OBJECT_CHECK(VmGenIdState, (obj), VMGENID_DEVICE)
+
+typedef struct VmGenIdState {
+ DeviceState parent_obj;
+ MemoryRegion iomem;
+ uint64_t addr;
+ uint8_t guid[16];
+ bool guid_set;
+} VmGenIdState;
+
+uint64_t vm_generation_addr(void)
+{
+ Object *obj = object_resolve_path_type("", VMGENID_DEVICE, NULL);
+ VmGenIdState *s = VMGENID(obj);
+
+ if (!obj) {
+ return 0;
+ }
+ return s->addr;
+}
+
+static void update_guest(VmGenIdState *s, bool notify)
+{
+ void *ptr = memory_region_get_ram_ptr(&s->iomem);
+ Object *acpi_obj;
+
+ memcpy(ptr, &s->guid, sizeof(s->guid));
+ memory_region_set_dirty(&s->iomem, 0, sizeof(s->guid));
+
+ if (notify) {
+ 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);
+
+ adevc->vm_generation_id_changed(adev);
+ }
+ }
+}
+
+static void vmgenid_set_uuid(Object *obj, const char *value, Error **errp)
+{
+ VmGenIdState *s = VMGENID(obj);
+ bool first_set = !s->guid_set;
+
+ if (qemu_uuid_parse(value, s->guid) < 0) {
+ error_setg(errp, "Fail to parse UUID string.");
+ return;
+ }
+ s->guid_set = true;
+
+ /* Skip the acpi notification when setting the vm generation id for the
+ * first time. This is done because in a q35 machine the gpe register is
+ * allocated after the device is initialized.
+ */
+ update_guest(s, !first_set);
+}
+
+static void vmgenid_realize(DeviceState *qdev, Error **errp)
+{
+ VmGenIdState *s = VMGENID(qdev);
+
+ if (!s->guid_set) {
+ error_setg(errp, "Missing virtual machine generation ID.");
+ return;
+ }
+
+ if (s->addr % 8 != 0) {
+ error_setg(errp, "Address must be 8-byte aligned.");
+ return;
+ }
+
+ vmstate_register_ram(&s->iomem, DEVICE(s));
+ memory_region_add_subregion(get_system_memory(), s->addr, &s->iomem);
+}
+
+static void vmgenid_init(Object *obj)
+{
+ VmGenIdState *s = VMGENID(obj);
+
+ object_property_add_str(obj, PROPERTY_UUID, NULL, vmgenid_set_uuid, NULL);
+
+ memory_region_init_ram(&s->iomem, OBJECT(s), "vm-generation-id", 0x1000,
+ &error_abort);
+}
+
+static Property vmgenid_properties[] = {
+ DEFINE_PROP_UINT64(PROPERTY_ADDR, VmGenIdState, addr,
+ VMGENID_DEFAULT_ADDRESS),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vmgenid_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = vmgenid_realize;
+ dc->props = vmgenid_properties;
+ dc->hotpluggable = false;
+
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+}
+
+static const TypeInfo vmgenid_device_info = {
+ .name = VMGENID_DEVICE,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(VmGenIdState),
+ .instance_init = vmgenid_init,
+ .class_init = vmgenid_class_init,
+};
+
+static void vmgenid_register_types(void)
+{
+ type_register_static(&vmgenid_device_info);
+}
+
+type_init(vmgenid_register_types)
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 86c5651..9492e32 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -281,6 +281,9 @@ void pc_system_firmware_init(MemoryRegion *rom_memory,
/* pvpanic.c */
uint16_t pvpanic_port(void);
+/* vmgenid.c */
+uint64_t vm_generation_addr(void);
+
/* e820 types */
#define E820_RAM 1
#define E820_RESERVED 2
--
2.1.0