Received: by 2002:a05:7412:d8a:b0:e2:908c:2ebd with SMTP id b10csp3075646rdg; Tue, 17 Oct 2023 04:04:13 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGdqN8ukl94O5/xM0ZzN0Xa1lbIDSQLZFcFRaJk/E2oaf+027VDCH9ktA15BvFCHtDT0ard X-Received: by 2002:a17:90a:86cc:b0:274:8949:d834 with SMTP id y12-20020a17090a86cc00b002748949d834mr1433904pjv.49.1697540653126; Tue, 17 Oct 2023 04:04:13 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1697540653; cv=none; d=google.com; s=arc-20160816; b=AUxD3f4zLXYof4dvUwqitjkGue3zCk2FudjM+vpTC46UyNHTit8KmjaqGF5wFxFx7L 5lKAY6Oa7iTjoEKv7vgT66g/bOzckHlbYe9ec2gmwMKBbLp4Rf/5Jv0mGHE0ukf3PY0p ZeHgB5tPqsd0tWNakyDw/dRa2mHAU6/qrPY1hCrcnNpqVeGpUD6VttsoQInwNZOwXRQT eqzwA0YixhD6Xs5z7yqvbSqdwT/fmRdV5e/lajNSwMDBA5k4ptBFgX+ngWlZ60kBT7sr MEEK5l6Lgpf+PyYNm/kCRPnvuB1pFYi4jj0NPt74Tzem+r7peHLZ79D96d6lRo8kGVp5 /yOg== 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=vi+S67K9iz1+ID7NL4s1aNDdLF+h05QVpwkCgoWqBtA=; fh=WBgbLtMencYhgeHuu2sUs5b9THiYLgy17d2w1N+xuf4=; b=vod5XHi2+QVfYHFt5XPUFLzkl5W7R2p4Li7t1XlhP6Eidh+YoLaY04eCi6MpMMwUo1 dWFaCwkNdcmFGdghbVz+gOUdEj7pZD6W3gHm0c2B92/phkNTEYBOZN2LAkz4poouzcDy 5s6X5gm1wK4u/idkQsTJtdRLBAFjDGh+8KHGs90VPrxW0ENXRQCFBBdWfF5iGn55yvL8 2NIFxOLMoAu5+gSFYQvzS5IIKpo18Tpf337P6pkG5cNkW5l1BWpyVgQf/yp/vcODaSir tUdmLEiiSlyGhHLwoCHs9hRrJpInIJmRYARIcOjrBWNEqGsGcE5U32OCelKPabczKYSx fnZw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b="h6T/X97q"; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:2 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 agentk.vger.email (agentk.vger.email. [2620:137:e000::3:2]) by mx.google.com with ESMTPS id c9-20020a17090ab28900b00277387bc11dsi1331010pjr.188.2023.10.17.04.04.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 17 Oct 2023 04:04:13 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:2 as permitted sender) client-ip=2620:137:e000::3:2; Authentication-Results: mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b="h6T/X97q"; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:2 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 agentk.vger.email (Postfix) with ESMTP id 777B8803686F; Tue, 17 Oct 2023 04:04:09 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at agentk.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234809AbjJQLD4 (ORCPT + 99 others); Tue, 17 Oct 2023 07:03:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51714 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1343645AbjJQKQh (ORCPT ); Tue, 17 Oct 2023 06:16:37 -0400 Received: from mgamail.intel.com (mgamail.intel.com [192.55.52.43]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C89AB182; Tue, 17 Oct 2023 03:16:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1697537765; x=1729073765; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=9dY6RLECRCet1JBs9dKs8gG3/dlOSjz1bQVPNQeewz0=; b=h6T/X97qbXQiDhKsfY9twg8KdNCEbVIaYGDVSWs1usM4W/w8FUcfdC/x xuz7F151Fpg7AYX/Xp1iVa7ErgE/xjX7kcvcRDZO888WBY2zCcspn5zSk CCmWViGxI3SXRVA2t+hnP/MIv9b1/aftqL+sm1E+ZV9ln+wZF5GWA+IRr 7zsC5F2qDwWIt0aftLh/uOGP3fvcXZodzrnD5tiGsbDv7eKnbv+Rd5f7h TiJnyLOvr7jHuKiVhAL7Uq0TXRAA3AmJUlpycNdXqAarX7uznA2Zy2MJI lnGCHvveJ4BZpdaaYkyWl1D+GVAy3bvqN7VSBlzoUI8YIJxyaQZdPF1pB Q==; X-IronPort-AV: E=McAfee;i="6600,9927,10865"; a="471972374" X-IronPort-AV: E=Sophos;i="6.03,231,1694761200"; d="scan'208";a="471972374" Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by fmsmga105.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Oct 2023 03:16:05 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10865"; a="872503630" X-IronPort-AV: E=Sophos;i="6.03,231,1694761200"; d="scan'208";a="872503630" Received: from chowe-mobl.amr.corp.intel.com (HELO khuang2-desk.gar.corp.intel.com) ([10.255.229.64]) by fmsmga002-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Oct 2023 03:15:59 -0700 From: Kai Huang To: linux-kernel@vger.kernel.org, kvm@vger.kernel.org Cc: x86@kernel.org, dave.hansen@intel.com, kirill.shutemov@linux.intel.com, peterz@infradead.org, tony.luck@intel.com, tglx@linutronix.de, bp@alien8.de, mingo@redhat.com, hpa@zytor.com, seanjc@google.com, pbonzini@redhat.com, rafael@kernel.org, david@redhat.com, dan.j.williams@intel.com, len.brown@intel.com, ak@linux.intel.com, isaku.yamahata@intel.com, ying.huang@intel.com, chao.gao@intel.com, sathyanarayanan.kuppuswamy@linux.intel.com, nik.borisov@suse.com, bagasdotme@gmail.com, sagis@google.com, imammedo@redhat.com, kai.huang@intel.com Subject: [PATCH v14 10/23] x86/virt/tdx: Add placeholder to construct TDMRs to cover all TDX memory regions Date: Tue, 17 Oct 2023 23:14:34 +1300 Message-ID: <0e1b79c227b5cfef94a632ae9b5b94643593126b.1697532085.git.kai.huang@intel.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-0.9 required=5.0 tests=DKIMWL_WL_HIGH,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on agentk.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 (agentk.vger.email [0.0.0.0]); Tue, 17 Oct 2023 04:04:09 -0700 (PDT) After the kernel selects all TDX-usable memory regions, the kernel needs to pass those regions to the TDX module via data structure "TD Memory Region" (TDMR). Add a placeholder to construct a list of TDMRs (in multiple steps) to cover all TDX-usable memory regions. === Long Version === TDX provides increased levels of memory confidentiality and integrity. This requires special hardware support for features like memory encryption and storage of memory integrity checksums. Not all memory satisfies these requirements. As a result, TDX introduced the concept of a "Convertible Memory Region" (CMR). During boot, the firmware builds a list of all of the memory ranges which can provide the TDX security guarantees. The list of these ranges is available to the kernel by querying the TDX module. The TDX architecture needs additional metadata to record things like which TD guest "owns" a given page of memory. This metadata essentially serves as the 'struct page' for the TDX module. The space for this metadata is not reserved by the hardware up front and must be allocated by the kernel and given to the TDX module. Since this metadata consumes space, the VMM can choose whether or not to allocate it for a given area of convertible memory. If it chooses not to, the memory cannot receive TDX protections and can not be used by TDX guests as private memory. For every memory region that the VMM wants to use as TDX memory, it sets up a "TD Memory Region" (TDMR). Each TDMR represents a physically contiguous convertible range and must also have its own physically contiguous metadata table, referred to as a Physical Address Metadata Table (PAMT), to track status for each page in the TDMR range. Unlike a CMR, each TDMR requires 1G granularity and alignment. To support physical RAM areas that don't meet those strict requirements, each TDMR permits a number of internal "reserved areas" which can be placed over memory holes. If PAMT metadata is placed within a TDMR it must be covered by one of these reserved areas. Let's summarize the concepts: CMR - Firmware-enumerated physical ranges that support TDX. CMRs are 4K aligned. TDMR - Physical address range which is chosen by the kernel to support TDX. 1G granularity and alignment required. Each TDMR has reserved areas where TDX memory holes and overlapping PAMTs can be represented. PAMT - Physically contiguous TDX metadata. One table for each page size per TDMR. Roughly 1/256th of TDMR in size. 256G TDMR = ~1G PAMT. As one step of initializing the TDX module, the kernel configures TDX-usable memory regions by passing a list of TDMRs to the TDX module. Constructing the list of TDMRs consists below steps: 1) Fill out TDMRs to cover all memory regions that the TDX module will use for TD memory. 2) Allocate and set up PAMT for each TDMR. 3) Designate reserved areas for each TDMR. Add a placeholder to construct TDMRs to do the above steps. To keep things simple, just allocate enough space to hold maximum number of TDMRs up front. Always free the buffer of TDMRs since they are only used during module initialization. Signed-off-by: Kai Huang Reviewed-by: Isaku Yamahata Reviewed-by: Dave Hansen Reviewed-by: Kirill A. Shutemov --- v13 -> v14: - No change. v12 -> v13: - No change. v11 -> v12: - Added tags from Dave/Kirill. v10 -> v11: - Changed to keep TDMRs after module initialization to deal with TDX erratum in future patches. v9 -> v10: - Changed the TDMR list from static variable back to local variable as now TDX module isn't disabled when tdx_cpu_enable() fails. v8 -> v9: - Changes around 'struct tdmr_info_list' (Dave): - Moved the declaration from tdx.c to tdx.h. - Renamed 'first_tdmr' to 'tdmrs'. - 'nr_tdmrs' -> 'nr_consumed_tdmrs'. - Changed 'tdmrs' to 'void *'. - Improved comments for all structure members. - Added a missing empty line in alloc_tdmr_list() (Dave). v7 -> v8: - Improved changelog to tell this is one step of "TODO list" in init_tdx_module(). - Other changelog improvement suggested by Dave (with "Create TDMRs" to "Fill out TDMRs" to align with the code). - Added a "TODO list" comment to lay out the steps to construct TDMRs, following the same idea of "TODO list" in tdx_module_init(). - Introduced 'struct tdmr_info_list' (Dave) - Further added additional members (tdmr_sz/max_tdmrs/nr_tdmrs) to simplify getting TDMR by given index, and reduce passing arguments around functions. - Added alloc_tdmr_list()/free_tdmr_list() accordingly, which internally uses tdmr_size_single() (Dave). - tdmr_num -> nr_tdmrs (Dave). v6 -> v7: - Improved commit message to explain 'int' overflow cannot happen in cal_tdmr_size() and alloc_tdmr_array(). -- Andy/Dave. ... --- arch/x86/virt/vmx/tdx/tdx.c | 97 ++++++++++++++++++++++++++++++++++++- arch/x86/virt/vmx/tdx/tdx.h | 32 ++++++++++++ 2 files changed, 127 insertions(+), 2 deletions(-) diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index 24fa8020cc7a..675c37123d45 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -301,9 +302,84 @@ static int build_tdx_memlist(struct list_head *tmb_list) return ret; } +/* Calculate the actual TDMR size */ +static int tdmr_size_single(u16 max_reserved_per_tdmr) +{ + int tdmr_sz; + + /* + * The actual size of TDMR depends on the maximum + * number of reserved areas. + */ + tdmr_sz = sizeof(struct tdmr_info); + tdmr_sz += sizeof(struct tdmr_reserved_area) * max_reserved_per_tdmr; + + return ALIGN(tdmr_sz, TDMR_INFO_ALIGNMENT); +} + +static int alloc_tdmr_list(struct tdmr_info_list *tdmr_list, + struct tdsysinfo_struct *sysinfo) +{ + size_t tdmr_sz, tdmr_array_sz; + void *tdmr_array; + + tdmr_sz = tdmr_size_single(sysinfo->max_reserved_per_tdmr); + tdmr_array_sz = tdmr_sz * sysinfo->max_tdmrs; + + /* + * To keep things simple, allocate all TDMRs together. + * The buffer needs to be physically contiguous to make + * sure each TDMR is physically contiguous. + */ + tdmr_array = alloc_pages_exact(tdmr_array_sz, + GFP_KERNEL | __GFP_ZERO); + if (!tdmr_array) + return -ENOMEM; + + tdmr_list->tdmrs = tdmr_array; + + /* + * Keep the size of TDMR to find the target TDMR + * at a given index in the TDMR list. + */ + tdmr_list->tdmr_sz = tdmr_sz; + tdmr_list->max_tdmrs = sysinfo->max_tdmrs; + tdmr_list->nr_consumed_tdmrs = 0; + + return 0; +} + +static void free_tdmr_list(struct tdmr_info_list *tdmr_list) +{ + free_pages_exact(tdmr_list->tdmrs, + tdmr_list->max_tdmrs * tdmr_list->tdmr_sz); +} + +/* + * Construct a list of TDMRs on the preallocated space in @tdmr_list + * to cover all TDX memory regions in @tmb_list based on the TDX module + * information in @sysinfo. + */ +static int construct_tdmrs(struct list_head *tmb_list, + struct tdmr_info_list *tdmr_list, + struct tdsysinfo_struct *sysinfo) +{ + /* + * TODO: + * + * - Fill out TDMRs to cover all TDX memory regions. + * - Allocate and set up PAMTs for each TDMR. + * - Designate reserved areas for each TDMR. + * + * Return -EINVAL until constructing TDMRs is done + */ + return -EINVAL; +} + static int init_tdx_module(void) { struct tdsysinfo_struct *tdsysinfo; + struct tdmr_info_list tdmr_list; struct cmr_info *cmr_array; int tdsysinfo_size; int cmr_array_size; @@ -345,11 +421,19 @@ static int init_tdx_module(void) if (ret) goto out_put_tdxmem; + /* Allocate enough space for constructing TDMRs */ + ret = alloc_tdmr_list(&tdmr_list, tdsysinfo); + if (ret) + goto out_free_tdxmem; + + /* Cover all TDX-usable memory regions in TDMRs */ + ret = construct_tdmrs(&tdx_memlist, &tdmr_list, tdsysinfo); + if (ret) + goto out_free_tdmrs; + /* * TODO: * - * - Construct a list of "TD Memory Regions" (TDMRs) to cover - * all TDX-usable memory regions. * - Configure the TDMRs and the global KeyID to the TDX module. * - Configure the global KeyID on all packages. * - Initialize all TDMRs. @@ -357,6 +441,15 @@ static int init_tdx_module(void) * Return error before all steps are done. */ ret = -EINVAL; +out_free_tdmrs: + /* + * Always free the buffer of TDMRs as they are only used during + * module initialization. + */ + free_tdmr_list(&tdmr_list); +out_free_tdxmem: + if (ret) + free_tdx_memlist(&tdx_memlist); out_put_tdxmem: /* * @tdx_memlist is written here and read at memory hotplug time. diff --git a/arch/x86/virt/vmx/tdx/tdx.h b/arch/x86/virt/vmx/tdx/tdx.h index 39c9c6fdc11e..536d89928cd6 100644 --- a/arch/x86/virt/vmx/tdx/tdx.h +++ b/arch/x86/virt/vmx/tdx/tdx.h @@ -79,6 +79,29 @@ struct tdsysinfo_struct { DECLARE_FLEX_ARRAY(struct cpuid_config, cpuid_configs); } __packed; +struct tdmr_reserved_area { + u64 offset; + u64 size; +} __packed; + +#define TDMR_INFO_ALIGNMENT 512 + +struct tdmr_info { + u64 base; + u64 size; + u64 pamt_1g_base; + u64 pamt_1g_size; + u64 pamt_2m_base; + u64 pamt_2m_size; + u64 pamt_4k_base; + u64 pamt_4k_size; + /* + * Actual number of reserved areas depends on + * 'struct tdsysinfo_struct'::max_reserved_per_tdmr. + */ + DECLARE_FLEX_ARRAY(struct tdmr_reserved_area, reserved_areas); +} __packed __aligned(TDMR_INFO_ALIGNMENT); + /* * Do not put any hardware-defined TDX structure representations below * this comment! @@ -97,4 +120,13 @@ struct tdx_memblock { unsigned long end_pfn; }; +struct tdmr_info_list { + void *tdmrs; /* Flexible array to hold 'tdmr_info's */ + int nr_consumed_tdmrs; /* How many 'tdmr_info's are in use */ + + /* Metadata for finding target 'tdmr_info' and freeing @tdmrs */ + int tdmr_sz; /* Size of one 'tdmr_info' */ + int max_tdmrs; /* How many 'tdmr_info's are allocated */ +}; + #endif -- 2.41.0