Arm Generic Interrupt Controller v3 and v4(GICv3v4)学习(二)

该博客聚焦于Arm Generic Interrupt Controller v3和v4中LPI的学习。介绍了LPI的背景、配置与管理,涉及Redistributors和ITS的相关内容,包括初始化步骤、表的大小与布局、命令队列操作等,还给出示例及知识问答,帮助理解LPI特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

提示

该博客主要为个人学习,通过阅读官网手册整理而来(个人觉得阅读官网的英文文档非常有助于理解各个IP特性)。若有不对之处请参考参考文档,以官网参考文档为准。
Arm Generic Interrupt Controller v3 and v4学习一共分为三章,这是第二章

1. Introduction

🔔背景:Interrupt Translation Service (ITS) 是一种硬件和软件结合的机制,用于处理中断信号在虚拟化环境中的传递和转换。ITS旨在解决虚拟化环境中的中断共享和管理问题。

中断是发送给处理器的一个信号,表明已经发生了需要处理的事件。中断通常是由外围设备产生的。LPIs通常用于产生消息信号中断(Message-Signaled Interrupts, MSIs)的外设。
在这里插入图片描述

LPI的配置和管理与其他中断类型不同,因为它们的状态保存在内存中,而不是保存在寄存器中。LPI是具有消息信号的中断(MSI),并由中断转换服务(Interrupt Translation Service ,ITS)提供转换。

2. LPIs

LPI的配置与其他中断类型非常不同,涉及以下内容:

  • Redistributors
  • ITSs (Interrupt Translation Service)

LPI始终是基于消息的中断,它们可以由ITS提供支持。ITS负责接收来自外设的中断,并将其作为LPI转发给适当的Redistributor。一个系统可能包括多个ITS,在这种情况下,每个ITS都必须单独配置。

对LPI的支持是可选的,并由GICD_TYPER.LPIS控制。如果至少存在一个ITS,外设也可以绕过ITS,直接发送LPI到Redistributor。

3. Redistributors

Redistributors使用内存中保存的表来获取LPI配置信息和每个物理LPI的状态。LPI的配置信息存储在LPI配置表中,由GICR_PROPBASER指向。LPI配置是全局的,也就是说,所有的Redistributors必须看到相同的配置。通常,系统有一个由所有Redistributors共享的LPI配置表。
LPI的状态信息也存储在内存表中。这些是由GICR_PENDBASER指向的LPI Pending表。每个Redistributor都有自己的LPI Pending表,并且这些表不会在Redistributors之间共享。
下图显示了三个Redistributors和相关的LPI表:
在这里插入图片描述

3.1 Initial configuration of a Redistributor

初始化系统中的Redistributors的步骤如下:

  1. 为LPI配置表(Configuration table)分配内存,并使用每个LPI的适当配置初始化该表。
  2. 在每个Redistributor中设置GICR_PROPBASER,以指向LPI配置表。
  3. 为每个Redistributor的LPI Pending表分配内存,并初始化每个表的内容。在系统启动时,这通常意味着将内存归零,这意味着所有的LPI接口都处于非活动状态(inactive state)。
  4. 在每个Redistributor设置GICR_PENDBASER,以指向与该Redistributor关联的LPI Pending表。
  5. 使能每个Redistributor中的GICR_CTLR.EnableLPIs,以启用LPIs。

3.1.1 LPI Configuration table

LPI配置表为每个LPI INTID设置了一个字节。下图显示了每个字节的格式:
在这里插入图片描述

尽管SPI、PPI和SGI的优先级可以设置8位,但表中只有6位用于记录LPI的优先级。LPI优先级的较低两位总是被视为0b00
这里没有用于记录Group配置的字段。LPI总是被视为Non-secure Group 1中断。
LPI配置表的大小取决于LPI的数量。GIC支持的INTIDs(SPIs、PPIs、SGIs和LPI)的最大数量由GICD_TYPER.IDbits表示。LPI配置表只处理LPI,其中使用的属性大于8191。因此,为了支持所有可能的LPI,LPI配置表的大小计算如下:

🔔Size in bytes = 2GICD_TYPER.IDbits+1 – 8192

然而,可以支持更小范围的INTIDs。GICR_PROPBASER还包括一个IDbits字段,它表示LPI配置表所支持的INTIDs的数量。这个数字必须等于或小于GICD_TYPER.IDbits中的值。软件必须为这个数量的入口分配足够的内存。在这种情况下,LPI配置表的要求大小变为:

🔔Size in bytes = 2GICR_PROPBASER.IDbits+1 – 8192

