/* Copyright 2024 Lucas Fryzek * * This file is part of DRVEMU. * * DRVEMU 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 3 of the License, or (at your option) any later * version. * * DRVEMU 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 DRVEMU. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xf86drm.h" #define DRM_PVR_RESERVED1 (DRM_COMMAND_BASE + 0) #define DRM_PVR_RESERVED2 (DRM_COMMAND_BASE + 1) #define DRM_PVR_RESERVED3 (DRM_COMMAND_BASE + 2) #define DRM_PVR_RESERVED4 (DRM_COMMAND_BASE + 3) #define DRM_PVR_RESERVED5 (DRM_COMMAND_BASE + 4) #define DRM_PVR_RESERVED6 (DRM_COMMAND_BASE + 5) /* PVR includes */ #define SUPPORT_MEMINFO_IDS #define SUPPORT_DRI_DRM_EXT #include #include #include #include #include #include #include #include #include #define DRM_IOCTL_PVR_SRVKM DRM_IOWR(PVR_DRM_SRVKM_CMD, PVRSRV_BRIDGE_PACKAGE) #include "pvr_ioctl.h" #define MAX_ALLOCATIONS 1024 #define ARRAY_SIZE(x) ((sizeof x) / (sizeof *x)) #define STR_DETAIL(x) #x #define STR(x) STR_DETAIL(x) #define LOG(msg...) fprintf(stderr, "[DRVEMU] " msg) #define SOFT_TODO(msg) LOG(__FILE__ ":" STR(__LINE__) " " msg "\n") #define TODO(msg) SOFT_TODO(msg); assert(0) struct memory_allocation { bool allocated; uint32_t device_ptr; void *cpu_ptr; size_t size; }; static struct memory_allocation allocations[MAX_ALLOCATIONS]; static int false_fd = 10241024; static int false_disp = 10241025; static int spoof_socket = -1; #define PROLOG(func) \ static typeof(func) *orig_##func = NULL; \ if(!orig_##func) \ orig_##func = dlsym(RTLD_NEXT, #func); int open64(const char *pathname, int flags, ...) { LOG("Called open64 on %s (%d)\n", pathname, flags); PROLOG(open64); if (strcmp(pathname, "/dev/dri/renderD128") == 0) { LOG("Spoofing FD!\n"); return false_fd; } int fd = orig_open64(pathname, flags); return fd; } int close(int fd) { LOG("Close called on %d\n", fd); PROLOG(close); if (fd != false_fd) return orig_close(fd); return 0; } int fcntl(int fd, int op, int arg) { LOG("Called fcntl on %d\n", fd); PROLOG(fcntl); if (fd == false_fd) return 0; return orig_fcntl(fd, op, arg); } static const char *pvr_heap_names[] = { "DRVEMU_INTERNAL", "SGX_GENERAL_HEAP", "SGX_TADATA_HEAP", "SGX_KERNEL_CODE_HEAP", "SGX_KERNEL_DATA_HEAP", "SGX_PIXELSHADER_HEAP", "SGX_VERTEXSHADER_HEAP", "SGX_PDSPIXEL_CODEDATA_HEAP", "SGX_PDSVERTEX_CODEDATA_HEAP", "SGX_SYNCINFO_HEAP", "SGX_SHARED_3DPARAMETERS_HEAP", "SGX_PERCONTEXT_3DPARAMETERS_HEAP" }; PVRSRV_HEAP_INFO pvr_heaps[] = { { .ui32HeapID = HEAP_ID( PVRSRV_DEVICE_TYPE_SGX, SGX_GENERAL_HEAP_ID), .hDevMemHeap = (void*)1, .sDevVAddrBase = {SGX_GENERAL_HEAP_BASE}, .ui32HeapByteSize = SGX_GENERAL_HEAP_SIZE, .ui32Attribs = PVRSRV_HAP_WRITECOMBINE, }, { .ui32HeapID = HEAP_ID( PVRSRV_DEVICE_TYPE_SGX, SGX_TADATA_HEAP_ID), .hDevMemHeap = (void*)2, .sDevVAddrBase = {SGX_TADATA_HEAP_BASE}, .ui32HeapByteSize = SGX_TADATA_HEAP_SIZE, .ui32Attribs = PVRSRV_HAP_WRITECOMBINE | PVRSRV_MEM_RAM_BACKED_ALLOCATION | PVRSRV_HAP_MULTI_PROCESS, }, { .ui32HeapID = HEAP_ID( PVRSRV_DEVICE_TYPE_SGX, SGX_KERNEL_CODE_HEAP_ID), .hDevMemHeap = (void*)3, .sDevVAddrBase = {SGX_KERNEL_CODE_HEAP_BASE}, .ui32HeapByteSize = SGX_KERNEL_CODE_HEAP_SIZE, .ui32Attribs = PVRSRV_HAP_WRITECOMBINE | PVRSRV_MEM_RAM_BACKED_ALLOCATION | PVRSRV_HAP_MULTI_PROCESS, }, { .ui32HeapID = HEAP_ID( PVRSRV_DEVICE_TYPE_SGX, SGX_KERNEL_DATA_HEAP_ID), .hDevMemHeap = (void*)4, .sDevVAddrBase = {SGX_KERNEL_DATA_HEAP_BASE}, .ui32HeapByteSize = SGX_KERNEL_DATA_HEAP_SIZE, .ui32Attribs = PVRSRV_HAP_WRITECOMBINE | PVRSRV_MEM_RAM_BACKED_ALLOCATION | PVRSRV_HAP_MULTI_PROCESS, }, { .ui32HeapID = HEAP_ID( PVRSRV_DEVICE_TYPE_SGX, SGX_PIXELSHADER_HEAP_ID), .hDevMemHeap = (void*)5, .sDevVAddrBase = {SGX_PIXELSHADER_HEAP_BASE}, .ui32HeapByteSize = SGX_PIXELSHADER_HEAP_SIZE, .ui32Attribs = PVRSRV_HAP_WRITECOMBINE | PVRSRV_MEM_RAM_BACKED_ALLOCATION | PVRSRV_HAP_SINGLE_PROCESS, }, { .ui32HeapID = HEAP_ID( PVRSRV_DEVICE_TYPE_SGX, SGX_VERTEXSHADER_HEAP_ID), .hDevMemHeap = (void*)6, .sDevVAddrBase = {SGX_VERTEXSHADER_HEAP_BASE}, .ui32HeapByteSize = SGX_VERTEXSHADER_HEAP_SIZE, .ui32Attribs = PVRSRV_HAP_WRITECOMBINE | PVRSRV_MEM_RAM_BACKED_ALLOCATION | PVRSRV_HAP_SINGLE_PROCESS, }, { .ui32HeapID = HEAP_ID( PVRSRV_DEVICE_TYPE_SGX, SGX_PDSPIXEL_CODEDATA_HEAP_ID), .hDevMemHeap = (void*)7, .sDevVAddrBase = {SGX_PDSPIXEL_CODEDATA_HEAP_BASE}, .ui32HeapByteSize = SGX_PDSPIXEL_CODEDATA_HEAP_SIZE, .ui32Attribs = PVRSRV_HAP_WRITECOMBINE | PVRSRV_MEM_RAM_BACKED_ALLOCATION | PVRSRV_HAP_SINGLE_PROCESS, }, { .ui32HeapID = HEAP_ID( PVRSRV_DEVICE_TYPE_SGX, SGX_PDSVERTEX_CODEDATA_HEAP_ID), .hDevMemHeap = (void*)8, .sDevVAddrBase = {SGX_PDSVERTEX_CODEDATA_HEAP_BASE}, .ui32HeapByteSize = SGX_PDSVERTEX_CODEDATA_HEAP_SIZE, .ui32Attribs = PVRSRV_HAP_WRITECOMBINE | PVRSRV_MEM_RAM_BACKED_ALLOCATION | PVRSRV_HAP_SINGLE_PROCESS, }, { .ui32HeapID = HEAP_ID( PVRSRV_DEVICE_TYPE_SGX, SGX_SYNCINFO_HEAP_ID), .hDevMemHeap = (void*)9, .sDevVAddrBase = {SGX_SYNCINFO_HEAP_BASE}, .ui32HeapByteSize = SGX_SYNCINFO_HEAP_SIZE, .ui32Attribs = PVRSRV_HAP_WRITECOMBINE | PVRSRV_MEM_RAM_BACKED_ALLOCATION | PVRSRV_HAP_MULTI_PROCESS, }, { .ui32HeapID = HEAP_ID( PVRSRV_DEVICE_TYPE_SGX, SGX_SHARED_3DPARAMETERS_HEAP_ID), .hDevMemHeap = (void*)10, .sDevVAddrBase = {SGX_SHARED_3DPARAMETERS_HEAP_BASE}, .ui32HeapByteSize = SGX_SHARED_3DPARAMETERS_HEAP_SIZE, .ui32Attribs = PVRSRV_HAP_WRITECOMBINE | PVRSRV_MEM_RAM_BACKED_ALLOCATION | PVRSRV_HAP_MULTI_PROCESS, }, { .ui32HeapID = HEAP_ID( PVRSRV_DEVICE_TYPE_SGX, SGX_PERCONTEXT_3DPARAMETERS_HEAP_ID), .hDevMemHeap = (void*)11, .sDevVAddrBase = {SGX_PERCONTEXT_3DPARAMETERS_HEAP_BASE}, .ui32HeapByteSize = SGX_PERCONTEXT_3DPARAMETERS_HEAP_SIZE, .ui32Attribs = PVRSRV_HAP_WRITECOMBINE | PVRSRV_MEM_RAM_BACKED_ALLOCATION | PVRSRV_HAP_SINGLE_PROCESS, }, }; static int alloc_memory(int heap_id, size_t size) { for (size_t i = 0; i < MAX_ALLOCATIONS; i++) { if (!allocations[i].allocated) { struct memory_allocation *mem = &allocations[i]; mem->allocated = true; // If heap is 0 that is for internal allocs if (heap_id != 0) { assert((heap_id-1) < ARRAY_SIZE(pvr_heaps)); PVRSRV_HEAP_INFO *heap = &pvr_heaps[heap_id-1]; mem->device_ptr = heap->sDevVAddrBase.uiAddr; heap->sDevVAddrBase.uiAddr += size; } else { mem->device_ptr = 0; } mem->cpu_ptr = malloc(size); mem->size = size; return i+1; } } assert(0); return -1; } static struct memory_allocation *alloc_sync(PVRSRV_CLIENT_SYNC_INFO *sync) { int handle = alloc_memory(0, 1024); struct memory_allocation *alloc = &allocations[handle-1]; sync->hMappingInfo = (void*)handle; sync->psSyncData = (void*)alloc; sync->sWriteOpsCompleteDevVAddr.uiAddr = handle; sync->sReadOpsCompleteDevVAddr.uiAddr = handle; sync->sReadOps2CompleteDevVAddr.uiAddr = handle; sync->hKernelSyncInfo = sync->hMappingInfo; return alloc; } #define create_handle(size, msg) create_handle_helper((size), (msg), __FILE__, __LINE__) static void *create_handle_helper(int size, const char *msg, const char *file, int line) { int handle = alloc_memory(0, size); // TODO handle heap correctly here; LOG("%s:%d Creating handle 0x%x for %s\n", file, line, handle, msg); return (void*)handle; } #define DEV_NAME "pvr" #define DISP_NAME "jz4780" #define DEV_DATE "2014545384" #define DEV_DESC "Imagination Technologies PVR DRM" static void get_misc_info(SGX_MISC_INFO *info) { switch(info->eRequest) { case SGX_MISC_INFO_REQUEST_DRIVER_SGXREV: info->uData.sSGXFeatures.ui32BuildOptions = SGX_BUILD_OPTIONS; info->uData.sSGXFeatures.ui32DDKBuild = 3759903; info->uData.sSGXFeatures.ui32DDKVersion = (1 << 16) | (14 << 8); break; case SGX_MISC_INFO_REQUEST_SGXREV: // TODO need to get this from real hardware LOG("TODO dump SGX_MISC_INFO_REQUEST_SGXREV from hw"); info->uData.sSGXFeatures.ui32BuildOptions = SGX_BUILD_OPTIONS; info->uData.sSGXFeatures.ui32DDKBuild = 3759903; info->uData.sSGXFeatures.ui32DDKVersion = (1 << 16) | (14 << 8); break; case SGX_MISC_INFO_REQUEST_CLOCKSPEED_SLCSIZE: info->uData.sQueryClockSpeedSLCSize.ui32SGXClockSpeed = SYS_SGX_CLOCK_SPEED; break; default: LOG("Unimplemented misc req %d\n", info->eRequest); assert(false); break; } } static void get_bridge_misc(PVRSRV_BRIDGE_IN_GET_MISC_INFO *in, PVRSRV_BRIDGE_OUT_GET_MISC_INFO *out) { #define if_present(present) if (in->sMiscInfo.ui32StateRequest & (present)) if_present(PVRSRV_MISC_INFO_MEMSTATS_PRESENT) { TODO("Implement PVRSRV_MISC_INFO_MEMSTATS_PRESENT"); } if_present(PVRSRV_MISC_INFO_DDKVERSION_PRESENT) { TODO("Implement PVRSRV_MISC_INFO_DDKVERSION_PRESENT"); } if_present(PVRSRV_MISC_INFO_FREEMEM_PRESENT) { TODO("Implement PVRSRV_MISC_INFO_FREEMEM_PRESENT"); } if_present(PVRSRV_MISC_INFO_TIMER_PRESENT) { out->sMiscInfo.ui32StatePresent |= PVRSRV_MISC_INFO_TIMER_PRESENT; out->sMiscInfo.pvSOCTimerRegisterKM = create_handle(1024, "PVRSRV_MISC_INFO_TIMER_PRESENT"); out->sMiscInfo.hSOCTimerRegisterOSMemHandle = out->sMiscInfo.pvSOCTimerRegisterKM; } else { out->sMiscInfo.pvSOCTimerRegisterKM = IMG_NULL; out->sMiscInfo.hSOCTimerRegisterOSMemHandle = IMG_NULL; } if_present(PVRSRV_MISC_INFO_CLOCKGATE_PRESENT) { out->sMiscInfo.ui32StatePresent |= PVRSRV_MISC_INFO_CLOCKGATE_PRESENT; SOFT_TODO("Returning clock gate registers that may be incorrect, get real data from hw"); out->sMiscInfo.pvSOCClockGateRegs = 0x0; out->sMiscInfo.ui32SOCClockGateRegsSize = 0x0; } if_present(PVRSRV_MISC_INFO_GLOBALEVENTOBJECT_PRESENT) { out->sMiscInfo.ui32StatePresent |= PVRSRV_MISC_INFO_GLOBALEVENTOBJECT_PRESENT; SOFT_TODO("Returning global event object that may not be correct, get real data from hw"); strcpy(out->sMiscInfo.sGlobalEventObject.szName, "GEOEMU"); out->sMiscInfo.sGlobalEventObject.hOSEventKM = create_handle(1024, "PVRSRV_MISC_INFO_GLOBALEVENTOBJECT_PRESENT"); } if_present(PVRSRV_MISC_INFO_CPUCACHEOP_PRESENT) { TODO("Implement PVRSRV_MISC_INFO_CPUCACHEOP_PRESENT"); } if_present(PVRSRV_MISC_INFO_RESET_PRESENT) { TODO("Implement PVRSRV_MISC_INFO_RESET_PRESENT"); } if_present(PVRSRV_MISC_INFO_GET_REF_COUNT_PRESENT) { TODO("Implement PVRSRV_MISC_INFO_GET_REF_COUNT_PRESENT"); } if_present(PVRSRV_MISC_INFO_GET_PAGE_SIZE_PRESENT) { TODO("Implement PVRSRV_MISC_INFO_GET_PAGE_SIZE_PRESENT"); } if_present(PVRSRV_MISC_INFO_FORCE_SWAP_TO_SYSTEM_PRESENT) { TODO("Implement PVRSRV_MISC_INFO_FORCE_SWAP_TO_SYSTEM_PRESENT"); } out->eError = PVRSRV_OK; #undef if_present } static bool pvrsrv_ioctl(int fd, PVRSRV_BRIDGE_PACKAGE *bridge_package) { int ioctl_nr = _IOC_NR(bridge_package->ui32BridgeID); LOG(">>> pvr_ioctl(%s)(0x%x)\n", pvrsrv_ioctl_names[ioctl_nr], bridge_package->ui32BridgeID); switch(ioctl_nr) { case _IOC_NR(PVRSRV_BRIDGE_CONNECT_SERVICES): { PVRSRV_BRIDGE_OUT_CONNECT_SERVICES *data = bridge_package->pvParamOut; data->eError = PVRSRV_OK; data->hKernelServices = (void*)0x2; break; } case _IOC_NR(PVRSRV_BRIDGE_ENUM_DEVICES): { PVRSRV_BRIDGE_OUT_ENUMDEVICE *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; out->ui32NumDevices = 1; out->asDeviceIdentifier[0].eDeviceType = PVRSRV_DEVICE_TYPE_SGX; out->asDeviceIdentifier[0].eDeviceClass = PVRSRV_DEVICE_CLASS_3D; out->asDeviceIdentifier[0].ui32DeviceIndex = 0; out->asDeviceIdentifier[0].pszPDumpDevName = NULL; out->asDeviceIdentifier[0].pszPDumpRegName = NULL; break; } case _IOC_NR(PVRSRV_BRIDGE_ACQUIRE_DEVICEINFO): { PVRSRV_BRIDGE_OUT_ACQUIRE_DEVICEINFO *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; out->hDevCookie = (void*)0x1; break; } case _IOC_NR(PVRSRV_BRIDGE_SGX_GETMISCINFO): { LOG("Get misc info!\n"); PVRSRV_BRIDGE_IN_SGXGETMISCINFO *in = bridge_package->pvParamIn; PVRSRV_BRIDGE_RETURN *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; get_misc_info(in->psMiscInfo); break; } case _IOC_NR(PVRSRV_BRIDGE_SGX_GETCLIENTINFO): { PVRSRV_BRIDGE_OUT_GETCLIENTINFO *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; out->sClientInfo.ui32ProcessID = getpid();; out->sClientInfo.pvProcess = NULL; PVRSRV_MISC_INFO *misc = &out->sClientInfo.sMiscInfo; misc->ui32StatePresent = 0; break; } case _IOC_NR(PVRSRV_BRIDGE_ENUM_CLASS): { PVRSRV_BRIDGE_OUT_ENUMDEVICE *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; out->ui32NumDevices = 1; out->asDeviceIdentifier[0].eDeviceType = PVRSRV_DEVICE_TYPE_SGX; out->asDeviceIdentifier[0].eDeviceClass = PVRSRV_DEVICE_CLASS_3D; out->asDeviceIdentifier[0].ui32DeviceIndex = 0; out->asDeviceIdentifier[0].pszPDumpDevName = ""; out->asDeviceIdentifier[0].pszPDumpRegName = ""; break; } case _IOC_NR(PVRSRV_BRIDGE_CREATE_DEVMEMCONTEXT): { PVRSRV_BRIDGE_OUT_CREATE_DEVMEMCONTEXT *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; out->hDevMemContext = (void*)0x2; out->ui32ClientHeapCount = sizeof(pvr_heaps)/sizeof(*pvr_heaps); memcpy(out->sHeapInfo, pvr_heaps, sizeof(pvr_heaps)); break; } case _IOC_NR(PVRSRV_BRIDGE_OPEN_DISPCLASS_DEVICE): { PVRSRV_BRIDGE_OUT_OPEN_DISPCLASS_DEVICE *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; out->hDeviceKM = (void*)0x1; break; } case _IOC_NR(PVRSRV_BRIDGE_GET_DISPCLASS_INFO): { PVRSRV_BRIDGE_OUT_GET_DISPCLASS_INFO *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; out->sDisplayInfo.ui32MaxSwapChains = 3; out->sDisplayInfo.ui32MaxSwapChainBuffers = 3; out->sDisplayInfo.ui32MinSwapInterval = 0; out->sDisplayInfo.ui32MaxSwapInterval = 1; out->sDisplayInfo.ui32PhysicalWidthmm = 256; out->sDisplayInfo.ui32PhysicalHeightmm = 256; strcpy(out->sDisplayInfo.szDisplayName, "Display"); break; } case _IOC_NR(PVRSRV_BRIDGE_ENUM_DISPCLASS_FORMATS): { PVRSRV_BRIDGE_OUT_ENUM_DISPCLASS_FORMATS *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; out->ui32Count = 1; out->asFormat[0].pixelformat = PVRSRV_PIXEL_FORMAT_A8R8G8B8_UNORM; break; } case _IOC_NR(PVRSRV_BRIDGE_ENUM_DISPCLASS_DIMS): { PVRSRV_BRIDGE_OUT_ENUM_DISPCLASS_DIMS *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; out->ui32Count = 1; out->asDim[0].ui32ByteStride = 256; out->asDim[0].ui32Width = 256; out->asDim[0].ui32Height = 256; break; } case _IOC_NR(PVRSRV_BRIDGE_GET_DISPCLASS_SYSBUFFER): { PVRSRV_BRIDGE_OUT_GET_DISPCLASS_SYSBUFFER *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; out->hBuffer = (void*)alloc_memory(0, 256*256*4); break; } case _IOC_NR(PVRSRV_BRIDGE_MAP_DEVICECLASS_MEMORY): { PVRSRV_BRIDGE_IN_MAP_DEVICECLASS_MEMORY *in = bridge_package->pvParamIn; PVRSRV_BRIDGE_OUT_MAP_DEVICECLASS_MEMORY *out = bridge_package->pvParamOut; LOG("Attempting map 0x%x\n", (uint32_t)in->hDeviceClassBuffer); out->eError = PVRSRV_OK; out->sClientMemInfo.hMappingInfo = (void*)alloc_memory(0, 256*256*4); struct memory_allocation *mem = &allocations[((size_t)out->sClientMemInfo.hMappingInfo) - 1]; out->sClientMemInfo.pvLinAddr = mem->cpu_ptr; out->sClientMemInfo.pvLinAddrKM = mem->cpu_ptr; out->sClientMemInfo.uAllocSize = mem->size; out->sClientMemInfo.hKernelMemInfo = out->sClientMemInfo.hMappingInfo; out->sClientMemInfo.hResItem = out->sClientMemInfo.hMappingInfo; out->sClientMemInfo.psNext = NULL; out->sClientSyncInfo.hMappingInfo = (void*)alloc_memory(0, 1024); out->sClientSyncInfo.psSyncData = NULL; out->sClientSyncInfo.sWriteOpsCompleteDevVAddr.uiAddr = 0xDEADBEEF; out->sClientSyncInfo.sReadOpsCompleteDevVAddr.uiAddr = (uintptr_t)out->sClientSyncInfo.hMappingInfo; out->sClientSyncInfo.sReadOps2CompleteDevVAddr.uiAddr = 0xDEADBEF1; out->sClientSyncInfo.hKernelSyncInfo = out->sClientSyncInfo.hMappingInfo; out->psKernelMemInfo = NULL; out->hMappingInfo = out->sClientMemInfo.hMappingInfo; LOG("Allocated 0x%x\n", (uint32_t)out->sClientMemInfo.hMappingInfo); break; } case _IOC_NR(PVRSRV_BRIDGE_MHANDLE_TO_MMAP_DATA): { PVRSRV_BRIDGE_IN_MHANDLE_TO_MMAP_DATA *in = bridge_package->pvParamIn; PVRSRV_BRIDGE_OUT_MHANDLE_TO_MMAP_DATA *out = bridge_package->pvParamOut; size_t handle = (size_t)in->hMHandle; LOG("Attempting mhandle map 0x%x\n", handle); if (handle - 1 < MAX_ALLOCATIONS) { struct memory_allocation *mem = &allocations[handle - 1]; out->eError = PVRSRV_OK; out->uiMMapOffset = (uintptr_t)mem->cpu_ptr; out->uiByteOffset = 0; out->uiRealByteSize = mem->size; out->uiUserVAddr = (uintptr_t)mem->cpu_ptr; } else { out->eError = PVRSRV_ERROR_INVALID_PARAMS; } break; } case _IOC_NR(PVRSRV_BRIDGE_SYNC_OPS_TAKE_TOKEN): { PVRSRV_BRIDGE_OUT_SYNC_OPS_TAKE_TOKEN *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; // TODO figure out what this is out->ui32ReadOpsPending = 0; out->ui32WriteOpsPending = 0; out->ui32ReadOps2Pending = 0; break; } case _IOC_NR(PVRSRV_BRIDGE_SYNC_OPS_FLUSH_TO_TOKEN): { PVRSRV_BRIDGE_RETURN *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; break; } case _IOC_NR(PVRSRV_BRIDGE_RELEASE_MMAP_DATA): { PVRSRV_BRIDGE_OUT_RELEASE_MMAP_DATA *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; // TODO figure out what this is out->bMUnmap = false; break; } case _IOC_NR(PVRSRV_BRIDGE_GET_MISC_INFO): { PVRSRV_BRIDGE_IN_GET_MISC_INFO *in = bridge_package->pvParamIn; PVRSRV_BRIDGE_OUT_GET_MISC_INFO *out = bridge_package->pvParamOut; get_bridge_misc(in, out); break; } case _IOC_NR(PVRSRV_BRIDGE_EVENT_OBJECT_OPEN): { PVRSRV_BRIDGE_IN_EVENT_OBJECT_OPEN *in = bridge_package->pvParamIn; PVRSRV_BRIDGE_OUT_EVENT_OBJECT_OPEN *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; out->hOSEvent = in->sEventObject.hOSEventKM; break; } case _IOC_NR(PVRSRV_BRIDGE_GET_DEVMEM_HEAPINFO): { PVRSRV_BRIDGE_OUT_GET_DEVMEM_HEAPINFO *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; out->ui32ClientHeapCount = sizeof(pvr_heaps)/sizeof(*pvr_heaps); memcpy(out->sHeapInfo, pvr_heaps, sizeof(pvr_heaps)); break; } case _IOC_NR(PVRSRV_BRIDGE_ALLOC_DEVICEMEM): { PVRSRV_BRIDGE_IN_ALLOCDEVICEMEM *in = bridge_package->pvParamIn; PVRSRV_BRIDGE_OUT_ALLOCDEVICEMEM *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; int handle = alloc_memory((int)in->hDevMemHeap, in->uSize); struct memory_allocation *alloc = &allocations[handle-1]; out->psKernelMemInfo = (void*)alloc; out->sClientMemInfo.pvLinAddr = alloc->cpu_ptr; out->sClientMemInfo.pvLinAddrKM = alloc->cpu_ptr; out->sClientMemInfo.sDevVAddr.uiAddr = alloc->device_ptr; // TODO missing flags out->sClientMemInfo.uAllocSize = alloc->size; // TODO missing sync out->sClientMemInfo.hMappingInfo = (void*)handle; out->sClientMemInfo.hKernelMemInfo = (void*)handle; out->sClientMemInfo.hResItem = (void*)handle; out->sClientMemInfo.psNext = NULL; if (in->ui32Attribs & PVRSRV_MEM_NO_SYNCOBJ) { out->sClientMemInfo.psClientSyncInfo = NULL; } else { struct memory_allocation *sync = alloc_sync(&out->sClientSyncInfo); out->sClientMemInfo.psClientSyncInfo = (void*)sync; } LOG("Allocated 0x%x at 0x%x:0x%x from %s\n", alloc->size, (uintptr_t)alloc->cpu_ptr, alloc->device_ptr, pvr_heap_names[(int)in->hDevMemHeap]); break; } case _IOC_NR(PVRSRV_BRIDGE_SGX_GETINTERNALDEVINFO): { PVRSRV_BRIDGE_OUT_GETINTERNALDEVINFO *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; out->sSGXInternalDevInfo.hHostCtlKernelMemInfoHandle = (void*)create_handle(1024, "PVRSRV_BRIDGE_SGX_GETINTERNALDEVINFO"); out->sSGXInternalDevInfo.bForcePTOff = false; break; } case _IOC_NR(PVRSRV_BRIDGE_SGX_REGISTER_HW_RENDER_CONTEXT): { PVRSRV_BRIDGE_IN_SGX_REGISTER_HW_RENDER_CONTEXT *in = bridge_package->pvParamIn; PVRSRV_BRIDGE_OUT_SGX_REGISTER_HW_RENDER_CONTEXT *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; out->hHWRenderContext = (void*)create_handle(in->ui32HWRenderContextSize, "PVRSRV_BRIDGE_SGX_REGISTER_HW_RENDER_CONTEXT"); SOFT_TODO("Not setting sHWRenderContextDevVAddr for new render context"); out->sHWRenderContextDevVAddr.uiAddr = 0; break; } case _IOC_NR(PVRSRV_BRIDGE_SGX_REGISTER_HW_TRANSFER_CONTEXT): { PVRSRV_BRIDGE_IN_SGX_REGISTER_HW_TRANSFER_CONTEXT *in = bridge_package->pvParamIn; PVRSRV_BRIDGE_OUT_SGX_REGISTER_HW_TRANSFER_CONTEXT *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; out->hHWTransferContext = (void*)create_handle(in->ui32HWTransferContextSize, "PVRSRV_BRIDGE_IN_SGX_REGISTER_HW_TRANSFER_CONTEXT"); SOFT_TODO("Not setting sHWTransferContextDevVAddr for transfer context"); out->sHWTransferContextDevVAddr.uiAddr = 0; break; } case _IOC_NR(PVRSRV_BRIDGE_DISCONNECT_SERVICES): case _IOC_NR(PVRSRV_BRIDGE_SGX_RELEASECLIENTINFO): case _IOC_NR(PVRSRV_BRIDGE_CLOSE_DISPCLASS_DEVICE): case _IOC_NR(PVRSRV_BRIDGE_UNMAP_DEVICECLASS_MEMORY): case _IOC_NR(PVRSRV_BRIDGE_DESTROY_DEVMEMCONTEXT): { PVRSRV_BRIDGE_RETURN *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; break; } #if 0 case _IOC_NR(PVRSRV_BRIDGE_SGX_DEVINITPART2): { PVRSRV_BRIDGE_OUT_SGXDEVINITPART2 *out = bridge_package->pvParamOut; out->eError = PVRSRV_OK; out->ui32KMBuildOptions = SGX_BUILD_OPTIONS; printf("Input size is 0x%x\n", bridge_package->ui32InBufferSize); printf("Build options 0x%x\n", SGX_BUILD_OPTIONS); //out->ui32KMBuildOptions = 0x36a118; break; } #endif default: LOG("Unimplemented pvrsrv ioctl %d, may be %s\n", ioctl_nr, pvrsrv_ioctl_names[ioctl_nr]); assert(false); break; } return false; } static bool spoof_ioctl(int fd, int request, void *ptr) { int ioctl_nr = _IOC_NR(request); switch(ioctl_nr) { case _IOC_NR(DRM_IOCTL_VERSION): { LOG("Spoofing device info\n"); drmVersionPtr version = (drmVersionPtr)ptr; version->version_major = 1; version->version_minor = 14; version->version_patchlevel = 544606452; if (fd == false_disp) version->name_len = sizeof(DISP_NAME); else version->name_len = sizeof(DEV_NAME); version->date_len = sizeof(DEV_DATE); version->desc_len = sizeof(DEV_DESC); if (version->name) { if (fd == false_disp) strcpy(version->name, DISP_NAME); else strcpy(version->name, DEV_NAME); } if (version->date) strcpy(version->date, DEV_DATE); if (version->desc) strcpy(version->desc, DEV_DESC); break; } case _IOC_NR(DRM_IOCTL_SET_VERSION): { //return -1; break; } case _IOC_NR(DRM_IOCTL_GET_MAGIC): { struct drm_auth *auth = ptr; auth->magic = 036; break; } case _IOC_NR(DRM_IOCTL_DROP_MASTER): LOG(">>> ioctl(DRM_IOCTL_DROP_MASTER)\n"); return 0; case PVR_DRM_SRVKM_CMD: //LOG(">>> ioctl(PVR_DRM_SRVKM_CMD)\n"); //fwrite(ptr, 1, sizeof(PVRSRV_BRIDGE_PACKAGE), log_file); //PPRINT(stdout, ptr, PVRSRV_BRIDGE_PACKAGE); return pvrsrv_ioctl(fd, ptr); break; case PVR_DRM_IS_MASTER_CMD: LOG(">>> ioctl(PVR_DRM_IS_MASTER_CMD) 0x%x\n", PVR_DRM_SRVKM_CMD); /* From KMD source code this seems to always return 0 */ break; default: LOG("Unimplemented ioctl 0x%x\n", ioctl_nr); assert(false); break; } return 0; } int ioctl(int fd, int request, ...) { PROLOG(ioctl); int ioc_size = _IOC_SIZE(request); bool pvr = fd == false_fd || fd == false_disp; //printf("Size is %d\n", ioc_size); void *ptr = NULL; if(ioc_size) { va_list args; va_start(args, request); ptr = va_arg(args, void *); va_end(args); } //LOG("Got ioctl %d!\n", fd); if (pvr) return spoof_ioctl(fd, request, ptr); else return orig_ioctl(fd, request, ptr); } struct iovec_spoof { struct iovec request[3]; struct iovec response[1]; size_t response_len; }; struct iovec_spoof spoofed_response[] = { { {{"b\0\3\0\4\0\0\0", 8}, {"DRI2", 4}, {"", 0}}, {{"\1\0\7\0\0\0\0\0\1\232f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 4096}}, 32, }, { {{"b\0\10\0\27\0\0\0", 8}, {"Generic Event Extension", 23}, {"\0", 1}}, {{"\1\0\10\0\0\0\0\0\1\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 4096}}, 32, }, { {{"\200\0\2\0\1\0\0\0", 8}, {NULL, 0}, {"", 0}}, {{"\1\0\t\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 4096}}, 32, }, { {{"\232\0\3\0\1\0\0\0\4\0\0\0", 12}, {NULL, 0}, {"", 0}}, {{"\1\0\n\0\0\0\0\0\1\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 4096}}, 32, }, { {{"\232\1\3\0\211\0\0\0\0\0\0\0", 12}, {NULL, 0}, {"", 0}}, {{"\1\0\v\0\6\0\0\0\6\0\0\0\16\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0jz4780\0\0/dev/dri/card0\0\0", 4096}}, 56, }, /* TODO I'm not so sure this one is right */ { {{"\232\1\3\0000\4\0\0\0\0\0\0", 12}, {NULL, 0}, {NULL, 0}}, {{"\1\0\v\0\6\0\0\0\3\0\0\0\21\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0pvr\0\0/dev/dri/renderD128\0\0", 4096}}, 61, }, { {{"\232\2\3\0000\4\0\0\36\0\0\0", 12}, {NULL, 0}, {"", 0}}, {{"\1\0\f\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 4096}}, 32, }, }; static struct iovec_spoof *iovec_response = NULL; static bool do_poll = false; static int recv_sent = 0; int connect(int sockfd, const struct sockaddr_un *addr, socklen_t addrlen) { PROLOG(connect); /* No idea why I need to offset socket path by 1 */ if (addr->sun_family == AF_LOCAL && strcmp(addr->sun_path+1, "/tmp/.X11-unix/X0") == 0) { LOG("Tracking socket %d for spoofing\n", sockfd); spoof_socket = sockfd; } return orig_connect(sockfd, addr, addrlen); } static struct iovec_spoof *compare_iov(const struct iovec *iov, int iovcnt) { for (size_t i = 0; i < ARRAY_SIZE(spoofed_response); i++) { struct iovec *req = spoofed_response[i].request; /* TODO all requests might not have length of 3 */ if (iovcnt != 3) continue; bool same = true; for (size_t j = 0; j < iovcnt; j++) { if (req[j].iov_len != iov[j].iov_len || memcmp(req[j].iov_base, iov[j].iov_base, iov[j].iov_len) != 0) { same = false; break; } } if (same) { LOG("Spoofing with response %d\n", i); return &spoofed_response[i]; } } return NULL; } int writev(int fd, const struct iovec *iov, int iovcnt) { PROLOG(writev); int ret = orig_writev(fd, iov, iovcnt); if (fd == spoof_socket) { iovec_response = compare_iov(iov, iovcnt); //LOG("Trying to spoof and got %p\n", iovec_response); /* TODO this is hardcoding the response * really compare_iov should give us more information */ if (iovec_response != NULL) { do_poll = true; recv_sent = 3; return iov[0].iov_len + iov[1].iov_len + iov[2].iov_len; } } return ret; } ssize_t recvmsg(int socket, struct msghdr *message, int flags) { PROLOG(recvmsg); int ret = orig_recvmsg(socket, message, flags); if (socket == spoof_socket && iovec_response) { //orig_recvmsg(socket, message, flags); LOG("Spoofing response\n"); uint16_t seq_num = ((uint16_t*)message->msg_iov->iov_base)[1]; memcpy(message->msg_iov->iov_base, iovec_response->response[0].iov_base, iovec_response[0].response_len); ((uint16_t*)message->msg_iov->iov_base)[1] = seq_num; message->msg_iov->iov_len = iovec_response[0].response_len; message->msg_name = NULL; message->msg_controllen = 0; message->msg_flags = 0; int ret_len = iovec_response->response_len; iovec_response = NULL; recv_sent--; do_poll = true; LOG("Returning %d\n", ret_len); return ret_len; } #if 0 if (recv_sent > 0) { recv_sent--; return -1; } #endif return ret; } int poll(struct pollfd *fds, nfds_t nfds, int timeout) { PROLOG(poll); if (nfds == 1 && fds[0].fd == spoof_socket && do_poll) { LOG("Spoofed poll 0x%x\n", fds[0].events); fds[0].revents = fds[0].events == (POLLIN|POLLOUT) ? POLLOUT : POLLIN; //do_poll = false; return 1; } return orig_poll(fds, nfds, timeout); } int open(const char *pathname, int flags, mode_t mode) { LOG("Called open on %s (%d)\n", pathname, flags); PROLOG(open); if (strcmp(pathname, "/dev/dri/card0") == 0) { LOG("Spoofing FD!\n"); return false_disp; } return orig_open(pathname, flags, mode); } int openat(int dirfd, const char *pathname, int flags, ...) { LOG("Called openat on %s (%d) (%d)\n", pathname, dirfd, flags); PROLOG(openat); return orig_openat(dirfd, pathname, flags); } int openat64(int fd, const char * path, int oflag, ...) { LOG("Called openat64 %s\n", path); PROLOG(openat64); return orig_openat64(fd, path, oflag); } DIR *opendir(const char *dirname) { LOG("Opening dir %s\n", dirname); PROLOG(opendir); return orig_opendir(dirname); } struct dirent *readdir(DIR *dirp) { PROLOG(readdir); struct dirent *out = orig_readdir(dirp); if (out) { //printf("Reading %d %d %s\n", (int)out->d_type, (int)out->d_reclen, (char*)out->d_name); } return out; }