Skip to content

Commit cf207ea

Browse files
Binbin Wubonzini
authored andcommitted
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 <[email protected]> Tested-by: Mikko Ylinen <[email protected]> Acked-by: Kai Huang <[email protected]> [Adjust userspace API. - Paolo] Signed-off-by: Paolo Bonzini <[email protected]>
1 parent b5aafcb commit cf207ea

File tree

3 files changed

+97
-1
lines changed

3 files changed

+97
-1
lines changed

Documentation/virt/kvm/api.rst

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6645,7 +6645,8 @@ to the byte array.
66456645
.. note::
66466646

66476647
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
66496650
operations are complete (and guest state is consistent) only after userspace
66506651
has re-entered the kernel with KVM_RUN. The kernel side will first finish
66516652
incomplete operations and then check for pending signals.
@@ -7174,6 +7175,52 @@ The valid value for 'flags' is:
71747175
- KVM_NOTIFY_CONTEXT_INVALID -- the VM context is corrupted and not valid
71757176
in VMCS. It would run into unknown result if resume the target VM.
71767177

7178+
::
7179+
7180+
/* KVM_EXIT_TDX */
7181+
struct {
7182+
__u64 flags;
7183+
__u64 nr;
7184+
union {
7185+
struct {
7186+
u64 ret;
7187+
u64 data[5];
7188+
} unknown;
7189+
struct {
7190+
u64 ret;
7191+
u64 gpa;
7192+
u64 size;
7193+
} get_quote;
7194+
};
7195+
} tdx;
7196+
7197+
Process a TDVMCALL from the guest. KVM forwards select TDVMCALL based
7198+
on the Guest-Hypervisor Communication Interface (GHCI) specification;
7199+
KVM bridges these requests to the userspace VMM with minimal changes,
7200+
placing the inputs in the union and copying them back to the guest
7201+
on re-entry.
7202+
7203+
Flags are currently always zero, whereas ``nr`` contains the TDVMCALL
7204+
number from register R11. The remaining field of the union provide the
7205+
inputs and outputs of the TDVMCALL. Currently the following values of
7206+
``nr`` are defined:
7207+
7208+
* ``TDVMCALL_GET_QUOTE``: the guest has requested to generate a TD-Quote
7209+
signed by a service hosting TD-Quoting Enclave operating on the host.
7210+
Parameters and return value are in the ``get_quote`` field of the union.
7211+
The ``gpa`` field and ``size`` specify the guest physical address
7212+
(without the shared bit set) and the size of a shared-memory buffer, in
7213+
which the TDX guest passes a TD Report. The ``ret`` field represents
7214+
the return value of the GetQuote request. When the request has been
7215+
queued successfully, the TDX guest can poll the status field in the
7216+
shared-memory area to check whether the Quote generation is completed or
7217+
not. When completed, the generated Quote is returned via the same buffer.
7218+
7219+
KVM may add support for more values in the future that may cause a userspace
7220+
exit, even without calls to ``KVM_ENABLE_CAP`` or similar. In this case,
7221+
it will enter with output fields already valid; in the common case, the
7222+
``unknown.ret`` field of the union will be ``TDVMCALL_STATUS_SUBFUNC_UNSUPPORTED``.
7223+
Userspace need not do anything if it does not wish to support a TDVMCALL.
71777224
::
71787225

71797226
/* Fix the size of the union. */

arch/x86/kvm/vmx/tdx.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1465,6 +1465,36 @@ static int tdx_get_td_vm_call_info(struct kvm_vcpu *vcpu)
14651465
return 1;
14661466
}
14671467

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+
14681498
static int handle_tdvmcall(struct kvm_vcpu *vcpu)
14691499
{
14701500
switch (tdvmcall_leaf(vcpu)) {
@@ -1474,6 +1504,8 @@ static int handle_tdvmcall(struct kvm_vcpu *vcpu)
14741504
return tdx_report_fatal_error(vcpu);
14751505
case TDVMCALL_GET_TD_VM_CALL_INFO:
14761506
return tdx_get_td_vm_call_info(vcpu);
1507+
case TDVMCALL_GET_QUOTE:
1508+
return tdx_get_quote(vcpu);
14771509
default:
14781510
break;
14791511
}

include/uapi/linux/kvm.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ struct kvm_xen_exit {
178178
#define KVM_EXIT_NOTIFY 37
179179
#define KVM_EXIT_LOONGARCH_IOCSR 38
180180
#define KVM_EXIT_MEMORY_FAULT 39
181+
#define KVM_EXIT_TDX 40
181182

182183
/* For KVM_EXIT_INTERNAL_ERROR */
183184
/* Emulate instruction failed. */
@@ -447,6 +448,22 @@ struct kvm_run {
447448
__u64 gpa;
448449
__u64 size;
449450
} memory_fault;
451+
/* KVM_EXIT_TDX */
452+
struct {
453+
__u64 flags;
454+
__u64 nr;
455+
union {
456+
struct {
457+
__u64 ret;
458+
__u64 data[5];
459+
} unknown;
460+
struct {
461+
__u64 ret;
462+
__u64 gpa;
463+
__u64 size;
464+
} get_quote;
465+
};
466+
} tdx;
450467
/* Fix the size of the union. */
451468
char padding[256];
452469
};

0 commit comments

Comments
 (0)