static int MMapGEM(struct file* pFile, struct vm_area_struct* ps_vma) { extern struct drm_device *gpsPVRDRMDev; uint32_t uHandle = (uint32_t)ps_vma->vm_pgoff; struct drm_file *pDRMFile = PVR_DRM_FILE_FROM_FILE(pFile); struct drm_gem_object *pObj = drm_gem_object_lookup(gpsPVRDRMDev, pDRMFile, uHandle); int ret; if (pObj == NULL) { return -EEXIST; } if (ps_vma->vm_file != NULL) { fput(ps_vma->vm_file); ps_vma->vm_file = pObj->filp; get_file(pObj->filp); } ps_vma->vm_pgoff = 0; ret = pObj->filp->f_op->mmap(pObj->filp, ps_vma); drm_gem_object_unreference_unlocked(pObj); return ret; }
PVRSRV_ERROR OSSecureImport(IMG_SECURE_TYPE hSecure, IMG_PVOID *ppvData) { struct file *secure_file; CONNECTION_DATA *psSecureConnection; PVRSRV_ERROR eError; secure_file = fget(hSecure); if (!secure_file) { eError = PVRSRV_ERROR_INVALID_PARAMS; goto err_out; } #if defined(SUPPORT_DRM) psSecureConnection = LinuxConnectionFromFile(PVR_DRM_FILE_FROM_FILE(secure_file)); #else psSecureConnection = LinuxConnectionFromFile(secure_file); #endif if (psSecureConnection->hSecureData == IMG_NULL) { eError = PVRSRV_ERROR_INVALID_PARAMS; goto err_fput; } *ppvData = psSecureConnection->hSecureData; fput(secure_file); return PVRSRV_OK; err_fput: fput(secure_file); err_out: PVR_ASSERT(eError != PVRSRV_OK); return eError; }
int MMapPMR(struct file *pFile, struct vm_area_struct *ps_vma) { PVRSRV_ERROR eError; IMG_HANDLE hSecurePMRHandle; IMG_SIZE_T uiLength; IMG_DEVMEM_OFFSET_T uiOffset; unsigned long uiPFN; IMG_HANDLE hPMRResmanHandle; PMR *psPMR; PMR_FLAGS_T ulPMRFlags; IMG_UINT32 ui32CPUCacheFlags; unsigned long ulNewFlags = 0; pgprot_t sPageProt; #if defined(SUPPORT_DRM) CONNECTION_DATA *psConnection = LinuxConnectionFromFile(PVR_DRM_FILE_FROM_FILE(pFile)); #else CONNECTION_DATA *psConnection = LinuxConnectionFromFile(pFile); #endif #if defined(PVR_MMAP_USE_VM_INSERT) IMG_BOOL bMixedMap = IMG_FALSE; #endif /* * The pmr lock used here to protect both handle related operations and PMR * operations. * This was introduced to fix lockdep issue. */ mutex_lock(&g_sMMapMutex); PMRLock(); #if defined(SUPPORT_DRM_DC_MODULE) psPMR = PVRSRVGEMMMapLookupPMR(pFile, ps_vma); if (!psPMR) #endif { hSecurePMRHandle = (IMG_HANDLE)((IMG_UINTPTR_T)ps_vma->vm_pgoff); eError = PVRSRVLookupHandle(psConnection->psHandleBase, (IMG_HANDLE *) &hPMRResmanHandle, hSecurePMRHandle, PVRSRV_HANDLE_TYPE_PHYSMEM_PMR); if (eError != PVRSRV_OK) { goto e0; } eError = ResManFindPrivateDataByPtr(hPMRResmanHandle, (void **)&psPMR); if (eError != PVRSRV_OK) { goto e0; } } /* * Take a reference on the PMR, make's sure that it can't be freed * while it's mapped into the user process */ PMRRefPMR(psPMR); PMRUnlock(); eError = PMRLockSysPhysAddresses(psPMR, PAGE_SHIFT); if (eError != PVRSRV_OK) { goto e1; } if (((ps_vma->vm_flags & VM_WRITE) != 0) && ((ps_vma->vm_flags & VM_SHARED) == 0)) { eError = PVRSRV_ERROR_INVALID_PARAMS; goto e1; } /* * We ought to call PMR_Flags() here to check the permissions * against the requested mode, and possibly to set up the cache * control protflags */ eError = PMR_Flags(psPMR, &ulPMRFlags); if (eError != PVRSRV_OK) { goto e1; } ulNewFlags = ps_vma->vm_flags; #if 0 /* Discard user read/write request, we will pull these flags from the PMR */ ulNewFlags &= ~(VM_READ | VM_WRITE); if (ulPMRFlags & PVRSRV_MEMALLOCFLAG_CPU_READABLE) { ulNewFlags |= VM_READ; } if (ulPMRFlags & PVRSRV_MEMALLOCFLAG_CPU_WRITEABLE) { ulNewFlags |= VM_WRITE; } #endif ps_vma->vm_flags = ulNewFlags; #if defined (CONFIG_ARM64) sPageProt = __pgprot_modify(ps_vma->vm_page_prot, 0, vm_get_page_prot(ulNewFlags)); #elif defined(CONFIG_ARM) sPageProt = __pgprot_modify(ps_vma->vm_page_prot, L_PTE_MT_MASK, vm_get_page_prot(ulNewFlags)); #elif defined(CONFIG_X86) sPageProt = pgprot_modify(ps_vma->vm_page_prot, vm_get_page_prot(ulNewFlags)); #elif defined(CONFIG_METAG) || defined(CONFIG_MIPS) sPageProt = vm_get_page_prot(ulNewFlags); #else #error Please add pgprot_modify equivalent for your system #endif ui32CPUCacheFlags = DevmemCPUCacheMode(ulPMRFlags); switch (ui32CPUCacheFlags) { case PVRSRV_MEMALLOCFLAG_CPU_UNCACHED: sPageProt = pgprot_noncached(sPageProt); break; case PVRSRV_MEMALLOCFLAG_CPU_WRITE_COMBINE: sPageProt = pgprot_writecombine(sPageProt); break; case PVRSRV_MEMALLOCFLAG_CPU_CACHED: break; default: eError = PVRSRV_ERROR_INVALID_PARAMS; goto e1; } ps_vma->vm_page_prot = sPageProt; uiLength = ps_vma->vm_end - ps_vma->vm_start; ps_vma->vm_flags |= VM_IO; /* Don't include the mapping in core dumps */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)) ps_vma->vm_flags |= VM_DONTDUMP; #else ps_vma->vm_flags |= VM_RESERVED; #endif /* * Disable mremap because our nopage handler assumes all * page requests have already been validated. */ ps_vma->vm_flags |= VM_DONTEXPAND; /* Don't allow mapping to be inherited across a process fork */ ps_vma->vm_flags |= VM_DONTCOPY; #if defined(PVR_MMAP_USE_VM_INSERT) { /* Scan the map range for pfns without struct page* handling. If we find * one, this is a mixed map, and we can't use vm_insert_page(). */ for (uiOffset = 0; uiOffset < uiLength; uiOffset += 1ULL<<PAGE_SHIFT) { IMG_CPU_PHYADDR sCpuPAddr; IMG_BOOL bValid; eError = PMR_CpuPhysAddr(psPMR, uiOffset, &sCpuPAddr, &bValid); PVR_ASSERT(eError == PVRSRV_OK); if (eError) { goto e2; } if (bValid) { uiPFN = sCpuPAddr.uiAddr >> PAGE_SHIFT; PVR_ASSERT(((IMG_UINT64)uiPFN << PAGE_SHIFT) == sCpuPAddr.uiAddr); if (!pfn_valid(uiPFN) || page_count(pfn_to_page(uiPFN)) == 0) { bMixedMap = IMG_TRUE; } } } if (bMixedMap) { ps_vma->vm_flags |= VM_MIXEDMAP; } } #endif /* defined(PVR_MMAP_USE_VM_INSERT) */ for (uiOffset = 0; uiOffset < uiLength; uiOffset += 1ULL<<PAGE_SHIFT) { IMG_SIZE_T uiNumContiguousBytes; IMG_INT32 iStatus; IMG_CPU_PHYADDR sCpuPAddr; IMG_BOOL bValid; uiNumContiguousBytes = 1ULL<<PAGE_SHIFT; eError = PMR_CpuPhysAddr(psPMR, uiOffset, &sCpuPAddr, &bValid); PVR_ASSERT(eError == PVRSRV_OK); if (eError) { goto e2; } /* Only map in pages that are valid, any that aren't will be picked up by the nopage handler which will return a zeroed page for us */ if (bValid) { uiPFN = sCpuPAddr.uiAddr >> PAGE_SHIFT; PVR_ASSERT(((IMG_UINT64)uiPFN << PAGE_SHIFT) == sCpuPAddr.uiAddr); #if defined(PVR_MMAP_USE_VM_INSERT) if (bMixedMap) { /* This path is just for debugging. It should be equivalent * to the remap_pfn_range() path. */ iStatus = vm_insert_mixed(ps_vma, ps_vma->vm_start + uiOffset, uiPFN); } else { iStatus = vm_insert_page(ps_vma, ps_vma->vm_start + uiOffset, pfn_to_page(uiPFN)); } #else /* defined(PVR_MMAP_USE_VM_INSERT) */ iStatus = remap_pfn_range(ps_vma, ps_vma->vm_start + uiOffset, uiPFN, uiNumContiguousBytes, ps_vma->vm_page_prot); #endif /* defined(PVR_MMAP_USE_VM_INSERT) */ PVR_ASSERT(iStatus == 0); if(iStatus) { // N.B. not the right error code, but, it doesn't get propagated anyway... :( eError = PVRSRV_ERROR_OUT_OF_MEMORY; goto e2; } #if defined(PVRSRV_ENABLE_PROCESS_STATS) /* USER MAPPING*/ #if !defined(PVRSRV_ENABLE_MEMORY_STATS) PVRSRVStatsIncrMemAllocStat(PVRSRV_MEM_ALLOC_TYPE_MAP_UMA_LMA_PAGES, PAGE_SIZE); #else PVRSRVStatsAddMemAllocRecord(PVRSRV_MEM_ALLOC_TYPE_MAP_UMA_LMA_PAGES, (IMG_VOID*)(IMG_UINTPTR_T)(ps_vma->vm_start + uiOffset), sCpuPAddr, PAGE_SIZE, IMG_NULL); #endif #endif } (void)pFile; } /* let us see the PMR so we can unlock it later */ ps_vma->vm_private_data = psPMR; /* Install open and close handlers for ref-counting */ ps_vma->vm_ops = &gsMMapOps; mutex_unlock(&g_sMMapMutex); return 0; /* error exit paths follow */ e2: PVR_DPF((PVR_DBG_ERROR, "don't know how to handle this error. Abort!")); PMRUnlockSysPhysAddresses(psPMR); e1: PMRUnrefPMR(psPMR); goto em1; e0: PVR_DPF((PVR_DBG_ERROR, "Error in MMapPMR critical section")); PMRUnlock(); em1: PVR_ASSERT(eError != PVRSRV_OK); PVR_DPF((PVR_DBG_ERROR, "unable to translate error %d", eError)); mutex_unlock(&g_sMMapMutex); return -ENOENT; // -EAGAIN // or what? }
PVRSRV_ERROR OSSecureExport(CONNECTION_DATA *psConnection, IMG_PVOID pvData, IMG_SECURE_TYPE *phSecure, CONNECTION_DATA **ppsSecureConnection) { ENV_CONNECTION_DATA *psEnvConnection; CONNECTION_DATA *psSecureConnection; struct file *connection_file; struct file *secure_file; struct dentry *secure_dentry; struct vfsmount *secure_mnt; int secure_fd; IMG_BOOL bPmrUnlocked = IMG_FALSE; PVRSRV_ERROR eError; /* Obtain the current connections struct file */ psEnvConnection = PVRSRVConnectionPrivateData(psConnection); connection_file = LinuxFileFromEnvConnection(psEnvConnection); /* Allocate a fd number */ secure_fd = get_unused_fd(); if (secure_fd < 0) { eError = PVRSRV_ERROR_OUT_OF_MEMORY; goto e0; } /* Get a reference to the dentry so when close is called we don't drop the last reference too early and delete the file */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)) secure_dentry = dget(connection_file->f_path.dentry); secure_mnt = mntget(connection_file->f_path.mnt); #else secure_dentry = dget(connection_file->f_dentry); secure_mnt = mntget(connection_file->f_vfsmnt); #endif /* PMR lock needs to be released before bridge lock to keep lock hierarchy * and avoid deadlock situation. * OSSecureExport() can be called from functions that are not acquiring * PMR lock (e.g. by PVRSRVSyncPrimServerSecureExportKM()) so we have to * check if PMR lock is locked. */ if (PMRIsLockedByMe()) { PMRUnlock(); bPmrUnlocked = IMG_TRUE; } OSReleaseBridgeLock(); /* Open our device (using the file information from our current connection) */ secure_file = dentry_open( #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) &connection_file->f_path, #else connection_file->f_dentry, connection_file->f_vfsmnt, #endif connection_file->f_flags, current_cred()); OSAcquireBridgeLock(); if (bPmrUnlocked) PMRLock(); /* Bail if the open failed */ if (IS_ERR(secure_file)) { put_unused_fd(secure_fd); eError = PVRSRV_ERROR_OUT_OF_MEMORY; goto e0; } /* Bind our struct file with it's fd number */ fd_install(secure_fd, secure_file); /* Return the new services connection our secure data created */ #if defined(SUPPORT_DRM) psSecureConnection = LinuxConnectionFromFile(PVR_DRM_FILE_FROM_FILE(secure_file)); #else psSecureConnection = LinuxConnectionFromFile(secure_file); #endif /* Save the private data */ PVR_ASSERT(psSecureConnection->hSecureData == IMG_NULL); psSecureConnection->hSecureData = pvData; *phSecure = secure_fd; *ppsSecureConnection = psSecureConnection; return PVRSRV_OK; e0: PVR_ASSERT(eError != PVRSRV_OK); return eError; }
PVRSRV_ERROR OSSecureExport(CONNECTION_DATA *psConnection, IMG_PVOID pvData, IMG_SECURE_TYPE *phSecure, CONNECTION_DATA **ppsSecureConnection) { ENV_CONNECTION_DATA *psEnvConnection; CONNECTION_DATA *psSecureConnection; struct file *connection_file; struct file *secure_file; struct dentry *secure_dentry; struct vfsmount *secure_mnt; int secure_fd; PVRSRV_ERROR eError; /* Obtain the current connections struct file */ psEnvConnection = PVRSRVConnectionPrivateData(psConnection); connection_file = LinuxFileFromEnvConnection(psEnvConnection); /* Allocate a fd number */ secure_fd = get_unused_fd(); if (secure_fd < 0) { eError = PVRSRV_ERROR_OUT_OF_MEMORY; goto e0; } /* Get a reference to the dentry so when close is called we don't drop the last reference too early and delete the file */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)) secure_dentry = dget(connection_file->f_path.dentry); secure_mnt = mntget(connection_file->f_path.mnt); #else secure_dentry = dget(connection_file->f_dentry); secure_mnt = mntget(connection_file->f_vfsmnt); #endif mutex_unlock(&gPVRSRVLock); /* Open our device (using the file information from our current connection) */ secure_file = dentry_open( #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) &connection_file->f_path, #else connection_file->f_dentry, connection_file->f_vfsmnt, #endif connection_file->f_flags, current_cred()); mutex_lock(&gPVRSRVLock); /* Bail if the open failed */ if (IS_ERR(secure_file)) { put_unused_fd(secure_fd); eError = PVRSRV_ERROR_OUT_OF_MEMORY; goto e0; } /* Bind our struct file with it's fd number */ fd_install(secure_fd, secure_file); /* Return the new services connection our secure data created */ #if defined(SUPPORT_DRM) psSecureConnection = LinuxConnectionFromFile(PVR_DRM_FILE_FROM_FILE(secure_file)); #else psSecureConnection = LinuxConnectionFromFile(secure_file); #endif /* Save the private data */ PVR_ASSERT(psSecureConnection->hSecureData == IMG_NULL); psSecureConnection->hSecureData = pvData; *phSecure = secure_fd; *ppsSecureConnection = psSecureConnection; return PVRSRV_OK; e0: PVR_ASSERT(eError != PVRSRV_OK); return eError; }