diff --git a/bsp/stm32/stm32h750-artpi-h750/board/SConscript b/bsp/stm32/stm32h750-artpi-h750/board/SConscript index a3800f7b3fd..5c40142cb4f 100644 --- a/bsp/stm32/stm32h750-artpi-h750/board/SConscript +++ b/bsp/stm32/stm32h750-artpi-h750/board/SConscript @@ -37,6 +37,9 @@ if GetDepend(['BSP_USING_ETH_H750']): if GetDepend(['PKG_USING_EASYFLASH']): src += Glob('port/ef_fal_port.c') +if GetDepend(['RT_USING_MAL']): + src += Glob('port/mpu_port.c') + for item in list: if os.path.isfile(os.path.join(cwd, item, 'SConscript')): objs = objs + SConscript(os.path.join(item, 'SConscript')) diff --git a/components/Kconfig b/components/Kconfig index f99ede204f6..f0a62801476 100644 --- a/components/Kconfig +++ b/components/Kconfig @@ -39,6 +39,8 @@ source "$RTT_DIR/components/vbus/Kconfig" source "$RTT_DIR/components/utilities/Kconfig" +source "$RTT_DIR/components/mal/Kconfig" + source "$RTT_DIR/components/lwp/Kconfig" endmenu diff --git a/components/mal/Kconfig b/components/mal/Kconfig new file mode 100644 index 00000000000..79282c5026a --- /dev/null +++ b/components/mal/Kconfig @@ -0,0 +1,33 @@ +menu "MPU abstraction layer" + +config RT_USING_MAL + bool "Enable mpu abstraction layer" + default n + + if RT_USING_MAL + config RT_MPU_USING_THREAD_STACK_PROTECT + bool "Enable thread stack protect" + default y + + config RT_MPU_USING_LOG + bool "Enable mpu abstraction layer debug log" + default n + + config RT_MPU_HW_USED_REGIONS + int "Set hardware used mpu regions number" + default 4 + + config RT_MPU_PROTECT_AREA_REGIONS + int "Set mpu protect area regions number" + default 2 + + config RT_MPU_REGIONS_NUMBER + int "Set mpu regions number" + default 8 if ARCH_ARM_CORTEX_M3 + default 16 if ARCH_ARM_CORTEX_M4 + default 16 if ARCH_ARM_CORTEX_M7 + default 16 if ARCH_ARM_CORTEX_M23 + default 16 if ARCH_ARM_CORTEX_M33 + endif + +endmenu diff --git a/components/mal/SConscript b/components/mal/SConscript new file mode 100644 index 00000000000..098846103a1 --- /dev/null +++ b/components/mal/SConscript @@ -0,0 +1,19 @@ +Import('RTT_ROOT') +from building import * + +cwd = GetCurrentDir() +src = Glob('src/*.c') + +path = [cwd] +path += [cwd + '/inc'] + +if GetDepend('ARCH_ARM'): + src += Glob('arch/arm/*.c') + path += [cwd + '/arch/arm'] +elif GetDepend('ARCH_RISCV'): + src += Glob('arch/riscv/*.c') + path += [cwd + '/arch/riscv'] + +group = DefineGroup('Mal', src, depend = ['RT_USING_MAL'], CPPPATH = path) + +Return('group') diff --git a/components/mal/arch/arm/arm_mal.c b/components/mal/arch/arm/arm_mal.c new file mode 100644 index 00000000000..020b7717c15 --- /dev/null +++ b/components/mal/arch/arm/arm_mal.c @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-11-25 liukang first version + */ + +#include +#include + +#define DBG_TAG "mal.arm" +#ifdef RT_MAL_USING_LOG +#define DBG_LVL DBG_LOG +#else +#define DBG_LVL DBG_INFO +#endif +#include + +static rt_uint32_t _mpu_align_min(rt_uint32_t len) +{ + rt_uint32_t region_size, value = 4; + + /* 32 is the smallest region size, 31 is the largest valid value */ + for (region_size = RT_MPU_ALIGN_SMALLEST_SIZE; value < 31UL; region_size <<= 1UL) + { + if( len <= region_size) + { + break; + } + else + { + value++; + } + } + + return value; +} + +/* enable mpu */ +static void _mpu_enable(void) +{ + ARM_MPU_Enable(4); +} + +/* disable mpu */ +static void _mpu_disable(void) +{ + ARM_MPU_Disable(); +} + +static rt_uint32_t _mpu_rasr_value(int attribute, int size) +{ + rt_uint32_t execute, access, type_extern, shareable, cacheable, bufferable; + + execute = (attribute >> REGION_EXECUTE_Pos) & 0x1UL; + access = (attribute >> REGION_PERMISSION_Pos) & 0x7UL; + type_extern = (attribute >> REGION_TEX_Pos) & 0x7UL; + shareable = (attribute >> REGION_SHAREABLE_Pos) & 0x1UL; + cacheable = (attribute >> REGION_CACHEABLE_Pos) & 0x1UL; + bufferable = (attribute >> REGION_BUFFERABLE_Pos) & 0x1UL; + + return ARM_MPU_RASR(execute, access, type_extern, shareable, cacheable, bufferable, 0x00, size); +}; + +static void _mpu_get_register_value(uint32_t rnr, int* rbar, int* rasr) +{ + MPU->RNR = rnr; + *rbar = MPU->RBAR; + *rasr = MPU->RASR; +} + +static void _mpu_get_region_config(void *arg) +{ + int index = 0, rbar, rasr; + struct rt_mal_region *tables = (struct rt_mal_region *)arg; + + for (index = 0; index < RT_MPU_REGIONS_NUMBER; index++) + { + _mpu_get_register_value(index, &rbar, &rasr); + tables[index].addr = rbar & 0xFFFFFF80UL; + if (((rasr >> MPU_RASR_SIZE_Pos) & 0x1FUL) > 0) + { + tables[index].size = 1 << (((rasr >> MPU_RASR_SIZE_Pos) & 0x1FUL) + 1); + } + else + { + tables[index].size = 0; + } + + tables[index].attribute = rt_mpu_region_attribute((rasr >> MPU_RASR_AP_Pos) & 0x03UL, + (rasr >> MPU_RASR_XN_Pos) & 0x01UL, + (rasr >> MPU_RASR_S_Pos) & 0x01UL, + (rasr >> MPU_RASR_C_Pos) & 0x01UL, + (rasr >> MPU_RASR_B_Pos) & 0x01UL, + (rasr >> MPU_RASR_TEX_Pos) & 0x03UL); + } +} + +static rt_err_t _mpu_get_info(rt_thread_t thread, rt_uint32_t type, void *arg) +{ + rt_err_t result = RT_EOK; + if (thread == RT_NULL || arg == RT_NULL) + { + return -RT_ERROR; + } + + switch (type) + { + case GET_MPU_REGIONS_NUMBER: + *(int *)arg = thread->setting.index; + break; + + case GET_MPU_REGIONS_CONFGIG: + _mpu_get_region_config(arg); + default: + LOG_W("not support type: %d", type); + result = -RT_ERROR; + break; + } + + return result; +} + +static void _mpu_general_region_table_switch(rt_thread_t thread) +{ + int align_size, align_addr, rabr, rasr, index = 0; + rt_uint8_t region = 0; + +#ifdef RT_MPU_USING_THREAD_STACK_PROTECT + rt_uint32_t attribute = 0; + /* current thread stack is proteced? */ + LOG_D("thread: %s, stack_addr: %p, size: %d", thread->name, thread->stack_addr, RT_MPU_THREAD_PROTECT_SIZE); + attribute = rt_mpu_region_attribute(RT_MPU_REGION_PRIVILEGED_RO, + RT_MPU_REGION_EXECUTE_ENABLE, + RT_MPU_REGION_SHAREABLE_ENABLE, + RT_MPU_REGION_CACHEABLE_ENABLE, + RT_MPU_REGION_BUFFERABLE_ENABLE, + RT_MPU_REGION_TEX_DISABLE); + align_size = _mpu_align_min(RT_MPU_THREAD_PROTECT_SIZE); +#ifdef ARCH_CPU_STACK_GROWS_UPWARD + align_addr = (rt_uint32_t)RT_ALIGN_DOWN((rt_uint32_t)thread->stack_addr + thread->stack_size - 32, 1 << (align_size + 1)); +#else + align_addr = (rt_uint32_t)RT_ALIGN_DOWN((rt_uint32_t)thread->stack_addr, 1 << (align_size + 1)); +#endif + rabr = ARM_MPU_RBAR(RT_MPU_THREAD_STACK_REGION, align_addr); + rasr = _mpu_rasr_value(attribute, align_size); + ARM_MPU_SetRegion(rabr, rasr); +#else + ARM_MPU_ClrRegion(RT_MPU_THREAD_STACK_REGION); +#endif + + for (index = 0; index < thread->setting.index; index++) + { + region = index + RT_MPU_FIRST_CONFIGURABLE_REGION; + align_size = _mpu_align_min(thread->setting.tables[index].size); + align_addr = (rt_uint32_t)RT_ALIGN_DOWN((rt_uint32_t)thread->setting.tables[index].addr, 1 << (align_size + 1)); + rabr = ARM_MPU_RBAR((rt_uint32_t)region, align_addr); + rasr = _mpu_rasr_value(thread->setting.tables[index].attribute, align_size); /* MPU Region Attribute and Size Register Value */ + + ARM_MPU_SetRegion(rabr, rasr); + } + + /* Invalidate other region. */ + for (; index < RT_MPU_NUM_CONFIGURABLE_REGION; index++) + { + ARM_MPU_ClrRegion(RT_MPU_FIRST_CONFIGURABLE_REGION + index); + } +} + +static void _mpu_protect_region_table_switch(rt_thread_t thread, rt_uint8_t mpu_protect_area_num, + struct mpu_protect_regions* mpu_protect_areas) +{ + int align_size, align_addr, rabr, rasr, index = 0, region; + + for (index = 0; index < mpu_protect_area_num; index++) + { + region = index + RT_MPU_FIRST_PROTECT_AREA_REGION; + align_size = _mpu_align_min(mpu_protect_areas[index].tables.size); + align_addr = (rt_uint32_t)RT_ALIGN_DOWN((rt_uint32_t)mpu_protect_areas[index].tables.addr, 1 << (align_size + 1)); + + /* thread can access this region */ + if (mpu_protect_areas[index].thread == thread) + { + rabr = ARM_MPU_RBAR(region, align_addr); + rasr = _mpu_rasr_value(mpu_protect_areas[index].tables.attribute, align_size); /* MPU Region Attribute and Size Register Value */ + ARM_MPU_SetRegion(rabr, rasr); + } + /* thread can't access this region */ + else + { + int attribute = (mpu_protect_areas[index].tables.attribute & (~REGION_PERMISSION_Msk)) | ((RT_MPU_REGION_NO_ACCESS << REGION_PERMISSION_Pos) & REGION_PERMISSION_Msk); + rabr = ARM_MPU_RBAR(region, align_addr); + rasr = _mpu_rasr_value(attribute, align_size); + ARM_MPU_SetRegion(rabr, rasr); + } + } + + for (; index < mpu_protect_area_num; index++) + { + ARM_MPU_ClrRegion(index + RT_MPU_FIRST_PROTECT_AREA_REGION); + } +} + +static void _mpu_switch_table(rt_thread_t thread, rt_uint8_t mpu_protect_area_num, + struct mpu_protect_regions* mpu_protect_areas) +{ + RT_ASSERT(thread != RT_NULL); + + _mpu_disable(); + + _mpu_general_region_table_switch(thread); + +#ifdef RT_MPU_PROTECT_AREA_REGIONS + _mpu_protect_region_table_switch(thread, mpu_protect_area_num, mpu_protect_areas); +#endif + + _mpu_enable(); +} + +static rt_err_t _mpu_init(struct rt_mal_region *tables) +{ + int align_size, align_addr, rbar, rasr, index = 0; + int regions_number; + + _mpu_disable(); + + regions_number = MPU->TYPE >> 8 & 0xFF; + if (regions_number == 0) + { + LOG_E("this cpu not support mpu"); + return -RT_ERROR; + } + + if (regions_number != RT_MPU_REGIONS_NUMBER) + { + LOG_W("this cpu support regions number is: %d", regions_number); + } + + /* The only permitted number of regions are 8 or 16. */ + RT_ASSERT((RT_MPU_REGIONS_NUMBER == 8 ) || RT_MPU_REGIONS_NUMBER == 16); + if (tables == RT_NULL) + { + LOG_W("please init the system region first."); + return -RT_ERROR; + } + + for (index = 0; index < RT_MPU_HW_USED_REGIONS; index++) + { + if (tables[index].size > 0) + { + align_size = _mpu_align_min(tables[index].size); + align_addr = (rt_uint32_t)RT_ALIGN_DOWN((rt_uint32_t)tables[index].addr, 1 << (align_size + 1)); + + rbar = ARM_MPU_RBAR(index, align_addr); + rasr = _mpu_rasr_value(tables[index].attribute, align_size); + + ARM_MPU_SetRegion(rbar, rasr); + } + } + + _mpu_enable(); + + LOG_I("mpu init success."); + + return RT_EOK; +} + +static struct rt_mpu_ops _mpu_ops = +{ + .init = _mpu_init, + .switch_table = _mpu_switch_table, + .get_info = _mpu_get_info +}; + +static int _mpu_register(void) +{ + rt_err_t result = RT_EOK; + + result = rt_mpu_ops_register(&_mpu_ops); + if (result != RT_EOK) + { + LOG_E(" arm mal ops register failed"); + } + + return result; +} +INIT_BOARD_EXPORT(_mpu_register); diff --git a/components/mal/arch/arm/arm_mal.h b/components/mal/arch/arm/arm_mal.h new file mode 100644 index 00000000000..1e9bf9dff5b --- /dev/null +++ b/components/mal/arch/arm/arm_mal.h @@ -0,0 +1,16 @@ +#ifndef __ARM_MAL_H__ +#define __ARM_MAL_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/mal/arch/riscv/riscv_mal.c b/components/mal/arch/riscv/riscv_mal.c new file mode 100644 index 00000000000..4f378aa55fe --- /dev/null +++ b/components/mal/arch/riscv/riscv_mal.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-11-25 liukang first version + */ + +#include +#include + +#define DBG_TAG "mal.riscv" +#ifdef RT_MAL_USING_LOG +#define DBG_LVL DBG_LOG +#else +#define DBG_LVL DBG_INFO +#endif +#include + +static rt_err_t _mpu_get_info(rt_thread_t thread, rt_uint32_t type, void *arg) +{ + return RT_EOK; +} + +static void _mpu_switch_table(rt_thread_t thread, rt_uint8_t mpu_protect_area_num, + struct mpu_protect_regions* mpu_protect_areas) +{ + return; +} + +static rt_err_t _mpu_init(struct rt_mal_region *tables) +{ + return RT_EOK; +} + +static struct rt_mpu_ops _mpu_ops = +{ + .init = _mpu_init, + .switch_table = _mpu_switch_table, + .get_info = _mpu_get_info +}; + +static int _mpu_register(void) +{ + rt_err_t result = RT_EOK; + + result = rt_mpu_ops_register(&_mpu_ops); + if (result != RT_EOK) + { + LOG_E("riscv mal ops register failed"); + } + + return result; +} +INIT_BOARD_EXPORT(_mpu_register); diff --git a/components/mal/arch/riscv/riscv_mal.h b/components/mal/arch/riscv/riscv_mal.h new file mode 100644 index 00000000000..1af5c98e3e9 --- /dev/null +++ b/components/mal/arch/riscv/riscv_mal.h @@ -0,0 +1,16 @@ +#ifndef __RISCV_MAL_H__ +#define __RISCV_MAL_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/mal/inc/mal.h b/components/mal/inc/mal.h new file mode 100644 index 00000000000..66af7cb51b7 --- /dev/null +++ b/components/mal/inc/mal.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-11-26 liukang the first version + */ + +#ifndef __MAL_H__ +#define __MAL_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* MAL version information */ +#define RT_MAL_VERSION 1L /**< major version number */ +#define RT_MAL_SUBVERSION 0L /**< minor version number */ +#define RT_MAL_REVISION 0L /**< revise version number */ + +#ifndef RT_MPU_ALIGN_SMALLEST_SIZE +#define RT_MPU_ALIGN_SMALLEST_SIZE 32 /* mpu region smallest size */ +#endif + +#ifndef RT_MPU_THREAD_PROTECT_SIZE +#define RT_MPU_THREAD_PROTECT_SIZE 32 +#endif + +/* region attribute +-------------------------------------------- +32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 +| Reserved +-------------------------------------------- +17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + |bufferable |cacheable |shareable |tex |execute |permission +-------------------------------------------- +*/ +#define REGION_PERMISSION_Pos 0U /* Data access permissions, allows you to configure read/write access for User and Privileged mode */ +#define REGION_PERMISSION_Msk (0x7U << REGION_PERMISSION_Pos) + +#define REGION_EXECUTE_Pos 4U /* Instruction access disable bit */ +#define REGION_EXECUTE_Msk (0x1U << REGION_EXECUTE_Pos) + +#define REGION_TEX_Pos 5U /* Type extension field */ +#define REGION_TEX_Msk (0x7U << REGION_TEX_Pos) + +#define REGION_SHAREABLE_Pos 8U /* Region is shareable between multiple bus masters */ +#define REGION_SHAREABLE_Msk (0x1U << REGION_SHAREABLE_Pos) + +#define REGION_CACHEABLE_Pos 9U /* Region is cacheable */ +#define REGION_CACHEABLE_Msk (0x1U << REGION_CACHEABLE_Pos) + +#define REGION_BUFFERABLE_Pos 10U /* Region is bufferable */ +#define REGION_BUFFERABLE_Msk (0x1U << REGION_BUFFERABLE_Pos) + +/* access */ +#define RT_MPU_REGION_NO_ACCESS 0x0U +#define RT_MPU_REGION_PRIVILEGED_RW 0x1U +#define RT_MPU_REGION_PRIVILEGED_RW_UNPRIV_RO 0x2U +#define RT_MPU_REGION_RW 0x3U +#define RT_MPU_REGION_PRIVILEGED_RO 0x5U +#define RT_MPU_REGION_RO 0x6U + +/* execute */ +#define RT_MPU_REGION_EXECUTE_ENABLE 0x0U +#define RT_MPU_REGION_EXECUTE_DISABLE 0x1U + +/* shareable */ +#define RT_MPU_REGION_SHAREABLE_ENABLE 0x1U +#define RT_MPU_REGION_SHAREABLE_DISABLE 0x0U + +/* cacheable */ +#define RT_MPU_REGION_CACHEABLE_ENABLE 0x1U +#define RT_MPU_REGION_CACHEABLE_DISABLE 0x0U + +/* bufferable */ +#define RT_MPU_REGION_BUFFERABLE_ENABLE 0x1U +#define RT_MPU_REGION_BUFFERABLE_DISABLE 0x0U + +/* type_extern */ +#define RT_MPU_REGION_TEX_ENABLE 0x1U +#define RT_MPU_REGION_TEX_DISABLE 0x0U + + +#define RT_MPU_REGION_INVALID 0x0U +#define RT_MPU_REGION_VALID 0x1U + +#ifndef RT_MPU_HW_USED_REGIONS +#define RT_MPU_HW_USED_REGIONS 0x0U +#endif + +#ifdef RT_MPU_USING_THREAD_STACK_PROTECT +#define RT_MPU_THREAD_STACK_REGION (RT_MPU_HW_USED_REGIONS) /* Thread stack region */ +#define RT_MPU_FIRST_CONFIGURABLE_REGION (RT_MPU_THREAD_STACK_REGION + 1) /* User can configurable first region number */ +#else +#define RT_MPU_FIRST_CONFIGURABLE_REGION (RT_MPU_HW_USED_REGIONS + 1) /* User can configurable first region number */ +#endif + +#define RT_MPU_FIRST_PROTECT_AREA_REGION (RT_MPU_REGIONS_NUMBER - RT_MPU_HW_USED_REGIONS) +#define RT_MPU_NUM_CONFIGURABLE_REGION (RT_MPU_REGIONS_NUMBER - RT_MPU_FIRST_CONFIGURABLE_REGION - RT_MPU_HW_USED_REGIONS) /* number of regions that can be configured */ +#define RT_MPU_LAST_CONFIGURABLE_REGION (RT_MPU_REGIONS_NUMBER - RT_MPU_HW_USED_REGIONS - 1) + +struct mpu_regions +{ + rt_uint32_t addr; + rt_uint32_t size; + rt_uint32_t attribute; +}; + +struct mpu_protect_regions +{ + rt_thread_t thread; + struct rt_mal_region tables; +}; + +struct rt_mpu_ops +{ + rt_err_t (*init) (struct rt_mal_region *regions); + void (*switch_table) (rt_thread_t thread, rt_uint8_t mpu_protect_area_num, struct mpu_protect_regions* mpu_protect_areas); + rt_err_t (*get_info) (rt_thread_t thread, rt_uint32_t type, void *arg); +}; + +enum mpu_info_type { + GET_MPU_REGIONS_NUMBER = 0, + GET_MPU_REGIONS_CONFGIG, +}; + +/** +* MPU Region Attribute +* \param access Data access permissions, allows you to configure read/write access for User and Privileged mode. +* \param execute Instruction access disable bit, 1= disable instruction fetches. +* \param shareable Region is shareable between multiple bus masters. +* \param cacheable Region is cacheable, i.e. its value may be kept in cache. +* \param bufferable Region is bufferable, i.e. using write-back caching. Cacheable but non-bufferable regions use write-through policy. +* \param type_extern Type extension field, allows you to configure memory access type, for example strongly ordered, peripheral. +*/ +rt_inline rt_uint32_t rt_mpu_region_attribute(rt_uint32_t access, + rt_uint32_t execute, + rt_uint32_t shareable, + rt_uint32_t cacheable, + rt_uint32_t bufferable, + rt_uint32_t type_extern) +{ + rt_uint32_t attribute = 0; + + attribute = (((access << REGION_PERMISSION_Pos) & REGION_PERMISSION_Msk) | \ + ((execute << REGION_EXECUTE_Pos) & REGION_EXECUTE_Msk) | \ + ((type_extern << REGION_TEX_Pos ) & REGION_TEX_Msk) | \ + ((shareable << REGION_SHAREABLE_Pos ) & REGION_SHAREABLE_Msk) | \ + ((cacheable << REGION_CACHEABLE_Pos ) & REGION_CACHEABLE_Msk) | \ + ((bufferable << REGION_BUFFERABLE_Pos ) & REGION_BUFFERABLE_Msk)); + + return attribute; +}; + +/* Extern Fucntion */ +rt_err_t rt_mpu_init(struct rt_mal_region *tables); +rt_err_t rt_mpu_attach(rt_thread_t thread, void* addr, size_t size, rt_uint32_t attribute); +rt_err_t rt_mpu_attach_table(rt_thread_t thread, struct mpu_regions *regions); +rt_err_t rt_mpu_delete(rt_thread_t thread, rt_uint8_t region); +rt_err_t rt_mpu_refresh(rt_thread_t thread, void *addr, size_t size, rt_uint32_t attribute, rt_uint8_t region); +rt_err_t rt_mpu_insert(rt_thread_t thread, void *addr, size_t size, rt_uint32_t attribute, rt_uint8_t region); +rt_err_t rt_mpu_get_info(rt_thread_t thread, rt_uint32_t type, void *arg); +rt_err_t rt_mpu_enable_protect_area(rt_thread_t thread, void *addr, size_t size, rt_uint32_t attribute); +rt_err_t rt_mpu_disable_protect_area(rt_thread_t thread, rt_uint8_t region); +void rt_mpu_table_switch(rt_thread_t thread); +rt_err_t rt_mpu_ops_register(struct rt_mpu_ops *ops); + +void rt_mpu_exception_sethook(rt_thread_t thread, rt_err_t (*hook)(void* addr, rt_uint32_t attribute)); +rt_err_t rt_mpu_exception_handler(rt_thread_t thread, void* addr, rt_uint32_t attribute); + +#ifdef __cplusplus +} +#endif + +#endif /* __MAL_H__ */ diff --git a/components/mal/src/mal.c b/components/mal/src/mal.c new file mode 100644 index 00000000000..c10e8b094d4 --- /dev/null +++ b/components/mal/src/mal.c @@ -0,0 +1,415 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-11-01 liukang first version + */ + +#include +#include + +#define DBG_TAG "mal" +#ifdef RT_MAL_USING_LOG +#define DBG_LVL DBG_LOG +#else +#define DBG_LVL DBG_INFO +#endif +#include + +#ifdef RT_USING_MAL + +#ifdef RT_MPU_PROTECT_AREA_REGIONS +static struct mpu_protect_regions mpu_protect_areas[RT_MPU_PROTECT_AREA_REGIONS] = {0}; +#endif + +static rt_uint8_t mpu_protect_area_num = 0; +static struct rt_mpu_ops* mpu_ops = RT_NULL; +static rt_uint8_t init_ok = 0; + +rt_err_t rt_mpu_init(struct rt_mal_region *tables) +{ + rt_err_t result = RT_EOK; + if (init_ok) + { + LOG_W("mpu already init."); + return -RT_ERROR; + } + + if (mpu_ops->init) + { + result = mpu_ops->init(tables); + init_ok = 1; + } + else + { + LOG_W("please implement the mpu initialization function"); + return -RT_ERROR; + } + + return result; +} + +/** + * @brief This function will add a region configuration to the mpu configuration table. + * + * @param thread is the static thread object. + * + * @param addr is the pointer to protect memory. + * + * @param size is the length of the protect memory. + * + * @param attribute is the permission setting of the protect memory. + * + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. + */ +rt_err_t rt_mpu_attach(rt_thread_t thread, void* addr, size_t size, rt_uint32_t attribute) +{ + if (thread->setting.index >= RT_MPU_NUM_CONFIGURABLE_REGION) + { + LOG_E("no region can be configurable, current thread the configured regions number: %d", thread->setting.index); + return -RT_ERROR; + } + + thread->setting.tables[thread->setting.index].addr = (rt_uint32_t)addr; + thread->setting.tables[thread->setting.index].size = size; + thread->setting.tables[thread->setting.index].attribute = attribute; + LOG_D("thread: %s. attach region: %d success.", thread->name, thread->setting.tables[thread->setting.index + 1].region); + thread->setting.index += 1; + return RT_EOK; +} + +/** + * @brief This function will add multiple regions configuration to the mpu table. + * + * @param thread is the static thread object. + * + * @param regions are the multiple memmory configuration. + * + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. + */ +rt_err_t rt_mpu_attach_table(rt_thread_t thread, struct mpu_regions *regions) +{ + rt_uint8_t index = thread->setting.index, i = 0; + + if (regions == RT_NULL || thread->setting.index == RT_MPU_NUM_CONFIGURABLE_REGION) + { + LOG_E("no region can be configurable."); + return -RT_ERROR; + } + + for (; index < RT_MPU_NUM_CONFIGURABLE_REGION; index++) + { + if (regions[i].size > 0) + { + thread->setting.tables[thread->setting.index].addr = (rt_uint32_t)regions[i].addr; + thread->setting.tables[thread->setting.index].size = (rt_uint32_t)regions[i].size; + thread->setting.tables[thread->setting.index].attribute = regions[i].attribute; + thread->setting.index += 1; + } + else + { + /* invalidate the region */ + rt_memset(&thread->setting.tables[thread->setting.index], 0x00, sizeof(thread->setting.tables[thread->setting.index])); + } + i += 1; + } + + return RT_EOK; +} + +/** + * @brief This function will delete a region configuration from the mpu configuration table, + * + * @param thread is the static thread object. + * + * @param region is the region of mpu table , which shall be delete. + * + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. + */ +rt_err_t rt_mpu_delete(rt_thread_t thread, rt_uint8_t region) +{ + rt_uint8_t index = 0; + + if (region > RT_MPU_NUM_CONFIGURABLE_REGION || region < RT_MPU_FIRST_CONFIGURABLE_REGION - 1) + { + return -RT_ERROR; + } + + if (region > thread->setting.index + RT_MPU_FIRST_CONFIGURABLE_REGION) + { + return -RT_ERROR; + } + + index = region - RT_MPU_FIRST_CONFIGURABLE_REGION; + for (; index < RT_MPU_LAST_CONFIGURABLE_REGION; index++) + { + thread->setting.tables[index].addr = thread->setting.tables[index + 1].addr; + thread->setting.tables[index].size = thread->setting.tables[index + 1].size; + thread->setting.tables[index].attribute = thread->setting.tables[index + 1].attribute; + } + + thread->setting.index -= 1; + LOG_D("thread: %s. delete region: %d success.", thread->name, region); + + return RT_EOK; +} + +/** + * @brief This function will refresh a region configuration to the mpu configuration table. + * + * @param thread is the static thread object. + * + * @param addr is the pointer to protect memory. + * + * @param size is the length of the protect memory. + * + * @param attribute is the permission setting of the protect memory. + * + * @param region is the region of mpu table, which shall be refresh. + * + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. + */ +rt_err_t rt_mpu_refresh(rt_thread_t thread, void *addr, size_t size, rt_uint32_t attribute, rt_uint8_t region) +{ + rt_uint8_t index = 0; + + if (region > RT_MPU_NUM_CONFIGURABLE_REGION || region < RT_MPU_FIRST_CONFIGURABLE_REGION) + { + return -RT_ERROR; + } + + if (size == 0 || addr == RT_NULL || thread == RT_NULL) + { + return -RT_ERROR; + } + + index = region - RT_MPU_FIRST_CONFIGURABLE_REGION; + + thread->setting.tables[index].addr = (rt_uint32_t)addr; + thread->setting.tables[index].size = (rt_uint32_t)size; + thread->setting.tables[index].attribute = attribute; + + return RT_EOK; +} + +/** + * @brief This function will insert a region configuration to the mpu configuration table. + * + * @param thread is the static thread object. + * + * @param addr is the pointer to protect memory. + * + * @param size is the length of the protect memory. + * + * @param attribute is the permission setting of the protect memory. + * + * @param region is the region of mpu table, which shall be insert. + * + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. + */ +rt_err_t rt_mpu_insert(rt_thread_t thread, void *addr, size_t size, rt_uint32_t attribute, rt_uint8_t region) +{ + rt_uint8_t index = 0; + + if (region > RT_MPU_NUM_CONFIGURABLE_REGION || region < RT_MPU_FIRST_CONFIGURABLE_REGION) + { + return -RT_ERROR; + } + + if (size == 0 || addr == RT_NULL || thread == RT_NULL) + { + return -RT_ERROR; + } + + if (thread->setting.index == RT_MPU_NUM_CONFIGURABLE_REGION) + { + return -RT_ERROR; + } + + index = region - RT_MPU_FIRST_CONFIGURABLE_REGION; + + for(int i = 0; i < RT_MPU_NUM_CONFIGURABLE_REGION; i++) + { + if (index == i) + { + for (int j = RT_MPU_NUM_CONFIGURABLE_REGION-1; j > index; j--) + { + rt_memcpy(&thread->setting.tables[j], &thread->setting.tables[j - 1], sizeof(thread->setting.tables[j])); + } + thread->setting.tables[i].addr = (rt_uint32_t)addr; + thread->setting.tables[i].size = (rt_uint32_t)size; + thread->setting.tables[i].attribute = attribute; + } + } + + return RT_EOK; +} + +/** + * @brief This function configures that only the current thread can access a block of memory. + * + * @param thread is the static thread object. + * + * @param addr is the pointer to protect memory. + * + * @param size is the length of the protect memory. + * + * @param attribute is the permission setting of the protect memory. + * + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. + */ +rt_err_t rt_mpu_enable_protect_area(rt_thread_t thread, void *addr, size_t size, rt_uint32_t attribute) +{ + if (mpu_protect_area_num >= RT_MPU_PROTECT_AREA_REGIONS) + { + LOG_E("no protect area enable"); + return -RT_ERROR; + } + + mpu_protect_areas[mpu_protect_area_num].thread = thread; + + mpu_protect_areas[mpu_protect_area_num].tables.addr = (rt_uint32_t)addr; + mpu_protect_areas[mpu_protect_area_num].tables.size = (rt_uint32_t)size; + mpu_protect_areas[mpu_protect_area_num].tables.attribute = attribute; + mpu_protect_area_num += 1; + + return RT_EOK; +} + +/** + * @brief This function will removes a memory-protected area. + * + * @param thread is the static thread object. + * + * @param region is the region to be delete of the protect region tables. + * + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. + */ +rt_err_t rt_mpu_disable_protect_area(rt_thread_t thread, rt_uint8_t region) +{ + rt_uint8_t index = 0; + + if (mpu_protect_area_num == 0) + { + LOG_E("no protect area can be disable"); + return -RT_ERROR; + } + + for (index = 0; index < mpu_protect_area_num; index++) + { + if ((mpu_protect_areas[index].thread == thread) && ((index + RT_MPU_FIRST_PROTECT_AREA_REGION) == region)) + { + rt_memset(&mpu_protect_areas[index], 0x00, sizeof(mpu_protect_areas[index])); + mpu_protect_area_num -= 1; + break; + } + } + + return RT_EOK; +} + +/** + * @brief This function will get the info of mpu. + * + * @param thread is the static thread object. + * + * @param type is the type of the cmd. + * + * @param arg is the return value address. + * + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. + */ +rt_err_t rt_mpu_get_info(rt_thread_t thread, rt_uint32_t type, void *arg) +{ + if (mpu_ops->get_info) + { + return mpu_ops->get_info(thread, type, arg); + } + + LOG_W("rt_mpu_get_info ops not realize."); + + return -RT_ERROR; +} + +/** + * @brief This function will switch the MPU configuration table when the thread switch. + * + * @param thread is the static thread object. + */ +void rt_mpu_table_switch(rt_thread_t thread) +{ + if (mpu_ops->switch_table == RT_NULL) + { + LOG_E("mpu switch table ops is null."); + } + + mpu_ops->switch_table(thread, mpu_protect_area_num, mpu_protect_areas); +} + +/** + * @brief This function will switch the MPU configuration table when the thread switch. + * + * @param thread is the static thread object. + * + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. + */ +rt_err_t rt_mpu_exception_handler(rt_thread_t thread, void* addr, rt_uint32_t attribute) +{ + if (thread->mpu_hook) + { + return thread->mpu_hook(addr, attribute); + } + else + { + return -RT_ERROR; + } +} + +/** + * @brief This function will register a mpu exception hook of current thread. + * + * @param thread is the static thread object. + * + * @param hook is the mpu exception function. + */ +void rt_mpu_exception_sethook(rt_thread_t thread, rt_err_t (*hook)(void* addr, rt_uint32_t attribute)) +{ + if (thread->mpu_hook != RT_NULL) + { + LOG_W("thread: %s update mpu hook function", thread->name); + } + thread->mpu_hook = hook; +} + +/** + * @brief This function will register mpu ops. + * + * @param ops is the ops. + * + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. + */ +rt_err_t rt_mpu_ops_register(struct rt_mpu_ops *ops) +{ + rt_err_t result = RT_EOK; + + RT_ASSERT(ops != RT_NULL); + + mpu_ops = ops; + + return result; +} + +#endif /* RT_USING_MAL */ diff --git a/include/rtdef.h b/include/rtdef.h index 74db37555ae..cd324851eb1 100644 --- a/include/rtdef.h +++ b/include/rtdef.h @@ -598,6 +598,27 @@ struct rt_cpu #endif +#ifdef RT_USING_MAL + +#ifndef RT_MPU_REGIONS_NUMBER +#define RT_MPU_REGIONS_NUMBER 16 +#endif + +struct rt_mal_region +{ + rt_uint32_t addr; + rt_uint32_t size; + rt_uint32_t attribute; +}; + +struct rt_mal +{ + rt_uint16_t index; + struct rt_mal_region tables[RT_MPU_REGIONS_NUMBER]; +}; + +#endif + /** * Thread structure */ @@ -677,6 +698,12 @@ struct rt_thread #ifdef RT_USING_LWP void *lwp; #endif + + /* memory protect unit if present */ +#ifdef RT_USING_MAL + struct rt_mal setting; /**< mpu tables setting */ + rt_err_t (*mpu_hook)(void* addr, rt_uint32_t attribute); +#endif rt_ubase_t user_data; /**< private user data beyond this thread */ }; diff --git a/libcpu/arm/cortex-m7/context_rvds.S b/libcpu/arm/cortex-m7/context_rvds.S index d051f819c1f..ee7b1938120 100644 --- a/libcpu/arm/cortex-m7/context_rvds.S +++ b/libcpu/arm/cortex-m7/context_rvds.S @@ -127,6 +127,9 @@ PendSV_Handler PROC STR r1, [r0] ; update from thread stack pointer switch_to_thread + IMPORT rt_mpu_table_switch + IMPORT rt_current_thread + LDR r1, =rt_interrupt_to_thread LDR r1, [r1] LDR r1, [r1] ; load thread stack pointer @@ -150,6 +153,12 @@ switch_to_thread BICNE lr, lr, #0x10 ; lr &= ~(1 << 4), set FPCA. ENDIF + PUSH {r0-r3, r12, lr} + LDR r1, =rt_current_thread + LDR r0, [r1] + BL rt_mpu_table_switch ; switch mpu table + POP {r0-r3, r12, lr} + pendsv_exit ; restore interrupt MSR PRIMASK, r2 @@ -219,9 +228,7 @@ rt_hw_interrupt_thread_switch PROC IMPORT rt_hw_hard_fault_exception EXPORT HardFault_Handler - EXPORT MemManage_Handler HardFault_Handler PROC -MemManage_Handler ; get current context TST lr, #0x04 ; if(!EXC_RETURN[2]) diff --git a/libcpu/arm/cortex-m7/cpuport.c b/libcpu/arm/cortex-m7/cpuport.c index f24c33cf4d1..ccd9398fbbc 100644 --- a/libcpu/arm/cortex-m7/cpuport.c +++ b/libcpu/arm/cortex-m7/cpuport.c @@ -445,6 +445,14 @@ RT_WEAK void rt_hw_cpu_reset(void) SCB_AIRCR = SCB_RESET_VALUE; } +/** + * switch table + */ +RT_WEAK void rt_mpu_table_switch(rt_thread_t thread) +{ + return; +} + #ifdef RT_USING_CPU_FFS /** * This function finds the first bit set (beginning with the least significant bit)