Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions doc/guides/arch/arm_cortex_m.rst
Original file line number Diff line number Diff line change
Expand Up @@ -440,12 +440,45 @@ are programmed during system boot.
SRAM. (An exception to this setting is when :kconfig:option:`CONFIG_MPU_GAP_FILLING` is disabled (Arm v8-M only);
in that case no SRAM MPU programming is done so the access is determined by the default
Arm memory map policies, allowing for privileged-only RWX permissions on SRAM).
* All the memory regions defined in the devicetree with the compatible
:dtcompatible:`zephyr,memory-region` and at least the property
``zephyr,memory-region-mpu`` defining the MPU permissions for the memory region.
See the next section for more details.

The above MPU regions are defined in :file:`soc/arm/common/arm_mpu_regions.c`.
Alternative MPU configurations are allowed by enabling :kconfig:option:`CONFIG_CPU_HAS_CUSTOM_FIXED_SOC_MPU_REGIONS`.
When enabled, this option signifies that the Cortex-M SoC will define and
configure its own fixed MPU regions in the SoC definition.

Fixed MPU regions defined in devicetree
---------------------------------------

The user can define memory regions to be allocated and created in the linker
script using nodes with the :dtcompatible:`zephyr,memory-region` devicetree
compatible. When the property ``zephyr,memory-region-mpu`` is present in such
a node, a new MPU region will be allocated and programmed during system
boot.

The property ``zephyr,memory-region-mpu`` is a string carrying the attributes
for the MPU region. It is converted to a C token for use defining the attributes
of the MPU region.

For example, to define a new non-cacheable memory region in devicetree:

.. code-block:: devicetree

sram_no_cache: memory@20300000 {
compatible = "zephyr,memory-region", "mmio-sram";
reg = <0x20300000 0x100000>;
zephyr,memory-region = "SRAM_NO_CACHE";
zephyr,memory-region-mpu = "RAM_NOCACHE";
};

This will automatically create a new MPU entry in
:zephyr_file:`soc/arm/common/arm_mpu_regions.c` with the correct name, base,
size and attributes gathered directly from the devicetree. See
:zephyr_file:`include/linker/devicetree_regions.h` for more details.

Static MPU regions
------------------

Expand Down
12 changes: 12 additions & 0 deletions dts/bindings/base/zephyr,memory-region.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,15 @@ properties:
memory region in the final executable. The region address and size
is taken from the <reg> property, while the name is the value of
this property.

zephyr,memory-region-mpu:
type: string
required: false
enum:
- "RAM"
- "RAM_NOCACHE"
- "FLASH"
description: |
Signify that this node should result in a dedicated MPU region. The
region address and size are taken from the <reg> property, while the MPU
attribute is the value of this property.
106 changes: 106 additions & 0 deletions include/linker/devicetree_regions.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
#define _DT_SECTION_SIZE(node_id) UTIL_CAT(_DT_SECTION_PREFIX(node_id), _size)
#define _DT_SECTION_LOAD(node_id) UTIL_CAT(_DT_SECTION_PREFIX(node_id), _load_start)

#define _DT_ATTR(token) UTIL_CAT(UTIL_CAT(REGION_, token), _ATTR)

/**
* @brief Declare a memory region
*
Expand Down Expand Up @@ -74,6 +76,35 @@
_DT_SECTION_SIZE(node_id) = _DT_SECTION_END(node_id) - _DT_SECTION_START(node_id); \
_DT_SECTION_LOAD(node_id) = LOADADDR(_DT_SECTION_NAME(node_id));

/**
* Call the user-provided MPU_FN() macro passing the expected arguments
*/
#define _EXPAND_MPU_FN(node_id, MPU_FN, ...) \
MPU_FN(LINKER_DT_NODE_REGION_NAME(node_id), \
DT_REG_ADDR(node_id), \
DT_REG_SIZE(node_id), \
_DT_ATTR(DT_STRING_TOKEN(node_id, zephyr_memory_region_mpu))),

/**
* Check that the node_id has both properties:
* - zephyr,memory-region-mpu
* - zephyr,memory-region
*
* and call the EXPAND_MPU_FN() macro
*/
#define _CHECK_ATTR_FN(node_id, EXPAND_MPU_FN, ...) \
COND_CODE_1(UTIL_AND(DT_NODE_HAS_PROP(node_id, zephyr_memory_region_mpu), \
DT_NODE_HAS_PROP(node_id, zephyr_memory_region)), \
(EXPAND_MPU_FN(node_id, __VA_ARGS__)), \
())

/**
* Call _CHECK_ATTR_FN() for each enabled node passing EXPAND_MPU_FN() as
* explicit argument and the user-provided MPU_FN() macro in __VA_ARGS__
*/
#define _CHECK_APPLY_FN(compat, EXPAND_MPU_FN, ...) \
DT_FOREACH_STATUS_OKAY_VARGS(compat, _CHECK_ATTR_FN, EXPAND_MPU_FN, __VA_ARGS__)

