}\r
\r
\r
+/**\r
+ Process a QEMU_LOADER_WRITE_POINTER command.\r
+\r
+ @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to process.\r
+\r
+ @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
+ structures created thus far.\r
+\r
+ @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in\r
+ WritePointer. Or, the WritePointer command\r
+ references a file unknown to Tracker or the\r
+ fw_cfg directory. Or, the pointer object to\r
+ rewrite has invalid location, size, or initial\r
+ relative value. Or, the pointer value to store\r
+ does not fit in the given pointer size.\r
+\r
+ @retval EFI_SUCCESS The pointer object inside the writeable fw_cfg\r
+ file has been written.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+ProcessCmdWritePointer (\r
+ IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer,\r
+ IN CONST ORDERED_COLLECTION *Tracker\r
+ )\r
+{\r
+ RETURN_STATUS Status;\r
+ FIRMWARE_CONFIG_ITEM PointerItem;\r
+ UINTN PointerItemSize;\r
+ ORDERED_COLLECTION_ENTRY *PointeeEntry;\r
+ BLOB *PointeeBlob;\r
+ UINT64 PointerValue;\r
+\r
+ if (WritePointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0' ||\r
+ WritePointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {\r
+ DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
+ return EFI_PROTOCOL_ERROR;\r
+ }\r
+\r
+ Status = QemuFwCfgFindFile ((CONST CHAR8 *)WritePointer->PointerFile,\r
+ &PointerItem, &PointerItemSize);\r
+ PointeeEntry = OrderedCollectionFind (Tracker, WritePointer->PointeeFile);\r
+ if (RETURN_ERROR (Status) || PointeeEntry == NULL) {\r
+ DEBUG ((DEBUG_ERROR,\r
+ "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",\r
+ __FUNCTION__, WritePointer->PointerFile, WritePointer->PointeeFile));\r
+ return EFI_PROTOCOL_ERROR;\r
+ }\r
+\r
+ if ((WritePointer->PointerSize != 1 && WritePointer->PointerSize != 2 &&\r
+ WritePointer->PointerSize != 4 && WritePointer->PointerSize != 8) ||\r
+ (PointerItemSize < WritePointer->PointerSize) ||\r
+ (PointerItemSize - WritePointer->PointerSize <\r
+ WritePointer->PointerOffset)) {\r
+ DEBUG ((DEBUG_ERROR, "%a: invalid pointer location or size in \"%a\"\n",\r
+ __FUNCTION__, WritePointer->PointerFile));\r
+ return EFI_PROTOCOL_ERROR;\r
+ }\r
+\r
+ PointeeBlob = OrderedCollectionUserStruct (PointeeEntry);\r
+ PointerValue = WritePointer->PointeeOffset;\r
+ if (PointerValue >= PointeeBlob->Size) {\r
+ DEBUG ((DEBUG_ERROR, "%a: invalid PointeeOffset\n", __FUNCTION__));\r
+ return EFI_PROTOCOL_ERROR;\r
+ }\r
+\r
+ //\r
+ // The memory allocation system ensures that the address of the byte past the\r
+ // last byte of any allocated object is expressible (no wraparound).\r
+ //\r
+ ASSERT ((UINTN)PointeeBlob->Base <= MAX_ADDRESS - PointeeBlob->Size);\r
+\r
+ PointerValue += (UINT64)(UINTN)PointeeBlob->Base;\r
+ if (RShiftU64 (\r
+ RShiftU64 (PointerValue, WritePointer->PointerSize * 8 - 1), 1) != 0) {\r
+ DEBUG ((DEBUG_ERROR, "%a: pointer value unrepresentable in \"%a\"\n",\r
+ __FUNCTION__, WritePointer->PointerFile));\r
+ return EFI_PROTOCOL_ERROR;\r
+ }\r
+\r
+ QemuFwCfgSelectItem (PointerItem);\r
+ QemuFwCfgSkipBytes (WritePointer->PointerOffset);\r
+ QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);\r
+\r
+ //\r
+ // Because QEMU has now learned PointeeBlob->Base, we must mark PointeeBlob\r
+ // as unreleasable, for the case when the whole linker/loader script is\r
+ // handled successfully.\r
+ //\r
+ PointeeBlob->HostsOnlyTableData = FALSE;\r
+\r
+ DEBUG ((DEBUG_VERBOSE, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "\r
+ "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n", __FUNCTION__,\r
+ WritePointer->PointerFile, WritePointer->PointeeFile,\r
+ WritePointer->PointerOffset, WritePointer->PointeeOffset,\r
+ WritePointer->PointerSize));\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Undo a QEMU_LOADER_WRITE_POINTER command.\r
+\r
+ This function revokes (zeroes out) a guest memory reference communicated to\r
+ QEMU earlier. The caller is responsible for invoking this function only on\r
+ such QEMU_LOADER_WRITE_POINTER commands that have been successfully processed\r
+ by ProcessCmdWritePointer().\r
+\r
+ @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.\r
+**/\r
+STATIC\r
+VOID\r
+UndoCmdWritePointer (\r
+ IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer\r
+ )\r
+{\r
+ RETURN_STATUS Status;\r
+ FIRMWARE_CONFIG_ITEM PointerItem;\r
+ UINTN PointerItemSize;\r
+ UINT64 PointerValue;\r
+\r
+ Status = QemuFwCfgFindFile ((CONST CHAR8 *)WritePointer->PointerFile,\r
+ &PointerItem, &PointerItemSize);\r
+ ASSERT_RETURN_ERROR (Status);\r
+\r
+ PointerValue = 0;\r
+ QemuFwCfgSelectItem (PointerItem);\r
+ QemuFwCfgSkipBytes (WritePointer->PointerOffset);\r
+ QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);\r
+\r
+ DEBUG ((DEBUG_VERBOSE,\r
+ "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__,\r
+ WritePointer->PointerFile, WritePointer->PointerOffset,\r
+ WritePointer->PointerSize));\r
+}\r
+\r
+\r
//\r
// We'll be saving the keys of installed tables so that we can roll them back\r
// in case of failure. 128 tables should be enough for anyone (TM).\r
UINTN FwCfgSize;\r
QEMU_LOADER_ENTRY *LoaderStart;\r
CONST QEMU_LOADER_ENTRY *LoaderEntry, *LoaderEnd;\r
+ CONST QEMU_LOADER_ENTRY *WritePointerSubsetEnd;\r
ORIGINAL_ATTRIBUTES *OriginalPciAttributes;\r
UINTN OriginalPciAttributesCount;\r
ORDERED_COLLECTION *Tracker;\r
//\r
// first pass: process the commands\r
//\r
+ // "WritePointerSubsetEnd" points one past the last successful\r
+ // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first\r
+ // pass, no such command has been encountered yet.\r
+ //\r
+ WritePointerSubsetEnd = LoaderStart;\r
for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {\r
switch (LoaderEntry->Type) {\r
case QemuLoaderCmdAllocate:\r
Tracker);\r
break;\r
\r
+ case QemuLoaderCmdWritePointer:\r
+ Status = ProcessCmdWritePointer (&LoaderEntry->Command.WritePointer,\r
+ Tracker);\r
+ if (!EFI_ERROR (Status)) {\r
+ WritePointerSubsetEnd = LoaderEntry + 1;\r
+ }\r
+ break;\r
+\r
default:\r
DEBUG ((EFI_D_VERBOSE, "%a: unknown loader command: 0x%x\n",\r
__FUNCTION__, LoaderEntry->Type));\r
}\r
\r
if (EFI_ERROR (Status)) {\r
- goto FreeTracker;\r
+ goto RollbackWritePointersAndFreeTracker;\r
}\r
}\r
\r
InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);\r
if (InstalledKey == NULL) {\r
Status = EFI_OUT_OF_RESOURCES;\r
- goto FreeTracker;\r
+ goto RollbackWritePointersAndFreeTracker;\r
}\r
\r
//\r
\r
FreePool (InstalledKey);\r
\r
-FreeTracker:\r
+RollbackWritePointersAndFreeTracker:\r
+ //\r
+ // In case of failure, revoke any allocation addresses that were communicated\r
+ // to QEMU previously, before we release all the blobs.\r
+ //\r
+ if (EFI_ERROR (Status)) {\r
+ LoaderEntry = WritePointerSubsetEnd;\r
+ while (LoaderEntry > LoaderStart) {\r
+ --LoaderEntry;\r
+ if (LoaderEntry->Type == QemuLoaderCmdWritePointer) {\r
+ UndoCmdWritePointer (&LoaderEntry->Command.WritePointer);\r
+ }\r
+ }\r
+ }\r
+\r
//\r
// Tear down the tracker infrastructure. Each fw_cfg blob will be left in\r
// place only if we're exiting with success and the blob hosts data that is\r