GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
+120
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <asm_macros.S>
|
||||
#include <assert_macros.S>
|
||||
#include <lib/xlat_tables/xlat_tables_v2.h>
|
||||
|
||||
.global enable_mmu_direct_svc_mon
|
||||
.global enable_mmu_direct_hyp
|
||||
|
||||
/* void enable_mmu_direct_svc_mon(unsigned int flags) */
|
||||
func enable_mmu_direct_svc_mon
|
||||
/* Assert that MMU is turned off */
|
||||
#if ENABLE_ASSERTIONS
|
||||
ldcopr r1, SCTLR
|
||||
tst r1, #SCTLR_M_BIT
|
||||
ASM_ASSERT(eq)
|
||||
#endif
|
||||
|
||||
/* Invalidate TLB entries */
|
||||
TLB_INVALIDATE(r0, TLBIALL)
|
||||
|
||||
mov r3, r0
|
||||
ldr r0, =mmu_cfg_params
|
||||
|
||||
/* MAIR0. Only the lower 32 bits are used. */
|
||||
ldr r1, [r0, #(MMU_CFG_MAIR << 3)]
|
||||
stcopr r1, MAIR0
|
||||
|
||||
/* TTBCR. Only the lower 32 bits are used. */
|
||||
ldr r2, [r0, #(MMU_CFG_TCR << 3)]
|
||||
stcopr r2, TTBCR
|
||||
|
||||
/* TTBR0 */
|
||||
ldr r1, [r0, #(MMU_CFG_TTBR0 << 3)]
|
||||
ldr r2, [r0, #((MMU_CFG_TTBR0 << 3) + 4)]
|
||||
stcopr16 r1, r2, TTBR0_64
|
||||
|
||||
/* TTBR1 is unused right now; set it to 0. */
|
||||
mov r1, #0
|
||||
mov r2, #0
|
||||
stcopr16 r1, r2, TTBR1_64
|
||||
|
||||
/*
|
||||
* Ensure all translation table writes have drained into memory, the TLB
|
||||
* invalidation is complete, and translation register writes are
|
||||
* committed before enabling the MMU
|
||||
*/
|
||||
dsb ish
|
||||
isb
|
||||
|
||||
/* Enable enable MMU by honoring flags */
|
||||
ldcopr r1, SCTLR
|
||||
ldr r2, =(SCTLR_WXN_BIT | SCTLR_C_BIT | SCTLR_M_BIT)
|
||||
orr r1, r1, r2
|
||||
|
||||
/* Clear C bit if requested */
|
||||
tst r3, #DISABLE_DCACHE
|
||||
bicne r1, r1, #SCTLR_C_BIT
|
||||
|
||||
stcopr r1, SCTLR
|
||||
isb
|
||||
|
||||
bx lr
|
||||
endfunc enable_mmu_direct_svc_mon
|
||||
|
||||
|
||||
/* void enable_mmu_direct_hyp(unsigned int flags) */
|
||||
func enable_mmu_direct_hyp
|
||||
/* Assert that MMU is turned off */
|
||||
#if ENABLE_ASSERTIONS
|
||||
ldcopr r1, HSCTLR
|
||||
tst r1, #HSCTLR_M_BIT
|
||||
ASM_ASSERT(eq)
|
||||
#endif
|
||||
|
||||
/* Invalidate TLB entries */
|
||||
TLB_INVALIDATE(r0, TLBIALL)
|
||||
|
||||
mov r3, r0
|
||||
ldr r0, =mmu_cfg_params
|
||||
|
||||
/* HMAIR0 */
|
||||
ldr r1, [r0, #(MMU_CFG_MAIR << 3)]
|
||||
stcopr r1, HMAIR0
|
||||
|
||||
/* HTCR */
|
||||
ldr r2, [r0, #(MMU_CFG_TCR << 3)]
|
||||
stcopr r2, HTCR
|
||||
|
||||
/* HTTBR */
|
||||
ldr r1, [r0, #(MMU_CFG_TTBR0 << 3)]
|
||||
ldr r2, [r0, #((MMU_CFG_TTBR0 << 3) + 4)]
|
||||
stcopr16 r1, r2, HTTBR_64
|
||||
|
||||
/*
|
||||
* Ensure all translation table writes have drained into memory, the TLB
|
||||
* invalidation is complete, and translation register writes are
|
||||
* committed before enabling the MMU
|
||||
*/
|
||||
dsb ish
|
||||
isb
|
||||
|
||||
/* Enable enable MMU by honoring flags */
|
||||
ldcopr r1, HSCTLR
|
||||
ldr r2, =(HSCTLR_WXN_BIT | HSCTLR_C_BIT | HSCTLR_M_BIT)
|
||||
orr r1, r1, r2
|
||||
|
||||
/* Clear C bit if requested */
|
||||
tst r3, #DISABLE_DCACHE
|
||||
bicne r1, r1, #HSCTLR_C_BIT
|
||||
|
||||
stcopr r1, HSCTLR
|
||||
isb
|
||||
|
||||
bx lr
|
||||
endfunc enable_mmu_direct_hyp
|
||||
+260
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <platform_def.h>
|
||||
|
||||
#include <arch.h>
|
||||
#include <arch_features.h>
|
||||
#include <arch_helpers.h>
|
||||
#include <lib/cassert.h>
|
||||
#include <lib/utils_def.h>
|
||||
#include <lib/xlat_tables/xlat_tables_v2.h>
|
||||
|
||||
#include "../xlat_tables_private.h"
|
||||
|
||||
#if (ARM_ARCH_MAJOR == 7) && !defined(ARMV7_SUPPORTS_LARGE_PAGE_ADDRESSING)
|
||||
#error ARMv7 target does not support LPAE MMU descriptors
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Returns true if the provided granule size is supported, false otherwise.
|
||||
*/
|
||||
bool xlat_arch_is_granule_size_supported(size_t size)
|
||||
{
|
||||
/*
|
||||
* The library uses the long descriptor translation table format, which
|
||||
* supports 4 KiB pages only.
|
||||
*/
|
||||
return size == PAGE_SIZE_4KB;
|
||||
}
|
||||
|
||||
size_t xlat_arch_get_max_supported_granule_size(void)
|
||||
{
|
||||
return PAGE_SIZE_4KB;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine the physical address space encoded in the 'attr' parameter.
|
||||
*
|
||||
* The physical address will fall into one of two spaces; secure or
|
||||
* nonsecure.
|
||||
*/
|
||||
uint32_t xlat_arch_get_pas(uint32_t attr)
|
||||
{
|
||||
uint32_t pas = MT_PAS(attr);
|
||||
|
||||
if (pas == MT_NS) {
|
||||
return LOWER_ATTRS(NS);
|
||||
} else { /* MT_SECURE */
|
||||
return 0U;
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_ASSERTIONS
|
||||
unsigned long long xlat_arch_get_max_supported_pa(void)
|
||||
{
|
||||
/* Physical address space size for long descriptor format. */
|
||||
return (1ULL << 40) - 1ULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return minimum virtual address space size supported by the architecture
|
||||
*/
|
||||
uintptr_t xlat_get_min_virt_addr_space_size(void)
|
||||
{
|
||||
return MIN_VIRT_ADDR_SPACE_SIZE;
|
||||
}
|
||||
#endif /* ENABLE_ASSERTIONS*/
|
||||
|
||||
bool is_mmu_enabled_ctx(const xlat_ctx_t *ctx)
|
||||
{
|
||||
if (ctx->xlat_regime == EL1_EL0_REGIME) {
|
||||
assert(xlat_arch_current_el() == 1U);
|
||||
return (read_sctlr() & SCTLR_M_BIT) != 0U;
|
||||
} else {
|
||||
assert(ctx->xlat_regime == EL2_REGIME);
|
||||
assert(xlat_arch_current_el() == 2U);
|
||||
return (read_hsctlr() & HSCTLR_M_BIT) != 0U;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_dcache_enabled(void)
|
||||
{
|
||||
if (IS_IN_EL2()) {
|
||||
return (read_hsctlr() & HSCTLR_C_BIT) != 0U;
|
||||
} else {
|
||||
return (read_sctlr() & SCTLR_C_BIT) != 0U;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t xlat_arch_regime_get_xn_desc(int xlat_regime)
|
||||
{
|
||||
if (xlat_regime == EL1_EL0_REGIME) {
|
||||
return UPPER_ATTRS(XN) | UPPER_ATTRS(PXN);
|
||||
} else {
|
||||
assert(xlat_regime == EL2_REGIME);
|
||||
return UPPER_ATTRS(XN);
|
||||
}
|
||||
}
|
||||
|
||||
void xlat_arch_tlbi_va(uintptr_t va, int xlat_regime)
|
||||
{
|
||||
/*
|
||||
* Ensure the translation table write has drained into memory before
|
||||
* invalidating the TLB entry.
|
||||
*/
|
||||
dsbishst();
|
||||
|
||||
if (xlat_regime == EL1_EL0_REGIME) {
|
||||
tlbimvaais(TLBI_ADDR(va));
|
||||
} else {
|
||||
assert(xlat_regime == EL2_REGIME);
|
||||
tlbimvahis(TLBI_ADDR(va));
|
||||
}
|
||||
}
|
||||
|
||||
void xlat_arch_tlbi_va_sync(void)
|
||||
{
|
||||
/* Invalidate all entries from branch predictors. */
|
||||
bpiallis();
|
||||
|
||||
/*
|
||||
* A TLB maintenance instruction can complete at any time after
|
||||
* it is issued, but is only guaranteed to be complete after the
|
||||
* execution of DSB by the PE that executed the TLB maintenance
|
||||
* instruction. After the TLB invalidate instruction is
|
||||
* complete, no new memory accesses using the invalidated TLB
|
||||
* entries will be observed by any observer of the system
|
||||
* domain. See section D4.8.2 of the ARMv8 (issue k), paragraph
|
||||
* "Ordering and completion of TLB maintenance instructions".
|
||||
*/
|
||||
dsbish();
|
||||
|
||||
/*
|
||||
* The effects of a completed TLB maintenance instruction are
|
||||
* only guaranteed to be visible on the PE that executed the
|
||||
* instruction after the execution of an ISB instruction by the
|
||||
* PE that executed the TLB maintenance instruction.
|
||||
*/
|
||||
isb();
|
||||
}
|
||||
|
||||
unsigned int xlat_arch_current_el(void)
|
||||
{
|
||||
if (IS_IN_HYP()) {
|
||||
return 2U;
|
||||
} else {
|
||||
assert(IS_IN_SVC() || IS_IN_MON());
|
||||
/*
|
||||
* If EL3 is in AArch32 mode, all secure PL1 modes (Monitor,
|
||||
* System, SVC, Abort, UND, IRQ and FIQ modes) execute at EL3.
|
||||
*
|
||||
* The PL1&0 translation regime in AArch32 behaves like the
|
||||
* EL1&0 regime in AArch64 except for the XN bits, but we set
|
||||
* and unset them at the same time, so there's no difference in
|
||||
* practice.
|
||||
*/
|
||||
return 1U;
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function for enabling the MMU in PL1 or PL2, assuming that the page tables
|
||||
* have already been created.
|
||||
******************************************************************************/
|
||||
void setup_mmu_cfg(uint64_t *params, unsigned int flags,
|
||||
const uint64_t *base_table, unsigned long long max_pa,
|
||||
uintptr_t max_va, __unused int xlat_regime)
|
||||
{
|
||||
uint64_t mair, ttbr0;
|
||||
uint32_t ttbcr;
|
||||
|
||||
/* Set attributes in the right indices of the MAIR */
|
||||
mair = MAIR0_ATTR_SET(ATTR_DEVICE, ATTR_DEVICE_INDEX);
|
||||
mair |= MAIR0_ATTR_SET(ATTR_IWBWA_OWBWA_NTR,
|
||||
ATTR_IWBWA_OWBWA_NTR_INDEX);
|
||||
mair |= MAIR0_ATTR_SET(ATTR_NON_CACHEABLE,
|
||||
ATTR_NON_CACHEABLE_INDEX);
|
||||
|
||||
/*
|
||||
* Configure the control register for stage 1 of the PL1&0 or EL2
|
||||
* translation regimes.
|
||||
*/
|
||||
|
||||
/* Use the Long-descriptor translation table format. */
|
||||
ttbcr = TTBCR_EAE_BIT;
|
||||
|
||||
if (xlat_regime == EL1_EL0_REGIME) {
|
||||
assert(IS_IN_SVC() || IS_IN_MON());
|
||||
/*
|
||||
* Disable translation table walk for addresses that are
|
||||
* translated using TTBR1. Therefore, only TTBR0 is used.
|
||||
*/
|
||||
ttbcr |= TTBCR_EPD1_BIT;
|
||||
} else {
|
||||
assert(xlat_regime == EL2_REGIME);
|
||||
assert(IS_IN_HYP());
|
||||
|
||||
/*
|
||||
* Set HTCR bits as well. Set HTTBR table properties
|
||||
* as Inner & outer WBWA & shareable.
|
||||
*/
|
||||
ttbcr |= HTCR_RES1 |
|
||||
HTCR_SH0_INNER_SHAREABLE | HTCR_RGN0_OUTER_WBA |
|
||||
HTCR_RGN0_INNER_WBA;
|
||||
}
|
||||
|
||||
/*
|
||||
* Limit the input address ranges and memory region sizes translated
|
||||
* using TTBR0 to the given virtual address space size, if smaller than
|
||||
* 32 bits.
|
||||
*/
|
||||
if (max_va != UINT32_MAX) {
|
||||
uintptr_t virtual_addr_space_size = max_va + 1U;
|
||||
|
||||
assert(virtual_addr_space_size >=
|
||||
xlat_get_min_virt_addr_space_size());
|
||||
assert(IS_POWER_OF_TWO(virtual_addr_space_size));
|
||||
|
||||
/*
|
||||
* __builtin_ctzll(0) is undefined but here we are guaranteed
|
||||
* that virtual_addr_space_size is in the range [1, UINT32_MAX].
|
||||
*/
|
||||
int t0sz = 32 - __builtin_ctzll(virtual_addr_space_size);
|
||||
|
||||
ttbcr |= (uint32_t) t0sz;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the cacheability and shareability attributes for memory
|
||||
* associated with translation table walks using TTBR0.
|
||||
*/
|
||||
if ((flags & XLAT_TABLE_NC) != 0U) {
|
||||
/* Inner & outer non-cacheable non-shareable. */
|
||||
ttbcr |= TTBCR_SH0_NON_SHAREABLE | TTBCR_RGN0_OUTER_NC |
|
||||
TTBCR_RGN0_INNER_NC;
|
||||
} else {
|
||||
/* Inner & outer WBWA & shareable. */
|
||||
ttbcr |= TTBCR_SH0_INNER_SHAREABLE | TTBCR_RGN0_OUTER_WBA |
|
||||
TTBCR_RGN0_INNER_WBA;
|
||||
}
|
||||
|
||||
/* Set TTBR0 bits as well */
|
||||
ttbr0 = (uint64_t)(uintptr_t) base_table;
|
||||
|
||||
if (is_armv8_2_ttcnp_present()) {
|
||||
/* Enable CnP bit so as to share page tables with all PEs. */
|
||||
ttbr0 |= TTBR_CNP_BIT;
|
||||
}
|
||||
|
||||
/* Now populate MMU configuration */
|
||||
params[MMU_CFG_MAIR] = mair;
|
||||
params[MMU_CFG_TCR] = (uint64_t) ttbcr;
|
||||
params[MMU_CFG_TTBR0] = ttbr0;
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <asm_macros.S>
|
||||
#include <assert_macros.S>
|
||||
#include <lib/xlat_tables/xlat_tables_v2.h>
|
||||
|
||||
.global enable_mmu_direct_el1
|
||||
.global enable_mmu_direct_el2
|
||||
.global enable_mmu_direct_el3
|
||||
|
||||
/* Macros to read and write to system register for a given EL. */
|
||||
.macro _msr reg_name, el, gp_reg
|
||||
msr \reg_name\()_el\()\el, \gp_reg
|
||||
.endm
|
||||
|
||||
.macro _mrs gp_reg, reg_name, el
|
||||
mrs \gp_reg, \reg_name\()_el\()\el
|
||||
.endm
|
||||
|
||||
.macro tlbi_invalidate_all el
|
||||
.if \el == 1
|
||||
TLB_INVALIDATE(vmalle1)
|
||||
.elseif \el == 2
|
||||
TLB_INVALIDATE(alle2)
|
||||
.elseif \el == 3
|
||||
TLB_INVALIDATE(alle3)
|
||||
.else
|
||||
.error "EL must be 1, 2 or 3"
|
||||
.endif
|
||||
.endm
|
||||
|
||||
/* void enable_mmu_direct_el<x>(unsigned int flags) */
|
||||
.macro define_mmu_enable_func el
|
||||
func enable_mmu_direct_\()el\el
|
||||
#if ENABLE_ASSERTIONS
|
||||
_mrs x1, sctlr, \el
|
||||
tst x1, #SCTLR_M_BIT
|
||||
ASM_ASSERT(eq)
|
||||
#endif
|
||||
/* Invalidate all TLB entries */
|
||||
tlbi_invalidate_all \el
|
||||
|
||||
mov x7, x0
|
||||
adrp x0, mmu_cfg_params
|
||||
add x0, x0, :lo12:mmu_cfg_params
|
||||
|
||||
/* MAIR */
|
||||
ldr x1, [x0, #(MMU_CFG_MAIR << 3)]
|
||||
_msr mair, \el, x1
|
||||
|
||||
/* TCR */
|
||||
ldr x2, [x0, #(MMU_CFG_TCR << 3)]
|
||||
_msr tcr, \el, x2
|
||||
|
||||
/* TTBR */
|
||||
ldr x3, [x0, #(MMU_CFG_TTBR0 << 3)]
|
||||
_msr ttbr0, \el, x3
|
||||
|
||||
/*
|
||||
* Ensure all translation table writes have drained into memory, the TLB
|
||||
* invalidation is complete, and translation register writes are
|
||||
* committed before enabling the MMU
|
||||
*/
|
||||
dsb ish
|
||||
isb
|
||||
|
||||
/* Set and clear required fields of SCTLR */
|
||||
_mrs x4, sctlr, \el
|
||||
mov_imm x5, SCTLR_WXN_BIT | SCTLR_C_BIT | SCTLR_M_BIT
|
||||
orr x4, x4, x5
|
||||
|
||||
/* Additionally, amend SCTLR fields based on flags */
|
||||
bic x5, x4, #SCTLR_C_BIT
|
||||
tst x7, #DISABLE_DCACHE
|
||||
csel x4, x5, x4, ne
|
||||
|
||||
_msr sctlr, \el, x4
|
||||
isb
|
||||
|
||||
ret
|
||||
endfunc enable_mmu_direct_\()el\el
|
||||
.endm
|
||||
|
||||
/*
|
||||
* Define MMU-enabling functions for EL1, EL2 and EL3:
|
||||
*
|
||||
* enable_mmu_direct_el1
|
||||
* enable_mmu_direct_el2
|
||||
* enable_mmu_direct_el3
|
||||
*/
|
||||
define_mmu_enable_func 1
|
||||
define_mmu_enable_func 2
|
||||
define_mmu_enable_func 3
|
||||
+324
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2021, Arm Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <arch.h>
|
||||
#include <arch_features.h>
|
||||
#include <arch_helpers.h>
|
||||
#include <lib/cassert.h>
|
||||
#include <lib/utils_def.h>
|
||||
#include <lib/xlat_tables/xlat_tables_v2.h>
|
||||
|
||||
#include "../xlat_tables_private.h"
|
||||
|
||||
/*
|
||||
* Returns true if the provided granule size is supported, false otherwise.
|
||||
*/
|
||||
bool xlat_arch_is_granule_size_supported(size_t size)
|
||||
{
|
||||
u_register_t id_aa64mmfr0_el1 = read_id_aa64mmfr0_el1();
|
||||
|
||||
if (size == PAGE_SIZE_4KB) {
|
||||
return ((id_aa64mmfr0_el1 >> ID_AA64MMFR0_EL1_TGRAN4_SHIFT) &
|
||||
ID_AA64MMFR0_EL1_TGRAN4_MASK) ==
|
||||
ID_AA64MMFR0_EL1_TGRAN4_SUPPORTED;
|
||||
} else if (size == PAGE_SIZE_16KB) {
|
||||
return ((id_aa64mmfr0_el1 >> ID_AA64MMFR0_EL1_TGRAN16_SHIFT) &
|
||||
ID_AA64MMFR0_EL1_TGRAN16_MASK) ==
|
||||
ID_AA64MMFR0_EL1_TGRAN16_SUPPORTED;
|
||||
} else if (size == PAGE_SIZE_64KB) {
|
||||
return ((id_aa64mmfr0_el1 >> ID_AA64MMFR0_EL1_TGRAN64_SHIFT) &
|
||||
ID_AA64MMFR0_EL1_TGRAN64_MASK) ==
|
||||
ID_AA64MMFR0_EL1_TGRAN64_SUPPORTED;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t xlat_arch_get_max_supported_granule_size(void)
|
||||
{
|
||||
if (xlat_arch_is_granule_size_supported(PAGE_SIZE_64KB)) {
|
||||
return PAGE_SIZE_64KB;
|
||||
} else if (xlat_arch_is_granule_size_supported(PAGE_SIZE_16KB)) {
|
||||
return PAGE_SIZE_16KB;
|
||||
} else {
|
||||
assert(xlat_arch_is_granule_size_supported(PAGE_SIZE_4KB));
|
||||
return PAGE_SIZE_4KB;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine the physical address space encoded in the 'attr' parameter.
|
||||
*
|
||||
* The physical address will fall into one of four spaces; secure,
|
||||
* nonsecure, root, or realm if RME is enabled, or one of two spaces;
|
||||
* secure and nonsecure otherwise.
|
||||
*/
|
||||
uint32_t xlat_arch_get_pas(uint32_t attr)
|
||||
{
|
||||
uint32_t pas = MT_PAS(attr);
|
||||
|
||||
switch (pas) {
|
||||
#if ENABLE_RME
|
||||
/* TTD.NSE = 1 and TTD.NS = 1 for Realm PAS */
|
||||
case MT_REALM:
|
||||
return LOWER_ATTRS(EL3_S1_NSE | NS);
|
||||
/* TTD.NSE = 1 and TTD.NS = 0 for Root PAS */
|
||||
case MT_ROOT:
|
||||
return LOWER_ATTRS(EL3_S1_NSE);
|
||||
#endif
|
||||
case MT_NS:
|
||||
return LOWER_ATTRS(NS);
|
||||
default: /* MT_SECURE */
|
||||
return 0U;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long long tcr_physical_addr_size_bits(unsigned long long max_addr)
|
||||
{
|
||||
/* Physical address can't exceed 48 bits */
|
||||
assert((max_addr & ADDR_MASK_48_TO_63) == 0U);
|
||||
|
||||
/* 48 bits address */
|
||||
if ((max_addr & ADDR_MASK_44_TO_47) != 0U)
|
||||
return TCR_PS_BITS_256TB;
|
||||
|
||||
/* 44 bits address */
|
||||
if ((max_addr & ADDR_MASK_42_TO_43) != 0U)
|
||||
return TCR_PS_BITS_16TB;
|
||||
|
||||
/* 42 bits address */
|
||||
if ((max_addr & ADDR_MASK_40_TO_41) != 0U)
|
||||
return TCR_PS_BITS_4TB;
|
||||
|
||||
/* 40 bits address */
|
||||
if ((max_addr & ADDR_MASK_36_TO_39) != 0U)
|
||||
return TCR_PS_BITS_1TB;
|
||||
|
||||
/* 36 bits address */
|
||||
if ((max_addr & ADDR_MASK_32_TO_35) != 0U)
|
||||
return TCR_PS_BITS_64GB;
|
||||
|
||||
return TCR_PS_BITS_4GB;
|
||||
}
|
||||
|
||||
#if ENABLE_ASSERTIONS
|
||||
/*
|
||||
* Physical Address ranges supported in the AArch64 Memory Model. Value 0b110 is
|
||||
* supported in ARMv8.2 onwards.
|
||||
*/
|
||||
static const unsigned int pa_range_bits_arr[] = {
|
||||
PARANGE_0000, PARANGE_0001, PARANGE_0010, PARANGE_0011, PARANGE_0100,
|
||||
PARANGE_0101, PARANGE_0110
|
||||
};
|
||||
|
||||
unsigned long long xlat_arch_get_max_supported_pa(void)
|
||||
{
|
||||
u_register_t pa_range = read_id_aa64mmfr0_el1() &
|
||||
ID_AA64MMFR0_EL1_PARANGE_MASK;
|
||||
|
||||
/* All other values are reserved */
|
||||
assert(pa_range < ARRAY_SIZE(pa_range_bits_arr));
|
||||
|
||||
return (1ULL << pa_range_bits_arr[pa_range]) - 1ULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return minimum virtual address space size supported by the architecture
|
||||
*/
|
||||
uintptr_t xlat_get_min_virt_addr_space_size(void)
|
||||
{
|
||||
uintptr_t ret;
|
||||
|
||||
if (is_armv8_4_ttst_present())
|
||||
ret = MIN_VIRT_ADDR_SPACE_SIZE_TTST;
|
||||
else
|
||||
ret = MIN_VIRT_ADDR_SPACE_SIZE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* ENABLE_ASSERTIONS*/
|
||||
|
||||
bool is_mmu_enabled_ctx(const xlat_ctx_t *ctx)
|
||||
{
|
||||
if (ctx->xlat_regime == EL1_EL0_REGIME) {
|
||||
assert(xlat_arch_current_el() >= 1U);
|
||||
return (read_sctlr_el1() & SCTLR_M_BIT) != 0U;
|
||||
} else if (ctx->xlat_regime == EL2_REGIME) {
|
||||
assert(xlat_arch_current_el() >= 2U);
|
||||
return (read_sctlr_el2() & SCTLR_M_BIT) != 0U;
|
||||
} else {
|
||||
assert(ctx->xlat_regime == EL3_REGIME);
|
||||
assert(xlat_arch_current_el() >= 3U);
|
||||
return (read_sctlr_el3() & SCTLR_M_BIT) != 0U;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_dcache_enabled(void)
|
||||
{
|
||||
unsigned int el = get_current_el_maybe_constant();
|
||||
|
||||
if (el == 1U) {
|
||||
return (read_sctlr_el1() & SCTLR_C_BIT) != 0U;
|
||||
} else if (el == 2U) {
|
||||
return (read_sctlr_el2() & SCTLR_C_BIT) != 0U;
|
||||
} else {
|
||||
return (read_sctlr_el3() & SCTLR_C_BIT) != 0U;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t xlat_arch_regime_get_xn_desc(int xlat_regime)
|
||||
{
|
||||
if (xlat_regime == EL1_EL0_REGIME) {
|
||||
return UPPER_ATTRS(UXN) | UPPER_ATTRS(PXN);
|
||||
} else {
|
||||
assert((xlat_regime == EL2_REGIME) ||
|
||||
(xlat_regime == EL3_REGIME));
|
||||
return UPPER_ATTRS(XN);
|
||||
}
|
||||
}
|
||||
|
||||
void xlat_arch_tlbi_va(uintptr_t va, int xlat_regime)
|
||||
{
|
||||
/*
|
||||
* Ensure the translation table write has drained into memory before
|
||||
* invalidating the TLB entry.
|
||||
*/
|
||||
dsbishst();
|
||||
|
||||
/*
|
||||
* This function only supports invalidation of TLB entries for the EL3
|
||||
* and EL1&0 translation regimes.
|
||||
*
|
||||
* Also, it is architecturally UNDEFINED to invalidate TLBs of a higher
|
||||
* exception level (see section D4.9.2 of the ARM ARM rev B.a).
|
||||
*/
|
||||
if (xlat_regime == EL1_EL0_REGIME) {
|
||||
assert(xlat_arch_current_el() >= 1U);
|
||||
tlbivaae1is(TLBI_ADDR(va));
|
||||
} else if (xlat_regime == EL2_REGIME) {
|
||||
assert(xlat_arch_current_el() >= 2U);
|
||||
tlbivae2is(TLBI_ADDR(va));
|
||||
} else {
|
||||
assert(xlat_regime == EL3_REGIME);
|
||||
assert(xlat_arch_current_el() >= 3U);
|
||||
tlbivae3is(TLBI_ADDR(va));
|
||||
}
|
||||
}
|
||||
|
||||
void xlat_arch_tlbi_va_sync(void)
|
||||
{
|
||||
/*
|
||||
* A TLB maintenance instruction can complete at any time after
|
||||
* it is issued, but is only guaranteed to be complete after the
|
||||
* execution of DSB by the PE that executed the TLB maintenance
|
||||
* instruction. After the TLB invalidate instruction is
|
||||
* complete, no new memory accesses using the invalidated TLB
|
||||
* entries will be observed by any observer of the system
|
||||
* domain. See section D4.8.2 of the ARMv8 (issue k), paragraph
|
||||
* "Ordering and completion of TLB maintenance instructions".
|
||||
*/
|
||||
dsbish();
|
||||
|
||||
/*
|
||||
* The effects of a completed TLB maintenance instruction are
|
||||
* only guaranteed to be visible on the PE that executed the
|
||||
* instruction after the execution of an ISB instruction by the
|
||||
* PE that executed the TLB maintenance instruction.
|
||||
*/
|
||||
isb();
|
||||
}
|
||||
|
||||
unsigned int xlat_arch_current_el(void)
|
||||
{
|
||||
unsigned int el = (unsigned int)GET_EL(read_CurrentEl());
|
||||
|
||||
assert(el > 0U);
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
void setup_mmu_cfg(uint64_t *params, unsigned int flags,
|
||||
const uint64_t *base_table, unsigned long long max_pa,
|
||||
uintptr_t max_va, int xlat_regime)
|
||||
{
|
||||
uint64_t mair, ttbr0, tcr;
|
||||
uintptr_t virtual_addr_space_size;
|
||||
|
||||
/* Set attributes in the right indices of the MAIR. */
|
||||
mair = MAIR_ATTR_SET(ATTR_DEVICE, ATTR_DEVICE_INDEX);
|
||||
mair |= MAIR_ATTR_SET(ATTR_IWBWA_OWBWA_NTR, ATTR_IWBWA_OWBWA_NTR_INDEX);
|
||||
mair |= MAIR_ATTR_SET(ATTR_NON_CACHEABLE, ATTR_NON_CACHEABLE_INDEX);
|
||||
|
||||
/*
|
||||
* Limit the input address ranges and memory region sizes translated
|
||||
* using TTBR0 to the given virtual address space size.
|
||||
*/
|
||||
assert(max_va < ((uint64_t)UINTPTR_MAX));
|
||||
|
||||
virtual_addr_space_size = (uintptr_t)max_va + 1U;
|
||||
|
||||
assert(virtual_addr_space_size >=
|
||||
xlat_get_min_virt_addr_space_size());
|
||||
assert(virtual_addr_space_size <= MAX_VIRT_ADDR_SPACE_SIZE);
|
||||
assert(IS_POWER_OF_TWO(virtual_addr_space_size));
|
||||
|
||||
/*
|
||||
* __builtin_ctzll(0) is undefined but here we are guaranteed that
|
||||
* virtual_addr_space_size is in the range [1,UINTPTR_MAX].
|
||||
*/
|
||||
int t0sz = 64 - __builtin_ctzll(virtual_addr_space_size);
|
||||
|
||||
tcr = (uint64_t)t0sz << TCR_T0SZ_SHIFT;
|
||||
|
||||
/*
|
||||
* Set the cacheability and shareability attributes for memory
|
||||
* associated with translation table walks.
|
||||
*/
|
||||
if ((flags & XLAT_TABLE_NC) != 0U) {
|
||||
/* Inner & outer non-cacheable non-shareable. */
|
||||
tcr |= TCR_SH_NON_SHAREABLE |
|
||||
TCR_RGN_OUTER_NC | TCR_RGN_INNER_NC;
|
||||
} else {
|
||||
/* Inner & outer WBWA & shareable. */
|
||||
tcr |= TCR_SH_INNER_SHAREABLE |
|
||||
TCR_RGN_OUTER_WBA | TCR_RGN_INNER_WBA;
|
||||
}
|
||||
|
||||
/*
|
||||
* It is safer to restrict the max physical address accessible by the
|
||||
* hardware as much as possible.
|
||||
*/
|
||||
unsigned long long tcr_ps_bits = tcr_physical_addr_size_bits(max_pa);
|
||||
|
||||
if (xlat_regime == EL1_EL0_REGIME) {
|
||||
/*
|
||||
* TCR_EL1.EPD1: Disable translation table walk for addresses
|
||||
* that are translated using TTBR1_EL1.
|
||||
*/
|
||||
tcr |= TCR_EPD1_BIT | (tcr_ps_bits << TCR_EL1_IPS_SHIFT);
|
||||
} else if (xlat_regime == EL2_REGIME) {
|
||||
tcr |= TCR_EL2_RES1 | (tcr_ps_bits << TCR_EL2_PS_SHIFT);
|
||||
} else {
|
||||
assert(xlat_regime == EL3_REGIME);
|
||||
tcr |= TCR_EL3_RES1 | (tcr_ps_bits << TCR_EL3_PS_SHIFT);
|
||||
}
|
||||
|
||||
/* Set TTBR bits as well */
|
||||
ttbr0 = (uint64_t) base_table;
|
||||
|
||||
if (is_armv8_2_ttcnp_present()) {
|
||||
/* Enable CnP bit so as to share page tables with all PEs. */
|
||||
ttbr0 |= TTBR_CNP_BIT;
|
||||
}
|
||||
|
||||
params[MMU_CFG_MAIR] = mair;
|
||||
params[MMU_CFG_TCR] = tcr;
|
||||
params[MMU_CFG_TTBR0] = ttbr0;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#
|
||||
# Copyright (c) 2020-2022, ARM Limited. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
ifeq (${USE_DEBUGFS}, 1)
|
||||
$(error "Debugfs requires functionality from the dynamic translation \
|
||||
library and is incompatible with ALLOW_RO_XLAT_TABLES.")
|
||||
endif
|
||||
|
||||
ifeq (${ARCH},aarch32)
|
||||
ifeq (${RESET_TO_SP_MIN},1)
|
||||
$(error "RESET_TO_SP_MIN requires functionality from the dynamic \
|
||||
translation library and is incompatible with \
|
||||
ALLOW_RO_XLAT_TABLES.")
|
||||
endif
|
||||
else # if AArch64
|
||||
ifeq (${PLAT},tegra)
|
||||
$(error "Tegra requires functionality from the dynamic translation \
|
||||
library and is incompatible with ALLOW_RO_XLAT_TABLES.")
|
||||
endif
|
||||
ifeq (${RESET_TO_BL31},1)
|
||||
$(error "RESET_TO_BL31 requires functionality from the dynamic \
|
||||
translation library and is incompatible with \
|
||||
ALLOW_RO_XLAT_TABLES.")
|
||||
endif
|
||||
ifeq (${SPD},trusty)
|
||||
$(error "Trusty requires functionality from the dynamic translation \
|
||||
library and is incompatible with ALLOW_RO_XLAT_TABLES.")
|
||||
endif
|
||||
ifeq (${SPM_MM},1)
|
||||
$(error "SPM_MM requires functionality to change memory region \
|
||||
attributes, which is not possible once the translation tables \
|
||||
have been made read-only.")
|
||||
endif
|
||||
ifeq (${SPMC_AT_EL3},1)
|
||||
$(error "EL3 SPMC requires functionality from the dynamic translation \
|
||||
library and is incompatible with ALLOW_RO_XLAT_TABLES.")
|
||||
endif
|
||||
endif
|
||||
@@ -0,0 +1,19 @@
|
||||
#
|
||||
# Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
XLAT_TABLES_LIB_SRCS := $(addprefix lib/xlat_tables_v2/, \
|
||||
${ARCH}/enable_mmu.S \
|
||||
${ARCH}/xlat_tables_arch.c \
|
||||
xlat_tables_context.c \
|
||||
xlat_tables_core.c \
|
||||
xlat_tables_utils.c)
|
||||
|
||||
XLAT_TABLES_LIB_V2 := 1
|
||||
$(eval $(call add_define,XLAT_TABLES_LIB_V2))
|
||||
|
||||
ifeq (${ALLOW_RO_XLAT_TABLES}, 1)
|
||||
include lib/xlat_tables_v2/ro_xlat_tables.mk
|
||||
endif
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <arch_helpers.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <platform_def.h>
|
||||
|
||||
#include <common/debug.h>
|
||||
#include <lib/xlat_tables/xlat_tables_defs.h>
|
||||
#include <lib/xlat_tables/xlat_tables_v2.h>
|
||||
|
||||
#include "xlat_tables_private.h"
|
||||
|
||||
/*
|
||||
* MMU configuration register values for the active translation context. Used
|
||||
* from the MMU assembly helpers.
|
||||
*/
|
||||
uint64_t mmu_cfg_params[MMU_CFG_PARAM_MAX];
|
||||
|
||||
/*
|
||||
* Allocate and initialise the default translation context for the BL image
|
||||
* currently executing.
|
||||
*/
|
||||
REGISTER_XLAT_CONTEXT(tf, MAX_MMAP_REGIONS, MAX_XLAT_TABLES,
|
||||
PLAT_VIRT_ADDR_SPACE_SIZE, PLAT_PHY_ADDR_SPACE_SIZE);
|
||||
|
||||
void mmap_add_region(unsigned long long base_pa, uintptr_t base_va, size_t size,
|
||||
unsigned int attr)
|
||||
{
|
||||
mmap_region_t mm = MAP_REGION(base_pa, base_va, size, attr);
|
||||
|
||||
mmap_add_region_ctx(&tf_xlat_ctx, &mm);
|
||||
}
|
||||
|
||||
void mmap_add(const mmap_region_t *mm)
|
||||
{
|
||||
mmap_add_ctx(&tf_xlat_ctx, mm);
|
||||
}
|
||||
|
||||
void mmap_add_region_alloc_va(unsigned long long base_pa, uintptr_t *base_va,
|
||||
size_t size, unsigned int attr)
|
||||
{
|
||||
mmap_region_t mm = MAP_REGION_ALLOC_VA(base_pa, size, attr);
|
||||
|
||||
mmap_add_region_alloc_va_ctx(&tf_xlat_ctx, &mm);
|
||||
|
||||
*base_va = mm.base_va;
|
||||
}
|
||||
|
||||
void mmap_add_alloc_va(mmap_region_t *mm)
|
||||
{
|
||||
while (mm->granularity != 0U) {
|
||||
assert(mm->base_va == 0U);
|
||||
mmap_add_region_alloc_va_ctx(&tf_xlat_ctx, mm);
|
||||
mm++;
|
||||
}
|
||||
}
|
||||
|
||||
#if PLAT_XLAT_TABLES_DYNAMIC
|
||||
|
||||
int mmap_add_dynamic_region(unsigned long long base_pa, uintptr_t base_va,
|
||||
size_t size, unsigned int attr)
|
||||
{
|
||||
mmap_region_t mm = MAP_REGION(base_pa, base_va, size, attr);
|
||||
|
||||
return mmap_add_dynamic_region_ctx(&tf_xlat_ctx, &mm);
|
||||
}
|
||||
|
||||
int mmap_add_dynamic_region_alloc_va(unsigned long long base_pa,
|
||||
uintptr_t *base_va, size_t size,
|
||||
unsigned int attr)
|
||||
{
|
||||
mmap_region_t mm = MAP_REGION_ALLOC_VA(base_pa, size, attr);
|
||||
|
||||
int rc = mmap_add_dynamic_region_alloc_va_ctx(&tf_xlat_ctx, &mm);
|
||||
|
||||
*base_va = mm.base_va;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int mmap_remove_dynamic_region(uintptr_t base_va, size_t size)
|
||||
{
|
||||
return mmap_remove_dynamic_region_ctx(&tf_xlat_ctx,
|
||||
base_va, size);
|
||||
}
|
||||
|
||||
#endif /* PLAT_XLAT_TABLES_DYNAMIC */
|
||||
|
||||
void __init init_xlat_tables(void)
|
||||
{
|
||||
assert(tf_xlat_ctx.xlat_regime == EL_REGIME_INVALID);
|
||||
|
||||
unsigned int current_el = xlat_arch_current_el();
|
||||
|
||||
if (current_el == 1U) {
|
||||
tf_xlat_ctx.xlat_regime = EL1_EL0_REGIME;
|
||||
} else if (current_el == 2U) {
|
||||
tf_xlat_ctx.xlat_regime = EL2_REGIME;
|
||||
} else {
|
||||
assert(current_el == 3U);
|
||||
tf_xlat_ctx.xlat_regime = EL3_REGIME;
|
||||
}
|
||||
|
||||
init_xlat_tables_ctx(&tf_xlat_ctx);
|
||||
}
|
||||
|
||||
int xlat_get_mem_attributes(uintptr_t base_va, uint32_t *attr)
|
||||
{
|
||||
return xlat_get_mem_attributes_ctx(&tf_xlat_ctx, base_va, attr);
|
||||
}
|
||||
|
||||
int xlat_change_mem_attributes(uintptr_t base_va, size_t size, uint32_t attr)
|
||||
{
|
||||
return xlat_change_mem_attributes_ctx(&tf_xlat_ctx, base_va, size, attr);
|
||||
}
|
||||
|
||||
#if PLAT_RO_XLAT_TABLES
|
||||
/* Change the memory attributes of the descriptors which resolve the address
|
||||
* range that belongs to the translation tables themselves, which are by default
|
||||
* mapped as part of read-write data in the BL image's memory.
|
||||
*
|
||||
* Since the translation tables map themselves via these level 3 (page)
|
||||
* descriptors, any change applied to them with the MMU on would introduce a
|
||||
* chicken and egg problem because of the break-before-make sequence.
|
||||
* Eventually, it would reach the descriptor that resolves the very table it
|
||||
* belongs to and the invalidation (break step) would cause the subsequent write
|
||||
* (make step) to it to generate an MMU fault. Therefore, the MMU is disabled
|
||||
* before making the change.
|
||||
*
|
||||
* No assumption is made about what data this function needs, therefore all the
|
||||
* caches are flushed in order to ensure coherency. A future optimization would
|
||||
* be to only flush the required data to main memory.
|
||||
*/
|
||||
int xlat_make_tables_readonly(void)
|
||||
{
|
||||
assert(tf_xlat_ctx.initialized == true);
|
||||
#ifdef __aarch64__
|
||||
if (tf_xlat_ctx.xlat_regime == EL1_EL0_REGIME) {
|
||||
disable_mmu_el1();
|
||||
} else if (tf_xlat_ctx.xlat_regime == EL3_REGIME) {
|
||||
disable_mmu_el3();
|
||||
} else {
|
||||
assert(tf_xlat_ctx.xlat_regime == EL2_REGIME);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Flush all caches. */
|
||||
dcsw_op_all(DCCISW);
|
||||
#else /* !__aarch64__ */
|
||||
assert(tf_xlat_ctx.xlat_regime == EL1_EL0_REGIME);
|
||||
/* On AArch32, we flush the caches before disabling the MMU. The reason
|
||||
* for this is that the dcsw_op_all AArch32 function pushes some
|
||||
* registers onto the stack under the assumption that it is writing to
|
||||
* cache, which is not true with the MMU off. This would result in the
|
||||
* stack becoming corrupted and a wrong/junk value for the LR being
|
||||
* restored at the end of the routine.
|
||||
*/
|
||||
dcsw_op_all(DC_OP_CISW);
|
||||
disable_mmu_secure();
|
||||
#endif
|
||||
|
||||
int rc = xlat_change_mem_attributes_ctx(&tf_xlat_ctx,
|
||||
(uintptr_t)tf_xlat_ctx.tables,
|
||||
tf_xlat_ctx.tables_num * XLAT_TABLE_SIZE,
|
||||
MT_RO_DATA | MT_SECURE);
|
||||
|
||||
#ifdef __aarch64__
|
||||
if (tf_xlat_ctx.xlat_regime == EL1_EL0_REGIME) {
|
||||
enable_mmu_el1(0U);
|
||||
} else {
|
||||
assert(tf_xlat_ctx.xlat_regime == EL3_REGIME);
|
||||
enable_mmu_el3(0U);
|
||||
}
|
||||
#else /* !__aarch64__ */
|
||||
enable_mmu_svc_mon(0U);
|
||||
#endif
|
||||
|
||||
if (rc == 0) {
|
||||
tf_xlat_ctx.readonly_tables = true;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif /* PLAT_RO_XLAT_TABLES */
|
||||
|
||||
/*
|
||||
* If dynamic allocation of new regions is disabled then by the time we call the
|
||||
* function enabling the MMU, we'll have registered all the memory regions to
|
||||
* map for the system's lifetime. Therefore, at this point we know the maximum
|
||||
* physical address that will ever be mapped.
|
||||
*
|
||||
* If dynamic allocation is enabled then we can't make any such assumption
|
||||
* because the maximum physical address could get pushed while adding a new
|
||||
* region. Therefore, in this case we have to assume that the whole address
|
||||
* space size might be mapped.
|
||||
*/
|
||||
#ifdef PLAT_XLAT_TABLES_DYNAMIC
|
||||
#define MAX_PHYS_ADDR tf_xlat_ctx.pa_max_address
|
||||
#else
|
||||
#define MAX_PHYS_ADDR tf_xlat_ctx.max_pa
|
||||
#endif
|
||||
|
||||
#ifdef __aarch64__
|
||||
|
||||
void enable_mmu_el1(unsigned int flags)
|
||||
{
|
||||
setup_mmu_cfg((uint64_t *)&mmu_cfg_params, flags,
|
||||
tf_xlat_ctx.base_table, MAX_PHYS_ADDR,
|
||||
tf_xlat_ctx.va_max_address, EL1_EL0_REGIME);
|
||||
enable_mmu_direct_el1(flags);
|
||||
}
|
||||
|
||||
void enable_mmu_el2(unsigned int flags)
|
||||
{
|
||||
setup_mmu_cfg((uint64_t *)&mmu_cfg_params, flags,
|
||||
tf_xlat_ctx.base_table, MAX_PHYS_ADDR,
|
||||
tf_xlat_ctx.va_max_address, EL2_REGIME);
|
||||
enable_mmu_direct_el2(flags);
|
||||
}
|
||||
|
||||
void enable_mmu_el3(unsigned int flags)
|
||||
{
|
||||
setup_mmu_cfg((uint64_t *)&mmu_cfg_params, flags,
|
||||
tf_xlat_ctx.base_table, MAX_PHYS_ADDR,
|
||||
tf_xlat_ctx.va_max_address, EL3_REGIME);
|
||||
enable_mmu_direct_el3(flags);
|
||||
}
|
||||
|
||||
void enable_mmu(unsigned int flags)
|
||||
{
|
||||
switch (get_current_el_maybe_constant()) {
|
||||
case 1:
|
||||
enable_mmu_el1(flags);
|
||||
break;
|
||||
case 2:
|
||||
enable_mmu_el2(flags);
|
||||
break;
|
||||
case 3:
|
||||
enable_mmu_el3(flags);
|
||||
break;
|
||||
default:
|
||||
panic();
|
||||
}
|
||||
}
|
||||
|
||||
#else /* !__aarch64__ */
|
||||
|
||||
void enable_mmu_svc_mon(unsigned int flags)
|
||||
{
|
||||
setup_mmu_cfg((uint64_t *)&mmu_cfg_params, flags,
|
||||
tf_xlat_ctx.base_table, MAX_PHYS_ADDR,
|
||||
tf_xlat_ctx.va_max_address, EL1_EL0_REGIME);
|
||||
enable_mmu_direct_svc_mon(flags);
|
||||
}
|
||||
|
||||
void enable_mmu_hyp(unsigned int flags)
|
||||
{
|
||||
setup_mmu_cfg((uint64_t *)&mmu_cfg_params, flags,
|
||||
tf_xlat_ctx.base_table, MAX_PHYS_ADDR,
|
||||
tf_xlat_ctx.va_max_address, EL2_REGIME);
|
||||
enable_mmu_direct_hyp(flags);
|
||||
}
|
||||
|
||||
#endif /* __aarch64__ */
|
||||
+1244
File diff suppressed because it is too large
Load Diff
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2021, Arm Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef XLAT_TABLES_PRIVATE_H
|
||||
#define XLAT_TABLES_PRIVATE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <platform_def.h>
|
||||
|
||||
#include <lib/xlat_tables/xlat_tables_defs.h>
|
||||
|
||||
#if PLAT_XLAT_TABLES_DYNAMIC
|
||||
/*
|
||||
* Private shifts and masks to access fields of an mmap attribute
|
||||
*/
|
||||
/* Dynamic or static */
|
||||
#define MT_DYN_SHIFT U(31)
|
||||
|
||||
/*
|
||||
* Memory mapping private attributes
|
||||
*
|
||||
* Private attributes not exposed in the public header.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Regions mapped before the MMU can't be unmapped dynamically (they are
|
||||
* static) and regions mapped with MMU enabled can be unmapped. This
|
||||
* behaviour can't be overridden.
|
||||
*
|
||||
* Static regions can overlap each other, dynamic regions can't.
|
||||
*/
|
||||
#define MT_STATIC (U(0) << MT_DYN_SHIFT)
|
||||
#define MT_DYNAMIC (U(1) << MT_DYN_SHIFT)
|
||||
|
||||
#endif /* PLAT_XLAT_TABLES_DYNAMIC */
|
||||
|
||||
extern uint64_t mmu_cfg_params[MMU_CFG_PARAM_MAX];
|
||||
|
||||
/* Determine the physical address space encoded in the 'attr' parameter. */
|
||||
uint32_t xlat_arch_get_pas(uint32_t attr);
|
||||
|
||||
/*
|
||||
* Return the execute-never mask that will prevent instruction fetch at the
|
||||
* given translation regime.
|
||||
*/
|
||||
uint64_t xlat_arch_regime_get_xn_desc(int xlat_regime);
|
||||
|
||||
/*
|
||||
* Invalidate all TLB entries that match the given virtual address. This
|
||||
* operation applies to all PEs in the same Inner Shareable domain as the PE
|
||||
* that executes this function. This functions must be called for every
|
||||
* translation table entry that is modified. It only affects the specified
|
||||
* translation regime.
|
||||
*
|
||||
* Note, however, that it is architecturally UNDEFINED to invalidate TLB entries
|
||||
* pertaining to a higher exception level, e.g. invalidating EL3 entries from
|
||||
* S-EL1.
|
||||
*/
|
||||
void xlat_arch_tlbi_va(uintptr_t va, int xlat_regime);
|
||||
|
||||
/*
|
||||
* This function has to be called at the end of any code that uses the function
|
||||
* xlat_arch_tlbi_va().
|
||||
*/
|
||||
void xlat_arch_tlbi_va_sync(void);
|
||||
|
||||
/* Print VA, PA, size and attributes of all regions in the mmap array. */
|
||||
void xlat_mmap_print(const mmap_region_t *mmap);
|
||||
|
||||
/*
|
||||
* Print the current state of the translation tables by reading them from
|
||||
* memory.
|
||||
*/
|
||||
void xlat_tables_print(xlat_ctx_t *ctx);
|
||||
|
||||
/*
|
||||
* Returns a block/page table descriptor for the given level and attributes.
|
||||
*/
|
||||
uint64_t xlat_desc(const xlat_ctx_t *ctx, uint32_t attr,
|
||||
unsigned long long addr_pa, unsigned int level);
|
||||
|
||||
/*
|
||||
* Architecture-specific initialization code.
|
||||
*/
|
||||
|
||||
/* Returns the current Exception Level. The returned EL must be 1 or higher. */
|
||||
unsigned int xlat_arch_current_el(void);
|
||||
|
||||
/*
|
||||
* Return the maximum physical address supported by the hardware.
|
||||
* This value depends on the execution state (AArch32/AArch64).
|
||||
*/
|
||||
unsigned long long xlat_arch_get_max_supported_pa(void);
|
||||
|
||||
/*
|
||||
* Returns true if the MMU of the translation regime managed by the given
|
||||
* xlat_ctx_t is enabled, false otherwise.
|
||||
*/
|
||||
bool is_mmu_enabled_ctx(const xlat_ctx_t *ctx);
|
||||
|
||||
/*
|
||||
* Returns minimum virtual address space size supported by the architecture
|
||||
*/
|
||||
uintptr_t xlat_get_min_virt_addr_space_size(void);
|
||||
|
||||
#endif /* XLAT_TABLES_PRIVATE_H */
|
||||
+592
@@ -0,0 +1,592 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2021, Arm Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <platform_def.h>
|
||||
|
||||
#include <arch_helpers.h>
|
||||
#include <common/debug.h>
|
||||
#include <lib/utils_def.h>
|
||||
#include <lib/xlat_tables/xlat_tables_defs.h>
|
||||
#include <lib/xlat_tables/xlat_tables_v2.h>
|
||||
|
||||
#include "xlat_tables_private.h"
|
||||
|
||||
#if LOG_LEVEL < LOG_LEVEL_VERBOSE
|
||||
|
||||
void xlat_mmap_print(__unused const mmap_region_t *mmap)
|
||||
{
|
||||
/* Empty */
|
||||
}
|
||||
|
||||
void xlat_tables_print(__unused xlat_ctx_t *ctx)
|
||||
{
|
||||
/* Empty */
|
||||
}
|
||||
|
||||
#else /* if LOG_LEVEL >= LOG_LEVEL_VERBOSE */
|
||||
|
||||
void xlat_mmap_print(const mmap_region_t *mmap)
|
||||
{
|
||||
printf("mmap:\n");
|
||||
const mmap_region_t *mm = mmap;
|
||||
|
||||
while (mm->size != 0U) {
|
||||
printf(" VA:0x%lx PA:0x%llx size:0x%zx attr:0x%x granularity:0x%zx\n",
|
||||
mm->base_va, mm->base_pa, mm->size, mm->attr,
|
||||
mm->granularity);
|
||||
++mm;
|
||||
};
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/* Print the attributes of the specified block descriptor. */
|
||||
static void xlat_desc_print(const xlat_ctx_t *ctx, uint64_t desc)
|
||||
{
|
||||
uint64_t mem_type_index = ATTR_INDEX_GET(desc);
|
||||
int xlat_regime = ctx->xlat_regime;
|
||||
|
||||
if (mem_type_index == ATTR_IWBWA_OWBWA_NTR_INDEX) {
|
||||
printf("MEM");
|
||||
} else if (mem_type_index == ATTR_NON_CACHEABLE_INDEX) {
|
||||
printf("NC");
|
||||
} else {
|
||||
assert(mem_type_index == ATTR_DEVICE_INDEX);
|
||||
printf("DEV");
|
||||
}
|
||||
|
||||
if ((xlat_regime == EL3_REGIME) || (xlat_regime == EL2_REGIME)) {
|
||||
/* For EL3 and EL2 only check the AP[2] and XN bits. */
|
||||
printf(((desc & LOWER_ATTRS(AP_RO)) != 0ULL) ? "-RO" : "-RW");
|
||||
printf(((desc & UPPER_ATTRS(XN)) != 0ULL) ? "-XN" : "-EXEC");
|
||||
} else {
|
||||
assert(xlat_regime == EL1_EL0_REGIME);
|
||||
/*
|
||||
* For EL0 and EL1:
|
||||
* - In AArch64 PXN and UXN can be set independently but in
|
||||
* AArch32 there is no UXN (XN affects both privilege levels).
|
||||
* For consistency, we set them simultaneously in both cases.
|
||||
* - RO and RW permissions must be the same in EL1 and EL0. If
|
||||
* EL0 can access that memory region, so can EL1, with the
|
||||
* same permissions.
|
||||
*/
|
||||
#if ENABLE_ASSERTIONS
|
||||
uint64_t xn_mask = xlat_arch_regime_get_xn_desc(EL1_EL0_REGIME);
|
||||
uint64_t xn_perm = desc & xn_mask;
|
||||
|
||||
assert((xn_perm == xn_mask) || (xn_perm == 0ULL));
|
||||
#endif
|
||||
printf(((desc & LOWER_ATTRS(AP_RO)) != 0ULL) ? "-RO" : "-RW");
|
||||
/* Only check one of PXN and UXN, the other one is the same. */
|
||||
printf(((desc & UPPER_ATTRS(PXN)) != 0ULL) ? "-XN" : "-EXEC");
|
||||
/*
|
||||
* Privileged regions can only be accessed from EL1, user
|
||||
* regions can be accessed from EL1 and EL0.
|
||||
*/
|
||||
printf(((desc & LOWER_ATTRS(AP_ACCESS_UNPRIVILEGED)) != 0ULL)
|
||||
? "-USER" : "-PRIV");
|
||||
}
|
||||
|
||||
#if ENABLE_RME
|
||||
switch (desc & LOWER_ATTRS(EL3_S1_NSE | NS)) {
|
||||
case 0ULL:
|
||||
printf("-S");
|
||||
break;
|
||||
case LOWER_ATTRS(NS):
|
||||
printf("-NS");
|
||||
break;
|
||||
case LOWER_ATTRS(EL3_S1_NSE):
|
||||
printf("-RT");
|
||||
break;
|
||||
default: /* LOWER_ATTRS(EL3_S1_NSE | NS) */
|
||||
printf("-RL");
|
||||
}
|
||||
#else
|
||||
printf(((LOWER_ATTRS(NS) & desc) != 0ULL) ? "-NS" : "-S");
|
||||
#endif
|
||||
|
||||
#ifdef __aarch64__
|
||||
/* Check Guarded Page bit */
|
||||
if ((desc & GP) != 0ULL) {
|
||||
printf("-GP");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static const char * const level_spacers[] = {
|
||||
"[LV0] ",
|
||||
" [LV1] ",
|
||||
" [LV2] ",
|
||||
" [LV3] "
|
||||
};
|
||||
|
||||
static const char *invalid_descriptors_ommited =
|
||||
"%s(%d invalid descriptors omitted)\n";
|
||||
|
||||
/*
|
||||
* Recursive function that reads the translation tables passed as an argument
|
||||
* and prints their status.
|
||||
*/
|
||||
static void xlat_tables_print_internal(xlat_ctx_t *ctx, uintptr_t table_base_va,
|
||||
const uint64_t *table_base, unsigned int table_entries,
|
||||
unsigned int level)
|
||||
{
|
||||
assert(level <= XLAT_TABLE_LEVEL_MAX);
|
||||
|
||||
uint64_t desc;
|
||||
uintptr_t table_idx_va = table_base_va;
|
||||
unsigned int table_idx = 0U;
|
||||
size_t level_size = XLAT_BLOCK_SIZE(level);
|
||||
|
||||
/*
|
||||
* Keep track of how many invalid descriptors are counted in a row.
|
||||
* Whenever multiple invalid descriptors are found, only the first one
|
||||
* is printed, and a line is added to inform about how many descriptors
|
||||
* have been omitted.
|
||||
*/
|
||||
int invalid_row_count = 0;
|
||||
|
||||
while (table_idx < table_entries) {
|
||||
|
||||
desc = table_base[table_idx];
|
||||
|
||||
if ((desc & DESC_MASK) == INVALID_DESC) {
|
||||
|
||||
if (invalid_row_count == 0) {
|
||||
printf("%sVA:0x%lx size:0x%zx\n",
|
||||
level_spacers[level],
|
||||
table_idx_va, level_size);
|
||||
}
|
||||
invalid_row_count++;
|
||||
|
||||
} else {
|
||||
|
||||
if (invalid_row_count > 1) {
|
||||
printf(invalid_descriptors_ommited,
|
||||
level_spacers[level],
|
||||
invalid_row_count - 1);
|
||||
}
|
||||
invalid_row_count = 0;
|
||||
|
||||
/*
|
||||
* Check if this is a table or a block. Tables are only
|
||||
* allowed in levels other than 3, but DESC_PAGE has the
|
||||
* same value as DESC_TABLE, so we need to check.
|
||||
*/
|
||||
if (((desc & DESC_MASK) == TABLE_DESC) &&
|
||||
(level < XLAT_TABLE_LEVEL_MAX)) {
|
||||
/*
|
||||
* Do not print any PA for a table descriptor,
|
||||
* as it doesn't directly map physical memory
|
||||
* but instead points to the next translation
|
||||
* table in the translation table walk.
|
||||
*/
|
||||
printf("%sVA:0x%lx size:0x%zx\n",
|
||||
level_spacers[level],
|
||||
table_idx_va, level_size);
|
||||
|
||||
uintptr_t addr_inner = desc & TABLE_ADDR_MASK;
|
||||
|
||||
xlat_tables_print_internal(ctx, table_idx_va,
|
||||
(uint64_t *)addr_inner,
|
||||
XLAT_TABLE_ENTRIES, level + 1U);
|
||||
} else {
|
||||
printf("%sVA:0x%lx PA:0x%" PRIx64 " size:0x%zx ",
|
||||
level_spacers[level], table_idx_va,
|
||||
(uint64_t)(desc & TABLE_ADDR_MASK),
|
||||
level_size);
|
||||
xlat_desc_print(ctx, desc);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
table_idx++;
|
||||
table_idx_va += level_size;
|
||||
}
|
||||
|
||||
if (invalid_row_count > 1) {
|
||||
printf(invalid_descriptors_ommited,
|
||||
level_spacers[level], invalid_row_count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void xlat_tables_print(xlat_ctx_t *ctx)
|
||||
{
|
||||
const char *xlat_regime_str;
|
||||
int used_page_tables;
|
||||
|
||||
if (ctx->xlat_regime == EL1_EL0_REGIME) {
|
||||
xlat_regime_str = "1&0";
|
||||
} else if (ctx->xlat_regime == EL2_REGIME) {
|
||||
xlat_regime_str = "2";
|
||||
} else {
|
||||
assert(ctx->xlat_regime == EL3_REGIME);
|
||||
xlat_regime_str = "3";
|
||||
}
|
||||
VERBOSE("Translation tables state:\n");
|
||||
VERBOSE(" Xlat regime: EL%s\n", xlat_regime_str);
|
||||
VERBOSE(" Max allowed PA: 0x%llx\n", ctx->pa_max_address);
|
||||
VERBOSE(" Max allowed VA: 0x%lx\n", ctx->va_max_address);
|
||||
VERBOSE(" Max mapped PA: 0x%llx\n", ctx->max_pa);
|
||||
VERBOSE(" Max mapped VA: 0x%lx\n", ctx->max_va);
|
||||
|
||||
VERBOSE(" Initial lookup level: %u\n", ctx->base_level);
|
||||
VERBOSE(" Entries @initial lookup level: %u\n",
|
||||
ctx->base_table_entries);
|
||||
|
||||
#if PLAT_XLAT_TABLES_DYNAMIC
|
||||
used_page_tables = 0;
|
||||
for (int i = 0; i < ctx->tables_num; ++i) {
|
||||
if (ctx->tables_mapped_regions[i] != 0)
|
||||
++used_page_tables;
|
||||
}
|
||||
#else
|
||||
used_page_tables = ctx->next_table;
|
||||
#endif
|
||||
VERBOSE(" Used %d sub-tables out of %d (spare: %d)\n",
|
||||
used_page_tables, ctx->tables_num,
|
||||
ctx->tables_num - used_page_tables);
|
||||
|
||||
xlat_tables_print_internal(ctx, 0U, ctx->base_table,
|
||||
ctx->base_table_entries, ctx->base_level);
|
||||
}
|
||||
|
||||
#endif /* LOG_LEVEL >= LOG_LEVEL_VERBOSE */
|
||||
|
||||
/*
|
||||
* Do a translation table walk to find the block or page descriptor that maps
|
||||
* virtual_addr.
|
||||
*
|
||||
* On success, return the address of the descriptor within the translation
|
||||
* table. Its lookup level is stored in '*out_level'.
|
||||
* On error, return NULL.
|
||||
*
|
||||
* xlat_table_base
|
||||
* Base address for the initial lookup level.
|
||||
* xlat_table_base_entries
|
||||
* Number of entries in the translation table for the initial lookup level.
|
||||
* virt_addr_space_size
|
||||
* Size in bytes of the virtual address space.
|
||||
*/
|
||||
static uint64_t *find_xlat_table_entry(uintptr_t virtual_addr,
|
||||
void *xlat_table_base,
|
||||
unsigned int xlat_table_base_entries,
|
||||
unsigned long long virt_addr_space_size,
|
||||
unsigned int *out_level)
|
||||
{
|
||||
unsigned int start_level;
|
||||
uint64_t *table;
|
||||
unsigned int entries;
|
||||
|
||||
start_level = GET_XLAT_TABLE_LEVEL_BASE(virt_addr_space_size);
|
||||
|
||||
table = xlat_table_base;
|
||||
entries = xlat_table_base_entries;
|
||||
|
||||
for (unsigned int level = start_level;
|
||||
level <= XLAT_TABLE_LEVEL_MAX;
|
||||
++level) {
|
||||
uint64_t idx, desc, desc_type;
|
||||
|
||||
idx = XLAT_TABLE_IDX(virtual_addr, level);
|
||||
if (idx >= entries) {
|
||||
WARN("Missing xlat table entry at address 0x%lx\n",
|
||||
virtual_addr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
desc = table[idx];
|
||||
desc_type = desc & DESC_MASK;
|
||||
|
||||
if (desc_type == INVALID_DESC) {
|
||||
VERBOSE("Invalid entry (memory not mapped)\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (level == XLAT_TABLE_LEVEL_MAX) {
|
||||
/*
|
||||
* Only page descriptors allowed at the final lookup
|
||||
* level.
|
||||
*/
|
||||
assert(desc_type == PAGE_DESC);
|
||||
*out_level = level;
|
||||
return &table[idx];
|
||||
}
|
||||
|
||||
if (desc_type == BLOCK_DESC) {
|
||||
*out_level = level;
|
||||
return &table[idx];
|
||||
}
|
||||
|
||||
assert(desc_type == TABLE_DESC);
|
||||
table = (uint64_t *)(uintptr_t)(desc & TABLE_ADDR_MASK);
|
||||
entries = XLAT_TABLE_ENTRIES;
|
||||
}
|
||||
|
||||
/*
|
||||
* This shouldn't be reached, the translation table walk should end at
|
||||
* most at level XLAT_TABLE_LEVEL_MAX and return from inside the loop.
|
||||
*/
|
||||
assert(false);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int xlat_get_mem_attributes_internal(const xlat_ctx_t *ctx,
|
||||
uintptr_t base_va, uint32_t *attributes, uint64_t **table_entry,
|
||||
unsigned long long *addr_pa, unsigned int *table_level)
|
||||
{
|
||||
uint64_t *entry;
|
||||
uint64_t desc;
|
||||
unsigned int level;
|
||||
unsigned long long virt_addr_space_size;
|
||||
|
||||
/*
|
||||
* Sanity-check arguments.
|
||||
*/
|
||||
assert(ctx != NULL);
|
||||
assert(ctx->initialized);
|
||||
assert((ctx->xlat_regime == EL1_EL0_REGIME) ||
|
||||
(ctx->xlat_regime == EL2_REGIME) ||
|
||||
(ctx->xlat_regime == EL3_REGIME));
|
||||
|
||||
virt_addr_space_size = (unsigned long long)ctx->va_max_address + 1ULL;
|
||||
assert(virt_addr_space_size > 0U);
|
||||
|
||||
entry = find_xlat_table_entry(base_va,
|
||||
ctx->base_table,
|
||||
ctx->base_table_entries,
|
||||
virt_addr_space_size,
|
||||
&level);
|
||||
if (entry == NULL) {
|
||||
WARN("Address 0x%lx is not mapped.\n", base_va);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (addr_pa != NULL) {
|
||||
*addr_pa = *entry & TABLE_ADDR_MASK;
|
||||
}
|
||||
|
||||
if (table_entry != NULL) {
|
||||
*table_entry = entry;
|
||||
}
|
||||
|
||||
if (table_level != NULL) {
|
||||
*table_level = level;
|
||||
}
|
||||
|
||||
desc = *entry;
|
||||
|
||||
#if LOG_LEVEL >= LOG_LEVEL_VERBOSE
|
||||
VERBOSE("Attributes: ");
|
||||
xlat_desc_print(ctx, desc);
|
||||
printf("\n");
|
||||
#endif /* LOG_LEVEL >= LOG_LEVEL_VERBOSE */
|
||||
|
||||
assert(attributes != NULL);
|
||||
*attributes = 0U;
|
||||
|
||||
uint64_t attr_index = (desc >> ATTR_INDEX_SHIFT) & ATTR_INDEX_MASK;
|
||||
|
||||
if (attr_index == ATTR_IWBWA_OWBWA_NTR_INDEX) {
|
||||
*attributes |= MT_MEMORY;
|
||||
} else if (attr_index == ATTR_NON_CACHEABLE_INDEX) {
|
||||
*attributes |= MT_NON_CACHEABLE;
|
||||
} else {
|
||||
assert(attr_index == ATTR_DEVICE_INDEX);
|
||||
*attributes |= MT_DEVICE;
|
||||
}
|
||||
|
||||
uint64_t ap2_bit = (desc >> AP2_SHIFT) & 1U;
|
||||
|
||||
if (ap2_bit == AP2_RW)
|
||||
*attributes |= MT_RW;
|
||||
|
||||
if (ctx->xlat_regime == EL1_EL0_REGIME) {
|
||||
uint64_t ap1_bit = (desc >> AP1_SHIFT) & 1U;
|
||||
|
||||
if (ap1_bit == AP1_ACCESS_UNPRIVILEGED)
|
||||
*attributes |= MT_USER;
|
||||
}
|
||||
|
||||
uint64_t ns_bit = (desc >> NS_SHIFT) & 1U;
|
||||
|
||||
if (ns_bit == 1U)
|
||||
*attributes |= MT_NS;
|
||||
|
||||
uint64_t xn_mask = xlat_arch_regime_get_xn_desc(ctx->xlat_regime);
|
||||
|
||||
if ((desc & xn_mask) == xn_mask) {
|
||||
*attributes |= MT_EXECUTE_NEVER;
|
||||
} else {
|
||||
assert((desc & xn_mask) == 0U);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int xlat_get_mem_attributes_ctx(const xlat_ctx_t *ctx, uintptr_t base_va,
|
||||
uint32_t *attr)
|
||||
{
|
||||
return xlat_get_mem_attributes_internal(ctx, base_va, attr,
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
int xlat_change_mem_attributes_ctx(const xlat_ctx_t *ctx, uintptr_t base_va,
|
||||
size_t size, uint32_t attr)
|
||||
{
|
||||
/* Note: This implementation isn't optimized. */
|
||||
|
||||
assert(ctx != NULL);
|
||||
assert(ctx->initialized);
|
||||
|
||||
unsigned long long virt_addr_space_size =
|
||||
(unsigned long long)ctx->va_max_address + 1U;
|
||||
assert(virt_addr_space_size > 0U);
|
||||
|
||||
if (!IS_PAGE_ALIGNED(base_va)) {
|
||||
WARN("%s: Address 0x%lx is not aligned on a page boundary.\n",
|
||||
__func__, base_va);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (size == 0U) {
|
||||
WARN("%s: Size is 0.\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((size % PAGE_SIZE) != 0U) {
|
||||
WARN("%s: Size 0x%zx is not a multiple of a page size.\n",
|
||||
__func__, size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (((attr & MT_EXECUTE_NEVER) == 0U) && ((attr & MT_RW) != 0U)) {
|
||||
WARN("%s: Mapping memory as read-write and executable not allowed.\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
size_t pages_count = size / PAGE_SIZE;
|
||||
|
||||
VERBOSE("Changing memory attributes of %zu pages starting from address 0x%lx...\n",
|
||||
pages_count, base_va);
|
||||
|
||||
uintptr_t base_va_original = base_va;
|
||||
|
||||
/*
|
||||
* Sanity checks.
|
||||
*/
|
||||
for (unsigned int i = 0U; i < pages_count; ++i) {
|
||||
const uint64_t *entry;
|
||||
uint64_t desc, attr_index;
|
||||
unsigned int level;
|
||||
|
||||
entry = find_xlat_table_entry(base_va,
|
||||
ctx->base_table,
|
||||
ctx->base_table_entries,
|
||||
virt_addr_space_size,
|
||||
&level);
|
||||
if (entry == NULL) {
|
||||
WARN("Address 0x%lx is not mapped.\n", base_va);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
desc = *entry;
|
||||
|
||||
/*
|
||||
* Check that all the required pages are mapped at page
|
||||
* granularity.
|
||||
*/
|
||||
if (((desc & DESC_MASK) != PAGE_DESC) ||
|
||||
(level != XLAT_TABLE_LEVEL_MAX)) {
|
||||
WARN("Address 0x%lx is not mapped at the right granularity.\n",
|
||||
base_va);
|
||||
WARN("Granularity is 0x%lx, should be 0x%lx.\n",
|
||||
XLAT_BLOCK_SIZE(level), PAGE_SIZE);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the region type is device, it shouldn't be executable.
|
||||
*/
|
||||
attr_index = (desc >> ATTR_INDEX_SHIFT) & ATTR_INDEX_MASK;
|
||||
if (attr_index == ATTR_DEVICE_INDEX) {
|
||||
if ((attr & MT_EXECUTE_NEVER) == 0U) {
|
||||
WARN("Setting device memory as executable at address 0x%lx.",
|
||||
base_va);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
base_va += PAGE_SIZE;
|
||||
}
|
||||
|
||||
/* Restore original value. */
|
||||
base_va = base_va_original;
|
||||
|
||||
for (unsigned int i = 0U; i < pages_count; ++i) {
|
||||
|
||||
uint32_t old_attr = 0U, new_attr;
|
||||
uint64_t *entry = NULL;
|
||||
unsigned int level = 0U;
|
||||
unsigned long long addr_pa = 0ULL;
|
||||
|
||||
(void) xlat_get_mem_attributes_internal(ctx, base_va, &old_attr,
|
||||
&entry, &addr_pa, &level);
|
||||
|
||||
/*
|
||||
* From attr, only MT_RO/MT_RW, MT_EXECUTE/MT_EXECUTE_NEVER and
|
||||
* MT_USER/MT_PRIVILEGED are taken into account. Any other
|
||||
* information is ignored.
|
||||
*/
|
||||
|
||||
/* Clean the old attributes so that they can be rebuilt. */
|
||||
new_attr = old_attr & ~(MT_RW | MT_EXECUTE_NEVER | MT_USER);
|
||||
|
||||
/*
|
||||
* Update attributes, but filter out the ones this function
|
||||
* isn't allowed to change.
|
||||
*/
|
||||
new_attr |= attr & (MT_RW | MT_EXECUTE_NEVER | MT_USER);
|
||||
|
||||
/*
|
||||
* The break-before-make sequence requires writing an invalid
|
||||
* descriptor and making sure that the system sees the change
|
||||
* before writing the new descriptor.
|
||||
*/
|
||||
*entry = INVALID_DESC;
|
||||
#if !HW_ASSISTED_COHERENCY
|
||||
dccvac((uintptr_t)entry);
|
||||
#endif
|
||||
/* Invalidate any cached copy of this mapping in the TLBs. */
|
||||
xlat_arch_tlbi_va(base_va, ctx->xlat_regime);
|
||||
|
||||
/* Ensure completion of the invalidation. */
|
||||
xlat_arch_tlbi_va_sync();
|
||||
|
||||
/* Write new descriptor */
|
||||
*entry = xlat_desc(ctx, new_attr, addr_pa, level);
|
||||
#if !HW_ASSISTED_COHERENCY
|
||||
dccvac((uintptr_t)entry);
|
||||
#endif
|
||||
base_va += PAGE_SIZE;
|
||||
}
|
||||
|
||||
/* Ensure that the last descriptor writen is seen by the system. */
|
||||
dsbish();
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user