arch: arm: core: aarch32: introduce basic ARMv7 MMU support

An initial implementation for memory management using the ARMv7 MMU.
A single L1 translation table for the whole 4 GB address space is al-
ways present, a configurable number of L2 page tables are linked to
the L1 table based on the static memory area configuration at boot
time, or whenever arch_mem_map/arch_mem_unmap are called at run-time.

Currently, a CPU with the Multiprocessor Extensions and execution at
PL1 are always assumed. Userspace-related features or thread stack
guard pages are not yet supported. Neither are LPAE, PXN or TEX re-
mapping. All mappings are currently assigned to the same domain. Re-
garding the permissions model, access permissions are specified using
the AP[2:1] model rather than the older AP[2:0] model, which, accor-
ding to ARM's documentation, is deprecated and should no longer be
used. The newer model adds some complexity when it comes to mapping
pages as unaccessible (the AP[2:1] model doesn't support explicit
specification of "no R, no W" permissions, it's always at least "RO"),
this is accomplished by invalidating the ID bits of the respective
page's PTE.

Includes sources, Kconfig integration, adjusted CMakeLists and the
modified linker command file (proper section alignment!).

Signed-off-by: Immo Birnbaum <Immo.Birnbaum@weidmueller.com>
This commit is contained in:
Immo Birnbaum 2021-07-15 10:33:58 +02:00 committed by Carles Cufí
commit 70c403c215
10 changed files with 1475 additions and 2 deletions

View file

@ -27,6 +27,7 @@ add_subdirectory_ifdef(CONFIG_ARM_SECURE_FIRMWARE cortex_m/tz)
add_subdirectory_ifdef(CONFIG_ARM_NONSECURE_FIRMWARE cortex_m/tz)
add_subdirectory_ifdef(CONFIG_ARM_MPU mpu)
add_subdirectory_ifdef(CONFIG_ARM_AARCH32_MMU mmu)
add_subdirectory_ifdef(CONFIG_CPU_CORTEX_R cortex_a_r)

View file

@ -306,3 +306,4 @@ rsource "cortex_m/Kconfig"
rsource "cortex_a_r/Kconfig"
rsource "mpu/Kconfig"
rsource "mmu/Kconfig"

View file

@ -0,0 +1,5 @@
# SPDX-License-Identifier: Apache-2.0
zephyr_library()
zephyr_library_sources(arm_mmu.c)

View file

@ -0,0 +1,35 @@
#
# ARMv7 Memory Management Unit (MMU) configuration options
#
# Copyright (c) 2021 Weidmueller Interface GmbH & Co. KG
# SPDX-License-Identifier: Apache-2.0
#
if CPU_HAS_MMU
config ARM_AARCH32_MMU
bool "ARMv7 Cortex-A MMU Support"
default y if CPU_AARCH32_CORTEX_A
select MMU
select SRAM_REGION_PERMISSIONS
select THREAD_STACK_INFO
select ARCH_HAS_EXECUTABLE_PAGE_BIT
help
The current CPU has an ARMv7 Memory Management Unit.
config ARM_MMU_NUM_L2_TABLES
depends on ARM_AARCH32_MMU
int "Number of L2 translation tables available to the MMU"
default 64
help
Number of level 2 translation tables. Each level 2 table
covers 1 MB of address space.
config ARM_MMU_REGION_MIN_ALIGN_AND_SIZE
int
default 4096
help
Minimum size (and alignment) of an ARM MMU page.
This value should not be modified.
endif # CPU_HAS_MMU

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,205 @@
/*
* ARMv7 MMU support
*
* Private data declarations
*
* Copyright (c) 2021 Weidmueller Interface GmbH & Co. KG
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_ARCH_AARCH32_ARM_MMU_PRIV_H_
#define ZEPHYR_ARCH_AARCH32_ARM_MMU_PRIV_H_
/*
* Comp.:
* ARM Architecture Reference Manual, ARMv7-A and ARMv7-R edition
* ARM document ID DDI0406C Rev. d, March 2018
* L1 / L2 page table entry formats and entry type IDs:
* Chapter B3.5.1, fig. B3-4 and B3-5, p. B3-1323 f.
*/
#define ARM_MMU_PT_L1_NUM_ENTRIES 4096
#define ARM_MMU_PT_L2_NUM_ENTRIES 256
#define ARM_MMU_PTE_L1_INDEX_PA_SHIFT 20
#define ARM_MMU_PTE_L1_INDEX_MASK 0xFFF
#define ARM_MMU_PTE_L2_INDEX_PA_SHIFT 12
#define ARM_MMU_PTE_L2_INDEX_MASK 0xFF
#define ARM_MMU_PT_L2_ADDR_SHIFT 10
#define ARM_MMU_PT_L2_ADDR_MASK 0x3FFFFF
#define ARM_MMU_PTE_L2_SMALL_PAGE_ADDR_SHIFT 12
#define ARM_MMU_PTE_L2_SMALL_PAGE_ADDR_MASK 0xFFFFF
#define ARM_MMU_ADDR_BELOW_PAGE_GRAN_MASK 0xFFF
#define ARM_MMU_PTE_ID_INVALID 0x0
#define ARM_MMU_PTE_ID_L2_PT 0x1
#define ARM_MMU_PTE_ID_SECTION 0x2
#define ARM_MMU_PTE_ID_LARGE_PAGE 0x1
#define ARM_MMU_PTE_ID_SMALL_PAGE 0x2
#define ARM_MMU_PERMS_AP2_DISABLE_WR 0x2
#define ARM_MMU_PERMS_AP1_ENABLE_PL0 0x1
#define ARM_MMU_TEX2_CACHEABLE_MEMORY 0x4
#define ARM_MMU_TEX_CACHE_ATTRS_WB_WA 0x1
#define ARM_MMU_TEX_CACHE_ATTRS_WT_nWA 0x2
#define ARM_MMU_TEX_CACHE_ATTRS_WB_nWA 0x3
#define ARM_MMU_C_CACHE_ATTRS_WB_WA 0
#define ARM_MMU_B_CACHE_ATTRS_WB_WA 1
#define ARM_MMU_C_CACHE_ATTRS_WT_nWA 1
#define ARM_MMU_B_CACHE_ATTRS_WT_nWA 0
#define ARM_MMU_C_CACHE_ATTRS_WB_nWA 1
#define ARM_MMU_B_CACHE_ATTRS_WB_nWA 1
/*
* The following defines might vary if support for CPUs without
* the multiprocessor extensions was to be implemented:
*/
#define ARM_MMU_TTBR_IRGN0_BIT_MP_EXT_ONLY BIT(6)
#define ARM_MMU_TTBR_NOS_BIT BIT(5)
#define ARM_MMU_TTBR_RGN_OUTER_NON_CACHEABLE 0x0
#define ARM_MMU_TTBR_RGN_OUTER_WB_WA_CACHEABLE 0x1
#define ARM_MMU_TTBR_RGN_OUTER_WT_CACHEABLE 0x2
#define ARM_MMU_TTBR_RGN_OUTER_WB_nWA_CACHEABLE 0x3
#define ARM_MMU_TTBR_RGN_SHIFT 3
#define ARM_MMU_TTBR_SHAREABLE_BIT BIT(1)
#define ARM_MMU_TTBR_IRGN1_BIT_MP_EXT_ONLY BIT(0)
#define ARM_MMU_TTBR_CACHEABLE_BIT_NON_MP_ONLY BIT(0)
/* <-- end MP-/non-MP-specific */
#define ARM_MMU_DOMAIN_OS 0
#define ARM_MMU_DOMAIN_DEVICE 1
#define ARM_MMU_DACR_ALL_DOMAINS_CLIENT 0x55555555
#define ARM_MMU_SCTLR_AFE_BIT BIT(29)
#define ARM_MMU_SCTLR_TEX_REMAP_ENABLE_BIT BIT(28)
#define ARM_MMU_SCTLR_HA_BIT BIT(17)
#define ARM_MMU_SCTLR_ICACHE_ENABLE_BIT BIT(12)
#define ARM_MMU_SCTLR_DCACHE_ENABLE_BIT BIT(2)
#define ARM_MMU_SCTLR_CHK_ALIGN_ENABLE_BIT BIT(1)
#define ARM_MMU_SCTLR_MMU_ENABLE_BIT BIT(0)
#define ARM_MMU_L2_PT_INDEX(pt) ((uint32_t)pt - (uint32_t)l2_page_tables) /\
sizeof(struct arm_mmu_l2_page_table);
union arm_mmu_l1_page_table_entry {
struct {
uint32_t id : 2; /* [00] */
uint32_t bufferable : 1;
uint32_t cacheable : 1;
uint32_t exec_never : 1;
uint32_t domain : 4;
uint32_t impl_def : 1;
uint32_t acc_perms10 : 2;
uint32_t tex : 3;
uint32_t acc_perms2 : 1;
uint32_t shared : 1;
uint32_t not_global : 1;
uint32_t zero : 1;
uint32_t non_sec : 1;
uint32_t base_address : 12; /* [31] */
} l1_section_1m;
struct {
uint32_t id : 2; /* [00] */
uint32_t zero0 : 1; /* PXN if avail. */
uint32_t non_sec : 1;
uint32_t zero1 : 1;
uint32_t domain : 4;
uint32_t impl_def : 1;
uint32_t l2_page_table_address : 22; /* [31] */
} l2_page_table_ref;
struct {
uint32_t id : 2; /* [00] */
uint32_t reserved : 30; /* [31] */
} undefined;
uint32_t word;
};
struct arm_mmu_l1_page_table {
union arm_mmu_l1_page_table_entry entries[ARM_MMU_PT_L1_NUM_ENTRIES];
};
union arm_mmu_l2_page_table_entry {
struct {
uint32_t id : 2; /* [00] */
uint32_t bufferable : 1;
uint32_t cacheable : 1;
uint32_t acc_perms10 : 2;
uint32_t tex : 3;
uint32_t acc_perms2 : 1;
uint32_t shared : 1;
uint32_t not_global : 1;
uint32_t pa_base : 20; /* [31] */
} l2_page_4k;
struct {
uint32_t id : 2; /* [00] */
uint32_t bufferable : 1;
uint32_t cacheable : 1;
uint32_t acc_perms10 : 2;
uint32_t zero : 3;
uint32_t acc_perms2 : 1;
uint32_t shared : 1;
uint32_t not_global : 1;
uint32_t tex : 3;
uint32_t exec_never : 1;
uint32_t pa_base : 16; /* [31] */
} l2_page_64k;
struct {
uint32_t id : 2; /* [00] */
uint32_t reserved : 30; /* [31] */
} undefined;
uint32_t word;
};
struct arm_mmu_l2_page_table {
union arm_mmu_l2_page_table_entry entries[ARM_MMU_PT_L2_NUM_ENTRIES];
};
/*
* Data structure for L2 table usage tracking, contains a
* L1 index reference if the respective L2 table is in use.
*/
struct arm_mmu_l2_page_table_status {
uint32_t l1_index : 12;
uint32_t entries : 9;
uint32_t reserved : 11;
};
/*
* Data structure used to describe memory areas defined by the
* current Zephyr image, for which an identity mapping (pa = va)
* will be set up. Those memory areas are processed during the
* MMU initialization.
*/
struct arm_mmu_flat_range {
const char *name;
uint32_t start;
uint32_t end;
uint32_t attrs;
};
/*
* Data structure containing the memory attributes and permissions
* data derived from a memory region's attr flags word in the format
* required for setting up the corresponding PTEs.
*/
struct arm_mmu_perms_attrs {
uint32_t acc_perms : 2;
uint32_t bufferable : 1;
uint32_t cacheable : 1;
uint32_t not_global : 1;
uint32_t non_sec : 1;
uint32_t shared : 1;
uint32_t tex : 3;
uint32_t exec_never : 1;
uint32_t id_mask : 2;
uint32_t domain : 4;
uint32_t reserved : 15;
};
#endif /* ZEPHYR_ARCH_AARCH32_ARM_MMU_PRIV_H_ */
/* EOF */

View file

@ -35,6 +35,9 @@ extern void z_arm_configure_static_mpu_regions(void);
extern void z_arm_configure_dynamic_mpu_regions(struct k_thread *thread);
extern int z_arm_mpu_init(void);
#endif /* CONFIG_ARM_MPU */
#ifdef CONFIG_ARM_AARCH32_MMU
extern int z_arm_mmu_init(void);
#endif /* CONFIG_ARM_AARCH32_MMU */
static ALWAYS_INLINE void arch_kernel_init(void)
{
@ -52,7 +55,10 @@ static ALWAYS_INLINE void arch_kernel_init(void)
* This function is invoked once, upon system initialization.
*/
z_arm_configure_static_mpu_regions();
#endif
#endif /* CONFIG_ARM_MPU */
#if defined(CONFIG_ARM_AARCH32_MMU)
z_arm_mmu_init();
#endif /* CONFIG_ARM_AARCH32_MMU */
}
static ALWAYS_INLINE void

View file

@ -71,6 +71,8 @@ extern "C" {
*/
#if defined(CONFIG_USERSPACE)
#define Z_THREAD_MIN_STACK_ALIGN CONFIG_ARM_MPU_REGION_MIN_ALIGN_AND_SIZE
#elif defined(CONFIG_ARM_MMU)
#define Z_THREAD_MIN_STACK_ALIGN CONFIG_ARM_MMU_REGION_MIN_ALIGN_AND_SIZE
#else
#define Z_THREAD_MIN_STACK_ALIGN ARCH_STACK_PTR_ALIGN
#endif
@ -190,6 +192,9 @@ extern "C" {
#include <arch/arm/aarch32/mpu/nxp_mpu.h>
#endif /* CONFIG_CPU_HAS_NXP_MPU */
#endif /* CONFIG_ARM_MPU */
#ifdef CONFIG_ARM_AARCH32_MMU
#include <arch/arm/aarch32/mmu/arm_mmu.h>
#endif /* CONFIG_ARM_AARCH32_MMU */
#ifdef __cplusplus
}

View file

@ -58,6 +58,8 @@
*/
#if defined(CONFIG_ARM_MPU_REGION_MIN_ALIGN_AND_SIZE)
_region_min_align = CONFIG_ARM_MPU_REGION_MIN_ALIGN_AND_SIZE;
#elif defined(CONFIG_ARM_AARCH32_MMU)
_region_min_align = CONFIG_MMU_PAGE_SIZE;
#else
/* If building without MPU support, use default 4-byte alignment. */
_region_min_align = 4;
@ -72,6 +74,8 @@ _region_min_align = 4;
. = ALIGN(_region_min_align)
#endif
#define BSS_ALIGN ALIGN(_region_min_align)
MEMORY
{
FLASH (rx) : ORIGIN = ROM_ADDR, LENGTH = ROM_SIZE
@ -127,7 +131,11 @@ SECTIONS
SECTION_PROLOGUE(_TEXT_SECTION_NAME,,)
{
. = ALIGN(_region_min_align);
__text_region_start = .;
#ifndef CONFIG_XIP
z_mapped_start = .;
#endif
#include <linker/kobject-text.ld>
@ -144,6 +152,7 @@ SECTIONS
} GROUP_LINK_IN(ROMABLE_REGION)
__text_region_end = .;
. = ALIGN(_region_min_align);
#if defined (CONFIG_CPLUSPLUS)
SECTION_PROLOGUE(.ARM.extab,,)
@ -170,6 +179,7 @@ SECTIONS
__exidx_end = .;
} GROUP_LINK_IN(ROMABLE_REGION)
. = ALIGN(_region_min_align);
__rodata_region_start = .;
#include <linker/common-rom.ld>
@ -210,6 +220,7 @@ SECTIONS
__rodata_region_end = .;
__rom_region_end = .;
MPU_ALIGN(__rodata_region_end - __rom_region_start);
_image_rom_end_order = (LOG2CEIL(__rom_region_end) - 1) << 1;
GROUP_END(ROMABLE_REGION)
@ -234,6 +245,9 @@ SECTIONS
*/
. = ALIGN(_region_min_align);
_image_ram_start = .;
#ifdef CONFIG_XIP
z_mapped_start = .;
#endif
/* Located in generated directory. This file is populated by the
* zephyr_linker_sources() Cmake function.
@ -250,7 +264,7 @@ SECTIONS
_app_smem_rom_start = LOADADDR(_APP_SMEM_SECTION_NAME);
#endif /* CONFIG_USERSPACE */
SECTION_DATA_PROLOGUE(_BSS_SECTION_NAME,(NOLOAD),)
SECTION_DATA_PROLOGUE(_BSS_SECTION_NAME,(NOLOAD), BSS_ALIGN)
{
/*
* For performance, BSS section is assumed to be 4 byte aligned and
@ -316,8 +330,10 @@ SECTIONS
/* Define linker symbols */
. = ALIGN(_region_min_align);
_image_ram_end = .;
_end = .; /* end of image */
z_mapped_end = .;
__kernel_ram_end = RAM_ADDR + RAM_SIZE;
__kernel_ram_size = __kernel_ram_end - __kernel_ram_start;

View file

@ -0,0 +1,105 @@
/*
* ARMv7 MMU support
*
* Copyright (c) 2021 Weidmueller Interface GmbH & Co. KG
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_ARCH_AARCH32_ARM_MMU_H_
#define ZEPHYR_INCLUDE_ARCH_AARCH32_ARM_MMU_H_
#ifndef _ASMLANGUAGE
/*
* Comp.:
* ARM Architecture Reference Manual, ARMv7-A and ARMv7-R edition,
* ARM document ID DDI0406C Rev. d, March 2018
* Memory type definitions:
* Table B3-10, chap. B3.8.2, p. B3-1363f.
* Outer / inner cache attributes for cacheable memory:
* Table B3-11, chap. B3.8.2, p. B3-1364
*/
/*
* The following definitions are used when specifying a memory
* range to be mapped at boot time using the MMU_REGION_ENTRY
* macro.
*/
#define MT_STRONGLY_ORDERED BIT(0)
#define MT_DEVICE BIT(1)
#define MT_NORMAL BIT(2)
#define MT_MASK 0x7
#define MPERM_R BIT(3)
#define MPERM_W BIT(4)
#define MPERM_X BIT(5)
#define MPERM_UNPRIVILEGED BIT(6)
#define MATTR_NON_SECURE BIT(7)
#define MATTR_NON_GLOBAL BIT(8)
#define MATTR_SHARED BIT(9)
#define MATTR_CACHE_OUTER_WB_WA BIT(10)
#define MATTR_CACHE_OUTER_WT_nWA BIT(11)
#define MATTR_CACHE_OUTER_WB_nWA BIT(12)
#define MATTR_CACHE_INNER_WB_WA BIT(13)
#define MATTR_CACHE_INNER_WT_nWA BIT(14)
#define MATTR_CACHE_INNER_WB_nWA BIT(15)
#define MATTR_MAY_MAP_L1_SECTION BIT(16)
/*
* The following macros are used for adding constant entries
* mmu_regions array of the mmu_config struct. Use MMU_REGION_ENTRY
* for the specification of mappings whose PA and VA differ,
* the use of MMU_REGION_FLAT_ENTRY always results in an identity
* mapping, which are used for the mappings of the Zephyr image's
* code and data.
*/
#define MMU_REGION_ENTRY(_name, _base_pa, _base_va, _size, _attrs) \
{\
.name = _name, \
.base_pa = _base_pa, \
.base_va = _base_va, \
.size = _size, \
.attrs = _attrs, \
}
#define MMU_REGION_FLAT_ENTRY(name, adr, sz, attrs) \
MMU_REGION_ENTRY(name, adr, adr, sz, attrs)
/* Region definition data structure */
struct arm_mmu_region {
/* Region Base Physical Address */
uintptr_t base_pa;
/* Region Base Virtual Address */
uintptr_t base_va;
/* Region size */
size_t size;
/* Region Name */
const char *name;
/* Region Attributes */
uint32_t attrs;
};
/* MMU configuration data structure */
struct arm_mmu_config {
/* Number of regions */
uint32_t num_regions;
/* Regions */
const struct arm_mmu_region *mmu_regions;
};
/*
* Reference to the MMU configuration.
*
* This struct is defined and populated for each SoC (in the SoC definition),
* and holds the build-time configuration information for the fixed MMU
* regions enabled during kernel initialization.
*/
extern const struct arm_mmu_config mmu_config;
int z_arm_mmu_init(void);
#endif /* _ASMLANGUAGE */
#endif /* ZEPHYR_INCLUDE_ARCH_AARCH32_ARM_MMU_H_ */