| /**************************************************************************** |
| * |
| * The MIT License (MIT) |
| * |
| * Copyright (c) 2014 - 2018 Vivante Corporation |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| * DEALINGS IN THE SOFTWARE. |
| * |
| ***************************************************************************** |
| * |
| * The GPL License (GPL) |
| * |
| * Copyright (C) 2014 - 2018 Vivante Corporation |
| * |
| * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| * |
| ***************************************************************************** |
| * |
| * Note: This software is released under dual MIT and GPL licenses. A |
| * recipient may use this file under the terms of either the MIT license or |
| * GPL License. If you wish to use only one license not the other, you can |
| * indicate your decision by deleting one of the above license notices in your |
| * version of this file. |
| * |
| *****************************************************************************/ |
| |
| |
| #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> |
| #include <linux/platform_device.h> |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) |
| #include <linux/anon_inodes.h> |
| #endif |
| |
| #if gcdANDROID_NATIVE_FENCE_SYNC |
| # include <linux/file.h> |
| # include "gc_hal_kernel_sync.h" |
| #endif |
| |
| #if defined(CONFIG_DMA_SHARED_BUFFER) |
| #include <linux/dma-buf.h> |
| #endif |
| |
| #if defined(CONFIG_ARM) && LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) |
| #include <dma.h> |
| #endif |
| |
| #define _GC_OBJ_ZONE gcvZONE_OS |
| |
| #include "gc_hal_kernel_allocator.h" |
| |
| #define gcmkBUG_ON(x) \ |
| do { \ |
| if (unlikely(!!(x))) \ |
| { \ |
| gcmkPRINT("[galcore]: BUG ON @ %s(%d)", __func__, __LINE__); \ |
| dump_stack(); \ |
| } \ |
| } while (0) |
| |
| /******************************************************************************\ |
| ******************************* 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 |
| } |
| |
| /* Must hold Mdl->mpasMutex before call this function. */ |
| static inline 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->count = 0; |
| |
| list_add(&mdlMap->link, &Mdl->mapsHead); |
| |
| gcmkFOOTER_ARG("0x%X", mdlMap); |
| return mdlMap; |
| } |
| |
| /* Must hold Mdl->mpasMutex before call this function. */ |
| static inline gceSTATUS |
| _DestroyMdlMap( |
| IN PLINUX_MDL Mdl, |
| IN PLINUX_MDL_MAP MdlMap |
| ) |
| { |
| gcmkHEADER_ARG("Mdl=0x%X MdlMap=0x%X", Mdl, MdlMap); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_ARGUMENT(MdlMap != gcvNULL); |
| |
| list_del(&MdlMap->link); |
| kfree(MdlMap); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /* Must hold Mdl->mpasMutex before call this function. */ |
| 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; |
| } |
| |
| list_for_each_entry(mdlMap, &Mdl->mapsHead, link) |
| { |
| if (mdlMap->pid == ProcessID) |
| { |
| gcmkFOOTER_ARG("0x%X", mdlMap); |
| return mdlMap; |
| } |
| } |
| |
| gcmkFOOTER_NO(); |
| return gcvNULL; |
| } |
| |
| |
| static PLINUX_MDL |
| _CreateMdl( |
| IN gckOS Os |
| ) |
| { |
| PLINUX_MDL mdl; |
| |
| gcmkHEADER(); |
| |
| mdl = (PLINUX_MDL)kzalloc(sizeof(struct _LINUX_MDL), GFP_KERNEL | gcdNOWARN); |
| |
| if (mdl) |
| { |
| mdl->os = Os; |
| atomic_set(&mdl->refs, 1); |
| mutex_init(&mdl->mapsMutex); |
| INIT_LIST_HEAD(&mdl->mapsHead); |
| } |
| |
| gcmkFOOTER_ARG("0x%X", mdl); |
| return mdl; |
| } |
| |
| static gceSTATUS |
| _DestroyMdl( |
| IN PLINUX_MDL Mdl |
| ) |
| { |
| gcmkHEADER_ARG("Mdl=0x%X", Mdl); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_ARGUMENT(Mdl != gcvNULL); |
| |
| if (atomic_dec_and_test(&Mdl->refs)) |
| { |
| gckOS os = Mdl->os; |
| gckALLOCATOR allocator = Mdl->allocator; |
| PLINUX_MDL_MAP mdlMap, next; |
| |
| /* Valid private means alloc/attach successfully */ |
| if (Mdl->priv) |
| { |
| if (Mdl->addr) |
| { |
| allocator->ops->UnmapKernel(allocator, Mdl, Mdl->addr); |
| } |
| allocator->ops->Free(allocator, Mdl); |
| } |
| |
| mutex_lock(&Mdl->mapsMutex); |
| list_for_each_entry_safe(mdlMap, next, &Mdl->mapsHead, link) |
| { |
| gcmkVERIFY_OK(_DestroyMdlMap(Mdl, mdlMap)); |
| } |
| mutex_unlock(&Mdl->mapsMutex); |
| |
| if (Mdl->link.next) |
| { |
| /* Remove the node from global list.. */ |
| mutex_lock(&os->mdlMutex); |
| list_del(&Mdl->link); |
| mutex_unlock(&os->mdlMutex); |
| } |
| |
| kfree(Mdl); |
| } |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** 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; |
| } |
| |
| static inline gceSTATUS |
| _QueryProcessPageTable( |
| IN gctPOINTER Logical, |
| OUT gctPHYS_ADDR_T * Address |
| ) |
| { |
| unsigned long logical = (unsigned long)Logical; |
| unsigned long offset = logical & ~PAGE_MASK; |
| |
| if (is_vmalloc_addr(Logical)) |
| { |
| /* vmalloc area. */ |
| *Address = page_to_phys(vmalloc_to_page(Logical)) | offset; |
| return gcvSTATUS_OK; |
| } |
| else if (virt_addr_valid(logical)) |
| { |
| /* Kernel logical address. */ |
| *Address = virt_to_phys(Logical); |
| return gcvSTATUS_OK; |
| } |
| else |
| { |
| /* Try user VM area. */ |
| struct vm_area_struct *vma; |
| spinlock_t *ptl; |
| pgd_t *pgd; |
| pud_t *pud; |
| pmd_t *pmd; |
| pte_t *pte; |
| |
| if (!current->mm) |
| return gcvSTATUS_NOT_FOUND; |
| |
| down_read(¤t->mm->mmap_sem); |
| vma = find_vma(current->mm, logical); |
| up_read(¤t->mm->mmap_sem); |
| |
| /* To check if mapped to user. */ |
| if (!vma) |
| 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, &ptl); |
| if (!pte) |
| { |
| spin_unlock(ptl); |
| return gcvSTATUS_NOT_FOUND; |
| } |
| |
| if (!pte_present(*pte)) |
| { |
| pte_unmap_unlock(pte, ptl); |
| return gcvSTATUS_NOT_FOUND; |
| } |
| |
| *Address = (pte_pfn(*pte) << PAGE_SHIFT) | offset; |
| pte_unmap_unlock(pte, ptl); |
| |
| 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; |
| } |
| |
| /******************************************************************************* |
| ** _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; |
| gctPHYS_ADDR_T 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, &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 |
| |
| |
| static gceSTATUS |
| _ShrinkMemory( |
| IN gckOS Os |
| ) |
| { |
| gcsPLATFORM * platform; |
| gceSTATUS status = gcvSTATUS_OK; |
| |
| gcmkHEADER_ARG("Os=0x%X", Os); |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| |
| platform = Os->device->platform; |
| |
| if (platform && platform->ops->shrinkMemory) |
| { |
| status = platform->ops->shrinkMemory(platform); |
| } |
| else |
| { |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_NOT_SUPPORTED; |
| } |
| |
| gcmkFOOTER_NO(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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; |
| |
| 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. */ |
| mutex_init(&os->mdlMutex); |
| |
| INIT_LIST_HEAD(&os->mdlHead); |
| |
| /* Get the kernel process ID. */ |
| os->kernelProcessID = _GetProcessID(); |
| |
| /* |
| * Initialize the signal manager. |
| */ |
| |
| /* Initialize mutex. */ |
| mutex_init(&os->signalMutex); |
| |
| /* Initialize signal id database lock. */ |
| spin_lock_init(&os->signalDB.lock); |
| |
| /* Initialize signal id database. */ |
| idr_init(&os->signalDB.idr); |
| |
| /* 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); |
| } |
| |
| spin_lock_init(&os->registerAccessLock); |
| |
| gckOS_ImportAllocators(os); |
| |
| #ifdef CONFIG_IOMMU_SUPPORT |
| if (((gckGALDEVICE)(os->device))->args.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 (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; |
| } |
| |
| /* |
| * Destroy the signal manager. |
| */ |
| |
| /* 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 |
| ) |
| { |
| gceSTATUS status; |
| PLINUX_MDL_MAP mdlMap; |
| PLINUX_MDL mdl = (PLINUX_MDL) Physical; |
| gckALLOCATOR allocator; |
| gctINT pid = _GetProcessID(); |
| |
| 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); |
| |
| mutex_lock(&mdl->mapsMutex); |
| |
| mdlMap = FindMdlMap(mdl, pid); |
| |
| if (mdlMap == gcvNULL) |
| { |
| mdlMap = _CreateMdlMap(mdl, pid); |
| |
| if (mdlMap == gcvNULL) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| } |
| |
| if (mdlMap->vmaAddr == gcvNULL) |
| { |
| allocator = mdl->allocator; |
| |
| gcmkONERROR( |
| allocator->ops->MapUser(allocator, |
| mdl, gcvFALSE, |
| &mdlMap->vmaAddr)); |
| } |
| |
| mutex_unlock(&mdl->mapsMutex); |
| |
| *Logical = mdlMap->vmaAddr; |
| |
| gcmkFOOTER_ARG("*Logical=0x%X", *Logical); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| mutex_unlock(&mdl->mapsMutex); |
| |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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); |
| |
| if (Logical) |
| { |
| gckALLOCATOR allocator = mdl->allocator; |
| |
| mutex_lock(&mdl->mapsMutex); |
| |
| mdlMap = FindMdlMap(mdl, PID); |
| |
| if (mdlMap == gcvNULL || mdlMap->vmaAddr == gcvNULL) |
| { |
| mutex_unlock(&mdl->mapsMutex); |
| |
| gcmkFOOTER_ARG("status=%d", gcvSTATUS_INVALID_ARGUMENT); |
| return gcvSTATUS_INVALID_ARGUMENT; |
| } |
| |
| BUG_ON(!allocator || !allocator->ops->UnmapUser); |
| |
| allocator->ops->UnmapUser(allocator, mdl, |
| mdlMap->vmaAddr, mdl->numPages * PAGE_SIZE); |
| |
| gcmkVERIFY_OK(_DestroyMdlMap(mdl, mdlMap)); |
| |
| mutex_unlock(&mdl->mapsMutex); |
| } |
| |
| /* 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; |
| gctPOINTER addr; |
| gceSTATUS status = gcvSTATUS_NOT_SUPPORTED; |
| gckALLOCATOR allocator; |
| gctUINT32 flag = gcvALLOC_FLAG_CONTIGUOUS; |
| |
| 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 structure */ |
| mdl = _CreateMdl(Os); |
| if (mdl == gcvNULL) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| if (Os->allocatorLimitMarker) |
| { |
| flag |= gcvALLOC_FLAG_CMA_LIMIT; |
| } |
| |
| /* Walk all allocators. */ |
| list_for_each_entry(allocator, &Os->allocatorList, link) |
| { |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_OS, |
| "%s(%d) flag = %x allocator->capability = %x", |
| __FUNCTION__, __LINE__, flag, allocator->capability); |
| |
| #ifndef NO_DMA_COHERENT |
| /* Point to dma coherent allocator. */ |
| if (strcmp(allocator->name, "dma")) |
| { |
| if (((flag & allocator->capability) != flag) || |
| (numPages > 1)) |
| { |
| continue; |
| } |
| } |
| #else |
| if ((flag & allocator->capability) != flag) |
| { |
| continue; |
| } |
| #endif |
| status = allocator->ops->Alloc(allocator, mdl, numPages, flag); |
| |
| if (gcmIS_SUCCESS(status)) |
| { |
| mdl->allocator = allocator; |
| break; |
| } |
| } |
| |
| /* Check status. */ |
| gcmkONERROR(status); |
| |
| mdl->numPages = numPages; |
| |
| mdl->contiguous = gcvTRUE; |
| |
| gcmkONERROR(allocator->ops->MapKernel(allocator, mdl, &addr)); |
| |
| /* Trigger a page fault. */ |
| memset(addr, 0, numPages * PAGE_SIZE); |
| |
| mdl->addr = addr; |
| |
| if (InUserSpace) |
| { |
| mdlMap = _CreateMdlMap(mdl, _GetProcessID()); |
| |
| if (mdlMap == gcvNULL) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| gcmkONERROR(allocator->ops->MapUser(allocator, mdl, gcvFALSE, &mdlMap->vmaAddr)); |
| |
| *Logical = mdlMap->vmaAddr; |
| } |
| else |
| { |
| *Logical = addr; |
| } |
| |
| /* |
| * Add this to a global list. |
| * Will be used by get physical address |
| * and mapuser pointer functions. |
| */ |
| mutex_lock(&Os->mdlMutex); |
| list_add_tail(&mdl->link, &Os->mdlHead); |
| mutex_unlock(&Os->mdlMutex); |
| |
| /* Return allocated memory. */ |
| *Bytes = bytes; |
| *Physical = (gctPHYS_ADDR) mdl; |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Bytes=%lu *Physical=0x%X *Logical=0x%X", |
| *Bytes, *Physical, *Logical); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (mdl != gcvNULL) |
| { |
| /* Free LINUX_MDL. */ |
| gcmkVERIFY_OK(_DestroyMdl(mdl)); |
| } |
| |
| /* 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)Physical; |
| |
| 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); |
| |
| gcmkVERIFY_OK(_DestroyMdl(mdl)); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| static inline gckALLOCATOR |
| _FindAllocator( |
| gckOS Os, |
| gctUINT Flag |
| ) |
| { |
| gckALLOCATOR allocator; |
| |
| list_for_each_entry(allocator, &Os->allocatorList, link) |
| { |
| if ((allocator->capability & Flag) == Flag) |
| { |
| return allocator; |
| } |
| } |
| |
| return gcvNULL; |
| } |
| |
| gceSTATUS |
| gckOS_RequestReservedMemory( |
| gckOS Os, |
| unsigned long Start, |
| unsigned long Size, |
| const char * Name, |
| gctBOOL Requested, |
| void ** MemoryHandle |
| ) |
| { |
| PLINUX_MDL mdl = gcvNULL; |
| gceSTATUS status; |
| gckALLOCATOR allocator; |
| gcsATTACH_DESC desc; |
| |
| gcmkHEADER_ARG("start=0x%lx size=0x%lx name=%s", Start, Size, Name); |
| |
| /* Round up to page size. */ |
| Size = (Size + ~PAGE_MASK) & PAGE_MASK; |
| |
| mdl = _CreateMdl(Os); |
| if (!mdl) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| desc.reservedMem.start = Start; |
| desc.reservedMem.size = Size; |
| desc.reservedMem.name = Name; |
| desc.reservedMem.requested = Requested; |
| |
| allocator = _FindAllocator(Os, gcvALLOC_FLAG_LINUX_RESERVED_MEM); |
| if (!allocator) |
| { |
| gcmkPRINT("reserved-mem allocator not integrated!"); |
| gcmkONERROR(gcvSTATUS_GENERIC_IO); |
| } |
| |
| /* Call attach. */ |
| gcmkONERROR(allocator->ops->Attach(allocator, &desc, mdl)); |
| |
| /* Assign alloator. */ |
| mdl->allocator = allocator; |
| mdl->numPages = Size >> PAGE_SHIFT; |
| mdl->contiguous = gcvTRUE; |
| mdl->addr = gcvNULL; |
| mdl->dmaHandle = Start; |
| mdl->gid = 0; |
| |
| /* |
| * Add this to a global list. |
| * Will be used by get physical address |
| * and mapuser pointer functions. |
| */ |
| mutex_lock(&Os->mdlMutex); |
| list_add_tail(&mdl->link, &Os->mdlHead); |
| mutex_unlock(&Os->mdlMutex); |
| |
| *MemoryHandle = (void *)mdl; |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (mdl) |
| { |
| gcmkVERIFY_OK(_DestroyMdl(mdl)); |
| } |
| |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| void |
| gckOS_ReleaseReservedMemory( |
| gckOS Os, |
| void * MemoryHandle |
| ) |
| { |
| gckALLOCATOR allocator; |
| PLINUX_MDL mdl = (PLINUX_MDL)MemoryHandle; |
| |
| allocator = _FindAllocator(Os, gcvALLOC_FLAG_LINUX_RESERVED_MEM); |
| |
| /* If no allocator, how comes the memory? */ |
| BUG_ON(!allocator); |
| |
| allocator->ops->Free(allocator, mdl); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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 |
| ) |
| { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&Os->registerAccessLock, flags); |
| |
| if (unlikely(Os->clockStates[Core] == gcvFALSE)) |
| { |
| spin_unlock_irqrestore(&Os->registerAccessLock, flags); |
| |
| /* |
| * Read register when power off: |
| * 1. In shared IRQ, read register may be called and that's not our irq. |
| * 2. In non-irq context, register access should not be called, |
| * otherwise it's driver bug. |
| */ |
| if (!in_irq()) |
| { |
| gcmkPRINT("[galcore]: %s(%d) GPU[%d] external clock off", |
| __func__, __LINE__, Core); |
| gcmkBUG_ON(1); |
| } |
| |
| return gcvSTATUS_GENERIC_IO; |
| } |
| |
| *Data = readl((gctUINT8 *)Os->device->registerBases[Core] + Address); |
| spin_unlock_irqrestore(&Os->registerAccessLock, flags); |
| |
| #if gcdDUMP_AHB_ACCESS |
| if (!in_irq()) |
| { |
| /* Dangerous to print in interrupt context, skip. */ |
| gcmkPRINT("@[RD %d] %08x %08x", Core, Address, *Data); |
| } |
| #endif |
| |
| /* Success. */ |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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 |
| ) |
| { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&Os->registerAccessLock, flags); |
| |
| if (unlikely(Os->clockStates[Core] == gcvFALSE)) |
| { |
| spin_unlock_irqrestore(&Os->registerAccessLock, flags); |
| |
| gcmkPRINT("[galcore]: %s(%d) GPU[%d] external clock off", |
| __func__, __LINE__, Core); |
| |
| /* Driver bug: register write when clock off. */ |
| gcmkBUG_ON(1); |
| return gcvSTATUS_GENERIC_IO; |
| } |
| |
| writel(Data, (gctUINT8 *)Os->device->registerBases[Core] + Address); |
| spin_unlock_irqrestore(&Os->registerAccessLock, flags); |
| |
| #if gcdDUMP_AHB_ACCESS |
| if (!in_irq()) |
| { |
| /* Dangerous to print in interrupt context, skip. */ |
| gcmkPRINT("@[WR %d] %08x %08x", Core, Address, Data); |
| } |
| #endif |
| |
| /* Success. */ |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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=%d", *PageSize); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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. |
| */ |
| static gceSTATUS |
| _GetPhysicalAddressProcess( |
| IN gckOS Os, |
| IN gctPOINTER Logical, |
| IN gctUINT32 ProcessID, |
| OUT gctPHYS_ADDR_T * Address |
| ) |
| { |
| PLINUX_MDL mdl; |
| 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); |
| |
| mutex_lock(&Os->mdlMutex); |
| |
| if (Os->device->contiguousPhysical) |
| { |
| /* Try the contiguous memory pool. */ |
| mdl = (PLINUX_MDL) Os->device->contiguousPhysical; |
| |
| mutex_lock(&mdl->mapsMutex); |
| |
| status = _ConvertLogical2Physical(Os, Logical, ProcessID, mdl, Address); |
| |
| mutex_unlock(&mdl->mapsMutex); |
| } |
| |
| if (gcmIS_ERROR(status)) |
| { |
| /* Walk all MDLs. */ |
| list_for_each_entry(mdl, &Os->mdlHead, link) |
| { |
| mutex_lock(&mdl->mapsMutex); |
| |
| status = _ConvertLogical2Physical(Os, Logical, ProcessID, mdl, Address); |
| |
| mutex_unlock(&mdl->mapsMutex); |
| |
| if (gcmIS_SUCCESS(status)) |
| { |
| break; |
| } |
| } |
| } |
| |
| mutex_unlock(&Os->mdlMutex); |
| |
| gcmkONERROR(status); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Address=%p", *Address); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| |
| |
| /******************************************************************************* |
| ** |
| ** 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 gctPHYS_ADDR_T * 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( |
| _GetPhysicalAddressProcess(Os, Logical, processID, Address)); |
| } |
| |
| gcmkVERIFY_OK(gckOS_CPUPhysicalToGPUPhysical(Os, *Address, Address)); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Address=%p", *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 gctPHYS_ADDR_T * 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 gctPHYS_ADDR_T * Physical |
| ) |
| { |
| gckALLOCATOR allocator = Mdl->allocator; |
| gctUINT32 offset; |
| gceSTATUS status = gcvSTATUS_NOT_FOUND; |
| gctINT8_PTR vBase; |
| |
| /* TASK_SIZE is userspace - kernelspace virtual memory split. */ |
| if ((gctUINTPTR_T)Logical >= TASK_SIZE) |
| { |
| /* Kernel virtual address. */ |
| vBase = Mdl->addr; |
| } |
| else |
| { |
| /* User virtual address. */ |
| PLINUX_MDL_MAP map; |
| |
| 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; |
| |
| allocator->ops->Physical(allocator, Mdl, offset, Physical); |
| |
| status = gcvSTATUS_OK; |
| } |
| |
| 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; |
| gctBOOL found = gcvFALSE; |
| 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); |
| |
| mutex_lock(&Os->mdlMutex); |
| |
| /* Go through our mapping to see if we know this physical address already. */ |
| list_for_each_entry(mdl, &Os->mdlHead, link) |
| { |
| if (mdl->dmaHandle != 0) |
| { |
| if ((physical >= mdl->dmaHandle) |
| && (physical < mdl->dmaHandle + mdl->numPages * PAGE_SIZE) |
| ) |
| { |
| *Logical = mdl->addr + (physical - mdl->dmaHandle); |
| found = gcvTRUE; |
| break; |
| } |
| } |
| } |
| |
| mutex_unlock(&Os->mdlMutex); |
| |
| if (!found) |
| { |
| unsigned long pfn = physical >> PAGE_SHIFT; |
| |
| if (pfn_valid(pfn)) |
| { |
| gctUINT32 offset = physical & ~PAGE_MASK; |
| struct page ** pages; |
| struct page * page; |
| 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; |
| } |
| |
| page = pfn_to_page(pfn); |
| |
| 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; |
| gctBOOL found = gcvFALSE; |
| |
| 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); |
| |
| mutex_lock(&Os->mdlMutex); |
| |
| list_for_each_entry(mdl, &Os->mdlHead, link) |
| { |
| if (mdl->addr != gcvNULL) |
| { |
| if ((Logical >= (gctPOINTER)mdl->addr) && |
| (Logical < (gctPOINTER)((gctSTRING)mdl->addr + mdl->numPages * PAGE_SIZE))) |
| { |
| found = gcvTRUE; |
| break; |
| } |
| } |
| } |
| |
| mutex_unlock(&Os->mdlMutex); |
| |
| if (!found) |
| { |
| /* Unmap the memory. */ |
| vunmap((void *)((unsigned long)Logical & PAGE_MASK)); |
| } |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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_NOT_SUPPORTED; |
| 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(Os); |
| if (mdl == gcvNULL) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| if (Os->allocatorLimitMarker && (Flag & gcvALLOC_FLAG_CMA_LIMIT)) |
| { |
| Flag &= ~gcvALLOC_FLAG_CACHEABLE; |
| } |
| else |
| { |
| Flag &= ~gcvALLOC_FLAG_CMA_LIMIT; |
| } |
| |
| /* Walk all allocators. */ |
| list_for_each_entry(allocator, &Os->allocatorList, link) |
| { |
| 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->contiguous = Flag & gcvALLOC_FLAG_CONTIGUOUS; |
| |
| if (Gid != gcvNULL) |
| { |
| *Gid = mdl->gid; |
| } |
| |
| /* |
| * Add this to a global list. |
| * Will be used by get physical address |
| * and mapuser pointer functions. |
| */ |
| mutex_lock(&Os->mdlMutex); |
| list_add_tail(&mdl->link, &Os->mdlHead); |
| mutex_unlock(&Os->mdlMutex); |
| |
| /* Return physical address. */ |
| *Physical = (gctPHYS_ADDR) mdl; |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Physical=0x%X", *Physical); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (mdl != gcvNULL) |
| { |
| /* Free the memory. */ |
| _DestroyMdl(mdl); |
| } |
| |
| /* 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; |
| |
| 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); |
| |
| /* 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; |
| |
| mutex_lock(&mdl->mapsMutex); |
| |
| mdlMap = FindMdlMap(mdl, _GetProcessID()); |
| |
| if (mdlMap == gcvNULL) |
| { |
| mdlMap = _CreateMdlMap(mdl, _GetProcessID()); |
| |
| if (mdlMap == gcvNULL) |
| { |
| mutex_unlock(&mdl->mapsMutex); |
| |
| gcmkFOOTER_ARG("*status=%d", gcvSTATUS_OUT_OF_MEMORY); |
| return gcvSTATUS_OUT_OF_MEMORY; |
| } |
| } |
| |
| if (mdlMap->vmaAddr == gcvNULL) |
| { |
| status = allocator->ops->MapUser(allocator, mdl, Cacheable, &mdlMap->vmaAddr); |
| |
| if (gcmIS_ERROR(status)) |
| { |
| mutex_unlock(&mdl->mapsMutex); |
| |
| 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); |
| |
| mutex_unlock(&mdl->mapsMutex); |
| |
| /* 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 gcvSTATUS_NOT_SUPPORTED; |
| } |
| |
| gceSTATUS |
| gckOS_MapPagesEx( |
| IN gckOS Os, |
| IN gceCORE Core, |
| IN gctPHYS_ADDR Physical, |
| IN gctSIZE_T PageCount, |
| IN gctUINT32 Address, |
| IN gctPOINTER PageTable, |
| IN gctBOOL Writable, |
| IN gceSURF_TYPE Type |
| ) |
| { |
| gceSTATUS status = gcvSTATUS_OK; |
| PLINUX_MDL mdl; |
| gctUINT32* table; |
| gctUINT32 offset = 0; |
| #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; |
| |
| gctUINT32 policyID = 0; |
| gctUINT32 axiConfig = 0; |
| |
| gcsPLATFORM * platform = Os->device->platform; |
| |
| 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; |
| |
| gcmkASSERT(allocator != gcvNULL); |
| |
| gcmkTRACE_ZONE( |
| gcvLEVEL_INFO, gcvZONE_OS, |
| "%s(%d): Physical->0x%X PageCount->0x%X", |
| __FUNCTION__, __LINE__, |
| (gctUINT32)(gctUINTPTR_T)Physical, |
| (gctUINT32)(gctUINTPTR_T)PageCount |
| ); |
| |
| #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 |
| |
| if (platform && platform->ops->getPolicyID) |
| { |
| platform->ops->getPolicyID(platform, Type, &policyID, &axiConfig); |
| |
| gcmkBUG_ON(policyID > 0x1F); |
| |
| /* ID[3:0] is used in STLB. */ |
| policyID &= 0xF; |
| } |
| |
| /* Get all the physical addresses and store them in the page table. */ |
| |
| PageCount = PageCount / (PAGE_SIZE / 4096); |
| |
| /* Try to get the user pages so DMA can happen. */ |
| while (PageCount-- > 0) |
| { |
| gctUINT i; |
| gctPHYS_ADDR_T phys = ~0U; |
| |
| allocator->ops->Physical(allocator, mdl, offset, &phys); |
| |
| gcmkVERIFY_OK(gckOS_CPUPhysicalToGPUPhysical(Os, phys, &phys)); |
| |
| if (policyID) |
| { |
| /* AxUSER must not used for address currently. */ |
| gcmkBUG_ON((phys >> 32) & 0xF); |
| |
| /* Merge policyID to AxUSER[7:4].*/ |
| phys |= ((gctPHYS_ADDR_T)policyID << 36); |
| } |
| |
| #ifdef CONFIG_IOMMU_SUPPORT |
| if (Os->iommu) |
| { |
| /* remove LSB. */ |
| phys &= PAGE_MASK; |
| |
| gcmkTRACE_ZONE( |
| gcvLEVEL_INFO, gcvZONE_OS, |
| "%s(%d): Setup mapping in IOMMU %x => %x", |
| __FUNCTION__, __LINE__, |
| Address + offset, phys |
| ); |
| |
| /* When use IOMMU, GPU use system PAGE_SIZE. */ |
| gcmkONERROR(gckIOMMU_Map( |
| Os->iommu, Address + offset, phys, PAGE_SIZE)); |
| } |
| else |
| #endif |
| { |
| /* remove LSB. */ |
| phys &= ~(4096ull - 1); |
| |
| #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 + (i * 4096), &pageTableEntry); |
| gcmkONERROR( |
| gckMMU_SetPage(mmu, |
| phys + (i * 4096), |
| Writable, |
| pageTableEntry)); |
| #else |
| gcmkONERROR( |
| gckMMU_SetPage(Os->device->kernels[Core]->mmu, |
| phys + (i * 4096), |
| Writable, |
| table++)); |
| #endif |
| } |
| } |
| } |
| |
| offset += PAGE_SIZE; |
| } |
| |
| #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; |
| gctINT pid = _GetProcessID(); |
| |
| 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); |
| |
| mutex_lock(&mdl->mapsMutex); |
| |
| list_for_each_entry(mdlMap, &mdl->mapsHead, link) |
| { |
| if ((mdlMap->vmaAddr != gcvNULL) && (mdlMap->pid == pid)) |
| { |
| if (--mdlMap->count == 0) |
| { |
| allocator->ops->UnmapUser( |
| allocator, |
| mdl, |
| mdlMap->vmaAddr, |
| mdl->numPages * PAGE_SIZE); |
| |
| mdlMap->vmaAddr = gcvNULL; |
| } |
| } |
| } |
| |
| mutex_unlock(&mdl->mapsMutex); |
| |
| /* 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->contiguousLogical; |
| 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 |
| ) |
| { |
| 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); |
| |
| *KernelPointer = Pointer; |
| |
| 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 |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Pointer=0x%X Size=%lu KernelPointer=0x%X", |
| Os, Pointer, Size, KernelPointer); |
| |
| 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; |
| } |
| |
| gceSTATUS |
| gckOS_ReadMappedPointer( |
| IN gckOS Os, |
| IN gctPOINTER Address, |
| IN gctUINT32_PTR 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_READ, Address, 4)) |
| { |
| /* User address. */ |
| if (get_user(*Data, (gctUINT32*)Address)) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ADDRESS); |
| } |
| } |
| else |
| { |
| /* Kernel address. */ |
| *Data = *(gctUINT32_PTR)Address; |
| } |
| |
| /* 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 |
| ) |
| { |
| return gcvSTATUS_NOT_SUPPORTED; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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 |
| ) |
| { |
| return gcvSTATUS_NOT_SUPPORTED; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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); |
| } |
| |
| 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; |
| } |
| |
| gceSTATUS |
| gckOS_ResumeInterrupt( |
| IN gckOS Os |
| ) |
| { |
| return gckOS_ResumeInterruptEx(Os, gcvCORE_MAJOR); |
| } |
| |
| 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; |
| } |
| |
| 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. |
| */ |
| |
| /* |
| |
| Following patch can be applied to kernel in case cache API is not exported. |
| |
| diff --git a/arch/arm/mm/proc-syms.c b/arch/arm/mm/proc-syms.c |
| index 054b491..e9e74ec 100644 |
| --- a/arch/arm/mm/proc-syms.c |
| +++ b/arch/arm/mm/proc-syms.c |
| @@ -30,6 +30,9 @@ EXPORT_SYMBOL(__cpuc_flush_user_all); |
| EXPORT_SYMBOL(__cpuc_flush_user_range); |
| EXPORT_SYMBOL(__cpuc_coherent_kern_range); |
| EXPORT_SYMBOL(__cpuc_flush_dcache_area); |
| +EXPORT_SYMBOL(__glue(_CACHE,_dma_map_area)); |
| +EXPORT_SYMBOL(__glue(_CACHE,_dma_unmap_area)); |
| +EXPORT_SYMBOL(__glue(_CACHE,_dma_flush_range)); |
| #else |
| EXPORT_SYMBOL(cpu_cache); |
| #endif |
| |
| */ |
| gceSTATUS |
| gckOS_CacheClean( |
| IN gckOS Os, |
| IN gctUINT32 ProcessID, |
| IN gctPHYS_ADDR Handle, |
| IN gctPHYS_ADDR_T Physical, |
| IN gctPOINTER Logical, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| gcsPLATFORM * platform; |
| |
| gcmkHEADER_ARG("Os=0x%X ProcessID=%d Handle=0x%X Logical=%p 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 |
| #if defined(CONFIG_ARM) || defined(CONFIG_ARM64) |
| |
| #if defined (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 |
| |
| #elif defined(CONFIG_ARM64) |
| __dma_map_area(Logical, Bytes, DMA_TO_DEVICE); |
| #endif |
| |
| #if defined(CONFIG_OUTER_CACHE) |
| /* Outer cache. */ |
| _HandleOuterCache(Os, Physical, Logical, Bytes, gcvCACHE_CLEAN); |
| #endif |
| |
| #elif defined(CONFIG_MIPS) |
| dma_cache_wback((unsigned long) Logical, Bytes); |
| #elif defined(CONFIG_PPC) |
| flush_dcache_range((unsigned long)Logical, (unsigned long)Logical + Bytes); |
| #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 gctPHYS_ADDR_T Physical, |
| IN gctPOINTER Logical, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| gcsPLATFORM * platform; |
| |
| gcmkHEADER_ARG("Os=0x%X ProcessID=%d Handle=0x%X Logical=%p 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 |
| #if defined(CONFIG_ARM) || defined(CONFIG_ARM64) |
| |
| #if defined (CONFIG_ARM) |
| /* Inner cache. */ |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) |
| dmac_unmap_area(Logical, Bytes, DMA_FROM_DEVICE); |
| # else |
| dmac_inv_range(Logical, Logical + Bytes); |
| # endif |
| #elif defined(CONFIG_ARM64) |
| __dma_unmap_area(Logical, Bytes, DMA_FROM_DEVICE); |
| #endif |
| |
| #if defined(CONFIG_OUTER_CACHE) |
| /* Outer cache. */ |
| _HandleOuterCache(Os, Physical, Logical, Bytes, gcvCACHE_INVALIDATE); |
| #endif |
| |
| #elif defined(CONFIG_MIPS) |
| dma_cache_inv((unsigned long) Logical, Bytes); |
| #elif defined(CONFIG_PPC) |
| flush_dcache_range((unsigned long)Logical, (unsigned long)Logical + Bytes); |
| #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 gctPHYS_ADDR_T Physical, |
| IN gctPOINTER Logical, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| gcsPLATFORM * platform; |
| |
| gcmkHEADER_ARG("Os=0x%X ProcessID=%d Handle=0x%X Logical=%p 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 |
| #if defined(CONFIG_ARM) || defined(CONFIG_ARM64) |
| #if defined (CONFIG_ARM) |
| /* Inner cache. */ |
| dmac_flush_range(Logical, Logical + Bytes); |
| #elif defined (CONFIG_ARM64) |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) |
| __dma_flush_area(Logical, Bytes); |
| #else |
| __dma_flush_range(Logical, Logical + Bytes); |
| #endif |
| #endif |
| |
| #if defined(CONFIG_OUTER_CACHE) |
| /* Outer cache. */ |
| _HandleOuterCache(Os, Physical, Logical, Bytes, gcvCACHE_FLUSH); |
| #endif |
| |
| #elif defined(CONFIG_MIPS) |
| dma_cache_wback_inv((unsigned long) Logical, Bytes); |
| #elif defined(CONFIG_PPC) |
| flush_dcache_range((unsigned long)Logical, (unsigned long)Logical + Bytes); |
| #elif defined(CONFIG_X86) |
| wbinvd_on_all_cpus(); |
| #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 |
| ) |
| { |
| 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. */ |
| down((struct semaphore *) Semaphore); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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. */ |
| *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 (clockChange) |
| { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&Os->registerAccessLock, flags); |
| |
| /* Record clock states, ahead. */ |
| Os->clockStates[Core] = Clock; |
| |
| spin_unlock_irqrestore(&Os->registerAccessLock, flags); |
| } |
| |
| if (powerChange && (Power == gcvTRUE)) |
| { |
| if (platform && platform->ops->setPower) |
| { |
| gcmkVERIFY_OK(platform->ops->setPower(platform, Core, Power)); |
| } |
| |
| Os->powerStates[Core] = Power; |
| } |
| |
| if (clockChange) |
| { |
| if (platform && platform->ops->setClock) |
| { |
| gcmkVERIFY_OK(platform->ops->setClock(platform, Core, Clock)); |
| } |
| } |
| |
| 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; |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) |
| res.tv_sec = 0; |
| res.tv_nsec = hrtimer_resolution; |
| #else |
| hrtimer_get_res(CLOCK_MONOTONIC, &res); |
| #endif |
| |
| *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->done = 0; |
| init_waitqueue_head(&signal->wait); |
| spin_lock_init(&signal->lock); |
| signal->manualReset = ManualReset; |
| |
| atomic_set(&signal->ref, 1); |
| |
| #if gcdANDROID_NATIVE_FENCE_SYNC |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(4,9,0) |
| signal->timeline = gcvNULL; |
| # else |
| signal->fence = gcvNULL; |
| # endif |
| #endif |
| |
| 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; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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); |
| |
| mutex_lock(&Os->signalMutex); |
| 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); |
| } |
| |
| mutex_unlock(&Os->signalMutex); |
| acquired = gcvFALSE; |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (acquired) |
| { |
| /* Release the mutex. */ |
| mutex_unlock(&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; |
| #if gcdANDROID_NATIVE_FENCE_SYNC |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(4,9,0) |
| struct sync_timeline * timeline = gcvNULL; |
| # else |
| struct fence * fence = gcvNULL; |
| # endif |
| #endif |
| |
| 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); |
| |
| mutex_lock(&Os->signalMutex); |
| |
| status = _QueryIntegerId(&Os->signalDB, |
| (gctUINT32)(gctUINTPTR_T)Signal, |
| (gctPOINTER)&signal); |
| |
| if (gcmIS_ERROR(status)) |
| { |
| mutex_unlock(&Os->signalMutex); |
| gcmkONERROR(status); |
| } |
| |
| /* |
| * Signal saved in event is not referenced. Inc reference here to avoid |
| * concurrent issue: signaling the signal while another thread is destroying |
| * it. |
| */ |
| atomic_inc(&signal->ref); |
| |
| mutex_unlock(&Os->signalMutex); |
| |
| gcmkONERROR(status); |
| |
| gcmkASSERT(signal->id == (gctUINT32)(gctUINTPTR_T)Signal); |
| |
| spin_lock(&signal->lock); |
| |
| if (State) |
| { |
| signal->done = 1; |
| |
| wake_up(&signal->wait); |
| |
| #if gcdANDROID_NATIVE_FENCE_SYNC |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(4,9,0) |
| timeline = signal->timeline; |
| # else |
| fence = signal->fence; |
| signal->fence = NULL; |
| # endif |
| #endif |
| } |
| else |
| { |
| signal->done = 0; |
| } |
| |
| spin_unlock(&signal->lock); |
| |
| #if gcdANDROID_NATIVE_FENCE_SYNC |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(4,9,0) |
| /* Signal timeline. */ |
| if (timeline) |
| { |
| sync_timeline_signal(timeline); |
| } |
| # else |
| if (fence) |
| { |
| fence_signal(fence); |
| fence_put(fence); |
| } |
| # endif |
| #endif |
| |
| mutex_lock(&Os->signalMutex); |
| |
| if (atomic_dec_and_test(&signal->ref)) |
| { |
| gcmkVERIFY_OK(_DestroyIntegerId(&Os->signalDB, signal->id)); |
| |
| /* Free the sgianl. */ |
| kfree(signal); |
| } |
| |
| mutex_unlock(&Os->signalMutex); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| 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; |
| |
| gcmkHEADER_ARG("Os=0x%X Signal=0x%X Process=%d", |
| Os, Signal, (gctINT32)(gctUINTPTR_T)Process); |
| |
| /* Signal. */ |
| status = gckOS_Signal(Os, Signal, gcvTRUE); |
| |
| 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 gctBOOL Interruptable, |
| IN gctUINT32 Wait |
| ) |
| { |
| gceSTATUS status; |
| gcsSIGNAL_PTR signal; |
| int done; |
| |
| 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); |
| |
| spin_lock(&signal->lock); |
| done = signal->done; |
| spin_unlock(&signal->lock); |
| |
| /* |
| * Do not need to lock below: |
| * 1. If signal already done, return immediately. |
| * 2. If signal not done, wait_event_xxx will handle correctly even read of |
| * signal->done is not atomic. |
| * |
| * Rest signal->done do not require lock either: |
| * No other thread can query/wait auto-reseted signal, because that is |
| * logic error. |
| */ |
| if (done) |
| { |
| status = gcvSTATUS_OK; |
| |
| if (!signal->manualReset) |
| { |
| signal->done = 0; |
| } |
| } |
| else if (Wait == 0) |
| { |
| status = gcvSTATUS_TIMEOUT; |
| } |
| else |
| { |
| /* Convert wait to milliseconds. */ |
| long timeout = (Wait == gcvINFINITE) |
| ? MAX_SCHEDULE_TIMEOUT |
| : msecs_to_jiffies(Wait); |
| |
| long ret; |
| |
| if (Interruptable) |
| { |
| ret = wait_event_interruptible_timeout(signal->wait, signal->done, timeout); |
| } |
| else |
| { |
| ret = wait_event_timeout(signal->wait, signal->done, timeout); |
| } |
| |
| if (likely(ret > 0)) |
| { |
| status = gcvSTATUS_OK; |
| |
| if (!signal->manualReset) |
| { |
| /* Auto reset. */ |
| signal->done = 0; |
| } |
| } |
| else |
| { |
| status = (ret == -ERESTARTSYS) ? gcvSTATUS_INTERRUPTED |
| : gcvSTATUS_TIMEOUT; |
| } |
| } |
| |
| OnError: |
| /* Return status. */ |
| gcmkFOOTER_ARG("Signal=0x%lX status=%d", Signal, status); |
| return status; |
| } |
| |
| gceSTATUS |
| _QuerySignal( |
| IN gckOS Os, |
| IN gctSIGNAL Signal |
| ) |
| { |
| /* |
| * This function is called by 'has_signaled' callback of sync_timeline. |
| * By design, 'has_signaled' could be called in interrupt context, but |
| * in current driver, it can be called only when 'gckOS_Signal' and |
| * 'gckOS_CreateNativeFence'. Thus its safe to use normal version of |
| * spinlock for 'Os->signalDB.lock' and 'signal->obj.wait.lock'. |
| */ |
| gceSTATUS status; |
| gcsSIGNAL_PTR signal = gcvNULL; |
| |
| status = _QueryIntegerId(&Os->signalDB, |
| (gctUINT32)(gctUINTPTR_T)Signal, |
| (gctPOINTER)&signal); |
| |
| if (gcmIS_SUCCESS(status)) |
| { |
| spin_lock(&signal->lock); |
| status = signal->done ? gcvSTATUS_TRUE : gcvSTATUS_FALSE; |
| spin_unlock(&signal->lock); |
| } |
| |
| 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 = gcvNULL; |
| gcmkHEADER_ARG("Os=0x%X Signal=0x%X Process=0x%X", Os, Signal, Process); |
| |
| gcmkVERIFY_ARGUMENT(Signal != gcvNULL); |
| gcmkVERIFY_ARGUMENT(MappedSignal != gcvNULL); |
| |
| mutex_lock(&Os->signalMutex); |
| |
| 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; |
| |
| mutex_unlock(&Os->signalMutex); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*MappedSignal=0x%X", *MappedSignal); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| mutex_unlock(&Os->signalMutex); |
| |
| 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, gcvTRUE, 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 |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(4,9,0) |
| gceSTATUS |
| gckOS_CreateSyncTimeline( |
| IN gckOS Os, |
| IN gceCORE Core, |
| OUT gctHANDLE * Timeline |
| ) |
| { |
| struct viv_sync_timeline * timeline; |
| char name[32]; |
| |
| snprintf(name, 32, "gccore-%u", (unsigned int) Core); |
| |
| /* Create viv sync timeline. */ |
| timeline = viv_sync_timeline_create(name, 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 gctSIGNAL Signal, |
| OUT gctINT * FenceFD |
| ) |
| { |
| int fd = -1; |
| struct viv_sync_timeline *timeline; |
| struct sync_pt * pt = gcvNULL; |
| struct sync_fence * fence; |
| char name[32]; |
| gcsSIGNAL_PTR signal; |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=0x%X Timeline=0x%X Signal=%d", |
| Os, Timeline, (gctUINT)(gctUINTPTR_T)Signal); |
| |
| gcmkONERROR( |
| _QueryIntegerId(&Os->signalDB, |
| (gctUINT32)(gctUINTPTR_T)Signal, |
| (gctPOINTER)&signal)); |
| |
| /* Cast timeline. */ |
| timeline = (struct viv_sync_timeline *) Timeline; |
| |
| fd = get_unused_fd_flags(O_CLOEXEC); |
| |
| if (fd < 0) |
| { |
| /* Out of resources. */ |
| gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); |
| } |
| |
| /* Create viv_sync_pt. */ |
| pt = viv_sync_pt_create(timeline, Signal); |
| |
| if (pt == gcvNULL) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| /* Reference sync_timeline. */ |
| signal->timeline = &timeline->obj; |
| |
| /* Build fence name. */ |
| snprintf(name, 32, "%.16s-signal_%lu", |
| current->comm, |
| (unsigned long)Signal); |
| |
| /* 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; |
| } |
| |
| static void |
| _NativeFenceSignaled( |
| struct sync_fence *fence, |
| struct sync_fence_waiter *waiter |
| ) |
| { |
| kfree(waiter); |
| sync_fence_put(fence); |
| } |
| |
| gceSTATUS |
| gckOS_WaitNativeFence( |
| IN gckOS Os, |
| IN gctHANDLE Timeline, |
| IN gctINT FenceFD, |
| IN gctUINT32 Timeout |
| ) |
| { |
| struct sync_timeline * timeline; |
| struct sync_fence * fence; |
| gctBOOL wait; |
| gceSTATUS status = gcvSTATUS_OK; |
| |
| gcmkHEADER_ARG("Os=0x%X Timeline=0x%X FenceFD=%d Timeout=%u", |
| Os, Timeline, FenceFD, Timeout); |
| |
| /* Get shortcut. */ |
| timeline = (struct sync_timeline *) Timeline; |
| |
| /* Get sync fence. */ |
| fence = sync_fence_fdget(FenceFD); |
| |
| if (!fence) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| |
| if (sync_fence_wait(fence, 0) == 0) |
| { |
| /* Already signaled. */ |
| sync_fence_put(fence); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| wait = gcvFALSE; |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0) |
| { |
| int i; |
| |
| for (i = 0; i < fence->num_fences; i++) |
| { |
| struct fence *f = fence->cbs[i].sync_pt; |
| struct sync_pt *pt = container_of(f, struct sync_pt, base); |
| |
| /* Do not need to wait on same timeline. */ |
| if ((sync_pt_parent(pt) != timeline) && !fence_is_signaled(f)) |
| { |
| wait = gcvTRUE; |
| break; |
| } |
| } |
| } |
| #else |
| { |
| struct list_head *pos; |
| list_for_each(pos, &fence->pt_list_head) |
| { |
| struct sync_pt * pt = |
| container_of(pos, struct sync_pt, pt_list); |
| |
| /* Do not need to wait on same timeline. */ |
| if (pt->parent != timeline) |
| { |
| wait = gcvTRUE; |
| break; |
| } |
| } |
| } |
| #endif |
| |
| if (wait) |
| { |
| int err; |
| long timeout = (Timeout == gcvINFINITE) ? - 1 : (long) Timeout; |
| err = sync_fence_wait(fence, timeout); |
| |
| /* Put the fence. */ |
| sync_fence_put(fence); |
| |
| switch (err) |
| { |
| case 0: |
| break; |
| case -ETIME: |
| status = gcvSTATUS_TIMEOUT; |
| break; |
| default: |
| gcmkONERROR(gcvSTATUS_GENERIC_IO); |
| break; |
| } |
| } |
| else |
| { |
| int err; |
| struct sync_fence_waiter *waiter; |
| waiter = (struct sync_fence_waiter *)kmalloc( |
| sizeof (struct sync_fence_waiter), gcdNOWARN | GFP_KERNEL); |
| |
| /* |
| * schedule a callback to put the sync_fence. Otherwise after this function |
| * is returned, the caller may free it since it's signaled. Then there's |
| * be a real signal on a free'ed sync fence. |
| */ |
| if (!waiter) |
| { |
| sync_fence_put(fence); |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| /* Schedule a waiter callback. */ |
| sync_fence_waiter_init(waiter, _NativeFenceSignaled); |
| err = sync_fence_wait_async(fence, waiter); |
| |
| switch (err) |
| { |
| case 0: |
| /* Put fence in callback function. */ |
| break; |
| case 1: |
| /* already signaled. */ |
| sync_fence_put(fence); |
| break; |
| default: |
| sync_fence_put(fence); |
| gcmkONERROR(gcvSTATUS_GENERIC_IO); |
| break; |
| } |
| } |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| # else /* v4.9.0 */ |
| |
| gceSTATUS |
| gckOS_CreateSyncTimeline( |
| IN gckOS Os, |
| IN gceCORE Core, |
| OUT gctHANDLE * Timeline |
| ) |
| { |
| struct viv_sync_timeline *timeline; |
| |
| char name[32]; |
| |
| snprintf(name, 32, "gccore-%u", (unsigned int) Core); |
| timeline = viv_sync_timeline_create(name, 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; |
| |
| /* Destroy timeline. */ |
| timeline = (struct viv_sync_timeline *) Timeline; |
| viv_sync_timeline_destroy(timeline); |
| |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_CreateNativeFence( |
| IN gckOS Os, |
| IN gctHANDLE Timeline, |
| IN gctSIGNAL Signal, |
| OUT gctINT * FenceFD |
| ) |
| { |
| struct fence *fence = NULL; |
| struct sync_file *sync = NULL; |
| int fd; |
| struct viv_sync_timeline *timeline; |
| gcsSIGNAL_PTR signal = gcvNULL; |
| gceSTATUS status = gcvSTATUS_OK; |
| |
| /* Create fence. */ |
| timeline = (struct viv_sync_timeline *) Timeline; |
| |
| gcmkONERROR( |
| _QueryIntegerId(&Os->signalDB, |
| (gctUINT32)(gctUINTPTR_T)Signal, |
| (gctPOINTER)&signal)); |
| |
| fence = viv_fence_create(timeline, signal); |
| |
| if (!fence) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| /* Create sync_file. */ |
| sync = sync_file_create(fence); |
| |
| if (!sync) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| /* Get a unused fd. */ |
| fd = get_unused_fd_flags(O_CLOEXEC); |
| |
| if (fd < 0) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); |
| } |
| |
| fd_install(fd, sync->file); |
| |
| *FenceFD = fd; |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (sync) |
| { |
| fput(sync->file); |
| } |
| |
| if (fence) |
| { |
| fence_put(fence); |
| } |
| |
| if (fd > 0) |
| { |
| put_unused_fd(fd); |
| } |
| |
| *FenceFD = -1; |
| return status; |
| } |
| |
| gceSTATUS |
| gckOS_WaitNativeFence( |
| IN gckOS Os, |
| IN gctHANDLE Timeline, |
| IN gctINT FenceFD, |
| IN gctUINT32 Timeout |
| ) |
| { |
| struct fence *fence; |
| struct viv_sync_timeline *timeline; |
| gceSTATUS status = gcvSTATUS_OK; |
| unsigned int i; |
| unsigned int numFences; |
| struct fence **fences; |
| unsigned long timeout; |
| |
| timeline = (struct viv_sync_timeline *) Timeline; |
| |
| fence = sync_file_get_fence(FenceFD); |
| |
| if (!fence) |
| { |
| gcmkONERROR(gcvSTATUS_GENERIC_IO); |
| } |
| |
| if (fence_is_array(fence)) |
| { |
| struct fence_array *array = to_fence_array(fence); |
| fences = array->fences; |
| numFences = array->num_fences; |
| } |
| else |
| { |
| fences = &fence; |
| numFences = 1; |
| } |
| |
| timeout = msecs_to_jiffies(Timeout); |
| |
| for (i = 0; i < numFences; i++) |
| { |
| struct fence *f = fences[i]; |
| |
| if (f->context != timeline->context && |
| !fence_is_signaled(f)) |
| { |
| signed long ret; |
| ret = fence_wait_timeout(fence, 1, timeout); |
| |
| if (ret == -ERESTARTSYS) |
| { |
| status = gcvSTATUS_INTERRUPTED; |
| break; |
| } |
| else if (ret <= 0) |
| { |
| status = gcvSTATUS_TIMEOUT; |
| break; |
| } |
| else |
| { |
| /* wait success. */ |
| timeout -= ret; |
| } |
| } |
| } |
| |
| fence_put(fence); |
| |
| return gcvSTATUS_OK; |
| |
| OnError: |
| return status; |
| } |
| |
| # endif /* v4.9.0 */ |
| #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; |
| |
| gctPHYS_ADDR_T phys_addr; |
| |
| allocator->ops->Physical(allocator, mdl, offset * PAGE_SIZE, &phys_addr); |
| |
| phys = (unsigned long)phys_addr; |
| |
| table[offset] = phys & PAGE_MASK; |
| |
| offset += 1; |
| } |
| |
| OnError: |
| |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| #endif |
| |
| gceSTATUS |
| gckOS_CPUPhysicalToGPUPhysical( |
| IN gckOS Os, |
| IN gctPHYS_ADDR_T CPUPhysical, |
| IN gctPHYS_ADDR_T * GPUPhysical |
| ) |
| { |
| gcsPLATFORM * platform; |
| gcmkHEADER_ARG("CPUPhysical=%p", 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 gctPHYS_ADDR_T * CPUPhysical |
| ) |
| { |
| gcsPLATFORM * platform; |
| gcmkHEADER_ARG("GPUPhysical=0x%X", GPUPhysical); |
| |
| platform = Os->device->platform; |
| |
| if (platform && platform->ops->getCPUPhysical) |
| { |
| gcmkVERIFY_OK( |
| platform->ops->getCPUPhysical(platform, GPUPhysical, CPUPhysical)); |
| } |
| else |
| { |
| *CPUPhysical = GPUPhysical; |
| } |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_PhysicalToPhysicalAddress( |
| IN gckOS Os, |
| IN gctPOINTER Physical, |
| IN gctUINT32 Offset, |
| OUT gctPHYS_ADDR_T * PhysicalAddress |
| ) |
| { |
| PLINUX_MDL mdl = (PLINUX_MDL)Physical; |
| gckALLOCATOR allocator = mdl->allocator; |
| |
| if (allocator) |
| { |
| return allocator->ops->Physical(allocator, mdl, Offset, PhysicalAddress); |
| } |
| |
| 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,27) |
| *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 |
| } |
| |
| 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->args.mmu; |
| #endif |
| return gcvSTATUS_OK; |
| } |
| else if (!strcmp(Option, "contiguousSize")) |
| { |
| *Value = device->contiguousSize; |
| return gcvSTATUS_OK; |
| } |
| else if (!strcmp(Option, "contiguousBase")) |
| { |
| *Value = (gctUINT32)device->contiguousBase; |
| return gcvSTATUS_OK; |
| } |
| else if (!strcmp(Option, "externalSize")) |
| { |
| *Value = device->externalSize; |
| return gcvSTATUS_OK; |
| } |
| else if (!strcmp(Option, "externalBase")) |
| { |
| *Value = device->externalBase; |
| return gcvSTATUS_OK; |
| } |
| else if (!strcmp(Option, "recovery")) |
| { |
| *Value = device->args.recovery; |
| return gcvSTATUS_OK; |
| } |
| else if (!strcmp(Option, "stuckDump")) |
| { |
| *Value = device->args.stuckDump; |
| return gcvSTATUS_OK; |
| } |
| else if (!strcmp(Option, "powerManagement")) |
| { |
| *Value = device->args.powerManagement; |
| return gcvSTATUS_OK; |
| } |
| else if (!strcmp(Option, "TA")) |
| { |
| *Value = 0; |
| return gcvSTATUS_OK; |
| } |
| else if (!strcmp(Option, "gpuProfiler")) |
| { |
| *Value = device->args.gpuProfiler; |
| return gcvSTATUS_OK; |
| } |
| |
| return gcvSTATUS_NOT_SUPPORTED; |
| } |
| |
| gceSTATUS |
| gckOS_MemoryGetSGT( |
| IN gckOS Os, |
| IN gctPHYS_ADDR Physical, |
| IN gctSIZE_T Offset, |
| IN gctSIZE_T Bytes, |
| OUT gctPOINTER *SGT |
| ) |
| { |
| PLINUX_MDL mdl; |
| gckALLOCATOR allocator; |
| gceSTATUS status = gcvSTATUS_OK; |
| |
| if (!Physical) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| |
| mdl = (PLINUX_MDL)Physical; |
| allocator = mdl->allocator; |
| |
| if (!allocator->ops->GetSGT) |
| { |
| gcmkONERROR(gcvSTATUS_NOT_SUPPORTED); |
| } |
| |
| if (Bytes > 0) |
| { |
| gcmkONERROR(allocator->ops->GetSGT(allocator, mdl, Offset, Bytes, SGT)); |
| } |
| |
| OnError: |
| return status; |
| } |
| |
| gceSTATUS |
| gckOS_MemoryMmap( |
| IN gckOS Os, |
| IN gctPHYS_ADDR Physical, |
| IN gctSIZE_T skipPages, |
| IN gctSIZE_T numPages, |
| INOUT gctPOINTER Vma |
| ) |
| { |
| PLINUX_MDL mdl; |
| gckALLOCATOR allocator; |
| gceSTATUS status = gcvSTATUS_OK; |
| |
| if (!Physical) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| |
| mdl = (PLINUX_MDL)Physical; |
| allocator = mdl->allocator; |
| |
| if (!allocator->ops->Mmap) |
| { |
| gcmkONERROR(gcvSTATUS_NOT_SUPPORTED); |
| } |
| |
| gcmkONERROR(allocator->ops->Mmap(allocator, mdl, skipPages, numPages, Vma)); |
| |
| OnError: |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckOS_WrapMemory |
| ** |
| ** Import a number of pages allocated by other allocator. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctUINT32 Flag |
| ** Memory type. |
| ** |
| ** 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. |
| */ |
| gceSTATUS |
| gckOS_WrapMemory( |
| IN gckOS Os, |
| IN gcsUSER_MEMORY_DESC_PTR Desc, |
| OUT gctSIZE_T *Bytes, |
| OUT gctPHYS_ADDR * Physical, |
| OUT gctBOOL *Contiguous |
| ) |
| { |
| PLINUX_MDL mdl = gcvNULL; |
| gceSTATUS status = gcvSTATUS_OUT_OF_MEMORY; |
| gckALLOCATOR allocator; |
| gcsATTACH_DESC desc; |
| gctSIZE_T bytes = 0; |
| |
| gcmkHEADER_ARG("Os=0x%X ", Os); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Physical != gcvNULL); |
| |
| mdl = _CreateMdl(Os); |
| if (mdl == gcvNULL) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| if (Desc->flag & gcvALLOC_FLAG_DMABUF) |
| { |
| desc.dmaBuf.dmabuf = gcmUINT64_TO_PTR(Desc->dmabuf); |
| |
| #if defined(CONFIG_DMA_SHARED_BUFFER) |
| { |
| struct dma_buf *dmabuf = (struct dma_buf*)desc.dmaBuf.dmabuf; |
| bytes = dmabuf->size; |
| } |
| #endif |
| } |
| else if (Desc->flag & gcvALLOC_FLAG_USERMEMORY) |
| { |
| desc.userMem.memory = gcmUINT64_TO_PTR(Desc->logical); |
| desc.userMem.physical = Desc->physical; |
| desc.userMem.size = Desc->size; |
| bytes = Desc->size; |
| } |
| else if (Desc->flag & gcvALLOC_FLAG_EXTERNAL_MEMORY) |
| { |
| desc.externalMem.info = Desc->externalMemoryInfo; |
| } |
| else |
| { |
| gcmkONERROR(gcvSTATUS_NOT_SUPPORTED); |
| } |
| |
| /* Walk all allocators. */ |
| list_for_each_entry(allocator, &Os->allocatorList, link) |
| { |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_OS, |
| "%s(%d) Flag = %x allocator->capability = %x", |
| __FUNCTION__, __LINE__, Desc->flag, allocator->capability); |
| |
| if ((Desc->flag & allocator->capability) != Desc->flag) |
| { |
| status = gcvSTATUS_NOT_SUPPORTED; |
| continue; |
| } |
| |
| if (Desc->flag == gcvALLOC_FLAG_EXTERNAL_MEMORY) |
| { |
| /* Use name to match suitable allocator for external memory. */ |
| if (!strncmp(Desc->externalMemoryInfo.allocatorName, |
| allocator->name, gcdEXTERNAL_MEMORY_NAME_MAX)) |
| { |
| status = gcvSTATUS_NOT_SUPPORTED; |
| continue; |
| } |
| } |
| |
| status = allocator->ops->Attach(allocator, &desc, mdl); |
| |
| if (gcmIS_SUCCESS(status)) |
| { |
| mdl->allocator = allocator; |
| break; |
| } |
| } |
| |
| /* Check status. */ |
| gcmkONERROR(status); |
| |
| mdl->dmaHandle = 0; |
| mdl->addr = 0; |
| |
| *Bytes = bytes ? bytes : mdl->numPages * PAGE_SIZE; |
| |
| /* Return physical address. */ |
| *Physical = (gctPHYS_ADDR) mdl; |
| |
| *Contiguous = mdl->contiguous; |
| |
| /* |
| * Add this to a global list. |
| * Will be used by get physical address |
| * and mapuser pointer functions. |
| */ |
| mutex_lock(&Os->mdlMutex); |
| list_add_tail(&mdl->link, &Os->mdlHead); |
| mutex_unlock(&Os->mdlMutex); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Physical=0x%X", *Physical); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (mdl != gcvNULL) |
| { |
| /* Free the memory. */ |
| _DestroyMdl(mdl); |
| } |
| |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| gceSTATUS |
| gckOS_GetPolicyID( |
| IN gckOS Os, |
| IN gceSURF_TYPE Type, |
| OUT gctUINT32_PTR PolicyID, |
| OUT gctUINT32_PTR AXIConfig |
| ) |
| { |
| gcsPLATFORM * platform = Os->device->platform; |
| |
| if (platform && platform->ops->getPolicyID) |
| { |
| return platform->ops->getPolicyID(platform, Type, PolicyID, AXIConfig); |
| } |
| |
| return gcvSTATUS_NOT_SUPPORTED; |
| } |
| |