| /**************************************************************************** |
| * |
| * 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" |
| |
| #define _GC_OBJ_ZONE gcvZONE_VIDMEM |
| |
| /******************************************************************************\ |
| ******************************* Private Functions ****************************** |
| \******************************************************************************/ |
| |
| /******************************************************************************* |
| ** |
| ** _Split |
| ** |
| ** Split a node on the required byte boundary. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gcuVIDMEM_NODE_PTR Node |
| ** Pointer to the node to split. |
| ** |
| ** gctSIZE_T Bytes |
| ** Number of bytes to keep in the node. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| ** |
| ** RETURNS: |
| ** |
| ** gctBOOL |
| ** gcvTRUE if the node was split successfully, or gcvFALSE if there is an |
| ** error. |
| ** |
| */ |
| static gctBOOL |
| _Split( |
| IN gckOS Os, |
| IN gcuVIDMEM_NODE_PTR Node, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| gcuVIDMEM_NODE_PTR node; |
| gctPOINTER pointer = gcvNULL; |
| |
| /* Make sure the byte boundary makes sense. */ |
| if ((Bytes <= 0) || (Bytes > Node->VidMem.bytes)) |
| { |
| return gcvFALSE; |
| } |
| |
| /* Allocate a new gcuVIDMEM_NODE object. */ |
| if (gcmIS_ERROR(gckOS_Allocate(Os, |
| gcmSIZEOF(gcuVIDMEM_NODE), |
| &pointer))) |
| { |
| /* Error. */ |
| return gcvFALSE; |
| } |
| |
| node = pointer; |
| |
| /* Initialize gcuVIDMEM_NODE structure. */ |
| node->VidMem.offset = Node->VidMem.offset + Bytes; |
| node->VidMem.bytes = Node->VidMem.bytes - Bytes; |
| node->VidMem.alignment = 0; |
| node->VidMem.locked = 0; |
| node->VidMem.parent = Node->VidMem.parent; |
| node->VidMem.pool = Node->VidMem.pool; |
| node->VidMem.processID = 0; |
| node->VidMem.logical = gcvNULL; |
| node->VidMem.kvaddr = gcvNULL; |
| |
| /* Insert node behind specified node. */ |
| node->VidMem.next = Node->VidMem.next; |
| node->VidMem.prev = Node; |
| Node->VidMem.next = node->VidMem.next->VidMem.prev = node; |
| |
| /* Insert free node behind specified node. */ |
| node->VidMem.nextFree = Node->VidMem.nextFree; |
| node->VidMem.prevFree = Node; |
| Node->VidMem.nextFree = node->VidMem.nextFree->VidMem.prevFree = node; |
| |
| /* Adjust size of specified node. */ |
| Node->VidMem.bytes = Bytes; |
| |
| /* Success. */ |
| return gcvTRUE; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** _Merge |
| ** |
| ** Merge two adjacent nodes together. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gcuVIDMEM_NODE_PTR Node |
| ** Pointer to the first of the two nodes to merge. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| ** |
| */ |
| static gceSTATUS |
| _Merge( |
| IN gckOS Os, |
| IN gcuVIDMEM_NODE_PTR Node |
| ) |
| { |
| gcuVIDMEM_NODE_PTR node; |
| gceSTATUS status; |
| |
| /* Save pointer to next node. */ |
| node = Node->VidMem.next; |
| |
| /* This is a good time to make sure the heap is not corrupted. */ |
| if (Node->VidMem.offset + Node->VidMem.bytes != node->VidMem.offset) |
| { |
| /* Corrupted heap. */ |
| gcmkASSERT( |
| Node->VidMem.offset + Node->VidMem.bytes == node->VidMem.offset); |
| return gcvSTATUS_HEAP_CORRUPTED; |
| } |
| |
| /* Adjust byte count. */ |
| Node->VidMem.bytes += node->VidMem.bytes; |
| |
| /* Unlink next node from linked list. */ |
| Node->VidMem.next = node->VidMem.next; |
| Node->VidMem.nextFree = node->VidMem.nextFree; |
| |
| Node->VidMem.next->VidMem.prev = |
| Node->VidMem.nextFree->VidMem.prevFree = Node; |
| |
| /* Free next node. */ |
| status = gcmkOS_SAFE_FREE(Os, node); |
| return status; |
| } |
| |
| /******************************************************************************\ |
| ******************************* gckVIDMEM API Code ****************************** |
| \******************************************************************************/ |
| |
| /******************************************************************************* |
| ** |
| ** gckVIDMEM_Construct |
| ** |
| ** Construct a new gckVIDMEM object. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to an gckOS object. |
| ** |
| ** gctPHYS_ADDR_T PhysicalBase |
| ** Base physical address for the video memory heap. |
| ** |
| ** gctSIZE_T Bytes |
| ** Number of bytes in the video memory heap. |
| ** |
| ** gctSIZE_T Threshold |
| ** Minimum number of bytes beyond am allocation before the node is |
| ** split. Can be used as a minimum alignment requirement. |
| ** |
| ** gctSIZE_T BankSize |
| ** Number of bytes per physical memory bank. Used by bank |
| ** optimization. |
| ** |
| ** OUTPUT: |
| ** |
| ** gckVIDMEM * Memory |
| ** Pointer to a variable that will hold the pointer to the gckVIDMEM |
| ** object. |
| */ |
| gceSTATUS |
| gckVIDMEM_Construct( |
| IN gckOS Os, |
| IN gctPHYS_ADDR_T PhysicalBase, |
| IN gctSIZE_T Bytes, |
| IN gctSIZE_T Threshold, |
| IN gctSIZE_T BankSize, |
| OUT gckVIDMEM * Memory |
| ) |
| { |
| gckVIDMEM memory = gcvNULL; |
| gceSTATUS status; |
| gcuVIDMEM_NODE_PTR node; |
| gctINT i, banks = 0; |
| gctPOINTER pointer = gcvNULL; |
| gctUINT32 heapBytes; |
| gctUINT32 bankSize; |
| gctUINT32 base = 0; |
| |
| gcmkHEADER_ARG("Os=0x%x PhysicalBase=%12llx Bytes=%lu Threshold=%lu " |
| "BankSize=%lu", |
| Os, PhysicalBase, Bytes, Threshold, BankSize); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| gcmkVERIFY_ARGUMENT(Memory != gcvNULL); |
| |
| gcmkSAFECASTSIZET(heapBytes, Bytes); |
| gcmkSAFECASTSIZET(bankSize, BankSize); |
| |
| /* Allocate the gckVIDMEM object. */ |
| gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(struct _gckVIDMEM), &pointer)); |
| gckOS_ZeroMemory(pointer, gcmSIZEOF(struct _gckVIDMEM)); |
| |
| memory = pointer; |
| |
| /* Initialize the gckVIDMEM object. */ |
| memory->object.type = gcvOBJ_VIDMEM; |
| memory->os = Os; |
| |
| /* Set video memory heap information. */ |
| memory->physicalBase = PhysicalBase; |
| memory->bytes = heapBytes; |
| memory->freeBytes = heapBytes; |
| memory->minFreeBytes = heapBytes; |
| memory->capability = ~0u; |
| memory->threshold = Threshold; |
| memory->mutex = gcvNULL; |
| |
| /* Walk all possible banks. */ |
| for (i = 0; i < gcmCOUNTOF(memory->sentinel); ++i) |
| { |
| gctUINT32 bytes; |
| |
| if (BankSize == 0) |
| { |
| /* Use all bytes for the first bank. */ |
| bytes = heapBytes; |
| } |
| else |
| { |
| /* Compute number of bytes for this bank. */ |
| bytes = gcmALIGN(base + 1, bankSize) - base; |
| |
| if (bytes > heapBytes) |
| { |
| /* Make sure we don't exceed the total number of bytes. */ |
| bytes = heapBytes; |
| } |
| } |
| |
| if (bytes == 0) |
| { |
| /* Mark heap is not used. */ |
| memory->sentinel[i].VidMem.next = |
| memory->sentinel[i].VidMem.prev = |
| memory->sentinel[i].VidMem.nextFree = |
| memory->sentinel[i].VidMem.prevFree = gcvNULL; |
| continue; |
| } |
| |
| /* Allocate one gcuVIDMEM_NODE union. */ |
| gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(gcuVIDMEM_NODE), &pointer)); |
| |
| node = pointer; |
| |
| /* Initialize gcuVIDMEM_NODE union. */ |
| node->VidMem.parent = memory; |
| |
| node->VidMem.next = |
| node->VidMem.prev = |
| node->VidMem.nextFree = |
| node->VidMem.prevFree = &memory->sentinel[i]; |
| |
| node->VidMem.offset = base; |
| node->VidMem.bytes = bytes; |
| node->VidMem.alignment = 0; |
| node->VidMem.pool = gcvPOOL_UNKNOWN; |
| |
| node->VidMem.locked = 0; |
| |
| node->VidMem.processID = 0; |
| node->VidMem.logical = gcvNULL; |
| |
| node->VidMem.kvaddr = gcvNULL; |
| |
| /* Initialize the linked list of nodes. */ |
| memory->sentinel[i].VidMem.next = |
| memory->sentinel[i].VidMem.prev = |
| memory->sentinel[i].VidMem.nextFree = |
| memory->sentinel[i].VidMem.prevFree = node; |
| |
| /* Mark sentinel. */ |
| memory->sentinel[i].VidMem.bytes = 0; |
| |
| /* Adjust address for next bank. */ |
| base += bytes; |
| heapBytes -= bytes; |
| banks ++; |
| } |
| |
| /* Assign all the bank mappings. */ |
| memory->mapping[gcvVIDMEM_TYPE_COLOR_BUFFER] = banks - 1; |
| memory->mapping[gcvVIDMEM_TYPE_BITMAP] = banks - 1; |
| |
| if (banks > 1) --banks; |
| memory->mapping[gcvVIDMEM_TYPE_DEPTH_BUFFER] = banks - 1; |
| memory->mapping[gcvVIDMEM_TYPE_HZ_BUFFER] = banks - 1; |
| |
| if (banks > 1) --banks; |
| memory->mapping[gcvVIDMEM_TYPE_TEXTURE] = banks - 1; |
| |
| if (banks > 1) --banks; |
| memory->mapping[gcvVIDMEM_TYPE_VERTEX_BUFFER] = banks - 1; |
| |
| if (banks > 1) --banks; |
| memory->mapping[gcvVIDMEM_TYPE_INDEX_BUFFER] = banks - 1; |
| |
| if (banks > 1) --banks; |
| memory->mapping[gcvVIDMEM_TYPE_TILE_STATUS] = banks - 1; |
| |
| if (banks > 1) --banks; |
| memory->mapping[gcvVIDMEM_TYPE_COMMAND] = banks - 1; |
| |
| if (banks > 1) --banks; |
| memory->mapping[gcvVIDMEM_TYPE_GENERIC] = 0; |
| |
| memory->mapping[gcvVIDMEM_TYPE_ICACHE] = 0; |
| memory->mapping[gcvVIDMEM_TYPE_TXDESC] = 0; |
| memory->mapping[gcvVIDMEM_TYPE_FENCE] = 0; |
| memory->mapping[gcvVIDMEM_TYPE_TFBHEADER] = 0; |
| |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, |
| "[GALCORE] INDEX: bank %d", |
| memory->mapping[gcvVIDMEM_TYPE_INDEX_BUFFER]); |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, |
| "[GALCORE] VERTEX: bank %d", |
| memory->mapping[gcvVIDMEM_TYPE_VERTEX_BUFFER]); |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, |
| "[GALCORE] TEXTURE: bank %d", |
| memory->mapping[gcvVIDMEM_TYPE_TEXTURE]); |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, |
| "[GALCORE] RENDER_TARGET: bank %d", |
| memory->mapping[gcvVIDMEM_TYPE_COLOR_BUFFER]); |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, |
| "[GALCORE] DEPTH: bank %d", |
| memory->mapping[gcvVIDMEM_TYPE_DEPTH_BUFFER]); |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, |
| "[GALCORE] TILE_STATUS: bank %d", |
| memory->mapping[gcvVIDMEM_TYPE_TILE_STATUS]); |
| |
| /* Allocate the mutex. */ |
| gcmkONERROR(gckOS_CreateMutex(Os, &memory->mutex)); |
| |
| /* Return pointer to the gckVIDMEM object. */ |
| *Memory = memory; |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Memory=0x%x", *Memory); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Roll back. */ |
| if (memory != gcvNULL) |
| { |
| if (memory->mutex != gcvNULL) |
| { |
| /* Delete the mutex. */ |
| gcmkVERIFY_OK(gckOS_DeleteMutex(Os, memory->mutex)); |
| } |
| |
| for (i = 0; i < banks; ++i) |
| { |
| /* Free the heap. */ |
| gcmkASSERT(memory->sentinel[i].VidMem.next != gcvNULL); |
| gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Os, memory->sentinel[i].VidMem.next)); |
| } |
| |
| /* Free the object. */ |
| gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Os, memory)); |
| } |
| |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckVIDMEM_Destroy |
| ** |
| ** Destroy an gckVIDMEM object. |
| ** |
| ** INPUT: |
| ** |
| ** gckVIDMEM Memory |
| ** Pointer to an gckVIDMEM object to destroy. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| gceSTATUS |
| gckVIDMEM_Destroy( |
| IN gckVIDMEM Memory |
| ) |
| { |
| gcuVIDMEM_NODE_PTR node, next; |
| gctINT i; |
| |
| gcmkHEADER_ARG("Memory=0x%x", Memory); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Memory, gcvOBJ_VIDMEM); |
| |
| /* Walk all sentinels. */ |
| for (i = 0; i < gcmCOUNTOF(Memory->sentinel); ++i) |
| { |
| /* Bail out of the heap is not used. */ |
| if (Memory->sentinel[i].VidMem.next == gcvNULL) |
| { |
| break; |
| } |
| |
| /* Walk all the nodes until we reach the sentinel. */ |
| for (node = Memory->sentinel[i].VidMem.next; |
| node->VidMem.bytes != 0; |
| node = next) |
| { |
| /* Save pointer to the next node. */ |
| next = node->VidMem.next; |
| |
| /* Free the node. */ |
| gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Memory->os, node)); |
| } |
| } |
| |
| /* Free the mutex. */ |
| gcmkVERIFY_OK(gckOS_DeleteMutex(Memory->os, Memory->mutex)); |
| |
| /* Mark the object as unknown. */ |
| Memory->object.type = gcvOBJ_UNKNOWN; |
| |
| /* Free the gckVIDMEM object. */ |
| gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Memory->os, Memory)); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| #if gcdENABLE_BANK_ALIGNMENT |
| |
| #if !gcdBANK_BIT_START |
| #error gcdBANK_BIT_START not defined. |
| #endif |
| |
| #if !gcdBANK_BIT_END |
| #error gcdBANK_BIT_END not defined. |
| #endif |
| /******************************************************************************* |
| ** _GetSurfaceBankAlignment |
| ** |
| ** Return the required offset alignment required to the make BaseAddress |
| ** aligned properly. |
| ** |
| ** INPUT: |
| ** |
| ** gckOS Os |
| ** Pointer to gcoOS object. |
| ** |
| ** gceVIDMEM_TYPE Type |
| ** Type of allocation. |
| ** |
| ** gctUINT32 BaseAddress |
| ** Base address of current video memory node. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctUINT32_PTR AlignmentOffset |
| ** Pointer to a variable that will hold the number of bytes to skip in |
| ** the current video memory node in order to make the alignment bank |
| ** aligned. |
| */ |
| static gceSTATUS |
| _GetSurfaceBankAlignment( |
| IN gckKERNEL Kernel, |
| IN gceVIDMEM_TYPE Type, |
| IN gctUINT32 BaseAddress, |
| OUT gctUINT32_PTR AlignmentOffset |
| ) |
| { |
| gctUINT32 bank; |
| /* To retrieve the bank. */ |
| static const gctUINT32 bankMask = (0xFFFFFFFF << gcdBANK_BIT_START) |
| ^ (0xFFFFFFFF << (gcdBANK_BIT_END + 1)); |
| |
| /* To retrieve the bank and all the lower bytes. */ |
| static const gctUINT32 byteMask = ~(0xFFFFFFFF << (gcdBANK_BIT_END + 1)); |
| |
| gcmkHEADER_ARG("Type=%d BaseAddress=0x%x ", Type, BaseAddress); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_ARGUMENT(AlignmentOffset != gcvNULL); |
| |
| switch (Type) |
| { |
| case gcvVIDMEM_TYPE_COLOR_BUFFER: |
| bank = (BaseAddress & bankMask) >> (gcdBANK_BIT_START); |
| |
| /* Align to the first bank. */ |
| *AlignmentOffset = (bank == 0) ? |
| 0 : |
| ((1 << (gcdBANK_BIT_END + 1)) + 0) - (BaseAddress & byteMask); |
| break; |
| |
| case gcvVIDMEM_TYPE_DEPTH_BUFFER: |
| bank = (BaseAddress & bankMask) >> (gcdBANK_BIT_START); |
| |
| /* Align to the third bank. */ |
| *AlignmentOffset = (bank == 2) ? |
| 0 : |
| ((1 << (gcdBANK_BIT_END + 1)) + (2 << gcdBANK_BIT_START)) - (BaseAddress & byteMask); |
| |
| /* Minimum 256 byte alignment needed for fast_msaa. */ |
| if ((gcdBANK_CHANNEL_BIT > 7) || |
| ((gckHARDWARE_IsFeatureAvailable(Kernel->hardware, gcvFEATURE_FAST_MSAA) != gcvSTATUS_TRUE) && |
| (gckHARDWARE_IsFeatureAvailable(Kernel->hardware, gcvFEATURE_SMALL_MSAA) != gcvSTATUS_TRUE))) |
| { |
| /* Add a channel offset at the channel bit. */ |
| *AlignmentOffset += (1 << gcdBANK_CHANNEL_BIT); |
| } |
| break; |
| |
| default: |
| /* no alignment needed. */ |
| *AlignmentOffset = 0; |
| } |
| |
| /* Return the status. */ |
| gcmkFOOTER_ARG("*AlignmentOffset=%u", *AlignmentOffset); |
| return gcvSTATUS_OK; |
| } |
| #endif |
| |
| static gcuVIDMEM_NODE_PTR |
| _FindNode( |
| IN gckKERNEL Kernel, |
| IN gckVIDMEM Memory, |
| IN gctINT Bank, |
| IN gctSIZE_T Bytes, |
| IN gceVIDMEM_TYPE Type, |
| IN OUT gctUINT32_PTR Alignment |
| ) |
| { |
| gcuVIDMEM_NODE_PTR node; |
| gctUINT32 alignment; |
| |
| #if gcdENABLE_BANK_ALIGNMENT |
| gctUINT32 bankAlignment; |
| gceSTATUS status; |
| #endif |
| |
| if (Memory->sentinel[Bank].VidMem.nextFree == gcvNULL) |
| { |
| /* No free nodes left. */ |
| return gcvNULL; |
| } |
| |
| #if gcdENABLE_BANK_ALIGNMENT |
| /* Walk all free nodes until we have one that is big enough or we have |
| ** reached the sentinel. */ |
| for (node = Memory->sentinel[Bank].VidMem.nextFree; |
| node->VidMem.bytes != 0; |
| node = node->VidMem.nextFree) |
| { |
| if (node->VidMem.bytes < Bytes) |
| { |
| continue; |
| } |
| |
| gcmkONERROR(_GetSurfaceBankAlignment( |
| Kernel, |
| Type, |
| (gctUINT32)(node->VidMem.parent->physicalBase + node->VidMem.offset), |
| &bankAlignment)); |
| |
| bankAlignment = gcmALIGN(bankAlignment, *Alignment); |
| |
| /* Compute number of bytes to skip for alignment. */ |
| alignment = (*Alignment == 0) |
| ? 0 |
| : (*Alignment - (node->VidMem.offset % *Alignment)); |
| |
| if (alignment == *Alignment) |
| { |
| /* Node is already aligned. */ |
| alignment = 0; |
| } |
| |
| if (node->VidMem.bytes >= Bytes + alignment + bankAlignment) |
| { |
| /* This node is big enough. */ |
| *Alignment = alignment + bankAlignment; |
| return node; |
| } |
| } |
| #endif |
| |
| /* Walk all free nodes until we have one that is big enough or we have |
| reached the sentinel. */ |
| for (node = Memory->sentinel[Bank].VidMem.nextFree; |
| node->VidMem.bytes != 0; |
| node = node->VidMem.nextFree) |
| { |
| gctUINT offset; |
| |
| gctINT modulo; |
| |
| gcmkSAFECASTSIZET(offset, node->VidMem.offset); |
| |
| modulo = gckMATH_ModuloInt(offset, *Alignment); |
| |
| /* Compute number of bytes to skip for alignment. */ |
| alignment = (*Alignment == 0) ? 0 : (*Alignment - modulo); |
| |
| if (alignment == *Alignment) |
| { |
| /* Node is already aligned. */ |
| alignment = 0; |
| } |
| |
| if (node->VidMem.bytes >= Bytes + alignment) |
| { |
| /* This node is big enough. */ |
| *Alignment = alignment; |
| return node; |
| } |
| } |
| |
| #if gcdENABLE_BANK_ALIGNMENT |
| OnError: |
| #endif |
| /* Not enough memory. */ |
| return gcvNULL; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckVIDMEM_AllocateLinear |
| ** |
| ** Allocate linear memory from the gckVIDMEM object. |
| ** |
| ** INPUT: |
| ** |
| ** gckVIDMEM Memory |
| ** Pointer to an gckVIDMEM object. |
| ** |
| ** gctSIZE_T Bytes |
| ** Number of bytes to allocate. |
| ** |
| ** gctUINT32 Alignment |
| ** Byte alignment for allocation. |
| ** |
| ** gceVIDMEM_TYPE Type |
| ** Type of surface to allocate (use by bank optimization). |
| ** |
| ** gctUINT32 Flag |
| ** Flag of allocatetion. |
| ** |
| ** gctBOOL Specified |
| ** If user must use this pool, it should set Specified to gcvTRUE, |
| ** otherwise allocator may reserve some memory for other usage, such |
| ** as small block size allocation request. |
| ** |
| ** OUTPUT: |
| ** |
| ** gcuVIDMEM_NODE_PTR * Node |
| ** Pointer to a variable that will hold the allocated memory node. |
| */ |
| static gceSTATUS |
| gckVIDMEM_AllocateLinear( |
| IN gckKERNEL Kernel, |
| IN gckVIDMEM Memory, |
| IN gctSIZE_T Bytes, |
| IN gctUINT32 Alignment, |
| IN gceVIDMEM_TYPE Type, |
| IN gctUINT32 Flag, |
| IN gctBOOL Specified, |
| OUT gcuVIDMEM_NODE_PTR * Node |
| ) |
| { |
| gceSTATUS status; |
| gcuVIDMEM_NODE_PTR node; |
| gctUINT32 alignment; |
| gctINT bank, i; |
| gctBOOL acquired = gcvFALSE; |
| gctUINT64 mappingInOne = 1; |
| |
| gcmkHEADER_ARG("Memory=0x%x Bytes=%lu Alignment=%u Type=%d", |
| Memory, Bytes, Alignment, Type); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Memory, gcvOBJ_VIDMEM); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| gcmkVERIFY_ARGUMENT(Node != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Type < gcvVIDMEM_TYPE_COUNT); |
| |
| /* Acquire the mutex. */ |
| gcmkONERROR(gckOS_AcquireMutex(Memory->os, Memory->mutex, gcvINFINITE)); |
| |
| acquired = gcvTRUE; |
| |
| if (Bytes > Memory->freeBytes) |
| { |
| /* Not enough memory. */ |
| status = gcvSTATUS_OUT_OF_MEMORY; |
| goto OnError; |
| } |
| |
| #if gcdSMALL_BLOCK_SIZE |
| if ((Memory->freeBytes < (Memory->bytes/gcdRATIO_FOR_SMALL_MEMORY)) |
| && (Bytes >= gcdSMALL_BLOCK_SIZE) |
| && (Specified == gcvFALSE) |
| ) |
| { |
| /* The left memory is for small memory.*/ |
| status = gcvSTATUS_OUT_OF_MEMORY; |
| goto OnError; |
| } |
| #endif |
| |
| /* Find the default bank for this surface type. */ |
| gcmkASSERT((gctINT) Type < gcmCOUNTOF(Memory->mapping)); |
| bank = Memory->mapping[Type]; |
| alignment = Alignment; |
| |
| /* Find a free node in the default bank. */ |
| node = _FindNode(Kernel, Memory, bank, Bytes, Type, &alignment); |
| |
| /* Out of memory? */ |
| if (node == gcvNULL) |
| { |
| /* Walk all lower banks. */ |
| for (i = bank - 1; i >= 0; --i) |
| { |
| /* Find a free node inside the current bank. */ |
| node = _FindNode(Kernel, Memory, i, Bytes, Type, &alignment); |
| if (node != gcvNULL) |
| { |
| break; |
| } |
| } |
| } |
| |
| if (node == gcvNULL) |
| { |
| /* Walk all upper banks. */ |
| for (i = bank + 1; i < gcmCOUNTOF(Memory->sentinel); ++i) |
| { |
| if (Memory->sentinel[i].VidMem.nextFree == gcvNULL) |
| { |
| /* Abort when we reach unused banks. */ |
| break; |
| } |
| |
| /* Find a free node inside the current bank. */ |
| node = _FindNode(Kernel, Memory, i, Bytes, Type, &alignment); |
| if (node != gcvNULL) |
| { |
| break; |
| } |
| } |
| } |
| |
| if (node == gcvNULL) |
| { |
| /* Out of memory. */ |
| status = gcvSTATUS_OUT_OF_MEMORY; |
| goto OnError; |
| } |
| |
| /* Do we have an alignment? */ |
| if (alignment > 0) |
| { |
| /* Split the node so it is aligned. */ |
| if (_Split(Memory->os, node, alignment)) |
| { |
| /* Successful split, move to aligned node. */ |
| node = node->VidMem.next; |
| |
| /* Remove alignment. */ |
| alignment = 0; |
| } |
| } |
| |
| /* Do we have enough memory after the allocation to split it? */ |
| if (node->VidMem.bytes - Bytes > Memory->threshold) |
| { |
| /* Adjust the node size. */ |
| _Split(Memory->os, node, Bytes); |
| } |
| |
| /* Remove the node from the free list. */ |
| node->VidMem.prevFree->VidMem.nextFree = node->VidMem.nextFree; |
| node->VidMem.nextFree->VidMem.prevFree = node->VidMem.prevFree; |
| node->VidMem.nextFree = |
| node->VidMem.prevFree = gcvNULL; |
| |
| /* Fill in the information. */ |
| node->VidMem.alignment = alignment; |
| node->VidMem.parent = Memory; |
| node->VidMem.logical = gcvNULL; |
| gcmkONERROR(gckOS_GetProcessID(&node->VidMem.processID)); |
| |
| /* Adjust the number of free bytes. */ |
| Memory->freeBytes -= node->VidMem.bytes; |
| |
| if (Memory->freeBytes < Memory->minFreeBytes) |
| { |
| Memory->minFreeBytes = Memory->freeBytes; |
| } |
| |
| gckOS_QueryOption(Memory->os, "allMapInOne", &mappingInOne); |
| if (!mappingInOne) |
| { |
| gcmkONERROR(gckOS_RequestReservedMemory( |
| Memory->os, |
| Memory->physicalBase + node->VidMem.offset, |
| node->VidMem.bytes, |
| "gal reserved memory", |
| gcvTRUE, |
| &node->VidMem.physical |
| )); |
| } |
| |
| /* Release the mutex. */ |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Memory->os, Memory->mutex)); |
| |
| /* Return the pointer to the node. */ |
| *Node = node; |
| |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, |
| "Allocated %u bytes @ 0x%x [0x%08X]", |
| node->VidMem.bytes, node, node->VidMem.offset); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Node=0x%x", *Node); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (acquired) |
| { |
| /* Release the mutex. */ |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Memory->os, Memory->mutex)); |
| } |
| |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckVIDMEM_AllocateVirtual |
| ** |
| ** Construct a new gcuVIDMEM_NODE union for virtual memory. |
| ** |
| ** INPUT: |
| ** |
| ** gckKERNEL Kernel |
| ** Pointer to an gckKERNEL object. |
| ** |
| ** gctSIZE_T Bytes |
| ** Number of byte to allocate. |
| ** |
| ** OUTPUT: |
| ** |
| ** gcuVIDMEM_NODE_PTR * Node |
| ** Pointer to a variable that receives the gcuVIDMEM_NODE union pointer. |
| */ |
| static gceSTATUS |
| gckVIDMEM_AllocateVirtual( |
| IN gckKERNEL Kernel, |
| IN gctUINT32 Flag, |
| IN gctSIZE_T Bytes, |
| OUT gcuVIDMEM_NODE_PTR * Node |
| ) |
| { |
| gckOS os; |
| gceSTATUS status; |
| gcuVIDMEM_NODE_PTR node = gcvNULL; |
| gctPOINTER pointer = gcvNULL; |
| gctINT i; |
| |
| gcmkHEADER_ARG("Kernel=0x%x Flag=%x Bytes=%lu", Kernel, Flag, Bytes); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| gcmkVERIFY_ARGUMENT(Node != gcvNULL); |
| |
| /* Extract the gckOS object pointer. */ |
| os = Kernel->os; |
| gcmkVERIFY_OBJECT(os, gcvOBJ_OS); |
| |
| /* Allocate an gcuVIDMEM_NODE union. */ |
| gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(gcuVIDMEM_NODE), &pointer)); |
| |
| node = pointer; |
| |
| /* Initialize gcuVIDMEM_NODE union for virtual memory. */ |
| node->Virtual.kernel = Kernel; |
| node->Virtual.contiguous = Flag & gcvALLOC_FLAG_CONTIGUOUS; |
| node->Virtual.logical = gcvNULL; |
| node->Virtual.kvaddr = gcvNULL; |
| node->Virtual.bytes = Bytes; |
| node->Virtual.secure = (Flag & gcvALLOC_FLAG_SECURITY) != 0; |
| node->Virtual.onFault = (Flag & gcvALLOC_FLAG_ALLOC_ON_FAULT) != 0; |
| |
| for (i = 0; i < gcvHARDWARE_NUM_TYPES; i++) |
| { |
| node->Virtual.lockeds[i] = 0; |
| node->Virtual.pageTables[i] = gcvNULL; |
| } |
| |
| /* Allocate the virtual memory. */ |
| gcmkONERROR( |
| gckOS_AllocatePagedMemory(os, |
| Flag, |
| &node->Virtual.bytes, |
| &node->Virtual.gid, |
| &node->Virtual.physical)); |
| |
| /* Calculate required GPU page (4096) count. */ |
| /* Assume start address is 4096 aligned. */ |
| node->Virtual.pageCount = (gctSIZE_T)(((gctUINT64)node->Virtual.bytes + (4096 - 1)) >> 12); |
| |
| /* Return pointer to the gcuVIDMEM_NODE union. */ |
| *Node = node; |
| |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, |
| "Created virtual node 0x%x for %u bytes @ 0x%x", |
| node, Bytes, node->Virtual.physical); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Node=0x%x", *Node); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Roll back. */ |
| if (node != gcvNULL) |
| { |
| /* Free the structure. */ |
| gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, node)); |
| } |
| |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| static gceSTATUS |
| _AddToBlockList( |
| IN gckKERNEL Kernel, |
| IN gckVIDMEM_BLOCK VidMemBlock |
| ) |
| { |
| VidMemBlock->next = Kernel->vidMemBlock; |
| Kernel->vidMemBlock = VidMemBlock; |
| |
| return gcvSTATUS_OK; |
| } |
| |
| static gceSTATUS |
| _RemoveFromBlockList( |
| IN gckKERNEL Kernel, |
| IN gckVIDMEM_BLOCK VidMemBlock |
| ) |
| { |
| gckVIDMEM_BLOCK vidMemBlock; |
| gckVIDMEM_BLOCK previous = gcvNULL; |
| gceHARDWARE_TYPE hwType; |
| |
| gcmkVERIFY_OK( |
| gckKERNEL_GetHardwareType(Kernel, |
| &hwType)); |
| |
| for (vidMemBlock = Kernel->vidMemBlock; vidMemBlock != gcvNULL; vidMemBlock = vidMemBlock->next) |
| { |
| if (vidMemBlock->addresses[hwType] == VidMemBlock->addresses[hwType]) |
| { |
| if (previous) |
| { |
| previous->next = vidMemBlock->next; |
| } |
| else |
| { |
| Kernel->vidMemBlock = vidMemBlock->next; |
| } |
| vidMemBlock->next = gcvNULL; |
| |
| break; |
| } |
| previous = vidMemBlock; |
| } |
| |
| return gcvSTATUS_OK; |
| } |
| |
| static gckVIDMEM_BLOCK |
| _FindFreeBlock( |
| IN gckKERNEL Kernel, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| gckVIDMEM_BLOCK vidMemBlock; |
| |
| for (vidMemBlock = Kernel->vidMemBlock; vidMemBlock != gcvNULL; vidMemBlock = vidMemBlock->next) |
| { |
| if (vidMemBlock->freeBytes >= Bytes) |
| { |
| /* Found the block */ |
| break; |
| } |
| } |
| |
| return vidMemBlock; |
| } |
| |
| static gceSTATUS |
| _SplitVirtualChunk( |
| IN gckOS Os, |
| IN gcuVIDMEM_NODE_PTR Node, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| gceSTATUS status = gcvSTATUS_OK; |
| gcuVIDMEM_NODE_PTR node = gcvNULL; |
| gctPOINTER pointer; |
| gctINT i; |
| |
| if ((Bytes <= 0) || (Bytes > Node->VirtualChunk.bytes)) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| |
| /* Allocate a new gcuVIDMEM_NODE object. */ |
| gcmkONERROR(gckOS_Allocate(Os, |
| gcmSIZEOF(gcuVIDMEM_NODE), |
| &pointer)); |
| |
| node = pointer; |
| |
| /* Intialize the new gcuVIDMEM_NODE. */ |
| node->VirtualChunk.offset = Node->VirtualChunk.offset + Bytes; |
| node->VirtualChunk.bytes = Node->VirtualChunk.bytes - Bytes; |
| node->VirtualChunk.parent = Node->VirtualChunk.parent; |
| node->VirtualChunk.kernel = Node->VirtualChunk.kernel; |
| node->VirtualChunk.kvaddr = gcvNULL; |
| |
| for (i = 0; i < gcvHARDWARE_NUM_TYPES; i++) |
| { |
| node->VirtualChunk.lockeds[i] = 0; |
| } |
| |
| /* Insert chunk behind specified chunk. */ |
| node->VirtualChunk.next = Node->VirtualChunk.next; |
| node->VirtualChunk.prev = Node; |
| Node->VirtualChunk.next = node->VirtualChunk.next->VirtualChunk.prev = node; |
| |
| /* Insert free chunk behind specified chunk. */ |
| node->VirtualChunk.nextFree = Node->VirtualChunk.nextFree; |
| node->VirtualChunk.prevFree = Node; |
| Node->VirtualChunk.nextFree = node->VirtualChunk.nextFree->VirtualChunk.prevFree = node; |
| |
| /* Adjust size of specified chunk. */ |
| Node->VirtualChunk.bytes = Bytes; |
| |
| OnError: |
| return status; |
| } |
| |
| static gceSTATUS |
| _MergeVirtualChunk( |
| IN gckOS Os, |
| IN gcuVIDMEM_NODE_PTR Node |
| ) |
| { |
| gcuVIDMEM_NODE_PTR node; |
| gceSTATUS status = gcvSTATUS_OK; |
| |
| node = Node->VirtualChunk.next; |
| |
| if (Node->VirtualChunk.offset + Node->VirtualChunk.bytes != node->VirtualChunk.offset) |
| { |
| /* Corrupted heap. */ |
| gcmkASSERT( |
| Node->VirtualChunk.offset + Node->VirtualChunk.bytes == node->VirtualChunk.offset); |
| |
| return gcvSTATUS_HEAP_CORRUPTED; |
| } |
| |
| /* Merge. */ |
| Node->VirtualChunk.bytes += node->VirtualChunk.bytes; |
| |
| /* Unlink next node from linked list. */ |
| Node->VirtualChunk.next = node->VirtualChunk.next; |
| Node->VirtualChunk.nextFree = node->VirtualChunk.nextFree; |
| |
| Node->VirtualChunk.next->VirtualChunk.prev = |
| Node->VirtualChunk.nextFree->VirtualChunk.prevFree = Node; |
| |
| /* Free the next node. */ |
| status = gcmkOS_SAFE_FREE(Os, node); |
| return status; |
| } |
| |
| static gctBOOL |
| _IsVidMemBlockFree( |
| IN gckVIDMEM_BLOCK VidMemBlock |
| ) |
| { |
| return (VidMemBlock->freeBytes == VidMemBlock->bytes); |
| } |
| |
| static gcuVIDMEM_NODE_PTR |
| _FindVirtualChunkNode( |
| IN gckKERNEL Kernel, |
| IN gckVIDMEM_BLOCK VidMemBlock, |
| IN gctSIZE_T Bytes |
| ) |
| { |
| gcuVIDMEM_NODE_PTR node; |
| |
| if (VidMemBlock->node.VirtualChunk.nextFree == gcvNULL) |
| { |
| /* No free chunk left. */ |
| return gcvNULL; |
| } |
| |
| for (node = VidMemBlock->node.VirtualChunk.nextFree; |
| node->VirtualChunk.bytes != 0; |
| node = node->VirtualChunk.nextFree) |
| { |
| if (node->VirtualChunk.bytes >= Bytes) |
| { |
| /* Got it. */ |
| return node; |
| } |
| } |
| |
| return gcvNULL; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** _ConvertPhysical |
| ** |
| ** Convert CPU physical to GPU address for video node. |
| ** |
| ** INPUT: |
| ** gckKERNEL Kernel |
| ** Pointer to an gckKERNEL object. |
| ** |
| ** gcuVIDMEM_NODE_PTR Node |
| ** Pointer to a gcuVIDMEM_NODE union. |
| ** |
| ** gceCORE Core |
| ** Id of current GPU. |
| ** |
| ** gctPHYS_ADDR_T PhysicalAddress |
| ** CPU physical address |
| ** |
| ** OUTPUT: |
| ** gctUINT32 * Address |
| ** A pointer hold the GPU address. |
| */ |
| static gceSTATUS |
| _ConvertPhysical( |
| IN gckKERNEL Kernel, |
| IN gceCORE Core, |
| IN gcuVIDMEM_NODE_PTR Node, |
| IN gckVIDMEM_BLOCK VidMemBlock, |
| IN gctPHYS_ADDR_T PhysicalAddress, |
| OUT gctUINT32 * Address |
| ) |
| { |
| gceSTATUS status; |
| gctUINT64 physical = 0; |
| |
| gcmkHEADER_ARG("Node=0x%X", Node); |
| |
| if ((Node && !Node->Virtual.contiguous) || |
| (VidMemBlock && !VidMemBlock->contiguous)) |
| { |
| /* non-contiguous, mapping is required. */ |
| status = gcvSTATUS_NOT_SUPPORTED; |
| goto OnError; |
| } |
| |
| if ((Node && Node->Virtual.secure) || |
| (VidMemBlock && VidMemBlock->secure)) |
| { |
| /* Secure, mapping is forced. */ |
| status = gcvSTATUS_NOT_SUPPORTED; |
| goto OnError; |
| } |
| |
| /* Convert to GPU physical address. */ |
| gckOS_CPUPhysicalToGPUPhysical(Kernel->os, PhysicalAddress, &physical); |
| |
| |
| if ((physical > gcvMAXUINT32) || |
| (Node && (physical + Node->Virtual.bytes - 1 > gcvMAXUINT32)) || |
| (VidMemBlock && (physical + VidMemBlock->bytes - 1 > gcvMAXUINT32))) |
| { |
| /* Above 4G (32bit), mapping is required currently. */ |
| status = gcvSTATUS_NOT_SUPPORTED; |
| goto OnError; |
| } |
| |
| if (!gckHARDWARE_IsFeatureAvailable(Kernel->hardware, gcvFEATURE_MMU)) |
| { |
| if (physical < Kernel->hardware->baseAddress) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| |
| /* Subtract baseAddress to get a GPU address used for programming. */ |
| physical -= Kernel->hardware->baseAddress; |
| |
| /* 2G upper is virtual space, better to move to gckHARDWARE section. */ |
| if (physical + Node->Virtual.bytes > 0x80000000) |
| { |
| /* End is above 2G, ie virtual space. */ |
| status = gcvSTATUS_NOT_SUPPORTED; |
| goto OnError; |
| } |
| |
| *Address = (gctUINT32)physical; |
| |
| gcmkFOOTER_ARG("*Address=0x%X", *Address); |
| return gcvSTATUS_OK; |
| } |
| else |
| { |
| gctBOOL flatMapped; |
| |
| gcmkONERROR(gckMMU_IsFlatMapped(Kernel->mmu, physical, gcvINVALID_ADDRESS, &flatMapped)); |
| |
| if (!flatMapped) |
| { |
| status = gcvSTATUS_NOT_SUPPORTED; |
| goto OnError; |
| } |
| |
| *Address = (gctUINT32)physical; |
| |
| gcmkFOOTER_ARG("*Address=0x%X", *Address); |
| return gcvSTATUS_OK; |
| } |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| static gceSTATUS |
| gckVIDMEM_MapVidMemBlock( |
| IN gckKERNEL Kernel, |
| IN gckVIDMEM_BLOCK VidMemBlock |
| ) |
| { |
| gceSTATUS status; |
| gckOS os = Kernel->os; |
| gctPHYS_ADDR_T physAddr; |
| gceHARDWARE_TYPE hwType; |
| |
| gcmkHEADER_ARG("Kernel=%p VidMemBlock=%p", Kernel, VidMemBlock); |
| |
| gcmkVERIFY_OK( |
| gckKERNEL_GetHardwareType(Kernel, |
| &hwType)); |
| |
| gcmkVERIFY_ARGUMENT(VidMemBlock != gcvNULL); |
| gcmkASSERT(VidMemBlock->pageCount > 0); |
| |
| gcmkONERROR( |
| gckOS_GetPhysicalFromHandle(os, |
| VidMemBlock->physical, |
| 0, |
| &physAddr)); |
| |
| status = _ConvertPhysical(Kernel, |
| Kernel->core, |
| gcvNULL, |
| VidMemBlock, |
| physAddr, |
| &VidMemBlock->addresses[hwType]); |
| if (gcmIS_ERROR(status)) |
| { |
| /* Allocate pages inside the MMU. */ |
| gcmkONERROR( |
| gckMMU_AllocatePagesEx(Kernel->mmu, |
| VidMemBlock->pageCount, |
| VidMemBlock->type, |
| gcvPAGE_TYPE_1M, |
| VidMemBlock->secure, |
| &VidMemBlock->pageTables[hwType], |
| &VidMemBlock->addresses[hwType])); |
| |
| if (VidMemBlock->onFault != gcvTRUE) |
| { |
| /* Map the pages. */ |
| gcmkONERROR( |
| gckOS_Map1MPages(os, |
| Kernel->core, |
| VidMemBlock->physical, |
| VidMemBlock->pageCount, |
| VidMemBlock->addresses[hwType], |
| VidMemBlock->pageTables[hwType], |
| gcvTRUE, |
| VidMemBlock->type)); |
| } |
| |
| gcmkONERROR(gckMMU_Flush(Kernel->mmu, VidMemBlock->type)); |
| |
| /* Calculate the GPU virtual address. */ |
| VidMemBlock->addresses[hwType] |= (gctUINT32) (physAddr & ((1 << 20) - 1)); |
| } |
| |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, |
| "Mapped video memory block 0x%x to 0x%08X", |
| VidMemBlock, |
| VidMemBlock->addresses[hwType]); |
| |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (VidMemBlock->pageTables[hwType] != gcvNULL) |
| { |
| /* Free the pages from the MMU. */ |
| gcmkVERIFY_OK( |
| gckMMU_FreePages(Kernel->mmu, |
| VidMemBlock->secure, |
| gcvPAGE_TYPE_1M, |
| VidMemBlock->addresses[hwType], |
| VidMemBlock->pageTables[hwType], |
| VidMemBlock->pageCount)); |
| |
| VidMemBlock->pageTables[hwType] = gcvNULL; |
| } |
| |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| static gceSTATUS |
| _UnmapVidMemBlock( |
| IN gckMMU Mmu, |
| IN gceHARDWARE_TYPE HwType, |
| IN gckVIDMEM_BLOCK VidMemBlock |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Mmu=%p VidMemBlock=%p", Mmu, VidMemBlock); |
| |
| gcmkVERIFY_ARGUMENT(VidMemBlock != gcvNULL); |
| |
| if (VidMemBlock->pageTables[HwType] != gcvNULL) |
| { |
| /* Free the pages from the MMU. */ |
| gcmkONERROR( |
| gckMMU_FreePages(Mmu, |
| VidMemBlock->secure, |
| gcvPAGE_TYPE_1M, |
| VidMemBlock->addresses[HwType], |
| VidMemBlock->pageTables[HwType], |
| VidMemBlock->pageCount)); |
| |
| VidMemBlock->pageTables[HwType] = gcvNULL; |
| } |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| static gceSTATUS |
| gckVIDMEM_BLOCK_Construct( |
| IN gckKERNEL Kernel, |
| IN gctSIZE_T BlockSize, |
| IN gceVIDMEM_TYPE Type, |
| IN gctUINT32 Flag, |
| OUT gckVIDMEM_BLOCK * VidMemBlock |
| ) |
| { |
| gceSTATUS status; |
| gckVIDMEM_BLOCK vidMemBlock = gcvNULL; |
| gcuVIDMEM_NODE_PTR node = gcvNULL; |
| gckOS os = Kernel->os; |
| gctPOINTER pointer; |
| gctINT i; |
| |
| gcmkHEADER_ARG("Kernel=0x%x BlockSize=%lu Type=%x Flag=%x", Kernel, BlockSize, Type); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_ARGUMENT(BlockSize > 0); |
| gcmkVERIFY_ARGUMENT(VidMemBlock != gcvNULL); |
| |
| /* Allocate an gckVIDMEM_BLOCK object. */ |
| gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(gcsVIDMEM_BLOCK), &pointer)); |
| |
| vidMemBlock = pointer; |
| |
| /* Initialize the gckVIDMEM_BLOCK object. */ |
| vidMemBlock->object.type = gcvOBJ_VIDMEM_BLOCK; |
| vidMemBlock->os = os; |
| vidMemBlock->bytes = BlockSize; |
| vidMemBlock->freeBytes = BlockSize; |
| /* 1M page count. */ |
| vidMemBlock->pageCount = (gctUINT32)(BlockSize >> 20); |
| vidMemBlock->type = Type; |
| vidMemBlock->contiguous = Flag & gcvALLOC_FLAG_CONTIGUOUS; |
| vidMemBlock->secure = (Flag & gcvALLOC_FLAG_SECURITY) != 0; |
| vidMemBlock->onFault = (Flag & gcvALLOC_FLAG_ALLOC_ON_FAULT) != 0; |
| vidMemBlock->mutex = gcvNULL; |
| vidMemBlock->physical = gcvNULL; |
| |
| for (i = 0; i < gcvHARDWARE_NUM_TYPES; i++) |
| { |
| vidMemBlock->pageTables[i] = gcvNULL; |
| } |
| |
| /* Allocate the mutex. */ |
| gcmkONERROR(gckOS_CreateMutex(os, &vidMemBlock->mutex)); |
| |
| /* Allocate one gcuVIDMEM_NODE union. */ |
| gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(gcuVIDMEM_NODE), &pointer)); |
| |
| node = pointer; |
| |
| if (!vidMemBlock->contiguous) |
| { |
| Flag |= gcvALLOC_FLAG_1M_PAGES; |
| } |
| |
| /* Alloc 1M page size aligned memory block. */ |
| gcmkONERROR( |
| gckOS_AllocatePagedMemory(os, |
| Flag, |
| &BlockSize, |
| &vidMemBlock->gid, |
| &vidMemBlock->physical)); |
| |
| /* Map current hardware mmu table with 1M pages for this video memory block. */ |
| gcmkONERROR(gckVIDMEM_MapVidMemBlock(Kernel, vidMemBlock)); |
| |
| /* Initialize gcuVIDMEM_NODE union for virtual memory. */ |
| node->VirtualChunk.kernel = Kernel; |
| node->VirtualChunk.offset = 0; |
| node->VirtualChunk.bytes = BlockSize; |
| node->VirtualChunk.kvaddr = gcvNULL; |
| node->VirtualChunk.logical = gcvNULL; |
| node->VirtualChunk.parent = vidMemBlock; |
| |
| for (i = 0; i < gcvHARDWARE_NUM_TYPES; i++) |
| { |
| node->VirtualChunk.lockeds[i] = 0; |
| } |
| |
| /* Initialize the virtual chunk linked-list. */ |
| node->VirtualChunk.next = |
| node->VirtualChunk.prev = |
| node->VirtualChunk.nextFree = |
| node->VirtualChunk.prevFree = &vidMemBlock->node; |
| |
| vidMemBlock->node.VirtualChunk.next = |
| vidMemBlock->node.VirtualChunk.prev = |
| vidMemBlock->node.VirtualChunk.nextFree = |
| vidMemBlock->node.VirtualChunk.prevFree = node; |
| |
| vidMemBlock->node.VirtualChunk.bytes = 0; |
| |
| *VidMemBlock = vidMemBlock; |
| |
| gcmkFOOTER_ARG("*VidMemBlock=0x%x", *VidMemBlock); |
| |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (vidMemBlock != gcvNULL) |
| { |
| if (vidMemBlock->mutex) |
| { |
| gcmkVERIFY_OK(gckOS_DeleteMutex(os, vidMemBlock->mutex)); |
| } |
| |
| if (vidMemBlock->physical) |
| { |
| gcmkVERIFY_OK(gckOS_FreePagedMemory(os, |
| vidMemBlock->physical, |
| vidMemBlock->bytes)); |
| } |
| |
| gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, vidMemBlock)); |
| } |
| |
| if (node != gcvNULL) |
| { |
| gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, node)); |
| } |
| |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| static gceSTATUS |
| gckVIDMEM_BLOCK_Destroy( |
| IN gckKERNEL Kernel, |
| IN gckVIDMEM_BLOCK VidMemBlock |
| ) |
| { |
| gckDEVICE device = Kernel->device; |
| gctINT i; |
| |
| gcmkHEADER_ARG("Kernel=%p VidMemBlock=%p", Kernel, VidMemBlock); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_ARGUMENT(Kernel != gcvNULL); |
| gcmkVERIFY_ARGUMENT(VidMemBlock != gcvNULL); |
| |
| if (VidMemBlock->physical) |
| { |
| gcmkVERIFY_OK(gckOS_FreePagedMemory(Kernel->os, |
| VidMemBlock->physical, |
| VidMemBlock->bytes)); |
| } |
| |
| for (i = 0; i < gcvHARDWARE_NUM_TYPES; i++) |
| { |
| if (VidMemBlock->pageTables[i]) |
| { |
| gcmkVERIFY_OK(_UnmapVidMemBlock(device->mmus[i], i, VidMemBlock)); |
| } |
| } |
| |
| /* Free the mutex. */ |
| gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, VidMemBlock->mutex)); |
| |
| /* Free the virtual chunk. */ |
| gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, VidMemBlock->node.VirtualChunk.next)); |
| |
| /* Free the video memory block. */ |
| gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, VidMemBlock)); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| static gceSTATUS |
| _AllocateVirtualChunk( |
| IN gckKERNEL Kernel, |
| IN gckVIDMEM_BLOCK VidMemBlock, |
| IN gceVIDMEM_TYPE Type, |
| INOUT gctSIZE_T *Bytes, |
| OUT gcuVIDMEM_NODE_PTR *Node |
| ) |
| { |
| gceSTATUS status = gcvSTATUS_OK; |
| gctBOOL acquired = gcvFALSE; |
| gcuVIDMEM_NODE_PTR node; |
| gctSIZE_T bytes; |
| |
| gcmkHEADER_ARG("Kernel=%p VidMemBlock=%p Type=%x Bytes=%zx", |
| Kernel, VidMemBlock, Type, *Bytes); |
| |
| gcmkVERIFY_ARGUMENT(Node != gcvNULL); |
| gcmkVERIFY_ARGUMENT(VidMemBlock != gcvNULL); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| gcmkVERIFY_ARGUMENT(Type < gcvVIDMEM_TYPE_COUNT); |
| |
| gcmkONERROR(gckOS_AcquireMutex(Kernel->os, VidMemBlock->mutex, gcvINFINITE)); |
| |
| acquired = gcvTRUE; |
| |
| bytes = gcmALIGN(*Bytes, 4096); |
| |
| if (bytes > VidMemBlock->freeBytes) |
| { |
| /* No enough memory. */ |
| status = gcvSTATUS_OUT_OF_MEMORY; |
| goto OnError; |
| } |
| |
| node = _FindVirtualChunkNode(Kernel, VidMemBlock, bytes); |
| if (node == gcvNULL) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| if (node->VirtualChunk.bytes > bytes) |
| { |
| /* Split the chunk. */ |
| _SplitVirtualChunk(Kernel->os, node, bytes); |
| } |
| |
| /* Remove the chunk from the free list. */ |
| node->VirtualChunk.prevFree->VirtualChunk.nextFree = node->VirtualChunk.nextFree; |
| node->VirtualChunk.nextFree->VirtualChunk.prevFree = node->VirtualChunk.prevFree; |
| node->VirtualChunk.nextFree = node->VirtualChunk.prevFree = gcvNULL; |
| |
| /* Fill in the information. */ |
| node->VirtualChunk.parent = VidMemBlock; |
| |
| VidMemBlock->freeBytes -= node->VirtualChunk.bytes; |
| |
| *Bytes = bytes; |
| *Node = node; |
| |
| OnError: |
| if (acquired) |
| { |
| /* Release the mutex. */ |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, VidMemBlock->mutex)); |
| } |
| |
| return status; |
| } |
| |
| static gceSTATUS |
| gckVIDMEM_AllocateVirtualChunk( |
| IN gckKERNEL Kernel, |
| IN gceVIDMEM_TYPE Type, |
| IN gctUINT32 Flag, |
| IN gctSIZE_T Bytes, |
| OUT gcuVIDMEM_NODE_PTR * Node |
| ) |
| { |
| gckOS os; |
| gceSTATUS status; |
| gcuVIDMEM_NODE_PTR node; |
| gckVIDMEM_BLOCK vidMemBlock = gcvNULL; |
| gctSIZE_T blockSize; |
| gctBOOL acquired = gcvFALSE; |
| |
| gcmkHEADER_ARG("Kernel=0x%x Flag=%x Bytes=%lu", Kernel, Flag, Bytes); |
| |
| /* Verify the arguments. */ |
| gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); |
| gcmkVERIFY_ARGUMENT(Bytes > 0); |
| gcmkVERIFY_ARGUMENT(Node != gcvNULL); |
| |
| /* Extract the gckOS object pointer. */ |
| os = Kernel->os; |
| gcmkVERIFY_OBJECT(os, gcvOBJ_OS); |
| |
| /* Acquire the vidMem block mutex */ |
| gcmkONERROR(gckOS_AcquireMutex(Kernel->os, Kernel->vidMemBlockMutex, gcvINFINITE)); |
| acquired = gcvTRUE; |
| |
| /* Find the free vidmem block. */ |
| vidMemBlock = _FindFreeBlock(Kernel, Bytes); |
| if (!vidMemBlock) |
| { |
| /* Not found, construct new block. */ |
| blockSize = gcmALIGN(Bytes, gcd1M_PAGE_SIZE); |
| |
| gcmkONERROR( |
| gckVIDMEM_BLOCK_Construct(Kernel, |
| blockSize, |
| Type, |
| Flag, |
| &vidMemBlock)); |
| |
| gcmkONERROR(_AddToBlockList(Kernel, vidMemBlock)); |
| } |
| |
| /* Allocate virtual chunk node in the found block. */ |
| gcmkONERROR( |
| _AllocateVirtualChunk(Kernel, |
| vidMemBlock, |
| Type, |
| &Bytes, |
| &node)); |
| |
| /* Return pointer to the gcuVIDMEM_NODE union. */ |
| *Node = node; |
| |
| /* Release the vidMem block mutex. */ |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->vidMemBlockMutex)); |
| |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, |
| "Created virtual node 0x%x for %u bytes @ 0x%x", |
| node, Bytes, node->Virtual.physical); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Node=0x%x", *Node); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (acquired) |
| { |
| /* Release the vidMem block mutex. */ |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->vidMemBlockMutex)); |
| } |
| |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckVIDMEM_Free |
| ** |
| ** Free an allocated video memory node. |
| ** |
| ** INPUT: |
| ** |
| ** gckKERNEL Kernel |
| ** Pointer to an gckKERNEL object. |
| ** |
| ** gcuVIDMEM_NODE_PTR Node |
| ** Pointer to a gcuVIDMEM_NODE object. |
| ** |
| ** OUTPUT: |
| ** |
| ** Nothing. |
| */ |
| static gceSTATUS |
| gckVIDMEM_Free( |
| IN gckKERNEL Kernel, |
| IN gcuVIDMEM_NODE_PTR Node |
| ) |
| { |
| gceSTATUS status; |
| gckKERNEL kernel = gcvNULL; |
| gckVIDMEM memory = gcvNULL; |
| gckVIDMEM_BLOCK vidMemBlock = gcvNULL; |
| gcuVIDMEM_NODE_PTR node; |
| gctBOOL mutexAcquired = gcvFALSE; |
| gctBOOL vbMutexAcquired = gcvFALSE; |
| gctBOOL vbListMutexAcquired = gcvFALSE; |
| gctUINT64 mappingInOne = 1; |
| |
| gcmkHEADER_ARG("Node=0x%x", Node); |
| |
| /* Verify the arguments. */ |
| if ((Node == gcvNULL) |
| || (Node->VidMem.parent == gcvNULL) |
| ) |
| { |
| /* Invalid object. */ |
| gcmkONERROR(gcvSTATUS_INVALID_OBJECT); |
| } |
| |
| vidMemBlock = Node->VirtualChunk.parent; |
| |
| /**************************** Video Memory ********************************/ |
| |
| if (Node->VidMem.parent->object.type == gcvOBJ_VIDMEM) |
| { |
| /* Extract pointer to gckVIDMEM object owning the node. */ |
| memory = Node->VidMem.parent; |
| |
| /* Acquire the mutex. */ |
| gcmkONERROR( |
| gckOS_AcquireMutex(memory->os, memory->mutex, gcvINFINITE)); |
| |
| mutexAcquired = gcvTRUE; |
| |
| if (Node->VidMem.kvaddr) |
| { |
| #if gcdCAPTURE_ONLY_MODE |
| gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, Node->VidMem.kvaddr)); |
| #else |
| |
| gcmkONERROR( |
| gckOS_DestroyKernelMapping(Kernel->os, |
| Node->VidMem.parent->physical, |
| Node->VidMem.kvaddr)); |
| #endif |
| |
| Node->VidMem.kvaddr = gcvNULL; |
| } |
| |
| #ifdef __QNXNTO__ |
| /* Unmap the video memory. */ |
| if (Node->VidMem.logical != gcvNULL) |
| { |
| gckKERNEL_UnmapVideoMemory( |
| Kernel, |
| Node->VidMem.pool, |
| Node->VidMem.physical, |
| Node->VidMem.logical, |
| Node->VidMem.processID, |
| Node->VidMem.bytes |
| ); |
| |
| Node->VidMem.logical = gcvNULL; |
| } |
| |
| /* Reset. */ |
| Node->VidMem.processID = 0; |
| |
| /* Don't try to re-free an already freed node. */ |
| if ((Node->VidMem.nextFree == gcvNULL) |
| && (Node->VidMem.prevFree == gcvNULL) |
| ) |
| #endif |
| { |
| |
| /* Check if Node is already freed. */ |
| if (Node->VidMem.nextFree) |
| { |
| /* Node is alread freed. */ |
| gcmkONERROR(gcvSTATUS_INVALID_DATA); |
| } |
| |
| /* Update the number of free bytes. */ |
| memory->freeBytes += Node->VidMem.bytes; |
| |
| /* Find the next free node. */ |
| for (node = Node->VidMem.next; |
| node != gcvNULL && node->VidMem.nextFree == gcvNULL; |
| node = node->VidMem.next) ; |
| |
| if (node == gcvNULL) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_DATA); |
| } |
| |
| /* Insert this node in the free list. */ |
| Node->VidMem.nextFree = node; |
| Node->VidMem.prevFree = node->VidMem.prevFree; |
| |
| Node->VidMem.prevFree->VidMem.nextFree = |
| node->VidMem.prevFree = Node; |
| |
| /* Is the next node a free node and not the sentinel? */ |
| if ((Node->VidMem.next == Node->VidMem.nextFree) |
| && (Node->VidMem.next->VidMem.bytes != 0) |
| ) |
| { |
| /* Merge this node with the next node. */ |
| gcmkONERROR(_Merge(memory->os, node = Node)); |
| gcmkASSERT(node->VidMem.nextFree != node); |
| gcmkASSERT(node->VidMem.prevFree != node); |
| } |
| |
| /* Is the previous node a free node and not the sentinel? */ |
| if ((Node->VidMem.prev == Node->VidMem.prevFree) |
| && (Node->VidMem.prev->VidMem.bytes != 0) |
| ) |
| { |
| /* Merge this node with the previous node. */ |
| gcmkONERROR(_Merge(memory->os, node = Node->VidMem.prev)); |
| gcmkASSERT(node->VidMem.nextFree != node); |
| gcmkASSERT(node->VidMem.prevFree != node); |
| } |
| } |
| |
| gckOS_QueryOption(memory->os, "allMapInOne", &mappingInOne); |
| if (!mappingInOne) |
| { |
| gckOS_ReleaseReservedMemory(memory->os, Node->VidMem.physical); |
| Node->VidMem.physical = gcvNULL; |
| } |
| |
| /* Release the mutex. */ |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(memory->os, memory->mutex)); |
| |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, |
| "Node 0x%x is freed.", |
| Node); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| else if (vidMemBlock && vidMemBlock->object.type == gcvOBJ_VIDMEM_BLOCK) |
| { |
| /* Acquire the vidMem block mutex */ |
| gcmkONERROR(gckOS_AcquireMutex(Kernel->os, Kernel->vidMemBlockMutex, gcvINFINITE)); |
| vbListMutexAcquired = gcvTRUE; |
| |
| if (vidMemBlock) |
| { |
| gckOS os = vidMemBlock->os; |
| |
| gcmkONERROR(gckOS_AcquireMutex(os, vidMemBlock->mutex, gcvINFINITE)); |
| vbMutexAcquired = gcvTRUE; |
| kernel = Node->VirtualChunk.kernel; |
| |
| if (Node->VirtualChunk.kvaddr) |
| { |
| gcmkONERROR( |
| gckOS_DestroyKernelMapping(kernel->os, |
| vidMemBlock->physical, |
| Node->VirtualChunk.kvaddr)); |
| |
| Node->VirtualChunk.kvaddr = gcvNULL; |
| } |
| |
| /* Handle the free chunk in the linked-list */ |
| { |
| /* Check if chunk is in free list. */ |
| if (Node->VirtualChunk.nextFree) |
| { |
| /* Chunk is already freed. */ |
| gcmkONERROR(gcvSTATUS_INVALID_DATA); |
| } |
| |
| vidMemBlock->freeBytes += Node->VirtualChunk.bytes; |
| |
| /* Find the next free chunk. */ |
| for (node = Node->VirtualChunk.next; |
| node != gcvNULL && node->VirtualChunk.nextFree == gcvNULL; |
| node = node->VirtualChunk.next); |
| |
| if (node == gcvNULL) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_DATA); |
| } |
| |
| /* Insert this chunk in the free list. */ |
| Node->VirtualChunk.nextFree = node; |
| Node->VirtualChunk.prevFree = node->VirtualChunk.prevFree; |
| |
| Node->VirtualChunk.prevFree->VirtualChunk.nextFree = |
| node->VirtualChunk.prevFree = Node; |
| |
| /* Is the next chunk a free chunk. */ |
| if ((Node->VirtualChunk.next == Node->VirtualChunk.nextFree) |
| && (Node->VirtualChunk.next->VirtualChunk.bytes != 0) |
| ) |
| { |
| /* Merge this chunk with the next chunk. */ |
| gcmkONERROR(_MergeVirtualChunk(os, node = Node)); |
| gcmkASSERT(node->VirtualChunk.nextFree != node); |
| gcmkASSERT(node->VirtualChunk.prevFree != node); |
| } |
| |
| /* Is the previous chunk a free chunk. */ |
| if ((Node->VirtualChunk.prev == Node->VirtualChunk.prevFree) |
| && (Node->VirtualChunk.prev->VirtualChunk.bytes != 0) |
| ) |
| { |
| /* Merge this chunk with the previous chunk. */ |
| gcmkONERROR(_MergeVirtualChunk(os, node = Node->VirtualChunk.prev)); |
| gcmkASSERT(node->VirtualChunk.nextFree != node); |
| gcmkASSERT(node->VirtualChunk.prevFree != node); |
| } |
| } |
| |
| /* Release the mutex. */ |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(os, vidMemBlock->mutex)); |
| |
| /* Only free the vidmem block when all the chunks are freed. */ |
| if (_IsVidMemBlockFree(vidMemBlock)) |
| { |
| gcmkONERROR(_RemoveFromBlockList(kernel, vidMemBlock)); |
| |
| gcmkONERROR(gckVIDMEM_BLOCK_Destroy(kernel, vidMemBlock)); |
| } |
| } |
| |
| /* Release the vidMem block mutex. */ |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->vidMemBlockMutex)); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /*************************** Virtual Memory *******************************/ |
| |
| /* Get gckKERNEL object. */ |
| kernel = Node->Virtual.kernel; |
| |
| /* Verify the gckKERNEL object pointer. */ |
| gcmkVERIFY_OBJECT(kernel, gcvOBJ_KERNEL); |
| |
| |
| if (Node->Virtual.kvaddr) |
| { |
| gcmkVERIFY_OK( |
| gckOS_DestroyKernelMapping(kernel->os, |
| Node->Virtual.physical, |
| Node->Virtual.kvaddr)); |
| } |
| |
| /* Free the virtual memory. */ |
| gcmkVERIFY_OK(gckOS_FreePagedMemory(kernel->os, |
| Node->Virtual.physical, |
| Node->Virtual.bytes)); |
| |
| /* Delete the gcuVIDMEM_NODE union. */ |
| gcmkVERIFY_OK(gcmkOS_SAFE_FREE(kernel->os, Node)); |
| |
| /* Success. */ |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (mutexAcquired) |
| { |
| /* Release the mutex. */ |
| gcmkVERIFY_OK(gckOS_ReleaseMutex( |
| memory->os, memory->mutex |
| )); |
| } |
| |
| if (vbMutexAcquired) |
| { |
| gcmkVERIFY_OK(gckOS_ReleaseMutex( |
| vidMemBlock->os, vidMemBlock->mutex |
| )); |
| } |
| |
| if (vbListMutexAcquired) |
| { |
| gcmkVERIFY_OK(gckOS_ReleaseMutex( |
| vidMemBlock->os, Kernel->vidMemBlockMutex)); |
| } |
| |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckVIDMEM_Lock |
| ** |
| ** Lock a video memory node and return its hardware specific address. |
| ** |
| ** INPUT: |
| ** |
| ** gckKERNEL Kernel |
| ** Pointer to an gckKERNEL object. |
| ** |
| ** gcuVIDMEM_NODE_PTR Node |
| ** Pointer to a gcuVIDMEM_NODE union. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctUINT32 * Address |
| ** Pointer to a variable that will hold the hardware specific address. |
| ** |
| ** gctUINT32 * PhysicalAddress |
| ** Pointer to a variable that will hold the bus address of a contiguous |
| ** video node. |
| */ |
| static gceSTATUS |
| gckVIDMEM_Lock( |
| IN gckKERNEL Kernel, |
| IN gcuVIDMEM_NODE_PTR Node, |
| OUT gctUINT32 * Address |
| ) |
| { |
| gckOS os; |
| |
| gcmkHEADER_ARG("Kernel=%p Node=%p", Kernel, Node); |
| |
| /* Extract the gckOS object pointer. */ |
| os = Kernel->os; |
| |
| /* Increment the lock count. */ |
| if (Node->VidMem.locked++ == 0) |
| { |
| gctUINT32 address; |
| gctUINT32 offset = (gctUINT32)Node->VidMem.offset; |
| |
| switch (Node->VidMem.pool) |
| { |
| case gcvPOOL_LOCAL_EXTERNAL: |
| address = Kernel->externalBaseAddress + offset; |
| break; |
| case gcvPOOL_LOCAL_INTERNAL: |
| address = Kernel->internalBaseAddress + offset; |
| break; |
| case gcvPOOL_INTERNAL_SRAM: |
| address = Kernel->sRAMBaseAddresses[Kernel->sRAMIndex] + offset; |
| break; |
| case gcvPOOL_EXTERNAL_SRAM: |
| address = Kernel->extSRAMBaseAddresses[Kernel->extSRAMIndex] + offset; |
| break; |
| default: |
| gcmkASSERT(Node->VidMem.pool == gcvPOOL_SYSTEM); |
| /*FALLTHRU*/ |
| case gcvPOOL_SYSTEM: |
| address = Kernel->contiguousBaseAddress + offset; |
| break; |
| } |
| |
| /* Save address. */ |
| Node->VidMem.address = address; |
| } |
| |
| *Address = Node->VidMem.address; |
| |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, |
| "Locked node 0x%x (%d) @ 0x%08X", |
| Node, |
| Node->VidMem.locked, |
| *Address); |
| |
| gcmkFOOTER_ARG("*Address=0x%08X", *Address); |
| return gcvSTATUS_OK; |
| } |
| |
| static gceSTATUS |
| gckVIDMEM_LockVirtual( |
| IN gckKERNEL Kernel, |
| IN gcuVIDMEM_NODE_PTR Node, |
| OUT gctUINT32 * Address |
| ) |
| { |
| gceSTATUS status; |
| gctPHYS_ADDR_T physicalAddress; |
| gctBOOL locked = gcvFALSE; |
| gckOS os = Kernel->os; |
| gceHARDWARE_TYPE hwType; |
| |
| gcmkHEADER_ARG("Kernel=%p Node=%p", Kernel, Node); |
| |
| gcmkVERIFY_OK( |
| gckKERNEL_GetHardwareType(Kernel, |
| &hwType)); |
| |
| gcmkONERROR( |
| gckOS_GetPhysicalFromHandle(os, |
| Node->Virtual.physical, |
| 0, |
| &physicalAddress)); |
| |
| |
| /* Increment the lock count. */ |
| if (Node->Virtual.lockeds[hwType]++ == 0) |
| { |
| locked = gcvTRUE; |
| |
| status = _ConvertPhysical( |
| Kernel, |
| Kernel->core, |
| Node, |
| gcvNULL, |
| physicalAddress, |
| &Node->Virtual.addresses[hwType] |
| ); |
| |
| if (gcmIS_ERROR(status)) |
| { |
| /* Do GPU address mapping. */ |
| #if gcdSECURITY |
| gctPHYS_ADDR physicalArrayPhysical; |
| gctPOINTER physicalArrayLogical; |
| |
| gcmkONERROR(gckOS_AllocatePageArray( |
| os, |
| Node->Virtual.physical, |
| Node->Virtual.pageCount, |
| &physicalArrayLogical, |
| &physicalArrayPhysical |
| )); |
| |
| gcmkONERROR(gckKERNEL_SecurityMapMemory( |
| Kernel, |
| physicalArrayLogical, |
| Node->Virtual.pageCount, |
| &Node->Virtual.addresses[hwType] |
| )); |
| |
| gcmkONERROR(gckOS_FreeNonPagedMemory( |
| os, |
| physicalArrayPhysical, |
| physicalArrayLogical, |
| 1 |
| )); |
| #else |
| { |
| /* Allocate pages inside the MMU. */ |
| gcmkONERROR( |
| gckMMU_AllocatePagesEx(Kernel->mmu, |
| Node->Virtual.pageCount, |
| Node->Virtual.type, |
| gcvPAGE_TYPE_4K, |
| Node->Virtual.secure, |
| &Node->Virtual.pageTables[hwType], |
| &Node->Virtual.addresses[hwType])); |
| } |
| |
| if (Node->Virtual.onFault != gcvTRUE) |
| { |
| #if gcdENABLE_TRUST_APPLICATION |
| if (Kernel->hardware->options.secureMode == gcvSECURE_IN_TA) |
| { |
| gcmkONERROR(gckKERNEL_MapInTrustApplicaiton( |
| Kernel, |
| Node->Virtual.logical, |
| Node->Virtual.physical, |
| Node->Virtual.addresses[hwType], |
| Node->Virtual.pageCount |
| )); |
| } |
| else |
| #endif |
| { |
| gcmkDUMP(os, "#[mmu: dynamic mapping: address=0x%08X pageCount=%lu]", |
| Node->Virtual.addresses[hwType], |
| (unsigned long)Node->Virtual.pageCount); |
| |
| /* Map the pages. */ |
| gcmkONERROR(gckOS_MapPagesEx(os, |
| Kernel->core, |
| Node->Virtual.physical, |
| Node->Virtual.pageCount, |
| Node->Virtual.addresses[hwType], |
| Node->Virtual.pageTables[hwType], |
| gcvTRUE, |
| Node->Virtual.type)); |
| } |
| } |
| |
| { |
| gcmkONERROR(gckMMU_Flush(Kernel->mmu, Node->Virtual.type)); |
| } |
| #endif |
| |
| /* GPU MMU page size is fixed at 4096 now. */ |
| Node->Virtual.addresses[hwType] |= (gctUINT32)physicalAddress & (4096 - 1); |
| } |
| |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, |
| "Mapped virtual node 0x%x to 0x%08X", |
| Node, |
| Node->Virtual.addresses[hwType]); |
| } |
| |
| /* Return hardware address. */ |
| *Address = Node->Virtual.addresses[hwType]; |
| |
| gcmkFOOTER_ARG("*Address=0x%08X", *Address); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (locked) |
| { |
| if (Node->Virtual.pageTables[hwType] != gcvNULL) |
| { |
| { |
| /* Free the pages from the MMU. */ |
| gcmkVERIFY_OK( |
| gckMMU_FreePages(Kernel->mmu, |
| Node->Virtual.secure, |
| gcvPAGE_TYPE_4K, |
| Node->Virtual.addresses[hwType], |
| Node->Virtual.pageTables[hwType], |
| Node->Virtual.pageCount)); |
| } |
| |
| Node->Virtual.pageTables[hwType] = gcvNULL; |
| } |
| |
| Node->Virtual.lockeds[hwType]--; |
| } |
| |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| static gceSTATUS |
| gckVIDMEM_LockVirtualChunk( |
| IN gckKERNEL Kernel, |
| IN gcuVIDMEM_NODE_PTR Node, |
| OUT gctUINT32 * Address |
| ) |
| { |
| gceSTATUS status = gcvSTATUS_OK; |
| gckVIDMEM_BLOCK vidMemBlock = Node->VirtualChunk.parent; |
| gceHARDWARE_TYPE hwType; |
| |
| gcmkHEADER_ARG("Kernel=%p Node=%p", Kernel, Node); |
| |
| gcmkVERIFY_OK( |
| gckKERNEL_GetHardwareType(Kernel, |
| &hwType)); |
| |
| gcmkASSERT(vidMemBlock != gcvNULL); |
| |
| /* Increment the lock count. */ |
| if (Node->VirtualChunk.lockeds[hwType]++ == 0) |
| { |
| if (!vidMemBlock->pageTables[hwType]) |
| { |
| /* Map current hardware mmu table with 1M pages for this video memory block. */ |
| gcmkONERROR(gckVIDMEM_MapVidMemBlock(Kernel, vidMemBlock)); |
| } |
| |
| Node->VirtualChunk.addresses[hwType] = vidMemBlock->addresses[hwType] |
| + (gctUINT32)Node->VirtualChunk.offset; |
| } |
| |
| /* Return hardware address. */ |
| *Address = Node->VirtualChunk.addresses[hwType]; |
| |
| gcmkFOOTER_ARG("*Address=0x%08X", *Address); |
| |
| return gcvSTATUS_OK; |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckVIDMEM_Unlock |
| ** |
| ** Unlock a video memory node. |
| ** |
| ** INPUT: |
| ** |
| ** gckKERNEL Kernel |
| ** Pointer to an gckKERNEL object. |
| ** |
| ** gcuVIDMEM_NODE_PTR Node |
| ** Pointer to a locked gcuVIDMEM_NODE union. |
| ** |
| ** gctBOOL * Asynchroneous |
| ** Pointer to a variable specifying whether the surface should be |
| ** unlocked asynchroneously or not. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctBOOL * Asynchroneous |
| ** Pointer to a variable receiving the number of bytes used in the |
| ** command buffer specified by 'Commands'. If gcvNULL, there is no |
| ** command buffer. |
| */ |
| static gceSTATUS |
| gckVIDMEM_Unlock( |
| IN gckKERNEL Kernel, |
| IN gcuVIDMEM_NODE_PTR Node, |
| IN OUT gctBOOL * Asynchroneous |
| ) |
| { |
| gceSTATUS status; |
| |
| gcmkHEADER_ARG("Node=0x%x *Asynchroneous=%d", |
| Node, gcmOPT_VALUE(Asynchroneous)); |
| |
| if (Node->VidMem.locked <= 0) |
| { |
| /* The surface was not locked. */ |
| gcmkONERROR(gcvSTATUS_MEMORY_UNLOCKED); |
| } |
| |
| if (Asynchroneous != gcvNULL) |
| { |
| /* Schedule an event to sync with GPU. */ |
| *Asynchroneous = gcvTRUE; |
| } |
| else |
| { |
| /* Decrement the lock count. */ |
| Node->VidMem.locked--; |
| } |
| |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, |
| "Unlocked node %p (%d)", |
| Node, |
| Node->VidMem.locked); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Asynchroneous=%d", gcmOPT_VALUE(Asynchroneous)); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| static gceSTATUS |
| gckVIDMEM_UnlockVirtual( |
| IN gckKERNEL Kernel, |
| IN gcuVIDMEM_NODE_PTR Node, |
| IN OUT gctBOOL * Asynchroneous |
| ) |
| { |
| gceSTATUS status; |
| gceHARDWARE_TYPE hwType; |
| |
| gcmkHEADER_ARG("Node=0x%x *Asynchroneous=%d", |
| Node, gcmOPT_VALUE(Asynchroneous)); |
| |
| gcmkVERIFY_OK( |
| gckKERNEL_GetHardwareType(Kernel, |
| &hwType)); |
| |
| if (Asynchroneous != gcvNULL) |
| { |
| /* Schedule the surface to be unlocked. */ |
| *Asynchroneous = gcvTRUE; |
| } |
| else |
| { |
| if (Node->Virtual.lockeds[hwType] == 0) |
| { |
| gcmkONERROR(gcvSTATUS_MEMORY_UNLOCKED); |
| } |
| |
| /* Decrement lock count. */ |
| --Node->Virtual.lockeds[hwType]; |
| |
| /* See if we can unlock the resources. */ |
| if (Node->Virtual.lockeds[hwType] == 0) |
| { |
| gctUINT32 address; |
| |
| /* Adjust address to page aligned for underlying functions. */ |
| address = Node->Virtual.addresses[hwType] & ~(4096 - 1); |
| |
| #if gcdSECURITY |
| if (Node->Virtual.addresses[hwType] > 0x80000000) |
| { |
| gcmkONERROR(gckKERNEL_SecurityUnmapMemory( |
| Kernel, |
| address, |
| Node->Virtual.pageCount |
| )); |
| } |
| #else |
| /* Free the page table. */ |
| if (Node->Virtual.pageTables[hwType] != gcvNULL) |
| { |
| { |
| gcmkONERROR( |
| gckMMU_FreePages(Kernel->mmu, |
| Node->Virtual.secure, |
| gcvPAGE_TYPE_4K, |
| address, |
| Node->Virtual.pageTables[hwType], |
| Node->Virtual.pageCount)); |
| } |
| |
| gcmkONERROR(gckOS_UnmapPages( |
| Kernel->os, |
| Node->Virtual.pageCount, |
| address |
| )); |
| |
| /* Mark page table as freed. */ |
| Node->Virtual.pageTables[hwType] = gcvNULL; |
| } |
| #endif |
| } |
| |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, |
| "Unmapped virtual node %p from 0x%08X", |
| Node, Node->Virtual.addresses[hwType]); |
| } |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Asynchroneous=%d", gcmOPT_VALUE(Asynchroneous)); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| static gceSTATUS |
| gckVIDMEM_UnlockVirtualChunk( |
| IN gckKERNEL Kernel, |
| IN gcuVIDMEM_NODE_PTR Node, |
| IN OUT gctBOOL * Asynchroneous |
| ) |
| { |
| gceSTATUS status; |
| gceHARDWARE_TYPE hwType; |
| |
| gcmkHEADER_ARG("Node=0x%x *Asynchroneous=%d", |
| Node, gcmOPT_VALUE(Asynchroneous)); |
| |
| gcmkVERIFY_OK( |
| gckKERNEL_GetHardwareType(Kernel, |
| &hwType)); |
| |
| if (Asynchroneous != gcvNULL) |
| { |
| /* Schedule an event to sync with GPU. */ |
| *Asynchroneous = gcvTRUE; |
| } |
| else |
| { |
| if (Node->VirtualChunk.lockeds[hwType] == 0) |
| { |
| /* The surface was not locked. */ |
| gcmkONERROR(gcvSTATUS_MEMORY_UNLOCKED); |
| } |
| |
| /* Unmap and free pages when video memory free. */ |
| |
| /* Decrement the lock count. */ |
| --Node->VirtualChunk.lockeds[hwType]; |
| } |
| |
| gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, |
| "Unlocked node %p (%d)", |
| Node, |
| Node->VirtualChunk.lockeds[hwType]); |
| |
| /* Success. */ |
| gcmkFOOTER_ARG("*Asynchroneous=%d", gcmOPT_VALUE(Asynchroneous)); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| /* Return the status. */ |
| gcmkFOOTER(); |
| return status; |
| } |
| /******************************************************************************* |
| ** |
| ** gckVIDMEM_HANDLE_Allocate |
| ** |
| ** Allocate a handle for a gckVIDMEM_NODE object. |
| ** |
| ** INPUT: |
| ** |
| ** gckKERNEL Kernel |
| ** Pointer to an gckKERNEL object. |
| ** |
| ** gckVIDMEM_NODE Node |
| ** Pointer to a gckVIDMEM_NODE object. |
| ** |
| ** OUTPUT: |
| ** |
| ** gctUINT32 * Handle |
| ** Pointer to a variable receiving a handle represent this |
| ** gckVIDMEM_NODE in userspace. |
| */ |
| gceSTATUS |
| gckVIDMEM_HANDLE_Allocate( |
| IN gckKERNEL Kernel, |
| IN gckVIDMEM_NODE Node, |
| OUT gctUINT32 * Handle |
| ) |
| { |
| gceSTATUS status; |
| gctUINT32 processID = 0; |
| gctPOINTER pointer = gcvNULL; |
| gctPOINTER handleDatabase = gcvNULL; |
| gctPOINTER mutex = gcvNULL; |
| gctUINT32 handle = 0; |
| gckVIDMEM_HANDLE handleObject = gcvNULL; |
| gckOS os = Kernel->os; |
| |
| gcmkHEADER_ARG("Kernel=0x%X, Node=0x%X", Kernel, Node); |
| |
| gcmkVERIFY_OBJECT(os, gcvOBJ_OS); |
| |
| /* Allocate a gckVIDMEM_HANDLE object. */ |
| gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(gcsVIDMEM_HANDLE), &pointer)); |
| |
| gcmkVERIFY_OK(gckOS_ZeroMemory(pointer, gcmSIZEOF(gcsVIDMEM_HANDLE))); |
| |
| handleObject = pointer; |
| |
| gcmkONERROR(gckOS_AtomConstruct(os, &handleObject->reference)); |
| |
| /* Set default reference count to 1. */ |
| gckOS_AtomSet(os, handleObject->reference, 1); |
| |
| gcmkVERIFY_OK(gckOS_GetProcessID(&processID)); |
| |
| gcmkONERROR( |
| gckKERNEL_FindHandleDatbase(Kernel, |
| processID, |
| &handleDatabase, |
| &mutex)); |
| |
| /* Allocate a handle for this object. */ |
| gcmkONERROR( |
| gckKERNEL_AllocateIntegerId(handleDatabase, handleObject, &handle)); |
| |
| handleObject->node = Node; |
| handleObject->handle = handle; |
| |
| *Handle = handle; |
| |
| gcmkFOOTER_ARG("*Handle=%d", *Handle); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (handleObject != gcvNULL) |
| { |
| if (handleObject->reference != gcvNULL) |
| { |
| gcmkVERIFY_OK(gckOS_AtomDestroy(os, handleObject->reference)); |
| } |
| |
| gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, handleObject)); |
| } |
| |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| gceSTATUS |
| gckVIDMEM_HANDLE_Reference( |
| IN gckKERNEL Kernel, |
| IN gctUINT32 ProcessID, |
| IN gctUINT32 Handle |
| ) |
| { |
| gceSTATUS status; |
| gckVIDMEM_HANDLE handleObject = gcvNULL; |
| gctPOINTER database = gcvNULL; |
| gctPOINTER mutex = gcvNULL; |
| gctINT32 oldValue = 0; |
| gctBOOL acquired = gcvFALSE; |
| |
| gcmkHEADER_ARG("Handle=%d PrcoessID=%d", Handle, ProcessID); |
| |
| gcmkONERROR( |
| gckKERNEL_FindHandleDatbase(Kernel, ProcessID, &database, &mutex)); |
| |
| gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, mutex, gcvINFINITE)); |
| acquired = gcvTRUE; |
| |
| /* Translate handle to gckVIDMEM_HANDLE object. */ |
| gcmkONERROR( |
| gckKERNEL_QueryIntegerId(database, Handle, (gctPOINTER *)&handleObject)); |
| |
| /* Increase the reference count. */ |
| gckOS_AtomIncrement(Kernel->os, handleObject->reference, &oldValue); |
| |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex)); |
| acquired = gcvFALSE; |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (acquired) |
| { |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex)); |
| } |
| |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| gceSTATUS |
| gckVIDMEM_HANDLE_Dereference( |
| IN gckKERNEL Kernel, |
| IN gctUINT32 ProcessID, |
| IN gctUINT32 Handle |
| ) |
| { |
| gceSTATUS status; |
| gctPOINTER handleDatabase = gcvNULL; |
| gctPOINTER mutex = gcvNULL; |
| gctINT32 oldValue = 0; |
| gckVIDMEM_HANDLE handleObject = gcvNULL; |
| gctBOOL acquired = gcvFALSE; |
| |
| gcmkHEADER_ARG("Handle=%d PrcoessID=%d", Handle, ProcessID); |
| |
| gcmkONERROR( |
| gckKERNEL_FindHandleDatbase(Kernel, |
| ProcessID, |
| &handleDatabase, |
| &mutex)); |
| |
| gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, mutex, gcvINFINITE)); |
| acquired = gcvTRUE; |
| |
| /* Translate handle to gckVIDMEM_HANDLE. */ |
| gcmkONERROR( |
| gckKERNEL_QueryIntegerId(handleDatabase, Handle, (gctPOINTER *)&handleObject)); |
| |
| gckOS_AtomDecrement(Kernel->os, handleObject->reference, &oldValue); |
| |
| if (oldValue == 1) |
| { |
| /* Remove handle from database if this is the last reference. */ |
| gcmkVERIFY_OK(gckKERNEL_FreeIntegerId(handleDatabase, Handle)); |
| } |
| |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex)); |
| acquired = gcvFALSE; |
| |
| if (oldValue == 1) |
| { |
| gcmkVERIFY_OK(gckOS_AtomDestroy(Kernel->os, handleObject->reference)); |
| gcmkOS_SAFE_FREE(Kernel->os, handleObject); |
| } |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (acquired) |
| { |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex)); |
| } |
| |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| gceSTATUS |
| gckVIDMEM_HANDLE_Lookup( |
| IN gckKERNEL Kernel, |
| IN gctUINT32 ProcessID, |
| IN gctUINT32 Handle, |
| OUT gckVIDMEM_NODE * Node |
| ) |
| { |
| gceSTATUS status; |
| gckVIDMEM_HANDLE handleObject = gcvNULL; |
| gckVIDMEM_NODE node = gcvNULL; |
| gctPOINTER database = gcvNULL; |
| gctPOINTER mutex = gcvNULL; |
| gctBOOL acquired = gcvFALSE; |
| |
| gcmkHEADER_ARG("Kernel=0x%X ProcessID=%d Handle=%d", |
| Kernel, ProcessID, Handle); |
| |
| gcmkONERROR( |
| gckKERNEL_FindHandleDatbase(Kernel, ProcessID, &database, &mutex)); |
| |
| gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, mutex, gcvINFINITE)); |
| acquired = gcvTRUE; |
| |
| gcmkONERROR( |
| gckKERNEL_QueryIntegerId(database, Handle, (gctPOINTER *)&handleObject)); |
| |
| node = handleObject->node; |
| |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex)); |
| acquired = gcvFALSE; |
| |
| *Node = node; |
| |
| gcmkFOOTER_ARG("*Node=%d", *Node); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (acquired) |
| { |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex)); |
| } |
| |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| gceSTATUS |
| gckVIDMEM_HANDLE_Lookup2( |
| IN gckKERNEL Kernel, |
| IN gcsDATABASE_PTR Database, |
| IN gctUINT32 Handle, |
| OUT gckVIDMEM_NODE * Node |
| ) |
| { |
| gceSTATUS status; |
| gckVIDMEM_HANDLE handleObject = gcvNULL; |
| gckVIDMEM_NODE node = gcvNULL; |
| gctPOINTER database = gcvNULL; |
| gctPOINTER mutex = gcvNULL; |
| gctBOOL acquired = gcvFALSE; |
| |
| gcmkHEADER_ARG("Kernel=0x%X Database=%p Handle=%d", |
| Kernel, Database, Handle); |
| |
| database = Database->handleDatabase; |
| mutex = Database->handleDatabaseMutex; |
| |
| gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, mutex, gcvINFINITE)); |
| acquired = gcvTRUE; |
| |
| gcmkONERROR( |
| gckKERNEL_QueryIntegerId(database, Handle, (gctPOINTER *)&handleObject)); |
| |
| node = handleObject->node; |
| |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex)); |
| acquired = gcvFALSE; |
| |
| *Node = node; |
| |
| gcmkFOOTER_ARG("*Node=%d", *Node); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (acquired) |
| { |
| gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex)); |
| } |
| |
| gcmkFOOTER(); |
| return status; |
| } |
| |
| |
| static gceSTATUS |
| gckVIDMEM_NODE_Construct( |
| IN gckKERNEL Kernel, |
| IN gcuVIDMEM_NODE_PTR VideoNode, |
| IN gceVIDMEM_TYPE Type, |
| IN gcePOOL Pool, |
| OUT gckVIDMEM_NODE * NodeObject |
| ) |
| { |
| gceSTATUS status; |
| gckVIDMEM_NODE node = gcvNULL; |
| gctPOINTER pointer = gcvNULL; |
| gckOS os = Kernel->os; |
| gctUINT i; |
| |
| /* Construct a node. */ |
| gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(gcsVIDMEM_NODE), &pointer)); |
| |
| gcmkVERIFY_OK(gckOS_ZeroMemory(pointer, gcmSIZEOF(gcsVIDMEM_NODE))); |
| |
| node = pointer; |
| |
| node->metadata.magic = VIV_VIDMEM_METADATA_MAGIC; |
| node->metadata.ts_fd = -1; |
| #ifdef gcdANDROID |
| node->metadata.ts_address = 0; |
| #endif |
| |
| node->node = VideoNode; |
| node->kernel = Kernel; |
| node->type = Type; |
| node->pool = Pool; |
| |
| gcmkONERROR(gckOS_AtomConstruct(os, &node->reference)); |
| |
| gcmkONERROR(gckOS_CreateMutex(os, &node->mutex)); |
| |
| for (i = 0; i < gcvENGINE_GPU_ENGINE_COUNT; i++) |
| { |
| gcmkONERROR(gckOS_CreateSignal(os, gcvFALSE, &node->sync[i].signal)); |
| } |
| |
| /* Reference is 1 by default . */ |
| gckOS_AtomSet(os, node->reference, 1); |
| |
| gcmkVERIFY_OK( |
| gckOS_AcquireMutex(Kernel->os, |
| Kernel->db->videoMemListMutex, |
| gcvINFINITE)); |
| |
| /* Add into video memory node list. */ |
| gcsLIST_Add(&node->link, &Kernel->db->videoMemList); |
| |
| gcmkVERIFY_OK( |
| gckOS_ReleaseMutex(Kernel->os, Kernel->db->videoMemListMutex)); |
| |
| *NodeObject = node; |
| |
| return gcvSTATUS_OK; |
| |
| OnError: |
| if (node != gcvNULL) |
| { |
| if (node->mutex) |
| { |
| gcmkVERIFY_OK(gckOS_DeleteMutex(os, node->mutex)); |
| } |
| |
| if (node->reference != gcvNULL) |
| { |
| gcmkVERIFY_OK(gckOS_AtomDestroy(os, node->reference)); |
| } |
| |
| for (i = 0; i < gcvENGINE_GPU_ENGINE_COUNT; i++) |
| { |
| if (node->sync[i].signal != gcvNULL) |
| { |
| gcmkVERIFY_OK(gckOS_DestroySignal(os, node->sync[i].signal)); |
| } |
| } |
| |
| gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, node)); |
| } |
| |
| return status; |
| } |
| |
| gceSTATUS |
| gckVIDMEM_NODE_AllocateLinear( |
| IN gckKERNEL Kernel, |
| IN gckVIDMEM VideoMemory, |
| IN gcePOOL Pool, |
| IN gceVIDMEM_TYPE Type, |
| IN gctUINT32 Flag, |
| IN gctUINT32 Alignment, |
| IN gctBOOL Specified, |
| IN OUT gctSIZE_T * Bytes, |
| OUT gckVIDMEM_NODE * NodeObject |
| ) |
|