pmp(Physical Memory Protection CSRs)的介绍可以看:
《The RISC-V Instruction Set Manual Volume II: Privileged Architecture》
重点内容在这里提一下:
- 32位CPU最多具有8个可配置的pmp,配置寄存器分别为pmp0cfg,pmp1cfg,pmp2cfg,pmp3cfg,pmp4cfg,pmp5cfg,pmp6cfg和pmp7cfg,它们都是8-bit的,而pmpcfg0管理着mp0cfg-mp3cfg,pmpcfg1管理着pmp4cfg-pmp7cfg,pmpcfg0和pmpcfg1是32-bit的。若CPU为64位,则最多可以具有16个可配置的pmp,pmp0cfg-pmp15cfg,同时多了pmpcfg2和pmpcfg3。
- 32位的CPU,pmp的匹配地址为32-bit,是正常地址右移两位得出,64位的CPU为64-bit,也是正常地址右移两位得出。因为我没有配置虚拟内存,所以这里的正常地址就是实际的物理地址。
- pmp?cfg为8-bit CSR寄存器,包含L,A,X,W,R位。L表示锁定位,A为2-bit的区域位,X为可执行权限,W为可写权限,R为可读权限。
- 区域配置中包括4种形式,2b’00表示不使用,2’b01表示向上的全部范围,2’b10表示对齐的4字节范围,2’b11表示对齐的2^N字节范围,且要≥8字节。
- L表示锁定位尤为重要。
当L置1后,只有系统复位才能清除。
当L置1后,对应的pmp?cfg和pmpaddr?都不能再配置,写入值被忽略。
当pmp?cfg.A配置为TOR时,这里的pmp?cfg,pmpaddr?和pmp?cfg和pmpaddr?-1都不能配置了。(if pmpicfg.A is set to TOR, writes to pmpaddri-1 are ignored.)
当L置1后,全部模式(M,S和U)都必须执行pmp?cfg的配置。
当L为0时,只有(S和U)执行pmp?cfg的配置,M模式不执行,且能自由修改配置。
注意:?表示pmp的编号,例如0-7。
- pmp具有优先级,编号越低优先级越高,pmp0>pmp1>pmp2,当pmp0和pmp1设置的地址具有重叠部分,那么pmp0起效,pmp1不起效。
测试代码如下。
#include "encoding.h"
#define U32 *(volatile unsigned int *)
#define DEBUG_SIG 0x70000000
#define DEBUG_VAL 0x70000004
//--------------------------------------------------------------------------
// handle_trap function
void handle_trap()
{
asm volatile ("nop");
U32(0x60003000) = 0x2021314;
U32(DEBUG_SIG) = 0xFF;
while(1);
}
//--------------------------------------------------------------------------
// pmp configuration function
void pmp_config()
{
unsigned int i;
U32(0x60000000) = read_csr(pmpcfg0);
U32(0x60000004) = read_csr(pmpcfg1);
U32(0x60000010) = read_csr(pmpaddr0);
U32(0x60000014) = read_csr(pmpaddr1);
U32(0x60000018) = read_csr(pmpaddr2);
U32(0x6000001c) = read_csr(pmpaddr3);
U32(0x60000020) = read_csr(pmpaddr4);
U32(0x60000024) = read_csr(pmpaddr5);
U32(0x60000028) = read_csr(pmpaddr6);
U32(0x6000002c) = read_csr(pmpaddr7);
//addr0 -> 0x6000_0000
write_csr(pmpaddr0,0x60000000>>2);
//addr1 -> 0x6000_1000
write_csr(pmpaddr1,0x60001000>>2);
//pmp1cfg: L=1 A=1(TOR) X-0 W-0 R-0
write_csr(pmpcfg0 ,0x00008800);
for ( i = 0; i < 100; i++ )
U32(0x80001000+4*i) = i + 1;
for ( i = 0; i < 100; i++ )
U32(0x60001000+4*i) = i + 1;
U32(0x60000000) = 0x10086;
}
//--------------------------------------------------------------------------
// Main
void main()
{
pmp_config();
while(1) {asm volatile ("wfi");}
}
代码功能:
- 读取pmp相关的CSR寄存器,并输出值0x6000_0000(mmio)的总线上。
- 配置pmp区域1,地址为0x6000_0000-0x6000_0FFF,不可读,不可写,不可执行。
- 往0x8000_1000-0x8000_118C区域写入数据,证明memory区域没有问题。
- 往0x6000_1000-0x6000_118C区域写入数据,证明配置区域外没有问题。
- 往0x6000_0000地址写0x10086。
- 第五步触发异常,函数调至handle_trap(),往0x6000_3000输出0x2021314,同时结束仿真。
仿真波形如下图。
- 红色箭头:读取pmp相关的CSR寄存器,并输出值0x6000_0000(mmio)的总线上,共8次,pmpcfg0,pmpcfg1和pmpaddr0-pmpaddr7。
- 蓝色箭头:往0x6000_1000-0x6000_118C区域写入数据,证明配置区域外没有问题。
- 黄色箭头:往0x6000_0000地址写0x10086,触发异常,异常为7(Store/AMO access fault)。
- 白色箭头:往0x6000_3000输出0x2021314,同时结束仿真。