Messages in this thread | | | Subject | Re: [RFC v2 14/32] x86/tdx: Handle port I/O | From | Dave Hansen <> | Date | Tue, 11 May 2021 07:39:33 -0700 |
| |
On 5/10/21 7:29 PM, Kuppuswamy, Sathyanarayanan wrote: > On 5/10/21 6:07 PM, Dan Williams wrote: >> On Mon, May 10, 2021 at 5:30 PM Kuppuswamy, Sathyanarayanan >> <sathyanarayanan.kuppuswamy@linux.intel.com> wrote: >> [..] >>> It is mainly used by functions like >>> __tdx_hypercall(),__tdx_hypercall_vendor_kvm() >>> and tdx_in{b,w,l}. >>> >>> u64 __tdx_hypercall(u64 fn, u64 r12, u64 r13, u64 r14, u64 r15, >>> struct tdx_hypercall_output *out); >>> u64 __tdx_hypercall_vendor_kvm(u64 fn, u64 r12, u64 r13, u64 r14, >>> u64 r15, struct tdx_hypercall_output >>> *out); >>> >>> struct tdx_hypercall_output { >>> u64 r11; >>> u64 r12; >>> u64 r13; >>> u64 r14; >>> u64 r15; >>> }; >> >> Why is this by register name and not something like: >> >> struct tdx_hypercall_payload { >> u64 data[5]; >> }; >> >> ...because the code in this patch is reading the payload out of a >> stack relative offset, not r11. > > Since this patch allocates this memory in ASM code, we read it via > offset. If you see other use cases in tdx.c, you will notice the use > of register names.
To what you do you refer by "this patch allocates this memory in ASM code"? Could you point to the specific ASM code that "allocates memory"?
Dan I'll try to answer your question. TDX has both a "hypercall" interface for guests to call into hosts and a "seamcall" interface where guests or hosts can talk to the TDX/SEAM module.
Both of these represent an ABI which _resembles_ a system call ABI. Values are placed in registers, including a "function" register which is very similar to a the system call number we place in RAX.
*But* those ABIs was actually designed to (IIRC) resemble the Windows/Microsoft ABI, not the Linux ABI. So the register conventions are unfamiliar. There is assembly code to convert between the ELF function call ABI and the TDX ABIs.
For instance, if you are in C code and you call:
__tdx_hypercall_vendor_kvm(u64 fn, u64 r12, ...
The value for "fn" will be placed in RAX and "r12" will be placed in RDI for the function call itself. The assembly code will, for instance, take the "r12" *VARIABLE* and ensure it gets into the R12 *REGISTER* for the hypercall.
The same thing happens on the output side. The TDX ABIs specify "return" values in certain registers (r11-r15). However, those registers are not preserved in our function return ABI. So, they must be stashed off in memory into a place where the caller can retrieve them.
Rather than being unstructured "data[]", the value in tdx_hypercall_output->r11 was actually in register R11 at some point. If you look at the spec, you can see the functions that use R11.
Let's say there's a hypercall to check for whether puppies are cute. Here's the kernel side:
bool tdx_hypercall_puppies_are_cute() { struct tdx_hypercall_output out; u64 ret;
ret = __tdx_hypercall_vendor_kvm(HOST_LIKES_PUPPIES, ..., &out);
/* Did the hypercall even succeed? */ if (ret != SUCCESS) return -EINVAL;
if (out->r11 == TDX_WHATEVER_CUTE_BIT) return true;
// Nope, I guess puppies are not cute return false; }
The spec would actually say, "Blah blah, puppies are cute if TDX_WHATEVER_CUTE_BIT is set in r11". So, this whole setup actually results in really nice C code that you can sit side-by-side with the spec and see if they agree.
| |