/** @endcond */

/**
Expand All @@ -93,3 +124,78 @@
*/
#define LINKER_DT_SECTIONS() \
DT_FOREACH_STATUS_OKAY(_DT_COMPATIBLE, _SECTION_DECLARE)

/**
* @brief Generate MPU regions from the device tree nodes with compatible
* 'zephyr,memory-region' and 'zephyr,memory-region-mpu' attribute.
*
* Helper macro to apply an MPU_FN macro to all the memory regions declared
* using the 'zephyr,memory-region-mpu' property and the 'zephyr,memory-region'
* compatible.
*
* @p MPU_FN must take the form:
*
* @code{.c}
* #define MPU_FN(name, base, size, attr) ...
* @endcode
*
* The 'name', 'base' and 'size' parameters are taken from the DT node.
*
* The 'zephyr,memory-region-mpu' enum property is passed as an extended token
* to the MPU_FN macro using the 'attr' parameter, in the form
* REGION_{attr}_ATTR.
*
* Currently only three enums are supported for the 'zephyr,memory-region-mpu'
* property:
*
* - RAM
* - RAM_NOCACHE
* - FLASH
*
* This means that usually the arch code would provide some macros or defines
* with the same name of the extended property, that is:
*
* - REGION_RAM_ATTR
* - REGION_RAM_NOCACHE_ATTR
* - REGION_FLASH_ATTR
*
* Example devicetree fragment:
*
* / {
* soc {
* sram1: memory@2000000 {
* zephyr,memory-region = "MY_NAME";
* zephyr,memory-region-mpu = "RAM_NOCACHE";
* };
* };
* };
*
* The 'attr' parameter of the MPU_FN function will be the extended
* 'REGION_RAM_NOCACHE_ATTR' token and the arch code will be usually
* implementing a macro with the same name.
*
* Example:
*
* @code{.c}
*
* #define REGION_RAM_NOCACHE_ATTR 0xAAAA
* #define REGION_RAM_ATTR 0xBBBB
* #define REGION_FLASH_ATTR 0xCCCC
*
* #define MPU_FN(p_name, p_base, p_size, p_attr) \
* { \
* .name = p_name, \
* .base = p_base, \
* .size = p_size, \
* .attr = p_attr, \
* }
*
* static const struct arm_mpu_region mpu_regions[] = {
* ...
* LINKER_DT_REGION_MPU(MPU_FN)
* ...
* };
* @endcode
*
*/
#define LINKER_DT_REGION_MPU(mpu_fn) _CHECK_APPLY_FN(_DT_COMPATIBLE, _EXPAND_MPU_FN, mpu_fn)
42 changes: 42 additions & 0 deletions soc/arm/common/cortex_m/arm_mpu_mem_cfg.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,48 @@
#error "Unsupported sram size configuration"
#endif

#define MPU_REGION_SIZE_32 REGION_32B
#define MPU_REGION_SIZE_64 REGION_64B
#define MPU_REGION_SIZE_128 REGION_128B
#define MPU_REGION_SIZE_256 REGION_256B
#define MPU_REGION_SIZE_512 REGION_512B
#define MPU_REGION_SIZE_1024 REGION_1K
#define MPU_REGION_SIZE_2048 REGION_2K
#define MPU_REGION_SIZE_4096 REGION_4K
#define MPU_REGION_SIZE_8192 REGION_8K
#define MPU_REGION_SIZE_16384 REGION_16K
#define MPU_REGION_SIZE_32768 REGION_32K
#define MPU_REGION_SIZE_65536 REGION_64K
#define MPU_REGION_SIZE_131072 REGION_128K
#define MPU_REGION_SIZE_262144 REGION_256K
#define MPU_REGION_SIZE_524288 REGION_512K
#define MPU_REGION_SIZE_1048576 REGION_1M
#define MPU_REGION_SIZE_2097152 REGION_2M
#define MPU_REGION_SIZE_4194304 REGION_4M
#define MPU_REGION_SIZE_8388608 REGION_8M
#define MPU_REGION_SIZE_16777216 REGION_16M
#define MPU_REGION_SIZE_33554432 REGION_32M
#define MPU_REGION_SIZE_67108864 REGION_64M
#define MPU_REGION_SIZE_134217728 REGION_128M
#define MPU_REGION_SIZE_268435456 REGION_256M
#define MPU_REGION_SIZE_536870912 REGION_512M

#define MPU_REGION_SIZE(x) MPU_REGION_SIZE_ ## x

#define ARM_MPU_REGION_INIT(p_name, p_base, p_size, p_attr) \
{ .name = p_name, \
.base = p_base, \
.attr = p_attr(MPU_REGION_SIZE(p_size)), \
}

#else

