| /**************************************************************************** |
| * |
| * The MIT License (MIT) |
| * |
| * Copyright (c) 2014 - 2020 Vivante Corporation |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| * DEALINGS IN THE SOFTWARE. |
| * |
| ***************************************************************************** |
| * |
| * The GPL License (GPL) |
| * |
| * Copyright (C) 2014 - 2020 Vivante Corporation |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version 2 |
| * of the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| * |
| ***************************************************************************** |
| * |
| * Note: This software is released under dual MIT and GPL licenses. A |
| * recipient may use this file under the terms of either the MIT license or |
| * GPL License. If you wish to use only one license not the other, you can |
| * indicate your decision by deleting one of the above license notices in your |
| * version of this file. |
| * |
| *****************************************************************************/ |
| |
| |
| #include "gc_hal_kernel_precomp.h" |
| |
| #if gcdDEC_ENABLE_AHB |
| #include "viv_dec300_main.h" |
| #endif |
| |
| #if gcdCAPTURE_ONLY_MODE |
| #include "arch/gc_hal_kernel_context.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_CHIP_INFO), |
| gcmDEFINE2TEXT(gcvHAL_VERSION), |
| gcmDEFINE2TEXT(gcvHAL_SET_TIMEOUT), |
| gcmDEFINE2TEXT(gcvHAL_QUERY_VIDEO_MEMORY), |
| gcmDEFINE2TEXT(gcvHAL_QUERY_CHIP_IDENTITY), |
| gcmDEFINE2TEXT(gcvHAL_QUERY_CHIP_OPTION), |
| gcmDEFINE2TEXT(gcvHAL_QUERY_CHIP_FREQUENCY), |
| gcmDEFINE2TEXT(gcvHAL_ALLOCATE_NON_PAGED_MEMORY), |
| gcmDEFINE2TEXT(gcvHAL_FREE_NON_PAGED_MEMORY), |
| gcmDEFINE2TEXT(gcvHAL_ALLOCATE_LINEAR_VIDEO_MEMORY), |
| gcmDEFINE2TEXT(gcvHAL_WRAP_USER_MEMORY), |
| gcmDEFINE2TEXT(gcvHAL_RELEASE_VIDEO_MEMORY), |
| gcmDEFINE2TEXT(gcvHAL_LOCK_VIDEO_MEMORY), |
| gcmDEFINE2TEXT(gcvHAL_UNLOCK_VIDEO_MEMORY), |
| gcmDEFINE2TEXT(gcvHAL_BOTTOM_HALF_UNLOCK_VIDEO_MEMORY), |
| gcmDEFINE2TEXT(gcvHAL_EXPORT_VIDEO_MEMORY), |
| gcmDEFINE2TEXT(gcvHAL_NAME_VIDEO_MEMORY), |
| gcmDEFINE2TEXT(gcvHAL_IMPORT_VIDEO_MEMORY), |
| gcmDEFINE2TEXT(gcvHAL_MAP_MEMORY), |
| gcmDEFINE2TEXT(gcvHAL_UNMAP_MEMORY), |
| gcmDEFINE2TEXT(gcvHAL_CACHE), |
| gcmDEFINE2TEXT(gcvHAL_ATTACH), |
| gcmDEFINE2TEXT(gcvHAL_DETACH), |
| gcmDEFINE2TEXT(gcvHAL_EVENT_COMMIT), |
| gcmDEFINE2TEXT(gcvHAL_COMMIT), |
| gcmDEFINE2TEXT(gcvHAL_COMMIT_DONE), |
| gcmDEFINE2TEXT(gcvHAL_USER_SIGNAL), |
| gcmDEFINE2TEXT(gcvHAL_SIGNAL), |
| gcmDEFINE2TEXT(gcvHAL_WRITE_DATA), |
| gcmDEFINE2TEXT(gcvHAL_READ_REGISTER), |
| gcmDEFINE2TEXT(gcvHAL_WRITE_REGISTER), |
| gcmDEFINE2TEXT(gcvHAL_READ_REGISTER_EX), |
| gcmDEFINE2TEXT(gcvHAL_WRITE_REGISTER_EX), |
| gcmDEFINE2TEXT(gcvHAL_GET_PROFILE_SETTING), |
| gcmDEFINE2TEXT(gcvHAL_SET_PROFILE_SETTING), |
| gcmDEFINE2TEXT(gcvHAL_READ_PROFILER_REGISTER_SETTING), |
| gcmDEFINE2TEXT(gcvHAL_READ_ALL_PROFILE_REGISTERS_PART1), |
| gcmDEFINE2TEXT(gcvHAL_READ_ALL_PROFILE_REGISTERS_PART2), |
| gcmDEFINE2TEXT(gcvHAL_PROFILE_REGISTERS_2D), |
| gcmDEFINE2TEXT(gcvHAL_SET_POWER_MANAGEMENT_STATE), |
| gcmDEFINE2TEXT(gcvHAL_QUERY_POWER_MANAGEMENT_STATE), |
| gcmDEFINE2TEXT(gcvHAL_CONFIG_POWER_MANAGEMENT), |
| gcmDEFINE2TEXT(gcvHAL_GET_BASE_ADDRESS), |
| gcmDEFINE2TEXT(gcvHAL_SET_IDLE), |
| gcmDEFINE2TEXT(gcvHAL_RESET), |
| gcmDEFINE2TEXT(gcvHAL_SET_DEBUG_LEVEL_ZONE), |
| gcmDEFINE2TEXT(gcvHAL_DEBUG_DUMP), |
| gcmDEFINE2TEXT(gcvHAL_UPDATE_DEBUG_CALLBACK), |
| gcmDEFINE2TEXT(gcvHAL_CONFIG_CTX_FRAMEWORK), |
| gcmDEFINE2TEXT(gcvHAL_DUMP_GPU_STATE), |
| gcmDEFINE2TEXT(gcvHAL_DUMP_EVENT), |
| gcmDEFINE2TEXT(gcvHAL_DUMP_GPU_PROFILE), |
| gcmDEFINE2TEXT(gcvHAL_TIMESTAMP), |
| gcmDEFINE2TEXT(gcvHAL_DATABASE), |
| gcmDEFINE2TEXT(gcvHAL_GET_FRAME_INFO), |
| gcmDEFINE2TEXT(gcvHAL_QUERY_COMMAND_BUFFER), |
| gcmDEFINE2TEXT(gcvHAL_SET_FSCALE_VALUE), |
| gcmDEFINE2TEXT(gcvHAL_GET_FSCALE_VALUE), |
| gcmDEFINE2TEXT(gcvHAL_QUERY_RESET_TIME_STAMP), |
| gcmDEFINE2TEXT(gcvHAL_CREATE_NATIVE_FENCE), |
| gcmDEFINE2TEXT(gcvHAL_WAIT_NATIVE_FENCE), |
| gcmDEFINE2TEXT(gcvHAL_SHBUF), |
| gcmDEFINE2TEXT(gcvHAL_GET_GRAPHIC_BUFFER_FD), |
| gcmDEFINE2TEXT(gcvHAL_SET_VIDEO_MEMORY_METADATA), |
| gcmDEFINE2TEXT(gcvHAL_GET_VIDEO_MEMORY_FD), |
| gcmDEFINE2TEXT(gcvHAL_DESTROY_MMU), |
| gcmDEFINE2TEXT(gcvHAL_WAIT_FENCE), |
| gcmDEFINE2TEXT(gcvHAL_DEVICE_MUTEX), |
| gcmDEFINE2TEXT(gcvHAL_DEC200_TEST), |
| gcmDEFINE2TEXT(gcvHAL_DEC300_READ), |
| gcmDEFINE2TEXT(gcvHAL_DEC300_WRITE), |
| gcmDEFINE2TEXT(gcvHAL_DEC300_FLUSH), |
| gcmDEFINE2TEXT(gcvHAL_DEC300_FLUSH_WAIT), |
| }; |
| #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 (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 |
| |
| 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)); |
| |
| /* 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 |
| |
| if (Kernel->command) |
| { |
| gcmkVERIFY_OK(gckCOMMAND_DumpExecutingBuffer(Kernel->command)); |
| } |
| } |
| |
| gceSTATUS |
| gckKERNEL_GetHardwareType( |
| IN gckKERNEL Kernel, |
| OUT gceHARDWARE_TYPE *Type |
| ) |
| { |
| gceHARDWARE_TYPE type; |
| gcmkHEADER(); |
| gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); |
| |
| { |
| type = Kernel->hardware->type; |
| } |
| |
| *Type = type; |
| |
| gcmkFOOTER_ARG("type=%d", type); |
| return gcvSTATUS_OK; |
| } |
| |
| 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; |
| gctUINT64 data; |
| gctUINT32 recovery; |
| gctUINT32 stuckDump; |
| gctUINT64 dynamicMap = 1; |
| |
| gcmkHEADER_ARG("Os=%p Context=%p", 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; |
| kernel->threadInitialized = gcvTRUE; |
| |
| #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, |
| 512, |
| &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, |
| 512, |
| &kernel->db->pointerDatabase |
| )); |
| |
| /* Initialize video memory node list. */ |
| gcsLIST_Init(&kernel->db->videoMemList); |
| |
| gcmkONERROR(gckOS_CreateMutex(Os, &kernel->db->videoMemListMutex)); |
| } |
| 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; |
| } |
| |
| gcmkONERROR(gckOS_CreateMutex(Os, &kernel->vidMemBlockMutex)); |
| |
| /* 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", &data); |
| recovery = (gctUINT32)data; |
| |
| if (gcmIS_SUCCESS(status)) |
| { |
| status = gckOS_QueryOption(Os, "stuckDump", &data); |
| stuckDump = (gctUINT32)data; |
| |
| gcmkASSERT(status == gcvSTATUS_OK); |
| |
| _SetRecovery(kernel, recovery, stuckDump); |
| } |
| |
| status = gckOS_QueryOption(Os, "sRAMLoopMode", &data); |
| kernel->sRAMLoopMode = (status == gcvSTATUS_OK) ? data : 0; |
| |
| /* Need the kernel reference before gckKERNEL_Construct() completes. |
| gckOS_MapPagesEx() is called to map kernel virtual command buffers. */ |
| *Kernel = kernel; |
| |
| { |
| /* Construct the gckHARDWARE object. */ |
| gcmkONERROR( |
| gckHARDWARE_Construct(Os, kernel->device, kernel->core, &kernel->hardware)); |
| |
| /* Set pointer to gckKERNEL object in gckHARDWARE object. */ |
| kernel->hardware->kernel = kernel; |
| |
| kernel->sRAMIndex = 0; |
| kernel->extSRAMIndex = 0; |
| |
| for (i = gcvSRAM_INTERNAL0; i < gcvSRAM_INTER_COUNT; i++) |
| { |
| kernel->sRAMVidMem[i] = kernel->hardware->sRAMVidMem[i]; |
| kernel->sRAMPhysical[i] = kernel->hardware->sRAMPhysical[i]; |
| kernel->sRAMPhysFaked[i] = gcvFALSE; |
| } |
| |
| kernel->timeOut = kernel->hardware->type == gcvHARDWARE_2D |
| ? gcdGPU_2D_TIMEOUT |
| : gcdGPU_TIMEOUT |
| ; |
| |
| #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)); |
| } |
| } |
| |
| gcmkONERROR( |
| gckMMU_SetupSRAM(kernel->mmu, kernel->hardware, kernel->device)); |
| |
| status = gckOS_QueryOption(Os, "mmuDynamicMap", &dynamicMap); |
| if (dynamicMap && kernel->hardware->mmuVersion && !kernel->mmu->dynamicAreaSetuped) |
| { |
| gcmkONERROR( |
| gckMMU_SetupDynamicSpace(kernel->mmu)); |
| |
| kernel->mmu->dynamicAreaSetuped = gcvTRUE; |
| } |
| |
| if (kernel->hardware->mmuVersion > 0) |
| { |
| /* Flush MTLB table. */ |
| gcmkONERROR(gckVIDMEM_NODE_CleanCache( |
| kernel, |
| kernel->mmu->mtlbVideoMem, |
| 0, |
| kernel->mmu->mtlbLogical, |
| kernel->mmu->mtlbSize |
| )); |
| } |
| #endif |
| |
| kernel->contiguousBaseAddress = kernel->mmu->contiguousBaseAddress; |
| kernel->externalBaseAddress = kernel->mmu->externalBaseAddress; |
| |
| /* Construct the gckCOMMAND object, either MCFE or wait-link FE can exist. */ |
| if (gckHARDWARE_IsFeatureAvailable(kernel->hardware, gcvFEATURE_MCFE)) |
| { |
| /* Construct the gckCOMMAND object for multi-channel FE. */ |
| gcmkONERROR(gckCOMMAND_Construct(kernel, gcvHW_FE_MULTI_CHANNEL, &kernel->command)); |
| |
| /* Construct gckEVENT for multi-channel FE. */ |
| gcmkONERROR(gckEVENT_Construct(kernel, kernel->command, &kernel->eventObj)); |
| } |
| else |
| { |
| /* Construct the gckCOMMAND object for legacy wait-link FE. */ |
| gcmkONERROR(gckCOMMAND_Construct(kernel, gcvHW_FE_WAIT_LINK, &kernel->command)); |
| |
| /* Construct the gckEVENT object. */ |
| gcmkONERROR(gckEVENT_Construct(kernel, kernel->command, &kernel->eventObj)); |
| } |
| |
| if (gckHARDWARE_IsFeatureAvailable(kernel->hardware, gcvFEATURE_ASYNC_BLIT)) |
| { |
| /* Construct the gckCOMMAND object for BLT engine. */ |
| gcmkONERROR(gckCOMMAND_Construct(kernel, gcvHW_FE_ASYNC, &kernel->asyncCommand)); |
| |
| /* Construct gckEVENT for BLT. */ |
| gcmkONERROR(gckEVENT_Construct(kernel, kernel->asyncCommand, &kernel->asyncEvent)); |
| } |
| |
| gcmkVERIFY_OK(gckOS_GetTime(&kernel->resetTimeStamp)); |
| |
| /* Post construct hardware elements after MMU settle. */ |
| gcmkONERROR(gckHARDWARE_PostConstruct(kernel->hardware)); |
| |
| /* Initialize the GPU. */ |
| 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 |
| if (kernel->command) |
| { |
| /* Start the command queue. */ |
| gcmkONERROR(gckCOMMAND_Start(kernel->command)); |
| } |
| |
| if (kernel->asyncCommand) |
| { |
| /* Start the async command queue. */ |
| gcmkONERROR(gckCOMMAND_Start(kernel->asyncCommand)); |
| } |
| #endif |
| } |
| |
| #if VIVANTE_PROFILER |
| /* Initialize profile setting */ |
| kernel->profileEnable = gcvFALSE; |
| kernel->profileCleanRegister = gcvTRUE; |
| #endif |
| |
| #if gcdLINUX_SYNC_FILE |
| 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=%p", *Kernel); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| gckOS_SetGPUPower(Os, kernel->core, gcvFALSE, gcvFALSE); |
| *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=%p", Kernel); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); |
| #if QNX_SINGLE_THREADED_DEBUGGING |
| gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Kernel->debugMutex)); |
| #endif |
| |
| if (Kernel->monitorTimer) |
| { |
| /* Stop and destroy monitor timer. */ |
| gcmkVERIFY_OK(gckOS_StopTimer(Kernel->os, Kernel->monitorTimer)); |
| gcmkVERIFY_OK(gckOS_DestroyTimer(Kernel->os, Kernel->monitorTimer)); |
| } |
| |
| { |
| if (Kernel->command) |
| { |
| /* Destroy the gckCOMMNAND object. */ |
| gcmkVERIFY_OK(gckCOMMAND_Destroy(Kernel->command)); |
| } |
| |
| if (Kernel->asyncCommand) |
| { |
| gcmkVERIFY_OK(gckCOMMAND_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)); |
| } |
| |
| /* Destroy hardware resources before destroying MMU. */ |
| gcmkVERIFY_OK(gckHARDWARE_PreDestroy(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 |
| } |
| |
| /* 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)); |
| } |
| |
| gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Kernel->vidMemBlockMutex)); |
| |
| /* 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->videoMemListMutex) |
| { |
| /* Destroy video memory list mutex. */ |
| gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Kernel->db->videoMemListMutex)); |
| } |
| |
| /* Destroy the database. */ |
| gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, Kernel->db)); |
| |
| /* Notify stuck timer to quit. */ |
| Kernel->monitorTimerStop = gcvTRUE; |
| } |
| |
| #if gcdDVFS |
| if (Kernel->dvfs) |
| { |
| gcmkVERIFY_OK(gckDVFS_Stop(Kernel->dvfs)); |
| gcmkVERIFY_OK(gckDVFS_Destroy(Kernel->dvfs)); |
| } |
| #endif |
| |
| #if gcdLINUX_SYNC_FILE |
| if (Kernel->timeline) |
| { |
| gcmkVERIFY_OK(gckOS_DestroySyncTimeline(Kernel->os, Kernel->timeline)); |
| } |
| #endif |
| |
| #if gcdSECURITY |
| if (Kernel->securityChannel) |
| { |
| gcmkVERIFY_OK(gckKERNEL_SecurityClose(Kernel->securityChannel)); |
| } |
| #endif |
| |
| /* 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; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckKERNEL_AllocateVideoMemory |
| ** |
| ** Walk requested pools to allocate video memory. |
| ** |
| ** INPUT: |
| ** |
| ** gckKERNEL Kernel |
| ** Pointer to an gckKERNEL object. |
| ** |
| ** OUTPUT: |
| ** |
| ** gckVIDMEM_NODE * NodeObject |
| ** Pointer to a variable receiving video memory represetation. |
| */ |
| gceSTATUS |
| gckKERNEL_AllocateVideoMemory( |
| IN gckKERNEL Kernel, |
| IN gctUINT32 Alignment, |
| IN gceVIDMEM_TYPE Type, |
| IN gctUINT32 Flag, |
| IN OUT gctSIZE_T * Bytes, |
| IN OUT gcePOOL * Pool, |
| OUT gckVIDMEM_NODE * NodeObject |
| ) |
| { |
| gceSTATUS status; |
| gcePOOL pool; |
| gckVIDMEM videoMemory; |
| gctINT loopCount; |
| gckVIDMEM_NODE nodeObject = gcvNULL; |
| gctBOOL contiguous = gcvFALSE; |
| gctBOOL cacheable = gcvFALSE; |
| gctBOOL secure = gcvFALSE; |
| gctBOOL fastPools = gcvFALSE; |
| gctBOOL virtualPool4K = gcvFALSE; |
| gctBOOL hasFastPools = gcvFALSE; |
| gctSIZE_T bytes = *Bytes; |
| |
| gcmkHEADER_ARG("Kernel=%p *Pool=%d *Bytes=%lu Alignment=%lu Type=%d", |
| Kernel, *Pool, *Bytes, Alignment, Type); |
| |
| *NodeObject = gcvNULL; |
| |
| /* 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 (Flag & gcvALLOC_FLAG_4K_PAGES) |
| { |
| virtualPool4K = gcvTRUE; |
| Flag &= ~gcvALLOC_FLAG_4K_PAGES; |
| } |
| |
| #if gcdALLOC_ON_FAULT |
| if (Type == gcvVIDMEM_COLOR_BUFFER) |
| { |
| 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); |
| } |
| |
| if (Type == gcvVIDMEM_TYPE_COMMAND) |
| { |
| #if gcdALLOC_CMD_FROM_RESERVE || gcdSECURITY || gcdDISABLE_GPU_VIRTUAL_ADDRESS || !USE_KERNEL_VIRTUAL_BUFFERS |
| Flag |= gcvALLOC_FLAG_CONTIGUOUS; |
| #endif |
| } |
| |
| if (Type == gcvVIDMEM_TYPE_TILE_STATUS) |
| { |
| gctBOOL tileStatusInVirtual; |
| |
| { |
| tileStatusInVirtual = |
| gckHARDWARE_IsFeatureAvailable(Kernel->hardware, gcvFEATURE_MC20); |
| } |
| |
| if (!tileStatusInVirtual) |
| { |
| /* Must be contiguous if not support virtual tile status. */ |
| Flag |= gcvALLOC_FLAG_CONTIGUOUS; |
| } |
| } |
| |
| if (*Pool == gcvPOOL_DEFAULT) { |
| switch (Type) |
| { |
| case gcvSURF_TYPE_UNKNOWN: |
| case gcvSURF_DEPTH: |
| case gcvSURF_RENDER_TARGET: |
| case gcvSURF_TEXTURE: |
| *Pool = gcvPOOL_VIRTUAL; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| AllocateMemory: |
| |
| #if gcdCAPTURE_ONLY_MODE |
| if (*Pool != gcvPOOL_VIRTUAL) |
| { |
| *Pool = gcvPOOL_SYSTEM; |
| } |
| #endif |
| |
| /* 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; |
| |
| default: |
| loopCount = 1; |
| break; |
| } |
| |
| while (loopCount-- > 0) |
| { |
| if (pool == gcvPOOL_VIRTUAL) |
| { |
| /* Try contiguous virtual first. */ |
| #if gcdCONTIGUOUS_SIZE_LIMIT |
| if (bytes > gcdCONTIGUOUS_SIZE_LIMIT && contiguous == gcvFALSE) |
| { |
| status = gcvSTATUS_OUT_OF_MEMORY; |
| } |
| else |
| #endif |
| #if gcdENABLE_GPU_1M_PAGE |
| if (!virtualPool4K && Kernel->core != gcvCORE_VG && Kernel->hardware->mmuVersion) |
| { |
| /* Create a gckVIDMEM_NODE from contiguous memory. */ |
| status = gckVIDMEM_NODE_AllocateVirtualChunk( |
| Kernel, |
| pool, |
| Type, |
| Flag | gcvALLOC_FLAG_CONTIGUOUS, |
| &bytes, |
| &nodeObject); |
| |
| if (gcmIS_SUCCESS(status)) |
| { |
| /* Memory allocated. */ |
| break; |
| } |
| } |
| #endif |
| { |
| /* Create a gckVIDMEM_NODE from contiguous memory. */ |
| status = gckVIDMEM_NODE_AllocateVirtual( |
| Kernel, |
| pool, |
| Type, |
| Flag | gcvALLOC_FLAG_CONTIGUOUS, |
| &bytes, |
| &nodeObject); |
| } |
| |
| if (gcmIS_SUCCESS(status)) |
| { |
| /* Memory allocated. */ |
| break; |
| } |
| |
| if (contiguous) |
| { |
| break; |
| } |
| |
| #if gcdENABLE_GPU_1M_PAGE |
| /* Try non-contiguous virtual chunk. */ |
| if (!virtualPool4K && Kernel->hardware->mmuVersion && Kernel->core != gcvCORE_VG) |
| { |
| /* Create a gckVIDMEM_NODE from contiguous memory. */ |
| status = gckVIDMEM_NODE_AllocateVirtualChunk( |
| Kernel, |
| pool, |
| Type, |
| Flag | gcvALLOC_FLAG_NON_CONTIGUOUS, |
| &bytes, |
| &nodeObject); |
| |
| if (gcmIS_SUCCESS(status)) |
| { |
| /* Memory allocated. */ |
| break; |
| } |
| } |
| #endif |
| /* Try non-contiguous virtual. */ |
| /* Create a gckVIDMEM_NODE for virtual memory. */ |
| gcmkONERROR( |
| gckVIDMEM_NODE_AllocateVirtual(Kernel, |
| pool, |
| Type, |
| Flag | gcvALLOC_FLAG_NON_CONTIGUOUS, |
| &bytes, &nodeObject)); |
| |
| /* Success. */ |
| break; |
| } |
| /* gcvPOOL_SYSTEM/gcvPOOL_SRAM can't be cacheable. */ |
| else if (cacheable == gcvFALSE && secure == gcvFALSE) |
| { |
| #ifdef EMULATOR |
| /* Cmodel only support 1 SRAM currently. */ |
| Kernel->sRAMIndex = 0; |
| Kernel->extSRAMIndex = 0; |
| #endif |
| |
| /* Get pointer to gckVIDMEM object for pool. */ |
| status = gckKERNEL_GetVideoMemoryPool(Kernel, pool, &videoMemory); |
| |
| if (gcmIS_SUCCESS(status)) |
| { |
| /* Allocate memory. */ |
| if ((Flag & videoMemory->capability) != Flag) |
| { |
| status = gcvSTATUS_NOT_SUPPORTED; |
| |
| } |
| #if defined(gcdLINEAR_SIZE_LIMIT) |
| /* 512 KB */ |
| else if (bytes > gcdLINEAR_SIZE_LIMIT) |
| { |
| status = gcvSTATUS_OUT_OF_MEMORY; |
| } |
| #endif |
| else |
| { |
| hasFastPools = gcvTRUE; |
| status = gckVIDMEM_NODE_AllocateLinear(Kernel, |
| videoMemory, |
| pool, |
| Type, |
| Flag, |
| Alignment, |
| (pool == gcvPOOL_SYSTEM || |
| pool == gcvPOOL_INTERNAL_SRAM || |
| pool == gcvPOOL_EXTERNAL_SRAM), |
| &bytes, |
| &nodeObject); |
| } |
| |
| if (gcmIS_SUCCESS(status)) |
| { |
| /* Memory allocated. */ |
| break; |
| } |
| #if gcdCAPTURE_ONLY_MODE |
| else |
| { |
| gcmkPRINT("Capture only mode: Out of Memory"); |
| } |
| #endif |
| |
| } |
| } |
| |
| if (pool == gcvPOOL_LOCAL_INTERNAL) |
| { |
| /* Advance to external memory. */ |
| pool = gcvPOOL_LOCAL_EXTERNAL; |
| } |
| |
| else |
| if (pool == gcvPOOL_LOCAL_EXTERNAL) |
| { |
| if (Kernel->sRAMLoopMode) |
| { |
| /* Advance to Internal SRAM memory block. */ |
| pool = gcvPOOL_INTERNAL_SRAM; |
| } |
| else |
| { |
| /* Advance to contiguous reserved memory. */ |
| pool = gcvPOOL_SYSTEM; |
| } |
| } |
| |
| else |
| if (pool == gcvPOOL_INTERNAL_SRAM) |
| { |
| if (Kernel->sRAMIndex < gcvSRAM_INTER_COUNT - 1 && !Kernel->sRAMPhysFaked[Kernel->sRAMIndex]) |
| { |
| Kernel->sRAMIndex++; |
| loopCount++; |
| } |
| else |
| { |
| /* Advance to contiguous reserved 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 virtual memory. */ |
| pool = gcvPOOL_VIRTUAL; |
| } |
| |
| else |
| { |
| /* Out of pools. */ |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| } |
| |
| if (nodeObject == 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); |
| } |
| |
| #if gcdCAPTURE_ONLY_MODE |
| nodeObject->captureSize = bytes; |
| #endif |
| |
| /* Return node and pool used for allocation. */ |
| *Pool = pool; |
| *Bytes = bytes; |
| *NodeObject = nodeObject; |
| |
| /* Return status. */ |
| gcmkFOOTER_ARG("*Pool=%d *NodeObject=%p", *Pool, *NodeObject); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| |
| /******************************************************************************* |
| ** |
| ** _AllocateLinearMemory |
| ** |
| ** Private function to allocate the requested amount of video memory, output |
| ** video memory handle. |
| */ |
| gceSTATUS |
| _AllocateLinearMemory( |
| IN gckKERNEL Kernel, |
| IN gctUINT32 ProcessID, |
| IN gcsHAL_INTERFACE * Interface |
| ) |
| { |
| gceSTATUS status; |
| gckVIDMEM_NODE nodeObject = gcvNULL; |
| gctUINT32 handle = 0; |
| gceDATABASE_TYPE dbType; |
| gcePOOL pool = (gcePOOL)Interface->u.AllocateLinearVideoMemory.pool; |
| gctSIZE_T bytes = (gctSIZE_T)Interface->u.AllocateLinearVideoMemory.bytes; |
| gctUINT32 alignment = Interface->u.AllocateLinearVideoMemory.alignment; |
| gceVIDMEM_TYPE type = (Interface->u.AllocateLinearVideoMemory.type & 0xFF); |
| gctUINT32 flag = Interface->u.AllocateLinearVideoMemory.flag; |
| gctUINT64 mappingInOne = 1; |
| gctBOOL isContiguous; |
| |
| gcmkHEADER_ARG("Kernel=%p pool=%d bytes=%lu alignment=%lu type=%d", |
| Kernel, pool, bytes, alignment, type); |
| |
| gcmkVERIFY_ARGUMENT(bytes != 0); |
| |
| if (Interface->u.AllocateLinearVideoMemory.sRAMIndex >= 0) |
| { |
| Kernel->sRAMIndex = Interface->u.AllocateLinearVideoMemory.sRAMIndex; |
| } |
| |
| if (Interface->u.AllocateLinearVideoMemory.extSRAMIndex >= 0) |
| { |
| Kernel->extSRAMIndex = Interface->u.AllocateLinearVideoMemory.extSRAMIndex; |
| } |
| |
| gckOS_QueryOption(Kernel->os, "allMapInOne", &mappingInOne); |
| if (mappingInOne == 0) |
| { |
| /* TODO: it should page align if driver uses dynamic mapping for mapped user memory. |
| * it should be adjusted with different os. |
| */ |
| alignment = gcmALIGN(alignment, 4096); |
| } |
| |
| /* Allocate video memory node. */ |
| gcmkONERROR( |
| gckKERNEL_AllocateVideoMemory(Kernel, |
| alignment, |
| type, |
| flag, |
| &bytes, |
| &pool, |
| &nodeObject)); |
| |
| /* Allocate handle for this video memory. */ |
| gcmkONERROR( |
| gckVIDMEM_HANDLE_Allocate(Kernel, nodeObject, &handle)); |
| |
| /* Return node and pool used for allocation. */ |
| Interface->u.AllocateLinearVideoMemory.node = handle; |
| Interface->u.AllocateLinearVideoMemory.pool = pool; |
| Interface->u.AllocateLinearVideoMemory.bytes = bytes; |
| |
| /* Encode surface type and pool to database type. */ |
| dbType = gcvDB_VIDEO_MEMORY |
| | (type << gcdDB_VIDEO_MEMORY_TYPE_SHIFT) |
| | (pool << gcdDB_VIDEO_MEMORY_POOL_SHIFT); |
| |
| /* Record in process db. */ |
| gcmkONERROR( |
| gckKERNEL_AddProcessDB(Kernel, |
| ProcessID, |
| dbType, |
| gcmINT2PTR(handle), |
| gcvNULL, |
| bytes)); |
| |
| gcmkONERROR(gckVIDMEM_NODE_IsContiguous(Kernel, nodeObject, &isContiguous)); |
| |
| if (isContiguous) |
| { |
| /* Record in process db. */ |
| gcmkONERROR( |
| gckKERNEL_AddProcessDB(Kernel, |
| ProcessID, |
| gcvDB_CONTIGUOUS, |
| gcmINT2PTR(handle), |
| gcvNULL, |
| bytes)); |
| } |
| |
| if (type & gcvVIDMEM_TYPE_COMMAND) |
| { |
| /* Record in process db. */ |
| gcmkONERROR( |
| gckKERNEL_AddProcessDB(Kernel, |
| ProcessID, |
| gcvDB_COMMAND_BUFFER, |
| gcmINT2PTR(handle), |
| gcvNULL, |
| bytes)); |
| } |
| |
| /* Return status. */ |
| gcmkFOOTER_ARG("pool=%d node=0x%x", pool, handle); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (handle) |
| { |
| /* Destroy handle allocated. */ |
| gcmkVERIFY_OK(gckVIDMEM_HANDLE_Dereference(Kernel, ProcessID, handle)); |
| } |
| |
| if (nodeObject) |
| { |
| /* Free video memory allocated. */ |
| gcmkVERIFY_OK(gckVIDMEM_NODE_Dereference(Kernel, nodeObject)); |
| } |
| |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** _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 |
| _ReleaseVideoMemory( |
| IN gckKERNEL Kernel, |
| IN gctUINT32 ProcessID, |
| IN gctUINT32 Handle |
| ) |
| { |
| gceSTATUS status; |
| gckVIDMEM_NODE nodeObject; |
| gceDATABASE_TYPE type; |
| gctBOOL isContiguous; |
| |
| gcmkHEADER_ARG("Kernel=%p 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))); |
| |
| gcmkONERROR(gckVIDMEM_NODE_IsContiguous(Kernel, nodeObject, &isContiguous)); |
| |
| if (isContiguous) |
| { |
| gckKERNEL_RemoveProcessDB(Kernel, |
| ProcessID, |
| gcvDB_CONTIGUOUS, |
| gcmINT2PTR(Handle)); |
| } |
| |
| if (nodeObject->type & gcvVIDMEM_TYPE_COMMAND) |
| { |
| gckKERNEL_RemoveProcessDB(Kernel, |
| ProcessID, |
| gcvDB_COMMAND_BUFFER, |
| gcmINT2PTR(Handle)); |
| } |
| |
| gckVIDMEM_HANDLE_Dereference(Kernel, ProcessID, Handle); |
| |
| gckVIDMEM_NODE_Dereference(Kernel, nodeObject); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** _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. |
| */ |
| static gceSTATUS |
| _LockVideoMemory( |
| IN gckKERNEL Kernel, |
| IN gceCORE Core, |
| IN gctUINT32 ProcessID, |
| IN OUT gcsHAL_INTERFACE * Interface |
| ) |
| { |
| gceSTATUS status; |
| gctUINT32 handle; |
| gckVIDMEM_NODE nodeObject = gcvNULL; |
| gctBOOL referenced = gcvFALSE; |
| gctUINT32 address = gcvINVALID_ADDRESS; |
| gctPOINTER logical = gcvNULL; |
| gctPHYS_ADDR_T physical = gcvINVALID_PHYSICAL_ADDRESS; |
| gctUINT32 gid = 0; |
| gctBOOL asynchronous = gcvFALSE; |
| |
| gcmkHEADER_ARG("Kernel=%p ProcessID=%d", |
| Kernel, ProcessID); |
| |
| handle = Interface->u.LockVideoMemory.node; |
| |
| gcmkONERROR( |
| gckVIDMEM_HANDLE_Lookup(Kernel, ProcessID, handle, &nodeObject)); |
| |
| /* Ref node. */ |
| gcmkONERROR(gckVIDMEM_NODE_Reference(Kernel, nodeObject)); |
| referenced = gcvTRUE; |
| |
| #if gcdCAPTURE_ONLY_MODE |
| if (Interface->u.LockVideoMemory.queryCapSize) |
| { |
| Interface->u.LockVideoMemory.captureSize = nodeObject->captureSize; |
| return gcvSTATUS_OK; |
| } |
| else |
| { |
| nodeObject->captureLogical = Interface->u.LockVideoMemory.captureLogical; |
| } |
| #endif |
| |
| /* Lock for userspace CPU userspace. */ |
| gcmkONERROR( |
| gckVIDMEM_NODE_LockCPU(Kernel, |
| nodeObject, |
| Interface->u.LockVideoMemory.cacheable, |
| gcvTRUE, |
| &logical)); |
| |
| /* Lock for GPU address. */ |
| gcmkONERROR(gckVIDMEM_NODE_Lock(Kernel, nodeObject, &address)); |
| |
| /* Get CPU physical address. */ |
| gcmkONERROR(gckVIDMEM_NODE_GetPhysical(Kernel, nodeObject, 0, &physical)); |
| gcmkONERROR(gckVIDMEM_NODE_GetGid(Kernel, nodeObject, &gid)); |
| |
| Interface->u.LockVideoMemory.address = address; |
| Interface->u.LockVideoMemory.memory = gcmPTR_TO_UINT64(logical); |
| Interface->u.LockVideoMemory.physicalAddress = physical; |
| Interface->u.LockVideoMemory.gid = gid; |
| |
| gcmkONERROR( |
| gckKERNEL_AddProcessDB(Kernel, |
| ProcessID, |
| gcvDB_VIDEO_MEMORY_LOCKED, |
| gcmINT2PTR(handle), |
| logical, |
| 0)); |
| |
| /* Ref handle. */ |
| gckVIDMEM_HANDLE_Reference(Kernel, ProcessID, handle); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (logical) |
| { |
| gckVIDMEM_NODE_UnlockCPU(Kernel, nodeObject, ProcessID, gcvTRUE, gcvFALSE); |
| } |
| |
| if (address) |
| { |
| gckVIDMEM_NODE_Unlock(Kernel, nodeObject, ProcessID, &asynchronous); |
| |
| if (asynchronous) |
| { |
| gckVIDMEM_NODE_Unlock(Kernel, nodeObject, ProcessID, gcvNULL); |
| } |
| } |
| |
| if (referenced) |
| { |
| gckVIDMEM_NODE_Dereference(Kernel, nodeObject); |
| } |
| |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** _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. |
| */ |
| static gceSTATUS |
| _UnlockVideoMemory( |
| IN gckKERNEL Kernel, |
| IN gctUINT32 ProcessID, |
| IN OUT gcsHAL_INTERFACE * Interface |
| ) |
| { |
| gceSTATUS status; |
| gckVIDMEM_NODE nodeObject; |
| gcuVIDMEM_NODE_PTR node; |
| gckVIDMEM_BLOCK vidMemBlock = gcvNULL; |
| gctSIZE_T bytes; |
| gctUINT64 mappingInOne = 1; |
| |
| gcmkHEADER_ARG("Kernel=%p 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 |
| )); |
| |
| gckOS_QueryOption(Kernel->os, "allMapInOne", &mappingInOne); |
| /* Unlock CPU. */ |
| gcmkONERROR(gckVIDMEM_NODE_UnlockCPU( |
| Kernel, nodeObject, ProcessID, gcvTRUE, mappingInOne == 1)); |
| |
| /* Unlock video memory. */ |
| gcmkONERROR(gckVIDMEM_NODE_Unlock( |
| Kernel, |
| nodeObject, |
| ProcessID, |
| &Interface->u.UnlockVideoMemory.asynchroneous |
| )); |
| |
| /* Leave deref handle and deref node in later operation. */ |
| |
| node = nodeObject->node; |
| |
| vidMemBlock = node->VirtualChunk.parent; |
| |
| if (node->VidMem.parent->object.type == gcvOBJ_VIDMEM) |
| { |
| bytes = node->VidMem.bytes; |
| } |
| else if (vidMemBlock && vidMemBlock->object.type == gcvOBJ_VIDMEM_BLOCK) |
| { |
| bytes = node->VirtualChunk.bytes; |
| } |
| else |
| { |
| bytes = node->Virtual.bytes; |
| } |
| |
| Interface->u.UnlockVideoMemory.pool = nodeObject->pool; |
| Interface->u.UnlockVideoMemory.bytes = bytes; |
| |
| #if gcdCAPTURE_ONLY_MODE |
| Interface->u.UnlockVideoMemory.captureLogical = nodeObject->captureLogical; |
| #endif |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* |
| * Unlikely to fail expect error node or unlocked, there's no error roll |
| * back requried for those two conditions. |
| */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** _BottomHalfUnlockVideoMemory |
| ** |
| ** Unlock video memory from gpu. |
| ** |
| ** INPUT: |
| ** |
| ** gckKERNEL Kernel |
| ** Pointer to an gckKERNEL object. |
| ** |
| ** gctUINT32 ProcessID |
| ** Process ID owning this memory. |
| ** |
| ** gceVIDMEM_TYPE |
| ** Video memory allocation type. |
| ** |
| ** gctPOINTER Pointer |
| ** Video memory to be unlock. |
| */ |
| static gceSTATUS |
| _BottomHalfUnlockVideoMemory( |
| IN gckKERNEL Kernel, |
| IN gctUINT32 ProcessID, |
| IN gceVIDMEM_TYPE Type, |
| 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 |
| )); |
| |
| /* Deref handle. */ |
| gckVIDMEM_HANDLE_Dereference(Kernel, ProcessID, Node); |
| |
| /* Unlock video memory, synced. */ |
| gcmkONERROR(gckVIDMEM_NODE_Unlock(Kernel, nodeObject, ProcessID, gcvNULL)); |
| |
| /* Deref node. */ |
| gcmkONERROR(gckVIDMEM_NODE_Dereference(Kernel, nodeObject)); |
| |
| return gcvSTATUS_OK; |
| |
| OnError: |
| return status; |
| } |
| |
| static gceSTATUS |
| _WrapUserMemory( |
| IN gckKERNEL Kernel, |
| IN gctUINT32 ProcessID, |
| IN gcsHAL_INTERFACE * Interface |
| ) |
| { |
| gceSTATUS status; |
| gckVIDMEM_NODE nodeObject = gcvNULL; |
| gceDATABASE_TYPE type; |
| gctUINT32 handle = 0; |
| |
| gcmkONERROR( |
| gckVIDMEM_NODE_WrapUserMemory(Kernel, |
| &Interface->u.WrapUserMemory.desc, |
| Interface->u.WrapUserMemory.type, |
| &nodeObject, |
| &Interface->u.WrapUserMemory.bytes)); |
| |
| /* Create handle representation for userspace. */ |
| gcmkONERROR( |
| gckVIDMEM_HANDLE_Allocate(Kernel, |
| nodeObject, |
| &handle)); |
| |
| type = gcvDB_VIDEO_MEMORY |
| | (nodeObject->type << gcdDB_VIDEO_MEMORY_TYPE_SHIFT) |
| | (nodeObject->pool << gcdDB_VIDEO_MEMORY_POOL_SHIFT); |
| |
| gcmkONERROR( |
| gckKERNEL_AddProcessDB(Kernel, |
| ProcessID, |
| type, |
| gcmINT2PTR(handle), |
| gcvNULL, |
| (gctSIZE_T)Interface->u.WrapUserMemory.bytes)); |
| |
| Interface->u.WrapUserMemory.node = handle; |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (handle) |
| { |
| gckVIDMEM_HANDLE_Dereference(Kernel, ProcessID, handle); |
| } |
| |
| if (nodeObject) |
| { |
| gckVIDMEM_NODE_Dereference(Kernel, nodeObject); |
| } |
| |
| return status; |
| } |
| |
| static gceSTATUS |
| _ExportVideoMemory( |
| IN gckKERNEL Kernel, |
| IN gctUINT32 ProcessID, |
| IN gcsHAL_INTERFACE * Interface |
| ) |
| { |
| gceSTATUS status; |
| gckVIDMEM_NODE nodeObject = gcvNULL; |
| |
| gcmkONERROR( |
| gckVIDMEM_HANDLE_Lookup(Kernel, |
| ProcessID, |
| Interface->u.ExportVideoMemory.node, |
| &nodeObject)); |
| |
| gcmkONERROR( |
| gckVIDMEM_NODE_Export(Kernel, |
| nodeObject, |
| Interface->u.ExportVideoMemory.flags, |
| gcvNULL, |
| &Interface->u.ExportVideoMemory.fd)); |
| |
| OnError: |
| return status; |
| } |
| |
| static gceSTATUS |
| _NameVideoMemory( |
| IN gckKERNEL Kernel, |
| IN gctUINT32 ProcessID, |
| IN gcsHAL_INTERFACE * Interface |
| ) |
| { |
| gceSTATUS status; |
| gckVIDMEM_NODE nodeObject = gcvNULL; |
| |
| gcmkONERROR( |
| gckVIDMEM_HANDLE_Lookup(Kernel, |
| ProcessID, |
| Interface->u.NameVideoMemory.handle, |
| &nodeObject)); |
| |
| gcmkONERROR( |
| gckVIDMEM_NODE_Name(Kernel, |
| nodeObject, |
| &Interface->u.NameVideoMemory.name)); |
| OnError: |
| return status; |
| } |
| |
| static gceSTATUS |
| _ImportVideoMemory( |
| IN gckKERNEL Kernel, |
| IN gctUINT32 ProcessID, |
| IN gcsHAL_INTERFACE * Interface |
| ) |
| { |
| gceSTATUS status; |
| gckVIDMEM_NODE nodeObject = gcvNULL; |
| gctUINT32 handle = 0; |
| |
| gcmkONERROR( |
| gckVIDMEM_NODE_Import(Kernel, |
| Interface->u.ImportVideoMemory.name, |
| &nodeObject)); |
| |
| /* Create handle representation for userspace. */ |
| gcmkONERROR( |
| gckVIDMEM_HANDLE_Allocate(Kernel, |
| nodeObject, |
| &handle)); |
| |
| gcmkONERROR( |
| gckKERNEL_AddProcessDB(Kernel, |
| ProcessID, |
| gcvDB_VIDEO_MEMORY, |
| gcmINT2PTR(handle), |
| gcvNULL, |
| 0)); |
| |
| Interface->u.ImportVideoMemory.handle = handle; |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (handle) |
| { |
| gckVIDMEM_HANDLE_Dereference(Kernel, ProcessID, handle); |
| } |
| |
| if (nodeObject) |
| { |
| gckVIDMEM_NODE_Dereference(Kernel, nodeObject); |
| } |
| |
| 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 |
| _SetVidMemMetadata( |
| IN gckKERNEL Kernel, |
| IN gctUINT32 ProcessID, |
| INOUT gcsHAL_INTERFACE * Interface |
| ) |
| { |
| gceSTATUS status = gcvSTATUS_NOT_SUPPORTED; |
| gckVIDMEM_NODE nodeObj = gcvNULL; |
| |
| gcmkHEADER_ARG("Kernel=%p 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 |
| { |
| #ifdef gcdANDROID |
| if (nodeObj->metadata.ts_address == 0 && nodeObj->tsNode != NULL) |
| { |
| gctUINT32 PhysicalAddress = 0; |
| |
| /* Lock for GPU address. */ |
| gcmkONERROR(gckVIDMEM_NODE_Lock(Kernel, nodeObj->tsNode, &PhysicalAddress)); |
| |
| nodeObj->metadata.ts_address = ( |
| PhysicalAddress + Kernel->hardware->baseAddress); |
| |
| gcmkONERROR(gckVIDMEM_NODE_Unlock(Kernel, nodeObj->tsNode, ProcessID, gcvNULL)); |
| } |
| #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); |
| } |
| else |
| { |
| nodeObj->metadata.ts_dma_buf = NULL; |
| } |
| #endif |
| |
| 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; |
| } |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| #else |
| |
| gceSTATUS |
| _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 |
| |
| static gceSTATUS |
| _GetVideoMemoryFd( |
| IN gckKERNEL Kernel, |
| IN gctUINT32 ProcessID, |
| IN gcsHAL_INTERFACE * Interface |
| ) |
| { |
| gceSTATUS status; |
| gckVIDMEM_NODE nodeObject = gcvNULL; |
| |
| gcmkONERROR( |
| gckVIDMEM_HANDLE_Lookup(Kernel, |
| ProcessID, |
| Interface->u.GetVideoMemoryFd.handle, |
| &nodeObject)); |
| |
| gcmkONERROR( |
| gckVIDMEM_NODE_GetFd(Kernel, |
| nodeObject, |
| &Interface->u.GetVideoMemoryFd.fd)); |
| |
| /* No need to add it to processDB because OS will release all fds when |
| ** process quits. |
| */ |
| OnError: |
| return status; |
| } |
| |
| |
| gceSTATUS |
| gckKERNEL_QueryDatabase( |
| IN gckKERNEL Kernel, |
| IN gctUINT32 ProcessID, |
| IN OUT gcsHAL_INTERFACE * Interface |
| ) |
| { |
| gceSTATUS status; |
| gctINT i; |
| |
| gceDATABASE_TYPE type[2] = { |
| gcvDB_VIDEO_MEMORY | (gcvPOOL_SYSTEM << 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 < 2; 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_EnablePowerManagement(Kernel->hardware, enable)); |
| |
| if (enable == gcvFALSE) |
| { |
| gcmkONERROR( |
| gckHARDWARE_SetPowerState(Kernel->hardware, gcvPOWER_ON)); |
| } |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| |
| static gceSTATUS |
| gckKERNEL_CacheOperation( |
| IN gckKERNEL Kernel, |
| IN gctUINT32 ProcessID, |
| IN gctUINT32 Node, |
| IN gceCACHEOPERATION Operation, |
| IN gctPOINTER Logical, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| gceSTATUS status; |
| gckVIDMEM_NODE nodeObject = gcvNULL; |
| gcuVIDMEM_NODE_PTR node = gcvNULL; |
| gckVIDMEM_BLOCK vidMemBlock = gcvNULL; |
| gctSIZE_T offset = 0; |
| void *memHandle; |
| |
| gcmkHEADER_ARG("Kernel=%p pid=%u Node=%u op=%d Logical=%p Bytes=0x%lx", |
| Kernel, ProcessID, Node, Operation, Logical, Bytes); |
| |
| gcmkONERROR(gckVIDMEM_HANDLE_Lookup(Kernel, |
| ProcessID, |
| Node, |
| &nodeObject)); |
| |
| node = nodeObject->node; |
| |
| vidMemBlock = node->VirtualChunk.parent; |
| |
| if (node->VidMem.parent->object.type == gcvOBJ_VIDMEM) |
| { |
| static gctBOOL printed; |
| |
| if (!printed) |
| { |
| printed = gcvTRUE; |
| gcmkPRINT("[galcore]: %s: Flush Video Memory", __FUNCTION__); |
| } |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| else if (vidMemBlock && vidMemBlock->object.type == gcvOBJ_VIDMEM_BLOCK) |
| { |
| memHandle = vidMemBlock->physical; |
| offset = node->VirtualChunk.offset; |
| } |
| else |
| { |
| memHandle = node->Virtual.physical; |
| } |
| |
| switch (Operation) |
| { |
| case gcvCACHE_FLUSH: |
| /* Clean and invalidate the cache. */ |
| status = gckOS_CacheFlush(Kernel->os, |
| ProcessID, |
| memHandle, |
| offset, |
| Logical, |
| Bytes); |
| break; |
| case gcvCACHE_CLEAN: |
| /* Clean the cache. */ |
| status = gckOS_CacheClean(Kernel->os, |
| ProcessID, |
| memHandle, |
| offset, |
| Logical, |
| Bytes); |
| break; |
| case gcvCACHE_INVALIDATE: |
| /* Invalidate the cache. */ |
| status = gckOS_CacheInvalidate(Kernel->os, |
| ProcessID, |
| memHandle, |
| offset, |
| Logical, |
| Bytes); |
| break; |
| |
| case gcvCACHE_MEMORY_BARRIER: |
| status = gckOS_MemoryBarrier(Kernel->os, Logical); |
| break; |
| |
| default: |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| break; |
| } |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| static gceSTATUS |
| _WaitFence( |
| IN gckKERNEL Kernel, |
| IN gctUINT32 ProcessID, |
| IN gcsHAL_INTERFACE * Interface |
| ) |
| { |
| gceSTATUS status; |
| gckVIDMEM_NODE node; |
| gckCOMMAND command = Kernel->command; |
| gckCOMMAND asyncCommand = Kernel->asyncCommand; |
| gckFENCE fence = gcvNULL; |
| gctUINT i; |
| |
| gcmkASSERT(command != gcvNULL); |
| |
| gcmkONERROR( |
| gckVIDMEM_HANDLE_Lookup(Kernel, |
| ProcessID, |
| Interface->u.WaitFence.handle, |
| &node)); |
| |
| gcmkONERROR(gckVIDMEM_NODE_Reference(Kernel, 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; |
| } |
| |
| gcmkONERROR(gckVIDMEM_NODE_InvalidateCache( |
| Kernel, |
| fence->videoMem, |
| 0, |
| fence->logical, |
| 8 |
| )); |
| |
| 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, |
| Interface->u.WaitFence.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; |
| } |
| |
| static gceSTATUS |
| _Commit( |
| IN gckDEVICE Device, |
| IN gceHARDWARE_TYPE HwType, |
| IN gceENGINE Engine, |
| IN gctUINT32 ProcessId, |
| IN OUT gcsHAL_COMMIT * Commit |
| ) |
| { |
| gceSTATUS status; |
| gcsHAL_SUBCOMMIT *subCommit = &Commit->subCommit; |
| gcsHAL_SUBCOMMIT _subCommit; |
| gctPOINTER userPtr = gcvNULL; |
| gctBOOL needCopy = gcvFALSE; |
| gckKERNEL kernel; |
| |
| gcmkVERIFY_OK(gckOS_QueryNeedCopy(Device->os, ProcessId, &needCopy)); |
| |
| do |
| { |
| gckCOMMAND command; |
| gckEVENT eventObj; |
| gctUINT64 next; |
| |
| /* Skip the first nested sub-commit struct. */ |
| if (userPtr) |
| { |
| /* Copy/map sub-commit from user. */ |
| if (needCopy) |
| { |
| subCommit = &_subCommit; |
| |
| status = gckOS_CopyFromUserData( |
| Device->os, |
| subCommit, |
| userPtr, |
| gcmSIZEOF(gcsHAL_SUBCOMMIT) |
| ); |
| } |
| else |
| { |
| status = gckOS_MapUserPointer( |
| Device->os, |
| userPtr, |
| gcmSIZEOF(gcsHAL_SUBCOMMIT), |
| (gctPOINTER *)&subCommit |
| ); |
| } |
| |
| if (gcmIS_ERROR(status)) |
| { |
| userPtr = gcvNULL; |
| |
| gcmkONERROR(status); |
| } |
| } |
| |
| if (subCommit->coreId >= gcvCORE_COUNT) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| |
| /* Determine the objects. */ |
| if (HwType == gcvHARDWARE_3D || HwType == gcvHARDWARE_3D2D || HwType == gcvHARDWARE_VIP) |
| { |
| kernel = Device->coreInfoArray[subCommit->coreId].kernel; |
| } |
| else |
| { |
| kernel = Device->map[HwType].kernels[subCommit->coreId]; |
| } |
| if (Engine == gcvENGINE_BLT) |
| { |
| command = kernel->asyncCommand; |
| eventObj = kernel->asyncEvent; |
| } |
| else |
| { |
| command = kernel->command; |
| eventObj = kernel->eventObj; |
| } |
| |
| { |
| /* Commit command buffers. */ |
| status = gckCOMMAND_Commit(command, |
| subCommit, |
| ProcessId, |
| Commit->shared, |
| &Commit->commitStamp, |
| &Commit->contextSwitched); |
| |
| if (status != gcvSTATUS_INTERRUPTED) |
| { |
| gcmkONERROR(status); |
| } |
| |
| /* Commit events. */ |
| status = gckEVENT_Commit( |
| eventObj, |
| gcmUINT64_TO_PTR(subCommit->queue), |
| kernel->hardware->options.powerManagement |
| ); |
| |
| if (status != gcvSTATUS_INTERRUPTED) |
| { |
| gcmkONERROR(status); |
| } |
| } |
| |
| next = subCommit->next; |
| |
| /* Unmap user pointer if mapped. */ |
| if (!needCopy && userPtr) |
| { |
| gcmkVERIFY_OK(gckOS_UnmapUserPointer( |
| Device->os, |
| userPtr, |
| gcmSIZEOF(gcsHAL_SUBCOMMIT), |
| subCommit |
| )); |
| } |
| |
| /* Advance to next sub-commit from user. */ |
| userPtr = gcmUINT64_TO_PTR(next); |
| } |
| while (userPtr); |
| |
| subCommit = &Commit->subCommit; |
| userPtr = gcvNULL; |
| |
| if (HwType == gcvHARDWARE_3D || HwType == gcvHARDWARE_3D2D || HwType == gcvHARDWARE_VIP) |
| { |
| kernel = Device->coreInfoArray[subCommit->coreId].kernel; |
| } |
| else |
| { |
| kernel = Device->map[HwType].kernels[subCommit->coreId]; |
| } |
| |
| if (!kernel->hardware->options.gpuProfiler || !kernel->profileEnable) |
| { |
| return gcvSTATUS_OK; |
| } |
| |
| do |
| { |
| gctUINT64 next; |
| |
| /* Skip the first nested sub-commit struct. */ |
| if (userPtr) |
| { |
| /* Copy/map sub-commit from user. */ |
| if (needCopy) |
| { |
| subCommit = &_subCommit; |
| |
| status = gckOS_CopyFromUserData( |
| Device->os, |
| subCommit, |
| userPtr, |
| gcmSIZEOF(gcsHAL_SUBCOMMIT) |
| ); |
| } |
| else |
| { |
| status = gckOS_MapUserPointer( |
| Device->os, |
| userPtr, |
| gcmSIZEOF(gcsHAL_SUBCOMMIT), |
| (gctPOINTER *)&subCommit |
| ); |
| } |
| |
| if (gcmIS_ERROR(status)) |
| { |
| userPtr = gcvNULL; |
| |
| gcmkONERROR(status); |
| } |
| } |
| |
| if (HwType == gcvHARDWARE_3D || HwType == gcvHARDWARE_3D2D || HwType == gcvHARDWARE_VIP) |
| { |
| kernel = Device->coreInfoArray[subCommit->coreId].kernel; |
| } |
| else |
| { |
| kernel = Device->map[HwType].kernels[subCommit->coreId]; |
| } |
| |
| 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)); |
| } |
| } |
| |
| next = subCommit->next; |
| |
| /* Unmap user pointer if mapped. */ |
| if (!needCopy && userPtr) |
| { |
| gcmkVERIFY_OK(gckOS_UnmapUserPointer( |
| Device->os, |
| userPtr, |
| gcmSIZEOF(gcsHAL_SUBCOMMIT), |
| subCommit |
| )); |
| } |
| |
| /* Advance to next sub-commit from user. */ |
| userPtr = gcmUINT64_TO_PTR(next); |
| } |
| while (userPtr); |
| |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (!needCopy && userPtr) |
| { |
| gckOS_UnmapUserPointer( |
| Device->os, |
| userPtr, |
| gcmSIZEOF(gcsHAL_SUBCOMMIT), |
| subCommit |
| ); |
| } |
| |
| 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 && Node[i] != 0; i++) |
| { |
| gckVIDMEM_NODE nodeObject = gcvNULL; |
| |
| gcmkONERROR( |
| gckVIDMEM_HANDLE_Lookup(Kernel, ProcessID, Node[i], &nodeObject)); |
| |
| gcmkONERROR(gckVIDMEM_NODE_Reference(Kernel, nodeObject)); |
| |
| parcle->node[i] = nodeObject; |
| } |
| |
| 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. |
| ** |
| ** 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 OUT gcsHAL_INTERFACE * Interface |
| ) |
| { |
| gceSTATUS status = gcvSTATUS_OK; |
| gctPHYS_ADDR physical = gcvNULL; |
| gctSIZE_T bytes; |
| gctPOINTER logical = gcvNULL; |
| #if (gcdENABLE_3D) |
| gckCONTEXT context = gcvNULL; |
| #endif |
| gckKERNEL kernel = Kernel; |
| gctUINT32 processID; |
| #if !USE_NEW_LINUX_SIGNAL |
| gctSIGNAL signal; |
| #endif |
| |
| gctBOOL powerMutexAcquired = gcvFALSE; |
| gctBOOL commitMutexAcquired = gcvFALSE; |
| gctBOOL idle = gcvFALSE; |
| |
| gcmkHEADER_ARG("Kernel=%p Interface=%p", Kernel, 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]); |
| |
| gcmSTATIC_ASSERT(gcvHAL_DESTROY_MMU == gcmCOUNTOF(_DispatchText) - 1, |
| "DispatchText array does not match command codes"); |
| #endif |
| #if QNX_SINGLE_THREADED_DEBUGGING |
| gckOS_AcquireMutex(Kernel->os, Kernel->debugMutex, gcvINFINITE); |
| #endif |
| |
| /* Get the current process ID. */ |
| gcmkONERROR(gckOS_GetProcessID(&processID)); |
| |
| /* 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->gpuPhysicalRangeCount; |
| if (Kernel->mmu->gpuPhysicalRangeCount) |
| { |
| gckOS_MemCopy(Interface->u.GetBaseAddress.flatMappingRanges, Kernel->mmu->gpuPhysicalRanges, |
| gcmSIZEOF(gcsFLAT_MAPPING_RANGE) * Kernel->mmu->gpuPhysicalRangeCount); |
| } |
| 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_QUERY_CHIP_FREQUENCY: |
| /* Query chip clock. */ |
| gcmkONERROR( |
| gckHARDWARE_QueryFrequency(Kernel->hardware)); |
| |
| Interface->u.QueryChipFrequency.mcClk = Kernel->hardware->mcClk; |
| Interface->u.QueryChipFrequency.shClk = Kernel->hardware->shClk; |
| break; |
| |
| case gcvHAL_MAP_MEMORY: |
| physical = gcmINT2PTR(Interface->u.MapMemory.physName); |
| |
| /* 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.physName); |
| |
| 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, |
| gcvTRUE, |
| gcvALLOC_FLAG_CONTIGUOUS, |
| &bytes, |
| &physical, |
| &logical)); |
| |
| Interface->u.AllocateNonPagedMemory.bytes = bytes; |
| Interface->u.AllocateNonPagedMemory.logical = gcmPTR_TO_UINT64(logical); |
| Interface->u.AllocateNonPagedMemory.physName = gcmPTR_TO_NAME(physical); |
| |
| gcmkVERIFY_OK( |
| gckKERNEL_AddProcessDB(Kernel, |
| processID, gcvDB_NON_PAGED, |
| logical, |
| gcmINT2PTR(Interface->u.AllocateNonPagedMemory.physName), |
| bytes)); |
| break; |
| |
| case gcvHAL_FREE_NON_PAGED_MEMORY: |
| physical = gcmNAME_TO_PTR(Interface->u.FreeNonPagedMemory.physName); |
| |
| gcmkVERIFY_OK( |
| gckKERNEL_RemoveProcessDB(Kernel, |
| processID, gcvDB_NON_PAGED, |
| gcmUINT64_TO_PTR(Interface->u.FreeNonPagedMemory.logical))); |
| |
| /* Free non-paged memory. */ |
| gcmkONERROR( |
| gckOS_FreeNonPagedMemory(Kernel->os, |
| physical, |
| gcmUINT64_TO_PTR(Interface->u.FreeNonPagedMemory.logical), |
| (gctSIZE_T) Interface->u.FreeNonPagedMemory.bytes)); |
| |
| gcmRELEASE_NAME(Interface->u.FreeNonPagedMemory.physName); |
| break; |
| |
| case gcvHAL_ALLOCATE_LINEAR_VIDEO_MEMORY: |
| /* Allocate memory. */ |
| gcmkONERROR(_AllocateLinearMemory(Kernel, processID, Interface)); |
| break; |
| |
| case gcvHAL_RELEASE_VIDEO_MEMORY: |
| /* Release video memory. */ |
| gcmkONERROR(_ReleaseVideoMemory( |
| Kernel, processID, |
| (gctUINT32)Interface->u.ReleaseVideoMemory.node |
| )); |
| break; |
| |
| case gcvHAL_LOCK_VIDEO_MEMORY: |
| /* Lock video memory. */ |
| gcmkONERROR(_LockVideoMemory(Kernel, Kernel->core, processID, Interface)); |
| break; |
| |
| case gcvHAL_UNLOCK_VIDEO_MEMORY: |
| /* Unlock video memory. */ |
| gcmkONERROR(_UnlockVideoMemory(Kernel, processID, Interface)); |
| break; |
| |
| case gcvHAL_BOTTOM_HALF_UNLOCK_VIDEO_MEMORY: |
| gcmkERR_BREAK(_BottomHalfUnlockVideoMemory(Kernel, processID, |
| Interface->u.BottomHalfUnlockVideoMemory.type, |
| Interface->u.BottomHalfUnlockVideoMemory.node)); |
| break; |
| |
| case gcvHAL_EVENT_COMMIT: |
| if (!Interface->commitMutex) |
| { |
| 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)); |
| } |
| |
| if (!Interface->commitMutex) |
| { |
| gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->device->commitMutex)); |
| commitMutexAcquired = gcvFALSE; |
| } |
| break; |
| |
| case gcvHAL_COMMIT: |
| if (!Interface->commitMutex) |
| { |
| gcmkONERROR(gckOS_AcquireMutex(Kernel->os, |
| Device->commitMutex, |
| gcvINFINITE |
| )); |
| commitMutexAcquired = gcvTRUE; |
| } |
| |
| gcmkONERROR(_Commit(Device, |
| Kernel->hardware->type, |
| Interface->engine, |
| processID, |
| &Interface->u.Commit)); |
| |
| if (!Interface->commitMutex) |
| { |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Device->commitMutex)); |
| commitMutexAcquired = gcvFALSE; |
| } |
| 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_SetPowerState(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_QueryPowerState( |
| 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_READ_FROM_USER |
| { |
| gceCHIPPOWERSTATE power; |
| |
| gcmkONERROR(gckOS_AcquireMutex(Kernel->os, Kernel->hardware->powerMutex, gcvINFINITE)); |
| powerMutexAcquired = gcvTRUE; |
| gcmkONERROR(gckHARDWARE_QueryPowerState(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_WRITE_FROM_USER |
| { |
| gceCHIPPOWERSTATE power; |
| |
| gcmkONERROR(gckOS_AcquireMutex(Kernel->os, Kernel->hardware->powerMutex, gcvINFINITE)); |
| powerMutexAcquired = gcvTRUE; |
| gcmkONERROR(gckHARDWARE_QueryPowerState(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_RESET: |
| /* Reset the hardware. */ |
| gcmkONERROR( |
| gckHARDWARE_Reset(Kernel->hardware)); |
| break; |
| |
| case gcvHAL_SET_DEBUG_LEVEL_ZONE: |
| /* Set debug level and zones. */ |
| gckOS_SetDebugLevel(Interface->u.DebugLevelZone.level); |
| gckOS_SetDebugZones(Interface->u.DebugLevelZone.zones, |
| Interface->u.DebugLevelZone.enable); |
| break; |
| |
| case gcvHAL_DEBUG_DUMP: |
| gckOS_DumpBuffer(Kernel->os, |
| Interface->u.DebugDump.type, |
| gcmUINT64_TO_PTR(Interface->u.DebugDump.ptr), |
| Interface->u.DebugDump.address, |
| Interface->u.DebugDump.size); |
| status = gcvSTATUS_OK; |
| break; |
| |
| case gcvHAL_DUMP_GPU_STATE: |
| { |
| gceCHIPPOWERSTATE power; |
| |
| _DumpDriverConfigure(Kernel); |
| |
| gcmkONERROR(gckHARDWARE_QueryPowerState( |
| 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; |
| |
| gcmkONERROR(gckKERNEL_CacheOperation(Kernel, |
| processID, |
| Interface->u.Cache.node, |
| Interface->u.Cache.operation, |
| logical, |
| bytes)); |
| 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 %s", gcvVERSION_STRING); |
| #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) |
| case gcvHAL_ATTACH: |
| if (Kernel->command) |
| { |
| #if gcdCAPTURE_ONLY_MODE |
| gckVIDMEM_NODE nodeObject = gcvNULL; |
| |
| if (Interface->u.Attach.queryCapSize) |
| { |
| /* 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); |
| |
| gcmkONERROR( |
| gckVIDMEM_HANDLE_Lookup(Kernel, processID, context->buffer->handle, &nodeObject)); |
| |
| Interface->u.Attach.captureSize = nodeObject->captureSize; |
| break; |
| } |
| else |
| { |
| gctUINT i = 0; |
| context = gcmNAME_TO_PTR(Interface->u.Attach.context); |
| |
| for (i = 0; i < gcdCONTEXT_BUFFER_NUM; ++i) |
| { |
| gcsCONTEXT_PTR buffer = context->buffer; |
| |
| gckOS_CopyToUserData(Kernel->os, buffer->logical, Interface->u.Attach.contextLogical[i], Interface->u.Attach.captureSize); |
| |
| buffer = buffer->next; |
| } |
| } |
| |
| #else |
| /* 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); |
| #endif |
| |
| if (Interface->u.Attach.map) |
| { |
| if (context != gcvNULL) |
| { |
| #if gcdCAPTURE_ONLY_MODE |
| gctUINT i = 0; |
| |
| for (i = 0; i < gcdCONTEXT_BUFFER_NUM; ++i) |
| { |
| Interface->u.Attach.logicals[i] = gcmPTR_TO_UINT64(Interface->u.Attach.contextLogical[i]); |
| } |
| |
| Interface->u.Attach.bytes = (gctUINT)context->totalSize; |
| #else |
| gcmkVERIFY_OK( |
| gckCONTEXT_MapBuffer(context, |
| Interface->u.Attach.logicals, |
| &Interface->u.Attach.bytes)); |
| #endif |
| } |
| else |
| { |
| gctUINT i; |
| |
| for (i = 0; i < gcmCOUNTOF(Interface->u.Attach.logicals); i++) |
| { |
| Interface->u.Attach.logicals[i] = 0; |
| } |
| |
| Interface->u.Attach.bytes = 0; |
| } |
| } |
| |
| gcmkVERIFY_OK( |
| gckKERNEL_AddProcessDB(Kernel, |
| processID, gcvDB_CONTEXT, |
| gcmINT2PTR(Interface->u.Attach.context), |
| gcvNULL, |
| 0)); |
| } |
| break; |
| #endif |
| |
| case gcvHAL_DETACH: |
| if (Kernel->command) |
| { |
| 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 |
| /* Wait for HW idle, otherwise it is not safe. */ |
| gcmkONERROR(gckCOMMAND_Stall(Kernel->command, gcvFALSE)); |
| |
| for (;;) |
| { |
| gcmkONERROR(gckHARDWARE_QueryIdle(Kernel->hardware, &idle)); |
| |
| if (idle) |
| { |
| break; |
| } |
| |
| gcmkVERIFY_OK(gckOS_Delay(Kernel->os, 1)); |
| } |
| |
| status = gckHARDWARE_SetFscaleValue(Kernel->hardware, |
| Interface->u.SetFscaleValue.value, |
| Interface->u.SetFscaleValue.shValue); |
| #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(_ExportVideoMemory(Kernel, processID, Interface)); |
| break; |
| |
| case gcvHAL_NAME_VIDEO_MEMORY: |
| gcmkONERROR(_NameVideoMemory(Kernel, processID, Interface)); |
| break; |
| |
| case gcvHAL_IMPORT_VIDEO_MEMORY: |
| gcmkONERROR(_ImportVideoMemory(Kernel, processID, Interface)); |
| break; |
| |
| case gcvHAL_SET_VIDEO_MEMORY_METADATA: |
| gcmkONERROR(_SetVidMemMetadata(Kernel, processID, Interface)); |
| break; |
| |
| case gcvHAL_GET_VIDEO_MEMORY_FD: |
| gcmkONERROR(_GetVideoMemoryFd(Kernel, processID, Interface)); |
| break; |
| |
| case gcvHAL_QUERY_RESET_TIME_STAMP: |
| Interface->u.QueryResetTimeStamp.timeStamp = Kernel->resetTimeStamp; |
| Interface->u.QueryResetTimeStamp.contextID = Kernel->hardware->contextID; |
| break; |
| |
| #if gcdLINUX_SYNC_FILE |
| 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(_WrapUserMemory(Kernel, processID, Interface)); |
| break; |
| |
| case gcvHAL_WAIT_FENCE: |
| gcmkONERROR(_WaitFence(Kernel, processID, Interface)); |
| break; |
| |
| case gcvHAL_DEVICE_MUTEX: |
| if (Interface->u.DeviceMutex.isMutexLocked) |
| { |
| gcmkONERROR(gckOS_AcquireMutex(Kernel->os, |
| Kernel->device->commitMutex, |
| gcvINFINITE |
| )); |
| } |
| else |
| { |
| gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->device->commitMutex)); |
| } |
| 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=%p 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=%p 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) |
| { |
| { |
| gcmkONERROR(gckOS_Broadcast(Kernel->os, |
| Kernel->hardware, |
| gcvBROADCAST_FIRST_PROCESS)); |
| } |
| } |
| |
| if (Kernel->dbCreated) |
| { |
| /* Create the process database. */ |
| gcmkONERROR(gckKERNEL_CreateProcessDB(Kernel, PID)); |
| } |
| } |
| else |
| { |
| if (Kernel->dbCreated) |
| { |
| /* Clean up the process database. */ |
| gcmkONERROR(gckKERNEL_DestroyProcessDB(Kernel, PID)); |
| |
| /* Save the last know process ID. */ |
| Kernel->db->lastProcessID = PID; |
| } |
| |
| { |
| 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) |
| { |
| { |
| /* Last client detached, switch to SUSPEND power state. */ |
| gcmkONERROR(gckOS_Broadcast(Kernel->os, |
| Kernel->hardware, |
| gcvBROADCAST_LAST_PROCESS)); |
| } |
| } |
| |
| if (Kernel->timeoutPID == PID) |
| { |
| Kernel->timeOut = Kernel->hardware->type == gcvHARDWARE_2D |
| ? gcdGPU_2D_TIMEOUT |
| : gcdGPU_TIMEOUT; |
| } |
| } |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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; |
| gctUINT32 mask = 0; |
| gctUINT32 i = 0, count = 0; |
| #if gcdINTERRUPT_STATISTIC |
| gctINT32 oldValue; |
| #endif |
| |
| gcmkHEADER_ARG("Kernel=%p", 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 (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=%p NeedCopy=%d StaticStorage=%p " |
| "UserPointer=%p Size=%lu KernelPointer=%p", |
| 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=%p NeedCopy=%d FlushData=%d " |
| "UserPointer=%p Size=%lu KernelPointer=%p", |
| 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. */ |
| gcmkVERIFY_OK(gckOS_UnmapUserPointer( |
| Kernel->os, |
| UserPointer, |
| Size, |
| * KernelPointer |
| )); |
| } |
| |
| /* Reset the kernel pointer. */ |
| * KernelPointer = gcvNULL; |
| } |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| 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 < 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 *************************** |
| \******************************************************************************/ |
| |
| /* The capacity is 32 initially, double when expand, but no more than 512. */ |
| #define gcdID_TABLE_MAX_EXPAND 512 |
| |
| typedef struct _gcsINTEGERDB * gckINTEGERDB; |
| typedef struct _gcsINTEGERDB |
| { |
| gckOS os; |
| gctPOINTER * table; |
| gctUINT32 * bitmap; |
| gctPOINTER mutex; |
| gctUINT32 capacity; |
| gctUINT32 nextId; |
| gctUINT32 freeCount; |
| } |
| gcsINTEGERDB; |
| |
| gceSTATUS |
| gckKERNEL_CreateIntegerDatabase( |
| IN gckKERNEL Kernel, |
| IN gctUINT32 Capacity, |
| OUT gctPOINTER * Database |
| ) |
| { |
| gceSTATUS status; |
| gckINTEGERDB database = gcvNULL; |
| |
| gcmkHEADER_ARG("Kernel=%p Capacity=%u Datbase=%p", Kernel, Capacity, Database); |
| |
| gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); |
| gcmkVERIFY_ARGUMENT(Database != gcvNULL); |
| |
| /* round up to 32 alignment. */ |
| Capacity = (Capacity + 31) & ~31; |
| |
| /* 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) * Capacity, |
| (gctPOINTER *)&database->table |
| )); |
| |
| /* Allocate bitmap. */ |
| gcmkONERROR(gckOS_Allocate( |
| Kernel->os, Capacity / 8, (gctPOINTER *)&database->bitmap)); |
| |
| gcmkONERROR(gckOS_ZeroMemory(database->bitmap, Capacity / 8)); |
| |
| /* Allocate a database mutex. */ |
| gcmkONERROR(gckOS_CreateMutex(Kernel->os, &database->mutex)); |
| |
| /* Initialize. */ |
| database->os = Kernel->os; |
| |
| database->nextId = 0; |
| database->freeCount = Capacity; |
| database->capacity = Capacity; |
| |
| *Database = database; |
| |
| gcmkFOOTER_ARG("*Database=0x%08X", *Database); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Rollback. */ |
| if (database) |
| { |
| if (database->table) |
| { |
| gckOS_Free(Kernel->os, database->table); |
| } |
| |
| if (database->bitmap) |
| { |
| gckOS_Free(Kernel->os, database->bitmap); |
| } |
| |
| gckOS_Free(Kernel->os, database); |
| } |
| |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| gceSTATUS |
| gckKERNEL_DestroyIntegerDatabase( |
| IN gckKERNEL Kernel, |
| IN gctPOINTER Database |
| ) |
| { |
| gckINTEGERDB database = Database; |
| |
| gcmkHEADER_ARG("Kernel=%p Datbase=%p", Kernel, Database); |
| |
| gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); |
| gcmkVERIFY_ARGUMENT(Database != gcvNULL); |
| |
| /* Destroy pointer table. */ |
| gcmkOS_SAFE_FREE(Kernel->os, database->table); |
| gcmkOS_SAFE_FREE(Kernel->os, database->bitmap); |
| |
| /* Destroy database mutex. */ |
| gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, database->mutex)); |
| |
| /* Destroy database. */ |
| gckOS_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 pos; |
| gctUINT32 n, i; |
| gckOS os = database->os; |
| gctPOINTER * table = gcvNULL; |
| |
| gcmkHEADER_ARG("Database=%p Pointer=%p", Database, Pointer); |
| |
| gcmkVERIFY_OK(gckOS_AcquireMutex(os, database->mutex, gcvINFINITE)); |
| |
| if (database->freeCount < 1) |
| { |
| gctUINT32 * bitmap = gcvNULL; |
| gctUINT32 expand; |
| gctUINT32 capacity; |
| |
| expand = database->capacity < gcdID_TABLE_MAX_EXPAND |
| ? database->capacity : gcdID_TABLE_MAX_EXPAND; |
| |
| capacity = database->capacity + expand; |
| |
| /* Extend table. */ |
| gcmkONERROR( |
| gckOS_Allocate(os, |
| gcmSIZEOF(gctPOINTER) * capacity, |
| (gctPOINTER *)&table)); |
| |
| gcmkONERROR( |
| gckOS_Allocate(os, capacity / 32 * sizeof(gctUINT32), (gctPOINTER *)&bitmap)); |
| |
| gckOS_ZeroMemory(bitmap + database->capacity / 32, expand / 8); |
| |
| /* Copy data from old table. */ |
| gckOS_MemCopy(table, |
| database->table, |
| database->capacity * gcmSIZEOF(gctPOINTER)); |
| |
| gckOS_MemCopy(bitmap, |
| database->bitmap, |
| database->capacity / 32 * sizeof(gctUINT32)); |
| |
| gckOS_Free(os, database->table); |
| gckOS_Free(os, database->bitmap); |
| |
| /* Update databse with new allocated table. */ |
| database->table = table; |
| database->bitmap = bitmap; |
| database->nextId = database->capacity; |
| database->capacity = capacity; |
| database->freeCount += expand; |
| } |
| |
| pos = database->nextId; |
| n = pos >> 5; |
| i = pos & 31; |
| |
| while (database->bitmap[n] & (1u << i)) |
| { |
| pos++; |
| |
| if (pos == database->capacity) |
| { |
| /* Wrap to the begin. */ |
| pos = 0; |
| } |
| |
| n = pos >> 5; |
| i = pos & 31; |
| } |
| |
| /* Connect id with pointer. */ |
| database->table[pos] = Pointer; |
| database->bitmap[n] |= (1u << i); |
| |
| *Id = pos + 1; |
| |
| database->nextId = (pos + 1) % database->capacity; |
| database->freeCount--; |
| |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(os, database->mutex)); |
| |
| gcmkFOOTER_ARG("*Id=%u", *Id); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (table) |
| { |
| gckOS_Free(os, table); |
| } |
| |
| 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; |
| gctUINT32 pos = Id - 1; |
| gctUINT32 n, i; |
| |
| gcmkHEADER_ARG("Database=%p Id=%d", Database, Id); |
| |
| gcmkVERIFY_OK(gckOS_AcquireMutex(os, database->mutex, gcvINFINITE)); |
| |
| n = pos >> 5; |
| i = pos & 31; |
| |
| if (pos >= database->capacity || (database->bitmap[n] & (1u << i)) == 0) |
| { |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(os, database->mutex)); |
| gcmkONERROR(gcvSTATUS_NOT_FOUND); |
| } |
| |
| /* Clear id. */ |
| database->bitmap[n] &= ~(1u << i); |
| database->table[pos] = gcvNULL; |
| |
| ++database->freeCount; |
| |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(os, database->mutex)); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| gceSTATUS |
| gckKERNEL_QueryIntegerId( |
| IN gctPOINTER Database, |
| IN gctUINT32 Id, |
| OUT gctPOINTER * Pointer |
| ) |
| { |
| gceSTATUS status; |
| gckINTEGERDB database = Database; |
| gckOS os = database->os; |
| gctUINT32 pos = Id - 1; |
| gctUINT32 n, i; |
| |
| gcmkHEADER_ARG("Database=%p Id=%d", Database, Id); |
| gcmkVERIFY_ARGUMENT(Pointer != gcvNULL); |
| |
| gcmkVERIFY_OK(gckOS_AcquireMutex(os, database->mutex, gcvINFINITE)); |
| |
| n = pos >> 5; |
| i = pos & 31; |
| |
| if (pos >= database->capacity || (database->bitmap[n] & (1u << i)) == 0) |
| { |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(os, database->mutex)); |
| gcmkONERROR(gcvSTATUS_NOT_FOUND); |
| } |
| |
| *Pointer = database->table[pos]; |
| |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(os, database->mutex)); |
| |
| gcmkFOOTER_ARG("*Pointer=0x%08X", *Pointer); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| |
| gctUINT32 |
| gckKERNEL_AllocateNameFromPointer( |
| IN gckKERNEL Kernel, |
| IN gctPOINTER Pointer |
| ) |
| { |
| gceSTATUS status; |
| gctUINT32 name; |
| gctPOINTER database = Kernel->db->pointerDatabase; |
| |
| gcmkHEADER_ARG("Kernel=%p Pointer=%p", 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=%p 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=%p Name=%p", 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=%p, 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; |
| |
| gcmkHEADER_ARG("Kernel=%p ShBuf=%u", |
| Kernel, (gctUINT32)(gctUINTPTR_T) ShBuf); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); |
| gcmkVERIFY_ARGUMENT(ShBuf != gcvNULL); |
| |
| /* 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); |
| } |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| 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; |
| |
| gcmkHEADER_ARG("Kernel=%p ShBuf=%u", |
| Kernel, (gctUINT32)(gctUINTPTR_T) ShBuf); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); |
| gcmkVERIFY_ARGUMENT(ShBuf != gcvNULL); |
| |
| /* 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); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| 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; |
| |
| gcmkHEADER_ARG("Kernel=%p ShBuf=%u UserData=%p ByteCount=%u", |
| Kernel, (gctUINT32)(gctUINTPTR_T) ShBuf, UserData, ByteCount); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); |
| gcmkVERIFY_ARGUMENT(ShBuf != gcvNULL); |
| |
| /* 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)); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (shBuf && shBuf->data) |
| { |
| gcmkOS_SAFE_FREE(Kernel->os, shBuf->data); |
| shBuf->data = gcvNULL; |
| } |
| |
| 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; |
| |
| gcmkHEADER_ARG("Kernel=%p ShBuf=%u UserData=%p ByteCount=%u", |
| Kernel, (gctUINT32)(gctUINTPTR_T) ShBuf, UserData, ByteCount); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); |
| gcmkVERIFY_ARGUMENT(ShBuf != gcvNULL); |
| |
| /* 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; |
| |
| gcmkFOOTER_ARG("*BytesRead=%u", bytes); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| 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; |
| |
| #if !gcdCAPTURE_ONLY_MODE |
| gcePOOL pool = gcvPOOL_DEFAULT; |
| #else |
| gcePOOL pool = gcvPOOL_VIRTUAL; |
| #endif |
| |
| gckFENCE fence = gcvNULL; |
| gctSIZE_T size = 8; |
| gctUINT32 allocFlag = gcvALLOC_FLAG_CONTIGUOUS; |
| |
| #if gcdENABLE_CACHEABLE_COMMAND_BUFFER |
| allocFlag |= gcvALLOC_FLAG_CACHEABLE; |
| #endif |
| |
| |
| gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(gcsFENCE), (gctPOINTER *)&fence)); |
| gcmkONERROR(gckOS_ZeroMemory(fence, gcmSIZEOF(gcsFENCE))); |
| gcmkONERROR(gckOS_CreateMutex(Os, (gctPOINTER *)&fence->mutex)); |
| |
| fence->kernel = Kernel; |
| |
| /* Allocate video memory node for fence. */ |
| gcmkONERROR(gckKERNEL_AllocateVideoMemory( |
| Kernel, |
| 64, |
| gcvVIDMEM_TYPE_FENCE, |
| allocFlag, |
| &size, |
| &pool, |
| &fence->videoMem |
| )); |
| |
| /* Lock for GPU access. */ |
| gcmkONERROR(gckVIDMEM_NODE_Lock( |
| Kernel, |
| fence->videoMem, |
| &fence->address |
| )); |
| |
| /* Lock for kernel side CPU access. */ |
| gcmkONERROR(gckVIDMEM_NODE_LockCPU( |
| Kernel, |
| fence->videoMem, |
| gcvFALSE, |
| gcvFALSE, |
| &fence->logical |
| )); |
| |
| 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) |
| { |
| gcmkVERIFY_OK(gckVIDMEM_NODE_UnlockCPU( |
| Fence->kernel, |
| Fence->videoMem, |
| 0, |
| gcvFALSE, |
| gcvFALSE |
| )); |
| |
| /* Synchronueous unlock. */ |
| gcmkVERIFY_OK(gckVIDMEM_NODE_Unlock( |
| Fence->kernel, |
| Fence->videoMem, |
| 0, |
| gcvNULL |
| )); |
| |
| /* Free video memory. */ |
| gcmkVERIFY_OK(gckVIDMEM_NODE_Dereference( |
| Fence->kernel, |
| Fence->videoMem |
| )); |
| } |
| |
| 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, struct _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, j; |
| |
| gcmkHEADER(); |
| |
| gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(gcsDEVICE), (gctPOINTER *)&device)); |
| |
| gckOS_ZeroMemory(device, gcmSIZEOF(gcsDEVICE)); |
| |
| for (i = 0; i < gcvCORE_COUNT; i++) |
| { |
| device->coreInfoArray[i].type = gcvHARDWARE_INVALID; |
| |
| /* Initialize internal SRAM. */ |
| for (j = 0; j < gcvSRAM_INTER_COUNT; j++) |
| { |
| device->sRAMBases[i][j] = gcvINVALID_PHYSICAL_ADDRESS; |
| device->sRAMSizes[i][j] = 0; |
| device->sRAMPhysFaked[i][j] = gcvFALSE; |
| } |
| } |
| |
| /* Initialize external SRAM. */ |
| for (i = 0; i < gcvSRAM_EXT_COUNT; i++) |
| { |
| device->extSRAMGPUBases[i] = gcvINVALID_PHYSICAL_ADDRESS; |
| device->extSRAMBases[i] = gcvINVALID_PHYSICAL_ADDRESS; |
| device->extSRAMSizes[i] = 0; |
| } |
| |
| device->defaultHwType = gcvHARDWARE_INVALID; |
| |
| gcmkONERROR(gckOS_CreateMutex(Os, &device->stuckDumpMutex)); |
| gcmkONERROR(gckOS_CreateMutex(Os, &device->commitMutex)); |
| |
| device->os = Os; |
| device->showSRAMMapInfo = 0; |
| |
| *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; |
| |
| gcmkHEADER_ARG("Device=%p Core=%d ChipID=%d Context=%p Kernel=%p", |
| Device, Core, ChipID, Context, 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; |
| } |
| |
| gcmkVERIFY_OK( |
| gckKERNEL_GetHardwareType(kernel, |
| &kernelType)); |
| |
| 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; |
| } |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| gcmkFOOTER(); |
| 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.coreIndexs[i] = info[i].core; |
| } |
| |
| 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 %s", gcvVERSION_STRING); |
| #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; |
| gctUINT32 processID = 0; |
| gcsCORE_INFO *info = Device->coreInfoArray; |
| |
| coreList = &Device->map[type]; |
| |
| /* Get the current process ID. */ |
| gckOS_GetProcessID(&processID); |
| |
| for (i = 0; i < Device->coreNum; i++) |
| { |
| if (type == gcvHARDWARE_3D || type == gcvHARDWARE_3D2D || type == gcvHARDWARE_VIP) |
| { |
| kernel = info[i].kernel; |
| } |
| else |
| { |
| kernel = coreList->kernels[i]; |
| } |
| |
| kernel->timeOut = Interface->u.SetTimeOut.timeOut; |
| |
| kernel->timeoutPID = processID; |
| } |
| #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. */ |
| if (type == gcvHARDWARE_3D || type == gcvHARDWARE_3D2D || type == gcvHARDWARE_VIP) |
| { |
| kernel = Device->coreInfoArray[coreIndex].kernel; |
| } |
| else |
| { |
| kernel = Device->map[type].kernels[coreIndex]; |
| } |
| |
| { |
| status = gckKERNEL_Dispatch(kernel, Device, 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; |
| } |
| |
| #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_GetPhysicalFromHandle( |
| Kernel->os, |
| Physical, |
| i * 4096, |
| &phys |
| ); |
| |
| if (status == gcvSTATUS_NOT_SUPPORTED) |
| { |
| gcmkONERROR(gckOS_GetPhysicalAddress( |
| Kernel->os, |
| logical, |
| &phys |
| )); |
| |
| gcmkVERIFY_OK(gckOS_CPUPhysicalToGPUPhysical(Kernel->os, phys, &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 |
| |