git: 56abdbc5f709 - main - arm64: VM/PMAP changes for CCA guest support
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 12 May 2026 16:55:31 UTC
The branch main has been updated by andrew:
URL: https://cgit.FreeBSD.org/src/commit/?id=56abdbc5f709fc0e18624b3b7586647459922a41
commit 56abdbc5f709fc0e18624b3b7586647459922a41
Author: Sarah Walker <sarah.walker2@arm.com>
AuthorDate: 2026-05-12 11:29:20 +0000
Commit: Andrew Turner <andrew@FreeBSD.org>
CommitDate: 2026-05-12 16:54:41 +0000
arm64: VM/PMAP changes for CCA guest support
When in a realm:
- Mappings with mode VM_MEMATTR_DEVICE and VM_MEMATTR_DEVICE_NP are
unprotected
- Imported busdma buffers in protected memory are always bounced
- If EARLY_PRINTK is in use, the UART physical address must be in the
unprotected address space
Reviewed by: andrew
Sponsored by: Arm Ltd
Differential Revision: https://reviews.freebsd.org/D56599
---
sys/arm64/arm64/busdma_bounce.c | 68 ++++++++++++++++++++++++++++++++++++++---
sys/arm64/arm64/efirt_machdep.c | 10 +++++-
sys/arm64/arm64/pmap.c | 40 ++++++++++++++++++++++++
sys/conf/files.arm64 | 1 +
sys/vm/vm_page.c | 8 +++++
5 files changed, 122 insertions(+), 5 deletions(-)
diff --git a/sys/arm64/arm64/busdma_bounce.c b/sys/arm64/arm64/busdma_bounce.c
index 74fa611e6d1a..db7e108812aa 100644
--- a/sys/arm64/arm64/busdma_bounce.c
+++ b/sys/arm64/arm64/busdma_bounce.c
@@ -36,6 +36,7 @@
#include <sys/domainset.h>
#include <sys/malloc.h>
#include <sys/bus.h>
+#include <sys/busdma_bufalloc.h>
#include <sys/interrupt.h>
#include <sys/kernel.h>
#include <sys/ktr.h>
@@ -56,6 +57,7 @@
#include <machine/atomic.h>
#include <machine/bus.h>
#include <machine/md_var.h>
+#include <machine/rsi.h>
#include <arm64/include/bus_dma_impl.h>
#define MAX_BPAGES 4096
@@ -120,6 +122,8 @@ static void _bus_dmamap_count_pages(bus_dma_tag_t dmat, bus_dmamap_t map,
static void _bus_dmamap_count_phys(bus_dma_tag_t dmat, bus_dmamap_t map,
vm_paddr_t buf, bus_size_t buflen, int flags);
+static busdma_bufalloc_t nonsecure_allocator;
+
static MALLOC_DEFINE(M_BUSDMA, "busdma", "busdma metadata");
#define dmat_alignment(dmat) ((dmat)->common.alignment)
@@ -215,6 +219,10 @@ might_bounce(bus_dma_tag_t dmat, bus_dmamap_t map, bus_addr_t paddr,
if (map && (map->flags & DMAMAP_FROM_DMAMEM) != 0)
return (false);
+ /* Bounce if accessing secure memory */
+ if (in_realm() && !(paddr & prot_ns_shared_pa))
+ return (true);
+
if ((dmat->bounce_flags & BF_COULD_BOUNCE) != 0)
return (true);
@@ -239,6 +247,10 @@ must_bounce(bus_dma_tag_t dmat, bus_dmamap_t map, bus_addr_t paddr,
addr_needs_bounce(dmat, paddr))
return (true);
+ /* Bounce if accessing secure memory */
+ if (in_realm() && !(paddr & prot_ns_shared_pa))
+ return (true);
+
return (false);
}
@@ -492,6 +504,7 @@ static int
bounce_bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags,
bus_dmamap_t *mapp)
{
+ struct busdma_bufzone *bufzone;
vm_memattr_t attr;
int mflags;
@@ -524,6 +537,9 @@ bounce_bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags,
else
attr = VM_MEMATTR_DEFAULT;
+ if (in_realm())
+ mflags |= M_UNPROTECTED;
+
/*
* Create the map, but don't set the could bounce flag as
* this allocation should never bounce;
@@ -567,13 +583,16 @@ bounce_bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags,
*
* In the meantime warn the user if malloc gets it wrong.
*/
- if (dmat->alloc_size <= PAGE_SIZE &&
+
+ bufzone = busdma_bufalloc_findzone(nonsecure_allocator,
+ dmat->alloc_size);
+
+ if (bufzone &&
+ dmat->alloc_size <= PAGE_SIZE &&
dmat->alloc_alignment <= PAGE_SIZE &&
dmat->common.lowaddr >= ptoa((vm_paddr_t)Maxmem) &&
attr == VM_MEMATTR_DEFAULT) {
- *vaddr = malloc_domainset_aligned(dmat->alloc_size,
- dmat->alloc_alignment, M_DEVBUF,
- DOMAINSET_PREF(dmat->common.domain), mflags);
+ *vaddr = uma_zalloc(bufzone->umazone, mflags);
} else if (dmat->common.nsegments >=
howmany(dmat->alloc_size, MIN(dmat->common.maxsegsz, PAGE_SIZE)) &&
dmat->alloc_alignment <= PAGE_SIZE &&
@@ -1148,3 +1167,44 @@ struct bus_dma_impl bus_dma_bounce_impl = {
.load_kmsan = bounce_bus_dmamap_load_kmsan,
#endif
};
+
+static void *
+nonsecure_alloc(uma_zone_t zone, vm_size_t size, int domain, uint8_t *pflag,
+ int wait)
+{
+ void *p; /* Returned page */
+
+ *pflag = UMA_SLAB_KERNEL;
+ p = kmem_malloc_domainset(DOMAINSET_FIXED(domain), size,
+ wait | M_UNPROTECTED);
+
+ return (p);
+}
+
+static void
+nonsecure_free(void *mem, vm_size_t size, uint8_t flags)
+{
+ KASSERT((flags & UMA_SLAB_KERNEL) != 0,
+ ("UMA: page_free used with invalid flags %x", flags));
+
+ kmem_free(mem, size);
+}
+
+static void
+busdma_bounce_init(void *dummy)
+{
+ if (in_realm())
+ nonsecure_allocator = busdma_bufalloc_create("nonsecure",
+ dcache_line_size, /* minimum_alignment */
+ nonsecure_alloc, /* uma_alloc func */
+ nonsecure_free, /* uma_free func */
+ UMA_ZONE_NOTOUCH); /* uma_zcreate_flags */
+ else
+ nonsecure_allocator = busdma_bufalloc_create("nonsecure",
+ dcache_line_size, /* minimum_alignment */
+ NULL, /* uma_alloc func */
+ NULL, /* uma_free func */
+ UMA_ZONE_NOTOUCH); /* uma_zcreate_flags */
+}
+
+SYSINIT(busdma, SI_SUB_KMEM + 1, SI_ORDER_FIRST, busdma_bounce_init, NULL);
diff --git a/sys/arm64/arm64/efirt_machdep.c b/sys/arm64/arm64/efirt_machdep.c
index b079393cd2a9..07946a2bdb82 100644
--- a/sys/arm64/arm64/efirt_machdep.c
+++ b/sys/arm64/arm64/efirt_machdep.c
@@ -46,6 +46,7 @@
#include <sys/vmmeter.h>
#include <machine/pte.h>
+#include <machine/rsi.h>
#include <machine/vmparam.h>
#include <vm/vm.h>
@@ -171,6 +172,7 @@ efi_create_1t1_map(struct efi_md *map, int ndesc, int descsz)
vm_page_t efi_l0_page;
uint64_t idx;
int i, mode;
+ uint64_t pa;
obj_1t1_pt = vm_pager_allocate(OBJT_PHYS, NULL, L0_ENTRIES +
L0_ENTRIES * Ln_ENTRIES + L0_ENTRIES * Ln_ENTRIES * Ln_ENTRIES +
@@ -231,7 +233,13 @@ efi_create_1t1_map(struct efi_md *map, int ndesc, int descsz)
for (va = p->md_phys, idx = 0; idx < p->md_pages;
idx += (PAGE_SIZE / EFI_PAGE_SIZE), va += PAGE_SIZE) {
l3 = efi_1t1_l3(va);
- *l3 = va | l3_attr;
+
+ if (mode == VM_MEMATTR_DEVICE && in_realm())
+ pa = va | prot_ns_shared_pa;
+ else
+ pa = va;
+
+ *l3 = PHYS_TO_PTE(pa) | l3_attr;
}
VM_OBJECT_WUNLOCK(obj_1t1_pt);
}
diff --git a/sys/arm64/arm64/pmap.c b/sys/arm64/arm64/pmap.c
index 2d2982fdfae7..adc583812e5b 100644
--- a/sys/arm64/arm64/pmap.c
+++ b/sys/arm64/arm64/pmap.c
@@ -152,6 +152,7 @@
#include <machine/machdep.h>
#include <machine/md_var.h>
#include <machine/pcb.h>
+#include <machine/rsi.h>
#ifdef NUMA
#define PMAP_MEMDOM MAXMEMDOM
@@ -539,6 +540,9 @@ static int pmap_bti_copy(pmap_t dst_pmap, pmap_t src_pmap);
static void pmap_bti_deassign_all(pmap_t pmap);
static void pagezero(void *);
+static void pmap_set_protected(pt_entry_t old_l3);
+static void pmap_set_unprotected(pt_entry_t new_l3);
+
/*
* These load the old table data and store the new value.
* They need to be atomic as the System MMU may write to the table at
@@ -2381,6 +2385,11 @@ pmap_kenter(vm_offset_t sva, vm_size_t size, vm_paddr_t pa, int mode)
KASSERT((size & PAGE_MASK) == 0,
("pmap_kenter: Mapping is not page-sized"));
+ /* CCA - Map devices as nonsecure */
+ if (in_realm() && (mode == VM_MEMATTR_DEVICE ||
+ mode == VM_MEMATTR_DEVICE_NP))
+ pa |= prot_ns_shared_pa;
+
attr = ATTR_AF | pmap_sh_attr | ATTR_S1_AP(ATTR_S1_AP_RW) |
ATTR_S1_XN | ATTR_KERN_GP | ATTR_S1_IDX(mode);
old_l3e = 0;
@@ -4224,6 +4233,9 @@ pmap_remove_l3_range(pmap_t pmap, pd_entry_t l2e, vm_offset_t sva,
if ((old_l3 & ATTR_SW_WIRED) != 0)
pmap->pm_stats.wired_count--;
pmap_resident_count_dec(pmap, 1);
+ /* Below will only be true in a realm environment. */
+ if (PTE_TO_PHYS(old_l3) & prot_ns_shared_pa)
+ pmap_set_protected(old_l3);
if ((old_l3 & ATTR_SW_MANAGED) != 0) {
m = PTE_TO_VM_PAGE(old_l3);
if (pmap_pte_dirty(pmap, old_l3))
@@ -5376,6 +5388,28 @@ restart:
return (KERN_SUCCESS);
}
+static void
+pmap_set_unprotected(pt_entry_t new_l3)
+{
+ vm_paddr_t pa;
+
+ pa = PTE_TO_PHYS(new_l3) & ~prot_ns_shared_pa;
+
+ rsi_set_addr_range_state(pa, pa + L3_SIZE, RSI_RIPAS_EMPTY,
+ RSI_CHANGE_DESTROYED, NULL);
+}
+
+static void
+pmap_set_protected(pt_entry_t old_l3)
+{
+ vm_paddr_t pa;
+
+ pa = PTE_TO_PHYS(old_l3) & ~prot_ns_shared_pa;
+
+ rsi_set_addr_range_state(pa, pa + L3_SIZE, RSI_RIPAS_RAM,
+ RSI_CHANGE_DESTROYED, NULL);
+}
+
/*
* Insert the given physical page (p) at
* the specified virtual address (v) in the
@@ -5409,6 +5443,8 @@ pmap_enter(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot,
if ((m->oflags & VPO_UNMANAGED) == 0)
VM_PAGE_OBJECT_BUSY_ASSERT(m);
pa = VM_PAGE_TO_PHYS(m);
+ if (in_realm() && (flags & PMAP_ENTER_UNPROTECTED) != 0)
+ pa |= prot_ns_shared_pa;
new_l3 = (pt_entry_t)(PHYS_TO_PTE(pa) | ATTR_AF | pmap_sh_attr |
L3_PAGE);
new_l3 |= pmap_pte_memattr(pmap, m->md.pv_memattr);
@@ -5728,6 +5764,10 @@ validate:
#endif
rv = KERN_SUCCESS;
+
+ if (in_realm() && (flags & PMAP_ENTER_UNPROTECTED) != 0)
+ pmap_set_unprotected(new_l3);
+
out:
if (lock != NULL)
rw_wunlock(lock);
diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64
index 8f550a644db6..faecea51d5c1 100644
--- a/sys/conf/files.arm64
+++ b/sys/conf/files.arm64
@@ -9,6 +9,7 @@ kern/subr_devmap.c standard
kern/subr_efi_map.c standard
kern/subr_intr.c optional intrng
kern/subr_physmem.c standard
+kern/subr_busdma_bufalloc.c standard
libkern/strlen.c standard
libkern/arm64/crc32c_armv8.S standard
diff --git a/sys/vm/vm_page.c b/sys/vm/vm_page.c
index bdea77bfabf0..233292bdd6cb 100644
--- a/sys/vm/vm_page.c
+++ b/sys/vm/vm_page.c
@@ -110,6 +110,10 @@
#include <vm/uma_int.h>
#include <machine/md_var.h>
+#if defined(__aarch64__)
+#include <machine/pmap.h>
+#include <machine/rsi.h>
+#endif
struct vm_domain vm_dom[MAXMEMDOM];
@@ -1290,6 +1294,10 @@ PHYS_TO_VM_PAGE(vm_paddr_t pa)
vm_page_t m;
#ifdef VM_PHYSSEG_SPARSE
+#if defined(__aarch64__)
+ if (in_realm())
+ pa &= ~prot_ns_shared_pa; /* Mask off secure bit */
+#endif
m = vm_phys_paddr_to_vm_page(pa);
if (m == NULL)
m = vm_phys_fictitious_to_vm_page(pa);