/** @file * File managing the MMU for ARMv7 architecture * * Copyright (c) 2011, ARM Limited. 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. * **/ #include #include #include #include #include #include #include #include "ArmV7Lib.h" #include "ArmLibPrivate.h" VOID PopulateLevel2PageTable ( IN UINT32 *SectionEntry, IN UINT32 PhysicalBase, IN UINT32 RemainLength, IN ARM_MEMORY_REGION_ATTRIBUTES Attributes ) { UINT32* PageEntry; UINT32 Pages; UINT32 Index; UINT32 PageAttributes; UINT32 SectionDescriptor; UINT32 TranslationTable; UINT32 BaseSectionAddress; switch (Attributes) { case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK: case ARM_MEMORY_REGION_ATTRIBUTE_SECURE_WRITE_BACK: PageAttributes = TT_DESCRIPTOR_PAGE_WRITE_BACK; break; case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH: case ARM_MEMORY_REGION_ATTRIBUTE_SECURE_WRITE_THROUGH: PageAttributes = TT_DESCRIPTOR_PAGE_WRITE_THROUGH; break; case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE: case ARM_MEMORY_REGION_ATTRIBUTE_SECURE_DEVICE: PageAttributes = TT_DESCRIPTOR_PAGE_DEVICE; break; case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED: case ARM_MEMORY_REGION_ATTRIBUTE_SECURE_UNCACHED_UNBUFFERED: PageAttributes = TT_DESCRIPTOR_PAGE_UNCACHED; break; default: PageAttributes = TT_DESCRIPTOR_PAGE_UNCACHED; break; } // Check if the Section Entry has already been populated. Otherwise attach a // Level 2 Translation Table to it if (*SectionEntry != 0) { // The entry must be a page table. Otherwise it exists an overlapping in the memory map if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(*SectionEntry)) { TranslationTable = *SectionEntry & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK; } else if ((*SectionEntry & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) { // Case where a virtual memory map descriptor overlapped a section entry // Allocate a Level2 Page Table for this Section TranslationTable = (UINTN)AllocatePages(EFI_SIZE_TO_PAGES(TRANSLATION_TABLE_PAGE_SIZE + TRANSLATION_TABLE_PAGE_ALIGNMENT)); TranslationTable = ((UINTN)TranslationTable + TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK) & ~TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK; // Translate the Section Descriptor into Page Descriptor SectionDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE; SectionDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(*SectionEntry,0); SectionDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_AP(*SectionEntry); SectionDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_XN(*SectionEntry,0); SectionDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_NG(*SectionEntry); SectionDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_S(*SectionEntry); BaseSectionAddress = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(*SectionEntry); // Populate the new Level2 Page Table for the section PageEntry = (UINT32*)TranslationTable; for (Index = 0; Index < TRANSLATION_TABLE_PAGE_COUNT; Index++) { PageEntry[Index] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseSectionAddress + (Index << 12)) | SectionDescriptor; } // Overwrite the section entry to point to the new Level2 Translation Table *SectionEntry = (TranslationTable & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) | (IS_ARM_MEMORY_REGION_ATTRIBUTES_SECURE(Attributes) ? (1 << 3) : 0) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE; } else { // We do not support the other section type (16MB Section) ASSERT(0); return; } } else { TranslationTable = (UINTN)AllocatePages(EFI_SIZE_TO_PAGES(TRANSLATION_TABLE_PAGE_SIZE + TRANSLATION_TABLE_PAGE_ALIGNMENT)); TranslationTable = ((UINTN)TranslationTable + TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK) & ~TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK; ZeroMem ((VOID *)TranslationTable, TRANSLATION_TABLE_PAGE_SIZE); *SectionEntry = (TranslationTable & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) | (IS_ARM_MEMORY_REGION_ATTRIBUTES_SECURE(Attributes) ? (1 << 3) : 0) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE; } PageEntry = ((UINT32 *)(TranslationTable) + ((PhysicalBase & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT)); Pages = RemainLength / TT_DESCRIPTOR_PAGE_SIZE; for (Index = 0; Index < Pages; Index++) { *PageEntry++ = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(PhysicalBase) | PageAttributes; PhysicalBase += TT_DESCRIPTOR_PAGE_SIZE; } } VOID FillTranslationTable ( IN UINT32 *TranslationTable, IN ARM_MEMORY_REGION_DESCRIPTOR *MemoryRegion ) { UINT32 *SectionEntry; UINT32 Attributes; UINT32 PhysicalBase = MemoryRegion->PhysicalBase; UINT32 RemainLength = MemoryRegion->Length; ASSERT(MemoryRegion->Length > 0); switch (MemoryRegion->Attributes) { case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK: Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(0); break; case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH: Attributes = TT_DESCRIPTOR_SECTION_WRITE_THROUGH(0); break; case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE: Attributes = TT_DESCRIPTOR_SECTION_DEVICE(0); break; case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED: Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(0); break; case ARM_MEMORY_REGION_ATTRIBUTE_SECURE_WRITE_BACK: Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(1); break; case ARM_MEMORY_REGION_ATTRIBUTE_SECURE_WRITE_THROUGH: Attributes = TT_DESCRIPTOR_SECTION_WRITE_THROUGH(1); break; case ARM_MEMORY_REGION_ATTRIBUTE_SECURE_DEVICE: Attributes = TT_DESCRIPTOR_SECTION_DEVICE(1); break; case ARM_MEMORY_REGION_ATTRIBUTE_SECURE_UNCACHED_UNBUFFERED: Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(1); break; default: Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(0); break; } // Get the first section entry for this mapping SectionEntry = TRANSLATION_TABLE_ENTRY_FOR_VIRTUAL_ADDRESS(TranslationTable, MemoryRegion->VirtualBase); while (RemainLength != 0) { if (PhysicalBase % TT_DESCRIPTOR_SECTION_SIZE == 0) { if (RemainLength >= TT_DESCRIPTOR_SECTION_SIZE) { // Case: Physical address aligned on the Section Size (1MB) && the length is greater than the Section Size *SectionEntry++ = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(PhysicalBase) | Attributes; PhysicalBase += TT_DESCRIPTOR_SECTION_SIZE; } else { // Case: Physical address aligned on the Section Size (1MB) && the length does not fill a section PopulateLevel2PageTable(SectionEntry++,PhysicalBase,RemainLength,MemoryRegion->Attributes); // It must be the last entry break; } } else { // Case: Physical address NOT aligned on the Section Size (1MB) PopulateLevel2PageTable(SectionEntry++,PhysicalBase,RemainLength,MemoryRegion->Attributes); // Aligned the address PhysicalBase = (PhysicalBase + TT_DESCRIPTOR_SECTION_SIZE) & ~(TT_DESCRIPTOR_SECTION_SIZE-1); // If it is the last entry if (RemainLength < TT_DESCRIPTOR_SECTION_SIZE) { break; } } RemainLength -= TT_DESCRIPTOR_SECTION_SIZE; } } VOID EFIAPI ArmConfigureMmu ( IN ARM_MEMORY_REGION_DESCRIPTOR *MemoryTable, OUT VOID **TranslationTableBase OPTIONAL, OUT UINTN *TranslationTableSize OPTIONAL ) { UINTN TranslationTable; ARM_MEMORY_REGION_ATTRIBUTES TranslationTableAttribute; UINT32 TTBRAttributes; // Allocate pages for translation table. TranslationTable = (UINTN)AllocatePages(EFI_SIZE_TO_PAGES(TRANSLATION_TABLE_SECTION_SIZE + TRANSLATION_TABLE_SECTION_ALIGNMENT)); TranslationTable = ((UINTN)TranslationTable + TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK) & ~TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK; if (TranslationTableBase != NULL) { *TranslationTableBase = (VOID *)TranslationTable; } if (TranslationTableBase != NULL) { *TranslationTableSize = TRANSLATION_TABLE_SECTION_SIZE; } ZeroMem ((VOID *)TranslationTable, TRANSLATION_TABLE_SECTION_SIZE); ArmCleanInvalidateDataCache(); ArmInvalidateInstructionCache(); ArmInvalidateTlb(); ArmDisableDataCache(); ArmDisableInstructionCache(); ArmDisableMmu(); // Make sure nothing sneaked into the cache ArmCleanInvalidateDataCache(); ArmInvalidateInstructionCache(); TranslationTableAttribute = (ARM_MEMORY_REGION_ATTRIBUTES)0; while (MemoryTable->Length != 0) { // Find the memory attribute for the Translation Table if ((TranslationTable >= MemoryTable->PhysicalBase) && (TranslationTable < MemoryTable->PhysicalBase + MemoryTable->Length)) { TranslationTableAttribute = MemoryTable->Attributes; } FillTranslationTable ((VOID *)TranslationTable, MemoryTable); MemoryTable++; } // Translate the Memory Attributes into Translation Table Register Attributes if ((TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED) || (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_SECURE_UNCACHED_UNBUFFERED)) { TTBRAttributes = TTBR_NON_CACHEABLE; } else if ((TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK) || (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_SECURE_WRITE_BACK)) { TTBRAttributes = TTBR_WRITE_BACK_ALLOC; } else if ((TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH) || (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_SECURE_WRITE_THROUGH)) { TTBRAttributes = TTBR_WRITE_THROUGH_NO_ALLOC; } else { //TODO: We should raise an error here TTBRAttributes = TTBR_NON_CACHEABLE; } ArmSetTTBR0 ((VOID *)(UINTN)((TranslationTable & 0xFFFFC000) | (TTBRAttributes & 0x7F))); ArmSetDomainAccessControl (DOMAIN_ACCESS_CONTROL_NONE(15) | DOMAIN_ACCESS_CONTROL_NONE(14) | DOMAIN_ACCESS_CONTROL_NONE(13) | DOMAIN_ACCESS_CONTROL_NONE(12) | DOMAIN_ACCESS_CONTROL_NONE(11) | DOMAIN_ACCESS_CONTROL_NONE(10) | DOMAIN_ACCESS_CONTROL_NONE( 9) | DOMAIN_ACCESS_CONTROL_NONE( 8) | DOMAIN_ACCESS_CONTROL_NONE( 7) | DOMAIN_ACCESS_CONTROL_NONE( 6) | DOMAIN_ACCESS_CONTROL_NONE( 5) | DOMAIN_ACCESS_CONTROL_NONE( 4) | DOMAIN_ACCESS_CONTROL_NONE( 3) | DOMAIN_ACCESS_CONTROL_NONE( 2) | DOMAIN_ACCESS_CONTROL_NONE( 1) | DOMAIN_ACCESS_CONTROL_MANAGER(0)); ArmEnableInstructionCache(); ArmEnableDataCache(); ArmEnableMmu(); }