Received: by 2002:a05:7412:518d:b0:e2:908c:2ebd with SMTP id fn13csp398771rdb; Thu, 5 Oct 2023 09:02:01 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGWELGYRJtfmj9u06taBfElKLNzBei/7tNcUrgycSSLOINe6YO0sEo08Zez55VXWT5F+pfZ X-Received: by 2002:a05:6358:921:b0:143:8f7f:9150 with SMTP id r33-20020a056358092100b001438f7f9150mr6340888rwi.13.1696521721380; Thu, 05 Oct 2023 09:02:01 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1696521721; cv=none; d=google.com; s=arc-20160816; b=wvHcnv5kcd3QWCQ4SXPp9e20TW8kBIl+vbg6k64tFn66atNdjtNl+gCcDWRK4eagZY +MC3qeBQeS7RZaAP3U5WG2fsu2v4KAnOt+kU7RUn0IuAibFoYYM5BIAleXQ/sekHbN9N cTcuICjEtDKb0rhwiWA2lf3qdZJ4T5QEdvGiBDCDzidQ5vg4qEJ+T9hDCAFYdK0x9MSU f2H85wHZiHetyQe08MzZfNupHpXgwjqpkF2axTSGyj1dNKX3sUyMu995+YNEVShuB4zQ uofWtAMGXaYSwjnkgy3D0ncwTF4/5h7xVRccGHbeWmYfqCqkOEhqmXurdMoNkvJTjGGr FYQQ== 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 :dkim-signature; bh=CGmfcDjf2h4wlLjEJ8BEcInbus4xMMzF/kCtIqpochY=; fh=F1gpqSS/HYttK+doOKuW4yrAifo5qykyq1MCI9SIQmQ=; b=dew2hYz68xYpZ3HETmaOo1FP56ZPPdorvG1piZ/r4Ifafj5qv6oGAJqQKZjRgWTZTX BbyKj52BjvvjMgB6CVkL1Z7Rm+hoyfpBAG3yvAMFoFln387S997eRuXv5xKinoKtmwJ4 MHZJrJcLHjsxIwVHOPtz1Ke5hcm0c9JuRXZv4wAybepyF2n9lIOrWiLzGSrMD4eW/ko6 FtdPCDPZsKvHqkN8JyRrSuggmRYNET/SiLu4x/RIRXUKoVG01+EU4SkANNaciQ0xTmb5 nh0msWyPGVE2VE5CqNBerpYU5RJ7xcHsJxCFAUm5dFhGSuX+L5Z1IECCSeMEHNvFLzpj jABw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b=aJCxXiWV; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:5 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from groat.vger.email (groat.vger.email. [2620:137:e000::3:5]) by mx.google.com with ESMTPS id 17-20020a630111000000b00578d460cd7esi224034pgb.431.2023.10.05.09.01.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 05 Oct 2023 09:02:01 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:5 as permitted sender) client-ip=2620:137:e000::3:5; Authentication-Results: mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b=aJCxXiWV; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:5 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by groat.vger.email (Postfix) with ESMTP id 87F20837B5C6; Thu, 5 Oct 2023 09:01:20 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at groat.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238631AbjJEP7K (ORCPT + 99 others); Thu, 5 Oct 2023 11:59:10 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50798 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234733AbjJEP4v (ORCPT ); Thu, 5 Oct 2023 11:56:51 -0400 Received: from mgamail.intel.com (mgamail.intel.com [192.55.52.93]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 890CB6632 for ; Thu, 5 Oct 2023 07:04:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1696514688; x=1728050688; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=2nx+VfYqCoJBnge8h8wvBHlxzs9Z5VteiY85TeYVjzU=; b=aJCxXiWVzP04Sn6jemuvaucC3JJc36rwyzWb/uQJoI+LEO0CTFU0mvAj ZMzJtCHJz5LuAAJpmT9IOQEWtfAlaltYZ4QgwYMTVl9FjTNjr3RahKSEf WtpY7u4HHy+6uFRk/4rYc0bHGs9n3o7sm2Ga83YNaO+fpsTKa2wxLUcAU 5/tc5ZdxO0LKpPLv25y/Y5IQ4Y6Ci5Crkdk5rSKLi+DFLmZQvl/xIlAy2 XH+WpkavX8c7jiZvN05Xg6gb0QSzldqYs1Ni5lWIe4LrIjAGmHjr5OwuW NJ9ckevmHwLDyDQs9lUPSzaER9y7lOUzSZ3N429MnAJ6BRLNkVEUENA/H Q==; X-IronPort-AV: E=McAfee;i="6600,9927,10854"; a="380767213" X-IronPort-AV: E=Sophos;i="6.03,203,1694761200"; d="scan'208";a="380767213" Received: from orsmga006.jf.intel.com ([10.7.209.51]) by fmsmga102.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 05 Oct 2023 06:14:29 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10854"; a="728449276" X-IronPort-AV: E=Sophos;i="6.03,203,1694761200"; d="scan'208";a="728449276" Received: from skwasnia-mobl.ger.corp.intel.com (HELO box.shutemov.name) ([10.251.222.71]) by orsmga006-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 05 Oct 2023 06:14:23 -0700 Received: by box.shutemov.name (Postfix, from userid 1000) id 7291D10A154; Thu, 5 Oct 2023 16:14:14 +0300 (+03) From: "Kirill A. Shutemov" To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org Cc: "Rafael J. Wysocki" , Peter Zijlstra , Adrian Hunter , Kuppuswamy Sathyanarayanan , Elena Reshetova , Jun Nakajima , Rick Edgecombe , Tom Lendacky , kexec@lists.infradead.org, linux-coco@lists.linux.dev, linux-kernel@vger.kernel.org, "Kirill A. Shutemov" Subject: [PATCH 10/13] x86/tdx: Convert shared memory back to private on kexec Date: Thu, 5 Oct 2023 16:13:59 +0300 Message-ID: <20231005131402.14611-11-kirill.shutemov@linux.intel.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20231005131402.14611-1-kirill.shutemov@linux.intel.com> References: <20231005131402.14611-1-kirill.shutemov@linux.intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-0.8 required=5.0 tests=DKIMWL_WL_HIGH,DKIM_SIGNED, DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI, SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on groat.vger.email Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (groat.vger.email [0.0.0.0]); Thu, 05 Oct 2023 09:01:21 -0700 (PDT) TDX guests allocate shared buffers to perform I/O. It is done by allocating pages normally from the buddy allocator and converting them to shared with set_memory_decrypted(). The target kernel has no idea what memory is converted this way. It only sees E820_TYPE_RAM. Accessing shared memory via private mapping is fatal. It leads to unrecoverable TD exit. On TD shutdown (also covers kexec), walk direct mapping and convert all shared memory back to private. It makes all RAM private again and target kernel may use it normally. Signed-off-by: Kirill A. Shutemov --- arch/x86/Kconfig | 1 + arch/x86/coco/tdx/kexec.c | 0 arch/x86/coco/tdx/tdx.c | 137 +++++++++++++++++++++++++++++++++++++- 3 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 arch/x86/coco/tdx/kexec.c diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 7368d254d01f..b5acf9fb4c70 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -884,6 +884,7 @@ config INTEL_TDX_GUEST select X86_MEM_ENCRYPT select X86_MCE select UNACCEPTED_MEMORY + select EMERGENCY_VIRT_CALLBACK help Support running as a guest under Intel TDX. Without this support, the guest kernel can not boot or run under TDX. diff --git a/arch/x86/coco/tdx/kexec.c b/arch/x86/coco/tdx/kexec.c new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c index 56e152126f20..ac0745303983 100644 --- a/arch/x86/coco/tdx/tdx.c +++ b/arch/x86/coco/tdx/tdx.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -14,6 +15,8 @@ #include #include #include +#include +#include /* MMIO direction */ #define EPT_READ 0 @@ -40,6 +43,9 @@ static atomic_long_t nr_shared; +static atomic_t conversions_in_progress; +static bool conversion_allowed = true; + static inline bool pte_decrypted(pte_t pte) { return cc_mkdec(pte_val(pte)) == pte_val(pte); @@ -704,6 +710,14 @@ static bool tdx_tlb_flush_required(bool private) static bool tdx_cache_flush_required(void) { + /* + * Avoid issuing CLFLUSH on set_memory_decrypted() if conversions + * stopped. Otherwise it can race with unshare_all_memory() and trigger + * implicit conversion to shared. + */ + if (!conversion_allowed) + return false; + /* * AMD SME/SEV can avoid cache flushing if HW enforces cache coherence. * TDX doesn't have such capability. @@ -787,12 +801,25 @@ static bool tdx_enc_status_changed(unsigned long vaddr, int numpages, bool enc) static int tdx_enc_status_change_prepare(unsigned long vaddr, int numpages, bool enc) { + atomic_inc(&conversions_in_progress); + + /* + * Check after bumping conversions_in_progress to serialize + * against tdx_shutdown(). + */ + if (!conversion_allowed) { + atomic_dec(&conversions_in_progress); + return -EBUSY; + } + /* * Only handle shared->private conversion here. * See the comment in tdx_early_init(). */ - if (enc && !tdx_enc_status_changed(vaddr, numpages, enc)) + if (enc && !tdx_enc_status_changed(vaddr, numpages, enc)) { + atomic_dec(&conversions_in_progress); return -EIO; + } return 0; } @@ -804,17 +831,115 @@ static int tdx_enc_status_change_finish(unsigned long vaddr, int numpages, * Only handle private->shared conversion here. * See the comment in tdx_early_init(). */ - if (!enc && !tdx_enc_status_changed(vaddr, numpages, enc)) + if (!enc && !tdx_enc_status_changed(vaddr, numpages, enc)) { + atomic_dec(&conversions_in_progress); return -EIO; + } if (enc) atomic_long_sub(numpages, &nr_shared); else atomic_long_add(numpages, &nr_shared); + atomic_dec(&conversions_in_progress); + return 0; } +static void unshare_all_memory(bool unmap) +{ + unsigned long addr, end; + long found = 0, shared; + + /* + * Walk direct mapping and convert all shared memory back to private, + */ + + addr = PAGE_OFFSET; + end = PAGE_OFFSET + get_max_mapped(); + + while (addr < end) { + unsigned long size; + unsigned int level; + pte_t *pte; + + pte = lookup_address(addr, &level); + size = page_level_size(level); + + if (pte && pte_decrypted(*pte)) { + int pages = size / PAGE_SIZE; + + /* + * Touching memory with shared bit set triggers implicit + * conversion to shared. + * + * Make sure nobody touches the shared range from + * now on. + * + * Bypass unmapping for crash scenario. Unmapping + * requires sleepable context, but in crash case kernel + * hits the code path with interrupts disabled. + * It shouldn't be a problem as all secondary CPUs are + * down and kernel runs with interrupts disabled, so + * there is no room for race. + */ + if (unmap) + set_memory_np(addr, pages); + + if (!tdx_enc_status_changed(addr, pages, true)) { + pr_err("Failed to unshare range %#lx-%#lx\n", + addr, addr + size); + } + + found += pages; + } + + addr += size; + } + + shared = atomic_long_read(&nr_shared); + if (shared != found) { + pr_err("shared page accounting is off\n"); + pr_err("nr_shared = %ld, nr_found = %ld\n", shared, found); + } +} + +static void tdx_shutdown(void) +{ + unsigned long timeout; + + /* + * Stop new private<->shared conversions and wait for in-flight + * conversions to complete. + * + * Do not wait more than 30 seconds. + */ + timeout = 30 * USEC_PER_SEC; + conversion_allowed = false; + while (atomic_read(&conversions_in_progress) && timeout--) + udelay(1); + + if (!timeout) + pr_warn("Failed to finish shared<->private conversions\n"); + + unshare_all_memory(true); + + native_machine_shutdown(); +} + +static void tdx_crash_shutdown(void) +{ + /* + * Crash can race with private<->shared conversion. + * + * There's no clean way out: report and proceed. + */ + if (atomic_read(&conversions_in_progress)) + pr_warn("Failed to finish shared<->private conversions\n"); + + unshare_all_memory(false); +} + void __init tdx_early_init(void) { struct tdx_module_args args = { @@ -882,6 +1007,14 @@ void __init tdx_early_init(void) */ x86_cpuinit.parallel_bringup = false; + machine_ops.shutdown = tdx_shutdown; + + /* + * KVM overrides machine_ops.crash_shutdown, use emergency + * virt callback instead. + */ + cpu_emergency_register_virt_callback(tdx_crash_shutdown); + pr_info("Guest detected\n"); } -- 2.41.0