--- /dev/null
+/** @file\r
+Routine procedures for memory allocate/free.\r
+\r
+Copyright (c) 2013-2015 Intel Corporation.\r
+\r
+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
+**/\r
+\r
+\r
+#include "OhcPeim.h"\r
+\r
+\r
+/**\r
+ Allocate a block of memory to be used by the buffer pool.\r
+\r
+ Use Redirect memory services to allocate memmory so that USB DMA transfers do\r
+ not cause IMR violations on Quark.\r
+\r
+ @param Pool The buffer pool to allocate memory for.\r
+ @param Pages How many pages to allocate.\r
+\r
+ @return The allocated memory block or NULL if failed.\r
+\r
+**/\r
+USBHC_MEM_BLOCK *\r
+UsbHcAllocMemBlock (\r
+ IN USBHC_MEM_POOL *Pool,\r
+ IN UINTN Pages\r
+ )\r
+{\r
+ USBHC_MEM_BLOCK *Block;\r
+ VOID *BufHost;\r
+ VOID *Mapping;\r
+ EFI_PHYSICAL_ADDRESS MappedAddr;\r
+ EFI_STATUS Status;\r
+ UINTN PageNumber;\r
+ EFI_PHYSICAL_ADDRESS TempPtr;\r
+\r
+ Mapping = NULL;\r
+ PageNumber = sizeof(USBHC_MEM_BLOCK)/PAGESIZE +1;\r
+ Status = PeiServicesAllocatePages (\r
+ EfiBootServicesCode,\r
+ PageNumber,\r
+ &TempPtr\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return NULL;\r
+ }\r
+ ZeroMem ((VOID *)(UINTN)TempPtr, PageNumber*EFI_PAGE_SIZE);\r
+\r
+ //\r
+ // each bit in the bit array represents USBHC_MEM_UNIT\r
+ // bytes of memory in the memory block.\r
+ //\r
+ ASSERT (USBHC_MEM_UNIT * 8 <= EFI_PAGE_SIZE);\r
+\r
+ Block = (USBHC_MEM_BLOCK*)(UINTN)TempPtr;\r
+ Block->BufLen = EFI_PAGES_TO_SIZE (Pages);\r
+ Block->BitsLen = Block->BufLen / (USBHC_MEM_UNIT * 8);\r
+\r
+ PageNumber = (Block->BitsLen)/PAGESIZE +1;\r
+ Status = PeiServicesAllocatePages (\r
+ EfiBootServicesCode,\r
+ PageNumber,\r
+ &TempPtr\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return NULL;\r
+ }\r
+ ZeroMem ((VOID *)(UINTN)TempPtr, PageNumber*EFI_PAGE_SIZE);\r
+\r
+ Block->Bits = (UINT8 *)(UINTN)TempPtr;\r
+\r
+ Status = PeiServicesAllocatePages (\r
+ EfiBootServicesCode,\r
+ Pages,\r
+ &TempPtr\r
+ );\r
+ ZeroMem ((VOID *)(UINTN)TempPtr, Pages*EFI_PAGE_SIZE);\r
+\r
+ BufHost = (VOID *)(UINTN)TempPtr;\r
+ MappedAddr = (EFI_PHYSICAL_ADDRESS) (UINTN) BufHost;\r
+ //\r
+ // Check whether the data structure used by the host controller\r
+ // should be restricted into the same 4G\r
+ //\r
+ if (Pool->Check4G && (Pool->Which4G != USB_HC_HIGH_32BIT (MappedAddr))) {\r
+ return NULL;\r
+ }\r
+\r
+ Block->BufHost = BufHost;\r
+ Block->Buf = (UINT8 *) ((UINTN) MappedAddr);\r
+ Block->Mapping = Mapping;\r
+ Block->Next = NULL;\r
+\r
+ return Block;\r
+\r
+}\r
+\r
+\r
+/**\r
+ Free the memory block from the memory pool.\r
+\r
+ @param Pool The memory pool to free the block from.\r
+ @param Block The memory block to free.\r
+\r
+**/\r
+VOID\r
+UsbHcFreeMemBlock (\r
+ IN USBHC_MEM_POOL *Pool,\r
+ IN USBHC_MEM_BLOCK *Block\r
+ )\r
+{\r
+\r
+ ASSERT ((Pool != NULL) && (Block != NULL));\r
+}\r
+\r
+\r
+/**\r
+ Alloc some memory from the block.\r
+\r
+ @param Block The memory block to allocate memory from.\r
+ @param Units Number of memory units to allocate.\r
+\r
+ @return The pointer to the allocated memory. If couldn't allocate the needed memory,\r
+ the return value is NULL.\r
+\r
+**/\r
+VOID *\r
+UsbHcAllocMemFromBlock (\r
+ IN USBHC_MEM_BLOCK *Block,\r
+ IN UINTN Units\r
+ )\r
+{\r
+ UINTN Byte;\r
+ UINT8 Bit;\r
+ UINTN StartByte;\r
+ UINT8 StartBit;\r
+ UINTN Available;\r
+ UINTN Count;\r
+\r
+ ASSERT ((Block != 0) && (Units != 0));\r
+\r
+ StartByte = 0;\r
+ StartBit = 0;\r
+ Available = 0;\r
+\r
+ for (Byte = 0, Bit = 0; Byte < Block->BitsLen;) {\r
+ //\r
+ // If current bit is zero, the corresponding memory unit is\r
+ // available, otherwise we need to restart our searching.\r
+ // Available counts the consective number of zero bit.\r
+ //\r
+ if (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit)) {\r
+ Available++;\r
+\r
+ if (Available >= Units) {\r
+ break;\r
+ }\r
+\r
+ NEXT_BIT (Byte, Bit);\r
+\r
+ } else {\r
+ NEXT_BIT (Byte, Bit);\r
+\r
+ Available = 0;\r
+ StartByte = Byte;\r
+ StartBit = Bit;\r
+ }\r
+ }\r
+\r
+ if (Available < Units) {\r
+ return NULL;\r
+ }\r
+\r
+ //\r
+ // Mark the memory as allocated\r
+ //\r
+ Byte = StartByte;\r
+ Bit = StartBit;\r
+\r
+ for (Count = 0; Count < Units; Count++) {\r
+ ASSERT (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));\r
+\r
+ Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] | (UINT8) USB_HC_BIT (Bit));\r
+ NEXT_BIT (Byte, Bit);\r
+ }\r
+\r
+ return Block->BufHost + (StartByte * 8 + StartBit) * USBHC_MEM_UNIT;\r
+}\r
+\r
+/**\r
+ Insert the memory block to the pool's list of the blocks.\r
+\r
+ @param Head The head of the memory pool's block list.\r
+ @param Block The memory block to insert.\r
+\r
+**/\r
+VOID\r
+UsbHcInsertMemBlockToPool (\r
+ IN USBHC_MEM_BLOCK *Head,\r
+ IN USBHC_MEM_BLOCK *Block\r
+ )\r
+{\r
+ ASSERT ((Head != NULL) && (Block != NULL));\r
+ Block->Next = Head->Next;\r
+ Head->Next = Block;\r
+}\r
+\r
+\r
+/**\r
+ Is the memory block empty?\r
+\r
+ @param Block The memory block to check.\r
+\r
+ @retval TRUE The memory block is empty.\r
+ @retval FALSE The memory block isn't empty.\r
+\r
+**/\r
+BOOLEAN\r
+UsbHcIsMemBlockEmpty (\r
+ IN USBHC_MEM_BLOCK *Block\r
+ )\r
+{\r
+ UINTN Index;\r
+\r
+ for (Index = 0; Index < Block->BitsLen; Index++) {\r
+ if (Block->Bits[Index] != 0) {\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+\r
+/**\r
+ Unlink the memory block from the pool's list.\r
+\r
+ @param Head The block list head of the memory's pool.\r
+ @param BlockToUnlink The memory block to unlink.\r
+\r
+**/\r
+VOID\r
+UsbHcUnlinkMemBlock (\r
+ IN USBHC_MEM_BLOCK *Head,\r
+ IN USBHC_MEM_BLOCK *BlockToUnlink\r
+ )\r
+{\r
+ USBHC_MEM_BLOCK *Block;\r
+\r
+ ASSERT ((Head != NULL) && (BlockToUnlink != NULL));\r
+\r
+ for (Block = Head; Block != NULL; Block = Block->Next) {\r
+ if (Block->Next == BlockToUnlink) {\r
+ Block->Next = BlockToUnlink->Next;\r
+ BlockToUnlink->Next = NULL;\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Initialize the memory management pool for the host controller.\r
+\r
+ @param PciIo The PciIo that can be used to access the host controller.\r
+ @param Check4G Whether the host controller requires allocated memory\r
+ from one 4G address space.\r
+ @param Which4G The 4G memory area each memory allocated should be from.\r
+\r
+ @retval EFI_SUCCESS The memory pool is initialized.\r
+ @retval EFI_OUT_OF_RESOURCE Fail to init the memory pool.\r
+\r
+**/\r
+USBHC_MEM_POOL *\r
+UsbHcInitMemPool (\r
+ IN BOOLEAN Check4G,\r
+ IN UINT32 Which4G\r
+ )\r
+{\r
+ USBHC_MEM_POOL *Pool;\r
+ UINTN PageNumber;\r
+ EFI_STATUS Status;\r
+ EFI_PHYSICAL_ADDRESS TempPtr;\r
+\r
+ PageNumber = sizeof(USBHC_MEM_POOL)/PAGESIZE +1;\r
+ Status = PeiServicesAllocatePages (\r
+ EfiBootServicesCode,\r
+ PageNumber,\r
+ &TempPtr\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return NULL;\r
+ }\r
+ ZeroMem ((VOID *)(UINTN)TempPtr, PageNumber*EFI_PAGE_SIZE);\r
+\r
+ Pool = (USBHC_MEM_POOL *) ((UINTN) TempPtr);\r
+ Pool->Check4G = Check4G;\r
+ Pool->Which4G = Which4G;\r
+ Pool->Head = UsbHcAllocMemBlock (Pool, USBHC_MEM_DEFAULT_PAGES);\r
+\r
+ if (Pool->Head == NULL) {\r
+ Pool = NULL;\r
+ }\r
+\r
+ return Pool;\r
+}\r
+\r
+\r
+/**\r
+ Release the memory management pool.\r
+\r
+ @param Pool The USB memory pool to free.\r
+\r
+ @retval EFI_SUCCESS The memory pool is freed.\r
+ @retval EFI_DEVICE_ERROR Failed to free the memory pool.\r
+\r
+**/\r
+EFI_STATUS\r
+UsbHcFreeMemPool (\r
+ IN USBHC_MEM_POOL *Pool\r
+ )\r
+{\r
+ USBHC_MEM_BLOCK *Block;\r
+\r
+ ASSERT (Pool->Head != NULL);\r
+\r
+ //\r
+ // Unlink all the memory blocks from the pool, then free them.\r
+ // UsbHcUnlinkMemBlock can't be used to unlink and free the\r
+ // first block.\r
+ //\r
+ for (Block = Pool->Head->Next; Block != NULL; Block = Pool->Head->Next) {\r
+ UsbHcFreeMemBlock (Pool, Block);\r
+ }\r
+\r
+ UsbHcFreeMemBlock (Pool, Pool->Head);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Allocate some memory from the host controller's memory pool\r
+ which can be used to communicate with host controller.\r
+\r
+ @param Pool The host controller's memory pool.\r
+ @param Size Size of the memory to allocate.\r
+\r
+ @return The allocated memory or NULL.\r
+\r
+**/\r
+VOID *\r
+UsbHcAllocateMem (\r
+ IN USBHC_MEM_POOL *Pool,\r
+ IN UINTN Size\r
+ )\r
+{\r
+ USBHC_MEM_BLOCK *Head;\r
+ USBHC_MEM_BLOCK *Block;\r
+ USBHC_MEM_BLOCK *NewBlock;\r
+ VOID *Mem;\r
+ UINTN AllocSize;\r
+ UINTN Pages;\r
+\r
+ Mem = NULL;\r
+ AllocSize = USBHC_MEM_ROUND (Size);\r
+ Head = Pool->Head;\r
+ ASSERT (Head != NULL);\r
+\r
+ //\r
+ // First check whether current memory blocks can satisfy the allocation.\r
+ //\r
+ for (Block = Head; Block != NULL; Block = Block->Next) {\r
+ Mem = UsbHcAllocMemFromBlock (Block, AllocSize / USBHC_MEM_UNIT);\r
+\r
+ if (Mem != NULL) {\r
+ ZeroMem (Mem, Size);\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (Mem != NULL) {\r
+ return Mem;\r
+ }\r
+\r
+ //\r
+ // Create a new memory block if there is not enough memory\r
+ // in the pool. If the allocation size is larger than the\r
+ // default page number, just allocate a large enough memory\r
+ // block. Otherwise allocate default pages.\r
+ //\r
+ if (AllocSize > EFI_PAGES_TO_SIZE (USBHC_MEM_DEFAULT_PAGES)) {\r
+ Pages = EFI_SIZE_TO_PAGES (AllocSize) + 1;\r
+ } else {\r
+ Pages = USBHC_MEM_DEFAULT_PAGES;\r
+ }\r
+\r
+ NewBlock = UsbHcAllocMemBlock (Pool, Pages);\r
+\r
+ if (NewBlock == NULL) {\r
+ DEBUG ((EFI_D_INFO, "UsbHcAllocateMem: failed to allocate block\n"));\r
+ return NULL;\r
+ }\r
+\r
+ //\r
+ // Add the new memory block to the pool, then allocate memory from it\r
+ //\r
+ UsbHcInsertMemBlockToPool (Head, NewBlock);\r
+ Mem = UsbHcAllocMemFromBlock (NewBlock, AllocSize / USBHC_MEM_UNIT);\r
+\r
+ if (Mem != NULL) {\r
+ ZeroMem (Mem, Size);\r
+ }\r
+\r
+ return Mem;\r
+}\r
+\r
+\r
+/**\r
+ Free the allocated memory back to the memory pool.\r
+\r
+ @param Pool The memory pool of the host controller.\r
+ @param Mem The memory to free.\r
+ @param Size The size of the memory to free.\r
+\r
+**/\r
+VOID\r
+UsbHcFreeMem (\r
+ IN USBHC_MEM_POOL *Pool,\r
+ IN VOID *Mem,\r
+ IN UINTN Size\r
+ )\r
+{\r
+ USBHC_MEM_BLOCK *Head;\r
+ USBHC_MEM_BLOCK *Block;\r
+ UINT8 *ToFree;\r
+ UINTN AllocSize;\r
+ UINTN Byte;\r
+ UINTN Bit;\r
+ UINTN Count;\r
+\r
+ Head = Pool->Head;\r
+ AllocSize = USBHC_MEM_ROUND (Size);\r
+ ToFree = (UINT8 *) Mem;\r
+\r
+ for (Block = Head; Block != NULL; Block = Block->Next) {\r
+ //\r
+ // scan the memory block list for the memory block that\r
+ // completely contains the memory to free.\r
+ //\r
+ if ((Block->BufHost <= ToFree) && ((ToFree + AllocSize) <= (Block->BufHost + Block->BufLen))) {\r
+ //\r
+ // compute the start byte and bit in the bit array\r
+ //\r
+ Byte = ((ToFree - Block->BufHost) / USBHC_MEM_UNIT) / 8;\r
+ Bit = ((ToFree - Block->BufHost) / USBHC_MEM_UNIT) % 8;\r
+\r
+ //\r
+ // reset associated bits in bit arry\r
+ //\r
+ for (Count = 0; Count < (AllocSize / USBHC_MEM_UNIT); Count++) {\r
+ ASSERT (USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));\r
+\r
+ Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] ^ USB_HC_BIT (Bit));\r
+ NEXT_BIT (Byte, Bit);\r
+ }\r
+\r
+ break;\r
+ }\r
+ }\r
+\r
+ //\r
+ // If Block == NULL, it means that the current memory isn't\r
+ // in the host controller's pool. This is critical because\r
+ // the caller has passed in a wrong memory point\r
+ //\r
+ ASSERT (Block != NULL);\r
+\r
+ //\r
+ // Release the current memory block if it is empty and not the head\r
+ //\r
+ if ((Block != Head) && UsbHcIsMemBlockEmpty (Block)) {\r
+ UsbHcFreeMemBlock (Pool, Block);\r
+ }\r
+\r
+ return ;\r
+}\r