Re: [RFC v2 05/32] x86/tdx: Add __tdcall() and __tdvmcall() helper functions
From: Dave Hansen
Date: Tue Apr 27 2021 - 10:29:44 EST
On 4/26/21 7:29 PM, Kuppuswamy, Sathyanarayanan wrote:
> On 4/26/21 4:17 PM, Dave Hansen wrote:
>> On 4/26/21 3:31 PM, Kuppuswamy, Sathyanarayanan wrote:
>>>>> +#define tdcall .byte 0x66,0x0f,0x01,0xcc
>>>>> +
>>>>> +/*
>>>>> + * __tdcall() - Used to communicate with the TDX module
>>>>
>>>> Why is this function here? What does it do? Why do we need it?
>>>
>>> __tdcall() function is used to request services from the TDX Module.
>>> Example use cases are, TDREPORT, VEINFO, TDINFO, etc.
>>
>> I think there might be some misinterpretation of my question. What you
>> are describing is what *TDCALL* does. Why do we need a wrapper
>> function? What purpose does this wrapper function serve? Why do we
>> need this wrapper function?
>>
> How about following explanation?
>
> Helper function for "tdcall" instruction, which can be used to request
> services from the TDX module (does not include VMM). Few examples of
> valid TDX module services are, "TDREPORT", "MEM PAGE ACCEPT", "VEINFO",
> etc.
Naming the services here is not useful. If I want to know who calls
this, I'll just literally do that: look up the callers of this function.
> This function serves as a wrapper to move user call arguments to
> the correct registers as specified by "tdcall" ABI and shares it with
> the TDX module. If the "tdcall" operation is successful and a
> valid "struct tdcall_out" pointer is available (in "out" argument),
> output from the TDX module (RCX, RDX, R8-R11) is saved to the memory
> specified in the "out" pointer. Also the status of the "tdcall"
> operation is returned back to the user as a function return value.
I tend to prefer function comments that talk high-level about what the
function does rather than waste space on the exact registers used in the
ABI. I also tend not to talk about things that can be trivially grepped
for, like the callers of this function.
I'd trim the fat out of there, but it's generally OK, although too
rotund for my taste.
>>>>> + /* Move TDCALL Leaf ID to RAX */
>>>>> + mov %rdi, %rax
>>>>> + /* Move output pointer to R12 */
>>>>> + mov %r9, %r12
>>>>
>>>> I thought 'struct tdcall_output' was a purely software construct. Why
>>>> are we passing a pointer to it into TDCALL?
>>>
>>> Its used to store the TDCALL result (RCX, RDX, R8-R11). As far as this
>>> function is concerned, its just a block of memory (accessed using
>>> base address + TDCALL_r* offsets).
>>
>> Is 'struct tdcall_output' a hardware architectural structure or a
>> software structure?
>>
>> If it's a software structure, then why are we passing a pointer to a
>> software structure into a hardware ABI?
>>
>> If it's a hardware architecture structure, where is the documentation
>> for it?
>>
>
> I think there is a misunderstanding here. We don't share the tdcall_output
> pointer with the TDX module. Current use cases of TDCALL (other than
> TDVMCALL)
> do not use registers from R12-R15. Since the registers R12-R15 are free and
> available, we are using R12 as temporary storage to hold the tdcall_output
> pointer.
In other words, 'struct tdcall_output' is a purely software concept.
However, its pointer is manipulated literally next to all of the TDCALL
register arguments and it has an *IDENTICAL* comment to all of those
other moves.
Please make it clear that %r12 is not being used at all for the TDCALL
instruction itself.
But, the bigger point here is that the code needs to be structured in a
way that makes the function and interactions clear. If I want to know
more about the "output pointer", where do I go? Do I go looking at the
calling functions or the TDINFO instruction reference?
...
>> Please go look at some other assembly code in the kernel called from C.
>> Do those functions do this? Why? Why not? Do they care about
>> "passing stale values back up"?
>
> Maybe I am being overly cautious here. Since TDX module is the trusted
> code, speculation attack is not a consideration here. I will remove this
> block of code.
Caution is OK. Caution without explanation somewhere as to why it is
warranted is not.
> Do we need to rename the helper functions ?
>
> tdvmcall(), tdvmcall_out_r11()
Yes.
> Also what about output structs?
>
> struct tdcall_output
> struct tdvmcall_output
Yes, they need sane, straightforward names which are not confusing too.
>>>>> +/* Helper function for standard type of TDVMCALL */
>>>>> +SYM_FUNC_START(__tdvmcall)
>>>>> + /* Set TDVMCALL type info (0 - Standard, > 0 - vendor) in R10 */
>>>>> + xor %r10, %r10
>>>>> + call do_tdvmcall
>>>>> + retq
>>>>> +SYM_FUNC_END(__tdvmcall)
>>>>
>>>> Why do we need this helper? Why does it need to be in assembly?
>>>
>>> Its simpler to do it in assembly. Also, grouping all register updates
>>> in the same file will make it easier for us to read or debug issues.
>>> Another
>>> reason is, we also call do_tdvmcall() from in/out instruction use case.
>>
>> Sathya, I seem to have to reverse-engineer what you are doing for all
>> this stuff. Your answers to my questions are almost entirely orthogonal
>> to the things I really want to know. I guess I need to be more precise
>> with the questions I'm asking. But, this is yet another case where I
>> think the burden for this series continues to fall on the reviewer
>> rather than the submitter. Not the way I think it is best.
>
> I have assumed that you are aware of reason for the existence of
> do_tdvmcall() helper function. It is mainly created to hold common
> code between vendor specific and standard type of tdvmcall's.
No, I was not aware of that. Remember, you're not doing this for *ME*.
You're doing it for the hundred other people that are going to look
over the code and who won't have been aware of your reasoning.