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

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

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

创建 guest GICv3 设备也需要 host GICv3。

KVM_DEV_ARM_VGIC_GRP_ADDR

属性

KVM_VGIC_V3_ADDR_TYPE_DIST (rw, 64 位)

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

KVM_VGIC_V3_ADDR_TYPE_REDIST (rw, 64 位)

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

KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION (rw, 64 位)

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

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

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

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

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

该区域中每个重分发器都有两个 64K 页面,并且重分发器在该区域内连续布局。区域按索引顺序填充重分发器。所有区域 count 字段的总和必须大于或等于 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     |

所有分发器寄存器均为 (rw, 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    |

请注意,分发器字段不是组的,而是返回相同的值,而与用于访问该寄存器的 mpidr 无关。

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

GICD_STATUSR 和 GICR_STATUSR 寄存器在架构上定义为:写入清除位不会产生任何影响,而写入设置位会清除该值。为了允许用户空间自由设置这两个寄存器的值,设置具有这两个寄存器的寄存器偏移量的属性只会将非保留位设置为写入的值。

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

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

向用户空间提供对锁存状态的原始访问权限,以便它可以保存和恢复整个 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 访问的所有系统寄存器均为 (rw, 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 待处理表中。

待处理表的前 1kB 不受此操作影响。

错误

-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 值,该值将包含一个位图,其中设置的位表示中断电平被置为有效。

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

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

PPI 根据 mpidr 字段中指定的每个 VCPU 报告,SPI 以相同的值报告,而与指定的 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