Re: [PATCH v19 029/130] KVM: TDX: Add C wrapper functions for SEAMCALLs to the TDX module

From: Isaku Yamahata
Date: Thu Mar 21 2024 - 20:17:08 EST


On Thu, Mar 21, 2024 at 11:37:58AM +1300,
"Huang, Kai" <kai.huang@xxxxxxxxx> wrote:

>
>
> On 21/03/2024 10:36 am, Isaku Yamahata wrote:
> > On Wed, Mar 20, 2024 at 01:03:21PM +1300,
> > "Huang, Kai" <kai.huang@xxxxxxxxx> wrote:
> >
> > > > +static inline u64 tdx_seamcall(u64 op, struct tdx_module_args *in,
> > > > + struct tdx_module_args *out)
> > > > +{
> > > > + u64 ret;
> > > > +
> > > > + if (out) {
> > > > + *out = *in;
> > > > + ret = seamcall_ret(op, out);
> > > > + } else
> > > > + ret = seamcall(op, in);
> > >
> > > I think it's silly to have the @out argument in this way.
> > >
> > > What is the main reason to still have it?
> > >
> > > Yeah we used to have the @out in __seamcall() assembly function. The
> > > assembly code checks the @out and skips copying registers to @out when it is
> > > NULL.
> > >
> > > But it got removed when we tried to unify the assembly for TDCALL/TDVMCALL
> > > and SEAMCALL to have a *SINGLE* assembly macro.
> > >
> > > https://lore.kernel.org/lkml/cover.1692096753.git.kai.huang@xxxxxxxxx/
> > >
> > > To me that means we should just accept the fact we will always have a valid
> > > @out.
> > >
> > > But there might be some case that you _obviously_ need the @out and I
> > > missed?
> >
> > As I replied at [1], those four wrappers need to return values.
> > The first three on error, the last one on success.
> >
> > [1] https://lore.kernel.org/kvm/20240320202040.GH1994522@xxxxxxxxxxxxxxxxxxxxx/
> >
> > tdh_mem_sept_add(kvm_tdx, gpa, tdx_level, hpa, &entry, &level_state);
> > tdh_mem_page_aug(kvm_tdx, gpa, hpa, &entry, &level_state);
> > tdh_mem_page_remove(kvm_tdx, gpa, tdx_level, &entry, &level_state);
> > u64 tdh_vp_rd(struct vcpu_tdx *tdx, u64 field, u64 *value)
> >
> > We can delete out from other wrappers.
>
> Ah, OK. I got you don't want to invent separate wrappers for each
> seamcall() variants like:
>
> - tdx_seamcall(u64 fn, struct tdx_module_args *args);
> - tdx_seamcall_ret(u64 fn, struct tdx_module_args *args);
> - tdx_seamcall_saved_ret(u64 fn, struct tdx_module_args *args);
>
> To be honest I found they were kinda annoying myself during the "unify
> TDCALL/SEAMCALL and TDVMCALL assembly" patchset.
>
> But life is hard...
>
> And given (it seems) we are going to remove kvm_spurious_fault(), I think
> the tdx_seamcall() variants are just very simple wrapper of plain seamcall()
> variants.
>
> So how about we have some macros:
>
> static inline bool is_seamcall_err_kernel_defined(u64 err)
> {
> return err & TDX_SW_ERROR;
> }
>
> #define TDX_KVM_SEAMCALL(_kvm, _seamcall_func, _fn, _args) \
> ({ \
> u64 _ret = _seamcall_func(_fn, _args);
> KVM_BUG_ON(_kvm, is_seamcall_err_kernel_defined(_ret));
> _ret;
> })

As we can move out KVM_BUG_ON() to the call site, we can simply have
seamcall() or seamcall_ret().
The call site has to check error. whether it is TDX_SW_ERROR or not.
And if it hit the unexpected error, it will mark the guest bugged.


> #define tdx_kvm_seamcall(_kvm, _fn, _args) \
> TDX_KVM_SEAMCALL(_kvm, seamcall, _fn, _args)
>
> #define tdx_kvm_seamcall_ret(_kvm, _fn, _args) \
> TDX_KVM_SEAMCALL(_kvm, seamcall_ret, _fn, _args)
>
> #define tdx_kvm_seamcall_saved_ret(_kvm, _fn, _args) \
> TDX_KVM_SEAMCALL(_kvm, seamcall_saved_ret, _fn, _args)
>
> This is consistent with what we have in TDX host code, and this handles
> NO_ENTROPY error internally.
>
> Or, maybe we can just use the seamcall_ret() for ALL SEAMCALLs, except using
> seamcall_saved_ret() for TDH.VP.ENTER.
>
> u64 tdx_kvm_seamcall(sruct kvm*kvm, u64 fn,
> struct tdx_module_args *args)
> {
> u64 ret = seamcall_ret(fn, args);
>
> KVM_BUG_ON(kvm, is_seamcall_err_kernel_defined(ret);
>
> return ret;
> }
>
> IIUC this at least should give us a single tdx_kvm_seamcall() API for
> majority (99%) code sites?

We can eleiminate tdx_kvm_seamcall() and use seamcall() or seamcall_ret()
directly.


> And obviously I'd like other people to weigh in too.
>
> > Because only TDH.MNG.CREATE() and TDH.MNG.ADDCX() can return TDX_RND_NO_ENTROPY, > we can use __seamcall(). The TDX spec doesn't guarantee such error code
> > convention. It's very unlikely, though.
>
> I don't quite follow the "convention" part. Can you elaborate?
>
> NO_ENTROPY is already handled in seamcall() variants. Can we just use them
> directly?

I intended for bad code generation. If the loop on NO_ENTRY error harms the
code generation, we might be able to use __seamcall() or __seamcall_ret()
instead of seamcall(), seamcall_ret().
--
Isaku Yamahata <isaku.yamahata@xxxxxxxxx>