1. 中断类型
GIC700管理SPIs、SGIs、PPIs和LPIs。如果支持GICv4.1架构,那么SGIs和LPIs可以被直接注入vPEs
2. multichip操作
包含多个芯片的系统可以有多个外部连接的SoC,也可以有多个连接在单个物理封装内的SoC。在所有情况下,每个SoC都集成了GIC700。一个多芯片系统最多可以有16个芯片。 为了控制配置中所有芯片的一致性,并使GIC对操作系统显示为单个实体,GIC700使用一组寄存器来定义芯片之间的连通性。这些寄存器被称为路由表,由以下三种寄存器类型组成:
- GICD_CHIPR
(chip register):这组chip寄存器定义了路由表。它指定了chip拥有的SPI,并且chip如何被访问。这个寄存器存在于每一个chip中,所以每一个chip都会拥有路由表的一个副本。 - GICD_DCHIPR (default chip register):指明了当前对路由表的一致性负责的chip,并指示什么时候对路由表进行更新。
- GICD_CHIPSR (chip status register):表明当前chip状态的细节。
在复位时,多芯片系统中的每一个芯片都有一个有效的独立的全功能的GIC。GICD_CHIPSR寄存器中RTS位表明当前各个chip上的GIC处于disconnected状态
当多芯片被连接在一起之后,每32个SPI被分为一组(SPI block),每一个组都会被一个特定的chip所拥有。所以芯片之间的SPI空间是分段的。
- 根据路由表,没有被任何芯片拥有的SPIs是不能被使用的
- 只有芯片拥有的SPIs,其在chip上的SPI信号线才能被使用。但是基于消息的SPIs,如果被任意一个芯片拥有,那么就可以使用
- 在某个时刻,路由表只能执行一个操作。因此,软件必须在对路由表执行任何操作前确保GICD_DCHIPR.PUP==0
3. 中断分组与安全
每一个中断都属于一个中断分组,每一个中断分组:
- 表明中断在这个分组内的安全状态,这取决于核的异常等级
- 拥有单独的使能开关,用于控制这个分组的中断是否能转发到核上
- 对后续在核接口上的路由策略会有影响
安全和分组:
- Secure group0:interrupt for EL3(Secure firmware)
- Secure group1:interrupt for EL1(trust OS)
- non-Secure group1:interrupt for non- Secure(OS or hypervisor, or one of both)
GICD_CTRL.DS(disable secure):
- 0:Secure enabled
- 1:Secure disabled
在具有安全状态的系统中,group0总是安全的。 如果一个系统有安全的概念,但一个或多个核没有,那么不能禁用安全性。每个核只能启用与它所支持的安全状态相对应的中断组。
4. 亲和路由和指派
GIC700使用亲和路由(一种分层方案)来识别连接的核,并将中断路由到特定的核。 Arm架构在核内定义了一个寄存器,用于标识核在系统中的逻辑地址。这个寄存器称为多处理器标识寄存器(multi processor identification register MPIDR),具有层次结构格式。层次结构的每一层都称为亲和级别,首先指定最高的亲和级别:
- 对于32位Armv8处理器,MPIDR定义了三个级别的亲和性,隐含的亲和性级别3的值为0。
- 对于64位Armv8处理器,MPIDR定义了四个亲和级别。
GIC700将支持多个硬件线程的处理器的每个硬件线程视为一个独立的内核。 核心的亲和度由4个8位字段表示,使用点十进制记数法,分别为
- aff0_thread == 1
- aff0_thread == 0