中断控制器必须能够读取为LPI配置表分配的内存。然而,它从不写入这个内存。

3.1.2 LPI Pending tables

LPIs的状态信息存储在内存中。LPIs有两种状态:非活动状态(inactive)或挂起状态(pending)。
在它们被响应时,中断从pending切换到inactive。
在这里插入图片描述

因为只有两个状态,所以在LPI Pending表中,每个LPI只有1位。因此,为了在实现中支持所有可能的INTID,LPI Pending表的大小必须为:

🔔Size in bytes = (2GICD_TYPER.IDbits+1) / 8

中断控制器必须能够从为LPI Pending表分配的内存中读取和写入。通常,当缓存中有太多的Pending中断或进入低功耗状态时,Redistributor在内部缓存最高优先级的Pending中断,并将状态信息写入LPI Pending表。
虽然在拥有的Redistributor中使能了LPIs,但软件永远不能直接访问LPI Pending表。

3.2 Reconfiguring LPIs

LPI配置信息存储在内存中的表中,而不是存储在寄存器中。出于性能原因,Redistributor缓存了LPI配置信息。这意味着要重新配置LPI,软件必须:

  1. 更新LPI配置表入口
  2. 确保更新的全局可见性
  3. 无效Redistributor中配置的任何缓存

通过发出ITS INV或INVALL命令,使Redistributor中的缓存失效。INV命令会使特定中断入口无效,因此,在重新配置少量LPIs时,通常会使用此命令。INVALL命令会使指定集合中的所有中断入口无效。
有关ITS命令的更多信息,请参见 Adding a new command to the command queue。
如果没有实现ITS,软件必须写入GICR_INVLPIR或GICR_INVALLR寄存器,以使Redistributor重新加载中断配置。

4. ITS

外设通过在ITS中向GITS_TRANSLATER写入消息来生成LPI。这为ITS提供了以下信息:

  • EventID
    EventID写入到GITS_TRANSLATER中。EventID标识外围设备正在发送的哪个中断。EventID可能与INTID相同,也可能被ITS转换为INTID。
  • DeviceID
    设备ID标识外围设。生成DeviceID的机制是由定义实现(IMPLEMENTATION DEFINED)。例如:AXI User signals可能会被用到

ITS handles将消息转换为可以传递到已连接的core的INTID。
物理LPI在集合中。一个集合中的所有LPI都被路由到同一Redistributor。软件将LPI分配给集合,允许它有效地将中断从一个处理元素(PE)移动到另一个处理元素(PE)。

ITS使用三种类型的表来处理LPIs的转换和路由。它们包括:

  • Device table
    每个ITS都有一个设备表(Device table)。设备表将device id映射到中断转换表(Interrupt Translation Tables)。
  • Interrupt Translation Tables
    每个DeviceID或外设都有一个中断转换表(ITT)。ITT包含EventID和INTID之间的外设特定的映射。它们还包含了INTID是其成员的集合。
  • Collection table
    每个ITS有一个集合表。收集表将集合映射到重新分配器。

当一个外设向GITS_TRANSLATER写入消息时,ITS会执行以下操作:

  1. 使用DeviceID从Device table中选则合适的入口。此入口标识要使用的ITT
  2. 使用EventID从ITT中选则合适的入口,该入口提供了INTID和集合ID(Collection ID)
  3. 使用集合ID在集合表(Collection Table)中选择所需的入口,然后返回路由信息
  4. 将中断转发给目标Redistributor。(该步骤省略了distributor,也就是说路由的动作是由distributor来做的)

在这里插入图片描述

一个ITS可以选择支持许多硬件集合(hardware collections)。硬件集合是ITS在内部保存配置的地方,而不是将其存储在内存中。GITS_TYPER.HCC将报告支持的硬件集合的数量。您仍然可以认为这是集合表的一部分(Collection Table),它只是不存储在内存中。

4.1 The command queue

ITS将使用内存中的命令队列进行控制。命令队列是一个循环缓冲区,它由三个寄存器定义。

  • GITS_CBASER
    此寄存器指定消息队列的基地址和大小。命令队列必须是64KB对齐的,并且大小必须是4KB的倍数。消息队列中的每个入口都是32bytes。GITS_CBASER还指定了ITS在访问消息队列时所使用的可缓存性和可共享性设置。
  • GITS_CREADR
    此寄存器指向ITS将处理的下一个命令。
  • GITS_CWRITER
    此寄存器指向队列中下一个应该写入新命令的入口
    在这里插入图片描述

