Received: by 2002:a05:6a10:2726:0:0:0:0 with SMTP id ib38csp1148790pxb; Wed, 6 Apr 2022 09:51:57 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxqad0EdTduMT7jq/222TI4yoRoTbCSzvxGkjryDbslL7M/eQ0DvkoOT2684OZHZw6IEKHQ X-Received: by 2002:a17:903:41c3:b0:156:ca9a:a928 with SMTP id u3-20020a17090341c300b00156ca9aa928mr9568419ple.23.1649263917219; Wed, 06 Apr 2022 09:51:57 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1649263917; cv=none; d=google.com; s=arc-20160816; b=wRxFe0VmDZW8aCtOdHX6Wxq9tPVzbYeHmWLB6kmr16Ydlbxpe9DJ7r2uhUMOHr2+03 i+gFVbS2CY8JzIQPCscI9fZ2MHqEE5FgHI/0Pa88xJMJ+6w7A+fQzT59x6xMR9Tm9rQK wKUACGiWcq++SC82gNkvFUkhiluKwusFX6gZIHhcxykEi5qZ8OEKjUACmt9G5xCjC8r4 /YWsiFDl+IJ8K7zqBCSLs0GITFnc29ovgXTZvfP3PXV/5aP1R09dtH3dCPU2q93o9vIk yiRpg9I6eR5TvkZEpjbulcMRcXh1XD5fWD4KFV+OsfeVhgb/HdS6lPZ75wVK5yGIv4dP MEJA== 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=5nhibr9VvErdfJnaeqctDXZWtL0UjAVx76E3Llivk8k=; b=ZRaLxArTHwfqqU0TveyGgjrkW0WzYES7h7lxJWnQOsoHvgHluGxSLL0FBy09i4w5mS AJN8MC2wENX8wwrWx5pfnghval6SH6PxBYj0gfR942eICEvcIbadH6dGDZSU4aBnJN4l bP2XP93/QrKfNeCIFpC9Nk1Z5ZNivNaLQUFjQJ0frItEgptKPi+i2Z77gvNu7KAq+ypz nGEOt+xbQAV3OS1KzOxWMkgpZwGfPl5JQjQMz9k1KkgssesWh17uMZW2P5vlIiP0nJ2D cT5HxBzfsvaFSSUd/2wjSeXril31GGI+leV9bt37w2xqrWqXY23sAIDJwkLp5wxgwyHT gVUA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b=eEw4pvWP; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:18 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 lindbergh.monkeyblade.net (lindbergh.monkeyblade.net. [2620:137:e000::1:18]) by mx.google.com with ESMTPS id b2-20020a170902b60200b00153b2d164casi16148801pls.210.2022.04.06.09.51.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 Apr 2022 09:51:57 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:18 as permitted sender) client-ip=2620:137:e000::1:18; Authentication-Results: mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b=eEw4pvWP; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:18 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 228BB4405CE; Wed, 6 Apr 2022 09:05:12 -0700 (PDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236997AbiDFQGm (ORCPT + 99 others); Wed, 6 Apr 2022 12:06:42 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56898 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236994AbiDFQGI (ORCPT ); Wed, 6 Apr 2022 12:06:08 -0400 Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5F459451D76; Tue, 5 Apr 2022 21:50:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1649220636; x=1680756636; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=U7JIb6jRGaOT3Ma7lXiC1ZND2BbsDZGoURUyJcHBlHw=; b=eEw4pvWPbXrO+SkDyCvIjELa7ITe0wLJemM30qmeL9ml4LkDBqW0u6/7 i/auPoBewbRv7T+LgtTkGv0NFWF9ORnSInFfX4f3RyAhy0bOoEJYFZjQk 9B4f7yT7qP/k4xRzH4Fv4gygDKAJOrFv4AtXK1Pagsa2td6mbUeYr8Zdy ZTD2fH3P8YisJa/XcY1vJ8qlaaFosgS0cVRxKqbRqIyELru7wxR24MaFa YN/sQ6dHuX5YOJUhFyCq+8mTmkKI1yf4Ge6o2+/86EvopZ+4Hf/Q/6/Vb 18mjTrAVBg4KDFHYaxrFo1+YreLuy0/4DrfkXb46UlVn9ztx7dJ/edzH3 w==; X-IronPort-AV: E=McAfee;i="6200,9189,10308"; a="243089867" X-IronPort-AV: E=Sophos;i="5.90,239,1643702400"; d="scan'208";a="243089867" Received: from orsmga006.jf.intel.com ([10.7.209.51]) by orsmga106.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 05 Apr 2022 21:50:36 -0700 X-IronPort-AV: E=Sophos;i="5.90,239,1643702400"; d="scan'208";a="524302385" Received: from dchang1-mobl3.amr.corp.intel.com (HELO khuang2-desk.gar.corp.intel.com) ([10.254.29.17]) by orsmga006-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 05 Apr 2022 21:50:32 -0700 From: Kai Huang To: linux-kernel@vger.kernel.org, kvm@vger.kernel.org Cc: seanjc@google.com, pbonzini@redhat.com, dave.hansen@intel.com, len.brown@intel.com, tony.luck@intel.com, rafael.j.wysocki@intel.com, reinette.chatre@intel.com, dan.j.williams@intel.com, peterz@infradead.org, ak@linux.intel.com, kirill.shutemov@linux.intel.com, sathyanarayanan.kuppuswamy@linux.intel.com, isaku.yamahata@intel.com, kai.huang@intel.com Subject: [PATCH v3 13/21] x86/virt/tdx: Allocate and set up PAMTs for TDMRs Date: Wed, 6 Apr 2022 16:49:25 +1200 Message-Id: X-Mailer: git-send-email 2.35.1 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,RDNS_NONE,SPF_HELO_NONE,T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org In order to provide crypto protection to guests, the TDX module uses additional metadata to record things like which guest "owns" a given page of memory. This metadata, referred as Physical Address Metadata Table (PAMT), essentially serves as the 'struct page' for the TDX module. PAMTs are not reserved by hardware upfront. They must be allocated by the kernel and then given to the TDX module. TDX supports 3 page sizes: 4K, 2M, and 1G. Each "TD Memory Region" (TDMR) has 3 PAMTs to track the 3 supported page sizes respectively. Each PAMT must be a physically contiguous area from the Convertible Memory Regions (CMR). However, the PAMTs which track pages in one TDMR do not need to reside within that TDMR but can be anywhere in CMRs. If one PAMT overlaps with any TDMR, the overlapping part must be reported as a reserved area in that particular TDMR. Use alloc_contig_pages() since PAMT must be a physically contiguous area and it may be potentially large (~1/256th of the size of the given TDMR). The current version of TDX supports at most 16 reserved areas per TDMR to cover both PAMTs and potential memory holes within the TDMR. If many PAMTs are allocated within a single TDMR, 16 reserved areas may not be sufficient to cover all of them. Adopt the following policies when allocating PAMTs for a given TDMR: - Allocate three PAMTs of the TDMR in one contiguous chunk to minimize the total number of reserved areas consumed for PAMTs. - Try to first allocate PAMT from the local node of the TDMR for better NUMA locality. Signed-off-by: Kai Huang --- arch/x86/Kconfig | 1 + arch/x86/virt/vmx/tdx/tdx.c | 165 ++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 7414625b938f..ff68d0829bd7 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1973,6 +1973,7 @@ config INTEL_TDX_HOST depends on CPU_SUP_INTEL depends on X86_64 select NUMA_KEEP_MEMINFO if NUMA + depends on CONTIG_ALLOC help Intel Trust Domain Extensions (TDX) protects guest VMs from malicious host and certain physical attacks. This option enables necessary TDX diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index 82534e70df96..1b807dcbc101 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "tdx.h" @@ -66,6 +67,16 @@ #define TDMR_START(_tdmr) ((_tdmr)->base) #define TDMR_END(_tdmr) ((_tdmr)->base + (_tdmr)->size) +/* Page sizes supported by TDX */ +enum tdx_page_sz { + TDX_PG_4K = 0, + TDX_PG_2M, + TDX_PG_1G, + TDX_PG_MAX, +}; + +#define TDX_HPAGE_SHIFT 9 + /* * TDX module status during initialization */ @@ -959,6 +970,148 @@ static int create_tdmrs(struct tdmr_info **tdmr_array, int *tdmr_num) return ret; } +/* Calculate PAMT size given a TDMR and a page size */ +static unsigned long __tdmr_get_pamt_sz(struct tdmr_info *tdmr, + enum tdx_page_sz pgsz) +{ + unsigned long pamt_sz; + + pamt_sz = (tdmr->size >> ((TDX_HPAGE_SHIFT * pgsz) + PAGE_SHIFT)) * + tdx_sysinfo.pamt_entry_size; + /* PAMT size must be 4K aligned */ + pamt_sz = ALIGN(pamt_sz, PAGE_SIZE); + + return pamt_sz; +} + +/* Calculate the size of all PAMTs for a TDMR */ +static unsigned long tdmr_get_pamt_sz(struct tdmr_info *tdmr) +{ + enum tdx_page_sz pgsz; + unsigned long pamt_sz; + + pamt_sz = 0; + for (pgsz = TDX_PG_4K; pgsz < TDX_PG_MAX; pgsz++) + pamt_sz += __tdmr_get_pamt_sz(tdmr, pgsz); + + return pamt_sz; +} + +/* + * Locate the NUMA node containing the start of the given TDMR's first + * RAM entry. The given TDMR may also cover memory in other NUMA nodes. + */ +static int tdmr_get_nid(struct tdmr_info *tdmr) +{ + u64 start, end; + int i; + + /* Find the first RAM entry covered by the TDMR */ + e820_for_each_mem(i, start, end) + if (end > TDMR_START(tdmr)) + break; + + /* + * One TDMR must cover at least one (or partial) RAM entry, + * otherwise it is kernel bug. WARN_ON() in this case. + */ + if (WARN_ON_ONCE((start >= end) || start >= TDMR_END(tdmr))) + return 0; + + /* + * The first RAM entry may be partially covered by the previous + * TDMR. In this case, use TDMR's start to find the NUMA node. + */ + if (start < TDMR_START(tdmr)) + start = TDMR_START(tdmr); + + return phys_to_target_node(start); +} + +static int tdmr_setup_pamt(struct tdmr_info *tdmr) +{ + unsigned long tdmr_pamt_base, pamt_base[TDX_PG_MAX]; + unsigned long pamt_sz[TDX_PG_MAX]; + unsigned long pamt_npages; + struct page *pamt; + enum tdx_page_sz pgsz; + int nid; + + /* + * Allocate one chunk of physically contiguous memory for all + * PAMTs. This helps minimize the PAMT's use of reserved areas + * in overlapped TDMRs. + */ + nid = tdmr_get_nid(tdmr); + pamt_npages = tdmr_get_pamt_sz(tdmr) >> PAGE_SHIFT; + pamt = alloc_contig_pages(pamt_npages, GFP_KERNEL, nid, + &node_online_map); + if (!pamt) + return -ENOMEM; + + /* Calculate PAMT base and size for all supported page sizes. */ + tdmr_pamt_base = page_to_pfn(pamt) << PAGE_SHIFT; + for (pgsz = TDX_PG_4K; pgsz < TDX_PG_MAX; pgsz++) { + unsigned long sz = __tdmr_get_pamt_sz(tdmr, pgsz); + + pamt_base[pgsz] = tdmr_pamt_base; + pamt_sz[pgsz] = sz; + + tdmr_pamt_base += sz; + } + + tdmr->pamt_4k_base = pamt_base[TDX_PG_4K]; + tdmr->pamt_4k_size = pamt_sz[TDX_PG_4K]; + tdmr->pamt_2m_base = pamt_base[TDX_PG_2M]; + tdmr->pamt_2m_size = pamt_sz[TDX_PG_2M]; + tdmr->pamt_1g_base = pamt_base[TDX_PG_1G]; + tdmr->pamt_1g_size = pamt_sz[TDX_PG_1G]; + + return 0; +} + +static void tdmr_free_pamt(struct tdmr_info *tdmr) +{ + unsigned long pamt_pfn, pamt_sz; + + pamt_pfn = tdmr->pamt_4k_base >> PAGE_SHIFT; + pamt_sz = tdmr->pamt_4k_size + tdmr->pamt_2m_size + tdmr->pamt_1g_size; + + /* Do nothing if PAMT hasn't been allocated for this TDMR */ + if (!pamt_sz) + return; + + if (WARN_ON(!pamt_pfn)) + return; + + free_contig_range(pamt_pfn, pamt_sz >> PAGE_SHIFT); +} + +static void tdmrs_free_pamt_all(struct tdmr_info **tdmr_array, int tdmr_num) +{ + int i; + + for (i = 0; i < tdmr_num; i++) + tdmr_free_pamt(tdmr_array[i]); +} + +/* Allocate and set up PAMTs for all TDMRs */ +static int tdmrs_setup_pamt_all(struct tdmr_info **tdmr_array, int tdmr_num) +{ + int i, ret; + + for (i = 0; i < tdmr_num; i++) { + ret = tdmr_setup_pamt(tdmr_array[i]); + if (ret) + goto err; + } + + return 0; +err: + tdmrs_free_pamt_all(tdmr_array, tdmr_num); + return -ENOMEM; +} + static int construct_tdmrs(struct tdmr_info **tdmr_array, int *tdmr_num) { int ret; @@ -971,8 +1124,14 @@ static int construct_tdmrs(struct tdmr_info **tdmr_array, int *tdmr_num) if (ret) goto err; + ret = tdmrs_setup_pamt_all(tdmr_array, *tdmr_num); + if (ret) + goto err_free_tdmrs; + /* Return -EFAULT until constructing TDMRs is done */ ret = -EFAULT; + tdmrs_free_pamt_all(tdmr_array, *tdmr_num); +err_free_tdmrs: free_tdmrs(tdmr_array, *tdmr_num); err: return ret; @@ -1022,6 +1181,12 @@ static int init_tdx_module(void) * initialization are done. */ ret = -EFAULT; + /* + * Free PAMTs allocated in construct_tdmrs() when TDX module + * initialization fails. + */ + if (ret) + tdmrs_free_pamt_all(tdmr_array, tdmr_num); out_free_tdmrs: /* * TDMRs are only used during initializing TDX module. Always -- 2.35.1