audk/EdkModulePkg/Bus/Pci/Ehci/Dxe/EhciMem.c

762 lines
18 KiB
C

/*++
Copyright (c) 2006, Intel Corporation
All rights reserved. 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.
Module Name:
EhciMem.c
Abstract:
Revision History
--*/
#include "Ehci.h"
EFI_STATUS
CreateMemoryBlock (
IN USB2_HC_DEV *HcDev,
OUT MEMORY_MANAGE_HEADER **MemoryHeader,
IN UINTN MemoryBlockSizeInPages
)
/*++
Routine Description:
Use PciIo->AllocateBuffer to allocate common buffer for the memory block,
and use PciIo->Map to map the common buffer for Bus Master Read/Write.
Arguments:
HcDev - USB2_HC_DEV
MemoryHeader - MEMORY_MANAGE_HEADER to output
MemoryBlockSizeInPages - MemoryBlockSizeInPages
Returns:
EFI_SUCCESS Success
EFI_OUT_OF_RESOURCES Fail for no resources
EFI_UNSUPPORTED Unsupported currently
--*/
{
EFI_STATUS Status;
VOID *CommonBuffer;
EFI_PHYSICAL_ADDRESS MappedAddress;
UINTN MemoryBlockSizeInBytes;
VOID *Mapping;
//
// Allocate memory for MemoryHeader
//
*MemoryHeader = AllocateZeroPool (sizeof (MEMORY_MANAGE_HEADER));
if (*MemoryHeader == NULL) {
return EFI_OUT_OF_RESOURCES;
}
(*MemoryHeader)->Next = NULL;
//
// set Memory block size
//
(*MemoryHeader)->MemoryBlockSizeInBytes = EFI_PAGES_TO_SIZE (MemoryBlockSizeInPages);
//
// each bit in Bit Array will manage 32 bytes memory in memory block
//
(*MemoryHeader)->BitArraySizeInBytes = ((*MemoryHeader)->MemoryBlockSizeInBytes / 32) / 8;
//
// Allocate memory for BitArray
//
(*MemoryHeader)->BitArrayPtr = AllocateZeroPool ((*MemoryHeader)->BitArraySizeInBytes);
if ((*MemoryHeader)->BitArrayPtr == NULL) {
gBS->FreePool (*MemoryHeader);
return EFI_OUT_OF_RESOURCES;
}
//
// Memory Block uses MemoryBlockSizeInPages pages,
// and it is allocated as common buffer use.
//
Status = HcDev->PciIo->AllocateBuffer (
HcDev->PciIo,
AllocateAnyPages,
EfiBootServicesData,
MemoryBlockSizeInPages,
&CommonBuffer,
0
);
if (EFI_ERROR (Status)) {
gBS->FreePool ((*MemoryHeader)->BitArrayPtr);
gBS->FreePool (*MemoryHeader);
return EFI_OUT_OF_RESOURCES;
}
MemoryBlockSizeInBytes = EFI_PAGES_TO_SIZE (MemoryBlockSizeInPages);
Status = HcDev->PciIo->Map (
HcDev->PciIo,
EfiPciIoOperationBusMasterCommonBuffer,
CommonBuffer,
&MemoryBlockSizeInBytes,
&MappedAddress,
&Mapping
);
//
// If returned Mapped size is less than the size
// we request,do not support.
//
if (EFI_ERROR (Status) || (MemoryBlockSizeInBytes != EFI_PAGES_TO_SIZE (MemoryBlockSizeInPages))) {
HcDev->PciIo->FreeBuffer (HcDev->PciIo, MemoryBlockSizeInPages, CommonBuffer);
gBS->FreePool ((*MemoryHeader)->BitArrayPtr);
gBS->FreePool (*MemoryHeader);
return EFI_UNSUPPORTED;
}
//
// Data structure involved by host controller
// should be restricted into the same 4G
//
if (HcDev->Is64BitCapable != 0) {
if (HcDev->High32BitAddr != GET_32B_TO_63B (MappedAddress)) {
HcDev->PciIo->Unmap (HcDev->PciIo, Mapping);
HcDev->PciIo->FreeBuffer (HcDev->PciIo, MemoryBlockSizeInPages, CommonBuffer);
gBS->FreePool ((*MemoryHeader)->BitArrayPtr);
gBS->FreePool (*MemoryHeader);
return EFI_UNSUPPORTED;
}
}
//
// Set Memory block initial address
//
(*MemoryHeader)->MemoryBlockPtr = (UINT8 *) ((UINTN) MappedAddress);
(*MemoryHeader)->Mapping = Mapping;
ZeroMem (
(*MemoryHeader)->MemoryBlockPtr,
EFI_PAGES_TO_SIZE (MemoryBlockSizeInPages)
);
return EFI_SUCCESS;
}
EFI_STATUS
FreeMemoryHeader (
IN USB2_HC_DEV *HcDev,
IN MEMORY_MANAGE_HEADER *MemoryHeader
)
/*++
Routine Description:
Free Memory Header
Arguments:
HcDev - USB2_HC_DEV
MemoryHeader - MemoryHeader to be freed
Returns:
EFI_SUCCESS Success
EFI_INVALID_PARAMETER Parameter is error
--*/
{
if ((MemoryHeader == NULL) || (HcDev == NULL)) {
return EFI_INVALID_PARAMETER;
}
//
// unmap the common buffer used by the memory block
//
HcDev->PciIo->Unmap (HcDev->PciIo, MemoryHeader->Mapping);
//
// free common buffer
//
HcDev->PciIo->FreeBuffer (
HcDev->PciIo,
EFI_SIZE_TO_PAGES (MemoryHeader->MemoryBlockSizeInBytes),
MemoryHeader->MemoryBlockPtr
);
//
// free bit array
//
gBS->FreePool (MemoryHeader->BitArrayPtr);
//
// free memory header
//
gBS->FreePool (MemoryHeader);
return EFI_SUCCESS;
}
EFI_STATUS
EhciAllocatePool (
IN USB2_HC_DEV *HcDev,
OUT UINT8 **Pool,
IN UINTN AllocSize
)
/*++
Routine Description:
Ehci Allocate Pool
Arguments:
HcDev - USB2_HC_DEV
Pool - Place to store pointer to the memory buffer
AllocSize - Alloc Size
Returns:
EFI_SUCCESS Success
EFI_DEVICE_ERROR Fail
--*/
{
MEMORY_MANAGE_HEADER *MemoryHeader;
MEMORY_MANAGE_HEADER *TempHeaderPtr;
MEMORY_MANAGE_HEADER *NewMemoryHeader;
UINTN RealAllocSize;
UINTN MemoryBlockSizeInPages;
EFI_STATUS Status;
EFI_TPL OldTpl;
*Pool = NULL;
MemoryHeader = HcDev->MemoryHeader;
ASSERT (MemoryHeader != NULL);
OldTpl = gBS->RaiseTPL (EFI_TPL_NOTIFY + 1);
//
// allocate unit is 32 bytes (align on 32 byte)
//
if (AllocSize & 0x1F) {
RealAllocSize = (AllocSize / 32 + 1) * 32;
} else {
RealAllocSize = AllocSize;
}
//
// There may be linked MemoryHeaders.
// To allocate a free pool in Memory blocks,
// must search in the MemoryHeader link list
// until enough free pool is found.
//
Status = EFI_NOT_FOUND;
for (TempHeaderPtr = MemoryHeader; TempHeaderPtr != NULL; TempHeaderPtr = TempHeaderPtr->Next) {
Status = AllocMemInMemoryBlock (
TempHeaderPtr,
(VOID **) Pool,
RealAllocSize / 32
);
if (!EFI_ERROR (Status)) {
ZeroMem (*Pool, AllocSize);
gBS->RestoreTPL (OldTpl);
return EFI_SUCCESS;
}
}
gBS->RestoreTPL (OldTpl);
//
// There is no enough memory,
// Create a new Memory Block
//
//
// if pool size is larger than NORMAL_MEMORY_BLOCK_UNIT_IN_PAGES,
// just allocate a large enough memory block.
//
if (RealAllocSize > EFI_PAGES_TO_SIZE (NORMAL_MEMORY_BLOCK_UNIT_IN_PAGES)) {
MemoryBlockSizeInPages = EFI_SIZE_TO_PAGES (RealAllocSize) + 1;
} else {
MemoryBlockSizeInPages = NORMAL_MEMORY_BLOCK_UNIT_IN_PAGES;
}
Status = CreateMemoryBlock (HcDev, &NewMemoryHeader, MemoryBlockSizeInPages);
if (EFI_ERROR (Status)) {
return Status;
}
OldTpl = gBS->RaiseTPL (EFI_TPL_NOTIFY + 1);
//
// Link the new Memory Block to the Memory Header list
//
InsertMemoryHeaderToList (MemoryHeader, NewMemoryHeader);
Status = AllocMemInMemoryBlock (
NewMemoryHeader,
(VOID **) Pool,
RealAllocSize / 32
);
if (!EFI_ERROR (Status)) {
ZeroMem (*Pool, AllocSize);
}
gBS->RestoreTPL (OldTpl);
return Status;
}
VOID
EhciFreePool (
IN USB2_HC_DEV *HcDev,
IN UINT8 *Pool,
IN UINTN AllocSize
)
/*++
Routine Description:
Uhci Free Pool
Arguments:
HcDev - USB_HC_DEV
Pool - Pool to free
AllocSize - Pool size
Returns:
VOID
--*/
{
MEMORY_MANAGE_HEADER *MemoryHeader;
MEMORY_MANAGE_HEADER *TempHeaderPtr;
UINTN StartBytePos;
UINTN Index;
UINT8 StartBitPos;
UINT8 Index2;
UINTN Count;
UINTN RealAllocSize;
EFI_TPL OldTpl;
OldTpl = gBS->RaiseTPL (EFI_TPL_NOTIFY + 1);
MemoryHeader = HcDev->MemoryHeader;
//
// allocate unit is 32 byte (align on 32 byte)
//
if (AllocSize & 0x1F) {
RealAllocSize = (AllocSize / 32 + 1) * 32;
} else {
RealAllocSize = AllocSize;
}
//
// scan the memory header linked list for
// the asigned memory to free.
//
for (TempHeaderPtr = MemoryHeader; TempHeaderPtr != NULL; TempHeaderPtr = TempHeaderPtr->Next) {
if ((Pool >= TempHeaderPtr->MemoryBlockPtr) &&
((Pool + RealAllocSize) <= (TempHeaderPtr->MemoryBlockPtr + TempHeaderPtr->MemoryBlockSizeInBytes))
) {
//
// Pool is in the Memory Block area,
// find the start byte and bit in the bit array
//
StartBytePos = ((Pool - TempHeaderPtr->MemoryBlockPtr) / 32) / 8;
StartBitPos = (UINT8) (((Pool - TempHeaderPtr->MemoryBlockPtr) / 32) & 0x7);
//
// reset associated bits in bit arry
//
for (Index = StartBytePos, Index2 = StartBitPos, Count = 0; Count < (RealAllocSize / 32); Count++) {
TempHeaderPtr->BitArrayPtr[Index] ^= (UINT8) (bit (Index2));
Index2++;
if (Index2 == 8) {
Index += 1;
Index2 = 0;
}
}
//
// break the loop
//
break;
}
}
//
// Release emptied memory blocks (only if the memory block is not
// the first one in the memory header list
//
for (TempHeaderPtr = MemoryHeader->Next; TempHeaderPtr != NULL;) {
ASSERT (MemoryHeader->Next != NULL);
if (IsMemoryBlockEmptied (TempHeaderPtr)) {
DelinkMemoryBlock (MemoryHeader, TempHeaderPtr);
//
// when the TempHeaderPtr is freed in FreeMemoryHeader(),
// the TempHeaderPtr is pointing to nonsense content.
//
gBS->RestoreTPL (OldTpl);
FreeMemoryHeader (HcDev, TempHeaderPtr);
OldTpl = gBS->RaiseTPL (EFI_TPL_NOTIFY + 1);
//
// reset the TempHeaderPtr, continue search for
// another empty memory block.
//
TempHeaderPtr = MemoryHeader->Next;
continue;
}
TempHeaderPtr = TempHeaderPtr->Next;
}
gBS->RestoreTPL (OldTpl);
}
VOID
InsertMemoryHeaderToList (
IN MEMORY_MANAGE_HEADER *MemoryHeader,
IN MEMORY_MANAGE_HEADER *NewMemoryHeader
)
/*++
Routine Description:
Insert Memory Header To List
Arguments:
MemoryHeader - MEMORY_MANAGE_HEADER
NewMemoryHeader - MEMORY_MANAGE_HEADER
Returns:
VOID
--*/
{
MEMORY_MANAGE_HEADER *TempHeaderPtr;
for (TempHeaderPtr = MemoryHeader; TempHeaderPtr != NULL; TempHeaderPtr = TempHeaderPtr->Next) {
if (TempHeaderPtr->Next == NULL) {
TempHeaderPtr->Next = NewMemoryHeader;
break;
}
}
}
EFI_STATUS
AllocMemInMemoryBlock (
IN MEMORY_MANAGE_HEADER *MemoryHeader,
OUT VOID **Pool,
IN UINTN NumberOfMemoryUnit
)
/*++
Routine Description:
Alloc Memory In MemoryBlock
Arguments:
MemoryHeader - MEMORY_MANAGE_HEADER
Pool - Place to store pointer to memory
NumberOfMemoryUnit - Number Of Memory Unit
Returns:
EFI_SUCCESS Success
EFI_NOT_FOUND Can't find the free memory
--*/
{
UINTN TempBytePos;
UINTN FoundBytePos;
UINT8 Index;
UINT8 FoundBitPos;
UINT8 ByteValue;
UINT8 BitValue;
UINTN NumberOfZeros;
UINTN Count;
FoundBytePos = 0;
FoundBitPos = 0;
ByteValue = MemoryHeader->BitArrayPtr[0];
NumberOfZeros = 0;
Index = 0;
for (TempBytePos = 0; TempBytePos < MemoryHeader->BitArraySizeInBytes;) {
//
// Pop out BitValue from a byte in TempBytePos.
//
BitValue = (UINT8) (ByteValue & 0x1);
//
// right shift the byte
//
ByteValue /= 2;
if (BitValue == 0) {
//
// Found a free bit, the NumberOfZeros only record the number
// of those consecutive zeros
//
NumberOfZeros++;
//
// Found enough consecutive free space, break the loop
//
if (NumberOfZeros >= NumberOfMemoryUnit) {
break;
}
} else {
//
// Encountering a '1', meant the bit is ocupied.
//
if (NumberOfZeros >= NumberOfMemoryUnit) {
//
// Found enough consecutive free space,break the loop
//
break;
} else {
//
// the NumberOfZeros only record the number of those consecutive zeros,
// so reset the NumberOfZeros to 0 when encountering '1' before finding
// enough consecutive '0's
//
NumberOfZeros = 0;
//
// reset the (FoundBytePos,FoundBitPos) to the position of '1'
//
FoundBytePos = TempBytePos;
FoundBitPos = Index;
}
}
//
// step forward a bit
//
Index++;
if (Index == 8) {
//
// step forward a byte, getting the byte value,
// and reset the bit pos.
//
TempBytePos += 1;
ByteValue = MemoryHeader->BitArrayPtr[TempBytePos];
Index = 0;
}
}
if (NumberOfZeros < NumberOfMemoryUnit) {
return EFI_NOT_FOUND;
}
//
// Found enough free space.
//
//
// The values recorded in (FoundBytePos,FoundBitPos) have two conditions:
// 1)(FoundBytePos,FoundBitPos) record the position
// of the last '1' before the consecutive '0's, it must
// be adjusted to the start position of the consecutive '0's.
// 2)the start address of the consecutive '0's is just the start of
// the bitarray. so no need to adjust the values of
// (FoundBytePos,FoundBitPos).
//
if ((MemoryHeader->BitArrayPtr[FoundBytePos] & bit (FoundBitPos)) != 0) {
FoundBitPos += 1;
}
//
// Have the (FoundBytePos,FoundBitPos) make sense.
//
if (FoundBitPos > 7) {
FoundBytePos += 1;
FoundBitPos -= 8;
}
//
// Set the memory as allocated
//
for (TempBytePos = FoundBytePos, Index = FoundBitPos, Count = 0; Count < NumberOfMemoryUnit; Count++) {
MemoryHeader->BitArrayPtr[TempBytePos] |= bit (Index);
Index++;
if (Index == 8) {
TempBytePos += 1;
Index = 0;
}
}
*Pool = MemoryHeader->MemoryBlockPtr + (FoundBytePos * 8 + FoundBitPos) * 32;
return EFI_SUCCESS;
}
BOOLEAN
IsMemoryBlockEmptied (
IN MEMORY_MANAGE_HEADER *MemoryHeaderPtr
)
/*++
Routine Description:
Is Memory Block Emptied
Arguments:
MemoryHeaderPtr - MEMORY_MANAGE_HEADER
Returns:
TRUE Empty
FALSE Not Empty
--*/
{
UINTN Index;
for (Index = 0; Index < MemoryHeaderPtr->BitArraySizeInBytes; Index++) {
if (MemoryHeaderPtr->BitArrayPtr[Index] != 0) {
return FALSE;
}
}
return TRUE;
}
VOID
DelinkMemoryBlock (
IN MEMORY_MANAGE_HEADER *FirstMemoryHeader,
IN MEMORY_MANAGE_HEADER *NeedFreeMemoryHeader
)
/*++
Routine Description:
Delink Memory Block
Arguments:
FirstMemoryHeader - MEMORY_MANAGE_HEADER
NeedFreeMemoryHeader - MEMORY_MANAGE_HEADER
Returns:
VOID
--*/
{
MEMORY_MANAGE_HEADER *TempHeaderPtr;
if ((FirstMemoryHeader == NULL) || (NeedFreeMemoryHeader == NULL)) {
return ;
}
for (TempHeaderPtr = FirstMemoryHeader; TempHeaderPtr != NULL; TempHeaderPtr = TempHeaderPtr->Next) {
if (TempHeaderPtr->Next == NeedFreeMemoryHeader) {
//
// Link the before and after
//
TempHeaderPtr->Next = NeedFreeMemoryHeader->Next;
break;
}
}
}
EFI_STATUS
InitialMemoryManagement (
IN USB2_HC_DEV *HcDev
)
/*++
Routine Description:
Initialize Memory Management
Arguments:
HcDev - USB2_HC_DEV
Returns:
EFI_SUCCESS Success
EFI_DEVICE_ERROR Fail
--*/
{
EFI_STATUS Status;
MEMORY_MANAGE_HEADER *MemoryHeader;
UINTN MemPages;
MemPages = NORMAL_MEMORY_BLOCK_UNIT_IN_PAGES;
Status = CreateMemoryBlock (HcDev, &MemoryHeader, MemPages);
if (EFI_ERROR (Status)) {
Status = EFI_OUT_OF_RESOURCES;
goto exit;
}
HcDev->MemoryHeader = MemoryHeader;
exit:
return Status;
}
EFI_STATUS
DeinitialMemoryManagement (
IN USB2_HC_DEV *HcDev
)
/*++
Routine Description:
Deinitialize Memory Management
Arguments:
HcDev - USB2_HC_DEV
Returns:
EFI_SUCCESS Success
EFI_DEVICE_ERROR Fail
--*/
{
MEMORY_MANAGE_HEADER *TempHeaderPtr;
for (TempHeaderPtr = HcDev->MemoryHeader->Next; TempHeaderPtr != NULL;) {
DelinkMemoryBlock (HcDev->MemoryHeader, TempHeaderPtr);
//
// when the TempHeaderPtr is freed in FreeMemoryHeader(),
// the TempHeaderPtr is pointing to nonsense content.
//
FreeMemoryHeader (HcDev, TempHeaderPtr);
//
// reset the TempHeaderPtr,continue free another memory block.
//
TempHeaderPtr = HcDev->MemoryHeader->Next;
}
FreeMemoryHeader (HcDev, HcDev->MemoryHeader);
return EFI_SUCCESS;
}