Received: by 2002:a6b:fb09:0:0:0:0:0 with SMTP id h9csp3420236iog; Mon, 27 Jun 2022 16:01:10 -0700 (PDT) X-Google-Smtp-Source: AGRyM1vkyjKYGM0xwMU0wLHed8gmJMOPU5a9B+BWh2X4leqFjckrtFXa0S/jQNGmGMJox0lFn+xb X-Received: by 2002:a17:906:e16:b0:726:a026:27ff with SMTP id l22-20020a1709060e1600b00726a02627ffmr7801193eji.573.1656370870568; Mon, 27 Jun 2022 16:01:10 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656370870; cv=none; d=google.com; s=arc-20160816; b=mCiXTJSXdpGob7nvPiOf8FaK6PbnyFz9SQ7BJBtMlpkdxbMCOzMVe5rPrFaENOt1il /r0UWZmjASvGE9cFgNdjGcYHXCEo214mrqaqIVoy8pbOT4MUPOI5OSkgHVZDIXeVPKiC CcOlRZXlu26fOBbVMH3ER8DFo+j3qL5UKbk7CRGhJNn7WVnVzYHST7B7qytdg2lXTIBF WxF/fztc3qyvX92mgXIdVmfDbn45sxCndtpMkQvfPDi21coir+UCDtu/R1rSeGuzSjy0 J+F5iUEa6nVYxeKY0YtW84mRugYeQpJR5sWEBxo8Xd+cYyPoUJ8s5fpu0w3i97+vHnAk vYpQ== 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=EnDkZr5UA3Ptfhd7J2YDlqgzlZuS1PwxAt/txRYg1fM=; b=DFL39GC23RyXPY1P9uoqPAm9I20RTFHkxwEjKaS/pbGaNyDi5JJl19TaD/BAUuc+/r iXnQNiM151hgR9PMz4aMumRETHRLvzGpibhFzgtsM3gO/YXo9yM2a6BB49mHTmnCKs86 cL92cNQl9fN7OqO9VkmWBhf7gVW84CcXQ0jVHlKLF1x3P2mzxeLJG+U/gREE+Y9JOSpB Vl+vA6oT16OgHTqbPr45JAqlXftBx0U8Z6q01Al9RkFsZ0StNIy1pH5SYl4VFecyAnZI bfmJhi1CcwHCooaqKMQGY3qKBg6+IPMgRZ5WZedd824ZHOA6EUedPyOGfd3EhsP5D6YG qn0Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b=U5aGSD2a; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 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 out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id bt24-20020a170906b15800b00718dba48d9asi12357414ejb.72.2022.06.27.16.00.45; Mon, 27 Jun 2022 16:01:10 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b=U5aGSD2a; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S242432AbiF0WCC (ORCPT + 99 others); Mon, 27 Jun 2022 18:02:02 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60890 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S241675AbiF0V4Q (ORCPT ); Mon, 27 Jun 2022 17:56:16 -0400 Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D0C3063C5; Mon, 27 Jun 2022 14:55:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1656366911; x=1687902911; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=zxMlIXklQM/ISaSdvRZFL/IvRDO0LUHpaRctG+gz45c=; b=U5aGSD2aCCage91jh2V795osTLbNpCKzvSjD+iHaEG6NUk0rUn0NuaWS TDpfHNacjoEjn/pQh4ua4MQCMDWNwg1CFPx6pzDLO/GyxRNhFiRX3qQei nt/IClbT/D08IPV+1jerFcbzeelaQF2hkostAwechoIfBrObPLfXEnWrN ZJCccnZSkkuoBTjq+unWHd86jmIHphVNBUR7k3nW00nYpYujbulWcaC36 Yk0shBVSEwMOUcYouGRlexGzvA0MchwVK3GIqx3m3jsv1VSKUVTMx9aVd NcNEs5rjk/IqsCHsr0Vbo1CsfFhijB+QVZTnXcy2dnAmUtujBQ9tMlIVT w==; X-IronPort-AV: E=McAfee;i="6400,9594,10391"; a="279116160" X-IronPort-AV: E=Sophos;i="5.92,227,1650956400"; d="scan'208";a="279116160" Received: from fmsmga004.fm.intel.com ([10.253.24.48]) by fmsmga102.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 27 Jun 2022 14:55:03 -0700 X-IronPort-AV: E=Sophos;i="5.92,227,1650956400"; d="scan'208";a="657863787" Received: from ls.sc.intel.com (HELO localhost) ([143.183.96.54]) by fmsmga004-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 27 Jun 2022 14:55:03 -0700 From: isaku.yamahata@intel.com To: kvm@vger.kernel.org, linux-kernel@vger.kernel.org Cc: isaku.yamahata@intel.com, isaku.yamahata@gmail.com, Paolo Bonzini Subject: [PATCH v7 102/102] KVM: x86: design documentation on TDX support of x86 KVM TDP MMU Date: Mon, 27 Jun 2022 14:54:34 -0700 Message-Id: <7af4efe32638544aecb58ed7365d0ef2ea6f85ea.1656366338.git.isaku.yamahata@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-7.5 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_HI, SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE autolearn=ham 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 From: Isaku Yamahata Add a high level design document on TDX changes to TDP MMU. Signed-off-by: Isaku Yamahata --- Documentation/virt/kvm/tdx-tdp-mmu.rst | 466 +++++++++++++++++++++++++ 1 file changed, 466 insertions(+) create mode 100644 Documentation/virt/kvm/tdx-tdp-mmu.rst diff --git a/Documentation/virt/kvm/tdx-tdp-mmu.rst b/Documentation/virt/kvm/tdx-tdp-mmu.rst new file mode 100644 index 000000000000..6d63bb75f785 --- /dev/null +++ b/Documentation/virt/kvm/tdx-tdp-mmu.rst @@ -0,0 +1,466 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Design of TDP MMU for TDX support +================================= +This document describes a (high level) design for TDX support of KVM TDP MMU of +x86 KVM. + +In this document, we use "TD" or "guest TD" to differentiate it from the current +"VM" (Virtual Machine), which is supported by KVM today. + + +Background of TDX +================= +TD private memory is designed to hold TD private content, encrypted by the CPU +using the TD ephemeral key. An encryption engine holds a table of encryption +keys, and an encryption key is selected for each memory transaction based on a +Host Key Identifier (HKID). By design, the host VMM does not have access to the +encryption keys. + +In the first generation of MKTME, HKID is "stolen" from the physical address by +allocating a configurable number of bits from the top of the physical address. +The HKID space is partitioned into shared HKIDs for legacy MKTME accesses and +private HKIDs for SEAM-mode-only accesses. We use 0 for the shared HKID on the +host so that MKTME can be opaque or bypassed on the host. + +During TDX non-root operation (i.e. guest TD), memory accesses can be qualified +as either shared or private, based on the value of a new SHARED bit in the Guest +Physical Address (GPA). The CPU translates shared GPAs using the usual VMX EPT +(Extended Page Table) or "Shared EPT" (in this document), which resides in the +host VMM memory. The Shared EPT is directly managed by the host VMM - the same +as with the current VMX. Since guest TDs usually require I/O, and the data +exchange needs to be done via shared memory, thus KVM needs to use the current +EPT functionality even for TDs. + +The CPU translates private GPAs using a separate Secure EPT. The Secure EPT +pages are encrypted and integrity-protected with the TD's ephemeral private key. +Secure EPT can be managed _indirectly_ by the host VMM, using the TDX interface +functions (SEAMCALLs), and thus conceptually Secure EPT is a subset of EPT +because not all functionalities are available. + +Since the execution of such interface functions takes much longer time than +accessing memory directly, in KVM we use the existing TDP code to mirror the +Secure EPT for the TD. And we think there are at least two options today in +terms of the timing for executing such SEAMCALLs: + +1. synchronous, i.e. while walking the TDP page tables, or +2. post-walk, i.e. record what needs to be done to the real Secure EPT during + the walk, and execute SEAMCALLs later. + +The option 1 seems to be more intuitive and simpler, but the Secure EPT +concurrency rules are different from the ones of the TDP or EPT. For example, +MEM.SEPT.RD acquire shared access to the whole Secure EPT tree of the target + +Secure EPT(SEPT) operations +--------------------------- +Secure EPT is an Extended Page Table for GPA-to-HPA translation of TD private +HPA. A Secure EPT is designed to be encrypted with the TD's ephemeral private +key. SEPT pages are allocated by the host VMM via Intel TDX functions, but their +content is intended to be hidden and is not architectural. + +Unlike the conventional EPT, the CPU can't directly read/write its entry. +Instead, TDX SEAMCALL API is used. Several SEAMCALLs correspond to operation on +the EPT entry. + +* TDH.MEM.SEPT.ADD(): + Add a secure EPT page from the secure EPT tree. This corresponds to updating + the non-leaf EPT entry with present bit set + +* TDH.MEM.SEPT.REMOVE(): + Remove the secure page from the secure EPT tree. There is no corresponding + to the EPT operation. + +* TDH.MEM.SEPT.RD(): + Read the secure EPT entry. This corresponds to reading the EPT entry as + memory. Please note that this is much slower than direct memory reading. + +* TDH.MEM.PAGE.ADD() and TDH.MEM.PAGE.AUG(): + Add a private page to the secure EPT tree. This corresponds to updating the + leaf EPT entry with present bit set. + +* THD.MEM.PAGE.REMOVE(): + Remove a private page from the secure EPT tree. There is no corresponding + to the EPT operation. + +* TDH.MEM.RANGE.BLOCK(): + This (mostly) corresponds to clearing the present bit of the leaf EPT entry. + Note that the private page is still linked in the secure EPT. To remove it + from the secure EPT, TDH.MEM.SEPT.REMOVE() and TDH.MEM.PAGE.REMOVE() needs to + be called. + +* TDH.MEM.TRACK(): + Increment the TLB epoch counter. This (mostly) corresponds to EPT TLB flush. + Note that the private page is still linked in the secure EPT. To remove it + from the secure EPT, tdh_mem_page_remove() needs to be called. + + +Adding private page +------------------- +The procedure of populating the private page looks as follows. + +1. TDH.MEM.SEPT.ADD(512G level) +2. TDH.MEM.SEPT.ADD(1G level) +3. TDH.MEM.SEPT.ADD(2M level) +4. TDH.MEM.PAGE.AUG(4K level) + +Those operations correspond to updating the EPT entries. + +Dropping private page and TLB shootdown +--------------------------------------- +The procedure of dropping the private page looks as follows. + +1. TDH.MEM.RANGE.BLOCK(4K level) + This mostly corresponds to clear the present bit in the EPT entry. This + prevents (or blocks) TLB entry from creating in the future. Note that the + private page is still linked in the secure EPT tree and the existing cache + entry in the TLB isn't flushed. +2. TDH.MEM.TRACK(range) and TLB shootdown + This mostly corresponds to the EPT TLB shootdown. Because all vcpus share + the same Secure EPT, all vcpus need to flush TLB. + * TDH.MEM.TRACK(range) by one vcpu. It increments the global internal TLB + epoch counter. + * send IPI to remote vcpus + * Other vcpu exits to VMM from guest TD and then re-enter. TDH.VP.ENTER(). + * TDH.VP.ENTER() checks the TLB epoch counter and If its TLB is old, flush + TLB. + Note that only single vcpu issues tdh_mem_track(). + Note that the private page is still linked in the secure EPT tree, unlike the + conventional EPT. +3. TDH.MEM.PAGE.PROMOTE, TDH.MEM.PAGEDEMOTE(), TDH.MEM.PAGE.RELOCATE(), or + TDH.MEM.PAGE.REMOVE() + There is no corresponding operation to the conventional EPT. + * When changing page size (e.g. 4K <-> 2M) TDH.MEM.PAGE.PROMOTE() or + TDH.MEM.PAGE.DEMOTE() is used. During those operation, the guest page is + kept referenced in the Secure EPT. + * When migrating page, TDH.MEM.PAGE.RELOCATE(). This requires both source + page and destination page. + * when destroying TD, TDH.MEM.PAGE.REMOVE() removes the private page from the + secure EPT tree. In this case TLB shootdown is not needed because vcpus + don't run any more. + +The basic idea for TDX support +============================== +Because shared EPT is the same as the existing EPT, use the existing logic for +shared EPT. On the other hand, secure EPT requires additional operations +instead of directly reading/writing of the EPT entry. + +On EPT violation, The KVM mmu walks down the EPT tree from the root, determines +the EPT entry to operate, and updates the entry. If necessary, a TLB shootdown +is done. Because it's very slow to directly walk secure EPT by TDX SEAMCALL, +TDH.MEM.SEPT.RD(), the mirror of secure EPT is created and maintained. Add +hooks to KVM MMU to reuse the existing code. + +EPT violation on shared GPA +--------------------------- +(1) EPT violation on shared GPA or zapping shared GPA + walk down shared EPT tree (the existing code) + | + | + V +shared EPT tree (CPU refers.) +(2) update the EPT entry. (the existing code) + TLB shootdown in the case of zapping. + + +EPT violation on private GPA +---------------------------- +(1) EPT violation on private GPA or zapping private GPA + walk down the mirror of secure EPT tree (mostly same as the existing code) + | + | + V +mirror of secure EPT tree (KVM MMU software only. reuse of the existing code) +(2) update the (mirrored) EPT entry. (mostly same as the existing code) +(3) call the hooks with what EPT entry is changed + | + NEW: hooks in KVM MMU + | + V +secure EPT root(CPU refers) +(4) the TDX backend calls necessary TDX SEAMCALLs to update real secure EPT. + +The major modification is to add hooks for the TDX backend for additional +operations and to pass down which EPT, shared EPT, or private EPT is used, and +twist the behavior if we're operating on private EPT. + +The following depicts the relationship. +:: + + KVM | TDX module + | | | + -------------+---------- | | + | | | | + V V | | + shared GPA private GPA | | + CPU shared EPT pointer KVM private EPT pointer | CPU secure EPT pointer + | | | | + | | | | + V V | V + shared EPT private EPT<-------mirror----->Secure EPT + | | | | + | \--------------------+------\ | + | | | | + V | V V + shared guest page | private guest page + | + | + non-encrypted memory | encrypted memory + | + +shared EPT: CPU and KVM walk with shared GPA + Maintained by the existing code +private EPT: KVM walks with private GPA + Maintained by the twisted existing code +secure EPT: CPU walks with private GPA. + Maintained by TDX module with TDX SEAMCALLs via hooks + + +Tracking private EPT page +========================= +Shared EPT pages are managed by struct kvm_mmu_page. They are linked in a list +structure. When necessary, the list is traversed to operate on. Private EPT +pages have different characteristics. For example, private pages can't be +swapped out. When shrinking memory, we'd like to traverse only shared EPT pages +and skip private EPT pages. Likewise, page migration isn't supported for +private pages (yet). Introduce an additional list to track shared EPT pages and +track private EPT pages independently. + +At the beginning of EPT violation, the fault handler knows fault GPA, thus it +knows which EPT to operate on, private or shared. If it's private EPT, +an additional task is done. Something like "if (private) { callback a hook }". +Since the fault handler has deep function calls, it's cumbersome to hold the +information of which EPT is operating. Options to mitigate it are + +1. Pass the information as an argument for the function call. +2. Record the information in struct kvm_mmu_page somehow. +3. Record the information in vcpu structure. + +Option 2 was chosen. Because option 1 requires modifying all the functions. It +would affect badly to the normal case. Option 3 doesn't work well because in +some cases, we need to walk both private and shared EPT. + +The role of the EPT page can be utilized and one bit can be curved out from +unused bits in struct kvm_mmu_page_role. When allocating the EPT page, +initialize the information. Mostly struct kvm_mmu_page is available because +we're operating on EPT pages. + + +The conversion of private GPA and shared GPA +============================================ +A page of a given GPA can be assigned to only private GPA xor shared GPA at one +time. The GPA can't be accessed simultaneously via both private GPA and shared +GPA. On guest startup, all the GPAs are assigned as private. Guest converts +the range of GPA to shared (or private) from private (or shared) by MapGPA +hypercall. MapGPA hypercall takes the start GPA and the size of the region. If +the given start GPA is shared, VMM converts the region into shared (if it's +already shared, nop). If the start GPA is private, VMM converts the region into +private. It implies the guest won't access the unmapped region. private(or +shared) region after converting to shared(or private). + +If the guest TD triggers an EPT violation on the already converted region, the +access won't be allowed (loop in EPT violation) until other vcpu converts back +the region. + +KVM MMU records which GPA is allowed to access, private or shared. It steals +software usable bit from MMU present mask. SPTE_SHARED_MASK. The bit is +recorded in both shared EPT and the mirror of secure EPT. + +* If SPTE_SHARED_MASK cleared in the shared EPT and the mirror of secure EPT: + Private GPA is allowed. Shared GPA is not allowed. + +* SPTE_SHARED_MASK set in the shared EPT and the mirror of secure EPT: + Private GPA is not allowed. Shared GPA is allowed. + +The default is that SPTE_SHARED_MASK is cleared so that the existing KVM +MMU code (mostly) works. + +The reason why the bit is recorded in both shared and private EPT is to optimize +for EPT violation path by penalizing MapGPA hypercall. + +The state machine of EPT entry +------------------------------ +(private EPT entry, shared EPT entry) = + (non-present, non-present): private mapping is allowed + (present, non-present): private mapping is mapped + (non-present | SPTE_SHARED_MASK, non-present | SPTE_SHARED_MASK): + shared mapping is allowed + (non-present | SPTE_SHARED_MASK, present | SPTE_SHARED_MASK): + shared mapping is mapped + (present | SPTE_SHARED_MASK, any) invalid combination + +* map_gpa(private GPA): Mark the region that private GPA is allowed(NEW) + private EPT entry: clear SPTE_SHARED_MASK + present: nop + non-present: nop + non-present | SPTE_SHARED_MASK -> non-present (clear SPTE_SHARED_MASK) + + shared EPT entry: zap the entry, clear SPTE_SHARED_MASK + present: invalid + non-present -> non-present: nop + present | SPTE_SHARED_MASK -> non-present + non-present | SPTE_SHARED_MASK -> non-present + +* map_gpa(shared GPA): Mark the region that shared GPA is allowed(NEW) + private EPT entry: zap and set SPTE_SHARED_MASK + present -> non-present | SPTE_SHARED_MASK + non-present -> non-present | SPTE_SHARED_MASK + non-present | SPTE_SHARED_MASK: nop + + shared EPT entry: set SPTE_SHARED_MASK + present: invalid + non-present -> non-present | SPTE_SHARED_MASK + present | SPTE_SHARED_MASK -> present | SPTE_SHARED_MASK: nop + non-present | SPTE_SHARED_MASK -> non-present | SPTE_SHARED_MASK: nop + +* map(private GPA) + private EPT entry + present: nop + non-present -> present + non-present | SPTE_SHARED_MASK: nop. looping on EPT violation(NEW) + + shared EPT entry: nop + +* map(shared GPA) + private EPT entry: nop + + shared EPT entry + present: invalid + present | SPTE_SHARED_MASK: nop + non-present | SPTE_SHARED_MASK -> present | SPTE_SHARED_MASK + non-present: nop. looping on EPT violation(NEW) + +* zap(private GPA) + private EPT entry: zap the entry with keeping SPTE_SHARED_MASK + present -> non-present + present | SPTE_SHARED_MASK: invalid + non-present: nop as is_shadow_present_pte() is checked + non-present | SPTE_SHARED_MASK: nop as is_shadow_present_pte() is + checked + + shared EPT entry: nop + +* zap(shared GPA) + private EPT entry: nop + + shared EPT entry: zap + any -> non-present + present: invalid + present | SPTE_SHARED_MASK -> non-present | SPTE_SHARED_MASK + non-present: nop as is_shadow_present_pte() is checked + non-present | SPTE_SHARED_MASK: nop as is_shadow_present_pte() is + checked + + +The original TDP MMU and race condition +======================================= +Because vcpus share the EPT, once the EPT entry is zapped, we need to shootdown +TLB. Send IPI to remote vcpus. Remote vcpus flush their down TLBs. Until TLB +shootdown is done, vcpus may reference the zapped guest page. + +TDP MMU uses read lock of mmu_lock to mitigate vcpu contention. When read lock +is obtained, it depends on the atomic update of the EPT entry. (On the other +hand legacy MMU uses write lock.) When vcpu is populating/zapping the EPT entry +with a read lock held, other vcpu may be populating or zapping the same EPT +entry at the same time. + +To avoid the race condition, the entry is frozen. It means the EPT entry is set +to the special value, REMOVED_SPTE which clears the present bit. And then after +TLB shootdown, update the EPT entry to the final value. + +Concurrent zapping +------------------ +1. read lock +2. freeze the EPT entry (atomically set the value to REMOVED_SPTE) + If other vcpu froze the entry, restart page fault. +3. TLB shootdown + * send IPI to remote vcpus + * TLB flush (local and remote) + For each entry update, TLB shootdown is needed because of the + concurrency. +4. atomically set the EPT entry to the final value +5. read unlock + +Concurrent populating +--------------------- +In the case of populating the non-present EPT entry, atomically update the EPT +entry. +1. read lock +2. atomically update the EPT entry + If other vcpu frozen the entry or updated the entry, restart page fault. +3. read unlock + +In the case of updating the present EPT entry (e.g. page migration), the +operation is split into two. Zapping the entry and populating the entry. +1. read lock +2. zap the EPT entry. follow the concurrent zapping case. +3. populate the non-present EPT entry. +4. read unlock + +Non-concurrent batched zapping +------------------------------ +In some cases, zapping the ranges is done exclusively with a write lock held. +In this case, the TLB shootdown is batched into one. + +1. write lock +2. zap the EPT entries by traversing them +3. TLB shootdown +4. write unlock + + +For Secure EPT, TDX SEAMCALLs are needed in addition to updating the mirrored +EPT entry. + +TDX concurrent zapping +---------------------- +Add a hook for TDX SEAMCALLs at the step of the TLB shootdown. + +1. read lock +2. freeze the EPT entry(set the value to REMOVED_SPTE) +3. TLB shootdown via a hook + * TLB.MEM.RANGE.BLOCK() + * TLB.MEM.TRACK() + * send IPI to remote vcpus +4. set the EPT entry to the final value +5. read unlock + +TDX concurrent populating +------------------------- +TDX SEAMCALLs are required in addition to operating the mirrored EPT entry. The +frozen entry is utilized by following the zapping case to avoid the race +condition. A hook can be added. + +1. read lock +2. freeze the EPT entry +3. hook + * TDH_MEM_SEPT_ADD() for non-leaf or TDH_MEM_PAGE_AUG() for leaf. +4. set the EPT entry to the final value +5. read unlock + +Without freezing the entry, the following race can happen. Suppose two vcpus +are faulting on the same GPA and the 2M and 4K level entries aren't populated +yet. + +* vcpu 1: update 2M level EPT entry +* vcpu 2: update 4K level EPT entry +* vcpu 2: TDX SEAMCALL to update 4K secure EPT entry => error +* vcpu 1: TDX SEAMCALL to update 2M secure EPT entry + + +TDX non-concurrent batched zapping +---------------------------------- +For simplicity, the procedure of concurrent populating is utilized. The +procedure can be optimized later. + + +Co-existing with unmapping guest private memory +=============================================== +TODO. This needs to be addressed. + + +Restrictions or future work +=========================== +The following features aren't supported yet at the moment. + +* optimizing non-concurrent zap +* Large page +* Page migration -- 2.25.1