From nobody Sat Nov 2 12:27:48 2024 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.zoho.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 1491289611298191.57727641047302; Tue, 4 Apr 2017 00:06:51 -0700 (PDT) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 0C8AC21DFA7BD; Tue, 4 Apr 2017 00:06:50 -0700 (PDT) Received: from mga04.intel.com (mga04.intel.com [192.55.52.120]) (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 31CD021A6F105 for ; Tue, 4 Apr 2017 00:06:48 -0700 (PDT) Received: from orsmga002.jf.intel.com ([10.7.209.21]) by fmsmga104.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 04 Apr 2017 00:06:47 -0700 Received: from jyao1-mobl.ccr.corp.intel.com ([10.254.214.148]) by orsmga002.jf.intel.com with ESMTP; 04 Apr 2017 00:06:45 -0700 X-Original-To: edk2-devel@lists.01.org DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=intel.com; i=@intel.com; q=dns/txt; s=intel; t=1491289608; x=1522825608; h=from:to:cc:subject:date:message-id:in-reply-to: references; bh=CKGNIk2aDHB5Dk4Ngo1NmxI+RvACw8HhoDFgZssjIis=; b=g52QLF4xCz7pSZSO6HWccRwonxDEk8BYX/vRqD8YZKOK8hqRzL9KhKVa bsZCiBcE0Fdb424zxh1+NhKrrz5b/A==; X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.36,274,1486454400"; d="scan'208";a="68864981" From: Jiewen Yao To: edk2-devel@lists.01.org Date: Tue, 4 Apr 2017 15:06:19 +0800 Message-Id: <1491289579-15888-4-git-send-email-jiewen.yao@intel.com> X-Mailer: git-send-email 2.7.4.windows.1 In-Reply-To: <1491289579-15888-1-git-send-email-jiewen.yao@intel.com> References: <1491289579-15888-1-git-send-email-jiewen.yao@intel.com> Subject: [edk2] [RFC] [PATCH V3 3/3] MdeModulePkg/PciBus: Add IOMMU support. 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: Ruiyu Ni , Brijesh Singh , Leo Duran , 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" The responsibility of PciBus driver is to set IOMMU attribute, because only PciBus knows which device submits DMA access request. PciBus driver guarantee that the request to PciHostBridge is IOMMU page aligned memory, as such PciHostBridge can allocate non-existent memory for device memory, to satisfy remap requirement. PciBus driver does not assume device address is same as the mapped host address, because IOMMU may remap it. Cc: Ruiyu Ni Cc: Leo Duran Cc: Brijesh Singh Cc: Ard Biesheuvel Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Jiewen Yao --- MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.c | 12 ++ MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h | 19 ++ MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf | 1 + MdeModulePkg/Bus/Pci/PciBusDxe/PciIo.c | 225 +++++++++++++++++++- 4 files changed, 247 insertions(+), 10 deletions(-) diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.c b/MdeModulePkg/Bus/Pci= /PciBusDxe/PciBus.c index f3be47a..c9ee4de 100644 --- a/MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.c +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.c @@ -42,6 +42,8 @@ UINT64 gAllZero = =3D 0; =20 EFI_PCI_PLATFORM_PROTOCOL *gPciPlatformProtocol; EFI_PCI_OVERRIDE_PROTOCOL *gPciOverrideProtocol; +EDKII_IOMMU_PROTOCOL *gIoMmuProtocol; +UINTN mIoMmuPageSize =3D 1; =20 =20 GLOBAL_REMOVE_IF_UNREFERENCED EFI_PCI_HOTPLUG_REQUEST_PROTOCOL mPciHotPlug= Request =3D { @@ -256,6 +258,16 @@ PciBusDriverBindingStart ( } =20 gBS->LocateProtocol ( + &gEdkiiIoMmuProtocolGuid, + NULL, + (VOID **) &gIoMmuProtocol + ); + if (gIoMmuProtocol !=3D NULL) { + gIoMmuProtocol->GetPageSize (gIoMmuProtocol, &mIoMmuPageSize); + ASSERT ((mIoMmuPageSize & (mIoMmuPageSize - 1)) =3D=3D 0); + } + + gBS->LocateProtocol ( &gEfiIncompatiblePciDeviceSupportProtocolGuid, NULL, (VOID **) &gIncompatiblePciDeviceSupport diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h b/MdeModulePkg/Bus/Pci= /PciBusDxe/PciBus.h index 39ba8b9..185d89c 100644 --- a/MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h @@ -32,6 +32,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER= EXPRESS OR IMPLIED. #include #include #include +#include =20 #include #include @@ -289,6 +290,8 @@ struct _PCI_IO_DEVICE { // This field is used to support this case. // UINT16 BridgeIoAlignment; + + LIST_ENTRY Maps; }; =20 #define PCI_IO_DEVICE_FROM_PCI_IO_THIS(a) \ @@ -304,6 +307,20 @@ struct _PCI_IO_DEVICE { CR (a, PCI_IO_DEVICE, LoadFile2, PCI_IO_DEVICE_SIGNATURE) =20 =20 +#define PCI_IO_MAP_INFO_SIGNATURE SIGNATURE_32 ('p', 'm', 'a', 'p') +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + EFI_PCI_IO_PROTOCOL_OPERATION Operation; + VOID *HostAddress; + EFI_PHYSICAL_ADDRESS DeviceAddress; + UINTN NumberOfBytes; + VOID *AlignedHostAddress; + UINTN AlignedNumberOfBytes; + VOID *MappedHostAddress; + VOID *PciRootBridgeIoMapping; +} PCI_IO_MAP_INFO; +#define PCI_IO_MAP_INFO_FROM_LINK(a) CR (a, PCI_IO_MAP_INFO, Link, PCI_IO_= MAP_INFO_SIGNATURE) =20 // // Global Variables @@ -321,6 +338,8 @@ extern EFI_PCI_PLATFORM_PROTOCOL *gP= ciPlatformProtocol; extern EFI_PCI_OVERRIDE_PROTOCOL *gPciOverrideProtocol; extern BOOLEAN mReserveIsaAliases; extern BOOLEAN mReserveVgaAliases; +extern EDKII_IOMMU_PROTOCOL *gIoMmuProtocol; +extern UINTN mIoMmuPageSize; =20 /** Macro that checks whether device is a GFX device. diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf b/MdeModulePkg/Bu= s/Pci/PciBusDxe/PciBusDxe.inf index a3ab11f..5da094f 100644 --- a/MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf @@ -95,6 +95,7 @@ gEfiPciRootBridgeIoProtocolGuid ## TO_START gEfiIncompatiblePciDeviceSupportProtocolGuid ## SOMETIMES_CONSUMES gEfiLoadFile2ProtocolGuid ## SOMETIMES_PRODUCES + gEdkiiIoMmuProtocolGuid ## SOMETIMES_CONSUMES =20 [FeaturePcd] gEfiMdeModulePkgTokenSpaceGuid.PcdPciBusHotplugDeviceSupport ## CON= SUMES diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciIo.c b/MdeModulePkg/Bus/Pci/= PciBusDxe/PciIo.c index f72598d..31b8c32 100644 --- a/MdeModulePkg/Bus/Pci/PciBusDxe/PciIo.c +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciIo.c @@ -58,6 +58,7 @@ InitializePciIoInstance ( ) { CopyMem (&PciIoDevice->PciIo, &mPciIoInterface, sizeof (EFI_PCI_IO_PROTO= COL)); + InitializeListHead (&PciIoDevice->Maps); } =20 /** @@ -936,6 +937,28 @@ PciIoCopyMem ( } =20 /** + Return if a value is aligned. + + @param Value the value to be checked + @param Alignment the alignment to be checked with. + + @retval TRUE The value is aligned + @retval FALSE The value is not aligned. +**/ +BOOLEAN +InternalIsAlgined ( + IN UINTN Value, + IN UINTN Alignment + ) +{ + if (Value =3D=3D ALIGN_VALUE(Value, Alignment)) { + return TRUE; + } else { + return FALSE; + } +} + +/** Provides the PCI controller-specific addresses needed to access system m= emory. =20 @param This A pointer to the EFI_PCI_IO_PROTOCOL insta= nce. @@ -967,6 +990,9 @@ PciIoMap ( { EFI_STATUS Status; PCI_IO_DEVICE *PciIoDevice; + PCI_IO_MAP_INFO *PciIoMapInfo; + UINT64 IoMmuAttribute; + EFI_STATUS RemapStatus; =20 PciIoDevice =3D PCI_IO_DEVICE_FROM_PCI_IO_THIS (This); =20 @@ -982,15 +1008,60 @@ PciIoMap ( Operation =3D (EFI_PCI_IO_PROTOCOL_OPERATION) (Operation + EfiPciOpera= tionBusMasterRead64); } =20 - Status =3D PciIoDevice->PciRootBridgeIo->Map ( - PciIoDevice->PciRootBridgeIo, - (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL= _OPERATION) Operation, - HostAddress, - NumberOfBytes, - DeviceAddress, - Mapping - ); - + if (gIoMmuProtocol !=3D NULL) { + PciIoMapInfo =3D AllocatePool (sizeof(*PciIoMapInfo)); + if (PciIoMapInfo =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + PciIoMapInfo->Signature =3D PCI_IO_MAP_INFO_SIGNATURE; + PciIoMapInfo->Operation =3D Operation; + PciIoMapInfo->NumberOfBytes =3D *NumberOfBytes; + PciIoMapInfo->DeviceAddress =3D *DeviceAddress; + PciIoMapInfo->HostAddress =3D HostAddress; + // + // For non common buffer, we always allocate a new memory if IOMMU exi= sts. + // because the original memory might not be DMA capable. + // + // For common buffer, it is not needed, because common buffer allocate= via PciIoAllocateBuffer. + // We cannot use AllocateAlignedPages here, because there might be mor= e restriction in PciIoAllocateBuffer(). + // + PciIoMapInfo->AlignedNumberOfBytes =3D ALIGN_VALUE (PciIoMapInfo->Nu= mberOfBytes, mIoMmuPageSize); + if (PciIoMapInfo->Operation !=3D EfiPciIoOperationBusMasterCommonBuffe= r) { + PciIoMapInfo->AlignedHostAddress =3D AllocateAlignedPages (EFI_SIZE_= TO_PAGES(PciIoMapInfo->AlignedNumberOfBytes), mIoMmuPageSize); + if (PciIoMapInfo->AlignedHostAddress =3D=3D NULL) { + FreePool (PciIoMapInfo); + return EFI_OUT_OF_RESOURCES; + } + } else { + // + // For common buffer, the HostAddress must be allocated via PciIoAll= ocateBuffer. + // + if (!InternalIsAlgined((UINTN)PciIoMapInfo->HostAddress, mIoMmuPageS= ize)) { + FreePool (PciIoMapInfo); + DEBUG ((DEBUG_ERROR, "PciIoMap - map unaligned common buffer with = IOMMU\n")); + return EFI_UNSUPPORTED; + } + PciIoMapInfo->AlignedHostAddress =3D PciIoMapInfo->HostAddress; + } + PciIoMapInfo->PciRootBridgeIoMapping =3D NULL; + Status =3D PciIoDevice->PciRootBridgeIo->Map ( + PciIoDevice->PciRootBridgeIo, + (EFI_PCI_ROOT_BRIDGE_IO_PROTOC= OL_OPERATION) Operation, + PciIoMapInfo->AlignedHostAddre= ss, + &PciIoMapInfo->AlignedNumberOf= Bytes, + &PciIoMapInfo->DeviceAddress, + &PciIoMapInfo->PciRootBridgeIo= Mapping + ); + } else { + Status =3D PciIoDevice->PciRootBridgeIo->Map ( + PciIoDevice->PciRootBridgeIo, + (EFI_PCI_ROOT_BRIDGE_IO_PROTOC= OL_OPERATION) Operation, + HostAddress, + NumberOfBytes, + DeviceAddress, + Mapping + ); + } if (EFI_ERROR (Status)) { REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_ERROR_CODE | EFI_ERROR_MINOR, @@ -999,6 +1070,63 @@ PciIoMap ( ); } =20 + if (gIoMmuProtocol !=3D NULL) { + if (EFI_ERROR(Status)) { + if (PciIoMapInfo->Operation !=3D EfiPciIoOperationBusMasterCommonBuf= fer) { + FreePages (PciIoMapInfo->AlignedHostAddress, EFI_SIZE_TO_PAGES(Pci= IoMapInfo->AlignedNumberOfBytes)); + } + FreePool (PciIoMapInfo); + } else { + *DeviceAddress =3D PciIoMapInfo->DeviceAddress; + *Mapping =3D PciIoMapInfo; + + switch (Operation) { + case EfiPciIoOperationBusMasterRead: + IoMmuAttribute =3D EDKII_IOMMU_ATTRIBUTE_READ; + break; + case EfiPciIoOperationBusMasterWrite: + IoMmuAttribute =3D EDKII_IOMMU_ATTRIBUTE_WRITE; + break; + case EfiPciIoOperationBusMasterCommonBuffer: + IoMmuAttribute =3D EDKII_IOMMU_ATTRIBUTE_READ | EDKII_IOMMU_ATTRIB= UTE_WRITE; + break; + default: + ASSERT(FALSE); + return EFI_INVALID_PARAMETER; + } + // + // PciHostBridge should map IOMMU page aligned HostAddress. + // + gIoMmuProtocol->SetAttribute ( + gIoMmuProtocol, + PciIoDevice->Handle, + PciIoMapInfo->DeviceAddress, + PciIoMapInfo->AlignedNumberOfBytes, + IoMmuAttribute + ); + // + // We need do copy mem after IoMmu->SetAttribute(), + // because it might change IOMMU state. + // + RemapStatus =3D gIoMmuProtocol->GetRemapAddress ( + gIoMmuProtocol, + PciIoDevice->Handle, + PciIoMapInfo->DeviceAddress, + &PciIoMapInfo->MappedHostAddress + ); + if (EFI_ERROR(RemapStatus)) { + PciIoMapInfo->MappedHostAddress =3D (VOID *)(UINTN)PciIoMapInfo->D= eviceAddress; + } + if (Operation =3D=3D EfiPciIoOperationBusMasterRead) { + CopyMem ( + PciIoMapInfo->MappedHostAddress, + PciIoMapInfo->HostAddress, + PciIoMapInfo->NumberOfBytes + ); + } + InsertTailList (&PciIoDevice->Maps, &PciIoMapInfo->Link); + } + } return Status; } =20 @@ -1021,9 +1149,48 @@ PciIoUnmap ( { EFI_STATUS Status; PCI_IO_DEVICE *PciIoDevice; + PCI_IO_MAP_INFO *PciIoMapInfo; + LIST_ENTRY *Link; =20 PciIoDevice =3D PCI_IO_DEVICE_FROM_PCI_IO_THIS (This); =20 + PciIoMapInfo =3D NULL; + if (gIoMmuProtocol !=3D NULL) { + PciIoMapInfo =3D NULL; + for (Link =3D GetFirstNode (&PciIoDevice->Maps) + ; !IsNull (&PciIoDevice->Maps, Link) + ; Link =3D GetNextNode (&PciIoDevice->Maps, Link) + ) { + PciIoMapInfo =3D PCI_IO_MAP_INFO_FROM_LINK (Link); + if (PciIoMapInfo =3D=3D Mapping) { + break; + } + } + // + // Mapping is not a valid value returned by Map() + // + if (PciIoMapInfo !=3D Mapping) { + DEBUG ((DEBUG_INFO, "PciIoUnmap - PciIoMapInfo not found!\n")); + return EFI_INVALID_PARAMETER; + } + RemoveEntryList (&PciIoMapInfo->Link); + Mapping =3D PciIoMapInfo->PciRootBridgeIoMapping; + + // + // PciHostBridge should map IOMMU page aligned HostAddress. + // + // We need do copy mem before PciRootBridgeIo->Unmap(), + // because it might free mapped host address. + // + if (PciIoMapInfo->Operation =3D=3D EfiPciIoOperationBusMasterWrite) { + CopyMem ( + PciIoMapInfo->HostAddress, + PciIoMapInfo->MappedHostAddress, + PciIoMapInfo->NumberOfBytes + ); + } + } + Status =3D PciIoDevice->PciRootBridgeIo->Unmap ( PciIoDevice->PciRootBridgeIo, Mapping @@ -1037,6 +1204,25 @@ PciIoUnmap ( ); } =20 + if (gIoMmuProtocol !=3D NULL) { + if (!EFI_ERROR(Status)) { + // + // PciHostBridge should map IOMMU page aligned HostAddress. + // + gIoMmuProtocol->SetAttribute ( + gIoMmuProtocol, + PciIoDevice->Handle, + PciIoMapInfo->DeviceAddress, + PciIoMapInfo->AlignedNumberOfBytes, + 0 + ); + if (PciIoMapInfo->Operation !=3D EfiPciIoOperationBusMasterCommonBuf= fer) { + FreePages (PciIoMapInfo->AlignedHostAddress, EFI_SIZE_TO_PAGES(Pci= IoMapInfo->AlignedNumberOfBytes)); + } + FreePool (PciIoMapInfo); + } + } + return Status; } =20 @@ -1073,6 +1259,7 @@ PciIoAllocateBuffer ( { EFI_STATUS Status; PCI_IO_DEVICE *PciIoDevice; + UINTN Size; =20 if ((Attributes & (~(EFI_PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE | EFI_PCI_ATTRIBUTE_MEMORY= _CACHED))) !=3D 0){ @@ -1085,6 +1272,12 @@ PciIoAllocateBuffer ( Attributes |=3D EFI_PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE; } =20 + if (gIoMmuProtocol !=3D NULL) { + Size =3D EFI_PAGES_TO_SIZE(Pages); + Size =3D ALIGN_VALUE(Size, mIoMmuPageSize); + Pages =3D EFI_SIZE_TO_PAGES (Size); + } + Status =3D PciIoDevice->PciRootBridgeIo->AllocateBuffer ( PciIoDevice->PciRootBridgeIo, Type, @@ -1101,7 +1294,9 @@ PciIoAllocateBuffer ( PciIoDevice->DevicePath ); } - + // + // No need to set attribute here, it is done in Map. + // return Status; } =20 @@ -1127,9 +1322,16 @@ PciIoFreeBuffer ( { EFI_STATUS Status; PCI_IO_DEVICE *PciIoDevice; + UINTN Size; =20 PciIoDevice =3D PCI_IO_DEVICE_FROM_PCI_IO_THIS (This); =20 + if (gIoMmuProtocol !=3D NULL) { + Size =3D EFI_PAGES_TO_SIZE(Pages); + Size =3D ALIGN_VALUE(Size, mIoMmuPageSize); + Pages =3D EFI_SIZE_TO_PAGES (Size); + } + Status =3D PciIoDevice->PciRootBridgeIo->FreeBuffer ( PciIoDevice->PciRootBridgeIo, Pages, @@ -1144,6 +1346,9 @@ PciIoFreeBuffer ( ); } =20 + // + // No need to set attribute here, it is done in Unmap. + // return Status; } =20 --=20 2.7.4.windows.1 _______________________________________________ edk2-devel mailing list edk2-devel@lists.01.org https://lists.01.org/mailman/listinfo/edk2-devel