mirror of https://github.com/acidanthera/audk.git
304 lines
12 KiB
C
304 lines
12 KiB
C
/** @file
|
|
* File managing the MMU for ARMv7 architecture
|
|
*
|
|
* Copyright (c) 2011-2013, 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 <Uefi.h>
|
|
#include <Chipset/ArmV7.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/ArmLib.h>
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include "ArmV7Lib.h"
|
|
#include "ArmLibPrivate.h"
|
|
|
|
UINT32
|
|
ConvertSectionAttributesToPageAttributes (
|
|
IN UINT32 SectionAttributes,
|
|
IN BOOLEAN IsLargePage
|
|
)
|
|
{
|
|
UINT32 PageAttributes;
|
|
|
|
PageAttributes = 0;
|
|
PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY (SectionAttributes, IsLargePage);
|
|
PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_AP (SectionAttributes);
|
|
PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_XN (SectionAttributes, IsLargePage);
|
|
PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_NG (SectionAttributes);
|
|
PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_S (SectionAttributes);
|
|
|
|
return PageAttributes;
|
|
}
|
|
|
|
STATIC
|
|
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_NONSECURE_WRITE_BACK:
|
|
PageAttributes = TT_DESCRIPTOR_PAGE_WRITE_BACK;
|
|
break;
|
|
case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:
|
|
case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:
|
|
PageAttributes = TT_DESCRIPTOR_PAGE_WRITE_THROUGH;
|
|
break;
|
|
case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:
|
|
case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:
|
|
PageAttributes = TT_DESCRIPTOR_PAGE_DEVICE;
|
|
break;
|
|
case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:
|
|
case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_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 | ConvertSectionAttributesToPageAttributes (*SectionEntry, FALSE);
|
|
|
|
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;
|
|
}
|
|
|
|
}
|
|
|
|
STATIC
|
|
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_NONSECURE_WRITE_BACK:
|
|
Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(1);
|
|
break;
|
|
case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:
|
|
Attributes = TT_DESCRIPTOR_SECTION_WRITE_THROUGH(1);
|
|
break;
|
|
case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:
|
|
Attributes = TT_DESCRIPTOR_SECTION_DEVICE(1);
|
|
break;
|
|
case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_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;
|
|
}
|
|
}
|
|
|
|
RETURN_STATUS
|
|
EFIAPI
|
|
ArmConfigureMmu (
|
|
IN ARM_MEMORY_REGION_DESCRIPTOR *MemoryTable,
|
|
OUT VOID **TranslationTableBase OPTIONAL,
|
|
OUT UINTN *TranslationTableSize OPTIONAL
|
|
)
|
|
{
|
|
VOID* TranslationTable;
|
|
ARM_MEMORY_REGION_ATTRIBUTES TranslationTableAttribute;
|
|
UINT32 TTBRAttributes;
|
|
|
|
// Allocate pages for translation table.
|
|
TranslationTable = AllocatePages (EFI_SIZE_TO_PAGES (TRANSLATION_TABLE_SECTION_SIZE + TRANSLATION_TABLE_SECTION_ALIGNMENT));
|
|
if (TranslationTable == NULL) {
|
|
return RETURN_OUT_OF_RESOURCES;
|
|
}
|
|
TranslationTable = (VOID*)(((UINTN)TranslationTable + TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK) & ~TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK);
|
|
|
|
if (TranslationTableBase != NULL) {
|
|
*TranslationTableBase = TranslationTable;
|
|
}
|
|
|
|
if (TranslationTableSize != NULL) {
|
|
*TranslationTableSize = TRANSLATION_TABLE_SECTION_SIZE;
|
|
}
|
|
|
|
ZeroMem (TranslationTable, TRANSLATION_TABLE_SECTION_SIZE);
|
|
|
|
// By default, mark the translation table as belonging to a uncached region
|
|
TranslationTableAttribute = ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED;
|
|
while (MemoryTable->Length != 0) {
|
|
// Find the memory attribute for the Translation Table
|
|
if (((UINTN)TranslationTable >= MemoryTable->PhysicalBase) && ((UINTN)TranslationTable <= MemoryTable->PhysicalBase - 1 + MemoryTable->Length)) {
|
|
TranslationTableAttribute = MemoryTable->Attributes;
|
|
}
|
|
|
|
FillTranslationTable (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_NONSECURE_UNCACHED_UNBUFFERED)) {
|
|
TTBRAttributes = TTBR_NON_CACHEABLE;
|
|
} else if ((TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK) ||
|
|
(TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK)) {
|
|
TTBRAttributes = TTBR_WRITE_BACK_ALLOC;
|
|
} else if ((TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH) ||
|
|
(TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH)) {
|
|
TTBRAttributes = TTBR_WRITE_THROUGH_NO_ALLOC;
|
|
} else {
|
|
ASSERT (0); // No support has been found for the attributes of the memory region that the translation table belongs to.
|
|
return RETURN_UNSUPPORTED;
|
|
}
|
|
|
|
ArmCleanInvalidateDataCache ();
|
|
ArmInvalidateInstructionCache ();
|
|
|
|
ArmDisableDataCache ();
|
|
ArmDisableInstructionCache();
|
|
// TLBs are also invalidated when calling ArmDisableMmu()
|
|
ArmDisableMmu ();
|
|
|
|
// Make sure nothing sneaked into the cache
|
|
ArmCleanInvalidateDataCache ();
|
|
ArmInvalidateInstructionCache ();
|
|
|
|
ArmSetTTBR0 ((VOID *)(UINTN)(((UINTN)TranslationTable & ~TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK) | (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();
|
|
return RETURN_SUCCESS;
|
|
}
|