+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+NonCoherentPciIoFreeBuffer (\r
+ IN EFI_PCI_IO_PROTOCOL *This,\r
+ IN UINTN Pages,\r
+ IN VOID *HostAddress\r
+ )\r
+{\r
+ NON_DISCOVERABLE_PCI_DEVICE *Dev;\r
+ LIST_ENTRY *Entry;\r
+ EFI_STATUS Status;\r
+ NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION *Alloc;\r
+ BOOLEAN Found;\r
+\r
+ Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This);\r
+\r
+ Found = FALSE;\r
+\r
+ //\r
+ // Find the uncached allocation list entry associated\r
+ // with this allocation\r
+ //\r
+ for (Entry = Dev->UncachedAllocationList.ForwardLink;\r
+ Entry != &Dev->UncachedAllocationList;\r
+ Entry = Entry->ForwardLink) {\r
+\r
+ Alloc = BASE_CR (Entry, NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION, List);\r
+ if (Alloc->HostAddress == HostAddress && Alloc->NumPages == Pages) {\r
+ //\r
+ // We are freeing the exact allocation we were given\r
+ // before by AllocateBuffer()\r
+ //\r
+ Found = TRUE;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (!Found) {\r
+ ASSERT_EFI_ERROR (EFI_NOT_FOUND);\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ RemoveEntryList (&Alloc->List);\r
+\r
+ Status = gDS->SetMemorySpaceAttributes (\r
+ (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress,\r
+ EFI_PAGES_TO_SIZE (Pages),\r
+ Alloc->Attributes);\r
+ if (EFI_ERROR (Status)) {\r
+ goto FreeAlloc;\r
+ }\r
+\r
+ //\r
+ // If we fail to restore the original attributes, it is better to leak the\r
+ // memory than to return it to the heap\r
+ //\r
+ FreePages (HostAddress, Pages);\r
+\r
+FreeAlloc:\r
+ FreePool (Alloc);\r
+ return Status;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+NonCoherentPciIoAllocateBuffer (\r
+ IN EFI_PCI_IO_PROTOCOL *This,\r
+ IN EFI_ALLOCATE_TYPE Type,\r
+ IN EFI_MEMORY_TYPE MemoryType,\r
+ IN UINTN Pages,\r
+ OUT VOID **HostAddress,\r
+ IN UINT64 Attributes\r
+ )\r
+{\r
+ NON_DISCOVERABLE_PCI_DEVICE *Dev;\r
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;\r
+ EFI_STATUS Status;\r
+ UINT64 MemType;\r
+ NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION *Alloc;\r
+ VOID *AllocAddress;\r
+\r
+ Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This);\r
+\r
+ Status = CoherentPciIoAllocateBuffer (This, Type, MemoryType, Pages,\r
+ &AllocAddress, Attributes);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = gDS->GetMemorySpaceDescriptor (\r
+ (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress,\r
+ &GcdDescriptor);\r
+ if (EFI_ERROR (Status)) {\r
+ goto FreeBuffer;\r
+ }\r
+\r
+ if ((GcdDescriptor.Capabilities & (EFI_MEMORY_WC | EFI_MEMORY_UC)) == 0) {\r
+ Status = EFI_UNSUPPORTED;\r
+ goto FreeBuffer;\r
+ }\r
+\r
+ //\r
+ // Set the preferred memory attributes\r
+ //\r
+ if ((Attributes & EFI_PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE) != 0 ||\r
+ (GcdDescriptor.Capabilities & EFI_MEMORY_UC) == 0) {\r
+ //\r
+ // Use write combining if it was requested, or if it is the only\r
+ // type supported by the region.\r
+ //\r
+ MemType = EFI_MEMORY_WC;\r
+ } else {\r
+ MemType = EFI_MEMORY_UC;\r
+ }\r
+\r
+ Alloc = AllocatePool (sizeof *Alloc);\r
+ if (Alloc == NULL) {\r
+ goto FreeBuffer;\r
+ }\r
+\r
+ Alloc->HostAddress = AllocAddress;\r
+ Alloc->NumPages = Pages;\r
+ Alloc->Attributes = GcdDescriptor.Attributes;\r
+\r
+ //\r
+ // Record this allocation in the linked list, so we\r
+ // can restore the memory space attributes later\r
+ //\r
+ InsertHeadList (&Dev->UncachedAllocationList, &Alloc->List);\r
+\r
+ Status = gDS->SetMemorySpaceAttributes (\r
+ (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress,\r
+ EFI_PAGES_TO_SIZE (Pages),\r
+ MemType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto RemoveList;\r
+ }\r
+\r
+ Status = mCpu->FlushDataCache (\r
+ mCpu,\r
+ (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress,\r
+ EFI_PAGES_TO_SIZE (Pages),\r
+ EfiCpuFlushTypeInvalidate);\r
+ if (EFI_ERROR (Status)) {\r
+ goto RemoveList;\r
+ }\r
+\r
+ *HostAddress = AllocAddress;\r
+\r
+ return EFI_SUCCESS;\r
+\r
+RemoveList:\r
+ RemoveEntryList (&Alloc->List);\r
+ FreePool (Alloc);\r
+\r
+FreeBuffer:\r
+ CoherentPciIoFreeBuffer (This, Pages, AllocAddress);\r
+ return Status;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+NonCoherentPciIoMap (\r
+ IN EFI_PCI_IO_PROTOCOL *This,\r
+ IN EFI_PCI_IO_PROTOCOL_OPERATION Operation,\r
+ IN VOID *HostAddress,\r
+ IN OUT UINTN *NumberOfBytes,\r
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
+ OUT VOID **Mapping\r
+ )\r
+{\r
+ NON_DISCOVERABLE_PCI_DEVICE *Dev;\r
+ EFI_STATUS Status;\r
+ NON_DISCOVERABLE_PCI_DEVICE_MAP_INFO *MapInfo;\r
+ UINTN AlignMask;\r
+ VOID *AllocAddress;\r
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;\r
+ BOOLEAN Bounce;\r
+\r
+ MapInfo = AllocatePool (sizeof *MapInfo);\r
+ if (MapInfo == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ MapInfo->HostAddress = HostAddress;\r
+ MapInfo->Operation = Operation;\r
+ MapInfo->NumberOfBytes = *NumberOfBytes;\r
+\r
+ Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This);\r
+\r
+ //\r
+ // If this device does not support 64-bit DMA addressing, we need to allocate\r
+ // a bounce buffer and copy over the data in case HostAddress >= 4 GB.\r
+ //\r
+ Bounce = ((Dev->Attributes & EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0 &&\r
+ (UINTN)HostAddress + *NumberOfBytes > SIZE_4GB);\r
+\r
+ if (!Bounce) {\r
+ switch (Operation) {\r
+ case EfiPciIoOperationBusMasterRead:\r
+ case EfiPciIoOperationBusMasterWrite:\r
+ //\r
+ // For streaming DMA, it is sufficient if the buffer is aligned to\r
+ // the CPUs DMA buffer alignment.\r
+ //\r
+ AlignMask = mCpu->DmaBufferAlignment - 1;\r
+ if ((((UINTN) HostAddress | *NumberOfBytes) & AlignMask) == 0) {\r
+ break;\r
+ }\r
+ // fall through\r
+\r
+ case EfiPciIoOperationBusMasterCommonBuffer:\r
+ //\r
+ // Check whether the host address refers to an uncached mapping.\r
+ //\r
+ Status = gDS->GetMemorySpaceDescriptor (\r
+ (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress,\r
+ &GcdDescriptor);\r
+ if (EFI_ERROR (Status) ||\r
+ (GcdDescriptor.Attributes & (EFI_MEMORY_WB|EFI_MEMORY_WT)) != 0) {\r
+ Bounce = TRUE;\r
+ }\r
+ break;\r
+\r
+ default:\r
+ ASSERT (FALSE);\r
+ }\r
+ }\r
+\r
+ if (Bounce) {\r
+ if (Operation == EfiPciIoOperationBusMasterCommonBuffer) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto FreeMapInfo;\r
+ }\r
+\r
+ Status = NonCoherentPciIoAllocateBuffer (This, AllocateAnyPages,\r
+ EfiBootServicesData, EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes),\r
+ &AllocAddress, EFI_PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE);\r
+ if (EFI_ERROR (Status)) {\r
+ goto FreeMapInfo;\r
+ }\r
+ MapInfo->AllocAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress;\r
+ if (Operation == EfiPciIoOperationBusMasterRead) {\r
+ gBS->CopyMem (AllocAddress, HostAddress, *NumberOfBytes);\r
+ }\r
+ *DeviceAddress = MapInfo->AllocAddress;\r
+ } else {\r
+ MapInfo->AllocAddress = 0;\r
+ *DeviceAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress;\r
+\r
+ //\r
+ // We are not using a bounce buffer: the mapping is sufficiently\r
+ // aligned to allow us to simply flush the caches. Note that cleaning\r
+ // the caches is necessary for both data directions:\r
+ // - for bus master read, we want the latest data to be present\r
+ // in main memory\r
+ // - for bus master write, we don't want any stale dirty cachelines that\r
+ // may be written back unexpectedly, and clobber the data written to\r
+ // main memory by the device.\r
+ //\r
+ mCpu->FlushDataCache (mCpu, (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress,\r
+ *NumberOfBytes, EfiCpuFlushTypeWriteBack);\r
+ }\r
+\r
+ *Mapping = MapInfo;\r
+ return EFI_SUCCESS;\r
+\r
+FreeMapInfo:\r
+ FreePool (MapInfo);\r
+\r
+ return Status;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+NonCoherentPciIoUnmap (\r
+ IN EFI_PCI_IO_PROTOCOL *This,\r
+ IN VOID *Mapping\r
+ )\r
+{\r
+ NON_DISCOVERABLE_PCI_DEVICE_MAP_INFO *MapInfo;\r
+\r
+ if (Mapping == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ MapInfo = Mapping;\r
+ if (MapInfo->AllocAddress != 0) {\r
+ //\r
+ // We are using a bounce buffer: copy back the data if necessary,\r
+ // and free the buffer.\r
+ //\r
+ if (MapInfo->Operation == EfiPciIoOperationBusMasterWrite) {\r
+ gBS->CopyMem (MapInfo->HostAddress, (VOID *)(UINTN)MapInfo->AllocAddress,\r
+ MapInfo->NumberOfBytes);\r
+ }\r
+ NonCoherentPciIoFreeBuffer (This,\r
+ EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes),\r
+ (VOID *)(UINTN)MapInfo->AllocAddress);\r
+ } else {\r
+ //\r
+ // We are *not* using a bounce buffer: if this is a bus master write,\r
+ // we have to invalidate the caches so the CPU will see the uncached\r
+ // data written by the device.\r
+ //\r
+ if (MapInfo->Operation == EfiPciIoOperationBusMasterWrite) {\r
+ mCpu->FlushDataCache (mCpu,\r
+ (EFI_PHYSICAL_ADDRESS)(UINTN)MapInfo->HostAddress,\r
+ MapInfo->NumberOfBytes, EfiCpuFlushTypeInvalidate);\r
+ }\r
+ }\r
+ FreePool (MapInfo);\r
+ return EFI_SUCCESS;\r
+}\r