From: Peter Fang Provide an in-kernel path for TDX Quote generation when handling TDG.VP.VMCALL, without requiring an exit to userspace. Use the core TDX API when the TDX Quoting extension is available. For simplicity, each KVM guest checks for availability only once during initialization. KVM does not handle Quoting service disruptions. Signed-off-by: Peter Fang Signed-off-by: Xu Yilun --- arch/x86/include/asm/tdx.h | 9 +++ arch/x86/kvm/vmx/tdx.h | 6 ++ arch/x86/kvm/vmx/tdx.c | 135 ++++++++++++++++++++++++++++++++++++- virt/kvm/kvm_main.c | 1 + 4 files changed, 150 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index 945e6817abb2..5863d6748100 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -115,6 +115,15 @@ struct tdx_quote_req { u32 out_len; u8 data[]; }; + +#define TDX_QUOTE_REQ_HDR_SIZE (offsetof(struct tdx_quote_req, data)) + +/* + * TDG.VP.VMCALL Status Codes + */ +#define TDX_QUOTE_STATUS_SUCCESS 0x0000000000000000ULL +#define TDX_QUOTE_STATUS_ERROR 0x8000000000000000ULL +#define TDX_QUOTE_STATUS_UNAVAILABLE 0x8000000000000001ULL #endif /* CONFIG_INTEL_TDX_GUEST || CONFIG_KVM_INTEL_TDX */ #ifdef CONFIG_INTEL_TDX_HOST diff --git a/arch/x86/kvm/vmx/tdx.h b/arch/x86/kvm/vmx/tdx.h index ac8323a68b16..18c93e80c0ec 100644 --- a/arch/x86/kvm/vmx/tdx.h +++ b/arch/x86/kvm/vmx/tdx.h @@ -47,6 +47,12 @@ struct kvm_tdx { * Set/unset is protected with kvm->mmu_lock. */ bool wait_for_sept_zap; + + /* + * Whether to get TDX quote directly in kernel, without exiting to + * userspace. + */ + bool get_quote_in_kernel; }; /* TDX module vCPU states */ diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 9f7c39e0d4b5..bade046da5a1 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -1538,11 +1538,133 @@ static int tdx_get_quote_user(struct kvm_vcpu *vcpu, u64 gpa, u64 size) return 0; } +static bool write_quote_status_to_guest(struct kvm_vcpu *vcpu, u64 status, + gpa_t gpa) +{ + if (kvm_vcpu_write_guest(vcpu, + gpa + offsetof(struct tdx_quote_req, status), + &status, sizeof(status))) + return false; + + return true; +} + +static bool write_quote_to_guest(struct kvm_vcpu *vcpu, void *quote_data, + u32 quote_len, gpa_t gpa) +{ + if (kvm_vcpu_write_guest(vcpu, + gpa + TDX_QUOTE_REQ_HDR_SIZE, + quote_data, quote_len)) + return false; + + if (kvm_vcpu_write_guest(vcpu, + gpa + offsetof(struct tdx_quote_req, out_len), + "e_len, sizeof(quote_len))) + return false; + + return true; +} + +static u64 __get_quote_kernel(struct kvm_vcpu *vcpu, struct tdx_quote_req *req, + size_t req_len, gpa_t req_gpa, size_t total_len) +{ + struct tdx_td *td = &to_kvm_tdx(vcpu->kvm)->td; + + /* Only support version 1 as defined in the GHCI spec */ + if (req->version != 1) + return TDX_QUOTE_STATUS_ERROR; + + if ((size_t)req->in_len + TDX_QUOTE_REQ_HDR_SIZE > req_len) + return TDX_QUOTE_STATUS_ERROR; + + /* The caller frees the quote data */ + void *quote_data __free(kvfree) = + tdx_quote_generate(td, req->data, req->in_len, &req->out_len); + + if (!quote_data) + return TDX_QUOTE_STATUS_UNAVAILABLE; + + if ((size_t)req->out_len + TDX_QUOTE_REQ_HDR_SIZE > total_len) + return TDX_QUOTE_STATUS_ERROR; + + if (!write_quote_to_guest(vcpu, quote_data, req->out_len, req_gpa)) + return TDX_QUOTE_STATUS_ERROR; + + return TDX_QUOTE_STATUS_SUCCESS; +} + +static u64 tdx_get_quote_check_args(struct kvm_vcpu *vcpu, u64 gpa, u64 size) +{ + gfn_t gfn_start, gfn_end; + u64 end; + + if (!size) + return TDVMCALL_STATUS_INVALID_OPERAND; + + if (!PAGE_ALIGNED(gpa) || !PAGE_ALIGNED(size)) + return TDVMCALL_STATUS_ALIGN_ERROR; + + if (check_add_overflow(gpa, size, &end)) + return TDVMCALL_STATUS_INVALID_OPERAND; + + gfn_start = gpa_to_gfn(gpa); + gfn_end = gpa_to_gfn(end); + + /* + * Reject if the guest didn't explicitly convert its quote pages to + * shared. + */ + if (!kvm_range_has_memory_attributes(vcpu->kvm, gfn_start, gfn_end, + KVM_MEMORY_ATTRIBUTE_PRIVATE, 0)) + return TDVMCALL_STATUS_INVALID_OPERAND; + + return TDVMCALL_STATUS_SUCCESS; +} + +static int tdx_get_quote_kernel(struct kvm_vcpu *vcpu, u64 gpa, u64 size) +{ + void *first_page = NULL; + u64 err, qerr; + + err = tdx_get_quote_check_args(vcpu, gpa, size); + if (err != TDVMCALL_STATUS_SUCCESS) + goto out; + + err = TDVMCALL_STATUS_INVALID_OPERAND; + + first_page = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!first_page) + goto out; + + /* + * Read the first GetQuote page for its header + in_data. The check + * above ensures that this GetQuote message is at least one page in + * size. in_data spanning more than a page is not supported. + */ + if (kvm_vcpu_read_guest(vcpu, gpa, first_page, PAGE_SIZE)) + goto out; + + qerr = __get_quote_kernel(vcpu, first_page, PAGE_SIZE, + (gpa_t)gpa, size); + + if (write_quote_status_to_guest(vcpu, qerr, (gpa_t)gpa) && + qerr == TDX_QUOTE_STATUS_SUCCESS) + err = TDVMCALL_STATUS_SUCCESS; + +out: + kfree(first_page); + tdvmcall_set_return_code(vcpu, err); + + return 1; +} + static int tdx_get_quote(struct kvm_vcpu *vcpu) { + struct kvm_tdx *kvm_tdx = to_kvm_tdx(vcpu->kvm); struct vcpu_tdx *tdx = to_tdx(vcpu); u64 gpa = tdx->vp_enter_args.r12; u64 size = tdx->vp_enter_args.r13; + int ret; /* The gpa of buffer must have shared bit set. */ if (vt_is_tdx_private_gpa(vcpu->kvm, gpa)) { @@ -1552,7 +1674,12 @@ static int tdx_get_quote(struct kvm_vcpu *vcpu) gpa &= ~gfn_to_gpa(kvm_gfn_direct_bits(vcpu->kvm)); - return tdx_get_quote_user(vcpu, gpa, size); + if (kvm_tdx->get_quote_in_kernel) + ret = tdx_get_quote_kernel(vcpu, gpa, size); + else + ret = tdx_get_quote_user(vcpu, gpa, size); + + return ret; } static int tdx_setup_event_notify_interrupt(struct kvm_vcpu *vcpu) @@ -2751,6 +2878,12 @@ static int tdx_td_init(struct kvm *kvm, struct kvm_tdx_cmd *cmd) else kvm->arch.gfn_direct_bits = TDX_SHARED_BIT_PWL_4; + /* + * Check only once at TD creation. If the quoting service gets disrupted + * during TD runtime, let the user handle it. + */ + kvm_tdx->get_quote_in_kernel = tdx_quote_enabled(); + kvm_tdx->state = TD_STATE_INITIALIZED; out: /* kfree() accepts NULL. */ diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 89489996fbc1..599f88a13071 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -2461,6 +2461,7 @@ bool kvm_range_has_memory_attributes(struct kvm *kvm, gfn_t start, gfn_t end, return true; } +EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_range_has_memory_attributes); static __always_inline void kvm_handle_gfn_range(struct kvm *kvm, struct kvm_mmu_notifier_range *range) -- 2.25.1