JVM Auto-Attach + Fix Thread Exiting + Fix Thread Signal Handler

This commit is contained in:
◱ PixelyIon 2020-10-17 17:08:27 +05:30 committed by ◱ PixelyIon
parent 6f2cd41470
commit 657beea070
18 changed files with 149 additions and 116 deletions

View File

@ -3,30 +3,53 @@
#include "jvm.h"
thread_local JNIEnv *env;
namespace skyline {
JvmManager::JvmManager(JNIEnv *environ, jobject instance) : instance(environ->NewGlobalRef(instance)), instanceClass(reinterpret_cast<jclass>(environ->NewGlobalRef(environ->GetObjectClass(instance)))), initializeControllersId(environ->GetMethodID(instanceClass, "initializeControllers", "()V")), vibrateDeviceId(environ->GetMethodID(instanceClass, "vibrateDevice", "(I[J[I)V")), clearVibrationDeviceId(environ->GetMethodID(instanceClass, "clearVibrationDevice", "(I)V")) {
env = environ;
/*
* @brief A thread-local wrapper over JNIEnv and JavaVM which automatically handles attaching and detaching threads
*/
struct JniEnvironment {
static inline JNIEnv *env{};
static inline JavaVM *vm{};
bool attached{};
JniEnvironment(JNIEnv *environment) {
env = environment;
if (env->GetJavaVM(&vm) < 0)
throw exception("Cannot get JavaVM from environment");
}
JniEnvironment() {
if (vm) {
vm->AttachCurrentThread(&env, nullptr);
attached = true;
}
}
~JniEnvironment() {
if (vm && attached)
vm->DetachCurrentThread();
}
operator JNIEnv *() {
return env;
}
JNIEnv* operator->() {
return env;
}
};
thread_local inline JniEnvironment env;
JvmManager::JvmManager(JNIEnv *environ, jobject instance) : instance(environ->NewGlobalRef(instance)), instanceClass(reinterpret_cast<jclass>(environ->NewGlobalRef(environ->GetObjectClass(instance)))), initializeControllersId(environ->GetMethodID(instanceClass, "initializeControllers", "()V")), vibrateDeviceId(environ->GetMethodID(instanceClass, "vibrateDevice", "(I[J[I)V")), clearVibrationDeviceId(environ->GetMethodID(instanceClass, "clearVibrationDevice", "(I)V")) {
env = JniEnvironment(environ);
}
JvmManager::~JvmManager() {
env->DeleteGlobalRef(instanceClass);
env->DeleteGlobalRef(instance);
}
void JvmManager::AttachThread() {
if (!env)
vm->AttachCurrentThread(&env, nullptr);
}
void JvmManager::DetachThread() {
if (env)
vm->DetachCurrentThread();
}
JNIEnv *JvmManager::GetEnv() {
return env;
}

View File

@ -12,7 +12,6 @@ namespace skyline {
*/
class JvmManager {
public:
JavaVM *vm{}; //!< A pointer to the Java VM
jobject instance; //!< A reference to the activity
jclass instanceClass; //!< The class of the activity
@ -24,16 +23,6 @@ namespace skyline {
~JvmManager();
/**
* @brief Attach the current thread to the Java VM
*/
void AttachThread();
/**
* @brief Detach the current thread to the Java VM
*/
void DetachThread();
/**
* @brief Returns a pointer to the JNI environment for the current thread
*/

View File

@ -141,16 +141,22 @@ namespace skyline::kernel {
if (lower->size) {
upper = chunks.insert(upper, lowerExtension);
chunks.insert(upper, chunk);
} else {
auto lower2{std::prev(lower)};
if (chunk.IsCompatible(*lower2) && lower2->ptr + lower2->size >= chunk.ptr) {
lower2->size = chunk.ptr + chunk.size - lower2->ptr;
upper = chunks.erase(lower);
} else {
*lower = chunk;
chunks.insert(upper, lowerExtension);
}
} else if (chunk.IsCompatible(*lower)) {
lower->size = std::max(lower->ptr + lower->size, chunk.ptr + chunk.size) - lower->ptr;
upper = chunks.insert(upper, lowerExtension);
}
} else if (chunk.IsCompatible(*lower) && lower->ptr + lower->size >= chunk.ptr) {
lower->size = chunk.ptr + chunk.size - lower->ptr;
} else {
if (lower->ptr + lower->size > chunk.ptr)
lower->size = chunk.ptr - lower->ptr;
if (upper != chunks.end() && chunk.IsCompatible(*upper)) {
if (upper != chunks.end() && chunk.IsCompatible(*upper) && chunk.ptr + chunk.size >= upper->ptr) {
upper->ptr = chunk.ptr;
upper->size = chunk.size + upper->size;
} else {
@ -170,7 +176,7 @@ namespace skyline::kernel {
return std::nullopt;
}
size_t MemoryManager::GetProgramSize() {
size_t MemoryManager::GetMemoryUsage() {
size_t size{};
for (const auto &chunk : chunks)
if (chunk.state != memory::states::Unmapped)

View File

@ -53,7 +53,7 @@ namespace skyline {
bool isDeviceShared : 1; //!< True when DeviceRefCount > 0
bool isUncached : 1; //!< This is used to disable memory caching to share memory with the GPU
};
u32 value;
u32 value{};
};
/**
@ -137,7 +137,7 @@ namespace skyline {
bool attributeChangeAllowed : 1; //!< If the application can use svcSetMemoryAttribute on this block
bool codeMemoryAllowed : 1; //!< If the application can use svcCreateCodeMemory on this block
};
u32 value;
u32 value{};
};
static_assert(sizeof(MemoryState) == sizeof(u32));
@ -239,7 +239,7 @@ namespace skyline {
/**
* @return The cumulative size of all memory mappings in bytes
*/
size_t GetProgramSize();
size_t GetMemoryUsage();
};
}
}

View File

@ -24,7 +24,7 @@ namespace skyline::kernel::svc {
state.ctx->gpr.w0 = Result{};
state.ctx->gpr.x1 = reinterpret_cast<u64>(heap->ptr);
state.logger->Debug("svcSetHeapSize: Allocated at {} for 0x{:X} bytes", heap->ptr, heap->size);
state.logger->Debug("svcSetHeapSize: Allocated at 0x{:X} - 0x{:X} (0x{:X} bytes)", heap->ptr, heap->ptr + heap->size, heap->size);
}
void SetMemoryAttribute(const DeviceState &state) {
@ -71,7 +71,7 @@ namespace skyline::kernel::svc {
newChunk.attributes.isUncached = value.isUncached;
state.process->memory.InsertChunk(newChunk);
state.logger->Debug("svcSetMemoryAttribute: Set caching to {} at 0x{:X} for 0x{:X} bytes", static_cast<bool>(value.isUncached), pointer, size);
state.logger->Debug("svcSetMemoryAttribute: Set caching to {} at 0x{:X} - 0x{:X} (0x{:X} bytes)", static_cast<bool>(value.isUncached), pointer, pointer + size, size);
state.ctx->gpr.w0 = Result{};
}
@ -216,7 +216,7 @@ namespace skyline::kernel::svc {
void ExitProcess(const DeviceState &state) {
state.logger->Debug("svcExitProcess: Exiting process");
//state.os->KillThread(state.process->pid);
exit(0);
}
void CreateThread(const DeviceState &state) {
@ -235,7 +235,7 @@ namespace skyline::kernel::svc {
if (!stack)
throw exception("svcCreateThread: Cannot find memory object in handle table for thread stack: 0x{:X}", stackTop);
auto thread{state.process->CreateThread(entry, entryArgument, priority, static_pointer_cast<type::KPrivateMemory>(stack->item))};
auto thread{state.process->CreateThread(entry, entryArgument, stackTop, priority)};
state.logger->Debug("svcCreateThread: Created thread with handle 0x{:X} (Entry Point: 0x{:X}, Argument: 0x{:X}, Stack Pointer: 0x{:X}, Priority: {}, ID: {})", thread->handle, entry, entryArgument, stackTop, priority, thread->id);
state.ctx->gpr.w1 = thread->handle;
@ -257,7 +257,8 @@ namespace skyline::kernel::svc {
void ExitThread(const DeviceState &state) {
state.logger->Debug("svcExitThread: Exiting current thread: {}", state.thread->id);
state.os->KillThread(state.thread->id);
state.thread->Kill();
pthread_exit(nullptr);
}
void SleepThread(const DeviceState &state) {
@ -338,7 +339,7 @@ namespace skyline::kernel::svc {
return;
}
state.logger->Debug("svcMapSharedMemory: Mapping shared memory at 0x{:X} for {} bytes ({}{}{})", pointer, size, permission.r ? 'R' : '-', permission.w ? 'W' : '-', permission.x ? 'X' : '-');
state.logger->Debug("svcMapSharedMemory: Mapping shared memory at 0x{:X} - 0x{:X} (0x{:X} bytes) ({}{}{})", pointer, pointer + size, size, permission.r ? 'R' : '-', permission.w ? 'W' : '-', permission.x ? 'X' : '-');
object->Map(pointer, size, permission);
@ -372,7 +373,7 @@ namespace skyline::kernel::svc {
}
auto tmem{state.process->NewHandle<type::KTransferMemory>(pointer, size, permission)};
state.logger->Debug("svcCreateTransferMemory: Creating transfer memory at 0x{:X} for {} bytes ({}{}{})", pointer, size, permission.r ? 'R' : '-', permission.w ? 'W' : '-', permission.x ? 'X' : '-');
state.logger->Debug("svcCreateTransferMemory: Creating transfer memory at 0x{:X} - 0x{:X} (0x{:X} bytes) ({}{}{})", pointer, pointer + size, size, permission.r ? 'R' : '-', permission.w ? 'W' : '-', permission.x ? 'X' : '-');
state.ctx->gpr.w0 = Result{};
state.ctx->gpr.w1 = tmem.handle;
@ -674,7 +675,7 @@ namespace skyline::kernel::svc {
break;
case constant::infoState::TotalMemoryUsage:
out = state.process->heap->size + state.thread->stack->size + state.process->memory.GetProgramSize();
out = state.process->memory.GetMemoryUsage();
break;
case constant::infoState::AddressSpaceBaseAddr:
@ -698,7 +699,7 @@ namespace skyline::kernel::svc {
break;
case constant::infoState::PersonalMmHeapUsage:
out = state.process->heap->size + state.thread->stack->size;
out = state.process->heap->size + state.process->mainThreadStack->size;
break;
case constant::infoState::TotalMemoryAvailableWithoutMmHeap:
@ -706,7 +707,7 @@ namespace skyline::kernel::svc {
break;
case constant::infoState::TotalMemoryUsedWithoutMmHeap:
out = state.process->heap->size + state.thread->stack->size; // TODO: Same as above
out = state.process->heap->size + state.process->mainThreadStack->size; // TODO: Same as above
break;
case constant::infoState::UserExceptionContextAddr:

View File

@ -11,8 +11,8 @@ namespace skyline::kernel::type {
KPrivateMemory::KPrivateMemory(const DeviceState &state, u8* ptr, size_t size, memory::Permission permission, memory::MemoryState memState) : ptr(ptr), size(size), permission(permission), memState(memState), KMemory(state, KType::KPrivateMemory) {
if (!state.process->memory.base.IsInside(ptr) || !state.process->memory.base.IsInside(ptr + size))
throw exception("KPrivateMemory allocation isn't inside guest address space: 0x{:X} - 0x{:X}", ptr, ptr + size);
if (!util::PageAligned(ptr))
throw exception("KPrivateMemory was created with non-page-aligned address: 0x{:X}", ptr);
if (!util::PageAligned(ptr) || !util::PageAligned(size))
throw exception("KPrivateMemory mapping isn't page-aligned: 0x{:X} - 0x{:X} (0x{:X})", ptr, ptr + size, size);
if (mprotect(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC) < 0) // We only need to reprotect as the allocation has already been reserved by the MemoryManager
throw exception("An occurred while mapping private memory: {} with 0x{:X} @ 0x{:X}", strerror(errno), ptr, size);

View File

@ -49,8 +49,15 @@ namespace skyline::kernel::type {
return tlsPage->ReserveSlot();
}
std::shared_ptr<KThread> KProcess::CreateThread(void *entry, u64 argument, i8 priority, const std::shared_ptr<KPrivateMemory> &stack) {
auto thread{NewHandle<KThread>(this, threads.size(), entry, argument, priority, stack).item};
std::shared_ptr<KThread> KProcess::CreateThread(void *entry, u64 argument, void *stackTop, i8 priority) {
if (!stackTop && threads.empty()) { //!< Main thread stack is created by the kernel and owned by the process
constexpr u64 DefaultStackSize{0x200000}; //!< The default amount of stack: 2 MB
mainThreadStack = mainThreadStack.make_shared(state, reinterpret_cast<u8 *>(state.process->memory.stack.address), DefaultStackSize, memory::Permission{true, true, false}, memory::states::Stack);
if (mprotect(mainThreadStack->ptr, PAGE_SIZE, PROT_NONE))
throw exception("Failed to create guard page for thread stack at 0x{:X}", mainThreadStack->ptr);
stackTop = mainThreadStack->ptr + mainThreadStack->size;
}
auto thread{NewHandle<KThread>(this, threads.size(), entry, argument, stackTop, priority).item};
threads.push_back(thread);
return thread;
}

View File

@ -64,6 +64,7 @@ namespace skyline {
public:
MemoryManager memory;
std::shared_ptr<KPrivateMemory> mainThreadStack;
std::shared_ptr<KPrivateMemory> heap;
std::vector<std::shared_ptr<KThread>> threads;
std::vector<std::shared_ptr<TlsPage>> tlsPages;
@ -80,7 +81,7 @@ namespace skyline {
*/
u8* AllocateTlsSlot();
std::shared_ptr<KThread> CreateThread(void *entry, u64 argument = 0, i8 priority = 44, const std::shared_ptr<KPrivateMemory> &stack = nullptr);
std::shared_ptr<KThread> CreateThread(void *entry, u64 argument = 0, void *stackTop = nullptr, i8 priority = constant::DefaultPriority);
/**
* @brief The output for functions that return created kernel objects

View File

@ -23,8 +23,8 @@ namespace skyline::kernel::type {
u8 *KSharedMemory::Map(u8 *ptr, u64 size, memory::Permission permission) {
if (!state.process->memory.base.IsInside(ptr) || !state.process->memory.base.IsInside(ptr + size))
throw exception("KPrivateMemory allocation isn't inside guest address space: 0x{:X} - 0x{:X}", ptr, ptr + size);
if (ptr && !util::PageAligned(ptr))
throw exception("KSharedMemory was mapped to a non-page-aligned address: 0x{:X}", ptr);
if (!util::PageAligned(ptr) || !util::PageAligned(size))
throw exception("KSharedMemory mapping isn't page-aligned: 0x{:X} - 0x{:X} (0x{:X})", ptr, ptr + size, size);
guest.ptr = reinterpret_cast<u8 *>(mmap(ptr, size, permission.Get(), MAP_SHARED | (ptr ? MAP_FIXED : 0), fd, 0));
if (guest.ptr == MAP_FAILED)

View File

@ -4,12 +4,15 @@
#include <sys/types.h>
#include <sys/resource.h>
#include <unistd.h>
#include <asm/unistd.h>
#include <nce.h>
#include <os.h>
#include <android/log.h>
#include <dlfcn.h>
#include "KProcess.h"
namespace skyline::kernel::type {
KThread::KThread(const DeviceState &state, KHandle handle, KProcess *parent, size_t id, void *entry, u64 argument, i8 priority, const std::shared_ptr<KPrivateMemory> &stack) : handle(handle), parent(parent), id(id), entry(entry), entryArgument(argument), stack(stack), KSyncObject(state, KType::KThread) {
KThread::KThread(const DeviceState &state, KHandle handle, KProcess *parent, size_t id, void *entry, u64 argument, void *stackTop, i8 priority) : handle(handle), parent(parent), id(id), entry(entry), entryArgument(argument), stackTop(stackTop), KSyncObject(state, KType::KThread) {
UpdatePriority(priority);
}
@ -17,6 +20,24 @@ namespace skyline::kernel::type {
Kill();
}
/**
* @brief Our delegator for sigaction, we need to do this due to sigchain hooking bionic's sigaction and it intercepting signals before they're passed onto userspace
* This not only leads to performance degradation but also requires host TLS to be in the TLS register which we cannot ensure for in-guest signals
*/
inline void Sigaction(int signal, const struct sigaction &action, struct sigaction *oldAction = nullptr) {
static decltype(&sigaction) realSigaction{};
if (!realSigaction) {
void *libc{dlopen("libc.so", RTLD_LOCAL | RTLD_LAZY)};
if (!libc)
throw exception("dlopen-ing libc has failed with: {}", dlerror());
realSigaction = reinterpret_cast<decltype(&sigaction)>(dlsym(libc, "sigaction"));
if (!realSigaction)
throw exception("Cannot find 'sigaction' in libc: {}", dlerror());
}
if (realSigaction(signal, &action, oldAction) < 0)
throw exception("sigaction has failed with {}", strerror(errno));
}
void KThread::StartThread() {
pthread_setname_np(pthread_self(), fmt::format("HOS-{}", id).c_str());
@ -31,9 +52,8 @@ namespace skyline::kernel::type {
.sa_sigaction = &nce::NCE::SignalHandler,
.sa_flags = SA_SIGINFO,
};
//for (int signal : {SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV})
// sigaction(signal, &sigact, nullptr);
for (int signal : {SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV})
Sigaction(signal, sigact);
asm volatile(
"MRS X0, TPIDR_EL0\n\t"
@ -109,7 +129,7 @@ namespace skyline::kernel::type {
"DUP V31.16B, WZR\n\t"
"RET"
:
: "r"(&ctx), "r"(stack->ptr + stack->size), "r"(entry), "r"(entryArgument), "r"(handle)
: "r"(&ctx), "r"(stackTop), "r"(entry), "r"(entryArgument), "r"(handle)
: "x0", "x1", "lr"
);
@ -119,16 +139,7 @@ namespace skyline::kernel::type {
void KThread::Start(bool self) {
if (!running) {
running = true;
state.logger->Debug("Starting thread #{}", id);
if (!stack) {
constexpr u64 DefaultStackSize{0x1E8480}; //!< The default amount of stack: 2 MB
stack = stack.make_shared(state, reinterpret_cast<u8 *>(state.process->memory.stack.address), DefaultStackSize, memory::Permission{true, true, false}, memory::states::Stack);
if (mprotect(stack->ptr, PAGE_SIZE, PROT_NONE))
throw exception("Failed to create guard page for thread stack at 0x{:X}", stack->ptr);
}
if (self)
StartThread();
else
@ -139,10 +150,7 @@ namespace skyline::kernel::type {
void KThread::Kill() {
if (running) {
running = false;
Signal();
exit(0);
//tgkill(gettgid(), tid, SIGTERM);
}
}

View File

@ -51,20 +51,20 @@ namespace skyline {
void StartThread();
public:
std::atomic<bool> running{false};
bool running{false};
std::atomic<bool> cancelSync{false}; //!< This is to flag to a thread to cancel a synchronization call it currently is in
KHandle handle;
size_t id; //!< Index of thread in parent process's KThread vector
std::shared_ptr<KPrivateMemory> stack;
nce::ThreadContext ctx{};
void* entry;
u64 entryArgument;
void* stackTop;
i8 priority;
KThread(const DeviceState &state, KHandle handle, KProcess *parent, size_t id, void *entry, u64 argument = 0, i8 priority = constant::DefaultPriority, const std::shared_ptr<KPrivateMemory>& stack = nullptr);
KThread(const DeviceState &state, KHandle handle, KProcess *parent, size_t id, void *entry, u64 argument, void *stackTop, i8 priority = constant::DefaultPriority);
~KThread();
@ -75,6 +75,10 @@ namespace skyline {
*/
void Start(bool self = false);
/**
* @brief Updates the internal state of the thread to signal it being dead
* @note This should only be called by the host thread running this guest thread
*/
void Kill();
/**

View File

@ -36,17 +36,4 @@ namespace skyline::kernel {
state.nce->Execute();
}
void OS::KillThread(pid_t pid) {
/*
if (process->pid == pid) {
state.logger->Debug("Killing process with PID: {}", pid);
for (auto &thread: process->threads)
thread.second->Kill();
} else {
state.logger->Debug("Killing thread with TID: {}", pid);
process->threads.at(pid)->Kill();
}
*/
}
}

View File

@ -39,11 +39,5 @@ namespace skyline::kernel {
* @return An instance of the KProcess of the created process
*/
std::shared_ptr<type::KProcess> CreateProcess(u64 entry, u64 argument, size_t stackSize);
/**
* @brief Kill a particular thread
* @param pid The PID of the thread
*/
void KillThread(pid_t pid);
};
}

View File

@ -20,7 +20,7 @@ namespace skyline::service::am {
return result::OutOfBounds;
if (size)
request.outputBuf.at(0).copy_from(span(parent->content.data() + offset, size));
span(parent->content).copy_from(request.outputBuf.at(0), size);
return {};
}

View File

@ -124,16 +124,18 @@ namespace skyline::service::hosbinder {
auto nvmap{driver->nvMap.lock()};
if (gbpBuffer.nvmapHandle) {
nvBuffer = nvmap->handleTable.at(gbpBuffer.nvmapHandle);
nvBuffer = nvmap->GetObject(gbpBuffer.nvmapHandle);
} else {
for (const auto &object : nvmap->handleTable) {
if (object.second->id == gbpBuffer.nvmapId) {
nvBuffer = object.second;
std::shared_lock nvmapLock(nvmap->mapMutex);
for (const auto &object : nvmap->maps) {
if (object->id == gbpBuffer.nvmapId) {
nvBuffer = object;
break;
}
}
if (!nvBuffer)
throw exception("A QueueBuffer request has an invalid NVMap Handle ({}) and ID ({})", gbpBuffer.nvmapHandle, gbpBuffer.nvmapId);
nvmapLock.unlock();
}
gpu::texture::Format format;
@ -187,9 +189,6 @@ namespace skyline::service::hosbinder {
}
}
/**
* @brief A mapping from a display's name to it's displayType entry
*/
static frz::unordered_map<frz::string, DisplayId, 5> DisplayTypeMap{
{"Default", DisplayId::Default},
{"External", DisplayId::External},

View File

@ -81,7 +81,7 @@ namespace skyline::service::nvdrv::device {
try {
auto driver{nvdrv::driver.lock()};
auto nvmap{driver->nvMap.lock()};
auto mapping{nvmap->handleTable.at(data.nvmapHandle)};
auto mapping{nvmap->GetObject(data.nvmapHandle)};
if (data.flags.remap) {
auto region{regionMap.upper_bound(data.offset)};
@ -180,7 +180,7 @@ namespace skyline::service::nvdrv::device {
try {
auto driver{nvdrv::driver.lock()};
auto nvmap{driver->nvMap.lock()};
auto mapping{nvmap->handleTable.at(entry.nvmapHandle)};
auto mapping{nvmap->GetObject(entry.nvmapHandle)};
u64 mapAddress{static_cast<u64>(entry.gpuOffset) << MinAlignmentShift};
u8* mapPointer{mapping->pointer + (static_cast<u64>(entry.mapOffset) << MinAlignmentShift)};

View File

@ -14,8 +14,9 @@ namespace skyline::service::nvdrv::device {
u32 handle; // Out
} &data = buffer.as<Data>();
handleTable[handleIndex] = std::make_shared<NvMapObject>(idIndex++, data.size);
data.handle = handleIndex++;
std::unique_lock lock(mapMutex);
maps.push_back(std::make_shared<NvMapObject>(idIndex++, data.size));
data.handle = maps.size();
state.logger->Debug("Size: 0x{:X} -> Handle: 0x{:X}", data.size, data.handle);
return NvStatus::Success;
@ -27,9 +28,10 @@ namespace skyline::service::nvdrv::device {
u32 handle; // Out
} &data = buffer.as<Data>();
for (const auto &object : handleTable) {
if (object.second->id == data.id) {
data.handle = object.first;
std::shared_lock lock(mapMutex);
for (auto it{maps.begin()}; it < maps.end(); it++) {
if ((*it)->id == data.id) {
data.handle = (it - maps.begin()) + 1;
state.logger->Debug("ID: 0x{:X} -> Handle: 0x{:X}", data.id, data.handle);
return NvStatus::Success;
}
@ -51,7 +53,7 @@ namespace skyline::service::nvdrv::device {
} &data = buffer.as<Data>();
try {
auto &object{handleTable.at(data.handle)};
auto object{GetObject(data.handle)};
object->heapMask = data.heapMask;
object->flags = data.flags;
object->align = data.align;
@ -76,8 +78,9 @@ namespace skyline::service::nvdrv::device {
u32 flags; // Out
} &data = buffer.as<Data>();
std::unique_lock lock(mapMutex);
try {
const auto &object{handleTable.at(data.handle)};
auto &object{maps.at(data.handle)};
if (object.use_count() > 1) {
data.pointer = object->pointer;
data.flags = 0x0;
@ -87,7 +90,7 @@ namespace skyline::service::nvdrv::device {
}
data.size = object->size;
handleTable.erase(data.handle);
object = nullptr;
state.logger->Debug("Handle: 0x{:X} -> Pointer: 0x{:X}, Size: 0x{:X}, Flags: 0x{:X}", data.handle, data.pointer, data.size, data.flags);
return NvStatus::Success;
@ -115,7 +118,7 @@ namespace skyline::service::nvdrv::device {
} &data = buffer.as<Data>();
try {
auto &object{handleTable.at(data.handle)};
auto object{GetObject(data.handle)};
switch (data.parameter) {
case Parameter::Size:
@ -158,7 +161,7 @@ namespace skyline::service::nvdrv::device {
} &data = buffer.as<Data>();
try {
data.id = handleTable.at(data.handle)->id;
data.id = GetObject(data.handle)->id;
state.logger->Debug("Handle: 0x{:X} -> ID: 0x{:X}", data.handle, data.id);
return NvStatus::Success;
} catch (const std::out_of_range &) {

View File

@ -32,12 +32,23 @@ namespace skyline::service::nvdrv::device {
NvMapObject(u32 id, u32 size);
};
std::unordered_map<KHandle, std::shared_ptr<NvMapObject>> handleTable; //!< A mapping from a handle to it's corresponding NvMapObject
KHandle handleIndex{1}; //!< This is used to keep track of the next handle to allocate
std::shared_mutex mapMutex; //!< Synchronizes mutations and accesses of the mappings
std::vector<std::shared_ptr<NvMapObject>> maps;
u32 idIndex{1}; //!< This is used to keep track of the next ID to allocate
NvMap(const DeviceState &state);
inline std::shared_ptr<NvMapObject> GetObject(u32 handle) {
if (handle-- == 0)
throw std::out_of_range("0 is an invalid nvmap handle");
std::shared_lock lock(mapMutex);
auto& object{maps.at(handle)};
if (!object)
throw std::out_of_range("A freed nvmap handle was requested");
return object;
}
/**
* @brief Creates an NvMapObject and returns an handle to it
* @url https://switchbrew.org/wiki/NV_services#NVMAP_IOC_CREATE