arch: arc: Add mpu support

* add arc mpu driver
* modify the corresponding kconfig and kbuild
* currently only em_starterkit 2.2's em7d configuration
  has mpu feature (mpu version 2)
* as the minimum region size of arc mpu version 2 is 2048 bytes and
  region size should be power of 2, the stack size of threads
  (including main thread and idle thread) should be at least
  2048 bytes and power of 2
* for mpu stack guard feature, a stack guard region of 2048 bytes
  is generated. This brings more memory footprint
* For arc mpu version 3, the minimum region size is 32 bytes.
* the codes are tested by the mpu_stack_guard_test and stackprot

Signed-off-by: Wayne Ren <wei.ren@synopsys.com>
This commit is contained in:
Wayne Ren 2017-08-15 12:20:42 +08:00 committed by Anas Nashif
commit 12cc6598b0
16 changed files with 548 additions and 1 deletions

View file

@ -186,6 +186,21 @@ config FP_SHARING
endmenu
menu "ARC MPU Options"
depends on CPU_HAS_MPU
config ARC_MPU_ENABLE
bool "Enable MPU"
depends on CPU_HAS_MPU
select ARC_MPU
default n
help
Enable MPU
source "arch/arc/core/mpu/Kconfig"
endmenu
config ICCM_SIZE
int "ICCM Size in kB"
help

View file

@ -16,3 +16,5 @@ obj-$(CONFIG_IRQ_OFFLOAD) += irq_offload.o
# Some ARC cores like the EM4 lack the atomic LLOCK/SCOND and
# can't use these.
obj-$(CONFIG_ATOMIC_OPERATIONS_CUSTOM) += atomic.o
obj-$(CONFIG_CPU_HAS_MPU) += mpu/

View file

@ -257,6 +257,13 @@ _firq_reschedule:
*/
_load_callee_saved_regs
#ifdef CONFIG_MPU_STACK_GUARD
push_s r2
mov r0, r2
bl configure_mpu_stack_guard
pop_s r2
#endif
ld_s r3, [r2, _thread_offset_to_relinquish_cause]
breq r3, _CAUSE_RIRQ, _firq_return_from_rirq

38
arch/arc/core/mpu/Kconfig Normal file
View file

@ -0,0 +1,38 @@
# Kconfig - Memory Protection Unit (MPU) configuration options
#
# Copyright (c) 2017 Synopsys
#
# SPDX-License-Identifier: Apache-2.0
#
config ARC_MPU_VER
int
prompt "ARC MPU version"
range 2 4
default 2
help
ARC MPU has several versions. For MPU v2, the minimum region is 2048 bytes;
For MPU v3, the minimum region is 32 bytes
config ARC_CORE_MPU
bool "ARC Core MPU functionalities"
depends on CPU_HAS_MPU
select THREAD_STACK_INFO
default n
help
ARC Core MPU functionalities
config MPU_STACK_GUARD
bool "Thread Stack Guards"
depends on ARC_CORE_MPU
default n
help
Enable Thread Stack Guards via MPU
config ARC_MPU
bool "ARC MPU Support"
depends on CPU_HAS_MPU
select ARC_CORE_MPU
default n
help
Target has ARC MPU (currently only works for EMSK 2.2 ARCEM7D)

View file

@ -0,0 +1,2 @@
obj-$(CONFIG_ARC_CORE_MPU) += arc_core_mpu.o
obj-$(CONFIG_ARC_MPU) += arc_mpu.o

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2017 Synopsys.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <device.h>
#include <init.h>
#include <kernel.h>
#include <soc.h>
#include <arch/arc/v2/mpu/arc_core_mpu.h>
#if defined(CONFIG_MPU_STACK_GUARD)
/*
* @brief Configure MPU stack guard
*
* This function configures per thread stack guards reprogramming the MPU.
* The functionality is meant to be used during context switch.
*
* @param thread thread info data structure.
*/
void configure_mpu_stack_guard(struct k_thread *thread)
{
arc_core_mpu_disable();
arc_core_mpu_configure(THREAD_STACK_GUARD_REGION,
thread->stack_info.start - STACK_ALIGN,
thread->stack_info.size);
arc_core_mpu_enable();
}
#endif

201
arch/arc/core/mpu/arc_mpu.c Normal file
View file

