本文属于《 RISC-V指令集基础系列教程》之一,欢迎查看其它文章。
Incoming MSI Controller(IMSIC)是一个可选的RISC-V硬件组件,它与hart紧密耦合:
- 每个hart有一个IMSIC。
- 每个IMSIC包含M/S/VS-level的interrupt file,对应hart内的不同特权等级的csr pending寄存器:mip、sip、vsip。
- IMSIC会接收target为该hart的MSI中断,一旦该MSI中断的pending和enable bit置位时,就会将其发送到该hart的对应特权等级中。
- IMSIC中包含一到多个物理地址内存映射的寄存器。
- 除此以外,软件通过CSR寄存器(*iselect、topei和ireg)与IMSIC interrupt file进行交互。
1 Interrupt files and interrupt identities
1.1 MSI指向
在 RISC-V 系统中,MSI 不仅可以定向特定的 hart,也可以定向 hart 的 privilege level(像是 machine 或是 supervisor level),另外假设 hart 实现了 hypervisor extension,IMSIC 也可以选择性的允许 MSI在 virtual supervisor(VS) level 指定到特定 virtual hart。
1.2 IMSIC与interrupt file的关系
由于 MSI 可能指向 hart 不同的 privilege level 或 virtual hart,因此每个hart 的 IMSIC 拥有独立的 interrupt file。假设 hart 实现了 supervisor mode,则它的 IMSIC 至少有两个 interrupt file,一个用于 machine level,另一个则是 supervisor level。当 hart 也实现了 hypervisor extension 时,IMSIC 也会为 virtual hart 增加格外的 interrupt file 称之为 guest interrupt file。
IMSIC 为 virtual hart 提供 guest interrupt file 的数量正好是 GEILEN,即由 RISC-V 特权架构定义的hypervisor extension 支持 guest external interrupt 的数量。
小结:
- cpu仅支持M模式,1个IMSIC中仅有1个machine-level interrupt file。
- cpu支持S模式,1个IMSIC中有1个machine-level interrupt file和1个supervisor-level interrupt file。
- cpu支持H模式,1个IMSIC中有1个machine-level interrupt file,1个supervisor-level interrupt file和1个guest interrupt file。
1.3 interrupt file构成
IMSIC中的interrupt file保存对应特权状态下的MSI的pending位和enable位,即它是一个寄存器组。
每个 interrupt file 是由两个 bit 位数相同的 array 所组成的:
- 一个 interrput pending array,用于纪录已经到达但尚未服务的 MSI(interrupt pending bit),是由中断控制器置位
- 一个 interrupt enable array,用于指定的 hart 将接受哪些 interrupt(interrupt enable bit),是由hart置位
以上两组寄存器的每个 bit 位置对应不同的 interrupt ID。
每个 interrupt file 最大支持interrupt 数量为 2047,因此换算下来,支持2047个中断的IMSIC中,就应该有64个pending寄存器和64个enable寄存器,每个寄存器32bit,每个bit对应一个中断号。其中eip0/eie0的bit0无效,因此interrupt ID的有效范围为1-2047。
- eip0.bit0,表示interrupt ID=0无效中断
- eip0.bit1,表示interrupt ID=1的中断pending
- eip0.bit2,表示interrupt ID=2的中断pending
- …
- eip0.bit31,表示interrupt ID=31的中断pending
- eip1.bit0,表示interrupt ID=32的中断pending
- eip1.bit1,表示interrupt ID=33的中断pending
- …
- eip63.bit31,表示interrupt ID=2047的中断pending
IMSIC、interrupt file、pending/enable array的组成结构,如下所示:
1.4 interrupt ID
通过该 interrupt ID 可以在 interrupt file 中区分来自不同来源的 MSI。由于 IMSIC 是 hart 的外部中断控制器,所以 interrupt file 对应的 interrupt ID 成为 external interrupt 的 minor identity,由软件指定(major identity是指interrupt cause)。
回顾前面描述,AIA规定了,如下外部中断主ID:
- S level外部中断,major identity为9
- H level外部中断,major identity为10
- M level外部中断,major identity为11
结合这里interrupt file中eip和eie array,对应外部中断次ID(minor identity),主次ID合起来,可表示在某模式下某具体的外部中断,比如:M模式下串口中断。
每个 interrupt file 支持的 interrupt 数量最小 63 最大 2047。当 interrupt file 支持 N 个不同的interrupt ID 时,有效ID介于 1~N 之间,因此超出此范围的不执行,数字 0 也不是有效的 interrupt ID。
IMSIC并未假定一个 interrupt file 的 interrupt ID 跟另一个 interrupt file 的 interrupt ID 有任何关系。通常希望软件不需要横跨 interrupt file 就能进行协调,将相同 interrupt ID 分配给不同的interrupt file中的不同MSI中断源。
系统中所有的 interrupt file 大小不一定皆相同,machine 以及 supervisor level 的 interrupt file 可能与 guest external interrupt 的 interrupt file 大小不同,不同 hart 的 interrupt file 大小也可能不相同。但是不同guest interrupt file的大小必须相同。
一个平台可以为软件提供配置接口去控制 interrupt file 的数量以及大小,但这不在本规范的讨论范围。然而建议只赋予 machine level 更改 interrupt file 的权力就好。
2 MSI encoding
PCI/PCIe等标准规定,来自设备的MSI通常以对齐的32-bit写的方式实现,其MSI的写地址和写数据,都是由该设备的软件驱动配置。
根据设备或是控制器所遵循的标准:
- 地址,可能被限制在较低的4GB(32 bit)范围内
- 写入的值,可能会被限制在16bit的范围内,较高的16bit会保持0
当hart具备IMSIC时,来自设备的MSI,通常由软件配置发送到目标hart对应的IMSIC中,IMSIC将接收到MSI存入对应特权等级的interrupt file中(pending置位),待软件置位enable后,将MSI在本hart中的对应特权等级处理。
- MSI写地址,是与目标interrupt file对应的word大小的特定寄存器的物理地址(通常指向hart index);
- MSI写数据,是中断的identity number,并据于此,将该identity指定的pending寄存器的bit置1。
系统软件,通过配置MSI的写地址和写数据,可以完全控制:
- 哪个 hart 接收指定的设备的 interrupt
- 目标为哪个特权级别或是 virtual hart
- 目标interrupt file(指定的hart关联的IMSIC中的哪个特权等级的interrupt file)存放该MSI的identity number
1和2可以找到目标interrupt file,3可以将该MSI存到interrupt ID对应的bit位置。由于IMSIC最多支持2047个中断,因此MSI identity number的范围为1-2047,因此MSI写数据位宽可以根据实际支持的MSI数量指定。
当 hypervisor extension 实现且设备由 guest os 管理时,设备的 MSI 地址会是 guest physical address,因为他们是由 guest OS 在设备上配置的,这些 guest 地址必须进行 IOMMU 的转换,IOMMU 会将这些 MSI 重新导向 interrupt file 获取正确的 guest external interrupt。
3 Interrupt priorities
在单个 interrupt file 当中,interrupt 的优先级是由 interrupt ID (MSI写数据,minor identity)决定的,号码越低表示优先级越高,完全由软件来配置。
比如,在同一个interrupt file中:
- eip0.bit2表示interrupt ID=2的中断pending
- eip0.bit3表示interrupt ID=3的中断pending
那么interrupt ID=2的中断优先级,高于interrupt ID=3的中断。
4 Reset and revealed state
当 IMSIC 重置时,其所有 interrupt file 的状态将变为有效且一致,但除了 machien level 和 supervisor level 的 interrupt file 中可能指定了eidelivery,其余状态将未指定。
5 Memory region for an interrupt file
IMSIC中的每个interrupt file,都有一个或两个内存映射的32位奇存器,用于接收MSI写操作,如下:
seteipnum_le与seteipnum_be的使用:
-
假设i表示interrupt identity,将i值写入到seteipnum_le会导致对中断i对应的pending位置位。反之若不是以 little-endian 方式的 interrupt ID,则忽略对 setipnum_le 的写入。
-
假设i表示interrupt identity,将i值写入到seteipnum_be会导致对中断i对应的pending位置位。反之若不是以 big-endian 方式的 interrupt ID,则忽略对 setipnum_be 的写入。如果系统只支持小端的字节写入,则会忽略对setipnum_be的写入。
在大多数系统中,seteipnum_le是指向该interrupt file的msi的写端口。对于主要为大端字节顺序构建的系统,seteipnum_be可以作为从某些设备定向到该interrupt file的msi的写端口。
对seteipnumle或seteipnum be的读取,在所有情况下都返回零。因此在硬件实现时,无需实现实际的寄存器与之对应。也就是说从seteipnum的写行为到interrupt file的pending置位可以通过非阻塞的方式实现。
这些内存映射寄存器(seteipnum_be和seteipnum_le),位于一个自然对齐的4-KiB区域(一个页)的物理地址空间中,该空间存在于interrupt file中,即每个interrupt file占用一个页。
interrupt file 的 4k内存空间中:
- 除了已定义的部分,其他 byte 皆被保留,并且须为 read only 0。
- 对于已定义的部分,则只接受对其 32bit 的 read 和 write。若对 read only 的 byte 进行写入,则直接忽略。
seteipnum_be和seteipnum_le作为memory-map的寄存器,需要规划每个hart的地址空间(4KB),因此如果外设的MSI接入到互联模块,可以根据规划的地址进行路由。
6 多个interrupt file的内存空间排布
每个interrupt file都有大小为4KB的内存空间,系统中所有IMSIC的machine-level的interrupt file的内存空间应当在连续的物理地址区间,另外,多个supervisor-level和guest interrupt file对应的内存空间应在另一片连续的物理地址区间内。这样做的好处是更好的实现PMP。
如果一台机器的结构决定了hart被细分成组,每一组都被归到它自己的地址空间中,那么最好的办法就是把hart放在一起。
每组hart的machine-level interrupt file分别存放,同样将每组hart的supervisor-level interrupt file和guest interrupt file分别存放在一起。
参考链接:
- 《RISCV AIA (四) Incoming MSI Controller (IMSIC)》