All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID
@ 2017-02-15  6:15 ben
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command ben
                   ` (7 more replies)
  0 siblings, 8 replies; 62+ messages in thread
From: ben @ 2017-02-15  6:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: lersek, mst, imammedo, Ben Warren

From: Ben Warren <ben@skyportsystems.com>

This patch set adds support for passing a GUID to Windows guests.  It is a
re-implementation of previous patch sets written by Igor Mammedov et al, but
this time passing the GUID data as a fw_cfg blob.

This patch set has dependencies on new guest functionality, in particular the
support for a new linker-loader command and the ability to write back data
to QEMU over a DMA link.  Work is in flight in both SeaBIOS and OVMF to support this.

v5->v6:
    - Rebased to top of tree.
    - Changed device from sysbus to a simple device.  This removed the need for
      adding dynamic sysbus support to pc_piix boards.
    - Removed patch that introduced QWORD patching of AML.
    - Removed ability to set GUID via QMP/HMP.
    - Improved comments/documentation in code.

v4->v5:
    - Added significantly more detail to the documentation.
    - Replaced the previously-implemented linker-loader command with a new one:
      "write pointer".  This allows writing the guest address of a fw_cfg blob back
      to an arbitrary offset in a writeable fw_cfg file visible to QEMU.  This will
      require support in SeaBIOS and OVMF (ongoing).
    - Fixed endianness issues throughout.
    - Several styling cleanups.

v3->v4:
    - Rebased to top of tree.
    - Re-added document patch that was accidentally dropped from the last revision.
    - Added VMState functionality so that VGIA is restored properly.
    - Added Unit tests
v2->v3:
    - Added second writeable fw_cfg for storing the VM Generaiton ID
      address.  This uses a new linker-loader command for instructing the
      guest to write back the allocated address.  A patch for SeaBIOS has been
      submitted (https://www.seabios.org/pipermail/seabios/2017-January/011079.html)
      and the resulting binary will need to be pulled into QEMU once accepted.
    - Setting VM Generation ID by command line or qmp/hmp now accepts an "auto"
      value, whereby QEMU generates a random GUID.
    - Incorporated review comments from v2 mainly around code styling and AML syntax
    - Changed to use the E05 ACPI event instead of E00
v1->v2:
    - Removed "changed" boolean parameter as it is unneeded
    - Added ACPI Notify logic
    - Style changes to pass checkpatch.pl
    - Added support for dynamic sysbus to pc_piix boards

This patch set adds support for passing a GUID to Windows guests.  It is a
re-implementation of previous patch sets written by Igor Mammedov et al, but
this time passing the GUID data as a fw_cfg blob.

This patch set has dependencies on new guest functionality, in particular the
support for a new linker-loader command and the ability to write back data
to QEMU over a DMA link.  Work is in flight in both SeaBIOS and OVMF to support this.

v5->v6:
    - Rebased to top of tree.
    - Changed device from sysbus to a simple device.  This removed the need for
      adding dynamic sysbus support to pc_piix boards.
    - Removed patch that introduced QWORD patching of AML.
    - Removed ability to set GUID via QMP/HMP.
    - Improved comments/documentation in code.

v4->v5:
    - Added significantly more detail to the documentation.
    - Replaced the previously-implemented linker-loader command with a new one:
      "write pointer".  This allows writing the guest address of a fw_cfg blob back
      to an arbitrary offset in a writeable fw_cfg file visible to QEMU.  This will
      require support in SeaBIOS and OVMF (ongoing).
    - Fixed endianness issues throughout.
    - Several styling cleanups.

v3->v4:
    - Rebased to top of tree.
    - Re-added document patch that was accidentally dropped from the last revision.
    - Added VMState functionality so that VGIA is restored properly.
    - Added Unit tests
v2->v3:
    - Added second writeable fw_cfg for storing the VM Generaiton ID
      address.  This uses a new linker-loader command for instructing the
      guest to write back the allocated address.  A patch for SeaBIOS has been
      submitted (https://www.seabios.org/pipermail/seabios/2017-January/011079.html)
      and the resulting binary will need to be pulled into QEMU once accepted.
    - Setting VM Generation ID by command line or qmp/hmp now accepts an "auto"
      value, whereby QEMU generates a random GUID.
    - Incorporated review comments from v2 mainly around code styling and AML syntax
    - Changed to use the E05 ACPI event instead of E00
v1->v2:
    - Removed "changed" boolean parameter as it is unneeded
    - Added ACPI Notify logic
    - Style changes to pass checkpatch.pl
    - Added support for dynamic sysbus to pc_piix boards


Ben Warren (6):
  linker-loader: Add new 'write pointer' command
  docs: VM Generation ID device description
  ACPI: Add vmgenid blob storage to the build tables
  ACPI: Add Virtual Machine Generation ID support
  tests: Move reusable ACPI macros into a new header file
  tests: Add unit tests for the VM Generation ID feature

Igor Mammedov (1):
  qmp/hmp: add query-vm-generation-id and 'info vm-generation-id'
    commands

 default-configs/i386-softmmu.mak     |   1 +
 default-configs/x86_64-softmmu.mak   |   1 +
 docs/specs/vmgenid.txt               | 245 +++++++++++++++++++++++++++++++++
 hmp-commands-info.hx                 |  13 ++
 hmp.c                                |   9 ++
 hmp.h                                |   1 +
 hw/acpi/Makefile.objs                |   1 +
 hw/acpi/aml-build.c                  |   2 +
 hw/acpi/bios-linker-loader.c         |  58 +++++++-
 hw/acpi/vmgenid.c                    | 253 +++++++++++++++++++++++++++++++++++
 hw/i386/acpi-build.c                 |  16 +++
 include/hw/acpi/acpi_dev_interface.h |   1 +
 include/hw/acpi/aml-build.h          |   1 +
 include/hw/acpi/bios-linker-loader.h |   6 +
 include/hw/acpi/vmgenid.h            |  35 +++++
 qapi-schema.json                     |  20 +++
 stubs/Makefile.objs                  |   1 +
 stubs/vmgenid.c                      |   8 ++
 tests/Makefile.include               |   2 +
 tests/acpi-utils.h                   |  75 +++++++++++
 tests/bios-tables-test.c             |  72 +---------
 tests/vmgenid-test.c                 | 195 +++++++++++++++++++++++++++
 22 files changed, 942 insertions(+), 74 deletions(-)
 create mode 100644 docs/specs/vmgenid.txt
 create mode 100644 hw/acpi/vmgenid.c
 create mode 100644 include/hw/acpi/vmgenid.h
 create mode 100644 stubs/vmgenid.c
 create mode 100644 tests/acpi-utils.h
 create mode 100644 tests/vmgenid-test.c

-- 
2.7.4

^ permalink raw reply	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15  6:15 [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID ben
@ 2017-02-15  6:15 ` ben
  2017-02-15 10:57   ` Igor Mammedov
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 2/7] docs: VM Generation ID device description ben
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 62+ messages in thread
From: ben @ 2017-02-15  6:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: lersek, mst, imammedo, Ben Warren

From: Ben Warren <ben@skyportsystems.com>

This is similar to the existing 'add pointer' functionality, but instead
of instructing the guest (BIOS or UEFI) to patch memory, it instructs
the guest to write the pointer back to QEMU via a writeable fw_cfg file.

Signed-off-by: Ben Warren <ben@skyportsystems.com>
---
 hw/acpi/bios-linker-loader.c         | 58 ++++++++++++++++++++++++++++++++++--
 include/hw/acpi/bios-linker-loader.h |  6 ++++
 2 files changed, 61 insertions(+), 3 deletions(-)

diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
index d963ebe..5030cf1 100644
--- a/hw/acpi/bios-linker-loader.c
+++ b/hw/acpi/bios-linker-loader.c
@@ -78,6 +78,19 @@ 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 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 offset;
+            uint8_t size;
+        } wr_pointer;
+
         /* padding */
         char pad[124];
     };
@@ -85,9 +98,10 @@ struct BiosLinkerLoaderEntry {
 typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
 
 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_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 +292,41 @@ 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
+ */
+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)
+{
+    BiosLinkerLoaderEntry entry;
+    const BiosLinkerFileEntry *source_file =
+        bios_linker_find_file(linker, src_file);
+
+    assert(source_file);
+    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.offset = cpu_to_le32(dst_patched_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);
+}
diff --git a/include/hw/acpi/bios-linker-loader.h b/include/hw/acpi/bios-linker-loader.h
index fa1e5d1..f9ba5d6 100644
--- a/include/hw/acpi/bios-linker-loader.h
+++ b/include/hw/acpi/bios-linker-loader.h
@@ -26,5 +26,11 @@ 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);
+
 void bios_linker_loader_cleanup(BIOSLinker *linker);
 #endif
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v6 2/7] docs: VM Generation ID device description
  2017-02-15  6:15 [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID ben
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command ben
@ 2017-02-15  6:15 ` ben
  2017-02-15 11:07   ` Igor Mammedov
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 3/7] ACPI: Add vmgenid blob storage to the build tables ben
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 62+ messages in thread
From: ben @ 2017-02-15  6:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: lersek, mst, imammedo, Ben Warren, Gal Hammer

From: Ben Warren <ben@skyportsystems.com>

This patch is based off an earlier version by
Gal Hammer (ghammer@redhat.com)

Requirements section, ASCII diagrams and overall help
provided by Laszlo Ersek (lersek@redhat.com)

Signed-off-by: Gal Hammer <ghammer@redhat.com>
Signed-off-by: Ben Warren <ben@skyportsystems.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
---
 docs/specs/vmgenid.txt | 245 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 245 insertions(+)
 create mode 100644 docs/specs/vmgenid.txt

diff --git a/docs/specs/vmgenid.txt b/docs/specs/vmgenid.txt
new file mode 100644
index 0000000..aa9f518
--- /dev/null
+++ b/docs/specs/vmgenid.txt
@@ -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.7.4

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v6 3/7] ACPI: Add vmgenid blob storage to the build tables
  2017-02-15  6:15 [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID ben
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command ben
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 2/7] docs: VM Generation ID device description ben
@ 2017-02-15  6:15 ` ben
  2017-02-15 11:15   ` Igor Mammedov
  2017-02-15 14:30   ` Laszlo Ersek
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 4/7] ACPI: Add Virtual Machine Generation ID support ben
                   ` (4 subsequent siblings)
  7 siblings, 2 replies; 62+ messages in thread
From: ben @ 2017-02-15  6:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: lersek, mst, imammedo, Ben Warren

From: Ben Warren <ben@skyportsystems.com>

This allows them to be centrally initialized and destroyed

The "AcpiBuildTables.vmgenid" array will be used to construct the
"etc/vmgenid" fw_cfg blob.

Its contents will be linked into fw_cfg after being built on the
pc_machine_done() -> acpi_setup() -> acpi_build() call path, and dropped
without use on the subsequent, guest triggered, acpi_build_update() ->
acpi_build() call path.

Signed-off-by: Ben Warren <ben@skyportsystems.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
---
 hw/acpi/aml-build.c         | 2 ++
 include/hw/acpi/aml-build.h | 1 +
 2 files changed, 3 insertions(+)

diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c
index b2a1e40..c6f2032 100644
--- a/hw/acpi/aml-build.c
+++ b/hw/acpi/aml-build.c
@@ -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 */
diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h
index 559326c..00c21f1 100644
--- a/include/hw/acpi/aml-build.h
+++ b/include/hw/acpi/aml-build.h
@@ -210,6 +210,7 @@ struct AcpiBuildTables {
     GArray *table_data;
     GArray *rsdp;
     GArray *tcpalog;
+    GArray *vmgenid;
     BIOSLinker *linker;
 } AcpiBuildTables;
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v6 4/7] ACPI: Add Virtual Machine Generation ID support
  2017-02-15  6:15 [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID ben
                   ` (2 preceding siblings ...)
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 3/7] ACPI: Add vmgenid blob storage to the build tables ben
@ 2017-02-15  6:15 ` ben
  2017-02-15 12:19   ` Igor Mammedov
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 5/7] qmp/hmp: add query-vm-generation-id and 'info vm-generation-id' commands ben
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 62+ messages in thread
From: ben @ 2017-02-15  6:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: lersek, mst, imammedo, Ben Warren

From: Ben Warren <ben@skyportsystems.com>

This implements the VM Generation ID feature by passing a 128-bit
GUID to the guest via a fw_cfg blob.
Any time the GUID changes, an ACPI notify event is sent to the guest

The user interface is a simple device with one parameter:
 - guid (string, must be "auto" or in UUID format
   xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)

Signed-off-by: Ben Warren <ben@skyportsystems.com>
---
 default-configs/i386-softmmu.mak     |   1 +
 default-configs/x86_64-softmmu.mak   |   1 +
 hw/acpi/Makefile.objs                |   1 +
 hw/acpi/vmgenid.c                    | 237 +++++++++++++++++++++++++++++++++++
 hw/i386/acpi-build.c                 |  16 +++
 include/hw/acpi/acpi_dev_interface.h |   1 +
 include/hw/acpi/vmgenid.h            |  35 ++++++
 7 files changed, 292 insertions(+)
 create mode 100644 hw/acpi/vmgenid.c
 create mode 100644 include/hw/acpi/vmgenid.h

diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index 48b07a4..029e952 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -59,3 +59,4 @@ CONFIG_I82801B11=y
 CONFIG_SMBIOS=y
 CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
 CONFIG_PXB=y
+CONFIG_ACPI_VMGENID=y
diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index fd96345..d1d7432 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -59,3 +59,4 @@ CONFIG_I82801B11=y
 CONFIG_SMBIOS=y
 CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
 CONFIG_PXB=y
+CONFIG_ACPI_VMGENID=y
diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
index 6acf798..11c35bc 100644
--- a/hw/acpi/Makefile.objs
+++ b/hw/acpi/Makefile.objs
@@ -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
diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c
new file mode 100644
index 0000000..b1b7b32
--- /dev/null
+++ b/hw/acpi/vmgenid.c
@@ -0,0 +1,237 @@
+/*
+ *  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);
+    memcpy(&guid_le.data, &vms->guid.data, sizeof(vms->guid.data));
+    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.
+     */
+    bios_linker_loader_write_pointer(linker,
+        VMGENID_ADDR_FW_CFG_FILE, 0, sizeof(uint64_t),
+        VMGENID_GUID_FW_CFG_FILE);
+
+    /* 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.
+             */
+            memcpy(&guid_le.data, &vms->guid.data, sizeof(vms->guid.data));
+            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 + VMGENID_GUID_OFFSET,
+                                      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_initfn(Object *obj)
+{
+    object_property_add_str(obj, VMGENID_GUID, NULL, vmgenid_set_guid, NULL);
+}
+
+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;
+}
+
+static const TypeInfo vmgenid_device_info = {
+    .name          = VMGENID_DEVICE,
+    .parent        = TYPE_DEVICE,
+    .instance_size = sizeof(VmGenIdState),
+    .instance_init = vmgenid_initfn,
+    .class_init    = vmgenid_device_class_init,
+};
+
+static void vmgenid_register_types(void)
+{
+    type_register_static(&vmgenid_device_info);
+}
+
+type_init(vmgenid_register_types)
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 1c928ab..db04cf5 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/acpi/vmgenid.h"
 #include "sysemu/tpm_backend.h"
 #include "hw/timer/mc146818rtc_regs.h"
 #include "sysemu/numa.h"
@@ -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.
diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h
index 71d3c48..3c2e4e9 100644
--- a/include/hw/acpi/acpi_dev_interface.h
+++ b/include/hw/acpi/acpi_dev_interface.h
@@ -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"
diff --git a/include/hw/acpi/vmgenid.h b/include/hw/acpi/vmgenid.h
new file mode 100644
index 0000000..db7fa0e
--- /dev/null
+++ b/include/hw/acpi/vmgenid.h
@@ -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
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v6 5/7] qmp/hmp: add query-vm-generation-id and 'info vm-generation-id' commands
  2017-02-15  6:15 [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID ben
                   ` (3 preceding siblings ...)
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 4/7] ACPI: Add Virtual Machine Generation ID support ben
@ 2017-02-15  6:15 ` ben
  2017-02-15 15:36   ` Laszlo Ersek
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 6/7] tests: Move reusable ACPI macros into a new header file ben
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 62+ messages in thread
From: ben @ 2017-02-15  6:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: lersek, mst, imammedo, Ben Warren

From: Igor Mammedov <imammedo@redhat.com>

Add commands to query Virtual Machine Generation ID counter.

QMP command example:
    { "execute": "query-vm-generation-id" }

HMP command example:
    info vm-generation-id

Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Ben Warren <ben@skyportsystems.com>
---
 hmp-commands-info.hx | 13 +++++++++++++
 hmp.c                |  9 +++++++++
 hmp.h                |  1 +
 hw/acpi/vmgenid.c    | 16 ++++++++++++++++
 qapi-schema.json     | 20 ++++++++++++++++++++
 stubs/Makefile.objs  |  1 +
 stubs/vmgenid.c      |  8 ++++++++
 7 files changed, 68 insertions(+)
 create mode 100644 stubs/vmgenid.c

diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
index b0f35e6..f3df793 100644
--- a/hmp-commands-info.hx
+++ b/hmp-commands-info.hx
@@ -802,6 +802,19 @@ Show information about hotpluggable CPUs
 ETEXI
 
 STEXI
+@item info 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
 
diff --git a/hmp.c b/hmp.c
index 2bc4f06..535613d 100644
--- a/hmp.c
+++ b/hmp.c
@@ -2565,3 +2565,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);
+}
diff --git a/hmp.h b/hmp.h
index 05daf7c..799fd37 100644
--- a/hmp.h
+++ b/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
diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c
index b1b7b32..c159c76 100644
--- a/hw/acpi/vmgenid.c
+++ b/hw/acpi/vmgenid.c
@@ -235,3 +235,19 @@ static void vmgenid_register_types(void)
 }
 
 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;
+}
diff --git a/qapi-schema.json b/qapi-schema.json
index 61151f3..5e2a47f 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -6051,3 +6051,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' }
diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
index a187295..0bffca6 100644
--- a/stubs/Makefile.objs
+++ b/stubs/Makefile.objs
@@ -35,3 +35,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
diff --git a/stubs/vmgenid.c b/stubs/vmgenid.c
new file mode 100644
index 0000000..8c448ac
--- /dev/null
+++ b/stubs/vmgenid.c
@@ -0,0 +1,8 @@
+#include "qemu/osdep.h"
+#include "qmp-commands.h"
+
+GuidInfo *qmp_query_vm_generation_id(Error **errp)
+{
+    error_setg(errp, "this command is not currently supported");
+    return NULL;
+}
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v6 6/7] tests: Move reusable ACPI macros into a new header file
  2017-02-15  6:15 [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID ben
                   ` (4 preceding siblings ...)
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 5/7] qmp/hmp: add query-vm-generation-id and 'info vm-generation-id' commands ben
@ 2017-02-15  6:15 ` ben
  2017-02-15 12:54   ` Igor Mammedov
  2017-02-15 21:35   ` Eric Blake
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 7/7] tests: Add unit tests for the VM Generation ID feature ben
  2017-02-15 19:47 ` [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID Laszlo Ersek
  7 siblings, 2 replies; 62+ messages in thread
From: ben @ 2017-02-15  6:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: lersek, mst, imammedo, Ben Warren

From: Ben Warren <ben@skyportsystems.com>

Also usable by upcoming VM Generation ID tests

Signed-off-by: Ben Warren <ben@skyportsystems.com>
---
 tests/acpi-utils.h       | 75 ++++++++++++++++++++++++++++++++++++++++++++++++
 tests/bios-tables-test.c | 72 +---------------------------------------------
 2 files changed, 76 insertions(+), 71 deletions(-)
 create mode 100644 tests/acpi-utils.h

diff --git a/tests/acpi-utils.h b/tests/acpi-utils.h
new file mode 100644
index 0000000..d5e5eff
--- /dev/null
+++ b/tests/acpi-utils.h
@@ -0,0 +1,75 @@
+#ifndef TEST_ACPI_UTILS_H
+#define TEST_ACPI_UTILS_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)
+
+#endif  /* TEST_ACPI_UTILS_H */
diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c
index 5404805..c642f7f 100644
--- a/tests/bios-tables-test.c
+++ b/tests/bios-tables-test.c
@@ -17,6 +17,7 @@
 #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 +25,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 +42,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
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v6 7/7] tests: Add unit tests for the VM Generation ID feature
  2017-02-15  6:15 [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID ben
                   ` (5 preceding siblings ...)
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 6/7] tests: Move reusable ACPI macros into a new header file ben
@ 2017-02-15  6:15 ` ben
  2017-02-15 13:13   ` Igor Mammedov
  2017-02-15 19:47 ` [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID Laszlo Ersek
  7 siblings, 1 reply; 62+ messages in thread
From: ben @ 2017-02-15  6:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: lersek, mst, imammedo, Ben Warren

From: Ben Warren <ben@skyportsystems.com>

The following tests are implemented:
* test that a GUID passed in by command line is propagated to the guest.
* test that changing the GUID at runtime via the monitor is reflected in
  the guest.
* test that the "auto" argument to the GUID generates a different, and
  correct GUID as seen by the guest.

  This patch is loosely based on a previous patch from:
  Gal Hammer <ghammer@redhat.com>  and Igor Mammedov <imammedo@redhat.com>

Signed-off-by: Ben Warren <ben@skyportsystems.com>
---
 tests/Makefile.include |   2 +
 tests/vmgenid-test.c   | 195 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 197 insertions(+)
 create mode 100644 tests/vmgenid-test.c

diff --git a/tests/Makefile.include b/tests/Makefile.include
index 634394a..ca4b3f7 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -241,6 +241,7 @@ check-qtest-i386-y += tests/usb-hcd-xhci-test$(EXESUF)
 gcov-files-i386-y += hw/usb/hcd-xhci.c
 check-qtest-i386-y += tests/pc-cpu-test$(EXESUF)
 check-qtest-i386-y += tests/q35-test$(EXESUF)
+check-qtest-i386-y += tests/vmgenid-test$(EXESUF)
 gcov-files-i386-y += hw/pci-host/q35.c
 check-qtest-i386-$(CONFIG_VHOST_NET_TEST_i386) += tests/vhost-user-test$(EXESUF)
 ifeq ($(CONFIG_VHOST_NET_TEST_i386),)
@@ -726,6 +727,7 @@ tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem
 tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o contrib/libvhost-user/libvhost-user.o $(test-util-obj-y)
 tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y)
 tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o
+tests/vmgenid-test$(EXESUF): tests/vmgenid-test.o
 
 tests/migration/stress$(EXESUF): tests/migration/stress.o
 	$(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@")
diff --git a/tests/vmgenid-test.c b/tests/vmgenid-test.c
new file mode 100644
index 0000000..721ba05
--- /dev/null
+++ b/tests/vmgenid-test.c
@@ -0,0 +1,195 @@
+/*
+ * QTest testcase for VM Generation ID
+ *
+ * Copyright (c) 2016 Red Hat, Inc.
+ * Copyright (c) 2017 Skyport Systems
+ *
+ * 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 <glib.h>
+#include <string.h>
+#include <unistd.h>
+#include "qemu/osdep.h"
+#include "qemu/bitmap.h"
+#include "qemu/uuid.h"
+#include "hw/acpi/acpi-defs.h"
+#include "acpi-utils.h"
+#include "libqtest.h"
+
+#define VGID_GUID "324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87"
+#define VMGENID_GUID_OFFSET      40   /* allow space for
+                                       * OVMF SDT Header Probe Supressor
+                                       */
+
+static uint32_t vgia;
+
+typedef struct {
+    AcpiTableHeader header;
+    gchar name_op;
+    gchar vgia[4];
+    gchar val_op;
+    uint32_t vgia_val;
+} QEMU_PACKED VgidTable;
+
+static uint32_t find_vgia(void)
+{
+    uint32_t off;
+    AcpiRsdpDescriptor rsdp_table;
+    uint32_t rsdt;
+    AcpiRsdtDescriptorRev1 rsdt_table;
+    int tables_nr;
+    uint32_t *tables;
+    AcpiTableHeader ssdt_table;
+    VgidTable vgid_table;
+    int i;
+
+    /* First, find the RSDP */
+    for (off = 0xf0000; off < 0x100000; off += 0x10) {
+        uint8_t sig[] = "RSD PTR ";
+
+        for (i = 0; i < sizeof sig - 1; ++i) {
+            sig[i] = readb(off + i);
+        }
+
+        if (!memcmp(sig, "RSD PTR ", sizeof sig)) {
+            break;
+        }
+    }
+    g_assert_cmphex(off, <, 0x100000);
+
+    /* Parse the RSDP header so we can find the RSDT */
+    ACPI_READ_FIELD(rsdp_table.signature, off);
+    ACPI_ASSERT_CMP64(rsdp_table.signature, "RSD PTR ");
+
+    ACPI_READ_FIELD(rsdp_table.checksum, off);
+    ACPI_READ_ARRAY(rsdp_table.oem_id, off);
+    ACPI_READ_FIELD(rsdp_table.revision, off);
+    ACPI_READ_FIELD(rsdp_table.rsdt_physical_address, off);
+
+    rsdt = rsdp_table.rsdt_physical_address;
+    /* read the header */
+    ACPI_READ_TABLE_HEADER(&rsdt_table, rsdt);
+    ACPI_ASSERT_CMP(rsdt_table.signature, "RSDT");
+
+    /* compute the table entries in rsdt */
+    tables_nr = (rsdt_table.length - sizeof(AcpiRsdtDescriptorRev1)) /
+                sizeof(uint32_t);
+    g_assert_cmpint(tables_nr, >, 0);
+
+    /* get the addresses of the tables pointed by rsdt */
+    tables = g_new0(uint32_t, tables_nr);
+    ACPI_READ_ARRAY_PTR(tables, tables_nr, rsdt);
+
+    for (i = 0; i < tables_nr; i++) {
+        ACPI_READ_TABLE_HEADER(&ssdt_table, tables[i]);
+        if (!strncmp((char *)ssdt_table.oem_table_id, "VMGENID", 7)) {
+            /* the first entry in the table should be VGIA
+             * That's all we need
+             */
+            ACPI_READ_FIELD(vgid_table.name_op, tables[i]);
+            g_assert(vgid_table.name_op == 0x08);  /* name */
+            ACPI_READ_ARRAY(vgid_table.vgia, tables[i]);
+            g_assert(memcmp(vgid_table.vgia, "VGIA", 4) == 0);
+            ACPI_READ_FIELD(vgid_table.val_op, tables[i]);
+            g_assert(vgid_table.val_op == 0x0C);  /* dword */
+            ACPI_READ_FIELD(vgid_table.vgia_val, tables[i]);
+            /* 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
+             */
+            return vgid_table.vgia_val + VMGENID_GUID_OFFSET;
+        }
+    }
+    return 0;
+}
+
+static void vmgenid_read_guid(QemuUUID *guid)
+{
+    int i;
+
+    if (vgia == 0) {
+        vgia = find_vgia();
+    }
+    g_assert(vgia);
+
+    /* Read the GUID directly from guest memory */
+    for (i = 0; i < 16; i++) {
+        guid->data[i] = readb(vgia + i);
+    }
+    /* The GUID is in little-endian format in the guest, while QEMU
+     * uses big-endian.  Swap after reading.
+     */
+    qemu_uuid_bswap(guid);
+}
+
+static void vmgenid_test(void)
+{
+    QemuUUID expected, measured;
+    g_assert(qemu_uuid_parse(VGID_GUID, &expected) == 0);
+    vmgenid_read_guid(&measured);
+    g_assert(memcmp(measured.data, expected.data, sizeof(measured.data)) == 0);
+}
+
+static void vmgenid_set_guid_test(void)
+{
+    QDict *response;
+    gchar *cmd;
+    QemuUUID expected, measured;
+    g_assert(qemu_uuid_parse(VGID_GUID, &expected) == 0);
+    /* Change the GUID slightly */
+    expected.data[0] += 1;
+
+    cmd = g_strdup_printf("{ 'execute': 'qom-set', 'arguments': { "
+                   "'path': '/machine/peripheral/testvgid', "
+                   "'property': 'guid', 'value': '%s' } }",
+                   qemu_uuid_unparse_strdup(&expected));
+    response = qmp(cmd);
+    g_assert(qdict_haskey(response, "return"));
+    QDECREF(response);
+
+    vmgenid_read_guid(&measured);
+    g_assert(memcmp(measured.data, expected.data, sizeof(measured.data)) == 0);
+}
+
+static void vmgenid_set_guid_auto_test(void)
+{
+    QDict *response;
+    QemuUUID expected, measured;
+
+    /* Read the initial value */
+    vmgenid_read_guid(&expected);
+
+    /* Setting to 'auto' generates a random GUID */
+    response = qmp("{ 'execute': 'qom-set', 'arguments': { "
+                   "'path': '/machine/peripheral/testvgid', "
+                   "'property': 'guid', 'value': 'auto' } }");
+
+    g_assert(qdict_haskey(response, "return"));
+    QDECREF(response);
+
+    vmgenid_read_guid(&measured);
+    g_assert(memcmp(measured.data, expected.data, sizeof(measured.data)) != 0);
+}
+
+int main(int argc, char **argv)
+{
+    int ret;
+    gchar *cmd;
+
+    g_test_init(&argc, &argv, NULL);
+
+    cmd = g_strdup_printf("-machine accel=tcg -device vmgenid,id=testvgid,"
+                          "guid=%s", VGID_GUID);
+    qtest_start(cmd);
+    qtest_add_func("/vmgenid/vmgenid", vmgenid_test);
+    qtest_add_func("/vmgenid/vmgenid/set-guid", vmgenid_set_guid_test);
+    qtest_add_func("/vmgenid/vmgenid/set-guid-auto",
+                   vmgenid_set_guid_auto_test);
+    ret = g_test_run();
+
+    qtest_end();
+
+    return ret;
+}
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command ben
@ 2017-02-15 10:57   ` Igor Mammedov
  2017-02-15 14:13     ` Laszlo Ersek
  0 siblings, 1 reply; 62+ messages in thread
From: Igor Mammedov @ 2017-02-15 10:57 UTC (permalink / raw)
  To: ben; +Cc: qemu-devel, lersek, mst

On Tue, 14 Feb 2017 22:15:43 -0800
ben@skyportsystems.com wrote:

> From: Ben Warren <ben@skyportsystems.com>
> 
> This is similar to the existing 'add pointer' functionality, but instead
> of instructing the guest (BIOS or UEFI) to patch memory, it instructs
> the guest to write the pointer back to QEMU via a writeable fw_cfg file.
> 
> Signed-off-by: Ben Warren <ben@skyportsystems.com>
> ---
>  hw/acpi/bios-linker-loader.c         | 58 ++++++++++++++++++++++++++++++++++--
>  include/hw/acpi/bios-linker-loader.h |  6 ++++
>  2 files changed, 61 insertions(+), 3 deletions(-)
> 
> diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
> index d963ebe..5030cf1 100644
> --- a/hw/acpi/bios-linker-loader.c
> +++ b/hw/acpi/bios-linker-loader.c
> @@ -78,6 +78,19 @@ 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 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 offset;
> +            uint8_t size;
> +        } wr_pointer;
> +
>          /* padding */
>          char pad[124];
>      };
> @@ -85,9 +98,10 @@ struct BiosLinkerLoaderEntry {
>  typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
>  
>  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_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 +292,41 @@ 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
> + */
> +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)
API is missing "src_offset" even though it's not used in this series,
a patch on top to fix it up is ok for me as far as Seabios/OVMF
counterpart can handle src_offset correctly from starters.

> +{
> +    BiosLinkerLoaderEntry entry;
> +    const BiosLinkerFileEntry *source_file =
> +        bios_linker_find_file(linker, src_file);
> +
> +    assert(source_file);
> +    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.offset = cpu_to_le32(dst_patched_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);
> +}
> diff --git a/include/hw/acpi/bios-linker-loader.h b/include/hw/acpi/bios-linker-loader.h
> index fa1e5d1..f9ba5d6 100644
> --- a/include/hw/acpi/bios-linker-loader.h
> +++ b/include/hw/acpi/bios-linker-loader.h
> @@ -26,5 +26,11 @@ 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);
> +
>  void bios_linker_loader_cleanup(BIOSLinker *linker);
>  #endif

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 2/7] docs: VM Generation ID device description
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 2/7] docs: VM Generation ID device description ben
@ 2017-02-15 11:07   ` Igor Mammedov
  2017-02-15 14:26     ` Laszlo Ersek
  0 siblings, 1 reply; 62+ messages in thread
From: Igor Mammedov @ 2017-02-15 11:07 UTC (permalink / raw)
  To: ben; +Cc: qemu-devel, Gal Hammer, lersek, mst

On Tue, 14 Feb 2017 22:15:44 -0800
ben@skyportsystems.com wrote:

> From: Ben Warren <ben@skyportsystems.com>
> 
> This patch is based off an earlier version by
> Gal Hammer (ghammer@redhat.com)
> 
> Requirements section, ASCII diagrams and overall help
> provided by Laszlo Ersek (lersek@redhat.com)
> 
> Signed-off-by: Gal Hammer <ghammer@redhat.com>
> Signed-off-by: Ben Warren <ben@skyportsystems.com>
> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>

> ---
>  docs/specs/vmgenid.txt | 245 +++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 245 insertions(+)
>  create mode 100644 docs/specs/vmgenid.txt
> 
> diff --git a/docs/specs/vmgenid.txt b/docs/specs/vmgenid.txt
> new file mode 100644
> index 0000000..aa9f518
> --- /dev/null
> +++ b/docs/specs/vmgenid.txt
> @@ -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.

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 3/7] ACPI: Add vmgenid blob storage to the build tables
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 3/7] ACPI: Add vmgenid blob storage to the build tables ben
@ 2017-02-15 11:15   ` Igor Mammedov
  2017-02-15 14:30   ` Laszlo Ersek
  1 sibling, 0 replies; 62+ messages in thread
From: Igor Mammedov @ 2017-02-15 11:15 UTC (permalink / raw)
  To: ben; +Cc: qemu-devel, lersek, mst

On Tue, 14 Feb 2017 22:15:45 -0800
ben@skyportsystems.com wrote:

> From: Ben Warren <ben@skyportsystems.com>
> 
> This allows them to be centrally initialized and destroyed
> 
> The "AcpiBuildTables.vmgenid" array will be used to construct the
> "etc/vmgenid" fw_cfg blob.
> 
> Its contents will be linked into fw_cfg after being built on the
> pc_machine_done() -> acpi_setup() -> acpi_build() call path, and dropped
> without use on the subsequent, guest triggered, acpi_build_update() ->
> acpi_build() call path.
> 
> Signed-off-by: Ben Warren <ben@skyportsystems.com>
> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>

> ---
>  hw/acpi/aml-build.c         | 2 ++
>  include/hw/acpi/aml-build.h | 1 +
>  2 files changed, 3 insertions(+)
> 
> diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c
> index b2a1e40..c6f2032 100644
> --- a/hw/acpi/aml-build.c
> +++ b/hw/acpi/aml-build.c
> @@ -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 */
> diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h
> index 559326c..00c21f1 100644
> --- a/include/hw/acpi/aml-build.h
> +++ b/include/hw/acpi/aml-build.h
> @@ -210,6 +210,7 @@ struct AcpiBuildTables {
>      GArray *table_data;
>      GArray *rsdp;
>      GArray *tcpalog;
> +    GArray *vmgenid;
>      BIOSLinker *linker;
>  } AcpiBuildTables;
>  

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 4/7] ACPI: Add Virtual Machine Generation ID support
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 4/7] ACPI: Add Virtual Machine Generation ID support ben
@ 2017-02-15 12:19   ` Igor Mammedov
  2017-02-15 15:24     ` Laszlo Ersek
  2017-02-15 17:11     ` Ben Warren
  0 siblings, 2 replies; 62+ messages in thread
From: Igor Mammedov @ 2017-02-15 12:19 UTC (permalink / raw)
  To: ben; +Cc: qemu-devel, lersek, mst

On Tue, 14 Feb 2017 22:15:46 -0800
ben@skyportsystems.com wrote:

> From: Ben Warren <ben@skyportsystems.com>
> 
> This implements the VM Generation ID feature by passing a 128-bit
> GUID to the guest via a fw_cfg blob.
> Any time the GUID changes, an ACPI notify event is sent to the guest
> 
> The user interface is a simple device with one parameter:
>  - guid (string, must be "auto" or in UUID format
>    xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
> 
> Signed-off-by: Ben Warren <ben@skyportsystems.com>
> ---
>  default-configs/i386-softmmu.mak     |   1 +
>  default-configs/x86_64-softmmu.mak   |   1 +
>  hw/acpi/Makefile.objs                |   1 +
>  hw/acpi/vmgenid.c                    | 237 +++++++++++++++++++++++++++++++++++
>  hw/i386/acpi-build.c                 |  16 +++
>  include/hw/acpi/acpi_dev_interface.h |   1 +
>  include/hw/acpi/vmgenid.h            |  35 ++++++
>  7 files changed, 292 insertions(+)
>  create mode 100644 hw/acpi/vmgenid.c
>  create mode 100644 include/hw/acpi/vmgenid.h
> 
> diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
> index 48b07a4..029e952 100644
> --- a/default-configs/i386-softmmu.mak
> +++ b/default-configs/i386-softmmu.mak
> @@ -59,3 +59,4 @@ CONFIG_I82801B11=y
>  CONFIG_SMBIOS=y
>  CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
>  CONFIG_PXB=y
> +CONFIG_ACPI_VMGENID=y
> diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
> index fd96345..d1d7432 100644
> --- a/default-configs/x86_64-softmmu.mak
> +++ b/default-configs/x86_64-softmmu.mak
> @@ -59,3 +59,4 @@ CONFIG_I82801B11=y
>  CONFIG_SMBIOS=y
>  CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
>  CONFIG_PXB=y
> +CONFIG_ACPI_VMGENID=y
> diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
> index 6acf798..11c35bc 100644
> --- a/hw/acpi/Makefile.objs
> +++ b/hw/acpi/Makefile.objs
> @@ -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
> diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c
> new file mode 100644
> index 0000000..b1b7b32
> --- /dev/null
> +++ b/hw/acpi/vmgenid.c
> @@ -0,0 +1,237 @@
> +/*
> + *  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);
> +    memcpy(&guid_le.data, &vms->guid.data, sizeof(vms->guid.data));
> +    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))));
Just curious,
so suggested in v5 simple declaration style 

Package(2) {
  ADD(VGIA, VMGENID_GUID_OFFSET),
  0
}

wasn't working for you?

> +    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.
> +     */
> +    bios_linker_loader_write_pointer(linker,
> +        VMGENID_ADDR_FW_CFG_FILE, 0, sizeof(uint64_t),
> +        VMGENID_GUID_FW_CFG_FILE);
> +
> +    /* 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.
> +             */
> +            memcpy(&guid_le.data, &vms->guid.data, sizeof(vms->guid.data));
potential stack corruption if guid_le and vms->guid types ever diverge
why not just do
  guid_le.data = guid.data

> +            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 + VMGENID_GUID_OFFSET,
> +                                      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_initfn(Object *obj)
> +{
> +    object_property_add_str(obj, VMGENID_GUID, NULL, vmgenid_set_guid, NULL);
missing:
  object_property_set_description()
or even better use class properties here:

object_class_property_add_str()/object_class_property_set_description()

> +}
> +
> +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;
it needs:

dc->hotpluggable = false;

> +}
> +
> +static const TypeInfo vmgenid_device_info = {
> +    .name          = VMGENID_DEVICE,
> +    .parent        = TYPE_DEVICE,
> +    .instance_size = sizeof(VmGenIdState),
> +    .instance_init = vmgenid_initfn,
> +    .class_init    = vmgenid_device_class_init,
> +};
> +
> +static void vmgenid_register_types(void)
> +{
> +    type_register_static(&vmgenid_device_info);
> +}
> +
> +type_init(vmgenid_register_types)
> diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
> index 1c928ab..db04cf5 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/acpi/vmgenid.h"
>  #include "sysemu/tpm_backend.h"
>  #include "hw/timer/mc146818rtc_regs.h"
>  #include "sysemu/numa.h"
> @@ -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.
> diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h
> index 71d3c48..3c2e4e9 100644
> --- a/include/hw/acpi/acpi_dev_interface.h
> +++ b/include/hw/acpi/acpi_dev_interface.h
> @@ -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"
> diff --git a/include/hw/acpi/vmgenid.h b/include/hw/acpi/vmgenid.h
> new file mode 100644
> index 0000000..db7fa0e
> --- /dev/null
> +++ b/include/hw/acpi/vmgenid.h
> @@ -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);
What will happen if CLI would be:
  -device vmgenid -device vmgenid
As I understand, it should be exclusive single instance device,
and there is nothing to prevent second instance of it.


> +}
> +
> +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

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 6/7] tests: Move reusable ACPI macros into a new header file
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 6/7] tests: Move reusable ACPI macros into a new header file ben
@ 2017-02-15 12:54   ` Igor Mammedov
  2017-02-15 21:35   ` Eric Blake
  1 sibling, 0 replies; 62+ messages in thread
From: Igor Mammedov @ 2017-02-15 12:54 UTC (permalink / raw)
  To: ben; +Cc: qemu-devel, lersek, mst

On Tue, 14 Feb 2017 22:15:48 -0800
ben@skyportsystems.com wrote:

> From: Ben Warren <ben@skyportsystems.com>
> 
> Also usable by upcoming VM Generation ID tests
> 
> Signed-off-by: Ben Warren <ben@skyportsystems.com>
> ---
>  tests/acpi-utils.h       | 75 ++++++++++++++++++++++++++++++++++++++++++++++++
>  tests/bios-tables-test.c | 72 +---------------------------------------------
>  2 files changed, 76 insertions(+), 71 deletions(-)
>  create mode 100644 tests/acpi-utils.h
> 
> diff --git a/tests/acpi-utils.h b/tests/acpi-utils.h
> new file mode 100644
> index 0000000..d5e5eff
> --- /dev/null
> +++ b/tests/acpi-utils.h
> @@ -0,0 +1,75 @@
> +#ifndef TEST_ACPI_UTILS_H
> +#define TEST_ACPI_UTILS_H
here should be includes for types/functions used within this file

> +
> +/* 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)
> +
> +#endif  /* TEST_ACPI_UTILS_H */
> diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c
> index 5404805..c642f7f 100644
> --- a/tests/bios-tables-test.c
> +++ b/tests/bios-tables-test.c
> @@ -17,6 +17,7 @@
>  #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 +25,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 +42,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

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 7/7] tests: Add unit tests for the VM Generation ID feature
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 7/7] tests: Add unit tests for the VM Generation ID feature ben
@ 2017-02-15 13:13   ` Igor Mammedov
  2017-02-16  6:15     ` Ben Warren
  0 siblings, 1 reply; 62+ messages in thread
From: Igor Mammedov @ 2017-02-15 13:13 UTC (permalink / raw)
  To: ben; +Cc: qemu-devel, lersek, mst

On Tue, 14 Feb 2017 22:15:49 -0800
ben@skyportsystems.com wrote:

> From: Ben Warren <ben@skyportsystems.com>
> 
> The following tests are implemented:
> * test that a GUID passed in by command line is propagated to the guest.
> * test that changing the GUID at runtime via the monitor is reflected in
>   the guest.
> * test that the "auto" argument to the GUID generates a different, and
>   correct GUID as seen by the guest.
> 
>   This patch is loosely based on a previous patch from:
>   Gal Hammer <ghammer@redhat.com>  and Igor Mammedov <imammedo@redhat.com>
> 
> Signed-off-by: Ben Warren <ben@skyportsystems.com>
> ---
>  tests/Makefile.include |   2 +
>  tests/vmgenid-test.c   | 195 +++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 197 insertions(+)
>  create mode 100644 tests/vmgenid-test.c
> 
> diff --git a/tests/Makefile.include b/tests/Makefile.include
> index 634394a..ca4b3f7 100644
> --- a/tests/Makefile.include
> +++ b/tests/Makefile.include
> @@ -241,6 +241,7 @@ check-qtest-i386-y += tests/usb-hcd-xhci-test$(EXESUF)
>  gcov-files-i386-y += hw/usb/hcd-xhci.c
>  check-qtest-i386-y += tests/pc-cpu-test$(EXESUF)
>  check-qtest-i386-y += tests/q35-test$(EXESUF)
> +check-qtest-i386-y += tests/vmgenid-test$(EXESUF)
>  gcov-files-i386-y += hw/pci-host/q35.c
>  check-qtest-i386-$(CONFIG_VHOST_NET_TEST_i386) += tests/vhost-user-test$(EXESUF)
>  ifeq ($(CONFIG_VHOST_NET_TEST_i386),)
> @@ -726,6 +727,7 @@ tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem
>  tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o contrib/libvhost-user/libvhost-user.o $(test-util-obj-y)
>  tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y)
>  tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o
> +tests/vmgenid-test$(EXESUF): tests/vmgenid-test.o
>  
>  tests/migration/stress$(EXESUF): tests/migration/stress.o
>  	$(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@")
> diff --git a/tests/vmgenid-test.c b/tests/vmgenid-test.c
> new file mode 100644
> index 0000000..721ba05
> --- /dev/null
> +++ b/tests/vmgenid-test.c
> @@ -0,0 +1,195 @@
> +/*
> + * QTest testcase for VM Generation ID
> + *
> + * Copyright (c) 2016 Red Hat, Inc.
> + * Copyright (c) 2017 Skyport Systems
> + *
> + * 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 <glib.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include "qemu/osdep.h"
> +#include "qemu/bitmap.h"
> +#include "qemu/uuid.h"
> +#include "hw/acpi/acpi-defs.h"
> +#include "acpi-utils.h"
> +#include "libqtest.h"
> +
> +#define VGID_GUID "324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87"
> +#define VMGENID_GUID_OFFSET      40   /* allow space for
> +                                       * OVMF SDT Header Probe Supressor
> +                                       */
> +
> +static uint32_t vgia;
> +
> +typedef struct {
> +    AcpiTableHeader header;
> +    gchar name_op;
> +    gchar vgia[4];
> +    gchar val_op;
> +    uint32_t vgia_val;
> +} QEMU_PACKED VgidTable;
> +
> +static uint32_t find_vgia(void)
> +{
[...]

===
> +    /* First, find the RSDP */
> +    for (off = 0xf0000; off < 0x100000; off += 0x10) {
> +        uint8_t sig[] = "RSD PTR ";
> +
> +        for (i = 0; i < sizeof sig - 1; ++i) {
> +            sig[i] = readb(off + i);
> +        }
> +
> +        if (!memcmp(sig, "RSD PTR ", sizeof sig)) {
> +            break;
> +        }
> +    }
> +    g_assert_cmphex(off, <, 0x100000);
> +
> +    /* Parse the RSDP header so we can find the RSDT */
> +    ACPI_READ_FIELD(rsdp_table.signature, off);
> +    ACPI_ASSERT_CMP64(rsdp_table.signature, "RSD PTR ");
> +
> +    ACPI_READ_FIELD(rsdp_table.checksum, off);
> +    ACPI_READ_ARRAY(rsdp_table.oem_id, off);
> +    ACPI_READ_FIELD(rsdp_table.revision, off);
> +    ACPI_READ_FIELD(rsdp_table.rsdt_physical_address, off);
> +
> +    rsdt = rsdp_table.rsdt_physical_address;
> +    /* read the header */
> +    ACPI_READ_TABLE_HEADER(&rsdt_table, rsdt);
> +    ACPI_ASSERT_CMP(rsdt_table.signature, "RSDT");
> +
> +    /* compute the table entries in rsdt */
> +    tables_nr = (rsdt_table.length - sizeof(AcpiRsdtDescriptorRev1)) /
> +                sizeof(uint32_t);
> +    g_assert_cmpint(tables_nr, >, 0);
> +
> +    /* get the addresses of the tables pointed by rsdt */
> +    tables = g_new0(uint32_t, tables_nr);
> +    ACPI_READ_ARRAY_PTR(tables, tables_nr, rsdt);
===
above is duplicated code from bios-tables-test.c
please extract it into separate functions and use them in both tests.

> +    for (i = 0; i < tables_nr; i++) {
> +        ACPI_READ_TABLE_HEADER(&ssdt_table, tables[i]);
> +        if (!strncmp((char *)ssdt_table.oem_table_id, "VMGENID", 7)) {
> +            /* the first entry in the table should be VGIA
> +             * That's all we need
> +             */
> +            ACPI_READ_FIELD(vgid_table.name_op, tables[i]);
> +            g_assert(vgid_table.name_op == 0x08);  /* name */
> +            ACPI_READ_ARRAY(vgid_table.vgia, tables[i]);
> +            g_assert(memcmp(vgid_table.vgia, "VGIA", 4) == 0);
> +            ACPI_READ_FIELD(vgid_table.val_op, tables[i]);
> +            g_assert(vgid_table.val_op == 0x0C);  /* dword */
> +            ACPI_READ_FIELD(vgid_table.vgia_val, tables[i]);
> +            /* 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
> +             */
> +            return vgid_table.vgia_val + VMGENID_GUID_OFFSET;
> +        }
> +    }
> +    return 0;
> +}
> +
> +static void vmgenid_read_guid(QemuUUID *guid)
> +{
> +    int i;
> +
> +    if (vgia == 0) {
> +        vgia = find_vgia();
> +    }
> +    g_assert(vgia);
> +
> +    /* Read the GUID directly from guest memory */
> +    for (i = 0; i < 16; i++) {
> +        guid->data[i] = readb(vgia + i);
> +    }
> +    /* The GUID is in little-endian format in the guest, while QEMU
> +     * uses big-endian.  Swap after reading.
> +     */
> +    qemu_uuid_bswap(guid);
> +}
> +
> +static void vmgenid_test(void)
> +{
> +    QemuUUID expected, measured;
> +    g_assert(qemu_uuid_parse(VGID_GUID, &expected) == 0);
> +    vmgenid_read_guid(&measured);
> +    g_assert(memcmp(measured.data, expected.data, sizeof(measured.data)) == 0);
> +}
> +
> +static void vmgenid_set_guid_test(void)
> +{
> +    QDict *response;
> +    gchar *cmd;
> +    QemuUUID expected, measured;
> +    g_assert(qemu_uuid_parse(VGID_GUID, &expected) == 0);
> +    /* Change the GUID slightly */
> +    expected.data[0] += 1;
> +
> +    cmd = g_strdup_printf("{ 'execute': 'qom-set', 'arguments': { "
> +                   "'path': '/machine/peripheral/testvgid', "
> +                   "'property': 'guid', 'value': '%s' } }",
> +                   qemu_uuid_unparse_strdup(&expected));
> +    response = qmp(cmd);
> +    g_assert(qdict_haskey(response, "return"));
> +    QDECREF(response);
> +
> +    vmgenid_read_guid(&measured);
> +    g_assert(memcmp(measured.data, expected.data, sizeof(measured.data)) == 0);
> +}
> +
> +static void vmgenid_set_guid_auto_test(void)
> +{
> +    QDict *response;
> +    QemuUUID expected, measured;
> +
> +    /* Read the initial value */
> +    vmgenid_read_guid(&expected);
> +
> +    /* Setting to 'auto' generates a random GUID */
> +    response = qmp("{ 'execute': 'qom-set', 'arguments': { "
> +                   "'path': '/machine/peripheral/testvgid', "
> +                   "'property': 'guid', 'value': 'auto' } }");
> +
> +    g_assert(qdict_haskey(response, "return"));
> +    QDECREF(response);
> +
> +    vmgenid_read_guid(&measured);
> +    g_assert(memcmp(measured.data, expected.data, sizeof(measured.data)) != 0);
> +}
> +
> +int main(int argc, char **argv)
> +{
> +    int ret;
> +    gchar *cmd;
> +
> +    g_test_init(&argc, &argv, NULL);
> +
> +    cmd = g_strdup_printf("-machine accel=tcg -device vmgenid,id=testvgid,"
> +                          "guid=%s", VGID_GUID);
> +    qtest_start(cmd);
> +    qtest_add_func("/vmgenid/vmgenid", vmgenid_test);
> +    qtest_add_func("/vmgenid/vmgenid/set-guid", vmgenid_set_guid_test);
this is probably no necessary as you've dropped dynamic set in v6,
so if this test passes it's a bug in vmgenid and you should forbid
setting property after device is realized.
See how qdev_prop_set_after_realize() is used.

> +    qtest_add_func("/vmgenid/vmgenid/set-guid-auto",
> +                   vmgenid_set_guid_auto_test);
this should be replaced with this first test but with 'auto' value

maybe add query-vm-generation-id test

> +    ret = g_test_run();
> +
> +    qtest_end();
> +
> +    return ret;
> +}

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15 10:57   ` Igor Mammedov
@ 2017-02-15 14:13     ` Laszlo Ersek
  2017-02-15 14:17       ` Laszlo Ersek
  2017-02-15 15:22       ` Igor Mammedov
  0 siblings, 2 replies; 62+ messages in thread
From: Laszlo Ersek @ 2017-02-15 14:13 UTC (permalink / raw)
  To: Igor Mammedov, ben; +Cc: qemu-devel, mst

Commenting under Igor's reply for simplicity

On 02/15/17 11:57, Igor Mammedov wrote:
> On Tue, 14 Feb 2017 22:15:43 -0800
> ben@skyportsystems.com wrote:
> 
>> From: Ben Warren <ben@skyportsystems.com>
>>
>> This is similar to the existing 'add pointer' functionality, but instead
>> of instructing the guest (BIOS or UEFI) to patch memory, it instructs
>> the guest to write the pointer back to QEMU via a writeable fw_cfg file.
>>
>> Signed-off-by: Ben Warren <ben@skyportsystems.com>
>> ---
>>  hw/acpi/bios-linker-loader.c         | 58 ++++++++++++++++++++++++++++++++++--
>>  include/hw/acpi/bios-linker-loader.h |  6 ++++
>>  2 files changed, 61 insertions(+), 3 deletions(-)
>>
>> diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
>> index d963ebe..5030cf1 100644
>> --- a/hw/acpi/bios-linker-loader.c
>> +++ b/hw/acpi/bios-linker-loader.c
>> @@ -78,6 +78,19 @@ 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 the table
>> +         * originating from @src_file. 1,2,4 or 8 byte unsigned
>> +         * addition is used depending on @wr_pointer.size.
>> +         */

The words "adding" and "addition" are causing confusion here.

In all of the previous discussion, *addition* was out of scope from
WRITE_POINTER. Again, the firmware is specifically not required to
*read* any part of the fw_cfg blob identified by "dest_file".

WRITE_POINTER instructs the firmware to return the allocation address of
the downloaded "src_file" to QEMU. Any necessary runtime subscripting
within "src_file" is to be handled by QEMU code dynamically.

For example, consider that "src_file" has *several* fields that QEMU
wants to massage; in that case, indexing within QEMU code with field
offsets is simply unavoidable.

(1) So, the above looks correct, but please replace "adding" with
"storing", and "unsigned addition" with "store".

Side point: the case for ADD_POINTER is different; there we patch
several individual ACPI objects. The fact that I requested explicit
addition within the ADDR method, as opposed to pre-setting VGIA to a
nonzero offset, is an *incidental* limitation (coming from the OVMF ACPI
SDT header probe suppressor), and we'll likely fix that up later, with
ALLOCATE command hints or something like that. However, in
WRITE_POINTER, asking for the exact allocation address of "src_file" is
an *inherent* characteristic.

For reference, this is the command's description from the (not as yet
posted) OVMF series:

// QemuLoaderCmdWritePointer: the bytes at
// [PointerOffset..PointerOffset+PointerSize) in the writeable fw_cfg
// file PointerFile are to receive the absolute address of PointeeFile,
// as allocated and downloaded by the firmware. Store the base address
// of where PointeeFile's contents have been placed (when
// QemuLoaderCmdAllocate has been executed for PointeeFile) to this
// portion of PointerFile.
//
// This command is similar to QemuLoaderCmdAddPointer; the difference is
// that the "pointer to patch" does not exist in guest-physical address
// space, only in "fw_cfg file space". In addition, the "pointer to
// patch" is not initialized by QEMU with a possibly nonzero offset
// value: the base address of the memory allocated for downloading
// PointeeFile shall not increment the pointer, but overwrite it.

In the last SeaBIOS patch series, namely

[SeaBIOS] [PATCH v3 2/2] QEMU fw_cfg: Add command to write back address
                         of file

function romfile_loader_write_pointer() implemented just that plain
store (not an addition), and that was exactly right.

Continuing:

>> +        struct {
>> +            char dest_file[BIOS_LINKER_LOADER_FILESZ];
>> +            char src_file[BIOS_LINKER_LOADER_FILESZ];
>> +            uint32_t offset;
>> +            uint8_t size;
>> +        } wr_pointer;
>> +
>>          /* padding */
>>          char pad[124];
>>      };
>> @@ -85,9 +98,10 @@ struct BiosLinkerLoaderEntry {
>>  typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
>>  
>>  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_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 +292,41 @@ 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
>> + */
>> +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)
> API is missing "src_offset" even though it's not used in this series,
> a patch on top to fix it up is ok for me as far as Seabios/OVMF
> counterpart can handle src_offset correctly from starters.

According to the above, it is the right thing not to add "src_offset"
here. The documentation on the command is slightly incorrect (and causes
confusion), but the helper function's signature and comments are okay.

> 
>> +{
>> +    BiosLinkerLoaderEntry entry;
>> +    const BiosLinkerFileEntry *source_file =
>> +        bios_linker_find_file(linker, src_file);
>> +
>> +    assert(source_file);

I wish we kept the following asserts from bios_linker_loader_add_pointer():

    assert(dst_patched_offset < dst_file->blob->len);
    assert(dst_patched_offset + dst_patched_size <= dst_file->blob->len);

Namely, just because the dst_file is never supposed to be downloaded by
the firmware, it still remains a requirement that the "dst file offset
range" that is to be rewritten *do fall* within the dst file.

Nonetheless, this is not critical. (OVMF at least verifies it anyway.)

Summary (from my side anyway): I feel that the documentation of the new
command is very important. Please fix it up as suggested under (1), in
v7. Regarding the asserts, I'll let you decide.

With the documentation fixed up:

Reviewed-by: Laszlo Ersek <lersek@redhat.com>

(If you don't wish to post a v7, I'm also completely fine if Michael or
someone else fixes up the docs as proposed in (1), before committing the
patch.)

Thanks!
Laszlo

>> +    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.offset = cpu_to_le32(dst_patched_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);
>> +}
>> diff --git a/include/hw/acpi/bios-linker-loader.h b/include/hw/acpi/bios-linker-loader.h
>> index fa1e5d1..f9ba5d6 100644
>> --- a/include/hw/acpi/bios-linker-loader.h
>> +++ b/include/hw/acpi/bios-linker-loader.h
>> @@ -26,5 +26,11 @@ 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);
>> +
>>  void bios_linker_loader_cleanup(BIOSLinker *linker);
>>  #endif
> 

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15 14:13     ` Laszlo Ersek
@ 2017-02-15 14:17       ` Laszlo Ersek
  2017-02-15 15:22       ` Igor Mammedov
  1 sibling, 0 replies; 62+ messages in thread
From: Laszlo Ersek @ 2017-02-15 14:17 UTC (permalink / raw)
  To: Igor Mammedov, ben; +Cc: qemu-devel, mst

On 02/15/17 15:13, Laszlo Ersek wrote:
> Commenting under Igor's reply for simplicity
> 
> On 02/15/17 11:57, Igor Mammedov wrote:
>> On Tue, 14 Feb 2017 22:15:43 -0800
>> ben@skyportsystems.com wrote:
>>
>>> From: Ben Warren <ben@skyportsystems.com>
>>>
>>> This is similar to the existing 'add pointer' functionality, but instead
>>> of instructing the guest (BIOS or UEFI) to patch memory, it instructs
>>> the guest to write the pointer back to QEMU via a writeable fw_cfg file.
>>>
>>> Signed-off-by: Ben Warren <ben@skyportsystems.com>
>>> ---
>>>  hw/acpi/bios-linker-loader.c         | 58 ++++++++++++++++++++++++++++++++++--
>>>  include/hw/acpi/bios-linker-loader.h |  6 ++++
>>>  2 files changed, 61 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
>>> index d963ebe..5030cf1 100644
>>> --- a/hw/acpi/bios-linker-loader.c
>>> +++ b/hw/acpi/bios-linker-loader.c
>>> @@ -78,6 +78,19 @@ 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 the table
>>> +         * originating from @src_file. 1,2,4 or 8 byte unsigned
>>> +         * addition is used depending on @wr_pointer.size.
>>> +         */
> 
> The words "adding" and "addition" are causing confusion here.
> 
> In all of the previous discussion, *addition* was out of scope from
> WRITE_POINTER. Again, the firmware is specifically not required to
> *read* any part of the fw_cfg blob identified by "dest_file".
> 
> WRITE_POINTER instructs the firmware to return the allocation address of
> the downloaded "src_file" to QEMU. Any necessary runtime subscripting
> within "src_file" is to be handled by QEMU code dynamically.
> 
> For example, consider that "src_file" has *several* fields that QEMU
> wants to massage; in that case, indexing within QEMU code with field
> offsets is simply unavoidable.
> 
> (1) So, the above looks correct, but please replace "adding" with
> "storing", and "unsigned addition" with "store".
> 
> Side point: the case for ADD_POINTER is different; there we patch
> several individual ACPI objects. The fact that I requested explicit
> addition within the ADDR method, as opposed to pre-setting VGIA to a
> nonzero offset, is an *incidental* limitation (coming from the OVMF ACPI
> SDT header probe suppressor), and we'll likely fix that up later, with
> ALLOCATE command hints or something like that. However, in
> WRITE_POINTER, asking for the exact allocation address of "src_file" is
> an *inherent* characteristic.
> 
> For reference, this is the command's description from the (not as yet
> posted) OVMF series:
> 
> // QemuLoaderCmdWritePointer: the bytes at
> // [PointerOffset..PointerOffset+PointerSize) in the writeable fw_cfg
> // file PointerFile are to receive the absolute address of PointeeFile,
> // as allocated and downloaded by the firmware. Store the base address
> // of where PointeeFile's contents have been placed (when
> // QemuLoaderCmdAllocate has been executed for PointeeFile) to this
> // portion of PointerFile.
> //
> // This command is similar to QemuLoaderCmdAddPointer; the difference is
> // that the "pointer to patch" does not exist in guest-physical address
> // space, only in "fw_cfg file space". In addition, the "pointer to
> // patch" is not initialized by QEMU with a possibly nonzero offset
> // value: the base address of the memory allocated for downloading
> // PointeeFile shall not increment the pointer, but overwrite it.
> 
> In the last SeaBIOS patch series, namely
> 
> [SeaBIOS] [PATCH v3 2/2] QEMU fw_cfg: Add command to write back address
>                          of file
> 
> function romfile_loader_write_pointer() implemented just that plain
> store (not an addition), and that was exactly right.
> 
> Continuing:
> 
>>> +        struct {
>>> +            char dest_file[BIOS_LINKER_LOADER_FILESZ];
>>> +            char src_file[BIOS_LINKER_LOADER_FILESZ];
>>> +            uint32_t offset;
>>> +            uint8_t size;
>>> +        } wr_pointer;
>>> +
>>>          /* padding */
>>>          char pad[124];
>>>      };
>>> @@ -85,9 +98,10 @@ struct BiosLinkerLoaderEntry {
>>>  typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
>>>  
>>>  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_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 +292,41 @@ 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
>>> + */
>>> +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)
>> API is missing "src_offset" even though it's not used in this series,
>> a patch on top to fix it up is ok for me as far as Seabios/OVMF
>> counterpart can handle src_offset correctly from starters.
> 
> According to the above, it is the right thing not to add "src_offset"
> here. The documentation on the command is slightly incorrect (and causes
> confusion), but the helper function's signature and comments are okay.
> 
>>
>>> +{
>>> +    BiosLinkerLoaderEntry entry;
>>> +    const BiosLinkerFileEntry *source_file =
>>> +        bios_linker_find_file(linker, src_file);
>>> +
>>> +    assert(source_file);
> 
> I wish we kept the following asserts from bios_linker_loader_add_pointer():
> 
>     assert(dst_patched_offset < dst_file->blob->len);
>     assert(dst_patched_offset + dst_patched_size <= dst_file->blob->len);
> 
> Namely, just because the dst_file is never supposed to be downloaded by
> the firmware, it still remains a requirement that the "dst file offset
> range" that is to be rewritten *do fall* within the dst file.
> 
> Nonetheless, this is not critical. (OVMF at least verifies it anyway.)

Update: here I missed for a moment that bios_linker_find_file() would
not be able to locate dst_file. The reason for that is that we
(correctly!) never produce an ALLOCATE command for dst_file, hence we
never add it to "linker->file_list" either.

Therefore, please ignore this comment about the assert()s. My only
comment for this patch remains the docs update, as described in (1).

Thank you,
Laszlo

> 
> Summary (from my side anyway): I feel that the documentation of the new
> command is very important. Please fix it up as suggested under (1), in
> v7. Regarding the asserts, I'll let you decide.
> 
> With the documentation fixed up:
> 
> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> 
> (If you don't wish to post a v7, I'm also completely fine if Michael or
> someone else fixes up the docs as proposed in (1), before committing the
> patch.)
> 
> Thanks!
> Laszlo
> 
>>> +    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.offset = cpu_to_le32(dst_patched_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);
>>> +}
>>> diff --git a/include/hw/acpi/bios-linker-loader.h b/include/hw/acpi/bios-linker-loader.h
>>> index fa1e5d1..f9ba5d6 100644
>>> --- a/include/hw/acpi/bios-linker-loader.h
>>> +++ b/include/hw/acpi/bios-linker-loader.h
>>> @@ -26,5 +26,11 @@ 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);
>>> +
>>>  void bios_linker_loader_cleanup(BIOSLinker *linker);
>>>  #endif
>>
> 

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 2/7] docs: VM Generation ID device description
  2017-02-15 11:07   ` Igor Mammedov
@ 2017-02-15 14:26     ` Laszlo Ersek
  0 siblings, 0 replies; 62+ messages in thread
From: Laszlo Ersek @ 2017-02-15 14:26 UTC (permalink / raw)
  To: Igor Mammedov, ben; +Cc: qemu-devel, Gal Hammer, mst

On 02/15/17 12:07, Igor Mammedov wrote:
> On Tue, 14 Feb 2017 22:15:44 -0800
> ben@skyportsystems.com wrote:
> 
>> From: Ben Warren <ben@skyportsystems.com>
>>
>> This patch is based off an earlier version by
>> Gal Hammer (ghammer@redhat.com)
>>
>> Requirements section, ASCII diagrams and overall help
>> provided by Laszlo Ersek (lersek@redhat.com)
>>
>> Signed-off-by: Gal Hammer <ghammer@redhat.com>
>> Signed-off-by: Ben Warren <ben@skyportsystems.com>
>> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> Reviewed-by: Igor Mammedov <imammedo@redhat.com>

I meticulously compared this version against the docs patch in v5, and I
agree that it's a good update.

I also see that

  [PATCH v6 4/7] ACPI: Add Virtual Machine Generation ID support

adds a reference to this file, as a comment, which was requested by Igor.

Looks good! My R-b stands.

Thank you,
Laszlo

>> ---
>>  docs/specs/vmgenid.txt | 245 +++++++++++++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 245 insertions(+)
>>  create mode 100644 docs/specs/vmgenid.txt
>>
>> diff --git a/docs/specs/vmgenid.txt b/docs/specs/vmgenid.txt
>> new file mode 100644
>> index 0000000..aa9f518
>> --- /dev/null
>> +++ b/docs/specs/vmgenid.txt
>> @@ -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.
> 

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 3/7] ACPI: Add vmgenid blob storage to the build tables
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 3/7] ACPI: Add vmgenid blob storage to the build tables ben
  2017-02-15 11:15   ` Igor Mammedov
@ 2017-02-15 14:30   ` Laszlo Ersek
  2017-02-16  6:11     ` Ben Warren
  1 sibling, 1 reply; 62+ messages in thread
From: Laszlo Ersek @ 2017-02-15 14:30 UTC (permalink / raw)
  To: ben, qemu-devel; +Cc: mst, imammedo

On 02/15/17 07:15, ben@skyportsystems.com wrote:
> From: Ben Warren <ben@skyportsystems.com>
> 
> This allows them to be centrally initialized and destroyed
> 
> The "AcpiBuildTables.vmgenid" array will be used to construct the
> "etc/vmgenid" fw_cfg blob.

Trivial wart: the blob is now called "etc/vmgenid_guid".

If you send a v7, feel free to fix it up. Not critical.

My R-b stands.

Thanks!
Laszlo

> Its contents will be linked into fw_cfg after being built on the
> pc_machine_done() -> acpi_setup() -> acpi_build() call path, and dropped
> without use on the subsequent, guest triggered, acpi_build_update() ->
> acpi_build() call path.
> 
> Signed-off-by: Ben Warren <ben@skyportsystems.com>
> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> ---
>  hw/acpi/aml-build.c         | 2 ++
>  include/hw/acpi/aml-build.h | 1 +
>  2 files changed, 3 insertions(+)
> 
> diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c
> index b2a1e40..c6f2032 100644
> --- a/hw/acpi/aml-build.c
> +++ b/hw/acpi/aml-build.c
> @@ -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 */
> diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h
> index 559326c..00c21f1 100644
> --- a/include/hw/acpi/aml-build.h
> +++ b/include/hw/acpi/aml-build.h
> @@ -210,6 +210,7 @@ struct AcpiBuildTables {
>      GArray *table_data;
>      GArray *rsdp;
>      GArray *tcpalog;
> +    GArray *vmgenid;
>      BIOSLinker *linker;
>  } AcpiBuildTables;
>  
> 

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15 14:13     ` Laszlo Ersek
  2017-02-15 14:17       ` Laszlo Ersek
@ 2017-02-15 15:22       ` Igor Mammedov
  2017-02-15 15:30         ` Michael S. Tsirkin
  1 sibling, 1 reply; 62+ messages in thread
From: Igor Mammedov @ 2017-02-15 15:22 UTC (permalink / raw)
  To: Laszlo Ersek; +Cc: ben, qemu-devel, mst

On Wed, 15 Feb 2017 15:13:20 +0100
Laszlo Ersek <lersek@redhat.com> wrote:

> Commenting under Igor's reply for simplicity
> 
> On 02/15/17 11:57, Igor Mammedov wrote:
> > On Tue, 14 Feb 2017 22:15:43 -0800
> > ben@skyportsystems.com wrote:
> >   
> >> From: Ben Warren <ben@skyportsystems.com>
> >>
> >> This is similar to the existing 'add pointer' functionality, but instead
> >> of instructing the guest (BIOS or UEFI) to patch memory, it instructs
> >> the guest to write the pointer back to QEMU via a writeable fw_cfg file.
> >>
> >> Signed-off-by: Ben Warren <ben@skyportsystems.com>
> >> ---
> >>  hw/acpi/bios-linker-loader.c         | 58 ++++++++++++++++++++++++++++++++++--
> >>  include/hw/acpi/bios-linker-loader.h |  6 ++++
> >>  2 files changed, 61 insertions(+), 3 deletions(-)
> >>
> >> diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
> >> index d963ebe..5030cf1 100644
> >> --- a/hw/acpi/bios-linker-loader.c
> >> +++ b/hw/acpi/bios-linker-loader.c
> >> @@ -78,6 +78,19 @@ 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 the table
> >> +         * originating from @src_file. 1,2,4 or 8 byte unsigned
> >> +         * addition is used depending on @wr_pointer.size.
> >> +         */  
> 
> The words "adding" and "addition" are causing confusion here.
> 
> In all of the previous discussion, *addition* was out of scope from
> WRITE_POINTER. Again, the firmware is specifically not required to
> *read* any part of the fw_cfg blob identified by "dest_file".
> 
> WRITE_POINTER instructs the firmware to return the allocation address of
> the downloaded "src_file" to QEMU. Any necessary runtime subscripting
> within "src_file" is to be handled by QEMU code dynamically.
> 
> For example, consider that "src_file" has *several* fields that QEMU
> wants to massage; in that case, indexing within QEMU code with field
> offsets is simply unavoidable.
what I don't like here is that this indexing would be rather fragile
and has to be done in different parts of QEMU /device, AML/.

I'd prefer this helper function to have the same @src_offset
behavior as ADD_POINTER where patched address could point to
any part of src_file i.e. not just beginning.



> (1) So, the above looks correct, but please replace "adding" with
> "storing", and "unsigned addition" with "store".
> 
> Side point: the case for ADD_POINTER is different; there we patch
> several individual ACPI objects. The fact that I requested explicit
> addition within the ADDR method, as opposed to pre-setting VGIA to a
> nonzero offset, is an *incidental* limitation (coming from the OVMF ACPI
> SDT header probe suppressor), and we'll likely fix that up later, with
> ALLOCATE command hints or something like that. However, in
> WRITE_POINTER, asking for the exact allocation address of "src_file" is
> an *inherent* characteristic.
> 
> For reference, this is the command's description from the (not as yet
> posted) OVMF series:
> 
> // QemuLoaderCmdWritePointer: the bytes at
> // [PointerOffset..PointerOffset+PointerSize) in the writeable fw_cfg
> // file PointerFile are to receive the absolute address of PointeeFile,
> // as allocated and downloaded by the firmware. Store the base address
> // of where PointeeFile's contents have been placed (when
> // QemuLoaderCmdAllocate has been executed for PointeeFile) to this
> // portion of PointerFile.
> //
> // This command is similar to QemuLoaderCmdAddPointer; the difference is
> // that the "pointer to patch" does not exist in guest-physical address
> // space, only in "fw_cfg file space". In addition, the "pointer to
> // patch" is not initialized by QEMU with a possibly nonzero offset
> // value: the base address of the memory allocated for downloading
> // PointeeFile shall not increment the pointer, but overwrite it.
> 
> In the last SeaBIOS patch series, namely
> 
> [SeaBIOS] [PATCH v3 2/2] QEMU fw_cfg: Add command to write back address
>                          of file
> 
> function romfile_loader_write_pointer() implemented just that plain
> store (not an addition), and that was exactly right.
> 
> Continuing:
> 
> >> +        struct {
> >> +            char dest_file[BIOS_LINKER_LOADER_FILESZ];
> >> +            char src_file[BIOS_LINKER_LOADER_FILESZ];
> >> +            uint32_t offset;
> >> +            uint8_t size;
> >> +        } wr_pointer;
> >> +
> >>          /* padding */
> >>          char pad[124];
> >>      };
> >> @@ -85,9 +98,10 @@ struct BiosLinkerLoaderEntry {
> >>  typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
> >>  
> >>  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_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 +292,41 @@ 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
> >> + */
> >> +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)  
> > API is missing "src_offset" even though it's not used in this series,
> > a patch on top to fix it up is ok for me as far as Seabios/OVMF
> > counterpart can handle src_offset correctly from starters.  
> 
> According to the above, it is the right thing not to add "src_offset"
> here. The documentation on the command is slightly incorrect (and causes
> confusion), but the helper function's signature and comments are okay.
> 
> >   
> >> +{
> >> +    BiosLinkerLoaderEntry entry;
> >> +    const BiosLinkerFileEntry *source_file =
> >> +        bios_linker_find_file(linker, src_file);
> >> +
> >> +    assert(source_file);  
> 
> I wish we kept the following asserts from bios_linker_loader_add_pointer():
> 
>     assert(dst_patched_offset < dst_file->blob->len);
>     assert(dst_patched_offset + dst_patched_size <= dst_file->blob->len);
> 
> Namely, just because the dst_file is never supposed to be downloaded by
> the firmware, it still remains a requirement that the "dst file offset
> range" that is to be rewritten *do fall* within the dst file.
> 
> Nonetheless, this is not critical. (OVMF at least verifies it anyway.)
> 
> Summary (from my side anyway): I feel that the documentation of the new
> command is very important. Please fix it up as suggested under (1), in
> v7. Regarding the asserts, I'll let you decide.
> 
> With the documentation fixed up:
> 
> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> 
> (If you don't wish to post a v7, I'm also completely fine if Michael or
> someone else fixes up the docs as proposed in (1), before committing the
> patch.)
> 
> Thanks!
> Laszlo
> 
> >> +    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.offset = cpu_to_le32(dst_patched_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);
> >> +}
> >> diff --git a/include/hw/acpi/bios-linker-loader.h b/include/hw/acpi/bios-linker-loader.h
> >> index fa1e5d1..f9ba5d6 100644
> >> --- a/include/hw/acpi/bios-linker-loader.h
> >> +++ b/include/hw/acpi/bios-linker-loader.h
> >> @@ -26,5 +26,11 @@ 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);
> >> +
> >>  void bios_linker_loader_cleanup(BIOSLinker *linker);
> >>  #endif  
> >   
> 

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 4/7] ACPI: Add Virtual Machine Generation ID support
  2017-02-15 12:19   ` Igor Mammedov
@ 2017-02-15 15:24     ` Laszlo Ersek
  2017-02-15 16:07       ` Igor Mammedov
  2017-02-16  6:13       ` Ben Warren
  2017-02-15 17:11     ` Ben Warren
  1 sibling, 2 replies; 62+ messages in thread
From: Laszlo Ersek @ 2017-02-15 15:24 UTC (permalink / raw)
  To: Igor Mammedov, ben; +Cc: qemu-devel, mst

On 02/15/17 13:19, Igor Mammedov wrote:
> On Tue, 14 Feb 2017 22:15:46 -0800
> ben@skyportsystems.com wrote:
> 
>> From: Ben Warren <ben@skyportsystems.com>
>>
>> This implements the VM Generation ID feature by passing a 128-bit
>> GUID to the guest via a fw_cfg blob.
>> Any time the GUID changes, an ACPI notify event is sent to the guest
>>
>> The user interface is a simple device with one parameter:
>>  - guid (string, must be "auto" or in UUID format
>>    xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
>>
>> Signed-off-by: Ben Warren <ben@skyportsystems.com>
>> ---
>>  default-configs/i386-softmmu.mak     |   1 +
>>  default-configs/x86_64-softmmu.mak   |   1 +
>>  hw/acpi/Makefile.objs                |   1 +
>>  hw/acpi/vmgenid.c                    | 237 +++++++++++++++++++++++++++++++++++
>>  hw/i386/acpi-build.c                 |  16 +++
>>  include/hw/acpi/acpi_dev_interface.h |   1 +
>>  include/hw/acpi/vmgenid.h            |  35 ++++++
>>  7 files changed, 292 insertions(+)
>>  create mode 100644 hw/acpi/vmgenid.c
>>  create mode 100644 include/hw/acpi/vmgenid.h
>>
>> diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
>> index 48b07a4..029e952 100644
>> --- a/default-configs/i386-softmmu.mak
>> +++ b/default-configs/i386-softmmu.mak
>> @@ -59,3 +59,4 @@ CONFIG_I82801B11=y
>>  CONFIG_SMBIOS=y
>>  CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
>>  CONFIG_PXB=y
>> +CONFIG_ACPI_VMGENID=y
>> diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
>> index fd96345..d1d7432 100644
>> --- a/default-configs/x86_64-softmmu.mak
>> +++ b/default-configs/x86_64-softmmu.mak
>> @@ -59,3 +59,4 @@ CONFIG_I82801B11=y
>>  CONFIG_SMBIOS=y
>>  CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
>>  CONFIG_PXB=y
>> +CONFIG_ACPI_VMGENID=y
>> diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
>> index 6acf798..11c35bc 100644
>> --- a/hw/acpi/Makefile.objs
>> +++ b/hw/acpi/Makefile.objs
>> @@ -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
>> diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c
>> new file mode 100644
>> index 0000000..b1b7b32
>> --- /dev/null
>> +++ b/hw/acpi/vmgenid.c
>> @@ -0,0 +1,237 @@
>> +/*
>> + *  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);
>> +    memcpy(&guid_le.data, &vms->guid.data, sizeof(vms->guid.data));
>> +    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));

Ben:

(1) The logic is sane here, but the initial sizing of the array is not
correct. The initial size should be

  (VMGENID_FW_CFG_SIZE - ARRAY_SIZE(guid_le.data))

The reason for this is that g_array_insert_vals() really inserts (it
doesn't overwrite) data, therefore it grows the array. From the GLib
source code [glib/garray.c]:

------------------
GArray*
g_array_insert_vals (GArray        *farray,
                     guint          index_,
                     gconstpointer  data,
                     guint          len)
{
  GRealArray *array = (GRealArray*) farray;

  g_return_val_if_fail (array, NULL);

  g_array_maybe_expand (array, len);

  memmove (g_array_elt_pos (array, len + index_),
           g_array_elt_pos (array, index_),
           g_array_elt_len (array, array->len - index_));

  memcpy (g_array_elt_pos (array, index_), data, g_array_elt_len (array,
len));

  array->len += len;

  g_array_zero_terminate (array);

  return farray;
}
------------------

This is an extremely minor wart, because later on:

>> +
>> +    /* 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))));
> Just curious,
> so suggested in v5 simple declaration style 
> 
> Package(2) {
>   ADD(VGIA, VMGENID_GUID_OFFSET),
>   0
> }
> 
> wasn't working for you?
> 
>> +    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.
>> +     */
>> +    bios_linker_loader_write_pointer(linker,
>> +        VMGENID_ADDR_FW_CFG_FILE, 0, sizeof(uint64_t),
>> +        VMGENID_GUID_FW_CFG_FILE);
>> +
>> +    /* 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);

we do expose the correct size to the guest (and the underlying QEMU-side
allocation is larger, not smaller than that, so it is safe).

So I guess I could call this an "innocent leak of 16 bytes" -- it's up
to you if you want to fix it. (I really don't want to obsess about this
at v6, I could have noticed the exact same in v5!)

>> +    /* 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.
>> +             */
>> +            memcpy(&guid_le.data, &vms->guid.data, sizeof(vms->guid.data));
> potential stack corruption if guid_le and vms->guid types ever diverge
> why not just do
>   guid_le.data = guid.data

Igor:

That would be an array assignment, and wouldn't work:

typedef struct {
    union {
        unsigned char data[16];
        struct {
            /* Generated in BE endian, can be swapped with
qemu_uuid_bswap. */
            uint32_t time_low;
            uint16_t time_mid;
            uint16_t time_high_and_version;
            uint8_t  clock_seq_and_reserved;
            uint8_t  clock_seq_low;
            uint8_t  node[6];
        } fields;
    };
} QemuUUID;

I think the code is safe: both the local variable "guid_le" and
"VmGenIdState.guid" are declared with type "QemuUUID".

I agree that a style improvement would be

  guid_le = vms->guid;

since structure assignment is okay.

Personally I feel neutrally about this.

> 
>> +            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 + VMGENID_GUID_OFFSET,
>> +                                      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_initfn(Object *obj)
>> +{
>> +    object_property_add_str(obj, VMGENID_GUID, NULL, vmgenid_set_guid, NULL);
> missing:
>   object_property_set_description()
> or even better use class properties here:
> 
> object_class_property_add_str()/object_class_property_set_description()
> 
>> +}
>> +
>> +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;
> it needs:
> 
> dc->hotpluggable = false;
> 
>> +}
>> +
>> +static const TypeInfo vmgenid_device_info = {
>> +    .name          = VMGENID_DEVICE,
>> +    .parent        = TYPE_DEVICE,
>> +    .instance_size = sizeof(VmGenIdState),
>> +    .instance_init = vmgenid_initfn,
>> +    .class_init    = vmgenid_device_class_init,
>> +};
>> +
>> +static void vmgenid_register_types(void)
>> +{
>> +    type_register_static(&vmgenid_device_info);
>> +}
>> +
>> +type_init(vmgenid_register_types)
>> diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
>> index 1c928ab..db04cf5 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/acpi/vmgenid.h"
>>  #include "sysemu/tpm_backend.h"
>>  #include "hw/timer/mc146818rtc_regs.h"
>>  #include "sysemu/numa.h"
>> @@ -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.
>> diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h
>> index 71d3c48..3c2e4e9 100644
>> --- a/include/hw/acpi/acpi_dev_interface.h
>> +++ b/include/hw/acpi/acpi_dev_interface.h
>> @@ -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"
>> diff --git a/include/hw/acpi/vmgenid.h b/include/hw/acpi/vmgenid.h
>> new file mode 100644
>> index 0000000..db7fa0e
>> --- /dev/null
>> +++ b/include/hw/acpi/vmgenid.h
>> @@ -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);
> What will happen if CLI would be:
>   -device vmgenid -device vmgenid
> As I understand, it should be exclusive single instance device,
> and there is nothing to prevent second instance of it.

Igor:

I agree with your observation, but I don't think it's a show-stopper
(not at this point anyway). I think it is similar to the case that I
raised earlier, about <= 2.6 machine types that have no DMA support in
fw_cfg, hence WRITE_POINTER cannot work on them.

Both of these cases (i.e., too early machine types, and multiple vmgenid
devices) come from "pathologic" command lines, and don't prevent the
intended use of the device (assuming a correct command line). So, I
think it should be safe to address these questions later, in a followup
series (for 2.10, likely).

Ben:

Summary:
- the sizing wart that I mentioned under (1) is innocent; it doesn't
deserve a repost on its own. If you do a v7, I suggest that you fix it
up, but I don't insist.

- Personally I'm fine with the rest. I see that Igor made some comments,
but I feel that a good chunk of those could have been made for v5 just
the same (example: dc->hotpluggable, object_property_set_description() /
class properties). I wouldn't like to delay this series any longer.
Those improvements can be added later, IMO -- but please do work out
with Igor whether he really wants a v7 for those.

I'm fine with the patch as-is, and I'm also fine with it if Igor's
comments are addressed:

Reviewed-by: Laszlo Ersek <lersek@redhat.com>

If you do other changes though, please drop my R-b.

... I'd like to look at the rest of this series a little, and then I'll
try to come back with test results (with OVMF).

Thanks!
Laszlo

> 
> 
>> +}
>> +
>> +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
> 

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15 15:22       ` Igor Mammedov
@ 2017-02-15 15:30         ` Michael S. Tsirkin
  2017-02-15 15:56           ` Igor Mammedov
  0 siblings, 1 reply; 62+ messages in thread
From: Michael S. Tsirkin @ 2017-02-15 15:30 UTC (permalink / raw)
  To: Igor Mammedov; +Cc: Laszlo Ersek, ben, qemu-devel

On Wed, Feb 15, 2017 at 04:22:25PM +0100, Igor Mammedov wrote:
> On Wed, 15 Feb 2017 15:13:20 +0100
> Laszlo Ersek <lersek@redhat.com> wrote:
> 
> > Commenting under Igor's reply for simplicity
> > 
> > On 02/15/17 11:57, Igor Mammedov wrote:
> > > On Tue, 14 Feb 2017 22:15:43 -0800
> > > ben@skyportsystems.com wrote:
> > >   
> > >> From: Ben Warren <ben@skyportsystems.com>
> > >>
> > >> This is similar to the existing 'add pointer' functionality, but instead
> > >> of instructing the guest (BIOS or UEFI) to patch memory, it instructs
> > >> the guest to write the pointer back to QEMU via a writeable fw_cfg file.
> > >>
> > >> Signed-off-by: Ben Warren <ben@skyportsystems.com>
> > >> ---
> > >>  hw/acpi/bios-linker-loader.c         | 58 ++++++++++++++++++++++++++++++++++--
> > >>  include/hw/acpi/bios-linker-loader.h |  6 ++++
> > >>  2 files changed, 61 insertions(+), 3 deletions(-)
> > >>
> > >> diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
> > >> index d963ebe..5030cf1 100644
> > >> --- a/hw/acpi/bios-linker-loader.c
> > >> +++ b/hw/acpi/bios-linker-loader.c
> > >> @@ -78,6 +78,19 @@ 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 the table
> > >> +         * originating from @src_file. 1,2,4 or 8 byte unsigned
> > >> +         * addition is used depending on @wr_pointer.size.
> > >> +         */  
> > 
> > The words "adding" and "addition" are causing confusion here.
> > 
> > In all of the previous discussion, *addition* was out of scope from
> > WRITE_POINTER. Again, the firmware is specifically not required to
> > *read* any part of the fw_cfg blob identified by "dest_file".
> > 
> > WRITE_POINTER instructs the firmware to return the allocation address of
> > the downloaded "src_file" to QEMU. Any necessary runtime subscripting
> > within "src_file" is to be handled by QEMU code dynamically.
> > 
> > For example, consider that "src_file" has *several* fields that QEMU
> > wants to massage; in that case, indexing within QEMU code with field
> > offsets is simply unavoidable.
> what I don't like here is that this indexing would be rather fragile
> and has to be done in different parts of QEMU /device, AML/.
> 
> I'd prefer this helper function to have the same @src_offset
> behavior as ADD_POINTER where patched address could point to
> any part of src_file i.e. not just beginning.



        /*
         * COMMAND_ADD_POINTER - patch the table (originating from
         * @dest_file) at @pointer.offset, by adding a pointer to the table
         * originating from @src_file. 1,2,4 or 8 byte unsigned
         * addition is used depending on @pointer.size.
         */
 
so the way ADD works is
	read at offset
	add table address
	write result at offset

in other words it is always beginning of table that is added.

Would the following be acceptable?


         * COMMAND_WRITE_POINTER - update the fw_cfg file (originating from
         * @dest_file) at @wr_pointer.offset, by writing a pointer to the table
         * originating from @src_file. 1,2,4 or 8 byte unsigned value
         * is written depending on @wr_pointer.size.


> 
> 
> > (1) So, the above looks correct, but please replace "adding" with
> > "storing", and "unsigned addition" with "store".
> > 
> > Side point: the case for ADD_POINTER is different; there we patch
> > several individual ACPI objects. The fact that I requested explicit
> > addition within the ADDR method, as opposed to pre-setting VGIA to a
> > nonzero offset, is an *incidental* limitation (coming from the OVMF ACPI
> > SDT header probe suppressor), and we'll likely fix that up later, with
> > ALLOCATE command hints or something like that. However, in
> > WRITE_POINTER, asking for the exact allocation address of "src_file" is
> > an *inherent* characteristic.
> > 
> > For reference, this is the command's description from the (not as yet
> > posted) OVMF series:
> > 
> > // QemuLoaderCmdWritePointer: the bytes at
> > // [PointerOffset..PointerOffset+PointerSize) in the writeable fw_cfg
> > // file PointerFile are to receive the absolute address of PointeeFile,
> > // as allocated and downloaded by the firmware. Store the base address
> > // of where PointeeFile's contents have been placed (when
> > // QemuLoaderCmdAllocate has been executed for PointeeFile) to this
> > // portion of PointerFile.
> > //
> > // This command is similar to QemuLoaderCmdAddPointer; the difference is
> > // that the "pointer to patch" does not exist in guest-physical address
> > // space, only in "fw_cfg file space". In addition, the "pointer to
> > // patch" is not initialized by QEMU with a possibly nonzero offset
> > // value: the base address of the memory allocated for downloading
> > // PointeeFile shall not increment the pointer, but overwrite it.
> > 
> > In the last SeaBIOS patch series, namely
> > 
> > [SeaBIOS] [PATCH v3 2/2] QEMU fw_cfg: Add command to write back address
> >                          of file
> > 
> > function romfile_loader_write_pointer() implemented just that plain
> > store (not an addition), and that was exactly right.
> > 
> > Continuing:
> > 
> > >> +        struct {
> > >> +            char dest_file[BIOS_LINKER_LOADER_FILESZ];
> > >> +            char src_file[BIOS_LINKER_LOADER_FILESZ];
> > >> +            uint32_t offset;
> > >> +            uint8_t size;
> > >> +        } wr_pointer;
> > >> +
> > >>          /* padding */
> > >>          char pad[124];
> > >>      };
> > >> @@ -85,9 +98,10 @@ struct BiosLinkerLoaderEntry {
> > >>  typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
> > >>  
> > >>  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_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 +292,41 @@ 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
> > >> + */
> > >> +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)  
> > > API is missing "src_offset" even though it's not used in this series,
> > > a patch on top to fix it up is ok for me as far as Seabios/OVMF
> > > counterpart can handle src_offset correctly from starters.  
> > 
> > According to the above, it is the right thing not to add "src_offset"
> > here. The documentation on the command is slightly incorrect (and causes
> > confusion), but the helper function's signature and comments are okay.
> > 
> > >   
> > >> +{
> > >> +    BiosLinkerLoaderEntry entry;
> > >> +    const BiosLinkerFileEntry *source_file =
> > >> +        bios_linker_find_file(linker, src_file);
> > >> +
> > >> +    assert(source_file);  
> > 
> > I wish we kept the following asserts from bios_linker_loader_add_pointer():
> > 
> >     assert(dst_patched_offset < dst_file->blob->len);
> >     assert(dst_patched_offset + dst_patched_size <= dst_file->blob->len);
> > 
> > Namely, just because the dst_file is never supposed to be downloaded by
> > the firmware, it still remains a requirement that the "dst file offset
> > range" that is to be rewritten *do fall* within the dst file.
> > 
> > Nonetheless, this is not critical. (OVMF at least verifies it anyway.)
> > 
> > Summary (from my side anyway): I feel that the documentation of the new
> > command is very important. Please fix it up as suggested under (1), in
> > v7. Regarding the asserts, I'll let you decide.
> > 
> > With the documentation fixed up:
> > 
> > Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> > 
> > (If you don't wish to post a v7, I'm also completely fine if Michael or
> > someone else fixes up the docs as proposed in (1), before committing the
> > patch.)
> > 
> > Thanks!
> > Laszlo
> > 
> > >> +    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.offset = cpu_to_le32(dst_patched_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);
> > >> +}
> > >> diff --git a/include/hw/acpi/bios-linker-loader.h b/include/hw/acpi/bios-linker-loader.h
> > >> index fa1e5d1..f9ba5d6 100644
> > >> --- a/include/hw/acpi/bios-linker-loader.h
> > >> +++ b/include/hw/acpi/bios-linker-loader.h
> > >> @@ -26,5 +26,11 @@ 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);
> > >> +
> > >>  void bios_linker_loader_cleanup(BIOSLinker *linker);
> > >>  #endif  
> > >   
> > 

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 5/7] qmp/hmp: add query-vm-generation-id and 'info vm-generation-id' commands
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 5/7] qmp/hmp: add query-vm-generation-id and 'info vm-generation-id' commands ben
@ 2017-02-15 15:36   ` Laszlo Ersek
  2017-02-16  6:13     ` Ben Warren
  0 siblings, 1 reply; 62+ messages in thread
From: Laszlo Ersek @ 2017-02-15 15:36 UTC (permalink / raw)
  To: ben, qemu-devel; +Cc: mst, imammedo

Two questions:

On 02/15/17 07:15, ben@skyportsystems.com wrote:
> From: Igor Mammedov <imammedo@redhat.com>
> 
> Add commands to query Virtual Machine Generation ID counter.
> 
> QMP command example:
>     { "execute": "query-vm-generation-id" }
> 
> HMP command example:
>     info vm-generation-id
> 
> Signed-off-by: Igor Mammedov <imammedo@redhat.com>
> Reviewed-by: Eric Blake <eblake@redhat.com>
> Signed-off-by: Ben Warren <ben@skyportsystems.com>
> ---
>  hmp-commands-info.hx | 13 +++++++++++++
>  hmp.c                |  9 +++++++++
>  hmp.h                |  1 +
>  hw/acpi/vmgenid.c    | 16 ++++++++++++++++
>  qapi-schema.json     | 20 ++++++++++++++++++++
>  stubs/Makefile.objs  |  1 +
>  stubs/vmgenid.c      |  8 ++++++++
>  7 files changed, 68 insertions(+)
>  create mode 100644 stubs/vmgenid.c
> 
> diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
> index b0f35e6..f3df793 100644
> --- a/hmp-commands-info.hx
> +++ b/hmp-commands-info.hx
> @@ -802,6 +802,19 @@ Show information about hotpluggable CPUs
>  ETEXI
>  
>  STEXI
> +@item info vm-generation-id

(1) Don't we need some kind of @findex here, for consistency with the
rest of the file?

> +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
>  
> diff --git a/hmp.c b/hmp.c
> index 2bc4f06..535613d 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -2565,3 +2565,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);
> +}
> diff --git a/hmp.h b/hmp.h
> index 05daf7c..799fd37 100644
> --- a/hmp.h
> +++ b/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
> diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c
> index b1b7b32..c159c76 100644
> --- a/hw/acpi/vmgenid.c
> +++ b/hw/acpi/vmgenid.c
> @@ -235,3 +235,19 @@ static void vmgenid_register_types(void)
>  }
>  
>  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;
> +}
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 61151f3..5e2a47f 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -6051,3 +6051,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' }
> diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
> index a187295..0bffca6 100644
> --- a/stubs/Makefile.objs
> +++ b/stubs/Makefile.objs
> @@ -35,3 +35,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
> diff --git a/stubs/vmgenid.c b/stubs/vmgenid.c
> new file mode 100644
> index 0000000..8c448ac
> --- /dev/null
> +++ b/stubs/vmgenid.c
> @@ -0,0 +1,8 @@
> +#include "qemu/osdep.h"
> +#include "qmp-commands.h"
> +
> +GuidInfo *qmp_query_vm_generation_id(Error **errp)
> +{
> +    error_setg(errp, "this command is not currently supported");
> +    return NULL;
> +}
> 

(2) Don't we usually employ QERR_UNSUPPORTED for the format string in
such cases?

With or without updates:

Reviewed-by: Laszlo Ersek <lersek@redhat.com>

Thanks
Laszlo

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15 15:30         ` Michael S. Tsirkin
@ 2017-02-15 15:56           ` Igor Mammedov
  2017-02-15 16:39             ` Michael S. Tsirkin
  0 siblings, 1 reply; 62+ messages in thread
From: Igor Mammedov @ 2017-02-15 15:56 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: Laszlo Ersek, ben, qemu-devel

On Wed, 15 Feb 2017 17:30:00 +0200
"Michael S. Tsirkin" <mst@redhat.com> wrote:

> On Wed, Feb 15, 2017 at 04:22:25PM +0100, Igor Mammedov wrote:
> > On Wed, 15 Feb 2017 15:13:20 +0100
> > Laszlo Ersek <lersek@redhat.com> wrote:
> >   
> > > Commenting under Igor's reply for simplicity
> > > 
> > > On 02/15/17 11:57, Igor Mammedov wrote:  
> > > > On Tue, 14 Feb 2017 22:15:43 -0800
> > > > ben@skyportsystems.com wrote:
> > > >     
> > > >> From: Ben Warren <ben@skyportsystems.com>
> > > >>
> > > >> This is similar to the existing 'add pointer' functionality, but instead
> > > >> of instructing the guest (BIOS or UEFI) to patch memory, it instructs
> > > >> the guest to write the pointer back to QEMU via a writeable fw_cfg file.
> > > >>
> > > >> Signed-off-by: Ben Warren <ben@skyportsystems.com>
> > > >> ---
> > > >>  hw/acpi/bios-linker-loader.c         | 58 ++++++++++++++++++++++++++++++++++--
> > > >>  include/hw/acpi/bios-linker-loader.h |  6 ++++
> > > >>  2 files changed, 61 insertions(+), 3 deletions(-)
> > > >>
> > > >> diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
> > > >> index d963ebe..5030cf1 100644
> > > >> --- a/hw/acpi/bios-linker-loader.c
> > > >> +++ b/hw/acpi/bios-linker-loader.c
> > > >> @@ -78,6 +78,19 @@ 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 the table
> > > >> +         * originating from @src_file. 1,2,4 or 8 byte unsigned
> > > >> +         * addition is used depending on @wr_pointer.size.
> > > >> +         */    
> > > 
> > > The words "adding" and "addition" are causing confusion here.
> > > 
> > > In all of the previous discussion, *addition* was out of scope from
> > > WRITE_POINTER. Again, the firmware is specifically not required to
> > > *read* any part of the fw_cfg blob identified by "dest_file".
> > > 
> > > WRITE_POINTER instructs the firmware to return the allocation address of
> > > the downloaded "src_file" to QEMU. Any necessary runtime subscripting
> > > within "src_file" is to be handled by QEMU code dynamically.
> > > 
> > > For example, consider that "src_file" has *several* fields that QEMU
> > > wants to massage; in that case, indexing within QEMU code with field
> > > offsets is simply unavoidable.  
> > what I don't like here is that this indexing would be rather fragile
> > and has to be done in different parts of QEMU /device, AML/.
> > 
> > I'd prefer this helper function to have the same @src_offset
> > behavior as ADD_POINTER where patched address could point to
> > any part of src_file i.e. not just beginning.  
> 
> 
> 
>         /*
>          * COMMAND_ADD_POINTER - patch the table (originating from
>          * @dest_file) at @pointer.offset, by adding a pointer to the table
>          * originating from @src_file. 1,2,4 or 8 byte unsigned
>          * addition is used depending on @pointer.size.
>          */
>  
> so the way ADD works is
> 	read at offset
> 	add table address
> 	write result at offset
> 
> in other words it is always beginning of table that is added.
more exactly it's, read at 
  src_offset = *(dst_blob_ptr+dst_offset)
  *(dst_blob+dst_offset) = src_blob_ptr + src_offset

> Would the following be acceptable?
> 
> 
>          * COMMAND_WRITE_POINTER - update the fw_cfg file (originating from
>          * @dest_file) at @wr_pointer.offset, by writing a pointer to the table
>          * originating from @src_file. 1,2,4 or 8 byte unsigned value
>          * is written depending on @wr_pointer.size.
it looses 'adding' part of ADD_POINTER command which handles src_offset,
however implementing adding part looks a bit complicated
as patched blob (dst) is not in guest memory but in QEMU and
on reset *(dst_blob+dst_offset) should be reset to src_offset.
Considering dst file could be device specific memory (field/blob/whatever)
it could be hard to track/notice proper reset behavior.

So now I'm not sure if src_offset is worth adding.

> 
> 
> > 
> >   
> > > (1) So, the above looks correct, but please replace "adding" with
> > > "storing", and "unsigned addition" with "store".
> > > 
> > > Side point: the case for ADD_POINTER is different; there we patch
> > > several individual ACPI objects. The fact that I requested explicit
> > > addition within the ADDR method, as opposed to pre-setting VGIA to a
> > > nonzero offset, is an *incidental* limitation (coming from the OVMF ACPI
> > > SDT header probe suppressor), and we'll likely fix that up later, with
> > > ALLOCATE command hints or something like that. However, in
> > > WRITE_POINTER, asking for the exact allocation address of "src_file" is
> > > an *inherent* characteristic.
> > > 
> > > For reference, this is the command's description from the (not as yet
> > > posted) OVMF series:
> > > 
> > > // QemuLoaderCmdWritePointer: the bytes at
> > > // [PointerOffset..PointerOffset+PointerSize) in the writeable fw_cfg
> > > // file PointerFile are to receive the absolute address of PointeeFile,
> > > // as allocated and downloaded by the firmware. Store the base address
> > > // of where PointeeFile's contents have been placed (when
> > > // QemuLoaderCmdAllocate has been executed for PointeeFile) to this
> > > // portion of PointerFile.
> > > //
> > > // This command is similar to QemuLoaderCmdAddPointer; the difference is
> > > // that the "pointer to patch" does not exist in guest-physical address
> > > // space, only in "fw_cfg file space". In addition, the "pointer to
> > > // patch" is not initialized by QEMU with a possibly nonzero offset
> > > // value: the base address of the memory allocated for downloading
> > > // PointeeFile shall not increment the pointer, but overwrite it.
> > > 
> > > In the last SeaBIOS patch series, namely
> > > 
> > > [SeaBIOS] [PATCH v3 2/2] QEMU fw_cfg: Add command to write back address
> > >                          of file
> > > 
> > > function romfile_loader_write_pointer() implemented just that plain
> > > store (not an addition), and that was exactly right.
> > > 
> > > Continuing:
> > >   
> > > >> +        struct {
> > > >> +            char dest_file[BIOS_LINKER_LOADER_FILESZ];
> > > >> +            char src_file[BIOS_LINKER_LOADER_FILESZ];
> > > >> +            uint32_t offset;
> > > >> +            uint8_t size;
> > > >> +        } wr_pointer;
> > > >> +
> > > >>          /* padding */
> > > >>          char pad[124];
> > > >>      };
> > > >> @@ -85,9 +98,10 @@ struct BiosLinkerLoaderEntry {
> > > >>  typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
> > > >>  
> > > >>  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_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 +292,41 @@ 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
> > > >> + */
> > > >> +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)    
> > > > API is missing "src_offset" even though it's not used in this series,
> > > > a patch on top to fix it up is ok for me as far as Seabios/OVMF
> > > > counterpart can handle src_offset correctly from starters.    
> > > 
> > > According to the above, it is the right thing not to add "src_offset"
> > > here. The documentation on the command is slightly incorrect (and causes
> > > confusion), but the helper function's signature and comments are okay.
> > >   
> > > >     
> > > >> +{
> > > >> +    BiosLinkerLoaderEntry entry;
> > > >> +    const BiosLinkerFileEntry *source_file =
> > > >> +        bios_linker_find_file(linker, src_file);
> > > >> +
> > > >> +    assert(source_file);    
> > > 
> > > I wish we kept the following asserts from bios_linker_loader_add_pointer():
> > > 
> > >     assert(dst_patched_offset < dst_file->blob->len);
> > >     assert(dst_patched_offset + dst_patched_size <= dst_file->blob->len);
> > > 
> > > Namely, just because the dst_file is never supposed to be downloaded by
> > > the firmware, it still remains a requirement that the "dst file offset
> > > range" that is to be rewritten *do fall* within the dst file.
> > > 
> > > Nonetheless, this is not critical. (OVMF at least verifies it anyway.)
> > > 
> > > Summary (from my side anyway): I feel that the documentation of the new
> > > command is very important. Please fix it up as suggested under (1), in
> > > v7. Regarding the asserts, I'll let you decide.
> > > 
> > > With the documentation fixed up:
> > > 
> > > Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> > > 
> > > (If you don't wish to post a v7, I'm also completely fine if Michael or
> > > someone else fixes up the docs as proposed in (1), before committing the
> > > patch.)
> > > 
> > > Thanks!
> > > Laszlo
> > >   
> > > >> +    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.offset = cpu_to_le32(dst_patched_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);
> > > >> +}
> > > >> diff --git a/include/hw/acpi/bios-linker-loader.h b/include/hw/acpi/bios-linker-loader.h
> > > >> index fa1e5d1..f9ba5d6 100644
> > > >> --- a/include/hw/acpi/bios-linker-loader.h
> > > >> +++ b/include/hw/acpi/bios-linker-loader.h
> > > >> @@ -26,5 +26,11 @@ 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);
> > > >> +
> > > >>  void bios_linker_loader_cleanup(BIOSLinker *linker);
> > > >>  #endif    
> > > >     
> > >   

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 4/7] ACPI: Add Virtual Machine Generation ID support
  2017-02-15 15:24     ` Laszlo Ersek
@ 2017-02-15 16:07       ` Igor Mammedov
  2017-02-15 16:40         ` Michael S. Tsirkin
  2017-02-16  6:13       ` Ben Warren
  1 sibling, 1 reply; 62+ messages in thread
From: Igor Mammedov @ 2017-02-15 16:07 UTC (permalink / raw)
  To: Laszlo Ersek; +Cc: ben, qemu-devel, mst

On Wed, 15 Feb 2017 16:24:52 +0100
Laszlo Ersek <lersek@redhat.com> wrote:

> On 02/15/17 13:19, Igor Mammedov wrote:
> > On Tue, 14 Feb 2017 22:15:46 -0800
> > ben@skyportsystems.com wrote:
> >   
> >> From: Ben Warren <ben@skyportsystems.com>
> >>
> >> This implements the VM Generation ID feature by passing a 128-bit
> >> GUID to the guest via a fw_cfg blob.
> >> Any time the GUID changes, an ACPI notify event is sent to the guest
> >>
> >> The user interface is a simple device with one parameter:
> >>  - guid (string, must be "auto" or in UUID format
> >>    xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
> >>
> >> Signed-off-by: Ben Warren <ben@skyportsystems.com>
> >> ---
> >>  default-configs/i386-softmmu.mak     |   1 +
> >>  default-configs/x86_64-softmmu.mak   |   1 +
> >>  hw/acpi/Makefile.objs                |   1 +
> >>  hw/acpi/vmgenid.c                    | 237 +++++++++++++++++++++++++++++++++++
> >>  hw/i386/acpi-build.c                 |  16 +++
> >>  include/hw/acpi/acpi_dev_interface.h |   1 +
> >>  include/hw/acpi/vmgenid.h            |  35 ++++++
> >>  7 files changed, 292 insertions(+)
> >>  create mode 100644 hw/acpi/vmgenid.c
> >>  create mode 100644 include/hw/acpi/vmgenid.h
> >>
> >> diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
> >> index 48b07a4..029e952 100644
> >> --- a/default-configs/i386-softmmu.mak
> >> +++ b/default-configs/i386-softmmu.mak
> >> @@ -59,3 +59,4 @@ CONFIG_I82801B11=y
> >>  CONFIG_SMBIOS=y
> >>  CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
> >>  CONFIG_PXB=y
> >> +CONFIG_ACPI_VMGENID=y
> >> diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
> >> index fd96345..d1d7432 100644
> >> --- a/default-configs/x86_64-softmmu.mak
> >> +++ b/default-configs/x86_64-softmmu.mak
> >> @@ -59,3 +59,4 @@ CONFIG_I82801B11=y
> >>  CONFIG_SMBIOS=y
> >>  CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
> >>  CONFIG_PXB=y
> >> +CONFIG_ACPI_VMGENID=y
> >> diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
> >> index 6acf798..11c35bc 100644
> >> --- a/hw/acpi/Makefile.objs
> >> +++ b/hw/acpi/Makefile.objs
> >> @@ -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
> >> diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c
> >> new file mode 100644
> >> index 0000000..b1b7b32
> >> --- /dev/null
> >> +++ b/hw/acpi/vmgenid.c
> >> @@ -0,0 +1,237 @@
> >> +/*
> >> + *  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);
> >> +    memcpy(&guid_le.data, &vms->guid.data, sizeof(vms->guid.data));
> >> +    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));  
> 
> Ben:
> 
> (1) The logic is sane here, but the initial sizing of the array is not
> correct. The initial size should be
> 
>   (VMGENID_FW_CFG_SIZE - ARRAY_SIZE(guid_le.data))
> 
> The reason for this is that g_array_insert_vals() really inserts (it
> doesn't overwrite) data, therefore it grows the array. From the GLib
> source code [glib/garray.c]:
> 
> ------------------
> GArray*
> g_array_insert_vals (GArray        *farray,
>                      guint          index_,
>                      gconstpointer  data,
>                      guint          len)
> {
>   GRealArray *array = (GRealArray*) farray;
> 
>   g_return_val_if_fail (array, NULL);
> 
>   g_array_maybe_expand (array, len);
> 
>   memmove (g_array_elt_pos (array, len + index_),
>            g_array_elt_pos (array, index_),
>            g_array_elt_len (array, array->len - index_));
> 
>   memcpy (g_array_elt_pos (array, index_), data, g_array_elt_len (array,
> len));
> 
>   array->len += len;
> 
>   g_array_zero_terminate (array);
> 
>   return farray;
> }
> ------------------
> 
> This is an extremely minor wart, because later on:
> 
> >> +
> >> +    /* 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))));  
> > Just curious,
> > so suggested in v5 simple declaration style 
> > 
> > Package(2) {
> >   ADD(VGIA, VMGENID_GUID_OFFSET),
> >   0
> > }
> > 
> > wasn't working for you?
> >   
> >> +    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.
> >> +     */
> >> +    bios_linker_loader_write_pointer(linker,
> >> +        VMGENID_ADDR_FW_CFG_FILE, 0, sizeof(uint64_t),
> >> +        VMGENID_GUID_FW_CFG_FILE);
> >> +
> >> +    /* 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);  
> 
> we do expose the correct size to the guest (and the underlying QEMU-side
> allocation is larger, not smaller than that, so it is safe).
> 
> So I guess I could call this an "innocent leak of 16 bytes" -- it's up
> to you if you want to fix it. (I really don't want to obsess about this
> at v6, I could have noticed the exact same in v5!)
> 
> >> +    /* 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.
> >> +             */
> >> +            memcpy(&guid_le.data, &vms->guid.data, sizeof(vms->guid.data));  
> > potential stack corruption if guid_le and vms->guid types ever diverge
> > why not just do
> >   guid_le.data = guid.data  
> 
> Igor:
> 
> That would be an array assignment, and wouldn't work:
> 
> typedef struct {
>     union {
>         unsigned char data[16];
>         struct {
>             /* Generated in BE endian, can be swapped with
> qemu_uuid_bswap. */
>             uint32_t time_low;
>             uint16_t time_mid;
>             uint16_t time_high_and_version;
>             uint8_t  clock_seq_and_reserved;
>             uint8_t  clock_seq_low;
>             uint8_t  node[6];
>         } fields;
>     };
> } QemuUUID;
> 
> I think the code is safe: both the local variable "guid_le" and
> "VmGenIdState.guid" are declared with type "QemuUUID".
> 
> I agree that a style improvement would be
> 
>   guid_le = vms->guid;
it looks much better than memcpy()

> 
> since structure assignment is okay.
> 
> Personally I feel neutrally about this.
> 
> >   
> >> +            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 + VMGENID_GUID_OFFSET,
> >> +                                      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_initfn(Object *obj)
> >> +{
> >> +    object_property_add_str(obj, VMGENID_GUID, NULL, vmgenid_set_guid, NULL);  
> > missing:
> >   object_property_set_description()
> > or even better use class properties here:
> > 
> > object_class_property_add_str()/object_class_property_set_description()
> >   
> >> +}
> >> +
> >> +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;  
> > it needs:
> > 
> > dc->hotpluggable = false;
> >   
> >> +}
> >> +
> >> +static const TypeInfo vmgenid_device_info = {
> >> +    .name          = VMGENID_DEVICE,
> >> +    .parent        = TYPE_DEVICE,
> >> +    .instance_size = sizeof(VmGenIdState),
> >> +    .instance_init = vmgenid_initfn,
> >> +    .class_init    = vmgenid_device_class_init,
> >> +};
> >> +
> >> +static void vmgenid_register_types(void)
> >> +{
> >> +    type_register_static(&vmgenid_device_info);
> >> +}
> >> +
> >> +type_init(vmgenid_register_types)
> >> diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
> >> index 1c928ab..db04cf5 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/acpi/vmgenid.h"
> >>  #include "sysemu/tpm_backend.h"
> >>  #include "hw/timer/mc146818rtc_regs.h"
> >>  #include "sysemu/numa.h"
> >> @@ -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.
> >> diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h
> >> index 71d3c48..3c2e4e9 100644
> >> --- a/include/hw/acpi/acpi_dev_interface.h
> >> +++ b/include/hw/acpi/acpi_dev_interface.h
> >> @@ -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"
> >> diff --git a/include/hw/acpi/vmgenid.h b/include/hw/acpi/vmgenid.h
> >> new file mode 100644
> >> index 0000000..db7fa0e
> >> --- /dev/null
> >> +++ b/include/hw/acpi/vmgenid.h
> >> @@ -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);  
> > What will happen if CLI would be:
> >   -device vmgenid -device vmgenid
> > As I understand, it should be exclusive single instance device,
> > and there is nothing to prevent second instance of it.  
> 
> Igor:
> 
> I agree with your observation, but I don't think it's a show-stopper
> (not at this point anyway). I think it is similar to the case that I
> raised earlier, about <= 2.6 machine types that have no DMA support in
> fw_cfg, hence WRITE_POINTER cannot work on them.
> 
> Both of these cases (i.e., too early machine types, and multiple vmgenid
> devices) come from "pathologic" command lines, and don't prevent the
> intended use of the device (assuming a correct command line). So, I
> think it should be safe to address these questions later, in a followup
> series (for 2.10, likely).
it is fixes so we can merge them in 2.9 during soft-freeze

> 
> Ben:
> 
> Summary:
> - the sizing wart that I mentioned under (1) is innocent; it doesn't
> deserve a repost on its own. If you do a v7, I suggest that you fix it
> up, but I don't insist.
> 
> - Personally I'm fine with the rest. I see that Igor made some comments,
> but I feel that a good chunk of those could have been made for v5 just
> the same (example: dc->hotpluggable, object_property_set_description() /
> class properties). I wouldn't like to delay this series any longer.
> Those improvements can be added later, IMO -- but please do work out
> with Igor whether he really wants a v7 for those.
since it's minor fixes not influencing other patches within series
there is not need to repost whole series,
just this fixed up patch as replay to this thread tagged as v7
or a patch on top, I'm fine with either way.

> 
> I'm fine with the patch as-is, and I'm also fine with it if Igor's
> comments are addressed:
> 
> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> 
> If you do other changes though, please drop my R-b.
> 
> ... I'd like to look at the rest of this series a little, and then I'll
> try to come back with test results (with OVMF).
> 
> Thanks!
> Laszlo
> 
> > 
> >   
> >> +}
> >> +
> >> +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  
> >   
> 

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15 15:56           ` Igor Mammedov
@ 2017-02-15 16:39             ` Michael S. Tsirkin
  2017-02-15 17:19               ` Laszlo Ersek
  2017-02-15 17:43               ` Igor Mammedov
  0 siblings, 2 replies; 62+ messages in thread
From: Michael S. Tsirkin @ 2017-02-15 16:39 UTC (permalink / raw)
  To: Igor Mammedov; +Cc: Laszlo Ersek, ben, qemu-devel

On Wed, Feb 15, 2017 at 04:56:02PM +0100, Igor Mammedov wrote:
> On Wed, 15 Feb 2017 17:30:00 +0200
> "Michael S. Tsirkin" <mst@redhat.com> wrote:
> 
> > On Wed, Feb 15, 2017 at 04:22:25PM +0100, Igor Mammedov wrote:
> > > On Wed, 15 Feb 2017 15:13:20 +0100
> > > Laszlo Ersek <lersek@redhat.com> wrote:
> > >   
> > > > Commenting under Igor's reply for simplicity
> > > > 
> > > > On 02/15/17 11:57, Igor Mammedov wrote:  
> > > > > On Tue, 14 Feb 2017 22:15:43 -0800
> > > > > ben@skyportsystems.com wrote:
> > > > >     
> > > > >> From: Ben Warren <ben@skyportsystems.com>
> > > > >>
> > > > >> This is similar to the existing 'add pointer' functionality, but instead
> > > > >> of instructing the guest (BIOS or UEFI) to patch memory, it instructs
> > > > >> the guest to write the pointer back to QEMU via a writeable fw_cfg file.
> > > > >>
> > > > >> Signed-off-by: Ben Warren <ben@skyportsystems.com>
> > > > >> ---
> > > > >>  hw/acpi/bios-linker-loader.c         | 58 ++++++++++++++++++++++++++++++++++--
> > > > >>  include/hw/acpi/bios-linker-loader.h |  6 ++++
> > > > >>  2 files changed, 61 insertions(+), 3 deletions(-)
> > > > >>
> > > > >> diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
> > > > >> index d963ebe..5030cf1 100644
> > > > >> --- a/hw/acpi/bios-linker-loader.c
> > > > >> +++ b/hw/acpi/bios-linker-loader.c
> > > > >> @@ -78,6 +78,19 @@ 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 the table
> > > > >> +         * originating from @src_file. 1,2,4 or 8 byte unsigned
> > > > >> +         * addition is used depending on @wr_pointer.size.
> > > > >> +         */    
> > > > 
> > > > The words "adding" and "addition" are causing confusion here.
> > > > 
> > > > In all of the previous discussion, *addition* was out of scope from
> > > > WRITE_POINTER. Again, the firmware is specifically not required to
> > > > *read* any part of the fw_cfg blob identified by "dest_file".
> > > > 
> > > > WRITE_POINTER instructs the firmware to return the allocation address of
> > > > the downloaded "src_file" to QEMU. Any necessary runtime subscripting
> > > > within "src_file" is to be handled by QEMU code dynamically.
> > > > 
> > > > For example, consider that "src_file" has *several* fields that QEMU
> > > > wants to massage; in that case, indexing within QEMU code with field
> > > > offsets is simply unavoidable.  
> > > what I don't like here is that this indexing would be rather fragile
> > > and has to be done in different parts of QEMU /device, AML/.
> > > 
> > > I'd prefer this helper function to have the same @src_offset
> > > behavior as ADD_POINTER where patched address could point to
> > > any part of src_file i.e. not just beginning.  
> > 
> > 
> > 
> >         /*
> >          * COMMAND_ADD_POINTER - patch the table (originating from
> >          * @dest_file) at @pointer.offset, by adding a pointer to the table
> >          * originating from @src_file. 1,2,4 or 8 byte unsigned
> >          * addition is used depending on @pointer.size.
> >          */
> >  
> > so the way ADD works is
> > 	read at offset
> > 	add table address
> > 	write result at offset
> > 
> > in other words it is always beginning of table that is added.
> more exactly it's, read at 
>   src_offset = *(dst_blob_ptr+dst_offset)
>   *(dst_blob+dst_offset) = src_blob_ptr + src_offset
> 
> > Would the following be acceptable?
> > 
> > 
> >          * COMMAND_WRITE_POINTER - update the fw_cfg file (originating from
> >          * @dest_file) at @wr_pointer.offset, by writing a pointer to the table
> >          * originating from @src_file. 1,2,4 or 8 byte unsigned value
> >          * is written depending on @wr_pointer.size.
> it looses 'adding' part of ADD_POINTER command which handles src_offset,
> however implementing adding part looks a bit complicated
> as patched blob (dst) is not in guest memory but in QEMU and
> on reset *(dst_blob+dst_offset) should be reset to src_offset.
> Considering dst file could be device specific memory (field/blob/whatever)
> it could be hard to track/notice proper reset behavior.
> 
> So now I'm not sure if src_offset is worth adding.

Right. Let's just do this math in QEMU if we have to.

> > 
> > 
> > > 
> > >   
> > > > (1) So, the above looks correct, but please replace "adding" with
> > > > "storing", and "unsigned addition" with "store".
> > > > 
> > > > Side point: the case for ADD_POINTER is different; there we patch
> > > > several individual ACPI objects. The fact that I requested explicit
> > > > addition within the ADDR method, as opposed to pre-setting VGIA to a
> > > > nonzero offset, is an *incidental* limitation (coming from the OVMF ACPI
> > > > SDT header probe suppressor), and we'll likely fix that up later, with
> > > > ALLOCATE command hints or something like that. However, in
> > > > WRITE_POINTER, asking for the exact allocation address of "src_file" is
> > > > an *inherent* characteristic.
> > > > 
> > > > For reference, this is the command's description from the (not as yet
> > > > posted) OVMF series:
> > > > 
> > > > // QemuLoaderCmdWritePointer: the bytes at
> > > > // [PointerOffset..PointerOffset+PointerSize) in the writeable fw_cfg
> > > > // file PointerFile are to receive the absolute address of PointeeFile,
> > > > // as allocated and downloaded by the firmware. Store the base address
> > > > // of where PointeeFile's contents have been placed (when
> > > > // QemuLoaderCmdAllocate has been executed for PointeeFile) to this
> > > > // portion of PointerFile.
> > > > //
> > > > // This command is similar to QemuLoaderCmdAddPointer; the difference is
> > > > // that the "pointer to patch" does not exist in guest-physical address
> > > > // space, only in "fw_cfg file space". In addition, the "pointer to
> > > > // patch" is not initialized by QEMU with a possibly nonzero offset
> > > > // value: the base address of the memory allocated for downloading
> > > > // PointeeFile shall not increment the pointer, but overwrite it.
> > > > 
> > > > In the last SeaBIOS patch series, namely
> > > > 
> > > > [SeaBIOS] [PATCH v3 2/2] QEMU fw_cfg: Add command to write back address
> > > >                          of file
> > > > 
> > > > function romfile_loader_write_pointer() implemented just that plain
> > > > store (not an addition), and that was exactly right.
> > > > 
> > > > Continuing:
> > > >   
> > > > >> +        struct {
> > > > >> +            char dest_file[BIOS_LINKER_LOADER_FILESZ];
> > > > >> +            char src_file[BIOS_LINKER_LOADER_FILESZ];
> > > > >> +            uint32_t offset;
> > > > >> +            uint8_t size;
> > > > >> +        } wr_pointer;
> > > > >> +
> > > > >>          /* padding */
> > > > >>          char pad[124];
> > > > >>      };
> > > > >> @@ -85,9 +98,10 @@ struct BiosLinkerLoaderEntry {
> > > > >>  typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
> > > > >>  
> > > > >>  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_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 +292,41 @@ 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
> > > > >> + */
> > > > >> +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)    
> > > > > API is missing "src_offset" even though it's not used in this series,
> > > > > a patch on top to fix it up is ok for me as far as Seabios/OVMF
> > > > > counterpart can handle src_offset correctly from starters.    
> > > > 
> > > > According to the above, it is the right thing not to add "src_offset"
> > > > here. The documentation on the command is slightly incorrect (and causes
> > > > confusion), but the helper function's signature and comments are okay.
> > > >   
> > > > >     
> > > > >> +{
> > > > >> +    BiosLinkerLoaderEntry entry;
> > > > >> +    const BiosLinkerFileEntry *source_file =
> > > > >> +        bios_linker_find_file(linker, src_file);
> > > > >> +
> > > > >> +    assert(source_file);    
> > > > 
> > > > I wish we kept the following asserts from bios_linker_loader_add_pointer():
> > > > 
> > > >     assert(dst_patched_offset < dst_file->blob->len);
> > > >     assert(dst_patched_offset + dst_patched_size <= dst_file->blob->len);
> > > > 
> > > > Namely, just because the dst_file is never supposed to be downloaded by
> > > > the firmware, it still remains a requirement that the "dst file offset
> > > > range" that is to be rewritten *do fall* within the dst file.
> > > > 
> > > > Nonetheless, this is not critical. (OVMF at least verifies it anyway.)
> > > > 
> > > > Summary (from my side anyway): I feel that the documentation of the new
> > > > command is very important. Please fix it up as suggested under (1), in
> > > > v7. Regarding the asserts, I'll let you decide.
> > > > 
> > > > With the documentation fixed up:
> > > > 
> > > > Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> > > > 
> > > > (If you don't wish to post a v7, I'm also completely fine if Michael or
> > > > someone else fixes up the docs as proposed in (1), before committing the
> > > > patch.)
> > > > 
> > > > Thanks!
> > > > Laszlo
> > > >   
> > > > >> +    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.offset = cpu_to_le32(dst_patched_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);
> > > > >> +}
> > > > >> diff --git a/include/hw/acpi/bios-linker-loader.h b/include/hw/acpi/bios-linker-loader.h
> > > > >> index fa1e5d1..f9ba5d6 100644
> > > > >> --- a/include/hw/acpi/bios-linker-loader.h
> > > > >> +++ b/include/hw/acpi/bios-linker-loader.h
> > > > >> @@ -26,5 +26,11 @@ 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);
> > > > >> +
> > > > >>  void bios_linker_loader_cleanup(BIOSLinker *linker);
> > > > >>  #endif    
> > > > >     
> > > >   

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 4/7] ACPI: Add Virtual Machine Generation ID support
  2017-02-15 16:07       ` Igor Mammedov
@ 2017-02-15 16:40         ` Michael S. Tsirkin
  2017-02-15 17:12           ` Ben Warren
  0 siblings, 1 reply; 62+ messages in thread
From: Michael S. Tsirkin @ 2017-02-15 16:40 UTC (permalink / raw)
  To: Igor Mammedov; +Cc: Laszlo Ersek, ben, qemu-devel

On Wed, Feb 15, 2017 at 05:07:57PM +0100, Igor Mammedov wrote:
> > Those improvements can be added later, IMO -- but please do work out
> > with Igor whether he really wants a v7 for those.
> since it's minor fixes not influencing other patches within series
> there is not need to repost whole series,
> just this fixed up patch as replay to this thread tagged as v7
> or a patch on top, I'm fine with either way.

OK, I'll merge v7, send more cleanups/fixes as patches on top pls.

-- 
MST

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 4/7] ACPI: Add Virtual Machine Generation ID support
  2017-02-15 12:19   ` Igor Mammedov
  2017-02-15 15:24     ` Laszlo Ersek
@ 2017-02-15 17:11     ` Ben Warren
  1 sibling, 0 replies; 62+ messages in thread
From: Ben Warren @ 2017-02-15 17:11 UTC (permalink / raw)
  To: Igor Mammedov; +Cc: qemu-devel, lersek, mst

[-- Attachment #1: Type: text/plain, Size: 17358 bytes --]

Hi Igor.  Thanks for the review!

> On Feb 15, 2017, at 4:19 AM, Igor Mammedov <imammedo@redhat.com> wrote:
> 
> On Tue, 14 Feb 2017 22:15:46 -0800
> ben@skyportsystems.com <mailto:ben@skyportsystems.com> wrote:
> 
>> From: Ben Warren <ben@skyportsystems.com>
>> 
>> This implements the VM Generation ID feature by passing a 128-bit
>> GUID to the guest via a fw_cfg blob.
>> Any time the GUID changes, an ACPI notify event is sent to the guest
>> 
>> The user interface is a simple device with one parameter:
>> - guid (string, must be "auto" or in UUID format
>>   xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
>> 
>> Signed-off-by: Ben Warren <ben@skyportsystems.com>
>> ---
>> default-configs/i386-softmmu.mak     |   1 +
>> default-configs/x86_64-softmmu.mak   |   1 +
>> hw/acpi/Makefile.objs                |   1 +
>> hw/acpi/vmgenid.c                    | 237 +++++++++++++++++++++++++++++++++++
>> hw/i386/acpi-build.c                 |  16 +++
>> include/hw/acpi/acpi_dev_interface.h |   1 +
>> include/hw/acpi/vmgenid.h            |  35 ++++++
>> 7 files changed, 292 insertions(+)
>> create mode 100644 hw/acpi/vmgenid.c
>> create mode 100644 include/hw/acpi/vmgenid.h
>> 
>> diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
>> index 48b07a4..029e952 100644
>> --- a/default-configs/i386-softmmu.mak
>> +++ b/default-configs/i386-softmmu.mak
>> @@ -59,3 +59,4 @@ CONFIG_I82801B11=y
>> CONFIG_SMBIOS=y
>> CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
>> CONFIG_PXB=y
>> +CONFIG_ACPI_VMGENID=y
>> diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
>> index fd96345..d1d7432 100644
>> --- a/default-configs/x86_64-softmmu.mak
>> +++ b/default-configs/x86_64-softmmu.mak
>> @@ -59,3 +59,4 @@ CONFIG_I82801B11=y
>> CONFIG_SMBIOS=y
>> CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
>> CONFIG_PXB=y
>> +CONFIG_ACPI_VMGENID=y
>> diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
>> index 6acf798..11c35bc 100644
>> --- a/hw/acpi/Makefile.objs
>> +++ b/hw/acpi/Makefile.objs
>> @@ -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
>> diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c
>> new file mode 100644
>> index 0000000..b1b7b32
>> --- /dev/null
>> +++ b/hw/acpi/vmgenid.c
>> @@ -0,0 +1,237 @@
>> +/*
>> + *  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);
>> +    memcpy(&guid_le.data, &vms->guid.data, sizeof(vms->guid.data));
>> +    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))));
> Just curious,
> so suggested in v5 simple declaration style 
> 
> Package(2) {
>  ADD(VGIA, VMGENID_GUID_OFFSET),
>  0
> }
> 
> wasn't working for you?
> 
I tried and tried and pulled my hair out and couldn’t get it to work.  I understand why this would be better, and will give it another quick go.
>> +    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.
>> +     */
>> +    bios_linker_loader_write_pointer(linker,
>> +        VMGENID_ADDR_FW_CFG_FILE, 0, sizeof(uint64_t),
>> +        VMGENID_GUID_FW_CFG_FILE);
>> +
>> +    /* 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.
>> +             */
>> +            memcpy(&guid_le.data, &vms->guid.data, sizeof(vms->guid.data));
> potential stack corruption if guid_le and vms->guid types ever diverge
> why not just do
>  guid_le.data = guid.data
> 
>> +            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 + VMGENID_GUID_OFFSET,
>> +                                      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_initfn(Object *obj)
>> +{
>> +    object_property_add_str(obj, VMGENID_GUID, NULL, vmgenid_set_guid, NULL);
> missing:
>  object_property_set_description()
> or even better use class properties here:
> 
> object_class_property_add_str()/object_class_property_set_description()
> 
>> +}
>> +
>> +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;
> it needs:
> 
> dc->hotpluggable = false;
> 
OK
>> +}
>> +
>> +static const TypeInfo vmgenid_device_info = {
>> +    .name          = VMGENID_DEVICE,
>> +    .parent        = TYPE_DEVICE,
>> +    .instance_size = sizeof(VmGenIdState),
>> +    .instance_init = vmgenid_initfn,
>> +    .class_init    = vmgenid_device_class_init,
>> +};
>> +
>> +static void vmgenid_register_types(void)
>> +{
>> +    type_register_static(&vmgenid_device_info);
>> +}
>> +
>> +type_init(vmgenid_register_types)
>> diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
>> index 1c928ab..db04cf5 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/acpi/vmgenid.h"
>> #include "sysemu/tpm_backend.h"
>> #include "hw/timer/mc146818rtc_regs.h"
>> #include "sysemu/numa.h"
>> @@ -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.
>> diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h
>> index 71d3c48..3c2e4e9 100644
>> --- a/include/hw/acpi/acpi_dev_interface.h
>> +++ b/include/hw/acpi/acpi_dev_interface.h
>> @@ -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"
>> diff --git a/include/hw/acpi/vmgenid.h b/include/hw/acpi/vmgenid.h
>> new file mode 100644
>> index 0000000..db7fa0e
>> --- /dev/null
>> +++ b/include/hw/acpi/vmgenid.h
>> @@ -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);
> What will happen if CLI would be:
>  -device vmgenid -device vmgenid
> As I understand, it should be exclusive single instance device,
> and there is nothing to prevent second instance of it.
> 
I don’t know, I’ll see what I can do here.
> 
>> +}
>> +
>> +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


[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 3583 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 4/7] ACPI: Add Virtual Machine Generation ID support
  2017-02-15 16:40         ` Michael S. Tsirkin
@ 2017-02-15 17:12           ` Ben Warren
  0 siblings, 0 replies; 62+ messages in thread
From: Ben Warren @ 2017-02-15 17:12 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: Igor Mammedov, Laszlo Ersek, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 713 bytes --]


> On Feb 15, 2017, at 8:40 AM, Michael S. Tsirkin <mst@redhat.com> wrote:
> 
> On Wed, Feb 15, 2017 at 05:07:57PM +0100, Igor Mammedov wrote:
>>> Those improvements can be added later, IMO -- but please do work out
>>> with Igor whether he really wants a v7 for those.
>> since it's minor fixes not influencing other patches within series
>> there is not need to repost whole series,
>> just this fixed up patch as replay to this thread tagged as v7
>> or a patch on top, I'm fine with either way.
> 
> OK, I'll merge v7, send more cleanups/fixes as patches on top pls.
> 
> -- 
> MST
Thanks everybody.  The requested changes are pretty minor so I should be able to turn v7 around today.

—Ben

[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 3583 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15 16:39             ` Michael S. Tsirkin
@ 2017-02-15 17:19               ` Laszlo Ersek
  2017-02-15 17:43               ` Igor Mammedov
  1 sibling, 0 replies; 62+ messages in thread
From: Laszlo Ersek @ 2017-02-15 17:19 UTC (permalink / raw)
  To: Michael S. Tsirkin, Igor Mammedov; +Cc: ben, qemu-devel

On 02/15/17 17:39, Michael S. Tsirkin wrote:
> On Wed, Feb 15, 2017 at 04:56:02PM +0100, Igor Mammedov wrote:
>> On Wed, 15 Feb 2017 17:30:00 +0200
>> "Michael S. Tsirkin" <mst@redhat.com> wrote:
>>
>>> On Wed, Feb 15, 2017 at 04:22:25PM +0100, Igor Mammedov wrote:
>>>> On Wed, 15 Feb 2017 15:13:20 +0100
>>>> Laszlo Ersek <lersek@redhat.com> wrote:
>>>>   
>>>>> Commenting under Igor's reply for simplicity
>>>>>
>>>>> On 02/15/17 11:57, Igor Mammedov wrote:  
>>>>>> On Tue, 14 Feb 2017 22:15:43 -0800
>>>>>> ben@skyportsystems.com wrote:
>>>>>>     
>>>>>>> From: Ben Warren <ben@skyportsystems.com>
>>>>>>>
>>>>>>> This is similar to the existing 'add pointer' functionality, but instead
>>>>>>> of instructing the guest (BIOS or UEFI) to patch memory, it instructs
>>>>>>> the guest to write the pointer back to QEMU via a writeable fw_cfg file.
>>>>>>>
>>>>>>> Signed-off-by: Ben Warren <ben@skyportsystems.com>
>>>>>>> ---
>>>>>>>  hw/acpi/bios-linker-loader.c         | 58 ++++++++++++++++++++++++++++++++++--
>>>>>>>  include/hw/acpi/bios-linker-loader.h |  6 ++++
>>>>>>>  2 files changed, 61 insertions(+), 3 deletions(-)
>>>>>>>
>>>>>>> diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
>>>>>>> index d963ebe..5030cf1 100644
>>>>>>> --- a/hw/acpi/bios-linker-loader.c
>>>>>>> +++ b/hw/acpi/bios-linker-loader.c
>>>>>>> @@ -78,6 +78,19 @@ 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 the table
>>>>>>> +         * originating from @src_file. 1,2,4 or 8 byte unsigned
>>>>>>> +         * addition is used depending on @wr_pointer.size.
>>>>>>> +         */    
>>>>>
>>>>> The words "adding" and "addition" are causing confusion here.
>>>>>
>>>>> In all of the previous discussion, *addition* was out of scope from
>>>>> WRITE_POINTER. Again, the firmware is specifically not required to
>>>>> *read* any part of the fw_cfg blob identified by "dest_file".
>>>>>
>>>>> WRITE_POINTER instructs the firmware to return the allocation address of
>>>>> the downloaded "src_file" to QEMU. Any necessary runtime subscripting
>>>>> within "src_file" is to be handled by QEMU code dynamically.
>>>>>
>>>>> For example, consider that "src_file" has *several* fields that QEMU
>>>>> wants to massage; in that case, indexing within QEMU code with field
>>>>> offsets is simply unavoidable.  
>>>> what I don't like here is that this indexing would be rather fragile
>>>> and has to be done in different parts of QEMU /device, AML/.
>>>>
>>>> I'd prefer this helper function to have the same @src_offset
>>>> behavior as ADD_POINTER where patched address could point to
>>>> any part of src_file i.e. not just beginning.  
>>>
>>>
>>>
>>>         /*
>>>          * COMMAND_ADD_POINTER - patch the table (originating from
>>>          * @dest_file) at @pointer.offset, by adding a pointer to the table
>>>          * originating from @src_file. 1,2,4 or 8 byte unsigned
>>>          * addition is used depending on @pointer.size.
>>>          */
>>>  
>>> so the way ADD works is
>>> 	read at offset
>>> 	add table address
>>> 	write result at offset
>>>
>>> in other words it is always beginning of table that is added.
>> more exactly it's, read at 
>>   src_offset = *(dst_blob_ptr+dst_offset)
>>   *(dst_blob+dst_offset) = src_blob_ptr + src_offset
>>
>>> Would the following be acceptable?
>>>
>>>
>>>          * COMMAND_WRITE_POINTER - update the fw_cfg file (originating from
>>>          * @dest_file) at @wr_pointer.offset, by writing a pointer to the table
>>>          * originating from @src_file. 1,2,4 or 8 byte unsigned value
>>>          * is written depending on @wr_pointer.size.
>> it looses 'adding' part of ADD_POINTER command which handles src_offset,
>> however implementing adding part looks a bit complicated
>> as patched blob (dst) is not in guest memory but in QEMU and
>> on reset *(dst_blob+dst_offset) should be reset to src_offset.
>> Considering dst file could be device specific memory (field/blob/whatever)
>> it could be hard to track/notice proper reset behavior.
>>
>> So now I'm not sure if src_offset is worth adding.
> 
> Right. Let's just do this math in QEMU if we have to.

Deal. :)

Thanks
Laszlo

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15 16:39             ` Michael S. Tsirkin
  2017-02-15 17:19               ` Laszlo Ersek
@ 2017-02-15 17:43               ` Igor Mammedov
  2017-02-15 17:54                 ` Ben Warren
  2017-02-15 18:04                 ` Michael S. Tsirkin
  1 sibling, 2 replies; 62+ messages in thread
From: Igor Mammedov @ 2017-02-15 17:43 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: Laszlo Ersek, qemu-devel, ben

On Wed, 15 Feb 2017 18:39:06 +0200
"Michael S. Tsirkin" <mst@redhat.com> wrote:

> On Wed, Feb 15, 2017 at 04:56:02PM +0100, Igor Mammedov wrote:
> > On Wed, 15 Feb 2017 17:30:00 +0200
> > "Michael S. Tsirkin" <mst@redhat.com> wrote:
> >   
> > > On Wed, Feb 15, 2017 at 04:22:25PM +0100, Igor Mammedov wrote:  
> > > > On Wed, 15 Feb 2017 15:13:20 +0100
> > > > Laszlo Ersek <lersek@redhat.com> wrote:
> > > >     
> > > > > Commenting under Igor's reply for simplicity
> > > > > 
> > > > > On 02/15/17 11:57, Igor Mammedov wrote:    
> > > > > > On Tue, 14 Feb 2017 22:15:43 -0800
> > > > > > ben@skyportsystems.com wrote:
> > > > > >       
> > > > > >> From: Ben Warren <ben@skyportsystems.com>
> > > > > >>
> > > > > >> This is similar to the existing 'add pointer' functionality, but instead
> > > > > >> of instructing the guest (BIOS or UEFI) to patch memory, it instructs
> > > > > >> the guest to write the pointer back to QEMU via a writeable fw_cfg file.
> > > > > >>
> > > > > >> Signed-off-by: Ben Warren <ben@skyportsystems.com>
> > > > > >> ---
> > > > > >>  hw/acpi/bios-linker-loader.c         | 58 ++++++++++++++++++++++++++++++++++--
> > > > > >>  include/hw/acpi/bios-linker-loader.h |  6 ++++
> > > > > >>  2 files changed, 61 insertions(+), 3 deletions(-)
> > > > > >>
> > > > > >> diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
> > > > > >> index d963ebe..5030cf1 100644
> > > > > >> --- a/hw/acpi/bios-linker-loader.c
> > > > > >> +++ b/hw/acpi/bios-linker-loader.c
> > > > > >> @@ -78,6 +78,19 @@ 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 the table
> > > > > >> +         * originating from @src_file. 1,2,4 or 8 byte unsigned
> > > > > >> +         * addition is used depending on @wr_pointer.size.
> > > > > >> +         */      
> > > > > 
> > > > > The words "adding" and "addition" are causing confusion here.
> > > > > 
> > > > > In all of the previous discussion, *addition* was out of scope from
> > > > > WRITE_POINTER. Again, the firmware is specifically not required to
> > > > > *read* any part of the fw_cfg blob identified by "dest_file".
> > > > > 
> > > > > WRITE_POINTER instructs the firmware to return the allocation address of
> > > > > the downloaded "src_file" to QEMU. Any necessary runtime subscripting
> > > > > within "src_file" is to be handled by QEMU code dynamically.
> > > > > 
> > > > > For example, consider that "src_file" has *several* fields that QEMU
> > > > > wants to massage; in that case, indexing within QEMU code with field
> > > > > offsets is simply unavoidable.    
> > > > what I don't like here is that this indexing would be rather fragile
> > > > and has to be done in different parts of QEMU /device, AML/.
> > > > 
> > > > I'd prefer this helper function to have the same @src_offset
> > > > behavior as ADD_POINTER where patched address could point to
> > > > any part of src_file i.e. not just beginning.    
> > > 
> > > 
> > > 
> > >         /*
> > >          * COMMAND_ADD_POINTER - patch the table (originating from
> > >          * @dest_file) at @pointer.offset, by adding a pointer to the table
> > >          * originating from @src_file. 1,2,4 or 8 byte unsigned
> > >          * addition is used depending on @pointer.size.
> > >          */
> > >  
> > > so the way ADD works is
> > > 	read at offset
> > > 	add table address
> > > 	write result at offset
> > > 
> > > in other words it is always beginning of table that is added.  
> > more exactly it's, read at 
> >   src_offset = *(dst_blob_ptr+dst_offset)
> >   *(dst_blob+dst_offset) = src_blob_ptr + src_offset
> >   
> > > Would the following be acceptable?
> > > 
> > > 
> > >          * COMMAND_WRITE_POINTER - update the fw_cfg file (originating from
> > >          * @dest_file) at @wr_pointer.offset, by writing a pointer to the table
> > >          * originating from @src_file. 1,2,4 or 8 byte unsigned value
> > >          * is written depending on @wr_pointer.size.  
> > it looses 'adding' part of ADD_POINTER command which handles src_offset,
> > however implementing adding part looks a bit complicated
> > as patched blob (dst) is not in guest memory but in QEMU and
> > on reset *(dst_blob+dst_offset) should be reset to src_offset.
> > Considering dst file could be device specific memory (field/blob/whatever)
> > it could be hard to track/notice proper reset behavior.
> > 
> > So now I'm not sure if src_offset is worth adding.  
> 
> Right. Let's just do this math in QEMU if we have to.
Math complicates QEMU code though and not only QMEMU but AML code as well.
Considering that we are adding a new command and don't have to keep
any sort of compatibility we can pass src_offset as part
of command instead of hiding it inside of dst_file.
Something like this:

        /*
         * COMMAND_WRITE_POINTER - write the fw_cfg file (originating from
         * @dest_file) at @wr_pointer.offset, by writing 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 offset;
+            uint32_t dst_offset;
+            uint32_t src_offset;
             uint8_t size;
        } wr_pointer;

> 
> > > 
> > >   
> > > > 
> > > >     
> > > > > (1) So, the above looks correct, but please replace "adding" with
> > > > > "storing", and "unsigned addition" with "store".
> > > > > 
> > > > > Side point: the case for ADD_POINTER is different; there we patch
> > > > > several individual ACPI objects. The fact that I requested explicit
> > > > > addition within the ADDR method, as opposed to pre-setting VGIA to a
> > > > > nonzero offset, is an *incidental* limitation (coming from the OVMF ACPI
> > > > > SDT header probe suppressor), and we'll likely fix that up later, with
> > > > > ALLOCATE command hints or something like that. However, in
> > > > > WRITE_POINTER, asking for the exact allocation address of "src_file" is
> > > > > an *inherent* characteristic.
> > > > > 
> > > > > For reference, this is the command's description from the (not as yet
> > > > > posted) OVMF series:
> > > > > 
> > > > > // QemuLoaderCmdWritePointer: the bytes at
> > > > > // [PointerOffset..PointerOffset+PointerSize) in the writeable fw_cfg
> > > > > // file PointerFile are to receive the absolute address of PointeeFile,
> > > > > // as allocated and downloaded by the firmware. Store the base address
> > > > > // of where PointeeFile's contents have been placed (when
> > > > > // QemuLoaderCmdAllocate has been executed for PointeeFile) to this
> > > > > // portion of PointerFile.
> > > > > //
> > > > > // This command is similar to QemuLoaderCmdAddPointer; the difference is
> > > > > // that the "pointer to patch" does not exist in guest-physical address
> > > > > // space, only in "fw_cfg file space". In addition, the "pointer to
> > > > > // patch" is not initialized by QEMU with a possibly nonzero offset
> > > > > // value: the base address of the memory allocated for downloading
> > > > > // PointeeFile shall not increment the pointer, but overwrite it.
> > > > > 
> > > > > In the last SeaBIOS patch series, namely
> > > > > 
> > > > > [SeaBIOS] [PATCH v3 2/2] QEMU fw_cfg: Add command to write back address
> > > > >                          of file
> > > > > 
> > > > > function romfile_loader_write_pointer() implemented just that plain
> > > > > store (not an addition), and that was exactly right.
> > > > > 
> > > > > Continuing:
> > > > >     
> > > > > >> +        struct {
> > > > > >> +            char dest_file[BIOS_LINKER_LOADER_FILESZ];
> > > > > >> +            char src_file[BIOS_LINKER_LOADER_FILESZ];
> > > > > >> +            uint32_t offset;
> > > > > >> +            uint8_t size;
> > > > > >> +        } wr_pointer;
> > > > > >> +
> > > > > >>          /* padding */
> > > > > >>          char pad[124];
> > > > > >>      };
> > > > > >> @@ -85,9 +98,10 @@ struct BiosLinkerLoaderEntry {
> > > > > >>  typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
> > > > > >>  
> > > > > >>  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_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 +292,41 @@ 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
> > > > > >> + */
> > > > > >> +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)      
> > > > > > API is missing "src_offset" even though it's not used in this series,
> > > > > > a patch on top to fix it up is ok for me as far as Seabios/OVMF
> > > > > > counterpart can handle src_offset correctly from starters.      
> > > > > 
> > > > > According to the above, it is the right thing not to add "src_offset"
> > > > > here. The documentation on the command is slightly incorrect (and causes
> > > > > confusion), but the helper function's signature and comments are okay.
> > > > >     
> > > > > >       
> > > > > >> +{
> > > > > >> +    BiosLinkerLoaderEntry entry;
> > > > > >> +    const BiosLinkerFileEntry *source_file =
> > > > > >> +        bios_linker_find_file(linker, src_file);
> > > > > >> +
> > > > > >> +    assert(source_file);      
> > > > > 
> > > > > I wish we kept the following asserts from bios_linker_loader_add_pointer():
> > > > > 
> > > > >     assert(dst_patched_offset < dst_file->blob->len);
> > > > >     assert(dst_patched_offset + dst_patched_size <= dst_file->blob->len);
> > > > > 
> > > > > Namely, just because the dst_file is never supposed to be downloaded by
> > > > > the firmware, it still remains a requirement that the "dst file offset
> > > > > range" that is to be rewritten *do fall* within the dst file.
> > > > > 
> > > > > Nonetheless, this is not critical. (OVMF at least verifies it anyway.)
> > > > > 
> > > > > Summary (from my side anyway): I feel that the documentation of the new
> > > > > command is very important. Please fix it up as suggested under (1), in
> > > > > v7. Regarding the asserts, I'll let you decide.
> > > > > 
> > > > > With the documentation fixed up:
> > > > > 
> > > > > Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> > > > > 
> > > > > (If you don't wish to post a v7, I'm also completely fine if Michael or
> > > > > someone else fixes up the docs as proposed in (1), before committing the
> > > > > patch.)
> > > > > 
> > > > > Thanks!
> > > > > Laszlo
> > > > >     
> > > > > >> +    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.offset = cpu_to_le32(dst_patched_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);
> > > > > >> +}
> > > > > >> diff --git a/include/hw/acpi/bios-linker-loader.h b/include/hw/acpi/bios-linker-loader.h
> > > > > >> index fa1e5d1..f9ba5d6 100644
> > > > > >> --- a/include/hw/acpi/bios-linker-loader.h
> > > > > >> +++ b/include/hw/acpi/bios-linker-loader.h
> > > > > >> @@ -26,5 +26,11 @@ 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);
> > > > > >> +
> > > > > >>  void bios_linker_loader_cleanup(BIOSLinker *linker);
> > > > > >>  #endif      
> > > > > >       
> > > > >     
> 

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15 17:43               ` Igor Mammedov
@ 2017-02-15 17:54                 ` Ben Warren
  2017-02-15 18:06                   ` Michael S. Tsirkin
  2017-02-15 18:04                 ` Michael S. Tsirkin
  1 sibling, 1 reply; 62+ messages in thread
From: Ben Warren @ 2017-02-15 17:54 UTC (permalink / raw)
  To: Igor Mammedov; +Cc: Michael S. Tsirkin, Laszlo Ersek, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 13818 bytes --]


> On Feb 15, 2017, at 9:43 AM, Igor Mammedov <imammedo@redhat.com> wrote:
> 
> On Wed, 15 Feb 2017 18:39:06 +0200
> "Michael S. Tsirkin" <mst@redhat.com <mailto:mst@redhat.com>> wrote:
> 
>> On Wed, Feb 15, 2017 at 04:56:02PM +0100, Igor Mammedov wrote:
>>> On Wed, 15 Feb 2017 17:30:00 +0200
>>> "Michael S. Tsirkin" <mst@redhat.com> wrote:
>>> 
>>>> On Wed, Feb 15, 2017 at 04:22:25PM +0100, Igor Mammedov wrote:  
>>>>> On Wed, 15 Feb 2017 15:13:20 +0100
>>>>> Laszlo Ersek <lersek@redhat.com> wrote:
>>>>> 
>>>>>> Commenting under Igor's reply for simplicity
>>>>>> 
>>>>>> On 02/15/17 11:57, Igor Mammedov wrote:    
>>>>>>> On Tue, 14 Feb 2017 22:15:43 -0800
>>>>>>> ben@skyportsystems.com wrote:
>>>>>>> 
>>>>>>>> From: Ben Warren <ben@skyportsystems.com>
>>>>>>>> 
>>>>>>>> This is similar to the existing 'add pointer' functionality, but instead
>>>>>>>> of instructing the guest (BIOS or UEFI) to patch memory, it instructs
>>>>>>>> the guest to write the pointer back to QEMU via a writeable fw_cfg file.
>>>>>>>> 
>>>>>>>> Signed-off-by: Ben Warren <ben@skyportsystems.com>
>>>>>>>> ---
>>>>>>>> hw/acpi/bios-linker-loader.c         | 58 ++++++++++++++++++++++++++++++++++--
>>>>>>>> include/hw/acpi/bios-linker-loader.h |  6 ++++
>>>>>>>> 2 files changed, 61 insertions(+), 3 deletions(-)
>>>>>>>> 
>>>>>>>> diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
>>>>>>>> index d963ebe..5030cf1 100644
>>>>>>>> --- a/hw/acpi/bios-linker-loader.c
>>>>>>>> +++ b/hw/acpi/bios-linker-loader.c
>>>>>>>> @@ -78,6 +78,19 @@ 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 the table
>>>>>>>> +         * originating from @src_file. 1,2,4 or 8 byte unsigned
>>>>>>>> +         * addition is used depending on @wr_pointer.size.
>>>>>>>> +         */      
>>>>>> 
>>>>>> The words "adding" and "addition" are causing confusion here.
>>>>>> 
>>>>>> In all of the previous discussion, *addition* was out of scope from
>>>>>> WRITE_POINTER. Again, the firmware is specifically not required to
>>>>>> *read* any part of the fw_cfg blob identified by "dest_file".
>>>>>> 
>>>>>> WRITE_POINTER instructs the firmware to return the allocation address of
>>>>>> the downloaded "src_file" to QEMU. Any necessary runtime subscripting
>>>>>> within "src_file" is to be handled by QEMU code dynamically.
>>>>>> 
>>>>>> For example, consider that "src_file" has *several* fields that QEMU
>>>>>> wants to massage; in that case, indexing within QEMU code with field
>>>>>> offsets is simply unavoidable.    
>>>>> what I don't like here is that this indexing would be rather fragile
>>>>> and has to be done in different parts of QEMU /device, AML/.
>>>>> 
>>>>> I'd prefer this helper function to have the same @src_offset
>>>>> behavior as ADD_POINTER where patched address could point to
>>>>> any part of src_file i.e. not just beginning.    
>>>> 
>>>> 
>>>> 
>>>>        /*
>>>>         * COMMAND_ADD_POINTER - patch the table (originating from
>>>>         * @dest_file) at @pointer.offset, by adding a pointer to the table
>>>>         * originating from @src_file. 1,2,4 or 8 byte unsigned
>>>>         * addition is used depending on @pointer.size.
>>>>         */
>>>> 
>>>> so the way ADD works is
>>>> 	read at offset
>>>> 	add table address
>>>> 	write result at offset
>>>> 
>>>> in other words it is always beginning of table that is added.  
>>> more exactly it's, read at 
>>>  src_offset = *(dst_blob_ptr+dst_offset)
>>>  *(dst_blob+dst_offset) = src_blob_ptr + src_offset
>>> 
>>>> Would the following be acceptable?
>>>> 
>>>> 
>>>>         * COMMAND_WRITE_POINTER - update the fw_cfg file (originating from
>>>>         * @dest_file) at @wr_pointer.offset, by writing a pointer to the table
>>>>         * originating from @src_file. 1,2,4 or 8 byte unsigned value
>>>>         * is written depending on @wr_pointer.size.  
>>> it looses 'adding' part of ADD_POINTER command which handles src_offset,
>>> however implementing adding part looks a bit complicated
>>> as patched blob (dst) is not in guest memory but in QEMU and
>>> on reset *(dst_blob+dst_offset) should be reset to src_offset.
>>> Considering dst file could be device specific memory (field/blob/whatever)
>>> it could be hard to track/notice proper reset behavior.
>>> 
>>> So now I'm not sure if src_offset is worth adding.  
>> 
>> Right. Let's just do this math in QEMU if we have to.
> Math complicates QEMU code though and not only QMEMU but AML code as well.
> Considering that we are adding a new command and don't have to keep
> any sort of compatibility we can pass src_offset as part
> of command instead of hiding it inside of dst_file.
> Something like this:
> 
>        /*
>         * COMMAND_WRITE_POINTER - write the fw_cfg file (originating from
>         * @dest_file) at @wr_pointer.offset, by writing 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 offset;
> +            uint32_t dst_offset;
> +            uint32_t src_offset;
>             uint8_t size;
>        } wr_pointer;
> 
OK, this is easy enough to do and maybe we’ll have a use case in the future.  I’ll make this change in v7

>> 
>>>> 
>>>> 
>>>>> 
>>>>> 
>>>>>> (1) So, the above looks correct, but please replace "adding" with
>>>>>> "storing", and "unsigned addition" with "store".
>>>>>> 
>>>>>> Side point: the case for ADD_POINTER is different; there we patch
>>>>>> several individual ACPI objects. The fact that I requested explicit
>>>>>> addition within the ADDR method, as opposed to pre-setting VGIA to a
>>>>>> nonzero offset, is an *incidental* limitation (coming from the OVMF ACPI
>>>>>> SDT header probe suppressor), and we'll likely fix that up later, with
>>>>>> ALLOCATE command hints or something like that. However, in
>>>>>> WRITE_POINTER, asking for the exact allocation address of "src_file" is
>>>>>> an *inherent* characteristic.
>>>>>> 
>>>>>> For reference, this is the command's description from the (not as yet
>>>>>> posted) OVMF series:
>>>>>> 
>>>>>> // QemuLoaderCmdWritePointer: the bytes at
>>>>>> // [PointerOffset..PointerOffset+PointerSize) in the writeable fw_cfg
>>>>>> // file PointerFile are to receive the absolute address of PointeeFile,
>>>>>> // as allocated and downloaded by the firmware. Store the base address
>>>>>> // of where PointeeFile's contents have been placed (when
>>>>>> // QemuLoaderCmdAllocate has been executed for PointeeFile) to this
>>>>>> // portion of PointerFile.
>>>>>> //
>>>>>> // This command is similar to QemuLoaderCmdAddPointer; the difference is
>>>>>> // that the "pointer to patch" does not exist in guest-physical address
>>>>>> // space, only in "fw_cfg file space". In addition, the "pointer to
>>>>>> // patch" is not initialized by QEMU with a possibly nonzero offset
>>>>>> // value: the base address of the memory allocated for downloading
>>>>>> // PointeeFile shall not increment the pointer, but overwrite it.
>>>>>> 
>>>>>> In the last SeaBIOS patch series, namely
>>>>>> 
>>>>>> [SeaBIOS] [PATCH v3 2/2] QEMU fw_cfg: Add command to write back address
>>>>>>                         of file
>>>>>> 
>>>>>> function romfile_loader_write_pointer() implemented just that plain
>>>>>> store (not an addition), and that was exactly right.
>>>>>> 
>>>>>> Continuing:
>>>>>> 
>>>>>>>> +        struct {
>>>>>>>> +            char dest_file[BIOS_LINKER_LOADER_FILESZ];
>>>>>>>> +            char src_file[BIOS_LINKER_LOADER_FILESZ];
>>>>>>>> +            uint32_t offset;
>>>>>>>> +            uint8_t size;
>>>>>>>> +        } wr_pointer;
>>>>>>>> +
>>>>>>>>         /* padding */
>>>>>>>>         char pad[124];
>>>>>>>>     };
>>>>>>>> @@ -85,9 +98,10 @@ struct BiosLinkerLoaderEntry {
>>>>>>>> typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
>>>>>>>> 
>>>>>>>> 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_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 +292,41 @@ 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
>>>>>>>> + */
>>>>>>>> +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)      
>>>>>>> API is missing "src_offset" even though it's not used in this series,
>>>>>>> a patch on top to fix it up is ok for me as far as Seabios/OVMF
>>>>>>> counterpart can handle src_offset correctly from starters.      
>>>>>> 
>>>>>> According to the above, it is the right thing not to add "src_offset"
>>>>>> here. The documentation on the command is slightly incorrect (and causes
>>>>>> confusion), but the helper function's signature and comments are okay.
>>>>>> 
>>>>>>> 
>>>>>>>> +{
>>>>>>>> +    BiosLinkerLoaderEntry entry;
>>>>>>>> +    const BiosLinkerFileEntry *source_file =
>>>>>>>> +        bios_linker_find_file(linker, src_file);
>>>>>>>> +
>>>>>>>> +    assert(source_file);      
>>>>>> 
>>>>>> I wish we kept the following asserts from bios_linker_loader_add_pointer():
>>>>>> 
>>>>>>    assert(dst_patched_offset < dst_file->blob->len);
>>>>>>    assert(dst_patched_offset + dst_patched_size <= dst_file->blob->len);
>>>>>> 
>>>>>> Namely, just because the dst_file is never supposed to be downloaded by
>>>>>> the firmware, it still remains a requirement that the "dst file offset
>>>>>> range" that is to be rewritten *do fall* within the dst file.
>>>>>> 
>>>>>> Nonetheless, this is not critical. (OVMF at least verifies it anyway.)
>>>>>> 
>>>>>> Summary (from my side anyway): I feel that the documentation of the new
>>>>>> command is very important. Please fix it up as suggested under (1), in
>>>>>> v7. Regarding the asserts, I'll let you decide.
>>>>>> 
>>>>>> With the documentation fixed up:
>>>>>> 
>>>>>> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
>>>>>> 
>>>>>> (If you don't wish to post a v7, I'm also completely fine if Michael or
>>>>>> someone else fixes up the docs as proposed in (1), before committing the
>>>>>> patch.)
>>>>>> 
>>>>>> Thanks!
>>>>>> Laszlo
>>>>>> 
>>>>>>>> +    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.offset = cpu_to_le32(dst_patched_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);
>>>>>>>> +}
>>>>>>>> diff --git a/include/hw/acpi/bios-linker-loader.h b/include/hw/acpi/bios-linker-loader.h
>>>>>>>> index fa1e5d1..f9ba5d6 100644
>>>>>>>> --- a/include/hw/acpi/bios-linker-loader.h
>>>>>>>> +++ b/include/hw/acpi/bios-linker-loader.h
>>>>>>>> @@ -26,5 +26,11 @@ 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);
>>>>>>>> +
>>>>>>>> void bios_linker_loader_cleanup(BIOSLinker *linker);
>>>>>>>> #endif      


[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 3583 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15 17:43               ` Igor Mammedov
  2017-02-15 17:54                 ` Ben Warren
@ 2017-02-15 18:04                 ` Michael S. Tsirkin
  2017-02-15 18:24                   ` Igor Mammedov
  1 sibling, 1 reply; 62+ messages in thread
From: Michael S. Tsirkin @ 2017-02-15 18:04 UTC (permalink / raw)
  To: Igor Mammedov; +Cc: Laszlo Ersek, qemu-devel, ben

On Wed, Feb 15, 2017 at 06:43:09PM +0100, Igor Mammedov wrote:
> On Wed, 15 Feb 2017 18:39:06 +0200
> "Michael S. Tsirkin" <mst@redhat.com> wrote:
> 
> > On Wed, Feb 15, 2017 at 04:56:02PM +0100, Igor Mammedov wrote:
> > > On Wed, 15 Feb 2017 17:30:00 +0200
> > > "Michael S. Tsirkin" <mst@redhat.com> wrote:
> > >   
> > > > On Wed, Feb 15, 2017 at 04:22:25PM +0100, Igor Mammedov wrote:  
> > > > > On Wed, 15 Feb 2017 15:13:20 +0100
> > > > > Laszlo Ersek <lersek@redhat.com> wrote:
> > > > >     
> > > > > > Commenting under Igor's reply for simplicity
> > > > > > 
> > > > > > On 02/15/17 11:57, Igor Mammedov wrote:    
> > > > > > > On Tue, 14 Feb 2017 22:15:43 -0800
> > > > > > > ben@skyportsystems.com wrote:
> > > > > > >       
> > > > > > >> From: Ben Warren <ben@skyportsystems.com>
> > > > > > >>
> > > > > > >> This is similar to the existing 'add pointer' functionality, but instead
> > > > > > >> of instructing the guest (BIOS or UEFI) to patch memory, it instructs
> > > > > > >> the guest to write the pointer back to QEMU via a writeable fw_cfg file.
> > > > > > >>
> > > > > > >> Signed-off-by: Ben Warren <ben@skyportsystems.com>
> > > > > > >> ---
> > > > > > >>  hw/acpi/bios-linker-loader.c         | 58 ++++++++++++++++++++++++++++++++++--
> > > > > > >>  include/hw/acpi/bios-linker-loader.h |  6 ++++
> > > > > > >>  2 files changed, 61 insertions(+), 3 deletions(-)
> > > > > > >>
> > > > > > >> diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
> > > > > > >> index d963ebe..5030cf1 100644
> > > > > > >> --- a/hw/acpi/bios-linker-loader.c
> > > > > > >> +++ b/hw/acpi/bios-linker-loader.c
> > > > > > >> @@ -78,6 +78,19 @@ 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 the table
> > > > > > >> +         * originating from @src_file. 1,2,4 or 8 byte unsigned
> > > > > > >> +         * addition is used depending on @wr_pointer.size.
> > > > > > >> +         */      
> > > > > > 
> > > > > > The words "adding" and "addition" are causing confusion here.
> > > > > > 
> > > > > > In all of the previous discussion, *addition* was out of scope from
> > > > > > WRITE_POINTER. Again, the firmware is specifically not required to
> > > > > > *read* any part of the fw_cfg blob identified by "dest_file".
> > > > > > 
> > > > > > WRITE_POINTER instructs the firmware to return the allocation address of
> > > > > > the downloaded "src_file" to QEMU. Any necessary runtime subscripting
> > > > > > within "src_file" is to be handled by QEMU code dynamically.
> > > > > > 
> > > > > > For example, consider that "src_file" has *several* fields that QEMU
> > > > > > wants to massage; in that case, indexing within QEMU code with field
> > > > > > offsets is simply unavoidable.    
> > > > > what I don't like here is that this indexing would be rather fragile
> > > > > and has to be done in different parts of QEMU /device, AML/.
> > > > > 
> > > > > I'd prefer this helper function to have the same @src_offset
> > > > > behavior as ADD_POINTER where patched address could point to
> > > > > any part of src_file i.e. not just beginning.    
> > > > 
> > > > 
> > > > 
> > > >         /*
> > > >          * COMMAND_ADD_POINTER - patch the table (originating from
> > > >          * @dest_file) at @pointer.offset, by adding a pointer to the table
> > > >          * originating from @src_file. 1,2,4 or 8 byte unsigned
> > > >          * addition is used depending on @pointer.size.
> > > >          */
> > > >  
> > > > so the way ADD works is
> > > > 	read at offset
> > > > 	add table address
> > > > 	write result at offset
> > > > 
> > > > in other words it is always beginning of table that is added.  
> > > more exactly it's, read at 
> > >   src_offset = *(dst_blob_ptr+dst_offset)
> > >   *(dst_blob+dst_offset) = src_blob_ptr + src_offset
> > >   
> > > > Would the following be acceptable?
> > > > 
> > > > 
> > > >          * COMMAND_WRITE_POINTER - update the fw_cfg file (originating from
> > > >          * @dest_file) at @wr_pointer.offset, by writing a pointer to the table
> > > >          * originating from @src_file. 1,2,4 or 8 byte unsigned value
> > > >          * is written depending on @wr_pointer.size.  
> > > it looses 'adding' part of ADD_POINTER command which handles src_offset,
> > > however implementing adding part looks a bit complicated
> > > as patched blob (dst) is not in guest memory but in QEMU and
> > > on reset *(dst_blob+dst_offset) should be reset to src_offset.
> > > Considering dst file could be device specific memory (field/blob/whatever)
> > > it could be hard to track/notice proper reset behavior.
> > > 
> > > So now I'm not sure if src_offset is worth adding.  
> > 
> > Right. Let's just do this math in QEMU if we have to.
> Math complicates QEMU code though and not only QMEMU but AML code as well.
> Considering that we are adding a new command and don't have to keep
> any sort of compatibility we can pass src_offset as part
> of command instead of hiding it inside of dst_file.
> Something like this:
> 
>         /*
>          * COMMAND_WRITE_POINTER - write the fw_cfg file (originating from
>          * @dest_file) at @wr_pointer.offset, by writing 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 offset;
> +            uint32_t dst_offset;
> +            uint32_t src_offset;
>              uint8_t size;
>         } wr_pointer;


As long as all users pass in 0 though there's a real possibility guests
will implement this incorrectly. I guess we can put in the offset just
behind the zero-filled padding we have there.

I'm mostly concerned we are adding new features to something
that has been through 25 revisions already.


> > 
> > > > 
> > > >   
> > > > > 
> > > > >     
> > > > > > (1) So, the above looks correct, but please replace "adding" with
> > > > > > "storing", and "unsigned addition" with "store".
> > > > > > 
> > > > > > Side point: the case for ADD_POINTER is different; there we patch
> > > > > > several individual ACPI objects. The fact that I requested explicit
> > > > > > addition within the ADDR method, as opposed to pre-setting VGIA to a
> > > > > > nonzero offset, is an *incidental* limitation (coming from the OVMF ACPI
> > > > > > SDT header probe suppressor), and we'll likely fix that up later, with
> > > > > > ALLOCATE command hints or something like that. However, in
> > > > > > WRITE_POINTER, asking for the exact allocation address of "src_file" is
> > > > > > an *inherent* characteristic.
> > > > > > 
> > > > > > For reference, this is the command's description from the (not as yet
> > > > > > posted) OVMF series:
> > > > > > 
> > > > > > // QemuLoaderCmdWritePointer: the bytes at
> > > > > > // [PointerOffset..PointerOffset+PointerSize) in the writeable fw_cfg
> > > > > > // file PointerFile are to receive the absolute address of PointeeFile,
> > > > > > // as allocated and downloaded by the firmware. Store the base address
> > > > > > // of where PointeeFile's contents have been placed (when
> > > > > > // QemuLoaderCmdAllocate has been executed for PointeeFile) to this
> > > > > > // portion of PointerFile.
> > > > > > //
> > > > > > // This command is similar to QemuLoaderCmdAddPointer; the difference is
> > > > > > // that the "pointer to patch" does not exist in guest-physical address
> > > > > > // space, only in "fw_cfg file space". In addition, the "pointer to
> > > > > > // patch" is not initialized by QEMU with a possibly nonzero offset
> > > > > > // value: the base address of the memory allocated for downloading
> > > > > > // PointeeFile shall not increment the pointer, but overwrite it.
> > > > > > 
> > > > > > In the last SeaBIOS patch series, namely
> > > > > > 
> > > > > > [SeaBIOS] [PATCH v3 2/2] QEMU fw_cfg: Add command to write back address
> > > > > >                          of file
> > > > > > 
> > > > > > function romfile_loader_write_pointer() implemented just that plain
> > > > > > store (not an addition), and that was exactly right.
> > > > > > 
> > > > > > Continuing:
> > > > > >     
> > > > > > >> +        struct {
> > > > > > >> +            char dest_file[BIOS_LINKER_LOADER_FILESZ];
> > > > > > >> +            char src_file[BIOS_LINKER_LOADER_FILESZ];
> > > > > > >> +            uint32_t offset;
> > > > > > >> +            uint8_t size;
> > > > > > >> +        } wr_pointer;
> > > > > > >> +
> > > > > > >>          /* padding */
> > > > > > >>          char pad[124];
> > > > > > >>      };
> > > > > > >> @@ -85,9 +98,10 @@ struct BiosLinkerLoaderEntry {
> > > > > > >>  typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
> > > > > > >>  
> > > > > > >>  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_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 +292,41 @@ 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
> > > > > > >> + */
> > > > > > >> +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)      
> > > > > > > API is missing "src_offset" even though it's not used in this series,
> > > > > > > a patch on top to fix it up is ok for me as far as Seabios/OVMF
> > > > > > > counterpart can handle src_offset correctly from starters.      
> > > > > > 
> > > > > > According to the above, it is the right thing not to add "src_offset"
> > > > > > here. The documentation on the command is slightly incorrect (and causes
> > > > > > confusion), but the helper function's signature and comments are okay.
> > > > > >     
> > > > > > >       
> > > > > > >> +{
> > > > > > >> +    BiosLinkerLoaderEntry entry;
> > > > > > >> +    const BiosLinkerFileEntry *source_file =
> > > > > > >> +        bios_linker_find_file(linker, src_file);
> > > > > > >> +
> > > > > > >> +    assert(source_file);      
> > > > > > 
> > > > > > I wish we kept the following asserts from bios_linker_loader_add_pointer():
> > > > > > 
> > > > > >     assert(dst_patched_offset < dst_file->blob->len);
> > > > > >     assert(dst_patched_offset + dst_patched_size <= dst_file->blob->len);
> > > > > > 
> > > > > > Namely, just because the dst_file is never supposed to be downloaded by
> > > > > > the firmware, it still remains a requirement that the "dst file offset
> > > > > > range" that is to be rewritten *do fall* within the dst file.
> > > > > > 
> > > > > > Nonetheless, this is not critical. (OVMF at least verifies it anyway.)
> > > > > > 
> > > > > > Summary (from my side anyway): I feel that the documentation of the new
> > > > > > command is very important. Please fix it up as suggested under (1), in
> > > > > > v7. Regarding the asserts, I'll let you decide.
> > > > > > 
> > > > > > With the documentation fixed up:
> > > > > > 
> > > > > > Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> > > > > > 
> > > > > > (If you don't wish to post a v7, I'm also completely fine if Michael or
> > > > > > someone else fixes up the docs as proposed in (1), before committing the
> > > > > > patch.)
> > > > > > 
> > > > > > Thanks!
> > > > > > Laszlo
> > > > > >     
> > > > > > >> +    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.offset = cpu_to_le32(dst_patched_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);
> > > > > > >> +}
> > > > > > >> diff --git a/include/hw/acpi/bios-linker-loader.h b/include/hw/acpi/bios-linker-loader.h
> > > > > > >> index fa1e5d1..f9ba5d6 100644
> > > > > > >> --- a/include/hw/acpi/bios-linker-loader.h
> > > > > > >> +++ b/include/hw/acpi/bios-linker-loader.h
> > > > > > >> @@ -26,5 +26,11 @@ 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);
> > > > > > >> +
> > > > > > >>  void bios_linker_loader_cleanup(BIOSLinker *linker);
> > > > > > >>  #endif      
> > > > > > >       
> > > > > >     
> > 

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15 17:54                 ` Ben Warren
@ 2017-02-15 18:06                   ` Michael S. Tsirkin
  2017-02-15 18:14                     ` Ben Warren
  0 siblings, 1 reply; 62+ messages in thread
From: Michael S. Tsirkin @ 2017-02-15 18:06 UTC (permalink / raw)
  To: Ben Warren; +Cc: Igor Mammedov, Laszlo Ersek, qemu-devel

On Wed, Feb 15, 2017 at 09:54:08AM -0800, Ben Warren wrote:
> 
>     On Feb 15, 2017, at 9:43 AM, Igor Mammedov <imammedo@redhat.com> wrote:
> 
>     On Wed, 15 Feb 2017 18:39:06 +0200
>     "Michael S. Tsirkin" <mst@redhat.com> wrote:
> 
> 
>         On Wed, Feb 15, 2017 at 04:56:02PM +0100, Igor Mammedov wrote:
> 
>             On Wed, 15 Feb 2017 17:30:00 +0200
>             "Michael S. Tsirkin" <mst@redhat.com> wrote:
> 
> 
>                 On Wed, Feb 15, 2017 at 04:22:25PM +0100, Igor Mammedov wrote:
>                  
> 
>                     On Wed, 15 Feb 2017 15:13:20 +0100
>                     Laszlo Ersek <lersek@redhat.com> wrote:
> 
> 
>                         Commenting under Igor's reply for simplicity
> 
>                         On 02/15/17 11:57, Igor Mammedov wrote:    
> 
>                             On Tue, 14 Feb 2017 22:15:43 -0800
>                             ben@skyportsystems.com wrote:
> 
> 
>                                 From: Ben Warren <ben@skyportsystems.com>
> 
>                                 This is similar to the existing 'add pointer'
>                                 functionality, but instead
>                                 of instructing the guest (BIOS or UEFI) to
>                                 patch memory, it instructs
>                                 the guest to write the pointer back to QEMU via
>                                 a writeable fw_cfg file.
> 
>                                 Signed-off-by: Ben Warren <
>                                 ben@skyportsystems.com>
>                                 ---
>                                 hw/acpi/bios-linker-loader.c         | 58
>                                 ++++++++++++++++++++++++++++++++++--
>                                 include/hw/acpi/bios-linker-loader.h |  6 ++++
>                                 2 files changed, 61 insertions(+), 3 deletions
>                                 (-)
> 
>                                 diff --git a/hw/acpi/bios-linker-loader.c b/hw/
>                                 acpi/bios-linker-loader.c
>                                 index d963ebe..5030cf1 100644
>                                 --- a/hw/acpi/bios-linker-loader.c
>                                 +++ b/hw/acpi/bios-linker-loader.c
>                                 @@ -78,6 +78,19 @@ 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 the table
>                                 +         * originating from @src_file. 1,2,4
>                                 or 8 byte unsigned
>                                 +         * addition is used depending on
>                                 @wr_pointer.size.
>                                 +         */      
> 
> 
>                         The words "adding" and "addition" are causing confusion
>                         here.
> 
>                         In all of the previous discussion, *addition* was out
>                         of scope from
>                         WRITE_POINTER. Again, the firmware is specifically not
>                         required to
>                         *read* any part of the fw_cfg blob identified by
>                         "dest_file".
> 
>                         WRITE_POINTER instructs the firmware to return the
>                         allocation address of
>                         the downloaded "src_file" to QEMU. Any necessary
>                         runtime subscripting
>                         within "src_file" is to be handled by QEMU code
>                         dynamically.
> 
>                         For example, consider that "src_file" has *several*
>                         fields that QEMU
>                         wants to massage; in that case, indexing within QEMU
>                         code with field
>                         offsets is simply unavoidable.    
> 
>                     what I don't like here is that this indexing would be
>                     rather fragile
>                     and has to be done in different parts of QEMU /device, AML
>                     /.
> 
>                     I'd prefer this helper function to have the same
>                     @src_offset
>                     behavior as ADD_POINTER where patched address could point
>                     to
>                     any part of src_file i.e. not just beginning.    
> 
> 
> 
> 
>                        /*
>                         * COMMAND_ADD_POINTER - patch the table (originating
>                 from
>                         * @dest_file) at @pointer.offset, by adding a pointer
>                 to the table
>                         * originating from @src_file. 1,2,4 or 8 byte unsigned
>                         * addition is used depending on @pointer.size.
>                         */
> 
>                 so the way ADD works is
>                 read at offset
>                 add table address
>                 write result at offset
> 
>                 in other words it is always beginning of table that is added.  
> 
>             more exactly it's, read at 
>              src_offset = *(dst_blob_ptr+dst_offset)
>              *(dst_blob+dst_offset) = src_blob_ptr + src_offset
> 
> 
>                 Would the following be acceptable?
> 
> 
>                         * COMMAND_WRITE_POINTER - update the fw_cfg file
>                 (originating from
>                         * @dest_file) at @wr_pointer.offset, by writing a
>                 pointer to the table
>                         * originating from @src_file. 1,2,4 or 8 byte unsigned
>                 value
>                         * is written depending on @wr_pointer.size.  
> 
>             it looses 'adding' part of ADD_POINTER command which handles
>             src_offset,
>             however implementing adding part looks a bit complicated
>             as patched blob (dst) is not in guest memory but in QEMU and
>             on reset *(dst_blob+dst_offset) should be reset to src_offset.
>             Considering dst file could be device specific memory (field/blob/
>             whatever)
>             it could be hard to track/notice proper reset behavior.
> 
>             So now I'm not sure if src_offset is worth adding.  
> 
> 
>         Right. Let's just do this math in QEMU if we have to.
> 
>     Math complicates QEMU code though and not only QMEMU but AML code as well.
>     Considering that we are adding a new command and don't have to keep
>     any sort of compatibility we can pass src_offset as part
>     of command instead of hiding it inside of dst_file.
>     Something like this:
> 
>            /*
>             * COMMAND_WRITE_POINTER - write the fw_cfg file (originating from
>             * @dest_file) at @wr_pointer.offset, by writing 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 offset;
>     +            uint32_t dst_offset;
>     +            uint32_t src_offset;
>                 uint8_t size;
>            } wr_pointer;
> 
> 
> OK, this is easy enough to do and maybe we’ll have a use case in the future.
>  I’ll make this change in v7


So if you do, you want to set it to VMGENID_GUID_OFFSET.

> 
> 
> 
> 
> 
> 
> 
> 
> 
>                         (1) So, the above looks correct, but please replace
>                         "adding" with
>                         "storing", and "unsigned addition" with "store".
> 
>                         Side point: the case for ADD_POINTER is different;
>                         there we patch
>                         several individual ACPI objects. The fact that I
>                         requested explicit
>                         addition within the ADDR method, as opposed to
>                         pre-setting VGIA to a
>                         nonzero offset, is an *incidental* limitation (coming
>                         from the OVMF ACPI
>                         SDT header probe suppressor), and we'll likely fix that
>                         up later, with
>                         ALLOCATE command hints or something like that. However,
>                         in
>                         WRITE_POINTER, asking for the exact allocation address
>                         of "src_file" is
>                         an *inherent* characteristic.
> 
>                         For reference, this is the command's description from
>                         the (not as yet
>                         posted) OVMF series:
> 
>                         // QemuLoaderCmdWritePointer: the bytes at
>                         // [PointerOffset..PointerOffset+PointerSize) in the
>                         writeable fw_cfg
>                         // file PointerFile are to receive the absolute address
>                         of PointeeFile,
>                         // as allocated and downloaded by the firmware. Store
>                         the base address
>                         // of where PointeeFile's contents have been placed
>                         (when
>                         // QemuLoaderCmdAllocate has been executed for
>                         PointeeFile) to this
>                         // portion of PointerFile.
>                         //
>                         // This command is similar to QemuLoaderCmdAddPointer;
>                         the difference is
>                         // that the "pointer to patch" does not exist in
>                         guest-physical address
>                         // space, only in "fw_cfg file space". In addition, the
>                         "pointer to
>                         // patch" is not initialized by QEMU with a possibly
>                         nonzero offset
>                         // value: the base address of the memory allocated for
>                         downloading
>                         // PointeeFile shall not increment the pointer, but
>                         overwrite it.
> 
>                         In the last SeaBIOS patch series, namely
> 
>                         [SeaBIOS] [PATCH v3 2/2] QEMU fw_cfg: Add command to
>                         write back address
>                                                 of file
> 
>                         function romfile_loader_write_pointer() implemented
>                         just that plain
>                         store (not an addition), and that was exactly right.
> 
>                         Continuing:
> 
> 
>                                 +        struct {
>                                 +            char dest_file
>                                 [BIOS_LINKER_LOADER_FILESZ];
>                                 +            char src_file
>                                 [BIOS_LINKER_LOADER_FILESZ];
>                                 +            uint32_t offset;
>                                 +            uint8_t size;
>                                 +        } wr_pointer;
>                                 +
>                                         /* padding */
>                                         char pad[124];
>                                     };
>                                 @@ -85,9 +98,10 @@ struct BiosLinkerLoaderEntry
>                                 {
>                                 typedef struct BiosLinkerLoaderEntry
>                                 BiosLinkerLoaderEntry;
> 
>                                 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_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 +292,41 @@ 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
>                                 + */
>                                 +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)      
> 
>                             API is missing "src_offset" even though it's not
>                             used in this series,
>                             a patch on top to fix it up is ok for me as far as
>                             Seabios/OVMF
>                             counterpart can handle src_offset correctly from
>                             starters.      
> 
> 
>                         According to the above, it is the right thing not to
>                         add "src_offset"
>                         here. The documentation on the command is slightly
>                         incorrect (and causes
>                         confusion), but the helper function's signature and
>                         comments are okay.
> 
> 
> 
> 
>                                 +{
>                                 +    BiosLinkerLoaderEntry entry;
>                                 +    const BiosLinkerFileEntry *source_file =
>                                 +        bios_linker_find_file(linker,
>                                 src_file);
>                                 +
>                                 +    assert(source_file);      
> 
> 
>                         I wish we kept the following asserts from
>                         bios_linker_loader_add_pointer():
> 
>                            assert(dst_patched_offset < dst_file->blob->len);
>                            assert(dst_patched_offset + dst_patched_size <=
>                         dst_file->blob->len);
> 
>                         Namely, just because the dst_file is never supposed to
>                         be downloaded by
>                         the firmware, it still remains a requirement that the
>                         "dst file offset
>                         range" that is to be rewritten *do fall* within the dst
>                         file.
> 
>                         Nonetheless, this is not critical. (OVMF at least
>                         verifies it anyway.)
> 
>                         Summary (from my side anyway): I feel that the
>                         documentation of the new
>                         command is very important. Please fix it up as
>                         suggested under (1), in
>                         v7. Regarding the asserts, I'll let you decide.
> 
>                         With the documentation fixed up:
> 
>                         Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> 
>                         (If you don't wish to post a v7, I'm also completely
>                         fine if Michael or
>                         someone else fixes up the docs as proposed in (1),
>                         before committing the
>                         patch.)
> 
>                         Thanks!
>                         Laszlo
> 
> 
>                                 +    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.offset = cpu_to_le32
>                                 (dst_patched_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);
>                                 +}
>                                 diff --git a/include/hw/acpi/
>                                 bios-linker-loader.h b/include/hw/acpi/
>                                 bios-linker-loader.h
>                                 index fa1e5d1..f9ba5d6 100644
>                                 --- a/include/hw/acpi/bios-linker-loader.h
>                                 +++ b/include/hw/acpi/bios-linker-loader.h
>                                 @@ -26,5 +26,11 @@ 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);
>                                 +
>                                 void bios_linker_loader_cleanup(BIOSLinker
>                                 *linker);
>                                 #endif      
> 
> 

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15 18:06                   ` Michael S. Tsirkin
@ 2017-02-15 18:14                     ` Ben Warren
  2017-02-15 18:35                       ` Igor Mammedov
  0 siblings, 1 reply; 62+ messages in thread
From: Ben Warren @ 2017-02-15 18:14 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: Igor Mammedov, Laszlo Ersek, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 22112 bytes --]


> On Feb 15, 2017, at 10:06 AM, Michael S. Tsirkin <mst@redhat.com> wrote:
> 
> On Wed, Feb 15, 2017 at 09:54:08AM -0800, Ben Warren wrote:
>> 
>>    On Feb 15, 2017, at 9:43 AM, Igor Mammedov <imammedo@redhat.com> wrote:
>> 
>>    On Wed, 15 Feb 2017 18:39:06 +0200
>>    "Michael S. Tsirkin" <mst@redhat.com> wrote:
>> 
>> 
>>        On Wed, Feb 15, 2017 at 04:56:02PM +0100, Igor Mammedov wrote:
>> 
>>            On Wed, 15 Feb 2017 17:30:00 +0200
>>            "Michael S. Tsirkin" <mst@redhat.com> wrote:
>> 
>> 
>>                On Wed, Feb 15, 2017 at 04:22:25PM +0100, Igor Mammedov wrote:
>> 
>> 
>>                    On Wed, 15 Feb 2017 15:13:20 +0100
>>                    Laszlo Ersek <lersek@redhat.com> wrote:
>> 
>> 
>>                        Commenting under Igor's reply for simplicity
>> 
>>                        On 02/15/17 11:57, Igor Mammedov wrote:    
>> 
>>                            On Tue, 14 Feb 2017 22:15:43 -0800
>>                            ben@skyportsystems.com wrote:
>> 
>> 
>>                                From: Ben Warren <ben@skyportsystems.com>
>> 
>>                                This is similar to the existing 'add pointer'
>>                                functionality, but instead
>>                                of instructing the guest (BIOS or UEFI) to
>>                                patch memory, it instructs
>>                                the guest to write the pointer back to QEMU via
>>                                a writeable fw_cfg file.
>> 
>>                                Signed-off-by: Ben Warren <
>>                                ben@skyportsystems.com>
>>                                ---
>>                                hw/acpi/bios-linker-loader.c         | 58
>>                                ++++++++++++++++++++++++++++++++++--
>>                                include/hw/acpi/bios-linker-loader.h |  6 ++++
>>                                2 files changed, 61 insertions(+), 3 deletions
>>                                (-)
>> 
>>                                diff --git a/hw/acpi/bios-linker-loader.c b/hw/
>>                                acpi/bios-linker-loader.c
>>                                index d963ebe..5030cf1 100644
>>                                --- a/hw/acpi/bios-linker-loader.c
>>                                +++ b/hw/acpi/bios-linker-loader.c
>>                                @@ -78,6 +78,19 @@ 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 the table
>>                                +         * originating from @src_file. 1,2,4
>>                                or 8 byte unsigned
>>                                +         * addition is used depending on
>>                                @wr_pointer.size.
>>                                +         */      
>> 
>> 
>>                        The words "adding" and "addition" are causing confusion
>>                        here.
>> 
>>                        In all of the previous discussion, *addition* was out
>>                        of scope from
>>                        WRITE_POINTER. Again, the firmware is specifically not
>>                        required to
>>                        *read* any part of the fw_cfg blob identified by
>>                        "dest_file".
>> 
>>                        WRITE_POINTER instructs the firmware to return the
>>                        allocation address of
>>                        the downloaded "src_file" to QEMU. Any necessary
>>                        runtime subscripting
>>                        within "src_file" is to be handled by QEMU code
>>                        dynamically.
>> 
>>                        For example, consider that "src_file" has *several*
>>                        fields that QEMU
>>                        wants to massage; in that case, indexing within QEMU
>>                        code with field
>>                        offsets is simply unavoidable.    
>> 
>>                    what I don't like here is that this indexing would be
>>                    rather fragile
>>                    and has to be done in different parts of QEMU /device, AML
>>                    /.
>> 
>>                    I'd prefer this helper function to have the same
>>                    @src_offset
>>                    behavior as ADD_POINTER where patched address could point
>>                    to
>>                    any part of src_file i.e. not just beginning.    
>> 
>> 
>> 
>> 
>>                       /*
>>                        * COMMAND_ADD_POINTER - patch the table (originating
>>                from
>>                        * @dest_file) at @pointer.offset, by adding a pointer
>>                to the table
>>                        * originating from @src_file. 1,2,4 or 8 byte unsigned
>>                        * addition is used depending on @pointer.size.
>>                        */
>> 
>>                so the way ADD works is
>>                read at offset
>>                add table address
>>                write result at offset
>> 
>>                in other words it is always beginning of table that is added.  
>> 
>>            more exactly it's, read at 
>>             src_offset = *(dst_blob_ptr+dst_offset)
>>             *(dst_blob+dst_offset) = src_blob_ptr + src_offset
>> 
>> 
>>                Would the following be acceptable?
>> 
>> 
>>                        * COMMAND_WRITE_POINTER - update the fw_cfg file
>>                (originating from
>>                        * @dest_file) at @wr_pointer.offset, by writing a
>>                pointer to the table
>>                        * originating from @src_file. 1,2,4 or 8 byte unsigned
>>                value
>>                        * is written depending on @wr_pointer.size.  
>> 
>>            it looses 'adding' part of ADD_POINTER command which handles
>>            src_offset,
>>            however implementing adding part looks a bit complicated
>>            as patched blob (dst) is not in guest memory but in QEMU and
>>            on reset *(dst_blob+dst_offset) should be reset to src_offset.
>>            Considering dst file could be device specific memory (field/blob/
>>            whatever)
>>            it could be hard to track/notice proper reset behavior.
>> 
>>            So now I'm not sure if src_offset is worth adding.  
>> 
>> 
>>        Right. Let's just do this math in QEMU if we have to.
>> 
>>    Math complicates QEMU code though and not only QMEMU but AML code as well.
>>    Considering that we are adding a new command and don't have to keep
>>    any sort of compatibility we can pass src_offset as part
>>    of command instead of hiding it inside of dst_file.
>>    Something like this:
>> 
>>           /*
>>            * COMMAND_WRITE_POINTER - write the fw_cfg file (originating from
>>            * @dest_file) at @wr_pointer.offset, by writing 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 offset;
>>    +            uint32_t dst_offset;
>>    +            uint32_t src_offset;
>>                uint8_t size;
>>           } wr_pointer;
>> 
>> 
>> OK, this is easy enough to do and maybe we’ll have a use case in the future.
>> I’ll make this change in v7
> 
> 
> So if you do, you want to set it to VMGENID_GUID_OFFSET.
> 
Oh, I was going to set it to 0 since that doesn’t require any other changes (other than to SeaBIOS)
>> 
>> 
>> 
>> 
>> 
>> 
>> 
>> 
>> 
>>                        (1) So, the above looks correct, but please replace
>>                        "adding" with
>>                        "storing", and "unsigned addition" with "store".
>> 
>>                        Side point: the case for ADD_POINTER is different;
>>                        there we patch
>>                        several individual ACPI objects. The fact that I
>>                        requested explicit
>>                        addition within the ADDR method, as opposed to
>>                        pre-setting VGIA to a
>>                        nonzero offset, is an *incidental* limitation (coming
>>                        from the OVMF ACPI
>>                        SDT header probe suppressor), and we'll likely fix that
>>                        up later, with
>>                        ALLOCATE command hints or something like that. However,
>>                        in
>>                        WRITE_POINTER, asking for the exact allocation address
>>                        of "src_file" is
>>                        an *inherent* characteristic.
>> 
>>                        For reference, this is the command's description from
>>                        the (not as yet
>>                        posted) OVMF series:
>> 
>>                        // QemuLoaderCmdWritePointer: the bytes at
>>                        // [PointerOffset..PointerOffset+PointerSize) in the
>>                        writeable fw_cfg
>>                        // file PointerFile are to receive the absolute address
>>                        of PointeeFile,
>>                        // as allocated and downloaded by the firmware. Store
>>                        the base address
>>                        // of where PointeeFile's contents have been placed
>>                        (when
>>                        // QemuLoaderCmdAllocate has been executed for
>>                        PointeeFile) to this
>>                        // portion of PointerFile.
>>                        //
>>                        // This command is similar to QemuLoaderCmdAddPointer;
>>                        the difference is
>>                        // that the "pointer to patch" does not exist in
>>                        guest-physical address
>>                        // space, only in "fw_cfg file space". In addition, the
>>                        "pointer to
>>                        // patch" is not initialized by QEMU with a possibly
>>                        nonzero offset
>>                        // value: the base address of the memory allocated for
>>                        downloading
>>                        // PointeeFile shall not increment the pointer, but
>>                        overwrite it.
>> 
>>                        In the last SeaBIOS patch series, namely
>> 
>>                        [SeaBIOS] [PATCH v3 2/2] QEMU fw_cfg: Add command to
>>                        write back address
>>                                                of file
>> 
>>                        function romfile_loader_write_pointer() implemented
>>                        just that plain
>>                        store (not an addition), and that was exactly right.
>> 
>>                        Continuing:
>> 
>> 
>>                                +        struct {
>>                                +            char dest_file
>>                                [BIOS_LINKER_LOADER_FILESZ];
>>                                +            char src_file
>>                                [BIOS_LINKER_LOADER_FILESZ];
>>                                +            uint32_t offset;
>>                                +            uint8_t size;
>>                                +        } wr_pointer;
>>                                +
>>                                        /* padding */
>>                                        char pad[124];
>>                                    };
>>                                @@ -85,9 +98,10 @@ struct BiosLinkerLoaderEntry
>>                                {
>>                                typedef struct BiosLinkerLoaderEntry
>>                                BiosLinkerLoaderEntry;
>> 
>>                                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_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 +292,41 @@ 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
>>                                + */
>>                                +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)      
>> 
>>                            API is missing "src_offset" even though it's not
>>                            used in this series,
>>                            a patch on top to fix it up is ok for me as far as
>>                            Seabios/OVMF
>>                            counterpart can handle src_offset correctly from
>>                            starters.      
>> 
>> 
>>                        According to the above, it is the right thing not to
>>                        add "src_offset"
>>                        here. The documentation on the command is slightly
>>                        incorrect (and causes
>>                        confusion), but the helper function's signature and
>>                        comments are okay.
>> 
>> 
>> 
>> 
>>                                +{
>>                                +    BiosLinkerLoaderEntry entry;
>>                                +    const BiosLinkerFileEntry *source_file =
>>                                +        bios_linker_find_file(linker,
>>                                src_file);
>>                                +
>>                                +    assert(source_file);      
>> 
>> 
>>                        I wish we kept the following asserts from
>>                        bios_linker_loader_add_pointer():
>> 
>>                           assert(dst_patched_offset < dst_file->blob->len);
>>                           assert(dst_patched_offset + dst_patched_size <=
>>                        dst_file->blob->len);
>> 
>>                        Namely, just because the dst_file is never supposed to
>>                        be downloaded by
>>                        the firmware, it still remains a requirement that the
>>                        "dst file offset
>>                        range" that is to be rewritten *do fall* within the dst
>>                        file.
>> 
>>                        Nonetheless, this is not critical. (OVMF at least
>>                        verifies it anyway.)
>> 
>>                        Summary (from my side anyway): I feel that the
>>                        documentation of the new
>>                        command is very important. Please fix it up as
>>                        suggested under (1), in
>>                        v7. Regarding the asserts, I'll let you decide.
>> 
>>                        With the documentation fixed up:
>> 
>>                        Reviewed-by: Laszlo Ersek <lersek@redhat.com>
>> 
>>                        (If you don't wish to post a v7, I'm also completely
>>                        fine if Michael or
>>                        someone else fixes up the docs as proposed in (1),
>>                        before committing the
>>                        patch.)
>> 
>>                        Thanks!
>>                        Laszlo
>> 
>> 
>>                                +    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.offset = cpu_to_le32
>>                                (dst_patched_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);
>>                                +}
>>                                diff --git a/include/hw/acpi/
>>                                bios-linker-loader.h b/include/hw/acpi/
>>                                bios-linker-loader.h
>>                                index fa1e5d1..f9ba5d6 100644
>>                                --- a/include/hw/acpi/bios-linker-loader.h
>>                                +++ b/include/hw/acpi/bios-linker-loader.h
>>                                @@ -26,5 +26,11 @@ 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);
>>                                +
>>                                void bios_linker_loader_cleanup(BIOSLinker
>>                                *linker);
>>                                #endif      


[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 3583 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15 18:04                 ` Michael S. Tsirkin
@ 2017-02-15 18:24                   ` Igor Mammedov
  2017-02-15 19:14                     ` Ben Warren
  2017-02-15 19:34                     ` Michael S. Tsirkin
  0 siblings, 2 replies; 62+ messages in thread
From: Igor Mammedov @ 2017-02-15 18:24 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: Laszlo Ersek, qemu-devel, ben

On Wed, 15 Feb 2017 20:04:40 +0200
"Michael S. Tsirkin" <mst@redhat.com> wrote:

> On Wed, Feb 15, 2017 at 06:43:09PM +0100, Igor Mammedov wrote:
> > On Wed, 15 Feb 2017 18:39:06 +0200
> > "Michael S. Tsirkin" <mst@redhat.com> wrote:
> >   
> > > On Wed, Feb 15, 2017 at 04:56:02PM +0100, Igor Mammedov wrote:  
> > > > On Wed, 15 Feb 2017 17:30:00 +0200
> > > > "Michael S. Tsirkin" <mst@redhat.com> wrote:
> > > >     
> > > > > On Wed, Feb 15, 2017 at 04:22:25PM +0100, Igor Mammedov wrote:    
> > > > > > On Wed, 15 Feb 2017 15:13:20 +0100
> > > > > > Laszlo Ersek <lersek@redhat.com> wrote:
> > > > > >       
> > > > > > > Commenting under Igor's reply for simplicity
> > > > > > > 
> > > > > > > On 02/15/17 11:57, Igor Mammedov wrote:      
> > > > > > > > On Tue, 14 Feb 2017 22:15:43 -0800
> > > > > > > > ben@skyportsystems.com wrote:
> > > > > > > >         
> > > > > > > >> From: Ben Warren <ben@skyportsystems.com>
> > > > > > > >>
> > > > > > > >> This is similar to the existing 'add pointer' functionality, but instead
> > > > > > > >> of instructing the guest (BIOS or UEFI) to patch memory, it instructs
> > > > > > > >> the guest to write the pointer back to QEMU via a writeable fw_cfg file.
> > > > > > > >>
> > > > > > > >> Signed-off-by: Ben Warren <ben@skyportsystems.com>
> > > > > > > >> ---
> > > > > > > >>  hw/acpi/bios-linker-loader.c         | 58 ++++++++++++++++++++++++++++++++++--
> > > > > > > >>  include/hw/acpi/bios-linker-loader.h |  6 ++++
> > > > > > > >>  2 files changed, 61 insertions(+), 3 deletions(-)
> > > > > > > >>
> > > > > > > >> diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
> > > > > > > >> index d963ebe..5030cf1 100644
> > > > > > > >> --- a/hw/acpi/bios-linker-loader.c
> > > > > > > >> +++ b/hw/acpi/bios-linker-loader.c
> > > > > > > >> @@ -78,6 +78,19 @@ 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 the table
> > > > > > > >> +         * originating from @src_file. 1,2,4 or 8 byte unsigned
> > > > > > > >> +         * addition is used depending on @wr_pointer.size.
> > > > > > > >> +         */        
> > > > > > > 
> > > > > > > The words "adding" and "addition" are causing confusion here.
> > > > > > > 
> > > > > > > In all of the previous discussion, *addition* was out of scope from
> > > > > > > WRITE_POINTER. Again, the firmware is specifically not required to
> > > > > > > *read* any part of the fw_cfg blob identified by "dest_file".
> > > > > > > 
> > > > > > > WRITE_POINTER instructs the firmware to return the allocation address of
> > > > > > > the downloaded "src_file" to QEMU. Any necessary runtime subscripting
> > > > > > > within "src_file" is to be handled by QEMU code dynamically.
> > > > > > > 
> > > > > > > For example, consider that "src_file" has *several* fields that QEMU
> > > > > > > wants to massage; in that case, indexing within QEMU code with field
> > > > > > > offsets is simply unavoidable.      
> > > > > > what I don't like here is that this indexing would be rather fragile
> > > > > > and has to be done in different parts of QEMU /device, AML/.
> > > > > > 
> > > > > > I'd prefer this helper function to have the same @src_offset
> > > > > > behavior as ADD_POINTER where patched address could point to
> > > > > > any part of src_file i.e. not just beginning.      
> > > > > 
> > > > > 
> > > > > 
> > > > >         /*
> > > > >          * COMMAND_ADD_POINTER - patch the table (originating from
> > > > >          * @dest_file) at @pointer.offset, by adding a pointer to the table
> > > > >          * originating from @src_file. 1,2,4 or 8 byte unsigned
> > > > >          * addition is used depending on @pointer.size.
> > > > >          */
> > > > >  
> > > > > so the way ADD works is
> > > > > 	read at offset
> > > > > 	add table address
> > > > > 	write result at offset
> > > > > 
> > > > > in other words it is always beginning of table that is added.    
> > > > more exactly it's, read at 
> > > >   src_offset = *(dst_blob_ptr+dst_offset)
> > > >   *(dst_blob+dst_offset) = src_blob_ptr + src_offset
> > > >     
> > > > > Would the following be acceptable?
> > > > > 
> > > > > 
> > > > >          * COMMAND_WRITE_POINTER - update the fw_cfg file (originating from
> > > > >          * @dest_file) at @wr_pointer.offset, by writing a pointer to the table
> > > > >          * originating from @src_file. 1,2,4 or 8 byte unsigned value
> > > > >          * is written depending on @wr_pointer.size.    
> > > > it looses 'adding' part of ADD_POINTER command which handles src_offset,
> > > > however implementing adding part looks a bit complicated
> > > > as patched blob (dst) is not in guest memory but in QEMU and
> > > > on reset *(dst_blob+dst_offset) should be reset to src_offset.
> > > > Considering dst file could be device specific memory (field/blob/whatever)
> > > > it could be hard to track/notice proper reset behavior.
> > > > 
> > > > So now I'm not sure if src_offset is worth adding.    
> > > 
> > > Right. Let's just do this math in QEMU if we have to.  
> > Math complicates QEMU code though and not only QMEMU but AML code as well.
> > Considering that we are adding a new command and don't have to keep
> > any sort of compatibility we can pass src_offset as part
> > of command instead of hiding it inside of dst_file.
> > Something like this:
> > 
> >         /*
> >          * COMMAND_WRITE_POINTER - write the fw_cfg file (originating from
> >          * @dest_file) at @wr_pointer.offset, by writing 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 offset;
> > +            uint32_t dst_offset;
> > +            uint32_t src_offset;
> >              uint8_t size;
> >         } wr_pointer;  
> 
> 
> As long as all users pass in 0 though there's a real possibility guests
> will implement this incorrectly.
We are here to ensure that at least Seabios (I'll review it)
and OVMF (Laszlo would take care of it I suppose) do it right,
and if there are other firmwares, they should do it correctly
as described fix their own bugs later wrt randomly written
implementation.

> I guess we can put in the offset just
> behind the zero-filled padding we have there.
I've assumed padding was there to make commands fixed size and give
a room for future extensions so hunk changing BiosLinkerLoaderEntry
would look like:

diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
index d963ebe..6983713 100644
--- a/hw/acpi/bios-linker-loader.c
+++ b/hw/acpi/bios-linker-loader.c
@@ -49,6 +49,7 @@ struct BiosLinkerLoaderEntry {
             char file[BIOS_LINKER_LOADER_FILESZ];
             uint32_t align;
             uint8_t zone;
+            uint32_t padding;
         } alloc;
 
         /*
@@ -62,6 +63,7 @@ struct BiosLinkerLoaderEntry {
             char src_file[BIOS_LINKER_LOADER_FILESZ];
             uint32_t offset;
             uint8_t size;
+            uint32_t padding;
         } pointer;
 
         /*
@@ -76,10 +78,25 @@ struct BiosLinkerLoaderEntry {
             uint32_t offset;
             uint32_t start;
             uint32_t length;
+            uint32_t padding;
         } cksum;
 
+        /*
+         * COMMAND_WRITE_POINTER - write the fw_cfg file (originating from
+         * @dest_file) at @wr_pointer.offset, by writing 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];
+        char pad[120];
     };
 } QEMU_PACKED;
 typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;

 
> I'm mostly concerned we are adding new features to something
> that has been through 25 revisions already.
It's ABI so it's worth extra effort,
it looks like only one more revision is left and there is
about a week left to post and merge it.

> 
> 
> > >   
> > > > > 
> > > > >     
> > > > > > 
> > > > > >       
> > > > > > > (1) So, the above looks correct, but please replace "adding" with
> > > > > > > "storing", and "unsigned addition" with "store".
> > > > > > > 
> > > > > > > Side point: the case for ADD_POINTER is different; there we patch
> > > > > > > several individual ACPI objects. The fact that I requested explicit
> > > > > > > addition within the ADDR method, as opposed to pre-setting VGIA to a
> > > > > > > nonzero offset, is an *incidental* limitation (coming from the OVMF ACPI
> > > > > > > SDT header probe suppressor), and we'll likely fix that up later, with
> > > > > > > ALLOCATE command hints or something like that. However, in
> > > > > > > WRITE_POINTER, asking for the exact allocation address of "src_file" is
> > > > > > > an *inherent* characteristic.
> > > > > > > 
> > > > > > > For reference, this is the command's description from the (not as yet
> > > > > > > posted) OVMF series:
> > > > > > > 
> > > > > > > // QemuLoaderCmdWritePointer: the bytes at
> > > > > > > // [PointerOffset..PointerOffset+PointerSize) in the writeable fw_cfg
> > > > > > > // file PointerFile are to receive the absolute address of PointeeFile,
> > > > > > > // as allocated and downloaded by the firmware. Store the base address
> > > > > > > // of where PointeeFile's contents have been placed (when
> > > > > > > // QemuLoaderCmdAllocate has been executed for PointeeFile) to this
> > > > > > > // portion of PointerFile.
> > > > > > > //
> > > > > > > // This command is similar to QemuLoaderCmdAddPointer; the difference is
> > > > > > > // that the "pointer to patch" does not exist in guest-physical address
> > > > > > > // space, only in "fw_cfg file space". In addition, the "pointer to
> > > > > > > // patch" is not initialized by QEMU with a possibly nonzero offset
> > > > > > > // value: the base address of the memory allocated for downloading
> > > > > > > // PointeeFile shall not increment the pointer, but overwrite it.
> > > > > > > 
> > > > > > > In the last SeaBIOS patch series, namely
> > > > > > > 
> > > > > > > [SeaBIOS] [PATCH v3 2/2] QEMU fw_cfg: Add command to write back address
> > > > > > >                          of file
> > > > > > > 
> > > > > > > function romfile_loader_write_pointer() implemented just that plain
> > > > > > > store (not an addition), and that was exactly right.
> > > > > > > 
> > > > > > > Continuing:
> > > > > > >       
> > > > > > > >> +        struct {
> > > > > > > >> +            char dest_file[BIOS_LINKER_LOADER_FILESZ];
> > > > > > > >> +            char src_file[BIOS_LINKER_LOADER_FILESZ];
> > > > > > > >> +            uint32_t offset;
> > > > > > > >> +            uint8_t size;
> > > > > > > >> +        } wr_pointer;
> > > > > > > >> +
> > > > > > > >>          /* padding */
> > > > > > > >>          char pad[124];
> > > > > > > >>      };
> > > > > > > >> @@ -85,9 +98,10 @@ struct BiosLinkerLoaderEntry {
> > > > > > > >>  typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
> > > > > > > >>  
> > > > > > > >>  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_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 +292,41 @@ 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
> > > > > > > >> + */
> > > > > > > >> +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)        
> > > > > > > > API is missing "src_offset" even though it's not used in this series,
> > > > > > > > a patch on top to fix it up is ok for me as far as Seabios/OVMF
> > > > > > > > counterpart can handle src_offset correctly from starters.        
> > > > > > > 
> > > > > > > According to the above, it is the right thing not to add "src_offset"
> > > > > > > here. The documentation on the command is slightly incorrect (and causes
> > > > > > > confusion), but the helper function's signature and comments are okay.
> > > > > > >       
> > > > > > > >         
> > > > > > > >> +{
> > > > > > > >> +    BiosLinkerLoaderEntry entry;
> > > > > > > >> +    const BiosLinkerFileEntry *source_file =
> > > > > > > >> +        bios_linker_find_file(linker, src_file);
> > > > > > > >> +
> > > > > > > >> +    assert(source_file);        
> > > > > > > 
> > > > > > > I wish we kept the following asserts from bios_linker_loader_add_pointer():
> > > > > > > 
> > > > > > >     assert(dst_patched_offset < dst_file->blob->len);
> > > > > > >     assert(dst_patched_offset + dst_patched_size <= dst_file->blob->len);
> > > > > > > 
> > > > > > > Namely, just because the dst_file is never supposed to be downloaded by
> > > > > > > the firmware, it still remains a requirement that the "dst file offset
> > > > > > > range" that is to be rewritten *do fall* within the dst file.
> > > > > > > 
> > > > > > > Nonetheless, this is not critical. (OVMF at least verifies it anyway.)
> > > > > > > 
> > > > > > > Summary (from my side anyway): I feel that the documentation of the new
> > > > > > > command is very important. Please fix it up as suggested under (1), in
> > > > > > > v7. Regarding the asserts, I'll let you decide.
> > > > > > > 
> > > > > > > With the documentation fixed up:
> > > > > > > 
> > > > > > > Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> > > > > > > 
> > > > > > > (If you don't wish to post a v7, I'm also completely fine if Michael or
> > > > > > > someone else fixes up the docs as proposed in (1), before committing the
> > > > > > > patch.)
> > > > > > > 
> > > > > > > Thanks!
> > > > > > > Laszlo
> > > > > > >       
> > > > > > > >> +    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.offset = cpu_to_le32(dst_patched_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);
> > > > > > > >> +}
> > > > > > > >> diff --git a/include/hw/acpi/bios-linker-loader.h b/include/hw/acpi/bios-linker-loader.h
> > > > > > > >> index fa1e5d1..f9ba5d6 100644
> > > > > > > >> --- a/include/hw/acpi/bios-linker-loader.h
> > > > > > > >> +++ b/include/hw/acpi/bios-linker-loader.h
> > > > > > > >> @@ -26,5 +26,11 @@ 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);
> > > > > > > >> +
> > > > > > > >>  void bios_linker_loader_cleanup(BIOSLinker *linker);
> > > > > > > >>  #endif        
> > > > > > > >         
> > > > > > >       
> > >   

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15 18:14                     ` Ben Warren
@ 2017-02-15 18:35                       ` Igor Mammedov
  2017-02-15 18:44                         ` Ben Warren
  0 siblings, 1 reply; 62+ messages in thread
From: Igor Mammedov @ 2017-02-15 18:35 UTC (permalink / raw)
  To: Ben Warren; +Cc: Michael S. Tsirkin, Laszlo Ersek, qemu-devel

On Wed, 15 Feb 2017 10:14:55 -0800
Ben Warren <ben@skyportsystems.com> wrote:

> > On Feb 15, 2017, at 10:06 AM, Michael S. Tsirkin <mst@redhat.com> wrote:
> > 
> > On Wed, Feb 15, 2017 at 09:54:08AM -0800, Ben Warren wrote:  
> >> 
> >>    On Feb 15, 2017, at 9:43 AM, Igor Mammedov <imammedo@redhat.com> wrote:
> >> 
> >>    On Wed, 15 Feb 2017 18:39:06 +0200
> >>    "Michael S. Tsirkin" <mst@redhat.com> wrote:
> >> 
> >> 
> >>        On Wed, Feb 15, 2017 at 04:56:02PM +0100, Igor Mammedov wrote:
> >> 
> >>            On Wed, 15 Feb 2017 17:30:00 +0200
> >>            "Michael S. Tsirkin" <mst@redhat.com> wrote:
> >> 
> >> 
> >>                On Wed, Feb 15, 2017 at 04:22:25PM +0100, Igor Mammedov wrote:
> >> 
> >> 
> >>                    On Wed, 15 Feb 2017 15:13:20 +0100
> >>                    Laszlo Ersek <lersek@redhat.com> wrote:
> >> 
> >> 
> >>                        Commenting under Igor's reply for simplicity
> >> 
> >>                        On 02/15/17 11:57, Igor Mammedov wrote:    
> >> 
> >>                            On Tue, 14 Feb 2017 22:15:43 -0800
> >>                            ben@skyportsystems.com wrote:
> >> 
> >> 
> >>                                From: Ben Warren <ben@skyportsystems.com>
> >> 
> >>                                This is similar to the existing 'add pointer'
> >>                                functionality, but instead
> >>                                of instructing the guest (BIOS or UEFI) to
> >>                                patch memory, it instructs
> >>                                the guest to write the pointer back to QEMU via
> >>                                a writeable fw_cfg file.
> >> 
> >>                                Signed-off-by: Ben Warren <  
> >>                                ben@skyportsystems.com>  
> >>                                ---
> >>                                hw/acpi/bios-linker-loader.c         | 58
> >>                                ++++++++++++++++++++++++++++++++++--
> >>                                include/hw/acpi/bios-linker-loader.h |  6 ++++
> >>                                2 files changed, 61 insertions(+), 3 deletions
> >>                                (-)
> >> 
> >>                                diff --git a/hw/acpi/bios-linker-loader.c b/hw/
> >>                                acpi/bios-linker-loader.c
> >>                                index d963ebe..5030cf1 100644
> >>                                --- a/hw/acpi/bios-linker-loader.c
> >>                                +++ b/hw/acpi/bios-linker-loader.c
> >>                                @@ -78,6 +78,19 @@ 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 the table
> >>                                +         * originating from @src_file. 1,2,4
> >>                                or 8 byte unsigned
> >>                                +         * addition is used depending on
> >>                                @wr_pointer.size.
> >>                                +         */      
> >> 
> >> 
> >>                        The words "adding" and "addition" are causing confusion
> >>                        here.
> >> 
> >>                        In all of the previous discussion, *addition* was out
> >>                        of scope from
> >>                        WRITE_POINTER. Again, the firmware is specifically not
> >>                        required to
> >>                        *read* any part of the fw_cfg blob identified by
> >>                        "dest_file".
> >> 
> >>                        WRITE_POINTER instructs the firmware to return the
> >>                        allocation address of
> >>                        the downloaded "src_file" to QEMU. Any necessary
> >>                        runtime subscripting
> >>                        within "src_file" is to be handled by QEMU code
> >>                        dynamically.
> >> 
> >>                        For example, consider that "src_file" has *several*
> >>                        fields that QEMU
> >>                        wants to massage; in that case, indexing within QEMU
> >>                        code with field
> >>                        offsets is simply unavoidable.    
> >> 
> >>                    what I don't like here is that this indexing would be
> >>                    rather fragile
> >>                    and has to be done in different parts of QEMU /device, AML
> >>                    /.
> >> 
> >>                    I'd prefer this helper function to have the same
> >>                    @src_offset
> >>                    behavior as ADD_POINTER where patched address could point
> >>                    to
> >>                    any part of src_file i.e. not just beginning.    
> >> 
> >> 
> >> 
> >> 
> >>                       /*
> >>                        * COMMAND_ADD_POINTER - patch the table (originating
> >>                from
> >>                        * @dest_file) at @pointer.offset, by adding a pointer
> >>                to the table
> >>                        * originating from @src_file. 1,2,4 or 8 byte unsigned
> >>                        * addition is used depending on @pointer.size.
> >>                        */
> >> 
> >>                so the way ADD works is
> >>                read at offset
> >>                add table address
> >>                write result at offset
> >> 
> >>                in other words it is always beginning of table that is added.  
> >> 
> >>            more exactly it's, read at 
> >>             src_offset = *(dst_blob_ptr+dst_offset)
> >>             *(dst_blob+dst_offset) = src_blob_ptr + src_offset
> >> 
> >> 
> >>                Would the following be acceptable?
> >> 
> >> 
> >>                        * COMMAND_WRITE_POINTER - update the fw_cfg file
> >>                (originating from
> >>                        * @dest_file) at @wr_pointer.offset, by writing a
> >>                pointer to the table
> >>                        * originating from @src_file. 1,2,4 or 8 byte unsigned
> >>                value
> >>                        * is written depending on @wr_pointer.size.  
> >> 
> >>            it looses 'adding' part of ADD_POINTER command which handles
> >>            src_offset,
> >>            however implementing adding part looks a bit complicated
> >>            as patched blob (dst) is not in guest memory but in QEMU and
> >>            on reset *(dst_blob+dst_offset) should be reset to src_offset.
> >>            Considering dst file could be device specific memory (field/blob/
> >>            whatever)
> >>            it could be hard to track/notice proper reset behavior.
> >> 
> >>            So now I'm not sure if src_offset is worth adding.  
> >> 
> >> 
> >>        Right. Let's just do this math in QEMU if we have to.
> >> 
> >>    Math complicates QEMU code though and not only QMEMU but AML code as well.
> >>    Considering that we are adding a new command and don't have to keep
> >>    any sort of compatibility we can pass src_offset as part
> >>    of command instead of hiding it inside of dst_file.
> >>    Something like this:
> >> 
> >>           /*
> >>            * COMMAND_WRITE_POINTER - write the fw_cfg file (originating from
> >>            * @dest_file) at @wr_pointer.offset, by writing 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 offset;
> >>    +            uint32_t dst_offset;
> >>    +            uint32_t src_offset;
> >>                uint8_t size;
> >>           } wr_pointer;
> >> 
> >> 
> >> OK, this is easy enough to do and maybe we’ll have a use case in the future.
> >> I’ll make this change in v7  
> > 
> > 
> > So if you do, you want to set it to VMGENID_GUID_OFFSET.
> >   
> Oh, I was going to set it to 0 since that doesn’t require any other changes (other than to SeaBIOS)
it should be changed in following places:

    bios_linker_loader_write_pointer(linker,
        VMGENID_ADDR_FW_CFG_FILE, 0, sizeof(uint64_t),
-        VMGENID_GUID_FW_CFG_FILE);
+        VMGENID_GUID_FW_CFG_FILE, VMGENID_GUID_OFFSET);
 
...
-            cpu_physical_memory_write(vmgenid_addr + VMGENID_GUID_OFFSET,
+            cpu_physical_memory_write(vmgenid_addr,
                                      guid_le.data, sizeof(guid_le.data));


> >> 
> >> 
> >> 
> >> 
> >> 
> >> 
> >> 
> >> 
> >> 
> >>                        (1) So, the above looks correct, but please replace
> >>                        "adding" with
> >>                        "storing", and "unsigned addition" with "store".
> >> 
> >>                        Side point: the case for ADD_POINTER is different;
> >>                        there we patch
> >>                        several individual ACPI objects. The fact that I
> >>                        requested explicit
> >>                        addition within the ADDR method, as opposed to
> >>                        pre-setting VGIA to a
> >>                        nonzero offset, is an *incidental* limitation (coming
> >>                        from the OVMF ACPI
> >>                        SDT header probe suppressor), and we'll likely fix that
> >>                        up later, with
> >>                        ALLOCATE command hints or something like that. However,
> >>                        in
> >>                        WRITE_POINTER, asking for the exact allocation address
> >>                        of "src_file" is
> >>                        an *inherent* characteristic.
> >> 
> >>                        For reference, this is the command's description from
> >>                        the (not as yet
> >>                        posted) OVMF series:
> >> 
> >>                        // QemuLoaderCmdWritePointer: the bytes at
> >>                        // [PointerOffset..PointerOffset+PointerSize) in the
> >>                        writeable fw_cfg
> >>                        // file PointerFile are to receive the absolute address
> >>                        of PointeeFile,
> >>                        // as allocated and downloaded by the firmware. Store
> >>                        the base address
> >>                        // of where PointeeFile's contents have been placed
> >>                        (when
> >>                        // QemuLoaderCmdAllocate has been executed for
> >>                        PointeeFile) to this
> >>                        // portion of PointerFile.
> >>                        //
> >>                        // This command is similar to QemuLoaderCmdAddPointer;
> >>                        the difference is
> >>                        // that the "pointer to patch" does not exist in
> >>                        guest-physical address
> >>                        // space, only in "fw_cfg file space". In addition, the
> >>                        "pointer to
> >>                        // patch" is not initialized by QEMU with a possibly
> >>                        nonzero offset
> >>                        // value: the base address of the memory allocated for
> >>                        downloading
> >>                        // PointeeFile shall not increment the pointer, but
> >>                        overwrite it.
> >> 
> >>                        In the last SeaBIOS patch series, namely
> >> 
> >>                        [SeaBIOS] [PATCH v3 2/2] QEMU fw_cfg: Add command to
> >>                        write back address
> >>                                                of file
> >> 
> >>                        function romfile_loader_write_pointer() implemented
> >>                        just that plain
> >>                        store (not an addition), and that was exactly right.
> >> 
> >>                        Continuing:
> >> 
> >> 
> >>                                +        struct {
> >>                                +            char dest_file
> >>                                [BIOS_LINKER_LOADER_FILESZ];
> >>                                +            char src_file
> >>                                [BIOS_LINKER_LOADER_FILESZ];
> >>                                +            uint32_t offset;
> >>                                +            uint8_t size;
> >>                                +        } wr_pointer;
> >>                                +
> >>                                        /* padding */
> >>                                        char pad[124];
> >>                                    };
> >>                                @@ -85,9 +98,10 @@ struct BiosLinkerLoaderEntry
> >>                                {
> >>                                typedef struct BiosLinkerLoaderEntry
> >>                                BiosLinkerLoaderEntry;
> >> 
> >>                                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_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 +292,41 @@ 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
> >>                                + */
> >>                                +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)      
> >> 
> >>                            API is missing "src_offset" even though it's not
> >>                            used in this series,
> >>                            a patch on top to fix it up is ok for me as far as
> >>                            Seabios/OVMF
> >>                            counterpart can handle src_offset correctly from
> >>                            starters.      
> >> 
> >> 
> >>                        According to the above, it is the right thing not to
> >>                        add "src_offset"
> >>                        here. The documentation on the command is slightly
> >>                        incorrect (and causes
> >>                        confusion), but the helper function's signature and
> >>                        comments are okay.
> >> 
> >> 
> >> 
> >> 
> >>                                +{
> >>                                +    BiosLinkerLoaderEntry entry;
> >>                                +    const BiosLinkerFileEntry *source_file =
> >>                                +        bios_linker_find_file(linker,
> >>                                src_file);
> >>                                +
> >>                                +    assert(source_file);      
> >> 
> >> 
> >>                        I wish we kept the following asserts from
> >>                        bios_linker_loader_add_pointer():
> >> 
> >>                           assert(dst_patched_offset < dst_file->blob->len);
> >>                           assert(dst_patched_offset + dst_patched_size <=
> >>                        dst_file->blob->len);
> >> 
> >>                        Namely, just because the dst_file is never supposed to
> >>                        be downloaded by
> >>                        the firmware, it still remains a requirement that the
> >>                        "dst file offset
> >>                        range" that is to be rewritten *do fall* within the dst
> >>                        file.
> >> 
> >>                        Nonetheless, this is not critical. (OVMF at least
> >>                        verifies it anyway.)
> >> 
> >>                        Summary (from my side anyway): I feel that the
> >>                        documentation of the new
> >>                        command is very important. Please fix it up as
> >>                        suggested under (1), in
> >>                        v7. Regarding the asserts, I'll let you decide.
> >> 
> >>                        With the documentation fixed up:
> >> 
> >>                        Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> >> 
> >>                        (If you don't wish to post a v7, I'm also completely
> >>                        fine if Michael or
> >>                        someone else fixes up the docs as proposed in (1),
> >>                        before committing the
> >>                        patch.)
> >> 
> >>                        Thanks!
> >>                        Laszlo
> >> 
> >> 
> >>                                +    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.offset = cpu_to_le32
> >>                                (dst_patched_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);
> >>                                +}
> >>                                diff --git a/include/hw/acpi/
> >>                                bios-linker-loader.h b/include/hw/acpi/
> >>                                bios-linker-loader.h
> >>                                index fa1e5d1..f9ba5d6 100644
> >>                                --- a/include/hw/acpi/bios-linker-loader.h
> >>                                +++ b/include/hw/acpi/bios-linker-loader.h
> >>                                @@ -26,5 +26,11 @@ 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);
> >>                                +
> >>                                void bios_linker_loader_cleanup(BIOSLinker
> >>                                *linker);
> >>                                #endif        
> 

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15 18:35                       ` Igor Mammedov
@ 2017-02-15 18:44                         ` Ben Warren
  2017-02-15 21:09                           ` Michael S. Tsirkin
  0 siblings, 1 reply; 62+ messages in thread
From: Ben Warren @ 2017-02-15 18:44 UTC (permalink / raw)
  To: Igor Mammedov; +Cc: Michael S. Tsirkin, Laszlo Ersek, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 23556 bytes --]


> On Feb 15, 2017, at 10:35 AM, Igor Mammedov <imammedo@redhat.com> wrote:
> 
> On Wed, 15 Feb 2017 10:14:55 -0800
> Ben Warren <ben@skyportsystems.com <mailto:ben@skyportsystems.com>> wrote:
> 
>>> On Feb 15, 2017, at 10:06 AM, Michael S. Tsirkin <mst@redhat.com> wrote:
>>> 
>>> On Wed, Feb 15, 2017 at 09:54:08AM -0800, Ben Warren wrote:  
>>>> 
>>>>   On Feb 15, 2017, at 9:43 AM, Igor Mammedov <imammedo@redhat.com> wrote:
>>>> 
>>>>   On Wed, 15 Feb 2017 18:39:06 +0200
>>>>   "Michael S. Tsirkin" <mst@redhat.com> wrote:
>>>> 
>>>> 
>>>>       On Wed, Feb 15, 2017 at 04:56:02PM +0100, Igor Mammedov wrote:
>>>> 
>>>>           On Wed, 15 Feb 2017 17:30:00 +0200
>>>>           "Michael S. Tsirkin" <mst@redhat.com> wrote:
>>>> 
>>>> 
>>>>               On Wed, Feb 15, 2017 at 04:22:25PM +0100, Igor Mammedov wrote:
>>>> 
>>>> 
>>>>                   On Wed, 15 Feb 2017 15:13:20 +0100
>>>>                   Laszlo Ersek <lersek@redhat.com> wrote:
>>>> 
>>>> 
>>>>                       Commenting under Igor's reply for simplicity
>>>> 
>>>>                       On 02/15/17 11:57, Igor Mammedov wrote:    
>>>> 
>>>>                           On Tue, 14 Feb 2017 22:15:43 -0800
>>>>                           ben@skyportsystems.com wrote:
>>>> 
>>>> 
>>>>                               From: Ben Warren <ben@skyportsystems.com>
>>>> 
>>>>                               This is similar to the existing 'add pointer'
>>>>                               functionality, but instead
>>>>                               of instructing the guest (BIOS or UEFI) to
>>>>                               patch memory, it instructs
>>>>                               the guest to write the pointer back to QEMU via
>>>>                               a writeable fw_cfg file.
>>>> 
>>>>                               Signed-off-by: Ben Warren <  
>>>>                               ben@skyportsystems.com>  
>>>>                               ---
>>>>                               hw/acpi/bios-linker-loader.c         | 58
>>>>                               ++++++++++++++++++++++++++++++++++--
>>>>                               include/hw/acpi/bios-linker-loader.h |  6 ++++
>>>>                               2 files changed, 61 insertions(+), 3 deletions
>>>>                               (-)
>>>> 
>>>>                               diff --git a/hw/acpi/bios-linker-loader.c b/hw/
>>>>                               acpi/bios-linker-loader.c
>>>>                               index d963ebe..5030cf1 100644
>>>>                               --- a/hw/acpi/bios-linker-loader.c
>>>>                               +++ b/hw/acpi/bios-linker-loader.c
>>>>                               @@ -78,6 +78,19 @@ 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 the table
>>>>                               +         * originating from @src_file. 1,2,4
>>>>                               or 8 byte unsigned
>>>>                               +         * addition is used depending on
>>>>                               @wr_pointer.size.
>>>>                               +         */      
>>>> 
>>>> 
>>>>                       The words "adding" and "addition" are causing confusion
>>>>                       here.
>>>> 
>>>>                       In all of the previous discussion, *addition* was out
>>>>                       of scope from
>>>>                       WRITE_POINTER. Again, the firmware is specifically not
>>>>                       required to
>>>>                       *read* any part of the fw_cfg blob identified by
>>>>                       "dest_file".
>>>> 
>>>>                       WRITE_POINTER instructs the firmware to return the
>>>>                       allocation address of
>>>>                       the downloaded "src_file" to QEMU. Any necessary
>>>>                       runtime subscripting
>>>>                       within "src_file" is to be handled by QEMU code
>>>>                       dynamically.
>>>> 
>>>>                       For example, consider that "src_file" has *several*
>>>>                       fields that QEMU
>>>>                       wants to massage; in that case, indexing within QEMU
>>>>                       code with field
>>>>                       offsets is simply unavoidable.    
>>>> 
>>>>                   what I don't like here is that this indexing would be
>>>>                   rather fragile
>>>>                   and has to be done in different parts of QEMU /device, AML
>>>>                   /.
>>>> 
>>>>                   I'd prefer this helper function to have the same
>>>>                   @src_offset
>>>>                   behavior as ADD_POINTER where patched address could point
>>>>                   to
>>>>                   any part of src_file i.e. not just beginning.    
>>>> 
>>>> 
>>>> 
>>>> 
>>>>                      /*
>>>>                       * COMMAND_ADD_POINTER - patch the table (originating
>>>>               from
>>>>                       * @dest_file) at @pointer.offset, by adding a pointer
>>>>               to the table
>>>>                       * originating from @src_file. 1,2,4 or 8 byte unsigned
>>>>                       * addition is used depending on @pointer.size.
>>>>                       */
>>>> 
>>>>               so the way ADD works is
>>>>               read at offset
>>>>               add table address
>>>>               write result at offset
>>>> 
>>>>               in other words it is always beginning of table that is added.  
>>>> 
>>>>           more exactly it's, read at 
>>>>            src_offset = *(dst_blob_ptr+dst_offset)
>>>>            *(dst_blob+dst_offset) = src_blob_ptr + src_offset
>>>> 
>>>> 
>>>>               Would the following be acceptable?
>>>> 
>>>> 
>>>>                       * COMMAND_WRITE_POINTER - update the fw_cfg file
>>>>               (originating from
>>>>                       * @dest_file) at @wr_pointer.offset, by writing a
>>>>               pointer to the table
>>>>                       * originating from @src_file. 1,2,4 or 8 byte unsigned
>>>>               value
>>>>                       * is written depending on @wr_pointer.size.  
>>>> 
>>>>           it looses 'adding' part of ADD_POINTER command which handles
>>>>           src_offset,
>>>>           however implementing adding part looks a bit complicated
>>>>           as patched blob (dst) is not in guest memory but in QEMU and
>>>>           on reset *(dst_blob+dst_offset) should be reset to src_offset.
>>>>           Considering dst file could be device specific memory (field/blob/
>>>>           whatever)
>>>>           it could be hard to track/notice proper reset behavior.
>>>> 
>>>>           So now I'm not sure if src_offset is worth adding.  
>>>> 
>>>> 
>>>>       Right. Let's just do this math in QEMU if we have to.
>>>> 
>>>>   Math complicates QEMU code though and not only QMEMU but AML code as well.
>>>>   Considering that we are adding a new command and don't have to keep
>>>>   any sort of compatibility we can pass src_offset as part
>>>>   of command instead of hiding it inside of dst_file.
>>>>   Something like this:
>>>> 
>>>>          /*
>>>>           * COMMAND_WRITE_POINTER - write the fw_cfg file (originating from
>>>>           * @dest_file) at @wr_pointer.offset, by writing 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 offset;
>>>>   +            uint32_t dst_offset;
>>>>   +            uint32_t src_offset;
>>>>               uint8_t size;
>>>>          } wr_pointer;
>>>> 
>>>> 
>>>> OK, this is easy enough to do and maybe we’ll have a use case in the future.
>>>> I’ll make this change in v7  
>>> 
>>> 
>>> So if you do, you want to set it to VMGENID_GUID_OFFSET.
>>> 
>> Oh, I was going to set it to 0 since that doesn’t require any other changes (other than to SeaBIOS)
> it should be changed in following places:
> 
>    bios_linker_loader_write_pointer(linker,
>        VMGENID_ADDR_FW_CFG_FILE, 0, sizeof(uint64_t),
> -        VMGENID_GUID_FW_CFG_FILE);
> +        VMGENID_GUID_FW_CFG_FILE, VMGENID_GUID_OFFSET);
> 
> ...
> -            cpu_physical_memory_write(vmgenid_addr + VMGENID_GUID_OFFSET,
> +            cpu_physical_memory_write(vmgenid_addr,
>                                      guid_le.data, sizeof(guid_le.data));
> 
OK, the more places I can get rid of this goofy offset the better.  Just to be clear, the address that gets patched into AML (via the add_pointer() call) remains at the beginning of /etc/vmgenid_guid, right?
> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>>                       (1) So, the above looks correct, but please replace
>>>>                       "adding" with
>>>>                       "storing", and "unsigned addition" with "store".
>>>> 
>>>>                       Side point: the case for ADD_POINTER is different;
>>>>                       there we patch
>>>>                       several individual ACPI objects. The fact that I
>>>>                       requested explicit
>>>>                       addition within the ADDR method, as opposed to
>>>>                       pre-setting VGIA to a
>>>>                       nonzero offset, is an *incidental* limitation (coming
>>>>                       from the OVMF ACPI
>>>>                       SDT header probe suppressor), and we'll likely fix that
>>>>                       up later, with
>>>>                       ALLOCATE command hints or something like that. However,
>>>>                       in
>>>>                       WRITE_POINTER, asking for the exact allocation address
>>>>                       of "src_file" is
>>>>                       an *inherent* characteristic.
>>>> 
>>>>                       For reference, this is the command's description from
>>>>                       the (not as yet
>>>>                       posted) OVMF series:
>>>> 
>>>>                       // QemuLoaderCmdWritePointer: the bytes at
>>>>                       // [PointerOffset..PointerOffset+PointerSize) in the
>>>>                       writeable fw_cfg
>>>>                       // file PointerFile are to receive the absolute address
>>>>                       of PointeeFile,
>>>>                       // as allocated and downloaded by the firmware. Store
>>>>                       the base address
>>>>                       // of where PointeeFile's contents have been placed
>>>>                       (when
>>>>                       // QemuLoaderCmdAllocate has been executed for
>>>>                       PointeeFile) to this
>>>>                       // portion of PointerFile.
>>>>                       //
>>>>                       // This command is similar to QemuLoaderCmdAddPointer;
>>>>                       the difference is
>>>>                       // that the "pointer to patch" does not exist in
>>>>                       guest-physical address
>>>>                       // space, only in "fw_cfg file space". In addition, the
>>>>                       "pointer to
>>>>                       // patch" is not initialized by QEMU with a possibly
>>>>                       nonzero offset
>>>>                       // value: the base address of the memory allocated for
>>>>                       downloading
>>>>                       // PointeeFile shall not increment the pointer, but
>>>>                       overwrite it.
>>>> 
>>>>                       In the last SeaBIOS patch series, namely
>>>> 
>>>>                       [SeaBIOS] [PATCH v3 2/2] QEMU fw_cfg: Add command to
>>>>                       write back address
>>>>                                               of file
>>>> 
>>>>                       function romfile_loader_write_pointer() implemented
>>>>                       just that plain
>>>>                       store (not an addition), and that was exactly right.
>>>> 
>>>>                       Continuing:
>>>> 
>>>> 
>>>>                               +        struct {
>>>>                               +            char dest_file
>>>>                               [BIOS_LINKER_LOADER_FILESZ];
>>>>                               +            char src_file
>>>>                               [BIOS_LINKER_LOADER_FILESZ];
>>>>                               +            uint32_t offset;
>>>>                               +            uint8_t size;
>>>>                               +        } wr_pointer;
>>>>                               +
>>>>                                       /* padding */
>>>>                                       char pad[124];
>>>>                                   };
>>>>                               @@ -85,9 +98,10 @@ struct BiosLinkerLoaderEntry
>>>>                               {
>>>>                               typedef struct BiosLinkerLoaderEntry
>>>>                               BiosLinkerLoaderEntry;
>>>> 
>>>>                               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_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 +292,41 @@ 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
>>>>                               + */
>>>>                               +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)      
>>>> 
>>>>                           API is missing "src_offset" even though it's not
>>>>                           used in this series,
>>>>                           a patch on top to fix it up is ok for me as far as
>>>>                           Seabios/OVMF
>>>>                           counterpart can handle src_offset correctly from
>>>>                           starters.      
>>>> 
>>>> 
>>>>                       According to the above, it is the right thing not to
>>>>                       add "src_offset"
>>>>                       here. The documentation on the command is slightly
>>>>                       incorrect (and causes
>>>>                       confusion), but the helper function's signature and
>>>>                       comments are okay.
>>>> 
>>>> 
>>>> 
>>>> 
>>>>                               +{
>>>>                               +    BiosLinkerLoaderEntry entry;
>>>>                               +    const BiosLinkerFileEntry *source_file =
>>>>                               +        bios_linker_find_file(linker,
>>>>                               src_file);
>>>>                               +
>>>>                               +    assert(source_file);      
>>>> 
>>>> 
>>>>                       I wish we kept the following asserts from
>>>>                       bios_linker_loader_add_pointer():
>>>> 
>>>>                          assert(dst_patched_offset < dst_file->blob->len);
>>>>                          assert(dst_patched_offset + dst_patched_size <=
>>>>                       dst_file->blob->len);
>>>> 
>>>>                       Namely, just because the dst_file is never supposed to
>>>>                       be downloaded by
>>>>                       the firmware, it still remains a requirement that the
>>>>                       "dst file offset
>>>>                       range" that is to be rewritten *do fall* within the dst
>>>>                       file.
>>>> 
>>>>                       Nonetheless, this is not critical. (OVMF at least
>>>>                       verifies it anyway.)
>>>> 
>>>>                       Summary (from my side anyway): I feel that the
>>>>                       documentation of the new
>>>>                       command is very important. Please fix it up as
>>>>                       suggested under (1), in
>>>>                       v7. Regarding the asserts, I'll let you decide.
>>>> 
>>>>                       With the documentation fixed up:
>>>> 
>>>>                       Reviewed-by: Laszlo Ersek <lersek@redhat.com>
>>>> 
>>>>                       (If you don't wish to post a v7, I'm also completely
>>>>                       fine if Michael or
>>>>                       someone else fixes up the docs as proposed in (1),
>>>>                       before committing the
>>>>                       patch.)
>>>> 
>>>>                       Thanks!
>>>>                       Laszlo
>>>> 
>>>> 
>>>>                               +    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.offset = cpu_to_le32
>>>>                               (dst_patched_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);
>>>>                               +}
>>>>                               diff --git a/include/hw/acpi/
>>>>                               bios-linker-loader.h b/include/hw/acpi/
>>>>                               bios-linker-loader.h
>>>>                               index fa1e5d1..f9ba5d6 100644
>>>>                               --- a/include/hw/acpi/bios-linker-loader.h
>>>>                               +++ b/include/hw/acpi/bios-linker-loader.h
>>>>                               @@ -26,5 +26,11 @@ 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);
>>>>                               +
>>>>                               void bios_linker_loader_cleanup(BIOSLinker
>>>>                               *linker);
>>>>                               #endif        


[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 3583 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15 18:24                   ` Igor Mammedov
@ 2017-02-15 19:14                     ` Ben Warren
  2017-02-15 19:19                       ` Ben Warren
  2017-02-15 19:34                     ` Michael S. Tsirkin
  1 sibling, 1 reply; 62+ messages in thread
From: Ben Warren @ 2017-02-15 19:14 UTC (permalink / raw)
  To: Igor Mammedov; +Cc: Michael S. Tsirkin, Laszlo Ersek, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 17495 bytes --]


> On Feb 15, 2017, at 10:24 AM, Igor Mammedov <imammedo@redhat.com> wrote:
> 
> On Wed, 15 Feb 2017 20:04:40 +0200
> "Michael S. Tsirkin" <mst@redhat.com <mailto:mst@redhat.com>> wrote:
> 
>> On Wed, Feb 15, 2017 at 06:43:09PM +0100, Igor Mammedov wrote:
>>> On Wed, 15 Feb 2017 18:39:06 +0200
>>> "Michael S. Tsirkin" <mst@redhat.com> wrote:
>>> 
>>>> On Wed, Feb 15, 2017 at 04:56:02PM +0100, Igor Mammedov wrote:  
>>>>> On Wed, 15 Feb 2017 17:30:00 +0200
>>>>> "Michael S. Tsirkin" <mst@redhat.com> wrote:
>>>>> 
>>>>>> On Wed, Feb 15, 2017 at 04:22:25PM +0100, Igor Mammedov wrote:    
>>>>>>> On Wed, 15 Feb 2017 15:13:20 +0100
>>>>>>> Laszlo Ersek <lersek@redhat.com> wrote:
>>>>>>> 
>>>>>>>> Commenting under Igor's reply for simplicity
>>>>>>>> 
>>>>>>>> On 02/15/17 11:57, Igor Mammedov wrote:      
>>>>>>>>> On Tue, 14 Feb 2017 22:15:43 -0800
>>>>>>>>> ben@skyportsystems.com wrote:
>>>>>>>>> 
>>>>>>>>>> From: Ben Warren <ben@skyportsystems.com>
>>>>>>>>>> 
>>>>>>>>>> This is similar to the existing 'add pointer' functionality, but instead
>>>>>>>>>> of instructing the guest (BIOS or UEFI) to patch memory, it instructs
>>>>>>>>>> the guest to write the pointer back to QEMU via a writeable fw_cfg file.
>>>>>>>>>> 
>>>>>>>>>> Signed-off-by: Ben Warren <ben@skyportsystems.com>
>>>>>>>>>> ---
>>>>>>>>>> hw/acpi/bios-linker-loader.c         | 58 ++++++++++++++++++++++++++++++++++--
>>>>>>>>>> include/hw/acpi/bios-linker-loader.h |  6 ++++
>>>>>>>>>> 2 files changed, 61 insertions(+), 3 deletions(-)
>>>>>>>>>> 
>>>>>>>>>> diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
>>>>>>>>>> index d963ebe..5030cf1 100644
>>>>>>>>>> --- a/hw/acpi/bios-linker-loader.c
>>>>>>>>>> +++ b/hw/acpi/bios-linker-loader.c
>>>>>>>>>> @@ -78,6 +78,19 @@ 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 the table
>>>>>>>>>> +         * originating from @src_file. 1,2,4 or 8 byte unsigned
>>>>>>>>>> +         * addition is used depending on @wr_pointer.size.
>>>>>>>>>> +         */        
>>>>>>>> 
>>>>>>>> The words "adding" and "addition" are causing confusion here.
>>>>>>>> 
>>>>>>>> In all of the previous discussion, *addition* was out of scope from
>>>>>>>> WRITE_POINTER. Again, the firmware is specifically not required to
>>>>>>>> *read* any part of the fw_cfg blob identified by "dest_file".
>>>>>>>> 
>>>>>>>> WRITE_POINTER instructs the firmware to return the allocation address of
>>>>>>>> the downloaded "src_file" to QEMU. Any necessary runtime subscripting
>>>>>>>> within "src_file" is to be handled by QEMU code dynamically.
>>>>>>>> 
>>>>>>>> For example, consider that "src_file" has *several* fields that QEMU
>>>>>>>> wants to massage; in that case, indexing within QEMU code with field
>>>>>>>> offsets is simply unavoidable.      
>>>>>>> what I don't like here is that this indexing would be rather fragile
>>>>>>> and has to be done in different parts of QEMU /device, AML/.
>>>>>>> 
>>>>>>> I'd prefer this helper function to have the same @src_offset
>>>>>>> behavior as ADD_POINTER where patched address could point to
>>>>>>> any part of src_file i.e. not just beginning.      
>>>>>> 
>>>>>> 
>>>>>> 
>>>>>>        /*
>>>>>>         * COMMAND_ADD_POINTER - patch the table (originating from
>>>>>>         * @dest_file) at @pointer.offset, by adding a pointer to the table
>>>>>>         * originating from @src_file. 1,2,4 or 8 byte unsigned
>>>>>>         * addition is used depending on @pointer.size.
>>>>>>         */
>>>>>> 
>>>>>> so the way ADD works is
>>>>>> 	read at offset
>>>>>> 	add table address
>>>>>> 	write result at offset
>>>>>> 
>>>>>> in other words it is always beginning of table that is added.    
>>>>> more exactly it's, read at 
>>>>>  src_offset = *(dst_blob_ptr+dst_offset)
>>>>>  *(dst_blob+dst_offset) = src_blob_ptr + src_offset
>>>>> 
>>>>>> Would the following be acceptable?
>>>>>> 
>>>>>> 
>>>>>>         * COMMAND_WRITE_POINTER - update the fw_cfg file (originating from
>>>>>>         * @dest_file) at @wr_pointer.offset, by writing a pointer to the table
>>>>>>         * originating from @src_file. 1,2,4 or 8 byte unsigned value
>>>>>>         * is written depending on @wr_pointer.size.    
>>>>> it looses 'adding' part of ADD_POINTER command which handles src_offset,
>>>>> however implementing adding part looks a bit complicated
>>>>> as patched blob (dst) is not in guest memory but in QEMU and
>>>>> on reset *(dst_blob+dst_offset) should be reset to src_offset.
>>>>> Considering dst file could be device specific memory (field/blob/whatever)
>>>>> it could be hard to track/notice proper reset behavior.
>>>>> 
>>>>> So now I'm not sure if src_offset is worth adding.    
>>>> 
>>>> Right. Let's just do this math in QEMU if we have to.  
>>> Math complicates QEMU code though and not only QMEMU but AML code as well.
>>> Considering that we are adding a new command and don't have to keep
>>> any sort of compatibility we can pass src_offset as part
>>> of command instead of hiding it inside of dst_file.
>>> Something like this:
>>> 
>>>        /*
>>>         * COMMAND_WRITE_POINTER - write the fw_cfg file (originating from
>>>         * @dest_file) at @wr_pointer.offset, by writing 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 offset;
>>> +            uint32_t dst_offset;
>>> +            uint32_t src_offset;
>>>             uint8_t size;
>>>        } wr_pointer;  
>> 
>> 
>> As long as all users pass in 0 though there's a real possibility guests
>> will implement this incorrectly.
> We are here to ensure that at least Seabios (I'll review it)
> and OVMF (Laszlo would take care of it I suppose) do it right,
> and if there are other firmwares, they should do it correctly
> as described fix their own bugs later wrt randomly written
> implementation.
> 
>> I guess we can put in the offset just
>> behind the zero-filled padding we have there.
> I've assumed padding was there to make commands fixed size and give
> a room for future extensions so hunk changing BiosLinkerLoaderEntry
> would look like:
> 
I can’t say I follow the logic of these extra paddings.  The sizes of the structs are all over the place, so adding 4 bytes doesn’t do much.  I assume you have a good reason, though.

> diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
> index d963ebe..6983713 100644
> --- a/hw/acpi/bios-linker-loader.c
> +++ b/hw/acpi/bios-linker-loader.c
> @@ -49,6 +49,7 @@ struct BiosLinkerLoaderEntry {
>             char file[BIOS_LINKER_LOADER_FILESZ];
>             uint32_t align;
>             uint8_t zone;
> +            uint32_t padding;
I’m a little wary of doing this - in a packed structure this new field will be mis-aligned.
>         } alloc;
> 
>         /*
> @@ -62,6 +63,7 @@ struct BiosLinkerLoaderEntry {
>             char src_file[BIOS_LINKER_LOADER_FILESZ];
>             uint32_t offset;
>             uint8_t size;
> +            uint32_t padding;
>         } pointer;
> 
>         /*
> @@ -76,10 +78,25 @@ struct BiosLinkerLoaderEntry {
>             uint32_t offset;
>             uint32_t start;
>             uint32_t length;
> +            uint32_t padding;
>         } cksum;
> 
> +        /*
> +         * COMMAND_WRITE_POINTER - write the fw_cfg file (originating from
> +         * @dest_file) at @wr_pointer.offset, by writing 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];
> +        char pad[120];
wr_pointer is 121 (56 + 56 + 32 + 32 + 1), so 124 still makes sense, doesn’t it? (also, 124 + 4 from command) % 8 == 0, so it’s nicely aligned.
>     };
> } QEMU_PACKED;
> typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
> 
> 
>> I'm mostly concerned we are adding new features to something
>> that has been through 25 revisions already.
> It's ABI so it's worth extra effort,
> it looks like only one more revision is left and there is
> about a week left to post and merge it.
> 
>> 
>> 
>>>> 
>>>>>> 
>>>>>> 
>>>>>>> 
>>>>>>> 
>>>>>>>> (1) So, the above looks correct, but please replace "adding" with
>>>>>>>> "storing", and "unsigned addition" with "store".
>>>>>>>> 
>>>>>>>> Side point: the case for ADD_POINTER is different; there we patch
>>>>>>>> several individual ACPI objects. The fact that I requested explicit
>>>>>>>> addition within the ADDR method, as opposed to pre-setting VGIA to a
>>>>>>>> nonzero offset, is an *incidental* limitation (coming from the OVMF ACPI
>>>>>>>> SDT header probe suppressor), and we'll likely fix that up later, with
>>>>>>>> ALLOCATE command hints or something like that. However, in
>>>>>>>> WRITE_POINTER, asking for the exact allocation address of "src_file" is
>>>>>>>> an *inherent* characteristic.
>>>>>>>> 
>>>>>>>> For reference, this is the command's description from the (not as yet
>>>>>>>> posted) OVMF series:
>>>>>>>> 
>>>>>>>> // QemuLoaderCmdWritePointer: the bytes at
>>>>>>>> // [PointerOffset..PointerOffset+PointerSize) in the writeable fw_cfg
>>>>>>>> // file PointerFile are to receive the absolute address of PointeeFile,
>>>>>>>> // as allocated and downloaded by the firmware. Store the base address
>>>>>>>> // of where PointeeFile's contents have been placed (when
>>>>>>>> // QemuLoaderCmdAllocate has been executed for PointeeFile) to this
>>>>>>>> // portion of PointerFile.
>>>>>>>> //
>>>>>>>> // This command is similar to QemuLoaderCmdAddPointer; the difference is
>>>>>>>> // that the "pointer to patch" does not exist in guest-physical address
>>>>>>>> // space, only in "fw_cfg file space". In addition, the "pointer to
>>>>>>>> // patch" is not initialized by QEMU with a possibly nonzero offset
>>>>>>>> // value: the base address of the memory allocated for downloading
>>>>>>>> // PointeeFile shall not increment the pointer, but overwrite it.
>>>>>>>> 
>>>>>>>> In the last SeaBIOS patch series, namely
>>>>>>>> 
>>>>>>>> [SeaBIOS] [PATCH v3 2/2] QEMU fw_cfg: Add command to write back address
>>>>>>>>                         of file
>>>>>>>> 
>>>>>>>> function romfile_loader_write_pointer() implemented just that plain
>>>>>>>> store (not an addition), and that was exactly right.
>>>>>>>> 
>>>>>>>> Continuing:
>>>>>>>> 
>>>>>>>>>> +        struct {
>>>>>>>>>> +            char dest_file[BIOS_LINKER_LOADER_FILESZ];
>>>>>>>>>> +            char src_file[BIOS_LINKER_LOADER_FILESZ];
>>>>>>>>>> +            uint32_t offset;
>>>>>>>>>> +            uint8_t size;
>>>>>>>>>> +        } wr_pointer;
>>>>>>>>>> +
>>>>>>>>>>         /* padding */
>>>>>>>>>>         char pad[124];
>>>>>>>>>>     };
>>>>>>>>>> @@ -85,9 +98,10 @@ struct BiosLinkerLoaderEntry {
>>>>>>>>>> typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
>>>>>>>>>> 
>>>>>>>>>> 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_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 +292,41 @@ 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
>>>>>>>>>> + */
>>>>>>>>>> +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)        
>>>>>>>>> API is missing "src_offset" even though it's not used in this series,
>>>>>>>>> a patch on top to fix it up is ok for me as far as Seabios/OVMF
>>>>>>>>> counterpart can handle src_offset correctly from starters.        
>>>>>>>> 
>>>>>>>> According to the above, it is the right thing not to add "src_offset"
>>>>>>>> here. The documentation on the command is slightly incorrect (and causes
>>>>>>>> confusion), but the helper function's signature and comments are okay.
>>>>>>>> 
>>>>>>>>> 
>>>>>>>>>> +{
>>>>>>>>>> +    BiosLinkerLoaderEntry entry;
>>>>>>>>>> +    const BiosLinkerFileEntry *source_file =
>>>>>>>>>> +        bios_linker_find_file(linker, src_file);
>>>>>>>>>> +
>>>>>>>>>> +    assert(source_file);        
>>>>>>>> 
>>>>>>>> I wish we kept the following asserts from bios_linker_loader_add_pointer():
>>>>>>>> 
>>>>>>>>    assert(dst_patched_offset < dst_file->blob->len);
>>>>>>>>    assert(dst_patched_offset + dst_patched_size <= dst_file->blob->len);
>>>>>>>> 
>>>>>>>> Namely, just because the dst_file is never supposed to be downloaded by
>>>>>>>> the firmware, it still remains a requirement that the "dst file offset
>>>>>>>> range" that is to be rewritten *do fall* within the dst file.
>>>>>>>> 
>>>>>>>> Nonetheless, this is not critical. (OVMF at least verifies it anyway.)
>>>>>>>> 
>>>>>>>> Summary (from my side anyway): I feel that the documentation of the new
>>>>>>>> command is very important. Please fix it up as suggested under (1), in
>>>>>>>> v7. Regarding the asserts, I'll let you decide.
>>>>>>>> 
>>>>>>>> With the documentation fixed up:
>>>>>>>> 
>>>>>>>> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>> 
>>>>>>>> (If you don't wish to post a v7, I'm also completely fine if Michael or
>>>>>>>> someone else fixes up the docs as proposed in (1), before committing the
>>>>>>>> patch.)
>>>>>>>> 
>>>>>>>> Thanks!
>>>>>>>> Laszlo
>>>>>>>> 
>>>>>>>>>> +    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.offset = cpu_to_le32(dst_patched_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);
>>>>>>>>>> +}
>>>>>>>>>> diff --git a/include/hw/acpi/bios-linker-loader.h b/include/hw/acpi/bios-linker-loader.h
>>>>>>>>>> index fa1e5d1..f9ba5d6 100644
>>>>>>>>>> --- a/include/hw/acpi/bios-linker-loader.h
>>>>>>>>>> +++ b/include/hw/acpi/bios-linker-loader.h
>>>>>>>>>> @@ -26,5 +26,11 @@ 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);
>>>>>>>>>> +
>>>>>>>>>> void bios_linker_loader_cleanup(BIOSLinker *linker);
>>>>>>>>>> #endif        


[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 3583 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15 19:14                     ` Ben Warren
@ 2017-02-15 19:19                       ` Ben Warren
  2017-02-16 11:10                         ` Igor Mammedov
  0 siblings, 1 reply; 62+ messages in thread
From: Ben Warren @ 2017-02-15 19:19 UTC (permalink / raw)
  To: Igor Mammedov; +Cc: Michael S. Tsirkin, Laszlo Ersek, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 18221 bytes --]


> On Feb 15, 2017, at 11:14 AM, Ben Warren <ben@skyportsystems.com> wrote:
> 
>> 
>> On Feb 15, 2017, at 10:24 AM, Igor Mammedov <imammedo@redhat.com <mailto:imammedo@redhat.com>> wrote:
>> 
>> On Wed, 15 Feb 2017 20:04:40 +0200
>> "Michael S. Tsirkin" <mst@redhat.com <mailto:mst@redhat.com>> wrote:
>> 
>>> On Wed, Feb 15, 2017 at 06:43:09PM +0100, Igor Mammedov wrote:
>>>> On Wed, 15 Feb 2017 18:39:06 +0200
>>>> "Michael S. Tsirkin" <mst@redhat.com <mailto:mst@redhat.com>> wrote:
>>>> 
>>>>> On Wed, Feb 15, 2017 at 04:56:02PM +0100, Igor Mammedov wrote:  
>>>>>> On Wed, 15 Feb 2017 17:30:00 +0200
>>>>>> "Michael S. Tsirkin" <mst@redhat.com <mailto:mst@redhat.com>> wrote:
>>>>>> 
>>>>>>> On Wed, Feb 15, 2017 at 04:22:25PM +0100, Igor Mammedov wrote:    
>>>>>>>> On Wed, 15 Feb 2017 15:13:20 +0100
>>>>>>>> Laszlo Ersek <lersek@redhat.com <mailto:lersek@redhat.com>> wrote:
>>>>>>>> 
>>>>>>>>> Commenting under Igor's reply for simplicity
>>>>>>>>> 
>>>>>>>>> On 02/15/17 11:57, Igor Mammedov wrote:      
>>>>>>>>>> On Tue, 14 Feb 2017 22:15:43 -0800
>>>>>>>>>> ben@skyportsystems.com <mailto:ben@skyportsystems.com> wrote:
>>>>>>>>>> 
>>>>>>>>>>> From: Ben Warren <ben@skyportsystems.com <mailto:ben@skyportsystems.com>>
>>>>>>>>>>> 
>>>>>>>>>>> This is similar to the existing 'add pointer' functionality, but instead
>>>>>>>>>>> of instructing the guest (BIOS or UEFI) to patch memory, it instructs
>>>>>>>>>>> the guest to write the pointer back to QEMU via a writeable fw_cfg file.
>>>>>>>>>>> 
>>>>>>>>>>> Signed-off-by: Ben Warren <ben@skyportsystems.com <mailto:ben@skyportsystems.com>>
>>>>>>>>>>> ---
>>>>>>>>>>> hw/acpi/bios-linker-loader.c         | 58 ++++++++++++++++++++++++++++++++++--
>>>>>>>>>>> include/hw/acpi/bios-linker-loader.h |  6 ++++
>>>>>>>>>>> 2 files changed, 61 insertions(+), 3 deletions(-)
>>>>>>>>>>> 
>>>>>>>>>>> diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
>>>>>>>>>>> index d963ebe..5030cf1 100644
>>>>>>>>>>> --- a/hw/acpi/bios-linker-loader.c
>>>>>>>>>>> +++ b/hw/acpi/bios-linker-loader.c
>>>>>>>>>>> @@ -78,6 +78,19 @@ 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 the table
>>>>>>>>>>> +         * originating from @src_file. 1,2,4 or 8 byte unsigned
>>>>>>>>>>> +         * addition is used depending on @wr_pointer.size.
>>>>>>>>>>> +         */        
>>>>>>>>> 
>>>>>>>>> The words "adding" and "addition" are causing confusion here.
>>>>>>>>> 
>>>>>>>>> In all of the previous discussion, *addition* was out of scope from
>>>>>>>>> WRITE_POINTER. Again, the firmware is specifically not required to
>>>>>>>>> *read* any part of the fw_cfg blob identified by "dest_file".
>>>>>>>>> 
>>>>>>>>> WRITE_POINTER instructs the firmware to return the allocation address of
>>>>>>>>> the downloaded "src_file" to QEMU. Any necessary runtime subscripting
>>>>>>>>> within "src_file" is to be handled by QEMU code dynamically.
>>>>>>>>> 
>>>>>>>>> For example, consider that "src_file" has *several* fields that QEMU
>>>>>>>>> wants to massage; in that case, indexing within QEMU code with field
>>>>>>>>> offsets is simply unavoidable.      
>>>>>>>> what I don't like here is that this indexing would be rather fragile
>>>>>>>> and has to be done in different parts of QEMU /device, AML/.
>>>>>>>> 
>>>>>>>> I'd prefer this helper function to have the same @src_offset
>>>>>>>> behavior as ADD_POINTER where patched address could point to
>>>>>>>> any part of src_file i.e. not just beginning.      
>>>>>>> 
>>>>>>> 
>>>>>>> 
>>>>>>>        /*
>>>>>>>         * COMMAND_ADD_POINTER - patch the table (originating from
>>>>>>>         * @dest_file) at @pointer.offset, by adding a pointer to the table
>>>>>>>         * originating from @src_file. 1,2,4 or 8 byte unsigned
>>>>>>>         * addition is used depending on @pointer.size.
>>>>>>>         */
>>>>>>> 
>>>>>>> so the way ADD works is
>>>>>>> 	read at offset
>>>>>>> 	add table address
>>>>>>> 	write result at offset
>>>>>>> 
>>>>>>> in other words it is always beginning of table that is added.    
>>>>>> more exactly it's, read at 
>>>>>>  src_offset = *(dst_blob_ptr+dst_offset)
>>>>>>  *(dst_blob+dst_offset) = src_blob_ptr + src_offset
>>>>>> 
>>>>>>> Would the following be acceptable?
>>>>>>> 
>>>>>>> 
>>>>>>>         * COMMAND_WRITE_POINTER - update the fw_cfg file (originating from
>>>>>>>         * @dest_file) at @wr_pointer.offset, by writing a pointer to the table
>>>>>>>         * originating from @src_file. 1,2,4 or 8 byte unsigned value
>>>>>>>         * is written depending on @wr_pointer.size.    
>>>>>> it looses 'adding' part of ADD_POINTER command which handles src_offset,
>>>>>> however implementing adding part looks a bit complicated
>>>>>> as patched blob (dst) is not in guest memory but in QEMU and
>>>>>> on reset *(dst_blob+dst_offset) should be reset to src_offset.
>>>>>> Considering dst file could be device specific memory (field/blob/whatever)
>>>>>> it could be hard to track/notice proper reset behavior.
>>>>>> 
>>>>>> So now I'm not sure if src_offset is worth adding.    
>>>>> 
>>>>> Right. Let's just do this math in QEMU if we have to.  
>>>> Math complicates QEMU code though and not only QMEMU but AML code as well.
>>>> Considering that we are adding a new command and don't have to keep
>>>> any sort of compatibility we can pass src_offset as part
>>>> of command instead of hiding it inside of dst_file.
>>>> Something like this:
>>>> 
>>>>        /*
>>>>         * COMMAND_WRITE_POINTER - write the fw_cfg file (originating from
>>>>         * @dest_file) at @wr_pointer.offset, by writing 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 offset;
>>>> +            uint32_t dst_offset;
>>>> +            uint32_t src_offset;
>>>>             uint8_t size;
>>>>        } wr_pointer;  
>>> 
>>> 
>>> As long as all users pass in 0 though there's a real possibility guests
>>> will implement this incorrectly.
>> We are here to ensure that at least Seabios (I'll review it)
>> and OVMF (Laszlo would take care of it I suppose) do it right,
>> and if there are other firmwares, they should do it correctly
>> as described fix their own bugs later wrt randomly written
>> implementation.
>> 
>>> I guess we can put in the offset just
>>> behind the zero-filled padding we have there.
>> I've assumed padding was there to make commands fixed size and give
>> a room for future extensions so hunk changing BiosLinkerLoaderEntry
>> would look like:
>> 
> I can’t say I follow the logic of these extra paddings.  The sizes of the structs are all over the place, so adding 4 bytes doesn’t do much.  I assume you have a good reason, though.
> 
>> diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
>> index d963ebe..6983713 100644
>> --- a/hw/acpi/bios-linker-loader.c
>> +++ b/hw/acpi/bios-linker-loader.c
>> @@ -49,6 +49,7 @@ struct BiosLinkerLoaderEntry {
>>             char file[BIOS_LINKER_LOADER_FILESZ];
>>             uint32_t align;
>>             uint8_t zone;
>> +            uint32_t padding;
> I’m a little wary of doing this - in a packed structure this new field will be mis-aligned.
>>         } alloc;
>> 
>>         /*
>> @@ -62,6 +63,7 @@ struct BiosLinkerLoaderEntry {
>>             char src_file[BIOS_LINKER_LOADER_FILESZ];
>>             uint32_t offset;
>>             uint8_t size;
>> +            uint32_t padding;
>>         } pointer;
>> 
>>         /*
>> @@ -76,10 +78,25 @@ struct BiosLinkerLoaderEntry {
>>             uint32_t offset;
>>             uint32_t start;
>>             uint32_t length;
>> +            uint32_t padding;
>>         } cksum;
>> 
>> +        /*
>> +         * COMMAND_WRITE_POINTER - write the fw_cfg file (originating from
>> +         * @dest_file) at @wr_pointer.offset, by writing 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];
>> +        char pad[120];
> wr_pointer is 121 (56 + 56 + 32 + 32 + 1), so 124 still makes sense, doesn’t it? (also, 124 + 4 from command) % 8 == 0, so it’s nicely aligned.
I mean (56 + 56 + 4 + 4 + 1), of course :)
>>     };
>> } QEMU_PACKED;
>> typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
>> 
>> 
>>> I'm mostly concerned we are adding new features to something
>>> that has been through 25 revisions already.
>> It's ABI so it's worth extra effort,
>> it looks like only one more revision is left and there is
>> about a week left to post and merge it.
>> 
>>> 
>>> 
>>>>> 
>>>>>>> 
>>>>>>> 
>>>>>>>> 
>>>>>>>> 
>>>>>>>>> (1) So, the above looks correct, but please replace "adding" with
>>>>>>>>> "storing", and "unsigned addition" with "store".
>>>>>>>>> 
>>>>>>>>> Side point: the case for ADD_POINTER is different; there we patch
>>>>>>>>> several individual ACPI objects. The fact that I requested explicit
>>>>>>>>> addition within the ADDR method, as opposed to pre-setting VGIA to a
>>>>>>>>> nonzero offset, is an *incidental* limitation (coming from the OVMF ACPI
>>>>>>>>> SDT header probe suppressor), and we'll likely fix that up later, with
>>>>>>>>> ALLOCATE command hints or something like that. However, in
>>>>>>>>> WRITE_POINTER, asking for the exact allocation address of "src_file" is
>>>>>>>>> an *inherent* characteristic.
>>>>>>>>> 
>>>>>>>>> For reference, this is the command's description from the (not as yet
>>>>>>>>> posted) OVMF series:
>>>>>>>>> 
>>>>>>>>> // QemuLoaderCmdWritePointer: the bytes at
>>>>>>>>> // [PointerOffset..PointerOffset+PointerSize) in the writeable fw_cfg
>>>>>>>>> // file PointerFile are to receive the absolute address of PointeeFile,
>>>>>>>>> // as allocated and downloaded by the firmware. Store the base address
>>>>>>>>> // of where PointeeFile's contents have been placed (when
>>>>>>>>> // QemuLoaderCmdAllocate has been executed for PointeeFile) to this
>>>>>>>>> // portion of PointerFile.
>>>>>>>>> //
>>>>>>>>> // This command is similar to QemuLoaderCmdAddPointer; the difference is
>>>>>>>>> // that the "pointer to patch" does not exist in guest-physical address
>>>>>>>>> // space, only in "fw_cfg file space". In addition, the "pointer to
>>>>>>>>> // patch" is not initialized by QEMU with a possibly nonzero offset
>>>>>>>>> // value: the base address of the memory allocated for downloading
>>>>>>>>> // PointeeFile shall not increment the pointer, but overwrite it.
>>>>>>>>> 
>>>>>>>>> In the last SeaBIOS patch series, namely
>>>>>>>>> 
>>>>>>>>> [SeaBIOS] [PATCH v3 2/2] QEMU fw_cfg: Add command to write back address
>>>>>>>>>                         of file
>>>>>>>>> 
>>>>>>>>> function romfile_loader_write_pointer() implemented just that plain
>>>>>>>>> store (not an addition), and that was exactly right.
>>>>>>>>> 
>>>>>>>>> Continuing:
>>>>>>>>> 
>>>>>>>>>>> +        struct {
>>>>>>>>>>> +            char dest_file[BIOS_LINKER_LOADER_FILESZ];
>>>>>>>>>>> +            char src_file[BIOS_LINKER_LOADER_FILESZ];
>>>>>>>>>>> +            uint32_t offset;
>>>>>>>>>>> +            uint8_t size;
>>>>>>>>>>> +        } wr_pointer;
>>>>>>>>>>> +
>>>>>>>>>>>         /* padding */
>>>>>>>>>>>         char pad[124];
>>>>>>>>>>>     };
>>>>>>>>>>> @@ -85,9 +98,10 @@ struct BiosLinkerLoaderEntry {
>>>>>>>>>>> typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
>>>>>>>>>>> 
>>>>>>>>>>> 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_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 +292,41 @@ 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
>>>>>>>>>>> + */
>>>>>>>>>>> +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)        
>>>>>>>>>> API is missing "src_offset" even though it's not used in this series,
>>>>>>>>>> a patch on top to fix it up is ok for me as far as Seabios/OVMF
>>>>>>>>>> counterpart can handle src_offset correctly from starters.        
>>>>>>>>> 
>>>>>>>>> According to the above, it is the right thing not to add "src_offset"
>>>>>>>>> here. The documentation on the command is slightly incorrect (and causes
>>>>>>>>> confusion), but the helper function's signature and comments are okay.
>>>>>>>>> 
>>>>>>>>>> 
>>>>>>>>>>> +{
>>>>>>>>>>> +    BiosLinkerLoaderEntry entry;
>>>>>>>>>>> +    const BiosLinkerFileEntry *source_file =
>>>>>>>>>>> +        bios_linker_find_file(linker, src_file);
>>>>>>>>>>> +
>>>>>>>>>>> +    assert(source_file);        
>>>>>>>>> 
>>>>>>>>> I wish we kept the following asserts from bios_linker_loader_add_pointer():
>>>>>>>>> 
>>>>>>>>>    assert(dst_patched_offset < dst_file->blob->len);
>>>>>>>>>    assert(dst_patched_offset + dst_patched_size <= dst_file->blob->len);
>>>>>>>>> 
>>>>>>>>> Namely, just because the dst_file is never supposed to be downloaded by
>>>>>>>>> the firmware, it still remains a requirement that the "dst file offset
>>>>>>>>> range" that is to be rewritten *do fall* within the dst file.
>>>>>>>>> 
>>>>>>>>> Nonetheless, this is not critical. (OVMF at least verifies it anyway.)
>>>>>>>>> 
>>>>>>>>> Summary (from my side anyway): I feel that the documentation of the new
>>>>>>>>> command is very important. Please fix it up as suggested under (1), in
>>>>>>>>> v7. Regarding the asserts, I'll let you decide.
>>>>>>>>> 
>>>>>>>>> With the documentation fixed up:
>>>>>>>>> 
>>>>>>>>> Reviewed-by: Laszlo Ersek <lersek@redhat.com <mailto:lersek@redhat.com>>
>>>>>>>>> 
>>>>>>>>> (If you don't wish to post a v7, I'm also completely fine if Michael or
>>>>>>>>> someone else fixes up the docs as proposed in (1), before committing the
>>>>>>>>> patch.)
>>>>>>>>> 
>>>>>>>>> Thanks!
>>>>>>>>> Laszlo
>>>>>>>>> 
>>>>>>>>>>> +    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.offset = cpu_to_le32(dst_patched_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);
>>>>>>>>>>> +}
>>>>>>>>>>> diff --git a/include/hw/acpi/bios-linker-loader.h b/include/hw/acpi/bios-linker-loader.h
>>>>>>>>>>> index fa1e5d1..f9ba5d6 100644
>>>>>>>>>>> --- a/include/hw/acpi/bios-linker-loader.h
>>>>>>>>>>> +++ b/include/hw/acpi/bios-linker-loader.h
>>>>>>>>>>> @@ -26,5 +26,11 @@ 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);
>>>>>>>>>>> +
>>>>>>>>>>> void bios_linker_loader_cleanup(BIOSLinker *linker);
>>>>>>>>>>> #endif        


[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 3583 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15 18:24                   ` Igor Mammedov
  2017-02-15 19:14                     ` Ben Warren
@ 2017-02-15 19:34                     ` Michael S. Tsirkin
  2017-02-16  8:25                       ` Igor Mammedov
  1 sibling, 1 reply; 62+ messages in thread
From: Michael S. Tsirkin @ 2017-02-15 19:34 UTC (permalink / raw)
  To: Igor Mammedov; +Cc: Laszlo Ersek, qemu-devel, ben

On Wed, Feb 15, 2017 at 07:24:36PM +0100, Igor Mammedov wrote:
> > As long as all users pass in 0 though there's a real possibility guests
> > will implement this incorrectly.
> We are here to ensure that at least Seabios (I'll review it)
> and OVMF (Laszlo would take care of it I suppose) do it right,
> and if there are other firmwares, they should do it correctly
> as described fix their own bugs later wrt randomly written
> implementation.

As long as it's untested, it can always do the wrong thing
even if it looks right, or it can bitrot.

> > I guess we can put in the offset just
> > behind the zero-filled padding we have there.
> I've assumed padding was there to make commands fixed size and give
> a room for future extensions

you mean
        char pad[124];
?

Right. So you can easily add fields, but if you do firmware
will just assume it does not know how to handle these
commands and skip them.

> so hunk changing BiosLinkerLoaderEntry
> would look like:
> 
> diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
> index d963ebe..6983713 100644
> --- a/hw/acpi/bios-linker-loader.c
> +++ b/hw/acpi/bios-linker-loader.c
> @@ -49,6 +49,7 @@ struct BiosLinkerLoaderEntry {
>              char file[BIOS_LINKER_LOADER_FILESZ];
>              uint32_t align;
>              uint8_t zone;
> +            uint32_t padding;
>          } alloc;
>  
>          /*
> @@ -62,6 +63,7 @@ struct BiosLinkerLoaderEntry {
>              char src_file[BIOS_LINKER_LOADER_FILESZ];
>              uint32_t offset;
>              uint8_t size;
> +            uint32_t padding;
>          } pointer;
>  
>          /*
> @@ -76,10 +78,25 @@ struct BiosLinkerLoaderEntry {
>              uint32_t offset;
>              uint32_t start;
>              uint32_t length;
> +            uint32_t padding;
>          } cksum;

why is this necessary?

> +        /*
> +         * COMMAND_WRITE_POINTER - write the fw_cfg file (originating from
> +         * @dest_file) at @wr_pointer.offset, by writing 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];
> +        char pad[120];

and this shrinks entry size by 4 bytes. Why?

>      };
>  } QEMU_PACKED;
>  typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
> 
>  
> > I'm mostly concerned we are adding new features to something
> > that has been through 25 revisions already.
> It's ABI so it's worth extra effort,

There's always yet another possible enhancement you can add.
I see it as a bit of a feature creep frankly.

> it looks like only one more revision is left and there is
> about a week left to post and merge it.

If we can do it quickly, fine, but I think we should merge ASAP.

-- 
MST

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID
  2017-02-15  6:15 [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID ben
                   ` (6 preceding siblings ...)
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 7/7] tests: Add unit tests for the VM Generation ID feature ben
@ 2017-02-15 19:47 ` Laszlo Ersek
  2017-02-15 20:09   ` Michael S. Tsirkin
  7 siblings, 1 reply; 62+ messages in thread
From: Laszlo Ersek @ 2017-02-15 19:47 UTC (permalink / raw)
  To: ben, qemu-devel; +Cc: mst, imammedo

On 02/15/17 07:15, ben@skyportsystems.com wrote:
> From: Ben Warren <ben@skyportsystems.com>
>
> This patch set adds support for passing a GUID to Windows guests.  It
> is a re-implementation of previous patch sets written by Igor Mammedov
> et al, but this time passing the GUID data as a fw_cfg blob.
>
> This patch set has dependencies on new guest functionality, in
> particular the support for a new linker-loader command and the ability
> to write back data to QEMU over a DMA link.  Work is in flight in both
> SeaBIOS and OVMF to support this.
>
> v5->v6:
>     - Rebased to top of tree.
>     - Changed device from sysbus to a simple device.  This removed the need for
>       adding dynamic sysbus support to pc_piix boards.
>     - Removed patch that introduced QWORD patching of AML.
>     - Removed ability to set GUID via QMP/HMP.
>     - Improved comments/documentation in code.

So here's my testing with a RHEL-7 guest:

(1) The command line option passed to QEMU is

  -device vmgenid,guid=00112233-4455-6677-8899-AABBCCDDEEFF

This is the example GUID provided in the SMBIOS spec v3.0.0 (DSP0134),
section 7.2.1 "System -- UUID". (SMBIOS is only relevant here because it
codifies the fact that Microsoft consumes UUID in little-endian order.)
The expected representation, according to the SMBIOS spec, is

  33 22 11 00 55 44 77 66 88 99 AA BB CC DD EE FF

(2) Here's an excerpt from the OVMF log:

> ProcessCmdAllocate: File="etc/vmgenid_guid" Alignment=0x1000 Zone=1 Size=0x1000 Address=0x7FE5C000

This is where "etc/vmgenid_guid" is allocated and downloaded, the
allocation address is 0x7FE5C000.

> Select Item: 0x19
> Select Item: 0x22
> ProcessCmdAllocate: File="etc/acpi/tables" Alignment=0x40 Zone=1 Size=0x20000 Address=0x7E7AB000
> ProcessCmdAddChecksum: File="etc/acpi/tables" ResultOffset=0x49 Start=0x40 Length=0x1403
> ProcessCmdAddPointer: PointerFile="etc/acpi/tables" PointeeFile="etc/acpi/tables" PointerOffset=0x1467 PointerSize=4
> ProcessCmdAddPointer: PointerFile="etc/acpi/tables" PointeeFile="etc/acpi/tables" PointerOffset=0x146B PointerSize=4
> ProcessCmdAddChecksum: File="etc/acpi/tables" ResultOffset=0x144C Start=0x1443 Length=0x74
> ProcessCmdAddChecksum: File="etc/acpi/tables" ResultOffset=0x14C0 Start=0x14B7 Length=0x80
> Select Item: 0x19
> SaveCondensedWritePointerToS3Context: 0x002B/[0x00000000+8] := 0x7FE5C000 (0)

This is where OVMF stashes the WRITE_POINTER command in "condensed"
form, for S3. The fw_cfg selector value is 0x2B (for the fw_cfg file to
be rewritten), the pointer is located at offset 0, has size 0, and the
value to assign is 0x7FE5C000. And, this is #0 of the saved / condensed
WRITE_POINTER commands.

> Select Item: 0x2B
> ProcessCmdWritePointer: PointerFile="etc/vmgenid_addr" PointeeFile="etc/vmgenid_guid" PointerOffset=0x0 PointerSize=8

This is where the WRITE_POINTER command is actually executed, during
normal boot.

> ProcessCmdAddPointer: PointerFile="etc/acpi/tables" PointeeFile="etc/vmgenid_guid" PointerOffset=0x1561 PointerSize=4

This is where we link "etc/vmgenid_guid" into VGIA.

> ProcessCmdAddChecksum: File="etc/acpi/tables" ResultOffset=0x1540 Start=0x1537 Length=0xCA
> ProcessCmdAddPointer: PointerFile="etc/acpi/tables" PointeeFile="etc/acpi/tables" PointerOffset=0x1625 PointerSize=4
> ProcessCmdAddPointer: PointerFile="etc/acpi/tables" PointeeFile="etc/acpi/tables" PointerOffset=0x1629 PointerSize=4
> ProcessCmdAddPointer: PointerFile="etc/acpi/tables" PointeeFile="etc/acpi/tables" PointerOffset=0x162D PointerSize=4
> ProcessCmdAddChecksum: File="etc/acpi/tables" ResultOffset=0x160A Start=0x1601 Length=0x30
> ProcessCmdAddPointer: PointerFile="etc/acpi/rsdp" PointeeFile="etc/acpi/tables" PointerOffset=0x10 PointerSize=4
> ProcessCmdAddChecksum: File="etc/acpi/rsdp" ResultOffset=0x8 Start=0x0 Length=0x24
> InstallQemuFwCfgTables: unknown loader command: 0x0
> InstallQemuFwCfgTables: unknown loader command: 0x0
> InstallQemuFwCfgTables: unknown loader command: 0x0
> InstallQemuFwCfgTables: unknown loader command: 0x0
> InstallQemuFwCfgTables: unknown loader command: 0x0
> InstallQemuFwCfgTables: unknown loader command: 0x0
> InstallQemuFwCfgTables: unknown loader command: 0x0
> InstallQemuFwCfgTables: unknown loader command: 0x0
> InstallQemuFwCfgTables: unknown loader command: 0x0
> InstallQemuFwCfgTables: unknown loader command: 0x0
> InstallQemuFwCfgTables: unknown loader command: 0x0
> InstallQemuFwCfgTables: unknown loader command: 0x0
> InstallQemuFwCfgTables: unknown loader command: 0x0
> InstallQemuFwCfgTables: unknown loader command: 0x0
> InstallQemuFwCfgTables: unknown loader command: 0x0
> Process2ndPassCmdAddPointer: checking for ACPI header in "etc/acpi/tables" at 0x7E7AB000 (remaining: 0x20000): found "FACS" size 0x40
> Process2ndPassCmdAddPointer: checking for ACPI header in "etc/acpi/tables" at 0x7E7AB040 (remaining: 0x1FFC0): found "DSDT" size 0x1403
> Process2ndPassCmdAddPointer: checking for ACPI header in "etc/vmgenid_guid" at 0x7FE5C000 (remaining: 0x1000): not found; marking fw_cfg blob as opaque

This is where the OVMF SDT Header Probe Suppressor does its job. (NB,
the "opaque marking" has happened already in ProcessCmdWritePointer()
too, above.)

> Process2ndPassCmdAddPointer: checking for ACPI header in "etc/acpi/tables" at 0x7E7AC443 (remaining: 0x1EBBD): found "FACP" size 0x74
> Process2ndPassCmdAddPointer: checking for ACPI header in "etc/acpi/tables" at 0x7E7AC4B7 (remaining: 0x1EB49): found "APIC" size 0x80
> Process2ndPassCmdAddPointer: checking for ACPI header in "etc/acpi/tables" at 0x7E7AC537 (remaining: 0x1EAC9): found "SSDT" size 0xCA
> Process2ndPassCmdAddPointer: checking for ACPI header in "etc/acpi/tables" at 0x7E7AC601 (remaining: 0x1E9FF): found "RSDT" size 0x30
> TransferS3ContextToBootScript: boot script fragment saved, ScratchBuffer=7FE4F018

This is where the WRITE_POINTER commands, stashed earlier in condensed
form, are translated to S3 Boot Script opcodes.

> InstallQemuFwCfgTables: installed 5 tables

Such as: FACS, DSDT, FACP, APIC, SSDT. OVMF recognizes RSDT and ignores
it (it's handled by edk2 automatically).

> InstallQemuFwCfgTables: freeing "etc/acpi/rsdp"
> InstallQemuFwCfgTables: freeing "etc/acpi/tables"

OVMF sees that the above two blobs have not been marked as "opaque" --
they only contained ACPI tables, judged from the ADD_POINTER commands
that pointed into them. So these two blobs are freed.

Note that "etc/vmgenid_guid" is not freed.

So, from the firmware log, everything looks OK.

(3) I dumped the SSDT in the RHEL-7 guest:

> /*
>  * Intel ACPI Component Architecture
>  * AML/ASL+ Disassembler version 20160527-64
>  * Copyright (c) 2000 - 2016 Intel Corporation
>  *
>  * Disassembling to symbolic ASL+ operators
>  *
>  * Disassembly of ssdt.dat, Wed Feb 15 19:21:11 2017
>  *
>  * Original Table Header:
>  *     Signature        "SSDT"
>  *     Length           0x000000CA (202)
>  *     Revision         0x01
>  *     Checksum         0x1D
>  *     OEM ID           "BOCHS "
>  *     OEM Table ID     "VMGENID"
>  *     OEM Revision     0x00000001 (1)
>  *     Compiler ID      "BXPC"
>  *     Compiler Version 0x00000001 (1)
>  */
> DefinitionBlock ("", "SSDT", 1, "BOCHS ", "VMGENID", 0x00000001)
> {
>     Name (VGIA, 0x7FE5C000)

Note that the value matches the value logged by the firmware in (2).

>     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) {}
>                 Local0 [Zero] = (VGIA + 0x28)
>                 Local0 [One] = Zero
>                 Return (Local0)
>             }
>         }
>     }
>
>     Method (\_GPE._E05, 0, NotSerialized)  // _Exx: Edge-Triggered GPE
>     {
>         Notify (\_SB.VGEN, 0x80) // Status Change
>     }
> }

Looks good and matches the documentation.

(4) To be sure, I checked the address against the guest dmesg, which
contains a dump of the UEFI memory map:

> [    0.000000] efi: mem52: type=10, attr=0xf, range=[0x000000007fe5a000-0x000000007fe5e000) (0MB)

The page (4096 bytes) at 0x7FE5C000 falls into this range. Type=10 means
EfiACPIMemoryNVS.

(5) At this point I dumped the guest RAM with the dump-guest-memory
monitor command, opened it with "crash", and listed it:

> crash> rd -p -8 0x7FE5C000 0x40
>         7fe5c000:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
>         7fe5c010:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
>         7fe5c020:  00 00 00 00 00 00 00 00 33 22 11 00 55 44 77 66   ........3"..UDwf
>         7fe5c030:  88 99 aa bb cc dd ee ff 00 00 00 00 00 00 00 00   ................

We can see that the GUID starts at 0x7FE5C000 + 0x28, and also that the
byte-level representation matches the little endian one given in (1).

This proves that the initial blob download worked fine.

(6) Here I attached "gdb" to QEMU, set a breakpoint on
vmgenid_handle_reset(), allowed the inferior process to continue
execution.

Then I suspended and resumed the guest (ACPI S3). The breakpoint was hit
during resume:

> Breakpoint 1, vmgenid_handle_reset (opaque=0x7f2bd03c36e0) at .../hw/acpi/vmgenid.c:205
> 205         VmGenIdState *vms = VMGENID(opaque);

First of all, before allowing QEMU to zero out the address blob, I
listed the address and the contents of the address blob (here exploiting
that my host is also little endian):

> (gdb) print (void*)vms->vmgenid_addr_le
> $2 = (void *) 0x7f2bd03c37b0

> (gdb) print /x *(uint64_t*)vms->vmgenid_addr_le
> $4 = 0x7fe5c000

This proves that QEMU has the right address, matching the firmware log
from (2), and the ACPI dump from (3).

(7) At this point I allowed the inferior to proceed a bit:

> (gdb) n
> 207         memset(vms->vmgenid_addr_le, 0, ARRAY_SIZE(vms->vmgenid_addr_le));
> (gdb) n
> 208     }

I verified that the blob was zeroed:

> (gdb) print /x *(uint64_t*)vms->vmgenid_addr_le
> $5 = 0x0

then allowed the inferior to run free.

> (gdb) cont
> Continuing.

(8) New messages appeared in the firmware log:

> S3ResumeExecuteBootScript()
> PeiS3ResumeState - 7FF92B18
> transfer control to Standalone Boot Script Executor
> S3BootScriptExecute:
> TableHeader - 0x7E7A7000
> TableHeader.Version - 0x0001
> TableHeader.TableLength - 0x000000ED
> ExecuteBootScript - 7E7A700D
> EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE
> BootScriptExecuteMemoryWrite - 0x7FE4F018, 0x00000010, 0x00000000

Here the ACPI S3 Boot Script, prepared in
TransferS3ContextToBootScript() -- see (2) -- creates a DMA access
command for fw_cfg. The DMA access command is written to pre-reserved
memory (see "ScratchBuffer" above).

> S3BootScriptWidthUint8 - 0x7FE4F018 (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F019 (0x2B)

The fw_cfg selector is 0x2B. (See under (2).)

> S3BootScriptWidthUint8 - 0x7FE4F01A (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F01B (0x0C)

This is a combined select+skip operation.

> S3BootScriptWidthUint8 - 0x7FE4F01C (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F01D (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F01E (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F01F (0x00)

The skip size is 0 bytes.

> S3BootScriptWidthUint8 - 0x7FE4F020 (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F021 (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F022 (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F023 (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F024 (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F025 (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F026 (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F027 (0x00)

The address is irrelevant for skip, so it's just nuleld.

> ExecuteBootScript - 7E7A7030
> EFI_BOOT_SCRIPT_IO_WRITE_OPCODE
> BootScriptExecuteIoWrite - 0x00000514, 0x00000002, 0x00000002
> S3BootScriptWidthUint32 - 0x00000514 (0x00000000)
> S3BootScriptWidthUint32 - 0x00000518 (0x18F0E47F)

The Boot Script passes the DMA command to QEMU, by writing the address
of the command buffer to IO ports 0x514 and 0x518, in BE byte order.

> ExecuteBootScript - 7E7A704B
> EFI_BOOT_SCRIPT_MEM_POLL_OPCODE
> BootScriptExecuteMemPoll - 0x7FE4F018, 0x00000000FFFFFFFF, 0x0000000000000000
> S3BootScriptWidthUint32 - 0x7FE4F018
> ExecuteBootScript - 7E7A7072

This waits until the DMA command succeeds (reading back the Control
field continuously until it reads as zero).

> EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE
> BootScriptExecuteMemoryWrite - 0x7FE4F018, 0x00000018, 0x00000000

This is another DMA access command for fw_cfg, prepared in the same
pre-reserved buffer. This time

> S3BootScriptWidthUint8 - 0x7FE4F018 (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F019 (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F01A (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F01B (0x10)

we request a write operation,

> S3BootScriptWidthUint8 - 0x7FE4F01C (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F01D (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F01E (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F01F (0x08)

with a length of 8 bytes (big endian), matching the pointer size,

> S3BootScriptWidthUint8 - 0x7FE4F020 (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F021 (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F022 (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F023 (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F024 (0x7F)
> S3BootScriptWidthUint8 - 0x7FE4F025 (0xE4)
> S3BootScriptWidthUint8 - 0x7FE4F026 (0xF0)
> S3BootScriptWidthUint8 - 0x7FE4F027 (0x28)

the data to transfer is located at 0x7FE4F028 (just below, tacked to the
command buffer itself),

> S3BootScriptWidthUint8 - 0x7FE4F028 (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F029 (0xC0)
> S3BootScriptWidthUint8 - 0x7FE4F02A (0xE5)
> S3BootScriptWidthUint8 - 0x7FE4F02B (0x7F)
> S3BootScriptWidthUint8 - 0x7FE4F02C (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F02D (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F02E (0x00)
> S3BootScriptWidthUint8 - 0x7FE4F02F (0x00)

and the data to write is the original allocation address of the blob
(0x7fe5c000).

> ExecuteBootScript - 7E7A709D
> EFI_BOOT_SCRIPT_IO_WRITE_OPCODE
> BootScriptExecuteIoWrite - 0x00000514, 0x00000002, 0x00000002
> S3BootScriptWidthUint32 - 0x00000514 (0x00000000)
> S3BootScriptWidthUint32 - 0x00000518 (0x18F0E47F)
> ExecuteBootScript - 7E7A70B8
> EFI_BOOT_SCRIPT_MEM_POLL_OPCODE
> BootScriptExecuteMemPoll - 0x7FE4F018, 0x00000000FFFFFFFF, 0x0000000000000000
> S3BootScriptWidthUint32 - 0x7FE4F018
> ExecuteBootScript - 7E7A70DF

Same story as above: fire off the transfer and wait until it completes.

> EFI_BOOT_SCRIPT_INFORMATION_OPCODE
> BootScriptExecuteInformation - 0x7E7A70E6
> BootScriptInformation: DE AD BE EF
> ExecuteBootScript - 7E7A70EA
> S3_BOOT_SCRIPT_LIB_TERMINATE_OPCODE
> S3BootScriptDone - Success
> [...]

The DEADBEEF informational (no-op) opcode is something that OVMF appends
to the very end for hysterical raisins.

(9) Okay, so the guest is now resumed and running, let's interrupt it in
gdb again, and check the contents of address blob again (we know the
address of the address blob from step (6)):

> ^C
> Program received signal SIGINT, Interrupt.
> 0x00007f2bbf1d1ebf in ppoll () from /lib64/libc.so.6
> (gdb) print /x *(uint64_t*)0x7f2bd03c37b0
> $6 = 0x7fe5c000

Et voila.

(10) I detached gdb from QEMU, and issued the following monitor command:

> $ virsh qemu-monitor-command ovmf.rhel7 --hmp 'info vm-generation-id'
> 00112233-4455-6677-8899-aabbccddeeff

(11) I also booted a Windows Server 2012 R2 guest (Q35, broadcast SMI
enabled) with a similar vmgenid device/parameter. According to Device
Manager | System devices, "Microsoft Hyper-V Generation Counter" is
working properly.

I also tested S3 briefly, it worked okay. (I mentioned the SMI broadcast
above because for that, OVMF generates an independent S3 Boot Script
fragment.)


I'll let someone else test live migration.

For patches #1, #3, #4 and #5:

Tested-by: Laszlo Ersek <lersek@redhat.com>

I'll soon post the OVMF patches.

Thanks!
Laszlo

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID
  2017-02-15 19:47 ` [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID Laszlo Ersek
@ 2017-02-15 20:09   ` Michael S. Tsirkin
  2017-02-15 20:15     ` Ben Warren
  2017-02-15 20:52     ` Laszlo Ersek
  0 siblings, 2 replies; 62+ messages in thread
From: Michael S. Tsirkin @ 2017-02-15 20:09 UTC (permalink / raw)
  To: Laszlo Ersek; +Cc: ben, qemu-devel, imammedo

On Wed, Feb 15, 2017 at 08:47:48PM +0100, Laszlo Ersek wrote:
> On 02/15/17 07:15, ben@skyportsystems.com wrote:
> > From: Ben Warren <ben@skyportsystems.com>
> >
> > This patch set adds support for passing a GUID to Windows guests.  It
> > is a re-implementation of previous patch sets written by Igor Mammedov
> > et al, but this time passing the GUID data as a fw_cfg blob.
> >
> > This patch set has dependencies on new guest functionality, in
> > particular the support for a new linker-loader command and the ability
> > to write back data to QEMU over a DMA link.  Work is in flight in both
> > SeaBIOS and OVMF to support this.
> >
> > v5->v6:
> >     - Rebased to top of tree.
> >     - Changed device from sysbus to a simple device.  This removed the need for
> >       adding dynamic sysbus support to pc_piix boards.
> >     - Removed patch that introduced QWORD patching of AML.
> >     - Removed ability to set GUID via QMP/HMP.
> >     - Improved comments/documentation in code.
> 
> So here's my testing with a RHEL-7 guest:
> 
> (1) The command line option passed to QEMU is
> 
>   -device vmgenid,guid=00112233-4455-6677-8899-AABBCCDDEEFF
> 
> This is the example GUID provided in the SMBIOS spec v3.0.0 (DSP0134),
> section 7.2.1 "System -- UUID". (SMBIOS is only relevant here because it
> codifies the fact that Microsoft consumes UUID in little-endian order.)
> The expected representation, according to the SMBIOS spec, is
> 
>   33 22 11 00 55 44 77 66 88 99 AA BB CC DD EE FF
> 
> (2) Here's an excerpt from the OVMF log:
> 
> > ProcessCmdAllocate: File="etc/vmgenid_guid" Alignment=0x1000 Zone=1 Size=0x1000 Address=0x7FE5C000
> 
> This is where "etc/vmgenid_guid" is allocated and downloaded, the
> allocation address is 0x7FE5C000.
> 
> > Select Item: 0x19
> > Select Item: 0x22
> > ProcessCmdAllocate: File="etc/acpi/tables" Alignment=0x40 Zone=1 Size=0x20000 Address=0x7E7AB000
> > ProcessCmdAddChecksum: File="etc/acpi/tables" ResultOffset=0x49 Start=0x40 Length=0x1403
> > ProcessCmdAddPointer: PointerFile="etc/acpi/tables" PointeeFile="etc/acpi/tables" PointerOffset=0x1467 PointerSize=4
> > ProcessCmdAddPointer: PointerFile="etc/acpi/tables" PointeeFile="etc/acpi/tables" PointerOffset=0x146B PointerSize=4
> > ProcessCmdAddChecksum: File="etc/acpi/tables" ResultOffset=0x144C Start=0x1443 Length=0x74
> > ProcessCmdAddChecksum: File="etc/acpi/tables" ResultOffset=0x14C0 Start=0x14B7 Length=0x80
> > Select Item: 0x19
> > SaveCondensedWritePointerToS3Context: 0x002B/[0x00000000+8] := 0x7FE5C000 (0)
> 
> This is where OVMF stashes the WRITE_POINTER command in "condensed"
> form, for S3. The fw_cfg selector value is 0x2B (for the fw_cfg file to
> be rewritten), the pointer is located at offset 0, has size 0, and the
> value to assign is 0x7FE5C000. And, this is #0 of the saved / condensed
> WRITE_POINTER commands.
> 
> > Select Item: 0x2B
> > ProcessCmdWritePointer: PointerFile="etc/vmgenid_addr" PointeeFile="etc/vmgenid_guid" PointerOffset=0x0 PointerSize=8
> 
> This is where the WRITE_POINTER command is actually executed, during
> normal boot.
> 
> > ProcessCmdAddPointer: PointerFile="etc/acpi/tables" PointeeFile="etc/vmgenid_guid" PointerOffset=0x1561 PointerSize=4
> 
> This is where we link "etc/vmgenid_guid" into VGIA.
> 
> > ProcessCmdAddChecksum: File="etc/acpi/tables" ResultOffset=0x1540 Start=0x1537 Length=0xCA
> > ProcessCmdAddPointer: PointerFile="etc/acpi/tables" PointeeFile="etc/acpi/tables" PointerOffset=0x1625 PointerSize=4
> > ProcessCmdAddPointer: PointerFile="etc/acpi/tables" PointeeFile="etc/acpi/tables" PointerOffset=0x1629 PointerSize=4
> > ProcessCmdAddPointer: PointerFile="etc/acpi/tables" PointeeFile="etc/acpi/tables" PointerOffset=0x162D PointerSize=4
> > ProcessCmdAddChecksum: File="etc/acpi/tables" ResultOffset=0x160A Start=0x1601 Length=0x30
> > ProcessCmdAddPointer: PointerFile="etc/acpi/rsdp" PointeeFile="etc/acpi/tables" PointerOffset=0x10 PointerSize=4
> > ProcessCmdAddChecksum: File="etc/acpi/rsdp" ResultOffset=0x8 Start=0x0 Length=0x24
> > InstallQemuFwCfgTables: unknown loader command: 0x0
> > InstallQemuFwCfgTables: unknown loader command: 0x0
> > InstallQemuFwCfgTables: unknown loader command: 0x0
> > InstallQemuFwCfgTables: unknown loader command: 0x0
> > InstallQemuFwCfgTables: unknown loader command: 0x0
> > InstallQemuFwCfgTables: unknown loader command: 0x0
> > InstallQemuFwCfgTables: unknown loader command: 0x0
> > InstallQemuFwCfgTables: unknown loader command: 0x0
> > InstallQemuFwCfgTables: unknown loader command: 0x0
> > InstallQemuFwCfgTables: unknown loader command: 0x0
> > InstallQemuFwCfgTables: unknown loader command: 0x0
> > InstallQemuFwCfgTables: unknown loader command: 0x0
> > InstallQemuFwCfgTables: unknown loader command: 0x0
> > InstallQemuFwCfgTables: unknown loader command: 0x0
> > InstallQemuFwCfgTables: unknown loader command: 0x0
> > Process2ndPassCmdAddPointer: checking for ACPI header in "etc/acpi/tables" at 0x7E7AB000 (remaining: 0x20000): found "FACS" size 0x40
> > Process2ndPassCmdAddPointer: checking for ACPI header in "etc/acpi/tables" at 0x7E7AB040 (remaining: 0x1FFC0): found "DSDT" size 0x1403
> > Process2ndPassCmdAddPointer: checking for ACPI header in "etc/vmgenid_guid" at 0x7FE5C000 (remaining: 0x1000): not found; marking fw_cfg blob as opaque
> 
> This is where the OVMF SDT Header Probe Suppressor does its job. (NB,
> the "opaque marking" has happened already in ProcessCmdWritePointer()
> too, above.)
> 
> > Process2ndPassCmdAddPointer: checking for ACPI header in "etc/acpi/tables" at 0x7E7AC443 (remaining: 0x1EBBD): found "FACP" size 0x74
> > Process2ndPassCmdAddPointer: checking for ACPI header in "etc/acpi/tables" at 0x7E7AC4B7 (remaining: 0x1EB49): found "APIC" size 0x80
> > Process2ndPassCmdAddPointer: checking for ACPI header in "etc/acpi/tables" at 0x7E7AC537 (remaining: 0x1EAC9): found "SSDT" size 0xCA
> > Process2ndPassCmdAddPointer: checking for ACPI header in "etc/acpi/tables" at 0x7E7AC601 (remaining: 0x1E9FF): found "RSDT" size 0x30
> > TransferS3ContextToBootScript: boot script fragment saved, ScratchBuffer=7FE4F018
> 
> This is where the WRITE_POINTER commands, stashed earlier in condensed
> form, are translated to S3 Boot Script opcodes.
> 
> > InstallQemuFwCfgTables: installed 5 tables
> 
> Such as: FACS, DSDT, FACP, APIC, SSDT. OVMF recognizes RSDT and ignores
> it (it's handled by edk2 automatically).
> 
> > InstallQemuFwCfgTables: freeing "etc/acpi/rsdp"
> > InstallQemuFwCfgTables: freeing "etc/acpi/tables"
> 
> OVMF sees that the above two blobs have not been marked as "opaque" --
> they only contained ACPI tables, judged from the ADD_POINTER commands
> that pointed into them. So these two blobs are freed.
> 
> Note that "etc/vmgenid_guid" is not freed.
> 
> So, from the firmware log, everything looks OK.
> 
> (3) I dumped the SSDT in the RHEL-7 guest:
> 
> > /*
> >  * Intel ACPI Component Architecture
> >  * AML/ASL+ Disassembler version 20160527-64
> >  * Copyright (c) 2000 - 2016 Intel Corporation
> >  *
> >  * Disassembling to symbolic ASL+ operators
> >  *
> >  * Disassembly of ssdt.dat, Wed Feb 15 19:21:11 2017
> >  *
> >  * Original Table Header:
> >  *     Signature        "SSDT"
> >  *     Length           0x000000CA (202)
> >  *     Revision         0x01
> >  *     Checksum         0x1D
> >  *     OEM ID           "BOCHS "
> >  *     OEM Table ID     "VMGENID"
> >  *     OEM Revision     0x00000001 (1)
> >  *     Compiler ID      "BXPC"
> >  *     Compiler Version 0x00000001 (1)
> >  */
> > DefinitionBlock ("", "SSDT", 1, "BOCHS ", "VMGENID", 0x00000001)
> > {
> >     Name (VGIA, 0x7FE5C000)
> 
> Note that the value matches the value logged by the firmware in (2).
> 
> >     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) {}
> >                 Local0 [Zero] = (VGIA + 0x28)
> >                 Local0 [One] = Zero
> >                 Return (Local0)
> >             }
> >         }
> >     }
> >
> >     Method (\_GPE._E05, 0, NotSerialized)  // _Exx: Edge-Triggered GPE
> >     {
> >         Notify (\_SB.VGEN, 0x80) // Status Change
> >     }
> > }
> 
> Looks good and matches the documentation.
> 
> (4) To be sure, I checked the address against the guest dmesg, which
> contains a dump of the UEFI memory map:
> 
> > [    0.000000] efi: mem52: type=10, attr=0xf, range=[0x000000007fe5a000-0x000000007fe5e000) (0MB)
> 
> The page (4096 bytes) at 0x7FE5C000 falls into this range. Type=10 means
> EfiACPIMemoryNVS.
> 
> (5) At this point I dumped the guest RAM with the dump-guest-memory
> monitor command, opened it with "crash", and listed it:
> 
> > crash> rd -p -8 0x7FE5C000 0x40
> >         7fe5c000:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
> >         7fe5c010:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
> >         7fe5c020:  00 00 00 00 00 00 00 00 33 22 11 00 55 44 77 66   ........3"..UDwf
> >         7fe5c030:  88 99 aa bb cc dd ee ff 00 00 00 00 00 00 00 00   ................
> 
> We can see that the GUID starts at 0x7FE5C000 + 0x28, and also that the
> byte-level representation matches the little endian one given in (1).
> 
> This proves that the initial blob download worked fine.
> 
> (6) Here I attached "gdb" to QEMU, set a breakpoint on
> vmgenid_handle_reset(), allowed the inferior process to continue
> execution.
> 
> Then I suspended and resumed the guest (ACPI S3). The breakpoint was hit
> during resume:
> 
> > Breakpoint 1, vmgenid_handle_reset (opaque=0x7f2bd03c36e0) at .../hw/acpi/vmgenid.c:205
> > 205         VmGenIdState *vms = VMGENID(opaque);
> 
> First of all, before allowing QEMU to zero out the address blob, I
> listed the address and the contents of the address blob (here exploiting
> that my host is also little endian):
> 
> > (gdb) print (void*)vms->vmgenid_addr_le
> > $2 = (void *) 0x7f2bd03c37b0
> 
> > (gdb) print /x *(uint64_t*)vms->vmgenid_addr_le
> > $4 = 0x7fe5c000
> 
> This proves that QEMU has the right address, matching the firmware log
> from (2), and the ACPI dump from (3).
> 
> (7) At this point I allowed the inferior to proceed a bit:
> 
> > (gdb) n
> > 207         memset(vms->vmgenid_addr_le, 0, ARRAY_SIZE(vms->vmgenid_addr_le));
> > (gdb) n
> > 208     }
> 
> I verified that the blob was zeroed:
> 
> > (gdb) print /x *(uint64_t*)vms->vmgenid_addr_le
> > $5 = 0x0
> 
> then allowed the inferior to run free.
> 
> > (gdb) cont
> > Continuing.
> 
> (8) New messages appeared in the firmware log:
> 
> > S3ResumeExecuteBootScript()
> > PeiS3ResumeState - 7FF92B18
> > transfer control to Standalone Boot Script Executor
> > S3BootScriptExecute:
> > TableHeader - 0x7E7A7000
> > TableHeader.Version - 0x0001
> > TableHeader.TableLength - 0x000000ED
> > ExecuteBootScript - 7E7A700D
> > EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE
> > BootScriptExecuteMemoryWrite - 0x7FE4F018, 0x00000010, 0x00000000
> 
> Here the ACPI S3 Boot Script, prepared in
> TransferS3ContextToBootScript() -- see (2) -- creates a DMA access
> command for fw_cfg. The DMA access command is written to pre-reserved
> memory (see "ScratchBuffer" above).
> 
> > S3BootScriptWidthUint8 - 0x7FE4F018 (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F019 (0x2B)
> 
> The fw_cfg selector is 0x2B. (See under (2).)
> 
> > S3BootScriptWidthUint8 - 0x7FE4F01A (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F01B (0x0C)
> 
> This is a combined select+skip operation.
> 
> > S3BootScriptWidthUint8 - 0x7FE4F01C (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F01D (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F01E (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F01F (0x00)
> 
> The skip size is 0 bytes.
> 
> > S3BootScriptWidthUint8 - 0x7FE4F020 (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F021 (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F022 (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F023 (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F024 (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F025 (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F026 (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F027 (0x00)
> 
> The address is irrelevant for skip, so it's just nuleld.
> 
> > ExecuteBootScript - 7E7A7030
> > EFI_BOOT_SCRIPT_IO_WRITE_OPCODE
> > BootScriptExecuteIoWrite - 0x00000514, 0x00000002, 0x00000002
> > S3BootScriptWidthUint32 - 0x00000514 (0x00000000)
> > S3BootScriptWidthUint32 - 0x00000518 (0x18F0E47F)
> 
> The Boot Script passes the DMA command to QEMU, by writing the address
> of the command buffer to IO ports 0x514 and 0x518, in BE byte order.
> 
> > ExecuteBootScript - 7E7A704B
> > EFI_BOOT_SCRIPT_MEM_POLL_OPCODE
> > BootScriptExecuteMemPoll - 0x7FE4F018, 0x00000000FFFFFFFF, 0x0000000000000000
> > S3BootScriptWidthUint32 - 0x7FE4F018
> > ExecuteBootScript - 7E7A7072
> 
> This waits until the DMA command succeeds (reading back the Control
> field continuously until it reads as zero).
> 
> > EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE
> > BootScriptExecuteMemoryWrite - 0x7FE4F018, 0x00000018, 0x00000000
> 
> This is another DMA access command for fw_cfg, prepared in the same
> pre-reserved buffer. This time
> 
> > S3BootScriptWidthUint8 - 0x7FE4F018 (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F019 (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F01A (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F01B (0x10)
> 
> we request a write operation,
> 
> > S3BootScriptWidthUint8 - 0x7FE4F01C (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F01D (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F01E (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F01F (0x08)
> 
> with a length of 8 bytes (big endian), matching the pointer size,
> 
> > S3BootScriptWidthUint8 - 0x7FE4F020 (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F021 (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F022 (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F023 (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F024 (0x7F)
> > S3BootScriptWidthUint8 - 0x7FE4F025 (0xE4)
> > S3BootScriptWidthUint8 - 0x7FE4F026 (0xF0)
> > S3BootScriptWidthUint8 - 0x7FE4F027 (0x28)
> 
> the data to transfer is located at 0x7FE4F028 (just below, tacked to the
> command buffer itself),
> 
> > S3BootScriptWidthUint8 - 0x7FE4F028 (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F029 (0xC0)
> > S3BootScriptWidthUint8 - 0x7FE4F02A (0xE5)
> > S3BootScriptWidthUint8 - 0x7FE4F02B (0x7F)
> > S3BootScriptWidthUint8 - 0x7FE4F02C (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F02D (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F02E (0x00)
> > S3BootScriptWidthUint8 - 0x7FE4F02F (0x00)
> 
> and the data to write is the original allocation address of the blob
> (0x7fe5c000).
> 
> > ExecuteBootScript - 7E7A709D
> > EFI_BOOT_SCRIPT_IO_WRITE_OPCODE
> > BootScriptExecuteIoWrite - 0x00000514, 0x00000002, 0x00000002
> > S3BootScriptWidthUint32 - 0x00000514 (0x00000000)
> > S3BootScriptWidthUint32 - 0x00000518 (0x18F0E47F)
> > ExecuteBootScript - 7E7A70B8
> > EFI_BOOT_SCRIPT_MEM_POLL_OPCODE
> > BootScriptExecuteMemPoll - 0x7FE4F018, 0x00000000FFFFFFFF, 0x0000000000000000
> > S3BootScriptWidthUint32 - 0x7FE4F018
> > ExecuteBootScript - 7E7A70DF
> 
> Same story as above: fire off the transfer and wait until it completes.
> 
> > EFI_BOOT_SCRIPT_INFORMATION_OPCODE
> > BootScriptExecuteInformation - 0x7E7A70E6
> > BootScriptInformation: DE AD BE EF
> > ExecuteBootScript - 7E7A70EA
> > S3_BOOT_SCRIPT_LIB_TERMINATE_OPCODE
> > S3BootScriptDone - Success
> > [...]
> 
> The DEADBEEF informational (no-op) opcode is something that OVMF appends
> to the very end for hysterical raisins.
> 
> (9) Okay, so the guest is now resumed and running, let's interrupt it in
> gdb again, and check the contents of address blob again (we know the
> address of the address blob from step (6)):
> 
> > ^C
> > Program received signal SIGINT, Interrupt.
> > 0x00007f2bbf1d1ebf in ppoll () from /lib64/libc.so.6
> > (gdb) print /x *(uint64_t*)0x7f2bd03c37b0
> > $6 = 0x7fe5c000
> 
> Et voila.
> 
> (10) I detached gdb from QEMU, and issued the following monitor command:
> 
> > $ virsh qemu-monitor-command ovmf.rhel7 --hmp 'info vm-generation-id'
> > 00112233-4455-6677-8899-aabbccddeeff
> 
> (11) I also booted a Windows Server 2012 R2 guest (Q35, broadcast SMI
> enabled) with a similar vmgenid device/parameter. According to Device
> Manager | System devices, "Microsoft Hyper-V Generation Counter" is
> working properly.
> 
> I also tested S3 briefly, it worked okay. (I mentioned the SMI broadcast
> above because for that, OVMF generates an independent S3 Boot Script
> fragment.)
> 
> 
> I'll let someone else test live migration.
> 
> For patches #1, #3, #4 and #5:
> 
> Tested-by: Laszlo Ersek <lersek@redhat.com>
> 
> I'll soon post the OVMF patches.
> 
> Thanks!
> Laszlo


How do you feel about Igor's request to change WRITE_POINTER to add
offset in there, so guest can pass in the address of GUID and
not start of table? Would that be a lot of work to add?

-- 
MST

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID
  2017-02-15 20:09   ` Michael S. Tsirkin
@ 2017-02-15 20:15     ` Ben Warren
  2017-02-15 20:52     ` Laszlo Ersek
  1 sibling, 0 replies; 62+ messages in thread
From: Ben Warren @ 2017-02-15 20:15 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: Laszlo Ersek, qemu-devel, imammedo

[-- Attachment #1: Type: text/plain, Size: 18169 bytes --]


> On Feb 15, 2017, at 12:09 PM, Michael S. Tsirkin <mst@redhat.com> wrote:
> 
> On Wed, Feb 15, 2017 at 08:47:48PM +0100, Laszlo Ersek wrote:
>> On 02/15/17 07:15, ben@skyportsystems.com wrote:
>>> From: Ben Warren <ben@skyportsystems.com>
>>> 
>>> This patch set adds support for passing a GUID to Windows guests.  It
>>> is a re-implementation of previous patch sets written by Igor Mammedov
>>> et al, but this time passing the GUID data as a fw_cfg blob.
>>> 
>>> This patch set has dependencies on new guest functionality, in
>>> particular the support for a new linker-loader command and the ability
>>> to write back data to QEMU over a DMA link.  Work is in flight in both
>>> SeaBIOS and OVMF to support this.
>>> 
>>> v5->v6:
>>>    - Rebased to top of tree.
>>>    - Changed device from sysbus to a simple device.  This removed the need for
>>>      adding dynamic sysbus support to pc_piix boards.
>>>    - Removed patch that introduced QWORD patching of AML.
>>>    - Removed ability to set GUID via QMP/HMP.
>>>    - Improved comments/documentation in code.
>> 
>> So here's my testing with a RHEL-7 guest:
>> 
>> (1) The command line option passed to QEMU is
>> 
>>  -device vmgenid,guid=00112233-4455-6677-8899-AABBCCDDEEFF
>> 
>> This is the example GUID provided in the SMBIOS spec v3.0.0 (DSP0134),
>> section 7.2.1 "System -- UUID". (SMBIOS is only relevant here because it
>> codifies the fact that Microsoft consumes UUID in little-endian order.)
>> The expected representation, according to the SMBIOS spec, is
>> 
>>  33 22 11 00 55 44 77 66 88 99 AA BB CC DD EE FF
>> 
>> (2) Here's an excerpt from the OVMF log:
>> 
>>> ProcessCmdAllocate: File="etc/vmgenid_guid" Alignment=0x1000 Zone=1 Size=0x1000 Address=0x7FE5C000
>> 
>> This is where "etc/vmgenid_guid" is allocated and downloaded, the
>> allocation address is 0x7FE5C000.
>> 
>>> Select Item: 0x19
>>> Select Item: 0x22
>>> ProcessCmdAllocate: File="etc/acpi/tables" Alignment=0x40 Zone=1 Size=0x20000 Address=0x7E7AB000
>>> ProcessCmdAddChecksum: File="etc/acpi/tables" ResultOffset=0x49 Start=0x40 Length=0x1403
>>> ProcessCmdAddPointer: PointerFile="etc/acpi/tables" PointeeFile="etc/acpi/tables" PointerOffset=0x1467 PointerSize=4
>>> ProcessCmdAddPointer: PointerFile="etc/acpi/tables" PointeeFile="etc/acpi/tables" PointerOffset=0x146B PointerSize=4
>>> ProcessCmdAddChecksum: File="etc/acpi/tables" ResultOffset=0x144C Start=0x1443 Length=0x74
>>> ProcessCmdAddChecksum: File="etc/acpi/tables" ResultOffset=0x14C0 Start=0x14B7 Length=0x80
>>> Select Item: 0x19
>>> SaveCondensedWritePointerToS3Context: 0x002B/[0x00000000+8] := 0x7FE5C000 (0)
>> 
>> This is where OVMF stashes the WRITE_POINTER command in "condensed"
>> form, for S3. The fw_cfg selector value is 0x2B (for the fw_cfg file to
>> be rewritten), the pointer is located at offset 0, has size 0, and the
>> value to assign is 0x7FE5C000. And, this is #0 of the saved / condensed
>> WRITE_POINTER commands.
>> 
>>> Select Item: 0x2B
>>> ProcessCmdWritePointer: PointerFile="etc/vmgenid_addr" PointeeFile="etc/vmgenid_guid" PointerOffset=0x0 PointerSize=8
>> 
>> This is where the WRITE_POINTER command is actually executed, during
>> normal boot.
>> 
>>> ProcessCmdAddPointer: PointerFile="etc/acpi/tables" PointeeFile="etc/vmgenid_guid" PointerOffset=0x1561 PointerSize=4
>> 
>> This is where we link "etc/vmgenid_guid" into VGIA.
>> 
>>> ProcessCmdAddChecksum: File="etc/acpi/tables" ResultOffset=0x1540 Start=0x1537 Length=0xCA
>>> ProcessCmdAddPointer: PointerFile="etc/acpi/tables" PointeeFile="etc/acpi/tables" PointerOffset=0x1625 PointerSize=4
>>> ProcessCmdAddPointer: PointerFile="etc/acpi/tables" PointeeFile="etc/acpi/tables" PointerOffset=0x1629 PointerSize=4
>>> ProcessCmdAddPointer: PointerFile="etc/acpi/tables" PointeeFile="etc/acpi/tables" PointerOffset=0x162D PointerSize=4
>>> ProcessCmdAddChecksum: File="etc/acpi/tables" ResultOffset=0x160A Start=0x1601 Length=0x30
>>> ProcessCmdAddPointer: PointerFile="etc/acpi/rsdp" PointeeFile="etc/acpi/tables" PointerOffset=0x10 PointerSize=4
>>> ProcessCmdAddChecksum: File="etc/acpi/rsdp" ResultOffset=0x8 Start=0x0 Length=0x24
>>> InstallQemuFwCfgTables: unknown loader command: 0x0
>>> InstallQemuFwCfgTables: unknown loader command: 0x0
>>> InstallQemuFwCfgTables: unknown loader command: 0x0
>>> InstallQemuFwCfgTables: unknown loader command: 0x0
>>> InstallQemuFwCfgTables: unknown loader command: 0x0
>>> InstallQemuFwCfgTables: unknown loader command: 0x0
>>> InstallQemuFwCfgTables: unknown loader command: 0x0
>>> InstallQemuFwCfgTables: unknown loader command: 0x0
>>> InstallQemuFwCfgTables: unknown loader command: 0x0
>>> InstallQemuFwCfgTables: unknown loader command: 0x0
>>> InstallQemuFwCfgTables: unknown loader command: 0x0
>>> InstallQemuFwCfgTables: unknown loader command: 0x0
>>> InstallQemuFwCfgTables: unknown loader command: 0x0
>>> InstallQemuFwCfgTables: unknown loader command: 0x0
>>> InstallQemuFwCfgTables: unknown loader command: 0x0
>>> Process2ndPassCmdAddPointer: checking for ACPI header in "etc/acpi/tables" at 0x7E7AB000 (remaining: 0x20000): found "FACS" size 0x40
>>> Process2ndPassCmdAddPointer: checking for ACPI header in "etc/acpi/tables" at 0x7E7AB040 (remaining: 0x1FFC0): found "DSDT" size 0x1403
>>> Process2ndPassCmdAddPointer: checking for ACPI header in "etc/vmgenid_guid" at 0x7FE5C000 (remaining: 0x1000): not found; marking fw_cfg blob as opaque
>> 
>> This is where the OVMF SDT Header Probe Suppressor does its job. (NB,
>> the "opaque marking" has happened already in ProcessCmdWritePointer()
>> too, above.)
>> 
>>> Process2ndPassCmdAddPointer: checking for ACPI header in "etc/acpi/tables" at 0x7E7AC443 (remaining: 0x1EBBD): found "FACP" size 0x74
>>> Process2ndPassCmdAddPointer: checking for ACPI header in "etc/acpi/tables" at 0x7E7AC4B7 (remaining: 0x1EB49): found "APIC" size 0x80
>>> Process2ndPassCmdAddPointer: checking for ACPI header in "etc/acpi/tables" at 0x7E7AC537 (remaining: 0x1EAC9): found "SSDT" size 0xCA
>>> Process2ndPassCmdAddPointer: checking for ACPI header in "etc/acpi/tables" at 0x7E7AC601 (remaining: 0x1E9FF): found "RSDT" size 0x30
>>> TransferS3ContextToBootScript: boot script fragment saved, ScratchBuffer=7FE4F018
>> 
>> This is where the WRITE_POINTER commands, stashed earlier in condensed
>> form, are translated to S3 Boot Script opcodes.
>> 
>>> InstallQemuFwCfgTables: installed 5 tables
>> 
>> Such as: FACS, DSDT, FACP, APIC, SSDT. OVMF recognizes RSDT and ignores
>> it (it's handled by edk2 automatically).
>> 
>>> InstallQemuFwCfgTables: freeing "etc/acpi/rsdp"
>>> InstallQemuFwCfgTables: freeing "etc/acpi/tables"
>> 
>> OVMF sees that the above two blobs have not been marked as "opaque" --
>> they only contained ACPI tables, judged from the ADD_POINTER commands
>> that pointed into them. So these two blobs are freed.
>> 
>> Note that "etc/vmgenid_guid" is not freed.
>> 
>> So, from the firmware log, everything looks OK.
>> 
>> (3) I dumped the SSDT in the RHEL-7 guest:
>> 
>>> /*
>>> * Intel ACPI Component Architecture
>>> * AML/ASL+ Disassembler version 20160527-64
>>> * Copyright (c) 2000 - 2016 Intel Corporation
>>> *
>>> * Disassembling to symbolic ASL+ operators
>>> *
>>> * Disassembly of ssdt.dat, Wed Feb 15 19:21:11 2017
>>> *
>>> * Original Table Header:
>>> *     Signature        "SSDT"
>>> *     Length           0x000000CA (202)
>>> *     Revision         0x01
>>> *     Checksum         0x1D
>>> *     OEM ID           "BOCHS "
>>> *     OEM Table ID     "VMGENID"
>>> *     OEM Revision     0x00000001 (1)
>>> *     Compiler ID      "BXPC"
>>> *     Compiler Version 0x00000001 (1)
>>> */
>>> DefinitionBlock ("", "SSDT", 1, "BOCHS ", "VMGENID", 0x00000001)
>>> {
>>>    Name (VGIA, 0x7FE5C000)
>> 
>> Note that the value matches the value logged by the firmware in (2).
>> 
>>>    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) {}
>>>                Local0 [Zero] = (VGIA + 0x28)
>>>                Local0 [One] = Zero
>>>                Return (Local0)
>>>            }
>>>        }
>>>    }
>>> 
>>>    Method (\_GPE._E05, 0, NotSerialized)  // _Exx: Edge-Triggered GPE
>>>    {
>>>        Notify (\_SB.VGEN, 0x80) // Status Change
>>>    }
>>> }
>> 
>> Looks good and matches the documentation.
>> 
>> (4) To be sure, I checked the address against the guest dmesg, which
>> contains a dump of the UEFI memory map:
>> 
>>> [    0.000000] efi: mem52: type=10, attr=0xf, range=[0x000000007fe5a000-0x000000007fe5e000) (0MB)
>> 
>> The page (4096 bytes) at 0x7FE5C000 falls into this range. Type=10 means
>> EfiACPIMemoryNVS.
>> 
>> (5) At this point I dumped the guest RAM with the dump-guest-memory
>> monitor command, opened it with "crash", and listed it:
>> 
>>> crash> rd -p -8 0x7FE5C000 0x40
>>>        7fe5c000:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
>>>        7fe5c010:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
>>>        7fe5c020:  00 00 00 00 00 00 00 00 33 22 11 00 55 44 77 66   ........3"..UDwf
>>>        7fe5c030:  88 99 aa bb cc dd ee ff 00 00 00 00 00 00 00 00   ................
>> 
>> We can see that the GUID starts at 0x7FE5C000 + 0x28, and also that the
>> byte-level representation matches the little endian one given in (1).
>> 
>> This proves that the initial blob download worked fine.
>> 
>> (6) Here I attached "gdb" to QEMU, set a breakpoint on
>> vmgenid_handle_reset(), allowed the inferior process to continue
>> execution.
>> 
>> Then I suspended and resumed the guest (ACPI S3). The breakpoint was hit
>> during resume:
>> 
>>> Breakpoint 1, vmgenid_handle_reset (opaque=0x7f2bd03c36e0) at .../hw/acpi/vmgenid.c:205
>>> 205         VmGenIdState *vms = VMGENID(opaque);
>> 
>> First of all, before allowing QEMU to zero out the address blob, I
>> listed the address and the contents of the address blob (here exploiting
>> that my host is also little endian):
>> 
>>> (gdb) print (void*)vms->vmgenid_addr_le
>>> $2 = (void *) 0x7f2bd03c37b0
>> 
>>> (gdb) print /x *(uint64_t*)vms->vmgenid_addr_le
>>> $4 = 0x7fe5c000
>> 
>> This proves that QEMU has the right address, matching the firmware log
>> from (2), and the ACPI dump from (3).
>> 
>> (7) At this point I allowed the inferior to proceed a bit:
>> 
>>> (gdb) n
>>> 207         memset(vms->vmgenid_addr_le, 0, ARRAY_SIZE(vms->vmgenid_addr_le));
>>> (gdb) n
>>> 208     }
>> 
>> I verified that the blob was zeroed:
>> 
>>> (gdb) print /x *(uint64_t*)vms->vmgenid_addr_le
>>> $5 = 0x0
>> 
>> then allowed the inferior to run free.
>> 
>>> (gdb) cont
>>> Continuing.
>> 
>> (8) New messages appeared in the firmware log:
>> 
>>> S3ResumeExecuteBootScript()
>>> PeiS3ResumeState - 7FF92B18
>>> transfer control to Standalone Boot Script Executor
>>> S3BootScriptExecute:
>>> TableHeader - 0x7E7A7000
>>> TableHeader.Version - 0x0001
>>> TableHeader.TableLength - 0x000000ED
>>> ExecuteBootScript - 7E7A700D
>>> EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE
>>> BootScriptExecuteMemoryWrite - 0x7FE4F018, 0x00000010, 0x00000000
>> 
>> Here the ACPI S3 Boot Script, prepared in
>> TransferS3ContextToBootScript() -- see (2) -- creates a DMA access
>> command for fw_cfg. The DMA access command is written to pre-reserved
>> memory (see "ScratchBuffer" above).
>> 
>>> S3BootScriptWidthUint8 - 0x7FE4F018 (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F019 (0x2B)
>> 
>> The fw_cfg selector is 0x2B. (See under (2).)
>> 
>>> S3BootScriptWidthUint8 - 0x7FE4F01A (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F01B (0x0C)
>> 
>> This is a combined select+skip operation.
>> 
>>> S3BootScriptWidthUint8 - 0x7FE4F01C (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F01D (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F01E (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F01F (0x00)
>> 
>> The skip size is 0 bytes.
>> 
>>> S3BootScriptWidthUint8 - 0x7FE4F020 (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F021 (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F022 (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F023 (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F024 (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F025 (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F026 (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F027 (0x00)
>> 
>> The address is irrelevant for skip, so it's just nuleld.
>> 
>>> ExecuteBootScript - 7E7A7030
>>> EFI_BOOT_SCRIPT_IO_WRITE_OPCODE
>>> BootScriptExecuteIoWrite - 0x00000514, 0x00000002, 0x00000002
>>> S3BootScriptWidthUint32 - 0x00000514 (0x00000000)
>>> S3BootScriptWidthUint32 - 0x00000518 (0x18F0E47F)
>> 
>> The Boot Script passes the DMA command to QEMU, by writing the address
>> of the command buffer to IO ports 0x514 and 0x518, in BE byte order.
>> 
>>> ExecuteBootScript - 7E7A704B
>>> EFI_BOOT_SCRIPT_MEM_POLL_OPCODE
>>> BootScriptExecuteMemPoll - 0x7FE4F018, 0x00000000FFFFFFFF, 0x0000000000000000
>>> S3BootScriptWidthUint32 - 0x7FE4F018
>>> ExecuteBootScript - 7E7A7072
>> 
>> This waits until the DMA command succeeds (reading back the Control
>> field continuously until it reads as zero).
>> 
>>> EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE
>>> BootScriptExecuteMemoryWrite - 0x7FE4F018, 0x00000018, 0x00000000
>> 
>> This is another DMA access command for fw_cfg, prepared in the same
>> pre-reserved buffer. This time
>> 
>>> S3BootScriptWidthUint8 - 0x7FE4F018 (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F019 (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F01A (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F01B (0x10)
>> 
>> we request a write operation,
>> 
>>> S3BootScriptWidthUint8 - 0x7FE4F01C (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F01D (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F01E (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F01F (0x08)
>> 
>> with a length of 8 bytes (big endian), matching the pointer size,
>> 
>>> S3BootScriptWidthUint8 - 0x7FE4F020 (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F021 (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F022 (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F023 (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F024 (0x7F)
>>> S3BootScriptWidthUint8 - 0x7FE4F025 (0xE4)
>>> S3BootScriptWidthUint8 - 0x7FE4F026 (0xF0)
>>> S3BootScriptWidthUint8 - 0x7FE4F027 (0x28)
>> 
>> the data to transfer is located at 0x7FE4F028 (just below, tacked to the
>> command buffer itself),
>> 
>>> S3BootScriptWidthUint8 - 0x7FE4F028 (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F029 (0xC0)
>>> S3BootScriptWidthUint8 - 0x7FE4F02A (0xE5)
>>> S3BootScriptWidthUint8 - 0x7FE4F02B (0x7F)
>>> S3BootScriptWidthUint8 - 0x7FE4F02C (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F02D (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F02E (0x00)
>>> S3BootScriptWidthUint8 - 0x7FE4F02F (0x00)
>> 
>> and the data to write is the original allocation address of the blob
>> (0x7fe5c000).
>> 
>>> ExecuteBootScript - 7E7A709D
>>> EFI_BOOT_SCRIPT_IO_WRITE_OPCODE
>>> BootScriptExecuteIoWrite - 0x00000514, 0x00000002, 0x00000002
>>> S3BootScriptWidthUint32 - 0x00000514 (0x00000000)
>>> S3BootScriptWidthUint32 - 0x00000518 (0x18F0E47F)
>>> ExecuteBootScript - 7E7A70B8
>>> EFI_BOOT_SCRIPT_MEM_POLL_OPCODE
>>> BootScriptExecuteMemPoll - 0x7FE4F018, 0x00000000FFFFFFFF, 0x0000000000000000
>>> S3BootScriptWidthUint32 - 0x7FE4F018
>>> ExecuteBootScript - 7E7A70DF
>> 
>> Same story as above: fire off the transfer and wait until it completes.
>> 
>>> EFI_BOOT_SCRIPT_INFORMATION_OPCODE
>>> BootScriptExecuteInformation - 0x7E7A70E6
>>> BootScriptInformation: DE AD BE EF
>>> ExecuteBootScript - 7E7A70EA
>>> S3_BOOT_SCRIPT_LIB_TERMINATE_OPCODE
>>> S3BootScriptDone - Success
>>> [...]
>> 
>> The DEADBEEF informational (no-op) opcode is something that OVMF appends
>> to the very end for hysterical raisins.
>> 
>> (9) Okay, so the guest is now resumed and running, let's interrupt it in
>> gdb again, and check the contents of address blob again (we know the
>> address of the address blob from step (6)):
>> 
>>> ^C
>>> Program received signal SIGINT, Interrupt.
>>> 0x00007f2bbf1d1ebf in ppoll () from /lib64/libc.so.6
>>> (gdb) print /x *(uint64_t*)0x7f2bd03c37b0
>>> $6 = 0x7fe5c000
>> 
>> Et voila.
>> 
>> (10) I detached gdb from QEMU, and issued the following monitor command:
>> 
>>> $ virsh qemu-monitor-command ovmf.rhel7 --hmp 'info vm-generation-id'
>>> 00112233-4455-6677-8899-aabbccddeeff
>> 
>> (11) I also booted a Windows Server 2012 R2 guest (Q35, broadcast SMI
>> enabled) with a similar vmgenid device/parameter. According to Device
>> Manager | System devices, "Microsoft Hyper-V Generation Counter" is
>> working properly.
>> 
>> I also tested S3 briefly, it worked okay. (I mentioned the SMI broadcast
>> above because for that, OVMF generates an independent S3 Boot Script
>> fragment.)
>> 
>> 
>> I'll let someone else test live migration.
>> 
>> For patches #1, #3, #4 and #5:
>> 
>> Tested-by: Laszlo Ersek <lersek@redhat.com>
>> 
>> I'll soon post the OVMF patches.
>> 
>> Thanks!
>> Laszlo
> 
> 
> How do you feel about Igor's request to change WRITE_POINTER to add
> offset in there, so guest can pass in the address of GUID and
> not start of table? Would that be a lot of work to add?
> 
I know you’re asking Laszlo, but hopefully the answer is “OK”.  I’m finished making those changes to QEMU and am coding up SeaBIOS right now. 
> -- 
> MST


[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 3583 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID
  2017-02-15 20:09   ` Michael S. Tsirkin
  2017-02-15 20:15     ` Ben Warren
@ 2017-02-15 20:52     ` Laszlo Ersek
  2017-02-16  6:10       ` Ben Warren
  2017-02-16 12:08       ` Igor Mammedov
  1 sibling, 2 replies; 62+ messages in thread
From: Laszlo Ersek @ 2017-02-15 20:52 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: ben, qemu-devel, imammedo

On 02/15/17 21:09, Michael S. Tsirkin wrote:
> On Wed, Feb 15, 2017 at 08:47:48PM +0100, Laszlo Ersek wrote:

[snip]

>> For patches #1, #3, #4 and #5:
>>
>> Tested-by: Laszlo Ersek <lersek@redhat.com>
>>
>> I'll soon post the OVMF patches.
>>
>> Thanks!
>> Laszlo
> 
> 
> How do you feel about Igor's request to change WRITE_POINTER to add
> offset in there, so guest can pass in the address of GUID and
> not start of table? Would that be a lot of work to add?

I think it's doable in practice: simply add a constant from the command
itself, for passing the value back to QEMU, and also for saving the
fw_cfg write commend for S3 resume time.

But, I disagree with it from a design POV.

Igor's point is:

> Math complicates QEMU code though and not only QMEMU but AML code as
> well.

As I understand it, the goal is to push the addition to the firmware
(which is "one place"), rather than having to implement it twice in
QEMU, i.e., in two places ((a) native QEMU logic, (b) AML generator).

Here's my counter-argument:

(a) As I mentioned earlier, assume a complex communication structure
between the guest OS and QEMU. Currently our shared structure consists
of a single field (the GUID), but next time it might contain several fields.

For such a multi-field shared structure, QEMU will have to do manual
offsetting into the guest RAM anyway, for accessing fields F1, F2, and
F3. We will not create three separate WRITE_POINTER commands and let the
firmware calculate and return the absolute GPAs of the fields F1, F2 and
F3. Instead, there will be one WRITE_POINTER command, and QEMU will do
the offsetting manually, minimally for fields F2 and F3.

"src_offset" looks tempting now only because we have a shared structure
with only one field, the GUID at offset 40 decimal.

(b) Regarding the runtime addition in the AML code:

As discussed before, the main reason *now*, for not pointing VGIA (and
other named integer objects) with ADD_POINTER commands directly to
"meaningful" fields, is that OVMF probes the targets of ADD_POINTER
commands for patterns that look like ACPI table headers. And, for the
time being, we want to suppress any mis-recognitions by prepending some
padding.

Igor was right to dislike this, and we agreed that *down the road* we
should add allocation flags, or further allocation commands, to supplant
this kind of heuristics in OVMF. But:

- we don't have time to do it now, plus

- please observe that the runtime addition in AML relates to the
  ADD_POINTER and the ALLOCATE commands. It does not relate to
  WRITE_POINTER at all.

  Whatever we change on WRITE_POINTER will do nothing for suppressing
  OVMF's table header probing -- because that is tied to ADD_POINTER
  --, therefore WRITE_POINTER tweaks cannot eliminate the "need to add"
  in AML.


In summary, I think the proposed WRITE_POINTER modification is
implementable, but I think it will not pay off, because:

(a) for QEMU logic, it will not prove useful as soon as we have a
multi-field shared structure (QEMU will have to add field offsets anyway),

(b) and for eliminating the AML addition (which is a consequence of the
current ADD_POINTER handling in OVMF), it does nothing.

Thanks
Laszlo

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15 18:44                         ` Ben Warren
@ 2017-02-15 21:09                           ` Michael S. Tsirkin
  0 siblings, 0 replies; 62+ messages in thread
From: Michael S. Tsirkin @ 2017-02-15 21:09 UTC (permalink / raw)
  To: Ben Warren; +Cc: Igor Mammedov, Laszlo Ersek, qemu-devel

On Wed, Feb 15, 2017 at 10:44:05AM -0800, Ben Warren wrote:
> 
>     On Feb 15, 2017, at 10:35 AM, Igor Mammedov <imammedo@redhat.com> wrote:
> 
>     On Wed, 15 Feb 2017 10:14:55 -0800
>     Ben Warren <ben@skyportsystems.com> wrote:
> 
> 
>             On Feb 15, 2017, at 10:06 AM, Michael S. Tsirkin <mst@redhat.com>
>             wrote:
> 
>             On Wed, Feb 15, 2017 at 09:54:08AM -0800, Ben Warren wrote:  
> 
> 
>                   On Feb 15, 2017, at 9:43 AM, Igor Mammedov <
>                 imammedo@redhat.com> wrote:
> 
>                   On Wed, 15 Feb 2017 18:39:06 +0200
>                   "Michael S. Tsirkin" <mst@redhat.com> wrote:
> 
> 
>                       On Wed, Feb 15, 2017 at 04:56:02PM +0100, Igor Mammedov
>                 wrote:
> 
>                           On Wed, 15 Feb 2017 17:30:00 +0200
>                           "Michael S. Tsirkin" <mst@redhat.com> wrote:
> 
> 
>                               On Wed, Feb 15, 2017 at 04:22:25PM +0100, Igor
>                 Mammedov wrote:
> 
> 
>                                   On Wed, 15 Feb 2017 15:13:20 +0100
>                                   Laszlo Ersek <lersek@redhat.com> wrote:
> 
> 
>                                       Commenting under Igor's reply for
>                 simplicity
> 
>                                       On 02/15/17 11:57, Igor Mammedov wrote:
>                    
> 
>                                           On Tue, 14 Feb 2017 22:15:43 -0800
>                                           ben@skyportsystems.com wrote:
> 
> 
>                                               From: Ben Warren <
>                 ben@skyportsystems.com>
> 
>                                               This is similar to the existing
>                 'add pointer'
>                                               functionality, but instead
>                                               of instructing the guest (BIOS or
>                 UEFI) to
>                                               patch memory, it instructs
>                                               the guest to write the pointer
>                 back to QEMU via
>                                               a writeable fw_cfg file.
> 
>                                               Signed-off-by: Ben Warren <  
>                                               ben@skyportsystems.com>  
>                                               ---
>                                               hw/acpi/bios-linker-loader.c
>                         | 58
>                                               ++++++++++++++++++++++++++++++++++--
>                                               include/hw/acpi/
>                 bios-linker-loader.h |  6 ++++
>                                               2 files changed, 61 insertions
>                 (+), 3 deletions
>                                               (-)
> 
>                                               diff --git a/hw/acpi/
>                 bios-linker-loader.c b/hw/
>                                               acpi/bios-linker-loader.c
>                                               index d963ebe..5030cf1 100644
>                                               --- a/hw/acpi/
>                 bios-linker-loader.c
>                                               +++ b/hw/acpi/
>                 bios-linker-loader.c
>                                               @@ -78,6 +78,19 @@ 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 the table
>                                               +         * originating from
>                 @src_file. 1,2,4
>                                               or 8 byte unsigned
>                                               +         * addition is used
>                 depending on
>                                               @wr_pointer.size.
>                                               +         */      
> 
> 
>                                       The words "adding" and "addition" are
>                 causing confusion
>                                       here.
> 
>                                       In all of the previous discussion,
>                 *addition* was out
>                                       of scope from
>                                       WRITE_POINTER. Again, the firmware is
>                 specifically not
>                                       required to
>                                       *read* any part of the fw_cfg blob
>                 identified by
>                                       "dest_file".
> 
>                                       WRITE_POINTER instructs the firmware to
>                 return the
>                                       allocation address of
>                                       the downloaded "src_file" to QEMU. Any
>                 necessary
>                                       runtime subscripting
>                                       within "src_file" is to be handled by
>                 QEMU code
>                                       dynamically.
> 
>                                       For example, consider that "src_file" has
>                 *several*
>                                       fields that QEMU
>                                       wants to massage; in that case, indexing
>                 within QEMU
>                                       code with field
>                                       offsets is simply unavoidable.    
> 
>                                   what I don't like here is that this indexing
>                 would be
>                                   rather fragile
>                                   and has to be done in different parts of QEMU
>                 /device, AML
>                                   /.
> 
>                                   I'd prefer this helper function to have the
>                 same
>                                   @src_offset
>                                   behavior as ADD_POINTER where patched address
>                 could point
>                                   to
>                                   any part of src_file i.e. not just beginning.
>                    
> 
> 
> 
> 
>                                      /*
>                                       * COMMAND_ADD_POINTER - patch the table
>                 (originating
>                               from
>                                       * @dest_file) at @pointer.offset, by
>                 adding a pointer
>                               to the table
>                                       * originating from @src_file. 1,2,4 or 8
>                 byte unsigned
>                                       * addition is used depending on
>                 @pointer.size.
>                                       */
> 
>                               so the way ADD works is
>                               read at offset
>                               add table address
>                               write result at offset
> 
>                               in other words it is always beginning of table
>                 that is added.  
> 
>                           more exactly it's, read at 
>                            src_offset = *(dst_blob_ptr+dst_offset)
>                            *(dst_blob+dst_offset) = src_blob_ptr + src_offset
> 
> 
>                               Would the following be acceptable?
> 
> 
>                                       * COMMAND_WRITE_POINTER - update the
>                 fw_cfg file
>                               (originating from
>                                       * @dest_file) at @wr_pointer.offset, by
>                 writing a
>                               pointer to the table
>                                       * originating from @src_file. 1,2,4 or 8
>                 byte unsigned
>                               value
>                                       * is written depending on
>                 @wr_pointer.size.  
> 
>                           it looses 'adding' part of ADD_POINTER command which
>                 handles
>                           src_offset,
>                           however implementing adding part looks a bit
>                 complicated
>                           as patched blob (dst) is not in guest memory but in
>                 QEMU and
>                           on reset *(dst_blob+dst_offset) should be reset to
>                 src_offset.
>                           Considering dst file could be device specific memory
>                 (field/blob/
>                           whatever)
>                           it could be hard to track/notice proper reset
>                 behavior.
> 
>                           So now I'm not sure if src_offset is worth adding.  
> 
> 
>                       Right. Let's just do this math in QEMU if we have to.
> 
>                   Math complicates QEMU code though and not only QMEMU but AML
>                 code as well.
>                   Considering that we are adding a new command and don't have
>                 to keep
>                   any sort of compatibility we can pass src_offset as part
>                   of command instead of hiding it inside of dst_file.
>                   Something like this:
> 
>                          /*
>                           * COMMAND_WRITE_POINTER - write the fw_cfg file
>                 (originating from
>                           * @dest_file) at @wr_pointer.offset, by writing 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 offset;
>                   +            uint32_t dst_offset;
>                   +            uint32_t src_offset;
>                               uint8_t size;
>                          } wr_pointer;
> 
> 
>                 OK, this is easy enough to do and maybe we’ll have a use case
>                 in the future.
>                 I’ll make this change in v7  
> 
> 
> 
>             So if you do, you want to set it to VMGENID_GUID_OFFSET.
> 
> 
>         Oh, I was going to set it to 0 since that doesn’t require any other
>         changes (other than to SeaBIOS)

This is equivalent to not adding this field at all.
If we do add it we better have at least some users
set it to something non-zero otherwise we do not
really know whether it works.



>     it should be changed in following places:
> 
>        bios_linker_loader_write_pointer(linker,
>            VMGENID_ADDR_FW_CFG_FILE, 0, sizeof(uint64_t),
>     -        VMGENID_GUID_FW_CFG_FILE);
>     +        VMGENID_GUID_FW_CFG_FILE, VMGENID_GUID_OFFSET);
> 
>     ...
>     -            cpu_physical_memory_write(vmgenid_addr + VMGENID_GUID_OFFSET,
>     +            cpu_physical_memory_write(vmgenid_addr,
>                                          guid_le.data, sizeof(guid_le.data));
> 
> 
> OK, the more places I can get rid of this goofy offset the better.  Just to be
> clear, the address that gets patched into AML (via the add_pointer() call)
> remains at the beginning of /etc/vmgenid_guid, right?

It's up to you really but OVMF wants the beginning I think.

> 
> 
> 
> 
> 
> 
> 
> 
> 
> 
> 
>                                       (1) So, the above looks correct, but
>                 please replace
>                                       "adding" with
>                                       "storing", and "unsigned addition" with
>                 "store".
> 
>                                       Side point: the case for ADD_POINTER is
>                 different;
>                                       there we patch
>                                       several individual ACPI objects. The fact
>                 that I
>                                       requested explicit
>                                       addition within the ADDR method, as
>                 opposed to
>                                       pre-setting VGIA to a
>                                       nonzero offset, is an *incidental*
>                 limitation (coming
>                                       from the OVMF ACPI
>                                       SDT header probe suppressor), and we'll
>                 likely fix that
>                                       up later, with
>                                       ALLOCATE command hints or something like
>                 that. However,
>                                       in
>                                       WRITE_POINTER, asking for the exact
>                 allocation address
>                                       of "src_file" is
>                                       an *inherent* characteristic.
> 
>                                       For reference, this is the command's
>                 description from
>                                       the (not as yet
>                                       posted) OVMF series:
> 
>                                       // QemuLoaderCmdWritePointer: the bytes
>                 at
>                                       //
>                 [PointerOffset..PointerOffset+PointerSize) in the
>                                       writeable fw_cfg
>                                       // file PointerFile are to receive the
>                 absolute address
>                                       of PointeeFile,
>                                       // as allocated and downloaded by the
>                 firmware. Store
>                                       the base address
>                                       // of where PointeeFile's contents have
>                 been placed
>                                       (when
>                                       // QemuLoaderCmdAllocate has been
>                 executed for
>                                       PointeeFile) to this
>                                       // portion of PointerFile.
>                                       //
>                                       // This command is similar to
>                 QemuLoaderCmdAddPointer;
>                                       the difference is
>                                       // that the "pointer to patch" does not
>                 exist in
>                                       guest-physical address
>                                       // space, only in "fw_cfg file space". In
>                 addition, the
>                                       "pointer to
>                                       // patch" is not initialized by QEMU with
>                 a possibly
>                                       nonzero offset
>                                       // value: the base address of the memory
>                 allocated for
>                                       downloading
>                                       // PointeeFile shall not increment the
>                 pointer, but
>                                       overwrite it.
> 
>                                       In the last SeaBIOS patch series, namely
> 
>                                       [SeaBIOS] [PATCH v3 2/2] QEMU fw_cfg: Add
>                 command to
>                                       write back address
>                                                               of file
> 
>                                       function romfile_loader_write_pointer()
>                 implemented
>                                       just that plain
>                                       store (not an addition), and that was
>                 exactly right.
> 
>                                       Continuing:
> 
> 
>                                               +        struct {
>                                               +            char dest_file
>                                               [BIOS_LINKER_LOADER_FILESZ];
>                                               +            char src_file
>                                               [BIOS_LINKER_LOADER_FILESZ];
>                                               +            uint32_t offset;
>                                               +            uint8_t size;
>                                               +        } wr_pointer;
>                                               +
>                                                       /* padding */
>                                                       char pad[124];
>                                                   };
>                                               @@ -85,9 +98,10 @@ struct
>                 BiosLinkerLoaderEntry
>                                               {
>                                               typedef struct
>                 BiosLinkerLoaderEntry
>                                               BiosLinkerLoaderEntry;
> 
>                                               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_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 +292,41 @@ 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
>                                               + */
>                                               +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)      
> 
>                                           API is missing "src_offset" even
>                 though it's not
>                                           used in this series,
>                                           a patch on top to fix it up is ok for
>                 me as far as
>                                           Seabios/OVMF
>                                           counterpart can handle src_offset
>                 correctly from
>                                           starters.      
> 
> 
>                                       According to the above, it is the right
>                 thing not to
>                                       add "src_offset"
>                                       here. The documentation on the command is
>                 slightly
>                                       incorrect (and causes
>                                       confusion), but the helper function's
>                 signature and
>                                       comments are okay.
> 
> 
> 
> 
>                                               +{
>                                               +    BiosLinkerLoaderEntry entry;
>                                               +    const BiosLinkerFileEntry
>                 *source_file =
>                                               +        bios_linker_find_file
>                 (linker,
>                                               src_file);
>                                               +
>                                               +    assert(source_file);      
> 
> 
>                                       I wish we kept the following asserts from
>                                       bios_linker_loader_add_pointer():
> 
>                                          assert(dst_patched_offset < dst_file->
>                 blob->len);
>                                          assert(dst_patched_offset +
>                 dst_patched_size <=
>                                       dst_file->blob->len);
> 
>                                       Namely, just because the dst_file is
>                 never supposed to
>                                       be downloaded by
>                                       the firmware, it still remains a
>                 requirement that the
>                                       "dst file offset
>                                       range" that is to be rewritten *do fall*
>                 within the dst
>                                       file.
> 
>                                       Nonetheless, this is not critical. (OVMF
>                 at least
>                                       verifies it anyway.)
> 
>                                       Summary (from my side anyway): I feel
>                 that the
>                                       documentation of the new
>                                       command is very important. Please fix it
>                 up as
>                                       suggested under (1), in
>                                       v7. Regarding the asserts, I'll let you
>                 decide.
> 
>                                       With the documentation fixed up:
> 
>                                       Reviewed-by: Laszlo Ersek <
>                 lersek@redhat.com>
> 
>                                       (If you don't wish to post a v7, I'm also
>                 completely
>                                       fine if Michael or
>                                       someone else fixes up the docs as
>                 proposed in (1),
>                                       before committing the
>                                       patch.)
> 
>                                       Thanks!
>                                       Laszlo
> 
> 
>                                               +    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.offset =
>                 cpu_to_le32
>                                               (dst_patched_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);
>                                               +}
>                                               diff --git a/include/hw/acpi/
>                                               bios-linker-loader.h b/include/hw
>                 /acpi/
>                                               bios-linker-loader.h
>                                               index fa1e5d1..f9ba5d6 100644
>                                               --- a/include/hw/acpi/
>                 bios-linker-loader.h
>                                               +++ b/include/hw/acpi/
>                 bios-linker-loader.h
>                                               @@ -26,5 +26,11 @@ 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);
>                                               +
>                                               void bios_linker_loader_cleanup
>                 (BIOSLinker
>                                               *linker);
>                                               #endif        
> 
> 

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 6/7] tests: Move reusable ACPI macros into a new header file
  2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 6/7] tests: Move reusable ACPI macros into a new header file ben
  2017-02-15 12:54   ` Igor Mammedov
@ 2017-02-15 21:35   ` Eric Blake
  2017-02-15 21:58     ` Ben Warren
  1 sibling, 1 reply; 62+ messages in thread
From: Eric Blake @ 2017-02-15 21:35 UTC (permalink / raw)
  To: ben, qemu-devel; +Cc: imammedo, lersek, mst

[-- Attachment #1: Type: text/plain, Size: 911 bytes --]

On 02/15/2017 12:15 AM, ben@skyportsystems.com wrote:
> From: Ben Warren <ben@skyportsystems.com>
> 
> Also usable by upcoming VM Generation ID tests
> 
> Signed-off-by: Ben Warren <ben@skyportsystems.com>
> ---
>  tests/acpi-utils.h       | 75 ++++++++++++++++++++++++++++++++++++++++++++++++
>  tests/bios-tables-test.c | 72 +---------------------------------------------
>  2 files changed, 76 insertions(+), 71 deletions(-)
>  create mode 100644 tests/acpi-utils.h
> 
> diff --git a/tests/acpi-utils.h b/tests/acpi-utils.h
> new file mode 100644
> index 0000000..d5e5eff
> --- /dev/null
> +++ b/tests/acpi-utils.h
> @@ -0,0 +1,75 @@
> +#ifndef TEST_ACPI_UTILS_H
> +#define TEST_ACPI_UTILS_H

No copyright blurb? Also, does MAINTAINERS need an update to cover the
new file?

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 6/7] tests: Move reusable ACPI macros into a new header file
  2017-02-15 21:35   ` Eric Blake
@ 2017-02-15 21:58     ` Ben Warren
  2017-02-15 22:56       ` Eric Blake
  0 siblings, 1 reply; 62+ messages in thread
From: Ben Warren @ 2017-02-15 21:58 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Igor Mammedov, Laszlo Ersek, mst

[-- Attachment #1: Type: text/plain, Size: 1231 bytes --]


> On Feb 15, 2017, at 1:35 PM, Eric Blake <eblake@redhat.com> wrote:
> 
> On 02/15/2017 12:15 AM, ben@skyportsystems.com <mailto:ben@skyportsystems.com> wrote:
>> From: Ben Warren <ben@skyportsystems.com>
>> 
>> Also usable by upcoming VM Generation ID tests
>> 
>> Signed-off-by: Ben Warren <ben@skyportsystems.com>
>> ---
>> tests/acpi-utils.h       | 75 ++++++++++++++++++++++++++++++++++++++++++++++++
>> tests/bios-tables-test.c | 72 +---------------------------------------------
>> 2 files changed, 76 insertions(+), 71 deletions(-)
>> create mode 100644 tests/acpi-utils.h
>> 
>> diff --git a/tests/acpi-utils.h b/tests/acpi-utils.h
>> new file mode 100644
>> index 0000000..d5e5eff
>> --- /dev/null
>> +++ b/tests/acpi-utils.h
>> @@ -0,0 +1,75 @@
>> +#ifndef TEST_ACPI_UTILS_H
>> +#define TEST_ACPI_UTILS_H
> 
> No copyright blurb? Also, does MAINTAINERS need an update to cover the
> new file?
> 
Sure, I didn’t realize the header files all have copyright headers.  As for MAINTAINERS, do you mean I should add a device entry for vmgenid?

thanks,
Ben
> -- 
> Eric Blake   eblake redhat com    +1-919-301-3266
> Libvirt virtualization library http://libvirt.org <http://libvirt.org/>

[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 3583 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 6/7] tests: Move reusable ACPI macros into a new header file
  2017-02-15 21:58     ` Ben Warren
@ 2017-02-15 22:56       ` Eric Blake
  2017-02-15 23:05         ` Ben Warren
  0 siblings, 1 reply; 62+ messages in thread
From: Eric Blake @ 2017-02-15 22:56 UTC (permalink / raw)
  To: Ben Warren; +Cc: qemu-devel, Igor Mammedov, Laszlo Ersek, mst

[-- Attachment #1: Type: text/plain, Size: 1402 bytes --]

On 02/15/2017 03:58 PM, Ben Warren wrote:

>> 
>> ---
>> tests/acpi-utils.h       | 75 ++++++++++++++++++++++++++++++++++++++++++++++++
>> tests/bios-tables-test.c | 72 +---------------------------------------------
>> 2 files changed, 76 insertions(+), 71 deletions(-)


>> No copyright blurb? Also, does MAINTAINERS need an update to cover the
>> new file?
>>
> Sure, I didn’t realize the header files all have copyright headers.  As for MAINTAINERS, do you mean I should add a device entry for vmgenid?

In this patch, you're just refactoring to a new tests/acpi-utils.h, so
I'd normally suggest adding it to the blurb that owns
tests/bios-tables-test.c - but as a pre-existing problem, that also has
no listed maintainer.  we're trying to ensure that all new added files
have something listed in MAINTAINERS, even if it is in a misc section
that only emails the list, although it's harder to say what maintainer
to use for existing files that you are merely touching, and failure to
list a maintainer is not (yet) a hard failure (although there have been
patches proposed to scripts/checkpatch.pl to tighten the rules).

A new section for vmgenid might not be a bad idea, especially it if
covers more files than just the one addition I noticed in this patch.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 6/7] tests: Move reusable ACPI macros into a new header file
  2017-02-15 22:56       ` Eric Blake
@ 2017-02-15 23:05         ` Ben Warren
  0 siblings, 0 replies; 62+ messages in thread
From: Ben Warren @ 2017-02-15 23:05 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Igor Mammedov, Laszlo Ersek, mst

[-- Attachment #1: Type: text/plain, Size: 1594 bytes --]


> On Feb 15, 2017, at 2:56 PM, Eric Blake <eblake@redhat.com> wrote:
> 
> On 02/15/2017 03:58 PM, Ben Warren wrote:
> 
>>> 
>>> ---
>>> tests/acpi-utils.h       | 75 ++++++++++++++++++++++++++++++++++++++++++++++++
>>> tests/bios-tables-test.c | 72 +---------------------------------------------
>>> 2 files changed, 76 insertions(+), 71 deletions(-)
> 
> 
>>> No copyright blurb? Also, does MAINTAINERS need an update to cover the
>>> new file?
>>> 
>> Sure, I didn’t realize the header files all have copyright headers.  As for MAINTAINERS, do you mean I should add a device entry for vmgenid?
> 
> In this patch, you're just refactoring to a new tests/acpi-utils.h, so
> I'd normally suggest adding it to the blurb that owns
> tests/bios-tables-test.c - but as a pre-existing problem, that also has
> no listed maintainer.  we're trying to ensure that all new added files
> have something listed in MAINTAINERS, even if it is in a misc section
> that only emails the list, although it's harder to say what maintainer
> to use for existing files that you are merely touching, and failure to
> list a maintainer is not (yet) a hard failure (although there have been
> patches proposed to scripts/checkpatch.pl to tighten the rules).
> 
> A new section for vmgenid might not be a bad idea, especially it if
> covers more files than just the one addition I noticed in this patch.
> 
Thank you for clarifying.  I’ll take care of it.
> -- 
> Eric Blake   eblake redhat com    +1-919-301-3266
> Libvirt virtualization library http://libvirt.org
> 
—Ben


[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 3583 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID
  2017-02-15 20:52     ` Laszlo Ersek
@ 2017-02-16  6:10       ` Ben Warren
  2017-02-16  9:36         ` Laszlo Ersek
  2017-02-16 12:08       ` Igor Mammedov
  1 sibling, 1 reply; 62+ messages in thread
From: Ben Warren @ 2017-02-16  6:10 UTC (permalink / raw)
  To: Laszlo Ersek; +Cc: Michael S. Tsirkin, qemu-devel, imammedo

[-- Attachment #1: Type: text/plain, Size: 3701 bytes --]


> On Feb 15, 2017, at 12:52 PM, Laszlo Ersek <lersek@redhat.com> wrote:
> 
> On 02/15/17 21:09, Michael S. Tsirkin wrote:
>> On Wed, Feb 15, 2017 at 08:47:48PM +0100, Laszlo Ersek wrote:
> 
> [snip]
> 
>>> For patches #1, #3, #4 and #5:
>>> 
>>> Tested-by: Laszlo Ersek <lersek@redhat.com>
>>> 
>>> I'll soon post the OVMF patches.
>>> 
>>> Thanks!
>>> Laszlo
>> 
>> 
>> How do you feel about Igor's request to change WRITE_POINTER to add
>> offset in there, so guest can pass in the address of GUID and
>> not start of table? Would that be a lot of work to add?
> 
> I think it's doable in practice: simply add a constant from the command
> itself, for passing the value back to QEMU, and also for saving the
> fw_cfg write commend for S3 resume time.
> 
> But, I disagree with it from a design POV.
> 
> Igor's point is:
> 
>> Math complicates QEMU code though and not only QMEMU but AML code as
>> well.
> 
> As I understand it, the goal is to push the addition to the firmware
> (which is "one place"), rather than having to implement it twice in
> QEMU, i.e., in two places ((a) native QEMU logic, (b) AML generator).
> 
> Here's my counter-argument:
> 
> (a) As I mentioned earlier, assume a complex communication structure
> between the guest OS and QEMU. Currently our shared structure consists
> of a single field (the GUID), but next time it might contain several fields.
> 
> For such a multi-field shared structure, QEMU will have to do manual
> offsetting into the guest RAM anyway, for accessing fields F1, F2, and
> F3. We will not create three separate WRITE_POINTER commands and let the
> firmware calculate and return the absolute GPAs of the fields F1, F2 and
> F3. Instead, there will be one WRITE_POINTER command, and QEMU will do
> the offsetting manually, minimally for fields F2 and F3.
> 
> "src_offset" looks tempting now only because we have a shared structure
> with only one field, the GUID at offset 40 decimal.
> 
> (b) Regarding the runtime addition in the AML code:
> 
> As discussed before, the main reason *now*, for not pointing VGIA (and
> other named integer objects) with ADD_POINTER commands directly to
> "meaningful" fields, is that OVMF probes the targets of ADD_POINTER
> commands for patterns that look like ACPI table headers. And, for the
> time being, we want to suppress any mis-recognitions by prepending some
> padding.
> 
> Igor was right to dislike this, and we agreed that *down the road* we
> should add allocation flags, or further allocation commands, to supplant
> this kind of heuristics in OVMF. But:
> 
> - we don't have time to do it now, plus
> 
> - please observe that the runtime addition in AML relates to the
>  ADD_POINTER and the ALLOCATE commands. It does not relate to
>  WRITE_POINTER at all.
> 
>  Whatever we change on WRITE_POINTER will do nothing for suppressing
>  OVMF's table header probing -- because that is tied to ADD_POINTER
>  --, therefore WRITE_POINTER tweaks cannot eliminate the "need to add"
>  in AML.
> 
> 
> In summary, I think the proposed WRITE_POINTER modification is
> implementable, but I think it will not pay off, because:
> 
> (a) for QEMU logic, it will not prove useful as soon as we have a
> multi-field shared structure (QEMU will have to add field offsets anyway),
> 
> (b) and for eliminating the AML addition (which is a consequence of the
> current ADD_POINTER handling in OVMF), it does nothing.
> 
OK Laszlo, in v7 (imminent)  I went ahead and implemented this src_offset.  If you are truly dead-set against it, it’s not very hard to remove.  To me it seems pretty harmless.

> Thanks
> Laszlo


[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 3583 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 3/7] ACPI: Add vmgenid blob storage to the build tables
  2017-02-15 14:30   ` Laszlo Ersek
@ 2017-02-16  6:11     ` Ben Warren
  0 siblings, 0 replies; 62+ messages in thread
From: Ben Warren @ 2017-02-16  6:11 UTC (permalink / raw)
  To: Laszlo Ersek; +Cc: qemu-devel, mst, imammedo

[-- Attachment #1: Type: text/plain, Size: 2305 bytes --]


> On Feb 15, 2017, at 6:30 AM, Laszlo Ersek <lersek@redhat.com> wrote:
> 
> On 02/15/17 07:15, ben@skyportsystems.com wrote:
>> From: Ben Warren <ben@skyportsystems.com>
>> 
>> This allows them to be centrally initialized and destroyed
>> 
>> The "AcpiBuildTables.vmgenid" array will be used to construct the
>> "etc/vmgenid" fw_cfg blob.
> 
> Trivial wart: the blob is now called "etc/vmgenid_guid".
> 
> If you send a v7, feel free to fix it up. Not critical.
> 
Fixed in v7
> My R-b stands.
> 
> Thanks!
> Laszlo
> 
>> Its contents will be linked into fw_cfg after being built on the
>> pc_machine_done() -> acpi_setup() -> acpi_build() call path, and dropped
>> without use on the subsequent, guest triggered, acpi_build_update() ->
>> acpi_build() call path.
>> 
>> Signed-off-by: Ben Warren <ben@skyportsystems.com>
>> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
>> ---
>> hw/acpi/aml-build.c         | 2 ++
>> include/hw/acpi/aml-build.h | 1 +
>> 2 files changed, 3 insertions(+)
>> 
>> diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c
>> index b2a1e40..c6f2032 100644
>> --- a/hw/acpi/aml-build.c
>> +++ b/hw/acpi/aml-build.c
>> @@ -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 */
>> diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h
>> index 559326c..00c21f1 100644
>> --- a/include/hw/acpi/aml-build.h
>> +++ b/include/hw/acpi/aml-build.h
>> @@ -210,6 +210,7 @@ struct AcpiBuildTables {
>>     GArray *table_data;
>>     GArray *rsdp;
>>     GArray *tcpalog;
>> +    GArray *vmgenid;
>>     BIOSLinker *linker;
>> } AcpiBuildTables;
>> 
>> 
> 


[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 3583 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 4/7] ACPI: Add Virtual Machine Generation ID support
  2017-02-15 15:24     ` Laszlo Ersek
  2017-02-15 16:07       ` Igor Mammedov
@ 2017-02-16  6:13       ` Ben Warren
  1 sibling, 0 replies; 62+ messages in thread
From: Ben Warren @ 2017-02-16  6:13 UTC (permalink / raw)
  To: Laszlo Ersek; +Cc: Igor Mammedov, qemu-devel, mst

[-- Attachment #1: Type: text/plain, Size: 21798 bytes --]


> On Feb 15, 2017, at 7:24 AM, Laszlo Ersek <lersek@redhat.com> wrote:
> 
> On 02/15/17 13:19, Igor Mammedov wrote:
>> On Tue, 14 Feb 2017 22:15:46 -0800
>> ben@skyportsystems.com wrote:
>> 
>>> From: Ben Warren <ben@skyportsystems.com>
>>> 
>>> This implements the VM Generation ID feature by passing a 128-bit
>>> GUID to the guest via a fw_cfg blob.
>>> Any time the GUID changes, an ACPI notify event is sent to the guest
>>> 
>>> The user interface is a simple device with one parameter:
>>> - guid (string, must be "auto" or in UUID format
>>>   xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
>>> 
>>> Signed-off-by: Ben Warren <ben@skyportsystems.com>
>>> ---
>>> default-configs/i386-softmmu.mak     |   1 +
>>> default-configs/x86_64-softmmu.mak   |   1 +
>>> hw/acpi/Makefile.objs                |   1 +
>>> hw/acpi/vmgenid.c                    | 237 +++++++++++++++++++++++++++++++++++
>>> hw/i386/acpi-build.c                 |  16 +++
>>> include/hw/acpi/acpi_dev_interface.h |   1 +
>>> include/hw/acpi/vmgenid.h            |  35 ++++++
>>> 7 files changed, 292 insertions(+)
>>> create mode 100644 hw/acpi/vmgenid.c
>>> create mode 100644 include/hw/acpi/vmgenid.h
>>> 
>>> diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
>>> index 48b07a4..029e952 100644
>>> --- a/default-configs/i386-softmmu.mak
>>> +++ b/default-configs/i386-softmmu.mak
>>> @@ -59,3 +59,4 @@ CONFIG_I82801B11=y
>>> CONFIG_SMBIOS=y
>>> CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
>>> CONFIG_PXB=y
>>> +CONFIG_ACPI_VMGENID=y
>>> diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
>>> index fd96345..d1d7432 100644
>>> --- a/default-configs/x86_64-softmmu.mak
>>> +++ b/default-configs/x86_64-softmmu.mak
>>> @@ -59,3 +59,4 @@ CONFIG_I82801B11=y
>>> CONFIG_SMBIOS=y
>>> CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
>>> CONFIG_PXB=y
>>> +CONFIG_ACPI_VMGENID=y
>>> diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
>>> index 6acf798..11c35bc 100644
>>> --- a/hw/acpi/Makefile.objs
>>> +++ b/hw/acpi/Makefile.objs
>>> @@ -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
>>> diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c
>>> new file mode 100644
>>> index 0000000..b1b7b32
>>> --- /dev/null
>>> +++ b/hw/acpi/vmgenid.c
>>> @@ -0,0 +1,237 @@
>>> +/*
>>> + *  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);
>>> +    memcpy(&guid_le.data, &vms->guid.data, sizeof(vms->guid.data));
>>> +    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));
> 
> Ben:
> 
> (1) The logic is sane here, but the initial sizing of the array is not
> correct. The initial size should be
> 
>  (VMGENID_FW_CFG_SIZE - ARRAY_SIZE(guid_le.data))
> 
> The reason for this is that g_array_insert_vals() really inserts (it
> doesn't overwrite) data, therefore it grows the array. From the GLib
> source code [glib/garray.c]:
> 
> ------------------
> GArray*
> g_array_insert_vals (GArray        *farray,
>                     guint          index_,
>                     gconstpointer  data,
>                     guint          len)
> {
>  GRealArray *array = (GRealArray*) farray;
> 
>  g_return_val_if_fail (array, NULL);
> 
>  g_array_maybe_expand (array, len);
> 
>  memmove (g_array_elt_pos (array, len + index_),
>           g_array_elt_pos (array, index_),
>           g_array_elt_len (array, array->len - index_));
> 
>  memcpy (g_array_elt_pos (array, index_), data, g_array_elt_len (array,
> len));
> 
>  array->len += len;
> 
>  g_array_zero_terminate (array);
> 
>  return farray;
> }
> ------------------
> 
> This is an extremely minor wart, because later on:
> 
Fixed in v7.  Thanks for pointing this out.
>>> +
>>> +    /* 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))));
>> Just curious,
>> so suggested in v5 simple declaration style 
>> 
>> Package(2) {
>>  ADD(VGIA, VMGENID_GUID_OFFSET),
>>  0
>> }
>> 
>> wasn't working for you?
>> 
>>> +    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.
>>> +     */
>>> +    bios_linker_loader_write_pointer(linker,
>>> +        VMGENID_ADDR_FW_CFG_FILE, 0, sizeof(uint64_t),
>>> +        VMGENID_GUID_FW_CFG_FILE);
>>> +
>>> +    /* 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);
> 
> we do expose the correct size to the guest (and the underlying QEMU-side
> allocation is larger, not smaller than that, so it is safe).
> 
> So I guess I could call this an "innocent leak of 16 bytes" -- it's up
> to you if you want to fix it. (I really don't want to obsess about this
> at v6, I could have noticed the exact same in v5!)
> 
>>> +    /* 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.
>>> +             */
>>> +            memcpy(&guid_le.data, &vms->guid.data, sizeof(vms->guid.data));
>> potential stack corruption if guid_le and vms->guid types ever diverge
>> why not just do
>>  guid_le.data = guid.data
> 
> Igor:
> 
> That would be an array assignment, and wouldn't work:
> 
> typedef struct {
>    union {
>        unsigned char data[16];
>        struct {
>            /* Generated in BE endian, can be swapped with
> qemu_uuid_bswap. */
>            uint32_t time_low;
>            uint16_t time_mid;
>            uint16_t time_high_and_version;
>            uint8_t  clock_seq_and_reserved;
>            uint8_t  clock_seq_low;
>            uint8_t  node[6];
>        } fields;
>    };
> } QemuUUID;
> 
> I think the code is safe: both the local variable "guid_le" and
> "VmGenIdState.guid" are declared with type "QemuUUID".
> 
> I agree that a style improvement would be
> 
>  guid_le = vms->guid;
> 
Changed to this in v7
> since structure assignment is okay.
> 
> Personally I feel neutrally about this.
> 
>> 
>>> +            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 + VMGENID_GUID_OFFSET,
>>> +                                      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_initfn(Object *obj)
>>> +{
>>> +    object_property_add_str(obj, VMGENID_GUID, NULL, vmgenid_set_guid, NULL);
>> missing:
>>  object_property_set_description()
>> or even better use class properties here:
>> 
>> object_class_property_add_str()/object_class_property_set_description()
>> 
>>> +}
>>> +
>>> +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;
>> it needs:
>> 
>> dc->hotpluggable = false;
>> 
>>> +}
>>> +
>>> +static const TypeInfo vmgenid_device_info = {
>>> +    .name          = VMGENID_DEVICE,
>>> +    .parent        = TYPE_DEVICE,
>>> +    .instance_size = sizeof(VmGenIdState),
>>> +    .instance_init = vmgenid_initfn,
>>> +    .class_init    = vmgenid_device_class_init,
>>> +};
>>> +
>>> +static void vmgenid_register_types(void)
>>> +{
>>> +    type_register_static(&vmgenid_device_info);
>>> +}
>>> +
>>> +type_init(vmgenid_register_types)
>>> diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
>>> index 1c928ab..db04cf5 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/acpi/vmgenid.h"
>>> #include "sysemu/tpm_backend.h"
>>> #include "hw/timer/mc146818rtc_regs.h"
>>> #include "sysemu/numa.h"
>>> @@ -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.
>>> diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h
>>> index 71d3c48..3c2e4e9 100644
>>> --- a/include/hw/acpi/acpi_dev_interface.h
>>> +++ b/include/hw/acpi/acpi_dev_interface.h
>>> @@ -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"
>>> diff --git a/include/hw/acpi/vmgenid.h b/include/hw/acpi/vmgenid.h
>>> new file mode 100644
>>> index 0000000..db7fa0e
>>> --- /dev/null
>>> +++ b/include/hw/acpi/vmgenid.h
>>> @@ -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);
>> What will happen if CLI would be:
>>  -device vmgenid -device vmgenid
>> As I understand, it should be exclusive single instance device,
>> and there is nothing to prevent second instance of it.
> 
> Igor:
> 
> I agree with your observation, but I don't think it's a show-stopper
> (not at this point anyway). I think it is similar to the case that I
> raised earlier, about <= 2.6 machine types that have no DMA support in
> fw_cfg, hence WRITE_POINTER cannot work on them.
> 
> Both of these cases (i.e., too early machine types, and multiple vmgenid
> devices) come from "pathologic" command lines, and don't prevent the
> intended use of the device (assuming a correct command line). So, I
> think it should be safe to address these questions later, in a followup
> series (for 2.10, likely).
> 
> Ben:
> 
> Summary:
> - the sizing wart that I mentioned under (1) is innocent; it doesn't
> deserve a repost on its own. If you do a v7, I suggest that you fix it
> up, but I don't insist.
> 
> - Personally I'm fine with the rest. I see that Igor made some comments,
> but I feel that a good chunk of those could have been made for v5 just
> the same (example: dc->hotpluggable, object_property_set_description() /
> class properties). I wouldn't like to delay this series any longer.
> Those improvements can be added later, IMO -- but please do work out
> with Igor whether he really wants a v7 for those.
> 
> I'm fine with the patch as-is, and I'm also fine with it if Igor's
> comments are addressed:
> 
> Reviewed-by: Laszlo Ersek <lersek@redhat.com <mailto:lersek@redhat.com>>
> 
> If you do other changes though, please drop my R-b.
> 
Thanks so much for all the reviews.  I’ve left off your R-b because I ended up using the ‘src_offset’ argument for write_pointer().  Other than that, all recommended changes are made.
> ... I'd like to look at the rest of this series a little, and then I'll
> try to come back with test results (with OVMF).
> 
> Thanks!
> Laszlo
> 
>> 
>> 
>>> +}
>>> +
>>> +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


[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 3583 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 5/7] qmp/hmp: add query-vm-generation-id and 'info vm-generation-id' commands
  2017-02-15 15:36   ` Laszlo Ersek
@ 2017-02-16  6:13     ` Ben Warren
  0 siblings, 0 replies; 62+ messages in thread
From: Ben Warren @ 2017-02-16  6:13 UTC (permalink / raw)
  To: Laszlo Ersek; +Cc: qemu-devel, mst, imammedo

[-- Attachment #1: Type: text/plain, Size: 5020 bytes --]


> On Feb 15, 2017, at 7:36 AM, Laszlo Ersek <lersek@redhat.com> wrote:
> 
> Two questions:
> 
> On 02/15/17 07:15, ben@skyportsystems.com <mailto:ben@skyportsystems.com> wrote:
>> From: Igor Mammedov <imammedo@redhat.com>
>> 
>> Add commands to query Virtual Machine Generation ID counter.
>> 
>> QMP command example:
>>    { "execute": "query-vm-generation-id" }
>> 
>> HMP command example:
>>    info vm-generation-id
>> 
>> Signed-off-by: Igor Mammedov <imammedo@redhat.com>
>> Reviewed-by: Eric Blake <eblake@redhat.com>
>> Signed-off-by: Ben Warren <ben@skyportsystems.com>
>> ---
>> hmp-commands-info.hx | 13 +++++++++++++
>> hmp.c                |  9 +++++++++
>> hmp.h                |  1 +
>> hw/acpi/vmgenid.c    | 16 ++++++++++++++++
>> qapi-schema.json     | 20 ++++++++++++++++++++
>> stubs/Makefile.objs  |  1 +
>> stubs/vmgenid.c      |  8 ++++++++
>> 7 files changed, 68 insertions(+)
>> create mode 100644 stubs/vmgenid.c
>> 
>> diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
>> index b0f35e6..f3df793 100644
>> --- a/hmp-commands-info.hx
>> +++ b/hmp-commands-info.hx
>> @@ -802,6 +802,19 @@ Show information about hotpluggable CPUs
>> ETEXI
>> 
>> STEXI
>> +@item info vm-generation-id
> 
> (1) Don't we need some kind of @findex here, for consistency with the
> rest of the file?
> 
>> +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
>> 
>> diff --git a/hmp.c b/hmp.c
>> index 2bc4f06..535613d 100644
>> --- a/hmp.c
>> +++ b/hmp.c
>> @@ -2565,3 +2565,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);
>> +}
>> diff --git a/hmp.h b/hmp.h
>> index 05daf7c..799fd37 100644
>> --- a/hmp.h
>> +++ b/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
>> diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c
>> index b1b7b32..c159c76 100644
>> --- a/hw/acpi/vmgenid.c
>> +++ b/hw/acpi/vmgenid.c
>> @@ -235,3 +235,19 @@ static void vmgenid_register_types(void)
>> }
>> 
>> 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;
>> +}
>> diff --git a/qapi-schema.json b/qapi-schema.json
>> index 61151f3..5e2a47f 100644
>> --- a/qapi-schema.json
>> +++ b/qapi-schema.json
>> @@ -6051,3 +6051,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' }
>> diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
>> index a187295..0bffca6 100644
>> --- a/stubs/Makefile.objs
>> +++ b/stubs/Makefile.objs
>> @@ -35,3 +35,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
>> diff --git a/stubs/vmgenid.c b/stubs/vmgenid.c
>> new file mode 100644
>> index 0000000..8c448ac
>> --- /dev/null
>> +++ b/stubs/vmgenid.c
>> @@ -0,0 +1,8 @@
>> +#include "qemu/osdep.h"
>> +#include "qmp-commands.h"
>> +
>> +GuidInfo *qmp_query_vm_generation_id(Error **errp)
>> +{
>> +    error_setg(errp, "this command is not currently supported");
>> +    return NULL;
>> +}
>> 
> 
> (2) Don't we usually employ QERR_UNSUPPORTED for the format string in
> such cases?
> 
> With or without updates:
> 
> Reviewed-by: Laszlo Ersek <lersek@redhat.com <mailto:lersek@redhat.com>>
> 
Both items changed.  Thanks!
> Thanks
> Laszlo


[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 3583 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 7/7] tests: Add unit tests for the VM Generation ID feature
  2017-02-15 13:13   ` Igor Mammedov
@ 2017-02-16  6:15     ` Ben Warren
  0 siblings, 0 replies; 62+ messages in thread
From: Ben Warren @ 2017-02-16  6:15 UTC (permalink / raw)
  To: Igor Mammedov; +Cc: qemu-devel, lersek, mst

[-- Attachment #1: Type: text/plain, Size: 9540 bytes --]


> On Feb 15, 2017, at 5:13 AM, Igor Mammedov <imammedo@redhat.com> wrote:
> 
> On Tue, 14 Feb 2017 22:15:49 -0800
> ben@skyportsystems.com <mailto:ben@skyportsystems.com> wrote:
> 
>> From: Ben Warren <ben@skyportsystems.com>
>> 
>> The following tests are implemented:
>> * test that a GUID passed in by command line is propagated to the guest.
>> * test that changing the GUID at runtime via the monitor is reflected in
>>  the guest.
>> * test that the "auto" argument to the GUID generates a different, and
>>  correct GUID as seen by the guest.
>> 
>>  This patch is loosely based on a previous patch from:
>>  Gal Hammer <ghammer@redhat.com>  and Igor Mammedov <imammedo@redhat.com>
>> 
>> Signed-off-by: Ben Warren <ben@skyportsystems.com>
>> ---
>> tests/Makefile.include |   2 +
>> tests/vmgenid-test.c   | 195 +++++++++++++++++++++++++++++++++++++++++++++++++
>> 2 files changed, 197 insertions(+)
>> create mode 100644 tests/vmgenid-test.c
>> 
>> diff --git a/tests/Makefile.include b/tests/Makefile.include
>> index 634394a..ca4b3f7 100644
>> --- a/tests/Makefile.include
>> +++ b/tests/Makefile.include
>> @@ -241,6 +241,7 @@ check-qtest-i386-y += tests/usb-hcd-xhci-test$(EXESUF)
>> gcov-files-i386-y += hw/usb/hcd-xhci.c
>> check-qtest-i386-y += tests/pc-cpu-test$(EXESUF)
>> check-qtest-i386-y += tests/q35-test$(EXESUF)
>> +check-qtest-i386-y += tests/vmgenid-test$(EXESUF)
>> gcov-files-i386-y += hw/pci-host/q35.c
>> check-qtest-i386-$(CONFIG_VHOST_NET_TEST_i386) += tests/vhost-user-test$(EXESUF)
>> ifeq ($(CONFIG_VHOST_NET_TEST_i386),)
>> @@ -726,6 +727,7 @@ tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem
>> tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o contrib/libvhost-user/libvhost-user.o $(test-util-obj-y)
>> tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y)
>> tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o
>> +tests/vmgenid-test$(EXESUF): tests/vmgenid-test.o
>> 
>> tests/migration/stress$(EXESUF): tests/migration/stress.o
>> 	$(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@")
>> diff --git a/tests/vmgenid-test.c b/tests/vmgenid-test.c
>> new file mode 100644
>> index 0000000..721ba05
>> --- /dev/null
>> +++ b/tests/vmgenid-test.c
>> @@ -0,0 +1,195 @@
>> +/*
>> + * QTest testcase for VM Generation ID
>> + *
>> + * Copyright (c) 2016 Red Hat, Inc.
>> + * Copyright (c) 2017 Skyport Systems
>> + *
>> + * 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 <glib.h>
>> +#include <string.h>
>> +#include <unistd.h>
>> +#include "qemu/osdep.h"
>> +#include "qemu/bitmap.h"
>> +#include "qemu/uuid.h"
>> +#include "hw/acpi/acpi-defs.h"
>> +#include "acpi-utils.h"
>> +#include "libqtest.h"
>> +
>> +#define VGID_GUID "324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87"
>> +#define VMGENID_GUID_OFFSET      40   /* allow space for
>> +                                       * OVMF SDT Header Probe Supressor
>> +                                       */
>> +
>> +static uint32_t vgia;
>> +
>> +typedef struct {
>> +    AcpiTableHeader header;
>> +    gchar name_op;
>> +    gchar vgia[4];
>> +    gchar val_op;
>> +    uint32_t vgia_val;
>> +} QEMU_PACKED VgidTable;
>> +
>> +static uint32_t find_vgia(void)
>> +{
> [...]
> 
> ===
>> +    /* First, find the RSDP */
>> +    for (off = 0xf0000; off < 0x100000; off += 0x10) {
>> +        uint8_t sig[] = "RSD PTR ";
>> +
>> +        for (i = 0; i < sizeof sig - 1; ++i) {
>> +            sig[i] = readb(off + i);
>> +        }
>> +
>> +        if (!memcmp(sig, "RSD PTR ", sizeof sig)) {
>> +            break;
>> +        }
>> +    }
>> +    g_assert_cmphex(off, <, 0x100000);
>> +
>> +    /* Parse the RSDP header so we can find the RSDT */
>> +    ACPI_READ_FIELD(rsdp_table.signature, off);
>> +    ACPI_ASSERT_CMP64(rsdp_table.signature, "RSD PTR ");
>> +
>> +    ACPI_READ_FIELD(rsdp_table.checksum, off);
>> +    ACPI_READ_ARRAY(rsdp_table.oem_id, off);
>> +    ACPI_READ_FIELD(rsdp_table.revision, off);
>> +    ACPI_READ_FIELD(rsdp_table.rsdt_physical_address, off);
>> +
>> +    rsdt = rsdp_table.rsdt_physical_address;
>> +    /* read the header */
>> +    ACPI_READ_TABLE_HEADER(&rsdt_table, rsdt);
>> +    ACPI_ASSERT_CMP(rsdt_table.signature, "RSDT");
>> +
>> +    /* compute the table entries in rsdt */
>> +    tables_nr = (rsdt_table.length - sizeof(AcpiRsdtDescriptorRev1)) /
>> +                sizeof(uint32_t);
>> +    g_assert_cmpint(tables_nr, >, 0);
>> +
>> +    /* get the addresses of the tables pointed by rsdt */
>> +    tables = g_new0(uint32_t, tables_nr);
>> +    ACPI_READ_ARRAY_PTR(tables, tables_nr, rsdt);
> ===
> above is duplicated code from bios-tables-test.c
> please extract it into separate functions and use them in both tests.
> 
Done in v7
>> +    for (i = 0; i < tables_nr; i++) {
>> +        ACPI_READ_TABLE_HEADER(&ssdt_table, tables[i]);
>> +        if (!strncmp((char *)ssdt_table.oem_table_id, "VMGENID", 7)) {
>> +            /* the first entry in the table should be VGIA
>> +             * That's all we need
>> +             */
>> +            ACPI_READ_FIELD(vgid_table.name_op, tables[i]);
>> +            g_assert(vgid_table.name_op == 0x08);  /* name */
>> +            ACPI_READ_ARRAY(vgid_table.vgia, tables[i]);
>> +            g_assert(memcmp(vgid_table.vgia, "VGIA", 4) == 0);
>> +            ACPI_READ_FIELD(vgid_table.val_op, tables[i]);
>> +            g_assert(vgid_table.val_op == 0x0C);  /* dword */
>> +            ACPI_READ_FIELD(vgid_table.vgia_val, tables[i]);
>> +            /* 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
>> +             */
>> +            return vgid_table.vgia_val + VMGENID_GUID_OFFSET;
>> +        }
>> +    }
>> +    return 0;
>> +}
>> +
>> +static void vmgenid_read_guid(QemuUUID *guid)
>> +{
>> +    int i;
>> +
>> +    if (vgia == 0) {
>> +        vgia = find_vgia();
>> +    }
>> +    g_assert(vgia);
>> +
>> +    /* Read the GUID directly from guest memory */
>> +    for (i = 0; i < 16; i++) {
>> +        guid->data[i] = readb(vgia + i);
>> +    }
>> +    /* The GUID is in little-endian format in the guest, while QEMU
>> +     * uses big-endian.  Swap after reading.
>> +     */
>> +    qemu_uuid_bswap(guid);
>> +}
>> +
>> +static void vmgenid_test(void)
>> +{
>> +    QemuUUID expected, measured;
>> +    g_assert(qemu_uuid_parse(VGID_GUID, &expected) == 0);
>> +    vmgenid_read_guid(&measured);
>> +    g_assert(memcmp(measured.data, expected.data, sizeof(measured.data)) == 0);
>> +}
>> +
>> +static void vmgenid_set_guid_test(void)
>> +{
>> +    QDict *response;
>> +    gchar *cmd;
>> +    QemuUUID expected, measured;
>> +    g_assert(qemu_uuid_parse(VGID_GUID, &expected) == 0);
>> +    /* Change the GUID slightly */
>> +    expected.data[0] += 1;
>> +
>> +    cmd = g_strdup_printf("{ 'execute': 'qom-set', 'arguments': { "
>> +                   "'path': '/machine/peripheral/testvgid', "
>> +                   "'property': 'guid', 'value': '%s' } }",
>> +                   qemu_uuid_unparse_strdup(&expected));
>> +    response = qmp(cmd);
>> +    g_assert(qdict_haskey(response, "return"));
>> +    QDECREF(response);
>> +
>> +    vmgenid_read_guid(&measured);
>> +    g_assert(memcmp(measured.data, expected.data, sizeof(measured.data)) == 0);
>> +}
>> +
>> +static void vmgenid_set_guid_auto_test(void)
>> +{
>> +    QDict *response;
>> +    QemuUUID expected, measured;
>> +
>> +    /* Read the initial value */
>> +    vmgenid_read_guid(&expected);
>> +
>> +    /* Setting to 'auto' generates a random GUID */
>> +    response = qmp("{ 'execute': 'qom-set', 'arguments': { "
>> +                   "'path': '/machine/peripheral/testvgid', "
>> +                   "'property': 'guid', 'value': 'auto' } }");
>> +
>> +    g_assert(qdict_haskey(response, "return"));
>> +    QDECREF(response);
>> +
>> +    vmgenid_read_guid(&measured);
>> +    g_assert(memcmp(measured.data, expected.data, sizeof(measured.data)) != 0);
>> +}
>> +
>> +int main(int argc, char **argv)
>> +{
>> +    int ret;
>> +    gchar *cmd;
>> +
>> +    g_test_init(&argc, &argv, NULL);
>> +
>> +    cmd = g_strdup_printf("-machine accel=tcg -device vmgenid,id=testvgid,"
>> +                          "guid=%s", VGID_GUID);
>> +    qtest_start(cmd);
>> +    qtest_add_func("/vmgenid/vmgenid", vmgenid_test);
>> +    qtest_add_func("/vmgenid/vmgenid/set-guid", vmgenid_set_guid_test);
> this is probably no necessary as you've dropped dynamic set in v6,
> so if this test passes it's a bug in vmgenid and you should forbid
> setting property after device is realized.
> See how qdev_prop_set_after_realize() is used.
> 
Removed
>> +    qtest_add_func("/vmgenid/vmgenid/set-guid-auto",
>> +                   vmgenid_set_guid_auto_test);
> this should be replaced with this first test but with 'auto' value
> 
> maybe add query-vm-generation-id test
I added code to read from the monitor and compare to the value read from memory.  I hope that’s OK.
> 
>> +    ret = g_test_run();
>> +
>> +    qtest_end();
>> +
>> +    return ret;
>> +}


[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 3583 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15 19:34                     ` Michael S. Tsirkin
@ 2017-02-16  8:25                       ` Igor Mammedov
  2017-02-16  9:49                         ` Laszlo Ersek
  0 siblings, 1 reply; 62+ messages in thread
From: Igor Mammedov @ 2017-02-16  8:25 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: Laszlo Ersek, qemu-devel, ben

On Wed, 15 Feb 2017 21:34:45 +0200
"Michael S. Tsirkin" <mst@redhat.com> wrote:

> On Wed, Feb 15, 2017 at 07:24:36PM +0100, Igor Mammedov wrote:
> > > As long as all users pass in 0 though there's a real possibility guests
> > > will implement this incorrectly.  
> > We are here to ensure that at least Seabios (I'll review it)
> > and OVMF (Laszlo would take care of it I suppose) do it right,
> > and if there are other firmwares, they should do it correctly
> > as described fix their own bugs later wrt randomly written
> > implementation.  
> 
> As long as it's untested, it can always do the wrong thing
> even if it looks right, or it can bitrot.
> 
> > > I guess we can put in the offset just
> > > behind the zero-filled padding we have there.  
> > I've assumed padding was there to make commands fixed size and give
> > a room for future extensions  
> 
> you mean
>         char pad[124];
> ?
> 
> Right. So you can easily add fields, but if you do firmware
> will just assume it does not know how to handle these
> commands and skip them.
> 
> > so hunk changing BiosLinkerLoaderEntry
> > would look like:
> > 
> > diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
> > index d963ebe..6983713 100644
> > --- a/hw/acpi/bios-linker-loader.c
> > +++ b/hw/acpi/bios-linker-loader.c
> > @@ -49,6 +49,7 @@ struct BiosLinkerLoaderEntry {
> >              char file[BIOS_LINKER_LOADER_FILESZ];
> >              uint32_t align;
> >              uint8_t zone;
> > +            uint32_t padding;
> >          } alloc;
> >  
> >          /*
> > @@ -62,6 +63,7 @@ struct BiosLinkerLoaderEntry {
> >              char src_file[BIOS_LINKER_LOADER_FILESZ];
> >              uint32_t offset;
> >              uint8_t size;
> > +            uint32_t padding;
> >          } pointer;
> >  
> >          /*
> > @@ -76,10 +78,25 @@ struct BiosLinkerLoaderEntry {
> >              uint32_t offset;
> >              uint32_t start;
> >              uint32_t length;
> > +            uint32_t padding;
> >          } cksum;  
> 
> why is this necessary?
> 
> > +        /*
> > +         * COMMAND_WRITE_POINTER - write the fw_cfg file (originating from
> > +         * @dest_file) at @wr_pointer.offset, by writing 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];
> > +        char pad[120];  
> 
> and this shrinks entry size by 4 bytes. Why?
so total size won't change, due to +4 to account for src_offest in wr_pointer

> 
> >      };
> >  } QEMU_PACKED;
> >  typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
> > 
> >    
> > > I'm mostly concerned we are adding new features to something
> > > that has been through 25 revisions already.  
> > It's ABI so it's worth extra effort,  
> 
> There's always yet another possible enhancement you can add.
> I see it as a bit of a feature creep frankly.
> 
> > it looks like only one more revision is left and there is
> > about a week left to post and merge it.  
> 
> If we can do it quickly, fine, but I think we should merge ASAP.
> 

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID
  2017-02-16  6:10       ` Ben Warren
@ 2017-02-16  9:36         ` Laszlo Ersek
  0 siblings, 0 replies; 62+ messages in thread
From: Laszlo Ersek @ 2017-02-16  9:36 UTC (permalink / raw)
  To: Ben Warren; +Cc: Michael S. Tsirkin, qemu-devel, imammedo

On 02/16/17 07:10, Ben Warren wrote:
> 
>> On Feb 15, 2017, at 12:52 PM, Laszlo Ersek <lersek@redhat.com> wrote:
>>
>> On 02/15/17 21:09, Michael S. Tsirkin wrote:
>>> On Wed, Feb 15, 2017 at 08:47:48PM +0100, Laszlo Ersek wrote:
>>
>> [snip]
>>
>>>> For patches #1, #3, #4 and #5:
>>>>
>>>> Tested-by: Laszlo Ersek <lersek@redhat.com>
>>>>
>>>> I'll soon post the OVMF patches.
>>>>
>>>> Thanks!
>>>> Laszlo
>>>
>>>
>>> How do you feel about Igor's request to change WRITE_POINTER to add
>>> offset in there, so guest can pass in the address of GUID and
>>> not start of table? Would that be a lot of work to add?
>>
>> I think it's doable in practice: simply add a constant from the command
>> itself, for passing the value back to QEMU, and also for saving the
>> fw_cfg write commend for S3 resume time.
>>
>> But, I disagree with it from a design POV.
>>
>> Igor's point is:
>>
>>> Math complicates QEMU code though and not only QMEMU but AML code as
>>> well.
>>
>> As I understand it, the goal is to push the addition to the firmware
>> (which is "one place"), rather than having to implement it twice in
>> QEMU, i.e., in two places ((a) native QEMU logic, (b) AML generator).
>>
>> Here's my counter-argument:
>>
>> (a) As I mentioned earlier, assume a complex communication structure
>> between the guest OS and QEMU. Currently our shared structure consists
>> of a single field (the GUID), but next time it might contain several fields.
>>
>> For such a multi-field shared structure, QEMU will have to do manual
>> offsetting into the guest RAM anyway, for accessing fields F1, F2, and
>> F3. We will not create three separate WRITE_POINTER commands and let the
>> firmware calculate and return the absolute GPAs of the fields F1, F2 and
>> F3. Instead, there will be one WRITE_POINTER command, and QEMU will do
>> the offsetting manually, minimally for fields F2 and F3.
>>
>> "src_offset" looks tempting now only because we have a shared structure
>> with only one field, the GUID at offset 40 decimal.
>>
>> (b) Regarding the runtime addition in the AML code:
>>
>> As discussed before, the main reason *now*, for not pointing VGIA (and
>> other named integer objects) with ADD_POINTER commands directly to
>> "meaningful" fields, is that OVMF probes the targets of ADD_POINTER
>> commands for patterns that look like ACPI table headers. And, for the
>> time being, we want to suppress any mis-recognitions by prepending some
>> padding.
>>
>> Igor was right to dislike this, and we agreed that *down the road* we
>> should add allocation flags, or further allocation commands, to supplant
>> this kind of heuristics in OVMF. But:
>>
>> - we don't have time to do it now, plus
>>
>> - please observe that the runtime addition in AML relates to the
>>  ADD_POINTER and the ALLOCATE commands. It does not relate to
>>  WRITE_POINTER at all.
>>
>>  Whatever we change on WRITE_POINTER will do nothing for suppressing
>>  OVMF's table header probing -- because that is tied to ADD_POINTER
>>  --, therefore WRITE_POINTER tweaks cannot eliminate the "need to add"
>>  in AML.
>>
>>
>> In summary, I think the proposed WRITE_POINTER modification is
>> implementable, but I think it will not pay off, because:
>>
>> (a) for QEMU logic, it will not prove useful as soon as we have a
>> multi-field shared structure (QEMU will have to add field offsets anyway),
>>
>> (b) and for eliminating the AML addition (which is a consequence of the
>> current ADD_POINTER handling in OVMF), it does nothing.
>>
> OK Laszlo, in v7 (imminent)  I went ahead and implemented this
> src_offset.  If you are truly dead-set against it, it’s not very hard
> to remove.  To me it seems pretty harmless.

I'd like to hear Igor's opinion about my counter-argument above.

It would be a fair bit of work in OVMF:
- it introduces a new addition which has to be range checked (the sum
  cannot overflow either the pointer object or the size of the
  pointed-to blob),
- if the range check fails, that's a new error condition that needs
  handling,
- in edk2 we document error conditions carefully, so it affects
  documentation too, for the function that processes WRITE_POINTER,
- I'd have to redo all the testing.

After I post the OVMF patches, we can count on about one week of review
time (assuming I don't have to post a v2, that is!). That comes pretty
close to the end of February, which is when I want to do a downstream
OVMF rebase. (I can't do it in March due to a conference, and then it's
too late.) The downstream rebase will take a few days too. I would have
preferred to post the OVMF patches near the beginning of this week.

So, as I said, it is implementable, I'm not "dead-set" against it, but
the price for me to pay is definitely not zero. And, as it stands, I
disagree with the design -- there don't seem to be any general benefits.

I'd like to hear back from Igor. If he convincingly refutes my argument,
I'm game.

Thanks,
Laszlo

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-16  8:25                       ` Igor Mammedov
@ 2017-02-16  9:49                         ` Laszlo Ersek
  0 siblings, 0 replies; 62+ messages in thread
From: Laszlo Ersek @ 2017-02-16  9:49 UTC (permalink / raw)
  To: Igor Mammedov; +Cc: Michael S. Tsirkin, qemu-devel, ben

On 02/16/17 09:25, Igor Mammedov wrote:
> On Wed, 15 Feb 2017 21:34:45 +0200
> "Michael S. Tsirkin" <mst@redhat.com> wrote:
> 
>> On Wed, Feb 15, 2017 at 07:24:36PM +0100, Igor Mammedov wrote:
>>>> As long as all users pass in 0 though there's a real possibility guests
>>>> will implement this incorrectly.  
>>> We are here to ensure that at least Seabios (I'll review it)
>>> and OVMF (Laszlo would take care of it I suppose) do it right,
>>> and if there are other firmwares, they should do it correctly
>>> as described fix their own bugs later wrt randomly written
>>> implementation.  
>>
>> As long as it's untested, it can always do the wrong thing
>> even if it looks right, or it can bitrot.
>>
>>>> I guess we can put in the offset just
>>>> behind the zero-filled padding we have there.  
>>> I've assumed padding was there to make commands fixed size and give
>>> a room for future extensions  
>>
>> you mean
>>         char pad[124];
>> ?
>>
>> Right. So you can easily add fields, but if you do firmware
>> will just assume it does not know how to handle these
>> commands and skip them.
>>
>>> so hunk changing BiosLinkerLoaderEntry
>>> would look like:
>>>
>>> diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
>>> index d963ebe..6983713 100644
>>> --- a/hw/acpi/bios-linker-loader.c
>>> +++ b/hw/acpi/bios-linker-loader.c
>>> @@ -49,6 +49,7 @@ struct BiosLinkerLoaderEntry {
>>>              char file[BIOS_LINKER_LOADER_FILESZ];
>>>              uint32_t align;
>>>              uint8_t zone;
>>> +            uint32_t padding;
>>>          } alloc;
>>>  
>>>          /*
>>> @@ -62,6 +63,7 @@ struct BiosLinkerLoaderEntry {
>>>              char src_file[BIOS_LINKER_LOADER_FILESZ];
>>>              uint32_t offset;
>>>              uint8_t size;
>>> +            uint32_t padding;
>>>          } pointer;
>>>  
>>>          /*
>>> @@ -76,10 +78,25 @@ struct BiosLinkerLoaderEntry {
>>>              uint32_t offset;
>>>              uint32_t start;
>>>              uint32_t length;
>>> +            uint32_t padding;
>>>          } cksum;  
>>
>> why is this necessary?
>>
>>> +        /*
>>> +         * COMMAND_WRITE_POINTER - write the fw_cfg file (originating from
>>> +         * @dest_file) at @wr_pointer.offset, by writing 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];
>>> +        char pad[120];  
>>
>> and this shrinks entry size by 4 bytes. Why?
> so total size won't change, due to +4 to account for src_offest in wr_pointer

To be clear, I still disagree with the introduction of "src_offset"
(please let's discuss that in the other sub-thread). Still I can comment
on this question in isolation:

The individual command structures, and the "pad" field (which is 124
bytes), are all members of a *union*. They all start at offset zero in
the union, and the union's size is determined by the largest member,
plus any additional padding at the end that the compiler wishes to
append. (In this case, becasue we use QEMU_PACKED, the compiler doesn't
do that.)

Therefore, as long as you don't exceed the size of

  char pad[124];

you can extend any of the command structures freely -- "pad" will remain
the largest union member, and thereby dictate the size of the union.

In short, "pad" does not *follow* any of the command structures; they
are all laid over each other, from offset 0 in the union.

(If you are curious about the magic constant 124, that comes from 128
minus the size of the "command" field in the outermost
BiosLinkerLoaderEntry structure. The guiding principle here is that the
outermost BiosLinkerLoaderEntry structure be 128 bytes in size.)

--*--

Again: I disagree with the introduction of "src_offset", and I'd like to
hear your opinion about my counter-argument in the other subthread.

Thanks
Laszlo

> 
>>
>>>      };
>>>  } QEMU_PACKED;
>>>  typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
>>>
>>>    
>>>> I'm mostly concerned we are adding new features to something
>>>> that has been through 25 revisions already.  
>>> It's ABI so it's worth extra effort,  
>>
>> There's always yet another possible enhancement you can add.
>> I see it as a bit of a feature creep frankly.
>>
>>> it looks like only one more revision is left and there is
>>> about a week left to post and merge it.  
>>
>> If we can do it quickly, fine, but I think we should merge ASAP.
>>
> 

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-15 19:19                       ` Ben Warren
@ 2017-02-16 11:10                         ` Igor Mammedov
  2017-02-16 15:38                           ` Eric Blake
  0 siblings, 1 reply; 62+ messages in thread
From: Igor Mammedov @ 2017-02-16 11:10 UTC (permalink / raw)
  To: Ben Warren; +Cc: Michael S. Tsirkin, Laszlo Ersek, qemu-devel

On Wed, 15 Feb 2017 11:19:44 -0800
Ben Warren <ben@skyportsystems.com> wrote:

> > On Feb 15, 2017, at 11:14 AM, Ben Warren <ben@skyportsystems.com> wrote:
> >   
> >> 
> >> On Feb 15, 2017, at 10:24 AM, Igor Mammedov <imammedo@redhat.com <mailto:imammedo@redhat.com>> wrote:
> >> 
> >> On Wed, 15 Feb 2017 20:04:40 +0200
> >> "Michael S. Tsirkin" <mst@redhat.com <mailto:mst@redhat.com>> wrote:
> >>   
> >>> On Wed, Feb 15, 2017 at 06:43:09PM +0100, Igor Mammedov wrote:  
> >>>> On Wed, 15 Feb 2017 18:39:06 +0200
> >>>> "Michael S. Tsirkin" <mst@redhat.com <mailto:mst@redhat.com>> wrote:
> >>>>   
> >>>>> On Wed, Feb 15, 2017 at 04:56:02PM +0100, Igor Mammedov wrote:    
> >>>>>> On Wed, 15 Feb 2017 17:30:00 +0200
> >>>>>> "Michael S. Tsirkin" <mst@redhat.com <mailto:mst@redhat.com>> wrote:
> >>>>>>   
> >>>>>>> On Wed, Feb 15, 2017 at 04:22:25PM +0100, Igor Mammedov wrote:      
> >>>>>>>> On Wed, 15 Feb 2017 15:13:20 +0100
> >>>>>>>> Laszlo Ersek <lersek@redhat.com <mailto:lersek@redhat.com>> wrote:
> >>>>>>>>   
> >>>>>>>>> Commenting under Igor's reply for simplicity
> >>>>>>>>> 
> >>>>>>>>> On 02/15/17 11:57, Igor Mammedov wrote:        
> >>>>>>>>>> On Tue, 14 Feb 2017 22:15:43 -0800
> >>>>>>>>>> ben@skyportsystems.com <mailto:ben@skyportsystems.com> wrote:
> >>>>>>>>>>   
> >>>>>>>>>>> From: Ben Warren <ben@skyportsystems.com <mailto:ben@skyportsystems.com>>
> >>>>>>>>>>> 
> >>>>>>>>>>> This is similar to the existing 'add pointer' functionality, but instead
> >>>>>>>>>>> of instructing the guest (BIOS or UEFI) to patch memory, it instructs
> >>>>>>>>>>> the guest to write the pointer back to QEMU via a writeable fw_cfg file.
> >>>>>>>>>>> 
> >>>>>>>>>>> Signed-off-by: Ben Warren <ben@skyportsystems.com <mailto:ben@skyportsystems.com>>
> >>>>>>>>>>> ---
> >>>>>>>>>>> hw/acpi/bios-linker-loader.c         | 58 ++++++++++++++++++++++++++++++++++--
> >>>>>>>>>>> include/hw/acpi/bios-linker-loader.h |  6 ++++
> >>>>>>>>>>> 2 files changed, 61 insertions(+), 3 deletions(-)
> >>>>>>>>>>> 
> >>>>>>>>>>> diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
> >>>>>>>>>>> index d963ebe..5030cf1 100644
> >>>>>>>>>>> --- a/hw/acpi/bios-linker-loader.c
> >>>>>>>>>>> +++ b/hw/acpi/bios-linker-loader.c
> >>>>>>>>>>> @@ -78,6 +78,19 @@ 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 the table
> >>>>>>>>>>> +         * originating from @src_file. 1,2,4 or 8 byte unsigned
> >>>>>>>>>>> +         * addition is used depending on @wr_pointer.size.
> >>>>>>>>>>> +         */          
> >>>>>>>>> 
> >>>>>>>>> The words "adding" and "addition" are causing confusion here.
> >>>>>>>>> 
> >>>>>>>>> In all of the previous discussion, *addition* was out of scope from
> >>>>>>>>> WRITE_POINTER. Again, the firmware is specifically not required to
> >>>>>>>>> *read* any part of the fw_cfg blob identified by "dest_file".
> >>>>>>>>> 
> >>>>>>>>> WRITE_POINTER instructs the firmware to return the allocation address of
> >>>>>>>>> the downloaded "src_file" to QEMU. Any necessary runtime subscripting
> >>>>>>>>> within "src_file" is to be handled by QEMU code dynamically.
> >>>>>>>>> 
> >>>>>>>>> For example, consider that "src_file" has *several* fields that QEMU
> >>>>>>>>> wants to massage; in that case, indexing within QEMU code with field
> >>>>>>>>> offsets is simply unavoidable.        
> >>>>>>>> what I don't like here is that this indexing would be rather fragile
> >>>>>>>> and has to be done in different parts of QEMU /device, AML/.
> >>>>>>>> 
> >>>>>>>> I'd prefer this helper function to have the same @src_offset
> >>>>>>>> behavior as ADD_POINTER where patched address could point to
> >>>>>>>> any part of src_file i.e. not just beginning.        
> >>>>>>> 
> >>>>>>> 
> >>>>>>> 
> >>>>>>>        /*
> >>>>>>>         * COMMAND_ADD_POINTER - patch the table (originating from
> >>>>>>>         * @dest_file) at @pointer.offset, by adding a pointer to the table
> >>>>>>>         * originating from @src_file. 1,2,4 or 8 byte unsigned
> >>>>>>>         * addition is used depending on @pointer.size.
> >>>>>>>         */
> >>>>>>> 
> >>>>>>> so the way ADD works is
> >>>>>>> 	read at offset
> >>>>>>> 	add table address
> >>>>>>> 	write result at offset
> >>>>>>> 
> >>>>>>> in other words it is always beginning of table that is added.      
> >>>>>> more exactly it's, read at 
> >>>>>>  src_offset = *(dst_blob_ptr+dst_offset)
> >>>>>>  *(dst_blob+dst_offset) = src_blob_ptr + src_offset
> >>>>>>   
> >>>>>>> Would the following be acceptable?
> >>>>>>> 
> >>>>>>> 
> >>>>>>>         * COMMAND_WRITE_POINTER - update the fw_cfg file (originating from
> >>>>>>>         * @dest_file) at @wr_pointer.offset, by writing a pointer to the table
> >>>>>>>         * originating from @src_file. 1,2,4 or 8 byte unsigned value
> >>>>>>>         * is written depending on @wr_pointer.size.      
> >>>>>> it looses 'adding' part of ADD_POINTER command which handles src_offset,
> >>>>>> however implementing adding part looks a bit complicated
> >>>>>> as patched blob (dst) is not in guest memory but in QEMU and
> >>>>>> on reset *(dst_blob+dst_offset) should be reset to src_offset.
> >>>>>> Considering dst file could be device specific memory (field/blob/whatever)
> >>>>>> it could be hard to track/notice proper reset behavior.
> >>>>>> 
> >>>>>> So now I'm not sure if src_offset is worth adding.      
> >>>>> 
> >>>>> Right. Let's just do this math in QEMU if we have to.    
> >>>> Math complicates QEMU code though and not only QMEMU but AML code as well.
> >>>> Considering that we are adding a new command and don't have to keep
> >>>> any sort of compatibility we can pass src_offset as part
> >>>> of command instead of hiding it inside of dst_file.
> >>>> Something like this:
> >>>> 
> >>>>        /*
> >>>>         * COMMAND_WRITE_POINTER - write the fw_cfg file (originating from
> >>>>         * @dest_file) at @wr_pointer.offset, by writing 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 offset;
> >>>> +            uint32_t dst_offset;
> >>>> +            uint32_t src_offset;
> >>>>             uint8_t size;
> >>>>        } wr_pointer;    
> >>> 
> >>> 
> >>> As long as all users pass in 0 though there's a real possibility guests
> >>> will implement this incorrectly.  
> >> We are here to ensure that at least Seabios (I'll review it)
> >> and OVMF (Laszlo would take care of it I suppose) do it right,
> >> and if there are other firmwares, they should do it correctly
> >> as described fix their own bugs later wrt randomly written
> >> implementation.
> >>   
> >>> I guess we can put in the offset just
> >>> behind the zero-filled padding we have there.  
> >> I've assumed padding was there to make commands fixed size and give
> >> a room for future extensions so hunk changing BiosLinkerLoaderEntry
> >> would look like:
> >>   
> > I can’t say I follow the logic of these extra paddings.  The sizes of the structs are all over the place, so adding 4 bytes doesn’t do much.  I assume you have a good reason, though.
> >   
> >> diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c
> >> index d963ebe..6983713 100644
> >> --- a/hw/acpi/bios-linker-loader.c
> >> +++ b/hw/acpi/bios-linker-loader.c
> >> @@ -49,6 +49,7 @@ struct BiosLinkerLoaderEntry {
> >>             char file[BIOS_LINKER_LOADER_FILESZ];
> >>             uint32_t align;
> >>             uint8_t zone;
> >> +            uint32_t padding;  
> > I’m a little wary of doing this - in a packed structure this new field will be mis-aligned.  
> >>         } alloc;
> >> 
> >>         /*
> >> @@ -62,6 +63,7 @@ struct BiosLinkerLoaderEntry {
> >>             char src_file[BIOS_LINKER_LOADER_FILESZ];
> >>             uint32_t offset;
> >>             uint8_t size;
> >> +            uint32_t padding;
> >>         } pointer;
> >> 
> >>         /*
> >> @@ -76,10 +78,25 @@ struct BiosLinkerLoaderEntry {
> >>             uint32_t offset;
> >>             uint32_t start;
> >>             uint32_t length;
> >> +            uint32_t padding;
> >>         } cksum;
> >> 
> >> +        /*
> >> +         * COMMAND_WRITE_POINTER - write the fw_cfg file (originating from
> >> +         * @dest_file) at @wr_pointer.offset, by writing 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];
> >> +        char pad[120];  
> > wr_pointer is 121 (56 + 56 + 32 + 32 + 1), so 124 still makes sense, doesn’t it? (also, 124 + 4 from command) % 8 == 0, so it’s nicely aligned.  
> I mean (56 + 56 + 4 + 4 + 1), of course :)
I' was wrong, as Laszlo pointed out pad is a member of union so it
as the biggest member defines total size of union/struct
as far as new command doesn't exceed 124 bytes size.



> >>     };
> >> } QEMU_PACKED;
> >> typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
> >> 
> >>   
> >>> I'm mostly concerned we are adding new features to something
> >>> that has been through 25 revisions already.  
> >> It's ABI so it's worth extra effort,
> >> it looks like only one more revision is left and there is
> >> about a week left to post and merge it.
> >>   
> >>> 
> >>>   
> >>>>>   
> >>>>>>> 
> >>>>>>>   
> >>>>>>>> 
> >>>>>>>>   
> >>>>>>>>> (1) So, the above looks correct, but please replace "adding" with
> >>>>>>>>> "storing", and "unsigned addition" with "store".
> >>>>>>>>> 
> >>>>>>>>> Side point: the case for ADD_POINTER is different; there we patch
> >>>>>>>>> several individual ACPI objects. The fact that I requested explicit
> >>>>>>>>> addition within the ADDR method, as opposed to pre-setting VGIA to a
> >>>>>>>>> nonzero offset, is an *incidental* limitation (coming from the OVMF ACPI
> >>>>>>>>> SDT header probe suppressor), and we'll likely fix that up later, with
> >>>>>>>>> ALLOCATE command hints or something like that. However, in
> >>>>>>>>> WRITE_POINTER, asking for the exact allocation address of "src_file" is
> >>>>>>>>> an *inherent* characteristic.
> >>>>>>>>> 
> >>>>>>>>> For reference, this is the command's description from the (not as yet
> >>>>>>>>> posted) OVMF series:
> >>>>>>>>> 
> >>>>>>>>> // QemuLoaderCmdWritePointer: the bytes at
> >>>>>>>>> // [PointerOffset..PointerOffset+PointerSize) in the writeable fw_cfg
> >>>>>>>>> // file PointerFile are to receive the absolute address of PointeeFile,
> >>>>>>>>> // as allocated and downloaded by the firmware. Store the base address
> >>>>>>>>> // of where PointeeFile's contents have been placed (when
> >>>>>>>>> // QemuLoaderCmdAllocate has been executed for PointeeFile) to this
> >>>>>>>>> // portion of PointerFile.
> >>>>>>>>> //
> >>>>>>>>> // This command is similar to QemuLoaderCmdAddPointer; the difference is
> >>>>>>>>> // that the "pointer to patch" does not exist in guest-physical address
> >>>>>>>>> // space, only in "fw_cfg file space". In addition, the "pointer to
> >>>>>>>>> // patch" is not initialized by QEMU with a possibly nonzero offset
> >>>>>>>>> // value: the base address of the memory allocated for downloading
> >>>>>>>>> // PointeeFile shall not increment the pointer, but overwrite it.
> >>>>>>>>> 
> >>>>>>>>> In the last SeaBIOS patch series, namely
> >>>>>>>>> 
> >>>>>>>>> [SeaBIOS] [PATCH v3 2/2] QEMU fw_cfg: Add command to write back address
> >>>>>>>>>                         of file
> >>>>>>>>> 
> >>>>>>>>> function romfile_loader_write_pointer() implemented just that plain
> >>>>>>>>> store (not an addition), and that was exactly right.
> >>>>>>>>> 
> >>>>>>>>> Continuing:
> >>>>>>>>>   
> >>>>>>>>>>> +        struct {
> >>>>>>>>>>> +            char dest_file[BIOS_LINKER_LOADER_FILESZ];
> >>>>>>>>>>> +            char src_file[BIOS_LINKER_LOADER_FILESZ];
> >>>>>>>>>>> +            uint32_t offset;
> >>>>>>>>>>> +            uint8_t size;
> >>>>>>>>>>> +        } wr_pointer;
> >>>>>>>>>>> +
> >>>>>>>>>>>         /* padding */
> >>>>>>>>>>>         char pad[124];
> >>>>>>>>>>>     };
> >>>>>>>>>>> @@ -85,9 +98,10 @@ struct BiosLinkerLoaderEntry {
> >>>>>>>>>>> typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
> >>>>>>>>>>> 
> >>>>>>>>>>> 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_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 +292,41 @@ 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
> >>>>>>>>>>> + */
> >>>>>>>>>>> +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)          
> >>>>>>>>>> API is missing "src_offset" even though it's not used in this series,
> >>>>>>>>>> a patch on top to fix it up is ok for me as far as Seabios/OVMF
> >>>>>>>>>> counterpart can handle src_offset correctly from starters.          
> >>>>>>>>> 
> >>>>>>>>> According to the above, it is the right thing not to add "src_offset"
> >>>>>>>>> here. The documentation on the command is slightly incorrect (and causes
> >>>>>>>>> confusion), but the helper function's signature and comments are okay.
> >>>>>>>>>   
> >>>>>>>>>>   
> >>>>>>>>>>> +{
> >>>>>>>>>>> +    BiosLinkerLoaderEntry entry;
> >>>>>>>>>>> +    const BiosLinkerFileEntry *source_file =
> >>>>>>>>>>> +        bios_linker_find_file(linker, src_file);
> >>>>>>>>>>> +
> >>>>>>>>>>> +    assert(source_file);          
> >>>>>>>>> 
> >>>>>>>>> I wish we kept the following asserts from bios_linker_loader_add_pointer():
> >>>>>>>>> 
> >>>>>>>>>    assert(dst_patched_offset < dst_file->blob->len);
> >>>>>>>>>    assert(dst_patched_offset + dst_patched_size <= dst_file->blob->len);
> >>>>>>>>> 
> >>>>>>>>> Namely, just because the dst_file is never supposed to be downloaded by
> >>>>>>>>> the firmware, it still remains a requirement that the "dst file offset
> >>>>>>>>> range" that is to be rewritten *do fall* within the dst file.
> >>>>>>>>> 
> >>>>>>>>> Nonetheless, this is not critical. (OVMF at least verifies it anyway.)
> >>>>>>>>> 
> >>>>>>>>> Summary (from my side anyway): I feel that the documentation of the new
> >>>>>>>>> command is very important. Please fix it up as suggested under (1), in
> >>>>>>>>> v7. Regarding the asserts, I'll let you decide.
> >>>>>>>>> 
> >>>>>>>>> With the documentation fixed up:
> >>>>>>>>> 
> >>>>>>>>> Reviewed-by: Laszlo Ersek <lersek@redhat.com <mailto:lersek@redhat.com>>
> >>>>>>>>> 
> >>>>>>>>> (If you don't wish to post a v7, I'm also completely fine if Michael or
> >>>>>>>>> someone else fixes up the docs as proposed in (1), before committing the
> >>>>>>>>> patch.)
> >>>>>>>>> 
> >>>>>>>>> Thanks!
> >>>>>>>>> Laszlo
> >>>>>>>>>   
> >>>>>>>>>>> +    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.offset = cpu_to_le32(dst_patched_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);
> >>>>>>>>>>> +}
> >>>>>>>>>>> diff --git a/include/hw/acpi/bios-linker-loader.h b/include/hw/acpi/bios-linker-loader.h
> >>>>>>>>>>> index fa1e5d1..f9ba5d6 100644
> >>>>>>>>>>> --- a/include/hw/acpi/bios-linker-loader.h
> >>>>>>>>>>> +++ b/include/hw/acpi/bios-linker-loader.h
> >>>>>>>>>>> @@ -26,5 +26,11 @@ 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);
> >>>>>>>>>>> +
> >>>>>>>>>>> void bios_linker_loader_cleanup(BIOSLinker *linker);
> >>>>>>>>>>> #endif          
> 

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID
  2017-02-15 20:52     ` Laszlo Ersek
  2017-02-16  6:10       ` Ben Warren
@ 2017-02-16 12:08       ` Igor Mammedov
  2017-02-16 13:29         ` Laszlo Ersek
  1 sibling, 1 reply; 62+ messages in thread
From: Igor Mammedov @ 2017-02-16 12:08 UTC (permalink / raw)
  To: Laszlo Ersek; +Cc: Michael S. Tsirkin, ben, qemu-devel

On Wed, 15 Feb 2017 21:52:55 +0100
Laszlo Ersek <lersek@redhat.com> wrote:

> On 02/15/17 21:09, Michael S. Tsirkin wrote:
> > On Wed, Feb 15, 2017 at 08:47:48PM +0100, Laszlo Ersek wrote:  
> 
> [snip]
> 
> >> For patches #1, #3, #4 and #5:
> >>
> >> Tested-by: Laszlo Ersek <lersek@redhat.com>
> >>
> >> I'll soon post the OVMF patches.
> >>
> >> Thanks!
> >> Laszlo  
> > 
> > 
> > How do you feel about Igor's request to change WRITE_POINTER to add
> > offset in there, so guest can pass in the address of GUID and
> > not start of table? Would that be a lot of work to add?  
> 
> I think it's doable in practice: simply add a constant from the command
> itself, for passing the value back to QEMU, and also for saving the
> fw_cfg write commend for S3 resume time.
> 
> But, I disagree with it from a design POV.
> 
> Igor's point is:
> 
> > Math complicates QEMU code though and not only QMEMU but AML code as
> > well.  
> 
> As I understand it, the goal is to push the addition to the firmware
> (which is "one place"), rather than having to implement it twice in
> QEMU, i.e., in two places ((a) native QEMU logic, (b) AML generator).
> 
> Here's my counter-argument:
> 
> (a) As I mentioned earlier, assume a complex communication structure
> between the guest OS and QEMU. Currently our shared structure consists
> of a single field (the GUID), but next time it might contain several fields.
> 
> For such a multi-field shared structure, QEMU will have to do manual
> offsetting into the guest RAM anyway, for accessing fields F1, F2, and
> F3. We will not create three separate WRITE_POINTER commands and let the
> firmware calculate and return the absolute GPAs of the fields F1, F2 and
> F3. Instead, there will be one WRITE_POINTER command, and QEMU will do
> the offsetting manually, minimally for fields F2 and F3.
> 
> "src_offset" looks tempting now only because we have a shared structure
> with only one field, the GUID at offset 40 decimal.

benefits of having src_offset from QEMU POV I see are:
 a) (biggest one) firmware and device code are clearly separated where:
     - VMGENID_GUID_OFFSET would be used only by firmware side, such as:
         WRITE_POINTER and AML addition to help OVMF detect non ACPI blob
     - device doesn't have to assume/or have a knowledge about
       layout of GUID blob except of size of data it's needs
       to access at location provided by WRITE_POINTER as v7 shows it.

 b) wrt shared blob I've envisioned slightly different approach,
    where multiple WRITE_POINTER commands are used instead of one
    with following workflow to extend shared blob:
     1) firmware part of QEMU (acpi-build.c):
          if (device_foo_present) {
             fw_cfg_add_file_callback('/etc/device_foo_addr', device_foo->addr_storage)

             shared_off = device_foo->align(next_free_shared_offset)
             WRITE_POINTER('/etc/device_foo_addr', 0,
                           '/etc/shared_blob, shared_off)

             next_free_shared_offset = shared_off + device_foo->data_size;
          }
     2) device_foo accesses data at device_foo->addr_storage directly
        * there is no need to spread knowledge of shared_blob
          layout to device code anymore.
        * no need to care where in shared_blob data will be placed,
        * shared space is used only when device is present
        * since there is no shared_writeback_blob, there isn't 
          need in mechanism to propagate written data to device
          or notify device about write
     
   drawback in this approach is that a device would consume
   a file slot if fw_cfg and space for WRITE_POINTER in
   linker file when present.

 
> (b) Regarding the runtime addition in the AML code:
as you pointed out WRITE_POINTER has nothing to do with addition
on AML side which is influenced by ADD_POINTER and OVMF and could
be fixed with flags down the road, so there is nothing to argue
about on this bullet.


> As discussed before, the main reason *now*, for not pointing VGIA (and
> other named integer objects) with ADD_POINTER commands directly to
> "meaningful" fields, is that OVMF probes the targets of ADD_POINTER
> commands for patterns that look like ACPI table headers. And, for the
> time being, we want to suppress any mis-recognitions by prepending some
> padding.
> 
> Igor was right to dislike this, and we agreed that *down the road* we
> should add allocation flags, or further allocation commands, to supplant
> this kind of heuristics in OVMF. But:
> 
> - we don't have time to do it now, plus
> 
> - please observe that the runtime addition in AML relates to the
>   ADD_POINTER and the ALLOCATE commands. It does not relate to
>   WRITE_POINTER at all.
> 
>   Whatever we change on WRITE_POINTER will do nothing for suppressing
>   OVMF's table header probing -- because that is tied to ADD_POINTER
>   --, therefore WRITE_POINTER tweaks cannot eliminate the "need to add"
>   in AML.
> 
> 
> In summary, I think the proposed WRITE_POINTER modification is
> implementable, but I think it will not pay off, because:
> 
> (a) for QEMU logic, it will not prove useful as soon as we have a
> multi-field shared structure (QEMU will have to add field offsets anyway),
> 
> (b) and for eliminating the AML addition (which is a consequence of the
> current ADD_POINTER handling in OVMF), it does nothing.
> 
> Thanks
> Laszlo

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID
  2017-02-16 12:08       ` Igor Mammedov
@ 2017-02-16 13:29         ` Laszlo Ersek
  2017-02-16 14:27           ` Igor Mammedov
  0 siblings, 1 reply; 62+ messages in thread
From: Laszlo Ersek @ 2017-02-16 13:29 UTC (permalink / raw)
  To: Igor Mammedov; +Cc: Michael S. Tsirkin, ben, qemu-devel

On 02/16/17 13:08, Igor Mammedov wrote:
> On Wed, 15 Feb 2017 21:52:55 +0100
> Laszlo Ersek <lersek@redhat.com> wrote:
> 
>> On 02/15/17 21:09, Michael S. Tsirkin wrote:
>>> On Wed, Feb 15, 2017 at 08:47:48PM +0100, Laszlo Ersek wrote:  
>>
>> [snip]
>>
>>>> For patches #1, #3, #4 and #5:
>>>>
>>>> Tested-by: Laszlo Ersek <lersek@redhat.com>
>>>>
>>>> I'll soon post the OVMF patches.
>>>>
>>>> Thanks!
>>>> Laszlo  
>>>
>>>
>>> How do you feel about Igor's request to change WRITE_POINTER to add
>>> offset in there, so guest can pass in the address of GUID and
>>> not start of table? Would that be a lot of work to add?  
>>
>> I think it's doable in practice: simply add a constant from the command
>> itself, for passing the value back to QEMU, and also for saving the
>> fw_cfg write commend for S3 resume time.
>>
>> But, I disagree with it from a design POV.
>>
>> Igor's point is:
>>
>>> Math complicates QEMU code though and not only QMEMU but AML code as
>>> well.  
>>
>> As I understand it, the goal is to push the addition to the firmware
>> (which is "one place"), rather than having to implement it twice in
>> QEMU, i.e., in two places ((a) native QEMU logic, (b) AML generator).
>>
>> Here's my counter-argument:
>>
>> (a) As I mentioned earlier, assume a complex communication structure
>> between the guest OS and QEMU. Currently our shared structure consists
>> of a single field (the GUID), but next time it might contain several fields.
>>
>> For such a multi-field shared structure, QEMU will have to do manual
>> offsetting into the guest RAM anyway, for accessing fields F1, F2, and
>> F3. We will not create three separate WRITE_POINTER commands and let the
>> firmware calculate and return the absolute GPAs of the fields F1, F2 and
>> F3. Instead, there will be one WRITE_POINTER command, and QEMU will do
>> the offsetting manually, minimally for fields F2 and F3.
>>
>> "src_offset" looks tempting now only because we have a shared structure
>> with only one field, the GUID at offset 40 decimal.
> 
> benefits of having src_offset from QEMU POV I see are:
>  a) (biggest one) firmware and device code are clearly separated where:
>      - VMGENID_GUID_OFFSET would be used only by firmware side, such as:
>          WRITE_POINTER and AML addition to help OVMF detect non ACPI blob
>      - device doesn't have to assume/or have a knowledge about
>        layout of GUID blob except of size of data it's needs
>        to access at location provided by WRITE_POINTER as v7 shows it.
> 
>  b) wrt shared blob I've envisioned slightly different approach,
>     where multiple WRITE_POINTER commands are used instead of one
>     with following workflow to extend shared blob:
>      1) firmware part of QEMU (acpi-build.c):
>           if (device_foo_present) {
>              fw_cfg_add_file_callback('/etc/device_foo_addr', device_foo->addr_storage)
> 
>              shared_off = device_foo->align(next_free_shared_offset)
>              WRITE_POINTER('/etc/device_foo_addr', 0,
>                            '/etc/shared_blob, shared_off)
> 
>              next_free_shared_offset = shared_off + device_foo->data_size;
>           }
>      2) device_foo accesses data at device_foo->addr_storage directly
>         * there is no need to spread knowledge of shared_blob
>           layout to device code anymore.

This is where I disagree, I think. Above you mention
device_foo->data_size. If "data_size" covers multiple fields, then the
device code itself will have to add relative offsets, for accessing
those different fields in guest RAM.

With the current command, the only difference is that the device code
has to receive a "base offset" from the outside, pointing into the
shared blob, and then add the field offsets to that. Thus the addition
cannot be avoided anyway.

You do have a point about sharing the same area between different
devices though. The above pseudo-code looks like a good pattern. This
way "acpi-build.c" won't have to hand out the shared blob offsets to
existing device instances directly; instead, the blob offsets are handed
down to the firmware, and the devices will get their struct base
addresses from the firmware. Using one WRITE_POINTER command for that,
per device, seems fine.

I'll update the OVMF patches.

Thanks
Laszlo

>         * no need to care where in shared_blob data will be placed,
>         * shared space is used only when device is present
>         * since there is no shared_writeback_blob, there isn't 
>           need in mechanism to propagate written data to device
>           or notify device about write
>      
>    drawback in this approach is that a device would consume
>    a file slot if fw_cfg and space for WRITE_POINTER in
>    linker file when present.
> 
>  
>> (b) Regarding the runtime addition in the AML code:
> as you pointed out WRITE_POINTER has nothing to do with addition
> on AML side which is influenced by ADD_POINTER and OVMF and could
> be fixed with flags down the road, so there is nothing to argue
> about on this bullet.
> 
> 
>> As discussed before, the main reason *now*, for not pointing VGIA (and
>> other named integer objects) with ADD_POINTER commands directly to
>> "meaningful" fields, is that OVMF probes the targets of ADD_POINTER
>> commands for patterns that look like ACPI table headers. And, for the
>> time being, we want to suppress any mis-recognitions by prepending some
>> padding.
>>
>> Igor was right to dislike this, and we agreed that *down the road* we
>> should add allocation flags, or further allocation commands, to supplant
>> this kind of heuristics in OVMF. But:
>>
>> - we don't have time to do it now, plus
>>
>> - please observe that the runtime addition in AML relates to the
>>   ADD_POINTER and the ALLOCATE commands. It does not relate to
>>   WRITE_POINTER at all.
>>
>>   Whatever we change on WRITE_POINTER will do nothing for suppressing
>>   OVMF's table header probing -- because that is tied to ADD_POINTER
>>   --, therefore WRITE_POINTER tweaks cannot eliminate the "need to add"
>>   in AML.
>>
>>
>> In summary, I think the proposed WRITE_POINTER modification is
>> implementable, but I think it will not pay off, because:
>>
>> (a) for QEMU logic, it will not prove useful as soon as we have a
>> multi-field shared structure (QEMU will have to add field offsets anyway),
>>
>> (b) and for eliminating the AML addition (which is a consequence of the
>> current ADD_POINTER handling in OVMF), it does nothing.
>>
>> Thanks
>> Laszlo
> 

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID
  2017-02-16 13:29         ` Laszlo Ersek
@ 2017-02-16 14:27           ` Igor Mammedov
  0 siblings, 0 replies; 62+ messages in thread
From: Igor Mammedov @ 2017-02-16 14:27 UTC (permalink / raw)
  To: Laszlo Ersek; +Cc: Michael S. Tsirkin, ben, qemu-devel

On Thu, 16 Feb 2017 14:29:28 +0100
Laszlo Ersek <lersek@redhat.com> wrote:

> On 02/16/17 13:08, Igor Mammedov wrote:
> > On Wed, 15 Feb 2017 21:52:55 +0100
> > Laszlo Ersek <lersek@redhat.com> wrote:
> >   
> >> On 02/15/17 21:09, Michael S. Tsirkin wrote:  
> >>> On Wed, Feb 15, 2017 at 08:47:48PM +0100, Laszlo Ersek wrote:    
> >>
> >> [snip]
> >>  
> >>>> For patches #1, #3, #4 and #5:
> >>>>
> >>>> Tested-by: Laszlo Ersek <lersek@redhat.com>
> >>>>
> >>>> I'll soon post the OVMF patches.
> >>>>
> >>>> Thanks!
> >>>> Laszlo    
> >>>
> >>>
> >>> How do you feel about Igor's request to change WRITE_POINTER to add
> >>> offset in there, so guest can pass in the address of GUID and
> >>> not start of table? Would that be a lot of work to add?    
> >>
> >> I think it's doable in practice: simply add a constant from the command
> >> itself, for passing the value back to QEMU, and also for saving the
> >> fw_cfg write commend for S3 resume time.
> >>
> >> But, I disagree with it from a design POV.
> >>
> >> Igor's point is:
> >>  
> >>> Math complicates QEMU code though and not only QMEMU but AML code as
> >>> well.    
> >>
> >> As I understand it, the goal is to push the addition to the firmware
> >> (which is "one place"), rather than having to implement it twice in
> >> QEMU, i.e., in two places ((a) native QEMU logic, (b) AML generator).
> >>
> >> Here's my counter-argument:
> >>
> >> (a) As I mentioned earlier, assume a complex communication structure
> >> between the guest OS and QEMU. Currently our shared structure consists
> >> of a single field (the GUID), but next time it might contain several fields.
> >>
> >> For such a multi-field shared structure, QEMU will have to do manual
> >> offsetting into the guest RAM anyway, for accessing fields F1, F2, and
> >> F3. We will not create three separate WRITE_POINTER commands and let the
> >> firmware calculate and return the absolute GPAs of the fields F1, F2 and
> >> F3. Instead, there will be one WRITE_POINTER command, and QEMU will do
> >> the offsetting manually, minimally for fields F2 and F3.
> >>
> >> "src_offset" looks tempting now only because we have a shared structure
> >> with only one field, the GUID at offset 40 decimal.  
> > 
> > benefits of having src_offset from QEMU POV I see are:
> >  a) (biggest one) firmware and device code are clearly separated where:
> >      - VMGENID_GUID_OFFSET would be used only by firmware side, such as:
> >          WRITE_POINTER and AML addition to help OVMF detect non ACPI blob
> >      - device doesn't have to assume/or have a knowledge about
> >        layout of GUID blob except of size of data it's needs
> >        to access at location provided by WRITE_POINTER as v7 shows it.
> > 
> >  b) wrt shared blob I've envisioned slightly different approach,
> >     where multiple WRITE_POINTER commands are used instead of one
> >     with following workflow to extend shared blob:
> >      1) firmware part of QEMU (acpi-build.c):
> >           if (device_foo_present) {
> >              fw_cfg_add_file_callback('/etc/device_foo_addr', device_foo->addr_storage)
> > 
> >              shared_off = device_foo->align(next_free_shared_offset)
> >              WRITE_POINTER('/etc/device_foo_addr', 0,
> >                            '/etc/shared_blob, shared_off)
> > 
> >              next_free_shared_offset = shared_off + device_foo->data_size;
> >           }
> >      2) device_foo accesses data at device_foo->addr_storage directly
> >         * there is no need to spread knowledge of shared_blob
> >           layout to device code anymore.  
> 
> This is where I disagree, I think. Above you mention
> device_foo->data_size. If "data_size" covers multiple fields, then the
> device code itself will have to add relative offsets, for accessing
> those different fields in guest RAM.
> 
> With the current command, the only difference is that the device code
> has to receive a "base offset" from the outside, pointing into the
> shared blob, and then add the field offsets to that. Thus the addition
> cannot be avoided anyway.
instead of explicit offsets for base offset, device will probably use
struct cast to access fields.


> You do have a point about sharing the same area between different
> devices though. The above pseudo-code looks like a good pattern. This
> way "acpi-build.c" won't have to hand out the shared blob offsets to
> existing device instances directly; instead, the blob offsets are handed
> down to the firmware, and the devices will get their struct base
> addresses from the firmware. Using one WRITE_POINTER command for that,
> per device, seems fine.
> 
> I'll update the OVMF patches.
Thanks!

> 
> Thanks
> Laszlo
> 
> >         * no need to care where in shared_blob data will be placed,
> >         * shared space is used only when device is present
> >         * since there is no shared_writeback_blob, there isn't 
> >           need in mechanism to propagate written data to device
> >           or notify device about write
> >      
> >    drawback in this approach is that a device would consume
> >    a file slot if fw_cfg and space for WRITE_POINTER in
> >    linker file when present.
> > 
> >    
> >> (b) Regarding the runtime addition in the AML code:  
> > as you pointed out WRITE_POINTER has nothing to do with addition
> > on AML side which is influenced by ADD_POINTER and OVMF and could
> > be fixed with flags down the road, so there is nothing to argue
> > about on this bullet.
> > 
> >   
> >> As discussed before, the main reason *now*, for not pointing VGIA (and
> >> other named integer objects) with ADD_POINTER commands directly to
> >> "meaningful" fields, is that OVMF probes the targets of ADD_POINTER
> >> commands for patterns that look like ACPI table headers. And, for the
> >> time being, we want to suppress any mis-recognitions by prepending some
> >> padding.
> >>
> >> Igor was right to dislike this, and we agreed that *down the road* we
> >> should add allocation flags, or further allocation commands, to supplant
> >> this kind of heuristics in OVMF. But:
> >>
> >> - we don't have time to do it now, plus
> >>
> >> - please observe that the runtime addition in AML relates to the
> >>   ADD_POINTER and the ALLOCATE commands. It does not relate to
> >>   WRITE_POINTER at all.
> >>
> >>   Whatever we change on WRITE_POINTER will do nothing for suppressing
> >>   OVMF's table header probing -- because that is tied to ADD_POINTER
> >>   --, therefore WRITE_POINTER tweaks cannot eliminate the "need to add"
> >>   in AML.
> >>
> >>
> >> In summary, I think the proposed WRITE_POINTER modification is
> >> implementable, but I think it will not pay off, because:
> >>
> >> (a) for QEMU logic, it will not prove useful as soon as we have a
> >> multi-field shared structure (QEMU will have to add field offsets anyway),
> >>
> >> (b) and for eliminating the AML addition (which is a consequence of the
> >> current ADD_POINTER handling in OVMF), it does nothing.
> >>
> >> Thanks
> >> Laszlo  
> >   
> 

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command
  2017-02-16 11:10                         ` Igor Mammedov
@ 2017-02-16 15:38                           ` Eric Blake
  0 siblings, 0 replies; 62+ messages in thread
From: Eric Blake @ 2017-02-16 15:38 UTC (permalink / raw)
  To: Igor Mammedov, Ben Warren; +Cc: Laszlo Ersek, qemu-devel, Michael S. Tsirkin

[-- Attachment #1: Type: text/plain, Size: 1426 bytes --]

[meta-comment]

On 02/16/2017 05:10 AM, Igor Mammedov wrote:
> On Wed, 15 Feb 2017 11:19:44 -0800
> Ben Warren <ben@skyportsystems.com> wrote:
> 
>>> On Feb 15, 2017, at 11:14 AM, Ben Warren <ben@skyportsystems.com> wrote:
...
>>>>>>>>>>>>> This is similar to the existing 'add pointer' functionality, but instead

13 levels of quoting...

>>>> +        char pad[120];  
>>> wr_pointer is 121 (56 + 56 + 32 + 32 + 1), so 124 still makes sense, doesn’t it? (also, 124 + 4 from command) % 8 == 0, so it’s nicely aligned.  
>> I mean (56 + 56 + 4 + 4 + 1), of course :)
> I' was wrong, as Laszlo pointed out pad is a member of union so it
> as the biggest member defines total size of union/struct
> as far as new command doesn't exceed 124 bytes size.

...and several pages of scrolling before I find the 3-line real content
of the message...

>>>>>>>>>>>>> #endif          

...buried by yet more scrolling through excessive quoting.  It's not
only okay, but ENCOURAGED, to trim your replies down to the relevant
portions (as I've done here), to quickly call attention to the content
you are adding to the thread.  Publicly archived lists have the benefit
that you don't have to full-quote the entire thread, because readers
interested in trimmed content can refer to the archives.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

end of thread, other threads:[~2017-02-16 15:38 UTC | newest]

Thread overview: 62+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-15  6:15 [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID ben
2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 1/7] linker-loader: Add new 'write pointer' command ben
2017-02-15 10:57   ` Igor Mammedov
2017-02-15 14:13     ` Laszlo Ersek
2017-02-15 14:17       ` Laszlo Ersek
2017-02-15 15:22       ` Igor Mammedov
2017-02-15 15:30         ` Michael S. Tsirkin
2017-02-15 15:56           ` Igor Mammedov
2017-02-15 16:39             ` Michael S. Tsirkin
2017-02-15 17:19               ` Laszlo Ersek
2017-02-15 17:43               ` Igor Mammedov
2017-02-15 17:54                 ` Ben Warren
2017-02-15 18:06                   ` Michael S. Tsirkin
2017-02-15 18:14                     ` Ben Warren
2017-02-15 18:35                       ` Igor Mammedov
2017-02-15 18:44                         ` Ben Warren
2017-02-15 21:09                           ` Michael S. Tsirkin
2017-02-15 18:04                 ` Michael S. Tsirkin
2017-02-15 18:24                   ` Igor Mammedov
2017-02-15 19:14                     ` Ben Warren
2017-02-15 19:19                       ` Ben Warren
2017-02-16 11:10                         ` Igor Mammedov
2017-02-16 15:38                           ` Eric Blake
2017-02-15 19:34                     ` Michael S. Tsirkin
2017-02-16  8:25                       ` Igor Mammedov
2017-02-16  9:49                         ` Laszlo Ersek
2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 2/7] docs: VM Generation ID device description ben
2017-02-15 11:07   ` Igor Mammedov
2017-02-15 14:26     ` Laszlo Ersek
2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 3/7] ACPI: Add vmgenid blob storage to the build tables ben
2017-02-15 11:15   ` Igor Mammedov
2017-02-15 14:30   ` Laszlo Ersek
2017-02-16  6:11     ` Ben Warren
2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 4/7] ACPI: Add Virtual Machine Generation ID support ben
2017-02-15 12:19   ` Igor Mammedov
2017-02-15 15:24     ` Laszlo Ersek
2017-02-15 16:07       ` Igor Mammedov
2017-02-15 16:40         ` Michael S. Tsirkin
2017-02-15 17:12           ` Ben Warren
2017-02-16  6:13       ` Ben Warren
2017-02-15 17:11     ` Ben Warren
2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 5/7] qmp/hmp: add query-vm-generation-id and 'info vm-generation-id' commands ben
2017-02-15 15:36   ` Laszlo Ersek
2017-02-16  6:13     ` Ben Warren
2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 6/7] tests: Move reusable ACPI macros into a new header file ben
2017-02-15 12:54   ` Igor Mammedov
2017-02-15 21:35   ` Eric Blake
2017-02-15 21:58     ` Ben Warren
2017-02-15 22:56       ` Eric Blake
2017-02-15 23:05         ` Ben Warren
2017-02-15  6:15 ` [Qemu-devel] [PATCH v6 7/7] tests: Add unit tests for the VM Generation ID feature ben
2017-02-15 13:13   ` Igor Mammedov
2017-02-16  6:15     ` Ben Warren
2017-02-15 19:47 ` [Qemu-devel] [PATCH v6 0/7] Add support for VM Generation ID Laszlo Ersek
2017-02-15 20:09   ` Michael S. Tsirkin
2017-02-15 20:15     ` Ben Warren
2017-02-15 20:52     ` Laszlo Ersek
2017-02-16  6:10       ` Ben Warren
2017-02-16  9:36         ` Laszlo Ersek
2017-02-16 12:08       ` Igor Mammedov
2017-02-16 13:29         ` Laszlo Ersek
2017-02-16 14:27           ` Igor Mammedov

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.