5. RAMs和ECC
GIC-700使用多个ram来存储所有类型中断的一系列状态。在典型操作中,ram对软件是透明的。 每个RAM都可以使用具有单错误校正和双错误检测(SECDED)的ECC来防止错误。 如果检测到单错误或双错误,则在软件可见的错误记录中报告。
5.1 RAM错误模拟
对于每一个RAM,软件可以用GICx_ERRINSR寄存器来模拟一个暂时的ECC单bit错误或者双bit错误。
GICR_ERRINSR寄存器对应的是GCI中的RAM。
GICD_ERRINSR寄存器对应的是以下RAM:
- 0X0 SGI RAM
- 0X1 SPI RAM 0
- 0X2 SPI RAM 1
- 0X3 SPI TGT RAM
- 0X4 SPI LPI RAM
- 0X5 LPI RAM BANK 0
- 0X6 LPI RAM BANK 1
- 0X7 LPI RAM BANK 2
- 0X8 LPI RAM BANK 3
- 0X9 PENDING TABLE MAP CACHE
- 0XA VICM RAM
- 0XB VICM SEARCH RAM
- 0XC VTGT RESIDENCY RAM
- 0XD VTGT STORE RAM
- 0XE VTGT SEARCH RAM
- 0XF CC RAM
这些寄存器导致错误被插入到相关RAM中的指定地址和位置。ECC编码器和解码器被检查,但不修改RAM内容。除非安全软件设置GICD_SACG.GICTNS为1,否则这些寄存器都只能安全访问。 软件插入错误后,与普通的ECC错误一样,GIC将该错误报告到相关的错误记录中。但是,软件注入的错误对GIC的功能没有影响,所以软件可以在运行过程中注入错误。 如果同时发生实际错误,那么GIC将报告实际错误,并触发该中断类型的正常机制。
5.2 Scrub
GIC700在RAM中保存重要的编程和中断状态,这是由单错误纠正和双错误检测(SECDED)保护的。 但是,有些RAM内容可能在很长一段时间内是静态的,如果不定期访问某个特定地址,就有可能累积错误。为了防止这种情况的发生,软件可以通过设置GITS_FCTLR.SIP、GICR_FCTLR.SIP、GICD_FCTLR.SIP周期性地触发对RAM的低优先级清理。这个过程触发一次检查,如果必要的话,会触发一次所有有效RAM条目的回写。在清理过程中发现的任何错误也会在相关的RAS错误记录中报告。
6. 直接注入
GIC700支持将vSGIs和vLPIs直接注入到虚拟机中,不需要处理器在虚拟环境中改变状态去执行hypervisor。
为了将vPEs与GIC之间映射起来,软件必须执行ITS中的VMAPP命令。
GIC700需要在VMAPP命令中使用Valid(V)和Allocate(A)两个bit位。以下操作会导致不可预测的行为:
- 当vPE上已经存在映射关系时,使用VMAPP(V1A1)命令
- 在VSYNC命令完成前,在VMAPP(V1A1)命令之后,针对同一个vPE使用VMAPP(V1A0)
- 当任何其它ITS与当前vPE存在映射关系时,使用VMAPP(V0A0)
- 针对vPE的VMAPP命令的有效数据字段除了RDbase以外都不相同
为了维护有关映射vPE的信息,GIC为每个芯片使用单个vPE表,该表在芯片上的所有redistributor和ITSs之间共享。
6.1 Doorbells
门铃中断是物理的LPIs中断,用于向hypervisor表明有一个中断发给vPE。
每一个vPE都可以通过ITS的VMAPP或者VMOVP命令指定一个独特的门铃中断。当给vPE的第一个vLPI或者vSGI中断处于pending状态时,GIC为vPE产生一个门铃中断。门铃中断在vPE变成resident状态时被屏蔽
GIC700有以下门铃中断特征:
- 门铃中断ID必须是独特的,它不能被映射到任何ITS上的任何DeviceID和EventID
- GIC700不支持单独的门铃中断,所以GITS_TYPER.nID == 1
- 只有当vPE上一次resident时相应的虚拟中断分组是enable的,才会产生门铃中断。在被映射之后vPE没有被resident
- GIC会忽略VMAPP和VMOVP命令中门铃中断ID超出GICR_PROPBASER.IDbits范围的命令
门铃属性在第一次创建映射时被缓存。可以通过更新LPI Configuration表并从任何映射了vPE的ITS发出VINVDBL命令来更改属性。 门铃属性不会在芯片之间传输,如果VMOVP将vPE移动到远程芯片,则会重新获取门铃属性。 当vPE映射存在时,软件不能禁用GICR_CTLR.EnableLPIs位,因为这样会阻止门铃中断获取属性,并且它们被缓存为禁用。
6.2 Resident和VMOVP
使用ITS VMOVP命令,软件可以在本地和远程芯片上的PE之间自由移动vPE。
GIC700只支持GITS_TYPER.VMOVP == 1。因此,VMOVP命令必须只在GIC的单个ITS(映射到vPE)上运行,以确保系统中的所有ITS能够无缝地继续向vPE发送中断。GIC只在下次访问vPE时更新ITS。 门铃中断被传送到vPE映射到的PE。但是,GIC支持使vPE驻留在同一芯片上的任何PE上(以交付中断),因为当前的映射没有VMOVP。也就是说,在使vPE驻留之前不需要发出VMOVP,除非移动芯片或在不同PE之间平衡门铃。
为了将中断传递给一个vPE,它必须驻留在PE上,这是通过使用相关PE的GICR_VPROPBASER并轮询GICR_VPROPBASER.Dirty == 0来完成的。在dropping GICR_VPROPBASER.Dirty之前,GIC尝试向PE交付任何中断。
建议在创建vPE驻留之前设置CPU虚拟组启用。这是为了确保GIC可以强制中断到达PE,从而防止在中断交付之前与GuestOS到达WFI的竞争。如果软件试图做以下任何一件事,行为是不可预测的:
- 让vPE驻留在多个PE上
- 当vPE没有映射到芯片上的PE时,让vPE驻留
- 对一个驻留的vPE执行VMOVP命令
GIC的设计是为了确保最高优先级的中断总是准备就绪,等待vPE驻留。然而,在GICD_FCTLR2中有两个比特控制是否在某些条件下延迟驻留更改:
- GICD_FCTLR2.RWC——resident wait during command
- GICD_FCTLR2.RWS——resident wait during search
建议正常操作时不设置RWC和RWS位。 当通过写入GICR_VPENDBASER.Valid == 0使vPE非驻留时,GICR_VPENDBASER.PendingLast表示是否有vPE的剩余中断。如果在写入GICR_VPENDBASER.Dirty== 0,那么门铃仍然被屏蔽,软件必须在将来的某个时候使vPE再次驻留。 如果在GICR_VPENDBASER.Valid被清除时,写GICR_VPENDBASER.PendingLast == 1,那么GIC会优化驻留握手并且在不检查vPE是否还有中断的情况下屏蔽门铃中断。
7. SGIs
软件生成中断(Software Generated Interrupts, sgi)是一种处理器间中断,即从一个内核产生的中断发送到其他内核。系统中的每个核(包括vPE)都独立于其他核来处理一个SGI。SGI的优先级和其他设置对于每个核也是独立的。物理SGI是通过写入产生中断的核心的CPU接口中的系统寄存器来生成的。SGI是边沿触发的。
每一个核或者vPE最多可以拥有16个SGI。每一个SGI都有不同的中断号,范围是从0到15.
7.1 SGI编程
- 物理SGI:控制物理的SGI需要使用每一个核对应的GICR中的寄存器
- 虚拟SGI:控制虚拟的SGI,软件可以使用vSGI的ITS命令。软件也可以通过写vPE的virtual pending table来控制vSGI,然后发送一个VMAPP命令给GIC开辟对应的地址空间。在发送VMAPP命令后,软件不能向virtual pending table发起写请求用于尝试产生一个虚拟SGI。
7.2 SGI直接注入
通过将vPE和SGI-INTID写入gits_sgi寄存器,软件可以直接注入sgi。 gits_sgir寄存器总是使用GICD ACE5-Lite从属接口访问,即使在分布式系统中也是如此。 与物理sgi不同,vsgi没有活动状态,因此不需要去激活。如果vPE没有映射到GIC用来生成vSGI的ITS上,则该GIC生成调试错误。
7.3 SGI multichip
当芯片被连接时,物理sgi将根据目标亲和路由值,路由到目标芯片。虚拟sgi被路由到目标vPE,与vPE当前映射到的芯片无关。
8. PPIs
私有外设中断(PPI private peripheral interrupt)标识一个中断源,例如定时器,它是内核私有的,并且独立于另一个内核的相同源。ppi通常用于与特定核紧密耦合的外设。
连接到与一个核相关的PPI输入源的中断只被发送到该核。每个核独立于其他核处理一个PPI。PPI的设置对于每个核也是独立的。
一个PPI对一个核是唯一的。但是,其他核的ppi可以具有相同的INTID。每个目标核最多可拥有48个PPI,其中每个PPI在ID16-ID31或ID1056-ID1087范围内具有不同的INTID。要使用ID1056-ID1087范围,核必须支持GICv3.1扩展。
PPI信号默认为active-LOW电平敏感。但是,可以使用GICR_ICFGR1、GICR_ICFGR2E和GICR_ICFGR3E将PPI信号设置为电平敏感或边缘触发。
8.1 PPI信号
每个PPI都是一个物理中断信号,可以配置为电平敏感中断或边缘触发中断。物理PPI信号的两种配置是:
- 电平敏感:当中断输入被断言时,中断处于挂起状态。Arm架构下PPI默认为active-LOW。但是可以更改这些默认设置。
- 边沿触发:中断输入上的上升边界导致中断成为挂起。当中断被CPU接口激活时,挂起的位稍后被清除。
为使系统正确设置,必须对GICR_ICFGR1、GICR_ICFGR2E和GICR_ICFGR3E寄存器进行编程。
8.2 PPI编程
控制物理的PPI需要使用处理器核对应的GICR的寄存器
9. SPIs
共享外设中断(SPI shared peripheral interrupt)是由整个系统可访问的外设(如USB接收器)生成的,并且可以连接到多个核。SPI通常用于与特定核没有紧密耦合的外设。 可以对每个SPI进行编程,以针对特定核或任何核。激活一个核上的SPI将激活所有核的SPI。也就是说,GIC-700最多允许一个核激活SPI(不能由多个内核激活)。每个SPI的设置也在所有核之间共享。 SPI可以通过连接输入生成,也可以通过写入ACE5-Lite附属编程接口生成。GIC-700可以支持多达1984个spi,对应于spi Collators上的spi输入信号。每个SPI Collator有1024个信号的限制。可用spi的数量取决于实现的配置。第一个SPI的ID号是32。允许的ID值为32步,取值范围如下:
- ID32-ID991
- ID4096-ID5119
在配置GIC时,可以将部分或全部SPI分配为基于消息的SPI,也可以将所有SPI设置为物理SPI信号。如果将SPI ID分配为物理SPI输入信号,则软件仍然可以使用该SPI ID作为基于消息的SPI,前提是硬件确保SPI信号保持在表示非活动状态的逻辑级别。
可以配置每个SPI是在上升沿触发还是高电平敏感。
SPI Collator将基于线的中断转换为消息,以减少系统布线,并允许更积极的GIC时钟门控,以降低功耗。
SPI通过GICD寄存器地址空间进行编程,该地址空间一致地分布在所有已配置的芯片上,以向操作系统(OS)提供单一视图。
可以通过使用GICD_SETSPI_NSR或GICD_SETSPI_SR寄存器触发有效的SPI。
9.1 SPI信号
每个SPI都是一个物理中断信号,可以配置为电平敏感中断或边缘触发中断。
- 电平敏感:当中断输入被断言时,中断正在挂起。SPI在默认情况下是active-HIGH的,但是可以更改这些默认设置
- 边沿触发:中断输入上的上升边界导致中断成为挂起。当中断被CPU接口激活时,挂起的位稍后被清除。
要为系统设置正确的设置,必须对GICD_ICFGRn或GICD_ICFGRnE寄存器进行编程。 GIC-700在每个中断线输入上提供可选的同步器。GIC还提供返回信号spi_r,以便在跨域边界发送边缘触发中断时启用脉冲扩展器。
9.2 SPI路由以及1 of N模式
如果GICD_TYPER.No1N==0,则GIC700支持1 of N类型SPI中断。可以将SPI编程为针对多个核心,并且GIC700可以选择哪些核心接收SPI。 当相关的GICD_IROUTERn.Interrupt_Routing_Mode == 1, GIC为SPI选择合适的核心。 当GICD_IROUTERn.Interrupt_Routing_Mode == 0,则SPI路由到GICD_IROUTERn剩余字段指定的核心。 GIC700只发送SPI到已上电并启用相关中断组的内核。GIC700优先考虑被认为是活动的核,但如果没有活动的内核,则选择非活动的内核。 GIC700所做的选择由1 of N 模式的特点所控制或影响:
-
cpu_active signal:cpu_active信号是对应于特定核对应的GICR的输入。当cpu_active信号为LOW时,它向GIC表明该核处于透明的低功耗状态,并且如果没有其他可能的选项,则将其选择为SPI的目标。理想情况下,如果没有明确的软件干预,低功耗状态的核不会被唤醒,这样核就会在低功耗状态上花费更多的时间。通常,电源控制器或电源控制逻辑产生cpu_active信号。如果该信号在系统中不可用,则输入必须为高电平。
-
GICR_CTLR.DPGxx(disabled processor group):设置DPG位可以防止特定组的1 of N中断被发送到该核。尚未到达核的任何中断都将被GIC召回并重新确定优先级。
-
processor and GICD group enables and GICR_WAKER.ProcessorSleep:一个1 of N类型的中断在以下情况出现时,不会发给核:
- 核处于asleep状态,通过GICR_WAKER.ProcessorSleep表明
- 在核上,该SPI的中断分组被禁用
-
中断类:这是GIC-700提供的具体实现中定义的特性。通过写入相关的GICR_CLASSR寄存器,可以将每个核分配给class 0或class 1。一个由GICD_IROUTERn.Interrupt_Routing_Mode编程为1 of N类型的SPI,可以通过GICD_ICLARn寄存器编程为类0,类1或二者兼有。缺省情况下,1 of N类型的spi是二者兼有的,因此中断类特性在缺省情况下是关闭的。系统可以将此分区用于任何目的,例如在Arm biglitletm系统中,所有的大核都可以在class 1中,而小内核可以在class 0中,允许根据它们需要的处理量对1 of N类型SPI进行分区。
-
GICD_CTLR.E1NWF:GICD_CTLR.E1NWF位控制如果没有其他可用的1 of N SPI目标,GIC700是否唤醒核心。GIC试图唤醒尽可能少的核心,只有在没有其他可能的目标唤醒能够接受1 of N中断时才唤醒一个核心。因此,GIC使用GICR_CTLR.DPG和GICR_CLASSR.Class位来确定是否有可以接受中断的内核处于唤醒状态。如果一个合适的核没有处于唤醒状态,那么GIC就唤醒一个核心。ARM强烈建议使用GICD_CTLR。在这种情况下,还必须设置GICR_CTLD.DPGx位用来指定一个核心是否可能及时接受一个特定的中断组。在找到一个核之前,GIC不会继续唤醒核。GIC-700使用两个通道来尝试找到1 of N中断的最佳位置,通过在以下之间使用轮询仲裁器:
- 任何具有cpu_active信号为高的核,且对于中断是完全启用的,并且没有其他挂起的中断。
- 为中断完全启用且没有高于1 of N中断优先级的中断正在处理的任何核.
如果1 of N中断都没有可用的选项,中断将被分配给任何合法的目标,并定期重新评估,以确保它不会被其他具有相同优先级的spi排除在外。
9.3 multichip下SPI拥有者
SPI block的所有者是由GICD_CHIPR
当从芯片中移除或添加SPI块时,所有与SPI块相关的编程都返回到重置状态。 不能更改在线芯片的SPI_BLOCK_MIN,因为结果是不可预测的。修改SPI_BLOCK_MIN需要以下步骤:
- 通过设置GICD_CHIPR
.SocketState = 0使芯片脱机。 - 当芯片重新连接时修改SPI_BLOCK_MIN。
9.4 multichip下SPI操作
当建立路由表时,可以通过任何连接的芯片对spi进行编程,并且可以通过拥有spi的芯片的跨芯片接口对更新的存储值进行访问。 spi可以通过编程相关的GICD_IROUTERn寄存器路由到远程芯片。使用Affinity2或Affinity3来定位远程芯片,并且可以使用GICD_CFGID.AFSL来发现亲和级别。
如果SPI块中的SPI被发送到多个芯片,建议不要读写GICD_ISACTIVERn(E), GICD_ICACTIVERn(E), GICD_ISPENDRn(E)和GICD_ICPENDRn(E)寄存器。这些操作是低效的,这些寄存器不需立即操作。
可以通过写入GICD_SETSPI_NSR, GICD_CLRSPI_NSR, GICD_SETSPI_SR和GICD_CLRSPI_SR来设置SPI中断进入pending状态。为了高效操作,建议将源代码编写其芯片拥有的SPI id。如果您统中拥有其他SPI,则支持这些SPI id。
默认情况下,GIC700不保证写入中断pending bit的顺序。这种行为意味着在bresp信号断言后,正在设置的挂起位和正在由GIC处理的激活位之间存在竞争。要确保写操作总是传播到序列化点,需要设置GICD_FCTLR.POS = 1。
SPI Collator的SPI线总是连接到芯片上最低拥有的SPI。
例如,如果GICD_CHIPRn.SPI_BLOCK_MIN = 4, SPI Collator连接到芯片x驱动的SPI id从160开始(由(4 × 32) + 32 = 160计算)。
10. ITS
GIC700在每个芯片上最多支持32个ITS。每个ITS用于将外设发出的基于消息的中断转换成LPIs或者vLPIS。每个ITS都符合GICv3和GICv4.1体系结构,并负责将带有EventID和DeviceID的翻译请求映射到INTID和目标。如下图所示为物理INTID(pINTID)的ITS流程。

