kvm_emulate_hypercall代码解析()

功能概述

kvm_emulate_hypercall 是 KVM(Kernel-based Virtual Machine)中处理虚拟机发起的 超级调用(Hypercall) 的核心函数。Hypercall 是虚拟机(Guest)向宿主机(Host)请求特权操作的机制,类似于系统调用(但针对虚拟化场景)。

csv_init
    ops->handle_exit = csv_handle_exit;
vcpu_run
    vcpu_enter_guest
		svm_x86_ops
		    handle_exit
		        svm_invoke_exit_handler
		            svm_exit_handlers
		                vmmcall_interception
		                    kvm_emulate_hypercall
		                        kvm_pv_psp_copy_forward_op

输入参数

  • struct kvm_vcpu *vcpu:表示当前发起 Hypercall 的虚拟 CPU(vCPU)。

执行流程

1. Hyper-V 兼容性检查
if (kvm_hv_hypercall_enabled(vcpu->kvm))
    return kvm_hv_hypercall(vcpu);
  • 如果启用了 Hyper-V 兼容模式,直接调用 Hyper-V 的 Hypercall 处理函数。

2. 读取寄存器参数

从 vCPU 的寄存器中读取 Hypercall 的编号和参数:

nr = kvm_rax_read(vcpu); // Hypercall 编号
a0 = kvm_rbx_read(vcpu); // 参数 0
a1 = kvm_rcx_read(vcpu); // 参数 1
a2 = kvm_rdx_read(vcpu); // 参数 2
a3 = kvm_rsi_read(vcpu); // 参数 3
  • x86 约定:Hypercall 编号通过 RAX 传递,参数通过 RBX, RCX, RDX, RSI 传递。

3. 模式检查(32/64 位)
op_64_bit = is_64_bit_hypercall(vcpu);
if (!op_64_bit) {
    nr &= 0xFFFFFFFF; // 截断为 32 位
    a0 &= 0xFFFFFFFF;
    a1 &= 0xFFFFFFFF;
    a2 &= 0xFFFFFFFF;
    a3 &= 0xFFFFFFFF;
}
  • 根据 vCPU 模式(32/64 位)决定是否截断参数的高 32 位。

4. 特权级检查
if (kvm_x86_ops.get_cpl(vcpu) != 0 && ...) {
    ret = -KVM_EPERM;
    goto out;
}
  • CPL(Current Privilege Level) 必须为 0(内核态),否则返回权限错误(-EPERM)。
  • 例外:允许 KVM_HC_VM_ATTESTATION 等特定 Hypercall 在非特权级调用。

5. Hypercall 分发与处理

通过 switch (nr) 分发到具体操作:

  • 典型 Hypercall 示例
    • KVM_HC_VAPIC_POLL_IRQ: 虚拟中断轮询(直接返回成功)。
    • KVM_HC_KICK_CPU: 唤醒指定 vCPU(需启用 KVM_FEATURE_PV_UNHALT)。
    • KVM_HC_CLOCK_PAIRING: 时钟同步(仅 x86_64)。
    • KVM_HC_MAP_GPA_RANGE: 映射 Guest 物理地址范围(需用户空间处理)。

6. 用户空间处理(以 MAP_GPA_RANGE 为例)
vcpu->run->exit_reason = KVM_EXIT_HYPERCALL;
vcpu->run->hypercall.nr = KVM_HC_MAP_GPA_RANGE;
...
vcpu->arch.complete_userspace_io = complete_hypercall_exit;
return 0;
  • 设置 KVM_EXIT_HYPERCALL 退出原因,并将参数传递给用户空间。
  • 用户空间处理完成后,通过 complete_hypercall_exit 回调恢复执行。

7. 返回值处理
out:
    if (!op_64_bit)
        ret = (u32)ret; // 32 位模式下截断返回值
    kvm_rax_write(vcpu, ret); // 结果写入 RAX
    ++vcpu->stat.hypercalls; // 统计计数器
    return kvm_skip_emulated_instruction(vcpu); // 跳过当前指令
  • 结果通过 RAX 返回给 Guest。
  • 更新 Hypercall 调用统计。

关键设计点

  1. 兼容性

    • 支持 Hyper-V 和 KVM 原生 Hypercall。
    • 区分 32/64 位模式。
  2. 安全性

    • 特权级(CPL)检查防止非法调用。
    • 参数合法性验证(如地址对齐)。
  3. 扩展性

    • 通过 kvm_x86_ops 抽象架构相关操作(如 vm_attestation)。
    • 允许用户空间处理复杂 Hypercall(如内存映射)。

