Start implementing PMR

PMRs are essentially the PowerVR equivalent of PlatformBuffers.

Change-Id: I5743a4a8fd768326193177111d3220d035da3e18
diff --git a/BUILD.gn b/BUILD.gn
index a41f1ea..c5b83f8 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -209,6 +209,7 @@
   deps = [
     "//garnet/lib/magma/include:msd_abi",
     "//garnet/lib/magma/src/magma_util",
+    "//garnet/lib/magma/src/magma_util/platform:buffer",
     "//garnet/lib/magma/src/magma_util/platform:device",
     "//zircon/public/lib/zx",
   ]
diff --git a/services/server/env/fuchsia/physmem_osmem_fuchsia.cc b/services/server/env/fuchsia/physmem_osmem_fuchsia.cc
index 8dd61e3..a5bfe0c 100644
--- a/services/server/env/fuchsia/physmem_osmem_fuchsia.cc
+++ b/services/server/env/fuchsia/physmem_osmem_fuchsia.cc
@@ -31,6 +31,9 @@
 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */ /**************************************************************************/
 /* include/ */
+
+#include <vector>
+
 extern "C" {
 #include "rgx_heaps.h"
 #include "img_types.h"
@@ -47,8 +50,148 @@
 #include "physmem_osmem.h"
 }
 
+#include "fuchsia/msd_img_device.h"
+#include "magma_util/macros.h"
+#include "platform_buffer.h"
+#include "platform_bus_mapper.h"
+
 #define NOT_IMPLEMENTED() fprintf(stderr, "msd-img-rgx-mtk: Not implemented in %s:%s:%d\n", __func__, __FILE__, __LINE__);
 
