EFI_STATUS PciIoAllocateBuffer ( IN EFI_PCI_IO_PROTOCOL *This, IN EFI_ALLOCATE_TYPE Type, IN EFI_MEMORY_TYPE MemoryType, IN UINTN Pages, OUT VOID **HostAddress, IN UINT64 Attributes ) { if (Attributes & EFI_PCI_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) { // Check this return EFI_UNSUPPORTED; } return DmaAllocateBuffer (MemoryType, Pages, HostAddress); }
EFI_STATUS PciRbAllocateBuffer ( IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *This, IN EFI_ALLOCATE_TYPE Type, IN EFI_MEMORY_TYPE MemoryType, IN UINTN Pages, IN OUT VOID **HostAddress, IN UINT64 Attributes ) { PCI_TRACE ("PciRbAllocateBuffer()"); if (Attributes & EFI_PCI_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) { return EFI_UNSUPPORTED; } return DmaAllocateBuffer (MemoryType, Pages, HostAddress); }
/** Provides the DMA controller-specific addresses needed to access system memory. Operation is relative to the DMA bus master. @param Operation Indicates if the bus master is going to read or write to system memory. @param HostAddress The system memory address to map to the DMA controller. @param NumberOfBytes On input the number of bytes to map. On output the number of bytes that were mapped. @param DeviceAddress The resulting map address for the bus master controller to use to access the hosts HostAddress. @param Mapping A resulting value to pass to Unmap(). @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes. @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer. @retval EFI_INVALID_PARAMETER One or more parameters are invalid. @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. @retval EFI_DEVICE_ERROR The system hardware could not map the requested address. **/ EFI_STATUS EFIAPI DmaMap ( IN DMA_MAP_OPERATION Operation, IN VOID *HostAddress, IN OUT UINTN *NumberOfBytes, OUT PHYSICAL_ADDRESS *DeviceAddress, OUT VOID **Mapping ) { EFI_STATUS Status; MAP_INFO_INSTANCE *Map; VOID *Buffer; EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor; if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL || Mapping == NULL ) { return EFI_INVALID_PARAMETER; } if (Operation >= MapOperationMaximum) { return EFI_INVALID_PARAMETER; } *DeviceAddress = ConvertToPhysicalAddress (HostAddress); // Remember range so we can flush on the other side Map = AllocatePool (sizeof (MAP_INFO_INSTANCE)); if (Map == NULL) { return EFI_OUT_OF_RESOURCES; } *Mapping = Map; if ((((UINTN)HostAddress & (gCacheAlignment - 1)) != 0) || ((*NumberOfBytes % gCacheAlignment) != 0)) { // Get the cacheability of the region Status = gDS->GetMemorySpaceDescriptor (*DeviceAddress, &GcdDescriptor); if (EFI_ERROR(Status)) { return Status; } // If the mapped buffer is not an uncached buffer if ( (GcdDescriptor.Attributes != EFI_MEMORY_WC) && (GcdDescriptor.Attributes != EFI_MEMORY_UC) ) { // // If the buffer does not fill entire cache lines we must double buffer into // uncached memory. Device (PCI) address becomes uncached page. // Map->DoubleBuffer = TRUE; Status = DmaAllocateBuffer (EfiBootServicesData, EFI_SIZE_TO_PAGES (*NumberOfBytes), &Buffer); if (EFI_ERROR (Status)) { return Status; } if ((Operation == MapOperationBusMasterRead) || (Operation == MapOperationBusMasterCommonBuffer)) { CopyMem (Buffer, HostAddress, *NumberOfBytes); } *DeviceAddress = (PHYSICAL_ADDRESS)(UINTN)Buffer; } else { Map->DoubleBuffer = FALSE; } } else { Map->DoubleBuffer = FALSE; // Flush the Data Cache (should not have any effect if the memory region is uncached) gCpu->FlushDataCache (gCpu, *DeviceAddress, *NumberOfBytes, EfiCpuFlushTypeWriteBackInvalidate); if ((Operation == MapOperationBusMasterRead) || (Operation == MapOperationBusMasterCommonBuffer)) { // In case the buffer is used for instance to send command to a PCI controller, we must ensure the memory is uncached Status = gDS->SetMemorySpaceAttributes (*DeviceAddress & ~(BASE_4KB - 1), ALIGN_VALUE (*NumberOfBytes, BASE_4KB), EFI_MEMORY_WC); ASSERT_EFI_ERROR (Status); } } Map->HostAddress = (UINTN)HostAddress; Map->DeviceAddress = *DeviceAddress; Map->NumberOfBytes = *NumberOfBytes; Map->Operation = Operation; return EFI_SUCCESS; }
/** Provides the DMA controller-specific addresses needed to access system memory. Operation is relative to the DMA bus master. @param Operation Indicates if the bus master is going to read or write to system memory. @param HostAddress The system memory address to map to the DMA controller. @param NumberOfBytes On input the number of bytes to map. On output the number of bytes that were mapped. @param DeviceAddress The resulting map address for the bus master controller to use to access the hosts HostAddress. @param Mapping A resulting value to pass to Unmap(). @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes. @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer. @retval EFI_INVALID_PARAMETER One or more parameters are invalid. @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. @retval EFI_DEVICE_ERROR The system hardware could not map the requested address. **/ EFI_STATUS EFIAPI DmaMap ( IN DMA_MAP_OPERATION Operation, IN VOID *HostAddress, IN OUT UINTN *NumberOfBytes, OUT PHYSICAL_ADDRESS *DeviceAddress, OUT VOID **Mapping ) { EFI_STATUS Status; MAP_INFO_INSTANCE *Map; VOID *Buffer; EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor; if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL || Mapping == NULL ) { return EFI_INVALID_PARAMETER; } if (Operation >= MapOperationMaximum) { return EFI_INVALID_PARAMETER; } // // The debug implementation of UncachedMemoryAllocationLib in ArmPkg returns // a virtual uncached alias, and unmaps the cached ID mapping of the buffer, // in order to catch inadvertent references to the cached mapping. // Since HostToDeviceAddress () expects ID mapped input addresses, convert // the host address to an ID mapped address first. // *DeviceAddress = HostToDeviceAddress (ConvertToPhysicalAddress (HostAddress)); // Remember range so we can flush on the other side Map = AllocatePool (sizeof (MAP_INFO_INSTANCE)); if (Map == NULL) { return EFI_OUT_OF_RESOURCES; } if ((((UINTN)HostAddress & (mCpu->DmaBufferAlignment - 1)) != 0) || ((*NumberOfBytes & (mCpu->DmaBufferAlignment - 1)) != 0)) { // Get the cacheability of the region Status = gDS->GetMemorySpaceDescriptor ((UINTN)HostAddress, &GcdDescriptor); if (EFI_ERROR(Status)) { goto FreeMapInfo; } // If the mapped buffer is not an uncached buffer if ((GcdDescriptor.Attributes & (EFI_MEMORY_WB | EFI_MEMORY_WT)) != 0) { // // Operations of type MapOperationBusMasterCommonBuffer are only allowed // on uncached buffers. // if (Operation == MapOperationBusMasterCommonBuffer) { DEBUG ((EFI_D_ERROR, "%a: Operation type 'MapOperationBusMasterCommonBuffer' is only supported\n" "on memory regions that were allocated using DmaAllocateBuffer ()\n", __FUNCTION__)); Status = EFI_UNSUPPORTED; goto FreeMapInfo; } // // If the buffer does not fill entire cache lines we must double buffer into // uncached memory. Device (PCI) address becomes uncached page. // Map->DoubleBuffer = TRUE; Status = DmaAllocateBuffer (EfiBootServicesData, EFI_SIZE_TO_PAGES (*NumberOfBytes), &Buffer); if (EFI_ERROR (Status)) { goto FreeMapInfo; } if (Operation == MapOperationBusMasterRead) { CopyMem (Buffer, HostAddress, *NumberOfBytes); } *DeviceAddress = HostToDeviceAddress (ConvertToPhysicalAddress (Buffer)); Map->BufferAddress = Buffer; } else { Map->DoubleBuffer = FALSE; } } else { Map->DoubleBuffer = FALSE; DEBUG_CODE_BEGIN (); // // The operation type check above only executes if the buffer happens to be // misaligned with respect to CWG, but even if it is aligned, we should not // allow arbitrary buffers to be used for creating consistent mappings. // So duplicate the check here when running in DEBUG mode, just to assert // that we are not trying to create a consistent mapping for cached memory. // Status = gDS->GetMemorySpaceDescriptor ((UINTN)HostAddress, &GcdDescriptor); ASSERT_EFI_ERROR(Status); ASSERT (Operation != MapOperationBusMasterCommonBuffer || (GcdDescriptor.Attributes & (EFI_MEMORY_WB | EFI_MEMORY_WT)) == 0); DEBUG_CODE_END (); // Flush the Data Cache (should not have any effect if the memory region is uncached) mCpu->FlushDataCache (mCpu, (UINTN)HostAddress, *NumberOfBytes, EfiCpuFlushTypeWriteBackInvalidate); } Map->HostAddress = (UINTN)HostAddress; Map->NumberOfBytes = *NumberOfBytes; Map->Operation = Operation; *Mapping = Map; return EFI_SUCCESS; FreeMapInfo: FreePool (Map); return Status; }