arch: arm64: Armv8-R AArch64 MPU implementation
Armv8-R AArch64 MPU can support a maximum 16 memory regions, and the actual region number can be retrieved from the system register(MPUIR) during MPU initialization. Current MPU driver only suppots EL1. Signed-off-by: Haibo Xu <haibo.xu@arm.com> Signed-off-by: Jaxson Han <jaxson.han@arm.com>
This commit is contained in:
parent
475374ce2f
commit
30ed92c218
|
@ -23,6 +23,7 @@
|
||||||
/arch/arm/include/aarch32/cortex_m/cmse.h @ioannisg
|
/arch/arm/include/aarch32/cortex_m/cmse.h @ioannisg
|
||||||
/arch/arm/core/aarch32/cortex_a_r/ @MaureenHelm @galak @ioannisg @bbolen @stephanosio
|
/arch/arm/core/aarch32/cortex_a_r/ @MaureenHelm @galak @ioannisg @bbolen @stephanosio
|
||||||
/arch/arm64/ @carlocaione
|
/arch/arm64/ @carlocaione
|
||||||
|
/arch/arm64/core/cortex_r/ @povergoing
|
||||||
/arch/common/ @ioannisg @andyross
|
/arch/common/ @ioannisg @andyross
|
||||||
/soc/arc/snps_*/ @abrodkin @ruuddw @evgeniy-paltsev
|
/soc/arc/snps_*/ @abrodkin @ruuddw @evgeniy-paltsev
|
||||||
/soc/nios2/ @nashif
|
/soc/nios2/ @nashif
|
||||||
|
|
7
arch/arm64/core/cortex_r/mpu/CMakeLists.txt
Normal file
7
arch/arm64/core/cortex_r/mpu/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# Copyright (c) 2021 Arm Limited (or its affiliates). All rights reserved.
|
||||||
|
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
zephyr_library()
|
||||||
|
|
||||||
|
zephyr_library_sources(arm_mpu.c)
|
60
arch/arm64/core/cortex_r/mpu/Kconfig
Normal file
60
arch/arm64/core/cortex_r/mpu/Kconfig
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
# Memory Protection Unit (MPU) configuration options
|
||||||
|
|
||||||
|
#Copyright (c) 2017 Linaro Limited.
|
||||||
|
#Copyright (c) 2021 Arm Limited (or its affiliates). All rights reserved.
|
||||||
|
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
if CPU_HAS_MPU
|
||||||
|
|
||||||
|
config ARM_MPU
|
||||||
|
bool "ARM MPU Support"
|
||||||
|
select THREAD_STACK_INFO
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
MPU implements Memory Protection Unit.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
The ARMv8-R MPU architecture requires a power-of-two alignment
|
||||||
|
of MPU region base address and size(64 bytes aligned).
|
||||||
|
|
||||||
|
The ARMv8-R MPU requires the active MPU regions be non-overlapping.
|
||||||
|
As a result of this, the ARMv8-R MPU needs to fully partition the
|
||||||
|
memory map when programming dynamic memory regions (e.g. PRIV stack
|
||||||
|
guard, user thread stack, and application memory domains), if the
|
||||||
|
system requires PRIV access policy different from the access policy
|
||||||
|
of the ARMv8-R background memory map. The application developer may
|
||||||
|
enforce full PRIV (kernel) memory partition by enabling the
|
||||||
|
CONFIG_MPU_GAP_FILLING option.
|
||||||
|
By not enforcing full partition, MPU may leave part of kernel
|
||||||
|
SRAM area covered only by the default ARMv8-R memory map. This
|
||||||
|
is fine for User Mode, since the background ARM map does not
|
||||||
|
allow nPRIV access at all. However, since the background map
|
||||||
|
policy allows instruction fetches by privileged code, forcing
|
||||||
|
this Kconfig option off prevents the system from directly
|
||||||
|
triggering MemManage exceptions upon accidental attempts to
|
||||||
|
execute code from SRAM in XIP builds.
|
||||||
|
Since this does not compromise User Mode, we make the skipping
|
||||||
|
of full partitioning the default behavior for the ARMv8-R MPU
|
||||||
|
driver.
|
||||||
|
|
||||||
|
config ARM_MPU_REGION_MIN_ALIGN_AND_SIZE
|
||||||
|
int
|
||||||
|
default 64 if ARM_MPU
|
||||||
|
default 4
|
||||||
|
help
|
||||||
|
Minimum size (and alignment) of an ARM MPU region. Use this
|
||||||
|
symbol to guarantee minimum size and alignment of MPU regions.
|
||||||
|
A minimum 4-byte alignment is enforced in ARM builds without
|
||||||
|
support for Memory Protection.
|
||||||
|
|
||||||
|
if ARM_MPU
|
||||||
|
|
||||||
|
config MPU_ALLOW_FLASH_WRITE
|
||||||
|
bool "Add MPU access to write to flash"
|
||||||
|
help
|
||||||
|
Enable this to allow MPU RWX access to flash memory
|
||||||
|
|
||||||
|
endif # ARM_MPU
|
||||||
|
|
||||||
|
endif # CPU_HAS_MPU
|
187
arch/arm64/core/cortex_r/mpu/arm_mpu.c
Normal file
187
arch/arm64/core/cortex_r/mpu/arm_mpu.c
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2017 Linaro Limited.
|
||||||
|
* Copyright (c) 2021 Arm Limited (or its affiliates). All rights reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <device.h>
|
||||||
|
#include <init.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <soc.h>
|
||||||
|
#include <arch/arm64/cortex_r/mpu/arm_mpu.h>
|
||||||
|
#include <linker/linker-defs.h>
|
||||||
|
|
||||||
|
#define LOG_LEVEL CONFIG_MPU_LOG_LEVEL
|
||||||
|
#include <logging/log.h>
|
||||||
|
LOG_MODULE_DECLARE(mpu);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AArch64 Memory Model Feature Register 0
|
||||||
|
* 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)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Global status variable holding the number of HW MPU region indices, which
|
||||||
|
* have been reserved by the MPU driver to program the static (fixed) memory
|
||||||
|
* regions.
|
||||||
|
*/
|
||||||
|
static uint8_t static_regions_num;
|
||||||
|
|
||||||
|
/* Get the number of supported MPU regions. */
|
||||||
|
static inline uint8_t get_num_regions(void)
|
||||||
|
{
|
||||||
|
uint64_t type;
|
||||||
|
|
||||||
|
type = read_mpuir_el1();
|
||||||
|
type = type & MPU_IR_REGION_Msk;
|
||||||
|
|
||||||
|
return (uint8_t)type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ARM Core MPU Driver API Implementation for ARM MPU */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief enable the MPU
|
||||||
|
*/
|
||||||
|
void arm_core_mpu_enable(void)
|
||||||
|
{
|
||||||
|
uint64_t val;
|
||||||
|
|
||||||
|
val = read_sctlr_el1();
|
||||||
|
val |= SCTLR_M_BIT;
|
||||||
|
write_sctlr_el1(val);
|
||||||
|
dsb();
|
||||||
|
isb();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief disable the MPU
|
||||||
|
*/
|
||||||
|
void arm_core_mpu_disable(void)
|
||||||
|
{
|
||||||
|
uint64_t val;
|
||||||
|
|
||||||
|
/* Force any outstanding transfers to complete before disabling MPU */
|
||||||
|
dmb();
|
||||||
|
|
||||||
|
val = read_sctlr_el1();
|
||||||
|
val &= ~SCTLR_M_BIT;
|
||||||
|
write_sctlr_el1(val);
|
||||||
|
dsb();
|
||||||
|
isb();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ARM MPU Driver Initial Setup
|
||||||
|
*
|
||||||
|
* Configure the cache-ability attributes for all the
|
||||||
|
* different types of memory regions.
|
||||||
|
*/
|
||||||
|
static void mpu_init(void)
|
||||||
|
{
|
||||||
|
/* Device region(s): Attribute-0
|
||||||
|
* 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_mair_el1(mair);
|
||||||
|
dsb();
|
||||||
|
isb();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void mpu_set_region(uint32_t rnr, uint64_t rbar,
|
||||||
|
uint64_t rlar)
|
||||||
|
{
|
||||||
|
write_prselr_el1(rnr);
|
||||||
|
dsb();
|
||||||
|
write_prbar_el1(rbar);
|
||||||
|
write_prlar_el1(rlar);
|
||||||
|
dsb();
|
||||||
|
isb();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This internal functions performs MPU region initialization. */
|
||||||
|
static void region_init(const uint32_t index,
|
||||||
|
const struct arm_mpu_region *region_conf)
|
||||||
|
{
|
||||||
|
uint64_t rbar = region_conf->base & MPU_RBAR_BASE_Msk;
|
||||||
|
uint64_t rlar = (region_conf->limit - 1) & MPU_RLAR_LIMIT_Msk;
|
||||||
|
|
||||||
|
rbar |= region_conf->attr.rbar &
|
||||||
|
(MPU_RBAR_XN_Msk | MPU_RBAR_AP_Msk | MPU_RBAR_SH_Msk);
|
||||||
|
rlar |= (region_conf->attr.mair_idx << MPU_RLAR_AttrIndx_Pos) &
|
||||||
|
MPU_RLAR_AttrIndx_Msk;
|
||||||
|
rlar |= MPU_RLAR_EN_Msk;
|
||||||
|
|
||||||
|
mpu_set_region(index, rbar, rlar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief MPU default configuration
|
||||||
|
*
|
||||||
|
* This function provides the default configuration mechanism for the Memory
|
||||||
|
* Protection Unit (MPU).
|
||||||
|
*/
|
||||||
|
static int arm_mpu_init(const struct device *arg)
|
||||||
|
{
|
||||||
|
uint64_t val;
|
||||||
|
uint32_t r_index;
|
||||||
|
|
||||||
|
/* Current MPU code supports only EL1 */
|
||||||
|
val = read_currentel();
|
||||||
|
__ASSERT(GET_EL(val) == MODE_EL1,
|
||||||
|
"Exception level not EL1, MPU not enabled!\n");
|
||||||
|
|
||||||
|
/* Check whether the processor supports MPU */
|
||||||
|
val = read_id_aa64mmfr0_el1();
|
||||||
|
if ((val & ID_AA64MMFR0_MSA_msk) != ID_AA64MMFR0_PMSA_EN) {
|
||||||
|
__ASSERT(0, "MPU not supported!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mpu_config.num_regions > get_num_regions()) {
|
||||||
|
/* Attempt to configure more MPU regions than
|
||||||
|
* what is supported by hardware. As this operation
|
||||||
|
* is executed during system (pre-kernel) initialization,
|
||||||
|
* we want to ensure we can detect an attempt to
|
||||||
|
* perform invalid configuration.
|
||||||
|
*/
|
||||||
|
__ASSERT(0,
|
||||||
|
"Request to configure: %u regions (supported: %u)\n",
|
||||||
|
mpu_config.num_regions,
|
||||||
|
get_num_regions());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("total region count: %d", get_num_regions());
|
||||||
|
|
||||||
|
arm_core_mpu_disable();
|
||||||
|
|
||||||
|
/* Architecture-specific configuration */
|
||||||
|
mpu_init();
|
||||||
|
|
||||||
|
/* Program fixed regions configured at SOC definition. */
|
||||||
|
for (r_index = 0U; r_index < mpu_config.num_regions; r_index++) {
|
||||||
|
region_init(r_index, &mpu_config.mpu_regions[r_index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the number of programmed MPU regions. */
|
||||||
|
static_regions_num = mpu_config.num_regions;
|
||||||
|
|
||||||
|
arm_core_mpu_enable();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SYS_INIT(arm_mpu_init, PRE_KERNEL_1,
|
||||||
|
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|
Loading…
Reference in a new issue