diff --git a/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_types.hpp b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_types.hpp index fbfcaaa33..4c0d0608d 100644 --- a/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_types.hpp @@ -22,4 +22,10 @@ namespace ams::htc::tenv { char str[0x40]; }; + constexpr inline auto PathLengthMax = 0x300; + + struct alignas(4) Path { + char str[PathLengthMax]; + }; + } diff --git a/libraries/libstratosphere/include/stratosphere/pm/pm_types.hpp b/libraries/libstratosphere/include/stratosphere/pm/pm_types.hpp index a26fe88aa..28699b52b 100644 --- a/libraries/libstratosphere/include/stratosphere/pm/pm_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/pm/pm_types.hpp @@ -96,6 +96,27 @@ namespace ams::pm { struct ProcessEventInfo { u32 event; os::ProcessId process_id; + + inline ProcessEvent GetProcessEvent() const { + if (hos::GetVersion() >= hos::Version_5_0_0) { + return static_cast(this->event); + } + switch (static_cast(event)) { + case ProcessEventDeprecated::None: + return ProcessEvent::None; + case ProcessEventDeprecated::Exited: + return ProcessEvent::Exited; + case ProcessEventDeprecated::Started: + return ProcessEvent::Started; + case ProcessEventDeprecated::Exception: + return ProcessEvent::Exception; + case ProcessEventDeprecated::DebugRunning: + return ProcessEvent::DebugRunning; + case ProcessEventDeprecated::DebugBreak: + return ProcessEvent::DebugBreak; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } }; static_assert(sizeof(ProcessEventInfo) == 0x10 && util::is_pod::value, "ProcessEventInfo definition!"); diff --git a/libraries/libstratosphere/source/htc/tenv/htc_tenv.cpp b/libraries/libstratosphere/source/htc/tenv/htc_tenv.cpp index 4e31262a3..7e5500702 100644 --- a/libraries/libstratosphere/source/htc/tenv/htc_tenv.cpp +++ b/libraries/libstratosphere/source/htc/tenv/htc_tenv.cpp @@ -15,6 +15,7 @@ */ #include #include "impl/htc_tenv_allocator.hpp" +#include "impl/htc_tenv_impl.hpp" namespace ams::htc::tenv { @@ -23,4 +24,8 @@ namespace ams::htc::tenv { impl::InitializeAllocator(allocate, deallocate); } + void UnregisterDefinitionFilePath(os::ProcessId process_id) { + return impl::UnregisterDefinitionFilePath(process_id.value); + } + } diff --git a/libraries/libstratosphere/source/htc/tenv/impl/htc_tenv_definition_file_info.hpp b/libraries/libstratosphere/source/htc/tenv/impl/htc_tenv_definition_file_info.hpp new file mode 100644 index 000000000..9d373a732 --- /dev/null +++ b/libraries/libstratosphere/source/htc/tenv/impl/htc_tenv_definition_file_info.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htc_tenv_allocator.hpp" + +namespace ams::htc::tenv::impl { + + struct DefinitionFileInfo : public util::IntrusiveListBaseNode { + u64 process_id; + Path path; + + DefinitionFileInfo(u64 pid, Path *p) : process_id(pid) { + AMS_ASSERT(p != nullptr); + util::Strlcpy(this->path.str, p->str, PathLengthMax); + } + + static void *operator new(size_t size) { + return Allocate(size); + } + + static void operator delete(void *p, size_t size) { + Deallocate(p, size); + } + }; + +} diff --git a/libraries/libstratosphere/source/htc/tenv/impl/htc_tenv_impl.cpp b/libraries/libstratosphere/source/htc/tenv/impl/htc_tenv_impl.cpp new file mode 100644 index 000000000..5043f197d --- /dev/null +++ b/libraries/libstratosphere/source/htc/tenv/impl/htc_tenv_impl.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htc_tenv_impl.hpp" +#include "htc_tenv_definition_file_info.hpp" + +namespace ams::htc::tenv::impl { + + namespace { + + class DefinitionFileInfoManager { + private: + using DefinitionFileInfoList = util::IntrusiveListBaseTraits::ListType; + private: + DefinitionFileInfoList m_list; + os::SdkMutex m_mutex; + public: + constexpr DefinitionFileInfoManager() = default; + + ~DefinitionFileInfoManager() { + while (!m_list.empty()) { + auto *p = std::addressof(*m_list.rbegin()); + m_list.erase(m_list.iterator_to(*p)); + delete p; + } + } + + void Remove(DefinitionFileInfo *info) { + std::scoped_lock lk(m_mutex); + + m_list.erase(m_list.iterator_to(*info)); + delete info; + } + + DefinitionFileInfo *GetInfo(u64 process_id) { + std::scoped_lock lk(m_mutex); + + for (auto &info : m_list) { + if (info.process_id == process_id) { + return std::addressof(info); + } + } + + return nullptr; + } + }; + + constinit DefinitionFileInfoManager g_definition_file_info_manager; + + ALWAYS_INLINE DefinitionFileInfoManager &GetDefinitionFileInfoManager() { + return g_definition_file_info_manager; + } + + } + + void UnregisterDefinitionFilePath(u64 process_id) { + /* Require the process id to be valid. */ + if (process_id == 0) { + return; + } + + /* Remove the definition file info, if we have one. */ + if (auto *info = GetDefinitionFileInfoManager().GetInfo(process_id); info != nullptr) { + GetDefinitionFileInfoManager().Remove(info); + } + } + +} diff --git a/libraries/libstratosphere/source/htc/tenv/impl/htc_tenv_impl.hpp b/libraries/libstratosphere/source/htc/tenv/impl/htc_tenv_impl.hpp new file mode 100644 index 000000000..41196574a --- /dev/null +++ b/libraries/libstratosphere/source/htc/tenv/impl/htc_tenv_impl.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htc::tenv::impl { + + void UnregisterDefinitionFilePath(u64 process_id); + +} diff --git a/libraries/libstratosphere/source/scs/scs_shell.cpp b/libraries/libstratosphere/source/scs/scs_shell.cpp index 905491b6a..9b4ac078c 100644 --- a/libraries/libstratosphere/source/scs/scs_shell.cpp +++ b/libraries/libstratosphere/source/scs/scs_shell.cpp @@ -29,9 +29,9 @@ namespace ams::scs { u64 id; s32 socket; s32 info_id; - bool _18; - bool _19; - bool _1A; + bool started; + bool jit_debug; + bool launched_by_cs; }; constexpr inline auto MaxSocketInfo = 2; @@ -84,6 +84,13 @@ namespace ams::scs { } } } + + void InvokeHandler(ProcessEventHandler handler, os::ProcessId process_id) { + /* Invoke the handler on all our sockets. */ + for (auto i = 0; i < m_count; ++i) { + handler(m_infos[i].id, m_infos[i].socket, process_id); + } + } }; class ProgramInfoManager { @@ -101,6 +108,74 @@ namespace ams::scs { /* Clear our count. */ m_count = 0; } + + const ProgramInfo *Find(os::ProcessId process_id) const { + /* Find a matching program. */ + for (auto i = 0; i < m_count; ++i) { + if (m_infos[i].process_id == process_id) { + return std::addressof(m_infos[i]); + } + } + return nullptr; + } + + bool Register(os::ProcessId process_id) { + /* Allocate an info id. */ + const auto info_id = m_next_info_id++; + + /* Check that we have space for the program. */ + if (m_count >= MaxProgramInfo) { + return false; + } + + /* Create the new program info. */ + m_infos[m_count++] = { + .process_id = process_id, + .id = 0, + .socket = 0, + .info_id = info_id, + .started = false, + .jit_debug = false, + .launched_by_cs = false, + }; + + return true; + } + + void Unregister(os::ProcessId process_id) { + /* Unregister the program, if it's registered. */ + for (auto i = 0; i < m_count; ++i) { + if (m_infos[i].process_id == process_id) { + /* Ensure that the valid program infos remain in bounds. */ + std::memcpy(m_infos + i, m_infos + i + 1, (m_count - (i + 1)) * sizeof(*m_infos)); + + /* Note that we now have one fewer program info. */ + --m_count; + + break; + } + } + } + + void SetStarted(os::ProcessId process_id) { + /* Start the program. */ + for (auto i = 0; i < m_count; ++i) { + if (m_infos[i].process_id == process_id) { + m_infos[i].started = true; + break; + } + } + } + + void SetJitDebug(os::ProcessId process_id) { + /* Set the program as jit debug. */ + for (auto i = 0; i < m_count; ++i) { + if (m_infos[i].process_id == process_id) { + m_infos[i].jit_debug = true; + break; + } + } + } }; alignas(os::ThreadStackAlignment) constinit u8 g_thread_stack[os::MemoryPageSize]; @@ -115,9 +190,130 @@ namespace ams::scs { constinit os::SdkMutex g_manager_mutex; + void ProcessExitEvent(const pm::ProcessEventInfo &event_info) { + /* Unregister the target environment definition. */ + htc::tenv::UnregisterDefinitionFilePath(event_info.process_id); + + /* Unregister program info. */ + ProgramInfo program_info; + bool found = false; + { + std::scoped_lock lk(g_manager_mutex); + + if (const ProgramInfo *pi = g_program_info_manager.Find(event_info.process_id); pi != nullptr) { + program_info = *pi; + found = true; + + g_program_info_manager.Unregister(event_info.process_id); + } + } + + /* If we found the program, handle callbacks. */ + if (found) { + /* Invoke the common exit handler. */ + if (program_info.launched_by_cs) { + g_common_exit_handler(program_info.id, program_info.socket, program_info.process_id); + } + + /* Notify the process event. */ + if (program_info.started) { + std::scoped_lock lk(g_manager_mutex); + + g_socket_info_manager.InvokeHandler(g_common_exit_handler, program_info.process_id); + } + } + } + + void ProcessStartedEvent(const pm::ProcessEventInfo &event_info) { + /* Start the program (registering it, if needed). */ + { + std::scoped_lock lk(g_manager_mutex); + + if (g_program_info_manager.Find(event_info.process_id) == nullptr) { + AMS_ABORT_UNLESS(g_program_info_manager.Register(event_info.process_id)); + } + + g_program_info_manager.SetStarted(event_info.process_id); + } + + /* Handle callbacks. */ + { + std::scoped_lock lk(g_manager_mutex); + + g_socket_info_manager.InvokeHandler(g_common_start_handler, event_info.process_id); + } + } + + void ProcessExceptionEvent(const pm::ProcessEventInfo &event_info) { + /* Find the program info. */ + ProgramInfo program_info; + bool found = false; + { + std::scoped_lock lk(g_manager_mutex); + + if (const ProgramInfo *pi = g_program_info_manager.Find(event_info.process_id); pi != nullptr) { + program_info = *pi; + found = true; + } + + /* Set the program as jit debug. */ + g_program_info_manager.SetJitDebug(event_info.process_id); + } + + /* If we found the program, handle callbacks. */ + if (found) { + /* Invoke the common exception handler. */ + if (program_info.launched_by_cs) { + g_common_jit_debug_handler(program_info.id, program_info.socket, program_info.process_id); + } + + /* Notify the process event. */ + if (program_info.started) { + std::scoped_lock lk(g_manager_mutex); + + g_socket_info_manager.InvokeHandler(g_common_jit_debug_handler, program_info.process_id); + } + } + } + void EventHandlerThread(void *) { - /* TODO */ - AMS_ABORT("scs::EventHandlerThread"); + /* Get event observer. */ + pgl::EventObserver observer; + R_ABORT_UNLESS(pgl::GetEventObserver(std::addressof(observer))); + + /* Get the observer's event. */ + os::SystemEventType shell_event; + R_ABORT_UNLESS(observer.GetSystemEvent(std::addressof(shell_event))); + + /* Loop handling events. */ + while (true) { + /* Wait for an event to come in. */ + os::WaitSystemEvent(std::addressof(shell_event)); + + /* Loop processing event infos. */ + while (true) { + /* Get the next event info. */ + pm::ProcessEventInfo event_info; + if (R_FAILED(observer.GetProcessEventInfo(std::addressof(event_info)))) { + break; + } + + /* Process the event. */ + switch (event_info.GetProcessEvent()) { + case pm::ProcessEvent::Exited: + ProcessExitEvent(event_info); + break; + case pm::ProcessEvent::Started: + ProcessStartedEvent(event_info); + break; + case pm::ProcessEvent::Exception: + ProcessExceptionEvent(event_info); + break; + default: + break; + } + } + } } void StartEventHandlerThread() {