diff mbox

[1/2] i386: Add a Virtual Machine Generation ID device

Message ID 1413726188-29285-2-git-send-email-ghammer@redhat.com
State New
Headers show

Commit Message

Gal Hammer Oct. 19, 2014, 1:43 p.m. UTC
Based on Microsoft's sepecifications (paper can be dowloaded 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 <ghammer@redhat.com>

---
 default-configs/i386-softmmu.mak     |   1 +
 default-configs/x86_64-softmmu.mak   |   1 +
 hw/acpi/core.c                       |   8 +++
 hw/acpi/ich9.c                       |   8 +++
 hw/acpi/piix4.c                      |   8 +++
 hw/i386/acpi-build.c                 |   8 +++
 hw/i386/acpi-dsdt.dsl                |   4 +-
 hw/i386/acpi-dsdt.hex.generated      |   6 +-
 hw/i386/pc.c                         |   8 +++
 hw/i386/q35-acpi-dsdt.dsl            |   5 +-
 hw/i386/q35-acpi-dsdt.hex.generated  |   8 +--
 hw/i386/ssdt-misc.dsl                |  36 +++++++++++
 hw/i386/ssdt-misc.hex.generated      |   8 +--
 hw/isa/lpc_ich9.c                    |   1 +
 hw/misc/Makefile.objs                |   1 +
 hw/misc/vmgenid.c                    | 116 +++++++++++++++++++++++++++++++++++
 include/hw/acpi/acpi.h               |   2 +
 include/hw/acpi/acpi_dev_interface.h |   4 ++
 include/hw/acpi/ich9.h               |   2 +
 include/hw/i386/pc.h                 |   3 +
 include/hw/misc/vmgenid.h            |  21 +++++++
 21 files changed, 246 insertions(+), 13 deletions(-)
 create mode 100644 hw/misc/vmgenid.c
 create mode 100644 include/hw/misc/vmgenid.h

Comments

Michael S. Tsirkin Oct. 19, 2014, 3:14 p.m. UTC | #1
On Sun, Oct 19, 2014 at 04:43:07PM +0300, Gal Hammer wrote:
> Based on Microsoft's sepecifications (paper can be dowloaded 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 <ghammer@redhat.com>
> 
> ---
>  default-configs/i386-softmmu.mak     |   1 +
>  default-configs/x86_64-softmmu.mak   |   1 +
>  hw/acpi/core.c                       |   8 +++
>  hw/acpi/ich9.c                       |   8 +++
>  hw/acpi/piix4.c                      |   8 +++
>  hw/i386/acpi-build.c                 |   8 +++
>  hw/i386/acpi-dsdt.dsl                |   4 +-
>  hw/i386/acpi-dsdt.hex.generated      |   6 +-
>  hw/i386/pc.c                         |   8 +++
>  hw/i386/q35-acpi-dsdt.dsl            |   5 +-
>  hw/i386/q35-acpi-dsdt.hex.generated  |   8 +--
>  hw/i386/ssdt-misc.dsl                |  36 +++++++++++
>  hw/i386/ssdt-misc.hex.generated      |   8 +--
>  hw/isa/lpc_ich9.c                    |   1 +
>  hw/misc/Makefile.objs                |   1 +
>  hw/misc/vmgenid.c                    | 116 +++++++++++++++++++++++++++++++++++
>  include/hw/acpi/acpi.h               |   2 +
>  include/hw/acpi/acpi_dev_interface.h |   4 ++
>  include/hw/acpi/ich9.h               |   2 +
>  include/hw/i386/pc.h                 |   3 +
>  include/hw/misc/vmgenid.h            |  21 +++++++
>  21 files changed, 246 insertions(+), 13 deletions(-)
>  create mode 100644 hw/misc/vmgenid.c
>  create mode 100644 include/hw/misc/vmgenid.h

Please document the host/guest API.
It seems that you are using a hard-coded hardware address,
and using up a GPE.


> 
> diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
> index 8e08841..bd33c75 100644
> --- a/default-configs/i386-softmmu.mak
> +++ b/default-configs/i386-softmmu.mak
> @@ -45,3 +45,4 @@ CONFIG_IOAPIC=y
>  CONFIG_ICC_BUS=y
>  CONFIG_PVPANIC=y
>  CONFIG_MEM_HOTPLUG=y
> +CONFIG_VMGENID=y
> diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
> index 66557ac..006fc7c 100644
> --- a/default-configs/x86_64-softmmu.mak
> +++ b/default-configs/x86_64-softmmu.mak
> @@ -45,3 +45,4 @@ CONFIG_IOAPIC=y
>  CONFIG_ICC_BUS=y
>  CONFIG_PVPANIC=y
>  CONFIG_MEM_HOTPLUG=y
> +CONFIG_VMGENID=y
> diff --git a/hw/acpi/core.c b/hw/acpi/core.c
> index a7368fb..a01c980 100644
> --- a/hw/acpi/core.c
> +++ b/hw/acpi/core.c
> @@ -28,6 +28,8 @@
>  #include "qapi-visit.h"
>  #include "qapi-event.h"
>  
> +#define ACPI_VM_GENERATION_ID_CHANGED_STATUS 1
> +
>  struct acpi_table_header {
>      uint16_t _length;         /* our length, not actual part of the hdr */
>                                /* allows easier parsing for fw_cfg clients */
> @@ -680,3 +682,9 @@ void acpi_update_sci(ACPIREGS *regs, qemu_irq irq)
>                         (regs->pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
>                         !(pm1a_sts & ACPI_BITMASK_TIMER_STATUS));
>  }
> +
> +void acpi_vm_generation_id_changed(ACPIREGS *acpi_regs, qemu_irq irq)
> +{
> +    acpi_regs->gpe.sts[0] |= ACPI_VM_GENERATION_ID_CHANGED_STATUS;
> +    acpi_update_sci(acpi_regs, irq);
> +}
> diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
> index 7b14bbb..5501c0e 100644
> --- a/hw/acpi/ich9.c
> +++ b/hw/acpi/ich9.c
> @@ -316,3 +316,11 @@ void ich9_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list)
>  
>      acpi_memory_ospm_status(&s->pm.acpi_memory_hotplug, list);
>  }
> +
> +void ich9_vm_generation_id_changed(AcpiDeviceIf *adev)
> +{
> +    ICH9LPCState *s = ICH9_LPC_DEVICE(adev);
> +    ICH9LPCPMRegs *pm = &s->pm;
> +
> +    acpi_vm_generation_id_changed(&pm->acpi_regs, pm->irq);
> +}
> diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
> index 0bfa814..ad0ef68 100644
> --- a/hw/acpi/piix4.c
> +++ b/hw/acpi/piix4.c
> @@ -580,6 +580,13 @@ static void piix4_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list)
>      acpi_memory_ospm_status(&s->acpi_memory_hotplug, list);
>  }
>  
> +static void piix4_vm_generation_id_changed(AcpiDeviceIf *adev)
> +{
> +    PIIX4PMState *s = PIIX4_PM(adev);
> +
> +    acpi_vm_generation_id_changed(&s->ar, s->irq);
> +}
> +
>  static Property piix4_pm_properties[] = {
>      DEFINE_PROP_UINT32("smb_io_base", PIIX4PMState, smb_io_base, 0),
>      DEFINE_PROP_UINT8(ACPI_PM_PROP_S3_DISABLED, PIIX4PMState, disable_s3, 0),
> @@ -617,6 +624,7 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data)
>      hc->plug = piix4_device_plug_cb;
>      hc->unplug_request = piix4_device_unplug_request_cb;
>      adevc->ospm_status = piix4_ospm_status;
> +    adevc->vm_generation_id_changed = piix4_vm_generation_id_changed;
>  }
>  
>  static const TypeInfo piix4_pm_info = {
> diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
> index 00be4bb..27d0494 100644
> --- a/hw/i386/acpi-build.c
> +++ b/hw/i386/acpi-build.c
> @@ -42,6 +42,7 @@
>  #include "hw/acpi/memory_hotplug.h"
>  #include "sysemu/tpm.h"
>  #include "hw/acpi/tpm.h"
> +#include "hw/misc/vmgenid.h"
>  
>  /* Supported chipsets: */
>  #include "hw/acpi/piix4.h"
> @@ -96,6 +97,7 @@ typedef struct AcpiMiscInfo {
>      const unsigned char *dsdt_code;
>      unsigned dsdt_size;
>      uint16_t pvpanic_port;
> +    bool vm_generation_id_set;
>  } AcpiMiscInfo;
>  
>  typedef struct AcpiBuildPciBusHotplugState {
> @@ -216,6 +218,7 @@ static void acpi_get_misc_info(AcpiMiscInfo *info)
>      info->has_hpet = hpet_find();
>      info->has_tpm = tpm_find();
>      info->pvpanic_port = pvpanic_port();
> +    info->vm_generation_id_set = vm_generation_id_set();
>  }
>  
>  static void acpi_get_pci_info(PcPciInfo *info)
> @@ -1088,6 +1091,11 @@ build_ssdt(GArray *table_data, GArray *linker,
>      ACPI_BUILD_SET_LE(ssdt_ptr, sizeof(ssdp_misc_aml),
>                        ssdt_isa_pest[0], 16, misc->pvpanic_port);
>  
> +    if (!misc->vm_generation_id_set) {
> +        ACPI_BUILD_SET_LE(ssdt_ptr, sizeof(ssdp_misc_aml),
> +                          ssdt_acpi_vm_gid_addr[0], 32, 0);
> +    }
> +
>      ACPI_BUILD_SET_LE(ssdt_ptr, sizeof(ssdp_misc_aml),
>                        ssdt_mctrl_nr_slots[0], 32, nr_mem);
>  
> diff --git a/hw/i386/acpi-dsdt.dsl b/hw/i386/acpi-dsdt.dsl
> index 559f4b6..10b4afb 100644
> --- a/hw/i386/acpi-dsdt.dsl
> +++ b/hw/i386/acpi-dsdt.dsl
> @@ -307,7 +307,9 @@ DefinitionBlock (
>      Scope(\_GPE) {
>          Name(_HID, "ACPI0006")
>  
> -        Method(_L00) {
> +        External(\_SB.VMGI, DeviceObj)
> +        Method(_E00) {
> +            Notify(\_SB.VMGI, 0x80)
>          }
>          Method(_E01) {
>              // PCI hotplug event
> diff --git a/hw/i386/acpi-dsdt.hex.generated b/hw/i386/acpi-dsdt.hex.generated
> index a21bf41..3fd06d2 100644
> --- a/hw/i386/acpi-dsdt.hex.generated
> +++ b/hw/i386/acpi-dsdt.hex.generated
> @@ -8,7 +8,7 @@ static unsigned char AcpiDsdtAmlCode[] = {
>  0x0,
>  0x0,
>  0x1,
> -0x1f,
> +0x18,
>  0x42,
>  0x58,
>  0x50,
> @@ -32,8 +32,8 @@ static unsigned char AcpiDsdtAmlCode[] = {
>  0x54,
>  0x4c,
>  0x28,
> -0x5,
> -0x10,
> +0x8,
> +0x14,
>  0x20,
>  0x10,
>  0x49,
> diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> index d045e8b..95ec233 100644
> --- a/hw/i386/pc.c
> +++ b/hw/i386/pc.c
> @@ -59,6 +59,7 @@
>  #include "hw/pci/pci_host.h"
>  #include "acpi-build.h"
>  #include "hw/mem/pc-dimm.h"
> +#include "hw/misc/vmgenid.h"
>  #include "trace.h"
>  #include "qapi/visitor.h"
>  
> @@ -1361,6 +1362,7 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
>      int i;
>      DriveInfo *fd[MAX_FD];
>      DeviceState *hpet = NULL;
> +    DeviceState *vmgenid;
>      int pit_isa_irq = 0;
>      qemu_irq pit_alt_irq = NULL;
>      qemu_irq rtc_irq = NULL;
> @@ -1459,6 +1461,12 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
>          fd[i] = drive_get(IF_FLOPPY, 0, i);
>      }
>      *floppy = fdctrl_init_isa(isa_bus, fd);
> +
> +    vmgenid = qdev_try_create(NULL, VMGENID_DEVICE);
> +    if (vmgenid) {
> +        qdev_init_nofail(vmgenid);
> +        sysbus_mmio_map(SYS_BUS_DEVICE(vmgenid), 0, VMGENID_BASE_ADDRESS);
> +    }
>  }
>  
>  void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
> diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
> index 054b035..b1d04e2 100644
> --- a/hw/i386/q35-acpi-dsdt.dsl
> +++ b/hw/i386/q35-acpi-dsdt.dsl
> @@ -415,7 +415,10 @@ DefinitionBlock (
>      Scope(\_GPE) {
>          Name(_HID, "ACPI0006")
>  
> -        Method(_L00) {
> +        External(\_SB.VMGI, DeviceObj)
> +        Method(_E00)
> +        {
> +            Notify(\_SB.VMGI, 0x80)
>          }
>          Method(_L01) {
>          }
> diff --git a/hw/i386/q35-acpi-dsdt.hex.generated b/hw/i386/q35-acpi-dsdt.hex.generated
> index c9eb4ac..866e648 100644
> --- a/hw/i386/q35-acpi-dsdt.hex.generated
> +++ b/hw/i386/q35-acpi-dsdt.hex.generated
> @@ -8,7 +8,7 @@ static unsigned char Q35AcpiDsdtAmlCode[] = {
>  0x0,
>  0x0,
>  0x1,
> -0xb7,
> +0xac,
>  0x42,
>  0x58,
>  0x50,
> @@ -31,9 +31,9 @@ static unsigned char Q35AcpiDsdtAmlCode[] = {
>  0x4e,
>  0x54,
>  0x4c,
> -0x15,
> -0x11,
> -0x13,
> +0x28,
> +0x8,
> +0x14,
>  0x20,
>  0x10,
>  0x49,
> diff --git a/hw/i386/ssdt-misc.dsl b/hw/i386/ssdt-misc.dsl
> index 0fd4480..6ae09fb 100644
> --- a/hw/i386/ssdt-misc.dsl
> +++ b/hw/i386/ssdt-misc.dsl
> @@ -13,6 +13,7 @@
>   * with this program; if not, see <http://www.gnu.org/licenses/>.
>   */
>  #include "hw/acpi/pc-hotplug.h"
> +#include "hw/misc/vmgenid.h"
>  
>  ACPI_EXTRACT_ALL_CODE ssdp_misc_aml
>  
> @@ -118,6 +119,41 @@ DefinitionBlock ("ssdt-misc.aml", "SSDT", 0x01, "BXPC", "BXSSDTSUSP", 0x1)
>          }
>      }
>  
> +    Scope(\_SB) {
> +        Device(VMGI) {
> +            Name(_HID, "QEMU0002")
> +            Name(_CID, "VM_Gen_Counter")
> +            Name(_DDN, "VM_Gen_Counter")
> +
> +            ACPI_EXTRACT_NAME_DWORD_CONST ssdt_acpi_vm_gid_addr
> +            Name(VGIA, VMGENID_BASE_ADDRESS)
> +
> +            OperationRegion(VGID, SystemMemory,
> +                VMGENID_BASE_ADDRESS, VMGENID_BASE_ADDR_LEN)
> +
> +            Name(_CRS, ResourceTemplate() {
> +                Memory32Fixed(ReadOnly,
> +                    VMGENID_BASE_ADDRESS, VMGENID_BASE_ADDR_LEN)
> +            })
> +
> +            Method(_STA, 0, NotSerialized) {
> +                Store(VGIA, Local0)
> +                If (LEqual(Local0, Zero)) {
> +                    Return (0x00)
> +                } Else {
> +                    Return (0x0F)
> +                }
> +            }
> +
> +            Method(ADDR, 0, Serialized) {
> +                Store(Package(2) { }, Local0)
> +                Store(VGIA, Index(Local0, 0))
> +                Store(0x0000, Index(Local0, 1))
> +                return (Local0)
> +            }
> +        }
> +    }
> +
>      External(MEMORY_SLOT_NOTIFY_METHOD, MethodObj)
>      Scope(\_SB.PCI0) {
>          Device(MEMORY_HOTPLUG_DEVICE) {
> diff --git a/hw/i386/ssdt-misc.hex.generated b/hw/i386/ssdt-misc.hex.generated
> index ba4268a..f0b7a27 100644
> --- a/hw/i386/ssdt-misc.hex.generated
> +++ b/hw/i386/ssdt-misc.hex.generated
> @@ -26,7 +26,7 @@ static unsigned char ssdp_misc_aml[] = {
>  0x0,
>  0x0,
>  0x1,
> -0x8b,
> +0x80,
>  0x42,
>  0x58,
>  0x50,
> @@ -49,9 +49,9 @@ static unsigned char ssdp_misc_aml[] = {
>  0x4e,
>  0x54,
>  0x4c,
> -0x15,
> -0x11,
> -0x13,
> +0x28,
> +0x8,
> +0x14,
>  0x20,
>  0x10,
>  0x42,
> diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
> index 530b074..3a012fa 100644
> --- a/hw/isa/lpc_ich9.c
> +++ b/hw/isa/lpc_ich9.c
> @@ -678,6 +678,7 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data)
>      hc->plug = ich9_device_plug_cb;
>      hc->unplug_request = ich9_device_unplug_request_cb;
>      adevc->ospm_status = ich9_pm_ospm_status;
> +    adevc->vm_generation_id_changed = ich9_vm_generation_id_changed;
>  }
>  
>  static const TypeInfo ich9_lpc_info = {
> diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
> index 979e532..c18b800 100644
> --- a/hw/misc/Makefile.objs
> +++ b/hw/misc/Makefile.objs
> @@ -41,3 +41,4 @@ obj-$(CONFIG_SLAVIO) += slavio_misc.o
>  obj-$(CONFIG_ZYNQ) += zynq_slcr.o
>  
>  obj-$(CONFIG_PVPANIC) += pvpanic.o
> +obj-$(CONFIG_VMGENID) += vmgenid.o
> diff --git a/hw/misc/vmgenid.c b/hw/misc/vmgenid.c
> new file mode 100644
> index 0000000..01aacd4
> --- /dev/null
> +++ b/hw/misc/vmgenid.c
> @@ -0,0 +1,116 @@
> +/*
> + *  Virtual Machine Generation ID Device
> + *
> + *  Copyright (C) 2014 Red Hat Inc.
> + *
> + *  Authors: Gal Hammer <ghammer@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/sysbus.h"
> +#include "hw/misc/vmgenid.h"
> +#include "hw/acpi/acpi_dev_interface.h"
> +
> +#define PROPERTY_UUID "uuid"
> +
> +#define VMGENID(obj) OBJECT_CHECK(VmGenIdState, (obj), VMGENID_DEVICE)
> +
> +typedef struct VmGenIdState {
> +    SysBusDevice parent_obj;
> +    MemoryRegion iomem;
> +    uint8_t guid[16];
> +    bool guid_set;
> +} VmGenIdState;
> +
> +bool vm_generation_id_set(void)
> +{
> +    Object *obj = object_resolve_path_type("", VMGENID_DEVICE, NULL);
> +    VmGenIdState *s = VMGENID(obj);
> +
> +    if (!obj) {
> +        return false;
> +    }
> +    return s->guid_set;
> +}
> +
> +static uint64_t vmgenid_ram_read(void *opaque, hwaddr addr,
> +                                 unsigned size)
> +{
> +    VmGenIdState *s = VMGENID(opaque);
> +    uint64_t value;
> +
> +    memcpy(&value, s->guid + addr, size);
> +    return value;
> +}
> +
> +static const MemoryRegionOps vmgenid_ram_ops = {
> +    .read = vmgenid_ram_read,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static void vmgenid_set_uuid(Object *obj, const char *value, Error **errp)
> +{
> +    VmGenIdState *s = VMGENID(obj);
> +    Object *acpi_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. */
> +    if (!first_set) {
> +        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_init(Object *obj)
> +{
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> +    VmGenIdState *s = VMGENID(obj);
> +
> +    memory_region_init_io(&s->iomem, obj, &vmgenid_ram_ops, s, "vgid", 16);
> +    sysbus_init_mmio(sbd, &s->iomem);

This will cause an exit on each access to the GID.
I'm guessing this is too expensive.


> +
> +    object_property_add_str(obj, PROPERTY_UUID, NULL, vmgenid_set_uuid, NULL);
> +}
> +
> +static void vmgenid_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
> +}
> +
> +static const TypeInfo vmgenid_device_info = {
> +    .name          = VMGENID_DEVICE,
> +    .parent        = TYPE_SYS_BUS_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/acpi/acpi.h b/include/hw/acpi/acpi.h
> index 1f678b4..9373b4d 100644
> --- a/include/hw/acpi/acpi.h
> +++ b/include/hw/acpi/acpi.h
> @@ -185,4 +185,6 @@ unsigned acpi_table_len(void *current);
>  void acpi_table_add(const QemuOpts *opts, Error **errp);
>  void acpi_table_add_builtin(const QemuOpts *opts, Error **errp);
>  
> +void acpi_vm_generation_id_changed(ACPIREGS *acpi_regs, qemu_irq irq);
> +
>  #endif /* !QEMU_HW_ACPI_H */
> diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h
> index f245f8d..757ce60 100644
> --- a/include/hw/acpi/acpi_dev_interface.h
> +++ b/include/hw/acpi/acpi_dev_interface.h
> @@ -28,6 +28,9 @@ typedef struct AcpiDeviceIf {
>   * ospm_status: returns status of ACPI device objects, reported
>   *              via _OST method if device supports it.
>   *
> + * vm_generation_id_changed: notify the guest that it generation
> + *                           id was changed.
> + *
>   * Interface is designed for providing unified interface
>   * to generic ACPI functionality that could be used without
>   * knowledge about internals of actual device that implements
> @@ -39,5 +42,6 @@ typedef struct AcpiDeviceIfClass {
>  
>      /* <public> */
>      void (*ospm_status)(AcpiDeviceIf *adev, ACPIOSTInfoList ***list);
> +    void (*vm_generation_id_changed)(AcpiDeviceIf *adev);
>  } AcpiDeviceIfClass;
>  #endif
> diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h
> index 7e42448..3b4eed5 100644
> --- a/include/hw/acpi/ich9.h
> +++ b/include/hw/acpi/ich9.h
> @@ -62,4 +62,6 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp);
>  void ich9_pm_device_plug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, Error **errp);
>  
>  void ich9_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list);
> +
> +void ich9_vm_generation_id_changed(AcpiDeviceIf *adev);
>  #endif /* HW_ACPI_ICH9_H */
> diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
> index 1c2602e..87912b2 100644
> --- a/include/hw/i386/pc.h
> +++ b/include/hw/i386/pc.h
> @@ -290,6 +290,9 @@ void pc_system_firmware_init(MemoryRegion *rom_memory,
>  /* pvpanic.c */
>  uint16_t pvpanic_port(void);
>  
> +/* vmgenid.c */
> +bool vm_generation_id_set(void);
> +
>  /* e820 types */
>  #define E820_RAM        1
>  #define E820_RESERVED   2
> diff --git a/include/hw/misc/vmgenid.h b/include/hw/misc/vmgenid.h
> new file mode 100644
> index 0000000..3d44421
> --- /dev/null
> +++ b/include/hw/misc/vmgenid.h
> @@ -0,0 +1,21 @@
> +/*
> + *  Virtual Machine Generation ID Device
> + *
> + *  Copyright (C) 2014 Red Hat Inc.
> + *
> + *  Authors: Gal Hammer <ghammer@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
> +
> +#define VMGENID_DEVICE "vmgenid"
> +
> +#define VMGENID_BASE_ADDRESS    0xfedf0000
> +#define VMGENID_BASE_ADDR_LEN   16
> +
> +#endif
> -- 
> 1.9.3
Gal Hammer Oct. 20, 2014, 5:57 a.m. UTC | #2
On 19/10/2014 18:14, Michael S. Tsirkin wrote:
> On Sun, Oct 19, 2014 at 04:43:07PM +0300, Gal Hammer wrote:
>> Based on Microsoft's sepecifications (paper can be dowloaded 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 <ghammer@redhat.com>
>>
>> ---
>>   default-configs/i386-softmmu.mak     |   1 +
>>   default-configs/x86_64-softmmu.mak   |   1 +
>>   hw/acpi/core.c                       |   8 +++
>>   hw/acpi/ich9.c                       |   8 +++
>>   hw/acpi/piix4.c                      |   8 +++
>>   hw/i386/acpi-build.c                 |   8 +++
>>   hw/i386/acpi-dsdt.dsl                |   4 +-
>>   hw/i386/acpi-dsdt.hex.generated      |   6 +-
>>   hw/i386/pc.c                         |   8 +++
>>   hw/i386/q35-acpi-dsdt.dsl            |   5 +-
>>   hw/i386/q35-acpi-dsdt.hex.generated  |   8 +--
>>   hw/i386/ssdt-misc.dsl                |  36 +++++++++++
>>   hw/i386/ssdt-misc.hex.generated      |   8 +--
>>   hw/isa/lpc_ich9.c                    |   1 +
>>   hw/misc/Makefile.objs                |   1 +
>>   hw/misc/vmgenid.c                    | 116 +++++++++++++++++++++++++++++++++++
>>   include/hw/acpi/acpi.h               |   2 +
>>   include/hw/acpi/acpi_dev_interface.h |   4 ++
>>   include/hw/acpi/ich9.h               |   2 +
>>   include/hw/i386/pc.h                 |   3 +
>>   include/hw/misc/vmgenid.h            |  21 +++++++
>>   21 files changed, 246 insertions(+), 13 deletions(-)
>>   create mode 100644 hw/misc/vmgenid.c
>>   create mode 100644 include/hw/misc/vmgenid.h
>
> Please document the host/guest API.
> It seems that you are using a hard-coded hardware address,
> and using up a GPE.

I'll add a document file which describes the device's implementation.

>
>
>>
>> diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
>> index 8e08841..bd33c75 100644
>> --- a/default-configs/i386-softmmu.mak
>> +++ b/default-configs/i386-softmmu.mak
>> @@ -45,3 +45,4 @@ CONFIG_IOAPIC=y
>>   CONFIG_ICC_BUS=y
>>   CONFIG_PVPANIC=y
>>   CONFIG_MEM_HOTPLUG=y
>> +CONFIG_VMGENID=y
>> diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
>> index 66557ac..006fc7c 100644
>> --- a/default-configs/x86_64-softmmu.mak
>> +++ b/default-configs/x86_64-softmmu.mak
>> @@ -45,3 +45,4 @@ CONFIG_IOAPIC=y
>>   CONFIG_ICC_BUS=y
>>   CONFIG_PVPANIC=y
>>   CONFIG_MEM_HOTPLUG=y
>> +CONFIG_VMGENID=y
>> diff --git a/hw/acpi/core.c b/hw/acpi/core.c
>> index a7368fb..a01c980 100644
>> --- a/hw/acpi/core.c
>> +++ b/hw/acpi/core.c
>> @@ -28,6 +28,8 @@
>>   #include "qapi-visit.h"
>>   #include "qapi-event.h"
>>
>> +#define ACPI_VM_GENERATION_ID_CHANGED_STATUS 1
>> +
>>   struct acpi_table_header {
>>       uint16_t _length;         /* our length, not actual part of the hdr */
>>                                 /* allows easier parsing for fw_cfg clients */
>> @@ -680,3 +682,9 @@ void acpi_update_sci(ACPIREGS *regs, qemu_irq irq)
>>                          (regs->pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
>>                          !(pm1a_sts & ACPI_BITMASK_TIMER_STATUS));
>>   }
>> +
>> +void acpi_vm_generation_id_changed(ACPIREGS *acpi_regs, qemu_irq irq)
>> +{
>> +    acpi_regs->gpe.sts[0] |= ACPI_VM_GENERATION_ID_CHANGED_STATUS;
>> +    acpi_update_sci(acpi_regs, irq);
>> +}
>> diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
>> index 7b14bbb..5501c0e 100644
>> --- a/hw/acpi/ich9.c
>> +++ b/hw/acpi/ich9.c
>> @@ -316,3 +316,11 @@ void ich9_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list)
>>
>>       acpi_memory_ospm_status(&s->pm.acpi_memory_hotplug, list);
>>   }
>> +
>> +void ich9_vm_generation_id_changed(AcpiDeviceIf *adev)
>> +{
>> +    ICH9LPCState *s = ICH9_LPC_DEVICE(adev);
>> +    ICH9LPCPMRegs *pm = &s->pm;
>> +
>> +    acpi_vm_generation_id_changed(&pm->acpi_regs, pm->irq);
>> +}
>> diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
>> index 0bfa814..ad0ef68 100644
>> --- a/hw/acpi/piix4.c
>> +++ b/hw/acpi/piix4.c
>> @@ -580,6 +580,13 @@ static void piix4_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list)
>>       acpi_memory_ospm_status(&s->acpi_memory_hotplug, list);
>>   }
>>
>> +static void piix4_vm_generation_id_changed(AcpiDeviceIf *adev)
>> +{
>> +    PIIX4PMState *s = PIIX4_PM(adev);
>> +
>> +    acpi_vm_generation_id_changed(&s->ar, s->irq);
>> +}
>> +
>>   static Property piix4_pm_properties[] = {
>>       DEFINE_PROP_UINT32("smb_io_base", PIIX4PMState, smb_io_base, 0),
>>       DEFINE_PROP_UINT8(ACPI_PM_PROP_S3_DISABLED, PIIX4PMState, disable_s3, 0),
>> @@ -617,6 +624,7 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data)
>>       hc->plug = piix4_device_plug_cb;
>>       hc->unplug_request = piix4_device_unplug_request_cb;
>>       adevc->ospm_status = piix4_ospm_status;
>> +    adevc->vm_generation_id_changed = piix4_vm_generation_id_changed;
>>   }
>>
>>   static const TypeInfo piix4_pm_info = {
>> diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
>> index 00be4bb..27d0494 100644
>> --- a/hw/i386/acpi-build.c
>> +++ b/hw/i386/acpi-build.c
>> @@ -42,6 +42,7 @@
>>   #include "hw/acpi/memory_hotplug.h"
>>   #include "sysemu/tpm.h"
>>   #include "hw/acpi/tpm.h"
>> +#include "hw/misc/vmgenid.h"
>>
>>   /* Supported chipsets: */
>>   #include "hw/acpi/piix4.h"
>> @@ -96,6 +97,7 @@ typedef struct AcpiMiscInfo {
>>       const unsigned char *dsdt_code;
>>       unsigned dsdt_size;
>>       uint16_t pvpanic_port;
>> +    bool vm_generation_id_set;
>>   } AcpiMiscInfo;
>>
>>   typedef struct AcpiBuildPciBusHotplugState {
>> @@ -216,6 +218,7 @@ static void acpi_get_misc_info(AcpiMiscInfo *info)
>>       info->has_hpet = hpet_find();
>>       info->has_tpm = tpm_find();
>>       info->pvpanic_port = pvpanic_port();
>> +    info->vm_generation_id_set = vm_generation_id_set();
>>   }
>>
>>   static void acpi_get_pci_info(PcPciInfo *info)
>> @@ -1088,6 +1091,11 @@ build_ssdt(GArray *table_data, GArray *linker,
>>       ACPI_BUILD_SET_LE(ssdt_ptr, sizeof(ssdp_misc_aml),
>>                         ssdt_isa_pest[0], 16, misc->pvpanic_port);
>>
>> +    if (!misc->vm_generation_id_set) {
>> +        ACPI_BUILD_SET_LE(ssdt_ptr, sizeof(ssdp_misc_aml),
>> +                          ssdt_acpi_vm_gid_addr[0], 32, 0);
>> +    }
>> +
>>       ACPI_BUILD_SET_LE(ssdt_ptr, sizeof(ssdp_misc_aml),
>>                         ssdt_mctrl_nr_slots[0], 32, nr_mem);
>>
>> diff --git a/hw/i386/acpi-dsdt.dsl b/hw/i386/acpi-dsdt.dsl
>> index 559f4b6..10b4afb 100644
>> --- a/hw/i386/acpi-dsdt.dsl
>> +++ b/hw/i386/acpi-dsdt.dsl
>> @@ -307,7 +307,9 @@ DefinitionBlock (
>>       Scope(\_GPE) {
>>           Name(_HID, "ACPI0006")
>>
>> -        Method(_L00) {
>> +        External(\_SB.VMGI, DeviceObj)
>> +        Method(_E00) {
>> +            Notify(\_SB.VMGI, 0x80)
>>           }
>>           Method(_E01) {
>>               // PCI hotplug event
>> diff --git a/hw/i386/acpi-dsdt.hex.generated b/hw/i386/acpi-dsdt.hex.generated
>> index a21bf41..3fd06d2 100644
>> --- a/hw/i386/acpi-dsdt.hex.generated
>> +++ b/hw/i386/acpi-dsdt.hex.generated
>> @@ -8,7 +8,7 @@ static unsigned char AcpiDsdtAmlCode[] = {
>>   0x0,
>>   0x0,
>>   0x1,
>> -0x1f,
>> +0x18,
>>   0x42,
>>   0x58,
>>   0x50,
>> @@ -32,8 +32,8 @@ static unsigned char AcpiDsdtAmlCode[] = {
>>   0x54,
>>   0x4c,
>>   0x28,
>> -0x5,
>> -0x10,
>> +0x8,
>> +0x14,
>>   0x20,
>>   0x10,
>>   0x49,
>> diff --git a/hw/i386/pc.c b/hw/i386/pc.c
>> index d045e8b..95ec233 100644
>> --- a/hw/i386/pc.c
>> +++ b/hw/i386/pc.c
>> @@ -59,6 +59,7 @@
>>   #include "hw/pci/pci_host.h"
>>   #include "acpi-build.h"
>>   #include "hw/mem/pc-dimm.h"
>> +#include "hw/misc/vmgenid.h"
>>   #include "trace.h"
>>   #include "qapi/visitor.h"
>>
>> @@ -1361,6 +1362,7 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
>>       int i;
>>       DriveInfo *fd[MAX_FD];
>>       DeviceState *hpet = NULL;
>> +    DeviceState *vmgenid;
>>       int pit_isa_irq = 0;
>>       qemu_irq pit_alt_irq = NULL;
>>       qemu_irq rtc_irq = NULL;
>> @@ -1459,6 +1461,12 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
>>           fd[i] = drive_get(IF_FLOPPY, 0, i);
>>       }
>>       *floppy = fdctrl_init_isa(isa_bus, fd);
>> +
>> +    vmgenid = qdev_try_create(NULL, VMGENID_DEVICE);
>> +    if (vmgenid) {
>> +        qdev_init_nofail(vmgenid);
>> +        sysbus_mmio_map(SYS_BUS_DEVICE(vmgenid), 0, VMGENID_BASE_ADDRESS);
>> +    }
>>   }
>>
>>   void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
>> diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
>> index 054b035..b1d04e2 100644
>> --- a/hw/i386/q35-acpi-dsdt.dsl
>> +++ b/hw/i386/q35-acpi-dsdt.dsl
>> @@ -415,7 +415,10 @@ DefinitionBlock (
>>       Scope(\_GPE) {
>>           Name(_HID, "ACPI0006")
>>
>> -        Method(_L00) {
>> +        External(\_SB.VMGI, DeviceObj)
>> +        Method(_E00)
>> +        {
>> +            Notify(\_SB.VMGI, 0x80)
>>           }
>>           Method(_L01) {
>>           }
>> diff --git a/hw/i386/q35-acpi-dsdt.hex.generated b/hw/i386/q35-acpi-dsdt.hex.generated
>> index c9eb4ac..866e648 100644
>> --- a/hw/i386/q35-acpi-dsdt.hex.generated
>> +++ b/hw/i386/q35-acpi-dsdt.hex.generated
>> @@ -8,7 +8,7 @@ static unsigned char Q35AcpiDsdtAmlCode[] = {
>>   0x0,
>>   0x0,
>>   0x1,
>> -0xb7,
>> +0xac,
>>   0x42,
>>   0x58,
>>   0x50,
>> @@ -31,9 +31,9 @@ static unsigned char Q35AcpiDsdtAmlCode[] = {
>>   0x4e,
>>   0x54,
>>   0x4c,
>> -0x15,
>> -0x11,
>> -0x13,
>> +0x28,
>> +0x8,
>> +0x14,
>>   0x20,
>>   0x10,
>>   0x49,
>> diff --git a/hw/i386/ssdt-misc.dsl b/hw/i386/ssdt-misc.dsl
>> index 0fd4480..6ae09fb 100644
>> --- a/hw/i386/ssdt-misc.dsl
>> +++ b/hw/i386/ssdt-misc.dsl
>> @@ -13,6 +13,7 @@
>>    * with this program; if not, see <http://www.gnu.org/licenses/>.
>>    */
>>   #include "hw/acpi/pc-hotplug.h"
>> +#include "hw/misc/vmgenid.h"
>>
>>   ACPI_EXTRACT_ALL_CODE ssdp_misc_aml
>>
>> @@ -118,6 +119,41 @@ DefinitionBlock ("ssdt-misc.aml", "SSDT", 0x01, "BXPC", "BXSSDTSUSP", 0x1)
>>           }
>>       }
>>
>> +    Scope(\_SB) {
>> +        Device(VMGI) {
>> +            Name(_HID, "QEMU0002")
>> +            Name(_CID, "VM_Gen_Counter")
>> +            Name(_DDN, "VM_Gen_Counter")
>> +
>> +            ACPI_EXTRACT_NAME_DWORD_CONST ssdt_acpi_vm_gid_addr
>> +            Name(VGIA, VMGENID_BASE_ADDRESS)
>> +
>> +            OperationRegion(VGID, SystemMemory,
>> +                VMGENID_BASE_ADDRESS, VMGENID_BASE_ADDR_LEN)
>> +
>> +            Name(_CRS, ResourceTemplate() {
>> +                Memory32Fixed(ReadOnly,
>> +                    VMGENID_BASE_ADDRESS, VMGENID_BASE_ADDR_LEN)
>> +            })
>> +
>> +            Method(_STA, 0, NotSerialized) {
>> +                Store(VGIA, Local0)
>> +                If (LEqual(Local0, Zero)) {
>> +                    Return (0x00)
>> +                } Else {
>> +                    Return (0x0F)
>> +                }
>> +            }
>> +
>> +            Method(ADDR, 0, Serialized) {
>> +                Store(Package(2) { }, Local0)
>> +                Store(VGIA, Index(Local0, 0))
>> +                Store(0x0000, Index(Local0, 1))
>> +                return (Local0)
>> +            }
>> +        }
>> +    }
>> +
>>       External(MEMORY_SLOT_NOTIFY_METHOD, MethodObj)
>>       Scope(\_SB.PCI0) {
>>           Device(MEMORY_HOTPLUG_DEVICE) {
>> diff --git a/hw/i386/ssdt-misc.hex.generated b/hw/i386/ssdt-misc.hex.generated
>> index ba4268a..f0b7a27 100644
>> --- a/hw/i386/ssdt-misc.hex.generated
>> +++ b/hw/i386/ssdt-misc.hex.generated
>> @@ -26,7 +26,7 @@ static unsigned char ssdp_misc_aml[] = {
>>   0x0,
>>   0x0,
>>   0x1,
>> -0x8b,
>> +0x80,
>>   0x42,
>>   0x58,
>>   0x50,
>> @@ -49,9 +49,9 @@ static unsigned char ssdp_misc_aml[] = {
>>   0x4e,
>>   0x54,
>>   0x4c,
>> -0x15,
>> -0x11,
>> -0x13,
>> +0x28,
>> +0x8,
>> +0x14,
>>   0x20,
>>   0x10,
>>   0x42,
>> diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
>> index 530b074..3a012fa 100644
>> --- a/hw/isa/lpc_ich9.c
>> +++ b/hw/isa/lpc_ich9.c
>> @@ -678,6 +678,7 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data)
>>       hc->plug = ich9_device_plug_cb;
>>       hc->unplug_request = ich9_device_unplug_request_cb;
>>       adevc->ospm_status = ich9_pm_ospm_status;
>> +    adevc->vm_generation_id_changed = ich9_vm_generation_id_changed;
>>   }
>>
>>   static const TypeInfo ich9_lpc_info = {
>> diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
>> index 979e532..c18b800 100644
>> --- a/hw/misc/Makefile.objs
>> +++ b/hw/misc/Makefile.objs
>> @@ -41,3 +41,4 @@ obj-$(CONFIG_SLAVIO) += slavio_misc.o
>>   obj-$(CONFIG_ZYNQ) += zynq_slcr.o
>>
>>   obj-$(CONFIG_PVPANIC) += pvpanic.o
>> +obj-$(CONFIG_VMGENID) += vmgenid.o
>> diff --git a/hw/misc/vmgenid.c b/hw/misc/vmgenid.c
>> new file mode 100644
>> index 0000000..01aacd4
>> --- /dev/null
>> +++ b/hw/misc/vmgenid.c
>> @@ -0,0 +1,116 @@
>> +/*
>> + *  Virtual Machine Generation ID Device
>> + *
>> + *  Copyright (C) 2014 Red Hat Inc.
>> + *
>> + *  Authors: Gal Hammer <ghammer@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/sysbus.h"
>> +#include "hw/misc/vmgenid.h"
>> +#include "hw/acpi/acpi_dev_interface.h"
>> +
>> +#define PROPERTY_UUID "uuid"
>> +
>> +#define VMGENID(obj) OBJECT_CHECK(VmGenIdState, (obj), VMGENID_DEVICE)
>> +
>> +typedef struct VmGenIdState {
>> +    SysBusDevice parent_obj;
>> +    MemoryRegion iomem;
>> +    uint8_t guid[16];
>> +    bool guid_set;
>> +} VmGenIdState;
>> +
>> +bool vm_generation_id_set(void)
>> +{
>> +    Object *obj = object_resolve_path_type("", VMGENID_DEVICE, NULL);
>> +    VmGenIdState *s = VMGENID(obj);
>> +
>> +    if (!obj) {
>> +        return false;
>> +    }
>> +    return s->guid_set;
>> +}
>> +
>> +static uint64_t vmgenid_ram_read(void *opaque, hwaddr addr,
>> +                                 unsigned size)
>> +{
>> +    VmGenIdState *s = VMGENID(opaque);
>> +    uint64_t value;
>> +
>> +    memcpy(&value, s->guid + addr, size);
>> +    return value;
>> +}
>> +
>> +static const MemoryRegionOps vmgenid_ram_ops = {
>> +    .read = vmgenid_ram_read,
>> +    .valid = {
>> +        .min_access_size = 4,
>> +        .max_access_size = 4,
>> +    },
>> +    .endianness = DEVICE_NATIVE_ENDIAN,
>> +};
>> +
>> +static void vmgenid_set_uuid(Object *obj, const char *value, Error **errp)
>> +{
>> +    VmGenIdState *s = VMGENID(obj);
>> +    Object *acpi_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. */
>> +    if (!first_set) {
>> +        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_init(Object *obj)
>> +{
>> +    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
>> +    VmGenIdState *s = VMGENID(obj);
>> +
>> +    memory_region_init_io(&s->iomem, obj, &vmgenid_ram_ops, s, "vgid", 16);
>> +    sysbus_init_mmio(sbd, &s->iomem);
>
> This will cause an exit on each access to the GID.
> I'm guessing this is too expensive.

Yes, it does cause an exit, but I don't think it is critical because the 
GID is rarely read by the guest OS. As far as I can tell it is only 
accessed on reboot and when the GID is changed.

Do you prefer that I'll use the cpu_physical_memory_write function? Or 
do you have a diffrent idea on how to implement it?

>
>
>> +
>> +    object_property_add_str(obj, PROPERTY_UUID, NULL, vmgenid_set_uuid, NULL);
>> +}
>> +
>> +static void vmgenid_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +
>> +    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
>> +}
>> +
>> +static const TypeInfo vmgenid_device_info = {
>> +    .name          = VMGENID_DEVICE,
>> +    .parent        = TYPE_SYS_BUS_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/acpi/acpi.h b/include/hw/acpi/acpi.h
>> index 1f678b4..9373b4d 100644
>> --- a/include/hw/acpi/acpi.h
>> +++ b/include/hw/acpi/acpi.h
>> @@ -185,4 +185,6 @@ unsigned acpi_table_len(void *current);
>>   void acpi_table_add(const QemuOpts *opts, Error **errp);
>>   void acpi_table_add_builtin(const QemuOpts *opts, Error **errp);
>>
>> +void acpi_vm_generation_id_changed(ACPIREGS *acpi_regs, qemu_irq irq);
>> +
>>   #endif /* !QEMU_HW_ACPI_H */
>> diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h
>> index f245f8d..757ce60 100644
>> --- a/include/hw/acpi/acpi_dev_interface.h
>> +++ b/include/hw/acpi/acpi_dev_interface.h
>> @@ -28,6 +28,9 @@ typedef struct AcpiDeviceIf {
>>    * ospm_status: returns status of ACPI device objects, reported
>>    *              via _OST method if device supports it.
>>    *
>> + * vm_generation_id_changed: notify the guest that it generation
>> + *                           id was changed.
>> + *
>>    * Interface is designed for providing unified interface
>>    * to generic ACPI functionality that could be used without
>>    * knowledge about internals of actual device that implements
>> @@ -39,5 +42,6 @@ typedef struct AcpiDeviceIfClass {
>>
>>       /* <public> */
>>       void (*ospm_status)(AcpiDeviceIf *adev, ACPIOSTInfoList ***list);
>> +    void (*vm_generation_id_changed)(AcpiDeviceIf *adev);
>>   } AcpiDeviceIfClass;
>>   #endif
>> diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h
>> index 7e42448..3b4eed5 100644
>> --- a/include/hw/acpi/ich9.h
>> +++ b/include/hw/acpi/ich9.h
>> @@ -62,4 +62,6 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp);
>>   void ich9_pm_device_plug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, Error **errp);
>>
>>   void ich9_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list);
>> +
>> +void ich9_vm_generation_id_changed(AcpiDeviceIf *adev);
>>   #endif /* HW_ACPI_ICH9_H */
>> diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
>> index 1c2602e..87912b2 100644
>> --- a/include/hw/i386/pc.h
>> +++ b/include/hw/i386/pc.h
>> @@ -290,6 +290,9 @@ void pc_system_firmware_init(MemoryRegion *rom_memory,
>>   /* pvpanic.c */
>>   uint16_t pvpanic_port(void);
>>
>> +/* vmgenid.c */
>> +bool vm_generation_id_set(void);
>> +
>>   /* e820 types */
>>   #define E820_RAM        1
>>   #define E820_RESERVED   2
>> diff --git a/include/hw/misc/vmgenid.h b/include/hw/misc/vmgenid.h
>> new file mode 100644
>> index 0000000..3d44421
>> --- /dev/null
>> +++ b/include/hw/misc/vmgenid.h
>> @@ -0,0 +1,21 @@
>> +/*
>> + *  Virtual Machine Generation ID Device
>> + *
>> + *  Copyright (C) 2014 Red Hat Inc.
>> + *
>> + *  Authors: Gal Hammer <ghammer@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
>> +
>> +#define VMGENID_DEVICE "vmgenid"
>> +
>> +#define VMGENID_BASE_ADDRESS    0xfedf0000
>> +#define VMGENID_BASE_ADDR_LEN   16
>> +
>> +#endif
>> --
>> 1.9.3
Michael S. Tsirkin Oct. 20, 2014, 7:04 a.m. UTC | #3
On Mon, Oct 20, 2014 at 08:57:05AM +0300, Gal Hammer wrote:
> On 19/10/2014 18:14, Michael S. Tsirkin wrote:
> >On Sun, Oct 19, 2014 at 04:43:07PM +0300, Gal Hammer wrote:
> >>Based on Microsoft's sepecifications (paper can be dowloaded 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 <ghammer@redhat.com>
> >>
> >>---
> >>  default-configs/i386-softmmu.mak     |   1 +
> >>  default-configs/x86_64-softmmu.mak   |   1 +
> >>  hw/acpi/core.c                       |   8 +++
> >>  hw/acpi/ich9.c                       |   8 +++
> >>  hw/acpi/piix4.c                      |   8 +++
> >>  hw/i386/acpi-build.c                 |   8 +++
> >>  hw/i386/acpi-dsdt.dsl                |   4 +-
> >>  hw/i386/acpi-dsdt.hex.generated      |   6 +-
> >>  hw/i386/pc.c                         |   8 +++
> >>  hw/i386/q35-acpi-dsdt.dsl            |   5 +-
> >>  hw/i386/q35-acpi-dsdt.hex.generated  |   8 +--
> >>  hw/i386/ssdt-misc.dsl                |  36 +++++++++++
> >>  hw/i386/ssdt-misc.hex.generated      |   8 +--
> >>  hw/isa/lpc_ich9.c                    |   1 +
> >>  hw/misc/Makefile.objs                |   1 +
> >>  hw/misc/vmgenid.c                    | 116 +++++++++++++++++++++++++++++++++++
> >>  include/hw/acpi/acpi.h               |   2 +
> >>  include/hw/acpi/acpi_dev_interface.h |   4 ++
> >>  include/hw/acpi/ich9.h               |   2 +
> >>  include/hw/i386/pc.h                 |   3 +
> >>  include/hw/misc/vmgenid.h            |  21 +++++++
> >>  21 files changed, 246 insertions(+), 13 deletions(-)
> >>  create mode 100644 hw/misc/vmgenid.c
> >>  create mode 100644 include/hw/misc/vmgenid.h
> >
> >Please document the host/guest API.
> >It seems that you are using a hard-coded hardware address,
> >and using up a GPE.
> 
> I'll add a document file which describes the device's implementation.
> 
> >
> >
> >>
> >>diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
> >>index 8e08841..bd33c75 100644
> >>--- a/default-configs/i386-softmmu.mak
> >>+++ b/default-configs/i386-softmmu.mak
> >>@@ -45,3 +45,4 @@ CONFIG_IOAPIC=y
> >>  CONFIG_ICC_BUS=y
> >>  CONFIG_PVPANIC=y
> >>  CONFIG_MEM_HOTPLUG=y
> >>+CONFIG_VMGENID=y
> >>diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
> >>index 66557ac..006fc7c 100644
> >>--- a/default-configs/x86_64-softmmu.mak
> >>+++ b/default-configs/x86_64-softmmu.mak
> >>@@ -45,3 +45,4 @@ CONFIG_IOAPIC=y
> >>  CONFIG_ICC_BUS=y
> >>  CONFIG_PVPANIC=y
> >>  CONFIG_MEM_HOTPLUG=y
> >>+CONFIG_VMGENID=y
> >>diff --git a/hw/acpi/core.c b/hw/acpi/core.c
> >>index a7368fb..a01c980 100644
> >>--- a/hw/acpi/core.c
> >>+++ b/hw/acpi/core.c
> >>@@ -28,6 +28,8 @@
> >>  #include "qapi-visit.h"
> >>  #include "qapi-event.h"
> >>
> >>+#define ACPI_VM_GENERATION_ID_CHANGED_STATUS 1
> >>+
> >>  struct acpi_table_header {
> >>      uint16_t _length;         /* our length, not actual part of the hdr */
> >>                                /* allows easier parsing for fw_cfg clients */
> >>@@ -680,3 +682,9 @@ void acpi_update_sci(ACPIREGS *regs, qemu_irq irq)
> >>                         (regs->pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
> >>                         !(pm1a_sts & ACPI_BITMASK_TIMER_STATUS));
> >>  }
> >>+
> >>+void acpi_vm_generation_id_changed(ACPIREGS *acpi_regs, qemu_irq irq)
> >>+{
> >>+    acpi_regs->gpe.sts[0] |= ACPI_VM_GENERATION_ID_CHANGED_STATUS;
> >>+    acpi_update_sci(acpi_regs, irq);
> >>+}
> >>diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
> >>index 7b14bbb..5501c0e 100644
> >>--- a/hw/acpi/ich9.c
> >>+++ b/hw/acpi/ich9.c
> >>@@ -316,3 +316,11 @@ void ich9_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list)
> >>
> >>      acpi_memory_ospm_status(&s->pm.acpi_memory_hotplug, list);
> >>  }
> >>+
> >>+void ich9_vm_generation_id_changed(AcpiDeviceIf *adev)
> >>+{
> >>+    ICH9LPCState *s = ICH9_LPC_DEVICE(adev);
> >>+    ICH9LPCPMRegs *pm = &s->pm;
> >>+
> >>+    acpi_vm_generation_id_changed(&pm->acpi_regs, pm->irq);
> >>+}
> >>diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
> >>index 0bfa814..ad0ef68 100644
> >>--- a/hw/acpi/piix4.c
> >>+++ b/hw/acpi/piix4.c
> >>@@ -580,6 +580,13 @@ static void piix4_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list)
> >>      acpi_memory_ospm_status(&s->acpi_memory_hotplug, list);
> >>  }
> >>
> >>+static void piix4_vm_generation_id_changed(AcpiDeviceIf *adev)
> >>+{
> >>+    PIIX4PMState *s = PIIX4_PM(adev);
> >>+
> >>+    acpi_vm_generation_id_changed(&s->ar, s->irq);
> >>+}
> >>+
> >>  static Property piix4_pm_properties[] = {
> >>      DEFINE_PROP_UINT32("smb_io_base", PIIX4PMState, smb_io_base, 0),
> >>      DEFINE_PROP_UINT8(ACPI_PM_PROP_S3_DISABLED, PIIX4PMState, disable_s3, 0),
> >>@@ -617,6 +624,7 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data)
> >>      hc->plug = piix4_device_plug_cb;
> >>      hc->unplug_request = piix4_device_unplug_request_cb;
> >>      adevc->ospm_status = piix4_ospm_status;
> >>+    adevc->vm_generation_id_changed = piix4_vm_generation_id_changed;
> >>  }
> >>
> >>  static const TypeInfo piix4_pm_info = {
> >>diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
> >>index 00be4bb..27d0494 100644
> >>--- a/hw/i386/acpi-build.c
> >>+++ b/hw/i386/acpi-build.c
> >>@@ -42,6 +42,7 @@
> >>  #include "hw/acpi/memory_hotplug.h"
> >>  #include "sysemu/tpm.h"
> >>  #include "hw/acpi/tpm.h"
> >>+#include "hw/misc/vmgenid.h"
> >>
> >>  /* Supported chipsets: */
> >>  #include "hw/acpi/piix4.h"
> >>@@ -96,6 +97,7 @@ typedef struct AcpiMiscInfo {
> >>      const unsigned char *dsdt_code;
> >>      unsigned dsdt_size;
> >>      uint16_t pvpanic_port;
> >>+    bool vm_generation_id_set;
> >>  } AcpiMiscInfo;
> >>
> >>  typedef struct AcpiBuildPciBusHotplugState {
> >>@@ -216,6 +218,7 @@ static void acpi_get_misc_info(AcpiMiscInfo *info)
> >>      info->has_hpet = hpet_find();
> >>      info->has_tpm = tpm_find();
> >>      info->pvpanic_port = pvpanic_port();
> >>+    info->vm_generation_id_set = vm_generation_id_set();
> >>  }
> >>
> >>  static void acpi_get_pci_info(PcPciInfo *info)
> >>@@ -1088,6 +1091,11 @@ build_ssdt(GArray *table_data, GArray *linker,
> >>      ACPI_BUILD_SET_LE(ssdt_ptr, sizeof(ssdp_misc_aml),
> >>                        ssdt_isa_pest[0], 16, misc->pvpanic_port);
> >>
> >>+    if (!misc->vm_generation_id_set) {
> >>+        ACPI_BUILD_SET_LE(ssdt_ptr, sizeof(ssdp_misc_aml),
> >>+                          ssdt_acpi_vm_gid_addr[0], 32, 0);
> >>+    }
> >>+
> >>      ACPI_BUILD_SET_LE(ssdt_ptr, sizeof(ssdp_misc_aml),
> >>                        ssdt_mctrl_nr_slots[0], 32, nr_mem);
> >>
> >>diff --git a/hw/i386/acpi-dsdt.dsl b/hw/i386/acpi-dsdt.dsl
> >>index 559f4b6..10b4afb 100644
> >>--- a/hw/i386/acpi-dsdt.dsl
> >>+++ b/hw/i386/acpi-dsdt.dsl
> >>@@ -307,7 +307,9 @@ DefinitionBlock (
> >>      Scope(\_GPE) {
> >>          Name(_HID, "ACPI0006")
> >>
> >>-        Method(_L00) {
> >>+        External(\_SB.VMGI, DeviceObj)
> >>+        Method(_E00) {
> >>+            Notify(\_SB.VMGI, 0x80)
> >>          }
> >>          Method(_E01) {
> >>              // PCI hotplug event
> >>diff --git a/hw/i386/acpi-dsdt.hex.generated b/hw/i386/acpi-dsdt.hex.generated
> >>index a21bf41..3fd06d2 100644
> >>--- a/hw/i386/acpi-dsdt.hex.generated
> >>+++ b/hw/i386/acpi-dsdt.hex.generated
> >>@@ -8,7 +8,7 @@ static unsigned char AcpiDsdtAmlCode[] = {
> >>  0x0,
> >>  0x0,
> >>  0x1,
> >>-0x1f,
> >>+0x18,
> >>  0x42,
> >>  0x58,
> >>  0x50,
> >>@@ -32,8 +32,8 @@ static unsigned char AcpiDsdtAmlCode[] = {
> >>  0x54,
> >>  0x4c,
> >>  0x28,
> >>-0x5,
> >>-0x10,
> >>+0x8,
> >>+0x14,
> >>  0x20,
> >>  0x10,
> >>  0x49,
> >>diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> >>index d045e8b..95ec233 100644
> >>--- a/hw/i386/pc.c
> >>+++ b/hw/i386/pc.c
> >>@@ -59,6 +59,7 @@
> >>  #include "hw/pci/pci_host.h"
> >>  #include "acpi-build.h"
> >>  #include "hw/mem/pc-dimm.h"
> >>+#include "hw/misc/vmgenid.h"
> >>  #include "trace.h"
> >>  #include "qapi/visitor.h"
> >>
> >>@@ -1361,6 +1362,7 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
> >>      int i;
> >>      DriveInfo *fd[MAX_FD];
> >>      DeviceState *hpet = NULL;
> >>+    DeviceState *vmgenid;
> >>      int pit_isa_irq = 0;
> >>      qemu_irq pit_alt_irq = NULL;
> >>      qemu_irq rtc_irq = NULL;
> >>@@ -1459,6 +1461,12 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
> >>          fd[i] = drive_get(IF_FLOPPY, 0, i);
> >>      }
> >>      *floppy = fdctrl_init_isa(isa_bus, fd);
> >>+
> >>+    vmgenid = qdev_try_create(NULL, VMGENID_DEVICE);
> >>+    if (vmgenid) {
> >>+        qdev_init_nofail(vmgenid);
> >>+        sysbus_mmio_map(SYS_BUS_DEVICE(vmgenid), 0, VMGENID_BASE_ADDRESS);
> >>+    }
> >>  }
> >>
> >>  void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
> >>diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
> >>index 054b035..b1d04e2 100644
> >>--- a/hw/i386/q35-acpi-dsdt.dsl
> >>+++ b/hw/i386/q35-acpi-dsdt.dsl
> >>@@ -415,7 +415,10 @@ DefinitionBlock (
> >>      Scope(\_GPE) {
> >>          Name(_HID, "ACPI0006")
> >>
> >>-        Method(_L00) {
> >>+        External(\_SB.VMGI, DeviceObj)
> >>+        Method(_E00)
> >>+        {
> >>+            Notify(\_SB.VMGI, 0x80)
> >>          }
> >>          Method(_L01) {
> >>          }
> >>diff --git a/hw/i386/q35-acpi-dsdt.hex.generated b/hw/i386/q35-acpi-dsdt.hex.generated
> >>index c9eb4ac..866e648 100644
> >>--- a/hw/i386/q35-acpi-dsdt.hex.generated
> >>+++ b/hw/i386/q35-acpi-dsdt.hex.generated
> >>@@ -8,7 +8,7 @@ static unsigned char Q35AcpiDsdtAmlCode[] = {
> >>  0x0,
> >>  0x0,
> >>  0x1,
> >>-0xb7,
> >>+0xac,
> >>  0x42,
> >>  0x58,
> >>  0x50,
> >>@@ -31,9 +31,9 @@ static unsigned char Q35AcpiDsdtAmlCode[] = {
> >>  0x4e,
> >>  0x54,
> >>  0x4c,
> >>-0x15,
> >>-0x11,
> >>-0x13,
> >>+0x28,
> >>+0x8,
> >>+0x14,
> >>  0x20,
> >>  0x10,
> >>  0x49,
> >>diff --git a/hw/i386/ssdt-misc.dsl b/hw/i386/ssdt-misc.dsl
> >>index 0fd4480..6ae09fb 100644
> >>--- a/hw/i386/ssdt-misc.dsl
> >>+++ b/hw/i386/ssdt-misc.dsl
> >>@@ -13,6 +13,7 @@
> >>   * with this program; if not, see <http://www.gnu.org/licenses/>.
> >>   */
> >>  #include "hw/acpi/pc-hotplug.h"
> >>+#include "hw/misc/vmgenid.h"
> >>
> >>  ACPI_EXTRACT_ALL_CODE ssdp_misc_aml
> >>
> >>@@ -118,6 +119,41 @@ DefinitionBlock ("ssdt-misc.aml", "SSDT", 0x01, "BXPC", "BXSSDTSUSP", 0x1)
> >>          }
> >>      }
> >>
> >>+    Scope(\_SB) {
> >>+        Device(VMGI) {
> >>+            Name(_HID, "QEMU0002")
> >>+            Name(_CID, "VM_Gen_Counter")
> >>+            Name(_DDN, "VM_Gen_Counter")
> >>+
> >>+            ACPI_EXTRACT_NAME_DWORD_CONST ssdt_acpi_vm_gid_addr
> >>+            Name(VGIA, VMGENID_BASE_ADDRESS)
> >>+
> >>+            OperationRegion(VGID, SystemMemory,
> >>+                VMGENID_BASE_ADDRESS, VMGENID_BASE_ADDR_LEN)
> >>+
> >>+            Name(_CRS, ResourceTemplate() {
> >>+                Memory32Fixed(ReadOnly,
> >>+                    VMGENID_BASE_ADDRESS, VMGENID_BASE_ADDR_LEN)
> >>+            })
> >>+
> >>+            Method(_STA, 0, NotSerialized) {
> >>+                Store(VGIA, Local0)
> >>+                If (LEqual(Local0, Zero)) {
> >>+                    Return (0x00)
> >>+                } Else {
> >>+                    Return (0x0F)
> >>+                }
> >>+            }
> >>+
> >>+            Method(ADDR, 0, Serialized) {
> >>+                Store(Package(2) { }, Local0)
> >>+                Store(VGIA, Index(Local0, 0))
> >>+                Store(0x0000, Index(Local0, 1))
> >>+                return (Local0)
> >>+            }
> >>+        }
> >>+    }
> >>+
> >>      External(MEMORY_SLOT_NOTIFY_METHOD, MethodObj)
> >>      Scope(\_SB.PCI0) {
> >>          Device(MEMORY_HOTPLUG_DEVICE) {
> >>diff --git a/hw/i386/ssdt-misc.hex.generated b/hw/i386/ssdt-misc.hex.generated
> >>index ba4268a..f0b7a27 100644
> >>--- a/hw/i386/ssdt-misc.hex.generated
> >>+++ b/hw/i386/ssdt-misc.hex.generated
> >>@@ -26,7 +26,7 @@ static unsigned char ssdp_misc_aml[] = {
> >>  0x0,
> >>  0x0,
> >>  0x1,
> >>-0x8b,
> >>+0x80,
> >>  0x42,
> >>  0x58,
> >>  0x50,
> >>@@ -49,9 +49,9 @@ static unsigned char ssdp_misc_aml[] = {
> >>  0x4e,
> >>  0x54,
> >>  0x4c,
> >>-0x15,
> >>-0x11,
> >>-0x13,
> >>+0x28,
> >>+0x8,
> >>+0x14,
> >>  0x20,
> >>  0x10,
> >>  0x42,
> >>diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
> >>index 530b074..3a012fa 100644
> >>--- a/hw/isa/lpc_ich9.c
> >>+++ b/hw/isa/lpc_ich9.c
> >>@@ -678,6 +678,7 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data)
> >>      hc->plug = ich9_device_plug_cb;
> >>      hc->unplug_request = ich9_device_unplug_request_cb;
> >>      adevc->ospm_status = ich9_pm_ospm_status;
> >>+    adevc->vm_generation_id_changed = ich9_vm_generation_id_changed;
> >>  }
> >>
> >>  static const TypeInfo ich9_lpc_info = {
> >>diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
> >>index 979e532..c18b800 100644
> >>--- a/hw/misc/Makefile.objs
> >>+++ b/hw/misc/Makefile.objs
> >>@@ -41,3 +41,4 @@ obj-$(CONFIG_SLAVIO) += slavio_misc.o
> >>  obj-$(CONFIG_ZYNQ) += zynq_slcr.o
> >>
> >>  obj-$(CONFIG_PVPANIC) += pvpanic.o
> >>+obj-$(CONFIG_VMGENID) += vmgenid.o
> >>diff --git a/hw/misc/vmgenid.c b/hw/misc/vmgenid.c
> >>new file mode 100644
> >>index 0000000..01aacd4
> >>--- /dev/null
> >>+++ b/hw/misc/vmgenid.c
> >>@@ -0,0 +1,116 @@
> >>+/*
> >>+ *  Virtual Machine Generation ID Device
> >>+ *
> >>+ *  Copyright (C) 2014 Red Hat Inc.
> >>+ *
> >>+ *  Authors: Gal Hammer <ghammer@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/sysbus.h"
> >>+#include "hw/misc/vmgenid.h"
> >>+#include "hw/acpi/acpi_dev_interface.h"
> >>+
> >>+#define PROPERTY_UUID "uuid"
> >>+
> >>+#define VMGENID(obj) OBJECT_CHECK(VmGenIdState, (obj), VMGENID_DEVICE)
> >>+
> >>+typedef struct VmGenIdState {
> >>+    SysBusDevice parent_obj;
> >>+    MemoryRegion iomem;
> >>+    uint8_t guid[16];
> >>+    bool guid_set;
> >>+} VmGenIdState;
> >>+
> >>+bool vm_generation_id_set(void)
> >>+{
> >>+    Object *obj = object_resolve_path_type("", VMGENID_DEVICE, NULL);
> >>+    VmGenIdState *s = VMGENID(obj);
> >>+
> >>+    if (!obj) {
> >>+        return false;
> >>+    }
> >>+    return s->guid_set;
> >>+}
> >>+
> >>+static uint64_t vmgenid_ram_read(void *opaque, hwaddr addr,
> >>+                                 unsigned size)
> >>+{
> >>+    VmGenIdState *s = VMGENID(opaque);
> >>+    uint64_t value;
> >>+
> >>+    memcpy(&value, s->guid + addr, size);
> >>+    return value;
> >>+}
> >>+
> >>+static const MemoryRegionOps vmgenid_ram_ops = {
> >>+    .read = vmgenid_ram_read,
> >>+    .valid = {
> >>+        .min_access_size = 4,
> >>+        .max_access_size = 4,
> >>+    },
> >>+    .endianness = DEVICE_NATIVE_ENDIAN,
> >>+};
> >>+
> >>+static void vmgenid_set_uuid(Object *obj, const char *value, Error **errp)
> >>+{
> >>+    VmGenIdState *s = VMGENID(obj);
> >>+    Object *acpi_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. */
> >>+    if (!first_set) {
> >>+        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_init(Object *obj)
> >>+{
> >>+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> >>+    VmGenIdState *s = VMGENID(obj);
> >>+
> >>+    memory_region_init_io(&s->iomem, obj, &vmgenid_ram_ops, s, "vgid", 16);
> >>+    sysbus_init_mmio(sbd, &s->iomem);
> >
> >This will cause an exit on each access to the GID.
> >I'm guessing this is too expensive.
> 
> Yes, it does cause an exit, but I don't think it is critical because the GID
> is rarely read by the guest OS. As far as I can tell it is only accessed on
> reboot and when the GID is changed.
> 
> Do you prefer that I'll use the cpu_physical_memory_write function? Or do
> you have a diffrent idea on how to implement it?

I think you need to get hold of some RAM, and use that.  For example,
allocate a buffer, pass the address to host in _INI.

> >
> >
> >>+
> >>+    object_property_add_str(obj, PROPERTY_UUID, NULL, vmgenid_set_uuid, NULL);
> >>+}
> >>+
> >>+static void vmgenid_class_init(ObjectClass *klass, void *data)
> >>+{
> >>+    DeviceClass *dc = DEVICE_CLASS(klass);
> >>+
> >>+    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
> >>+}
> >>+
> >>+static const TypeInfo vmgenid_device_info = {
> >>+    .name          = VMGENID_DEVICE,
> >>+    .parent        = TYPE_SYS_BUS_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/acpi/acpi.h b/include/hw/acpi/acpi.h
> >>index 1f678b4..9373b4d 100644
> >>--- a/include/hw/acpi/acpi.h
> >>+++ b/include/hw/acpi/acpi.h
> >>@@ -185,4 +185,6 @@ unsigned acpi_table_len(void *current);
> >>  void acpi_table_add(const QemuOpts *opts, Error **errp);
> >>  void acpi_table_add_builtin(const QemuOpts *opts, Error **errp);
> >>
> >>+void acpi_vm_generation_id_changed(ACPIREGS *acpi_regs, qemu_irq irq);
> >>+
> >>  #endif /* !QEMU_HW_ACPI_H */
> >>diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h
> >>index f245f8d..757ce60 100644
> >>--- a/include/hw/acpi/acpi_dev_interface.h
> >>+++ b/include/hw/acpi/acpi_dev_interface.h
> >>@@ -28,6 +28,9 @@ typedef struct AcpiDeviceIf {
> >>   * ospm_status: returns status of ACPI device objects, reported
> >>   *              via _OST method if device supports it.
> >>   *
> >>+ * vm_generation_id_changed: notify the guest that it generation
> >>+ *                           id was changed.
> >>+ *
> >>   * Interface is designed for providing unified interface
> >>   * to generic ACPI functionality that could be used without
> >>   * knowledge about internals of actual device that implements
> >>@@ -39,5 +42,6 @@ typedef struct AcpiDeviceIfClass {
> >>
> >>      /* <public> */
> >>      void (*ospm_status)(AcpiDeviceIf *adev, ACPIOSTInfoList ***list);
> >>+    void (*vm_generation_id_changed)(AcpiDeviceIf *adev);
> >>  } AcpiDeviceIfClass;
> >>  #endif
> >>diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h
> >>index 7e42448..3b4eed5 100644
> >>--- a/include/hw/acpi/ich9.h
> >>+++ b/include/hw/acpi/ich9.h
> >>@@ -62,4 +62,6 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp);
> >>  void ich9_pm_device_plug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, Error **errp);
> >>
> >>  void ich9_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list);
> >>+
> >>+void ich9_vm_generation_id_changed(AcpiDeviceIf *adev);
> >>  #endif /* HW_ACPI_ICH9_H */
> >>diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
> >>index 1c2602e..87912b2 100644
> >>--- a/include/hw/i386/pc.h
> >>+++ b/include/hw/i386/pc.h
> >>@@ -290,6 +290,9 @@ void pc_system_firmware_init(MemoryRegion *rom_memory,
> >>  /* pvpanic.c */
> >>  uint16_t pvpanic_port(void);
> >>
> >>+/* vmgenid.c */
> >>+bool vm_generation_id_set(void);
> >>+
> >>  /* e820 types */
> >>  #define E820_RAM        1
> >>  #define E820_RESERVED   2
> >>diff --git a/include/hw/misc/vmgenid.h b/include/hw/misc/vmgenid.h
> >>new file mode 100644
> >>index 0000000..3d44421
> >>--- /dev/null
> >>+++ b/include/hw/misc/vmgenid.h
> >>@@ -0,0 +1,21 @@
> >>+/*
> >>+ *  Virtual Machine Generation ID Device
> >>+ *
> >>+ *  Copyright (C) 2014 Red Hat Inc.
> >>+ *
> >>+ *  Authors: Gal Hammer <ghammer@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
> >>+
> >>+#define VMGENID_DEVICE "vmgenid"
> >>+
> >>+#define VMGENID_BASE_ADDRESS    0xfedf0000
> >>+#define VMGENID_BASE_ADDR_LEN   16
> >>+
> >>+#endif
> >>--
> >>1.9.3
diff mbox

Patch

diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index 8e08841..bd33c75 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -45,3 +45,4 @@  CONFIG_IOAPIC=y
 CONFIG_ICC_BUS=y
 CONFIG_PVPANIC=y
 CONFIG_MEM_HOTPLUG=y
+CONFIG_VMGENID=y
diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index 66557ac..006fc7c 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -45,3 +45,4 @@  CONFIG_IOAPIC=y
 CONFIG_ICC_BUS=y
 CONFIG_PVPANIC=y
 CONFIG_MEM_HOTPLUG=y
+CONFIG_VMGENID=y
diff --git a/hw/acpi/core.c b/hw/acpi/core.c
index a7368fb..a01c980 100644
--- a/hw/acpi/core.c
+++ b/hw/acpi/core.c
@@ -28,6 +28,8 @@ 
 #include "qapi-visit.h"
 #include "qapi-event.h"
 
+#define ACPI_VM_GENERATION_ID_CHANGED_STATUS 1
+
 struct acpi_table_header {
     uint16_t _length;         /* our length, not actual part of the hdr */
                               /* allows easier parsing for fw_cfg clients */
@@ -680,3 +682,9 @@  void acpi_update_sci(ACPIREGS *regs, qemu_irq irq)
                        (regs->pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
                        !(pm1a_sts & ACPI_BITMASK_TIMER_STATUS));
 }
+
+void acpi_vm_generation_id_changed(ACPIREGS *acpi_regs, qemu_irq irq)
+{
+    acpi_regs->gpe.sts[0] |= ACPI_VM_GENERATION_ID_CHANGED_STATUS;
+    acpi_update_sci(acpi_regs, irq);
+}
diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
index 7b14bbb..5501c0e 100644
--- a/hw/acpi/ich9.c
+++ b/hw/acpi/ich9.c
@@ -316,3 +316,11 @@  void ich9_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list)
 
     acpi_memory_ospm_status(&s->pm.acpi_memory_hotplug, list);
 }
+
+void ich9_vm_generation_id_changed(AcpiDeviceIf *adev)
+{
+    ICH9LPCState *s = ICH9_LPC_DEVICE(adev);
+    ICH9LPCPMRegs *pm = &s->pm;
+
+    acpi_vm_generation_id_changed(&pm->acpi_regs, pm->irq);
+}
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
index 0bfa814..ad0ef68 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -580,6 +580,13 @@  static void piix4_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list)
     acpi_memory_ospm_status(&s->acpi_memory_hotplug, list);
 }
 
+static void piix4_vm_generation_id_changed(AcpiDeviceIf *adev)
+{
+    PIIX4PMState *s = PIIX4_PM(adev);
+
+    acpi_vm_generation_id_changed(&s->ar, s->irq);
+}
+
 static Property piix4_pm_properties[] = {
     DEFINE_PROP_UINT32("smb_io_base", PIIX4PMState, smb_io_base, 0),
     DEFINE_PROP_UINT8(ACPI_PM_PROP_S3_DISABLED, PIIX4PMState, disable_s3, 0),
@@ -617,6 +624,7 @@  static void piix4_pm_class_init(ObjectClass *klass, void *data)
     hc->plug = piix4_device_plug_cb;
     hc->unplug_request = piix4_device_unplug_request_cb;
     adevc->ospm_status = piix4_ospm_status;
+    adevc->vm_generation_id_changed = piix4_vm_generation_id_changed;
 }
 
 static const TypeInfo piix4_pm_info = {
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 00be4bb..27d0494 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -42,6 +42,7 @@ 
 #include "hw/acpi/memory_hotplug.h"
 #include "sysemu/tpm.h"
 #include "hw/acpi/tpm.h"
+#include "hw/misc/vmgenid.h"
 
 /* Supported chipsets: */
 #include "hw/acpi/piix4.h"
@@ -96,6 +97,7 @@  typedef struct AcpiMiscInfo {
     const unsigned char *dsdt_code;
     unsigned dsdt_size;
     uint16_t pvpanic_port;
+    bool vm_generation_id_set;
 } AcpiMiscInfo;
 
 typedef struct AcpiBuildPciBusHotplugState {
@@ -216,6 +218,7 @@  static void acpi_get_misc_info(AcpiMiscInfo *info)
     info->has_hpet = hpet_find();
     info->has_tpm = tpm_find();
     info->pvpanic_port = pvpanic_port();
+    info->vm_generation_id_set = vm_generation_id_set();
 }
 
 static void acpi_get_pci_info(PcPciInfo *info)
@@ -1088,6 +1091,11 @@  build_ssdt(GArray *table_data, GArray *linker,
     ACPI_BUILD_SET_LE(ssdt_ptr, sizeof(ssdp_misc_aml),
                       ssdt_isa_pest[0], 16, misc->pvpanic_port);
 
+    if (!misc->vm_generation_id_set) {
+        ACPI_BUILD_SET_LE(ssdt_ptr, sizeof(ssdp_misc_aml),
+                          ssdt_acpi_vm_gid_addr[0], 32, 0);
+    }
+
     ACPI_BUILD_SET_LE(ssdt_ptr, sizeof(ssdp_misc_aml),
                       ssdt_mctrl_nr_slots[0], 32, nr_mem);
 
diff --git a/hw/i386/acpi-dsdt.dsl b/hw/i386/acpi-dsdt.dsl
index 559f4b6..10b4afb 100644
--- a/hw/i386/acpi-dsdt.dsl
+++ b/hw/i386/acpi-dsdt.dsl
@@ -307,7 +307,9 @@  DefinitionBlock (
     Scope(\_GPE) {
         Name(_HID, "ACPI0006")
 
-        Method(_L00) {
+        External(\_SB.VMGI, DeviceObj)
+        Method(_E00) {
+            Notify(\_SB.VMGI, 0x80)
         }
         Method(_E01) {
             // PCI hotplug event
diff --git a/hw/i386/acpi-dsdt.hex.generated b/hw/i386/acpi-dsdt.hex.generated
index a21bf41..3fd06d2 100644
--- a/hw/i386/acpi-dsdt.hex.generated
+++ b/hw/i386/acpi-dsdt.hex.generated
@@ -8,7 +8,7 @@  static unsigned char AcpiDsdtAmlCode[] = {
 0x0,
 0x0,
 0x1,
-0x1f,
+0x18,
 0x42,
 0x58,
 0x50,
@@ -32,8 +32,8 @@  static unsigned char AcpiDsdtAmlCode[] = {
 0x54,
 0x4c,
 0x28,
-0x5,
-0x10,
+0x8,
+0x14,
 0x20,
 0x10,
 0x49,
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index d045e8b..95ec233 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -59,6 +59,7 @@ 
 #include "hw/pci/pci_host.h"
 #include "acpi-build.h"
 #include "hw/mem/pc-dimm.h"
+#include "hw/misc/vmgenid.h"
 #include "trace.h"
 #include "qapi/visitor.h"
 
@@ -1361,6 +1362,7 @@  void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
     int i;
     DriveInfo *fd[MAX_FD];
     DeviceState *hpet = NULL;
+    DeviceState *vmgenid;
     int pit_isa_irq = 0;
     qemu_irq pit_alt_irq = NULL;
     qemu_irq rtc_irq = NULL;
@@ -1459,6 +1461,12 @@  void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
         fd[i] = drive_get(IF_FLOPPY, 0, i);
     }
     *floppy = fdctrl_init_isa(isa_bus, fd);
+
+    vmgenid = qdev_try_create(NULL, VMGENID_DEVICE);
+    if (vmgenid) {
+        qdev_init_nofail(vmgenid);
+        sysbus_mmio_map(SYS_BUS_DEVICE(vmgenid), 0, VMGENID_BASE_ADDRESS);
+    }
 }
 
 void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
index 054b035..b1d04e2 100644
--- a/hw/i386/q35-acpi-dsdt.dsl
+++ b/hw/i386/q35-acpi-dsdt.dsl
@@ -415,7 +415,10 @@  DefinitionBlock (
     Scope(\_GPE) {
         Name(_HID, "ACPI0006")
 
-        Method(_L00) {
+        External(\_SB.VMGI, DeviceObj)
+        Method(_E00)
+        {
+            Notify(\_SB.VMGI, 0x80)
         }
         Method(_L01) {
         }
diff --git a/hw/i386/q35-acpi-dsdt.hex.generated b/hw/i386/q35-acpi-dsdt.hex.generated
index c9eb4ac..866e648 100644
--- a/hw/i386/q35-acpi-dsdt.hex.generated
+++ b/hw/i386/q35-acpi-dsdt.hex.generated
@@ -8,7 +8,7 @@  static unsigned char Q35AcpiDsdtAmlCode[] = {
 0x0,
 0x0,
 0x1,
-0xb7,
+0xac,
 0x42,
 0x58,
 0x50,
@@ -31,9 +31,9 @@  static unsigned char Q35AcpiDsdtAmlCode[] = {
 0x4e,
 0x54,
 0x4c,
-0x15,
-0x11,
-0x13,
+0x28,
+0x8,
+0x14,
 0x20,
 0x10,
 0x49,
diff --git a/hw/i386/ssdt-misc.dsl b/hw/i386/ssdt-misc.dsl
index 0fd4480..6ae09fb 100644
--- a/hw/i386/ssdt-misc.dsl
+++ b/hw/i386/ssdt-misc.dsl
@@ -13,6 +13,7 @@ 
  * with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 #include "hw/acpi/pc-hotplug.h"
+#include "hw/misc/vmgenid.h"
 
 ACPI_EXTRACT_ALL_CODE ssdp_misc_aml
 
@@ -118,6 +119,41 @@  DefinitionBlock ("ssdt-misc.aml", "SSDT", 0x01, "BXPC", "BXSSDTSUSP", 0x1)
         }
     }
 
+    Scope(\_SB) {
+        Device(VMGI) {
+            Name(_HID, "QEMU0002")
+            Name(_CID, "VM_Gen_Counter")
+            Name(_DDN, "VM_Gen_Counter")
+
+            ACPI_EXTRACT_NAME_DWORD_CONST ssdt_acpi_vm_gid_addr
+            Name(VGIA, VMGENID_BASE_ADDRESS)
+
+            OperationRegion(VGID, SystemMemory,
+                VMGENID_BASE_ADDRESS, VMGENID_BASE_ADDR_LEN)
+
+            Name(_CRS, ResourceTemplate() {
+                Memory32Fixed(ReadOnly,
+                    VMGENID_BASE_ADDRESS, VMGENID_BASE_ADDR_LEN)
+            })
+
+            Method(_STA, 0, NotSerialized) {
+                Store(VGIA, Local0)
+                If (LEqual(Local0, Zero)) {
+                    Return (0x00)
+                } Else {
+                    Return (0x0F)
+                }
+            }
+
+            Method(ADDR, 0, Serialized) {
+                Store(Package(2) { }, Local0)
+                Store(VGIA, Index(Local0, 0))
+                Store(0x0000, Index(Local0, 1))
+                return (Local0)
+            }
+        }
+    }
+
     External(MEMORY_SLOT_NOTIFY_METHOD, MethodObj)
     Scope(\_SB.PCI0) {
         Device(MEMORY_HOTPLUG_DEVICE) {
diff --git a/hw/i386/ssdt-misc.hex.generated b/hw/i386/ssdt-misc.hex.generated
index ba4268a..f0b7a27 100644
--- a/hw/i386/ssdt-misc.hex.generated
+++ b/hw/i386/ssdt-misc.hex.generated
@@ -26,7 +26,7 @@  static unsigned char ssdp_misc_aml[] = {
 0x0,
 0x0,
 0x1,
-0x8b,
+0x80,
 0x42,
 0x58,
 0x50,
@@ -49,9 +49,9 @@  static unsigned char ssdp_misc_aml[] = {
 0x4e,
 0x54,
 0x4c,
-0x15,
-0x11,
-0x13,
+0x28,
+0x8,
+0x14,
 0x20,
 0x10,
 0x42,
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index 530b074..3a012fa 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -678,6 +678,7 @@  static void ich9_lpc_class_init(ObjectClass *klass, void *data)
     hc->plug = ich9_device_plug_cb;
     hc->unplug_request = ich9_device_unplug_request_cb;
     adevc->ospm_status = ich9_pm_ospm_status;
+    adevc->vm_generation_id_changed = ich9_vm_generation_id_changed;
 }
 
 static const TypeInfo ich9_lpc_info = {
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 979e532..c18b800 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -41,3 +41,4 @@  obj-$(CONFIG_SLAVIO) += slavio_misc.o
 obj-$(CONFIG_ZYNQ) += zynq_slcr.o
 
 obj-$(CONFIG_PVPANIC) += pvpanic.o
+obj-$(CONFIG_VMGENID) += vmgenid.o
diff --git a/hw/misc/vmgenid.c b/hw/misc/vmgenid.c
new file mode 100644
index 0000000..01aacd4
--- /dev/null
+++ b/hw/misc/vmgenid.c
@@ -0,0 +1,116 @@ 
+/*
+ *  Virtual Machine Generation ID Device
+ *
+ *  Copyright (C) 2014 Red Hat Inc.
+ *
+ *  Authors: Gal Hammer <ghammer@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/sysbus.h"
+#include "hw/misc/vmgenid.h"
+#include "hw/acpi/acpi_dev_interface.h"
+
+#define PROPERTY_UUID "uuid"
+
+#define VMGENID(obj) OBJECT_CHECK(VmGenIdState, (obj), VMGENID_DEVICE)
+
+typedef struct VmGenIdState {
+    SysBusDevice parent_obj;
+    MemoryRegion iomem;
+    uint8_t guid[16];
+    bool guid_set;
+} VmGenIdState;
+
+bool vm_generation_id_set(void)
+{
+    Object *obj = object_resolve_path_type("", VMGENID_DEVICE, NULL);
+    VmGenIdState *s = VMGENID(obj);
+
+    if (!obj) {
+        return false;
+    }
+    return s->guid_set;
+}
+
+static uint64_t vmgenid_ram_read(void *opaque, hwaddr addr,
+                                 unsigned size)
+{
+    VmGenIdState *s = VMGENID(opaque);
+    uint64_t value;
+
+    memcpy(&value, s->guid + addr, size);
+    return value;
+}
+
+static const MemoryRegionOps vmgenid_ram_ops = {
+    .read = vmgenid_ram_read,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void vmgenid_set_uuid(Object *obj, const char *value, Error **errp)
+{
+    VmGenIdState *s = VMGENID(obj);
+    Object *acpi_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. */
+    if (!first_set) {
+        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_init(Object *obj)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    VmGenIdState *s = VMGENID(obj);
+
+    memory_region_init_io(&s->iomem, obj, &vmgenid_ram_ops, s, "vgid", 16);
+    sysbus_init_mmio(sbd, &s->iomem);
+
+    object_property_add_str(obj, PROPERTY_UUID, NULL, vmgenid_set_uuid, NULL);
+}
+
+static void vmgenid_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+}
+
+static const TypeInfo vmgenid_device_info = {
+    .name          = VMGENID_DEVICE,
+    .parent        = TYPE_SYS_BUS_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/acpi/acpi.h b/include/hw/acpi/acpi.h
index 1f678b4..9373b4d 100644
--- a/include/hw/acpi/acpi.h
+++ b/include/hw/acpi/acpi.h
@@ -185,4 +185,6 @@  unsigned acpi_table_len(void *current);
 void acpi_table_add(const QemuOpts *opts, Error **errp);
 void acpi_table_add_builtin(const QemuOpts *opts, Error **errp);
 
+void acpi_vm_generation_id_changed(ACPIREGS *acpi_regs, qemu_irq irq);
+
 #endif /* !QEMU_HW_ACPI_H */
diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h
index f245f8d..757ce60 100644
--- a/include/hw/acpi/acpi_dev_interface.h
+++ b/include/hw/acpi/acpi_dev_interface.h
@@ -28,6 +28,9 @@  typedef struct AcpiDeviceIf {
  * ospm_status: returns status of ACPI device objects, reported
  *              via _OST method if device supports it.
  *
+ * vm_generation_id_changed: notify the guest that it generation
+ *                           id was changed.
+ *
  * Interface is designed for providing unified interface
  * to generic ACPI functionality that could be used without
  * knowledge about internals of actual device that implements
@@ -39,5 +42,6 @@  typedef struct AcpiDeviceIfClass {
 
     /* <public> */
     void (*ospm_status)(AcpiDeviceIf *adev, ACPIOSTInfoList ***list);
+    void (*vm_generation_id_changed)(AcpiDeviceIf *adev);
 } AcpiDeviceIfClass;
 #endif
diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h
index 7e42448..3b4eed5 100644
--- a/include/hw/acpi/ich9.h
+++ b/include/hw/acpi/ich9.h
@@ -62,4 +62,6 @@  void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp);
 void ich9_pm_device_plug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, Error **errp);
 
 void ich9_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list);
+
+void ich9_vm_generation_id_changed(AcpiDeviceIf *adev);
 #endif /* HW_ACPI_ICH9_H */
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 1c2602e..87912b2 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -290,6 +290,9 @@  void pc_system_firmware_init(MemoryRegion *rom_memory,
 /* pvpanic.c */
 uint16_t pvpanic_port(void);
 
+/* vmgenid.c */
+bool vm_generation_id_set(void);
+
 /* e820 types */
 #define E820_RAM        1
 #define E820_RESERVED   2
diff --git a/include/hw/misc/vmgenid.h b/include/hw/misc/vmgenid.h
new file mode 100644
index 0000000..3d44421
--- /dev/null
+++ b/include/hw/misc/vmgenid.h
@@ -0,0 +1,21 @@ 
+/*
+ *  Virtual Machine Generation ID Device
+ *
+ *  Copyright (C) 2014 Red Hat Inc.
+ *
+ *  Authors: Gal Hammer <ghammer@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
+
+#define VMGENID_DEVICE "vmgenid"
+
+#define VMGENID_BASE_ADDRESS    0xfedf0000
+#define VMGENID_BASE_ADDR_LEN   16
+
+#endif