From db860a89e44785e99ad7b03104f71eda9ac78f49 Mon Sep 17 00:00:00 2001 From: Leon Schuermann Date: Wed, 20 Dec 2023 16:30:17 -0500 Subject: [PATCH] keystone-linux-driver: fix CMA physical address handling (#398) This commit changes the EPM physical address attribute to be set to the `dma_addr_t dma_handle` returned by `dma_alloc_coherent`, instead of the virtual to physical address mapping of the returned virtual address. The __pa macro used for the virtual to physical translation can be used to map addresses within the contiguous kernel virtual to physical mapping. However, this operation appears to overflow if the allocation in the CMA memory region is outside this range (e.g., before kernel_map.va_pa_offset) and instead returns an address outside of any physical memory mapping (e.g. 0xff20000111b01000 on a system with RAM mapped from 0x080000000 to 0x880000000). When this happens, the Enclave initialization routine is stuck in the EnclavePhysicalMemory::writeMem's memcpy routine while copying the loader binary. Trying to debug this in GDB has the program freeze on the first memory write instruction (sd), and the debugger is unable to move past this instruction. While I have not traced the exact behavior, presumably this is because the Keystone kernel driver sets up the userspace-virtual address mapping to this (unmapped) physical range. A write to this memory then causes a hardware fault. However, because this virtual access is backed by a valid userspace-virtual mapping, which is further not paged out, it will return to the userspace application and attempt to reexecute the faulting instruction. For buddy-allocator based EPM memory, `pa` is simply set to `__pa(epm_vaddr)`. This change replaces remaining virtual-physical mappings with usage of the dedicated `pa` attribute. Both `root_page_table` and `ptr` still contain the kernel-virtual `epm_vaddr`. With this change, I can successfully initialize Enclaves with a large amount of freemem (>= 64MB) on a Linux 6.1.66 #1-NixOS SMP system running on a (patched) QEMU 8.1.3 riscv64 virt machine with 32GB RAM and a 1GB CMA reservation as a kernel parameter. Co-authored-by: Gongqi Huang --- linux-keystone-driver/keystone-ioctl.c | 2 +- linux-keystone-driver/keystone-page.c | 2 +- linux-keystone-driver/keystone.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/linux-keystone-driver/keystone-ioctl.c b/linux-keystone-driver/keystone-ioctl.c index d743076e8..de8bd66d6 100644 --- a/linux-keystone-driver/keystone-ioctl.c +++ b/linux-keystone-driver/keystone-ioctl.c @@ -24,7 +24,7 @@ int keystone_create_enclave(struct file *filep, unsigned long arg) } /* Pass base page table */ - enclp->pt_ptr = __pa(enclave->epm->root_page_table); + enclp->pt_ptr = enclave->epm->pa; enclp->epm_size = enclave->epm->size; /* allocate UID */ diff --git a/linux-keystone-driver/keystone-page.c b/linux-keystone-driver/keystone-page.c index 68b78143c..e27d0aa8c 100644 --- a/linux-keystone-driver/keystone-page.c +++ b/linux-keystone-driver/keystone-page.c @@ -68,7 +68,7 @@ int epm_init(struct epm* epm, unsigned int min_pages) memset((void*)epm_vaddr, 0, PAGE_SIZE*count); epm->root_page_table = (void*)epm_vaddr; - epm->pa = __pa(epm_vaddr); + epm->pa = (epm->is_cma) ? device_phys_addr : __pa(epm_vaddr); epm->order = order; epm->size = count << PAGE_SHIFT; epm->ptr = epm_vaddr; diff --git a/linux-keystone-driver/keystone.c b/linux-keystone-driver/keystone.c index 23a2c448e..4486dfb76 100644 --- a/linux-keystone-driver/keystone.c +++ b/linux-keystone-driver/keystone.c @@ -56,7 +56,7 @@ int keystone_mmap(struct file* filp, struct vm_area_struct *vma) if(enclave->is_init){ if (vsize > PAGE_SIZE) return -EINVAL; - paddr = __pa(epm->root_page_table) + (vma->vm_pgoff << PAGE_SHIFT); + paddr = epm->pa + (vma->vm_pgoff << PAGE_SHIFT); remap_pfn_range(vma, vma->vm_start, paddr >> PAGE_SHIFT,