This series propagates DMA_ATTR_CC_SHARED through the dma-direct,
dma-pool, and swiotlb paths so that encrypted and decrypted DMA buffers
are handled consistently.
Today, the direct DMA path mostly relies on force_dma_unencrypted() for
shared/decrypted buffer handling. This series consolidates the
force_dma_unencrypted() checks in the top-level functions and ensures
that the remaining DMA interfaces use DMA attributes to make the correct
decisions.
The series:
- moves swiotlb-backed allocations out of __dma_direct_alloc_pages(),
- propagates DMA_ATTR_CC_SHARED through the dma-direct alloc/free
paths
- teaches the atomic DMA pools to track encrypted versus decrypted
state
- tracks swiotlb pool encryption state and enforces strict pool
selection
- centralizes encrypted/decrypted pgprot handling in dma_pgprot() using
DMA attributes
- passes DMA attributes down to dma_capable() so capability checks can
validate whether the selected DMA address encoding matches
DMA_ATTR_CC_SHARED
- makes dma_direct_map_phys() choose the DMA address encoding from
DMA_ATTR_CC_SHARED and fall back to swiotlb when a shared DMA request
cannot use the direct mapping, which lets arm64 and x86 CCA guests stop
relying on SWIOTLB_FORCE for DMA mappings
- use the selected swiotlb pool state to derive the returned DMA
address.
Changes from v3:
https://lore.kernel.org/all/20260427055509.898190-1-aneesh.kumar@kernel.org
* Handle DMA_ATTR_MMIO correctly in dma_direct_map_phys()
* Address most of sashiko review
* Rebase to latest kernel
* drop SWIOTLB_FORCE for s390 and powerpc secure guest.
Changes from v2:
https://lore.kernel.org/all/20260420061415.3650870-1-aneesh.kumar@kernel.org
* pass attrs to dma_capable() and update direct, swiotlb, Xen swiotlb, and
x86 GART paths so the capability checks see the DMA address attr value
DMA_ATTR_CC_SHARED.
* rework dma_direct_map_phys() so DMA_ATTR_CC_SHARED selects
phys_to_dma_unencrypted() while the default path uses
phys_to_dma_encrypted(), with swiotlb fallback when the requested
shared/private state cannot be satisfied by a direct DMA address.
* stop relying on SWIOTLB_FORCE for arm64 and x86 CC guest DMA mappings;
swiotlb is still enabled there, but shared mappings is now selected
through the generic dma_direct_map_phys()/dma_capable() decision instead
of a global force-bounce flag.
Changes from v1:
https://lore.kernel.org/all/20260417085900.3062416-1-aneesh.kumar@kernel.org
* rebased to latest kernel (change from DMA_ATTR_CC_DECRYPTED -> DMA_ATTR_CC_SHARED)
* update the alloc path so DMA_ATTR_CC_SHARED is not a caller-visible attribute.
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Cc: Will Deacon <will@kernel.org>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Steven Price <steven.price@arm.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Jiri Pirko <jiri@resnulli.us>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Mostafa Saleh <smostafa@google.com>
Cc: Petr Tesarik <redacted>
Cc: Alexey Kardashevskiy <redacted>
Cc: Dan Williams <redacted>
Cc: Xu Yilun <redacted>
Cc: linuxppc-dev@lists.ozlabs.org
Cc: linux-s390@vger.kernel.org
Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Nicholas Piggin <npiggin@gmail.com>
Cc: "Christophe Leroy (CS GROUP)" <chleroy@kernel.org>
Cc: Alexander Gordeev <agordeev@linux.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@linux.ibm.com>
Cc: Heiko Carstens <hca@linux.ibm.com>
Cc: Vasily Gorbik <gor@linux.ibm.com>
Cc: Christian Borntraeger <borntraeger@linux.ibm.com>
Cc: Sven Schnelle <svens@linux.ibm.com>
Cc: x86@kernel.org
Aneesh Kumar K.V (Arm) (13):
dma-direct: swiotlb: handle swiotlb alloc/free outside
__dma_direct_alloc_pages
dma-direct: use DMA_ATTR_CC_SHARED in alloc/free paths
dma-pool: track decrypted atomic pools and select them via attrs
dma: swiotlb: track pool encryption state and honor DMA_ATTR_CC_SHARED
dma-mapping: make dma_pgprot() honor DMA_ATTR_CC_SHARED
dma-direct: pass attrs to dma_capable() for DMA_ATTR_CC_SHARED checks
dma-direct: make dma_direct_map_phys() honor DMA_ATTR_CC_SHARED
dma-direct: set decrypted flag for remapped DMA allocations
dma-direct: select DMA address encoding from DMA_ATTR_CC_SHARED
dma-pool: fix page leak in atomic_pool_expand() cleanup
dma-direct: rename ret to cpu_addr in alloc helpers
dma-direct: return struct page from dma_direct_alloc_from_pool()
x86/amd-gart: preserve the direct DMA address until GART mapping
succeeds
arch/arm64/mm/init.c | 4 +-
arch/powerpc/platforms/pseries/svm.c | 2 +-
arch/s390/mm/init.c | 2 +-
arch/x86/kernel/amd_gart_64.c | 36 ++--
arch/x86/kernel/pci-dma.c | 4 +-
drivers/iommu/dma-iommu.c | 2 +-
drivers/xen/swiotlb-xen.c | 10 +-
include/linux/dma-direct.h | 19 ++-
include/linux/dma-map-ops.h | 2 +-
include/linux/swiotlb.h | 8 +-
kernel/dma/direct.c | 243 ++++++++++++++++++++-------
kernel/dma/direct.h | 40 ++---
kernel/dma/mapping.c | 16 +-
kernel/dma/pool.c | 165 +++++++++++-------
kernel/dma/swiotlb.c | 109 +++++++++---
15 files changed, 459 insertions(+), 203 deletions(-)
base-commit: 50897c955902c93ae71c38698abb910525ebdc89
--
2.43.0
Move swiotlb allocation out of __dma_direct_alloc_pages() and handle it in
dma_direct_alloc() / dma_direct_alloc_pages().
This is needed for follow-up changes that simplify the handling of
memory encryption/decryption based on the DMA attribute flags.
swiotlb backing pages are already mapped decrypted by
swiotlb_update_mem_attributes() and rmem_swiotlb_device_init(), so
dma-direct should not call dma_set_decrypted() on allocation nor
dma_set_encrypted() on free for swiotlb-backed memory.
Update alloc/free paths to detect swiotlb-backed pages and skip
encrypt/decrypt transitions for those paths. Keep the existing highmem
rejection in dma_direct_alloc_pages() for swiotlb allocations.
Only for "restricted-dma-pool", we currently set `for_alloc = true`, while
rmem_swiotlb_device_init() decrypts the whole pool up front. This pool is
typically used together with "shared-dma-pool", where the shared region is
accessed after remap/ioremap and the returned address is suitable for
decrypted memory access. So existing code paths remain valid.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/direct.c | 44 +++++++++++++++++++++++++++++++++++++-------
1 file changed, 37 insertions(+), 7 deletions(-)
@@ -250,11 +248,21 @@ void *dma_direct_alloc(struct device *dev, size_t size,dma_direct_use_pool(dev,gfp))returndma_direct_alloc_from_pool(dev,size,dma_handle,gfp);+if(is_swiotlb_for_alloc(dev)){+page=dma_direct_alloc_swiotlb(dev,size);+if(page){+mark_mem_decrypt=false;+gotosetup_page;+}+returnNULL;+}+/* we always manually zero the memory once we are done */page=__dma_direct_alloc_pages(dev,size,gfp&~__GFP_ZERO,true);if(!page)returnNULL;+setup_page:/**dma_alloc_contiguouscanreturnhighmempagesdependingona*combinationthecma=argumentsandper-archsetup.Theseneedtobe
@@ -378,13 +404,17 @@ void dma_direct_free_pages(struct device *dev, size_t size,enumdma_data_directiondir){void*vaddr=page_address(page);+boolmark_mem_encrypted=true;/* If cpu_addr is not from an atomic pool, dma_free_from_pool() fails */if(IS_ENABLED(CONFIG_DMA_COHERENT_POOL)&&dma_free_from_pool(dev,vaddr,size))return;-if(dma_set_encrypted(dev,vaddr,size))+if(swiotlb_find_pool(dev,page_to_phys(page)))+mark_mem_encrypted=false;++if(mark_mem_encrypted&&dma_set_encrypted(dev,vaddr,size))return;__dma_direct_free_pages(dev,page,size);}
Propagate force_dma_unencrypted() into DMA_ATTR_CC_SHARED in the
dma-direct allocation path and use the attribute to drive the related
decisions.
This updates dma_direct_alloc(), dma_direct_free(), and
dma_direct_alloc_pages() to fold the forced unencrypted case into attrs.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/direct.c | 44 ++++++++++++++++++++++++++++++++++++--------
1 file changed, 36 insertions(+), 8 deletions(-)
Teach the atomic DMA pool code to distinguish between encrypted and
decrypted pools, and make pool allocation select the matching pool based
on DMA attributes.
Introduce a dma_gen_pool wrapper that records whether a pool is
decrypted, initialize that state when the atomic pools are created, and
use it when expanding and resizing the pools. Update dma_alloc_from_pool()
to take attrs and skip pools whose encrypted/decrypted state does not
match DMA_ATTR_CC_SHARED. Update dma_free_from_pool() accordingly.
Also pass DMA_ATTR_CC_SHARED from the swiotlb atomic allocation path
so decrypted swiotlb allocations are taken from the correct atomic pool.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
drivers/iommu/dma-iommu.c | 2 +-
include/linux/dma-map-ops.h | 2 +-
kernel/dma/direct.c | 11 ++-
kernel/dma/pool.c | 163 +++++++++++++++++++++++-------------
kernel/dma/swiotlb.c | 7 +-
5 files changed, 122 insertions(+), 63 deletions(-)
@@ -12,12 +12,18 @@#include<linux/set_memory.h>#include<linux/slab.h>#include<linux/workqueue.h>+#include<linux/cc_platform.h>-staticstructgen_pool*atomic_pool_dma__ro_after_init;+structdma_gen_pool{+boolunencrypted;+structgen_pool*pool;+};++staticstructdma_gen_poolatomic_pool_dma__ro_after_init;staticunsignedlongpool_size_dma;-staticstructgen_pool*atomic_pool_dma32__ro_after_init;+staticstructdma_gen_poolatomic_pool_dma32__ro_after_init;staticunsignedlongpool_size_dma32;-staticstructgen_pool*atomic_pool_kernel__ro_after_init;+staticstructdma_gen_poolatomic_pool_kernel__ro_after_init;staticunsignedlongpool_size_kernel;/* Size can be defined by the coherent_pool command line */
@@ -126,11 +135,15 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,return0;encrypt_mapping:-ret=set_memory_encrypted((unsignedlong)page_to_virt(page),-1<<order);-if(WARN_ON_ONCE(ret)){-/* Decrypt succeeded but encrypt failed, purposely leak */-gotoout;+if(dma_pool->unencrypted){+intrc;++rc=set_memory_encrypted((unsignedlong)page_to_virt(page),+1<<order);+if(WARN_ON_ONCE(rc)){+/* Decrypt succeeded but encrypt failed, purposely leak */+gotoout;+}}remove_mapping:#ifdef CONFIG_DMA_DIRECT_REMAP
@@ -142,46 +155,52 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,returnret;}-staticvoidatomic_pool_resize(structgen_pool*pool,gfp_tgfp)+staticvoidatomic_pool_resize(structdma_gen_pool*dma_pool,gfp_tgfp){-if(pool&&gen_pool_avail(pool)<atomic_pool_size)-atomic_pool_expand(pool,gen_pool_size(pool),gfp);+if(dma_pool->pool&&gen_pool_avail(dma_pool->pool)<atomic_pool_size)+atomic_pool_expand(dma_pool,gen_pool_size(dma_pool->pool),gfp);}staticvoidatomic_pool_work_fn(structwork_struct*work){if(IS_ENABLED(CONFIG_ZONE_DMA))-atomic_pool_resize(atomic_pool_dma,+atomic_pool_resize(&atomic_pool_dma,GFP_KERNEL|GFP_DMA);if(IS_ENABLED(CONFIG_ZONE_DMA32))-atomic_pool_resize(atomic_pool_dma32,+atomic_pool_resize(&atomic_pool_dma32,GFP_KERNEL|GFP_DMA32);-atomic_pool_resize(atomic_pool_kernel,GFP_KERNEL);+atomic_pool_resize(&atomic_pool_kernel,GFP_KERNEL);}-static__initstructgen_pool*__dma_atomic_pool_init(size_tpool_size,-gfp_tgfp)+static__initstructdma_gen_pool*__dma_atomic_pool_init(structdma_gen_pool*dma_pool,+size_tpool_size,gfp_tgfp){-structgen_pool*pool;intret;-pool=gen_pool_create(PAGE_SHIFT,NUMA_NO_NODE);-if(!pool)+dma_pool->pool=gen_pool_create(PAGE_SHIFT,NUMA_NO_NODE);+if(!dma_pool->pool)returnNULL;-gen_pool_set_algo(pool,gen_pool_first_fit_order_align,NULL);+gen_pool_set_algo(dma_pool->pool,gen_pool_first_fit_order_align,NULL);++/* if platform is using memory encryption atomic pools are by default decrypted. */+if(cc_platform_has(CC_ATTR_MEM_ENCRYPT))+dma_pool->unencrypted=true;+else+dma_pool->unencrypted=false;-ret=atomic_pool_expand(pool,pool_size,gfp);+ret=atomic_pool_expand(dma_pool,pool_size,gfp);if(ret){-gen_pool_destroy(pool);+gen_pool_destroy(dma_pool->pool);+dma_pool->pool=NULL;pr_err("DMA: failed to allocate %zu KiB %pGg pool for atomic allocation\n",pool_size>>10,&gfp);returnNULL;}pr_info("DMA: preallocated %zu KiB %pGg pool for atomic allocations\n",-gen_pool_size(pool)>>10,&gfp);-returnpool;+gen_pool_size(dma_pool->pool)>>10,&gfp);+returndma_pool;}#ifdef CONFIG_ZONE_DMA32
@@ -207,21 +226,22 @@ static int __init dma_atomic_pool_init(void)/* All memory might be in the DMA zone(s) to begin with */if(has_managed_zone(ZONE_NORMAL)){-atomic_pool_kernel=__dma_atomic_pool_init(atomic_pool_size,-GFP_KERNEL);-if(!atomic_pool_kernel)+__dma_atomic_pool_init(&atomic_pool_kernel,atomic_pool_size,GFP_KERNEL);+if(!atomic_pool_kernel.pool)ret=-ENOMEM;}+if(has_managed_dma()){-atomic_pool_dma=__dma_atomic_pool_init(atomic_pool_size,-GFP_KERNEL|GFP_DMA);-if(!atomic_pool_dma)+__dma_atomic_pool_init(&atomic_pool_dma,atomic_pool_size,+GFP_KERNEL|GFP_DMA);+if(!atomic_pool_dma.pool)ret=-ENOMEM;}+if(has_managed_dma32){-atomic_pool_dma32=__dma_atomic_pool_init(atomic_pool_size,-GFP_KERNEL|GFP_DMA32);-if(!atomic_pool_dma32)+__dma_atomic_pool_init(&atomic_pool_dma32,atomic_pool_size,+GFP_KERNEL|GFP_DMA32);+if(!atomic_pool_dma32.pool)ret=-ENOMEM;}
@@ -230,19 +250,44 @@ static int __init dma_atomic_pool_init(void)}postcore_initcall(dma_atomic_pool_init);-staticinlinestructgen_pool*dma_guess_pool(structgen_pool*prev,gfp_tgfp)+staticinlinestructdma_gen_pool*__dma_guess_pool(structdma_gen_pool*first,+structdma_gen_pool*second,structdma_gen_pool*third)+{+if(first->pool)+returnfirst;+if(second&&second->pool)+returnsecond;+if(third&&third->pool)+returnthird;+returnNULL;+}++staticinlinestructdma_gen_pool*dma_guess_pool(structdma_gen_pool*prev,+gfp_tgfp){-if(prev==NULL){+if(!prev){if(gfp&GFP_DMA)-returnatomic_pool_dma?:atomic_pool_dma32?:atomic_pool_kernel;+return__dma_guess_pool(&atomic_pool_dma,+&atomic_pool_dma32,+&atomic_pool_kernel);+if(gfp&GFP_DMA32)-returnatomic_pool_dma32?:atomic_pool_dma?:atomic_pool_kernel;-returnatomic_pool_kernel?:atomic_pool_dma32?:atomic_pool_dma;+return__dma_guess_pool(&atomic_pool_dma32,+&atomic_pool_dma,+&atomic_pool_kernel);++return__dma_guess_pool(&atomic_pool_kernel,+&atomic_pool_dma32,+&atomic_pool_dma);}-if(prev==atomic_pool_kernel)-returnatomic_pool_dma32?atomic_pool_dma32:atomic_pool_dma;-if(prev==atomic_pool_dma32)-returnatomic_pool_dma;++if(prev==&atomic_pool_kernel)+return__dma_guess_pool(&atomic_pool_dma32,+&atomic_pool_dma,NULL);++if(prev==&atomic_pool_dma32)+return__dma_guess_pool(&atomic_pool_dma,NULL,NULL);+returnNULL;}
Teach swiotlb to distinguish between encrypted and decrypted bounce
buffer pools, and make allocation and mapping paths select a pool whose
state matches the requested DMA attributes.
Add a decrypted flag to io_tlb_mem, initialize it for the default and
restricted pools, and propagate DMA_ATTR_CC_SHARED into swiotlb pool
allocation. Reject swiotlb alloc/map requests when the selected pool does
not match the required encrypted/decrypted state.
Also return DMA addresses with the matching phys_to_dma_{encrypted,
unencrypted} helper so the DMA address encoding stays consistent with the
chosen pool.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
include/linux/dma-direct.h | 10 ++++
include/linux/swiotlb.h | 8 ++-
kernel/dma/direct.c | 14 +++--
kernel/dma/swiotlb.c | 108 +++++++++++++++++++++++++++----------
4 files changed, 107 insertions(+), 33 deletions(-)
@@ -1411,6 +1436,16 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,if(cc_platform_has(CC_ATTR_MEM_ENCRYPT))pr_warn_once("Memory encryption is active and system is using DMA bounce buffers\n");+/*+*ifwearetryingtoswiotlbmapadecryptedpaddrorthepaddrisencrypted+*butthedeviceisforcingdecryption,usedecryptedio_tlb_mem+*/+if((attrs&DMA_ATTR_CC_SHARED)||force_dma_unencrypted(dev))+require_decrypted=true;++if(require_decrypted!=mem->unencrypted)+return(phys_addr_t)DMA_MAPPING_ERROR;+/**ThedefaultswiotlbmemorypoolisallocatedwithPAGE_SIZE*alignment.Ifamappingisrequestedwithlargeralignment,
@@ -1608,8 +1643,14 @@ dma_addr_t swiotlb_map(struct device *dev, phys_addr_t paddr, size_t size,if(swiotlb_addr==(phys_addr_t)DMA_MAPPING_ERROR)returnDMA_MAPPING_ERROR;-/* Ensure that the address returned is DMA'ble */-dma_addr=phys_to_dma_unencrypted(dev,swiotlb_addr);+/*+*Usetheallocatedio_tlb_memencryptiontypetodeterminedmaaddr.+*/+if(dev->dma_io_tlb_mem->unencrypted)+dma_addr=phys_to_dma_unencrypted(dev,swiotlb_addr);+else+dma_addr=phys_to_dma_encrypted(dev,swiotlb_addr);+if(unlikely(!dma_capable(dev,dma_addr,size,true))){__swiotlb_tbl_unmap_single(dev,swiotlb_addr,size,dir,attrs|DMA_ATTR_SKIP_CPU_SYNC,
Fold encrypted/decrypted pgprot selection into dma_pgprot() so callers
do not need to adjust the page protection separately.
Update dma_pgprot() to apply pgprot_decrypted() when
DMA_ATTR_CC_SHARED is set and pgprot_encrypted() otherwise Convert
the dma-direct allocation and mmap paths to pass DMA_ATTR_CC_SHARED
instead of open-coding force_dma_unencrypted() handling around
dma_pgprot().
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/direct.c | 8 +++-----
kernel/dma/mapping.c | 16 ++++++++++++----
2 files changed, 15 insertions(+), 9 deletions(-)
@@ -298,9 +298,6 @@ void *dma_direct_alloc(struct device *dev, size_t size,if(remap){pgprot_tprot=dma_pgprot(dev,PAGE_KERNEL,attrs);-if(force_dma_unencrypted(dev))-prot=pgprot_decrypted(prot);-/* remove any dirty cache lines on the kernel alias */arch_dma_prep_coherent(page,size);
Teach dma_capable() about DMA_ATTR_CC_SHARED so the capability
check can reject encrypted DMA addresses for devices that require
unencrypted/shared DMA.
Also propagate DMA_ATTR_CC_SHARED in swiotlb_map() when the selected
SWIOTLB pool is decrypted so the capability check sees the correct DMA
address attribute.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/x86/kernel/amd_gart_64.c | 30 ++++++++++++++++--------------
drivers/xen/swiotlb-xen.c | 10 +++++++---
include/linux/dma-direct.h | 9 ++++++++-
kernel/dma/direct.h | 6 +++---
kernel/dma/swiotlb.c | 8 +++++---
5 files changed, 39 insertions(+), 24 deletions(-)
@@ -180,22 +180,23 @@ static void iommu_full(struct device *dev, size_t size, int dir)}staticinlineint-need_iommu(structdevice*dev,unsignedlongaddr,size_tsize)+need_iommu(structdevice*dev,unsignedlongaddr,size_tsize,unsignedlongattrs){-returnforce_iommu||!dma_capable(dev,addr,size,true);+returnforce_iommu||!dma_capable(dev,addr,size,true,attrs);}staticinlineint-nonforced_iommu(structdevice*dev,unsignedlongaddr,size_tsize)+nonforced_iommu(structdevice*dev,unsignedlongaddr,size_tsize,+unsignedlongattrs){-return!dma_capable(dev,addr,size,true);+return!dma_capable(dev,addr,size,true,attrs);}/* Map a single continuous physical area into the IOMMU.*Callerneedstocheckiftheiommuisneededandflush.*/staticdma_addr_tdma_map_area(structdevice*dev,dma_addr_tphys_mem,-size_tsize,intdir,unsignedlongalign_mask)+size_tsize,intdir,unsignedlongalign_mask,unsignedlongattrs){unsignedlongnpages=iommu_num_pages(phys_mem,size,PAGE_SIZE);unsignedlongiommu_page;
@@ -289,7 +290,7 @@ static void gart_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,/* Fallback for dma_map_sg in case of overflow */staticintdma_map_sg_nonforce(structdevice*dev,structscatterlist*sg,-intnents,intdir)+intnents,intdir,unsignedlongattrs){structscatterlist*s;inti;
@@ -401,7 +402,7 @@ static int gart_map_sg(struct device *dev, struct scatterlist *sg, int nents,s->dma_address=addr;BUG_ON(s->length==0);-nextneed=need_iommu(dev,addr,s->length);+nextneed=need_iommu(dev,addr,s->length,attrs);/* Handle the previous not yet processed entries */if(i>start){
@@ -449,7 +450,7 @@ static int gart_map_sg(struct device *dev, struct scatterlist *sg, int nents,/* When it was forced or merged try again in a dumb way */if(force_iommu||iommu_merge){-out=dma_map_sg_nonforce(dev,sg,nents,dir);+out=dma_map_sg_nonforce(dev,sg,nents,dir,attrs);if(out>0)returnout;}
Teach dma_direct_map_phys() to select the DMA address encoding based on
DMA_ATTR_CC_SHARED.
Use phys_to_dma_unencrypted() for decrypted mappings and
phys_to_dma_encrypted() otherwise. If a device requires unencrypted DMA
but the source physical address is still encrypted, force the mapping
through swiotlb so the DMA address and backing memory attributes remain
consistent.
Update the arm64, x86, s390 and powerpc secure-guest setup to not use
swiotlb force option
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
Changes from v3:
* Handle DMA_ATTR_MMIO
---
arch/arm64/mm/init.c | 4 +--
arch/powerpc/platforms/pseries/svm.c | 2 +-
arch/s390/mm/init.c | 2 +-
arch/x86/kernel/pci-dma.c | 4 +--
kernel/dma/direct.c | 4 ++-
kernel/dma/direct.h | 38 +++++++++++++---------------
6 files changed, 24 insertions(+), 30 deletions(-)
@@ -29,7 +29,7 @@ static int __init init_svm(void)*needtousetheSWIOTLBbufferforDMAevenifdma_capable()says*otherwise.*/-ppc_swiotlb_flags|=SWIOTLB_ANY|SWIOTLB_FORCE;+ppc_swiotlb_flags|=SWIOTLB_ANY;/* Share the SWIOTLB buffer with the host. */swiotlb_update_mem_attributes();
@@ -691,8 +691,10 @@ size_t dma_direct_max_mapping_size(struct device *dev){/* If SWIOTLB is active, use its maximum mapping size */if(is_swiotlb_active(dev)&&-(dma_addressing_limited(dev)||is_swiotlb_force_bounce(dev)))+(dma_addressing_limited(dev)||is_swiotlb_force_bounce(dev)||+force_dma_unencrypted(dev)))returnswiotlb_max_mapping_size(dev);+returnSIZE_MAX;}
Devices that are DMA non-coherent and require a remap were skipping
dma_set_decrypted(), leaving DMA buffers encrypted even when the device
requires unencrypted access. Move the call after the if (remap) branch
so that both the direct and remapped allocation paths correctly mark the
allocation as decrypted (or fail cleanly) before use.
Fix dma_direct_alloc() and dma_direct_free() to apply set_memory_*() to
the linear-map alias of the backing pages instead of the remapped CPU
address. Also disallow highmem pages for DMA_ATTR_CC_SHARED, because
highmem buffers do not provide a usable linear-map address.
Fixes: f3c962226dbe ("dma-direct: clean up the remapping checks in dma_direct_alloc")
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/direct.c | 56 ++++++++++++++++++++++++++++++++++++---------
1 file changed, 45 insertions(+), 11 deletions(-)
@@ -280,7 +290,7 @@ 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,true);+page=__dma_direct_alloc_pages(dev,size,gfp&~__GFP_ZERO,allow_highmem);if(!page)returnNULL;
Make the dma-direct helpers derive the DMA address encoding from
DMA_ATTR_CC_SHARED instead of implicitly relying on
force_dma_unencrypted() inside phys_to_dma_direct()
Pass an explicit unencrypted/decrypted state into phys_to_dma_direct(),
make the alloc paths return DMA addresses that match the requested
buffer encryption state. Also only call dma_set_decrypted()
DMA_ATTR_CC_SHARED is actually set.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/direct.c | 48 ++++++++++++++++++++++++++++-----------------
1 file changed, 30 insertions(+), 18 deletions(-)
@@ -193,9 +197,11 @@ static void *dma_direct_alloc_no_mapping(struct device *dev, size_t size,/* remove any dirty cache lines on the kernel alias */if(!PageHighMem(page))arch_dma_prep_coherent(page,size);--/* return the page pointer as the opaque cookie */-*dma_handle=phys_to_dma_direct(dev,page_to_phys(page));+/*+*returnthepagepointerastheopaquecookie.+*Neverusedforunencryptedallocation+*/+*dma_handle=phys_to_dma_encrypted(dev,page_to_phys(page));returnpage;}
@@ -471,8 +479,12 @@ void dma_direct_free_pages(struct device *dev, size_t size,structpage*page,dma_addr_tdma_addr,enumdma_data_directiondir){+/*+*ifthedevicehadrequestedforanunencryptedbuffer,+*convertittoencryptedonfree+*/+boolmark_mem_encrypted=force_dma_unencrypted(dev);void*vaddr=page_address(page);-boolmark_mem_encrypted=true;/* If cpu_addr is not from an atomic pool, dma_free_from_pool() fails */if(IS_ENABLED(CONFIG_DMA_COHERENT_POOL)&&
atomic_pool_expand() frees the allocated pages from the remove_mapping
error path only when CONFIG_DMA_DIRECT_REMAP is enabled.
When CONFIG_DMA_DIRECT_REMAP is disabled, failures after page allocation,
such as gen_pool_add_virt(), jump to remove_mapping and return without
freeing the pages.
Move __free_pages(page, order) out of the CONFIG_DMA_DIRECT_REMAP block so
that cleanup paths always release the allocation.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/pool.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
ret in dma_direct_alloc() and dma_direct_alloc_pages() holds the returned
CPU mapping, not a generic return value. Rename it to cpu_addr and update
the remaining uses to match.
This makes the allocation paths easier to follow and keeps the local naming
consistent with what the variable actually represents.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/direct.c | 31 +++++++++++++++----------------
1 file changed, 15 insertions(+), 16 deletions(-)
Commit 5b138c534fda ("dma-direct: factor out a dma_direct_alloc_from_pool
helper") changed dma_direct_alloc_from_pool() to return the CPU address
from dma_alloc_from_pool(). That fits dma_direct_alloc(), but
dma_direct_alloc_pages() also uses the helper and expects a struct page *.
Fix this by making dma_direct_alloc_from_pool() return the struct page *
again, and pass the CPU address back through an out-parameter for the
dma_direct_alloc() caller.
Fixes: 5b138c534fda ("dma-direct: factor out a dma_direct_alloc_from_pool helper")
Cc: stable@vger.kernel.org
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/direct.c | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
gart_alloc_coherent() first allocates memory through dma_direct_alloc(),
which returns a direct-mapped DMA address in dma_addr. When force_iommu is
enabled, the buffer is then remapped.
Do not overwrite dma_addr before dma_map_area() has succeeded. Keep the
dma_map_area result in a temporary variable so the direct DMA address
remains available for dma_direct_free() on the error path.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/x86/kernel/amd_gart_64.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
On Tue, May 12, 2026 at 02:33:56PM +0530, Aneesh Kumar K.V (Arm) wrote:
quoted hunk
Move swiotlb allocation out of __dma_direct_alloc_pages() and handle it in
dma_direct_alloc() / dma_direct_alloc_pages().
This is needed for follow-up changes that simplify the handling of
memory encryption/decryption based on the DMA attribute flags.
swiotlb backing pages are already mapped decrypted by
swiotlb_update_mem_attributes() and rmem_swiotlb_device_init(), so
dma-direct should not call dma_set_decrypted() on allocation nor
dma_set_encrypted() on free for swiotlb-backed memory.
Update alloc/free paths to detect swiotlb-backed pages and skip
encrypt/decrypt transitions for those paths. Keep the existing highmem
rejection in dma_direct_alloc_pages() for swiotlb allocations.
Only for "restricted-dma-pool", we currently set `for_alloc = true`, while
rmem_swiotlb_device_init() decrypts the whole pool up front. This pool is
typically used together with "shared-dma-pool", where the shared region is
accessed after remap/ioremap and the returned address is suitable for
decrypted memory access. So existing code paths remain valid.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/direct.c | 44 +++++++++++++++++++++++++++++++++++++-------
1 file changed, 37 insertions(+), 7 deletions(-)
@@ -250,11 +248,21 @@ void *dma_direct_alloc(struct device *dev, size_t size,dma_direct_use_pool(dev,gfp))returndma_direct_alloc_from_pool(dev,size,dma_handle,gfp);+if(is_swiotlb_for_alloc(dev)){+page=dma_direct_alloc_swiotlb(dev,size);+if(page){+mark_mem_decrypt=false;+gotosetup_page;+}+returnNULL;+}+/* we always manually zero the memory once we are done */page=__dma_direct_alloc_pages(dev,size,gfp&~__GFP_ZERO,true);if(!page)returnNULL;+setup_page:/**dma_alloc_contiguouscanreturnhighmempagesdependingona*combinationthecma=argumentsandper-archsetup.Theseneedtobe
@@ -338,12 +347,15 @@ void dma_direct_free(struct device *dev, size_t size, dma_free_from_pool(dev, cpu_addr, PAGE_ALIGN(size))) return;+ if (swiotlb_find_pool(dev, dma_to_phys(dev, dma_addr)))+ mark_mem_encrypted = false;+ if (is_vmalloc_addr(cpu_addr)) { vunmap(cpu_addr); } else { if (IS_ENABLED(CONFIG_ARCH_HAS_DMA_CLEAR_UNCACHED)) arch_dma_clear_uncached(cpu_addr, size);- if (dma_set_encrypted(dev, cpu_addr, size))+ if (mark_mem_encrypted && dma_set_encrypted(dev, cpu_addr, size)) return; }
@@ -359,6 +371,19 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size, if (force_dma_unencrypted(dev) && dma_direct_use_pool(dev, gfp)) return dma_direct_alloc_from_pool(dev, size, dma_handle, gfp);+ if (is_swiotlb_for_alloc(dev)) {+ page = dma_direct_alloc_swiotlb(dev, size);+ if (!page)+ return NULL;++ if (PageHighMem(page)) {
My understanding is that rmem_swiotlb_device_init() asserts that there
is no PageHighMem()? Also a similar check doesn’t exist in
dma_direct_alloc().
Thanks,
Mostafa
On Tue, May 12, 2026 at 02:33:57PM +0530, Aneesh Kumar K.V (Arm) wrote:
quoted hunk
Propagate force_dma_unencrypted() into DMA_ATTR_CC_SHARED in the
dma-direct allocation path and use the attribute to drive the related
decisions.
This updates dma_direct_alloc(), dma_direct_free(), and
dma_direct_alloc_pages() to fold the forced unencrypted case into attrs.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/direct.c | 44 ++++++++++++++++++++++++++++++++++++--------
1 file changed, 36 insertions(+), 8 deletions(-)
What about dma_direct_free_pages()? Nothing inside uses attrs, but
that’s quite similar to dma_direct_alloc_pages()
Also, at this point, shouldn’t this patch also remove
force_dma_unencrypted() calls from dma_set_decrypted() and
dma_set_encrypted()?
Thanks,
Mostafa
On Tue, May 12, 2026 at 02:33:58PM +0530, Aneesh Kumar K.V (Arm) wrote:
quoted hunk
Teach the atomic DMA pool code to distinguish between encrypted and
decrypted pools, and make pool allocation select the matching pool based
on DMA attributes.
Introduce a dma_gen_pool wrapper that records whether a pool is
decrypted, initialize that state when the atomic pools are created, and
use it when expanding and resizing the pools. Update dma_alloc_from_pool()
to take attrs and skip pools whose encrypted/decrypted state does not
match DMA_ATTR_CC_SHARED. Update dma_free_from_pool() accordingly.
Also pass DMA_ATTR_CC_SHARED from the swiotlb atomic allocation path
so decrypted swiotlb allocations are taken from the correct atomic pool.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
drivers/iommu/dma-iommu.c | 2 +-
include/linux/dma-map-ops.h | 2 +-
kernel/dma/direct.c | 11 ++-
kernel/dma/pool.c | 163 +++++++++++++++++++++++-------------
kernel/dma/swiotlb.c | 7 +-
5 files changed, 122 insertions(+), 63 deletions(-)
@@ -12,12 +12,18 @@#include<linux/set_memory.h>#include<linux/slab.h>#include<linux/workqueue.h>+#include<linux/cc_platform.h>-staticstructgen_pool*atomic_pool_dma__ro_after_init;+structdma_gen_pool{+boolunencrypted;+structgen_pool*pool;+};++staticstructdma_gen_poolatomic_pool_dma__ro_after_init;staticunsignedlongpool_size_dma;-staticstructgen_pool*atomic_pool_dma32__ro_after_init;+staticstructdma_gen_poolatomic_pool_dma32__ro_after_init;staticunsignedlongpool_size_dma32;-staticstructgen_pool*atomic_pool_kernel__ro_after_init;+staticstructdma_gen_poolatomic_pool_kernel__ro_after_init;staticunsignedlongpool_size_kernel;/* Size can be defined by the coherent_pool command line */
@@ -126,11 +135,15 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,return0;encrypt_mapping:-ret=set_memory_encrypted((unsignedlong)page_to_virt(page),-1<<order);-if(WARN_ON_ONCE(ret)){-/* Decrypt succeeded but encrypt failed, purposely leak */-gotoout;+if(dma_pool->unencrypted){+intrc;++rc=set_memory_encrypted((unsignedlong)page_to_virt(page),+1<<order);+if(WARN_ON_ONCE(rc)){+/* Decrypt succeeded but encrypt failed, purposely leak */+gotoout;+}}remove_mapping:#ifdef CONFIG_DMA_DIRECT_REMAP
@@ -142,46 +155,52 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,returnret;}-staticvoidatomic_pool_resize(structgen_pool*pool,gfp_tgfp)+staticvoidatomic_pool_resize(structdma_gen_pool*dma_pool,gfp_tgfp){-if(pool&&gen_pool_avail(pool)<atomic_pool_size)-atomic_pool_expand(pool,gen_pool_size(pool),gfp);+if(dma_pool->pool&&gen_pool_avail(dma_pool->pool)<atomic_pool_size)+atomic_pool_expand(dma_pool,gen_pool_size(dma_pool->pool),gfp);}staticvoidatomic_pool_work_fn(structwork_struct*work){if(IS_ENABLED(CONFIG_ZONE_DMA))-atomic_pool_resize(atomic_pool_dma,+atomic_pool_resize(&atomic_pool_dma,GFP_KERNEL|GFP_DMA);if(IS_ENABLED(CONFIG_ZONE_DMA32))-atomic_pool_resize(atomic_pool_dma32,+atomic_pool_resize(&atomic_pool_dma32,GFP_KERNEL|GFP_DMA32);-atomic_pool_resize(atomic_pool_kernel,GFP_KERNEL);+atomic_pool_resize(&atomic_pool_kernel,GFP_KERNEL);}-static__initstructgen_pool*__dma_atomic_pool_init(size_tpool_size,-gfp_tgfp)+static__initstructdma_gen_pool*__dma_atomic_pool_init(structdma_gen_pool*dma_pool,+size_tpool_size,gfp_tgfp){-structgen_pool*pool;intret;-pool=gen_pool_create(PAGE_SHIFT,NUMA_NO_NODE);-if(!pool)+dma_pool->pool=gen_pool_create(PAGE_SHIFT,NUMA_NO_NODE);+if(!dma_pool->pool)returnNULL;-gen_pool_set_algo(pool,gen_pool_first_fit_order_align,NULL);+gen_pool_set_algo(dma_pool->pool,gen_pool_first_fit_order_align,NULL);++/* if platform is using memory encryption atomic pools are by default decrypted. */+if(cc_platform_has(CC_ATTR_MEM_ENCRYPT))+dma_pool->unencrypted=true;+else+dma_pool->unencrypted=false;
I believe that’s a good start, although we might need to have more
fine grained policies in the future as CC guests might need
encrypted pools
Reviewed-by: Mostafa Saleh <smostafa@google.com>
Thanks,
Mostafa
quoted hunk
- ret = atomic_pool_expand(pool, pool_size, gfp);+ ret = atomic_pool_expand(dma_pool, pool_size, gfp); if (ret) {- gen_pool_destroy(pool);+ gen_pool_destroy(dma_pool->pool);+ dma_pool->pool = NULL; pr_err("DMA: failed to allocate %zu KiB %pGg pool for atomic allocation\n", pool_size >> 10, &gfp); return NULL; } pr_info("DMA: preallocated %zu KiB %pGg pool for atomic allocations\n",- gen_pool_size(pool) >> 10, &gfp);- return pool;+ gen_pool_size(dma_pool->pool) >> 10, &gfp);+ return dma_pool; } #ifdef CONFIG_ZONE_DMA32
@@ -207,21 +226,22 @@ static int __init dma_atomic_pool_init(void) /* All memory might be in the DMA zone(s) to begin with */ if (has_managed_zone(ZONE_NORMAL)) {- atomic_pool_kernel = __dma_atomic_pool_init(atomic_pool_size,- GFP_KERNEL);- if (!atomic_pool_kernel)+ __dma_atomic_pool_init(&atomic_pool_kernel, atomic_pool_size, GFP_KERNEL);+ if (!atomic_pool_kernel.pool) ret = -ENOMEM; }+ if (has_managed_dma()) {- atomic_pool_dma = __dma_atomic_pool_init(atomic_pool_size,- GFP_KERNEL | GFP_DMA);- if (!atomic_pool_dma)+ __dma_atomic_pool_init(&atomic_pool_dma, atomic_pool_size,+ GFP_KERNEL | GFP_DMA);+ if (!atomic_pool_dma.pool) ret = -ENOMEM; }+ if (has_managed_dma32) {- atomic_pool_dma32 = __dma_atomic_pool_init(atomic_pool_size,- GFP_KERNEL | GFP_DMA32);- if (!atomic_pool_dma32)+ __dma_atomic_pool_init(&atomic_pool_dma32, atomic_pool_size,+ GFP_KERNEL | GFP_DMA32);+ if (!atomic_pool_dma32.pool) ret = -ENOMEM; }
On Tue, May 12, 2026 at 02:33:59PM +0530, Aneesh Kumar K.V (Arm) wrote:
quoted hunk
Teach swiotlb to distinguish between encrypted and decrypted bounce
buffer pools, and make allocation and mapping paths select a pool whose
state matches the requested DMA attributes.
Add a decrypted flag to io_tlb_mem, initialize it for the default and
restricted pools, and propagate DMA_ATTR_CC_SHARED into swiotlb pool
allocation. Reject swiotlb alloc/map requests when the selected pool does
not match the required encrypted/decrypted state.
Also return DMA addresses with the matching phys_to_dma_{encrypted,
unencrypted} helper so the DMA address encoding stays consistent with the
chosen pool.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
include/linux/dma-direct.h | 10 ++++
include/linux/swiotlb.h | 8 ++-
kernel/dma/direct.c | 14 +++--
kernel/dma/swiotlb.c | 108 +++++++++++++++++++++++++++----------
4 files changed, 107 insertions(+), 33 deletions(-)
@@ -1411,6 +1436,16 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,if(cc_platform_has(CC_ATTR_MEM_ENCRYPT))pr_warn_once("Memory encryption is active and system is using DMA bounce buffers\n");+/*+*ifwearetryingtoswiotlbmapadecryptedpaddrorthepaddrisencrypted+*butthedeviceisforcingdecryption,usedecryptedio_tlb_mem+*/+if((attrs&DMA_ATTR_CC_SHARED)||force_dma_unencrypted(dev))+require_decrypted=true;++if(require_decrypted!=mem->unencrypted)+return(phys_addr_t)DMA_MAPPING_ERROR;+/**ThedefaultswiotlbmemorypoolisallocatedwithPAGE_SIZE*alignment.Ifamappingisrequestedwithlargeralignment,
@@ -1608,8 +1643,14 @@ dma_addr_t swiotlb_map(struct device *dev, phys_addr_t paddr, size_t size,if(swiotlb_addr==(phys_addr_t)DMA_MAPPING_ERROR)returnDMA_MAPPING_ERROR;-/* Ensure that the address returned is DMA'ble */-dma_addr=phys_to_dma_unencrypted(dev,swiotlb_addr);+/*+*Usetheallocatedio_tlb_memencryptiontypetodeterminedmaaddr.+*/+if(dev->dma_io_tlb_mem->unencrypted)+dma_addr=phys_to_dma_unencrypted(dev,swiotlb_addr);+else+dma_addr=phys_to_dma_encrypted(dev,swiotlb_addr);+if(unlikely(!dma_capable(dev,dma_addr,size,true))){__swiotlb_tbl_unmap_single(dev,swiotlb_addr,size,dir,attrs|DMA_ATTR_SKIP_CPU_SYNC,
@@ -1853,9 +1898,18 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,kfree(mem);return-ENOMEM;}+/*+*ifplatformsupportsmemoryencryption,+*restrictedmempoolisdecryptedbydefault+*/+if(cc_platform_has(CC_ATTR_MEM_ENCRYPT)){+mem->unencrypted=true;+set_memory_decrypted((unsignedlong)phys_to_virt(rmem->base),+rmem->size>>PAGE_SHIFT);+}else{+mem->unencrypted=false;+}
This breaks pKVM as it doesn’t set CC_ATTR_MEM_ENCRYPT, so all virtio
traffic now fails.
Also, by design, some drivers are clueless about bouncing, so
I believe that the pool should have a way to control it’s property
(encrypted or decrypted) and that takes priority over whatever
attributes comes from allocation.
And that brings us to the same point whether it’s better to return
the memory along with it’s state or we pass the requested state.
I think for other cases it’s fine for the device/DMA-API to dictate
the attrs, but not in restricted-dma case, the firmware just knows better.
Thanks,
Mostafa
From: Jason Gunthorpe <jgg@ziepe.ca> Date: 2026-05-13 17:24:54
On Wed, May 13, 2026 at 02:27:14PM +0000, Mostafa Saleh wrote:
quoted
+ /*+ * if platform supports memory encryption,+ * restricted mem pool is decrypted by default+ */+ if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {+ mem->unencrypted = true;+ set_memory_decrypted((unsigned long)phys_to_virt(rmem->base),+ rmem->size >> PAGE_SHIFT);+ } else {+ mem->unencrypted = false;+ }
This breaks pKVM as it doesn’t set CC_ATTR_MEM_ENCRYPT, so all virtio
traffic now fails.
How will pKVM signal what kind of memory the DMA needs then?
Does it use set_memory_decrypted()? How can it use
set_memory_decrypted() without offering CC_ATTR_MEM_ENCRYPT ?
Also, by design, some drivers are clueless about bouncing, so
Oh? What does this mean? We take quite a dim view of drivers mis-using
the DMA API..
I believe that the pool should have a way to control it’s property
(encrypted or decrypted) and that takes priority over whatever
attributes comes from allocation.
We should get here because dma_capable() fails, and then swiotlb needs
to return something that makes dma_capable() succeed. Yes, it should
return details about the thing it decided, but it shouldn't have been
pre-created with some idea how to make dma_capable() work.
If dma_capable() can fail, then swiotlb should know exactly what to do
to fix it.
If pkvm wants to use the hacky scheme where you force a swiotlb pool
configuration during arch init with force swiotlb that's a somewhat
different flow and, sure the forced pool should force do whatever it
is forced to.
But lets try to keep them seperated in the discussion..
And that brings us to the same point whether it’s better to return
the memory along with it’s state or we pass the requested state.
I think for other cases it’s fine for the device/DMA-API to dictate
the attrs, but not in restricted-dma case, the firmware just knows better.
The memory type must be returned back at some level so downstream
things can do the right transformation of the phys_addr_t.
One of the aspirational CC things that should work is a T=1 device
tries to DMA from a decrypted page, finds the address is above the dma
limit of the device, so it bounces it with SWIOTLB to an encrypted low
address page and then the DMA API internal flow switiches from working
with decrypted to encrypted phys_addr_t.
If we can make that work then maybe the flows are designed correctly.
Jason
On Tue, May 12, 2026 at 02:33:56PM +0530, Aneesh Kumar K.V (Arm) wrote:
quoted
Move swiotlb allocation out of __dma_direct_alloc_pages() and handle it in
dma_direct_alloc() / dma_direct_alloc_pages().
This is needed for follow-up changes that simplify the handling of
memory encryption/decryption based on the DMA attribute flags.
swiotlb backing pages are already mapped decrypted by
swiotlb_update_mem_attributes() and rmem_swiotlb_device_init(), so
dma-direct should not call dma_set_decrypted() on allocation nor
dma_set_encrypted() on free for swiotlb-backed memory.
Update alloc/free paths to detect swiotlb-backed pages and skip
encrypt/decrypt transitions for those paths. Keep the existing highmem
rejection in dma_direct_alloc_pages() for swiotlb allocations.
Only for "restricted-dma-pool", we currently set `for_alloc = true`, while
rmem_swiotlb_device_init() decrypts the whole pool up front. This pool is
typically used together with "shared-dma-pool", where the shared region is
accessed after remap/ioremap and the returned address is suitable for
decrypted memory access. So existing code paths remain valid.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/direct.c | 44 +++++++++++++++++++++++++++++++++++++-------
1 file changed, 37 insertions(+), 7 deletions(-)
@@ -250,11 +248,21 @@ void *dma_direct_alloc(struct device *dev, size_t size,dma_direct_use_pool(dev,gfp))returndma_direct_alloc_from_pool(dev,size,dma_handle,gfp);+if(is_swiotlb_for_alloc(dev)){+page=dma_direct_alloc_swiotlb(dev,size);+if(page){+mark_mem_decrypt=false;+gotosetup_page;+}+returnNULL;+}+/* we always manually zero the memory once we are done */page=__dma_direct_alloc_pages(dev,size,gfp&~__GFP_ZERO,true);if(!page)returnNULL;+setup_page:/**dma_alloc_contiguouscanreturnhighmempagesdependingona*combinationthecma=argumentsandper-archsetup.Theseneedtobe
I was not sure whether we need dma_page. As shown in this series, we can
simplify the allocation and free paths without adding new abstractions
like dma_page.
@@ -338,12 +347,15 @@ void dma_direct_free(struct device *dev, size_t size, dma_free_from_pool(dev, cpu_addr, PAGE_ALIGN(size))) return;+ if (swiotlb_find_pool(dev, dma_to_phys(dev, dma_addr)))+ mark_mem_encrypted = false;+ if (is_vmalloc_addr(cpu_addr)) { vunmap(cpu_addr); } else { if (IS_ENABLED(CONFIG_ARCH_HAS_DMA_CLEAR_UNCACHED)) arch_dma_clear_uncached(cpu_addr, size);- if (dma_set_encrypted(dev, cpu_addr, size))+ if (mark_mem_encrypted && dma_set_encrypted(dev, cpu_addr, size)) return; }
@@ -359,6 +371,19 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size, if (force_dma_unencrypted(dev) && dma_direct_use_pool(dev, gfp)) return dma_direct_alloc_from_pool(dev, size, dma_handle, gfp);+ if (is_swiotlb_for_alloc(dev)) {+ page = dma_direct_alloc_swiotlb(dev, size);+ if (!page)+ return NULL;++ if (PageHighMem(page)) {
My understanding is that rmem_swiotlb_device_init() asserts that there
is no PageHighMem()? Also a similar check doesn’t exist in
dma_direct_alloc().
The reason I added that HighMem check is that __dma_direct_alloc_pages()
already has that check.
page = dma_alloc_contiguous(dev, size, gfp);
if (page) {
if (dma_coherent_ok(dev, page_to_phys(page), size) &&
(allow_highmem || !PageHighMem(page)))
return page;
dma_free_contiguous(dev, page, size);
}
I understand that the current usage of swiotlb alloc is restricted to
restricted memory, and it will not return HighMem pages. I will drop
this hunk from the patch.
-aneesh
On Tue, May 12, 2026 at 02:33:57PM +0530, Aneesh Kumar K.V (Arm) wrote:
quoted
Propagate force_dma_unencrypted() into DMA_ATTR_CC_SHARED in the
dma-direct allocation path and use the attribute to drive the related
decisions.
This updates dma_direct_alloc(), dma_direct_free(), and
dma_direct_alloc_pages() to fold the forced unencrypted case into attrs.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/direct.c | 44 ++++++++++++++++++++++++++++++++++++--------
1 file changed, 36 insertions(+), 8 deletions(-)
What about dma_direct_free_pages()? Nothing inside uses attrs, but
that’s quite similar to dma_direct_alloc_pages()
Also, at this point, shouldn’t this patch also remove
force_dma_unencrypted() calls from dma_set_decrypted() and
dma_set_encrypted()?
The final change have
void dma_direct_free_pages(struct device *dev, size_t size,
...
{
/*
* if the device had requested for an unencrypted buffer,
* convert it to encrypted on free
*/
bool mark_mem_encrypted = force_dma_unencrypted(dev);
That got added by "dma-direct: select DMA address encoding from DMA_ATTR_CC_SHARED "
I'll move that hunk into this patch. The overall goal is
dma_direct_alloc(.. attrs)
{
if (force_dma_unencrypted(dev))
attrs |= DMA_ATTR_CC_SHARED;
}
dma_direct_free(.. attrs)
{
if (force_dma_unencrypted(dev)) {
attrs |= DMA_ATTR_CC_SHARED;
mark_mem_encrypted = true;
}
if (swiotlb_find_pool(dev, dma_to_phys(dev, dma_addr)))
mark_mem_encrypted = false;
}
dma_direct_alloc_pages()
{
if (force_dma_unencrypted(dev))
attrs |= DMA_ATTR_CC_SHARED;
}
dma_direct_free_pages()
{
bool mark_mem_encrypted = force_dma_unencrypted(dev);
if (swiotlb_find_pool(dev, page_to_phys(page)))
mark_mem_encrypted = false;
}
-aneesh
On Tue, May 12, 2026 at 02:33:59PM +0530, Aneesh Kumar K.V (Arm) wrote:
quoted
Teach swiotlb to distinguish between encrypted and decrypted bounce
buffer pools, and make allocation and mapping paths select a pool whose
state matches the requested DMA attributes.
Add a decrypted flag to io_tlb_mem, initialize it for the default and
restricted pools, and propagate DMA_ATTR_CC_SHARED into swiotlb pool
allocation. Reject swiotlb alloc/map requests when the selected pool does
not match the required encrypted/decrypted state.
Also return DMA addresses with the matching phys_to_dma_{encrypted,
unencrypted} helper so the DMA address encoding stays consistent with the
chosen pool.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
include/linux/dma-direct.h | 10 ++++
include/linux/swiotlb.h | 8 ++-
kernel/dma/direct.c | 14 +++--
kernel/dma/swiotlb.c | 108 +++++++++++++++++++++++++++----------
4 files changed, 107 insertions(+), 33 deletions(-)
@@ -1411,6 +1436,16 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,if(cc_platform_has(CC_ATTR_MEM_ENCRYPT))pr_warn_once("Memory encryption is active and system is using DMA bounce buffers\n");+/*+*ifwearetryingtoswiotlbmapadecryptedpaddrorthepaddrisencrypted+*butthedeviceisforcingdecryption,usedecryptedio_tlb_mem+*/+if((attrs&DMA_ATTR_CC_SHARED)||force_dma_unencrypted(dev))+require_decrypted=true;++if(require_decrypted!=mem->unencrypted)+return(phys_addr_t)DMA_MAPPING_ERROR;+/**ThedefaultswiotlbmemorypoolisallocatedwithPAGE_SIZE*alignment.Ifamappingisrequestedwithlargeralignment,
@@ -1608,8 +1643,14 @@ dma_addr_t swiotlb_map(struct device *dev, phys_addr_t paddr, size_t size,if(swiotlb_addr==(phys_addr_t)DMA_MAPPING_ERROR)returnDMA_MAPPING_ERROR;-/* Ensure that the address returned is DMA'ble */-dma_addr=phys_to_dma_unencrypted(dev,swiotlb_addr);+/*+*Usetheallocatedio_tlb_memencryptiontypetodeterminedmaaddr.+*/+if(dev->dma_io_tlb_mem->unencrypted)+dma_addr=phys_to_dma_unencrypted(dev,swiotlb_addr);+else+dma_addr=phys_to_dma_encrypted(dev,swiotlb_addr);+if(unlikely(!dma_capable(dev,dma_addr,size,true))){__swiotlb_tbl_unmap_single(dev,swiotlb_addr,size,dir,attrs|DMA_ATTR_SKIP_CPU_SYNC,
@@ -1853,9 +1898,18 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,kfree(mem);return-ENOMEM;}+/*+*ifplatformsupportsmemoryencryption,+*restrictedmempoolisdecryptedbydefault+*/+if(cc_platform_has(CC_ATTR_MEM_ENCRYPT)){+mem->unencrypted=true;+set_memory_decrypted((unsignedlong)phys_to_virt(rmem->base),+rmem->size>>PAGE_SHIFT);+}else{+mem->unencrypted=false;+}
This breaks pKVM as it doesn’t set CC_ATTR_MEM_ENCRYPT, so all virtio
traffic now fails.
Also, by design, some drivers are clueless about bouncing, so
I believe that the pool should have a way to control it’s property
(encrypted or decrypted) and that takes priority over whatever
attributes comes from allocation.
And that brings us to the same point whether it’s better to return
the memory along with it’s state or we pass the requested state.
I think for other cases it’s fine for the device/DMA-API to dictate
the attrs, but not in restricted-dma case, the firmware just knows better.
Is it that the pKVM guest kernel does not have awareness of
encrypted/decrypted DMA allocations? Instead, the firmware attaches
hypervisor-shared pages to the device via restricted-dma-pool? The
kernel then has swiotlb->for_alloc = true, and hence all DMA allocations
go through the restricted-dma-pool?
Given that pKVM supports pkvm_set_memory_encrypted() and
pkvm_set_memory_decrypted(), can we consider adding CC_ATTR_MEM_ENCRYPT
support to pKVM? It would also be good to investigate whether we can set
force_dma_unencrypted(dev) to true where needed.
I agree that this patch, as it stands, can break pKVM because we are now
missing the set_memory_decrypted() call required for pKVM to work.
We now mark the swiotlb io_tlb_mem as unencrypted/encrypted in the guest
using struct io_tlb_mem->unencrypted. I am not clear what we can use for
pKVM to conditionalize this so that it works for both protected and
unprotected guests.
-aneesh
And that brings us to the same point whether it’s better to return
the memory along with it’s state or we pass the requested state.
I think for other cases it’s fine for the device/DMA-API to dictate
the attrs, but not in restricted-dma case, the firmware just knows better.
The memory type must be returned back at some level so downstream
things can do the right transformation of the phys_addr_t.
One of the aspirational CC things that should work is a T=1 device
tries to DMA from a decrypted page, finds the address is above the dma
limit of the device, so it bounces it with SWIOTLB to an encrypted low
address page and then the DMA API internal flow switiches from working
with decrypted to encrypted phys_addr_t.
If we can make that work then maybe the flows are designed correctly.
That is what this patch series aims to do. dma_direct_map_phys() will
derive the DMA address based on the attributes of the physical address:
if (attrs & DMA_ATTR_CC_SHARED)
dma_addr = phys_to_dma_unencrypted(dev, phys);
else
dma_addr = phys_to_dma_encrypted(dev, phys);
If that fails the dma_capable() check, we fall back to swiotlb_map():
if (unlikely(!dma_capable(dev, dma_addr, size, true, attrs)))
return swiotlb_map(dev, phys, size, dir, attrs);
We currently do not have an encrypted SWIOTLB pool, but once that is
supported, swiotlb_map() should do the right thing and return the
correct encrypted dma_addr_t based on io_tlb_mem->unencrypted:
if (dev->dma_io_tlb_mem->unencrypted) {
dma_addr = phys_to_dma_unencrypted(dev, swiotlb_addr);
attrs |= DMA_ATTR_CC_SHARED;
} else {
dma_addr = phys_to_dma_encrypted(dev, swiotlb_addr);
}
-aneesh
nit: If we fail to find a matching pool, a slightly misleading message
is printed as pool_found = false
The message printed is
WARN(1, "Failed to get suitable pool for %s\n", dev_name(dev));
That is correct, isn’t it? The kernel failed to find a pool with the
correct encryption attribute. For example, the request was for an
encrypted allocation from the pool, but no encrypted pool was available.
quoted
pool_found = true;- page = __dma_alloc_from_pool(dev, size, pool, cpu_addr,+ page = __dma_alloc_from_pool(dev, size, dma_pool->pool, cpu_addr, phys_addr_ok); if (page) return page;
nit: If we fail to find a matching pool, a slightly misleading message
is printed as pool_found = false
The message printed is
WARN(1, "Failed to get suitable pool for %s\n", dev_name(dev));
That is correct, isn’t it? The kernel failed to find a pool with the
correct encryption attribute. For example, the request was for an
encrypted allocation from the pool, but no encrypted pool was available.
Sure, I’d prefer a clearer print in that case, especially since that’s new code:
“Only {encrypted/decrypted} pool found for a {encrypted/decrypted} alloction”
But no strong opinion.
Thanks,
Mostafa
quoted
quoted
pool_found = true;- page = __dma_alloc_from_pool(dev, size, pool, cpu_addr,+ page = __dma_alloc_from_pool(dev, size, dma_pool->pool, cpu_addr, phys_addr_ok); if (page) return page;
On Wed, May 13, 2026 at 02:24:50PM -0300, Jason Gunthorpe wrote:
On Wed, May 13, 2026 at 02:27:14PM +0000, Mostafa Saleh wrote:
quoted
quoted
+ /*+ * if platform supports memory encryption,+ * restricted mem pool is decrypted by default+ */+ if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {+ mem->unencrypted = true;+ set_memory_decrypted((unsigned long)phys_to_virt(rmem->base),+ rmem->size >> PAGE_SHIFT);+ } else {+ mem->unencrypted = false;+ }
This breaks pKVM as it doesn’t set CC_ATTR_MEM_ENCRYPT, so all virtio
traffic now fails.
How will pKVM signal what kind of memory the DMA needs then?
Does it use set_memory_decrypted()? How can it use
set_memory_decrypted() without offering CC_ATTR_MEM_ENCRYPT ?
pKVM (hypervisor) doesn’t signal anything.
The VMM when running protected guests will use restricted dma-pools
for emulated vritio devices in the guest, which gets decrypted by
the guest kernel and hence shared with the host kernel, and then
traffic is bounced via the pool.
It’s also worth noting that bouncing here isn't just about visibility.
Because memory sharing operates at page granularity, bouncing sub-page
allocations through the restricted pool prevents adjacent, sensitive
guest data from being exposed to the untrusted host.
quoted
Also, by design, some drivers are clueless about bouncing, so
Oh? What does this mean? We take quite a dim view of drivers mis-using
the DMA API..
Maybe clueless is not the right word, I mean when virtio drivers use
the DMA API they don’t know whether it’s going to bounce or not as
that is decided by dma-direct (and in other cases by dma-iommu,
but not for pKVM).
quoted
I believe that the pool should have a way to control it’s property
(encrypted or decrypted) and that takes priority over whatever
attributes comes from allocation.
We should get here because dma_capable() fails, and then swiotlb needs
to return something that makes dma_capable() succeed. Yes, it should
return details about the thing it decided, but it shouldn't have been
pre-created with some idea how to make dma_capable() work.
That sounds neat, but at the end we have force_dma_unencrypted() in
dma_capable() which is just hardcoded to true/false by the platform.
How is that different from having the state static by the pool?
If dma_capable() can fail, then swiotlb should know exactly what to do
to fix it.
dma_capable() returns a bool, I don’t think it can know what exactly
went wrong (based on address, size, attrs, dev...)
If pkvm wants to use the hacky scheme where you force a swiotlb pool
configuration during arch init with force swiotlb that's a somewhat
different flow and, sure the forced pool should force do whatever it
is forced to.
But lets try to keep them seperated in the discussion..
While we can debate the aesthetics of the setup , this is
the exisitng behaviour for Linux, which existed for years
and pKVM relies on and is used extensively.
And, this patch alters that long-standing logic and introduces
a functional regression.
We can address this by either adjusting this patch or by changing
pKVM guests to be more aligned with other CCA guests which is
something I have been wondering about if it would help reduce
bouncing.
quoted
And that brings us to the same point whether it’s better to return
the memory along with it’s state or we pass the requested state.
I think for other cases it’s fine for the device/DMA-API to dictate
the attrs, but not in restricted-dma case, the firmware just knows better.
The memory type must be returned back at some level so downstream
things can do the right transformation of the phys_addr_t.
Agreed, I believe that will be needed at least for
SWIOTLB/restricted-dma -> dma-API interactions.
One of the aspirational CC things that should work is a T=1 device
tries to DMA from a decrypted page, finds the address is above the dma
limit of the device, so it bounces it with SWIOTLB to an encrypted low
address page and then the DMA API internal flow switiches from working
with decrypted to encrypted phys_addr_t.
If we can make that work then maybe the flows are designed correctly.
Mmm, I am not sure I understand this one, shouldn’t the device also be
notified about the switch in memory state, if it expects to read/write
decrypted memory, how would that work if the kernel changes it to an
encrypted one?
Thanks,
Mostafa
On Thu, May 14, 2026 at 11:24:42AM +0530, Aneesh Kumar K.V wrote:
Mostafa Saleh [off-list ref] writes:
quoted
On Tue, May 12, 2026 at 02:33:59PM +0530, Aneesh Kumar K.V (Arm) wrote:
quoted
Teach swiotlb to distinguish between encrypted and decrypted bounce
buffer pools, and make allocation and mapping paths select a pool whose
state matches the requested DMA attributes.
Add a decrypted flag to io_tlb_mem, initialize it for the default and
restricted pools, and propagate DMA_ATTR_CC_SHARED into swiotlb pool
allocation. Reject swiotlb alloc/map requests when the selected pool does
not match the required encrypted/decrypted state.
Also return DMA addresses with the matching phys_to_dma_{encrypted,
unencrypted} helper so the DMA address encoding stays consistent with the
chosen pool.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
include/linux/dma-direct.h | 10 ++++
include/linux/swiotlb.h | 8 ++-
kernel/dma/direct.c | 14 +++--
kernel/dma/swiotlb.c | 108 +++++++++++++++++++++++++++----------
4 files changed, 107 insertions(+), 33 deletions(-)
@@ -1411,6 +1436,16 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,if(cc_platform_has(CC_ATTR_MEM_ENCRYPT))pr_warn_once("Memory encryption is active and system is using DMA bounce buffers\n");+/*+*ifwearetryingtoswiotlbmapadecryptedpaddrorthepaddrisencrypted+*butthedeviceisforcingdecryption,usedecryptedio_tlb_mem+*/+if((attrs&DMA_ATTR_CC_SHARED)||force_dma_unencrypted(dev))+require_decrypted=true;++if(require_decrypted!=mem->unencrypted)+return(phys_addr_t)DMA_MAPPING_ERROR;+/**ThedefaultswiotlbmemorypoolisallocatedwithPAGE_SIZE*alignment.Ifamappingisrequestedwithlargeralignment,
@@ -1608,8 +1643,14 @@ dma_addr_t swiotlb_map(struct device *dev, phys_addr_t paddr, size_t size,if(swiotlb_addr==(phys_addr_t)DMA_MAPPING_ERROR)returnDMA_MAPPING_ERROR;-/* Ensure that the address returned is DMA'ble */-dma_addr=phys_to_dma_unencrypted(dev,swiotlb_addr);+/*+*Usetheallocatedio_tlb_memencryptiontypetodeterminedmaaddr.+*/+if(dev->dma_io_tlb_mem->unencrypted)+dma_addr=phys_to_dma_unencrypted(dev,swiotlb_addr);+else+dma_addr=phys_to_dma_encrypted(dev,swiotlb_addr);+if(unlikely(!dma_capable(dev,dma_addr,size,true))){__swiotlb_tbl_unmap_single(dev,swiotlb_addr,size,dir,attrs|DMA_ATTR_SKIP_CPU_SYNC,
@@ -1853,9 +1898,18 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,kfree(mem);return-ENOMEM;}+/*+*ifplatformsupportsmemoryencryption,+*restrictedmempoolisdecryptedbydefault+*/+if(cc_platform_has(CC_ATTR_MEM_ENCRYPT)){+mem->unencrypted=true;+set_memory_decrypted((unsignedlong)phys_to_virt(rmem->base),+rmem->size>>PAGE_SHIFT);+}else{+mem->unencrypted=false;+}
This breaks pKVM as it doesn’t set CC_ATTR_MEM_ENCRYPT, so all virtio
traffic now fails.
Also, by design, some drivers are clueless about bouncing, so
I believe that the pool should have a way to control it’s property
(encrypted or decrypted) and that takes priority over whatever
attributes comes from allocation.
And that brings us to the same point whether it’s better to return
the memory along with it’s state or we pass the requested state.
I think for other cases it’s fine for the device/DMA-API to dictate
the attrs, but not in restricted-dma case, the firmware just knows better.
Is it that the pKVM guest kernel does not have awareness of
encrypted/decrypted DMA allocations? Instead, the firmware attaches
hypervisor-shared pages to the device via restricted-dma-pool? The
kernel then has swiotlb->for_alloc = true, and hence all DMA allocations
go through the restricted-dma-pool?
Yes.
Given that pKVM supports pkvm_set_memory_encrypted() and
pkvm_set_memory_decrypted(), can we consider adding CC_ATTR_MEM_ENCRYPT
support to pKVM? It would also be good to investigate whether we can set
force_dma_unencrypted(dev) to true where needed.
I was looking in to that, but it didn't work because
force_dma_unencrypted() is broken with restricted-dma due to the
double decryption issue, that's when I sent my first series [1]
May be we should land some basic fixes for that path so we can
convert pKVM, then we do the full rework.
I will revive my old work and see if I can send a RFC.
[1] https://lore.kernel.org/all/20260305170335.963568-1-smostafa@google.com/
I agree that this patch, as it stands, can break pKVM because we are now
missing the set_memory_decrypted() call required for pKVM to work.
We now mark the swiotlb io_tlb_mem as unencrypted/encrypted in the guest
using struct io_tlb_mem->unencrypted. I am not clear what we can use for
pKVM to conditionalize this so that it works for both protected and
unprotected guests.
There is no problem with non-protected guests as they don't use memory
encryption, my initial thought was that th encrpyted/decrypted is
per-pool property which is decided by FW (device-tree).
Thanks,
Mostafa
From: Jason Gunthorpe <jgg@ziepe.ca> Date: 2026-05-14 12:35:32
quoted
How will pKVM signal what kind of memory the DMA needs then?
Does it use set_memory_decrypted()? How can it use
set_memory_decrypted() without offering CC_ATTR_MEM_ENCRYPT ?
pKVM (hypervisor) doesn’t signal anything.
The VMM when running protected guests will use restricted dma-pools
for emulated vritio devices in the guest, which gets decrypted by
the guest kernel and hence shared with the host kernel, and then
traffic is bounced via the pool.
That really does sound like CC and set_memory_decrypted() to me..
It’s also worth noting that bouncing here isn't just about visibility.
Because memory sharing operates at page granularity, bouncing sub-page
allocations through the restricted pool prevents adjacent, sensitive
guest data from being exposed to the untrusted host.
That's a somewhat different problem, we have the dev->trusted stuff
that is supposed to deal with this kind of security. We need it for
IOMMU based systems too, eg hot plug thunderbolt should have it.
Then CC issue is more that the DMA API can't decrypt random passed in
memory because doing so often requires changing the PTEs pointing at
the page so it would break everything if done transparently.
quoted
quoted
I believe that the pool should have a way to control it’s property
(encrypted or decrypted) and that takes priority over whatever
attributes comes from allocation.
We should get here because dma_capable() fails, and then swiotlb needs
to return something that makes dma_capable() succeed. Yes, it should
return details about the thing it decided, but it shouldn't have been
pre-created with some idea how to make dma_capable() work.
That sounds neat, but at the end we have force_dma_unencrypted() in
dma_capable() which is just hardcoded to true/false by the platform.
For now, the next step is it becomes per-device and dynamic during the
device lifecycle.
How is that different from having the state static by the pool?
statically attached pools to the device are not so flexible when
devices have dynamically changing capabilities..
quoted
If dma_capable() can fail, then swiotlb should know exactly what to do
to fix it.
dma_capable() returns a bool, I don’t think it can know what exactly
went wrong (based on address, size, attrs, dev...)
Yes, but I think the design is swiotlb is supposed to re-inspect what
is going on against the limits dma_capable checks and then select the
correct remedy..
While we can debate the aesthetics of the setup , this is
the exisitng behaviour for Linux, which existed for years
and pKVM relies on and is used extensively.
And, this patch alters that long-standing logic and introduces
a functional regression.
Yeah, Aneesh needs to do something here, I'm pointing out it is
entirely seperate thing from the CC path we are working on which is
decoupling CC from reylying on force swiotlb.
We can address this by either adjusting this patch or by changing
pKVM guests to be more aligned with other CCA guests which is
something I have been wondering about if it would help reduce
bouncing.
Every time I look at pkvm I think it is just ARM CCA with a different
design and no access to the unique HW features..
quoted
If we can make that work then maybe the flows are designed correctly.
Mmm, I am not sure I understand this one, shouldn’t the device also be
notified about the switch in memory state, if it expects to read/write
decrypted memory, how would that work if the kernel changes it to an
encrypted one?
Nothing on the device changes. In a CC world we put the device in a
T=0 or T=1 state before the driver loads and the expectation from the
DMA API is that the device will only use that T=x DMA type during
operation.
A T=1 state device can access all of memory, private or shared. Any
information the platform may need is encoded in the dma_addr_t or in
the S1 IOPTEs.
So we never need to tell the device driver what kind of memory the DMA
is targetting, and we NEVER expect a device in T=1 mode to have to
issue a T=0 DMA to use the DMA API.
In a pkvm world it should be the same, the S2 table for the SMMU will
control what the device can access, and if the SMMU points to a
"private" or "shared" page is not something the device needs to know
or care about.
Jason
On Thu, May 14, 2026 at 11:24:42AM +0530, Aneesh Kumar K.V wrote:
quoted
Mostafa Saleh [off-list ref] writes:
quoted
On Tue, May 12, 2026 at 02:33:59PM +0530, Aneesh Kumar K.V (Arm) wrote:
quoted
Teach swiotlb to distinguish between encrypted and decrypted bounce
buffer pools, and make allocation and mapping paths select a pool whose
state matches the requested DMA attributes.
Add a decrypted flag to io_tlb_mem, initialize it for the default and
restricted pools, and propagate DMA_ATTR_CC_SHARED into swiotlb pool
allocation. Reject swiotlb alloc/map requests when the selected pool does
not match the required encrypted/decrypted state.
Also return DMA addresses with the matching phys_to_dma_{encrypted,
unencrypted} helper so the DMA address encoding stays consistent with the
chosen pool.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
include/linux/dma-direct.h | 10 ++++
include/linux/swiotlb.h | 8 ++-
kernel/dma/direct.c | 14 +++--
kernel/dma/swiotlb.c | 108 +++++++++++++++++++++++++++----------
4 files changed, 107 insertions(+), 33 deletions(-)
@@ -1411,6 +1436,16 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,if(cc_platform_has(CC_ATTR_MEM_ENCRYPT))pr_warn_once("Memory encryption is active and system is using DMA bounce buffers\n");+/*+*ifwearetryingtoswiotlbmapadecryptedpaddrorthepaddrisencrypted+*butthedeviceisforcingdecryption,usedecryptedio_tlb_mem+*/+if((attrs&DMA_ATTR_CC_SHARED)||force_dma_unencrypted(dev))+require_decrypted=true;++if(require_decrypted!=mem->unencrypted)+return(phys_addr_t)DMA_MAPPING_ERROR;+/**ThedefaultswiotlbmemorypoolisallocatedwithPAGE_SIZE*alignment.Ifamappingisrequestedwithlargeralignment,
@@ -1608,8 +1643,14 @@ dma_addr_t swiotlb_map(struct device *dev, phys_addr_t paddr, size_t size,if(swiotlb_addr==(phys_addr_t)DMA_MAPPING_ERROR)returnDMA_MAPPING_ERROR;-/* Ensure that the address returned is DMA'ble */-dma_addr=phys_to_dma_unencrypted(dev,swiotlb_addr);+/*+*Usetheallocatedio_tlb_memencryptiontypetodeterminedmaaddr.+*/+if(dev->dma_io_tlb_mem->unencrypted)+dma_addr=phys_to_dma_unencrypted(dev,swiotlb_addr);+else+dma_addr=phys_to_dma_encrypted(dev,swiotlb_addr);+if(unlikely(!dma_capable(dev,dma_addr,size,true))){__swiotlb_tbl_unmap_single(dev,swiotlb_addr,size,dir,attrs|DMA_ATTR_SKIP_CPU_SYNC,
@@ -1853,9 +1898,18 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,kfree(mem);return-ENOMEM;}+/*+*ifplatformsupportsmemoryencryption,+*restrictedmempoolisdecryptedbydefault+*/+if(cc_platform_has(CC_ATTR_MEM_ENCRYPT)){+mem->unencrypted=true;+set_memory_decrypted((unsignedlong)phys_to_virt(rmem->base),+rmem->size>>PAGE_SHIFT);+}else{+mem->unencrypted=false;+}
This breaks pKVM as it doesn’t set CC_ATTR_MEM_ENCRYPT, so all virtio
traffic now fails.
Also, by design, some drivers are clueless about bouncing, so
I believe that the pool should have a way to control it’s property
(encrypted or decrypted) and that takes priority over whatever
attributes comes from allocation.
And that brings us to the same point whether it’s better to return
the memory along with it’s state or we pass the requested state.
I think for other cases it’s fine for the device/DMA-API to dictate
the attrs, but not in restricted-dma case, the firmware just knows better.
Is it that the pKVM guest kernel does not have awareness of
encrypted/decrypted DMA allocations? Instead, the firmware attaches
hypervisor-shared pages to the device via restricted-dma-pool? The
kernel then has swiotlb->for_alloc = true, and hence all DMA allocations
go through the restricted-dma-pool?
Yes.
quoted
Given that pKVM supports pkvm_set_memory_encrypted() and
pkvm_set_memory_decrypted(), can we consider adding CC_ATTR_MEM_ENCRYPT
support to pKVM? It would also be good to investigate whether we can set
force_dma_unencrypted(dev) to true where needed.
I was looking in to that, but it didn't work because
force_dma_unencrypted() is broken with restricted-dma due to the
double decryption issue, that's when I sent my first series [1]
May be we should land some basic fixes for that path so we can
convert pKVM, then we do the full rework.
I will revive my old work and see if I can send a RFC.
[1] https://lore.kernel.org/all/20260305170335.963568-1-smostafa@google.com/
With this series, can you check whether the only change needed is
something like the following?
modified kernel/dma/swiotlb.c
@@ -1905,7 +1905,8 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem, * if platform supports memory encryption, * restricted mem pool is decrypted by default */- if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {+ //if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {+ if (true) { mem->unencrypted = true; set_memory_decrypted((unsigned long)phys_to_virt(rmem->base), rmem->size >> PAGE_SHIFT);
quoted
I agree that this patch, as it stands, can break pKVM because we are now
missing the set_memory_decrypted() call required for pKVM to work.
We now mark the swiotlb io_tlb_mem as unencrypted/encrypted in the guest
using struct io_tlb_mem->unencrypted. I am not clear what we can use for
pKVM to conditionalize this so that it works for both protected and
unprotected guests.
There is no problem with non-protected guests as they don't use memory
encryption, my initial thought was that th encrpyted/decrypted is
per-pool property which is decided by FW (device-tree).
What I meant was that we need a generic way to identify a pKVM guest, so
that we can use it in the conditional above.
-aneesh
On Thu, May 14, 2026 at 06:18:05PM +0530, Aneesh Kumar K.V wrote:
quoted hunk
Mostafa Saleh [off-list ref] writes:
quoted
On Thu, May 14, 2026 at 11:24:42AM +0530, Aneesh Kumar K.V wrote:
quoted
Mostafa Saleh [off-list ref] writes:
quoted
On Tue, May 12, 2026 at 02:33:59PM +0530, Aneesh Kumar K.V (Arm) wrote:
quoted
Teach swiotlb to distinguish between encrypted and decrypted bounce
buffer pools, and make allocation and mapping paths select a pool whose
state matches the requested DMA attributes.
Add a decrypted flag to io_tlb_mem, initialize it for the default and
restricted pools, and propagate DMA_ATTR_CC_SHARED into swiotlb pool
allocation. Reject swiotlb alloc/map requests when the selected pool does
not match the required encrypted/decrypted state.
Also return DMA addresses with the matching phys_to_dma_{encrypted,
unencrypted} helper so the DMA address encoding stays consistent with the
chosen pool.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
include/linux/dma-direct.h | 10 ++++
include/linux/swiotlb.h | 8 ++-
kernel/dma/direct.c | 14 +++--
kernel/dma/swiotlb.c | 108 +++++++++++++++++++++++++++----------
4 files changed, 107 insertions(+), 33 deletions(-)
@@ -1411,6 +1436,16 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,if(cc_platform_has(CC_ATTR_MEM_ENCRYPT))pr_warn_once("Memory encryption is active and system is using DMA bounce buffers\n");+/*+*ifwearetryingtoswiotlbmapadecryptedpaddrorthepaddrisencrypted+*butthedeviceisforcingdecryption,usedecryptedio_tlb_mem+*/+if((attrs&DMA_ATTR_CC_SHARED)||force_dma_unencrypted(dev))+require_decrypted=true;++if(require_decrypted!=mem->unencrypted)+return(phys_addr_t)DMA_MAPPING_ERROR;+/**ThedefaultswiotlbmemorypoolisallocatedwithPAGE_SIZE*alignment.Ifamappingisrequestedwithlargeralignment,
@@ -1608,8 +1643,14 @@ dma_addr_t swiotlb_map(struct device *dev, phys_addr_t paddr, size_t size,if(swiotlb_addr==(phys_addr_t)DMA_MAPPING_ERROR)returnDMA_MAPPING_ERROR;-/* Ensure that the address returned is DMA'ble */-dma_addr=phys_to_dma_unencrypted(dev,swiotlb_addr);+/*+*Usetheallocatedio_tlb_memencryptiontypetodeterminedmaaddr.+*/+if(dev->dma_io_tlb_mem->unencrypted)+dma_addr=phys_to_dma_unencrypted(dev,swiotlb_addr);+else+dma_addr=phys_to_dma_encrypted(dev,swiotlb_addr);+if(unlikely(!dma_capable(dev,dma_addr,size,true))){__swiotlb_tbl_unmap_single(dev,swiotlb_addr,size,dir,attrs|DMA_ATTR_SKIP_CPU_SYNC,
@@ -1853,9 +1898,18 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,kfree(mem);return-ENOMEM;}+/*+*ifplatformsupportsmemoryencryption,+*restrictedmempoolisdecryptedbydefault+*/+if(cc_platform_has(CC_ATTR_MEM_ENCRYPT)){+mem->unencrypted=true;+set_memory_decrypted((unsignedlong)phys_to_virt(rmem->base),+rmem->size>>PAGE_SHIFT);+}else{+mem->unencrypted=false;+}
This breaks pKVM as it doesn’t set CC_ATTR_MEM_ENCRYPT, so all virtio
traffic now fails.
Also, by design, some drivers are clueless about bouncing, so
I believe that the pool should have a way to control it’s property
(encrypted or decrypted) and that takes priority over whatever
attributes comes from allocation.
And that brings us to the same point whether it’s better to return
the memory along with it’s state or we pass the requested state.
I think for other cases it’s fine for the device/DMA-API to dictate
the attrs, but not in restricted-dma case, the firmware just knows better.
Is it that the pKVM guest kernel does not have awareness of
encrypted/decrypted DMA allocations? Instead, the firmware attaches
hypervisor-shared pages to the device via restricted-dma-pool? The
kernel then has swiotlb->for_alloc = true, and hence all DMA allocations
go through the restricted-dma-pool?
Yes.
quoted
Given that pKVM supports pkvm_set_memory_encrypted() and
pkvm_set_memory_decrypted(), can we consider adding CC_ATTR_MEM_ENCRYPT
support to pKVM? It would also be good to investigate whether we can set
force_dma_unencrypted(dev) to true where needed.
I was looking in to that, but it didn't work because
force_dma_unencrypted() is broken with restricted-dma due to the
double decryption issue, that's when I sent my first series [1]
May be we should land some basic fixes for that path so we can
convert pKVM, then we do the full rework.
I will revive my old work and see if I can send a RFC.
[1] https://lore.kernel.org/all/20260305170335.963568-1-smostafa@google.com/
With this series, can you check whether the only change needed is
something like the following?
modified kernel/dma/swiotlb.c
@@ -1905,7 +1905,8 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem, * if platform supports memory encryption, * restricted mem pool is decrypted by default */- if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {+ //if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {+ if (true) { mem->unencrypted = true; set_memory_decrypted((unsigned long)phys_to_virt(rmem->base), rmem->size >> PAGE_SHIFT);
Yes, that boots, but I will need to do more tests.
quoted
quoted
I agree that this patch, as it stands, can break pKVM because we are now
missing the set_memory_decrypted() call required for pKVM to work.
We now mark the swiotlb io_tlb_mem as unencrypted/encrypted in the guest
using struct io_tlb_mem->unencrypted. I am not clear what we can use for
pKVM to conditionalize this so that it works for both protected and
unprotected guests.
There is no problem with non-protected guests as they don't use memory
encryption, my initial thought was that th encrpyted/decrypted is
per-pool property which is decided by FW (device-tree).
What I meant was that we need a generic way to identify a pKVM guest, so
that we can use it in the conditional above.
I have this patch, with that I can boot with your series unmodified,
but I will need to do more testing.
From d795b4c4ee2437587616b2b342e9996afe6d6680 Mon Sep 17 00:00:00 2001
From: Mostafa Saleh <smostafa@google.com>
Date: Thu, 14 May 2026 13:46:15 +0000
Subject: [PATCH] arm64/coco: Add pKVM as a CC platform
pKVM does support memory encryption, expose that to the rest of
the kernel through cc_platform_has()
At the moment, all devices inside the guest are emulated which
requires its memory to be shared back to the host (decrypted), so
set force_dma_unencrypted() to always return true.
Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
arch/arm64/include/asm/hypervisor.h | 6 ++++++
arch/arm64/include/asm/mem_encrypt.h | 3 ++-
arch/arm64/kernel/rsi.c | 12 ------------
arch/arm64/mm/init.c | 13 +++++++++++++
drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c | 5 +++++
5 files changed, 26 insertions(+), 13 deletions(-)
From: Jason Gunthorpe <jgg@ziepe.ca> Date: 2026-05-14 14:37:35
On Thu, May 14, 2026 at 06:18:05PM +0530, Aneesh Kumar K.V wrote:
quoted
There is no problem with non-protected guests as they don't use memory
encryption, my initial thought was that th encrpyted/decrypted is
per-pool property which is decided by FW (device-tree).
What I meant was that we need a generic way to identify a pKVM guest, so
that we can use it in the conditional above.
If I understood Mostafa's remarks I think different devices in the
guest need shared/decrypted and some don't? Ie a virtio hypervisor
device needs shared while a real PCI device doesn't? Is that right?
In CC terms that would be a mixture of T=0 and T=1 devices hardwired
and signaled by firwmare..
Ideally we'd have a flow where if the arch precreates a swiotlb pool
with special parameters this overrides all other decision making. Then
this series is about making CC NOT use that flow... ??
Jason
On Thu, May 14, 2026 at 06:18:05PM +0530, Aneesh Kumar K.V wrote:
quoted
Mostafa Saleh [off-list ref] writes:
quoted
On Thu, May 14, 2026 at 11:24:42AM +0530, Aneesh Kumar K.V wrote:
quoted
Mostafa Saleh [off-list ref] writes:
quoted
On Tue, May 12, 2026 at 02:33:59PM +0530, Aneesh Kumar K.V (Arm) wrote:
quoted
Teach swiotlb to distinguish between encrypted and decrypted bounce
buffer pools, and make allocation and mapping paths select a pool whose
state matches the requested DMA attributes.
Add a decrypted flag to io_tlb_mem, initialize it for the default and
restricted pools, and propagate DMA_ATTR_CC_SHARED into swiotlb pool
allocation. Reject swiotlb alloc/map requests when the selected pool does
not match the required encrypted/decrypted state.
Also return DMA addresses with the matching phys_to_dma_{encrypted,
unencrypted} helper so the DMA address encoding stays consistent with the
chosen pool.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
include/linux/dma-direct.h | 10 ++++
include/linux/swiotlb.h | 8 ++-
kernel/dma/direct.c | 14 +++--
kernel/dma/swiotlb.c | 108 +++++++++++++++++++++++++++----------
4 files changed, 107 insertions(+), 33 deletions(-)
@@ -1411,6 +1436,16 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,if(cc_platform_has(CC_ATTR_MEM_ENCRYPT))pr_warn_once("Memory encryption is active and system is using DMA bounce buffers\n");+/*+*ifwearetryingtoswiotlbmapadecryptedpaddrorthepaddrisencrypted+*butthedeviceisforcingdecryption,usedecryptedio_tlb_mem+*/+if((attrs&DMA_ATTR_CC_SHARED)||force_dma_unencrypted(dev))+require_decrypted=true;++if(require_decrypted!=mem->unencrypted)+return(phys_addr_t)DMA_MAPPING_ERROR;+/**ThedefaultswiotlbmemorypoolisallocatedwithPAGE_SIZE*alignment.Ifamappingisrequestedwithlargeralignment,
@@ -1608,8 +1643,14 @@ dma_addr_t swiotlb_map(struct device *dev, phys_addr_t paddr, size_t size,if(swiotlb_addr==(phys_addr_t)DMA_MAPPING_ERROR)returnDMA_MAPPING_ERROR;-/* Ensure that the address returned is DMA'ble */-dma_addr=phys_to_dma_unencrypted(dev,swiotlb_addr);+/*+*Usetheallocatedio_tlb_memencryptiontypetodeterminedmaaddr.+*/+if(dev->dma_io_tlb_mem->unencrypted)+dma_addr=phys_to_dma_unencrypted(dev,swiotlb_addr);+else+dma_addr=phys_to_dma_encrypted(dev,swiotlb_addr);+if(unlikely(!dma_capable(dev,dma_addr,size,true))){__swiotlb_tbl_unmap_single(dev,swiotlb_addr,size,dir,attrs|DMA_ATTR_SKIP_CPU_SYNC,
@@ -1853,9 +1898,18 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,kfree(mem);return-ENOMEM;}+/*+*ifplatformsupportsmemoryencryption,+*restrictedmempoolisdecryptedbydefault+*/+if(cc_platform_has(CC_ATTR_MEM_ENCRYPT)){+mem->unencrypted=true;+set_memory_decrypted((unsignedlong)phys_to_virt(rmem->base),+rmem->size>>PAGE_SHIFT);+}else{+mem->unencrypted=false;+}
This breaks pKVM as it doesn’t set CC_ATTR_MEM_ENCRYPT, so all virtio
traffic now fails.
Also, by design, some drivers are clueless about bouncing, so
I believe that the pool should have a way to control it’s property
(encrypted or decrypted) and that takes priority over whatever
attributes comes from allocation.
And that brings us to the same point whether it’s better to return
the memory along with it’s state or we pass the requested state.
I think for other cases it’s fine for the device/DMA-API to dictate
the attrs, but not in restricted-dma case, the firmware just knows better.
Is it that the pKVM guest kernel does not have awareness of
encrypted/decrypted DMA allocations? Instead, the firmware attaches
hypervisor-shared pages to the device via restricted-dma-pool? The
kernel then has swiotlb->for_alloc = true, and hence all DMA allocations
go through the restricted-dma-pool?
Yes.
quoted
Given that pKVM supports pkvm_set_memory_encrypted() and
pkvm_set_memory_decrypted(), can we consider adding CC_ATTR_MEM_ENCRYPT
support to pKVM? It would also be good to investigate whether we can set
force_dma_unencrypted(dev) to true where needed.
I was looking in to that, but it didn't work because
force_dma_unencrypted() is broken with restricted-dma due to the
double decryption issue, that's when I sent my first series [1]
May be we should land some basic fixes for that path so we can
convert pKVM, then we do the full rework.
I will revive my old work and see if I can send a RFC.
[1] https://lore.kernel.org/all/20260305170335.963568-1-smostafa@google.com/
With this series, can you check whether the only change needed is
something like the following?
modified kernel/dma/swiotlb.c
@@ -1905,7 +1905,8 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem, * if platform supports memory encryption, * restricted mem pool is decrypted by default */- if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {+ //if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {+ if (true) { mem->unencrypted = true; set_memory_decrypted((unsigned long)phys_to_virt(rmem->base), rmem->size >> PAGE_SHIFT);
Yes, that boots, but I will need to do more tests.
quoted
quoted
quoted
I agree that this patch, as it stands, can break pKVM because we are now
missing the set_memory_decrypted() call required for pKVM to work.
We now mark the swiotlb io_tlb_mem as unencrypted/encrypted in the guest
using struct io_tlb_mem->unencrypted. I am not clear what we can use for
pKVM to conditionalize this so that it works for both protected and
unprotected guests.
There is no problem with non-protected guests as they don't use memory
encryption, my initial thought was that th encrpyted/decrypted is
per-pool property which is decided by FW (device-tree).
What I meant was that we need a generic way to identify a pKVM guest, so
that we can use it in the conditional above.
I have this patch, with that I can boot with your series unmodified,
but I will need to do more testing.
Thanks, I can add this to the series once you complete the required testing.
quoted hunk
From d795b4c4ee2437587616b2b342e9996afe6d6680 Mon Sep 17 00:00:00 2001
From: Mostafa Saleh <smostafa@google.com>
Date: Thu, 14 May 2026 13:46:15 +0000
Subject: [PATCH] arm64/coco: Add pKVM as a CC platform
pKVM does support memory encryption, expose that to the rest of
the kernel through cc_platform_has()
At the moment, all devices inside the guest are emulated which
requires its memory to be shared back to the host (decrypted), so
set force_dma_unencrypted() to always return true.
Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
arch/arm64/include/asm/hypervisor.h | 6 ++++++
arch/arm64/include/asm/mem_encrypt.h | 3 ++-
arch/arm64/kernel/rsi.c | 12 ------------
arch/arm64/mm/init.c | 13 +++++++++++++
drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c | 5 +++++
5 files changed, 26 insertions(+), 13 deletions(-)
On Thu, May 14, 2026 at 09:35:29AM -0300, Jason Gunthorpe wrote:
quoted
quoted
How will pKVM signal what kind of memory the DMA needs then?
Does it use set_memory_decrypted()? How can it use
set_memory_decrypted() without offering CC_ATTR_MEM_ENCRYPT ?
pKVM (hypervisor) doesn’t signal anything.
The VMM when running protected guests will use restricted dma-pools
for emulated vritio devices in the guest, which gets decrypted by
the guest kernel and hence shared with the host kernel, and then
traffic is bounced via the pool.
That really does sound like CC and set_memory_decrypted() to me..
quoted
It’s also worth noting that bouncing here isn't just about visibility.
Because memory sharing operates at page granularity, bouncing sub-page
allocations through the restricted pool prevents adjacent, sensitive
guest data from being exposed to the untrusted host.
That's a somewhat different problem, we have the dev->trusted stuff
that is supposed to deal with this kind of security. We need it for
IOMMU based systems too, eg hot plug thunderbolt should have it.
I see that it is used only for dma-iommu and for PCI devices.
However, I think that should be a problem with other CCA solutions
with emulated devices as they are untrusted. As I'd expect they
would have virtio devices.
Then CC issue is more that the DMA API can't decrypt random passed in
memory because doing so often requires changing the PTEs pointing at
the page so it would break everything if done transparently.
quoted
quoted
quoted
I believe that the pool should have a way to control it’s property
(encrypted or decrypted) and that takes priority over whatever
attributes comes from allocation.
We should get here because dma_capable() fails, and then swiotlb needs
to return something that makes dma_capable() succeed. Yes, it should
return details about the thing it decided, but it shouldn't have been
pre-created with some idea how to make dma_capable() work.
That sounds neat, but at the end we have force_dma_unencrypted() in
dma_capable() which is just hardcoded to true/false by the platform.
For now, the next step is it becomes per-device and dynamic during the
device lifecycle.
quoted
How is that different from having the state static by the pool?
statically attached pools to the device are not so flexible when
devices have dynamically changing capabilities..
Pools can be per-device also. A device can have mutiple pools with
different memory attrs, which then can be matched by the DMA code
at runtime, it's not as flexible, but removes some complexity from
the guest code.
quoted
quoted
If dma_capable() can fail, then swiotlb should know exactly what to do
to fix it.
dma_capable() returns a bool, I don’t think it can know what exactly
went wrong (based on address, size, attrs, dev...)
Yes, but I think the design is swiotlb is supposed to re-inspect what
is going on against the limits dma_capable checks and then select the
correct remedy..
I see, but that’s not part of this series, and probably would require
some rework so dma_capable() can return an error code (ERANGE, EPERM...)
so that caller can deal with that.
quoted
While we can debate the aesthetics of the setup , this is
the exisitng behaviour for Linux, which existed for years
and pKVM relies on and is used extensively.
And, this patch alters that long-standing logic and introduces
a functional regression.
Yeah, Aneesh needs to do something here, I'm pointing out it is
entirely seperate thing from the CC path we are working on which is
decoupling CC from reylying on force swiotlb.
I am looking into converting pKVM to use the CC stuff, I replied with
a patch to Aneesh in this thread. However, I need to do more testing
and make sure there are not any unwanted consequences.
quoted
We can address this by either adjusting this patch or by changing
pKVM guests to be more aligned with other CCA guests which is
something I have been wondering about if it would help reduce
bouncing.
Every time I look at pkvm I think it is just ARM CCA with a different
design and no access to the unique HW features..
quoted
quoted
If we can make that work then maybe the flows are designed correctly.
Mmm, I am not sure I understand this one, shouldn’t the device also be
notified about the switch in memory state, if it expects to read/write
decrypted memory, how would that work if the kernel changes it to an
encrypted one?
Nothing on the device changes. In a CC world we put the device in a
T=0 or T=1 state before the driver loads and the expectation from the
DMA API is that the device will only use that T=x DMA type during
operation.
A T=1 state device can access all of memory, private or shared. Any
information the platform may need is encoded in the dma_addr_t or in
the S1 IOPTEs.
So we never need to tell the device driver what kind of memory the DMA
is targetting, and we NEVER expect a device in T=1 mode to have to
issue a T=0 DMA to use the DMA API.
In a pkvm world it should be the same, the S2 table for the SMMU will
control what the device can access, and if the SMMU points to a
"private" or "shared" page is not something the device needs to know
or care about.
I see that's because dma-iommu chooses the attrs for iommu_map().
In pKVM, dma_addr_t and IOPTE are the same for private and shared,
so nothing differs in that case.
We don’t expect pass-through devices to interact with shared
memory (T=0) at the moment.
However, I can see use cases for that, where the host and the guest
collaborate with device passthrough and require zero copy.
One other interesting case for device-passthrough is non-coherent
devices which then require private pools for bouncing.
Thanks,
Mostafa
On Thu, May 14, 2026 at 11:37:33AM -0300, Jason Gunthorpe wrote:
On Thu, May 14, 2026 at 06:18:05PM +0530, Aneesh Kumar K.V wrote:
quoted
quoted
There is no problem with non-protected guests as they don't use memory
encryption, my initial thought was that th encrpyted/decrypted is
per-pool property which is decided by FW (device-tree).
What I meant was that we need a generic way to identify a pKVM guest, so
that we can use it in the conditional above.
If I understood Mostafa's remarks I think different devices in the
guest need shared/decrypted and some don't? Ie a virtio hypervisor
device needs shared while a real PCI device doesn't? Is that right?
In upstream, device passthrough is not supported, but that case is
supported in Android and we plan to upstream it (it currently
depends on the SMMUv3 series first)
In CC terms that would be a mixture of T=0 and T=1 devices hardwired
and signaled by firwmare..
Ideally we'd have a flow where if the arch precreates a swiotlb pool
with special parameters this overrides all other decision making. Then
this series is about making CC NOT use that flow... ??
Yes, I believe that will be needed, we do this at android by a per-pool
property added in the device tree.
Thanks,
Mostafa
From: Jason Gunthorpe <jgg@ziepe.ca> Date: 2026-05-15 22:51:15
On Thu, May 14, 2026 at 02:43:39PM +0000, Mostafa Saleh wrote:
quoted
That's a somewhat different problem, we have the dev->trusted stuff
that is supposed to deal with this kind of security. We need it for
IOMMU based systems too, eg hot plug thunderbolt should have it.
I see that it is used only for dma-iommu and for PCI devices.
However, I think that should be a problem with other CCA solutions
with emulated devices as they are untrusted. As I'd expect they
would have virtio devices.
Yes, any security solution with an out of TCB device should be using either
memory encryption so the kernel already bounces or this trusted stuff
and a force strict dma-iommu so the dma layer is careful.
This is more policy from userspace what devices they want in or out of
their TCB. Like you make accept the device into T=1 but then still
want to keep it out of your TCB with the vIOMMU, I can see good
arguments for something like that.
quoted
quoted
While we can debate the aesthetics of the setup , this is
the exisitng behaviour for Linux, which existed for years
and pKVM relies on and is used extensively.
And, this patch alters that long-standing logic and introduces
a functional regression.
Yeah, Aneesh needs to do something here, I'm pointing out it is
entirely seperate thing from the CC path we are working on which is
decoupling CC from reylying on force swiotlb.
I am looking into converting pKVM to use the CC stuff, I replied with
a patch to Aneesh in this thread. However, I need to do more testing
and make sure there are not any unwanted consequences.
Yeah, it is a nice patch and I think it will help reduce the
complexity if it aligns to CCA type stuff.
quoted
In a pkvm world it should be the same, the S2 table for the SMMU will
control what the device can access, and if the SMMU points to a
"private" or "shared" page is not something the device needs to know
or care about.
I see that's because dma-iommu chooses the attrs for iommu_map().
Long term the DMA API path through the dma-iommu will pass the
ATTR_CC_SHARED through to iommu_map so when the arch requires a
different IOPTE it can construct it.
In pKVM, dma_addr_t and IOPTE are the same for private and shared,
so nothing differs in that case.
Yes, so you don't have to worry.
We don’t expect pass-through devices to interact with shared
memory (T=0) at the moment.
However, I can see use cases for that, where the host and the guest
collaborate with device passthrough and require zero copy.
Once you add the CC patch it becomes immediately possible though
because the user can allocate a CC shared DMA HEAP and feed that all
over the place.
One other interesting case for device-passthrough is non-coherent
devices which then require private pools for bouncing.
Why does shared/private matter for bouncing? Why do you need to bounce
at all? Do cmo's not work in pkvm guests?
Jason
Teach the atomic DMA pool code to distinguish between encrypted and
decrypted pools, and make pool allocation select the matching pool based
on DMA attributes.
Introduce a dma_gen_pool wrapper that records whether a pool is
decrypted, initialize that state when the atomic pools are created, and
use it when expanding and resizing the pools. Update dma_alloc_from_pool()
to take attrs and skip pools whose encrypted/decrypted state does not
match DMA_ATTR_CC_SHARED. Update dma_free_from_pool() accordingly.
Also pass DMA_ATTR_CC_SHARED from the swiotlb atomic allocation path
so decrypted swiotlb allocations are taken from the correct atomic pool.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
drivers/iommu/dma-iommu.c | 2 +-
include/linux/dma-map-ops.h | 2 +-
kernel/dma/direct.c | 11 ++-
kernel/dma/pool.c | 163 +++++++++++++++++++++++-------------
kernel/dma/swiotlb.c | 7 +-
5 files changed, 122 insertions(+), 63 deletions(-)
@@ -12,12 +12,18 @@#include<linux/set_memory.h>#include<linux/slab.h>#include<linux/workqueue.h>+#include<linux/cc_platform.h>-staticstructgen_pool*atomic_pool_dma__ro_after_init;+structdma_gen_pool{+boolunencrypted;+structgen_pool*pool;+};++staticstructdma_gen_poolatomic_pool_dma__ro_after_init;staticunsignedlongpool_size_dma;-staticstructgen_pool*atomic_pool_dma32__ro_after_init;+staticstructdma_gen_poolatomic_pool_dma32__ro_after_init;staticunsignedlongpool_size_dma32;-staticstructgen_pool*atomic_pool_kernel__ro_after_init;+staticstructdma_gen_poolatomic_pool_kernel__ro_after_init;staticunsignedlongpool_size_kernel;/* Size can be defined by the coherent_pool command line */
@@ -126,11 +135,15 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,return0;encrypt_mapping:-ret=set_memory_encrypted((unsignedlong)page_to_virt(page),-1<<order);-if(WARN_ON_ONCE(ret)){-/* Decrypt succeeded but encrypt failed, purposely leak */-gotoout;+if(dma_pool->unencrypted){+intrc;++rc=set_memory_encrypted((unsignedlong)page_to_virt(page),+1<<order);+if(WARN_ON_ONCE(rc)){+/* Decrypt succeeded but encrypt failed, purposely leak */+gotoout;+}}remove_mapping:#ifdef CONFIG_DMA_DIRECT_REMAP
@@ -142,46 +155,52 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,returnret;}-staticvoidatomic_pool_resize(structgen_pool*pool,gfp_tgfp)+staticvoidatomic_pool_resize(structdma_gen_pool*dma_pool,gfp_tgfp){-if(pool&&gen_pool_avail(pool)<atomic_pool_size)-atomic_pool_expand(pool,gen_pool_size(pool),gfp);+if(dma_pool->pool&&gen_pool_avail(dma_pool->pool)<atomic_pool_size)+atomic_pool_expand(dma_pool,gen_pool_size(dma_pool->pool),gfp);}staticvoidatomic_pool_work_fn(structwork_struct*work){if(IS_ENABLED(CONFIG_ZONE_DMA))-atomic_pool_resize(atomic_pool_dma,+atomic_pool_resize(&atomic_pool_dma,GFP_KERNEL|GFP_DMA);if(IS_ENABLED(CONFIG_ZONE_DMA32))-atomic_pool_resize(atomic_pool_dma32,+atomic_pool_resize(&atomic_pool_dma32,GFP_KERNEL|GFP_DMA32);-atomic_pool_resize(atomic_pool_kernel,GFP_KERNEL);+atomic_pool_resize(&atomic_pool_kernel,GFP_KERNEL);}-static__initstructgen_pool*__dma_atomic_pool_init(size_tpool_size,-gfp_tgfp)+static__initstructdma_gen_pool*__dma_atomic_pool_init(structdma_gen_pool*dma_pool,+size_tpool_size,gfp_tgfp){-structgen_pool*pool;intret;-pool=gen_pool_create(PAGE_SHIFT,NUMA_NO_NODE);-if(!pool)+dma_pool->pool=gen_pool_create(PAGE_SHIFT,NUMA_NO_NODE);+if(!dma_pool->pool)returnNULL;-gen_pool_set_algo(pool,gen_pool_first_fit_order_align,NULL);+gen_pool_set_algo(dma_pool->pool,gen_pool_first_fit_order_align,NULL);++/* if platform is using memory encryption atomic pools are by default decrypted. */+if(cc_platform_has(CC_ATTR_MEM_ENCRYPT))+dma_pool->unencrypted=true;+else+dma_pool->unencrypted=false;-ret=atomic_pool_expand(pool,pool_size,gfp);+ret=atomic_pool_expand(dma_pool,pool_size,gfp);if(ret){-gen_pool_destroy(pool);+gen_pool_destroy(dma_pool->pool);+dma_pool->pool=NULL;pr_err("DMA: failed to allocate %zu KiB %pGg pool for atomic allocation\n",pool_size>>10,&gfp);returnNULL;}pr_info("DMA: preallocated %zu KiB %pGg pool for atomic allocations\n",-gen_pool_size(pool)>>10,&gfp);-returnpool;+gen_pool_size(dma_pool->pool)>>10,&gfp);+returndma_pool;}#ifdef CONFIG_ZONE_DMA32
@@ -207,21 +226,22 @@ static int __init dma_atomic_pool_init(void)/* All memory might be in the DMA zone(s) to begin with */if(has_managed_zone(ZONE_NORMAL)){-atomic_pool_kernel=__dma_atomic_pool_init(atomic_pool_size,-GFP_KERNEL);-if(!atomic_pool_kernel)+__dma_atomic_pool_init(&atomic_pool_kernel,atomic_pool_size,GFP_KERNEL);+if(!atomic_pool_kernel.pool)ret=-ENOMEM;}+if(has_managed_dma()){-atomic_pool_dma=__dma_atomic_pool_init(atomic_pool_size,-GFP_KERNEL|GFP_DMA);-if(!atomic_pool_dma)+__dma_atomic_pool_init(&atomic_pool_dma,atomic_pool_size,+GFP_KERNEL|GFP_DMA);+if(!atomic_pool_dma.pool)ret=-ENOMEM;}+if(has_managed_dma32){-atomic_pool_dma32=__dma_atomic_pool_init(atomic_pool_size,-GFP_KERNEL|GFP_DMA32);-if(!atomic_pool_dma32)+__dma_atomic_pool_init(&atomic_pool_dma32,atomic_pool_size,+GFP_KERNEL|GFP_DMA32);+if(!atomic_pool_dma32.pool)ret=-ENOMEM;}
@@ -230,19 +250,44 @@ static int __init dma_atomic_pool_init(void)}postcore_initcall(dma_atomic_pool_init);-staticinlinestructgen_pool*dma_guess_pool(structgen_pool*prev,gfp_tgfp)+staticinlinestructdma_gen_pool*__dma_guess_pool(structdma_gen_pool*first,+structdma_gen_pool*second,structdma_gen_pool*third)+{+if(first->pool)+returnfirst;+if(second&&second->pool)+returnsecond;+if(third&&third->pool)+returnthird;+returnNULL;+}++staticinlinestructdma_gen_pool*dma_guess_pool(structdma_gen_pool*prev,+gfp_tgfp){-if(prev==NULL){+if(!prev){if(gfp&GFP_DMA)-returnatomic_pool_dma?:atomic_pool_dma32?:atomic_pool_kernel;+return__dma_guess_pool(&atomic_pool_dma,+&atomic_pool_dma32,+&atomic_pool_kernel);+if(gfp&GFP_DMA32)-returnatomic_pool_dma32?:atomic_pool_dma?:atomic_pool_kernel;-returnatomic_pool_kernel?:atomic_pool_dma32?:atomic_pool_dma;+return__dma_guess_pool(&atomic_pool_dma32,+&atomic_pool_dma,+&atomic_pool_kernel);++return__dma_guess_pool(&atomic_pool_kernel,+&atomic_pool_dma32,+&atomic_pool_dma);}-if(prev==atomic_pool_kernel)-returnatomic_pool_dma32?atomic_pool_dma32:atomic_pool_dma;-if(prev==atomic_pool_dma32)-returnatomic_pool_dma;++if(prev==&atomic_pool_kernel)+return__dma_guess_pool(&atomic_pool_dma32,+&atomic_pool_dma,NULL);++if(prev==&atomic_pool_dma32)+return__dma_guess_pool(&atomic_pool_dma,NULL,NULL);+returnNULL;}
Tue, May 12, 2026 at 11:03:55AM +0200, aneesh.kumar@kernel.org wrote:
This series propagates DMA_ATTR_CC_SHARED through the dma-direct,
dma-pool, and swiotlb paths so that encrypted and decrypted DMA buffers
are handled consistently.
Today, the direct DMA path mostly relies on force_dma_unencrypted() for
shared/decrypted buffer handling. This series consolidates the
force_dma_unencrypted() checks in the top-level functions and ensures
that the remaining DMA interfaces use DMA attributes to make the correct
decisions.
FWIW, the patchset in general looks good to me. I tested this with my
system_cc_shared dmabuf flow, works flawlessly.
Thanks!
Teach swiotlb to distinguish between encrypted and decrypted bounce
buffer pools, and make allocation and mapping paths select a pool whose
state matches the requested DMA attributes.
Add a decrypted flag to io_tlb_mem, initialize it for the default and
restricted pools, and propagate DMA_ATTR_CC_SHARED into swiotlb pool
allocation. Reject swiotlb alloc/map requests when the selected pool does
not match the required encrypted/decrypted state.
Also return DMA addresses with the matching phys_to_dma_{encrypted,
unencrypted} helper so the DMA address encoding stays consistent with the
chosen pool.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
include/linux/dma-direct.h | 10 ++++
include/linux/swiotlb.h | 8 ++-
kernel/dma/direct.c | 14 +++--
kernel/dma/swiotlb.c | 108 +++++++++++++++++++++++++++----------
4 files changed, 107 insertions(+), 33 deletions(-)
- page = dma_direct_alloc_swiotlb(dev, size);+ page = dma_direct_alloc_swiotlb(dev, size, attrs); if (page) {+ /*+ * swiotlb allocations comes from pool already marked+ * decrypted+ */
... is not this needed here
attrs |= DMA_ATTR_CC_SHARED;
?
and then the setup_page label below can do the right thing, which I tried, with enforcing io_tlb_default_mem.for_alloc=1, it works - accepted device can still do DMA to shared memory. Thanks,
quoted hunk
mark_mem_decrypt = false;
goto setup_page;
}
@@ -374,6 +379,7 @@ void dma_direct_free(struct device *dev, size_t size, return; if (swiotlb_find_pool(dev, dma_to_phys(dev, dma_addr)))+ /* Swiotlb doesn't need a page attribute update on free */ mark_mem_encrypted = false; if (is_vmalloc_addr(cpu_addr)) {
@@ -1411,6 +1436,16 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,if(cc_platform_has(CC_ATTR_MEM_ENCRYPT))pr_warn_once("Memory encryption is active and system is using DMA bounce buffers\n");+/*+*ifwearetryingtoswiotlbmapadecryptedpaddrorthepaddrisencrypted+*butthedeviceisforcingdecryption,usedecryptedio_tlb_mem+*/+if((attrs&DMA_ATTR_CC_SHARED)||force_dma_unencrypted(dev))+require_decrypted=true;++if(require_decrypted!=mem->unencrypted)+return(phys_addr_t)DMA_MAPPING_ERROR;+/**ThedefaultswiotlbmemorypoolisallocatedwithPAGE_SIZE*alignment.Ifamappingisrequestedwithlargeralignment,
@@ -1608,8 +1643,14 @@ dma_addr_t swiotlb_map(struct device *dev, phys_addr_t paddr, size_t size,if(swiotlb_addr==(phys_addr_t)DMA_MAPPING_ERROR)returnDMA_MAPPING_ERROR;-/* Ensure that the address returned is DMA'ble */-dma_addr=phys_to_dma_unencrypted(dev,swiotlb_addr);+/*+*Usetheallocatedio_tlb_memencryptiontypetodeterminedmaaddr.+*/+if(dev->dma_io_tlb_mem->unencrypted)+dma_addr=phys_to_dma_unencrypted(dev,swiotlb_addr);+else+dma_addr=phys_to_dma_encrypted(dev,swiotlb_addr);+if(unlikely(!dma_capable(dev,dma_addr,size,true))){__swiotlb_tbl_unmap_single(dev,swiotlb_addr,size,dir,attrs|DMA_ATTR_SKIP_CPU_SYNC,
Teach the atomic DMA pool code to distinguish between encrypted and
decrypted pools, and make pool allocation select the matching pool based
on DMA attributes.
Introduce a dma_gen_pool wrapper that records whether a pool is
decrypted, initialize that state when the atomic pools are created, and
use it when expanding and resizing the pools. Update dma_alloc_from_pool()
to take attrs and skip pools whose encrypted/decrypted state does not
match DMA_ATTR_CC_SHARED. Update dma_free_from_pool() accordingly.
Also pass DMA_ATTR_CC_SHARED from the swiotlb atomic allocation path
so decrypted swiotlb allocations are taken from the correct atomic pool.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
drivers/iommu/dma-iommu.c | 2 +-
include/linux/dma-map-ops.h | 2 +-
kernel/dma/direct.c | 11 ++-
kernel/dma/pool.c | 163 +++++++++++++++++++++++-------------
kernel/dma/swiotlb.c | 7 +-
5 files changed, 122 insertions(+), 63 deletions(-)
* Memory in the atomic DMA pools must be unencrypted, the pools do not
* shrink so no re-encryption occurs in dma_direct_free().
*/
- ret = set_memory_decrypted((unsigned long)page_to_virt(page),
+ if (dma_pool->unencrypted) {
+ ret = set_memory_decrypted((unsigned long)page_to_virt(page),
1 << order);
- if (ret)
- goto remove_mapping;
- ret = gen_pool_add_virt(pool, (unsigned long)addr, page_to_phys(page),
- pool_size, NUMA_NO_NODE);
+ if (ret)
+ goto remove_mapping;
+ }
+
+ ret = gen_pool_add_virt(dma_pool->pool, (unsigned long)addr,
+ page_to_phys(page), pool_size, NUMA_NO_NODE);
@@ -207,21 +226,22 @@ static int __init dma_atomic_pool_init(void)
/* All memory might be in the DMA zone(s) to begin with */
if (has_managed_zone(ZONE_NORMAL)) {
- atomic_pool_kernel = __dma_atomic_pool_init(atomic_pool_size,
- GFP_KERNEL);
- if (!atomic_pool_kernel)
+ __dma_atomic_pool_init(&atomic_pool_kernel, atomic_pool_size, GFP_KERNEL);
+ if (!atomic_pool_kernel.pool)
ret = -ENOMEM;
}
+
if (has_managed_dma()) {
- atomic_pool_dma = __dma_atomic_pool_init(atomic_pool_size,
- GFP_KERNEL | GFP_DMA);
- if (!atomic_pool_dma)
+ __dma_atomic_pool_init(&atomic_pool_dma, atomic_pool_size,
+ GFP_KERNEL | GFP_DMA);
+ if (!atomic_pool_dma.pool)
ret = -ENOMEM;
}
+
if (has_managed_dma32) {
- atomic_pool_dma32 = __dma_atomic_pool_init(atomic_pool_size,
- GFP_KERNEL | GFP_DMA32);
- if (!atomic_pool_dma32)
+ __dma_atomic_pool_init(&atomic_pool_dma32, atomic_pool_size,
+ GFP_KERNEL | GFP_DMA32);
+ if (!atomic_pool_dma32.pool)
ret = -ENOMEM;
}
@@ -230,19 +250,44 @@ static int __init dma_atomic_pool_init(void)
v3 of this just crashed here with dma_pool!=NULL but dma_pool->pool==NULL. continuing debugging... Thanks,
dma_direct_free:
dma_free_from_pool (loop over pools) -> false
[here was a crash which I fixed by "if (!dma_pool->pool) continue"]
swiotlb_find_pool (loop again) -> false
__dma_direct_free_pages
swiotlb_free
swiotlb_find_pool (loop again)
dma_free_contiguous => done.
so that works but kinda hard to follow and there is some room for optimization. I do not normally have swiottlb when I test this and there is too many of this swiotlb stuff on the real direct dma mapping path imho. Thanks,
Tue, May 12, 2026 at 11:03:55AM +0200, aneesh.kumar@kernel.org wrote:
quoted
This series propagates DMA_ATTR_CC_SHARED through the dma-direct,
dma-pool, and swiotlb paths so that encrypted and decrypted DMA buffers
are handled consistently.
Today, the direct DMA path mostly relies on force_dma_unencrypted() for
shared/decrypted buffer handling. This series consolidates the
force_dma_unencrypted() checks in the top-level functions and ensures
that the remaining DMA interfaces use DMA attributes to make the correct
decisions.
FWIW, the patchset in general looks good to me. I tested this with my
system_cc_shared dmabuf flow, works flawlessly.
Thanks!
Thanks, Can I add
Tested-by: Jiri Pirko <jiri@resnulli.us>
-aneesh
* Memory in the atomic DMA pools must be unencrypted, the pools do not
* shrink so no re-encryption occurs in dma_direct_free().
*/
- ret = set_memory_decrypted((unsigned long)page_to_virt(page),
+ if (dma_pool->unencrypted) {
+ ret = set_memory_decrypted((unsigned long)page_to_virt(page),
1 << order);
- if (ret)
- goto remove_mapping;
- ret = gen_pool_add_virt(pool, (unsigned long)addr, page_to_phys(page),
- pool_size, NUMA_NO_NODE);
+ if (ret)
+ goto remove_mapping;
+ }
+
+ ret = gen_pool_add_virt(dma_pool->pool, (unsigned long)addr,
+ page_to_phys(page), pool_size, NUMA_NO_NODE);
v3 of this just crashed here with dma_pool!=NULL but dma_pool->pool==NULL. continuing debugging... Thanks,
dma_direct_free:
dma_free_from_pool (loop over pools) -> false
[here was a crash which I fixed by "if (!dma_pool->pool) continue"]
swiotlb_find_pool (loop again) -> false
__dma_direct_free_pages
swiotlb_free
swiotlb_find_pool (loop again)
dma_free_contiguous => done.
so that works but kinda hard to follow and there is some room for
optimization. I do not normally have swiottlb when I test this and
there is too many of this swiotlb stuff on the real direct dma mapping
path imho. Thanks,
I will work on this in the next update. I can possibly drop the
swiotlb_find_pool from the swiotlb_free() path.
Mon, May 18, 2026 at 10:23:58AM +0200, aneesh.kumar@kernel.org wrote:
Jiri Pirko [off-list ref] writes:
quoted
Tue, May 12, 2026 at 11:03:55AM +0200, aneesh.kumar@kernel.org wrote:
quoted
This series propagates DMA_ATTR_CC_SHARED through the dma-direct,
dma-pool, and swiotlb paths so that encrypted and decrypted DMA buffers
are handled consistently.
Today, the direct DMA path mostly relies on force_dma_unencrypted() for
shared/decrypted buffer handling. This series consolidates the
force_dma_unencrypted() checks in the top-level functions and ensures
that the remaining DMA interfaces use DMA attributes to make the correct
decisions.
FWIW, the patchset in general looks good to me. I tested this with my
system_cc_shared dmabuf flow, works flawlessly.
Thanks!
Thanks, Can I add
Tested-by: Jiri Pirko <jiri@resnulli.us>
From: Christian Borntraeger <borntraeger@linux.ibm.com> Date: 2026-05-18 10:04:49
cc Halil, Matt, Jaehoon.
Can you have a look what this means for virtio on secure execution?
Am 12.05.26 um 11:04 schrieb Aneesh Kumar K.V (Arm):
quoted hunk
Teach dma_direct_map_phys() to select the DMA address encoding based on
DMA_ATTR_CC_SHARED.
Use phys_to_dma_unencrypted() for decrypted mappings and
phys_to_dma_encrypted() otherwise. If a device requires unencrypted DMA
but the source physical address is still encrypted, force the mapping
through swiotlb so the DMA address and backing memory attributes remain
consistent.
Update the arm64, x86, s390 and powerpc secure-guest setup to not use
swiotlb force option
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
Changes from v3:
* Handle DMA_ATTR_MMIO
---
arch/arm64/mm/init.c | 4 +--
arch/powerpc/platforms/pseries/svm.c | 2 +-
arch/s390/mm/init.c | 2 +-
arch/x86/kernel/pci-dma.c | 4 +--
kernel/dma/direct.c | 4 ++-
kernel/dma/direct.h | 38 +++++++++++++---------------
6 files changed, 24 insertions(+), 30 deletions(-)
@@ -29,7 +29,7 @@ static int __init init_svm(void)*needtousetheSWIOTLBbufferforDMAevenifdma_capable()says*otherwise.*/-ppc_swiotlb_flags|=SWIOTLB_ANY|SWIOTLB_FORCE;+ppc_swiotlb_flags|=SWIOTLB_ANY;/* Share the SWIOTLB buffer with the host. */swiotlb_update_mem_attributes();
@@ -691,8 +691,10 @@ size_t dma_direct_max_mapping_size(struct device *dev){/* If SWIOTLB is active, use its maximum mapping size */if(is_swiotlb_active(dev)&&-(dma_addressing_limited(dev)||is_swiotlb_force_bounce(dev)))+(dma_addressing_limited(dev)||is_swiotlb_force_bounce(dev)||+force_dma_unencrypted(dev)))returnswiotlb_max_mapping_size(dev);+returnSIZE_MAX;}
On Thu, May 14, 2026 at 08:13:25PM +0530, Aneesh Kumar K.V wrote:
quoted
quoted
What I meant was that we need a generic way to identify a pKVM guest, so
that we can use it in the conditional above.
I have this patch, with that I can boot with your series unmodified,
but I will need to do more testing.
Thanks, I can add this to the series once you complete the required testing.
I am still running more tests, but looking more into it. Setting
force_dma_unencrypted() to true for pKVM guests is wrong, as the
guest shouldn’t try to decrypt arbitrary memory as it can include
sensitive information (for example in case of virtio sub-page
allocation) and should strictly rely on the restricted-dma-pool
for that.
However, with my patch and setting force_dma_unencrypted() to false
on top of this series, it fails on pKVM due to a missing shared
attribute as Alexey mentioned, as now SWIOTLB rejects non shared
attrs, so, the DMA-API has to pass it. With that, I can boot again:
--
I will keep testing and let you know how it goes. If there is nothing
else required to convert pKVM guests to CC, I can just post the patch
separately as it has no dependency on this series.
Re force_dma_unencrypted(), I am looking into a safe way to use it
for pKVM as I beleive it will be useful to eliminate some bouncing.
However, that’s not critical for this series and can be added later
as I am still investigating it, if I reach something I can post it
along the pKVM patch above.
Thanks,
Mostafa
>
>
> -aneesh
On Fri, May 15, 2026 at 07:51:13PM -0300, Jason Gunthorpe wrote:
On Thu, May 14, 2026 at 02:43:39PM +0000, Mostafa Saleh wrote:
quoted
quoted
That's a somewhat different problem, we have the dev->trusted stuff
that is supposed to deal with this kind of security. We need it for
IOMMU based systems too, eg hot plug thunderbolt should have it.
I see that it is used only for dma-iommu and for PCI devices.
However, I think that should be a problem with other CCA solutions
with emulated devices as they are untrusted. As I'd expect they
would have virtio devices.
Yes, any security solution with an out of TCB device should be using either
memory encryption so the kernel already bounces or this trusted stuff
and a force strict dma-iommu so the dma layer is careful.
This is more policy from userspace what devices they want in or out of
their TCB. Like you make accept the device into T=1 but then still
want to keep it out of your TCB with the vIOMMU, I can see good
arguments for something like that.
quoted
quoted
quoted
While we can debate the aesthetics of the setup , this is
the exisitng behaviour for Linux, which existed for years
and pKVM relies on and is used extensively.
And, this patch alters that long-standing logic and introduces
a functional regression.
Yeah, Aneesh needs to do something here, I'm pointing out it is
entirely seperate thing from the CC path we are working on which is
decoupling CC from reylying on force swiotlb.
I am looking into converting pKVM to use the CC stuff, I replied with
a patch to Aneesh in this thread. However, I need to do more testing
and make sure there are not any unwanted consequences.
Yeah, it is a nice patch and I think it will help reduce the
complexity if it aligns to CCA type stuff.
quoted
quoted
In a pkvm world it should be the same, the S2 table for the SMMU will
control what the device can access, and if the SMMU points to a
"private" or "shared" page is not something the device needs to know
or care about.
I see that's because dma-iommu chooses the attrs for iommu_map().
Long term the DMA API path through the dma-iommu will pass the
ATTR_CC_SHARED through to iommu_map so when the arch requires a
different IOPTE it can construct it.
quoted
In pKVM, dma_addr_t and IOPTE are the same for private and shared,
so nothing differs in that case.
Yes, so you don't have to worry.
quoted
We don’t expect pass-through devices to interact with shared
memory (T=0) at the moment.
However, I can see use cases for that, where the host and the guest
collaborate with device passthrough and require zero copy.
Once you add the CC patch it becomes immediately possible though
because the user can allocate a CC shared DMA HEAP and feed that all
over the place.
quoted
One other interesting case for device-passthrough is non-coherent
devices which then require private pools for bouncing.
Why does shared/private matter for bouncing? Why do you need to bounce
at all? Do cmo's not work in pkvm guests?
At the moment, in iommu_dma_map_phys(), if a non coherent device
tries to map an unaligned address or size it will be bounced.
In pKVM, dma-iommu is used for assigned devices which operate on
private memory, so bouncing that through the SWIOTLB would leak
information from the guest as the SWIOTLB is decrypted.
In that case, the device needs a pool which remains private.
Thanks,
Mostafa
On Thu, May 14, 2026 at 08:13:25PM +0530, Aneesh Kumar K.V wrote:
quoted
quoted
quoted
What I meant was that we need a generic way to identify a pKVM guest, so
that we can use it in the conditional above.
I have this patch, with that I can boot with your series unmodified,
but I will need to do more testing.
Thanks, I can add this to the series once you complete the required testing.
I am still running more tests, but looking more into it. Setting
force_dma_unencrypted() to true for pKVM guests is wrong, as the
guest shouldn’t try to decrypt arbitrary memory as it can include
sensitive information (for example in case of virtio sub-page
allocation) and should strictly rely on the restricted-dma-pool
for that.
However, with my patch and setting force_dma_unencrypted() to false
on top of this series, it fails on pKVM due to a missing shared
attribute as Alexey mentioned, as now SWIOTLB rejects non shared
attrs, so, the DMA-API has to pass it. With that, I can boot again:
I will keep testing and let you know how it goes. If there is nothing
else required to convert pKVM guests to CC, I can just post the patch
separately as it has no dependency on this series.
That would be useful. I can then carry the patch as a dependent change,
which can also be merged separately
Re force_dma_unencrypted(), I am looking into a safe way to use it
for pKVM as I beleive it will be useful to eliminate some bouncing.
However, that’s not critical for this series and can be added later
as I am still investigating it, if I reach something I can post it
along the pKVM patch above.
Thanks,
Mostafa
From: Jason Gunthorpe <jgg@ziepe.ca> Date: 2026-05-19 13:29:13
On Tue, May 19, 2026 at 11:04:37AM +0000, Mostafa Saleh wrote:
On Thu, May 14, 2026 at 08:13:25PM +0530, Aneesh Kumar K.V wrote:
quoted
quoted
quoted
What I meant was that we need a generic way to identify a pKVM guest, so
that we can use it in the conditional above.
I have this patch, with that I can boot with your series unmodified,
but I will need to do more testing.
Thanks, I can add this to the series once you complete the required testing.
I am still running more tests, but looking more into it. Setting
force_dma_unencrypted() to true for pKVM guests is wrong, as the
guest shouldn’t try to decrypt arbitrary memory as it can include
sensitive information (for example in case of virtio sub-page
allocation) and should strictly rely on the restricted-dma-pool
for that.
??
Where does force_dma_unencrypted() cause arbitary memory passed into
the DMA API to be decrypted? That should never happen???
Jason
From: Jason Gunthorpe <jgg@ziepe.ca> Date: 2026-05-19 13:39:15
On Tue, May 19, 2026 at 11:06:52AM +0000, Mostafa Saleh wrote:
quoted
quoted
One other interesting case for device-passthrough is non-coherent
devices which then require private pools for bouncing.
Why does shared/private matter for bouncing? Why do you need to bounce
at all? Do cmo's not work in pkvm guests?
At the moment, in iommu_dma_map_phys(), if a non coherent device
tries to map an unaligned address or size it will be bounced.
Sure, that's fine.
In pKVM, dma-iommu is used for assigned devices which operate on
private memory, so bouncing that through the SWIOTLB would leak
information from the guest as the SWIOTLB is decrypted.
Yes, a device that can do private access should not be using a shared
SWIOTLB, that should be part of the selection logic inside the SWIOTLB
stuff..
Jason
On Tue, May 19, 2026 at 10:29:11AM -0300, Jason Gunthorpe wrote:
On Tue, May 19, 2026 at 11:04:37AM +0000, Mostafa Saleh wrote:
quoted
On Thu, May 14, 2026 at 08:13:25PM +0530, Aneesh Kumar K.V wrote:
quoted
quoted
quoted
What I meant was that we need a generic way to identify a pKVM guest, so
that we can use it in the conditional above.
I have this patch, with that I can boot with your series unmodified,
but I will need to do more testing.
Thanks, I can add this to the series once you complete the required testing.
I am still running more tests, but looking more into it. Setting
force_dma_unencrypted() to true for pKVM guests is wrong, as the
guest shouldn’t try to decrypt arbitrary memory as it can include
sensitive information (for example in case of virtio sub-page
allocation) and should strictly rely on the restricted-dma-pool
for that.
??
Where does force_dma_unencrypted() cause arbitary memory passed into
the DMA API to be decrypted? That should never happen???
Sorry, maybe arbitrary is not the right expression again :)
I mean that, with emulated devices that use the DMA-API under pKVM,
they will map memory coming from other layers (VFS, net) through
vitrio-block, virtio-net... These can be smaller than a page, and
using force_dma_unencrypted() will share the whole page.
And as discussed, that leaks sensitive information to the untrusted
host.
I am currently investigating passing iova/phys/size
to force_dma_unencrypted() and then we can share pages inplace only
if possible without leaking extra information.
I am trying to get some performance results first. But the tricky part
is to get the semantics right, I believe in that case those devices
shouldn’t use restricted-dma-pools as those should always force
bouncing. Instead bouncing happens through the default SWIOTLB pool,
if not possible to decrypt in place.
Thanks,
Mostafa
On Tue, May 19, 2026 at 10:29:11AM -0300, Jason Gunthorpe wrote:
quoted
On Tue, May 19, 2026 at 11:04:37AM +0000, Mostafa Saleh wrote:
quoted
On Thu, May 14, 2026 at 08:13:25PM +0530, Aneesh Kumar K.V wrote:
quoted
quoted
quoted
What I meant was that we need a generic way to identify a pKVM guest, so
that we can use it in the conditional above.
I have this patch, with that I can boot with your series unmodified,
but I will need to do more testing.
Thanks, I can add this to the series once you complete the required testing.
I am still running more tests, but looking more into it. Setting
force_dma_unencrypted() to true for pKVM guests is wrong, as the
guest shouldn’t try to decrypt arbitrary memory as it can include
sensitive information (for example in case of virtio sub-page
allocation) and should strictly rely on the restricted-dma-pool
for that.
??
Where does force_dma_unencrypted() cause arbitary memory passed into
the DMA API to be decrypted? That should never happen???
Sorry, maybe arbitrary is not the right expression again :)
I mean that, with emulated devices that use the DMA-API under pKVM,
they will map memory coming from other layers (VFS, net) through
vitrio-block, virtio-net... These can be smaller than a page, and
Don't we PAGE_ALIGN these requests?
dma_direct_alloc
size = PAGE_ALIGN(size);
iommu_dma_alloc_pages
size_t alloc_size = PAGE_ALIGN(size);
using force_dma_unencrypted() will share the whole page.
And as discussed, that leaks sensitive information to the untrusted
host.
I am currently investigating passing iova/phys/size
to force_dma_unencrypted() and then we can share pages inplace only
if possible without leaking extra information.
I am trying to get some performance results first. But the tricky part
is to get the semantics right, I believe in that case those devices
shouldn’t use restricted-dma-pools as those should always force
bouncing. Instead bouncing happens through the default SWIOTLB pool,
if not possible to decrypt in place.
On Tue, May 19, 2026 at 07:30:16PM +0530, Aneesh Kumar K.V wrote:
Mostafa Saleh [off-list ref] writes:
quoted
quoted
quoted
I am still running more tests, but looking more into it. Setting
force_dma_unencrypted() to true for pKVM guests is wrong, as the
guest shouldn’t try to decrypt arbitrary memory as it can include
sensitive information (for example in case of virtio sub-page
allocation) and should strictly rely on the restricted-dma-pool
for that.
??
Where does force_dma_unencrypted() cause arbitary memory passed into
the DMA API to be decrypted? That should never happen???
Sorry, maybe arbitrary is not the right expression again :)
I mean that, with emulated devices that use the DMA-API under pKVM,
they will map memory coming from other layers (VFS, net) through
vitrio-block, virtio-net... These can be smaller than a page, and
Don't we PAGE_ALIGN these requests?
dma_direct_alloc
size = PAGE_ALIGN(size);
iommu_dma_alloc_pages
size_t alloc_size = PAGE_ALIGN(size);
For allocation, yes, and that's fine because we bring memory from
the pool.
But not for mapping, as dma_direct_map_phys(), where the memory is
allocated from the driver or other parts in the kernel and the page
may be shared with other kernel components.
Thanks,
Mostafa
quoted
using force_dma_unencrypted() will share the whole page.
And as discussed, that leaks sensitive information to the untrusted
host.
I am currently investigating passing iova/phys/size
to force_dma_unencrypted() and then we can share pages inplace only
if possible without leaking extra information.
I am trying to get some performance results first. But the tricky part
is to get the semantics right, I believe in that case those devices
shouldn’t use restricted-dma-pools as those should always force
bouncing. Instead bouncing happens through the default SWIOTLB pool,
if not possible to decrypt in place.
On Tue, May 19, 2026 at 07:30:16PM +0530, Aneesh Kumar K.V wrote:
quoted
Mostafa Saleh [off-list ref] writes:
quoted
quoted
quoted
I am still running more tests, but looking more into it. Setting
force_dma_unencrypted() to true for pKVM guests is wrong, as the
guest shouldn’t try to decrypt arbitrary memory as it can include
sensitive information (for example in case of virtio sub-page
allocation) and should strictly rely on the restricted-dma-pool
for that.
??
Where does force_dma_unencrypted() cause arbitary memory passed into
the DMA API to be decrypted? That should never happen???
Sorry, maybe arbitrary is not the right expression again :)
I mean that, with emulated devices that use the DMA-API under pKVM,
they will map memory coming from other layers (VFS, net) through
vitrio-block, virtio-net... These can be smaller than a page, and
Don't we PAGE_ALIGN these requests?
dma_direct_alloc
size = PAGE_ALIGN(size);
iommu_dma_alloc_pages
size_t alloc_size = PAGE_ALIGN(size);
For allocation, yes, and that's fine because we bring memory from
the pool.
But not for mapping, as dma_direct_map_phys(), where the memory is
allocated from the driver or other parts in the kernel and the page
may be shared with other kernel components.
But if we are using restricted-dma-pool, we also have:
mem->force_bounce = true;
mem->for_alloc = true;
So, will we use the swiotlb buffers for mapping and copy only the shared
content into those swiotlb buffers?
-aneesh
On Tue, May 19, 2026 at 07:47:48PM +0530, Aneesh Kumar K.V wrote:
Mostafa Saleh [off-list ref] writes:
quoted
On Tue, May 19, 2026 at 07:30:16PM +0530, Aneesh Kumar K.V wrote:
quoted
Mostafa Saleh [off-list ref] writes:
quoted
quoted
quoted
I am still running more tests, but looking more into it. Setting
force_dma_unencrypted() to true for pKVM guests is wrong, as the
guest shouldn’t try to decrypt arbitrary memory as it can include
sensitive information (for example in case of virtio sub-page
allocation) and should strictly rely on the restricted-dma-pool
for that.
??
Where does force_dma_unencrypted() cause arbitary memory passed into
the DMA API to be decrypted? That should never happen???
Sorry, maybe arbitrary is not the right expression again :)
I mean that, with emulated devices that use the DMA-API under pKVM,
they will map memory coming from other layers (VFS, net) through
vitrio-block, virtio-net... These can be smaller than a page, and
Don't we PAGE_ALIGN these requests?
dma_direct_alloc
size = PAGE_ALIGN(size);
iommu_dma_alloc_pages
size_t alloc_size = PAGE_ALIGN(size);
For allocation, yes, and that's fine because we bring memory from
the pool.
But not for mapping, as dma_direct_map_phys(), where the memory is
allocated from the driver or other parts in the kernel and the page
may be shared with other kernel components.
But if we are using restricted-dma-pool, we also have:
mem->force_bounce = true;
mem->for_alloc = true;
So, will we use the swiotlb buffers for mapping and copy only the shared
content into those swiotlb buffers?
True, that's why under pKVM, force_dma_unencrypted() should never
cause any memory to be decrypted and so we set it to false.
As in case of any bugs, the guest does not leak any information,
similar to what just happened initially here due to missing attrs.
However, as I mentioned to Jason, I think with some tweaks to
force_dma_unencrypted() we can make it work under pKVM for aligned
memory which eliminates some of the bouncing.
I am currently investigating that.
Thanks,
Mostafa
From: Jason Gunthorpe <jgg@ziepe.ca> Date: 2026-05-19 14:35:31
On Tue, May 19, 2026 at 01:41:42PM +0000, Mostafa Saleh wrote:
On Tue, May 19, 2026 at 10:29:11AM -0300, Jason Gunthorpe wrote:
quoted
On Tue, May 19, 2026 at 11:04:37AM +0000, Mostafa Saleh wrote:
quoted
On Thu, May 14, 2026 at 08:13:25PM +0530, Aneesh Kumar K.V wrote:
quoted
quoted
quoted
What I meant was that we need a generic way to identify a pKVM guest, so
that we can use it in the conditional above.
I have this patch, with that I can boot with your series unmodified,
but I will need to do more testing.
Thanks, I can add this to the series once you complete the required testing.
I am still running more tests, but looking more into it. Setting
force_dma_unencrypted() to true for pKVM guests is wrong, as the
guest shouldn’t try to decrypt arbitrary memory as it can include
sensitive information (for example in case of virtio sub-page
allocation) and should strictly rely on the restricted-dma-pool
for that.
??
Where does force_dma_unencrypted() cause arbitary memory passed into
the DMA API to be decrypted? That should never happen???
Sorry, maybe arbitrary is not the right expression again :)
I mean that, with emulated devices that use the DMA-API under pKVM,
they will map memory coming from other layers (VFS, net) through
vitrio-block, virtio-net... These can be smaller than a page, and
using force_dma_unencrypted() will share the whole page.
force_dma_unencrypted() should only trigger swiotlb and that never
memcpy's more than necessary?
Where does it do otherwise? That sounds like a bug?
Jason
From: Jason Gunthorpe <jgg@ziepe.ca> Date: 2026-05-19 14:37:50
On Tue, May 19, 2026 at 02:27:54PM +0000, Mostafa Saleh wrote:
However, as I mentioned to Jason, I think with some tweaks to
force_dma_unencrypted() we can make it work under pKVM for aligned
memory which eliminates some of the bouncing.
I am currently investigating that.
force_dma_unencrypted() literally means that memory passed into the
DMA API *without* DMA_ATTR_CC_SHARED cannot be DMA'd from.
It should not mean anything else. The DMA API should never decrypt
passed in memory. You always have to bounce.
Jason
On Tue, May 19, 2026 at 11:35:29AM -0300, Jason Gunthorpe wrote:
On Tue, May 19, 2026 at 01:41:42PM +0000, Mostafa Saleh wrote:
quoted
On Tue, May 19, 2026 at 10:29:11AM -0300, Jason Gunthorpe wrote:
quoted
On Tue, May 19, 2026 at 11:04:37AM +0000, Mostafa Saleh wrote:
quoted
On Thu, May 14, 2026 at 08:13:25PM +0530, Aneesh Kumar K.V wrote:
quoted
quoted
quoted
What I meant was that we need a generic way to identify a pKVM guest, so
that we can use it in the conditional above.
I have this patch, with that I can boot with your series unmodified,
but I will need to do more testing.
Thanks, I can add this to the series once you complete the required testing.
I am still running more tests, but looking more into it. Setting
force_dma_unencrypted() to true for pKVM guests is wrong, as the
guest shouldn’t try to decrypt arbitrary memory as it can include
sensitive information (for example in case of virtio sub-page
allocation) and should strictly rely on the restricted-dma-pool
for that.
??
Where does force_dma_unencrypted() cause arbitary memory passed into
the DMA API to be decrypted? That should never happen???
Sorry, maybe arbitrary is not the right expression again :)
I mean that, with emulated devices that use the DMA-API under pKVM,
they will map memory coming from other layers (VFS, net) through
vitrio-block, virtio-net... These can be smaller than a page, and
using force_dma_unencrypted() will share the whole page.
force_dma_unencrypted() should only trigger swiotlb and that never
memcpy's more than necessary?
Where does it do otherwise? That sounds like a bug?
Agh, I got confused and thought that it can be triggered from dma_map()
too. I need to figure out why that made pKVM guests boot with broken
restricted-dma-pool then.
However, it should not alway use SWIOTLB? It can trigger decryption for
any memory returned from __dma_direct_alloc_pages() which can come
from alloc_pages_node().
Thanks,
Mostafa
From: Jason Gunthorpe <jgg@ziepe.ca> Date: 2026-05-19 14:49:44
On Tue, May 19, 2026 at 02:45:35PM +0000, Mostafa Saleh wrote:
However, it should not alway use SWIOTLB? It can trigger decryption for
any memory returned from __dma_direct_alloc_pages() which can come
from alloc_pages_node().
The alloc coherent flow is seperate and different, these are not pages
'passed into the DMA API' but pages fully allocated internally and
owned by it.
Yes, it should cause decrypted *allocation*.
Jason
On Thu, May 14, 2026 at 08:13:25PM +0530, Aneesh Kumar K.V wrote:
quoted
quoted
quoted
What I meant was that we need a generic way to identify a pKVM guest, so
that we can use it in the conditional above.
I have this patch, with that I can boot with your series unmodified,
but I will need to do more testing.
Thanks, I can add this to the series once you complete the required testing.
I am still running more tests, but looking more into it. Setting
force_dma_unencrypted() to true for pKVM guests is wrong, as the
guest shouldn’t try to decrypt arbitrary memory as it can include
sensitive information (for example in case of virtio sub-page
allocation) and should strictly rely on the restricted-dma-pool
for that.
However, with my patch and setting force_dma_unencrypted() to false
on top of this series, it fails on pKVM due to a missing shared
attribute as Alexey mentioned, as now SWIOTLB rejects non shared
attrs, so, the DMA-API has to pass it. With that, I can boot again:
if we get force_dma_unencrypted(dev) correct, we won't need the above.
for dma_direct_alloc and dma_direct_alloc_pages() we have
if (force_dma_unencrypted(dev))
attrs |= DMA_ATTR_CC_SHARED;
for dma_direct_map_phys(), if we have swiotlb bouncing forced,
swiotlb_tbl_map_single():
if ((attrs & DMA_ATTR_CC_SHARED) || force_dma_unencrypted(dev))
require_decrypted = true;
-aneesh
From: Jason Gunthorpe <jgg@ziepe.ca> Date: 2026-05-19 15:27:43
On Tue, May 19, 2026 at 08:37:54PM +0530, Aneesh Kumar K.V wrote:
if we get force_dma_unencrypted(dev) correct, we won't need the above.
for dma_direct_alloc and dma_direct_alloc_pages() we have
if (force_dma_unencrypted(dev))
attrs |= DMA_ATTR_CC_SHARED;
for dma_direct_map_phys(), if we have swiotlb bouncing forced,
swiotlb_tbl_map_single():
if ((attrs & DMA_ATTR_CC_SHARED) || force_dma_unencrypted(dev))
require_decrypted = true;
IMHO I really do prefer the DMA_ATTR_CC_SHARED flows closer to the
thing that did the decryption. While the above is possibly sound it is
very obtuse to be guessing what kind of memory swiotlb decided to
return..
Can we pass a pointer to the attrs into the swiotlb stuff and it can
update it based on the kind of memory it has allocated?
Jason
On Tue, May 19, 2026 at 08:37:54PM +0530, Aneesh Kumar K.V wrote:
quoted
if we get force_dma_unencrypted(dev) correct, we won't need the above.
for dma_direct_alloc and dma_direct_alloc_pages() we have
if (force_dma_unencrypted(dev))
attrs |= DMA_ATTR_CC_SHARED;
for dma_direct_map_phys(), if we have swiotlb bouncing forced,
swiotlb_tbl_map_single():
if ((attrs & DMA_ATTR_CC_SHARED) || force_dma_unencrypted(dev))
require_decrypted = true;
IMHO I really do prefer the DMA_ATTR_CC_SHARED flows closer to the
thing that did the decryption. While the above is possibly sound it is
very obtuse to be guessing what kind of memory swiotlb decided to
return..
Can we pass a pointer to the attrs into the swiotlb stuff and it can
update it based on the kind of memory it has allocated?
Yes, that also resulted in simpler and cleaner code.
swiotlb_tbl_map_single
/*
* If the physical address is encrypted but the device requires
* decrypted DMA, use a decrypted io_tlb_mem and update the
* attributes so the caller knows that a decrypted io_tlb_mem
* was used.
*/
if (!(*attrs & DMA_ATTR_CC_SHARED) && force_dma_unencrypted(dev))
*attrs |= DMA_ATTR_CC_SHARED;
if (mem->unencrypted != !!(*attrs & DMA_ATTR_CC_SHARED))
return (phys_addr_t)DMA_MAPPING_ERROR;
and
From: Jason Gunthorpe <jgg@ziepe.ca> Date: 2026-05-19 16:11:22
On Tue, May 19, 2026 at 09:35:30PM +0530, Aneesh Kumar K.V wrote:
Yes, that also resulted in simpler and cleaner code.
swiotlb_tbl_map_single
/*
* If the physical address is encrypted but the device requires
* decrypted DMA, use a decrypted io_tlb_mem and update the
* attributes so the caller knows that a decrypted io_tlb_mem
* was used.
*/
if (!(*attrs & DMA_ATTR_CC_SHARED) && force_dma_unencrypted(dev))
*attrs |= DMA_ATTR_CC_SHARED;
if (mem->unencrypted != !!(*attrs & DMA_ATTR_CC_SHARED))
return (phys_addr_t)DMA_MAPPING_ERROR;
Yeah, exactly that is so much clearer now that the mem->unecrypted is
tied directly.
That logic is reversed though, the incoming ATTR_CC doesn't matter for
swiotlb, that is just the source of the memcpy.
/* swiotlb pool is incorrect for this device */
if (mem->unencrypted != force_dma_unencrypted(dev))
return (phys_addr_t)DMA_MAPPING_ERROR;
/* Force attrs to match the kind of memory in the pool */
if (mem->unencrypted)
*attrs |= DMA_ATTR_CC_SHARED;
else
*attrs &= ~DMA_ATTR_CC_SHARED;
Attrs should be forced to whatever memory swiotlb selected.
Jason
On Tue, May 19, 2026 at 09:35:30PM +0530, Aneesh Kumar K.V wrote:
quoted
Yes, that also resulted in simpler and cleaner code.
swiotlb_tbl_map_single
/*
* If the physical address is encrypted but the device requires
* decrypted DMA, use a decrypted io_tlb_mem and update the
* attributes so the caller knows that a decrypted io_tlb_mem
* was used.
*/
if (!(*attrs & DMA_ATTR_CC_SHARED) && force_dma_unencrypted(dev))
*attrs |= DMA_ATTR_CC_SHARED;
if (mem->unencrypted != !!(*attrs & DMA_ATTR_CC_SHARED))
return (phys_addr_t)DMA_MAPPING_ERROR;
Yeah, exactly that is so much clearer now that the mem->unecrypted is
tied directly.
That logic is reversed though, the incoming ATTR_CC doesn't matter for
swiotlb, that is just the source of the memcpy.
/* swiotlb pool is incorrect for this device */
if (mem->unencrypted != force_dma_unencrypted(dev))
return (phys_addr_t)DMA_MAPPING_ERROR;
/* Force attrs to match the kind of memory in the pool */
if (mem->unencrypted)
*attrs |= DMA_ATTR_CC_SHARED;
else
*attrs &= ~DMA_ATTR_CC_SHARED;
Attrs should be forced to whatever memory swiotlb selected.
But that will not handle a T=1 device that wants to use swiotlb to
bounce unencrypted memory. That is:
force_dma_unencrypted(dev) == 0 /* T=1 device */
attrs = DMA_ATTR_CC_SHARED;
In that case, it should use an unencrypted io_tlb_mem:
mem->unencrypted == 1
-aneesh
From: Jason Gunthorpe <jgg@ziepe.ca> Date: 2026-05-20 13:40:42
On Wed, May 20, 2026 at 09:27:27AM +0530, Aneesh Kumar K.V wrote:
Jason Gunthorpe [off-list ref] writes:
quoted
On Tue, May 19, 2026 at 09:35:30PM +0530, Aneesh Kumar K.V wrote:
quoted
Yes, that also resulted in simpler and cleaner code.
swiotlb_tbl_map_single
/*
* If the physical address is encrypted but the device requires
* decrypted DMA, use a decrypted io_tlb_mem and update the
* attributes so the caller knows that a decrypted io_tlb_mem
* was used.
*/
if (!(*attrs & DMA_ATTR_CC_SHARED) && force_dma_unencrypted(dev))
*attrs |= DMA_ATTR_CC_SHARED;
if (mem->unencrypted != !!(*attrs & DMA_ATTR_CC_SHARED))
return (phys_addr_t)DMA_MAPPING_ERROR;
Yeah, exactly that is so much clearer now that the mem->unecrypted is
tied directly.
That logic is reversed though, the incoming ATTR_CC doesn't matter for
swiotlb, that is just the source of the memcpy.
/* swiotlb pool is incorrect for this device */
if (mem->unencrypted != force_dma_unencrypted(dev))
return (phys_addr_t)DMA_MAPPING_ERROR;
/* Force attrs to match the kind of memory in the pool */
if (mem->unencrypted)
*attrs |= DMA_ATTR_CC_SHARED;
else
*attrs &= ~DMA_ATTR_CC_SHARED;
Attrs should be forced to whatever memory swiotlb selected.
But that will not handle a T=1 device that wants to use swiotlb to
bounce unencrypted memory. That is:
force_dma_unencrypted(dev) == 0 /* T=1 device */
attrs = DMA_ATTR_CC_SHARED;
In that case, it should use an unencrypted io_tlb_mem:
mem->unencrypted == 1
No! The DMA_ATTR_CC_SHARED only states the nature of the source
memory, the DMA transfer will always happen under T=1
It is perfectly fine to memcpy from shared to private and do a T=1 DMA
from the private memory if we have to bounce.
Jason
gart_alloc_coherent() first allocates memory through dma_direct_alloc(),
which returns a direct-mapped DMA address in dma_addr. When force_iommu is
enabled, the buffer is then remapped.
Do not overwrite dma_addr before dma_map_area() has succeeded. Keep the
dma_map_area result in a temporary variable so the direct DMA address
remains available for dma_direct_free() on the error path.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/x86/kernel/amd_gart_64.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
On Tue, May 12, 2026 at 10:05 AM Aneesh Kumar K.V (Arm)
[off-list ref] wrote:
quoted hunk
@@ -1411,6 +1436,16 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr, if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) pr_warn_once("Memory encryption is active and system is using DMA bounce buffers\n");+ /*+ * if we are trying to swiotlb map a decrypted paddr or the paddr is encrypted+ * but the device is forcing decryption, use decrypted io_tlb_mem+ */+ if ((attrs & DMA_ATTR_CC_SHARED) || force_dma_unencrypted(dev))
I don't think swiotlb needs to know about force_dma_unencrypted(), the
dma/direct caller should have all the information to pass the
appropriate flags.
Thanks.
Mostafa
quoted hunk
+ require_decrypted = true;++ if (require_decrypted != mem->unencrypted)+ return (phys_addr_t)DMA_MAPPING_ERROR;+
On Tue, May 12, 2026 at 10:05 AM Aneesh Kumar K.V (Arm)
[off-list ref] wrote:
quoted
@@ -1411,6 +1436,16 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr, if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) pr_warn_once("Memory encryption is active and system is using DMA bounce buffers\n");+ /*+ * if we are trying to swiotlb map a decrypted paddr or the paddr is encrypted+ * but the device is forcing decryption, use decrypted io_tlb_mem+ */+ if ((attrs & DMA_ATTR_CC_SHARED) || force_dma_unencrypted(dev))
I don't think swiotlb needs to know about force_dma_unencrypted(), the
dma/direct caller should have all the information to pass the
appropriate flags.
Thanks.
Mostafa
quoted
+ require_decrypted = true;++ if (require_decrypted != mem->unencrypted)+ return (phys_addr_t)DMA_MAPPING_ERROR;+
Based on other email threads, this is now updated to
@@ -1372,9 +1417,19 @@ static unsigned long mem_used(struct io_tlb_mem *mem) * any pre- or post-padding for alignment * @alloc_align_mask: Required start and end alignment of the allocated buffer * @dir: DMA direction- * @attrs: Optional DMA attributes for the map operation+ * @attrs: Optional DMA attributes for the map operation, updated+ * to match the selected SWIOTLB pool * * Find and allocate a suitable sequence of IO TLB slots for the request.+ * The device's SWIOTLB pool must match the device's current DMA encryption+ * requirements. If the device requires decrypted DMA, bouncing is done through+ * an unencrypted pool and the mapping is marked shared. If the device can DMA+ * to encrypted memory, bouncing is done through an encrypted pool even when the+ * original DMA address was unencrypted. Enabling encrypted DMA for a device is+ * therefore expected to update its default io_tlb_mem to an encrypted pool, so+ * later bounce mappings for both encrypted and decrypted original memory use+ * that encrypted pool.+ * * The allocated space starts at an alignment specified by alloc_align_mask, * and the size of the allocated space is rounded up so that the total amount * of allocated space is a multiple of (alloc_align_mask + 1). If
@@ -1411,6 +1466,16 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr, if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) pr_warn_once("Memory encryption is active and system is using DMA bounce buffers\n");+ /* swiotlb pool is incorrect for this device */+ if (unlikely(mem->unencrypted != force_dma_unencrypted(dev)))+ return (phys_addr_t)DMA_MAPPING_ERROR;++ /* Force attrs to match the kind of memory in the pool */+ if (mem->unencrypted)+ *attrs |= DMA_ATTR_CC_SHARED;+ else+ *attrs &= ~DMA_ATTR_CC_SHARED;+ /* * The default swiotlb memory pool is allocated with PAGE_SIZE * alignment. If a mapping is requested with larger alignment,
@@ -1608,8 +1673,11 @@ dma_addr_t swiotlb_map(struct device *dev, phys_addr_t paddr, size_t size, if (swiotlb_addr == (phys_addr_t)DMA_MAPPING_ERROR) return DMA_MAPPING_ERROR;- /* Ensure that the address returned is DMA'ble */- dma_addr = phys_to_dma_unencrypted(dev, swiotlb_addr);+ if (attrs & DMA_ATTR_CC_SHARED)+ dma_addr = phys_to_dma_unencrypted(dev, swiotlb_addr);+ else+ dma_addr = phys_to_dma_encrypted(dev, swiotlb_addr);+ if (unlikely(!dma_capable(dev, dma_addr, size, true))) { __swiotlb_tbl_unmap_single(dev, swiotlb_addr, size, dir, attrs | DMA_ATTR_SKIP_CPU_SYNC,