ARM SMMUv3命令和事件队列分析(四)
1.简介
SMMUv3新增了命令和事件队列(Command and Event queues),可选的PRI队列,用于和软件进行交互。软件将要执行的命令提交到命令队列,比如预取配置、Invalidate STE命令等,SMMU从命令队列取出命令并执行。当命令不正确或配置出错等错误发生时,SMMU将会将这些事件写入到事件队列,软件从事件队列取出事件进行处理。PRI队列用于接收PCIe page requests。
2.循环队列
命令和事件队列基于循环队列(circular queues)。循环队列是基于内存的生产者和消费者循环FIFO。生产者向FIFO中写入任务,消费者从FIFO中读取1任务,生产者的位置由PROD指针确定,表示下一个可以写入的位置,消费者的位置由CONS指针确定,表示下一个可以读取的位置,PROD指针和CONS指针保存在寄存器中。对于命令队列,CPU为生产者,SMMU为消费者。对于事件队列,SMMU为生产者,CPU为消费者。循环队列生命周期如下图所示,当队列指针从队列尾跳到队列头时,wrap值会变化。
- PROD.WR == CONS.RD and PROD.WR_WRAP == CONS.RD_WRAP,此时队列空。
- PROD.WR > CONS.RD and PROD.WR_WRAP == CONS.RD_WRAP,生产者写入任务。
- PROD.WR == CONS.RD and PROD.WR_WRAP == CONS.RD_WRAP,消费者读取任务,此时队列空。
- PROD.WR < CONS.RD and PROD.WR_WRAP != CONS.RD_WRAP,生产者写入任务,PROD指针跳到了队列头。
- PROD.WR == CONS.RD and PROD.WR_WRAP != CONS.RD_WRAP,生产者继续写入任务,此时队列满。
- PROD.WR > CONS.RD and PROD.WR_WRAP == CONS.RD_WRAP,消费者读取任务,CONS指针跳到了队列头。
- PROD.WR == CONS.RD and PROD.WR_WRAP == CONS.RD_WRAP,消费者继续读取任务,此时队列空。
3.命令
CPU将命令(Command)写入命令队列,SMMU从命令队列中取出命令执行。命令队列中每个元素都为16字节,采用小端字节序,开始的8位表示命令操作码。SMMU总共定义了6种命令,分别为Prefetch、Configuration structure invalidation、TLB invalidation、ATS and PRI、DPT maintenance、Fault response and synchronization commands,具体的作用如下。
- Prefetch:将STE和CD等数据结构预取到Cache中,可以提高地址转换速度。
- TLB invalidation:维护TLB数据一致性。
- ATS and PRI:用于处理PCIe设备DMA地址转换。
- DPT maintenance:主要用于维护页表结构的完整性和一致性,尤其是在多核系统或动态内存管理场景中。
- Fault response and synchronization commands:用于处理Fault和同步命令。
下面只介绍常用的几个,其他的参考SMMU Spec。
3.1.Prefetch
Prefetch命令可以让SMMU预取和stream相关的配置和地址转换数据。
3.1.1.CMD_PREFETCH_CONFIG
预取来自给定StreamID(以及当SSV == 1时的SubstreamID)流量所需的任何STE和CD配置结构。
3.1.2.CMD_PREFETCH_ADDR
预取给定地址范围内的任何STE和CD配置结构及TLB项。
3.2.Configuration structure invalidation
当软件修改某些配置结构(Stream table、CD table)后,需要向SMMU发送命令,确保invalidates缓存这些结构的Cache。
3.2.1.CMD_CFGI_STE
invalidates StreamID关联的STE Cache。
3.2.2.CMD_CFGI_STE_RANGE
invalidates StreamID关联的多个STE Cache,Start = (StreamID & ~(2^(Range+1) - 1)。
End = Start + 2^(Range+1) - 1。
3.2.3.CMD_CFGI_CD
invalidates StreamID和SubstreamID关联的一个CD Cache。该命令在改变TTBRx/ASID或者试能TBI时使用。
3.2.4.CMD_CFGI_CD_ALL
invalidates StreamID关联的所有CD Cache。
3.3.Fault response and synchronization commands
当错误发生时,CPU需要向SMMU发送Fault response以处理错误。CPU提交的命令SMMU是异步执行的,因此CPU需要提交synchronization command命令,获取命令执行结果。
3.3.1.CMD_RESUME
使用StreamID和STAG参数,唤醒处于stalled状态的事务。StreamID用于索引事务,STAG参数定义事务唤醒后的行为。
Action (Ac) | Result |
---|---|
1 | 事务将被重试(Retry),其行为与第一次执行时时相同。系统会重新查询配置和地址转换。此后,该事务可能继续正常执行,也可能再次触发错误(Fault)。 |
0 | 事务将以Abort parameter(Ab)参数定义的方式终止(Terminate)。当Ab==0时,事务以RAZ/WI(Read-As-Zero/Write-Ignored)语义成功完成。当Ab==1时,系统会向客户端报告中止/总线错误(abort/bus error)。 当SMMU_IDR0.TERM_MODEL == 1,Ab参数将被忽略,事务以abort终止。 Ab参数和CD.A(non-stalled terminated transactions)的配置类似。 |
3.3.2.CMD_SYNC
CMD_SYNC提供了以下的同步机制:
- 与CMD_SYNC提交至同一命令队列的前序命令(类似于内存屏障指令)。当CMD_SYNC执行完成,同一队列CMD_SYNC的前序命令必须执行完成。
- 在CMD_SYNC之前终止的客户端事务所对应事件记录的可观测性。
- 由地址转换完成引起的HTTU更新。
当CMD_SYNC执行完成后,会产生一个完成信号(ComplSignal),信号可以是中断(可以采用MSI形式),也可以是WFE唤醒事件。完成信号的机制由参数CS决定,具体如下:
CS | Result |
---|---|
0b00 | SIG_NONE。CMD_SYNC执行完成后不会有任何行为,MSIAddress、MSIData、MSIWriteAttributes参数被忽略。 |
0b01 | SIG_IRQ。CMD_SYNC执行完成后会产生中断。如果支持MSI,将会把MSIData写到MSIAddress(物理地址)中触发MSI中断,通知PE CMD_SYNC执行完成。如果MSIAddress非0,内存属性由MSIAttr决定。若不支持MSI,则MSIData、MSIAddress会被忽略。 |
0b10 | SIG_SEV。CMD_SYNC执行完成后会给PE发送类似于SEV的事件。当SMMU_IDR0.SEV==1时,SIG_SEV才有效,否则效果和SIG_NONE一样。此时CPU通过轮询SMMU_CMDQ_CONS.RD判断CMD_SYNC执行是否完成,每次轮询之间使用WFE命令等待SEV事件。 |
0b11 | Reserved。将会引发CERROR_ILL错误。 |
4.事件
SMMU将事件(Event)写入事件队列,CPU从事件队列中取出事件处理。每个Event占用32字节。所有Event以小端字节序保存在Event队列中。SMMU定义了19种标准事件,Event number范围为0x1-0x13、0x20-0x21、0x24-0x25,用户也可以自定义事件,Event number范围为0xE0-0xEF。这些Event有一些公共的字段,具体如下表所示。
Common Fields | Reason |
---|---|
StreamID | 触发Event对应的StreamID。 |
RnW | 触发Event的事务读写属性。0:写,1:读。 |
PnU | Privileged/Unprivileged。0:Unprivileged,1:Privileged。 |
InD | 触发Event的访问类型是指令访问还是数据访问。0:Data,1:Instruction。 |
InputAddr | 输入到SMMU触发Event的地址。根据场景和Event number,此字段可能被解释为VA、IPA或PA,比如在地址转换第一阶段触发F_TRANSLATION,InputAddr是VA。 |
SSV | SubstreamID是否有效标志。0:invalid,1:valid。 |
SubstreamID | 触发Event对应的SubstreamID,只有SSV==1时有效。 |
S2 | 触发Event的地址转换阶段。0:Stage 1 fault occurred,1:Stage 2 fault occurred。 |
CLASS | 触发Event的操作的类别。 0b00: CD, CD fetch. 0b01: TTD, Stage 1 translation table fetch. 0b10: IN, Input address caused fault. 0b11: Reserved. |
NSIPA | Non-secure IPA。区分访问安全还是非安全IPA空间触发的Event。 |
GPCF | 页表权限校验Fault。 0: 由外部错误触发的Fault,不是页表权限触发。 1: 由页表权限触发的Fault。 |
在Linux内核中只处理和地址转换相关的F_TRANSLATION、F_ADDR_SIZE、F_ACCESS、F_PERMISSION四种Event,下面也只介绍这4种Event。
4.1.F_TRANSLATION
F_TRANSLATION错误表示访问地址在某一级地址转换阶段未通过由TxSZ/SLx定义的范围检查,或者地址位于一个已禁用的TTBx范围内,又或者未能为该地址找到有效的转换表描述符。
4.2.F_ADDR_SIZE
在某一级地址转换阶段,输出的地址超出了该阶段的有效地址空间范围(由 xPS 定义),从而触发的错误。具体触发条件如下:
- 转换过程中地址越界
- 当某级转换(Stage1或Stage2)解析中间页表和最后一级页表描述符时,若输出的地址超出了该转换表关联的有效物理地址空间xPS(由CD.IPS或STE.S2PS定义),则触发此错误。
- 注意:如果TTB本身超出范围(在转换表遍历开始前),则不会触发此错误,而是会直接导致 C_BAD_CD(配置描述符无效)或 C_BAD_STE(STE 无效)。 - Stage 1 旁路转换时的地址越界
- 如果Stage 1 bypass,则输出地址 == 输入地址。若该地址超出了硬件支持的地址范围也会触发此错误。
4.3.F_ACCESS
页或块描述符中AF == 0导致的访问标志错误。如果支持并启用了HTTU,则将带有AF == 0的描述符修改为AF == 1,且不会上报该错误。
4.4. F_PERMISSION
页表访问权限错误。
参考资料
- Arm ® System Memory Management Unit Architecture Specification version 3.
- Linux Kernel 6.12.31 Source Code.