* Re: [PATCH v2 09/19] PCI/TSM: Support creating encrypted MMIO descriptors via TDISP Report
From: Jason Gunthorpe @ 2026-03-23 18:20 UTC (permalink / raw)
To: Alexey Kardashevskiy
Cc: Xu Yilun, Aneesh Kumar K.V, Dan Williams, linux-coco, linux-pci,
gregkh, bhelgaas, alistair23, lukas, Arnd Bergmann
In-Reply-To: <228298b7-76bc-4ffb-bec0-0c35fd487c4c@amd.com>
On Mon, Mar 16, 2026 at 04:19:30PM +1100, Alexey Kardashevskiy wrote:
> and btw this only works if the entity generating the MMIO reporting
> offset (==TSM) knows about BARs sizes, which is not the case for AMD
> - the FW has no access to the config space (so the HV needs to feed
> this to the FW? may be). Thanks,
Then your platform just shouldn't use the mmio offset feature. Set it
to 0 always.
Jason
^ permalink raw reply
* [PATCH v2 0/5] Fuller TDX kexec support
From: Vishal Verma @ 2026-03-23 20:59 UTC (permalink / raw)
To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Kiryl Shutsemau, Rick Edgecombe,
Sean Christopherson, Paolo Bonzini
Cc: linux-kernel, linux-coco, kvm, Vishal Verma, Kai Huang,
Kiryl Shutsemau
Hi,
This series adds a couple of cool things -
1. Allow kexec and kdump on systems with the partial write errata
2. Allow using TDX in the second (kexec'ed) kernel
It has been waiting for VMXON refactor to land because the implementation
is much cleaner on top of that.
KVM folks, just a few deletions on your side and the long discussed moving
of tdx_errno.h. Tip folks and reviewers, the changes here are pretty small.
Optimistically, I'm hoping we can iterate this quickly and see it off the
list in the next few weeks.
Background
==========
Some early TDX-capable platforms have an erratum where a partial write
to TDX private memory can cause a machine check on a subsequent read.
Currently, kexec and kdump are disabled on these platforms because the
new (or kdump) kernel may trip over these, causing a machine check.
Future TDX modules will support TDH.SYS.DISABLE SEAMCALL, which disables
the TDX module and reclaims all memory resources allocated to TDX, and
cleans up any poison. After this SEAMCALL succeeds, the new kernel
can also re-initialize the TDX module from scratch via the normal bring-up
sequence.
It is probably worth mentioning that this is a different kind of cleanup
than the WBINVD stuff that was the cause of all the fuss in the earlier
kexec enabling. The WBINVD is flushing private keyid cachelines so they
are not later written back over the new kernels memory. It needs to happen
after the last SEAMCALL that might have produced them. So this new
SEAMCALL is for something else, but also needs to be slotted with respect
to WBINVD.
Implementation
==============
The series adds:
1. A pre-requisite patch to move TDX error code definitions to a
shared location so that TDX_INTERRUPTED_RESUMABLE etc. are
accessible from arch/x86/virt/vmx/tdx/. This comes from the Dynamic
PAMT series [0], but is also needed by some other series, and can
benefit them all from an early merge.
2. A preparatory patch to move some straggling stuff into arch/x86 in the
wake of the VMXON series.
3. A tdx_sys_disable() helper that wraps calls TDH.SYS.DISABLE with a
retry loop to handle TDX_INTERRUPTED_RESUMABLE.
4. Integration into the kexec path: Remove the check for partial write
errata platforms as this is addressed by the SEAMCALL clearing any
poisoned memory locations. Call tdx_sys_disable() in tdx_shutdown
which is called via syscore ops in the kexec path. Call
tdx_sys_disable() in native_machine_crash_shutdown() to cover the
crash (kdump) path.
5. A patch to update (remove) the kexec section in TDX docs.
Testing
=======
The implementation is based on the draft TDX module spec available at
[1], and was smoke tested with an engineering build of the TDX module
that supports the new SEAMCALL. The new kernel was able to initialize
the TDX module successfully:
kvm: exiting hardware virtualization
kexec_core: Starting new kernel
Linux version 7.0.0-rc2-g0077f702b21c...
...
virt/tdx: 1034220 KB allocated for PAMT
virt/tdx: TDX-Module initialized
All the other TDX CI tests pass, and some other scenarios that were
manually tested and also pass or behave as expected:
- Running on a completely non-TDX system
- Running on a TDX capable system with an old module
- Running on a TDX capable system where the module hasn't been
initialized
During development further testing was done by mocking up the new
SEAMCALL to introduce delays and exercise the retry loops, combined with
kexec, kdump, reboot and shutdown flows.
Base
====
This series is based on the vmxon branch Sean pushed to kvm_x86,
kvm-x86-vmxon-2026.03.05.
[0]: https://lore.kernel.org/kvm/20260129011517.3545883-11-seanjc@google.com/
[1]: https://cdrdv2.intel.com/v1/dl/getContent/871617
---
Changes in v2:
- Use patch 1 from the DPAMT series with other feedback (Kai)
- Fix commit message typo (s/adjust_/adjust /)
- In patch 2, drop "too late to be helpful" in favor of something more
explicit (Kai)
- Fix commit message typo in patch 2 (s/both/bother/)
- In patch 2, add a bit about dropping the TDX specific WBINVD (Kai)
- Reword some commit logs to use the imperative mood (Chao)
- Kai raised offline that TDH.SYS.DISABLE can return TDX_SYS_BUSY too.
In theory this could happen if another SEAMCALL happens concurrently,
however that contention should be short lived. Update the loop to
continue on a TDX_SYS_BUSY error code too. (Kai)
- Patch 3: Add a print for SEAMCALL errors reported by the TDX module
(excluding SW errors like #UD and #GP) (Kiryl)
- Patch 3: Add a sentence to the log about skipping enumeration for the
new SEAMCALL (Kiryl)
- Adjust the patch 4 subject (Chao)
- Add a new patch to update the docs (Chao)
- Smoke test with TDX module engineering build with the new SEAMCALL.
Kiryl Shutsemau (1):
x86/tdx: Move all TDX error defines into <asm/shared/tdx_errno.h>
Rick Edgecombe (2):
x86/virt/tdx: Pull kexec cache flush logic into arch/x86
x86/virt/tdx: Remove kexec docs
Vishal Verma (2):
x86/virt/tdx: Add SEAMCALL wrapper for TDH.SYS.DISABLE
x86/tdx: Disable the TDX module during kexec and kdump
Documentation/arch/x86/tdx.rst | 7 -------
arch/x86/include/asm/shared/tdx.h | 1 +
arch/x86/{kvm/vmx => include/asm/shared}/tdx_errno.h | 29 +++++++++++++++++++++++------
arch/x86/include/asm/tdx.h | 30 +++---------------------------
arch/x86/kvm/vmx/tdx.h | 1 -
arch/x86/virt/vmx/tdx/tdx.h | 1 +
arch/x86/kernel/crash.c | 2 ++
arch/x86/kernel/machine_kexec_64.c | 16 ----------------
arch/x86/kvm/vmx/tdx.c | 10 ----------
arch/x86/virt/vmx/tdx/tdx.c | 54 ++++++++++++++++++++++++++++++++++++++++++------------
10 files changed, 72 insertions(+), 79 deletions(-)
--
2.53.0
---
Kiryl Shutsemau (1):
x86/tdx: Move all TDX error defines into <asm/shared/tdx_errno.h>
Rick Edgecombe (2):
x86/virt/tdx: Pull kexec cache flush logic into arch/x86
x86/virt/tdx: Remove kexec docs
Vishal Verma (2):
x86/virt/tdx: Add SEAMCALL wrapper for TDH.SYS.DISABLE
x86/tdx: Disable the TDX module during kexec and kdump
Documentation/arch/x86/tdx.rst | 7 ---
arch/x86/include/asm/shared/tdx.h | 1 +
.../{kvm/vmx => include/asm/shared}/tdx_errno.h | 29 +++++++++---
arch/x86/include/asm/tdx.h | 30 ++----------
arch/x86/kvm/vmx/tdx.h | 1 -
arch/x86/virt/vmx/tdx/tdx.h | 1 +
arch/x86/kernel/crash.c | 2 +
arch/x86/kernel/machine_kexec_64.c | 16 -------
arch/x86/kvm/vmx/tdx.c | 10 ----
arch/x86/virt/vmx/tdx/tdx.c | 54 +++++++++++++++++-----
10 files changed, 72 insertions(+), 79 deletions(-)
---
base-commit: f630de1f8d70d7e29e12bc25dc63f9c5f771dc59
change-id: 20260317-fuller_tdx_kexec_support-bc79694678be
Best regards,
--
Vishal Verma <vishal.l.verma@intel.com>
^ permalink raw reply
* [PATCH v2 1/5] x86/tdx: Move all TDX error defines into <asm/shared/tdx_errno.h>
From: Vishal Verma @ 2026-03-23 20:59 UTC (permalink / raw)
To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Kiryl Shutsemau, Rick Edgecombe,
Sean Christopherson, Paolo Bonzini
Cc: linux-kernel, linux-coco, kvm, Vishal Verma, Kiryl Shutsemau,
Kiryl Shutsemau
In-Reply-To: <20260323-fuller_tdx_kexec_support-v2-0-87a36409e051@intel.com>
From: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>
Today there are two separate locations where TDX error codes are defined:
arch/x86/include/asm/tdx.h
arch/x86/kvm/vmx/tdx_errno.h
They have some overlap that is already defined similarly. Reduce the
duplication and prepare to introduce some helpers for these error codes in
the central place by unifying them. Join them at:
asm/shared/tdx_errno.h
...and update the headers that contained the duplicated definitions to
include the new unified header.
"asm/shared" is used for sharing TDX code between the early compressed
code and the normal kernel code. While the compressed code for the guest
doesn't use these error code header definitions today, it does make the
types of calls that return the values they define. So place the defines in
"shared" location so that it can, but leave such cleanups for future
changes.
Also, adjust BITUL() -> _BITULL() to address 32 bit build errors after the
move.
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
[enhance log]
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
arch/x86/include/asm/shared/tdx.h | 1 +
.../{kvm/vmx => include/asm/shared}/tdx_errno.h | 28 +++++++++++++++++-----
arch/x86/include/asm/tdx.h | 21 ----------------
arch/x86/kvm/vmx/tdx.h | 1 -
4 files changed, 23 insertions(+), 28 deletions(-)
diff --git a/arch/x86/include/asm/shared/tdx.h b/arch/x86/include/asm/shared/tdx.h
index 8bc074c8d7c6..6a1646fc2b2f 100644
--- a/arch/x86/include/asm/shared/tdx.h
+++ b/arch/x86/include/asm/shared/tdx.h
@@ -4,6 +4,7 @@
#include <linux/bits.h>
#include <linux/types.h>
+#include <asm/shared/tdx_errno.h>
#define TDX_HYPERCALL_STANDARD 0
diff --git a/arch/x86/kvm/vmx/tdx_errno.h b/arch/x86/include/asm/shared/tdx_errno.h
similarity index 64%
rename from arch/x86/kvm/vmx/tdx_errno.h
rename to arch/x86/include/asm/shared/tdx_errno.h
index 6ff4672c4181..8bf6765cf082 100644
--- a/arch/x86/kvm/vmx/tdx_errno.h
+++ b/arch/x86/include/asm/shared/tdx_errno.h
@@ -1,14 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0 */
-/* architectural status code for SEAMCALL */
-
-#ifndef __KVM_X86_TDX_ERRNO_H
-#define __KVM_X86_TDX_ERRNO_H
+#ifndef _ASM_X86_SHARED_TDX_ERRNO_H
+#define _ASM_X86_SHARED_TDX_ERRNO_H
+#include <asm/trapnr.h>
+/* Upper 32 bit of the TDX error code encodes the status */
#define TDX_SEAMCALL_STATUS_MASK 0xFFFFFFFF00000000ULL
/*
- * TDX SEAMCALL Status Codes (returned in RAX)
+ * TDX Status Codes (returned in RAX)
*/
+#define TDX_SUCCESS 0ULL
#define TDX_NON_RECOVERABLE_VCPU 0x4000000100000000ULL
#define TDX_NON_RECOVERABLE_TD 0x4000000200000000ULL
#define TDX_NON_RECOVERABLE_TD_NON_ACCESSIBLE 0x6000000500000000ULL
@@ -17,6 +18,7 @@
#define TDX_OPERAND_INVALID 0xC000010000000000ULL
#define TDX_OPERAND_BUSY 0x8000020000000000ULL
#define TDX_PREVIOUS_TLB_EPOCH_BUSY 0x8000020100000000ULL
+#define TDX_RND_NO_ENTROPY 0x8000020300000000ULL
#define TDX_PAGE_METADATA_INCORRECT 0xC000030000000000ULL
#define TDX_VCPU_NOT_ASSOCIATED 0x8000070200000000ULL
#define TDX_KEY_GENERATION_FAILED 0x8000080000000000ULL
@@ -28,6 +30,20 @@
#define TDX_EPT_ENTRY_STATE_INCORRECT 0xC0000B0D00000000ULL
#define TDX_METADATA_FIELD_NOT_READABLE 0xC0000C0200000000ULL
+/*
+ * SW-defined error codes.
+ *
+ * Bits 47:40 == 0xFF indicate Reserved status code class that never used by
+ * TDX module.
+ */
+#define TDX_ERROR _BITULL(63)
+#define TDX_NON_RECOVERABLE _BITULL(62)
+#define TDX_SW_ERROR (TDX_ERROR | GENMASK_ULL(47, 40))
+#define TDX_SEAMCALL_VMFAILINVALID (TDX_SW_ERROR | _ULL(0xFFFF0000))
+
+#define TDX_SEAMCALL_GP (TDX_SW_ERROR | X86_TRAP_GP)
+#define TDX_SEAMCALL_UD (TDX_SW_ERROR | X86_TRAP_UD)
+
/*
* TDX module operand ID, appears in 31:0 part of error code as
* detail information
@@ -37,4 +53,4 @@
#define TDX_OPERAND_ID_SEPT 0x92
#define TDX_OPERAND_ID_TD_EPOCH 0xa9
-#endif /* __KVM_X86_TDX_ERRNO_H */
+#endif /* _ASM_X86_SHARED_TDX_ERRNO_H */
diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
index a149740b24e8..2917b3451491 100644
--- a/arch/x86/include/asm/tdx.h
+++ b/arch/x86/include/asm/tdx.h
@@ -9,29 +9,8 @@
#include <asm/errno.h>
#include <asm/ptrace.h>
-#include <asm/trapnr.h>
#include <asm/shared/tdx.h>
-/*
- * SW-defined error codes.
- *
- * Bits 47:40 == 0xFF indicate Reserved status code class that never used by
- * TDX module.
- */
-#define TDX_ERROR _BITUL(63)
-#define TDX_NON_RECOVERABLE _BITUL(62)
-#define TDX_SW_ERROR (TDX_ERROR | GENMASK_ULL(47, 40))
-#define TDX_SEAMCALL_VMFAILINVALID (TDX_SW_ERROR | _UL(0xFFFF0000))
-
-#define TDX_SEAMCALL_GP (TDX_SW_ERROR | X86_TRAP_GP)
-#define TDX_SEAMCALL_UD (TDX_SW_ERROR | X86_TRAP_UD)
-
-/*
- * TDX module SEAMCALL leaf function error codes
- */
-#define TDX_SUCCESS 0ULL
-#define TDX_RND_NO_ENTROPY 0x8000020300000000ULL
-
#ifndef __ASSEMBLER__
#include <uapi/asm/mce.h>
diff --git a/arch/x86/kvm/vmx/tdx.h b/arch/x86/kvm/vmx/tdx.h
index b5cd2ffb303e..ac8323a68b16 100644
--- a/arch/x86/kvm/vmx/tdx.h
+++ b/arch/x86/kvm/vmx/tdx.h
@@ -3,7 +3,6 @@
#define __KVM_X86_VMX_TDX_H
#include "tdx_arch.h"
-#include "tdx_errno.h"
#ifdef CONFIG_KVM_INTEL_TDX
#include "common.h"
--
2.53.0
^ permalink raw reply related
* [PATCH v2 2/5] x86/virt/tdx: Pull kexec cache flush logic into arch/x86
From: Vishal Verma @ 2026-03-23 20:59 UTC (permalink / raw)
To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Kiryl Shutsemau, Rick Edgecombe,
Sean Christopherson, Paolo Bonzini
Cc: linux-kernel, linux-coco, kvm, Kai Huang, Vishal Verma
In-Reply-To: <20260323-fuller_tdx_kexec_support-v2-0-87a36409e051@intel.com>
From: Rick Edgecombe <rick.p.edgecombe@intel.com>
KVM tries to take care of some required cache flushing earlier in the
kexec path in order to be kind to some long standing races that can occur
later in the operation. Until recently, VMXOFF was handled within KVM.
Since VMX being enabled is required to make a SEAMCALL, it had the best
per-cpu scoped operation to plug the flushing into. So it is kicked off
from there.
This early kexec cache flushing in KVM happens via a syscore shutdown
callback. Now that VMX enablement control has moved to arch/x86, which has
grown its own syscore shutdown callback, it no longer make sense for it to
live in KVM. It fits better with the TDX enablement managing code.
In addition, future changes will add a SEAMCALL that happens immediately
before VMXOFF, which means the cache flush in KVM will be too late to
flush the cache before the last SEAMCALL. So move it to the newly added TDX
arch/x86 syscore shutdown handler.
Since tdx_cpu_flush_cache_for_kexec() is no longer needed by KVM, make it
static and remove the export. Since it is also not part of an operation
spread across disparate components, remove the redundant comments and
verbose naming.
In the existing KVM based code, CPU offline also funnels through
tdx_cpu_flush_cache_for_kexec(). So the centralization to the arch/x86
syscore shutdown callback elides this CPU offline time behavior. However,
WBINVD is already generally done at CPU offline as matter of course. So
don't bother adding TDX specific logic for this, and rely on the normal
WBINVD to handle it.
Acked-by: Kai Huang <kai.huang@intel.com>
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
arch/x86/include/asm/tdx.h | 6 ------
arch/x86/kvm/vmx/tdx.c | 10 ----------
arch/x86/virt/vmx/tdx/tdx.c | 39 ++++++++++++++++++++-------------------
3 files changed, 20 insertions(+), 35 deletions(-)
diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
index 2917b3451491..7674fc530090 100644
--- a/arch/x86/include/asm/tdx.h
+++ b/arch/x86/include/asm/tdx.h
@@ -205,11 +205,5 @@ static inline const char *tdx_dump_mce_info(struct mce *m) { return NULL; }
static inline const struct tdx_sys_info *tdx_get_sysinfo(void) { return NULL; }
#endif /* CONFIG_INTEL_TDX_HOST */
-#ifdef CONFIG_KEXEC_CORE
-void tdx_cpu_flush_cache_for_kexec(void);
-#else
-static inline void tdx_cpu_flush_cache_for_kexec(void) { }
-#endif
-
#endif /* !__ASSEMBLER__ */
#endif /* _ASM_X86_TDX_H */
diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index b7264b533feb..50a5cfdbd33e 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -440,16 +440,6 @@ void tdx_disable_virtualization_cpu(void)
tdx_flush_vp(&arg);
}
local_irq_restore(flags);
-
- /*
- * Flush cache now if kexec is possible: this is necessary to avoid
- * having dirty private memory cachelines when the new kernel boots,
- * but WBINVD is a relatively expensive operation and doing it during
- * kexec can exacerbate races in native_stop_other_cpus(). Do it
- * now, since this is a safe moment and there is going to be no more
- * TDX activity on this CPU from this point on.
- */
- tdx_cpu_flush_cache_for_kexec();
}
#define TDX_SEAMCALL_RETRIES 10000
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index cb9b3210ab71..0802d0fd18a4 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -224,8 +224,28 @@ static int tdx_offline_cpu(unsigned int cpu)
return 0;
}
+static void tdx_cpu_flush_cache(void)
+{
+ lockdep_assert_preemption_disabled();
+
+ if (!this_cpu_read(cache_state_incoherent))
+ return;
+
+ wbinvd();
+ this_cpu_write(cache_state_incoherent, false);
+}
+
static void tdx_shutdown_cpu(void *ign)
{
+ /*
+ * Flush cache now if kexec is possible: this is necessary to avoid
+ * having dirty private memory cachelines when the new kernel boots,
+ * but WBINVD is a relatively expensive operation and doing it during
+ * kexec can exacerbate races in native_stop_other_cpus(). Do it
+ * now, since this is a safe moment and there is going to be no more
+ * TDX activity on this CPU from this point on.
+ */
+ tdx_cpu_flush_cache();
x86_virt_put_ref(X86_FEATURE_VMX);
}
@@ -1920,22 +1940,3 @@ u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, struct page *page)
return seamcall(TDH_PHYMEM_PAGE_WBINVD, &args);
}
EXPORT_SYMBOL_FOR_KVM(tdh_phymem_page_wbinvd_hkid);
-
-#ifdef CONFIG_KEXEC_CORE
-void tdx_cpu_flush_cache_for_kexec(void)
-{
- lockdep_assert_preemption_disabled();
-
- if (!this_cpu_read(cache_state_incoherent))
- return;
-
- /*
- * Private memory cachelines need to be clean at the time of
- * kexec. Write them back now, as the caller promises that
- * there should be no more SEAMCALLs on this CPU.
- */
- wbinvd();
- this_cpu_write(cache_state_incoherent, false);
-}
-EXPORT_SYMBOL_FOR_KVM(tdx_cpu_flush_cache_for_kexec);
-#endif
--
2.53.0
^ permalink raw reply related
* [PATCH v2 3/5] x86/virt/tdx: Add SEAMCALL wrapper for TDH.SYS.DISABLE
From: Vishal Verma @ 2026-03-23 20:59 UTC (permalink / raw)
To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Kiryl Shutsemau, Rick Edgecombe,
Sean Christopherson, Paolo Bonzini
Cc: linux-kernel, linux-coco, kvm, Vishal Verma
In-Reply-To: <20260323-fuller_tdx_kexec_support-v2-0-87a36409e051@intel.com>
Some early TDX-capable platforms have an erratum where a partial write
to TDX private memory can cause a machine check on a subsequent read.
On these platforms, kexec and kdump have been disabled in these cases,
because the old kernel cannot safely hand off TDX state to the new
kernel. Later TDX modules support the TDH.SYS.DISABLE SEAMCALL, which
provides a way to cleanly disable TDX and allow kexec to proceed.
The new SEAMCALL has an enumeration bit, but that is ignored. It is
expected that users will be using the latest TDX module, and the failure
mode for running the missing SEAMCALL on an older module is not fatal.
This can be a long running operation, and the time needed largely
depends on the amount of memory that has been allocated to TDs. If all
TDs have been destroyed prior to the sys_disable call, then it is fast,
with only needing to override the TDX module memory.
After the SEAMCALL completes, the TDX module is disabled and all memory
resources allocated to TDX are freed and reset. The next kernel can then
re-initialize the TDX module from scratch via the normal TDX bring-up
sequence.
The SEAMCALL can return two different error codes that expect a retry.
- TDX_INTERRUPTED_RESUMABLE can be returned in the case of a host
interrupt. However, it will not return until it makes some forward
progress, so we can expect to complete even in the case of interrupt
storms.
- TDX_SYS_BUSY will be returned on contention with other TDH.SYS.*
SEAMCALLs, however a side effect of TDH.SYS.DISABLE is that it will
block other SEAMCALLs once it gets going. So this contention will be
short lived.
So loop infinitely on either of these error codes, until success or other
error.
Co-developed-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
arch/x86/include/asm/shared/tdx_errno.h | 1 +
arch/x86/include/asm/tdx.h | 3 +++
arch/x86/virt/vmx/tdx/tdx.h | 1 +
arch/x86/virt/vmx/tdx/tdx.c | 28 ++++++++++++++++++++++++++++
4 files changed, 33 insertions(+)
diff --git a/arch/x86/include/asm/shared/tdx_errno.h b/arch/x86/include/asm/shared/tdx_errno.h
index 8bf6765cf082..246b4fd54a48 100644
--- a/arch/x86/include/asm/shared/tdx_errno.h
+++ b/arch/x86/include/asm/shared/tdx_errno.h
@@ -15,6 +15,7 @@
#define TDX_NON_RECOVERABLE_TD_NON_ACCESSIBLE 0x6000000500000000ULL
#define TDX_NON_RECOVERABLE_TD_WRONG_APIC_MODE 0x6000000700000000ULL
#define TDX_INTERRUPTED_RESUMABLE 0x8000000300000000ULL
+#define TDX_SYS_BUSY 0x8000020200000000ULL
#define TDX_OPERAND_INVALID 0xC000010000000000ULL
#define TDX_OPERAND_BUSY 0x8000020000000000ULL
#define TDX_PREVIOUS_TLB_EPOCH_BUSY 0x8000020100000000ULL
diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
index 7674fc530090..a0a4a15142fc 100644
--- a/arch/x86/include/asm/tdx.h
+++ b/arch/x86/include/asm/tdx.h
@@ -172,6 +172,8 @@ static inline int pg_level_to_tdx_sept_level(enum pg_level level)
return level - 1;
}
+void tdx_sys_disable(void);
+
u64 tdh_vp_enter(struct tdx_vp *vp, struct tdx_module_args *args);
u64 tdh_mng_addcx(struct tdx_td *td, struct page *tdcs_page);
u64 tdh_mem_page_add(struct tdx_td *td, u64 gpa, struct page *page, struct page *source, u64 *ext_err1, u64 *ext_err2);
@@ -203,6 +205,7 @@ static inline void tdx_init(void) { }
static inline u32 tdx_get_nr_guest_keyids(void) { return 0; }
static inline const char *tdx_dump_mce_info(struct mce *m) { return NULL; }
static inline const struct tdx_sys_info *tdx_get_sysinfo(void) { return NULL; }
+static inline void tdx_sys_disable(void) { }
#endif /* CONFIG_INTEL_TDX_HOST */
#endif /* !__ASSEMBLER__ */
diff --git a/arch/x86/virt/vmx/tdx/tdx.h b/arch/x86/virt/vmx/tdx/tdx.h
index dde219c823b4..e2cf2dd48755 100644
--- a/arch/x86/virt/vmx/tdx/tdx.h
+++ b/arch/x86/virt/vmx/tdx/tdx.h
@@ -46,6 +46,7 @@
#define TDH_PHYMEM_PAGE_WBINVD 41
#define TDH_VP_WR 43
#define TDH_SYS_CONFIG 45
+#define TDH_SYS_DISABLE 69
/*
* SEAMCALL leaf:
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index 0802d0fd18a4..3a76000dec7a 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -37,6 +37,7 @@
#include <asm/msr.h>
#include <asm/cpufeature.h>
#include <asm/tdx.h>
+#include <asm/shared/tdx_errno.h>
#include <asm/cpu_device_id.h>
#include <asm/processor.h>
#include <asm/mce.h>
@@ -1940,3 +1941,30 @@ u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, struct page *page)
return seamcall(TDH_PHYMEM_PAGE_WBINVD, &args);
}
EXPORT_SYMBOL_FOR_KVM(tdh_phymem_page_wbinvd_hkid);
+
+void tdx_sys_disable(void)
+{
+ struct tdx_module_args args = {};
+ u64 ret;
+
+ /*
+ * Don't loop forever.
+ * - TDX_INTERRUPTED_RESUMABLE guarantees forward progress between
+ * calls.
+ * - TDX_SYS_BUSY could transiently contend with TDH.SYS.* SEAMCALLs,
+ * but will lock out future ones.
+ *
+ * This is a 'destructive' SEAMCALL, in that no other SEAMCALL can be
+ * run after this until a full reinitialization is done.
+ */
+ do {
+ ret = seamcall(TDH_SYS_DISABLE, &args);
+ } while (ret == TDX_INTERRUPTED_RESUMABLE || ret == TDX_SYS_BUSY);
+
+ /*
+ * Print SEAMCALL failures, but not SW-defined error codes
+ * (SEAMCALL faulted with #GP/#UD, TDX not supported).
+ */
+ if (ret && (ret & TDX_SW_ERROR) != TDX_SW_ERROR)
+ pr_err("TDH.SYS.DISABLE failed: 0x%016llx\n", ret);
+}
--
2.53.0
^ permalink raw reply related
* [PATCH v2 4/5] x86/tdx: Disable the TDX module during kexec and kdump
From: Vishal Verma @ 2026-03-23 20:59 UTC (permalink / raw)
To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Kiryl Shutsemau, Rick Edgecombe,
Sean Christopherson, Paolo Bonzini
Cc: linux-kernel, linux-coco, kvm, Vishal Verma
In-Reply-To: <20260323-fuller_tdx_kexec_support-v2-0-87a36409e051@intel.com>
Use the TDH.SYS.DISABLE SEAMCALL, which disables the TDX module,
reclaims all memory resources assigned to TDX, and clears any
partial-write induced poison, to allow kexec and kdump on platforms with
the partial write errata.
On TDX-capable platforms with the partial write erratum, kexec has been
disabled because the new kernel could hit a machine check reading a
previously poisoned memory location.
Later TDX modules support TDH.SYS.DISABLE, which disables the module and
reclaims all TDX memory resources, allowing the new kernel to re-initialize
TDX from scratch. This operation also clears the old memory, cleaning up
any poison.
Add tdx_sys_disable() to tdx_shutdown(), which is called in the
syscore_shutdown path for kexec. This is done just before tdx_shutdown()
disables VMX on all CPUs.
For kdump, call tdx_sys_disable() in the crash path before
x86_virt_emergency_disable_virtualization_cpu() does VMXOFF.
Since this clears any poison on TDX-managed memory, remove the
X86_BUG_TDX_PW_MCE check in machine_kexec() that blocked kexec on
partial write errata platforms.
Co-developed-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
arch/x86/kernel/crash.c | 2 ++
arch/x86/kernel/machine_kexec_64.c | 16 ----------------
arch/x86/virt/vmx/tdx/tdx.c | 1 +
3 files changed, 3 insertions(+), 16 deletions(-)
diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c
index cd796818d94d..623d4474631a 100644
--- a/arch/x86/kernel/crash.c
+++ b/arch/x86/kernel/crash.c
@@ -38,6 +38,7 @@
#include <linux/kdebug.h>
#include <asm/cpu.h>
#include <asm/reboot.h>
+#include <asm/tdx.h>
#include <asm/intel_pt.h>
#include <asm/crash.h>
#include <asm/cmdline.h>
@@ -112,6 +113,7 @@ void native_machine_crash_shutdown(struct pt_regs *regs)
crash_smp_send_stop();
+ tdx_sys_disable();
x86_virt_emergency_disable_virtualization_cpu();
/*
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c
index 0590d399d4f1..c3f4a389992d 100644
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -347,22 +347,6 @@ int machine_kexec_prepare(struct kimage *image)
unsigned long reloc_end = (unsigned long)__relocate_kernel_end;
int result;
- /*
- * Some early TDX-capable platforms have an erratum. A kernel
- * partial write (a write transaction of less than cacheline
- * lands at memory controller) to TDX private memory poisons that
- * memory, and a subsequent read triggers a machine check.
- *
- * On those platforms the old kernel must reset TDX private
- * memory before jumping to the new kernel otherwise the new
- * kernel may see unexpected machine check. For simplicity
- * just fail kexec/kdump on those platforms.
- */
- if (boot_cpu_has_bug(X86_BUG_TDX_PW_MCE)) {
- pr_info_once("Not allowed on platform with tdx_pw_mce bug\n");
- return -EOPNOTSUPP;
- }
-
/* Setup the identity mapped 64bit page table */
result = init_pgtable(image, __pa(control_page));
if (result)
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index 3a76000dec7a..aaf22a87717a 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -252,6 +252,7 @@ static void tdx_shutdown_cpu(void *ign)
static void tdx_shutdown(void *ign)
{
+ tdx_sys_disable();
on_each_cpu(tdx_shutdown_cpu, NULL, 1);
}
--
2.53.0
^ permalink raw reply related
* [PATCH v2 5/5] x86/virt/tdx: Remove kexec docs
From: Vishal Verma @ 2026-03-23 20:59 UTC (permalink / raw)
To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Kiryl Shutsemau, Rick Edgecombe,
Sean Christopherson, Paolo Bonzini
Cc: linux-kernel, linux-coco, kvm, Vishal Verma
In-Reply-To: <20260323-fuller_tdx_kexec_support-v2-0-87a36409e051@intel.com>
From: Rick Edgecombe <rick.p.edgecombe@intel.com>
Recent changes have removed the hard limitations for using kexec and
TDX together. So remove the section in the TDX docs.
Users on partial write erratums will need an updated TDX module to
handle the rare edge cases. The docs do not currently provide any
guidance on recommended TDX module versions, so don't keep a whole
section around to document this interaction.
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
Documentation/arch/x86/tdx.rst | 7 -------
1 file changed, 7 deletions(-)
diff --git a/Documentation/arch/x86/tdx.rst b/Documentation/arch/x86/tdx.rst
index ff6b110291bc..1a3b5bac1021 100644
--- a/Documentation/arch/x86/tdx.rst
+++ b/Documentation/arch/x86/tdx.rst
@@ -138,13 +138,6 @@ If the platform has such erratum, the kernel prints additional message in
machine check handler to tell user the machine check may be caused by
kernel bug on TDX private memory.
-Kexec
-~~~~~~~
-
-Currently kexec doesn't work on the TDX platforms with the aforementioned
-erratum. It fails when loading the kexec kernel image. Otherwise it
-works normally.
-
Interaction vs S3 and deeper states
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v2 3/5] x86/virt/tdx: Add SEAMCALL wrapper for TDH.SYS.DISABLE
From: Verma, Vishal L @ 2026-03-23 21:54 UTC (permalink / raw)
To: Edgecombe, Rick P, seanjc@google.com, bp@alien8.de,
x86@kernel.org, kas@kernel.org, hpa@zytor.com, mingo@redhat.com,
dave.hansen@linux.intel.com, tglx@kernel.org, pbonzini@redhat.com
Cc: kvm@vger.kernel.org, linux-coco@lists.linux.dev,
linux-kernel@vger.kernel.org
In-Reply-To: <20260323-fuller_tdx_kexec_support-v2-3-87a36409e051@intel.com>
On Mon, 2026-03-23 at 14:59 -0600, Vishal Verma wrote:
>
[..]
> +void tdx_sys_disable(void)
> +{
> + struct tdx_module_args args = {};
> + u64 ret;
> +
> + /*
> + * Don't loop forever.
> + * - TDX_INTERRUPTED_RESUMABLE guarantees forward progress between
> + * calls.
> + * - TDX_SYS_BUSY could transiently contend with TDH.SYS.* SEAMCALLs,
> + * but will lock out future ones.
> + *
> + * This is a 'destructive' SEAMCALL, in that no other SEAMCALL can be
> + * run after this until a full reinitialization is done.
> + */
> + do {
> + ret = seamcall(TDH_SYS_DISABLE, &args);
> + } while (ret == TDX_INTERRUPTED_RESUMABLE || ret == TDX_SYS_BUSY);
> +
> + /*
> + * Print SEAMCALL failures, but not SW-defined error codes
> + * (SEAMCALL faulted with #GP/#UD, TDX not supported).
> + */
> + if (ret && (ret & TDX_SW_ERROR) != TDX_SW_ERROR)
> + pr_err("TDH.SYS.DISABLE failed: 0x%016llx\n", ret);
> +}
Note - old TDX modules that don't implement this SEAMCALL produce a
message like:
virt/tdx: TDH.SYS.DISABLE failed: 0xc000010000000000
Where that code translates to TDX_OPERAND_INVALID.
This also serves as a nudge that the module should be updated.
It might be worth including a blurb about this in the commit message -
something like below. This could be included when applying, or I can
send an updated version with this if it is acceptable.
---
An error is printed if the SEAMCALL fails with anything other than the
error codes that cause retries, or 'synthesized' error codes produced
for #GP or #UD. e.g., an old module that has been properly initialized,
that doesn't implement SYS_DISABLE, returns TDX_OPERAND_INVALID. This
prints:
virt/tdx: TDH.SYS.DISABLE failed: 0xc000010000000000
But a system that doesn't have any TDX support at all doesn't print
anything.
^ permalink raw reply
* Re: [PATCH v2 3/5] x86/virt/tdx: Add SEAMCALL wrapper for TDH.SYS.DISABLE
From: Huang, Kai @ 2026-03-23 22:40 UTC (permalink / raw)
To: Edgecombe, Rick P, seanjc@google.com, bp@alien8.de,
x86@kernel.org, kas@kernel.org, hpa@zytor.com, mingo@redhat.com,
Verma, Vishal L, dave.hansen@linux.intel.com, tglx@kernel.org,
pbonzini@redhat.com
Cc: kvm@vger.kernel.org, linux-coco@lists.linux.dev,
linux-kernel@vger.kernel.org
In-Reply-To: <20260323-fuller_tdx_kexec_support-v2-3-87a36409e051@intel.com>
On Mon, 2026-03-23 at 14:59 -0600, Vishal Verma wrote:
> Some early TDX-capable platforms have an erratum where a partial write
> to TDX private memory can cause a machine check on a subsequent read.
> On these platforms, kexec and kdump have been disabled in these cases,
> because the old kernel cannot safely hand off TDX state to the new
> kernel. Later TDX modules support the TDH.SYS.DISABLE SEAMCALL, which
> provides a way to cleanly disable TDX and allow kexec to proceed.
>
> The new SEAMCALL has an enumeration bit, but that is ignored. It is
> expected that users will be using the latest TDX module, and the failure
> mode for running the missing SEAMCALL on an older module is not fatal.
>
> This can be a long running operation, and the time needed largely
> depends on the amount of memory that has been allocated to TDs. If all
> TDs have been destroyed prior to the sys_disable call, then it is fast,
> with only needing to override the TDX module memory.
>
> After the SEAMCALL completes, the TDX module is disabled and all memory
> resources allocated to TDX are freed and reset. The next kernel can then
> re-initialize the TDX module from scratch via the normal TDX bring-up
> sequence.
>
> The SEAMCALL can return two different error codes that expect a retry.
> - TDX_INTERRUPTED_RESUMABLE can be returned in the case of a host
> interrupt. However, it will not return until it makes some forward
> progress, so we can expect to complete even in the case of interrupt
> storms.
> - TDX_SYS_BUSY will be returned on contention with other TDH.SYS.*
> SEAMCALLs, however a side effect of TDH.SYS.DISABLE is that it will
> block other SEAMCALLs once it gets going. So this contention will be
> short lived.
>
> So loop infinitely on either of these error codes, until success or other
> error.
>
> Co-developed-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
> Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
> Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
>
Acked-by: Kai Huang <kai.huang@intel.com>
^ permalink raw reply
* Re: [PATCH v2 4/5] x86/tdx: Disable the TDX module during kexec and kdump
From: Huang, Kai @ 2026-03-23 22:41 UTC (permalink / raw)
To: Edgecombe, Rick P, seanjc@google.com, bp@alien8.de,
x86@kernel.org, kas@kernel.org, hpa@zytor.com, mingo@redhat.com,
Verma, Vishal L, dave.hansen@linux.intel.com, tglx@kernel.org,
pbonzini@redhat.com
Cc: kvm@vger.kernel.org, linux-coco@lists.linux.dev,
linux-kernel@vger.kernel.org
In-Reply-To: <20260323-fuller_tdx_kexec_support-v2-4-87a36409e051@intel.com>
On Mon, 2026-03-23 at 14:59 -0600, Vishal Verma wrote:
> Use the TDH.SYS.DISABLE SEAMCALL, which disables the TDX module,
> reclaims all memory resources assigned to TDX, and clears any
> partial-write induced poison, to allow kexec and kdump on platforms with
> the partial write errata.
>
> On TDX-capable platforms with the partial write erratum, kexec has been
> disabled because the new kernel could hit a machine check reading a
> previously poisoned memory location.
>
> Later TDX modules support TDH.SYS.DISABLE, which disables the module and
> reclaims all TDX memory resources, allowing the new kernel to re-initialize
> TDX from scratch. This operation also clears the old memory, cleaning up
> any poison.
>
> Add tdx_sys_disable() to tdx_shutdown(), which is called in the
> syscore_shutdown path for kexec. This is done just before tdx_shutdown()
> disables VMX on all CPUs.
>
> For kdump, call tdx_sys_disable() in the crash path before
> x86_virt_emergency_disable_virtualization_cpu() does VMXOFF.
>
> Since this clears any poison on TDX-managed memory, remove the
> X86_BUG_TDX_PW_MCE check in machine_kexec() that blocked kexec on
> partial write errata platforms.
>
> Co-developed-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
> Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
> Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
>
Acked-by: Kai Huang <kai.huang@intel.com>
^ permalink raw reply
* Re: [PATCH v2 5/5] x86/virt/tdx: Remove kexec docs
From: Huang, Kai @ 2026-03-23 22:41 UTC (permalink / raw)
To: Edgecombe, Rick P, seanjc@google.com, bp@alien8.de,
x86@kernel.org, kas@kernel.org, hpa@zytor.com, mingo@redhat.com,
Verma, Vishal L, dave.hansen@linux.intel.com, tglx@kernel.org,
pbonzini@redhat.com
Cc: kvm@vger.kernel.org, linux-coco@lists.linux.dev,
linux-kernel@vger.kernel.org
In-Reply-To: <20260323-fuller_tdx_kexec_support-v2-5-87a36409e051@intel.com>
On Mon, 2026-03-23 at 14:59 -0600, Vishal Verma wrote:
> From: Rick Edgecombe <rick.p.edgecombe@intel.com>
>
> Recent changes have removed the hard limitations for using kexec and
> TDX together. So remove the section in the TDX docs.
>
> Users on partial write erratums will need an updated TDX module to
> handle the rare edge cases. The docs do not currently provide any
> guidance on recommended TDX module versions, so don't keep a whole
> section around to document this interaction.
>
> Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
> Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
>
Acked-by: Kai Huang <kai.huang@intel.com>
^ permalink raw reply
* Re: [PATCH v4 2/2] dma-buf: heaps: system: add system_cc_decrypted heap for explicitly decrypted memory
From: T.J. Mercier @ 2026-03-23 23:25 UTC (permalink / raw)
To: Jiri Pirko
Cc: dri-devel, linaro-mm-sig, iommu, linux-media, sumit.semwal,
benjamin.gaignard, Brian.Starkey, jstultz, christian.koenig,
m.szyprowski, robin.murphy, jgg, leon, sean.anderson, ptesarik,
catalin.marinas, aneesh.kumar, suzuki.poulose, steven.price,
thomas.lendacky, john.allen, ashish.kalra, suravee.suthikulpanit,
linux-coco
In-Reply-To: <20260316125857.617836-3-jiri@resnulli.us>
On Mon, Mar 16, 2026 at 5:59 AM Jiri Pirko <jiri@resnulli.us> wrote:
>
> From: Jiri Pirko <jiri@nvidia.com>
>
> Add a new "system_cc_decrypted" dma-buf heap to allow userspace to
> allocate decrypted (shared) memory for confidential computing (CoCo)
> VMs.
>
> On CoCo VMs, guest memory is encrypted by default. The hardware uses an
> encryption bit in page table entries (C-bit on AMD SEV, "shared" bit on
> Intel TDX) to control whether a given memory access is encrypted or
> decrypted. The kernel's direct map is set up with encryption enabled,
> so pages returned by alloc_pages() are encrypted in the direct map
> by default. To make this memory usable for devices that do not support
> DMA to encrypted memory (no TDISP support), it has to be explicitly
> decrypted. A couple of things are needed to properly handle
> decrypted memory for the dma-buf use case:
>
> - set_memory_decrypted() on the direct map after allocation:
> Besides clearing the encryption bit in the direct map PTEs, this
> also notifies the hypervisor about the page state change. On free,
> the inverse set_memory_encrypted() must be called before returning
> pages to the allocator. If re-encryption fails, pages
> are intentionally leaked to prevent decrypted memory from being
> reused as private.
>
> - pgprot_decrypted() for userspace and kernel virtual mappings:
> Any new mapping of the decrypted pages, be it to userspace via
> mmap or to kernel vmalloc space via vmap, creates PTEs independent
> of the direct map. These must also have the encryption bit cleared,
> otherwise accesses through them would see encrypted (garbage) data.
>
> - DMA_ATTR_CC_DECRYPTED for DMA mapping:
> Since the pages are already decrypted, the DMA API needs to be
> informed via DMA_ATTR_CC_DECRYPTED so it can map them correctly
> as unencrypted for device access.
>
> On non-CoCo VMs, the system_cc_decrypted heap is not registered
> to prevent misuse by userspace that does not understand
> the security implications of explicitly decrypted memory.
>
> Signed-off-by: Jiri Pirko <jiri@nvidia.com>
> ---
> v2->v3:
> - removed couple of leftovers from headers
> v1->v2:
> - fixed build errors on s390 by including mem_encrypt.h
> - converted system heap flag implementation to a separate heap
> ---
> drivers/dma-buf/heaps/system_heap.c | 103 ++++++++++++++++++++++++++--
> 1 file changed, 98 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/dma-buf/heaps/system_heap.c b/drivers/dma-buf/heaps/system_heap.c
> index b3650d8fd651..a525e9aaaffa 100644
> --- a/drivers/dma-buf/heaps/system_heap.c
> +++ b/drivers/dma-buf/heaps/system_heap.c
> @@ -10,17 +10,25 @@
> * Andrew F. Davis <afd@ti.com>
> */
>
> +#include <linux/cc_platform.h>
> #include <linux/dma-buf.h>
> #include <linux/dma-mapping.h>
> #include <linux/dma-heap.h>
> #include <linux/err.h>
> #include <linux/highmem.h>
> +#include <linux/mem_encrypt.h>
> #include <linux/mm.h>
> +#include <linux/set_memory.h>
> #include <linux/module.h>
> +#include <linux/pgtable.h>
> #include <linux/scatterlist.h>
> #include <linux/slab.h>
> #include <linux/vmalloc.h>
>
> +struct system_heap_priv {
> + bool decrypted;
> +};
Hi Jiri,
I wonder if it'd better to call this cc_decrypted (or I guess
cc_shared based on Robin's comment in the previous patch) like the DMA
attr? There's a separate effort for "restricted" heaps with TEE for
(encrypted) video playback, which doesn't involve VMs or RDMA. I think
the cc_ prefix might help avoid any confusion between the usecase here
and restricted heaps.
> +
> struct system_heap_buffer {
> struct dma_heap *heap;
> struct list_head attachments;
> @@ -29,6 +37,7 @@ struct system_heap_buffer {
> struct sg_table sg_table;
> int vmap_cnt;
> void *vaddr;
> + bool decrypted;
> };
>
> struct dma_heap_attachment {
> @@ -36,6 +45,7 @@ struct dma_heap_attachment {
> struct sg_table table;
> struct list_head list;
> bool mapped;
> + bool decrypted;
> };
>
> #define LOW_ORDER_GFP (GFP_HIGHUSER | __GFP_ZERO)
> @@ -52,6 +62,34 @@ static gfp_t order_flags[] = {HIGH_ORDER_GFP, HIGH_ORDER_GFP, LOW_ORDER_GFP};
> static const unsigned int orders[] = {8, 4, 0};
> #define NUM_ORDERS ARRAY_SIZE(orders)
>
> +static int system_heap_set_page_decrypted(struct page *page)
> +{
> + unsigned long addr = (unsigned long)page_address(page);
> + unsigned int nr_pages = 1 << compound_order(page);
> + int ret;
> +
> + ret = set_memory_decrypted(addr, nr_pages);
> + if (ret)
> + pr_warn_ratelimited("dma-buf system heap: failed to decrypt page at %p\n",
> + page_address(page));
> +
> + return ret;
> +}
> +
> +static int system_heap_set_page_encrypted(struct page *page)
> +{
> + unsigned long addr = (unsigned long)page_address(page);
> + unsigned int nr_pages = 1 << compound_order(page);
> + int ret;
> +
> + ret = set_memory_encrypted(addr, nr_pages);
> + if (ret)
> + pr_warn_ratelimited("dma-buf system heap: failed to re-encrypt page at %p, leaking memory\n",
> + page_address(page));
> +
> + return ret;
> +}
> +
> static int dup_sg_table(struct sg_table *from, struct sg_table *to)
> {
> struct scatterlist *sg, *new_sg;
> @@ -90,6 +128,7 @@ static int system_heap_attach(struct dma_buf *dmabuf,
> a->dev = attachment->dev;
> INIT_LIST_HEAD(&a->list);
> a->mapped = false;
> + a->decrypted = buffer->decrypted;
>
> attachment->priv = a;
>
> @@ -119,9 +158,11 @@ static struct sg_table *system_heap_map_dma_buf(struct dma_buf_attachment *attac
> {
> struct dma_heap_attachment *a = attachment->priv;
> struct sg_table *table = &a->table;
> + unsigned long attrs;
> int ret;
>
> - ret = dma_map_sgtable(attachment->dev, table, direction, 0);
> + attrs = a->decrypted ? DMA_ATTR_CC_DECRYPTED : 0;
> + ret = dma_map_sgtable(attachment->dev, table, direction, attrs);
> if (ret)
> return ERR_PTR(ret);
>
> @@ -188,8 +229,13 @@ static int system_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
> unsigned long addr = vma->vm_start;
> unsigned long pgoff = vma->vm_pgoff;
> struct scatterlist *sg;
> + pgprot_t prot;
> int i, ret;
>
> + prot = vma->vm_page_prot;
> + if (buffer->decrypted)
> + prot = pgprot_decrypted(prot);
> +
> for_each_sgtable_sg(table, sg, i) {
> unsigned long n = sg->length >> PAGE_SHIFT;
>
> @@ -206,8 +252,7 @@ static int system_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
> if (addr + size > vma->vm_end)
> size = vma->vm_end - addr;
>
> - ret = remap_pfn_range(vma, addr, page_to_pfn(page),
> - size, vma->vm_page_prot);
> + ret = remap_pfn_range(vma, addr, page_to_pfn(page), size, prot);
> if (ret)
> return ret;
>
> @@ -225,6 +270,7 @@ static void *system_heap_do_vmap(struct system_heap_buffer *buffer)
> struct page **pages = vmalloc(sizeof(struct page *) * npages);
> struct page **tmp = pages;
> struct sg_page_iter piter;
> + pgprot_t prot;
> void *vaddr;
>
> if (!pages)
> @@ -235,7 +281,10 @@ static void *system_heap_do_vmap(struct system_heap_buffer *buffer)
> *tmp++ = sg_page_iter_page(&piter);
> }
>
> - vaddr = vmap(pages, npages, VM_MAP, PAGE_KERNEL);
> + prot = PAGE_KERNEL;
> + if (buffer->decrypted)
> + prot = pgprot_decrypted(prot);
> + vaddr = vmap(pages, npages, VM_MAP, prot);
> vfree(pages);
>
> if (!vaddr)
> @@ -296,6 +345,14 @@ static void system_heap_dma_buf_release(struct dma_buf *dmabuf)
> for_each_sgtable_sg(table, sg, i) {
> struct page *page = sg_page(sg);
>
> + /*
> + * Intentionally leak pages that cannot be re-encrypted
> + * to prevent decrypted memory from being reused.
> + */
> + if (buffer->decrypted &&
> + system_heap_set_page_encrypted(page))
> + continue;
> +
> __free_pages(page, compound_order(page));
> }
> sg_free_table(table);
> @@ -347,6 +404,8 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
> DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
> unsigned long size_remaining = len;
> unsigned int max_order = orders[0];
> + struct system_heap_priv *priv = dma_heap_get_drvdata(heap);
> + bool decrypted = priv->decrypted;
> struct dma_buf *dmabuf;
> struct sg_table *table;
> struct scatterlist *sg;
> @@ -362,6 +421,7 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
> mutex_init(&buffer->lock);
> buffer->heap = heap;
> buffer->len = len;
> + buffer->decrypted = decrypted;
>
> INIT_LIST_HEAD(&pages);
> i = 0;
> @@ -396,6 +456,14 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
> list_del(&page->lru);
> }
>
> + if (decrypted) {
> + for_each_sgtable_sg(table, sg, i) {
> + ret = system_heap_set_page_decrypted(sg_page(sg));
> + if (ret)
> + goto free_pages;
> + }
> + }
> +
> /* create the dmabuf */
> exp_info.exp_name = dma_heap_get_name(heap);
> exp_info.ops = &system_heap_buf_ops;
> @@ -413,6 +481,13 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
> for_each_sgtable_sg(table, sg, i) {
> struct page *p = sg_page(sg);
>
> + /*
> + * Intentionally leak pages that cannot be re-encrypted
> + * to prevent decrypted memory from being reused.
> + */
> + if (buffer->decrypted &&
> + system_heap_set_page_encrypted(p))
> + continue;
> __free_pages(p, compound_order(p));
> }
> sg_free_table(table);
> @@ -428,6 +503,14 @@ static const struct dma_heap_ops system_heap_ops = {
> .allocate = system_heap_allocate,
> };
>
> +static struct system_heap_priv system_heap_priv = {
> + .decrypted = false,
> +};
> +
> +static struct system_heap_priv system_heap_cc_decrypted_priv = {
> + .decrypted = true,
> +};
> +
> static int __init system_heap_create(void)
> {
> struct dma_heap_export_info exp_info;
> @@ -435,8 +518,18 @@ static int __init system_heap_create(void)
>
> exp_info.name = "system";
> exp_info.ops = &system_heap_ops;
> - exp_info.priv = NULL;
> + exp_info.priv = &system_heap_priv;
> +
> + sys_heap = dma_heap_add(&exp_info);
> + if (IS_ERR(sys_heap))
> + return PTR_ERR(sys_heap);
> +
> + if (IS_ENABLED(CONFIG_HIGHMEM) ||
> + !cc_platform_has(CC_ATTR_MEM_ENCRYPT))
> + return 0;
>
> + exp_info.name = "system_cc_decrypted";
> + exp_info.priv = &system_heap_cc_decrypted_priv;
> sys_heap = dma_heap_add(&exp_info);
> if (IS_ERR(sys_heap))
> return PTR_ERR(sys_heap);
> --
> 2.51.1
>
^ permalink raw reply
* Re: [PATCH v2 03/19] device core: Introduce confidential device acceptance
From: Dan Williams @ 2026-03-24 2:18 UTC (permalink / raw)
To: Jason Gunthorpe, Dan Williams
Cc: Greg KH, linux-coco, linux-pci, aik, aneesh.kumar, yilun.xu,
bhelgaas, alistair23, lukas, Christoph Hellwig, Marek Szyprowski,
Robin Murphy, Roman Kisel, Samuel Ortiz, Rafael J. Wysocki,
Danilo Krummrich
In-Reply-To: <20260323181413.GP7340@nvidia.com>
Jason Gunthorpe wrote:
> On Fri, Mar 13, 2026 at 06:32:27PM -0700, Dan Williams wrote:
>
> > The problem is that for all the buses that do not currently have a
> > "device authorization" concept only userspace can decide that a device
> > should skip bind by default. For that, I propose module autoprobe policy
> > [1]. Not yet convinced the kernel needs its own per-device "no bind"
> > policy.
>
> I think it is just part of the broader definition of the level that
> extends into the iommu and so on. It makes sense to have this kind of
> no-binding security level, IMHO.
Easy to include for completeness.
In a CC VM userspace can set "$module autoprobe=0" to get a control
point to set @trust to zero, but @trust otherwise defaults to 1. This
allows for userspace policy to generically distrust devices, but without
needing to build a new mechanism to specify which devices start life at
trust == 0 (i.e. "device filter" proposal previously NAK'd).
> > > The DMA API just wants a flag in the struct device that says if the
> > > device can access encrypted memory or only decrypted.
> >
> > You mean separate "trusted to access private" and "currently enabled to
> > access private" properties? I am trying to think of a situation where
> > "dev->trust >= 3" and a flag saying "disable bouncing for encrypted
> > memory" would ever disagree.
>
> I'm steering the trust level toward more of an acceptance criteria.
> If the trust level is you have access to private memory but the device
> can't actually do that then fail the trust level change.
>
> Same for the reverse, if the trust level says no private memory and the
> device is T=1 then fail the trust level change.
Ok, so the uapi for PCI/TDISP would be:
echo $tsm > $pdev/tsm/lock
<gather evidence, validate with relying party>
echo 3 > $pdev/trust
...where that @trust attribute is a generic device semantic, but in the
case of PCI device connected to a given TSM it invokes the TSM hypercall
to transition the device to the RUN state and the TSM local call to
unblock DMA to private memory.
So, userspace can generically understand what privileges come with which
trust levels, but the mechanism to get those privileges remains bus
specific.
> > That bit though has lock-to-run consistency expectations. So if the
> > kernel does not yet fully trust the device by time the relying party is
> > satisfied, and the uAPI to transition the device into the TCB (level 3)
> > is driver-core generic it raises TOCTOU issues in my mind. The
> > driver-core would need to ask the bus "user now trusts this device, do
> > you?".
>
> Huh? No, there is no concept of trust in the kernel. The userspace
> setting level 3 is "I now ack that this device is trusted", there is
> no further trust cross check. If TSM side says it is in RUN/T=1 then
> we are done.
Ok, so maybe I misunderstood your point. Per the above, if the trust
setting is what kicks the bus to finalize T=1 then it makes sense. If
that kick fails, the user's trust setting request fails.
What I expect is unwanted / surprising is device has already been
transitioned to T=1 state ahead of the trust setting, it is a
synchronous mechanism.
> If we fall out of RUN then the level auto-resets back to 0 and
> userspace has to go around and fix it again. (ignoring driver RAS)
Yes, at the moment that the bus detects that an event like SPDM session
loss, IDE link loss, TDISP ERROR state entry has occurred it can
downgrade trust and notify. That notification fits well with netlink
because all of those events are downstream of evidence validation.
> > Aneesh and I are currently debating on Discord whether the kernel needs
> > to protect against guest userspace confusing itself.
>
> Userspace that controls acceptance must be part of the TCB or the
> whole model is fully broken. If your guest userspace is so security
> broken it can accept devices it doesn't mean to then just forget it.
Agree, it is a non-problem. If guest userspace confuses itself by racing
sysfs operations then the relying party should not trust that userspace.
> > However, to Aneesh's point we could protect against that with a
> > transactional uAPI like netlink that can express "trust if and only if
> > the device has not been relocked before final accept" by passing a
> > cookie obtained at lock to accept. That would be awkward to coordinate
> > with driver-core generic uAPI for trust.
>
> You could, but why make it so complicated? The whole LOCKED/RUN thing
> is already supposed to deal with TOCTOU, doesn't it?
Right, the threat vector is the guest accepting something it has had no
chance to validate. Guest userspace confusion is not that. Guest
userspace asking the device to be re-locked in a way that confuses an
ongoing evidence validation sequence in another thread is a "you get to
keep the pieces" event.
> The CSP cannot trick a device to fall out of LOCKED an the re-enter
> LOCKED without the VM knowing.
>
> The VM attacking itself on something as security critical as device
> accepance can't be in scope :\
The complication vs benefit tradeoff is indeed not mathing, but wanted
to do justice to Aneesh's proposal and the suitability of the sysfs
uapi.
> > > This way nothing is coupled and the kernel can offer all kinds of
> > > different uAPI for device verification. Userspaces picks the
> > > appropriate one and acks it with the level change.
> >
> > Thunderbolt already has authorized uAPI. I expect adding dev->trust
> > support to thunderbolt is more related to ATS privilege and private
> > memory privilege.
>
> It brings it into the whole 'measure the device and then decide what
> to do with it' framework. The trust level is still the generic ack
> that the device is allowed the participate in the system with whatever
> level of security.
Some thought experiments to confirm alignment...
'Generic ack' is a synchronous mechanism for the bus to evaluate. So if
@trust appears for any device, and by consequence alongside @authorized
for a thunderbolt device, it should be the case that these operations
are equivalent:
# echo 1 > $dev/trust
# echo 1 > $dev/authorized
...and the result is cross-reflected for comptability:
# echo 1 > $dev/trust
# cat $dev/authorized
1
Consequently this:
# echo 2 > $dev/trust
...would be equivalent to authorizing the device and unblocking ATS (if
such a thing existed).
For bare metal PCI device security the TSM 'connection' needs to be
established in order to enable device evidence collection.
echo $tsm > $pdev/tsm/connect
<validate device evidence>
echo 2 > $pdev/trust
Now, I question whether 5 trust levels instead of 4. This would be to
explicitly only trust devices where the TSM has established physical
link encryption, or the TSM has asserted that the link is protected by
other means. So the trust levels are:
0 disconnected: bus does not attach drivers
1 limited: core code deploys hostile device mitigations like disable
ATS, CC force shared memory bouncing.
2 DMA: full DMA access, driver responsible for protecting against
adverarial devices.
3 Link: mitigations against physical integrity and snooping attacks
deployed
4 TCB: full device interface validation via a protocol like TDISP,
CC private memory access granted.
Where the native Rust library based SPDM driver only offers trust level
2, bare metal TSMs can support trust level 3, and the TSM interfaces in
CC VMs can support trust level 4. I could see squashing 2 and 3 and
making it a documentation problem to understand the capabilities of the
various TSM drivers.
^ permalink raw reply
* Re: [PATCH v2 09/19] PCI/TSM: Support creating encrypted MMIO descriptors via TDISP Report
From: Dan Williams @ 2026-03-24 3:26 UTC (permalink / raw)
To: Xu Yilun, Jason Gunthorpe
Cc: Aneesh Kumar K.V, Dan Williams, linux-coco, linux-pci, gregkh,
aik, bhelgaas, alistair23, lukas, Arnd Bergmann
In-Reply-To: <abji3A72ogqdRiGc@yilunxu-OptiPlex-7050>
Xu Yilun wrote:
[..]
> And I've no idea why unlocked MSIX/PBA must be hidden? How about other
> non-TEE ranges, must be hidden or mustn't? Is there a possibility we
> enforce DSM to present all ranges, then the layout is clear to OS?
Just to close this question, this was discussed at the last device
security call. Indeed the expectation is that Linux will assume that all
ranges besides MSIX/PBA must be present in the report, and that offset
is always aligned. If / when an implementation violates that expectation
they can help write the Linux quirk for that case or otherwise fix their
implementation.
^ permalink raw reply
* Re: [PATCH v2 00/16] fs,x86/resctrl: Add kernel-mode (e.g., PLZA) support to the resctrl subsystem
From: Askar Safin @ 2026-03-24 6:15 UTC (permalink / raw)
To: babu.moger; +Cc: kvm, linux-coco, linux-doc, linux-kernel, x86
In-Reply-To: <cover.1773347820.git.babu.moger@amd.com>
Please, remove me from CC list in future versions of this patchset
--
Askar Safin
^ permalink raw reply
* Re: [PATCH v2 1/5] x86/tdx: Move all TDX error defines into <asm/shared/tdx_errno.h>
From: Chao Gao @ 2026-03-24 9:49 UTC (permalink / raw)
To: Vishal Verma
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Kiryl Shutsemau, Rick Edgecombe,
Sean Christopherson, Paolo Bonzini, linux-kernel, linux-coco, kvm
In-Reply-To: <20260323-fuller_tdx_kexec_support-v2-1-87a36409e051@intel.com>
On Mon, Mar 23, 2026 at 02:59:04PM -0600, Vishal Verma wrote:
>From: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>
>
>Today there are two separate locations where TDX error codes are defined:
> arch/x86/include/asm/tdx.h
> arch/x86/kvm/vmx/tdx_errno.h
>
>They have some overlap that is already defined similarly. Reduce the
>duplication and prepare to introduce some helpers for these error codes in
>the central place by unifying them. Join them at:
> asm/shared/tdx_errno.h
>...and update the headers that contained the duplicated definitions to
>include the new unified header.
>
>"asm/shared" is used for sharing TDX code between the early compressed
>code and the normal kernel code. While the compressed code for the guest
>doesn't use these error code header definitions today, it does make the
>types of calls that return the values they define. So place the defines in
>"shared" location so that it can, but leave such cleanups for future
>changes.
>
>Also, adjust BITUL() -> _BITULL() to address 32 bit build errors after the
>move.
>
>Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
>[enhance log]
>Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
>Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
Reviewed-by: Chao Gao <chao.gao@intel.com>
^ permalink raw reply
* Re: [PATCH v2 2/5] x86/virt/tdx: Pull kexec cache flush logic into arch/x86
From: Chao Gao @ 2026-03-24 10:03 UTC (permalink / raw)
To: Vishal Verma
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Kiryl Shutsemau, Rick Edgecombe,
Sean Christopherson, Paolo Bonzini, linux-kernel, linux-coco, kvm,
Kai Huang
In-Reply-To: <20260323-fuller_tdx_kexec_support-v2-2-87a36409e051@intel.com>
On Mon, Mar 23, 2026 at 02:59:05PM -0600, Vishal Verma wrote:
>From: Rick Edgecombe <rick.p.edgecombe@intel.com>
>
>KVM tries to take care of some required cache flushing earlier in the
>kexec path in order to be kind to some long standing races that can occur
>later in the operation. Until recently, VMXOFF was handled within KVM.
>Since VMX being enabled is required to make a SEAMCALL, it had the best
>per-cpu scoped operation to plug the flushing into. So it is kicked off
>from there.
>
>This early kexec cache flushing in KVM happens via a syscore shutdown
>callback. Now that VMX enablement control has moved to arch/x86, which has
>grown its own syscore shutdown callback, it no longer make sense for it to
>live in KVM. It fits better with the TDX enablement managing code.
>
>In addition, future changes will add a SEAMCALL that happens immediately
>before VMXOFF, which means the cache flush in KVM will be too late to
>flush the cache before the last SEAMCALL. So move it to the newly added TDX
>arch/x86 syscore shutdown handler.
>
>Since tdx_cpu_flush_cache_for_kexec() is no longer needed by KVM, make it
>static and remove the export. Since it is also not part of an operation
>spread across disparate components, remove the redundant comments and
>verbose naming.
>
>In the existing KVM based code, CPU offline also funnels through
>tdx_cpu_flush_cache_for_kexec(). So the centralization to the arch/x86
>syscore shutdown callback elides this CPU offline time behavior. However,
>WBINVD is already generally done at CPU offline as matter of course. So
>don't bother adding TDX specific logic for this, and rely on the normal
>WBINVD to handle it.
>
>Acked-by: Kai Huang <kai.huang@intel.com>
>Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
>Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
Reviewed-by: Chao Gao <chao.gao@intel.com>
^ permalink raw reply
* Re: [PATCH v2 3/5] x86/virt/tdx: Add SEAMCALL wrapper for TDH.SYS.DISABLE
From: Chao Gao @ 2026-03-24 10:18 UTC (permalink / raw)
To: Vishal Verma
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Kiryl Shutsemau, Rick Edgecombe,
Sean Christopherson, Paolo Bonzini, linux-kernel, linux-coco, kvm
In-Reply-To: <20260323-fuller_tdx_kexec_support-v2-3-87a36409e051@intel.com>
On Mon, Mar 23, 2026 at 02:59:06PM -0600, Vishal Verma wrote:
>Some early TDX-capable platforms have an erratum where a partial write
>to TDX private memory can cause a machine check on a subsequent read.
>On these platforms, kexec and kdump have been disabled in these cases,
>because the old kernel cannot safely hand off TDX state to the new
>kernel. Later TDX modules support the TDH.SYS.DISABLE SEAMCALL, which
>provides a way to cleanly disable TDX and allow kexec to proceed.
>
>The new SEAMCALL has an enumeration bit, but that is ignored. It is
>expected that users will be using the latest TDX module, and the failure
>mode for running the missing SEAMCALL on an older module is not fatal.
>
>This can be a long running operation, and the time needed largely
>depends on the amount of memory that has been allocated to TDs. If all
>TDs have been destroyed prior to the sys_disable call, then it is fast,
>with only needing to override the TDX module memory.
>
>After the SEAMCALL completes, the TDX module is disabled and all memory
>resources allocated to TDX are freed and reset. The next kernel can then
>re-initialize the TDX module from scratch via the normal TDX bring-up
>sequence.
>
>The SEAMCALL can return two different error codes that expect a retry.
> - TDX_INTERRUPTED_RESUMABLE can be returned in the case of a host
> interrupt. However, it will not return until it makes some forward
> progress, so we can expect to complete even in the case of interrupt
> storms.
> - TDX_SYS_BUSY will be returned on contention with other TDH.SYS.*
> SEAMCALLs, however a side effect of TDH.SYS.DISABLE is that it will
> block other SEAMCALLs once it gets going. So this contention will be
> short lived.
>
>So loop infinitely on either of these error codes, until success or other
>error.
>
>Co-developed-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
>Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
>Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
Reviewed-by: Chao Gao <chao.gao@intel.com>
^ permalink raw reply
* Re: [PATCH net-next v3 0/2] dma-buf: heaps: system: add an option to allocate explicitly decrypted memory
From: Jason Gunthorpe @ 2026-03-24 12:00 UTC (permalink / raw)
To: Mostafa Saleh
Cc: Jiri Pirko, dri-devel, linaro-mm-sig, iommu, linux-media,
sumit.semwal, benjamin.gaignard, Brian.Starkey, jstultz,
tjmercier, christian.koenig, m.szyprowski, robin.murphy, leon,
sean.anderson, ptesarik, catalin.marinas, aneesh.kumar,
suzuki.poulose, steven.price, thomas.lendacky, john.allen,
ashish.kalra, suravee.suthikulpanit, linux-coco
In-Reply-To: <ablV_f_l7wD2m63E@google.com>
On Tue, Mar 17, 2026 at 01:24:13PM +0000, Mostafa Saleh wrote:
> On the other hand, for restricted-dma, the memory decryption is deep
> in the DMA direct memory allocation and the DMA API callers (for ex
> virtio drivers) are clueless about it and can’t pass any attrs.
> My proposal was specific to restricted-dma and won’t work for your case.
How is this any different from CC?
If the device cannot dma to "encrypted" memory, whatever that means
for you, then the DMA API:
- Makes dma alloc coherent return "decrypted" memory, and the built
in mapping of coherent memory knows about this
- Makes dma_map_xxx use SWIOTLB to bounce to decrypted memory
There is no need for something like virtio drivers to be aware of
any of this.
On the other hand if the driver deliberately allocates decrypted
memory without using DMA API alloc coherent then it knows it did it
and can pass the flag to map it.
> I am wondering if the kernel should have a more solid, unified method
> for identifying already-decrypted memory instead. Perhaps we need a
> way for the DMA API to natively recognize the encryption state of a
> physical page (working alongside force_dma_unencrypted(dev)), rather
> than relying on caller-provided attributes?
Definately not, we do not want the DMA API inspecting things like
this.
Jason
^ permalink raw reply
* Re: [PATCH net-next v3 0/2] dma-buf: heaps: system: add an option to allocate explicitly decrypted memory
From: Mostafa Saleh @ 2026-03-24 12:14 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: Jiri Pirko, dri-devel, linaro-mm-sig, iommu, linux-media,
sumit.semwal, benjamin.gaignard, Brian.Starkey, jstultz,
tjmercier, christian.koenig, m.szyprowski, robin.murphy, leon,
sean.anderson, ptesarik, catalin.marinas, aneesh.kumar,
suzuki.poulose, steven.price, thomas.lendacky, john.allen,
ashish.kalra, suravee.suthikulpanit, linux-coco
In-Reply-To: <20260324120057.GC8437@ziepe.ca>
On Tue, Mar 24, 2026 at 12:01 PM Jason Gunthorpe <jgg@ziepe.ca> wrote:
>
> On Tue, Mar 17, 2026 at 01:24:13PM +0000, Mostafa Saleh wrote:
>
> > On the other hand, for restricted-dma, the memory decryption is deep
> > in the DMA direct memory allocation and the DMA API callers (for ex
> > virtio drivers) are clueless about it and can’t pass any attrs.
> > My proposal was specific to restricted-dma and won’t work for your case.
>
> How is this any different from CC?
>
> If the device cannot dma to "encrypted" memory, whatever that means
> for you, then the DMA API:
> - Makes dma alloc coherent return "decrypted" memory, and the built
> in mapping of coherent memory knows about this
> - Makes dma_map_xxx use SWIOTLB to bounce to decrypted memory
>
> There is no need for something like virtio drivers to be aware of
> any of this.
>
> On the other hand if the driver deliberately allocates decrypted
> memory without using DMA API alloc coherent then it knows it did it
> and can pass the flag to map it.
>
The problem is that the DMA API currently gets confused by this; it
can end up double decrypting the memory or using the wrong functions
as mentioned in [1]
In addition to the complexity it adds to the already complicated DMA
code. I don't have a strong opinion on how to solve this, but I
believe we need clear boundaries (and wrappers) for cases where memory
encryption is expected as it is starting to spill into the kernel.
[1] https://lore.kernel.org/all/20260305170335.963568-1-smostafa@google.com/
Thanks,
Mostafa
> > I am wondering if the kernel should have a more solid, unified method
> > for identifying already-decrypted memory instead. Perhaps we need a
> > way for the DMA API to natively recognize the encryption state of a
> > physical page (working alongside force_dma_unencrypted(dev)), rather
> > than relying on caller-provided attributes?
>
> Definately not, we do not want the DMA API inspecting things like
> this.
>
> Jason
^ permalink raw reply
* Re: [PATCH net-next v3 0/2] dma-buf: heaps: system: add an option to allocate explicitly decrypted memory
From: Jason Gunthorpe @ 2026-03-24 12:24 UTC (permalink / raw)
To: Mostafa Saleh
Cc: Jiri Pirko, dri-devel, linaro-mm-sig, iommu, linux-media,
sumit.semwal, benjamin.gaignard, Brian.Starkey, jstultz,
tjmercier, christian.koenig, m.szyprowski, robin.murphy, leon,
sean.anderson, ptesarik, catalin.marinas, aneesh.kumar,
suzuki.poulose, steven.price, thomas.lendacky, john.allen,
ashish.kalra, suravee.suthikulpanit, linux-coco
In-Reply-To: <CAFgf54qoFhvVR2yfROGaTJmLcjuLACswVFv=trqgJwGb_DB3zA@mail.gmail.com>
On Tue, Mar 24, 2026 at 12:14:36PM +0000, Mostafa Saleh wrote:
> On Tue, Mar 24, 2026 at 12:01 PM Jason Gunthorpe <jgg@ziepe.ca> wrote:
> >
> > On Tue, Mar 17, 2026 at 01:24:13PM +0000, Mostafa Saleh wrote:
> >
> > > On the other hand, for restricted-dma, the memory decryption is deep
> > > in the DMA direct memory allocation and the DMA API callers (for ex
> > > virtio drivers) are clueless about it and can’t pass any attrs.
> > > My proposal was specific to restricted-dma and won’t work for your case.
> >
> > How is this any different from CC?
> >
> > If the device cannot dma to "encrypted" memory, whatever that means
> > for you, then the DMA API:
> > - Makes dma alloc coherent return "decrypted" memory, and the built
> > in mapping of coherent memory knows about this
> > - Makes dma_map_xxx use SWIOTLB to bounce to decrypted memory
> >
> > There is no need for something like virtio drivers to be aware of
> > any of this.
> >
> > On the other hand if the driver deliberately allocates decrypted
> > memory without using DMA API alloc coherent then it knows it did it
> > and can pass the flag to map it.
> >
>
> The problem is that the DMA API currently gets confused by this; it
> can end up double decrypting the memory or using the wrong functions
> as mentioned in [1]
I fully belive there are bugs, but the API design is sound. If you use
the coherent allocations from the DMA API then it knows decryption has
happened when it generates a dma_addr_t and there should be no issue.
Now, if drivers are using the DMA API wrong, like trying to double map
coherent allocations then they are broken. I also would not be
surprised to find cases like this.
Jason
^ permalink raw reply
* Re: [PATCH v2 03/19] device core: Introduce confidential device acceptance
From: Jason Gunthorpe @ 2026-03-24 12:36 UTC (permalink / raw)
To: Dan Williams
Cc: Greg KH, linux-coco, linux-pci, aik, aneesh.kumar, yilun.xu,
bhelgaas, alistair23, lukas, Christoph Hellwig, Marek Szyprowski,
Robin Murphy, Roman Kisel, Samuel Ortiz, Rafael J. Wysocki,
Danilo Krummrich
In-Reply-To: <69c1f469f2814_51621100bc@dwillia2-mobl4.notmuch>
On Mon, Mar 23, 2026 at 07:18:17PM -0700, Dan Williams wrote:
> Jason Gunthorpe wrote:
> > On Fri, Mar 13, 2026 at 06:32:27PM -0700, Dan Williams wrote:
> >
> > > The problem is that for all the buses that do not currently have a
> > > "device authorization" concept only userspace can decide that a device
> > > should skip bind by default. For that, I propose module autoprobe policy
> > > [1]. Not yet convinced the kernel needs its own per-device "no bind"
> > > policy.
> >
> > I think it is just part of the broader definition of the level that
> > extends into the iommu and so on. It makes sense to have this kind of
> > no-binding security level, IMHO.
>
> Easy to include for completeness.
>
> In a CC VM userspace can set "$module autoprobe=0" to get a control
> point to set @trust to zero, but @trust otherwise defaults to 1. This
> allows for userspace policy to generically distrust devices, but without
> needing to build a new mechanism to specify which devices start life at
> trust == 0 (i.e. "device filter" proposal previously NAK'd).
I feel like starting with trust=0 is much cleaner than using
autoprobe. Especially since it would be nice that when you do
ultimately set trust!=0 then you do want the kernel to do the normal
autoprobe flow.
Double so because I would like the iommu drivers to respond to trust 0
by fully blocking the device 100% of the time without holes, so to
make that work I would like to see the struct device report trust 0
the moment the iommu framework attaches the iommu.
How you decide the starting trust value for device during system boot
is definately something we need to discuss properly..
I liked your idea of using built in driver match, so if there is a
simple command line paramater that says 'only built in is trusted'
then we'd default all devices to untrusted and during device probe
check if any built in driver is matching and auto-set trust to X based
on the commandline parameter.
With the idea that only devices required to get to the initrd are
built in. Then the initrd userspace has the policy to bring more
devices into trusted!=0 to get to the root file system, then the
rootfs has more policy for further devices, and so on.
Probably this would ultimately escalate into annotations in the
modinfo about default policies for various drivers.
A kernel default policy of trusting everything without a "trust ops"
(see below) may also be quite reasonable, however boot ordering the
trust ops might be really tricky...
> > > > The DMA API just wants a flag in the struct device that says if the
> > > > device can access encrypted memory or only decrypted.
> > >
> > > You mean separate "trusted to access private" and "currently enabled to
> > > access private" properties? I am trying to think of a situation where
> > > "dev->trust >= 3" and a flag saying "disable bouncing for encrypted
> > > memory" would ever disagree.
> >
> > I'm steering the trust level toward more of an acceptance criteria.
> > If the trust level is you have access to private memory but the device
> > can't actually do that then fail the trust level change.
> >
> > Same for the reverse, if the trust level says no private memory and the
> > device is T=1 then fail the trust level change.
>
> Ok, so the uapi for PCI/TDISP would be:
>
> echo $tsm > $pdev/tsm/lock
> <gather evidence, validate with relying party>
> echo 3 > $pdev/trust
>
> ...where that @trust attribute is a generic device semantic, but in the
> case of PCI device connected to a given TSM it invokes the TSM hypercall
> to transition the device to the RUN state and the TSM local call to
> unblock DMA to private memory.
Maybe, but I was thinking the transition through run/locked would be
done through TSM uAPIs too. trust setting in the kernel just confirms
the device is in the right state.
But I haven't thought of a reason why the final switch to RUN couldn't
happen like this either.
> So, userspace can generically understand what privileges come with which
> trust levels, but the mechanism to get those privileges remains bus
> specific.
Yes
> > If we fall out of RUN then the level auto-resets back to 0 and
> > userspace has to go around and fix it again. (ignoring driver RAS)
>
> Yes, at the moment that the bus detects that an event like SPDM session
> loss, IDE link loss, TDISP ERROR state entry has occurred it can
> downgrade trust and notify. That notification fits well with netlink
> because all of those events are downstream of evidence validation.
Which is also why it would be nice to be consistent and rely on
trust=0 to isolate the device in all cases, not a mixture of
autoprobe.
> The complication vs benefit tradeoff is indeed not mathing, but wanted
> to do justice to Aneesh's proposal and the suitability of the sysfs
> uapi.
I think if you want something like this then it is better to target
the root - remove the ability for concurrent userspace to wrongly
operate the TSM entirely. Ie use a cdev, make it so going to LOCKED
isolates access to only this cdev fd and require only this cdev fd to
go to RUN. Then these kinds of bugs don't exist.
> > It brings it into the whole 'measure the device and then decide what
> > to do with it' framework. The trust level is still the generic ack
> > that the device is allowed the participate in the system with whatever
> > level of security.
>
> Some thought experiments to confirm alignment...
>
> 'Generic ack' is a synchronous mechanism for the bus to evaluate. So if
> @trust appears for any device, and by consequence alongside @authorized
> for a thunderbolt device, it should be the case that these operations
> are equivalent:
>
> # echo 1 > $dev/trust
> # echo 1 > $dev/authorized
>
> ...and the result is cross-reflected for comptability:
>
> # echo 1 > $dev/trust
> # cat $dev/authorized
> 1
>
> Consequently this:
>
> # echo 2 > $dev/trust
>
> ...would be equivalent to authorizing the device and unblocking ATS (if
> such a thing existed).
Yes, I don't know anything about thunderbolt, but this seems
reasonable. You could also do as you suggested for TDISP that trust!=0
auto-authorizes.
Basically the 'trust' generic framework sits on top of some "trust
ops" that will be provided by the security module that is affiliated
with the struct device (ie thunderbolt, TSM TDISP, TSM Link IDE, etc,
etc)
Then it becomes a general synchronization point where on one side the
"tust ops" can ack that the level is acceptable and consistent with
the system when on the other side generic compoments like IOMMU,
driver binding, etc can respond to it and change their behavior.
> For bare metal PCI device security the TSM 'connection' needs to be
> established in order to enable device evidence collection.
>
> echo $tsm > $pdev/tsm/connect
> <validate device evidence>
> echo 2 > $pdev/trust
>
> Now, I question whether 5 trust levels instead of 4. This would be to
> explicitly only trust devices where the TSM has established physical
> link encryption, or the TSM has asserted that the link is protected by
> other means. So the trust levels are:
I probably wouldn't use an int for the uAPI, but yes picking the
initial levels is important. As above since this is a clearing point
between two different worlds it needs to be defined in some way both
sides can understand what it means for them.
> 0 disconnected: bus does not attach drivers
> 1 limited: core code deploys hostile device mitigations like disable
> ATS, CC force shared memory bouncing.
> 2 DMA: full DMA access, driver responsible for protecting against
> adverarial devices.
> 3 Link: mitigations against physical integrity and snooping attacks
> deployed
> 4 TCB: full device interface validation via a protocol like TDISP,
> CC private memory access granted.
This seems reasonable to me, the 3/4 distinction is not meaningful for
the iommu&dev side, but it does provide a good check point for the
"trust ops". If userspace ack's that it expects physical security and
the kernel says it isn't physically secure (or becomes insecure later)
then it should fail.
> Where the native Rust library based SPDM driver only offers trust level
> 2, bare metal TSMs can support trust level 3, and the TSM interfaces in
> CC VMs can support trust level 4.
I'm not sure that the SPDM driver even provides a "trust ops" right? I
would guess that 0/1/2 are simply built in always available if trust ops are
NULL and 3/4 require positive reply from the ops to accept it.
So #3 needs a "trust ops" linked to enabling link IDE.. If this is
done in-kernel the link IDE module is providing the trust ops and just
using SPDM as a library to establish the link IDE keys?
Jason
^ permalink raw reply
* Re: [PATCH v2 09/19] PCI/TSM: Support creating encrypted MMIO descriptors via TDISP Report
From: Jason Gunthorpe @ 2026-03-24 12:38 UTC (permalink / raw)
To: Dan Williams
Cc: Xu Yilun, Aneesh Kumar K.V, linux-coco, linux-pci, gregkh, aik,
bhelgaas, alistair23, lukas, Arnd Bergmann
In-Reply-To: <69c2046287d35_7ee3100ed@dwillia2-mobl4.notmuch>
On Mon, Mar 23, 2026 at 08:26:26PM -0700, Dan Williams wrote:
> Xu Yilun wrote:
> [..]
> > And I've no idea why unlocked MSIX/PBA must be hidden? How about other
> > non-TEE ranges, must be hidden or mustn't? Is there a possibility we
> > enforce DSM to present all ranges, then the layout is clear to OS?
>
> Just to close this question, this was discussed at the last device
> security call. Indeed the expectation is that Linux will assume that all
> ranges besides MSIX/PBA must be present in the report, and that offset
> is always aligned. If / when an implementation violates that expectation
> they can help write the Linux quirk for that case or otherwise fix their
> implementation.
I don't think you can quirk it.
Implementations can always follow this requirement by setting the
offset to 0. If they cannot compute a proper aligned offset then this
is what they must do.
Jason
^ permalink raw reply
* Re: [PATCH v4 2/2] dma-buf: heaps: system: add system_cc_decrypted heap for explicitly decrypted memory
From: Jiri Pirko @ 2026-03-24 12:58 UTC (permalink / raw)
To: T.J. Mercier
Cc: dri-devel, linaro-mm-sig, iommu, linux-media, sumit.semwal,
benjamin.gaignard, Brian.Starkey, jstultz, christian.koenig,
m.szyprowski, robin.murphy, jgg, leon, sean.anderson, ptesarik,
catalin.marinas, aneesh.kumar, suzuki.poulose, steven.price,
thomas.lendacky, john.allen, ashish.kalra, suravee.suthikulpanit,
linux-coco
In-Reply-To: <CABdmKX0Ux2HMTTuOnvZryBBRwCjH0zUe-EMvL9k1VxZtz+BWiA@mail.gmail.com>
Tue, Mar 24, 2026 at 12:25:46AM +0100, tjmercier@google.com wrote:
>On Mon, Mar 16, 2026 at 5:59 AM Jiri Pirko <jiri@resnulli.us> wrote:
[..]
>> --- a/drivers/dma-buf/heaps/system_heap.c
>> +++ b/drivers/dma-buf/heaps/system_heap.c
>> @@ -10,17 +10,25 @@
>> * Andrew F. Davis <afd@ti.com>
>> */
>>
>> +#include <linux/cc_platform.h>
>> #include <linux/dma-buf.h>
>> #include <linux/dma-mapping.h>
>> #include <linux/dma-heap.h>
>> #include <linux/err.h>
>> #include <linux/highmem.h>
>> +#include <linux/mem_encrypt.h>
>> #include <linux/mm.h>
>> +#include <linux/set_memory.h>
>> #include <linux/module.h>
>> +#include <linux/pgtable.h>
>> #include <linux/scatterlist.h>
>> #include <linux/slab.h>
>> #include <linux/vmalloc.h>
>>
>> +struct system_heap_priv {
>> + bool decrypted;
>> +};
>
>Hi Jiri,
>
>I wonder if it'd better to call this cc_decrypted (or I guess
>cc_shared based on Robin's comment in the previous patch) like the DMA
>attr? There's a separate effort for "restricted" heaps with TEE for
>(encrypted) video playback, which doesn't involve VMs or RDMA. I think
>the cc_ prefix might help avoid any confusion between the usecase here
>and restricted heaps.
Sure. I'll rename this.
Thanks!
[..]
^ permalink raw reply
* [PATCH 0/2] x86/tdx: Fix memory hotplug in TDX guests
From: Marc-André Lureau @ 2026-03-24 15:21 UTC (permalink / raw)
To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Kiryl Shutsemau, Rick Edgecombe, Chenyi Qiang
Cc: linux-kernel, linux-coco, kvm, Marc-André Lureau
In TDX guests, hotplugged memory (e.g., via virtio-mem) must be accepted
via TDG.MEM.PAGE.ACCEPT before use. The first access to an unaccepted
page triggers a fatal "SEPT entry in PENDING state" EPT violation and
KVM terminates the guest.
This was discovered while testing virtio-mem resize with TDX guests.
The associated QEMU virtio-mem + TDX patch series is under review at:
https://patchew.org/QEMU/20260226140001.3622334-1-marcandre.lureau@redhat.com/
The fix has two parts:
1. Handle TDG.MEM.PAGE.ACCEPT "success-with-warning" returns for pages
that are already in MAPPED state (e.g., after offline/re-online
cycles), instead of treating them as fatal errors.
2. Register a MEM_GOING_ONLINE memory hotplug notifier that calls
tdx_accept_memory() before pages are freed to the buddy allocator.
The TDCALL transparently triggers KVM-side page augmentation (AUG)
followed by acceptance, avoiding the fatal EPT violation path.
The solution was suggested by Claude Code (Anthropic) and has been
tested with virtio-mem hot-add on a TDX guest. I did my best to review
the produced code and comments. Apologies if the agent did hallucinate.
Let me know if I need to check or correct something.
Thanks,
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
Marc-André Lureau (2):
x86/tdx: Handle TDG.MEM.PAGE.ACCEPT success-with-warning returns
x86/tdx: Accept hotplugged memory before online
arch/x86/coco/tdx/tdx-shared.c | 2 +-
arch/x86/coco/tdx/tdx.c | 38 ++++++++++++++++++++++++++++++++++++++
2 files changed, 39 insertions(+), 1 deletion(-)
---
base-commit: c369299895a591d96745d6492d4888259b004a9e
change-id: 20260324-tdx-hotplug-fixes-644d009dad63
Best regards,
--
Marc-André Lureau <marcandre.lureau@redhat.com>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox