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" #include "jvm.h"
thread_local JNIEnv *env;
namespace skyline { 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) if (env->GetJavaVM(&vm) < 0)
throw exception("Cannot get JavaVM from environment"); 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() { JvmManager::~JvmManager() {
env->DeleteGlobalRef(instanceClass); env->DeleteGlobalRef(instanceClass);
env->DeleteGlobalRef(instance); env->DeleteGlobalRef(instance);
} }
void JvmManager::AttachThread() {
if (!env)
vm->AttachCurrentThread(&env, nullptr);
}
void JvmManager::DetachThread() {
if (env)
vm->DetachCurrentThread();
}
JNIEnv *JvmManager::GetEnv() { JNIEnv *JvmManager::GetEnv() {
return env; return env;
} }

View File

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

View File

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

View File

@ -53,7 +53,7 @@ namespace skyline {
bool isDeviceShared : 1; //!< True when DeviceRefCount > 0 bool isDeviceShared : 1; //!< True when DeviceRefCount > 0
bool isUncached : 1; //!< This is used to disable memory caching to share memory with the GPU 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 attributeChangeAllowed : 1; //!< If the application can use svcSetMemoryAttribute on this block
bool codeMemoryAllowed : 1; //!< If the application can use svcCreateCodeMemory 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)); static_assert(sizeof(MemoryState) == sizeof(u32));
@ -239,7 +239,7 @@ namespace skyline {
/** /**
* @return The cumulative size of all memory mappings in bytes * @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.w0 = Result{};
state.ctx->gpr.x1 = reinterpret_cast<u64>(heap->ptr); 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) { void SetMemoryAttribute(const DeviceState &state) {
@ -71,7 +71,7 @@ namespace skyline::kernel::svc {
newChunk.attributes.isUncached = value.isUncached; newChunk.attributes.isUncached = value.isUncached;
state.process->memory.InsertChunk(newChunk); 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{}; state.ctx->gpr.w0 = Result{};
} }
@ -216,7 +216,7 @@ namespace skyline::kernel::svc {
void ExitProcess(const DeviceState &state) { void ExitProcess(const DeviceState &state) {
state.logger->Debug("svcExitProcess: Exiting process"); state.logger->Debug("svcExitProcess: Exiting process");
//state.os->KillThread(state.process->pid); exit(0);
} }
void CreateThread(const DeviceState &state) { void CreateThread(const DeviceState &state) {
@ -235,7 +235,7 @@ namespace skyline::kernel::svc {
if (!stack) if (!stack)
throw exception("svcCreateThread: Cannot find memory object in handle table for thread stack: 0x{:X}", stackTop); 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.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; state.ctx->gpr.w1 = thread->handle;
@ -257,7 +257,8 @@ namespace skyline::kernel::svc {
void ExitThread(const DeviceState &state) { void ExitThread(const DeviceState &state) {
state.logger->Debug("svcExitThread: Exiting current thread: {}", state.thread->id); 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) { void SleepThread(const DeviceState &state) {
@ -338,7 +339,7 @@ namespace skyline::kernel::svc {
return; 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); object->Map(pointer, size, permission);
@ -372,7 +373,7 @@ namespace skyline::kernel::svc {
} }
auto tmem{state.process->NewHandle<type::KTransferMemory>(pointer, size, permission)}; 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.w0 = Result{};
state.ctx->gpr.w1 = tmem.handle; state.ctx->gpr.w1 = tmem.handle;
@ -674,7 +675,7 @@ namespace skyline::kernel::svc {
break; break;
case constant::infoState::TotalMemoryUsage: case constant::infoState::TotalMemoryUsage:
out = state.process->heap->size + state.thread->stack->size + state.process->memory.GetProgramSize(); out = state.process->memory.GetMemoryUsage();
break; break;
case constant::infoState::AddressSpaceBaseAddr: case constant::infoState::AddressSpaceBaseAddr:
@ -698,7 +699,7 @@ namespace skyline::kernel::svc {
break; break;
case constant::infoState::PersonalMmHeapUsage: case constant::infoState::PersonalMmHeapUsage:
out = state.process->heap->size + state.thread->stack->size; out = state.process->heap->size + state.process->mainThreadStack->size;
break; break;
case constant::infoState::TotalMemoryAvailableWithoutMmHeap: case constant::infoState::TotalMemoryAvailableWithoutMmHeap:
@ -706,7 +707,7 @@ namespace skyline::kernel::svc {
break; break;
case constant::infoState::TotalMemoryUsedWithoutMmHeap: 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; break;
case constant::infoState::UserExceptionContextAddr: 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) { 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)) 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); throw exception("KPrivateMemory allocation isn't inside guest address space: 0x{:X} - 0x{:X}", ptr, ptr + size);
if (!util::PageAligned(ptr)) if (!util::PageAligned(ptr) || !util::PageAligned(size))
throw exception("KPrivateMemory was created with non-page-aligned address: 0x{:X}", ptr); 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 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); 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(); return tlsPage->ReserveSlot();
} }
std::shared_ptr<KThread> KProcess::CreateThread(void *entry, u64 argument, i8 priority, const std::shared_ptr<KPrivateMemory> &stack) { std::shared_ptr<KThread> KProcess::CreateThread(void *entry, u64 argument, void *stackTop, i8 priority) {
auto thread{NewHandle<KThread>(this, threads.size(), entry, argument, priority, stack).item}; 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); threads.push_back(thread);
return thread; return thread;
} }

View File

@ -64,6 +64,7 @@ namespace skyline {
public: public:
MemoryManager memory; MemoryManager memory;
std::shared_ptr<KPrivateMemory> mainThreadStack;
std::shared_ptr<KPrivateMemory> heap; std::shared_ptr<KPrivateMemory> heap;
std::vector<std::shared_ptr<KThread>> threads; std::vector<std::shared_ptr<KThread>> threads;
std::vector<std::shared_ptr<TlsPage>> tlsPages; std::vector<std::shared_ptr<TlsPage>> tlsPages;
@ -80,7 +81,7 @@ namespace skyline {
*/ */
u8* AllocateTlsSlot(); 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 * @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) { u8 *KSharedMemory::Map(u8 *ptr, u64 size, memory::Permission permission) {
if (!state.process->memory.base.IsInside(ptr) || !state.process->memory.base.IsInside(ptr + size)) 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); throw exception("KPrivateMemory allocation isn't inside guest address space: 0x{:X} - 0x{:X}", ptr, ptr + size);
if (ptr && !util::PageAligned(ptr)) if (!util::PageAligned(ptr) || !util::PageAligned(size))
throw exception("KSharedMemory was mapped to a non-page-aligned address: 0x{:X}", ptr); 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)); guest.ptr = reinterpret_cast<u8 *>(mmap(ptr, size, permission.Get(), MAP_SHARED | (ptr ? MAP_FIXED : 0), fd, 0));
if (guest.ptr == MAP_FAILED) if (guest.ptr == MAP_FAILED)

