/** @file\r
- CPU DXE Module.\r
+ CPU DXE Module to produce CPU ARCH Protocol.\r
\r
- Copyright (c) 2008 - 2013, Intel Corporation. All rights reserved.<BR>\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
+ Copyright (c) 2008 - 2018, Intel Corporation. All rights reserved.<BR>\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
#include "CpuDxe.h"\r
#include "CpuMp.h"\r
+#include "CpuPageTable.h"\r
+\r
+#define CACHE_ATTRIBUTE_MASK (EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_WP)\r
+#define MEMORY_ATTRIBUTE_MASK (EFI_MEMORY_RP | EFI_MEMORY_XP | EFI_MEMORY_RO)\r
\r
//\r
// Global Variables\r
BOOLEAN InterruptState = FALSE;\r
EFI_HANDLE mCpuHandle = NULL;\r
BOOLEAN mIsFlushingGCD;\r
-UINT64 mValidMtrrAddressMask = MTRR_LIB_CACHE_VALID_ADDRESS;\r
-UINT64 mValidMtrrBitsMask = MTRR_LIB_MSR_VALID_MASK;\r
+BOOLEAN mIsAllocatingPageTable = FALSE;\r
+UINT64 mValidMtrrAddressMask;\r
+UINT64 mValidMtrrBitsMask;\r
+UINT64 mTimerPeriod = 0;\r
\r
FIXED_MTRR mFixedMtrrTable[] = {\r
{\r
- MTRR_LIB_IA32_MTRR_FIX64K_00000,\r
+ MSR_IA32_MTRR_FIX64K_00000,\r
0,\r
0x10000\r
},\r
{\r
- MTRR_LIB_IA32_MTRR_FIX16K_80000,\r
+ MSR_IA32_MTRR_FIX16K_80000,\r
0x80000,\r
0x4000\r
},\r
{\r
- MTRR_LIB_IA32_MTRR_FIX16K_A0000,\r
+ MSR_IA32_MTRR_FIX16K_A0000,\r
0xA0000,\r
0x4000\r
},\r
{\r
- MTRR_LIB_IA32_MTRR_FIX4K_C0000,\r
+ MSR_IA32_MTRR_FIX4K_C0000,\r
0xC0000,\r
0x1000\r
},\r
{\r
- MTRR_LIB_IA32_MTRR_FIX4K_C8000,\r
+ MSR_IA32_MTRR_FIX4K_C8000,\r
0xC8000,\r
0x1000\r
},\r
{\r
- MTRR_LIB_IA32_MTRR_FIX4K_D0000,\r
+ MSR_IA32_MTRR_FIX4K_D0000,\r
0xD0000,\r
0x1000\r
},\r
{\r
- MTRR_LIB_IA32_MTRR_FIX4K_D8000,\r
+ MSR_IA32_MTRR_FIX4K_D8000,\r
0xD8000,\r
0x1000\r
},\r
{\r
- MTRR_LIB_IA32_MTRR_FIX4K_E0000,\r
+ MSR_IA32_MTRR_FIX4K_E0000,\r
0xE0000,\r
0x1000\r
},\r
{\r
- MTRR_LIB_IA32_MTRR_FIX4K_E8000,\r
+ MSR_IA32_MTRR_FIX4K_E8000,\r
0xE8000,\r
0x1000\r
},\r
{\r
- MTRR_LIB_IA32_MTRR_FIX4K_F0000,\r
+ MSR_IA32_MTRR_FIX4K_F0000,\r
0xF0000,\r
0x1000\r
},\r
{\r
- MTRR_LIB_IA32_MTRR_FIX4K_F8000,\r
+ MSR_IA32_MTRR_FIX4K_F8000,\r
0xF8000,\r
0x1000\r
},\r
OUT UINT64 *TimerPeriod OPTIONAL\r
)\r
{\r
+ UINT64 BeginValue;\r
+ UINT64 EndValue;\r
+\r
if (TimerValue == NULL) {\r
return EFI_INVALID_PARAMETER;\r
}\r
*TimerValue = AsmReadTsc ();\r
\r
if (TimerPeriod != NULL) {\r
+ if (mTimerPeriod == 0) {\r
+ //\r
+ // Read time stamp counter before and after delay of 100 microseconds\r
+ //\r
+ BeginValue = AsmReadTsc ();\r
+ MicroSecondDelay (100);\r
+ EndValue = AsmReadTsc ();\r
//\r
- // BugBug: Hard coded. Don't know how to do this generically\r
+ // Calculate the actual frequency\r
//\r
- *TimerPeriod = 1000000000;\r
+ mTimerPeriod = DivU64x64Remainder (\r
+ MultU64x32 (\r
+ 1000 * 1000 * 1000,\r
+ 100\r
+ ),\r
+ EndValue - BeginValue,\r
+ NULL\r
+ );\r
+ }\r
+ *TimerPeriod = mTimerPeriod;\r
}\r
\r
return EFI_SUCCESS;\r
}\r
\r
+/**\r
+ A minimal wrapper function that allows MtrrSetAllMtrrs() to be passed to\r
+ EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() as Procedure.\r
+\r
+ @param[in] Buffer Pointer to an MTRR_SETTINGS object, to be passed to\r
+ MtrrSetAllMtrrs().\r
+**/\r
+VOID\r
+EFIAPI\r
+SetMtrrsFromBuffer (\r
+ IN VOID *Buffer\r
+ )\r
+{\r
+ MtrrSetAllMtrrs (Buffer);\r
+}\r
\r
/**\r
Implementation of SetMemoryAttributes() service of CPU Architecture Protocol.\r
{\r
RETURN_STATUS Status;\r
MTRR_MEMORY_CACHE_TYPE CacheType;\r
-\r
- if (!IsMtrrSupported ()) {\r
- return EFI_UNSUPPORTED;\r
- }\r
+ EFI_STATUS MpStatus;\r
+ EFI_MP_SERVICES_PROTOCOL *MpService;\r
+ MTRR_SETTINGS MtrrSettings;\r
+ UINT64 CacheAttributes;\r
+ UINT64 MemoryAttributes;\r
+ MTRR_MEMORY_CACHE_TYPE CurrentCacheType;\r
\r
//\r
// If this function is called because GCD SetMemorySpaceAttributes () is called\r
// to avoid unnecessary computing.\r
//\r
if (mIsFlushingGCD) {\r
- DEBUG((EFI_D_INFO, " Flushing GCD\n"));\r
+ DEBUG((DEBUG_VERBOSE, " Flushing GCD\n"));\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // During memory attributes updating, new pages may be allocated to setup\r
+ // smaller granularity of page table. Page allocation action might then cause\r
+ // another calling of CpuSetMemoryAttributes() recursively, due to memory\r
+ // protection policy configured (such as PcdDxeNxMemoryProtectionPolicy).\r
+ // Since this driver will always protect memory used as page table by itself,\r
+ // there's no need to apply protection policy requested from memory service.\r
+ // So it's safe to just return EFI_SUCCESS if this time of calling is caused\r
+ // by page table memory allocation.\r
+ //\r
+ if (mIsAllocatingPageTable) {\r
+ DEBUG((DEBUG_VERBOSE, " Allocating page table memory\n"));\r
return EFI_SUCCESS;\r
}\r
\r
- switch (Attributes) {\r
- case EFI_MEMORY_UC:\r
- CacheType = CacheUncacheable;\r
- break;\r
+ CacheAttributes = Attributes & CACHE_ATTRIBUTE_MASK;\r
+ MemoryAttributes = Attributes & MEMORY_ATTRIBUTE_MASK;\r
\r
- case EFI_MEMORY_WC:\r
- CacheType = CacheWriteCombining;\r
- break;\r
+ if (Attributes != (CacheAttributes | MemoryAttributes)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
\r
- case EFI_MEMORY_WT:\r
- CacheType = CacheWriteThrough;\r
- break;\r
+ if (CacheAttributes != 0) {\r
+ if (!IsMtrrSupported ()) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
\r
- case EFI_MEMORY_WP:\r
- CacheType = CacheWriteProtected;\r
- break;\r
+ switch (CacheAttributes) {\r
+ case EFI_MEMORY_UC:\r
+ CacheType = CacheUncacheable;\r
+ break;\r
\r
- case EFI_MEMORY_WB:\r
- CacheType = CacheWriteBack;\r
- break;\r
+ case EFI_MEMORY_WC:\r
+ CacheType = CacheWriteCombining;\r
+ break;\r
\r
- case EFI_MEMORY_UCE:\r
- case EFI_MEMORY_RP:\r
- case EFI_MEMORY_XP:\r
- case EFI_MEMORY_RUNTIME:\r
- return EFI_UNSUPPORTED;\r
+ case EFI_MEMORY_WT:\r
+ CacheType = CacheWriteThrough;\r
+ break;\r
\r
- default:\r
- return EFI_INVALID_PARAMETER;\r
+ case EFI_MEMORY_WP:\r
+ CacheType = CacheWriteProtected;\r
+ break;\r
+\r
+ case EFI_MEMORY_WB:\r
+ CacheType = CacheWriteBack;\r
+ break;\r
+\r
+ default:\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ CurrentCacheType = MtrrGetMemoryAttribute(BaseAddress);\r
+ if (CurrentCacheType != CacheType) {\r
+ //\r
+ // call MTRR libary function\r
+ //\r
+ Status = MtrrSetMemoryAttribute (\r
+ BaseAddress,\r
+ Length,\r
+ CacheType\r
+ );\r
+\r
+ if (!RETURN_ERROR (Status)) {\r
+ MpStatus = gBS->LocateProtocol (\r
+ &gEfiMpServiceProtocolGuid,\r
+ NULL,\r
+ (VOID **)&MpService\r
+ );\r
+ //\r
+ // Synchronize the update with all APs\r
+ //\r
+ if (!EFI_ERROR (MpStatus)) {\r
+ MtrrGetAllMtrrs (&MtrrSettings);\r
+ MpStatus = MpService->StartupAllAPs (\r
+ MpService, // This\r
+ SetMtrrsFromBuffer, // Procedure\r
+ FALSE, // SingleThread\r
+ NULL, // WaitEvent\r
+ 0, // TimeoutInMicrosecsond\r
+ &MtrrSettings, // ProcedureArgument\r
+ NULL // FailedCpuList\r
+ );\r
+ ASSERT (MpStatus == EFI_SUCCESS || MpStatus == EFI_NOT_STARTED);\r
+ }\r
+ }\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+ }\r
}\r
+\r
//\r
- // call MTRR libary function\r
+ // Set memory attribute by page table\r
//\r
- Status = MtrrSetMemoryAttribute (\r
- BaseAddress,\r
- Length,\r
- CacheType\r
- );\r
-\r
- return (EFI_STATUS) Status;\r
+ return AssignMemoryPageAttributes (NULL, BaseAddress, Length, MemoryAttributes, NULL);\r
}\r
\r
/**\r
AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);\r
\r
PhysicalAddressBits = (UINT8) RegEax;\r
-\r
- mValidMtrrBitsMask = LShiftU64 (1, PhysicalAddressBits) - 1;\r
- mValidMtrrAddressMask = mValidMtrrBitsMask & 0xfffffffffffff000ULL;\r
} else {\r
- mValidMtrrBitsMask = MTRR_LIB_MSR_VALID_MASK;\r
- mValidMtrrAddressMask = MTRR_LIB_CACHE_VALID_ADDRESS;\r
+ PhysicalAddressBits = 36;\r
}\r
+\r
+ mValidMtrrBitsMask = LShiftU64 (1, PhysicalAddressBits) - 1;\r
+ mValidMtrrAddressMask = mValidMtrrBitsMask & 0xfffffffffffff000ULL;\r
}\r
\r
/**\r
\r
**/\r
VOID\r
-RefreshGcdMemoryAttributes (\r
+RefreshMemoryAttributesFromMtrr (\r
VOID\r
)\r
{\r
UINT32 FirmwareVariableMtrrCount;\r
UINT8 DefaultMemoryType;\r
\r
- if (!IsMtrrSupported ()) {\r
- return;\r
- }\r
-\r
FirmwareVariableMtrrCount = GetFirmwareVariableMtrrCount ();\r
ASSERT (FirmwareVariableMtrrCount <= MTRR_NUMBER_OF_VARIABLE_MTRR);\r
\r
- mIsFlushingGCD = TRUE;\r
MemorySpaceMap = NULL;\r
\r
//\r
if (MemorySpaceMap != NULL) {\r
FreePool (MemorySpaceMap);\r
}\r
+}\r
+\r
+/**\r
+ Check if paging is enabled or not.\r
+**/\r
+BOOLEAN\r
+IsPagingAndPageAddressExtensionsEnabled (\r
+ VOID\r
+ )\r
+{\r
+ IA32_CR0 Cr0;\r
+ IA32_CR4 Cr4;\r
+\r
+ Cr0.UintN = AsmReadCr0 ();\r
+ Cr4.UintN = AsmReadCr4 ();\r
+\r
+ return ((Cr0.Bits.PG != 0) && (Cr4.Bits.PAE != 0));\r
+}\r
+\r
+/**\r
+ Refreshes the GCD Memory Space attributes according to MTRRs and Paging.\r
+\r
+ This function refreshes the GCD Memory Space attributes according to MTRRs\r
+ and page tables.\r
+\r
+**/\r
+VOID\r
+RefreshGcdMemoryAttributes (\r
+ VOID\r
+ )\r
+{\r
+ mIsFlushingGCD = TRUE;\r
+\r
+ if (IsMtrrSupported ()) {\r
+ RefreshMemoryAttributesFromMtrr ();\r
+ }\r
+\r
+ if (IsPagingAndPageAddressExtensionsEnabled ()) {\r
+ RefreshGcdMemoryAttributesFromPaging ();\r
+ }\r
\r
mIsFlushingGCD = FALSE;\r
}\r
CpuSleep ();\r
}\r
\r
+/**\r
+ Ensure the compatibility of a memory space descriptor with the MMIO aperture.\r
+\r
+ The memory space descriptor can come from the GCD memory space map, or it can\r
+ represent a gap between two neighboring memory space descriptors. In the\r
+ latter case, the GcdMemoryType field is expected to be\r
+ EfiGcdMemoryTypeNonExistent.\r
+\r
+ If the memory space descriptor already has type\r
+ EfiGcdMemoryTypeMemoryMappedIo, and its capabilities are a superset of the\r
+ required capabilities, then no action is taken -- it is by definition\r
+ compatible with the aperture.\r
+\r
+ Otherwise, the intersection of the memory space descriptor is calculated with\r
+ the aperture. If the intersection is the empty set (no overlap), no action is\r
+ taken; the memory space descriptor is compatible with the aperture.\r
+\r
+ Otherwise, the type of the descriptor is investigated again. If the type is\r
+ EfiGcdMemoryTypeNonExistent (representing a gap, or a genuine descriptor with\r
+ such a type), then an attempt is made to add the intersection as MMIO space\r
+ to the GCD memory space map, with the specified capabilities. This ensures\r
+ continuity for the aperture, and the descriptor is deemed compatible with the\r
+ aperture.\r
+\r
+ Otherwise, the memory space descriptor is incompatible with the MMIO\r
+ aperture.\r
+\r
+ @param[in] Base Base address of the aperture.\r
+ @param[in] Length Length of the aperture.\r
+ @param[in] Capabilities Capabilities required by the aperture.\r
+ @param[in] Descriptor The descriptor to ensure compatibility with the\r
+ aperture for.\r
+\r
+ @retval EFI_SUCCESS The descriptor is compatible. The GCD memory\r
+ space map may have been updated, for\r
+ continuity within the aperture.\r
+ @retval EFI_INVALID_PARAMETER The descriptor is incompatible.\r
+ @return Error codes from gDS->AddMemorySpace().\r
+**/\r
+EFI_STATUS\r
+IntersectMemoryDescriptor (\r
+ IN UINT64 Base,\r
+ IN UINT64 Length,\r
+ IN UINT64 Capabilities,\r
+ IN CONST EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Descriptor\r
+ )\r
+{\r
+ UINT64 IntersectionBase;\r
+ UINT64 IntersectionEnd;\r
+ EFI_STATUS Status;\r
+\r
+ if (Descriptor->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo &&\r
+ (Descriptor->Capabilities & Capabilities) == Capabilities) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ IntersectionBase = MAX (Base, Descriptor->BaseAddress);\r
+ IntersectionEnd = MIN (Base + Length,\r
+ Descriptor->BaseAddress + Descriptor->Length);\r
+ if (IntersectionBase >= IntersectionEnd) {\r
+ //\r
+ // The descriptor and the aperture don't overlap.\r
+ //\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (Descriptor->GcdMemoryType == EfiGcdMemoryTypeNonExistent) {\r
+ Status = gDS->AddMemorySpace (EfiGcdMemoryTypeMemoryMappedIo,\r
+ IntersectionBase, IntersectionEnd - IntersectionBase,\r
+ Capabilities);\r
+\r
+ DEBUG ((EFI_ERROR (Status) ? DEBUG_ERROR : DEBUG_VERBOSE,\r
+ "%a: %a: add [%Lx, %Lx): %r\n", gEfiCallerBaseName, __FUNCTION__,\r
+ IntersectionBase, IntersectionEnd, Status));\r
+ return Status;\r
+ }\r
+\r
+ DEBUG ((DEBUG_ERROR, "%a: %a: desc [%Lx, %Lx) type %u cap %Lx conflicts "\r
+ "with aperture [%Lx, %Lx) cap %Lx\n", gEfiCallerBaseName, __FUNCTION__,\r
+ Descriptor->BaseAddress, Descriptor->BaseAddress + Descriptor->Length,\r
+ (UINT32)Descriptor->GcdMemoryType, Descriptor->Capabilities,\r
+ Base, Base + Length, Capabilities));\r
+ return EFI_INVALID_PARAMETER;\r
+}\r
+\r
+/**\r
+ Add MMIO space to GCD.\r
+ The routine checks the GCD database and only adds those which are\r
+ not added in the specified range to GCD.\r
+\r
+ @param Base Base address of the MMIO space.\r
+ @param Length Length of the MMIO space.\r
+ @param Capabilities Capabilities of the MMIO space.\r
+\r
+ @retval EFI_SUCCES The MMIO space was added successfully.\r
+**/\r
+EFI_STATUS\r
+AddMemoryMappedIoSpace (\r
+ IN UINT64 Base,\r
+ IN UINT64 Length,\r
+ IN UINT64 Capabilities\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Index;\r
+ UINTN NumberOfDescriptors;\r
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;\r
+\r
+ Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "%a: %a: GetMemorySpaceMap(): %r\n",\r
+ gEfiCallerBaseName, __FUNCTION__, Status));\r
+ return Status;\r
+ }\r
+\r
+ for (Index = 0; Index < NumberOfDescriptors; Index++) {\r
+ Status = IntersectMemoryDescriptor (Base, Length, Capabilities,\r
+ &MemorySpaceMap[Index]);\r
+ if (EFI_ERROR (Status)) {\r
+ goto FreeMemorySpaceMap;\r
+ }\r
+ }\r
+\r
+ DEBUG_CODE (\r
+ //\r
+ // Make sure there are adjacent descriptors covering [Base, Base + Length).\r
+ // It is possible that they have not been merged; merging can be prevented\r
+ // by allocation and different capabilities.\r
+ //\r
+ UINT64 CheckBase;\r
+ EFI_STATUS CheckStatus;\r
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;\r
+\r
+ for (CheckBase = Base;\r
+ CheckBase < Base + Length;\r
+ CheckBase = Descriptor.BaseAddress + Descriptor.Length) {\r
+ CheckStatus = gDS->GetMemorySpaceDescriptor (CheckBase, &Descriptor);\r
+ ASSERT_EFI_ERROR (CheckStatus);\r
+ ASSERT (Descriptor.GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo);\r
+ ASSERT ((Descriptor.Capabilities & Capabilities) == Capabilities);\r
+ }\r
+ );\r
+\r
+FreeMemorySpaceMap:\r
+ FreePool (MemorySpaceMap);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Add and allocate CPU local APIC memory mapped space.\r
+\r
+ @param[in]ImageHandle Image handle this driver.\r
+\r
+**/\r
+VOID\r
+AddLocalApicMemorySpace (\r
+ IN EFI_HANDLE ImageHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PHYSICAL_ADDRESS BaseAddress;\r
+\r
+ BaseAddress = (EFI_PHYSICAL_ADDRESS) GetLocalApicBaseAddress();\r
+ Status = AddMemoryMappedIoSpace (BaseAddress, SIZE_4KB, EFI_MEMORY_UC);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // Try to allocate APIC memory mapped space, does not check return\r
+ // status because it may be allocated by other driver, or DXE Core if\r
+ // this range is built into Memory Allocation HOB.\r
+ //\r
+ Status = gDS->AllocateMemorySpace (\r
+ EfiGcdAllocateAddress,\r
+ EfiGcdMemoryTypeMemoryMappedIo,\r
+ 0,\r
+ SIZE_4KB,\r
+ &BaseAddress,\r
+ ImageHandle,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_INFO, "%a: %a: AllocateMemorySpace() Status - %r\n",\r
+ gEfiCallerBaseName, __FUNCTION__, Status));\r
+ }\r
+}\r
\r
/**\r
Initialize the state information for the CPU Architectural Protocol.\r
EFI_STATUS Status;\r
EFI_EVENT IdleLoopEvent;\r
\r
+ InitializePageTableLib();\r
+\r
InitializeFloatingPointUnits ();\r
\r
//\r
//\r
InitInterruptDescriptorTable ();\r
\r
- //\r
- // Enable the local APIC for Virtual Wire Mode.\r
- //\r
- ProgramVirtualWireMode ();\r
-\r
//\r
// Install CPU Architectural Protocol\r
//\r
//\r
RefreshGcdMemoryAttributes ();\r
\r
+ //\r
+ // Add and allocate local APIC memory mapped space\r
+ //\r
+ AddLocalApicMemorySpace (ImageHandle);\r
+\r
//\r
// Setup a callback for idle events\r
//\r