@ -0,0 +1,201 @@
/*
* Copyright (c) 2017 Synopsys.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <device.h>
#include <init.h>
#include <kernel.h>
#include <soc.h>
#include <arch/arc/v2/aux_regs.h>
#include <arch/arc/v2/mpu/arc_mpu.h>
#include <arch/arc/v2/mpu/arc_core_mpu.h>
#include <logging/sys_log.h>
#define AUX_MPU_RDB_VALID_MASK (0x1)
#define AUX_MPU_EN_ENABLE (0x40000000)
#define AUX_MPU_EN_DISABLE (0x0)
#define AUX_MPU_RDP_REGION_SIZE(bits) \
(((bits - 1) & 0x3) | (((bits - 1) & 0x1C) << 7))
#define _ARC_V2_MPU_EN (0x409)
#define _ARC_V2_MPU_RDB0 (0x422)
#define _ARC_V2_MPU_RDP0 (0x423)
/* For MPU version 2, the minimum protection region size is 2048 bytes */
/* FOr MPU version 3, the minimum protection region size is 32 bytes */
#if CONFIG_ARC_MPU_VER == 2
#define ARC_FEATURE_MPU_ALIGNMENT_BITS 11
#elif CONFIG_ARC_MPU_VER == 3
#define ARC_FEATURE_MPU_ALIGNMENT_BITS 5
#endif
/**
* @brief Get the number of supported mpu regions
*
*/
static inline u8_t _get_num_regions(void)
{
u32_t num = _arc_v2_aux_reg_read(_ARC_V2_MPU_BUILD);
num = (num & 0xFF00) >> 8;
return (u8_t)num;
}
/**
* This internal function is utilized by the MPU driver to parse the intent
* type (i.e. THREAD_STACK_REGION) and return the correct parameter set.
*/
static inline u32_t _get_region_attr_by_type(u32_t type, u32_t size)
{
switch (type) {
case THREAD_STACK_REGION:
return 0;
case THREAD_STACK_GUARD_REGION:
/* no Read, Write and Execute to guard region */
return AUX_MPU_RDP_REGION_SIZE(
ARC_FEATURE_MPU_ALIGNMENT_BITS);
default:
/* Size 0 region */
return 0;
}
}
static inline void _region_init(u32_t index, u32_t region_addr,
u32_t region_attr)
{
index = 2 * index;
_arc_v2_aux_reg_write(_ARC_V2_MPU_RDP0 + index, region_attr);
_arc_v2_aux_reg_write(_ARC_V2_MPU_RDB0 + index, region_addr);
}
/* ARC Core MPU Driver API Implementation for ARC MPU */
/**
* @brief enable the MPU
*/
void arc_core_mpu_enable(void)
{
/* Enable MPU */
_arc_v2_aux_reg_write(_ARC_V2_MPU_EN, AUX_MPU_EN_ENABLE);
}
/**
* @brief disable the MPU
*/
void arc_core_mpu_disable(void)
{
/* Disable MPU */
_arc_v2_aux_reg_write(_ARC_V2_MPU_EN, AUX_MPU_EN_DISABLE);
}
/**
* @brief configure the base address and size for an MPU region
*
* @param type MPU region type
* @param base base address in RAM
* @param size size of the region
*/
void arc_core_mpu_configure(u8_t type, u32_t base, u32_t size)
{
u32_t region_index;
u32_t region_attr;
SYS_LOG_DBG("Region info: 0x%x 0x%x", base, size);
/*
* The new MPU regions are allocated per type before
* the statically configured regions.
*
* For ARC MPU v2, MPU regions can be overlapped, smaller
* region index has higher priority.
*/
region_index = _get_num_regions() - mpu_config.num_regions;
if (type > region_index) {
return;
}
region_index -= type;
region_attr = _get_region_attr_by_type(type, size);
if (region_attr == 0) {
return;
}
base |= AUX_MPU_RDB_VALID_MASK;
_region_init(region_index, base, region_attr);
}
/* ARC MPU Driver Initial Setup */
/*
* @brief MPU default configuration
*
* This function provides the default configuration mechanism for the Memory
* Protection Unit (MPU).
*/
static void _arc_mpu_config(void)
{
u32_t r_index;
u32_t num_regions;
u32_t i;
num_regions = _get_num_regions();
/* ARC MPU supports up to 16 Regions */
if (mpu_config.num_regions > num_regions) {
return;
}
/*
* the MPU regions are filled in the reverse order.
* According to ARCv2 ISA, the mpu region with smaller
* index has higher priority. The static background mpu
* regions in mpu_config will be in the bottom. Then
* the special type regions will be above.
*
*/
r_index = num_regions - mpu_config.num_regions;
/* Disable MPU */
arc_core_mpu_disable();
/* clear the regions reserved for special type */
for (i = 0; i < r_index; i++) {
_region_init(i, 0, 0);
}
/* configure the static regions */
for (r_index = 0; i < num_regions; i++) {
_region_init(i,
mpu_config.mpu_regions[r_index].base
| AUX_MPU_RDB_VALID_MASK,
mpu_config.mpu_regions[r_index].attr);
r_index++;
}
/* Enable MPU */
arc_core_mpu_enable();
}
static int arc_mpu_init(struct device *arg)
{
ARG_UNUSED(arg);
_arc_mpu_config();
return 0;
}
SYS_INIT(arc_mpu_init, PRE_KERNEL_1,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);

