* [PATCH v3 0/2] Extend KVM_HC_MAP_GPA_RANGE api to allow retry
@ 2026-02-06 22:28 Sagi Shahar
2026-02-06 22:28 ` [PATCH v3 1/2] KVM: TDX: Allow userspace to return errors to guest for MAPGPA Sagi Shahar
2026-02-06 22:28 ` [PATCH v3 2/2] KVM: SEV: Restrict userspace return codes for KVM_HC_MAP_GPA_RANGE Sagi Shahar
0 siblings, 2 replies; 9+ messages in thread
From: Sagi Shahar @ 2026-02-06 22:28 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Dave Hansen, Kiryl Shutsemau,
Rick Edgecombe
Cc: Thomas Gleixner, Borislav Petkov, H. Peter Anvin, Michael Roth,
Tom Lendacky, x86, kvm, linux-kernel, linux-coco, Sagi Shahar
In some cases, userspace might decide to split MAP_GPA requests and
retry them the next time the guest runs. One common case is MAP_GPA
requests received right before intrahost migration when userspace
might decide to complete the request after the migration is complete
to reduce blackout time.
This is v3 of the series, v1[1] and v2[2] were posted as standalone
patches.
Changes from v2:
* Rebased on top of v6.19-rc8.
* Updated documentation.
* Restricted SNP error codes to match TDX restrictions.
[1] https://lore.kernel.org/kvm/20260114003015.1386066-1-sagis@google.com/
[2] https://lore.kernel.org/lkml/20260115225238.2837449-1-sagis@google.com/
Sagi Shahar (1):
KVM: SEV: Restrict userspace return codes for KVM_HC_MAP_GPA_RANGE
Vishal Annapurve (1):
KVM: TDX: Allow userspace to return errors to guest for MAPGPA
Documentation/virt/kvm/api.rst | 3 +++
arch/x86/kvm/svm/sev.c | 12 ++++++++++--
arch/x86/kvm/vmx/tdx.c | 15 +++++++++++++--
arch/x86/kvm/x86.h | 6 ++++++
4 files changed, 32 insertions(+), 4 deletions(-)
--
2.53.0.rc2.204.g2597b5adb4-goog
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3 1/2] KVM: TDX: Allow userspace to return errors to guest for MAPGPA
2026-02-06 22:28 [PATCH v3 0/2] Extend KVM_HC_MAP_GPA_RANGE api to allow retry Sagi Shahar
@ 2026-02-06 22:28 ` Sagi Shahar
2026-02-17 18:05 ` Michael Roth
2026-02-06 22:28 ` [PATCH v3 2/2] KVM: SEV: Restrict userspace return codes for KVM_HC_MAP_GPA_RANGE Sagi Shahar
1 sibling, 1 reply; 9+ messages in thread
From: Sagi Shahar @ 2026-02-06 22:28 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Dave Hansen, Kiryl Shutsemau,
Rick Edgecombe
Cc: Thomas Gleixner, Borislav Petkov, H. Peter Anvin, Michael Roth,
Tom Lendacky, x86, kvm, linux-kernel, linux-coco,
Vishal Annapurve, Sagi Shahar
From: Vishal Annapurve <vannapurve@google.com>
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@google.com>
Co-developed-by: Sagi Shahar <sagis@google.com>
Signed-off-by: Sagi Shahar <sagis@google.com>
---
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;
}
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
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 2/2] KVM: SEV: Restrict userspace return codes for KVM_HC_MAP_GPA_RANGE
2026-02-06 22:28 [PATCH v3 0/2] Extend KVM_HC_MAP_GPA_RANGE api to allow retry Sagi Shahar
2026-02-06 22:28 ` [PATCH v3 1/2] KVM: TDX: Allow userspace to return errors to guest for MAPGPA Sagi Shahar
@ 2026-02-06 22:28 ` Sagi Shahar
2026-02-17 18:19 ` Michael Roth
1 sibling, 1 reply; 9+ messages in thread
From: Sagi Shahar @ 2026-02-06 22:28 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Dave Hansen, Kiryl Shutsemau,
Rick Edgecombe
Cc: Thomas Gleixner, Borislav Petkov, H. Peter Anvin, Michael Roth,
Tom Lendacky, x86, kvm, linux-kernel, linux-coco, Sagi Shahar
To align with the updated TDX api that allows userspace to request
that guests retry MAP_GPA operations, make sure that userspace is only
returning EINVAL or EAGAIN as possible error codes.
Signed-off-by: Sagi Shahar <sagis@google.com>
---
arch/x86/kvm/svm/sev.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index f59c65abe3cf..5f78e4c3eb5d 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -3722,9 +3722,13 @@ static int snp_rmptable_psmash(kvm_pfn_t pfn)
static int snp_complete_psc_msr(struct kvm_vcpu *vcpu)
{
+ u64 hypercall_ret = READ_ONCE(vcpu->run->hypercall.ret);
struct vcpu_svm *svm = to_svm(vcpu);
- if (vcpu->run->hypercall.ret)
+ if (!kvm_is_valid_map_gpa_range_ret(hypercall_ret))
+ return -EINVAL;
+
+ if (hypercall_ret)
set_ghcb_msr(svm, GHCB_MSR_PSC_RESP_ERROR);
else
set_ghcb_msr(svm, GHCB_MSR_PSC_RESP);
@@ -3815,10 +3819,14 @@ static void __snp_complete_one_psc(struct vcpu_svm *svm)
static int snp_complete_one_psc(struct kvm_vcpu *vcpu)
{
+ u64 hypercall_ret = READ_ONCE(vcpu->run->hypercall.ret);
struct vcpu_svm *svm = to_svm(vcpu);
struct psc_buffer *psc = svm->sev_es.ghcb_sa;
- if (vcpu->run->hypercall.ret) {
+ if (!kvm_is_valid_map_gpa_range_ret(hypercall_ret))
+ return -EINVAL;
+
+ if (hypercall_ret) {
snp_complete_psc(svm, VMGEXIT_PSC_ERROR_GENERIC);
return 1; /* resume guest */
}
--
2.53.0.rc2.204.g2597b5adb4-goog
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v3 1/2] KVM: TDX: Allow userspace to return errors to guest for MAPGPA
2026-02-06 22:28 ` [PATCH v3 1/2] KVM: TDX: Allow userspace to return errors to guest for MAPGPA Sagi Shahar
@ 2026-02-17 18:05 ` Michael Roth
2026-02-17 18:45 ` Tom Lendacky
0 siblings, 1 reply; 9+ messages in thread
From: Michael Roth @ 2026-02-17 18:05 UTC (permalink / raw)
To: Sagi Shahar
Cc: Sean Christopherson, Paolo Bonzini, Dave Hansen, Kiryl Shutsemau,
Rick Edgecombe, Thomas Gleixner, Borislav Petkov, H. Peter Anvin,
Tom Lendacky, x86, kvm, linux-kernel, linux-coco,
Vishal Annapurve
On Fri, Feb 06, 2026 at 10:28:28PM +0000, Sagi Shahar wrote:
> From: Vishal Annapurve <vannapurve@google.com>
>
> 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@google.com>
> Co-developed-by: Sagi Shahar <sagis@google.com>
> Signed-off-by: Sagi Shahar <sagis@google.com>
> ---
> 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@amd.com>
> 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
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3 2/2] KVM: SEV: Restrict userspace return codes for KVM_HC_MAP_GPA_RANGE
2026-02-06 22:28 ` [PATCH v3 2/2] KVM: SEV: Restrict userspace return codes for KVM_HC_MAP_GPA_RANGE Sagi Shahar
@ 2026-02-17 18:19 ` Michael Roth
0 siblings, 0 replies; 9+ messages in thread
From: Michael Roth @ 2026-02-17 18:19 UTC (permalink / raw)
To: Sagi Shahar
Cc: Sean Christopherson, Paolo Bonzini, Dave Hansen, Kiryl Shutsemau,
Rick Edgecombe, Thomas Gleixner, Borislav Petkov, H. Peter Anvin,
Tom Lendacky, x86, kvm, linux-kernel, linux-coco
On Fri, Feb 06, 2026 at 10:28:29PM +0000, Sagi Shahar wrote:
> To align with the updated TDX api that allows userspace to request
> that guests retry MAP_GPA operations, make sure that userspace is only
> returning EINVAL or EAGAIN as possible error codes.
>
> Signed-off-by: Sagi Shahar <sagis@google.com>
Reviewed-by: Michael Roth <michael.roth@amd.com>
> ---
> arch/x86/kvm/svm/sev.c | 12 ++++++++++--
> 1 file changed, 10 insertions(+), 2 deletions(-)
>
> diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
> index f59c65abe3cf..5f78e4c3eb5d 100644
> --- a/arch/x86/kvm/svm/sev.c
> +++ b/arch/x86/kvm/svm/sev.c
> @@ -3722,9 +3722,13 @@ static int snp_rmptable_psmash(kvm_pfn_t pfn)
>
> static int snp_complete_psc_msr(struct kvm_vcpu *vcpu)
> {
> + u64 hypercall_ret = READ_ONCE(vcpu->run->hypercall.ret);
> struct vcpu_svm *svm = to_svm(vcpu);
>
> - if (vcpu->run->hypercall.ret)
> + if (!kvm_is_valid_map_gpa_range_ret(hypercall_ret))
> + return -EINVAL;
> +
> + if (hypercall_ret)
> set_ghcb_msr(svm, GHCB_MSR_PSC_RESP_ERROR);
> else
> set_ghcb_msr(svm, GHCB_MSR_PSC_RESP);
> @@ -3815,10 +3819,14 @@ static void __snp_complete_one_psc(struct vcpu_svm *svm)
>
> static int snp_complete_one_psc(struct kvm_vcpu *vcpu)
> {
> + u64 hypercall_ret = READ_ONCE(vcpu->run->hypercall.ret);
> struct vcpu_svm *svm = to_svm(vcpu);
> struct psc_buffer *psc = svm->sev_es.ghcb_sa;
>
> - if (vcpu->run->hypercall.ret) {
> + if (!kvm_is_valid_map_gpa_range_ret(hypercall_ret))
> + return -EINVAL;
> +
> + if (hypercall_ret) {
> snp_complete_psc(svm, VMGEXIT_PSC_ERROR_GENERIC);
> return 1; /* resume guest */
> }
> --
> 2.53.0.rc2.204.g2597b5adb4-goog
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3 1/2] KVM: TDX: Allow userspace to return errors to guest for MAPGPA
2026-02-17 18:05 ` Michael Roth
@ 2026-02-17 18:45 ` Tom Lendacky
2026-02-17 19:16 ` Michael Roth
0 siblings, 1 reply; 9+ messages in thread
From: Tom Lendacky @ 2026-02-17 18:45 UTC (permalink / raw)
To: Michael Roth, Sagi Shahar
Cc: Sean Christopherson, Paolo Bonzini, Dave Hansen, Kiryl Shutsemau,
Rick Edgecombe, Thomas Gleixner, Borislav Petkov, H. Peter Anvin,
x86, kvm, linux-kernel, linux-coco, Vishal Annapurve
On 2/17/26 12:05, Michael Roth wrote:
> On Fri, Feb 06, 2026 at 10:28:28PM +0000, Sagi Shahar wrote:
>> From: Vishal Annapurve <vannapurve@google.com>
>>
>> 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@google.com>
>> Co-developed-by: Sagi Shahar <sagis@google.com>
>> Signed-off-by: Sagi Shahar <sagis@google.com>
>> ---
>> 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 */
I think you want a break here, not a fallthrough, so that you don't set
the return code twice with the last one not being correct for EAGAIN.
Thanks,
Tom
> 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@amd.com>
>
>> 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
>>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3 1/2] KVM: TDX: Allow userspace to return errors to guest for MAPGPA
2026-02-17 18:45 ` Tom Lendacky
@ 2026-02-17 19:16 ` Michael Roth
2026-02-17 19:20 ` Sean Christopherson
0 siblings, 1 reply; 9+ messages in thread
From: Michael Roth @ 2026-02-17 19:16 UTC (permalink / raw)
To: Tom Lendacky
Cc: Sagi Shahar, Sean Christopherson, Paolo Bonzini, Dave Hansen,
Kiryl Shutsemau, Rick Edgecombe, Thomas Gleixner, Borislav Petkov,
H. Peter Anvin, x86, kvm, linux-kernel, linux-coco,
Vishal Annapurve
On Tue, Feb 17, 2026 at 12:45:52PM -0600, Tom Lendacky wrote:
> On 2/17/26 12:05, Michael Roth wrote:
> > On Fri, Feb 06, 2026 at 10:28:28PM +0000, Sagi Shahar wrote:
> >> From: Vishal Annapurve <vannapurve@google.com>
> >>
> >> 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@google.com>
> >> Co-developed-by: Sagi Shahar <sagis@google.com>
> >> Signed-off-by: Sagi Shahar <sagis@google.com>
> >> ---
> >> 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 */
>
> I think you want a break here, not a fallthrough, so that you don't set
> the return code twice with the last one not being correct for EAGAIN.
Doh, thanks for the catch. I guess a break for the EINVAL case as well would
be more consistent then.
switch (hypercall_ret) {
case EAGAIN:
tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_RETRY);
break;
case EINVAL:
tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND);
break;
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;
Thanks,
Mike
> > switch (hypercall_ret) {
> > case EAGAIN:
> > tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_RETRY);
> > /* fallthrough */
>
> I think you want a break here, not a fallthrough, so that you don't set
> the return code twice with the last one not being correct for EAGAIN.
>
> Thanks,
> Tom
>
> > 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;
>
> Thanks,
> Tom
>
> > 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@amd.com>
> >
> >> 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
> >>
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3 1/2] KVM: TDX: Allow userspace to return errors to guest for MAPGPA
2026-02-17 19:16 ` Michael Roth
@ 2026-02-17 19:20 ` Sean Christopherson
2026-03-05 22:27 ` Sagi Shahar
0 siblings, 1 reply; 9+ messages in thread
From: Sean Christopherson @ 2026-02-17 19:20 UTC (permalink / raw)
To: Michael Roth
Cc: Tom Lendacky, Sagi Shahar, Paolo Bonzini, Dave Hansen,
Kiryl Shutsemau, Rick Edgecombe, Thomas Gleixner, Borislav Petkov,
H. Peter Anvin, x86, kvm, linux-kernel, linux-coco,
Vishal Annapurve
On Tue, Feb 17, 2026, Michael Roth wrote:
> On Tue, Feb 17, 2026 at 12:45:52PM -0600, Tom Lendacky wrote:
> > On 2/17/26 12:05, Michael Roth wrote:
> > >> 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 */
> >
> > I think you want a break here, not a fallthrough, so that you don't set
> > the return code twice with the last one not being correct for EAGAIN.
>
> Doh, thanks for the catch. I guess a break for the EINVAL case as well would
> be more consistent then.
>
> switch (hypercall_ret) {
> case EAGAIN:
> tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_RETRY);
> break;
> case EINVAL:
> tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND);
> break;
> 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;
Heh, except then KVM will fail to handle the next chunk on success. I like the
idea of a switch statement, so what if we add that and dedup the error handling?
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);
long rc;
switch (hypercall_ret) {
case 0:
break;
case EAGAIN:
rc = TDVMCALL_STATUS_RETRY;
goto propagate_error;
case EINVAL:
rc = TDVMCALL_STATUS_INVALID_OPERAND;
goto propagate_error;
default:
WARN_ON_ONCE(kvm_is_valid_map_gpa_range_ret(hypercall_ret));
return -EINVAL;
}
tdx->map_gpa_next += TDX_MAP_GPA_MAX_LEN;
if (tdx->map_gpa_next >= tdx->map_gpa_end)
return 1;
/*
* Stop processing the remaining part if there is a pending interrupt,
* which could be qualified to deliver. Skip checking pending RVI for
* TDVMCALL_MAP_GPA, see comments in tdx_protected_apic_has_interrupt().
*/
if (kvm_vcpu_has_events(vcpu)) {
rc = TDVMCALL_STATUS_RETRY;
goto propagate_error;
}
__tdx_map_gpa(tdx);
return 0;
propagate_error:
tdvmcall_set_return_code(vcpu, rc);
tdx->vp_enter_args.r11 = tdx->map_gpa_next;
return 1;
}
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3 1/2] KVM: TDX: Allow userspace to return errors to guest for MAPGPA
2026-02-17 19:20 ` Sean Christopherson
@ 2026-03-05 22:27 ` Sagi Shahar
0 siblings, 0 replies; 9+ messages in thread
From: Sagi Shahar @ 2026-03-05 22:27 UTC (permalink / raw)
To: Sean Christopherson
Cc: Michael Roth, Tom Lendacky, Paolo Bonzini, Dave Hansen,
Kiryl Shutsemau, Rick Edgecombe, Thomas Gleixner, Borislav Petkov,
H. Peter Anvin, x86, kvm, linux-kernel, linux-coco,
Vishal Annapurve
On Tue, Feb 17, 2026 at 1:20 PM Sean Christopherson <seanjc@google.com> wrote:
>
> On Tue, Feb 17, 2026, Michael Roth wrote:
> > On Tue, Feb 17, 2026 at 12:45:52PM -0600, Tom Lendacky wrote:
> > > On 2/17/26 12:05, Michael Roth wrote:
> > > >> 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 */
> > >
> > > I think you want a break here, not a fallthrough, so that you don't set
> > > the return code twice with the last one not being correct for EAGAIN.
> >
> > Doh, thanks for the catch. I guess a break for the EINVAL case as well would
> > be more consistent then.
> >
> > switch (hypercall_ret) {
> > case EAGAIN:
> > tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_RETRY);
> > break;
> > case EINVAL:
> > tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND);
> > break;
> > 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;
>
> Heh, except then KVM will fail to handle the next chunk on success. I like the
> idea of a switch statement, so what if we add that and dedup the error handling?
>
> 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);
> long rc;
>
> switch (hypercall_ret) {
> case 0:
> break;
> case EAGAIN:
> rc = TDVMCALL_STATUS_RETRY;
> goto propagate_error;
> case EINVAL:
> rc = TDVMCALL_STATUS_INVALID_OPERAND;
> goto propagate_error;
> default:
> WARN_ON_ONCE(kvm_is_valid_map_gpa_range_ret(hypercall_ret));
> return -EINVAL;
> }
>
> tdx->map_gpa_next += TDX_MAP_GPA_MAX_LEN;
> if (tdx->map_gpa_next >= tdx->map_gpa_end)
> return 1;
>
> /*
> * Stop processing the remaining part if there is a pending interrupt,
> * which could be qualified to deliver. Skip checking pending RVI for
> * TDVMCALL_MAP_GPA, see comments in tdx_protected_apic_has_interrupt().
> */
> if (kvm_vcpu_has_events(vcpu)) {
> rc = TDVMCALL_STATUS_RETRY;
> goto propagate_error;
> }
>
> __tdx_map_gpa(tdx);
> return 0;
>
> propagate_error:
> tdvmcall_set_return_code(vcpu, rc);
> tdx->vp_enter_args.r11 = tdx->map_gpa_next;
> return 1;
> }
Thanks for the review. I updated the code and sent out v4 for review.
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-03-05 22:28 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-06 22:28 [PATCH v3 0/2] Extend KVM_HC_MAP_GPA_RANGE api to allow retry Sagi Shahar
2026-02-06 22:28 ` [PATCH v3 1/2] KVM: TDX: Allow userspace to return errors to guest for MAPGPA Sagi Shahar
2026-02-17 18:05 ` Michael Roth
2026-02-17 18:45 ` Tom Lendacky
2026-02-17 19:16 ` Michael Roth
2026-02-17 19:20 ` Sean Christopherson
2026-03-05 22:27 ` Sagi Shahar
2026-02-06 22:28 ` [PATCH v3 2/2] KVM: SEV: Restrict userspace return codes for KVM_HC_MAP_GPA_RANGE Sagi Shahar
2026-02-17 18:19 ` Michael Roth
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox