#include <Library/DebugLib.h>\r
#include <Library/UefiBootServicesTableLib.h>\r
#include <Protocol/MpService.h>\r
+#include <Protocol/SmmBase2.h>\r
+#include <Register/Cpuid.h>\r
+#include <Register/Msr.h>\r
\r
#include "CpuDxe.h"\r
#include "CpuPageTable.h"\r
\r
+///\r
+/// Paging registers\r
+///\r
+#define CR0_WP BIT16\r
+#define CR0_PG BIT31\r
+#define CR4_PSE BIT4\r
+#define CR4_PAE BIT5\r
+\r
///\r
/// Page Table Entry\r
///\r
{Page1G, SIZE_1GB, PAGING_1G_ADDRESS_MASK_64},\r
};\r
\r
-PAGE_TABLE_POOL *mPageTablePool = NULL;\r
-\r
-/**\r
- Enable write protection function for AP.\r
-\r
- @param[in,out] Buffer The pointer to private data buffer.\r
-**/\r
-VOID\r
-EFIAPI\r
-SyncCpuEnableWriteProtection (\r
- IN OUT VOID *Buffer\r
- )\r
-{\r
- AsmWriteCr0 (AsmReadCr0 () | BIT16);\r
-}\r
+PAGE_TABLE_POOL *mPageTablePool = NULL;\r
+PAGE_TABLE_LIB_PAGING_CONTEXT mPagingContext;\r
+EFI_SMM_BASE2_PROTOCOL *mSmmBase2 = NULL;\r
\r
/**\r
- CpuFlushTlb function for AP.\r
-\r
- @param[in,out] Buffer The pointer to private data buffer.\r
+ Check if current execution environment is in SMM mode or not, via\r
+ EFI_SMM_BASE2_PROTOCOL.\r
+\r
+ This is necessary because of the fact that MdePkg\Library\SmmMemoryAllocationLib\r
+ supports to free memory outside SMRAM. The library will call gBS->FreePool() or\r
+ gBS->FreePages() and then SetMemorySpaceAttributes interface in turn to change\r
+ memory paging attributes during free operation, if some memory related features\r
+ are enabled (like Heap Guard).\r
+\r
+ This means that SetMemorySpaceAttributes() has chance to run in SMM mode. This\r
+ will cause incorrect result because SMM mode always loads its own page tables,\r
+ which are usually different from DXE. This function can be used to detect such\r
+ situation and help to avoid further misoperations.\r
+\r
+ @retval TRUE In SMM mode.\r
+ @retval FALSE Not in SMM mode.\r
**/\r
-VOID\r
-EFIAPI\r
-SyncCpuFlushTlb (\r
- IN OUT VOID *Buffer\r
+BOOLEAN\r
+IsInSmm (\r
+ VOID\r
)\r
{\r
- CpuFlushTlb();\r
-}\r
+ BOOLEAN InSmm;\r
\r
-/**\r
- Sync memory page attributes for AP.\r
+ InSmm = FALSE;\r
+ if (mSmmBase2 == NULL) {\r
+ gBS->LocateProtocol (&gEfiSmmBase2ProtocolGuid, NULL, (VOID **)&mSmmBase2);\r
+ }\r
\r
- @param[in] Procedure A pointer to the function to be run on enabled APs of\r
- the system.\r
-**/\r
-VOID\r
-SyncMemoryPageAttributesAp (\r
- IN EFI_AP_PROCEDURE Procedure\r
- )\r
-{\r
- EFI_STATUS Status;\r
- EFI_MP_SERVICES_PROTOCOL *MpService;\r
-\r
- Status = gBS->LocateProtocol (\r
- &gEfiMpServiceProtocolGuid,\r
- NULL,\r
- (VOID **)&MpService\r
- );\r
- //\r
- // Synchronize the update with all APs\r
- //\r
- if (!EFI_ERROR (Status)) {\r
- Status = MpService->StartupAllAPs (\r
- MpService, // This\r
- Procedure, // Procedure\r
- FALSE, // SingleThread\r
- NULL, // WaitEvent\r
- 0, // TimeoutInMicrosecsond\r
- NULL, // ProcedureArgument\r
- NULL // FailedCpuList\r
- );\r
- ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_STARTED || Status == EFI_NOT_READY);\r
+ if (mSmmBase2 != NULL) {\r
+ mSmmBase2->InSmm (mSmmBase2, &InSmm);\r
}\r
+\r
+ return InSmm;\r
}\r
\r
/**\r
IN OUT PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext\r
)\r
{\r
- UINT32 RegEax;\r
- UINT32 RegEdx;\r
+ UINT32 RegEax;\r
+ CPUID_EXTENDED_CPU_SIG_EDX RegEdx;\r
+ MSR_IA32_EFER_REGISTER MsrEfer;\r
\r
- ZeroMem(PagingContext, sizeof(*PagingContext));\r
- if (sizeof(UINTN) == sizeof(UINT64)) {\r
- PagingContext->MachineType = IMAGE_FILE_MACHINE_X64;\r
- } else {\r
- PagingContext->MachineType = IMAGE_FILE_MACHINE_I386;\r
- }\r
- if ((AsmReadCr0 () & BIT31) != 0) {\r
- PagingContext->ContextData.X64.PageTableBase = (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64);\r
- } else {\r
- PagingContext->ContextData.X64.PageTableBase = 0;\r
- }\r
+ //\r
+ // Don't retrieve current paging context from processor if in SMM mode.\r
+ //\r
+ if (!IsInSmm ()) {\r
+ ZeroMem (&mPagingContext, sizeof(mPagingContext));\r
+ if (sizeof(UINTN) == sizeof(UINT64)) {\r
+ mPagingContext.MachineType = IMAGE_FILE_MACHINE_X64;\r
+ } else {\r
+ mPagingContext.MachineType = IMAGE_FILE_MACHINE_I386;\r
+ }\r
+ if ((AsmReadCr0 () & CR0_PG) != 0) {\r
+ mPagingContext.ContextData.X64.PageTableBase = (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64);\r
+ } else {\r
+ mPagingContext.ContextData.X64.PageTableBase = 0;\r
+ }\r
\r
- if ((AsmReadCr4 () & BIT4) != 0) {\r
- PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE;\r
- }\r
- if ((AsmReadCr4 () & BIT5) != 0) {\r
- PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE;\r
- }\r
- if ((AsmReadCr0 () & BIT16) != 0) {\r
- PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE;\r
- }\r
+ if ((AsmReadCr4 () & CR4_PSE) != 0) {\r
+ mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE;\r
+ }\r
+ if ((AsmReadCr4 () & CR4_PAE) != 0) {\r
+ mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE;\r
+ }\r
+ if ((AsmReadCr0 () & CR0_WP) != 0) {\r
+ mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE;\r
+ }\r
+\r
+ AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL);\r
+ if (RegEax >= CPUID_EXTENDED_CPU_SIG) {\r
+ AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &RegEdx.Uint32);\r
+\r
+ if (RegEdx.Bits.NX != 0) {\r
+ // XD supported\r
+ MsrEfer.Uint64 = AsmReadMsr64(MSR_CORE_IA32_EFER);\r
+ if (MsrEfer.Bits.NXE != 0) {\r
+ // XD activated\r
+ mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED;\r
+ }\r
+ }\r
\r
- AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
- if (RegEax > 0x80000000) {\r
- AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);\r
- if ((RegEdx & BIT20) != 0) {\r
- // XD supported\r
- if ((AsmReadMsr64 (0xC0000080) & BIT11) != 0) {\r
- // XD activated\r
- PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED;\r
+ if (RegEdx.Bits.Page1GB != 0) {\r
+ mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPORT;\r
}\r
}\r
- if ((RegEdx & BIT26) != 0) {\r
- PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPORT;\r
- }\r
}\r
+\r
+ //\r
+ // This can avoid getting SMM paging context if in SMM mode. We cannot assume\r
+ // SMM mode shares the same paging context as DXE.\r
+ //\r
+ CopyMem (PagingContext, &mPagingContext, sizeof (mPagingContext));\r
}\r
\r
/**\r
for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {\r
NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | AddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);\r
}\r
- (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);\r
+ (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | ((*PageEntry) & PAGE_ATTRIBUTE_BITS);\r
return RETURN_SUCCESS;\r
} else {\r
return RETURN_UNSUPPORTED;\r
for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {\r
NewPageEntry[Index] = (BaseAddress + SIZE_2MB * Index) | AddressEncMask | IA32_PG_PS | ((*PageEntry) & PAGE_PROGATE_BITS);\r
}\r
- (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);\r
+ (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | ((*PageEntry) & PAGE_ATTRIBUTE_BITS);\r
return RETURN_SUCCESS;\r
} else {\r
return RETURN_UNSUPPORTED;\r
VOID\r
)\r
{\r
- return ((AsmReadCr0 () & BIT16) != 0);\r
-}\r
-\r
-/**\r
- Disable write protection function for AP.\r
-\r
- @param[in,out] Buffer The pointer to private data buffer.\r
-**/\r
-VOID\r
-EFIAPI\r
-SyncCpuDisableWriteProtection (\r
- IN OUT VOID *Buffer\r
- )\r
-{\r
- AsmWriteCr0 (AsmReadCr0() & ~BIT16);\r
+ //\r
+ // To avoid unforseen consequences, don't touch paging settings in SMM mode\r
+ // in this driver.\r
+ //\r
+ if (!IsInSmm ()) {\r
+ return ((AsmReadCr0 () & CR0_WP) != 0);\r
+ }\r
+ return FALSE;\r
}\r
\r
/**\r
VOID\r
)\r
{\r
- AsmWriteCr0 (AsmReadCr0() & ~BIT16);\r
- SyncMemoryPageAttributesAp (SyncCpuDisableWriteProtection);\r
+ //\r
+ // To avoid unforseen consequences, don't touch paging settings in SMM mode\r
+ // in this driver.\r
+ //\r
+ if (!IsInSmm ()) {\r
+ AsmWriteCr0 (AsmReadCr0 () & ~CR0_WP);\r
+ }\r
}\r
\r
/**\r
VOID\r
)\r
{\r
- AsmWriteCr0 (AsmReadCr0() | BIT16);\r
- SyncMemoryPageAttributesAp (SyncCpuEnableWriteProtection);\r
+ //\r
+ // To avoid unforseen consequences, don't touch paging settings in SMM mode\r
+ // in this driver.\r
+ //\r
+ if (!IsInSmm ()) {\r
+ AsmWriteCr0 (AsmReadCr0 () | CR0_WP);\r
+ }\r
}\r
\r
/**\r
DEBUG ((DEBUG_ERROR, "Non-PAE Paging!\n"));\r
return EFI_UNSUPPORTED;\r
}\r
+ if ((BaseAddress + Length) > BASE_4GB) {\r
+ DEBUG ((DEBUG_ERROR, "Beyond 4GB memory in 32-bit mode!\n"));\r
+ return EFI_UNSUPPORTED;\r
+ }\r
break;\r
case IMAGE_FILE_MACHINE_X64:\r
ASSERT (CurrentPagingContext.ContextData.X64.PageTableBase != 0);\r
if (!EFI_ERROR(Status)) {\r
if ((PagingContext == NULL) && IsModified) {\r
//\r
- // Flush TLB as last step\r
+ // Flush TLB as last step.\r
+ //\r
+ // Note: Since APs will always init CR3 register in HLT loop mode or do\r
+ // TLB flush in MWAIT loop mode, there's no need to flush TLB for them\r
+ // here.\r
//\r
CpuFlushTlb();\r
- SyncMemoryPageAttributesAp (SyncCpuFlushTlb);\r
}\r
}\r
\r
);\r
ASSERT_EFI_ERROR (Status);\r
DEBUG ((\r
- DEBUG_INFO,\r
+ DEBUG_VERBOSE,\r
"Updated memory space attribute: [%lu] %016lx - %016lx (%016lx -> %016lx)\r\n",\r
(UINT64)Index, BaseAddress, BaseAddress + Length - 1,\r
MemorySpaceMap[Index].Attributes,\r