mirror of https://github.com/acidanthera/audk.git
1063 lines
31 KiB
C
1063 lines
31 KiB
C
/** @file
|
|
CPU DXE Module to produce CPU ARCH Protocol.
|
|
|
|
Copyright (c) 2008 - 2023, Intel Corporation. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include "CpuDxe.h"
|
|
#include "CpuMp.h"
|
|
#include "CpuPageTable.h"
|
|
|
|
#define CPU_INTERRUPT_NUM 256
|
|
|
|
//
|
|
// Global Variables
|
|
//
|
|
BOOLEAN InterruptState = FALSE;
|
|
EFI_HANDLE mCpuHandle = NULL;
|
|
BOOLEAN mIsFlushingGCD;
|
|
BOOLEAN mIsAllocatingPageTable = FALSE;
|
|
UINT64 mTimerPeriod = 0;
|
|
|
|
EFI_CPU_ARCH_PROTOCOL gCpu = {
|
|
CpuFlushCpuDataCache,
|
|
CpuEnableInterrupt,
|
|
CpuDisableInterrupt,
|
|
CpuGetInterruptState,
|
|
CpuInit,
|
|
CpuRegisterInterruptHandler,
|
|
CpuGetTimerValue,
|
|
CpuSetMemoryAttributes,
|
|
1, // NumberOfTimers
|
|
4 // DmaBufferAlignment
|
|
};
|
|
|
|
//
|
|
// CPU Arch Protocol Functions
|
|
//
|
|
|
|
/**
|
|
Flush CPU data cache. If the instruction cache is fully coherent
|
|
with all DMA operations then function can just return EFI_SUCCESS.
|
|
|
|
@param This Protocol instance structure
|
|
@param Start Physical address to start flushing from.
|
|
@param Length Number of bytes to flush. Round up to chipset
|
|
granularity.
|
|
@param FlushType Specifies the type of flush operation to perform.
|
|
|
|
@retval EFI_SUCCESS If cache was flushed
|
|
@retval EFI_UNSUPPORTED If flush type is not supported.
|
|
@retval EFI_DEVICE_ERROR If requested range could not be flushed.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
CpuFlushCpuDataCache (
|
|
IN EFI_CPU_ARCH_PROTOCOL *This,
|
|
IN EFI_PHYSICAL_ADDRESS Start,
|
|
IN UINT64 Length,
|
|
IN EFI_CPU_FLUSH_TYPE FlushType
|
|
)
|
|
{
|
|
if (FlushType == EfiCpuFlushTypeWriteBackInvalidate) {
|
|
AsmWbinvd ();
|
|
return EFI_SUCCESS;
|
|
} else if (FlushType == EfiCpuFlushTypeInvalidate) {
|
|
AsmInvd ();
|
|
return EFI_SUCCESS;
|
|
} else {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Enables CPU interrupts.
|
|
|
|
@param This Protocol instance structure
|
|
|
|
@retval EFI_SUCCESS If interrupts were enabled in the CPU
|
|
@retval EFI_DEVICE_ERROR If interrupts could not be enabled on the CPU.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
CpuEnableInterrupt (
|
|
IN EFI_CPU_ARCH_PROTOCOL *This
|
|
)
|
|
{
|
|
EnableInterrupts ();
|
|
|
|
InterruptState = TRUE;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Disables CPU interrupts.
|
|
|
|
@param This Protocol instance structure
|
|
|
|
@retval EFI_SUCCESS If interrupts were disabled in the CPU.
|
|
@retval EFI_DEVICE_ERROR If interrupts could not be disabled on the CPU.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
CpuDisableInterrupt (
|
|
IN EFI_CPU_ARCH_PROTOCOL *This
|
|
)
|
|
{
|
|
DisableInterrupts ();
|
|
|
|
InterruptState = FALSE;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Return the state of interrupts.
|
|
|
|
@param This Protocol instance structure
|
|
@param State Pointer to the CPU's current interrupt state
|
|
|
|
@retval EFI_SUCCESS If interrupts were disabled in the CPU.
|
|
@retval EFI_INVALID_PARAMETER State is NULL.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
CpuGetInterruptState (
|
|
IN EFI_CPU_ARCH_PROTOCOL *This,
|
|
OUT BOOLEAN *State
|
|
)
|
|
{
|
|
if (State == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
*State = InterruptState;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Generates an INIT to the CPU.
|
|
|
|
@param This Protocol instance structure
|
|
@param InitType Type of CPU INIT to perform
|
|
|
|
@retval EFI_SUCCESS If CPU INIT occurred. This value should never be
|
|
seen.
|
|
@retval EFI_DEVICE_ERROR If CPU INIT failed.
|
|
@retval EFI_UNSUPPORTED Requested type of CPU INIT not supported.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
CpuInit (
|
|
IN EFI_CPU_ARCH_PROTOCOL *This,
|
|
IN EFI_CPU_INIT_TYPE InitType
|
|
)
|
|
{
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
/**
|
|
Registers a function to be called from the CPU interrupt handler.
|
|
|
|
@param This Protocol instance structure
|
|
@param InterruptType Defines which interrupt to hook. IA-32
|
|
valid range is 0x00 through 0xFF
|
|
@param InterruptHandler A pointer to a function of type
|
|
EFI_CPU_INTERRUPT_HANDLER that is called
|
|
when a processor interrupt occurs. A null
|
|
pointer is an error condition.
|
|
|
|
@retval EFI_SUCCESS If handler installed or uninstalled.
|
|
@retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler
|
|
for InterruptType was previously installed.
|
|
@retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for
|
|
InterruptType was not previously installed.
|
|
@retval EFI_UNSUPPORTED The interrupt specified by InterruptType
|
|
is not supported.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
CpuRegisterInterruptHandler (
|
|
IN EFI_CPU_ARCH_PROTOCOL *This,
|
|
IN EFI_EXCEPTION_TYPE InterruptType,
|
|
IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
|
|
)
|
|
{
|
|
return RegisterCpuInterruptHandler (InterruptType, InterruptHandler);
|
|
}
|
|
|
|
/**
|
|
Returns a timer value from one of the CPU's internal timers. There is no
|
|
inherent time interval between ticks but is a function of the CPU frequency.
|
|
|
|
@param This - Protocol instance structure.
|
|
@param TimerIndex - Specifies which CPU timer is requested.
|
|
@param TimerValue - Pointer to the returned timer value.
|
|
@param TimerPeriod - A pointer to the amount of time that passes
|
|
in femtoseconds (10-15) for each increment
|
|
of TimerValue. If TimerValue does not
|
|
increment at a predictable rate, then 0 is
|
|
returned. The amount of time that has
|
|
passed between two calls to GetTimerValue()
|
|
can be calculated with the formula
|
|
(TimerValue2 - TimerValue1) * TimerPeriod.
|
|
This parameter is optional and may be NULL.
|
|
|
|
@retval EFI_SUCCESS - If the CPU timer count was returned.
|
|
@retval EFI_UNSUPPORTED - If the CPU does not have any readable timers.
|
|
@retval EFI_DEVICE_ERROR - If an error occurred while reading the timer.
|
|
@retval EFI_INVALID_PARAMETER - TimerIndex is not valid or TimerValue is NULL.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
CpuGetTimerValue (
|
|
IN EFI_CPU_ARCH_PROTOCOL *This,
|
|
IN UINT32 TimerIndex,
|
|
OUT UINT64 *TimerValue,
|
|
OUT UINT64 *TimerPeriod OPTIONAL
|
|
)
|
|
{
|
|
UINT64 BeginValue;
|
|
UINT64 EndValue;
|
|
|
|
if (TimerValue == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (TimerIndex != 0) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
*TimerValue = AsmReadTsc ();
|
|
|
|
if (TimerPeriod != NULL) {
|
|
if (mTimerPeriod == 0) {
|
|
//
|
|
// Read time stamp counter before and after delay of 100 microseconds
|
|
//
|
|
BeginValue = AsmReadTsc ();
|
|
MicroSecondDelay (100);
|
|
EndValue = AsmReadTsc ();
|
|
//
|
|
// Calculate the actual frequency
|
|
//
|
|
mTimerPeriod = DivU64x64Remainder (
|
|
MultU64x32 (
|
|
1000 * 1000 * 1000,
|
|
100
|
|
),
|
|
EndValue - BeginValue,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
*TimerPeriod = mTimerPeriod;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
A minimal wrapper function that allows MtrrSetAllMtrrs() to be passed to
|
|
EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() as Procedure.
|
|
|
|
@param[in] Buffer Pointer to an MTRR_SETTINGS object, to be passed to
|
|
MtrrSetAllMtrrs().
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
SetMtrrsFromBuffer (
|
|
IN VOID *Buffer
|
|
)
|
|
{
|
|
MtrrSetAllMtrrs (Buffer);
|
|
}
|
|
|
|
/**
|
|
Implementation of SetMemoryAttributes() service of CPU Architecture Protocol.
|
|
|
|
This function modifies the attributes for the memory region specified by BaseAddress and
|
|
Length from their current attributes to the attributes specified by Attributes.
|
|
|
|
@param This The EFI_CPU_ARCH_PROTOCOL instance.
|
|
@param BaseAddress The physical address that is the start address of a memory region.
|
|
@param Length The size in bytes of the memory region.
|
|
@param Attributes The bit mask of attributes to set for the memory region.
|
|
|
|
@retval EFI_SUCCESS The attributes were set for the memory region.
|
|
@retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
|
|
BaseAddress and Length cannot be modified.
|
|
@retval EFI_INVALID_PARAMETER Length is zero.
|
|
Attributes specified an illegal combination of attributes that
|
|
cannot be set together.
|
|
@retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
|
|
the memory resource range.
|
|
@retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
|
|
resource range specified by BaseAddress and Length.
|
|
The bit mask of attributes is not support for the memory resource
|
|
range specified by BaseAddress and Length.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
CpuSetMemoryAttributes (
|
|
IN EFI_CPU_ARCH_PROTOCOL *This,
|
|
IN EFI_PHYSICAL_ADDRESS BaseAddress,
|
|
IN UINT64 Length,
|
|
IN UINT64 Attributes
|
|
)
|
|
{
|
|
RETURN_STATUS Status;
|
|
MTRR_MEMORY_CACHE_TYPE CacheType;
|
|
EFI_STATUS MpStatus;
|
|
EFI_MP_SERVICES_PROTOCOL *MpService;
|
|
MTRR_SETTINGS MtrrSettings;
|
|
UINT64 CacheAttributes;
|
|
UINT64 MemoryAttributes;
|
|
MTRR_MEMORY_CACHE_TYPE CurrentCacheType;
|
|
|
|
//
|
|
// If this function is called because GCD SetMemorySpaceAttributes () is called
|
|
// by RefreshGcdMemoryAttributes (), then we are just synchronizing GCD memory
|
|
// map with MTRR values. So there is no need to modify MTRRs, just return immediately
|
|
// to avoid unnecessary computing.
|
|
//
|
|
if (mIsFlushingGCD) {
|
|
DEBUG ((DEBUG_VERBOSE, " Flushing GCD\n"));
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// During memory attributes updating, new pages may be allocated to setup
|
|
// smaller granularity of page table. Page allocation action might then cause
|
|
// another calling of CpuSetMemoryAttributes() recursively, due to memory
|
|
// protection policy configured (such as PcdDxeNxMemoryProtectionPolicy).
|
|
// Since this driver will always protect memory used as page table by itself,
|
|
// there's no need to apply protection policy requested from memory service.
|
|
// So it's safe to just return EFI_SUCCESS if this time of calling is caused
|
|
// by page table memory allocation.
|
|
//
|
|
if (mIsAllocatingPageTable) {
|
|
DEBUG ((DEBUG_VERBOSE, " Allocating page table memory\n"));
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
CacheAttributes = Attributes & EFI_CACHE_ATTRIBUTE_MASK;
|
|
MemoryAttributes = Attributes & EFI_MEMORY_ATTRIBUTE_MASK;
|
|
|
|
if (Attributes != (CacheAttributes | MemoryAttributes)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (CacheAttributes != 0) {
|
|
if (!IsMtrrSupported ()) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
switch (CacheAttributes) {
|
|
case EFI_MEMORY_UC:
|
|
CacheType = CacheUncacheable;
|
|
break;
|
|
|
|
case EFI_MEMORY_WC:
|
|
CacheType = CacheWriteCombining;
|
|
break;
|
|
|
|
case EFI_MEMORY_WT:
|
|
CacheType = CacheWriteThrough;
|
|
break;
|
|
|
|
case EFI_MEMORY_WP:
|
|
CacheType = CacheWriteProtected;
|
|
break;
|
|
|
|
case EFI_MEMORY_WB:
|
|
CacheType = CacheWriteBack;
|
|
break;
|
|
|
|
default:
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
CurrentCacheType = MtrrGetMemoryAttribute (BaseAddress);
|
|
if (CurrentCacheType != CacheType) {
|
|
//
|
|
// call MTRR library function
|
|
//
|
|
Status = MtrrSetMemoryAttribute (
|
|
BaseAddress,
|
|
Length,
|
|
CacheType
|
|
);
|
|
|
|
if (!RETURN_ERROR (Status)) {
|
|
MpStatus = gBS->LocateProtocol (
|
|
&gEfiMpServiceProtocolGuid,
|
|
NULL,
|
|
(VOID **)&MpService
|
|
);
|
|
//
|
|
// Synchronize the update with all APs
|
|
//
|
|
if (!EFI_ERROR (MpStatus)) {
|
|
MtrrGetAllMtrrs (&MtrrSettings);
|
|
MpStatus = MpService->StartupAllAPs (
|
|
MpService, // This
|
|
SetMtrrsFromBuffer, // Procedure
|
|
FALSE, // SingleThread
|
|
NULL, // WaitEvent
|
|
0, // TimeoutInMicrosecsond
|
|
&MtrrSettings, // ProcedureArgument
|
|
NULL // FailedCpuList
|
|
);
|
|
ASSERT (MpStatus == EFI_SUCCESS || MpStatus == EFI_NOT_STARTED);
|
|
}
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set memory attribute by page table
|
|
//
|
|
return AssignMemoryPageAttributes (NULL, BaseAddress, Length, MemoryAttributes, NULL);
|
|
}
|
|
|
|
/**
|
|
Gets GCD Mem Space type from MTRR Type.
|
|
|
|
This function gets GCD Mem Space type from MTRR Type.
|
|
|
|
@param Type MTRR memory type
|
|
|
|
@return GCD Mem Space type
|
|
|
|
**/
|
|
UINT64
|
|
GetMemorySpaceAttributeFromMtrrType (
|
|
IN MTRR_MEMORY_CACHE_TYPE Type
|
|
)
|
|
{
|
|
switch (Type) {
|
|
case CacheUncacheable:
|
|
return EFI_MEMORY_UC;
|
|
case CacheWriteCombining:
|
|
return EFI_MEMORY_WC;
|
|
case CacheWriteThrough:
|
|
return EFI_MEMORY_WT;
|
|
case CacheWriteProtected:
|
|
return EFI_MEMORY_WP;
|
|
case CacheWriteBack:
|
|
return EFI_MEMORY_WB;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Searches memory descriptors covered by given memory range.
|
|
|
|
This function searches into the Gcd Memory Space for descriptors
|
|
(from StartIndex to EndIndex) that contains the memory range
|
|
specified by BaseAddress and Length.
|
|
|
|
@param MemorySpaceMap Gcd Memory Space Map as array.
|
|
@param NumberOfDescriptors Number of descriptors in map.
|
|
@param BaseAddress BaseAddress for the requested range.
|
|
@param Length Length for the requested range.
|
|
@param StartIndex Start index into the Gcd Memory Space Map.
|
|
@param EndIndex End index into the Gcd Memory Space Map.
|
|
|
|
@retval EFI_SUCCESS Search successfully.
|
|
@retval EFI_NOT_FOUND The requested descriptors does not exist.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
SearchGcdMemorySpaces (
|
|
IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap,
|
|
IN UINTN NumberOfDescriptors,
|
|
IN EFI_PHYSICAL_ADDRESS BaseAddress,
|
|
IN UINT64 Length,
|
|
OUT UINTN *StartIndex,
|
|
OUT UINTN *EndIndex
|
|
)
|
|
{
|
|
UINTN Index;
|
|
|
|
*StartIndex = 0;
|
|
*EndIndex = 0;
|
|
for (Index = 0; Index < NumberOfDescriptors; Index++) {
|
|
if ((BaseAddress >= MemorySpaceMap[Index].BaseAddress) &&
|
|
(BaseAddress < MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length))
|
|
{
|
|
*StartIndex = Index;
|
|
}
|
|
|
|
if ((BaseAddress + Length - 1 >= MemorySpaceMap[Index].BaseAddress) &&
|
|
(BaseAddress + Length - 1 < MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length))
|
|
{
|
|
*EndIndex = Index;
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
/**
|
|
Sets the attributes for a specified range in Gcd Memory Space Map.
|
|
|
|
This function sets the attributes for a specified range in
|
|
Gcd Memory Space Map.
|
|
|
|
@param MemorySpaceMap Gcd Memory Space Map as array
|
|
@param NumberOfDescriptors Number of descriptors in map
|
|
@param BaseAddress BaseAddress for the range
|
|
@param Length Length for the range
|
|
@param Attributes Attributes to set
|
|
|
|
@retval EFI_SUCCESS Memory attributes set successfully
|
|
@retval EFI_NOT_FOUND The specified range does not exist in Gcd Memory Space
|
|
|
|
**/
|
|
EFI_STATUS
|
|
SetGcdMemorySpaceAttributes (
|
|
IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap,
|
|
IN UINTN NumberOfDescriptors,
|
|
IN EFI_PHYSICAL_ADDRESS BaseAddress,
|
|
IN UINT64 Length,
|
|
IN UINT64 Attributes
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
UINTN StartIndex;
|
|
UINTN EndIndex;
|
|
EFI_PHYSICAL_ADDRESS RegionStart;
|
|
UINT64 RegionLength;
|
|
|
|
//
|
|
// Get all memory descriptors covered by the memory range
|
|
//
|
|
Status = SearchGcdMemorySpaces (
|
|
MemorySpaceMap,
|
|
NumberOfDescriptors,
|
|
BaseAddress,
|
|
Length,
|
|
&StartIndex,
|
|
&EndIndex
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Go through all related descriptors and set attributes accordingly
|
|
//
|
|
for (Index = StartIndex; Index <= EndIndex; Index++) {
|
|
if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeNonExistent) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Calculate the start and end address of the overlapping range
|
|
//
|
|
if (BaseAddress >= MemorySpaceMap[Index].BaseAddress) {
|
|
RegionStart = BaseAddress;
|
|
} else {
|
|
RegionStart = MemorySpaceMap[Index].BaseAddress;
|
|
}
|
|
|
|
if (BaseAddress + Length - 1 < MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length) {
|
|
RegionLength = BaseAddress + Length - RegionStart;
|
|
} else {
|
|
RegionLength = MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length - RegionStart;
|
|
}
|
|
|
|
//
|
|
// Set memory attributes according to MTRR attribute and the original attribute of descriptor
|
|
//
|
|
gDS->SetMemorySpaceAttributes (
|
|
RegionStart,
|
|
RegionLength,
|
|
(MemorySpaceMap[Index].Attributes & ~EFI_CACHE_ATTRIBUTE_MASK) | (MemorySpaceMap[Index].Capabilities & Attributes)
|
|
);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Refreshes the GCD Memory Space attributes according to MTRRs.
|
|
|
|
This function refreshes the GCD Memory Space attributes according to MTRRs.
|
|
|
|
**/
|
|
VOID
|
|
RefreshMemoryAttributesFromMtrr (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
RETURN_STATUS ReturnStatus;
|
|
UINTN Index;
|
|
UINTN NumberOfDescriptors;
|
|
EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;
|
|
MTRR_MEMORY_RANGE *Ranges;
|
|
UINTN RangeCount;
|
|
|
|
MemorySpaceMap = NULL;
|
|
|
|
//
|
|
// Get the memory space map from GCD
|
|
//
|
|
Status = gDS->GetMemorySpaceMap (
|
|
&NumberOfDescriptors,
|
|
&MemorySpaceMap
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
RangeCount = 0;
|
|
ReturnStatus = MtrrGetMemoryAttributesInMtrrSettings (NULL, NULL, &RangeCount);
|
|
ASSERT (ReturnStatus == RETURN_BUFFER_TOO_SMALL);
|
|
Ranges = AllocatePool (sizeof (*Ranges) * RangeCount);
|
|
ASSERT (Ranges != NULL);
|
|
ReturnStatus = MtrrGetMemoryAttributesInMtrrSettings (NULL, Ranges, &RangeCount);
|
|
ASSERT_RETURN_ERROR (ReturnStatus);
|
|
|
|
for (Index = 0; Index < RangeCount; Index++) {
|
|
SetGcdMemorySpaceAttributes (
|
|
MemorySpaceMap,
|
|
NumberOfDescriptors,
|
|
Ranges[Index].BaseAddress,
|
|
Ranges[Index].Length,
|
|
GetMemorySpaceAttributeFromMtrrType (Ranges[Index].Type)
|
|
);
|
|
}
|
|
|
|
//
|
|
// Free memory space map allocated by GCD service GetMemorySpaceMap ()
|
|
//
|
|
if (MemorySpaceMap != NULL) {
|
|
FreePool (MemorySpaceMap);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Check if paging is enabled or not.
|
|
**/
|
|
BOOLEAN
|
|
IsPagingAndPageAddressExtensionsEnabled (
|
|
VOID
|
|
)
|
|
{
|
|
IA32_CR0 Cr0;
|
|
IA32_CR4 Cr4;
|
|
|
|
Cr0.UintN = AsmReadCr0 ();
|
|
Cr4.UintN = AsmReadCr4 ();
|
|
|
|
return ((Cr0.Bits.PG != 0) && (Cr4.Bits.PAE != 0));
|
|
}
|
|
|
|
/**
|
|
Refreshes the GCD Memory Space attributes according to MTRRs and Paging.
|
|
|
|
This function refreshes the GCD Memory Space attributes according to MTRRs
|
|
and page tables.
|
|
|
|
**/
|
|
VOID
|
|
RefreshGcdMemoryAttributes (
|
|
VOID
|
|
)
|
|
{
|
|
mIsFlushingGCD = TRUE;
|
|
|
|
if (IsMtrrSupported ()) {
|
|
RefreshMemoryAttributesFromMtrr ();
|
|
}
|
|
|
|
if (IsPagingAndPageAddressExtensionsEnabled ()) {
|
|
RefreshGcdMemoryAttributesFromPaging ();
|
|
}
|
|
|
|
mIsFlushingGCD = FALSE;
|
|
}
|
|
|
|
/**
|
|
Initialize Interrupt Descriptor Table for interrupt handling.
|
|
|
|
**/
|
|
VOID
|
|
InitInterruptDescriptorTable (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_VECTOR_HANDOFF_INFO *VectorInfoList;
|
|
EFI_VECTOR_HANDOFF_INFO *VectorInfo;
|
|
IA32_IDT_GATE_DESCRIPTOR *IdtTable;
|
|
IA32_DESCRIPTOR IdtDescriptor;
|
|
UINTN IdtEntryCount;
|
|
|
|
VectorInfo = NULL;
|
|
Status = EfiGetSystemConfigurationTable (&gEfiVectorHandoffTableGuid, (VOID **)&VectorInfoList);
|
|
if ((Status == EFI_SUCCESS) && (VectorInfoList != NULL)) {
|
|
VectorInfo = VectorInfoList;
|
|
}
|
|
|
|
AsmReadIdtr (&IdtDescriptor);
|
|
IdtEntryCount = (IdtDescriptor.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR);
|
|
if (IdtEntryCount < CPU_INTERRUPT_NUM) {
|
|
//
|
|
// Increase Interrupt Descriptor Table and Copy the old IDT table in
|
|
//
|
|
IdtTable = AllocateZeroPool (sizeof (IA32_IDT_GATE_DESCRIPTOR) * CPU_INTERRUPT_NUM);
|
|
ASSERT (IdtTable != NULL);
|
|
CopyMem (IdtTable, (VOID *)IdtDescriptor.Base, sizeof (IA32_IDT_GATE_DESCRIPTOR) * IdtEntryCount);
|
|
|
|
//
|
|
// Load Interrupt Descriptor Table
|
|
//
|
|
IdtDescriptor.Base = (UINTN)IdtTable;
|
|
IdtDescriptor.Limit = (UINT16)(sizeof (IA32_IDT_GATE_DESCRIPTOR) * CPU_INTERRUPT_NUM - 1);
|
|
AsmWriteIdtr (&IdtDescriptor);
|
|
}
|
|
|
|
Status = InitializeCpuExceptionHandlers (VectorInfo);
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
|
|
/**
|
|
Callback function for idle events.
|
|
|
|
@param Event Event whose notification function is being invoked.
|
|
@param Context The pointer to the notification function's context,
|
|
which is implementation-dependent.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
IdleLoopEventCallback (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
CpuSleep ();
|
|
}
|
|
|
|
/**
|
|
Ensure the compatibility of a memory space descriptor with the MMIO aperture.
|
|
|
|
The memory space descriptor can come from the GCD memory space map, or it can
|
|
represent a gap between two neighboring memory space descriptors. In the
|
|
latter case, the GcdMemoryType field is expected to be
|
|
EfiGcdMemoryTypeNonExistent.
|
|
|
|
If the memory space descriptor already has type
|
|
EfiGcdMemoryTypeMemoryMappedIo, and its capabilities are a superset of the
|
|
required capabilities, then no action is taken -- it is by definition
|
|
compatible with the aperture.
|
|
|
|
Otherwise, the intersection of the memory space descriptor is calculated with
|
|
the aperture. If the intersection is the empty set (no overlap), no action is
|
|
taken; the memory space descriptor is compatible with the aperture.
|
|
|
|
Otherwise, the type of the descriptor is investigated again. If the type is
|
|
EfiGcdMemoryTypeNonExistent (representing a gap, or a genuine descriptor with
|
|
such a type), then an attempt is made to add the intersection as MMIO space
|
|
to the GCD memory space map, with the specified capabilities. This ensures
|
|
continuity for the aperture, and the descriptor is deemed compatible with the
|
|
aperture.
|
|
|
|
Otherwise, the memory space descriptor is incompatible with the MMIO
|
|
aperture.
|
|
|
|
@param[in] Base Base address of the aperture.
|
|
@param[in] Length Length of the aperture.
|
|
@param[in] Capabilities Capabilities required by the aperture.
|
|
@param[in] Descriptor The descriptor to ensure compatibility with the
|
|
aperture for.
|
|
|
|
@retval EFI_SUCCESS The descriptor is compatible. The GCD memory
|
|
space map may have been updated, for
|
|
continuity within the aperture.
|
|
@retval EFI_INVALID_PARAMETER The descriptor is incompatible.
|
|
@return Error codes from gDS->AddMemorySpace().
|
|
**/
|
|
EFI_STATUS
|
|
IntersectMemoryDescriptor (
|
|
IN UINT64 Base,
|
|
IN UINT64 Length,
|
|
IN UINT64 Capabilities,
|
|
IN CONST EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Descriptor
|
|
)
|
|
{
|
|
UINT64 IntersectionBase;
|
|
UINT64 IntersectionEnd;
|
|
EFI_STATUS Status;
|
|
|
|
if ((Descriptor->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) &&
|
|
((Descriptor->Capabilities & Capabilities) == Capabilities))
|
|
{
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
IntersectionBase = MAX (Base, Descriptor->BaseAddress);
|
|
IntersectionEnd = MIN (
|
|
Base + Length,
|
|
Descriptor->BaseAddress + Descriptor->Length
|
|
);
|
|
if (IntersectionBase >= IntersectionEnd) {
|
|
//
|
|
// The descriptor and the aperture don't overlap.
|
|
//
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
if (Descriptor->GcdMemoryType == EfiGcdMemoryTypeNonExistent) {
|
|
Status = gDS->AddMemorySpace (
|
|
EfiGcdMemoryTypeMemoryMappedIo,
|
|
IntersectionBase,
|
|
IntersectionEnd - IntersectionBase,
|
|
Capabilities
|
|
);
|
|
|
|
DEBUG ((
|
|
EFI_ERROR (Status) ? DEBUG_ERROR : DEBUG_VERBOSE,
|
|
"%a: %a: add [%Lx, %Lx): %r\n",
|
|
gEfiCallerBaseName,
|
|
__func__,
|
|
IntersectionBase,
|
|
IntersectionEnd,
|
|
Status
|
|
));
|
|
return Status;
|
|
}
|
|
|
|
DEBUG ((
|
|
DEBUG_ERROR,
|
|
"%a: %a: desc [%Lx, %Lx) type %u cap %Lx conflicts "
|
|
"with aperture [%Lx, %Lx) cap %Lx\n",
|
|
gEfiCallerBaseName,
|
|
__func__,
|
|
Descriptor->BaseAddress,
|
|
Descriptor->BaseAddress + Descriptor->Length,
|
|
(UINT32)Descriptor->GcdMemoryType,
|
|
Descriptor->Capabilities,
|
|
Base,
|
|
Base + Length,
|
|
Capabilities
|
|
));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
/**
|
|
Add MMIO space to GCD.
|
|
The routine checks the GCD database and only adds those which are
|
|
not added in the specified range to GCD.
|
|
|
|
@param Base Base address of the MMIO space.
|
|
@param Length Length of the MMIO space.
|
|
@param Capabilities Capabilities of the MMIO space.
|
|
|
|
@retval EFI_SUCCESS The MMIO space was added successfully.
|
|
**/
|
|
EFI_STATUS
|
|
AddMemoryMappedIoSpace (
|
|
IN UINT64 Base,
|
|
IN UINT64 Length,
|
|
IN UINT64 Capabilities
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
UINTN NumberOfDescriptors;
|
|
EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;
|
|
|
|
Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((
|
|
DEBUG_ERROR,
|
|
"%a: %a: GetMemorySpaceMap(): %r\n",
|
|
gEfiCallerBaseName,
|
|
__func__,
|
|
Status
|
|
));
|
|
return Status;
|
|
}
|
|
|
|
for (Index = 0; Index < NumberOfDescriptors; Index++) {
|
|
Status = IntersectMemoryDescriptor (
|
|
Base,
|
|
Length,
|
|
Capabilities,
|
|
&MemorySpaceMap[Index]
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto FreeMemorySpaceMap;
|
|
}
|
|
}
|
|
|
|
DEBUG_CODE_BEGIN ();
|
|
//
|
|
// Make sure there are adjacent descriptors covering [Base, Base + Length).
|
|
// It is possible that they have not been merged; merging can be prevented
|
|
// by allocation and different capabilities.
|
|
//
|
|
UINT64 CheckBase;
|
|
EFI_STATUS CheckStatus;
|
|
EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;
|
|
|
|
for (CheckBase = Base;
|
|
CheckBase < Base + Length;
|
|
CheckBase = Descriptor.BaseAddress + Descriptor.Length)
|
|
{
|
|
CheckStatus = gDS->GetMemorySpaceDescriptor (CheckBase, &Descriptor);
|
|
ASSERT_EFI_ERROR (CheckStatus);
|
|
ASSERT (Descriptor.GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo);
|
|
ASSERT ((Descriptor.Capabilities & Capabilities) == Capabilities);
|
|
}
|
|
|
|
DEBUG_CODE_END ();
|
|
|
|
FreeMemorySpaceMap:
|
|
FreePool (MemorySpaceMap);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Add and allocate CPU local APIC memory mapped space.
|
|
|
|
@param[in]ImageHandle Image handle this driver.
|
|
|
|
**/
|
|
VOID
|
|
AddLocalApicMemorySpace (
|
|
IN EFI_HANDLE ImageHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_PHYSICAL_ADDRESS BaseAddress;
|
|
|
|
BaseAddress = (EFI_PHYSICAL_ADDRESS)GetLocalApicBaseAddress ();
|
|
Status = AddMemoryMappedIoSpace (BaseAddress, SIZE_4KB, EFI_MEMORY_UC);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
//
|
|
// Try to allocate APIC memory mapped space, does not check return
|
|
// status because it may be allocated by other driver, or DXE Core if
|
|
// this range is built into Memory Allocation HOB.
|
|
//
|
|
Status = gDS->AllocateMemorySpace (
|
|
EfiGcdAllocateAddress,
|
|
EfiGcdMemoryTypeMemoryMappedIo,
|
|
0,
|
|
SIZE_4KB,
|
|
&BaseAddress,
|
|
ImageHandle,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"%a: %a: AllocateMemorySpace() Status - %r\n",
|
|
gEfiCallerBaseName,
|
|
__func__,
|
|
Status
|
|
));
|
|
}
|
|
}
|
|
|
|
/**
|
|
Initialize the state information for the CPU Architectural Protocol.
|
|
|
|
@param ImageHandle Image handle this driver.
|
|
@param SystemTable Pointer to the System Table.
|
|
|
|
@retval EFI_SUCCESS Thread can be successfully created
|
|
@retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
|
|
@retval EFI_DEVICE_ERROR Cannot create the thread
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
InitializeCpu (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_EVENT IdleLoopEvent;
|
|
|
|
InitializePageTableLib ();
|
|
|
|
InitializeFloatingPointUnits ();
|
|
|
|
//
|
|
// Make sure interrupts are disabled
|
|
//
|
|
DisableInterrupts ();
|
|
|
|
//
|
|
// Init GDT for DXE
|
|
//
|
|
InitGlobalDescriptorTable ();
|
|
|
|
//
|
|
// Setup IDT pointer, IDT and interrupt entry points
|
|
//
|
|
InitInterruptDescriptorTable ();
|
|
|
|
//
|
|
// Install CPU Architectural Protocol
|
|
//
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
&mCpuHandle,
|
|
&gEfiCpuArchProtocolGuid,
|
|
&gCpu,
|
|
NULL
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
//
|
|
// Refresh GCD memory space map according to MTRR value.
|
|
//
|
|
RefreshGcdMemoryAttributes ();
|
|
|
|
//
|
|
// Add and allocate local APIC memory mapped space
|
|
//
|
|
AddLocalApicMemorySpace (ImageHandle);
|
|
|
|
//
|
|
// Setup a callback for idle events
|
|
//
|
|
Status = gBS->CreateEventEx (
|
|
EVT_NOTIFY_SIGNAL,
|
|
TPL_NOTIFY,
|
|
IdleLoopEventCallback,
|
|
NULL,
|
|
&gIdleLoopEventGuid,
|
|
&IdleLoopEvent
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
InitializeMpSupport ();
|
|
|
|
return Status;
|
|
}
|