View File

@ -4,12 +4,15 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <unistd.h> #include <unistd.h>
#include <asm/unistd.h>
#include <nce.h> #include <nce.h>
#include <os.h> #include <os.h>
#include <android/log.h>
#include <dlfcn.h>
#include "KProcess.h" #include "KProcess.h"
namespace skyline::kernel::type { 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); UpdatePriority(priority);
} }
@ -17,6 +20,24 @@ namespace skyline::kernel::type {
Kill(); 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() { void KThread::StartThread() {
pthread_setname_np(pthread_self(), fmt::format("HOS-{}", id).c_str()); 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_sigaction = &nce::NCE::SignalHandler,
.sa_flags = SA_SIGINFO, .sa_flags = SA_SIGINFO,
}; };
for (int signal : {SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV})
//for (int signal : {SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV}) Sigaction(signal, sigact);
// sigaction(signal, &sigact, nullptr);
asm volatile( asm volatile(
"MRS X0, TPIDR_EL0\n\t" "MRS X0, TPIDR_EL0\n\t"
@ -109,7 +129,7 @@ namespace skyline::kernel::type {
"DUP V31.16B, WZR\n\t" "DUP V31.16B, WZR\n\t"
"RET" "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" : "x0", "x1", "lr"
); );
@ -119,16 +139,7 @@ namespace skyline::kernel::type {
void KThread::Start(bool self) { void KThread::Start(bool self) {
if (!running) { if (!running) {
running = true; running = true;
state.logger->Debug("Starting thread #{}", id); 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) if (self)
StartThread(); StartThread();
else else
@ -139,10 +150,7 @@ namespace skyline::kernel::type {
void KThread::Kill() { void KThread::Kill() {
if (running) { if (running) {
running = false; running = false;
Signal(); Signal();
exit(0);
//tgkill(gettgid(), tid, SIGTERM);
} }
} }

View File

@ -51,20 +51,20 @@ namespace skyline {
void StartThread(); void StartThread();
public: 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 std::atomic<bool> cancelSync{false}; //!< This is to flag to a thread to cancel a synchronization call it currently is in
KHandle handle; KHandle handle;
size_t id; //!< Index of thread in parent process's KThread vector size_t id; //!< Index of thread in parent process's KThread vector
std::shared_ptr<KPrivateMemory> stack;
nce::ThreadContext ctx{}; nce::ThreadContext ctx{};
void* entry; void* entry;
u64 entryArgument; u64 entryArgument;
void* stackTop;
i8 priority; 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(); ~KThread();
@ -75,6 +75,10 @@ namespace skyline {
*/ */
void Start(bool self = false); 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(); void Kill();
/** /**

View File

@ -36,17 +36,4 @@ namespace skyline::kernel {
state.nce->Execute(); 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 * @return An instance of the KProcess of the created process
*/ */
std::shared_ptr<type::KProcess> CreateProcess(u64 entry, u64 argument, size_t stackSize); 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; return result::OutOfBounds;
if (size) 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 {}; return {};
} }

View File

@ -124,16 +124,18 @@ namespace skyline::service::hosbinder {
auto nvmap{driver->nvMap.lock()}; auto nvmap{driver->nvMap.lock()};
if (gbpBuffer.nvmapHandle) { if (gbpBuffer.nvmapHandle) {
nvBuffer = nvmap->handleTable.at(gbpBuffer.nvmapHandle); nvBuffer = nvmap->GetObject(gbpBuffer.nvmapHandle);
} else { } else {
for (const auto &object : nvmap->handleTable) { std::shared_lock nvmapLock(nvmap->mapMutex);
if (object.second->id == gbpBuffer.nvmapId) { for (const auto &object : nvmap->maps) {
nvBuffer = object.second; if (object->id == gbpBuffer.nvmapId) {
nvBuffer = object;
break; break;
} }
} }
if (!nvBuffer) if (!nvBuffer)
throw exception("A QueueBuffer request has an invalid NVMap Handle ({}) and ID ({})", gbpBuffer.nvmapHandle, gbpBuffer.nvmapId); throw exception("A QueueBuffer request has an invalid NVMap Handle ({}) and ID ({})", gbpBuffer.nvmapHandle, gbpBuffer.nvmapId);
nvmapLock.unlock();
} }
gpu::texture::Format format; 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{ static frz::unordered_map<frz::string, DisplayId, 5> DisplayTypeMap{
{"Default", DisplayId::Default}, {"Default", DisplayId::Default},
{"External", DisplayId::External}, {"External", DisplayId::External},

View File

@ -81,7 +81,7 @@ namespace skyline::service::nvdrv::device {
try { try {
auto driver{nvdrv::driver.lock()}; auto driver{nvdrv::driver.lock()};
auto nvmap{driver->nvMap.lock()}; auto nvmap{driver->nvMap.lock()};
auto mapping{nvmap->handleTable.at(data.nvmapHandle)}; auto mapping{nvmap->GetObject(data.nvmapHandle)};
if (data.flags.remap) { if (data.flags.remap) {
auto region{regionMap.upper_bound(data.offset)}; auto region{regionMap.upper_bound(data.offset)};
@ -180,7 +180,7 @@ namespace skyline::service::nvdrv::device {
try { try {
auto driver{nvdrv::driver.lock()}; auto driver{nvdrv::driver.lock()};
auto nvmap{driver->nvMap.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}; u64 mapAddress{static_cast<u64>(entry.gpuOffset) << MinAlignmentShift};
u8* mapPointer{mapping->pointer + (static_cast<u64>(entry.mapOffset) << 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 u32 handle; // Out
} &data = buffer.as<Data>(); } &data = buffer.as<Data>();
handleTable[handleIndex] = std::make_shared<NvMapObject>(idIndex++, data.size); std::unique_lock lock(mapMutex);
data.handle = handleIndex++; 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); state.logger->Debug("Size: 0x{:X} -> Handle: 0x{:X}", data.size, data.handle);
return NvStatus::Success; return NvStatus::Success;
@ -27,9 +28,10 @@ namespace skyline::service::nvdrv::device {
u32 handle; // Out u32 handle; // Out
} &data = buffer.as<Data>(); } &data = buffer.as<Data>();
for (const auto &object : handleTable) { std::shared_lock lock(mapMutex);
if (object.second->id == data.id) { for (auto it{maps.begin()}; it < maps.end(); it++) {
data.handle = object.first; if ((*it)->id == data.id) {
data.handle = (it - maps.begin()) + 1;
state.logger->Debug("ID: 0x{:X} -> Handle: 0x{:X}", data.id, data.handle); state.logger->Debug("ID: 0x{:X} -> Handle: 0x{:X}", data.id, data.handle);
return NvStatus::Success; return NvStatus::Success;
} }
@ -47,11 +49,11 @@ namespace skyline::service::nvdrv::device {
u32 align; // In u32 align; // In
u8 kind; // In u8 kind; // In
u8 _pad0_[7]; u8 _pad0_[7];
u8* pointer; // InOut u8 *pointer; // InOut
} &data = buffer.as<Data>(); } &data = buffer.as<Data>();
try { try {
auto &object{handleTable.at(data.handle)}; auto object{GetObject(data.handle)};
object->heapMask = data.heapMask; object->heapMask = data.heapMask;
object->flags = data.flags; object->flags = data.flags;
object->align = data.align; object->align = data.align;
@ -71,13 +73,14 @@ namespace skyline::service::nvdrv::device {
struct Data { struct Data {
u32 handle; // In u32 handle; // In
u32 _pad0_; u32 _pad0_;
u8* pointer; // Out u8 *pointer; // Out
u32 size; // Out u32 size; // Out
u32 flags; // Out u32 flags; // Out
} &data = buffer.as<Data>(); } &data = buffer.as<Data>();
std::unique_lock lock(mapMutex);
try { try {
const auto &object{handleTable.at(data.handle)}; auto &object{maps.at(data.handle)};
if (object.use_count() > 1) { if (object.use_count() > 1) {
data.pointer = object->pointer; data.pointer = object->pointer;
data.flags = 0x0; data.flags = 0x0;
@ -87,7 +90,7 @@ namespace skyline::service::nvdrv::device {
} }
data.size = object->size; 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); 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; return NvStatus::Success;
@ -115,7 +118,7 @@ namespace skyline::service::nvdrv::device {
} &data = buffer.as<Data>(); } &data = buffer.as<Data>();
try { try {
auto &object{handleTable.at(data.handle)}; auto object{GetObject(data.handle)};
switch (data.parameter) { switch (data.parameter) {
case Parameter::Size: case Parameter::Size:
@ -158,7 +161,7 @@ namespace skyline::service::nvdrv::device {
} &data = buffer.as<Data>(); } &data = buffer.as<Data>();
try { 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); state.logger->Debug("Handle: 0x{:X} -> ID: 0x{:X}", data.handle, data.id);
return NvStatus::Success; return NvStatus::Success;
} catch (const std::out_of_range &) { } catch (const std::out_of_range &) {

View File

@ -18,7 +18,7 @@ namespace skyline::service::nvdrv::device {
struct NvMapObject { struct NvMapObject {
u32 id; u32 id;
u32 size; u32 size;
u8* pointer{}; u8 *pointer{};
u32 flags{}; //!< The flag of the memory (0 = Read Only, 1 = Read-Write) u32 flags{}; //!< The flag of the memory (0 = Read Only, 1 = Read-Write)
u32 align{}; u32 align{};
u32 heapMask{}; //!< This is set during Alloc and returned during Param u32 heapMask{}; //!< This is set during Alloc and returned during Param
@ -32,12 +32,23 @@ namespace skyline::service::nvdrv::device {
NvMapObject(u32 id, u32 size); NvMapObject(u32 id, u32 size);
}; };
std::unordered_map<KHandle, std::shared_ptr<NvMapObject>> handleTable; //!< A mapping from a handle to it's corresponding NvMapObject std::shared_mutex mapMutex; //!< Synchronizes mutations and accesses of the mappings
KHandle handleIndex{1}; //!< This is used to keep track of the next handle to allocate std::vector<std::shared_ptr<NvMapObject>> maps;
u32 idIndex{1}; //!< This is used to keep track of the next ID to allocate u32 idIndex{1}; //!< This is used to keep track of the next ID to allocate
NvMap(const DeviceState &state); 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 * @brief Creates an NvMapObject and returns an handle to it
* @url https://switchbrew.org/wiki/NV_services#NVMAP_IOC_CREATE * @url https://switchbrew.org/wiki/NV_services#NVMAP_IOC_CREATE