--- /dev/null
+/*++\r
+\r
+Copyright (c) 2004 - 2007, Intel Corporation \r
+All rights reserved. This program and the accompanying materials \r
+are licensed and made available under the terms and conditions of the BSD License \r
+which accompanies this distribution. The full text of the license may be found at \r
+http://opensource.org/licenses/bsd-license.php \r
+ \r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
+\r
+Module Name:\r
+ CpuIo.c\r
+\r
+Abstract:\r
+\r
+ This is the code that publishes the CPU I/O Protocol.\r
+ The intent herein is to have a single I/O service that can load\r
+ as early as possible, extend into runtime, and be layered upon by\r
+ the implementations of architectural protocols and the PCI Root\r
+ Bridge I/O Protocol.\r
+\r
+--*/\r
+\r
+#include "CpuIo.h"\r
+#include "CpuIoAccess.h"\r
+\r
+#define IA32_MAX_IO_ADDRESS 0xFFFF\r
+\r
+EFI_CPU_IO_PROTOCOL mCpuIo = {\r
+ {\r
+ CpuMemoryServiceRead,\r
+ CpuMemoryServiceWrite\r
+ },\r
+ {\r
+ CpuIoServiceRead,\r
+ CpuIoServiceWrite\r
+ }\r
+};\r
+\r
+STATIC\r
+EFI_STATUS\r
+CpuIoMemRW (\r
+ IN EFI_CPU_IO_PROTOCOL_WIDTH Width,\r
+ IN UINTN Count,\r
+ IN BOOLEAN DestinationStrideFlag,\r
+ OUT PTR Destination,\r
+ IN BOOLEAN SourceStrideFlag,\r
+ IN PTR Source\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ Private service to perform memory mapped I/O read/write\r
+\r
+Arguments:\r
+\r
+ Width - Width of the memory mapped I/O operation\r
+ Count - Count of the number of accesses to perform\r
+ DestinationStrideFlag - Boolean flag indicates if the destination is to be incremented\r
+ Destination - Destination of the memory mapped I/O operation\r
+ SourceStrideFlag - Boolean flag indicates if the source is to be incremented\r
+ Source - Source of the memory mapped I/O operation\r
+\r
+Returns:\r
+\r
+ EFI_SUCCESS - Successful operation\r
+ EFI_INVALID_PARAMETER - Width is invalid\r
+\r
+--*/\r
+{\r
+ UINTN Stride;\r
+ UINTN DestinationStride;\r
+ UINTN SourceStride;\r
+\r
+ Width = Width & 0x03;\r
+ Stride = (UINTN)1 << Width;\r
+ DestinationStride = DestinationStrideFlag ? Stride : 0;\r
+ SourceStride = SourceStrideFlag ? Stride : 0;\r
+\r
+ //\r
+ // Loop for each iteration and move the data\r
+ //\r
+ switch (Width) {\r
+ case EfiCpuIoWidthUint8:\r
+ for (; Count > 0; Count--, Destination.buf += DestinationStride, Source.buf += SourceStride) {\r
+ MemoryFence();\r
+ *Destination.ui8 = *Source.ui8;\r
+ MemoryFence();\r
+ }\r
+ break;\r
+\r
+ case EfiCpuIoWidthUint16:\r
+ for (; Count > 0; Count--, Destination.buf += DestinationStride, Source.buf += SourceStride) {\r
+ MemoryFence ();\r
+ *Destination.ui16 = *Source.ui16;\r
+ MemoryFence ();\r
+ }\r
+ break;\r
+\r
+ case EfiCpuIoWidthUint32:\r
+ for (; Count > 0; Count--, Destination.buf += DestinationStride, Source.buf += SourceStride) {\r
+ MemoryFence ();\r
+ *Destination.ui32 = *Source.ui32;\r
+ MemoryFence ();\r
+ }\r
+ break;\r
+\r
+ case EfiCpuIoWidthUint64:\r
+ for (; Count > 0; Count--, Destination.buf += DestinationStride, Source.buf += SourceStride) {\r
+ MemoryFence ();\r
+ *Destination.ui64 = *Source.ui64;\r
+ MemoryFence ();\r
+ }\r
+ break;\r
+\r
+ default:\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+CpuMemoryServiceRead (\r
+ IN EFI_CPU_IO_PROTOCOL *This,\r
+ IN EFI_CPU_IO_PROTOCOL_WIDTH Width,\r
+ IN UINT64 Address,\r
+ IN UINTN Count,\r
+ OUT VOID *Buffer\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ Perform the memory mapped I/O read service\r
+\r
+Arguments:\r
+\r
+ This - Pointer to an instance of the CPU I/O Protocol\r
+ Width - Width of the memory mapped I/O operation\r
+ Address - Base address of the memory mapped I/O operation\r
+ Count - Count of the number of accesses to perform\r
+ Buffer - Pointer to the destination buffer to store the results\r
+\r
+Returns:\r
+\r
+ EFI_SUCCESS - The data was read.\r
+ EFI_INVALID_PARAMETER - Width is invalid.\r
+ EFI_INVALID_PARAMETER - Buffer is NULL.\r
+ EFI_UNSUPPORTED - The Buffer is not aligned for the given Width.\r
+ EFI_UNSUPPORTED - The address range specified by Address, Width,\r
+ and Count is not valid.\r
+\r
+--*/\r
+{\r
+ PTR Source;\r
+ PTR Destination;\r
+ EFI_STATUS Status;\r
+\r
+ Status = CpuIoCheckParameter (Width, Address, Count, Buffer, EFI_MAX_ADDRESS);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Destination.buf = Buffer;\r
+ Source.buf = (VOID *) (UINTN) Address;\r
+\r
+ if (Width >= EfiCpuIoWidthUint8 && Width <= EfiCpuIoWidthUint64) {\r
+ return CpuIoMemRW (Width, Count, TRUE, Destination, TRUE, Source);\r
+ }\r
+\r
+ if (Width >= EfiCpuIoWidthFifoUint8 && Width <= EfiCpuIoWidthFifoUint64) {\r
+ return CpuIoMemRW (Width, Count, TRUE, Destination, FALSE, Source);\r
+ }\r
+\r
+ if (Width >= EfiCpuIoWidthFillUint8 && Width <= EfiCpuIoWidthFillUint64) {\r
+ return CpuIoMemRW (Width, Count, FALSE, Destination, TRUE, Source);\r
+ }\r
+\r
+ return EFI_INVALID_PARAMETER;\r
+}\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+CpuMemoryServiceWrite (\r
+ IN EFI_CPU_IO_PROTOCOL *This,\r
+ IN EFI_CPU_IO_PROTOCOL_WIDTH Width,\r
+ IN UINT64 Address,\r
+ IN UINTN Count,\r
+ IN VOID *Buffer\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ Perform the memory mapped I/O write service\r
+\r
+Arguments:\r
+\r
+ This - Pointer to an instance of the CPU I/O Protocol\r
+ Width - Width of the memory mapped I/O operation\r
+ Address - Base address of the memory mapped I/O operation\r
+ Count - Count of the number of accesses to perform\r
+ Buffer - Pointer to the source buffer from which to write data\r
+\r
+Returns:\r
+\r
+ EFI_SUCCESS - The data was written.\r
+ EFI_INVALID_PARAMETER - Width is invalid.\r
+ EFI_INVALID_PARAMETER - Buffer is NULL.\r
+ EFI_UNSUPPORTED - The Buffer is not aligned for the given Width.\r
+ EFI_UNSUPPORTED - The address range specified by Address, Width,\r
+ and Count is not valid.\r
+\r
+--*/\r
+{\r
+ PTR Source;\r
+ PTR Destination;\r
+ EFI_STATUS Status;\r
+\r
+ Status = CpuIoCheckParameter (Width, Address, Count, Buffer, EFI_MAX_ADDRESS);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Destination.buf = (VOID *) (UINTN) Address;\r
+ Source.buf = Buffer;\r
+\r
+ if (Width >= EfiCpuIoWidthUint8 && Width <= EfiCpuIoWidthUint64) {\r
+ return CpuIoMemRW (Width, Count, TRUE, Destination, TRUE, Source);\r
+ }\r
+\r
+ if (Width >= EfiCpuIoWidthFifoUint8 && Width <= EfiCpuIoWidthFifoUint64) {\r
+ return CpuIoMemRW (Width, Count, FALSE, Destination, TRUE, Source);\r
+ }\r
+\r
+ if (Width >= EfiCpuIoWidthFillUint8 && Width <= EfiCpuIoWidthFillUint64) {\r
+ return CpuIoMemRW (Width, Count, TRUE, Destination, FALSE, Source);\r
+ }\r
+\r
+ return EFI_INVALID_PARAMETER;\r
+}\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+CpuIoServiceRead (\r
+ IN EFI_CPU_IO_PROTOCOL *This,\r
+ IN EFI_CPU_IO_PROTOCOL_WIDTH Width,\r
+ IN UINT64 UserAddress,\r
+ IN UINTN Count,\r
+ OUT VOID *UserBuffer\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ Perform the port I/O read service\r
+\r
+Arguments:\r
+\r
+ This - Pointer to an instance of the CPU I/O Protocol\r
+ Width - Width of the port I/O operation\r
+ Address - Base address of the port I/O operation\r
+ Count - Count of the number of accesses to perform\r
+ Buffer - Pointer to the destination buffer to store the results\r
+\r
+Returns:\r
+\r
+ EFI_SUCCESS - The data was read.\r
+ EFI_INVALID_PARAMETER - Width is invalid.\r
+ EFI_INVALID_PARAMETER - Buffer is NULL.\r
+ EFI_UNSUPPORTED - The Buffer is not aligned for the given Width.\r
+ EFI_UNSUPPORTED - The address range specified by Address, Width,\r
+ and Count is not valid.\r
+\r
+--*/\r
+{\r
+ UINTN InStride;\r
+ UINTN OutStride;\r
+ UINTN Address;\r
+ PTR Buffer;\r
+ EFI_STATUS Status;\r
+\r
+ Buffer.buf = (UINT8 *) UserBuffer;\r
+\r
+ if (Width >= EfiCpuIoWidthMaximum) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = CpuIoCheckParameter (Width, UserAddress, Count, UserBuffer, IA32_MAX_IO_ADDRESS);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Address = (UINTN) UserAddress;\r
+ InStride = (UINTN)1 << (Width & 0x03);\r
+ OutStride = InStride;\r
+ if (Width >= EfiCpuIoWidthFifoUint8 && Width <= EfiCpuIoWidthFifoUint64) {\r
+ InStride = 0;\r
+ }\r
+\r
+ if (Width >= EfiCpuIoWidthFillUint8 && Width <= EfiCpuIoWidthFillUint64) {\r
+ OutStride = 0;\r
+ }\r
+\r
+ Width = Width & 0x03;\r
+\r
+ //\r
+ // Loop for each iteration and move the data\r
+ //\r
+ switch (Width) {\r
+ case EfiCpuIoWidthUint8:\r
+ for (; Count > 0; Count--, Buffer.buf += OutStride, Address += InStride) {\r
+ *Buffer.ui8 = CpuIoRead8 ((UINT16) Address);\r
+ }\r
+ break;\r
+\r
+ case EfiCpuIoWidthUint16:\r
+ for (; Count > 0; Count--, Buffer.buf += OutStride, Address += InStride) {\r
+ *Buffer.ui16 = CpuIoRead16 ((UINT16) Address);\r
+ }\r
+ break;\r
+\r
+ case EfiCpuIoWidthUint32:\r
+ for (; Count > 0; Count--, Buffer.buf += OutStride, Address += InStride) {\r
+ *Buffer.ui32 = CpuIoRead32 ((UINT16) Address);\r
+ }\r
+ break;\r
+\r
+ default:\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+CpuIoServiceWrite (\r
+ IN EFI_CPU_IO_PROTOCOL *This,\r
+ IN EFI_CPU_IO_PROTOCOL_WIDTH Width,\r
+ IN UINT64 UserAddress,\r
+ IN UINTN Count,\r
+ IN VOID *UserBuffer\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ Perform the port I/O write service\r
+\r
+Arguments:\r
+\r
+ This - Pointer to an instance of the CPU I/O Protocol\r
+ Width - Width of the port I/O operation\r
+ Address - Base address of the port I/O operation\r
+ Count - Count of the number of accesses to perform\r
+ Buffer - Pointer to the source buffer from which to write data\r
+\r
+Returns:\r
+\r
+ EFI_SUCCESS - The data was written.\r
+ EFI_INVALID_PARAMETER - Width is invalid.\r
+ EFI_INVALID_PARAMETER - Buffer is NULL.\r
+ EFI_UNSUPPORTED - The Buffer is not aligned for the given Width.\r
+ EFI_UNSUPPORTED - The address range specified by Address, Width,\r
+ and Count is not valid.\r
+\r
+--*/\r
+{\r
+ UINTN InStride;\r
+ UINTN OutStride;\r
+ UINTN Address;\r
+ PTR Buffer;\r
+ EFI_STATUS Status;\r
+\r
+ Buffer.buf = (UINT8 *) UserBuffer;\r
+\r
+ if (Width >= EfiCpuIoWidthMaximum) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = CpuIoCheckParameter (Width, UserAddress, Count, UserBuffer, IA32_MAX_IO_ADDRESS);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Address = (UINTN) UserAddress;\r
+ InStride = (UINTN)1 << (Width & 0x03);\r
+ OutStride = InStride;\r
+ if (Width >= EfiCpuIoWidthFifoUint8 && Width <= EfiCpuIoWidthFifoUint64) {\r
+ InStride = 0;\r
+ }\r
+\r
+ if (Width >= EfiCpuIoWidthFillUint8 && Width <= EfiCpuIoWidthFillUint64) {\r
+ OutStride = 0;\r
+ }\r
+\r
+ Width = Width & 0x03;\r
+\r
+ //\r
+ // Loop for each iteration and move the data\r
+ //\r
+ switch (Width) {\r
+ case EfiCpuIoWidthUint8:\r
+ for (; Count > 0; Count--, Buffer.buf += OutStride, Address += InStride) {\r
+ CpuIoWrite8 ((UINT16) Address, *Buffer.ui8);\r
+ }\r
+ break;\r
+\r
+ case EfiCpuIoWidthUint16:\r
+ for (; Count > 0; Count--, Buffer.buf += OutStride, Address += InStride) {\r
+ CpuIoWrite16 ((UINT16) Address, *Buffer.ui16);\r
+ }\r
+ break;\r
+\r
+ case EfiCpuIoWidthUint32:\r
+ for (; Count > 0; Count--, Buffer.buf += OutStride, Address += InStride) {\r
+ CpuIoWrite32 ((UINT16) Address, *Buffer.ui32);\r
+ }\r
+ break;\r
+\r
+ default:\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+CpuIoInitialize (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ CpuIo driver entry point.\r
+\r
+Arguments:\r
+\r
+ ImageHandle - The firmware allocated handle for the EFI image.\r
+ SystemTable - A pointer to the EFI System Table.\r
+\r
+Returns:\r
+\r
+ EFI_SUCCESS - The driver was initialized.\r
+ EFI_OUT_OF_RESOURCES - The request could not be completed due to a lack of resources.\r
+\r
+--*/\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE Handle;\r
+\r
+ Handle = NULL;\r
+ Status = SystemTable->BootServices->InstallProtocolInterface (\r
+ &Handle,\r
+ &gEfiCpuIoProtocolGuid,\r
+ EFI_NATIVE_INTERFACE,\r
+ &mCpuIo\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ return Status;\r
+}\r
+\r
+EFI_STATUS\r
+CpuIoCheckParameter (\r
+ IN EFI_CPU_IO_PROTOCOL_WIDTH Width,\r
+ IN UINT64 Address,\r
+ IN UINTN Count,\r
+ IN VOID *Buffer,\r
+ IN UINT64 Limit\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ Check the validation of parameters for CPU I/O interface functions.\r
+\r
+Arguments:\r
+\r
+ Width - Width of the Memory Access\r
+ Address - Address of the Memory access\r
+ Count - Count of the number of accesses to perform\r
+ Buffer - Pointer to the buffer to read from memory\r
+ Buffer - Memory buffer for the I/O operation\r
+ Limit - Maximum address supported\r
+\r
+Returns:\r
+\r
+ EFI_INVALID_PARAMETER - Buffer is NULL\r
+ EFI_UNSUPPORTED - The address range specified by Width, Address and Count is invalid\r
+ EFI_UNSUPPORTED - The memory buffer is not aligned\r
+ EFI_SUCCESS - Parameters are OK\r
+\r
+--*/\r
+{\r
+ UINTN AlignMask;\r
+\r
+ if (Buffer == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Address > Limit) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // For FiFo type, the target address won't increase during the access,\r
+ // so treat count as 1\r
+ //\r
+ if (Width >= EfiCpuIoWidthFifoUint8 && Width <= EfiCpuIoWidthFifoUint64) {\r
+ Count = 1;\r
+ }\r
+\r
+ Width = Width & 0x03;\r
+ if (Address - 1 + ((UINTN)1 << Width) * Count > Limit) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ AlignMask = ((UINTN)1 << Width) - 1;\r
+ if ((UINTN) Buffer & AlignMask) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r