From nobody Tue Jul 15 02:11:46 2025 Delivered-To: importer@patchew.org Received-SPF: none (zoho.com: 198.145.21.10 is neither permitted nor denied by domain of lists.01.org) client-ip=198.145.21.10; envelope-from=edk2-devel-bounces@lists.01.org; helo=ml01.01.org; Authentication-Results: mx.zohomail.com; spf=none (zoho.com: 198.145.21.10 is neither permitted nor denied by domain of lists.01.org) smtp.mailfrom=edk2-devel-bounces@lists.01.org Return-Path: Received: from ml01.01.org (ml01.01.org [198.145.21.10]) by mx.zohomail.com with SMTPS id 1501709142418209.1970464966895; Wed, 2 Aug 2017 14:25:42 -0700 (PDT) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 9493B209589F4; Wed, 2 Aug 2017 14:23:15 -0700 (PDT) Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 83C2D209589F8 for ; Wed, 2 Aug 2017 14:23:14 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id EA025C04B30E; Wed, 2 Aug 2017 21:25:24 +0000 (UTC) Received: from lacos-laptop-7.usersys.redhat.com (ovpn-116-47.phx2.redhat.com [10.3.116.47]) by smtp.corp.redhat.com (Postfix) with ESMTP id 9C6CE17B57; Wed, 2 Aug 2017 21:25:23 +0000 (UTC) X-Original-To: edk2-devel@lists.01.org DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com EA025C04B30E Authentication-Results: ext-mx07.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx07.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=lersek@redhat.com From: Laszlo Ersek To: edk2-devel-01 Date: Wed, 2 Aug 2017 23:24:53 +0200 Message-Id: <20170802212453.19221-13-lersek@redhat.com> In-Reply-To: <20170802212453.19221-1-lersek@redhat.com> References: <20170802212453.19221-1-lersek@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.31]); Wed, 02 Aug 2017 21:25:25 +0000 (UTC) Subject: [edk2] [PATCH 12/12] OvmfPkg/IoMmuDxe: Unmap(): recycle MAP_INFO after BusMasterCommonBuffer[64] X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Jordan Justen , Tom Lendacky , Ard Biesheuvel MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Errors-To: edk2-devel-bounces@lists.01.org Sender: "edk2-devel" X-ZohoMail: RSF_4 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" In order for Unmap() to be callable from ExitBootServices() event handler context (for cleaning up a BusMasterCommonBuffer[64] operation), we have to completely liberate the affected path in Unmap() from dynamic memory management. The last remaining piece is the release of the MAP_INFO structure. Rather than freeing it with FreePool(), recycle it to an internal list. Elements of this "free list" can be reused for any kind of Map() operation, and can be freed later, or recycled again. Cc: Ard Biesheuvel Cc: Brijesh Singh Cc: Jordan Justen Cc: Tom Lendacky Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Laszlo Ersek --- OvmfPkg/IoMmuDxe/AmdSevIoMmu.c | 48 ++++++++++++++++---- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/OvmfPkg/IoMmuDxe/AmdSevIoMmu.c b/OvmfPkg/IoMmuDxe/AmdSevIoMmu.c index ee94cd4efbe2..b2c825123fbb 100644 --- a/OvmfPkg/IoMmuDxe/AmdSevIoMmu.c +++ b/OvmfPkg/IoMmuDxe/AmdSevIoMmu.c @@ -20,22 +20,36 @@ =20 #include "AmdSevIoMmu.h" =20 +#define MAP_INFO_SIG SIGNATURE_64 ('M', 'A', 'P', '_', 'I', 'N', 'F', 'O') + typedef struct { + UINT64 Signature; + LIST_ENTRY Link; EDKII_IOMMU_OPERATION Operation; UINTN NumberOfBytes; UINTN NumberOfPages; EFI_PHYSICAL_ADDRESS CryptedAddress; EFI_PHYSICAL_ADDRESS PlainTextAddress; } MAP_INFO; =20 +// +// List of MAP_INFO structures recycled by Unmap(). +// +// Recycled MAP_INFO structures are equally good for future recycling and +// freeing. +// +STATIC LIST_ENTRY mRecycledMapInfos =3D INITIALIZE_LIST_HEAD_VARIABLE ( + mRecycledMapInfos + ); + #define COMMON_BUFFER_SIG SIGNATURE_64 ('C', 'M', 'N', 'B', 'U', 'F', 'F',= 'R') =20 // // The following structure enables Map() and Unmap() to perform in-place // decryption and encryption, respectively, for BusMasterCommonBuffer[64] // operations, without dynamic memory allocation or release. // // Both COMMON_BUFFER_HEADER and COMMON_BUFFER_HEADER.StashBuffer are allo= cated // by AllocateBuffer() and released by FreeBuffer(). // #pragma pack (1) @@ -89,178 +103,190 @@ EFIAPI IoMmuMap ( IN EDKII_IOMMU_PROTOCOL *This, IN EDKII_IOMMU_OPERATION Operation, IN VOID *HostAddress, IN OUT UINTN *NumberOfBytes, OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, OUT VOID **Mapping ) { EFI_STATUS Status; + LIST_ENTRY *RecycledMapInfo; MAP_INFO *MapInfo; EFI_ALLOCATE_TYPE AllocateType; COMMON_BUFFER_HEADER *CommonBufferHeader; VOID *DecryptionSource; =20 if (HostAddress =3D=3D NULL || NumberOfBytes =3D=3D NULL || DeviceAddres= s =3D=3D NULL || Mapping =3D=3D NULL) { return EFI_INVALID_PARAMETER; } =20 // // Allocate a MAP_INFO structure to remember the mapping when Unmap() is // called later. // - MapInfo =3D AllocatePool (sizeof (MAP_INFO)); - if (MapInfo =3D=3D NULL) { - Status =3D EFI_OUT_OF_RESOURCES; - goto Failed; + RecycledMapInfo =3D GetFirstNode (&mRecycledMapInfos); + if (RecycledMapInfo =3D=3D &mRecycledMapInfos) { + // + // No recycled MAP_INFO structure, allocate a new one. + // + MapInfo =3D AllocatePool (sizeof (MAP_INFO)); + if (MapInfo =3D=3D NULL) { + Status =3D EFI_OUT_OF_RESOURCES; + goto Failed; + } + } else { + MapInfo =3D CR (RecycledMapInfo, MAP_INFO, Link, MAP_INFO_SIG); + RemoveEntryList (RecycledMapInfo); } =20 // // Initialize the MAP_INFO structure, except the PlainTextAddress field // + ZeroMem (&MapInfo->Link, sizeof MapInfo->Link); + MapInfo->Signature =3D MAP_INFO_SIG; MapInfo->Operation =3D Operation; MapInfo->NumberOfBytes =3D *NumberOfBytes; MapInfo->NumberOfPages =3D EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes= ); MapInfo->CryptedAddress =3D (UINTN)HostAddress; =20 // // In the switch statement below, we point "MapInfo->PlainTextAddress" t= o the // plaintext buffer, according to Operation. We also set "DecryptionSour= ce". // MapInfo->PlainTextAddress =3D MAX_ADDRESS; AllocateType =3D AllocateAnyPages; DecryptionSource =3D (VOID *)(UINTN)MapInfo->CryptedAddress; switch (Operation) { // // For BusMasterRead[64] and BusMasterWrite[64] operations, a bounce buf= fer // is necessary regardless of whether the original (crypted) buffer cros= ses // the 4GB limit or not -- we have to allocate a separate plaintext buff= er. // The only variable is whether the plaintext buffer should be under 4GB. // case EdkiiIoMmuOperationBusMasterRead: case EdkiiIoMmuOperationBusMasterWrite: MapInfo->PlainTextAddress =3D BASE_4GB - 1; AllocateType =3D AllocateMaxAddress; // // fall through // case EdkiiIoMmuOperationBusMasterRead64: case EdkiiIoMmuOperationBusMasterWrite64: // // Allocate the implicit plaintext bounce buffer. // Status =3D gBS->AllocatePages ( AllocateType, EfiBootServicesData, MapInfo->NumberOfPages, &MapInfo->PlainTextAddress ); if (EFI_ERROR (Status)) { goto FreeMapInfo; } break; =20 // // For BusMasterCommonBuffer[64] operations, a to-be-plaintext buffer an= d a // stash buffer (for in-place decryption) have been allocated already, w= ith // AllocateBuffer(). We only check whether the address of the to-be-plai= ntext // buffer is low enough for the requested operation. // case EdkiiIoMmuOperationBusMasterCommonBuffer: if ((MapInfo->CryptedAddress > BASE_4GB) || (EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages) > BASE_4GB - MapInfo->CryptedAddress)) { // // CommonBuffer operations cannot be remapped. If the common buffer = is // above 4GB, then it is not possible to generate a mapping, so retu= rn an // error. // Status =3D EFI_UNSUPPORTED; goto FreeMapInfo; } // // fall through // case EdkiiIoMmuOperationBusMasterCommonBuffer64: // // The buffer at MapInfo->CryptedAddress comes from AllocateBuffer(). // MapInfo->PlainTextAddress =3D MapInfo->CryptedAddress; // // Stash the crypted data. // CommonBufferHeader =3D (COMMON_BUFFER_HEADER *)( (UINTN)MapInfo->CryptedAddress - EFI_PAGE_SIZE ); ASSERT (CommonBufferHeader->Signature =3D=3D COMMON_BUFFER_SIG); CopyMem ( CommonBufferHeader->StashBuffer, (VOID *)(UINTN)MapInfo->CryptedAddress, MapInfo->NumberOfBytes ); // // Point "DecryptionSource" to the stash buffer so that we decrypt // it to the original location, after the switch statement. // DecryptionSource =3D CommonBufferHeader->StashBuffer; break; =20 default: // // Operation is invalid // Status =3D EFI_INVALID_PARAMETER; goto FreeMapInfo; } =20 // // Clear the memory encryption mask on the plaintext buffer. // Status =3D MemEncryptSevClearPageEncMask ( 0, MapInfo->PlainTextAddress, MapInfo->NumberOfPages, TRUE ); ASSERT_EFI_ERROR (Status); if (EFI_ERROR (Status)) { CpuDeadLoop (); } =20 // // If this is a read operation from the Bus Master's point of view, // then copy the contents of the real buffer into the mapped buffer // so the Bus Master can read the contents of the real buffer. // // For BusMasterCommonBuffer[64] operations, the CopyMem() below will de= crypt // the original data (from the stash buffer) back to the original locati= on. // if (Operation =3D=3D EdkiiIoMmuOperationBusMasterRead || Operation =3D=3D EdkiiIoMmuOperationBusMasterRead64 || Operation =3D=3D EdkiiIoMmuOperationBusMasterCommonBuffer || Operation =3D=3D EdkiiIoMmuOperationBusMasterCommonBuffer64) { CopyMem ( (VOID *) (UINTN) MapInfo->PlainTextAddress, DecryptionSource, MapInfo->NumberOfBytes ); } =20 // // Populate output parameters. // *DeviceAddress =3D MapInfo->PlainTextAddress; *Mapping =3D MapInfo; =20 DEBUG (( DEBUG_VERBOSE, "%a PlainText 0x%Lx Crypted 0x%Lx Pages 0x%Lx Bytes 0x%Lx\n", __FUNCTION__, MapInfo->PlainTextAddress, MapInfo->CryptedAddress, (UINT64)MapInfo->NumberOfPages, (UINT64)MapInfo->NumberOfBytes )); =20 return EFI_SUCCESS; @@ -290,132 +316,138 @@ EFIAPI IoMmuUnmap ( IN EDKII_IOMMU_PROTOCOL *This, IN VOID *Mapping ) { MAP_INFO *MapInfo; EFI_STATUS Status; COMMON_BUFFER_HEADER *CommonBufferHeader; VOID *EncryptionTarget; =20 if (Mapping =3D=3D NULL) { return EFI_INVALID_PARAMETER; } =20 MapInfo =3D (MAP_INFO *)Mapping; =20 // // For BusMasterWrite[64] operations and BusMasterCommonBuffer[64] opera= tions // we have to encrypt the results, ultimately to the original place (i.e= ., // "MapInfo->CryptedAddress"). // // For BusMasterCommonBuffer[64] operations however, this encryption has= to // land in-place, so divert the encryption to the stash buffer first. // EncryptionTarget =3D (VOID *)(UINTN)MapInfo->CryptedAddress; =20 switch (MapInfo->Operation) { case EdkiiIoMmuOperationBusMasterCommonBuffer: case EdkiiIoMmuOperationBusMasterCommonBuffer64: ASSERT (MapInfo->PlainTextAddress =3D=3D MapInfo->CryptedAddress); =20 CommonBufferHeader =3D (COMMON_BUFFER_HEADER *)( (UINTN)MapInfo->PlainTextAddress - EFI_PAGE_SIZE ); ASSERT (CommonBufferHeader->Signature =3D=3D COMMON_BUFFER_SIG); EncryptionTarget =3D CommonBufferHeader->StashBuffer; // // fall through // =20 case EdkiiIoMmuOperationBusMasterWrite: case EdkiiIoMmuOperationBusMasterWrite64: CopyMem ( EncryptionTarget, (VOID *) (UINTN) MapInfo->PlainTextAddress, MapInfo->NumberOfBytes ); break; =20 default: // // nothing to encrypt after BusMasterRead[64] operations // break; } =20 DEBUG (( DEBUG_VERBOSE, "%a PlainText 0x%Lx Crypted 0x%Lx Pages 0x%Lx Bytes 0x%Lx\n", __FUNCTION__, MapInfo->PlainTextAddress, MapInfo->CryptedAddress, (UINT64)MapInfo->NumberOfPages, (UINT64)MapInfo->NumberOfBytes )); =20 // // Restore the memory encryption mask on the area we used to hold the // plaintext. // Status =3D MemEncryptSevSetPageEncMask ( 0, MapInfo->PlainTextAddress, MapInfo->NumberOfPages, TRUE ); ASSERT_EFI_ERROR (Status); if (EFI_ERROR (Status)) { CpuDeadLoop (); } =20 // // For BusMasterCommonBuffer[64] operations, copy the stashed data to the // original (now encrypted) location. // // For all other operations, fill the late bounce buffer (which existed = as // plaintext at some point) with zeros, and then release it. // if (MapInfo->Operation =3D=3D EdkiiIoMmuOperationBusMasterCommonBuffer || MapInfo->Operation =3D=3D EdkiiIoMmuOperationBusMasterCommonBuffer64= ) { CopyMem ( (VOID *)(UINTN)MapInfo->CryptedAddress, CommonBufferHeader->StashBuffer, MapInfo->NumberOfBytes ); + + // + // Recycle the MAP_INFO structure. + // + InsertTailList (&mRecycledMapInfos, &MapInfo->Link); } else { ZeroMem ( (VOID *)(UINTN)MapInfo->PlainTextAddress, EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages) ); gBS->FreePages (MapInfo->PlainTextAddress, MapInfo->NumberOfPages); + + // + // Free the MAP_INFO structure. + // + FreePool (MapInfo); } =20 - // - // Free the MAP_INFO structure. - // - FreePool (Mapping); return EFI_SUCCESS; } =20 /** Allocates pages that are suitable for an OperationBusMasterCommonBuffer = or OperationBusMasterCommonBuffer64 mapping. =20 @param This The protocol instance pointer. @param Type This parameter is not used and must be ign= ored. @param MemoryType The type of memory to allocate, EfiBootServicesData or EfiRuntimeServicesD= ata. @param Pages The number of pages to allocate. @param HostAddress A pointer to store the base system memory address of the allocated range. @param Attributes The requested bit mask of attributes for t= he allocated range. =20 @retval EFI_SUCCESS The requested memory pages were allocated. @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are MEMORY_WRITE_COMBINE and MEMORY_CACHED. @retval EFI_INVALID_PARAMETER One or more parameters are invalid. @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. =20 **/ --=20 2.13.1.3.g8be5a757fa67 _______________________________________________ edk2-devel mailing list edk2-devel@lists.01.org https://lists.01.org/mailman/listinfo/edk2-devel