286 lines
8.9 KiB
C
286 lines
8.9 KiB
C
/*
|
|
* Copyright (c) 2008-2012, Freescale Semiconductor, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
*
|
|
* o Redistributions of source code must retain the above copyright notice, this list
|
|
* of conditions and the following disclaimer.
|
|
*
|
|
* o Redistributions in binary form must reproduce the above copyright notice, this
|
|
* list of conditions and the following disclaimer in the documentation and/or
|
|
* other materials provided with the distribution.
|
|
*
|
|
* o Neither the name of Freescale Semiconductor, Inc. nor the names of its
|
|
* contributors may be used to endorse or promote products derived from this
|
|
* software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*!
|
|
* @file mmu.c
|
|
* @brief System memory arangement.
|
|
*/
|
|
#include "cortex_a.h"
|
|
#include "mmu.h"
|
|
#include "arm_cp_registers.h"
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Definitions
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//! @brief Size in bytes of the first-level page table.
|
|
#define MMU_L1_PAGE_TABLE_SIZE (16 * 1024)
|
|
|
|
//! @brief First-level 1MB section descriptor entry.
|
|
typedef union mmu_l1_section {
|
|
uint32_t u;
|
|
struct {
|
|
uint32_t id:2; //!< ID
|
|
uint32_t b:1; //!< Bufferable
|
|
uint32_t c:1; //!< Cacheable
|
|
uint32_t xn:1; //!< Execute-not
|
|
uint32_t domain:4; //!< Domain
|
|
uint32_t _impl_defined:1; //!< Implementation defined, should be zero.
|
|
uint32_t ap1_0:2; //!< Access permissions AP[1:0]
|
|
uint32_t tex:3; //!< TEX remap
|
|
uint32_t ap2:1; //!< Access permissions AP[2]
|
|
uint32_t s:1; //!< Shareable
|
|
uint32_t ng:1; //!< Not-global
|
|
uint32_t _zero:1; //!< Should be zero.
|
|
uint32_t ns:1; //!< Non-secure
|
|
uint32_t address:12; //!< Physical base address
|
|
};
|
|
} mmu_l1_section_t;
|
|
|
|
enum {
|
|
kMMU_L1_Section_ID = 2, //!< ID value for a 1MB section first-level entry.
|
|
kMMU_L1_Section_Address_Shift = 20 //!< Bit offset of the physical base address field.
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Externs
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
extern char __l1_page_table_start;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Code
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void mmu_enable()
|
|
{
|
|
// invalidate all tlb
|
|
arm_unified_tlb_invalidate();
|
|
|
|
// read SCTLR
|
|
uint32_t sctlr;
|
|
_ARM_MRC(15, 0, sctlr, 1, 0, 0);
|
|
|
|
// set MMU enable bit
|
|
sctlr |= BM_SCTLR_M;
|
|
|
|
// write modified SCTLR
|
|
_ARM_MCR(15, 0, sctlr, 1, 0, 0);
|
|
}
|
|
|
|
void mmu_disable()
|
|
{
|
|
// read current SCTLR
|
|
uint32_t sctlr;
|
|
_ARM_MRC(15, 0, sctlr, 1, 0, 0);
|
|
|
|
// clear MMU enable bit
|
|
sctlr &=~ BM_SCTLR_M;
|
|
|
|
// write modified SCTLR
|
|
_ARM_MCR(15, 0, sctlr, 1, 0, 0);
|
|
}
|
|
|
|
void mmu_init()
|
|
{
|
|
// Get the L1 page table base address.
|
|
uint32_t * table = (uint32_t *)&__l1_page_table_start;
|
|
uint32_t share_attr = kShareable;
|
|
|
|
// write table address to TTBR0
|
|
_ARM_MCR(15, 0, table, 2, 0, 0);
|
|
|
|
// set Client mode for all Domains
|
|
uint32_t dacr = 0x55555555;
|
|
_ARM_MCR(15, 0, dacr, 3, 0, 0); // MCR p15, 0, <Rd>, c3, c0, 0 ; Write DACR
|
|
|
|
// Clear the L1 table.
|
|
bzero(table, MMU_L1_PAGE_TABLE_SIZE);
|
|
|
|
// Create default mappings.
|
|
mmu_map_l1_range(0x00000000, 0x00000000, 0x00900000, kStronglyOrdered, kShareable, kRWAccess); // ROM and peripherals
|
|
mmu_map_l1_range(0x00900000, 0x00900000, 0x00100000, kStronglyOrdered, kShareable, kRWAccess); // OCRAM
|
|
mmu_map_l1_range(0x00a00000, 0x00a00000, 0x0f600000, kStronglyOrdered, kShareable, kRWAccess); // More peripherals
|
|
|
|
// Check whether SMP is enabled. If it is not, then we don't want to make SDRAM shareable.
|
|
uint32_t actlr = 0x0;
|
|
_ARM_MRC(15, 0, actlr, 1, 0, 1);
|
|
if (actlr & BM_ACTLR_SMP)
|
|
{
|
|
share_attr = kShareable;
|
|
}
|
|
else
|
|
{
|
|
share_attr = kNonshareable;
|
|
}
|
|
|
|
#if defined(CHIP_MX6DQ) || defined(CHIP_MX6SDL)
|
|
mmu_map_l1_range(0x10000000, 0x10000000, 0x80000000, kOuterInner_WB_WA, share_attr, kRWAccess); // 2GB DDR
|
|
#elif defined(CHIP_MX6SL) || defined(CHIP_MX6UL)
|
|
mmu_map_l1_range(0x80000000, 0x80000000, 0x40000000, kOuterInner_WB_WA, share_attr, kRWAccess); // 1GB DDR
|
|
#else
|
|
#error Unknown chip type!
|
|
#endif
|
|
}
|
|
|
|
void mmu_map_l1_range(uint32_t pa, uint32_t va, uint32_t length, mmu_memory_type_t memoryType, mmu_shareability_t isShareable, mmu_access_t access)
|
|
{
|
|
register mmu_l1_section_t entry;
|
|
entry.u = 0;
|
|
|
|
// Set constant attributes.
|
|
entry.id = kMMU_L1_Section_ID;
|
|
entry.xn = 0; // Allow execution
|
|
entry.domain = 0; // Domain 0
|
|
entry.ng = 0; // Global
|
|
entry.ns = 0; // Secure
|
|
|
|
// Set attributes based on the selected memory type.
|
|
switch (memoryType)
|
|
{
|
|
case kStronglyOrdered:
|
|
entry.c = 0;
|
|
entry.b = 0;
|
|
entry.tex = 0;
|
|
entry.s = 1; // Ignored
|
|
break;
|
|
case kDevice:
|
|
if (isShareable)
|
|
{
|
|
entry.c = 0;
|
|
entry.b = 1;
|
|
entry.tex = 0;
|
|
entry.s = 1; // Ignored
|
|
}
|
|
else
|
|
{
|
|
entry.c = 0;
|
|
entry.b = 0;
|
|
entry.tex = 2;
|
|
entry.s = 0; // Ignored
|
|
}
|
|
break;
|
|
case kOuterInner_WB_WA:
|
|
entry.c = 1;
|
|
entry.b = 1;
|
|
entry.tex = 1;
|
|
entry.s = isShareable;
|
|
break;
|
|
case kOuterInner_WT:
|
|
entry.c = 1;
|
|
entry.b = 0;
|
|
entry.tex = 0;
|
|
entry.s = isShareable;
|
|
break;
|
|
case kNoncacheable:
|
|
entry.c = 0;
|
|
entry.b = 0;
|
|
entry.tex = 1;
|
|
entry.s = isShareable;
|
|
break;
|
|
}
|
|
|
|
// Set attributes from specified access mode.
|
|
switch (access)
|
|
{
|
|
case kNoAccess:
|
|
entry.ap2 = 0;
|
|
entry.ap1_0 = 0;
|
|
break;
|
|
case kROAccess:
|
|
entry.ap2 = 1;
|
|
entry.ap1_0 = 3;
|
|
break;
|
|
case kRWAccess:
|
|
entry.ap2 = 0;
|
|
entry.ap1_0 = 3;
|
|
break;
|
|
}
|
|
|
|
// Get the L1 page table base address.
|
|
uint32_t * table = (uint32_t *)&__l1_page_table_start;
|
|
|
|
// Convert addresses to 12-bit bases.
|
|
uint32_t vbase = va >> kMMU_L1_Section_Address_Shift;
|
|
uint32_t pbase = pa >> kMMU_L1_Section_Address_Shift;
|
|
uint32_t entries = length >> kMMU_L1_Section_Address_Shift;
|
|
|
|
// Fill in L1 page table entries.
|
|
for (; entries > 0; ++pbase, ++vbase, --entries)
|
|
{
|
|
entry.address = pbase;
|
|
table[vbase] = entry.u;
|
|
}
|
|
|
|
// Invalidate TLB
|
|
arm_unified_tlb_invalidate();
|
|
}
|
|
|
|
bool mmu_virtual_to_physical(uint32_t virtualAddress, uint32_t * physicalAddress)
|
|
{
|
|
uint32_t pa = 0;
|
|
|
|
// VA to PA translation with privileged read permission check
|
|
_ARM_MCR(15, 0, virtualAddress & 0xfffffc00, 7, 8, 0);
|
|
|
|
// Read PA register
|
|
_ARM_MRC(15, 0, pa, 7, 4, 0);
|
|
|
|
// First bit of returned value is Result of conversion (0 is successful translation)
|
|
if (pa & 1)
|
|
{
|
|
// We can try write permission also
|
|
// VA to PA translation with privileged write permission check
|
|
_ARM_MCR(15, 0, virtualAddress & 0xfffffc00, 7, 8, 1);
|
|
|
|
// Read PA register
|
|
_ARM_MRC(15, 0, pa, 7, 4, 0);
|
|
|
|
// First bit of returned value is Result of conversion (0 is successful translation)
|
|
if (pa & 1)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (physicalAddress)
|
|
{
|
|
// complete address returning base + offset
|
|
pa = (pa & 0xfffff000) | (virtualAddress & 0x00000fff);
|
|
*physicalAddress = pa;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// EOF
|
|
////////////////////////////////////////////////////////////////////////////////
|