深度探索Linux系统虚拟化:原理与实现
上QQ阅读APP看书,第一时间看更新

1.3.3 访问具有副作用的寄存器

Guest在访问CPU的很多寄存器时,除了读写寄存器的内容外,一些访问会产生副作用。对于这些具有副作用的访问,CPU也需要从Guest陷入VMM,由VMM进行模拟,也就是完成副作用。

典型的比如前面提到的核间中断,对于LAPIC而言,写中断控制寄存器可能需要LAPIC向另外一个处理器发送核间中断,发送核间中断就是写中断控制寄存器这个操作的副作用。因此,当Guest访问LAPIC的中断控制寄存器时,CPU需要陷入KVM中,由KVM调用虚拟LAPIC芯片提供的函数向目标CPU发送核间中断。

再比如地址翻译,每当Guest内切换进程,Guest的内核将设置cr3寄存器指向即将运行的进程的页表。而当使用影子页表机制完成虚拟机地址(GVA)到宿主机物理地址(HPA)的映射时,我们期望物理CPU的cr3寄存器指向KVM为Guest中即将投入运行的进程准备的影子页表,因此当Guest切换进程时,CPU需要从Guest陷入KVM中,让KVM将cr3寄存器设置为指向影子页表。因此,当使用影子页表机制时,KVM需要设置VMCS中的Processor-Based VM-Execution Controls的第15位CR3-load exiting,当设置了CR3-load exiting后,每当Guest访问物理CPU的cr3寄存器时,都将触发物理CPU陷入KVM,KVM调用函数handle_cr设置cr3寄存器指向影子页表,如下代码所示。关于更进一步的详细内容,我们将在“内存虚拟化”一章探讨。


commit 6aa8b732ca01c3d7a54e93f4d701b8aabbe60fb7
[PATCH] kvm: userspace interface
linux.git/drivers/kvm/vmx.c
static int handle_cr(struct kvm_vcpu *vcpu, …)
{
    u64 exit_qualification;
    int cr;
    int reg;

    exit_qualification = vmcs_read64(EXIT_QUALIFICATION);
    cr = exit_qualification & 15;
    reg = (exit_qualification >> 8) & 15;
    switch ((exit_qualification >> 4) & 3) {
    case 0: /* mov to cr */
        switch (cr) {
        …
        case 3:
            vcpu_load_rsp_rip(vcpu);
            set_cr3(vcpu, vcpu->regs[reg]);
            skip_emulated_instruction(vcpu);
            return 1;
    …
}