blob: a7ce7cabe23cbb57e5b5d5f8f38f2454f2b51750 [file] [log] [blame]
/****************************************************************************
* arch/arm/src/armv8-m/arm_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 <assert.h>
#include <debug.h>
#include <sys/param.h>
#include "mpu.h"
#include "arm_internal.h"
#include "barriers.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
#ifndef CONFIG_ARM_MPU_NREGIONS
# define CONFIG_ARM_MPU_NREGIONS 8
#endif
/****************************************************************************
* Private Data
****************************************************************************/
/* The available region bitmap */
static unsigned int g_mpu_region;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: mpu_reset_internal
*
* Description:
* Resets the MPU to disabled.
*
* Input Parameters:
* None.
*
* Returned Value:
* None.
*
****************************************************************************/
#if defined(CONFIG_ARM_MPU_RESET) || defined(CONFIG_ARM_MPU_EARLY_RESET)
static void mpu_reset_internal(void)
{
int region;
int regions;
regions = (getreg32(MPU_TYPE) & MPU_TYPE_DREGION_MASK)
>> MPU_TYPE_DREGION_SHIFT;
for (region = 0; region < regions; region++)
{
putreg32(region, MPU_RNR);
putreg32(0, MPU_RLAR);
putreg32(0, MPU_RBAR);
}
putreg32(0, MPU_CTRL);
ARM_DSB();
ARM_ISB();
}
#endif
/****************************************************************************
* 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 i = ffs(~g_mpu_region) - 1;
/* There are not enough regions to apply */
DEBUGASSERT(i < CONFIG_ARM_MPU_NREGIONS);
g_mpu_region |= 1 << i;
return i;
}
/****************************************************************************
* 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)
{
DEBUGASSERT(region < CONFIG_ARM_MPU_NREGIONS);
/* Clear and disable the given MPU Region */
putreg32(region, MPU_RNR);
putreg32(0, MPU_RLAR);
putreg32(0, MPU_RBAR);
g_mpu_region &= ~(1 << region);
ARM_DSB();
ARM_ISB();
}
/****************************************************************************
* Name: mpu_control
*
* Description:
* Configure and enable (or disable) the MPU
*
* Input Parameters:
* enable - Flag indicating whether to enable the MPU.
* hfnmiena - Flag indicating whether to enable the MPU during hard
* fult, NMI, and FAULTMAS.
* privdefena - Flag indicating whether to enable privileged access to
* the default memory map.
*
* Returned Value:
* None.
*
****************************************************************************/
void mpu_control(bool enable, bool hfnmiena, bool privdefena)
{
uint32_t regval = 0;
putreg32((MPU_MAIR_STRONGLY_ORDER << 0) |
(MPU_MAIR_DEVICE << 8) |
(MPU_MAIR_NONCACHEABLE << 16) |
(MPU_MAIR_WRITE_THROUGH << 24),
MPU_MAIR0);
putreg32((MPU_MAIR_WRITE_BACK << 0),
MPU_MAIR1);
if (enable)
{
regval |= MPU_CTRL_ENABLE; /* Enable the MPU */
if (hfnmiena)
{
regval |= MPU_CTRL_HFNMIENA; /* Enable MPU during hard fault, NMI, and FAULTMAS */
}
if (privdefena)
{
regval |= MPU_CTRL_PRIVDEFENA; /* Enable privileged access to default memory map */
}
}
putreg32(regval, MPU_CTRL);
/* Ensure MPU setting take effects */
ARM_DSB();
ARM_ISB();
}
/****************************************************************************
* 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)
{
uintptr_t limit;
uintptr_t rbase;
/* Check that the region is valid */
DEBUGASSERT(g_mpu_region & (1 << region));
/* Ensure the base address alignment
*
* ARMv8-M Architecture Reference Manual
* B3.5.8 MPU Region Base Address Register, MPU_RBAR
* "Software must ensure that the value written to the ADDR field
* aligns with the size of the selected region."
*/
limit = (base + size - 1) & MPU_RLAR_LIMIT_MASK;
rbase = base & MPU_RBAR_BASE_MASK;
/* Select the region */
putreg32(region, MPU_RNR);
/* Set the region base, limit and attribute */
putreg32(rbase | flags1, MPU_RBAR);
putreg32(limit | flags2 | MPU_RLAR_ENABLE, MPU_RLAR);
/* Ensure MPU setting take effects */
ARM_DSB();
ARM_ISB();
}
/****************************************************************************
* 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_initialize
*
* Description:
* Configure a region for privileged, strongly ordered memory
*
* Input Parameters:
* table - MPU initialization table.
* count - Initialize the number of entries in the region table.
* hfnmiena - A boolean indicating whether the MPU should enable the
* HFNMIENA bit.
* privdefena - A boolean indicating whether the MPU should enable the
* PRIVDEFENA bit.
*
* Returned Value:
* NULL.
*
****************************************************************************/
void mpu_initialize(const struct mpu_region_s *table, size_t count,
bool hfnmiena, bool privdefena)
{
const struct mpu_region_s *conf;
size_t index;
mpu_control(false, false, false);
for (index = 0; index < count; index++)
{
conf = &table[index];
mpu_configure_region(conf->base, conf->size, conf->flags1,
conf->flags2);
}
mpu_control(true, hfnmiena, privdefena);
}
/****************************************************************************
* Name: mpu_dump_region
*
* Description:
* Dump the region that has been used.
*
* Input Parameters:
* None.
*
* Returned Value:
* None.
*
****************************************************************************/
void mpu_dump_region(void)
{
int i;
int count = 0;
uint32_t rlar;
uint32_t rbar;
uint32_t ctrl;
/* Get free region */
ctrl = getreg32(MPU_CTRL);
_info("MPU-CTRL Enable:%" PRIu32 ", HFNMIENA:%"PRIu32","
"PRIVDEFENA:%" PRIu32 "\n", ctrl & MPU_CTRL_ENABLE,
ctrl & MPU_CTRL_HFNMIENA, ctrl & MPU_CTRL_PRIVDEFENA);
for (i = 0; i < CONFIG_ARM_MPU_NREGIONS; i++)
{
putreg32(i, MPU_RNR);
rlar = getreg32(MPU_RLAR);
rbar = getreg32(MPU_RBAR);
_info("MPU-%d, 0x%08"PRIx32"-0x%08"PRIx32" SH=%"PRIx32" AP=%"PRIx32""
"XN=%"PRIu32"\n", i, rbar & MPU_RBAR_BASE_MASK,
rlar & MPU_RLAR_LIMIT_MASK, rbar & MPU_RBAR_SH_MASK,
rbar & MPU_RBAR_AP_MASK, rbar & MPU_RBAR_XN);
if (rlar & MPU_RLAR_ENABLE)
{
count++;
}
}
_info("Total Use Region:%d, Remaining Available:%d\n", count,
CONFIG_ARM_MPU_NREGIONS - count);
}
/****************************************************************************
* Name: mpu_reset
*
* Description:
* Conditional public interface that resets the MPU to disabled during
* MPU initialization.
*
* Input Parameters:
* None.
*
* Returned Value:
* None.
*
****************************************************************************/
#if defined(CONFIG_ARM_MPU_RESET)
void mpu_reset(void)
{
mpu_reset_internal();
}
#endif
/****************************************************************************
* Name: mpu_early_reset
*
* Description:
* Conditional public interface that resets the MPU to disabled immediately
* after reset.
*
* Input Parameters:
* None.
*
* Returned Value:
* None.
*
****************************************************************************/
#if defined(CONFIG_ARM_MPU_EARLY_RESET)
void mpu_early_reset(void)
{
mpu_reset_internal();
}
#endif