GIC3.0和4.0提供了ITS支持的所有命令以及如何编码这些命令的详细信息。

4.2 Initial configuration of an ITS

要在系统启动时配置ITS,软件必须:

  1. 为设备表和集合表分配内存
    GITS_BASER[0…7]寄存器指定ITS设备和集合表的基地址和大小。软件使用这些寄存器来发现ITS所支持的表的数量和类型。然后,软件必须分配所需的内存,并将GITS_BASERn寄存器设置为指向这个已分配的内存。
  2. 为命令队列分配内存
    软件必须为命令队列分配内存,并将GITS_CBASER和GITS_CWRITER设置为指向此所分配内存的起点。
  3. 使能ITS
    在分配了表和命令队列后,就可以启用ITS。这是通过设置GITS_CTLR.Enable = 1。
    一旦GITS_CTLR.Enable 被设置,GITS_BASERn和GITS_CBASER寄存器变为只读。

4.3 The sizes and layout of Collection and Device tables

集合表和设备表的位置和大小将使用GITS_BASERn寄存器进行配置。软件在启用ITS之前,必须为这些表分配内存,并配置GITS_BASERn寄存器。
软件可以分配一个单级表或二级表。这是由GITS_BASERn.Indirect指定的。

对两级表的支持是可选的。如果ITS只支持单级表,则支持GITS_BASERn.Indirect的是RAZ/WI。

  • 单级表(Flat/Single level tables)
    对于单级表,将单个连续的内存块分配给ITS来记录映射。在启用ITS之前,软件需要用0填充内存。然后,在处理命令队列中的命令时,由ITS填充该表。
    下图显示了一个单级表。该表是一个连续的内存块,一个GITS_BASERn寄存器指向表的基地址:
    在这里插入图片描述

表的大小可根据DeviceID或CollectionID的宽度进行调整。所需大小可计算如下:
📌Size in bytes = 2ID_width * entry_size

其中entry_size是每个表入口的字节数,由GITS_BASERn.Entry_Size报告。
在配置GITS_BASERn寄存器时,表的大小被指定为页。页的大小由GITS_BASERn.Page_Size控制。可以是4KB、16KB或64KB。因此,上面给出的公式的结果必须四舍五入到下一个整页的大小
例如,如果系统实现了8bit的DeviceID,每个表的字节为8bytes,一个4K大小的页将被使用:

📌28 * 8 = 2048 bytes = 4K (rounded up to the next full page)

  • 二级表(Two-level tables)
    对于二级表,软件分别分配一个第一级表和几个二级表,如下图所示:
    在这里插入图片描述

第一级表由软件填充,每个入口要么指向第二级表,要么被标记为无效。第二级表必须用0来填充,然后再将它们分配给ITS,并在处理从命令队列中获得的命令时由ITS填充。
当使能ITS时(GITS_CTLR.Enabled== 1)软件可能会分配额外的二级表,并更新相应的第一级表条目指向这些附加表。在使能ITS时,软件不能删除分配,或更改现有的分配。
每个二级表的大小是一页。与平面表(flat tables)一样,页面大小是由GITS_BASERn.Page_Size配置的。因此,它包含(page_size / entry_size)入口。
每第一级表入口有(page_size/entry_size)个id,可以指向二级表,也可以标记为无效。任何使用与无效条目对应的ID的ITS命令都将被丢弃。
一级表的所需大小可以通过计算:

📌Size in bytes = (2ID_width / (page_size / entry_size)) * 8

与单级表一样,第一级表的大小被指定为页数。因此,该公式的结果必须四舍五入到下一个整页的大小。

4.4 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会按顺序从命令队列中读取这些命令。但是,这些命令对Redistributors的影响可能是无序可见的。SYNC命令可以确保以前发布的命令的效果可见。
    当GITS_CWRITER指向GITS_CREADR之前的位置时,命令队列已满。在尝试添加新命令之前,软件必须检查队列中是否有足够的空间。

4.5 Mapping an interrupt to a Redistributor

每个可以向ITS发送中断的外设都有自己的DeviceID。每个DeviceID都需要它自己的中断转换表(ITT)来保存其EventID到INTID的映射。软件必须为ITT分配内存,然后使用MAPD命令将DeviceID映射到ITT,如下命令所示:

📌MAPD <DeviceID>, <ITT_Address>, <Size>

当外设的DeviceID已映射到ITT时,必须将它可以发送的不同EventID映射到INTID和集合(collections)。每个集合都被映射到一个Redistributor。
使用MAPTIMAPI命令,可以将这些命令映射到集合。

  • 当EventID和INTID相同时,使用MAPI命令,如下所示:
