The QEMU_LOADER_WRITE_POINTER command instructs the firmware to write the
address of a field within a previously allocated/downloaded fw_cfg blob
into another (writeable) fw_cfg file at a specific offset.
Put differently, QEMU_LOADER_WRITE_POINTER propagates, to QEMU, the
address that QEMU_LOADER_ALLOCATE placed the designated fw_cfg blob at, as
adjusted for the given field inside the allocated blob.
The implementation is similar to that of QEMU_LOADER_ADD_POINTER. Since
here we "patch" a pointer object in "fw_cfg file space", not guest memory
space, we utilize the QemuFwCfgSkipBytes() and QemuFwCfgWriteBytes() APIs
completed in commit range 465663e9f128..7fcb73541299.
An interesting aspect is that QEMU_LOADER_WRITE_POINTER creates a
host-level reference to a guest memory location. Therefore, if we fail to
process the linker/loader script for any reason, we have to clear out
those references first, before we release the guest memory allocations in
response to the error.
Cc: Jordan Justen <jordan.l.justen@intel.com>
Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=359
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
---
OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c | 171 +++++++++++++++++++-
1 file changed, 168 insertions(+), 3 deletions(-)
diff --git a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
index 404589cad0b7..de827c2df204 100644
--- a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
+++ b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
@@ -350,10 +350,147 @@ ProcessCmdAddChecksum (
AddChecksum->ResultOffset, AddChecksum->Start, AddChecksum->Length));
return EFI_SUCCESS;
}
+/**
+ Process a QEMU_LOADER_WRITE_POINTER command.
+
+ @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to process.
+
+ @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
+ structures created thus far.
+
+ @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
+ WritePointer. Or, the WritePointer command
+ references a file unknown to Tracker or the
+ fw_cfg directory. Or, the pointer object to
+ rewrite has invalid location, size, or initial
+ relative value. Or, the pointer value to store
+ does not fit in the given pointer size.
+
+ @retval EFI_SUCCESS The pointer object inside the writeable fw_cfg
+ file has been written.
+**/
+STATIC
+EFI_STATUS
+ProcessCmdWritePointer (
+ IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer,
+ IN CONST ORDERED_COLLECTION *Tracker
+ )
+{
+ RETURN_STATUS Status;
+ FIRMWARE_CONFIG_ITEM PointerItem;
+ UINTN PointerItemSize;
+ ORDERED_COLLECTION_ENTRY *PointeeEntry;
+ BLOB *PointeeBlob;
+ UINT64 PointerValue;
+
+ if (WritePointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0' ||
+ WritePointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {
+ DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ Status = QemuFwCfgFindFile ((CONST CHAR8 *)WritePointer->PointerFile,
+ &PointerItem, &PointerItemSize);
+ PointeeEntry = OrderedCollectionFind (Tracker, WritePointer->PointeeFile);
+ if (RETURN_ERROR (Status) || PointeeEntry == NULL) {
+ DEBUG ((DEBUG_ERROR,
+ "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",
+ __FUNCTION__, WritePointer->PointerFile, WritePointer->PointeeFile));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ if ((WritePointer->PointerSize != 1 && WritePointer->PointerSize != 2 &&
+ WritePointer->PointerSize != 4 && WritePointer->PointerSize != 8) ||
+ (PointerItemSize < WritePointer->PointerSize) ||
+ (PointerItemSize - WritePointer->PointerSize <
+ WritePointer->PointerOffset)) {
+ DEBUG ((DEBUG_ERROR, "%a: invalid pointer location or size in \"%a\"\n",
+ __FUNCTION__, WritePointer->PointerFile));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ PointeeBlob = OrderedCollectionUserStruct (PointeeEntry);
+ PointerValue = WritePointer->PointeeOffset;
+ if (PointerValue >= PointeeBlob->Size) {
+ DEBUG ((DEBUG_ERROR, "%a: invalid PointeeOffset\n", __FUNCTION__));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ //
+ // The memory allocation system ensures that the address of the byte past the
+ // last byte of any allocated object is expressible (no wraparound).
+ //
+ ASSERT ((UINTN)PointeeBlob->Base <= MAX_ADDRESS - PointeeBlob->Size);
+
+ PointerValue += (UINT64)(UINTN)PointeeBlob->Base;
+ if (RShiftU64 (
+ RShiftU64 (PointerValue, WritePointer->PointerSize * 8 - 1), 1) != 0) {
+ DEBUG ((DEBUG_ERROR, "%a: pointer value unrepresentable in \"%a\"\n",
+ __FUNCTION__, WritePointer->PointerFile));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ QemuFwCfgSelectItem (PointerItem);
+ QemuFwCfgSkipBytes (WritePointer->PointerOffset);
+ QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);
+
+ //
+ // Because QEMU has now learned PointeeBlob->Base, we must mark PointeeBlob
+ // as unreleasable, for the case when the whole linker/loader script is
+ // handled successfully.
+ //
+ PointeeBlob->HostsOnlyTableData = FALSE;
+
+ DEBUG ((DEBUG_VERBOSE, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
+ "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n", __FUNCTION__,
+ WritePointer->PointerFile, WritePointer->PointeeFile,
+ WritePointer->PointerOffset, WritePointer->PointeeOffset,
+ WritePointer->PointerSize));
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Undo a QEMU_LOADER_WRITE_POINTER command.
+
+ This function revokes (zeroes out) a guest memory reference communicated to
+ QEMU earlier. The caller is responsible for invoking this function only on
+ such QEMU_LOADER_WRITE_POINTER commands that have been successfully processed
+ by ProcessCmdWritePointer().
+
+ @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.
+**/
+STATIC
+VOID
+UndoCmdWritePointer (
+ IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer
+ )
+{
+ RETURN_STATUS Status;
+ FIRMWARE_CONFIG_ITEM PointerItem;
+ UINTN PointerItemSize;
+ UINT64 PointerValue;
+
+ Status = QemuFwCfgFindFile ((CONST CHAR8 *)WritePointer->PointerFile,
+ &PointerItem, &PointerItemSize);
+ ASSERT_RETURN_ERROR (Status);
+
+ PointerValue = 0;
+ QemuFwCfgSelectItem (PointerItem);
+ QemuFwCfgSkipBytes (WritePointer->PointerOffset);
+ QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);
+
+ DEBUG ((DEBUG_VERBOSE,
+ "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__,
+ WritePointer->PointerFile, WritePointer->PointerOffset,
+ WritePointer->PointerSize));
+}
+
+
//
// We'll be saving the keys of installed tables so that we can roll them back
// in case of failure. 128 tables should be enough for anyone (TM).
//
#define INSTALLED_TABLES_MAX 128
@@ -559,10 +696,11 @@ InstallQemuFwCfgTables (
EFI_STATUS Status;
FIRMWARE_CONFIG_ITEM FwCfgItem;
UINTN FwCfgSize;
QEMU_LOADER_ENTRY *LoaderStart;
CONST QEMU_LOADER_ENTRY *LoaderEntry, *LoaderEnd;
+ CONST QEMU_LOADER_ENTRY *WritePointerSubsetEnd;
ORIGINAL_ATTRIBUTES *OriginalPciAttributes;
UINTN OriginalPciAttributesCount;
ORDERED_COLLECTION *Tracker;
UINTN *InstalledKey;
INT32 Installed;
@@ -595,10 +733,15 @@ InstallQemuFwCfgTables (
}
//
// first pass: process the commands
//
+ // "WritePointerSubsetEnd" points one past the last successful
+ // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first
+ // pass, no such command has been encountered yet.
+ //
+ WritePointerSubsetEnd = LoaderStart;
for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
switch (LoaderEntry->Type) {
case QemuLoaderCmdAllocate:
Status = ProcessCmdAllocate (&LoaderEntry->Command.Allocate, Tracker);
break;
@@ -611,25 +754,33 @@ InstallQemuFwCfgTables (
case QemuLoaderCmdAddChecksum:
Status = ProcessCmdAddChecksum (&LoaderEntry->Command.AddChecksum,
Tracker);
break;
+ case QemuLoaderCmdWritePointer:
+ Status = ProcessCmdWritePointer (&LoaderEntry->Command.WritePointer,
+ Tracker);
+ if (!EFI_ERROR (Status)) {
+ WritePointerSubsetEnd = LoaderEntry + 1;
+ }
+ break;
+
default:
DEBUG ((EFI_D_VERBOSE, "%a: unknown loader command: 0x%x\n",
__FUNCTION__, LoaderEntry->Type));
break;
}
if (EFI_ERROR (Status)) {
- goto FreeTracker;
+ goto RollbackWritePointersAndFreeTracker;
}
}
InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);
if (InstalledKey == NULL) {
Status = EFI_OUT_OF_RESOURCES;
- goto FreeTracker;
+ goto RollbackWritePointersAndFreeTracker;
}
//
// second pass: identify and install ACPI tables
//
@@ -656,11 +807,25 @@ InstallQemuFwCfgTables (
DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));
}
FreePool (InstalledKey);
-FreeTracker:
+RollbackWritePointersAndFreeTracker:
+ //
+ // In case of failure, revoke any allocation addresses that were communicated
+ // to QEMU previously, before we release all the blobs.
+ //
+ if (EFI_ERROR (Status)) {
+ LoaderEntry = WritePointerSubsetEnd;
+ while (LoaderEntry > LoaderStart) {
+ --LoaderEntry;
+ if (LoaderEntry->Type == QemuLoaderCmdWritePointer) {
+ UndoCmdWritePointer (&LoaderEntry->Command.WritePointer);
+ }
+ }
+ }
+
//
// Tear down the tracker infrastructure. Each fw_cfg blob will be left in
// place only if we're exiting with success and the blob hosts data that is
// not directly part of some ACPI table.
//
--
2.9.3
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
On 2017-02-16 12:41:36, Laszlo Ersek wrote:
> The QEMU_LOADER_WRITE_POINTER command instructs the firmware to write the
> address of a field within a previously allocated/downloaded fw_cfg blob
> into another (writeable) fw_cfg file at a specific offset.
>
> Put differently, QEMU_LOADER_WRITE_POINTER propagates, to QEMU, the
> address that QEMU_LOADER_ALLOCATE placed the designated fw_cfg blob at, as
> adjusted for the given field inside the allocated blob.
>
> The implementation is similar to that of QEMU_LOADER_ADD_POINTER. Since
> here we "patch" a pointer object in "fw_cfg file space", not guest memory
> space, we utilize the QemuFwCfgSkipBytes() and QemuFwCfgWriteBytes() APIs
> completed in commit range 465663e9f128..7fcb73541299.
>
> An interesting aspect is that QEMU_LOADER_WRITE_POINTER creates a
> host-level reference to a guest memory location. Therefore, if we fail to
> process the linker/loader script for any reason, we have to clear out
> those references first, before we release the guest memory allocations in
> response to the error.
>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=359
> Contributed-under: TianoCore Contribution Agreement 1.0
> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> ---
> OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c | 171 +++++++++++++++++++-
> 1 file changed, 168 insertions(+), 3 deletions(-)
>
> diff --git a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
> index 404589cad0b7..de827c2df204 100644
> --- a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
> +++ b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
> @@ -350,10 +350,147 @@ ProcessCmdAddChecksum (
> AddChecksum->ResultOffset, AddChecksum->Start, AddChecksum->Length));
> return EFI_SUCCESS;
> }
>
>
> +/**
> + Process a QEMU_LOADER_WRITE_POINTER command.
> +
> + @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to process.
> +
> + @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
> + structures created thus far.
> +
> + @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
> + WritePointer. Or, the WritePointer command
> + references a file unknown to Tracker or the
> + fw_cfg directory. Or, the pointer object to
> + rewrite has invalid location, size, or initial
> + relative value. Or, the pointer value to store
> + does not fit in the given pointer size.
> +
> + @retval EFI_SUCCESS The pointer object inside the writeable fw_cfg
> + file has been written.
> +**/
> +STATIC
> +EFI_STATUS
> +ProcessCmdWritePointer (
> + IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer,
> + IN CONST ORDERED_COLLECTION *Tracker
> + )
> +{
> + RETURN_STATUS Status;
> + FIRMWARE_CONFIG_ITEM PointerItem;
> + UINTN PointerItemSize;
> + ORDERED_COLLECTION_ENTRY *PointeeEntry;
> + BLOB *PointeeBlob;
> + UINT64 PointerValue;
> +
> + if (WritePointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0' ||
> + WritePointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {
> + DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));
> + return EFI_PROTOCOL_ERROR;
> + }
> +
> + Status = QemuFwCfgFindFile ((CONST CHAR8 *)WritePointer->PointerFile,
> + &PointerItem, &PointerItemSize);
> + PointeeEntry = OrderedCollectionFind (Tracker, WritePointer->PointeeFile);
> + if (RETURN_ERROR (Status) || PointeeEntry == NULL) {
> + DEBUG ((DEBUG_ERROR,
> + "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",
> + __FUNCTION__, WritePointer->PointerFile, WritePointer->PointeeFile));
> + return EFI_PROTOCOL_ERROR;
> + }
> +
> + if ((WritePointer->PointerSize != 1 && WritePointer->PointerSize != 2 &&
> + WritePointer->PointerSize != 4 && WritePointer->PointerSize != 8) ||
> + (PointerItemSize < WritePointer->PointerSize) ||
> + (PointerItemSize - WritePointer->PointerSize <
> + WritePointer->PointerOffset)) {
> + DEBUG ((DEBUG_ERROR, "%a: invalid pointer location or size in \"%a\"\n",
> + __FUNCTION__, WritePointer->PointerFile));
> + return EFI_PROTOCOL_ERROR;
> + }
> +
> + PointeeBlob = OrderedCollectionUserStruct (PointeeEntry);
> + PointerValue = WritePointer->PointeeOffset;
> + if (PointerValue >= PointeeBlob->Size) {
> + DEBUG ((DEBUG_ERROR, "%a: invalid PointeeOffset\n", __FUNCTION__));
> + return EFI_PROTOCOL_ERROR;
> + }
> +
> + //
> + // The memory allocation system ensures that the address of the byte past the
> + // last byte of any allocated object is expressible (no wraparound).
> + //
> + ASSERT ((UINTN)PointeeBlob->Base <= MAX_ADDRESS - PointeeBlob->Size);
> +
> + PointerValue += (UINT64)(UINTN)PointeeBlob->Base;
> + if (RShiftU64 (
> + RShiftU64 (PointerValue, WritePointer->PointerSize * 8 - 1), 1) != 0) {
What do you think of this instead?
if (WritePointer->PointerSize < 8 &&
RShiftU64 (PointerValue, WritePointer->PointerSize * 8) != 0) {
Patches 1-4 Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
> + DEBUG ((DEBUG_ERROR, "%a: pointer value unrepresentable in \"%a\"\n",
> + __FUNCTION__, WritePointer->PointerFile));
> + return EFI_PROTOCOL_ERROR;
> + }
> +
> + QemuFwCfgSelectItem (PointerItem);
> + QemuFwCfgSkipBytes (WritePointer->PointerOffset);
> + QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);
> +
> + //
> + // Because QEMU has now learned PointeeBlob->Base, we must mark PointeeBlob
> + // as unreleasable, for the case when the whole linker/loader script is
> + // handled successfully.
> + //
> + PointeeBlob->HostsOnlyTableData = FALSE;
> +
> + DEBUG ((DEBUG_VERBOSE, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
> + "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n", __FUNCTION__,
> + WritePointer->PointerFile, WritePointer->PointeeFile,
> + WritePointer->PointerOffset, WritePointer->PointeeOffset,
> + WritePointer->PointerSize));
> + return EFI_SUCCESS;
> +}
> +
> +
> +/**
> + Undo a QEMU_LOADER_WRITE_POINTER command.
> +
> + This function revokes (zeroes out) a guest memory reference communicated to
> + QEMU earlier. The caller is responsible for invoking this function only on
> + such QEMU_LOADER_WRITE_POINTER commands that have been successfully processed
> + by ProcessCmdWritePointer().
> +
> + @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.
> +**/
> +STATIC
> +VOID
> +UndoCmdWritePointer (
> + IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer
> + )
> +{
> + RETURN_STATUS Status;
> + FIRMWARE_CONFIG_ITEM PointerItem;
> + UINTN PointerItemSize;
> + UINT64 PointerValue;
> +
> + Status = QemuFwCfgFindFile ((CONST CHAR8 *)WritePointer->PointerFile,
> + &PointerItem, &PointerItemSize);
> + ASSERT_RETURN_ERROR (Status);
> +
> + PointerValue = 0;
> + QemuFwCfgSelectItem (PointerItem);
> + QemuFwCfgSkipBytes (WritePointer->PointerOffset);
> + QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);
> +
> + DEBUG ((DEBUG_VERBOSE,
> + "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__,
> + WritePointer->PointerFile, WritePointer->PointerOffset,
> + WritePointer->PointerSize));
> +}
> +
> +
> //
> // We'll be saving the keys of installed tables so that we can roll them back
> // in case of failure. 128 tables should be enough for anyone (TM).
> //
> #define INSTALLED_TABLES_MAX 128
> @@ -559,10 +696,11 @@ InstallQemuFwCfgTables (
> EFI_STATUS Status;
> FIRMWARE_CONFIG_ITEM FwCfgItem;
> UINTN FwCfgSize;
> QEMU_LOADER_ENTRY *LoaderStart;
> CONST QEMU_LOADER_ENTRY *LoaderEntry, *LoaderEnd;
> + CONST QEMU_LOADER_ENTRY *WritePointerSubsetEnd;
> ORIGINAL_ATTRIBUTES *OriginalPciAttributes;
> UINTN OriginalPciAttributesCount;
> ORDERED_COLLECTION *Tracker;
> UINTN *InstalledKey;
> INT32 Installed;
> @@ -595,10 +733,15 @@ InstallQemuFwCfgTables (
> }
>
> //
> // first pass: process the commands
> //
> + // "WritePointerSubsetEnd" points one past the last successful
> + // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first
> + // pass, no such command has been encountered yet.
> + //
> + WritePointerSubsetEnd = LoaderStart;
> for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
> switch (LoaderEntry->Type) {
> case QemuLoaderCmdAllocate:
> Status = ProcessCmdAllocate (&LoaderEntry->Command.Allocate, Tracker);
> break;
> @@ -611,25 +754,33 @@ InstallQemuFwCfgTables (
> case QemuLoaderCmdAddChecksum:
> Status = ProcessCmdAddChecksum (&LoaderEntry->Command.AddChecksum,
> Tracker);
> break;
>
> + case QemuLoaderCmdWritePointer:
> + Status = ProcessCmdWritePointer (&LoaderEntry->Command.WritePointer,
> + Tracker);
> + if (!EFI_ERROR (Status)) {
> + WritePointerSubsetEnd = LoaderEntry + 1;
> + }
> + break;
> +
> default:
> DEBUG ((EFI_D_VERBOSE, "%a: unknown loader command: 0x%x\n",
> __FUNCTION__, LoaderEntry->Type));
> break;
> }
>
> if (EFI_ERROR (Status)) {
> - goto FreeTracker;
> + goto RollbackWritePointersAndFreeTracker;
> }
> }
>
> InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);
> if (InstalledKey == NULL) {
> Status = EFI_OUT_OF_RESOURCES;
> - goto FreeTracker;
> + goto RollbackWritePointersAndFreeTracker;
> }
>
> //
> // second pass: identify and install ACPI tables
> //
> @@ -656,11 +807,25 @@ InstallQemuFwCfgTables (
> DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));
> }
>
> FreePool (InstalledKey);
>
> -FreeTracker:
> +RollbackWritePointersAndFreeTracker:
> + //
> + // In case of failure, revoke any allocation addresses that were communicated
> + // to QEMU previously, before we release all the blobs.
> + //
> + if (EFI_ERROR (Status)) {
> + LoaderEntry = WritePointerSubsetEnd;
> + while (LoaderEntry > LoaderStart) {
> + --LoaderEntry;
> + if (LoaderEntry->Type == QemuLoaderCmdWritePointer) {
> + UndoCmdWritePointer (&LoaderEntry->Command.WritePointer);
> + }
> + }
> + }
> +
> //
> // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
> // place only if we're exiting with success and the blob hosts data that is
> // not directly part of some ACPI table.
> //
> --
> 2.9.3
>
>
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
On 02/17/17 20:34, Jordan Justen wrote:
> On 2017-02-16 12:41:36, Laszlo Ersek wrote:
>> The QEMU_LOADER_WRITE_POINTER command instructs the firmware to write the
>> address of a field within a previously allocated/downloaded fw_cfg blob
>> into another (writeable) fw_cfg file at a specific offset.
>>
>> Put differently, QEMU_LOADER_WRITE_POINTER propagates, to QEMU, the
>> address that QEMU_LOADER_ALLOCATE placed the designated fw_cfg blob at, as
>> adjusted for the given field inside the allocated blob.
>>
>> The implementation is similar to that of QEMU_LOADER_ADD_POINTER. Since
>> here we "patch" a pointer object in "fw_cfg file space", not guest memory
>> space, we utilize the QemuFwCfgSkipBytes() and QemuFwCfgWriteBytes() APIs
>> completed in commit range 465663e9f128..7fcb73541299.
>>
>> An interesting aspect is that QEMU_LOADER_WRITE_POINTER creates a
>> host-level reference to a guest memory location. Therefore, if we fail to
>> process the linker/loader script for any reason, we have to clear out
>> those references first, before we release the guest memory allocations in
>> response to the error.
>>
>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=359
>> Contributed-under: TianoCore Contribution Agreement 1.0
>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>> ---
>> OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c | 171 +++++++++++++++++++-
>> 1 file changed, 168 insertions(+), 3 deletions(-)
>>
>> diff --git a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
>> index 404589cad0b7..de827c2df204 100644
>> --- a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
>> +++ b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
>> @@ -350,10 +350,147 @@ ProcessCmdAddChecksum (
>> AddChecksum->ResultOffset, AddChecksum->Start, AddChecksum->Length));
>> return EFI_SUCCESS;
>> }
>>
>>
>> +/**
>> + Process a QEMU_LOADER_WRITE_POINTER command.
>> +
>> + @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to process.
>> +
>> + @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
>> + structures created thus far.
>> +
>> + @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
>> + WritePointer. Or, the WritePointer command
>> + references a file unknown to Tracker or the
>> + fw_cfg directory. Or, the pointer object to
>> + rewrite has invalid location, size, or initial
>> + relative value. Or, the pointer value to store
>> + does not fit in the given pointer size.
>> +
>> + @retval EFI_SUCCESS The pointer object inside the writeable fw_cfg
>> + file has been written.
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +ProcessCmdWritePointer (
>> + IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer,
>> + IN CONST ORDERED_COLLECTION *Tracker
>> + )
>> +{
>> + RETURN_STATUS Status;
>> + FIRMWARE_CONFIG_ITEM PointerItem;
>> + UINTN PointerItemSize;
>> + ORDERED_COLLECTION_ENTRY *PointeeEntry;
>> + BLOB *PointeeBlob;
>> + UINT64 PointerValue;
>> +
>> + if (WritePointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0' ||
>> + WritePointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {
>> + DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));
>> + return EFI_PROTOCOL_ERROR;
>> + }
>> +
>> + Status = QemuFwCfgFindFile ((CONST CHAR8 *)WritePointer->PointerFile,
>> + &PointerItem, &PointerItemSize);
>> + PointeeEntry = OrderedCollectionFind (Tracker, WritePointer->PointeeFile);
>> + if (RETURN_ERROR (Status) || PointeeEntry == NULL) {
>> + DEBUG ((DEBUG_ERROR,
>> + "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",
>> + __FUNCTION__, WritePointer->PointerFile, WritePointer->PointeeFile));
>> + return EFI_PROTOCOL_ERROR;
>> + }
>> +
>> + if ((WritePointer->PointerSize != 1 && WritePointer->PointerSize != 2 &&
>> + WritePointer->PointerSize != 4 && WritePointer->PointerSize != 8) ||
>> + (PointerItemSize < WritePointer->PointerSize) ||
>> + (PointerItemSize - WritePointer->PointerSize <
>> + WritePointer->PointerOffset)) {
>> + DEBUG ((DEBUG_ERROR, "%a: invalid pointer location or size in \"%a\"\n",
>> + __FUNCTION__, WritePointer->PointerFile));
>> + return EFI_PROTOCOL_ERROR;
>> + }
>> +
>> + PointeeBlob = OrderedCollectionUserStruct (PointeeEntry);
>> + PointerValue = WritePointer->PointeeOffset;
>> + if (PointerValue >= PointeeBlob->Size) {
>> + DEBUG ((DEBUG_ERROR, "%a: invalid PointeeOffset\n", __FUNCTION__));
>> + return EFI_PROTOCOL_ERROR;
>> + }
>> +
>> + //
>> + // The memory allocation system ensures that the address of the byte past the
>> + // last byte of any allocated object is expressible (no wraparound).
>> + //
>> + ASSERT ((UINTN)PointeeBlob->Base <= MAX_ADDRESS - PointeeBlob->Size);
>> +
>> + PointerValue += (UINT64)(UINTN)PointeeBlob->Base;
>> + if (RShiftU64 (
>> + RShiftU64 (PointerValue, WritePointer->PointerSize * 8 - 1), 1) != 0) {
>
> What do you think of this instead?
>
> if (WritePointer->PointerSize < 8 &&
> RShiftU64 (PointerValue, WritePointer->PointerSize * 8) != 0) {
Entirely valid, but then I should update ProcessCmdAddPointer()
similarly :) Also, I'll feel less smart about myself! ;)
If that's OK with you, I'll fix it up later. (I'll collect these warts
into another BZ, as long as they aren't critical.)
BTW, today I had a successful end-to-end (VM migration) test with Ben's
QEMU v8 series, and this one for OVMF; that is, VMGENID worked "in
action" with a Windows Server 2012 R2 guest. In the guest, I used a
small Windows application from a colleague to wait for the VMGENID to
change (and to report / query it).
>
> Patches 1-4 Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
Thank you very much!
Laszlo
>
>> + DEBUG ((DEBUG_ERROR, "%a: pointer value unrepresentable in \"%a\"\n",
>> + __FUNCTION__, WritePointer->PointerFile));
>> + return EFI_PROTOCOL_ERROR;
>> + }
>> +
>> + QemuFwCfgSelectItem (PointerItem);
>> + QemuFwCfgSkipBytes (WritePointer->PointerOffset);
>> + QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);
>> +
>> + //
>> + // Because QEMU has now learned PointeeBlob->Base, we must mark PointeeBlob
>> + // as unreleasable, for the case when the whole linker/loader script is
>> + // handled successfully.
>> + //
>> + PointeeBlob->HostsOnlyTableData = FALSE;
>> +
>> + DEBUG ((DEBUG_VERBOSE, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
>> + "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n", __FUNCTION__,
>> + WritePointer->PointerFile, WritePointer->PointeeFile,
>> + WritePointer->PointerOffset, WritePointer->PointeeOffset,
>> + WritePointer->PointerSize));
>> + return EFI_SUCCESS;
>> +}
>> +
>> +
>> +/**
>> + Undo a QEMU_LOADER_WRITE_POINTER command.
>> +
>> + This function revokes (zeroes out) a guest memory reference communicated to
>> + QEMU earlier. The caller is responsible for invoking this function only on
>> + such QEMU_LOADER_WRITE_POINTER commands that have been successfully processed
>> + by ProcessCmdWritePointer().
>> +
>> + @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.
>> +**/
>> +STATIC
>> +VOID
>> +UndoCmdWritePointer (
>> + IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer
>> + )
>> +{
>> + RETURN_STATUS Status;
>> + FIRMWARE_CONFIG_ITEM PointerItem;
>> + UINTN PointerItemSize;
>> + UINT64 PointerValue;
>> +
>> + Status = QemuFwCfgFindFile ((CONST CHAR8 *)WritePointer->PointerFile,
>> + &PointerItem, &PointerItemSize);
>> + ASSERT_RETURN_ERROR (Status);
>> +
>> + PointerValue = 0;
>> + QemuFwCfgSelectItem (PointerItem);
>> + QemuFwCfgSkipBytes (WritePointer->PointerOffset);
>> + QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);
>> +
>> + DEBUG ((DEBUG_VERBOSE,
>> + "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__,
>> + WritePointer->PointerFile, WritePointer->PointerOffset,
>> + WritePointer->PointerSize));
>> +}
>> +
>> +
>> //
>> // We'll be saving the keys of installed tables so that we can roll them back
>> // in case of failure. 128 tables should be enough for anyone (TM).
>> //
>> #define INSTALLED_TABLES_MAX 128
>> @@ -559,10 +696,11 @@ InstallQemuFwCfgTables (
>> EFI_STATUS Status;
>> FIRMWARE_CONFIG_ITEM FwCfgItem;
>> UINTN FwCfgSize;
>> QEMU_LOADER_ENTRY *LoaderStart;
>> CONST QEMU_LOADER_ENTRY *LoaderEntry, *LoaderEnd;
>> + CONST QEMU_LOADER_ENTRY *WritePointerSubsetEnd;
>> ORIGINAL_ATTRIBUTES *OriginalPciAttributes;
>> UINTN OriginalPciAttributesCount;
>> ORDERED_COLLECTION *Tracker;
>> UINTN *InstalledKey;
>> INT32 Installed;
>> @@ -595,10 +733,15 @@ InstallQemuFwCfgTables (
>> }
>>
>> //
>> // first pass: process the commands
>> //
>> + // "WritePointerSubsetEnd" points one past the last successful
>> + // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first
>> + // pass, no such command has been encountered yet.
>> + //
>> + WritePointerSubsetEnd = LoaderStart;
>> for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
>> switch (LoaderEntry->Type) {
>> case QemuLoaderCmdAllocate:
>> Status = ProcessCmdAllocate (&LoaderEntry->Command.Allocate, Tracker);
>> break;
>> @@ -611,25 +754,33 @@ InstallQemuFwCfgTables (
>> case QemuLoaderCmdAddChecksum:
>> Status = ProcessCmdAddChecksum (&LoaderEntry->Command.AddChecksum,
>> Tracker);
>> break;
>>
>> + case QemuLoaderCmdWritePointer:
>> + Status = ProcessCmdWritePointer (&LoaderEntry->Command.WritePointer,
>> + Tracker);
>> + if (!EFI_ERROR (Status)) {
>> + WritePointerSubsetEnd = LoaderEntry + 1;
>> + }
>> + break;
>> +
>> default:
>> DEBUG ((EFI_D_VERBOSE, "%a: unknown loader command: 0x%x\n",
>> __FUNCTION__, LoaderEntry->Type));
>> break;
>> }
>>
>> if (EFI_ERROR (Status)) {
>> - goto FreeTracker;
>> + goto RollbackWritePointersAndFreeTracker;
>> }
>> }
>>
>> InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);
>> if (InstalledKey == NULL) {
>> Status = EFI_OUT_OF_RESOURCES;
>> - goto FreeTracker;
>> + goto RollbackWritePointersAndFreeTracker;
>> }
>>
>> //
>> // second pass: identify and install ACPI tables
>> //
>> @@ -656,11 +807,25 @@ InstallQemuFwCfgTables (
>> DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));
>> }
>>
>> FreePool (InstalledKey);
>>
>> -FreeTracker:
>> +RollbackWritePointersAndFreeTracker:
>> + //
>> + // In case of failure, revoke any allocation addresses that were communicated
>> + // to QEMU previously, before we release all the blobs.
>> + //
>> + if (EFI_ERROR (Status)) {
>> + LoaderEntry = WritePointerSubsetEnd;
>> + while (LoaderEntry > LoaderStart) {
>> + --LoaderEntry;
>> + if (LoaderEntry->Type == QemuLoaderCmdWritePointer) {
>> + UndoCmdWritePointer (&LoaderEntry->Command.WritePointer);
>> + }
>> + }
>> + }
>> +
>> //
>> // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
>> // place only if we're exiting with success and the blob hosts data that is
>> // not directly part of some ACPI table.
>> //
>> --
>> 2.9.3
>>
>>
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
© 2016 - 2026 Red Hat, Inc.