ARM 虚拟通用中断控制器 v3 及更高版本 (VGICv3)

支持的设备类型
  • KVM_DEV_TYPE_ARM_VGIC_V3 ARM 通用中断控制器 v3.0

此 API 只能实例化一个 VGIC 实例。创建的 VGIC 将充当 VM 中断控制器,要求模拟的用户空间设备将中断注入到 VGIC,而不是直接注入到 CPU。无法在同一 VM 上同时创建 GICv3 和 GICv2。

创建访客 GICv3 设备也需要主机 GICv3。

KVM_DEV_ARM_VGIC_GRP_ADDR

属性

KVM_VGIC_V3_ADDR_TYPE_DIST (读写, 64 位)

GICv3 分发器寄存器映射在访客物理地址空间中的基地址。仅对 KVM_DEV_TYPE_ARM_VGIC_V3 有效。该地址需要 64KB 对齐,且区域覆盖 64KB。

KVM_VGIC_V3_ADDR_TYPE_REDIST (读写, 64 位)

GICv3 重分发器寄存器映射在访客物理地址空间中的基地址。每个 VCPU 有两个 64KB 页,并且所有重分发器页都是连续的。仅对 KVM_DEV_TYPE_ARM_VGIC_V3 有效。该地址需要 64KB 对齐。

KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION (读写, 64 位)

kvm_device_attr.addr 指向的属性数据是一个 __u64 值

bits:     | 63   ....  52  |  51   ....   16 | 15 - 12  |11 - 0
values:   |     count      |       base      |  flags   | index
  • index 编码了唯一的重分发器区域索引

  • flags:保留供将来使用,当前为 0

  • base 字段编码了区域中第一个重分发器的访客物理基地址的位 [51:16]。

  • count 编码了区域中重分发器的数量。必须大于 0。

区域中的每个重分发器有两个 64KB 页,并且重分发器在区域内是连续布局的。区域按照索引顺序填充重分发器。所有区域计数字段的总和必须大于或等于 VCPU 的数量。重分发器区域必须按增量索引顺序注册,从索引 0 开始。

可以通过预设 attr 数据中的 index 字段来读取特定重分发器区域的特性。仅对 KVM_DEV_TYPE_ARM_VGIC_V3 有效。

混合使用 KVM_VGIC_V3_ADDR_TYPE_REDIST 和 KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION 属性的调用是无效的。

请注意,为了获得可重现的结果(在保存/恢复操作中,同一个 VCPU 与同一个重分发器相关联),VCPU 创建顺序、重分发器区域创建顺序以及 VCPU 和区域创建的相应交错顺序必须保留。任何顺序上的更改都可能导致不同的 vcpu_id/重分发器关联,从而导致 VM 在恢复时无法运行。

错误

-E2BIG

地址超出可寻址 IPA 范围

-EINVAL

地址未正确对齐,重分发器区域计数/索引错误,混合使用重分发器区域属性

-EEXIST

地址已配置

-ENOENT

尝试读取不存在的重分发器区域的特性

-ENXIO

该组或属性对于此设备而言未知/不支持,或者缺少硬件支持。

-EFAULT

attr->addr 的用户指针无效。

KVM_DEV_ARM_VGIC_GRP_DIST_REGS, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS

属性

kvm_device_attr 的 attr 字段编码了两个值

bits:     | 63   ....  32  |  31   ....    0 |
values:   |      mpidr     |      offset     |

所有分发器寄存器都是 (读写, 32 位),且 kvm_device_attr.addr 指向一个 __u32 值。必须通过分别访问低位字和高位字来访问 64 位寄存器。

对只读寄存器的写入操作将被内核忽略。

KVM_DEV_ARM_VGIC_GRP_DIST_REGS 访问主分发器寄存器。KVM_DEV_ARM_VGIC_GRP_REDIST_REGS 访问由 mpidr 指定的 CPU 的重分发器。

偏移量是相对于 GICv3/4 规范中定义的“[重]分发器基地址”的。获取或设置此类寄存器与在实际硬件上读取或写入寄存器具有相同的效果,以下寄存器除外:GICD_STATUSR、GICR_STATUSR、GICD_ISPENDR、GICR_ISPENDR0、GICD_ICPENDR 和 GICR_ICPENDR0。当通过此接口访问这些寄存器时,它们的行为与架构定义的行为不同,以便软件能够全面查看 VGIC 的内部状态。

mpidr 字段用于指定访问哪个重分发器。对于分发器,mpidr 被忽略。

mpidr 编码基于架构定义的 MPIDR 中的亲和信息,该字段编码如下

| 63 .... 56 | 55 .... 48 | 47 .... 40 | 39 .... 32 |
|    Aff3    |    Aff2    |    Aff1    |    Aff0    |

请注意,分发器字段不是分区的(banked),无论用于访问寄存器的 mpidr 是什么,它们都返回相同的值。

当 KVM 实现以访客或用户空间可以直接观察到的方式更改时,GICD_IIDR.Revision 会更新。用户空间应从 KVM 读取 GICD_IIDR 并将读取的值写回,以确认其预期行为与 KVM 实现保持一致。用户空间在设置任何其他寄存器之前应设置 GICD_IIDR,以确保预期行为。

GICD_STATUSR 和 GICR_STATUSR 寄存器在架构上定义为:清除位的写入没有效果,而设置位的写入会清除该值。为了允许用户空间自由设置这两个寄存器的值,通过这两个寄存器的寄存器偏移量设置属性时,只需将非保留位设置为写入的值。

对 GICD_ISPENDR 寄存器区域和 GICR_ISPENDR0 寄存器的访问(读写)会获取/设置中断的锁存挂起状态的值。