为了减少内存流量并将中断延迟保持在最低限度,GIC-700在每个ITS中有三个双向集关联缓存:
- DeviceID cache
- EventID cache
- Collection cache
如果缓存不需要ECC保护,可以将ECC从每个RAM中单独移除。
DeviceID通常是一个从PCIe RequestorID派生出来的不连续数字。为了确保这不会导致稀疏的DeviceID表和浪费内存,GIC700支持间接设备表(GITS_BASERn.Indirect = 1),其中第一级表指向可以在运行时分配的子表。
GIC700只使用内存支持的集合,这意味着在通过写入GITS_CTLR.Enable来启用ITS之前。启用后,必须为Device表、Collection表和ITS命令队列分配内存。为了遵从这个体系结构,软件必须预先将这些表清除为0,除了指向已清除的二级设备表的指针,除非这些表先前已被GIC-700填充。
当软件使用GICv4.1命令时,它必须在启用ITS之前提供一个指向芯片范围vPE表的指针。
对于所有配置,GITS_TYPER.PTA都是0,这意味着ITS命令中对处理器核的所有引用都是通过gicr_type.ProcessorNumber字段实现的。
10.1 ITS cache的控制、锁和测试
GIC700可以在EventID缓存中锁定某些中断转换。如果在缓存中丢失了翻译,则可能需要多次内存读取才能从内存中获取所需的数据。这种行为可能导致某些LPI无法接受的延迟范围。GIC700可以将某些翻译锁定到ITS缓存中,并提供以下保证:•锁定在ITS缓存中的中断总是命中且不需要任何翻译。
在更新GITS_BASERn寄存器时,自动管理ITS缓存并根据需要使其失效。因此,不需要软件干预。但是,为了帮助调试和集成测试,可以通过在GITS_FCTLR寄存器中设置相关位来强制适当的缓存失效。事件缓存的强制无效将放弃所有锁定项。
当软件提供DeviceID、EventID和正确的GITS_OPR.LOCK_TYPE(ITS lock =2)时,GITS_OPR和GITS_OPSR寄存器控制cache锁定。GIC会尝试执行cache锁定行为,并且在GITS_OPR寄存器中报告当前的状态。如果执行锁定成功,GITS_OPSR.REQUEST_COMPLETE == 1并且GITS_OPSR.REQUEST_PASS == 1。
每个缓存集都是双向关联的。每个缓存集中只能锁定一个条目。在GITS_OPSR中,任何试图在一个集合中同时锁定这两种方式的尝试都会报告为失败。还可以使用GITS_OPR寄存器来解锁被锁定的条目。
GITS_OPR寄存器有两个测试特性:
- trial:通过向GITS_OPR写入DeviceID和EventID来测试映射当GITS_OPR.LOCK_TYPE = 1 (Trial)时。这导致ITS转换提供的DeviceID和或EventID对,并在GITS_OPSR中报告生成的翻译数据。GIC还报告翻译是否失败——GITS_OPSR.REQUEST_PASS == 0,如果遇到锁定的表项,则为GITS_OPSR.ENTRY_LOCKED。中断不会设置为挂起。
- track:可以用来检测某个EventID和或DeviceID对的到达,GIC通过对GITS_OPSR.REQUEST_COMPLETE字段的设置来报告这些。
当任何GITS_OPR操作(除了Track)正在进行时,GIC 会设置GITS_OPSR.REQUEST_IN_PROGRESS位,并且不接受对GITS_OPR的更新,直到前一个操作完成。为了确保该操作被接受,建议在写入后读取GITS_OPR值。可以通过写入GITS_OPR.LOCK_TYPE ==track abort,来中止track操作。
10.2 MSI-64
MSI-64 Encapsulator可用于将DeviceID合并为ITS中GITS_TRANSLATER寄存器的单个内存访问写操作。
ITS将DeviceID/EventID对转换为LPI物理intid。
正常的MSI/MSI64写操作在低16位或32位数据中包含EventID。但是,必须使用不同的方法传输DeviceID。DeviceID通常直接派生自PCIe RequestorID或系统内存管理单元(SMMU)流。如果EventID大于16位,则16位MSI写入用零填充。
GIC700 ITS支持两种机制:
-
awuser_*_s signal:DeviceID通过边带用户信号到达。但是必须确保恶意软件不能直接或间接地使用与真实设备匹配的DeviceID对GITS_TRANSLATER寄存器执行访问。
-
MSI-64:当支持MSI-64时,ITS期望DeviceID位于对GITS_TRANSLATER寄存器64位写操作的高32bit。为了防止恶意软件访问GITS_TRANSLATER寄存器并欺骗任何设备,ARM建议将GITS_TRANSLATER寄存器移动到由管理程序保护的任意页面。GIC700使用两种方法来支持这一点:
-
MSI-64 Encapsulator将访问GITS_TRANSLATER地址的页面地址(由msi_translator_page绑定信号设置)修改为由msi64_translator_page信号设置的系统定义页面。
-
当ITS共享一个ACE5-Lite附属端口时,its_transr_page_offset绑定信号允许GITS_TRANSLATER寄存器页移动到地址映射中的任何位置,以匹配与GICD地址映射重置无关的msi64_translator_page信号值。msi64_translator_page和its_transr_page_offset信号,或其中之一,不得位于任何其他GIC寄存器页的顶部。为了确保对软件隐藏这种映射方法,对GITS_TRANSLATER寄存器的所有访问都必须通过encapsulator或类似的嵌入式功能。
image-20241215131231982
-
10.3 ITS命令和错误
需要测试一下unused的命令,cmd会停在这个unused cmd地方,无法继续往下执行,只能从新建立cmd queue
10.3.1 VMAPP命令
将vPEID对应的vPE表项与目标GICR以及虚拟LPI pending table映射起来。
VMAPP vPEID, RDbase, VPT_addr,VPT_size, V
10.3.2 VMAPI命令
将DeviceID和EventID定义的事件与vPEID定义的ITT表项映射起来(vINTID=EventID)
VMAPP DeviceID, EventID, Dbell_pINTID
需要注意的点:
- vINTID=EventID ≥ 0x2000(有效的LPI中断)
- 该命令等价于VMAPTI DeviceID, EventID,EventID, pINTID, vPEID
- Dbell_pINTID要么是1023号中断要么是≥0x2000号中断(有效的LPI中断),如果门铃中断号是1023,则不会产生物理门铃中断
10.3.3 VMAPTI命令
将DeviceID和EventID定义的事件与vPEID定义的ITT表项映射起来
VMAPTI DeviceID, EventID, vINTID, Dbell_pINTID,vPEID
与VMAPI命令类似,只是EventID与vINTID可以不强制相同。
10.3.4 VINVALL命令
该命令用于确保vPEID相关的cache GICR信息与在内存中的LPI config table中的信息是一致的
VINVALL vPEID
10.3.5 VSYNC命令
该命令用于确保,所有针对vPEID的未完成的ITS操作,在其它ITS命令执行之前,都是全局可见的。在执行VSYNC之后,之前所有命令的效果必须应用于后续对GITS_TRANSLATER的写操作。
VSYNC vPEID
**硬件在这个地方不使用vPEID这个参数
10.3.6 VMOVP命令
这个命令用于更新vPEID和目标RDbase相应的在vPE表中的表项。软件必须使用SequenceNumber和ITSList,在多个ITS之间对VMOVP命令进行同步。
软件必须确保该命令不会执行在正在目标GICR上调度的vPEID,否则系统的行为将不可预测。
- SequenceNumber:用于指定ITSList中包含的每个 ITS 所使用的同步点的标识
- 如果GITS_TYPER.VMOVP == 0,那么SequenceNumber指定ITSList中包含的所有ITSs所使用的同步点的标识
- 如果GITS_TYPER.VMOVP == 1,那么SequenceNumber全都保留为0
- ITSList:用于指定包含在同步操作中的ITS实例:
- ITSList中bit n代表ITS n
- 如果相应的bit位为1,那么对应的ITS就包含在内
VMOVP vPEID, RDbase, SequenceNumber, ITSList
如果多个 ITS 控制同一 vPE 的中断,则必须在不同 ITS 之间协调移动该 vPE。这可以通过软件进行控制,使用本文详述的两种方法之一:
- 当GITS_TYPER.VMOVP == 0时:
- 必须为每个控制移动 vPE 中断的 ITS 发布 VMOVP 命令。每条命令都必须有一个共同的SequenceNumber。在所有 ITS 处理完之前使用该SequenceNumber的所有命令之前,该SequenceNumber不能用于其他 VMOVP 命令。
- 为每个 ITS 发出的 VMOVP 命令包含受移动 vPE 影响的所有 ITS 的列表。这就是 ITS 列表。
- 每个 ITS 收到的序列号必须与其他 ITS 收到的序列号顺序一致。
- 当GITS_TYPER.VMOVP == 1时:
- VMOVP 命令必须只在控制被移动 vPE 中断的一个 ITS 上发出。
- 具体的设计实现负责传播更新的映射。
10.3.7 VMOVI命令
该命令更新 由 DeviceID 和 EventID 对应的ITT表项中的 vPEID 字段。它还将由 EventID 和 DeviceID 定义的事件转换为 vPEID 和 pINTID,并指示相应的GICR将中断的挂起状态移至由新 vPEID 对应的GICR,同时更新与事件相关的 ITE 以使用新 vPEID。
VMOVI DeviceID, EventID, vPEID,[Dbell_pINTID]
10.3.8 ITS命令错误
每个ITS检测各种命令错误和翻译错误,并在GICD中Armv8.2 ras的兼容错误记录中报告它们。 ITS记录错误综合征包括四组,每组在GITS_FCTLR寄存器中都有单独的启用项。下表显示ITS记录错误综合征组。
group | control |
---|---|
ACE5-Lite subordinate interface write translation errors. Only when the ITS has a separate ACE5- Lite subordinate port. | GITS_FCTLR.AEE (access error enabel) |
Translation errors on incoming writes to GITS_TRANSLATER | GITS_FCTLR.UEE (unmapped error enable) |
Errors during commands | GITS_FCTLR.CEE (command error enable) |
Other errors such as memory system, or memory allocation errors | None |
11. LPIs
特定位置的外设中断(LPI locality-specific Peripheral interrupts)总是基于消息的,可以来自外设,也可以来自PCIe根复合体。 一个LPI只针对一个核。LPI是在外设写入ITS时生成的。ITS包含寄存器来控制LPI的生成和维护。ITS提供INTID转换,如果外设也存在SMMU,则允许这些外设由虚拟机直接拥有。
如果使用GICv3,ITS允许将中断转换为hypervisor的ID空间,而不是直接转换给虚拟机。如果使用GIC体系结构版本4.1,则hypervisor可以将ITS配置为直接发送中断。
11.1 LPI直接注入
当LPI被映射到一个vPE并且ITS使用一个VMAPI或者VMAPTI命令时,ITS可以直接注入一个LPI给vPE。
11.2 LPI multichip操作
GIC700不使用物理目标地址,所以GITS_TYPER.PTA == 0。因此,GIC-700使用GICR_TYPER.ProcessorNumber的值将所有LPI和命令路由到它们的目标。
GIC700拆分GITS_TYPER.ProcessorNumber值分为两个字段,Chip_ID和填充的线性片上核心编号。填充的片上核心数字段的宽度由max_pe_on_chip配置参数定义。该参数设置单个芯片上的最大内核或线程数通过GICD_CFGID.PEW可以发现线性片上核数域的宽度。例如,max_pe_on_chip = 17,则片上核数字段的下半部分的宽度为ceil[log2(17)] = 5位。因此,芯片1上第一个核的ProcessorNumber值为0x20,芯片1上第二个核的值为0x21,芯片2上第一个核的值为0x40。 下图显示了带有典型值的ProcessorNumber字段。

