文章目录
- Introduction
- LPIs
- Redistributors
- ITS
Introduction
Locality-specific Peripheral Interrupts (LPIs) 是一种在GICv3/v4中引入的中断类型。
中断是向处理器发出的一个信号,表示发生了需要处理的事件。中断通常由外围设备产生。LPI通常用于产生消息信号中断(MSIs)的外围设备。
LPI的配置和管理与其他中断类型不同,因为它们的状态保存在内存中,而不是寄存器中。LPI是消息信号中断(MSIs),其翻译由Interrupt Translation Service (ITS)提供。
LPIs
LPI的配置与其他中断类型非常不同,涉及以下内容:
- Redistributor
- ITSs (Interrupt Translation Service)
LPI始终是基于消息的中断,并且它们可以由ITS支持。ITS负责接收来自外围设备的中断,并将其作为LPI转发给适当的Redistributor。一个系统可能包括多个 ITS,在这种情况下,每个 ITS必须单独配置。
对LPI的支持是可选的,由GICD_TYPER.LPIs表示。
Redistributors
Redistributor使用内存中的表来存储LPI配置信息和每个物理LPI的状态。LPI的配置信息存储在由GICR_PROPBASER指向的LPI Configuration table中。LPI配置是全局的,也就是说,所有Redistributor都必须看到相同的配置。通常,一个系统只有一个LPI Configuration table,由所有Redistributor共享。
LPI的状态信息也存储在内存中的表中。这些是由GICR_PENDBASER指向的 LPI Pending tables。每个Redistributor都有自己的 LPI Pending tables,并且这些表在Redistributer之间不共享。
下图显示了三个Redistributor和相关的LPI表:
Initial configuration of a Redistributor
在系统中初始化Redistributers的步骤如下:
- 为LPI Configuration table 分配内存,并使用每个LPI的适当配置初始化该表。
- 在每个Redistributor中设置GICR_PROPBASER以指向LPI配置表。
- 为每个Redistributor的 LPI Pending table 分配内存,并初始化每个表的内容。在系统启动时,这通常意味着将内存归零,这意味着所有LPI INTID都处于inactive状态。
- 将每个Redistributor中的GICR_PENDBASER设置为指向与该Redistributer关联的 LPI Pending table 。
- 在每个Redistributor中将GICR_CTLR.EnableLPI设置为1以启用LPI。
LPI Configuration table
LPI Configuration table中每个LPI INTID包含一个字节。下图显示了每个条目的格式:
尽管SPIs、PPI和SGI的优先级值为8位,但表中只有6位用于记录LPI的优先级。LPI的优先级的较低两位总是被视为0b00。
没有用于记录组配置的字段。LPI始终被视为Non-secure Group 1中断。
LPI配置表的大小取决于LPI的数量。GIC支持的INTID(SPI、PPI、SGI和LPI)的最大数量由GICD_TYPER.IDbits表示。LPI配置表仅处理使用大于8191的INTID的LPI。因此,为了支持所有可能的LPI,LPI配置表的大小计算如下:
GICR_PROPBASER还包括一个IDbits字段,该字段指示LPI配置表支持的INTID的数量。这个数字必须等于或小于GICD_TYPER.IDbits中的值。软件必须为这个数量的条目分配足够的内存。在这种情况下,LPI Configuration table的所需大小变为:
中断控制器必须能够读取为LPI Configuration table分配的内存。
LPI Pending tables
LPI的状态信息存储在内存中。LPI有两种状态:inactive或pending状态。
中断在acknowledged后从pending转换为inactive。
因为只有两个状态,所以在LPI Pending tables中,每个LPI只有一个比特。因此,为了在实现中支持所有可能的INTID,LPI Pending tables的大小必须为
与LPI Configuration table不同, LPI Pending tables 的大小没有调整为考虑从INTID 8192开始的LPI。该表的第一个1KB(对应于INTID 0到8291的条目)存储IMPLEMENTATION DEFINED状态。
如LPI Configuration table中所述,可以使用比硬件支持的范围更小的INTID。GICR_PROPBASER.IBITs控制INTID范围的大小。因此,它同时影响LPI Configuration table的大小和 LPI Pending table的大小。为了支持配置的INTID范围,所需的 LPI Pending table大小如下:
中断控制器必须能够读取和写入分配给 LPI Pending table的内存。通常,Redistributor在内部缓存优先级最高的pending中断,并在有太多挂起中断要缓存或进入低功率状态时将状态信息写入 LPI Pending table。
虽然在拥有的Redistributor中启用了LPI,但软件永远不会直接访问 LPI Pending table。
Reconfiguring LPIs
LPI配置信息存储在内存中的表中,而不是寄存器中。出于性能原因,Redistributors缓存LPI configuration information。这意味着要重新配置LPI,软件必须:
- 更新 LPI Configuration table中的条目。
- 确保更新的全局可见性。
- 使Redistributors中配置的任何缓存无效。
Redistributor中的缓存无效是通过发出ITS INV或INVALL命令来执行的。INV命令使特定中断的条目无效,因此此命令通常用于重新配置少量LPI。INVALL命令使指定集合中所有中断的条目无效。有关ITS命令的详细信息,请参见将新命令添加到命令队列。
如果没有实现ITS,软件必须写入GICR_INVLPIR或GICR_INVALLR寄存器,以使Redistributors重新加载中断配置。
ITS
外围设备通过向ITS中的GITS_TRANSLATER写入消息来生成LPI。这为智能交通系统提供了以下信息:
EventID
这是写入GITS_TRANSLATER的值。EventID标识外围设备正在发送的中断。EventID可能与INTID相同,也可能由ITS转换为INTID。
DeviceID
DeviceID标识外围设备。
ITS处理将消息转换为INTID的过程,INTID可以传递到连接的PE。
物理LPI被分组在集合中。集合中的所有LPI都路由到同一个Redistributor。软件将LPI分配给集合,使其能够有效地将中断从一个PE到PE。
ITS使用三种类型的表来处理LPI的转换和路由。这些是:
Device table
每个ITS有一个Device table。Device table将DeviceID映射到 Interrupt Translation Tables。
Interrupt Translation Tables
每个设备ID或外围设备有一个Interrupt Translation Table(ITT)。ITT包含EventID和INTID之间的外围设备特定映射。它们还包含INTID所属的集合。
Collection table
每个ITS有一个 Collection table。 Collection table将集合映射到Redistributers。
当外围设备向GITS_TRANSLATER写入消息时,ITS执行以下操作:
- 使用DeviceID从Device table中选择适当的条目。此条目标识要使用的Interrupt Translation Table。
- 使用EventID从所选的Interrupt Translation Table中选择适当的条目。此条目提供INTID和集合ID。
- 使用 Collection ID在Collection table表中选择所需条目,该表将返回路由信息。
- 将中断转发到目标Redistributor。
下图说明了这一过程:
ITS可以选择支持许多硬件集合。硬件集合是ITS内部保存配置的地方,而不是将其存储在内存中。GITS_TYPER.HCC报告支持的硬件集合的数量。您仍然可以将其视为 Collection table的一部分,只是它没有存储在内存中。
The command queue
ITS使用存储器中的命令队列进行控制。命令队列是一个循环缓冲区,由以下三个寄存器定义:
GITS_CBASER
此寄存器指定命令队列的基地址和大小。命令队列必须对齐64KB,并且大小必须是4KB的倍数。命令队列中的每个条目都是32个字节。GITS_CBASER还指定ITS在访问命令队列时使用的可缓存性和可共享性设置。
GITS_CREADR
此寄存器指向ITS将处理的下一个命令。
GITS_CWRITER
这个寄存器指向队列中应该写入下一个新命令的条目。
下图显示了命令队列的简化表示:
Arm通用中断控制器体系结构规范GIC体系结构版本3.0和4.0提供了ITS支持的所有命令的详细信息以及这些命令的编码方式。
Initial configuration of an ITS
要在系统启动时配置ITS,软件必须:
1.为Device tables和Collection tables分配内存。
GITS_BASER[0.7]寄存器指定ITS Device tables和Collection tables的基本地址和大小。软件使用这些寄存器来发现ITS支持的表的数量和类型。然后,软件必须分配所需的内存,并将GITS_BASERn寄存器设置为指向该分配的内存。
2.为命令队列分配内存。
软件必须为命令队列分配内存,并将GITS_CBASER和GITS_CWRITER设置为指向该分配内存的起始位置。
3.启用ITS。
当分配了表格和命令队列后,可以启用ITS。这是通过将GITS_CTLR启用位设置为1来完成的。
一旦设置了GITS_CTLR.Enable,GITS_BASERn和GITS_CBASER寄存器将变为只读。
The sizes and layout of Collection and Device tables
Collection tables和Device tables的位置和大小使用GITS_BASERn寄存器进行配置。软件必须为这些表分配内存,并在启用ITS之前配置GITS_BASERn寄存器。
软件可以分配一个平面(单级)表或两级表。这是由GITS_BASERn.Indirect指定的。
对两级表的支持是可选的。如果ITS仅支持平面表格,则GITS_BASERn.Indirect为RAZ/WI。
Flat level tables
使用平面表,将单个连续的内存块分配给ITS以记录映射。在启用ITS之前,软件需要在存储器中填充零。此后,ITS在处理来自命令队列的命令时填充该表。
下图显示了一个平面表。该表是一个连续的内存块,GITS_BASERn寄存器指向该表的基址:
表的大小随DeviceID或CollectionID的宽度而定(视情况而定)。所需尺寸可计算如下:
其中entry_size是每个表条目的字节数,由GITS_BASERn.entry_size报告。
配置GITS_BASERn寄存器时,表的大小指定为页数。页面大小由GITS_BASERn.page_size控制,可以是4KB、16KB或64KB。因此,上面给出的公式的结果必须rounded up到下一个整页大小。
例如,如果系统实现8位DeviceID,则每个表条目的字节数为8,并且使用4K页面大小:
28*8=2048字节=4K(rounded up到下一整页).
Two-level tables
对于两级表,软件分配一个一级表和几个二级表,如下图所示:
第一级表由软件填充,每个条目要么指向第二级表,要么标记为无效。二级表在分配给ITS之前必须用零填充,并且在ITS处理来自命令队列的命令时由ITS填充。
当ITS被启用时(GITS_CTLR.enabled==1),软件可以分配额外的第二级表并更新相应的第一级表条目以指向这些额外的表。启用ITS时,软件不得删除分配或更改现有分配。
每个二级表的大小为一页。与平面表一样,页面大小由GITS_BASERn.page_size配置。因此,它包含page_size/entry_size)条目。
每个一级表条目表示(page_size/entry_size)ID,可以指向二级表,也可以标记为无效。任何使用与无效条目对应的ID的ITS命令都将被丢弃。
一级表所需的大小可以通过以下方式计算:
与单级表一样,第一级表的大小被指定为页数。因此,公式的结果必须rounded up到下一个整页大小。
Adding a new command to the command queue
要将新命令添加到命令队列,软件必须:
1.将新命令写入队列。
GITS_CWRITER指向命令队列中不包含有效命令的下一个条目。软件必须将命令写入该条目,并且必须确保全局可见性。
2.更新GITS_CWRITER。
软件必须将GITS_CWRITER更新到不包含新命令的下一个条目。更新GITS_CWRITER会通知ITS已添加新命令。
软件可以同时向队列中添加多个命令,前提是命令队列中有足够的空格,并且相应地更新了GITS_CWRITER。
3.等待ITS读取命令。
软件可以通过轮询GITS_CREADR来检查ITS是否已读取该命令。当GITS_CWRITER.Offset等于GITS_CREADR.Offset时,ITS已读取所有命令。
或者,可以添加INT命令以生成中断,以发出ITS已读取一组命令的信号。
ITS按顺序读取命令队列中的命令。但是,这些命令对Redistributers的影响可能会出现无序现象。SYNC命令可以确保以前发布的命令的效果是可见的。
当GITS_CWRITER指向GITS_CREADR之前的位置时,命令队列已满。在尝试添加新命令之前,软件必须检查队列中是否有足够的空间。
Mapping an interrupt to a Redistributor
每个可以向ITS发送中断的外围设备都有自己的DeviceID。每个DeviceID都需要自己的中断转换表(ITT)来保存其EventID到INTID的映射。软件必须为ITT分配内存,然后使用MAPD命令将DeviceID映射到ITT,如以下命令所示:
当外围设备的DeviceID已映射到ITT时,它可以发送的不同EventID必须映射到INTID和集合(collection)。每个集合都映射到一个目标Redistributor。
INTID可以使用MAPTI和MAPI命令映射到集合。当EventID和INTID相同时,将使用MAPI命令,如下所示:
当EventID和INTID不同时,使用MAPTI命令:
集合使用MAPC命令映射到Redistributor:
目标Redistributor的标识取决于GITS_TYPER.PTA:
•GITS_TYPER.PTA == 0
Redistributor由其ID指定,该ID可以从GICR_TYPER.Processor_Number读取。
•GITS_TYPER.PTA ==1
Redistributor由其物理地址指定。
Example
计时器的DeviceID为5,并使用2位EventID。我们希望EventID 0映射到INTID 8725。分配给计时器的ITT位于地址0x84500000。
我们决定使用集合编号3,并将中断传递给ID为7的Redistributor。
此操作的命令序列如下:
该示例假设之前没有设置任何映射,并且GITS_TYPER.PTA==0。
Migrating interrupts between Redistributors
您可以使用几种不同的技术将中断从一个Redistributor移动到另一个:
重新映射集合
软件可以通过重新映射整个集合,将所有中断从一个Redistributor移动到不同的Redistributer。这通常是在连接到Redistributor的处理元素断电时完成的,并且中断必须移动到另一个Redistributer。这可以使用以下命令序列来完成:
在这个命令序列中,RDADDR1是以前的目标Redistributor,而RDADDR2是新的目标Reditributor。
如果有多个集合以RDADDR1为目标,那么我们将需要多个MAPC命令,每个集合一个。此序列假定所有集合都被重新映射到同一个新的目标Redistributor。
将中断映射到其他集合。单个中断可以重新映射到不同的集合。这可以使用以下命令序列来完成:
在这个命令序列中,RDADDR1是重新映射中断之前,中断最初分配给的集合所针对的Redistributor。
Removing interrupt mappings
要重新映射或删除中断的映射,软件必须执行以下操作:
- 禁用中断当前映射到的物理INTID。这是在LPI配置表中完成的。有关更多信息,请参阅重新配置LPI。
- 发出DISCARD命令。这将删除中断的映射,并清除映射的INTID的pending状态。
- 发出SYNC命令并等待,直到该命令完成。
命令完成后,不再向中断之前映射到的Redistributor发送任何中断。
Remapping or removing the mapping of devices
要更改或删除设备的映射,软件必须执行以下操作:
- 按照删除当前映射的外围设备的每个EventID的中断映射中的步骤进行操作。
- 发出MAPD命令以重新映射设备。或者,将有效位清除为0的MAPD命令将删除映射。
- 发出SYNC命令并等待,直到该命令完成。