应用场景

  • 虚拟中断管理:唤醒其他 vCPU(KICK_CPU)。
  • 时钟同步:保证 Guest 与 Host 时间一致。
  • 安全认证:虚拟机证明(VM_ATTESTATION)。
  • 内存管理:动态调整 Guest 物理地址映射。

总结

kvm_emulate_hypercall 是 KVM 实现 Hypercall 的核心分发逻辑,结合特权检查、模式适配和用户空间协作,为虚拟机提供了与宿主机交互的安全通道。

int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) { unsigned long nr, a0, a1, a2, a3, ret; int op_64_bit; if (kvm_hv_hypercall_enabled(vcpu->kvm)) return kvm_hv_hypercall(vcpu); nr = kvm_rax_read(vcpu); a0 = kvm_rbx_read(vcpu); a1 = kvm_rcx_read(vcpu); a2 = kvm_rdx_read(vcpu); a3 = kvm_rsi_read(vcpu); trace_kvm_hypercall(nr, a0, a1, a2, a3); op_64_bit = is_64_bit_hypercall(vcpu); if (!op_64_bit) { nr &= 0xFFFFFFFF; a0 &= 0xFFFFFFFF; a1 &= 0xFFFFFFFF; a2 &= 0xFFFFFFFF; a3 &= 0xFFFFFFFF; } if (kvm_x86_ops.get_cpl(vcpu) != 0 && !(nr == KVM_HC_VM_ATTESTATION || nr == KVM_HC_PSP_OP_OBSOLETE || nr == KVM_HC_PSP_COPY_FORWARD_OP || nr == KVM_HC_PSP_FORWARD_OP)) { ret = -KVM_EPERM; goto out; } ret = -KVM_ENOSYS; switch (nr) { case KVM_HC_VAPIC_POLL_IRQ: ret = 0; break; case KVM_HC_KICK_CPU: if (!guest_pv_has(vcpu, KVM_FEATURE_PV_UNHALT)) break; kvm_pv_kick_cpu_op(vcpu->kvm, a0, a1); kvm_sched_yield(vcpu->kvm, a1); ret = 0; break; #ifdef CONFIG_X86_64 case KVM_HC_CLOCK_PAIRING: ret = kvm_pv_clock_pairing(vcpu, a0, a1); break; #endif case KVM_HC_SEND_IPI: if (!guest_pv_has(vcpu, KVM_FEATURE_PV_SEND_IPI)) break; ret = kvm_pv_send_ipi(vcpu->kvm, a0, a1, a2, a3, op_64_bit); break; case KVM_HC_SCHED_YIELD: if (!guest_pv_has(vcpu, KVM_FEATURE_PV_SCHED_YIELD)) break; kvm_sched_yield(vcpu->kvm, a0); ret = 0; break; case KVM_HC_MAP_GPA_RANGE: { u64 gpa = a0, npages = a1, attrs = a2; ret = -KVM_ENOSYS; if (!(vcpu->kvm->arch.hypercall_exit_enabled & (1 << KVM_HC_MAP_GPA_RANGE))) break; if (!PAGE_ALIGNED(gpa) || !npages || gpa_to_gfn(gpa) + npages <= gpa_to_gfn(gpa)) { ret = -KVM_EINVAL; break; } vcpu->run->exit_reason = KVM_EXIT_HYPERCALL; vcpu->run->hypercall.nr = KVM_HC_MAP_GPA_RANGE; vcpu->run->hypercall.args[0] = gpa; vcpu->run->hypercall.args[1] = npages; vcpu->run->hypercall.args[2] = attrs; vcpu->run->hypercall.longmode = op_64_bit; vcpu->arch.complete_userspace_io = complete_hypercall_exit; return 0; } case KVM_HC_VM_ATTESTATION: ret = -KVM_ENOSYS; if (kvm_x86_ops.vm_attestation) ret = kvm_x86_ops.vm_attestation(vcpu->kvm, a0, a1); break; case KVM_HC_PSP_OP_OBSOLETE: case KVM_HC_PSP_COPY_FORWARD_OP: case KVM_HC_PSP_FORWARD_OP: ret = -KVM_ENOSYS; if (kvm_x86_ops.arch_hypercall) ret = kvm_x86_ops.arch_hypercall(vcpu->kvm, nr, a0, a1, a2, a3); break; default: ret = -KVM_ENOSYS; break; } out: if (!op_64_bit) ret = (u32)ret; kvm_rax_write(vcpu, ret); ++vcpu->stat.hypercalls; return kvm_skip_emulated_instruction(vcpu); } EXPORT_SYMBOL_GPL(kvm_emulate_hypercall);
最新发布
03-18
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值