Received: by 2002:a05:6a10:206:0:0:0:0 with SMTP id 6csp227102pxj; Wed, 26 May 2021 21:26:09 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzYqL95nPkoBsSjLbyNvqj9ylcpeUW7RCRvRuFJbZcM6cxlHv2wWvogIJXWnsGf2QxVfYWH X-Received: by 2002:a05:6602:2e82:: with SMTP id m2mr1276555iow.190.1622089568885; Wed, 26 May 2021 21:26:08 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1622089568; cv=none; d=google.com; s=arc-20160816; b=xUjvyRBjWAE62acahCzjBH3IS0507LU6+jBoyTR/RDRCCy/ic+mJNb3H1Y9WFpmDst bM+X43kRluOc4QXAHOmJECbF/wcJDHkaT2TZzE8ebGOK+PS0zfxOI3Wg2kYeKpk736UK uN1UhdnWwQ/iyR3Rt+y8nAWOzC/zO1I9sX8YpvVovRzf9TC1+iIfHKhArmodd7UVuS3D ut+tvOKw8dYI10IDIADI3/p6Pi0ZhVPm5KppJ62o4wok95rxZ200NnE5e2eR6Aj4cj0S HXHWlmmSPH6G7aVfA9MedZN1NrlaGpqb4tJVZ//CCFaryQd84bHkewzPLv2Ama46zUEG 5TYg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :ironport-sdr:ironport-sdr; bh=UsREBZ8rwCJaser4jmoABzA6nC+wnWuQmrew9blukJs=; b=Duyp7I5BkKv3aThzLhwoQRvB0Lh+5jAYV6pyA3ifzett5pdVG4JG+6lJg7lKpgG89+ vidfV7GSf1dnaZUmmZeaUaK27B8bUBtz/ayjhZXKXtK234iQB8Hgcl09wNirZRPevbRK XMCYDvVd31KjKPDm5KWLgHT6OjJfPqfA4zSDYerCdzsdlmaNocPaB2SYA/Xb0uQquTyu tW5c1Rb4iO+bUwvlceb9WBXbvlC1UsWyAtxH4s6czB1Hc4POJF8WBLxUG9OrscoUfgTJ FbGcofquwaUqAwYcxMu8JeGZaOt9iGkP7UO8v40ksBjFi65REZH7M4fYWgZU1TQ0KvDD IkmQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id l11si1327291jah.65.2021.05.26.21.25.54; Wed, 26 May 2021 21:26:08 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229836AbhE0EZv (ORCPT + 99 others); Thu, 27 May 2021 00:25:51 -0400 Received: from mga01.intel.com ([192.55.52.88]:41864 "EHLO mga01.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229744AbhE0EZr (ORCPT ); Thu, 27 May 2021 00:25:47 -0400 IronPort-SDR: Xays9yGgyJqM+lcB9cKrc9Geij6p4ic4GuIzdNV78iXSuhvk7Wv8b8M3wLvX1Kl82vBc3K8ruv 92ODJF4TVsOw== X-IronPort-AV: E=McAfee;i="6200,9189,9996"; a="223844428" X-IronPort-AV: E=Sophos;i="5.82,333,1613462400"; d="scan'208";a="223844428" Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by fmsmga101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 26 May 2021 21:24:14 -0700 IronPort-SDR: H3e85iHs8TwK6U/umaRoYxMzR9vndp8fNXN4pa7zc2C8Pg7RMXRfS97cwB0XjjvpD9ezJHAKy4 f/owgpyGuXJg== X-IronPort-AV: E=Sophos;i="5.82,333,1613462400"; d="scan'208";a="480391418" Received: from skgangad-mobl.amr.corp.intel.com (HELO skuppusw-desk1.amr.corp.intel.com) ([10.254.33.45]) by fmsmga002-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 26 May 2021 21:24:13 -0700 From: Kuppuswamy Sathyanarayanan To: Peter Zijlstra , Andy Lutomirski , Dave Hansen , Tony Luck , Dan Williams Cc: Andi Kleen , Kirill Shutemov , Kuppuswamy Sathyanarayanan , Raj Ashok , Sean Christopherson , Kuppuswamy Sathyanarayanan , linux-kernel@vger.kernel.org Subject: [RFC v2-fix-v1 3/3] x86/tdx: Handle port I/O Date: Wed, 26 May 2021 21:23:56 -0700 Message-Id: <20210527042356.3983284-4-sathyanarayanan.kuppuswamy@linux.intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210527042356.3983284-1-sathyanarayanan.kuppuswamy@linux.intel.com> References: <20210527042356.3983284-1-sathyanarayanan.kuppuswamy@linux.intel.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: "Kirill A. Shutemov" TDX hypervisors cannot emulate instructions directly. This includes port IO which is normally emulated in the hypervisor. All port IO instructions inside TDX trigger the #VE exception in the guest and would be normally emulated there. 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, add support for paravirt based I/O port virtualization. Also string I/O is not supported in TDX guest. So, unroll the string I/O operation into a loop operating on one element at a time. This method is similar to AMD SEV, so just extend the support for TDX guest platform. Co-developed-by: Kuppuswamy Sathyanarayanan Signed-off-by: Kuppuswamy Sathyanarayanan Signed-off-by: Kirill A. Shutemov Reviewed-by: Andi Kleen --- arch/x86/boot/compressed/Makefile | 1 + arch/x86/boot/compressed/tdcall.S | 3 ++ arch/x86/boot/compressed/tdx.c | 28 ++++++++++++++++++ arch/x86/include/asm/io.h | 7 +++-- arch/x86/include/asm/tdx.h | 47 ++++++++++++++++++++++++++++++- arch/x86/kernel/tdx.c | 39 +++++++++++++++++++++++++ 6 files changed, 122 insertions(+), 3 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 a2554621cefe..a944a2038797 100644 --- a/arch/x86/boot/compressed/Makefile +++ b/arch/x86/boot/compressed/Makefile @@ -97,6 +97,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/boot/compressed/tdx.c b/arch/x86/boot/compressed/tdx.c index 0a87c1775b67..cb20962c7da6 100644 --- a/arch/x86/boot/compressed/tdx.c +++ b/arch/x86/boot/compressed/tdx.c @@ -4,6 +4,8 @@ */ #include +#include +#include static int __ro_after_init tdx_guest = -1; @@ -30,3 +32,29 @@ bool is_tdx_guest(void) return !!tdx_guest; } +/* + * Helper function used for making hypercall for "out" + * instruction. It will be called from __out IO + * macro (in tdx.h). + */ +void tdg_out(int size, int port, unsigned int value) +{ + __tdx_hypercall(EXIT_REASON_IO_INSTRUCTION, size, 1, + port, value, NULL); +} + +/* + * Helper function used for making hypercall for "in" + * instruction. It will be called from __in IO macro + * (in tdx.h). If IO is failed, it will return all 1s. + */ +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; +} diff --git a/arch/x86/include/asm/io.h b/arch/x86/include/asm/io.h index ef7a686a55a9..daa75c8eef5d 100644 --- a/arch/x86/include/asm/io.h +++ b/arch/x86/include/asm/io.h @@ -40,6 +40,7 @@ #include #include +#include #include #include #include @@ -309,7 +310,8 @@ static inline unsigned type in##bwl##_p(int port) \ \ static inline void outs##bwl(int port, const void *addr, unsigned long count) \ { \ - if (sev_key_active()) { \ + if (sev_key_active() || \ + protected_guest_has(VM_UNROLL_STRING_IO)) { \ unsigned type *value = (unsigned type *)addr; \ while (count) { \ out##bwl(*value, port); \ @@ -325,7 +327,8 @@ static inline void outs##bwl(int port, const void *addr, unsigned long count) \ \ static inline void ins##bwl(int port, void *addr, unsigned long count) \ { \ - if (sev_key_active()) { \ + if (sev_key_active() || \ + protected_guest_has(VM_UNROLL_STRING_IO)) { \ unsigned type *value = (unsigned type *)addr; \ while (count) { \ *value = in##bwl(port); \ diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index e880a9dd40d3..6ba2dcea533f 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -5,6 +5,8 @@ #define TDX_CPUID_LEAF_ID 0x21 +#ifndef __ASSEMBLY__ + #ifdef CONFIG_INTEL_TDX_GUEST #include @@ -74,6 +76,48 @@ u64 __tdx_hypercall(u64 fn, u64 r12, u64 r13, u64 r14, u64 r15, bool tdx_protected_guest_has(unsigned long flag); bool tdg_early_handle_ve(struct pt_regs *regs); +void tdg_out(int size, int port, unsigned int value); +unsigned int tdg_in(int size, int port); + +/* Helper function for converting {b,w,l} to byte size */ +static inline int tdx_get_iosize(char *str) +{ + if (str[0] == 'w') + return 2; + else if (str[0] == 'l') + return 4; + + return 1; +} + +/* + * 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 +#define __out(bwl, bw) \ +do { \ + if (is_tdx_guest()) { \ + tdg_out(tdx_get_iosize(#bwl), port, value); \ + } else { \ + asm volatile("out" #bwl " %" #bw "0, %w1" : : \ + "a"(value), "Nd"(port)); \ + } \ +} while (0) +#define __in(bwl, bw) \ +do { \ + if (is_tdx_guest()) { \ + value = tdg_in(tdx_get_iosize(#bwl), 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) @@ -161,6 +205,7 @@ static inline long tdx_kvm_hypercall4(unsigned int nr, unsigned long p1, { return -ENODEV; } -#endif /* CONFIG_INTEL_TDX_GUEST_KVM */ +#endif /* CONFIG_INTEL_TDX_GUEST_KVM */ +#endif /* __ASSEMBLY__ */ #endif /* _ASM_X86_TDX_H */ diff --git a/arch/x86/kernel/tdx.c b/arch/x86/kernel/tdx.c index ca3442b7accf..4a84487ee8ff 100644 --- a/arch/x86/kernel/tdx.c +++ b/arch/x86/kernel/tdx.c @@ -202,6 +202,42 @@ static void tdg_handle_cpuid(struct pt_regs *regs) regs->dx = out.r15; } +void tdg_out(int size, int port, unsigned int value) +{ + tdx_hypercall(EXIT_REASON_IO_INSTRUCTION, size, 1, port, value); +} + +unsigned int tdg_in(int size, int port) +{ + struct tdx_hypercall_output out = {0}; + u64 err; + + err = __tdx_hypercall(EXIT_REASON_IO_INSTRUCTION, size, 0, + port, 0, &out); + + return err ? UINT_MAX : out.r11; +} + +static void tdg_handle_io(struct pt_regs *regs, u32 exit_qual) +{ + bool string = exit_qual & 16; + int out, size, port; + + /* I/O strings ops are unrolled at build time. */ + BUG_ON(string); + + out = VE_GET_IO_TYPE(exit_qual); + size = VE_GET_IO_SIZE(exit_qual); + port = VE_GET_PORT_NUM(exit_qual); + + if (out) { + tdg_out(size, port, regs->ax); + } else { + regs->ax &= ~GENMASK(8 * size, 0); + regs->ax |= tdg_in(size, port) & GENMASK(8 * size, 0); + } +} + unsigned long tdg_get_ve_info(struct ve_info *ve) { u64 ret; @@ -248,6 +284,9 @@ int tdg_handle_virtualization_exception(struct pt_regs *regs, case EXIT_REASON_CPUID: tdg_handle_cpuid(regs); break; + case EXIT_REASON_IO_INSTRUCTION: + tdg_handle_io(regs, ve->exit_qual); + break; default: pr_warn("Unexpected #VE: %lld\n", ve->exit_reason); return -EFAULT; -- 2.25.1