From: Kiryl Shutsemau <kas@kernel.org>
To: Vishal Verma <vishal.l.verma@intel.com>
Cc: x86@kernel.org, Dave Hansen <dave.hansen@linux.intel.com>,
Rick Edgecombe <rick.p.edgecombe@intel.com>,
linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev,
kvm@vger.kernel.org
Subject: Re: [PATCH 2/2] x86/early_printk: Avoid #VE emulation for TDX guest serial output
Date: Fri, 11 Sep 2026 11:49:48 +0100 [thread overview]
Message-ID: <aqPcbMxJiSGGMU-U@thinkstation> (raw)
In-Reply-To: <20260910-b4-tdx_earlyprintk_tdcalls-v1-2-4b2b1bf9001b@intel.com>
On Thu, Sep 10, 2026 at 04:34:09PM -0600, Vishal Verma wrote:
> A TDX guest cannot execute port I/O instructions directly, but
> earlyprintk's serial console still issues plain inb()/outb() and lets
> each one fault into the #VE handler to be emulated as a TDVMCALL.
>
> While that works, it is a roundabout way to get a character out.
> early_serial_putc() polls the LSR, and then writes a byte, but since the
> TDX guest can't directly do port I/O, a #VE exception is raised. The #VE
> handler must call TDG.VP.VEINFO.GET to find out what faulted, and then
> it can issue the TDVMCALL that does the actual work.
>
> This makes #VE a functional mechanism for doing I/O, which is not
> desirable, is unnecessarily complicated and fragile, and results in
> twice the number of calls into the TDX module.
>
> Instead, issue the TDVMCALL directly. In early_printk.c, port access is
> routed through static calls so the MMIO console can substitute its own
> accessors. Add a TDX pair and swap them in the same way.
>
> Note that the output does not appear any earlier - "earlyprintk=" is an
> early_param(), so the console is still registered from
> parse_early_param(). This only changes how the bytes leave the guest
> once it is up.
>
> LLMs were used under supervision to create this patch, to help
> understand the scope and mechanisms, create testing instrumentation
> (throwaway) to count #VEs before/after the change, and to drive lab
> machines to do this testing.
>
> Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
> ---
> arch/x86/kernel/early_printk.c | 48 ++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 48 insertions(+)
>
> diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c
> index cba75306e5b6..4a70799cd80a 100644
> --- a/arch/x86/kernel/early_printk.c
> +++ b/arch/x86/kernel/early_printk.c
> @@ -21,6 +21,8 @@
> #include <linux/usb/xhci-dbgp.h>
> #include <asm/pci_x86.h>
> #include <linux/static_call.h>
> +#include <asm/shared/tdx.h>
> +#include <asm/vmx.h>
>
> /* Simple VGA output */
> #define VGABASE (__ISA_IO_base + 0xb8000)
> @@ -111,6 +113,48 @@ ANNOTATE_NOENDBR_SYM(io_serial_out);
> DEFINE_STATIC_CALL(serial_in, io_serial_in);
> DEFINE_STATIC_CALL(serial_out, io_serial_out);
>
> +#ifdef CONFIG_INTEL_TDX_GUEST
> +/*
> + * A TDX guest cannot execute port I/O instructions, so ask the VMM to do it.
> + */
> +static __noendbr unsigned int tdx_serial_in(unsigned long addr, int offset)
> +{
> + struct tdx_module_args args = {
> + .r10 = TDX_HYPERCALL_STANDARD,
> + .r11 = hcall_func(EXIT_REASON_IO_INSTRUCTION),
> + .r12 = 1, /* One byte */
> + .r13 = TDVMCALL_PORT_READ,
> + .r14 = addr + offset,
> + };
> +
> + if (__tdx_hypercall(&args))
> + return UINT_MAX;
> +
> + return args.r11;
> +}
> +ANNOTATE_NOENDBR_SYM(tdx_serial_in);
> +
> +static __noendbr void tdx_serial_out(unsigned long addr, int offset, int value)
> +{
> + /* One byte */
> + _tdx_hypercall(hcall_func(EXIT_REASON_IO_INSTRUCTION), 1,
> + TDVMCALL_PORT_WRITE, addr + offset, value);
> +}
> +ANNOTATE_NOENDBR_SYM(tdx_serial_out);
> +
> +/* Substitute the hypercall accessors, but only in an actual TDX guest */
> +static __init void early_serial_tdx_init(void)
> +{
> + if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST))
> + return;
> +
> + static_call_update(serial_in, tdx_serial_in);
> + static_call_update(serial_out, tdx_serial_out);
> +}
> +#else
> +static inline void early_serial_tdx_init(void) { }
> +#endif /* CONFIG_INTEL_TDX_GUEST */
> +
I don't particularly like this being in early_printk.c
Maybe coco/tdx/tdx.c should provide tdx_inb() and tdx_outb() helpers
that we just hook up here?
--
Kiryl Shutsemau / Kirill A. Shutemov
next prev parent reply other threads:[~2026-09-11 10:49 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-10 22:34 [PATCH 0/2] x86/tdx: Use TDVMCALLs directly for earlyprintk Vishal Verma
2026-09-10 22:34 ` [PATCH 1/2] x86/tdx: Move port I/O definitions to a shared header Vishal Verma
2026-09-11 10:40 ` Kiryl Shutsemau
2026-09-10 22:34 ` [PATCH 2/2] x86/early_printk: Avoid #VE emulation for TDX guest serial output Vishal Verma
2026-09-10 22:48 ` sashiko-bot
2026-09-10 23:44 ` Verma, Vishal L
2026-09-11 10:49 ` Kiryl Shutsemau [this message]
2026-09-11 20:25 ` Dave Hansen
2026-09-11 1:17 ` [PATCH 0/2] x86/tdx: Use TDVMCALLs directly for earlyprintk Edgecombe, Rick P
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aqPcbMxJiSGGMU-U@thinkstation \
--to=kas@kernel.org \
--cc=dave.hansen@linux.intel.com \
--cc=kvm@vger.kernel.org \
--cc=linux-coco@lists.linux.dev \
--cc=linux-kernel@vger.kernel.org \
--cc=rick.p.edgecombe@intel.com \
--cc=vishal.l.verma@intel.com \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.