blob: 00c2a3d267b2ec55a5dd7efee52b3b2ed558a061 [file] [log] [blame]
/*************************************************************************/ /*!
@File
@Title Environment related functions
@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved
@License MIT
The contents of this file are subject to the MIT license as set out below.
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.
This License is also included in this distribution in the file called
"MIT-COPYING".
EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) 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; AND (B) 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.
*/ /**************************************************************************/
#include <condition_variable>
#include <mutex>
#include <shared_mutex>
#include <thread>
#include <unordered_set>
#include <vector>
#include <lib/zx/event.h>
#include <zircon/syscalls.h>
#include "fuchsia/msd_img_connection.h"
#include "fuchsia/msd_img_device.h"
#include "fuchsia/msd_img_semaphore.h"
#include "magma_util/macros.h"
#include "platform_barriers.h"
#include "platform_bus_mapper.h"
#include "platform_semaphore.h"
#include "platform_thread.h"
extern "C" {
#include "allocmem.h"
#include "cache_km.h"
#include "img_types.h"
#include "log2.h"
#include "osfunc.h"
#include "osfunc_fuchsia.h"
#if defined(PVRSRV_ENABLE_PROCESS_STATS)
#include "process_stats.h"
#endif
#include "pvr_debug.h"
#include "pvrsrv_memallocflags.h"
}
#define NOT_IMPLEMENTED() fprintf(stderr, PVR_BUILD_DIR ": Not implemented in %s:%s:%d\n", __func__, __FILE__, __LINE__);
namespace msd_img
{
struct EventObject
{
std::mutex mutex;
std::condition_variable condition_variable;
// wake_count is guarded by mutex, but clang's thread safety analysis
// doesn't understand that.
uint32_t wake_count = 0;
// These are used to wake other processes.
MAGMA_GUARDED(mutex) std::unordered_set<std::shared_ptr<MsdImgSemaphore>> semaphores;
};
struct EventObjectHandle
{
static constexpr uint32_t kMagic = 'hEVT';
static EventObjectHandle *FromHandle(IMG_HANDLE img_event_object)
{
auto event_object = reinterpret_cast<EventObjectHandle *>(img_event_object);
assert(event_object->magic == kMagic);
return event_object;
}
uint32_t magic = kMagic;
std::shared_ptr<EventObject> object;
};
// This struct is used by a single thread to wait for a signal to happen.
struct EventObjectOpenHandle
{
static constexpr uint32_t kMagic = 'hEVO';
static EventObjectOpenHandle *FromHandle(IMG_HANDLE img_event_object)
{
auto event_object = reinterpret_cast<EventObjectOpenHandle *>(img_event_object);
assert(event_object->magic == kMagic);
return event_object;
}
~EventObjectOpenHandle()
{
if (semaphore)
{
std::lock_guard<std::mutex> lock(object->mutex);
object->semaphores.erase(semaphore);
}
}
uint32_t magic = kMagic;
std::shared_ptr<EventObject> object;
std::shared_ptr<MsdImgSemaphore> semaphore;
// last_seen_wake_count is set to 0 on the new object, so the first wait
// will succeed instantly if the object was ever signaled.
uint32_t last_seen_wake_count = 0;
};
struct Thread
{
static constexpr uint32_t kMagic = 'hThr';
uint32_t magic = kMagic;
~Thread()
{
if (running)
{
thrd_join(thread, 0);
}
}
// Use C11 threads so we can get the thread handle to set the priority
thrd_t thread = 0;
bool running = false;
std::string name;
void *hData = nullptr;
PFN_THREAD pfnThread = nullptr;
static Thread *cast(IMG_HANDLE thr)
{
auto thread_ptr = static_cast<Thread *>(thr);
assert(thread_ptr->magic == kMagic);
return thread_ptr;
}
};
struct PageWrapper
{
static constexpr uint32_t kMagic = 'hPGS';
uint32_t magic = kMagic;
std::unique_ptr<magma::PlatformBuffer> buffer;
std::unique_ptr<magma::PlatformBusMapper::BusMapping> mapping;
static PageWrapper *FromHandle(PG_HANDLE *psMemHandle)
{
auto object = reinterpret_cast<PageWrapper *>(psMemHandle->u.pvHandle);
DASSERT(object->magic == kMagic);
return object;
}
};
struct MisrHandle
{
std::thread thread;
// ZX_EVENT_SIGNALED is used to wake up, and ZX_USER_SIGNAL_0 is used to
// force the handler to quit.
zx::event event;
};
} // namespace msd_img
static PVRSRV_ERROR
ZxStatusToError(zx_status_t status)
{
switch (status)
{
case ZX_OK: return PVRSRV_OK;
case ZX_ERR_INTERNAL: return PVRSRV_ERROR_INTERNAL_ERROR;
case ZX_ERR_NOT_SUPPORTED: return PVRSRV_ERROR_NOT_SUPPORTED;
case ZX_ERR_NO_RESOURCES: return PVRSRV_ERROR_OUT_OF_MEMORY;
case ZX_ERR_NO_MEMORY: return PVRSRV_ERROR_OUT_OF_MEMORY;
case ZX_ERR_INVALID_ARGS: return PVRSRV_ERROR_INVALID_PARAMS;
case ZX_ERR_BAD_HANDLE: return PVRSRV_ERROR_INVALID_HANDLE_TYPE;
case ZX_ERR_OUT_OF_RANGE: return PVRSRV_ERROR_HANDLE_INDEX_OUT_OF_RANGE;
case ZX_ERR_BUFFER_TOO_SMALL: return PVRSRV_ERROR_INVALID_PARAMS;
case ZX_ERR_BAD_STATE: return PVRSRV_ERROR_INVALID_DEVICE;
case ZX_ERR_TIMED_OUT: return PVRSRV_ERROR_TIMEOUT;
case ZX_ERR_UNAVAILABLE: return PVRSRV_ERROR_INVALID_DEVICE;
case ZX_ERR_ACCESS_DENIED: return PVRSRV_ERROR_INIT_FAILURE;
default: return PVRSRV_ERROR_NOT_SUPPORTED;
}
}
void
OSThreadDumpInfo(IMG_HANDLE hDbgReqestHandle, DUMPDEBUG_PRINTF_FUNC *pfnDumpDebugPrintf, void *pvDumpDebugFile)
{
}
PVRSRV_ERROR
OSPhyContigPagesAlloc(PVRSRV_DEVICE_NODE *psDevNode, size_t uiSize, PG_HANDLE *psMemHandle, IMG_DEV_PHYADDR *psDevPAddr)
{
MsdImgDevice *device = MsdImgDevice::cast(psDevNode->psDevConfig->pvOSDevice);
auto page_struct = std::make_unique<msd_img::PageWrapper>();
page_struct->buffer = device->bus_mapper()->CreateContiguousBuffer(uiSize, magma::page_shift(), "IMG contig");
if (!page_struct->buffer)
{
return DRET(PVRSRV_ERROR_OUT_OF_MEMORY);
}
page_struct->mapping = device->bus_mapper()->MapPageRangeBus(page_struct->buffer.get(), 0,
page_struct->buffer->size() / magma::page_size());
if (!page_struct->mapping)
{
return DRET(PVRSRV_ERROR_OUT_OF_MEMORY);
}
psDevPAddr->uiAddr = page_struct->mapping->Get()[0];
psMemHandle->u.pvHandle = page_struct.release();
return PVRSRV_OK;
}
void
OSPhyContigPagesFree(PVRSRV_DEVICE_NODE *psDevNode, PG_HANDLE *psMemHandle)
{
auto page_struct = msd_img::PageWrapper::FromHandle(psMemHandle);
delete page_struct;
}
PVRSRV_ERROR
OSPhyContigPagesMap(PVRSRV_DEVICE_NODE *psDevNode,
PG_HANDLE *psMemHandle,
size_t uiSize,
IMG_DEV_PHYADDR *psDevPAddr,
void **pvPtr)
{
auto page_struct = msd_img::PageWrapper::FromHandle(psMemHandle);
if (!page_struct->buffer->MapCpu(pvPtr, 0))
{
return DRET(PVRSRV_ERROR_OUT_OF_MEMORY);
}
return PVRSRV_OK;
}
void
OSPhyContigPagesUnmap(PVRSRV_DEVICE_NODE *psDevNode, PG_HANDLE *psMemHandle, void *pvPtr)
{
auto page_struct = msd_img::PageWrapper::FromHandle(psMemHandle);
page_struct->buffer->UnmapCpu();
}
PVRSRV_ERROR
OSPhyContigPagesClean(PVRSRV_DEVICE_NODE *psDevNode, PG_HANDLE *psMemHandle, IMG_UINT32 uiOffset, IMG_UINT32 uiLength)
{
auto page_struct = msd_img::PageWrapper::FromHandle(psMemHandle);
if (!page_struct->buffer->CleanCache(uiOffset, uiLength, false))
{
return DRET(PVRSRV_ERROR_INVALID_PARAMS);
}
return PVRSRV_OK;
}
IMG_UINT32
OSCPUCacheAttributeSize(IMG_DCACHE_ATTRIBUTE eCacheAttribute)
{
return zx_system_get_dcache_line_size();
}
IMG_UINT32
OSVSScanf(IMG_CHAR *pStr, const IMG_CHAR *pszFormat, ...)
{
va_list args;
va_start(args, pszFormat);
uint32_t res = vsscanf(pStr, pszFormat, args);
va_end(args);
return res;
}
IMG_INT
OSMemCmp(void *pvBufA, void *pvBufB, size_t uiLen)
{
return memcmp(pvBufA, pvBufB, uiLen);
}
size_t
OSStringLCopy(IMG_CHAR *pszDest, const IMG_CHAR *pszSrc, size_t uSize)
{
return strlcpy(pszDest, pszSrc, uSize);
}
IMG_CHAR *
OSStringNCopy(IMG_CHAR *pszDest, const IMG_CHAR *pszSrc, size_t uSize)
{
return strncpy(pszDest, pszSrc, uSize);
}
IMG_INT32
OSSNPrintf(IMG_CHAR *pStr, size_t ui32Size, const IMG_CHAR *pszFormat, ...)
{
va_list args;
va_start(args, pszFormat);
int res = vsnprintf(pStr, ui32Size, pszFormat, args);
va_end(args);
return res;
}
size_t
OSStringLength(const IMG_CHAR *pStr)
{
return strlen(pStr);
}
size_t
OSStringNLength(const IMG_CHAR *pStr, size_t uiCount)
{
return strnlen(pStr, uiCount);
}
IMG_INT32
OSStringCompare(const IMG_CHAR *pStr1, const IMG_CHAR *pStr2)
{
return strcmp(pStr1, pStr2);
}
IMG_INT32
OSStringNCompare(const IMG_CHAR *pStr1, const IMG_CHAR *pStr2, size_t uiSize)
{
return strncmp(pStr1, pStr2, uiSize);
}
PVRSRV_ERROR
OSStringToUINT32(const IMG_CHAR *pStr, IMG_UINT32 ui32Base, IMG_UINT32 *ui32Result)
{
*ui32Result = strtoul(pStr, nullptr, ui32Base);
return PVRSRV_OK;
}
PVRSRV_ERROR
OSInitEnvData(void)
{
return PVRSRV_OK;
}
void
OSDeInitEnvData(void)
{
NOT_IMPLEMENTED();
}
#if defined(PVRSRV_USE_BRIDGE_LOCK)
PVRSRV_ERROR
OSGetGlobalBridgeBuffers(void **ppvBridgeInBuffer, void **ppvBridgeOutBuffer)
{
NOT_IMPLEMENTED();
return PVRSRV_OK;
}
#endif
void
OSReleaseThreadQuanta(void)
{
std::this_thread::yield();
}
IMG_UINT64
OSClockns64(void)
{
return zx_clock_get_monotonic();
}
IMG_UINT64
OSClockus64(void)
{
return zx_clock_get_monotonic() / 1000;
}
IMG_UINT32
OSClockus(void)
{
return zx_clock_get_monotonic() / 1000;
}
IMG_UINT32
OSClockms(void)
{
return zx_clock_get_monotonic() / 1000000;
}
PVRSRV_ERROR
OSClockMonotonicns64(IMG_UINT64 *pui64Time)
{
*pui64Time = zx_clock_get_monotonic();
return PVRSRV_OK;
}
PVRSRV_ERROR
OSClockMonotonicus64(IMG_UINT64 *pui64Time)
{
*pui64Time = zx_clock_get_monotonic() / 1000;
return PVRSRV_OK;
}
IMG_UINT64
OSClockMonotonicRawns64(void)
{
return zx_clock_get_monotonic();
}
IMG_UINT64
OSClockMonotonicRawus64(void)
{
return zx_clock_get_monotonic() / 1000;
}
void
OSWaitus(IMG_UINT32 ui32Timeus)
{
// We don't have to worry about interrupt contexts or anything like that, so it's reasonable
// to sleep if the desired delay is long enough. 50 microseconds is a reasonable guess about
// how long is enough time for another thread to do some useful work after being scheduled
// in.
if (ui32Timeus > 50)
{
zx_nanosleep(zx_deadline_after(ZX_USEC(ui32Timeus)));
}
else
{
uint64_t start = OSClockus64();
while (OSClockus64() - start < ui32Timeus)
{
#if defined(__aarch64__)
__asm__ __volatile__("yield");
#endif
}
}
}
void
OSSleepms(IMG_UINT32 ui32Timems)
{
zx_nanosleep(zx_deadline_after(ZX_MSEC(ui32Timems)));
}
IMG_UINT64
OSGetCurrentProcessVASpaceSize(void)
{
// Currently every process on Fuchsia has the same VA space size.
return magma::PlatformBuffer::MinimumMappableAddress() + magma::PlatformBuffer::MappableAddressRegionLength();
}
IMG_PID
OSGetCurrentProcessID(void)
{
return magma::PlatformProcessHelper::GetCurrentProcessId();
}
IMG_CHAR *
OSGetCurrentProcessName(void)
{
return OSGetCurrentClientProcessNameKM();
}
uintptr_t
OSGetCurrentThreadID(void)
{
NOT_IMPLEMENTED();
return 0;
}
IMG_PID
OSGetCurrentClientProcessIDKM(void)
{
MsdImgConnection *connection = MsdImgConnection::GetCurrentConnection();
// Some statistics will check for the process id on startup when there
// is no current connection.
if (!connection)
return 0;
return connection->client_id();
}
IMG_CHAR *
OSGetCurrentClientProcessNameKM(void)
{
static char msd_name[] = "MSD";
MsdImgConnection *connection = MsdImgConnection::GetCurrentConnection();
// If there's no connection the current action is on the behalf of the
// MSD.
return connection ? connection->client_name() : msd_name;
}
uintptr_t
OSGetCurrentClientThreadIDKM(void)
{
MsdImgConnection *connection = MsdImgConnection::GetCurrentConnection();
// This is used only for debug logging, so it doesn't matter if it doesn't
// exist.
if (!connection)
return 0;
return connection->current_client_thread_id();
}
size_t
OSGetPageSize(void)
{
return magma::page_size();
}
size_t
OSGetPageShift(void)
{
return magma::page_shift();
}
size_t
OSGetPageMask(void)
{
// Lower bits set.
return magma::page_size() - 1;
}
size_t
OSGetOrder(size_t uSize)
{
NOT_IMPLEMENTED();
return 0;
}
IMG_UINT64
OSGetRAMSize(void)
{
return zx_system_get_physmem();
}
int
PVRSRVToNativeError(PVRSRV_ERROR e)
{
NOT_IMPLEMENTED();
return 0;
}
PVRSRV_ERROR
OSInstallMISR(IMG_HANDLE *hMISRData, PFN_MISR pfnMISR, void *hData)
{
auto misr_handle = std::make_unique<msd_img::MisrHandle>();
zx_status_t status = zx::event::create(0, &misr_handle->event);
if (status != ZX_OK)
{
return ZxStatusToError(status);
}
misr_handle->thread = std::thread(
[](PFN_MISR pfnMISR, void *hData, msd_img::MisrHandle *misr_handle) {
magma::PlatformThreadHelper::SetCurrentThreadName("MISR Thread");
#if !defined(NO_HARDWARE)
std::unique_ptr<magma::PlatformHandle> profile =
MsdImgDevice::GetGlobalDevice()->platform_device()->GetSchedulerProfile(
magma::PlatformDevice::kPriorityHighest, "img-priority");
magma::PlatformThreadHelper::SetProfile(profile.get());
#endif // defined(NO_HARDWARE)
while (true)
{
zx_signals_t observed;
zx_status_t status = misr_handle->event.wait_one(ZX_EVENT_SIGNALED | ZX_USER_SIGNAL_0,
zx::time::infinite(), &observed);
if (status != ZX_OK)
{
DLOG("Failed to wait on MISR handle");
return;
}
// Clear before running the callback to ensure a signal during the
// callback won't be ignored.
status = misr_handle->event.signal(ZX_EVENT_SIGNALED, 0);
if (status != ZX_OK)
{
DLOG("Failed clear MISR handle");
return;
}
if (observed & ZX_USER_SIGNAL_0)
break;
pfnMISR(hData);
}
},
pfnMISR, hData, misr_handle.get());
*hMISRData = misr_handle.release();
return PVRSRV_OK;
}
PVRSRV_ERROR
OSUninstallMISR(IMG_HANDLE hMISRData)
{
auto misr_handle = reinterpret_cast<msd_img::MisrHandle *>(hMISRData);
if (misr_handle->event.signal(0, ZX_USER_SIGNAL_0) != ZX_OK)
{
DLOG("Failed to signal MISR on shutdown");
misr_handle->thread.detach();
}
else
{
misr_handle->thread.join();
}
delete misr_handle;
return PVRSRV_OK;
}
PVRSRV_ERROR
OSScheduleMISR(IMG_HANDLE hMISRData)
{
auto misr_handle = reinterpret_cast<msd_img::MisrHandle *>(hMISRData);
return ZxStatusToError(misr_handle->event.signal(0, ZX_EVENT_SIGNALED));
}
PVRSRV_ERROR
OSThreadCreate(IMG_HANDLE *phThread,
IMG_CHAR *pszThreadName,
PFN_THREAD pfnThread,
IMG_HANDLE pfnDebugDumpCB,
IMG_BOOL bIsSupportingThread,
void *hData)
{
return OSThreadCreatePriority(phThread, pszThreadName, pfnThread, pfnDebugDumpCB, bIsSupportingThread, hData,
OS_THREAD_NOSET_PRIORITY);
}
namespace
{
int
thread_func(void *data)
{
msd_img::Thread *handle = msd_img::Thread::cast(data);
magma::PlatformThreadHelper::SetCurrentThreadName(handle->name);
handle->pfnThread(handle->hData);
return 0;
}
} // namespace
PVRSRV_ERROR
OSThreadCreatePriority(IMG_HANDLE *phThread,
IMG_CHAR *pszThreadName,
PFN_THREAD pfnThread,
IMG_HANDLE pfnDebugDumpCB,
IMG_BOOL bIsSupportingThread,
void *hData,
OS_THREAD_LEVEL eThreadPriority)
{
auto handle = std::make_unique<msd_img::Thread>();
handle->name = std::string(pszThreadName);
handle->hData = hData;
handle->pfnThread = pfnThread;
int res = thrd_create(&handle->thread, thread_func, handle.get());
if (res != thrd_success)
{
return PVRSRV_ERROR_INIT_FAILURE;
}
handle->running = true;
*phThread = handle.release();
if (eThreadPriority != OS_THREAD_NOSET_PRIORITY)
{
return OSSetThreadPriority(*phThread, eThreadPriority, 0);
}
return PVRSRV_OK;
}
PVRSRV_ERROR
OSThreadDestroy(IMG_HANDLE hThread)
{
msd_img::Thread *handle = msd_img::Thread::cast(hThread);
delete handle;
return PVRSRV_OK;
}
void
OSPanic(void)
{
NOT_IMPLEMENTED();
}
magma::PlatformDevice::Priority
OSThreadToMagmaPriority(IMG_UINT32 nThreadPriority)
{
switch (nThreadPriority)
{
case OS_THREAD_HIGHEST_PRIORITY:
case OS_THREAD_HIGH_PRIORITY:
// TODO - Reduce priority of high.
return magma::PlatformDevice::kPriorityHighest;
case OS_THREAD_LOWEST_PRIORITY: return magma::PlatformDevice::kPriorityLowest;
case OS_THREAD_LOW_PRIORITY: return magma::PlatformDevice::kPriorityLow;
case OS_THREAD_NOSET_PRIORITY:
case OS_THREAD_NORMAL_PRIORITY:
default: return magma::PlatformDevice::kPriorityDefault;
}
}
PVRSRV_ERROR
OSSetThreadPriority(IMG_HANDLE hThread, IMG_UINT32 nThreadPriority, IMG_UINT32 nThreadWeight)
{
msd_img::Thread *handle = msd_img::Thread::cast(hThread);
#if defined(NO_HARDWARE)
// With no hardware there's no platform device to read the profile
// from.
return PVRSRV_OK;
#endif
std::unique_ptr<magma::PlatformHandle> profile =
MsdImgDevice::GetGlobalDevice()->platform_device()->GetSchedulerProfile(
OSThreadToMagmaPriority(nThreadPriority), "img-priority");
if (!profile)
{
return PVRSRV_ERROR_INIT_FAILURE;
}
return magma::PlatformThreadHelper::SetThreadProfile(handle->thread, profile.get()) ? PVRSRV_OK
: PVRSRV_ERROR_INIT_FAILURE;
}
void *
OSMapPhysToLin(IMG_CPU_PHYADDR BasePAddr, size_t ui32Bytes, IMG_UINT32 ui32MappingFlags)
{
if (BasePAddr.uiAddr == MsdImgDevice::GetRegistersPhysicalAddress())
{
return MsdImgDevice::GetRegistersCpuAddress();
}
NOT_IMPLEMENTED();
return nullptr;
}
IMG_BOOL
OSUnMapPhysToLin(void *pvLinAddr, size_t ui32Bytes, IMG_UINT32 ui32MappingFlags)
{
NOT_IMPLEMENTED();
return IMG_FALSE;
}
IMG_HANDLE
OSAddTimer(PFN_TIMER_FUNC pfnTimerFunc, void *pvData, IMG_UINT32 ui32MsTimeout)
{
NOT_IMPLEMENTED();
return 0;
}
PVRSRV_ERROR
OSRemoveTimer(IMG_HANDLE hTimer)
{
NOT_IMPLEMENTED();
return PVRSRV_ERROR_NOT_SUPPORTED;
}
PVRSRV_ERROR
OSEnableTimer(IMG_HANDLE hTimer)
{
NOT_IMPLEMENTED();
return PVRSRV_ERROR_NOT_SUPPORTED;
}
PVRSRV_ERROR
OSDisableTimer(IMG_HANDLE hTimer)
{
NOT_IMPLEMENTED();
return PVRSRV_ERROR_NOT_SUPPORTED;
}
PVRSRV_ERROR
OSEventObjectCreate(const IMG_CHAR *pszName, IMG_HANDLE *hEventObject)
{
auto handle = std::make_unique<msd_img::EventObjectHandle>();
handle->object = std::make_shared<msd_img::EventObject>();
*hEventObject = handle.release();
return PVRSRV_OK;
}
PVRSRV_ERROR
OSEventObjectDestroy(IMG_HANDLE hEventObject)
{
delete msd_img::EventObjectHandle::FromHandle(hEventObject);
return PVRSRV_OK;
}
PVRSRV_ERROR
OSEventObjectWait(IMG_HANDLE hOSEventKM)
{
auto handle = msd_img::EventObjectOpenHandle::FromHandle(hOSEventKM);
std::unique_lock<std::mutex> lock(handle->object->mutex);
handle->object->condition_variable.wait(
lock, [&handle]() { return handle->object->wake_count != handle->last_seen_wake_count; });
handle->last_seen_wake_count = handle->object->wake_count;
return PVRSRV_OK;
}
PVRSRV_ERROR
OSEventObjectWaitTimeoutAndHoldBridgeLock(IMG_HANDLE hOSEventKM, IMG_UINT64 uiTimeoutus)
{
#if defined(PVRSRV_USE_BRIDGE_LOCK)
#error Bridge lock not supported
#endif
return OSEventObjectWaitTimeout(hOSEventKM, uiTimeoutus);
}
PVRSRV_ERROR
OSEventObjectWaitAndHoldBridgeLock(IMG_HANDLE hOSEventKM)
{
#if defined(PVRSRV_USE_BRIDGE_LOCK)
#error Bridge lock not supported
#endif
return OSEventObjectWait(hOSEventKM);
}
PVRSRV_ERROR
OSEventObjectWaitKernel(IMG_HANDLE hOSEventKM, IMG_UINT64 uiTimeoutus)
{
auto handle = msd_img::EventObjectOpenHandle::FromHandle(hOSEventKM);
std::unique_lock<std::mutex> lock(handle->object->mutex);
auto timeout_time = std::chrono::steady_clock::now() + std::chrono::microseconds(uiTimeoutus);
if (!handle->object->condition_variable.wait_until(
lock, timeout_time, [&handle]() { return handle->object->wake_count != handle->last_seen_wake_count; }))
{
DASSERT(handle->last_seen_wake_count == handle->object->wake_count);
return PVRSRV_ERROR_TIMEOUT;
}
handle->last_seen_wake_count = handle->object->wake_count;
return PVRSRV_OK;
}
PVRSRV_ERROR
OSEventObjectOpenWithConnection(IMG_HANDLE hEventObject, MsdImgConnection *connection, IMG_HANDLE *phOSEvent)
{
auto event_object = msd_img::EventObjectHandle::FromHandle(hEventObject);
auto event_object_open = std::make_unique<msd_img::EventObjectOpenHandle>();
event_object_open->object = event_object->object;
std::shared_ptr<MsdImgSemaphore> semaphore = connection ? connection->TakeAdditionalSemaphore() : nullptr;
if (semaphore)
{
event_object_open->semaphore = semaphore;
std::lock_guard<std::mutex> lock(event_object_open->object->mutex);
event_object_open->object->semaphores.insert(semaphore);
// Just be sure we don't barely miss a signal. Waiters should be able
// to handle spurious wakeups.
semaphore->platform_semaphore()->Signal();
}
*phOSEvent = event_object_open.release();
return PVRSRV_OK;
}
PVRSRV_ERROR
OSEventObjectOpen(IMG_HANDLE hEventObject, IMG_HANDLE *phOSEvent)
{
MsdImgConnection *connection = MsdImgConnection::GetCurrentConnection();
return OSEventObjectOpenWithConnection(hEventObject, connection, phOSEvent);
}
PVRSRV_ERROR
OSEventObjectClose(IMG_HANDLE hOSEventKM)
{
delete msd_img::EventObjectOpenHandle::FromHandle(hOSEventKM);
return PVRSRV_OK;
}
PVRSRV_ERROR
OSEventObjectSignal(IMG_HANDLE hEventObject)
{
auto handle = msd_img::EventObjectHandle::FromHandle(hEventObject);
std::lock_guard<std::mutex> lock(handle->object->mutex);
handle->object->wake_count++;
handle->object->condition_variable.notify_all();
for (auto &semaphore : handle->object->semaphores)
{
semaphore->platform_semaphore()->Signal();
}
return PVRSRV_OK;
}
PVRSRV_ERROR
OSCopyToUser(void *pvProcess, void __user *pvDest, const void *pvSrc, size_t ui32Bytes)
{
PVR_UNREFERENCED_PARAMETER(pvProcess);
// We would use pvProcess for this, but the generated code always sets it to
// NULL.
MsdImgConnection *connection = MsdImgConnection::GetCurrentConnection();
return connection->CopyToUser(pvDest, pvSrc, ui32Bytes) ? PVRSRV_OK : PVRSRV_ERROR_INVALID_PARAMS;
}
PVRSRV_ERROR
OSCopyFromUser(void *pvProcess, void *pvDest, const void __user *pvSrc, size_t ui32Bytes)
{
PVR_UNREFERENCED_PARAMETER(pvProcess);
// We would use pvProcess for this, but the generated code always sets it to
// NULL.
MsdImgConnection *connection = MsdImgConnection::GetCurrentConnection();
return connection->CopyFromUser(pvDest, pvSrc, ui32Bytes) ? PVRSRV_OK : PVRSRV_ERROR_INVALID_PARAMS;
}
IMG_UINT64
OSDivide64r64(IMG_UINT64 ui64Divident, IMG_UINT32 ui32Divisor, IMG_UINT32 *pui32Remainder)
{
*pui32Remainder = ui64Divident % ui32Divisor;
return ui64Divident / ui32Divisor;
}
IMG_UINT32
OSDivide64(IMG_UINT64 ui64Divident, IMG_UINT32 ui32Divisor, IMG_UINT32 *pui32Remainder)
{
*pui32Remainder = ui64Divident % ui32Divisor;
return ui64Divident / ui32Divisor;
}
/* One time osfunc initialisation */
PVRSRV_ERROR
PVROSFuncInit(void)
{
NOT_IMPLEMENTED();
return PVRSRV_ERROR_NOT_SUPPORTED;
}
/*
* Osfunc deinitialisation.
* Note that PVROSFuncInit may not have been called
*/
void
PVROSFuncDeInit(void)
{
NOT_IMPLEMENTED();
}
void
OSDumpStack(void)
{
NOT_IMPLEMENTED();
}
#if defined(PVRSRV_USE_BRIDGE_LOCK)
void
OSAcquireBridgeLock(void)
{
NOT_IMPLEMENTED();
}
void
OSReleaseBridgeLock(void)
{
NOT_IMPLEMENTED();
}
IMG_BOOL
BridgeLockIsLocked(void)
{
NOT_IMPLEMENTED();
return 0;
}
#endif
struct StatisticEntry
{
uint32_t unused;
};
void *
OSCreateStatisticEntry(IMG_CHAR *pszName,
void *pvFolder,
OS_STATS_PRINT_FUNC *pfnStatsPrint,
OS_INC_STATS_MEM_REFCOUNT_FUNC *pfnIncMemRefCt,
OS_DEC_STATS_MEM_REFCOUNT_FUNC *pfnDecMemRefCt,
void *pvData)
{
// TODO(fxbug.dev/13185): Implement.
NOT_IMPLEMENTED();
return new StatisticEntry;
}
void
OSRemoveStatisticEntry(void *pvEntry)
{
delete reinterpret_cast<StatisticEntry *>(pvEntry);
}
#if defined(PVRSRV_ENABLE_MEMTRACK_STATS_FILE)
void *
OSCreateRawStatisticEntry(const IMG_CHAR *pszFileName, void *pvParentDir, OS_STATS_PRINT_FUNC *pfStatsPrint)
{
NOT_IMPLEMENTED();
return 0;
}
void
OSRemoveRawStatisticEntry(void *pvEntry)
{
NOT_IMPLEMENTED();
}
#endif
/*************************************************************************/ /*!
@Function OSCreateStatisticFolder
@Description Create a statistic folder to hold statistic entries.
@Input pszName String containing the name for the folder.
@Input pvFolder Reference from OSCreateStatisticFolder() of the folder
to create the folder in, or NULL for the root.
@Return Pointer void reference to the folder created, which can be
passed to OSRemoveStatisticFolder() to remove the folder.
*/ /**************************************************************************/
void *
OSCreateStatisticFolder(IMG_CHAR *pszName, void *pvFolder)
{
NOT_IMPLEMENTED();
return 0;
} /* OSCreateStatisticFolder */
/*************************************************************************/ /*!
@Function OSRemoveStatisticFolder
@Description Removes a statistic folder.
@Input ppvFolder Reference from OSCreateStatisticFolder() of the
folder that should be removed.
This needs to be double pointer because it has to
be NULLed right after memory is freed to avoid
possible races and use-after-free situations.
*/ /**************************************************************************/
void
OSRemoveStatisticFolder(void **ppvFolder)
{
NOT_IMPLEMENTED();
} /* OSRemoveStatisticFolder */
PVRSRV_ERROR
OSChangeSparseMemCPUAddrMap(void **psPageArray,
IMG_UINT64 sCpuVAddrBase,
IMG_CPU_PHYADDR sCpuPAHeapBase,
IMG_UINT32 ui32AllocPageCount,
IMG_UINT32 *pai32AllocIndices,
IMG_UINT32 ui32FreePageCount,
IMG_UINT32 *pai32FreeIndices,
IMG_BOOL bIsLMA)
{
NOT_IMPLEMENTED();
return PVRSRV_ERROR_NOT_SUPPORTED;
}
/*************************************************************************/ /*!
@Function OSDebugSignalPID
@Description Sends a SIGTRAP signal to a specific PID in user mode for
debugging purposes. The user mode process can register a handler
against this signal.
This is necessary to support the Rogue debugger. If the Rogue
debugger is not used then this function may be implemented as
a stub.
@Input ui32PID The PID for the signal.
@Return PVRSRV_OK on success, a failure code otherwise.
*/ /**************************************************************************/
PVRSRV_ERROR
OSDebugSignalPID(IMG_UINT32 ui32PID)
{
NOT_IMPLEMENTED();
return PVRSRV_ERROR_NOT_SUPPORTED;
}
void
OSDumpVersionInfo(DUMPDEBUG_PRINTF_FUNC *pfnDumpDebugPrintf, void *pvDumpDebugFile)
{
NOT_IMPLEMENTED();
}
void
OSWriteMemoryBarrier(void)
{
magma::barriers::WriteBarrier();
}
void
OSMemoryBarrier(void)
{
magma::barriers::Barrier();
}
struct _OSWR_LOCK_ {
std::shared_mutex mutex;
};
PVRSRV_ERROR OSWRLockCreate(POSWR_LOCK *ppsLock) {
*ppsLock = new _OSWR_LOCK_;
return PVRSRV_OK;
}
void OSWRLockDestroy(POSWR_LOCK psLock) {
delete psLock;
}
void OSWRLockAcquireRead(POSWR_LOCK psLock) {
psLock->mutex.lock_shared();
}
void OSWRLockReleaseRead(POSWR_LOCK psLock) {
psLock->mutex.unlock_shared();
}
void OSWRLockAcquireWrite(POSWR_LOCK psLock) {
psLock->mutex.lock();
}
void OSWRLockReleaseWrite(POSWR_LOCK psLock) {
psLock->mutex.unlock();
}
PVRSRV_ERROR
OSBridgeCopyFromUser(void *pvProcess, void *pvDest, const void *pvSrc, size_t ui32Bytes)
{
// pvSrv should be in the mapped payload buffer. The size of the buffer was
// validated when the bridge command was started.
memcpy(pvDest, pvSrc, ui32Bytes);
return PVRSRV_OK;
}
PVRSRV_ERROR
OSBridgeCopyToUser(void *pvProcess, void *pvDest, const void *pvSrc, size_t ui32Bytes)
{
// pvDest should be in the mapped payload buffer. The size of the buffer was
// validated when the bridge command was started.
memcpy(pvDest, pvSrc, ui32Bytes);
return PVRSRV_OK;
}
#if !defined(NO_HARDWARE)
// If NO_HARDWARE is defined these are replace with no-op macros.
// The memory barriers in the following functions don't seem to necessarily be
// required by the definitions of the methods, but we have them to match linux
// in case they happen to matter.
IMG_UINT8
OSReadHWReg8(volatile void *pvLinRegBaseAddr, IMG_UINT32 ui32Offset)
{
uint8_t value = *(reinterpret_cast<volatile uint8_t *>(pvLinRegBaseAddr) + ui32Offset);
magma::barriers::ReadBarrier();
return value;
}
IMG_UINT16
OSReadHWReg16(volatile void *pvLinRegBaseAddr, IMG_UINT32 ui32Offset)
{
uint16_t value =
*reinterpret_cast<volatile uint16_t *>(reinterpret_cast<volatile uint8_t *>(pvLinRegBaseAddr) + ui32Offset);
magma::barriers::ReadBarrier();
return value;
}
IMG_UINT32
OSReadHWReg32(volatile void *pvLinRegBaseAddr, IMG_UINT32 ui32Offset)
{
uint32_t value =
*reinterpret_cast<volatile uint32_t *>(reinterpret_cast<volatile uint8_t *>(pvLinRegBaseAddr) + ui32Offset);
magma::barriers::ReadBarrier();
return value;
}
IMG_UINT64
OSReadHWReg64(volatile void *pvLinRegBaseAddr, IMG_UINT32 ui32Offset)
{
auto first_reg = reinterpret_cast<volatile uint32_t *>(reinterpret_cast<uintptr_t>(pvLinRegBaseAddr) + ui32Offset);
uint64_t value = first_reg[0] | (static_cast<uint64_t>(first_reg[1]) << 32);
magma::barriers::ReadBarrier();
return value;
}
void
OSWriteHWReg8(volatile void *pvLinRegBaseAddr, IMG_UINT32 ui32Offset, IMG_UINT8 ui8Value)
{
magma::barriers::WriteBarrier();
*(reinterpret_cast<volatile uint8_t *>(pvLinRegBaseAddr) + ui32Offset) = ui8Value;
}
void
OSWriteHWReg16(volatile void *pvLinRegBaseAddr, IMG_UINT32 ui32Offset, IMG_UINT16 ui16Value)
{
magma::barriers::WriteBarrier();
*reinterpret_cast<volatile uint16_t *>(reinterpret_cast<volatile uint8_t *>(pvLinRegBaseAddr) + ui32Offset) =
ui16Value;
}
void
OSWriteHWReg32(volatile void *pvLinRegBaseAddr, IMG_UINT32 ui32Offset, IMG_UINT32 ui32Value)
{
magma::barriers::WriteBarrier();
*reinterpret_cast<volatile uint32_t *>(reinterpret_cast<volatile uint8_t *>(pvLinRegBaseAddr) + ui32Offset) =
ui32Value;
}
void
OSWriteHWReg64(volatile void *pvLinRegBaseAddr, IMG_UINT32 ui32Offset, IMG_UINT64 ui64Value)
{
magma::barriers::WriteBarrier();
auto first_reg = reinterpret_cast<volatile uint32_t *>(reinterpret_cast<uintptr_t>(pvLinRegBaseAddr) + ui32Offset);
first_reg[0] = ui64Value & 0xffffffff;
first_reg[1] = (ui64Value >> 32) & 0xffffffff;
}
#endif