armv8m 上的MPU 有8个region,每一个region都有起始地址,结束地址,访问权限和内存属性。每一个region都有单独的属性。
本文仅以secure MPU为例讲解MPU。
armv8m 上的MPU和armv7m的MPU有一点不同, v8m的MPU不支持overlap,如果一个地址同时出现在两个不同的region中,会导致bus fault。
Memory Type | Shareability | Other attributes | Description |
---|---|---|---|
Device-nGnRnE | Sharable | - | Used to access memory mapped peripherals.All accesses to DevicenGnRnE memory occur in program order. All regions are assumed to be shared |
Device-nGnRE | Sharable | - | Used to access memory mapped peripherals.Weaker ordering thanDevice-nGnRnE. |
Device-nGRE | Sharable | - | Used to access memory mapped peripherals.Weaker ordering thanDevice-nGnRE. |
Device-GRE | Sharable | - | Used to access memory mapped peripherals.Weaker ordering thanDevice-nGRE. |
Normal | Sharable | Non-cacheable Write-Through Cacheable Write-Back Cacheable | Normal memory that is shared between several processors. |
Normal | Non-Shareable | Non-cacheable Write-Through Cacheable Write-Back Cacheable | Normal memory that only a single processor uses. |
Address | Name | Access Type | Reset Value |
---|---|---|---|
0xE000ED90 | MPU_TYPE | RO | 该寄存器的值是固定的,取决于SOC实现 |
0xE000ED94 | MPU_CTRL | RW | 0x00000000 |
0xE000ED98 | MPU_RNR | RW | 未知 |
0xE000ED9C | MPU_RBAR | RW | 未知 |
0xE000EDA0 | MPU_RLAR | RW | 未知 |
0xE000EDA4 | MPU_RBAR_A |
RW | 未知 |
0xE000EDA8 | MPU_RBAR_A |
RW | 未知 |
0xE000EDC0 | MPU_MAIR0 | RW | 未知 |
0xE000EDC4 | MPU_MAIR1 | RW | 未知 |
MPU_TYPE寄存器用来表示MPU是否存在以及它支持多少个region。
Bits | Name | Function |
---|---|---|
[31:16] | - | 保留位 |
[15:8] | REGION | 0x0-MPU没有region支持 0x8当前MPU 支持8个REGION |
[7:1] | - | 保留位 |
[0] | SEPARATE | 0 |
MPU_CTRL用来
Bits | Name | Function |
---|---|---|
[31:3] | - | 保留位 |
[2] | PRIVDEFENA | 使能特权级软件使用default memory map 0 - 关闭default memory map。任何访问没有在region中描述的都会产生bus fault。 1 - 使能default memory map,当它使能后,访问没有在region中memory会使用默认的memory map |
[1] | HFNMIENA | 使HardFault和NMI时能MPU在 0 - 在HardFault和NMI时, MPU无效 1 - 在HardFault和NMI时, MPU仍然有效 |
[0] | ENABLE | 使能MPU 0 - 关闭MPU 1 - 打开MPU |
MPU_RNR用来选择region,在访问MPU_RBAR和MPU_RLAR之前,必须先写入MPU_RNR来选择region
Bits | Name | Function |
---|---|---|
[31:8] | - | 保留位 |
[7:0]REGION | REGION索引,如果选择REGION1, 则设为1 |
MPU_RBAR定义了MPU region的起始地址
Bits | Name | Function |
---|---|---|
[31:5] | BASE | REGION 基地址 |
[4:3] | SH | 定义了Normal的Shareability属性 0b00 Non-shareable. 0b01 UNPREDICATABLE 0b10 Outer shareable. 0b11 Inner shareable |
[2:1] | AP | 访问属性 0b00 特权级代码可读写. 0b01 特权级代码可读写 0b10 特权级代码只读. 0b11 所有代码只读 |
[0] | XN | 0 可执行Memory 1 不可执行Memory |
MPU_RLAR定义了REGION的上限地址以及REGION属性选择
Bits | Name | Function |
---|---|---|
[31:5] | LIMIT | REGION 上限地址 |
[4] | - | 保留位 |
[3:1] | AttrIndx | 属性索引号 |
[0] | EN | 0 不使能REGION 1 使能REGION |
MPU_MAIR0
Bits | Name | Function |
---|---|---|
[31:24] | Attr3 | REGION3属性 |
[23:16] | Attr2 | REGION2属性 |
[15:8] | Attr1 | REGION1属性 |
[7:0] | Attr0 | REGION0属性 |
MPU_MAIR1
Bits | Name | Function |
---|---|---|
[31:24] | Attr7 | REGION7属性 |
[23:16] | Attr6 | REGION6属性 |
[15:8] | Attr5 | REGION5属性 |
[7:0] | Attr4 | REGION4属性 |
每一个REGION属性MARI_ATTR占8位,如果MAIR_ATTR[7:4]为0:那MAIR_ATTR定义如下:
Bits | Name | Function |
---|---|---|
[3:2] | Device | 0b00 Device-nGnRnE 0b01 Device-nGnRE 0b10 Device-nGRE 0b11 Device-GRE |
如果MAIR_ATTR[7:4]不为0, 那么MAIR_ATTR定义如下
Bits | Name | Function |
---|---|---|
[7:4] | Outer | Outer attributes. Specifies the Outer memory attributes. The possible values of this field are: 0b0000 -Device memory. 00RW -Normal memory, Outer write-through transient (RW is not 00). 0b0100 -Normal memory, Outer non-cacheable. 01RW-Normal memory, Outer write-back transient (RW is not 00).10RW Normal memory, Outer write-through non-transient. 11RW -Normal memory, Outer write-back non-transient. R and W specify the outer read and write allocation policy: 0 = do not allocate, 1 = allocate. |
[3:0] | Inner | Inner attributes. Specifies the Inner memory attributes. The possible values of this field are: 0b0000 -UNPREDICTABLE. 00RW- Normal memory, Inner write-through transient (RW is not 00). 0b0100- Normal memory, Inner non-cacheable. 01RW -Normal memory, Inner write-back transient (RW is not 00). 10RW -Normal memory, Inner write-through non-transient. 11RW -Normal memory, Inner write-back non-transient. R and W specify the outer read and write allocation policy: 0 = do not allocate, 1 = allocate. |
代码git
MPS2开发板手册
安装qemu-system-arm, 确保该版本下有cortex m33的仿真板
mps2-an385 ARM MPS2 with AN385 FPGA image for Cortex-M3
mps2-an505 ARM MPS2 with AN505 FPGA image for Cortex-M33
mps2-an511 ARM MPS2 with AN511 DesignStart FPGA image for Cortex-M3
mps2-an521 ARM MPS2 with AN521 FPGA image for dual Cortex-M33
安装工具链armv8l-linux-gnueabihf-
运行命令:
make;make qemu
log:
qemu-system-arm -machine mps2-an505 -cpu cortex-m33 \
-m 4096 \
-nographic \
-kernel kernel.elf
qemu-system-arm: invalid accelerator kvm
qemu-system-arm: falling back to tcg
hello cortex m33
test_func_print entry
test_armv8m_xn:mpu setup done
default_exception_handler entry
#ifndef _ARMV8M_MPU_H_
#define _ARMV8M_MPU_H_
#include
#include
typedef struct armv8m_mpu_tag {
volatile uint32_t mpu_type;
volatile uint32_t mpu_ctrl;
volatile uint32_t mpu_rnr;
volatile uint32_t mpu_rbar;
volatile uint32_t mpu_rlar;
volatile uint32_t reserved[7];
volatile uint32_t mpu_mair0;
volatile uint32_t mpu_mair1;
} armv8m_mpu_t;
#define MPU_CTRL_ENABLE_SHITF 0UL
#define MPU_CTRL_ENABLE_MASK (1UL << MPU_CTRL_ENABLE_SHITF)
#define MPU_CTRL_HFNMIENA_SHIFT 1UL
#define MPU_CTRL_HFNMIENA_MASK (1UL << MPU_CTRL_HFNMIENA_SHIFT)
#define MPU_CTRL_PRIVDEFENA_SHIFT 2UL
#define MPU_CTRL_PRIVDEFENA_MASK (1UL << MPU_CTRL_PRIVDEFENA_SHIFT)
#define MPU_RNR_REGION_SHIFT 0UL
#define MPU_RNR_REGION_MASK (0xFFUL << MPU_RNR_REGION_SHIFT)
#define MPU_CTRL_RBAR_BASE_SHIFT 0x5UL
#define MPU_CTRL_RBAR_BASE_MASK 0xFFFFFFE0UL
#define MPU_CTRL_RBAR_SH_SHIFT 3UL
#define MPU_CTRL_RBAR_SH_MASK (0x3UL << MPU_CTRL_RBAR_SH_SHIFT)
#define MPU_CTRL_RBAR_AP_SHIFT 1UL
#define MPU_CTRL_RBAR_AP_MASK (0x3UL << MPU_CTRL_RBAR_AP_SHIFT)
#define MPU_CTRL_RBAR_XN_SHIFT 0UL
#define MPU_CTRL_RBAR_XN_MASK (1UL << MPU_CTRL_RBAR_XN_SHIFT)
#define MPU_CTRL_RLAR_LIMIT_SHIFT 5UL
#define MPU_CTRL_RLAR_LIMIT_MASK 0xFFFFFFE0UL
#define MPU_CTRL_RLAR_ATTRIDX_SHIFT 1UL
#define MPU_CTRL_RLAR_ATTRIDX_MASK (0x7UL << MPU_CTRL_RLAR_ATTRIDX_SHIFT)
#define MPU_CTRL_RLAR_EN_SHIFT 0UL
#define MPU_CTRL_RLAR_EN_MASK (1UL << MPU_CTRL_RLAR_EN_SHIFT)
#define REGION_NON_SHAREABLE 0
#define REGION_UNPREDICTABLE 1
#define REGION_OUTER_SHAREABLE 2
#define REGION_INNER_SHAREABLE 3
#define REGION_RW_PRIV_ONLY 0
#define REGION_RW_ANY 1
#define REGION_RO_PRIV_ONLY 2
#define REGION_RO_ANY 3
#define REGION_X 0
#define REGION_XN 1
#define REGION_DISABLE 0
#define REGION_EN 1
static inline void mpu_disable(armv8m_mpu_t *mpu)
{
mpu->mpu_ctrl &= ~MPU_CTRL_ENABLE_MASK;
__asm("dsb");
__asm("isb");
}
static inline void mpu_enable(armv8m_mpu_t *mpu)
{
mpu->mpu_ctrl |= MPU_CTRL_ENABLE_MASK;
__asm("dsb");
__asm("isb");
}
static inline void mpu_hfnmiena_enable(armv8m_mpu_t *mpu)
{
mpu->mpu_ctrl |= MPU_CTRL_HFNMIENA_MASK;
}
static inline void mpu_hfnmiena_disable(armv8m_mpu_t *mpu)
{
mpu->mpu_ctrl &= ~MPU_CTRL_HFNMIENA_MASK;
}
static inline void mpu_privdefena_enable(armv8m_mpu_t *mpu)
{
mpu->mpu_ctrl |= MPU_CTRL_PRIVDEFENA_MASK;
}
static inline void mpu_privdefena_disable(armv8m_mpu_t *mpu)
{
mpu->mpu_ctrl &= ~MPU_CTRL_PRIVDEFENA_MASK;
}
static inline void mpu_select_region(armv8m_mpu_t *mpu, uint8_t region)
{
mpu->mpu_rnr |= ((region << MPU_RNR_REGION_SHIFT) & MPU_RNR_REGION_MASK);
}
static inline void mpu_set_region_base(armv8m_mpu_t *mpu, uint32_t base,
uint8_t share_att, uint8_t ap_att, uint32_t is_xn)
{
mpu->mpu_rbar = 0;
mpu->mpu_rbar = (base & MPU_CTRL_RBAR_BASE_MASK) |
((share_att << MPU_CTRL_RBAR_SH_SHIFT) & MPU_CTRL_RBAR_SH_MASK) |
((ap_att << MPU_CTRL_RBAR_AP_SHIFT) & MPU_CTRL_RBAR_AP_MASK) |
(is_xn & MPU_CTRL_RBAR_XN_MASK);
}
static inline void mpu_set_region_limit(armv8m_mpu_t *mpu, uint32_t limit,
uint8_t att_idx, uint8_t en)
{
mpu->mpu_rlar = 0;
mpu->mpu_rlar = (limit & MPU_CTRL_RLAR_LIMIT_MASK) |
((att_idx << MPU_CTRL_RLAR_ATTRIDX_SHIFT) & MPU_CTRL_RLAR_ATTRIDX_MASK) |
(en & MPU_CTRL_RLAR_EN_MASK);
}
mpu->mpu_mair1 &= ~(0xFF << ((idx - 4) * 8));
mpu->mpu_mair1 |= (attr << ((idx - 4) * 8));
}
}
#endif
//测试MPU是否关闭region写权限
void test_armv8m_mpu_write()
{
volatile uint32_t *temp_addr = (volatile uint32_t *)0x30001000UL;
//关闭MPU前可以在Memory进行读写
easy_printf("0x30001000:%x\n", *temp_addr);
*temp_addr = 0x1;
easy_printf("0x30001000:%x\n", *temp_addr);
armv8m_mpu_t *mpu = (armv8m_mpu_t *)0xE000ED90;
mpu_disable(mpu); //关闭MPU
mpu_select_region(mpu, 0); //设置region0
//设置region0基地址0x30000000,只读不可执行
mpu_set_region_base(mpu, 0x30000000UL, REGION_NON_SHAREABLE, REGION_RO_PRIV_ONLY, REGION_XN);
//设置region0上限为0x30001FFFF, region0使用attr0,并且使能region
mpu_set_region_limit(mpu, 0x30001FFFUL, 0, REGION_EN);
//设置region0属性为device memory
mpu_set_region_attr(mpu, 0, 0); /*device memory*/
//打开MPU
mpu_hfnmiena_disable(mpu);
mpu_privdefena_enable(mpu);
mpu_enable(mpu);
easy_printf("%s:mpu setup done\n", __func__);
//打开MPU后测试是否具有写权限
easy_printf("0x30001000:%x\n", *temp_addr);
//程序执行到这会触发hard fault
*temp_addr = 0x2;
easy_printf("0x30001000:%x\n", *temp_addr);
easy_printf("%s done\n", __func__);
}
执行log:
qemu-system-arm: invalid accelerator kvm
qemu-system-arm: falling back to tcg
hello cortex m33
0x30001000:00000000
0x30001000:00000001
test_armv8m_mpu_write:mpu setup done
0x30001000:00000001
default_exception_handler entry
oid test_armv8m_mpu_overlap()
{
volatile uint32_t *temp_addr = (volatile uint32_t *)0x30001000UL;
easy_printf("0x30001000:%x\n", *temp_addr);
*temp_addr = 0x1;
easy_printf("0x30001000:%x\n", *temp_addr);
armv8m_mpu_t *mpu = (armv8m_mpu_t *)0xE000ED90;
mpu_disable(mpu);
//设置region0:0x30000000 - 0x30001FFF
//可读可写,不可执行,device memory
mpu_select_region(mpu, 0);
mpu_set_region_base(mpu, 0x30000000UL, REGION_NON_SHAREABLE, REGION_RW_PRIV_ONLY, REGION_XN);
mpu_set_region_limit(mpu, 0x30001FFFUL, 0, REGION_EN);
mpu_set_region_attr(mpu, 0, 0); /*device memory*/
//设置region1:0x30001000 - 0x30001FFF
//可读可写,不可执行,device memory
mpu_select_region(mpu, 1);
mpu_set_region_base(mpu, 0x30001000UL, REGION_NON_SHAREABLE, REGION_RW_PRIV_ONLY, REGION_XN);
mpu_set_region_limit(mpu, 0x30001FFFUL, 1, REGION_EN);
mpu_set_region_attr(mpu, 0, 1); /*device memory*/
//打开MPU
mpu_hfnmiena_disable(mpu);
mpu_privdefena_enable(mpu);
mpu_enable(mpu);
easy_printf("%s:mpu setup done\n", __func__);
//打开MPU后,由于0x30001000的地址在region0和region1中重叠了,访问该地址会触发hard fault
//代码执行到这一句的时候会触发异常
easy_printf("0x30001000:%x\n", *temp_addr);
easy_printf("%s done\n", __func__);
}
qemu-system-arm: invalid accelerator kvm
qemu-system-arm: falling back to tcg
hello cortex m33
0x30001000:00000000
0x30001000:00000001
test_armv8m_mpu_overlap:mpu setup done
default_exception_handler entry
void test_func_print()
{
easy_printf("%s entry\n", __func__);
}
void test_armv8m_xn()
{
/* Inject code at 0x30001000 */
//在0x300001000地址注入代码test_func
typedef void (*test_func_t)(void);
volatile uint32_t *temp_addr = (volatile uint32_t *)0x30001000UL;
test_func_t test_f = (test_func_t )0x30001001;//Thumb指令集,所以函数call的时候地址要加1
/* 1000041c :
1000041c: b500 push {lr}
1000041e: 4801 ldr r0, [pc, #4] ; (10000424 )
10000420: 4780 blx r0
10000422: bd00 pop {pc}
10000424: 100005eb andne r0, r0, fp, ror #11 //100005eb是test_func_print的地址
*/
*temp_addr++ = 0x4801b500;
*temp_addr++ = 0xbd004780;
*temp_addr++ = 0x100005eb;
//使能MPU前可以执行在0x30001000处的test_func
test_f();
//使能MPU region0:0x30000000 - 0x300010000
//只读,不可执行,device memory
armv8m_mpu_t *mpu = (armv8m_mpu_t *)0xE000ED90;
mpu_disable(mpu);
mpu_select_region(mpu, 0);
mpu_set_region_base(mpu, 0x30000100UL, REGION_NON_SHAREABLE, REGION_RO_PRIV_ONLY, REGION_XN);
mpu_set_region_limit(mpu, 0x30001FFFUL, 0, REGION_EN);
mpu_set_region_attr(mpu, 0, 0); /*device memory*/
mpu_hfnmiena_disable(mpu);
mpu_privdefena_enable(mpu);
mpu_enable(mpu);
easy_printf("%s:mpu setup done\n", __func__);
//使能MPU后不可执行0x30001000的test_func,因此代码执行到这里的时候会触发hardfault
test_f();
}
log
qemu-system-arm: invalid accelerator kvm
qemu-system-arm: falling back to tcg
hello cortex m33
test_func_print entry
test_armv8m_xn:mpu setup done
default_exception_handler entry