| /**************************************************************************** |
| * |
| * Copyright (C) 2005 - 2014 by Vivante Corp. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the license, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not write to the Free Software |
| * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| * |
| *****************************************************************************/ |
| |
| |
| #include "gc_hal_kernel_linux.h" |
| |
| #include <linux/pagemap.h> |
| #include <linux/seq_file.h> |
| #include <linux/mman.h> |
| #include <asm/atomic.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/slab.h> |
| #include <linux/workqueue.h> |
| #include <linux/irqflags.h> |
| #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23) |
| #include <linux/math64.h> |
| #endif |
| #include <linux/delay.h> |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) |
| #include <linux/anon_inodes.h> |
| #endif |
| |
| #if gcdANDROID_NATIVE_FENCE_SYNC |
| #include <linux/file.h> |
| #include "gc_hal_kernel_sync.h" |
| #endif |
| |
| #define _GC_OBJ_ZONE gcvZONE_OS |
| |
| #include "gc_hal_kernel_allocator.h" |
| |
| #define MEMORY_LOCK(os) \ |
| gcmkVERIFY_OK(gckOS_AcquireMutex( \ |
| (os), \ |
| (os)->memoryLock, \ |
| gcvINFINITE)) |
| |
| #define MEMORY_UNLOCK(os) \ |
| gcmkVERIFY_OK(gckOS_ReleaseMutex((os), (os)->memoryLock)) |
| |
| #define MEMORY_MAP_LOCK(os) \ |
| gcmkVERIFY_OK(gckOS_AcquireMutex( \ |
| (os), \ |
| (os)->memoryMapLock, \ |
| gcvINFINITE)) |
| |
| #define MEMORY_MAP_UNLOCK(os) \ |
| gcmkVERIFY_OK(gckOS_ReleaseMutex((os), (os)->memoryMapLock)) |
| |
| |
| /******************************************************************************\ |
| ******************************* Private Functions ****************************** |
| \******************************************************************************/ |
| static gctINT |
| _GetThreadID( |
| void |
| ) |
| { |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) |
| return task_pid_vnr(current); |
| #else |
| return current->pid; |
| #endif |
| } |
| |
| static PLINUX_MDL |
| _CreateMdl( |
| void |
| ) |
| { |
| PLINUX_MDL mdl; |
| |
| gcmkHEADER(); |
| |
| mdl = (PLINUX_MDL)kzalloc(sizeof(struct _LINUX_MDL), GFP_KERNEL | gcdNOWARN); |
| |
| gcmkFOOTER_ARG("0x%X", mdl); |
| return mdl; |
| } |
| |
| static gceSTATUS |
| _DestroyMdlMap( |
| IN PLINUX_MDL Mdl, |
| IN PLINUX_MDL_MAP MdlMap |
| ); |
| |
| static gceSTATUS |
| _DestroyMdl( |
| IN PLINUX_MDL Mdl |
| ) |
| { |
| PLINUX_MDL_MAP mdlMap, next; |
| |
| gcmkHEADER_ARG("Mdl=0x%X", Mdl); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_ARGUMENT(Mdl != gcvNULL); |
| |
| mdlMap = Mdl->maps; |
| |
| while (mdlMap != gcvNULL) |
| { |
| next = mdlMap->next; |
| |
| gcmkVERIFY_OK(_DestroyMdlMap(Mdl, mdlMap)); |
| |
| mdlMap = next; |
| } |
| |
| kfree(Mdl); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| static PLINUX_MDL_MAP |
| _CreateMdlMap( |
| IN PLINUX_MDL Mdl, |
| IN gctINT ProcessID |
| ) |
| { |
| PLINUX_MDL_MAP mdlMap; |
| |
| gcmkHEADER_ARG("Mdl=0x%X ProcessID=%d", Mdl, ProcessID); |
| |
| mdlMap = (PLINUX_MDL_MAP)kmalloc(sizeof(struct _LINUX_MDL_MAP), GFP_KERNEL | gcdNOWARN); |
| if (mdlMap == gcvNULL) |
| { |
| gcmkFOOTER_NO(); |
| return gcvNULL; |
| } |
| |
| mdlMap->pid = ProcessID; |
| mdlMap->vmaAddr = gcvNULL; |
| mdlMap->vma = gcvNULL; |
| mdlMap->count = 0; |
| |
| mdlMap->next = Mdl->maps; |
| Mdl->maps = mdlMap; |
| |
| gcmkFOOTER_ARG("0x%X", mdlMap); |
| return mdlMap; |
| } |
| |
| static gceSTATUS |
| _DestroyMdlMap( |
| IN PLINUX_MDL Mdl, |
| IN PLINUX_MDL_MAP MdlMap |
| ) |
| { |
| PLINUX_MDL_MAP prevMdlMap; |
| |
| gcmkHEADER_ARG("Mdl=0x%X MdlMap=0x%X", Mdl, MdlMap); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_ARGUMENT(MdlMap != gcvNULL); |
| gcmkASSERT(Mdl->maps != gcvNULL); |
| |
| if (Mdl->maps == MdlMap) |
| { |
| Mdl->maps = MdlMap->next; |
| } |
| else |
| { |
| prevMdlMap = Mdl->maps; |
| |
| while (prevMdlMap->next != MdlMap) |
| { |
| prevMdlMap = prevMdlMap->next; |
| |
| gcmkASSERT(prevMdlMap != gcvNULL); |
| } |
| |
| prevMdlMap->next = MdlMap->next; |
| } |
| |
| kfree(MdlMap); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| extern PLINUX_MDL_MAP |
| FindMdlMap( |
| IN PLINUX_MDL Mdl, |
| IN gctINT ProcessID |
| ) |
| { |
| PLINUX_MDL_MAP mdlMap; |
| |
| gcmkHEADER_ARG("Mdl=0x%X ProcessID=%d", Mdl, ProcessID); |
| if(Mdl == gcvNULL) |
| { |
| gcmkFOOTER_NO(); |
| return gcvNULL; |
| } |
| mdlMap = Mdl->maps; |
| |
| while (mdlMap != gcvNULL) |
| { |
| if (mdlMap->pid == ProcessID) |
| { |
| gcmkFOOTER_ARG("0x%X", mdlMap); |
| return mdlMap; |
| } |
| |
| mdlMap = mdlMap->next; |
| } |
| |
| gcmkFOOTER_NO(); |
| return gcvNULL; |
| } |
| |
| /******************************************************************************* |
| ** Integer Id Management. |
| */ |
| gceSTATUS |
| _AllocateIntegerId( |
| IN gcsINTEGER_DB_PTR Database, |
| IN gctPOINTER KernelPointer, |
| OUT gctUINT32 *Id |
| ) |
| { |
| int result; |
| gctINT next; |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) |
| idr_preload(GFP_KERNEL | gcdNOWARN); |
| |
| spin_lock(&Database->lock); |
| |
| next = (Database->curr + 1 <= 0) ? 1 : Database->curr + 1; |
| |
| result = idr_alloc(&Database->idr, KernelPointer, next, 0, GFP_ATOMIC); |
| |
| /* ID allocated should not be 0. */ |
| gcmkASSERT(result != 0); |
| |
| if (result > 0) |
| { |
| Database->curr = *Id = result; |
| } |
| |
| spin_unlock(&Database->lock); |
| |
| idr_preload_end(); |
| |
| if (result < 0) |
| { |
| return gcvSTATUS_OUT_OF_RESOURCES; |
| } |
| #else |
| again: |
| if (idr_pre_get(&Database->idr, GFP_KERNEL | gcdNOWARN) == 0) |
| { |
| return gcvSTATUS_OUT_OF_MEMORY; |
| } |
| |
| spin_lock(&Database->lock); |
| |
| next = (Database->curr + 1 <= 0) ? 1 : Database->curr + 1; |
| |
| /* Try to get a id greater than 0. */ |
| result = idr_get_new_above(&Database->idr, KernelPointer, next, Id); |
| |
| if (!result) |
| { |
| Database->curr = *Id; |
| } |
| |
| spin_unlock(&Database->lock); |
| |
| if (result == -EAGAIN) |
| { |
| goto again; |
| } |
| |
| if (result != 0) |
| { |
| return gcvSTATUS_OUT_OF_RESOURCES; |
| } |
| #endif |
| |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| _QueryIntegerId( |
| IN gcsINTEGER_DB_PTR Database, |
| IN gctUINT32 Id, |
| OUT gctPOINTER * KernelPointer |
| ) |
| { |
| gctPOINTER pointer; |
| |
| spin_lock(&Database->lock); |
| |
| pointer = idr_find(&Database->idr, Id); |
| |
| spin_unlock(&Database->lock); |
| |
| if(pointer) |
| { |
| *KernelPointer = pointer; |
| return gcvSTATUS_OK; |
| } |
| else |
| { |
| gcmkTRACE_ZONE( |
| gcvLEVEL_ERROR, gcvZONE_OS, |
| "%s(%d) Id = %d is not found", |
| __FUNCTION__, __LINE__, Id); |
| |
| return gcvSTATUS_NOT_FOUND; |
| } |
| } |
| |
| gceSTATUS |
| _DestroyIntegerId( |
| IN gcsINTEGER_DB_PTR Database, |
| IN gctUINT32 Id |
| ) |
| { |
| spin_lock(&Database->lock); |
| |
| idr_remove(&Database->idr, Id); |
| |
| spin_unlock(&Database->lock); |
| |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| _QueryProcessPageTable( |
| IN gctPOINTER Logical, |
| OUT gctUINT32 * Address |
| ) |
| { |
| spinlock_t *lock; |
| gctUINTPTR_T logical = (gctUINTPTR_T)Logical; |
| pgd_t *pgd; |
| pud_t *pud; |
| pmd_t *pmd; |
| pte_t *pte; |
| |
| if (!current->mm) |
| { |
| return gcvSTATUS_NOT_FOUND; |
| } |
| |
| pgd = pgd_offset(current->mm, logical); |
| if (pgd_none(*pgd) || pgd_bad(*pgd)) |
| { |
| return gcvSTATUS_NOT_FOUND; |
| } |
| |
| pud = pud_offset(pgd, logical); |
| if (pud_none(*pud) || pud_bad(*pud)) |
| { |
| return gcvSTATUS_NOT_FOUND; |
| } |
| |
| pmd = pmd_offset(pud, logical); |
| if (pmd_none(*pmd) || pmd_bad(*pmd)) |
| { |
| return gcvSTATUS_NOT_FOUND; |
| } |
| |
| pte = pte_offset_map_lock(current->mm, pmd, logical, &lock); |
| if (!pte) |
| { |
| return gcvSTATUS_NOT_FOUND; |
| } |
| |
| if (!pte_present(*pte)) |
| { |
| pte_unmap_unlock(pte, lock); |
| return gcvSTATUS_NOT_FOUND; |
| } |
| |
| *Address = (pte_pfn(*pte) << PAGE_SHIFT) | (logical & ~PAGE_MASK); |
| pte_unmap_unlock(pte, lock); |
| |
| return gcvSTATUS_OK; |
| } |
| |
| #if !gcdCACHE_FUNCTION_UNIMPLEMENTED && defined(CONFIG_OUTER_CACHE) |
| static inline gceSTATUS |
| outer_func( |
| gceCACHEOPERATION Type, |
| unsigned long Start, |
| unsigned long End |
| ) |
| { |
| switch (Type) |
| { |
| case gcvCACHE_CLEAN: |
| outer_clean_range(Start, End); |
| break; |
| case gcvCACHE_INVALIDATE: |
| outer_inv_range(Start, End); |
| break; |
| case gcvCACHE_FLUSH: |
| outer_flush_range(Start, End); |
| break; |
| default: |
| return gcvSTATUS_INVALID_ARGUMENT; |
| break; |
| } |
| return gcvSTATUS_OK; |
| } |
| |
| #if gcdENABLE_OUTER_CACHE_PATCH |
| /******************************************************************************* |
| ** _HandleOuterCache |
| ** |
| ** Handle the outer cache for the specified addresses. |
| ** |
| ** ARGUMENTS: |
| ** |
| ** gckOS Os |
| ** Pointer to gckOS object. |
| ** |
| ** gctPOINTER Physical |
| ** Physical address to flush. |
| ** |
| ** gctPOINTER Logical |
| ** Logical address to flush. |
| ** |
| ** gctSIZE_T Bytes |
| ** Size of the address range in bytes to flush. |
| ** |
| ** gceOUTERCACHE_OPERATION Type |
| ** Operation need to be execute. |
| */ |
| gceSTATUS |
| _HandleOuterCache( |
| IN gckOS Os, |
| IN gctUINT32 Physical, |
| IN gctPOINTER Logical, |
| IN gctSIZE_T Bytes, |
| IN gceCACHEOPERATION Type |
| ) |
| { |
| gceSTATUS status; |
| unsigned long paddr; |
| gctPOINTER vaddr; |
| gctUINT32 offset, bytes, left; |
| |
| gcmkHEADER_ARG("Os=0x%X Logical=0x%X Bytes=%lu", |
| Os, Logical, Bytes); |
| |
| if (Physical != gcvINVALID_ADDRESS) |
| { |
| /* Non paged memory or gcvPOOL_USER surface */ |
| paddr = (unsigned long) Physical; |
| gcmkONERROR(outer_func(Type, paddr, paddr + Bytes)); |
| } |
| else |
| { |
| /* Non contiguous virtual memory */ |
| vaddr = Logical; |
| left = Bytes; |
| |
| while (left) |
| { |
| /* Handle (part of) current page. */ |
| offset = (gctUINTPTR_T)vaddr & ~PAGE_MASK; |
| |
| bytes = gcmMIN(left, PAGE_SIZE - offset); |
| |
| gcmkONERROR(_QueryProcessPageTable(vaddr, (gctUINT32*)&paddr)); |
| gcmkONERROR(outer_func(Type, paddr, paddr + bytes)); |
| |
| vaddr = (gctUINT8_PTR)vaddr + bytes; |
| left -= bytes; |
| } |
| } |
| |
| mb(); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| #endif |
| #endif |
| |
| gctBOOL |
| _AllowAccess( |
| IN gckOS Os, |
| IN gceCORE Core, |
| IN gctUINT32 Address |
| ) |
| { |
| gctUINT32 data; |
| |
| /* Check external clock state. */ |
| if (Os->clockStates[Core] == gcvFALSE) |
| { |
| gcmkPRINT("[galcore]: %s(%d) External clock off", __FUNCTION__, __LINE__); |
| return gcvFALSE; |
| } |
| |
| /* Check internal clock state. */ |
| if (Address == 0) |
| { |
| return gcvTRUE; |
| } |
| |
| #if gcdMULTI_GPU |
| if (Core == gcvCORE_MAJOR) |
| { |
| data = readl((gctUINT8 *)Os->device->registerBases[gcvCORE_3D_0_ID] + 0x0); |
| } |
| else |
| #endif |
| { |
| data = readl((gctUINT8 *)Os->device->registerBases[Core] + 0x0); |
| } |
| |
| if ((data & 0x3) == 0x3) |
| { |
| gcmkPRINT("[galcore]: %s(%d) Internal clock off", __FUNCTION__, __LINE__); |
| return gcvFALSE; |
| } |
| |
| return gcvTRUE; |
| } |
| |
| static gceSTATUS |
| _ShrinkMemory( |
| IN gckOS Os |
| ) |
| { |
| gcsPLATFORM * platform; |
| |
| gcmkHEADER_ARG("Os=0x%X", Os); |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| |
| platform = Os->device->platform; |
| |
| if (platform && platform->ops->shrinkMemory) |
| { |
| platform->ops->shrinkMemory(platform); |
| } |
| else |
| { |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_NOT_SUPPORTED; |
| } |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_Construct |
| ** |
| ** Construct a new gckOS object. |
| ** |
| ** INPUT: |
| ** |
| ** gctPOINTER Context |
| ** Pointer to the gckGALDEVICE class. |
| ** |
| ** OUTPUT: |
| ** |
| ** gckOS * Os |
| ** Pointer to a variable that will hold the pointer to the gckOS object. |
| */ |
| gceSTATUS |
| gckOS_Construct( |
| IN gctPOINTER Context, |
| OUT gckOS * Os |
| ) |
| { |
| gckOS os; |
| gceSTATUS status; |
| gctINT i; |
| |
| gcmkHEADER_ARG("Context=0x%X", Context); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_ARGUMENT(Os != gcvNULL); |
| |
| /* Allocate the gckOS object. */ |
| os = (gckOS) kmalloc(gcmSIZEOF(struct _gckOS), GFP_KERNEL | gcdNOWARN); |
| |
| if (os == gcvNULL) |
| { |
| /* Out of memory. */ |
| gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_MEMORY); |
| return gcvSTATUS_OUT_OF_MEMORY; |
| } |
| |
| /* Zero the memory. */ |
| gckOS_ZeroMemory(os, gcmSIZEOF(struct _gckOS)); |
| |
| /* Initialize the gckOS object. */ |
| os->object.type = gcvOBJ_OS; |
| |
| /* Set device device. */ |
| os->device = Context; |
| |
| /* Set allocateCount to 0, gckOS_Allocate has not been used yet. */ |
| atomic_set(&os->allocateCount, 0); |
| |
| /* Initialize the memory lock. */ |
| gcmkONERROR(gckOS_CreateMutex(os, &os->memoryLock)); |
| gcmkONERROR(gckOS_CreateMutex(os, &os->memoryMapLock)); |
| |
| /* Create debug lock mutex. */ |
| gcmkONERROR(gckOS_CreateMutex(os, &os->debugLock)); |
| |
| os->mdlHead = os->mdlTail = gcvNULL; |
| |
| /* Get the kernel process ID. */ |
| gcmkONERROR(gckOS_GetProcessID(&os->kernelProcessID)); |
| |
| /* |
| * Initialize the signal manager. |
| */ |
| |
| /* Initialize mutex. */ |
| gcmkONERROR(gckOS_CreateMutex(os, &os->signalMutex)); |
| |
| /* Initialize signal id database lock. */ |
| spin_lock_init(&os->signalDB.lock); |
| |
| /* Initialize signal id database. */ |
| idr_init(&os->signalDB.idr); |
| |
| #if gcdANDROID_NATIVE_FENCE_SYNC |
| /* |
| * Initialize the sync point manager. |
| */ |
| |
| /* Initialize mutex. */ |
| gcmkONERROR(gckOS_CreateMutex(os, &os->syncPointMutex)); |
| |
| /* Initialize sync point id database lock. */ |
| spin_lock_init(&os->syncPointDB.lock); |
| |
| /* Initialize sync point id database. */ |
| idr_init(&os->syncPointDB.idr); |
| #endif |
| |
| /* Create a workqueue for os timer. */ |
| os->workqueue = create_singlethread_workqueue("galcore workqueue"); |
| |
| if (os->workqueue == gcvNULL) |
| { |
| /* Out of memory. */ |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| os->paddingPage = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | gcdNOWARN); |
| if (os->paddingPage == gcvNULL) |
| { |
| /* Out of memory. */ |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| else |
| { |
| SetPageReserved(os->paddingPage); |
| } |
| |
| for (i = 0; i < gcdMAX_GPU_COUNT; i++) |
| { |
| mutex_init(&os->registerAccessLocks[i]); |
| } |
| |
| gckOS_ImportAllocators(os); |
| |
| #ifdef CONFIG_IOMMU_SUPPORT |
| if (((gckGALDEVICE)(os->device))->mmu == gcvFALSE) |
| { |
| /* Only use IOMMU when internal MMU is not enabled. */ |
| status = gckIOMMU_Construct(os, &os->iommu); |
| |
| if (gcmIS_ERROR(status)) |
| { |
| gcmkTRACE_ZONE( |
| gcvLEVEL_INFO, gcvZONE_OS, |
| "%s(%d): Fail to setup IOMMU", |
| __FUNCTION__, __LINE__ |
| ); |
| } |
| } |
| #endif |
| |
| /* Return pointer to the gckOS object. */ |
| *Os = os; |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Os=0x%X", *Os); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| |
| #if gcdANDROID_NATIVE_FENCE_SYNC |
| if (os->syncPointMutex != gcvNULL) |
| { |
| gcmkVERIFY_OK( |
| gckOS_DeleteMutex(os, os->syncPointMutex)); |
| } |
| #endif |
| |
| if (os->signalMutex != gcvNULL) |
| { |
| gcmkVERIFY_OK( |
| gckOS_DeleteMutex(os, os->signalMutex)); |
| } |
| |
| if (os->memoryMapLock != gcvNULL) |
| { |
| gcmkVERIFY_OK( |
| gckOS_DeleteMutex(os, os->memoryMapLock)); |
| } |
| |
| if (os->memoryLock != gcvNULL) |
| { |
| gcmkVERIFY_OK( |
| gckOS_DeleteMutex(os, os->memoryLock)); |
| } |
| |
| if (os->debugLock != gcvNULL) |
| { |
| gcmkVERIFY_OK( |
| gckOS_DeleteMutex(os, os->debugLock)); |
| } |
| |
| if (os->workqueue != gcvNULL) |
| { |
| destroy_workqueue(os->workqueue); |
| } |
| |
| kfree(os); |
| |
| /* Return the error. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_Destroy |
| ** |
| ** Destroy an gckOS object. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object that needs to be destroyed. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_Destroy( |
| IN gckOS Os |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X", Os); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| |
| if (Os->paddingPage != gcvNULL) |
| { |
| ClearPageReserved(Os->paddingPage); |
| __free_page(Os->paddingPage); |
| Os->paddingPage = gcvNULL; |
| } |
| |
| #if gcdANDROID_NATIVE_FENCE_SYNC |
| /* |
| * Destroy the sync point manager. |
| */ |
| |
| /* Destroy the mutex. */ |
| gcmkVERIFY_OK(gckOS_DeleteMutex(Os, Os->syncPointMutex)); |
| #endif |
| |
| /* |
| * Destroy the signal manager. |
| */ |
| |
| /* Destroy the mutex. */ |
| gcmkVERIFY_OK(gckOS_DeleteMutex(Os, Os->signalMutex)); |
| |
| /* Destroy the memory lock. */ |
| gcmkVERIFY_OK(gckOS_DeleteMutex(Os, Os->memoryMapLock)); |
| gcmkVERIFY_OK(gckOS_DeleteMutex(Os, Os->memoryLock)); |
| |
| /* Destroy debug lock mutex. */ |
| gcmkVERIFY_OK(gckOS_DeleteMutex(Os, Os->debugLock)); |
| |
| /* Wait for all works done. */ |
| flush_workqueue(Os->workqueue); |
| |
| /* Destory work queue. */ |
| destroy_workqueue(Os->workqueue); |
| |
| gckOS_FreeAllocators(Os); |
| |
| #ifdef CONFIG_IOMMU_SUPPORT |
| if (Os->iommu) |
| { |
| gckIOMMU_Destory(Os, Os->iommu); |
| } |
| #endif |
| |
| /* Flush the debug cache. */ |
| gcmkDEBUGFLUSH(~0U); |
| |
| /* Mark the gckOS object as unknown. */ |
| Os->object.type = gcvOBJ_UNKNOWN; |
| |
| |
| /* Free the gckOS object. */ |
| kfree(Os); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_CreateKernelVirtualMapping( |
| IN gckOS Os, |
| IN gctPHYS_ADDR Physical, |
| IN gctSIZE_T Bytes, |
| OUT gctPOINTER * Logical, |
| OUT gctSIZE_T * PageCount |
| ) |
| { |
| gceSTATUS status; |
| PLINUX_MDL mdl = (PLINUX_MDL)Physical; |
| gckALLOCATOR allocator = mdl->allocator; |
| |
| gcmkHEADER(); |
| |
| *PageCount = mdl->numPages; |
| |
| gcmkONERROR(allocator->ops->MapKernel(allocator, mdl, Logical)); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| gceSTATUS |
| gckOS_DestroyKernelVirtualMapping( |
| IN gckOS Os, |
| IN gctPHYS_ADDR Physical, |
| IN gctSIZE_T Bytes, |
| IN gctPOINTER Logical |
| ) |
| { |
| PLINUX_MDL mdl = (PLINUX_MDL)Physical; |
| gckALLOCATOR allocator = mdl->allocator; |
| |
| gcmkHEADER(); |
| |
| allocator->ops->UnmapKernel(allocator, mdl, Logical); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_CreateUserVirtualMapping( |
| IN gckOS Os, |
| IN gctPHYS_ADDR Physical, |
| IN gctSIZE_T Bytes, |
| OUT gctPOINTER * Logical, |
| OUT gctSIZE_T * PageCount |
| ) |
| { |
| return gckOS_LockPages(Os, Physical, Bytes, gcvFALSE, Logical, PageCount); |
| } |
| |
| gceSTATUS |
| gckOS_DestroyUserVirtualMapping( |
| IN gckOS Os, |
| IN gctPHYS_ADDR Physical, |
| IN gctSIZE_T Bytes, |
| IN gctPOINTER Logical |
| ) |
| { |
| return gckOS_UnlockPages(Os, Physical, Bytes, Logical); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_Allocate |
| ** |
| ** Allocate memory. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctSIZE_T Bytes |
| ** Number of bytes to allocate. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctPOINTER * Memory |
| ** Pointer to a variable that will hold the allocated memory location. |
| */ |
| gceSTATUS |
| gckOS_Allocate( |
| IN gckOS Os, |
| IN gctSIZE_T Bytes, |
| OUT gctPOINTER * Memory |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=0x%X Bytes=%lu", Os, Bytes); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| gcmkVERIFY_ARGUMENT(Memory != gcvNULL); |
| |
| gcmkONERROR(gckOS_AllocateMemory(Os, Bytes, Memory)); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Memory=0x%X", *Memory); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_Free |
| ** |
| ** Free allocated memory. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPOINTER Memory |
| ** Pointer to memory allocation to free. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_Free( |
| IN gckOS Os, |
| IN gctPOINTER Memory |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=0x%X Memory=0x%X", Os, Memory); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Memory != gcvNULL); |
| |
| gcmkONERROR(gckOS_FreeMemory(Os, Memory)); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_AllocateMemory |
| ** |
| ** Allocate memory wrapper. |
| ** |
| ** INPUT: |
| ** |
| ** gctSIZE_T Bytes |
| ** Number of bytes to allocate. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctPOINTER * Memory |
| ** Pointer to a variable that will hold the allocated memory location. |
| */ |
| gceSTATUS |
| gckOS_AllocateMemory( |
| IN gckOS Os, |
| IN gctSIZE_T Bytes, |
| OUT gctPOINTER * Memory |
| ) |
| { |
| gctPOINTER memory; |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=0x%X Bytes=%lu", Os, Bytes); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| gcmkVERIFY_ARGUMENT(Memory != gcvNULL); |
| |
| if (Bytes > PAGE_SIZE) |
| { |
| memory = (gctPOINTER) vmalloc(Bytes); |
| } |
| else |
| { |
| memory = (gctPOINTER) kmalloc(Bytes, GFP_KERNEL | gcdNOWARN); |
| } |
| |
| if (memory == gcvNULL) |
| { |
| /* Out of memory. */ |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| /* Increase count. */ |
| atomic_inc(&Os->allocateCount); |
| |
| /* Return pointer to the memory allocation. */ |
| *Memory = memory; |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Memory=0x%X", *Memory); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_FreeMemory |
| ** |
| ** Free allocated memory wrapper. |
| ** |
| ** INPUT: |
| ** |
| ** gctPOINTER Memory |
| ** Pointer to memory allocation to free. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_FreeMemory( |
| IN gckOS Os, |
| IN gctPOINTER Memory |
| ) |
| { |
| gcmkHEADER_ARG("Memory=0x%X", Memory); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_ARGUMENT(Memory != gcvNULL); |
| |
| /* Free the memory from the OS pool. */ |
| if (is_vmalloc_addr(Memory)) |
| { |
| vfree(Memory); |
| } |
| else |
| { |
| kfree(Memory); |
| } |
| |
| /* Decrease count. */ |
| atomic_dec(&Os->allocateCount); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_MapMemory |
| ** |
| ** Map physical memory into the current process. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPHYS_ADDR Physical |
| ** Start of physical address memory. |
| ** |
| ** gctSIZE_T Bytes |
| ** Number of bytes to map. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctPOINTER * Memory |
| ** Pointer to a variable that will hold the logical address of the |
| ** mapped memory. |
| */ |
| gceSTATUS |
| gckOS_MapMemory( |
| IN gckOS Os, |
| IN gctPHYS_ADDR Physical, |
| IN gctSIZE_T Bytes, |
| OUT gctPOINTER * Logical |
| ) |
| { |
| PLINUX_MDL_MAP mdlMap; |
| PLINUX_MDL mdl = (PLINUX_MDL)Physical; |
| |
| gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu", Os, Physical, Bytes); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Physical != 0); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| gcmkVERIFY_ARGUMENT(Logical != gcvNULL); |
| |
| MEMORY_LOCK(Os); |
| |
| mdlMap = FindMdlMap(mdl, _GetProcessID()); |
| |
| if (mdlMap == gcvNULL) |
| { |
| mdlMap = _CreateMdlMap(mdl, _GetProcessID()); |
| |
| if (mdlMap == gcvNULL) |
| { |
| MEMORY_UNLOCK(Os); |
| |
| gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_MEMORY); |
| return gcvSTATUS_OUT_OF_MEMORY; |
| } |
| } |
| |
| if (mdlMap->vmaAddr == gcvNULL) |
| { |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) |
| mdlMap->vmaAddr = (char *)vm_mmap(gcvNULL, |
| 0L, |
| mdl->numPages * PAGE_SIZE, |
| PROT_READ | PROT_WRITE, |
| MAP_SHARED, |
| 0); |
| #else |
| down_write(¤t->mm->mmap_sem); |
| |
| mdlMap->vmaAddr = (char *)do_mmap_pgoff(gcvNULL, |
| 0L, |
| mdl->numPages * PAGE_SIZE, |
| PROT_READ | PROT_WRITE, |
| MAP_SHARED, |
| 0); |
| |
| up_write(¤t->mm->mmap_sem); |
| #endif |
| |
| if (IS_ERR(mdlMap->vmaAddr)) |
| { |
| gcmkTRACE( |
| gcvLEVEL_ERROR, |
| "%s(%d): do_mmap_pgoff error", |
| __FUNCTION__, __LINE__ |
| ); |
| |
| gcmkTRACE( |
| gcvLEVEL_ERROR, |
| "%s(%d): mdl->numPages: %d mdl->vmaAddr: 0x%X", |
| __FUNCTION__, __LINE__, |
| mdl->numPages, |
| mdlMap->vmaAddr |
| ); |
| |
| mdlMap->vmaAddr = gcvNULL; |
| |
| MEMORY_UNLOCK(Os); |
| |
| gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_MEMORY); |
| return gcvSTATUS_OUT_OF_MEMORY; |
| } |
| |
| down_write(¤t->mm->mmap_sem); |
| |
| mdlMap->vma = find_vma(current->mm, (unsigned long)mdlMap->vmaAddr); |
| |
| if (!mdlMap->vma) |
| { |
| gcmkTRACE( |
| gcvLEVEL_ERROR, |
| "%s(%d): find_vma error.", |
| __FUNCTION__, __LINE__ |
| ); |
| |
| mdlMap->vmaAddr = gcvNULL; |
| |
| up_write(¤t->mm->mmap_sem); |
| |
| MEMORY_UNLOCK(Os); |
| |
| gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_RESOURCES); |
| return gcvSTATUS_OUT_OF_RESOURCES; |
| } |
| |
| #ifndef NO_DMA_COHERENT |
| if (dma_mmap_writecombine(gcvNULL, |
| mdlMap->vma, |
| mdl->addr, |
| mdl->dmaHandle, |
| mdl->numPages * PAGE_SIZE) < 0) |
| { |
| up_write(¤t->mm->mmap_sem); |
| |
| gcmkTRACE( |
| gcvLEVEL_ERROR, |
| "%s(%d): dma_mmap_coherent error.", |
| __FUNCTION__, __LINE__ |
| ); |
| |
| mdlMap->vmaAddr = gcvNULL; |
| |
| MEMORY_UNLOCK(Os); |
| |
| gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_RESOURCES); |
| return gcvSTATUS_OUT_OF_RESOURCES; |
| } |
| #else |
| #if !gcdPAGED_MEMORY_CACHEABLE |
| mdlMap->vma->vm_page_prot = gcmkPAGED_MEMROY_PROT(mdlMap->vma->vm_page_prot); |
| mdlMap->vma->vm_flags |= gcdVM_FLAGS; |
| # endif |
| mdlMap->vma->vm_pgoff = 0; |
| |
| if (remap_pfn_range(mdlMap->vma, |
| mdlMap->vma->vm_start, |
| mdl->dmaHandle >> PAGE_SHIFT, |
| mdl->numPages*PAGE_SIZE, |
| mdlMap->vma->vm_page_prot) < 0) |
| { |
| up_write(¤t->mm->mmap_sem); |
| |
| gcmkTRACE( |
| gcvLEVEL_ERROR, |
| "%s(%d): remap_pfn_range error.", |
| __FUNCTION__, __LINE__ |
| ); |
| |
| mdlMap->vmaAddr = gcvNULL; |
| |
| MEMORY_UNLOCK(Os); |
| |
| gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_RESOURCES); |
| return gcvSTATUS_OUT_OF_RESOURCES; |
| } |
| #endif |
| |
| up_write(¤t->mm->mmap_sem); |
| } |
| |
| MEMORY_UNLOCK(Os); |
| |
| *Logical = mdlMap->vmaAddr; |
| |
| gcmkFOOTER_ARG("*Logical=0x%X", *Logical); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_UnmapMemory |
| ** |
| ** Unmap physical memory out of the current process. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPHYS_ADDR Physical |
| ** Start of physical address memory. |
| ** |
| ** gctSIZE_T Bytes |
| ** Number of bytes to unmap. |
| ** |
| ** gctPOINTER Memory |
| ** Pointer to a previously mapped memory region. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_UnmapMemory( |
| IN gckOS Os, |
| IN gctPHYS_ADDR Physical, |
| IN gctSIZE_T Bytes, |
| IN gctPOINTER Logical |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu Logical=0x%X", |
| Os, Physical, Bytes, Logical); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Physical != 0); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| gcmkVERIFY_ARGUMENT(Logical != gcvNULL); |
| |
| gckOS_UnmapMemoryEx(Os, Physical, Bytes, Logical, _GetProcessID()); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_UnmapMemoryEx |
| ** |
| ** Unmap physical memory in the specified process. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPHYS_ADDR Physical |
| ** Start of physical address memory. |
| ** |
| ** gctSIZE_T Bytes |
| ** Number of bytes to unmap. |
| ** |
| ** gctPOINTER Memory |
| ** Pointer to a previously mapped memory region. |
| ** |
| ** gctUINT32 PID |
| ** Pid of the process that opened the device and mapped this memory. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_UnmapMemoryEx( |
| IN gckOS Os, |
| IN gctPHYS_ADDR Physical, |
| IN gctSIZE_T Bytes, |
| IN gctPOINTER Logical, |
| IN gctUINT32 PID |
| ) |
| { |
| PLINUX_MDL_MAP mdlMap; |
| PLINUX_MDL mdl = (PLINUX_MDL)Physical; |
| |
| gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu Logical=0x%X PID=%d", |
| Os, Physical, Bytes, Logical, PID); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Physical != 0); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| gcmkVERIFY_ARGUMENT(Logical != gcvNULL); |
| gcmkVERIFY_ARGUMENT(PID != 0); |
| |
| MEMORY_LOCK(Os); |
| |
| if (Logical) |
| { |
| mdlMap = FindMdlMap(mdl, PID); |
| |
| if (mdlMap == gcvNULL || mdlMap->vmaAddr == gcvNULL) |
| { |
| MEMORY_UNLOCK(Os); |
| |
| gcmkFOOTER_ARG("status=%d", gcvSTATUS_INVALID_ARGUMENT); |
| return gcvSTATUS_INVALID_ARGUMENT; |
| } |
| |
| _UnmapUserLogical(mdlMap->vmaAddr, mdl->numPages * PAGE_SIZE); |
| |
| gcmkVERIFY_OK(_DestroyMdlMap(mdl, mdlMap)); |
| } |
| |
| MEMORY_UNLOCK(Os); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_UnmapUserLogical |
| ** |
| ** Unmap user logical memory out of physical memory. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPHYS_ADDR Physical |
| ** Start of physical address memory. |
| ** |
| ** gctSIZE_T Bytes |
| ** Number of bytes to unmap. |
| ** |
| ** gctPOINTER Memory |
| ** Pointer to a previously mapped memory region. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_UnmapUserLogical( |
| IN gckOS Os, |
| IN gctPHYS_ADDR Physical, |
| IN gctSIZE_T Bytes, |
| IN gctPOINTER Logical |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu Logical=0x%X", |
| Os, Physical, Bytes, Logical); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Physical != 0); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| gcmkVERIFY_ARGUMENT(Logical != gcvNULL); |
| |
| gckOS_UnmapMemory(Os, Physical, Bytes, Logical); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_AllocateNonPagedMemory |
| ** |
| ** Allocate a number of pages from non-paged memory. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctBOOL InUserSpace |
| ** gcvTRUE if the pages need to be mapped into user space. |
| ** |
| ** gctSIZE_T * Bytes |
| ** Pointer to a variable that holds the number of bytes to allocate. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctSIZE_T * Bytes |
| ** Pointer to a variable that hold the number of bytes allocated. |
| ** |
| ** gctPHYS_ADDR * Physical |
| ** Pointer to a variable that will hold the physical address of the |
| ** allocation. |
| ** |
| ** gctPOINTER * Logical |
| ** Pointer to a variable that will hold the logical address of the |
| ** allocation. |
| */ |
| gceSTATUS |
| gckOS_AllocateNonPagedMemory( |
| IN gckOS Os, |
| IN gctBOOL InUserSpace, |
| IN OUT gctSIZE_T * Bytes, |
| OUT gctPHYS_ADDR * Physical, |
| OUT gctPOINTER * Logical |
| ) |
| { |
| gctSIZE_T bytes; |
| gctINT numPages; |
| PLINUX_MDL mdl = gcvNULL; |
| PLINUX_MDL_MAP mdlMap = gcvNULL; |
| gctSTRING addr; |
| gckKERNEL kernel; |
| #ifdef NO_DMA_COHERENT |
| struct page * page; |
| long size, order; |
| gctPOINTER vaddr; |
| #endif |
| gctBOOL locked = gcvFALSE; |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=0x%X InUserSpace=%d *Bytes=%lu", |
| Os, InUserSpace, gcmOPT_VALUE(Bytes)); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Bytes != gcvNULL); |
| gcmkVERIFY_ARGUMENT(*Bytes > 0); |
| gcmkVERIFY_ARGUMENT(Physical != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Logical != gcvNULL); |
| |
| /* Align number of bytes to page size. */ |
| bytes = gcmALIGN(*Bytes, PAGE_SIZE); |
| |
| /* Get total number of pages.. */ |
| numPages = GetPageCount(bytes, 0); |
| |
| /* Allocate mdl+vector structure */ |
| mdl = _CreateMdl(); |
| if (mdl == gcvNULL) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| mdl->pagedMem = 0; |
| mdl->numPages = numPages; |
| |
| MEMORY_LOCK(Os); |
| locked = gcvTRUE; |
| |
| #ifndef NO_DMA_COHERENT |
| #ifdef CONFIG_ARM64 |
| addr = dma_alloc_coherent(gcvNULL, |
| #else |
| addr = dma_alloc_writecombine(gcvNULL, |
| #endif |
| mdl->numPages * PAGE_SIZE, |
| &mdl->dmaHandle, |
| GFP_KERNEL | gcdNOWARN); |
| #else |
| size = mdl->numPages * PAGE_SIZE; |
| order = get_order(size); |
| |
| page = alloc_pages(GFP_KERNEL | gcdNOWARN, order); |
| |
| if (page == gcvNULL) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| vaddr = (gctPOINTER)page_address(page); |
| mdl->contiguous = gcvTRUE; |
| mdl->u.contiguousPages = page; |
| addr = _CreateKernelVirtualMapping(mdl); |
| mdl->dmaHandle = virt_to_phys(vaddr); |
| mdl->kaddr = vaddr; |
| |
| /* Trigger a page fault. */ |
| memset(addr, 0, numPages * PAGE_SIZE); |
| |
| #if !defined(CONFIG_PPC) |
| /* Cache invalidate. */ |
| dma_sync_single_for_device( |
| gcvNULL, |
| page_to_phys(page), |
| bytes, |
| DMA_FROM_DEVICE); |
| #endif |
| |
| while (size > 0) |
| { |
| SetPageReserved(virt_to_page(vaddr)); |
| |
| vaddr += PAGE_SIZE; |
| size -= PAGE_SIZE; |
| } |
| #endif |
| |
| if (addr == gcvNULL) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| kernel = Os->device->kernels[gcvCORE_MAJOR] != gcvNULL ? |
| Os->device->kernels[gcvCORE_MAJOR] : Os->device->kernels[gcvCORE_2D]; |
| if (((Os->device->baseAddress & 0x80000000) != (mdl->dmaHandle & 0x80000000)) && |
| kernel->hardware->mmuVersion == 0) |
| { |
| mdl->dmaHandle = (mdl->dmaHandle & ~0x80000000) |
| | (Os->device->baseAddress & 0x80000000); |
| } |
| |
| mdl->addr = addr; |
| |
| /* Return allocated memory. */ |
| *Bytes = bytes; |
| *Physical = (gctPHYS_ADDR) mdl; |
| |
| if (InUserSpace) |
| { |
| mdlMap = _CreateMdlMap(mdl, _GetProcessID()); |
| |
| if (mdlMap == gcvNULL) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| /* Only after mmap this will be valid. */ |
| |
| /* We need to map this to user space. */ |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) |
| mdlMap->vmaAddr = (gctSTRING) vm_mmap(gcvNULL, |
| 0L, |
| mdl->numPages * PAGE_SIZE, |
| PROT_READ | PROT_WRITE, |
| MAP_SHARED, |
| 0); |
| #else |
| down_write(¤t->mm->mmap_sem); |
| |
| mdlMap->vmaAddr = (gctSTRING) do_mmap_pgoff(gcvNULL, |
| 0L, |
| mdl->numPages * PAGE_SIZE, |
| PROT_READ | PROT_WRITE, |
| MAP_SHARED, |
| 0); |
| |
| up_write(¤t->mm->mmap_sem); |
| #endif |
| |
| if (IS_ERR(mdlMap->vmaAddr)) |
| { |
| gcmkTRACE_ZONE( |
| gcvLEVEL_WARNING, gcvZONE_OS, |
| "%s(%d): do_mmap_pgoff error", |
| __FUNCTION__, __LINE__ |
| ); |
| |
| mdlMap->vmaAddr = gcvNULL; |
| |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| down_write(¤t->mm->mmap_sem); |
| |
| mdlMap->vma = find_vma(current->mm, (unsigned long)mdlMap->vmaAddr); |
| |
| if (mdlMap->vma == gcvNULL) |
| { |
| gcmkTRACE_ZONE( |
| gcvLEVEL_WARNING, gcvZONE_OS, |
| "%s(%d): find_vma error", |
| __FUNCTION__, __LINE__ |
| ); |
| |
| up_write(¤t->mm->mmap_sem); |
| |
| gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); |
| } |
| |
| #ifndef NO_DMA_COHERENT |
| if (dma_mmap_coherent(gcvNULL, |
| mdlMap->vma, |
| mdl->addr, |
| mdl->dmaHandle, |
| mdl->numPages * PAGE_SIZE) < 0) |
| { |
| gcmkTRACE_ZONE( |
| gcvLEVEL_WARNING, gcvZONE_OS, |
| "%s(%d): dma_mmap_coherent error", |
| __FUNCTION__, __LINE__ |
| ); |
| |
| up_write(¤t->mm->mmap_sem); |
| |
| gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); |
| } |
| #else |
| #if !gcdSECURITY |
| mdlMap->vma->vm_page_prot = gcmkNONPAGED_MEMROY_PROT(mdlMap->vma->vm_page_prot); |
| #endif |
| mdlMap->vma->vm_flags |= gcdVM_FLAGS; |
| mdlMap->vma->vm_pgoff = 0; |
| |
| if (remap_pfn_range(mdlMap->vma, |
| mdlMap->vma->vm_start, |
| mdl->dmaHandle >> PAGE_SHIFT, |
| mdl->numPages * PAGE_SIZE, |
| mdlMap->vma->vm_page_prot)) |
| { |
| gcmkTRACE_ZONE( |
| gcvLEVEL_WARNING, gcvZONE_OS, |
| "%s(%d): remap_pfn_range error", |
| __FUNCTION__, __LINE__ |
| ); |
| |
| up_write(¤t->mm->mmap_sem); |
| |
| gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); |
| } |
| #endif /* NO_DMA_COHERENT */ |
| |
| up_write(¤t->mm->mmap_sem); |
| |
| *Logical = mdlMap->vmaAddr; |
| } |
| else |
| { |
| #if gcdSECURITY |
| *Logical = (gctPOINTER)mdl->kaddr; |
| #else |
| *Logical = (gctPOINTER)mdl->addr; |
| #endif |
| } |
| |
| /* |
| * Add this to a global list. |
| * Will be used by get physical address |
| * and mapuser pointer functions. |
| */ |
| |
| if (!Os->mdlHead) |
| { |
| /* Initialize the queue. */ |
| Os->mdlHead = Os->mdlTail = mdl; |
| } |
| else |
| { |
| /* Add to the tail. */ |
| mdl->prev = Os->mdlTail; |
| Os->mdlTail->next = mdl; |
| Os->mdlTail = mdl; |
| } |
| |
| MEMORY_UNLOCK(Os); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Bytes=%lu *Physical=0x%X *Logical=0x%X", |
| *Bytes, *Physical, *Logical); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (mdlMap != gcvNULL) |
| { |
| /* Free LINUX_MDL_MAP. */ |
| gcmkVERIFY_OK(_DestroyMdlMap(mdl, mdlMap)); |
| } |
| |
| if (mdl != gcvNULL) |
| { |
| /* Free LINUX_MDL. */ |
| gcmkVERIFY_OK(_DestroyMdl(mdl)); |
| } |
| *Physical = gcvNULL; |
| *Bytes = 0; |
| |
| if (locked) |
| { |
| /* Unlock memory. */ |
| MEMORY_UNLOCK(Os); |
| } |
| |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_FreeNonPagedMemory |
| ** |
| ** Free previously allocated and mapped pages from non-paged memory. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctSIZE_T Bytes |
| ** Number of bytes allocated. |
| ** |
| ** gctPHYS_ADDR Physical |
| ** Physical address of the allocated memory. |
| ** |
| ** gctPOINTER Logical |
| ** Logical address of the allocated memory. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS gckOS_FreeNonPagedMemory( |
| IN gckOS Os, |
| IN gctSIZE_T Bytes, |
| IN gctPHYS_ADDR Physical, |
| IN gctPOINTER Logical |
| ) |
| { |
| PLINUX_MDL mdl; |
| PLINUX_MDL_MAP mdlMap; |
| #ifdef NO_DMA_COHERENT |
| unsigned size; |
| gctPOINTER vaddr; |
| #endif /* NO_DMA_COHERENT */ |
| |
| gcmkHEADER_ARG("Os=0x%X Bytes=%lu Physical=0x%X Logical=0x%X", |
| Os, Bytes, Physical, Logical); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| gcmkVERIFY_ARGUMENT(Physical != 0); |
| gcmkVERIFY_ARGUMENT(Logical != gcvNULL); |
| |
| /* Convert physical address into a pointer to a MDL. */ |
| mdl = (PLINUX_MDL) Physical; |
| |
| MEMORY_LOCK(Os); |
| |
| #ifndef NO_DMA_COHERENT |
| #ifdef CONFIG_ARM64 |
| dma_free_coherent(gcvNULL, |
| #else |
| dma_free_writecombine(gcvNULL, |
| #endif |
| mdl->numPages * PAGE_SIZE, |
| mdl->addr, |
| mdl->dmaHandle); |
| #else |
| size = mdl->numPages * PAGE_SIZE; |
| vaddr = mdl->kaddr; |
| |
| while (size > 0) |
| { |
| ClearPageReserved(virt_to_page(vaddr)); |
| |
| vaddr += PAGE_SIZE; |
| size -= PAGE_SIZE; |
| } |
| |
| free_pages((unsigned long)mdl->kaddr, get_order(mdl->numPages * PAGE_SIZE)); |
| |
| _DestoryKernelVirtualMapping(mdl->addr); |
| #endif /* NO_DMA_COHERENT */ |
| |
| mdlMap = mdl->maps; |
| |
| while (mdlMap != gcvNULL) |
| { |
| /* No mapped memory exists when free nonpaged memory */ |
| gcmkASSERT(mdlMap->vmaAddr == gcvNULL); |
| |
| mdlMap = mdlMap->next; |
| } |
| |
| /* Remove the node from global list.. */ |
| if (mdl == Os->mdlHead) |
| { |
| if ((Os->mdlHead = mdl->next) == gcvNULL) |
| { |
| Os->mdlTail = gcvNULL; |
| } |
| } |
| else |
| { |
| mdl->prev->next = mdl->next; |
| if (mdl == Os->mdlTail) |
| { |
| Os->mdlTail = mdl->prev; |
| } |
| else |
| { |
| mdl->next->prev = mdl->prev; |
| } |
| } |
| |
| MEMORY_UNLOCK(Os); |
| |
| gcmkVERIFY_OK(_DestroyMdl(mdl)); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_ReadRegister |
| ** |
| ** Read data from a register. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctUINT32 Address |
| ** Address of register. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctUINT32 * Data |
| ** Pointer to a variable that receives the data read from the register. |
| */ |
| gceSTATUS |
| gckOS_ReadRegister( |
| IN gckOS Os, |
| IN gctUINT32 Address, |
| OUT gctUINT32 * Data |
| ) |
| { |
| return gckOS_ReadRegisterEx(Os, gcvCORE_MAJOR, Address, Data); |
| } |
| |
| gceSTATUS |
| gckOS_ReadRegisterEx( |
| IN gckOS Os, |
| IN gceCORE Core, |
| IN gctUINT32 Address, |
| OUT gctUINT32 * Data |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Core=%d Address=0x%X", Os, Core, Address); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| #if !gcdMULTI_GPU |
| gcmkVERIFY_ARGUMENT(Address < Os->device->requestedRegisterMemSizes[Core]); |
| #endif |
| gcmkVERIFY_ARGUMENT(Data != gcvNULL); |
| |
| if (!in_interrupt()) |
| { |
| mutex_lock(&Os->registerAccessLocks[Core]); |
| } |
| |
| BUG_ON(!_AllowAccess(Os, Core, Address)); |
| |
| #if gcdMULTI_GPU |
| if (Core == gcvCORE_MAJOR) |
| { |
| *Data = readl((gctUINT8 *)Os->device->registerBase3D[gcvCORE_3D_0_ID] + Address); |
| } |
| else |
| #endif |
| { |
| *Data = readl((gctUINT8 *)Os->device->registerBases[Core] + Address); |
| } |
| |
| if (!in_interrupt()) |
| { |
| mutex_unlock(&Os->registerAccessLocks[Core]); |
| } |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Data=0x%08x", *Data); |
| return gcvSTATUS_OK; |
| } |
| |
| #if gcdMULTI_GPU |
| gceSTATUS |
| gckOS_ReadRegisterByCoreId( |
| IN gckOS Os, |
| IN gceCORE Core, |
| IN gctUINT32 CoreId, |
| IN gctUINT32 Address, |
| OUT gctUINT32 * Data |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Core=%d CoreId=%d Address=0x%X", |
| Os, Core, CoreId, Address); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Data != gcvNULL); |
| |
| *Data = readl((gctUINT8 *)Os->device->registerBase3D[CoreId] + Address); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Data=0x%08x", *Data); |
| return gcvSTATUS_OK; |
| } |
| #endif |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_WriteRegister |
| ** |
| ** Write data to a register. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctUINT32 Address |
| ** Address of register. |
| ** |
| ** gctUINT32 Data |
| ** Data for register. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_WriteRegister( |
| IN gckOS Os, |
| IN gctUINT32 Address, |
| IN gctUINT32 Data |
| ) |
| { |
| return gckOS_WriteRegisterEx(Os, gcvCORE_MAJOR, Address, Data); |
| } |
| |
| gceSTATUS |
| gckOS_WriteRegisterEx( |
| IN gckOS Os, |
| IN gceCORE Core, |
| IN gctUINT32 Address, |
| IN gctUINT32 Data |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Core=%d Address=0x%X Data=0x%08x", Os, Core, Address, Data); |
| |
| #if !gcdMULTI_GPU |
| gcmkVERIFY_ARGUMENT(Address < Os->device->requestedRegisterMemSizes[Core]); |
| #endif |
| |
| if (!in_interrupt()) |
| { |
| mutex_lock(&Os->registerAccessLocks[Core]); |
| } |
| |
| BUG_ON(!_AllowAccess(Os, Core, Address)); |
| |
| #if gcdMULTI_GPU |
| if (Core == gcvCORE_MAJOR) |
| { |
| writel(Data, (gctUINT8 *)Os->device->registerBase3D[gcvCORE_3D_0_ID] + Address); |
| #if gcdMULTI_GPU > 1 |
| writel(Data, (gctUINT8 *)Os->device->registerBase3D[gcvCORE_3D_1_ID] + Address); |
| #endif |
| } |
| else |
| #endif |
| { |
| writel(Data, (gctUINT8 *)Os->device->registerBases[Core] + Address); |
| } |
| |
| if (!in_interrupt()) |
| { |
| mutex_unlock(&Os->registerAccessLocks[Core]); |
| } |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| #if gcdMULTI_GPU |
| gceSTATUS |
| gckOS_WriteRegisterByCoreId( |
| IN gckOS Os, |
| IN gceCORE Core, |
| IN gctUINT32 CoreId, |
| IN gctUINT32 Address, |
| IN gctUINT32 Data |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Core=%d CoreId=%d Address=0x%X Data=0x%08x", |
| Os, Core, CoreId, Address, Data); |
| |
| writel(Data, (gctUINT8 *)Os->device->registerBase3D[CoreId] + Address); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| #endif |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_GetPageSize |
| ** |
| ** Get the system's page size. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctSIZE_T * PageSize |
| ** Pointer to a variable that will receive the system's page size. |
| */ |
| gceSTATUS gckOS_GetPageSize( |
| IN gckOS Os, |
| OUT gctSIZE_T * PageSize |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X", Os); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(PageSize != gcvNULL); |
| |
| /* Return the page size. */ |
| *PageSize = (gctSIZE_T) PAGE_SIZE; |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*PageSize", *PageSize); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_GetPhysicalAddress |
| ** |
| ** Get the physical system address of a corresponding virtual address. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPOINTER Logical |
| ** Logical address. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctUINT32 * Address |
| ** Poinetr to a variable that receives the 32-bit physical adress. |
| */ |
| gceSTATUS |
| gckOS_GetPhysicalAddress( |
| IN gckOS Os, |
| IN gctPOINTER Logical, |
| OUT gctUINT32 * Address |
| ) |
| { |
| gceSTATUS status; |
| gctUINT32 processID; |
| |
| gcmkHEADER_ARG("Os=0x%X Logical=0x%X", Os, Logical); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Address != gcvNULL); |
| |
| /* Query page table of current process first. */ |
| status = _QueryProcessPageTable(Logical, Address); |
| |
| if (gcmIS_ERROR(status)) |
| { |
| /* Get current process ID. */ |
| processID = _GetProcessID(); |
| |
| /* Route through other function. */ |
| gcmkONERROR( |
| gckOS_GetPhysicalAddressProcess(Os, Logical, processID, Address)); |
| } |
| |
| gcmkVERIFY_OK(gckOS_CPUPhysicalToGPUPhysical(Os, *Address, Address)); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Address=0x%08x", *Address); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_UserLogicalToPhysical |
| ** |
| ** Get the physical system address of a corresponding user virtual address. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPOINTER Logical |
| ** Logical address. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctUINT32 * Address |
| ** Pointer to a variable that receives the 32-bit physical address. |
| */ |
| gceSTATUS gckOS_UserLogicalToPhysical( |
| IN gckOS Os, |
| IN gctPOINTER Logical, |
| OUT gctUINT32 * Address |
| ) |
| { |
| return gckOS_GetPhysicalAddress(Os, Logical, Address); |
| } |
| |
| #if gcdSECURE_USER |
| static gceSTATUS |
| gckOS_AddMapping( |
| IN gckOS Os, |
| IN gctUINT32 Physical, |
| IN gctPOINTER Logical, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| gceSTATUS status; |
| gcsUSER_MAPPING_PTR map; |
| |
| gcmkHEADER_ARG("Os=0x%X Physical=0x%X Logical=0x%X Bytes=%lu", |
| Os, Physical, Logical, Bytes); |
| |
| gcmkONERROR(gckOS_Allocate(Os, |
| gcmSIZEOF(gcsUSER_MAPPING), |
| (gctPOINTER *) &map)); |
| |
| map->next = Os->userMap; |
| map->physical = Physical - Os->device->baseAddress; |
| map->logical = Logical; |
| map->bytes = Bytes; |
| map->start = (gctINT8_PTR) Logical; |
| map->end = map->start + Bytes; |
| |
| Os->userMap = map; |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| static gceSTATUS |
| gckOS_RemoveMapping( |
| IN gckOS Os, |
| IN gctPOINTER Logical, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| gceSTATUS status; |
| gcsUSER_MAPPING_PTR map, prev; |
| |
| gcmkHEADER_ARG("Os=0x%X Logical=0x%X Bytes=%lu", Os, Logical, Bytes); |
| |
| for (map = Os->userMap, prev = gcvNULL; map != gcvNULL; map = map->next) |
| { |
| if ((map->logical == Logical) |
| && (map->bytes == Bytes) |
| ) |
| { |
| break; |
| } |
| |
| prev = map; |
| } |
| |
| if (map == gcvNULL) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ADDRESS); |
| } |
| |
| if (prev == gcvNULL) |
| { |
| Os->userMap = map->next; |
| } |
| else |
| { |
| prev->next = map->next; |
| } |
| |
| gcmkONERROR(gcmkOS_SAFE_FREE(Os, map)); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| #endif |
| |
| gceSTATUS |
| _ConvertLogical2Physical( |
| IN gckOS Os, |
| IN gctPOINTER Logical, |
| IN gctUINT32 ProcessID, |
| IN PLINUX_MDL Mdl, |
| OUT gctUINT32_PTR Physical |
| ) |
| { |
| gctINT8_PTR base, vBase; |
| gctUINT32 offset; |
| PLINUX_MDL_MAP map; |
| gcsUSER_MAPPING_PTR userMap; |
| |
| #if gcdSECURITY |
| base = (Mdl == gcvNULL) ? gcvNULL : (gctINT8_PTR) Mdl->kaddr; |
| #else |
| base = (Mdl == gcvNULL) ? gcvNULL : (gctINT8_PTR) Mdl->addr; |
| #endif |
| |
| /* Check for the logical address match. */ |
| if ((base != gcvNULL) |
| && ((gctINT8_PTR) Logical >= base) |
| && ((gctINT8_PTR) Logical < base + Mdl->numPages * PAGE_SIZE) |
| ) |
| { |
| offset = (gctINT8_PTR) Logical - base; |
| |
| if (Mdl->dmaHandle != 0) |
| { |
| /* The memory was from coherent area. */ |
| *Physical = (gctUINT32) Mdl->dmaHandle + offset; |
| } |
| else if (Mdl->pagedMem && !Mdl->contiguous) |
| { |
| /* paged memory is not mapped to kernel space. */ |
| return gcvSTATUS_INVALID_ADDRESS; |
| } |
| else |
| { |
| *Physical = gcmPTR2INT32(virt_to_phys(base)) + offset; |
| } |
| |
| return gcvSTATUS_OK; |
| } |
| |
| /* Walk user maps. */ |
| for (userMap = Os->userMap; userMap != gcvNULL; userMap = userMap->next) |
| { |
| if (((gctINT8_PTR) Logical >= userMap->start) |
| && ((gctINT8_PTR) Logical < userMap->end) |
| ) |
| { |
| *Physical = userMap->physical |
| + (gctUINT32) ((gctINT8_PTR) Logical - userMap->start); |
| |
| return gcvSTATUS_OK; |
| } |
| } |
| |
| if (ProcessID != Os->kernelProcessID) |
| { |
| map = FindMdlMap(Mdl, (gctINT) ProcessID); |
| vBase = (map == gcvNULL) ? gcvNULL : (gctINT8_PTR) map->vmaAddr; |
| |
| /* Is the given address within that range. */ |
| if ((vBase != gcvNULL) |
| && ((gctINT8_PTR) Logical >= vBase) |
| && ((gctINT8_PTR) Logical < vBase + Mdl->numPages * PAGE_SIZE) |
| ) |
| { |
| offset = (gctINT8_PTR) Logical - vBase; |
| |
| if (Mdl->dmaHandle != 0) |
| { |
| /* The memory was from coherent area. */ |
| *Physical = (gctUINT32) Mdl->dmaHandle + offset; |
| } |
| else if (Mdl->pagedMem && !Mdl->contiguous) |
| { |
| *Physical = _NonContiguousToPhys(Mdl->u.nonContiguousPages, offset/PAGE_SIZE); |
| } |
| else |
| { |
| *Physical = page_to_phys(Mdl->u.contiguousPages) + offset; |
| } |
| |
| return gcvSTATUS_OK; |
| } |
| } |
| |
| /* Address not yet found. */ |
| return gcvSTATUS_INVALID_ADDRESS; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_GetPhysicalAddressProcess |
| ** |
| ** Get the physical system address of a corresponding virtual address for a |
| ** given process. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to gckOS object. |
| ** |
| ** gctPOINTER Logical |
| ** Logical address. |
| ** |
| ** gctUINT32 ProcessID |
| ** Process ID. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctUINT32 * Address |
| ** Poinetr to a variable that receives the 32-bit physical adress. |
| */ |
| gceSTATUS |
| gckOS_GetPhysicalAddressProcess( |
| IN gckOS Os, |
| IN gctPOINTER Logical, |
| IN gctUINT32 ProcessID, |
| OUT gctUINT32 * Address |
| ) |
| { |
| PLINUX_MDL mdl; |
| gctINT8_PTR base; |
| gckALLOCATOR allocator = gcvNULL; |
| gceSTATUS status = gcvSTATUS_INVALID_ADDRESS; |
| |
| gcmkHEADER_ARG("Os=0x%X Logical=0x%X ProcessID=%d", Os, Logical, ProcessID); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Address != gcvNULL); |
| |
| MEMORY_LOCK(Os); |
| |
| /* First try the contiguous memory pool. */ |
| if (Os->device->contiguousMapped) |
| { |
| base = (gctINT8_PTR) Os->device->contiguousBase; |
| |
| if (((gctINT8_PTR) Logical >= base) |
| && ((gctINT8_PTR) Logical < base + Os->device->contiguousSize) |
| ) |
| { |
| /* Convert logical address into physical. */ |
| *Address = Os->device->contiguousVidMem->baseAddress |
| + (gctINT8_PTR) Logical - base; |
| status = gcvSTATUS_OK; |
| } |
| } |
| else |
| { |
| /* Try the contiguous memory pool. */ |
| mdl = (PLINUX_MDL) Os->device->contiguousPhysical; |
| status = _ConvertLogical2Physical(Os, |
| Logical, |
| ProcessID, |
| mdl, |
| Address); |
| } |
| |
| if (gcmIS_ERROR(status)) |
| { |
| /* Walk all MDLs. */ |
| for (mdl = Os->mdlHead; mdl != gcvNULL; mdl = mdl->next) |
| { |
| /* Try this MDL. */ |
| allocator = mdl->allocator; |
| |
| if (allocator) |
| { |
| status = allocator->ops->LogicalToPhysical( |
| allocator, |
| mdl, |
| Logical, |
| ProcessID, |
| Address |
| ); |
| } |
| else |
| { |
| status = _ConvertLogical2Physical(Os, |
| Logical, |
| ProcessID, |
| mdl, |
| Address); |
| } |
| |
| if (gcmIS_SUCCESS(status)) |
| { |
| break; |
| } |
| } |
| } |
| |
| MEMORY_UNLOCK(Os); |
| |
| gcmkONERROR(status); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Address=0x%08x", *Address); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_MapPhysical |
| ** |
| ** Map a physical address into kernel space. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctUINT32 Physical |
| ** Physical address of the memory to map. |
| ** |
| ** gctSIZE_T Bytes |
| ** Number of bytes to map. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctPOINTER * Logical |
| ** Pointer to a variable that receives the base address of the mapped |
| ** memory. |
| */ |
| gceSTATUS |
| gckOS_MapPhysical( |
| IN gckOS Os, |
| IN gctUINT32 Physical, |
| IN gctSIZE_T Bytes, |
| OUT gctPOINTER * Logical |
| ) |
| { |
| gctPOINTER logical; |
| PLINUX_MDL mdl; |
| gctUINT32 physical = Physical; |
| |
| gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu", Os, Physical, Bytes); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| gcmkVERIFY_ARGUMENT(Logical != gcvNULL); |
| |
| MEMORY_LOCK(Os); |
| |
| /* Go through our mapping to see if we know this physical address already. */ |
| mdl = Os->mdlHead; |
| |
| while (mdl != gcvNULL) |
| { |
| if (mdl->dmaHandle != 0) |
| { |
| if ((physical >= mdl->dmaHandle) |
| && (physical < mdl->dmaHandle + mdl->numPages * PAGE_SIZE) |
| ) |
| { |
| *Logical = mdl->addr + (physical - mdl->dmaHandle); |
| break; |
| } |
| } |
| |
| mdl = mdl->next; |
| } |
| |
| MEMORY_UNLOCK(Os); |
| |
| if (mdl == gcvNULL) |
| { |
| struct page * page = pfn_to_page(physical >> PAGE_SHIFT); |
| |
| if (pfn_valid(page_to_pfn(page))) |
| { |
| gctUINT32 offset = physical & ~PAGE_MASK; |
| struct page ** pages; |
| gctUINT numPages; |
| gctINT i; |
| |
| numPages = GetPageCount(PAGE_ALIGN(offset + Bytes), 0); |
| |
| pages = kmalloc(sizeof(struct page *) * numPages, GFP_KERNEL | gcdNOWARN); |
| |
| if (!pages) |
| { |
| gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_MEMORY); |
| return gcvSTATUS_OUT_OF_MEMORY; |
| } |
| |
| for (i = 0; i < numPages; i++) |
| { |
| pages[i] = nth_page(page, i); |
| } |
| |
| logical = vmap(pages, numPages, 0, gcmkNONPAGED_MEMROY_PROT(PAGE_KERNEL)); |
| |
| kfree(pages); |
| |
| if (logical == gcvNULL) |
| { |
| gcmkTRACE_ZONE( |
| gcvLEVEL_INFO, gcvZONE_OS, |
| "%s(%d): Failed to vmap", |
| __FUNCTION__, __LINE__ |
| ); |
| |
| /* Out of resources. */ |
| gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_RESOURCES); |
| return gcvSTATUS_OUT_OF_RESOURCES; |
| } |
| |
| logical += offset; |
| } |
| else |
| { |
| /* Map memory as cached memory. */ |
| request_mem_region(physical, Bytes, "MapRegion"); |
| logical = (gctPOINTER) ioremap_nocache(physical, Bytes); |
| |
| if (logical == gcvNULL) |
| { |
| gcmkTRACE_ZONE( |
| gcvLEVEL_INFO, gcvZONE_OS, |
| "%s(%d): Failed to ioremap", |
| __FUNCTION__, __LINE__ |
| ); |
| |
| /* Out of resources. */ |
| gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_RESOURCES); |
| return gcvSTATUS_OUT_OF_RESOURCES; |
| } |
| } |
| |
| /* Return pointer to mapped memory. */ |
| *Logical = logical; |
| } |
| |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Logical=0x%X", *Logical); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_UnmapPhysical |
| ** |
| ** Unmap a previously mapped memory region from kernel memory. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPOINTER Logical |
| ** Pointer to the base address of the memory to unmap. |
| ** |
| ** gctSIZE_T Bytes |
| ** Number of bytes to unmap. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_UnmapPhysical( |
| IN gckOS Os, |
| IN gctPOINTER Logical, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| PLINUX_MDL mdl; |
| |
| gcmkHEADER_ARG("Os=0x%X Logical=0x%X Bytes=%lu", Os, Logical, Bytes); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Logical != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| |
| MEMORY_LOCK(Os); |
| |
| mdl = Os->mdlHead; |
| |
| while (mdl != gcvNULL) |
| { |
| if (mdl->addr != gcvNULL) |
| { |
| if (Logical >= (gctPOINTER)mdl->addr |
| && Logical < (gctPOINTER)((gctSTRING)mdl->addr + mdl->numPages * PAGE_SIZE)) |
| { |
| break; |
| } |
| } |
| |
| mdl = mdl->next; |
| } |
| |
| if (mdl == gcvNULL) |
| { |
| /* Unmap the memory. */ |
| vunmap((void *)((unsigned long)Logical & PAGE_MASK)); |
| } |
| |
| MEMORY_UNLOCK(Os); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_CreateMutex |
| ** |
| ** Create a new mutex. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctPOINTER * Mutex |
| ** Pointer to a variable that will hold a pointer to the mutex. |
| */ |
| gceSTATUS |
| gckOS_CreateMutex( |
| IN gckOS Os, |
| OUT gctPOINTER * Mutex |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=0x%X", Os); |
| |
| /* Validate the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Mutex != gcvNULL); |
| |
| /* Allocate the mutex structure. */ |
| gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(struct mutex), Mutex)); |
| |
| /* Initialize the mutex. */ |
| mutex_init(*Mutex); |
| |
| /* Return status. */ |
| gcmkFOOTER_ARG("*Mutex=0x%X", *Mutex); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_DeleteMutex |
| ** |
| ** Delete a mutex. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPOINTER Mutex |
| ** Pointer to the mute to be deleted. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_DeleteMutex( |
| IN gckOS Os, |
| IN gctPOINTER Mutex |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=0x%X Mutex=0x%X", Os, Mutex); |
| |
| /* Validate the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Mutex != gcvNULL); |
| |
| /* Destroy the mutex. */ |
| mutex_destroy((struct mutex *)Mutex); |
| |
| /* Free the mutex structure. */ |
| gcmkONERROR(gckOS_Free(Os, Mutex)); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_AcquireMutex |
| ** |
| ** Acquire a mutex. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPOINTER Mutex |
| ** Pointer to the mutex to be acquired. |
| ** |
| ** gctUINT32 Timeout |
| ** Timeout value specified in milliseconds. |
| ** Specify the value of gcvINFINITE to keep the thread suspended |
| ** until the mutex has been acquired. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_AcquireMutex( |
| IN gckOS Os, |
| IN gctPOINTER Mutex, |
| IN gctUINT32 Timeout |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Mutex=0x%0x Timeout=%u", Os, Mutex, Timeout); |
| |
| /* Validate the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Mutex != gcvNULL); |
| |
| if (Timeout == gcvINFINITE) |
| { |
| /* Lock the mutex. */ |
| mutex_lock(Mutex); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| for (;;) |
| { |
| /* Try to acquire the mutex. */ |
| if (mutex_trylock(Mutex)) |
| { |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| if (Timeout-- == 0) |
| { |
| break; |
| } |
| |
| /* Wait for 1 millisecond. */ |
| gcmkVERIFY_OK(gckOS_Delay(Os, 1)); |
| } |
| |
| /* Timeout. */ |
| gcmkFOOTER_ARG("status=%d", gcvSTATUS_TIMEOUT); |
| return gcvSTATUS_TIMEOUT; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_ReleaseMutex |
| ** |
| ** Release an acquired mutex. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPOINTER Mutex |
| ** Pointer to the mutex to be released. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_ReleaseMutex( |
| IN gckOS Os, |
| IN gctPOINTER Mutex |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Mutex=0x%0x", Os, Mutex); |
| |
| /* Validate the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Mutex != gcvNULL); |
| |
| /* Release the mutex. */ |
| mutex_unlock(Mutex); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_AtomicExchange |
| ** |
| ** Atomically exchange a pair of 32-bit values. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** IN OUT gctINT32_PTR Target |
| ** Pointer to the 32-bit value to exchange. |
| ** |
| ** IN gctINT32 NewValue |
| ** Specifies a new value for the 32-bit value pointed to by Target. |
| ** |
| ** OUT gctINT32_PTR OldValue |
| ** The old value of the 32-bit value pointed to by Target. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_AtomicExchange( |
| IN gckOS Os, |
| IN OUT gctUINT32_PTR Target, |
| IN gctUINT32 NewValue, |
| OUT gctUINT32_PTR OldValue |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Target=0x%X NewValue=%u", Os, Target, NewValue); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(OldValue != gcvNULL); |
| |
| /* Exchange the pair of 32-bit values. */ |
| *OldValue = (gctUINT32) atomic_xchg((atomic_t *) Target, (int) NewValue); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*OldValue=%u", *OldValue); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_AtomicExchangePtr |
| ** |
| ** Atomically exchange a pair of pointers. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** IN OUT gctPOINTER * Target |
| ** Pointer to the 32-bit value to exchange. |
| ** |
| ** IN gctPOINTER NewValue |
| ** Specifies a new value for the pointer pointed to by Target. |
| ** |
| ** OUT gctPOINTER * OldValue |
| ** The old value of the pointer pointed to by Target. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_AtomicExchangePtr( |
| IN gckOS Os, |
| IN OUT gctPOINTER * Target, |
| IN gctPOINTER NewValue, |
| OUT gctPOINTER * OldValue |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Target=0x%X NewValue=0x%X", Os, Target, NewValue); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(OldValue != gcvNULL); |
| |
| /* Exchange the pair of pointers. */ |
| *OldValue = (gctPOINTER)(gctUINTPTR_T) atomic_xchg((atomic_t *) Target, (int)(gctUINTPTR_T) NewValue); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*OldValue=0x%X", *OldValue); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_AtomicSetMask |
| ** |
| ** Atomically set mask to Atom |
| ** |
| ** INPUT: |
| ** IN OUT gctPOINTER Atom |
| ** Pointer to the atom to set. |
| ** |
| ** IN gctUINT32 Mask |
| ** Mask to set. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_AtomSetMask( |
| IN gctPOINTER Atom, |
| IN gctUINT32 Mask |
| ) |
| { |
| gctUINT32 oval, nval; |
| |
| gcmkHEADER_ARG("Atom=0x%0x", Atom); |
| gcmkVERIFY_ARGUMENT(Atom != gcvNULL); |
| |
| do |
| { |
| oval = atomic_read((atomic_t *) Atom); |
| nval = oval | Mask; |
| } while (atomic_cmpxchg((atomic_t *) Atom, oval, nval) != oval); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_AtomClearMask |
| ** |
| ** Atomically clear mask from Atom |
| ** |
| ** INPUT: |
| ** IN OUT gctPOINTER Atom |
| ** Pointer to the atom to clear. |
| ** |
| ** IN gctUINT32 Mask |
| ** Mask to clear. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_AtomClearMask( |
| IN gctPOINTER Atom, |
| IN gctUINT32 Mask |
| ) |
| { |
| gctUINT32 oval, nval; |
| |
| gcmkHEADER_ARG("Atom=0x%0x", Atom); |
| gcmkVERIFY_ARGUMENT(Atom != gcvNULL); |
| |
| do |
| { |
| oval = atomic_read((atomic_t *) Atom); |
| nval = oval & ~Mask; |
| } while (atomic_cmpxchg((atomic_t *) Atom, oval, nval) != oval); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_AtomConstruct |
| ** |
| ** Create an atom. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to a gckOS object. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctPOINTER * Atom |
| ** Pointer to a variable receiving the constructed atom. |
| */ |
| gceSTATUS |
| gckOS_AtomConstruct( |
| IN gckOS Os, |
| OUT gctPOINTER * Atom |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=0x%X", Os); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Atom != gcvNULL); |
| |
| /* Allocate the atom. */ |
| gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(atomic_t), Atom)); |
| |
| /* Initialize the atom. */ |
| atomic_set((atomic_t *) *Atom, 0); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Atom=0x%X", *Atom); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_AtomDestroy |
| ** |
| ** Destroy an atom. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to a gckOS object. |
| ** |
| ** gctPOINTER Atom |
| ** Pointer to the atom to destroy. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_AtomDestroy( |
| IN gckOS Os, |
| OUT gctPOINTER Atom |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=0x%X Atom=0x%0x", Os, Atom); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Atom != gcvNULL); |
| |
| /* Free the atom. */ |
| gcmkONERROR(gcmkOS_SAFE_FREE(Os, Atom)); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_AtomGet |
| ** |
| ** Get the 32-bit value protected by an atom. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to a gckOS object. |
| ** |
| ** gctPOINTER Atom |
| ** Pointer to the atom. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctINT32_PTR Value |
| ** Pointer to a variable the receives the value of the atom. |
| */ |
| gceSTATUS |
| gckOS_AtomGet( |
| IN gckOS Os, |
| IN gctPOINTER Atom, |
| OUT gctINT32_PTR Value |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Atom=0x%0x", Os, Atom); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Atom != gcvNULL); |
| |
| /* Return the current value of atom. */ |
| *Value = atomic_read((atomic_t *) Atom); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Value=%d", *Value); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_AtomSet |
| ** |
| ** Set the 32-bit value protected by an atom. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to a gckOS object. |
| ** |
| ** gctPOINTER Atom |
| ** Pointer to the atom. |
| ** |
| ** gctINT32 Value |
| ** The value of the atom. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_AtomSet( |
| IN gckOS Os, |
| IN gctPOINTER Atom, |
| IN gctINT32 Value |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Atom=0x%0x Value=%d", Os, Atom); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Atom != gcvNULL); |
| |
| /* Set the current value of atom. */ |
| atomic_set((atomic_t *) Atom, Value); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_AtomIncrement |
| ** |
| ** Atomically increment the 32-bit integer value inside an atom. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to a gckOS object. |
| ** |
| ** gctPOINTER Atom |
| ** Pointer to the atom. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctINT32_PTR Value |
| ** Pointer to a variable that receives the original value of the atom. |
| */ |
| gceSTATUS |
| gckOS_AtomIncrement( |
| IN gckOS Os, |
| IN gctPOINTER Atom, |
| OUT gctINT32_PTR Value |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Atom=0x%0x", Os, Atom); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Atom != gcvNULL); |
| |
| /* Increment the atom. */ |
| *Value = atomic_inc_return((atomic_t *) Atom) - 1; |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Value=%d", *Value); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_AtomDecrement |
| ** |
| ** Atomically decrement the 32-bit integer value inside an atom. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to a gckOS object. |
| ** |
| ** gctPOINTER Atom |
| ** Pointer to the atom. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctINT32_PTR Value |
| ** Pointer to a variable that receives the original value of the atom. |
| */ |
| gceSTATUS |
| gckOS_AtomDecrement( |
| IN gckOS Os, |
| IN gctPOINTER Atom, |
| OUT gctINT32_PTR Value |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Atom=0x%0x", Os, Atom); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Atom != gcvNULL); |
| |
| /* Decrement the atom. */ |
| *Value = atomic_dec_return((atomic_t *) Atom) + 1; |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Value=%d", *Value); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_Delay |
| ** |
| ** Delay execution of the current thread for a number of milliseconds. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctUINT32 Delay |
| ** Delay to sleep, specified in milliseconds. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_Delay( |
| IN gckOS Os, |
| IN gctUINT32 Delay |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Delay=%u", Os, Delay); |
| |
| if (Delay > 0) |
| { |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) |
| ktime_t delay = ktime_set((Delay / MSEC_PER_SEC), (Delay % MSEC_PER_SEC) * NSEC_PER_MSEC); |
| __set_current_state(TASK_UNINTERRUPTIBLE); |
| schedule_hrtimeout(&delay, HRTIMER_MODE_REL); |
| #else |
| msleep(Delay); |
| #endif |
| } |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_GetTicks |
| ** |
| ** Get the number of milliseconds since the system started. |
| ** |
| ** INPUT: |
| ** |
| ** OUTPUT: |
| ** |
| ** gctUINT32_PTR Time |
| ** Pointer to a variable to get time. |
| ** |
| */ |
| gceSTATUS |
| gckOS_GetTicks( |
| OUT gctUINT32_PTR Time |
| ) |
| { |
| gcmkHEADER(); |
| |
| *Time = jiffies_to_msecs(jiffies); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_TicksAfter |
| ** |
| ** Compare time values got from gckOS_GetTicks. |
| ** |
| ** INPUT: |
| ** gctUINT32 Time1 |
| ** First time value to be compared. |
| ** |
| ** gctUINT32 Time2 |
| ** Second time value to be compared. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctBOOL_PTR IsAfter |
| ** Pointer to a variable to result. |
| ** |
| */ |
| gceSTATUS |
| gckOS_TicksAfter( |
| IN gctUINT32 Time1, |
| IN gctUINT32 Time2, |
| OUT gctBOOL_PTR IsAfter |
| ) |
| { |
| gcmkHEADER(); |
| |
| *IsAfter = time_after((unsigned long)Time1, (unsigned long)Time2); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_GetTime |
| ** |
| ** Get the number of microseconds since the system started. |
| ** |
| ** INPUT: |
| ** |
| ** OUTPUT: |
| ** |
| ** gctUINT64_PTR Time |
| ** Pointer to a variable to get time. |
| ** |
| */ |
| gceSTATUS |
| gckOS_GetTime( |
| OUT gctUINT64_PTR Time |
| ) |
| { |
| struct timeval tv; |
| gcmkHEADER(); |
| |
| /* Return the time of day in microseconds. */ |
| do_gettimeofday(&tv); |
| *Time = (tv.tv_sec * 1000000ULL) + tv.tv_usec; |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_MemoryBarrier |
| ** |
| ** Make sure the CPU has executed everything up to this point and the data got |
| ** written to the specified pointer. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPOINTER Address |
| ** Address of memory that needs to be barriered. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_MemoryBarrier( |
| IN gckOS Os, |
| IN gctPOINTER Address |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Address=0x%X", Os, Address); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| |
| #if gcdNONPAGED_MEMORY_BUFFERABLE \ |
| && defined (CONFIG_ARM) \ |
| && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34)) |
| /* drain write buffer */ |
| dsb(); |
| |
| /* drain outer cache's write buffer? */ |
| #else |
| mb(); |
| #endif |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_AllocatePagedMemory |
| ** |
| ** Allocate memory from the paged pool. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctSIZE_T Bytes |
| ** Number of bytes to allocate. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctPHYS_ADDR * Physical |
| ** Pointer to a variable that receives the physical address of the |
| ** memory allocation. |
| */ |
| gceSTATUS |
| gckOS_AllocatePagedMemory( |
| IN gckOS Os, |
| IN gctSIZE_T Bytes, |
| OUT gctPHYS_ADDR * Physical |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=0x%X Bytes=%lu", Os, Bytes); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| gcmkVERIFY_ARGUMENT(Physical != gcvNULL); |
| |
| /* Allocate the memory. */ |
| gcmkONERROR(gckOS_AllocatePagedMemoryEx(Os, gcvALLOC_FLAG_NONE, Bytes, gcvNULL, Physical)); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Physical=0x%X", *Physical); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_AllocatePagedMemoryEx |
| ** |
| ** Allocate memory from the paged pool. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctUINT32 Flag |
| ** Allocation attribute. |
| ** |
| ** gctSIZE_T Bytes |
| ** Number of bytes to allocate. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctUINT32 * Gid |
| ** Save the global ID for the piece of allocated memory. |
| ** |
| ** gctPHYS_ADDR * Physical |
| ** Pointer to a variable that receives the physical address of the |
| ** memory allocation. |
| */ |
| gceSTATUS |
| gckOS_AllocatePagedMemoryEx( |
| IN gckOS Os, |
| IN gctUINT32 Flag, |
| IN gctSIZE_T Bytes, |
| OUT gctUINT32 * Gid, |
| OUT gctPHYS_ADDR * Physical |
| ) |
| { |
| gctINT numPages; |
| PLINUX_MDL mdl = gcvNULL; |
| gctSIZE_T bytes; |
| gceSTATUS status = gcvSTATUS_OUT_OF_MEMORY; |
| gckALLOCATOR allocator; |
| |
| gcmkHEADER_ARG("Os=0x%X Flag=%x Bytes=%lu", Os, Flag, Bytes); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| gcmkVERIFY_ARGUMENT(Physical != gcvNULL); |
| |
| bytes = gcmALIGN(Bytes, PAGE_SIZE); |
| |
| numPages = GetPageCount(bytes, 0); |
| |
| mdl = _CreateMdl(); |
| if (mdl == gcvNULL) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| /* Walk all allocators. */ |
| list_for_each_entry(allocator, &Os->allocatorList, head) |
| { |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_OS, |
| "%s(%d) flag = %x allocator->capability = %x", |
| __FUNCTION__, __LINE__, Flag, allocator->capability); |
| |
| if ((Flag & allocator->capability) != Flag) |
| { |
| continue; |
| } |
| |
| status = allocator->ops->Alloc(allocator, mdl, numPages, Flag); |
| |
| if (gcmIS_SUCCESS(status)) |
| { |
| mdl->allocator = allocator; |
| break; |
| } |
| } |
| |
| /* Check status. */ |
| gcmkONERROR(status); |
| |
| mdl->dmaHandle = 0; |
| mdl->addr = 0; |
| mdl->numPages = numPages; |
| mdl->pagedMem = 1; |
| mdl->contiguous = Flag & gcvALLOC_FLAG_CONTIGUOUS; |
| |
| /* Return physical address. */ |
| *Physical = (gctPHYS_ADDR) mdl; |
| |
| if (Gid != gcvNULL) |
| { |
| *Gid = mdl->gid; |
| } |
| |
| MEMORY_LOCK(Os); |
| |
| /* |
| * Add this to a global list. |
| * Will be used by get physical address |
| * and mapuser pointer functions. |
| */ |
| if (!Os->mdlHead) |
| { |
| /* Initialize the queue. */ |
| Os->mdlHead = Os->mdlTail = mdl; |
| } |
| else |
| { |
| /* Add to tail. */ |
| mdl->prev = Os->mdlTail; |
| Os->mdlTail->next = mdl; |
| Os->mdlTail = mdl; |
| } |
| |
| MEMORY_UNLOCK(Os); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Physical=0x%X", *Physical); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (mdl != gcvNULL) |
| { |
| /* Free the memory. */ |
| _DestroyMdl(mdl); |
| } |
| *Physical = gcvNULL; |
| |
| /* Return the status. */ |
| gcmkFOOTER_ARG("Os=0x%X Flag=%x Bytes=%lu", Os, Flag, Bytes); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_FreePagedMemory |
| ** |
| ** Free memory allocated from the paged pool. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPHYS_ADDR Physical |
| ** Physical address of the allocation. |
| ** |
| ** gctSIZE_T Bytes |
| ** Number of bytes of the allocation. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_FreePagedMemory( |
| IN gckOS Os, |
| IN gctPHYS_ADDR Physical, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| PLINUX_MDL mdl = (PLINUX_MDL) Physical; |
| gckALLOCATOR allocator = (gckALLOCATOR)mdl->allocator; |
| |
| gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu", Os, Physical, Bytes); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Physical != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| |
| MEMORY_LOCK(Os); |
| |
| /* Remove the node from global list. */ |
| if (mdl == Os->mdlHead) |
| { |
| if ((Os->mdlHead = mdl->next) == gcvNULL) |
| { |
| Os->mdlTail = gcvNULL; |
| } |
| } |
| else |
| { |
| mdl->prev->next = mdl->next; |
| |
| if (mdl == Os->mdlTail) |
| { |
| Os->mdlTail = mdl->prev; |
| } |
| else |
| { |
| mdl->next->prev = mdl->prev; |
| } |
| } |
| |
| MEMORY_UNLOCK(Os); |
| |
| allocator->ops->Free(allocator, mdl); |
| |
| /* Free the structure... */ |
| gcmkVERIFY_OK(_DestroyMdl(mdl)); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_LockPages |
| ** |
| ** Lock memory allocated from the paged pool. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPHYS_ADDR Physical |
| ** Physical address of the allocation. |
| ** |
| ** gctSIZE_T Bytes |
| ** Number of bytes of the allocation. |
| ** |
| ** gctBOOL Cacheable |
| ** Cache mode of mapping. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctPOINTER * Logical |
| ** Pointer to a variable that receives the address of the mapped |
| ** memory. |
| ** |
| ** gctSIZE_T * PageCount |
| ** Pointer to a variable that receives the number of pages required for |
| ** the page table according to the GPU page size. |
| */ |
| gceSTATUS |
| gckOS_LockPages( |
| IN gckOS Os, |
| IN gctPHYS_ADDR Physical, |
| IN gctSIZE_T Bytes, |
| IN gctBOOL Cacheable, |
| OUT gctPOINTER * Logical, |
| OUT gctSIZE_T * PageCount |
| ) |
| { |
| gceSTATUS status; |
| PLINUX_MDL mdl; |
| PLINUX_MDL_MAP mdlMap; |
| gckALLOCATOR allocator; |
| |
| gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu", Os, Physical, Logical); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Physical != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Logical != gcvNULL); |
| gcmkVERIFY_ARGUMENT(PageCount != gcvNULL); |
| |
| mdl = (PLINUX_MDL) Physical; |
| allocator = mdl->allocator; |
| |
| MEMORY_LOCK(Os); |
| |
| mdlMap = FindMdlMap(mdl, _GetProcessID()); |
| |
| if (mdlMap == gcvNULL) |
| { |
| mdlMap = _CreateMdlMap(mdl, _GetProcessID()); |
| |
| if (mdlMap == gcvNULL) |
| { |
| MEMORY_UNLOCK(Os); |
| |
| gcmkFOOTER_ARG("*status=%d", gcvSTATUS_OUT_OF_MEMORY); |
| return gcvSTATUS_OUT_OF_MEMORY; |
| } |
| } |
| |
| if (mdlMap->vmaAddr == gcvNULL) |
| { |
| status = allocator->ops->MapUser(allocator, mdl, mdlMap, Cacheable); |
| |
| if (gcmIS_ERROR(status)) |
| { |
| MEMORY_UNLOCK(Os); |
| |
| gcmkFOOTER_ARG("*status=%d", status); |
| return status; |
| } |
| } |
| |
| mdlMap->count++; |
| |
| /* Convert pointer to MDL. */ |
| *Logical = mdlMap->vmaAddr; |
| |
| /* Return the page number according to the GPU page size. */ |
| gcmkASSERT((PAGE_SIZE % 4096) == 0); |
| gcmkASSERT((PAGE_SIZE / 4096) >= 1); |
| |
| *PageCount = mdl->numPages * (PAGE_SIZE / 4096); |
| |
| MEMORY_UNLOCK(Os); |
| |
| gcmkVERIFY_OK(gckOS_CacheFlush( |
| Os, |
| _GetProcessID(), |
| Physical, |
| gcvINVALID_ADDRESS, |
| (gctPOINTER)mdlMap->vmaAddr, |
| mdl->numPages * PAGE_SIZE |
| )); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Logical=0x%X *PageCount=%lu", *Logical, *PageCount); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_MapPages |
| ** |
| ** Map paged memory into a page table. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPHYS_ADDR Physical |
| ** Physical address of the allocation. |
| ** |
| ** gctSIZE_T PageCount |
| ** Number of pages required for the physical address. |
| ** |
| ** gctPOINTER PageTable |
| ** Pointer to the page table to fill in. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_MapPages( |
| IN gckOS Os, |
| IN gctPHYS_ADDR Physical, |
| IN gctSIZE_T PageCount, |
| IN gctPOINTER PageTable |
| ) |
| { |
| return gckOS_MapPagesEx(Os, |
| gcvCORE_MAJOR, |
| Physical, |
| PageCount, |
| 0, |
| PageTable); |
| } |
| |
| gceSTATUS |
| gckOS_MapPagesEx( |
| IN gckOS Os, |
| IN gceCORE Core, |
| IN gctPHYS_ADDR Physical, |
| IN gctSIZE_T PageCount, |
| IN gctUINT32 Address, |
| IN gctPOINTER PageTable |
| ) |
| { |
| gceSTATUS status = gcvSTATUS_OK; |
| PLINUX_MDL mdl; |
| gctUINT32* table; |
| gctUINT32 offset; |
| #if gcdNONPAGED_MEMORY_CACHEABLE |
| gckMMU mmu; |
| PLINUX_MDL mmuMdl; |
| gctUINT32 bytes; |
| gctPHYS_ADDR pageTablePhysical; |
| #endif |
| |
| #if gcdPROCESS_ADDRESS_SPACE |
| gckKERNEL kernel = Os->device->kernels[Core]; |
| gckMMU mmu; |
| #endif |
| gckALLOCATOR allocator; |
| |
| gcmkHEADER_ARG("Os=0x%X Core=%d Physical=0x%X PageCount=%u PageTable=0x%X", |
| Os, Core, Physical, PageCount, PageTable); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Physical != gcvNULL); |
| gcmkVERIFY_ARGUMENT(PageCount > 0); |
| gcmkVERIFY_ARGUMENT(PageTable != gcvNULL); |
| |
| /* Convert pointer to MDL. */ |
| mdl = (PLINUX_MDL)Physical; |
| |
| allocator = mdl->allocator; |
| |
| gcmkTRACE_ZONE( |
| gcvLEVEL_INFO, gcvZONE_OS, |
| "%s(%d): Physical->0x%X PageCount->0x%X PagedMemory->?%d", |
| __FUNCTION__, __LINE__, |
| (gctUINT32)(gctUINTPTR_T)Physical, |
| (gctUINT32)(gctUINTPTR_T)PageCount, |
| mdl->pagedMem |
| ); |
| |
| #if gcdPROCESS_ADDRESS_SPACE |
| gcmkONERROR(gckKERNEL_GetProcessMMU(kernel, &mmu)); |
| #endif |
| |
| table = (gctUINT32 *)PageTable; |
| #if gcdNONPAGED_MEMORY_CACHEABLE |
| mmu = Os->device->kernels[Core]->mmu; |
| bytes = PageCount * sizeof(*table); |
| mmuMdl = (PLINUX_MDL)mmu->pageTablePhysical; |
| #endif |
| |
| /* Get all the physical addresses and store them in the page table. */ |
| |
| offset = 0; |
| PageCount = PageCount / (PAGE_SIZE / 4096); |
| |
| /* Try to get the user pages so DMA can happen. */ |
| while (PageCount-- > 0) |
| { |
| gctUINT i; |
| gctUINT32 phys = ~0; |
| |
| if (mdl->pagedMem && !mdl->contiguous) |
| { |
| allocator->ops->Physical(allocator, mdl, offset, &phys); |
| } |
| else |
| { |
| if (!mdl->pagedMem) |
| { |
| gcmkTRACE_ZONE( |
| gcvLEVEL_INFO, gcvZONE_OS, |
| "%s(%d): we should not get this call for Non Paged Memory!", |
| __FUNCTION__, __LINE__ |
| ); |
| } |
| |
| phys = page_to_phys(nth_page(mdl->u.contiguousPages, offset)); |
| } |
| |
| gcmkVERIFY_OK(gckOS_CPUPhysicalToGPUPhysical(Os, phys, &phys)); |
| |
| #ifdef CONFIG_IOMMU_SUPPORT |
| if (Os->iommu) |
| { |
| gcmkTRACE_ZONE( |
| gcvLEVEL_INFO, gcvZONE_OS, |
| "%s(%d): Setup mapping in IOMMU %x => %x", |
| __FUNCTION__, __LINE__, |
| Address + (offset * PAGE_SIZE), phys |
| ); |
| |
| /* When use IOMMU, GPU use system PAGE_SIZE. */ |
| gcmkONERROR(gckIOMMU_Map( |
| Os->iommu, Address + (offset * PAGE_SIZE), phys, PAGE_SIZE)); |
| } |
| else |
| #endif |
| { |
| |
| #if gcdENABLE_VG |
| if (Core == gcvCORE_VG) |
| { |
| for (i = 0; i < (PAGE_SIZE / 4096); i++) |
| { |
| gcmkONERROR( |
| gckVGMMU_SetPage(Os->device->kernels[Core]->vg->mmu, |
| phys + (i * 4096), |
| table++)); |
| } |
| } |
| else |
| #endif |
| { |
| for (i = 0; i < (PAGE_SIZE / 4096); i++) |
| { |
| #if gcdPROCESS_ADDRESS_SPACE |
| gctUINT32_PTR pageTableEntry; |
| gckMMU_GetPageEntry(mmu, Address + (offset * 4096), &pageTableEntry); |
| gcmkONERROR( |
| gckMMU_SetPage(mmu, |
| phys + (i * 4096), |
| pageTableEntry)); |
| #else |
| gcmkONERROR( |
| gckMMU_SetPage(Os->device->kernels[Core]->mmu, |
| phys + (i * 4096), |
| table++)); |
| #endif |
| } |
| } |
| } |
| |
| offset += 1; |
| } |
| |
| #if gcdNONPAGED_MEMORY_CACHEABLE |
| /* Get physical address of pageTable */ |
| pageTablePhysical = (gctPHYS_ADDR)(mmuMdl->dmaHandle + |
| ((gctUINT32 *)PageTable - mmu->pageTableLogical)); |
| |
| /* Flush the mmu page table cache. */ |
| gcmkONERROR(gckOS_CacheClean( |
| Os, |
| _GetProcessID(), |
| gcvNULL, |
| pageTablePhysical, |
| PageTable, |
| bytes |
| )); |
| #endif |
| |
| OnError: |
| |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| gceSTATUS |
| gckOS_UnmapPages( |
| IN gckOS Os, |
| IN gctSIZE_T PageCount, |
| IN gctUINT32 Address |
| ) |
| { |
| #ifdef CONFIG_IOMMU_SUPPORT |
| if (Os->iommu) |
| { |
| gcmkVERIFY_OK(gckIOMMU_Unmap( |
| Os->iommu, Address, PageCount * PAGE_SIZE)); |
| } |
| #endif |
| |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_UnlockPages |
| ** |
| ** Unlock memory allocated from the paged pool. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPHYS_ADDR Physical |
| ** Physical address of the allocation. |
| ** |
| ** gctSIZE_T Bytes |
| ** Number of bytes of the allocation. |
| ** |
| ** gctPOINTER Logical |
| ** Address of the mapped memory. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_UnlockPages( |
| IN gckOS Os, |
| IN gctPHYS_ADDR Physical, |
| IN gctSIZE_T Bytes, |
| IN gctPOINTER Logical |
| ) |
| { |
| PLINUX_MDL_MAP mdlMap; |
| PLINUX_MDL mdl = (PLINUX_MDL)Physical; |
| gckALLOCATOR allocator = mdl->allocator; |
| |
| gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%u Logical=0x%X", |
| Os, Physical, Bytes, Logical); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Physical != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Logical != gcvNULL); |
| |
| MEMORY_LOCK(Os); |
| |
| mdlMap = mdl->maps; |
| |
| while (mdlMap != gcvNULL) |
| { |
| if ((mdlMap->vmaAddr != gcvNULL) && (_GetProcessID() == mdlMap->pid)) |
| { |
| if (--mdlMap->count == 0) |
| { |
| allocator->ops->UnmapUser( |
| allocator, |
| mdlMap->vmaAddr, |
| mdl->numPages * PAGE_SIZE); |
| |
| mdlMap->vmaAddr = gcvNULL; |
| } |
| } |
| |
| mdlMap = mdlMap->next; |
| } |
| |
| MEMORY_UNLOCK(Os); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_AllocateContiguous |
| ** |
| ** Allocate memory from the contiguous pool. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctBOOL InUserSpace |
| ** gcvTRUE if the pages need to be mapped into user space. |
| ** |
| ** gctSIZE_T * Bytes |
| ** Pointer to the number of bytes to allocate. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctSIZE_T * Bytes |
| ** Pointer to a variable that receives the number of bytes allocated. |
| ** |
| ** gctPHYS_ADDR * Physical |
| ** Pointer to a variable that receives the physical address of the |
| ** memory allocation. |
| ** |
| ** gctPOINTER * Logical |
| ** Pointer to a variable that receives the logical address of the |
| ** memory allocation. |
| */ |
| gceSTATUS |
| gckOS_AllocateContiguous( |
| IN gckOS Os, |
| IN gctBOOL InUserSpace, |
| IN OUT gctSIZE_T * Bytes, |
| OUT gctPHYS_ADDR * Physical, |
| OUT gctPOINTER * Logical |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=0x%X InUserSpace=%d *Bytes=%lu", |
| Os, InUserSpace, gcmOPT_VALUE(Bytes)); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Bytes != gcvNULL); |
| gcmkVERIFY_ARGUMENT(*Bytes > 0); |
| gcmkVERIFY_ARGUMENT(Physical != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Logical != gcvNULL); |
| |
| /* Same as non-paged memory for now. */ |
| gcmkONERROR(gckOS_AllocateNonPagedMemory(Os, |
| InUserSpace, |
| Bytes, |
| Physical, |
| Logical)); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Bytes=%lu *Physical=0x%X *Logical=0x%X", |
| *Bytes, *Physical, *Logical); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_FreeContiguous |
| ** |
| ** Free memory allocated from the contiguous pool. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPHYS_ADDR Physical |
| ** Physical address of the allocation. |
| ** |
| ** gctPOINTER Logical |
| ** Logicval address of the allocation. |
| ** |
| ** gctSIZE_T Bytes |
| ** Number of bytes of the allocation. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_FreeContiguous( |
| IN gckOS Os, |
| IN gctPHYS_ADDR Physical, |
| IN gctPOINTER Logical, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=0x%X Physical=0x%X Logical=0x%X Bytes=%lu", |
| Os, Physical, Logical, Bytes); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Physical != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Logical != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| |
| /* Same of non-paged memory for now. */ |
| gcmkONERROR(gckOS_FreeNonPagedMemory(Os, Bytes, Physical, Logical)); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| #if gcdENABLE_VG |
| /****************************************************************************** |
| ** |
| ** gckOS_GetKernelLogical |
| ** |
| ** Return the kernel logical pointer that corresponods to the specified |
| ** hardware address. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctUINT32 Address |
| ** Hardware physical address. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctPOINTER * KernelPointer |
| ** Pointer to a variable receiving the pointer in kernel address space. |
| */ |
| gceSTATUS |
| gckOS_GetKernelLogical( |
| IN gckOS Os, |
| IN gctUINT32 Address, |
| OUT gctPOINTER * KernelPointer |
| ) |
| { |
| return gckOS_GetKernelLogicalEx(Os, gcvCORE_MAJOR, Address, KernelPointer); |
| } |
| |
| gceSTATUS |
| gckOS_GetKernelLogicalEx( |
| IN gckOS Os, |
| IN gceCORE Core, |
| IN gctUINT32 Address, |
| OUT gctPOINTER * KernelPointer |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=0x%X Core=%d Address=0x%08x", Os, Core, Address); |
| |
| do |
| { |
| gckGALDEVICE device; |
| gckKERNEL kernel; |
| gcePOOL pool; |
| gctUINT32 offset; |
| gctPOINTER logical; |
| |
| /* Extract the pointer to the gckGALDEVICE class. */ |
| device = (gckGALDEVICE) Os->device; |
| |
| /* Kernel shortcut. */ |
| kernel = device->kernels[Core]; |
| #if gcdENABLE_VG |
| if (Core == gcvCORE_VG) |
| { |
| gcmkERR_BREAK(gckVGHARDWARE_SplitMemory( |
| kernel->vg->hardware, Address, &pool, &offset |
| )); |
| } |
| else |
| #endif |
| { |
| /* Split the memory address into a pool type and offset. */ |
| gcmkERR_BREAK(gckHARDWARE_SplitMemory( |
| kernel->hardware, Address, &pool, &offset |
| )); |
| } |
| |
| /* Dispatch on pool. */ |
| switch (pool) |
| { |
| case gcvPOOL_LOCAL_INTERNAL: |
| /* Internal memory. */ |
| logical = device->internalLogical; |
| break; |
| |
| case gcvPOOL_LOCAL_EXTERNAL: |
| /* External memory. */ |
| logical = device->externalLogical; |
| break; |
| |
| case gcvPOOL_SYSTEM: |
| /* System memory. */ |
| logical = device->contiguousBase; |
| break; |
| |
| default: |
| /* Invalid memory pool. */ |
| gcmkFOOTER(); |
| return gcvSTATUS_INVALID_ARGUMENT; |
| } |
| |
| /* Build logical address of specified address. */ |
| * KernelPointer = ((gctUINT8_PTR) logical) + offset; |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*KernelPointer=0x%X", *KernelPointer); |
| return gcvSTATUS_OK; |
| } |
| while (gcvFALSE); |
| |
| /* Return status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| #endif |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_MapUserPointer |
| ** |
| ** Map a pointer from the user process into the kernel address space. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPOINTER Pointer |
| ** Pointer in user process space that needs to be mapped. |
| ** |
| ** gctSIZE_T Size |
| ** Number of bytes that need to be mapped. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctPOINTER * KernelPointer |
| ** Pointer to a variable receiving the mapped pointer in kernel address |
| ** space. |
| */ |
| gceSTATUS |
| gckOS_MapUserPointer( |
| IN gckOS Os, |
| IN gctPOINTER Pointer, |
| IN gctSIZE_T Size, |
| OUT gctPOINTER * KernelPointer |
| ) |
| { |
| gctPOINTER buf = gcvNULL; |
| gctUINT32 len; |
| |
| gcmkHEADER_ARG("Os=0x%X Pointer=0x%X Size=%lu", Os, Pointer, Size); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Pointer != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Size > 0); |
| gcmkVERIFY_ARGUMENT(KernelPointer != gcvNULL); |
| |
| buf = kmalloc(Size, GFP_KERNEL | gcdNOWARN); |
| if (buf == gcvNULL) |
| { |
| gcmkTRACE( |
| gcvLEVEL_ERROR, |
| "%s(%d): Failed to allocate memory.", |
| __FUNCTION__, __LINE__ |
| ); |
| |
| gcmkFOOTER_ARG("*status=%d", gcvSTATUS_OUT_OF_MEMORY); |
| return gcvSTATUS_OUT_OF_MEMORY; |
| } |
| |
| len = copy_from_user(buf, Pointer, Size); |
| if (len != 0) |
| { |
| gcmkTRACE( |
| gcvLEVEL_ERROR, |
| "%s(%d): Failed to copy data from user.", |
| __FUNCTION__, __LINE__ |
| ); |
| |
| if (buf != gcvNULL) |
| { |
| kfree(buf); |
| } |
| |
| gcmkFOOTER_ARG("*status=%d", gcvSTATUS_GENERIC_IO); |
| return gcvSTATUS_GENERIC_IO; |
| } |
| |
| *KernelPointer = buf; |
| |
| gcmkFOOTER_ARG("*KernelPointer=0x%X", *KernelPointer); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_UnmapUserPointer |
| ** |
| ** Unmap a user process pointer from the kernel address space. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPOINTER Pointer |
| ** Pointer in user process space that needs to be unmapped. |
| ** |
| ** gctSIZE_T Size |
| ** Number of bytes that need to be unmapped. |
| ** |
| ** gctPOINTER KernelPointer |
| ** Pointer in kernel address space that needs to be unmapped. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_UnmapUserPointer( |
| IN gckOS Os, |
| IN gctPOINTER Pointer, |
| IN gctSIZE_T Size, |
| IN gctPOINTER KernelPointer |
| ) |
| { |
| gctUINT32 len; |
| |
| gcmkHEADER_ARG("Os=0x%X Pointer=0x%X Size=%lu KernelPointer=0x%X", |
| Os, Pointer, Size, KernelPointer); |
| |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Pointer != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Size > 0); |
| gcmkVERIFY_ARGUMENT(KernelPointer != gcvNULL); |
| |
| len = copy_to_user(Pointer, KernelPointer, Size); |
| |
| kfree(KernelPointer); |
| |
| if (len != 0) |
| { |
| gcmkTRACE( |
| gcvLEVEL_ERROR, |
| "%s(%d): Failed to copy data to user.", |
| __FUNCTION__, __LINE__ |
| ); |
| |
| gcmkFOOTER_ARG("status=%d", gcvSTATUS_GENERIC_IO); |
| return gcvSTATUS_GENERIC_IO; |
| } |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_QueryNeedCopy |
| ** |
| ** Query whether the memory can be accessed or mapped directly or it has to be |
| ** copied. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctUINT32 ProcessID |
| ** Process ID of the current process. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctBOOL_PTR NeedCopy |
| ** Pointer to a boolean receiving gcvTRUE if the memory needs a copy or |
| ** gcvFALSE if the memory can be accessed or mapped dircetly. |
| */ |
| gceSTATUS |
| gckOS_QueryNeedCopy( |
| IN gckOS Os, |
| IN gctUINT32 ProcessID, |
| OUT gctBOOL_PTR NeedCopy |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X ProcessID=%d", Os, ProcessID); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(NeedCopy != gcvNULL); |
| |
| /* We need to copy data. */ |
| *NeedCopy = gcvTRUE; |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*NeedCopy=%d", *NeedCopy); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_CopyFromUserData |
| ** |
| ** Copy data from user to kernel memory. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPOINTER KernelPointer |
| ** Pointer to kernel memory. |
| ** |
| ** gctPOINTER Pointer |
| ** Pointer to user memory. |
| ** |
| ** gctSIZE_T Size |
| ** Number of bytes to copy. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_CopyFromUserData( |
| IN gckOS Os, |
| IN gctPOINTER KernelPointer, |
| IN gctPOINTER Pointer, |
| IN gctSIZE_T Size |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=0x%X KernelPointer=0x%X Pointer=0x%X Size=%lu", |
| Os, KernelPointer, Pointer, Size); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(KernelPointer != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Pointer != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Size > 0); |
| |
| /* Copy data from user. */ |
| if (copy_from_user(KernelPointer, Pointer, Size) != 0) |
| { |
| /* Could not copy all the bytes. */ |
| gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); |
| } |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_CopyToUserData |
| ** |
| ** Copy data from kernel to user memory. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPOINTER KernelPointer |
| ** Pointer to kernel memory. |
| ** |
| ** gctPOINTER Pointer |
| ** Pointer to user memory. |
| ** |
| ** gctSIZE_T Size |
| ** Number of bytes to copy. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_CopyToUserData( |
| IN gckOS Os, |
| IN gctPOINTER KernelPointer, |
| IN gctPOINTER Pointer, |
| IN gctSIZE_T Size |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=0x%X KernelPointer=0x%X Pointer=0x%X Size=%lu", |
| Os, KernelPointer, Pointer, Size); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(KernelPointer != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Pointer != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Size > 0); |
| |
| /* Copy data to user. */ |
| if (copy_to_user(Pointer, KernelPointer, Size) != 0) |
| { |
| /* Could not copy all the bytes. */ |
| gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); |
| } |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_WriteMemory |
| ** |
| ** Write data to a memory. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPOINTER Address |
| ** Address of the memory to write to. |
| ** |
| ** gctUINT32 Data |
| ** Data for register. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_WriteMemory( |
| IN gckOS Os, |
| IN gctPOINTER Address, |
| IN gctUINT32 Data |
| ) |
| { |
| gceSTATUS status; |
| gcmkHEADER_ARG("Os=0x%X Address=0x%X Data=%u", Os, Address, Data); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_ARGUMENT(Address != gcvNULL); |
| |
| /* Write memory. */ |
| if (access_ok(VERIFY_WRITE, Address, 4)) |
| { |
| /* User address. */ |
| if(put_user(Data, (gctUINT32*)Address)) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ADDRESS); |
| } |
| } |
| else |
| { |
| /* Kernel address. */ |
| *(gctUINT32 *)Address = Data; |
| } |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_MapUserMemory |
| ** |
| ** Lock down a user buffer and return an DMA'able address to be used by the |
| ** hardware to access it. |
| ** |
| ** INPUT: |
| ** |
| ** gctPOINTER Memory |
| ** Pointer to memory to lock down. |
| ** |
| ** gctSIZE_T Size |
| ** Size in bytes of the memory to lock down. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctPOINTER * Info |
| ** Pointer to variable receiving the information record required by |
| ** gckOS_UnmapUserMemory. |
| ** |
| ** gctUINT32_PTR Address |
| ** Pointer to a variable that will receive the address DMA'able by the |
| ** hardware. |
| */ |
| gceSTATUS |
| gckOS_MapUserMemory( |
| IN gckOS Os, |
| IN gceCORE Core, |
| IN gctPOINTER Memory, |
| IN gctUINT32 Physical, |
| IN gctSIZE_T Size, |
| OUT gctPOINTER * Info, |
| OUT gctUINT32_PTR Address |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=0x%x Core=%d Memory=0x%x Size=%lu", Os, Core, Memory, Size); |
| |
| #if gcdSECURE_USER |
| gcmkONERROR(gckOS_AddMapping(Os, *Address, Memory, Size)); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| #else |
| { |
| gctSIZE_T pageCount, i, j; |
| gctUINT32_PTR pageTable; |
| gctUINT32 address = 0, physical = ~0U; |
| gctUINTPTR_T start, end, memory; |
| gctUINT32 offset; |
| gctINT result = 0; |
| #if gcdPROCESS_ADDRESS_SPACE |
| gckMMU mmu; |
| #endif |
| |
| gcsPageInfo_PTR info = gcvNULL; |
| struct page **pages = gcvNULL; |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Memory != gcvNULL || Physical != ~0U); |
| gcmkVERIFY_ARGUMENT(Size > 0); |
| gcmkVERIFY_ARGUMENT(Info != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Address != gcvNULL); |
| |
| do |
| { |
| gctSIZE_T extraPage; |
| |
| memory = (gctUINTPTR_T) Memory; |
| |
| /* Get the number of required pages. */ |
| end = (memory + Size + PAGE_SIZE - 1) >> PAGE_SHIFT; |
| start = memory >> PAGE_SHIFT; |
| pageCount = end - start; |
| |
| /* Allocate extra 64 bytes to avoid cache overflow */ |
| extraPage = (((memory + gcmALIGN(Size + 64, 64) + PAGE_SIZE - 1) >> PAGE_SHIFT) > end) ? 1 : 0; |
| |
| gcmkTRACE_ZONE( |
| gcvLEVEL_INFO, gcvZONE_OS, |
| "%s(%d): pageCount: %d.", |
| __FUNCTION__, __LINE__, |
| pageCount |
| ); |
| |
| /* Overflow. */ |
| if ((memory + Size) < memory) |
| { |
| gcmkFOOTER_ARG("status=%d", gcvSTATUS_INVALID_ARGUMENT); |
| return gcvSTATUS_INVALID_ARGUMENT; |
| } |
| |
| MEMORY_MAP_LOCK(Os); |
| |
| /* Allocate the Info struct. */ |
| info = (gcsPageInfo_PTR)kmalloc(sizeof(gcsPageInfo), GFP_KERNEL | gcdNOWARN); |
| |
| if (info == gcvNULL) |
| { |
| status = gcvSTATUS_OUT_OF_MEMORY; |
| break; |
| } |
| |
| info->extraPage = 0; |
| |
| /* Allocate the array of page addresses. */ |
| pages = (struct page **)kmalloc((pageCount + extraPage) * sizeof(struct page *), GFP_KERNEL | gcdNOWARN); |
| |
| if (pages == gcvNULL) |
| { |
| status = gcvSTATUS_OUT_OF_MEMORY; |
| break; |
| } |
| |
| if (Physical != ~0U) |
| { |
| for (i = 0; i < pageCount; i++) |
| { |
| pages[i] = pfn_to_page((Physical >> PAGE_SHIFT) + i); |
| |
| if (pfn_valid(page_to_pfn(pages[i]))) |
| { |
| get_page(pages[i]); |
| } |
| } |
| } |
| else |
| { |
| /* Get the user pages. */ |
| down_read(¤t->mm->mmap_sem); |
| |
| result = get_user_pages(current, |
| current->mm, |
| memory & PAGE_MASK, |
| pageCount, |
| 1, |
| 0, |
| pages, |
| gcvNULL |
| ); |
| |
| up_read(¤t->mm->mmap_sem); |
| |
| if (result <=0 || result < pageCount) |
| { |
| struct vm_area_struct *vma; |
| |
| /* Release the pages if any. */ |
| if (result > 0) |
| { |
| for (i = 0; i < result; i++) |
| { |
| if (pages[i] == gcvNULL) |
| { |
| break; |
| } |
| |
| page_cache_release(pages[i]); |
| pages[i] = gcvNULL; |
| } |
| |
| result = 0; |
| } |
| |
| vma = find_vma(current->mm, memory); |
| |
| if (vma && (vma->vm_flags & VM_PFNMAP)) |
| { |
| pte_t * pte; |
| spinlock_t * ptl; |
| gctUINTPTR_T logical = memory; |
| |
| for (i = 0; i < pageCount; i++) |
| { |
| pgd_t * pgd = pgd_offset(current->mm, logical); |
| pud_t * pud = pud_offset(pgd, logical); |
| |
| if (pud) |
| { |
| pmd_t * pmd = pmd_offset(pud, logical); |
| pte = pte_offset_map_lock(current->mm, pmd, logical, &ptl); |
| if (!pte) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); |
| } |
| } |
| else |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); |
| } |
| |
| pages[i] = pte_page(*pte); |
| pte_unmap_unlock(pte, ptl); |
| |
| /* Advance to next. */ |
| logical += PAGE_SIZE; |
| } |
| } |
| else |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); |
| } |
| |
| /* Check if this memory is contiguous for old mmu. */ |
| if (Os->device->kernels[Core]->hardware->mmuVersion == 0) |
| { |
| for (i = 1; i < pageCount; i++) |
| { |
| if (pages[i] != nth_page(pages[0], i)) |
| { |
| /* Non-contiguous. */ |
| break; |
| } |
| } |
| |
| if (i == pageCount) |
| { |
| /* Contiguous memory. */ |
| physical = page_to_phys(pages[0]) | (memory & ~PAGE_MASK); |
| |
| if (!((physical - Os->device->baseAddress) & 0x80000000)) |
| { |
| kfree(pages); |
| pages = gcvNULL; |
| |
| info->pages = gcvNULL; |
| info->pageTable = gcvNULL; |
| |
| MEMORY_MAP_UNLOCK(Os); |
| |
| *Address = physical - Os->device->baseAddress; |
| *Info = info; |
| |
| gcmkVERIFY_OK( |
| gckOS_CPUPhysicalToGPUPhysical(Os, *Address, Address)); |
| |
| gcmkFOOTER_ARG("*Info=0x%X *Address=0x%08x", |
| *Info, *Address); |
| |
| return gcvSTATUS_OK; |
| } |
| } |
| } |
| |
| /* Reference pages. */ |
| for (i = 0; i < pageCount; i++) |
| { |
| if (pfn_valid(page_to_pfn(pages[i]))) |
| { |
| get_page(pages[i]); |
| } |
| } |
| } |
| } |
| |
| for (i = 0; i < pageCount; i++) |
| { |
| #ifdef CONFIG_ARM |
| gctUINT32 data; |
| get_user(data, (gctUINT32*)((memory & PAGE_MASK) + i * PAGE_SIZE)); |
| #endif |
| |
| /* Flush(clean) the data cache. */ |
| gcmkONERROR(gckOS_CacheFlush(Os, _GetProcessID(), gcvNULL, |
| page_to_phys(pages[i]), |
| (gctPOINTER)(memory & PAGE_MASK) + i*PAGE_SIZE, |
| PAGE_SIZE)); |
| } |
| |
| #if gcdPROCESS_ADDRESS_SPACE |
| gcmkONERROR(gckKERNEL_GetProcessMMU(Os->device->kernels[Core], &mmu)); |
| #endif |
| |
| if (extraPage) |
| { |
| pages[pageCount++] = Os->paddingPage; |
| info->extraPage = 1; |
| } |
| |
| #if gcdSECURITY |
| { |
| gctPHYS_ADDR physicalArrayPhysical; |
| gctPOINTER physicalArrayLogical; |
| gctUINT32_PTR logical; |
| gctSIZE_T bytes = pageCount * gcmSIZEOF(gctUINT32); |
| pageTable = gcvNULL; |
| |
| gcmkONERROR(gckOS_AllocateNonPagedMemory( |
| Os, |
| gcvFALSE, |
| &bytes, |
| &physicalArrayPhysical, |
| &physicalArrayLogical |
| )); |
| |
| logical = physicalArrayLogical; |
| |
| /* Fill the page table. */ |
| for (i = 0; i < pageCount; i++) |
| { |
| gctUINT32 phys; |
| phys = page_to_phys(pages[i]); |
| |
| logical[i] = phys; |
| } |
| j = 0; |
| |
| |
| gcmkONERROR(gckKERNEL_SecurityMapMemory( |
| Os->device->kernels[Core], |
| physicalArrayLogical, |
| pageCount, |
| &address |
| )); |
| |
| gcmkONERROR(gckOS_FreeNonPagedMemory( |
| Os, |
| 1, |
| physicalArrayPhysical, |
| physicalArrayLogical |
| )); |
| } |
| |
| #else |
| #if gcdENABLE_VG |
| if (Core == gcvCORE_VG) |
| { |
| /* Allocate pages inside the page table. */ |
| gcmkERR_BREAK(gckVGMMU_AllocatePages(Os->device->kernels[Core]->vg->mmu, |
| pageCount * (PAGE_SIZE/4096), |
| (gctPOINTER *) &pageTable, |
| &address)); |
| } |
| else |
| #endif |
| { |
| #if gcdPROCESS_ADDRESS_SPACE |
| /* Allocate pages inside the page table. */ |
| gcmkERR_BREAK(gckMMU_AllocatePages(mmu, |
| pageCount * (PAGE_SIZE/4096), |
| (gctPOINTER *) &pageTable, |
| &address)); |
| #else |
| /* Allocate pages inside the page table. */ |
| gcmkERR_BREAK(gckMMU_AllocatePages(Os->device->kernels[Core]->mmu, |
| pageCount * (PAGE_SIZE/4096), |
| (gctPOINTER *) &pageTable, |
| &address)); |
| #endif |
| } |
| |
| /* Fill the page table. */ |
| for (i = 0; i < pageCount; i++) |
| { |
| gctUINT32 phys; |
| gctUINT32_PTR tab = pageTable + i * (PAGE_SIZE/4096); |
| |
| #if gcdPROCESS_ADDRESS_SPACE |
| gckMMU_GetPageEntry(mmu, address + i * 4096, &tab); |
| #endif |
| phys = page_to_phys(pages[i]); |
| |
| #ifdef CONFIG_IOMMU_SUPPORT |
| if (Os->iommu) |
| { |
| gcmkTRACE_ZONE( |
| gcvLEVEL_INFO, gcvZONE_OS, |
| "%s(%d): Setup mapping in IOMMU %x => %x", |
| __FUNCTION__, __LINE__, |
| Address + (i * PAGE_SIZE), phys |
| ); |
| |
| gcmkONERROR(gckIOMMU_Map( |
| Os->iommu, address + i * PAGE_SIZE, phys, PAGE_SIZE)); |
| } |
| else |
| #endif |
| { |
| |
| #if gcdENABLE_VG |
| if (Core == gcvCORE_VG) |
| { |
| gcmkVERIFY_OK( |
| gckOS_CPUPhysicalToGPUPhysical(Os, phys, &phys)); |
| |
| /* Get the physical address from page struct. */ |
| gcmkONERROR( |
| gckVGMMU_SetPage(Os->device->kernels[Core]->vg->mmu, |
| phys, |
| tab)); |
| } |
| else |
| #endif |
| { |
| /* Get the physical address from page struct. */ |
| gcmkONERROR( |
| gckMMU_SetPage(Os->device->kernels[Core]->mmu, |
| phys, |
| tab)); |
| } |
| |
| for (j = 1; j < (PAGE_SIZE/4096); j++) |
| { |
| pageTable[i * (PAGE_SIZE/4096) + j] = pageTable[i * (PAGE_SIZE/4096)] + 4096 * j; |
| } |
| } |
| |
| #if !gcdPROCESS_ADDRESS_SPACE |
| gcmkTRACE_ZONE( |
| gcvLEVEL_INFO, gcvZONE_OS, |
| "%s(%d): pageTable[%d]: 0x%X 0x%X.", |
| __FUNCTION__, __LINE__, |
| i, phys, pageTable[i]); |
| #endif |
| } |
| |
| #if gcdENABLE_VG |
| if (Core == gcvCORE_VG) |
| { |
| gcmkONERROR(gckVGMMU_Flush(Os->device->kernels[Core]->vg->mmu)); |
| } |
| else |
| #endif |
| { |
| #if gcdPROCESS_ADDRESS_SPACE |
| info->mmu = mmu; |
| gcmkONERROR(gckMMU_Flush(mmu)); |
| #else |
| gcmkONERROR(gckMMU_Flush(Os->device->kernels[Core]->mmu, gcvSURF_TYPE_UNKNOWN)); |
| #endif |
| } |
| #endif |
| info->address = address; |
| |
| /* Save pointer to page table. */ |
| info->pageTable = pageTable; |
| info->pages = pages; |
| |
| *Info = (gctPOINTER) info; |
| |
| gcmkTRACE_ZONE( |
| gcvLEVEL_INFO, gcvZONE_OS, |
| "%s(%d): info->pages: 0x%X, info->pageTable: 0x%X, info: 0x%X.", |
| __FUNCTION__, __LINE__, |
| info->pages, |
| info->pageTable, |
| info |
| ); |
| |
| offset = (Physical != ~0U) |
| ? (Physical & ~PAGE_MASK) |
| : (memory & ~PAGE_MASK); |
| |
| /* Return address. */ |
| *Address = address + offset; |
| |
| gcmkTRACE_ZONE( |
| gcvLEVEL_INFO, gcvZONE_OS, |
| "%s(%d): Address: 0x%X.", |
| __FUNCTION__, __LINE__, |
| *Address |
| ); |
| |
| /* Success. */ |
| status = gcvSTATUS_OK; |
| } |
| while (gcvFALSE); |
| |
| OnError: |
| |
| if (gcmIS_ERROR(status)) |
| { |
| gcmkTRACE( |
| gcvLEVEL_ERROR, |
| "%s(%d): error occured: %d.", |
| __FUNCTION__, __LINE__, |
| status |
| ); |
| |
| /* Release page array. */ |
| if (result > 0 && pages != gcvNULL) |
| { |
| gcmkTRACE( |
| gcvLEVEL_ERROR, |
| "%s(%d): error: page table is freed.", |
| __FUNCTION__, __LINE__ |
| ); |
| |
| for (i = 0; i < result; i++) |
| { |
| if (pages[i] == gcvNULL) |
| { |
| break; |
| } |
| page_cache_release(pages[i]); |
| } |
| } |
| |
| if (info!= gcvNULL && pages != gcvNULL) |
| { |
| gcmkTRACE( |
| gcvLEVEL_ERROR, |
| "%s(%d): error: pages is freed.", |
| __FUNCTION__, __LINE__ |
| ); |
| |
| /* Free the page table. */ |
| kfree(pages); |
| info->pages = gcvNULL; |
| } |
| |
| /* Release page info struct. */ |
| if (info != gcvNULL) |
| { |
| gcmkTRACE( |
| gcvLEVEL_ERROR, |
| "%s(%d): error: info is freed.", |
| __FUNCTION__, __LINE__ |
| ); |
| |
| /* Free the page info struct. */ |
| kfree(info); |
| *Info = gcvNULL; |
| } |
| } |
| |
| MEMORY_MAP_UNLOCK(Os); |
| |
| /* Return the status. */ |
| if (gcmIS_SUCCESS(status)) |
| { |
| gcmkFOOTER_ARG("*Info=0x%X *Address=0x%08x", *Info, *Address); |
| } |
| else |
| { |
| gcmkFOOTER(); |
| } |
| |
| return status; |
| } |
| #endif |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_UnmapUserMemory |
| ** |
| ** Unlock a user buffer and that was previously locked down by |
| ** gckOS_MapUserMemory. |
| ** |
| ** INPUT: |
| ** |
| ** gctPOINTER Memory |
| ** Pointer to memory to unlock. |
| ** |
| ** gctSIZE_T Size |
| ** Size in bytes of the memory to unlock. |
| ** |
| ** gctPOINTER Info |
| ** Information record returned by gckOS_MapUserMemory. |
| ** |
| ** gctUINT32_PTR Address |
| ** The address returned by gckOS_MapUserMemory. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_UnmapUserMemory( |
| IN gckOS Os, |
| IN gceCORE Core, |
| IN gctPOINTER Memory, |
| IN gctSIZE_T Size, |
| IN gctPOINTER Info, |
| IN gctUINT32 Address |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=0x%X Core=%d Memory=0x%X Size=%lu Info=0x%X Address0x%08x", |
| Os, Core, Memory, Size, Info, Address); |
| |
| #if gcdSECURE_USER |
| gcmkONERROR(gckOS_RemoveMapping(Os, Memory, Size)); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| #else |
| { |
| gctUINTPTR_T memory, start, end; |
| gcsPageInfo_PTR info; |
| gctSIZE_T pageCount, i; |
| struct page **pages; |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Memory != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Size > 0); |
| gcmkVERIFY_ARGUMENT(Info != gcvNULL); |
| |
| do |
| { |
| info = (gcsPageInfo_PTR) Info; |
| |
| pages = info->pages; |
| |
| gcmkTRACE_ZONE( |
| gcvLEVEL_INFO, gcvZONE_OS, |
| "%s(%d): info=0x%X, pages=0x%X.", |
| __FUNCTION__, __LINE__, |
| info, pages |
| ); |
| |
| /* Invalid page array. */ |
| if (pages == gcvNULL && info->pageTable == gcvNULL) |
| { |
| kfree(info); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| memory = (gctUINTPTR_T)Memory; |
| end = (memory + Size + PAGE_SIZE - 1) >> PAGE_SHIFT; |
| start = memory >> PAGE_SHIFT; |
| pageCount = end - start; |
| |
| /* Overflow. */ |
| if ((memory + Size) < memory) |
| { |
| gcmkFOOTER_ARG("status=%d", gcvSTATUS_INVALID_ARGUMENT); |
| return gcvSTATUS_INVALID_ARGUMENT; |
| } |
| |
| gcmkTRACE_ZONE( |
| gcvLEVEL_INFO, gcvZONE_OS, |
| "%s(%d): memory: 0x%X, pageCount: %d, pageTable: 0x%X.", |
| __FUNCTION__, __LINE__, |
| memory, pageCount, info->pageTable |
| ); |
| |
| MEMORY_MAP_LOCK(Os); |
| |
| #if !gcdSECURITY |
| gcmkASSERT(info->pageTable != gcvNULL); |
| #endif |
| |
| if (info->extraPage) |
| { |
| pageCount += 1; |
| } |
| |
| #if gcdSECURITY |
| if (info->address > 0x80000000) |
| { |
| gckKERNEL_SecurityUnmapMemory( |
| Os->device->kernels[Core], |
| info->address, |
| pageCount |
| ); |
| } |
| else |
| { |
| gcmkPRINT("Wrong address %s(%d) %x", __FUNCTION__, __LINE__, info->address); |
| } |
| #else |
| #if gcdENABLE_VG |
| if (Core == gcvCORE_VG) |
| { |
| /* Free the pages from the MMU. */ |
| gcmkERR_BREAK(gckVGMMU_FreePages(Os->device->kernels[Core]->vg->mmu, |
| info->pageTable, |
| pageCount * (PAGE_SIZE/4096) |
| )); |
| } |
| else |
| #endif |
| { |
| /* Free the pages from the MMU. */ |
| #if gcdPROCESS_ADDRESS_SPACE |
| gcmkERR_BREAK(gckMMU_FreePagesEx(info->mmu, |
| info->address, |
| pageCount * (PAGE_SIZE/4096) |
| )); |
| |
| #else |
| gcmkERR_BREAK(gckMMU_FreePages(Os->device->kernels[Core]->mmu, |
| info->pageTable, |
| pageCount * (PAGE_SIZE/4096) |
| )); |
| #endif |
| |
| gcmkERR_BREAK(gckOS_UnmapPages( |
| Os, |
| pageCount * (PAGE_SIZE/4096), |
| info->address |
| )); |
| } |
| #endif |
| |
| if (info->extraPage) |
| { |
| pageCount -= 1; |
| info->extraPage = 0; |
| } |
| |
| /* Release the page cache. */ |
| if (pages) |
| { |
| for (i = 0; i < pageCount; i++) |
| { |
| gcmkTRACE_ZONE( |
| gcvLEVEL_INFO, gcvZONE_OS, |
| "%s(%d): pages[%d]: 0x%X.", |
| __FUNCTION__, __LINE__, |
| i, pages[i] |
| ); |
| |
| if (!PageReserved(pages[i])) |
| { |
| SetPageDirty(pages[i]); |
| } |
| |
| if (pfn_valid(page_to_pfn(pages[i]))) |
| { |
| page_cache_release(pages[i]); |
| } |
| } |
| } |
| |
| /* Success. */ |
| status = gcvSTATUS_OK; |
| } |
| while (gcvFALSE); |
| |
| if (info != gcvNULL) |
| { |
| /* Free the page array. */ |
| if (info->pages != gcvNULL) |
| { |
| kfree(info->pages); |
| } |
| |
| kfree(info); |
| } |
| |
| MEMORY_MAP_UNLOCK(Os); |
| |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| #endif |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_GetBaseAddress |
| ** |
| ** Get the base address for the physical memory. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to the gckOS object. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctUINT32_PTR BaseAddress |
| ** Pointer to a variable that will receive the base address. |
| */ |
| gceSTATUS |
| gckOS_GetBaseAddress( |
| IN gckOS Os, |
| OUT gctUINT32_PTR BaseAddress |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X", Os); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(BaseAddress != gcvNULL); |
| |
| /* Return base address. */ |
| *BaseAddress = Os->device->baseAddress; |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*BaseAddress=0x%08x", *BaseAddress); |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_SuspendInterrupt( |
| IN gckOS Os |
| ) |
| { |
| return gckOS_SuspendInterruptEx(Os, gcvCORE_MAJOR); |
| } |
| |
| #if gcdMULTI_GPU |
| gceSTATUS |
| gckOS_SuspendInterruptEx( |
| IN gckOS Os, |
| IN gceCORE Core |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Core=%d", Os, Core); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| |
| if (Core == gcvCORE_MAJOR) |
| { |
| disable_irq(Os->device->irqLine3D[gcvCORE_3D_0_ID]); |
| disable_irq(Os->device->irqLine3D[gcvCORE_3D_1_ID]); |
| } |
| else |
| { |
| disable_irq(Os->device->irqLines[Core]); |
| } |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| #else |
| gceSTATUS |
| gckOS_SuspendInterruptEx( |
| IN gckOS Os, |
| IN gceCORE Core |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Core=%d", Os, Core); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| |
| disable_irq(Os->device->irqLines[Core]); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| #endif |
| |
| gceSTATUS |
| gckOS_ResumeInterrupt( |
| IN gckOS Os |
| ) |
| { |
| return gckOS_ResumeInterruptEx(Os, gcvCORE_MAJOR); |
| } |
| |
| #if gcdMULTI_GPU |
| gceSTATUS |
| gckOS_ResumeInterruptEx( |
| IN gckOS Os, |
| IN gceCORE Core |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Core=%d", Os, Core); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| |
| if (Core == gcvCORE_MAJOR) |
| { |
| enable_irq(Os->device->irqLine3D[gcvCORE_3D_0_ID]); |
| enable_irq(Os->device->irqLine3D[gcvCORE_3D_1_ID]); |
| } |
| else |
| { |
| enable_irq(Os->device->irqLines[Core]); |
| } |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| #else |
| gceSTATUS |
| gckOS_ResumeInterruptEx( |
| IN gckOS Os, |
| IN gceCORE Core |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Core=%d", Os, Core); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| |
| enable_irq(Os->device->irqLines[Core]); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| #endif |
| |
| gceSTATUS |
| gckOS_MemCopy( |
| IN gctPOINTER Destination, |
| IN gctCONST_POINTER Source, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| gcmkHEADER_ARG("Destination=0x%X Source=0x%X Bytes=%lu", |
| Destination, Source, Bytes); |
| |
| gcmkVERIFY_ARGUMENT(Destination != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Source != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| |
| memcpy(Destination, Source, Bytes); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_ZeroMemory( |
| IN gctPOINTER Memory, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| gcmkHEADER_ARG("Memory=0x%X Bytes=%lu", Memory, Bytes); |
| |
| gcmkVERIFY_ARGUMENT(Memory != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| |
| memset(Memory, 0, Bytes); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ********************************* Cache Control ******************************** |
| *******************************************************************************/ |
| |
| /******************************************************************************* |
| ** gckOS_CacheClean |
| ** |
| ** Clean the cache for the specified addresses. The GPU is going to need the |
| ** data. If the system is allocating memory as non-cachable, this function can |
| ** be ignored. |
| ** |
| ** ARGUMENTS: |
| ** |
| ** gckOS Os |
| ** Pointer to gckOS object. |
| ** |
| ** gctUINT32 ProcessID |
| ** Process ID Logical belongs. |
| ** |
| ** gctPHYS_ADDR Handle |
| ** Physical address handle. If gcvNULL it is video memory. |
| ** |
| ** gctPOINTER Physical |
| ** Physical address to flush. |
| ** |
| ** gctPOINTER Logical |
| ** Logical address to flush. |
| ** |
| ** gctSIZE_T Bytes |
| ** Size of the address range in bytes to flush. |
| */ |
| gceSTATUS |
| gckOS_CacheClean( |
| IN gckOS Os, |
| IN gctUINT32 ProcessID, |
| IN gctPHYS_ADDR Handle, |
| IN gctUINT32 Physical, |
| IN gctPOINTER Logical, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| gcsPLATFORM * platform; |
| |
| gcmkHEADER_ARG("Os=0x%X ProcessID=%d Handle=0x%X Logical=0x%X Bytes=%lu", |
| Os, ProcessID, Handle, Logical, Bytes); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Logical != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| |
| platform = Os->device->platform; |
| |
| if (platform && platform->ops->cache) |
| { |
| platform->ops->cache( |
| platform, |
| ProcessID, |
| Handle, |
| Physical, |
| Logical, |
| Bytes, |
| gcvCACHE_CLEAN |
| ); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| #if !gcdCACHE_FUNCTION_UNIMPLEMENTED |
| #ifdef CONFIG_ARM |
| |
| /* Inner cache. */ |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) |
| dmac_map_area(Logical, Bytes, DMA_TO_DEVICE); |
| # else |
| dmac_clean_range(Logical, Logical + Bytes); |
| # endif |
| |
| #if defined(CONFIG_OUTER_CACHE) |
| /* Outer cache. */ |
| #if gcdENABLE_OUTER_CACHE_PATCH |
| _HandleOuterCache(Os, Physical, Logical, Bytes, gcvCACHE_CLEAN); |
| #else |
| outer_clean_range((unsigned long) Handle, (unsigned long) Handle + Bytes); |
| #endif |
| #endif |
| |
| #elif defined(CONFIG_MIPS) |
| |
| dma_cache_wback((unsigned long) Logical, Bytes); |
| |
| #elif defined(CONFIG_PPC) |
| |
| /* TODO */ |
| |
| #else |
| dma_sync_single_for_device( |
| gcvNULL, |
| (dma_addr_t)Physical, |
| Bytes, |
| DMA_TO_DEVICE); |
| #endif |
| #endif |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** gckOS_CacheInvalidate |
| ** |
| ** Invalidate the cache for the specified addresses. The GPU is going to need |
| ** data. If the system is allocating memory as non-cachable, this function can |
| ** be ignored. |
| ** |
| ** ARGUMENTS: |
| ** |
| ** gckOS Os |
| ** Pointer to gckOS object. |
| ** |
| ** gctUINT32 ProcessID |
| ** Process ID Logical belongs. |
| ** |
| ** gctPHYS_ADDR Handle |
| ** Physical address handle. If gcvNULL it is video memory. |
| ** |
| ** gctPOINTER Logical |
| ** Logical address to flush. |
| ** |
| ** gctSIZE_T Bytes |
| ** Size of the address range in bytes to flush. |
| */ |
| gceSTATUS |
| gckOS_CacheInvalidate( |
| IN gckOS Os, |
| IN gctUINT32 ProcessID, |
| IN gctPHYS_ADDR Handle, |
| IN gctUINT32 Physical, |
| IN gctPOINTER Logical, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| gcsPLATFORM * platform; |
| |
| gcmkHEADER_ARG("Os=0x%X ProcessID=%d Handle=0x%X Logical=0x%X Bytes=%lu", |
| Os, ProcessID, Handle, Logical, Bytes); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Logical != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| |
| platform = Os->device->platform; |
| |
| if (platform && platform->ops->cache) |
| { |
| platform->ops->cache( |
| platform, |
| ProcessID, |
| Handle, |
| Physical, |
| Logical, |
| Bytes, |
| gcvCACHE_INVALIDATE |
| ); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| #if !gcdCACHE_FUNCTION_UNIMPLEMENTED |
| #ifdef CONFIG_ARM |
| |
| /* Inner cache. */ |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) |
| dmac_map_area(Logical, Bytes, DMA_FROM_DEVICE); |
| # else |
| dmac_inv_range(Logical, Logical + Bytes); |
| # endif |
| |
| #if defined(CONFIG_OUTER_CACHE) |
| /* Outer cache. */ |
| #if gcdENABLE_OUTER_CACHE_PATCH |
| _HandleOuterCache(Os, Physical, Logical, Bytes, gcvCACHE_INVALIDATE); |
| #else |
| outer_inv_range((unsigned long) Handle, (unsigned long) Handle + Bytes); |
| #endif |
| #endif |
| |
| #elif defined(CONFIG_MIPS) |
| dma_cache_inv((unsigned long) Logical, Bytes); |
| #elif defined(CONFIG_PPC) |
| /* TODO */ |
| #else |
| dma_sync_single_for_device( |
| gcvNULL, |
| (dma_addr_t)Physical, |
| Bytes, |
| DMA_FROM_DEVICE); |
| #endif |
| #endif |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** gckOS_CacheFlush |
| ** |
| ** Clean the cache for the specified addresses and invalidate the lines as |
| ** well. The GPU is going to need and modify the data. If the system is |
| ** allocating memory as non-cachable, this function can be ignored. |
| ** |
| ** ARGUMENTS: |
| ** |
| ** gckOS Os |
| ** Pointer to gckOS object. |
| ** |
| ** gctUINT32 ProcessID |
| ** Process ID Logical belongs. |
| ** |
| ** gctPHYS_ADDR Handle |
| ** Physical address handle. If gcvNULL it is video memory. |
| ** |
| ** gctPOINTER Logical |
| ** Logical address to flush. |
| ** |
| ** gctSIZE_T Bytes |
| ** Size of the address range in bytes to flush. |
| */ |
| gceSTATUS |
| gckOS_CacheFlush( |
| IN gckOS Os, |
| IN gctUINT32 ProcessID, |
| IN gctPHYS_ADDR Handle, |
| IN gctUINT32 Physical, |
| IN gctPOINTER Logical, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| gcsPLATFORM * platform; |
| |
| gcmkHEADER_ARG("Os=0x%X ProcessID=%d Handle=0x%X Logical=0x%X Bytes=%lu", |
| Os, ProcessID, Handle, Logical, Bytes); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Logical != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| |
| platform = Os->device->platform; |
| |
| if (platform && platform->ops->cache) |
| { |
| platform->ops->cache( |
| platform, |
| ProcessID, |
| Handle, |
| Physical, |
| Logical, |
| Bytes, |
| gcvCACHE_FLUSH |
| ); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| #if !gcdCACHE_FUNCTION_UNIMPLEMENTED |
| #ifdef CONFIG_ARM |
| /* Inner cache. */ |
| dmac_flush_range(Logical, Logical + Bytes); |
| |
| #if defined(CONFIG_OUTER_CACHE) |
| /* Outer cache. */ |
| #if gcdENABLE_OUTER_CACHE_PATCH |
| _HandleOuterCache(Os, Physical, Logical, Bytes, gcvCACHE_FLUSH); |
| #else |
| outer_flush_range((unsigned long) Handle, (unsigned long) Handle + Bytes); |
| #endif |
| #endif |
| |
| #elif defined(CONFIG_MIPS) |
| dma_cache_wback_inv((unsigned long) Logical, Bytes); |
| #elif defined(CONFIG_PPC) |
| /* TODO */ |
| #else |
| dma_sync_single_for_device( |
| gcvNULL, |
| (dma_addr_t)Physical, |
| Bytes, |
| DMA_BIDIRECTIONAL); |
| #endif |
| #endif |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ********************************* Broadcasting ********************************* |
| *******************************************************************************/ |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_Broadcast |
| ** |
| ** System hook for broadcast events from the kernel driver. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to the gckOS object. |
| ** |
| ** gckHARDWARE Hardware |
| ** Pointer to the gckHARDWARE object. |
| ** |
| ** gceBROADCAST Reason |
| ** Reason for the broadcast. Can be one of the following values: |
| ** |
| ** gcvBROADCAST_GPU_IDLE |
| ** Broadcasted when the kernel driver thinks the GPU might be |
| ** idle. This can be used to handle power management. |
| ** |
| ** gcvBROADCAST_GPU_COMMIT |
| ** Broadcasted when any client process commits a command |
| ** buffer. This can be used to handle power management. |
| ** |
| ** gcvBROADCAST_GPU_STUCK |
| ** Broadcasted when the kernel driver hits the timeout waiting |
| ** for the GPU. |
| ** |
| ** gcvBROADCAST_FIRST_PROCESS |
| ** First process is trying to connect to the kernel. |
| ** |
| ** gcvBROADCAST_LAST_PROCESS |
| ** Last process has detached from the kernel. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_Broadcast( |
| IN gckOS Os, |
| IN gckHARDWARE Hardware, |
| IN gceBROADCAST Reason |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=0x%X Hardware=0x%X Reason=%d", Os, Hardware, Reason); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); |
| |
| switch (Reason) |
| { |
| case gcvBROADCAST_FIRST_PROCESS: |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_OS, "First process has attached"); |
| break; |
| |
| case gcvBROADCAST_LAST_PROCESS: |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_OS, "Last process has detached"); |
| |
| /* Put GPU OFF. */ |
| gcmkONERROR( |
| gckHARDWARE_SetPowerManagementState(Hardware, |
| gcvPOWER_OFF_BROADCAST)); |
| break; |
| |
| case gcvBROADCAST_GPU_IDLE: |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_OS, "GPU idle."); |
| |
| /* Put GPU IDLE. */ |
| gcmkONERROR( |
| gckHARDWARE_SetPowerManagementState(Hardware, |
| #if gcdPOWER_SUSPEND_WHEN_IDLE |
| gcvPOWER_SUSPEND_BROADCAST)); |
| #else |
| gcvPOWER_IDLE_BROADCAST)); |
| #endif |
| |
| /* Add idle process DB. */ |
| gcmkONERROR(gckKERNEL_AddProcessDB(Hardware->kernel, |
| 1, |
| gcvDB_IDLE, |
| gcvNULL, gcvNULL, 0)); |
| break; |
| |
| case gcvBROADCAST_GPU_COMMIT: |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_OS, "COMMIT has arrived."); |
| |
| /* Add busy process DB. */ |
| gcmkONERROR(gckKERNEL_AddProcessDB(Hardware->kernel, |
| 0, |
| gcvDB_IDLE, |
| gcvNULL, gcvNULL, 0)); |
| |
| /* Put GPU ON. */ |
| gcmkONERROR( |
| gckHARDWARE_SetPowerManagementState(Hardware, gcvPOWER_ON_AUTO)); |
| break; |
| |
| case gcvBROADCAST_GPU_STUCK: |
| gcmkTRACE_N(gcvLEVEL_ERROR, 0, "gcvBROADCAST_GPU_STUCK\n"); |
| gcmkONERROR(gckKERNEL_Recovery(Hardware->kernel)); |
| break; |
| |
| case gcvBROADCAST_AXI_BUS_ERROR: |
| gcmkTRACE_N(gcvLEVEL_ERROR, 0, "gcvBROADCAST_AXI_BUS_ERROR\n"); |
| gcmkONERROR(gckHARDWARE_DumpGPUState(Hardware)); |
| gcmkONERROR(gckKERNEL_Recovery(Hardware->kernel)); |
| break; |
| |
| case gcvBROADCAST_OUT_OF_MEMORY: |
| gcmkTRACE_N(gcvLEVEL_INFO, 0, "gcvBROADCAST_OUT_OF_MEMORY\n"); |
| |
| status = _ShrinkMemory(Os); |
| |
| if (status == gcvSTATUS_NOT_SUPPORTED) |
| { |
| goto OnError; |
| } |
| |
| gcmkONERROR(status); |
| |
| break; |
| |
| default: |
| /* Skip unimplemented broadcast. */ |
| break; |
| } |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_BroadcastHurry |
| ** |
| ** The GPU is running too slow. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to the gckOS object. |
| ** |
| ** gckHARDWARE Hardware |
| ** Pointer to the gckHARDWARE object. |
| ** |
| ** gctUINT Urgency |
| ** The higher the number, the higher the urgency to speed up the GPU. |
| ** The maximum value is defined by the gcdDYNAMIC_EVENT_THRESHOLD. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_BroadcastHurry( |
| IN gckOS Os, |
| IN gckHARDWARE Hardware, |
| IN gctUINT Urgency |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%x Hardware=0x%x Urgency=%u", Os, Hardware, Urgency); |
| |
| /* Do whatever you need to do to speed up the GPU now. */ |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_BroadcastCalibrateSpeed |
| ** |
| ** Calibrate the speed of the GPU. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to the gckOS object. |
| ** |
| ** gckHARDWARE Hardware |
| ** Pointer to the gckHARDWARE object. |
| ** |
| ** gctUINT Idle, Time |
| ** Idle/Time will give the percentage the GPU is idle, so you can use |
| ** this to calibrate the working point of the GPU. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_BroadcastCalibrateSpeed( |
| IN gckOS Os, |
| IN gckHARDWARE Hardware, |
| IN gctUINT Idle, |
| IN gctUINT Time |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%x Hardware=0x%x Idle=%u Time=%u", |
| Os, Hardware, Idle, Time); |
| |
| /* Do whatever you need to do to callibrate the GPU speed. */ |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ********************************** Semaphores ********************************** |
| *******************************************************************************/ |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_CreateSemaphore |
| ** |
| ** Create a semaphore. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to the gckOS object. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctPOINTER * Semaphore |
| ** Pointer to the variable that will receive the created semaphore. |
| */ |
| gceSTATUS |
| gckOS_CreateSemaphore( |
| IN gckOS Os, |
| OUT gctPOINTER * Semaphore |
| ) |
| { |
| gceSTATUS status; |
| struct semaphore *sem = gcvNULL; |
| |
| gcmkHEADER_ARG("Os=0x%X", Os); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL); |
| |
| /* Allocate the semaphore structure. */ |
| sem = (struct semaphore *)kmalloc(gcmSIZEOF(struct semaphore), GFP_KERNEL | gcdNOWARN); |
| if (sem == gcvNULL) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| /* Initialize the semaphore. */ |
| sema_init(sem, 1); |
| |
| /* Return to caller. */ |
| *Semaphore = (gctPOINTER) sem; |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_AcquireSemaphore |
| ** |
| ** Acquire a semaphore. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to the gckOS object. |
| ** |
| ** gctPOINTER Semaphore |
| ** Pointer to the semaphore thet needs to be acquired. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_AcquireSemaphore( |
| IN gckOS Os, |
| IN gctPOINTER Semaphore |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=0x%08X Semaphore=0x%08X", Os, Semaphore); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL); |
| |
| /* Acquire the semaphore. */ |
| if (down_interruptible((struct semaphore *) Semaphore)) |
| { |
| gcmkONERROR(gcvSTATUS_INTERRUPTED); |
| } |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_TryAcquireSemaphore |
| ** |
| ** Try to acquire a semaphore. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to the gckOS object. |
| ** |
| ** gctPOINTER Semaphore |
| ** Pointer to the semaphore thet needs to be acquired. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_TryAcquireSemaphore( |
| IN gckOS Os, |
| IN gctPOINTER Semaphore |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=0x%x", Os); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL); |
| |
| /* Acquire the semaphore. */ |
| if (down_trylock((struct semaphore *) Semaphore)) |
| { |
| /* Timeout. */ |
| status = gcvSTATUS_TIMEOUT; |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_ReleaseSemaphore |
| ** |
| ** Release a previously acquired semaphore. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to the gckOS object. |
| ** |
| ** gctPOINTER Semaphore |
| ** Pointer to the semaphore thet needs to be released. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_ReleaseSemaphore( |
| IN gckOS Os, |
| IN gctPOINTER Semaphore |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Semaphore=0x%X", Os, Semaphore); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL); |
| |
| /* Release the semaphore. */ |
| up((struct semaphore *) Semaphore); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_DestroySemaphore |
| ** |
| ** Destroy a semaphore. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to the gckOS object. |
| ** |
| ** gctPOINTER Semaphore |
| ** Pointer to the semaphore thet needs to be destroyed. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_DestroySemaphore( |
| IN gckOS Os, |
| IN gctPOINTER Semaphore |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Semaphore=0x%X", Os, Semaphore); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL); |
| |
| /* Free the sempahore structure. */ |
| kfree(Semaphore); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_GetProcessID |
| ** |
| ** Get current process ID. |
| ** |
| ** INPUT: |
| ** |
| ** Nothing. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctUINT32_PTR ProcessID |
| ** Pointer to the variable that receives the process ID. |
| */ |
| gceSTATUS |
| gckOS_GetProcessID( |
| OUT gctUINT32_PTR ProcessID |
| ) |
| { |
| /* Get process ID. */ |
| if (ProcessID != gcvNULL) |
| { |
| *ProcessID = _GetProcessID(); |
| } |
| |
| /* Success. */ |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_GetThreadID |
| ** |
| ** Get current thread ID. |
| ** |
| ** INPUT: |
| ** |
| ** Nothing. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctUINT32_PTR ThreadID |
| ** Pointer to the variable that receives the thread ID. |
| */ |
| gceSTATUS |
| gckOS_GetThreadID( |
| OUT gctUINT32_PTR ThreadID |
| ) |
| { |
| /* Get thread ID. */ |
| if (ThreadID != gcvNULL) |
| { |
| *ThreadID = _GetThreadID(); |
| } |
| |
| /* Success. */ |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_SetGPUPower |
| ** |
| ** Set the power of the GPU on or off. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to a gckOS object. |
| ** |
| ** gceCORE Core |
| ** GPU whose power is set. |
| ** |
| ** gctBOOL Clock |
| ** gcvTRUE to turn on the clock, or gcvFALSE to turn off the clock. |
| ** |
| ** gctBOOL Power |
| ** gcvTRUE to turn on the power, or gcvFALSE to turn off the power. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_SetGPUPower( |
| IN gckOS Os, |
| IN gceCORE Core, |
| IN gctBOOL Clock, |
| IN gctBOOL Power |
| ) |
| { |
| gcsPLATFORM * platform; |
| |
| gctBOOL powerChange = gcvFALSE; |
| gctBOOL clockChange = gcvFALSE; |
| |
| gcmkHEADER_ARG("Os=0x%X Core=%d Clock=%d Power=%d", Os, Core, Clock, Power); |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| |
| platform = Os->device->platform; |
| |
| powerChange = (Power != Os->powerStates[Core]); |
| |
| clockChange = (Clock != Os->clockStates[Core]); |
| |
| if (powerChange && (Power == gcvTRUE)) |
| { |
| if (platform && platform->ops->setPower) |
| { |
| gcmkVERIFY_OK(platform->ops->setPower(platform, Core, Power)); |
| } |
| |
| Os->powerStates[Core] = Power; |
| } |
| |
| if (clockChange) |
| { |
| mutex_lock(&Os->registerAccessLocks[Core]); |
| |
| if (platform && platform->ops->setClock) |
| { |
| gcmkVERIFY_OK(platform->ops->setClock(platform, Core, Clock)); |
| } |
| |
| Os->clockStates[Core] = Clock; |
| |
| mutex_unlock(&Os->registerAccessLocks[Core]); |
| } |
| |
| if (powerChange && (Power == gcvFALSE)) |
| { |
| if (platform && platform->ops->setPower) |
| { |
| gcmkVERIFY_OK(platform->ops->setPower(platform, Core, Power)); |
| } |
| |
| Os->powerStates[Core] = Power; |
| } |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_ResetGPU |
| ** |
| ** Reset the GPU. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to a gckOS object. |
| ** |
| ** gckCORE Core |
| ** GPU whose power is set. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_ResetGPU( |
| IN gckOS Os, |
| IN gceCORE Core |
| ) |
| { |
| gceSTATUS status = gcvSTATUS_NOT_SUPPORTED; |
| gcsPLATFORM * platform; |
| |
| gcmkHEADER_ARG("Os=0x%X Core=%d", Os, Core); |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| |
| platform = Os->device->platform; |
| |
| if (platform && platform->ops->reset) |
| { |
| status = platform->ops->reset(platform, Core); |
| } |
| |
| gcmkFOOTER_NO(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_PrepareGPUFrequency |
| ** |
| ** Prepare to set GPU frequency and voltage. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to a gckOS object. |
| ** |
| ** gckCORE Core |
| ** GPU whose frequency and voltage will be set. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_PrepareGPUFrequency( |
| IN gckOS Os, |
| IN gceCORE Core |
| ) |
| { |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_FinishGPUFrequency |
| ** |
| ** Finish GPU frequency setting. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to a gckOS object. |
| ** |
| ** gckCORE Core |
| ** GPU whose frequency and voltage is set. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_FinishGPUFrequency( |
| IN gckOS Os, |
| IN gceCORE Core |
| ) |
| { |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_QueryGPUFrequency |
| ** |
| ** Query the current frequency of the GPU. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to a gckOS object. |
| ** |
| ** gckCORE Core |
| ** GPU whose power is set. |
| ** |
| ** gctUINT32 * Frequency |
| ** Pointer to a gctUINT32 to obtain current frequency, in MHz. |
| ** |
| ** gctUINT8 * Scale |
| ** Pointer to a gctUINT8 to obtain current scale(1 - 64). |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_QueryGPUFrequency( |
| IN gckOS Os, |
| IN gceCORE Core, |
| OUT gctUINT32 * Frequency, |
| OUT gctUINT8 * Scale |
| ) |
| { |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_SetGPUFrequency |
| ** |
| ** Set frequency and voltage of the GPU. |
| ** |
| ** 1. DVFS manager gives the target scale of full frequency, BSP must find |
| ** a real frequency according to this scale and board's configure. |
| ** |
| ** 2. BSP should find a suitable voltage for this frequency. |
| ** |
| ** 3. BSP must make sure setting take effect before this function returns. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to a gckOS object. |
| ** |
| ** gckCORE Core |
| ** GPU whose power is set. |
| ** |
| ** gctUINT8 Scale |
| ** Target scale of full frequency, range is [1, 64]. 1 means 1/64 of |
| ** full frequency and 64 means 64/64 of full frequency. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_SetGPUFrequency( |
| IN gckOS Os, |
| IN gceCORE Core, |
| IN gctUINT8 Scale |
| ) |
| { |
| return gcvSTATUS_OK; |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*----- Profile --------------------------------------------------------------*/ |
| |
| gceSTATUS |
| gckOS_GetProfileTick( |
| OUT gctUINT64_PTR Tick |
| ) |
| { |
| struct timespec time; |
| |
| ktime_get_ts(&time); |
| |
| *Tick = time.tv_nsec + time.tv_sec * 1000000000ULL; |
| |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_QueryProfileTickRate( |
| OUT gctUINT64_PTR TickRate |
| ) |
| { |
| struct timespec res; |
| |
| hrtimer_get_res(CLOCK_MONOTONIC, &res); |
| |
| *TickRate = res.tv_nsec + res.tv_sec * 1000000000ULL; |
| |
| return gcvSTATUS_OK; |
| } |
| |
| gctUINT32 |
| gckOS_ProfileToMS( |
| IN gctUINT64 Ticks |
| ) |
| { |
| #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23) |
| return div_u64(Ticks, 1000000); |
| #else |
| gctUINT64 rem = Ticks; |
| gctUINT64 b = 1000000; |
| gctUINT64 res, d = 1; |
| gctUINT32 high = rem >> 32; |
| |
| /* Reduce the thing a bit first */ |
| res = 0; |
| if (high >= 1000000) |
| { |
| high /= 1000000; |
| res = (gctUINT64) high << 32; |
| rem -= (gctUINT64) (high * 1000000) << 32; |
| } |
| |
| while (((gctINT64) b > 0) && (b < rem)) |
| { |
| b <<= 1; |
| d <<= 1; |
| } |
| |
| do |
| { |
| if (rem >= b) |
| { |
| rem -= b; |
| res += d; |
| } |
| |
| b >>= 1; |
| d >>= 1; |
| } |
| while (d); |
| |
| return (gctUINT32) res; |
| #endif |
| } |
| |
| /******************************************************************************\ |
| ******************************* Signal Management ****************************** |
| \******************************************************************************/ |
| |
| #undef _GC_OBJ_ZONE |
| #define _GC_OBJ_ZONE gcvZONE_SIGNAL |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_CreateSignal |
| ** |
| ** Create a new signal. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctBOOL ManualReset |
| ** If set to gcvTRUE, gckOS_Signal with gcvFALSE must be called in |
| ** order to set the signal to nonsignaled state. |
| ** If set to gcvFALSE, the signal will automatically be set to |
| ** nonsignaled state by gckOS_WaitSignal function. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctSIGNAL * Signal |
| ** Pointer to a variable receiving the created gctSIGNAL. |
| */ |
| gceSTATUS |
| gckOS_CreateSignal( |
| IN gckOS Os, |
| IN gctBOOL ManualReset, |
| OUT gctSIGNAL * Signal |
| ) |
| { |
| gceSTATUS status; |
| gcsSIGNAL_PTR signal; |
| |
| gcmkHEADER_ARG("Os=0x%X ManualReset=%d", Os, ManualReset); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Signal != gcvNULL); |
| |
| /* Create an event structure. */ |
| signal = (gcsSIGNAL_PTR) kmalloc(sizeof(gcsSIGNAL), GFP_KERNEL | gcdNOWARN); |
| |
| if (signal == gcvNULL) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| /* Save the process ID. */ |
| signal->process = (gctHANDLE)(gctUINTPTR_T) _GetProcessID(); |
| signal->manualReset = ManualReset; |
| signal->hardware = gcvNULL; |
| init_completion(&signal->obj); |
| atomic_set(&signal->ref, 1); |
| |
| gcmkONERROR(_AllocateIntegerId(&Os->signalDB, signal, &signal->id)); |
| |
| *Signal = (gctSIGNAL)(gctUINTPTR_T)signal->id; |
| |
| gcmkFOOTER_ARG("*Signal=0x%X", *Signal); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (signal != gcvNULL) |
| { |
| kfree(signal); |
| } |
| |
| gcmkFOOTER_NO(); |
| return status; |
| } |
| |
| gceSTATUS |
| gckOS_SignalQueryHardware( |
| IN gckOS Os, |
| IN gctSIGNAL Signal, |
| OUT gckHARDWARE * Hardware |
| ) |
| { |
| gceSTATUS status; |
| gcsSIGNAL_PTR signal; |
| |
| gcmkHEADER_ARG("Os=0x%X Signal=0x%X Hardware=0x%X", Os, Signal, Hardware); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Signal != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Hardware != gcvNULL); |
| |
| gcmkONERROR(_QueryIntegerId(&Os->signalDB, (gctUINT32)(gctUINTPTR_T)Signal, (gctPOINTER)&signal)); |
| |
| *Hardware = signal->hardware; |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| gceSTATUS |
| gckOS_SignalSetHardware( |
| IN gckOS Os, |
| IN gctSIGNAL Signal, |
| IN gckHARDWARE Hardware |
| ) |
| { |
| gceSTATUS status; |
| gcsSIGNAL_PTR signal; |
| |
| gcmkHEADER_ARG("Os=0x%X Signal=0x%X Hardware=0x%X", Os, Signal, Hardware); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Signal != gcvNULL); |
| |
| gcmkONERROR(_QueryIntegerId(&Os->signalDB, (gctUINT32)(gctUINTPTR_T)Signal, (gctPOINTER)&signal)); |
| |
| signal->hardware = Hardware; |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_DestroySignal |
| ** |
| ** Destroy a signal. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctSIGNAL Signal |
| ** Pointer to the gctSIGNAL. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_DestroySignal( |
| IN gckOS Os, |
| IN gctSIGNAL Signal |
| ) |
| { |
| gceSTATUS status; |
| gcsSIGNAL_PTR signal; |
| gctBOOL acquired = gcvFALSE; |
| |
| gcmkHEADER_ARG("Os=0x%X Signal=0x%X", Os, Signal); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Signal != gcvNULL); |
| |
| gcmkONERROR(gckOS_AcquireMutex(Os, Os->signalMutex, gcvINFINITE)); |
| acquired = gcvTRUE; |
| |
| gcmkONERROR(_QueryIntegerId(&Os->signalDB, (gctUINT32)(gctUINTPTR_T)Signal, (gctPOINTER)&signal)); |
| |
| gcmkASSERT(signal->id == (gctUINT32)(gctUINTPTR_T)Signal); |
| |
| if (atomic_dec_and_test(&signal->ref)) |
| { |
| gcmkVERIFY_OK(_DestroyIntegerId(&Os->signalDB, signal->id)); |
| |
| /* Free the sgianl. */ |
| kfree(signal); |
| } |
| |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Os, Os->signalMutex)); |
| acquired = gcvFALSE; |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (acquired) |
| { |
| /* Release the mutex. */ |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Os, Os->signalMutex)); |
| } |
| |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_Signal |
| ** |
| ** Set a state of the specified signal. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctSIGNAL Signal |
| ** Pointer to the gctSIGNAL. |
| ** |
| ** gctBOOL State |
| ** If gcvTRUE, the signal will be set to signaled state. |
| ** If gcvFALSE, the signal will be set to nonsignaled state. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_Signal( |
| IN gckOS Os, |
| IN gctSIGNAL Signal, |
| IN gctBOOL State |
| ) |
| { |
| gceSTATUS status; |
| gcsSIGNAL_PTR signal; |
| gctBOOL acquired = gcvFALSE; |
| |
| gcmkHEADER_ARG("Os=0x%X Signal=0x%X State=%d", Os, Signal, State); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Signal != gcvNULL); |
| |
| gcmkONERROR(gckOS_AcquireMutex(Os, Os->signalMutex, gcvINFINITE)); |
| acquired = gcvTRUE; |
| |
| gcmkONERROR(_QueryIntegerId(&Os->signalDB, (gctUINT32)(gctUINTPTR_T)Signal, (gctPOINTER)&signal)); |
| |
| gcmkASSERT(signal->id == (gctUINT32)(gctUINTPTR_T)Signal); |
| |
| if (State) |
| { |
| /* unbind the signal from hardware. */ |
| signal->hardware = gcvNULL; |
| |
| /* Set the event to a signaled state. */ |
| complete(&signal->obj); |
| } |
| else |
| { |
| /* Set the event to an unsignaled state. */ |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0) |
| reinit_completion(&signal->obj); |
| #else |
| INIT_COMPLETION(signal->obj); |
| #endif |
| } |
| |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Os, Os->signalMutex)); |
| acquired = gcvFALSE; |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (acquired) |
| { |
| /* Release the mutex. */ |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Os, Os->signalMutex)); |
| } |
| |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| #if gcdENABLE_VG |
| gceSTATUS |
| gckOS_SetSignalVG( |
| IN gckOS Os, |
| IN gctHANDLE Process, |
| IN gctSIGNAL Signal |
| ) |
| { |
| gceSTATUS status; |
| gctINT result; |
| struct task_struct * userTask; |
| struct siginfo info; |
| |
| userTask = FIND_TASK_BY_PID((pid_t)(gctUINTPTR_T) Process); |
| |
| if (userTask != gcvNULL) |
| { |
| info.si_signo = 48; |
| info.si_code = __SI_CODE(__SI_RT, SI_KERNEL); |
| info.si_pid = 0; |
| info.si_uid = 0; |
| info.si_ptr = (gctPOINTER) Signal; |
| |
| /* Signals with numbers between 32 and 63 are real-time, |
| send a real-time signal to the user process. */ |
| result = send_sig_info(48, &info, userTask); |
| |
| printk("gckOS_SetSignalVG:0x%x\n", result); |
| /* Error? */ |
| if (result < 0) |
| { |
| status = gcvSTATUS_GENERIC_IO; |
| |
| gcmkTRACE( |
| gcvLEVEL_ERROR, |
| "%s(%d): an error has occurred.\n", |
| __FUNCTION__, __LINE__ |
| ); |
| } |
| else |
| { |
| status = gcvSTATUS_OK; |
| } |
| } |
| else |
| { |
| status = gcvSTATUS_GENERIC_IO; |
| |
| gcmkTRACE( |
| gcvLEVEL_ERROR, |
| "%s(%d): an error has occurred.\n", |
| __FUNCTION__, __LINE__ |
| ); |
| } |
| |
| /* Return status. */ |
| return status; |
| } |
| #endif |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_UserSignal |
| ** |
| ** Set the specified signal which is owned by a process to signaled state. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctSIGNAL Signal |
| ** Pointer to the gctSIGNAL. |
| ** |
| ** gctHANDLE Process |
| ** Handle of process owning the signal. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_UserSignal( |
| IN gckOS Os, |
| IN gctSIGNAL Signal, |
| IN gctHANDLE Process |
| ) |
| { |
| gceSTATUS status; |
| gctSIGNAL signal; |
| |
| gcmkHEADER_ARG("Os=0x%X Signal=0x%X Process=%d", |
| Os, Signal, (gctINT32)(gctUINTPTR_T)Process); |
| |
| /* Map the signal into kernel space. */ |
| gcmkONERROR(gckOS_MapSignal(Os, Signal, Process, &signal)); |
| |
| /* Signal. */ |
| status = gckOS_Signal(Os, signal, gcvTRUE); |
| |
| /* Unmap the signal */ |
| gcmkVERIFY_OK(gckOS_UnmapSignal(Os, Signal)); |
| |
| gcmkFOOTER(); |
| return status; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_WaitSignal |
| ** |
| ** Wait for a signal to become signaled. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctSIGNAL Signal |
| ** Pointer to the gctSIGNAL. |
| ** |
| ** gctUINT32 Wait |
| ** Number of milliseconds to wait. |
| ** Pass the value of gcvINFINITE for an infinite wait. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_WaitSignal( |
| IN gckOS Os, |
| IN gctSIGNAL Signal, |
| IN gctUINT32 Wait |
| ) |
| { |
| gceSTATUS status = gcvSTATUS_OK; |
| gcsSIGNAL_PTR signal; |
| |
| gcmkHEADER_ARG("Os=0x%X Signal=0x%X Wait=0x%08X", Os, Signal, Wait); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Signal != gcvNULL); |
| |
| gcmkONERROR(_QueryIntegerId(&Os->signalDB, (gctUINT32)(gctUINTPTR_T)Signal, (gctPOINTER)&signal)); |
| |
| gcmkASSERT(signal->id == (gctUINT32)(gctUINTPTR_T)Signal); |
| |
| might_sleep(); |
| |
| spin_lock_irq(&signal->obj.wait.lock); |
| |
| if (signal->obj.done) |
| { |
| if (!signal->manualReset) |
| { |
| signal->obj.done = 0; |
| } |
| |
| status = gcvSTATUS_OK; |
| } |
| else if (Wait == 0) |
| { |
| status = gcvSTATUS_TIMEOUT; |
| } |
| else |
| { |
| /* Convert wait to milliseconds. */ |
| long timeout = (Wait == gcvINFINITE) |
| ? MAX_SCHEDULE_TIMEOUT |
| : Wait * HZ / 1000; |
| |
| DECLARE_WAITQUEUE(wait, current); |
| wait.flags |= WQ_FLAG_EXCLUSIVE; |
| __add_wait_queue_tail(&signal->obj.wait, &wait); |
| |
| while (gcvTRUE) |
| { |
| if (signal_pending(current)) |
| { |
| /* Interrupt received. */ |
| status = gcvSTATUS_INTERRUPTED; |
| break; |
| } |
| |
| __set_current_state(TASK_INTERRUPTIBLE); |
| spin_unlock_irq(&signal->obj.wait.lock); |
| timeout = schedule_timeout(timeout); |
| spin_lock_irq(&signal->obj.wait.lock); |
| |
| if (signal->obj.done) |
| { |
| if (!signal->manualReset) |
| { |
| signal->obj.done = 0; |
| } |
| |
| status = gcvSTATUS_OK; |
| break; |
| } |
| |
| if (timeout == 0) |
| { |
| |
| status = gcvSTATUS_TIMEOUT; |
| break; |
| } |
| } |
| |
| __remove_wait_queue(&signal->obj.wait, &wait); |
| } |
| |
| spin_unlock_irq(&signal->obj.wait.lock); |
| |
| OnError: |
| /* Return status. */ |
| gcmkFOOTER_ARG("Signal=0x%X status=%d", Signal, status); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_MapSignal |
| ** |
| ** Map a signal in to the current process space. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctSIGNAL Signal |
| ** Pointer to tha gctSIGNAL to map. |
| ** |
| ** gctHANDLE Process |
| ** Handle of process owning the signal. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctSIGNAL * MappedSignal |
| ** Pointer to a variable receiving the mapped gctSIGNAL. |
| */ |
| gceSTATUS |
| gckOS_MapSignal( |
| IN gckOS Os, |
| IN gctSIGNAL Signal, |
| IN gctHANDLE Process, |
| OUT gctSIGNAL * MappedSignal |
| ) |
| { |
| gceSTATUS status; |
| gcsSIGNAL_PTR signal; |
| gcmkHEADER_ARG("Os=0x%X Signal=0x%X Process=0x%X", Os, Signal, Process); |
| |
| gcmkVERIFY_ARGUMENT(Signal != gcvNULL); |
| gcmkVERIFY_ARGUMENT(MappedSignal != gcvNULL); |
| |
| gcmkONERROR(_QueryIntegerId(&Os->signalDB, (gctUINT32)(gctUINTPTR_T)Signal, (gctPOINTER)&signal)); |
| |
| if(atomic_inc_return(&signal->ref) <= 1) |
| { |
| /* The previous value is 0, it has been deleted. */ |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| |
| *MappedSignal = (gctSIGNAL) Signal; |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*MappedSignal=0x%X", *MappedSignal); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| gcmkFOOTER_NO(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_UnmapSignal |
| ** |
| ** Unmap a signal . |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctSIGNAL Signal |
| ** Pointer to that gctSIGNAL mapped. |
| */ |
| gceSTATUS |
| gckOS_UnmapSignal( |
| IN gckOS Os, |
| IN gctSIGNAL Signal |
| ) |
| { |
| return gckOS_DestroySignal(Os, Signal); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_CreateUserSignal |
| ** |
| ** Create a new signal to be used in the user space. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctBOOL ManualReset |
| ** If set to gcvTRUE, gckOS_Signal with gcvFALSE must be called in |
| ** order to set the signal to nonsignaled state. |
| ** If set to gcvFALSE, the signal will automatically be set to |
| ** nonsignaled state by gckOS_WaitSignal function. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctINT * SignalID |
| ** Pointer to a variable receiving the created signal's ID. |
| */ |
| gceSTATUS |
| gckOS_CreateUserSignal( |
| IN gckOS Os, |
| IN gctBOOL ManualReset, |
| OUT gctINT * SignalID |
| ) |
| { |
| gceSTATUS status; |
| gctSIZE_T signal; |
| |
| /* Create a new signal. */ |
| gcmkONERROR(gckOS_CreateSignal(Os, ManualReset, (gctSIGNAL *) &signal)); |
| *SignalID = (gctINT) signal; |
| |
| OnError: |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_DestroyUserSignal |
| ** |
| ** Destroy a signal to be used in the user space. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctINT SignalID |
| ** The signal's ID. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_DestroyUserSignal( |
| IN gckOS Os, |
| IN gctINT SignalID |
| ) |
| { |
| return gckOS_DestroySignal(Os, (gctSIGNAL)(gctUINTPTR_T)SignalID); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_WaitUserSignal |
| ** |
| ** Wait for a signal used in the user mode to become signaled. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctINT SignalID |
| ** Signal ID. |
| ** |
| ** gctUINT32 Wait |
| ** Number of milliseconds to wait. |
| ** Pass the value of gcvINFINITE for an infinite wait. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_WaitUserSignal( |
| IN gckOS Os, |
| IN gctINT SignalID, |
| IN gctUINT32 Wait |
| ) |
| { |
| return gckOS_WaitSignal(Os, (gctSIGNAL)(gctUINTPTR_T)SignalID, Wait); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_SignalUserSignal |
| ** |
| ** Set a state of the specified signal to be used in the user space. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctINT SignalID |
| ** SignalID. |
| ** |
| ** gctBOOL State |
| ** If gcvTRUE, the signal will be set to signaled state. |
| ** If gcvFALSE, the signal will be set to nonsignaled state. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_SignalUserSignal( |
| IN gckOS Os, |
| IN gctINT SignalID, |
| IN gctBOOL State |
| ) |
| { |
| return gckOS_Signal(Os, (gctSIGNAL)(gctUINTPTR_T)SignalID, State); |
| } |
| |
| #if gcdENABLE_VG |
| gceSTATUS |
| gckOS_CreateSemaphoreVG( |
| IN gckOS Os, |
| OUT gctSEMAPHORE * Semaphore |
| ) |
| { |
| gceSTATUS status; |
| struct semaphore * newSemaphore; |
| |
| gcmkHEADER_ARG("Os=0x%X Semaphore=0x%x", Os, Semaphore); |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL); |
| |
| do |
| { |
| /* Allocate the semaphore structure. */ |
| newSemaphore = (struct semaphore *)kmalloc(gcmSIZEOF(struct semaphore), GFP_KERNEL | gcdNOWARN); |
| if (newSemaphore == gcvNULL) |
| { |
| gcmkERR_BREAK(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| /* Initialize the semaphore. */ |
| sema_init(newSemaphore, 0); |
| |
| /* Set the handle. */ |
| * Semaphore = (gctSEMAPHORE) newSemaphore; |
| |
| /* Success. */ |
| status = gcvSTATUS_OK; |
| } |
| while (gcvFALSE); |
| |
| gcmkFOOTER(); |
| /* Return the status. */ |
| return status; |
| } |
| |
| |
| gceSTATUS |
| gckOS_IncrementSemaphore( |
| IN gckOS Os, |
| IN gctSEMAPHORE Semaphore |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Semaphore=0x%x", Os, Semaphore); |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL); |
| |
| /* Increment the semaphore's count. */ |
| up((struct semaphore *) Semaphore); |
| |
| gcmkFOOTER_NO(); |
| /* Success. */ |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_DecrementSemaphore( |
| IN gckOS Os, |
| IN gctSEMAPHORE Semaphore |
| ) |
| { |
| gceSTATUS status; |
| gctINT result; |
| |
| gcmkHEADER_ARG("Os=0x%X Semaphore=0x%x", Os, Semaphore); |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL); |
| |
| do |
| { |
| /* Decrement the semaphore's count. If the count is zero, wait |
| until it gets incremented. */ |
| result = down_interruptible((struct semaphore *) Semaphore); |
| |
| /* Signal received? */ |
| if (result != 0) |
| { |
| status = gcvSTATUS_TERMINATE; |
| break; |
| } |
| |
| /* Success. */ |
| status = gcvSTATUS_OK; |
| } |
| while (gcvFALSE); |
| |
| gcmkFOOTER(); |
| /* Return the status. */ |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_SetSignal |
| ** |
| ** Set the specified signal to signaled state. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to the gckOS object. |
| ** |
| ** gctHANDLE Process |
| ** Handle of process owning the signal. |
| ** |
| ** gctSIGNAL Signal |
| ** Pointer to the gctSIGNAL. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_SetSignal( |
| IN gckOS Os, |
| IN gctHANDLE Process, |
| IN gctSIGNAL Signal |
| ) |
| { |
| gceSTATUS status; |
| gctINT result; |
| struct task_struct * userTask; |
| struct siginfo info; |
| |
| userTask = FIND_TASK_BY_PID((pid_t)(gctUINTPTR_T) Process); |
| |
| if (userTask != gcvNULL) |
| { |
| info.si_signo = 48; |
| info.si_code = __SI_CODE(__SI_RT, SI_KERNEL); |
| info.si_pid = 0; |
| info.si_uid = 0; |
| info.si_ptr = (gctPOINTER) Signal; |
| |
| /* Signals with numbers between 32 and 63 are real-time, |
| send a real-time signal to the user process. */ |
| result = send_sig_info(48, &info, userTask); |
| |
| /* Error? */ |
| if (result < 0) |
| { |
| status = gcvSTATUS_GENERIC_IO; |
| |
| gcmkTRACE( |
| gcvLEVEL_ERROR, |
| "%s(%d): an error has occurred.\n", |
| __FUNCTION__, __LINE__ |
| ); |
| } |
| else |
| { |
| status = gcvSTATUS_OK; |
| } |
| } |
| else |
| { |
| status = gcvSTATUS_GENERIC_IO; |
| |
| gcmkTRACE( |
| gcvLEVEL_ERROR, |
| "%s(%d): an error has occurred.\n", |
| __FUNCTION__, __LINE__ |
| ); |
| } |
| |
| /* Return status. */ |
| return status; |
| } |
| |
| /******************************************************************************\ |
| ******************************** Thread Object ********************************* |
| \******************************************************************************/ |
| |
| gceSTATUS |
| gckOS_StartThread( |
| IN gckOS Os, |
| IN gctTHREADFUNC ThreadFunction, |
| IN gctPOINTER ThreadParameter, |
| OUT gctTHREAD * Thread |
| ) |
| { |
| gceSTATUS status; |
| struct task_struct * thread; |
| |
| gcmkHEADER_ARG("Os=0x%X ", Os); |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(ThreadFunction != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Thread != gcvNULL); |
| |
| do |
| { |
| /* Create the thread. */ |
| thread = kthread_create( |
| ThreadFunction, |
| ThreadParameter, |
| "Vivante Kernel Thread" |
| ); |
| |
| /* Failed? */ |
| if (IS_ERR(thread)) |
| { |
| status = gcvSTATUS_GENERIC_IO; |
| break; |
| } |
| |
| /* Start the thread. */ |
| wake_up_process(thread); |
| |
| /* Set the thread handle. */ |
| * Thread = (gctTHREAD) thread; |
| |
| /* Success. */ |
| status = gcvSTATUS_OK; |
| } |
| while (gcvFALSE); |
| |
| gcmkFOOTER(); |
| /* Return the status. */ |
| return status; |
| } |
| |
| gceSTATUS |
| gckOS_StopThread( |
| IN gckOS Os, |
| IN gctTHREAD Thread |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Thread=0x%x", Os, Thread); |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Thread != gcvNULL); |
| |
| /* Thread should have already been enabled to terminate. */ |
| kthread_stop((struct task_struct *) Thread); |
| |
| gcmkFOOTER_NO(); |
| /* Success. */ |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_VerifyThread( |
| IN gckOS Os, |
| IN gctTHREAD Thread |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Thread=0x%x", Os, Thread); |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Thread != gcvNULL); |
| |
| gcmkFOOTER_NO(); |
| /* Success. */ |
| return gcvSTATUS_OK; |
| } |
| #endif |
| |
| /******************************************************************************\ |
| ******************************** Software Timer ******************************** |
| \******************************************************************************/ |
| |
| void |
| _TimerFunction( |
| struct work_struct * work |
| ) |
| { |
| gcsOSTIMER_PTR timer = (gcsOSTIMER_PTR)work; |
| |
| gctTIMERFUNCTION function = timer->function; |
| |
| function(timer->data); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_CreateTimer |
| ** |
| ** Create a software timer. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to the gckOS object. |
| ** |
| ** gctTIMERFUNCTION Function. |
| ** Pointer to a call back function which will be called when timer is |
| ** expired. |
| ** |
| ** gctPOINTER Data. |
| ** Private data which will be passed to call back function. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctPOINTER * Timer |
| ** Pointer to a variable receiving the created timer. |
| */ |
| gceSTATUS |
| gckOS_CreateTimer( |
| IN gckOS Os, |
| IN gctTIMERFUNCTION Function, |
| IN gctPOINTER Data, |
| OUT gctPOINTER * Timer |
| ) |
| { |
| gceSTATUS status; |
| gcsOSTIMER_PTR pointer; |
| gcmkHEADER_ARG("Os=0x%X Function=0x%X Data=0x%X", Os, Function, Data); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Timer != gcvNULL); |
| |
| gcmkONERROR(gckOS_Allocate(Os, sizeof(gcsOSTIMER), (gctPOINTER)&pointer)); |
| |
| pointer->function = Function; |
| pointer->data = Data; |
| |
| INIT_DELAYED_WORK(&pointer->work, _TimerFunction); |
| |
| *Timer = pointer; |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_DestroyTimer |
| ** |
| ** Destory a software timer. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to the gckOS object. |
| ** |
| ** gctPOINTER Timer |
| ** Pointer to the timer to be destoryed. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_DestroyTimer( |
| IN gckOS Os, |
| IN gctPOINTER Timer |
| ) |
| { |
| gcsOSTIMER_PTR timer; |
| gcmkHEADER_ARG("Os=0x%X Timer=0x%X", Os, Timer); |
| |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Timer != gcvNULL); |
| |
| timer = (gcsOSTIMER_PTR)Timer; |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) |
| cancel_delayed_work_sync(&timer->work); |
| #else |
| cancel_delayed_work(&timer->work); |
| flush_workqueue(Os->workqueue); |
| #endif |
| |
| gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Os, Timer)); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_StartTimer |
| ** |
| ** Schedule a software timer. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to the gckOS object. |
| ** |
| ** gctPOINTER Timer |
| ** Pointer to the timer to be scheduled. |
| ** |
| ** gctUINT32 Delay |
| ** Delay in milliseconds. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_StartTimer( |
| IN gckOS Os, |
| IN gctPOINTER Timer, |
| IN gctUINT32 Delay |
| ) |
| { |
| gcsOSTIMER_PTR timer; |
| |
| gcmkHEADER_ARG("Os=0x%X Timer=0x%X Delay=%u", Os, Timer, Delay); |
| |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Timer != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Delay != 0); |
| |
| timer = (gcsOSTIMER_PTR)Timer; |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) |
| mod_delayed_work(Os->workqueue, &timer->work, msecs_to_jiffies(Delay)); |
| #else |
| if (unlikely(delayed_work_pending(&timer->work))) |
| { |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) |
| cancel_delayed_work_sync(&timer->work); |
| #else |
| cancel_delayed_work(&timer->work); |
| flush_workqueue(Os->workqueue); |
| #endif |
| } |
| |
| queue_delayed_work(Os->workqueue, &timer->work, msecs_to_jiffies(Delay)); |
| #endif |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_StopTimer |
| ** |
| ** Cancel a unscheduled timer. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to the gckOS object. |
| ** |
| ** gctPOINTER Timer |
| ** Pointer to the timer to be cancel. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckOS_StopTimer( |
| IN gckOS Os, |
| IN gctPOINTER Timer |
| ) |
| { |
| gcsOSTIMER_PTR timer; |
| gcmkHEADER_ARG("Os=0x%X Timer=0x%X", Os, Timer); |
| |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Timer != gcvNULL); |
| |
| timer = (gcsOSTIMER_PTR)Timer; |
| |
| cancel_delayed_work(&timer->work); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_GetProcessNameByPid( |
| IN gctINT Pid, |
| IN gctSIZE_T Length, |
| OUT gctUINT8_PTR String |
| ) |
| { |
| struct task_struct *task; |
| |
| /* Get the task_struct of the task with pid. */ |
| rcu_read_lock(); |
| |
| task = FIND_TASK_BY_PID(Pid); |
| |
| if (task == gcvNULL) |
| { |
| rcu_read_unlock(); |
| return gcvSTATUS_NOT_FOUND; |
| } |
| |
| /* Get name of process. */ |
| strncpy(String, task->comm, Length); |
| |
| rcu_read_unlock(); |
| |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_DumpCallStack( |
| IN gckOS Os |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X", Os); |
| |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| |
| dump_stack(); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_DetectProcessByName |
| ** |
| ** task->comm maybe part of process name, so this function |
| ** can only be used for debugging. |
| ** |
| ** INPUT: |
| ** |
| ** gctCONST_POINTER Name |
| ** Pointer to a string to hold name to be check. If the length |
| ** of name is longer than TASK_COMM_LEN (16), use part of name |
| ** to detect. |
| ** |
| ** OUTPUT: |
| ** |
| ** gcvSTATUS_TRUE if name of current process matches Name. |
| ** |
| */ |
| gceSTATUS |
| gckOS_DetectProcessByName( |
| IN gctCONST_POINTER Name |
| ) |
| { |
| char comm[sizeof(current->comm)]; |
| |
| memset(comm, 0, sizeof(comm)); |
| |
| gcmkVERIFY_OK( |
| gckOS_GetProcessNameByPid(_GetProcessID(), sizeof(current->comm), comm)); |
| |
| return strstr(comm, Name) ? gcvSTATUS_TRUE |
| : gcvSTATUS_FALSE; |
| } |
| |
| #if gcdANDROID_NATIVE_FENCE_SYNC |
| |
| gceSTATUS |
| gckOS_CreateSyncPoint( |
| IN gckOS Os, |
| OUT gctSYNC_POINT * SyncPoint |
| ) |
| { |
| gceSTATUS status; |
| gcsSYNC_POINT_PTR syncPoint; |
| |
| gcmkHEADER_ARG("Os=0x%X", Os); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| |
| /* Create an sync point structure. */ |
| syncPoint = (gcsSYNC_POINT_PTR) kmalloc( |
| sizeof(gcsSYNC_POINT), GFP_KERNEL | gcdNOWARN); |
| |
| if (syncPoint == gcvNULL) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| /* Initialize the sync point. */ |
| atomic_set(&syncPoint->ref, 1); |
| atomic_set(&syncPoint->state, 0); |
| |
| gcmkONERROR(_AllocateIntegerId(&Os->syncPointDB, syncPoint, &syncPoint->id)); |
| |
| *SyncPoint = (gctSYNC_POINT)(gctUINTPTR_T)syncPoint->id; |
| |
| gcmkFOOTER_ARG("*SyncPonint=%d", syncPoint->id); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (syncPoint != gcvNULL) |
| { |
| kfree(syncPoint); |
| } |
| |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| gceSTATUS |
| gckOS_ReferenceSyncPoint( |
| IN gckOS Os, |
| IN gctSYNC_POINT SyncPoint |
| ) |
| { |
| gceSTATUS status; |
| gcsSYNC_POINT_PTR syncPoint; |
| |
| gcmkHEADER_ARG("Os=0x%X", Os); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(SyncPoint != gcvNULL); |
| |
| gcmkONERROR( |
| _QueryIntegerId(&Os->syncPointDB, |
| (gctUINT32)(gctUINTPTR_T)SyncPoint, |
| (gctPOINTER)&syncPoint)); |
| |
| /* Initialize the sync point. */ |
| atomic_inc(&syncPoint->ref); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| gceSTATUS |
| gckOS_DestroySyncPoint( |
| IN gckOS Os, |
| IN gctSYNC_POINT SyncPoint |
| ) |
| { |
| gceSTATUS status; |
| gcsSYNC_POINT_PTR syncPoint; |
| gctBOOL acquired = gcvFALSE; |
| |
| gcmkHEADER_ARG("Os=0x%X SyncPoint=%d", Os, (gctUINT32)(gctUINTPTR_T)SyncPoint); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(SyncPoint != gcvNULL); |
| |
| gcmkONERROR(gckOS_AcquireMutex(Os, Os->syncPointMutex, gcvINFINITE)); |
| acquired = gcvTRUE; |
| |
| gcmkONERROR( |
| _QueryIntegerId(&Os->syncPointDB, |
| (gctUINT32)(gctUINTPTR_T)SyncPoint, |
| (gctPOINTER)&syncPoint)); |
| |
| gcmkASSERT(syncPoint->id == (gctUINT32)(gctUINTPTR_T)SyncPoint); |
| |
| if (atomic_dec_and_test(&syncPoint->ref)) |
| { |
| gcmkVERIFY_OK(_DestroyIntegerId(&Os->syncPointDB, syncPoint->id)); |
| |
| /* Free the sgianl. */ |
| syncPoint->timeline = gcvNULL; |
| kfree(syncPoint); |
| } |
| |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Os, Os->syncPointMutex)); |
| acquired = gcvFALSE; |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (acquired) |
| { |
| /* Release the mutex. */ |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Os, Os->syncPointMutex)); |
| } |
| |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| gceSTATUS |
| gckOS_SignalSyncPoint( |
| IN gckOS Os, |
| IN gctSYNC_POINT SyncPoint |
| ) |
| { |
| gceSTATUS status; |
| gcsSYNC_POINT_PTR syncPoint; |
| struct sync_timeline * timeline; |
| gctBOOL acquired = gcvFALSE; |
| |
| gcmkHEADER_ARG("Os=0x%X SyncPoint=%d", Os, (gctUINT32)(gctUINTPTR_T)SyncPoint); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(SyncPoint != gcvNULL); |
| |
| gcmkONERROR(gckOS_AcquireMutex(Os, Os->syncPointMutex, gcvINFINITE)); |
| acquired = gcvTRUE; |
| |
| gcmkONERROR( |
| _QueryIntegerId(&Os->syncPointDB, |
| (gctUINT32)(gctUINTPTR_T)SyncPoint, |
| (gctPOINTER)&syncPoint)); |
| |
| gcmkASSERT(syncPoint->id == (gctUINT32)(gctUINTPTR_T)SyncPoint); |
| |
| /* Set signaled state. */ |
| atomic_set(&syncPoint->state, 1); |
| |
| /* Get parent timeline. */ |
| timeline = syncPoint->timeline; |
| |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Os, Os->syncPointMutex)); |
| acquired = gcvFALSE; |
| |
| /* Signal timeline. */ |
| if (timeline) |
| { |
| sync_timeline_signal(timeline); |
| } |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (acquired) |
| { |
| /* Release the mutex. */ |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Os, Os->syncPointMutex)); |
| } |
| |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| gceSTATUS |
| gckOS_QuerySyncPoint( |
| IN gckOS Os, |
| IN gctSYNC_POINT SyncPoint, |
| OUT gctBOOL_PTR State |
| ) |
| { |
| gceSTATUS status; |
| gcsSYNC_POINT_PTR syncPoint; |
| |
| gcmkHEADER_ARG("Os=0x%X SyncPoint=%d", Os, (gctUINT32)(gctUINTPTR_T)SyncPoint); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(SyncPoint != gcvNULL); |
| |
| gcmkONERROR( |
| _QueryIntegerId(&Os->syncPointDB, |
| (gctUINT32)(gctUINTPTR_T)SyncPoint, |
| (gctPOINTER)&syncPoint)); |
| |
| gcmkASSERT(syncPoint->id == (gctUINT32)(gctUINTPTR_T)SyncPoint); |
| |
| /* Get state. */ |
| *State = atomic_read(&syncPoint->state); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*State=%d", *State); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| gceSTATUS |
| gckOS_CreateSyncTimeline( |
| IN gckOS Os, |
| OUT gctHANDLE * Timeline |
| ) |
| { |
| struct viv_sync_timeline * timeline; |
| |
| /* Create viv sync timeline. */ |
| timeline = viv_sync_timeline_create("viv timeline", Os); |
| |
| if (timeline == gcvNULL) |
| { |
| /* Out of memory. */ |
| return gcvSTATUS_OUT_OF_MEMORY; |
| } |
| |
| *Timeline = (gctHANDLE) timeline; |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_DestroySyncTimeline( |
| IN gckOS Os, |
| IN gctHANDLE Timeline |
| ) |
| { |
| struct viv_sync_timeline * timeline; |
| gcmkASSERT(Timeline != gcvNULL); |
| |
| /* Destroy timeline. */ |
| timeline = (struct viv_sync_timeline *) Timeline; |
| sync_timeline_destroy(&timeline->obj); |
| |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_CreateNativeFence( |
| IN gckOS Os, |
| IN gctHANDLE Timeline, |
| IN gctSYNC_POINT SyncPoint, |
| OUT gctINT * FenceFD |
| ) |
| { |
| int fd = -1; |
| struct viv_sync_timeline *timeline; |
| struct sync_pt * pt = gcvNULL; |
| struct sync_fence * fence; |
| char name[32]; |
| gcsSYNC_POINT_PTR syncPoint; |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=0x%X Timeline=0x%X SyncPoint=%d", |
| Os, Timeline, (gctUINT)(gctUINTPTR_T)SyncPoint); |
| |
| gcmkONERROR( |
| _QueryIntegerId(&Os->syncPointDB, |
| (gctUINT32)(gctUINTPTR_T)SyncPoint, |
| (gctPOINTER)&syncPoint)); |
| |
| /* Cast timeline. */ |
| timeline = (struct viv_sync_timeline *) Timeline; |
| |
| fd = get_unused_fd(); |
| |
| if (fd < 0) |
| { |
| /* Out of resources. */ |
| gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); |
| } |
| |
| /* Create viv_sync_pt. */ |
| pt = viv_sync_pt_create(timeline, SyncPoint); |
| |
| if (pt == gcvNULL) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| /* Reference sync_timeline. */ |
| syncPoint->timeline = &timeline->obj; |
| |
| /* Build fence name. */ |
| snprintf(name, 32, "viv sync_fence-%u", (gctUINT)(gctUINTPTR_T)SyncPoint); |
| |
| /* Create sync_fence. */ |
| fence = sync_fence_create(name, pt); |
| |
| if (fence == NULL) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| /* Install fence to fd. */ |
| sync_fence_install(fence, fd); |
| |
| *FenceFD = fd; |
| gcmkFOOTER_ARG("*FenceFD=%d", fd); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Error roll back. */ |
| if (pt) |
| { |
| sync_pt_free(pt); |
| } |
| |
| if (fd > 0) |
| { |
| put_unused_fd(fd); |
| } |
| |
| gcmkFOOTER(); |
| return status; |
| } |
| #endif |
| |
| #if gcdSECURITY |
| gceSTATUS |
| gckOS_AllocatePageArray( |
| IN gckOS Os, |
| IN gctPHYS_ADDR Physical, |
| IN gctSIZE_T PageCount, |
| OUT gctPOINTER * PageArrayLogical, |
| OUT gctPHYS_ADDR * PageArrayPhysical |
| ) |
| { |
| gceSTATUS status = gcvSTATUS_OK; |
| PLINUX_MDL mdl; |
| gctUINT32* table; |
| gctUINT32 offset; |
| gctSIZE_T bytes; |
| gckALLOCATOR allocator; |
| |
| gcmkHEADER_ARG("Os=0x%X Physical=0x%X PageCount=%u", |
| Os, Physical, PageCount); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Physical != gcvNULL); |
| gcmkVERIFY_ARGUMENT(PageCount > 0); |
| |
| bytes = PageCount * gcmSIZEOF(gctUINT32); |
| gcmkONERROR(gckOS_AllocateNonPagedMemory( |
| Os, |
| gcvFALSE, |
| &bytes, |
| PageArrayPhysical, |
| PageArrayLogical |
| )); |
| |
| table = *PageArrayLogical; |
| |
| /* Convert pointer to MDL. */ |
| mdl = (PLINUX_MDL)Physical; |
| |
| allocator = mdl->allocator; |
| |
| /* Get all the physical addresses and store them in the page table. */ |
| |
| offset = 0; |
| PageCount = PageCount / (PAGE_SIZE / 4096); |
| |
| /* Try to get the user pages so DMA can happen. */ |
| while (PageCount-- > 0) |
| { |
| unsigned long phys = ~0; |
| |
| if (mdl->pagedMem && !mdl->contiguous) |
| { |
| if (allocator) |
| { |
| gctUINT32 phys_addr; |
| allocator->ops->Physical(allocator, mdl, offset, &phys_addr); |
| phys = (unsigned long)phys_addr; |
| } |
| } |
| else |
| { |
| if (!mdl->pagedMem) |
| { |
| gcmkTRACE_ZONE( |
| gcvLEVEL_INFO, gcvZONE_OS, |
| "%s(%d): we should not get this call for Non Paged Memory!", |
| __FUNCTION__, __LINE__ |
| ); |
| } |
| |
| phys = page_to_phys(nth_page(mdl->u.contiguousPages, offset)); |
| } |
| |
| table[offset] = phys; |
| |
| offset += 1; |
| } |
| |
| OnError: |
| |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| #endif |
| |
| gceSTATUS |
| gckOS_CPUPhysicalToGPUPhysical( |
| IN gckOS Os, |
| IN gctUINT32 CPUPhysical, |
| IN gctUINT32_PTR GPUPhysical |
| ) |
| { |
| gcsPLATFORM * platform; |
| gcmkHEADER_ARG("CPUPhysical=0x%X", CPUPhysical); |
| |
| platform = Os->device->platform; |
| |
| if (platform && platform->ops->getGPUPhysical) |
| { |
| gcmkVERIFY_OK( |
| platform->ops->getGPUPhysical(platform, CPUPhysical, GPUPhysical)); |
| } |
| else |
| { |
| *GPUPhysical = CPUPhysical; |
| } |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_GPUPhysicalToCPUPhysical( |
| IN gckOS Os, |
| IN gctUINT32 GPUPhysical, |
| IN gctUINT32_PTR CPUPhysical |
| ) |
| { |
| gcmkHEADER_ARG("GPUPhysical=0x%X", GPUPhysical); |
| |
| *CPUPhysical = GPUPhysical; |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_PhysicalToPhysicalAddress( |
| IN gckOS Os, |
| IN gctPOINTER Physical, |
| OUT gctUINT32 * PhysicalAddress |
| ) |
| { |
| PLINUX_MDL mdl = (PLINUX_MDL)Physical; |
| gckALLOCATOR allocator = mdl->allocator; |
| |
| if (allocator) |
| { |
| return allocator->ops->Physical(allocator, mdl, 0, PhysicalAddress); |
| } |
| |
| return gcvSTATUS_NOT_SUPPORTED; |
| } |
| |
| gceSTATUS |
| gckOS_QueryOption( |
| IN gckOS Os, |
| IN gctCONST_STRING Option, |
| OUT gctUINT32 * Value |
| ) |
| { |
| gckGALDEVICE device = Os->device; |
| |
| if (!strcmp(Option, "physBase")) |
| { |
| *Value = device->physBase; |
| return gcvSTATUS_OK; |
| } |
| else if (!strcmp(Option, "physSize")) |
| { |
| *Value = device->physSize; |
| return gcvSTATUS_OK; |
| } |
| else if (!strcmp(Option, "mmu")) |
| { |
| #if gcdSECURITY |
| *Value = 0; |
| #else |
| *Value = device->mmu; |
| #endif |
| return gcvSTATUS_OK; |
| } |
| |
| return gcvSTATUS_NOT_SUPPORTED; |
| } |
| |
| static int |
| fd_release( |
| struct inode *inode, |
| struct file *file |
| ) |
| { |
| gcsFDPRIVATE_PTR private = (gcsFDPRIVATE_PTR)file->private_data; |
| |
| if (private && private->release) |
| { |
| return private->release(private); |
| } |
| |
| return 0; |
| } |
| |
| static const struct file_operations fd_fops = { |
| .release = fd_release, |
| }; |
| |
| gceSTATUS |
| gckOS_GetFd( |
| IN gctSTRING Name, |
| IN gcsFDPRIVATE_PTR Private, |
| OUT gctINT *Fd |
| ) |
| { |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) |
| *Fd = anon_inode_getfd(Name, &fd_fops, Private, O_RDWR); |
| |
| if (*Fd < 0) |
| { |
| return gcvSTATUS_OUT_OF_RESOURCES; |
| } |
| |
| return gcvSTATUS_OK; |
| #else |
| return gcvSTATUS_NOT_SUPPORTED; |
| #endif |
| } |
| |