gpu: pvr: move debugfs infrastructure to its own files
[sgx.git] / pvr / devicemem.c
index 79866aa..3912f2b 100644 (file)
 #include "services_headers.h"
 #include "buffer_manager.h"
 #include "pdump_km.h"
-#include "sgxmmu.h"
-#include "sgxapi_km.h"
 #include "pvr_bridge_km.h"
 
-#include "linux/kernel.h"
-#include "linux/pagemap.h"
+#include <linux/pagemap.h>
 
 static enum PVRSRV_ERROR AllocDeviceMem(void *hDevCookie, void *hDevMemHeap,
-                                  u32 ui32Flags, u32 ui32Size,
-                                  u32 ui32Alignment,
-                                  struct PVRSRV_KERNEL_MEM_INFO **ppsMemInfo);
+                               u32 ui32Flags, u32 ui32Size, u32 ui32Alignment,
+                               struct PVRSRV_KERNEL_MEM_INFO **ppsMemInfo);
+
+struct RESMAN_MAP_DEVICE_MEM_DATA {
+       struct PVRSRV_KERNEL_MEM_INFO *psMemInfo;
+       struct PVRSRV_KERNEL_MEM_INFO *psSrcMemInfo;
+};
+
+static inline void get_page_details(u32 vaddr, size_t byte_size,
+               u32 *page_offset_out, int *page_count_out)
+{
+       size_t host_page_size;
+       u32 page_offset;
+       int page_count;
+
+       host_page_size = PAGE_SIZE;
+       page_offset = vaddr & (host_page_size - 1);
+       page_count = PAGE_ALIGN(byte_size + page_offset) / host_page_size;
+
+       *page_offset_out = page_offset;
+       *page_count_out = page_count;
+}
+
+static inline int get_page_count(u32 vaddr, size_t byte_size)
+{
+       u32 page_offset;
+       int page_count;
+
+       get_page_details(vaddr, byte_size, &page_offset, &page_count);
+
+       return page_count;
+}
 
 enum PVRSRV_ERROR PVRSRVGetDeviceMemHeapsKM(void *hDevCookie,
                                        struct PVRSRV_HEAP_INFO *psHeapInfo)
