Add support to replace in/out instructions in
decompression code with TDX IO hypercalls.
TDX cannot do port IO directly. The TDX module triggers
a #VE exception to let the guest kernel to emulate port
I/O, by converting them into TDX hypercalls to call the
host.
But for the really early code in the decompressor, #VE
cannot be used because the IDT needed for handling the
exception is not set-up, and some other infrastructure
needed by the handler is missing. So to support port IO
in decompressor code, directly replace in/out instructions
with TDX IO hypercalls. This can beeasily achieved by
modifying __in/__out macros.
Also, since TDX IO hypercall requires an IO size parameter,
modify __in/__out macros to accept size as input parameter.
Signed-off-by: Kuppuswamy Sathyanarayanan <[email protected]>
---
arch/x86/boot/compressed/Makefile | 1 +
arch/x86/boot/compressed/tdcall.S | 3 ++
arch/x86/include/asm/io.h | 9 +++---
arch/x86/include/asm/tdx.h | 48 +++++++++++++++++++++++++++++++
4 files changed, 57 insertions(+), 4 deletions(-)
create mode 100644 arch/x86/boot/compressed/tdcall.S
diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 22a2a6cc2ab4..1bfe30ebadbe 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -99,6 +99,7 @@ endif
vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o
vmlinux-objs-$(CONFIG_INTEL_TDX_GUEST) += $(obj)/tdx.o
+vmlinux-objs-$(CONFIG_INTEL_TDX_GUEST) += $(obj)/tdcall.o
vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o
efi-obj-$(CONFIG_EFI_STUB) = $(objtree)/drivers/firmware/efi/libstub/lib.a
diff --git a/arch/x86/boot/compressed/tdcall.S b/arch/x86/boot/compressed/tdcall.S
new file mode 100644
index 000000000000..aafadc136c88
--- /dev/null
+++ b/arch/x86/boot/compressed/tdcall.S
@@ -0,0 +1,3 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include "../../kernel/tdcall.S"
diff --git a/arch/x86/include/asm/io.h b/arch/x86/include/asm/io.h
index be96bf1e667a..391205dace98 100644
--- a/arch/x86/include/asm/io.h
+++ b/arch/x86/include/asm/io.h
@@ -40,6 +40,7 @@
#include <linux/string.h>
#include <linux/compiler.h>
+#include <linux/protected_guest.h>
#include <asm/page.h>
#include <asm/early_ioremap.h>
#include <asm/pgtable_types.h>
@@ -272,25 +273,25 @@ static inline bool sev_key_active(void) { return false; }
#endif /* CONFIG_AMD_MEM_ENCRYPT */
#ifndef __out
-#define __out(bwl, bw) \
+#define __out(bwl, bw, sz) \
asm volatile("out" #bwl " %" #bw "0, %w1" : : "a"(value), "Nd"(port))
#endif
#ifndef __in
-#define __in(bwl, bw) \
+#define __in(bwl, bw, sz) \
asm volatile("in" #bwl " %w1, %" #bw "0" : "=a"(value) : "Nd"(port))
#endif
#define BUILDIO(bwl, bw, type) \
static inline void out##bwl(unsigned type value, int port) \
{ \
- __out(bwl, bw); \
+ __out(bwl, bw, sizeof(type)); \
} \
\
static inline unsigned type in##bwl(int port) \
{ \
unsigned type value; \
- __in(bwl, bw); \
+ __in(bwl, bw, sizeof(type)); \
return value; \
} \
\
diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
index cbfe7479f2a3..ac38ed5b50db 100644
--- a/arch/x86/include/asm/tdx.h
+++ b/arch/x86/include/asm/tdx.h
@@ -9,6 +9,8 @@
#include <asm/cpufeature.h>
#include <linux/types.h>
+#include <vdso/limits.h>
+#include <asm/vmx.h>
/*
* Used in __tdx_module_call() helper function to gather the
@@ -73,6 +75,52 @@ u64 __tdx_hypercall(u64 fn, u64 r12, u64 r13, u64 r14, u64 r15,
bool tdx_protected_guest_has(unsigned long flag);
+/*
+ * To support I/O port access in decompressor or early kernel init
+ * code, since #VE exception handler cannot be used, use paravirt
+ * model to implement __in/__out macros which will in turn be used
+ * by in{b,w,l}()/out{b,w,l} I/O helper macros used in kernel. You
+ * can find the __in/__out macro usage in arch/x86/include/asm/io.h
+ */
+#ifdef BOOT_COMPRESSED_MISC_H
+
+/*
+ * Helper function used for making hypercall for "in"
+ * instruction. It will be called from __in IO macro
+ * If IO is failed, it will return all 1s.
+ */
+static inline unsigned int tdg_in(int size, int port)
+{
+ struct tdx_hypercall_output out = {0};
+ int err;
+
+ err = __tdx_hypercall(EXIT_REASON_IO_INSTRUCTION, size, 0,
+ port, 0, &out);
+
+ return err ? UINT_MAX : out.r11;
+}
+
+#define __out(bwl, bw, sz) \
+do { \
+ if (is_tdx_guest()) { \
+ __tdx_hypercall(EXIT_REASON_IO_INSTRUCTION, sz, 1, \
+ port, value, NULL); \
+ } else { \
+ asm volatile("out" #bwl " %" #bw "0, %w1" : : \
+ "a"(value), "Nd"(port)); \
+ } \
+} while (0)
+#define __in(bwl, bw, sz) \
+do { \
+ if (is_tdx_guest()) { \
+ value = tdg_in(sz, port); \
+ } else { \
+ asm volatile("in" #bwl " %w1, %" #bw "0" : \
+ "=a"(value) : "Nd"(port)); \
+ } \
+} while (0)
+#endif
+
#else // !CONFIG_INTEL_TDX_GUEST
static inline bool is_tdx_guest(void)
--
2.25.1
On Tue, Jun 8, 2021 at 8:40 AM Kuppuswamy Sathyanarayanan
<[email protected]> wrote:
>
> Add support to replace in/out instructions in
> decompression code with TDX IO hypercalls.
>
> TDX cannot do port IO directly. The TDX module triggers
> a #VE exception to let the guest kernel to emulate port
> I/O, by converting them into TDX hypercalls to call the
> host.
>
> But for the really early code in the decompressor, #VE
> cannot be used because the IDT needed for handling the
> exception is not set-up, and some other infrastructure
> needed by the handler is missing. So to support port IO
> in decompressor code, directly replace in/out instructions
> with TDX IO hypercalls. This can beeasily achieved by
> modifying __in/__out macros.
>
> Also, since TDX IO hypercall requires an IO size parameter,
> modify __in/__out macros to accept size as input parameter.
Looks good to me:
Reviewed-by: Dan Williams <[email protected]>