[PATCH v2 06/25] x86/virt/tdx: Add SEAMCALL wrappers for TDX TD creation

Rick Edgecombe posted 25 patches 3 weeks, 4 days ago
[PATCH v2 06/25] x86/virt/tdx: Add SEAMCALL wrappers for TDX TD creation
Posted by Rick Edgecombe 3 weeks, 4 days ago
From: Isaku Yamahata <isaku.yamahata@intel.com>

Intel TDX protects guest VMs from malicious hosts and certain physical
attacks. It defines various control structures that hold state for things
like TDs or vCPUs. These control structures are stored in pages given to
the TDX module and encrypted with either the global KeyID or the guest
KeyIDs.

To manipulate these control structures the TDX module defines a few
SEAMCALLs. KVM will use these during the process of creating a TD as
follows:

1) Allocate a unique TDX KeyID for a new guest.

1) Call TDH.MNG.CREATE to create a "TD Root" (TDR) page, together with
   the new allocated KeyID. Unlike the rest of the TDX guest, the TDR
   page is crypto-protected by the 'global KeyID'.

2) Call the previously added TDH.MNG.KEY.CONFIG on each package to
   configure the KeyID for the guest. After this step, the KeyID to
   protect the guest is ready and the rest of the guest will be protected
   by this KeyID.

3) Call TDH.MNG.ADDCX to add TD Control Structure (TDCS) pages.

4) Call TDH.MNG.INIT to initialize the TDCS.

To reclaim these pages for use by the kernel other SEAMCALLs are needed,
which will be added in future patches.

Add tdh_mng_addcx(), tdh_mng_create() and tdh_mng_init() to export these
SEAMCALLs so that KVM can use them to create TDs.

For SEAMCALLs that give a page to the TDX module to be encrypted, clflush
the page mapped with KeyID 0, such that any dirty cache lines don't write
back later and clobber TD memory or control structures. Don't worry about
the other MK-TME KeyIDs because the kernel doesn't use them. The TDX docs
specify that this flush is not needed unless the TDX module exposes the
CLFLUSH_BEFORE_ALLOC feature bit. Be conservative and aways flush.

Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Isaku Yamahata <isaku.yamahata@intel.com>
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
Reviewed-by: Binbin Wu <binbin.wu@linux.intel.com>
Reviewed-by: Yuan Yao <yuan.yao@intel.com>
---
uAPI breakout v2:
 - Change to use 'u64' as function parameter to prepare to move
   SEAMCALL wrappers to arch/x86. (Kai)
 - Split to separate patch
 - Move SEAMCALL wrappers from KVM to x86 core;
 - Move TDH_xx macros from KVM to x86 core;
 - Re-write log

uAPI breakout v1:
 - Make argument to C wrapper function struct kvm_tdx * or
   struct vcpu_tdx * .(Sean)
 - Drop unused helpers (Kai)
 - Fix bisectability issues in headers (Kai)
 - Updates from seamcall overhaul (Kai)

v19:
 - Update the commit message to match the patch by Yuan
 - Use seamcall() and seamcall_ret() by paolo

v18:
 - removed stub functions for __seamcall{,_ret}()
 - Added Reviewed-by Binbin
 - Make tdx_seamcall() use struct tdx_module_args instead of taking
  each inputs.

v16:
 - use struct tdx_module_args instead of struct tdx_module_output
 - Add tdh_mem_sept_rd() for SEPT_VE_DISABLE=1.
---
 arch/x86/include/asm/tdx.h  |  3 +++
 arch/x86/virt/vmx/tdx/tdx.c | 39 +++++++++++++++++++++++++++++++++++++
 arch/x86/virt/vmx/tdx/tdx.h |  3 +++
 3 files changed, 45 insertions(+)

diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
index 9897335a8e2f..9d19ca33e884 100644
--- a/arch/x86/include/asm/tdx.h
+++ b/arch/x86/include/asm/tdx.h
@@ -123,8 +123,11 @@ int tdx_guest_keyid_alloc(void);
 void tdx_guest_keyid_free(unsigned int keyid);
 
 /* SEAMCALL wrappers for creating/destroying/running TDX guests */
+u64 tdh_mng_addcx(u64 tdr, u64 tdcs);
 u64 tdh_mng_key_config(u64 tdr);
+u64 tdh_mng_create(u64 tdr, u64 hkid);
 u64 tdh_mng_key_freeid(u64 tdr);
+u64 tdh_mng_init(u64 tdr, u64 td_params, u64 *rcx);
 #else
 static inline void tdx_init(void) { }
 static inline int tdx_cpu_enable(void) { return -ENODEV; }
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index c42eab8cc069..16122fd552ff 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -1563,6 +1563,18 @@ void tdx_guest_keyid_free(unsigned int keyid)
 }
 EXPORT_SYMBOL_GPL(tdx_guest_keyid_free);
 