View file

@ -161,6 +161,13 @@ _rirq_common_interrupt_swap:
*/
_load_callee_saved_regs
#ifdef CONFIG_MPU_STACK_GUARD
push_s r2
mov r0, r2
bl configure_mpu_stack_guard
pop_s r2
#endif
ld_s r3, [r2, _thread_offset_to_relinquish_cause]
breq r3, _CAUSE_RIRQ, _rirq_return_from_rirq

View file

@ -110,6 +110,13 @@ SECTION_FUNC(TEXT, __swap)
_load_callee_saved_regs
#ifdef CONFIG_MPU_STACK_GUARD
push_s r2
mov r0, r2
bl configure_mpu_stack_guard
pop_s r2
#endif
ld_s r3, [r2, _thread_offset_to_relinquish_cause]
breq r3, _CAUSE_RIRQ, _swap_return_from_rirq

View file

@ -5,4 +5,6 @@ ccflags-y +=-I$(srctree)/drivers
asflags-y := ${ccflags-y}
obj-y = soc.o soc_config.o
obj-y += soc.o soc_config.o
obj-$(CONFIG_ARC_MPU_ENABLE) += arc_mpu_regions.o

View file

@ -1,3 +1,4 @@
config SOC_EM7D
bool "Synopsys ARC EM7D"
select CPU_HAS_MPU

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2017 Synopsys
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <soc.h>
#include <arch/arc/v2/mpu/arc_mpu.h>
static struct arc_mpu_region mpu_regions[] = {
#if CONFIG_ICCM_SIZE > 0
/* Region ICCM */
MPU_REGION_ENTRY("ICCM",
CONFIG_ICCM_BASE_ADDRESS,
REGION_FLASH_ATTR(REGION_256K)),
#endif
#if CONFIG_DCCM_SIZE > 0
/* Region DCCM */
MPU_REGION_ENTRY("DCCM",
CONFIG_DCCM_BASE_ADDRESS,
REGION_RAM_ATTR(REGION_128K)),
#endif
#if CONFIG_SRAM_SIZE > 0
/* Region DDR RAM */
MPU_REGION_ENTRY("DDR RAM",
CONFIG_SRAM_BASE_ADDRESS,
REGION_ALL_ATTR(REGION_128M)),
#endif
/* Region Peripheral */
MPU_REGION_ENTRY("PERIPHERAL",
0xF0000000,
REGION_IO_ATTR(REGION_64K)),
};
struct arc_mpu_config mpu_config = {
.num_regions = ARRAY_SIZE(mpu_regions),
.mpu_regions = mpu_regions,
};

View file

@ -16,6 +16,7 @@ choice
bool "2.2"
config BOARD_EM_STARTERKIT_R23
depends on (SOC_EM9D || SOC_EM11D)
bool "2.3"
endchoice

View file

@ -38,7 +38,37 @@ extern "C" {
#include <arch/arc/v2/addr_types.h>
#endif
#if defined(CONFIG_MPU_STACK_GUARD)
#if defined(CONFIG_ARC_CORE_MPU)
#if CONFIG_ARC_MPU_VER == 2
/* The minimum MPU region of MPU v2 is 2048 bytes */
#define STACK_ALIGN 2048
#elif CONFIG_ARC_MPU_VER == 3
#define STACK_ALIGN 32
#endif
#else /* CONFIG_ARC_CORE_MPU */
#error "Unsupported STACK_ALIGN"
#endif
#else /* CONFIG_MPU_STACK_GUARD */
#define STACK_ALIGN 4
#endif
#define _ARCH_THREAD_STACK_DEFINE(sym, size) \
struct _k_thread_stack_element __noinit __aligned(STACK_ALIGN) \
sym[size+STACK_ALIGN]
#define _ARCH_THREAD_STACK_ARRAY_DEFINE(sym, nmemb, size) \
struct _k_thread_stack_element __noinit __aligned(STACK_ALIGN) \
sym[nmemb][size+STACK_ALIGN]
#define _ARCH_THREAD_STACK_MEMBER(sym, size) \
struct _k_thread_stack_element __aligned(STACK_ALIGN) \
sym[size+STACK_ALIGN]
#define _ARCH_THREAD_STACK_SIZEOF(sym) (sizeof(sym) - STACK_ALIGN)
#define _ARCH_THREAD_STACK_BUFFER(sym) ((char *)(sym + STACK_ALIGN))
#ifdef __cplusplus
}

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2017 Synopsys.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ARC_CORE_MPU_H_
#define _ARC_CORE_MPU_H_
#ifdef __cplusplus
extern "C" {
#endif
/*
* The defines below represent the region types. The MPU driver is responsible
* to allocate the region accordingly to the type and set the correct
* attributes.
*
* Each MPU is different and has a different set of attributes, hence instead
* of having the attributes at this level the arm_mpu_core defines the intent
* types.
* An intent type (i.e. THREAD_STACK_GUARD) can correspond to a different set
* of operations and attributes for each MPU and it is responsibility of the
* MPU driver to select the correct ones.
*
* The intent based configuration can't fail hence at this level no error
* is returned by the configuration functions.
* If one of the operations corresponding to an intent fails the error has to
* be managed inside the MPU driver and not escalated.
*/
/* Thread Stack Region Intent Type */
#define THREAD_STACK_REGION 0x1
#define THREAD_STACK_GUARD_REGION 0x2
#if defined(CONFIG_ARC_CORE_MPU)
/* ARC Core MPU Driver API */
/*
* This API has to be implemented by all the MPU drivers that have
* ARC_CORE_MPU support.
*/
/**
* @brief enable the MPU
*/
void arc_core_mpu_enable(void);
/**
* @brief disable the MPU
*/
void arc_core_mpu_disable(void);
/**
* @brief configure the base address and size for an MPU region
*
* @param type MPU region type
* @param base base address in RAM
* @param size size of the region
*/
void arc_core_mpu_configure(u8_t type, u32_t base, u32_t size);
#endif /* CONFIG_ARC_CORE_MPU */
#ifdef __cplusplus
}
#endif
#endif /* _ARC_CORE_MPU_H_ */

