博主按:这篇博文被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)