+u64 tdh_mng_addcx(u64 tdr, u64 tdcs)
+{
+	struct tdx_module_args args = {
+		.rcx = tdcs,
+		.rdx = tdr,
+	};
+
+	clflush_cache_range(__va(tdcs), PAGE_SIZE);
+	return seamcall(TDH_MNG_ADDCX, &args);
+}
+EXPORT_SYMBOL_GPL(tdh_mng_addcx);
+
 u64 tdh_mng_key_config(u64 tdr)
 {
 	struct tdx_module_args args = {
@@ -1573,6 +1585,17 @@ u64 tdh_mng_key_config(u64 tdr)
 }
 EXPORT_SYMBOL_GPL(tdh_mng_key_config);
 
+u64 tdh_mng_create(u64 tdr, u64 hkid)
+{
+	struct tdx_module_args args = {
+		.rcx = tdr,
+		.rdx = hkid,
+	};
+	clflush_cache_range(__va(tdr), PAGE_SIZE);
+	return seamcall(TDH_MNG_CREATE, &args);
+}
+EXPORT_SYMBOL_GPL(tdh_mng_create);
+
 u64 tdh_mng_key_freeid(u64 tdr)
 {
 	struct tdx_module_args args = {
@@ -1582,3 +1605,19 @@ u64 tdh_mng_key_freeid(u64 tdr)
 	return seamcall(TDH_MNG_KEY_FREEID, &args);
 }
 EXPORT_SYMBOL_GPL(tdh_mng_key_freeid);
+
+u64 tdh_mng_init(u64 tdr, u64 td_params, u64 *rcx)
+{
+	struct tdx_module_args args = {
+		.rcx = tdr,
+		.rdx = td_params,
+	};
+	u64 ret;
+
+	ret = seamcall_ret(TDH_MNG_INIT, &args);
+
+	*rcx = args.rcx;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(tdh_mng_init);
diff --git a/arch/x86/virt/vmx/tdx/tdx.h b/arch/x86/virt/vmx/tdx/tdx.h
index 95002e7ff4c5..b9287304f372 100644
--- a/arch/x86/virt/vmx/tdx/tdx.h
+++ b/arch/x86/virt/vmx/tdx/tdx.h
@@ -17,8 +17,11 @@
 /*
  * TDX module SEAMCALL leaf functions
  */
+#define TDH_MNG_ADDCX			1
 #define TDH_MNG_KEY_CONFIG		8
+#define TDH_MNG_CREATE			9
 #define TDH_MNG_KEY_FREEID		20
+#define TDH_MNG_INIT			21
 #define TDH_PHYMEM_PAGE_RDMD		24
 #define TDH_SYS_KEY_CONFIG		31
 #define TDH_SYS_INIT			33
-- 
2.47.0
Re: [PATCH v2 06/25] x86/virt/tdx: Add SEAMCALL wrappers for TDX TD creation
Posted by Dave Hansen 1 week, 5 days ago
On 10/30/24 12:00, Rick Edgecombe wrote:
> +u64 tdh_mng_create(u64 tdr, u64 hkid)
> +{
> +	struct tdx_module_args args = {
> +		.rcx = tdr,
> +		.rdx = hkid,
> +	};
> +	clflush_cache_range(__va(tdr), PAGE_SIZE);
> +	return seamcall(TDH_MNG_CREATE, &args);
> +}
> +EXPORT_SYMBOL_GPL(tdh_mng_create);

I'd _prefer_ that this explain why the clflush is there.

The other goofy thing here is why it's getting a physical address passed
in.  It's my old 32-bit paranoia kicking in, but everything that has a
valid virtual address _also_ has a valid physical address.  The inverse
is not true, though.  So I like to keep things as pointers as long as
possible.
Re: [PATCH v2 06/25] x86/virt/tdx: Add SEAMCALL wrappers for TDX TD creation
Posted by Edgecombe, Rick P 1 week, 5 days ago
On Tue, 2024-11-12 at 12:17 -0800, Dave Hansen wrote:
> On 10/30/24 12:00, Rick Edgecombe wrote:
> > +u64 tdh_mng_create(u64 tdr, u64 hkid)
> > +{
> > +	struct tdx_module_args args = {
> > +		.rcx = tdr,
> > +		.rdx = hkid,
> > +	};
> > +	clflush_cache_range(__va(tdr), PAGE_SIZE);
> > +	return seamcall(TDH_MNG_CREATE, &args);
> > +}
> > +EXPORT_SYMBOL_GPL(tdh_mng_create);
> 
> I'd _prefer_ that this explain why the clflush is there.

How about:
/*
 * The TDX module exposes a CLFLUSH_BEFORE_ALLOC bit to specify whether
 * a CLFLUSH of pages is required before handing them to the TDX module.
 * Be conservative and make the code simpler by doing the CLFLUSH 
 * unconditionally.
 */

> 
> The other goofy thing here is why it's getting a physical address passed
> in.  It's my old 32-bit paranoia kicking in, but everything that has a
> valid virtual address _also_ has a valid physical address.  The inverse
> is not true, though.  So I like to keep things as pointers as long as
> possible.

Ok, seems reasonable.
Re: [PATCH v2 06/25] x86/virt/tdx: Add SEAMCALL wrappers for TDX TD creation
Posted by Dave Hansen 1 week, 5 days ago
On 11/12/24 13:21, Edgecombe, Rick P wrote:
> On Tue, 2024-11-12 at 12:17 -0800, Dave Hansen wrote:
>> On 10/30/24 12:00, Rick Edgecombe wrote:
>>> +u64 tdh_mng_create(u64 tdr, u64 hkid)
>>> +{
>>> +   struct tdx_module_args args = {
>>> +           .rcx = tdr,
>>> +           .rdx = hkid,
>>> +   };
>>> +   clflush_cache_range(__va(tdr), PAGE_SIZE);
>>> +   return seamcall(TDH_MNG_CREATE, &args);
>>> +}
>>> +EXPORT_SYMBOL_GPL(tdh_mng_create);
>> I'd _prefer_ that this explain why the clflush is there.
> How about:
> /*
>  * The TDX module exposes a CLFLUSH_BEFORE_ALLOC bit to specify whether
>  * a CLFLUSH of pages is required before handing them to the TDX module.
>  * Be conservative and make the code simpler by doing the CLFLUSH
>  * unconditionally.
>  */

Is there a chance we could put this in a helper so the "be conservative"
policy is centralized in one location?  The comment could also go there.