Add the in-kernel handling for the VBS secure-plane hypercalls so the plane switch happens without bouncing through userspace: - KVM_HC_VBS_VTL_CALL: the normal plane (plane 0) records the calling-area GPA and switches to the secure plane. While the secure plane is still booting the call is parked (vtl_call_pending) and delivered once the plane parks itself; once ready (vtl_plane_ready) the GPA is delivered directly via kvm_vcpu_switch_plane(). - KVM_HC_VBS_VTL_RETURN: the secure plane parks and hands control back to plane 0, marking itself ready and delivering any pending call. - KVM_HC_VBS_SET_MEM_ATTRS: the secure plane applies cross-plane EPT restrictions to a lower plane via kvm_vm_set_mem_attributes() (rejected from plane 0). Track the per-CPU bootstrap state (vtl_plane_ready, vtl_call_pending, vtl_call_ca) in kvm_vcpu_common and assign the new hypercall numbers KVM_HC_VBS_VTL_RETURN (16) and KVM_HC_VBS_SET_MEM_ATTRS (17). Signed-off-by: Sriram Nambakam --- arch/x86/kvm/x86.c | 139 +++++++++++++++++++++++++++++++++- include/linux/kvm_host.h | 17 +++++ include/uapi/linux/kvm_para.h | 2 + 3 files changed, 156 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index eb82dde62399..3c73ab1dcfe8 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -10588,9 +10588,145 @@ int ____kvm_emulate_hypercall(struct kvm_vcpu *vcpu, int cpl, vcpu->arch.complete_userspace_io = complete_hypercall; return 0; } + case KVM_HC_VBS_VTL_CALL: +#ifdef CONFIG_VM_PLANES + /* + * Runtime VBS/VTL call from the normal world (plane 0) into the + * secure plane. Serviced in-kernel by switching to the secure + * plane (plane 1) — no userspace round trip. This is + * arch-neutral: it works for both Intel (VMX) and AMD (SVM), and + * mirrors the SEV-SNP in-kernel VMPL switch. a0 carries the + * guest-physical address of the shared calling area. + * + * Two cases: + * - Secure plane already booted and parked in its dispatch loop + * (vtl_plane_ready): deliver the calling-area GPA directly in + * RAX (its pending VTL return value) and switch to it. + * - Secure plane not booted yet (bootstrap): record the call as + * pending and switch to the secure plane so it boots; it will + * pick up the pending GPA when it reaches its first VTL return. + * + * If there is no secure plane configured at all, fall through to + * the userspace path so QEMU can service the call. + */ + if (vcpu->plane_level == 0) { + struct kvm_vcpu_common *common = vcpu->common; + struct kvm_vcpu *secure = common->vcpus[1]; + + if (secure) { + common->vtl_call_ca = a0; + + if (common->vtl_plane_ready) { + /* Parked in vtl_return: deliver now. */ + kvm_rax_write(secure, a0); + common->vtl_call_pending = false; + } else { + /* Still booting: deliver on readiness. */ + common->vtl_call_pending = true; + } + + if (kvm_vcpu_switch_plane(vcpu, secure) == 1) { + ret = 0; + goto out; + } + ret = -KVM_EINVAL; + goto out; + } + } +#endif /* CONFIG_VM_PLANES */ + goto vtl_userspace_exit; + case KVM_HC_VBS_VTL_RETURN: +#ifdef CONFIG_VM_PLANES + /* + * The secure plane (plane >0) hands control back to plane 0 + * in-kernel. This covers three situations: + * - Bootstrap "ready": the secure plane has just booted and is + * issuing its first VTL return to announce it is parked. + * - Normal completion: it has finished servicing a VTL call; + * the result is already in the shared calling area. + * - A call that arrived while the secure plane was still booting + * is now delivered (vtl_call_pending) by returning its + * calling-area GPA in RAX and keeping the secure plane running. + * a0 is an optional status carried for tracing only. + */ + if (vcpu->plane_level == 0) { + ret = -KVM_EPERM; + goto out; + } else { + struct kvm_vcpu_common *common = vcpu->common; + + common->vtl_plane_ready = true; + + if (common->vtl_call_pending) { + /* + * Deliver the call that triggered the secure + * plane's boot: return its calling-area GPA and + * stay in the secure plane to service it. The + * GPA is delivered as this hypercall's return + * value (RAX) via the normal completion path; do + * not write RAX directly here, as the completion + * handler would overwrite it with hypercall.ret. + */ + common->vtl_call_pending = false; + ret = common->vtl_call_ca; + goto out; + } + + if (kvm_vcpu_switch_plane(vcpu, common->vcpus[0]) == 1) { + ret = 0; + goto out; + } + } +#endif /* CONFIG_VM_PLANES */ + ret = -KVM_EINVAL; + goto out; + case KVM_HC_VBS_SET_MEM_ATTRS: +#if defined(CONFIG_VM_PLANES) && defined(CONFIG_KVM_GENERIC_MEMORY_ATTRIBUTES) + /* + * The secure plane (plane >0) enforces EPT permissions on the + * normal plane's memory. It cannot issue the host + * KVM_SET_MEMORY_ATTRIBUTES ioctl, so it asks KVM to do it via + * this hypercall. Only a higher-privilege plane may call it. + * + * a0 = guest-physical address (page aligned) + * a1 = region size in bytes (page aligned) + * a2 = access bits to retain for lower planes: + * bit0 read (implicit), bit1 write, bit2 exec + * (matches VBS_MEM_READ/WRITE/EXEC) + */ + if (vcpu->plane_level == 0) { + ret = -KVM_EPERM; + goto out; + } + + if (!PAGE_ALIGNED(a0) || !PAGE_ALIGNED(a1) || a1 == 0 || + a0 + a1 < a0) { + ret = -KVM_EINVAL; + goto out; + } else { + unsigned long attrs = 0; + gfn_t start = a0 >> PAGE_SHIFT; + gfn_t end = (a0 + a1) >> PAGE_SHIFT; + + if (!(a2 & BIT(1))) + attrs |= KVM_MEMORY_ATTRIBUTE_NO_WRITE; + if (!(a2 & BIT(2))) + attrs |= KVM_MEMORY_ATTRIBUTE_NO_EXEC; + + if (kvm_vm_set_mem_attributes(vcpu->kvm, start, end, + attrs)) + ret = -KVM_EINVAL; + else + ret = 0; + goto out; + } +#else + ret = -KVM_ENOSYS; + goto out; +#endif /* CONFIG_VM_PLANES && CONFIG_KVM_GENERIC_MEMORY_ATTRIBUTES */ case KVM_HC_VM_PLANES_CONFIG: case KVM_HC_VM_PLANES_ACTIVATE: - case KVM_HC_VBS_VTL_CALL: { + vtl_userspace_exit: ret = -KVM_ENOSYS; if (!user_exit_on_hypercall(vcpu->kvm, nr)) break; @@ -10609,7 +10745,6 @@ int ____kvm_emulate_hypercall(struct kvm_vcpu *vcpu, int cpl, WARN_ON_ONCE(vcpu->run->hypercall.flags & KVM_EXIT_HYPERCALL_MBZ); vcpu->arch.complete_userspace_io = complete_hypercall; return 0; - } default: ret = -KVM_ENOSYS; break; diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index c6cf2b6c0076..f14d78fd8cd3 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -386,6 +386,23 @@ struct kvm_vcpu_common { bool plane_switch; +#ifdef CONFIG_VM_PLANES + /* + * VBS/VTL secure-plane bootstrap state (per logical CPU). + * + * @vtl_plane_ready: the secure plane has booted and parked itself in + * its dispatch loop (issued its first VTL return). + * @vtl_call_pending: a normal-plane VTL call has been registered but + * not yet delivered to the secure plane (used while + * the secure plane is still booting). + * @vtl_call_ca: guest-physical address of the pending call's + * shared calling area. + */ + bool vtl_plane_ready; + bool vtl_call_pending; + u64 vtl_call_ca; +#endif + struct kvm_vcpu_arch_common arch; }; diff --git a/include/uapi/linux/kvm_para.h b/include/uapi/linux/kvm_para.h index 1703238952fb..eec4fce6b33a 100644 --- a/include/uapi/linux/kvm_para.h +++ b/include/uapi/linux/kvm_para.h @@ -33,6 +33,8 @@ #define KVM_HC_VM_PLANES_CONFIG 13 #define KVM_HC_VM_PLANES_ACTIVATE 14 #define KVM_HC_VBS_VTL_CALL 15 +#define KVM_HC_VBS_VTL_RETURN 16 +#define KVM_HC_VBS_SET_MEM_ATTRS 17 /* * hypercalls use architecture specific -- 2.55.0