diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 8069f74f..de973ce5 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -38,7 +38,6 @@ add_library(skyline SHARED ${source_DIR}/skyline/loader/nro.cpp ${source_DIR}/skyline/kernel/ipc.cpp ${source_DIR}/skyline/kernel/svc.cpp - ${source_DIR}/skyline/kernel/types/KSyncObject.cpp ${source_DIR}/skyline/kernel/types/KProcess.cpp ${source_DIR}/skyline/kernel/types/KThread.cpp ${source_DIR}/skyline/kernel/types/KSharedMemory.cpp @@ -59,5 +58,5 @@ add_library(skyline SHARED ${source_DIR}/skyline/services/vi/vi_m.cpp ) -target_link_libraries(skyline vulkan GLESv3 EGL android fmt tinyxml2) +target_link_libraries(skyline vulkan android fmt tinyxml2) target_compile_options(skyline PRIVATE -Wno-c++17-extensions) diff --git a/app/src/main/cpp/.clang-tidy b/app/src/main/cpp/.clang-tidy new file mode 100644 index 00000000..da3d27f7 --- /dev/null +++ b/app/src/main/cpp/.clang-tidy @@ -0,0 +1,155 @@ +--- +Checks: 'clang-diagnostic-*,clang-analyzer-*,*, android-*, -bugprone-bool-pointer-implicit-conversion, -cert-env33-c, -cert-dcl50-cpp, -cert-dcl59-cpp, -cppcoreguidelines-no-malloc, -cppcoreguidelines-owning-memory, -cppcoreguidelines-pro-bounds-array-to-pointer-decay, -cppcoreguidelines-pro-bounds-constant-array-index, -cppcoreguidelines-pro-bounds-pointer-arithmetic, -cppcoreguidelines-pro-type-const-cast, + -cppcoreguidelines-pro-type-cstyle-cast, -cppcoreguidelines-pro-type-reinterpret-cast, -cppcoreguidelines-pro-type-union-access, -cppcoreguidelines-pro-type-vararg, -cppcoreguidelines-special-member-functions, -fuchsia-*, -google-*, google-default-arguments, google-explicit-constructor, google-runtime-member-string-references, google-runtime-operator, -hicpp-braces-around-statements, + -hicpp-braces-around-statements, -hicpp-named-parameter, -hicpp-no-array-decay, -hicpp-no-assembler, -hicpp-no-malloc, -hicpp-function-size, -hicpp-special-member-functions, -hicpp-vararg, -llvm-*, -objc-*, -readability-else-after-return, -readability-implicit-bool-conversion, -readability-named-parameter, -readability-simplify-boolean-expr, -readability-braces-around-statements, + -readability-identifier-naming, -readability-function-size, -readability-redundant-member-init, -misc-bool-pointer-implicit-conversion, -misc-definitions-in-headers, -misc-unused-alias-decls, -misc-unused-parameters, -misc-unused-using-decls, -modernize-use-using, -modernize-use-default-member-init, -clang-diagnostic-*, -clang-analyzer-*, -hicpp-signed-bitwise' +WarningsAsErrors: '' +HeaderFilterRegex: '' +AnalyzeTemporaryDtors: false +FormatStyle: none +CheckOptions: + - key: bugprone-argument-comment.StrictMode + value: '0' + - key: bugprone-assert-side-effect.AssertMacros + value: assert + - key: bugprone-assert-side-effect.CheckFunctionCalls + value: '0' + - key: bugprone-dangling-handle.HandleClasses + value: 'std::basic_string_view;std::experimental::basic_string_view' + - key: bugprone-string-constructor.LargeLengthThreshold + value: '8388608' + - key: bugprone-string-constructor.WarnOnLargeLength + value: '1' + - key: cert-err09-cpp.CheckThrowTemporaries + value: '1' + - key: cert-err61-cpp.CheckThrowTemporaries + value: '1' + - key: cert-oop11-cpp.IncludeStyle + value: llvm + - key: cppcoreguidelines-pro-type-member-init.IgnoreArrays + value: '0' + - key: google-readability-braces-around-statements.ShortStatementLines + value: '1' + - key: google-readability-function-size.StatementThreshold + value: '800' + - key: google-readability-namespace-comments.ShortNamespaceLines + value: '10' + - key: google-readability-namespace-comments.SpacesBeforeComments + value: '2' + - key: hicpp-member-init.IgnoreArrays + value: '0' + - key: hicpp-move-const-arg.CheckTriviallyCopyableMove + value: '1' + - key: hicpp-use-auto.RemoveStars + value: '0' + - key: hicpp-use-emplace.ContainersWithPushBack + value: '::std::vector;::std::list;::std::deque' + - key: hicpp-use-emplace.SmartPointers + value: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr' + - key: hicpp-use-emplace.TupleMakeFunctions + value: '::std::make_pair;::std::make_tuple' + - key: hicpp-use-emplace.TupleTypes + value: '::std::pair;::std::tuple' + - key: hicpp-use-equals-default.IgnoreMacros + value: '1' + - key: hicpp-use-noexcept.ReplacementString + value: '' + - key: hicpp-use-noexcept.UseNoexceptFalse + value: '1' + - key: hicpp-use-nullptr.NullMacros + value: '' + - key: misc-misplaced-widening-cast.CheckImplicitCasts + value: '0' + - key: misc-sizeof-expression.WarnOnSizeOfCompareToConstant + value: '1' + - key: misc-sizeof-expression.WarnOnSizeOfConstant + value: '1' + - key: misc-sizeof-expression.WarnOnSizeOfThis + value: '1' + - key: misc-suspicious-enum-usage.StrictMode + value: '0' + - key: misc-suspicious-missing-comma.MaxConcatenatedTokens + value: '5' + - key: misc-suspicious-missing-comma.RatioThreshold + value: '0.200000' + - key: misc-suspicious-missing-comma.SizeThreshold + value: '5' + - key: misc-suspicious-string-compare.StringCompareLikeFunctions + value: '' + - key: misc-suspicious-string-compare.WarnOnImplicitComparison + value: '1' + - key: misc-suspicious-string-compare.WarnOnLogicalNotComparison + value: '0' + - key: misc-throw-by-value-catch-by-reference.CheckThrowTemporaries + value: '1' + - key: modernize-loop-convert.MaxCopySize + value: '16' + - key: modernize-loop-convert.MinConfidence + value: reasonable + - key: modernize-loop-convert.NamingStyle + value: CamelCase + - key: modernize-make-shared.IgnoreMacros + value: '1' + - key: modernize-make-shared.IncludeStyle + value: '0' + - key: modernize-make-shared.MakeSmartPtrFunction + value: 'std::make_shared' + - key: modernize-make-shared.MakeSmartPtrFunctionHeader + value: memory + - key: modernize-make-unique.IgnoreMacros + value: '1' + - key: modernize-make-unique.IncludeStyle + value: '0' + - key: modernize-make-unique.MakeSmartPtrFunction + value: 'std::make_unique' + - key: modernize-make-unique.MakeSmartPtrFunctionHeader + value: memory + - key: modernize-pass-by-value.IncludeStyle + value: llvm + - key: modernize-pass-by-value.ValuesOnly + value: '0' + - key: modernize-raw-string-literal.ReplaceShorterLiterals + value: '0' + - key: modernize-replace-auto-ptr.IncludeStyle + value: llvm + - key: modernize-replace-random-shuffle.IncludeStyle + value: llvm + - key: modernize-use-auto.RemoveStars + value: '0' + - key: modernize-use-emplace.ContainersWithPushBack + value: '::std::vector;::std::list;::std::deque' + - key: modernize-use-emplace.SmartPointers + value: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr' + - key: modernize-use-emplace.TupleMakeFunctions + value: '::std::make_pair;::std::make_tuple' + - key: modernize-use-emplace.TupleTypes + value: '::std::pair;::std::tuple' + - key: modernize-use-equals-default.IgnoreMacros + value: '1' + - key: modernize-use-noexcept.ReplacementString + value: '' + - key: modernize-use-noexcept.UseNoexceptFalse + value: '1' + - key: modernize-use-nullptr.NullMacros + value: 'NULL' + - key: modernize-use-transparent-functors.SafeMode + value: '0' + - key: performance-faster-string-find.StringLikeClasses + value: 'std::basic_string' + - key: performance-for-range-copy.WarnOnAllAutoCopies + value: '0' + - key: performance-inefficient-string-concatenation.StrictMode + value: '0' + - key: performance-inefficient-vector-operation.VectorLikeClasses + value: '::std::vector' + - key: performance-move-const-arg.CheckTriviallyCopyableMove + value: '1' + - key: performance-move-constructor-init.IncludeStyle + value: llvm + - key: performance-type-promotion-in-math-fn.IncludeStyle + value: llvm + - key: performance-unnecessary-value-param.IncludeStyle + value: llvm + - key: readability-static-accessed-through-instance.NameSpecifierNestingThreshold + value: '3' +... diff --git a/app/src/main/cpp/main.cpp b/app/src/main/cpp/main.cpp index 0fe0b311..917cc99a 100644 --- a/app/src/main/cpp/main.cpp +++ b/app/src/main/cpp/main.cpp @@ -7,7 +7,7 @@ bool Halt; jobject Surface; uint FaultCount; -skyline::Mutex jniMtx; +skyline::GroupMutex jniMtx; void signalHandler(int signal) { syslog(LOG_ERR, "Halting program due to signal: %s", strsignal(signal)); @@ -56,16 +56,16 @@ extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_executeRom(JNIEnv *env, } extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_setHalt(JNIEnv *env, jobject instance, jboolean halt) { - jniMtx.lock(); + jniMtx.lock(skyline::GroupMutex::Group::Group2); Halt = halt; jniMtx.unlock(); } extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_setSurface(JNIEnv *env, jobject instance, jobject surface) { - jniMtx.lock(); - if(!env->IsSameObject(Surface, nullptr)) + jniMtx.lock(skyline::GroupMutex::Group::Group2); + if (!env->IsSameObject(Surface, nullptr)) env->DeleteGlobalRef(Surface); - if(!env->IsSameObject(surface, nullptr)) + if (!env->IsSameObject(surface, nullptr)) Surface = env->NewGlobalRef(surface); else Surface = surface; diff --git a/app/src/main/cpp/skyline/common.cpp b/app/src/main/cpp/skyline/common.cpp index 2abbc7a0..3bf214a2 100644 --- a/app/src/main/cpp/skyline/common.cpp +++ b/app/src/main/cpp/skyline/common.cpp @@ -16,6 +16,17 @@ namespace skyline { return !flag.test_and_set(std::memory_order_acquire); } + void GroupMutex::lock(Group group) { + auto none = Group::None; + while (!flag.compare_exchange_weak(none, group) && flag != group); + num++; + } + + void GroupMutex::unlock() { + if (!--num) + flag.exchange(Group::None); + } + Settings::Settings(const int preferenceFd) { tinyxml2::XMLDocument pref; if (pref.LoadFile(fdopen(preferenceFd, "r"))) @@ -95,5 +106,5 @@ namespace skyline { } thread_local std::shared_ptr DeviceState::thread = 0; - thread_local ThreadContext* DeviceState::ctx = 0; + thread_local ThreadContext *DeviceState::ctx = 0; } diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h index 6d69222f..976e45a3 100644 --- a/app/src/main/cpp/skyline/common.h +++ b/app/src/main/cpp/skyline/common.h @@ -42,7 +42,7 @@ namespace skyline { constexpr u16 BrkRdy = 0xFF; //!< This is reserved for our kernel's to know when a process/thread is ready constexpr u32 TpidrroEl0 = 0x5E83; //!< ID of TPIDRRO_EL0 in MRS constexpr u32 CntfrqEl0 = 0x5F00; //!< ID of CNTFRQ_EL0 in MRS - constexpr u32 TegraX1Freq = 0x124F800; //!< The clock frequency of the Tegra X1 + constexpr u32 TegraX1Freq = 0x124F800; //!< The clock frequency of the Tegra X1 (19.2 MHz) constexpr u32 CntpctEl0 = 0x5F01; //!< ID of CNTPCT_EL0 in MRS constexpr u32 CntvctEl0 = 0x5F02; //!< ID of CNTVCT_EL0 in MRS // Kernel @@ -111,7 +111,7 @@ namespace skyline { void lock(); /** - * @brief Lock the mutex if it is unlocked else return + * @brief Try to lock the mutex if it is unlocked else return * @return If the mutex was successfully locked or not */ bool try_lock(); @@ -122,6 +122,36 @@ namespace skyline { void unlock(); }; + /** + * @brief The GroupMutex class is a special type of mutex that allows two groups of users and only allows one group to run in parallel + */ + class GroupMutex { + public: + /** + * @brief This enumeration holds all the possible owners of the mutex + */ + enum class Group : u8 { + None = 0, //!< No group owns this mutex + Group1 = 1, //!< Group 1 owns this mutex + Group2 = 2 //!< Group 2 owns this mutex + }; + + /** + * @brief Wait on and lock the mutex + */ + void lock(Group group = Group::Group1); + + /** + * @brief Unlock the mutex + * @note Undefined behavior in case unlocked by thread in non-owner group + */ + void unlock(); + + private: + std::atomic flag = Group::None; //!< An atomic flag to hold which group holds the mutex + std::atomic num = 0; //!< An atomic u8 keeping track of how many users are holding the mutex + }; + /** * @brief The Logger class is to write log output to file and logcat */ @@ -293,7 +323,7 @@ namespace skyline { kernel::OS *os; //!< This holds a reference to the OS class std::shared_ptr &process; //!< This holds a reference to the process object thread_local static std::shared_ptr thread; //!< This holds a reference to the current thread object - thread_local static ThreadContext* ctx; //!< This holds the context of the thread + thread_local static ThreadContext *ctx; //!< This holds the context of the thread std::shared_ptr nce; //!< This holds a reference to the NCE class std::shared_ptr gpu; //!< This holds a reference to the GPU class std::shared_ptr jvmManager; //!< This holds a reference to the JvmManager class diff --git a/app/src/main/cpp/skyline/gpu.cpp b/app/src/main/cpp/skyline/gpu.cpp index f063bed6..cc2bc600 100644 --- a/app/src/main/cpp/skyline/gpu.cpp +++ b/app/src/main/cpp/skyline/gpu.cpp @@ -62,8 +62,8 @@ namespace skyline::gpu { u8 *inBlock = inBuffer; u8 *outBlock = outBuffer + (y * strideBytes) + x; for (u32 i = 0; i < 32; i++) { - const u32 yT = ((i >> 1) & 0x06) | (i & 0x01); // NOLINT(hicpp-signed-bitwise) - const u32 xT = ((i << 3) & 0x10) | ((i << 1) & 0x20); // NOLINT(hicpp-signed-bitwise) + const u32 yT = ((i >> 1) & 0x06) | (i & 0x01); + const u32 xT = ((i << 3) & 0x10) | ((i << 1) & 0x20); std::memcpy(outBlock + (yT * strideBytes) + xT, inBlock, sizeof(u128)); inBlock += sizeof(u128); } @@ -135,8 +135,8 @@ namespace skyline::gpu { void GPU::Ioctl(u32 fd, u32 cmd, kernel::ipc::IpcRequest &request, kernel::ipc::IpcResponse &response) { state.logger->Debug("IOCTL on device: 0x{:X}, cmd: 0x{:X}", fd, cmd); try { - if(request.inputBuf.empty() || request.outputBuf.empty()) { - if(request.inputBuf.empty()) { + if (request.inputBuf.empty() || request.outputBuf.empty()) { + if (request.inputBuf.empty()) { device::IoctlData data(request.outputBuf.at(0)); fdMap.at(fd)->HandleIoctl(cmd, data); response.Push(data.status); diff --git a/app/src/main/cpp/skyline/gpu/display.cpp b/app/src/main/cpp/skyline/gpu/display.cpp index de399240..9f5e96bf 100644 --- a/app/src/main/cpp/skyline/gpu/display.cpp +++ b/app/src/main/cpp/skyline/gpu/display.cpp @@ -17,7 +17,7 @@ namespace skyline::gpu { if (!nvBuffer) throw exception("A QueueBuffer request has an invalid NVMap Handle ({}) and ID ({})", gbpBuffer.nvmapHandle, gbpBuffer.nvmapId); } - switch(gbpBuffer.format) { + switch (gbpBuffer.format) { case WINDOW_FORMAT_RGBA_8888: case WINDOW_FORMAT_RGBX_8888: bpp = sizeof(u32); @@ -34,10 +34,6 @@ namespace skyline::gpu { state.process->ReadMemory(dataBuffer.data(), nvBuffer->address + gbpBuffer.offset, gbpBuffer.size); } - BufferQueue::WaitContext::WaitContext(std::shared_ptr thread, DequeueIn input, kernel::ipc::OutputBuffer& buffer) : thread(std::move(thread)), input(input), buffer(buffer) {} - - BufferQueue::DequeueOut::DequeueOut(u32 slot) : slot(slot), _unk0_(0x1), _unk1_(0x24) {} - BufferQueue::BufferQueue(const DeviceState &state) : state(state) {} void BufferQueue::RequestBuffer(Parcel &in, Parcel &out) { @@ -50,9 +46,15 @@ namespace skyline::gpu { } void BufferQueue::DequeueBuffer(Parcel &in, Parcel &out) { - auto *data = reinterpret_cast(in.data.data() + constant::TokenLength); + struct Data { + u32 format; + u32 width; + u32 height; + u32 timestamps; + u32 usage; + } *data = reinterpret_cast(in.data.data() + constant::TokenLength); i64 slot{-1}; - while(slot == -1) { + while (slot == -1) { for (auto &buffer : queue) { if (buffer.second->status == BufferStatus::Free && buffer.second->resolution.width == data->width && buffer.second->resolution.height == data->height && buffer.second->gbpBuffer.usage == data->usage) { slot = buffer.first; @@ -62,7 +64,12 @@ namespace skyline::gpu { } sched_yield(); } - DequeueOut output(static_cast(slot)); + struct { + u32 slot; + u32 _unk_[13]; + } output{ + .slot = static_cast(slot) + }; out.WriteData(output); state.logger->Debug("DequeueBuffer: Width: {}, Height: {}, Format: {}, Usage: {}, Timestamps: {}, Slot: {}", data->width, data->height, data->format, data->usage, data->timestamps, slot); } @@ -118,28 +125,19 @@ namespace skyline::gpu { auto gbpBuffer = reinterpret_cast(pointer); queue[data->slot] = std::make_shared(state, data->slot, *gbpBuffer); state.gpu->bufferEvent->Signal(); - state.logger->Debug("SetPreallocatedBuffer: Slot: {}, Magic: 0x{:X}, Width: {}, Height: {}, Stride: {}, Format: {}, Usage: {}, Index: {}, ID: {}, Handle: {}, Offset: 0x{:X}, Block Height: {}, Size: 0x{:X}", data->slot, gbpBuffer->magic, gbpBuffer->width, gbpBuffer->height, gbpBuffer->stride, gbpBuffer->format, gbpBuffer->usage, gbpBuffer->index,gbpBuffer->nvmapId, gbpBuffer->nvmapHandle, gbpBuffer->offset, (1U << gbpBuffer->blockHeightLog2), gbpBuffer->size); - } - - void BufferQueue::FreeBuffer(u32 slotNo) { - auto &slot = queue.at(slotNo); - if (waitVec.empty()) - slot->status = BufferStatus::Free; - else { - auto context = waitVec.begin(); - while (context != waitVec.end()) { - if (slot->resolution.width == context->input.width && slot->resolution.height == context->input.height && slot->gbpBuffer.usage == context->input.usage) { - context->thread->WakeUp(); - gpu::Parcel out(state); - DequeueOut output(slotNo); - out.WriteData(output); - out.WriteParcel(context->buffer); - slot->status = BufferStatus::Dequeued; - waitVec.erase(context); - break; - } - context++; - } - } + state.logger->Debug("SetPreallocatedBuffer: Slot: {}, Magic: 0x{:X}, Width: {}, Height: {}, Stride: {}, Format: {}, Usage: {}, Index: {}, ID: {}, Handle: {}, Offset: 0x{:X}, Block Height: {}, Size: 0x{:X}", + data->slot, + gbpBuffer->magic, + gbpBuffer->width, + gbpBuffer->height, + gbpBuffer->stride, + gbpBuffer->format, + gbpBuffer->usage, + gbpBuffer->index, + gbpBuffer->nvmapId, + gbpBuffer->nvmapHandle, + gbpBuffer->offset, + (1U << gbpBuffer->blockHeightLog2), + gbpBuffer->size); } } diff --git a/app/src/main/cpp/skyline/gpu/display.h b/app/src/main/cpp/skyline/gpu/display.h index f6cec24d..0d08022e 100644 --- a/app/src/main/cpp/skyline/gpu/display.h +++ b/app/src/main/cpp/skyline/gpu/display.h @@ -13,11 +13,11 @@ namespace skyline::gpu { u32 width; //!< The width component of the resolution u32 height; //!< The height component of the resolution - bool operator==(const Resolution &r) { + inline bool operator==(const Resolution &r) { return (width == r.width) && (height == r.height); } - bool operator!=(const Resolution &r) { + inline bool operator!=(const Resolution &r) { return !operator==(r); } }; @@ -58,8 +58,7 @@ namespace skyline::gpu { enum class BufferStatus { Free, Dequeued, - Queued, - Acquired + Queued }; /** @@ -106,7 +105,6 @@ namespace skyline::gpu { GbpBuffer gbpBuffer; //!< The information about the underlying buffer BufferStatus status{BufferStatus::Free}; //!< The status of this buffer std::vector dataBuffer; //!< The vector holding the actual pixel data - std::vector swizzBuffer; //!< The vector holding the swizzled pixel data std::shared_ptr nvBuffer{}; //!< A shared pointer to the buffer's nvmap object /** @@ -123,55 +121,12 @@ namespace skyline::gpu { }; /** - * @brief This holds the state of all the buffers used by the guest application + * @brief This is used to manage and queue up all display buffers to be shown */ class BufferQueue { private: const DeviceState &state; //!< The state of the device - /** - * @brief This is the input struct for DequeueBuffer - */ - struct DequeueIn { - u32 format; - u32 width; - u32 height; - u32 timestamps; - u32 usage; - }; - - /** - * @brief This is the output struct for DequeueBuffer - */ - struct DequeueOut { - u32 slot; //!< The slot of the dequeued buffer - u32 _unk0_; - u32 _unk1_; - u32 _unk2_[11]{}; - - /** - * @param slot The slot of the dequeued buffer - */ - DequeueOut(u32 slot); - }; - - /** - * @brief This holds the context of a thread waiting on a buffer - */ - struct WaitContext { - std::shared_ptr thread; //!< The thread that is waiting on a buffer - DequeueIn input; //!< The input of DequeueBuffer - kernel::ipc::OutputBuffer buffer; //!< The output buffer to write the parcel into - - /** - * @param thread The thread that is waiting on a buffer - * @param input The input of DequeueBuffer - * @param buffer The output buffer to write the parcel into - */ - WaitContext(std::shared_ptr thread, DequeueIn input, kernel::ipc::OutputBuffer& buffer); - }; - std::vector waitVec; //!< A vector of shared pointers to threads waiting on a buffer - public: std::unordered_map> queue; //!< A vector of shared pointers to all the queued buffers std::queue> displayQueue; //!< A queue of all the buffers to be posted to the display @@ -210,6 +165,8 @@ namespace skyline::gpu { * @brief This frees a buffer which is currently queued * @param slotNo The slot of the buffer */ - void FreeBuffer(u32 slotNo); + inline void FreeBuffer(u32 slotNo) { + queue.at(slotNo)->status = BufferStatus::Free; + } }; } diff --git a/app/src/main/cpp/skyline/gpu/parcel.cpp b/app/src/main/cpp/skyline/gpu/parcel.cpp index fd3a6d79..a624c583 100644 --- a/app/src/main/cpp/skyline/gpu/parcel.cpp +++ b/app/src/main/cpp/skyline/gpu/parcel.cpp @@ -17,7 +17,7 @@ namespace skyline::gpu { Parcel::Parcel(const DeviceState &state) : state(state) {} - u64 Parcel::WriteParcel(kernel::ipc::OutputBuffer& buffer) { + u64 Parcel::WriteParcel(kernel::ipc::OutputBuffer &buffer) { return WriteParcel(buffer.address, buffer.size); } diff --git a/app/src/main/cpp/skyline/gpu/parcel.h b/app/src/main/cpp/skyline/gpu/parcel.h index c0c66dcf..09de3265 100644 --- a/app/src/main/cpp/skyline/gpu/parcel.h +++ b/app/src/main/cpp/skyline/gpu/parcel.h @@ -82,7 +82,7 @@ namespace skyline::gpu { * @param buffer The buffer to write into * @return The total size of the message */ - u64 WriteParcel(kernel::ipc::OutputBuffer& buffer); + u64 WriteParcel(kernel::ipc::OutputBuffer &buffer); /** * @brief Writes the Parcel object into the process's memory diff --git a/app/src/main/cpp/skyline/jvm.cpp b/app/src/main/cpp/skyline/jvm.cpp index d14901a1..ed0694fb 100644 --- a/app/src/main/cpp/skyline/jvm.cpp +++ b/app/src/main/cpp/skyline/jvm.cpp @@ -5,21 +5,21 @@ thread_local JNIEnv *env; namespace skyline { JvmManager::JvmManager(JNIEnv *environ, jobject instance) : instance(instance), instanceClass(reinterpret_cast(environ->NewGlobalRef(environ->GetObjectClass(instance)))) { env = environ; - if(env->GetJavaVM(&vm) < 0) + if (env->GetJavaVM(&vm) < 0) throw exception("Cannot get JavaVM from environment"); } void JvmManager::AttachThread() { - if(!env) + if (!env) vm->AttachCurrentThread(&env, nullptr); } void JvmManager::DetachThread() { - if(env) + if (env) vm->DetachCurrentThread(); } - JNIEnv* JvmManager::GetEnv() { + JNIEnv *JvmManager::GetEnv() { return env; } @@ -31,7 +31,7 @@ namespace skyline { return env->IsSameObject(env->GetObjectField(instance, env->GetFieldID(instanceClass, key, signature)), nullptr); } - bool JvmManager::CheckNull(jobject& object) { + bool JvmManager::CheckNull(jobject &object) { return env->IsSameObject(object, nullptr); } } diff --git a/app/src/main/cpp/skyline/jvm.h b/app/src/main/cpp/skyline/jvm.h index ebf31d1a..ab630ac5 100644 --- a/app/src/main/cpp/skyline/jvm.h +++ b/app/src/main/cpp/skyline/jvm.h @@ -32,7 +32,7 @@ namespace skyline { /** * @brief Returns a pointer to the JNI environment for the current thread */ - JNIEnv* GetEnv(); + JNIEnv *GetEnv(); /** * @brief Retrieves a specific field of the given type from the activity diff --git a/app/src/main/cpp/skyline/kernel/ipc.h b/app/src/main/cpp/skyline/kernel/ipc.h index cbcb6081..3cee270b 100644 --- a/app/src/main/cpp/skyline/kernel/ipc.h +++ b/app/src/main/cpp/skyline/kernel/ipc.h @@ -259,8 +259,8 @@ namespace skyline::kernel::ipc { * @tparam ValueType The type of the object to read */ template - inline ValueType& Pop() { - ValueType& value = *reinterpret_cast(payloadOffset); + inline ValueType &Pop() { + ValueType &value = *reinterpret_cast(payloadOffset); payloadOffset += sizeof(ValueType); return value; } diff --git a/app/src/main/cpp/skyline/kernel/svc.cpp b/app/src/main/cpp/skyline/kernel/svc.cpp index 17fb6397..b8d1ffd5 100644 --- a/app/src/main/cpp/skyline/kernel/svc.cpp +++ b/app/src/main/cpp/skyline/kernel/svc.cpp @@ -4,7 +4,7 @@ namespace skyline::kernel::svc { void SetHeapSize(DeviceState &state) { const u32 size = state.ctx->registers.w1; - if(size%constant::HeapSizeDiv != 0) { + if (size % constant::HeapSizeDiv != 0) { state.ctx->registers.x1 = 0; state.ctx->registers.w0 = constant::status::InvSize; state.logger->Warn("svcSetHeapSize: 'size' not divisible by 2MB: {}", size); @@ -26,13 +26,13 @@ namespace skyline::kernel::svc { void SetMemoryAttribute(DeviceState &state) { const u64 addr = state.ctx->registers.x0; - if((addr & (PAGE_SIZE - 1U))) { + if ((addr & (PAGE_SIZE - 1U))) { state.ctx->registers.w0 = constant::status::InvAddress; state.logger->Warn("svcSetMemoryAttribute: 'address' not page aligned: {}", addr); return; } const u64 size = state.ctx->registers.x1; - if((size & (PAGE_SIZE - 1U)) || !size) { + if ((size & (PAGE_SIZE - 1U)) || !size) { state.ctx->registers.w0 = constant::status::InvSize; state.logger->Warn("svcSetMemoryAttribute: 'size' {}: {}", size ? "not page aligned" : "is zero", size); return; @@ -40,12 +40,12 @@ namespace skyline::kernel::svc { u32 mask = state.ctx->registers.w2; u32 value = state.ctx->registers.w3; u32 maskedValue = mask | value; - if(maskedValue != mask) { + if (maskedValue != mask) { state.ctx->registers.w0 = constant::status::InvCombination; state.logger->Warn("svcSetMemoryAttribute: 'mask' invalid: 0x{:X}, 0x{:X}", mask, value); return; } - memory::MemoryAttribute attribute = *reinterpret_cast(&maskedValue); + memory::MemoryAttribute attribute = *reinterpret_cast(&maskedValue); bool found = false; for (const auto&[address, region] : state.process->memoryMap) { if (addr >= address && addr < (address + region->size)) { @@ -62,7 +62,7 @@ namespace skyline::kernel::svc { break; } } - if(!found) { + if (!found) { state.ctx->registers.w0 = constant::status::InvAddress; state.logger->Warn("svcSetMemoryAttribute: Cannot find memory region: 0x{:X}", addr); return; @@ -73,7 +73,7 @@ namespace skyline::kernel::svc { void QueryMemory(DeviceState &state) { memory::MemoryInfo memInfo{}; - u64 addr = (state.ctx->registers.x2 & ~(PAGE_SIZE - 1)); + u64 addr = state.ctx->registers.x2 & ~(PAGE_SIZE - 1); bool found = false; for (const auto&[address, region] : state.process->memoryMap) { if (addr >= address && addr < (address + region->size)) { @@ -126,7 +126,7 @@ namespace skyline::kernel::svc { u64 entryArg = state.ctx->registers.x2; u64 stackTop = state.ctx->registers.x3; u8 priority = static_cast(state.ctx->registers.w4); - if((priority < constant::PriorityNin.first) && (priority > constant::PriorityNin.second)) { // NOLINT(misc-redundant-expression) + if ((priority < constant::PriorityNin.first) && (priority > constant::PriorityNin.second)) { // NOLINT(misc-redundant-expression) state.ctx->registers.w0 = constant::status::InvAddress; state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority); return; @@ -143,7 +143,7 @@ namespace skyline::kernel::svc { auto thread = state.process->GetHandle(handle); state.logger->Debug("svcStartThread: Starting thread: 0x{:X}, PID: {}", handle, thread->pid); thread->Start(); - } catch (const std::exception&) { + } catch (const std::exception &) { state.logger->Warn("svcStartThread: 'handle' invalid: 0x{:X}", handle); state.ctx->registers.w0 = constant::status::InvHandle; } @@ -161,12 +161,9 @@ namespace skyline::kernel::svc { case 1: case 2: state.logger->Debug("svcSleepThread: Yielding thread: {}", in); - state.thread->status = type::KThread::Status::Runnable; // Will cause the application to awaken on the next iteration of the main loop break; default: state.logger->Debug("svcSleepThread: Thread sleeping for {} ns", in); - state.thread->timeout = GetCurrTimeNs() + in; - state.thread->status = type::KThread::Status::Sleeping; } } @@ -177,7 +174,7 @@ namespace skyline::kernel::svc { state.ctx->registers.w1 = priority; state.ctx->registers.w0 = constant::status::Success; state.logger->Debug("svcGetThreadPriority: Writing thread priority {}", priority); - } catch (const std::exception&) { + } catch (const std::exception &) { state.logger->Warn("svcGetThreadPriority: 'handle' invalid: 0x{:X}", handle); state.ctx->registers.w0 = constant::status::InvHandle; } @@ -190,7 +187,7 @@ namespace skyline::kernel::svc { state.process->GetHandle(handle)->UpdatePriority(static_cast(priority)); state.ctx->registers.w0 = constant::status::Success; state.logger->Debug("svcSetThreadPriority: Setting thread priority to {}", priority); - } catch (const std::exception&) { + } catch (const std::exception &) { state.logger->Warn("svcSetThreadPriority: 'handle' invalid: 0x{:X}", handle); state.ctx->registers.w0 = constant::status::InvHandle; } @@ -259,7 +256,7 @@ namespace skyline::kernel::svc { state.process->handleTable.erase(handle); state.logger->Debug("svcCloseHandle: Closing handle: 0x{:X}", handle); state.ctx->registers.w0 = constant::status::Success; - } catch(const std::exception&) { + } catch (const std::exception &) { state.logger->Warn("svcCloseHandle: 'handle' invalid: 0x{:X}", handle); state.ctx->registers.w0 = constant::status::InvHandle; } @@ -284,7 +281,7 @@ namespace skyline::kernel::svc { } state.logger->Debug("svcResetSignal: Resetting signal: 0x{:X}", handle); state.ctx->registers.w0 = constant::status::Success; - } catch(const std::out_of_range&) { + } catch (const std::out_of_range &) { state.logger->Warn("svcResetSignal: 'handle' invalid: 0x{:X}", handle); state.ctx->registers.w0 = constant::status::InvHandle; return; @@ -298,9 +295,9 @@ namespace skyline::kernel::svc { return; } std::vector waitHandles(numHandles); + std::vector> objectTable; state.process->ReadMemory(waitHandles.data(), state.ctx->registers.x1, numHandles * sizeof(handle_t)); std::string handleStr; - uint index{}; for (const auto &handle : waitHandles) { handleStr += fmt::format("* 0x{:X}\n", handle); auto object = state.process->handleTable.at(handle); @@ -312,33 +309,34 @@ namespace skyline::kernel::svc { break; default: { state.ctx->registers.w0 = constant::status::InvHandle; - state.thread->ClearWaitObjects(); return; } } - auto syncObject = std::static_pointer_cast(object); - if (syncObject->signalled) { - state.logger->Debug("svcWaitSynchronization: Signalled handle: 0x{:X}", handle); - state.ctx->registers.w0 = constant::status::Success; - state.ctx->registers.w1 = index; - state.thread->ClearWaitObjects(); + objectTable.push_back(std::static_pointer_cast(object)); + } + state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, state.ctx->registers.x3); + auto timeout = state.ctx->registers.x3 + GetCurrTimeNs(); + while (true) { + uint index{}; + for (const auto &object : objectTable) { + if (object->signalled) { + state.logger->Debug("svcWaitSynchronization: Signalled handle: 0x{:X}", waitHandles.at(index)); + state.ctx->registers.w0 = constant::status::Success; + state.ctx->registers.w1 = index; + return; + } + index++; + } + if (GetCurrTimeNs() >= timeout) { + state.ctx->registers.w0 = constant::status::Timeout; return; } - state.thread->waitObjects.push_back(syncObject); - syncObject->waitThreads.emplace_back(state.thread->pid, index); } - auto timeout = state.ctx->registers.x3; - state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, timeout); - if (state.ctx->registers.x3 != std::numeric_limits::max()) - state.thread->timeout = GetCurrTimeNs() + timeout; - else - state.thread->timeout = 0; - state.thread->status = type::KThread::Status::WaitSync; } void ArbitrateLock(DeviceState &state) { auto addr = state.ctx->registers.x1; - if((addr & ((1UL << WORD_BIT) - 1U))) { + if ((addr & ((1UL << WORD_BIT) - 1U))) { state.ctx->registers.w0 = constant::status::InvAddress; state.logger->Warn("svcArbitrateLock: 'address' not word aligned: {}", addr); return; @@ -353,7 +351,7 @@ namespace skyline::kernel::svc { void ArbitrateUnlock(DeviceState &state) { auto addr = state.ctx->registers.x0; - if((addr & ((1UL << WORD_BIT) - 1U))) { + if ((addr & ((1UL << WORD_BIT) - 1U))) { state.ctx->registers.w0 = constant::status::InvAddress; state.logger->Warn("svcArbitrateUnlock: 'address' not word aligned: {}", addr); return; @@ -365,49 +363,53 @@ namespace skyline::kernel::svc { void WaitProcessWideKeyAtomic(DeviceState &state) { auto mtxAddr = state.ctx->registers.x0; - if((mtxAddr & ((1UL << WORD_BIT) - 1U))) { - state.ctx->registers.w0 = constant::status::InvAddress; - state.logger->Warn("svcWaitProcessWideKeyAtomic: mutex address not word aligned: {}", mtxAddr); - return; - } - auto handle = state.ctx->registers.w2; - if (handle != state.thread->handle) - throw exception("svcWaitProcessWideKeyAtomic: Called from another thread"); - state.process->MutexUnlock(mtxAddr); auto condAddr = state.ctx->registers.x1; - auto &cvarVec = state.process->condVarMap[condAddr]; - for (auto thread = cvarVec.begin();; thread++) { - if ((*thread)->priority < state.thread->priority) { - cvarVec.insert(thread, state.thread); - break; - } else if (thread + 1 == cvarVec.end()) { - cvarVec.push_back(state.thread); - break; + try { + auto &cvar = state.process->condVarMap.at(condAddr); + if ((mtxAddr & ((1UL << WORD_BIT) - 1U))) { + state.ctx->registers.w0 = constant::status::InvAddress; + state.logger->Warn("svcWaitProcessWideKeyAtomic: mutex address not word aligned: {}", mtxAddr); + return; } + auto handle = state.ctx->registers.w2; + if (handle != state.thread->handle) + throw exception("svcWaitProcessWideKeyAtomic: Called from another thread"); + state.process->MutexLock(mtxAddr); + auto &mutex = state.process->mutexMap.at(mtxAddr); + auto timeout = state.ctx->registers.x3; + state.logger->Debug("svcWaitProcessWideKeyAtomic: Mutex: 0x{:X}, Conditional-Variable: 0x:{:X}, Timeout: {} ns", mtxAddr, condAddr, timeout); + timespec spec{}; + clock_gettime(CLOCK_REALTIME, &spec); + u128 time = u128(spec.tv_sec * 1000000000U + spec.tv_nsec) + timeout; // u128 to prevent overflow + spec.tv_sec = static_cast(time / 1000000000U); + spec.tv_nsec = static_cast(time % 1000000000U); + if (pthread_cond_timedwait(&cvar, &mutex, &spec) == ETIMEDOUT) + state.ctx->registers.w0 = constant::status::Timeout; + else + state.ctx->registers.w0 = constant::status::Success; + state.process->MutexUnlock(mtxAddr); + } catch (const std::out_of_range &) { + state.logger->Debug("svcWaitProcessWideKeyAtomic: No Conditional-Variable at 0x{:X}", condAddr); + state.process->condVarMap[condAddr] = PTHREAD_COND_INITIALIZER; + state.ctx->registers.w0 = constant::status::Success; } - auto timeout = state.ctx->registers.x3; - state.logger->Debug("svcWaitProcessWideKeyAtomic: Mutex: 0x{:X}, Conditional-Variable: 0x:{:X}, Timeout: {} ns", mtxAddr, condAddr, timeout); - state.thread->status = type::KThread::Status::WaitCondVar; - state.thread->timeout = GetCurrTimeNs() + timeout; - state.ctx->registers.w0 = constant::status::Success; } void SignalProcessWideKey(DeviceState &state) { auto address = state.ctx->registers.x0; - auto count = state.ctx->registers.w1; - state.ctx->registers.w0 = constant::status::Success; - if (!state.process->condVarMap.count(address)) { + auto count = state.ctx->registers.x1; + try { + state.logger->Debug("svcSignalProcessWideKey: Signalling Conditional-Variable at 0x{:X} for {}", address, count); + auto &cvar = state.process->condVarMap.at(address); + /* + for (u32 iter = 0; iter < count; iter++) + pthread_cond_signal(&cvar); + */ + } catch (const std::out_of_range &) { state.logger->Debug("svcSignalProcessWideKey: No Conditional-Variable at 0x{:X}", address); - return; + state.process->condVarMap[address] = PTHREAD_COND_INITIALIZER; } - auto &cvarVec = state.process->condVarMap.at(address); - count = std::min(count, static_cast(cvarVec.size())); - for (uint index = 0; index < count; index++) - cvarVec[index]->status = type::KThread::Status::Runnable; - cvarVec.erase(cvarVec.begin(), cvarVec.begin() + count); - if (cvarVec.empty()) - state.process->condVarMap.erase(address); - state.logger->Debug("svcSignalProcessWideKey: Signalling Conditional-Variable at 0x{:X} for {}", address, count); + state.ctx->registers.w0 = constant::status::Success; } void GetSystemTick(DeviceState &state) { @@ -459,7 +461,7 @@ namespace skyline::kernel::svc { void OutputDebugString(DeviceState &state) { std::string debug(state.ctx->registers.x1, '\0'); state.process->ReadMemory(debug.data(), state.ctx->registers.x0, state.ctx->registers.x1); - if(debug.back() == '\n') + if (debug.back() == '\n') debug.pop_back(); state.logger->Info("Debug Output: {}", debug); state.ctx->registers.w0 = constant::status::Success; diff --git a/app/src/main/cpp/skyline/kernel/svc.cpp___jb_old___ b/app/src/main/cpp/skyline/kernel/svc.cpp___jb_old___ deleted file mode 100644 index a3107ceb..00000000 --- a/app/src/main/cpp/skyline/kernel/svc.cpp___jb_old___ +++ /dev/null @@ -1,534 +0,0 @@ -#include "svc.h" -#include - -namespace skyline::kernel::svc { - void SetHeapSize(DeviceState &state) { - const u32 size = state.ctx->registers.w1; - if(size%constant::HeapSizeDiv != 0) { - state.ctx->registers.x1 = 0; - state.ctx->registers.w0 = constant::status::InvSize; - state.logger->Warn("svcSetHeapSize: 'size' not divisible by 2MB: {}", size); - return; - } - std::shared_ptr heap; - try { - heap = state.process->memoryRegionMap.at(memory::Region::Heap); - heap->Resize(size, true); - } catch (const exception &) { - state.logger->Warn("svcSetHeapSize: Falling back to recreating memory"); - state.process->UnmapPrivateRegion(memory::Region::Heap); - heap = state.process->MapPrivateRegion(constant::HeapAddr, size, {true, true, false}, memory::Type::Heap, memory::Region::Heap).item; - } - state.ctx->registers.w0 = constant::status::Success; - state.ctx->registers.x1 = heap->address; - state.logger->Debug("svcSetHeapSize: Allocated at 0x{:X} for 0x{:X} bytes", heap->address, heap->size); - } - - void SetMemoryAttribute(DeviceState &state) { - const u64 addr = state.ctx->registers.x0; - if((addr & (PAGE_SIZE - 1U))) { - state.ctx->registers.w0 = constant::status::InvAddress; - state.logger->Warn("svcSetMemoryAttribute: 'address' not page aligned: {}", addr); - return; - } - const u64 size = state.ctx->registers.x1; - if((size & (PAGE_SIZE - 1U)) || !size) { - state.ctx->registers.w0 = constant::status::InvSize; - state.logger->Warn("svcSetMemoryAttribute: 'size' {}: {}", size ? "not page aligned" : "is zero", size); - return; - } - u32 mask = state.ctx->registers.w2; - u32 value = state.ctx->registers.w3; - u32 maskedValue = mask | value; - if(maskedValue != mask) { - state.ctx->registers.w0 = constant::status::InvCombination; - state.logger->Warn("svcSetMemoryAttribute: 'mask' invalid: 0x{:X}, 0x{:X}", mask, value); - return; - } - memory::MemoryAttribute attribute = *reinterpret_cast(&maskedValue); - bool found = false; - for (const auto&[address, region] : state.process->memoryMap) { - if (addr >= address && addr < (address + region->size)) { - bool subFound = false; - for (auto &subregion : region->regionInfoVec) { - if ((address >= subregion.address) && (address < (subregion.address + subregion.size))) - subregion.isUncached = attribute.isUncached; - subFound = true; - break; - } - if (!subFound) - region->regionInfoVec.emplace_back(addr, size, static_cast(attribute.isUncached)); - found = true; - break; - } - } - if(!found) { - state.ctx->registers.w0 = constant::status::InvAddress; - state.logger->Warn("svcSetMemoryAttribute: Cannot find memory region: 0x{:X}", addr); - return; - } - state.logger->Debug("svcSetMemoryAttribute: Set caching to {} at 0x{:X} for 0x{:X} bytes", !attribute.isUncached, addr, size); - state.ctx->registers.w0 = constant::status::Success; - } - - void QueryMemory(DeviceState &state) { - memory::MemoryInfo memInfo{}; - u64 addr = (state.ctx->registers.x2 & ~(PAGE_SIZE-1)); - bool found = false; - for (const auto&[address, region] : state.process->memoryMap) { - if (addr >= address && addr < (address + region->size)) { - memInfo = region->GetInfo(addr); - found = true; - break; - } - } - if (!found) { - for (const auto &object : state.process->handleTable) { - if (object.second->objectType == type::KType::KSharedMemory) { - const auto &mem = state.process->GetHandle(object.first); - if (mem->guest.valid()) { - if (addr >= mem->guest.address && addr < (mem->guest.address + mem->guest.size)) { - memInfo = mem->GetInfo(); - found = true; - break; - } - } - } else if (object.second->objectType == type::KType::KTransferMemory) { - const auto &mem = state.process->GetHandle(object.first); - if (addr >= mem->cAddress && addr < (mem->cAddress + mem->cSize)) { - memInfo = mem->GetInfo(); - found = true; - break; - } - } - } - if (!found) { - memInfo = { - .baseAddress = constant::BaseAddr, - .size = static_cast(constant::BaseEnd), - .type = static_cast(memory::Type::Unmapped) - }; - state.logger->Debug("svcQueryMemory: Cannot find block of address: 0x{:X}", addr); - } - } - state.logger->Debug("svcQueryMemory: Address: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Is Uncached: {}, Permissions: {}{}{}", memInfo.baseAddress, memInfo.size, memInfo.type, static_cast(memInfo.memoryAttribute.isUncached), memInfo.r ? "R" : "-", memInfo.w ? "W" : "-", memInfo.x ? "X" : "-"); - state.process->WriteMemory(memInfo, state.ctx->registers.x0); - state.ctx->registers.w0 = constant::status::Success; - } - - void ExitProcess(DeviceState &state) { - state.logger->Debug("svcExitProcess: Exiting current process: {}", state.process->pid); - state.os->KillThread(state.process->pid); - } - - void CreateThread(DeviceState &state) { - u64 entryAddr = state.ctx->registers.x1; - u64 entryArg = state.ctx->registers.x2; - u64 stackTop = state.ctx->registers.x3; - u8 priority = static_cast(state.ctx->registers.w4); - if((priority < constant::PriorityNin.first) && (priority > constant::PriorityNin.second)) { // NOLINT(misc-redundant-expression) - state.ctx->registers.w0 = constant::status::InvAddress; - state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority); - return; - } - auto thread = state.process->CreateThread(entryAddr, entryArg, stackTop, priority); - state.logger->Debug("svcCreateThread: Created thread with handle 0x{:X} (Entry Point: 0x{:X}, Argument: 0x{:X}, Stack Pointer: 0x{:X}, Priority: {}, PID: {})", thread->handle, entryAddr, entryArg, stackTop, priority, thread->pid); - state.ctx->registers.w1 = thread->handle; - state.ctx->registers.w0 = constant::status::Success; - } - - void StartThread(DeviceState &state) { - auto handle = state.ctx->registers.w0; - try { - auto thread = state.process->GetHandle(handle); - state.logger->Debug("svcStartThread: Starting thread: 0x{:X}, PID: {}", handle, thread->pid); - thread->Start(); - } catch (const std::exception&) { - state.logger->Warn("svcStartThread: 'handle' invalid: 0x{:X}", handle); - state.ctx->registers.w0 = constant::status::InvHandle; - } - } - - void ExitThread(DeviceState &state) { - state.logger->Debug("svcExitProcess: Exiting current thread: {}", state.thread->pid); - state.os->KillThread(state.thread->pid); - } - - void SleepThread(DeviceState &state) { - auto in = state.ctx->registers.x0; - switch (in) { - case 0: - case 1: - case 2: - state.logger->Debug("svcSleepThread: Yielding thread: {}", in); - state.thread->status = type::KThread::Status::Runnable; // Will cause the application to awaken on the next iteration of the main loop - break; - default: - state.logger->Debug("svcSleepThread: Thread sleeping for {} ns", in); - state.thread->timeout = GetCurrTimeNs() + in; - state.thread->status = type::KThread::Status::Sleeping; - } - } - - void GetThreadPriority(DeviceState &state) { - auto handle = state.ctx->registers.w0; - try { - auto priority = state.process->GetHandle(handle)->priority; - state.ctx->registers.w1 = priority; - state.ctx->registers.w0 = constant::status::Success; - state.logger->Debug("svcGetThreadPriority: Writing thread priority {}", priority); - } catch (const std::exception&) { - state.logger->Warn("svcGetThreadPriority: 'handle' invalid: 0x{:X}", handle); - state.ctx->registers.w0 = constant::status::InvHandle; - } - } - - void SetThreadPriority(DeviceState &state) { - auto handle = state.ctx->registers.w0; - auto priority = state.ctx->registers.w1; - try { - state.process->GetHandle(handle)->UpdatePriority(static_cast(priority)); - state.ctx->registers.w0 = constant::status::Success; - state.logger->Debug("svcSetThreadPriority: Setting thread priority to {}", priority); - } catch (const std::exception&) { - state.logger->Warn("svcSetThreadPriority: 'handle' invalid: 0x{:X}", handle); - state.ctx->registers.w0 = constant::status::InvHandle; - } - } - - void MapSharedMemory(DeviceState &state) { - try { - auto object = state.process->GetHandle(state.ctx->registers.w0); - u64 addr = state.ctx->registers.x1; - if ((addr & (PAGE_SIZE - 1U))) { - state.ctx->registers.w0 = constant::status::InvAddress; - state.logger->Warn("svcMapSharedMemory: 'address' not page aligned: 0x{:X}", addr); - return; - } - const u64 size = state.ctx->registers.x2; - if ((size & (PAGE_SIZE - 1U)) || !size) { - state.ctx->registers.w0 = constant::status::InvSize; - state.logger->Warn("svcMapSharedMemory: 'size' {}: {}", size ? "not page aligned" : "is zero", size); - return; - } - u32 perm = state.ctx->registers.w3; - memory::Permission permission = *reinterpret_cast(&perm); - if ((permission.w && !permission.r) || (permission.x && !permission.r)) { - state.logger->Warn("svcMapSharedMemory: 'permission' invalid: {}{}{}", permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-"); - state.ctx->registers.w0 = constant::status::InvPermission; - return; - } - state.logger->Debug("svcMapSharedMemory: Mapping shared memory at 0x{:X} for {} bytes ({}{}{})", addr, size, permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-"); - object->Map(addr, size, permission); - state.ctx->registers.w0 = constant::status::Success; - } catch (const std::exception &) { - state.logger->Warn("svcMapSharedMemory: 'handle' invalid: 0x{:X}", state.ctx->registers.w0); - state.ctx->registers.w0 = constant::status::InvHandle; - } - } - - void CreateTransferMemory(DeviceState &state) { - u64 addr = state.ctx->registers.x1; - if ((addr & (PAGE_SIZE - 1U))) { - state.ctx->registers.w0 = constant::status::InvAddress; - state.logger->Warn("svcCreateTransferMemory: 'address' not page aligned: {}", addr); - return; - } - u64 size = state.ctx->registers.x2; - if ((size & (PAGE_SIZE - 1U)) || !size) { - state.ctx->registers.w0 = constant::status::InvSize; - state.logger->Warn("svcCreateTransferMemory: 'size' {}: {}", size ? "not page aligned" : "is zero", size); - return; - } - u32 perm = state.ctx->registers.w3; - memory::Permission permission = *reinterpret_cast(&perm); - if ((permission.w && !permission.r) || (permission.x && !permission.r)) { - state.logger->Warn("svcCreateTransferMemory: 'permission' invalid: {}{}{}", permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-"); - state.ctx->registers.w0 = constant::status::InvPermission; - return; - } - state.logger->Debug("svcCreateTransferMemory: Creating transfer memory at 0x{:X} for {} bytes ({}{}{})", addr, size, permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-"); - auto shmem = state.process->NewHandle(state.process->pid, addr, size, permission); - state.ctx->registers.w0 = constant::status::Success; - state.ctx->registers.w1 = shmem.handle; - } - - void CloseHandle(DeviceState &state) { - auto handle = static_cast(state.ctx->registers.w0); - try { - state.process->handleTable.erase(handle); - state.logger->Debug("svcCloseHandle: Closing handle: 0x{:X}", handle); - state.ctx->registers.w0 = constant::status::Success; - } catch(const std::exception&) { - state.logger->Warn("svcCloseHandle: 'handle' invalid: 0x{:X}", handle); - state.ctx->registers.w0 = constant::status::InvHandle; - } - } - - void ResetSignal(DeviceState &state) { - auto handle = state.ctx->registers.w0; - try { - auto &object = state.process->handleTable.at(handle); - switch (object->objectType) { - case (type::KType::KEvent): - std::static_pointer_cast(object)->ResetSignal(); - break; - case (type::KType::KProcess): - std::static_pointer_cast(object)->ResetSignal(); - break; - default: { - state.logger->Warn("svcResetSignal: 'handle' type invalid: 0x{:X} ({})", handle, object->objectType); - state.ctx->registers.w0 = constant::status::InvHandle; - return; - } - } - state.logger->Debug("svcResetSignal: Resetting signal: 0x{:X}", handle); - state.ctx->registers.w0 = constant::status::Success; - } catch(const std::out_of_range&) { - state.logger->Warn("svcResetSignal: 'handle' invalid: 0x{:X}", handle); - state.ctx->registers.w0 = constant::status::InvHandle; - return; - } - } - - void WaitSynchronization(DeviceState &state) { - auto numHandles = state.ctx->registers.w2; - if (numHandles > constant::MaxSyncHandles) { - state.ctx->registers.w0 = constant::status::MaxHandles; - return; - } - std::vector waitHandles(numHandles); - state.process->ReadMemory(waitHandles.data(), state.ctx->registers.x1, numHandles * sizeof(handle_t)); - std::string handleStr; - uint index{}; - for (const auto &handle : waitHandles) { - handleStr += fmt::format("* 0x{:X}\n", handle); - auto object = state.process->handleTable.at(handle); - switch (object->objectType) { - case type::KType::KProcess: - case type::KType::KThread: - case type::KType::KEvent: - case type::KType::KSession: - break; - default: { - state.ctx->registers.w0 = constant::status::InvHandle; - state.thread->ClearWaitObjects(); - return; - } - } - auto syncObject = std::static_pointer_cast(object); - if (syncObject->signalled) { - state.logger->Debug("svcWaitSynchronization: Signalled handle: 0x{:X}", handle); - state.ctx->registers.w0 = constant::status::Success; - state.ctx->registers.w1 = index; - state.thread->ClearWaitObjects(); - return; - } - state.thread->waitObjects.push_back(syncObject); - syncObject->waitThreads.emplace_back(state.thread->pid, index); - } - auto timeout = state.ctx->registers.x3; - state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, timeout); - if (state.ctx->registers.x3 != std::numeric_limits::max()) - state.thread->timeout = GetCurrTimeNs() + timeout; - else - state.thread->timeout = 0; - state.thread->status = type::KThread::Status::WaitSync; - } - - void ArbitrateLock(DeviceState &state) { - auto addr = state.ctx->registers.x1; - if((addr & ((1UL << WORD_BIT) - 1U))) { - state.ctx->registers.w0 = constant::status::InvAddress; - state.logger->Warn("svcArbitrateLock: 'address' not word aligned: {}", addr); - return; - } - auto handle = state.ctx->registers.w2; - if (handle != state.thread->handle) - throw exception("svcArbitrateLock: Called from another thread"); - state.logger->Debug("svcArbitrateLock: Locking mutex at 0x{:X} for thread 0x{:X}", addr, handle); - state.process->MutexLock(addr); - state.ctx->registers.w0 = constant::status::Success; - } - - void ArbitrateUnlock(DeviceState &state) { - auto addr = state.ctx->registers.x0; - if((addr & ((1UL << WORD_BIT) - 1U))) { - state.ctx->registers.w0 = constant::status::InvAddress; - state.logger->Warn("svcArbitrateUnlock: 'address' not word aligned: {}", addr); - return; - } - state.logger->Debug("svcArbitrateUnlock: Unlocking mutex at 0x{:X}", addr); - state.process->MutexUnlock(addr); - state.ctx->registers.w0 = constant::status::Success; - } - - void WaitProcessWideKeyAtomic(DeviceState &state) { - auto mtxAddr = state.ctx->registers.x0; - if((mtxAddr & ((1UL << WORD_BIT) - 1U))) { - state.ctx->registers.w0 = constant::status::InvAddress; - state.logger->Warn("svcWaitProcessWideKeyAtomic: mutex address not word aligned: {}", mtxAddr); - return; - } - auto handle = state.ctx->registers.w2; - if (handle != state.thread->handle) - throw exception("svcWaitProcessWideKeyAtomic: Called from another thread"); - state.process->MutexUnlock(mtxAddr); - auto condAddr = state.ctx->registers.x1; - auto &cvarVec = state.process->condVarMap[condAddr]; - for (auto thread = cvarVec.begin();; thread++) { - if ((*thread)->priority < state.thread->priority) { - cvarVec.insert(thread, state.thread); - break; - } else if (thread + 1 == cvarVec.end()) { - cvarVec.push_back(state.thread); - break; - } - } - auto timeout = state.ctx->registers.x3; - state.logger->Debug("svcWaitProcessWideKeyAtomic: Mutex: 0x{:X}, Conditional-Variable: 0x:{:X}, Timeout: {} ns", mtxAddr, condAddr, timeout); - state.thread->status = type::KThread::Status::WaitCondVar; - state.thread->timeout = GetCurrTimeNs() + timeout; - state.ctx->registers.w0 = constant::status::Success; - } - - void SignalProcessWideKey(DeviceState &state) { - auto address = state.ctx->registers.x0; - auto count = state.ctx->registers.w1; - state.ctx->registers.w0 = constant::status::Success; - if (!state.process->condVarMap.count(address)) { - state.logger->Debug("svcSignalProcessWideKey: No Conditional-Variable at 0x{:X}", address); - return; - } - auto &cvarVec = state.process->condVarMap.at(address); - count = std::min(count, static_cast(cvarVec.size())); - for (uint index = 0; index < count; index++) - cvarVec[index]->status = type::KThread::Status::Runnable; - cvarVec.erase(cvarVec.begin(), cvarVec.begin() + count); - if (cvarVec.empty()) - state.process->condVarMap.erase(address); - state.logger->Debug("svcSignalProcessWideKey: Signalling Conditional-Variable at 0x{:X} for {}", address, count); - } - - void GetSystemTick(DeviceState &state) { - u64 tick; - asm("STR X1, [SP, #-16]!\n\t" - "MRS %0, CNTVCT_EL0\n\t" - "MOV X1, #0xF800\n\t" - "MOVK X1, #0x124, lsl #16\n\t" - "MUL %0, %0, X1\n\t" - "MRS X1, CNTFRQ_EL0\n\t" - "UDIV %0, %0, X1\n\t" - "LDR X1, [SP], #16" : "=r"(tick)); - state.ctx->registers.x0 = tick; - } - - void ConnectToNamedPort(DeviceState &state) { - char port[constant::PortSize + 1]{0}; - state.process->ReadMemory(port, state.ctx->registers.x1, constant::PortSize); - handle_t handle{}; - if (std::strcmp(port, "sm:") == 0) - handle = state.os->serviceManager.NewSession(service::Service::sm); - else { - state.logger->Warn("svcConnectToNamedPort: Connecting to invalid port: '{}'", port); - state.ctx->registers.w0 = constant::status::NotFound; - return; - } - state.logger->Debug("svcConnectToNamedPort: Connecting to port '{}' at 0x{:X}", port, handle); - state.ctx->registers.w1 = handle; - state.ctx->registers.w0 = constant::status::Success; - } - - void SendSyncRequest(DeviceState &state) { - state.os->serviceManager.SyncRequestHandler(static_cast(state.ctx->registers.x0)); - state.ctx->registers.w0 = constant::status::Success; - } - - void GetThreadId(DeviceState &state) { - pid_t pid{}; - auto handle = state.ctx->registers.w1; - if (handle != constant::ThreadSelf) { - pid = state.process->GetHandle(handle)->pid; - } else - pid = state.thread->pid; - state.logger->Debug("svcGetThreadId: Handle: 0x{:X}, PID: {}", handle, pid); - state.ctx->registers.x1 = static_cast(pid); - state.ctx->registers.w0 = constant::status::Success; - } - - void OutputDebugString(DeviceState &state) { - std::string debug(state.ctx->registers.x1, '\0'); - state.process->ReadMemory(debug.data(), state.ctx->registers.x0, state.ctx->registers.x1); - if(debug.back() == '\n') - debug.pop_back(); - state.logger->Info("Debug Output: {}", debug); - state.ctx->registers.w0 = constant::status::Success; - } - - void GetInfo(DeviceState &state) { - auto id0 = state.ctx->registers.w1; - auto handle = state.ctx->registers.w2; - auto id1 = state.ctx->registers.x3; - u64 out{}; - switch (id0) { - case constant::infoState::AllowedCpuIdBitmask: - case constant::infoState::AllowedThreadPriorityMask: - case constant::infoState::IsCurrentProcessBeingDebugged: - case constant::infoState::TitleId: - case constant::infoState::PrivilegedProcessId: - break; - case constant::infoState::AliasRegionBaseAddr: - out = constant::MapAddr; - break; - case constant::infoState::AliasRegionSize: - out = constant::MapSize; - break; - case constant::infoState::HeapRegionBaseAddr: - out = state.process->memoryRegionMap.at(memory::Region::Heap)->address; - break; - case constant::infoState::HeapRegionSize: - out = state.process->memoryRegionMap.at(memory::Region::Heap)->size; - break; - case constant::infoState::TotalMemoryAvailable: - out = constant::TotalPhyMem; - break; - case constant::infoState::TotalMemoryUsage: - out = state.process->memoryRegionMap.at(memory::Region::Heap)->address + state.process->mainThreadStackSz + state.process->GetProgramSize(); - break; - case constant::infoState::AddressSpaceBaseAddr: - out = constant::BaseAddr; - break; - case constant::infoState::AddressSpaceSize: - out = constant::BaseEnd; - break; - case constant::infoState::StackRegionBaseAddr: - out = state.thread->stackTop; - break; - case constant::infoState::StackRegionSize: - out = state.process->mainThreadStackSz; - break; - case constant::infoState::PersonalMmHeapSize: - out = constant::TotalPhyMem; - break; - case constant::infoState::PersonalMmHeapUsage: - out = state.process->memoryRegionMap.at(memory::Region::Heap)->address + state.process->mainThreadStackSz; - break; - case constant::infoState::TotalMemoryAvailableWithoutMmHeap: - out = constant::TotalPhyMem; // TODO: NPDM specifies SystemResourceSize, subtract that from this - break; - case constant::infoState::TotalMemoryUsedWithoutMmHeap: - out = state.process->memoryRegionMap.at(memory::Region::Heap)->address + state.process->mainThreadStackSz; // TODO: Same as above - break; - case constant::infoState::UserExceptionContextAddr: - out = state.process->tlsPages[0]->Get(0); - break; - default: - state.logger->Warn("svcGetInfo: Unimplemented case ID0: {}, ID1: {}", id0, id1); - state.ctx->registers.w0 = constant::status::Unimpl; - return; - } - state.logger->Debug("svcGetInfo: ID0: {}, ID1: {}, Out: 0x{:X}", id0, id1, out); - state.ctx->registers.x1 = out; - state.ctx->registers.w0 = constant::status::Success; - } -} diff --git a/app/src/main/cpp/skyline/kernel/svc.h b/app/src/main/cpp/skyline/kernel/svc.h index 81332151..080e2aa3 100644 --- a/app/src/main/cpp/skyline/kernel/svc.h +++ b/app/src/main/cpp/skyline/kernel/svc.h @@ -110,6 +110,7 @@ namespace skyline { * @brief Stalls a thread till a KSyncObject signals or the timeout has ended (https://switchbrew.org/wiki/SVC#svcWaitSynchronization) */ void WaitSynchronization(DeviceState &state); + /** * @brief Locks a specified mutex */ diff --git a/app/src/main/cpp/skyline/kernel/types/KEvent.h b/app/src/main/cpp/skyline/kernel/types/KEvent.h index eb529961..3eca2d90 100644 --- a/app/src/main/cpp/skyline/kernel/types/KEvent.h +++ b/app/src/main/cpp/skyline/kernel/types/KEvent.h @@ -13,16 +13,6 @@ namespace skyline::kernel::type { */ KEvent(const DeviceState &state) : KSyncObject(state, KType::KEvent) {} - /** - * @brief Signals all threads waiting on this object - */ - virtual inline void Signal() { - if (!signalled) { - KSyncObject::Signal(); - signalled = true; - } - } - /** * @brief Resets the KEvent to an unsignalled state */ diff --git a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp index ab7855d4..28c6662b 100644 --- a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp @@ -9,7 +9,7 @@ namespace skyline::kernel::type { fregs.x0 = dstAddress; fregs.x1 = size; fregs.x2 = static_cast(permission.Get()); - fregs.x3 = static_cast(MAP_PRIVATE | MAP_ANONYMOUS | ((dstAddress) ? MAP_FIXED : 0)); // NOLINT(hicpp-signed-bitwise) + fregs.x3 = static_cast(MAP_PRIVATE | MAP_ANONYMOUS | ((dstAddress) ? MAP_FIXED : 0)); fregs.x4 = static_cast(-1); fregs.x8 = __NR_mmap; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, thread); @@ -79,13 +79,14 @@ namespace skyline::kernel::type { KPrivateMemory::~KPrivateMemory() { try { - if(state.process) { + if (state.process) { Registers fregs{}; fregs.x0 = address; fregs.x1 = size; fregs.x8 = __NR_munmap; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.process->pid); } - } catch (const std::exception &) {} + } catch (const std::exception &) { + } } }; diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp index c1532f3d..50c232e1 100644 --- a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp @@ -3,7 +3,6 @@ #include #include #include -#include namespace skyline::kernel::type { KProcess::TlsPage::TlsPage(u64 address) : address(address) {} @@ -39,20 +38,21 @@ namespace skyline::kernel::type { return tlsPage->ReserveSlot(); } - KProcess::KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, u64 stackBase, u64 stackSize, std::shared_ptr& tlsMemory) : pid(pid), mainThreadStackSz(stackSize), KSyncObject(state, KType::KProcess) { + KProcess::KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, u64 stackBase, u64 stackSize, std::shared_ptr &tlsMemory) : pid(pid), mainThreadStackSz(stackSize), KSyncObject(state, KType::KProcess) { auto thread = NewHandle(pid, entryPoint, 0x0, stackBase + stackSize, 0, constant::DefaultPriority, this, tlsMemory).item; // Remove GetTlsSlot from KThread ctor and cleanup ctor in general threadMap[pid] = thread; state.nce->WaitThreadInit(thread); thread->tls = GetTlsSlot(); MapPrivateRegion(constant::HeapAddr, constant::DefHeapSize, {true, true, false}, memory::Type::Heap, memory::Region::Heap); - memFd = open(fmt::format("/proc/{}/mem", pid).c_str(), O_RDWR | O_CLOEXEC); // NOLINT(hicpp-signed-bitwise) + 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; } /** @@ -64,7 +64,7 @@ namespace skyline::kernel::type { } u64 CreateThreadFunc(u64 stackTop) { - pid_t pid = clone(&ExecuteChild, reinterpret_cast(stackTop), CLONE_THREAD | CLONE_SIGHAND | CLONE_PTRACE | CLONE_FS | CLONE_VM | CLONE_FILES | CLONE_IO, nullptr); // NOLINT(hicpp-signed-bitwise) + pid_t pid = clone(&ExecuteChild, reinterpret_cast(stackTop), CLONE_THREAD | CLONE_SIGHAND | CLONE_PTRACE | CLONE_FS | CLONE_VM | CLONE_FILES | CLONE_IO, nullptr); return static_cast(pid); } @@ -85,7 +85,7 @@ namespace skyline::kernel::type { threadMap[pid] = process; return process; */ - return 0; + return nullptr; } void KProcess::ReadMemory(void *destination, u64 offset, size_t size) const { @@ -96,10 +96,6 @@ namespace skyline::kernel::type { pwrite64(memFd, source, size, offset); } - int KProcess::GetMemoryFd() const { - return memFd; - } - KProcess::HandleOut KProcess::MapPrivateRegion(u64 address, size_t size, const memory::Permission perms, const memory::Type type, const memory::Region region) { auto mem = NewHandle(address, size, perms, type, threadMap.at(pid)); memoryMap[mem.item->address] = mem.item; @@ -123,44 +119,28 @@ namespace skyline::kernel::type { } void KProcess::MutexLock(u64 address) { - auto mtxVec = state.process->mutexMap[address]; - u32 mtxVal = state.process->ReadMemory(address); - if (mtxVec.empty()) { + try { + auto mtx = mutexMap.at(address); + pthread_mutex_lock(&mtx); + u32 mtxVal = ReadMemory(address); mtxVal = (mtxVal & ~constant::MtxOwnerMask) | state.thread->handle; - state.process->WriteMemory(mtxVal, address); - } else { - for (auto thread = mtxVec.begin();; thread++) { - if ((*thread)->priority < state.thread->priority) { - mtxVec.insert(thread, state.thread); - break; - } else if (thread + 1 == mtxVec.end()) { - mtxVec.push_back(state.thread); - break; - } - } - state.thread->status = KThread::Status::WaitMutex; + WriteMemory(mtxVal, address); + } catch (const std::out_of_range &) { + mutexMap[address] = PTHREAD_MUTEX_INITIALIZER; } } void KProcess::MutexUnlock(u64 address) { - auto mtxVec = state.process->mutexMap[address]; - u32 mtxVal = state.process->ReadMemory(address); - if ((mtxVal & constant::MtxOwnerMask) != state.thread->pid) - throw exception("A non-owner thread tried to release a mutex"); - if (mtxVec.empty()) { + try { + auto mtx = mutexMap.at(address); + u32 mtxVal = ReadMemory(address); + if ((mtxVal & constant::MtxOwnerMask) != state.thread->handle) + throw exception("A non-owner thread tried to release a mutex"); mtxVal = 0; - } else { - auto &thread = mtxVec.front(); - mtxVal = (mtxVal & ~constant::MtxOwnerMask) | thread->handle; - thread->status = KThread::Status::Runnable; - mtxVec.erase(mtxVec.begin()); - if (!mtxVec.empty()) - mtxVal |= (~constant::MtxOwnerMask); + WriteMemory(mtxVal, address); + pthread_mutex_unlock(&mtx); + } catch (const std::out_of_range &) { + mutexMap[address] = PTHREAD_MUTEX_INITIALIZER; } - state.process->WriteMemory(mtxVal, address); - } - - void KProcess::ResetSignal() { - signalled = false; } } diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.h b/app/src/main/cpp/skyline/kernel/types/KProcess.h index 432404a8..9eff4fef 100644 --- a/app/src/main/cpp/skyline/kernel/types/KProcess.h +++ b/app/src/main/cpp/skyline/kernel/types/KProcess.h @@ -6,6 +6,7 @@ #include "KSharedMemory.h" #include "KSession.h" #include "KEvent.h" +#include namespace skyline::kernel::type { /** @@ -70,10 +71,9 @@ namespace skyline::kernel::type { std::unordered_map> memoryRegionMap; //!< A mapping from every MemoryRegion to a shared pointer of it's corresponding KPrivateMemory std::unordered_map> handleTable; //!< A mapping from a handle_t to it's corresponding KObject which is the actual underlying object std::unordered_map> threadMap; //!< A mapping from a PID to it's corresponding KThread object - std::unordered_map>> mutexMap; //!< A map from a mutex's address to a vector of threads waiting on it (Sorted by priority) - std::unordered_map>> condVarMap; //!< A map from a conditional variable's address to a vector of threads waiting on it (Sorted by priority) + std::unordered_map mutexMap; //!< A map from a mutex's address to a vector of threads waiting on it + std::unordered_map condVarMap; //!< 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 - /** * This is used as the output for functions that return created kernel objects * @tparam objectClass The class of the kernel object @@ -93,7 +93,7 @@ namespace skyline::kernel::type { * @param stackSize The size of the stack * @param tlsMemory The KSharedMemory object for TLS memory allocated by the guest process */ - KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, u64 stackBase, u64 stackSize, std::shared_ptr& tlsMemory); + KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, u64 stackBase, u64 stackSize, std::shared_ptr &tlsMemory); /** * Close the file descriptor to the process's memory @@ -150,12 +150,6 @@ namespace skyline::kernel::type { */ void WriteMemory(void *source, u64 offset, size_t size) const; - /** - * @brief Returns the FD of the memory for the process - * @return The FD of the memory for the process - */ - int GetMemoryFd() const; - /** * @brief Map a chunk of process local memory (private memory) * @param address The address to map to (Can be 0 if address doesn't matter) @@ -258,6 +252,8 @@ namespace skyline::kernel::type { /** * @brief This resets the object to an unsignalled state */ - void ResetSignal(); + inline void ResetSignal() { + signalled = false; + } }; } diff --git a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp index 096d1130..6fb21d1d 100644 --- a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp @@ -7,7 +7,7 @@ namespace skyline::kernel::type { u64 MapSharedFunc(u64 address, size_t size, u64 perms, u64 fd) { - return reinterpret_cast(mmap(reinterpret_cast(address), size, static_cast(perms), MAP_SHARED | ((address) ? MAP_FIXED : 0), static_cast(fd), 0)); // NOLINT(hicpp-signed-bitwise) + return reinterpret_cast(mmap(reinterpret_cast(address), size, static_cast(perms), MAP_SHARED | ((address) ? MAP_FIXED : 0), static_cast(fd), 0)); } KSharedMemory::KSharedMemory(const DeviceState &state, u64 address, size_t size, const memory::Permission permission, memory::Type type) : type(type), KObject(state, KType::KSharedMemory) { @@ -15,7 +15,7 @@ namespace skyline::kernel::type { if (fd < 0) throw exception("An error occurred while creating shared memory: {}", fd); address = reinterpret_cast(mmap(reinterpret_cast(address), size, permission.Get(), MAP_SHARED | ((address) ? MAP_FIXED : 0), static_cast(fd), 0)); - if (address == reinterpret_cast(MAP_FAILED)) // NOLINT(hicpp-signed-bitwise) + if (address == reinterpret_cast(MAP_FAILED)) throw exception("An occurred while mapping shared region: {}", strerror(errno)); kernel = {address, size, permission}; } @@ -25,9 +25,8 @@ namespace skyline::kernel::type { fregs.x0 = address; fregs.x1 = size; fregs.x2 = static_cast(permission.Get()); - fregs.x3 = static_cast(MAP_SHARED | ((address) ? MAP_FIXED : 0)); // NOLINT(hicpp-signed-bitwise) + fregs.x3 = static_cast(MAP_SHARED | ((address) ? MAP_FIXED : 0)); fregs.x4 = static_cast(fd); - fregs.x4 = static_cast(-1); fregs.x8 = __NR_mmap; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread->pid); if (fregs.x0 < 0) @@ -51,7 +50,8 @@ namespace skyline::kernel::type { } if (kernel.valid()) UnmapSharedFunc(kernel.address, kernel.size); - } catch (const std::exception &) {} + } catch (const std::exception &) { + } close(fd); } diff --git a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h index 8e4a0247..92b8665b 100644 --- a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h +++ b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h @@ -60,7 +60,7 @@ namespace skyline::kernel::type { * @param permission The new permissions to be set for the memory * @param kernel Set the permissions for the kernel rather than the guest */ - void UpdatePermission(memory::Permission permission, bool host=0); + void UpdatePermission(memory::Permission permission, bool host = 0); /** * @brief Creates a MemoryInfo struct from the current instance diff --git a/app/src/main/cpp/skyline/kernel/types/KSyncObject.cpp b/app/src/main/cpp/skyline/kernel/types/KSyncObject.cpp deleted file mode 100644 index b623c746..00000000 --- a/app/src/main/cpp/skyline/kernel/types/KSyncObject.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "KSyncObject.h" -#include - -namespace skyline::kernel::type { - KSyncObject::KSyncObject(const skyline::DeviceState &state, skyline::kernel::type::KType type) : KObject(state, type) {} - - KSyncObject::threadInfo::threadInfo(pid_t process, u32 index) : process(process), index(index) {} - - void KSyncObject::Signal() { - for (const auto &info : waitThreads) { - state.ctx->registers.w1 = info.index; - state.process->threadMap.at(info.process)->status = KThread::Status::Runnable; - } - waitThreads.clear(); - } -} diff --git a/app/src/main/cpp/skyline/kernel/types/KSyncObject.h b/app/src/main/cpp/skyline/kernel/types/KSyncObject.h index c35c5472..24f5b46f 100644 --- a/app/src/main/cpp/skyline/kernel/types/KSyncObject.h +++ b/app/src/main/cpp/skyline/kernel/types/KSyncObject.h @@ -9,27 +9,19 @@ namespace skyline::kernel::type { */ class KSyncObject : public KObject { public: - bool signalled{false}; //!< If the current object is signalled (Used by KEvent as it stays signalled till svcClearEvent or svcClearSignal is called) - /** - * @brief This holds information about a specific thread that's waiting on this object - */ - struct threadInfo { - pid_t process; //!< The PID of the waiting thread - u32 index; //!< The index of the object in the wait list - - threadInfo(pid_t process, u32 index); - }; - std::vector waitThreads; //!< A vector of threads waiting on this object + std::atomic signalled{false}; //!< If the current object is signalled (Used as object stays signalled till the signal is consumed) /** * @param state The state of the device * @param type The type of the object */ - KSyncObject(const DeviceState &state, skyline::kernel::type::KType type); + KSyncObject(const DeviceState &state, skyline::kernel::type::KType type) : KObject(state, type) {}; /** * @brief A function for calling when a particular KSyncObject is signalled */ - virtual void Signal(); + virtual void Signal() { + signalled = true; + } }; } diff --git a/app/src/main/cpp/skyline/kernel/types/KThread.cpp b/app/src/main/cpp/skyline/kernel/types/KThread.cpp index e8fe5f2a..467d32be 100644 --- a/app/src/main/cpp/skyline/kernel/types/KThread.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KThread.cpp @@ -4,8 +4,8 @@ #include namespace skyline::kernel::type { - KThread::KThread(const DeviceState &state, handle_t handle, pid_t self_pid, u64 entryPoint, u64 entryArg, u64 stackTop, u64 tls, u8 priority, KProcess *parent, std::shared_ptr& tlsMemory) : handle(handle), pid(self_pid), entryPoint(entryPoint), entryArg(entryArg), stackTop(stackTop), tls(tls), priority(priority), parent(parent), ctxMemory(tlsMemory), KSyncObject(state, - KType::KThread) { + KThread::KThread(const DeviceState &state, handle_t handle, pid_t self_pid, u64 entryPoint, u64 entryArg, u64 stackTop, u64 tls, u8 priority, KProcess *parent, std::shared_ptr &tlsMemory) : handle(handle), pid(self_pid), entryPoint(entryPoint), entryArg(entryArg), stackTop(stackTop), tls(tls), priority(priority), parent(parent), ctxMemory(tlsMemory), KSyncObject(state, + KType::KThread) { UpdatePriority(priority); } @@ -14,7 +14,7 @@ namespace skyline::kernel::type { } void KThread::Start() { - if(status == Status::Created) { + if (status == Status::Created) { if (pid == parent->pid) parent->status = KProcess::Status::Started; status = Status::Running; @@ -23,9 +23,8 @@ namespace skyline::kernel::type { } void KThread::Kill() { - if(status != Status::Dead) { + if (status != Status::Dead) { status = Status::Dead; - kill(pid, SIGKILL); Signal(); } } @@ -36,30 +35,4 @@ namespace skyline::kernel::type { if (setpriority(PRIO_PROCESS, static_cast(pid), liPriority) == -1) throw exception("Couldn't set process priority to {} for PID: {}", liPriority, pid); } - - void KThread::Sleep() { - if (status == Status::Running) { - status = Status::Sleeping; - timeout = 0; - } - } - - void KThread::WakeUp() { - if (status == Status::Sleeping) - status = Status::Runnable; - } - - void KThread::ClearWaitObjects() { - for (auto &object : waitObjects) { - auto iter = object->waitThreads.begin(); - while (iter != object->waitThreads.end()) { - if (iter->process == pid) { - object->waitThreads.erase(iter); - break; - } - iter++; - } - } - waitObjects.clear(); - } } diff --git a/app/src/main/cpp/skyline/kernel/types/KThread.h b/app/src/main/cpp/skyline/kernel/types/KThread.h index 374f678d..00401a3d 100644 --- a/app/src/main/cpp/skyline/kernel/types/KThread.h +++ b/app/src/main/cpp/skyline/kernel/types/KThread.h @@ -17,16 +17,9 @@ namespace skyline::kernel::type { enum class Status { Created, //!< The thread has been created but has not been started yet Running, //!< The thread is running currently - Sleeping, //!< The thread is sleeping due to svcSleepThread - WaitSync, //!< The thread is waiting for a KSyncObject signal - WaitMutex, //!< The thread is waiting on a Mutex - WaitCondVar, //!< The thread is waiting on a Conditional Variable - Runnable, //!< The thread is ready to run after waiting Dead //!< The thread is dead and not running } status = Status::Created; //!< The state of the thread std::shared_ptr ctxMemory; //!< The KSharedMemory of the shared memory allocated by the guest process TLS - std::vector> waitObjects; //!< A vector holding the objects this thread is waiting for - u64 timeout{}; //!< The end of a timeout for svcWaitSynchronization or the end of the sleep period for svcSleepThread handle_t handle; // The handle of the object in the handle table pid_t pid; //!< The PID of the current thread (As in kernel PID and not PGID [In short, Linux implements threads as processes that share a lot of stuff at the kernel level]) u64 stackTop; //!< The top of the stack (Where it starts growing downwards from) @@ -45,7 +38,7 @@ namespace skyline::kernel::type { * @param parent The parent process of this thread * @param tlsMemory The KSharedMemory object for TLS memory allocated by the guest process */ - KThread(const DeviceState &state, handle_t handle, pid_t self_pid, u64 entryPoint, u64 entryArg, u64 stackTop, u64 tls, u8 priority, KProcess *parent, std::shared_ptr& tlsMemory); + KThread(const DeviceState &state, handle_t handle, pid_t self_pid, u64 entryPoint, u64 entryArg, u64 stackTop, u64 tls, u8 priority, KProcess *parent, std::shared_ptr &tlsMemory); /** * @brief Kills the thread and deallocates the memory allocated for stack. @@ -62,21 +55,11 @@ namespace skyline::kernel::type { */ void Kill(); - /** - * @brief This causes this thread to sleep indefinitely (no-op if thread is already sleeping) - */ - void Sleep(); - /** * @brief This wakes up the thread from it's sleep (no-op if thread is already awake) */ void WakeUp(); - /** - * @brief This clears all the objects in the waitObjects vector - */ - void ClearWaitObjects(); - /** * @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. diff --git a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp index 2b2300f8..6e6b3b47 100644 --- a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp @@ -5,7 +5,7 @@ namespace skyline::kernel::type { u64 MapTransferFunc(u64 address, size_t size, u64 perms) { - return reinterpret_cast(mmap(reinterpret_cast(address), size, static_cast(perms), MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0), -1, 0)); // NOLINT(hicpp-signed-bitwise) + return reinterpret_cast(mmap(reinterpret_cast(address), size, static_cast(perms), MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0), -1, 0)); } KTransferMemory::KTransferMemory(const DeviceState &state, pid_t pid, u64 address, size_t size, const memory::Permission permission) : owner(pid), cSize(size), permission(permission), KObject(state, KType::KTransferMemory) { @@ -14,7 +14,7 @@ namespace skyline::kernel::type { fregs.x0 = address; fregs.x1 = size; fregs.x2 = static_cast(permission.Get()); - fregs.x3 = static_cast(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0)); // NOLINT(hicpp-signed-bitwise) + fregs.x3 = static_cast(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0)); fregs.x4 = static_cast(-1); fregs.x8 = __NR_mmap; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, pid); @@ -22,28 +22,23 @@ namespace skyline::kernel::type { throw exception("An error occurred while mapping shared region in child process"); cAddress = fregs.x0; } else { - address = MapTransferFunc(address, size, static_cast(permission.Get())); + address = reinterpret_cast(mmap(reinterpret_cast(address), size, permission.Get(), MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0), -1, 0)); if (reinterpret_cast(address) == MAP_FAILED) throw exception("An error occurred while mapping transfer memory in kernel"); cAddress = address; } } - u64 UnmapTransferFunc(u64 address, size_t size) { - return static_cast(munmap(reinterpret_cast(address), size)); - } - u64 KTransferMemory::Transfer(pid_t process, u64 address, u64 size) { if (process) { Registers fregs{}; fregs.x0 = address; fregs.x1 = size; fregs.x2 = static_cast(permission.Get()); - fregs.x3 = static_cast(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0)); // NOLINT(hicpp-signed-bitwise) + fregs.x3 = static_cast(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0)); fregs.x4 = static_cast(-1); fregs.x8 = __NR_mmap; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, process); - // state.nce->ExecuteFunction(reinterpret_cast(MapTransferFunc), fregs, process); if (fregs.x0 < 0) throw exception("An error occurred while mapping transfer memory in child process"); address = fregs.x0; @@ -65,11 +60,10 @@ namespace skyline::kernel::type { fregs.x1 = size; fregs.x8 = __NR_munmap; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, owner); - // state.nce->ExecuteFunction(reinterpret_cast(UnmapTransferFunc), fregs, owner); if (fregs.x0 < 0) throw exception("An error occurred while unmapping transfer memory in child process"); } else { - if (reinterpret_cast(UnmapTransferFunc(address, size)) == MAP_FAILED) + if (reinterpret_cast(munmap(reinterpret_cast(address), size)) == MAP_FAILED) throw exception("An error occurred while unmapping transfer memory in kernel"); } owner = process; @@ -96,14 +90,15 @@ namespace skyline::kernel::type { KTransferMemory::~KTransferMemory() { if (owner) { try { - if(state.process) { + if (state.process) { Registers fregs{}; fregs.x0 = cAddress; fregs.x1 = cSize; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.process->pid); } - } catch (const std::exception &) {} + } catch (const std::exception &) { + } } else - UnmapTransferFunc(cAddress, cSize); + munmap(reinterpret_cast(cAddress), cSize); } }; diff --git a/app/src/main/cpp/skyline/nce.cpp b/app/src/main/cpp/skyline/nce.cpp index d22e49ac..6158b78f 100644 --- a/app/src/main/cpp/skyline/nce.cpp +++ b/app/src/main/cpp/skyline/nce.cpp @@ -1,4 +1,5 @@ #include +#include #include "os.h" #include "jvm.h" #include "nce/guest.h" @@ -6,14 +7,17 @@ #include "kernel/svc.h" extern bool Halt; -extern skyline::Mutex jniMtx; +extern skyline::GroupMutex jniMtx; namespace skyline { void NCE::KernelThread(pid_t thread) { try { state.thread = state.process->threadMap.at(thread); state.ctx = reinterpret_cast(state.thread->ctxMemory->guest.address); - while (!Halt) { + while (true) { + std::lock_guard jniGd(jniMtx); + if (Halt) + break; if (state.ctx->state == ThreadState::WaitKernel) { const u16 svc = static_cast(state.ctx->commandId); try { @@ -29,6 +33,7 @@ namespace skyline { } else if (state.ctx->state == ThreadState::GuestCrash) { state.logger->Warn("Thread with PID {} has crashed due to signal: {}", thread, strsignal(state.ctx->commandId)); ThreadTrace(); + state.ctx->state = ThreadState::WaitRun; break; } } @@ -37,7 +42,12 @@ namespace skyline { } catch (...) { state.logger->Error("An unknown exception has occurred"); } - state.os->KillThread(thread); + if (thread == state.process->pid) { + jniMtx.lock(GroupMutex::Group::Group2); + state.os->KillThread(thread); + Halt = true; + jniMtx.unlock(); + } } NCE::NCE(DeviceState &state) : state(state) {} @@ -48,15 +58,25 @@ namespace skyline { } void NCE::Execute() { - while (!Halt && state.os->process) { + while (true) { std::lock_guard jniGd(jniMtx); + if (Halt) + break; state.os->serviceManager.Loop(); state.gpu->Loop(); } - Halt = false; + jniMtx.lock(GroupMutex::Group::Group2); + Halt = true; + jniMtx.unlock(); } - void ExecuteFunctionCtx(ThreadCall call, Registers &funcRegs, ThreadContext *ctx) { + /** + * 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->commandId = static_cast(call); Registers registers = ctx->registers; while (ctx->state != ThreadState::WaitInit && ctx->state != ThreadState::WaitKernel); @@ -72,10 +92,13 @@ namespace skyline { } void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs, pid_t pid) { - ExecuteFunctionCtx(call, funcRegs, reinterpret_cast(state.process->threadMap.at(pid)->ctxMemory->kernel.address)); + if (state.process->status != kernel::type::KProcess::Status::Exiting) + ExecuteFunctionCtx(call, funcRegs, reinterpret_cast(state.process->threadMap.at(pid)->ctxMemory->kernel.address)); + else + throw std::out_of_range("The KProcess object is missing"); } - void NCE::WaitThreadInit(std::shared_ptr &thread) { + void NCE::WaitThreadInit(std::shared_ptr &thread) __attribute__ ((optnone)) { auto ctx = reinterpret_cast(thread->ctxMemory->kernel.address); while (ctx->state == ThreadState::NotReady); } @@ -98,7 +121,7 @@ namespace skyline { ctx = ctx ? ctx : state.ctx; if (numHist) { std::vector instrs(numHist); - u64 size = (sizeof(u32) * numHist); + u64 size = sizeof(u32) * numHist; u64 offset = ctx->pc - size + (2 * sizeof(u32)); state.process->ReadMemory(instrs.data(), offset, size); for (auto &instr : instrs) { @@ -111,17 +134,22 @@ namespace skyline { offset += sizeof(u32); } } + if (ctx->faultAddress) + regStr += fmt::format("\nFault Address: 0x{:X}", ctx->faultAddress); + if (ctx->sp) + regStr += fmt::format("\nStack Pointer: 0x{:X}", ctx->sp); for (u16 index = 0; index < constant::NumRegs - 1; index += 2) { regStr += fmt::format("\nX{}: 0x{:X}, X{}: 0x{:X}", index, ctx->registers.regs[index], index + 1, ctx->registers.regs[index + 1]); } if (numHist) { state.logger->Debug("Process Trace:{}", trace); - state.logger->Debug("Raw Instructions: 0x{}\nCPU Context:{}", raw, regStr); + state.logger->Debug("Raw Instructions: 0x{}", raw); + state.logger->Debug("CPU Context:{}", regStr); } else - state.logger->Warn("CPU Context:{}", regStr); + state.logger->Debug("CPU Context:{}", regStr); } - const std::array cntpctEl0X0 = { + const std::array cntpctEl0X0 = { 0xA9BF0BE1, // STP X1, X2, [SP, #-16]! 0x3C9F0FE0, // STR Q0, [SP, #-16]! 0x3C9F0FE1, // STR Q1, [SP, #-16]! @@ -138,10 +166,11 @@ namespace skyline { 0x9E790000, // FCVTZU X0, D0 0x3CC107E2, // LDR Q2, [SP], #16 0x3CC107E1, // LDR Q1, [SP], #16 + 0x3CC107E0, // LDR Q0, [SP], #16 0xA8C10BE1, // LDP X1, X2, [SP], #16 }; - const std::array cntpctEl0X1 = { + const std::array cntpctEl0X1 = { 0xA9BF0BE0, // STP X0, X2, [SP, #-16]! 0x3C9F0FE0, // STR Q0, [SP, #-16]! 0x3C9F0FE1, // STR Q1, [SP, #-16]! @@ -158,10 +187,11 @@ namespace skyline { 0x9E790001, // FCVTZU X0, D0 0x3CC107E2, // LDR Q2, [SP], #16 0x3CC107E1, // LDR Q1, [SP], #16 + 0x3CC107E0, // LDR Q0, [SP], #16 0xA8C10BE0, // LDP X0, X2, [SP], #16 }; - std::array cntpctEl0Xn = { + std::array cntpctEl0Xn = { 0xA9BF07E0, // STP X0, X1, [SP, #-16]! 0x3C9F0FE0, // STR Q0, [SP, #-16]! 0x3C9F0FE1, // STR Q1, [SP, #-16]! @@ -178,6 +208,7 @@ namespace skyline { 0x00000000, // FCVTZU Xn, D0 (Set at runtime) 0x3CC107E2, // LDR Q2, [SP], #16 0x3CC107E1, // LDR Q1, [SP], #16 + 0x3CC107E0, // LDR Q0, [SP], #16 0xA8C107E0, // LDP X0, X1, [SP], #16 }; @@ -268,9 +299,6 @@ namespace skyline { patch.push_back(ldrX0); patch.push_back(bret.raw); } else if (instrMrs->srcReg == constant::CntpctEl0) { - instr::Mrs mrs(constant::CntvctEl0, regs::X(instrMrs->destReg)); - *address = mrs.raw; - /* instr::B bjunc(offset); if (instrMrs->destReg == 0) offset += cntpctEl0X0.size() * sizeof(u32); @@ -294,7 +322,6 @@ namespace skyline { patch.push_back(instr); } patch.push_back(bret.raw); - */ } else if (instrMrs->srcReg == constant::CntfrqEl0) { instr::B bjunc(offset); auto movFreq = instr::MoveU32Reg(static_cast(instrMrs->destReg), constant::TegraX1Freq); @@ -311,6 +338,7 @@ namespace skyline { offset -= sizeof(u32); patchOffset -= sizeof(u32); } + patch.resize(patch.size() + PAGE_SIZE - 1 & ~(PAGE_SIZE - 1), 0x0); return patch; } } diff --git a/app/src/main/cpp/skyline/nce.h b/app/src/main/cpp/skyline/nce.h index 83012756..cf5e4c7f 100644 --- a/app/src/main/cpp/skyline/nce.h +++ b/app/src/main/cpp/skyline/nce.h @@ -42,7 +42,7 @@ namespace skyline { * @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 ExecuteFunction(ThreadCall call, Registers &funcRegs, std::shared_ptr &thread); /** * @brief Execute any arbitrary function on a particular child process @@ -56,7 +56,7 @@ namespace skyline { * @brief Waits till a thread is ready to execute commands * @param thread The KThread to wait for initialization */ - void WaitThreadInit(std::shared_ptr& thread); + void WaitThreadInit(std::shared_ptr &thread); /** * @brief Sets the X0 and X1 registers in a thread and starts it and it's kernel thread diff --git a/app/src/main/cpp/skyline/nce/guest.cpp b/app/src/main/cpp/skyline/nce/guest.cpp index 8ecbb123..6d019012 100644 --- a/app/src/main/cpp/skyline/nce/guest.cpp +++ b/app/src/main/cpp/skyline/nce/guest.cpp @@ -1,6 +1,7 @@ #include -#include -#include +#include +#include +#include // This is used implicitly #include "guest_common.h" #define FORCE_INLINE __attribute__((always_inline)) inline // NOLINT(cppcoreguidelines-macro-usage) @@ -95,11 +96,71 @@ namespace skyline::guest { asm("MRS %0, TPIDR_EL0":"=r"(ctx)); ctx->pc = pc; ctx->commandId = svc; + if (svc == 0xB) { // svcSleepThread + switch (ctx->registers.x0) { + case 0: + case 1: + case 2: { + asm("MOV X0, XZR\n\t" + "MOV X1, XZR\n\t" + "MOV X2, XZR\n\t" + "MOV X3, XZR\n\t" + "MOV X4, XZR\n\t" + "MOV X5, XZR\n\t" + "MOV X8, #124\n\t" // __NR_sched_yield + "STR LR, [SP, #-16]!\n\t" + "MOV LR, SP\n\t" + "SVC #0\n\t" + "MOV SP, LR\n\t" + "LDR LR, [SP], #16":: : "x0", "x1", "x2", "x3", "x4", "x5", "x8"); + break; + } + default: { + struct timespec spec = { + .tv_sec = static_cast(ctx->registers.x0 / 1000000000), + .tv_nsec = static_cast(ctx->registers.x0 % 1000000000) + }; + volatile register __unused timespec *specPtr asm("x0") = &spec; + asm("MOV X1, XZR\n\t" + "MOV X2, XZR\n\t" + "MOV X3, XZR\n\t" + "MOV X4, XZR\n\t" + "MOV X5, XZR\n\t" + "MOV X8, #101\n\t" // __NR_nanosleep + "STR LR, [SP, #-16]!\n\t" + "MOV LR, SP\n\t" + "SVC #0\n\t" + "MOV SP, LR\n\t" + "LDR LR, [SP], #16":: : "x0", "x1", "x2", "x3", "x4", "x5", "x8"); + } + } + return; + } else if (svc == 0x1E) { + asm("STP X1, X2, [SP, #-16]!\n\t" + "STR Q0, [SP, #-16]!\n\t" + "STR Q1, [SP, #-16]!\n\t" + "STR Q2, [SP, #-16]!\n\t" + "MRS X1, CNTFRQ_EL0\n\t" + "MRS X2, CNTVCT_EL0\n\t" + "UCVTF D0, X0\n\t" + "MOV X1, 87411174408192\n\t" + "MOVK X1, 0x4172, LSL 48\n\t" + "FMOV D2, X1\n\t" + "UCVTF D1, X1\n\t" + "FDIV D0, D0, D2\n\t" + "FMUL D0, D0, D1\n\t" + "FCVTZU %0, D0\n\t" + "LDR Q2, [SP], #16\n\t" + "LDR Q1, [SP], #16\n\t" + "LDR Q0, [SP], #16\n\t" + "LDP X1, X2, [SP], #16"::"r"(ctx->registers.x0)); + return; + } while (true) { ctx->state = ThreadState::WaitKernel; while (ctx->state == ThreadState::WaitKernel); if (ctx->state == ThreadState::WaitRun) - return; + break; else if (ctx->state == ThreadState::WaitFunc) { if (ctx->commandId == static_cast(ThreadCall::Syscall)) { saveCtxStack(); @@ -114,17 +175,23 @@ namespace skyline::guest { } } } + ctx->state = ThreadState::Running; } void signalHandler(int signal, siginfo_t *info, ucontext_t *ucontext) { volatile ThreadContext *ctx; asm("MRS %0, TPIDR_EL0":"=r"(ctx)); - for (u8 index = 0; index < constant::NumRegs; index++) + for (u8 index = 0; index < 30; index++) ctx->registers.regs[index] = ucontext->uc_mcontext.regs[index]; ctx->pc = ucontext->uc_mcontext.pc; ctx->commandId = static_cast(signal); - ctx->state = ThreadState::GuestCrash; - exit(0); + ctx->faultAddress = ucontext->uc_mcontext.fault_address; + ctx->sp = ucontext->uc_mcontext.sp; + while (true) { + ctx->state = ThreadState::GuestCrash; + if (ctx->state == ThreadState::WaitRun) + exit(0); + } } void entry(u64 address) { diff --git a/app/src/main/cpp/skyline/nce/guest.h b/app/src/main/cpp/skyline/nce/guest.h index be77f0ab..0479fc6b 100644 --- a/app/src/main/cpp/skyline/nce/guest.h +++ b/app/src/main/cpp/skyline/nce/guest.h @@ -4,10 +4,17 @@ namespace skyline { namespace guest { constexpr size_t saveCtxSize = 20 * sizeof(u32); constexpr size_t loadCtxSize = 20 * sizeof(u32); - constexpr size_t svcHandlerSize = 200 * sizeof(u32); + #ifdef NDEBUG + constexpr size_t svcHandlerSize = 150 * sizeof(u32); + #else + constexpr size_t svcHandlerSize = 250 * sizeof(u32); + #endif + void entry(u64 address); + extern "C" void saveCtx(void); extern "C" void loadCtx(void); + void svcHandler(u64 pc, u32 svc); } } diff --git a/app/src/main/cpp/skyline/nce/guest_common.h b/app/src/main/cpp/skyline/nce/guest_common.h index 09fd4de2..c9a9eaf0 100644 --- a/app/src/main/cpp/skyline/nce/guest_common.h +++ b/app/src/main/cpp/skyline/nce/guest_common.h @@ -121,12 +121,12 @@ namespace skyline { * @brief This enumeration is used to convey the state of a thread to the kernel */ enum class ThreadState : u32 { - NotReady = 0, //!< The thread hasn't yet entered the entry handler - Running = 1, //!< The thread is currently executing code + 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 + 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 }; @@ -146,5 +146,7 @@ namespace skyline { u64 pc; //!< The program counter register on the guest Registers registers; //!< The general purpose registers on the guest u64 tpidrroEl0; //!< The value for TPIDRRO_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 }; } diff --git a/app/src/main/cpp/skyline/os.cpp b/app/src/main/cpp/skyline/os.cpp index de8a7638..bc7fe38d 100644 --- a/app/src/main/cpp/skyline/os.cpp +++ b/app/src/main/cpp/skyline/os.cpp @@ -19,7 +19,7 @@ namespace skyline::kernel { std::shared_ptr OS::CreateProcess(u64 entry, u64 argument, size_t stackSize) { madvise(reinterpret_cast(constant::BaseAddr), constant::BaseEnd, MADV_DONTFORK); - auto *stack = static_cast(mmap(nullptr, stackSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS | MAP_STACK, -1, 0)); // NOLINT(hicpp-signed-bitwise) + auto *stack = static_cast(mmap(nullptr, stackSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS | MAP_STACK, -1, 0)); madvise(stack, reinterpret_cast(stack) + stackSize, MADV_DOFORK); if (stack == MAP_FAILED) throw exception("Failed to allocate stack memory"); @@ -27,12 +27,13 @@ namespace skyline::kernel { munmap(stack, stackSize); throw exception("Failed to create guard pages"); } - auto tlsMem = std::make_shared(state, 0, (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1), memory::Permission(true, true, 0), memory::Type::Reserved); // NOLINT(hicpp-signed-bitwise) + auto tlsMem = std::make_shared(state, 0, (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1), memory::Permission(true, true, false), memory::Type::Reserved); tlsMem->guest = tlsMem->kernel; madvise(reinterpret_cast(tlsMem->guest.address), tlsMem->guest.size, MADV_DOFORK); - pid_t pid = clone(reinterpret_cast(&guest::entry), stack + stackSize, CLONE_FILES | CLONE_FS | CLONE_SETTLS | SIGCHLD, reinterpret_cast(entry), nullptr, reinterpret_cast(tlsMem->guest.address)); // NOLINT(hicpp-signed-bitwise) + pid_t pid = clone(reinterpret_cast(&guest::entry), stack + stackSize, CLONE_FILES | CLONE_FS | CLONE_SETTLS | SIGCHLD, reinterpret_cast(entry), nullptr, reinterpret_cast(tlsMem->guest.address)); if (pid == -1) throw exception("Call to clone() has failed: {}", strerror(errno)); + state.logger->Debug("Successfully created process with PID: {}", pid); process = std::make_shared(state, pid, argument, reinterpret_cast(stack), stackSize, tlsMem); state.logger->Debug("Successfully created process with PID: {}", pid); return process; @@ -41,14 +42,11 @@ namespace skyline::kernel { void OS::KillThread(pid_t pid) { if (process->pid == pid) { state.logger->Debug("Killing process with PID: {}", pid); - for (auto&[key, value]: process->threadMap) { - value->Kill(); - } - process.reset(); + for (auto &thread: process->threadMap) + thread.second->Kill(); } else { state.logger->Debug("Killing thread with TID: {}", pid); process->threadMap.at(pid)->Kill(); - process->threadMap.erase(pid); } } } diff --git a/app/src/main/cpp/skyline/os.h b/app/src/main/cpp/skyline/os.h index 2e1a49e3..d318fdd1 100644 --- a/app/src/main/cpp/skyline/os.h +++ b/app/src/main/cpp/skyline/os.h @@ -26,7 +26,7 @@ namespace skyline::kernel { * @param settings An instance of the Settings class * @param window The ANativeWindow object to draw the screen to */ - OS(std::shared_ptr& jvmManager, std::shared_ptr &logger, std::shared_ptr &settings); + OS(std::shared_ptr &jvmManager, std::shared_ptr &logger, std::shared_ptr &settings); /** * @brief Execute a particular ROM file. This launches the main process and calls the NCE class to handle execution. diff --git a/app/src/main/cpp/skyline/services/am/applet.cpp b/app/src/main/cpp/skyline/services/am/applet.cpp index eac044ed..271c16e8 100644 --- a/app/src/main/cpp/skyline/services/am/applet.cpp +++ b/app/src/main/cpp/skyline/services/am/applet.cpp @@ -10,7 +10,7 @@ namespace skyline::service::am { manager.RegisterService(SRVREG(IApplicationProxy), session, response); } - appletAE::appletAE(const DeviceState& state, ServiceManager& manager) : BaseService(state, manager, false, Service::am_appletAE, { + appletAE::appletAE(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, false, Service::am_appletAE, { {0x64, SFUNC(appletAE::OpenSystemAppletProxy)}, {0xC8, SFUNC(appletAE::OpenLibraryAppletProxy)}, {0xC9, SFUNC(appletAE::OpenLibraryAppletProxy)}, @@ -26,11 +26,11 @@ namespace skyline::service::am { manager.RegisterService(SRVREG(IApplicationProxy), session, response); } - void appletAE::OpenOverlayAppletProxy(type::KSession& session, ipc::IpcRequest& request, ipc::IpcResponse& response) { + void appletAE::OpenOverlayAppletProxy(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { manager.RegisterService(SRVREG(IOverlayAppletProxy), session, response); } - void appletAE::OpenSystemAppletProxy(type::KSession& session, ipc::IpcRequest& request, ipc::IpcResponse& response) { + void appletAE::OpenSystemAppletProxy(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { manager.RegisterService(SRVREG(ISystemAppletProxy), session, response); } @@ -64,7 +64,7 @@ namespace skyline::service::am { manager.RegisterService(SRVREG(IDebugFunctions), session, response); } - void BaseProxy::GetAppletCommonFunctions(type::KSession& session, ipc::IpcRequest& request, ipc::IpcResponse& response) { + void BaseProxy::GetAppletCommonFunctions(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { manager.RegisterService(SRVREG(IAppletCommonFunctions), session, response); } @@ -93,7 +93,7 @@ namespace skyline::service::am { {0x3E8, SFUNC(BaseProxy::GetDebugFunctions)} }) {} - ISystemAppletProxy::ISystemAppletProxy(const DeviceState& state, ServiceManager& manager) : BaseProxy(state, manager, Service::am_ISystemAppletProxy, { + ISystemAppletProxy::ISystemAppletProxy(const DeviceState &state, ServiceManager &manager) : BaseProxy(state, manager, Service::am_ISystemAppletProxy, { {0x0, SFUNC(BaseProxy::GetCommonStateGetter)}, {0x1, SFUNC(BaseProxy::GetSelfController)}, {0x2, SFUNC(BaseProxy::GetWindowController)}, @@ -104,7 +104,7 @@ namespace skyline::service::am { {0x3E8, SFUNC(BaseProxy::GetDebugFunctions)} }) {} - IOverlayAppletProxy::IOverlayAppletProxy(const DeviceState& state, ServiceManager& manager) : BaseProxy(state, manager, Service::am_IOverlayAppletProxy, { + IOverlayAppletProxy::IOverlayAppletProxy(const DeviceState &state, ServiceManager &manager) : BaseProxy(state, manager, Service::am_IOverlayAppletProxy, { {0x0, SFUNC(BaseProxy::GetCommonStateGetter)}, {0x1, SFUNC(BaseProxy::GetSelfController)}, {0x2, SFUNC(BaseProxy::GetWindowController)}, diff --git a/app/src/main/cpp/skyline/services/am/applet.h b/app/src/main/cpp/skyline/services/am/applet.h index d4f22e0d..416079a4 100644 --- a/app/src/main/cpp/skyline/services/am/applet.h +++ b/app/src/main/cpp/skyline/services/am/applet.h @@ -37,12 +37,12 @@ namespace skyline::service::am { /** * @brief This returns #IOverlayAppletProxy (https://switchbrew.org/wiki/Applet_Manager_services#OpenOverlayAppletProxy) */ - void OpenOverlayAppletProxy(type::KSession& session, ipc::IpcRequest& request, ipc::IpcResponse& response); + void OpenOverlayAppletProxy(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); /** * @brief This returns #ISystemAppletProxy (https://switchbrew.org/wiki/Applet_Manager_services#OpenSystemAppletProxy) */ - void OpenSystemAppletProxy(type::KSession& session, ipc::IpcRequest& request, ipc::IpcResponse& response); + void OpenSystemAppletProxy(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); }; /** @@ -90,7 +90,7 @@ namespace skyline::service::am { /** * @brief This returns #IAppletCommonFunctions (https://switchbrew.org/wiki/Applet_Manager_services#IAppletCommonFunctions) */ - void GetAppletCommonFunctions(type::KSession& session, ipc::IpcRequest& request, ipc::IpcResponse& response); + void GetAppletCommonFunctions(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); }; /** @@ -111,7 +111,7 @@ namespace skyline::service::am { */ class IOverlayAppletProxy : public BaseProxy { public: - IOverlayAppletProxy(const DeviceState& state, ServiceManager& manager); + IOverlayAppletProxy(const DeviceState &state, ServiceManager &manager); }; /** @@ -119,7 +119,7 @@ namespace skyline::service::am { */ class ISystemAppletProxy : public BaseProxy { public: - ISystemAppletProxy(const DeviceState& state, ServiceManager& manager); + ISystemAppletProxy(const DeviceState &state, ServiceManager &manager); }; /** diff --git a/app/src/main/cpp/skyline/services/am/appletController.cpp b/app/src/main/cpp/skyline/services/am/appletController.cpp index eea63c32..5d868a0c 100644 --- a/app/src/main/cpp/skyline/services/am/appletController.cpp +++ b/app/src/main/cpp/skyline/services/am/appletController.cpp @@ -46,11 +46,11 @@ namespace skyline::service::am { response.Push(static_cast(operationMode)); } - void ICommonStateGetter::GetDefaultDisplayResolution(type::KSession& session, ipc::IpcRequest& request, ipc::IpcResponse& response) { + void ICommonStateGetter::GetDefaultDisplayResolution(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { if (operationMode == OperationMode::Handheld) { response.Push(constant::HandheldResolutionW); response.Push(constant::HandheldResolutionH); - } else if (operationMode == OperationMode::Docked) { + } else if (operationMode == OperationMode::Docked) { response.Push(constant::DockedResolutionW); response.Push(constant::DockedResolutionH); } @@ -111,6 +111,6 @@ namespace skyline::service::am { IDebugFunctions::IDebugFunctions(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, false, Service::am_IDebugFunctions, { }) {} - IAppletCommonFunctions::IAppletCommonFunctions(const DeviceState& state, ServiceManager& manager) : BaseService(state, manager, false, Service::am_IAppletCommonFunctions, { + IAppletCommonFunctions::IAppletCommonFunctions(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, false, Service::am_IAppletCommonFunctions, { }) {} } diff --git a/app/src/main/cpp/skyline/services/am/appletController.h b/app/src/main/cpp/skyline/services/am/appletController.h index e24a7c3e..5bf6528e 100644 --- a/app/src/main/cpp/skyline/services/am/appletController.h +++ b/app/src/main/cpp/skyline/services/am/appletController.h @@ -76,7 +76,7 @@ namespace skyline::service::am { /** * @brief This returns the current display width and height in two u32s (https://switchbrew.org/wiki/Applet_Manager_services#GetDefaultDisplayResolution) */ - void GetDefaultDisplayResolution(type::KSession& session, ipc::IpcRequest& request, ipc::IpcResponse& response); + void GetDefaultDisplayResolution(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); }; /** @@ -180,6 +180,6 @@ namespace skyline::service::am { */ class IAppletCommonFunctions : public BaseService { public: - IAppletCommonFunctions(const DeviceState& state, ServiceManager& manager); + IAppletCommonFunctions(const DeviceState &state, ServiceManager &manager); }; } diff --git a/app/src/main/cpp/skyline/services/serviceman.cpp b/app/src/main/cpp/skyline/services/serviceman.cpp index 8984a5f8..989ba9e7 100644 --- a/app/src/main/cpp/skyline/services/serviceman.cpp +++ b/app/src/main/cpp/skyline/services/serviceman.cpp @@ -17,7 +17,7 @@ namespace skyline::service { ServiceManager::ServiceManager(const DeviceState &state) : state(state) {} std::shared_ptr ServiceManager::GetService(const Service serviceType) { - if(serviceMap.count(serviceType)) { + if (serviceMap.count(serviceType)) { return serviceMap.at(serviceType); } std::shared_ptr serviceObj; @@ -169,7 +169,7 @@ namespace skyline::service { void ServiceManager::Loop() { std::lock_guard serviceGuard(mutex); - for (auto& [type, service] : serviceMap) + for (auto&[type, service] : serviceMap) if (service->hasLoop) service->Loop(); }