APIC虚拟化
简介:
中断虚拟化中不可避免需要接触到APIC的虚拟化,其中包括IOAPIC和LAPIC。这里挖个坑,准备逐步写一下从硬件原型到软件模拟的知识,谨作学习记录。
LAPIC硬件
1. LAPIC内部寄存器布局可参考下图
大体按照功能分类如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | Timer related: CCR: Current Count Register ICR: Initial Count Register DCR: Divide Configuration Register Timer: in LVT LVT (Local Vector Table): Timer Local Interrupt Performance Monitor Counters Thermal Sensor Error IPI: ICR: Interrupt Command Register LDR: Logical Destination Register DFR: Destination Format Register Interrupt State: ISR: In-Service Register IRR: Interrupt Request Register TMR: Trigger Mode Register |
2. LAPIC地址访问方式
x86 CPU中一共有三种访问APIC的方式:
- 通过CR8来访问TPR寄存器(仅IA32-e即64位模式)
- 通过MMIO来访问APIC的寄存器,这是xAPIC模式提供的访问方式
- 通过MSR来访问APIC的寄存器,这是x2APIC模式提供的访问方式
3. APIC地址模型
LAPIC地址为0XFEE00000开始的4K MMIO地址空间,所有CPU的LAPIC地址都是这里。
访问APIC寄存器为针对0xfee00000的4K以内的偏移量,MMIO地址访问会产生陷出,在VMM中根据偏移量在reg申请的4K空间中操作对应CPU中虚拟出来的寄存器。
LAPIC虚拟化
1. lapic寄存器虚拟化(APIC-register virtualization)
- 寄存器虚拟化是其他中断优化的基础
- TPR shadow需要使能
- APIC寄存器物理地址需要写到VMCS
所有CPU访问自己的LAPIC用了相同的物理地址,那么guest中两个虚拟CPU访问自己的虚拟LAPIC也用相同的物理地址,但一个guest只有一套EPT机制,这怎么区分?
intel发明了apic-access page和virtual-apic page,这两个page的物理地址写在VMCS中,一个guest只有一个apic-access page,每个虚拟CPU有一个virtual-apic page,guest虚拟CPU读写LAPIC时,EPT把地址翻译指向apic-access page,EPT翻译完了,CPU知道自己此时处于guest模式,正在运行哪个虚拟CPU,加载着哪个VMCS,然后去virtual apic page去拿数据,这样就能保证虚拟CPU访问相同的物理地址拿到不同的结果,而且不需要hypervisor软件介入,比如虚拟CPU读自己的LAPIC ID,那结果肯定不一样,EPT翻译到相同的地址,如果没重定向到virtual-apic page那么结果就一样了。
需要使能和实现的VMCS中VM-Execution control区域如下所示:
primary processor-based VM-executeion controls: Use TPR shadow, Virtual-APIC address
secondary processor-based VM-executeion controls: APIC register virtualization
APIC-Register Virtualization = 1,则对下列寄存器的读访问不会引起APIC Access VM Exit
- APIC ID Register、APIC Version Register
- TPR、LDR、DFR
- Spurious-Interrupt Vector Register
- IRR、ISR、TMR、EOI Register
- Error Status Register
- ICR
- LVT Entries、Initial Count Register、Divide Configuration Register
- 换句话说除了只读的PPR和Current Count Register,APIC中其余所有寄存器都允许从Virtual-APIC Page中读取
APIC-Register Virtualization = 1,则对下列寄存器的访问不会引起APIC Access VM Exit
- APIC ID Register
- TPR、LDR、DFR
- Spurious-Interrupt Vector Register
- Error Status Register
- ICR
- LVT Entries、Initial Count Register、Divide Configuration Register
- 相比读取请求,删去了只读的APIC Verson Register以及IRR、ISR、TMR
2. apic-access使能
primary processor-based VM-executeion controls: Use TPR shadow, Virtual-APIC address、APIC-access address
secondary processor-based VM-executeion controls: virtualize apic access、APIC register virtualization、
相比于只实现寄存器虚拟化变化点:
- 新增两个退出原因apic_access/apic_write
- 对apic寄存器操作不再通过EPT violation陷出
相比于只实现寄存器虚拟化优化点:
- 执行MMIO指令模拟前不需要先遍历一遍memory slot
3. X2APIC
- X2APIC和apic-access不能共存。
- X2APIC也需要APIC-register virtualization使能以保证GUEST可以不陷出。
- Use-TPR shadow需要使能。
- MSR的bitmap对应的800-8ff需要置位。
primary processor-based VM-executeion controls: Use TPR shadow, Virtual-APIC address secondary processor-based VM-executeion controls: virtualize X2APIC mode、APIC register virtualization Read bitmap for low MSRS, Read bitmap for high MSRS, Write bitmap for low MSRS, Write bitmap for high MSRS,
相比较与只实现寄存器虚拟化变化点:
- 对apic寄存器操作不通过MMIO陷出,而使用MSR:0x800-0x8ff
- GUEST内核需要开启X2APIC支持
相比较与只实现寄存器虚拟化优化点:
- 不需要MMIO陷出后的指令解析和执行的模拟,直接读写对应地址的值
4. Virtual-interrupt delivery
secondary processor-based VM-executeion controls: virtual interrupt delivery
控制位使能后将自动使能VMCS的Guest-state区域中的Guest interrupt status区域。区域介绍如下:
Guest interrupt status (16 bits). This field is supported only on processors that support the 1-setting of the
“virtual-interrupt delivery” VM-execution control. It characterizes part of the guest’s virtual-APIC state and
does not correspond to any processor or APIC registers. It comprises two 8-bit subfields:
— Requesting virtual interrupt (RVI). This is the low byte of the guest interrupt status. The processor
treats this value as the vector of the highest priority virtual interrupt that is requesting service. (The value
0 implies that there is no such interrupt.)
— Servicing virtual interrupt (SVI). This is the high byte of the guest interrupt status. The processor treats
this value as the vector of the highest priority virtual interrupt that is in service. (The value 0 implies that
there is no such interrupt.)
Virtual-interrupt delivery使能后,物理核会执行Evaluation of Pending Virtual Interrupts。
会执行Evaluation of Pending Virtual Interrupts的场景:
- VM entry;
- TPR virtualization;
- EOI virtualization;
- self-IPI virtualization;
- posted-interrupt processing。
Virtual-interrupt delivery会在不产生VM exit情况下更新RVI和SVI,大致流程如下:
5. post interrupt
需置位的VMCS Execution control区域如下:
pin-based VM-execution controls: external-interrupt exiting、Process posted interrupts primary processor-based VM-executeion controls: Use TPR shadow, Virtual-APIC address、posted-interrupt notification vector、 posted-interrupt descriptor address secondary processor-based VM-executeion controls:APIC register virtualization、Virtual-interrupt delivery
Posted Interrupt,它引入了一个Posted-Interrupt Notification Vector(VMCS[0x0002](16 bit),仅最低8位有效)和一个64字节(恰好占满一个Cache Line)的Posted-Interrupt Descriptor。后者位于内存中,其地址(HPA)通过Posted-Interrupt Descriptor Address(VMCS[0x2016]/VMCS[0x2017](64 bit full/high))指定,格式如下:
第0-255位为Posted-Interrupt Requests (PIR),是一个Bitmap,每个位对应一个Vector
第256位为Outstanding Notification (ON),取1表示有一个Outstanding的Notificaton Event(即中断)尚未处理
第257-511位为Available,即允许软件使用的Ignored位
结构体如下:
作用:
Posted Interrupt是对Virtual-Interrupt Delivery的进一步发展,让我们可以省略Interrupt Acceptance的过程,直接令正在运行的vCPU收到一个虚假中断,而不产生VM Exit。
前置条件:
当Non-root模式下收到一个外部中断时,CPU首先完成Interrupt Acceptance和Interrupt Acknowledgement(因为开启Posted Interrupt必先开启Acknowledge Interrupt on Exit),并取得中断的Vector。然后,若Vector与Posted-Interrupt Notification Vector相等,则进入Posted-Interrupt Processing,否则照常产生External Interrupt VM Exit。
Posted-Interrupt Processing的过程如下:
- 清除Descriptor的ON位
- 向CPU的EOI寄存器写入0,执行EOI,至此在硬件APIC上该中断已经处理完毕
- 令VIRR |= PIR,并清空PIR
- 设置RVI = max(RVI, PIRV),其中PIRV为PIR的旧值中优先级最高的Vector
- 最后evaluate pending virtual interrupts
qemu初始化逻辑:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了