这与访客从 ISPENDR 读取边缘触发中断时返回的值相同,但对于电平触发中断可能不同。对于边缘触发中断,一旦中断变为挂起(无论是由于输入线检测到边缘还是由于访客写入 ISPENDR),此状态即被“锁存”,并且仅当中断被激活或访客写入 ICPENDR 时才清除。电平触发中断可能处于挂起状态,原因可能是设备将电平输入保持为高电平,或者由于访客写入 ISPENDR 寄存器。只有 ISPENDR 写入会被锁存;如果设备降低线路电平,则除非访客也写入 ISPENDR,否则中断将不再挂起;相反,如果线路电平仍保持高电平,则对 ICPENDR 的写入或中断的激活不会清除挂起状态。(这些规则在 GICv3 规范中 ICPENDR 和 ISPENDR 寄存器的描述中有记载。)对于电平触发中断,此处访问的值是 ISPENDR 设置并由 ICPENDR 或中断激活清除的锁存器的值,而访客从 ISPENDR 读取返回的值是锁存器值与输入线电平的逻辑或(OR)结果。

提供对锁存状态的原始访问权限给用户空间,以便用户空间可以保存和恢复整个 GIC 内部状态(该状态由当前输入线电平和锁存状态的组合定义,不能仅从线电平和 ISPENDR 寄存器的值推断出来)。

对 GICD_ICPENDR 寄存器区域和 GICR_ICPENDR0 寄存器的访问具有 RAZ/WI 语义,这意味着读取总是返回 0,写入总是被忽略。

错误

-ENXIO

尚不支持获取或设置此寄存器

-EBUSY

一个或多个 VCPU 正在运行

KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS

属性

kvm_device_attr 的 attr 字段编码了两个值

bits:     | 63      ....       32 | 31  ....  16 | 15  ....  0 |
values:   |         mpidr         |      RES     |    instr    |

mpidr 字段基于架构定义的 MPIDR 中的亲和信息编码 CPU ID,该字段编码如下

| 63 .... 56 | 55 .... 48 | 47 .... 40 | 39 .... 32 |
|    Aff3    |    Aff2    |    Aff1    |    Aff0    |

instr 字段基于 A64 指令集编码中定义的系统寄存器访问字段来编码要访问的系统寄存器(RES 表示这些位保留供将来使用,应为零)

| 15 ... 14 | 13 ... 11 | 10 ... 7 | 6 ... 3 | 2 ... 0 |
|   Op 0    |    Op1    |    CRn   |   CRm   |   Op2   |

通过此 API 访问的所有系统寄存器都是 (读写, 64 位),且 kvm_device_attr.addr 指向一个 __u64 值。

KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS 访问由 mpidr 字段指定的 CPU 的 CPU 接口寄存器。

CPU 接口寄存器访问未在 AArch32 模式下实现。在 AArch32 模式下访问时返回错误 -ENXIO。

错误

-ENXIO

尚不支持获取或设置此寄存器

-EBUSY

VCPU 正在运行

-EINVAL

提供了无效的 mpidr 或寄存器值

KVM_DEV_ARM_VGIC_GRP_NR_IRQS

属性

一个描述此 GIC 实例中断数量(SGI、PPI 和 SPI)的值,范围从 64 到 1024,增量为 32。

kvm_device_attr.addr 指向一个 __u32 值。

错误

-EINVAL

设置的值超出预期范围

-EBUSY

值已设置。

KVM_DEV_ARM_VGIC_GRP_CTRL

属性

KVM_DEV_ARM_VGIC_CTRL_INIT

请求初始化 VGIC,kvm_device_attr.addr 中无额外参数。必须在所有 VCPU 创建后调用。

KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES

将所有 LPI 挂起位保存到访客 RAM 挂起表中。

挂起表中的第一个 KB 不受此操作影响。

错误

-ENXIO

在调用此属性之前,VGIC 未按要求正确配置

-ENODEV

没有在线 VCPU

-ENOMEM

分配 vgic 内部数据时内存不足

-EFAULT

无效的访客 RAM 访问

-EBUSY

一个或多个 VCPU 正在运行

KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO

属性

kvm_device_attr 的 attr 字段编码了以下值

bits:     | 63      ....       32 | 31   ....    10 | 9  ....  0 |
values:   |         mpidr         |      info       |   vINTID   |

vINTID 指定报告哪个 IRQ 集合。

info 字段指定用户空间希望使用此接口获取或设置的信息。目前我们支持以下 info 值

VGIC_LEVEL_INFO_LINE_LEVEL

获取/设置一组 32 个连续编号中断的 IRQ 线的输入电平。

vINTID 必须是 32 的倍数。

kvm_device_attr.addr 指向一个 __u32 值,该值将包含一个位图,其中设置的位表示中断电平被置位。

位[n] 表示中断 vINTID + n 的状态。

SGI 以及任何 ID 高于支持中断数量的中断都将是 RAZ/WI。LPI 总是边缘触发的,因此此接口不支持。

PPIs 根据 mpidr 字段指定为每个 VCPU 报告,而 SPIs 无论指定的 mpidr 是什么都报告相同的值。

mpidr 字段基于架构定义的 MPIDR 中的亲和信息编码 CPU ID,该字段编码如下

| 63 .... 56 | 55 .... 48 | 47 .... 40 | 39 .... 32 |
|    Aff3    |    Aff2    |    Aff1    |    Aff0    |
错误

-EINVAL

vINTID 不是 32 的倍数或 info 字段不是 VGIC_LEVEL_INFO_LINE_LEVEL

KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ

属性

kvm_device_attr 的 attr 字段编码了以下值

位: | 31 .... 5 | 4 .... 0 | 值: | RES0 | vINTID |

vINTID 指定当 vGIC 必须生成维护中断时,生成哪个中断。这必须是一个 PPI。