[PATCH v2 10/25] x86/virt/tdx: Add SEAMCALL wrappers for TDX flush operations

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

Intel TDX protects guest VMs from malicious host and certain physical
attacks. The TDX module has the concept of flushing vCPUs. These flushes
include both a flush of the translation caches and also any other state
internal to the TDX module. Before freeing a KeyID, this flush operation
needs to be done. KVM will need to perform the flush on each pCPU
associated with the TD, and also perform a TD scoped operation that checks
if the flush has been done on all vCPU's associated with the TD.

Add a tdh_vp_flush() function to be used to call TDH.VP.FLUSH on each pCPU
associated with the TD during TD teardown. It will also be called when
disabling TDX and during vCPU migration between pCPUs.

Add tdh_mng_vpflushdone() to be used by KVM to call TDH.MNG.VPFLUSHDONE.
KVM will use this during TD teardown to verify that TDH.VP.FLUSH has been
called sufficiently, and advance the state machine that will allow for
reclaiming the TD's KeyID.

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.

---
 arch/x86/include/asm/tdx.h  |  2 ++
 arch/x86/virt/vmx/tdx/tdx.c | 20 ++++++++++++++++++++
 arch/x86/virt/vmx/tdx/tdx.h |  2 ++
 3 files changed, 24 insertions(+)

diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
index a70933ec7808..d093dc4350ac 100644
--- a/arch/x86/include/asm/tdx.h
+++ b/arch/x86/include/asm/tdx.h
@@ -129,6 +129,8 @@ u64 tdh_mng_key_config(u64 tdr);
 u64 tdh_mng_create(u64 tdr, u64 hkid);
 u64 tdh_vp_create(u64 tdr, u64 tdvpr);
 u64 tdh_mng_rd(u64 tdr, u64 field, u64 *data);
+u64 tdh_vp_flush(u64 tdvpr);
+u64 tdh_mng_vpflushdone(u64 tdr);
 u64 tdh_mng_key_freeid(u64 tdr);
 u64 tdh_mng_init(u64 tdr, u64 td_params, u64 *rcx);
 u64 tdh_vp_init(u64 tdvpr, u64 initial_rcx);
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index 82820422d698..af121a73de80 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -1638,6 +1638,26 @@ u64 tdh_mng_rd(u64 tdr, u64 field, u64 *data)
 }
 EXPORT_SYMBOL_GPL(tdh_mng_rd);
 