#define ARM_MPU_REGION_INIT(p_name, p_base, p_size, p_attr) \
{ .name = p_name, \
.base = p_base, \
.attr = p_attr(p_base, p_size), \
}

#endif /* !ARMV8_M_BASELINE && !ARMV8_M_MAINLINE */

#endif /* _ARM_CORTEX_M_MPU_MEM_CFG_H_ */
4 changes: 4 additions & 0 deletions soc/arm/common/cortex_m/arm_mpu_regions.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <sys/slist.h>
#include <arch/arm/aarch32/mpu/arm_mpu.h>
#include <linker/devicetree_regions.h>

#include "arm_mpu_mem_cfg.h"

Expand All @@ -28,6 +29,9 @@ static const struct arm_mpu_region mpu_regions[] = {
#else
REGION_RAM_ATTR(REGION_SRAM_SIZE)),
#endif

/* DT-defined regions */
LINKER_DT_REGION_MPU(ARM_MPU_REGION_INIT)
};

const struct arm_mpu_config mpu_config = {
Expand Down
8 changes: 8 additions & 0 deletions tests/misc/arm_mpu_regions/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(arm_mpu_regions)

target_sources(app PRIVATE src/main.c)
41 changes: 41 additions & 0 deletions tests/misc/arm_mpu_regions/boards/mps2_an385.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2021 Carlo Caione <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0
*/

/ {
/delete-node/ memory@20000000;

sram0: memory@20000000 {
compatible = "mmio-sram";
reg = <0x20000000 0x200000>;
};

sram_cache: memory@20200000 {
compatible = "zephyr,memory-region", "mmio-sram";
reg = <0x20200000 0x100000>;
zephyr,memory-region = "SRAM_CACHE";
zephyr,memory-region-mpu = "RAM";
};

sram_no_cache: memory@20300000 {
compatible = "zephyr,memory-region", "mmio-sram";
reg = <0x20300000 0x100000>;
zephyr,memory-region = "SRAM_NO_CACHE";
zephyr,memory-region-mpu = "RAM_NOCACHE";
};

sram_dtcm_fake: memory@abcdabcd {
compatible = "zephyr,memory-region", "arm,dtcm";
reg = <0xabcdabcd 0x100000>;
zephyr,memory-region = "SRAM_DTCM_FAKE";
zephyr,memory-region-mpu = "RAM";
};

sram_no_mpu: memory@deaddead {
compatible = "zephyr,memory-region", "mmio-sram";
reg = <0xdeaddead 0x100000>;
zephyr,memory-region = "SRAM_NO_MPU";
};
};
1 change: 1 addition & 0 deletions tests/misc/arm_mpu_regions/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CONFIG_ZTEST=y
60 changes: 60 additions & 0 deletions tests/misc/arm_mpu_regions/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2021 Carlo Caione <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr.h>
#include <linker/linker-defs.h>
#include <sys/slist.h>
#include <arch/arm/aarch32/mpu/arm_mpu.h>
#include <ztest.h>
#include <string.h>

extern const struct arm_mpu_config mpu_config;

static arm_mpu_region_attr_t cacheable = REGION_RAM_ATTR(REGION_1M);
static arm_mpu_region_attr_t noncacheable = REGION_RAM_NOCACHE_ATTR(REGION_1M);

static void test_regions(void)
{
int cnt = 0;

for (size_t i = 0; i < mpu_config.num_regions; i++) {
const struct arm_mpu_region *r = &mpu_config.mpu_regions[i];

if (!strcmp(r->name, "SRAM_CACHE")) {
zassert_equal(r->base, 0x20200000, "Wrong base");
zassert_equal(r->attr.rasr, cacheable.rasr,
"Wrong attr for SRAM_CACHE");
cnt++;
} else if (!strcmp(r->name, "SRAM_NO_CACHE")) {
zassert_equal(r->base, 0x20300000, "Wrong base");
zassert_equal(r->attr.rasr, noncacheable.rasr,
"Wrong attr for SRAM_NO_CACHE");
cnt++;
} else if (!strcmp(r->name, "SRAM_DTCM_FAKE")) {
zassert_equal(r->base, 0xabcdabcd, "Wrong base");
zassert_equal(r->attr.rasr, cacheable.rasr,
"Wrong attr for SRAM_DTCM_FAKE");
cnt++;
}
}

if (cnt != 3) {
/*
* SRAM0 and SRAM_NO_MPU should not create any MPU region.
* Check that.
*/
ztest_test_fail();
}
}

void test_main(void)
{
ztest_test_suite(test_c_arm_mpu_regions,
ztest_unit_test(test_regions)
);

ztest_run_test_suite(test_c_arm_mpu_regions);
}
4 changes: 4 additions & 0 deletions tests/misc/arm_mpu_regions/testcase.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
tests:
misc.arm_mpu_regions:
platform_allow: mps2_an385
tags: sample board sram mpu