mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-23 17:51:52 +01:00
JVM Auto-Attach + Fix Thread Exiting + Fix Thread Signal Handler
This commit is contained in:
parent
6f2cd41470
commit
657beea070
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
@ -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)
|
||||||
|
@ -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();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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:
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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 {};
|
||||||
}
|
}
|
||||||
|
@ -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},
|
||||||
|
@ -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)};
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
@ -51,7 +53,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)};
|
||||||
object->heapMask = data.heapMask;
|
object->heapMask = data.heapMask;
|
||||||
object->flags = data.flags;
|
object->flags = data.flags;
|
||||||
object->align = data.align;
|
object->align = data.align;
|
||||||
@ -76,8 +78,9 @@ namespace skyline::service::nvdrv::device {
|
|||||||
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 &) {
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user