Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/boot: Port I/O: Add decompression-time support for TDX

Port I/O instructions trigger #VE in the TDX environment. In response to
the exception, kernel emulates these instructions using hypercalls.

But during early boot, on the decompression stage, it is cumbersome to
deal with #VE. It is cleaner to go to hypercalls directly, bypassing #VE
handling.

Hook up TDX-specific port I/O helpers if booting in TDX environment.

Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
Link: https://lkml.kernel.org/r/20220405232939.73860-17-kirill.shutemov@linux.intel.com

authored by

Kirill A. Shutemov and committed by
Dave Hansen
4c5b9aac eb4ea1ae

+97 -28
+1 -1
arch/x86/boot/compressed/Makefile
··· 101 101 endif 102 102 103 103 vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o 104 - vmlinux-objs-$(CONFIG_INTEL_TDX_GUEST) += $(obj)/tdx.o 104 + vmlinux-objs-$(CONFIG_INTEL_TDX_GUEST) += $(obj)/tdx.o $(obj)/tdcall.o 105 105 106 106 vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o 107 107 efi-obj-$(CONFIG_EFI_STUB) = $(objtree)/drivers/firmware/efi/libstub/lib.a
+3
arch/x86/boot/compressed/tdcall.S
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + 3 + #include "../../coco/tdx/tdcall.S"
+61
arch/x86/boot/compressed/tdx.c
··· 2 2 3 3 #include "../cpuflags.h" 4 4 #include "../string.h" 5 + #include "../io.h" 6 + #include "error.h" 7 + 8 + #include <vdso/limits.h> 9 + #include <uapi/asm/vmx.h> 5 10 6 11 #include <asm/shared/tdx.h> 12 + 13 + /* Called from __tdx_hypercall() for unrecoverable failure */ 14 + void __tdx_hypercall_failed(void) 15 + { 16 + error("TDVMCALL failed. TDX module bug?"); 17 + } 18 + 19 + static inline unsigned int tdx_io_in(int size, u16 port) 20 + { 21 + struct tdx_hypercall_args args = { 22 + .r10 = TDX_HYPERCALL_STANDARD, 23 + .r11 = EXIT_REASON_IO_INSTRUCTION, 24 + .r12 = size, 25 + .r13 = 0, 26 + .r14 = port, 27 + }; 28 + 29 + if (__tdx_hypercall(&args, TDX_HCALL_HAS_OUTPUT)) 30 + return UINT_MAX; 31 + 32 + return args.r11; 33 + } 34 + 35 + static inline void tdx_io_out(int size, u16 port, u32 value) 36 + { 37 + struct tdx_hypercall_args args = { 38 + .r10 = TDX_HYPERCALL_STANDARD, 39 + .r11 = EXIT_REASON_IO_INSTRUCTION, 40 + .r12 = size, 41 + .r13 = 1, 42 + .r14 = port, 43 + .r15 = value, 44 + }; 45 + 46 + __tdx_hypercall(&args, 0); 47 + } 48 + 49 + static inline u8 tdx_inb(u16 port) 50 + { 51 + return tdx_io_in(1, port); 52 + } 53 + 54 + static inline void tdx_outb(u8 value, u16 port) 55 + { 56 + tdx_io_out(1, port, value); 57 + } 58 + 59 + static inline void tdx_outw(u16 value, u16 port) 60 + { 61 + tdx_io_out(2, port, value); 62 + } 7 63 8 64 void early_tdx_detect(void) 9 65 { ··· 69 13 70 14 if (memcmp(TDX_IDENT, sig, sizeof(sig))) 71 15 return; 16 + 17 + /* Use hypercalls instead of I/O instructions */ 18 + pio_ops.f_inb = tdx_inb; 19 + pio_ops.f_outb = tdx_outb; 20 + pio_ops.f_outw = tdx_outw; 72 21 }
+32
arch/x86/include/asm/shared/tdx.h
··· 2 2 #ifndef _ASM_X86_SHARED_TDX_H 3 3 #define _ASM_X86_SHARED_TDX_H 4 4 5 + #include <linux/bits.h> 6 + #include <linux/types.h> 7 + 8 + #define TDX_HYPERCALL_STANDARD 0 9 + 10 + #define TDX_HCALL_HAS_OUTPUT BIT(0) 11 + #define TDX_HCALL_ISSUE_STI BIT(1) 12 + 5 13 #define TDX_CPUID_LEAF_ID 0x21 6 14 #define TDX_IDENT "IntelTDX " 7 15 16 + #ifndef __ASSEMBLY__ 17 + 18 + /* 19 + * Used in __tdx_hypercall() to pass down and get back registers' values of 20 + * the TDCALL instruction when requesting services from the VMM. 21 + * 22 + * This is a software only structure and not part of the TDX module/VMM ABI. 23 + */ 24 + struct tdx_hypercall_args { 25 + u64 r10; 26 + u64 r11; 27 + u64 r12; 28 + u64 r13; 29 + u64 r14; 30 + u64 r15; 31 + }; 32 + 33 + /* Used to request services from the VMM */ 34 + u64 __tdx_hypercall(struct tdx_hypercall_args *args, unsigned long flags); 35 + 36 + /* Called from __tdx_hypercall() for unrecoverable failure */ 37 + void __tdx_hypercall_failed(void); 38 + 39 + #endif /* !__ASSEMBLY__ */ 8 40 #endif /* _ASM_X86_SHARED_TDX_H */
-27
arch/x86/include/asm/tdx.h
··· 3 3 #ifndef _ASM_X86_TDX_H 4 4 #define _ASM_X86_TDX_H 5 5 6 - #include <linux/bits.h> 7 6 #include <linux/init.h> 8 7 #include <linux/bits.h> 9 8 #include <asm/ptrace.h> 10 9 #include <asm/shared/tdx.h> 11 - 12 - #define TDX_HYPERCALL_STANDARD 0 13 - 14 - #define TDX_HCALL_HAS_OUTPUT BIT(0) 15 - #define TDX_HCALL_ISSUE_STI BIT(1) 16 10 17 11 /* 18 12 * SW-defined error codes. ··· 36 42 }; 37 43 38 44 /* 39 - * Used in __tdx_hypercall() to pass down and get back registers' values of 40 - * the TDCALL instruction when requesting services from the VMM. 41 - * 42 - * This is a software only structure and not part of the TDX module/VMM ABI. 43 - */ 44 - struct tdx_hypercall_args { 45 - u64 r10; 46 - u64 r11; 47 - u64 r12; 48 - u64 r13; 49 - u64 r14; 50 - u64 r15; 51 - }; 52 - 53 - /* 54 45 * Used by the #VE exception handler to gather the #VE exception 55 46 * info from the TDX module. This is a software only structure 56 47 * and not part of the TDX module/VMM ABI. ··· 58 79 /* Used to communicate with the TDX module */ 59 80 u64 __tdx_module_call(u64 fn, u64 rcx, u64 rdx, u64 r8, u64 r9, 60 81 struct tdx_module_output *out); 61 - 62 - /* Used to request services from the VMM */ 63 - u64 __tdx_hypercall(struct tdx_hypercall_args *args, unsigned long flags); 64 - 65 - /* Called from __tdx_hypercall() for unrecoverable failure */ 66 - void __tdx_hypercall_failed(void); 67 82 68 83 void tdx_get_ve_info(struct ve_info *ve); 69 84