📌MAPI <DeviceID>, <EventID>, <Collection ID>
  • 当EventID和INTID不同时,使用MAPTI命令,如下所示:
📌MAPTI <DeviceID>, <EventID>, <INTID>, <Collection ID>

目标Redistributor的识别取决于GITS_TYPER.PTA:

  • GITS_TYPER.PTA == 0
    Redistributor由其ID指定,可以从GICR_TYPER.Processor_Number中读取。
  • GITS_TYPER.PTA == 1
    Redistributor由其物理地址指定。
    例如:
    一个计时器有DeviceID 5,并使用一个2位的EventID。我们希望将“EventID 0”映射到INTID 8725。为计时器分配的ITT在地址0x84500000处。
    我们决定使用collection number 3,并将中断发送给ID 7的Redistributor。
    其命令顺序如下:
MAPD  5, 0x84500000, 2 // Map DeviceID 5 to an ITT 
MAPTI 5, 0, 8725, 3 // Map EventID 0 to INTID 8725 and collection 3
MAPC  3, 7 // Map collection 3 to Redistributor 7
SYNC  7该示例假设之前没有设置任何映射,并且是GITS_TYPER.PTA==0

4.6 Migrating(迁移) interrupts between Redistributors

我们可以使用几种不同的技术将中断从一个Redistributor移动到另一个Redistributor:

  • Remap a collection
    软件可以通过重新映射整个集合,将所有中断从一个Redistributor移动到另一个Redistributor。这通常在连接到Redistributor的PE断电时完成,并且中断必须移动到另一个Redistributor。可以使用以下命令序列完成:
MAPC   <Collection ID>, <RDADDR2> // Remap collection to new Redistributor
SYNC   <RDADDR2> // Ensure visibility of the mapping
MOVALL <RDADDR1>, <RDADDR2> // Move pending state to new Redistributor
SYNC   <RDADDR1> // Ensure visibility of move

在这个命令序列中,RDADDR1是以前的目标Redistributor,而RDADDR2是新的目标Redistributor。
如果有多个针对RDADDR1的集合,那么我们将需要多个MAPC命令,每个集合对应一个。此序列假设所有集合都被重新映射到相同的新目标Redistributor。

  • 将一个中断映射到另一个集合。单个中断可以重新映射到不同的集合。可以使用以下命令序列完成
MOVI <DeviceID>, <EventID>, <ID of new Collection>
SYNC <RDADDR1>

在这个命令序列中,RDADDR1是在重新映射中断之前,中断最初分配到的集合的目标Redistributor。

4.7 Removing interrupt mappings

要重新映射或删除中断的映射,软件必须执行以下操作:

  1. 失能中断当前映射到的物理INTID。这将在LPI配置表中完成。有关详细信息,请参见3.2节 Reconfiguring LPIs。
  2. 发出DISCARD 命令。这将删除中断的映射,并清除已映射的INTID的挂起状态。
  3. 发出一个SYNC命令,并等待到该命令完成为止。

命令完成后,不再将中断发送到之前映射到的再分配器。

4.8 Remapping or removing the mapping of devices

要更改或删除设备的映射,软件必须执行以下操作:

  1. 当前映射外设的每个EventID按照4.7节Removing interrupt mappings的步骤。
  2. 发出MAPD命令以重新映射该设备。或者,将有效位清除为0的MAPD命令将删除该映射
  3. 发出一个SYNC命令,并等待到该命令完成为止。

5. Example

请点这里AArch64_GIC_v3_v4_example下载示例。该示例演示了初始化GIC、使用ITS配置LPI以及处理生成的LPI中断。
该示例包含以下文件:

  • gicv3_basic.c:包含与GIC交互的函数。
  • gicv3_lpis.c:包含与LPIs特别相关的函数。
  • main_lpi.c:一个使用LPIs的简短测试程序。
    在本例中,我们查看文件main_lpi.c如下:
