From 90127740f0ef6fd1cc280df38971593c15ad7464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=97=B1=20PixelyIon?= Date: Wed, 7 Oct 2020 21:11:13 +0530 Subject: [PATCH] Rework NCE/KThread/KProcess + Remove Guest Process --- app/CMakeLists.txt | 1 - app/src/main/cpp/emu_jni.cpp | 2 + app/src/main/cpp/skyline/common.h | 31 ++-- app/src/main/cpp/skyline/input.cpp | 8 - app/src/main/cpp/skyline/input.h | 2 +- app/src/main/cpp/skyline/kernel/ipc.cpp | 8 +- app/src/main/cpp/skyline/kernel/memory.cpp | 46 ++++-- app/src/main/cpp/skyline/kernel/memory.h | 9 +- app/src/main/cpp/skyline/kernel/svc.cpp | 72 +++++---- .../skyline/kernel/types/KPrivateMemory.cpp | 20 ++- .../cpp/skyline/kernel/types/KProcess.cpp | 116 ++++--------- .../main/cpp/skyline/kernel/types/KProcess.h | 153 +++++++----------- .../skyline/kernel/types/KSharedMemory.cpp | 8 +- .../main/cpp/skyline/kernel/types/KThread.cpp | 138 ++++++++++++++-- .../main/cpp/skyline/kernel/types/KThread.h | 110 ++++++------- app/src/main/cpp/skyline/loader/loader.cpp | 16 +- app/src/main/cpp/skyline/loader/loader.h | 7 - app/src/main/cpp/skyline/loader/nca.cpp | 7 +- app/src/main/cpp/skyline/loader/nro.cpp | 6 +- app/src/main/cpp/skyline/loader/nso.cpp | 7 +- app/src/main/cpp/skyline/nce.cpp | 63 ++------ app/src/main/cpp/skyline/nce.h | 36 +---- app/src/main/cpp/skyline/nce/guest.S | 1 - app/src/main/cpp/skyline/nce/guest.cpp | 91 +---------- app/src/main/cpp/skyline/nce/guest.h | 6 - app/src/main/cpp/skyline/nce/guest_common.h | 35 +--- app/src/main/cpp/skyline/os.cpp | 40 ++--- app/src/main/cpp/skyline/os.h | 2 - .../am/controller/IWindowController.cpp | 2 +- 29 files changed, 434 insertions(+), 609 deletions(-) delete mode 100644 app/src/main/cpp/skyline/input.cpp diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index e184244b..8d2467af 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -50,7 +50,6 @@ add_library(skyline SHARED ${source_DIR}/skyline/gpu/syncpoint.cpp ${source_DIR}/skyline/gpu/texture.cpp ${source_DIR}/skyline/gpu/engines/maxwell_3d.cpp - ${source_DIR}/skyline/input.cpp ${source_DIR}/skyline/input/npad.cpp ${source_DIR}/skyline/input/npad_device.cpp ${source_DIR}/skyline/input/touch.cpp diff --git a/app/src/main/cpp/emu_jni.cpp b/app/src/main/cpp/emu_jni.cpp index e992806c..da6e83a1 100644 --- a/app/src/main/cpp/emu_jni.cpp +++ b/app/src/main/cpp/emu_jni.cpp @@ -28,12 +28,14 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication( fps = 0; frametime = 0; + /* std::signal(SIGTERM, signalHandler); std::signal(SIGSEGV, signalHandler); std::signal(SIGINT, signalHandler); std::signal(SIGILL, signalHandler); std::signal(SIGABRT, signalHandler); std::signal(SIGFPE, signalHandler); + */ setpriority(PRIO_PROCESS, static_cast(gettid()), -8); // Set the priority of this process to the highest value diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h index b6fb71f7..8c380117 100644 --- a/app/src/main/cpp/skyline/common.h +++ b/app/src/main/cpp/skyline/common.h @@ -58,7 +58,7 @@ namespace skyline { namespace constant { // Memory constexpr u64 BaseAddress{0x8000000}; //!< The address space base - constexpr u64 DefStackSize{0x1E8480}; //!< The default amount of stack: 2 MB + constexpr u64 DefaultStackSize{0x1E8480}; //!< The default amount of stack: 2 MB // Display constexpr u16 HandheldResolutionW{1280}; //!< The width component of the handheld resolution constexpr u16 HandheldResolutionH{720}; //!< The height component of the handheld resolution @@ -71,16 +71,18 @@ namespace skyline { namespace util { /** * @brief A way to implicitly cast all typed pointers to void pointers, this is used for libfmt as it requires wrapping non-void pointers with fmt::ptr + * @note There's the exception of signed char pointers as they represent C Strings * @note This does not cover std::shared_ptr or std::unique_ptr and those will have to be explicitly passed through fmt::ptr */ - template - constexpr T VoidCast(T item) { - return item; - } - - template - constexpr const void* VoidCast(T* ptr) { - return ptr; + template + constexpr auto FmtCast(T object) { + if constexpr (std::is_pointer::value) + if constexpr (std::is_same::type>::type>::value) + return reinterpret_cast::type>(object); + else + return reinterpret_cast(object); + else + return object; } } @@ -94,7 +96,7 @@ namespace skyline { * @param args The arguments based on format_str */ template - inline exception(const S &formatStr, Args &&... args) : runtime_error(fmt::format(formatStr, util::VoidCast(args)...)) {} + inline exception(const S &formatStr, Args &&... args) : runtime_error(fmt::format(formatStr, util::FmtCast(args)...)) {} }; namespace util { @@ -444,7 +446,7 @@ namespace skyline { template inline void Error(const S &formatStr, Args &&... args) { if (LogLevel::Error <= configLevel) { - Write(LogLevel::Error, fmt::format(formatStr, util::VoidCast(args)...)); + Write(LogLevel::Error, fmt::format(formatStr, util::FmtCast(args)...)); } } @@ -456,7 +458,7 @@ namespace skyline { template inline void Warn(const S &formatStr, Args &&... args) { if (LogLevel::Warn <= configLevel) { - Write(LogLevel::Warn, fmt::format(formatStr, util::VoidCast(args)...)); + Write(LogLevel::Warn, fmt::format(formatStr, util::FmtCast(args)...)); } } @@ -468,7 +470,7 @@ namespace skyline { template inline void Info(const S &formatStr, Args &&... args) { if (LogLevel::Info <= configLevel) { - Write(LogLevel::Info, fmt::format(formatStr, util::VoidCast(args)...)); + Write(LogLevel::Info, fmt::format(formatStr, util::FmtCast(args)...)); } } @@ -480,7 +482,7 @@ namespace skyline { template inline void Debug(const S &formatStr, Args &&... args) { if (LogLevel::Debug <= configLevel) { - Write(LogLevel::Debug, fmt::format(formatStr, util::VoidCast(args)...)); + Write(LogLevel::Debug, fmt::format(formatStr, util::FmtCast(args)...)); } } }; @@ -527,7 +529,6 @@ namespace skyline { void List(const std::shared_ptr &logger); }; - class NCE; class JvmManager; namespace gpu { class GPU; diff --git a/app/src/main/cpp/skyline/input.cpp b/app/src/main/cpp/skyline/input.cpp deleted file mode 100644 index 94e454c7..00000000 --- a/app/src/main/cpp/skyline/input.cpp +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) - -#include "input.h" - -namespace skyline::input { - Input::Input(const DeviceState &state) : state(state), kHid(std::make_shared(state, sizeof(HidSharedMemory))), hid(reinterpret_cast(kHid->kernel.ptr)), npad(state, hid), touch(state, hid) {} -} diff --git a/app/src/main/cpp/skyline/input.h b/app/src/main/cpp/skyline/input.h index bf3f94dc..382768c9 100644 --- a/app/src/main/cpp/skyline/input.h +++ b/app/src/main/cpp/skyline/input.h @@ -24,6 +24,6 @@ namespace skyline::input { NpadManager npad; TouchManager touch; - Input(const DeviceState &state); + Input(const DeviceState &state) : state(state), kHid(std::make_shared(state, sizeof(HidSharedMemory))), hid(reinterpret_cast(kHid->kernel.ptr)), npad(state, hid), touch(state, hid) {} }; } diff --git a/app/src/main/cpp/skyline/kernel/ipc.cpp b/app/src/main/cpp/skyline/kernel/ipc.cpp index 7d21b78e..c84d0125 100644 --- a/app/src/main/cpp/skyline/kernel/ipc.cpp +++ b/app/src/main/cpp/skyline/kernel/ipc.cpp @@ -6,7 +6,7 @@ namespace skyline::kernel::ipc { IpcRequest::IpcRequest(bool isDomain, const DeviceState &state) : isDomain(isDomain) { - auto tls{state.thread->tls}; + auto tls{state.ctx->tpidrroEl0}; u8 *pointer{tls}; header = reinterpret_cast(pointer); @@ -64,7 +64,7 @@ namespace skyline::kernel::ipc { pointer += sizeof(BufferDescriptorABW); } - auto offset{reinterpret_cast(pointer) - reinterpret_cast(tls)}; // We calculate the relative offset as the absolute one might differ + auto offset{pointer - tls}; // We calculate the relative offset as the absolute one might differ auto padding{util::AlignUp(offset, constant::IpcPaddingSum) - offset}; // Calculate the amount of padding at the front pointer += padding; @@ -129,7 +129,7 @@ namespace skyline::kernel::ipc { IpcResponse::IpcResponse(const DeviceState &state) : state(state) {} void IpcResponse::WriteResponse(bool isDomain) { - auto tls{state.thread->tls}; + auto tls{state.ctx->tpidrroEl0}; u8 *pointer{tls}; memset(tls, 0, constant::TlsIpcSize); @@ -156,7 +156,7 @@ namespace skyline::kernel::ipc { } } - auto offset{reinterpret_cast(pointer) - reinterpret_cast(tls)}; // We calculate the relative offset as the absolute one might differ + auto offset{pointer - tls}; // We calculate the relative offset as the absolute one might differ auto padding{util::AlignUp(offset, constant::IpcPaddingSum) - offset}; // Calculate the amount of padding at the front pointer += padding; diff --git a/app/src/main/cpp/skyline/kernel/memory.cpp b/app/src/main/cpp/skyline/kernel/memory.cpp index aa5836a4..9e4c40c6 100644 --- a/app/src/main/cpp/skyline/kernel/memory.cpp +++ b/app/src/main/cpp/skyline/kernel/memory.cpp @@ -7,8 +7,7 @@ namespace skyline::kernel { MemoryManager::MemoryManager(const DeviceState &state) : state(state) {} - void MemoryManager::InitializeRegions(u8* codeStart, u64 size, memory::AddressSpaceType type) { - u64 address{reinterpret_cast(codeStart)}; + void MemoryManager::InitializeVmm(memory::AddressSpaceType type) { switch (type) { case memory::AddressSpaceType::AddressSpace32Bit: throw exception("32-bit address spaces are not supported"); @@ -18,6 +17,32 @@ namespace skyline::kernel { addressSpace.size = 1UL << 36; base.address = constant::BaseAddress; base.size = 0xFF8000000; + break; + } + + case memory::AddressSpaceType::AddressSpace39Bit: { + addressSpace.address = 0; + addressSpace.size = 1UL << 39; + base.address = constant::BaseAddress; + base.size = 0x7FF8000000; + break; + } + + default: + throw exception("VMM initialization with unknown address space"); + } + + chunks = {ChunkDescriptor{ + .ptr = reinterpret_cast(base.address), + .size = base.size, + .state = memory::states::Unmapped, + }}; + } + + void MemoryManager::InitializeRegions(u8 *codeStart, u64 size) { + u64 address{reinterpret_cast(codeStart)}; + switch (addressSpace.size) { + case 1UL << 36: { code.address = base.address; code.size = 0x78000000; if (code.address > address || (code.size - (address - code.address)) < size) @@ -33,11 +58,7 @@ namespace skyline::kernel { break; } - case memory::AddressSpaceType::AddressSpace39Bit: { - addressSpace.address = 0; - addressSpace.size = 1UL << 39; - base.address = constant::BaseAddress; - base.size = 0x7FF8000000; + case 1UL << 39: { code.address = util::AlignDown(address, 0x200000); code.size = util::AlignUp(address + size, 0x200000) - code.address; alias.address = code.address + code.size; @@ -50,13 +71,10 @@ namespace skyline::kernel { tlsIo.size = 0x1000000000; break; } - } - chunks = {ChunkDescriptor{ - .ptr = reinterpret_cast(base.address), - .size = base.size, - .state = memory::states::Unmapped, - }}; + default: + throw exception("Regions initialized without VMM initialization"); + } state.logger->Debug("Region Map:\nCode Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nAlias Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nHeap Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nStack Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nTLS/IO Region: 0x{:X} - 0x{:X} (Size: 0x{:X})", code.address, code.address + code.size, code.size, alias.address, alias.address + alias.size, alias.size, heap.address, heap .address + heap.size, heap.size, stack.address, stack.address + stack.size, stack.size, tlsIo.address, tlsIo.address + tlsIo.size, tlsIo.size); @@ -95,7 +113,7 @@ namespace skyline::kernel { } } - std::optional MemoryManager::Get(void* ptr) { + std::optional MemoryManager::Get(void *ptr) { std::shared_lock lock(mutex); auto chunk{std::upper_bound(chunks.begin(), chunks.end(), reinterpret_cast(ptr), [](const u8 *ptr, const ChunkDescriptor &chunk) -> bool { return ptr < chunk.ptr; })}; diff --git a/app/src/main/cpp/skyline/kernel/memory.h b/app/src/main/cpp/skyline/kernel/memory.h index a08dec05..0ca0738b 100644 --- a/app/src/main/cpp/skyline/kernel/memory.h +++ b/app/src/main/cpp/skyline/kernel/memory.h @@ -170,7 +170,7 @@ namespace skyline { } }; - enum class AddressSpaceType { + enum class AddressSpaceType : u8 { AddressSpace32Bit, //!< 32-bit address space used by 32-bit applications AddressSpace36Bit, //!< 36-bit address space used by 64-bit applications before 2.0.0 AddressSpace39Bit, //!< 39-bit address space used by 64-bit applications after 2.0.0 @@ -218,16 +218,17 @@ namespace skyline { MemoryManager(const DeviceState &state); /** - * @brief Initializes all of the regions in the address space + * @note This should be called before any mappings in the VMM or calls to InitalizeRegions are done */ - void InitializeRegions(u8* codeStart, u64 size, memory::AddressSpaceType type); + void InitializeVmm(memory::AddressSpaceType type); + + void InitializeRegions(u8* codeStart, u64 size); void InsertChunk(const ChunkDescriptor &chunk); std::optional Get(void* ptr); /** - * @brief The total amount of space in bytes occupied by all memory mappings * @return The cumulative size of all memory mappings in bytes */ size_t GetProgramSize(); diff --git a/app/src/main/cpp/skyline/kernel/svc.cpp b/app/src/main/cpp/skyline/kernel/svc.cpp index b26e86d0..a6ab56ee 100644 --- a/app/src/main/cpp/skyline/kernel/svc.cpp +++ b/app/src/main/cpp/skyline/kernel/svc.cpp @@ -52,7 +52,7 @@ namespace skyline::kernel::svc { return; } - auto chunk{state.os->memory.Get(pointer)}; + auto chunk{state.process->memory.Get(pointer)}; if (!chunk) { state.ctx->registers.w0 = result::InvalidAddress; state.logger->Warn("svcSetMemoryAttribute: Cannot find memory region: 0x{:X}", pointer); @@ -69,7 +69,7 @@ namespace skyline::kernel::svc { newChunk.ptr = pointer; newChunk.size = size; newChunk.attributes.isUncached = value.isUncached; - state.os->memory.InsertChunk(newChunk); + state.process->memory.InsertChunk(newChunk); state.logger->Debug("svcSetMemoryAttribute: Set caching to {} at 0x{:X} for 0x{:X} bytes", bool(value.isUncached), pointer, size); state.ctx->registers.w0 = Result{}; @@ -92,14 +92,14 @@ namespace skyline::kernel::svc { return; } - auto stack{state.os->memory.stack}; + auto stack{state.process->memory.stack}; if (!stack.IsInside(destination)) { state.ctx->registers.w0 = result::InvalidMemoryRegion; state.logger->Warn("svcMapMemory: Destination not within stack region: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size); return; } - auto chunk{state.os->memory.Get(source)}; + auto chunk{state.process->memory.Get(source)}; if (!chunk) { state.ctx->registers.w0 = result::InvalidAddress; state.logger->Warn("svcMapMemory: Source has no descriptor: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size); @@ -140,15 +140,15 @@ namespace skyline::kernel::svc { return; } - auto stack{state.os->memory.stack}; + auto stack{state.process->memory.stack}; if (!stack.IsInside(source)) { state.ctx->registers.w0 = result::InvalidMemoryRegion; state.logger->Warn("svcUnmapMemory: Source not within stack region: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size); return; } - auto sourceChunk{state.os->memory.Get(source)}; - auto destChunk{state.os->memory.Get(destination)}; + auto sourceChunk{state.process->memory.Get(source)}; + auto destChunk{state.process->memory.Get(destination)}; if (!sourceChunk || !destChunk) { state.ctx->registers.w0 = result::InvalidAddress; state.logger->Warn("svcUnmapMemory: Addresses have no descriptor: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size); @@ -183,7 +183,7 @@ namespace skyline::kernel::svc { memory::MemoryInfo memInfo{}; auto pointer{reinterpret_cast(state.ctx->registers.x2)}; - auto chunk{state.os->memory.Get(pointer)}; + auto chunk{state.process->memory.Get(pointer)}; if (chunk) { memInfo = { @@ -198,7 +198,7 @@ namespace skyline::kernel::svc { state.logger->Debug("svcQueryMemory: Address: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Is Uncached: {}, Permissions: {}{}{}", memInfo.address, memInfo.size, memInfo.type, bool(chunk->attributes.isUncached), chunk->permission.r ? 'R' : '-', chunk->permission.w ? 'W' : '-', chunk->permission.x ? 'X' : '-'); } else { - auto addressSpaceEnd{reinterpret_cast(state.os->memory.addressSpace.address + state.os->memory.addressSpace.size)}; + auto addressSpaceEnd{reinterpret_cast(state.process->memory.addressSpace.address + state.process->memory.addressSpace.size)}; memInfo = { .address = addressSpaceEnd, @@ -215,24 +215,28 @@ namespace skyline::kernel::svc { } void ExitProcess(DeviceState &state) { - state.logger->Debug("svcExitProcess: Exiting current process: {}", state.process->pid); - state.os->KillThread(state.process->pid); + state.logger->Debug("svcExitProcess: Exiting process"); + //state.os->KillThread(state.process->pid); } void CreateThread(DeviceState &state) { - auto entryAddress{state.ctx->registers.x1}; + auto entry{reinterpret_cast(state.ctx->registers.x1)}; auto entryArgument{state.ctx->registers.x2}; - auto stackTop{state.ctx->registers.x3}; + auto stackTop{reinterpret_cast(state.ctx->registers.x3)}; auto priority{static_cast(state.ctx->registers.w4)}; - if (!state.thread->switchPriority.Valid(priority)) { + if (!constant::HosPriority.Valid(priority)) { state.ctx->registers.w0 = result::InvalidAddress; state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority); return; } - auto thread{state.process->CreateThread(entryAddress, 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: {}, TID: {})", thread->handle, entryAddress, entryArgument, stackTop, priority, thread->tid); + auto stack{state.process->GetMemoryObject(stackTop)}; + 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(stack->item))}; + 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->registers.w1 = thread->handle; state.ctx->registers.w0 = Result{}; @@ -242,7 +246,7 @@ namespace skyline::kernel::svc { auto handle{state.ctx->registers.w0}; try { auto thread{state.process->GetHandle(handle)}; - state.logger->Debug("svcStartThread: Starting thread: 0x{:X}, PID: {}", handle, thread->tid); + state.logger->Debug("svcStartThread: Starting thread: 0x{:X}, PID: {}", handle, thread->id); thread->Start(); state.ctx->registers.w0 = Result{}; } catch (const std::exception &) { @@ -252,8 +256,8 @@ namespace skyline::kernel::svc { } void ExitThread(DeviceState &state) { - state.logger->Debug("svcExitThread: Exiting current thread: {}", state.thread->tid); - state.os->KillThread(state.thread->tid); + state.logger->Debug("svcExitThread: Exiting current thread: {}", state.thread->id); + state.os->KillThread(state.thread->id); } void SleepThread(DeviceState &state) { @@ -389,7 +393,7 @@ namespace skyline::kernel::svc { void ResetSignal(DeviceState &state) { auto handle{state.ctx->registers.w0}; try { - auto &object{state.process->handles.at(handle)}; + auto object{state.process->GetHandle(handle)}; switch (object->objectType) { case type::KType::KEvent: std::static_pointer_cast(object)->ResetSignal(); @@ -431,7 +435,7 @@ namespace skyline::kernel::svc { for (const auto &handle : waitHandles) { handleStr += fmt::format("* 0x{:X}\n", handle); - auto object{state.process->handles.at(handle)}; + auto object{state.process->GetHandle(handle)}; switch (object->objectType) { case type::KType::KProcess: case type::KType::KThread: @@ -612,9 +616,9 @@ namespace skyline::kernel::svc { pid_t pid{}; if (handle != threadSelf) - pid = state.process->GetHandle(handle)->tid; + pid = state.process->GetHandle(handle)->id; else - pid = state.thread->tid; + pid = state.thread->id; state.logger->Debug("svcGetThreadId: Handle: 0x{:X}, PID: {}", handle, pid); @@ -650,19 +654,19 @@ namespace skyline::kernel::svc { break; case constant::infoState::AliasRegionBaseAddr: - out = state.os->memory.alias.address; + out = state.process->memory.alias.address; break; case constant::infoState::AliasRegionSize: - out = state.os->memory.alias.size; + out = state.process->memory.alias.size; break; case constant::infoState::HeapRegionBaseAddr: - out = state.os->memory.heap.address; + out = state.process->memory.heap.address; break; case constant::infoState::HeapRegionSize: - out = state.os->memory.heap.size; + out = state.process->memory.heap.size; break; case constant::infoState::TotalMemoryAvailable: @@ -670,23 +674,23 @@ namespace skyline::kernel::svc { break; case constant::infoState::TotalMemoryUsage: - out = state.process->heap->size + constant::DefStackSize + state.os->memory.GetProgramSize(); + out = state.process->heap->size + constant::DefaultStackSize + state.process->memory.GetProgramSize(); break; case constant::infoState::AddressSpaceBaseAddr: - out = state.os->memory.base.address; + out = state.process->memory.base.address; break; case constant::infoState::AddressSpaceSize: - out = state.os->memory.base.size; + out = state.process->memory.base.size; break; case constant::infoState::StackRegionBaseAddr: - out = state.os->memory.stack.address; + out = state.process->memory.stack.address; break; case constant::infoState::StackRegionSize: - out = state.os->memory.stack.size; + out = state.process->memory.stack.size; break; case constant::infoState::PersonalMmHeapSize: @@ -694,7 +698,7 @@ namespace skyline::kernel::svc { break; case constant::infoState::PersonalMmHeapUsage: - out = state.process->heap->size + constant::DefStackSize; + out = state.process->heap->size + constant::DefaultStackSize; break; case constant::infoState::TotalMemoryAvailableWithoutMmHeap: @@ -702,7 +706,7 @@ namespace skyline::kernel::svc { break; case constant::infoState::TotalMemoryUsedWithoutMmHeap: - out = state.process->heap->size + constant::DefStackSize; // TODO: Same as above + out = state.process->heap->size + constant::DefaultStackSize; // TODO: Same as above break; case constant::infoState::UserExceptionContextAddr: diff --git a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp index e04fc7cc..76a69d04 100644 --- a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp @@ -14,18 +14,16 @@ namespace skyline::kernel::type { if (ptr && !util::PageAligned(ptr)) throw exception("KPrivateMemory was created with non-page-aligned address: 0x{:X}", ptr); - ptr = reinterpret_cast(mmap(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, ptr ? MAP_FIXED : 0, 0, 0)); - if (ptr == MAP_FAILED) - throw exception("An occurred while mapping private memory: {}", strerror(errno)); + this->ptr = reinterpret_cast(mmap(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, (ptr ? MAP_FIXED : 0) | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); + if (this->ptr == MAP_FAILED) + throw exception("An occurred while mapping private memory: {} with {} @ 0x{:X}", strerror(errno), ptr, size); - state.os->memory.InsertChunk(ChunkDescriptor{ - .ptr = ptr, + state.process->memory.InsertChunk(ChunkDescriptor{ + .ptr = this->ptr, .size = size, .permission = permission, .state = memState, }); - - this->ptr = ptr; } void KPrivateMemory::Resize(size_t nSize) { @@ -34,13 +32,13 @@ namespace skyline::kernel::type { throw exception("An occurred while resizing private memory: {}", strerror(errno)); if (nSize < size) { - state.os->memory.InsertChunk(ChunkDescriptor{ + state.process->memory.InsertChunk(ChunkDescriptor{ .ptr = ptr + nSize, .size = size - nSize, .state = memory::states::Unmapped, }); } else if (size < nSize) { - state.os->memory.InsertChunk(ChunkDescriptor{ + state.process->memory.InsertChunk(ChunkDescriptor{ .ptr = ptr + size, .size = nSize - size, .permission = permission, @@ -59,7 +57,7 @@ namespace skyline::kernel::type { if (memState.value == memory::states::CodeStatic.value && permission.w) memState = memory::states::CodeMutable; - state.os->memory.InsertChunk(ChunkDescriptor{ + state.process->memory.InsertChunk(ChunkDescriptor{ .ptr = ptr, .size = size, .permission = permission, @@ -69,7 +67,7 @@ namespace skyline::kernel::type { KPrivateMemory::~KPrivateMemory() { munmap(ptr, size); - state.os->memory.InsertChunk(ChunkDescriptor{ + state.process->memory.InsertChunk(ChunkDescriptor{ .ptr = ptr, .size = size, .state = memory::states::Unmapped, diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp index ba7e5a45..8ba698ff 100644 --- a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp @@ -1,110 +1,64 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) -#include -#include -#include -#include -#include #include #include #include "KProcess.h" namespace skyline::kernel::type { - KProcess::TlsPage::TlsPage(u8* ptr) : ptr(ptr) {} + KProcess::WaitStatus::WaitStatus(i8 priority, KHandle handle) : priority(priority), handle(handle) {} - u8* KProcess::TlsPage::ReserveSlot() { + KProcess::WaitStatus::WaitStatus(i8 priority, KHandle handle, u32 *mutex) : priority(priority), handle(handle), mutex(mutex) {} + + KProcess::TlsPage::TlsPage(const std::shared_ptr &memory) : memory(memory) {} + + u8 *KProcess::TlsPage::ReserveSlot() { if (Full()) - throw exception("Trying to get TLS slot from full page"); - - slot[index] = true; - return Get(index++); // ++ on right will cause increment after evaluation of expression + throw exception("Trying to reserve TLS slot in full page"); + return Get(index++); } - u8* KProcess::TlsPage::Get(u8 slotNo) { - if (slotNo >= constant::TlsSlots) + u8 *KProcess::TlsPage::Get(u8 index) { + if (index >= constant::TlsSlots) throw exception("TLS slot is out of range"); - - return ptr + (constant::TlsSlotSize * slotNo); + return memory->ptr + (constant::TlsSlotSize * index); } bool KProcess::TlsPage::Full() { - return slot[constant::TlsSlots - 1]; + return index == constant::TlsSlots; } - u8* KProcess::GetTlsSlot() { + KProcess::KProcess(const DeviceState &state) : memory(state), KSyncObject(state, KType::KProcess) {} + + void KProcess::InitializeHeap() { + constexpr size_t DefaultHeapSize{0x200000}; + heap.make_shared(state, reinterpret_cast(state.process->memory.heap.address), DefaultHeapSize, memory::Permission{true, true, false}, memory::states::Heap); + } + + u8 *KProcess::AllocateTlsSlot() { for (auto &tlsPage: tlsPages) if (!tlsPage->Full()) return tlsPage->ReserveSlot(); - u8* ptr; - if (tlsPages.empty()) { - auto region{state.os->memory.tlsIo}; - ptr = reinterpret_cast(region.size ? region.address : 0); - } else { - ptr = (*(tlsPages.end() - 1))->ptr + PAGE_SIZE; - } - - auto tlsMem{NewHandle(ptr, PAGE_SIZE, memory::Permission(true, true, false), memory::states::ThreadLocal).item}; - tlsPages.push_back(std::make_shared(tlsMem->ptr)); - - auto &tlsPage{tlsPages.back()}; - if (tlsPages.empty()) - tlsPage->ReserveSlot(); // User-mode exception handling + u8 *ptr = tlsPages.empty() ? reinterpret_cast(state.process->memory.tlsIo.address) : ((*(tlsPages.end() - 1))->memory->ptr + PAGE_SIZE); + auto tlsPage{std::make_shared(std::make_shared(state, ptr, PAGE_SIZE, memory::Permission(true, true, false), memory::states::ThreadLocal))}; + tlsPages.push_back(tlsPage); + tlsPage->ReserveSlot(); // User-mode exception handling return tlsPage->ReserveSlot(); } - void KProcess::InitializeMemory() { - constexpr size_t DefHeapSize{0x200000}; // The default amount of heap - heap = NewHandle(reinterpret_cast(state.os->memory.heap.address), DefHeapSize, memory::Permission{true, true, false}, memory::states::Heap).item; - threads[pid]->tls = GetTlsSlot(); - } - - KProcess::KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, std::shared_ptr &stack, std::shared_ptr &tlsMemory) : pid(pid), stack(stack), KSyncObject(state, KType::KProcess) { - constexpr u8 DefaultPriority{44}; // The default priority of a process - - auto thread{NewHandle(pid, entryPoint, 0, reinterpret_cast(stack->guest.ptr + stack->guest.size), nullptr, DefaultPriority, this, tlsMemory).item}; - threads[pid] = thread; - state.nce->WaitThreadInit(thread); - - memFd = open(fmt::format("/proc/{}/mem", pid).c_str(), O_RDWR | O_CLOEXEC); - if (memFd == -1) - throw exception("Cannot open file descriptor to /proc/{}/mem, \"{}\"", pid, strerror(errno)); - } - - KProcess::~KProcess() { - close(memFd); - status = Status::Exiting; - } - - std::shared_ptr KProcess::CreateThread(u64 entryPoint, u64 entryArg, u64 stackTop, i8 priority) { - auto size{(sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1)}; - auto tlsMem{std::make_shared(state, size, memory::states::Reserved)}; - - Registers fregs{ - .x0 = CLONE_THREAD | CLONE_SIGHAND | CLONE_PTRACE | CLONE_FS | CLONE_VM | CLONE_FILES | CLONE_IO, - .x1 = stackTop, - .x3 = reinterpret_cast(tlsMem->Map(nullptr, size, memory::Permission{true, true, false})), - .x8 = __NR_clone, - .x5 = reinterpret_cast(&guest::GuestEntry), - .x6 = entryPoint, - }; - - state.nce->ExecuteFunction(ThreadCall::Clone, fregs); - if (static_cast(fregs.x0) < 0) - throw exception("Cannot create thread: Address: 0x{:X}, Stack Top: 0x{:X}", entryPoint, stackTop); - - auto pid{static_cast(fregs.x0)}; - auto thread{NewHandle(pid, entryPoint, entryArg, stackTop, GetTlsSlot(), priority, this, tlsMem).item}; - threads[pid] = thread; - + std::shared_ptr KProcess::CreateThread(void *entry, u64 argument, i8 priority, const std::shared_ptr &stack) { + auto thread{NewHandle(this, threads.size(), entry, argument, priority, stack).item}; + threads.push_back(thread); return thread; } - std::optional> KProcess::GetMemoryObject(u8* ptr) { + std::optional> KProcess::GetMemoryObject(u8 *ptr) { + std::shared_lock lock(handleMutex); + for (KHandle index{}; index < handles.size(); index++) { - auto& object{handles[index]}; + auto &object{handles[index]}; switch (object->objectType) { case type::KType::KPrivateMemory: case type::KType::KSharedMemory: @@ -121,7 +75,7 @@ namespace skyline::kernel::type { return std::nullopt; } - bool KProcess::MutexLock(u32* mutex, KHandle owner) { + bool KProcess::MutexLock(u32 *mutex, KHandle owner) { std::unique_lock lock(mutexLock); auto &mtxWaiters{mutexes[reinterpret_cast(mutex)]}; @@ -159,7 +113,7 @@ namespace skyline::kernel::type { return true; } - bool KProcess::MutexUnlock(u32* mutex) { + bool KProcess::MutexUnlock(u32 *mutex) { std::unique_lock lock(mutexLock); auto &mtxWaiters{mutexes[reinterpret_cast(mutex)]}; @@ -186,7 +140,7 @@ namespace skyline::kernel::type { return true; } - bool KProcess::ConditionalVariableWait(void* conditional, u32* mutex, u64 timeout) { + bool KProcess::ConditionalVariableWait(void *conditional, u32 *mutex, u64 timeout) { std::unique_lock lock(conditionalLock); auto &condWaiters{conditionals[reinterpret_cast(conditional)]}; @@ -227,7 +181,7 @@ namespace skyline::kernel::type { return !timedOut; } - void KProcess::ConditionalVariableSignal(void* conditional, u64 amount) { + void KProcess::ConditionalVariableSignal(void *conditional, u64 amount) { std::unique_lock condLock(conditionalLock); auto &condWaiters{conditionals[reinterpret_cast(conditional)]}; diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.h b/app/src/main/cpp/skyline/kernel/types/KProcess.h index e2b0ef20..365eea09 100644 --- a/app/src/main/cpp/skyline/kernel/types/KProcess.h +++ b/app/src/main/cpp/skyline/kernel/types/KProcess.h @@ -4,8 +4,8 @@ #pragma once #include +#include #include "KThread.h" -#include "KPrivateMemory.h" #include "KTransferMemory.h" #include "KSession.h" #include "KEvent.h" @@ -20,11 +20,28 @@ namespace skyline { namespace kernel::type { /** - * @brief The KProcess class is responsible for holding the state of a process + * @brief KProcess manages process-global state such as memory, kernel handles allocated to the process and synchronization primitives */ class KProcess : public KSyncObject { private: - KHandle handleIndex{constant::BaseHandleIndex}; //!< The index of the handle that will be allocated next + std::vector> handles; + std::shared_mutex handleMutex; + + struct WaitStatus { + std::atomic_bool flag{false}; + i8 priority; + KHandle handle; + u32* mutex{}; + + WaitStatus(i8 priority, KHandle handle); + + WaitStatus(i8 priority, KHandle handle, u32* mutex); + }; + + std::unordered_map>> mutexes; //!< A map from a mutex's address to a vector of Mutex objects for threads waiting on it + std::unordered_map>> conditionals; //!< A map from a conditional variable's address to a vector of threads waiting on it + Mutex mutexLock; + Mutex conditionalLock; /** * @brief The status of a single TLS page (A page is 4096 bytes on ARMv8) @@ -33,44 +50,37 @@ namespace skyline { * @url https://switchbrew.org/wiki/Thread_Local_Storage */ struct TlsPage { - u8* ptr; u8 index{}; //!< The slots are assigned sequentially, this holds the index of the last TLS slot reserved - bool slot[constant::TlsSlots]{}; //!< An array of booleans denoting which TLS slots are reserved + std::shared_ptr memory; - TlsPage(u8* ptr); + TlsPage(const std::shared_ptr& memory); - /** - * @brief Reserves a single 0x200 byte TLS slot - * @return The address of the reserved slot - */ u8* ReserveSlot(); - /** - * @brief Returns the address of a particular slot - * @param slotNo The number of the slot to be returned - * @return The address of the specified slot - */ - u8* Get(u8 slotNo); + u8* Get(u8 index); - /** - * @brief Returns boolean on if the TLS page has free slots or not - * @return If the whole page is full or not - */ bool Full(); }; - /** - * @return The address of a free TLS slot - */ - u8* GetTlsSlot(); - - /** - * @brief Initializes heap and the initial TLS page - */ - void InitializeMemory(); - public: - friend OS; + MemoryManager memory; + std::shared_ptr heap; + std::vector> threads; + std::vector> tlsPages; + + KProcess(const DeviceState &state); + + /** + * @note This requires VMM regions to be initialized, it will map heap at an arbitrary location otherwise + */ + void InitializeHeap(); + + /** + * @return A 0x200 TLS slot allocated inside the TLS/IO region + */ + u8* AllocateTlsSlot(); + + std::shared_ptr CreateThread(void *entry, u64 argument = 0, i8 priority = 44, const std::shared_ptr &stack = nullptr); /** * @brief The output for functions that return created kernel objects @@ -82,96 +92,40 @@ namespace skyline { KHandle handle; //!< The handle of the object in the process }; - enum class Status { - Created, //!< The process was created but the main thread has not started yet - Started, //!< The process has been started - Exiting, //!< The process is exiting - } status = Status::Created; - - /** - * @brief Metadata on a thread waiting for mutexes or conditional variables - */ - struct WaitStatus { - std::atomic_bool flag{false}; //!< The underlying atomic flag of the thread - u8 priority; //!< The priority of the thread - KHandle handle; //!< The handle of the thread - u32* mutex{}; - - WaitStatus(u8 priority, KHandle handle) : priority(priority), handle(handle) {} - - WaitStatus(u8 priority, KHandle handle, u32* mutex) : priority(priority), handle(handle), mutex(mutex) {} - }; - - pid_t pid; //!< The PID of the process or TGID of the threads - int memFd; //!< The file descriptor to the memory of the process - std::vector> handles; //!< A vector of KObject which corresponds to the handle - std::unordered_map> threads; //!< A mapping from a PID to it's corresponding KThread object - std::unordered_map>> mutexes; //!< A map from a mutex's address to a vector of Mutex objects for threads waiting on it - std::unordered_map>> conditionals; //!< A map from a conditional variable's address to a vector of threads waiting on it - std::vector> tlsPages; //!< A vector of all allocated TLS pages - std::shared_ptr stack; //!< The shared memory used to hold the stack of the main thread - std::shared_ptr heap; //!< The kernel memory object backing the allocated heap - Mutex mutexLock; //!< Synchronizes all concurrent guest mutex operations - Mutex conditionalLock; //!< Synchronizes all concurrent guest conditional variable operations - - /** - * @param pid The PID of the main thread - * @param entryPoint The entry point of execution for the guest - * @param tlsMemory The KSharedMemory object for TLS memory allocated by the guest process - */ - KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, std::shared_ptr &stack, std::shared_ptr &tlsMemory); - - /** - * Close the file descriptor to the process's memory - */ - ~KProcess(); - - /** - * @brief Create a thread in this process - * @param entryPoint The address of the initial function - * @param entryArg An argument to the function - * @param stackTop The top of the stack - * @param priority The priority of the thread - * @return An instance of KThread class for the corresponding thread - */ - std::shared_ptr CreateThread(u64 entryPoint, u64 entryArg, u64 stackTop, i8 priority); - /** * @brief Creates a new handle to a KObject and adds it to the process handle_table * @tparam objectClass The class of the kernel object to create * @param args The arguments for the kernel object except handle, pid and state - * @return A shared pointer to the corresponding object */ template HandleOut NewHandle(objectArgs... args) { + std::unique_lock lock(handleMutex); + std::shared_ptr item; if constexpr (std::is_same()) - item = std::make_shared(state, handleIndex, args...); + item = std::make_shared(state, constant::BaseHandleIndex + handles.size(), args...); else item = std::make_shared(state, args...); handles.push_back(std::static_pointer_cast(item)); - return {item, handleIndex++}; + return {item, static_cast((constant::BaseHandleIndex + handles.size()) - 1)}; } /** * @brief Inserts an item into the process handle table - * @param item The item to insert * @return The handle of the corresponding item in the handle table */ template KHandle InsertItem(std::shared_ptr &item) { + std::unique_lock lock(handleMutex); + handles.push_back(std::static_pointer_cast(item)); - return handleIndex++; + return static_cast((constant::BaseHandleIndex + handles.size()) - 1); } - /** - * @brief Returns the underlying kernel object for a handle - * @tparam objectClass The class of the kernel object present in the handle - * @param handle The handle of the object - * @return A shared pointer to the object - */ - template + template std::shared_ptr GetHandle(KHandle handle) { + std::shared_lock lock(handleMutex); + KType objectType; if constexpr(std::is_same()) objectType = KType::KThread; @@ -202,6 +156,11 @@ namespace skyline { } } + template<> + std::shared_ptr GetHandle(KHandle handle) { + return handles.at(handle - constant::BaseHandleIndex); + } + /** * @brief Retrieves a kernel memory object that owns the specified address * @param address The address to look for diff --git a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp index da161b71..5c9f8689 100644 --- a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp @@ -26,12 +26,12 @@ namespace skyline::kernel::type { if (ptr && !util::PageAligned(ptr)) throw exception("KSharedMemory was mapped to a non-page-aligned address: 0x{:X}", ptr); - guest.ptr = reinterpret_cast(mmap(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | (ptr ? MAP_FIXED : 0), fd, 0)); + guest.ptr = reinterpret_cast(mmap(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | (ptr ? MAP_FIXED_NOREPLACE : 0), fd, 0)); if (guest.ptr == MAP_FAILED) throw exception("An error occurred while mapping shared memory in guest"); guest.size = size; - state.os->memory.InsertChunk(ChunkDescriptor{ + state.process->memory.InsertChunk(ChunkDescriptor{ .ptr = guest.ptr, .size = size, .permission = permission, @@ -51,7 +51,7 @@ namespace skyline::kernel::type { if (guest.ptr == MAP_FAILED) throw exception("An error occurred while updating shared memory's permissions in guest"); - state.os->memory.InsertChunk(ChunkDescriptor{ + state.process->memory.InsertChunk(ChunkDescriptor{ .ptr = ptr, .size = size, .permission = permission, @@ -66,7 +66,7 @@ namespace skyline::kernel::type { if (guest.Valid()) { munmap(guest.ptr, guest.size); - state.os->memory.InsertChunk(ChunkDescriptor{ + state.process->memory.InsertChunk(ChunkDescriptor{ .ptr = guest.ptr, .size = guest.size, .state = memory::states::Unmapped, diff --git a/app/src/main/cpp/skyline/kernel/types/KThread.cpp b/app/src/main/cpp/skyline/kernel/types/KThread.cpp index 84f8560d..daa2c9a1 100644 --- a/app/src/main/cpp/skyline/kernel/types/KThread.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KThread.cpp @@ -1,14 +1,15 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) +#include #include +#include #include -#include "KThread.h" +#include #include "KProcess.h" namespace skyline::kernel::type { - KThread::KThread(const DeviceState &state, KHandle handle, pid_t selfTid, u64 entryPoint, u64 entryArg, u64 stackTop, u8* tls, i8 priority, KProcess *parent, const std::shared_ptr &tlsMemory) : handle(handle), tid(selfTid), entryPoint(entryPoint), entryArg(entryArg), stackTop(stackTop), tls(tls), priority(priority), parent(parent), ctxMemory(tlsMemory), KSyncObject(state, - KType::KThread) { + KThread::KThread(const DeviceState &state, KHandle handle, KProcess *parent, size_t id, void *entry, u64 argument, i8 priority, const std::shared_ptr &stack) : handle(handle), parent(parent), id(id), entry(entry), entryArgument(argument), stack(stack), KSyncObject(state, KType::KThread) { UpdatePriority(priority); } @@ -16,30 +17,135 @@ namespace skyline::kernel::type { Kill(); } - void KThread::Start() { - if (status == Status::Created) { - if (tid == parent->pid) - parent->status = KProcess::Status::Started; - status = Status::Running; + void KThread::StartThread() { + pthread_setname_np(pthread_self(), fmt::format("HOS-{}", id).c_str()); - state.nce->StartThread(entryArg, handle, parent->threads.at(tid)); + ctx.sp = stack->ptr + stack->size; + if (!ctx.tpidrroEl0) + ctx.tpidrroEl0 = parent->AllocateTlsSlot(); + ctx.nce = state.nce.get(); + state.ctx = &ctx; + + struct sigaction sigact{ + .sa_sigaction = &NCE::SignalHandler, + .sa_flags = SA_SIGINFO, + }; + + //for (int signal : {SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV}) + // sigaction(signal, &sigact, nullptr); + + asm volatile( + "MSR TPIDR_EL0, %x0\n\t" // Set TLS to ThreadContext + "MOV X0, SP\n\t" + "STR X0, [%x0, #16]\n\t" // Store SP in ThreadContext + "MOV LR, %x1\n\t" // Store entry in Link Register so it is jumped to on return + "MOV X0, %x2\n\t" // Store the argument in X0 + "MOV X1, %x3\n\t" // Store the thread handle in X1, NCA applications require this + "MOV X2, XZR\n\t" + "MOV X3, XZR\n\t" + "MOV X4, XZR\n\t" + "MOV X5, XZR\n\t" + "MOV X6, XZR\n\t" + "MOV X7, XZR\n\t" + "MOV X8, XZR\n\t" + "MOV X9, XZR\n\t" + "MOV X10, XZR\n\t" + "MOV X11, XZR\n\t" + "MOV X12, XZR\n\t" + "MOV X13, XZR\n\t" + "MOV X14, XZR\n\t" + "MOV X15, XZR\n\t" + "MOV X16, XZR\n\t" + "MOV X17, XZR\n\t" + "MOV X18, XZR\n\t" + "MOV X19, XZR\n\t" + "MOV X20, XZR\n\t" + "MOV X21, XZR\n\t" + "MOV X22, XZR\n\t" + "MOV X23, XZR\n\t" + "MOV X24, XZR\n\t" + "MOV X25, XZR\n\t" + "MOV X26, XZR\n\t" + "MOV X27, XZR\n\t" + "MOV X28, XZR\n\t" + "MOV X29, XZR\n\t" + "MSR FPSR, XZR\n\t" + "MSR FPCR, XZR\n\t" + "DUP V0.16B, WZR\n\t" + "DUP V1.16B, WZR\n\t" + "DUP V2.16B, WZR\n\t" + "DUP V3.16B, WZR\n\t" + "DUP V4.16B, WZR\n\t" + "DUP V5.16B, WZR\n\t" + "DUP V6.16B, WZR\n\t" + "DUP V7.16B, WZR\n\t" + "DUP V8.16B, WZR\n\t" + "DUP V9.16B, WZR\n\t" + "DUP V10.16B, WZR\n\t" + "DUP V11.16B, WZR\n\t" + "DUP V12.16B, WZR\n\t" + "DUP V13.16B, WZR\n\t" + "DUP V14.16B, WZR\n\t" + "DUP V15.16B, WZR\n\t" + "DUP V16.16B, WZR\n\t" + "DUP V17.16B, WZR\n\t" + "DUP V18.16B, WZR\n\t" + "DUP V19.16B, WZR\n\t" + "DUP V20.16B, WZR\n\t" + "DUP V21.16B, WZR\n\t" + "DUP V22.16B, WZR\n\t" + "DUP V23.16B, WZR\n\t" + "DUP V24.16B, WZR\n\t" + "DUP V25.16B, WZR\n\t" + "DUP V26.16B, WZR\n\t" + "DUP V27.16B, WZR\n\t" + "DUP V28.16B, WZR\n\t" + "DUP V29.16B, WZR\n\t" + "DUP V30.16B, WZR\n\t" + "DUP V31.16B, WZR\n\t" + "RET" + : + : "r"(&ctx), "r"(entry), "r"(entryArgument), "r"(handle) + : "x0", "x1", "lr" + ); + + __builtin_unreachable(); + } + + void KThread::Start(bool self) { + if (!running) { + running = true; + + state.logger->Debug("Starting thread #{}", id); + + if (!stack) { + stack = stack.make_shared(state, reinterpret_cast(state.process->memory.stack.address), constant::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 + thread.emplace(&KThread::StartThread, this); } } void KThread::Kill() { - if (status != Status::Dead) { - status = Status::Dead; - Signal(); + if (running) { + running = false; - tgkill(parent->pid, tid, SIGTERM); + Signal(); + exit(0); + //tgkill(gettgid(), tid, SIGTERM); } } void KThread::UpdatePriority(i8 priority) { this->priority = priority; - auto priorityValue{androidPriority.Rescale(switchPriority, priority)}; + auto priorityValue{constant::AndroidPriority.Rescale(constant::HosPriority, priority)}; - if (setpriority(PRIO_PROCESS, static_cast(tid), priorityValue) == -1) - throw exception("Couldn't set process priority to {} for PID: {}", priorityValue, tid); + if (setpriority(PRIO_PROCESS, getpid(), priorityValue) == -1) + throw exception("Couldn't set thread priority to {} for #{}", priorityValue, id); } } diff --git a/app/src/main/cpp/skyline/kernel/types/KThread.h b/app/src/main/cpp/skyline/kernel/types/KThread.h index 7ba38747..47eea0e4 100644 --- a/app/src/main/cpp/skyline/kernel/types/KThread.h +++ b/app/src/main/cpp/skyline/kernel/types/KThread.h @@ -4,21 +4,11 @@ #pragma once #include "KSyncObject.h" +#include "KPrivateMemory.h" #include "KSharedMemory.h" -namespace skyline::kernel::type { - /** - * @brief KThread class is responsible for holding the state of a thread - */ - class KThread : public KSyncObject { - private: - KProcess *parent; //!< The parent process of this thread - u64 entryPoint; //!< The address to start execution at - u64 entryArg; //!< An argument to pass to the process on entry - - /** - * @brief A range of priorities for a corresponding system - */ +namespace skyline { + namespace kernel::type { struct Priority { i8 low; //!< The low range of priority i8 high; //!< The high range of priority @@ -28,7 +18,7 @@ namespace skyline::kernel::type { * @param value The priority value to rescale * @return The rescaled priority value according to this range */ - constexpr i8 Rescale(const Priority &priority, i8 value) { + constexpr i8 Rescale(const Priority &priority, i8 value) const { return static_cast(priority.low + ((static_cast(priority.high - priority.low) / static_cast(priority.low - priority.high)) * (static_cast(value) - priority.low))); } @@ -36,61 +26,61 @@ namespace skyline::kernel::type { * @param value The priority value to check for validity * @return If the supplied priority value is valid */ - constexpr bool Valid(i8 value) { + constexpr bool Valid(i8 value) const { return (value >= low) && (value <= high); } }; + } - public: - enum class Status { - Created, //!< The thread has been created but has not been started yet - Running, //!< The thread is running currently - Dead, //!< The thread is dead and not running - } status = Status::Created; - std::atomic cancelSync{false}; //!< This is to flag to a thread to cancel a synchronization call it currently is in - std::shared_ptr ctxMemory; //!< The KSharedMemory of the shared memory allocated by the guest process TLS - KHandle handle; // The handle of the object in the handle table - pid_t tid; //!< The Linux Thread ID of the current thread - u64 stackTop; //!< The top of the stack (Where it starts growing downwards from) - u8* tls; //!< The address of TLS (Thread Local Storage) slot assigned to the current thread - i8 priority; //!< The priority of a thread in Nintendo format - - Priority androidPriority{19, -8}; //!< The range of priorities for Android - Priority switchPriority{0, 63}; //!< The range of priorities for the Nintendo Switch + namespace constant { + constexpr i8 DefaultPriority{44}; // The default priority of an HOS process + constexpr kernel::type::Priority AndroidPriority{19, -8}; //!< The range of priorities for Android + constexpr kernel::type::Priority HosPriority{0, 63}; //!< The range of priorities for Horizon OS + } + namespace kernel::type { /** - * @param handle The handle of the current thread - * @param selfTid The TID of this thread - * @param entryPoint The address to start execution at - * @param entryArg An argument to pass to the process on entry - * @param stackTop The top of the stack - * @param tls The address of the TLS slot assigned - * @param priority The priority of the thread in Nintendo format - * @param parent The parent process of this thread - * @param tlsMemory The KSharedMemory object for TLS memory allocated by the guest process + * @brief KThread manages a single thread of execution which is responsible for running guest code and kernel code which is invoked by the guest */ - KThread(const DeviceState &state, KHandle handle, pid_t selfTid, u64 entryPoint, u64 entryArg, u64 stackTop, u8* tls, i8 priority, KProcess *parent, const std::shared_ptr &tlsMemory); + class KThread : public KSyncObject { + private: + KProcess *parent; + std::optional thread; //!< If this KThread is backed by a host thread then this'll hold it - /** - * @brief Kills the thread and deallocates the memory allocated for stack. - */ - ~KThread(); + void StartThread(); - /** - * @brief Starts this thread process - */ - void Start(); + public: + std::atomic running{false}; + std::atomic cancelSync{false}; //!< This is to flag to a thread to cancel a synchronization call it currently is in - /** - * @brief Kills the thread process - */ - void Kill(); + KHandle handle; + size_t id; //!< Index of thread in parent process's KThread vector - /** - * @brief Update the priority level for the process. - * @details Set the priority of the current thread to `priority` using setpriority [https://linux.die.net/man/3/setpriority]. We rescale the priority from Nintendo scale to that of Android. - * @param priority The priority of the thread in Nintendo format - */ - void UpdatePriority(i8 priority); - }; + std::shared_ptr stack; + ThreadContext ctx{}; + + void* entry; + u64 entryArgument; + 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& stack = nullptr); + + ~KThread(); + + /** + * @param self If the calling thread should jump directly into guest code or if a new thread should be created for it + * @note If the thread is already running then this does nothing + * @note 'stack' will be created if it wasn't set prior to calling this + */ + void Start(bool self = false); + + void Kill(); + + /** + * @brief Sets the host priority using setpriority with a rescaled the priority from HOS to Android + * @note It also affects guest scheduler behavior, this isn't purely for host + */ + void UpdatePriority(i8 priority); + }; + } } diff --git a/app/src/main/cpp/skyline/loader/loader.cpp b/app/src/main/cpp/skyline/loader/loader.cpp index 86744471..cb5f96bb 100644 --- a/app/src/main/cpp/skyline/loader/loader.cpp +++ b/app/src/main/cpp/skyline/loader/loader.cpp @@ -29,21 +29,21 @@ namespace skyline::loader { u64 padding{util::AlignUp(patchSize, PAGE_SIZE) - patchSize}; process->NewHandle(base + executable.text.offset, textSize, memory::Permission{true, false, true}, memory::states::CodeStatic); // R-X - state.logger->Debug("Successfully mapped section .text @ 0x{0:X}, Size = 0x{1:X}", base + executable.text.offset, textSize); + state.logger->Debug("Successfully mapped section .text @ {}, Size = 0x{:X}", base + executable.text.offset, textSize); process->NewHandle(base + executable.ro.offset, roSize, memory::Permission{true, false, false}, memory::states::CodeReadOnly); // R-- - state.logger->Debug("Successfully mapped section .rodata @ 0x{0:X}, Size = 0x{1:X}", base + executable.ro.offset, roSize); + state.logger->Debug("Successfully mapped section .rodata @ {}, Size = 0x{:X}", base + executable.ro.offset, roSize); process->NewHandle(base + executable.data.offset, dataSize, memory::Permission{true, true, false}, memory::states::CodeMutable); // RW- - state.logger->Debug("Successfully mapped section .data @ 0x{0:X}, Size = 0x{1:X}", base + executable.data.offset, dataSize); + state.logger->Debug("Successfully mapped section .data @ {}, Size = 0x{:X}", base + executable.data.offset, dataSize); process->NewHandle(base + patchOffset, patchSize + padding, memory::Permission{true, true, true}, memory::states::CodeMutable); // RWX - state.logger->Debug("Successfully mapped section .patch @ 0x{0:X}, Size = 0x{1:X}", base + patchOffset, patchSize + padding); + state.logger->Debug("Successfully mapped section .patch @ {}, Size = 0x{:X}", base + patchOffset, patchSize + padding); - std::memcpy(executable.text.contents.data(), base + executable.text.offset, textSize); - std::memcpy(executable.ro.contents.data(), base + executable.ro.offset, roSize); - std::memcpy(executable.data.contents.data(), base + executable.data.offset, dataSize - executable.bssSize); - std::memcpy(patch.data(), base + patchOffset, patchSize); + std::memcpy(base + executable.text.offset, executable.text.contents.data(), textSize); + std::memcpy(base + executable.ro.offset, executable.ro.contents.data(), roSize); + std::memcpy(base + executable.data.offset, executable.data.contents.data(), dataSize - executable.bssSize); + std::memcpy(base + patchOffset, patch.data(), patchSize); return {base, patchOffset + patchSize + padding}; } diff --git a/app/src/main/cpp/skyline/loader/loader.h b/app/src/main/cpp/skyline/loader/loader.h index 3a487fb1..1fa80e0e 100644 --- a/app/src/main/cpp/skyline/loader/loader.h +++ b/app/src/main/cpp/skyline/loader/loader.h @@ -70,17 +70,10 @@ namespace skyline::loader { virtual ~Loader() = default; - /** - * @return The icon of the loaded application - */ virtual std::vector GetIcon() { return std::vector(); } - /** - * @brief Loads in the data of the main process - * @param process The process to load in the data - */ virtual void LoadProcessData(const std::shared_ptr process, const DeviceState &state) = 0; }; } diff --git a/app/src/main/cpp/skyline/loader/nca.cpp b/app/src/main/cpp/skyline/loader/nca.cpp index 835f61e6..3a5b4839 100644 --- a/app/src/main/cpp/skyline/loader/nca.cpp +++ b/app/src/main/cpp/skyline/loader/nca.cpp @@ -2,8 +2,7 @@ // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) #include -#include -#include +#include #include "nso.h" #include "nca.h" @@ -21,6 +20,8 @@ namespace skyline::loader { if (nsoFile == nullptr) throw exception("Cannot load an ExeFS that doesn't contain rtld"); + state.process->memory.InitializeVmm(memory::AddressSpaceType::AddressSpace39Bit); + auto loadInfo{NsoLoader::LoadNso(nsoFile, process, state)}; u64 offset{loadInfo.size}; u8* base{loadInfo.base}; @@ -38,7 +39,7 @@ namespace skyline::loader { offset += loadInfo.size; } - state.os->memory.InitializeRegions(base, offset, memory::AddressSpaceType::AddressSpace39Bit); + state.process->memory.InitializeRegions(base, offset); } void NcaLoader::LoadProcessData(const std::shared_ptr process, const DeviceState &state) { diff --git a/app/src/main/cpp/skyline/loader/nro.cpp b/app/src/main/cpp/skyline/loader/nro.cpp index dfaec4e6..a778b01c 100644 --- a/app/src/main/cpp/skyline/loader/nro.cpp +++ b/app/src/main/cpp/skyline/loader/nro.cpp @@ -2,8 +2,7 @@ // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) #include -#include -#include +#include #include #include #include "nro.h" @@ -59,7 +58,8 @@ namespace skyline::loader { nroExecutable.bssSize = header.bssSize; + state.process->memory.InitializeVmm(memory::AddressSpaceType::AddressSpace39Bit); auto loadInfo{LoadExecutable(process, state, nroExecutable)}; - state.os->memory.InitializeRegions(loadInfo.base, loadInfo.size, memory::AddressSpaceType::AddressSpace39Bit); + state.process->memory.InitializeRegions(loadInfo.base, loadInfo.size); } } diff --git a/app/src/main/cpp/skyline/loader/nso.cpp b/app/src/main/cpp/skyline/loader/nso.cpp index c7efc3f7..bd092e63 100644 --- a/app/src/main/cpp/skyline/loader/nso.cpp +++ b/app/src/main/cpp/skyline/loader/nso.cpp @@ -3,8 +3,7 @@ #include #include -#include -#include +#include #include "nso.h" namespace skyline::loader { @@ -56,8 +55,8 @@ namespace skyline::loader { } void NsoLoader::LoadProcessData(const std::shared_ptr process, const DeviceState &state) { + state.process->memory.InitializeVmm(memory::AddressSpaceType::AddressSpace39Bit); auto loadInfo{LoadNso(backing, process, state)}; - - state.os->memory.InitializeRegions(loadInfo.base, loadInfo.size, memory::AddressSpaceType::AddressSpace39Bit); + state.process->memory.InitializeRegions(loadInfo.base, loadInfo.size); } } diff --git a/app/src/main/cpp/skyline/nce.cpp b/app/src/main/cpp/skyline/nce.cpp index ae02c615..79071516 100644 --- a/app/src/main/cpp/skyline/nce.cpp +++ b/app/src/main/cpp/skyline/nce.cpp @@ -18,6 +18,7 @@ extern skyline::GroupMutex JniMtx; namespace skyline { void NCE::KernelThread(pid_t thread) { + /* state.jvm->AttachThread(); try { state.thread = state.process->threads.at(thread); @@ -81,6 +82,7 @@ namespace skyline { } state.jvm->DetachThread(); + */ } NCE::NCE(DeviceState &state) : state(state) {} @@ -111,55 +113,18 @@ namespace skyline { } } - /** - * This function will not work if optimizations are enabled as ThreadContext isn't volatile - * and due to that is not read on every iteration of the while loop. - * However, making ThreadContext or parts of it volatile slows down the applications as a whole. - * So, we opted to use the hacky solution and disable optimizations for this single function. - */ - void ExecuteFunctionCtx(ThreadCall call, Registers &funcRegs, ThreadContext *ctx) __attribute__ ((optnone)) { - ctx->threadCall = call; - Registers registers{ctx->registers}; - - while (ctx->state != ThreadState::WaitInit && ctx->state != ThreadState::WaitKernel); - - ctx->registers = funcRegs; - ctx->state = ThreadState::WaitFunc; - - while (ctx->state != ThreadState::WaitInit && ctx->state != ThreadState::WaitKernel); - - funcRegs = ctx->registers; - ctx->registers = registers; + inline ThreadContext *GetContext() { + ThreadContext *ctx; + asm("MRS %0, TPIDR_EL0":"=r"(ctx)); + return ctx; } - void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs, std::shared_ptr &thread) { - ExecuteFunctionCtx(call, funcRegs, reinterpret_cast(thread->ctxMemory->kernel.ptr)); + void NCE::SignalHandler(int signal, const siginfo &info, const ucontext &context) { + } - void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs) { - if (state.process->status == kernel::type::KProcess::Status::Exiting) - throw exception("Executing function on Exiting process"); - - auto thread{state.thread ? state.thread : state.process->threads.at(state.process->pid)}; - ExecuteFunctionCtx(call, funcRegs, reinterpret_cast(thread->ctxMemory->kernel.ptr)); - } - - void NCE::WaitThreadInit(std::shared_ptr &thread) __attribute__ ((optnone)) { - auto ctx{reinterpret_cast(thread->ctxMemory->kernel.ptr)}; - while (ctx->state == ThreadState::NotReady); - } - - void NCE::StartThread(u64 entryArg, u32 handle, std::shared_ptr &thread) { - auto ctx{reinterpret_cast(thread->ctxMemory->kernel.ptr)}; - while (ctx->state != ThreadState::WaitInit); - - ctx->tpidrroEl0 = reinterpret_cast(thread->tls); - ctx->registers.x0 = entryArg; - ctx->registers.x1 = handle; - ctx->state = ThreadState::WaitRun; - - state.logger->Debug("Starting kernel thread for guest thread: {}", thread->tid); - threadMap[thread->tid] = std::make_shared(&NCE::KernelThread, this, thread->tid); + void NCE::SignalHandler(int signal, siginfo *info, void *context) { + (GetContext()->nce)->SignalHandler(signal, *info, *reinterpret_cast(context)); } void NCE::ThreadTrace(u16 instructionCount, ThreadContext *ctx) { @@ -170,8 +135,8 @@ namespace skyline { ctx = ctx ? ctx : state.ctx; if (instructionCount) { - auto offset{ctx->pc - ((instructionCount - 2) * sizeof(u32))}; - span instructions(reinterpret_cast(offset), instructionCount); + auto offset{ctx->pc - (instructionCount * sizeof(u32)) + (2 * sizeof(u32))}; + span instructions(reinterpret_cast(offset), instructionCount); for (auto &instruction : instructions) { instruction = __builtin_bswap32(instruction); @@ -186,8 +151,8 @@ namespace skyline { } } - if (ctx->faultAddress) - regStr += fmt::format("\nFault Address: 0x{:X}", ctx->faultAddress); + //if (ctx->faultAddress) + // regStr += fmt::format("\nFault Address: 0x{:X}", ctx->faultAddress); if (ctx->sp) regStr += fmt::format("\nStack Pointer: 0x{:X}", ctx->sp); diff --git a/app/src/main/cpp/skyline/nce.h b/app/src/main/cpp/skyline/nce.h index c348a6d4..7de7e455 100644 --- a/app/src/main/cpp/skyline/nce.h +++ b/app/src/main/cpp/skyline/nce.h @@ -24,50 +24,20 @@ namespace skyline { public: NCE(DeviceState &state); - /** - * @brief The destructor for NCE, this calls join() on all the threads - */ ~NCE(); - /** - * @brief The main event loop of the program - */ void Execute(); - /** - * @brief Execute any arbitrary function on a specific child thread - * @param call The specific call to execute - * @param funcRegs A set of registers to run the function - * @param thread The thread to execute the function on - */ - void ExecuteFunction(ThreadCall call, Registers &funcRegs, std::shared_ptr &thread); + void SignalHandler(int signal, const siginfo &info, const ucontext &ucontext); /** - * @brief Execute any arbitrary function on the child process - * @param call The specific call to execute - * @param funcRegs A set of registers to run the function + * @brief A delegator to the real signal handler after restoring context */ - void ExecuteFunction(ThreadCall call, Registers &funcRegs); - - /** - * @brief Waits till a thread is ready to execute commands - * @param thread The KThread to wait for initialization - */ - void WaitThreadInit(std::shared_ptr &thread); - - /** - * @brief Sets the X0 and X1 registers in a thread and starts it and it's kernel thread - * @param entryArg The argument to pass in for the entry function - * @param handle The handle of the main thread - * @param thread The thread to set the registers and start - * @note This function will block forever if the thread has already started - */ - void StartThread(u64 entryArg, u32 handle, std::shared_ptr &thread); + static void SignalHandler(int signal, siginfo *info, void *context); /** * @brief Prints out a trace and the CPU context * @param instructionCount The amount of previous instructions to print (Can be 0) - * @param ctx The ThreadContext of the thread to log */ void ThreadTrace(u16 instructionCount = 10, ThreadContext *ctx = nullptr); diff --git a/app/src/main/cpp/skyline/nce/guest.S b/app/src/main/cpp/skyline/nce/guest.S index 5732bcec..e5b11a08 100644 --- a/app/src/main/cpp/skyline/nce/guest.S +++ b/app/src/main/cpp/skyline/nce/guest.S @@ -22,7 +22,6 @@ SaveCtx: STP X26, X27, [LR, #224] STP X28, X29, [LR, #240] LDR LR, [SP], #16 - DSB ST RET .global LoadCtx diff --git a/app/src/main/cpp/skyline/nce/guest.cpp b/app/src/main/cpp/skyline/nce/guest.cpp index b25616e5..494c8b0a 100644 --- a/app/src/main/cpp/skyline/nce/guest.cpp +++ b/app/src/main/cpp/skyline/nce/guest.cpp @@ -99,6 +99,7 @@ namespace skyline::guest { * @note Do not use any functions that cannot be inlined from this, as this function is placed at an arbitrary address in the guest. In addition, do not use any static variables or globals as the .bss section is not copied into the guest. */ void SvcHandler(u64 pc, u16 svc) { + /* volatile ThreadContext *ctx; asm("MRS %0, TPIDR_EL0":"=r"(ctx)); @@ -206,6 +207,7 @@ namespace skyline::guest { } ctx->state = ThreadState::Running; + */ } [[noreturn]] void Exit(int) { @@ -217,6 +219,7 @@ namespace skyline::guest { } [[noreturn]] void SignalHandler(int signal, siginfo_t *info, ucontext_t *ucontext) { + /* volatile ThreadContext *ctx; asm("MRS %0, TPIDR_EL0":"=r"(ctx)); @@ -234,9 +237,11 @@ namespace skyline::guest { if (ctx->state == ThreadState::WaitRun) Exit(0); } + */ } void GuestEntry(u64 address) { + /* volatile ThreadContext *ctx; asm("MRS %0, TPIDR_EL0":"=r"(ctx)); @@ -270,90 +275,6 @@ namespace skyline::guest { *(src++) = *(dest++); } } - - struct sigaction sigact{ - .sa_sigaction = reinterpret_cast(reinterpret_cast(SignalHandler)), - .sa_flags = SA_SIGINFO, - }; - - for (int signal : {SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV}) - sigaction(signal, &sigact, nullptr); - - sigact = { - .sa_handler = Exit, - }; - - sigaction(SIGTERM, &sigact, nullptr); - - ctx->state = ThreadState::Running; - - asm("MOV LR, %0\n\t" - "MOV X0, %1\n\t" - "MOV X1, %2\n\t" - "MOV X2, XZR\n\t" - "MOV X3, XZR\n\t" - "MOV X4, XZR\n\t" - "MOV X5, XZR\n\t" - "MOV X6, XZR\n\t" - "MOV X7, XZR\n\t" - "MOV X8, XZR\n\t" - "MOV X9, XZR\n\t" - "MOV X10, XZR\n\t" - "MOV X11, XZR\n\t" - "MOV X12, XZR\n\t" - "MOV X13, XZR\n\t" - "MOV X14, XZR\n\t" - "MOV X15, XZR\n\t" - "MOV X16, XZR\n\t" - "MOV X17, XZR\n\t" - "MOV X18, XZR\n\t" - "MOV X19, XZR\n\t" - "MOV X20, XZR\n\t" - "MOV X21, XZR\n\t" - "MOV X22, XZR\n\t" - "MOV X23, XZR\n\t" - "MOV X24, XZR\n\t" - "MOV X25, XZR\n\t" - "MOV X26, XZR\n\t" - "MOV X27, XZR\n\t" - "MOV X28, XZR\n\t" - "MOV X29, XZR\n\t" - "MSR FPSR, XZR\n\t" - "MSR FPCR, XZR\n\t" - "DUP V0.16B, WZR\n\t" - "DUP V1.16B, WZR\n\t" - "DUP V2.16B, WZR\n\t" - "DUP V3.16B, WZR\n\t" - "DUP V4.16B, WZR\n\t" - "DUP V5.16B, WZR\n\t" - "DUP V6.16B, WZR\n\t" - "DUP V7.16B, WZR\n\t" - "DUP V8.16B, WZR\n\t" - "DUP V9.16B, WZR\n\t" - "DUP V10.16B, WZR\n\t" - "DUP V11.16B, WZR\n\t" - "DUP V12.16B, WZR\n\t" - "DUP V13.16B, WZR\n\t" - "DUP V14.16B, WZR\n\t" - "DUP V15.16B, WZR\n\t" - "DUP V16.16B, WZR\n\t" - "DUP V17.16B, WZR\n\t" - "DUP V18.16B, WZR\n\t" - "DUP V19.16B, WZR\n\t" - "DUP V20.16B, WZR\n\t" - "DUP V21.16B, WZR\n\t" - "DUP V22.16B, WZR\n\t" - "DUP V23.16B, WZR\n\t" - "DUP V24.16B, WZR\n\t" - "DUP V25.16B, WZR\n\t" - "DUP V26.16B, WZR\n\t" - "DUP V27.16B, WZR\n\t" - "DUP V28.16B, WZR\n\t" - "DUP V29.16B, WZR\n\t" - "DUP V30.16B, WZR\n\t" - "DUP V31.16B, WZR\n\t" - "RET"::"r"(address), "r"(ctx->registers.x0), "r"(ctx->registers.x1) : "x0", "x1", "lr"); - - __builtin_unreachable(); + */ } } diff --git a/app/src/main/cpp/skyline/nce/guest.h b/app/src/main/cpp/skyline/nce/guest.h index d41592f1..2c68b113 100644 --- a/app/src/main/cpp/skyline/nce/guest.h +++ b/app/src/main/cpp/skyline/nce/guest.h @@ -16,12 +16,6 @@ namespace skyline { constexpr size_t SvcHandlerSize{400 * sizeof(u32)}; //!< The size of the SvcHandler (Debug) function in 32-bit ARMv8 instructions #endif - /** - * @brief The entry point for all guest threads - * @param address The address of the actual thread entry point - */ - void GuestEntry(u64 address); - /** * @brief Saves the context from CPU registers into TLS */ diff --git a/app/src/main/cpp/skyline/nce/guest_common.h b/app/src/main/cpp/skyline/nce/guest_common.h index 49e47374..e52aeb19 100644 --- a/app/src/main/cpp/skyline/nce/guest_common.h +++ b/app/src/main/cpp/skyline/nce/guest_common.h @@ -122,38 +122,17 @@ namespace skyline { }; }; - enum class ThreadState : u8 { - NotReady = 0, //!< The thread hasn't yet entered the entry handler - Running = 1, //!< The thread is currently executing code - WaitKernel = 2, //!< The thread is currently waiting on the kernel - WaitRun = 3, //!< The thread should be ready to run - WaitInit = 4, //!< The thread is waiting to be initialized - WaitFunc = 5, //!< The kernel is waiting for the thread to run a function - GuestCrash = 6, //!< This is a notification to the kernel that the guest has crashed - }; + class NCE; /** - * @brief The functions that can be run on the guest process - */ - enum class ThreadCall : u8 { - Syscall = 1, //!< A linux syscall needs to be called from the guest - Memcopy = 2, //!< To copy memory from one location to another - Clone = 3, //!< Use the clone syscall to create a new thread - }; - - /** - * @brief The context of a thread during kernel calls, it is stored in TLS on each guest thread + * @brief The context of a thread during kernel calls, it is stored for each thread */ struct ThreadContext { - ThreadState state; //!< The state of the guest - ThreadCall threadCall; //!< The function to run in the guest process - u16 svc; //!< The SVC ID of the current kernel call - u32 signal; //!< The signal caught by the guest process - u64 pc; //!< The program counter register on the guest + u8* pc; //!< The program counter on the guest + u8* sp; //!< The stack pointer on the guest Registers registers; //!< The general purpose registers on the guest - u64 tpidrroEl0; //!< The value for TPIDRRO_EL0 for the current thread - u64 tpidrEl0; //!< The value for TPIDR_EL0 for the current thread - u64 faultAddress; //!< The address a fault has occurred at during guest crash - u64 sp; //!< The current location of the stack pointer set during guest crash + u8* tpidrroEl0; //!< The value for TPIDRRO_EL0 for the current thread + u8* tpidrEl0; //!< The value for TPIDR_EL0 for the current thread + NCE* nce; //!< An instance of the NCE class, used by trampoline functions to call class methods }; } diff --git a/app/src/main/cpp/skyline/os.cpp b/app/src/main/cpp/skyline/os.cpp index 448d0763..2bf8a7c6 100644 --- a/app/src/main/cpp/skyline/os.cpp +++ b/app/src/main/cpp/skyline/os.cpp @@ -3,7 +3,6 @@ #include "nce.h" #include "nce/guest.h" -#include "kernel/memory.h" #include "kernel/types/KProcess.h" #include "vfs/os_backing.h" #include "loader/nro.h" @@ -13,51 +12,33 @@ #include "os.h" namespace skyline::kernel { - OS::OS(std::shared_ptr &jvmManager, std::shared_ptr &logger, std::shared_ptr &settings, const std::string &appFilesPath) : state(this, process, jvmManager, settings, logger), memory(state), serviceManager(state), appFilesPath(appFilesPath) {} + OS::OS(std::shared_ptr &jvmManager, std::shared_ptr &logger, std::shared_ptr &settings, const std::string &appFilesPath) : state(this, process, jvmManager, settings, logger), serviceManager(state), appFilesPath(appFilesPath) {} void OS::Execute(int romFd, loader::RomFormat romType) { auto romFile{std::make_shared(romFd)}; auto keyStore{std::make_shared(appFilesPath)}; - if (romType == loader::RomFormat::NRO) { + if (romType == loader::RomFormat::NRO) state.loader = std::make_shared(romFile); - } else if (romType == loader::RomFormat::NSO) { + else if (romType == loader::RomFormat::NSO) state.loader = std::make_shared(romFile); - } else if (romType == loader::RomFormat::NCA) { + else if (romType == loader::RomFormat::NCA) state.loader = std::make_shared(romFile, keyStore); - } else if (romType == loader::RomFormat::NSP) { + else if (romType == loader::RomFormat::NSP) state.loader = std::make_shared(romFile, keyStore); - } else { + else throw exception("Unsupported ROM extension."); - } - process = CreateProcess(constant::BaseAddress, 0, constant::DefStackSize); + process = std::make_shared(state); state.loader->LoadProcessData(process, state); - process->InitializeMemory(); - process->threads.at(process->pid)->Start(); // The kernel itself is responsible for starting the main thread + process->InitializeHeap(); + process->CreateThread(reinterpret_cast(constant::BaseAddress))->Start(); state.nce->Execute(); } - std::shared_ptr OS::CreateProcess(u64 entry, u64 argument, size_t stackSize) { - auto stack{std::make_shared(state, stackSize, memory::states::Stack)}; - stack->guest = stack->kernel; - - if (mprotect(stack->guest.ptr, PAGE_SIZE, PROT_NONE)) - throw exception("Failed to create guard pages"); - - auto tlsMem{std::make_shared(state, (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1), memory::states::Reserved)}; - tlsMem->guest = tlsMem->kernel; - - auto pid{clone(reinterpret_cast(&guest::GuestEntry), stack->guest.ptr + stackSize, CLONE_FILES | CLONE_FS | CLONE_SETTLS | SIGCHLD, reinterpret_cast(entry), nullptr, tlsMem->guest.ptr)}; - if (pid == -1) - throw exception("Call to clone() has failed: {}", strerror(errno)); - - state.logger->Debug("Successfully created process with PID: {}", pid); - return std::make_shared(state, pid, entry, stack, tlsMem); - } - void OS::KillThread(pid_t pid) { + /* if (process->pid == pid) { state.logger->Debug("Killing process with PID: {}", pid); for (auto &thread: process->threads) @@ -66,5 +47,6 @@ namespace skyline::kernel { state.logger->Debug("Killing thread with TID: {}", pid); process->threads.at(pid)->Kill(); } + */ } } diff --git a/app/src/main/cpp/skyline/os.h b/app/src/main/cpp/skyline/os.h index 2e5a561b..9494101c 100644 --- a/app/src/main/cpp/skyline/os.h +++ b/app/src/main/cpp/skyline/os.h @@ -3,7 +3,6 @@ #pragma once -#include "kernel/memory.h" #include "loader/loader.h" #include "services/serviceman.h" @@ -16,7 +15,6 @@ namespace skyline::kernel { DeviceState state; std::shared_ptr process; service::ServiceManager serviceManager; - MemoryManager memory; std::string appFilesPath; //!< The full path to the app's files directory /** diff --git a/app/src/main/cpp/skyline/services/am/controller/IWindowController.cpp b/app/src/main/cpp/skyline/services/am/controller/IWindowController.cpp index 0e8b16b2..eda0564b 100644 --- a/app/src/main/cpp/skyline/services/am/controller/IWindowController.cpp +++ b/app/src/main/cpp/skyline/services/am/controller/IWindowController.cpp @@ -8,7 +8,7 @@ namespace skyline::service::am { IWindowController::IWindowController(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {} Result IWindowController::GetAppletResourceUserId(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - response.Push(static_cast(state.process->pid)); + response.Push(static_cast(0)); return {}; }