View file

@ -0,0 +1,100 @@
/*
* Copyright (c) 2017 Synopsys.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ARC_MPU_H_
#define _ARC_MPU_H_
#define AUX_MPU_RDP_UE 0x008 /* allow user execution */
#define AUX_MPU_RDP_UW 0x010 /* allow user write */
#define AUX_MPU_RDP_UR 0x020 /* allow user read */
#define AUX_MPU_RDP_KE 0x040 /* only allow kernel execution */
#define AUX_MPU_RDP_KW 0x080 /* only allow kernel write */
#define AUX_MPU_RDP_KR 0x100 /* only allow kernel read */
/* Some helper defines for common regions */
#define REGION_RAM_ATTR(size) \
(AUX_MPU_RDP_UW | AUX_MPU_RDP_UR | \
AUX_MPU_RDP_KW | AUX_MPU_RDP_KR | \
size)
#define REGION_FLASH_ATTR(size) \
(AUX_MPU_RDP_UE | AUX_MPU_RDP_UR | \
AUX_MPU_RDP_KE | AUX_MPU_RDP_KR | \
size)
#define REGION_IO_ATTR(size) \
(AUX_MPU_RDP_UW | AUX_MPU_RDP_UR | \
AUX_MPU_RDP_KW | AUX_MPU_RDP_KR | \
size)
#define REGION_ALL_ATTR(size) \
(AUX_MPU_RDP_UW | AUX_MPU_RDP_UR | \
AUX_MPU_RDP_KW | AUX_MPU_RDP_KR | \
AUX_MPU_RDP_KE | AUX_MPU_RDP_UE | \
size)
#define REGION_32B 0x200
#define REGION_64B 0x201
#define REGION_128B 0x202
#define REGION_256B 0x203
#define REGION_512B 0x400
#define REGION_1K 0x401
#define REGION_2K 0x402
#define REGION_4K 0x403
#define REGION_8K 0x600
#define REGION_16K 0x601
#define REGION_32K 0x602
#define REGION_64K 0x603
#define REGION_128K 0x800
#define REGION_256K 0x801
#define REGION_512K 0x802
#define REGION_1M 0x803
#define REGION_2M 0xA00
#define REGION_4M 0xA01
#define REGION_8M 0xA02
#define REGION_16M 0xA03
#define REGION_32M 0xC00
#define REGION_64M 0xC01
#define REGION_128M 0xC02
#define REGION_256M 0xC03
#define REGION_512M 0xE00
#define REGION_1G 0xE01
#define REGION_2G 0xE02
#define REGION_4G 0xE03
/* Region definition data structure */
struct arc_mpu_region {
/* Region Base Address */
u32_t base;
/* Region Name */
const char *name;
/* Region Attributes */
u32_t attr;
};
#define MPU_REGION_ENTRY(_name, _base, _attr) \
{\
.name = _name, \
.base = _base, \
.attr = _attr, \
}
/* MPU configuration data structure */
struct arc_mpu_config {
/* Number of regions */
u32_t num_regions;
/* Regions */
struct arc_mpu_region *mpu_regions;
};
/* Reference to the MPU configuration */
extern struct arc_mpu_config mpu_config;
#endif /* _ARC_CORE_MPU_H_ */