Re: [PATCH v2 1/1] x86/tdx: Add __tdcall() and __tdvmcall() helper functions

From: Dave Hansen
Date: Tue Apr 20 2021 - 19:42:06 EST


On 4/20/21 4:12 PM, Kuppuswamy, Sathyanarayanan wrote:
> On 4/20/21 12:59 PM, Dave Hansen wrote:
>> On 4/20/21 12:20 PM, Kuppuswamy, Sathyanarayanan wrote:
>>>>> approach is, it adds a few extra instructions for every
>>>>> TDCALL use case when compared to distributed checks. Although
>>>>> it's a bit less efficient, it's worth it to make the code more
>>>>> readable.
>>>>
>>>> What's a "distributed check"?
>>>
>>> It should be "distributed TDVMCALL/TDCALL inline assembly calls"
>>
>> It's still not clear to what that refers.
>
> I am just comparing the performance cost of using generic
> TDCALL()/TDVMCALL() function implementation with "usage specific"
> (GetQuote,MapGPA, HLT,etc) custom TDCALL()/TDVMCALL() inline assembly
> implementation.

So, I actually had an idea what you were talking about, but I have
*ZERO* idea what "distributed" means in this context.

I think you are trying to say something along the lines of:

Just like syscalls, not all TDVMCALL/TDCALLs use the same set
of argument registers. The implementation here picks the
current worst-case scenario for TDCALL (4 registers). For
TDCALLs with fewer than 4 arguments, there will end up being
a few superfluous (cheap) instructions. But, this approach
maximizes code reuse.


>>>> This also doesn't talk at all about why this approach was
>>>> chosen versus inline assembly. You're going to be asked "why
>>>> not use inline asm?"
>>> "To make the core more readable and less error prone." I have
>>> added this info in above paragraph. Do you think we need more
>>> argument to justify our approach?
>>
>> Yes, you need much more justification. That's pretty generic and
>> non-specific.
> readability is one of the main motivation for not choosing inline

I'm curious. Is there a reason you are not choosing to use
capitalization in your replies? I personally use capitalization as a
visual clue for where a reply starts.

I'm not sure whether this indicates that your keyboard is not
functioning properly, or that these replies are simply not important
enough to warrant the use of the Shift key. Or, is it simply an
oversight? Or, maybe I'm just being overly picky because I've been
working on these exact things with my third-grader a bit too much lately.

Either way, I personally would appreciate your attention to detail in
crafting writing that is easy to parse, since I'm the one that's going
to have to parse it. Details show that you care about the content you
produce. Even if you don't mean it, a lack of attention to detail (even
capital letters) can be perceived to mean that you do not care about
what you write. If you don't care about it, why should the reader?

> assembly. Since number of lines of instructions (with comments) are
> over 70, using inline assembly made it hard to read. Another reason
> is, since we
> are using many registers (R8-R15, R[A-D]X)) in TDVMCAL/TDCALL
> operation, we are not sure whether some older compiler can follow
> our specified inline assembly constraints.

As for the justification, that's much improved. Please include that,
along with some careful review of the grammar.

>>>>> +    movl $TDVMCALL_EXPOSE_REGS_MASK, %ecx
>>>>> +
>>>>> +    tdcall
>>>>> +
>>>>> +    /* Panic if TDCALL reports failure. */
>>>>> +    test %rax, %rax
>>>>> +    jnz 2f
>>>>
>>>> Why panic?
>>> As per spec, TDCALL should never fail. Note that it has nothing to do
>>> with TDVMCALL function specific failure (which is reported via R10).
>>
>> You've introduced two concepts here, without differentiating them.  You
>> need to work to differentiate these two kinds of failure somewhere.  You
>> can't simply refer to both as "failure".
> will clarify it. I have assumed that once the user reads the spec, its
> easier
> to understand.

Your code should be 100% self-supporting without the spec. The spec can
be there in a supportive role to help resolve ambiguity or add fine
detail. But, I think this is a major, repeated problem with this patch
set: it relies too much on reviewers spending quality time with the spec.

>>>> Also, do you *REALLY* need to do this from assembly?  Can't it be done
>>>> in the C wrapper?
>>> Its common for all use cases of TDVMCALL (vendor specific, in/out, etc).
>>> so added
>>> it here.
>>
>> That's not a good reason.  You could just as easily have a C wrapper
>> which all uses of TDVMCALL go through.
> Any reason for not preferring it in assembly code?

Assembly is a last resort. It should only be used for things that
simply can't be written in C or are horrific to understand and manage
when written in C. A single statement like:

BUG_ON(something);

does not qualify in my book as something that's horrific to write in C.

> Also, using wrapper will add more complication for in/out instruction
> substitution use case. please check the use case in following patch.
> https://github.com/intel/tdx/commit/1b73f60aa5bb93554f3b15cd786a9b10b53c1543

I'm seeing a repeated theme here. The approach in this patch series,
and in this email thread in general appears to be one where the patch
submitter is doing as little work as possible and trying to make the
reviewer do as much work as possible.

This is a 300-line diff with all kinds of stuff going on in it. I'm not
sure to what you are referring. You haven't made it easy to figure out.

It would make it a lot easier if you pointed to a specific line, or
copied-and-pasted the code to which you refer. I would really encourage
you to try to make your content easier for reviewers to digest:
Capitalize the start of sentences. Make unambiguous references to code.
Don't blindly cite the spec. Fully express your thoughts.

You'll end up with happier reviewers instead of grumpy ones.

...
>>> More warnings at-least show that we are working
>>> with malicious VMM.
>>
>> That argument does not hold water for me.
>>
>> You can argue that a counter can be kept, or that a WARN_ON_ONCE() is
>> appropriate, or that a printk_ratelimited() is nice.  But, allowing an
>> untrusted software component to write unlimited warnings to the kernel
>> console is utterly nonsensical.
>>
>> By the same argument, any userspace exploit attempts could spew warnings
>> to the console also so that we can tell we are working with malicious
>> userspace.
> In our case, we will get WARN() output only if guest triggers
> TDCALL()/TDVMCALL()
> right? So getting WARN() message for failure of guest triggered call is
> justifiable right?

The output of these calls and thus the error code comes from another
piece of software, either the SEAM module or the VMM.

The error can be from one of several reasons:
1. Guest error/bug where the guest provides a bad value. This is
probably the most likely scenario. But, it's impossible to
differentiate from the other cases because it's a guest bug.
2. SEAM error/bug. If the spec says "SEAM will not do this", then you
can probably justify a WARN_ON_ONCE(). If the call is security-
sensitve, like part of attestation, then you can't meaningfully
recover from it and it probably deserves a BUG_ON().
3. VMM error/bug/malice. Again, you might be able to justify a
WARN_ON_ONCE(). We do that for userspace that might be attacking
the kernel. These are *NEVER* fatal and should be rate-limited.

I don't see *ANYWHERE* in this list where an unbounded, unratelimited
WARN() is appropriate. But, that's just my $0.02.