int main(void)
{
     uint32_t type, entry_size;
     uint32_t rd, target_rd;
     //
     // Configure the interrupt controller
     //
     rd = initGIC();

函数initGIC()执行GIC的基本初始化。有关更多信息,请参见:Arm Generic Interrupt Controller v3 and v4(GICv3v4)学习(一)
以下代码为LPI配置和挂起表分配内存:

//
// Set up Redistributor structures used for LPIs
//
setLPIConfigTableAddr(rd, CONFIG_TABLE, GICV3_LPI_DEVICE_nGnRnE /*Attributes*/, 15 /* Number of ID bits */);
setLPIPendingTableAddr(rd, PENDING_TABLE, GICV3_LPI_DEVICE_nGnRnE /*Attributes*/, 15 /* Number of ID bits */);
enableLPIs(rd);

配置表由多个Redistributors共享,但每个Redistributors都有一个挂起表(Pending Table)。本示例使用单个核心,因此它只分配单个LPI挂起表。
接下来,该代码将配置ITS:

 // Allocate memory for the ITS command queue
 initITSCommandQueue(CMD_QUEUE, GICV3_ITS_CQUEUE_VALID /*Attributes*/, 
                        1 /*num_pages*/);
// Allocate Device table
 setITSTableAddr(0 /*index*/,
                 DEVICE_TABLE /* addr */,
                 (GICV3_ITS_TABLE_PAGE_VALID | GICV3_ITS_TABLE_PAGE_DIRECT |
                 GICV3_ITS_TABLE_PAGE_DEVICE_nGnRnE),
                 GICV3_ITS_TABLE_PAGE_SIZE_4K,
                 16 /*num_pages*/);
 //Allocate Collection table
 setITSTableAddr(1 /*index*/,
                 COLLECTION_TABLE /* addr */,
                 (GICV3_ITS_TABLE_PAGE_VALID | GICV3_ITS_TABLE_PAGE_DIRECT |
                 GICV3_ITS_TABLE_PAGE_DEVICE_nGnRnE),
                 GICV3_ITS_TABLE_PAGE_SIZE_4K,
                 16 /*num_pages*/);
 // Enable the ITS

enableITS();这段代码为ITS的Command Queue, Device table和 Collection table。当表被初始化后,ITS将被使能。
使能ITS后,可以通过向命令队列中添加命令来映射中断,如下代码所示:

// Set up a mapping
 itsMAPD(0 /*DeviceID*/, ITT /*addr of ITT*/, 2 /*bit width of ID*/); 
 itsMAPTI(0 /*DeviceID*/, 0 /*EventID*/, 8193 /*intid*/, 0 /*collection*/); 
 itsMAPC(target_rd /* target Redistributor*/, 0 /*collection*/); 
 itsSYNC(target_rd /* target Redistributor*/);

此示例将执行以下操作:

  • 将DeviceID 0映射到中断转换表(Interrupt Translation Table, ITT)
  • 将EventID 0从DeviceID 0映射到INTID 8193,并将其分配到集合0(Collection 0)
  • 将集合0映射到当前的Redistributor(0.0.0.0)

此时,我们已经为GIC的DeviceID/EventID启用了其映射。接下来,我们对INTID 8193的配置如下:

 configureLPI(rd, 8193 /*INTID*/, GICV3_LPI_ENABLE, 0 /*Priority*/);
 printf("main(): Sending LPI 8193\n");
 itsINV(0 /*DeviceID*/, 0 /*EventID*/);
 itsINT(0 /*DeviceID*/, 0 /*EventID*/);

此代码启用INTID 8193并设置其优先级。GIC可能已经缓存了旧的配置,因此需要使用INV命令来确保使用新的配置。
FVP基础平台模型不包括能够生成此MSI的外设,因此我们使用INT命令来生成它。

6. Check your konwledge

  • LPI的状态机与其他中断类型有何不同?
    💡其他中断类型有四种状态:Inactive, Pending, Active和Active and Pending。
    LPIs只有两种状态:Inactive和Pending。
  • LPI的配置和状态存储在哪里?
    🔔在内存中。Redistributors共享一个存储该配置的公共LPI配置表。每个Redistributor有一个LPI挂起表(Pending table),它存储pending状态。
  • MSI包含哪些允许ITS转换它信息?
    📌一个DeviceID和一个EventID。DeviceID标识哪个外设发送中断。EventID标识外设正在发送的哪个中断。
  • 软件如何在ITS中创建映射?
    👉通过使用ITS命令队列发出命令。一个MAPD命令将为该设备创建一个映射。MAPI和MAPTI命令映射外设的EventID。
  • 软件如何改变LPI的优先级?
    👋该软件必须执行以下操作:
    1. 更新LPI配置表(Configuration Table)中的相应入口
    2. 确保改变的全局可见性
    3. 通过使用ITS发出无效操作或写入其中一个无效寄存器,来使GIC cache中的任何配置失效

参考文档

Locality-Specific Peripheral Interrupts Arm Generic Interrupt Controller v3 and v4

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CinzWS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值