如果软件试图访问不存在的芯片、offline或访问不存在的核,请求将被丢弃并通过ITS命令和翻译错误记录报告。
11.3 LPI caching
如果支持LPI,则GIC700支持每个芯片的单个LPI缓存,最多可达4个banks。
LPI缓存是基于LPI INTID的最低位的双向集合关联,并存储LPI属性表中的LPI属性。当每个LPI到达系统时,将检查相关集合的有效属性。如果选择了多个banks,那么GIC使用核心vPE号码的较低位来选择bank。
缓存对于挂起的LPI是完全关联的,这意味着在将任何内容发送到Pending表之前,LPI系统将填充几乎所有的cache line。GIC700没有针对具有相同INTID的lpi进行优化。然而,随着时间的推移系统对缓存进行重新排序。在某些情况下,这种行为可能导致无法有效地整理重复的中断。但是,减少Pending表的使用,可以在负载下获得更好的延迟边界。
这种缓存方法意味着优先级与传入的LPI相关联,并一直保持到它被服务为止。GIC不接受LPI属性表中的更改,直到相关的INV和SYNC命令通过ITS、GICR_INVLPIR或GICR_INVALLR执行。
最多可以同时运行16个INV命令。一旦INV的执行受到影响,并且任何匹配的中断已经从目标PE中被召回,GIC设置GICR_SYNCR.BUSY==0
在GIC发现中断在缓存中不存在之后,命令槽变得空闲,这可能需要根据负载和缓存内容进行线性搜索。如果缓存溢出,那么可能还需要检查Pending表中无效的ID。
GIC700在选择要保留在缓存中的数据时考虑优先级和启用。挂起的中断总是优先于未挂起的中断,因此不能保证最高优先级的中断数据始终存储在缓存中。
有关软件如何使用利用率引擎来优化LPI缓存内容的信息,请参阅GICD_UTILR寄存器。
12. 内存访问和属性
LPI和ITS的翻译和属性位于内存表中,这些内存表的位置在寄存器中定义,寄存器指定了它们的基址、大小和访问属性。ARM建议将所有表放在普通内存中。所有ITS表都是私有的,在分配之后,只有GIC访问它们。但是,LPI属性表和ITS命令队列由内核写入,由GIC读取。 下表显示了用于GIC生成的内存事务的a
访问类型 | 寄存器 | 映射控制位 |
---|---|---|
LPI property table | GICR_PROPBASER | GICD_FCTLR2.DCC |
LPI pending table | GICR_PENDBASER | GICD_FCTLR2.DCC |
LPI virtual property table | ITS VMAPP | GICD_FCTLR2.DCC |
LPI virtual pending table | ITS VMAPP | GICD_FCTLR2.DCC |
vPE configuration table | GICR_VPROBASER and GITS_BASER2 | GICD_FCTLR2.DCC |
ITS device table | GITS_BASER0 | GITS_FCTLR.DCC |
ITS translation table | GITS_BASER0 | GITS_FCTLR.DCC |
ITS collection table | GITS_BASER1 | GITS_FCTLR.DCC |
ITS command queue | GITS_CBASER | GITS_FCTLR.DCC |
13. 电源管理
GIC700可以通过系统电源控制器下电。GIC还支持电源控制器为GIC服务的核下电。GICR_WAKER和GICR_PWRR寄存器为与电源管理相关的函数提供控制位。
13.1 Redistributor电源管理
在复位时,GICR默认是下电的。为了让GICR上电,软件需要使用GICR_PWRR寄存器。
GICR_PWRR可以被核或者GICR操作,用于控制GICR的电源管理。
如果是核进行操作,每一个核必须将它对应的GICR_PWRR.RDPD=0 并且 GICR_PWRR.RDAG = 0,用于确保GICR上电。除此之外,单个核也可以帮连接在一个GICR上的所有核来上电GICR。它需要写GICR_PWRR.RDPD=0 并且 GICR_PWRR.RDAG = 1。
// 下电操作序列
power off:
//check group not transitioning
repeat
until(GICR_PWRR.RDGPD == GICR_PWRR.RDGPO)
// write to power the CPU off
GICR_PWRR.RDPD = 1
// 上电操作序列
power on:
repeat
// check group not transitioning
repeat
until(GICR_PWRR.RDGPD == GICR_PWRR.RDGPO)
// write to power the CPU on
GICR_PWRR.RDPD = 0
//check access, if RDPD == 0 then powered on
until(GICR_PWRR.RDPD == 0)
13.2 处理器核电源管理
GIC架构定义了一个编程序列用于安全的将连接到GIC700的核进行下电。
这个下电流程会用到GICR_WAKER.ProcessorSleep控制位。当cluster中所有的核都使用架构定义的流程进行下电操作之后,可以对cluster的GIC stream接口进行power gate。
在核下电之前,必须设置GICR_WAKER.ProcessorSleep位为1。核必须轮询GICR_WAKER.ChildrenAsleep位,确保在核的GIC stream接口上没有未完成的传输。
为了确保在核下电的过程中没有中断,通常下电的流程如下:
- 在核内启用中断屏蔽
- 禁用CPU interface
- 禁用CPU interface的中断旁路bypass功能
当一个核被下电并且GICR_WAKER.ProcessorSleep位为1时,如果GIC700接收到一个中断,并且该中断的目标就是当前被下电的核,那么Wake request模块会断言对应核的wake_request信号。wake_request信号必须连接到系统电源控制器。
正常情况下不能直接设置GICR_WAKER.ProcessorSleep位为1,除非核进入的电源状态无法使用GIC stream接口来唤醒,只能让GIC700通过电源控制器来唤醒。
中断可以让执行WFI或者WFE指令进入低功耗状态的核离开低功耗状态。
13.3 P-channel和电源控制
可以使用P-channel将chip从系统中隔离出来
p-channle有如下几个状态:
- RUN(pstate信号==0x0):正常功能模式
- CONFIG(pstate信号==0x9):GIC不会发送任何跨chip消息。它会接收发送来的消息但是不会处理它们
- OFF(pstate信号==0xf):GIC不会发送任何跨chip消息,并且不会接收任何发送来的消息。icrdtready信号被拉低,用于阻止任何对GIC的访问
在CONFIG和OFF状态下,通常发送到另一个芯片的寄存器访问会在本地进行。因此,路由表寄存器读取本地版本,而不是路由表所有者的副本。对于远程拥有的spi也是如此。因此,在这两种P-Channel状态下保存和恢复GICD寄存器值是安全的。 通过设置pstate信号的初始值,GIC可以在RUN或OFF状态下退出复位。如果已经保存了寄存器值并打算恢复它们,那么在尝试恢复任何SPI寄存器之前,必须使用OFF状态并首先恢复路由表。
13.4 SPI RAM保存
如果GIC经常掉电和复位,那么在spi很多的情况下,保存和恢复状态会非常耗时。在reset退出时,spi_ram_retained信号使GIC能够信任ram中包含的SPI编程状态。
u_spi_ram0和u_spi_ram1会保存以下寄存器的状态:
- GICD_IGROUPRn(E)
- GICD_ISENABLERn(E)
- GICD_ISPENDRn(E)
- GICD_ISACTIVERn(E)
- GICD_IPRIORITYRn(E)
- GICD_ICFGRn(E)
- GICD_IGRPMODRn(E)
- GICD_NASCRn(E)
- GICD_IROUTERn(E)
- GICD_IERR
GIC700不会驱动spi_ram_retained信号。因此在GIC进入QSTOPPED状态之后,必须有其它逻辑驱动RAM retention信号
进入SPI RAM保存步骤:
- 确保GIC处于IDLE状态:
- 所有的PEs完成GICR_WAKER.ProcessorSleep握手
- 给所有的中断分组清除GICD_CTLR中的使能位,并且轮询直到GICD_CTLR.RWP == 0
- 将所有的SPI Collators设置为Q-STOPPED状态。这一步确保没有更多的中断从SPI Collators发送出来。SPI Collators拒绝任何不在空闲状态的中断。根据系统配置,系统可能需要设置一些外部中断屏蔽。
- 通过GICD的Q-channel接口将GIC设置进入Q_STOPPED状态
- 系统驱动必要的信号到SPI ram,将它们放入retention。系统可以使用RAM I/O边带信号将保留信号连接到RAM实例。
从SPI RAM保存中恢复的步骤:
- GIC上电
- 在退出复位前驱动spi_ram_retained信号为高。只有在复位时才能改变spi_ram_retained信号状态
- GICD退出复位
- 像正常一样重新编程GIC,除了所有与spi相关的寄存器都保留其值。软件必须对所有GICR和通用的非缩放寄存器(如GICD_CTLR)进行编程。
- 通过GICD的Q-channel接口将GIC设置进入Q-RUN状态
- 将SPI Collator设置为Q-RUN状态
- 给所有的中断分组设置GICD_CTLR中的使能位,并且轮询直到GICD_CTLR.RWP == 0
- 唤醒所有的PEs
14. 性能检测单元(PMU)
GIC700包含了一个PMU用于计数主要的GIC事件,主要包括GICD的事件和对ITS block的任何配置。PMU不会追踪GCI的事件。软件可以通过记录核中断服务的调用的次数来计数PPI和SGI中断。
PMU有5个计数器,拥有快照功能和溢出中断。
安全和非安全中断会被一起计数。因此非安全的软件默认情况下不能访问GICP(PMU)的寄存器空间,安全的软件可以访问。想要让非安全的软件可以访问GICP(PMU)寄存器空间可以有以下两种方式:
- 将GICD_SAC.GICPNS位置1
- 将gicp_allow_ns信号拉高
软件可以通过设置GICP_INTENSET0的相关比特位来使能和禁用每个计数器的溢出中断。
当溢出中断使能时,在以下情况下会产生溢出中断:
- 对任意计数器写GICP_OVSSET0寄存器
- 任意一个使能的计数器发生了溢出
GICP_OVSSET0寄存器和GICP_OVSCLR0寄存器可以用于保存和恢复操作以及测试pmu中断线是否正确连接。
pmu_int信号可以用于触发外部逻辑,例如,触发捕获数据的读取。 或者,通过将有效的SPI ID编程到GICP_IRQCR.SPIID字段,pmu_int信号SPI按照正常的SPI编程在内部交付。
每个PMU计数器GICP_EVCNTRn都有相应的GICP_SVRn快照寄存器。 在快照事件中,所有五个计数器都被复制到它们的备份寄存器中,以便在较长的时间内复制所有一致的数据。
快照事件是:
- 在4阶段sample_req / sample_ack信号外部握手上的握手
- 对GICP_CAPR.CAPTURE位写入1
- 当GICP_EVTYPERn.OVFCAP置位时,被启用的计数器的溢出。
只有一组快照寄存器,因此数据在多个捕获事件中被替换。