KVM Hypercall第一弹

1370阅读 0评论2014-09-21 embeddedlwp
分类:LINUX

博主按:这篇博文被CU推荐时改了标题为 --- 看发展还是得LINUX 谈KVM Hypercall  其实我想说的很隐晦的在第一段了,没敢说多,因为我以前承诺过如果标题是咖啡豆,那么一定不谈咖啡的。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

个人其实非常看好KVM,其实个人还非常看好Linux。请注意哥今天发这篇博文的背景:中国人民政府正式发出公告,政府部门采购一律不得用Windows 8, 还有一个新闻是美帝要对5名中国军官进行起诉,再一个是最近国内媒体有意无意提到马航370失联的相关报道,矛头隐隐指向美帝和波音,真假难辨。大意是这样。 然后前不久IBM发布了Power KVM, 而Redhat早就大力支持KVM了,个中滋味请同学们自己思考。。。另一个是:国内号称做OS的厂商几乎没有离开过Linux的,其实大家都懂的。


今天的话就了这么一点,不算多的,哈哈。下面进入正题,今天咱们讲一下Linux下的Hypercall,幕后的黑手其实是某特尔处理器的VMCALL指令咯,手册里说得也不怎么详细,只给了op code:0F 01 C1 -- VMCALL Call to VM monitor by causing VM exit. 如此简洁颇具大家风范,嘿嘿
然后,然后注意Linux kernel source code下的这个文件,3.10的arch/x86/include/asm/kvm_para.h第19行:

/* This instruction is vmcall.  On non-VT architectures, it will generate a
 * trap that we will then rewrite to the appropriate instruction.
 */
#define KVM_HYPERCALL ".byte 0x0f,0x01,0xc1"

这条指令明显给Guest使用无疑,而且Guest一旦使用这个的话基本上意味着是所谓的半虚拟化了,其实我倒觉得你从哪个层面看了,如果你自己写一个内核模块在guest os里跑,然后你的内核模块中用到了VMCALL,那算不算半个虚拟化啊,有点抬杠了。。。

KVM_HYPERCALL其实跟SYSCALL非常相似,包括参数的传递。咱们先看只有一个参数的vmcall:

static inline long kvm_hypercall0(unsigned int nr)
{
    long ret;
    asm volatile(KVM_HYPERCALL
             : "=a"(ret)
             : "a"(nr)
             : "memory");
    return ret;
}

熟悉inline汇编的同学一看就明白,nr可以认为是系统调用号,放在AX寄存器中,返回值也是放在AX中。当Guest OS中调用该函数时,导致VM-EXIT,于是KVM内核模块开始接收到控制权:

arch/x86/kvm/vmx.c:
handle_vmcall() --> kvm_emulate_hypercall():

int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
{
    unsigned long nr, a0, a1, a2, a3, ret;
    int r = 1;

    if (kvm_hv_hypercall_enabled(vcpu->kvm))
        return kvm_hv_hypercall(vcpu);

    nr = kvm_register_read(vcpu, VCPU_REGS_RAX);
    a0 = kvm_register_read(vcpu, VCPU_REGS_RBX);
    a1 = kvm_register_read(vcpu, VCPU_REGS_RCX);
    a2 = kvm_register_read(vcpu, VCPU_REGS_RDX);
    a3 = kvm_register_read(vcpu, VCPU_REGS_RSI);

    trace_kvm_hypercall(nr, a0, a1, a2, a3);

    if (!is_long_mode(vcpu)) {
        nr &= 0xFFFFFFFF;
        a0 &= 0xFFFFFFFF;
        a1 &= 0xFFFFFFFF;
        a2 &= 0xFFFFFFFF;
        a3 &= 0xFFFFFFFF;
    }

    if (kvm_x86_ops->get_cpl(vcpu) != 0) {
        ret = -KVM_EPERM;
        goto out;
    }

    switch (nr) {
    case KVM_HC_VAPIC_POLL_IRQ:
        ret = 0;
        break;
    default:
        ret = -KVM_ENOSYS;
        break;
    }
out:
    kvm_register_write(vcpu, VCPU_REGS_RAX, ret);
    ++vcpu->stat.hypercalls;
    return r;
}
EXPORT_SYMBOL_GPL(kvm_emulate_hypercall);

代码也很简单,根据Hypercall number(nr)进行处理,参数从(R)AX, BX, CX,DX,SI中得到。注意其中的kvm_register_write(vcpu, VCPU_REGS_RAX, ret),将hypercall处理结果返回给guest。如果此处ret = 0x55AA,那么guest中对kvm_hypercall0()的调用将返回0x55AA。
另一个要注意的是, Hypercall是同步执行的,如果HOST中不返回(比如睡眠10秒钟),那么guest中对kvm_hypercall0()的调用也不会返回,直到host里的睡眠结束。


gfn_to_hva(struct kvm *kvm, gfn_t gfn)
上一篇:中断-整体流程
下一篇:KVM+QEMU世界中的pci总线与virtio总线