前言
最近在学习一些M33相关的体系结构,看文档过程中顺便做一下记录。
以下是查阅 Arm® Cortex®-M33 Devices Generic User Guide
文档过程中的一些记录。
异常模型
异常
安全模式下的异常:
- Reset:
- NMI:
- If AIRCR.BFHFNMINS=0, then the NMI is Secure.
- If AIRCR.BFHFNMINS=1, then NMI is Non-secure.
- HardFault:
- MemManage:
- BusFault:
- If AIRCR.BFHFNMINS=0, BusFaults target the Secure state.
- If AIRCR.BFHFNMINS=1, BusFaults target the Non-secure state.
- UsageFault:通常由指令错误产生
- 未定义指令,不对齐访问,非法指令执行状态,错误异常返回,除零
- SecureFault:执行各种安全检查触发的,多半用于转跳到安全模式
- SVCall:SVC指令触发
- DebugMonitor:
- PendSV:
- SysTick:
- IRQ:
总结如下表:
非安模式下异常和安全差不多,就是没有 SecureFault
优先级
除了 Reset、NMI、HardFault 外,所有异常均可配置优先级,所以 Reset、NMI、HardFault 被称为固定优先级异常
拓展的优先级如下:
可以看到,AIRCR.PRIS 控制着 安全异常是否可抢占 非安全异常。
原因:AIRCR.PRIS = 1 时,非安全异常优先级 < 安全异常优先级 (数字越小,优先级越高)
栈帧
-
非安全模式下触发非安全异常或中断的栈帧:
-
安全模式下触发非安全异常或中断时,会额外保存一些寄存器,如下:
-
安全模式下触发安全异常,分2种情况,FPCCR.TS=1或0
-
非安全异常不能触发安全异常
异常返回
进入异常的时候会保存一个 EXEC_RETURN
到 LR寄存器。EXEC_RETURN
的低6bit用于表示一些属性,如处理器模式,安全状态等。
安全模式下的EXEC_RETURN
如下图所示:
非安全模式下的EXEC_RETURN
如下图所示:
状态转换如下图:
- BLXNS:安全 -> 非安全(进入)
- 调用非安全函数,名为
FNC_RETURN
的特殊值会保存到LR,并且 返回地址和 XPSR 会保存到安全堆栈
- BL:非安全 -> 安全(退出)
- 下面的指令会将PC恢复成
FNC_RETURN
,触发从非安全返回安全,且自动从安全堆栈恢复返回地址和 XPSR
POP
or LDM
指令来会恢复PC
LDR
指令来恢复PC
- 使用任何寄存器的
BX
指令
- BX:非安全 -> 安全(进入)
- BXNS:安全 -> 非安全(退出)
- 除了上面标注列出的场景,其余状态转换都会触发
SecureFault
故障类型
故障是异常的一个子集,产生故障的原因有:
- 总线错误
- 取址或数据访问
- 向量表加载
- 内部检测错误,如未定义指令
- 执行被标记为XN(Execute Never)区域的代码
- 违反特权或访问未管理区域而造成MPU fault
- 违反安全
故障类型有如下:
- HardFault
- VECTTBL:读取vector时发生总线错误
- FORCED:故障升级成硬件故障
- MemManage:MPU或默认映射不匹配引发,具体有以下情况
- IACCVIOL :在指令访问
- DACCVIOL:在数据访问
- MSTKERR:在异常入栈
- MUNSKERR:在异常出栈
- MLSPERR:在懒惰浮点保存
- BusFault:总线错误
- STKERR:在异常入栈
- UNSTKERR:在异常出栈
- IBUSERR:在指令预取
- LSPERR:在懒惰浮点保存
- PRECISERR:精确的数据总线错误
- IMPRECISERR:不精确的数据总线错误
- UsageFault
- NOCP:尝试访问协处理器
- UNDEFINSTR:未定义指令
- INVSTATE:试图进入无效的指令集状态
- INVPC:无效
EXC_RETURN
值
- UNALIGNED:非法未对其访问
- STKOF:栈溢出
- DIVBYZERO:除零
- SecureFault
- LSERR:懒惰状态错误标志
- LSPERR:懒惰状态保存错误标志
- INVTRAN:无效转换标志
- AUVIOL:属性单元违反标志
- INVER:非法异常返回标志
- INVIS:无效完整性签名标志
- INVEP:非法入口
故障升级
除了HardFault之外,所有的异常都可以配置优先级。
某些情况下,可配置异常会升级到HardFault,这被称为优先级升级:
- 错误处理程序会导致发生与自己相同类型的错误,且他们具有相同优先级。这会被升级成HardFault,因为处理程序无法抢占自身。
- 错误处理程序会导致发生优先级小于等于自己的错误,这会被升级成HardFault,因为处理程序无法抢占当前正在执行的处理程序
- 异常触发,但是没有设置对应的处理程序
AIRCR.BFHFMNINS可以配置 BusFaults和固定优先级异常的安全属性:
- AIRCR.BFHFMNINS = 0:BusFaults和固定优先级异常是安全的,HardFault优先级=-1,NMI优先级=-2
- AIRCR.BFHFMNINS = 1:BusFaults和固定优先级异常是非安全的,且引入
Secure HardFault
,优先级=-3
非安全状态下,不能影响以安全为目标的 BusFaults和固定优先级异常,所以 FAULTMASK_NS 只能影响到可配置异常。
非安全状态下,可配置优先级异常的优先级被映射到0-255,如果 AIRCR.PRIS =0,非安全可配置异常的优先级被映射到 128-255,如果 AIRCR.PRIS =1,非安全可配置异常的优先级被映射到 0 - 127。
当 BusFaults和固定优先级异常被配置为安全的,FAULTMASK_S 将设置为-1,来抑制包括 HardFault 在内的所有异常
当 BusFaults和固定优先级异常被配置为非安全的,FAULTMASK_NS 将设置为-1,来抑制包括 非安全HardFault 在内的所有异常,FAULTMASK_S 将设置为-3,来抑制包括 Secure HardFault 在内的所有异常。
Note
- AIRCR.BFHFNMINS = 1:只有Reset可以抢占 Secure HardFault,Secure HardFault可以抢占除Reset外所有异常
- AIRCR.BFHFNMINS = 0:Secure HardFault可以抢占除Reset,NMI,另一个HardFault外 所有异常
故障寄存器
-
故障状态寄存器:用于表示故障原因
-
故障地址寄存器:在 BusFaults 、 MemManage、 Security Extension表示发生故障的地址
-
HardFault:状态寄存器名称 HFSR
-
MemManage:状态寄存器名称 MMFSR,故障地址寄存器名称 MMFAR
-
BusFault:状态寄存器名称 BFSR,故障地址寄存器名称 BFAR
-
UsageFault:状态寄存器名称 UFSR
-
SecureFault:状态寄存器名称 SFSR,故障地址寄存器名称 SFAR
PS:*FSR其实都对应一个故障状态物理寄存器,*FAR其实都对应一个故障地址物理寄存器
如果实现了安全拓展,会有2组4个寄存器,分别表示安全和非安全状态下故障的状态和地址信息
故障地址寄存器只有一个,需要清楚 *FARVALID 位,下一个故障才会更新该寄存器的值。
HFSR(HardFault)
故障寄存器
地址:0xE000ED2C
- bit[1] VECTTBL:故障发生在读取向量表
- bit[30] FORCED:指示一个强制的HardFault,由无法处理的具有可配置优先级的故障升级产生,原因可能是优先级或被禁用
CFSR(BusFault、UsageFault、MemManage)
可配置故障寄存器,表示 BusFaults 、 MemManage、 UsageFault 的故障原因
地址:0xE000ED28
bit分配如下图:
下面所有位=1表示发生相应的故障
MMFSR具体位域分配:
- bit[0] IACCVIOL: 故障发生在 尝试去取不允许执行位置的指令
- bit[1] DACCVIOL: 故障发生在 尝试去访问不允许访问的位置
- bit[3] MUNSTKERR: 故障发生在 异常出栈
- bit[4] MSTKERRMSTKERR: 故障发生在 异常入栈
- bit[5] MLSPERR: 故障发生在 懒惰浮点保存
- bit[7] MMARVALID: MMFAR保存一个有效的故障地址
BFSR
地址:0xE000ED29
- bit[0] IBUSERR:指令总线错误,=1时,故障地址不会被写入到 BFAR
- bit[1] PRECISERR:已经发生数据总线错误并且异常返回的PC的值为发生故障的地址,=1时,故障地址会写入到 BFAR
- bit[3] UNSTKERR:异常出栈时候发生故障
- bit[4] STKERR:异常入栈时候发生故障
- bit[5] LSPERR:浮点惰性状态保存期间发生总线故障
- bit[7] BFARVALID:BFAR 保存一个有效的故障地址。
UFSR
地址:0xE000ED2A
- bit[0] UNDEFINSTR:未定义指令
- bit[1] INVSTATE:无效状态,表示 EPSR.T 或 EPSR.IT 合法错误发生
- bit[2] INVPC:非法PC
- bit[3] NOCP:无协处理器
- bit[4] STKOF:栈溢出
- bit[8] UNALIGNED:不对齐访问
- bit[9] DIVBYZERO:除零
MMFAR
地址:0xE000ED34
CFSR.MMARVALID = 1时,该寄存器保存一个有效的故障地址(bit[31:0])
BFAR
地址:0xE000ED38
CFSR.BFARVALID = 1时,该寄存器保存一个有效的故障地址(bit[31:0])
协处理器相关
CPACR
地址:0xE000ED88
- bit[2m+1:2m] m=0-7:控制 协处理器m 的状态
- 2b’00:拒绝访问,任何访问都会触发 NOCP UsageFault.
- 2b’01:允许特权访问,其余访问都会触发 NOCP UsageFault.
- 2b’11:所有均可访问
- bit[21:20]:控制CP10状态,控制浮点访问
- 2b’00:
- 2b’00:
- 2b’00:
- bit[23:22]:控制CP11状态
- 2b’00:不允许访问浮点拓展,任何访问都会触发 NOCP UsageFault.
- 2b’01:仅限特权使用浮现拓展,其余访问都会触发 NOCP UsageFault.
- 2b’11:所有均可访问
NSACR
地址:0xE000ED8C
主要控制NS对浮点拓展的访问,bit和CP编号一一对应,CP5对应bit5。
=1表示NS状态下能访问,=0 访问会触发 NOCP UsageFault.
SCB总览
里面列举了各个系统控制块寄存器的地址
安全拓展
如果实现了安全拓展,处理器可以用安全属性单元(SAU)和内存保护单元(MPU)来管理敏感数据。
SAU
SAU用来检查地址或数据是否安全的。
寄存器
SAU相关的寄存器:
SAU_CTRL
- bit[0] ENABLE:=1 使能SAU
- bit[1] ALLNS: 用来标记内存是否安全的,ENABLE=1时,这个Bit无效
SAU_TYPE
表示SAU实现的区域数
- bit[7:0] SREGION:SAU实现了多少个region
SAU_RNR
用于选择当前被 SAU_RBAR 和 SAU_RLAR 的region
- bit[7:0] SREGION:region号
SAU_RBAR
选择该region的基址。
- bit[31:5]:基地址,低5bit为0,意味着地址32字节对齐
SAU_RLAR
控制对应region是否是能,是否可被非安全调用(即非安全状态下在这个region执行SG指令)。
- bit[0]:=1 是能 SAU region
- bit[1]:=1 允许非安全呼叫
SFSR
提供和安全相关的故障信息,某bit=1均表示在该情况下发生故障(fault)
- bit[0] INVEP:无效入口,非安全或异常下调用安全状态下的目标(非SG指令)导致,如果调用目标是SG,该bit也会=1,但没有匹配的 SAU或IDAU 时,会设置 NSC 标志
- bit[1] INVIS:无效签名标志,出栈期间发现堆栈的完整性签名无效
- bit[2] INVER:无效返回标志,可能由 非安全状态返回时 EXC_RETURN.DCRS=0 或 异常返回时 EXC_RETURN.ES = 1 导致的
- bit[3] AUVIOL:属性单元违规标志,表示尝试去访问被标记为 使用Ns-Reg进行安全处理的地址空间
- bit[4] INVTRAN:非法转换标志,表示由于未标记分支指令导致从安全内存到安全内存而引发的异常
- bit[5] LSPERR:懒惰状态保存错误标志,表示在延迟保存浮点期间发现 SAU 或 IDAU 违规
- bit[6] SFARVALID:安全故障地址有效,表示 SFAR 保存一个有效故障地址。
- bit[7] LSERR:懒惰状态错误标志
SFAR
- bit[31:0]:保存引发SAU违规的地址
MPU
- MPU定义了8个region。
- 如果实现了安全拓展,可以由一个可选的安全MPU
- 如果region重叠,访问重叠区域会引发一个fault
- MPU内存映射是统一的,意味着指令和数据访问有相同的region设置
- 如果程序访问MPU禁止的区域,会触发 MemManage 异常
- MPU可以被动态配置
以下是MPU可能的region属性:
类型 |
共享 |
其余属性 |
描述 |
Device-nGnRnE |
共享 |
- |
用于访问内存映射的外设,所有访问顺序都是按照程序顺序进行的 |
Device-nGnRE |
共享 |
- |
用于访问内存映射的外设,比Device-nGnRnE更弱排序 |
Device-nGRE |
共享 |
- |
用于访问内存映射的外设,比Device-nGnRE更弱排序 |
Device-GRE |
共享 |
- |
用于访问内存映射的外设,比Device-nGRE更弱排序 |
Normal |
共享 |
非cache的通写 可cache的回写 |
在几个处理器间共享的普通内存 |
Normal |
不共享 |
非cache的通写 可cache的回写 |
仅供一个处理器使用的普通内存 |
寄存器
MPU相关寄存器:
MPU_TYPE
记录是否支持MPU,由几个region
- bit[08]:
- 1b’0:统一的指令和数据区域,ARMv8-M只支持这个,固定=0
- bit[15:8]:ARMv8-M只支持8个,所以要么=8 要么=0
MPU_CTRL
控制MPU使能
- bit[0] ENABLE: =1 是能MPU
- bit[1] HFNMIENA:=1,允许在 HardFault 和 NMI 中操作MPU
- bit[2] PRIVDEFENA:=1 默认内存映射作为特权软件的后台映射,=0 然和 region之外的内存访问都会触发fault
Xn 和 Device-nGnRnE 规则始终应用在SCB空间,不受Enable位影响
PRIVDEFENA= 0,只有是能一个 region,ENABLE才能配置为1
PRIVDEFENA= 1,ENABLE = 1,且没有配置任何region,则只有特权软件能访问
ENABLE = 0,系统使用默认内存映射,行为跟没有MPU是一样的
HFNMIENA = 0,处理器处理优先级为 -1,-2,-3的异常时候,不启用MPU
MPU_RNR
bit[7:0]:配置 MPU_RBAR 和 MPU_RLAR 选择哪个region
MPU_RBAR
定义该region的基址
- bit[31:5] BASE:基址,低5位拓展为0
- bit[4:3] SH:共享属性
- 2b’00:不共享
- 2b’01:不可预测
- 2b’10:外部共享
- 2b’11:内部共享
- bit[2:1] AP:访问权限
- 2b’00:特权级别可读写
- 2b’01:任何级别可读写
- 2b’10:特权级别只读
- 2b’11:任何级别只读
- bit[0] XN:=1 该区域不可执行,=0允许读时,才可执行
MPU_RBAR_A
n = [1,3]
访问 MPU_RBAR_A = MPU_RNR[7:2]:n[1:0]
MPU_RLAR
MPU region的限制地址
- bit[31:5] LIMIT:限制地址,真实限制地址 = bit[31:5] + 0x1f
- bit[3:1] AttrIndx:属性所以,在 MPU_MAIR0 和 MPU_MAIR1 中关联一组属性
- bit[0] EN:=1 是能region
MPU_RLAR_A
n = [1,3]
访问 MPU_RLAR_A = MPU_RLAR[7:2]:n[1:0]
MPU_MAIR0/1
配置具体属性组。
-
MAIR_ATTR[7:4] = 4b’0000
-
bit[7:4] = 4b’0000
-
bit[3:2] = Device,格式如下
-
2b’00:Device-nGnRnE
-
2b’01:Device-nGnRE
-
2b’10:Device-nGRE.
-
2b’11:Device-GRE.
-
bit[1:0] = 2b’00
-
MAIR_ATTR[7:4] != 4b’0000
- bit[7:4] = Outer
- 4b’00RW:普通内存,外部直写瞬态(RW 不能是 00)。瞬态是啥意思?没看懂,猜测应该是写操作是否bufferable
- 4b’0100:普通内存,外部非cache
- 4b’01RW:普通内存,外部回写瞬态(RW 不能是 00)
- 4b’10RW:普通内存。外部直写非瞬态
- 4b’11RW:普通内存。外部回写非瞬态
- bit[3:0] = Inner
- 4b’00RW:普通内存,内部直写瞬态(RW 不能是 00)
- 4b’0100:普通内存,内部非cache
- 4b’01RW:普通内存,内部回写瞬态(RW 不能是 00)
- 4b’10RW:普通内存,内部直写非瞬态
- 4b’11RW:普通内存,内部回写非瞬态
更新 MPU region
实例:
; R1 = MPU region number
; R2 = base address, permissions and shareability
; R3 = limit address, attributes index and enable
LDR R0,=MPU_RNR
STR R1, [R0, #0x0] ; MPU_RNR
STR R2, [R0, #0x4] ; MPU_RBAR
STR R3, [R0, #0x8] ; MPU_RLAR
Note
- 软件必须在配置MPU前使用内存屏障指令
- 配置完MPU后,如果想立即生效,需要使用 DSP 和 ISB 指令
更新 SAU region
示例:
; R1 = SAU region number
; R2 = base address
; R3 = limit address, Non-secure callable attribute and enable
LDR R0,=SAU_RNR
STR R1, [R0, #0x0] ; SAU_RNR
STR R2, [R0, #0x4] ; SAU_RBAR
STR R3, [R0, #0x8] ; SAU_RLAR
Note
- 软件必须在配置MPU前使用内存屏障指令
- 配置完MPU后,如果想立即生效,需要使用 DSP 和 ISB 指令
MPU配置Tips
通常微处理器只有一个,且没cache的,MPU可以参考如下配置: