virtio, pc: fixes, features
virtio support for region caches broke a bunch of stuff - fixing most of it though it's not ideal. Still pondering the right way to fix it. New: VM gen ID and hotplug for PXB. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> -----BEGIN PGP SIGNATURE----- iQEcBAABAgAGBQJYt7llAAoJECgfDbjSjVRp+r4H/1cmQ4F67H8oSOAT8xuAQFku OdHoVRJMWf7CRvZ7JqVke/a877d+h6ZpfW5dZQ7hp7O7rkPiuPHa5PVb0WGwDqrD scSOIvDPxJm19pnfZoF4zx+Ov45W5ahF+gwwm/sJU232ApLqOmAjs0FUxidkadQE f5Jrjs20WO2Vkkcd3U7Zl31myre0V7AbwIm7dB/8B+dpL6bJcxSvlM4krwLdBY6S lLs9V6ypRzjUxS3MDANL75KNrO/zys55J+Pa4sEh4+H0OX71v9Icl3s1zaM8J/EN VPjdqhDvJuEahc50FbJyRZQGIzOZ6PcGMsKUHKlxoVmDYZ6Pv5lOnpaLZRT6HMk= =ITdO -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/mst/tags/for_upstream' into staging virtio, pc: fixes, features virtio support for region caches broke a bunch of stuff - fixing most of it though it's not ideal. Still pondering the right way to fix it. New: VM gen ID and hotplug for PXB. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> # gpg: Signature made Thu 02 Mar 2017 06:19:17 GMT # gpg: using RSA key 0x281F0DB8D28D5469 # gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>" # gpg: aka "Michael S. Tsirkin <mst@redhat.com>" # Primary key fingerprint: 0270 606B 6F3C DF3D 0B17 0970 C350 3912 AFBE 8E67 # Subkey fingerprint: 5D09 FD08 71C8 F85B 94CA 8A0D 281F 0DB8 D28D 5469 * remotes/mst/tags/for_upstream: hw/pxb-pcie: fix PCI Express hotplug support tests/acpi: update DSDT after last patch acpi: simplify _OSC virtio: unbreak virtio-pci with IOMMU after caching ring translations virtio: add missing region cache init in virtio_load() virtio: invalidate memory in vring_set_avail_event() virtio: guard vring access when setting notification virtio: check for vring setup in virtio_queue_empty MAINTAINERS: Add VM Generation ID entries tests: Move reusable ACPI code into a utility file qmp/hmp: add query-vm-generation-id and 'info vm-generation-id' commands ACPI: Add Virtual Machine Generation ID support ACPI: Add vmgenid blob storage to the build tables docs: VM Generation ID device description linker-loader: Add new 'write pointer' command Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
		
						commit
						9a81b792cc
					
				
							
								
								
									
										11
									
								
								MAINTAINERS
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								MAINTAINERS
									
									
									
									
									
								
							@ -908,6 +908,8 @@ F: hw/acpi/*
 | 
			
		||||
F: hw/smbios/*
 | 
			
		||||
F: hw/i386/acpi-build.[hc]
 | 
			
		||||
F: hw/arm/virt-acpi-build.c
 | 
			
		||||
F: tests/bios-tables-test.c
 | 
			
		||||
F: tests/acpi-utils.[hc]
 | 
			
		||||
 | 
			
		||||
ppc4xx
 | 
			
		||||
M: Alexander Graf <agraf@suse.de>
 | 
			
		||||
@ -1122,6 +1124,15 @@ F: hw/nvram/chrp_nvram.c
 | 
			
		||||
F: include/hw/nvram/chrp_nvram.h
 | 
			
		||||
F: tests/prom-env-test.c
 | 
			
		||||
 | 
			
		||||
VM Generation ID
 | 
			
		||||
M: Ben Warren <ben@skyportsystems.com>
 | 
			
		||||
S: Maintained
 | 
			
		||||
F: hw/acpi/vmgenid.c
 | 
			
		||||
F: include/hw/acpi/vmgenid.h
 | 
			
		||||
F: docs/specs/vmgenid.txt
 | 
			
		||||
F: tests/vmgenid-test.c
 | 
			
		||||
F: stubs/vmgenid.c
 | 
			
		||||
 | 
			
		||||
Subsystems
 | 
			
		||||
----------
 | 
			
		||||
Audio
 | 
			
		||||
 | 
			
		||||
@ -59,3 +59,4 @@ CONFIG_I82801B11=y
 | 
			
		||||
CONFIG_SMBIOS=y
 | 
			
		||||
CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
 | 
			
		||||
CONFIG_PXB=y
 | 
			
		||||
CONFIG_ACPI_VMGENID=y
 | 
			
		||||
 | 
			
		||||
@ -59,3 +59,4 @@ CONFIG_I82801B11=y
 | 
			
		||||
CONFIG_SMBIOS=y
 | 
			
		||||
CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
 | 
			
		||||
CONFIG_PXB=y
 | 
			
		||||
CONFIG_ACPI_VMGENID=y
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										245
									
								
								docs/specs/vmgenid.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										245
									
								
								docs/specs/vmgenid.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,245 @@
 | 
			
		||||
VIRTUAL MACHINE GENERATION ID
 | 
			
		||||
=============================
 | 
			
		||||
 | 
			
		||||
Copyright (C) 2016 Red Hat, Inc.
 | 
			
		||||
Copyright (C) 2017 Skyport Systems, Inc.
 | 
			
		||||
 | 
			
		||||
This work is licensed under the terms of the GNU GPL, version 2 or later.
 | 
			
		||||
See the COPYING file in the top-level directory.
 | 
			
		||||
 | 
			
		||||
===
 | 
			
		||||
 | 
			
		||||
The VM generation ID (vmgenid) device is an emulated device which
 | 
			
		||||
exposes a 128-bit, cryptographically random, integer value identifier,
 | 
			
		||||
referred to as a Globally Unique Identifier, or GUID.
 | 
			
		||||
 | 
			
		||||
This allows management applications (e.g. libvirt) to notify the guest
 | 
			
		||||
operating system when the virtual machine is executed with a different
 | 
			
		||||
configuration (e.g. snapshot execution or creation from a template).  The
 | 
			
		||||
guest operating system notices the change, and is then able to react as
 | 
			
		||||
appropriate by marking its copies of distributed databases as dirty,
 | 
			
		||||
re-initializing its random number generator etc.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Requirements
 | 
			
		||||
------------
 | 
			
		||||
 | 
			
		||||
These requirements are extracted from the "How to implement virtual machine
 | 
			
		||||
generation ID support in a virtualization platform" section of the
 | 
			
		||||
specification, dated August 1, 2012.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
The document may be found on the web at:
 | 
			
		||||
  http://go.microsoft.com/fwlink/?LinkId=260709
 | 
			
		||||
 | 
			
		||||
R1a. The generation ID shall live in an 8-byte aligned buffer.
 | 
			
		||||
 | 
			
		||||
R1b. The buffer holding the generation ID shall be in guest RAM, ROM, or device
 | 
			
		||||
     MMIO range.
 | 
			
		||||
 | 
			
		||||
R1c. The buffer holding the generation ID shall be kept separate from areas
 | 
			
		||||
     used by the operating system.
 | 
			
		||||
 | 
			
		||||
R1d. The buffer shall not be covered by an AddressRangeMemory or
 | 
			
		||||
     AddressRangeACPI entry in the E820 or UEFI memory map.
 | 
			
		||||
 | 
			
		||||
R1e. The generation ID shall not live in a page frame that could be mapped with
 | 
			
		||||
     caching disabled. (In other words, regardless of whether the generation ID
 | 
			
		||||
     lives in RAM, ROM or MMIO, it shall only be mapped as cacheable.)
 | 
			
		||||
 | 
			
		||||
R2 to R5. [These AML requirements are isolated well enough in the Microsoft
 | 
			
		||||
          specification for us to simply refer to them here.]
 | 
			
		||||
 | 
			
		||||
R6. The hypervisor shall expose a _HID (hardware identifier) object in the
 | 
			
		||||
    VMGenId device's scope that is unique to the hypervisor vendor.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
QEMU Implementation
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
The above-mentioned specification does not dictate which ACPI descriptor table
 | 
			
		||||
will contain the VM Generation ID device.  Other implementations (Hyper-V and
 | 
			
		||||
Xen) put it in the main descriptor table (Differentiated System Description
 | 
			
		||||
Table or DSDT).  For ease of debugging and implementation, we have decided to
 | 
			
		||||
put it in its own Secondary System Description Table, or SSDT.
 | 
			
		||||
 | 
			
		||||
The following is a dump of the contents from a running system:
 | 
			
		||||
 | 
			
		||||
# iasl -p ./SSDT -d /sys/firmware/acpi/tables/SSDT
 | 
			
		||||
 | 
			
		||||
Intel ACPI Component Architecture
 | 
			
		||||
ASL+ Optimizing Compiler version 20150717-64
 | 
			
		||||
Copyright (c) 2000 - 2015 Intel Corporation
 | 
			
		||||
 | 
			
		||||
Reading ACPI table from file /sys/firmware/acpi/tables/SSDT - Length
 | 
			
		||||
00000198 (0x0000C6)
 | 
			
		||||
ACPI: SSDT 0x0000000000000000 0000C6 (v01 BOCHS  VMGENID  00000001 BXPC
 | 
			
		||||
00000001)
 | 
			
		||||
Acpi table [SSDT] successfully installed and loaded
 | 
			
		||||
Pass 1 parse of [SSDT]
 | 
			
		||||
Pass 2 parse of [SSDT]
 | 
			
		||||
Parsing Deferred Opcodes (Methods/Buffers/Packages/Regions)
 | 
			
		||||
 | 
			
		||||
Parsing completed
 | 
			
		||||
Disassembly completed
 | 
			
		||||
ASL Output:    ./SSDT.dsl - 1631 bytes
 | 
			
		||||
# cat SSDT.dsl
 | 
			
		||||
/*
 | 
			
		||||
 * Intel ACPI Component Architecture
 | 
			
		||||
 * AML/ASL+ Disassembler version 20150717-64
 | 
			
		||||
 * Copyright (c) 2000 - 2015 Intel Corporation
 | 
			
		||||
 *
 | 
			
		||||
 * Disassembling to symbolic ASL+ operators
 | 
			
		||||
 *
 | 
			
		||||
 * Disassembly of /sys/firmware/acpi/tables/SSDT, Sun Feb  5 00:19:37 2017
 | 
			
		||||
 *
 | 
			
		||||
 * Original Table Header:
 | 
			
		||||
 *     Signature        "SSDT"
 | 
			
		||||
 *     Length           0x000000CA (202)
 | 
			
		||||
 *     Revision         0x01
 | 
			
		||||
 *     Checksum         0x4B
 | 
			
		||||
 *     OEM ID           "BOCHS "
 | 
			
		||||
 *     OEM Table ID     "VMGENID"
 | 
			
		||||
 *     OEM Revision     0x00000001 (1)
 | 
			
		||||
 *     Compiler ID      "BXPC"
 | 
			
		||||
 *     Compiler Version 0x00000001 (1)
 | 
			
		||||
 */
 | 
			
		||||
