* arch/arm64/src/common/arm64_mpu.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <debug.h>
#include <assert.h>
#include <nuttx/arch.h>
#include <arch/barriers.h>
#include <arch/irq.h>
#include <arch/chip/chip.h>
#include "arm64_arch.h"
#include "arm64_internal.h"
#include "arm64_fatal.h"
#include "arm64_mpu.h"
* Pre-processor Definitions
****************************************************************************/
#define __MPU_ASSERT(__cond, fmt, ...) \
do \
{ \
if (!(__cond)) \
{ \
sinfo(fmt, ## __VA_ARGS__); \
PANIC(); \
} \
} \
while (false)
* Provides information about the implemented memory model and memory
* management support in AArch64 state.
* See Arm Architecture Reference Manual Supplement
* Armv8, for Armv8-R AArch64 architecture profile, G1.3.7
*
* ID_AA64MMFR0_MSA_FRAC, bits[55:52]
* ID_AA64MMFR0_MSA, bits [51:48]
*/
#define ID_AA64MMFR0_MSA_MSK (0xFFUL << 48U)
#define ID_AA64MMFR0_PMSA_EN (0x1FUL << 48U)
#define ID_AA64MMFR0_PMSA_VMSA_EN (0x2FUL << 48U)
* have been reserved by the MPU driver to program the static (fixed) memory
* regions.
*/
static DEFINE_PER_CPU_BSS(unsigned int, g_mpu_region);
#define g_mpu_region this_cpu_var(g_mpu_region)
* Private Functions
****************************************************************************/
* Name: get_num_regions
*
* Description:
* Get the number of supported MPU regions.
*
* Input Parameters:
* None
*
* Returned Value:
* Numbers of the region.
*
****************************************************************************/
static inline unsigned int get_num_regions(void)
{
uint64_t type;
type = read_sysreg(mpuir_el1);
type = type & MPU_IR_REGION_MSK;
return (unsigned int)type;
}
* Name: mpu_init
*
* Description:
* Configure the cache-ability attributes for all the different types
* of memory regions.
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
static void mpu_init(void)
{
* Flash region(s): Attribute-1
* SRAM region(s): Attribute-2
* SRAM no cache-able regions(s): Attribute-3
*/
uint64_t mair = MPU_MAIR_ATTRS;
write_sysreg(mair, mair_el1);
UP_MB();
}
* Public Functions
****************************************************************************/
* Name: mpu_allocregion
*
* Description:
* Allocate the next region
*
* Input Parameters:
* None
*
* Returned Value:
* The index of the allocated region.
*
****************************************************************************/
unsigned int mpu_allocregion(void)
{
unsigned int num_regions = get_num_regions();
unsigned int i = ffs(~g_mpu_region) - 1;
DEBUGASSERT(i < num_regions);
g_mpu_region |= 1 << i;
return i;
}
* Name: mpu_allocregion
*
* Description:
* Get the number of MPU region used
*
* Input Parameters:
* None
*
* Returned Value:
* The the number of MPU region used
*
****************************************************************************/
unsigned int mpu_usedregion(void)
{
unsigned int n = g_mpu_region;
unsigned int count = 0;
while (n)
{
count += n & 1;
n >>= 1;
}
return count;
}
* Name: mpu_freeregion
*
* Description:
* Free target region.
*
* Input Parameters:
* region - The index of the region to be freed.
*
* Returned Value:
* None
*
****************************************************************************/
void mpu_freeregion(unsigned int region)
{
unsigned int num_regions = get_num_regions();
DEBUGASSERT(region < num_regions);
write_sysreg(region, prselr_el1);
UP_DSB();
* Have to set limit register first as the enable/disable bit of the
* region is in the limit register.
*/
write_sysreg(0, prlar_el1);
write_sysreg(0, prbar_el1);
g_mpu_region &= ~(1 << region);
UP_MB();
}
* Name: arm64_mpu_enable
*
* Description:
* Enable the MPU
*
* Input Parameters:
* None
*
* Return Value:
* None
*
****************************************************************************/
void arm64_mpu_enable(void)
{
uint64_t val;
val = read_sysreg(sctlr_el1);
val |= (SCTLR_M_BIT
#ifndef CONFIG_ARM64_DCACHE_DISABLE
| SCTLR_C_BIT
#endif
#ifdef CONFIG_ARM64_BACKGROUND_REGION
| SCTLR_BR_BIT
#endif
);
write_sysreg(val, sctlr_el1);
UP_MB();
}
* Name: arm64_mpu_disable
*
* Description:
* Disable the MPU
*
* Input Parameters:
* None
*
* Return Value:
* None
*
****************************************************************************/
void arm64_mpu_disable(void)
{
uint64_t val;
UP_DMB();
val = read_sysreg(sctlr_el1);
val &= ~(SCTLR_M_BIT | SCTLR_C_BIT);
write_sysreg(val, sctlr_el1);
UP_MB();
}
* Name: mpu_modify_region
*
* Description:
* Modify a region for privileged, strongly ordered memory
*
* Input Parameters:
* region - The index of the MPU region to modify.
* base - The base address of the region.
* size - The size of the region.
* flags1 - Additional flags for the region.
* flags2 - Additional flags for the region.
*
* Returned Value:
* None
*
****************************************************************************/
void mpu_modify_region(unsigned int region, uintptr_t base, size_t size,
uint32_t flags1, uint32_t flags2)
{
uint64_t rbar = base & MPU_RBAR_BASE_MSK;
uint64_t rlar = (base + size - 1) & MPU_RLAR_LIMIT_MSK;
DEBUGASSERT(g_mpu_region & (1 << region));
rbar |= flags1 &
(MPU_RBAR_XN_MSK | MPU_RBAR_AP_MSK | MPU_RBAR_SH_MSK);
rlar |=
(flags2 <<
MPU_RLAR_ATTRINDX_POS) & MPU_RLAR_ATTRINDX_MSK;
rlar |= MPU_RLAR_EN_MSK;
write_sysreg(region, prselr_el1);
UP_DSB();
write_sysreg(rbar, prbar_el1);
write_sysreg(rlar, prlar_el1);
UP_MB();
}
* Name: mpu_configure_region
*
* Description:
* Configure a region for privileged, strongly ordered memory
*
* Input Parameters:
* base - The base address of the region.
* size - The size of the region.
* flags1 - Additional flags for the region.
* flags2 - Additional flags for the region.
*
* Returned Value:
* The region number allocated for the configured region.
*
****************************************************************************/
unsigned int mpu_configure_region(uintptr_t base, size_t size,
uint32_t flags1, uint32_t flags2)
{
unsigned int region = mpu_allocregion();
mpu_modify_region(region, base, size, flags1, flags2);
return region;
}
* Name: mpu_dump_region
*
* Description:
* Dump the region that has been used.
*
* Input Parameters:
* None.
*
* Returned Value:
* None.
****************************************************************************/
void mpu_dump_region(void)
{
uint64_t sctlr_el1;
uint64_t prlar;
uint64_t prbar;
unsigned int num_regions;
int i;
int count = 0;
num_regions = get_num_regions();
sctlr_el1 = read_sysreg(sctlr_el1);
_info("MPU-SCTLR_EL1 Enable:%" PRIu64 ", Cacheable: %" PRIu64 "\n",
sctlr_el1 & SCTLR_M_BIT, sctlr_el1 & SCTLR_C_BIT);
for (i = 0; i < num_regions; i++)
{
write_sysreg(i, prselr_el1);
prlar = read_sysreg(prlar_el1);
prbar = read_sysreg(prbar_el1);
_info("MPU-%d, 0x%08llX-0x%08llX SH=%llX AP=%llX XN=%llX\n", i,
prbar & MPU_RBAR_BASE_MSK, prlar & MPU_RLAR_LIMIT_MSK,
prbar & MPU_RBAR_SH_MSK, prbar & MPU_RBAR_AP_MSK,
prbar & MPU_RBAR_XN_MSK);
if (prlar & MPU_RLAR_EN_MSK)
{
count++;
}
}
_info("Total Use Region:%d, Remaining Available:%d\n", count,
num_regions - count);
}
* Name: arm64_mpu_init
*
* Description:
* This function here provides the default configuration mechanism
* for the Memory Protection Unit (MPU).
*
* Input Parameters:
* is_primary_core: A boolean indicating whether the current core is the
* primary core.
*
* Returned Value:
* None
*
****************************************************************************/
void arm64_mpu_init(bool is_primary_core)
{
uint64_t val;
#ifdef CONFIG_MM_KASAN_SW_TAGS
val = read_sysreg(tcr_el1);
val |= (TCR_TBI0 | TCR_TBI1 | TCR_ASID_8);
write_sysreg(val, tcr_el1);
#endif
__asm__ volatile ("mrs %0, CurrentEL" : "=r" (val));
__MPU_ASSERT(GET_EL(
val) == MODE_EL1,
"Exception level not EL1, MPU not enabled!\n");
val = read_sysreg(id_aa64mmfr0_el1) & ID_AA64MMFR0_MSA_MSK;
if ((val != ID_AA64MMFR0_PMSA_EN) && (val != ID_AA64MMFR0_PMSA_VMSA_EN))
{
__MPU_ASSERT(0, "MPU not supported!\n");
return;
}
arm64_mpu_disable();
mpu_init();
arm64_mpu_init_regions();
arm64_mpu_enable();
}