diff options
Diffstat (limited to 'drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel.c')
-rw-r--r-- | drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel.c | 6450 |
1 files changed, 6450 insertions, 0 deletions
diff --git a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel.c b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel.c new file mode 100644 index 000000000000..2cdc21f92930 --- /dev/null +++ b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel.c @@ -0,0 +1,6450 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2018 Vivante Corporation +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2018 Vivante Corporation +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_precomp.h" + +#if gcdDEC_ENABLE_AHB +#include "viv_dec300_main.h" +#endif + +#define _GC_OBJ_ZONE gcvZONE_KERNEL + +/******************************************************************************* +***** Version Signature *******************************************************/ + +#define _gcmTXT2STR(t) #t +#define gcmTXT2STR(t) _gcmTXT2STR(t) +const char * _VERSION = "\n\0$VERSION$" + gcmTXT2STR(gcvVERSION_MAJOR) "." + gcmTXT2STR(gcvVERSION_MINOR) "." + gcmTXT2STR(gcvVERSION_PATCH) ":" + gcmTXT2STR(gcvVERSION_BUILD) "$\n"; + +/******************************************************************************\ +******************************* gckKERNEL API Code ****************************** +\******************************************************************************/ + +#if gcmIS_DEBUG(gcdDEBUG_TRACE) +#define gcmDEFINE2TEXT(d) #d +gctCONST_STRING _DispatchText[] = +{ + gcmDEFINE2TEXT(gcvHAL_QUERY_VIDEO_MEMORY), + gcmDEFINE2TEXT(gcvHAL_QUERY_CHIP_IDENTITY), + gcmDEFINE2TEXT(gcvHAL_ALLOCATE_NON_PAGED_MEMORY), + gcmDEFINE2TEXT(gcvHAL_FREE_NON_PAGED_MEMORY), + gcmDEFINE2TEXT(gcvHAL_ALLOCATE_CONTIGUOUS_MEMORY), + gcmDEFINE2TEXT(gcvHAL_FREE_CONTIGUOUS_MEMORY), + gcmDEFINE2TEXT(gcvHAL_ALLOCATE_VIDEO_MEMORY), + gcmDEFINE2TEXT(gcvHAL_ALLOCATE_LINEAR_VIDEO_MEMORY), + gcmDEFINE2TEXT(gcvHAL_RELEASE_VIDEO_MEMORY), + gcmDEFINE2TEXT(gcvHAL_MAP_MEMORY), + gcmDEFINE2TEXT(gcvHAL_UNMAP_MEMORY), + gcmDEFINE2TEXT(gcvHAL_MAP_USER_MEMORY), + gcmDEFINE2TEXT(gcvHAL_UNMAP_USER_MEMORY), + gcmDEFINE2TEXT(gcvHAL_LOCK_VIDEO_MEMORY), + gcmDEFINE2TEXT(gcvHAL_UNLOCK_VIDEO_MEMORY), + gcmDEFINE2TEXT(gcvHAL_EVENT_COMMIT), + gcmDEFINE2TEXT(gcvHAL_USER_SIGNAL), + gcmDEFINE2TEXT(gcvHAL_SIGNAL), + gcmDEFINE2TEXT(gcvHAL_WRITE_DATA), + gcmDEFINE2TEXT(gcvHAL_COMMIT), + gcmDEFINE2TEXT(gcvHAL_STALL), + gcmDEFINE2TEXT(gcvHAL_READ_REGISTER), + gcmDEFINE2TEXT(gcvHAL_WRITE_REGISTER), + gcmDEFINE2TEXT(gcvHAL_GET_PROFILE_SETTING), + gcmDEFINE2TEXT(gcvHAL_SET_PROFILE_SETTING), + gcmDEFINE2TEXT(gcvHAL_PROFILE_REGISTERS_2D), + gcmDEFINE2TEXT(gcvHAL_READ_ALL_PROFILE_REGISTERS_PART1), + gcmDEFINE2TEXT(gcvHAL_READ_ALL_PROFILE_REGISTERS_PART2), + gcmDEFINE2TEXT(gcvHAL_READ_PROFILER_REGISTER_SETTING), + gcmDEFINE2TEXT(gcvHAL_SET_POWER_MANAGEMENT_STATE), + gcmDEFINE2TEXT(gcvHAL_QUERY_POWER_MANAGEMENT_STATE), + gcmDEFINE2TEXT(gcvHAL_GET_BASE_ADDRESS), + gcmDEFINE2TEXT(gcvHAL_SET_IDLE), + gcmDEFINE2TEXT(gcvHAL_QUERY_KERNEL_SETTINGS), + gcmDEFINE2TEXT(gcvHAL_RESET), + gcmDEFINE2TEXT(gcvHAL_MAP_PHYSICAL), + gcmDEFINE2TEXT(gcvHAL_DEBUG), + gcmDEFINE2TEXT(gcvHAL_CACHE), + gcmDEFINE2TEXT(gcvHAL_TIMESTAMP), + gcmDEFINE2TEXT(gcvHAL_DATABASE), + gcmDEFINE2TEXT(gcvHAL_VERSION), + gcmDEFINE2TEXT(gcvHAL_CHIP_INFO), + gcmDEFINE2TEXT(gcvHAL_ATTACH), + gcmDEFINE2TEXT(gcvHAL_DETACH), + gcmDEFINE2TEXT(gcvHAL_SET_TIMEOUT), + gcmDEFINE2TEXT(gcvHAL_GET_FRAME_INFO), + gcmDEFINE2TEXT(gcvHAL_DUMP_GPU_PROFILE), + gcmDEFINE2TEXT(gcvHAL_QUERY_COMMAND_BUFFER), + gcmDEFINE2TEXT(gcvHAL_COMMIT_DONE), + gcmDEFINE2TEXT(gcvHAL_DUMP_GPU_STATE), + gcmDEFINE2TEXT(gcvHAL_DUMP_EVENT), + gcmDEFINE2TEXT(gcvHAL_ALLOCATE_VIRTUAL_COMMAND_BUFFER), + gcmDEFINE2TEXT(gcvHAL_FREE_VIRTUAL_COMMAND_BUFFER), + gcmDEFINE2TEXT(gcvHAL_SET_FSCALE_VALUE), + gcmDEFINE2TEXT(gcvHAL_GET_FSCALE_VALUE), + gcmDEFINE2TEXT(gcvHAL_EXPORT_VIDEO_MEMORY), + gcmDEFINE2TEXT(gcvHAL_NAME_VIDEO_MEMORY), + gcmDEFINE2TEXT(gcvHAL_IMPORT_VIDEO_MEMORY), + gcmDEFINE2TEXT(gcvHAL_QUERY_RESET_TIME_STAMP), + gcmDEFINE2TEXT(gcvHAL_READ_REGISTER_EX), + gcmDEFINE2TEXT(gcvHAL_WRITE_REGISTER_EX), + gcmDEFINE2TEXT(gcvHAL_CREATE_NATIVE_FENCE), + gcmDEFINE2TEXT(gcvHAL_WAIT_NATIVE_FENCE), + gcmDEFINE2TEXT(gcvHAL_DESTROY_MMU), + gcmDEFINE2TEXT(gcvHAL_SHBUF), + gcmDEFINE2TEXT(gcvHAL_GET_GRAPHIC_BUFFER_FD), + gcmDEFINE2TEXT(gcvHAL_SET_VIDEO_MEMORY_METADATA), + gcmDEFINE2TEXT(gcvHAL_GET_VIDEO_MEMORY_FD), + gcmDEFINE2TEXT(gcvHAL_CONFIG_POWER_MANAGEMENT), + gcmDEFINE2TEXT(gcvHAL_WRAP_USER_MEMORY), + gcmDEFINE2TEXT(gcvHAL_WAIT_FENCE), +#if gcdDEC_ENABLE_AHB + gcmDEFINE2TEXT(gcvHAL_DEC300_READ), + gcmDEFINE2TEXT(gcvHAL_DEC300_WRITE), + gcmDEFINE2TEXT(gcvHAL_DEC300_FLUSH), + gcmDEFINE2TEXT(gcvHAL_DEC300_FLUSH_WAIT), +#endif + gcmDEFINE2TEXT(gcvHAL_BOTTOM_HALF_UNLOCK_VIDEO_MEMORY), + gcmDEFINE2TEXT(gcvHAL_QUERY_CHIP_OPTION), +}; +#endif + +#if gcdGPU_TIMEOUT && gcdINTERRUPT_STATISTIC +void +_MonitorTimerFunction( + gctPOINTER Data + ) +{ + gckKERNEL kernel = (gckKERNEL)Data; + gctINT32 pendingInterrupt; + gctBOOL reset = gcvFALSE; + gctINT32 mask; + gctUINT32 advance = kernel->timeOut/2; + +#if gcdENABLE_VG + if (kernel->core == gcvCORE_VG) + { + return; + } +#endif + + if (kernel->monitorTimerStop) + { + /* Stop. */ + return; + } + + gckOS_AtomGet(kernel->os, kernel->eventObj->interruptCount, &pendingInterrupt); + + if (pendingInterrupt < 0) + { + gctINT i = 0 - pendingInterrupt; + gctINT pendingMask; + + gcmkVERIFY_OK(gckOS_AtomGet( + kernel->os, + kernel->hardware->pendingEvent, + &pendingMask + )); + + gcmkPRINT("[galcore]: Number of pending interrupt is %d mask is %x", + pendingInterrupt, pendingMask); + + while (i--) + { + /* Ignore counting which should not exist. */ + gckOS_AtomIncrement(kernel->os, kernel->eventObj->interruptCount, &pendingInterrupt); + } + + gckOS_AtomGet(kernel->os, kernel->eventObj->interruptCount, &pendingInterrupt); + } + + if (kernel->monitoring == gcvFALSE) + { + if (pendingInterrupt) + { + /* Begin to mointor GPU state. */ + kernel->monitoring = gcvTRUE; + + /* Record current state. */ + kernel->lastCommitStamp = kernel->eventObj->lastCommitStamp; + kernel->restoreAddress = kernel->hardware->lastWaitLink; + gcmkVERIFY_OK(gckOS_AtomGet( + kernel->os, + kernel->hardware->pendingEvent, + &kernel->restoreMask + )); + + /* Clear timeout. */ + kernel->timer = 0; + } + } + else + { + if (pendingInterrupt) + { + gcmkVERIFY_OK(gckOS_AtomGet( + kernel->os, + kernel->hardware->pendingEvent, + &mask + )); + + if (kernel->eventObj->lastCommitStamp == kernel->lastCommitStamp + && kernel->hardware->lastWaitLink == kernel->restoreAddress + && mask == kernel->restoreMask + ) + { + /* GPU state is not changed, accumlate timeout. */ + kernel->timer += advance; + + if (kernel->timer >= kernel->timeOut) + { + /* GPU stuck, trigger reset. */ + reset = gcvTRUE; + } + } + else + { + /* GPU state changed, cancel current timeout.*/ + kernel->monitoring = gcvFALSE; + } + } + else + { + /* GPU finish all jobs, cancel current timeout*/ + kernel->monitoring = gcvFALSE; + } + } + + if (reset) + { + gckKERNEL_Recovery(kernel); + + /* Work in this timeout is done. */ + kernel->monitoring = gcvFALSE; + } + + gcmkVERIFY_OK(gckOS_StartTimer(kernel->os, kernel->monitorTimer, advance)); +} +#endif + +#if gcdPROCESS_ADDRESS_SPACE +gceSTATUS +_MapCommandBuffer( + IN gckKERNEL Kernel + ) +{ + gceSTATUS status; + gctUINT32 i; + gctPHYS_ADDR_T physical; + gctUINT32 address; + gckMMU mmu; + + gcmkONERROR(gckKERNEL_GetProcessMMU(Kernel, &mmu)); + + for (i = 0; i < gcdCOMMAND_QUEUES; i++) + { + gcmkONERROR(gckOS_GetPhysicalAddress( + Kernel->os, + Kernel->command->queues[i].logical, + &physical + )); + + gcmkSAFECASTPHYSADDRT(address, physical); + + gcmkONERROR(gckMMU_FlatMapping(mmu, address, 1)); + } + + return gcvSTATUS_OK; + +OnError: + return status; +} +#endif + +void +_DumpDriverConfigure( + IN gckKERNEL Kernel + ) +{ + gcmkPRINT_N(0, "**************************\n"); + gcmkPRINT_N(0, "*** GPU DRV CONFIG ***\n"); + gcmkPRINT_N(0, "**************************\n"); + + gcmkPRINT("Galcore version %d.%d.%d.%d\n", + gcvVERSION_MAJOR, gcvVERSION_MINOR, gcvVERSION_PATCH, gcvVERSION_BUILD); + + gckOS_DumpParam(); +} + +void +_DumpState( + IN gckKERNEL Kernel + ) +{ + /* Dump GPU Debug registers. */ + gcmkVERIFY_OK(gckHARDWARE_DumpGPUState(Kernel->hardware)); + + gcmkVERIFY_OK(gckCOMMAND_DumpExecutingBuffer(Kernel->command)); + + /* Dump Pending event. */ + gcmkVERIFY_OK(gckEVENT_Dump(Kernel->eventObj)); + + /* Dump Process DB. */ + gcmkVERIFY_OK(gckKERNEL_DumpProcessDB(Kernel)); + +#if gcdRECORD_COMMAND + /* Dump record. */ + gckRECORDER_Dump(Kernel->command->recorder); +#endif +} + +static gceHARDWARE_TYPE +_GetHardwareType( + IN gckKERNEL Kernel + ) +{ + gceHARDWARE_TYPE type; + gcmkHEADER(); + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + +#if gcdENABLE_VG + if (Kernel->vg) + { + type = gcvHARDWARE_VG; + } + else +#endif + { + type = Kernel->hardware->type; + } + + gcmkFOOTER_ARG("type=%d", type); + return type; +} + +gceSTATUS +_SetRecovery( + IN gckKERNEL Kernel, + IN gctBOOL Recovery, + IN gctUINT32 StuckDump + ) +{ + Kernel->recovery = Recovery; + + if (Recovery == gcvFALSE) + { + /* Dump stuck information if Recovery is disabled. */ + Kernel->stuckDump = gcmMAX(StuckDump, gcvSTUCK_DUMP_USER_COMMAND); + } + + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckKERNEL_Construct +** +** Construct a new gckKERNEL object. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gceCORE Core +** Specified core. +** +** IN gctPOINTER Context +** Pointer to a driver defined context. +** +** IN gckDB SharedDB, +** Pointer to a shared DB. +** +** OUTPUT: +** +** gckKERNEL * Kernel +** Pointer to a variable that will hold the pointer to the gckKERNEL +** object. +*/ + +gceSTATUS +gckKERNEL_Construct( + IN gckOS Os, + IN gceCORE Core, + IN gctUINT ChipID, + IN gctPOINTER Context, + IN gckDEVICE Device, + IN gckDB SharedDB, + OUT gckKERNEL * Kernel + ) +{ + gckKERNEL kernel = gcvNULL; + gceSTATUS status; + gctSIZE_T i; + gctPOINTER pointer = gcvNULL; + gctUINT32 recovery; + gctUINT32 stuckDump; + + gcmkHEADER_ARG("Os=0x%x Context=0x%x", Os, Context); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Kernel != gcvNULL); + + /* Allocate the gckKERNEL object. */ + gcmkONERROR(gckOS_Allocate(Os, + gcmSIZEOF(struct _gckKERNEL), + &pointer)); + + /* Zero the object. */ + gckOS_ZeroMemory(pointer, gcmSIZEOF(struct _gckKERNEL)); + + kernel = pointer; + + /* Initialize the gckKERNEL object. */ + kernel->object.type = gcvOBJ_KERNEL; + kernel->os = Os; + kernel->core = Core; + kernel->device = Device; + kernel->chipID = ChipID; + +#if gcdENABLE_TRUST_APPLICATION + /* Connect to security service for this GPU. */ + gcmkONERROR(gckKERNEL_SecurityOpen(kernel, kernel->core, &kernel->securityChannel)); +#endif + + if (SharedDB == gcvNULL) + { + gcmkONERROR(gckOS_Allocate(Os, + gcmSIZEOF(struct _gckDB), + &pointer)); + + kernel->db = pointer; + kernel->dbCreated = gcvTRUE; + kernel->db->freeDatabase = gcvNULL; + kernel->db->freeRecord = gcvNULL; + kernel->db->dbMutex = gcvNULL; + kernel->db->lastDatabase = gcvNULL; + kernel->db->idleTime = 0; + kernel->db->lastIdle = 0; + kernel->db->lastSlowdown = 0; + + for (i = 0; i < gcmCOUNTOF(kernel->db->db); ++i) + { + kernel->db->db[i] = gcvNULL; + } + + /* Construct a database mutex. */ + gcmkONERROR(gckOS_CreateMutex(Os, &kernel->db->dbMutex)); + + /* Construct a video memory name database. */ + gcmkONERROR(gckKERNEL_CreateIntegerDatabase(kernel, &kernel->db->nameDatabase)); + + /* Construct a video memory name database mutex. */ + gcmkONERROR(gckOS_CreateMutex(Os, &kernel->db->nameDatabaseMutex)); + + /* Construct a pointer name database. */ + gcmkONERROR(gckKERNEL_CreateIntegerDatabase(kernel, &kernel->db->pointerDatabase)); + + /* Construct a pointer name database mutex. */ + gcmkONERROR(gckOS_CreateMutex(Os, &kernel->db->pointerDatabaseMutex)); + + /* Initialize on fault vidmem list. */ + gcsLIST_Init(&kernel->db->onFaultVidmemList); + } + else + { + kernel->db = SharedDB; + kernel->dbCreated = gcvFALSE; + } + + for (i = 0; i < gcmCOUNTOF(kernel->timers); ++i) + { + kernel->timers[i].startTime = 0; + kernel->timers[i].stopTime = 0; + } + + /* Save context. */ + kernel->context = Context; + + /* Construct atom holding number of clients. */ + kernel->atomClients = gcvNULL; + gcmkONERROR(gckOS_AtomConstruct(Os, &kernel->atomClients)); + + kernel->recovery = gcvTRUE; + kernel->stuckDump = gcvSTUCK_DUMP_NONE; + + /* Override default recovery and stuckDump setting. */ + status = gckOS_QueryOption(Os, "recovery", &recovery); + + if (gcmIS_SUCCESS(status)) + { + status = gckOS_QueryOption(Os, "stuckDump", &stuckDump); + + gcmkASSERT(status == gcvSTATUS_OK); + + _SetRecovery(kernel, recovery, stuckDump); + } + + /* Need the kernel reference before gckKERNEL_Construct() completes. + gckOS_MapPagesEx() is called to map kernel virtual command buffers. */ + *Kernel = kernel; + + kernel->virtualBufferHead = + kernel->virtualBufferTail = gcvNULL; + + gcmkONERROR( + gckOS_CreateMutex(Os, (gctPOINTER)&kernel->virtualBufferLock)); + +#if gcdENABLE_VG + kernel->vg = gcvNULL; + + if (Core == gcvCORE_VG) + { + gctUINT32 contiguousBase; + gctUINT32 contiguousSize = 0; + + /* Construct the gckMMU object. */ + gcmkONERROR( + gckVGKERNEL_Construct(Os, Context, kernel, &kernel->vg)); + + kernel->timeOut = gcdGPU_TIMEOUT; + + status = gckOS_QueryOption(Os, "contiguousBase", &contiguousBase); + + if (gcmIS_SUCCESS(status)) + { + status = gckOS_QueryOption(Os, "contiguousSize", &contiguousSize); + } + + if (gcmIS_SUCCESS(status) && contiguousSize) + { + gctUINT64 gpuContiguousBase; + + gcmkONERROR(gckOS_CPUPhysicalToGPUPhysical(Os, contiguousBase, &gpuContiguousBase)); + + gcmkSAFECASTPHYSADDRT(kernel->contiguousBaseAddress, gpuContiguousBase); + } + } + else +#endif + { + /* Construct the gckHARDWARE object. */ + gcmkONERROR( + gckHARDWARE_Construct(Os, kernel->core, &kernel->hardware)); + + /* Set pointer to gckKERNEL object in gckHARDWARE object. */ + kernel->hardware->kernel = kernel; + + kernel->timeOut = kernel->hardware->type == gcvHARDWARE_2D + ? gcdGPU_2D_TIMEOUT + : gcdGPU_TIMEOUT + ; + + /* Initialize virtual command buffer. */ +#if gcdALLOC_CMD_FROM_RESERVE || gcdSECURITY || gcdDISABLE_GPU_VIRTUAL_ADDRESS || !USE_KERNEL_VIRTUAL_BUFFERS + kernel->virtualCommandBuffer = gcvFALSE; +#else + kernel->virtualCommandBuffer = kernel->hardware->options.enableMMU; +#endif + +#if gcdSHARED_PAGETABLE + /* Construct the gckMMU object. */ + gcmkONERROR( + gckMMU_Construct(kernel, gcdMMU_SIZE, &kernel->mmu)); +#else + if (Device == gcvNULL) + { + /* Construct the gckMMU object. */ + gcmkONERROR( + gckMMU_Construct(kernel, gcdMMU_SIZE, &kernel->mmu)); + } + else + { + gcmkONERROR(gckDEVICE_GetMMU(Device, kernel->hardware->type, &kernel->mmu)); + + if (kernel->mmu == gcvNULL) + { + gcmkONERROR( + gckMMU_Construct(kernel, gcdMMU_SIZE, &kernel->mmu)); + + gcmkONERROR( + gckDEVICE_SetMMU(Device, kernel->hardware->type, kernel->mmu)); + } + } + + gcmkVERIFY_OK(gckMMU_AttachHardware(kernel->mmu, kernel->hardware)); +#endif + + kernel->contiguousBaseAddress = kernel->mmu->contiguousBaseAddress; + kernel->externalBaseAddress = kernel->mmu->externalBaseAddress; + + /* Construct the gckCOMMAND object. */ + gcmkONERROR( + gckCOMMAND_Construct(kernel, &kernel->command)); + + if (gckHARDWARE_IsFeatureAvailable(kernel->hardware, gcvFEATURE_ASYNC_BLIT)) + { + /* Construct the gckASYNC_COMMAND object for BLT engine. */ + gcmkONERROR(gckASYNC_COMMAND_Construct(kernel, &kernel->asyncCommand)); + + /* Construct gckEVENT for BLT. */ + gcmkONERROR(gckEVENT_Construct(kernel, &kernel->asyncEvent)); + + kernel->asyncEvent->asyncCommand = kernel->asyncCommand; + + kernel->command->asyncCommand = kernel->asyncCommand; + } + + /* Construct the gckEVENT object. */ + gcmkONERROR( + gckEVENT_Construct(kernel, &kernel->eventObj)); + + gcmkVERIFY_OK(gckOS_GetTime(&kernel->resetTimeStamp)); + + gcmkONERROR(gckHARDWARE_PrepareFunctions(kernel->hardware)); + + /* Initialize the hardware. */ + gcmkONERROR( + gckHARDWARE_InitializeHardware(kernel->hardware)); + +#if gcdDVFS + if (gckHARDWARE_IsFeatureAvailable(kernel->hardware, + gcvFEATURE_DYNAMIC_FREQUENCY_SCALING)) + { + gcmkONERROR(gckDVFS_Construct(kernel->hardware, &kernel->dvfs)); + gcmkONERROR(gckDVFS_Start(kernel->dvfs)); + } +#endif + +#if COMMAND_PROCESSOR_VERSION == 1 + /* Start the command queue. */ + gcmkONERROR(gckCOMMAND_Start(kernel->command)); +#endif + } + +#if VIVANTE_PROFILER + /* Initialize profile setting */ + kernel->profileEnable = gcvFALSE; + kernel->profileCleanRegister = gcvTRUE; +#endif + +#if gcdANDROID_NATIVE_FENCE_SYNC + gcmkONERROR(gckOS_CreateSyncTimeline(Os, Core, &kernel->timeline)); +#endif + +#if gcdSECURITY + /* Connect to security service for this GPU. */ + gcmkONERROR(gckKERNEL_SecurityOpen(kernel, kernel->core, &kernel->securityChannel)); +#endif + +#if gcdGPU_TIMEOUT && gcdINTERRUPT_STATISTIC + if (kernel->timeOut) + { + gcmkVERIFY_OK(gckOS_CreateTimer( + Os, + (gctTIMERFUNCTION)_MonitorTimerFunction, + (gctPOINTER)kernel, + &kernel->monitorTimer + )); + + kernel->monitoring = gcvFALSE; + + kernel->monitorTimerStop = gcvFALSE; + + gcmkVERIFY_OK(gckOS_StartTimer( + Os, + kernel->monitorTimer, + 100 + )); + } +#endif + + /* Return pointer to the gckKERNEL object. */ + *Kernel = kernel; + + /* Success. */ + gcmkFOOTER_ARG("*Kernel=0x%x", *Kernel); + return gcvSTATUS_OK; + +OnError: + *Kernel = gcvNULL; + + if (kernel != gcvNULL) + { + gckKERNEL_Destroy(kernel); + } + + /* Return the error. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_Destroy +** +** Destroy an gckKERNEL object. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object to destroy. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_Destroy( + IN gckKERNEL Kernel + ) +{ + gctSIZE_T i; + gcsDATABASE_PTR database, databaseNext; + gcsDATABASE_RECORD_PTR record, recordNext; + + gcmkHEADER_ARG("Kernel=0x%x", Kernel); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); +#if QNX_SINGLE_THREADED_DEBUGGING + gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Kernel->debugMutex)); +#endif + + /* Destroy the database. */ + if (Kernel->dbCreated) + { + for (i = 0; i < gcmCOUNTOF(Kernel->db->db); ++i) + { + if (Kernel->db->db[i] != gcvNULL) + { + gcmkVERIFY_OK( + gckKERNEL_DestroyProcessDB(Kernel, Kernel->db->db[i]->processID)); + } + } + + /* Free all databases. */ + for (database = Kernel->db->freeDatabase; + database != gcvNULL; + database = databaseNext) + { + databaseNext = database->next; + + if (database->counterMutex) + { + gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, database->counterMutex)); + } + + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, database)); + } + + if (Kernel->db->lastDatabase != gcvNULL) + { + if (Kernel->db->lastDatabase->counterMutex) + { + gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Kernel->db->lastDatabase->counterMutex)); + } + + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, Kernel->db->lastDatabase)); + } + + /* Free all database records. */ + for (record = Kernel->db->freeRecord; record != gcvNULL; record = recordNext) + { + recordNext = record->next; + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, record)); + } + + if (Kernel->db->dbMutex) + { + /* Destroy the database mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Kernel->db->dbMutex)); + } + + if (Kernel->db->nameDatabase) + { + /* Destroy video memory name database. */ + gcmkVERIFY_OK(gckKERNEL_DestroyIntegerDatabase(Kernel, Kernel->db->nameDatabase)); + } + + if (Kernel->db->nameDatabaseMutex) + { + /* Destroy video memory name database mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Kernel->db->nameDatabaseMutex)); + } + + if (Kernel->db->pointerDatabase) + { + /* Destroy id-pointer database. */ + gcmkVERIFY_OK(gckKERNEL_DestroyIntegerDatabase(Kernel, Kernel->db->pointerDatabase)); + } + + if (Kernel->db->pointerDatabaseMutex) + { + /* Destroy id-pointer database mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Kernel->db->pointerDatabaseMutex)); + } + + if (Kernel->db) + { + /* Destroy the database. */ + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, Kernel->db)); + } + + /* Notify stuck timer to quit. */ + Kernel->monitorTimerStop = gcvTRUE; + } + +#if gcdENABLE_VG + if (Kernel->vg) + { + gcmkVERIFY_OK(gckVGKERNEL_Destroy(Kernel->vg)); + } + else +#endif + { + if (Kernel->command) + { + /* Destroy the gckCOMMNAND object. */ + gcmkVERIFY_OK(gckCOMMAND_Destroy(Kernel->command)); + } + + if (Kernel->asyncCommand) + { + gcmkVERIFY_OK(gckASYNC_COMMAND_Destroy(Kernel->asyncCommand)); + } + + if (Kernel->asyncEvent) + { + gcmkVERIFY_OK(gckEVENT_Destroy(Kernel->asyncEvent)); + } + + if (Kernel->eventObj) + { + /* Destroy the gckEVENT object. */ + gcmkVERIFY_OK(gckEVENT_Destroy(Kernel->eventObj)); + } + + gcmkVERIFY_OK(gckHARDWARE_DestroyFunctions(Kernel->hardware)); + + if (Kernel->mmu) + { +#if gcdSHARED_PAGETABLE + /* Destroy the gckMMU object. */ + gcmkVERIFY_OK(gckMMU_Destroy(Kernel->mmu)); +#else + if (Kernel->mmu->hardware == Kernel->hardware) + { + /* Destroy the gckMMU object. */ + gcmkVERIFY_OK(gckMMU_Destroy(Kernel->mmu)); + } +#endif + } + + if (Kernel->hardware) + { + /* Destroy the gckHARDWARE object. */ + gcmkVERIFY_OK(gckHARDWARE_Destroy(Kernel->hardware)); + } + } + + if (Kernel->atomClients) + { + /* Detsroy the client atom. */ + gcmkVERIFY_OK(gckOS_AtomDestroy(Kernel->os, Kernel->atomClients)); + } + + if (Kernel->virtualBufferLock) + { + gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Kernel->virtualBufferLock)); + } + +#if gcdDVFS + if (Kernel->dvfs) + { + gcmkVERIFY_OK(gckDVFS_Stop(Kernel->dvfs)); + gcmkVERIFY_OK(gckDVFS_Destroy(Kernel->dvfs)); + } +#endif + +#if gcdANDROID_NATIVE_FENCE_SYNC + if (Kernel->timeline) + { + gcmkVERIFY_OK(gckOS_DestroySyncTimeline(Kernel->os, Kernel->timeline)); + } +#endif + +#if gcdSECURITY + if (Kernel->securityChannel) + { + gcmkVERIFY_OK(gckKERNEL_SecurityClose(Kernel->securityChannel)); + } +#endif + + if (Kernel->monitorTimer) + { + gcmkVERIFY_OK(gckOS_StopTimer(Kernel->os, Kernel->monitorTimer)); + gcmkVERIFY_OK(gckOS_DestroyTimer(Kernel->os, Kernel->monitorTimer)); + } + + /* Mark the gckKERNEL object as unknown. */ + Kernel->object.type = gcvOBJ_UNKNOWN; + + /* Free the gckKERNEL object. */ + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, Kernel)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** _AllocateMemory +** +** Private function to walk all required memory pools to allocate the requested +** amount of video memory. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gcsHAL_INTERFACE * Interface +** Pointer to a gcsHAL_INTERFACE structure that defines the command to +** be dispatched. +** +** OUTPUT: +** +** gcsHAL_INTERFACE * Interface +** Pointer to a gcsHAL_INTERFACE structure that receives any data to be +** returned. +*/ +gceSTATUS +gckKERNEL_AllocateLinearMemory( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN OUT gcePOOL * Pool, + IN gctSIZE_T Bytes, + IN gctUINT32 Alignment, + IN gceSURF_TYPE Type, + IN gctUINT32 Flag, + OUT gctUINT32 * Node + ) +{ + gcePOOL pool; + gceSTATUS status; + gckVIDMEM videoMemory; + gctINT loopCount; + gcuVIDMEM_NODE_PTR node = gcvNULL; + gctBOOL tileStatusInVirtual; + gctBOOL contiguous = gcvFALSE; + gctBOOL cacheable = gcvFALSE; + gctBOOL secure = gcvFALSE; + gctBOOL fastPools = gcvFALSE; + gctBOOL hasFastPools = gcvFALSE; + gctSIZE_T bytes = Bytes; + gctUINT32 handle = 0; + gceDATABASE_TYPE type; + + gcmkHEADER_ARG("Kernel=0x%x *Pool=%d Bytes=%lu Alignment=%lu Type=%d", + Kernel, *Pool, Bytes, Alignment, Type); + + gcmkVERIFY_ARGUMENT(Pool != gcvNULL); + gcmkVERIFY_ARGUMENT(Bytes != 0); + + /* Get basic type. */ + Type &= 0xFF; + + /* Check flags. */ + contiguous = Flag & gcvALLOC_FLAG_CONTIGUOUS; + cacheable = Flag & gcvALLOC_FLAG_CACHEABLE; + secure = Flag & gcvALLOC_FLAG_SECURITY; + if (Flag & gcvALLOC_FLAG_FAST_POOLS) + { + fastPools = gcvTRUE; + Flag &= ~gcvALLOC_FLAG_FAST_POOLS; + } + +#if gcdALLOC_ON_FAULT + if (Type == gcvSURF_RENDER_TARGET) + { + Flag |= gcvALLOC_FLAG_ALLOC_ON_FAULT; + } +#endif + + if (Flag & gcvALLOC_FLAG_ALLOC_ON_FAULT) + { + *Pool = gcvPOOL_VIRTUAL; + } + + if (Flag & gcvALLOC_FLAG_DMABUF_EXPORTABLE) + { + gctSIZE_T pageSize = 0; + gckOS_GetPageSize(Kernel->os, &pageSize); + + /* Usually, the exported dmabuf might be later imported to DRM, + ** while DRM requires input size to be page aligned. + */ + Bytes = gcmALIGN(Bytes, pageSize); + } + +AllocateMemory: + + /* Get initial pool. */ + switch (pool = *Pool) + { + case gcvPOOL_DEFAULT: + case gcvPOOL_LOCAL: + pool = gcvPOOL_LOCAL_INTERNAL; + loopCount = (gctINT) gcvPOOL_NUMBER_OF_POOLS; + break; + + case gcvPOOL_UNIFIED: + pool = gcvPOOL_SYSTEM; + loopCount = (gctINT) gcvPOOL_NUMBER_OF_POOLS; + break; + + case gcvPOOL_CONTIGUOUS: + loopCount = (gctINT) gcvPOOL_NUMBER_OF_POOLS; + break; + + default: + loopCount = 1; + break; + } + + while (loopCount-- > 0) + { + if (pool == gcvPOOL_VIRTUAL) + { + /* Create a gcuVIDMEM_NODE for virtual memory. */ + gcmkONERROR( + gckVIDMEM_ConstructVirtual(Kernel, Flag | gcvALLOC_FLAG_NON_CONTIGUOUS, Bytes, &node)); + + bytes = node->Virtual.bytes; + node->Virtual.type = Type; + + /* Success. */ + break; + } + + else + if (pool == gcvPOOL_CONTIGUOUS) + { +#if gcdCONTIGUOUS_SIZE_LIMIT + if (Bytes > gcdCONTIGUOUS_SIZE_LIMIT && contiguous == gcvFALSE) + { + status = gcvSTATUS_OUT_OF_MEMORY; + } + else +#endif + { + /* Create a gcuVIDMEM_NODE from contiguous memory. */ + status = gckVIDMEM_ConstructVirtual( + Kernel, + Flag | gcvALLOC_FLAG_CONTIGUOUS, + Bytes, + &node); + } + + if (gcmIS_SUCCESS(status)) + { + bytes = node->Virtual.bytes; + node->Virtual.type = Type; + + /* Memory allocated. */ + break; + } + } + + else + /* gcvPOOL_SYSTEM can't be cacheable. */ + if (cacheable == gcvFALSE && secure == gcvFALSE) + { + /* Get pointer to gckVIDMEM object for pool. */ + status = gckKERNEL_GetVideoMemoryPool(Kernel, pool, &videoMemory); + + if (gcmIS_SUCCESS(status)) + { + /* Allocate memory. */ +#if defined(gcdLINEAR_SIZE_LIMIT) + /* 512 KB */ + if (Bytes > gcdLINEAR_SIZE_LIMIT) + { + status = gcvSTATUS_OUT_OF_MEMORY; + } + else +#endif + { + hasFastPools = gcvTRUE; + status = gckVIDMEM_AllocateLinear(Kernel, + videoMemory, + Bytes, + Alignment, + Type, + (*Pool == gcvPOOL_SYSTEM), + &node); + } + + if (gcmIS_SUCCESS(status)) + { + /* Memory allocated. */ + node->VidMem.pool = pool; + bytes = node->VidMem.bytes; + break; + } + } + } + + if (pool == gcvPOOL_LOCAL_INTERNAL) + { + /* Advance to external memory. */ + pool = gcvPOOL_LOCAL_EXTERNAL; + } + + else + if (pool == gcvPOOL_LOCAL_EXTERNAL) + { + /* Advance to contiguous system memory. */ + pool = gcvPOOL_SYSTEM; + } + + else + if (pool == gcvPOOL_SYSTEM) + { + /* Do not go ahead to try relative slow pools */ + if (fastPools && hasFastPools) + { + status = gcvSTATUS_OUT_OF_MEMORY; + break; + } + + /* Advance to contiguous memory. */ + pool = gcvPOOL_CONTIGUOUS; + } + + else + if (pool == gcvPOOL_CONTIGUOUS) + { +#if gcdENABLE_VG + if (Kernel->vg) + { + tileStatusInVirtual = gcvFALSE; + } + else +#endif + { + tileStatusInVirtual = + gckHARDWARE_IsFeatureAvailable(Kernel->hardware, + gcvFEATURE_MC20); + } + + if (Type == gcvSURF_TILE_STATUS && tileStatusInVirtual != gcvTRUE) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + if (contiguous) + { + break; + } + + /* Advance to virtual memory. */ + pool = gcvPOOL_VIRTUAL; + } + + else + { + /* Out of pools. */ + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + } + + if (node == gcvNULL) + { + if (contiguous) + { + /* Broadcast OOM message. */ + status = gckOS_Broadcast(Kernel->os, Kernel->hardware, gcvBROADCAST_OUT_OF_MEMORY); + + if (gcmIS_SUCCESS(status)) + { + /* Get some memory. */ + gckOS_Delay(gcvNULL, 1); + goto AllocateMemory; + } + } + + /* Nothing allocated. */ + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + /* Allocate handle for this video memory. */ + gcmkONERROR( + gckVIDMEM_NODE_Allocate(Kernel, node, Type, pool, &handle)); + + /* Return node and pool used for allocation. */ + *Node = handle; + *Pool = pool; + + /* Encode surface type and pool to database type. */ + type = gcvDB_VIDEO_MEMORY + | (Type << gcdDB_VIDEO_MEMORY_TYPE_SHIFT) + | (pool << gcdDB_VIDEO_MEMORY_POOL_SHIFT); + + /* Record in process db. */ + gcmkONERROR( + gckKERNEL_AddProcessDB(Kernel, + ProcessID, + type, + gcmINT2PTR(handle), + gcvNULL, + bytes)); + + + /* Return status. */ + gcmkFOOTER_ARG("*Pool=%d *Node=0x%x", *Pool, *Node); + return gcvSTATUS_OK; + +OnError: + if (handle) + { + /* Destroy handle allocated. */ + gcmkVERIFY_OK(gckVIDMEM_HANDLE_Dereference(Kernel, ProcessID, handle)); + } + + if (node) + { + /* Free video memory allocated. */ + gcmkVERIFY_OK(gckVIDMEM_Free(Kernel, node)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_ReleaseVideoMemory +** +** Release handle of a video memory. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctUINT32 ProcessID +** ProcessID of current process. +** +** gctUINT32 Handle +** Handle of video memory. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_ReleaseVideoMemory( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gctUINT32 Handle + ) +{ + gceSTATUS status; + gckVIDMEM_NODE nodeObject; + gceDATABASE_TYPE type; + + gcmkHEADER_ARG("Kernel=0x%08X ProcessID=%d Handle=%d", + Kernel, ProcessID, Handle); + + gcmkONERROR( + gckVIDMEM_HANDLE_Lookup(Kernel, ProcessID, Handle, &nodeObject)); + + type = gcvDB_VIDEO_MEMORY + | (nodeObject->type << gcdDB_VIDEO_MEMORY_TYPE_SHIFT) + | (nodeObject->pool << gcdDB_VIDEO_MEMORY_POOL_SHIFT); + + gcmkONERROR( + gckKERNEL_RemoveProcessDB(Kernel, + ProcessID, + type, + gcmINT2PTR(Handle))); + + gckVIDMEM_HANDLE_Dereference(Kernel, ProcessID, Handle); + + gckVIDMEM_NODE_Dereference(Kernel, nodeObject); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_LockVideoMemory +** +** Lock a video memory node. It will generate a cpu virtual address used +** by software and a GPU address used by GPU. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gceCORE Core +** GPU to which video memory is locked. +** +** gcsHAL_INTERFACE * Interface +** Pointer to a gcsHAL_INTERFACE structure that defines the command to +** be dispatched. +** +** OUTPUT: +** +** gcsHAL_INTERFACE * Interface +** Pointer to a gcsHAL_INTERFACE structure that receives any data to be +** returned. +*/ +gceSTATUS +gckKERNEL_LockVideoMemory( + IN gckKERNEL Kernel, + IN gceCORE Core, + IN gctUINT32 ProcessID, + IN gctBOOL FromUser, + IN OUT gcsHAL_INTERFACE * Interface + ) +{ + gceSTATUS status; + gckVIDMEM_NODE nodeObject = gcvNULL; + gcuVIDMEM_NODE_PTR node = gcvNULL; + gctBOOL locked = gcvFALSE; + gctBOOL asynchronous = gcvFALSE; +#ifndef __QNXNTO__ + gctPOINTER pointer = gcvNULL; +#endif + + gcmkHEADER_ARG("Kernel=0x%08X ProcessID=%d", + Kernel, ProcessID); + + gcmkONERROR( + gckVIDMEM_HANDLE_LookupAndReference(Kernel, + Interface->u.LockVideoMemory.node, + &nodeObject)); + + node = nodeObject->node; + + Interface->u.LockVideoMemory.gid = 0; + + /* Lock video memory. */ + gcmkONERROR( + gckVIDMEM_Lock(Kernel, + nodeObject, + Interface->u.LockVideoMemory.cacheable, + &Interface->u.LockVideoMemory.address, + &Interface->u.LockVideoMemory.gid, + &Interface->u.LockVideoMemory.physicalAddress)); + + locked = gcvTRUE; + + if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM) + { + /* Map video memory address into user space. */ +#ifdef __QNXNTO__ + if (node->VidMem.logical == gcvNULL) + { + gcmkONERROR( + gckKERNEL_MapVideoMemory(Kernel, + FromUser, + Interface->u.LockVideoMemory.address, + ProcessID, + node->VidMem.bytes, + &node->VidMem.logical)); + } + gcmkASSERT(node->VidMem.logical != gcvNULL); + + Interface->u.LockVideoMemory.memory = gcmPTR_TO_UINT64(node->VidMem.logical); +#else + gcmkONERROR( + gckKERNEL_MapVideoMemoryEx(Kernel, + Core, + FromUser, + Interface->u.LockVideoMemory.address, + node->VidMem.pool, + &pointer)); + + Interface->u.LockVideoMemory.memory = gcmPTR_TO_UINT64(pointer); +#endif + } + else + { + Interface->u.LockVideoMemory.memory = gcmPTR_TO_UINT64(node->Virtual.logical); + + /* Success. */ + status = gcvSTATUS_OK; + } + +#if gcdPROCESS_ADDRESS_SPACE + gcmkONERROR(gckVIDMEM_Node_Lock( + Kernel, + nodeObject, + &Interface->u.LockVideoMemory.address + )); +#endif + + +#if gcdSECURE_USER + /* Return logical address as physical address. */ + Interface->u.LockVideoMemory.address = + (gctUINT32)(Interface->u.LockVideoMemory.memory); +#endif + gcmkONERROR( + gckKERNEL_AddProcessDB(Kernel, + ProcessID, gcvDB_VIDEO_MEMORY_LOCKED, + gcmINT2PTR(Interface->u.LockVideoMemory.node), + gcvNULL, + 0)); + + gckVIDMEM_HANDLE_Reference( + Kernel, ProcessID, (gctUINT32)Interface->u.LockVideoMemory.node); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (locked) + { + /* Roll back the lock. */ + gcmkVERIFY_OK(gckVIDMEM_Unlock(Kernel, + nodeObject, + gcvSURF_TYPE_UNKNOWN, + &asynchronous)); + + if (gcvTRUE == asynchronous) + { + /* Bottom Half */ + gcmkVERIFY_OK(gckVIDMEM_Unlock(Kernel, + nodeObject, + gcvSURF_TYPE_UNKNOWN, + gcvNULL)); + } + } + + if (nodeObject != gcvNULL) + { + gckVIDMEM_NODE_Dereference(Kernel, nodeObject); + } + + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_UnlockVideoMemory +** +** Unlock a video memory node. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctUINT32 ProcessID +** ProcessID of current process. +** +** gcsHAL_INTERFACE * Interface +** Pointer to a gcsHAL_INTERFACE structure that defines the command to +** be dispatched. +** +** OUTPUT: +** +** gcsHAL_INTERFACE * Interface +** Pointer to a gcsHAL_INTERFACE structure that receives any data to be +** returned. +*/ +gceSTATUS +gckKERNEL_UnlockVideoMemory( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN OUT gcsHAL_INTERFACE * Interface + ) +{ + gceSTATUS status; + gckVIDMEM_NODE nodeObject; + gcuVIDMEM_NODE_PTR node; + gctSIZE_T bytes; + + gcmkHEADER_ARG("Kernel=0x%08X ProcessID=%d", + Kernel, ProcessID); + + Interface->u.UnlockVideoMemory.pool = gcvPOOL_UNKNOWN; + Interface->u.UnlockVideoMemory.bytes = 0; + + gcmkONERROR(gckVIDMEM_HANDLE_Lookup( + Kernel, + ProcessID, + (gctUINT32)Interface->u.UnlockVideoMemory.node, + &nodeObject)); + + node = nodeObject->node; + bytes = (node->VidMem.memory->object.type == gcvOBJ_VIDMEM) + ? node->VidMem.bytes + : node->Virtual.bytes; + + /* Unlock video memory. */ + gcmkONERROR(gckVIDMEM_Unlock( + Kernel, + nodeObject, + Interface->u.UnlockVideoMemory.type, + &Interface->u.UnlockVideoMemory.asynchroneous)); + +#if gcdSECURE_USER + /* Flush the translation cache for virtual surfaces. */ + if (node->VidMem.memory->object.type != gcvOBJ_VIDMEM) + { + gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache(Kernel, + cache, + node->Virtual.logical, + bytes)); + } +#endif + + Interface->u.UnlockVideoMemory.pool = nodeObject->pool; + Interface->u.UnlockVideoMemory.bytes = (gctUINT)bytes; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_BottomHalfUnlockVideoMemory +** +** Unlock video memory from gpu. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctUINT32 ProcessID +** Process ID owning this memory. +** +** gctPOINTER Pointer +** Video memory to be unlock. +*/ +gceSTATUS +gckKERNEL_BottomHalfUnlockVideoMemory( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gctUINT32 Node + ) +{ + gceSTATUS status; + gckVIDMEM_NODE nodeObject = gcvNULL; + + /* Remove record from process db. */ + gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB( + Kernel, + ProcessID, + gcvDB_VIDEO_MEMORY_LOCKED, + gcmINT2PTR(Node))); + + gcmkONERROR(gckVIDMEM_HANDLE_Lookup( + Kernel, + ProcessID, + Node, + &nodeObject)); + + gckVIDMEM_HANDLE_Dereference(Kernel, ProcessID, Node); + + /* Unlock video memory. */ + gcmkONERROR(gckVIDMEM_Unlock( + Kernel, + nodeObject, + gcvSURF_TYPE_UNKNOWN, + gcvNULL)); + + gcmkONERROR(gckVIDMEM_NODE_Dereference( + Kernel, + nodeObject)); + + return gcvSTATUS_OK; + +OnError: + return status; +} + +/******************************************************************************* +** +** gckKERNEL_SetVidMemMetadata +** +** Set/Get metadata to/from gckVIDMEM_NODE object. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctUINT32 ProcessID +** ProcessID of current process. +** +** INOUT: +** +** gcsHAL_INTERFACE * Interface +** Pointer to a interface structure +*/ +#if defined(CONFIG_DMA_SHARED_BUFFER) +#include <linux/dma-buf.h> + +gceSTATUS +gckKERNEL_SetVidMemMetadata( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + INOUT gcsHAL_INTERFACE * Interface + ) +{ + gceSTATUS status = gcvSTATUS_NOT_SUPPORTED; + gckVIDMEM_NODE nodeObj = gcvNULL; + + gcmkHEADER_ARG("Kernel=0x%X ProcessID=%d", Kernel, ProcessID); + + gcmkONERROR(gckVIDMEM_HANDLE_Lookup(Kernel, ProcessID, Interface->u.SetVidMemMetadata.node, &nodeObj)); + + if (Interface->u.SetVidMemMetadata.readback) + { + Interface->u.SetVidMemMetadata.ts_fd = nodeObj->metadata.ts_fd; + Interface->u.SetVidMemMetadata.fc_enabled = nodeObj->metadata.fc_enabled; + Interface->u.SetVidMemMetadata.fc_value = nodeObj->metadata.fc_value; + Interface->u.SetVidMemMetadata.fc_value_upper = nodeObj->metadata.fc_value_upper; + Interface->u.SetVidMemMetadata.compressed = nodeObj->metadata.compressed; + Interface->u.SetVidMemMetadata.compress_format = nodeObj->metadata.compress_format; + } + else + { + nodeObj->metadata.ts_fd = Interface->u.SetVidMemMetadata.ts_fd; + if (nodeObj->metadata.ts_fd > 0) + { + nodeObj->metadata.ts_dma_buf = dma_buf_get(nodeObj->metadata.ts_fd); + if (IS_ERR(nodeObj->metadata.ts_dma_buf)) + { + gcmkONERROR(gcvSTATUS_NOT_FOUND); + } + dma_buf_put(nodeObj->metadata.ts_dma_buf); + } + nodeObj->metadata.fc_enabled = Interface->u.SetVidMemMetadata.fc_enabled; + nodeObj->metadata.fc_value = Interface->u.SetVidMemMetadata.fc_value; + nodeObj->metadata.fc_value_upper = Interface->u.SetVidMemMetadata.fc_value_upper; + nodeObj->metadata.compressed = Interface->u.SetVidMemMetadata.compressed; + nodeObj->metadata.compress_format = Interface->u.SetVidMemMetadata.compress_format; + } + + gcmkFOOTER(); + +OnError: + return status; +} + +#else + +gceSTATUS +gckKERNEL_SetVidMemMetadata( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + INOUT gcsHAL_INTERFACE * Interface + ) +{ + gcmkFATAL("The kernel did NOT support CONFIG_DMA_SHARED_BUFFER"); + return gcvSTATUS_NOT_SUPPORTED; +} +#endif + +/******************************************************************************* +** +** gckKERNEL_QueryVidMemPoolNodes +** +** Loop all databases to query used memory nodes of a specific pool. +** +** INPUT: +** +** Pool The memory pool for query. + +** TotalSize Total size of the used contiguous memory. +** +** MemoryBlocks Used contiguous memory block info. +** +** NumMaxBlock The count of memory blocks array provided by the caller. +** +** NumBlocks The actual number of memory blocks returned. +** +** OUTPUT: +** +** Error status. Should always be gcvSTATUS_SUCCESS since a query should always succeed. +*/ +gceSTATUS +gckKERNEL_QueryVidMemPoolNodes( + gckKERNEL Kernel, + gcePOOL Pool, + gctUINT32 * TotalSize, + gcsContiguousBlock * MemoryBlocks, + gctUINT32 NumMaxBlocks, + gctUINT32 * NumBlocks + ) +{ + gceSTATUS status = gcvSTATUS_OK; + gctINT i; + gctINT bcount; + gctUINT32 num_blocks = 0; + gctSIZE_T total_size = 0; + gckVIDMEM memory; + gcuVIDMEM_NODE_PTR nodes, node; + + do + { + /* Get the heap and nodes. */ + status = gckKERNEL_GetVideoMemoryPool(Kernel, Pool, &memory); + if (status != gcvSTATUS_OK) + break; + + status = gckVIDMEM_QueryNodes(Kernel, Pool, &bcount, &nodes); + if (status != gcvSTATUS_OK) + break; + + /* Iterate all nodes. */ + for (i = 0; i < bcount; i++) + { + node = nodes[i].VidMem.next; + do + { + if (node == gcvNULL) + { + break; + } + + /* Is it in the "free" list? */ + if (node->VidMem.nextFree == gcvNULL) + { + if (num_blocks < NumMaxBlocks) + { + MemoryBlocks[num_blocks].ptr = (gctUINT32)node->VidMem.offset + memory->baseAddress; + MemoryBlocks[num_blocks].size = node->VidMem.bytes; + } + total_size += node->VidMem.bytes; + num_blocks++; + } + + node = node->VidMem.next; + } while (node != &nodes[i]); + } + } + while (gcvFALSE); + + if (TotalSize != gcvNULL) + *TotalSize = (gctUINT32)total_size; + + if (NumBlocks != gcvNULL) + *NumBlocks = num_blocks; + + return status; +} + +gceSTATUS +gckKERNEL_QueryDatabase( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN OUT gcsHAL_INTERFACE * Interface + ) +{ + gceSTATUS status; + gctINT i; + + gceDATABASE_TYPE type[3] = { + gcvDB_VIDEO_MEMORY | (gcvPOOL_SYSTEM << gcdDB_VIDEO_MEMORY_POOL_SHIFT), + gcvDB_VIDEO_MEMORY | (gcvPOOL_CONTIGUOUS << gcdDB_VIDEO_MEMORY_POOL_SHIFT), + gcvDB_VIDEO_MEMORY | (gcvPOOL_VIRTUAL << gcdDB_VIDEO_MEMORY_POOL_SHIFT), + }; + + gcmkHEADER(); + + /* Query video memory. */ + gcmkONERROR( + gckKERNEL_QueryProcessDB(Kernel, + Interface->u.Database.processID, + !Interface->u.Database.validProcessID, + gcvDB_VIDEO_MEMORY, + &Interface->u.Database.vidMem)); + + /* Query non-paged memory. */ + gcmkONERROR( + gckKERNEL_QueryProcessDB(Kernel, + Interface->u.Database.processID, + !Interface->u.Database.validProcessID, + gcvDB_NON_PAGED, + &Interface->u.Database.nonPaged)); + + /* Query contiguous memory. */ + gcmkONERROR( + gckKERNEL_QueryProcessDB(Kernel, + Interface->u.Database.processID, + !Interface->u.Database.validProcessID, + gcvDB_CONTIGUOUS, + &Interface->u.Database.contiguous)); + + /* Query GPU idle time. */ + gcmkONERROR( + gckKERNEL_QueryProcessDB(Kernel, + Interface->u.Database.processID, + !Interface->u.Database.validProcessID, + gcvDB_IDLE, + &Interface->u.Database.gpuIdle)); + for (i = 0; i < 3; i++) + { + /* Query each video memory pool. */ + gcmkONERROR( + gckKERNEL_QueryProcessDB(Kernel, + Interface->u.Database.processID, + !Interface->u.Database.validProcessID, + type[i], + &Interface->u.Database.vidMemPool[i])); + } + +#if gcmIS_DEBUG(gcdDEBUG_TRACE) + gckKERNEL_DumpVidMemUsage(Kernel, Interface->u.Database.processID); +#endif + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_ConfigPowerManagement( + IN gckKERNEL Kernel, + IN OUT gcsHAL_INTERFACE * Interface +) +{ + gceSTATUS status; + gctBOOL enable = Interface->u.ConfigPowerManagement.enable; + + gcmkHEADER(); + + gcmkONERROR(gckHARDWARE_SetPowerManagement(Kernel->hardware, enable)); + + if (enable == gcvFALSE) + { + gcmkONERROR( + gckHARDWARE_SetPowerManagementState(Kernel->hardware, gcvPOWER_ON)); + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_WaitFence( + IN gckKERNEL Kernel, + IN gctUINT32 Handle, + IN gctUINT32 TimeOut + ) +{ + gceSTATUS status; + gckVIDMEM_NODE node; + gctUINT32 processID; + gckCOMMAND command = Kernel->command; + gckASYNC_COMMAND asyncCommand = Kernel->asyncCommand; + gckFENCE fence = gcvNULL; + gctUINT i; + + gckOS_GetProcessID(&processID); + + gcmkONERROR(gckVIDMEM_HANDLE_LookupAndReference(Kernel, Handle, &node)); + + /* Wait for fence of all engines. */ + for (i = 0; i < gcvENGINE_GPU_ENGINE_COUNT; i++) + { + gckFENCE_SYNC sync = &node->sync[i]; + + if (i == gcvENGINE_RENDER) + { + fence = command->fence; + } + else + { + fence = asyncCommand->fence; + } + + if (sync->commitStamp <= *(gctUINT64_PTR)fence->logical) + { + continue; + } + else + { + gckOS_Signal(Kernel->os, sync->signal, gcvFALSE); + + gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, &fence->mutex, gcvINFINITE)); + + /* Add to waiting list. */ + gcsLIST_AddTail(&sync->head, &fence->waitingList); + + gcmkASSERT(sync->inList == gcvFALSE); + + sync->inList = gcvTRUE; + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, &fence->mutex)); + + /* Wait. */ + status = gckOS_WaitSignal(Kernel->os, sync->signal, gcvTRUE, TimeOut); + + gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, &fence->mutex, gcvINFINITE)); + + if (sync->inList) + { + gcsLIST_Del(&sync->head); + sync->inList = gcvFALSE; + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, &fence->mutex)); + } + } + + gckVIDMEM_NODE_Dereference(Kernel, node); + +OnError: + return status; +} + +#ifdef __linux__ + +typedef struct _gcsGRRAPHIC_BUFFER_PARCLE +{ + gcsFDPRIVATE base; + gckKERNEL kernel; + + gckVIDMEM_NODE node[3]; + gctSHBUF shBuf; + gctINT32 signal; +} +gcsGRAPHIC_BUFFER_PARCLE; + +static void +_ReleaseGraphicBuffer( + gckKERNEL Kernel, + gcsGRAPHIC_BUFFER_PARCLE * Parcle + ) +{ + gctUINT i; + + for (i = 0; i < 3; i++) + { + if (Parcle->node[i]) + { + gckVIDMEM_NODE_Dereference(Kernel, Parcle->node[i]); + } + } + + if (Parcle->shBuf) + { + gckKERNEL_DestroyShBuffer(Kernel, Parcle->shBuf); + } + + if (Parcle->signal) + { + gckOS_DestroyUserSignal(Kernel->os, Parcle->signal); + } + + gcmkOS_SAFE_FREE(Kernel->os, Parcle); +} + +static gctINT +_FdReleaseGraphicBuffer( + gcsFDPRIVATE_PTR Private + ) +{ + gcsGRAPHIC_BUFFER_PARCLE * parcle = (gcsGRAPHIC_BUFFER_PARCLE *) Private; + + _ReleaseGraphicBuffer(parcle->kernel, parcle); + return 0; +} + +static gceSTATUS +_GetGraphicBufferFd( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gctUINT32 Node[3], + IN gctUINT64 ShBuf, + IN gctUINT64 Signal, + OUT gctINT32 * Fd + ) +{ + gceSTATUS status; + gctUINT i; + gcsGRAPHIC_BUFFER_PARCLE * parcle = gcvNULL; + + gcmkONERROR(gckOS_Allocate( + Kernel->os, + gcmSIZEOF(gcsGRAPHIC_BUFFER_PARCLE), + (gctPOINTER *)&parcle + )); + + gckOS_ZeroMemory(parcle, sizeof(gcsGRAPHIC_BUFFER_PARCLE)); + + parcle->base.release = _FdReleaseGraphicBuffer; + parcle->kernel = Kernel; + + for (i = 0; i < 3; i++) + { + if (Node[i] != 0) + { + gcmkONERROR( + gckVIDMEM_HANDLE_LookupAndReference(Kernel, Node[i], &parcle->node[i])); + } + } + + if (ShBuf) + { + gctSHBUF shBuf = gcmUINT64_TO_PTR(ShBuf); + + gcmkONERROR(gckKERNEL_MapShBuffer(Kernel, shBuf)); + parcle->shBuf = shBuf; + } + + if (Signal) + { + gctSIGNAL signal = gcmUINT64_TO_PTR(Signal); + + gcmkONERROR( + gckOS_MapSignal(Kernel->os, + signal, + (gctHANDLE)(gctUINTPTR_T)ProcessID, + &signal)); + + parcle->signal= (gctINT32)Signal; + } + + gcmkONERROR(gckOS_GetFd("viv-gr", &parcle->base, Fd)); + + return gcvSTATUS_OK; + +OnError: + if (parcle) + { + _ReleaseGraphicBuffer(Kernel, parcle); + } + return status; +} +#endif + +/******************************************************************************* +** +** gckKERNEL_Dispatch +** +** Dispatch a command received from the user HAL layer. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctBOOL FromUser +** whether the call is from the user space. +** +** gcsHAL_INTERFACE * Interface +** Pointer to a gcsHAL_INTERFACE structure that defines the command to +** be dispatched. +** +** OUTPUT: +** +** gcsHAL_INTERFACE * Interface +** Pointer to a gcsHAL_INTERFACE structure that receives any data to be +** returned. +*/ +gceSTATUS +gckKERNEL_Dispatch( + IN gckKERNEL Kernel, + IN gckDEVICE Device, + IN gctBOOL FromUser, + IN OUT gcsHAL_INTERFACE * Interface + ) +{ + gceSTATUS status = gcvSTATUS_OK; + gctPHYS_ADDR physical = gcvNULL; + gctSIZE_T bytes; + gctPOINTER logical = gcvNULL; +#if (gcdENABLE_3D || gcdENABLE_2D) + gckCONTEXT context = gcvNULL; +#endif + gckKERNEL kernel = Kernel; + gctUINT32 processID; +#if gcdSECURE_USER + gcskSECURE_CACHE_PTR cache; + gctPOINTER logical; +#endif + gctUINT64 paddr = gcvINVALID_ADDRESS; +#if !USE_NEW_LINUX_SIGNAL + gctSIGNAL signal; +#endif + gckVIRTUAL_COMMAND_BUFFER_PTR buffer; + + gctBOOL powerMutexAcquired = gcvFALSE; + gctBOOL commitMutexAcquired = gcvFALSE; + + gcmkHEADER_ARG("Kernel=0x%x FromUser=%d Interface=0x%x", + Kernel, FromUser, Interface); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Interface != gcvNULL); + +#if gcmIS_DEBUG(gcdDEBUG_TRACE) + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_KERNEL, + "Dispatching command %d (%s)", + Interface->command, _DispatchText[Interface->command]); +#endif +#if QNX_SINGLE_THREADED_DEBUGGING + gckOS_AcquireMutex(Kernel->os, Kernel->debugMutex, gcvINFINITE); +#endif + + /* Get the current process ID. */ + gcmkONERROR(gckOS_GetProcessID(&processID)); + +#if gcdSECURE_USER + gcmkONERROR(gckKERNEL_GetProcessDBCache(Kernel, processID, &cache)); +#endif + + /* Dispatch on command. */ + switch (Interface->command) + { + case gcvHAL_GET_BASE_ADDRESS: + /* Get base address. */ + Interface->u.GetBaseAddress.baseAddress = Kernel->hardware->baseAddress; + Interface->u.GetBaseAddress.flatMappingRangeCount = Kernel->mmu->flatMappingRangeCount; + if (Kernel->mmu->flatMappingRangeCount) + { + gckOS_MemCopy(Interface->u.GetBaseAddress.flatMappingRanges, Kernel->mmu->flatMappingRanges, + gcmSIZEOF(gcsFLAT_MAPPING_RANGE) * Kernel->mmu->flatMappingRangeCount); + } + break; + + case gcvHAL_QUERY_VIDEO_MEMORY: + /* Query video memory size. */ + gcmkONERROR(gckKERNEL_QueryVideoMemory(Kernel, Interface)); + break; + + case gcvHAL_QUERY_CHIP_IDENTITY: + /* Query chip identity. */ + gcmkONERROR( + gckHARDWARE_QueryChipIdentity( + Kernel->hardware, + &Interface->u.QueryChipIdentity)); + break; + + case gcvHAL_MAP_MEMORY: + physical = gcmINT2PTR(Interface->u.MapMemory.physical); + + /* Map memory. */ + gcmkONERROR( + gckKERNEL_MapMemory(Kernel, + physical, + (gctSIZE_T) Interface->u.MapMemory.bytes, + &logical)); + + Interface->u.MapMemory.logical = gcmPTR_TO_UINT64(logical); + + gcmkVERIFY_OK( + gckKERNEL_AddProcessDB(Kernel, + processID, gcvDB_MAP_MEMORY, + logical, + physical, + (gctSIZE_T) Interface->u.MapMemory.bytes)); + break; + + case gcvHAL_UNMAP_MEMORY: + physical = gcmINT2PTR(Interface->u.UnmapMemory.physical); + + gcmkVERIFY_OK( + gckKERNEL_RemoveProcessDB(Kernel, + processID, gcvDB_MAP_MEMORY, + gcmUINT64_TO_PTR(Interface->u.UnmapMemory.logical))); + + /* Unmap memory. */ + gcmkONERROR( + gckKERNEL_UnmapMemory(Kernel, + physical, + (gctSIZE_T) Interface->u.UnmapMemory.bytes, + gcmUINT64_TO_PTR(Interface->u.UnmapMemory.logical), + processID)); + break; + + case gcvHAL_ALLOCATE_NON_PAGED_MEMORY: + bytes = (gctSIZE_T) Interface->u.AllocateNonPagedMemory.bytes; + + /* Allocate non-paged memory. */ + gcmkONERROR( + gckOS_AllocateNonPagedMemory( + Kernel->os, + FromUser, + &bytes, + &physical, + &logical)); + + Interface->u.AllocateNonPagedMemory.bytes = bytes; + Interface->u.AllocateNonPagedMemory.logical = gcmPTR_TO_UINT64(logical); + Interface->u.AllocateNonPagedMemory.physical = gcmPTR_TO_NAME(physical); + + gcmkVERIFY_OK( + gckKERNEL_AddProcessDB(Kernel, + processID, gcvDB_NON_PAGED, + logical, + gcmINT2PTR(Interface->u.AllocateNonPagedMemory.physical), + bytes)); + break; + + case gcvHAL_ALLOCATE_VIRTUAL_COMMAND_BUFFER: + bytes = (gctSIZE_T) Interface->u.AllocateVirtualCommandBuffer.bytes; + + gcmkONERROR( + gckKERNEL_AllocateVirtualCommandBuffer( + Kernel, + FromUser, + &bytes, + &physical, + &logical)); + + Interface->u.AllocateVirtualCommandBuffer.bytes = bytes; + Interface->u.AllocateVirtualCommandBuffer.logical = gcmPTR_TO_UINT64(logical); + Interface->u.AllocateVirtualCommandBuffer.physical = gcmPTR_TO_NAME(physical); + + gcmkVERIFY_OK( + gckKERNEL_AddProcessDB(Kernel, + processID, gcvDB_COMMAND_BUFFER, + logical, + gcmINT2PTR(Interface->u.AllocateVirtualCommandBuffer.physical), + bytes)); + break; + + case gcvHAL_FREE_NON_PAGED_MEMORY: + physical = gcmNAME_TO_PTR(Interface->u.FreeNonPagedMemory.physical); + + gcmkVERIFY_OK( + gckKERNEL_RemoveProcessDB(Kernel, + processID, gcvDB_NON_PAGED, + gcmUINT64_TO_PTR(Interface->u.FreeNonPagedMemory.logical))); + + /* Unmap user logical out of physical memory first. */ + gcmkONERROR(gckOS_UnmapUserLogical(Kernel->os, + physical, + (gctSIZE_T) Interface->u.FreeNonPagedMemory.bytes, + gcmUINT64_TO_PTR(Interface->u.FreeNonPagedMemory.logical))); + + /* Free non-paged memory. */ + gcmkONERROR( + gckOS_FreeNonPagedMemory(Kernel->os, + (gctSIZE_T) Interface->u.FreeNonPagedMemory.bytes, + physical, + gcmUINT64_TO_PTR(Interface->u.FreeNonPagedMemory.logical))); + +#if gcdSECURE_USER + gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache( + Kernel, + cache, + gcmUINT64_TO_PTR(Interface->u.FreeNonPagedMemory.logical), + (gctSIZE_T) Interface->u.FreeNonPagedMemory.bytes)); +#endif + + gcmRELEASE_NAME(Interface->u.FreeNonPagedMemory.physical); + break; + + case gcvHAL_ALLOCATE_CONTIGUOUS_MEMORY: + bytes = (gctSIZE_T) Interface->u.AllocateContiguousMemory.bytes; + + /* Allocate contiguous memory. */ + gcmkONERROR(gckOS_AllocateContiguous( + Kernel->os, + FromUser, + &bytes, + &physical, + &logical)); + + Interface->u.AllocateContiguousMemory.bytes = bytes; + Interface->u.AllocateContiguousMemory.logical = gcmPTR_TO_UINT64(logical); + Interface->u.AllocateContiguousMemory.physical = gcmPTR_TO_NAME(physical); + + gcmkONERROR(gckHARDWARE_ConvertLogical( + Kernel->hardware, + logical, + gcvTRUE, + &Interface->u.AllocateContiguousMemory.address)); + + gcmkVERIFY_OK(gckKERNEL_AddProcessDB( + Kernel, + processID, gcvDB_CONTIGUOUS, + logical, + gcmINT2PTR(Interface->u.AllocateContiguousMemory.physical), + bytes)); + break; + + case gcvHAL_FREE_CONTIGUOUS_MEMORY: + physical = gcmNAME_TO_PTR(Interface->u.FreeContiguousMemory.physical); + + gcmkVERIFY_OK( + gckKERNEL_RemoveProcessDB(Kernel, + processID, gcvDB_CONTIGUOUS, + gcmUINT64_TO_PTR(Interface->u.FreeNonPagedMemory.logical))); + + /* Unmap user logical out of physical memory first. */ + gcmkONERROR(gckOS_UnmapUserLogical(Kernel->os, + physical, + (gctSIZE_T) Interface->u.FreeContiguousMemory.bytes, + gcmUINT64_TO_PTR(Interface->u.FreeContiguousMemory.logical))); + + /* Free contiguous memory. */ + gcmkONERROR( + gckOS_FreeContiguous(Kernel->os, + physical, + gcmUINT64_TO_PTR(Interface->u.FreeContiguousMemory.logical), + (gctSIZE_T) Interface->u.FreeContiguousMemory.bytes)); + +#if gcdSECURE_USER + gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache( + Kernel, + cache, + gcmUINT64_TO_PTR(Interface->u.FreeContiguousMemory.logical), + (gctSIZE_T) Interface->u.FreeContiguousMemory.bytes)); +#endif + + gcmRELEASE_NAME(Interface->u.FreeContiguousMemory.physical); + break; + + case gcvHAL_ALLOCATE_VIDEO_MEMORY: + + gcmkONERROR(gcvSTATUS_NOT_SUPPORTED); + + break; + + case gcvHAL_ALLOCATE_LINEAR_VIDEO_MEMORY: + /* Allocate memory. */ + gcmkONERROR( + gckKERNEL_AllocateLinearMemory(Kernel, processID, + &Interface->u.AllocateLinearVideoMemory.pool, + Interface->u.AllocateLinearVideoMemory.bytes, + Interface->u.AllocateLinearVideoMemory.alignment, + Interface->u.AllocateLinearVideoMemory.type, + Interface->u.AllocateLinearVideoMemory.flag, + &Interface->u.AllocateLinearVideoMemory.node)); + break; + + case gcvHAL_RELEASE_VIDEO_MEMORY: + /* Release video memory. */ + gcmkONERROR(gckKERNEL_ReleaseVideoMemory( + Kernel, processID, + (gctUINT32)Interface->u.ReleaseVideoMemory.node + )); + break; + + case gcvHAL_LOCK_VIDEO_MEMORY: + /* Lock video memory. */ + gcmkONERROR(gckKERNEL_LockVideoMemory(Kernel, Kernel->core, processID, FromUser, Interface)); + break; + + case gcvHAL_UNLOCK_VIDEO_MEMORY: + /* Unlock video memory. */ + gcmkONERROR(gckKERNEL_UnlockVideoMemory(Kernel, processID, Interface)); + break; + + case gcvHAL_BOTTOM_HALF_UNLOCK_VIDEO_MEMORY: + gcmkERR_BREAK(gckKERNEL_BottomHalfUnlockVideoMemory(Kernel, processID, + Interface->u.BottomHalfUnlockVideoMemory.node)); + break; + + case gcvHAL_EVENT_COMMIT: + gcmkONERROR(gckOS_AcquireMutex(Kernel->os, + Kernel->device->commitMutex, + gcvINFINITE + )); + + commitMutexAcquired = gcvTRUE; + /* Commit an event queue. */ + if (Interface->engine == gcvENGINE_BLT) + { + if (!gckHARDWARE_IsFeatureAvailable(Kernel->hardware, gcvFEATURE_ASYNC_BLIT)) + { + gcmkONERROR(gcvSTATUS_NOT_SUPPORTED); + } + + gcmkONERROR(gckEVENT_Commit( + Kernel->asyncEvent, gcmUINT64_TO_PTR(Interface->u.Event.queue), gcvFALSE)); + } + else + { + gcmkONERROR(gckEVENT_Commit( + Kernel->eventObj, gcmUINT64_TO_PTR(Interface->u.Event.queue), gcvFALSE)); + } + + gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->device->commitMutex)); + commitMutexAcquired = gcvFALSE; + break; + + case gcvHAL_COMMIT: + gcmkONERROR(gckOS_AcquireMutex(Kernel->os, + Kernel->device->commitMutex, + gcvINFINITE + )); + commitMutexAcquired = gcvTRUE; + + /* Commit a command and context buffer. */ + if (Interface->engine == gcvENGINE_BLT) + { + gctUINT64 *commandBuffers = gcmUINT64_TO_PTR(Interface->u.Commit.commandBuffer); + + if (!gckHARDWARE_IsFeatureAvailable(Kernel->hardware, gcvFEATURE_ASYNC_BLIT)) + { + gcmkONERROR(gcvSTATUS_NOT_SUPPORTED); + } + + gcmkONERROR(gckASYNC_COMMAND_Commit( + Kernel->asyncCommand, + gcmUINT64_TO_PTR(commandBuffers[0]), + gcmUINT64_TO_PTR(Interface->u.Commit.queue) + )); + + gcmkONERROR(gckEVENT_Commit( + Kernel->asyncEvent, + gcmUINT64_TO_PTR(Interface->u.Commit.queue), + gcvFALSE + )); + } + else + { + gctUINT32 i; + if (Interface->u.Commit.count > 1 && Interface->engine == gcvENGINE_RENDER) + { + gctUINT32 i; + + for (i = 0; i < Interface->u.Commit.count; i++) + { + gceHARDWARE_TYPE type = Interface->hardwareType; + gckKERNEL kernel = Device->map[type].kernels[i]; + + gcmkONERROR(gckOS_Broadcast(kernel->os, + kernel->hardware, + gcvBROADCAST_GPU_COMMIT)); + } + } + + status = gckCOMMAND_Commit(Kernel->command, + Interface->u.Commit.contexts[0] ? + gcmNAME_TO_PTR(Interface->u.Commit.contexts[0]) : gcvNULL, + gcmUINT64_TO_PTR(Interface->u.Commit.commandBuffers[0]), + gcmUINT64_TO_PTR(Interface->u.Commit.deltas[0]), + processID, + Interface->u.Commit.shared, + Interface->u.Commit.index, + &Interface->u.Commit.commitStamp, + &Interface->u.Commit.contextSwitched + ); + + if (status != gcvSTATUS_INTERRUPTED) + { + gcmkONERROR(status); + } + + /* Force an event if powerManagement is on. */ + status = gckEVENT_Commit(Kernel->eventObj, + gcmUINT64_TO_PTR(Interface->u.Commit.queue), + Kernel->hardware->options.powerManagement); + + if (status != gcvSTATUS_INTERRUPTED) + { + gcmkONERROR(status); + } + + if (Interface->u.Commit.count > 1 && Interface->engine == gcvENGINE_RENDER) + { + + for (i = 1; i < Interface->u.Commit.count; i++) + { + gceHARDWARE_TYPE type = Interface->hardwareType; + gckKERNEL kernel = Device->map[type].kernels[i]; + + status = gckCOMMAND_Commit(kernel->command, + Interface->u.Commit.contexts[i] ? + gcmNAME_TO_PTR(Interface->u.Commit.contexts[i]) : gcvNULL, + Interface->u.Commit.commandBuffers[i] ? + gcmUINT64_TO_PTR(Interface->u.Commit.commandBuffers[i]) : gcmUINT64_TO_PTR(Interface->u.Commit.commandBuffers[0]), + gcmUINT64_TO_PTR(Interface->u.Commit.deltas[i]), + processID, + Interface->u.Commit.shared, + Interface->u.Commit.commandBuffers[i] ? + Interface->u.Commit.index : i, + &Interface->u.Commit.commitStamp, + &Interface->u.Commit.contextSwitched + ); + + if (status != gcvSTATUS_INTERRUPTED) + { + gcmkONERROR(status); + } + + /* Force an event if powerManagement is on. */ + status = gckEVENT_Commit(kernel->eventObj, + gcvNULL, + kernel->hardware->options.powerManagement); + + if (status != gcvSTATUS_INTERRUPTED) + { + gcmkONERROR(status); + } + } + } + + for (i = 0; i < Interface->u.Commit.count; i++) { + gceHARDWARE_TYPE type = Interface->hardwareType; + gckKERNEL kernel = Device->map[type].kernels[i]; + + if ((kernel->hardware->options.gpuProfiler == gcvTRUE) && + (kernel->profileEnable == gcvTRUE)) { + gcmkONERROR(gckCOMMAND_Stall(kernel->command, gcvTRUE)); + + if (kernel->command->currContext) { + gcmkONERROR(gckHARDWARE_UpdateContextProfile( + kernel->hardware, + kernel->command->currContext)); + } + } + } + + } + gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->device->commitMutex)); + commitMutexAcquired = gcvFALSE; + + break; + + case gcvHAL_STALL: + /* Stall the command queue. */ + gcmkONERROR(gckCOMMAND_Stall(Kernel->command, gcvFALSE)); + + break; + + case gcvHAL_MAP_USER_MEMORY: + + gcmkONERROR(gcvSTATUS_NOT_SUPPORTED); + + break; + + case gcvHAL_UNMAP_USER_MEMORY: + + gcmkONERROR(gcvSTATUS_NOT_SUPPORTED); + + break; + +#if !USE_NEW_LINUX_SIGNAL + case gcvHAL_USER_SIGNAL: + /* Dispatch depends on the user signal subcommands. */ + switch(Interface->u.UserSignal.command) + { + case gcvUSER_SIGNAL_CREATE: + /* Create a signal used in the user space. */ + gcmkONERROR( + gckOS_CreateUserSignal(Kernel->os, + Interface->u.UserSignal.manualReset, + &Interface->u.UserSignal.id)); + + gcmkVERIFY_OK( + gckKERNEL_AddProcessDB(Kernel, + processID, gcvDB_SIGNAL, + gcmINT2PTR(Interface->u.UserSignal.id), + gcvNULL, + 0)); + break; + + case gcvUSER_SIGNAL_DESTROY: + gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB( + Kernel, + processID, gcvDB_SIGNAL, + gcmINT2PTR(Interface->u.UserSignal.id))); + + /* Destroy the signal. */ + gcmkONERROR( + gckOS_DestroyUserSignal(Kernel->os, + Interface->u.UserSignal.id)); + break; + + case gcvUSER_SIGNAL_SIGNAL: + /* Signal the signal. */ + gcmkONERROR( + gckOS_SignalUserSignal(Kernel->os, + Interface->u.UserSignal.id, + Interface->u.UserSignal.state)); + break; + + case gcvUSER_SIGNAL_WAIT: + /* Wait on the signal. */ + status = gckOS_WaitUserSignal(Kernel->os, + Interface->u.UserSignal.id, + Interface->u.UserSignal.wait); + break; + + case gcvUSER_SIGNAL_MAP: + gcmkONERROR( + gckOS_MapSignal(Kernel->os, + (gctSIGNAL)(gctUINTPTR_T)Interface->u.UserSignal.id, + (gctHANDLE)(gctUINTPTR_T)processID, + &signal)); + + gcmkVERIFY_OK( + gckKERNEL_AddProcessDB(Kernel, + processID, gcvDB_SIGNAL, + gcmINT2PTR(Interface->u.UserSignal.id), + gcvNULL, + 0)); + break; + + case gcvUSER_SIGNAL_UNMAP: + gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB( + Kernel, + processID, gcvDB_SIGNAL, + gcmINT2PTR(Interface->u.UserSignal.id))); + + /* Destroy the signal. */ + gcmkONERROR( + gckOS_DestroyUserSignal(Kernel->os, + Interface->u.UserSignal.id)); + break; + + default: + /* Invalid user signal command. */ + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + break; +#endif + + case gcvHAL_SET_POWER_MANAGEMENT_STATE: + /* Set the power management state. */ + gcmkONERROR( + gckHARDWARE_SetPowerManagementState( + Kernel->hardware, + Interface->u.SetPowerManagement.state)); + break; + + case gcvHAL_QUERY_POWER_MANAGEMENT_STATE: + /* Chip is not idle. */ + Interface->u.QueryPowerManagement.isIdle = gcvFALSE; + + /* Query the power management state. */ + gcmkONERROR(gckHARDWARE_QueryPowerManagementState( + Kernel->hardware, + &Interface->u.QueryPowerManagement.state)); + + /* Query the idle state. */ + gcmkONERROR( + gckHARDWARE_QueryIdle(Kernel->hardware, + &Interface->u.QueryPowerManagement.isIdle)); + break; + + case gcvHAL_READ_REGISTER: +#if gcdREGISTER_ACCESS_FROM_USER + { + gceCHIPPOWERSTATE power; + + gcmkONERROR(gckOS_AcquireMutex(Kernel->os, Kernel->hardware->powerMutex, gcvINFINITE)); + powerMutexAcquired = gcvTRUE; + gcmkONERROR(gckHARDWARE_QueryPowerManagementState(Kernel->hardware, + &power)); + if (power == gcvPOWER_ON) + { + /* Read a register. */ + gcmkONERROR(gckOS_ReadRegisterEx( + Kernel->os, + Kernel->core, + Interface->u.ReadRegisterData.address, + &Interface->u.ReadRegisterData.data)); + } + else + { + /* Chip is in power-state. */ + Interface->u.ReadRegisterData.data = 0; + status = gcvSTATUS_CHIP_NOT_READY; + } + gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->hardware->powerMutex)); + powerMutexAcquired = gcvFALSE; + } +#else + /* No access from user land to read registers. */ + Interface->u.ReadRegisterData.data = 0; + status = gcvSTATUS_NOT_SUPPORTED; +#endif + break; + + case gcvHAL_WRITE_REGISTER: +#if gcdREGISTER_ACCESS_FROM_USER + { + gceCHIPPOWERSTATE power; + + gcmkONERROR(gckOS_AcquireMutex(Kernel->os, Kernel->hardware->powerMutex, gcvINFINITE)); + powerMutexAcquired = gcvTRUE; + gcmkONERROR(gckHARDWARE_QueryPowerManagementState(Kernel->hardware, + &power)); + if (power == gcvPOWER_ON) + { + /* Write a register. */ + gcmkONERROR( + gckOS_WriteRegisterEx(Kernel->os, + Kernel->core, + Interface->u.WriteRegisterData.address, + Interface->u.WriteRegisterData.data)); + } + else + { + /* Chip is in power-state. */ + Interface->u.WriteRegisterData.data = 0; + status = gcvSTATUS_CHIP_NOT_READY; + } + gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->hardware->powerMutex)); + powerMutexAcquired = gcvFALSE; + } +#else + /* No access from user land to write registers. */ + status = gcvSTATUS_NOT_SUPPORTED; +#endif + break; + + case gcvHAL_READ_ALL_PROFILE_REGISTERS_PART1: + /* Read profile data according to the context. */ + gcmkONERROR( + gckHARDWARE_QueryContextProfile( + Kernel->hardware, + Kernel->profileCleanRegister, + gcmNAME_TO_PTR(Interface->u.RegisterProfileData_part1.context), + &Interface->u.RegisterProfileData_part1.Counters, + gcvNULL)); + break; + case gcvHAL_READ_ALL_PROFILE_REGISTERS_PART2: + /* Read profile data according to the context. */ + gcmkONERROR( + gckHARDWARE_QueryContextProfile( + Kernel->hardware, + Kernel->profileCleanRegister, + gcmNAME_TO_PTR(Interface->u.RegisterProfileData_part2.context), + gcvNULL, + &Interface->u.RegisterProfileData_part2.Counters)); + break; + + case gcvHAL_GET_PROFILE_SETTING: +#if VIVANTE_PROFILER + /* Get profile setting */ + Interface->u.GetProfileSetting.enable = Kernel->profileEnable; +#endif + + status = gcvSTATUS_OK; + break; + + case gcvHAL_SET_PROFILE_SETTING: +#if VIVANTE_PROFILER + /* Set profile setting */ + if(Kernel->hardware->options.gpuProfiler) + { + Kernel->profileEnable = Interface->u.SetProfileSetting.enable; + + if (Kernel->profileEnable) + { + gcmkONERROR(gckHARDWARE_InitProfiler(Kernel->hardware)); + } + + } + else + { + status = gcvSTATUS_NOT_SUPPORTED; + break; + } +#endif + + status = gcvSTATUS_OK; + break; + + case gcvHAL_READ_PROFILER_REGISTER_SETTING: + Kernel->profileCleanRegister = Interface->u.SetProfilerRegisterClear.bclear; + status = gcvSTATUS_OK; + break; + + case gcvHAL_QUERY_KERNEL_SETTINGS: + /* Get kernel settings. */ + gcmkONERROR( + gckKERNEL_QuerySettings(Kernel, + &Interface->u.QueryKernelSettings.settings)); + break; + + case gcvHAL_RESET: + /* Reset the hardware. */ + gcmkONERROR( + gckHARDWARE_Reset(Kernel->hardware)); + break; + + case gcvHAL_DEBUG: + /* Set debug level and zones. */ + if (Interface->u.Debug.set) + { + gckOS_SetDebugLevel(Interface->u.Debug.level); + gckOS_SetDebugZones(Interface->u.Debug.zones, + Interface->u.Debug.enable); + } + + if (Interface->u.Debug.message[0] != '\0') + { + /* Print a message to the debugger. */ + if (Interface->u.Debug.type == gcvMESSAGE_TEXT) + { + gckOS_DumpBuffer(Kernel->os, + Interface->u.Debug.message, + gcmSIZEOF(Interface->u.Debug.message), + gcvDUMP_BUFFER_FROM_USER, + gcvTRUE); + } + else + { + gckOS_DumpBuffer(Kernel->os, + Interface->u.Debug.message, + Interface->u.Debug.messageSize, + gcvDUMP_BUFFER_FROM_USER, + gcvTRUE); + } + } + status = gcvSTATUS_OK; + break; + + case gcvHAL_DUMP_GPU_STATE: + { + gceCHIPPOWERSTATE power; + + _DumpDriverConfigure(Kernel); + + gcmkONERROR(gckHARDWARE_QueryPowerManagementState( + Kernel->hardware, + &power + )); + + if (power == gcvPOWER_ON) + { + Interface->u.ReadRegisterData.data = 1; + + _DumpState(Kernel); + } + else + { + Interface->u.ReadRegisterData.data = 0; + status = gcvSTATUS_CHIP_NOT_READY; + + gcmkPRINT("[galcore]: Can't dump state if GPU isn't POWER ON."); + } + } + break; + + case gcvHAL_DUMP_EVENT: + break; + + case gcvHAL_CACHE: + + logical = gcmUINT64_TO_PTR(Interface->u.Cache.logical); + + bytes = (gctSIZE_T) Interface->u.Cache.bytes; + switch(Interface->u.Cache.operation) + { + case gcvCACHE_FLUSH: + /* Clean and invalidate the cache. */ + status = gckOS_CacheFlush(Kernel->os, + processID, + physical, + paddr, + logical, + bytes); + break; + case gcvCACHE_CLEAN: + /* Clean the cache. */ + status = gckOS_CacheClean(Kernel->os, + processID, + physical, + paddr, + logical, + bytes); + break; + case gcvCACHE_INVALIDATE: + /* Invalidate the cache. */ + status = gckOS_CacheInvalidate(Kernel->os, + processID, + physical, + paddr, + logical, + bytes); + break; + + case gcvCACHE_MEMORY_BARRIER: + status = gckOS_MemoryBarrier(Kernel->os, + logical); + break; + default: + status = gcvSTATUS_INVALID_ARGUMENT; + break; + } + break; + + case gcvHAL_TIMESTAMP: + /* Check for invalid timer. */ + if ((Interface->u.TimeStamp.timer >= gcmCOUNTOF(Kernel->timers)) + || (Interface->u.TimeStamp.request != 2)) + { + Interface->u.TimeStamp.timeDelta = 0; + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + /* Return timer results and reset timer. */ + { + gcsTIMER_PTR timer = &(Kernel->timers[Interface->u.TimeStamp.timer]); + gctUINT64 timeDelta = 0; + + if (timer->stopTime < timer->startTime ) + { + Interface->u.TimeStamp.timeDelta = 0; + gcmkONERROR(gcvSTATUS_TIMER_OVERFLOW); + } + + timeDelta = timer->stopTime - timer->startTime; + + /* Check truncation overflow. */ + Interface->u.TimeStamp.timeDelta = (gctINT32) timeDelta; + /*bit0~bit30 is available*/ + if (timeDelta>>31) + { + Interface->u.TimeStamp.timeDelta = 0; + gcmkONERROR(gcvSTATUS_TIMER_OVERFLOW); + } + + status = gcvSTATUS_OK; + } + break; + + case gcvHAL_DATABASE: + gcmkONERROR(gckKERNEL_QueryDatabase(Kernel, processID, Interface)); + break; + + case gcvHAL_VERSION: + Interface->u.Version.major = gcvVERSION_MAJOR; + Interface->u.Version.minor = gcvVERSION_MINOR; + Interface->u.Version.patch = gcvVERSION_PATCH; + Interface->u.Version.build = gcvVERSION_BUILD; +#if gcmIS_DEBUG(gcdDEBUG_TRACE) + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_KERNEL, + "KERNEL version %d.%d.%d build %u", + gcvVERSION_MAJOR, gcvVERSION_MINOR, + gcvVERSION_PATCH, gcvVERSION_BUILD); +#endif + break; + + case gcvHAL_CHIP_INFO: + /* Only if not support multi-core */ + Interface->u.ChipInfo.count = 1; + Interface->u.ChipInfo.types[0] = Kernel->hardware->type; + break; + +#if (gcdENABLE_3D || gcdENABLE_2D) + case gcvHAL_ATTACH: + /* Attach user process. */ + gcmkONERROR( + gckCOMMAND_Attach(Kernel->command, + &context, + &bytes, + &Interface->u.Attach.numStates, + processID)); + + Interface->u.Attach.maxState = bytes; + Interface->u.Attach.context = gcmPTR_TO_NAME(context); + + if (Interface->u.Attach.map == gcvTRUE) + { + gcmkVERIFY_OK( + gckCONTEXT_MapBuffer(context, + Interface->u.Attach.physicals, + Interface->u.Attach.logicals, + &Interface->u.Attach.bytes)); + } + + gcmkVERIFY_OK( + gckKERNEL_AddProcessDB(Kernel, + processID, gcvDB_CONTEXT, + gcmINT2PTR(Interface->u.Attach.context), + gcvNULL, + 0)); + break; +#endif + + case gcvHAL_DETACH: + gcmkVERIFY_OK( + gckKERNEL_RemoveProcessDB(Kernel, + processID, gcvDB_CONTEXT, + gcmINT2PTR(Interface->u.Detach.context))); + + /* Detach user process. */ + gcmkONERROR( + gckCOMMAND_Detach(Kernel->command, + gcmNAME_TO_PTR(Interface->u.Detach.context))); + + gcmRELEASE_NAME(Interface->u.Detach.context); + break; + + case gcvHAL_GET_FRAME_INFO: + gcmkONERROR(gckHARDWARE_GetFrameInfo( + Kernel->hardware, + gcmUINT64_TO_PTR(Interface->u.GetFrameInfo.frameInfo))); + break; + + case gcvHAL_DUMP_GPU_PROFILE: + gcmkONERROR(gckHARDWARE_DumpGpuProfile(Kernel->hardware)); + break; + + case gcvHAL_SET_FSCALE_VALUE: +#if gcdENABLE_FSCALE_VAL_ADJUST + status = gckHARDWARE_SetFscaleValue(Kernel->hardware, + Interface->u.SetFscaleValue.value); +#else + status = gcvSTATUS_NOT_SUPPORTED; +#endif + break; + case gcvHAL_GET_FSCALE_VALUE: +#if gcdENABLE_FSCALE_VAL_ADJUST + status = gckHARDWARE_GetFscaleValue(Kernel->hardware, + &Interface->u.GetFscaleValue.value, + &Interface->u.GetFscaleValue.minValue, + &Interface->u.GetFscaleValue.maxValue); +#else + status = gcvSTATUS_NOT_SUPPORTED; +#endif + break; + + case gcvHAL_EXPORT_VIDEO_MEMORY: + /* Unlock video memory. */ + gcmkONERROR(gckVIDMEM_NODE_Export(Kernel, + Interface->u.ExportVideoMemory.node, + Interface->u.ExportVideoMemory.flags, + gcvNULL, + &Interface->u.ExportVideoMemory.fd)); + break; + + case gcvHAL_NAME_VIDEO_MEMORY: + gcmkONERROR(gckVIDMEM_NODE_Name(Kernel, + Interface->u.NameVideoMemory.handle, + &Interface->u.NameVideoMemory.name)); + break; + + case gcvHAL_IMPORT_VIDEO_MEMORY: + gcmkONERROR(gckVIDMEM_NODE_Import(Kernel, + Interface->u.ImportVideoMemory.name, + &Interface->u.ImportVideoMemory.handle)); + + gcmkONERROR( + gckKERNEL_AddProcessDB(Kernel, + processID, gcvDB_VIDEO_MEMORY, + gcmINT2PTR(Interface->u.ImportVideoMemory.handle), + gcvNULL, + 0)); + break; + + case gcvHAL_SET_VIDEO_MEMORY_METADATA: + gcmkONERROR(gckKERNEL_SetVidMemMetadata(Kernel, processID, Interface)); + break; + + case gcvHAL_GET_VIDEO_MEMORY_FD: + gcmkONERROR(gckVIDMEM_NODE_GetFd( + Kernel, + Interface->u.GetVideoMemoryFd.handle, + &Interface->u.GetVideoMemoryFd.fd + )); + + /* No need to add it to processDB because OS will release all fds when + ** process quits. + */ + break; + + case gcvHAL_QUERY_RESET_TIME_STAMP: + Interface->u.QueryResetTimeStamp.timeStamp = Kernel->resetTimeStamp; + Interface->u.QueryResetTimeStamp.contextID = Kernel->hardware->contextID; + break; + + case gcvHAL_FREE_VIRTUAL_COMMAND_BUFFER: + buffer = (gckVIRTUAL_COMMAND_BUFFER_PTR)gcmNAME_TO_PTR(Interface->u.FreeVirtualCommandBuffer.physical); + + gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB( + Kernel, + processID, + gcvDB_COMMAND_BUFFER, + gcmUINT64_TO_PTR(Interface->u.FreeVirtualCommandBuffer.logical))); + + gcmkONERROR(gckOS_DestroyUserVirtualMapping( + Kernel->os, + buffer->virtualBuffer.physical, + (gctSIZE_T)Interface->u.FreeVirtualCommandBuffer.bytes, + gcmUINT64_TO_PTR(Interface->u.FreeVirtualCommandBuffer.logical))); + + gcmkONERROR(gckKERNEL_DestroyVirtualCommandBuffer( + Kernel, + (gctSIZE_T)Interface->u.FreeVirtualCommandBuffer.bytes, + (gctPHYS_ADDR)buffer, + gcmUINT64_TO_PTR(Interface->u.FreeVirtualCommandBuffer.logical))); + + gcmRELEASE_NAME(Interface->u.FreeVirtualCommandBuffer.physical); + break; + +#if gcdANDROID_NATIVE_FENCE_SYNC + case gcvHAL_CREATE_NATIVE_FENCE: + { + gctINT fenceFD; + gctSIGNAL signal = + gcmUINT64_TO_PTR(Interface->u.CreateNativeFence.signal); + + gcmkONERROR( + gckOS_CreateNativeFence(Kernel->os, + Kernel->timeline, + signal, + &fenceFD)); + + Interface->u.CreateNativeFence.fenceFD = fenceFD; + } + break; + + case gcvHAL_WAIT_NATIVE_FENCE: + { + gctINT fenceFD; + gctUINT32 timeout; + + fenceFD = Interface->u.WaitNativeFence.fenceFD; + timeout = Interface->u.WaitNativeFence.timeout; + + gcmkONERROR( + gckOS_WaitNativeFence(Kernel->os, + Kernel->timeline, + fenceFD, + timeout)); + } + break; +#endif + + case gcvHAL_SHBUF: + { + gctSHBUF shBuf; + gctPOINTER uData; + gctUINT32 bytes; + + switch (Interface->u.ShBuf.command) + { + case gcvSHBUF_CREATE: + bytes = Interface->u.ShBuf.bytes; + + /* Create. */ + gcmkONERROR(gckKERNEL_CreateShBuffer(Kernel, bytes, &shBuf)); + + Interface->u.ShBuf.id = gcmPTR_TO_UINT64(shBuf); + + gcmkVERIFY_OK( + gckKERNEL_AddProcessDB(Kernel, + processID, + gcvDB_SHBUF, + shBuf, + gcvNULL, + 0)); + break; + + case gcvSHBUF_DESTROY: + shBuf = gcmUINT64_TO_PTR(Interface->u.ShBuf.id); + + /* Check db first to avoid illegal destroy in the process. */ + gcmkONERROR( + gckKERNEL_RemoveProcessDB(Kernel, + processID, + gcvDB_SHBUF, + shBuf)); + + gcmkONERROR(gckKERNEL_DestroyShBuffer(Kernel, shBuf)); + break; + + case gcvSHBUF_MAP: + shBuf = gcmUINT64_TO_PTR(Interface->u.ShBuf.id); + + /* Map for current process access. */ + gcmkONERROR(gckKERNEL_MapShBuffer(Kernel, shBuf)); + + gcmkVERIFY_OK( + gckKERNEL_AddProcessDB(Kernel, + processID, + gcvDB_SHBUF, + shBuf, + gcvNULL, + 0)); + break; + + case gcvSHBUF_WRITE: + shBuf = gcmUINT64_TO_PTR(Interface->u.ShBuf.id); + uData = gcmUINT64_TO_PTR(Interface->u.ShBuf.data); + bytes = Interface->u.ShBuf.bytes; + + /* Write. */ + gcmkONERROR( + gckKERNEL_WriteShBuffer(Kernel, shBuf, uData, bytes)); + break; + + case gcvSHBUF_READ: + shBuf = gcmUINT64_TO_PTR(Interface->u.ShBuf.id); + uData = gcmUINT64_TO_PTR(Interface->u.ShBuf.data); + bytes = Interface->u.ShBuf.bytes; + + /* Read. */ + gcmkONERROR( + gckKERNEL_ReadShBuffer(Kernel, + shBuf, + uData, + bytes, + &bytes)); + + /* Return copied size. */ + Interface->u.ShBuf.bytes = bytes; + break; + + default: + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + break; + } + } + break; + +#ifdef __linux__ + case gcvHAL_GET_GRAPHIC_BUFFER_FD: + gcmkONERROR(_GetGraphicBufferFd( + Kernel, + processID, + Interface->u.GetGraphicBufferFd.node, + Interface->u.GetGraphicBufferFd.shBuf, + Interface->u.GetGraphicBufferFd.signal, + &Interface->u.GetGraphicBufferFd.fd + )); + break; +#endif + + + case gcvHAL_CONFIG_POWER_MANAGEMENT: + gcmkONERROR(gckKERNEL_ConfigPowerManagement(Kernel, Interface)); + break; + + case gcvHAL_WRAP_USER_MEMORY: + gcmkONERROR(gckVIDMEM_NODE_WrapUserMemory(Kernel, + &Interface->u.WrapUserMemory.desc, + &Interface->u.WrapUserMemory.node, + &Interface->u.WrapUserMemory.bytes)); + + gcmkONERROR( + gckKERNEL_AddProcessDB(Kernel, + processID, + gcvDB_VIDEO_MEMORY, + gcmINT2PTR(Interface->u.WrapUserMemory.node), + gcvNULL, + 0)); + break; + + case gcvHAL_WAIT_FENCE: + gcmkONERROR(gckKERNEL_WaitFence( + Kernel, + Interface->u.WaitFence.handle, + Interface->u.WaitFence.timeOut + )); + break; + +#if gcdDEC_ENABLE_AHB + case gcvHAL_DEC300_READ: + gcmkONERROR(viv_dec300_read( + Interface->u.DEC300Read.enable, + Interface->u.DEC300Read.readId, + Interface->u.DEC300Read.format, + Interface->u.DEC300Read.strides, + Interface->u.DEC300Read.is3D, + Interface->u.DEC300Read.isMSAA, + Interface->u.DEC300Read.clearValue, + Interface->u.DEC300Read.isTPC, + Interface->u.DEC300Read.isTPCCompressed, + Interface->u.DEC300Read.surfAddrs, + Interface->u.DEC300Read.tileAddrs + )); + break; + + case gcvHAL_DEC300_WRITE: + gcmkONERROR(viv_dec300_write( + Interface->u.DEC300Write.enable, + Interface->u.DEC300Write.readId, + Interface->u.DEC300Write.writeId, + Interface->u.DEC300Write.format, + Interface->u.DEC300Write.surfAddr, + Interface->u.DEC300Write.tileAddr + )); + break; + + case gcvHAL_DEC300_FLUSH: + gcmkONERROR(viv_dec300_flush(0)); + break; + + case gcvHAL_DEC300_FLUSH_WAIT: + gcmkONERROR(viv_dec300_flush_done(&Interface->u.DEC300FlushWait.done)); + break; +#endif + + + case gcvHAL_QUERY_CHIP_OPTION: + /* Query chip options. */ + gcmkONERROR( + gckHARDWARE_QueryChipOptions( + Kernel->hardware, + &Interface->u.QueryChipOptions)); + break; + + default: + /* Invalid command. */ + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + +OnError: + /* Save status. */ + Interface->status = status; + +#if QNX_SINGLE_THREADED_DEBUGGING + gckOS_ReleaseMutex(Kernel->os, Kernel->debugMutex); +#endif + + if (powerMutexAcquired == gcvTRUE) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->hardware->powerMutex)); + } + + if (commitMutexAcquired == gcvTRUE) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->device->commitMutex)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_AttachProcess +** +** Attach or detach a process. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctBOOL Attach +** gcvTRUE if a new process gets attached or gcFALSE when a process +** gets detatched. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_AttachProcess( + IN gckKERNEL Kernel, + IN gctBOOL Attach + ) +{ + gceSTATUS status; + gctUINT32 processID; + + gcmkHEADER_ARG("Kernel=0x%x Attach=%d", Kernel, Attach); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + /* Get current process ID. */ + gcmkONERROR(gckOS_GetProcessID(&processID)); + + gcmkONERROR(gckKERNEL_AttachProcessEx(Kernel, Attach, processID)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_AttachProcessEx +** +** Attach or detach a process with the given PID. Can be paired with gckKERNEL_AttachProcess +** provided the programmer is aware of the consequences. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctBOOL Attach +** gcvTRUE if a new process gets attached or gcFALSE when a process +** gets detatched. +** +** gctUINT32 PID +** PID of the process to attach or detach. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_AttachProcessEx( + IN gckKERNEL Kernel, + IN gctBOOL Attach, + IN gctUINT32 PID + ) +{ + gceSTATUS status; + gctINT32 old; + + gcmkHEADER_ARG("Kernel=0x%x Attach=%d PID=%d", Kernel, Attach, PID); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + if (Attach) + { + /* Increment the number of clients attached. */ + gcmkONERROR( + gckOS_AtomIncrement(Kernel->os, Kernel->atomClients, &old)); + + if (old == 0) + { +#if gcdENABLE_VG + if (Kernel->vg == gcvNULL) +#endif + { + gcmkONERROR(gckOS_Broadcast(Kernel->os, + Kernel->hardware, + gcvBROADCAST_FIRST_PROCESS)); + } + } + + if (Kernel->dbCreated) + { + /* Create the process database. */ + gcmkONERROR(gckKERNEL_CreateProcessDB(Kernel, PID)); + } + +#if gcdPROCESS_ADDRESS_SPACE + /* Map kernel command buffer in the process's own MMU. */ + gcmkONERROR(_MapCommandBuffer(Kernel)); +#endif + } + else + { + if (Kernel->dbCreated) + { + /* Clean up the process database. */ + gcmkONERROR(gckKERNEL_DestroyProcessDB(Kernel, PID)); + + /* Save the last know process ID. */ + Kernel->db->lastProcessID = PID; + } + +#if gcdENABLE_VG + if (Kernel->vg == gcvNULL) +#endif + { + status = gckEVENT_Submit(Kernel->eventObj, gcvTRUE, gcvFALSE); + + if (status == gcvSTATUS_INTERRUPTED && Kernel->eventObj->submitTimer) + { + gcmkONERROR(gckOS_StartTimer(Kernel->os, + Kernel->eventObj->submitTimer, + 1)); + } + else + { + gcmkONERROR(status); + } + } + + /* Decrement the number of clients attached. */ + gcmkONERROR( + gckOS_AtomDecrement(Kernel->os, Kernel->atomClients, &old)); + + if (old == 1) + { +#if gcdENABLE_VG + if (Kernel->vg == gcvNULL) +#endif + { + /* Last client detached, switch to SUSPEND power state. */ + gcmkONERROR(gckOS_Broadcast(Kernel->os, + Kernel->hardware, + gcvBROADCAST_LAST_PROCESS)); + } + + /* Flush the debug cache. */ + gcmkDEBUGFLUSH(~0U); + } + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +#if gcdSECURE_USER +gceSTATUS +gckKERNEL_MapLogicalToPhysical( + IN gckKERNEL Kernel, + IN gcskSECURE_CACHE_PTR Cache, + IN OUT gctPOINTER * Data + ) +{ + gceSTATUS status; + static gctBOOL baseAddressValid = gcvFALSE; + static gctUINT32 baseAddress; + gctBOOL needBase; + gcskLOGICAL_CACHE_PTR slot; + + gcmkHEADER_ARG("Kernel=0x%x Cache=0x%x *Data=0x%x", + Kernel, Cache, gcmOPT_POINTER(Data)); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + if (!baseAddressValid) + { + /* Get base address. */ + gcmkONERROR(gckHARDWARE_GetBaseAddress(Kernel->hardware, &baseAddress)); + + baseAddressValid = gcvTRUE; + } + + /* Does this state load need a base address? */ + gcmkONERROR(gckHARDWARE_NeedBaseAddress(Kernel->hardware, + ((gctUINT32_PTR) Data)[-1], + &needBase)); + +#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_LRU + { + gcskLOGICAL_CACHE_PTR next; + gctINT i; + + /* Walk all used cache slots. */ + for (i = 1, slot = Cache->cache[0].next, next = gcvNULL; + (i <= gcdSECURE_CACHE_SLOTS) && (slot->logical != gcvNULL); + ++i, slot = slot->next + ) + { + if (slot->logical == *Data) + { + /* Bail out. */ + next = slot; + break; + } + } + + /* See if we had a miss. */ + if (next == gcvNULL) + { + /* Use the tail of the cache. */ + slot = Cache->cache[0].prev; + + /* Initialize the cache line. */ + slot->logical = *Data; + + /* Map the logical address to a DMA address. */ + gcmkONERROR( + gckOS_GetPhysicalAddress(Kernel->os, *Data, &slot->dma)); + } + + /* Move slot to head of list. */ + if (slot != Cache->cache[0].next) + { + /* Unlink. */ + slot->prev->next = slot->next; + slot->next->prev = slot->prev; + + /* Move to head of chain. */ + slot->prev = &Cache->cache[0]; + slot->next = Cache->cache[0].next; + slot->prev->next = slot; + slot->next->prev = slot; + } + } +#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_LINEAR + { + gctINT i; + gcskLOGICAL_CACHE_PTR next = gcvNULL; + gcskLOGICAL_CACHE_PTR oldestSlot = gcvNULL; + slot = gcvNULL; + + if (Cache->cacheIndex != gcvNULL) + { + /* Walk the cache forwards. */ + for (i = 1, slot = Cache->cacheIndex; + (i <= gcdSECURE_CACHE_SLOTS) && (slot->logical != gcvNULL); + ++i, slot = slot->next) + { + if (slot->logical == *Data) + { + /* Bail out. */ + next = slot; + break; + } + + /* Determine age of this slot. */ + if ((oldestSlot == gcvNULL) + || (oldestSlot->stamp > slot->stamp) + ) + { + oldestSlot = slot; + } + } + + if (next == gcvNULL) + { + /* Walk the cache backwards. */ + for (slot = Cache->cacheIndex->prev; + (i <= gcdSECURE_CACHE_SLOTS) && (slot->logical != gcvNULL); + ++i, slot = slot->prev) + { + if (slot->logical == *Data) + { + /* Bail out. */ + next = slot; + break; + } + + /* Determine age of this slot. */ + if ((oldestSlot == gcvNULL) + || (oldestSlot->stamp > slot->stamp) + ) + { + oldestSlot = slot; + } + } + } + } + + /* See if we had a miss. */ + if (next == gcvNULL) + { + if (Cache->cacheFree != 0) + { + slot = &Cache->cache[Cache->cacheFree]; + gcmkASSERT(slot->logical == gcvNULL); + + ++ Cache->cacheFree; + if (Cache->cacheFree >= gcmCOUNTOF(Cache->cache)) + { + Cache->cacheFree = 0; + } + } + else + { + /* Use the oldest cache slot. */ + gcmkASSERT(oldestSlot != gcvNULL); + slot = oldestSlot; + + /* Unlink from the chain. */ + slot->prev->next = slot->next; + slot->next->prev = slot->prev; + + /* Append to the end. */ + slot->prev = Cache->cache[0].prev; + slot->next = &Cache->cache[0]; + slot->prev->next = slot; + slot->next->prev = slot; + } + + /* Initialize the cache line. */ + slot->logical = *Data; + + /* Map the logical address to a DMA address. */ + gcmkONERROR( + gckOS_GetPhysicalAddress(Kernel->os, *Data, &slot->dma)); + } + + /* Save time stamp. */ + slot->stamp = ++ Cache->cacheStamp; + + /* Save current slot for next lookup. */ + Cache->cacheIndex = slot; + } +#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH + { + gctINT i; + gctUINT32 data = gcmPTR2INT32(*Data); + gctUINT32 key, index; + gcskLOGICAL_CACHE_PTR hash; + + /* Generate a hash key. */ + key = (data >> 24) + (data >> 16) + (data >> 8) + data; + index = key % gcmCOUNTOF(Cache->hash); + + /* Get the hash entry. */ + hash = &Cache->hash[index]; + + for (slot = hash->nextHash, i = 0; + (slot != gcvNULL) && (i < gcdSECURE_CACHE_SLOTS); + slot = slot->nextHash, ++i + ) + { + if (slot->logical == (*Data)) + { + break; + } + } + + if (slot == gcvNULL) + { + /* Grab from the tail of the cache. */ + slot = Cache->cache[0].prev; + + /* Unlink slot from any hash table it is part of. */ + if (slot->prevHash != gcvNULL) + { + slot->prevHash->nextHash = slot->nextHash; + } + if (slot->nextHash != gcvNULL) + { + slot->nextHash->prevHash = slot->prevHash; + } + + /* Initialize the cache line. */ + slot->logical = *Data; + + /* Map the logical address to a DMA address. */ + gcmkONERROR( + gckOS_GetPhysicalAddress(Kernel->os, *Data, &slot->dma)); + + if (hash->nextHash != gcvNULL) + { + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_KERNEL, + "Hash Collision: logical=0x%x key=0x%08x", + *Data, key); + } + + /* Insert the slot at the head of the hash list. */ + slot->nextHash = hash->nextHash; + if (slot->nextHash != gcvNULL) + { + slot->nextHash->prevHash = slot; + } + slot->prevHash = hash; + hash->nextHash = slot; + } + + /* Move slot to head of list. */ + if (slot != Cache->cache[0].next) + { + /* Unlink. */ + slot->prev->next = slot->next; + slot->next->prev = slot->prev; + + /* Move to head of chain. */ + slot->prev = &Cache->cache[0]; + slot->next = Cache->cache[0].next; + slot->prev->next = slot; + slot->next->prev = slot; + } + } +#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_TABLE + { + gctUINT32 index = (gcmPTR2INT32(*Data) % gcdSECURE_CACHE_SLOTS) + 1; + + /* Get cache slot. */ + slot = &Cache->cache[index]; + + /* Check for cache miss. */ + if (slot->logical != *Data) + { + /* Initialize the cache line. */ + slot->logical = *Data; + + /* Map the logical address to a DMA address. */ + gcmkONERROR( + gckOS_GetPhysicalAddress(Kernel->os, *Data, &slot->dma)); + } + } +#endif + + /* Return DMA address. */ + *Data = gcmINT2PTR(slot->dma + (needBase ? baseAddress : 0)); + + /* Success. */ + gcmkFOOTER_ARG("*Data=0x%08x", *Data); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_FlushTranslationCache( + IN gckKERNEL Kernel, + IN gcskSECURE_CACHE_PTR Cache, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes + ) +{ + gctINT i; + gcskLOGICAL_CACHE_PTR slot; + gctUINT8_PTR ptr; + + gcmkHEADER_ARG("Kernel=0x%x Cache=0x%x Logical=0x%x Bytes=%lu", + Kernel, Cache, Logical, Bytes); + + /* Do we need to flush the entire cache? */ + if (Logical == gcvNULL) + { + /* Clear all cache slots. */ + for (i = 1; i <= gcdSECURE_CACHE_SLOTS; ++i) + { + Cache->cache[i].logical = gcvNULL; + +#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH + Cache->cache[i].nextHash = gcvNULL; + Cache->cache[i].prevHash = gcvNULL; +#endif +} + +#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH + /* Zero the hash table. */ + for (i = 0; i < gcmCOUNTOF(Cache->hash); ++i) + { + Cache->hash[i].nextHash = gcvNULL; + } +#endif + + /* Reset the cache functionality. */ + Cache->cacheIndex = gcvNULL; + Cache->cacheFree = 1; + Cache->cacheStamp = 0; + } + + else + { + gctUINT8_PTR low = (gctUINT8_PTR) Logical; + gctUINT8_PTR high = low + Bytes; + +#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_LRU + gcskLOGICAL_CACHE_PTR next; + + /* Walk all used cache slots. */ + for (i = 1, slot = Cache->cache[0].next; + (i <= gcdSECURE_CACHE_SLOTS) && (slot->logical != gcvNULL); + ++i, slot = next + ) + { + /* Save pointer to next slot. */ + next = slot->next; + + /* Test if this slot falls within the range to flush. */ + ptr = (gctUINT8_PTR) slot->logical; + if ((ptr >= low) && (ptr < high)) + { + /* Unlink slot. */ + slot->prev->next = slot->next; + slot->next->prev = slot->prev; + + /* Append slot to tail of cache. */ + slot->prev = Cache->cache[0].prev; + slot->next = &Cache->cache[0]; + slot->prev->next = slot; + slot->next->prev = slot; + + /* Mark slot as empty. */ + slot->logical = gcvNULL; + } + } + +#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_LINEAR + gcskLOGICAL_CACHE_PTR next; + + for (i = 1, slot = Cache->cache[0].next; + (i <= gcdSECURE_CACHE_SLOTS) && (slot->logical != gcvNULL); + ++i, slot = next) + { + /* Save pointer to next slot. */ + next = slot->next; + + /* Test if this slot falls within the range to flush. */ + ptr = (gctUINT8_PTR) slot->logical; + if ((ptr >= low) && (ptr < high)) + { + /* Test if this slot is the current slot. */ + if (slot == Cache->cacheIndex) + { + /* Move to next or previous slot. */ + Cache->cacheIndex = (slot->next->logical != gcvNULL) + ? slot->next + : (slot->prev->logical != gcvNULL) + ? slot->prev + : gcvNULL; + } + + /* Unlink slot from cache. */ + slot->prev->next = slot->next; + slot->next->prev = slot->prev; + + /* Insert slot to head of cache. */ + slot->prev = &Cache->cache[0]; + slot->next = Cache->cache[0].next; + slot->prev->next = slot; + slot->next->prev = slot; + + /* Mark slot as empty. */ + slot->logical = gcvNULL; + slot->stamp = 0; + } + } + +#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH + gctINT j; + gcskLOGICAL_CACHE_PTR hash, next; + + /* Walk all hash tables. */ + for (i = 0, hash = Cache->hash; + i < gcmCOUNTOF(Cache->hash); + ++i, ++hash) + { + /* Walk all slots in the hash. */ + for (j = 0, slot = hash->nextHash; + (j < gcdSECURE_CACHE_SLOTS) && (slot != gcvNULL); + ++j, slot = next) + { + /* Save pointer to next slot. */ + next = slot->next; + + /* Test if this slot falls within the range to flush. */ + ptr = (gctUINT8_PTR) slot->logical; + if ((ptr >= low) && (ptr < high)) + { + /* Unlink slot from hash table. */ + if (slot->prevHash == hash) + { + hash->nextHash = slot->nextHash; + } + else + { + slot->prevHash->nextHash = slot->nextHash; + } + + if (slot->nextHash != gcvNULL) + { + slot->nextHash->prevHash = slot->prevHash; + } + + /* Unlink slot from cache. */ + slot->prev->next = slot->next; + slot->next->prev = slot->prev; + + /* Append slot to tail of cache. */ + slot->prev = Cache->cache[0].prev; + slot->next = &Cache->cache[0]; + slot->prev->next = slot; + slot->next->prev = slot; + + /* Mark slot as empty. */ + slot->logical = gcvNULL; + slot->prevHash = gcvNULL; + slot->nextHash = gcvNULL; + } + } + } + +#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_TABLE + gctUINT32 index; + + /* Loop while inside the range. */ + for (i = 1; (low < high) && (i <= gcdSECURE_CACHE_SLOTS); ++i) + { + /* Get index into cache for this range. */ + index = (gcmPTR2INT32(low) % gcdSECURE_CACHE_SLOTS) + 1; + slot = &Cache->cache[index]; + + /* Test if this slot falls within the range to flush. */ + ptr = (gctUINT8_PTR) slot->logical; + if ((ptr >= low) && (ptr < high)) + { + /* Remove entry from cache. */ + slot->logical = gcvNULL; + } + + /* Next block. */ + low += gcdSECURE_CACHE_SLOTS; + } +#endif + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} +#endif + +/******************************************************************************* +** +** gckKERNEL_Recovery +** +** Try to recover the GPU from a fatal error. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_Recovery( + IN gckKERNEL Kernel + ) +{ + gceSTATUS status; + gckEVENT eventObj; + gckHARDWARE hardware; +#if gcdSECURE_USER + gctUINT32 processID; + gcskSECURE_CACHE_PTR cache; +#endif + gctUINT32 mask = 0; + gctUINT32 i = 0, count = 0; +#if gcdINTERRUPT_STATISTIC + gctINT32 oldValue; +#endif + + gcmkHEADER_ARG("Kernel=0x%x", Kernel); + + /* Validate the arguemnts. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + /* Grab gckEVENT object. */ + eventObj = Kernel->eventObj; + gcmkVERIFY_OBJECT(eventObj, gcvOBJ_EVENT); + + /* Grab gckHARDWARE object. */ + hardware = Kernel->hardware; + gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE); + +#if gcdSECURE_USER + /* Flush the secure mapping cache. */ + gcmkONERROR(gckOS_GetProcessID(&processID)); + gcmkONERROR(gckKERNEL_GetProcessDBCache(Kernel, processID, &cache)); + gcmkONERROR(gckKERNEL_FlushTranslationCache(Kernel, cache, gcvNULL, 0)); +#endif + + if (Kernel->stuckDump == gcvSTUCK_DUMP_NONE) + { + gcmkPRINT("[galcore]: GPU[%d] hang, automatic recovery.", Kernel->core); + } + else + { + gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, Kernel->device->stuckDumpMutex, gcvINFINITE)); + + _DumpDriverConfigure(Kernel); + _DumpState(Kernel); + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->device->stuckDumpMutex)); + } + + if (Kernel->recovery == gcvFALSE) + { + gcmkPRINT("[galcore]: Stop driver to keep scene."); + + /* Stop monitor timer. */ + Kernel->monitorTimerStop = gcvTRUE; + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + + /* Issuing a soft reset for the GPU. */ + gcmkONERROR(gckHARDWARE_Reset(hardware)); + + mask = Kernel->restoreMask; + + for (i = 0; i < 32; i++) + { + if (mask & (1 << i)) + { + count++; + } + } + + /* Handle all outstanding events now. */ + gcmkONERROR(gckOS_AtomSet(Kernel->os, eventObj->pending, mask)); + +#if gcdINTERRUPT_STATISTIC + while (count--) + { + gcmkONERROR(gckOS_AtomDecrement( + Kernel->os, + eventObj->interruptCount, + &oldValue + )); + } + + gckOS_AtomClearMask(Kernel->hardware->pendingEvent, mask); +#endif + + gcmkONERROR(gckEVENT_Notify(eventObj, 1)); + + gcmkVERIFY_OK(gckOS_GetTime(&Kernel->resetTimeStamp)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_OpenUserData +** +** Get access to the user data. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctBOOL NeedCopy +** The flag indicating whether or not the data should be copied. +** +** gctPOINTER StaticStorage +** Pointer to the kernel storage where the data is to be copied if +** NeedCopy is gcvTRUE. +** +** gctPOINTER UserPointer +** User pointer to the data. +** +** gctSIZE_T Size +** Size of the data. +** +** OUTPUT: +** +** gctPOINTER * KernelPointer +** Pointer to the kernel pointer that will be pointing to the data. +*/ +gceSTATUS +gckKERNEL_OpenUserData( + IN gckKERNEL Kernel, + IN gctBOOL NeedCopy, + IN gctPOINTER StaticStorage, + IN gctPOINTER UserPointer, + IN gctSIZE_T Size, + OUT gctPOINTER * KernelPointer + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG( + "Kernel=0x%08X NeedCopy=%d StaticStorage=0x%08X " + "UserPointer=0x%08X Size=%lu KernelPointer=0x%08X", + Kernel, NeedCopy, StaticStorage, UserPointer, Size, KernelPointer + ); + + /* Validate the arguemnts. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(!NeedCopy || (StaticStorage != gcvNULL)); + gcmkVERIFY_ARGUMENT(UserPointer != gcvNULL); + gcmkVERIFY_ARGUMENT(KernelPointer != gcvNULL); + gcmkVERIFY_ARGUMENT(Size > 0); + + if (NeedCopy) + { + /* Copy the user data to the static storage. */ + gcmkONERROR(gckOS_CopyFromUserData( + Kernel->os, StaticStorage, UserPointer, Size + )); + + /* Set the kernel pointer. */ + * KernelPointer = StaticStorage; + } + else + { + gctPOINTER pointer = gcvNULL; + + /* Map the user pointer. */ + gcmkONERROR(gckOS_MapUserPointer( + Kernel->os, UserPointer, Size, &pointer + )); + + /* Set the kernel pointer. */ + * KernelPointer = pointer; + } + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_CloseUserData +** +** Release resources associated with the user data connection opened by +** gckKERNEL_OpenUserData. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctBOOL NeedCopy +** The flag indicating whether or not the data should be copied. +** +** gctBOOL FlushData +** If gcvTRUE, the data is written back to the user. +** +** gctPOINTER UserPointer +** User pointer to the data. +** +** gctSIZE_T Size +** Size of the data. +** +** OUTPUT: +** +** gctPOINTER * KernelPointer +** Kernel pointer to the data. +*/ +gceSTATUS +gckKERNEL_CloseUserData( + IN gckKERNEL Kernel, + IN gctBOOL NeedCopy, + IN gctBOOL FlushData, + IN gctPOINTER UserPointer, + IN gctSIZE_T Size, + OUT gctPOINTER * KernelPointer + ) +{ + gceSTATUS status = gcvSTATUS_OK; + gctPOINTER pointer; + + gcmkHEADER_ARG( + "Kernel=0x%08X NeedCopy=%d FlushData=%d " + "UserPointer=0x%08X Size=%lu KernelPointer=0x%08X", + Kernel, NeedCopy, FlushData, UserPointer, Size, KernelPointer + ); + + /* Validate the arguemnts. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(UserPointer != gcvNULL); + gcmkVERIFY_ARGUMENT(KernelPointer != gcvNULL); + gcmkVERIFY_ARGUMENT(Size > 0); + + /* Get a shortcut to the kernel pointer. */ + pointer = * KernelPointer; + + if (pointer != gcvNULL) + { + if (NeedCopy) + { + if (FlushData) + { + gcmkONERROR(gckOS_CopyToUserData( + Kernel->os, * KernelPointer, UserPointer, Size + )); + } + } + else + { + /* Unmap record from kernel memory. */ + gcmkONERROR(gckOS_UnmapUserPointer( + Kernel->os, + UserPointer, + Size, + * KernelPointer + )); + } + + /* Reset the kernel pointer. */ + * KernelPointer = gcvNULL; + } + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_AllocateVirtualCommandBuffer( + IN gckKERNEL Kernel, + IN gctBOOL InUserSpace, + IN OUT gctSIZE_T * Bytes, + OUT gctPHYS_ADDR * Physical, + OUT gctPOINTER * Logical + ) +{ + gceSTATUS status; + gckOS os = Kernel->os; + gckVIRTUAL_COMMAND_BUFFER_PTR buffer; + + 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); + + gcmkONERROR( + gckOS_Allocate( + os, + sizeof(gckVIRTUAL_COMMAND_BUFFER), + (gctPOINTER)&buffer + )); + + gcmkONERROR(gckOS_ZeroMemory(buffer, sizeof(gckVIRTUAL_COMMAND_BUFFER))); + + gcmkONERROR( + gckKERNEL_AllocateVirtualMemory( + Kernel, + gcvFALSE, + InUserSpace, + Bytes, + (gctPHYS_ADDR *)&buffer, + Logical + )); + + gcmkVERIFY_OK(gckOS_AcquireMutex(os, Kernel->virtualBufferLock, gcvINFINITE)); + + if (Kernel->virtualBufferHead == gcvNULL) + { + Kernel->virtualBufferHead = + Kernel->virtualBufferTail = buffer; + } + else + { + buffer->prev = Kernel->virtualBufferTail; + Kernel->virtualBufferTail->next = buffer; + Kernel->virtualBufferTail = buffer; + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Kernel->virtualBufferLock)); + + *Physical = buffer; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkVERIFY_OK(gckOS_Free(os, buffer)); + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_DestroyVirtualCommandBuffer( + IN gckKERNEL Kernel, + IN gctSIZE_T Bytes, + IN gctPHYS_ADDR Physical, + IN gctPOINTER Logical + ) +{ + gckOS os; + gckKERNEL kernel; + gckVIRTUAL_COMMAND_BUFFER_PTR buffer = (gckVIRTUAL_COMMAND_BUFFER_PTR)Physical; + + gcmkHEADER(); + gcmkVERIFY_ARGUMENT(buffer != gcvNULL); + + kernel = buffer->virtualBuffer.kernel; + os = kernel->os; + + gcmkVERIFY_OK(gckOS_AcquireMutex(os, kernel->virtualBufferLock, gcvINFINITE)); + + if (buffer == kernel->virtualBufferHead) + { + if ((kernel->virtualBufferHead = buffer->next) == gcvNULL) + { + kernel->virtualBufferTail = gcvNULL; + } + } + else + { + buffer->prev->next = buffer->next; + + if (buffer == kernel->virtualBufferTail) + { + kernel->virtualBufferTail = buffer->prev; + } + else + { + buffer->next->prev = buffer->prev; + } + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, kernel->virtualBufferLock)); + + gcmkVERIFY_OK( + gckKERNEL_FreeVirtualMemory( + Physical, + Logical, + gcvFALSE + )); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckKERNEL_AllocateVirtualMemory( + IN gckKERNEL Kernel, + IN gctBOOL NonPaged, + IN gctBOOL InUserSpace, + IN OUT gctSIZE_T * Bytes, + OUT gctPHYS_ADDR * Physical, + OUT gctPOINTER * Logical + ) +{ + gckOS os = Kernel->os; + gceSTATUS status; + gctPOINTER logical = gcvNULL; + gctSIZE_T pageCount; + gctSIZE_T bytes = *Bytes; + gckVIRTUAL_BUFFER_PTR buffer = gcvNULL; + gckMMU mmu = gcvNULL; + gctUINT32 flag = gcvALLOC_FLAG_NON_CONTIGUOUS; + + gcmkHEADER_ARG("Os=0x%X InUserSpace=%d *Bytes=%lu", + os, InUserSpace, gcmOPT_VALUE(Bytes)); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Bytes != gcvNULL); + gcmkVERIFY_ARGUMENT(*Bytes > 0); + gcmkVERIFY_ARGUMENT(Physical != gcvNULL); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + + if (*Physical == gcvNULL) + { + gcmkONERROR(gckOS_Allocate(os, + sizeof(gckVIRTUAL_BUFFER), + (gctPOINTER)&buffer)); + + gcmkONERROR(gckOS_ZeroMemory(buffer, sizeof(gckVIRTUAL_BUFFER))); + } + else + { + buffer = *Physical; + } + + buffer->bytes = bytes; + + if (NonPaged) + { + gcmkONERROR(gckOS_AllocateNonPagedMemory( + os, + InUserSpace, + &bytes, + &buffer->physical, + &logical + )); + } + else + { + gcmkONERROR(gckOS_AllocatePagedMemoryEx(os, + flag, + bytes, + gcvNULL, + &buffer->physical)); + } + + if (NonPaged) + { + gctSIZE_T pageSize; + gcmkONERROR(gckOS_GetPageSize(os, &pageSize)); + + pageCount = (bytes + pageSize - 1) / pageSize; + + if (InUserSpace) + { + *Logical = + buffer->userLogical = logical; + } + else + { + *Logical = + buffer->kernelLogical = logical; + } + } + else + { + if (InUserSpace) + { + gcmkONERROR(gckOS_CreateUserVirtualMapping(os, + buffer->physical, + bytes, + &logical, + &pageCount)); + + *Logical = + buffer->userLogical = logical; + } + else + { + gcmkONERROR(gckOS_CreateKernelVirtualMapping(os, + buffer->physical, + bytes, + &logical, + &pageCount)); + + *Logical = + buffer->kernelLogical = logical; + } + + } + + buffer->pageCount = pageCount; + buffer->kernel = Kernel; + + gcmkONERROR(gckOS_GetProcessID(&buffer->pid)); + +#if gcdPROCESS_ADDRESS_SPACE + gcmkONERROR(gckKERNEL_GetProcessMMU(Kernel, &mmu)); + buffer->mmu = mmu; +#else + mmu = Kernel->mmu; +#endif + + gcmkONERROR(gckMMU_AllocatePages(mmu, + pageCount, + &buffer->pageTable, + &buffer->gpuAddress)); + +#if gcdENABLE_TRUST_APPLICATION + if (Kernel->hardware->options.secureMode == gcvSECURE_IN_TA) + { + gcmkONERROR(gckKERNEL_MapInTrustApplicaiton( + Kernel, + logical, + buffer->physical, + buffer->gpuAddress, + pageCount + )); + } + else +#endif + { + gcmkONERROR(gckOS_MapPagesEx(os, + Kernel->core, + buffer->physical, + pageCount, + buffer->gpuAddress, + buffer->pageTable, + gcvFALSE, + gcvSURF_TYPE_UNKNOWN + )); + } + + gcmkONERROR(gckMMU_Flush(mmu, gcvSURF_INDEX)); + + if (*Physical == gcvNULL) + *Physical = buffer; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_KERNEL, + "gpuAddress = %x pageCount = %d kernelLogical = %x userLogical=%x", + buffer->gpuAddress, buffer->pageCount, + buffer->kernelLogical, buffer->userLogical); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (buffer && buffer->gpuAddress) + { + gcmkVERIFY_OK( + gckMMU_FreePages(mmu, gcvFALSE, buffer->gpuAddress, buffer->pageTable, buffer->pageCount)); + } + + if (NonPaged && buffer->physical) + { + gcmkVERIFY_OK(gckOS_FreeNonPagedMemory( + os, + bytes, + buffer->physical, + logical + )); + } + else + { + if (buffer && buffer->userLogical) + { + gcmkVERIFY_OK( + gckOS_DestroyUserVirtualMapping(os, + buffer->physical, + bytes, + (NonPaged ? 0 : buffer->userLogical))); + } + + if (buffer && buffer->kernelLogical) + { + gcmkVERIFY_OK( + gckOS_DestroyKernelVirtualMapping(os, + buffer->physical, + bytes, + (NonPaged ? 0 : buffer->kernelLogical))); + } + + if (buffer && buffer->physical) + { + gcmkVERIFY_OK(gckOS_FreePagedMemory(os, buffer->physical, bytes)); + } + } + + if (*Physical == gcvNULL) + gcmkVERIFY_OK(gckOS_Free(os, buffer)); + + /* Return the status. */ + gcmkFOOTER(); + return status; + +} + +gceSTATUS +gckKERNEL_FreeVirtualMemory( + IN gctPHYS_ADDR Physical, + IN gctPOINTER Logical, + IN gctBOOL NonPaged + ) +{ + gckOS os; + gckKERNEL kernel; + gckMMU mmu; + gckVIRTUAL_BUFFER_PTR buffer = (gckVIRTUAL_BUFFER_PTR)Physical; + + gcmkHEADER(); + gcmkVERIFY_ARGUMENT(buffer != gcvNULL); + + kernel = buffer->kernel; + os = kernel->os; + +#if gcdPROCESS_ADDRESS_SPACE + gcmkONERROR(gckKERNEL_GetProcessMMU(Kernel, &mmu)); +#else + mmu = kernel->mmu; +#endif + + if (!buffer->userLogical && !NonPaged) + { + gcmkVERIFY_OK(gckOS_DestroyKernelVirtualMapping(os, + buffer->physical, + buffer->bytes, + Logical)); + } + + gcmkVERIFY_OK( + gckMMU_FreePages(mmu, gcvFALSE, buffer->gpuAddress, buffer->pageTable, buffer->pageCount)); + + gcmkVERIFY_OK(gckOS_UnmapPages(os, buffer->pageCount, buffer->gpuAddress)); + + if (NonPaged) + { + gcmkVERIFY_OK(gckOS_FreeNonPagedMemory( + os, + buffer->bytes, + buffer->physical, + Logical + )); + } + else + { + gcmkVERIFY_OK(gckOS_FreePagedMemory(os, buffer->physical, buffer->bytes)); + } + + gcmkVERIFY_OK(gckOS_Free(os, buffer)); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckKERNEL_GetGPUAddress( + IN gckKERNEL Kernel, + IN gctPOINTER Logical, + IN gctBOOL InUserSpace, + IN gctPHYS_ADDR Physical, + OUT gctUINT32 * Address + ) +{ + gckVIRTUAL_BUFFER_PTR buffer = Physical; + gctPOINTER start; + + gcmkHEADER_ARG("Logical = %x InUserSpace=%d.", Logical, InUserSpace); + + if (InUserSpace) + { + start = buffer->userLogical; + } + else + { + start = buffer->kernelLogical; + } + + gcmkASSERT(Logical >= start + && (Logical < (gctPOINTER)((gctUINT8_PTR)start + buffer->bytes))); + + * Address = buffer->gpuAddress + (gctUINT32)((gctUINT8_PTR)Logical - (gctUINT8_PTR)start); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckKERNEL_QueryGPUAddress( + IN gckKERNEL Kernel, + IN gctUINT32 GpuAddress, + OUT gckVIRTUAL_COMMAND_BUFFER_PTR * Buffer + ) +{ + gckVIRTUAL_COMMAND_BUFFER_PTR buffer; + gctUINT32 start; + gceSTATUS status = gcvSTATUS_NOT_SUPPORTED; + + gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, Kernel->virtualBufferLock, gcvINFINITE)); + + /* Walk all command buffers. */ + for (buffer = Kernel->virtualBufferHead; buffer != gcvNULL; buffer = buffer->next) + { + start = (gctUINT32)buffer->virtualBuffer.gpuAddress; + + if (GpuAddress >= start && GpuAddress <= (start - 1 + buffer->virtualBuffer.pageCount * 4096)) + { + /* Find a range matched. */ + *Buffer = buffer; + status = gcvSTATUS_OK; + break; + } + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->virtualBufferLock)); + + return status; +} + +static void +gckQUEUE_Dequeue( + IN gckQUEUE LinkQueue + ) +{ + gcmkASSERT(LinkQueue->count == LinkQueue->size); + + LinkQueue->count--; + LinkQueue->front = (LinkQueue->front + 1) % gcdLINK_QUEUE_SIZE; +} + +void +gckQUEUE_Enqueue( + IN gckQUEUE LinkQueue, + IN gcuQUEUEDATA *Data + ) +{ + gcuQUEUEDATA * datas = LinkQueue->datas; + + if (LinkQueue->count == LinkQueue->size) + { + gckQUEUE_Dequeue(LinkQueue); + } + + gcmkASSERT(LinkQueue->count < LinkQueue->size); + + LinkQueue->count++; + + datas[LinkQueue->rear] = *Data; + + LinkQueue->rear = (LinkQueue->rear + 1) % LinkQueue->size; +} + +void +gckQUEUE_GetData( + IN gckQUEUE LinkQueue, + IN gctUINT32 Index, + OUT gcuQUEUEDATA ** Data + ) +{ + gcuQUEUEDATA * datas = LinkQueue->datas; + + gcmkASSERT(Index >= 0 && Index < LinkQueue->size); + + *Data = &datas[(Index + LinkQueue->front) % LinkQueue->size]; +} + +gceSTATUS +gckQUEUE_Allocate( + IN gckOS Os, + IN gckQUEUE Queue, + IN gctUINT32 Size + ) +{ + gceSTATUS status; + + gcmkONERROR(gckOS_Allocate( + Os, + gcmSIZEOF(struct _gckLINKDATA) * Size, + (gctPOINTER *)&Queue->datas + )); + + Queue->size = Size; + + return gcvSTATUS_OK; + +OnError: + return status; +} + +gceSTATUS +gckQUEUE_Free( + IN gckOS Os, + IN gckQUEUE Queue + ) +{ + if (Queue->datas) + { + gcmkVERIFY_OK(gckOS_Free(Os, (gctPOINTER)Queue->datas)); + } + + return gcvSTATUS_OK; +} + +/******************************************************************************\ +*************************** Pointer - ID translation *************************** +\******************************************************************************/ +#define gcdID_TABLE_LENGTH 1024 +typedef struct _gcsINTEGERDB * gckINTEGERDB; +typedef struct _gcsINTEGERDB +{ + gckOS os; + gctPOINTER* table; + gctPOINTER mutex; + gctUINT32 tableLen; + gctUINT32 currentID; + gctUINT32 unused; +} +gcsINTEGERDB; + +gceSTATUS +gckKERNEL_CreateIntegerDatabase( + IN gckKERNEL Kernel, + OUT gctPOINTER * Database + ) +{ + gceSTATUS status; + gckINTEGERDB database = gcvNULL; + + gcmkHEADER_ARG("Kernel=0x%08X Datbase=0x%08X", Kernel, Database); + + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Database != gcvNULL); + + /* Allocate a database. */ + gcmkONERROR(gckOS_Allocate( + Kernel->os, gcmSIZEOF(gcsINTEGERDB), (gctPOINTER *)&database)); + + gcmkONERROR(gckOS_ZeroMemory(database, gcmSIZEOF(gcsINTEGERDB))); + + /* Allocate a pointer table. */ + gcmkONERROR(gckOS_Allocate( + Kernel->os, gcmSIZEOF(gctPOINTER) * gcdID_TABLE_LENGTH, (gctPOINTER *)&database->table)); + + gcmkONERROR(gckOS_ZeroMemory(database->table, gcmSIZEOF(gctPOINTER) * gcdID_TABLE_LENGTH)); + + /* Allocate a database mutex. */ + gcmkONERROR(gckOS_CreateMutex(Kernel->os, &database->mutex)); + + /* Initialize. */ + database->currentID = 0; + database->unused = gcdID_TABLE_LENGTH; + database->os = Kernel->os; + database->tableLen = gcdID_TABLE_LENGTH; + + *Database = database; + + gcmkFOOTER_ARG("*Database=0x%08X", *Database); + return gcvSTATUS_OK; + +OnError: + /* Rollback. */ + if (database) + { + if (database->table) + { + gcmkOS_SAFE_FREE(Kernel->os, database->table); + } + + gcmkOS_SAFE_FREE(Kernel->os, database); + } + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_DestroyIntegerDatabase( + IN gckKERNEL Kernel, + IN gctPOINTER Database + ) +{ + gckINTEGERDB database = Database; + + gcmkHEADER_ARG("Kernel=0x%08X Datbase=0x%08X", Kernel, Database); + + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Database != gcvNULL); + + /* Destroy pointer table. */ + gcmkOS_SAFE_FREE(Kernel->os, database->table); + + /* Destroy database mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, database->mutex)); + + /* Destroy database. */ + gcmkOS_SAFE_FREE(Kernel->os, database); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckKERNEL_AllocateIntegerId( + IN gctPOINTER Database, + IN gctPOINTER Pointer, + OUT gctUINT32 * Id + ) +{ + gceSTATUS status; + gckINTEGERDB database = Database; + gctUINT32 i, unused, currentID, tableLen; + gctPOINTER * table; + gckOS os = database->os; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Database=0x%08X Pointer=0x%08X", Database, Pointer); + + gcmkVERIFY_ARGUMENT(Id != gcvNULL); + + gcmkVERIFY_OK(gckOS_AcquireMutex(os, database->mutex, gcvINFINITE)); + acquired = gcvTRUE; + + if (database->unused < 1) + { + /* Extend table. */ + gcmkONERROR( + gckOS_Allocate(os, + gcmSIZEOF(gctPOINTER) * (database->tableLen + gcdID_TABLE_LENGTH), + (gctPOINTER *)&table)); + + gcmkONERROR(gckOS_ZeroMemory(table + database->tableLen, + gcmSIZEOF(gctPOINTER) * gcdID_TABLE_LENGTH)); + + /* Copy data from old table. */ + gckOS_MemCopy(table, + database->table, + database->tableLen * gcmSIZEOF(gctPOINTER)); + + gcmkOS_SAFE_FREE(os, database->table); + + /* Update databse with new allocated table. */ + database->table = table; + database->currentID = database->tableLen; + database->tableLen += gcdID_TABLE_LENGTH; + database->unused += gcdID_TABLE_LENGTH; + } + + table = database->table; + currentID = database->currentID; + tableLen = database->tableLen; + unused = database->unused; + + /* Connect id with pointer. */ + table[currentID] = Pointer; + + *Id = currentID + 1; + + /* Update the currentID. */ + if (--unused > 0) + { + for (i = 0; i < tableLen; i++) + { + if (++currentID >= tableLen) + { + /* Wrap to the begin. */ + currentID = 0; + } + + if (table[currentID] == gcvNULL) + { + break; + } + } + } + + database->table = table; + database->currentID = currentID; + database->tableLen = tableLen; + database->unused = unused; + + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, database->mutex)); + acquired = gcvFALSE; + + gcmkFOOTER_ARG("*Id=%d", *Id); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, database->mutex)); + } + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_FreeIntegerId( + IN gctPOINTER Database, + IN gctUINT32 Id + ) +{ + gceSTATUS status; + gckINTEGERDB database = Database; + gckOS os = database->os; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Database=0x%08X Id=%d", Database, Id); + + gcmkVERIFY_OK(gckOS_AcquireMutex(os, database->mutex, gcvINFINITE)); + acquired = gcvTRUE; + + if (!(Id > 0 && Id <= database->tableLen)) + { + gcmkONERROR(gcvSTATUS_NOT_FOUND); + } + + Id -= 1; + + database->table[Id] = gcvNULL; + + if (database->unused++ == 0) + { + database->currentID = Id; + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, database->mutex)); + acquired = gcvFALSE; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, database->mutex)); + } + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_QueryIntegerId( + IN gctPOINTER Database, + IN gctUINT32 Id, + OUT gctPOINTER * Pointer + ) +{ + gceSTATUS status; + gckINTEGERDB database = Database; + gctPOINTER pointer; + gckOS os = database->os; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Database=0x%08X Id=%d", Database, Id); + gcmkVERIFY_ARGUMENT(Pointer != gcvNULL); + + gcmkVERIFY_OK(gckOS_AcquireMutex(os, database->mutex, gcvINFINITE)); + acquired = gcvTRUE; + + if (!(Id > 0 && Id <= database->tableLen)) + { + gcmkONERROR(gcvSTATUS_NOT_FOUND); + } + + Id -= 1; + + pointer = database->table[Id]; + + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, database->mutex)); + acquired = gcvFALSE; + + if (pointer) + { + *Pointer = pointer; + } + else + { + gcmkONERROR(gcvSTATUS_NOT_FOUND); + } + + gcmkFOOTER_ARG("*Pointer=0x%08X", *Pointer); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, database->mutex)); + } + + gcmkFOOTER(); + return status; +} + + +gctUINT32 +gckKERNEL_AllocateNameFromPointer( + IN gckKERNEL Kernel, + IN gctPOINTER Pointer + ) +{ + gceSTATUS status; + gctUINT32 name; + gctPOINTER database = Kernel->db->pointerDatabase; + + gcmkHEADER_ARG("Kernel=0x%X Pointer=0x%X", Kernel, Pointer); + + gcmkONERROR( + gckKERNEL_AllocateIntegerId(database, Pointer, &name)); + + gcmkFOOTER_ARG("name=%d", name); + return name; + +OnError: + gcmkFOOTER(); + return 0; +} + +gctPOINTER +gckKERNEL_QueryPointerFromName( + IN gckKERNEL Kernel, + IN gctUINT32 Name + ) +{ + gceSTATUS status; + gctPOINTER pointer = gcvNULL; + gctPOINTER database = Kernel->db->pointerDatabase; + + gcmkHEADER_ARG("Kernel=0x%X Name=%d", Kernel, Name); + + /* Lookup in database to get pointer. */ + gcmkONERROR(gckKERNEL_QueryIntegerId(database, Name, &pointer)); + + gcmkFOOTER_ARG("pointer=0x%X", pointer); + return pointer; + +OnError: + gcmkFOOTER(); + return gcvNULL; +} + +gceSTATUS +gckKERNEL_DeleteName( + IN gckKERNEL Kernel, + IN gctUINT32 Name + ) +{ + gctPOINTER database = Kernel->db->pointerDatabase; + + gcmkHEADER_ARG("Kernel=0x%X Name=0x%X", Kernel, Name); + + /* Free name if exists. */ + gcmkVERIFY_OK(gckKERNEL_FreeIntegerId(database, Name)); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +***** Shared Buffer ************************************************************ +*******************************************************************************/ + +/******************************************************************************* +** +** gckKERNEL_CreateShBuffer +** +** Create shared buffer. +** The shared buffer can be used across processes. Other process needs call +** gckKERNEL_MapShBuffer before use it. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctUINT32 Size +** Specify the shared buffer size. +** +** OUTPUT: +** +** gctSHBUF * ShBuf +** Pointer to hold return shared buffer handle. +*/ +gceSTATUS +gckKERNEL_CreateShBuffer( + IN gckKERNEL Kernel, + IN gctUINT32 Size, + OUT gctSHBUF * ShBuf + ) +{ + gceSTATUS status; + gcsSHBUF_PTR shBuf = gcvNULL; + + gcmkHEADER_ARG("Kernel=0x%X, Size=%u", Kernel, Size); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + if (Size == 0) + { + /* Invalid size. */ + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + else if (Size > 1024) + { + /* Limite shared buffer size. */ + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + /* Create a shared buffer structure. */ + gcmkONERROR( + gckOS_Allocate(Kernel->os, + sizeof (gcsSHBUF), + (gctPOINTER *)&shBuf)); + + /* Initialize shared buffer. */ + shBuf->id = 0; + shBuf->reference = gcvNULL; + shBuf->size = Size; + shBuf->data = gcvNULL; + + /* Allocate integer id for this shared buffer. */ + gcmkONERROR( + gckKERNEL_AllocateIntegerId(Kernel->db->pointerDatabase, + shBuf, + &shBuf->id)); + + /* Allocate atom. */ + gcmkONERROR(gckOS_AtomConstruct(Kernel->os, &shBuf->reference)); + + /* Set default reference count to 1. */ + gcmkVERIFY_OK(gckOS_AtomSet(Kernel->os, shBuf->reference, 1)); + + /* Return integer id. */ + *ShBuf = (gctSHBUF)(gctUINTPTR_T)shBuf->id; + + gcmkFOOTER_ARG("*ShBuf=%u", shBuf->id); + return gcvSTATUS_OK; + +OnError: + /* Error roll back. */ + if (shBuf != gcvNULL) + { + if (shBuf->id != 0) + { + gcmkVERIFY_OK( + gckKERNEL_FreeIntegerId(Kernel->db->pointerDatabase, + shBuf->id)); + } + + gcmkOS_SAFE_FREE(Kernel->os, shBuf); + } + + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_DestroyShBuffer +** +** Destroy shared buffer. +** This will decrease reference of specified shared buffer and do actual +** destroy when no reference on it. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctSHBUF ShBuf +** Specify the shared buffer to be destroyed. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_DestroyShBuffer( + IN gckKERNEL Kernel, + IN gctSHBUF ShBuf + ) +{ + gceSTATUS status; + gcsSHBUF_PTR shBuf; + gctINT32 oldValue = 0; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Kernel=0x%X ShBuf=%u", + Kernel, (gctUINT32)(gctUINTPTR_T) ShBuf); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(ShBuf != gcvNULL); + + /* Acquire mutex. */ + gcmkONERROR( + gckOS_AcquireMutex(Kernel->os, + Kernel->db->pointerDatabaseMutex, + gcvINFINITE)); + acquired = gcvTRUE; + + /* Find shared buffer structure. */ + gcmkONERROR( + gckKERNEL_QueryIntegerId(Kernel->db->pointerDatabase, + (gctUINT32)(gctUINTPTR_T)ShBuf, + (gctPOINTER)&shBuf)); + + gcmkASSERT(shBuf->id == (gctUINT32)(gctUINTPTR_T)ShBuf); + + /* Decrease the reference count. */ + gckOS_AtomDecrement(Kernel->os, shBuf->reference, &oldValue); + + if (oldValue == 1) + { + /* Free integer id. */ + gcmkVERIFY_OK( + gckKERNEL_FreeIntegerId(Kernel->db->pointerDatabase, + shBuf->id)); + + /* Free atom. */ + gcmkVERIFY_OK(gckOS_AtomDestroy(Kernel->os, shBuf->reference)); + + if (shBuf->data) + { + gcmkOS_SAFE_FREE(Kernel->os, shBuf->data); + shBuf->data = gcvNULL; + } + + /* Free the shared buffer. */ + gcmkOS_SAFE_FREE(Kernel->os, shBuf); + } + + /* Release the mutex. */ + gcmkVERIFY_OK( + gckOS_ReleaseMutex(Kernel->os, Kernel->db->pointerDatabaseMutex)); + acquired = gcvFALSE; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the mutex. */ + gcmkVERIFY_OK( + gckOS_ReleaseMutex(Kernel->os, Kernel->db->pointerDatabaseMutex)); + } + + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_MapShBuffer +** +** Map shared buffer into this process so that it can be used in this process. +** This will increase reference count on the specified shared buffer. +** Call gckKERNEL_DestroyShBuffer to dereference. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctSHBUF ShBuf +** Specify the shared buffer to be mapped. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_MapShBuffer( + IN gckKERNEL Kernel, + IN gctSHBUF ShBuf + ) +{ + gceSTATUS status; + gcsSHBUF_PTR shBuf; + gctINT32 oldValue = 0; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Kernel=0x%X ShBuf=%u", + Kernel, (gctUINT32)(gctUINTPTR_T) ShBuf); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(ShBuf != gcvNULL); + + /* Acquire mutex. */ + gcmkONERROR( + gckOS_AcquireMutex(Kernel->os, + Kernel->db->pointerDatabaseMutex, + gcvINFINITE)); + acquired = gcvTRUE; + + /* Find shared buffer structure. */ + gcmkONERROR( + gckKERNEL_QueryIntegerId(Kernel->db->pointerDatabase, + (gctUINT32)(gctUINTPTR_T)ShBuf, + (gctPOINTER)&shBuf)); + + gcmkASSERT(shBuf->id == (gctUINT32)(gctUINTPTR_T)ShBuf); + + /* Increase the reference count. */ + gckOS_AtomIncrement(Kernel->os, shBuf->reference, &oldValue); + + /* Release the mutex. */ + gcmkVERIFY_OK( + gckOS_ReleaseMutex(Kernel->os, Kernel->db->pointerDatabaseMutex)); + acquired = gcvFALSE; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the mutex. */ + gcmkVERIFY_OK( + gckOS_ReleaseMutex(Kernel->os, Kernel->db->pointerDatabaseMutex)); + } + + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_WriteShBuffer +** +** Write user data into shared buffer. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctSHBUF ShBuf +** Specify the shared buffer to be written to. +** +** gctPOINTER UserData +** User mode pointer to hold the source data. +** +** gctUINT32 ByteCount +** Specify number of bytes to write. If this is larger than +** shared buffer size, gcvSTATUS_INVALID_ARGUMENT is returned. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_WriteShBuffer( + IN gckKERNEL Kernel, + IN gctSHBUF ShBuf, + IN gctPOINTER UserData, + IN gctUINT32 ByteCount + ) +{ + gceSTATUS status; + gcsSHBUF_PTR shBuf = gcvNULL; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Kernel=0x%X ShBuf=%u UserData=0x%X ByteCount=%u", + Kernel, (gctUINT32)(gctUINTPTR_T) ShBuf, UserData, ByteCount); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(ShBuf != gcvNULL); + + /* Acquire mutex. */ + gcmkONERROR( + gckOS_AcquireMutex(Kernel->os, + Kernel->db->pointerDatabaseMutex, + gcvINFINITE)); + acquired = gcvTRUE; + + /* Find shared buffer structure. */ + gcmkONERROR( + gckKERNEL_QueryIntegerId(Kernel->db->pointerDatabase, + (gctUINT32)(gctUINTPTR_T)ShBuf, + (gctPOINTER)&shBuf)); + + gcmkASSERT(shBuf->id == (gctUINT32)(gctUINTPTR_T)ShBuf); + + if ((ByteCount > shBuf->size) || + (ByteCount == 0) || + (UserData == gcvNULL)) + { + /* Exceeds buffer max size or invalid. */ + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + if (shBuf->data == gcvNULL) + { + /* Allocate buffer data when first time write. */ + gcmkONERROR(gckOS_Allocate(Kernel->os, ByteCount, &shBuf->data)); + } + + /* Copy data from user. */ + gcmkONERROR( + gckOS_CopyFromUserData(Kernel->os, + shBuf->data, + UserData, + ByteCount)); + + /* Release the mutex. */ + gcmkVERIFY_OK( + gckOS_ReleaseMutex(Kernel->os, Kernel->db->pointerDatabaseMutex)); + acquired = gcvFALSE; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (shBuf && shBuf->data) + { + gcmkOS_SAFE_FREE(Kernel->os, shBuf->data); + shBuf->data = gcvNULL; + } + + if (acquired) + { + /* Release the mutex. */ + gcmkVERIFY_OK( + gckOS_ReleaseMutex(Kernel->os, Kernel->db->pointerDatabaseMutex)); + } + + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_ReadShBuffer +** +** Read data from shared buffer and copy to user pointer. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctSHBUF ShBuf +** Specify the shared buffer to be read from. +** +** gctPOINTER UserData +** User mode pointer to save output data. +** +** gctUINT32 ByteCount +** Specify number of bytes to read. +** If this is larger than shared buffer size, only avaiable bytes are +** copied. If smaller, copy requested size. +** +** OUTPUT: +** +** gctUINT32 * BytesRead +** Pointer to hold how many bytes actually read from shared buffer. +*/ +gceSTATUS +gckKERNEL_ReadShBuffer( + IN gckKERNEL Kernel, + IN gctSHBUF ShBuf, + IN gctPOINTER UserData, + IN gctUINT32 ByteCount, + OUT gctUINT32 * BytesRead + ) +{ + gceSTATUS status; + gcsSHBUF_PTR shBuf; + gctUINT32 bytes; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Kernel=0x%X ShBuf=%u UserData=0x%X ByteCount=%u", + Kernel, (gctUINT32)(gctUINTPTR_T) ShBuf, UserData, ByteCount); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(ShBuf != gcvNULL); + + /* Acquire mutex. */ + gcmkONERROR( + gckOS_AcquireMutex(Kernel->os, + Kernel->db->pointerDatabaseMutex, + gcvINFINITE)); + acquired = gcvTRUE; + + /* Find shared buffer structure. */ + gcmkONERROR( + gckKERNEL_QueryIntegerId(Kernel->db->pointerDatabase, + (gctUINT32)(gctUINTPTR_T)ShBuf, + (gctPOINTER)&shBuf)); + + gcmkASSERT(shBuf->id == (gctUINT32)(gctUINTPTR_T)ShBuf); + + if (shBuf->data == gcvNULL) + { + *BytesRead = 0; + + /* No data in shared buffer, skip copy. */ + status = gcvSTATUS_SKIP; + goto OnError; + } + else if (ByteCount == 0) + { + /* Invalid size to read. */ + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + /* Determine bytes to copy. */ + bytes = (ByteCount < shBuf->size) ? ByteCount : shBuf->size; + + /* Copy data to user. */ + gcmkONERROR( + gckOS_CopyToUserData(Kernel->os, + shBuf->data, + UserData, + bytes)); + + /* Return copied size. */ + *BytesRead = bytes; + + /* Release the mutex. */ + gcmkVERIFY_OK( + gckOS_ReleaseMutex(Kernel->os, Kernel->db->pointerDatabaseMutex)); + acquired = gcvFALSE; + + gcmkFOOTER_ARG("*BytesRead=%u", bytes); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the mutex. */ + gcmkVERIFY_OK( + gckOS_ReleaseMutex(Kernel->os, Kernel->db->pointerDatabaseMutex)); + } + + gcmkFOOTER(); + return status; +} + +/*******************************************************************************\ +*************************** List Helper ***************************************** +\*******************************************************************************/ + +static void +_ListAdd( + gcsLISTHEAD_PTR New, + gcsLISTHEAD_PTR Prev, + gcsLISTHEAD_PTR Next + ) +{ + Next->prev = New; + New->next = Next; + New->prev = Prev; + Prev->next = New; +} + +void +_ListDel( + gcsLISTHEAD_PTR Prev, + gcsLISTHEAD_PTR Next + ) +{ + Next->prev = Prev; + Prev->next = Next; +} + +void +gcsLIST_Init( + gcsLISTHEAD_PTR Node + ) +{ + Node->prev = Node; + Node->next = Node; +} + +void +gcsLIST_Add( + gcsLISTHEAD_PTR New, + gcsLISTHEAD_PTR Head + ) +{ + _ListAdd(New, Head, Head->next); +} + +void +gcsLIST_AddTail( + gcsLISTHEAD_PTR New, + gcsLISTHEAD_PTR Head + ) +{ + _ListAdd(New, Head->prev, Head); +} + +void +gcsLIST_Del( + gcsLISTHEAD_PTR Node + ) +{ + _ListDel(Node->prev, Node->next); +} + +gctBOOL +gcsLIST_Empty( + gcsLISTHEAD_PTR Head + ) +{ + return Head->next == Head; +} + +/*******************************************************************************\ +********************************* Fence ***************************************** +\*******************************************************************************/ + +gceSTATUS +gckFENCE_Create( + IN gckOS Os, + IN gckKERNEL Kernel, + OUT gckFENCE * Fence + ) +{ + gceSTATUS status; + gckFENCE fence = gcvNULL; + gctSIZE_T pageSize = 4096; + + gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(gcsFENCE), (gctPOINTER *)&fence)); + gcmkONERROR(gckOS_ZeroMemory(fence, gcmSIZEOF(gcsFENCE))); + gcmkONERROR(gckOS_CreateMutex(Os, (gctPOINTER *)&fence->mutex)); + + fence->kernel = Kernel; + +#if USE_KERNEL_VIRTUAL_BUFFERS + if (Kernel->virtualCommandBuffer) + { + gcmkONERROR(gckKERNEL_AllocateVirtualMemory( + Kernel, + gcvFALSE, + gcvFALSE, + &pageSize, + &fence->physical, + &fence->logical + )); + + gcmkONERROR(gckKERNEL_GetGPUAddress( + Kernel, + fence->logical, + gcvFALSE, + fence->physical, + &fence->address + )); + } + else +#endif + { + gcmkONERROR(gckOS_AllocateNonPagedMemory( + Os, + gcvFALSE, + &pageSize, + &fence->physical, + &fence->logical + )); + + gcmkONERROR(gckHARDWARE_ConvertLogical( + Kernel->hardware, + fence->logical, + gcvFALSE, + &fence->address + )); + + gcmkONERROR(gckMMU_FillFlatMapping( + Kernel->mmu, fence->address, pageSize + )); + } + + gcsLIST_Init(&fence->waitingList); + + *Fence = fence; + + return gcvSTATUS_OK; +OnError: + if (fence) + { + gckFENCE_Destory(Os, fence); + } + + return status; +} + +gceSTATUS +gckFENCE_Destory( + IN gckOS Os, + OUT gckFENCE Fence + ) +{ + if (Fence->mutex) + { + gcmkVERIFY_OK(gckOS_DeleteMutex(Os, Fence->mutex)); + } + + if (Fence->logical) + { +#if USE_KERNEL_VIRTUAL_BUFFERS + if (Fence->kernel->virtualCommandBuffer) + { + gcmkVERIFY_OK(gckKERNEL_FreeVirtualMemory( + Fence->physical, + Fence->logical, + gcvFALSE + )); + } + else +#endif + { + gcmkVERIFY_OK(gckOS_FreeNonPagedMemory( + Os, + 4096, + Fence->physical, + Fence->logical + )); + } + } + + gcmkOS_SAFE_FREE(Os, Fence); + + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckFENCE_Signal +** +** Signal all completed nodes. +** +** +*/ +gceSTATUS +gckFENCE_Signal( + IN gckOS Os, + IN gckFENCE Fence + ) +{ + gcsLISTHEAD_PTR list = &Fence->waitingList; + gcsLISTHEAD_PTR nodeHead, nodeTemp; + gckFENCE_SYNC sync; + gckOS os = Os; + gctUINT64 stamp = *(gctUINT64 *)Fence->logical; + + gcmkVERIFY_OK(gckOS_AcquireMutex(os, Fence->mutex, gcvINFINITE)); + + gcmkLIST_FOR_EACH_SAFE(nodeHead, nodeTemp, list) + { + sync = gcmCONTAINEROF(nodeHead, _gcsFENCE_SYNC, head); + + /* Signal all nodes which are complete. */ + if (sync->commitStamp <= stamp && sync->inList) + { + /* Signal. */ + gckOS_Signal(os, sync->signal, gcvTRUE); + + /* Remove from wait list. */ + gcsLIST_Del(nodeHead); + + /* Mark node not in waiting list. */ + sync->inList = gcvFALSE; + } + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Fence->mutex)); + + return gcvSTATUS_OK; +} + +gceSTATUS +gckDEVICE_Construct( + IN gckOS Os, + OUT gckDEVICE * Device + ) +{ + gceSTATUS status; + gckDEVICE device; + gctUINT i; + + gcmkHEADER(); + + gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(gcsDEVICE), (gctPOINTER *)&device)); + + for (i = 0; i < gcvCORE_COUNT; i++) + { + device->coreInfoArray[i].type = gcvHARDWARE_INVALID; + } + device->defaultHwType = gcvHARDWARE_INVALID; + + gckOS_ZeroMemory(device, gcmSIZEOF(gcsDEVICE)); + + gcmkONERROR(gckOS_CreateMutex(Os, &device->stuckDumpMutex)); + gcmkONERROR(gckOS_CreateMutex(Os, &device->commitMutex)); + + device->os = Os; + + *Device = device; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + + if (device != gcvNULL) + { + gckDEVICE_Destroy(Os, device); + } + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckDEVICE_AddCore( + IN gckDEVICE Device, + IN gceCORE Core, + IN gctUINT ChipID, + IN gctPOINTER Context, + IN gckKERNEL * Kernel + ) +{ + gceSTATUS status; + gcsCORE_INFO * info = Device->coreInfoArray; + gceHARDWARE_TYPE type = (gceHARDWARE_TYPE)((gctUINT)gcvHARDWARE_INVALID); + gctUINT32 index = Device->coreNum; + gctUINT32 i; + gcsCORE_LIST *coreList; + gceHARDWARE_TYPE kernelType; + gceHARDWARE_TYPE defaultHwType; + gckKERNEL kernel; + + gcmkASSERT(Device->coreNum < gcvCORE_COUNT); + + if (Core >= gcvCORE_MAJOR && Core <= gcvCORE_3D_MAX) + { + /* Chip ID is only used for 3D cores. */ + if (ChipID == gcvCHIP_ID_DEFAULT) + { + /* Apply default chipID if it is not set. */ + ChipID = Core; + } + } + + /* Construct gckKERNEL for this core. */ + gcmkONERROR(gckKERNEL_Construct( + Device->os, Core, ChipID, Context, Device, Device->database, Kernel)); + + kernel = *Kernel; + + if (Device->database == gcvNULL) + { + Device->database = kernel->db; + } + + kernelType = _GetHardwareType(kernel); + + if (kernelType >= gcvHARDWARE_NUM_TYPES) + { + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + info[index].type = kernelType; + info[index].core = Core; + info[index].kernel = kernel; + info[index].chipID = ChipID; + + if (index == 0) + { + /* First core, map all type/core to it. */ + for (; type != gcvHARDWARE_NUM_TYPES; type = (gceHARDWARE_TYPE)((gctUINT)type + 1)) + { + Device->map[type].num = 0; + + for (i = 0 ; i < 4; i++) + { + Device->map[type].kernels[i] = kernel; + } + } + } + + /* Get core list of this type. */ + coreList = &Device->map[kernelType]; + + /* Setup gceHARDWARE_TYPE to gceCORE mapping. */ + coreList->kernels[coreList->num++] = kernel; + + defaultHwType = kernelType; + if (kernelType == gcvHARDWARE_3D2D) + { + coreList = &Device->map[gcvHARDWARE_3D]; + coreList->kernels[coreList->num++] = kernel; + defaultHwType = gcvHARDWARE_3D; + } + + /* Advance total core number. */ + Device->coreNum++; + + /* Default HW type was chosen: 3D > 2D > VG */ + if (Device->defaultHwType == gcvHARDWARE_INVALID) + { + Device->defaultHwType = defaultHwType; + } + else if (Device->defaultHwType > defaultHwType) + { + Device->defaultHwType = defaultHwType; + } + + return gcvSTATUS_OK; + +OnError: + return status; +} + +gceSTATUS +gckDEVICE_ChipInfo( + IN gckDEVICE Device, + IN gcsHAL_INTERFACE_PTR Interface + ) +{ + gctUINT i; + gcsCORE_INFO * info = Device->coreInfoArray; + + for (i = 0; i < Device->coreNum; i++) + { + Interface->u.ChipInfo.types[i] = info[i].type; + Interface->u.ChipInfo.ids[i] = info[i].chipID; + } + + Interface->u.ChipInfo.count = Device->coreNum; + + return gcvSTATUS_OK; +} + +gceSTATUS +gckDEVICE_Version( + IN gckDEVICE Device, + IN gcsHAL_INTERFACE_PTR Interface + ) +{ + Interface->u.Version.major = gcvVERSION_MAJOR; + Interface->u.Version.minor = gcvVERSION_MINOR; + Interface->u.Version.patch = gcvVERSION_PATCH; + Interface->u.Version.build = gcvVERSION_BUILD; +#if gcmIS_DEBUG(gcdDEBUG_TRACE) + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_KERNEL, + "KERNEL version %d.%d.%d build %u", + gcvVERSION_MAJOR, gcvVERSION_MINOR, + gcvVERSION_PATCH, gcvVERSION_BUILD); +#endif + + return gcvSTATUS_OK; +} + +gceSTATUS +gckDEVICE_Destroy( + IN gckOS Os, + IN gckDEVICE Device + ) +{ + gctINT i; + gcsCORE_INFO * info = Device->coreInfoArray; + + for (i = Device->coreNum - 1; i >= 0 ; i--) + { + if (info[i].kernel != gcvNULL) + { + gckKERNEL_Destroy(info[i].kernel); + } + } + + if (Device->commitMutex) + { + gcmkVERIFY_OK(gckOS_DeleteMutex(Os, Device->commitMutex)); + } + if (Device->stuckDumpMutex) + { + gcmkVERIFY_OK(gckOS_DeleteMutex(Os, Device->stuckDumpMutex)); + } + + gcmkOS_SAFE_FREE(Os, Device); + + return gcvSTATUS_OK; +} + +static gceSTATUS +gckDEVICE_SetTimeOut( + IN gckDEVICE Device, + IN gcsHAL_INTERFACE_PTR Interface + ) +{ +#if gcdGPU_TIMEOUT + gckKERNEL kernel; + gctUINT i; + gceHARDWARE_TYPE type = Interface->hardwareType; + gcsCORE_LIST *coreList; + + coreList = &Device->map[type]; + + for (i = 0; i < coreList->num; i++) + { + kernel = coreList->kernels[i]; + + kernel->timeOut = Interface->u.SetTimeOut.timeOut; + } +#endif + + return gcvSTATUS_OK; +} + + +gceSTATUS +gckDEVICE_Dispatch( + IN gckDEVICE Device, + IN gcsHAL_INTERFACE_PTR Interface + ) +{ + gceSTATUS status = gcvSTATUS_NOT_SUPPORTED; + gckKERNEL kernel; + gceHARDWARE_TYPE type = Interface->hardwareType; + gctUINT32 coreIndex = Interface->coreIndex; + + switch (Interface->command) + { + case gcvHAL_CHIP_INFO: + status = gckDEVICE_ChipInfo(Device, Interface); + break; + + case gcvHAL_VERSION: + status = gckDEVICE_Version(Device, Interface); + break; + + case gcvHAL_SET_TIMEOUT: + status = gckDEVICE_SetTimeOut(Device, Interface); + break; + + default: + status = gcvSTATUS_NOT_SUPPORTED; + break; + } + + if (gcmIS_SUCCESS(status)) + { + /* Dispatch handled in this layer. */ + Interface->status = status; + } + else + { + /* Need go through gckKERNEL dispatch. */ + kernel = Device->map[type].kernels[coreIndex]; + + +#if gcdENABLE_VG + if (kernel->vg) + { + status = gckVGKERNEL_Dispatch(kernel, gcvTRUE, Interface); + } + else +#endif + { + status = gckKERNEL_Dispatch(kernel, Device, gcvTRUE, Interface); + } + + /* Interface->status is handled in gckKERNEL_Dispatch(). */ + } + + return status; +} + +gceSTATUS +gckDEVICE_GetMMU( + IN gckDEVICE Device, + IN gceHARDWARE_TYPE Type, + IN gckMMU *Mmu + ) +{ + gcmkHEADER(); + gcmkVERIFY_ARGUMENT(Type < gcvHARDWARE_NUM_TYPES); + + *Mmu = Device->mmus[Type]; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckDEVICE_SetMMU( + IN gckDEVICE Device, + IN gceHARDWARE_TYPE Type, + IN gckMMU Mmu + ) +{ + gcmkHEADER(); + gcmkVERIFY_ARGUMENT(Type < gcvHARDWARE_NUM_TYPES); + + Device->mmus[Type] = Mmu; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckDEVICE_QueryGPUAddress +** +** Search GPUAddress in other core's address space, whose type is same as current +** core. It is used to find correct command buffer which is shared by mulitple +** core. +** +*/ +gceSTATUS +gckDEVICE_QueryGPUAddress( + IN gckDEVICE Device, + IN gckKERNEL Kernel, + IN gctUINT32 GPUAddress, + OUT gckVIRTUAL_COMMAND_BUFFER_PTR * Buffer + ) +{ + gceSTATUS status = gcvSTATUS_NOT_FOUND; + gctUINT i; + gceHARDWARE_TYPE kernelType; + + kernelType = _GetHardwareType(Kernel); + + if (Device != gcvNULL) + { + for (i = 0; i < Device->coreNum; i++) + { + if (Device->coreInfoArray[i].type == kernelType) + { + /* Search other core's command buffer list whose type is same. */ + status = gckKERNEL_QueryGPUAddress( + Device->coreInfoArray[i].kernel, GPUAddress, Buffer); + + if (gcmIS_SUCCESS(status)) + { + break; + } + } + } + } + else + { + status = gckKERNEL_QueryGPUAddress(Kernel, GPUAddress, Buffer); + } + + return status; +} + +#if gcdENABLE_TRUST_APPLICATION +gceSTATUS +gckKERNEL_MapInTrustApplicaiton( + IN gckKERNEL Kernel, + IN gctPOINTER Logical, + IN gctPHYS_ADDR Physical, + IN gctUINT32 GPUAddress, + IN gctSIZE_T PageCount + ) +{ + gceSTATUS status; + gctUINT32 * physicalArrayLogical = gcvNULL; + gctSIZE_T bytes; + gctPOINTER logical = Logical; + gctUINT32 i; + gctSIZE_T pageSize; + gctUINT32 pageMask; + + gcmkHEADER(); + + gcmkVERIFY_OK(gckOS_GetPageSize(Kernel->os, &pageSize)); + + pageMask = (gctUINT32)pageSize - 1; + + bytes = PageCount * gcmSIZEOF(gctUINT32); + + gcmkONERROR(gckOS_Allocate( + Kernel->os, + bytes, + (gctPOINTER *)&physicalArrayLogical + )); + + /* Fill in physical array. */ + for (i = 0; i < PageCount; i++) + { + gctPHYS_ADDR_T phys; + status = gckOS_PhysicalToPhysicalAddress( + Kernel->os, + Physical, + i * 4096, + &phys + ); + + if (status == gcvSTATUS_NOT_SUPPORTED) + { + gcmkONERROR(gckOS_GetPhysicalAddress( + Kernel->os, + logical, + &phys + )); + } + + phys &= ~pageMask; + + gcmkSAFECASTPHYSADDRT(physicalArrayLogical[i], phys); + + logical = (gctUINT8_PTR)logical + 4096; + } + + gcmkONERROR(gckKERNEL_SecurityMapMemory( + Kernel, + physicalArrayLogical, + 0, + (gctUINT32)PageCount, + &GPUAddress + )); + + gcmkVERIFY_OK(gckOS_Free( + Kernel->os, + physicalArrayLogical + )) + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if(physicalArrayLogical != gcvNULL) + gcmkVERIFY_OK(gckOS_Free( + Kernel->os, + (gctPOINTER)physicalArrayLogical + )); + gcmkFOOTER(); + return status; +} +#endif + +/******************************************************************************* +***** Test Code **************************************************************** +*******************************************************************************/ + |