文章目录
Introduction
本指南介绍了GICv3和GICv4体系结构中对虚拟化的支持。它涵盖了hypervisor可用于生成和管理虚拟中断的控件。本指南适用于需要了解中断控制器功能或需要编写软件来管理虚拟中断。
本指南介绍了GICv3.x和GICv4.1中的功能。它不包括GICv4.0,只是作为一个介绍。
在本指南的最后,您将能够:
- 列出生成虚拟中断的不同方式。
- 命名软件用于管理CPU interface内的GIC虚拟化的寄存器。
- 描述GICv4.1如何允许直接注入虚拟中断
Virtualization
Armv8-A包括对虚拟化的可选支持。为了补充这一功能,GICv3还支持虚拟化。对GICv3中虚拟化的支持增加了以下功能:
- CPU interface寄存器的硬件虚拟化。
- 生成虚拟中断并发出信号的能力。
- 维护中断,通知监控软件(如hypervisor)虚拟机内的特定事件。
Terminology
Hypervisors创建、控制和调度虚拟机(VM)。虚拟机在功能上等同于物理系统,并包含一个或多个虚拟处理器。这些虚拟处理器中的每一个都包含一个或多个虚拟PE(vPE)。
GICv3.x和GICv4.1中的虚拟化支持在vPE层级工作。例如,当创建虚拟中断时,它的目标是特定的vPE,而不是VM。通常,GIC不知道不同的vPE如何与虚拟机相关。在考虑稍后引入的一些控件时,记住这一点很重要。
本指南使用术语hypervisor来表示在EL2上运行的负责管理vPE的任何软件。在本指南中,我们忽略了虚拟化软件之间可能存在的差异,因为我们专注于GIC中的功能。但是,请记住,并不是所有的虚拟化解决方案都使用GIC中的所有可用功能。
给定的vPE可以被描述为scheduled或not-scheduled。scheduled的vPE是指被hypervisor已调度到物理PE(pPE)并正在运行的vPE。一个系统可能包含比pPE更多的vPE。未由hypervisor调度的vPE未运行,因此当前无法接收中断。
GICv3 - Virtualization
本节概述了GICv3中对虚拟化的支持。GICv3虚拟化与GICv2中首次引入的支持类似,主要在CPU interface内。它允许向pPE上当前调度的vPE发送虚拟中断信号。
Interfaces
CPU interface 寄存器分为三组:
- ICC:物理CPU interface 寄存器
- ICH:虚拟化控制寄存器
- ICV:虚拟CPU interface寄存器
下图显示了三组CPU interface 调节器:
Physical CPU interface registers
这些寄存器的名称格式为ICC _ * _ ELx。
在EL2执行的hypervisor使用常规的ICC_*_ELx寄存器来处理物理中断。
Virtualization control registers
这些寄存器的名称的格式为ICH _ * _EL2。
hypervisor可以访问额外的寄存器,以控制体系结构提供的虚拟化功能。这些功能如下:
- 启用和禁用虚拟CPU interface。
- 访问虚拟寄存器状态以启用上下文切换。
- 配置维护中断。
- 控制当前scheduled的vPE的虚拟中断。
这些寄存器控制访问它们的物理PE的虚拟化功能。不可能访问另一个PE的状态。也就是说,PE X上的软件无法访问PE Y的状态。
Virtual CPU interface registers
这些寄存器的名称的格式为 ICV _ * _EL1。
在虚拟化环境中执行的软件使用ICV _ * _EL1寄存器来处理虚拟中断。这些寄存器与相应的ICC _ * _ EL1寄存器具有相同的格式和功能。
ICV和ICC寄存器具有相同的指令编码。在EL2和EL3,ICC寄存器总是被访问。在EL1处,HCR_EL2中的路由位确定是访问ICC寄存器还是访问ICV寄存器。
ICV寄存器分为三组:
Group 0
用于处理Group 0中断的寄存器,例如ICC_IAR0_EL1和ICV_IAR0_EL1。当HCR_EL2.FMO设置为1时,在EL1处访问ICV寄存器而不是ICC寄存器。
Group 1
用于处理Group 1中断的寄存器,例如ICC_IAR1_EL1和ICV_IAR1_EL1。当HCR_EL2.IMO设置为1时,在EL1处访问ICV寄存器而不是ICC寄存器。
Common
用于处理Group 0和Group 1中断的寄存器,例如ICC_DIR_EL1和ICV_DIR_EL1。当HCR_EL2.IMO设置为1或HCR_EL2.FMO设置为1时,在EL1处访问ICV寄存器而不是ICC寄存器。
安全EL1中是否使用ICV寄存器取决于是否启用了安全虚拟化。稍后将对此进行详细介绍。
下图显示了同一指令如何基于HCR_EL2路由控制访问ICC或ICV寄存器的示例.
Managing virtual interrupts
hypervisor可以使用列表寄存器(List Registers) ICH_LRn_EL2为当前scheduled的vPE生成虚拟中断。每个寄存器代表一个虚拟中断,并记录以下信息:
vINTID (virtual INTID)
在虚拟环境中报告的INTID。
State
虚拟中断的状态(Pending、Active、Active and Pending或Inactive)。当虚拟环境中的软件与GIC交互时,状态机会自动更新。例如,hypervisor可能会创建一个新的虚拟中断,最初将状态设置为Pending。当vPE上的软件读取到ICV_IARn_EL1时,状态将更新为Active。
Group
在非安全状态下,虚拟环境的行为始终如同GICD_CTLR.DS为1。在安全状态下,虚拟环境的行为就像GICD_CTLR.DS为0,FIQ路由到EL1。因此,在这两种情况下,虚拟中断都可以是Group0或Group1。Group 0中断以vFIQ的形式传送。Group 1中断作为vIRQ进行传递。
pINTID (physical INTID)
虚拟中断可以选择性地标记有物理中断的INTID。当vINTID的状态机更新时,pITID的状态机也会更新。
列表寄存器(List Registers)不记录目标vPE。列表寄存器隐式地以当前scheduled的vPE为目标,软件有责任在更改scheduled vPE时上下文切换列表寄存器.
Example of a physical interrupt being forwarded to a vPE
下图显示了转发到vPE的物理中断的示例序列:
顺序如下:
- 物理中断从Redistributor转发到物理CPU interface。
- 物理CPU interface检查物理中断是否可以转发到PE。在这种情况下,检查通过,并断言物理异常。
- 中断被带到EL2。hypervisor读取IAR,IAR返回pINTID。pINTID.现在处于Active状态。hypervisor确定要将中断转发到当前正在运行的vPE。hypervisor将pINTID写入ICC_EOIR1_EL1。在ICC_CTLR_EL1.EOImode==1的情况下,这只会执行优先级下降,而不会deactivate物理中断。
- hypervisor写入其中一个列表寄存器(List registers),将虚拟中断注册为pending。List寄存器条目指定要发送的vINTID和原始pINTID.。然后,hypervisor执行异常返回,将执行返回给vPE。
- 虚拟CPU interface检查虚拟中断是否可以转发到vPE。除了使用ICV寄存器之外,这些检查与物理中断相同。在这种情况下,检查通过,并断言一个虚拟异常。
- 虚拟异常被带到EL1。当软件读取IAR时,将返回vINTID,并且虚拟中断现在处于Active状态。
- 来Guest OS处理中断。当它完成对中断的处理时,它会写入EOIR以执行优先级下降和deactivate。由于List寄存器记录了pINTID,这将deactivate vINTID和pNTID。
此示例显示了物理中断作为虚拟中断转发到vPE。例如,这可能来自hypervisor分配给虚拟机的外围设备。并非所有的虚拟中断都需要由物理中断引起。虚拟化软件可以随时在列表寄存器中创建虚拟中断。
Maintenance interrupts
如果虚拟CPU interface中的某些条件成立,则CPU interface可以被配置为生成物理中断。
INTID为25中断被报告为PPI。该中断通常被配置为Non-secure Group 1,并由EL2处的hypervisor处理。
维护中断的生成由ICH_HCR_EL2控制,并且当前断言的中断在ICH_MISR_EL2中报告。
Maintenance interrupt example
如果vPE清除虚拟CPU interface中的一个组启用位,则可以生成维护中断。因此,hypervisor可以删除属于禁用组的挂起虚拟中断的任何列表寄存器条目(List Register entries)。
Context switching
在vPE之间切换上下文时,hypervisor软件会保存一个vPE的状态,并加载另一个的上下文。虚拟CPU interface的状态构成vPE上下文的一部分。虚拟CPU interface状态由以下信息组成:
- ICV寄存器的状态。
- active virtual priorities。
- 任何pending、active或active and pending的虚拟中断。
可以使用ICH寄存器从EL2访问ICV寄存器的状态。例如,下图显示了ICH_VMCR_EL2中的字段如何映射到ICV寄存器状态。
切换vPE时,必须保存和恢复active virtual priorities。可以使用ICH_APxRn_EL2注册表访问当前vPE的活动优先级。
如管理虚拟中断中所述,使用列表寄存器(List registers)管理虚拟中断。这些寄存器的状态是特定于当前vPE的。因此,必须在上下文开关上保存和恢复这些寄存器。
GICv3.1 - Secure virtualization
Armv8.4-A引入了对安全状态下虚拟化的支持,如下图所示:
当PE支持时,可以使用SCR_EL3.EEL2启用或禁用对安全虚拟化的支持。
GICv3.1将GICv3.0对虚拟化的支持扩展到安全状态,以与Armv8.4-A保持一致。当SCR_EL3.EEL2为1时,上一节中描述的所有功能也适用于安全状态。
安全和非安全状态虚拟化之间有一些细微的区别。在非安全状态下,虚拟环境总是表现为GICD_CTLR.DS为1。在安全状态下,虚拟环境的行为就像GICD_CTLR.DS为0,FIQ路由到EL1。对于大多数寄存器访问,这种区别并没有实际的区别。然而,当写入ICV_BPR1_EL1时,它会改变允许的最小值。
Sharing the maintenance interrupt
只有一个GIC维护中断,由处于安全和非安全状态的不同虚拟化软件共享。处理此问题的一种方法是在更改安全状态时保存并恢复此中断的配置。
GICv4.1 - Direct injection of virtual interrupts
GICv4继承了前几节中介绍的对虚拟化的所有支持。它增加了对虚拟中断的直接注入(direct injection of virtual interrupts)的支持。此功能允许软件提前向ITS描述物理事件如何映射到虚拟中断。如果虚拟中断所针对的vPE正在运行,则可以转发虚拟中断,而无需首先进入hypervisor。这可以通过减少进入hypervisor的次数来减少与虚拟化中断相关的开销。
GICv4.0支持直接注入虚拟LPI(vLPI)。GICv4.1扩展了对虚拟SGI(vSGI)的支持。GICv4.0和GICv4.1之间有一些变化,这使得它们彼此不兼容。本指南涵盖了GICv4.1编程模型。
在GICv4.0和GICv4.1中,直接注入仅限于非安全状态。在安全状态下不支持直接注入。
Overview
本节首先简要概述了直接注入在GICv4.1中的工作原理。以下部分提供了更多详细信息。
GICv4.1允许软件定义几个虚拟PE(vPE),并将物理中断映射到这些vPE。vPE由vPEID(虚拟处理元素ID)标识。vPEID是一个全局标识符,由系统中的所有Redistributor和ITS共享。
vPE的配置和状态存储在基于内存的表中。这与物理LPI的配置和状态的管理方式类似。Redistributors用于管理虚拟中断的基于内存的表有三种类型:
Virtual LPI Pending Table
每个vPE有一个虚拟LPI挂起表。它存储针对该vPE的虚拟中断的pending状态。
Virtual LPI Configuration Table
虚拟LPI配置表存储vLPI的配置(启用和优先级)。虚拟配置表可以由多个vPE共享。例如,一个虚拟机中的所有vPE可能共享一个虚拟LPI配置表。
vPE Configuration Table
vPE配置存储所有vPE的设置。每个vPE在表中有一个条目,存储指向该vPE的virtual Pending and Configuration tables的指针。vPE配置表条目还存储有关vPE的其他信息,例如vINTID命名空间有多大。vPE配置表格由多个Redistributor共享,通常每个SoC有一个表格副本。
下图显示了这些表格之间的关系:
vPE配置表的位置和大小由使用GICR_VPROPBASER寄存器的软件指定。发出ITS命令,表中的条目被填充。稍后将对此进行介绍。
GIC需要知道当前在物理PE(pPE)上调度了哪个vPE(如果有的话)。对于GICv4.1,当前scheduled的vPE在GICR_VPENDBASER中指定。
这是GICv3和GICv4之间的显著差异。在GICv3和GICv4中,虚拟中断只能传递到当前调度的vPE。在GICv3中,硬件不知道调度的vPE的ID,而是由软件负责管理上下文切换。在GICv4中,硬件需要知道当前调度了哪个vPE,因为中断可以随时到达。
软件使用ITS命令来创建和管理vPE。VMAPP定义一个新的vPE,指定其配置以及virtual Pending and Configuration tables的位置。此信息存储在vPE配置表(vPE Configuration Table)中。VMAPI和VMAPTI将物理中断映射到针对特定vPE的虚拟中断。
下图显示了当以vPE为目标的中断到达时会发生什么:
- 外围设备向ITS发送MSI。
- ITS转换MSI中的EventID/DeviceID。返回的映射指示中断被映射到vPE,而不是物理LPI。
- ITS将中断转发给目标Redistributor,发送中断的vINTID和vPEID。
- Redistributor从vPE配置表中检索vPE和vINTID的配置。它还使用ICR_VPENDBASER检查vPE是否已调度。
- 如果vPE被调度,则中断被转发到虚拟CPU interface.。否则,中断将被记录为pending,并在下一次调度vPE时传递。
ITS和Redistributors可以缓存来自不同表的信息。因此,在实践中,并不是所有的中断都需要访问内存来检索表内容。
Redistributor
Redistributor从vPE配置表中检索vPE和vINTID的配置。
CommonLPIAff groups
Redistributors 的分组由GICR_TYPER.CommonLPIAff和GICR_TTYPER.Affinity定义。CommonLPIAff充当Affinity值的掩码,应用掩码后具有相同affinity的Redistributors属于同一组。
例如,如果CommonLPIAff==2,那么具有相同Aff3.Aff2值的所有Redistributor都在同一组中。
考虑一个具有四个Redistributor的系统,具有以下affinities:
• 0.0.0.0
• 0.0.0.1
• 0.1.0.0
• 0.1.0.1
mask 应用之后
• 0.0.x.x
• 0.0.x.x
• 0.1.x.x
• 0.1.x.x
也就是说,我们有两个组0.1.x.x和0.0.x.x
CommonLPIAff组应该是物理上彼此接近的Redistributor。
例如,在多芯片设计中,每个芯片可能有一个组。
CommonLPIAff值很重要,因为它决定了软件必须分配多少内存结构,以及vPE可以调度在哪些Redistributors上。我们将在以下部分中对此进行讨论。
The vPE Configuration Table
vPE配置表存储所有vPE的详细信息。表格的大小决定了软件可以创建多少vPE。
vPE配置表由GIC,通过ITS命令来填充和维护。在将内存提供给GIC之后,软件永远不会读取或写入表。这样做可能会导致GIC的行为不正确。
软件必须为每个CommonLPIAff组分配一份表的副本。也就是说,如果有两个CommonLPIAff组,软件必须为表的两个副本分配足够的内存。这是一个性能优化,因为它允许Redistributors使用与其接近的内存。
在创建映射vPE之前,软件必须分配所需数量的表,并填充每个活动Redistributor的GICR_VPROPBASER。
Controlling which vPE is scheduled
当前在PE上运行的vPE由GICR_VPENDBASER定义。要更改scheduled的vPE,软件必须:
Clear GICR_VPENDBASER.Valid
清除Valid位会通知Redistributor正在进行上下文切换。Redistributor从虚拟CPU interface检索任何挂起的虚拟中断,并确保内存中的Virtual LPI Pending Table是正确的。
Poll GICR_VPENDBASER.Dirty until it reads 0
Dirty位报告Redistributor何时完成更新其内部状态。这包括从vCPU interface检索旧vPE的任何pending的虚拟中断。在该位读取0之前,无法调度新vPE。Arm建议,在观察到Dirty为0之前,虚拟化软件不会对ICH寄存器进行上下文切换。
Update GICR_VPENDBASER, setting Valid==1 in the process
将Valid位设置为1会通知Redistributor新的vPEID现在是有效的,并且可以转发该vPE的虚拟中断。GICR_VPENDBASER还包含virtual Distributor Group启用,它控制哪些虚拟中断组可以转发到CPU interface。
Optional: Poll GICR_VPENDBASER.Dirty until it reads 0
当Valid被写入1时,GIC会搜索新调度的vPE的中断。Dirty读取为1,直到GIC找到了它可以传递的中断,或者它已经完成了遍历pending表并且没有发现pending中断。
在ITS中,vPE被映射到特定的Redistributor。这种映射可能会随着时间的推移而改变,但在任何给定的点上,vPE都有一个单一的目标Redistributor。但是,可以在与目标Redistributor属于同一CommonLPIAff组的任何Redistributer上调度vPE。在属于不同组的Redistributor上进行调度可能会导致GIC行为不正确。
在单芯片设计中,所有Redistributor都可能是同一CommonLPIAff组的一部分。在这种情况下,您可以在任何Redistributor上安排vPE。
软件不得:
- 在ITS中映射vPE之前,在任何Redistributor上的vPEID设置为scheduled。
- 在多个Redistributor上,标记相同的vPEID为scheduled。
Doorbells
Hypervisor通常将vPE分为三类:
Running
vPE当前由hypervisor和物理PE调度。对于GIC,这意味着vPE可以接收直接注入的虚拟中断。
Runnable or to-be-scheduled
vPE没有在任何物理PE上进行调度。hypervisor知道vPE还有工作要做,所以会在将来的某个时候进行调度。GIC当前无法将虚拟中断传递到此vPE。
Idle
vPE当前未在任何物理PE上进行调度。hypervisor认为vPE没有工作要做,因此将来不会进行调度。
当vPE的工作可用时,vPE将从Idle移动到Runnable。发生这种情况的一种方式是,以vPE为目标的中断到达。但这需要hypervisor知道中断已经到达。这种机制被称为doorbell中断。
当虚拟中断到达时,如果调度了目标vPE,则中断可以转发到CPU interface:
当vPE未被调度时,可以选择生成doorbell中断:
这个doorbells是一个物理中断,通常会被带到EL2并由hypervisor处理。它向hypervisor发出信号,表示未调度的vPE有一个挂起的中断,这意味着它应该被移动到可运行队列中以进行未来的调度。
GICv4.1支持两种类型的门铃:
•默认doorbells
•单独的doorbells(在GICv4.1中支持是可选的)
Default doorbells
每个vPE都可以分配一个默认doorbell。当针对vPE的任何中断变为pending并且vPE未被调度时,将生成默认门铃。
该体系结构为默认门铃提供了几个保证:
- vPE的默认门铃设置为在residencies之间pending不超过一次。一旦vPE从Idle移动到Runnable,软件就不需要为该vPE再按门铃了。vPE已scheduled。
- 只有在vPE non-scheduled时,且有请求时,才会生成默认门铃。如果hypervisor已将vPE标记为可运行,则不需要门铃。这样接收一个将是低效的。
- 只有在启用了pending的中断时,才会生成默认门铃。软件只想知道将被转发到vPE的pending中断。如果中断被禁用,那么我们不需要使vPE可运行。
- 通过scheduled vPE来清除pending的默认门铃。如果vPE在scheduled时仍有一个未完成的默认门铃,则该中断将被清除。由于hypervisor不再需要知道vPE正在执行工作。
默认门铃生成由GICR_VPENDBASER中的两位控制:
GICR_VPENDBASER.PendingLast
当vPE被设为non-scheduled时,此位报告该vPE是否存在pending中断。
GICR_VPENDBASER.Doorbell
软件设置此位以指示是否需要生成门铃。
当PendingLast报告vPE有挂起的中断时,门铃被视为0(没有门铃)。否则,这意味着必须立即生成门铃。
用于vPE默认门铃的INTID是使用ITS命令设置的,我们稍后将对此进行介绍。
软件在创建vPE时不需要注册默认门铃。将默认门铃INTID设置为1023意味着没有门铃。
Changing the configuration of default doorbells
默认的门铃是物理的LPI,这意味着它们的配置存储在内存中。具体而言,在物理LPI配置表中。如Locality-Specific Peripheral
Interrupts guide指南所述,如果软件想要更改物理LPI的配置,它会写入表,然后使用INV命令使旧配置的任何缓存无效。
尽管默认门铃是物理LPI,但在缓存方面,它们的行为与其他LPI不同。软件必须为用作默认门铃的INTID发出INVDB命令。
Individual doorbells
每个虚拟中断可以选择设置一个单独的门铃,而不是每个vPE。这意味着,hypervisor可能会根据针对vPE的中断pending而采取不同的操作。例如,大多数中断都可以使用默认的门铃,只会导致vPE被标记为可运行。一个高优先级的中断可以被分配给一个单独的门铃,并导致立即rescheduling。
单个门铃并不能像默认门铃那样提供所有的保证。特别是:
- 无法保证在两次residencies之间会有一次单独的门铃挂起。
- 软件无法注册是否需要单独的门铃使vPE non-scheduled。如果为虚拟中断提供了一个中断,则在vPE未调度时生成该中断。
软件可以为多个虚拟中断分配相同的物理INTID,只要所有这些中断都属于同一vPE。
对单个门铃的支持是可选的,支持由GITS_TYPER.nID报告。
当映射虚拟中断时,软件不需要注册单个门铃。将门铃INTID设置为1023意味着没有门铃。
ITS
ITS负责转换传入的MSIs并将其作为虚拟中断转发。在之前的指南中,我们介绍了基本的翻译机制。如果您还不熟悉此过程,请阅读Locality-Specific Peripheral Interrupts指南。
vPE Table
对于虚拟中断,中断转换表(ITT)记录中断的目标vPE和虚拟INTID。ITS还需要记录vPE映射到哪组Redistributors。ITS有两种方法可以做到这一点,GITS_TYPER.SVEPT表示支持哪种模型:
SVEPT==0
ITS使用专用表来记录vPE映射。软件必须为此表分配内存,并将GITS_BASER2设置为指向分配的内存。
SVEPT==1
ITS重用Redistributors的vPE配置表。软件必须将GITS_BASER2设置为指向为Redistributers分配的vPE配置表。
与其他ITS表一样,必须在启用ITS之前分配这些结构。这适用于两种型号。
Mapping a vPE
使用VMAPP命令创建vPE:
在此命令中:
- vPEID是vPE的ID。
- RDADDR是目标Redistributor。
- VPT地址和VCT地址是virtual Pending tables和Configuration tables的地址。
- doorbell是vPE默认门铃的物理INTID。指定1023意味着vPE没有门铃中断。
在目标Redistributor中分配和设置vPE配置表之前,软件不得使用VMAPP创建vPE。
Mapping MSI to vINTID
EventID/DeviceID组合映射到vINTID和vPE。当EventID和vINTID相同时,将使用VMAPI命令。
当EventID和vINTID不同时,将使用VMAPTI命令。
在这些命令中:
- DeviceID和EventID一起标识正在映射的中断。
- vPEID是vPE的ID。
- vINTID是虚拟LPI的INTID。对于VMAPI,EventID和vINTID具有相同的值。
- pINTID是在vPE未被调度时生成的单个门铃中断。指定1023意味着没有单独的门铃中断。
在使用VMAPP命令创建vPE之前,软件不得将中断映射到vPE。
Example
外围设备的设备ID为5。它生成两个EventID,0和1。两个EventID都映射到属于vPE ID为6的vPE的vINTID:
- 事件ID 0–vINTID 8725,无单个门铃中断 。
- 事件ID 1–vINTID 9000,无单个门铃中断。
vPE 6被映射到Redistributor编号7,并使用8192作为其默认门铃。
此操作的命令序列如下:
该示例假设GITS_TYPER.PTA==0,并且先前已发出映射ITT的MAPD命令。
Remapping a vPE to a different Redistributor
如果hypervisor将vPE迁移到属于不同CommonLPIAff组的Redistributor,则必须更新ITS映射,以便将虚拟中断发送到正确的位置。使用VMOVP命令更新ITS映射,然后使用VSYNC来同步上下文。
Doorbell中断总是传递到映射的Redistributor,但vPE可以调度到同一CommonLPIAff组内的任何Redistributer。如果软件希望将vPE的门铃中断发送到不同的PE,则必须发出VMOVP命令。
一个系统可以包括多个IT。如果多个ITS具有vPE的映射,则任何更改都必须应用于包含原始映射的所有ITS。GICv4支持两种模型来实现这一点,ITS_TYPER.VMOVP指示使用哪种模型。
GITS_TYPER.VMOVP==1
VMOVP命令必须仅在一个ITS上发出,无论有多少ITS具有vPE的映射。需要硬件来传播更改并处理同步。这意味着不需要ITS列表和序列号字段。
Arm预计这将是大多数GIC实现的模型。
GITS_TYPER.VMOVP==0
必须在具有vPE映射的所有IT上发出VMOVP命令。
在此命令中:
- vPEID 是vPE的ID
- RDADDR 是vPE要重新映射到的Redistributor。
- ITS List 是具有vPE映射的所有IT的列表。该字段被编码为每个ITS一个比特,其中比特0映射到ITS 0。ITS的编号由GITS_CTLR.ITS_number报告。
- Sequence Number 是同步点。当向不同的IT发出VMOVP命令时,软件必须使用相同的值,并且在所有IT上完成命令之前,不得重复使用相同的数值。
Remapping vINTIDs
VMOVI命令将EventID和DeviceID组合重新映射到不同的vINTID和vPE。
在此命令中:
- DeviceID 和EventID一起识别正在重新映射的中断。
- vPEID 是将中断移动到的vPE的ID。
- vINTID 是中断现在应该使用的虚拟INTID。
- pINTID 是在vPE未被调度时生成的单个doorbell中断。指定1023意味着没有单个的门铃中断。
Removing mappings
映射命令VMAPP和VMAPTI具有一个V字段。当V为1时,它们将被视为映射命令。当V为0时,它们将被视为取消映射命令。
删除vPE的映射时,软件必须首先删除该vPE的所有中断映射。
Changing vLPI configuration
与物理LPI一样,Redistributor被允许缓存vLPI的配置。如果vLPI的配置发生更改,则缓存的副本必须无效。有两个ITS命令可用于执行此操作。
INV命令通常用于更改单个或少量vLPI的配置。每个修改的vLPI都需要一个单独的INV命令。
VINVAL命令会使属于指定vPE的所有vLPI的配置无效。此命令通常在修改许多vLPI时使用。
在GICv4.1中,还可以使用每个Redistributor中的GICR_INVLPIR寄存器来执行无效操作。Arm预计软件会使用命令或寄存器,但不会经常混合使用这两种方法。
GICv4.1 - Direct injection of vSGIs
GICv4.1引入了一项新功能,即直接注入虚拟SGI(vSGI)的能力。此功能通过消除一些需要输入hypervisor的情况,进一步减少了开销。
Sending a virtual SGI
软件通过写入ITS中的GITS_SGIR寄存器来生成vSGI。软件写入目标vPE的vPEID和正在发送的SGI的INTID。ITS查找指定vPE的映射,然后将vSGI转发到目标Redistributor。
下图显示了发送vSGI的流程:
- 在虚拟机中运行的软件写入ICC_SGI寄存器之一以生成SGI。此写入包含正在发送的INTID和目标vPE的关联值。此寄存器写入触发EL2的陷阱异常。
- hypervisor将虚拟机写入的关联值转换为vPEID,然后写入GITS_SGIR以生成虚拟中断。
- ITS查找指定vEID的目标Redistributor,然后将vSGI转发给该Redistributer。
- 如果调度了目标vPE,Redistributor将检索中断的配置,并将中断转发到CPU interface。然后,CPU interface将vSGI作为虚拟中断异常发送信号。
如果vPE未被调度,则中断将被记录为pending.。可以选择生成默认门铃。
此过程仍然需要一定程度的hypervisor交互,才能将写入ICC_SGIR寄存器的虚拟关联值转换为vPEID。但在此之后,GIC的直接注入机制可以处理剩下的过程。
SGI configuration
为了直接注入vSGI,Redistributor需要知道SGI的配置(启用、优先级和组)。此信息记录在virtual LPI Pending Table中。在Locality Specific Peripheral Interrupts指南中,我们介绍了Pending表,并描述了该表的前1K是如何用于记录特定于实现的信息的。GICv4.1使用该空间的一小部分来存储vSGI配置。
与vLPI不同,软件不能直接写入表来更新vSGI的配置。相反,使用新的ITS命令设置配置:
这里:
- vPEID标识目标vPE。
- vINTID是正在更新的vSGI。
- Enable(启用)、Group(组)和Priority(优先级)是vSGI配置。
- Clear(清除)可用于清除中断的pending状态。
Sending Group versus receiving Group
当软件生成SGI时,写入的寄存器指定要发送的组:
- ICC_SGI0R_EL1:发送组0中断 。
- ICC_SGI1R_EL1:发送组1中断。
接收器还为每个SGI INTID指定一个组。对于物理中断,这是使用GICR_IGROUPR0和GICR_IGRPMODR0寄存器指定的。对于虚拟SGI,正如我们刚刚看到的,Group是使用VSGI命令设置的。
对于物理SGI,GIC根据接收器上配置的组检查发送的组。只有当它们匹配时,中断才会设置为pending。
对于虚拟SGI,GITS_SGIR中没有“组”字段。GIC将始终使用为接收器配置的组。因此,由软件(通常是hypervisor)根据接收器配置的组来检查发送的组。
Differences between vSGIs and pSGIs
在大多数方面,虚拟和物理SGI的行为是相同的。有一个区域它们是不同的 vSGI使用与LPI相同的状态机,如下图所示:
这降低了GIC硬件的复杂性。在大多数情况下,这种差异对软件来说是看不见的。要想看到差异,虚拟机中的软件必须使用EOImode==1。这就是使用两个单独的寄存器写入来执行优先级降低和去激活的地方。对于pSGI,直到去激活之后才能看到相同的INTID。对于vSGI,在优先级下降之后,在去激活之前,可以看到相同的INTID。
Querying SGI state
有时,hypervisor需要检查vSGI是否处于pending.状态。例如,处理VM对GICR_ISPENDR0寄存器的读取。
为了实现这一点,在Redistributor中添加了两个新寄存器,以查询vPE的vSGI的当前挂起状态。软件执行以下操作:
- 将vPEID写入GICR_VSGIR。
- 轮询GICR_VSGIPENDR.Busy,直到它读取到0,此时GICR_VSGIPENDR.Pending报告vPE的SGI的挂起状态。
没有可模拟写入GICR_ISPENDR0和GICR_ICPENDR0寄存器的Redistributor寄存器。这些可以使用ITS进行仿真:
- 写入GICR_ICPENDR0可以使用Clear==1的VSGI命令进行模拟。
- 写入GICR_ISPENDR0可以通过写入GITS_SGIR寄存器来模拟。