- }\r
-\r
- Buffer = (UINT8 *)mPageTablePool + mPageTablePool->Offset;\r
-\r
- mPageTablePool->Offset += EFI_PAGES_TO_SIZE (Pages);\r
- mPageTablePool->FreePages -= Pages;\r
-\r
- DEBUG ((\r
- DEBUG_VERBOSE,\r
- "%a:%a: Buffer=0x%Lx Pages=%ld\n",\r
- gEfiCallerBaseName,\r
- __FUNCTION__,\r
- Buffer,\r
- Pages\r
- ));\r
-\r
- return Buffer;\r
-}\r
-\r
-\r
-/**\r
- Split 2M page to 4K.\r
-\r
- @param[in] PhysicalAddress Start physical address the 2M page\r
- covered.\r
- @param[in, out] PageEntry2M Pointer to 2M page entry.\r
- @param[in] StackBase Stack base address.\r
- @param[in] StackSize Stack size.\r
-\r
-**/\r
-STATIC\r
-VOID\r
-Split2MPageTo4K (\r
- IN PHYSICAL_ADDRESS PhysicalAddress,\r
- IN OUT UINT64 *PageEntry2M,\r
- IN PHYSICAL_ADDRESS StackBase,\r
- IN UINTN StackSize\r
- )\r
-{\r
- PHYSICAL_ADDRESS PhysicalAddress4K;\r
- UINTN IndexOfPageTableEntries;\r
- PAGE_TABLE_4K_ENTRY *PageTableEntry, *PageTableEntry1;\r
- UINT64 AddressEncMask;\r
-\r
- PageTableEntry = AllocatePageTableMemory(1);\r
-\r
- PageTableEntry1 = PageTableEntry;\r
-\r
- AddressEncMask = GetMemEncryptionAddressMask ();\r
-\r
- ASSERT (PageTableEntry != NULL);\r
- ASSERT (*PageEntry2M & AddressEncMask);\r
-\r
- PhysicalAddress4K = PhysicalAddress;\r
- for (IndexOfPageTableEntries = 0;\r
- IndexOfPageTableEntries < 512;\r
- (IndexOfPageTableEntries++,\r
- PageTableEntry++,\r
- PhysicalAddress4K += SIZE_4KB)) {\r
- //\r
- // Fill in the Page Table entries\r
- //\r
- PageTableEntry->Uint64 = (UINT64) PhysicalAddress4K | AddressEncMask;\r
- PageTableEntry->Bits.ReadWrite = 1;\r
- PageTableEntry->Bits.Present = 1;\r
- if ((PhysicalAddress4K >= StackBase) &&\r
- (PhysicalAddress4K < StackBase + StackSize)) {\r
- //\r
- // Set Nx bit for stack.\r
- //\r
- PageTableEntry->Bits.Nx = 1;\r
- }\r
- }\r
-\r
- //\r
- // Fill in 2M page entry.\r
- //\r
- *PageEntry2M = ((UINT64)(UINTN)PageTableEntry1 |\r
- IA32_PG_P | IA32_PG_RW | AddressEncMask);\r
-}\r
-\r
-/**\r
- Set one page of page table pool memory to be read-only.\r
-\r
- @param[in] PageTableBase Base address of page table (CR3).\r
- @param[in] Address Start address of a page to be set as read-only.\r
- @param[in] Level4Paging Level 4 paging flag.\r
-\r
-**/\r
-STATIC\r
-VOID\r
-SetPageTablePoolReadOnly (\r
- IN UINTN PageTableBase,\r
- IN EFI_PHYSICAL_ADDRESS Address,\r
- IN BOOLEAN Level4Paging\r
- )\r
-{\r
- UINTN Index;\r
- UINTN EntryIndex;\r
- UINT64 AddressEncMask;\r
- EFI_PHYSICAL_ADDRESS PhysicalAddress;\r
- UINT64 *PageTable;\r
- UINT64 *NewPageTable;\r
- UINT64 PageAttr;\r
- UINT64 LevelSize[5];\r
- UINT64 LevelMask[5];\r
- UINTN LevelShift[5];\r
- UINTN Level;\r
- UINT64 PoolUnitSize;\r
-\r
- ASSERT (PageTableBase != 0);\r
-\r
- //\r
- // Since the page table is always from page table pool, which is always\r
- // located at the boundary of PcdPageTablePoolAlignment, we just need to\r
- // set the whole pool unit to be read-only.\r
- //\r
- Address = Address & PAGE_TABLE_POOL_ALIGN_MASK;\r
-\r
- LevelShift[1] = PAGING_L1_ADDRESS_SHIFT;\r
- LevelShift[2] = PAGING_L2_ADDRESS_SHIFT;\r
- LevelShift[3] = PAGING_L3_ADDRESS_SHIFT;\r
- LevelShift[4] = PAGING_L4_ADDRESS_SHIFT;\r
-\r
- LevelMask[1] = PAGING_4K_ADDRESS_MASK_64;\r
- LevelMask[2] = PAGING_2M_ADDRESS_MASK_64;\r
- LevelMask[3] = PAGING_1G_ADDRESS_MASK_64;\r
- LevelMask[4] = PAGING_1G_ADDRESS_MASK_64;\r
-\r
- LevelSize[1] = SIZE_4KB;\r
- LevelSize[2] = SIZE_2MB;\r
- LevelSize[3] = SIZE_1GB;\r
- LevelSize[4] = SIZE_512GB;\r
-\r
- AddressEncMask = GetMemEncryptionAddressMask() &\r
- PAGING_1G_ADDRESS_MASK_64;\r
- PageTable = (UINT64 *)(UINTN)PageTableBase;\r
- PoolUnitSize = PAGE_TABLE_POOL_UNIT_SIZE;\r
-\r
- for (Level = (Level4Paging) ? 4 : 3; Level > 0; --Level) {\r
- Index = ((UINTN)RShiftU64 (Address, LevelShift[Level]));\r
- Index &= PAGING_PAE_INDEX_MASK;\r
-\r
- PageAttr = PageTable[Index];\r
- if ((PageAttr & IA32_PG_PS) == 0) {\r
- //\r
- // Go to next level of table.\r
- //\r
- PageTable = (UINT64 *)(UINTN)(PageAttr & ~AddressEncMask &\r
- PAGING_4K_ADDRESS_MASK_64);\r
- continue;\r
- }\r
-\r
- if (PoolUnitSize >= LevelSize[Level]) {\r
- //\r
- // Clear R/W bit if current page granularity is not larger than pool unit\r
- // size.\r
- //\r
- if ((PageAttr & IA32_PG_RW) != 0) {\r
- while (PoolUnitSize > 0) {\r
- //\r
- // PAGE_TABLE_POOL_UNIT_SIZE and PAGE_TABLE_POOL_ALIGNMENT are fit in\r
- // one page (2MB). Then we don't need to update attributes for pages\r
- // crossing page directory. ASSERT below is for that purpose.\r
- //\r
- ASSERT (Index < EFI_PAGE_SIZE/sizeof (UINT64));\r
-\r
- PageTable[Index] &= ~(UINT64)IA32_PG_RW;\r
- PoolUnitSize -= LevelSize[Level];\r
-\r
- ++Index;\r
- }\r
- }\r
-\r
- break;\r
-\r
- } else {\r
- //\r
- // The smaller granularity of page must be needed.\r
- //\r
- ASSERT (Level > 1);\r
-\r
- NewPageTable = AllocatePageTableMemory (1);\r
- ASSERT (NewPageTable != NULL);\r
-\r
- PhysicalAddress = PageAttr & LevelMask[Level];\r
- for (EntryIndex = 0;\r
- EntryIndex < EFI_PAGE_SIZE/sizeof (UINT64);\r
- ++EntryIndex) {\r
- NewPageTable[EntryIndex] = PhysicalAddress | AddressEncMask |\r
- IA32_PG_P | IA32_PG_RW;\r
- if (Level > 2) {\r
- NewPageTable[EntryIndex] |= IA32_PG_PS;\r
- }\r
- PhysicalAddress += LevelSize[Level - 1];\r
- }\r
-\r
- PageTable[Index] = (UINT64)(UINTN)NewPageTable | AddressEncMask |\r
- IA32_PG_P | IA32_PG_RW;\r
- PageTable = NewPageTable;\r