Re: [PATCH v3 1/2] KVM: TDX: Allow userspace to return errors to guest for MAPGPA

From: Michael Roth

Date: Tue Feb 17 2026 - 13:20:20 EST


On Fri, Feb 06, 2026 at 10:28:28PM +0000, Sagi Shahar wrote:
> From: Vishal Annapurve <vannapurve@xxxxxxxxxx>
>
> MAPGPA request from TDX VMs gets split into chunks by KVM using a loop
> of userspace exits until the complete range is handled.
>
> In some cases userspace VMM might decide to break the MAPGPA operation
> and continue it later. For example: in the case of intrahost migration
> userspace might decide to continue the MAPGPA operation after the
> migration is completed.
>
> Allow userspace to signal to TDX guests that the MAPGPA operation should
> be retried the next time the guest is scheduled.
>
> This is potentially a breaking change since if userspace sets
> hypercall.ret to a value other than EBUSY or EINVAL an EINVAL error code
> will be returned to userspace. As of now QEMU never sets hypercall.ret
> to a non-zero value after handling KVM_EXIT_HYPERCALL so this change
> should be safe.
>
> Signed-off-by: Vishal Annapurve <vannapurve@xxxxxxxxxx>
> Co-developed-by: Sagi Shahar <sagis@xxxxxxxxxx>
> Signed-off-by: Sagi Shahar <sagis@xxxxxxxxxx>
> ---
> Documentation/virt/kvm/api.rst | 3 +++
> arch/x86/kvm/vmx/tdx.c | 15 +++++++++++++--
> arch/x86/kvm/x86.h | 6 ++++++
> 3 files changed, 22 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
> index 01a3abef8abb..9978cd9d897e 100644
> --- a/Documentation/virt/kvm/api.rst
> +++ b/Documentation/virt/kvm/api.rst
> @@ -8679,6 +8679,9 @@ block sizes is exposed in KVM_CAP_ARM_SUPPORTED_BLOCK_SIZES as a
>
> This capability, if enabled, will cause KVM to exit to userspace
> with KVM_EXIT_HYPERCALL exit reason to process some hypercalls.
> +Userspace may fail the hypercall by setting hypercall.ret to EINVAL
> +or may request the hypercall to be retried the next time the guest run
> +by setting hypercall.ret to EAGAIN.
>
> Calling KVM_CHECK_EXTENSION for this capability will return a bitmask
> of hypercalls that can be configured to exit to userspace.
> diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
> index 2d7a4d52ccfb..056a44b9d78b 100644
> --- a/arch/x86/kvm/vmx/tdx.c
> +++ b/arch/x86/kvm/vmx/tdx.c
> @@ -1186,10 +1186,21 @@ static void __tdx_map_gpa(struct vcpu_tdx *tdx);
>
> static int tdx_complete_vmcall_map_gpa(struct kvm_vcpu *vcpu)
> {
> + u64 hypercall_ret = READ_ONCE(vcpu->run->hypercall.ret);
> struct vcpu_tdx *tdx = to_tdx(vcpu);
>
> - if (vcpu->run->hypercall.ret) {
> - tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND);
> + if (hypercall_ret) {
> + if (hypercall_ret == EAGAIN) {
> + tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_RETRY);
> + } else if (vcpu->run->hypercall.ret == EINVAL) {
> + tdvmcall_set_return_code(
> + vcpu, TDVMCALL_STATUS_INVALID_OPERAND);
> + } else {
> + WARN_ON_ONCE(
> + kvm_is_valid_map_gpa_range_ret(hypercall_ret));
> + return -EINVAL;
> + }
> +
> tdx->vp_enter_args.r11 = tdx->map_gpa_next;
> return 1;
> }

Maybe slightly more readable?

switch (hypercall_ret) {
case EAGAIN:
tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_RETRY);
/* fallthrough */
case EINVAL:
tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND);
/* fallthrough */
case 0:
break;
case default:
WARN_ON_ONCE(kvm_is_valid_map_gpa_range_ret(hypercall_ret));
return -EINVAL;
}

tdx->vp_enter_args.r11 = tdx->map_gpa_next;
return 1;

Either way:

Reviewed-by: Michael Roth <michael.roth@xxxxxxx>

> diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h
> index fdab0ad49098..3d464d12423a 100644
> --- a/arch/x86/kvm/x86.h
> +++ b/arch/x86/kvm/x86.h
> @@ -706,6 +706,12 @@ int kvm_sev_es_string_io(struct kvm_vcpu *vcpu, unsigned int size,
> unsigned int port, void *data, unsigned int count,
> int in);
>
> +static inline bool kvm_is_valid_map_gpa_range_ret(u64 hypercall_ret)
> +{
> + return !hypercall_ret || hypercall_ret == EINVAL ||
> + hypercall_ret == EAGAIN;
> +}
> +
> static inline bool user_exit_on_hypercall(struct kvm *kvm, unsigned long hc_nr)
> {
> return kvm->arch.hypercall_exit_enabled & BIT(hc_nr);
> --
> 2.53.0.rc2.204.g2597b5adb4-goog
>