+u64 tdh_vp_flush(u64 tdvpr)
+{
+	struct tdx_module_args args = {
+		.rcx = tdvpr,
+	};
+
+	return seamcall(TDH_VP_FLUSH, &args);
+}
+EXPORT_SYMBOL_GPL(tdh_vp_flush);
+
+u64 tdh_mng_vpflushdone(u64 tdr)
+{
+	struct tdx_module_args args = {
+		.rcx = tdr,
+	};
+
+	return seamcall(TDH_MNG_VPFLUSHDONE, &args);
+}
+EXPORT_SYMBOL_GPL(tdh_mng_vpflushdone);
+
 u64 tdh_mng_key_freeid(u64 tdr)
 {
 	struct tdx_module_args args = {
diff --git a/arch/x86/virt/vmx/tdx/tdx.h b/arch/x86/virt/vmx/tdx/tdx.h
index 1915a558c126..a63037036c91 100644
--- a/arch/x86/virt/vmx/tdx/tdx.h
+++ b/arch/x86/virt/vmx/tdx/tdx.h
@@ -23,6 +23,8 @@
 #define TDH_MNG_CREATE			9
 #define TDH_VP_CREATE			10
 #define TDH_MNG_RD			11
+#define TDH_VP_FLUSH			18
+#define TDH_MNG_VPFLUSHDONE		19
 #define TDH_MNG_KEY_FREEID		20
 #define TDH_MNG_INIT			21
 #define TDH_VP_INIT			22
-- 
2.47.0
Re: [PATCH v2 10/25] x86/virt/tdx: Add SEAMCALL wrappers for TDX flush operations
Posted by Dave Hansen 1 week, 5 days ago
On 10/30/24 12:00, Rick Edgecombe wrote:
> +u64 tdh_vp_flush(u64 tdvpr)
> +{
> +	struct tdx_module_args args = {
> +		.rcx = tdvpr,
> +	};
> +
> +	return seamcall(TDH_VP_FLUSH, &args);
> +}
> +EXPORT_SYMBOL_GPL(tdh_vp_flush);

This also just isn't looking right.  The 'tdvpr' is a _thing_.  It has a
type and it came back from some _other_ bit of the same type.

So, in the worst case, this could be:

struct tdvpr {
	u64 tdvpr_paddr;
};

u64 tdh_vp_flush(struct tdvpr *tdpr)
{
	...

But just passing around physical addresses and then having this things
stick it right in to seamcall() doesn't seem like the best we can do.
Re: [PATCH v2 10/25] x86/virt/tdx: Add SEAMCALL wrappers for TDX flush operations
Posted by Edgecombe, Rick P 1 week, 4 days ago
On Tue, 2024-11-12 at 17:11 -0800, Dave Hansen wrote:
> On 10/30/24 12:00, Rick Edgecombe wrote:
> > +u64 tdh_vp_flush(u64 tdvpr)
> > +{
> > +	struct tdx_module_args args = {
> > +		.rcx = tdvpr,
> > +	};
> > +
> > +	return seamcall(TDH_VP_FLUSH, &args);
> > +}
> > +EXPORT_SYMBOL_GPL(tdh_vp_flush);
> 
> This also just isn't looking right.  The 'tdvpr' is a _thing_.  It has a
> type and it came back from some _other_ bit of the same type.
> 
> So, in the worst case, this could be:
> 
> struct tdvpr {
> 	u64 tdvpr_paddr;
> };
> 
> u64 tdh_vp_flush(struct tdvpr *tdpr)
> {
> 	...
> 
> But just passing around physical addresses and then having this things
> stick it right in to seamcall() doesn't seem like the best we can do.

Earlier you mentioned passing pointers instead of PA's. Could we have something
like the below? It turns out the KVM code has to go through extra steps to
translate between PA and VA on its side. So if we keep it a VA in KVM and let
the SEAMCALL wrappers translate to PA, it actually simplifies the KVM code.

Or keep the VA in the struct.

diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
index 01409a59224d..1f48813ade33 100644
--- a/arch/x86/include/asm/tdx.h
+++ b/arch/x86/include/asm/tdx.h
@@ -137,7 +137,7 @@ u64 tdh_vp_create(u64 tdr, u64 tdvpr);
 u64 tdh_mng_rd(u64 tdr, u64 field, u64 *data);
 u64 tdh_mr_extend(u64 tdr, u64 gpa, u64 *rcx, u64 *rdx);
 u64 tdh_mr_finalize(u64 tdr);
-u64 tdh_vp_flush(u64 tdvpr);
+u64 tdh_vp_flush(void *tdvpr);
 u64 tdh_mng_vpflushdone(u64 tdr);
 u64 tdh_mng_key_freeid(u64 tdr);
 u64 tdh_mng_init(u64 tdr, u64 td_params, u64 *rcx);
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index 2a8997eb1ef1..d456e0b0b90c 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -1785,10 +1785,10 @@ u64 tdh_mr_finalize(u64 tdr)
 }
 EXPORT_SYMBOL_GPL(tdh_mr_finalize);
 
-u64 tdh_vp_flush(u64 tdvpr)
+u64 tdh_vp_flush(void *tdvpr)
 {
        struct tdx_module_args args = {
-               .rcx = tdvpr,
+               .rcx = __pa(tdvpr),
        };
 
        return seamcall(TDH_VP_FLUSH, &args);

Re: [PATCH v2 10/25] x86/virt/tdx: Add SEAMCALL wrappers for TDX flush operations
Posted by Dave Hansen 1 week, 4 days ago
On 11/13/24 13:18, Edgecombe, Rick P wrote:
> -u64 tdh_vp_flush(u64 tdvpr)
> +u64 tdh_vp_flush(void *tdvpr)
>  {
>         struct tdx_module_args args = {
> -               .rcx = tdvpr,
> +               .rcx = __pa(tdvpr),
>         };
> 
>         return seamcall(TDH_VP_FLUSH, &args);

I'd much rather these be:

	tdx->tdvpr_page = alloc_page(GFP_KERNEL_ACCOUNT);

and then you pass around the struct page and do:

	.rcx = page_to_phys(tdvpr)

Because it's honestly _not_ an address.  It really and truly is a page
and you never need to dereference it, only pass it around as a handle.
You could get fancy and make a typedef for it or something, or even

struct tdvpr_struct {
	struct page *page;
}

But that's probably overkill.  It would help to, for instance, avoid
mixing up these two pages:

+u64 tdh_vp_create(u64 tdr, u64 tdvpr);

But it wouldn't help as much for these:

+u64 tdh_vp_addcx(u64 tdvpr, u64 tdcx);
+u64 tdh_vp_init(u64 tdvpr, u64 initial_rcx);
+u64 tdh_vp_init_apicid(u64 tdvpr, u64 initial_rcx, u32 x2apicid);
+u64 tdh_vp_flush(u64 tdvpr);
+u64 tdh_vp_rd(u64 tdvpr, u64 field, u64 *data);
+u64 tdh_vp_wr(u64 tdvpr, u64 field, u64 data, u64 mask);

Except for (for instance) 'tdr' vs. 'tdvpr' confusion.  Spot the bug:

	tdh_vp_flush(kvm_tdx(foo)->tdr_pa);
	tdh_vp_flush(kvm_tdx(foo)->tdrvp_pa);

Do you want the compiler's help for those?
Re: [PATCH v2 10/25] x86/virt/tdx: Add SEAMCALL wrappers for TDX flush operations
Posted by Edgecombe, Rick P 1 week, 4 days ago
On Wed, 2024-11-13 at 13:41 -0800, Dave Hansen wrote:
> I'd much rather these be:
> 
> 	tdx->tdvpr_page = alloc_page(GFP_KERNEL_ACCOUNT);
> 
> and then you pass around the struct page and do:
> 
> 	.rcx = page_to_phys(tdvpr)
> 
> Because it's honestly _not_ an address.  It really and truly is a page
> and you never need to dereference it, only pass it around as a handle.

That is a really good point.

> You could get fancy and make a typedef for it or something, or even
> 
> struct tdvpr_struct {
> 	struct page *page;
> }
> 
> But that's probably overkill.  It would help to, for instance, avoid
> mixing up these two pages:
> 
> +u64 tdh_vp_create(u64 tdr, u64 tdvpr);
> 
> But it wouldn't help as much for these:
> 
> +u64 tdh_vp_addcx(u64 tdvpr, u64 tdcx);
> +u64 tdh_vp_init(u64 tdvpr, u64 initial_rcx);
> +u64 tdh_vp_init_apicid(u64 tdvpr, u64 initial_rcx, u32 x2apicid);
> +u64 tdh_vp_flush(u64 tdvpr);
> +u64 tdh_vp_rd(u64 tdvpr, u64 field, u64 *data);
> +u64 tdh_vp_wr(u64 tdvpr, u64 field, u64 data, u64 mask);
> 
> Except for (for instance) 'tdr' vs. 'tdvpr' confusion.  Spot the bug:
> 
> 	tdh_vp_flush(kvm_tdx(foo)->tdr_pa);
> 	tdh_vp_flush(kvm_tdx(foo)->tdrvp_pa);
> 
> Do you want the compiler's help for those?

Haha, we have already had bugs around these names actually. If we we end up with
the current arch/x86 based approach we can see if we can fit it in.