| /**************************************************************************** |
| * |
| * The MIT License (MIT) |
| * |
| * Copyright (c) 2014 - 2020 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 - 2020 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_vxworks.h" |
| |
| #define _GC_OBJ_ZONE gcvZONE_OS |
| |
| #include "gc_hal_kernel_allocator.h" |
| |
| #define gcmkBUG_ON(x) \ |
| do { \ |
| if (!!(x)) \ |
| { \ |
| gcmkPRINT("[galcore]: BUG ON @ %s(%d)\n", __func__, __LINE__); \ |
| } \ |
| } while (0) |
| |
| static gctUINT32 ticksPerSecond = 0; |
| |
| /******************************************************************************\ |
| ******************************* Private Functions ****************************** |
| \******************************************************************************/ |
| static gctINT |
| _GetThreadID( |
| void |
| ) |
| { |
| return pthread_self(); |
| } |
| |
| /* Must hold Mdl->mpasMutex before call this function. */ |
| static inline PVX_MDL_MAP |
| _CreateMdlMap( |
| IN PVX_MDL Mdl, |
| IN gctINT ProcessID |
| ) |
| { |
| PVX_MDL_MAP mdlMap; |
| |
| gcmkHEADER_ARG("Mdl=0x%X ProcessID=%d", Mdl, ProcessID); |
| |
| mdlMap = (PVX_MDL_MAP)malloc(sizeof(struct _VX_MDL_MAP)); |
| |
| 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 PVX_MDL Mdl, |
| IN PVX_MDL_MAP MdlMap |
| ) |
| { |
| gcmkHEADER_ARG("Mdl=0x%X MdlMap=0x%X", Mdl, MdlMap); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_ARGUMENT(MdlMap != gcvNULL); |
| |
| list_del(&MdlMap->link); |
| free(MdlMap); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /* Must hold Mdl->mpasMutex before call this function. */ |
| extern PVX_MDL_MAP |
| FindMdlMap( |
| IN PVX_MDL Mdl, |
| IN gctINT ProcessID |
| ) |
| { |
| PVX_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 PVX_MDL |
| _CreateMdl( |
| IN gckOS Os |
| ) |
| { |
| PVX_MDL mdl; |
| |
| gcmkHEADER(); |
| |
| mdl = (PVX_MDL)malloc(sizeof(struct _VX_MDL)); |
| |
| if (mdl) |
| { |
| mdl->os = Os; |
| vxAtomicSet(&mdl->refs, 1); |
| pthread_mutex_init(&mdl->mapsMutex, 0); |
| INIT_LIST_HEAD(&mdl->mapsHead); |
| } |
| |
| gcmkFOOTER_ARG("0x%X", mdl); |
| return mdl; |
| } |
| |
| static gceSTATUS |
| _DestroyMdl( |
| IN PVX_MDL Mdl |
| ) |
| { |
| gcmkHEADER_ARG("Mdl=0x%X", Mdl); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_ARGUMENT(Mdl != gcvNULL); |
| |
| vxAtomicDec(&Mdl->refs); |
| if ((gctINT)vxAtomicGet(&Mdl->refs) == 0) |
| { |
| gckOS os = Mdl->os; |
| gckALLOCATOR allocator = Mdl->allocator; |
| PVX_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); |
| } |
| |
| pthread_mutex_lock(&Mdl->mapsMutex); |
| list_for_each_entry_safe(mdlMap, next, &Mdl->mapsHead, link) |
| { |
| gcmkVERIFY_OK(_DestroyMdlMap(Mdl, mdlMap)); |
| } |
| pthread_mutex_unlock(&Mdl->mapsMutex); |
| |
| if (Mdl->link.next) |
| { |
| /* Remove the node from global list.. */ |
| pthread_mutex_lock(&os->mdlMutex); |
| list_del(&Mdl->link); |
| pthread_mutex_unlock(&os->mdlMutex); |
| } |
| |
| free(Mdl); |
| } |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| static inline gceSTATUS |
| _QueryProcessPageTable( |
| IN gctPOINTER Logical, |
| OUT gctPHYS_ADDR_T * Address |
| ) |
| { |
| *Address = KM_TO_PHYS(Logical); |
| |
| return gcvSTATUS_OK; |
| } |
| |
| |
| 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) malloc(gcmSIZEOF(struct _gckOS)); |
| |
| 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; |
| |
| ticksPerSecond = sysClkRateGet(); |
| |
| /* Set allocateCount to 0, gckOS_Allocate has not been used yet. */ |
| vxAtomicSet(&os->allocateCount, 0); |
| |
| /* Initialize the memory lock. */ |
| pthread_mutex_init(&os->mdlMutex, 0); |
| |
| INIT_LIST_HEAD(&os->mdlHead); |
| |
| /* Get the kernel process ID. */ |
| os->kernelProcessID = _GetProcessID(); |
| |
| /* |
| * Initialize the signal manager. |
| */ |
| |
| /* Initialize mutex. */ |
| pthread_mutex_init(&os->signalMutex, 0); |
| |
| os->paddingPage = valloc(PAGE_SIZE); |
| if (os->paddingPage == gcvNULL) |
| { |
| /* Out of memory. */ |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| spinLockIsrInit(&os->registerAccessLock, 0); |
| |
| 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: |
| free(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); |
| |
| gckOS_FreeAllocators(Os); |
| |
| #ifdef CONFIG_IOMMU_SUPPORT |
| if (Os->iommu) |
| { |
| gckIOMMU_Destory(Os, Os->iommu); |
| } |
| #endif |
| |
| /* Mark the gckOS object as unknown. */ |
| Os->object.type = gcvOBJ_UNKNOWN; |
| |
| /* Free the gckOS object. */ |
| free(Os); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_CreateKernelMapping( |
| IN gckOS Os, |
| IN gctPHYS_ADDR Physical, |
| IN gctSIZE_T Offset, |
| IN gctSIZE_T Bytes, |
| OUT gctPOINTER * Logical |
| ) |
| { |
| gceSTATUS status = gcvSTATUS_OK; |
| PVX_MDL mdl = (PVX_MDL)Physical; |
| gckALLOCATOR allocator = mdl->allocator; |
| |
| gcmkHEADER_ARG("Os=%p Physical=%p Offset=0x%zx Bytes=0x%zx", |
| Os, Physical, Offset, Bytes); |
| |
| if (mdl->addr) |
| { |
| /* Already mapped whole memory. */ |
| *Logical = (gctUINT8_PTR)mdl->addr + Offset; |
| } |
| else |
| { |
| gcmkONERROR(allocator->ops->MapKernel(allocator, mdl, Offset, Bytes, Logical)); |
| } |
| |
| OnError: |
| gcmkFOOTER_ARG("*Logical=%p", gcmOPT_POINTER(Logical)); |
| return status; |
| } |
| |
| gceSTATUS |
| gckOS_DestroyKernelMapping( |
| IN gckOS Os, |
| IN gctPHYS_ADDR Physical, |
| IN gctPOINTER Logical |
| ) |
| { |
| PVX_MDL mdl = (PVX_MDL)Physical; |
| gckALLOCATOR allocator = mdl->allocator; |
| |
| gcmkHEADER_ARG("Os=%p Physical=%p Logical=%p", Os, Physical, Logical); |
| |
| if (mdl->addr) |
| { |
| /* Nothing to do. */ |
| } |
| else |
| { |
| allocator->ops->UnmapKernel(allocator, mdl, Logical); |
| } |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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); |
| |
| memory = (gctPOINTER) malloc(Bytes); |
| |
| if (memory == gcvNULL) |
| { |
| /* Out of memory. */ |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| /* Increase count. */ |
| vxAtomicInc(&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. */ |
| free(Memory); |
| |
| /* Decrease count. */ |
| vxAtomicDec(&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; |
| PVX_MDL_MAP mdlMap; |
| PVX_MDL mdl = (PVX_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); |
| |
| pthread_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, mdlMap, |
| gcvFALSE)); |
| } |
| |
| pthread_mutex_unlock(&mdl->mapsMutex); |
| |
| *Logical = mdlMap->vmaAddr; |
| |
| gcmkFOOTER_ARG("*Logical=0x%X", *Logical); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| pthread_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 |
| ) |
| { |
| PVX_MDL_MAP mdlMap; |
| PVX_MDL mdl = (PVX_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; |
| |
| pthread_mutex_lock(&mdl->mapsMutex); |
| |
| mdlMap = FindMdlMap(mdl, PID); |
| |
| if (mdlMap == gcvNULL) |
| { |
| pthread_mutex_unlock(&mdl->mapsMutex); |
| |
| gcmkFOOTER_ARG("status=%d", gcvSTATUS_INVALID_ARGUMENT); |
| return gcvSTATUS_INVALID_ARGUMENT; |
| } |
| |
| if (mdlMap->vmaAddr != gcvNULL) |
| { |
| gcmkBUG_ON(!allocator || !allocator->ops->UnmapUser); |
| allocator->ops->UnmapUser(allocator, mdl, mdlMap, mdl->bytes); |
| } |
| |
| gcmkVERIFY_OK(_DestroyMdlMap(mdl, mdlMap)); |
| |
| pthread_mutex_unlock(&mdl->mapsMutex); |
| } |
| |
| /* 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. |
| ** |
| ** gctUINT32 Flag |
| ** Allocation attribute. |
| ** |
| ** 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 gctUINT32 Flag, |
| IN OUT gctSIZE_T * Bytes, |
| OUT gctPHYS_ADDR * Physical, |
| OUT gctPOINTER * Logical |
| ) |
| { |
| gctSIZE_T bytes; |
| gctINT numPages; |
| PVX_MDL mdl = gcvNULL; |
| PVX_MDL_MAP mdlMap = gcvNULL; |
| gctPOINTER addr; |
| gceSTATUS status = gcvSTATUS_NOT_SUPPORTED; |
| gckALLOCATOR allocator; |
| |
| 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); |
| } |
| |
| gcmkASSERT(Flag & gcvALLOC_FLAG_CONTIGUOUS); |
| |
| /* 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->cacheable = Flag & gcvALLOC_FLAG_CACHEABLE; |
| |
| mdl->bytes = bytes; |
| mdl->numPages = numPages; |
| |
| mdl->contiguous = gcvTRUE; |
| |
| gcmkONERROR(allocator->ops->MapKernel(allocator, mdl, 0, bytes, &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, mdlMap, gcvFALSE)); |
| |
| *Logical = mdlMap->vmaAddr; |
| } |
| else |
| { |
| *Logical = addr; |
| } |
| |
| /* |
| * Add this to a global list. |
| * Will be used by get physical address |
| * and mapuser pointer functions. |
| */ |
| pthread_mutex_lock(&Os->mdlMutex); |
| list_add_tail(&mdl->link, &Os->mdlHead); |
| pthread_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 VX_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 gctPHYS_ADDR Physical, |
| IN gctPOINTER Logical, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| PVX_MDL mdl = (PVX_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 |
| ) |
| { |
| PVX_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->bytes = Size; |
| mdl->numPages = Size >> PAGE_SHIFT; |
| mdl->contiguous = gcvTRUE; |
| mdl->addr = gcvNULL; |
| mdl->gid = 0; |
| |
| /* |
| * Add this to a global list. |
| * Will be used by get physical address |
| * and mapuser pointer functions. |
| */ |
| pthread_mutex_lock(&Os->mdlMutex); |
| list_add_tail(&mdl->link, &Os->mdlHead); |
| pthread_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; |
| PVX_MDL mdl = (PVX_MDL)MemoryHandle; |
| |
| allocator = _FindAllocator(Os, gcvALLOC_FLAG_LINUX_RESERVED_MEM); |
| |
| /* If no allocator, how comes the memory? */ |
| gcmkBUG_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 |
| ) |
| { |
| spinLockIsrTake(&Os->registerAccessLock); |
| |
| if (Os->clockStates[Core] == gcvFALSE) |
| { |
| spinLockIsrGive(&Os->registerAccessLock); |
| |
| /* |
| * Read register when power off: |
| * 1. In shared IRQ, read register may be called and that's not our irq. |
| */ |
| return gcvSTATUS_GENERIC_IO; |
| } |
| |
| *Data = *((volatile unsigned int *)((gctUINT8 *)Os->device->registerBases[Core] + Address)); |
| |
| spinLockIsrGive(&Os->registerAccessLock); |
| |
| /* 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 |
| ) |
| { |
| spinLockIsrTake(&Os->registerAccessLock); |
| |
| if (Os->clockStates[Core] == gcvFALSE) |
| { |
| spinLockIsrGive(&Os->registerAccessLock); |
| |
| 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; |
| } |
| |
| *((volatile unsigned int *)((gctUINT8 *)Os->device->registerBases[Core] + Address)) = Data; |
| |
| spinLockIsrGive(&Os->registerAccessLock); |
| |
| /* Success. */ |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_WriteRegisterEx_NoDump( |
| IN gckOS Os, |
| IN gceCORE Core, |
| IN gctUINT32 Address, |
| IN gctUINT32 Data |
| ) |
| { |
| return gckOS_WriteRegisterEx(Os, Core, Address, Data); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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 |
| ) |
| { |
| PVX_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); |
| |
| pthread_mutex_lock(&Os->mdlMutex); |
| |
| if (Os->device->contiguousPhysical) |
| { |
| /* Try the contiguous memory pool. */ |
| mdl = (PVX_MDL) Os->device->contiguousPhysical; |
| |
| pthread_mutex_lock(&mdl->mapsMutex); |
| |
| status = _ConvertLogical2Physical(Os, Logical, ProcessID, mdl, Address); |
| |
| pthread_mutex_unlock(&mdl->mapsMutex); |
| } |
| |
| if (gcmIS_ERROR(status)) |
| { |
| /* Walk all MDLs. */ |
| list_for_each_entry(mdl, &Os->mdlHead, link) |
| { |
| pthread_mutex_lock(&mdl->mapsMutex); |
| |
| status = _ConvertLogical2Physical(Os, Logical, ProcessID, mdl, Address); |
| |
| pthread_mutex_unlock(&mdl->mapsMutex); |
| |
| if (gcmIS_SUCCESS(status)) |
| { |
| break; |
| } |
| } |
| } |
| |
| pthread_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)); |
| } |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Address=%p", *Address); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| gceSTATUS |
| gckOS_GetPhysicalFromHandle( |
| IN gckOS Os, |
| IN gctPHYS_ADDR Physical, |
| IN gctUINT32 Offset, |
| OUT gctPHYS_ADDR_T * PhysicalAddress |
| ) |
| { |
| PVX_MDL mdl = (PVX_MDL)Physical; |
| gckALLOCATOR allocator = mdl->allocator; |
| |
| return allocator->ops->Physical(allocator, mdl, Offset, PhysicalAddress); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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); |
| } |
| |
| gceSTATUS |
| _ConvertLogical2Physical( |
| IN gckOS Os, |
| IN gctPOINTER Logical, |
| IN gctUINT32 ProcessID, |
| IN PVX_MDL Mdl, |
| OUT gctPHYS_ADDR_T * Physical |
| ) |
| { |
| gckALLOCATOR allocator = Mdl->allocator; |
| gctUINT32 offset; |
| gceSTATUS status = gcvSTATUS_NOT_FOUND; |
| gctINT8_PTR vBase; |
| |
| vBase = (gctINT8_PTR) Mdl->addr; |
| |
| /* 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. |
| ** |
| ** gctPHYS_ADDR_T 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 gctPHYS_ADDR_T Physical, |
| IN gctSIZE_T Bytes, |
| OUT gctPOINTER * Logical |
| ) |
| { |
| *Logical = (gctPOINTER) PHYS_TO_KM(Physical); |
| |
| /* Success. */ |
| 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 |
| ) |
| { |
| 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. */ |
| pthread_mutex_destroy(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. */ |
| pthread_mutex_lock(&Mutex); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| for (;;) |
| { |
| /* Try to acquire the mutex. */ |
| if (pthread_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. */ |
| pthread_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 |
| ) |
| { |
| /* Exchange the pair of 32-bit values. */ |
| *OldValue = (gctUINT32) vxAtomicGet((atomic_t *) Target); |
| vxAtomicSet((atomic_t *) Target, NewValue); |
| |
| 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 |
| ) |
| { |
| /* Exchange the pair of pointers. */ |
| *OldValue = (gctPOINTER)(gctUINTPTR_T) vxAtomicGet((atomic_t *) Target); |
| vxAtomicSet((atomic_t *) Target, (gctUINTPTR_T)NewValue); |
| |
| 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; |
| do |
| { |
| oval = (gctUINT32) vxAtomicGet((atomic_t *) Atom); |
| nval = oval | Mask; |
| } |
| while (vxCas((atomic_t *) Atom, oval, nval) != 1); |
| 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; |
| |
| do |
| { |
| oval = (gctUINT32) vxAtomicGet((atomic_t *) Atom); |
| nval = oval & ~Mask; |
| } |
| while (vxCas((atomic_t *) Atom, oval, nval) != 1); |
| |
| 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. */ |
| vxAtomicSet((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 |
| ) |
| { |
| /* Return the current value of atom. */ |
| *Value = (gctINT32) vxAtomicGet((atomic_t *) Atom); |
| 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 |
| ) |
| { |
| /* Set the current value of atom. */ |
| vxAtomicSet((atomic_t *) Atom, Value); |
| 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 |
| ) |
| { |
| *Value = (gctINT32) vxAtomicGet((atomic_t *)Atom); |
| |
| /* Increment the atom. */ |
| vxAtomicInc((atomic_t *) Atom); |
| |
| 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 |
| ) |
| { |
| *Value = (gctINT32) vxAtomicGet((atomic_t *)Atom); |
| |
| /* Decrement the atom. */ |
| vxAtomicDec((atomic_t *) Atom); |
| |
| 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 |
| ) |
| { |
| gctUINT32 tps = ticksPerSecond; |
| gctUINT32 ticks = 0; |
| |
| gcmkHEADER_ARG("Os=0x%X Delay=%u", Os, Delay); |
| |
| if (Delay > 0) |
| { |
| tps = 1000 / tps; |
| ticks = Delay / tps + 1; |
| |
| taskDelay(ticks); |
| } |
| |
| /* 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 = tickGet() * 1000 / 60; |
| |
| 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 = (Time1 > Time2) ? 1 : 0; |
| |
| 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 timespec tv; |
| |
| clock_gettime(CLOCK_REALTIME, &tv); |
| |
| *Time = (tv.tv_sec * 1000000) + (tv.tv_nsec + 500 / 1000); |
| |
| 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 |
| ) |
| { |
| 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 gctUINT32 Flag, |
| IN OUT gctSIZE_T * Bytes, |
| OUT gctUINT32 * Gid, |
| OUT gctPHYS_ADDR * Physical |
| ) |
| { |
| gctINT numPages; |
| PVX_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); |
| } |
| |
| /* 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->addr = 0; |
| mdl->bytes = bytes; |
| mdl->numPages = numPages; |
| mdl->contiguous = Flag & gcvALLOC_FLAG_CONTIGUOUS; |
| mdl->cacheable = Flag & gcvALLOC_FLAG_CACHEABLE; |
| |
| /* |
| * Add this to a global list. |
| * Will be used by get physical address |
| * and mapuser pointer functions. |
| */ |
| pthread_mutex_lock(&Os->mdlMutex); |
| list_add_tail(&mdl->link, &Os->mdlHead); |
| pthread_mutex_unlock(&Os->mdlMutex); |
| |
| /* Return allocated bytes. */ |
| *Bytes = bytes; |
| |
| if (Gid != gcvNULL) |
| { |
| *Gid = mdl->gid; |
| } |
| |
| /* 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 |
| ) |
| { |
| PVX_MDL mdl = (PVX_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. |
| */ |
| gceSTATUS |
| gckOS_LockPages( |
| IN gckOS Os, |
| IN gctPHYS_ADDR Physical, |
| IN gctSIZE_T Bytes, |
| IN gctBOOL Cacheable, |
| OUT gctPOINTER * Logical |
| ) |
| { |
| gceSTATUS status; |
| PVX_MDL mdl; |
| PVX_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); |
| |
| mdl = (PVX_MDL) Physical; |
| allocator = mdl->allocator; |
| |
| pthread_mutex_lock(&mdl->mapsMutex); |
| |
| mdlMap = FindMdlMap(mdl, _GetProcessID()); |
| |
| if (mdlMap == gcvNULL) |
| { |
| mdlMap = _CreateMdlMap(mdl, _GetProcessID()); |
| |
| if (mdlMap == gcvNULL) |
| { |
| pthread_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, mdlMap, Cacheable); |
| |
| if (gcmIS_ERROR(status)) |
| { |
| pthread_mutex_unlock(&mdl->mapsMutex); |
| |
| gcmkFOOTER_ARG("*status=%d", status); |
| return status; |
| } |
| } |
| |
| mdlMap->count++; |
| |
| /* Convert pointer to MDL. */ |
| *Logical = mdlMap->vmaAddr; |
| |
| pthread_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 gceVIDMEM_TYPE Type |
| ) |
| { |
| gceSTATUS status = gcvSTATUS_OK; |
| PVX_MDL mdl; |
| gctUINT32* table; |
| gctUINT32 offset = 0; |
| |
| gctUINT32 bytes = PageCount * 4; |
| 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 = (PVX_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 |
| ); |
| |
| table = (gctUINT32 *)PageTable; |
| |
| 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 = ~0ULL; |
| |
| 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); |
| |
| { |
| for (i = 0; i < (PAGE_SIZE / 4096); i++) |
| { |
| gcmkONERROR( |
| gckMMU_SetPage(Os->device->kernels[Core]->mmu, |
| phys + (i * 4096), |
| gcvPAGE_TYPE_4K, |
| Writable, |
| table++)); |
| } |
| } |
| } |
| |
| offset += PAGE_SIZE; |
| } |
| |
| { |
| gckMMU mmu = Os->device->kernels[Core]->mmu; |
| gcsADDRESS_AREA * area = &mmu->dynamicArea4K; |
| |
| offset = (gctUINT8_PTR)PageTable - (gctUINT8_PTR)area->stlbLogical; |
| |
| /* must be in dynamic area. */ |
| gcmkASSERT(offset < area->stlbSize); |
| |
| gcmkVERIFY_OK(gckVIDMEM_NODE_CleanCache( |
| Os->device->kernels[Core], |
| area->stlbVideoMem, |
| offset, |
| PageTable, |
| bytes |
| )); |
| |
| if (mmu->mtlbVideoMem) |
| { |
| /* Flush MTLB table. */ |
| gcmkVERIFY_OK(gckVIDMEM_NODE_CleanCache( |
| Os->device->kernels[Core], |
| mmu->mtlbVideoMem, |
| offset, |
| mmu->mtlbLogical, |
| mmu->mtlbSize |
| )); |
| } |
| } |
| |
| OnError: |
| |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| gceSTATUS |
| gckOS_Map1MPages( |
| IN gckOS Os, |
| IN gceCORE Core, |
| IN gctPHYS_ADDR Physical, |
| IN gctSIZE_T PageCount, |
| IN gctUINT32 Address, |
| IN gctPOINTER PageTable, |
| IN gctBOOL Writable, |
| IN gceVIDMEM_TYPE Type |
| ) |
| { |
| return gcvSTATUS_OK; |
| } |
| |
| 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 |
| ) |
| { |
| PVX_MDL_MAP mdlMap; |
| PVX_MDL mdl = (PVX_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); |
| |
| pthread_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, |
| mdl->bytes); |
| |
| mdlMap->vmaAddr = gcvNULL; |
| } |
| } |
| } |
| |
| pthread_mutex_unlock(&mdl->mapsMutex); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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 |
| ) |
| { |
| 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. */ |
| memcpy(KernelPointer, Pointer, Size); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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 |
| ) |
| { |
| 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. */ |
| memcpy(Pointer, KernelPointer, Size); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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 |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Address=0x%X Data=%u", Os, Address, Data); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_ARGUMENT(Address != gcvNULL); |
| |
| /* Kernel address. */ |
| *(gctUINT32 *)Address = Data; |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_ReadMappedPointer( |
| IN gckOS Os, |
| IN gctPOINTER Address, |
| IN gctUINT32_PTR Data |
| ) |
| { |
| gcmkHEADER_ARG("Os=0x%X Address=0x%X Data=%u", Os, Address, Data); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_ARGUMENT(Address != gcvNULL); |
| |
| /* Kernel address. */ |
| *Data = *(gctUINT32_PTR)Address; |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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); |
| |
| intDisable(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); |
| |
| intEnable(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 ******************************** |
| *******************************************************************************/ |
| static gceSTATUS |
| _CacheOperation( |
| IN gckOS Os, |
| IN gctUINT32 ProcessID, |
| IN gctPHYS_ADDR Handle, |
| IN gctSIZE_T Offset, |
| IN gctPOINTER Logical, |
| IN gctSIZE_T Bytes, |
| IN gceCACHEOPERATION Operation |
| ) |
| { |
| PVX_MDL mdl = (PVX_MDL)Handle; |
| PVX_MDL_MAP mdlMap; |
| gckALLOCATOR allocator; |
| |
| if (!mdl || !mdl->allocator) |
| { |
| gcmkPRINT("[galcore]: %s: Logical=%p no mdl", __FUNCTION__, Logical); |
| return gcvSTATUS_INVALID_ARGUMENT; |
| } |
| |
| allocator = mdl->allocator; |
| |
| if (allocator->ops->Cache) |
| { |
| pthread_mutex_lock(&mdl->mapsMutex); |
| |
| mdlMap = FindMdlMap(mdl, ProcessID); |
| |
| pthread_mutex_unlock(&mdl->mapsMutex); |
| |
| if (ProcessID && mdlMap == gcvNULL) |
| { |
| return gcvSTATUS_INVALID_ARGUMENT; |
| } |
| |
| if ((!ProcessID && mdl->cacheable) || |
| (mdlMap && mdlMap->cacheable)) |
| { |
| allocator->ops->Cache(allocator, |
| mdl, Offset, Logical, Bytes, Operation); |
| |
| return gcvSTATUS_OK; |
| } |
| } |
| |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** 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. |
| ** |
| ** gctSIZE_T Offset |
| ** Offset to this memory block. |
| ** |
| ** 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 gctSIZE_T Offset, |
| IN gctPOINTER Logical, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=%p ProcessID=%d Handle=%p Offset=0x%llx Logical=%p Bytes=0x%zx", |
| Os, ProcessID, Handle, Offset, Logical, Bytes); |
| |
| gcmkONERROR(_CacheOperation(Os, ProcessID, |
| Handle, Offset, Logical, Bytes, |
| gcvCACHE_CLEAN)); |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** 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 gctSIZE_T Offset, |
| IN gctPOINTER Logical, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=%p ProcessID=%d Handle=%p Offset=0x%llx Logical=%p Bytes=0x%zx", |
| Os, ProcessID, Handle, Offset, Logical, Bytes); |
| |
| gcmkONERROR(_CacheOperation(Os, ProcessID, |
| Handle, Offset, Logical, Bytes, |
| gcvCACHE_INVALIDATE)); |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** 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 gctSIZE_T Offset, |
| IN gctPOINTER Logical, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Os=%p ProcessID=%d Handle=%p Offset=0x%llx Logical=%p Bytes=0x%zx", |
| Os, ProcessID, Handle, Offset, Logical, Bytes); |
| |
| gcmkONERROR(_CacheOperation(Os, ProcessID, |
| Handle, Offset, Logical, Bytes, |
| gcvCACHE_FLUSH)); |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ********************************* 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 = gcvSTATUS_OK; |
| gceCHIPPOWERSTATE state; |
| |
| gcmkHEADER_ARG("Os=%p Hardware=%p 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_SetPowerState(Hardware, |
| gcvPOWER_OFF_BROADCAST)); |
| break; |
| |
| case gcvBROADCAST_GPU_IDLE: |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_OS, "GPU idle."); |
| #if gcdPOWER_SUSPEND_WHEN_IDLE |
| state = gcvPOWER_SUSPEND_BROADCAST; |
| #else |
| state = gcvPOWER_IDLE_BROADCAST; |
| #endif |
| |
| /* Put GPU IDLE or SUSPEND. */ |
| gcmkONERROR( |
| gckHARDWARE_SetPowerState(Hardware, state)); |
| |
| /* 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_SetPowerState(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; |
| } |
| |
| 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 |
| ) |
| { |
| SEM_ID sem; |
| |
| gcmkHEADER_ARG("Os=0x%X", Os); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL); |
| |
| /* Initialize the semaphore. */ |
| sem = semBCreate(SEM_Q_FIFO, 1); |
| |
| /* Return to caller. */ |
| *Semaphore = (gctPOINTER) sem; |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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. */ |
| semTake(Semaphore,WAIT_FOREVER); |
| |
| /* 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 (semTake(Semaphore, NO_WAIT)) |
| { |
| /* 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. */ |
| semGive(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. */ |
| semDelete(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 (powerChange && (Power == gcvTRUE)) |
| { |
| if (platform && platform->ops->setPower) |
| { |
| gcmkVERIFY_OK(platform->ops->setPower(platform, Core, Power)); |
| } |
| |
| Os->powerStates[Core] = Power; |
| } |
| |
| if (clockChange) |
| { |
| if (!Clock) |
| { |
| spinLockIsrTake(&Os->registerAccessLock); |
| |
| /* Record clock off, ahead. */ |
| Os->clockStates[Core] = gcvFALSE; |
| |
| spinLockIsrGive(&Os->registerAccessLock); |
| } |
| |
| if (platform && platform->ops->setClock) |
| { |
| gcmkVERIFY_OK(platform->ops->setClock(platform, Core, Clock)); |
| } |
| |
| if (Clock) |
| { |
| spinLockIsrTake(&Os->registerAccessLock); |
| |
| /* Record clock on, behind. */ |
| Os->clockStates[Core] = gcvTRUE; |
| |
| spinLockIsrGive(&Os->registerAccessLock); |
| } |
| } |
| |
| 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 |
| ) |
| { |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_QueryProfileTickRate( |
| OUT gctUINT64_PTR TickRate |
| ) |
| { |
| return gcvSTATUS_OK; |
| } |
| |
| gctUINT32 |
| gckOS_ProfileToMS( |
| IN gctUINT64 Ticks |
| ) |
| { |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************\ |
| ******************************* 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)malloc(sizeof(gcsSIGNAL)); |
| |
| if (signal == gcvNULL) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| /* Save the process ID. */ |
| signal->process = (gctHANDLE)(gctUINTPTR_T) _GetProcessID(); |
| |
| signal->done = 0; |
| signal->sem = semBCreate(SEM_Q_FIFO, 1); |
| |
| spinLockTaskInit(&signal->lock, 0); |
| |
| signal->manualReset = ManualReset; |
| |
| vxAtomicSet(&signal->ref, 1); |
| |
| *Signal = (gctSIGNAL)signal; |
| |
| gcmkFOOTER_ARG("*Signal=0x%X", *Signal); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (signal != gcvNULL) |
| { |
| free(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 |
| ) |
| { |
| 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); |
| |
| pthread_mutex_lock(&Os->signalMutex); |
| acquired = gcvTRUE; |
| |
| signal = Signal; |
| |
| vxAtomicDec(&signal->ref); |
| if ((gctINT)vxAtomicGet(&signal->ref) == 0) |
| { |
| /* Free the sgianl. */ |
| semDelete(signal->sem); |
| free(signal); |
| } |
| |
| pthread_mutex_unlock(&Os->signalMutex); |
| acquired = gcvFALSE; |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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 |
| ) |
| { |
| gcsSIGNAL_PTR signal; |
| |
| 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); |
| |
| pthread_mutex_lock(&Os->signalMutex); |
| |
| signal = Signal; |
| |
| /* |
| * Signal saved in event is not referenced. Inc reference here to avoid |
| * concurrent issue: signaling the signal while another thread is destroying |
| * it. |
| */ |
| vxAtomicInc(&signal->ref); |
| |
| pthread_mutex_unlock(&Os->signalMutex); |
| |
| spinLockTaskTake(&signal->lock); |
| |
| if (State) |
| { |
| signal->done = 1; |
| semGive(signal->sem); |
| } |
| else |
| { |
| signal->done = 0; |
| } |
| |
| spinLockTaskGive(&signal->lock); |
| |
| pthread_mutex_lock(&Os->signalMutex); |
| |
| vxAtomicDec(&signal->ref); |
| |
| if ((gctINT) vxAtomicGet(&signal->ref) == 0) |
| { |
| /* Free the sgianl. */ |
| semDelete(signal->sem); |
| free(signal); |
| } |
| |
| pthread_mutex_unlock(&Os->signalMutex); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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); |
| |
| signal = Signal; |
| |
| spinLockTaskTake(&signal->lock); |
| done = signal->done; |
| |
| /* |
| * 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; |
| } |
| |
| spinLockTaskGive(&signal->lock); |
| } |
| else if (Wait == 0) |
| { |
| status = gcvSTATUS_TIMEOUT; |
| spinLockTaskGive(&signal->lock); |
| } |
| else |
| { |
| /* Convert wait to milliseconds. */ |
| int timeout = (Wait == gcvINFINITE) |
| ? WAIT_FOREVER |
| : Wait; |
| |
| int ret; |
| |
| spinLockTaskGive(&signal->lock); |
| while (!signal->done) |
| { |
| int wait = (timeout >= 1 || timeout < 0) ? 1 : timeout; |
| |
| ret = semTake(signal->sem, wait); |
| |
| if (timeout >= 0) |
| { |
| timeout -= wait; |
| |
| if (timeout == 0) |
| { |
| break; |
| } |
| } |
| } |
| |
| if (signal->done) |
| { |
| status = gcvSTATUS_OK; |
| |
| if (!signal->manualReset) |
| { |
| /* Auto reset. */ |
| signal->done = 0; |
| } |
| } |
| else |
| { |
| status = gcvSTATUS_TIMEOUT; |
| } |
| } |
| |
| /* Return status. */ |
| gcmkFOOTER_ARG("Signal=0x%lX status=%d", Signal, status); |
| return status; |
| } |
| |
| gceSTATUS |
| _QuerySignal( |
| IN gckOS Os, |
| IN gctSIGNAL Signal |
| ) |
| { |
| gceSTATUS status; |
| gcsSIGNAL_PTR signal = Signal; |
| |
| spinLockTaskTake(&signal->lock); |
| status = signal->done ? gcvSTATUS_TRUE : gcvSTATUS_FALSE; |
| spinLockTaskGive(&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 |
| ) |
| { |
| 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); |
| |
| pthread_mutex_lock(&Os->signalMutex); |
| signal = Signal; |
| |
| vxAtomicInc(&signal->ref); |
| |
| if ((gctINT) vxAtomicGet(&signal->ref) <= 1) |
| { |
| /* The previous value is 0, it has been deleted. */ |
| return gcvSTATUS_INVALID_ARGUMENT; |
| } |
| |
| *MappedSignal = (gctSIGNAL)Signal; |
| |
| pthread_mutex_unlock(&Os->signalMutex); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*MappedSignal=0x%X", *MappedSignal); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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); |
| } |
| |
| |
| /******************************************************************************\ |
| ******************************** Software Timer ******************************** |
| \******************************************************************************/ |
| |
| /******************************************************************************* |
| ** |
| ** 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 |
| ) |
| { |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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 |
| ) |
| { |
| 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 |
| ) |
| { |
| 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 |
| ) |
| { |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_GetProcessNameByPid( |
| IN gctINT Pid, |
| IN gctSIZE_T Length, |
| OUT gctUINT8_PTR String |
| ) |
| { |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_DumpCallStack( |
| IN gckOS Os |
| ) |
| { |
| 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 |
| ) |
| { |
| return gcvSTATUS_OK; |
| } |
| |
| #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; |
| PVX_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, |
| gcvALLOC_FLAG_CONTIGUOUS, |
| &bytes, |
| PageArrayPhysical, |
| PageArrayLogical |
| )); |
| |
| table = *PageArrayLogical; |
| |
| /* Convert pointer to MDL. */ |
| mdl = (PVX_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 |
| ) |
| { |
| PVX_MDL mdl = (PVX_MDL)Physical; |
| gckALLOCATOR allocator = mdl->allocator; |
| |
| if (allocator) |
| { |
| return allocator->ops->Physical(allocator, mdl, Offset, PhysicalAddress); |
| } |
| |
| return gcvSTATUS_NOT_SUPPORTED; |
| } |
| |
| gceSTATUS |
| gckOS_GetFd( |
| IN gctSTRING Name, |
| IN gcsFDPRIVATE_PTR Private, |
| OUT gctINT * Fd |
| ) |
| { |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckOS_QueryOption( |
| IN gckOS Os, |
| IN gctCONST_STRING Option, |
| OUT gctUINT64 * 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 |
| ) |
| { |
| PVX_MDL mdl; |
| gckALLOCATOR allocator; |
| gceSTATUS status = gcvSTATUS_OK; |
| |
| if (!Physical) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| |
| mdl = (PVX_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 |
| ) |
| { |
| PVX_MDL mdl; |
| PVX_MDL_MAP mdlMap; |
| gckALLOCATOR allocator; |
| gceSTATUS status = gcvSTATUS_OK; |
| gctBOOL cacheable = gcvFALSE; |
| |
| if (!Physical) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| |
| mdl = (PVX_MDL)Physical; |
| allocator = mdl->allocator; |
| |
| if (!allocator->ops->Mmap) |
| { |
| gcmkONERROR(gcvSTATUS_NOT_SUPPORTED); |
| } |
| |
| pthread_mutex_lock(&mdl->mapsMutex); |
| |
| mdlMap = FindMdlMap(mdl, _GetProcessID()); |
| if (mdlMap) |
| { |
| cacheable = mdlMap->cacheable; |
| } |
| |
| pthread_mutex_unlock(&mdl->mapsMutex); |
| |
| gcmkONERROR(allocator->ops->Mmap(allocator, mdl, cacheable, 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, |
| OUT gctSIZE_T * PageCountCpu |
| ) |
| { |
| PVX_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->addr = 0; |
| |
| mdl->bytes = bytes ? bytes : mdl->numPages * PAGE_SIZE; |
| *Bytes = mdl->bytes; |
| |
| /* Return physical address. */ |
| *Physical = (gctPHYS_ADDR) mdl; |
| |
| *Contiguous = mdl->contiguous; |
| |
| if (PageCountCpu) |
| { |
| *PageCountCpu = mdl->numPages; |
| } |
| |
| /* |
| * Add this to a global list. |
| * Will be used by get physical address |
| * and mapuser pointer functions. |
| */ |
| pthread_mutex_lock(&Os->mdlMutex); |
| list_add_tail(&mdl->link, &Os->mdlHead); |
| pthread_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 gceVIDMEM_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; |
| } |
| |