+struct MagmaPhysicalMemoryResource
+{
+	PVRSRV_DEVICE_NODE *psDevNode;
+	std::unique_ptr<magma::PlatformBuffer> buffer;
+	std::unique_ptr<magma::PlatformBusMapper::BusMapping> bus_mapping;
+};
+
+static PVRSRV_ERROR
+LockPhysAddresses(PMR_IMPL_PRIVDATA data)
+{
+	MagmaPhysicalMemoryResource *pmr = reinterpret_cast<MagmaPhysicalMemoryResource *>(data);
+	auto device = MsdImgDevice::cast(pmr->psDevNode->psDevConfig->pvOSDevice);
+	pmr->bus_mapping =
+		device->bus_mapper()->MapPageRangeBus(pmr->buffer.get(), 0, pmr->buffer->size() / magma::page_size());
+	if (!pmr->bus_mapping)
+	{
+		// Could instead be PVRSRV_ERROR_DEVICEMEM_OUT_OF_RANGE, or similar.
+		return PVRSRV_ERROR_OUT_OF_MEMORY;
+	}
+	return PVRSRV_OK;
+}
+
+static PVRSRV_ERROR
+UnlockPhysAddresses(PMR_IMPL_PRIVDATA data)
+{
+	MagmaPhysicalMemoryResource *pmr = reinterpret_cast<MagmaPhysicalMemoryResource *>(data);
+	pmr->bus_mapping.reset();
+	return PVRSRV_OK;
+}
+
+static PVRSRV_ERROR
+DevPhysAddr(PMR_IMPL_PRIVDATA pvPriv,
+	    IMG_UINT32 ui32Log2PageSize,
+	    IMG_UINT32 ui32NumOfAddr,
+	    IMG_DEVMEM_OFFSET_T *puiOffset,
+	    IMG_BOOL *pbValid,
+	    IMG_DEV_PHYADDR *psDevAddrPtr)
+{
+	MagmaPhysicalMemoryResource *pmr = reinterpret_cast<MagmaPhysicalMemoryResource *>(pvPriv);
+	if (ui32Log2PageSize != magma::page_shift())
+	{
+		NOT_IMPLEMENTED();
+		return PVRSRV_ERROR_NOT_SUPPORTED;
+	}
+	for (uint32_t i = 0; i < ui32NumOfAddr; i++)
+	{
+		if (pbValid[i])
+		{
+			uint64_t page_index = puiOffset[i] >> magma::page_shift();
+			uint64_t in_page_offset = puiOffset[i] & (magma::page_size() - 1);
+			psDevAddrPtr[i].uiAddr = pmr->bus_mapping->Get()[page_index] + in_page_offset;
+		}
+	}
+	return PVRSRV_OK;
+}
+
+struct MapHandle
+{
+	uint32_t unused;
+};
+
+static PVRSRV_ERROR
+AcquireKernelMappingData(PMR_IMPL_PRIVDATA pvPriv,
+			 size_t uiOffset,
+			 size_t uiSize,
+			 void **ppvKernelAddressOut,
+			 IMG_HANDLE *phHandleOut,
+			 PMR_FLAGS_T ulFlags)
+{
+	MagmaPhysicalMemoryResource *pmr = reinterpret_cast<MagmaPhysicalMemoryResource *>(pvPriv);
+	void *cpu_map;
+	// Map entire buffer for now. Also ignore flags.
+	if (!pmr->buffer->MapCpu(&cpu_map))
+	{
+		return PVRSRV_ERROR_OUT_OF_MEMORY;
+	}
+	*ppvKernelAddressOut = reinterpret_cast<uint8_t *>(cpu_map) + uiOffset;
+	*phHandleOut = new MapHandle;
+	return PVRSRV_OK;
+}
+
+static void
+ReleaseKernelMappingData(PMR_IMPL_PRIVDATA pvPriv, IMG_HANDLE hHandle)
+{
+	MagmaPhysicalMemoryResource *pmr = reinterpret_cast<MagmaPhysicalMemoryResource *>(pvPriv);
+	pmr->buffer->UnmapCpu();
+	delete reinterpret_cast<MapHandle *>(hHandle);
+}
+
+static PVRSRV_ERROR
+ChangeSparseMem(PMR_IMPL_PRIVDATA pPriv,
+		const PMR *psPMR,
+		IMG_UINT32 ui32AllocPageCount,
+		IMG_UINT32 *pai32AllocIndices,
+		IMG_UINT32 ui32FreePageCount,
+		IMG_UINT32 *pai32FreeIndices,
+		IMG_UINT32 uiFlags)
+{
+	NOT_IMPLEMENTED();
+	return PVRSRV_ERROR_NOT_SUPPORTED;
+}
+
+static PVRSRV_ERROR
+ChangeSparseMemCpuMap(PMR_IMPL_PRIVDATA pPriv,
+		      const PMR *psPMR,
+		      IMG_UINT64 sCpuVAddrBase,
+		      IMG_UINT32 ui32AllocPageCount,
+		      IMG_UINT32 *pai32AllocIndices,
+		      IMG_UINT32 ui32FreePageCount,
+		      IMG_UINT32 *pai32FreeIndices)
+{
+	NOT_IMPLEMENTED();
+	return PVRSRV_ERROR_NOT_SUPPORTED;
+}
+
+static PVRSRV_ERROR
+Finalize(PMR_IMPL_PRIVDATA data)
+{
+	MagmaPhysicalMemoryResource *pmr = reinterpret_cast<MagmaPhysicalMemoryResource *>(data);
+	delete pmr;
+	return PVRSRV_OK;
+}
+
+static const PMR_IMPL_FUNCTAB kPmrTable = {
+	.pfnLockPhysAddresses = LockPhysAddresses,
+	.pfnUnlockPhysAddresses = UnlockPhysAddresses,
+	.pfnDevPhysAddr = DevPhysAddr,
+	.pfnAcquireKernelMappingData = AcquireKernelMappingData,
+	.pfnReleaseKernelMappingData = ReleaseKernelMappingData,
+	.pfnChangeSparseMem = ChangeSparseMem,
+	.pfnChangeSparseMemCPUMap = ChangeSparseMemCpuMap,
+	.pfnFinalize = Finalize,
+};
+
+
 PVRSRV_ERROR
 PhysmemNewOSRamBackedPMR(PVRSRV_DEVICE_NODE *psDevNode,
 			 IMG_DEVMEM_SIZE_T uiSize,
@@ -62,6 +205,41 @@
 			 IMG_PID uiPid,
 			 PMR **ppsPMRPtr)
 {
-	NOT_IMPLEMENTED();
-	return PVRSRV_ERROR_NOT_SUPPORTED;
+	auto pmr = std::make_unique<MagmaPhysicalMemoryResource>();
+	// This may be called before the pvOSDevice is initialized, so store the
+	// entire device node for use later.
+	pmr->psDevNode = psDevNode;
+
+	pmr->buffer = magma::PlatformBuffer::Create(uiSize, pszAnnotation);
+	if (PVRSRV_CHECK_CPU_UNCACHED(uiFlags))
+	{
+		if (!pmr->buffer->SetCachePolicy(MAGMA_CACHE_POLICY_UNCACHED))
+		{
+			return DRET(PVRSRV_ERROR_OUT_OF_MEMORY);
+		}
+	}
+	if (PVRSRV_CHECK_CPU_WRITE_COMBINE(uiFlags))
+	{
+		if (!pmr->buffer->SetCachePolicy(MAGMA_CACHE_POLICY_WRITE_COMBINING))
+		{
+			return DRET(PVRSRV_ERROR_OUT_OF_MEMORY);
+		}
+	}
+
+	PHYS_HEAP *psPhysHeap = psDevNode->apsPhysHeap[PVRSRV_DEVICE_PHYS_HEAP_CPU_LOCAL];
+	return PMRCreatePMR(psDevNode,
+			    psPhysHeap,
+			    uiSize,
+			    uiChunkSize,
+			    ui32NumPhysChunks,
+			    ui32NumVirtChunks,
+			    puiAllocIndices,
+			    magma::page_shift(),
+			    0,
+			    pszAnnotation,
+			    &kPmrTable,
+			    pmr.release(),
+			    PMR_TYPE_OSMEM,
+			    ppsPMRPtr,
+			    0);
 }