| /** @file | |
| Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR> | |
| Copyright (c) 2016, Linaro, Ltd. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "NonDiscoverablePciDeviceIo.h" | |
| #include <Library/DxeServicesTableLib.h> | |
| #include <IndustryStandard/Acpi.h> | |
| #include <Protocol/PciRootBridgeIo.h> | |
| typedef struct { | |
| EFI_PHYSICAL_ADDRESS AllocAddress; | |
| VOID *HostAddress; | |
| EFI_PCI_IO_PROTOCOL_OPERATION Operation; | |
| UINTN NumberOfBytes; | |
| } NON_DISCOVERABLE_PCI_DEVICE_MAP_INFO; | |
| /** | |
| Get the resource associated with BAR number 'BarIndex'. | |
| @param Dev Point to the NON_DISCOVERABLE_PCI_DEVICE instance. | |
| @param BarIndex The BAR index of the standard PCI Configuration header to use as the | |
| base address for the memory operation to perform. | |
| @param Descriptor Points to the address space descriptor | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| GetBarResource ( | |
| IN NON_DISCOVERABLE_PCI_DEVICE *Dev, | |
| IN UINT8 BarIndex, | |
| OUT EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR **Descriptor | |
| ) | |
| { | |
| EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Desc; | |
| if (BarIndex < Dev->BarOffset) { | |
| return EFI_NOT_FOUND; | |
| } | |
| BarIndex -= (UINT8)Dev->BarOffset; | |
| for (Desc = Dev->Device->Resources; | |
| Desc->Desc != ACPI_END_TAG_DESCRIPTOR; | |
| Desc = (VOID *)((UINT8 *)Desc + Desc->Len + 3)) { | |
| if (BarIndex == 0) { | |
| *Descriptor = Desc; | |
| return EFI_SUCCESS; | |
| } | |
| BarIndex -= 1; | |
| } | |
| return EFI_NOT_FOUND; | |
| } | |
| /** | |
| Reads from the memory space of a PCI controller. Returns either when the polling exit criteria is | |
| satisfied or after a defined duration. | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| @param Width Signifies the width of the memory or I/O operations. | |
| @param BarIndex The BAR index of the standard PCI Configuration header to use as the | |
| base address for the memory operation to perform. | |
| @param Offset The offset within the selected BAR to start the memory operation. | |
| @param Mask Mask used for the polling criteria. | |
| @param Value The comparison value used for the polling exit criteria. | |
| @param Delay The number of 100 ns units to poll. | |
| @param Result Pointer to the last value read from the memory location. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| PciIoPollMem ( | |
| IN EFI_PCI_IO_PROTOCOL *This, | |
| IN EFI_PCI_IO_PROTOCOL_WIDTH Width, | |
| IN UINT8 BarIndex, | |
| IN UINT64 Offset, | |
| IN UINT64 Mask, | |
| IN UINT64 Value, | |
| IN UINT64 Delay, | |
| OUT UINT64 *Result | |
| ) | |
| { | |
| ASSERT (FALSE); | |
| return EFI_UNSUPPORTED; | |
| } | |
| /** | |
| Reads from the memory space of a PCI controller. Returns either when the polling exit criteria is | |
| satisfied or after a defined duration. | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| @param Width Signifies the width of the memory or I/O operations. | |
| @param BarIndex The BAR index of the standard PCI Configuration header to use as the | |
| base address for the memory operation to perform. | |
| @param Offset The offset within the selected BAR to start the memory operation. | |
| @param Mask Mask used for the polling criteria. | |
| @param Value The comparison value used for the polling exit criteria. | |
| @param Delay The number of 100 ns units to poll. | |
| @param Result Pointer to the last value read from the memory location. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| PciIoPollIo ( | |
| IN EFI_PCI_IO_PROTOCOL *This, | |
| IN EFI_PCI_IO_PROTOCOL_WIDTH Width, | |
| IN UINT8 BarIndex, | |
| IN UINT64 Offset, | |
| IN UINT64 Mask, | |
| IN UINT64 Value, | |
| IN UINT64 Delay, | |
| OUT UINT64 *Result | |
| ) | |
| { | |
| ASSERT (FALSE); | |
| return EFI_UNSUPPORTED; | |
| } | |
| /** | |
| Enable a PCI driver to access PCI controller registers in the PCI memory or I/O space. | |
| @param Width Signifies the width of the memory or I/O operations. | |
| @param Count The number of memory or I/O operations to perform. | |
| @param DstStride The stride of the destination buffer. | |
| @param Dst For read operations, the destination buffer to store the results. For write | |
| operations, the destination buffer to write data to. | |
| @param SrcStride The stride of the source buffer. | |
| @param Src For read operations, the source buffer to read data from. For write | |
| operations, the source buffer to write data from. | |
| @retval EFI_SUCCESS The data was read from or written to the PCI controller. | |
| @retval EFI_INVALID_PARAMETER One or more parameters are invalid. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| PciIoMemRW ( | |
| IN EFI_PCI_IO_PROTOCOL_WIDTH Width, | |
| IN UINTN Count, | |
| IN UINTN DstStride, | |
| IN VOID *Dst, | |
| IN UINTN SrcStride, | |
| OUT CONST VOID *Src | |
| ) | |
| { | |
| volatile UINT8 *Dst8; | |
| volatile UINT16 *Dst16; | |
| volatile UINT32 *Dst32; | |
| volatile CONST UINT8 *Src8; | |
| volatile CONST UINT16 *Src16; | |
| volatile CONST UINT32 *Src32; | |
| // | |
| // Loop for each iteration and move the data | |
| // | |
| switch (Width & 0x3) { | |
| case EfiPciWidthUint8: | |
| Dst8 = (UINT8 *)Dst; | |
| Src8 = (UINT8 *)Src; | |
| for (;Count > 0; Count--, Dst8 += DstStride, Src8 += SrcStride) { | |
| *Dst8 = *Src8; | |
| } | |
| break; | |
| case EfiPciWidthUint16: | |
| Dst16 = (UINT16 *)Dst; | |
| Src16 = (UINT16 *)Src; | |
| for (;Count > 0; Count--, Dst16 += DstStride, Src16 += SrcStride) { | |
| *Dst16 = *Src16; | |
| } | |
| break; | |
| case EfiPciWidthUint32: | |
| Dst32 = (UINT32 *)Dst; | |
| Src32 = (UINT32 *)Src; | |
| for (;Count > 0; Count--, Dst32 += DstStride, Src32 += SrcStride) { | |
| *Dst32 = *Src32; | |
| } | |
| break; | |
| default: | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Enable a PCI driver to access PCI controller registers in the PCI memory or I/O space. | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| @param Width Signifies the width of the memory or I/O operations. | |
| @param BarIndex The BAR index of the standard PCI Configuration header to use as the | |
| base address for the memory or I/O operation to perform. | |
| @param Offset The offset within the selected BAR to start the memory or I/O operation. | |
| @param Count The number of memory or I/O operations to perform. | |
| @param Buffer For read operations, the destination buffer to store the results. For write | |
| operations, the source buffer to write data from. | |
| @retval EFI_SUCCESS The data was read from or written to the PCI controller. | |
| @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. | |
| @retval EFI_UNSUPPORTED The address range specified by Offset, Width, and Count is not | |
| valid for the PCI BAR specified by BarIndex. | |
| @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. | |
| @retval EFI_INVALID_PARAMETER One or more parameters are invalid. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| PciIoMemRead ( | |
| IN EFI_PCI_IO_PROTOCOL *This, | |
| IN EFI_PCI_IO_PROTOCOL_WIDTH Width, | |
| IN UINT8 BarIndex, | |
| IN UINT64 Offset, | |
| IN UINTN Count, | |
| IN OUT VOID *Buffer | |
| ) | |
| { | |
| NON_DISCOVERABLE_PCI_DEVICE *Dev; | |
| UINTN AlignMask; | |
| VOID *Address; | |
| EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Desc; | |
| EFI_STATUS Status; | |
| if (Buffer == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This); | |
| // | |
| // Only allow accesses to the BARs we emulate | |
| // | |
| Status = GetBarResource (Dev, BarIndex, &Desc); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| if (Offset + (Count << (Width & 0x3)) > Desc->AddrLen) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| Address = (VOID *)(UINTN)(Desc->AddrRangeMin + Offset); | |
| AlignMask = (1 << (Width & 0x03)) - 1; | |
| if ((UINTN)Address & AlignMask) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| switch (Width) { | |
| case EfiPciIoWidthUint8: | |
| case EfiPciIoWidthUint16: | |
| case EfiPciIoWidthUint32: | |
| case EfiPciIoWidthUint64: | |
| return PciIoMemRW (Width, Count, 1, Buffer, 1, Address); | |
| case EfiPciIoWidthFifoUint8: | |
| case EfiPciIoWidthFifoUint16: | |
| case EfiPciIoWidthFifoUint32: | |
| case EfiPciIoWidthFifoUint64: | |
| return PciIoMemRW (Width, Count, 1, Buffer, 0, Address); | |
| case EfiPciIoWidthFillUint8: | |
| case EfiPciIoWidthFillUint16: | |
| case EfiPciIoWidthFillUint32: | |
| case EfiPciIoWidthFillUint64: | |
| return PciIoMemRW (Width, Count, 0, Buffer, 1, Address); | |
| default: | |
| break; | |
| } | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| /** | |
| Enable a PCI driver to access PCI controller registers in the PCI memory or I/O space. | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| @param Width Signifies the width of the memory or I/O operations. | |
| @param BarIndex The BAR index of the standard PCI Configuration header to use as the | |
| base address for the memory or I/O operation to perform. | |
| @param Offset The offset within the selected BAR to start the memory or I/O operation. | |
| @param Count The number of memory or I/O operations to perform. | |
| @param Buffer For read operations, the destination buffer to store the results. For write | |
| operations, the source buffer to write data from. | |
| @retval EFI_SUCCESS The data was read from or written to the PCI controller. | |
| @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. | |
| @retval EFI_UNSUPPORTED The address range specified by Offset, Width, and Count is not | |
| valid for the PCI BAR specified by BarIndex. | |
| @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. | |
| @retval EFI_INVALID_PARAMETER One or more parameters are invalid. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| PciIoMemWrite ( | |
| IN EFI_PCI_IO_PROTOCOL *This, | |
| IN EFI_PCI_IO_PROTOCOL_WIDTH Width, | |
| IN UINT8 BarIndex, | |
| IN UINT64 Offset, | |
| IN UINTN Count, | |
| IN OUT VOID *Buffer | |
| ) | |
| { | |
| NON_DISCOVERABLE_PCI_DEVICE *Dev; | |
| UINTN AlignMask; | |
| VOID *Address; | |
| EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Desc; | |
| EFI_STATUS Status; | |
| if (Buffer == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This); | |
| // | |
| // Only allow accesses to the BARs we emulate | |
| // | |
| Status = GetBarResource (Dev, BarIndex, &Desc); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| if (Offset + (Count << (Width & 0x3)) > Desc->AddrLen) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| Address = (VOID *)(UINTN)(Desc->AddrRangeMin + Offset); | |
| AlignMask = (1 << (Width & 0x03)) - 1; | |
| if ((UINTN)Address & AlignMask) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| switch (Width) { | |
| case EfiPciIoWidthUint8: | |
| case EfiPciIoWidthUint16: | |
| case EfiPciIoWidthUint32: | |
| case EfiPciIoWidthUint64: | |
| return PciIoMemRW (Width, Count, 1, Address, 1, Buffer); | |
| case EfiPciIoWidthFifoUint8: | |
| case EfiPciIoWidthFifoUint16: | |
| case EfiPciIoWidthFifoUint32: | |
| case EfiPciIoWidthFifoUint64: | |
| return PciIoMemRW (Width, Count, 0, Address, 1, Buffer); | |
| case EfiPciIoWidthFillUint8: | |
| case EfiPciIoWidthFillUint16: | |
| case EfiPciIoWidthFillUint32: | |
| case EfiPciIoWidthFillUint64: | |
| return PciIoMemRW (Width, Count, 1, Address, 0, Buffer); | |
| default: | |
| break; | |
| } | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| /** | |
| Enable a PCI driver to access PCI controller registers in the PCI memory or I/O space. | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| @param Width Signifies the width of the memory or I/O operations. | |
| @param BarIndex The BAR index of the standard PCI Configuration header to use as the | |
| base address for the memory or I/O operation to perform. | |
| @param Offset The offset within the selected BAR to start the memory or I/O operation. | |
| @param Count The number of memory or I/O operations to perform. | |
| @param Buffer For read operations, the destination buffer to store the results. For write | |
| operations, the source buffer to write data from. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| PciIoIoRead ( | |
| IN EFI_PCI_IO_PROTOCOL *This, | |
| IN EFI_PCI_IO_PROTOCOL_WIDTH Width, | |
| IN UINT8 BarIndex, | |
| IN UINT64 Offset, | |
| IN UINTN Count, | |
| IN OUT VOID *Buffer | |
| ) | |
| { | |
| ASSERT (FALSE); | |
| return EFI_UNSUPPORTED; | |
| } | |
| /** | |
| Enable a PCI driver to access PCI controller registers in the PCI memory or I/O space. | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| @param Width Signifies the width of the memory or I/O operations. | |
| @param BarIndex The BAR index of the standard PCI Configuration header to use as the | |
| base address for the memory or I/O operation to perform. | |
| @param Offset The offset within the selected BAR to start the memory or I/O operation. | |
| @param Count The number of memory or I/O operations to perform. | |
| @param Buffer For read operations, the destination buffer to store the results. For write | |
| operations, the source buffer to write data from. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| PciIoIoWrite ( | |
| IN EFI_PCI_IO_PROTOCOL *This, | |
| IN EFI_PCI_IO_PROTOCOL_WIDTH Width, | |
| IN UINT8 BarIndex, | |
| IN UINT64 Offset, | |
| IN UINTN Count, | |
| IN OUT VOID *Buffer | |
| ) | |
| { | |
| ASSERT (FALSE); | |
| return EFI_UNSUPPORTED; | |
| } | |
| /** | |
| Enable a PCI driver to access PCI config space. | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| @param Width Signifies the width of the memory or I/O operations. | |
| @param Offset The offset within the selected BAR to start the memory or I/O operation. | |
| @param Count The number of memory or I/O operations to perform. | |
| @param Buffer For read operations, the destination buffer to store the results. For write | |
| operations, the source buffer to write data from. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| PciIoPciRead ( | |
| IN EFI_PCI_IO_PROTOCOL *This, | |
| IN EFI_PCI_IO_PROTOCOL_WIDTH Width, | |
| IN UINT32 Offset, | |
| IN UINTN Count, | |
| IN OUT VOID *Buffer | |
| ) | |
| { | |
| NON_DISCOVERABLE_PCI_DEVICE *Dev; | |
| VOID *Address; | |
| UINTN Length; | |
| if (Width < 0 || Width >= EfiPciIoWidthMaximum || Buffer == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This); | |
| Address = (UINT8 *)&Dev->ConfigSpace + Offset; | |
| Length = Count << ((UINTN)Width & 0x3); | |
| if (Offset >= sizeof (Dev->ConfigSpace)) { | |
| ZeroMem (Buffer, Length); | |
| return EFI_SUCCESS; | |
| } | |
| if (Offset + Length > sizeof (Dev->ConfigSpace)) { | |
| // | |
| // Read all zeroes for config space accesses beyond the first | |
| // 64 bytes | |
| // | |
| Length -= sizeof (Dev->ConfigSpace) - Offset; | |
| ZeroMem ((UINT8 *)Buffer + sizeof (Dev->ConfigSpace) - Offset, Length); | |
| Count -= Length >> ((UINTN)Width & 0x3); | |
| } | |
| return PciIoMemRW (Width, Count, 1, Buffer, 1, Address); | |
| } | |
| /** | |
| Enable a PCI driver to access PCI config space. | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| @param Width Signifies the width of the memory or I/O operations. | |
| @param Offset The offset within the selected BAR to start the memory or I/O operation. | |
| @param Count The number of memory or I/O operations to perform. | |
| @param Buffer For read operations, the destination buffer to store the results. For write | |
| operations, the source buffer to write data from | |
| @retval EFI_SUCCESS The data was read from or written to the PCI controller. | |
| @retval EFI_UNSUPPORTED The address range specified by Offset, Width, and Count is not | |
| valid for the PCI BAR specified by BarIndex. | |
| @retval EFI_INVALID_PARAMETER One or more parameters are invalid. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| PciIoPciWrite ( | |
| IN EFI_PCI_IO_PROTOCOL *This, | |
| IN EFI_PCI_IO_PROTOCOL_WIDTH Width, | |
| IN UINT32 Offset, | |
| IN UINTN Count, | |
| IN OUT VOID *Buffer | |
| ) | |
| { | |
| NON_DISCOVERABLE_PCI_DEVICE *Dev; | |
| VOID *Address; | |
| if (Width < 0 || Width >= EfiPciIoWidthMaximum || Buffer == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This); | |
| Address = (UINT8 *)&Dev->ConfigSpace + Offset; | |
| if (Offset + (Count << ((UINTN)Width & 0x3)) > sizeof (Dev->ConfigSpace)) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| return PciIoMemRW (Width, Count, 1, Address, 1, Buffer); | |
| } | |
| /** | |
| Enables a PCI driver to copy one region of PCI memory space to another region of PCI | |
| memory space. | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| @param Width Signifies the width of the memory operations. | |
| @param DestBarIndex The BAR index in the standard PCI Configuration header to use as the | |
| base address for the memory operation to perform. | |
| @param DestOffset The destination offset within the BAR specified by DestBarIndex to | |
| start the memory writes for the copy operation. | |
| @param SrcBarIndex The BAR index in the standard PCI Configuration header to use as the | |
| base address for the memory operation to perform. | |
| @param SrcOffset The source offset within the BAR specified by SrcBarIndex to start | |
| the memory reads for the copy operation. | |
| @param Count The number of memory operations to perform. Bytes moved is Width | |
| size * Count, starting at DestOffset and SrcOffset. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| PciIoCopyMem ( | |
| IN EFI_PCI_IO_PROTOCOL *This, | |
| IN EFI_PCI_IO_PROTOCOL_WIDTH Width, | |
| IN UINT8 DestBarIndex, | |
| IN UINT64 DestOffset, | |
| IN UINT8 SrcBarIndex, | |
| IN UINT64 SrcOffset, | |
| IN UINTN Count | |
| ) | |
| { | |
| ASSERT (FALSE); | |
| return EFI_UNSUPPORTED; | |
| } | |
| /** | |
| Provides the PCI controller-specific addresses needed to access system memory. | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| @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 PCI 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 PCI 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. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| CoherentPciIoMap ( | |
| IN EFI_PCI_IO_PROTOCOL *This, | |
| IN EFI_PCI_IO_PROTOCOL_OPERATION Operation, | |
| IN VOID *HostAddress, | |
| IN OUT UINTN *NumberOfBytes, | |
| OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, | |
| OUT VOID **Mapping | |
| ) | |
| { | |
| NON_DISCOVERABLE_PCI_DEVICE *Dev; | |
| EFI_STATUS Status; | |
| NON_DISCOVERABLE_PCI_DEVICE_MAP_INFO *MapInfo; | |
| // | |
| // If HostAddress exceeds 4 GB, and this device does not support 64-bit DMA | |
| // addressing, we need to allocate a bounce buffer and copy over the data. | |
| // | |
| Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This); | |
| if ((Dev->Attributes & EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0 && | |
| (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress + *NumberOfBytes > SIZE_4GB) { | |
| // | |
| // Bounce buffering is not possible for consistent mappings | |
| // | |
| if (Operation == EfiPciIoOperationBusMasterCommonBuffer) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| MapInfo = AllocatePool (sizeof *MapInfo); | |
| if (MapInfo == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| MapInfo->AllocAddress = MAX_UINT32; | |
| MapInfo->HostAddress = HostAddress; | |
| MapInfo->Operation = Operation; | |
| MapInfo->NumberOfBytes = *NumberOfBytes; | |
| Status = gBS->AllocatePages (AllocateMaxAddress, EfiBootServicesData, | |
| EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes), | |
| &MapInfo->AllocAddress); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // If we fail here, it is likely because the system has no memory below | |
| // 4 GB to begin with. There is not much we can do about that other than | |
| // fail the map request. | |
| // | |
| FreePool (MapInfo); | |
| return EFI_DEVICE_ERROR; | |
| } | |
| if (Operation == EfiPciIoOperationBusMasterRead) { | |
| gBS->CopyMem ((VOID *)(UINTN)MapInfo->AllocAddress, HostAddress, | |
| *NumberOfBytes); | |
| } | |
| *DeviceAddress = MapInfo->AllocAddress; | |
| *Mapping = MapInfo; | |
| } else { | |
| *DeviceAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress; | |
| *Mapping = NULL; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Completes the Map() operation and releases any corresponding resources. | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| @param Mapping The mapping value returned from Map(). | |
| @retval EFI_SUCCESS The range was unmapped. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| CoherentPciIoUnmap ( | |
| IN EFI_PCI_IO_PROTOCOL *This, | |
| IN VOID *Mapping | |
| ) | |
| { | |
| NON_DISCOVERABLE_PCI_DEVICE_MAP_INFO *MapInfo; | |
| MapInfo = Mapping; | |
| if (MapInfo != NULL) { | |
| if (MapInfo->Operation == EfiPciIoOperationBusMasterWrite) { | |
| gBS->CopyMem (MapInfo->HostAddress, (VOID *)(UINTN)MapInfo->AllocAddress, | |
| MapInfo->NumberOfBytes); | |
| } | |
| gBS->FreePages (MapInfo->AllocAddress, | |
| EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes)); | |
| FreePool (MapInfo); | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Allocates pages. | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| @param Type This parameter is not used and must be ignored. | |
| @param MemoryType The type of memory to allocate, EfiBootServicesData or | |
| EfiRuntimeServicesData. | |
| @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 the allocated range. | |
| @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. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| CoherentPciIoAllocateBuffer ( | |
| 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 | |
| ) | |
| { | |
| NON_DISCOVERABLE_PCI_DEVICE *Dev; | |
| EFI_PHYSICAL_ADDRESS AllocAddress; | |
| EFI_ALLOCATE_TYPE AllocType; | |
| EFI_STATUS Status; | |
| if ((Attributes & ~(EFI_PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE | | |
| EFI_PCI_ATTRIBUTE_MEMORY_CACHED)) != 0) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| // | |
| // Allocate below 4 GB if the dual address cycle attribute has not | |
| // been set. If the system has no memory available below 4 GB, there | |
| // is little we can do except propagate the error. | |
| // | |
| Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This); | |
| if ((Dev->Attributes & EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) { | |
| AllocAddress = MAX_UINT32; | |
| AllocType = AllocateMaxAddress; | |
| } else { | |
| AllocType = AllocateAnyPages; | |
| } | |
| Status = gBS->AllocatePages (AllocType, MemoryType, Pages, &AllocAddress); | |
| if (!EFI_ERROR (Status)) { | |
| *HostAddress = (VOID *)(UINTN)AllocAddress; | |
| } | |
| return Status; | |
| } | |
| /** | |
| Frees memory that was allocated in function CoherentPciIoAllocateBuffer (). | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| @param Pages The number of pages to free. | |
| @param HostAddress The base system memory address of the allocated range. | |
| @retval EFI_SUCCESS The requested memory pages were freed. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| CoherentPciIoFreeBuffer ( | |
| IN EFI_PCI_IO_PROTOCOL *This, | |
| IN UINTN Pages, | |
| IN VOID *HostAddress | |
| ) | |
| { | |
| FreePages (HostAddress, Pages); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Frees memory that was allocated in function NonCoherentPciIoAllocateBuffer (). | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| @param Pages The number of pages to free. | |
| @param HostAddress The base system memory address of the allocated range. | |
| @retval EFI_SUCCESS The requested memory pages were freed. | |
| @retval others The operation contain some errors. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| NonCoherentPciIoFreeBuffer ( | |
| IN EFI_PCI_IO_PROTOCOL *This, | |
| IN UINTN Pages, | |
| IN VOID *HostAddress | |
| ) | |
| { | |
| NON_DISCOVERABLE_PCI_DEVICE *Dev; | |
| LIST_ENTRY *Entry; | |
| EFI_STATUS Status; | |
| NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION *Alloc; | |
| BOOLEAN Found; | |
| Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This); | |
| Found = FALSE; | |
| Alloc = NULL; | |
| // | |
| // Find the uncached allocation list entry associated | |
| // with this allocation | |
| // | |
| for (Entry = Dev->UncachedAllocationList.ForwardLink; | |
| Entry != &Dev->UncachedAllocationList; | |
| Entry = Entry->ForwardLink) { | |
| Alloc = BASE_CR (Entry, NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION, List); | |
| if (Alloc->HostAddress == HostAddress && Alloc->NumPages == Pages) { | |
| // | |
| // We are freeing the exact allocation we were given | |
| // before by AllocateBuffer() | |
| // | |
| Found = TRUE; | |
| break; | |
| } | |
| } | |
| if (!Found) { | |
| ASSERT_EFI_ERROR (EFI_NOT_FOUND); | |
| return EFI_NOT_FOUND; | |
| } | |
| RemoveEntryList (&Alloc->List); | |
| Status = gDS->SetMemorySpaceAttributes ( | |
| (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress, | |
| EFI_PAGES_TO_SIZE (Pages), | |
| Alloc->Attributes); | |
| if (EFI_ERROR (Status)) { | |
| goto FreeAlloc; | |
| } | |
| // | |
| // If we fail to restore the original attributes, it is better to leak the | |
| // memory than to return it to the heap | |
| // | |
| FreePages (HostAddress, Pages); | |
| FreeAlloc: | |
| FreePool (Alloc); | |
| return Status; | |
| } | |
| /** | |
| Allocates pages. | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| @param Type This parameter is not used and must be ignored. | |
| @param MemoryType The type of memory to allocate, EfiBootServicesData or | |
| EfiRuntimeServicesData. | |
| @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 the allocated range. | |
| @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. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| NonCoherentPciIoAllocateBuffer ( | |
| 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 | |
| ) | |
| { | |
| NON_DISCOVERABLE_PCI_DEVICE *Dev; | |
| EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor; | |
| EFI_STATUS Status; | |
| UINT64 MemType; | |
| NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION *Alloc; | |
| VOID *AllocAddress; | |
| Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This); | |
| Status = CoherentPciIoAllocateBuffer (This, Type, MemoryType, Pages, | |
| &AllocAddress, Attributes); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| Status = gDS->GetMemorySpaceDescriptor ( | |
| (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress, | |
| &GcdDescriptor); | |
| if (EFI_ERROR (Status)) { | |
| goto FreeBuffer; | |
| } | |
| if ((GcdDescriptor.Capabilities & (EFI_MEMORY_WC | EFI_MEMORY_UC)) == 0) { | |
| Status = EFI_UNSUPPORTED; | |
| goto FreeBuffer; | |
| } | |
| // | |
| // Set the preferred memory attributes | |
| // | |
| if ((Attributes & EFI_PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE) != 0 || | |
| (GcdDescriptor.Capabilities & EFI_MEMORY_UC) == 0) { | |
| // | |
| // Use write combining if it was requested, or if it is the only | |
| // type supported by the region. | |
| // | |
| MemType = EFI_MEMORY_WC; | |
| } else { | |
| MemType = EFI_MEMORY_UC; | |
| } | |
| Alloc = AllocatePool (sizeof *Alloc); | |
| if (Alloc == NULL) { | |
| goto FreeBuffer; | |
| } | |
| Alloc->HostAddress = AllocAddress; | |
| Alloc->NumPages = Pages; | |
| Alloc->Attributes = GcdDescriptor.Attributes; | |
| // | |
| // Record this allocation in the linked list, so we | |
| // can restore the memory space attributes later | |
| // | |
| InsertHeadList (&Dev->UncachedAllocationList, &Alloc->List); | |
| Status = gDS->SetMemorySpaceAttributes ( | |
| (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress, | |
| EFI_PAGES_TO_SIZE (Pages), | |
| MemType); | |
| if (EFI_ERROR (Status)) { | |
| goto RemoveList; | |
| } | |
| Status = mCpu->FlushDataCache ( | |
| mCpu, | |
| (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress, | |
| EFI_PAGES_TO_SIZE (Pages), | |
| EfiCpuFlushTypeInvalidate); | |
| if (EFI_ERROR (Status)) { | |
| goto RemoveList; | |
| } | |
| *HostAddress = AllocAddress; | |
| return EFI_SUCCESS; | |
| RemoveList: | |
| RemoveEntryList (&Alloc->List); | |
| FreePool (Alloc); | |
| FreeBuffer: | |
| CoherentPciIoFreeBuffer (This, Pages, AllocAddress); | |
| return Status; | |
| } | |
| /** | |
| Provides the PCI controller-specific addresses needed to access system memory. | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| @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 PCI 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 PCI 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. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| NonCoherentPciIoMap ( | |
| IN EFI_PCI_IO_PROTOCOL *This, | |
| IN EFI_PCI_IO_PROTOCOL_OPERATION Operation, | |
| IN VOID *HostAddress, | |
| IN OUT UINTN *NumberOfBytes, | |
| OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, | |
| OUT VOID **Mapping | |
| ) | |
| { | |
| NON_DISCOVERABLE_PCI_DEVICE *Dev; | |
| EFI_STATUS Status; | |
| NON_DISCOVERABLE_PCI_DEVICE_MAP_INFO *MapInfo; | |
| UINTN AlignMask; | |
| VOID *AllocAddress; | |
| EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor; | |
| BOOLEAN Bounce; | |
| MapInfo = AllocatePool (sizeof *MapInfo); | |
| if (MapInfo == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| MapInfo->HostAddress = HostAddress; | |
| MapInfo->Operation = Operation; | |
| MapInfo->NumberOfBytes = *NumberOfBytes; | |
| Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This); | |
| // | |
| // If this device does not support 64-bit DMA addressing, we need to allocate | |
| // a bounce buffer and copy over the data in case HostAddress >= 4 GB. | |
| // | |
| Bounce = ((Dev->Attributes & EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0 && | |
| (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress + *NumberOfBytes > SIZE_4GB); | |
| if (!Bounce) { | |
| switch (Operation) { | |
| case EfiPciIoOperationBusMasterRead: | |
| case EfiPciIoOperationBusMasterWrite: | |
| // | |
| // For streaming DMA, it is sufficient if the buffer is aligned to | |
| // the CPUs DMA buffer alignment. | |
| // | |
| AlignMask = mCpu->DmaBufferAlignment - 1; | |
| if ((((UINTN) HostAddress | *NumberOfBytes) & AlignMask) == 0) { | |
| break; | |
| } | |
| // fall through | |
| case EfiPciIoOperationBusMasterCommonBuffer: | |
| // | |
| // Check whether the host address refers to an uncached mapping. | |
| // | |
| Status = gDS->GetMemorySpaceDescriptor ( | |
| (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress, | |
| &GcdDescriptor); | |
| if (EFI_ERROR (Status) || | |
| (GcdDescriptor.Attributes & (EFI_MEMORY_WB|EFI_MEMORY_WT)) != 0) { | |
| Bounce = TRUE; | |
| } | |
| break; | |
| default: | |
| ASSERT (FALSE); | |
| } | |
| } | |
| if (Bounce) { | |
| if (Operation == EfiPciIoOperationBusMasterCommonBuffer) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto FreeMapInfo; | |
| } | |
| Status = NonCoherentPciIoAllocateBuffer (This, AllocateAnyPages, | |
| EfiBootServicesData, EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes), | |
| &AllocAddress, EFI_PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE); | |
| if (EFI_ERROR (Status)) { | |
| goto FreeMapInfo; | |
| } | |
| MapInfo->AllocAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress; | |
| if (Operation == EfiPciIoOperationBusMasterRead) { | |
| gBS->CopyMem (AllocAddress, HostAddress, *NumberOfBytes); | |
| } | |
| *DeviceAddress = MapInfo->AllocAddress; | |
| } else { | |
| MapInfo->AllocAddress = 0; | |
| *DeviceAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress; | |
| // | |
| // We are not using a bounce buffer: the mapping is sufficiently | |
| // aligned to allow us to simply flush the caches. Note that cleaning | |
| // the caches is necessary for both data directions: | |
| // - for bus master read, we want the latest data to be present | |
| // in main memory | |
| // - for bus master write, we don't want any stale dirty cachelines that | |
| // may be written back unexpectedly, and clobber the data written to | |
| // main memory by the device. | |
| // | |
| mCpu->FlushDataCache (mCpu, (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress, | |
| *NumberOfBytes, EfiCpuFlushTypeWriteBack); | |
| } | |
| *Mapping = MapInfo; | |
| return EFI_SUCCESS; | |
| FreeMapInfo: | |
| FreePool (MapInfo); | |
| return Status; | |
| } | |
| /** | |
| Completes the Map() operation and releases any corresponding resources. | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| @param Mapping The mapping value returned from Map(). | |
| @retval EFI_SUCCESS The range was unmapped. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| NonCoherentPciIoUnmap ( | |
| IN EFI_PCI_IO_PROTOCOL *This, | |
| IN VOID *Mapping | |
| ) | |
| { | |
| NON_DISCOVERABLE_PCI_DEVICE_MAP_INFO *MapInfo; | |
| if (Mapping == NULL) { | |
| return EFI_DEVICE_ERROR; | |
| } | |
| MapInfo = Mapping; | |
| if (MapInfo->AllocAddress != 0) { | |
| // | |
| // We are using a bounce buffer: copy back the data if necessary, | |
| // and free the buffer. | |
| // | |
| if (MapInfo->Operation == EfiPciIoOperationBusMasterWrite) { | |
| gBS->CopyMem (MapInfo->HostAddress, (VOID *)(UINTN)MapInfo->AllocAddress, | |
| MapInfo->NumberOfBytes); | |
| } | |
| NonCoherentPciIoFreeBuffer (This, | |
| EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes), | |
| (VOID *)(UINTN)MapInfo->AllocAddress); | |
| } else { | |
| // | |
| // We are *not* using a bounce buffer: if this is a bus master write, | |
| // we have to invalidate the caches so the CPU will see the uncached | |
| // data written by the device. | |
| // | |
| if (MapInfo->Operation == EfiPciIoOperationBusMasterWrite) { | |
| mCpu->FlushDataCache (mCpu, | |
| (EFI_PHYSICAL_ADDRESS)(UINTN)MapInfo->HostAddress, | |
| MapInfo->NumberOfBytes, EfiCpuFlushTypeInvalidate); | |
| } | |
| } | |
| FreePool (MapInfo); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Flushes all PCI posted write transactions from a PCI host bridge to system memory. | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| PciIoFlush ( | |
| IN EFI_PCI_IO_PROTOCOL *This | |
| ) | |
| { | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Retrieves this PCI controller's current PCI bus number, device number, and function number. | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| @param SegmentNumber The PCI controller's current PCI segment number. | |
| @param BusNumber The PCI controller's current PCI bus number. | |
| @param DeviceNumber The PCI controller's current PCI device number. | |
| @param FunctionNumber The PCI controller's current PCI function number. | |
| @retval EFI_SUCCESS The PCI controller location was returned. | |
| @retval EFI_INVALID_PARAMETER One or more parameters are invalid. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| PciIoGetLocation ( | |
| IN EFI_PCI_IO_PROTOCOL *This, | |
| OUT UINTN *SegmentNumber, | |
| OUT UINTN *BusNumber, | |
| OUT UINTN *DeviceNumber, | |
| OUT UINTN *FunctionNumber | |
| ) | |
| { | |
| if (SegmentNumber == NULL || | |
| BusNumber == NULL || | |
| DeviceNumber == NULL || | |
| FunctionNumber == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| *SegmentNumber = 0; | |
| *BusNumber = 0xff; | |
| *DeviceNumber = 0; | |
| *FunctionNumber = 0; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Performs an operation on the attributes that this PCI controller supports. The operations include | |
| getting the set of supported attributes, retrieving the current attributes, setting the current | |
| attributes, enabling attributes, and disabling attributes. | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| @param Operation The operation to perform on the attributes for this PCI controller. | |
| @param Attributes The mask of attributes that are used for Set, Enable, and Disable | |
| operations. | |
| @param Result A pointer to the result mask of attributes that are returned for the Get | |
| and Supported operations. | |
| @retval EFI_SUCCESS The operation on the PCI controller's attributes was completed. | |
| @retval EFI_INVALID_PARAMETER One or more parameters are invalid. | |
| @retval EFI_UNSUPPORTED one or more of the bits set in | |
| Attributes are not supported by this PCI controller or one of | |
| its parent bridges when Operation is Set, Enable or Disable. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| PciIoAttributes ( | |
| IN EFI_PCI_IO_PROTOCOL *This, | |
| IN EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION Operation, | |
| IN UINT64 Attributes, | |
| OUT UINT64 *Result OPTIONAL | |
| ) | |
| { | |
| NON_DISCOVERABLE_PCI_DEVICE *Dev; | |
| BOOLEAN Enable; | |
| Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This); | |
| Enable = FALSE; | |
| switch (Operation) { | |
| case EfiPciIoAttributeOperationGet: | |
| if (Result == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| *Result = Dev->Attributes; | |
| break; | |
| case EfiPciIoAttributeOperationSupported: | |
| if (Result == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| *Result = EFI_PCI_DEVICE_ENABLE | EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE; | |
| break; | |
| case EfiPciIoAttributeOperationEnable: | |
| Attributes |= Dev->Attributes; | |
| case EfiPciIoAttributeOperationSet: | |
| Enable = ((~Dev->Attributes & Attributes) & EFI_PCI_DEVICE_ENABLE) != 0; | |
| Dev->Attributes = Attributes; | |
| break; | |
| case EfiPciIoAttributeOperationDisable: | |
| Dev->Attributes &= ~Attributes; | |
| break; | |
| default: | |
| return EFI_INVALID_PARAMETER; | |
| }; | |
| // | |
| // If we're setting any of the EFI_PCI_DEVICE_ENABLE bits, perform | |
| // the device specific initialization now. | |
| // | |
| if (Enable && !Dev->Enabled && Dev->Device->Initialize != NULL) { | |
| Dev->Device->Initialize (Dev->Device); | |
| Dev->Enabled = TRUE; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Gets the attributes that this PCI controller supports setting on a BAR using | |
| SetBarAttributes(), and retrieves the list of resource descriptors for a BAR. | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| @param BarIndex The BAR index of the standard PCI Configuration header to use as the | |
| base address for resource range. The legal range for this field is 0..5. | |
| @param Supports A pointer to the mask of attributes that this PCI controller supports | |
| setting for this BAR with SetBarAttributes(). | |
| @param Resources A pointer to the ACPI 2.0 resource descriptors that describe the current | |
| configuration of this BAR of the PCI controller. | |
| @retval EFI_SUCCESS If Supports is not NULL, then the attributes that the PCI | |
| controller supports are returned in Supports. If Resources | |
| is not NULL, then the ACPI 2.0 resource descriptors that the PCI | |
| controller is currently using are returned in Resources. | |
| @retval EFI_INVALID_PARAMETER Both Supports and Attributes are NULL. | |
| @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. | |
| @retval EFI_OUT_OF_RESOURCES There are not enough resources available to allocate | |
| Resources. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| PciIoGetBarAttributes ( | |
| IN EFI_PCI_IO_PROTOCOL *This, | |
| IN UINT8 BarIndex, | |
| OUT UINT64 *Supports OPTIONAL, | |
| OUT VOID **Resources OPTIONAL | |
| ) | |
| { | |
| NON_DISCOVERABLE_PCI_DEVICE *Dev; | |
| EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor; | |
| EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *BarDesc; | |
| EFI_ACPI_END_TAG_DESCRIPTOR *End; | |
| EFI_STATUS Status; | |
| if (Supports == NULL && Resources == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This); | |
| Status = GetBarResource (Dev, BarIndex, &BarDesc); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Don't expose any configurable attributes for our emulated BAR | |
| // | |
| if (Supports != NULL) { | |
| *Supports = 0; | |
| } | |
| if (Resources != NULL) { | |
| Descriptor = AllocatePool (sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) + | |
| sizeof (EFI_ACPI_END_TAG_DESCRIPTOR)); | |
| if (Descriptor == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| CopyMem (Descriptor, BarDesc, sizeof *Descriptor); | |
| End = (EFI_ACPI_END_TAG_DESCRIPTOR *) (Descriptor + 1); | |
| End->Desc = ACPI_END_TAG_DESCRIPTOR; | |
| End->Checksum = 0; | |
| *Resources = Descriptor; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Sets the attributes for a range of a BAR on a PCI controller. | |
| @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. | |
| @param Attributes The mask of attributes to set for the resource range specified by | |
| BarIndex, Offset, and Length. | |
| @param BarIndex The BAR index of the standard PCI Configuration header to use as the | |
| base address for resource range. The legal range for this field is 0..5. | |
| @param Offset A pointer to the BAR relative base address of the resource range to be | |
| modified by the attributes specified by Attributes. | |
| @param Length A pointer to the length of the resource range to be modified by the | |
| attributes specified by Attributes. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| PciIoSetBarAttributes ( | |
| IN EFI_PCI_IO_PROTOCOL *This, | |
| IN UINT64 Attributes, | |
| IN UINT8 BarIndex, | |
| IN OUT UINT64 *Offset, | |
| IN OUT UINT64 *Length | |
| ) | |
| { | |
| ASSERT (FALSE); | |
| return EFI_UNSUPPORTED; | |
| } | |
| STATIC CONST EFI_PCI_IO_PROTOCOL PciIoTemplate = | |
| { | |
| PciIoPollMem, | |
| PciIoPollIo, | |
| { PciIoMemRead, PciIoMemWrite }, | |
| { PciIoIoRead, PciIoIoWrite }, | |
| { PciIoPciRead, PciIoPciWrite }, | |
| PciIoCopyMem, | |
| CoherentPciIoMap, | |
| CoherentPciIoUnmap, | |
| CoherentPciIoAllocateBuffer, | |
| CoherentPciIoFreeBuffer, | |
| PciIoFlush, | |
| PciIoGetLocation, | |
| PciIoAttributes, | |
| PciIoGetBarAttributes, | |
| PciIoSetBarAttributes, | |
| 0, | |
| 0 | |
| }; | |
| /** | |
| Initialize PciIo Protocol. | |
| @param Dev Point to NON_DISCOVERABLE_PCI_DEVICE instance. | |
| **/ | |
| VOID | |
| InitializePciIoProtocol ( | |
| NON_DISCOVERABLE_PCI_DEVICE *Dev | |
| ) | |
| { | |
| EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Desc; | |
| INTN Idx; | |
| InitializeListHead (&Dev->UncachedAllocationList); | |
| Dev->ConfigSpace.Hdr.VendorId = PCI_ID_VENDOR_UNKNOWN; | |
| Dev->ConfigSpace.Hdr.DeviceId = PCI_ID_DEVICE_DONTCARE; | |
| // Copy protocol structure | |
| CopyMem(&Dev->PciIo, &PciIoTemplate, sizeof PciIoTemplate); | |
| if (Dev->Device->DmaType == NonDiscoverableDeviceDmaTypeNonCoherent) { | |
| Dev->PciIo.AllocateBuffer = NonCoherentPciIoAllocateBuffer; | |
| Dev->PciIo.FreeBuffer = NonCoherentPciIoFreeBuffer; | |
| Dev->PciIo.Map = NonCoherentPciIoMap; | |
| Dev->PciIo.Unmap = NonCoherentPciIoUnmap; | |
| } | |
| if (CompareGuid (Dev->Device->Type, &gEdkiiNonDiscoverableAhciDeviceGuid)) { | |
| Dev->ConfigSpace.Hdr.ClassCode[0] = PCI_IF_MASS_STORAGE_AHCI; | |
| Dev->ConfigSpace.Hdr.ClassCode[1] = PCI_CLASS_MASS_STORAGE_SATADPA; | |
| Dev->ConfigSpace.Hdr.ClassCode[2] = PCI_CLASS_MASS_STORAGE; | |
| Dev->BarOffset = 5; | |
| } else if (CompareGuid (Dev->Device->Type, | |
| &gEdkiiNonDiscoverableEhciDeviceGuid)) { | |
| Dev->ConfigSpace.Hdr.ClassCode[0] = PCI_IF_EHCI; | |
| Dev->ConfigSpace.Hdr.ClassCode[1] = PCI_CLASS_SERIAL_USB; | |
| Dev->ConfigSpace.Hdr.ClassCode[2] = PCI_CLASS_SERIAL; | |
| Dev->BarOffset = 0; | |
| } else if (CompareGuid (Dev->Device->Type, | |
| &gEdkiiNonDiscoverableNvmeDeviceGuid)) { | |
| Dev->ConfigSpace.Hdr.ClassCode[0] = 0x2; // PCI_IF_NVMHCI | |
| Dev->ConfigSpace.Hdr.ClassCode[1] = 0x8; // PCI_CLASS_MASS_STORAGE_NVM | |
| Dev->ConfigSpace.Hdr.ClassCode[2] = PCI_CLASS_MASS_STORAGE; | |
| Dev->BarOffset = 0; | |
| } else if (CompareGuid (Dev->Device->Type, | |
| &gEdkiiNonDiscoverableOhciDeviceGuid)) { | |
| Dev->ConfigSpace.Hdr.ClassCode[0] = PCI_IF_OHCI; | |
| Dev->ConfigSpace.Hdr.ClassCode[1] = PCI_CLASS_SERIAL_USB; | |
| Dev->ConfigSpace.Hdr.ClassCode[2] = PCI_CLASS_SERIAL; | |
| Dev->BarOffset = 0; | |
| } else if (CompareGuid (Dev->Device->Type, | |
| &gEdkiiNonDiscoverableSdhciDeviceGuid)) { | |
| Dev->ConfigSpace.Hdr.ClassCode[0] = 0x0; // don't care | |
| Dev->ConfigSpace.Hdr.ClassCode[1] = PCI_SUBCLASS_SD_HOST_CONTROLLER; | |
| Dev->ConfigSpace.Hdr.ClassCode[2] = PCI_CLASS_SYSTEM_PERIPHERAL; | |
| Dev->BarOffset = 0; | |
| } else if (CompareGuid (Dev->Device->Type, | |
| &gEdkiiNonDiscoverableXhciDeviceGuid)) { | |
| Dev->ConfigSpace.Hdr.ClassCode[0] = PCI_IF_XHCI; | |
| Dev->ConfigSpace.Hdr.ClassCode[1] = PCI_CLASS_SERIAL_USB; | |
| Dev->ConfigSpace.Hdr.ClassCode[2] = PCI_CLASS_SERIAL; | |
| Dev->BarOffset = 0; | |
| } else if (CompareGuid (Dev->Device->Type, | |
| &gEdkiiNonDiscoverableUhciDeviceGuid)) { | |
| Dev->ConfigSpace.Hdr.ClassCode[0] = PCI_IF_UHCI; | |
| Dev->ConfigSpace.Hdr.ClassCode[1] = PCI_CLASS_SERIAL_USB; | |
| Dev->ConfigSpace.Hdr.ClassCode[2] = PCI_CLASS_SERIAL; | |
| Dev->BarOffset = 0; | |
| } else if (CompareGuid (Dev->Device->Type, | |
| &gEdkiiNonDiscoverableUfsDeviceGuid)) { | |
| Dev->ConfigSpace.Hdr.ClassCode[0] = 0x0; // don't care | |
| Dev->ConfigSpace.Hdr.ClassCode[1] = 0x9; // UFS controller subclass; | |
| Dev->ConfigSpace.Hdr.ClassCode[2] = PCI_CLASS_MASS_STORAGE; | |
| Dev->BarOffset = 0; | |
| } else { | |
| ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER); | |
| } | |
| // | |
| // Iterate over the resources to populate the virtual BARs | |
| // | |
| Idx = Dev->BarOffset; | |
| for (Desc = Dev->Device->Resources, Dev->BarCount = 0; | |
| Desc->Desc != ACPI_END_TAG_DESCRIPTOR; | |
| Desc = (VOID *)((UINT8 *)Desc + Desc->Len + 3)) { | |
| ASSERT (Desc->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR); | |
| ASSERT (Desc->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM); | |
| if (Idx >= PCI_MAX_BARS || | |
| (Idx == PCI_MAX_BARS - 1 && Desc->AddrSpaceGranularity == 64)) { | |
| DEBUG ((DEBUG_ERROR, | |
| "%a: resource count exceeds number of emulated BARs\n", | |
| __FUNCTION__)); | |
| ASSERT (FALSE); | |
| break; | |
| } | |
| Dev->ConfigSpace.Device.Bar[Idx] = (UINT32)Desc->AddrRangeMin; | |
| Dev->BarCount++; | |
| if (Desc->AddrSpaceGranularity == 64) { | |
| Dev->ConfigSpace.Device.Bar[Idx] |= 0x4; | |
| Dev->ConfigSpace.Device.Bar[++Idx] = (UINT32)RShiftU64 ( | |
| Desc->AddrRangeMin, 32); | |
| } | |
| } | |
| } |