* [PATCH v8 4/6] virt: coco: arm-cca-guest: Rename TSM report source file
From: Aneesh Kumar K.V (Arm) @ 2026-07-07 8:13 UTC (permalink / raw)
To: linux-coco, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Greg KH, Jeremy Linton,
Jonathan Cameron, Lorenzo Pieralisi, Mark Rutland, Sudeep Holla,
Will Deacon, Steven Price, Suzuki K Poulose, Andre Przywara
In-Reply-To: <20260707081351.1680209-1-aneesh.kumar@kernel.org>
The Arm CCA guest driver currently only implements TSM report support, but
follow-up changes will add more TSM-related functionality to the same
module.
Rename arm-cca-guest.c to arm-cca.c and build it as an object of the
arm-cca-guest module. This leaves room for the module to grow additional
source files.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
drivers/virt/coco/arm-cca-guest/Makefile | 2 ++
.../virt/coco/arm-cca-guest/{arm-cca-guest.c => arm-cca.c} | 6 +++---
2 files changed, 5 insertions(+), 3 deletions(-)
rename drivers/virt/coco/arm-cca-guest/{arm-cca-guest.c => arm-cca.c} (97%)
diff --git a/drivers/virt/coco/arm-cca-guest/Makefile b/drivers/virt/coco/arm-cca-guest/Makefile
index 69eeba08e98a..778146148515 100644
--- a/drivers/virt/coco/arm-cca-guest/Makefile
+++ b/drivers/virt/coco/arm-cca-guest/Makefile
@@ -1,2 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_ARM_CCA_GUEST) += arm-cca-guest.o
+
+arm-cca-guest-y += arm-cca.o
diff --git a/drivers/virt/coco/arm-cca-guest/arm-cca-guest.c b/drivers/virt/coco/arm-cca-guest/arm-cca.c
similarity index 97%
rename from drivers/virt/coco/arm-cca-guest/arm-cca-guest.c
rename to drivers/virt/coco/arm-cca-guest/arm-cca.c
index d05acddf6c4e..0c9fb61a28f1 100644
--- a/drivers/virt/coco/arm-cca-guest/arm-cca-guest.c
+++ b/drivers/virt/coco/arm-cca-guest/arm-cca.c
@@ -184,7 +184,7 @@ static int arm_cca_report_new(struct tsm_report *report, void *data)
return ret;
}
-static const struct tsm_report_ops arm_cca_tsm_ops = {
+static const struct tsm_report_ops arm_cca_tsm_report_ops = {
.name = KBUILD_MODNAME,
.report_new = arm_cca_report_new,
};
@@ -205,7 +205,7 @@ static int __init arm_cca_guest_init(void)
if (!is_realm_world())
return -ENODEV;
- ret = tsm_report_register(&arm_cca_tsm_ops, NULL);
+ ret = tsm_report_register(&arm_cca_tsm_report_ops, NULL);
if (ret < 0)
pr_err("Error %d registering with TSM\n", ret);
@@ -219,7 +219,7 @@ module_init(arm_cca_guest_init);
*/
static void __exit arm_cca_guest_exit(void)
{
- tsm_report_unregister(&arm_cca_tsm_ops);
+ tsm_report_unregister(&arm_cca_tsm_report_ops);
}
module_exit(arm_cca_guest_exit);
--
2.43.0
^ permalink raw reply related
* [PATCH v8 5/6] firmware: smccc: arm-cca-guest: Bind the TSM provider to an SMCCC device
From: Aneesh Kumar K.V (Arm) @ 2026-07-07 8:13 UTC (permalink / raw)
To: linux-coco, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Greg KH, Jeremy Linton,
Jonathan Cameron, Lorenzo Pieralisi, Mark Rutland, Sudeep Holla,
Will Deacon, Steven Price, Suzuki K Poulose, Andre Przywara
In-Reply-To: <20260707081351.1680209-1-aneesh.kumar@kernel.org>
The Arm CCA guest TSM provider currently binds through the arm-cca-dev
platform device. Like arm-smccc-trng, this device is not an independent
platform resource; it is a software representation of the RSI firmware
service discovered through SMCCC.
Move RSI discovery into the SMCCC firmware driver. When the SMCCC conduit
is SMC and if RSI ABI version call is supported, create an arm-rsi-dev
SMCCC device. Convert the Arm CCA guest TSM provider to an SMCCC driver so
it binds to that discovered RSI service and keeps module autoloading
through the SMCCC device id table.
Keep the old arm-cca-dev platform-device registration for now. Userspace
has used that device as a Realm-guest indicator, so removing it is left to
a follow-up patch that adds a replacement sysfs ABI.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rsi.h | 2 -
arch/arm64/kernel/rsi.c | 2 +-
drivers/firmware/smccc/smccc.c | 7 +++
drivers/virt/coco/arm-cca-guest/Kconfig | 1 +
drivers/virt/coco/arm-cca-guest/arm-cca.c | 56 +++++++++++------------
include/linux/arm-smccc-rsi.h | 2 +
6 files changed, 39 insertions(+), 31 deletions(-)
diff --git a/arch/arm64/include/asm/rsi.h b/arch/arm64/include/asm/rsi.h
index 88b50d660e85..5f9c8623183d 100644
--- a/arch/arm64/include/asm/rsi.h
+++ b/arch/arm64/include/asm/rsi.h
@@ -10,8 +10,6 @@
#include <linux/jump_label.h>
#include <asm/rsi_cmds.h>
-#define RSI_PDEV_NAME "arm-cca-dev"
-
DECLARE_STATIC_KEY_FALSE(rsi_present);
void __init arm64_rsi_init(void);
diff --git a/arch/arm64/kernel/rsi.c b/arch/arm64/kernel/rsi.c
index 92160f2e57ff..da440f71bb64 100644
--- a/arch/arm64/kernel/rsi.c
+++ b/arch/arm64/kernel/rsi.c
@@ -161,7 +161,7 @@ void __init arm64_rsi_init(void)
}
static struct platform_device rsi_dev = {
- .name = RSI_PDEV_NAME,
+ .name = "arm-cca-dev",
.id = PLATFORM_DEVID_NONE
};
diff --git a/drivers/firmware/smccc/smccc.c b/drivers/firmware/smccc/smccc.c
index 092bdbac26f2..537b4b22c395 100644
--- a/drivers/firmware/smccc/smccc.c
+++ b/drivers/firmware/smccc/smccc.c
@@ -10,6 +10,7 @@
#include <linux/arm-smccc.h>
#include <linux/kernel.h>
#include <linux/arm-smccc-bus.h>
+#include <linux/arm-smccc-rsi.h>
#include <asm/archrandom.h>
@@ -94,6 +95,12 @@ static const struct smccc_device_info smccc_devices[] __initconst = {
.requires_smc = false,
.device_name = "arm-smccc-trng",
},
+
+ {
+ .func_id = SMC_RSI_ABI_VERSION,
+ .requires_smc = true,
+ .device_name = RSI_DEV_NAME,
+ },
};
static bool __init smccc_probe_smccc_device(const struct smccc_device_info *smccc_dev)
diff --git a/drivers/virt/coco/arm-cca-guest/Kconfig b/drivers/virt/coco/arm-cca-guest/Kconfig
index 3f0f013f03f1..ad7538750c5a 100644
--- a/drivers/virt/coco/arm-cca-guest/Kconfig
+++ b/drivers/virt/coco/arm-cca-guest/Kconfig
@@ -1,6 +1,7 @@
config ARM_CCA_GUEST
tristate "Arm CCA Guest driver"
depends on ARM64
+ depends on HAVE_ARM_SMCCC_DISCOVERY
select TSM_REPORTS
help
The driver provides userspace interface to request and
diff --git a/drivers/virt/coco/arm-cca-guest/arm-cca.c b/drivers/virt/coco/arm-cca-guest/arm-cca.c
index 0c9fb61a28f1..399915f1fb3e 100644
--- a/drivers/virt/coco/arm-cca-guest/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-guest/arm-cca.c
@@ -4,6 +4,7 @@
*/
#include <linux/arm-smccc.h>
+#include <linux/arm-smccc-bus.h>
#include <linux/cc_platform.h>
#include <linux/kernel.h>
#include <linux/device-id/platform.h>
@@ -189,16 +190,12 @@ static const struct tsm_report_ops arm_cca_tsm_report_ops = {
.report_new = arm_cca_report_new,
};
-/**
- * arm_cca_guest_init - Register with the Trusted Security Module (TSM)
- * interface.
- *
- * Return:
- * * %0 - Registered successfully with the TSM interface.
- * * %-ENODEV - The execution context is not an Arm Realm.
- * * %-EBUSY - Already registered.
- */
-static int __init arm_cca_guest_init(void)
+static void unregister_cca_tsm_report(void *data)
+{
+ tsm_report_unregister(&arm_cca_tsm_report_ops);
+}
+
+static int cca_tsm_probe(struct arm_smccc_device *sdev)
{
int ret;
@@ -206,30 +203,33 @@ static int __init arm_cca_guest_init(void)
return -ENODEV;
ret = tsm_report_register(&arm_cca_tsm_report_ops, NULL);
- if (ret < 0)
- pr_err("Error %d registering with TSM\n", ret);
+ if (ret < 0) {
+ dev_err_probe(&sdev->dev, ret, "Error registering with TSM\n");
+ return ret;
+ }
- return ret;
-}
-module_init(arm_cca_guest_init);
+ ret = devm_add_action_or_reset(&sdev->dev, unregister_cca_tsm_report,
+ NULL);
+ if (ret < 0) {
+ dev_err_probe(&sdev->dev, ret, "Error registering devm action\n");
+ return ret;
+ }
-/**
- * arm_cca_guest_exit - unregister with the Trusted Security Module (TSM)
- * interface.
- */
-static void __exit arm_cca_guest_exit(void)
-{
- tsm_report_unregister(&arm_cca_tsm_report_ops);
+ return 0;
}
-module_exit(arm_cca_guest_exit);
-/* modalias, so userspace can autoload this module when RSI is available */
-static const struct platform_device_id arm_cca_match[] __maybe_unused = {
- { RSI_PDEV_NAME, 0},
- { }
+static const struct arm_smccc_device_id cca_tsm_id_table[] = {
+ { .name = RSI_DEV_NAME },
+ {}
};
+MODULE_DEVICE_TABLE(arm_smccc, cca_tsm_id_table);
-MODULE_DEVICE_TABLE(platform, arm_cca_match);
+static struct arm_smccc_driver cca_tsm_driver = {
+ .name = KBUILD_MODNAME,
+ .probe = cca_tsm_probe,
+ .id_table = cca_tsm_id_table,
+};
+module_arm_smccc_driver(cca_tsm_driver);
MODULE_AUTHOR("Sami Mujawar <sami.mujawar@arm.com>");
MODULE_DESCRIPTION("Arm CCA Guest TSM Driver");
MODULE_LICENSE("GPL");
diff --git a/include/linux/arm-smccc-rsi.h b/include/linux/arm-smccc-rsi.h
index fddb77986f70..ae663aa8fd7f 100644
--- a/include/linux/arm-smccc-rsi.h
+++ b/include/linux/arm-smccc-rsi.h
@@ -8,6 +8,8 @@
#include <linux/arm-smccc.h>
+#define RSI_DEV_NAME "arm-rsi-dev"
+
/*
* This file describes the Realm Services Interface (RSI) Application Binary
* Interface (ABI) for SMC calls made from within the Realm to the RMM and
--
2.43.0
^ permalink raw reply related
* [PATCH v8 6/6] coco: guest: arm64: Replace dummy CCA device with sysfs ABI
From: Aneesh Kumar K.V (Arm) @ 2026-07-07 8:13 UTC (permalink / raw)
To: linux-coco, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Greg KH, Jeremy Linton,
Jonathan Cameron, Lorenzo Pieralisi, Mark Rutland, Sudeep Holla,
Will Deacon, Steven Price, Suzuki K Poulose, Andre Przywara
In-Reply-To: <20260707081351.1680209-1-aneesh.kumar@kernel.org>
The SMCCC firmware driver now creates the arm-smccc platform device and
instantiates the CCA RSI auxiliary devices once the RSI ABI is discovered.
The arm64-specific arm-cca-dev platform device stub is therefore no longer
needed.
However, userspace has used the arm-cca-dev platform device to detect Arm
CCA Realm guests [1]. Removing it without a replacement would break that
detection and would also leave userspace depending on kernel device-model
details.
Add /sys/firmware/cca/realm_guest as a stable, architecture-provided ABI
for detecting whether the kernel is running as an Arm CCA Realm guest. The
file returns 1 in Realm world and 0 otherwise, similar to the existing s390
/sys/firmware/uv/prot_virt_guest interface for protected virtualization
guests.
Remove the dummy arm-cca-dev registration now that userspace has a
dedicated CCA Realm guest indicator, and document the new ABI in
Documentation/ABI/testing/sysfs-firmware-cca.
[1] https://lore.kernel.org/all/4a7d84b2-2ec4-4773-a2d5-7b63d5c683cf@arm.com
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
Documentation/ABI/testing/sysfs-firmware-cca | 10 +++++
arch/arm64/kernel/rsi.c | 39 +++++++++++++++-----
2 files changed, 39 insertions(+), 10 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-firmware-cca
diff --git a/Documentation/ABI/testing/sysfs-firmware-cca b/Documentation/ABI/testing/sysfs-firmware-cca
new file mode 100644
index 000000000000..bf177d636b92
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-firmware-cca
@@ -0,0 +1,10 @@
+What: /sys/firmware/cca/realm_guest
+Date: May 2026
+Contact: Linux ARM Kernel Mailing list <linux-arm-kernel@lists.infradead.org>
+Description: Read-only. Indicates whether the kernel is running as an
+ Arm Confidential Compute Architecture (CCA) Realm guest.
+
+ The value is one of:
+
+ 0: the kernel is not running as a Realm guest
+ 1: the kernel is running as a Realm guest
diff --git a/arch/arm64/kernel/rsi.c b/arch/arm64/kernel/rsi.c
index da440f71bb64..a333029ddf08 100644
--- a/arch/arm64/kernel/rsi.c
+++ b/arch/arm64/kernel/rsi.c
@@ -9,6 +9,8 @@
#include <linux/swiotlb.h>
#include <linux/cc_platform.h>
#include <linux/platform_device.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
#include <asm/io.h>
#include <asm/mem_encrypt.h>
@@ -16,6 +18,7 @@
#include <asm/rsi.h>
static struct realm_config config;
+static struct kobject *cca_kobj;
unsigned long prot_ns_shared;
EXPORT_SYMBOL(prot_ns_shared);
@@ -160,17 +163,33 @@ void __init arm64_rsi_init(void)
static_branch_enable(&rsi_present);
}
-static struct platform_device rsi_dev = {
- .name = "arm-cca-dev",
- .id = PLATFORM_DEVID_NONE
+static ssize_t cca_is_realm_guest(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "%d\n", is_realm_world());
+}
+
+static struct kobj_attribute cca_realm_guest =
+ __ATTR(realm_guest, 0444, cca_is_realm_guest, NULL);
+
+static const struct attribute *cca_realm_attrs[] = {
+ &cca_realm_guest.attr,
+ NULL,
};
-static int __init arm64_create_dummy_rsi_dev(void)
+static int __init realm_sysfs_init(void)
{
- if (is_realm_world() &&
- platform_device_register(&rsi_dev))
- pr_err("failed to register rsi platform device\n");
- return 0;
-}
+ int ret;
+
+ cca_kobj = kobject_create_and_add("cca", firmware_kobj);
+ if (!cca_kobj)
+ return -ENOMEM;
-arch_initcall(arm64_create_dummy_rsi_dev)
+ ret = sysfs_create_files(cca_kobj, cca_realm_attrs);
+ if (!ret)
+ return 0;
+
+ kobject_put(cca_kobj);
+ return ret;
+}
+device_initcall(realm_sysfs_init);
--
2.43.0
^ permalink raw reply related
* CXL TSP+DCD: does trusted range re-use rebind to a device extent across release/re-add?
From: Flavien Solt @ 2026-07-07 10:42 UTC (permalink / raw)
To: linux-coco@lists.linux.dev
Cc: linux-pci@vger.kernel.org, linux-cxl@vger.kernel.org,
dan.j.williams@intel.com, ira.weiny@intel.com,
jonathan.cameron@huawei.com
Hi all,
I raised the host side of this on linux-cxl recently while this is the confidential-computing half of it. In the CoCo model, the Fabric Manager and the fabric sit outside the TVM's TCB, hence a release/re-add that swaps the backing behind a host range either has to be prevented, or the guest's trust in that range shouldn't survive it. What I can't tell from the specs or the current kernel is which of those the architecture intends.
Under Dynamic Capacity, an FM can release an extent and add a different one at the same host physical address. r3.2 §11.5.4.10 already has the target clear memory and reset TE State on add, so residue and cross-host disclosure are covered. My question is specifically about identity. After a release and re-add, the range the TVM accepted looks the same and the TDI stays in RUN (a DCD reassignment isn't a TDISP configuration-space change). But the device-internal extent, the fabric tag and also the grant lifetime behind that range have all changed. A correct TE-State reset tells you the memory is fresh and private. It doesn't tell you the range is still backed by the same extent and grant the guest started with.
So I'm wondering where is that change of backing supposed to get caught? The trusted device interface the kernel binds doesn't seem to carry any DPA-extent or grant identity, which leaves nothing on the host side to compare a re-add against. Is that deliberate, with the binding meant to live in the target's TE-State reset plus the SPDM device evidence? Or is the missing piece a host/TSM-held binding of a range to a specific extent and grant epoch? Put another way, should a DCD release/re-add of a trusted range trigger any TSP/TDISP re-validation, or is the TVM supposed to keep trusting the range across it? It feels like the in-place version of the cross-device migration trust question that came up at Plumbers last year.
One related corner: forced release (Table 7-71) reclaims without host cooperation. Does the TE-State reset have to be atomic with a forced release and observable across it, or can a re-add race it?
I've got a small QEMU repro, for what it's worth, with an attested device's DCD-backed range gets force-released and re-added with physically different backing and the guest then reads different bytes at the same address with nothing re-validated. That also touches the forced-release race above. Address equality clearly isn't extent or grant identity.
Thanks,
Flavien
________________________________
Important: This email is confidential and may be privileged. If you are not the intended recipient, please delete it and notify us immediately; you should not copy or use it for any purpose, nor disclose its contents to any other person. Thank you.
^ permalink raw reply
* Re: [PATCH v8 4/6] virt: coco: arm-cca-guest: Rename TSM report source file
From: Suzuki K Poulose @ 2026-07-07 12:37 UTC (permalink / raw)
To: Aneesh Kumar K.V (Arm), linux-coco, linux-arm-kernel,
linux-kernel
Cc: Catalin Marinas, Greg KH, Jeremy Linton, Jonathan Cameron,
Lorenzo Pieralisi, Mark Rutland, Sudeep Holla, Will Deacon,
Steven Price, Andre Przywara
In-Reply-To: <20260707081351.1680209-5-aneesh.kumar@kernel.org>
On 07/07/2026 09:13, Aneesh Kumar K.V (Arm) wrote:
> The Arm CCA guest driver currently only implements TSM report support, but
> follow-up changes will add more TSM-related functionality to the same
> module.
>
> Rename arm-cca-guest.c to arm-cca.c and build it as an object of the
> arm-cca-guest module. This leaves room for the module to grow additional
> source files.
>
> Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> drivers/virt/coco/arm-cca-guest/Makefile | 2 ++
> .../virt/coco/arm-cca-guest/{arm-cca-guest.c => arm-cca.c} | 6 +++---
> 2 files changed, 5 insertions(+), 3 deletions(-)
> rename drivers/virt/coco/arm-cca-guest/{arm-cca-guest.c => arm-cca.c} (97%)
>
> diff --git a/drivers/virt/coco/arm-cca-guest/Makefile b/drivers/virt/coco/arm-cca-guest/Makefile
> index 69eeba08e98a..778146148515 100644
> --- a/drivers/virt/coco/arm-cca-guest/Makefile
> +++ b/drivers/virt/coco/arm-cca-guest/Makefile
> @@ -1,2 +1,4 @@
> # SPDX-License-Identifier: GPL-2.0-only
> obj-$(CONFIG_ARM_CCA_GUEST) += arm-cca-guest.o
> +
> +arm-cca-guest-y += arm-cca.o
> diff --git a/drivers/virt/coco/arm-cca-guest/arm-cca-guest.c b/drivers/virt/coco/arm-cca-guest/arm-cca.c
> similarity index 97%
> rename from drivers/virt/coco/arm-cca-guest/arm-cca-guest.c
> rename to drivers/virt/coco/arm-cca-guest/arm-cca.c
> index d05acddf6c4e..0c9fb61a28f1 100644
> --- a/drivers/virt/coco/arm-cca-guest/arm-cca-guest.c
> +++ b/drivers/virt/coco/arm-cca-guest/arm-cca.c
> @@ -184,7 +184,7 @@ static int arm_cca_report_new(struct tsm_report *report, void *data)
> return ret;
> }
>
> -static const struct tsm_report_ops arm_cca_tsm_ops = {
> +static const struct tsm_report_ops arm_cca_tsm_report_ops = {
> .name = KBUILD_MODNAME,
> .report_new = arm_cca_report_new,
> };
> @@ -205,7 +205,7 @@ static int __init arm_cca_guest_init(void)
> if (!is_realm_world())
> return -ENODEV;
>
> - ret = tsm_report_register(&arm_cca_tsm_ops, NULL);
> + ret = tsm_report_register(&arm_cca_tsm_report_ops, NULL);
> if (ret < 0)
> pr_err("Error %d registering with TSM\n", ret);
>
> @@ -219,7 +219,7 @@ module_init(arm_cca_guest_init);
> */
> static void __exit arm_cca_guest_exit(void)
> {
> - tsm_report_unregister(&arm_cca_tsm_ops);
> + tsm_report_unregister(&arm_cca_tsm_report_ops);
> }
> module_exit(arm_cca_guest_exit);
>
^ permalink raw reply
* Re: [PATCH v8 5/6] firmware: smccc: arm-cca-guest: Bind the TSM provider to an SMCCC device
From: Suzuki K Poulose @ 2026-07-07 12:41 UTC (permalink / raw)
To: Aneesh Kumar K.V (Arm), linux-coco, linux-arm-kernel,
linux-kernel
Cc: Catalin Marinas, Greg KH, Jeremy Linton, Jonathan Cameron,
Lorenzo Pieralisi, Mark Rutland, Sudeep Holla, Will Deacon,
Steven Price, Andre Przywara
In-Reply-To: <20260707081351.1680209-6-aneesh.kumar@kernel.org>
On 07/07/2026 09:13, Aneesh Kumar K.V (Arm) wrote:
> The Arm CCA guest TSM provider currently binds through the arm-cca-dev
> platform device. Like arm-smccc-trng, this device is not an independent
> platform resource; it is a software representation of the RSI firmware
> service discovered through SMCCC.
>
> Move RSI discovery into the SMCCC firmware driver. When the SMCCC conduit
> is SMC and if RSI ABI version call is supported, create an arm-rsi-dev
> SMCCC device. Convert the Arm CCA guest TSM provider to an SMCCC driver so
> it binds to that discovered RSI service and keeps module autoloading
> through the SMCCC device id table.
>
> Keep the old arm-cca-dev platform-device registration for now. Userspace
> has used that device as a Realm-guest indicator, so removing it is left to
> a follow-up patch that adds a replacement sysfs ABI.
>
> Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> arch/arm64/include/asm/rsi.h | 2 -
> arch/arm64/kernel/rsi.c | 2 +-
> drivers/firmware/smccc/smccc.c | 7 +++
> drivers/virt/coco/arm-cca-guest/Kconfig | 1 +
> drivers/virt/coco/arm-cca-guest/arm-cca.c | 56 +++++++++++------------
> include/linux/arm-smccc-rsi.h | 2 +
> 6 files changed, 39 insertions(+), 31 deletions(-)
>
> diff --git a/arch/arm64/include/asm/rsi.h b/arch/arm64/include/asm/rsi.h
> index 88b50d660e85..5f9c8623183d 100644
> --- a/arch/arm64/include/asm/rsi.h
> +++ b/arch/arm64/include/asm/rsi.h
> @@ -10,8 +10,6 @@
> #include <linux/jump_label.h>
> #include <asm/rsi_cmds.h>
>
> -#define RSI_PDEV_NAME "arm-cca-dev"
> -
> DECLARE_STATIC_KEY_FALSE(rsi_present);
>
> void __init arm64_rsi_init(void);
> diff --git a/arch/arm64/kernel/rsi.c b/arch/arm64/kernel/rsi.c
> index 92160f2e57ff..da440f71bb64 100644
> --- a/arch/arm64/kernel/rsi.c
> +++ b/arch/arm64/kernel/rsi.c
> @@ -161,7 +161,7 @@ void __init arm64_rsi_init(void)
> }
>
> static struct platform_device rsi_dev = {
> - .name = RSI_PDEV_NAME,
> + .name = "arm-cca-dev",
> .id = PLATFORM_DEVID_NONE
> };
>
> diff --git a/drivers/firmware/smccc/smccc.c b/drivers/firmware/smccc/smccc.c
> index 092bdbac26f2..537b4b22c395 100644
> --- a/drivers/firmware/smccc/smccc.c
> +++ b/drivers/firmware/smccc/smccc.c
> @@ -10,6 +10,7 @@
> #include <linux/arm-smccc.h>
> #include <linux/kernel.h>
> #include <linux/arm-smccc-bus.h>
> +#include <linux/arm-smccc-rsi.h>
>
> #include <asm/archrandom.h>
>
> @@ -94,6 +95,12 @@ static const struct smccc_device_info smccc_devices[] __initconst = {
> .requires_smc = false,
> .device_name = "arm-smccc-trng",
> },
> +
> + {
> + .func_id = SMC_RSI_ABI_VERSION,
> + .requires_smc = true,
> + .device_name = RSI_DEV_NAME,
> + },
> };
>
> static bool __init smccc_probe_smccc_device(const struct smccc_device_info *smccc_dev)
> diff --git a/drivers/virt/coco/arm-cca-guest/Kconfig b/drivers/virt/coco/arm-cca-guest/Kconfig
> index 3f0f013f03f1..ad7538750c5a 100644
> --- a/drivers/virt/coco/arm-cca-guest/Kconfig
> +++ b/drivers/virt/coco/arm-cca-guest/Kconfig
> @@ -1,6 +1,7 @@
> config ARM_CCA_GUEST
> tristate "Arm CCA Guest driver"
> depends on ARM64
> + depends on HAVE_ARM_SMCCC_DISCOVERY
> select TSM_REPORTS
> help
> The driver provides userspace interface to request and
> diff --git a/drivers/virt/coco/arm-cca-guest/arm-cca.c b/drivers/virt/coco/arm-cca-guest/arm-cca.c
> index 0c9fb61a28f1..399915f1fb3e 100644
> --- a/drivers/virt/coco/arm-cca-guest/arm-cca.c
> +++ b/drivers/virt/coco/arm-cca-guest/arm-cca.c
> @@ -4,6 +4,7 @@
> */
>
> #include <linux/arm-smccc.h>
> +#include <linux/arm-smccc-bus.h>
> #include <linux/cc_platform.h>
> #include <linux/kernel.h>
> #include <linux/device-id/platform.h>
> @@ -189,16 +190,12 @@ static const struct tsm_report_ops arm_cca_tsm_report_ops = {
> .report_new = arm_cca_report_new,
> };
>
> -/**
> - * arm_cca_guest_init - Register with the Trusted Security Module (TSM)
> - * interface.
> - *
> - * Return:
> - * * %0 - Registered successfully with the TSM interface.
> - * * %-ENODEV - The execution context is not an Arm Realm.
> - * * %-EBUSY - Already registered.
> - */
> -static int __init arm_cca_guest_init(void)
> +static void unregister_cca_tsm_report(void *data)
> +{
> + tsm_report_unregister(&arm_cca_tsm_report_ops);
> +}
> +
> +static int cca_tsm_probe(struct arm_smccc_device *sdev)
> {
> int ret;
>
> @@ -206,30 +203,33 @@ static int __init arm_cca_guest_init(void)
> return -ENODEV;
>
> ret = tsm_report_register(&arm_cca_tsm_report_ops, NULL);
> - if (ret < 0)
> - pr_err("Error %d registering with TSM\n", ret);
> + if (ret < 0) {
> + dev_err_probe(&sdev->dev, ret, "Error registering with TSM\n");
> + return ret;
> + }
>
> - return ret;
> -}
> -module_init(arm_cca_guest_init);
> + ret = devm_add_action_or_reset(&sdev->dev, unregister_cca_tsm_report,
> + NULL);
> + if (ret < 0) {
> + dev_err_probe(&sdev->dev, ret, "Error registering devm action\n");
> + return ret;
> + }
>
> -/**
> - * arm_cca_guest_exit - unregister with the Trusted Security Module (TSM)
> - * interface.
> - */
> -static void __exit arm_cca_guest_exit(void)
> -{
> - tsm_report_unregister(&arm_cca_tsm_report_ops);
> + return 0;
> }
> -module_exit(arm_cca_guest_exit);
>
> -/* modalias, so userspace can autoload this module when RSI is available */
> -static const struct platform_device_id arm_cca_match[] __maybe_unused = {
> - { RSI_PDEV_NAME, 0},
> - { }
> +static const struct arm_smccc_device_id cca_tsm_id_table[] = {
> + { .name = RSI_DEV_NAME },
> + {}
> };
> +MODULE_DEVICE_TABLE(arm_smccc, cca_tsm_id_table);
>
> -MODULE_DEVICE_TABLE(platform, arm_cca_match);
> +static struct arm_smccc_driver cca_tsm_driver = {
> + .name = KBUILD_MODNAME,
> + .probe = cca_tsm_probe,
> + .id_table = cca_tsm_id_table,
> +};
> +module_arm_smccc_driver(cca_tsm_driver);
> MODULE_AUTHOR("Sami Mujawar <sami.mujawar@arm.com>");
> MODULE_DESCRIPTION("Arm CCA Guest TSM Driver");
> MODULE_LICENSE("GPL");
> diff --git a/include/linux/arm-smccc-rsi.h b/include/linux/arm-smccc-rsi.h
> index fddb77986f70..ae663aa8fd7f 100644
> --- a/include/linux/arm-smccc-rsi.h
> +++ b/include/linux/arm-smccc-rsi.h
> @@ -8,6 +8,8 @@
>
> #include <linux/arm-smccc.h>
>
> +#define RSI_DEV_NAME "arm-rsi-dev"
> +
> /*
> * This file describes the Realm Services Interface (RSI) Application Binary
> * Interface (ABI) for SMC calls made from within the Realm to the RMM and
^ permalink raw reply
* Re: [PATCH 00/15] Device Evidence and Trust for PCI Security Protocol (TDISP)
From: Jason Gunthorpe @ 2026-07-07 12:43 UTC (permalink / raw)
To: Dan Williams (nvidia)
Cc: linux-coco, linux-pci, driver-core, ankita, Aaron Tomlin,
Alexey Kardashevskiy, Alistair Francis, Aneesh Kumar K.V,
Arnd Bergmann, Bjorn Helgaas, Daniel Gomez, Danilo Krummrich,
Dexuan Cui, Donald Hunter, Greg Kroah-Hartman, Jakub Kicinski,
Luis Chamberlain, Lukas Wunner, Petr Pavlu, Rafael J. Wysocki,
Robin Murphy, Sami Tolvanen, Samuel Ortiz, Saravana Kannan,
Will Deacon, Xu Yilun
In-Reply-To: <6a4c163072c60_174db6100c4@djbw-dev.notmuch>
On Mon, Jul 06, 2026 at 01:55:12PM -0700, Dan Williams (nvidia) wrote:
> Jason Gunthorpe wrote:
> > On Sun, Jul 05, 2026 at 03:08:04PM -0700, Dan Williams wrote:
> > > * NONE: no usage of the device unless the trust is explicitly overridden
> > > by user policy specified via a driver flag, module flag, or uapi (TBD).
> > >
> > > * ADVERSARY: needs acknowledgement from the bus and IOMMU / DMA layers
> > > that the device is limited to strict IOMMU translation behavior. Drivers
> > > can use this as a signal to limit functionality. This designation
> > > implies follow-on IOMMU and bus enabling work for features like
> > > arranging for the device to attach to a blocked IOMMU domain when
> > > detached from a driver.
> > >
> > > * AUTO: typical / historical Linux driver model.
> > >
> > > * TCB: a trust level that only exists in Confidential Computing
> > > environments. When acked by the IOMMU / DMA layer it enables the device
> > > to issue direct-DMA to private/encrypted addresses or otherwise attach to
> > > a secure vIOMMU within the TCB.
> >
> > I'm not sure I entirely like this one, certainly it needs to be
> > possible to have both T=1 and ADVERSARY together.
>
> T=1 and ADVERSARY are independent for link encryption and private MMIO.
> In other words the device is placed into the TDISP RUN state independent
> of its trust level.
That's the right thing
> Downstream accesses to the device must have T=1, and
> its upstream accesses will have T=1, but with force_dma_unencrypted() ==
> true.
That should never happen. Once in RUN force_dma_unencrypyted() ==
false, it has nothing to do with the trust level.
Even if you set ADVERSARY it should still be bouncing partial page
DMAs into private memory. The point of running something like this is
to remove the shared memory attack surface - ie the hypervisor SW. The
attack surface is reduced to the device itself by remaining in shared
memory.
> > I'd also argue this list is missing "FULL" trust, which is the
> > historical Linux behavior for a normal device. AUTO should be
> > selecting between FULL/ADVERSARY based on things like the ACPI/etc as
> > it does today.
>
> 1/ that is effectively how the UNSET level behaves. If the
> bus has not set ADVERSARY before device_add() then the default behavior
> is the AUTO level. Where AUTO means all of the automatic privileges a
> device can be offered without needing any other coordination.
I think my other remark about two enums is some of the issue, the
policy can have things like UNSET or AUTO, but once the driver starts
to probe an in-effect mode should be computed and be concrete. Having
a driver run with a trust mode of AUTO or UNSET is just confusing.
> 2/ The ambiguity and conflict occurs at ->dma_configure() time when the
> bus and IOMMU layer want to reject the device's access to some privilege
> by failing. When FULL is defined as !ADVERSARY then it is difficult to
> describe the semantics when FULL trust honors rejections to private DMA
> and when it falls back to shared operation.
Given that the trust level shouldn't impact force_dma_unencrypted(),
the only thing left is to setup the IOMMU differently, and maybe
operate a driver in a hardened mode or something like that.
I don't see what the TCB is supposed to be changing here.
That leaves it just as a policy gate to check that T=1, I'm not sure
if that is worthwhile enough for dedicated UAPI?
> The above more points to a need to have an explicit trust level for
> adversarial private memory access. The address spaces are distinct
> assets with different levels of trust.
>
> UNSET: bus picks initial level, or leaves it to the device_core().
> NONE:
> ADVERSARY: Device can be in T=0, or T=1 mode (UNLOCKED, or RUN).
> AUTO: Could rename this to be FULL or ALL or DEFAULT, I still keep
> coming back to the "AUTO" name because the privileges are not
> uniform based on the IOMMU / DMA topology and device capability.
> Again, the TDISP state is independent. The TSM driver does
> not get called to gatekeep and verify access in this mode.
> TCB_ADVERSARY: or PRIVATE_ADVERSARY. Device can access private platform
> resources iff an enforcing IOMMU is present.
> TCB: or PRIVATE_FULL, automatically enable all access privileges
> including private memory access.
Yeah, we can keep adding more modes to make a big cross product, but
I do wonder if this is going to get too big..
IDK, maybe it should be a bitmap instead of a level?
bit 0 = Force Disable
bit 1 = Device is adversarial:
- Enable strong IOMMU protections
- Enable driver protections
bit 2 = Require T=1
bit 3 = Require IOMMU
bit 4 = Require DMA/MMIO security (eg Link IDE)
Where value 0 means the current level of full trust.
It is a little easier to explain what each thing is doing and easier
to add new things
Then from a sysfs perpsective the policy would have special string
values like 'use bus default'
> > If the trust level is reduced to just be a command to the kernel how
> > it should operate the device then it would be up to userspace to
> > confirm things like T=1 before setting the trust.
>
> This discussion gets strained for me when T=1 is used to mean both
> "device is in TDISP RUN (with link encryption and private MMIO)" and
> "device is in TDISP RUN + force_dma_unencrypted() == false".
It means both things though, we really must not run with
force_dma_unencrypted() = true when T=1, that's pointless and harmful.
> Otherwise, full bi-directional T=1 before setting the trust would
> require an IOMMU to be blocking the device until that final confirmation
> point. Given that is not always available the proposal is to defer
> acknowledging the trust level with the TSM until ->dma_configure().
?? If you have no iommu the instance you set T=1 and do the platform
step to activate DMA the device has 100% acess to all
memory. force_dma_unencrypted() does nothing to constrain device
access, it is all about accommodating an addressing limitation.
ARM at least has a dedicated call to enable DMA. It would be nice to
place that call right before the driver probes so DMA remains off
until we commit to using the device. Maybe other platforms have the
same - but I'm not sure it is *essential* as the point of setting RUN
can reasonably be the acceptance point.
> NONE: Device core rejects device operation
> ADVERSARY: reject device operation if an IOMMU to set IOMMU_DOMAIN_DMA
> not available (not in current patches)
> AUTO: no rejections, but no private memory access either
> TCB_ADVERSARY: reject device operation if IOMMU_DOMAIN_DMA not
> available, or TSM rejects the evidence used to enable
> private memory access.
> TCB: reject device operation if TSM rejects chosen evidence
What does "TSM rejectes chosen evidence" mean? Kernel isn't supposed
to be evaluating evidence?
Jason
^ permalink raw reply
* Re: [PATCH v7 00/22] dma-mapping: Track shared DMA state through direct, pool and swiotlb paths
From: Marek Szyprowski @ 2026-07-07 13:03 UTC (permalink / raw)
To: Aneesh Kumar K.V, iommu, linux-arm-kernel, linux-kernel,
linux-coco
Cc: Robin Murphy, Will Deacon, Marc Zyngier, Steven Price,
Suzuki K Poulose, Catalin Marinas, Jiri Pirko, Jason Gunthorpe,
Mostafa Saleh, Petr Tesarik, Alexey Kardashevskiy, Dan Williams,
Xu Yilun, linuxppc-dev, linux-s390, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Christophe Leroy (CS GROUP),
Alexander Gordeev, Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <yq5av7argr65.fsf@kernel.org>
On 07.07.2026 10:06, Aneesh Kumar K.V wrote:
> "Aneesh Kumar K.V (Arm)" <aneesh.kumar@kernel.org> writes:
>
>> This series tracks confidential-computing shared DMA state through the
>> dma-direct, dma-pool, and swiotlb paths so that encrypted and decrypted
>> DMA buffers are handled consistently.
>>
>> Today, the direct DMA path mostly relies on force_dma_unencrypted() for
>> shared/decrypted buffer handling. This series consolidates the
>> force_dma_unencrypted() checks in the top-level functions and ensures
>> that the remaining DMA interfaces use DMA attributes to make the correct
>> decisions.
>>
>> The series separates mapping and allocation state:
>> - DMA_ATTR_CC_SHARED describes the DMA address attribute requested for a
>> mapping. It tells the DMA mapping path that the DMA address must target
>> shared/decrypted memory.
>> - __DMA_ATTR_ALLOC_CC_SHARED is an internal DMA-mapping attribute used only
>> by allocation paths after the DMA core decides that the backing pages
>> must be allocated as shared/decrypted memory.
>>
>> The series:
>> - moves swiotlb-backed allocations out of __dma_direct_alloc_pages(),
>> - uses __DMA_ATTR_ALLOC_CC_SHARED through the dma-direct alloc/free paths
>> - teaches the atomic DMA pools to track encrypted versus decrypted
>> state
>> - tracks swiotlb pool encryption state and enforces strict pool
>> selection
>> - centralizes encrypted/decrypted pgprot handling in dma_pgprot() using
>> DMA attributes
>> - passes DMA attributes down to dma_capable() so capability checks can
>> validate whether the selected DMA address encoding matches
>> DMA_ATTR_CC_SHARED
>> - makes dma_direct_map_phys() choose the DMA address encoding from
>> DMA_ATTR_CC_SHARED and fall back to swiotlb when a shared DMA request
>> cannot use the direct mapping, which lets arm64 and x86 CCA guests stop
>> relying on SWIOTLB_FORCE for DMA mappings
>> - use the selected swiotlb pool state to derive the returned DMA
>> address
>> - reports CC_ATTR_GUEST_MEM_ENCRYPT for arm64 Realms, powerpc secure
>> guests, and s390 protected virtualization guests.
>>
>> Dependency:
>> This series depends on the pKVM changes posted at:
>> https://lore.kernel.org/all/20260603110522.3331819-1-smostafa@google.com
>>
>> Please merge this series only after the pKVM changes above are merged.
>> Otherwise pKVM will be broken.
>>
> A rebased tree on top of the dependent pKVM changes can be found at:
> https://gitlab.arm.com/linux-arm/linux-cca/-/tree/scratch/pkvm/testing?ref_type=heads
>
> The patches had minor conflicts. I am not sure how we want to get this
> merged.
>
> Should we ask the pKVM maintainers for a topic branch, and then I can
> repost the updated series on top of that?
I'm fine with merging on top of the topic branch and I assume that this
patchset is mature enough to give it a try in linux-next, but first I
would like to get a review or at least acks from others with good CC
knowledge or experience.
Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland
^ permalink raw reply
* Re: [PATCH 09/15] PCI, device core: Move "untrusted" concept to DEVICE_TRUST_ADVERSARY
From: Robin Murphy @ 2026-07-07 13:04 UTC (permalink / raw)
To: Dan Williams, linux-coco
Cc: linux-pci, driver-core, ankita, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Will Deacon, Jason Gunthorpe,
Bjorn Helgaas, Alexey Kardashevskiy, Xu Yilun, Aneesh Kumar K.V,
Dexuan Cui
In-Reply-To: <20260705220819.2472765-10-djbw@kernel.org>
On 05/07/2026 11:08 pm, Dan Williams wrote:
> An "adversary" device is one that the kernel is allowed to operate, but
> with extra safety / paranoia added. It aims to keep the device bounded in
> both address space and time (no batched invalidation) for DMA accesses. The
> concept of adversarial devices already exists in the PCI core. The
> categorization is applied to externally attached PCI devices by default
> (like Thunderbolt attached devices). Unlike DEVICE_TRUST_NONE that says "do
> not allow driver bind", DEVICE_TRUST_ADVERSARY says "allow driver bind as
> long as the driver and/or IOMMU are taking precautions." Uplevel this trust
> mechanism from a PCI device "untrusted" boolean flag to the core device
> trust level.
>
> The rationale for dev->bus_trust separate from dev->p->trust is to give
> buses the ability to establish a trust level before device_add(). Then,
> after device_add(), the dependency on 'struct device_private' mandates that
> the device-core own and coordinate changes to the device's trust level.
>
> There are implications of the bus expressing less than full trust in a
> device and a module later expressing higher levels of trust. For PCI, the
> "adversary" / pci_untrusted() determination is made prior to initial
> iommu_probe_device(). The default IOMMU domain determination is latched
> from bus's specified trust value. Later changes to the trust level, for
> example by "modprobe $module trust=auto" skip that IOMMU probing. The
> result is that moving a device to DEVICE_TRUST_AUTO may lift translation
> blocking (PCI_ACS_TB), but not change the default IOMMU domain from
> IOMMU_DOMAIN_DMA.
>
> The default domain can be validated / changed via iommu_group sysfs. The
> longer term goal is to dynamically adjust iommu domain at the next
> $bus_dma_configure() event, but that needs more surgery and consideration
> for multi-device iommu groups.
>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Cc: "Rafael J. Wysocki" <rafael@kernel.org>
> Cc: Danilo Krummrich <dakr@kernel.org>
> Cc: Will Deacon <will@kernel.org>
> Cc: Robin Murphy <robin.murphy@arm.com>
> Cc: Jason Gunthorpe <jgg@ziepe.ca>
> Cc: Bjorn Helgaas <bhelgaas@google.com>
> Cc: Alexey Kardashevskiy <aik@amd.com>
> Cc: Xu Yilun <yilun.xu@linux.intel.com>
> Cc: "Aneesh Kumar K.V" <aneesh.kumar@kernel.org>
> Cc: Dexuan Cui <decui@microsoft.com>
> Signed-off-by: Dan Williams <djbw@kernel.org>
> ---
> drivers/base/Kconfig | 13 +++++++++++++
> drivers/pci/Kconfig | 1 +
> include/linux/device.h | 3 +++
> include/linux/device/trust.h | 20 +++++++++++++++-----
> include/linux/pci.h | 13 +++++--------
> drivers/base/trust.c | 20 +++++++++++++++++++-
> drivers/iommu/amd/iommu.c | 2 +-
> drivers/iommu/dma-iommu.c | 13 ++++---------
> drivers/iommu/intel/iommu.c | 2 +-
> drivers/iommu/iommu.c | 2 +-
For the straightforward drivers/iommu changes:
Acked-by: Robin Murphy <robin.murphy@arm.com>
I don't have time to have a strong opinion about the internal
implementation details, but I agree a driver-core-level abstraction of
hierarchical bus/device trust seems like the right step to take in general.
Cheers,
Robin.
> drivers/pci/ats.c | 2 +-
> drivers/pci/pci.c | 2 +-
> drivers/pci/probe.c | 10 +++++++---
> drivers/pci/quirks.c | 4 ++--
> 14 files changed, 74 insertions(+), 33 deletions(-)
>
> diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
> index a08523d348d8..a4233bdf9804 100644
> --- a/drivers/base/Kconfig
> +++ b/drivers/base/Kconfig
> @@ -291,6 +291,14 @@ config DEVICE_TRUST_NONE
> possible, the device is blocked by an IOMMU from accessing
> assets.
>
> +config DEVICE_TRUST_ADVERSARY
> + bool "Adversary"
> + help
> + Device is allowed to bind. Bus, IOMMU, and driver layers may
> + react to this trust level by disabling access validation
> + bypass mechanisms like PCI ATS. When device is unbound from a
> + driver the device is blocked by an IOMMU where possible.
> +
> config DEVICE_TRUST_AUTO
> bool "Auto"
> help
> @@ -317,6 +325,11 @@ config BUILTIN_DEVICE_TRUST_AUTO
> a driver and deploy all available mechanisms to allow performant
> direct memory access This trust level does not grant TCB privileges.
>
> +config BUILTIN_DEVICE_TRUST_ADVERSARY
> + bool "Adversary"
> + help
> + Deploy mitigations in the IOMMU layer and driver to limit access.
> +
> endchoice
>
> endmenu
> diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
> index ed17b5d2d5ae..185b42cefe20 100644
> --- a/drivers/pci/Kconfig
> +++ b/drivers/pci/Kconfig
> @@ -22,6 +22,7 @@ menuconfig PCI
> bool "PCI support"
> depends on HAVE_PCI
> depends on MMU
> + select DEVICE_TRUST
> help
> This option enables support for the PCI local bus, including
> support for PCI-X and the foundations for PCI Express support.
> diff --git a/include/linux/device.h b/include/linux/device.h
> index 7b2baffdd2f5..3e203d573a58 100644
> --- a/include/linux/device.h
> +++ b/include/linux/device.h
> @@ -688,6 +688,8 @@ enum struct_device_flags {
> * @removable: Whether the device can be removed from the system. This
> * should be set by the subsystem / bus driver that discovered
> * the device.
> + * @bus_trust: Device's initial / prior to device_add() trust level.
> + *
> * @flags: DEV_FLAG_XXX flags. Use atomic bitfield operations to modify.
> *
> * At the lowest level, every device in a Linux system is represented by an
> @@ -791,6 +793,7 @@ struct device {
> struct device_physical_location *physical_location;
>
> enum device_removable removable;
> + enum device_trust bus_trust;
>
> DECLARE_BITMAP(flags, DEV_FLAG_COUNT);
> };
> diff --git a/include/linux/device/trust.h b/include/linux/device/trust.h
> index 3377d26dc485..283d3196e5e6 100644
> --- a/include/linux/device/trust.h
> +++ b/include/linux/device/trust.h
> @@ -11,30 +11,40 @@
> *
> * @DEVICE_TRUST_UNSET: Unregistered device object with no current bus
> * @DEVICE_TRUST_NONE: Blocked when idle, cannot bind
> + * @DEVICE_TRUST_ADVERSARY: Blocked when idle, constrained when active.
> * @DEVICE_TRUST_AUTO: All typical privileges granted
> + *
> + * Devices flagged as adversarial are the ones that can potentially
> + * execute DMA attacks and similar. They are typically connected through
> + * external ports such as Thunderbolt but not limited to that. When an
> + * IOMMU is enabled they should be getting full mappings to make sure
> + * they cannot access arbitrary memory.
> */
> enum device_trust {
> DEVICE_TRUST_UNSET,
> DEVICE_TRUST_NONE,
> + DEVICE_TRUST_ADVERSARY,
> DEVICE_TRUST_AUTO,
> };
>
> -#define DEVICE_DEFAULT_TRUST \
> - (IS_ENABLED(CONFIG_DEVICE_TRUST_NONE) ? DEVICE_TRUST_NONE : \
> - DEVICE_TRUST_AUTO)
> +#define DEVICE_DEFAULT_TRUST \
> + (IS_ENABLED(CONFIG_DEVICE_TRUST_NONE) ? DEVICE_TRUST_NONE : \
> + IS_ENABLED(CONFIG_DEVICE_TRUST_ADVERSARY) ? DEVICE_TRUST_ADVERSARY : \
> + DEVICE_TRUST_AUTO)
>
> struct device;
> struct device_driver;
>
> #ifdef CONFIG_DEVICE_TRUST
> +bool device_untrusted(struct device *dev);
> void module_driver_trust(struct module *mod, const char *val);
> -void module_driver_trust_init(struct module *mod, bool distrust);
> +void module_driver_trust_init(struct module *mod, bool require_trust);
> #else
> static inline void module_driver_trust(struct module *mod, const char *val)
> {
> pr_warn("module: %s: trust= support disabled\n", mod->name);
> }
> -static inline void module_driver_trust_init(struct module *mod, bool distrust)
> +static inline void module_driver_trust_init(struct module *mod, bool require_trust)
> {
> }
> #endif
> diff --git a/include/linux/pci.h b/include/linux/pci.h
> index ebb5b9d76360..095ea37fc6d0 100644
> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -485,14 +485,6 @@ struct pci_dev {
> unsigned int shpc_managed:1; /* SHPC owned by shpchp */
> unsigned int is_thunderbolt:1; /* Thunderbolt controller */
> unsigned int is_cxl:1; /* Compute Express Link (CXL) */
> - /*
> - * Devices marked being untrusted are the ones that can potentially
> - * execute DMA attacks and similar. They are typically connected
> - * through external ports such as Thunderbolt but not limited to
> - * that. When an IOMMU is enabled they should be getting full
> - * mappings to make sure they cannot access arbitrary memory.
> - */
> - unsigned int untrusted:1;
> /*
> * Info from the platform, e.g., ACPI or device tree, may mark a
> * device as "external-facing". An external-facing device is
> @@ -812,6 +804,11 @@ static inline bool pcie_is_cxl(struct pci_dev *pci_dev)
> return pci_dev->is_cxl;
> }
>
> +static inline bool pci_untrusted(struct pci_dev *pdev)
> +{
> + return device_untrusted(&pdev->dev);
> +}
> +
> #define for_each_pci_bridge(dev, bus) \
> list_for_each_entry(dev, &bus->devices, bus_list) \
> if (!pci_is_bridge(dev)) {} else
> diff --git a/drivers/base/trust.c b/drivers/base/trust.c
> index 0fd494e1557d..8efbe5c51250 100644
> --- a/drivers/base/trust.c
> +++ b/drivers/base/trust.c
> @@ -6,15 +6,28 @@
> #include <linux/module.h>
> #include "base.h"
>
> +/* If the bus did not initialize trust, set a default */
> void device_initialize_trust(struct device *dev)
> {
> + dev->p->trust = dev->bus_trust;
> if (dev->p->trust == DEVICE_TRUST_UNSET)
> dev->p->trust = DEVICE_DEFAULT_TRUST;
> }
>
> +/*
> + * ->bus_trust is evaluated / manipulated prior to device_add() and
> + * synced with dev->p->trust post device_add() under device_lock().
> + */
> +bool device_untrusted(struct device *dev)
> +{
> + return dev->bus_trust && dev->bus_trust <= DEVICE_TRUST_ADVERSARY;
> +}
> +
> /* Driver trust policy requires modules, builtin drivers always attach */
> static enum device_trust builtin_driver_trust(void)
> {
> + if (IS_ENABLED(CONFIG_BUILTIN_DEVICE_TRUST_ADVERSARY))
> + return DEVICE_TRUST_ADVERSARY;
> return DEVICE_TRUST_AUTO;
> }
>
> @@ -30,18 +43,23 @@ static enum device_trust driver_trust(struct module *mod)
> * policy on trusting devices it attaches, update the device's trust
> * level from that policy. Trust privileges beyond driver bind are
> * realized in a bus's ->dma_configure().
> + *
> + * Reflect the operational trust level back to the public indicator.
> */
> bool device_trust_bind(const struct device_driver *drv, struct device *dev)
> {
> enum device_trust drv_trust = driver_trust(drv->owner);
>
> - if (drv_trust != DEVICE_TRUST_UNSET)
> + if (drv_trust != DEVICE_TRUST_UNSET) {
> dev->p->trust = drv_trust;
> + dev->bus_trust = drv_trust;
> + }
> return dev->p->trust > DEVICE_TRUST_NONE;
> }
>
> static const char * const device_trust_names[] = {
> [DEVICE_TRUST_NONE] = "none",
> + [DEVICE_TRUST_ADVERSARY] = "adversary",
> [DEVICE_TRUST_AUTO] = "auto",
> };
>
> diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
> index 563f9c2672d5..ef663e3efc70 100644
> --- a/drivers/iommu/amd/iommu.c
> +++ b/drivers/iommu/amd/iommu.c
> @@ -3121,7 +3121,7 @@ static int amd_iommu_def_domain_type(struct device *dev)
> return 0;
>
> /* Always use DMA domain for untrusted device */
> - if (dev_is_pci(dev) && to_pci_dev(dev)->untrusted)
> + if (device_untrusted(dev))
> return IOMMU_DOMAIN_DMA;
>
> /*
> diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
> index 9abaec0703ef..957ef77911a9 100644
> --- a/drivers/iommu/dma-iommu.c
> +++ b/drivers/iommu/dma-iommu.c
> @@ -588,16 +588,11 @@ static int iova_reserve_iommu_regions(struct device *dev,
> return ret;
> }
>
> -static bool dev_is_untrusted(struct device *dev)
> -{
> - return dev_is_pci(dev) && to_pci_dev(dev)->untrusted;
> -}
> -
> static bool dev_use_swiotlb(struct device *dev, size_t size,
> enum dma_data_direction dir)
> {
> return IS_ENABLED(CONFIG_SWIOTLB) &&
> - (dev_is_untrusted(dev) ||
> + (device_untrusted(dev) ||
> dma_kmalloc_needs_bounce(dev, size, dir));
> }
>
> @@ -610,7 +605,7 @@ static bool dev_use_sg_swiotlb(struct device *dev, struct scatterlist *sg,
> if (!IS_ENABLED(CONFIG_SWIOTLB))
> return false;
>
> - if (dev_is_untrusted(dev))
> + if (device_untrusted(dev))
> return true;
>
> /*
> @@ -1188,7 +1183,7 @@ static phys_addr_t iommu_dma_map_swiotlb(struct device *dev, phys_addr_t phys,
> * swiotlb_tbl_map_single() has initialized the bounce buffer proper to
> * the contents of the original memory buffer.
> */
> - if (phys != (phys_addr_t)DMA_MAPPING_ERROR && dev_is_untrusted(dev)) {
> + if (phys != (phys_addr_t)DMA_MAPPING_ERROR && device_untrusted(dev)) {
> size_t start, virt = (size_t)phys_to_virt(phys);
>
> /* Pre-padding */
> @@ -1761,7 +1756,7 @@ size_t iommu_dma_opt_mapping_size(void)
>
> size_t iommu_dma_max_mapping_size(struct device *dev)
> {
> - if (dev_is_untrusted(dev))
> + if (device_untrusted(dev))
> return swiotlb_max_mapping_size(dev);
>
> return SIZE_MAX;
> diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
> index 849d06dfe1ae..82d9abe87d99 100644
> --- a/drivers/iommu/intel/iommu.c
> +++ b/drivers/iommu/intel/iommu.c
> @@ -3501,7 +3501,7 @@ static bool intel_iommu_is_attach_deferred(struct device *dev)
> */
> static bool risky_device(struct pci_dev *pdev)
> {
> - if (pdev->untrusted) {
> + if (pci_untrusted(pdev)) {
> pci_info(pdev,
> "Skipping IOMMU quirk for dev [%04X:%04X] on untrusted PCI link\n",
> pdev->vendor, pdev->device);
> diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
> index e8f13dcebbde..4769d2b548d6 100644
> --- a/drivers/iommu/iommu.c
> +++ b/drivers/iommu/iommu.c
> @@ -1901,7 +1901,7 @@ static int iommu_get_default_domain_type(struct iommu_group *group,
> driver_type = iommu_get_def_domain_type(group, gdev->dev,
> driver_type);
>
> - if (dev_is_pci(gdev->dev) && to_pci_dev(gdev->dev)->untrusted) {
> + if (device_untrusted(gdev->dev)) {
> /*
> * No ARM32 using systems will set untrusted, it cannot
> * work.
> diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c
> index 96efa00d9743..7296f0ce7530 100644
> --- a/drivers/pci/ats.c
> +++ b/drivers/pci/ats.c
> @@ -43,7 +43,7 @@ bool pci_ats_supported(struct pci_dev *dev)
> if (!dev->ats_cap)
> return false;
>
> - return (dev->untrusted == 0);
> + return !pci_untrusted(dev);
> }
> EXPORT_SYMBOL_GPL(pci_ats_supported);
>
> diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> index 77b17b13ee61..ff1ca74f30b1 100644
> --- a/drivers/pci/pci.c
> +++ b/drivers/pci/pci.c
> @@ -994,7 +994,7 @@ static void pci_std_enable_acs(struct pci_dev *dev, struct pci_acs *caps)
> caps->ctrl |= (dev->acs_capabilities & PCI_ACS_UF);
>
> /* Enable Translation Blocking for external devices and noats */
> - if (pci_ats_disabled() || dev->external_facing || dev->untrusted)
> + if (pci_ats_disabled() || dev->external_facing || pci_untrusted(dev))
> caps->ctrl |= (dev->acs_capabilities & PCI_ACS_TB);
> }
>
> diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
> index dd0abbc63e18..91a7bbcfdecb 100644
> --- a/drivers/pci/probe.c
> +++ b/drivers/pci/probe.c
> @@ -6,6 +6,7 @@
> #include <linux/array_size.h>
> #include <linux/kernel.h>
> #include <linux/delay.h>
> +#include <linux/device/trust.h>
> #include <linux/init.h>
> #include <linux/pci.h>
> #include <linux/msi.h>
> @@ -1737,20 +1738,23 @@ static void set_pcie_untrusted(struct pci_dev *dev)
> {
> struct pci_dev *parent = pci_upstream_bridge(dev);
>
> + dev->dev.bus_trust = DEVICE_DEFAULT_TRUST;
> if (!parent)
> return;
> /*
> * If the upstream bridge is untrusted we treat this device as
> * untrusted as well.
> */
> - if (parent->untrusted) {
> - dev->untrusted = true;
> + if (pci_untrusted(parent)) {
> + dev->dev.bus_trust =
> + min(dev->dev.bus_trust, parent->dev.bus_trust);
> return;
> }
>
> if (arch_pci_dev_is_removable(dev)) {
> pci_dbg(dev, "marking as untrusted\n");
> - dev->untrusted = true;
> + dev->dev.bus_trust =
> + min(dev->dev.bus_trust, DEVICE_TRUST_ADVERSARY);
> }
> }
>
> diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
> index b09f27f7846f..73e473856999 100644
> --- a/drivers/pci/quirks.c
> +++ b/drivers/pci/quirks.c
> @@ -5370,7 +5370,7 @@ static void pci_quirk_enable_intel_rp_mpc_acs(struct pci_dev *dev)
> * PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF
> *
> * TODO: This quirk also needs to do equivalent of PCI_ACS_TB,
> - * if dev->external_facing || dev->untrusted
> + * if dev->external_facing || pci_untrusted(dev)
> */
> static int pci_quirk_enable_intel_pch_acs(struct pci_dev *dev)
> {
> @@ -5411,7 +5411,7 @@ static int pci_quirk_enable_intel_spt_pch_acs(struct pci_dev *dev)
> ctrl |= (cap & PCI_ACS_CR);
> ctrl |= (cap & PCI_ACS_UF);
>
> - if (pci_ats_disabled() || dev->external_facing || dev->untrusted)
> + if (pci_ats_disabled() || dev->external_facing || pci_untrusted(dev))
> ctrl |= (cap & PCI_ACS_TB);
>
> pci_write_config_dword(dev, pos + INTEL_SPT_ACS_CTRL, ctrl);
^ permalink raw reply
* Re: [PATCH v5 2/3] x86/insn-eval: Add insn_assign_reg() helper
From: Sean Christopherson @ 2026-07-07 14:46 UTC (permalink / raw)
To: Kiryl Shutsemau
Cc: David Laight, Dave Hansen, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, x86, Paolo Bonzini, Kuppuswamy Sathyanarayanan,
Kai Huang, Xiaoyao Li, Rick Edgecombe, Binbin Wu, Andi Kleen,
Dan Williams, Borys Tsyrulnikov, kvm, linux-coco, linux-kernel,
stable
In-Reply-To: <akaCzNRGVy5Xr-bG@thinkstation>
On Thu, Jul 02, 2026, Kiryl Shutsemau wrote:
> On Wed, Jul 01, 2026 at 06:00:33PM +0100, David Laight wrote:
> > Or be even more specific and use '& 0xffffffff' rather than a cast.
> > Particularly since the casts of the RHS in the byte/short cases aren't
> > needed at all.
>
> I'd rather keep the body exactly as KVM has it today.
+1. My main argument for casting in the 1-byte and 2-byte cases is consistency
above all else, using a mask for the 4-byte case defeats that goal.
> This is now a straight move + rename with no functional change, and the v4
> attempt to rewrite it with arithmetic is precisely what introduced the
> AH/CH/DH/BH clobber Sashiko flagged. Tidying the casts turns it back into a
> rewrite and diverges from the form KVM has shipped for years. Feel free to
> submit a separate cleanup on top if you feel strongly.
>
> Updated patch below; I'll fold it into v6.
>
> -- >8 --
> Subject: [PATCH] x86/insn-eval: Move assign_register() out of KVM as insn_assign_reg()
>
> KVM's instruction emulator has a small helper, assign_register(), that
> writes a value into a register following the x86 rules for writes to
> general-purpose registers: an 8- or 16-bit write leaves the rest of the
> register untouched, a 32-bit write zero-extends the result to 64 bits,
> and a 64-bit write replaces the whole register.
>
> The TDX guest #VE handler needs the same logic for port I/O emulation
> to get 32-bit zero-extension right. Rather than add a third copy of
> the same switch, move the helper verbatim to <asm/insn-eval.h>, rename
> it to insn_assign_reg(), and route KVM's callers through it.
>
> Add <asm/insn.h> to the header's includes so it builds standalone in
> callers that have not pulled it in transitively.
>
> No functional change.
>
> Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
> Cc: stable@vger.kernel.org # prerequisite for the following 32-bit port I/O zero-extension fix
> ---
Acked-by: Sean Christopherson <seanjc@google.com>
^ permalink raw reply
* Re: [PATCH v7 08/22] coco: arm64: s390: powerpc: Mark secure guests with CC_ATTR_GUEST_MEM_ENCRYPT
From: Suzuki K Poulose @ 2026-07-07 15:41 UTC (permalink / raw)
To: Aneesh Kumar K.V (Arm), iommu, linux-arm-kernel, linux-kernel,
linux-coco
Cc: Robin Murphy, Marek Szyprowski, Will Deacon, Marc Zyngier,
Steven Price, Catalin Marinas, Jiri Pirko, Jason Gunthorpe,
Mostafa Saleh, Petr Tesarik, Alexey Kardashevskiy, Dan Williams,
Xu Yilun, linuxppc-dev, linux-s390, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Christophe Leroy (CS GROUP),
Alexander Gordeev, Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <20260701054926.825925-9-aneesh.kumar@kernel.org>
On 01/07/2026 06:49, Aneesh Kumar K.V (Arm) wrote:
> arm64 Realms, powerpc secure guests and s390 protected virtualization
> guests currently report CC_ATTR_MEM_ENCRYPT, but not
> CC_ATTR_GUEST_MEM_ENCRYPT.
>
> These environments are all secure guest configurations. Their
> force_dma_unencrypted() implementations also treat the same secure guest
> state as requiring unencrypted DMA. Report CC_ATTR_GUEST_MEM_ENCRYPT for
> the same condition so generic confidential-computing code can distinguish
> guest memory encryption from host memory encryption instead of relying only
> on CC_ATTR_MEM_ENCRYPT.
>
> Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
For arm64 bits:
> ---
> arch/arm64/kernel/rsi.c | 1 +
> arch/powerpc/platforms/pseries/cc_platform.c | 1 +
> arch/s390/mm/init.c | 1 +
> 3 files changed, 3 insertions(+)
>
> diff --git a/arch/arm64/kernel/rsi.c b/arch/arm64/kernel/rsi.c
> index 92160f2e57ff..207e36db1e7a 100644
> --- a/arch/arm64/kernel/rsi.c
> +++ b/arch/arm64/kernel/rsi.c
> @@ -27,6 +27,7 @@ bool cc_platform_has(enum cc_attr attr)
> {
> switch (attr) {
> case CC_ATTR_MEM_ENCRYPT:
> + case CC_ATTR_GUEST_MEM_ENCRYPT:
> return is_realm_world();
> default:
> return false;
Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Suzuki
^ permalink raw reply
* Re: [PATCH] x86/virt/tdx: Formalize SEAMCALL version encoding support
From: Xu Yilun @ 2026-07-07 15:45 UTC (permalink / raw)
To: Xiaoyao Li
Cc: x86, linux-kernel, kas, rick.p.edgecombe, dave.hansen,
dave.hansen, yilun.xu, chao.gao, djbw, linux-coco, peter.fang
In-Reply-To: <ede965a3-0fd6-46b4-906d-716bce163036@intel.com>
On Fri, Jul 03, 2026 at 08:00:38AM +0800, Xiaoyao Li wrote:
> On 7/2/2026 10:46 PM, Xu Yilun wrote:
> > +/*
> > + * SEAMCALL leaf:
> > + *
> > + * Bit 15:0 Leaf number
> > + * Bit 23:16 Version number
> > + */
> > +#define SEAMCALL_VERSION_MASK GENMASK_U64(23, 16)
> > +
> > +static __always_inline u64 __seamcall_encode_fn(sc_func_t func, u64 fn,
> > + struct tdx_module_args *args)
> > +{
> > + FIELD_MODIFY(SEAMCALL_VERSION_MASK, &fn, args->version);
> > +
> > + return func(fn, args);
> > +}
> > +
> > static __always_inline u64 __seamcall_dirty_cache(sc_func_t func, u64 fn,
> > struct tdx_module_args *args)
> > {
> > @@ -39,7 +56,7 @@ static __always_inline u64 __seamcall_dirty_cache(sc_func_t func, u64 fn,
> > */
> > this_cpu_write(cache_state_incoherent, true);
> > - return func(fn, args);
> > + return __seamcall_encode_fn(func, fn, args);
> > }
>
> Can we drop the new wrapper? I don't see why we need it. The wrapper makes
> the code harder to read.
OK, will call FIELD_MODIFY() directly in __seamcall_dirty_cache()
>
>
^ permalink raw reply
* Re: [PATCH v6 00/11] Dynamic PAMT
From: Sean Christopherson @ 2026-07-07 16:17 UTC (permalink / raw)
To: Rick P Edgecombe
Cc: kvm@vger.kernel.org, linux-coco@lists.linux.dev, Kai Huang,
Dave Hansen, Yan Y Zhao, kas@kernel.org, mingo@redhat.com,
linux-kernel@vger.kernel.org, pbonzini@redhat.com,
nik.borisov@suse.com, linux-doc@vger.kernel.org, hpa@zytor.com,
tglx@kernel.org, Vishal Annapurve, bp@alien8.de, Chao Gao,
x86@kernel.org
In-Reply-To: <f2b2a3a048ad6429305e0e21319a8c27bedf39db.camel@intel.com>
On Mon, Jul 06, 2026, Rick P Edgecombe wrote:
> Sean,
>
> On Mon, 2026-05-25 at 19:35 -0700, Rick Edgecombe wrote:
> > KVM: TDX: Allocate PAMT memory for TD and vCPU control structures
> > KVM: TDX: Get/put PAMT pages when (un)mapping private memory
>
> Would you be willing to take a look at these two patches that need ack's from
> the KVM side? I'm hoping to make the next version of this the last one. So it
> would be great to get any remaining comments before then.
LGTM beyond the to_tdx() and Assited-by nits.
^ permalink raw reply
* Re: [PATCH] x86/virt/tdx: Formalize SEAMCALL version encoding support
From: Xu Yilun @ 2026-07-07 16:18 UTC (permalink / raw)
To: Edgecombe, Rick P
Cc: linux-kernel@vger.kernel.org, x86@kernel.org, Gao, Chao,
Xu, Yilun, Hansen, Dave, dave.hansen@linux.intel.com,
kas@kernel.org, djbw@kernel.org, Fang, Peter,
linux-coco@lists.linux.dev
In-Reply-To: <7d3eba7a5442cf3e84fe3658ae53337a183290b1.camel@intel.com>
On Mon, Jul 06, 2026 at 09:44:29PM +0000, Edgecombe, Rick P wrote:
> On Thu, 2026-07-02 at 22:46 +0800, Xu Yilun wrote:
> > TDX uses the SEAMCALL instruction to invoke various TDX module
> > functions. Just like the syscall, a SEAMCALL specifies the operation
> > using a function number and parameters. Moreover, TDX also uses SEAMCALL
> > versions to extend the functionalities of existing SEAMCALLs while
> > keeping backward compatibility. Unlike syscall versions that assign
> > brand new numbers, TDX segments the function number into a basic
> > function number field and a version field. Together, they encode the new
> > function number.
>
> So isn't this pretty much like numbered seamcalls, except there is a special
> format for generating seamcall2, seamcall3, etc? In the end you just use a
> different number for a different version of the call. So it's just like
> syscalls, except there is a pattern in the specific number for calls of the same
> family.
OK. I will refactored this paragraph:
SEAMCALL invokes TDX module functions using a function number and
parameters. To extend the functionalities of existing SEAMCALLs while
keeping backward compatibility, TDX adds more numbered SEAMCALLs of the
same family. This is just like syscalls, except that TDX defines a
specific function number encoding pattern for the same family: a base
function number and a version together encode the full function number.
>
> >
> > An existing SEAMCALL (TDH.VP.INIT) helper is already using the version
> ^ maybe swap the order of these two?
> > field. However, having the caller pack the version into the function
> ^Can drop this?
> > number open-codes the ABI layout, making the SEAMCALL helper definition
> > obscure and error prone.
>
> Do we need the second part of the sentence?
>
> >
> > Add a version field in struct tdx_module_args, so that most existing
> > SEAMCALL helpers get a default "version == 0" behavior without code
> > churn, while callers requiring extended functionalities can specify the
> > version descriptively.
> >
> > As an internal implementation detail,
>
> ^ Is this needed to make it clear?
I think these are also about "make every word count", I'll drop all
3 places above.
>
> > encode the
> > tdx_module_args.version in the function number before calling into
> > assembly code.
> >
> > Two alternative schemes were considered:
> >
> > 1. Define versioned macros like TDH_VP_INIT_V0, TDH_VP_INIT_V1, etc.
> > However, this breaks naming consistency unless all existing stable
> > function macros are changed to TDH_XXX_V0.
> >
> > 2. Add an explicit 'version' parameter to the base seamcall() API. This
> > unnecessarily forces all stable SEAMCALL helpers to pass a
> > meaningless '0' argument. Additionally, the magic '0' or '1' values
> > at caller sites are not descriptive.
> >
>
> Dave was recently saying something to the effect of "make every word count". I
> think we could lose some filler words.
OK. I'll delete these adverbs.
>
> > Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
>
> How about a link to the thread where this was suggested.
>
> > ---
> > arch/x86/include/asm/shared/tdx.h | 2 ++
> > arch/x86/virt/vmx/tdx/seamcall_internal.h | 19 ++++++++++++++++++-
> > arch/x86/virt/vmx/tdx/tdx.h | 8 --------
> > arch/x86/virt/vmx/tdx/tdx.c | 5 +++--
> > 4 files changed, 23 insertions(+), 11 deletions(-)
> >
> > diff --git a/arch/x86/include/asm/shared/tdx.h b/arch/x86/include/asm/shared/tdx.h
> > index f20e91d7ac35..b9aac2de233a 100644
> > --- a/arch/x86/include/asm/shared/tdx.h
> > +++ b/arch/x86/include/asm/shared/tdx.h
> > @@ -143,6 +143,8 @@ struct tdx_module_args {
> > u64 rbx;
> > u64 rdi;
> > u64 rsi;
> > + /* ABI version, encoded in rax */
> > + u8 version;
> > };
> >
> > /* Used to communicate with the TDX module */
> > diff --git a/arch/x86/virt/vmx/tdx/seamcall_internal.h b/arch/x86/virt/vmx/tdx/seamcall_internal.h
> > index be5f446467df..7002e41cddad 100644
> > --- a/arch/x86/virt/vmx/tdx/seamcall_internal.h
> > +++ b/arch/x86/virt/vmx/tdx/seamcall_internal.h
> > @@ -11,6 +11,7 @@
> > #ifndef _X86_VIRT_SEAMCALL_INTERNAL_H
> > #define _X86_VIRT_SEAMCALL_INTERNAL_H
> >
> > +#include <linux/bitfield.h>
> > #include <linux/printk.h>
> > #include <linux/types.h>
> > #include <asm/archrandom.h>
> > @@ -23,6 +24,22 @@ u64 __seamcall_saved_ret(u64 fn, struct tdx_module_args *args);
> >
> > typedef u64 (*sc_func_t)(u64 fn, struct tdx_module_args *args);
> >
> > +/*
> > + * SEAMCALL leaf:
> > + *
> > + * Bit 15:0 Leaf number
> > + * Bit 23:16 Version number
> > + */
> > +#define SEAMCALL_VERSION_MASK GENMASK_U64(23, 16)
>
> The annoying thing is that the path touched here is also used for seamldr calls
> now, which afaict has no concept of version. Underscoring how much of a mess the
> wrapper stack is.
OK, I'll drop the wrapper.
>
> > +
> > +static __always_inline u64 __seamcall_encode_fn(sc_func_t func, u64 fn,
> > + struct tdx_module_args *args)
> > +{
>
> I have the same question as Xiaoyao. We have so many wrappers already.
>
> > + FIELD_MODIFY(SEAMCALL_VERSION_MASK, &fn, args->version);
> > +
> > + return func(fn, args);
> > +}
> > +
> > static __always_inline u64 __seamcall_dirty_cache(sc_func_t func, u64 fn,
> > struct tdx_module_args *args)
> > {
> > @@ -39,7 +56,7 @@ static __always_inline u64 __seamcall_dirty_cache(sc_func_t func, u64 fn,
> > */
> > this_cpu_write(cache_state_incoherent, true);
> >
> > - return func(fn, args);
> > + return __seamcall_encode_fn(func, fn, args);
> > }
^ permalink raw reply
* SVSM Development Call July 8th, 2026
From: Jörg Rödel @ 2026-07-07 16:19 UTC (permalink / raw)
To: coconut-svsm, linux-coco
Hi,
Here is the call for agenda items for this weeks SVSM development call. Please
send any agenda items you have in mind as a reply to this email or raise them
in the meeting.
We will use the LF Zoom instance. Details of the meeting can be found in our
governance repository at:
https://github.com/coconut-svsm/governance
The link to the COCONUT-SVSM calendar is:
https://zoom-lfx.platform.linuxfoundation.org/meetings/coconut-svsm?view=week
The meeting will be recorded and the recording eventually published.
Regards,
Jörg
^ permalink raw reply
* Re: [PATCH v7 08/22] coco: arm64: s390: powerpc: Mark secure guests with CC_ATTR_GUEST_MEM_ENCRYPT
From: Catalin Marinas @ 2026-07-07 16:58 UTC (permalink / raw)
To: Aneesh Kumar K.V (Arm)
Cc: iommu, linux-arm-kernel, linux-kernel, linux-coco, Robin Murphy,
Marek Szyprowski, Will Deacon, Marc Zyngier, Steven Price,
Suzuki K Poulose, Jiri Pirko, Jason Gunthorpe, Mostafa Saleh,
Petr Tesarik, Alexey Kardashevskiy, Dan Williams, Xu Yilun,
linuxppc-dev, linux-s390, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <20260701054926.825925-9-aneesh.kumar@kernel.org>
On Wed, Jul 01, 2026 at 11:19:12AM +0530, Aneesh Kumar K.V (Arm) wrote:
> arm64 Realms, powerpc secure guests and s390 protected virtualization
> guests currently report CC_ATTR_MEM_ENCRYPT, but not
> CC_ATTR_GUEST_MEM_ENCRYPT.
>
> These environments are all secure guest configurations. Their
> force_dma_unencrypted() implementations also treat the same secure guest
> state as requiring unencrypted DMA. Report CC_ATTR_GUEST_MEM_ENCRYPT for
> the same condition so generic confidential-computing code can distinguish
> guest memory encryption from host memory encryption instead of relying only
> on CC_ATTR_MEM_ENCRYPT.
>
> Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
For arm64:
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
^ permalink raw reply
* Re: [PATCH v6 00/11] Dynamic PAMT
From: Edgecombe, Rick P @ 2026-07-07 17:25 UTC (permalink / raw)
To: seanjc@google.com
Cc: kvm@vger.kernel.org, linux-coco@lists.linux.dev, Huang, Kai,
Hansen, Dave, Zhao, Yan Y, kas@kernel.org, mingo@redhat.com,
linux-kernel@vger.kernel.org, pbonzini@redhat.com,
nik.borisov@suse.com, linux-doc@vger.kernel.org, hpa@zytor.com,
tglx@kernel.org, Annapurve, Vishal, bp@alien8.de, Gao, Chao,
x86@kernel.org
In-Reply-To: <ak0mnZKoxK5Y04Dw@google.com>
On Tue, 2026-07-07 at 09:17 -0700, Sean Christopherson wrote:
> On Mon, Jul 06, 2026, Rick P Edgecombe wrote:
> > Sean,
> >
> > On Mon, 2026-05-25 at 19:35 -0700, Rick Edgecombe wrote:
> > > KVM: TDX: Allocate PAMT memory for TD and vCPU control structures
> > > KVM: TDX: Get/put PAMT pages when (un)mapping private memory
> >
> > Would you be willing to take a look at these two patches that need ack's from
> > the KVM side? I'm hoping to make the next version of this the last one. So it
> > would be great to get any remaining comments before then.
>
> LGTM beyond the to_tdx() and Assited-by nits.
Thanks Sean!
^ permalink raw reply
* Re: [PATCH v6 01/11] x86/virt/tdx: Simplify tdmr_get_pamt_sz()
From: Edgecombe, Rick P @ 2026-07-07 19:08 UTC (permalink / raw)
To: Zhao, Yan Y
Cc: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev,
Huang, Kai, kvm@vger.kernel.org, Hansen, Dave, kas@kernel.org,
seanjc@google.com, mingo@redhat.com, pbonzini@redhat.com,
binbin.wu@linux.intel.com, nik.borisov@suse.com,
linux-doc@vger.kernel.org, hpa@zytor.com, Annapurve, Vishal,
tglx@kernel.org, Gao, Chao, bp@alien8.de, x86@kernel.org
In-Reply-To: <akxxWF5nMbFQWlD3@yzhao56-desk.sh.intel.com>
On Tue, 2026-07-07 at 11:24 +0800, Yan Zhao wrote:
> On Tue, Jul 07, 2026 at 04:18:25AM +0800, Edgecombe, Rick P wrote:
> > On Fri, 2026-07-03 at 13:48 +0800, Chao Gao wrote:
> > > On Mon, May 25, 2026 at 07:35:05PM -0700, Rick Edgecombe wrote:
> > > > Since the loop that iterates over it is gone, further simplify the code by
> Nit:
> The patch is titled "Simplify tdmr_get_pamt_sz()" but the log mainly talks about
> removing the loop in tdmr_set_up_pamt().
> So, how about renaming the patch to "Simplify setting up pamt calculation for
> TDMRs"?
Hmm, yea. I'll change it to:
x86/virt/tdx: Simplify PAMT layout calculation
>
> > > > dropping the array of intermediate size and base storage. Just store the
> > > > values to their final locations.
> > >
> > > > Accept the small complication of having
> > > > to clear tdmr->pamt_4k_base in the error path, so that tdmr_do_pamt_func()
> > > > will not try to operate on the TDMR struct when attempting to free it.
> > >
> > > The clearing of tdmr->pamt_4k_base was dropped, so this section is a bit
> > > stale. Apart from this nit,
> >
> > Oh, good point.
>
> With those nits fixed,
>
> Reviewed-by: Yan Zhao <yan.y.zhao@intel.com>
Thanks.
^ permalink raw reply
* Re: [PATCH v6 02/11] x86/virt/tdx: Allocate page bitmap for Dynamic PAMT
From: Edgecombe, Rick P @ 2026-07-07 21:25 UTC (permalink / raw)
To: Zhao, Yan Y
Cc: kvm@vger.kernel.org, linux-coco@lists.linux.dev, Huang, Kai,
Hansen, Dave, kirill.shutemov@linux.intel.com, kas@kernel.org,
seanjc@google.com, mingo@redhat.com, linux-kernel@vger.kernel.org,
pbonzini@redhat.com, nik.borisov@suse.com,
linux-doc@vger.kernel.org, hpa@zytor.com, tglx@kernel.org,
Annapurve, Vishal, bp@alien8.de, Gao, Chao,
binbin.wu@linux.intel.com, x86@kernel.org
In-Reply-To: <akx5pdOgoaGzsw/u@yzhao56-desk.sh.intel.com>
On Tue, 2026-07-07 at 11:59 +0800, Yan Zhao wrote:
> On Mon, May 25, 2026 at 07:35:06PM -0700, Rick Edgecombe wrote:
> > +static inline bool tdx_supports_dynamic_pamt(const struct tdx_sys_info *sysinfo)
> > +{
> > + return false; /* To be enabled when kernel is ready */
> > +}
> > +
> Nit:
> Would the following style be better, though the comment will soon be removed?
>
> static inline bool tdx_supports_dynamic_pamt(const struct tdx_sys_info *sysinfo)
> {
> /* To be enabled when kernel is ready */
> return false;
> }
>
> Reviewed-by: Yan Zhao <yan.y.zhao@intel.com>
Sure.
^ permalink raw reply
* Re: [PATCH v6 04/11] x86/virt/tdx: Allocate ref counts for Dynamic PAMT memory
From: Edgecombe, Rick P @ 2026-07-07 22:32 UTC (permalink / raw)
To: Zhao, Yan Y
Cc: kvm@vger.kernel.org, linux-coco@lists.linux.dev, Huang, Kai,
Hansen, Dave, kirill.shutemov@linux.intel.com, kas@kernel.org,
seanjc@google.com, mingo@redhat.com, linux-kernel@vger.kernel.org,
pbonzini@redhat.com, nik.borisov@suse.com,
linux-doc@vger.kernel.org, hpa@zytor.com, tglx@kernel.org,
Annapurve, Vishal, bp@alien8.de, Gao, Chao, x86@kernel.org
In-Reply-To: <akyGVOzkHBkST4hK@yzhao56-desk.sh.intel.com>
On Tue, 2026-07-07 at 12:53 +0800, Yan Zhao wrote:
> On Mon, May 25, 2026 at 07:35:08PM -0700, Rick Edgecombe wrote:
> > From: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>
> >
> > The PAMT memory holds metadata for all possible TDX protected memory. Each
> > physical address range is covered by PAMT entries at three levels (1GB,
> > 2MB, 4KB). With Dynamic PAMT, the 4KB range of PAMT is allocated on
> ^level ?
> > demand. The kernel supplies the TDX module with page pairs to store the
> > 4KB entries, which cover 2MB of host physical memory. The kernel must
> 4KB-level entries ?
Sure.
>
> > provide this page pair before using pages from the range for TDX. If this
> > is not done, SEAMCALLs that give the pages to be protected by the TDX module
> Nit: > 75 chars per line.
Oh yea, must have been a late change.
>
> Reviewed-by: Yan Zhao <yan.y.zhao@intel.com>
^ permalink raw reply
* Re: [PATCH v6 01/11] x86/virt/tdx: Simplify tdmr_get_pamt_sz()
From: Sohil Mehta @ 2026-07-07 23:22 UTC (permalink / raw)
To: Rick Edgecombe, bp, dave.hansen, hpa, kas, kvm, linux-coco,
linux-doc, linux-kernel, mingo, nik.borisov, pbonzini, seanjc,
tglx, vannapurve, x86, chao.gao, yan.y.zhao, kai.huang
Cc: Binbin Wu
In-Reply-To: <20260526023515.288829-2-rick.p.edgecombe@intel.com>
On 5/25/2026 7:35 PM, Rick Edgecombe wrote:
> For each memory region that the TDX module might use (called TDMR), three
> separate traditional PAMT allocations are needed.
> One for each supported page size (1GB, 2MB, 4KB).
Missing verb in this sentence. Maybe use a '-' to merge it with the
previous sentence.
> There are some commonalities in the math needed to calculate the base and
> size for each smaller allocation, and so an effort was made to share logic
> across the three. Unfortunately doing this turned out unnaturally tortured,
> with a loop iterating over the three page sizes, only to call into a
> function with cases statement for each page size.
Did you mean "..with a case statement for each.."?
> diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
> index 967482ae3c801..487f389f52f4b 100644
> --- a/arch/x86/virt/vmx/tdx/tdx.c
> +++ b/arch/x86/virt/vmx/tdx/tdx.c
> @@ -516,31 +516,21 @@ static __init int fill_out_tdmrs(struct list_head *tmb_list,
> * Calculate PAMT size given a TDMR and a page size. The returned
> * PAMT size is always aligned up to 4K page boundary.
> */
> -static __init unsigned long tdmr_get_pamt_sz(struct tdmr_info *tdmr, int pgsz,
> - u16 pamt_entry_size)
> +static __init unsigned long tdmr_get_pamt_sz(struct tdmr_info *tdmr, int pgsz)
> {
> unsigned long pamt_sz, nr_pamt_entries;
> + const int tdx_pg_size_shift[] = { PAGE_SHIFT, PMD_SHIFT, PUD_SHIFT };
Both of these consts go hand-in-hand, right? I would write it as
tdx_pg_size_shift[TDX_PS_NR] to make the connection obvious.
Just curious, why is TDX_PS_NR defined as (TDX_PS_1G + 1)? I don't think
we are planning to add TDX_PS_256G anytime soon. But, should
TDX_PS_4K..TDX_PS_NR be an enum?
> + const u16 pamt_entry_size[TDX_PS_NR] = {
> + tdx_sysinfo.tdmr.pamt_4k_entry_size,
> + tdx_sysinfo.tdmr.pamt_2m_entry_size,
> + tdx_sysinfo.tdmr.pamt_1g_entry_size,
> + };
>
^ permalink raw reply
* Re: [PATCH 00/15] Device Evidence and Trust for PCI Security Protocol (TDISP)
From: Dan Williams (nvidia) @ 2026-07-08 0:12 UTC (permalink / raw)
To: Jason Gunthorpe, Dan Williams (nvidia)
Cc: linux-coco, linux-pci, driver-core, ankita, Aaron Tomlin,
Alexey Kardashevskiy, Alistair Francis, Aneesh Kumar K.V,
Arnd Bergmann, Bjorn Helgaas, Daniel Gomez, Danilo Krummrich,
Dexuan Cui, Donald Hunter, Greg Kroah-Hartman, Jakub Kicinski,
Luis Chamberlain, Lukas Wunner, Petr Pavlu, Rafael J. Wysocki,
Robin Murphy, Sami Tolvanen, Samuel Ortiz, Saravana Kannan,
Will Deacon, Xu Yilun
In-Reply-To: <20260707124321.GF118978@ziepe.ca>
Jason Gunthorpe wrote:
> On Mon, Jul 06, 2026 at 01:55:12PM -0700, Dan Williams (nvidia) wrote:
> > Jason Gunthorpe wrote:
> > > On Sun, Jul 05, 2026 at 03:08:04PM -0700, Dan Williams wrote:
> > > > * NONE: no usage of the device unless the trust is explicitly overridden
> > > > by user policy specified via a driver flag, module flag, or uapi (TBD).
> > > >
> > > > * ADVERSARY: needs acknowledgement from the bus and IOMMU / DMA layers
> > > > that the device is limited to strict IOMMU translation behavior. Drivers
> > > > can use this as a signal to limit functionality. This designation
> > > > implies follow-on IOMMU and bus enabling work for features like
> > > > arranging for the device to attach to a blocked IOMMU domain when
> > > > detached from a driver.
> > > >
> > > > * AUTO: typical / historical Linux driver model.
> > > >
> > > > * TCB: a trust level that only exists in Confidential Computing
> > > > environments. When acked by the IOMMU / DMA layer it enables the device
> > > > to issue direct-DMA to private/encrypted addresses or otherwise attach to
> > > > a secure vIOMMU within the TCB.
> > >
> > > I'm not sure I entirely like this one, certainly it needs to be
> > > possible to have both T=1 and ADVERSARY together.
> >
> > T=1 and ADVERSARY are independent for link encryption and private MMIO.
> > In other words the device is placed into the TDISP RUN state independent
> > of its trust level.
>
> That's the right thing
>
> > Downstream accesses to the device must have T=1, and
> > its upstream accesses will have T=1, but with force_dma_unencrypted() ==
> > true.
>
> That should never happen. Once in RUN force_dma_unencrypyted() ==
> false, it has nothing to do with the trust level.
2 concerns:
1/ We previously discussed a use case to operate a device's private MMIO
while not allowing access to private memory (software encrypted NVME
with private MMIO [1]). Many of the following comments are based on
preserving this assumption so you can save some reading if we agree that
use case can be abandoned.
[1]: https://lore.kernel.org/all/20260326120046.GG67624@nvidia.com/
If the private-MMIO-no-private-DMA case exists then ->dma_configure()
needs a "trusted to access private-DMA" signal not a "required to access
private-DMA" signal to determine how to set force_dma_unencrypyted()
while the device is in the RUN state. I am ok to reconsider that use
case, but that was part of what lead to the proposal for
force_dma_unencrypyted() to use the trust level.
2/ The confirmation of the trust level and the enabling of DMA are
separated in time from setting the trust level and entering the RUN
state.
All the archs separate the RUN step from the ENABLE DMA step, and the
implementation separates those steps in time.
echo tsm0 > $pdev/tsm/lock
cat $nonce | device-evidence dump $pdev
device-evidence validate $pdev $generation
echo full > $pdev/trust
echo 1 > $pdev/tsm/accept
<--- T=1 and force_dma_unencrypted() remains true in this window
echo $pdev > $driver/bind
The configuration window where $pdev/trust and $pdev/accept can be
dynamically changing should not be changing the force_dma_unencrypyted()
result if only because that value will mismatch the hardware state until
the next ->dma_configure() event.
Another factor that lead to the assumption that "operational trust level
configures force_dma_unencrypted()" is taken from a comment from Greg
[2].
There are also buses and paravisors that may know that private-DMA is
enabled for a device by construction. In that case it is also a "trusted
to access" signal, and not a "required to access" signal.
My answer, given the concerns of drivers dangerously flipping the
force_dma_unencrypted() result at runtime was to place it in 'struct
device_private' and make private memory access a result of the trust
level. Otherwise it is confusing when 2 devices are at FULL trust, but
one has private memory access and the other does not. One is FULL the
other is FULL+.
[2]: http://lore.kernel.org/2026031230-mastiff-create-7593@gregkh
> Even if you set ADVERSARY it should still be bouncing partial page
> DMAs into private memory. The point of running something like this is
> to remove the shared memory attack surface - ie the hypervisor SW. The
> attack surface is reduced to the device itself by remaining in shared
> memory.
Yes.
>
> > > I'd also argue this list is missing "FULL" trust, which is the
> > > historical Linux behavior for a normal device. AUTO should be
> > > selecting between FULL/ADVERSARY based on things like the ACPI/etc as
> > > it does today.
> >
> > 1/ that is effectively how the UNSET level behaves. If the
> > bus has not set ADVERSARY before device_add() then the default behavior
> > is the AUTO level. Where AUTO means all of the automatic privileges a
> > device can be offered without needing any other coordination.
>
> I think my other remark about two enums is some of the issue, the
> policy can have things like UNSET or AUTO, but once the driver starts
> to probe an in-effect mode should be computed and be concrete. Having
> a driver run with a trust mode of AUTO or UNSET is just confusing.
UNSET never escapes to something a driver would see, but yes, appears
AUTO is too confusing a term compared to FULL.
> > 2/ The ambiguity and conflict occurs at ->dma_configure() time when the
> > bus and IOMMU layer want to reject the device's access to some privilege
> > by failing. When FULL is defined as !ADVERSARY then it is difficult to
> > describe the semantics when FULL trust honors rejections to private DMA
> > and when it falls back to shared operation.
>
> Given that the trust level shouldn't impact force_dma_unencrypted(),
> the only thing left is to setup the IOMMU differently, and maybe
> operate a driver in a hardened mode or something like that.
>
> I don't see what the TCB is supposed to be changing here.
>
> That leaves it just as a policy gate to check that T=1, I'm not sure
> if that is worthwhile enough for dedicated UAPI?
If we ditch the private-MMIO-no-private-DMA case then yes I can
see T=1 being a uniform requirement.
> > The above more points to a need to have an explicit trust level for
> > adversarial private memory access. The address spaces are distinct
> > assets with different levels of trust.
> >
> > UNSET: bus picks initial level, or leaves it to the device_core().
> > NONE:
> > ADVERSARY: Device can be in T=0, or T=1 mode (UNLOCKED, or RUN).
> > AUTO: Could rename this to be FULL or ALL or DEFAULT, I still keep
> > coming back to the "AUTO" name because the privileges are not
> > uniform based on the IOMMU / DMA topology and device capability.
> > Again, the TDISP state is independent. The TSM driver does
> > not get called to gatekeep and verify access in this mode.
> > TCB_ADVERSARY: or PRIVATE_ADVERSARY. Device can access private platform
> > resources iff an enforcing IOMMU is present.
> > TCB: or PRIVATE_FULL, automatically enable all access privileges
> > including private memory access.
>
> Yeah, we can keep adding more modes to make a big cross product, but
> I do wonder if this is going to get too big..
The bus specific requirements like private MMIO + link encryption are
pushed out to the TSM driver. Userspace policy that wants to require
that the TSM driver is consulted on enable private DMA events needs to
arrange for it to be loaded otherwise ->dma_configure() can just proceed
with what the bus pre-configured.
> IDK, maybe it should be a bitmap instead of a level?
...not sure
> bit 0 = Force Disable
> bit 1 = Device is adversarial:
> - Enable strong IOMMU protections
> - Enable driver protections
> bit 2 = Require T=1
> bit 3 = Require IOMMU
> bit 4 = Require DMA/MMIO security (eg Link IDE)
>
> Where value 0 means the current level of full trust.
>
> It is a little easier to explain what each thing is doing and easier
> to add new things
The question is whether these requirements belong on a central device
trust mechanism bitmap or should be pushed out to other ABI. For
example, if the presence of the secure IOMMU is enumerated
after the device is in the LOCKED state then userspace policy can know
what it is getting into without needing to tell the kernel. If T=1 is
required then either a TSM driver is present to validate it can enable
DMA, or the bus/paravisor arranges for that T=1 capability to be
pre-enabled.
Perhaps the levels are indeed:
DISABLED
ADVERSARY
FULL
...with a T=1 flag on the side that leaf drivers can not set on their
own.
> Then from a sysfs perpsective the policy would have special string
> values like 'use bus default'
>
> > > If the trust level is reduced to just be a command to the kernel how
> > > it should operate the device then it would be up to userspace to
> > > confirm things like T=1 before setting the trust.
> >
> > This discussion gets strained for me when T=1 is used to mean both
> > "device is in TDISP RUN (with link encryption and private MMIO)" and
> > "device is in TDISP RUN + force_dma_unencrypted() == false".
>
> It means both things though, we really must not run with
> force_dma_unencrypted() = true when T=1, that's pointless and harmful.
Like I said above, I am ok to ditch the consideration of the
private-MMIO-no-private-DMA case.
> > Otherwise, full bi-directional T=1 before setting the trust would
> > require an IOMMU to be blocking the device until that final confirmation
> > point. Given that is not always available the proposal is to defer
> > acknowledging the trust level with the TSM until ->dma_configure().
>
> ?? If you have no iommu the instance you set T=1 and do the platform
> step to activate DMA the device has 100% acess to all
> memory. force_dma_unencrypted() does nothing to constrain device
> access, it is all about accommodating an addressing limitation.
Right, this is my point above about why "echo 1 > $pdev/accept" does not
immediately set force_dma_unencrypted to false nor enable private DMA.
> ARM at least has a dedicated call to enable DMA. It would be nice to
> place that call right before the driver probes so DMA remains off
> until we commit to using the device. Maybe other platforms have the
> same - but I'm not sure it is *essential* as the point of setting RUN
> can reasonably be the acceptance point.
Not essential, but it is useful to have the "operate device" intent,
IOMMU default domain configuration, trust level, and private DMA enable
all in the same bus operation (->dma_configure). Which is why "accept"
was demoted to just set RUN state and leave the rest to be finalized
later.
> > NONE: Device core rejects device operation
> > ADVERSARY: reject device operation if an IOMMU to set IOMMU_DOMAIN_DMA
> > not available (not in current patches)
> > AUTO: no rejections, but no private memory access either
> > TCB_ADVERSARY: reject device operation if IOMMU_DOMAIN_DMA not
> > available, or TSM rejects the evidence used to enable
> > private memory access.
> > TCB: reject device operation if TSM rejects chosen evidence
>
> What does "TSM rejectes chosen evidence" mean? Kernel isn't supposed
> to be evaluating evidence?
The host / platform TSM is protecting the VM against evidence that has
changed since the last time the VM asked. I.e. the TSM is tracking if
the untrusted VMM relocked the device without the VM having re-read the
evidence.
^ permalink raw reply
* Re: [PATCH v6 02/11] x86/virt/tdx: Allocate page bitmap for Dynamic PAMT
From: Sohil Mehta @ 2026-07-08 0:49 UTC (permalink / raw)
To: Rick Edgecombe, bp, dave.hansen, hpa, kas, kvm, linux-coco,
linux-doc, linux-kernel, mingo, nik.borisov, pbonzini, seanjc,
tglx, vannapurve, x86, chao.gao, yan.y.zhao, kai.huang
Cc: Kirill A. Shutemov, Binbin Wu
In-Reply-To: <20260526023515.288829-3-rick.p.edgecombe@intel.com>
On 5/25/2026 7:35 PM, Rick Edgecombe wrote:
> From: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>
>
> The TDX Physical Address Metadata Table (PAMT) holds data about the
> physical memory used by TDX, and must be allocated by the kernel during
> TDX module initialization.
>
> The exact size of the required PAMT memory is determined by the TDX module
> and may vary between TDX module versions. Currently it is approximately
> 0.4% of the system memory. This is a significant commitment, especially if
> it is not known upfront whether the machine will run any TDX guests.
>
> Each memory region that the TDX module might use needs three separate PAMT
> allocations. One for each supported page size (1GB, 2MB, 4KB). The
> TDX module supports a new feature designed to reduce PAMT overhead called
> Dynamic PAMT. At a high level, Dynamic PAMT still has the 1GB and 2MB
> levels allocated on TDX module initialization, but the 4KB level is
> allocated dynamically during runtime.
The last statement is slightly confusing to me. Is it trying to say that
the "dynamic" part is only applicable to 4KB allocations?
>
> However, in the details, Dynamic PAMT still needs some smaller per 4KB
> page scoped data (currently it is 1 bit per page). The TDX module exposes
> the number of bits as a separate piece of metadata than the 4KB static
> allocation for regular PAMT. Although the size is enumerated differently,
> it is handed to the TDX module in the same way the 4KB page size PAMT
> allocation is for regular, non-dynamic PAMT.
>
> Begin to implement Dynamic PAMT in the kernel by reading the bits-per-page
> needed for Dynamic PAMT. Calculate the size needed for the bitmap,
> and use it instead of the 4KB size determined for normal PAMT, in the case
> of Dynamic PAMT.
>
> Unlike the existing metadata reading code, this code is not generated by a
> script.
It might be useful to say that this file was auto-generated in the past
but going forward it is going to be manually updated.
> So adjust the comment to be more generic. Also, start to adopt a
> more normal kernel code style without the tenary statements and if
s/a more/
s/tenary/ternary
> conditionals assignments that the auto generated code has.
>
> Assisted-by: Sashiko:claude-opus-4-6
> Reviewed-by: Binbin Wu <binbin.wu@linux.intel.com>
The review tags goes after the SOBs.
> Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
> Co-developed-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
> Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
> ---
> diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
> index 503f9a3f46d61..82dc27aecf297 100644
> --- a/arch/x86/include/asm/tdx.h
> +++ b/arch/x86/include/asm/tdx.h
> @@ -149,6 +149,11 @@ static __always_inline u64 sc_retry(sc_func_t func, u64 fn,
> const char *tdx_dump_mce_info(struct mce *m);
> const struct tdx_sys_info *tdx_get_sysinfo(void);
>
> +static inline bool tdx_supports_dynamic_pamt(const struct tdx_sys_info *sysinfo)
> +{
> + return false; /* To be enabled when kernel is ready */
I would avoid the tail comment even if it is temporary.
> +}
> +
> int tdx_guest_keyid_alloc(void);
> u32 tdx_get_nr_guest_keyids(void);
> void tdx_guest_keyid_free(unsigned int keyid);
> @@ -33,6 +33,18 @@ static __init int get_tdx_sys_info_features(struct tdx_sys_info_features *sysinf
> return ret;
> }
>
> +static __init int get_tdx_sys_info_tdmr_dpamt(struct tdx_sys_info_tdmr *sysinfo_tdmr)
> +{
> + int ret;
> + u64 val;
> +
> + ret = read_sys_metadata_field(0x9100000100000013, &val);
Should this be a #define now that the file is being manually updated? Or
is the plan to do it all together? A #define would make it easier to
read this patch.
> + if (!ret)
> + sysinfo_tdmr->pamt_page_bitmap_entry_bits = val;
> +
> + return ret;
> +}
> +
> static __init int get_tdx_sys_info_tdmr(struct tdx_sys_info_tdmr *sysinfo_tdmr)
> {
> int ret = 0;
> @@ -116,5 +128,12 @@ static __init int get_tdx_sys_info(struct tdx_sys_info *sysinfo)
> ret = ret ?: get_tdx_sys_info_td_ctrl(&sysinfo->td_ctrl);
> ret = ret ?: get_tdx_sys_info_td_conf(&sysinfo->td_conf);
>
> + /*
> + * Don't treat a module that doesn't support Dynamic PAMT
> + * as a failure. Only read the metadata optionally.
> + */
> + if (!ret && tdx_supports_dynamic_pamt(sysinfo))
> + ret = get_tdx_sys_info_tdmr_dpamt(&sysinfo->tdmr);
There is a need for the comment because it combines two checks:
1) Did any of the previous stages fail?
2) Does the TDX module support Dynamic PAMT?
Should these be separated for readability and to follow the typical
kernel style?
if (ret)
return ret;
if (tdx_supports_dynamic_pamt(sysinfo))
ret = get_tdx_sys_info_tdmr_dpamt(&sysinfo->tdmr);
return ret;
I think you can avoid the comment altogether in that case.
> +
> return ret;
> }
^ permalink raw reply
* Re: [PATCH v6 01/11] x86/virt/tdx: Simplify tdmr_get_pamt_sz()
From: Edgecombe, Rick P @ 2026-07-08 1:23 UTC (permalink / raw)
To: kvm@vger.kernel.org, linux-coco@lists.linux.dev, Huang, Kai,
Hansen, Dave, Zhao, Yan Y, kas@kernel.org, seanjc@google.com,
mingo@redhat.com, pbonzini@redhat.com,
linux-kernel@vger.kernel.org, nik.borisov@suse.com,
linux-doc@vger.kernel.org, hpa@zytor.com, Annapurve, Vishal,
tglx@kernel.org, Mehta, Sohil, bp@alien8.de, Gao, Chao,
x86@kernel.org
Cc: binbin.wu@linux.intel.com
In-Reply-To: <a4b596d1-c0c7-4674-b5c4-dd565a6fd3ec@intel.com>
Thanks for the review!
On Tue, 2026-07-07 at 16:22 -0700, Sohil Mehta wrote:
> On 5/25/2026 7:35 PM, Rick Edgecombe wrote:
> > For each memory region that the TDX module might use (called TDMR), three
> > separate traditional PAMT allocations are needed.
>
> > One for each supported page size (1GB, 2MB, 4KB).
> Missing verb in this sentence. Maybe use a '-' to merge it with the
> previous sentence.
Hmm, yea I'll update it to:
There is one for each supported page size (1GB, 2MB, 4KB).
>
>
> > There are some commonalities in the math needed to calculate the base and
> > size for each smaller allocation, and so an effort was made to share logic
> > across the three. Unfortunately doing this turned out unnaturally tortured,
> > with a loop iterating over the three page sizes, only to call into a
> > function with cases statement for each page size.
>
> Did you mean "..with a case statement for each.."?
yep.
>
>
>
> > diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
> > index 967482ae3c801..487f389f52f4b 100644
> > --- a/arch/x86/virt/vmx/tdx/tdx.c
> > +++ b/arch/x86/virt/vmx/tdx/tdx.c
> > @@ -516,31 +516,21 @@ static __init int fill_out_tdmrs(struct list_head *tmb_list,
> > * Calculate PAMT size given a TDMR and a page size. The returned
> > * PAMT size is always aligned up to 4K page boundary.
> > */
> > -static __init unsigned long tdmr_get_pamt_sz(struct tdmr_info *tdmr, int pgsz,
> > - u16 pamt_entry_size)
> > +static __init unsigned long tdmr_get_pamt_sz(struct tdmr_info *tdmr, int pgsz)
> > {
> > unsigned long pamt_sz, nr_pamt_entries;
> > + const int tdx_pg_size_shift[] = { PAGE_SHIFT, PMD_SHIFT, PUD_SHIFT };
>
> Both of these consts go hand-in-hand, right? I would write it as
> tdx_pg_size_shift[TDX_PS_NR] to make the connection obvious.
Yea that seems reasonable.
>
> Just curious, why is TDX_PS_NR defined as (TDX_PS_1G + 1)? I don't think
> we are planning to add TDX_PS_256G anytime soon. But, should
> TDX_PS_4K..TDX_PS_NR be an enum?
Yea probably. The point of this patch is to improve the code such the following
patch can not make it worse. So I'd think it's best not to add extra cleanups to
it. I'll add this to the list. But we probably have more pressing cleanups.
^ permalink raw reply
* Re: [PATCH v6 02/11] x86/virt/tdx: Allocate page bitmap for Dynamic PAMT
From: Edgecombe, Rick P @ 2026-07-08 2:07 UTC (permalink / raw)
To: kvm@vger.kernel.org, linux-coco@lists.linux.dev, Huang, Kai,
Hansen, Dave, Zhao, Yan Y, kas@kernel.org, seanjc@google.com,
mingo@redhat.com, pbonzini@redhat.com,
linux-kernel@vger.kernel.org, nik.borisov@suse.com,
linux-doc@vger.kernel.org, hpa@zytor.com, Annapurve, Vishal,
tglx@kernel.org, Mehta, Sohil, bp@alien8.de, Gao, Chao,
x86@kernel.org
Cc: kirill.shutemov@linux.intel.com, binbin.wu@linux.intel.com
In-Reply-To: <efbff823-7b4e-4349-bdc4-72a244699d76@intel.com>
On Tue, 2026-07-07 at 17:49 -0700, Sohil Mehta wrote:
> On 5/25/2026 7:35 PM, Rick Edgecombe wrote:
> > From: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>
> >
> > The TDX Physical Address Metadata Table (PAMT) holds data about the
> > physical memory used by TDX, and must be allocated by the kernel during
> > TDX module initialization.
> >
> > The exact size of the required PAMT memory is determined by the TDX module
> > and may vary between TDX module versions. Currently it is approximately
> > 0.4% of the system memory. This is a significant commitment, especially if
> > it is not known upfront whether the machine will run any TDX guests.
> >
> > Each memory region that the TDX module might use needs three separate PAMT
> > allocations. One for each supported page size (1GB, 2MB, 4KB). The
> > TDX module supports a new feature designed to reduce PAMT overhead called
> > Dynamic PAMT. At a high level, Dynamic PAMT still has the 1GB and 2MB
> > levels allocated on TDX module initialization, but the 4KB level is
> > allocated dynamically during runtime.
>
> The last statement is slightly confusing to me. Is it trying to say that
> the "dynamic" part is only applicable to 4KB allocations?
Yea. Can you explain more about what is confusing? I guess this is more compact:
Under Dynamic PAMT the 4KB level is allocated dynamically during runtime, while
the 1GB and 2MB levels remain allocated on TDX module initialization.
>
>
> >
> > However, in the details, Dynamic PAMT still needs some smaller per 4KB
> > page scoped data (currently it is 1 bit per page). The TDX module exposes
> > the number of bits as a separate piece of metadata than the 4KB static
> > allocation for regular PAMT. Although the size is enumerated differently,
> > it is handed to the TDX module in the same way the 4KB page size PAMT
> > allocation is for regular, non-dynamic PAMT.
> >
> > Begin to implement Dynamic PAMT in the kernel by reading the bits-per-page
> > needed for Dynamic PAMT. Calculate the size needed for the bitmap,
> > and use it instead of the 4KB size determined for normal PAMT, in the case
> > of Dynamic PAMT.
> >
> > Unlike the existing metadata reading code, this code is not generated by a
> > script.
>
>
> It might be useful to say that this file was auto-generated in the past
> but going forward it is going to be manually updated.
>
> > So adjust the comment to be more generic. Also, start to adopt a
> > more normal kernel code style without the tenary statements and if
>
> s/a more/
> s/tenary/ternary
How about this?
The existing metadata reading code was generated by a script, but the current
plan is to stop generating this code, as the script has continued to need
adjustments. So add manually written code and adjust the comment about it being
autogenerated to be more generic. Start to adopt a more normal kernel code style
without the ternary statements and if conditionals assignments that the auto
generated code has.
>
>
> > conditionals assignments that the auto generated code has.
> >
> > Assisted-by: Sashiko:claude-opus-4-6
> > Reviewed-by: Binbin Wu <binbin.wu@linux.intel.com>
>
> The review tags goes after the SOBs.
Yep, I have been fixing those across the series.
>
> > Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
> > Co-developed-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
> > Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
> > ---
>
> > diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
> > index 503f9a3f46d61..82dc27aecf297 100644
> > --- a/arch/x86/include/asm/tdx.h
> > +++ b/arch/x86/include/asm/tdx.h
> > @@ -149,6 +149,11 @@ static __always_inline u64 sc_retry(sc_func_t func, u64 fn,
> > const char *tdx_dump_mce_info(struct mce *m);
> > const struct tdx_sys_info *tdx_get_sysinfo(void);
> >
> > +static inline bool tdx_supports_dynamic_pamt(const struct tdx_sys_info *sysinfo)
> > +{
> > + return false; /* To be enabled when kernel is ready */
>
> I would avoid the tail comment even if it is temporary.
Yep, Yan commented the same thing.
>
> > +}
> > +
> > int tdx_guest_keyid_alloc(void);
> > u32 tdx_get_nr_guest_keyids(void);
> > void tdx_guest_keyid_free(unsigned int keyid);
>
>
>
> > @@ -33,6 +33,18 @@ static __init int get_tdx_sys_info_features(struct tdx_sys_info_features *sysinf
> > return ret;
> > }
> >
> > +static __init int get_tdx_sys_info_tdmr_dpamt(struct tdx_sys_info_tdmr *sysinfo_tdmr)
> > +{
> > + int ret;
> > + u64 val;
> > +
> > + ret = read_sys_metadata_field(0x9100000100000013, &val);
>
> Should this be a #define now that the file is being manually updated? Or
> is the plan to do it all together? A #define would make it easier to
> read this patch.
I think switching to defines would overlap too much into the ultimate metadata
reading solution that gets discussed. Chao is currently working on a series for
this, so I'd think to leave that part for later.
>
> > + if (!ret)
> > + sysinfo_tdmr->pamt_page_bitmap_entry_bits = val;
> > +
> > + return ret;
> > +}
> > +
> > static __init int get_tdx_sys_info_tdmr(struct tdx_sys_info_tdmr *sysinfo_tdmr)
> > {
> > int ret = 0;
> > @@ -116,5 +128,12 @@ static __init int get_tdx_sys_info(struct tdx_sys_info *sysinfo)
> > ret = ret ?: get_tdx_sys_info_td_ctrl(&sysinfo->td_ctrl);
> > ret = ret ?: get_tdx_sys_info_td_conf(&sysinfo->td_conf);
> >
> > + /*
> > + * Don't treat a module that doesn't support Dynamic PAMT
> > + * as a failure. Only read the metadata optionally.
> > + */
> > + if (!ret && tdx_supports_dynamic_pamt(sysinfo))
> > + ret = get_tdx_sys_info_tdmr_dpamt(&sysinfo->tdmr);
>
> There is a need for the comment because it combines two checks:
>
> 1) Did any of the previous stages fail?
> 2) Does the TDX module support Dynamic PAMT?
>
> Should these be separated for readability and to follow the typical
> kernel style?
>
> if (ret)
> return ret;
>
> if (tdx_supports_dynamic_pamt(sysinfo))
> ret = get_tdx_sys_info_tdmr_dpamt(&sysinfo->tdmr);
>
> return ret;
>
> I think you can avoid the comment altogether in that case.
I don't think it removes the need for a comment. The point is if
tdx_supports_dynamic_pamt() is not supported. The comment should be more about
"why", than what the code does, right?
How about:
if (ret)
return ret;
/*
* The kernel supports using TDX without Dynamic PAMT, so
* avoid reporting failure if it's not supported.
*/
if (tdx_supports_dynamic_pamt(sysinfo))
ret = get_tdx_sys_info_tdmr_dpamt(&sysinfo->tdmr);
return ret;
>
> > +
> > return ret;
> > }
>
^ 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