KVM: TDX: Handle TDG.VP.VMCALL<GetQuote>

Handle TDVMCALL for GetQuote to generate a TD-Quote.

GetQuote is a doorbell-like interface used by TDX guests to request VMM
to generate a TD-Quote signed by a service hosting TD-Quoting Enclave
operating on the host. A TDX guest passes a TD Report (TDREPORT_STRUCT) in
a shared-memory area as parameter. Host VMM can access it and queue the
operation for a service hosting TD-Quoting enclave. When completed, the
Quote is returned via the same shared-memory area.

KVM only checks the GPA from the TDX guest has the shared-bit set and drops
the shared-bit before exiting to userspace to avoid bleeding the shared-bit
into KVM's exit ABI. KVM forwards the request to userspace VMM (e.g. QEMU)
and userspace VMM queues the operation asynchronously. KVM sets the return
code according to the 'ret' field set by userspace to notify the TDX guest
whether the request has been queued successfully or not. When the request
has been queued successfully, the TDX guest can poll the status field in
the shared-memory area to check whether the Quote generation is completed
or not. When completed, the generated Quote is returned via the same
buffer.

Add KVM_EXIT_TDX as a new exit reason to userspace. Userspace is
required to handle the KVM exit reason as the initial support for TDX,
by reentering KVM to ensure that the TDVMCALL is complete. While at it,
add a note that KVM_EXIT_HYPERCALL also requires reentry with KVM_RUN.

Signed-off-by: Binbin Wu <binbin.wu@linux.intel.com>
Tested-by: Mikko Ylinen <mikko.ylinen@linux.intel.com>
Acked-by: Kai Huang <kai.huang@intel.com>
[Adjust userspace API. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

authored by Binbin Wu and committed by Paolo Bonzini cf207eac b5aafcb4

+97 -1
+48 -1
Documentation/virt/kvm/api.rst
··· 6645 6645 .. note:: 6646 6646 6647 6647 For KVM_EXIT_IO, KVM_EXIT_MMIO, KVM_EXIT_OSI, KVM_EXIT_PAPR, KVM_EXIT_XEN, 6648 - KVM_EXIT_EPR, KVM_EXIT_X86_RDMSR and KVM_EXIT_X86_WRMSR the corresponding 6648 + KVM_EXIT_EPR, KVM_EXIT_HYPERCALL, KVM_EXIT_TDX, 6649 + KVM_EXIT_X86_RDMSR and KVM_EXIT_X86_WRMSR the corresponding 6649 6650 operations are complete (and guest state is consistent) only after userspace 6650 6651 has re-entered the kernel with KVM_RUN. The kernel side will first finish 6651 6652 incomplete operations and then check for pending signals. ··· 7175 7174 - KVM_NOTIFY_CONTEXT_INVALID -- the VM context is corrupted and not valid 7176 7175 in VMCS. It would run into unknown result if resume the target VM. 7177 7176 7177 + :: 7178 + 7179 + /* KVM_EXIT_TDX */ 7180 + struct { 7181 + __u64 flags; 7182 + __u64 nr; 7183 + union { 7184 + struct { 7185 + u64 ret; 7186 + u64 data[5]; 7187 + } unknown; 7188 + struct { 7189 + u64 ret; 7190 + u64 gpa; 7191 + u64 size; 7192 + } get_quote; 7193 + }; 7194 + } tdx; 7195 + 7196 + Process a TDVMCALL from the guest. KVM forwards select TDVMCALL based 7197 + on the Guest-Hypervisor Communication Interface (GHCI) specification; 7198 + KVM bridges these requests to the userspace VMM with minimal changes, 7199 + placing the inputs in the union and copying them back to the guest 7200 + on re-entry. 7201 + 7202 + Flags are currently always zero, whereas ``nr`` contains the TDVMCALL 7203 + number from register R11. The remaining field of the union provide the 7204 + inputs and outputs of the TDVMCALL. Currently the following values of 7205 + ``nr`` are defined: 7206 + 7207 + * ``TDVMCALL_GET_QUOTE``: the guest has requested to generate a TD-Quote 7208 + signed by a service hosting TD-Quoting Enclave operating on the host. 7209 + Parameters and return value are in the ``get_quote`` field of the union. 7210 + The ``gpa`` field and ``size`` specify the guest physical address 7211 + (without the shared bit set) and the size of a shared-memory buffer, in 7212 + which the TDX guest passes a TD Report. The ``ret`` field represents 7213 + the return value of the GetQuote request. When the request has been 7214 + queued successfully, the TDX guest can poll the status field in the 7215 + shared-memory area to check whether the Quote generation is completed or 7216 + not. When completed, the generated Quote is returned via the same buffer. 7217 + 7218 + KVM may add support for more values in the future that may cause a userspace 7219 + exit, even without calls to ``KVM_ENABLE_CAP`` or similar. In this case, 7220 + it will enter with output fields already valid; in the common case, the 7221 + ``unknown.ret`` field of the union will be ``TDVMCALL_STATUS_SUBFUNC_UNSUPPORTED``. 7222 + Userspace need not do anything if it does not wish to support a TDVMCALL. 7178 7223 :: 7179 7224 7180 7225 /* Fix the size of the union. */
+32
arch/x86/kvm/vmx/tdx.c
··· 1465 1465 return 1; 1466 1466 } 1467 1467 1468 + static int tdx_complete_simple(struct kvm_vcpu *vcpu) 1469 + { 1470 + tdvmcall_set_return_code(vcpu, vcpu->run->tdx.unknown.ret); 1471 + return 1; 1472 + } 1473 + 1474 + static int tdx_get_quote(struct kvm_vcpu *vcpu) 1475 + { 1476 + struct vcpu_tdx *tdx = to_tdx(vcpu); 1477 + u64 gpa = tdx->vp_enter_args.r12; 1478 + u64 size = tdx->vp_enter_args.r13; 1479 + 1480 + /* The gpa of buffer must have shared bit set. */ 1481 + if (vt_is_tdx_private_gpa(vcpu->kvm, gpa)) { 1482 + tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND); 1483 + return 1; 1484 + } 1485 + 1486 + vcpu->run->exit_reason = KVM_EXIT_TDX; 1487 + vcpu->run->tdx.flags = 0; 1488 + vcpu->run->tdx.nr = TDVMCALL_GET_QUOTE; 1489 + vcpu->run->tdx.get_quote.ret = TDVMCALL_STATUS_SUBFUNC_UNSUPPORTED; 1490 + vcpu->run->tdx.get_quote.gpa = gpa & ~gfn_to_gpa(kvm_gfn_direct_bits(tdx->vcpu.kvm)); 1491 + vcpu->run->tdx.get_quote.size = size; 1492 + 1493 + vcpu->arch.complete_userspace_io = tdx_complete_simple; 1494 + 1495 + return 0; 1496 + } 1497 + 1468 1498 static int handle_tdvmcall(struct kvm_vcpu *vcpu) 1469 1499 { 1470 1500 switch (tdvmcall_leaf(vcpu)) { ··· 1504 1474 return tdx_report_fatal_error(vcpu); 1505 1475 case TDVMCALL_GET_TD_VM_CALL_INFO: 1506 1476 return tdx_get_td_vm_call_info(vcpu); 1477 + case TDVMCALL_GET_QUOTE: 1478 + return tdx_get_quote(vcpu); 1507 1479 default: 1508 1480 break; 1509 1481 }
+17
include/uapi/linux/kvm.h
··· 178 178 #define KVM_EXIT_NOTIFY 37 179 179 #define KVM_EXIT_LOONGARCH_IOCSR 38 180 180 #define KVM_EXIT_MEMORY_FAULT 39 181 + #define KVM_EXIT_TDX 40 181 182 182 183 /* For KVM_EXIT_INTERNAL_ERROR */ 183 184 /* Emulate instruction failed. */ ··· 448 447 __u64 gpa; 449 448 __u64 size; 450 449 } memory_fault; 450 + /* KVM_EXIT_TDX */ 451 + struct { 452 + __u64 flags; 453 + __u64 nr; 454 + union { 455 + struct { 456 + __u64 ret; 457 + __u64 data[5]; 458 + } unknown; 459 + struct { 460 + __u64 ret; 461 + __u64 gpa; 462 + __u64 size; 463 + } get_quote; 464 + }; 465 + } tdx; 451 466 /* Fix the size of the union. */ 452 467 char padding[256]; 453 468 };