From nobody Sat May 4 21:18:11 2024 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 31814C433EF for ; Tue, 24 May 2022 04:06:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234161AbiEXEGK (ORCPT ); Tue, 24 May 2022 00:06:10 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47408 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234132AbiEXEGC (ORCPT ); Tue, 24 May 2022 00:06:02 -0400 Received: from mga07.intel.com (mga07.intel.com [134.134.136.100]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7176B8FF80 for ; Mon, 23 May 2022 21:05:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1653365159; x=1684901159; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=IncPPs933HBWmJiPyuJDNZ7LaB4FaYtQUR7THXlvi4o=; b=VaosMGMphZfIqzq0cbIAoCyfKQmmktBpOhw5oZ1Bkyy9v19Od6m9gDJe 9r5pLiLQ2ka8p+GKVPThH89cZcxBQlq1sXiGao133wUvGl1KFiPxGwrn/ 9XrRtZ2wgc/FBYh18WrPfkREbJJ4IrDUpwzKgciJMIMY/Cw/r945h55RI 6J/PiXlPxMLsyO8xoEJn6/lVi2tOfoWeoPo3SBhRnPklWNCR+oEN7jmMN ePFinF3kXsSwv65tJfDY/rsAQA9xG7l6N25QzGyMuTuDyNjYOkCA1ysz+ ONBrIs0KT1w1nPInfiJMx5TGyz7bucnI/of4e7I+mt5guCHvlvLH2ci+S w==; X-IronPort-AV: E=McAfee;i="6400,9594,10356"; a="336479186" X-IronPort-AV: E=Sophos;i="5.91,248,1647327600"; d="scan'208";a="336479186" Received: from orsmga003.jf.intel.com ([10.7.209.27]) by orsmga105.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 May 2022 21:05:56 -0700 X-IronPort-AV: E=Sophos;i="5.91,248,1647327600"; d="scan'208";a="526242029" Received: from jwosulli-mobl1.ger.corp.intel.com (HELO skuppusw-desk1.home) ([10.212.165.122]) by orsmga003-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 May 2022 21:05:54 -0700 From: Kuppuswamy Sathyanarayanan To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org Cc: "H . Peter Anvin" , Kuppuswamy Sathyanarayanan , "Kirill A . Shutemov" , Tony Luck , Andi Kleen , Kai Huang , Wander Lairson Costa , Isaku Yamahata , marcelo.cerri@canonical.com, tim.gardner@canonical.com, khalid.elmously@canonical.com, philip.cox@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v7 1/5] x86/tdx: Add TDX Guest attestation interface driver Date: Mon, 23 May 2022 21:05:13 -0700 Message-Id: <20220524040517.703581-2-sathyanarayanan.kuppuswamy@linux.intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220524040517.703581-1-sathyanarayanan.kuppuswamy@linux.intel.com> References: <20220524040517.703581-1-sathyanarayanan.kuppuswamy@linux.intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" In TDX guest, attestation is used to verify the trustworthiness of a TD to other entities before provisioning secrets to the TD. One usage example is, when a TD guest uses encrypted drive and if the decryption keys required to access the drive are stored in a secure 3rd party keyserver, the key server can use attestation to verify TD's trustworthiness and release the decryption keys to the TD. The attestation process consists of two steps: TDREPORT generation and Quote generation. TDREPORT (TDREPORT_STRUCT) is a fixed-size data structure generated by the TDX module which contains TD-specific information (such as TD measurements), platform security version, and the MAC to protect the integrity of the TDREPORT. The TD kernel uses TDCALL[TDG.MR.REPORT] to get the TDREPORT from the TDX module. A user-provided 64-Byte REPORTDATA is used as input and included in the TDREPORT. Typically it can be some nonce provided by attestation service so the TDREPORT can be verified uniquely. More details about TDREPORT can be found in Intel TDX Module specification, section titled "TDG.MR.REPORT Leaf". TDREPORT can only be verified on local platform as the MAC key is bound to the platform. To support remote verification of the TDREPORT, TDX leverages Intel SGX Quote Enclave (QE) to verify the TDREPORT locally and convert it to a remote verifiable Quote. After getting the TDREPORT, the second step of the attestation process is to send it to the QE to generate the Quote. TDX doesn't support SGX inside the TD, so the QE can be deployed in the host, or in another legacy VM with SGX support. How to send the TDREPORT to QE and receive the Quote is implementation and deployment specific. Implement a basic attestation driver to allow TD userspace to get the TDREPORT. The TD userspace attestation software can get the TDREPORT and then choose whatever communication channel available (i.e. vsock) to send the TDREPORT to QE and receive the Quote. Also note that explicit access permissions are not enforced in this driver because the quote and measurements are not a secret. However the access permissions of the device node can be used to set any desired access policy. The udev default is usually root access only. Operations like getting TDREPORT or Quote generation involves sending a blob of data as input and getting another blob of data as output. It was considered to use a sysfs interface for this, but it doesn't fit well into the standard sysfs model for configuring values. It would be possible to do read/write on files, but it would need multiple file descriptors, which would be somewhat messy. IOCTLs seems to be the best fitting and simplest model for this use case. Also, the REPORTDATA used in TDREPORT generation can possibly come from attestation service to uniquely verify the Quote (like per instance verification). In such case, since REPORTDATA is a secret, using sysfs to share it is insecure compared to sending it via IOCTL. Reviewed-by: Tony Luck Reviewed-by: Andi Kleen Acked-by: Kirill A. Shutemov Signed-off-by: Kuppuswamy Sathyanarayanan Acked-by: Kai Huang Acked-by: Wander Lairson Costa --- arch/x86/coco/tdx/Makefile | 2 +- arch/x86/coco/tdx/attest.c | 118 ++++++++++++++++++++++++++++++++ arch/x86/include/uapi/asm/tdx.h | 42 ++++++++++++ 3 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 arch/x86/coco/tdx/attest.c create mode 100644 arch/x86/include/uapi/asm/tdx.h diff --git a/arch/x86/coco/tdx/Makefile b/arch/x86/coco/tdx/Makefile index 46c55998557d..d2db3e6770e5 100644 --- a/arch/x86/coco/tdx/Makefile +++ b/arch/x86/coco/tdx/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 =20 -obj-y +=3D tdx.o tdcall.o +obj-y +=3D tdx.o tdcall.o attest.o diff --git a/arch/x86/coco/tdx/attest.c b/arch/x86/coco/tdx/attest.c new file mode 100644 index 000000000000..24db0bad4923 --- /dev/null +++ b/arch/x86/coco/tdx/attest.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * attest.c - TDX guest attestation interface driver. + * + * Implements user interface to trigger attestation process. + * + * Copyright (C) 2022 Intel Corporation + * + */ + +#define pr_fmt(fmt) "x86/tdx: attest: " fmt + +#include +#include +#include +#include +#include + +#define DRIVER_NAME "tdx-attest" + +/* TDREPORT module call leaf ID */ +#define TDX_GET_REPORT 4 + +static struct miscdevice miscdev; + +static long tdx_get_report(void __user *argp) +{ + void *reportdata =3D NULL, *tdreport =3D NULL; + long ret; + + /* Allocate buffer space for REPORTDATA */ + reportdata =3D kmalloc(TDX_REPORTDATA_LEN, GFP_KERNEL); + if (!reportdata) + return -ENOMEM; + + /* Allocate buffer space for TDREPORT */ + tdreport =3D kmalloc(TDX_REPORT_LEN, GFP_KERNEL); + if (!tdreport) { + ret =3D -ENOMEM; + goto out; + } + + /* Copy REPORTDATA from the user buffer */ + if (copy_from_user(reportdata, argp, TDX_REPORTDATA_LEN)) { + ret =3D -EFAULT; + goto out; + } + + /* + * Generate TDREPORT using "TDG.MR.REPORT" TDCALL. + * + * Get the TDREPORT using REPORTDATA as input. Refer to + * section 22.3.3 TDG.MR.REPORT leaf in the TDX Module 1.0 + * Specification for detailed information. + */ + ret =3D __tdx_module_call(TDX_GET_REPORT, virt_to_phys(tdreport), + virt_to_phys(reportdata), 0, 0, NULL); + if (ret) { + pr_debug("TDREPORT TDCALL failed, status:%lx\n", ret); + ret =3D -EIO; + goto out; + } + + /* Copy TDREPORT back to the user buffer */ + if (copy_to_user(argp, tdreport, TDX_REPORT_LEN)) + ret =3D -EFAULT; + +out: + kfree(reportdata); + kfree(tdreport); + return ret; +} + +static long tdx_attest_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp =3D (void __user *)arg; + long ret =3D -EINVAL; + + switch (cmd) { + case TDX_CMD_GET_REPORT: + ret =3D tdx_get_report(argp); + break; + default: + pr_debug("cmd %d not supported\n", cmd); + break; + } + + return ret; +} + +static const struct file_operations tdx_attest_fops =3D { + .owner =3D THIS_MODULE, + .unlocked_ioctl =3D tdx_attest_ioctl, + .llseek =3D no_llseek, +}; + +static int __init tdx_attestation_init(void) +{ + int ret; + + /* Make sure we are in a valid TDX platform */ + if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) + return -EIO; + + miscdev.name =3D DRIVER_NAME; + miscdev.minor =3D MISC_DYNAMIC_MINOR; + miscdev.fops =3D &tdx_attest_fops; + + ret =3D misc_register(&miscdev); + if (ret) { + pr_err("misc device registration failed\n"); + return ret; + } + + return 0; +} +device_initcall(tdx_attestation_init) diff --git a/arch/x86/include/uapi/asm/tdx.h b/arch/x86/include/uapi/asm/td= x.h new file mode 100644 index 000000000000..8b57dea67eab --- /dev/null +++ b/arch/x86/include/uapi/asm/tdx.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_ASM_X86_TDX_H +#define _UAPI_ASM_X86_TDX_H + +#include +#include + +/* Length of the REPORTDATA used in TDG.MR.REPORT TDCALL */ +#define TDX_REPORTDATA_LEN 64 + +/* Length of TDREPORT used in TDG.MR.REPORT TDCALL */ +#define TDX_REPORT_LEN 1024 + +/** + * struct tdx_report_req: Get TDREPORT using REPORTDATA as input. + * + * @reportdata : User-defined 64-Byte REPORTDATA to be included into + * TDREPORT. Typically it can be some nonce provided by + * attestation service, so the generated TDREPORT can be + * uniquely verified. + * @tdreport : TDREPORT output from TDCALL[TDG.MR.REPORT] of size + * TDX_REPORT_LEN. + * + * Used in TDX_CMD_GET_REPORT IOCTL request. + */ +struct tdx_report_req { + union { + __u8 reportdata[TDX_REPORTDATA_LEN]; + __u8 tdreport[TDX_REPORT_LEN]; + }; +}; + +/* + * TDX_CMD_GET_REPORT - Get TDREPORT using TDCALL[TDG.MR.REPORT] + * + * Return 0 on success, -EIO on TDCALL execution failure, and + * standard errno on other general error cases. + * + */ +#define TDX_CMD_GET_REPORT _IOWR('T', 0x01, struct tdx_report_req) + +#endif /* _UAPI_ASM_X86_TDX_H */ --=20 2.25.1 From nobody Sat May 4 21:18:11 2024 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0737AC433F5 for ; Tue, 24 May 2022 04:06:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233628AbiEXEGX (ORCPT ); Tue, 24 May 2022 00:06:23 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47412 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234135AbiEXEGC (ORCPT ); Tue, 24 May 2022 00:06:02 -0400 Received: from mga07.intel.com (mga07.intel.com [134.134.136.100]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5C1232A6 for ; Mon, 23 May 2022 21:05:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1653365159; x=1684901159; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=hDDJt6MYhoCh7Rj4OmR2N48edxZ1+wqyexSmp2CDOh0=; b=WtxXLKpWDHXzOL7yzRV0f99RdT7B6jS1V+sVVI3oaOy36Nz1SzcyYFfS 4nz1ljaSYMEYnOi9ceeZApdFUIjJSPgNUUfE2771B/ro8BhqgEp//pFeX b8Uv5j17Y6Rl5qpWF34Ry4yoxi8kdvPsi5x+wFudx/I25JarNrs6fjtgH 948DQQrMKA+KIUlHDmhG3VEB7dmpi4FRdKhsY7wDS1D05EbhMF8ybX9TJ KqCkqUBns6P8EgCi7rsLulUJQO0M12vE5glAzZfbydNHjZ+bqxjDHyRSg QRpnwBvQzZu16Bj1E0r7dy08tBvood9HYOQNDbszjIT6cF+sKMPT3bU3b g==; X-IronPort-AV: E=McAfee;i="6400,9594,10356"; a="336479187" X-IronPort-AV: E=Sophos;i="5.91,248,1647327600"; d="scan'208";a="336479187" Received: from orsmga003.jf.intel.com ([10.7.209.27]) by orsmga105.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 May 2022 21:05:56 -0700 X-IronPort-AV: E=Sophos;i="5.91,248,1647327600"; d="scan'208";a="526242033" Received: from jwosulli-mobl1.ger.corp.intel.com (HELO skuppusw-desk1.home) ([10.212.165.122]) by orsmga003-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 May 2022 21:05:55 -0700 From: Kuppuswamy Sathyanarayanan To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org Cc: "H . Peter Anvin" , Kuppuswamy Sathyanarayanan , "Kirill A . Shutemov" , Tony Luck , Andi Kleen , Kai Huang , Wander Lairson Costa , Isaku Yamahata , marcelo.cerri@canonical.com, tim.gardner@canonical.com, khalid.elmously@canonical.com, philip.cox@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v7 2/5] x86/tdx: Add TDX Guest event notify interrupt support Date: Mon, 23 May 2022 21:05:14 -0700 Message-Id: <20220524040517.703581-3-sathyanarayanan.kuppuswamy@linux.intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220524040517.703581-1-sathyanarayanan.kuppuswamy@linux.intel.com> References: <20220524040517.703581-1-sathyanarayanan.kuppuswamy@linux.intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Host-guest event notification via configured interrupt vector is useful in cases where a guest makes an asynchronous request and needs a callback from the host to indicate the completion or to let the host notify the guest about events like device removal. One usage example is, callback requirement of GetQuote asynchronous hypercall. In TDX guest, SetupEventNotifyInterrupt hypercall can be used by the guest to specify which interrupt vector to use as an event-notify vector to the VMM. Details about the SetupEventNotifyInterrupt hypercall can be found in TDX Guest-Host Communication Interface (GHCI) Specification, sec 3.5 "VP.VMCALL". Add a tdx_hcall_set_notify_intr() helper function to implement the SetupEventNotifyInterrupt hypercall. Reserve 0xec IRQ vector address for TDX guest to receive the event completion notification from VMM. Also add related IDT handler to process the notification event. Add support to track the notification event status via /proc/interrupts. Reviewed-by: Tony Luck Reviewed-by: Andi Kleen Acked-by: Kirill A. Shutemov Acked-by: Wander Lairson Costa Signed-off-by: Kuppuswamy Sathyanarayanan --- arch/x86/coco/tdx/tdx.c | 73 ++++++++++++++++++++++++++++++ arch/x86/include/asm/hardirq.h | 3 ++ arch/x86/include/asm/idtentry.h | 4 ++ arch/x86/include/asm/irq_vectors.h | 7 ++- arch/x86/include/asm/tdx.h | 4 ++ arch/x86/kernel/irq.c | 7 +++ 6 files changed, 97 insertions(+), 1 deletion(-) diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c index 03deb4d6920d..b49211994864 100644 --- a/arch/x86/coco/tdx/tdx.c +++ b/arch/x86/coco/tdx/tdx.c @@ -11,6 +11,10 @@ #include #include #include +#include +#include +#include +#include =20 /* TDX module Call Leaf IDs */ #define TDX_GET_INFO 1 @@ -19,6 +23,7 @@ =20 /* TDX hypercall Leaf IDs */ #define TDVMCALL_MAP_GPA 0x10001 +#define TDVMCALL_SETUP_NOTIFY_INTR 0x10004 =20 /* MMIO direction */ #define EPT_READ 0 @@ -34,6 +39,28 @@ #define VE_GET_PORT_NUM(e) ((e) >> 16) #define VE_IS_IO_STRING(e) ((e) & BIT(4)) =20 +/* + * Handler used to report notifications about + * TDX_GUEST_EVENT_NOTIFY_VECTOR IRQ. Currently it will be + * used only by the attestation driver. So, race condition + * with read/write operation is not considered. + */ +static void (*tdx_event_notify_handler)(void); + +/* Helper function to register tdx_event_notify_handler */ +void tdx_setup_ev_notify_handler(void (*handler)(void)) +{ + tdx_event_notify_handler =3D handler; +} +EXPORT_SYMBOL_GPL(tdx_setup_ev_notify_handler); + +/* Helper function to unregister tdx_event_notify_handler */ +void tdx_remove_ev_notify_handler(void) +{ + tdx_event_notify_handler =3D NULL; +} +EXPORT_SYMBOL_GPL(tdx_remove_ev_notify_handler); + /* * Wrapper for standard use of __tdx_hypercall with no output aside from * return code. @@ -98,6 +125,46 @@ static inline void tdx_module_call(u64 fn, u64 rcx, u64= rdx, u64 r8, u64 r9, panic("TDCALL %lld failed (Buggy TDX module!)\n", fn); } =20 +/* TDX guest event notification handler */ +DEFINE_IDTENTRY_SYSVEC(sysvec_tdx_event_notify) +{ + struct pt_regs *old_regs =3D set_irq_regs(regs); + + inc_irq_stat(irq_tdx_event_notify_count); + + if (tdx_event_notify_handler) + tdx_event_notify_handler(); + + ack_APIC_irq(); + + set_irq_regs(old_regs); +} + +/* + * tdx_hcall_set_notify_intr() - Setup Event Notify Interrupt Vector. + * + * @vector: Vector address to be used for notification. + * + * return 0 on success or failure error number. + */ +static long tdx_hcall_set_notify_intr(u8 vector) +{ + /* Minimum vector value allowed is 32 */ + if (vector < 32) + return -EINVAL; + + /* + * Register callback vector address with VMM. More details + * about the ABI can be found in TDX Guest-Host-Communication + * Interface (GHCI), sec titled + * "TDG.VP.VMCALL". + */ + if (_tdx_hypercall(TDVMCALL_SETUP_NOTIFY_INTR, vector, 0, 0, 0)) + return -EIO; + + return 0; +} + static u64 get_cc_mask(void) { struct tdx_module_output out; @@ -688,5 +755,11 @@ void __init tdx_early_init(void) x86_platform.guest.enc_tlb_flush_required =3D tdx_tlb_flush_required; x86_platform.guest.enc_status_change_finish =3D tdx_enc_status_changed; =20 + alloc_intr_gate(TDX_GUEST_EVENT_NOTIFY_VECTOR, + asm_sysvec_tdx_event_notify); + + if (tdx_hcall_set_notify_intr(TDX_GUEST_EVENT_NOTIFY_VECTOR)) + pr_warn("Setting event notification interrupt failed\n"); + pr_info("Guest detected\n"); } diff --git a/arch/x86/include/asm/hardirq.h b/arch/x86/include/asm/hardirq.h index 275e7fd20310..582deff56210 100644 --- a/arch/x86/include/asm/hardirq.h +++ b/arch/x86/include/asm/hardirq.h @@ -44,6 +44,9 @@ typedef struct { unsigned int irq_hv_reenlightenment_count; unsigned int hyperv_stimer0_count; #endif +#if IS_ENABLED(CONFIG_INTEL_TDX_GUEST) + unsigned int irq_tdx_event_notify_count; +#endif } ____cacheline_aligned irq_cpustat_t; =20 DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat); diff --git a/arch/x86/include/asm/idtentry.h b/arch/x86/include/asm/idtentr= y.h index 72184b0b2219..655086dd940e 100644 --- a/arch/x86/include/asm/idtentry.h +++ b/arch/x86/include/asm/idtentry.h @@ -700,6 +700,10 @@ DECLARE_IDTENTRY_SYSVEC(HYPERVISOR_CALLBACK_VECTOR, sy= svec_xen_hvm_callback); DECLARE_IDTENTRY_SYSVEC(HYPERVISOR_CALLBACK_VECTOR, sysvec_kvm_asyncpf_int= errupt); #endif =20 +#if IS_ENABLED(CONFIG_INTEL_TDX_GUEST) +DECLARE_IDTENTRY_SYSVEC(TDX_GUEST_EVENT_NOTIFY_VECTOR, sysvec_tdx_event_no= tify); +#endif + #undef X86_TRAP_OTHER =20 #endif diff --git a/arch/x86/include/asm/irq_vectors.h b/arch/x86/include/asm/irq_= vectors.h index 43dcb9284208..82ac0c0a34b1 100644 --- a/arch/x86/include/asm/irq_vectors.h +++ b/arch/x86/include/asm/irq_vectors.h @@ -104,7 +104,12 @@ #define HYPERV_STIMER0_VECTOR 0xed #endif =20 -#define LOCAL_TIMER_VECTOR 0xec +#if IS_ENABLED(CONFIG_INTEL_TDX_GUEST) +/* Vector on which TDX Guest event notification is delivered */ +#define TDX_GUEST_EVENT_NOTIFY_VECTOR 0xec +#endif + +#define LOCAL_TIMER_VECTOR 0xeb =20 #define NR_VECTORS 256 =20 diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index 020c81a7c729..eb4db837cc44 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -67,6 +67,10 @@ void tdx_safe_halt(void); =20 bool tdx_early_handle_ve(struct pt_regs *regs); =20 +void tdx_setup_ev_notify_handler(void (*handler)(void)); + +void tdx_remove_ev_notify_handler(void); + #else =20 static inline void tdx_early_init(void) { }; diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c index 766ffe3ba313..a96ecd866723 100644 --- a/arch/x86/kernel/irq.c +++ b/arch/x86/kernel/irq.c @@ -181,6 +181,13 @@ int arch_show_interrupts(struct seq_file *p, int prec) seq_printf(p, "%10u ", irq_stats(j)->kvm_posted_intr_wakeup_ipis); seq_puts(p, " Posted-interrupt wakeup event\n"); +#endif +#if IS_ENABLED(CONFIG_INTEL_TDX_GUEST) + seq_printf(p, "%*s: ", prec, "TGN"); + for_each_online_cpu(j) + seq_printf(p, "%10u ", + irq_stats(j)->irq_tdx_event_notify_count); + seq_puts(p, " TDX Guest event notification\n"); #endif return 0; } --=20 2.25.1 From nobody Sat May 4 21:18:11 2024 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 674BCC433EF for ; Tue, 24 May 2022 04:06:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234174AbiEXEGO (ORCPT ); Tue, 24 May 2022 00:06:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47534 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234143AbiEXEGD (ORCPT ); Tue, 24 May 2022 00:06:03 -0400 Received: from mga07.intel.com (mga07.intel.com [134.134.136.100]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 801CA100C for ; Mon, 23 May 2022 21:06:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1653365162; x=1684901162; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=axd6CIMkKKwgPL8PgFH1WkIgbrXV7y44ZdJBhgbW0e0=; b=S6tLWdDYGDPswjrq3xgDFgd7FnpDUXCWbhdlYAB9nmdFI8ndf2VE0llc KiJ57gGhB3lJL91qyA0poFM7IjtAE+3cSBEH02QnVtlnOGVO8RggRPsmK Cu1DeuhS98YsvYvLc6UUAYGKwjg6J1XXwiP9tPKdQBShnKZR57mz/SuBY rX0xwZIVSTLcEtiN7H0nA7WGK5IE8Ux7b6O+Hhlg5VRrSopQuoL9SPSz6 szZ5i/FOCyfXv3PoVNj9+A/nkHp/iaNj6pnKJ0Hc5kkwoO4YQ8jQfRAKk Q+21lRH/E78q6JEv8Od8bjJGU6abQGRaODmoLm1pEepJmg6Xjej1lUTbC g==; X-IronPort-AV: E=McAfee;i="6400,9594,10356"; a="336479188" X-IronPort-AV: E=Sophos;i="5.91,248,1647327600"; d="scan'208";a="336479188" Received: from orsmga003.jf.intel.com ([10.7.209.27]) by orsmga105.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 May 2022 21:05:56 -0700 X-IronPort-AV: E=Sophos;i="5.91,248,1647327600"; d="scan'208";a="526242039" Received: from jwosulli-mobl1.ger.corp.intel.com (HELO skuppusw-desk1.home) ([10.212.165.122]) by orsmga003-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 May 2022 21:05:55 -0700 From: Kuppuswamy Sathyanarayanan To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org Cc: "H . Peter Anvin" , Kuppuswamy Sathyanarayanan , "Kirill A . Shutemov" , Tony Luck , Andi Kleen , Kai Huang , Wander Lairson Costa , Isaku Yamahata , marcelo.cerri@canonical.com, tim.gardner@canonical.com, khalid.elmously@canonical.com, philip.cox@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v7 3/5] x86/mm: Make tdx_enc_status_changed() vmalloc address compatible Date: Mon, 23 May 2022 21:05:15 -0700 Message-Id: <20220524040517.703581-4-sathyanarayanan.kuppuswamy@linux.intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220524040517.703581-1-sathyanarayanan.kuppuswamy@linux.intel.com> References: <20220524040517.703581-1-sathyanarayanan.kuppuswamy@linux.intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" set_memory_*crypted() APIs are used to change encryption or decryption page attributes for the given address. It also by default support the conversion for the vmalloc'ed memory address. In TDX Guest, tdx_enc_status_changed() function is triggered by set_memory_*crypted() APIs when converting memory from/to shared or private. Internally this function uses __pa() for physical address conversion, which breaks the vmalloc address compatibility of the set_memory_*crypted() APIs. So add support to fix the vmalloc'ed address compatibility issue. Signed-off-by: Kuppuswamy Sathyanarayanan Acked-by: Wander Lairson Costa --- arch/x86/coco/tdx/tdx.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c index b49211994864..37d58675ccf1 100644 --- a/arch/x86/coco/tdx/tdx.c +++ b/arch/x86/coco/tdx/tdx.c @@ -15,6 +15,7 @@ #include #include #include +#include =20 /* TDX module Call Leaf IDs */ #define TDX_GET_INFO 1 @@ -680,8 +681,14 @@ static bool try_accept_one(phys_addr_t *start, unsigne= d long len, */ static bool tdx_enc_status_changed(unsigned long vaddr, int numpages, bool= enc) { - phys_addr_t start =3D __pa(vaddr); - phys_addr_t end =3D __pa(vaddr + numpages * PAGE_SIZE); + phys_addr_t start, end; + + if (is_vmalloc_addr((void *)vaddr)) + start =3D vmalloc_to_pfn((void *) vaddr) << PAGE_SHIFT; + else + start =3D __pa(vaddr); + + end =3D start + numpages * PAGE_SIZE; =20 if (!enc) { /* Set the shared (decrypted) bits: */ --=20 2.25.1 From nobody Sat May 4 21:18:11 2024 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4F197C433EF for ; Tue, 24 May 2022 04:06:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234129AbiEXEGA (ORCPT ); Tue, 24 May 2022 00:06:00 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47228 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229462AbiEXEF6 (ORCPT ); Tue, 24 May 2022 00:05:58 -0400 Received: from mga07.intel.com (mga07.intel.com [134.134.136.100]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 320E08FF80 for ; Mon, 23 May 2022 21:05:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1653365157; x=1684901157; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=5A7dT1L6ZJt8z9kOLisNXrC0kDqefQt3kFNjxh+z7CQ=; b=P7tp1yOPSCYAK+DYu5Ub2e/maPqv0bMb1SjvnxBK2EI0kzje3cJnshnW c7rB7u8rW4pFNFDR1NeYz45B8RYEZ8AH8YjSZj157w3grhpSBui9i1dWz pnI2cq/jmsCK8XaU+sT9h5DXTDGTHGNRT2CRTp9N9IFMyp0eBMUcK/hU9 St81CVC1Dqz4rw2l5Dct6IcuFAZ8PIchLNC9DleseKksj2uJi9pDA6eYX B88TgagXBw4HaA/zo86/sk2gPhG+oK4pGJCuZ/cZxjcoCq9ZdxIg0cXIx hh/0WXibeQ7X59kybOS+oe/nwl9g8K9fRCSz+8mDQbYcxRM/OaBdOoa5f A==; X-IronPort-AV: E=McAfee;i="6400,9594,10356"; a="336479189" X-IronPort-AV: E=Sophos;i="5.91,248,1647327600"; d="scan'208";a="336479189" Received: from orsmga003.jf.intel.com ([10.7.209.27]) by orsmga105.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 May 2022 21:05:56 -0700 X-IronPort-AV: E=Sophos;i="5.91,248,1647327600"; d="scan'208";a="526242043" Received: from jwosulli-mobl1.ger.corp.intel.com (HELO skuppusw-desk1.home) ([10.212.165.122]) by orsmga003-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 May 2022 21:05:56 -0700 From: Kuppuswamy Sathyanarayanan To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org Cc: "H . Peter Anvin" , Kuppuswamy Sathyanarayanan , "Kirill A . Shutemov" , Tony Luck , Andi Kleen , Kai Huang , Wander Lairson Costa , Isaku Yamahata , marcelo.cerri@canonical.com, tim.gardner@canonical.com, khalid.elmously@canonical.com, philip.cox@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v7 4/5] x86/mm: Add noalias variants of set_memory_*crypted() functions Date: Mon, 23 May 2022 21:05:16 -0700 Message-Id: <20220524040517.703581-5-sathyanarayanan.kuppuswamy@linux.intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220524040517.703581-1-sathyanarayanan.kuppuswamy@linux.intel.com> References: <20220524040517.703581-1-sathyanarayanan.kuppuswamy@linux.intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" set_memory_*crypted() functions are used to modify the "shared" page attribute of the given memory. Using these APIs will modify the page attributes of the aliased mappings (which also includes the direct mapping). But such aliased mappings modification is not desirable in use cases like TDX guest, where the requirement is to create the shared mapping without touching the direct map. It is used when allocating VMM shared buffers using alloc_pages()/vmap()/set_memory_*crypted() API combinations. So to support such use cases, add support for noalias variants of set_memory_*crypted() functions. Signed-off-by: Kuppuswamy Sathyanarayanan Acked-by: Wander Lairson Costa --- arch/x86/include/asm/set_memory.h | 2 ++ arch/x86/mm/pat/set_memory.c | 26 ++++++++++++++++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/arch/x86/include/asm/set_memory.h b/arch/x86/include/asm/set_m= emory.h index 78ca53512486..0e5fc2b818be 100644 --- a/arch/x86/include/asm/set_memory.h +++ b/arch/x86/include/asm/set_memory.h @@ -46,7 +46,9 @@ int set_memory_wb(unsigned long addr, int numpages); int set_memory_np(unsigned long addr, int numpages); int set_memory_4k(unsigned long addr, int numpages); int set_memory_encrypted(unsigned long addr, int numpages); +int set_memory_encrypted_noalias(unsigned long addr, int numpages); int set_memory_decrypted(unsigned long addr, int numpages); +int set_memory_decrypted_noalias(unsigned long addr, int numpages); int set_memory_np_noalias(unsigned long addr, int numpages); int set_memory_nonglobal(unsigned long addr, int numpages); int set_memory_global(unsigned long addr, int numpages); diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c index 0656db33574d..4475f6e3bebb 100644 --- a/arch/x86/mm/pat/set_memory.c +++ b/arch/x86/mm/pat/set_memory.c @@ -1976,7 +1976,8 @@ int set_memory_global(unsigned long addr, int numpage= s) * __set_memory_enc_pgtable() is used for the hypervisors that get * informed about "encryption" status via page tables. */ -static int __set_memory_enc_pgtable(unsigned long addr, int numpages, bool= enc) +static int __set_memory_enc_pgtable(unsigned long addr, int numpages, + bool enc, int checkalias) { pgprot_t empty =3D __pgprot(0); struct cpa_data cpa; @@ -2004,7 +2005,7 @@ static int __set_memory_enc_pgtable(unsigned long add= r, int numpages, bool enc) /* Notify hypervisor that we are about to set/clr encryption attribute. */ x86_platform.guest.enc_status_change_prepare(addr, numpages, enc); =20 - ret =3D __change_page_attr_set_clr(&cpa, 1); + ret =3D __change_page_attr_set_clr(&cpa, checkalias); =20 /* * After changing the encryption attribute, we need to flush TLBs again @@ -2024,29 +2025,42 @@ static int __set_memory_enc_pgtable(unsigned long a= ddr, int numpages, bool enc) return ret; } =20 -static int __set_memory_enc_dec(unsigned long addr, int numpages, bool enc) +static int __set_memory_enc_dec(unsigned long addr, int numpages, bool enc, + int checkalias) { if (hv_is_isolation_supported()) return hv_set_mem_host_visibility(addr, numpages, !enc); =20 if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) - return __set_memory_enc_pgtable(addr, numpages, enc); + return __set_memory_enc_pgtable(addr, numpages, enc, checkalias); =20 return 0; } =20 int set_memory_encrypted(unsigned long addr, int numpages) { - return __set_memory_enc_dec(addr, numpages, true); + return __set_memory_enc_dec(addr, numpages, true, 1); } EXPORT_SYMBOL_GPL(set_memory_encrypted); =20 int set_memory_decrypted(unsigned long addr, int numpages) { - return __set_memory_enc_dec(addr, numpages, false); + return __set_memory_enc_dec(addr, numpages, false, 1); } EXPORT_SYMBOL_GPL(set_memory_decrypted); =20 +int set_memory_encrypted_noalias(unsigned long addr, int numpages) +{ + return __set_memory_enc_dec(addr, numpages, true, 0); +} +EXPORT_SYMBOL_GPL(set_memory_encrypted_noalias); + +int set_memory_decrypted_noalias(unsigned long addr, int numpages) +{ + return __set_memory_enc_dec(addr, numpages, false, 0); +} +EXPORT_SYMBOL_GPL(set_memory_decrypted_noalias); + int set_pages_uc(struct page *page, int numpages) { unsigned long addr =3D (unsigned long)page_address(page); --=20 2.25.1 From nobody Sat May 4 21:18:11 2024 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D2961C433F5 for ; Tue, 24 May 2022 04:06:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234151AbiEXEGE (ORCPT ); Tue, 24 May 2022 00:06:04 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47290 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234043AbiEXEF7 (ORCPT ); Tue, 24 May 2022 00:05:59 -0400 Received: from mga04.intel.com (mga04.intel.com [192.55.52.120]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C0AC68FF9B for ; Mon, 23 May 2022 21:05:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1653365157; x=1684901157; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=56PmCyV9eRfm3xfya29b1n2o+80uBzHotBb3Hdgc3FQ=; b=Tp4C15uJfCNBab9jCqE+MIrmKQqcJoUSLCJB9Sltd9C0L3V7gkZYYPuK DOVbk01BwOfmSX9oYGRvD3LynYH0ydXOyZ0oZ13bQKt12SI7YF+LBZ1Sn W4Ie+Gi3PCmaF/GskJlBkltZcdJ7xGmtzSgjC+U8gX5DEhr//33AG5crZ gBgdqZRqUbVTIWyPZOnD5rzWwVUa7mXYAyhOaow6h+TLClmcajxDqUhwX rxxKoGxSce8Q6jG8jBs/wlC+QBkrOYEOjsGgR5CuBmoLlARw27c960l/g AnEa9OK7j3YR0GQb87Flhcjlwv/ve9pluwxHqjkQiF3mEJZlV3wsUZn5C g==; X-IronPort-AV: E=McAfee;i="6400,9594,10356"; a="272243090" X-IronPort-AV: E=Sophos;i="5.91,248,1647327600"; d="scan'208";a="272243090" Received: from orsmga003.jf.intel.com ([10.7.209.27]) by fmsmga104.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 May 2022 21:05:57 -0700 X-IronPort-AV: E=Sophos;i="5.91,248,1647327600"; d="scan'208";a="526242049" Received: from jwosulli-mobl1.ger.corp.intel.com (HELO skuppusw-desk1.home) ([10.212.165.122]) by orsmga003-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 May 2022 21:05:56 -0700 From: Kuppuswamy Sathyanarayanan To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org Cc: "H . Peter Anvin" , Kuppuswamy Sathyanarayanan , "Kirill A . Shutemov" , Tony Luck , Andi Kleen , Kai Huang , Wander Lairson Costa , Isaku Yamahata , marcelo.cerri@canonical.com, tim.gardner@canonical.com, khalid.elmously@canonical.com, philip.cox@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v7 5/5] x86/tdx: Add Quote generation support Date: Mon, 23 May 2022 21:05:17 -0700 Message-Id: <20220524040517.703581-6-sathyanarayanan.kuppuswamy@linux.intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220524040517.703581-1-sathyanarayanan.kuppuswamy@linux.intel.com> References: <20220524040517.703581-1-sathyanarayanan.kuppuswamy@linux.intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" In TDX guest, the second stage in attestation process is to send the TDREPORT to QE/QGS to generate the TD Quote. For platforms that does not support communication channels like vsock or TCP/IP, implement support to get TD Quote using hypercall. GetQuote hypercall can be used by the TD guest to request VMM facilitate the Quote generation via QE/QGS. More details about GetQuote hypercall can be found in TDX Guest-Host Communication Interface (GHCI) for Intel TDX 1.0, section titled "TDG.VP.VMCALL. Since GetQuote is an asynchronous request hypercall, it will not block till the TD Quote is generated. So VMM uses callback interrupt vector configured by SetupEventNotifyInterrupt hypercall to notify the guest about Quote generation completion or failure. GetQuote TDVMCALL requires TD guest pass a 4K aligned shared buffer with TDREPORT data as input, which is further used by the VMM to copy the TD Quote result after successful Quote generation. To create the shared buffer without breaking the direct map, allocate physically contiguous kernel memory and create a virtual mapping for it using vmap(). set_memory_*crypted_noalias() functions can be used to share or unshare the vmapped page without affecting the direct map. Also note that, shared buffer allocation is currently handled in IOCTL handler, although it will increase the TDX_CMD_GET_QUOTE IOCTL response time, it is negligible compared to the time required for the quote generation completion. So IOCTL performance optimization is not considered at this time. For shared buffer allocation, alternatives like using the DMA API is also considered. Although it simpler to use, it is not preferred because dma_alloc_*() APIs require a valid bus device as argument, which would need converting the attestation driver into a platform device driver. This is unnecessary, and since the attestation driver does not do real DMA, there is no need to use real DMA APIs. Add support for TDX_CMD_GET_QUOTE IOCTL to allow attestation agent submit GetQuote requests from the user space. Since Quote generation is an asynchronous request, IOCTL will block indefinitely for the VMM response in wait_for_completion_interruptible() call. Using this call will also add an option for the user to end the current request prematurely by raising any signals. This can be used by attestation agent to implement Quote generation timeout feature. If attestation agent is aware of time it can validly wait for QE/QGS response, then a possible timeout support can be implemented in the user application using signals. Quote generation timeout feature is currently not implemented in the driver because the current TDX specification does not have any recommendation for it. After submitting the GetQuote request using hypercall, the shared buffer allocated for the current request is owned by the VMM. So, during this wait window, if the user terminates the request by raising a signal or by terminating the application, add a logic to do the memory cleanup after receiving the VMM response at a later time. Such memory cleanup support requires accepting the page again using TDX_ACCEPT_PAGE TDX Module call. So to not overload the callback IRQ handler, move the callback handler logic to a separate work queue. To support parallel GetQuote requests, use linked list to track the active GetQuote requests and upon receiving the callback IRQ, loop through the active requests and mark the processed requests complete. Users can open multiple instances of the attestation device and send GetQuote requests in parallel. Reviewed-by: Tony Luck Reviewed-by: Andi Kleen Acked-by: Kirill A. Shutemov Signed-off-by: Kuppuswamy Sathyanarayanan --- arch/x86/coco/tdx/attest.c | 314 ++++++++++++++++++++++++++++++++ arch/x86/include/uapi/asm/tdx.h | 45 +++++ 2 files changed, 359 insertions(+) diff --git a/arch/x86/coco/tdx/attest.c b/arch/x86/coco/tdx/attest.c index 24db0bad4923..16f593a9d46d 100644 --- a/arch/x86/coco/tdx/attest.c +++ b/arch/x86/coco/tdx/attest.c @@ -13,16 +13,56 @@ #include #include #include +#include +#include #include +#include #include =20 #define DRIVER_NAME "tdx-attest" =20 /* TDREPORT module call leaf ID */ #define TDX_GET_REPORT 4 +/* GetQuote hypercall leaf ID */ +#define TDVMCALL_GET_QUOTE 0x10002 + +/* Used for buffer allocation in GetQuote request */ +struct quote_buf { + /* vmapped address of kernel buffer (size is page aligned) */ + void *vmaddr; + /* Number of pages */ + int count; +}; + +/* List entry of quote_list */ +struct quote_entry { + /* Flag to check validity of the GetQuote request */ + bool valid; + /* Kernel buffer to share data with VMM */ + struct quote_buf *buf; + /* Completion object to track completion of GetQuote request */ + struct completion compl; + struct list_head list; +}; =20 static struct miscdevice miscdev; =20 +/* + * To support parallel GetQuote requests, use the list + * to track active GetQuote requests. + */ +static LIST_HEAD(quote_list); + +/* Lock to protect quote_list */ +static DEFINE_MUTEX(quote_lock); + +/* + * Workqueue to handle Quote data after Quote generation + * notification from VMM. + */ +struct workqueue_struct *quote_wq; +struct work_struct quote_work; + static long tdx_get_report(void __user *argp) { void *reportdata =3D NULL, *tdreport =3D NULL; @@ -71,6 +111,270 @@ static long tdx_get_report(void __user *argp) return ret; } =20 +/* tdx_get_quote_hypercall() - Request to get TD Quote using TDREPORT */ +static long tdx_get_quote_hypercall(struct quote_buf *buf) +{ + struct tdx_hypercall_args args =3D {0}; + + args.r10 =3D TDX_HYPERCALL_STANDARD; + args.r11 =3D TDVMCALL_GET_QUOTE; + args.r12 =3D cc_mkdec(page_to_phys(vmalloc_to_page(buf->vmaddr))); + args.r13 =3D buf->count * PAGE_SIZE; + + /* + * Pass the physical address of TDREPORT to the VMM and + * trigger the Quote generation. It is not a blocking + * call, hence completion of this request will be notified to + * the TD guest via a callback interrupt. More info about ABI + * can be found in TDX Guest-Host-Communication Interface + * (GHCI), sec titled "TDG.VP.VMCALL". + */ + return __tdx_hypercall(&args, 0); +} + +/* + * alloc_quote_buf() - Used to allocate a shared buffer of + * given size. + * + * Size is page aligned and the allocated memory is decrypted + * to allow VMM to access it. Uses VMAP to create a virtual + * mapping, which is further used to create a shared mapping + * for the buffer without affecting the direct map. + */ +static struct quote_buf *alloc_quote_buf(u64 req_size) +{ + int size =3D PAGE_ALIGN(req_size); + void *addr =3D NULL, *vmaddr =3D NULL; + int count =3D size >> PAGE_SHIFT; + struct page **pages =3D NULL; + struct quote_buf *buf; + int i; + + buf =3D kmalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + return NULL; + + addr =3D alloc_pages_exact(size, GFP_KERNEL); + if (!addr) + goto alloc_failed; + + /* Allocate mem for array of page ptrs */ + pages =3D kcalloc(count, sizeof(*pages), GFP_KERNEL); + if (!pages) + goto alloc_failed; + + for (i =3D 0; i < count; i++) + pages[i] =3D virt_to_page(addr + i * PAGE_SIZE); + + /* + * Use VMAP to create a virtual mapping, which is used + * to create shared mapping without affecting the + * direct map. Use VM_MAP_PUT_PAGES to allow vmap() + * responsible for freeing the pages when using vfree(). + */ + vmaddr =3D vmap(pages, count, VM_MAP_PUT_PAGES, PAGE_KERNEL); + if (!vmaddr) + goto alloc_failed; + + /* Use noalias variant to not affect the direct mapping */ + if (set_memory_decrypted_noalias((unsigned long)vmaddr, count)) + goto alloc_failed; + + buf->vmaddr =3D vmaddr; + buf->count =3D count; + + return buf; + +alloc_failed: + if (!vmaddr) { + kfree(pages); + if (addr) + free_pages_exact(addr, size); + } + vfree(vmaddr); + kfree(buf); + return NULL; +} + +/* Remove the shared mapping and free the buffer */ +static void free_quote_buf(struct quote_buf *buf) +{ + if (!buf) + return; + + /* Mark pages private */ + if (set_memory_encrypted_noalias((unsigned long)buf->vmaddr, + buf->count)) { + pr_warn("Failed to encrypt %d pages at %p", buf->count, + buf->vmaddr); + return; + } + + vfree(buf->vmaddr); + kfree(buf); +} + +static struct quote_entry *alloc_quote_entry(u64 buf_len) +{ + struct quote_entry *entry =3D NULL; + + entry =3D kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return NULL; + + /* Allocate buffer for quote request */ + entry->buf =3D alloc_quote_buf(buf_len); + if (!entry->buf) { + kfree(entry); + return NULL; + } + + init_completion(&entry->compl); + entry->valid =3D true; + + return entry; +} + +static void free_quote_entry(struct quote_entry *entry) +{ + free_quote_buf(entry->buf); + kfree(entry); +} + +/* Must be called with quote_lock held */ +static void _del_quote_entry(struct quote_entry *entry) +{ + list_del(&entry->list); + free_quote_entry(entry); +} + +static void del_quote_entry(struct quote_entry *entry) +{ + mutex_lock("e_lock); + _del_quote_entry(entry); + mutex_unlock("e_lock); +} + +/* Handles early termination of GetQuote requests */ +void terminate_quote_request(struct quote_entry *entry) +{ + struct tdx_quote_hdr *quote_hdr; + + /* + * For early termination, if the request is not yet + * processed by VMM (GET_QUOTE_IN_FLIGHT), the VMM + * still owns the shared buffer, so mark the request + * invalid to let quote_callback_handler() handle the + * memory cleanup function. If the request is already + * processed, then do the cleanup and return. + */ + + mutex_lock("e_lock); + quote_hdr =3D (struct tdx_quote_hdr *)entry->buf->vmaddr; + if (quote_hdr->status =3D=3D GET_QUOTE_IN_FLIGHT) { + entry->valid =3D false; + mutex_unlock("e_lock); + return; + } + _del_quote_entry(entry); + mutex_unlock("e_lock); +} + +static long tdx_get_quote(void __user *argp) +{ + struct quote_entry *entry; + struct tdx_quote_req req; + struct quote_buf *buf; + long ret; + + /* Copy GetQuote request struct from user buffer */ + if (copy_from_user(&req, argp, sizeof(struct tdx_quote_req))) + return -EFAULT; + + /* Make sure the length is valid */ + if (!req.len) + return -EINVAL; + + entry =3D alloc_quote_entry(req.len); + if (!entry) + return -ENOMEM; + + buf =3D entry->buf; + + /* Copy TDREPORT from user buffer to kernel Quote buffer */ + if (copy_from_user(buf->vmaddr, (void __user *)req.buf, req.len)) { + free_quote_entry(entry); + return -EFAULT; + } + + mutex_lock("e_lock); + + /* Submit GetQuote Request */ + ret =3D tdx_get_quote_hypercall(buf); + if (ret) { + mutex_unlock("e_lock); + pr_err("GetQuote hypercall failed, status:%lx\n", ret); + free_quote_entry(entry); + return -EIO; + } + + /* Add current quote entry to quote_list to track active requests */ + list_add_tail(&entry->list, "e_list); + + mutex_unlock("e_lock); + + /* Wait for attestation completion */ + ret =3D wait_for_completion_interruptible(&entry->compl); + if (ret < 0) { + terminate_quote_request(entry); + return -EINTR; + } + + /* + * If GetQuote request completed successfully, copy the result + * back to the user and do the cleanup. + */ + if (copy_to_user((void __user *)req.buf, buf->vmaddr, req.len)) + ret =3D -EFAULT; + + /* + * Reaching here means GetQuote request is processed + * successfully. So do the cleanup and return 0. + */ + del_quote_entry(entry); + + return 0; +} + +static void attestation_callback_handler(void) +{ + queue_work(quote_wq, "e_work); +} + +static void quote_callback_handler(struct work_struct *work) +{ + struct tdx_quote_hdr *quote_hdr; + struct quote_entry *entry, *next; + + /* Find processed quote request and mark it complete */ + mutex_lock("e_lock); + list_for_each_entry_safe(entry, next, "e_list, list) { + quote_hdr =3D (struct tdx_quote_hdr *)entry->buf->vmaddr; + if (quote_hdr->status =3D=3D GET_QUOTE_IN_FLIGHT) + continue; + /* + * If user invalidated the current request, remove the + * entry from the quote list and free it. If the request + * is still valid, mark it complete. + */ + if (entry->valid) + complete(&entry->compl); + else + _del_quote_entry(entry); + } + mutex_unlock("e_lock); +} + static long tdx_attest_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -81,6 +385,9 @@ static long tdx_attest_ioctl(struct file *file, unsigned= int cmd, case TDX_CMD_GET_REPORT: ret =3D tdx_get_report(argp); break; + case TDX_CMD_GET_QUOTE: + ret =3D tdx_get_quote(argp); + break; default: pr_debug("cmd %d not supported\n", cmd); break; @@ -103,6 +410,13 @@ static int __init tdx_attestation_init(void) if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) return -EIO; =20 + quote_wq =3D create_singlethread_workqueue("tdx_quote_handler"); + + INIT_WORK("e_work, quote_callback_handler); + + /* Register attestation event notify handler */ + tdx_setup_ev_notify_handler(attestation_callback_handler); + miscdev.name =3D DRIVER_NAME; miscdev.minor =3D MISC_DYNAMIC_MINOR; miscdev.fops =3D &tdx_attest_fops; diff --git a/arch/x86/include/uapi/asm/tdx.h b/arch/x86/include/uapi/asm/td= x.h index 8b57dea67eab..fe941cdb084d 100644 --- a/arch/x86/include/uapi/asm/tdx.h +++ b/arch/x86/include/uapi/asm/tdx.h @@ -39,4 +39,49 @@ struct tdx_report_req { */ #define TDX_CMD_GET_REPORT _IOWR('T', 0x01, struct tdx_report_req) =20 +/* struct tdx_quote_req: Request to generate TD Quote using TDREPORT + * + * @buf : Pass user data that includes TDREPORT as input. Upon + * successful completion of IOCTL, output is copied + * back to the same buffer. + * @len : Length of the buffer. + */ +struct tdx_quote_req { + __u64 buf; + __u64 len; +}; + +/* + * TDX_CMD_GET_QUOTE - Get TD Quote from QE/QGS using GetQuote + * TDVMCALL. + * + * Returns 0 on success, -EINTR for interrupted request, and + * standard errono on other failures. + */ +#define TDX_CMD_GET_QUOTE _IOR('T', 0x02, struct tdx_quote_req) + +/* TD Quote status codes */ +#define GET_QUOTE_SUCCESS 0 +#define GET_QUOTE_IN_FLIGHT 0xffffffffffffffff +#define GET_QUOTE_ERROR 0x8000000000000000 +#define GET_QUOTE_SERVICE_UNAVAILABLE 0x8000000000000001 + +/* + * Format of Quote data header. More details can be found in TDX + * Guest-Host Communication Interface (GHCI) for Intel TDX 1.0, + * section titled "TDG.VP.VMCALL" + */ +struct tdx_quote_hdr { + /* Quote version, filled by TD */ + __u64 version; + /* Status code of Quote request, filled by VMM */ + __u64 status; + /* Length of TDREPORT, filled by TD */ + __u32 in_len; + /* Length of Quote, filled by VMM */ + __u32 out_len; + /* Actual Quote data */ + __u64 data[0]; +}; + #endif /* _UAPI_ASM_X86_TDX_H */ --=20 2.25.1