@@ -49,8 +75,14 @@ enum PVRSRV_ERROR PVRSRVGetDeviceMemHeapsKM(void *hDevCookie,
        struct DEVICE_MEMORY_HEAP_INFO *psDeviceMemoryHeap;
        u32 i;
 
+       if (hDevCookie == NULL) {
+               PVR_DPF(PVR_DBG_ERROR,
+                        "PVRSRVGetDeviceMemHeapsKM: hDevCookie invalid");
+               PVR_DBG_BREAK;
+               return PVRSRV_ERROR_INVALID_PARAMS;
+       }
+
        psDeviceNode = (struct PVRSRV_DEVICE_NODE *)hDevCookie;
-       PVR_ASSERT(psDeviceNode != NULL);
 
        ui32HeapCount = psDeviceNode->sDevMemoryInfo.ui32HeapCount;
        psDeviceMemoryHeap = psDeviceNode->sDevMemoryInfo.psDeviceMemoryHeap;
@@ -58,7 +90,6 @@ enum PVRSRV_ERROR PVRSRVGetDeviceMemHeapsKM(void *hDevCookie,
        PVR_ASSERT(ui32HeapCount <= PVRSRV_MAX_CLIENT_HEAPS);
 
        for (i = 0; i < ui32HeapCount; i++) {
-
                psHeapInfo[i].ui32HeapID = psDeviceMemoryHeap[i].ui32HeapID;
                psHeapInfo[i].hDevMemHeap = psDeviceMemoryHeap[i].hDevMemHeap;
                psHeapInfo[i].sDevVAddrBase =
@@ -70,7 +101,7 @@ enum PVRSRV_ERROR PVRSRVGetDeviceMemHeapsKM(void *hDevCookie,
 
        for (; i < PVRSRV_MAX_CLIENT_HEAPS; i++) {
                OSMemSet(psHeapInfo + i, 0, sizeof(*psHeapInfo));
-               psHeapInfo[i].ui32HeapID = (u32) SGX_UNDEFINED_HEAP_ID;
+               psHeapInfo[i].ui32HeapID = (u32) PVRSRV_UNDEFINED_HEAP_ID;
        }
 
        return PVRSRV_OK;
@@ -91,8 +122,14 @@ enum PVRSRV_ERROR PVRSRVCreateDeviceMemContextKM(void *hDevCookie,
        struct IMG_DEV_PHYADDR sPDDevPAddr;
        u32 i;
 
+       if (hDevCookie == NULL) {
+               PVR_DPF(PVR_DBG_ERROR,
+                        "PVRSRVCreateDeviceMemContextKM: hDevCookie invalid");
+               PVR_DBG_BREAK;
+               return PVRSRV_ERROR_INVALID_PARAMS;
+       }
+
        psDeviceNode = (struct PVRSRV_DEVICE_NODE *)hDevCookie;
-       PVR_ASSERT(psDeviceNode != NULL);
 
        ui32HeapCount = psDeviceNode->sDevMemoryInfo.ui32HeapCount;
        psDeviceMemoryHeap = psDeviceNode->sDevMemoryInfo.psDeviceMemoryHeap;
@@ -102,54 +139,47 @@ enum PVRSRV_ERROR PVRSRVCreateDeviceMemContextKM(void *hDevCookie,
        hDevMemContext = BM_CreateContext(psDeviceNode,
                                          &sPDDevPAddr, psPerProc, pbCreated);
        if (hDevMemContext == NULL) {
-               PVR_DPF(PVR_DBG_ERROR, "PVRSRVCreateDeviceMemContextKM: "
-                         "Failed BM_CreateContext");
+               PVR_DPF(PVR_DBG_ERROR,
+               "PVRSRVCreateDeviceMemContextKM: Failed BM_CreateContext");
                return PVRSRV_ERROR_OUT_OF_MEMORY;
        }
 
-       for (i = 0; i < ui32HeapCount; i++)
+       for (i = 0; i < ui32HeapCount; i++) {
                switch (psDeviceMemoryHeap[i].DevMemHeapType) {
                case DEVICE_MEMORY_HEAP_SHARED_EXPORTED:
-                       {
-
-                               psHeapInfo[ui32ClientHeapCount].ui32HeapID =
+                       psHeapInfo[ui32ClientHeapCount].ui32HeapID =
                                    psDeviceMemoryHeap[i].ui32HeapID;
-                               psHeapInfo[ui32ClientHeapCount].hDevMemHeap =
+                       psHeapInfo[ui32ClientHeapCount].hDevMemHeap =
                                    psDeviceMemoryHeap[i].hDevMemHeap;
-                               psHeapInfo[ui32ClientHeapCount].sDevVAddrBase =
+                       psHeapInfo[ui32ClientHeapCount].sDevVAddrBase =
                                    psDeviceMemoryHeap[i].sDevVAddrBase;
-                               psHeapInfo[ui32ClientHeapCount].
-                                   ui32HeapByteSize =
+                       psHeapInfo[ui32ClientHeapCount].ui32HeapByteSize =
                                    psDeviceMemoryHeap[i].ui32HeapSize;
-                               psHeapInfo[ui32ClientHeapCount].ui32Attribs =
+                       psHeapInfo[ui32ClientHeapCount].ui32Attribs =
                                    psDeviceMemoryHeap[i].ui32Attribs;
-                               pbShared[ui32ClientHeapCount] = IMG_TRUE;
-                               ui32ClientHeapCount++;
-                               break;
-                       }
+                       pbShared[ui32ClientHeapCount] = IMG_TRUE;
+                       ui32ClientHeapCount++;
+                       break;
                case DEVICE_MEMORY_HEAP_PERCONTEXT:
-                       {
-                               hDevMemHeap = BM_CreateHeap(hDevMemContext,
-                                                           &psDeviceMemoryHeap
-                                                           [i]);
+                       hDevMemHeap = BM_CreateHeap(hDevMemContext,
+                                                   &psDeviceMemoryHeap[i]);
 
-                               psHeapInfo[ui32ClientHeapCount].ui32HeapID =
+                       psHeapInfo[ui32ClientHeapCount].ui32HeapID =
                                    psDeviceMemoryHeap[i].ui32HeapID;
-                               psHeapInfo[ui32ClientHeapCount].hDevMemHeap =
+                       psHeapInfo[ui32ClientHeapCount].hDevMemHeap =
                                    hDevMemHeap;
-                               psHeapInfo[ui32ClientHeapCount].sDevVAddrBase =
+                       psHeapInfo[ui32ClientHeapCount].sDevVAddrBase =
                                    psDeviceMemoryHeap[i].sDevVAddrBase;
-                               psHeapInfo[ui32ClientHeapCount].
-                                   ui32HeapByteSize =
+                       psHeapInfo[ui32ClientHeapCount].ui32HeapByteSize =
                                    psDeviceMemoryHeap[i].ui32HeapSize;
-                               psHeapInfo[ui32ClientHeapCount].ui32Attribs =
+                       psHeapInfo[ui32ClientHeapCount].ui32Attribs =
                                    psDeviceMemoryHeap[i].ui32Attribs;
-                               pbShared[ui32ClientHeapCount] = IMG_FALSE;
+                       pbShared[ui32ClientHeapCount] = IMG_FALSE;
 
-                               ui32ClientHeapCount++;
-                               break;
-                       }
+                       ui32ClientHeapCount++;
+                       break;
                }
+       }
 
        *pui32ClientHeapCount = ui32ClientHeapCount;
        *phDevMemContext = hDevMemContext;
@@ -184,58 +214,55 @@ enum PVRSRV_ERROR PVRSRVGetDeviceMemHeapInfoKM(void *hDevCookie,
        void *hDevMemHeap;
        u32 i;
 
+       if (hDevCookie == NULL) {
+               PVR_DPF(PVR_DBG_ERROR,
+                        "PVRSRVGetDeviceMemHeapInfoKM: hDevCookie invalid");
+               PVR_DBG_BREAK;
+               return PVRSRV_ERROR_INVALID_PARAMS;
+       }
+
        psDeviceNode = (struct PVRSRV_DEVICE_NODE *)hDevCookie;
-       PVR_ASSERT(psDeviceNode != NULL);
 
        ui32HeapCount = psDeviceNode->sDevMemoryInfo.ui32HeapCount;
        psDeviceMemoryHeap = psDeviceNode->sDevMemoryInfo.psDeviceMemoryHeap;
 
        PVR_ASSERT(ui32HeapCount <= PVRSRV_MAX_CLIENT_HEAPS);
 
-       for (i = 0; i < ui32HeapCount; i++)
+       for (i = 0; i < ui32HeapCount; i++) {
                switch (psDeviceMemoryHeap[i].DevMemHeapType) {
                case DEVICE_MEMORY_HEAP_SHARED_EXPORTED:
-                       {
-
-                               psHeapInfo[ui32ClientHeapCount].ui32HeapID =
+                       psHeapInfo[ui32ClientHeapCount].ui32HeapID =
                                    psDeviceMemoryHeap[i].ui32HeapID;
-                               psHeapInfo[ui32ClientHeapCount].hDevMemHeap =
+                       psHeapInfo[ui32ClientHeapCount].hDevMemHeap =
                                    psDeviceMemoryHeap[i].hDevMemHeap;
-                               psHeapInfo[ui32ClientHeapCount].sDevVAddrBase =
+                       psHeapInfo[ui32ClientHeapCount].sDevVAddrBase =
                                    psDeviceMemoryHeap[i].sDevVAddrBase;
-                               psHeapInfo[ui32ClientHeapCount].
-                                   ui32HeapByteSize =
+                       psHeapInfo[ui32ClientHeapCount].ui32HeapByteSize =
                                    psDeviceMemoryHeap[i].ui32HeapSize;
-                               psHeapInfo[ui32ClientHeapCount].ui32Attribs =
+                       psHeapInfo[ui32ClientHeapCount].ui32Attribs =
                                    psDeviceMemoryHeap[i].ui32Attribs;
-                               pbShared[ui32ClientHeapCount] = IMG_TRUE;
-                               ui32ClientHeapCount++;
-                               break;
-                       }
+                       pbShared[ui32ClientHeapCount] = IMG_TRUE;
+                       ui32ClientHeapCount++;
+                       break;
                case DEVICE_MEMORY_HEAP_PERCONTEXT:
-                       {
-                               hDevMemHeap = BM_CreateHeap(hDevMemContext,
-                                                           &psDeviceMemoryHeap
-                                                           [i]);
-
-                               psHeapInfo[ui32ClientHeapCount].ui32HeapID =
+                       hDevMemHeap = BM_CreateHeap(hDevMemContext,
+                                                   &psDeviceMemoryHeap[i]);
+                       psHeapInfo[ui32ClientHeapCount].ui32HeapID =
                                    psDeviceMemoryHeap[i].ui32HeapID;
-                               psHeapInfo[ui32ClientHeapCount].hDevMemHeap =
+                       psHeapInfo[ui32ClientHeapCount].hDevMemHeap =
                                    hDevMemHeap;
-                               psHeapInfo[ui32ClientHeapCount].sDevVAddrBase =
+                       psHeapInfo[ui32ClientHeapCount].sDevVAddrBase =
                                    psDeviceMemoryHeap[i].sDevVAddrBase;
-                               psHeapInfo[ui32ClientHeapCount].
-                                   ui32HeapByteSize =
+                       psHeapInfo[ui32ClientHeapCount].ui32HeapByteSize =
                                    psDeviceMemoryHeap[i].ui32HeapSize;
-                               psHeapInfo[ui32ClientHeapCount].ui32Attribs =
+                       psHeapInfo[ui32ClientHeapCount].ui32Attribs =
                                    psDeviceMemoryHeap[i].ui32Attribs;
-                               pbShared[ui32ClientHeapCount] = IMG_FALSE;
+                       pbShared[ui32ClientHeapCount] = IMG_FALSE;
 
-                               ui32ClientHeapCount++;
-                               break;
-                       }
+                       ui32ClientHeapCount++;
+                       break;
                }
-
+       }
        *pui32ClientHeapCount = ui32ClientHeapCount;
 
        return PVRSRV_OK;
@@ -256,7 +283,7 @@ static enum PVRSRV_ERROR AllocDeviceMem(void *hDevCookie, void *hDevMemHeap,
 
        *ppsMemInfo = NULL;
 
-       if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP,
+       if (OSAllocMem(PVRSRV_PAGEABLE_SELECT,
                       sizeof(struct PVRSRV_KERNEL_MEM_INFO),
                       (void **) &psMemInfo, NULL) != PVRSRV_OK) {
                PVR_DPF(PVR_DBG_ERROR,
@@ -264,6 +291,8 @@ static enum PVRSRV_ERROR AllocDeviceMem(void *hDevCookie, void *hDevMemHeap,
                return PVRSRV_ERROR_OUT_OF_MEMORY;
        }
 
+       OSMemSet(psMemInfo, 0, sizeof(*psMemInfo));
+
        psMemBlock = &(psMemInfo->sMemBlk);
 
        psMemInfo->ui32Flags = ui32Flags | PVRSRV_MEM_RAM_BACKED_ALLOCATION;
@@ -273,7 +302,7 @@ static enum PVRSRV_ERROR AllocDeviceMem(void *hDevCookie, void *hDevMemHeap,
 
        if (!bBMError) {
                PVR_DPF(PVR_DBG_ERROR, "AllocDeviceMem: BM_Alloc Failed");
-               OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP,
+               OSFreeMem(PVRSRV_PAGEABLE_SELECT,
                          sizeof(struct PVRSRV_KERNEL_MEM_INFO), psMemInfo,
                          NULL);
                return PVRSRV_ERROR_OUT_OF_MEMORY;
@@ -282,10 +311,13 @@ static enum PVRSRV_ERROR AllocDeviceMem(void *hDevCookie, void *hDevMemHeap,
        psMemBlock->sDevVirtAddr = BM_HandleToDevVaddr(hBuffer);
        psMemBlock->hOSMemHandle = BM_HandleToOSMemHandle(hBuffer);
 
-       psMemBlock->hBuffer = (void *) hBuffer;
+       psMemBlock->hBuffer = (void *)hBuffer;
        psMemInfo->pvLinAddrKM = BM_HandleToCpuVaddr(hBuffer);
        psMemInfo->sDevVAddr = psMemBlock->sDevVirtAddr;
        psMemInfo->ui32AllocSize = ui32Size;
+
+       psMemInfo->pvSysBackupBuffer = NULL;
+
        *ppsMemInfo = psMemInfo;
 
        return PVRSRV_OK;
@@ -300,8 +332,12 @@ static enum PVRSRV_ERROR FreeDeviceMem(struct PVRSRV_KERNEL_MEM_INFO *psMemInfo)
 
        hBuffer = psMemInfo->sMemBlk.hBuffer;
        BM_Free(hBuffer, psMemInfo->ui32Flags);
-       OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP,
-                 sizeof(struct PVRSRV_KERNEL_MEM_INFO),
+
+       if (psMemInfo->pvSysBackupBuffer)
+               OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, psMemInfo->ui32AllocSize,
+                         psMemInfo->pvSysBackupBuffer, NULL);
+
+       OSFreeMem(PVRSRV_PAGEABLE_SELECT, sizeof(struct PVRSRV_KERNEL_MEM_INFO),
                  psMemInfo, NULL);
 
        return PVRSRV_OK;
@@ -317,7 +353,7 @@ enum PVRSRV_ERROR PVRSRVAllocSyncInfoKM(void *hDevCookie, void *hDevMemContext,
        struct PVRSRV_KERNEL_SYNC_INFO *psKernelSyncInfo;
        struct PVRSRV_SYNC_DATA *psSyncData;
 
-       eError = OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP,
+       eError = OSAllocMem(PVRSRV_PAGEABLE_SELECT,
                            sizeof(struct PVRSRV_KERNEL_SYNC_INFO),
                            (void **) &psKernelSyncInfo, NULL);
        if (eError != PVRSRV_OK) {
@@ -332,16 +368,15 @@ enum PVRSRV_ERROR PVRSRVAllocSyncInfoKM(void *hDevCookie, void *hDevMemContext,
        hSyncDevMemHeap = psDevMemoryInfo->psDeviceMemoryHeap[psDevMemoryInfo->
                                                ui32SyncHeapID].hDevMemHeap;
 
-       eError = AllocDeviceMem(hDevCookie, hSyncDevMemHeap, 0,
-                               sizeof(struct PVRSRV_SYNC_DATA),
-                               sizeof(u32),
+       eError = AllocDeviceMem(hDevCookie, hSyncDevMemHeap,
+                               PVRSRV_MEM_CACHE_CONSISTENT,
+                               sizeof(struct PVRSRV_SYNC_DATA), sizeof(u32),
                                &psKernelSyncInfo->psSyncDataMemInfoKM);
 
        if (eError != PVRSRV_OK) {
-
                PVR_DPF(PVR_DBG_ERROR,
                         "PVRSRVAllocSyncInfoKM: Failed to alloc memory");
-               OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP,
+               OSFreeMem(PVRSRV_PAGEABLE_SELECT,
                          sizeof(struct PVRSRV_KERNEL_SYNC_INFO),
                          psKernelSyncInfo, NULL);
                return PVRSRV_ERROR_OUT_OF_MEMORY;
@@ -358,6 +393,13 @@ enum PVRSRV_ERROR PVRSRVAllocSyncInfoKM(void *hDevCookie, void *hDevMemContext,
        psSyncData->ui32LastOpDumpVal = 0;
        psSyncData->ui32LastReadOpDumpVal = 0;
 
+#if defined(PDUMP)
+       PDUMPMEM(psKernelSyncInfo->psSyncDataMemInfoKM->pvLinAddrKM,
+                psKernelSyncInfo->psSyncDataMemInfoKM, 0,
+                psKernelSyncInfo->psSyncDataMemInfoKM->ui32AllocSize,
+                0, MAKEUNIQUETAG(psKernelSyncInfo->psSyncDataMemInfoKM));
+#endif
+
        psKernelSyncInfo->sWriteOpsCompleteDevVAddr.uiAddr =
            psKernelSyncInfo->psSyncDataMemInfoKM->sDevVAddr.uiAddr +
            offsetof(struct PVRSRV_SYNC_DATA, ui32WriteOpsComplete);
@@ -376,8 +418,69 @@ enum PVRSRV_ERROR PVRSRVFreeSyncInfoKM(
                        struct PVRSRV_KERNEL_SYNC_INFO *psKernelSyncInfo)
 {
        FreeDeviceMem(psKernelSyncInfo->psSyncDataMemInfoKM);
-       OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP,
-               sizeof(struct PVRSRV_KERNEL_SYNC_INFO), psKernelSyncInfo, NULL);
+       OSFreeMem(PVRSRV_PAGEABLE_SELECT,
+                 sizeof(struct PVRSRV_KERNEL_SYNC_INFO), psKernelSyncInfo,
+                 NULL);
+
+       return PVRSRV_OK;
+}
+
+static inline
+void get_syncinfo(struct PVRSRV_KERNEL_SYNC_INFO *syncinfo)
+{
+       syncinfo->refcount++;
+}
+
+static inline
+void put_syncinfo(struct PVRSRV_KERNEL_SYNC_INFO *syncinfo)
+{
+       struct PVRSRV_DEVICE_NODE *dev = syncinfo->dev_cookie;
+
+       syncinfo->refcount--;
+
+       if (!syncinfo->refcount) {
+               HASH_Remove(dev->sync_table,
+                           syncinfo->phys_addr.uiAddr);
+               PVRSRVFreeSyncInfoKM(syncinfo);
+       }
+}
+
+static inline
+enum PVRSRV_ERROR alloc_or_reuse_syncinfo(void *dev_cookie,
+                   void *mem_context_handle,
+                   struct PVRSRV_KERNEL_SYNC_INFO **syncinfo,
+                   struct IMG_SYS_PHYADDR *phys_addr)
+{
+       enum PVRSRV_ERROR error;
+       struct PVRSRV_DEVICE_NODE *dev;
+
+       dev = (struct PVRSRV_DEVICE_NODE *) dev_cookie;
+
+       *syncinfo = (struct PVRSRV_KERNEL_SYNC_INFO *)
+                                       HASH_Retrieve(dev->sync_table,
+                                                     phys_addr->uiAddr);
+
+       if (!*syncinfo) {
+               /* Dont' have one so create one */
+               error = PVRSRVAllocSyncInfoKM(dev_cookie, mem_context_handle,
+                                              syncinfo);
+
+               if (error != PVRSRV_OK)
+                       return error;
+
+               /* Setup our extra data */
+               (*syncinfo)->phys_addr.uiAddr = phys_addr->uiAddr;
+               (*syncinfo)->dev_cookie = dev_cookie;
+               (*syncinfo)->refcount = 1;
+
+               if (!HASH_Insert(dev->sync_table, phys_addr->uiAddr,
+                           (u32) *syncinfo)) {
+                       PVR_DPF(PVR_DBG_ERROR, "alloc_or_reuse_syncinfo: "
+                               "Failed to add syncobject to hash table");
+                       return PVRSRV_ERROR_GENERIC;
+               }
+       } else
+               get_syncinfo(*syncinfo);
 
        return PVRSRV_OK;
 }
@@ -389,6 +492,40 @@ static enum PVRSRV_ERROR FreeDeviceMemCallBack(void *pvParam, u32 ui32Param)
 
        PVR_UNREFERENCED_PARAMETER(ui32Param);
 
+       psMemInfo->ui32RefCount--;
+
+       if (psMemInfo->ui32Flags & PVRSRV_MEM_EXPORTED) {
+               void *hMemInfo = NULL;
+
+               if (psMemInfo->ui32RefCount != 0) {
+                       PVR_DPF(PVR_DBG_ERROR, "FreeDeviceMemCallBack: "
+                                       "mappings are open in other processes");
+                       return PVRSRV_ERROR_GENERIC;
+               }
+
+               eError = PVRSRVFindHandle(KERNEL_HANDLE_BASE,
+                                         &hMemInfo,
+                                         psMemInfo,
+                                         PVRSRV_HANDLE_TYPE_MEM_INFO);
+               if (eError != PVRSRV_OK) {
+                       PVR_DPF(PVR_DBG_ERROR, "FreeDeviceMemCallBack: "
+                               "can't find exported meminfo in the "
+                               "global handle list");
+                       return eError;
+               }
+
+               eError = PVRSRVReleaseHandle(KERNEL_HANDLE_BASE,
+                                            hMemInfo,
+                                            PVRSRV_HANDLE_TYPE_MEM_INFO);
+               if (eError != PVRSRV_OK) {
+                       PVR_DPF(PVR_DBG_ERROR, "FreeDeviceMemCallBack: "
+                       "PVRSRVReleaseHandle failed for exported meminfo");
+                       return eError;
+               }
+       }
+
+       PVR_ASSERT(psMemInfo->ui32RefCount == 0);
+
        if (psMemInfo->psKernelSyncInfo)
                eError = PVRSRVFreeSyncInfoKM(psMemInfo->psKernelSyncInfo);
 
@@ -437,7 +574,6 @@ enum PVRSRV_ERROR PVRSRVAllocDeviceMemKM(void *hDevCookie,
        if (ui32Flags & PVRSRV_MEM_NO_SYNCOBJ) {
                psMemInfo->psKernelSyncInfo = NULL;
        } else {
-
                psBMHeap = (struct BM_HEAP *)hDevMemHeap;
                hDevMemContext = (void *) psBMHeap->pBMContext;
                eError = PVRSRVAllocSyncInfoKM(hDevCookie,
@@ -452,18 +588,18 @@ enum PVRSRV_ERROR PVRSRVAllocDeviceMemKM(void *hDevCookie,
        if (ui32Flags & PVRSRV_MEM_NO_RESMAN) {
                psMemInfo->sMemBlk.hResItem = NULL;
        } else {
-
                psMemInfo->sMemBlk.hResItem =
                    ResManRegisterRes(psPerProc->hResManContext,
                                      RESMAN_TYPE_DEVICEMEM_ALLOCATION,
                                      psMemInfo, 0, FreeDeviceMemCallBack);
                if (psMemInfo->sMemBlk.hResItem == NULL) {
-
                        eError = PVRSRV_ERROR_OUT_OF_MEMORY;
                        goto free_mainalloc;
                }
        }
 
+       psMemInfo->ui32RefCount++;
+
        return PVRSRV_OK;
 
 free_mainalloc:
@@ -475,7 +611,7 @@ free_mainalloc:
 enum PVRSRV_ERROR PVRSRVDissociateDeviceMemKM(void *hDevCookie,
                                  struct PVRSRV_KERNEL_MEM_INFO *psMemInfo)
 {
-       enum PVRSRV_ERROR eError = PVRSRV_OK;
+       enum PVRSRV_ERROR eError;
        struct PVRSRV_DEVICE_NODE *psDeviceNode = hDevCookie;
 
        PVR_UNREFERENCED_PARAMETER(hDevCookie);
@@ -483,8 +619,10 @@ enum PVRSRV_ERROR PVRSRVDissociateDeviceMemKM(void *hDevCookie,
        if (!psMemInfo)
                return PVRSRV_ERROR_INVALID_PARAMS;
 
-       ResManDissociateRes(psMemInfo->sMemBlk.hResItem,
-                           psDeviceNode->hResManContext);
+       eError = ResManDissociateRes(psMemInfo->sMemBlk.hResItem,
+                                   psDeviceNode->hResManContext);
+
+       PVR_ASSERT(eError == PVRSRV_OK);
 
        return eError;
 }
@@ -508,6 +646,7 @@ enum PVRSRV_ERROR PVRSRVUnwrapExtMemoryKM(
                return PVRSRV_ERROR_INVALID_PARAMS;
 
        ResManFreeResByPtr(psMemInfo->sMemBlk.hResItem);
+
        return PVRSRV_OK;
 }
 
@@ -515,39 +654,49 @@ static enum PVRSRV_ERROR UnwrapExtMemoryCallBack(void *pvParam, u32 ui32Param)
 {
        enum PVRSRV_ERROR eError = PVRSRV_OK;
        struct PVRSRV_KERNEL_MEM_INFO *psMemInfo = pvParam;
-       void *hOSWrapMem = NULL;
-       struct BM_BUF *psBMBuf;
+       void *hOSWrapMem;
 
        PVR_UNREFERENCED_PARAMETER(ui32Param);
 
-       psBMBuf = (struct BM_BUF *)psMemInfo->sMemBlk.hBuffer;
+       hOSWrapMem = psMemInfo->sMemBlk.hOSWrapMem;
 
-       if ((psBMBuf->ui32RefCount == 1) && (psMemInfo->psKernelSyncInfo)) {
-               eError = PVRSRVFreeSyncInfoKM(psMemInfo->psKernelSyncInfo);
-               hOSWrapMem = psBMBuf->hOSWrapMem;
+       if (psMemInfo->psKernelSyncInfo)
+               put_syncinfo(psMemInfo->psKernelSyncInfo);
+
+       if (psMemInfo->sMemBlk.psIntSysPAddr) {
+               int page_count;
+
+               page_count = get_page_count((u32)psMemInfo->pvLinAddrKM,
+                               psMemInfo->ui32AllocSize);
+
+               OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP,
+                         page_count * sizeof(struct IMG_SYS_PHYADDR),
+                         psMemInfo->sMemBlk.psIntSysPAddr, NULL);
        }
 
-       if (eError == PVRSRV_OK)
+       if (eError == PVRSRV_OK) {
+               psMemInfo->ui32RefCount--;
                eError = FreeDeviceMem(psMemInfo);
+       }
 
        if (hOSWrapMem)
-               OSReleasePhysPageAddr(hOSWrapMem, IMG_TRUE);
+               OSReleasePhysPageAddr(hOSWrapMem);
 
        return eError;
 }
 
 enum PVRSRV_ERROR PVRSRVWrapExtMemoryKM(void *hDevCookie,
-                                  struct PVRSRV_PER_PROCESS_DATA *psPerProc,
-                                  u32 ui32ByteSize, u32 ui32PageOffset,
-                                  IMG_BOOL bPhysContig,
-                                  struct IMG_SYS_PHYADDR *psExtSysPAddr,
-                                  void *pvLinAddr,
-                                  struct PVRSRV_KERNEL_MEM_INFO **ppsMemInfo)
+                           struct PVRSRV_PER_PROCESS_DATA *psPerProc,
+                           void *hDevMemContext, u32 ui32ByteSize,
+                           u32 ui32PageOffset, IMG_BOOL bPhysContig,
+                           struct IMG_SYS_PHYADDR *psExtSysPAddr,
+                           void *pvLinAddr,
+                           struct PVRSRV_KERNEL_MEM_INFO **ppsMemInfo)
 {
        struct PVRSRV_KERNEL_MEM_INFO *psMemInfo = NULL;
        struct DEVICE_MEMORY_INFO *psDevMemoryInfo;
        u32 ui32HostPageSize = HOST_PAGESIZE();
-       void *hDevMemHeap, *hDevMemContext;
+       void *hDevMemHeap = NULL;
        struct PVRSRV_DEVICE_NODE *psDeviceNode;
        void *hBuffer;
        struct PVRSRV_MEMBLK *psMemBlock;
@@ -557,79 +706,72 @@ enum PVRSRV_ERROR PVRSRVWrapExtMemoryKM(void *hDevCookie,
        void *pvPageAlignedCPUVAddr;
        struct IMG_SYS_PHYADDR *psIntSysPAddr = NULL;
        void *hOSWrapMem = NULL;
-       struct BM_BUF *psBMBuf;
-       struct IMG_SYS_PHYADDR *pPageList = psExtSysPAddr;
-       u32 ui32PageCount;
-
-       u32 ui32CalculatedPageOffset = ((u32)pvLinAddr) & ~PAGE_MASK;
-       if (ui32CalculatedPageOffset != ui32PageOffset) {
-               PVR_DPF(PVR_DBG_ERROR, "PVRSRVWrapExtMemoryKM: "
-                       "offset from address not match offset param");
-               return PVRSRV_ERROR_BAD_MAPPING;
-       }
+       struct DEVICE_MEMORY_HEAP_INFO *psDeviceMemoryHeap;
+       int page_count = 0;
+       u32 i;
 
        psDeviceNode = (struct PVRSRV_DEVICE_NODE *)hDevCookie;
        PVR_ASSERT(psDeviceNode != NULL);
-       psDevMemoryInfo = &psDeviceNode->sDevMemoryInfo;
-       hDevMemHeap =
-           psDevMemoryInfo->psDeviceMemoryHeap[psDevMemoryInfo->
-                                               ui32MappingHeapID].hDevMemHeap;
 
-       ui32PageCount =
-           HOST_PAGEALIGN(ui32ByteSize + ui32PageOffset) / ui32HostPageSize;
+       if (psDeviceNode == NULL) {
+               PVR_DPF(PVR_DBG_ERROR,
+                        "PVRSRVWrapExtMemoryKM: invalid parameter");
+               return PVRSRV_ERROR_INVALID_PARAMS;
+       }
 
        if (pvLinAddr) {
-               pvPageAlignedCPUVAddr =
-                   (void *) ((u8 *) pvLinAddr - ui32PageOffset);
+               get_page_details((u32)pvLinAddr, ui32ByteSize,
+                               &ui32PageOffset, &page_count);
+               pvPageAlignedCPUVAddr = (void *)((u8 *) pvLinAddr -
+                                       ui32PageOffset);
 
                if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP,
-                              ui32PageCount * sizeof(struct IMG_SYS_PHYADDR),
-                              (void **) &psIntSysPAddr,
-                              NULL) != PVRSRV_OK) {
+                              page_count * sizeof(struct IMG_SYS_PHYADDR),
+                              (void **)&psIntSysPAddr, NULL) != PVRSRV_OK) {
                        PVR_DPF(PVR_DBG_ERROR, "PVRSRVWrapExtMemoryKM: "
                                        "Failed to alloc memory for block");
                        return PVRSRV_ERROR_OUT_OF_MEMORY;
                }
 
-               /* let's start by getting the address of the first page */
                eError = OSAcquirePhysPageAddr(pvPageAlignedCPUVAddr,
-                                              ui32HostPageSize,
-                                              psIntSysPAddr, &hOSWrapMem,
-                                              IMG_TRUE);
+                                      page_count * ui32HostPageSize,
+                                      psIntSysPAddr, &hOSWrapMem);
                if (eError != PVRSRV_OK) {
-                       PVR_DPF(PVR_DBG_ERROR, "PVRSRVWrapExtMemoryKM: "
-                                       "Failed to alloc memory for block");
-                       eError = PVRSRV_ERROR_OUT_OF_MEMORY;
-                       goto ErrorExitPhase1;
-               }
-               /* now check if this memory address is already wrapped */
-               if (BM_IsWrapped(hDevMemHeap, ui32PageOffset,
-                                psIntSysPAddr[0])) {
-                       /* already wrapped */
-                       OSReleasePhysPageAddr(hOSWrapMem, IMG_TRUE);
-                       hOSWrapMem = NULL;
-               } else if (ui32PageCount > 1) {
-                       OSReleasePhysPageAddr(hOSWrapMem, IMG_TRUE);
-                       hOSWrapMem = NULL;
-                       /* the memory is going to wrapped for the first time,
-                        * so we need full page list */
-                       eError = OSAcquirePhysPageAddr(pvPageAlignedCPUVAddr,
-                                                      ui32PageCount *
-                                                      ui32HostPageSize,
-                                                      psIntSysPAddr,
-                                                      &hOSWrapMem,
-                                                      IMG_TRUE);
-                       if (eError != PVRSRV_OK) {
-                               PVR_DPF(PVR_DBG_ERROR, "PVRSRVWrapExtMemoryKM:"
-                                         " Failed to alloc memory for block");
+                       PVR_DPF(PVR_DBG_ERROR, "PVRSRVWrapExtMemoryKM:"
+                                  " Failed to alloc memory for block");
                                eError = PVRSRV_ERROR_OUT_OF_MEMORY;
                                goto ErrorExitPhase1;
-                       }
                }
-
                psExtSysPAddr = psIntSysPAddr;
-       } else {
+               bPhysContig = IMG_FALSE;
+       }
 
+       psDevMemoryInfo =
+           &((struct BM_CONTEXT *)hDevMemContext)->psDeviceNode->
+                                                           sDevMemoryInfo;
+       psDeviceMemoryHeap = psDevMemoryInfo->psDeviceMemoryHeap;
+       for (i = 0; i < PVRSRV_MAX_CLIENT_HEAPS; i++) {
+               if (HEAP_IDX(psDeviceMemoryHeap[i].ui32HeapID) ==
+                   psDevMemoryInfo->ui32MappingHeapID) {
+                       if (psDeviceMemoryHeap[i].DevMemHeapType ==
+                           DEVICE_MEMORY_HEAP_PERCONTEXT) {
+                               hDevMemHeap =
+                                   BM_CreateHeap(hDevMemContext,
+                                                 &psDeviceMemoryHeap[i]);
+                       } else {
+                               hDevMemHeap =
+                                   psDevMemoryInfo->psDeviceMemoryHeap[i].
+                                   hDevMemHeap;
+                       }
+                       break;
+               }
+       }
+
+       if (hDevMemHeap == NULL) {
+               PVR_DPF(PVR_DBG_ERROR,
+                        "PVRSRVWrapExtMemoryKM: unable to find mapping heap");
+               eError = PVRSRV_ERROR_GENERIC;
+               goto ErrorExitPhase2;
        }
 
        if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP,
@@ -643,35 +785,24 @@ enum PVRSRV_ERROR PVRSRVWrapExtMemoryKM(void *hDevCookie,
 
        OSMemSet(psMemInfo, 0, sizeof(*psMemInfo));
        psMemBlock = &(psMemInfo->sMemBlk);
-       bBMError = BM_Wrap(hDevMemHeap, ui32ByteSize, ui32PageOffset,
-                          bPhysContig, psExtSysPAddr,
-                          IMG_TRUE, NULL, &psMemInfo->ui32Flags, &hBuffer);
+
+       bBMError = BM_Wrap(hDevMemHeap,
+                          ui32ByteSize,
+                          ui32PageOffset,
+                          bPhysContig,
+                          psExtSysPAddr,
+                          NULL, &psMemInfo->ui32Flags, &hBuffer);
        if (!bBMError) {
-               /* Alloc failed from current mapping heap, try the other one */
-               psDevMemoryInfo->ui32MappingHeapID =
-                   psDevMemoryInfo->ui32MappingHeapID ==
-                   SGX_GENERAL_MAPPING_HEAP_ID ? SGX_ALT_MAPPING_HEAP_ID :
-                   SGX_GENERAL_MAPPING_HEAP_ID;
-               hDevMemHeap =
-                   psDevMemoryInfo->psDeviceMemoryHeap[psDevMemoryInfo->
-                                               ui32MappingHeapID]. hDevMemHeap;
-               bBMError =
-                   BM_Wrap(hDevMemHeap, ui32ByteSize, ui32PageOffset,
-                           bPhysContig, psExtSysPAddr, IMG_TRUE, NULL,
-                           &psMemInfo->ui32Flags, &hBuffer);
-               if (!bBMError) {
-                       PVR_DPF(PVR_DBG_ERROR,
-                                "PVRSRVWrapExtMemoryKM: BM_Wrap Failed");
-                       eError = PVRSRV_ERROR_BAD_MAPPING;
-                       goto ErrorExitPhase2;
-               }
+               PVR_DPF(PVR_DBG_ERROR,
+                        "PVRSRVWrapExtMemoryKM: BM_Wrap Failed");
+               eError = PVRSRV_ERROR_BAD_MAPPING;
+               goto ErrorExitPhase3;
        }
-       /* wrap was successful and BM_Wrap has taken ownership of the page list,
-        * clear psIntSysPAddr here, so we don't double free the memory */
-       psIntSysPAddr = NULL;
 
        psMemBlock->sDevVirtAddr = BM_HandleToDevVaddr(hBuffer);
        psMemBlock->hOSMemHandle = BM_HandleToOSMemHandle(hBuffer);
+       psMemBlock->hOSWrapMem = hOSWrapMem;
+       psMemBlock->psIntSysPAddr = psIntSysPAddr;
 
        psMemBlock->hBuffer = (void *) hBuffer;
 
@@ -679,46 +810,49 @@ enum PVRSRV_ERROR PVRSRVWrapExtMemoryKM(void *hDevCookie,
        psMemInfo->sDevVAddr = psMemBlock->sDevVirtAddr;
        psMemInfo->ui32AllocSize = ui32ByteSize;
 
+       psMemInfo->pvSysBackupBuffer = NULL;
+
        psBMHeap = (struct BM_HEAP *)hDevMemHeap;
        hDevMemContext = (void *) psBMHeap->pBMContext;
-       psBMBuf = (struct BM_BUF *)hBuffer;
-       if (psBMBuf->ui32RefCount == 1) {
-               eError = PVRSRVAllocSyncInfoKM(hDevCookie, hDevMemContext,
-                                              &psMemInfo->psKernelSyncInfo);
-               if (eError != PVRSRV_OK)
-                       goto ErrorExitPhase2;
-               psBMBuf->pvKernelSyncInfo = psMemInfo->psKernelSyncInfo;
-               psBMBuf->hOSWrapMem = hOSWrapMem;
-       } else {
-               psMemInfo->psKernelSyncInfo = psBMBuf->pvKernelSyncInfo;
-       }
+       eError = alloc_or_reuse_syncinfo(hDevCookie,
+                                        hDevMemContext,
+                                        &psMemInfo->psKernelSyncInfo,
+                                        psExtSysPAddr);
+       if (eError != PVRSRV_OK)
+               goto ErrorExitPhase4;
+
+       psMemInfo->ui32RefCount++;
 
        psMemInfo->sMemBlk.hResItem =
            ResManRegisterRes(psPerProc->hResManContext,
                              RESMAN_TYPE_DEVICEMEM_WRAP, psMemInfo, 0,
                              UnwrapExtMemoryCallBack);
-       /* check if we were passed a page list
-        * but we didn't use use it */
-       if (pPageList && (pPageList != psExtSysPAddr))
-               OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP,
-                         ui32PageCount * sizeof(struct IMG_SYS_PHYADDR),
-                         (void *)pPageList, NULL);
 
        *ppsMemInfo = psMemInfo;
 
        return PVRSRV_OK;
 
-ErrorExitPhase2:
-       if (psMemInfo)
-               FreeDeviceMem(psMemInfo);
+ErrorExitPhase4:
+       FreeDeviceMem(psMemInfo);
+       psMemInfo = NULL;
 
+ErrorExitPhase3:
+       if (psMemInfo) {
+               OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP,
+                         sizeof(struct PVRSRV_KERNEL_MEM_INFO), psMemInfo,
+                         NULL);
+       }
+
+ErrorExitPhase2:
        if (hOSWrapMem)
-               OSReleasePhysPageAddr(hOSWrapMem, IMG_TRUE);
+               OSReleasePhysPageAddr(hOSWrapMem);
+
 ErrorExitPhase1:
-       if (psIntSysPAddr)
+       if (psIntSysPAddr) {
                OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP,
-                         ui32PageCount * sizeof(struct IMG_SYS_PHYADDR),
+                         page_count * sizeof(struct IMG_SYS_PHYADDR),
                          psIntSysPAddr, NULL);
+       }
 
        return eError;
 }
@@ -730,16 +864,50 @@ enum PVRSRV_ERROR PVRSRVUnmapDeviceMemoryKM(
                return PVRSRV_ERROR_INVALID_PARAMS;
 
        ResManFreeResByPtr(psMemInfo->sMemBlk.hResItem);
+
        return PVRSRV_OK;
 }
 
 static enum PVRSRV_ERROR UnmapDeviceMemoryCallBack(void *pvParam, u32 ui32Param)
 {
-       struct PVRSRV_KERNEL_MEM_INFO *psMemInfo = pvParam;
+       enum PVRSRV_ERROR eError;
+       struct RESMAN_MAP_DEVICE_MEM_DATA *psMapData = pvParam;
+       int page_count;
 
        PVR_UNREFERENCED_PARAMETER(ui32Param);
 
-       return FreeDeviceMem(psMemInfo);
+       page_count = get_page_count((u32)psMapData->psMemInfo->pvLinAddrKM,
+                               psMapData->psMemInfo->ui32AllocSize);
+
+       if (psMapData->psMemInfo->sMemBlk.psIntSysPAddr)
+               OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP,
+                         page_count * sizeof(struct IMG_SYS_PHYADDR),
+                         psMapData->psMemInfo->sMemBlk.psIntSysPAddr, NULL);
+
+       eError = FreeDeviceMem(psMapData->psMemInfo);
+       if (eError != PVRSRV_OK) {
+               PVR_DPF(PVR_DBG_ERROR, "UnmapDeviceMemoryCallBack: "
+                               "Failed to free DST meminfo");
+               return eError;
+       }
+
+       psMapData->psSrcMemInfo->ui32RefCount--;
+
+       PVR_ASSERT(psMapData->psSrcMemInfo->ui32RefCount != (u32) (-1));
+/*
+ * Don't free the source MemInfo as we didn't allocate it
+ * and it's not our job as the process the allocated
+ * should also free it when it's finished
+ */
+       OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP,
+                 sizeof(struct RESMAN_MAP_DEVICE_MEM_DATA), psMapData, NULL);
+
+       return eError;
+}
+
+static inline int bm_is_continuous(const struct BM_BUF *buf)
+{
+       return buf->pMapping->eCpuMemoryOrigin == hm_wrapped_virtaddr;
 }
 
 enum PVRSRV_ERROR PVRSRVMapDeviceMemoryKM(
@@ -749,9 +917,10 @@ enum PVRSRV_ERROR PVRSRVMapDeviceMemoryKM(
                              struct PVRSRV_KERNEL_MEM_INFO **ppsDstMemInfo)
 {
        enum PVRSRV_ERROR eError;
-       u32 i;
-       u32 ui32PageCount, ui32PageOffset;
+       u32 ui32PageOffset;
        u32 ui32HostPageSize = HOST_PAGESIZE();
+       int page_count;
+       int i;
        struct IMG_SYS_PHYADDR *psSysPAddr = NULL;
        struct IMG_DEV_PHYADDR sDevPAddr;
        struct BM_BUF *psBuf;
@@ -762,6 +931,7 @@ enum PVRSRV_ERROR PVRSRVMapDeviceMemoryKM(
        IMG_BOOL bBMError;
        struct PVRSRV_DEVICE_NODE *psDeviceNode;
        void *pvPageAlignedCPUVAddr;
+       struct RESMAN_MAP_DEVICE_MEM_DATA *psMapData = NULL;
 
        if (!psSrcMemInfo || !hDstDevMemHeap || !ppsDstMemInfo) {
                PVR_DPF(PVR_DBG_ERROR,
@@ -771,17 +941,15 @@ enum PVRSRV_ERROR PVRSRVMapDeviceMemoryKM(
 
        *ppsDstMemInfo = NULL;
 
-       ui32PageOffset =
-           psSrcMemInfo->sDevVAddr.uiAddr & (ui32HostPageSize - 1);
-       ui32PageCount =
-           HOST_PAGEALIGN(psSrcMemInfo->ui32AllocSize +
-                          ui32PageOffset) / ui32HostPageSize;
+       get_page_details((u32)psSrcMemInfo->pvLinAddrKM,
+                       psSrcMemInfo->ui32AllocSize,
+                       &ui32PageOffset, &page_count);
        pvPageAlignedCPUVAddr =
            (void *) ((u8 *) psSrcMemInfo->pvLinAddrKM -
                          ui32PageOffset);
 
        if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP,
-                      ui32PageCount * sizeof(struct IMG_SYS_PHYADDR),
+                      page_count * sizeof(struct IMG_SYS_PHYADDR),
                       (void **) &psSysPAddr, NULL) != PVRSRV_OK) {
                PVR_DPF(PVR_DBG_ERROR, "PVRSRVMapDeviceMemoryKM: "
                                        "Failed to alloc memory for block");
@@ -793,7 +961,7 @@ enum PVRSRV_ERROR PVRSRVMapDeviceMemoryKM(
        psDeviceNode = psBuf->pMapping->pBMHeap->pBMContext->psDeviceNode;
 
        sDevVAddr.uiAddr = psSrcMemInfo->sDevVAddr.uiAddr - ui32PageOffset;
-       for (i = 0; i < ui32PageCount; i++) {
+       for (i = 0; i < page_count; i++) {
                eError =
                    BM_GetPhysPageAddr(psSrcMemInfo, sDevVAddr, &sDevPAddr);
                if (eError != PVRSRV_OK) {
@@ -810,10 +978,19 @@ enum PVRSRV_ERROR PVRSRVMapDeviceMemoryKM(
        }
 
        if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP,
+                      sizeof(struct RESMAN_MAP_DEVICE_MEM_DATA),
+                      (void **)&psMapData, NULL) != PVRSRV_OK) {
+               PVR_DPF(PVR_DBG_ERROR, "PVRSRVMapDeviceMemoryKM: "
+                               "Failed to alloc resman map data");
+               eError = PVRSRV_ERROR_OUT_OF_MEMORY;
+               goto ErrorExit;
+       }
+
+       if (OSAllocMem(PVRSRV_PAGEABLE_SELECT,
                       sizeof(struct PVRSRV_KERNEL_MEM_INFO),
-                      (void **) &psMemInfo, NULL) != PVRSRV_OK) {
+                      (void **)&psMemInfo, NULL) != PVRSRV_OK) {
                PVR_DPF(PVR_DBG_ERROR, "PVRSRVMapDeviceMemoryKM: "
-                                       "Failed to alloc memory for block");
+                               "Failed to alloc memory for block");
                eError = PVRSRV_ERROR_OUT_OF_MEMORY;
                goto ErrorExit;
        }
@@ -822,11 +999,10 @@ enum PVRSRV_ERROR PVRSRVMapDeviceMemoryKM(
 
        psMemBlock = &(psMemInfo->sMemBlk);
 
-       bBMError = BM_Wrap(psBuf->pMapping->pBMHeap,
-                          psSrcMemInfo->ui32AllocSize,
-                          ui32PageOffset, IMG_FALSE, psSysPAddr,
-                          IMG_TRUE, pvPageAlignedCPUVAddr,
-                          &psMemInfo->ui32Flags, &hBuffer);
+       bBMError = BM_Wrap(hDstDevMemHeap, psSrcMemInfo->ui32AllocSize,
+                          ui32PageOffset, bm_is_continuous(psBuf), psSysPAddr,
+                          pvPageAlignedCPUVAddr, &psMemInfo->ui32Flags,
+                          &hBuffer);
 
        if (!bBMError) {
                PVR_DPF(PVR_DBG_ERROR,
@@ -840,15 +1016,24 @@ enum PVRSRV_ERROR PVRSRVMapDeviceMemoryKM(
 
        psMemBlock->hBuffer = (void *) hBuffer;
 
+       psMemBlock->psIntSysPAddr = psSysPAddr;
+
        psMemInfo->pvLinAddrKM = psSrcMemInfo->pvLinAddrKM;
 
        psMemInfo->sDevVAddr = psMemBlock->sDevVirtAddr;
        psMemInfo->ui32AllocSize = psSrcMemInfo->ui32AllocSize;
        psMemInfo->psKernelSyncInfo = psSrcMemInfo->psKernelSyncInfo;
 
+       psMemInfo->pvSysBackupBuffer = NULL;
+
+       psSrcMemInfo->ui32RefCount++;
+
+       psMapData->psMemInfo = psMemInfo;
+       psMapData->psSrcMemInfo = psSrcMemInfo;
+
        psMemInfo->sMemBlk.hResItem =
            ResManRegisterRes(psPerProc->hResManContext,
-                             RESMAN_TYPE_DEVICEMEM_MAPPING, psMemInfo, 0,
+                             RESMAN_TYPE_DEVICEMEM_MAPPING, psMapData, 0,
                              UnmapDeviceMemoryCallBack);
 
        *ppsDstMemInfo = psMemInfo;
@@ -857,14 +1042,22 @@ enum PVRSRV_ERROR PVRSRVMapDeviceMemoryKM(
 
 ErrorExit:
 
-       if (psSysPAddr)
-               OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, ui32PageCount *
-                           sizeof(struct IMG_SYS_PHYADDR), psSysPAddr, NULL);
-
-       if (psMemInfo)
+       if (psSysPAddr) {
                OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP,
+                         sizeof(struct IMG_SYS_PHYADDR), psSysPAddr, NULL);
+       }
+
+       if (psMemInfo) {
+               OSFreeMem(PVRSRV_PAGEABLE_SELECT,
                          sizeof(struct PVRSRV_KERNEL_MEM_INFO), psMemInfo,
                          NULL);
+       }
+
+       if (psMapData) {
+               OSFreeMem(PVRSRV_PAGEABLE_SELECT,
+                         sizeof(struct RESMAN_MAP_DEVICE_MEM_DATA), psMapData,
+                         NULL);
+       }
 
        return eError;
 }
@@ -876,6 +1069,7 @@ enum PVRSRV_ERROR PVRSRVUnmapDeviceClassMemoryKM(
                return PVRSRV_ERROR_INVALID_PARAMS;
 
        ResManFreeResByPtr(psMemInfo->sMemBlk.hResItem);
+
        return PVRSRV_OK;
 }
 
@@ -891,7 +1085,7 @@ static enum PVRSRV_ERROR UnmapDeviceClassMemoryCallBack(void *pvParam,
 
 enum PVRSRV_ERROR PVRSRVMapDeviceClassMemoryKM(
                                struct PVRSRV_PER_PROCESS_DATA *psPerProc,
-                               void *hDeviceClassBuffer,
+                               void *hDevMemContext, void *hDeviceClassBuffer,
                                struct PVRSRV_KERNEL_MEM_INFO **ppsMemInfo,
                                void **phOSMapInfo)
 {
@@ -903,15 +1097,18 @@ enum PVRSRV_ERROR PVRSRVMapDeviceClassMemoryKM(
        IMG_BOOL bPhysContig;
        struct BM_CONTEXT *psBMContext;
        struct DEVICE_MEMORY_INFO *psDevMemoryInfo;
-       void *hDevMemHeap;
+       struct DEVICE_MEMORY_HEAP_INFO *psDeviceMemoryHeap;
+       void *hDevMemHeap = NULL;
        u32 ui32ByteSize;
        u32 ui32Offset;
        u32 ui32PageSize = HOST_PAGESIZE();
        void *hBuffer;
        struct PVRSRV_MEMBLK *psMemBlock;
        IMG_BOOL bBMError;
+       u32 i;
 
-       if (!hDeviceClassBuffer || !ppsMemInfo || !phOSMapInfo) {
+       if (!hDeviceClassBuffer || !ppsMemInfo || !phOSMapInfo ||
+           !hDevMemContext) {
                PVR_DPF(PVR_DBG_ERROR,
                         "PVRSRVMapDeviceClassMemoryKM: invalid parameters");
                return PVRSRV_ERROR_INVALID_PARAMS;
@@ -930,25 +1127,43 @@ enum PVRSRV_ERROR PVRSRVMapDeviceClassMemoryKM(
                                                  phOSMapInfo, &bPhysContig);
        if (eError != PVRSRV_OK) {
                PVR_DPF(PVR_DBG_ERROR, "PVRSRVMapDeviceClassMemoryKM: "
-                               "unable to get buffer address");
+                                       "unable to get buffer address");
                return PVRSRV_ERROR_GENERIC;
        }
 
        psBMContext = (struct BM_CONTEXT *)psDeviceClassBuffer->hDevMemContext;
        psDevMemoryInfo = &psBMContext->psDeviceNode->sDevMemoryInfo;
-       hDevMemHeap =
-           psDevMemoryInfo->psDeviceMemoryHeap[SGX_FB_MAPPING_HEAP_ID].
-           hDevMemHeap;
+       psDeviceMemoryHeap = psDevMemoryInfo->psDeviceMemoryHeap;
+       for (i = 0; i < PVRSRV_MAX_CLIENT_HEAPS; i++) {
+               if (HEAP_IDX(psDeviceMemoryHeap[i].ui32HeapID) ==
+                   psDevMemoryInfo->ui32MappingHeapID) {
+                       if (psDeviceMemoryHeap[i].DevMemHeapType ==
+                           DEVICE_MEMORY_HEAP_PERCONTEXT)
+                               hDevMemHeap =
+                                   BM_CreateHeap(hDevMemContext,
+                                                 &psDeviceMemoryHeap[i]);
+                       else
+                               hDevMemHeap =
+                                   psDevMemoryInfo->psDeviceMemoryHeap[i].
+                                                                   hDevMemHeap;
+                       break;
+               }
+       }
 
-       ui32Offset = ((u32) pvCPUVAddr) & (ui32PageSize - 1);
-       pvPageAlignedCPUVAddr =
-           (void *) ((u8 *) pvCPUVAddr - ui32Offset);
+       if (hDevMemHeap == NULL) {
+               PVR_DPF(PVR_DBG_ERROR, "PVRSRVMapDeviceClassMemoryKM: "
+                                       "unable to find mapping heap");
+               return PVRSRV_ERROR_GENERIC;
+       }
 
-       if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP,
+       ui32Offset = ((u32)pvCPUVAddr) & (ui32PageSize - 1);
+       pvPageAlignedCPUVAddr = (void *)((u8 *)pvCPUVAddr - ui32Offset);
+
+       if (OSAllocMem(PVRSRV_PAGEABLE_SELECT,
                       sizeof(struct PVRSRV_KERNEL_MEM_INFO),
-                      (void **) &psMemInfo, NULL) != PVRSRV_OK) {
+                      (void **)&psMemInfo, NULL) != PVRSRV_OK) {
                PVR_DPF(PVR_DBG_ERROR, "PVRSRVMapDeviceClassMemoryKM: "
-                        "Failed to alloc memory for block");
+                                       "Failed to alloc memory for block");
                return PVRSRV_ERROR_OUT_OF_MEMORY;
        }
 
@@ -956,21 +1171,16 @@ enum PVRSRV_ERROR PVRSRVMapDeviceClassMemoryKM(
 
        psMemBlock = &(psMemInfo->sMemBlk);
 
-       bBMError = BM_Wrap(hDevMemHeap,
-                          ui32ByteSize,
-                          ui32Offset,
-                          bPhysContig,
-                          psSysPAddr,
-                          IMG_FALSE,
-                          pvPageAlignedCPUVAddr,
+       bBMError = BM_Wrap(hDevMemHeap, ui32ByteSize, ui32Offset, bPhysContig,
+                          psSysPAddr, pvPageAlignedCPUVAddr,
                           &psMemInfo->ui32Flags, &hBuffer);
 
        if (!bBMError) {
                PVR_DPF(PVR_DBG_ERROR,
                         "PVRSRVMapDeviceClassMemoryKM: BM_Wrap Failed");
-               OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP,
-                         sizeof(struct PVRSRV_KERNEL_MEM_INFO),
-                         psMemInfo, NULL);
+               OSFreeMem(PVRSRV_PAGEABLE_SELECT,
+                         sizeof(struct PVRSRV_KERNEL_MEM_INFO), psMemInfo,
+                         NULL);
                return PVRSRV_ERROR_BAD_MAPPING;
        }
 
@@ -985,6 +1195,8 @@ enum PVRSRV_ERROR PVRSRVMapDeviceClassMemoryKM(
        psMemInfo->ui32AllocSize = ui32ByteSize;
        psMemInfo->psKernelSyncInfo = psDeviceClassBuffer->psKernelSyncInfo;
 
+       psMemInfo->pvSysBackupBuffer = NULL;
+
        psMemInfo->sMemBlk.hResItem =
            ResManRegisterRes(psPerProc->hResManContext,
                              RESMAN_TYPE_DEVICECLASSMEM_MAPPING, psMemInfo, 0,