前回は、
今回は、
Linux KVMのソースコード構成
Linux KVMは、
Linux KVMのソースコード ディレクトリ
Linux KVMのカーネルモジュールは、Linuxカーネルのソースコードツリーのうち、以下のディレクトリに収録されています。- virt/
kvm/ ――アーキテクチャ非依存コード - Linux KVMの実装部分のうち、
APIなど、 ユーザモードから見える部分については、 このディレクトリ以下に収録されています。たとえば、 /dev/ kvmデバイスのインターフェース実装などが含まれます。 - arch/*/kvm/ ――各アーキテクチャ向けコード
-
仮想マシン機能を実現するために、
x86などの各アーキテクチャによって実装が異なるハードウェア固有部分は、 こちらのディレクトリ以下に、 各アーキテクチャごとに収録されています。たとえばx86プロセッサ向けのコードはarch/ x86/ kvm/に見つかります。 Linux KVMはおもにx86で利用できますが、
実際にはその他のアーキテクチャにも対応しています。現時点でLinux KVMがポートされているアーキテクチャについて整理すると、 下記のとおりとなっています。 - x86
(x86アーキテクチャ 32ビット, 64ビット) - ia64
(IA-64アーキテクチャ) - s390
(S/ 390アーキテクチャ) - ppc
(Power PCアーキテクチャ)
- x86
x86プロセッサ向け仮想化支援機能の抽象化
x86プロセッサに限って、
このため、
もっとも、
表1 おもなx86プロセッサの仮想化支援対応コード
ソースコード | 説明 |
---|---|
x86. | x86アーキテクチャサポート(Intel VT/ |
vmx. | Intel VTサポート |
svm. | AMD-Vサポート |
Linux KVMからのIntel VTの利用
では、
VMCSの生成
Intel VT-xでは、

VMCSは、
1569 static struct vmcs *alloc_vmcs_cpu(int cpu)
1570 {
1571 int node = cpu_to_node(cpu);
1572 struct page *pages;
1573 struct vmcs *vmcs;
1574
1575 pages = alloc_pages_exact_node(node, GFP_KERNEL, vmcs_config.order);
1576 if (!pages)
1577 return NULL;
1578 vmcs = page_address(pages);
1579 memset(vmcs, 0, vmcs_config.size);
1580 vmcs->revision_id = vmcs_config.revision_id; /* vmcs revision id */
1581 return vmcs;
1582 }
さらに、
論理プロセッサの実行
プロセッサの初期化が終わったとして、
論理プロセッサへのモード遷移を行うにあたっての最初の処理は、
セグメントレジスタなど一部のレジスタはIntel VT-xの機能を用いてステート保存しますが、
論理プロセッサのステートは、
Intel VT-xを使った論理プロセッサへのモード遷移処理はvmx_
3965 static void vmx_vcpu_run(struct kvm_vcpu *vcpu)
3966 {
3967 struct vcpu_vmx *vmx = to_vmx(vcpu);
《中略》
3991 asm(
3992 /* Store host registers */
3993 "push %%"R"dx; push %%"R"bp;"
3994 "push %%"R"cx \n\t"
3995 "cmp %%"R"sp, %c[host_rsp](%0) \n\t"
3996 "je 1f \n\t"
3997 "mov %%"R"sp, %c[host_rsp](%0) \n\t"
3998 __ex(ASM_VMX_VMWRITE_RSP_RDX) "\n\t"
3999 "1: \n\t"
4000 /* Reload cr2 if changed */
4001 "mov %c[cr2](%0), %%"R"ax \n\t"
4002 "mov %%cr2, %%"R"dx \n\t"
4003 "cmp %%"R"ax, %%"R"dx \n\t"
4004 "je 2f \n\t"
4005 "mov %%"R"ax, %%cr2 \n\t"
4006 "2: \n\t"
4007 /* Check if vmlaunch of vmresume is needed */
4008 "cmpl $0, %c[launched](%0) \n\t"
4009 /* Load guest registers. Don't clobber flags. */
4010 "mov %c[rax](%0), %%"R"ax \n\t"
4011 "mov %c[rbx](%0), %%"R"bx \n\t"
4012 "mov %c[rdx](%0), %%"R"dx \n\t"
4013 "mov %c[rsi](%0), %%"R"si \n\t"
4014 "mov %c[rdi](%0), %%"R"di \n\t"
4015 "mov %c[rbp](%0), %%"R"bp \n\t"
4016 #ifdef CONFIG_X86_64
4017 "mov %c[r8](%0), %%r8 \n\t"
4018 "mov %c[r9](%0), %%r9 \n\t"
4019 "mov %c[r10](%0), %%r10 \n\t"
4020 "mov %c[r11](%0), %%r11 \n\t"
4021 "mov %c[r12](%0), %%r12 \n\t"
4022 "mov %c[r13](%0), %%r13 \n\t"
4023 "mov %c[r14](%0), %%r14 \n\t"
4024 "mov %c[r15](%0), %%r15 \n\t"
4025 #endif
4026 "mov %c[rcx](%0), %%"R"cx \n\t" /* kills %0 (ecx) */
4027
準備ができたらVMRESUME命令
4028 /* Enter guest mode */
4029 "jne .Llaunched \n\t"
4030 __ex(ASM_VMX_VMLAUNCH) "\n\t"
4031 "jmp .Lkvm_vmx_return \n\t"
4032 ".Llaunched: " __ex(ASM_VMX_VMRESUME) "\n\t"
何らかの理由により論理プロセッサの実行が終了すると、
4033 ".Lkvm_vmx_return: "
4034 /* Save guest registers, load host registers, keep flags */
4035 "xchg %0, (%%"R"sp) \n\t"
4036 "mov %%"R"ax, %c[rax](%0) \n\t"
4037 "mov %%"R"bx, %c[rbx](%0) \n\t"
4038 "push"Q" (%%"R"sp); pop"Q" %c[rcx](%0) \n\t"
4039 "mov %%"R"dx, %c[rdx](%0) \n\t"
4040 "mov %%"R"si, %c[rsi](%0) \n\t"
4041 "mov %%"R"di, %c[rdi](%0) \n\t"
4042 "mov %%"R"bp, %c[rbp](%0) \n\t"
4043 #ifdef CONFIG_X86_64
4044 "mov %%r8, %c[r8](%0) \n\t"
4045 "mov %%r9, %c[r9](%0) \n\t"
4046 "mov %%r10, %c[r10](%0) \n\t"
4047 "mov %%r11, %c[r11](%0) \n\t"
4048 "mov %%r12, %c[r12](%0) \n\t"
4049 "mov %%r13, %c[r13](%0) \n\t"
4050 "mov %%r14, %c[r14](%0) \n\t"
4051 "mov %%r15, %c[r15](%0) \n\t"
4052 #endif
4053 "mov %%cr2, %%"R"ax \n\t"
4054 "mov %%"R"ax, %c[cr2](%0) \n\t"
4055
4056 "pop %%"R"bp; pop %%"R"bp; pop %%"R"dx \n\t"
4057 "setbe %c[fail](%0) \n\t"
4058 : : "c"(vmx), "d"((unsigned long)HOST_RSP),
4059 [launched]"i"(offsetof(struct vcpu_vmx, launched)),
4060 [fail]"i"(offsetof(struct vcpu_vmx, fail)),
4061 [host_rsp]"i"(offsetof(struct vcpu_vmx, host_rsp)),
4062 [rax]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RAX])),
4063 [rbx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RBX])),
4064 [rcx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RCX])),
4065 [rdx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RDX])),
4066 [rsi]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RSI])),
4067 [rdi]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RDI])),
4068 [rbp]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RBP])),
4069 #ifdef CONFIG_X86_64
4070 [r8]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R8])),
4071 [r9]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R9])),
4072 [r10]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R10])),
4073 [r11]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R11])),
4074 [r12]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R12])),
4075 [r13]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R13])),
4076 [r14]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R14])),
4077 [r15]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R15])),
4078 #endif
4079 [cr2]"i"(offsetof(struct vcpu_vmx, vcpu.arch.cr2))
4080 : "cc", "memory"
4081 , R"ax", R"bx", R"di", R"si"
4082 #ifdef CONFIG_X86_64
4083 , "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
4084 #endif
4085 );
論理プロセッサの実行を終えた後の処理として注目したいのが、
4097 vmx->exit_reason = vmcs_read32(VM_EXIT_REASON);
4098 vmx->exit_intr_info = vmcs_read32(VM_EXIT_INTR_INFO);
《中略》
4103 }
VMEXIT後のイベントディスパッチ
次に、
3756 /*
3757 * The guest has exited. See if we can fix it or if we need userspace
3758 * assistance.
3759 */
3760 static int vmx_handle_exit(struct kvm_vcpu *vcpu)
3761 {
3762 struct vcpu_vmx *vmx = to_vmx(vcpu);
3763 u32 exit_reason = vmx->exit_reason;
3764 u32 vectoring_info = vmx->idt_vectoring_info;
《中略》
3812 if (exit_reason < kvm_vmx_max_exit_handlers
3813 && kvm_vmx_exit_handlers[exit_reason])
3814 return kvm_vmx_exit_handlers[exit_reason](vcpu);
3815 else {
3816 vcpu->run->exit_reason = KVM_EXIT_UNKNOWN;
3817 vcpu->run->hw.hardware_exit_reason = exit_reason;
3818 }
3819 return 0;
3820 }
実際のイベントハンドラはkvm_
3704 /*
3705 * The exit handlers return 1 if the exit was handled fully and guest execution
3706 * may resume. Otherwise they set the kvm_run parameter to indicate what needs
3707 * to be done to userspace and return 0.
3708 */
3709 static int (*kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu) = {
3710 [EXIT_REASON_EXCEPTION_NMI] = handle_exception,
3711 [EXIT_REASON_EXTERNAL_INTERRUPT] = handle_external_interrupt,
3712 [EXIT_REASON_TRIPLE_FAULT] = handle_triple_fault,
3713 [EXIT_REASON_NMI_WINDOW] = handle_nmi_window,
3714 [EXIT_REASON_IO_INSTRUCTION] = handle_io,
3715 [EXIT_REASON_CR_ACCESS] = handle_cr,
3716 [EXIT_REASON_DR_ACCESS] = handle_dr,
3717 [EXIT_REASON_CPUID] = handle_cpuid,
3718 [EXIT_REASON_MSR_READ] = handle_rdmsr,
3719 [EXIT_REASON_MSR_WRITE] = handle_wrmsr,
3720 [EXIT_REASON_PENDING_INTERRUPT] = handle_interrupt_window,
3721 [EXIT_REASON_HLT] = handle_halt,
3722 [EXIT_REASON_INVD] = handle_invd,
3723 [EXIT_REASON_INVLPG] = handle_invlpg,
《中略》
3745 };
VMEXITハンドラの例 ――RDMSR/WRMSRのエミュレーション
Intel VT-xでは、
ここでは、
- MSRの読み取り
(handle_ rdmsr) -
RDMSR命令は、
Machine Specific Register (MSR)の設定値を汎用レジスタにロードするためのリング0の命令です。あらかじめECXレジスタを読み取りたいMSRの番号を設定しておきRDMSR 命令を実行すると、 MSRの上位32ビット分がEDX、 下位32ビット分がEAXにコピーされる、 というものです。 Intel VT-xの論理プロセッサ上でこの命令が実行された場合には、
handle_ rdmsr()関数が呼び出されます。この関数では、 論理プロセッサのECX の値が示すMSRレジスタの値を、 KVMのMSR管理ルーチンから読み出しまて、 取得されたMSRレジスタの値を、 論理プロセッサのEAX, EDXに書き戻し、 「次に実行すべき命令のアドレス」 を指すインストラクションポインタ(IP)を、 RDMSRの命令の長さだけ進める、 という、 命令エミュレーション処理を行っています。このため、 次に論理プロセッサがが実行される際には、 まるでRDMSRが実行された直後のような状態になっており、 次の命令から実行されることになります。
3329 static int handle_rdmsr(struct kvm_vcpu *vcpu)
3330 {
3331 u32 ecx = vcpu->arch.regs[VCPU_REGS_RCX];
3332 u64 data;
3333
3334 if (vmx_get_msr(vcpu, ecx, &data)) {
3335 trace_kvm_msr_read_ex(ecx);
3336 kvm_inject_gp(vcpu, 0);
3337 return 1;
3338 }
3339
3340 trace_kvm_msr_read(ecx, data);
3341
3342 /* FIXME: handling of bits 32:63 of rax, rdx */
3343 vcpu->arch.regs[VCPU_REGS_RAX] = data & -1u;
3344 vcpu->arch.regs[VCPU_REGS_RDX] = (data >> 32) & -1u;
3345 skip_emulated_instruction(vcpu);
3346 return 1;
3347 }
- MSRの書き込み
(handle_ wrmsr) - handle_
rdmsr()の仕組みを理解した後であれば、 handle_ wrmsr()関数もすぐに理解できるでしょう。こちらはECXの値が指すMSRに対してEDX:EAXの64ビット値を書き込み、 次の命令から論理プロセッサの実行を再開するという仕組みです。
3349 static int handle_wrmsr(struct kvm_vcpu *vcpu)
3350 {
3351 u32 ecx = vcpu->arch.regs[VCPU_REGS_RCX];
3352 u64 data = (vcpu->arch.regs[VCPU_REGS_RAX] & -1u)
3353 | ((u64)(vcpu->arch.regs[VCPU_REGS_RDX] & -1u) << 32);
3354
3355 if (vmx_set_msr(vcpu, ecx, data) != 0) {
3356 trace_kvm_msr_write_ex(ecx, data);
3357 kvm_inject_gp(vcpu, 0);
3358 return 1;
3359 }
3360
3361 trace_kvm_msr_write(ecx, data);
3362 skip_emulated_instruction(vcpu);
3363 return 1;
3364 }
ここまではLinux KVMのソースコードを読んできましたが、