* [PATCH 15/15] PCI/TSM: Add relative MMIO offset support?
From: Dan Williams @ 2026-07-05 22:08 UTC (permalink / raw)
To: linux-coco
Cc: linux-pci, driver-core, ankita, Alexey Kardashevskiy, Xu Yilun,
Aneesh Kumar K.V
In-Reply-To: <20260705220819.2472765-1-djbw@kernel.org>
The RMM specification, DEN0137-2.0-bet2 section A9.6.2 "Realm validation of
device memory mappings" documents the expectation that the
MMIO_REPORTING_OFFSET chosen for TDISP Interface Reports is always BAR
aligned.
Ideally this change is not needed and all implementations share the same
expectation.
If this semantic is already shipping in production and/or the PCI-SIG
clarifies that an implementation can hold this assumption then Linux will
need to ask the TSM drivers for this hint.
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: "Aneesh Kumar K.V" <aneesh.kumar@kernel.org>
Signed-off-by: Dan Williams <djbw@kernel.org>
---
include/linux/pci-tsm.h | 15 ++++++++++++++-
drivers/pci/tsm/core.c | 14 ++++++++++----
2 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/include/linux/pci-tsm.h b/include/linux/pci-tsm.h
index 6d5fadd79360..be9f78ca2c1a 100644
--- a/include/linux/pci-tsm.h
+++ b/include/linux/pci-tsm.h
@@ -296,7 +296,20 @@ struct pci_tsm_devsec *to_pci_tsm_devsec(struct pci_tsm *tsm);
int pci_tsm_mmio_setup(struct pci_dev *pdev, struct pci_tsm_mmio *mmio);
void pci_tsm_mmio_teardown(struct pci_tsm_mmio *mmio);
-struct pci_tsm_mmio *pci_tsm_mmio_alloc(struct pci_dev *pdev);
+/**
+ * enum tdisp_offset_scheme - MMIO_REPORTING_OFFSET assumptions
+ * @TDISP_OFFSET_BAR_ALIGN: mask by bar size to recover offset
+ * @TDISP_OFFSET_RELATIVE: first mmio report per bar is bar-offset-0
+ *
+ * A TSM driver may know that the default TDISP_OFFSET_BAR_ALIGN
+ * assumption is being violated.
+ */
+enum tdisp_offset_scheme {
+ TDISP_OFFSET_BAR_ALIGN,
+ TDISP_OFFSET_RELATIVE,
+};
+struct pci_tsm_mmio *pci_tsm_mmio_alloc(struct pci_dev *pdev,
+ enum tdisp_offset_scheme scheme);
int pci_tsm_mmio_free(struct pci_dev *pdev, struct pci_tsm_mmio *mmio);
#else
static inline int pci_tsm_register(struct tsm_dev *tsm_dev)
diff --git a/drivers/pci/tsm/core.c b/drivers/pci/tsm/core.c
index 9ac216ad896d..19ad35f2da4a 100644
--- a/drivers/pci/tsm/core.c
+++ b/drivers/pci/tsm/core.c
@@ -643,13 +643,15 @@ struct pci_tsm_devif_report {
/**
* pci_tsm_mmio_alloc() - allocate encrypted MMIO range descriptor
* @pdev: device owner of MMIO ranges
+ * @scheme: allow the low level TSM driver to hint the offset calc scheme
*
* Return: the encrypted MMIO range descriptor on success, NULL on failure
*
* Assumes that this is called within the live lifetime of a PCI device's
* association with a low level TSM.
*/
-struct pci_tsm_mmio *pci_tsm_mmio_alloc(struct pci_dev *pdev)
+struct pci_tsm_mmio *pci_tsm_mmio_alloc(struct pci_dev *pdev,
+ enum tdisp_offset_scheme scheme)
{
struct device_evidence *evidence = pdev->tsm->evidence;
u64 reporting_bar_base, last_reporting_end;
@@ -712,10 +714,14 @@ struct pci_tsm_mmio *pci_tsm_mmio_alloc(struct pci_dev *pdev)
last_bar = bar;
/*
- * Determine the obfuscated base of the BAR. BAR
- * offsets are never obfuscated.
+ * Either the first range per bar always maps
+ * the start of the BAR, or the reporting_offset
+ * is BAR size aligned.
*/
- reporting_bar_base = tsm_offset & ~mask;
+ if (scheme == TDISP_OFFSET_RELATIVE)
+ reporting_bar_base = tsm_offset;
+ else
+ reporting_bar_base = tsm_offset & ~mask;
} else if (tsm_offset < last_reporting_end) {
pci_dbg(pdev, "Reporting ranges within BAR not in ascending order\n");
return NULL;
--
2.54.0
^ permalink raw reply related
* [PATCH v5 00/10] coco: guest: Enforce host page-size alignment for shared buffers
From: Aneesh Kumar K.V (Arm) @ 2026-07-06 6:04 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel, iommu
Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Jason Gunthorpe,
Marc Zyngier, Marek Szyprowski, Robin Murphy, Steven Price,
Suzuki K Poulose, Thomas Gleixner, Will Deacon
Hi,
This series tightens the alignment requirements for buffers that are shared
between confidential-computing guests and the host.
When a guest runs with private memory, buffers shared with the hypervisor
are not only accessed by the guest. They are also accessed by the host
kernel, and the host may manage the corresponding shared/private state at a
granularity larger than the guest page size.
This matters for CCA systems where the Realm stage-2 mappings managed by
the RMM can still operate at 4K granularity, while the non-secure host may
manage the IPA state change at a larger page size, for example 64K. In that
case, allowing a guest to convert and share only a 4K subrange of a
host-managed granule is unsafe.
Architectures such as Arm can detect incorrect accesses to Realm physical
address space PFNs through GPC faults. However, relying on that as the only
line of defence is fragile and can still lead to kernel crashes. The risk
is especially visible for shared buffers that are later mmapped into
userspace, such as guest_memfd or dma-buf backed allocations. Once
userspace can access the mapping, the kernel cannot guarantee that
applications will only touch the intended 4K region rather than the whole
host page mapped into their address space. Those userspace addresses may
also be passed back into the kernel and accessed through the linear map,
resulting in a GPC fault.
To avoid this, shared buffers must satisfy two constraints:
- the address must be aligned to the CoCo shared-granule size
- the size must be a multiple of that granule size
The series adds generic helpers for this:
- mem_cc_shared_granule_size()
- mem_cc_align_to_shared_granule()
The generic implementation defaults to PAGE_SIZE. arm64 CCA overrides this
by querying the host IPA state change granule size through RHI and exposing that
value through the arm64 memory-encryption operations.
The patche series update the main shared-buffer allocation paths that can
be used by private-memory guests:
- arm64 set_memory_encrypted()/set_memory_decrypted() now reject unaligned
addresses or sizes.
- GIC ITS shared allocations are rounded to the shared granule size.
- dma-direct and atomic DMA pool allocations use aligned allocation and
conversion sizes.
- SWIOTLB pools, including dynamic pools, are allocated and converted at the
shared granule size.
- restricted-dma-pool regions are checked and rejected if firmware did not
provide a base and size aligned to the shared granule size.
- dma-buf system heap cc-shared allocations require aligned sizes and use at
least the required allocation order.
Hyper-V users of set_memory_encrypted() and set_memory_decrypted() are not
changed by this series. Those paths are not currently used by the arm64 CCA
code path, and therefore are not part of the arm64 CCA IPA state change alignment
problem addressed here.
The series is based on:
- https://lore.kernel.org/all/20260701054926.825925-1-aneesh.kumar@kernel.org
- https://lore.kernel.org/all/20260611130429.295516-1-aneesh.kumar@kernel.org
Changes from v4:
https://lore.kernel.org/all/20260427063108.909019-1-aneesh.kumar@kernel.org
Changes since v4:
* Rename the helpers to use CoCo terminology
(mem_cc_shared_granule_size() / mem_cc_align_to_shared_granule() instead of
mem_decrypt_granule_size() / mem_decrypt_align()).
* Use __DMA_ATTR_ALLOC_CC_SHARED to pass CoCo shared allocation requirements
down to CMA-based allocation helpers.
* Add validation for restricted DMA pools to reject pools that are not aligned
to the shared granule size.
* Add dma-buf system heap handling for cc-shared buffers.
* Split the previous combined DMA/SWIOTLB/ITS change into smaller subsystem
patches covering ITS, DMA direct, SWIOTLB, restricted DMA pools, dma-buf
system heap, and arm64 Realm support.
* Rework arm64 Realm support by moving Realm memory encryption ops into RSI
code and exposing the CCA shared granule size through arm64_mem_crypt_ops.
Changes from v3:
https://lore.kernel.org/all/20260309102625.2315725-1-aneesh.kumar@kernel.org
* Fix build error reported by kernel test robot <lkp@intel.com>
Changes from v2:
https://lore.kernel.org/all/20251221160920.297689-1-aneesh.kumar@kernel.org
* Rebase to latest kernel
* Consider swiotlb always decrypted and don't align when allocating from swiotlb.
Changes from v1:
* Rename the helper to mem_encrypt_align
* Improve the commit message
* Handle DMA allocations from contiguous memory
* Handle DMA allocations from the pool
* swiotlb is still considered unencrypted. Support for an encrypted swiotlb pool
is left as TODO and is independent of this series.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Steven Price <steven.price@arm.com>
Cc: Suzuki K Poulose <suzuki.poulose@arm.com>
Cc: Thomas Gleixner <tglx@kernel.org>
Cc: Will Deacon <will@kernel.org>
Aneesh Kumar K.V (Arm) (10):
mm/mem_encrypt: Add helpers for shared-buffer alignment
irqchip/gic-v3-its: Align shared ITS allocations to the CoCo shared
granule size
dma-mapping: Pass allocation attrs to contiguous allocation helpers
dma-direct: Align CoCo shared DMA allocations to the shared granule
size
swiotlb: Align shared IO TLB pools to the shared granule size
swiotlb: Reject misaligned restricted DMA pools for CoCo guests
dma-buf: system_heap: Enforce shared-granule alignment for cc-shared
buffers
arm64: realm: Move Realm memory encryption ops to RSI code
arm64: realm: Add RHI helper to query IPA state change alignment
arm64: realm: Expose the CCA shared granule size through mem_encrypt
ops
arch/arm64/include/asm/mem_encrypt.h | 5 +-
arch/arm64/include/asm/rsi_cmds.h | 10 ++++
arch/arm64/kernel/rsi.c | 90 ++++++++++++++++++++++++++++
arch/arm64/mm/mem_encrypt.c | 32 ++++++++--
arch/arm64/mm/pageattr.c | 38 +-----------
drivers/dma-buf/heaps/system_heap.c | 50 +++++++++++++---
drivers/iommu/dma-iommu.c | 2 +-
drivers/irqchip/irq-gic-v3-its.c | 17 ++++--
include/linux/arm-smccc-rhi.h | 25 ++++++++
include/linux/arm-smccc-rsi.h | 7 +++
include/linux/dma-map-ops.h | 5 +-
include/linux/mem_encrypt.h | 14 +++++
kernel/dma/contiguous.c | 13 +++-
kernel/dma/direct.c | 27 ++++++---
kernel/dma/ops_helpers.c | 3 +-
kernel/dma/pool.c | 4 +-
kernel/dma/swiotlb.c | 36 ++++++++---
17 files changed, 300 insertions(+), 78 deletions(-)
create mode 100644 include/linux/arm-smccc-rhi.h
--
2.43.0
^ permalink raw reply
* [PATCH v5 01/10] mm/mem_encrypt: Add helpers for shared-buffer alignment
From: Aneesh Kumar K.V (Arm) @ 2026-07-06 6:04 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel, iommu
Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Jason Gunthorpe,
Marc Zyngier, Marek Szyprowski, Robin Murphy, Steven Price,
Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <20260706060432.1375570-1-aneesh.kumar@kernel.org>
confidential-computing guests may require shared buffers with alignment
larger than the guest page size. As these buffers are shared with the host,
which may manage shared/private state at a different page-size granularity,
the required alignment must account for the host's page size as well.
Add helpers for querying the shared-buffer granule size and for rounding
sizes up to that granule. The generic implementation defaults to PAGE_SIZE
so that existing architectures keep their current behaviour unless they
override the granule size.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
include/linux/mem_encrypt.h | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/include/linux/mem_encrypt.h b/include/linux/mem_encrypt.h
index 07584c5e36fb..5d2a868f8d3d 100644
--- a/include/linux/mem_encrypt.h
+++ b/include/linux/mem_encrypt.h
@@ -11,6 +11,8 @@
#define __MEM_ENCRYPT_H__
#ifndef __ASSEMBLY__
+#include <linux/align.h>
+#include <vdso/page.h>
#ifdef CONFIG_ARCH_HAS_MEM_ENCRYPT
@@ -54,6 +56,18 @@
#define dma_addr_canonical(x) (x)
#endif
+#ifndef mem_cc_shared_granule_size
+static inline size_t mem_cc_shared_granule_size(void)
+{
+ return PAGE_SIZE;
+}
+#endif
+
+static inline size_t mem_cc_align_to_shared_granule(size_t size)
+{
+ return ALIGN(size, mem_cc_shared_granule_size());
+}
+
#endif /* __ASSEMBLY__ */
#endif /* __MEM_ENCRYPT_H__ */
--
2.43.0
^ permalink raw reply related
* [PATCH v5 02/10] irqchip/gic-v3-its: Align shared ITS allocations to the CoCo shared granule size
From: Aneesh Kumar K.V (Arm) @ 2026-07-06 6:04 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel, iommu
Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Jason Gunthorpe,
Marc Zyngier, Marek Szyprowski, Robin Murphy, Steven Price,
Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <20260706060432.1375570-1-aneesh.kumar@kernel.org>
ITS tables allocated by the coco guest are shared with the hypervisor.
These allocations must satisfy the host shared-buffer granule size so that
the full converted range is safe for host access.
Allocate ITS pages using a size rounded up to the shared granule size and
use the same allocation order when encrypting, decrypting and freeing the
memory. Also grow the ITT cache in shared-granule sized chunks instead of
assuming PAGE_SIZE is sufficient.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
drivers/irqchip/irq-gic-v3-its.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index b57d81ad33a0..5d6d38aa0dae 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -213,16 +213,17 @@ static gfp_t gfp_flags_quirk;
static struct page *its_alloc_pages_node(int node, gfp_t gfp,
unsigned int order)
{
+ unsigned int alloc_order;
struct page *page;
int ret = 0;
- page = alloc_pages_node(node, gfp | gfp_flags_quirk, order);
-
+ alloc_order = get_order(mem_cc_align_to_shared_granule(PAGE_SIZE << order));
+ page = alloc_pages_node(node, gfp | gfp_flags_quirk, alloc_order);
if (!page)
return NULL;
ret = set_memory_decrypted((unsigned long)page_address(page),
- 1 << order);
+ 1 << alloc_order);
/*
* If set_memory_decrypted() fails then we don't know what state the
* page is in, so we can't free it. Instead we leak it.
@@ -241,13 +242,16 @@ static struct page *its_alloc_pages(gfp_t gfp, unsigned int order)
static void its_free_pages(void *addr, unsigned int order)
{
+ int alloc_order;
+
+ alloc_order = get_order(mem_cc_align_to_shared_granule(PAGE_SIZE << order));
/*
* If the memory cannot be encrypted again then we must leak the pages.
* set_memory_encrypted() will already have WARNed.
*/
- if (set_memory_encrypted((unsigned long)addr, 1 << order))
+ if (set_memory_encrypted((unsigned long)addr, 1 << alloc_order))
return;
- free_pages((unsigned long)addr, order);
+ free_pages((unsigned long)addr, alloc_order);
}
static struct gen_pool *itt_pool;
@@ -272,7 +276,8 @@ static void *itt_alloc_pool(int node, int size)
if (!page)
break;
- gen_pool_add(itt_pool, (unsigned long)page_address(page), PAGE_SIZE, node);
+ gen_pool_add(itt_pool, (unsigned long)page_address(page),
+ mem_cc_align_to_shared_granule(PAGE_SIZE), node);
} while (!addr);
return (void *)addr;
--
2.43.0
^ permalink raw reply related
* [PATCH v5 03/10] dma-mapping: Pass allocation attrs to contiguous allocation helpers
From: Aneesh Kumar K.V (Arm) @ 2026-07-06 6:04 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel, iommu
Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Jason Gunthorpe,
Marc Zyngier, Marek Szyprowski, Robin Murphy, Steven Price,
Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <20260706060432.1375570-1-aneesh.kumar@kernel.org>
Prepare for handling CoCo shared allocation requirements in the common
contiguous allocation path by passing DMA allocation attributes down to the
helpers that may allocate from CMA.
The next patch uses this to apply shared-granule alignment only to
allocations that are actually creating CoCo shared backing pages.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
drivers/iommu/dma-iommu.c | 2 +-
include/linux/dma-map-ops.h | 5 +++--
kernel/dma/contiguous.c | 4 +++-
kernel/dma/direct.c | 11 ++++++-----
kernel/dma/ops_helpers.c | 3 ++-
5 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index fe387829ee92..da4aa4373842 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -1627,7 +1627,7 @@ static void *iommu_dma_alloc_pages(struct device *dev, size_t size,
struct page *page = NULL;
void *cpu_addr;
- page = dma_alloc_contiguous(dev, alloc_size, gfp);
+ page = dma_alloc_contiguous(dev, alloc_size, gfp, attrs);
if (!page)
page = alloc_pages_node(node, gfp, get_order(alloc_size));
if (!page)
diff --git a/include/linux/dma-map-ops.h b/include/linux/dma-map-ops.h
index 8fae2b7deb20..1849f352fb88 100644
--- a/include/linux/dma-map-ops.h
+++ b/include/linux/dma-map-ops.h
@@ -102,7 +102,8 @@ struct page *dma_alloc_from_contiguous(struct device *dev, size_t count,
unsigned int order, bool no_warn);
bool dma_release_from_contiguous(struct device *dev, struct page *pages,
int count);
-struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp);
+struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp,
+ unsigned long attrs);
void dma_free_contiguous(struct device *dev, struct page *page, size_t size);
void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size);
@@ -136,7 +137,7 @@ static inline bool dma_release_from_contiguous(struct device *dev,
}
/* Use fallback alloc() and free() when CONFIG_DMA_CMA=n */
static inline struct page *dma_alloc_contiguous(struct device *dev, size_t size,
- gfp_t gfp)
+ gfp_t gfp, unsigned long attrs)
{
return NULL;
}
diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c
index f754079a287d..6c8a61ab5456 100644
--- a/kernel/dma/contiguous.c
+++ b/kernel/dma/contiguous.c
@@ -400,6 +400,7 @@ static struct page *cma_alloc_aligned(struct cma *cma, size_t size, gfp_t gfp)
* @dev: Pointer to device for which the allocation is performed.
* @size: Requested allocation size.
* @gfp: Allocation flags.
+ * @attrs: DMA attributes.
*
* tries to use device specific contiguous memory area if available, or it
* tries to use per-numa cma, if the allocation fails, it will fallback to
@@ -410,7 +411,8 @@ static struct page *cma_alloc_aligned(struct cma *cma, size_t size, gfp_t gfp)
* there is no need to waste CMA pages for that kind; it also helps reduce
* fragmentations.
*/
-struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)
+struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp,
+ unsigned long attrs)
{
#ifdef CONFIG_DMA_NUMA_CMA
int nid = dev_to_node(dev);
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index 9575d68571bf..80f9f7d23ad4 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -113,7 +113,7 @@ static struct page *dma_direct_alloc_swiotlb(struct device *dev, size_t size,
}
static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
- gfp_t gfp, bool allow_highmem)
+ gfp_t gfp, bool allow_highmem, unsigned long attrs)
{
int node = dev_to_node(dev);
struct page *page;
@@ -122,7 +122,7 @@ static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
WARN_ON_ONCE(!PAGE_ALIGNED(size));
gfp |= dma_direct_optimal_gfp_mask(dev, &phys_limit);
- page = dma_alloc_contiguous(dev, size, gfp);
+ page = dma_alloc_contiguous(dev, size, gfp, attrs);
if (page) {
if (dma_coherent_ok(dev, page_to_phys(page), size) &&
(allow_highmem || !PageHighMem(page)))
@@ -182,7 +182,7 @@ static void *dma_direct_alloc_no_mapping(struct device *dev, size_t size,
{
struct page *page;
- page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO, true);
+ page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO, true, 0);
if (!page)
return NULL;
@@ -281,7 +281,8 @@ void *dma_direct_alloc(struct device *dev, size_t size,
}
/* we always manually zero the memory once we are done */
- page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO, allow_highmem);
+ page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO,
+ allow_highmem, attrs);
if (!page)
return NULL;
@@ -447,7 +448,7 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
goto setup_page;
}
- page = __dma_direct_alloc_pages(dev, size, gfp, false);
+ page = __dma_direct_alloc_pages(dev, size, gfp, false, attrs);
if (!page)
return NULL;
diff --git a/kernel/dma/ops_helpers.c b/kernel/dma/ops_helpers.c
index 20caf9cabf69..d67a5c838291 100644
--- a/kernel/dma/ops_helpers.c
+++ b/kernel/dma/ops_helpers.c
@@ -66,7 +66,8 @@ struct page *dma_common_alloc_pages(struct device *dev, size_t size,
struct page *page;
phys_addr_t phys;
- page = dma_alloc_contiguous(dev, size, gfp);
+ /* __DMA_ATTR_ALLOC_CC_SHARED is not yet supported here, attrs = 0 */
+ page = dma_alloc_contiguous(dev, size, gfp, 0);
if (!page)
page = alloc_pages_node(dev_to_node(dev), gfp, get_order(size));
if (!page)
--
2.43.0
^ permalink raw reply related
* [PATCH v5 04/10] dma-direct: Align CoCo shared DMA allocations to the shared granule size
From: Aneesh Kumar K.V (Arm) @ 2026-07-06 6:04 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel, iommu
Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Jason Gunthorpe,
Marc Zyngier, Marek Szyprowski, Robin Murphy, Steven Price,
Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <20260706060432.1375570-1-aneesh.kumar@kernel.org>
DMA allocations that create shared backing pages for confidential-computing
guests are converted between private and shared memory before being used
for DMA. On some architecture, the conversion granule may be larger than
PAGE_SIZE, so converting only the requested size can leave the rest of the
host-managed granule private.
Use the internal __DMA_ATTR_ALLOC_CC_SHARED allocation attribute to
identify those allocations in the DMA allocation paths. Round the allocated
and converted size up to mem_cc_shared_granule_size(), and use the same
aligned size when restoring encryption on free.
Also reject CMA allocations for CoCo shared backing pages when CMA cannot
provide alignment at the required shared granule size, and keep atomic DMA
pool expansion from falling below the order needed for shared-buffer
conversions.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/contiguous.c | 9 +++++++++
kernel/dma/direct.c | 16 ++++++++++++++--
kernel/dma/pool.c | 4 +++-
3 files changed, 26 insertions(+), 3 deletions(-)
diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c
index 6c8a61ab5456..3d0eac2e2a20 100644
--- a/kernel/dma/contiguous.c
+++ b/kernel/dma/contiguous.c
@@ -45,6 +45,7 @@
#include <linux/dma-map-ops.h>
#include <linux/cma.h>
#include <linux/nospec.h>
+#include <linux/mem_encrypt.h>
#ifdef CONFIG_CMA_SIZE_MBYTES
#define CMA_SIZE_MBYTES CONFIG_CMA_SIZE_MBYTES
@@ -417,6 +418,14 @@ struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp,
#ifdef CONFIG_DMA_NUMA_CMA
int nid = dev_to_node(dev);
#endif
+ /*
+ * CoCo shared allocations require CMA alignment large enough for the
+ * architecture's shared-buffer granule.
+ */
+ if (attrs & __DMA_ATTR_ALLOC_CC_SHARED) {
+ if (get_order(mem_cc_shared_granule_size()) > CONFIG_CMA_ALIGNMENT)
+ return NULL;
+ }
/* CMA can be used only in the context which permits sleeping */
if (!gfpflags_allow_blocking(gfp))
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index 80f9f7d23ad4..6d0e485fdc34 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -280,6 +280,9 @@ void *dma_direct_alloc(struct device *dev, size_t size,
return NULL;
}
+ if (mark_mem_decrypt)
+ size = mem_cc_align_to_shared_granule(size);
+
/* we always manually zero the memory once we are done */
page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO,
allow_highmem, attrs);
@@ -402,6 +405,9 @@ void dma_direct_free(struct device *dev, size_t size,
/* Swiotlb doesn't need a page attribute update on free */
mark_mem_encrypted = false;
+ if (mark_mem_encrypted && force_dma_unencrypted(dev))
+ size = mem_cc_align_to_shared_granule(size);
+
if (is_vmalloc_addr(cpu_addr)) {
vunmap(cpu_addr);
} else {
@@ -448,6 +454,9 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
goto setup_page;
}
+ if (attrs & __DMA_ATTR_ALLOC_CC_SHARED)
+ size = mem_cc_align_to_shared_granule(size);
+
page = __dma_direct_alloc_pages(dev, size, gfp, false, attrs);
if (!page)
return NULL;
@@ -488,8 +497,11 @@ void dma_direct_free_pages(struct device *dev, size_t size,
if (swiotlb_pool)
mark_mem_encrypted = false;
- if (mark_mem_encrypted && dma_set_encrypted(dev, vaddr, size))
- return;
+ if (mark_mem_encrypted) {
+ size = mem_cc_align_to_shared_granule(size);
+ if (dma_set_encrypted(dev, vaddr, size))
+ return;
+ }
if (swiotlb_pool)
swiotlb_free_from_pool(dev, phys, swiotlb_pool);
diff --git a/kernel/dma/pool.c b/kernel/dma/pool.c
index 5115a3e8c722..d02b630e80fc 100644
--- a/kernel/dma/pool.c
+++ b/kernel/dma/pool.c
@@ -90,7 +90,9 @@ static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,
bool leak_pages = false;
void *addr;
int ret = -ENOMEM;
+ unsigned int min_encrypt_order = get_order(mem_cc_shared_granule_size());
+ pool_size = mem_cc_align_to_shared_granule(pool_size);
/* Cannot allocate larger than MAX_PAGE_ORDER */
order = min(get_order(pool_size), MAX_PAGE_ORDER);
@@ -101,7 +103,7 @@ static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,
order, false);
if (!page)
page = alloc_pages(gfp | __GFP_NOWARN, order);
- } while (!page && order-- > 0);
+ } while (!page && order-- > min_encrypt_order);
if (!page)
goto out;
--
2.43.0
^ permalink raw reply related
* [PATCH v5 05/10] swiotlb: Align shared IO TLB pools to the shared granule size
From: Aneesh Kumar K.V (Arm) @ 2026-07-06 6:04 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel, iommu
Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Jason Gunthorpe,
Marc Zyngier, Marek Szyprowski, Robin Murphy, Steven Price,
Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <20260706060432.1375570-1-aneesh.kumar@kernel.org>
swiotlb pools used by confidential-computing guests are shared with the
host and therefore must be converted at the host shared-granule size. A
PAGE_SIZE-sized or PAGE_SIZE-aligned pool is not sufficient when the host
tracks shared state at a larger granularity.
Round swiotlb pool sizes to mem_cc_shared_granule_size(), allocate the
pools at that alignment, and use the same aligned size when decrypting,
encrypting and freeing pool memory. Apply the same rule to dynamically
allocated swiotlb pools.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/swiotlb.c | 22 ++++++++++++++--------
1 file changed, 14 insertions(+), 8 deletions(-)
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index 897aba538c5b..1d77746b6f31 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -287,7 +287,8 @@ void __init swiotlb_update_mem_attributes(void)
if (!mem->nslabs || mem->late_alloc)
return;
- bytes = PAGE_ALIGN(mem->nslabs << IO_TLB_SHIFT);
+
+ bytes = mem_cc_align_to_shared_granule(mem->nslabs << IO_TLB_SHIFT);
if (io_tlb_default_mem.cc_shared) {
int ret;
@@ -354,8 +355,8 @@ static void __init *swiotlb_memblock_alloc(unsigned long nslabs,
unsigned int flags,
int (*remap)(void *tlb, unsigned long nslabs))
{
- size_t bytes = PAGE_ALIGN(nslabs << IO_TLB_SHIFT);
void *tlb;
+ size_t bytes = mem_cc_align_to_shared_granule(nslabs << IO_TLB_SHIFT);
/*
* By default allocate the bounce buffer memory from low memory, but
@@ -363,9 +364,9 @@ static void __init *swiotlb_memblock_alloc(unsigned long nslabs,
* memory encryption.
*/
if (flags & SWIOTLB_ANY)
- tlb = memblock_alloc(bytes, PAGE_SIZE);
+ tlb = memblock_alloc(bytes, mem_cc_shared_granule_size());
else
- tlb = memblock_alloc_low(bytes, PAGE_SIZE);
+ tlb = memblock_alloc_low(bytes, mem_cc_shared_granule_size());
if (!tlb) {
pr_warn("%s: Failed to allocate %zu bytes tlb structure\n",
@@ -374,7 +375,7 @@ static void __init *swiotlb_memblock_alloc(unsigned long nslabs,
}
if (remap && remap(tlb, nslabs) < 0) {
- memblock_free(tlb, PAGE_ALIGN(bytes));
+ memblock_free(tlb, bytes);
pr_warn("%s: Failed to remap %zu bytes\n", __func__, bytes);
return NULL;
}
@@ -496,7 +497,7 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,
swiotlb_adjust_nareas(num_possible_cpus());
retry:
- order = get_order(nslabs << IO_TLB_SHIFT);
+ order = get_order(mem_cc_align_to_shared_granule(nslabs << IO_TLB_SHIFT));
nslabs = SLABS_PER_PAGE << order;
while ((SLABS_PER_PAGE << order) > IO_TLB_MIN_SLABS) {
@@ -505,6 +506,8 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,
if (vstart)
break;
order--;
+ if (order < get_order(mem_cc_shared_granule_size()))
+ break;
nslabs = SLABS_PER_PAGE << order;
retried = true;
}
@@ -585,7 +588,7 @@ void __init swiotlb_exit(void)
pr_info("tearing down default memory pool\n");
tbl_vaddr = (unsigned long)phys_to_virt(mem->start);
- tbl_size = PAGE_ALIGN(mem->end - mem->start);
+ tbl_size = mem_cc_align_to_shared_granule(mem->end - mem->start);
slots_size = PAGE_ALIGN(array_size(sizeof(*mem->slots), mem->nslabs));
if (io_tlb_default_mem.cc_shared) {
@@ -629,12 +632,14 @@ void __init swiotlb_exit(void)
static struct page *alloc_dma_pages(gfp_t gfp, size_t bytes,
u64 phys_limit, unsigned long attrs)
{
- unsigned int order = get_order(bytes);
bool cc_shared = attrs & __DMA_ATTR_ALLOC_CC_SHARED;
+ unsigned int order;
struct page *page;
phys_addr_t paddr;
void *vaddr;
+ bytes = mem_cc_align_to_shared_granule(bytes);
+ order = get_order(bytes);
page = alloc_pages(gfp, order);
if (!page)
return NULL;
@@ -725,6 +730,7 @@ static void swiotlb_free_tlb(void *vaddr, size_t bytes, bool cc_shared)
dma_free_from_pool(NULL, vaddr, bytes))
return;
+ bytes = mem_cc_align_to_shared_granule(bytes);
/* Intentional leak if pages cannot be encrypted again. */
if (!cc_shared ||
!set_memory_encrypted((unsigned long)vaddr, PFN_UP(bytes)))
--
2.43.0
^ permalink raw reply related
* [PATCH v5 06/10] swiotlb: Reject misaligned restricted DMA pools for CoCo guests
From: Aneesh Kumar K.V (Arm) @ 2026-07-06 6:04 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel, iommu
Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Jason Gunthorpe,
Marc Zyngier, Marek Szyprowski, Robin Murphy, Steven Price,
Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <20260706060432.1375570-1-aneesh.kumar@kernel.org>
Restricted DMA pools are described by firmware reserved-memory nodes and
are not resized or realigned by the kernel. For confidential-computing
guests, such pools can only be shared safely when both the base address and
size are aligned to the shared-granule size.
Reject restricted DMA pools that are not aligned to
mem_cc_shared_granule_size() when guest memory encryption is active.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/swiotlb.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index 1d77746b6f31..ad8a1ccf3977 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -2002,6 +2002,20 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,
* if platform supports memory encryption,
* restricted mem pool is shared by default
*/
+ if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) {
+ size_t cc_shared_granule_size = mem_cc_shared_granule_size();
+
+ if (!IS_ALIGNED(rmem->base, cc_shared_granule_size) ||
+ !IS_ALIGNED(rmem->size, cc_shared_granule_size)) {
+ dev_err(dev, "Restricted DMA pool must be aligned to %#zx bytes for memory encryption\n",
+ cc_shared_granule_size);
+ kfree(pool->areas);
+ kfree(pool->slots);
+ kfree(mem);
+ return -EINVAL;
+ }
+ }
+
if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {
int ret;
--
2.43.0
^ permalink raw reply related
* [PATCH v5 07/10] dma-buf: system_heap: Enforce shared-granule alignment for cc-shared buffers
From: Aneesh Kumar K.V (Arm) @ 2026-07-06 6:04 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel, iommu
Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Jason Gunthorpe,
Marc Zyngier, Marek Szyprowski, Robin Murphy, Steven Price,
Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <20260706060432.1375570-1-aneesh.kumar@kernel.org>
The system heap can allocate buffers that are decrypted and shared with the
host. For confidential-computing guests, those shared buffers must cover
whole shared-buffer granule; otherwise a userspace mmap of the dma-buf may
expose only part of a host-managed granule and allow unintended access to
adjacent private memory.
Require cc-shared system-heap allocations to have a size aligned to
mem_cc_shared_granule_size(), and allocate pages at least as large as the
required granule. Keep the allocation bounded by the existing heap orders,
but fall back to an exact minimum-order allocation when the required
granule is not one of the preferred heap orders.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
drivers/dma-buf/heaps/system_heap.c | 50 +++++++++++++++++++++++------
1 file changed, 41 insertions(+), 9 deletions(-)
diff --git a/drivers/dma-buf/heaps/system_heap.c b/drivers/dma-buf/heaps/system_heap.c
index c8959eadc71d..9cbfcebe2088 100644
--- a/drivers/dma-buf/heaps/system_heap.c
+++ b/drivers/dma-buf/heaps/system_heap.c
@@ -55,7 +55,6 @@ struct dma_heap_attachment {
#define HIGH_ORDER_GFP (((GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN \
| __GFP_NORETRY) & ~__GFP_RECLAIM) \
| __GFP_COMP)
-static gfp_t order_flags[] = {HIGH_ORDER_GFP, HIGH_ORDER_GFP, LOW_ORDER_GFP};
/*
* The selection of the orders used for allocation (1MB, 64K, 4K) is designed
* to match with the sizes often found in IOMMUs. Using order 4 pages instead
@@ -375,26 +374,44 @@ static const struct dma_buf_ops system_heap_buf_ops = {
.release = system_heap_dma_buf_release,
};
+static struct page *system_heap_alloc_order(unsigned int order)
+{
+ gfp_t flags = order ? HIGH_ORDER_GFP : LOW_ORDER_GFP;
+
+ if (mem_accounting)
+ flags |= __GFP_ACCOUNT;
+
+ return alloc_pages(flags, order);
+}
+
static struct page *alloc_largest_available(unsigned long size,
- unsigned int max_order)
+ unsigned int max_order,
+ unsigned int min_order)
{
struct page *page;
int i;
- gfp_t flags;
for (i = 0; i < NUM_ORDERS; i++) {
if (size < (PAGE_SIZE << orders[i]))
continue;
- if (max_order < orders[i])
+
+ if (max_order < orders[i] || orders[i] < min_order)
continue;
- flags = order_flags[i];
- if (mem_accounting)
- flags |= __GFP_ACCOUNT;
- page = alloc_pages(flags, orders[i]);
+
+ page = system_heap_alloc_order(orders[i]);
if (!page)
continue;
return page;
}
+ /*
+ * The required minimum order might not be one of the preferred heap
+ * orders. Allocate exactly min_order when it does not exceed the
+ * remaining size.
+ */
+ if (min_order && min_order <= max_order &&
+ size >= (PAGE_SIZE << min_order))
+ return system_heap_alloc_order(min_order);
+
return NULL;
}
@@ -409,6 +426,8 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
unsigned int max_order = orders[0];
struct system_heap_priv *priv = dma_heap_get_drvdata(heap);
bool cc_shared = priv->cc_shared;
+ unsigned int min_order = 0;
+ size_t cc_granule_size;
struct dma_buf *dmabuf;
struct sg_table *table;
struct scatterlist *sg;
@@ -425,6 +444,18 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
buffer->heap = heap;
buffer->len = len;
buffer->cc_shared = cc_shared;
+ if (cc_shared_buffer(buffer)) {
+ cc_granule_size = mem_cc_shared_granule_size();
+ if (!IS_ALIGNED(len, cc_granule_size)) {
+ ret = -EINVAL;
+ goto free_buffer;
+ }
+ min_order = get_order(cc_granule_size);
+ if (min_order > max_order) {
+ ret = -EINVAL;
+ goto free_buffer;
+ }
+ }
INIT_LIST_HEAD(&pages);
i = 0;
@@ -438,7 +469,8 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
goto free_buffer;
}
- page = alloc_largest_available(size_remaining, max_order);
+ page = alloc_largest_available(size_remaining, max_order,
+ min_order);
if (!page)
goto free_buffer;
--
2.43.0
^ permalink raw reply related
* [PATCH v5 08/10] arm64: realm: Move Realm memory encryption ops to RSI code
From: Aneesh Kumar K.V (Arm) @ 2026-07-06 6:04 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel, iommu
Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Jason Gunthorpe,
Marc Zyngier, Marek Szyprowski, Robin Murphy, Steven Price,
Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <20260706060432.1375570-1-aneesh.kumar@kernel.org>
Realm memory encryption callbacks are CCA-specific. Keep the Realm callback
registration with the RSI initialization code instead of pageattr.c, which
only needs to provide the low-level page-attribute transition helper.
Export __set_memory_enc_dec() within arm64 so the RSI code can wrap it with
the Realm-specific encrypt/decrypt callbacks and warning policy.
No functional changes in this patch.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/mem_encrypt.h | 3 +--
arch/arm64/kernel/rsi.c | 34 +++++++++++++++++++++++++
arch/arm64/mm/pageattr.c | 38 +---------------------------
3 files changed, 36 insertions(+), 39 deletions(-)
diff --git a/arch/arm64/include/asm/mem_encrypt.h b/arch/arm64/include/asm/mem_encrypt.h
index 314b2b52025f..f6325f30e844 100644
--- a/arch/arm64/include/asm/mem_encrypt.h
+++ b/arch/arm64/include/asm/mem_encrypt.h
@@ -15,8 +15,7 @@ int arm64_mem_crypt_ops_register(const struct arm64_mem_crypt_ops *ops);
int set_memory_encrypted(unsigned long addr, int numpages);
int set_memory_decrypted(unsigned long addr, int numpages);
-
-int realm_register_memory_enc_ops(void);
+int __set_memory_enc_dec(unsigned long addr, int numpages, bool encrypt);
static inline bool force_dma_unencrypted(struct device *dev)
{
diff --git a/arch/arm64/kernel/rsi.c b/arch/arm64/kernel/rsi.c
index 1fb2abd79800..5c566700974c 100644
--- a/arch/arm64/kernel/rsi.c
+++ b/arch/arm64/kernel/rsi.c
@@ -143,6 +143,40 @@ static int realm_ioremap_hook(phys_addr_t phys, size_t size, pgprot_t *prot)
return 0;
}
+static int realm_set_memory_encrypted(unsigned long addr, int numpages)
+{
+ int ret = __set_memory_enc_dec(addr, numpages, true);
+
+ /*
+ * If the request to change state fails, then the only sensible cause
+ * of action for the caller is to leak the memory
+ */
+ WARN(ret, "Failed to encrypt memory, %d pages will be leaked",
+ numpages);
+
+ return ret;
+}
+
+static int realm_set_memory_decrypted(unsigned long addr, int numpages)
+{
+ int ret = __set_memory_enc_dec(addr, numpages, false);
+
+ WARN(ret, "Failed to decrypt memory, %d pages will be leaked",
+ numpages);
+
+ return ret;
+}
+
+static const struct arm64_mem_crypt_ops realm_crypt_ops = {
+ .encrypt = realm_set_memory_encrypted,
+ .decrypt = realm_set_memory_decrypted,
+};
+
+static int realm_register_memory_enc_ops(void)
+{
+ return arm64_mem_crypt_ops_register(&realm_crypt_ops);
+}
+
void __init arm64_rsi_init(void)
{
if (arm_smccc_1_1_get_conduit() != SMCCC_CONDUIT_SMC)
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index bbe98ac9ad8c..14b2a3801f40 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -275,9 +275,7 @@ int set_direct_map_default_noflush(struct page *page)
PAGE_SIZE, set_mask, clear_mask);
}
-static int __set_memory_enc_dec(unsigned long addr,
- int numpages,
- bool encrypt)
+int __set_memory_enc_dec(unsigned long addr, int numpages, bool encrypt)
{
unsigned long set_prot = 0, clear_prot = 0;
phys_addr_t start, end;
@@ -321,40 +319,6 @@ static int __set_memory_enc_dec(unsigned long addr,
__pgprot(PTE_PRESENT_INVALID));
}
-static int realm_set_memory_encrypted(unsigned long addr, int numpages)
-{
- int ret = __set_memory_enc_dec(addr, numpages, true);
-
- /*
- * If the request to change state fails, then the only sensible cause
- * of action for the caller is to leak the memory
- */
- WARN(ret, "Failed to encrypt memory, %d pages will be leaked",
- numpages);
-
- return ret;
-}
-
-static int realm_set_memory_decrypted(unsigned long addr, int numpages)
-{
- int ret = __set_memory_enc_dec(addr, numpages, false);
-
- WARN(ret, "Failed to decrypt memory, %d pages will be leaked",
- numpages);
-
- return ret;
-}
-
-static const struct arm64_mem_crypt_ops realm_crypt_ops = {
- .encrypt = realm_set_memory_encrypted,
- .decrypt = realm_set_memory_decrypted,
-};
-
-int realm_register_memory_enc_ops(void)
-{
- return arm64_mem_crypt_ops_register(&realm_crypt_ops);
-}
-
int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid)
{
unsigned long addr = (unsigned long)page_address(page);
--
2.43.0
^ permalink raw reply related
* [PATCH v5 09/10] arm64: realm: Add RHI helper to query IPA state change alignment
From: Aneesh Kumar K.V (Arm) @ 2026-07-06 6:04 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel, iommu
Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Jason Gunthorpe,
Marc Zyngier, Marek Szyprowski, Robin Murphy, Steven Price,
Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <20260706060432.1375570-1-aneesh.kumar@kernel.org>
Arm CCA guests need to know the granularity at which the host expects IPA
state changes to be performed. This can be larger than the guest page size
and is needed when deciding the alignment for memory shared with the host.
Add the Realm Host Interface host configuration definitions and an
get_ipa_state_change_alignment() helper. The helper uses RSI_HOST_CALL to
query the supported HostConf version and features, reads the IPA change
alignment when available, and falls back to PAGE_SIZE if the interface is
unavailable or returns an invalid value.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rsi_cmds.h | 10 +++++++
arch/arm64/kernel/rsi.c | 45 +++++++++++++++++++++++++++++++
include/linux/arm-smccc-rhi.h | 25 +++++++++++++++++
include/linux/arm-smccc-rsi.h | 7 +++++
4 files changed, 87 insertions(+)
create mode 100644 include/linux/arm-smccc-rhi.h
diff --git a/arch/arm64/include/asm/rsi_cmds.h b/arch/arm64/include/asm/rsi_cmds.h
index 8537d0fd3da6..c890e2334a15 100644
--- a/arch/arm64/include/asm/rsi_cmds.h
+++ b/arch/arm64/include/asm/rsi_cmds.h
@@ -86,4 +86,14 @@ static inline long rsi_set_addr_range_state(phys_addr_t start,
return res.a0;
}
+static inline unsigned long rsi_host_call(struct rsi_host_call *rhi_call)
+{
+ phys_addr_t addr = virt_to_phys(rhi_call);
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RSI_HOST_CALL, addr, &res);
+
+ return res.a0;
+}
+
#endif /* __ASM_RSI_CMDS_H */
diff --git a/arch/arm64/kernel/rsi.c b/arch/arm64/kernel/rsi.c
index 5c566700974c..f52f240bb44c 100644
--- a/arch/arm64/kernel/rsi.c
+++ b/arch/arm64/kernel/rsi.c
@@ -11,6 +11,7 @@
#include <linux/platform_device.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
+#include <linux/arm-smccc-rhi.h>
#include <asm/io.h>
#include <asm/mem_encrypt.h>
@@ -177,6 +178,50 @@ static int realm_register_memory_enc_ops(void)
return arm64_mem_crypt_ops_register(&realm_crypt_ops);
}
+/* we need an aligned struct for rsi_host_call. slab is not yet ready */
+static struct rsi_host_call hostconf_call __initdata;
+static unsigned long __maybe_unused __init get_ipa_state_change_alignment(void)
+{
+ long ret;
+ unsigned long shared_granule_size;
+
+ hostconf_call.imm = 0;
+ hostconf_call.gprs[0] = RHI_HOSTCONF_VERSION;
+ ret = rsi_host_call(lm_alias(&hostconf_call));
+ if (ret != RSI_SUCCESS)
+ goto err_out;
+
+ if (hostconf_call.gprs[0] != RHI_HOSTCONF_VER_1_0)
+ goto err_out;
+
+ hostconf_call.imm = 0;
+ hostconf_call.gprs[0] = RHI_HOSTCONF_FEATURES;
+ ret = rsi_host_call(lm_alias(&hostconf_call));
+ if (ret != RSI_SUCCESS)
+ goto err_out;
+
+ if (!(hostconf_call.gprs[0] & __RHI_HOSTCONF_GET_IPA_CHANGE_ALIGNMENT))
+ goto err_out;
+
+ hostconf_call.imm = 0;
+ hostconf_call.gprs[0] = RHI_HOSTCONF_GET_IPA_CHANGE_ALIGNMENT;
+ ret = rsi_host_call(lm_alias(&hostconf_call));
+ if (ret != RSI_SUCCESS)
+ goto err_out;
+
+ shared_granule_size = hostconf_call.gprs[0];
+ if (shared_granule_size & (SZ_4K - 1))
+ goto err_out;
+
+ return max(PAGE_SIZE, shared_granule_size);
+err_out:
+ /*
+ * For failure condition assume host is built with 4K page size
+ * and hence IPA state change alignment can be guest PAGE_SIZE.
+ */
+ return PAGE_SIZE;
+}
+
void __init arm64_rsi_init(void)
{
if (arm_smccc_1_1_get_conduit() != SMCCC_CONDUIT_SMC)
diff --git a/include/linux/arm-smccc-rhi.h b/include/linux/arm-smccc-rhi.h
new file mode 100644
index 000000000000..91a29996d72d
--- /dev/null
+++ b/include/linux/arm-smccc-rhi.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2026 ARM Ltd.
+ */
+
+#ifndef __LINUX_ARM_SMCCC_RHI_H_
+#define __LINUX_ARM_SMCCC_RHI_H_
+
+#include <linux/arm-smccc.h>
+
+#define SMC_RHI_CALL(func) \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_64, \
+ ARM_SMCCC_OWNER_STANDARD_HYP,\
+ (func))
+
+#define RHI_HOSTCONF_VER_1_0 0x10000
+#define RHI_HOSTCONF_VERSION SMC_RHI_CALL(0x004E)
+
+#define __RHI_HOSTCONF_GET_IPA_CHANGE_ALIGNMENT BIT(0)
+#define RHI_HOSTCONF_FEATURES SMC_RHI_CALL(0x004F)
+
+#define RHI_HOSTCONF_GET_IPA_CHANGE_ALIGNMENT SMC_RHI_CALL(0x0050)
+
+#endif /* __LINUX_ARM_SMCCC_RHI_H_ */
diff --git a/include/linux/arm-smccc-rsi.h b/include/linux/arm-smccc-rsi.h
index ae663aa8fd7f..e33487ab000c 100644
--- a/include/linux/arm-smccc-rsi.h
+++ b/include/linux/arm-smccc-rsi.h
@@ -184,6 +184,13 @@ struct realm_config {
*/
#define SMC_RSI_IPA_STATE_GET SMC_RSI_FID(0x198)
+struct rsi_host_call {
+ union {
+ u16 imm;
+ u64 padding0;
+ };
+ u64 gprs[31];
+} __aligned(0x100);
/*
* Make a Host call.
*
--
2.43.0
^ permalink raw reply related
* [PATCH v5 10/10] arm64: realm: Expose the CCA shared granule size through mem_encrypt ops
From: Aneesh Kumar K.V (Arm) @ 2026-07-06 6:04 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel, iommu
Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Jason Gunthorpe,
Marc Zyngier, Marek Szyprowski, Robin Murphy, Steven Price,
Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <20260706060432.1375570-1-aneesh.kumar@kernel.org>
CCA guests must align shared/private memory transitions to the size
reported by the host for IPA state changes. This size can be larger than
PAGE_SIZE, so allowing a guest to convert only a PAGE_SIZE-sized subrange
can leave the remaining part of that host-sized range in the wrong state.
Cache the RHI-reported IPA sate change alignment during Realm
initialization and expose it through a new arm64 mem_encrypt callback. Use
PAGE_SIZE as the default shared granule size when no backend callback is
registered.
Validate both the address and byte size passed to set_memory_encrypted()
and set_memory_decrypted() against mem_cc_shared_granule_size() before
calling into the backend. This prevents callers from converting only part
of a host-managed page.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/mem_encrypt.h | 4 ++++
arch/arm64/kernel/rsi.c | 13 ++++++++++-
arch/arm64/mm/mem_encrypt.c | 32 ++++++++++++++++++++++++----
3 files changed, 44 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/include/asm/mem_encrypt.h b/arch/arm64/include/asm/mem_encrypt.h
index f6325f30e844..5018581fed87 100644
--- a/arch/arm64/include/asm/mem_encrypt.h
+++ b/arch/arm64/include/asm/mem_encrypt.h
@@ -9,6 +9,7 @@ struct device;
struct arm64_mem_crypt_ops {
int (*encrypt)(unsigned long addr, int numpages);
int (*decrypt)(unsigned long addr, int numpages);
+ size_t (*cc_shared_granule_size)(void);
};
int arm64_mem_crypt_ops_register(const struct arm64_mem_crypt_ops *ops);
@@ -17,6 +18,9 @@ int set_memory_encrypted(unsigned long addr, int numpages);
int set_memory_decrypted(unsigned long addr, int numpages);
int __set_memory_enc_dec(unsigned long addr, int numpages, bool encrypt);
+#define mem_cc_shared_granule_size mem_cc_shared_granule_size
+size_t mem_cc_shared_granule_size(void);
+
static inline bool force_dma_unencrypted(struct device *dev)
{
return is_realm_world();
diff --git a/arch/arm64/kernel/rsi.c b/arch/arm64/kernel/rsi.c
index f52f240bb44c..54fb929067c9 100644
--- a/arch/arm64/kernel/rsi.c
+++ b/arch/arm64/kernel/rsi.c
@@ -20,6 +20,7 @@
static struct realm_config config;
static struct kobject *cca_kobj;
+static unsigned long ipa_state_change_granule_size;
unsigned long prot_ns_shared;
EXPORT_SYMBOL(prot_ns_shared);
@@ -168,9 +169,17 @@ static int realm_set_memory_decrypted(unsigned long addr, int numpages)
return ret;
}
+static size_t realm_cc_shared_granule_size(void)
+{
+ if (is_realm_world())
+ return ipa_state_change_granule_size;
+ return PAGE_SIZE;
+}
+
static const struct arm64_mem_crypt_ops realm_crypt_ops = {
.encrypt = realm_set_memory_encrypted,
.decrypt = realm_set_memory_decrypted,
+ .cc_shared_granule_size = realm_cc_shared_granule_size,
};
static int realm_register_memory_enc_ops(void)
@@ -180,7 +189,7 @@ static int realm_register_memory_enc_ops(void)
/* we need an aligned struct for rsi_host_call. slab is not yet ready */
static struct rsi_host_call hostconf_call __initdata;
-static unsigned long __maybe_unused __init get_ipa_state_change_alignment(void)
+static unsigned long __init get_ipa_state_change_alignment(void)
{
long ret;
unsigned long shared_granule_size;
@@ -230,6 +239,8 @@ void __init arm64_rsi_init(void)
return;
if (WARN_ON(rsi_get_realm_config(lm_alias(&config))))
return;
+
+ ipa_state_change_granule_size = get_ipa_state_change_alignment();
prot_ns_shared = __phys_to_pte_val(BIT(config.ipa_bits - 1));
if (arm64_ioremap_prot_hook_register(realm_ioremap_hook))
diff --git a/arch/arm64/mm/mem_encrypt.c b/arch/arm64/mm/mem_encrypt.c
index ee3c0ab04384..69783c6a3c08 100644
--- a/arch/arm64/mm/mem_encrypt.c
+++ b/arch/arm64/mm/mem_encrypt.c
@@ -17,8 +17,7 @@
#include <linux/compiler.h>
#include <linux/err.h>
#include <linux/mm.h>
-
-#include <asm/mem_encrypt.h>
+#include <linux/mem_encrypt.h>
static const struct arm64_mem_crypt_ops *crypt_ops;
@@ -33,18 +32,43 @@ int arm64_mem_crypt_ops_register(const struct arm64_mem_crypt_ops *ops)
int set_memory_encrypted(unsigned long addr, int numpages)
{
- if (likely(!crypt_ops) || WARN_ON(!PAGE_ALIGNED(addr)))
+ unsigned long size = (unsigned long)numpages << PAGE_SHIFT;
+
+ if (likely(!crypt_ops))
return 0;
+ if (WARN_ON(!IS_ALIGNED(addr, mem_cc_shared_granule_size())))
+ return -EINVAL;
+
+ if (WARN_ON(!IS_ALIGNED(size, mem_cc_shared_granule_size())))
+ return -EINVAL;
+
return crypt_ops->encrypt(addr, numpages);
}
EXPORT_SYMBOL_GPL(set_memory_encrypted);
int set_memory_decrypted(unsigned long addr, int numpages)
{
- if (likely(!crypt_ops) || WARN_ON(!PAGE_ALIGNED(addr)))
+ unsigned long size = (unsigned long)numpages << PAGE_SHIFT;
+
+ if (likely(!crypt_ops))
return 0;
+ if (WARN_ON(!IS_ALIGNED(addr, mem_cc_shared_granule_size())))
+ return -EINVAL;
+
+ if (WARN_ON(!IS_ALIGNED(size, mem_cc_shared_granule_size())))
+ return -EINVAL;
+
return crypt_ops->decrypt(addr, numpages);
}
EXPORT_SYMBOL_GPL(set_memory_decrypted);
+
+size_t mem_cc_shared_granule_size(void)
+{
+ if (likely(!crypt_ops) || !crypt_ops->cc_shared_granule_size)
+ return PAGE_SIZE;
+
+ return crypt_ops->cc_shared_granule_size();
+}
+EXPORT_SYMBOL_GPL(mem_cc_shared_granule_size);
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v2 20/31] x86/virt/tdx: Add a helper to loop on TDX_INTERRUPTED_RESUMABLE
From: Xu Yilun @ 2026-07-06 9:16 UTC (permalink / raw)
To: Ackerley Tng
Cc: linux-coco, linux-pci, dan.j.williams, x86, chao.gao, dave.jiang,
baolu.lu, yilun.xu, zhenzhong.duan, kvm, rick.p.edgecombe,
dave.hansen, kas, xiaoyao.li, vishal.l.verma, linux-kernel
In-Reply-To: <CAEvNRgGgTkOPWELeg5U3pjjw9NZOBQ5zswWy=MRxOXsUXXuFhQ@mail.gmail.com>
> > +static u64 __maybe_unused __seamcall_ir_resched(sc_func_t sc_func, u64 fn,
> > + struct tdx_module_args *args)
> > +{
> > + struct tdx_module_args _args;
> > + u64 r;
> > +
> > + while (1) {
> > + _args = *(args);
>
> Is this copying meant to ensure that every retry has the same input
> args? Perhaps a comment should be added so it's obvious that every retry
> will have the same input, and outputs from each retry are dropped.
Yes, that's the intent.
But now I've found this is not a good convention for INTERRUPTED_RESUMABLE.
If outputs are necessary why we drop them, if outputs are not useful why
we output them to overwrite inputs. The fact is that TDX module only
outputs all 0's to zero out the inputs, that makes no sense.
So we now switch to another existing convention - if there is valid
output, use the output to update the input for next loop, or keep the input
unchanged. This way we only need to:
do {
r = seamcall(TDH_XX_XX, &args);
} while (r == TDX_INTERRUPTED_RESUMABLE);
if (r != TDX_SUCCESS)
return -EFAULT;
https://lore.kernel.org/kvm/20260618081355.3253581-6-yilun.xu@linux.intel.com/
https://lore.kernel.org/kvm/20260618081355.3253581-5-yilun.xu@linux.intel.com/
>
> > + r = sc_retry(sc_func, fn, &_args);
> > + if (r != TDX_INTERRUPTED_RESUMABLE)
> > + break;
> > +
> > + cond_resched();
> > + }
> > +
> > + *args = _args;
> > +
> > + return r;
> > +}
^ permalink raw reply
* Re: [PATCH 1/2] x86/tdx: Add helper to query maximum TD Quote size
From: Peter Fang @ 2026-07-06 9:18 UTC (permalink / raw)
To: Xiaoyao Li
Cc: Dave Hansen, Kiryl Shutsemau, Rick Edgecombe,
Kuppuswamy Sathyanarayanan, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, x86, H. Peter Anvin, linux-kernel, linux-coco,
kvm
In-Reply-To: <cdb43316-6535-4f11-9fb0-abdf2adf4568@intel.com>
On Thu, Jul 02, 2026 at 06:16:21PM +0800, Xiaoyao Li wrote:
> > >
> > > I have another nit other than Kiryl's
> > >
> > > > +u32 tdx_get_max_quote_size(void)
> > > > +{
> > > > + u64 val, ret;
> > > > +
> > > > + ret = tdg_vm_rd(TDCS_QUOTE_MAX_SIZE, &val);
> > > > +
> > > > + return ret ? 0 : (u32)val;
> > > > +}
> > > > +EXPORT_SYMBOL_GPL(tdx_get_max_quote_size);
> > >
> > > Do we need to start to use
> > >
> > > EXPORT_SYMBOL_FOR_MODULES(tdx_get_max_quote_size, "tdx-guest") ?
> > >
> >
> > This makes sense. But can we use a follow-up patch to improve this file
> > later? Right now there are only EXPORT_SYMBOL_GPL() usages, so using
> > EXPORT_SYMBOL_FOR_MODULES() here might look inconsistent.
>
> If maintainers is going to merge the series as-is, then fine.
>
> If we will have a v2, I don't see why cannot update to use
> EXPORT_SYMBOL_FOR_MODULES().
>
> The existing ones can be updated in a separate patch before or after.
Thanks. I will respin a v2 to address some review feedback. But I'd like
to hold off on this improvement unless there's a strong push to do it
right now. I'm going to add this to my list of guest-side cleanup items
for after this series, along with the feedback I got in [1].
[1] https://lore.kernel.org/linux-coco/5f9474ed-bacb-44d5-a0fc-5a29a1e79b60@intel.com/
^ permalink raw reply
* Re: [PATCH v10 3/6] x86/sev: Disable CPU hotplug while SNP is active
From: Jethro Beekman @ 2026-07-06 12:02 UTC (permalink / raw)
To: Kalra, Ashish, tglx, mingo, bp, dave.hansen, x86, hpa, seanjc,
peterz, thomas.lendacky, herbert, davem, ardb
Cc: pbonzini, aik, Michael.Roth, KPrateek.Nayak, Tycho.Andersen,
Nathan.Fontenot, ackerleytng, jackyli, pgonda, rientjes, jacobhxu,
xin, pawan.kumar.gupta, babu.moger, dyoung, nikunj, john.allen,
darwi, linux-kernel, linux-crypto, kvm, linux-coco
In-Reply-To: <5147d9bd-42f8-4ceb-aca4-6ac5fd5cb7f0@amd.com>
[-- Attachment #1: Type: text/plain, Size: 1770 bytes --]
On 2026-07-01 23:25, Kalra, Ashish wrote:
>
> On 7/1/2026 4:40 AM, Jethro Beekman wrote:
>> Hi Ashish,
>>
>> I don't believe my concern has been addressed
>>
>> https://lore.kernel.org/lkml/0df3b665-3a9c-4c46-a7aa-14388e8e1577@fortanix.com/
>>
>> --
>
> The disable tracks SNP_INIT, not "SNP" in general: SNP_INIT requires SnpEn to be set on all present CPUs, and a CPU brought online afterward wouldn't have it, so the kernel that runs SNP_INIT must keep its CPU set stable. Today the only kernel that runs SNP_INIT is the bare-metal host, so a plain L1 guest keeps full CPU hotplug.
>
> Concretely, the path is gated by CC_ATTR_HOST_SEV_SNP, which bsp_determine_snp() sets only when X86_FEATURE_HYPERVISOR is clear and clears otherwise
> (as Prateek pointed out). So a Linux L1 guest never has it set, never reaches snp_prepare()/snp_rmptable_init(), and keeps CPU hotplug —
> including while running SEV/SEV-ES confidential L2 guests. Only SNP initialization disables hotplug; the other SEV variants don't. And KVM doesn't expose
> SNP to L1, so an L1 can't be an SNP host today in any case.
>
> On the nested scenario you raised: if SNP-guest-as-L2 support is added, an L1 acting as an SNP host would run a *virtualized* SNP_INIT. A faithful virtualization carries the same constraint as physical SNP_INIT — all present (v)CPUs must be SnpEn — so that L1 would have the same (v)CPU-hotplug-disable requirement, just over its virtual CPUs, and this same code would apply at that level. So the disable isn't too broad; it correctly tracks SNP_INIT. It simply doesn't apply to a plain L1 guest today, because such a guest isn't running SNP_INIT.
Thanks, Ashish, Prateek, for the clarification.
--
Jethro Beekman | CTO | Fortanix
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4839 bytes --]
^ permalink raw reply
* Re: [PATCH v6 03/11] x86/virt/tdx: Add tdx_alloc/free_control_page() helpers
From: Chao Gao @ 2026-07-06 12:31 UTC (permalink / raw)
To: Rick Edgecombe
Cc: bp, dave.hansen, hpa, kas, kvm, linux-coco, linux-doc,
linux-kernel, mingo, nik.borisov, pbonzini, seanjc, tglx,
vannapurve, x86, yan.y.zhao, kai.huang, Kirill A. Shutemov
In-Reply-To: <20260526023515.288829-4-rick.p.edgecombe@intel.com>
On Mon, May 25, 2026 at 07:35:07PM -0700, Rick Edgecombe wrote:
>From: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>
>
>Add helpers to use when allocating or preparing pages that are handed to
>the TDX-Module for use as control/S-EPT pages, and thus need Dynamic PAMT
For the whole series: s/TDX-Module/TDX module, to match the existing
convention.
>+/*
>+ * Calculate the arg needed for operating on the DPAMT backing for
>+ * a given 4KB page.
>+ */
>+static u64 pamt_2mb_arg(kvm_pfn_t pfn)
>+{
>+ unsigned long hpa_2mb = ALIGN_DOWN(pfn << PAGE_SHIFT, PMD_SIZE);
The changelog and the comment don't explain why the pfn is aligned down to a
2MB boundary. And ...
>+
>+ return hpa_2mb | TDX_PS_2M;
>+}
>+
>+/* Add PAMT backing for the given page. */
... this adds PAMT backing for the whole 2MB region containing the given page,
not just the given page itself. Is it worth clarifying in the comment?
<snip>
>+/*
>+ * Return a page that can be gifted to the TDX-Module for use as a "control"
>+ * page, i.e. pages that are used for control and S-EPT structures for a given
>+ * TDX guest, and bound to said guest's HKID and thus obtain TDX protections,
>+ * including PAMT tracking.
>+ */
IIUC, S-EPT structures are not allocated through this helper. Why mention it?
>+struct page *tdx_alloc_control_page(void)
>+{
>+ struct page *page;
>+
>+ page = alloc_page(GFP_KERNEL_ACCOUNT);
>+ if (!page)
>+ return NULL;
>+
>+ if (tdx_pamt_get(page_to_pfn(page))) {
>+ __free_page(page);
>+ return NULL;
>+ }
>+
>+ return page;
>+}
>+EXPORT_SYMBOL_FOR_KVM(tdx_alloc_control_page);
>+
>+/*
>+ * Free a page that was gifted to the TDX-Module for use as a control/S-EPT
>+ * page. After this, the page is no longer protected by TDX.
>+ */
Ditto.
>+void tdx_free_control_page(struct page *page)
>+{
>+ if (!page)
>+ return;
>+
>+ tdx_pamt_put(page_to_pfn(page));
>+ __free_page(page);
>+}
>+EXPORT_SYMBOL_FOR_KVM(tdx_free_control_page);
With above nits fixed,
Reviewed-by: Chao Gao <chao.gao@intel.com>
^ permalink raw reply
* Re: [PATCH 13/15] PCI, device core: Add private memory access for DEVICE_TRUST_TCB
From: Aneesh Kumar K.V @ 2026-07-06 12:42 UTC (permalink / raw)
To: Dan Williams, linux-coco
Cc: linux-pci, driver-core, ankita, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Bjorn Helgaas, Dexuan Cui
In-Reply-To: <20260705220819.2472765-14-djbw@kernel.org>
Dan Williams <djbw@kernel.org> writes:
> A device that wants to access private memory needs to have its trust
> elevated to DEVICE_TRUST_TCB. That trust is established either at compile
> time (unlikely), the bus knows the device is within the TCB to start (some
> paravisor setups), or the device is dynamically added to the TCB in
> coordination with a TSM driver (primary TDISP use case) and the trust is
> elevated by driver match.
>
Do we have the last case, where the device is dynamically added to the
TCB in coordination with a TSM, implemented in this series? Do we expect
the CCA driver to set that up?
>
> When a PCI device is associated with a TSM for security services the low
> level TSM driver in the CC VM has the opportunity validate DMA access.
> That validation happens at ->dma_configure() time when the device attaches
> to a driver. The TSM driver is responsible for proving to the platform TSM
> that the VM is enabling DMA with respect to the most recently generated
> evidence. If that fails, driver attach fails.
>
> When a PCI device is not associated with a TSM provider for security
> services, but the device is trusted there are 3 options.
>
> 1/ Arch requires all DMA enable events to be acked by TSM driver
>
> 2/ Arch does not require, but admin policy is responsible for knowing which
> devices need TSM coordination to become active within the TCB.
>
> 3/ Device is approved by a paravisor to operate within the TCB, no TSM
> coordination required.
>
> In cases 2 and 3 if the device needed TSM driver coordination, but the TSM
> driver or association to the device is missing, it triggers hardware
> errors. Those errors are a configuration error that the kernel does not
> actively prevent.
>
> Architectures still need to fixup force_dma_unencrypted() to call
> device_tcb_trusted() to tell the DMA layer that TCB access is granted.
>
-aneesh
^ permalink raw reply
* Re: [PATCH 00/15] Device Evidence and Trust for PCI Security Protocol (TDISP)
From: Jason Gunthorpe @ 2026-07-06 12:51 UTC (permalink / raw)
To: Dan Williams
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: <20260705220819.2472765-1-djbw@kernel.org>
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.
Arguably the T=0/1 decision is much more like link encryption, it
effects the transport of the DMA into the CPU. That it also impacts
how the VIOMMU works is the only thing that make it sort of
trust-like in this model.
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.
In a CC VM if we have a T=0 device we probably want to operate it with
ADVERSARY (there is no T=0 VIOMMU so this is equivilent to FULL)
For a T=1 device we need to have the choice of FULL or ADVERSARY. When
a VIOMMU is present ADVERSARY will further restrict the T=1 traffic as
a defense in depth.
So, it is really necessary to have "TCB" here?
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. The kernel would
have to set the trust to NONE when security sensitive changes are
detected.
If we still want a kernel-side policy gate, eg kernel will not
progress unless T=1 is met, then I think that would be better as an
independent pair'd policy field, ie
trust=FULL when policy=T=1, LINK_ENCRYPTION=1, etc
As bundling the two into "TCB" is probably going to turn problematic
as we have more and more combinations of policy conditions.
Jason
^ permalink raw reply
* Re: [PATCH v7 1/6] firmware: smccc: Add an Arm SMCCC bus
From: Sudeep Holla @ 2026-07-06 13:18 UTC (permalink / raw)
To: Aneesh Kumar K.V (Arm)
Cc: linux-coco, linux-arm-kernel, linux-kernel, Sudeep Holla,
Catalin Marinas, Greg KH, Jeremy Linton, Jonathan Cameron,
Lorenzo Pieralisi, Mark Rutland, Will Deacon, Steven Price,
Suzuki K Poulose, Andre Przywara
In-Reply-To: <20260611130429.295516-2-aneesh.kumar@kernel.org>
On Thu, Jun 11, 2026 at 06:34:24PM +0530, Aneesh Kumar K.V (Arm) wrote:
> SMCCC-discovered firmware services are currently represented by separate
> platform devices, such as smccc_trng and arm-cca-dev. Those devices do not
> represent independent DT/ACPI-described platform resources; they are
> features of the SMCCC firmware interface.
>
> Add an Arm SMCCC bus for services discovered through the SMCCC firmware
> interface. The bus provides SMCCC device and driver registration helpers,
> name-based matching, modalias generation, and a sysfs modalias attribute so
> SMCCC service drivers can bind to discovered firmware services and autoload
> as modules.
>
> Follow-up changes can then register SMCCC firmware services as arm-smccc
> devices instead of creating independent per-feature platform devices.
>
> Based on arm_ffa code
>
> Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
> ---
> drivers/firmware/smccc/Makefile | 2 +-
> drivers/firmware/smccc/bus.c | 164 ++++++++++++++++++++++++++++++
> include/linux/arm-smccc-bus.h | 49 +++++++++
> include/linux/mod_devicetable.h | 13 +++
> scripts/mod/devicetable-offsets.c | 3 +
> scripts/mod/file2alias.c | 8 ++
> 6 files changed, 238 insertions(+), 1 deletion(-)
> create mode 100644 drivers/firmware/smccc/bus.c
> create mode 100644 include/linux/arm-smccc-bus.h
>
> diff --git a/drivers/firmware/smccc/Makefile b/drivers/firmware/smccc/Makefile
> index 40d19144a860..68bbff1407b8 100644
> --- a/drivers/firmware/smccc/Makefile
> +++ b/drivers/firmware/smccc/Makefile
> @@ -1,4 +1,4 @@
> # SPDX-License-Identifier: GPL-2.0
> #
> -obj-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smccc.o kvm_guest.o
> +obj-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += bus.o smccc.o kvm_guest.o
> obj-$(CONFIG_ARM_SMCCC_SOC_ID) += soc_id.o
> diff --git a/drivers/firmware/smccc/bus.c b/drivers/firmware/smccc/bus.c
> new file mode 100644
> index 000000000000..fe7e893130ce
> --- /dev/null
> +++ b/drivers/firmware/smccc/bus.c
> @@ -0,0 +1,164 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2026 Arm Limited
> + */
> +
> +#include <linux/arm-smccc-bus.h>
> +#include <linux/idr.h>
> +#include <linux/slab.h>
> +
> +static DEFINE_IDA(arm_smccc_bus_id);
> +
> +static int arm_smccc_bus_match(struct device *dev,
> + const struct device_driver *drv)
> +{
> + const struct arm_smccc_device_id *id_table;
> + struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev);
> +
> + id_table = to_arm_smccc_driver(drv)->id_table;
> + if (!id_table)
> + return 0;
> +
Any reason for not having this check when registering the driver also so
that it becomes a must to register ? I remember vaguely adding this as fix to
FF-A driver recently.
> + while (id_table->name[0]) {
> + if (!strcmp(smccc_dev->name, id_table->name))
> + return 1;
> + id_table++;
> + }
> +
> + return 0;
> +}
> +
> +static int arm_smccc_bus_probe(struct device *dev)
> +{
> + struct arm_smccc_driver *smccc_drv = to_arm_smccc_driver(dev->driver);
> +
> + return smccc_drv->probe(to_arm_smccc_device(dev));
> +}
> +
> +static void arm_smccc_bus_remove(struct device *dev)
> +{
> + struct arm_smccc_driver *smcc_drv = to_arm_smccc_driver(dev->driver);
> +
> + if (smcc_drv->remove)
> + smcc_drv->remove(to_arm_smccc_device(dev));
> +}
> +
> +static int arm_smccc_bus_uevent(const struct device *dev,
> + struct kobj_uevent_env *env)
> +{
> + const struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev);
> +
> + return add_uevent_var(env, "MODALIAS=" ARM_SMCCC_MODULE_PREFIX "%s",
> + smccc_dev->name);
> +}
> +
> +static ssize_t modalias_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev);
> +
> + return sysfs_emit(buf, ARM_SMCCC_MODULE_PREFIX "%s\n", smccc_dev->name);
> +}
> +static DEVICE_ATTR_RO(modalias);
> +
> +static struct attribute *arm_smccc_device_attrs[] = {
> + &dev_attr_modalias.attr,
> + NULL,
> +};
> +ATTRIBUTE_GROUPS(arm_smccc_device);
> +
> +const struct bus_type arm_smccc_bus_type = {
> + .name = "arm_smccc",
> + .match = arm_smccc_bus_match,
> + .probe = arm_smccc_bus_probe,
> + .remove = arm_smccc_bus_remove,
> + .uevent = arm_smccc_bus_uevent,
> + .dev_groups = arm_smccc_device_groups,
> +};
> +EXPORT_SYMBOL_GPL(arm_smccc_bus_type);
> +
> +int arm_smccc_driver_register(struct arm_smccc_driver *driver,
> + struct module *owner, const char *mod_name)
> +{
> + if (!driver->probe)
> + return -EINVAL;
> +
> + driver->driver.bus = &arm_smccc_bus_type;
> + driver->driver.name = driver->name;
> + driver->driver.owner = owner;
> + driver->driver.mod_name = mod_name;
> +
> + return driver_register(&driver->driver);
> +}
> +EXPORT_SYMBOL_GPL(arm_smccc_driver_register);
> +
> +void arm_smccc_driver_unregister(struct arm_smccc_driver *driver)
> +{
> + driver_unregister(&driver->driver);
> +}
> +EXPORT_SYMBOL_GPL(arm_smccc_driver_unregister);
> +
> +static void arm_smccc_release_device(struct device *dev)
> +{
> + struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev);
> +
> + ida_free(&arm_smccc_bus_id, smccc_dev->id);
> + kfree(smccc_dev);
> +}
> +
> +struct arm_smccc_device *arm_smccc_device_register(const char *name)
> +{
> + struct arm_smccc_device *smccc_dev;
> + int id, ret;
> +
> + id = ida_alloc_min(&arm_smccc_bus_id, 1, GFP_KERNEL);
> + if (id < 0)
> + return ERR_PTR(id);
> +
> + smccc_dev = kzalloc_obj(*smccc_dev);
> + if (!smccc_dev) {
> + ida_free(&arm_smccc_bus_id, id);
> + return ERR_PTR(-ENOMEM);
> + }
> +
> + smccc_dev->id = id;
> + if (strscpy(smccc_dev->name, name) < 0) {
Since this is exported symbol and one can call arm_smccc_device_register(ptr)
where ptr = NULL, won't it blow up then ? IIUC strscpy() handles NULL dst
but doesn't check for NULL src.
> + kfree(smccc_dev);
> + ida_free(&arm_smccc_bus_id, id);
> + return ERR_PTR(-EINVAL);
> + }
> + smccc_dev->dev.bus = &arm_smccc_bus_type;
> + smccc_dev->dev.release = arm_smccc_release_device;
> +
> + ret = dev_set_name(&smccc_dev->dev, "%s-%d", smccc_dev->name, id);
> + if (ret) {
> + kfree(smccc_dev);
> + ida_free(&arm_smccc_bus_id, id);
> + return ERR_PTR(ret);
> + }
> +
> + ret = device_register(&smccc_dev->dev);
> + if (ret) {
> + put_device(&smccc_dev->dev);
> + return ERR_PTR(ret);
> + }
> +
> + return smccc_dev;
> +}
> +EXPORT_SYMBOL_GPL(arm_smccc_device_register);
> +
> +void arm_smccc_device_unregister(struct arm_smccc_device *smccc_dev)
> +{
> + if (!smccc_dev)
> + return;
> +
> + device_unregister(&smccc_dev->dev);
> +}
> +EXPORT_SYMBOL_GPL(arm_smccc_device_unregister);
> +
> +static int __init arm_smccc_bus_init(void)
> +{
> + return bus_register(&arm_smccc_bus_type);
> +}
> +subsys_initcall(arm_smccc_bus_init);
> +
> diff --git a/include/linux/arm-smccc-bus.h b/include/linux/arm-smccc-bus.h
> new file mode 100644
> index 000000000000..188891441e57
> --- /dev/null
> +++ b/include/linux/arm-smccc-bus.h
> @@ -0,0 +1,49 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2026 Arm Limited
> + */
> +#ifndef __LINUX_ARM_SMCCC_BUS_H
> +#define __LINUX_ARM_SMCCC_BUS_H
> +
> +#include <linux/device.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +
> +struct arm_smccc_device {
> + int id;
> + char name[ARM_SMCCC_NAME_SIZE];
> + struct device dev;
> +};
> +
> +#define to_arm_smccc_device(d) container_of(d, struct arm_smccc_device, dev)
> +
> +struct arm_smccc_driver {
> + const char *name;
> + int (*probe)(struct arm_smccc_device *sdev);
> + void (*remove)(struct arm_smccc_device *sdev);
> + const struct arm_smccc_device_id *id_table;
> +
> + struct device_driver driver;
> +};
> +
> +#define to_arm_smccc_driver(d) \
> + container_of_const(d, struct arm_smccc_driver, driver)
> +
> +int arm_smccc_driver_register(struct arm_smccc_driver *driver,
> + struct module *owner, const char *mod_name);
> +void arm_smccc_driver_unregister(struct arm_smccc_driver *driver);
> +struct arm_smccc_device *arm_smccc_device_register(const char *name);
> +void arm_smccc_device_unregister(struct arm_smccc_device *smcc_dev);
> +
I may be overthinking but what will happen if HAVE_ARM_SMCCC_DISCOVERY=n
and some driver is compiled using this header ? It should be fine if it
fails to compile, just thinking out loud if we need to handle that are not.
As long as all the drivers using these depends on HAVE_ARM_SMCCC_DISCOVERY
it should be fine I think.
> +#define arm_smccc_register(driver) \
> + arm_smccc_driver_register(driver, THIS_MODULE, KBUILD_MODNAME)
> +#define arm_smccc_unregister(driver) \
> + arm_smccc_driver_unregister(driver)
> +
> +#define module_arm_smccc_driver(__arm_smccc_driver) \
> + module_driver(__arm_smccc_driver, arm_smccc_register, \
> + arm_smccc_unregister)
> +
> +extern const struct bus_type arm_smccc_bus_type;
> +
> +#endif /* __LINUX_ARM_SMCCC_BUS_H */
> diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
> index 23ff24080dfd..c9cee8c5a0b2 100644
> --- a/include/linux/mod_devicetable.h
> +++ b/include/linux/mod_devicetable.h
This file seems to be reworked recently, so you need to rebase it moving
smccc specific changes to separate file I think.
> @@ -876,6 +876,19 @@ struct auxiliary_device_id {
> kernel_ulong_t driver_data;
> };
>
> +#define ARM_SMCCC_NAME_SIZE 40
> +#define ARM_SMCCC_MODULE_PREFIX "arm_smccc:"
> +
> +/**
> + * struct arm_smccc_device_id - Arm SMCCC bus device identifier
> + * @name: SMCCC device name
> + * @driver_data: driver data
> + */
> +struct arm_smccc_device_id {
> + char name[ARM_SMCCC_NAME_SIZE];
> + kernel_ulong_t driver_data;
Can't find any users of the above driver_data ?
Is it for future ? If so, can you add details on how it is supposed to be used
if you don't want to drop it.
> +};
> +
> /* Surface System Aggregator Module */
>
> #define SSAM_MATCH_TARGET 0x1
> diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c
> index b4178c42d08f..a485011ff137 100644
> --- a/scripts/mod/devicetable-offsets.c
> +++ b/scripts/mod/devicetable-offsets.c
> @@ -254,6 +254,9 @@ int main(void)
> DEVID(auxiliary_device_id);
> DEVID_FIELD(auxiliary_device_id, name);
>
> + DEVID(arm_smccc_device_id);
> + DEVID_FIELD(arm_smccc_device_id, name);
> +
> DEVID(ssam_device_id);
> DEVID_FIELD(ssam_device_id, match_flags);
> DEVID_FIELD(ssam_device_id, domain);
> diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
> index 2ad87a74bb03..92d3917f27cc 100644
> --- a/scripts/mod/file2alias.c
> +++ b/scripts/mod/file2alias.c
> @@ -1323,6 +1323,13 @@ static void do_auxiliary_entry(struct module *mod, void *symval)
> module_alias_printf(mod, false, AUXILIARY_MODULE_PREFIX "%s", *name);
> }
>
> +static void do_arm_smccc_entry(struct module *mod, void *symval)
> +{
> + DEF_FIELD_ADDR(symval, arm_smccc_device_id, name);
> +
> + module_alias_printf(mod, false, ARM_SMCCC_MODULE_PREFIX "%s", *name);
> +}
> +
> /*
> * Looks like: ssam:dNcNtNiNfN
> *
> @@ -1493,6 +1500,7 @@ static const struct devtable devtable[] = {
> {"mhi", SIZE_mhi_device_id, do_mhi_entry},
> {"mhi_ep", SIZE_mhi_device_id, do_mhi_ep_entry},
> {"auxiliary", SIZE_auxiliary_device_id, do_auxiliary_entry},
> + {"arm_smccc", SIZE_arm_smccc_device_id, do_arm_smccc_entry},
> {"ssam", SIZE_ssam_device_id, do_ssam_entry},
> {"dfl", SIZE_dfl_device_id, do_dfl_entry},
> {"ishtp", SIZE_ishtp_device_id, do_ishtp_entry},
> --
> 2.43.0
>
--
Regards,
Sudeep
^ permalink raw reply
* Re: [PATCH v6 04/11] x86/virt/tdx: Allocate ref counts for Dynamic PAMT memory
From: Chao Gao @ 2026-07-06 13:22 UTC (permalink / raw)
To: Rick Edgecombe
Cc: bp, dave.hansen, hpa, kas, kvm, linux-coco, linux-doc,
linux-kernel, mingo, nik.borisov, pbonzini, seanjc, tglx,
vannapurve, x86, yan.y.zhao, kai.huang, Kirill A. Shutemov
In-Reply-To: <20260526023515.288829-5-rick.p.edgecombe@intel.com>
>+/*
>+ * Allocate PAMT reference counters for all physical memory.
>+ *
>+ * It consumes 2MiB for every 1TiB of physical memory.
>+ */
>+static int init_pamt_refcounts(void)
>+{
>+ size_t size = DIV_ROUND_UP(max_pfn, PTRS_PER_PTE) * sizeof(*pamt_refcounts);
>+
>+ if (!tdx_supports_dynamic_pamt(&tdx_sysinfo))
>+ return 0;
>+
>+ pamt_refcounts = __vmalloc(size, GFP_KERNEL | __GFP_ZERO);
>+ if (!pamt_refcounts)
>+ return -ENOMEM;
>+
>+ return 0;
>+}
>+
>+static void free_pamt_refcounts(void)
>+{
>+ if (!tdx_supports_dynamic_pamt(&tdx_sysinfo))
>+ return;
>+
>+ vfree(pamt_refcounts);
>+ pamt_refcounts = NULL;
>+}
Both functions are only called during boot. They can be annotated with __init.
Reviewed-by: Chao Gao <chao.gao@intel.com>
^ permalink raw reply
* Re: [PATCH v5 02/10] irqchip/gic-v3-its: Align shared ITS allocations to the CoCo shared granule size
From: Marc Zyngier @ 2026-07-06 13:35 UTC (permalink / raw)
To: Aneesh Kumar K.V (Arm)
Cc: linux-coco, kvmarm, linux-arm-kernel, linux-kernel, iommu,
Catalin Marinas, Jason Gunthorpe, Marek Szyprowski, Robin Murphy,
Steven Price, Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <20260706060432.1375570-3-aneesh.kumar@kernel.org>
On Mon, 06 Jul 2026 07:04:24 +0100,
"Aneesh Kumar K.V (Arm)" <aneesh.kumar@kernel.org> wrote:
>
> ITS tables allocated by the coco guest are shared with the hypervisor.
> These allocations must satisfy the host shared-buffer granule size so that
> the full converted range is safe for host access.
>
> Allocate ITS pages using a size rounded up to the shared granule size and
> use the same allocation order when encrypting, decrypting and freeing the
> memory. Also grow the ITT cache in shared-granule sized chunks instead of
> assuming PAGE_SIZE is sufficient.
>
> Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
> ---
> drivers/irqchip/irq-gic-v3-its.c | 17 +++++++++++------
> 1 file changed, 11 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
> index b57d81ad33a0..5d6d38aa0dae 100644
> --- a/drivers/irqchip/irq-gic-v3-its.c
> +++ b/drivers/irqchip/irq-gic-v3-its.c
> @@ -213,16 +213,17 @@ static gfp_t gfp_flags_quirk;
> static struct page *its_alloc_pages_node(int node, gfp_t gfp,
> unsigned int order)
> {
> + unsigned int alloc_order;
> struct page *page;
> int ret = 0;
>
> - page = alloc_pages_node(node, gfp | gfp_flags_quirk, order);
> -
> + alloc_order = get_order(mem_cc_align_to_shared_granule(PAGE_SIZE << order));
> + page = alloc_pages_node(node, gfp | gfp_flags_quirk, alloc_order);
> if (!page)
> return NULL;
>
> ret = set_memory_decrypted((unsigned long)page_address(page),
> - 1 << order);
> + 1 << alloc_order);
> /*
> * If set_memory_decrypted() fails then we don't know what state the
> * page is in, so we can't free it. Instead we leak it.
> @@ -241,13 +242,16 @@ static struct page *its_alloc_pages(gfp_t gfp, unsigned int order)
>
> static void its_free_pages(void *addr, unsigned int order)
> {
> + int alloc_order;
> +
> + alloc_order = get_order(mem_cc_align_to_shared_granule(PAGE_SIZE << order));
> /*
> * If the memory cannot be encrypted again then we must leak the pages.
> * set_memory_encrypted() will already have WARNed.
> */
> - if (set_memory_encrypted((unsigned long)addr, 1 << order))
> + if (set_memory_encrypted((unsigned long)addr, 1 << alloc_order))
> return;
> - free_pages((unsigned long)addr, order);
> + free_pages((unsigned long)addr, alloc_order);
The comments I had in [1] are still applicable.
M.
[1] https://lore.kernel.org/all/86zf2ozrb8.wl-maz@kernel.org/
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply
* Re: [PATCH v5 02/10] irqchip/gic-v3-its: Align shared ITS allocations to the CoCo shared granule size
From: Aneesh Kumar K.V @ 2026-07-06 13:42 UTC (permalink / raw)
To: Marc Zyngier
Cc: linux-coco, kvmarm, linux-arm-kernel, linux-kernel, iommu,
Catalin Marinas, Jason Gunthorpe, Marek Szyprowski, Robin Murphy,
Steven Price, Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <86ik6sp7fc.wl-maz@kernel.org>
Marc Zyngier <maz@kernel.org> writes:
> On Mon, 06 Jul 2026 07:04:24 +0100,
> "Aneesh Kumar K.V (Arm)" <aneesh.kumar@kernel.org> wrote:
>>
>> ITS tables allocated by the coco guest are shared with the hypervisor.
>> These allocations must satisfy the host shared-buffer granule size so that
>> the full converted range is safe for host access.
>>
>> Allocate ITS pages using a size rounded up to the shared granule size and
>> use the same allocation order when encrypting, decrypting and freeing the
>> memory. Also grow the ITT cache in shared-granule sized chunks instead of
>> assuming PAGE_SIZE is sufficient.
>>
>> Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
>> ---
>> drivers/irqchip/irq-gic-v3-its.c | 17 +++++++++++------
>> 1 file changed, 11 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
>> index b57d81ad33a0..5d6d38aa0dae 100644
>> --- a/drivers/irqchip/irq-gic-v3-its.c
>> +++ b/drivers/irqchip/irq-gic-v3-its.c
>> @@ -213,16 +213,17 @@ static gfp_t gfp_flags_quirk;
>> static struct page *its_alloc_pages_node(int node, gfp_t gfp,
>> unsigned int order)
>> {
>> + unsigned int alloc_order;
>> struct page *page;
>> int ret = 0;
>>
>> - page = alloc_pages_node(node, gfp | gfp_flags_quirk, order);
>> -
>> + alloc_order = get_order(mem_cc_align_to_shared_granule(PAGE_SIZE << order));
>> + page = alloc_pages_node(node, gfp | gfp_flags_quirk, alloc_order);
>> if (!page)
>> return NULL;
>>
>> ret = set_memory_decrypted((unsigned long)page_address(page),
>> - 1 << order);
>> + 1 << alloc_order);
>> /*
>> * If set_memory_decrypted() fails then we don't know what state the
>> * page is in, so we can't free it. Instead we leak it.
>> @@ -241,13 +242,16 @@ static struct page *its_alloc_pages(gfp_t gfp, unsigned int order)
>>
>> static void its_free_pages(void *addr, unsigned int order)
>> {
>> + int alloc_order;
>> +
>> + alloc_order = get_order(mem_cc_align_to_shared_granule(PAGE_SIZE << order));
>> /*
>> * If the memory cannot be encrypted again then we must leak the pages.
>> * set_memory_encrypted() will already have WARNed.
>> */
>> - if (set_memory_encrypted((unsigned long)addr, 1 << order))
>> + if (set_memory_encrypted((unsigned long)addr, 1 << alloc_order))
>> return;
>> - free_pages((unsigned long)addr, order);
>> + free_pages((unsigned long)addr, alloc_order);
>
> The comments I had in [1] are still applicable.
>
> M.
>
> [1] https://lore.kernel.org/all/86zf2ozrb8.wl-maz@kernel.org/
>
I renamed it to alloc_order to make it clear that it refers to the
allocation order rather than the requested order. If you feel strongly
about not introducing another variable, I'll drop the new variable.
-aneesh
^ permalink raw reply
* Re: [PATCH 08/15] device core: Initial device trust infrastructure
From: Jason Gunthorpe @ 2026-07-06 13:45 UTC (permalink / raw)
To: Dan Williams
Cc: linux-coco, linux-pci, driver-core, ankita, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin, Bjorn Helgaas,
Samuel Ortiz, Alexey Kardashevskiy, Xu Yilun, Aneesh Kumar K.V,
Dexuan Cui
In-Reply-To: <20260705220819.2472765-9-djbw@kernel.org>
On Sun, Jul 05, 2026 at 03:08:12PM -0700, Dan Williams wrote:
> Introduce a per-device trust concept [1]. To start, it plumbs "auto" and
> "none" levels to enable a scheme of requiring explicit opt-in to operate a
> device. This supports a Confidential Computing use case of operating a
> limited / vetted device and driver set to bootstrap the environment. It
> leans on module policy to effect changes to device's trust level.
>
> The motivations for using module policy to effect trust are:
>
> 1/ It exists, no new ABI needed to inject "device firewall" policy into the
> kernel [2].
> 2/ Trust is a function of how the device is operated. A driver can be
> trusted to carefully operate an untrusted device.
>
> It may prove too coarse for some situations, like a driver that talks to a
> mix of trusted and untrusted devices, or a module that contains multiple
> drivers, but it is a place to start.
I'm a little confused what this is is for?
Is this how we bootstrap enough drivers to get into the initrd? Ie
is it only relavent for key boot time things like CLOCK, IRQ and IOMMU
drivers?
Once we get to the initrd eveything should be driven on a per-device
basis without anything to do with modules. We already have cases for
mlx5 where it will have to drive trusted and untrusted devices in the
same system.
> Specifically this "module trust" scheme allows for only attaching drivers
> from built-in modules by default, or modules that explicitly pass a
> "trust=auto" option by building with CONFIG_DEVICE_TRUST_NONE=y. Modules
> can also be configured to skip device attach by default by setting
> /sys/module/module/parameters/require_trust.
So I'd imagine most systems would make all modules have
require_trust=1 and some userspace compoment in the initrd will
evaluate and assign trust to devices to trigger binding and module
loading?
> The new dev->p->trust level is the operational trust level of a device. It is
> only in effect after device_add(). It is private to the device core because
> the "tcb" level is not suitable to be changed while a driver is attached to
> the device. The result force_dma_unencrypted() must be stable while the
> device is attached to its driver. That core coordination responsibility is
> not suitable to export to non-core code.
I don't think force_dma_unencrypted() should be driven from the trust
level.. It is a property of the interconnect, the trust level sits on
top of that.
> +config DEVICE_TRUST
> + bool "Device core support for specifying trust levels for devices"
> + help
> + Enable support for generic device trust levels when building a kernel
> + that needs to operate in the presence of potentially adversarial
> + devices. This is selected by buses that want to operate devices with
> + reduced privileges, like externally connected devices, and it is a
> + pre-requisite for operating devices with access to private memory in a
> + Confidential Computing VM. See
> + Documentation/ABI/testing/sysfs-kernel-iommu_groups for the type of
> + IOMMU enforcement in effect.
> +
> +choice
> + prompt "Default device trust"
> + default DEVICE_TRUST_AUTO
> + depends on DEVICE_TRUST
> + help
> + Specify the device trust level at initial attachment. Any choice other
> + than "Auto" assumes auditing the built-in driver set for trusted
> + drivers, and an enlightened userspace modprobe policy for other
> + devices + drivers.
> +
> +config DEVICE_TRUST_NONE
> + bool "None"
> + help
> + Devices are disallowed from attaching to a driver, and where
> + possible, the device is blocked by an IOMMU from accessing
> + assets.
It is a confusing what devices this applies to? I guess it is all
devices that are not built in?
I think it should also be in a kernel commandline, we should be able
to support a single kernel image that works for CC VM and non CC VM, I
think. You'd want the CC VM to have a kernel command line to turn on
some of this stuff.
> +choice
> + prompt "Trust devices with built-in drivers"
> + default BUILTIN_DEVICE_TRUST_AUTO
> + depends on DEVICE_TRUST
> + help
> + Built-in drivers always bind to devices they match. Only
> + select a setting other than "Auto" when building a kernel image
> + targeted for use in Confidential Computing or other known
> + adversarial environments.
> +
> +config BUILTIN_DEVICE_TRUST_AUTO
> + bool "Auto"
> + help
> + Typical historical driver model, devices eagerly attempt to attach to
> + a driver and deploy all available mechanisms to allow performant
> + direct memory access This trust level does not grant TCB privileges.
So what's the other choice?
Is this "AUTO" or is it just all builtin devices?
I think it does grant TCB privileges in some cases, ie the VIOMMU
driver that binds is in the TCB and does have T=1 equivilent DMA
access.
What we want for a CC VM is a curated set of pre-initrd devices can
auto probe:
- Drivers internally have some kind of attestation, eg on ARM the
VIOMMU driver is going to have to ask the RMM to prove the VIOMMU is
legitimate
- Drivers that don't do any DMA at all, and have approved drivers
- Drivers that are known to be hardened for untrusted DMA
Maybe these options should be named a bit differently?
config DEVICE_TRUST <-- If the device trust framework is compiled in at all.
DEVICE_TRUST_BUILTIN_DEFAULT = ALL, AUTO
Decide how pre-initrd devices are handled. ALL is current Linux,
every built in driver binds and runs, AUTO follows some module policy?
DEVICE_TRUST_MODULE_DEFUALT = AUTO, NONE
Decide how post-initrd devices are handled. AUTO is current Linux,
follow the ACPI/etc. NONE means the initrd must run a policy agent
to assign trust.
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (C) 2026 NVIDIA Corporation & Affiliates */
> +#ifndef __DEVICE_TRUST_H__
> +#define __DEVICE_TRUST_H__
> +
> +/**
> + * enum device_trust - Level of restrictions and privileges for a
> + * device. Trust is initially assigned by the bus, and the bus is
> + * responsible for coordinating transitions between trust levels with
> + * DMA/IOMMU and its own device security mechanisms.
> + *
> + * @DEVICE_TRUST_UNSET: Unregistered device object with no current bus
> + * @DEVICE_TRUST_NONE: Blocked when idle, cannot bind
> + * @DEVICE_TRUST_AUTO: All typical privileges granted
I expect there would be two enums, the policy - eg from the sysfs/etc
which is possibly this, and then the actual in-effect trust level on
the actual device which should only be DISABLED/FULL/ADVERSARY ?
So maybe call this enum device_trust_policy ?
Jason
^ permalink raw reply
* Re: [PATCH 09/15] PCI, device core: Move "untrusted" concept to DEVICE_TRUST_ADVERSARY
From: Jason Gunthorpe @ 2026-07-06 13:49 UTC (permalink / raw)
To: Dan Williams
Cc: linux-coco, linux-pci, driver-core, ankita, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Will Deacon, Robin Murphy,
Bjorn Helgaas, Alexey Kardashevskiy, Xu Yilun, Aneesh Kumar K.V,
Dexuan Cui
In-Reply-To: <20260705220819.2472765-10-djbw@kernel.org>
On Sun, Jul 05, 2026 at 03:08:13PM -0700, Dan Williams wrote:
> 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.
I'm not sure it makes sense to have these options as compile time
defaults. Arguably the non-built in path should only be auto or none
and everything else should be handled by the initrd at runtime. If
necessary a sysfs for the initrd to set a kernel default might make
sense, IDK.
Ideally built in devices should not be any kind that is hooked up to
the iommu.
> 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));
> }
I think it would be nice to use the new 'adversarial' word here and in
other places instead of untrusted.
Jason
^ permalink raw reply
* Re: [PATCH v7 1/6] firmware: smccc: Add an Arm SMCCC bus
From: Aneesh Kumar K.V @ 2026-07-06 14:35 UTC (permalink / raw)
To: Sudeep Holla
Cc: linux-coco, linux-arm-kernel, linux-kernel, Sudeep Holla,
Catalin Marinas, Greg KH, Jeremy Linton, Jonathan Cameron,
Lorenzo Pieralisi, Mark Rutland, Will Deacon, Steven Price,
Suzuki K Poulose, Andre Przywara
In-Reply-To: <20260706-belligerent-excellent-saluki-a4e431@sudeepholla>
Sudeep Holla <sudeep.holla@kernel.org> writes:
> On Thu, Jun 11, 2026 at 06:34:24PM +0530, Aneesh Kumar K.V (Arm) wrote:
...
>> +static int arm_smccc_bus_match(struct device *dev,
>> + const struct device_driver *drv)
>> +{
>> + const struct arm_smccc_device_id *id_table;
>> + struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev);
>> +
>> + id_table = to_arm_smccc_driver(drv)->id_table;
>> + if (!id_table)
>> + return 0;
>> +
>
> Any reason for not having this check when registering the driver also so
> that it becomes a must to register ? I remember vaguely adding this as fix to
> FF-A driver recently.
>
I will add
int arm_smccc_driver_register(struct arm_smccc_driver *driver,
struct module *owner, const char *mod_name)
{
if (!driver->probe || !driver->id_table)
return -EINVAL;
>
>> + while (id_table->name[0]) {
>> + if (!strcmp(smccc_dev->name, id_table->name))
>> + return 1;
>> + id_table++;
>> + }
>> +
>> + return 0;
>> +}
>> +
...
>> +struct arm_smccc_device *arm_smccc_device_register(const char *name)
>> +{
>> + struct arm_smccc_device *smccc_dev;
>> + int id, ret;
>> +
>> + id = ida_alloc_min(&arm_smccc_bus_id, 1, GFP_KERNEL);
>> + if (id < 0)
>> + return ERR_PTR(id);
>> +
>> + smccc_dev = kzalloc_obj(*smccc_dev);
>> + if (!smccc_dev) {
>> + ida_free(&arm_smccc_bus_id, id);
>> + return ERR_PTR(-ENOMEM);
>> + }
>> +
>> + smccc_dev->id = id;
>> + if (strscpy(smccc_dev->name, name) < 0) {
>
> Since this is exported symbol and one can call arm_smccc_device_register(ptr)
> where ptr = NULL, won't it blow up then ? IIUC strscpy() handles NULL dst
> but doesn't check for NULL src.
>
Will add the NULL check.
>> + kfree(smccc_dev);
>> + ida_free(&arm_smccc_bus_id, id);
>> + return ERR_PTR(-EINVAL);
>> + }
>> + smccc_dev->dev.bus = &arm_smccc_bus_type;
>> + smccc_dev->dev.release = arm_smccc_release_device;
>> +
>> + ret = dev_set_name(&smccc_dev->dev, "%s-%d", smccc_dev->name, id);
>> + if (ret) {
>> + kfree(smccc_dev);
>> + ida_free(&arm_smccc_bus_id, id);
>> + return ERR_PTR(ret);
>> + }
>> +
>> + ret = device_register(&smccc_dev->dev);
>> + if (ret) {
>> + put_device(&smccc_dev->dev);
>> + return ERR_PTR(ret);
>> + }
>> +
>> + return smccc_dev;
>> +}
>> +EXPORT_SYMBOL_GPL(arm_smccc_device_register);
...
>> +#define to_arm_smccc_driver(d) \
>> + container_of_const(d, struct arm_smccc_driver, driver)
>> +
>> +int arm_smccc_driver_register(struct arm_smccc_driver *driver,
>> + struct module *owner, const char *mod_name);
>> +void arm_smccc_driver_unregister(struct arm_smccc_driver *driver);
>> +struct arm_smccc_device *arm_smccc_device_register(const char *name);
>> +void arm_smccc_device_unregister(struct arm_smccc_device *smcc_dev);
>> +
>
> I may be overthinking but what will happen if HAVE_ARM_SMCCC_DISCOVERY=n
> and some driver is compiled using this header ? It should be fine if it
> fails to compile, just thinking out loud if we need to handle that are not.
> As long as all the drivers using these depends on HAVE_ARM_SMCCC_DISCOVERY
> it should be fine I think.
This will result in a build failure. The driver should either select
HAVE_ARM_SMCCC_DISCOVERY or depend on HAVE_ARM_SMCCC_DISCOVERY.
>
>> +#define arm_smccc_register(driver) \
>> + arm_smccc_driver_register(driver, THIS_MODULE, KBUILD_MODNAME)
>> +#define arm_smccc_unregister(driver) \
>> + arm_smccc_driver_unregister(driver)
>> +
>> +#define module_arm_smccc_driver(__arm_smccc_driver) \
>> + module_driver(__arm_smccc_driver, arm_smccc_register, \
>> + arm_smccc_unregister)
>> +
>> +extern const struct bus_type arm_smccc_bus_type;
>> +
>> +#endif /* __LINUX_ARM_SMCCC_BUS_H */
>> diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
>> index 23ff24080dfd..c9cee8c5a0b2 100644
>> --- a/include/linux/mod_devicetable.h
>> +++ b/include/linux/mod_devicetable.h
>
> This file seems to be reworked recently, so you need to rebase it moving
> smccc specific changes to separate file I think.
>
Do we need to add a separate file? I was able to rebase the series on
7.2-rc1
modified include/linux/mod_devicetable.h
@@ -879,6 +879,19 @@ struct auxiliary_device_id {
kernel_ulong_t driver_data;
};
+#define ARM_SMCCC_NAME_SIZE 40
+#define ARM_SMCCC_MODULE_PREFIX "arm_smccc:"
+
+/**
+ * struct arm_smccc_device_id - Arm SMCCC bus device identifier
+ * @name: SMCCC device name
+ * @driver_data: driver data
+ */
+struct arm_smccc_device_id {
+ char name[ARM_SMCCC_NAME_SIZE];
+ kernel_ulong_t driver_data;
+};
+
/* Surface System Aggregator Module */
#define SSAM_MATCH_TARGET 0x1
>> @@ -876,6 +876,19 @@ struct auxiliary_device_id {
>> kernel_ulong_t driver_data;
>> };
>>
>> +#define ARM_SMCCC_NAME_SIZE 40
>> +#define ARM_SMCCC_MODULE_PREFIX "arm_smccc:"
>> +
>> +/**
>> + * struct arm_smccc_device_id - Arm SMCCC bus device identifier
>> + * @name: SMCCC device name
>> + * @driver_data: driver data
>> + */
>> +struct arm_smccc_device_id {
>> + char name[ARM_SMCCC_NAME_SIZE];
>> + kernel_ulong_t driver_data;
>
> Can't find any users of the above driver_data ?
> Is it for future ? If so, can you add details on how it is supposed to be used
> if you don't want to drop it.
>
That would depend on the driver, wouldn’t it? I was comparing this with
struct auxiliary_device_id, and very few drivers use driver_data;
mei_gsc_probe() is one example.
I am not sure there is a generic rule here. IIUC, it provides a place
where a driver can add driver-specific data that can be used during the
probe routine.
>> +};
>> +
>> /* Surface System Aggregator Module */
>>
>> #define SSAM_MATCH_TARGET 0x1
>> diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c
>> index b4178c42d08f..a485011ff137 100644
>> --- a/scripts/mod/devicetable-offsets.c
>> +++ b/scripts/mod/devicetable-offsets.c
>> @@ -254,6 +254,9 @@ int main(void)
>> DEVID(auxiliary_device_id);
>> DEVID_FIELD(auxiliary_device_id, name);
>>
>> + DEVID(arm_smccc_device_id);
>> + DEVID_FIELD(arm_smccc_device_id, name);
>> +
>> DEVID(ssam_device_id);
>> DEVID_FIELD(ssam_device_id, match_flags);
>> DEVID_FIELD(ssam_device_id, domain);
-aneesh
^ 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