DefinitionBlock ("/sys/firmware/acpi/tables/SSDT.aml", "SSDT", 1, "BOCHS ",
 | 
			
		||||
"VMGENID", 0x00000001)
 | 
			
		||||
{
 | 
			
		||||
    Name (VGIA, 0x07FFF000)
 | 
			
		||||
    Scope (\_SB)
 | 
			
		||||
    {
 | 
			
		||||
        Device (VGEN)
 | 
			
		||||
        {
 | 
			
		||||
            Name (_HID, "QEMUVGID")  // _HID: Hardware ID
 | 
			
		||||
            Name (_CID, "VM_Gen_Counter")  // _CID: Compatible ID
 | 
			
		||||
            Name (_DDN, "VM_Gen_Counter")  // _DDN: DOS Device Name
 | 
			
		||||
            Method (_STA, 0, NotSerialized)  // _STA: Status
 | 
			
		||||
            {
 | 
			
		||||
                Local0 = 0x0F
 | 
			
		||||
                If ((VGIA == Zero))
 | 
			
		||||
                {
 | 
			
		||||
                    Local0 = Zero
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Return (Local0)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Method (ADDR, 0, NotSerialized)
 | 
			
		||||
            {
 | 
			
		||||
                Local0 = Package (0x02) {}
 | 
			
		||||
                Index (Local0, Zero) = (VGIA + 0x28)
 | 
			
		||||
                Index (Local0, One) = Zero
 | 
			
		||||
                Return (Local0)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Method (\_GPE._E05, 0, NotSerialized)  // _Exx: Edge-Triggered GPE
 | 
			
		||||
    {
 | 
			
		||||
        Notify (\_SB.VGEN, 0x80) // Status Change
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Design Details:
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
Requirements R1a through R1e dictate that the memory holding the
 | 
			
		||||
VM Generation ID must be allocated and owned by the guest firmware,
 | 
			
		||||
in this case BIOS or UEFI.  However, to be useful, QEMU must be able to
 | 
			
		||||
change the contents of the memory at runtime, specifically when starting a
 | 
			
		||||
backed-up or snapshotted image.  In order to do this, QEMU must know the
 | 
			
		||||
address that has been allocated.
 | 
			
		||||
 | 
			
		||||
The mechanism chosen for this memory sharing is writeable fw_cfg blobs.
 | 
			
		||||
These are data object that are visible to both QEMU and guests, and are
 | 
			
		||||
addressable as sequential files.
 | 
			
		||||
 | 
			
		||||
More information about fw_cfg can be found in "docs/specs/fw_cfg.txt"
 | 
			
		||||
 | 
			
		||||
Two fw_cfg blobs are used in this case:
 | 
			
		||||
 | 
			
		||||
/etc/vmgenid_guid - contains the actual VM Generation ID GUID
 | 
			
		||||
                  - read-only to the guest
 | 
			
		||||
/etc/vmgenid_addr - contains the address of the downloaded vmgenid blob
 | 
			
		||||
                  - writeable by the guest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
QEMU sends the following commands to the guest at startup:
 | 
			
		||||
 | 
			
		||||
1. Allocate memory for vmgenid_guid fw_cfg blob.
 | 
			
		||||
2. Write the address of vmgenid_guid into the SSDT (VGIA ACPI variable as
 | 
			
		||||
   shown above in the iasl dump).  Note that this change is not propagated
 | 
			
		||||
   back to QEMU.
 | 
			
		||||
3. Write the address of vmgenid_guid back to QEMU's copy of vmgenid_addr
 | 
			
		||||
   via the fw_cfg DMA interface.
 | 
			
		||||
 | 
			
		||||
After step 3, QEMU is able to update the contents of vmgenid_guid at will.
 | 
			
		||||
 | 
			
		||||
Since BIOS or UEFI does not necessarily run when we wish to change the GUID,
 | 
			
		||||
the value of VGIA is persisted via the VMState mechanism.
 | 
			
		||||
 | 
			
		||||
As spelled out in the specification, any change to the GUID executes an
 | 
			
		||||
ACPI notification.  The exact handler to use is not specified, so the vmgenid
 | 
			
		||||
device uses the first unused one:  \_GPE._E05.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Endian-ness Considerations:
 | 
			
		||||
---------------------------
 | 
			
		||||
 | 
			
		||||
Although not specified in Microsoft's document, it is assumed that the
 | 
			
		||||
device is expected to use little-endian format.
 | 
			
		||||
 | 
			
		||||
All GUID passed in via command line or monitor are treated as big-endian.
 | 
			
		||||
GUID values displayed via monitor are shown in big-endian format.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
GUID Storage Format:
 | 
			
		||||
--------------------
 | 
			
		||||
 | 
			
		||||
In order to implement an OVMF "SDT Header Probe Suppressor", the contents of
 | 
			
		||||
the vmgenid_guid fw_cfg blob are not simply a 128-bit GUID.  There is also
 | 
			
		||||
significant padding in order to align and fill a memory page, as shown in the
 | 
			
		||||
following diagram:
 | 
			
		||||
 | 
			
		||||
+----------------------------------+
 | 
			
		||||
| SSDT with OEM Table ID = VMGENID |
 | 
			
		||||
+----------------------------------+
 | 
			
		||||
| ...                              |       TOP OF PAGE
 | 
			
		||||
| VGIA dword object ---------------|-----> +---------------------------+
 | 
			
		||||
| ...                              |       | fw-allocated array for    |
 | 
			
		||||
| _STA method referring to VGIA    |       | "etc/vmgenid_guid"        |
 | 
			
		||||
| ...                              |       +---------------------------+
 | 
			
		||||
| ADDR method referring to VGIA    |       |  0: OVMF SDT Header probe |
 | 
			
		||||
| ...                              |       |     suppressor            |
 | 
			
		||||
+----------------------------------+       | 36: padding for 8-byte    |
 | 
			
		||||
                                           |     alignment             |
 | 
			
		||||
                                           | 40: GUID                  |
 | 
			
		||||
                                           | 56: padding to page size  |
 | 
			
		||||
                                           +---------------------------+
 | 
			
		||||
                                           END OF PAGE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Device Usage:
 | 
			
		||||
-------------
 | 
			
		||||
 | 
			
		||||
The device has one property, which may be only be set using the command line:
 | 
			
		||||
 | 
			
		||||
  guid - sets the value of the GUID.  A special value "auto" instructs
 | 
			
		||||
         QEMU to generate a new random GUID.
 | 
			
		||||
 | 
			
		||||
For example:
 | 
			
		||||
 | 
			
		||||
  QEMU  -device vmgenid,guid="324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87"
 | 
			
		||||
  QEMU  -device vmgenid,guid=auto
 | 
			
		||||
 | 
			
		||||
The property may be queried via QMP/HMP:
 | 
			
		||||
 | 
			
		||||
  (QEMU) query-vm-generation-id
 | 
			
		||||
  {"return": {"guid": "324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87"}}
 | 
			
		||||
 | 
			
		||||
Setting of this parameter is intentionally left out from the QMP/HMP
 | 
			
		||||
interfaces.  There are no known use cases for changing the GUID once QEMU is
 | 
			
		||||
running, and adding this capability would greatly increase the complexity.
 | 
			
		||||
							
								
								
									
										2
									
								
								dtc
									
									
									
									
									
								
							
							
								
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								dtc
									
									
									
									
									
								
							@ -1 +1 @@
 | 
			
		||||
Subproject commit ec02b34c05be04f249ffaaca4b666f5246877dea
 | 
			
		||||
Subproject commit 65cc4d2748a2c2e6f27f1cf39e07a5dbabd80ebf
 | 
			
		||||
@ -801,6 +801,20 @@ STEXI
 | 
			
		||||
Show information about hotpluggable CPUs
 | 
			
		||||
ETEXI
 | 
			
		||||
 | 
			
		||||
STEXI
 | 
			
		||||
@item info vm-generation-id
 | 
			
		||||
@findex vm-generation-id
 | 
			
		||||
Show Virtual Machine Generation ID
 | 
			
		||||
ETEXI
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        .name       = "vm-generation-id",
 | 
			
		||||
        .args_type  = "",
 | 
			
		||||
        .params     = "",
 | 
			
		||||
        .help       = "Show Virtual Machine Generation ID",
 | 
			
		||||
        .cmd = hmp_info_vm_generation_id,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
STEXI
 | 
			
		||||
@end table
 | 
			
		||||
ETEXI
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								hmp.c
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								hmp.c
									
									
									
									
									
								
							@ -2605,3 +2605,12 @@ void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict)
 | 
			
		||||
 | 
			
		||||
    qapi_free_HotpluggableCPUList(saved);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict)
 | 
			
		||||
{
 | 
			
		||||
    GuidInfo *info = qmp_query_vm_generation_id(NULL);
 | 
			
		||||
    if (info) {
 | 
			
		||||
        monitor_printf(mon, "%s\n", info->guid);
 | 
			
		||||
    }
 | 
			
		||||
    qapi_free_GuidInfo(info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								hmp.h
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								hmp.h
									
									
									
									
									
								
							@ -137,5 +137,6 @@ void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict);
 | 
			
		||||
void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict);
 | 
			
		||||
void hmp_info_dump(Monitor *mon, const QDict *qdict);
 | 
			
		||||
void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict);
 | 
			
		||||
void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@ common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o
 | 
			
		||||
common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
 | 
			
		||||
common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
 | 
			
		||||
common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
 | 
			
		||||
common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o
 | 
			
		||||
common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
 | 
			
		||||
 | 
			
		||||
common-obj-y += acpi_interface.o
 | 
			
		||||
 | 
			
		||||
@ -1559,6 +1559,7 @@ void acpi_build_tables_init(AcpiBuildTables *tables)
 | 
			
		||||
    tables->rsdp = g_array_new(false, true /* clear */, 1);
 | 
			
		||||
    tables->table_data = g_array_new(false, true /* clear */, 1);
 | 
			
		||||
    tables->tcpalog = g_array_new(false, true /* clear */, 1);
 | 
			
		||||
    tables->vmgenid = g_array_new(false, true /* clear */, 1);
 | 
			
		||||
    tables->linker = bios_linker_loader_init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1568,6 +1569,7 @@ void acpi_build_tables_cleanup(AcpiBuildTables *tables, bool mfre)
 | 
			
		||||
    g_array_free(tables->rsdp, true);
 | 
			
		||||
    g_array_free(tables->table_data, true);
 | 
			
		||||
    g_array_free(tables->tcpalog, mfre);
 | 
			
		||||
    g_array_free(tables->vmgenid, mfre);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Build rsdt table */
 | 
			
		||||
 | 
			
		||||
@ -78,6 +78,21 @@ struct BiosLinkerLoaderEntry {
 | 
			
		||||
            uint32_t length;
 | 
			
		||||
        } cksum;
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * COMMAND_WRITE_POINTER - write the fw_cfg file (originating from
 | 
			
		||||
         * @dest_file) at @wr_pointer.offset, by adding a pointer to
 | 
			
		||||
         * @src_offset within the table originating from @src_file.
 | 
			
		||||
         * 1,2,4 or 8 byte unsigned addition is used depending on
 | 
			
		||||
         * @wr_pointer.size.
 | 
			
		||||
         */
 | 
			
		||||
        struct {
 | 
			
		||||
            char dest_file[BIOS_LINKER_LOADER_FILESZ];
 | 
			
		||||
            char src_file[BIOS_LINKER_LOADER_FILESZ];
 | 
			
		||||
            uint32_t dst_offset;
 | 
			
		||||
            uint32_t src_offset;
 | 
			
		||||
            uint8_t size;
 | 
			
		||||
        } wr_pointer;
 | 
			
		||||
 | 
			
		||||
        /* padding */
 | 
			
		||||
        char pad[124];
 | 
			
		||||
    };
 | 
			
		||||
@ -88,6 +103,7 @@ enum {
 | 
			
		||||
    BIOS_LINKER_LOADER_COMMAND_ALLOCATE          = 0x1,
 | 
			
		||||
    BIOS_LINKER_LOADER_COMMAND_ADD_POINTER       = 0x2,
 | 
			
		||||
    BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM      = 0x3,
 | 
			
		||||
    BIOS_LINKER_LOADER_COMMAND_WRITE_POINTER     = 0x4,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
@ -278,3 +294,47 @@ void bios_linker_loader_add_pointer(BIOSLinker *linker,
 | 
			
		||||
 | 
			
		||||
    g_array_append_vals(linker->cmd_blob, &entry, sizeof entry);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * bios_linker_loader_write_pointer: ask guest to write a pointer to the
 | 
			
		||||
 * source file into the destination file, and write it back to QEMU via
 | 
			
		||||
 * fw_cfg DMA.
 | 
			
		||||
 *
 | 
			
		||||
 * @linker: linker object instance
 | 
			
		||||
 * @dest_file: destination file that must be written
 | 
			
		||||
 * @dst_patched_offset: location within destination file blob to be patched
 | 
			
		||||
 *                      with the pointer to @src_file, in bytes
 | 
			
		||||
 * @dst_patched_offset_size: size of the pointer to be patched
 | 
			
		||||
 *                      at @dst_patched_offset in @dest_file blob, in bytes
 | 
			
		||||
 * @src_file: source file who's address must be taken
 | 
			
		||||
 * @src_offset: location within source file blob to which
 | 
			
		||||
 *              @dest_file+@dst_patched_offset will point to after
 | 
			
		||||
 *              firmware's executed WRITE_POINTER command
 | 
			
		||||
 */
 | 
			
		||||
void bios_linker_loader_write_pointer(BIOSLinker *linker,
 | 
			
		||||
                                    const char *dest_file,
 | 
			
		||||
                                    uint32_t dst_patched_offset,
 | 
			
		||||
                                    uint8_t dst_patched_size,
 | 
			
		||||
                                    const char *src_file,
 | 
			
		||||
                                    uint32_t src_offset)
 | 
			
		||||
{
 | 
			
		||||
    BiosLinkerLoaderEntry entry;
 | 
			
		||||
    const BiosLinkerFileEntry *source_file =
 | 
			
		||||
        bios_linker_find_file(linker, src_file);
 | 
			
		||||
 | 
			
		||||
    assert(source_file);
 | 
			
		||||
    assert(src_offset < source_file->blob->len);
 | 
			
		||||
    memset(&entry, 0, sizeof entry);
 | 
			
		||||
    strncpy(entry.wr_pointer.dest_file, dest_file,
 | 
			
		||||
            sizeof entry.wr_pointer.dest_file - 1);
 | 
			
		||||
    strncpy(entry.wr_pointer.src_file, src_file,
 | 
			
		||||
            sizeof entry.wr_pointer.src_file - 1);
 | 
			
		||||
    entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_WRITE_POINTER);
 | 
			
		||||
    entry.wr_pointer.dst_offset = cpu_to_le32(dst_patched_offset);
 | 
			
		||||
    entry.wr_pointer.src_offset = cpu_to_le32(src_offset);
 | 
			
		||||
    entry.wr_pointer.size = dst_patched_size;
 | 
			
		||||
    assert(dst_patched_size == 1 || dst_patched_size == 2 ||
 | 
			
		||||
           dst_patched_size == 4 || dst_patched_size == 8);
 | 
			
		||||
 | 
			
		||||
    g_array_append_vals(linker->cmd_blob, &entry, sizeof entry);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										258
									
								
								hw/acpi/vmgenid.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										258
									
								
								hw/acpi/vmgenid.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,258 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  Virtual Machine Generation ID Device
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017 Skyport Systems.
 | 
			
		||||
 *
 | 
			
		||||
 *  Author: Ben Warren <ben@skyportsystems.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 "qemu/osdep.h"
 | 
			
		||||
#include "qmp-commands.h"
 | 
			
		||||
#include "hw/acpi/acpi.h"
 | 
			
		||||
#include "hw/acpi/aml-build.h"
 | 
			
		||||
#include "hw/acpi/vmgenid.h"
 | 
			
		||||
#include "hw/nvram/fw_cfg.h"
 | 
			
		||||
#include "sysemu/sysemu.h"
 | 
			
		||||
 | 
			
		||||
void vmgenid_build_acpi(VmGenIdState *vms, GArray *table_data, GArray *guid,
 | 
			
		||||
                        BIOSLinker *linker)
 | 
			
		||||
{
 | 
			
		||||
    Aml *ssdt, *dev, *scope, *method, *addr, *if_ctx;
 | 
			
		||||
    uint32_t vgia_offset;
 | 
			
		||||
    QemuUUID guid_le;
 | 
			
		||||
 | 
			
		||||
    /* Fill in the GUID values.  These need to be converted to little-endian
 | 
			
		||||
     * first, since that's what the guest expects
 | 
			
		||||
     */
 | 
			
		||||
    g_array_set_size(guid, VMGENID_FW_CFG_SIZE - ARRAY_SIZE(guid_le.data));
 | 
			
		||||
    guid_le = vms->guid;
 | 
			
		||||
    qemu_uuid_bswap(&guid_le);
 | 
			
		||||
    /* The GUID is written at a fixed offset into the fw_cfg file
 | 
			
		||||
     * in order to implement the "OVMF SDT Header probe suppressor"
 | 
			
		||||
     * see docs/specs/vmgenid.txt for more details
 | 
			
		||||
     */
 | 
			
		||||
    g_array_insert_vals(guid, VMGENID_GUID_OFFSET, guid_le.data,
 | 
			
		||||
                        ARRAY_SIZE(guid_le.data));
 | 
			
		||||
 | 
			
		||||
    /* Put this in a separate SSDT table */
 | 
			
		||||
    ssdt = init_aml_allocator();
 | 
			
		||||
 | 
			
		||||
    /* Reserve space for header */
 | 
			
		||||
    acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader));
 | 
			
		||||
 | 
			
		||||
    /* Storage for the GUID address */
 | 
			
		||||
    vgia_offset = table_data->len +
 | 
			
		||||
        build_append_named_dword(ssdt->buf, "VGIA");
 | 
			
		||||
    scope = aml_scope("\\_SB");
 | 
			
		||||
    dev = aml_device("VGEN");
 | 
			
		||||
    aml_append(dev, aml_name_decl("_HID", aml_string("QEMUVGID")));
 | 
			
		||||
    aml_append(dev, aml_name_decl("_CID", aml_string("VM_Gen_Counter")));
 | 
			
		||||
    aml_append(dev, aml_name_decl("_DDN", aml_string("VM_Gen_Counter")));
 | 
			
		||||
 | 
			
		||||
    /* Simple status method to check that address is linked and non-zero */
 | 
			
		||||
    method = aml_method("_STA", 0, AML_NOTSERIALIZED);
 | 
			
		||||
    addr = aml_local(0);
 | 
			
		||||
    aml_append(method, aml_store(aml_int(0xf), addr));
 | 
			
		||||
    if_ctx = aml_if(aml_equal(aml_name("VGIA"), aml_int(0)));
 | 
			
		||||
    aml_append(if_ctx, aml_store(aml_int(0), addr));
 | 
			
		||||
    aml_append(method, if_ctx);
 | 
			
		||||
    aml_append(method, aml_return(addr));
 | 
			
		||||
    aml_append(dev, method);
 | 
			
		||||
 | 
			
		||||
    /* the ADDR method returns two 32-bit words representing the lower and
 | 
			
		||||
     * upper halves * of the physical address of the fw_cfg blob
 | 
			
		||||
     * (holding the GUID)
 | 
			
		||||
     */
 | 
			
		||||
    method = aml_method("ADDR", 0, AML_NOTSERIALIZED);
 | 
			
		||||
 | 
			
		||||
    addr = aml_local(0);
 | 
			
		||||
    aml_append(method, aml_store(aml_package(2), addr));
 | 
			
		||||
 | 
			
		||||
    aml_append(method, aml_store(aml_add(aml_name("VGIA"),
 | 
			
		||||
                                         aml_int(VMGENID_GUID_OFFSET), NULL),
 | 
			
		||||
                                 aml_index(addr, aml_int(0))));
 | 
			
		||||
    aml_append(method, aml_store(aml_int(0), aml_index(addr, aml_int(1))));
 | 
			
		||||
    aml_append(method, aml_return(addr));
 | 
			
		||||
 | 
			
		||||
    aml_append(dev, method);
 | 
			
		||||
    aml_append(scope, dev);
 | 
			
		||||
    aml_append(ssdt, scope);
 | 
			
		||||
 | 
			
		||||
    /* attach an ACPI notify */
 | 
			
		||||
    method = aml_method("\\_GPE._E05", 0, AML_NOTSERIALIZED);
 | 
			
		||||
    aml_append(method, aml_notify(aml_name("\\_SB.VGEN"), aml_int(0x80)));
 | 
			
		||||
    aml_append(ssdt, method);
 | 
			
		||||
 | 
			
		||||
    g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len);
 | 
			
		||||
 | 
			
		||||
    /* Allocate guest memory for the Data fw_cfg blob */
 | 
			
		||||
    bios_linker_loader_alloc(linker, VMGENID_GUID_FW_CFG_FILE, guid, 4096,
 | 
			
		||||
                             false /* page boundary, high memory */);
 | 
			
		||||
 | 
			
		||||
    /* Patch address of GUID fw_cfg blob into the ADDR fw_cfg blob
 | 
			
		||||
     * so QEMU can write the GUID there.  The address is expected to be
 | 
			
		||||
     * < 4GB, but write 64 bits anyway.
 | 
			
		||||
     * The address that is patched in is offset in order to implement
 | 
			
		||||
     * the "OVMF SDT Header probe suppressor"
 | 
			
		||||
     * see docs/specs/vmgenid.txt for more details.
 | 
			
		||||
     */
 | 
			
		||||
    bios_linker_loader_write_pointer(linker,
 | 
			
		||||
        VMGENID_ADDR_FW_CFG_FILE, 0, sizeof(uint64_t),
 | 
			
		||||
        VMGENID_GUID_FW_CFG_FILE, VMGENID_GUID_OFFSET);
 | 
			
		||||
 | 
			
		||||
    /* Patch address of GUID fw_cfg blob into the AML so OSPM can retrieve
 | 
			
		||||
     * and read it.  Note that while we provide storage for 64 bits, only
 | 
			
		||||
     * the least-signficant 32 get patched into AML.
 | 
			
		||||
     */
 | 
			
		||||
    bios_linker_loader_add_pointer(linker,
 | 
			
		||||
        ACPI_BUILD_TABLE_FILE, vgia_offset, sizeof(uint32_t),
 | 
			
		||||
        VMGENID_GUID_FW_CFG_FILE, 0);
 | 
			
		||||
 | 
			
		||||
    build_header(linker, table_data,
 | 
			
		||||
        (void *)(table_data->data + table_data->len - ssdt->buf->len),
 | 
			
		||||
        "SSDT", ssdt->buf->len, 1, NULL, "VMGENID");
 | 
			
		||||
    free_aml_allocator();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void vmgenid_add_fw_cfg(VmGenIdState *vms, FWCfgState *s, GArray *guid)
 | 
			
		||||
{
 | 
			
		||||
    /* Create a read-only fw_cfg file for GUID */
 | 
			
		||||
    fw_cfg_add_file(s, VMGENID_GUID_FW_CFG_FILE, guid->data,
 | 
			
		||||
                    VMGENID_FW_CFG_SIZE);
 | 
			
		||||
    /* Create a read-write fw_cfg file for Address */
 | 
			
		||||
    fw_cfg_add_file_callback(s, VMGENID_ADDR_FW_CFG_FILE, NULL, NULL,
 | 
			
		||||
                             vms->vmgenid_addr_le,
 | 
			
		||||
                             ARRAY_SIZE(vms->vmgenid_addr_le), false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void vmgenid_update_guest(VmGenIdState *vms)
 | 
			
		||||
{
 | 
			
		||||
    Object *obj = object_resolve_path_type("", TYPE_ACPI_DEVICE_IF, NULL);
 | 
			
		||||
    uint32_t vmgenid_addr;
 | 
			
		||||
    QemuUUID guid_le;
 | 
			
		||||
 | 
			
		||||
    if (obj) {
 | 
			
		||||
        /* Write the GUID to guest memory */
 | 
			
		||||
        memcpy(&vmgenid_addr, vms->vmgenid_addr_le, sizeof(vmgenid_addr));
 | 
			
		||||
        vmgenid_addr = le32_to_cpu(vmgenid_addr);
 | 
			
		||||
        /* A zero value in vmgenid_addr means that BIOS has not yet written
 | 
			
		||||
         * the address
 | 
			
		||||
         */
 | 
			
		||||
        if (vmgenid_addr) {
 | 
			
		||||
            /* QemuUUID has the first three words as big-endian, and expect
 | 
			
		||||
             * that any GUIDs passed in will always be BE.  The guest,
 | 
			
		||||
             * however, will expect the fields to be little-endian.
 | 
			
		||||
             * Perform a byte swap immediately before writing.
 | 
			
		||||
             */
 | 
			
		||||
            guid_le = vms->guid;
 | 
			
		||||
            qemu_uuid_bswap(&guid_le);
 | 
			
		||||
            /* The GUID is written at a fixed offset into the fw_cfg file
 | 
			
		||||
             * in order to implement the "OVMF SDT Header probe suppressor"
 | 
			
		||||
             * see docs/specs/vmgenid.txt for more details.
 | 
			
		||||
             */
 | 
			
		||||
            cpu_physical_memory_write(vmgenid_addr, guid_le.data,
 | 
			
		||||
                                      sizeof(guid_le.data));
 | 
			
		||||
            /* Send _GPE.E05 event */
 | 
			
		||||
            acpi_send_event(DEVICE(obj), ACPI_VMGENID_CHANGE_STATUS);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void vmgenid_set_guid(Object *obj, const char *value, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    VmGenIdState *vms = VMGENID(obj);
 | 
			
		||||
 | 
			
		||||
    if (!strcmp(value, "auto")) {
 | 
			
		||||
        qemu_uuid_generate(&vms->guid);
 | 
			
		||||
    } else if (qemu_uuid_parse(value, &vms->guid) < 0) {
 | 
			
		||||
        error_setg(errp, "'%s. %s': Failed to parse GUID string: %s",
 | 
			
		||||
                   object_get_typename(OBJECT(vms)), VMGENID_GUID, value);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vmgenid_update_guest(vms);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* After restoring an image, we need to update the guest memory and notify
 | 
			
		||||
 * it of a potential change to VM Generation ID
 | 
			
		||||
 */
 | 
			
		||||
static int vmgenid_post_load(void *opaque, int version_id)
 | 
			
		||||
{
 | 
			
		||||
    VmGenIdState *vms = opaque;
 | 
			
		||||
    vmgenid_update_guest(vms);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const VMStateDescription vmstate_vmgenid = {
 | 
			
		||||
    .name = "vmgenid",
 | 
			
		||||
    .version_id = 1,
 | 
			
		||||
    .minimum_version_id = 1,
 | 
			
		||||
    .post_load = vmgenid_post_load,
 | 
			
		||||
    .fields = (VMStateField[]) {
 | 
			
		||||
        VMSTATE_UINT8_ARRAY(vmgenid_addr_le, VmGenIdState, sizeof(uint64_t)),
 | 
			
		||||
        VMSTATE_END_OF_LIST()
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void vmgenid_handle_reset(void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    VmGenIdState *vms = VMGENID(opaque);
 | 
			
		||||
    /* Clear the guest-allocated GUID address when the VM resets */
 | 
			
		||||
    memset(vms->vmgenid_addr_le, 0, ARRAY_SIZE(vms->vmgenid_addr_le));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void vmgenid_realize(DeviceState *dev, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    VmGenIdState *vms = VMGENID(dev);
 | 
			
		||||
    qemu_register_reset(vmgenid_handle_reset, vms);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void vmgenid_device_class_init(ObjectClass *klass, void *data)
 | 
			
		||||
{
 | 
			
		||||
    DeviceClass *dc = DEVICE_CLASS(klass);
 | 
			
		||||
 | 
			
		||||
    dc->vmsd = &vmstate_vmgenid;
 | 
			
		||||
    dc->realize = vmgenid_realize;
 | 
			
		||||
    dc->hotpluggable = false;
 | 
			
		||||
 | 
			
		||||
    object_class_property_add_str(klass, VMGENID_GUID, NULL,
 | 
			
		||||
                                  vmgenid_set_guid, NULL);
 | 
			
		||||
    object_class_property_set_description(klass, VMGENID_GUID,
 | 
			
		||||
                                    "Set Global Unique Identifier "
 | 
			
		||||
                                    "(big-endian) or auto for random value",
 | 
			
		||||
                                    NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const TypeInfo vmgenid_device_info = {
 | 
			
		||||
    .name          = VMGENID_DEVICE,
 | 
			
		||||
    .parent        = TYPE_DEVICE,
 | 
			
		||||
    .instance_size = sizeof(VmGenIdState),
 | 
			
		||||
    .class_init    = vmgenid_device_class_init,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void vmgenid_register_types(void)
 | 
			
		||||
{
 | 
			
		||||
    type_register_static(&vmgenid_device_info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type_init(vmgenid_register_types)
 | 
			
		||||
 | 
			
		||||
GuidInfo *qmp_query_vm_generation_id(Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    GuidInfo *info;
 | 
			
		||||
    VmGenIdState *vms;
 | 
			
		||||
    Object *obj = find_vmgenid_dev();
 | 
			
		||||
 | 
			
		||||
    if (!obj) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    vms = VMGENID(obj);
 | 
			
		||||
 | 
			
		||||
    info = g_malloc0(sizeof(*info));
 | 
			
		||||
    info->guid = qemu_uuid_unparse_strdup(&vms->guid);
 | 
			
		||||
    return info;
 | 
			
		||||
}
 | 
			
		||||
@ -42,6 +42,7 @@
 | 
			
		||||
#include "hw/acpi/memory_hotplug.h"
 | 
			
		||||
#include "sysemu/tpm.h"
 | 
			
		||||
#include "hw/acpi/tpm.h"
 | 
			
		||||
#include "hw/acpi/vmgenid.h"
 | 
			
		||||
#include "sysemu/tpm_backend.h"
 | 
			
		||||
#include "hw/timer/mc146818rtc_regs.h"
 | 
			
		||||
#include "sysemu/numa.h"
 | 
			
		||||
@ -1803,7 +1804,7 @@ static Aml *build_q35_osc_method(void)
 | 
			
		||||
    Aml *else_ctx;
 | 
			
		||||
    Aml *method;
 | 
			
		||||
    Aml *a_cwd1 = aml_name("CDW1");
 | 
			
		||||
    Aml *a_ctrl = aml_name("CTRL");
 | 
			
		||||
    Aml *a_ctrl = aml_local(0);
 | 
			
		||||
 | 
			
		||||
    method = aml_method("_OSC", 4, AML_NOTSERIALIZED);
 | 
			
		||||
    aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1"));
 | 
			
		||||
@ -1813,7 +1814,6 @@ static Aml *build_q35_osc_method(void)
 | 
			
		||||
    aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2"));
 | 
			
		||||
    aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3"));
 | 
			
		||||
 | 
			
		||||
    aml_append(if_ctx, aml_store(aml_name("CDW2"), aml_name("SUPP")));
 | 
			
		||||
    aml_append(if_ctx, aml_store(aml_name("CDW3"), a_ctrl));
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
@ -1898,8 +1898,6 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
 | 
			
		||||
        aml_append(dev, aml_name_decl("_CID", aml_eisaid("PNP0A03")));
 | 
			
		||||
        aml_append(dev, aml_name_decl("_ADR", aml_int(0)));
 | 
			
		||||
        aml_append(dev, aml_name_decl("_UID", aml_int(1)));
 | 
			
		||||
        aml_append(dev, aml_name_decl("SUPP", aml_int(0)));
 | 
			
		||||
        aml_append(dev, aml_name_decl("CTRL", aml_int(0)));
 | 
			
		||||
        aml_append(dev, build_q35_osc_method());
 | 
			
		||||
        aml_append(sb_scope, dev);
 | 
			
		||||
        aml_append(dsdt, sb_scope);
 | 
			
		||||
@ -1964,6 +1962,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
 | 
			
		||||
            aml_append(dev, aml_name_decl("_UID", aml_int(bus_num)));
 | 
			
		||||
            aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A03")));
 | 
			
		||||
            aml_append(dev, aml_name_decl("_BBN", aml_int(bus_num)));
 | 
			
		||||
            if (pci_bus_is_express(bus)) {
 | 
			
		||||
                aml_append(dev, build_q35_osc_method());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (numa_node != NUMA_NODE_UNASSIGNED) {
 | 
			
		||||
                aml_append(dev, aml_name_decl("_PXM", aml_int(numa_node)));
 | 
			
		||||
@ -2610,6 +2611,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
 | 
			
		||||
    size_t aml_len = 0;
 | 
			
		||||
    GArray *tables_blob = tables->table_data;
 | 
			
		||||
    AcpiSlicOem slic_oem = { .id = NULL, .table_id = NULL };
 | 
			
		||||
    Object *vmgenid_dev;
 | 
			
		||||
 | 
			
		||||
    acpi_get_pm_info(&pm);
 | 
			
		||||
    acpi_get_misc_info(&misc);
 | 
			
		||||
@ -2653,6 +2655,13 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
 | 
			
		||||
    acpi_add_table(table_offsets, tables_blob);
 | 
			
		||||
    build_madt(tables_blob, tables->linker, pcms);
 | 
			
		||||
 | 
			
		||||
    vmgenid_dev = find_vmgenid_dev();
 | 
			
		||||
    if (vmgenid_dev) {
 | 
			
		||||
        acpi_add_table(table_offsets, tables_blob);
 | 
			
		||||
        vmgenid_build_acpi(VMGENID(vmgenid_dev), tables_blob,
 | 
			
		||||
                           tables->vmgenid, tables->linker);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (misc.has_hpet) {
 | 
			
		||||
        acpi_add_table(table_offsets, tables_blob);
 | 
			
		||||
        build_hpet(tables_blob, tables->linker);
 | 
			
		||||
@ -2823,6 +2832,7 @@ void acpi_setup(void)
 | 
			
		||||
    PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
 | 
			
		||||
    AcpiBuildTables tables;
 | 
			
		||||
    AcpiBuildState *build_state;
 | 
			
		||||
    Object *vmgenid_dev;
 | 
			
		||||
 | 
			
		||||
    if (!pcms->fw_cfg) {
 | 
			
		||||
        ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n");
 | 
			
		||||
@ -2859,6 +2869,12 @@ void acpi_setup(void)
 | 
			
		||||
    fw_cfg_add_file(pcms->fw_cfg, ACPI_BUILD_TPMLOG_FILE,
 | 
			
		||||
                    tables.tcpalog->data, acpi_data_len(tables.tcpalog));
 | 
			
		||||
 | 
			
		||||
    vmgenid_dev = find_vmgenid_dev();
 | 
			
		||||
    if (vmgenid_dev) {
 | 
			
		||||
        vmgenid_add_fw_cfg(VMGENID(vmgenid_dev), pcms->fw_cfg,
 | 
			
		||||
                           tables.vmgenid);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!pcmc->rsdp_in_ram) {
 | 
			
		||||
        /*
 | 
			
		||||
         * Keep for compatibility with old machine types.
 | 
			
		||||
 | 
			
		||||
@ -1153,7 +1153,7 @@ static AddressSpace *virtio_pci_get_dma_as(DeviceState *d)
 | 
			
		||||
    VirtIOPCIProxy *proxy = VIRTIO_PCI(d);
 | 
			
		||||
    PCIDevice *dev = &proxy->pci_dev;
 | 
			
		||||
 | 
			
		||||
    return pci_get_address_space(dev);
 | 
			
		||||
    return pci_device_iommu_address_space(dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int virtio_pci_add_mem_cap(VirtIOPCIProxy *proxy,
 | 
			
		||||
 | 
			
		||||
@ -282,12 +282,17 @@ static inline void vring_set_avail_event(VirtQueue *vq, uint16_t val)
 | 
			
		||||
    caches = atomic_rcu_read(&vq->vring.caches);
 | 
			
		||||
    pa = offsetof(VRingUsed, ring[vq->vring.num]);
 | 
			
		||||
    virtio_stw_phys_cached(vq->vdev, &caches->used, pa, val);
 | 
			
		||||
    address_space_cache_invalidate(&caches->used, pa, sizeof(val));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void virtio_queue_set_notification(VirtQueue *vq, int enable)
 | 
			
		||||
{
 | 
			
		||||
    vq->notification = enable;
 | 
			
		||||
 | 
			
		||||
    if (!vq->vring.desc) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rcu_read_lock();
 | 
			
		||||
    if (virtio_vdev_has_feature(vq->vdev, VIRTIO_RING_F_EVENT_IDX)) {
 | 
			
		||||
        vring_set_avail_event(vq, vring_avail_idx(vq));
 | 
			
		||||
@ -1852,7 +1857,10 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f)
 | 
			
		||||
        if (k->has_variable_vring_alignment) {
 | 
			
		||||
            qemu_put_be32(f, vdev->vq[i].vring.align);
 | 
			
		||||
        }
 | 
			
		||||
        /* XXX virtio-1 devices */
 | 
			
		||||
        /*
 | 
			
		||||
         * Save desc now, the rest of the ring addresses are saved in
 | 
			
		||||
         * subsections for VIRTIO-1 devices.
 | 
			
		||||
         */
 | 
			
		||||
        qemu_put_be64(f, vdev->vq[i].vring.desc);
 | 
			
		||||
        qemu_put_be16s(f, &vdev->vq[i].last_avail_idx);
 | 
			
		||||
        if (k->save_queue) {
 | 
			
		||||
@ -1993,10 +2001,7 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
 | 
			
		||||
        vdev->vq[i].signalled_used_valid = false;
 | 
			
		||||
        vdev->vq[i].notification = true;
 | 
			
		||||
 | 
			
		||||
        if (vdev->vq[i].vring.desc) {
 | 
			
		||||
            /* XXX virtio-1 devices */
 | 
			
		||||
            virtio_queue_update_rings(vdev, i);
 | 
			
		||||
        } else if (vdev->vq[i].last_avail_idx) {
 | 
			
		||||
        if (!vdev->vq[i].vring.desc && vdev->vq[i].last_avail_idx) {
 | 
			
		||||
            error_report("VQ %d address 0x0 "
 | 
			
		||||
                         "inconsistent with Host index 0x%x",
 | 
			
		||||
                         i, vdev->vq[i].last_avail_idx);
 | 
			
		||||
@ -2061,6 +2066,19 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
 | 
			
		||||
    for (i = 0; i < num; i++) {
 | 
			
		||||
        if (vdev->vq[i].vring.desc) {
 | 
			
		||||
            uint16_t nheads;
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
             * VIRTIO-1 devices migrate desc, used, and avail ring addresses so
 | 
			
		||||
             * only the region cache needs to be set up.  Legacy devices need
 | 
			
		||||
             * to calculate used and avail ring addresses based on the desc
 | 
			
		||||
             * address.
 | 
			
		||||
             */
 | 
			
		||||
            if (virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) {
 | 
			
		||||
                virtio_init_region_cache(vdev, i);
 | 
			
		||||
            } else {
 | 
			
		||||
                virtio_queue_update_rings(vdev, i);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            nheads = vring_avail_idx(&vdev->vq[i]) - vdev->vq[i].last_avail_idx;
 | 
			
		||||
            /* Check it isn't doing strange things with descriptor numbers. */
 | 
			
		||||
            if (nheads > vdev->vq[i].vring.num) {
 | 
			
		||||
@ -2291,7 +2309,7 @@ static bool virtio_queue_host_notifier_aio_poll(void *opaque)
 | 
			
		||||
    VirtQueue *vq = container_of(n, VirtQueue, host_notifier);
 | 
			
		||||
    bool progress;
 | 
			
		||||
 | 
			
		||||
    if (virtio_queue_empty(vq)) {
 | 
			
		||||
    if (!vq->vring.desc || virtio_queue_empty(vq)) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,7 @@ typedef enum {
 | 
			
		||||
    ACPI_CPU_HOTPLUG_STATUS = 4,
 | 
			
		||||
    ACPI_MEMORY_HOTPLUG_STATUS = 8,
 | 
			
		||||
    ACPI_NVDIMM_HOTPLUG_STATUS = 16,
 | 
			
		||||
    ACPI_VMGENID_CHANGE_STATUS = 32,
 | 
			
		||||
} AcpiEventStatusBits;
 | 
			
		||||
 | 
			
		||||
#define TYPE_ACPI_DEVICE_IF "acpi-device-interface"
 | 
			
		||||
 | 
			
		||||
@ -210,6 +210,7 @@ struct AcpiBuildTables {
 | 
			
		||||
    GArray *table_data;
 | 
			
		||||
    GArray *rsdp;
 | 
			
		||||
    GArray *tcpalog;
 | 
			
		||||
    GArray *vmgenid;
 | 
			
		||||
    BIOSLinker *linker;
 | 
			
		||||
} AcpiBuildTables;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -26,5 +26,12 @@ void bios_linker_loader_add_pointer(BIOSLinker *linker,
 | 
			
		||||
                                    const char *src_file,
 | 
			
		||||
                                    uint32_t src_offset);
 | 
			
		||||
 | 
			
		||||
void bios_linker_loader_write_pointer(BIOSLinker *linker,
 | 
			
		||||
                                      const char *dest_file,
 | 
			
		||||
                                      uint32_t dst_patched_offset,
 | 
			
		||||
                                      uint8_t dst_patched_size,
 | 
			
		||||
                                      const char *src_file,
 | 
			
		||||
                                      uint32_t src_offset);
 | 
			
		||||
 | 
			
		||||
void bios_linker_loader_cleanup(BIOSLinker *linker);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										35
									
								
								include/hw/acpi/vmgenid.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								include/hw/acpi/vmgenid.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
			
		||||
#ifndef ACPI_VMGENID_H
 | 
			
		||||
#define ACPI_VMGENID_H
 | 
			
		||||
 | 
			
		||||
#include "hw/acpi/bios-linker-loader.h"
 | 
			
		||||
#include "hw/qdev.h"
 | 
			
		||||
#include "qemu/uuid.h"
 | 
			
		||||
 | 
			
		||||
#define VMGENID_DEVICE           "vmgenid"
 | 
			
		||||
#define VMGENID_GUID             "guid"
 | 
			
		||||
#define VMGENID_GUID_FW_CFG_FILE      "etc/vmgenid_guid"
 | 
			
		||||
#define VMGENID_ADDR_FW_CFG_FILE      "etc/vmgenid_addr"
 | 
			
		||||
 | 
			
		||||
#define VMGENID_FW_CFG_SIZE      4096 /* Occupy a page of memory */
 | 
			
		||||
#define VMGENID_GUID_OFFSET      40   /* allow space for
 | 
			
		||||
                                       * OVMF SDT Header Probe Supressor
 | 
			
		||||
                                       */
 | 
			
		||||
 | 
			
		||||
#define VMGENID(obj) OBJECT_CHECK(VmGenIdState, (obj), VMGENID_DEVICE)
 | 
			
		||||
 | 
			
		||||
typedef struct VmGenIdState {
 | 
			
		||||
    DeviceClass parent_obj;
 | 
			
		||||
    QemuUUID guid;                /* The 128-bit GUID seen by the guest */
 | 
			
		||||
    uint8_t vmgenid_addr_le[8];   /* Address of the GUID (little-endian) */
 | 
			
		||||
} VmGenIdState;
 | 
			
		||||
 | 
			
		||||
static inline Object *find_vmgenid_dev(void)
 | 
			
		||||
{
 | 
			
		||||
    return object_resolve_path_type("", VMGENID_DEVICE, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void vmgenid_build_acpi(VmGenIdState *vms, GArray *table_data, GArray *guid,
 | 
			
		||||
                        BIOSLinker *linker);
 | 
			
		||||
void vmgenid_add_fw_cfg(VmGenIdState *vms, FWCfgState *s, GArray *guid);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@ -6197,3 +6197,23 @@
 | 
			
		||||
#
 | 
			
		||||
##
 | 
			
		||||
{ 'command': 'query-hotpluggable-cpus', 'returns': ['HotpluggableCPU'] }
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
# @GuidInfo:
 | 
			
		||||
#
 | 
			
		||||
# GUID information.
 | 
			
		||||
#
 | 
			
		||||
# @guid: the globally unique identifier
 | 
			
		||||
#
 | 
			
		||||
# Since: 2.9
 | 
			
		||||
##
 | 
			
		||||
{ 'struct': 'GuidInfo', 'data': {'guid': 'str'} }
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
# @query-vm-generation-id:
 | 
			
		||||
#
 | 
			
		||||
# Show Virtual Machine Generation ID
 | 
			
		||||
#
 | 
			
		||||
# Since 2.9
 | 
			
		||||
##
 | 
			
		||||
{ 'command': 'query-vm-generation-id', 'returns': 'GuidInfo' }
 | 
			
		||||
 | 
			
		||||
@ -36,3 +36,4 @@ stub-obj-y += qmp_pc_dimm_device_list.o
 | 
			
		||||
stub-obj-y += target-monitor-defs.o
 | 
			
		||||
stub-obj-y += target-get-monitor-def.o
 | 
			
		||||
stub-obj-y += pc_madt_cpu_entry.o
 | 
			
		||||
stub-obj-y += vmgenid.o
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								stubs/vmgenid.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								stubs/vmgenid.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
#include "qmp-commands.h"
 | 
			
		||||
#include "qapi/qmp/qerror.h"
 | 
			
		||||
 | 
			
		||||
GuidInfo *qmp_query_vm_generation_id(Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    error_setg(errp, QERR_UNSUPPORTED);
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
@ -669,7 +669,7 @@ tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o
 | 
			
		||||
tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y)
 | 
			
		||||
tests/boot-serial-test$(EXESUF): tests/boot-serial-test.o $(libqos-obj-y)
 | 
			
		||||
tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o \
 | 
			
		||||
	tests/boot-sector.o $(libqos-obj-y)
 | 
			
		||||
	tests/boot-sector.o tests/acpi-utils.o $(libqos-obj-y)
 | 
			
		||||
tests/pxe-test$(EXESUF): tests/pxe-test.o tests/boot-sector.o $(libqos-obj-y)
 | 
			
		||||
tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y)
 | 
			
		||||
tests/ds1338-test$(EXESUF): tests/ds1338-test.o $(libqos-imx-obj-y)
 | 
			
		||||
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										65
									
								
								tests/acpi-utils.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								tests/acpi-utils.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,65 @@
 | 
			
		||||
/*
 | 
			
		||||
 * ACPI Utility Functions
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2013 Red Hat Inc.
 | 
			
		||||
 * Copyright (c) 2017 Skyport Systems
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *  Michael S. Tsirkin <mst@redhat.com>,
 | 
			
		||||
 *  Ben Warren <ben@skyportsystems.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 "qemu/osdep.h"
 | 
			
		||||
#include <glib/gstdio.h>
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "hw/smbios/smbios.h"
 | 
			
		||||
#include "qemu/bitmap.h"
 | 
			
		||||
#include "acpi-utils.h"
 | 
			
		||||
#include "boot-sector.h"
 | 
			
		||||
 | 
			
		||||
uint8_t acpi_calc_checksum(const uint8_t *data, int len)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    uint8_t sum = 0;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < len; i++) {
 | 
			
		||||
        sum += data[i];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return sum;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t acpi_find_rsdp_address(void)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t off;
 | 
			
		||||
 | 
			
		||||
    /* RSDP location can vary across a narrow range */
 | 
			
		||||
    for (off = 0xf0000; off < 0x100000; off += 0x10) {
 | 
			
		||||
        uint8_t sig[] = "RSD PTR ";
 | 
			
		||||
        int i;
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < sizeof sig - 1; ++i) {
 | 
			
		||||
            sig[i] = readb(off + i);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!memcmp(sig, "RSD PTR ", sizeof sig)) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return off;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void acpi_parse_rsdp_table(uint32_t addr, AcpiRsdpDescriptor *rsdp_table)
 | 
			
		||||
{
 | 
			
		||||
    ACPI_READ_FIELD(rsdp_table->signature, addr);
 | 
			
		||||
    ACPI_ASSERT_CMP64(rsdp_table->signature, "RSD PTR ");
 | 
			
		||||
 | 
			
		||||
    ACPI_READ_FIELD(rsdp_table->checksum, addr);
 | 
			
		||||
    ACPI_READ_ARRAY(rsdp_table->oem_id, addr);
 | 
			
		||||
    ACPI_READ_FIELD(rsdp_table->revision, addr);
 | 
			
		||||
    ACPI_READ_FIELD(rsdp_table->rsdt_physical_address, addr);
 | 
			
		||||
    ACPI_READ_FIELD(rsdp_table->length, addr);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										94
									
								
								tests/acpi-utils.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								tests/acpi-utils.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,94 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Utilities for working with ACPI tables
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2013 Red Hat Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *  Michael S. Tsirkin <mst@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 TEST_ACPI_UTILS_H
 | 
			
		||||
#define TEST_ACPI_UTILS_H
 | 
			
		||||
 | 
			
		||||
#include "hw/acpi/acpi-defs.h"
 | 
			
		||||
#include "libqtest.h"
 | 
			
		||||
 | 
			
		||||
/* DSDT and SSDTs format */
 | 
			
		||||
typedef struct {
 | 
			
		||||
    AcpiTableHeader header;
 | 
			
		||||
    gchar *aml;            /* aml bytecode from guest */
 | 
			
		||||
    gsize aml_len;
 | 
			
		||||
    gchar *aml_file;
 | 
			
		||||
    gchar *asl;            /* asl code generated from aml */
 | 
			
		||||
    gsize asl_len;
 | 
			
		||||
    gchar *asl_file;
 | 
			
		||||
    bool tmp_files_retain;   /* do not delete the temp asl/aml */
 | 
			
		||||
} QEMU_PACKED AcpiSdtTable;
 | 
			
		||||
 | 
			
		||||
#define ACPI_READ_FIELD(field, addr)           \
 | 
			
		||||
    do {                                       \
 | 
			
		||||
        switch (sizeof(field)) {               \
 | 
			
		||||
        case 1:                                \
 | 
			
		||||
            field = readb(addr);               \
 | 
			
		||||
            break;                             \
 | 
			
		||||
        case 2:                                \
 | 
			
		||||
            field = readw(addr);               \
 | 
			
		||||
            break;                             \
 | 
			
		||||
        case 4:                                \
 | 
			
		||||
            field = readl(addr);               \
 | 
			
		||||
            break;                             \
 | 
			
		||||
        case 8:                                \
 | 
			
		||||
            field = readq(addr);               \
 | 
			
		||||
            break;                             \
 | 
			
		||||
        default:                               \
 | 
			
		||||
            g_assert(false);                   \
 | 
			
		||||
        }                                      \
 | 
			
		||||
        addr += sizeof(field);                  \
 | 
			
		||||
    } while (0);
 | 
			
		||||
 | 
			
		||||
#define ACPI_READ_ARRAY_PTR(arr, length, addr)  \
 | 
			
		||||
    do {                                        \
 | 
			
		||||
        int idx;                                \
 | 
			
		||||
        for (idx = 0; idx < length; ++idx) {    \
 | 
			
		||||
            ACPI_READ_FIELD(arr[idx], addr);    \
 | 
			
		||||
        }                                       \
 | 
			
		||||
    } while (0);
 | 
			
		||||
 | 
			
		||||
#define ACPI_READ_ARRAY(arr, addr)                               \
 | 
			
		||||
    ACPI_READ_ARRAY_PTR(arr, sizeof(arr) / sizeof(arr[0]), addr)
 | 
			
		||||
 | 
			
		||||
#define ACPI_READ_TABLE_HEADER(table, addr)                      \
 | 
			
		||||
    do {                                                         \
 | 
			
		||||
        ACPI_READ_FIELD((table)->signature, addr);               \
 | 
			
		||||
        ACPI_READ_FIELD((table)->length, addr);                  \
 | 
			
		||||
        ACPI_READ_FIELD((table)->revision, addr);                \
 | 
			
		||||
        ACPI_READ_FIELD((table)->checksum, addr);                \
 | 
			
		||||
        ACPI_READ_ARRAY((table)->oem_id, addr);                  \
 | 
			
		||||
        ACPI_READ_ARRAY((table)->oem_table_id, addr);            \
 | 
			
		||||
        ACPI_READ_FIELD((table)->oem_revision, addr);            \
 | 
			
		||||
        ACPI_READ_ARRAY((table)->asl_compiler_id, addr);         \
 | 
			
		||||
        ACPI_READ_FIELD((table)->asl_compiler_revision, addr);   \
 | 
			
		||||
    } while (0);
 | 
			
		||||
 | 
			
		||||
#define ACPI_ASSERT_CMP(actual, expected) do { \
 | 
			
		||||
    uint32_t ACPI_ASSERT_CMP_le = cpu_to_le32(actual); \
 | 
			
		||||
    char ACPI_ASSERT_CMP_str[5] = {}; \
 | 
			
		||||
    memcpy(ACPI_ASSERT_CMP_str, &ACPI_ASSERT_CMP_le, 4); \
 | 
			
		||||
    g_assert_cmpstr(ACPI_ASSERT_CMP_str, ==, expected); \
 | 
			
		||||
} while (0)
 | 
			
		||||
 | 
			
		||||
#define ACPI_ASSERT_CMP64(actual, expected) do { \
 | 
			
		||||
    uint64_t ACPI_ASSERT_CMP_le = cpu_to_le64(actual); \
 | 
			
		||||
    char ACPI_ASSERT_CMP_str[9] = {}; \
 | 
			
		||||
    memcpy(ACPI_ASSERT_CMP_str, &ACPI_ASSERT_CMP_le, 8); \
 | 
			
		||||
    g_assert_cmpstr(ACPI_ASSERT_CMP_str, ==, expected); \
 | 
			
		||||
} while (0)
 | 
			
		||||
 | 
			
		||||
uint8_t acpi_calc_checksum(const uint8_t *data, int len);
 | 
			
		||||
uint32_t acpi_find_rsdp_address(void);
 | 
			
		||||
void acpi_parse_rsdp_table(uint32_t addr, AcpiRsdpDescriptor *rsdp_table);
 | 
			
		||||
 | 
			
		||||
#endif  /* TEST_ACPI_UTILS_H */
 | 
			
		||||
@ -13,10 +13,9 @@
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
#include <glib/gstdio.h>
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "libqtest.h"
 | 
			
		||||
#include "hw/acpi/acpi-defs.h"
 | 
			
		||||
#include "hw/smbios/smbios.h"
 | 
			
		||||
#include "qemu/bitmap.h"
 | 
			
		||||
#include "acpi-utils.h"
 | 
			
		||||
#include "boot-sector.h"
 | 
			
		||||
 | 
			
		||||
#define MACHINE_PC "pc"
 | 
			
		||||
@ -24,18 +23,6 @@
 | 
			
		||||
 | 
			
		||||
#define ACPI_REBUILD_EXPECTED_AML "TEST_ACPI_REBUILD_AML"
 | 
			
		||||
 | 
			
		||||
/* DSDT and SSDTs format */
 | 
			
		||||
typedef struct {
 | 
			
		||||
    AcpiTableHeader header;
 | 
			
		||||
    gchar *aml;            /* aml bytecode from guest */
 | 
			
		||||
    gsize aml_len;
 | 
			
		||||
    gchar *aml_file;
 | 
			
		||||
    gchar *asl;            /* asl code generated from aml */
 | 
			
		||||
    gsize asl_len;
 | 
			
		||||
    gchar *asl_file;
 | 
			
		||||
    bool tmp_files_retain;   /* do not delete the temp asl/aml */
 | 
			
		||||
} QEMU_PACKED AcpiSdtTable;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    const char *machine;
 | 
			
		||||
    const char *variant;
 | 
			
		||||
@ -53,65 +40,6 @@ typedef struct {
 | 
			
		||||
    int required_struct_types_len;
 | 
			
		||||
} test_data;
 | 
			
		||||
 | 
			
		||||
#define ACPI_READ_FIELD(field, addr)           \
 | 
			
		||||
    do {                                       \
 | 
			
		||||
        switch (sizeof(field)) {               \
 | 
			
		||||
        case 1:                                \
 | 
			
		||||
            field = readb(addr);               \
 | 
			
		||||
            break;                             \
 | 
			
		||||
        case 2:                                \
 | 
			
		||||
            field = readw(addr);               \
 | 
			
		||||
            break;                             \
 | 
			
		||||
        case 4:                                \
 | 
			
		||||
            field = readl(addr);               \
 | 
			
		||||
            break;                             \
 | 
			
		||||
        case 8:                                \
 | 
			
		||||
            field = readq(addr);               \
 | 
			
		||||
            break;                             \
 | 
			
		||||
        default:                               \
 | 
			
		||||
            g_assert(false);                   \
 | 
			
		||||
        }                                      \
 | 
			
		||||
        addr += sizeof(field);                  \
 | 
			
		||||
    } while (0);
 | 
			
		||||
 | 
			
		||||
#define ACPI_READ_ARRAY_PTR(arr, length, addr)  \
 | 
			
		||||
    do {                                        \
 | 
			
		||||
        int idx;                                \
 | 
			
		||||
        for (idx = 0; idx < length; ++idx) {    \
 | 
			
		||||
            ACPI_READ_FIELD(arr[idx], addr);    \
 | 
			
		||||
        }                                       \
 | 
			
		||||
    } while (0);
 | 
			
		||||
 | 
			
		||||
#define ACPI_READ_ARRAY(arr, addr)                               \
 | 
			
		||||
    ACPI_READ_ARRAY_PTR(arr, sizeof(arr)/sizeof(arr[0]), addr)
 | 
			
		||||
 | 
			
		||||
#define ACPI_READ_TABLE_HEADER(table, addr)                      \
 | 
			
		||||
    do {                                                         \
 | 
			
		||||
        ACPI_READ_FIELD((table)->signature, addr);               \
 | 
			
		||||
        ACPI_READ_FIELD((table)->length, addr);                  \
 | 
			
		||||
        ACPI_READ_FIELD((table)->revision, addr);                \
 | 
			
		||||
        ACPI_READ_FIELD((table)->checksum, addr);                \
 | 
			
		||||
        ACPI_READ_ARRAY((table)->oem_id, addr);                  \
 | 
			
		||||
        ACPI_READ_ARRAY((table)->oem_table_id, addr);            \
 | 
			
		||||
        ACPI_READ_FIELD((table)->oem_revision, addr);            \
 | 
			
		||||
        ACPI_READ_ARRAY((table)->asl_compiler_id, addr);         \
 | 
			
		||||
        ACPI_READ_FIELD((table)->asl_compiler_revision, addr);   \
 | 
			
		||||
    } while (0);
 | 
			
		||||
 | 
			
		||||
#define ACPI_ASSERT_CMP(actual, expected) do { \
 | 
			
		||||
    uint32_t ACPI_ASSERT_CMP_le = cpu_to_le32(actual); \
 | 
			
		||||
    char ACPI_ASSERT_CMP_str[5] = {}; \
 | 
			
		||||
    memcpy(ACPI_ASSERT_CMP_str, &ACPI_ASSERT_CMP_le, 4); \
 | 
			
		||||
    g_assert_cmpstr(ACPI_ASSERT_CMP_str, ==, expected); \
 | 
			
		||||
} while (0)
 | 
			
		||||
 | 
			
		||||
#define ACPI_ASSERT_CMP64(actual, expected) do { \
 | 
			
		||||
    uint64_t ACPI_ASSERT_CMP_le = cpu_to_le64(actual); \
 | 
			
		||||
    char ACPI_ASSERT_CMP_str[9] = {}; \
 | 
			
		||||
    memcpy(ACPI_ASSERT_CMP_str, &ACPI_ASSERT_CMP_le, 8); \
 | 
			
		||||
    g_assert_cmpstr(ACPI_ASSERT_CMP_str, ==, expected); \
 | 
			
		||||
} while (0)
 | 
			
		||||
 | 
			
		||||
static char disk[] = "tests/acpi-test-disk-XXXXXX";
 | 
			
		||||
static const char *data_dir = "tests/acpi-test-data";
 | 
			
		||||
#ifdef CONFIG_IASL
 | 
			
		||||
@ -147,36 +75,9 @@ static void free_test_data(test_data *data)
 | 
			
		||||
    g_array_free(data->tables, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint8_t acpi_checksum(const uint8_t *data, int len)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    uint8_t sum = 0;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < len; i++) {
 | 
			
		||||
        sum += data[i];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return sum;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_acpi_rsdp_address(test_data *data)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t off;
 | 
			
		||||
 | 
			
		||||
    /* OK, now find RSDP */
 | 
			
		||||
    for (off = 0xf0000; off < 0x100000; off += 0x10) {
 | 
			
		||||
        uint8_t sig[] = "RSD PTR ";
 | 
			
		||||
        int i;
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < sizeof sig - 1; ++i) {
 | 
			
		||||
            sig[i] = readb(off + i);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!memcmp(sig, "RSD PTR ", sizeof sig)) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint32_t off = acpi_find_rsdp_address();
 | 
			
		||||
    g_assert_cmphex(off, <, 0x100000);
 | 
			
		||||
    data->rsdp_addr = off;
 | 
			
		||||
}
 | 
			
		||||
@ -186,17 +87,10 @@ static void test_acpi_rsdp_table(test_data *data)
 | 
			
		||||
    AcpiRsdpDescriptor *rsdp_table = &data->rsdp_table;
 | 
			
		||||
    uint32_t addr = data->rsdp_addr;
 | 
			
		||||
 | 
			
		||||
    ACPI_READ_FIELD(rsdp_table->signature, addr);
 | 
			
		||||
    ACPI_ASSERT_CMP64(rsdp_table->signature, "RSD PTR ");
 | 
			
		||||
 | 
			
		||||
    ACPI_READ_FIELD(rsdp_table->checksum, addr);
 | 
			
		||||
    ACPI_READ_ARRAY(rsdp_table->oem_id, addr);
 | 
			
		||||
    ACPI_READ_FIELD(rsdp_table->revision, addr);
 | 
			
		||||
    ACPI_READ_FIELD(rsdp_table->rsdt_physical_address, addr);
 | 
			
		||||
    ACPI_READ_FIELD(rsdp_table->length, addr);
 | 
			
		||||
    acpi_parse_rsdp_table(addr, rsdp_table);
 | 
			
		||||
 | 
			
		||||
    /* rsdp checksum is not for the whole table, but for the first 20 bytes */
 | 
			
		||||
    g_assert(!acpi_checksum((uint8_t *)rsdp_table, 20));
 | 
			
		||||
    g_assert(!acpi_calc_checksum((uint8_t *)rsdp_table, 20));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_acpi_rsdt_table(test_data *data)
 | 
			
		||||
@ -220,8 +114,9 @@ static void test_acpi_rsdt_table(test_data *data)
 | 
			
		||||
    tables = g_new0(uint32_t, tables_nr);
 | 
			
		||||
    ACPI_READ_ARRAY_PTR(tables, tables_nr, addr);
 | 
			
		||||
 | 
			
		||||
    checksum = acpi_checksum((uint8_t *)rsdt_table, rsdt_table->length) +
 | 
			
		||||
               acpi_checksum((uint8_t *)tables, tables_nr * sizeof(uint32_t));
 | 
			
		||||
    checksum = acpi_calc_checksum((uint8_t *)rsdt_table, rsdt_table->length) +
 | 
			
		||||
               acpi_calc_checksum((uint8_t *)tables,
 | 
			
		||||
                                  tables_nr * sizeof(uint32_t));
 | 
			
		||||
    g_assert(!checksum);
 | 
			
		||||
 | 
			
		||||
   /* SSDT tables after FADT */
 | 
			
		||||
@ -279,7 +174,7 @@ static void test_acpi_fadt_table(test_data *data)
 | 
			
		||||
    ACPI_READ_FIELD(fadt_table->flags, addr);
 | 
			
		||||
 | 
			
		||||
    ACPI_ASSERT_CMP(fadt_table->signature, "FACP");
 | 
			
		||||
    g_assert(!acpi_checksum((uint8_t *)fadt_table, fadt_table->length));
 | 
			
		||||
    g_assert(!acpi_calc_checksum((uint8_t *)fadt_table, fadt_table->length));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_acpi_facs_table(test_data *data)
 | 
			
		||||
@ -308,8 +203,10 @@ static void test_dst_table(AcpiSdtTable *sdt_table, uint32_t addr)
 | 
			
		||||
    sdt_table->aml = g_malloc0(sdt_table->aml_len);
 | 
			
		||||
    ACPI_READ_ARRAY_PTR(sdt_table->aml, sdt_table->aml_len, addr);
 | 
			
		||||
 | 
			
		||||
    checksum = acpi_checksum((uint8_t *)sdt_table, sizeof(AcpiTableHeader)) +
 | 
			
		||||
               acpi_checksum((uint8_t *)sdt_table->aml, sdt_table->aml_len);
 | 
			
		||||
    checksum = acpi_calc_checksum((uint8_t *)sdt_table,
 | 
			
		||||
                                  sizeof(AcpiTableHeader)) +
 | 
			
		||||
               acpi_calc_checksum((uint8_t *)sdt_table->aml,
 | 
			
		||||
                                  sdt_table->aml_len);
 | 
			
		||||
    g_assert(!checksum);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -608,8 +505,9 @@ static bool smbios_ep_table_ok(test_data *data)
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    ACPI_READ_FIELD(ep_table->smbios_bcd_revision, addr);
 | 
			
		||||
    if (acpi_checksum((uint8_t *)ep_table, sizeof *ep_table) ||
 | 
			
		||||
        acpi_checksum((uint8_t *)ep_table + 0x10, sizeof *ep_table - 0x10)) {
 | 
			
		||||
    if (acpi_calc_checksum((uint8_t *)ep_table, sizeof *ep_table) ||
 | 
			
		||||
        acpi_calc_checksum((uint8_t *)ep_table + 0x10,
 | 
			
		||||
                           sizeof *ep_table - 0x10)) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user