Re: [PATCH v5 08/22] x86/virt/tdx: Shut down TDX module in case of error

From: Kai Huang
Date: Mon Jun 27 2022 - 01:26:36 EST


On Fri, 2022-06-24 at 11:50 -0700, Dave Hansen wrote:
> So, the last patch was called:
>
> Implement SEAMCALL function
>
> and yet, in this patch, we have a "seamcall()" function. That's a bit
> confusing and not covered at *all* in this subject.
>
> Further, seamcall() is the *ONLY* caller of __seamcall() that I see in
> this series. That makes its presence here even more odd.
>
> The seamcall() bits should either be in their own patch, or mashed in
> with __seamcall().

Right. The reason I didn't put the seamcall() into previous patch was it is
only used in this tdx.c, so it should be static. But adding a static function
w/o using it in previous patch will trigger a compile warning. So I introduced
here where it is first used.

One option is I can introduce seamcall() as a static inline function in tdx.h in
previous patch so there won't be a warning. I'll change to use this way.
Please let me know if you have any comments.

>
> > +/*
> > + * Wrapper of __seamcall(). It additionally prints out the error
> > + * informationi if __seamcall() fails normally. It is useful during
> > + * the module initialization by providing more information to the user.
> > + */
> > +static u64 seamcall(u64 fn, u64 rcx, u64 rdx, u64 r8, u64 r9,
> > + struct tdx_module_output *out)
> > +{
> > + u64 ret;
> > +
> > + ret = __seamcall(fn, rcx, rdx, r8, r9, out);
> > + if (ret == TDX_SEAMCALL_VMFAILINVALID || !ret)
> > + return ret;
> > +
> > + pr_err("SEAMCALL failed: leaf: 0x%llx, error: 0x%llx\n", fn, ret);
> > + if (out)
> > + pr_err("SEAMCALL additional output: rcx 0x%llx, rdx 0x%llx, r8 0x%llx, r9 0x%llx, r10 0x%llx, r11 0x%llx.\n",
> > + out->rcx, out->rdx, out->r8, out->r9, out->r10, out->r11);
> > +
> > + return ret;
> > +}
> > +
> > +static void seamcall_smp_call_function(void *data)
> > +{
> > + struct seamcall_ctx *sc = data;
> > + struct tdx_module_output out;
> > + u64 ret;
> > +
> > + ret = seamcall(sc->fn, sc->rcx, sc->rdx, sc->r8, sc->r9, &out);
> > + if (ret)
> > + atomic_set(&sc->err, -EFAULT);
> > +}
> > +
> > +/*
> > + * Call the SEAMCALL on all online CPUs concurrently. Caller to check
> > + * @sc->err to determine whether any SEAMCALL failed on any cpu.
> > + */
> > +static void seamcall_on_each_cpu(struct seamcall_ctx *sc)
> > +{
> > + on_each_cpu(seamcall_smp_call_function, sc, true);
> > +}
>
> You can get away with this three-liner seamcall_on_each_cpu() being in
> this patch, but seamcall() itself doesn't belong here.

Right. Please see above reply.

>
> > /*
> > * Detect and initialize the TDX module.
> > *
> > @@ -138,7 +195,10 @@ static int init_tdx_module(void)
> >
> > static void shutdown_tdx_module(void)
> > {
> > - /* TODO: Shut down the TDX module */
> > + struct seamcall_ctx sc = { .fn = TDH_SYS_LP_SHUTDOWN };
> > +
> > + seamcall_on_each_cpu(&sc);
> > +
> > tdx_module_status = TDX_MODULE_SHUTDOWN;
> > }
> >
> > @@ -221,6 +281,9 @@ bool platform_tdx_enabled(void)
> > * CPU hotplug is temporarily disabled internally to prevent any cpu
> > * from going offline.
> > *
> > + * Caller also needs to guarantee all CPUs are in VMX operation during
> > + * this function, otherwise Oops may be triggered.
>
> I would *MUCH* rather have this be a:
>
> if (!cpu_feature_enabled(X86_FEATURE_VMX))
> WARN_ONCE("VMX should be on blah blah\n");
>
> than just plain oops. Even a pr_err() that preceded the oops would be
> nicer than an oops that someone has to go decode and then grumble when
> their binutils is too old that it can't disassemble the TDCALL.

I can add this to seamcall():

/*
* SEAMCALL requires CPU being in VMX operation otherwise it causes
#UD.
* Sanity check and return early to avoid Oops. Note cpu_vmx_enabled()
* actually only checks whether VMX is enabled but doesn't check
whether
* CPU is in VMX operation (VMXON is done). There's no way to check
* whether VMXON has been done, but currently enabling VMX and doing
* VMXON are always done together.
*/
if (!cpu_vmx_enabled()) {
WARN_ONCE("CPU is not in VMX operation before making
SEAMCALL");
return -EINVAL;
}

The reason I didn't do is I'd like to make seamcall() simple, that it only
returns TDX_SEAMCALL_VMFAILINVALID or the actual SEAMCALL leaf error. With
above, this function also returns kernel error code, which isn't good.

Alternatively, we can always add EXTABLE to TDX_MODULE_CALL macro to handle #UD
and #GP by returning dedicated error codes (please also see my reply to previous
patch for the code needed to handle), in which case we don't need such check
here.

Always handling #UD in TDX_MODULE_CALL macro also has another advantage: there
will be no Oops for #UD regardless the issue that "there's no way to check
whether VMXON has been done" in the above comment.

What's your opinion?


--
Thanks,
-Kai