From: Laszlo Ersek <lersek@redhat.com>
To: edk2-devel-01 <edk2-devel@ml01.01.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Subject: [PATCH 4/5] OvmfPkg/AcpiPlatformDxe: implement the QEMU_LOADER_WRITE_POINTER command
Date: Thu, 16 Feb 2017 21:41:36 +0100 [thread overview]
Message-ID: <20170216204137.30221-5-lersek@redhat.com> (raw)
In-Reply-To: <20170216204137.30221-1-lersek@redhat.com>
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
next prev parent reply other threads:[~2017-02-16 20:41 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-02-16 20:41 [PATCH 0/5] OvmfPkg: support QEMU_LOADER_WRITE_POINTER Laszlo Ersek
2017-02-16 20:41 ` [PATCH 1/5] OvmfPkg/AcpiPlatformDxe: prepare for QEMU_LOADER_WRITE_POINTER definitions Laszlo Ersek
2017-02-16 20:41 ` [PATCH 2/5] OvmfPkg/AcpiPlatformDxe: add " Laszlo Ersek
2017-02-16 20:41 ` [PATCH 3/5] OvmfPkg/AcpiPlatformDxe: rewrap license block in "QemuFwCfgAcpi.c" Laszlo Ersek
2017-02-16 20:41 ` Laszlo Ersek [this message]
2017-02-17 19:34 ` [PATCH 4/5] OvmfPkg/AcpiPlatformDxe: implement the QEMU_LOADER_WRITE_POINTER command Jordan Justen
2017-02-17 20:51 ` Laszlo Ersek
2017-02-16 20:41 ` [PATCH 5/5] OvmfPkg/AcpiPlatformDxe: replay QEMU_LOADER_WRITE_POINTER commands at S3 Laszlo Ersek
2017-02-17 21:25 ` Jordan Justen
2017-02-17 22:41 ` Laszlo Ersek
2017-02-17 22:48 ` Laszlo Ersek
2017-02-21 0:17 ` Jordan Justen
2017-02-21 12:15 ` Laszlo Ersek
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20170216204137.30221-5-lersek@redhat.com \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox