diff options
Diffstat (limited to 'src/core/hle/kernel')
28 files changed, 913 insertions, 839 deletions
diff --git a/src/core/hle/kernel/condition_variable.cpp b/src/core/hle/kernel/condition_variable.cpp deleted file mode 100644 index a786d7f74..000000000 --- a/src/core/hle/kernel/condition_variable.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/assert.h" -#include "core/hle/kernel/condition_variable.h" -#include "core/hle/kernel/errors.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/object_address_table.h" -#include "core/hle/kernel/thread.h" - -namespace Kernel { - -ConditionVariable::ConditionVariable() {} -ConditionVariable::~ConditionVariable() {} - -ResultVal<SharedPtr<ConditionVariable>> ConditionVariable::Create(VAddr guest_addr, - std::string name) { - SharedPtr<ConditionVariable> condition_variable(new ConditionVariable); - - condition_variable->name = std::move(name); - condition_variable->guest_addr = guest_addr; - condition_variable->mutex_addr = 0; - - // Condition variables are referenced by guest address, so track this in the kernel - g_object_address_table.Insert(guest_addr, condition_variable); - - return MakeResult<SharedPtr<ConditionVariable>>(std::move(condition_variable)); -} - -bool ConditionVariable::ShouldWait(Thread* thread) const { - return GetAvailableCount() <= 0; -} - -void ConditionVariable::Acquire(Thread* thread) { - if (GetAvailableCount() <= 0) - return; - - SetAvailableCount(GetAvailableCount() - 1); -} - -ResultCode ConditionVariable::Release(s32 target) { - if (target == -1) { - // When -1, wake up all waiting threads - SetAvailableCount(static_cast<s32>(GetWaitingThreads().size())); - WakeupAllWaitingThreads(); - } else { - // Otherwise, wake up just a single thread - SetAvailableCount(target); - WakeupWaitingThread(GetHighestPriorityReadyThread()); - } - - return RESULT_SUCCESS; -} - -s32 ConditionVariable::GetAvailableCount() const { - return Memory::Read32(guest_addr); -} - -void ConditionVariable::SetAvailableCount(s32 value) const { - Memory::Write32(guest_addr, value); -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/condition_variable.h b/src/core/hle/kernel/condition_variable.h deleted file mode 100644 index 1c9f06769..000000000 --- a/src/core/hle/kernel/condition_variable.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <string> -#include <queue> -#include "common/common_types.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/wait_object.h" -#include "core/hle/result.h" - -namespace Kernel { - -class ConditionVariable final : public WaitObject { -public: - /** - * Creates a condition variable. - * @param guest_addr Address of the object tracking the condition variable in guest memory. If - * specified, this condition variable will update the guest object when its state changes. - * @param name Optional name of condition variable. - * @return The created condition variable. - */ - static ResultVal<SharedPtr<ConditionVariable>> Create(VAddr guest_addr, - std::string name = "Unknown"); - - std::string GetTypeName() const override { - return "ConditionVariable"; - } - std::string GetName() const override { - return name; - } - - static const HandleType HANDLE_TYPE = HandleType::ConditionVariable; - HandleType GetHandleType() const override { - return HANDLE_TYPE; - } - - s32 GetAvailableCount() const; - void SetAvailableCount(s32 value) const; - - std::string name; ///< Name of condition variable (optional) - VAddr guest_addr; ///< Address of the guest condition variable value - VAddr mutex_addr; ///< (optional) Address of guest mutex value associated with this condition - ///< variable, used for implementing events - - bool ShouldWait(Thread* thread) const override; - void Acquire(Thread* thread) override; - - /** - * Releases a slot from a condition variable. - * @param target The number of threads to wakeup, -1 is all. - * @return ResultCode indicating if the operation succeeded. - */ - ResultCode Release(s32 target); - -private: - ConditionVariable(); - ~ConditionVariable() override; -}; - -} // namespace Kernel diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index 29d8dfdaa..e1b5430bf 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h @@ -20,7 +20,10 @@ enum { MaxConnectionsReached = 52, // Confirmed Switch OS error codes + MisalignedAddress = 102, + InvalidProcessorId = 113, InvalidHandle = 114, + InvalidCombination = 116, Timeout = 117, SynchronizationCanceled = 118, TooLarge = 119, diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp index 3beb55753..f7a9920d8 100644 --- a/src/core/hle/kernel/handle_table.cpp +++ b/src/core/hle/kernel/handle_table.cpp @@ -5,6 +5,7 @@ #include <utility> #include "common/assert.h" #include "common/logging/log.h" +#include "core/core.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" @@ -25,7 +26,7 @@ ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { u16 slot = next_free_slot; if (slot >= generations.size()) { - LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use."); + NGLOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use."); return ERR_OUT_OF_HANDLES; } next_free_slot = generations[slot]; @@ -47,7 +48,7 @@ ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { ResultVal<Handle> HandleTable::Duplicate(Handle handle) { SharedPtr<Object> object = GetGeneric(handle); if (object == nullptr) { - LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle); + NGLOG_ERROR(Kernel, "Tried to duplicate invalid handle: {:08X}", handle); return ERR_INVALID_HANDLE; } return Create(std::move(object)); @@ -77,7 +78,7 @@ SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const { if (handle == CurrentThread) { return GetCurrentThread(); } else if (handle == CurrentProcess) { - return g_current_process; + return Core::CurrentProcess(); } if (!IsValid(handle)) { diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index d9faf4b53..01904467e 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -7,6 +7,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/event.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/kernel.h" @@ -26,6 +27,32 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s boost::range::remove_erase(connected_sessions, server_session); } +SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread, + const std::string& reason, u64 timeout, + WakeupCallback&& callback) { + + // Put the client thread to sleep until the wait event is signaled or the timeout expires. + thread->wakeup_callback = + [context = *this, callback](ThreadWakeupReason reason, SharedPtr<Thread> thread, + SharedPtr<WaitObject> object, size_t index) mutable -> bool { + ASSERT(thread->status == THREADSTATUS_WAIT_HLE_EVENT); + callback(thread, context, reason); + context.WriteToOutgoingCommandBuffer(*thread); + return true; + }; + + auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason); + thread->status = THREADSTATUS_WAIT_HLE_EVENT; + thread->wait_objects = {event}; + event->AddWaitingThread(thread); + + if (timeout > 0) { + thread->WakeAfterDelay(timeout); + } + + return event; +} + HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session) : server_session(std::move(server_session)) { cmd_buf[0] = 0; @@ -35,7 +62,7 @@ HLERequestContext::~HLERequestContext() = default; void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { IPC::RequestParser rp(src_cmdbuf); - command_header = std::make_unique<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>()); + command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>()); if (command_header->type == IPC::CommandType::Close) { // Close does not populate the rest of the IPC header @@ -45,7 +72,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { // If handle descriptor is present, add size of it if (command_header->enable_handle_descriptor) { handle_descriptor_header = - std::make_unique<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>()); + std::make_shared<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>()); if (handle_descriptor_header->send_current_pid) { rp.Skip(2, false); } @@ -83,20 +110,22 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { // Padding to align to 16 bytes rp.AlignWithPadding(); - if (Session()->IsDomain() && (command_header->type == IPC::CommandType::Request || !incoming)) { + if (Session()->IsDomain() && ((command_header->type == IPC::CommandType::Request || + command_header->type == IPC::CommandType::RequestWithContext) || + !incoming)) { // If this is an incoming message, only CommandType "Request" has a domain header // All outgoing domain messages have the domain header, if only incoming has it if (incoming || domain_message_header) { domain_message_header = - std::make_unique<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>()); + std::make_shared<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>()); } else { if (Session()->IsDomain()) - LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!"); + NGLOG_WARNING(IPC, "Domain request has no DomainMessageHeader!"); } } data_payload_header = - std::make_unique<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>()); + std::make_shared<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>()); data_payload_offset = rp.GetCurrentOffset(); @@ -159,8 +188,11 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb return RESULT_SUCCESS; } -ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, - HandleTable& dst_table) { +ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { + std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf; + Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), + dst_cmdbuf.size() * sizeof(u32)); + // The header was already built in the internal command buffer. Attempt to parse it to verify // the integrity and then copy it over to the target command buffer. ParseCommandBuffer(cmd_buf.data(), false); @@ -171,7 +203,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P if (domain_message_header) size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32); - std::copy_n(cmd_buf.begin(), size, dst_cmdbuf); + std::copy_n(cmd_buf.begin(), size, dst_cmdbuf.data()); if (command_header->enable_handle_descriptor) { ASSERT_MSG(!move_objects.empty() || !copy_objects.empty(), @@ -213,50 +245,63 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P dst_cmdbuf[domain_offset++] = static_cast<u32_le>(request_handlers.size()); } } + + // Copy the translated command buffer back into the thread's command buffer area. + Memory::WriteBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), + dst_cmdbuf.size() * sizeof(u32)); + return RESULT_SUCCESS; } -std::vector<u8> HLERequestContext::ReadBuffer() const { +std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const { std::vector<u8> buffer; - const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[0].Size()}; + const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()}; if (is_buffer_a) { - buffer.resize(BufferDescriptorA()[0].Size()); - Memory::ReadBlock(BufferDescriptorA()[0].Address(), buffer.data(), buffer.size()); + buffer.resize(BufferDescriptorA()[buffer_index].Size()); + Memory::ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), + buffer.size()); } else { - buffer.resize(BufferDescriptorX()[0].Size()); - Memory::ReadBlock(BufferDescriptorX()[0].Address(), buffer.data(), buffer.size()); + buffer.resize(BufferDescriptorX()[buffer_index].Size()); + Memory::ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), + buffer.size()); } return buffer; } -size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size) const { - const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[0].Size()}; - - ASSERT_MSG(size <= GetWriteBufferSize(), "Size %d is too big", size); +size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size, int buffer_index) const { + const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; + const size_t buffer_size{GetWriteBufferSize(buffer_index)}; + if (size > buffer_size) { + NGLOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, + buffer_size); + size = buffer_size; // TODO(bunnei): This needs to be HW tested + } if (is_buffer_b) { - Memory::WriteBlock(BufferDescriptorB()[0].Address(), buffer, size); + Memory::WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); } else { - Memory::WriteBlock(BufferDescriptorC()[0].Address(), buffer, size); + Memory::WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); } return size; } -size_t HLERequestContext::WriteBuffer(const std::vector<u8>& buffer) const { +size_t HLERequestContext::WriteBuffer(const std::vector<u8>& buffer, int buffer_index) const { return WriteBuffer(buffer.data(), buffer.size()); } -size_t HLERequestContext::GetReadBufferSize() const { - const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[0].Size()}; - return is_buffer_a ? BufferDescriptorA()[0].Size() : BufferDescriptorX()[0].Size(); +size_t HLERequestContext::GetReadBufferSize(int buffer_index) const { + const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()}; + return is_buffer_a ? BufferDescriptorA()[buffer_index].Size() + : BufferDescriptorX()[buffer_index].Size(); } -size_t HLERequestContext::GetWriteBufferSize() const { - const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[0].Size()}; - return is_buffer_b ? BufferDescriptorB()[0].Size() : BufferDescriptorC()[0].Size(); +size_t HLERequestContext::GetWriteBufferSize(int buffer_index) const { + const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; + return is_buffer_b ? BufferDescriptorB()[buffer_index].Size() + : BufferDescriptorC()[buffer_index].Size(); } std::string HLERequestContext::Description() const { diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index b5631b773..376263eac 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -6,6 +6,7 @@ #include <array> #include <memory> +#include <string> #include <vector> #include <boost/container/small_vector.hpp> #include "common/common_types.h" @@ -13,6 +14,7 @@ #include "core/hle/ipc.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/server_session.h" +#include "core/hle/kernel/thread.h" namespace Service { class ServiceFrameworkBase; @@ -24,6 +26,7 @@ class Domain; class HandleTable; class HLERequestContext; class Process; +class Event; /** * Interface implemented by HLE Session handlers. @@ -102,14 +105,31 @@ public: return server_session; } + using WakeupCallback = std::function<void(SharedPtr<Thread> thread, HLERequestContext& context, + ThreadWakeupReason reason)>; + + /** + * Puts the specified guest thread to sleep until the returned event is signaled or until the + * specified timeout expires. + * @param thread Thread to be put to sleep. + * @param reason Reason for pausing the thread, to be used for debugging purposes. + * @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback + * invoked with a Timeout reason. + * @param callback Callback to be invoked when the thread is resumed. This callback must write + * the entire command response once again, regardless of the state of it before this function + * was called. + * @returns Event that when signaled will resume the thread and call the callback function. + */ + SharedPtr<Event> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason, + u64 timeout, WakeupCallback&& callback); + void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming); /// Populates this context with data from the requesting process/thread. ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process, HandleTable& src_table); /// Writes data from this context back to the requesting process/thread. - ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, - HandleTable& dst_table); + ResultCode WriteToOutgoingCommandBuffer(Thread& thread); u32_le GetCommand() const { return command; @@ -139,24 +159,24 @@ public: return buffer_c_desciptors; } - const std::unique_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const { + const std::shared_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const { return domain_message_header; } /// Helper function to read a buffer using the appropriate buffer descriptor - std::vector<u8> ReadBuffer() const; + std::vector<u8> ReadBuffer(int buffer_index = 0) const; /// Helper function to write a buffer using the appropriate buffer descriptor - size_t WriteBuffer(const void* buffer, size_t size) const; + size_t WriteBuffer(const void* buffer, size_t size, int buffer_index = 0) const; /// Helper function to write a buffer using the appropriate buffer descriptor - size_t WriteBuffer(const std::vector<u8>& buffer) const; + size_t WriteBuffer(const std::vector<u8>& buffer, int buffer_index = 0) const; /// Helper function to get the size of the input buffer - size_t GetReadBufferSize() const; + size_t GetReadBufferSize(int buffer_index = 0) const; /// Helper function to get the size of the output buffer - size_t GetWriteBufferSize() const; + size_t GetWriteBufferSize(int buffer_index = 0) const; template <typename T> SharedPtr<T> GetCopyObject(size_t index) { @@ -182,6 +202,16 @@ public: domain_objects.emplace_back(std::move(object)); } + template <typename T> + std::shared_ptr<T> GetDomainRequestHandler(size_t index) const { + return std::static_pointer_cast<T>(domain_request_handlers[index]); + } + + void SetDomainRequestHandlers( + const std::vector<std::shared_ptr<SessionRequestHandler>>& handlers) { + domain_request_handlers = handlers; + } + /// Clears the list of objects so that no lingering objects are written accidentally to the /// response buffer. void ClearIncomingObjects() { @@ -212,10 +242,10 @@ private: boost::container::small_vector<SharedPtr<Object>, 8> copy_objects; boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects; - std::unique_ptr<IPC::CommandHeader> command_header; - std::unique_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header; - std::unique_ptr<IPC::DataPayloadHeader> data_payload_header; - std::unique_ptr<IPC::DomainMessageHeader> domain_message_header; + std::shared_ptr<IPC::CommandHeader> command_header; + std::shared_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header; + std::shared_ptr<IPC::DataPayloadHeader> data_payload_header; + std::shared_ptr<IPC::DomainMessageHeader> domain_message_header; std::vector<IPC::BufferDescriptorX> buffer_x_desciptors; std::vector<IPC::BufferDescriptorABW> buffer_a_desciptors; std::vector<IPC::BufferDescriptorABW> buffer_b_desciptors; @@ -225,6 +255,8 @@ private: unsigned data_payload_offset{}; unsigned buffer_c_offset{}; u32_le command{}; + + std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers; }; } // namespace Kernel diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index b0c3f4ae1..b325b879b 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -41,7 +41,6 @@ void Shutdown() { g_object_address_table.Clear(); Kernel::ThreadingShutdown(); - g_current_process = nullptr; Kernel::TimersShutdown(); Kernel::ResourceLimitsShutdown(); diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index c77e58f3c..402ae900f 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -18,12 +18,10 @@ using Handle = u32; enum class HandleType : u32 { Unknown, Event, - Mutex, SharedMemory, Thread, Process, AddressArbiter, - ConditionVariable, Timer, ResourceLimit, CodeSet, @@ -33,10 +31,6 @@ enum class HandleType : u32 { ServerSession, }; -enum { - DEFAULT_STACK_SIZE = 0x10000, -}; - enum class ResetType { OneShot, Sticky, @@ -67,9 +61,7 @@ public: bool IsWaitable() const { switch (GetHandleType()) { case HandleType::Event: - case HandleType::Mutex: case HandleType::Thread: - case HandleType::ConditionVariable: case HandleType::Timer: case HandleType::ServerPort: case HandleType::ServerSession: diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 0b9dc700c..bc144f3de 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -7,6 +7,7 @@ #include <boost/range/algorithm_ext/erase.hpp> #include "common/assert.h" #include "core/core.h" +#include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/mutex.h" @@ -15,124 +16,119 @@ namespace Kernel { -void ReleaseThreadMutexes(Thread* thread) { - for (auto& mtx : thread->held_mutexes) { - mtx->SetHasWaiters(false); - mtx->SetHoldingThread(nullptr); - mtx->WakeupAllWaitingThreads(); - } - thread->held_mutexes.clear(); -} +/// Returns the number of threads that are waiting for a mutex, and the highest priority one among +/// those. +static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread( + SharedPtr<Thread> current_thread, VAddr mutex_addr) { -Mutex::Mutex() {} -Mutex::~Mutex() {} + SharedPtr<Thread> highest_priority_thread; + u32 num_waiters = 0; -SharedPtr<Mutex> Mutex::Create(SharedPtr<Kernel::Thread> holding_thread, VAddr guest_addr, - std::string name) { - SharedPtr<Mutex> mutex(new Mutex); + for (auto& thread : current_thread->wait_mutex_threads) { + if (thread->mutex_wait_address != mutex_addr) + continue; - mutex->guest_addr = guest_addr; - mutex->name = std::move(name); + ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX); - // If mutex was initialized with a holding thread, acquire it by the holding thread - if (holding_thread) { - mutex->Acquire(holding_thread.get()); + ++num_waiters; + if (highest_priority_thread == nullptr || + thread->GetPriority() < highest_priority_thread->GetPriority()) { + highest_priority_thread = thread; + } } - // Mutexes are referenced by guest address, so track this in the kernel - g_object_address_table.Insert(guest_addr, mutex); - - return mutex; + return {highest_priority_thread, num_waiters}; } -bool Mutex::ShouldWait(Thread* thread) const { - auto holding_thread = GetHoldingThread(); - return holding_thread != nullptr && thread != holding_thread; +/// Update the mutex owner field of all threads waiting on the mutex to point to the new owner. +static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_thread, + SharedPtr<Thread> new_owner) { + auto threads = current_thread->wait_mutex_threads; + for (auto& thread : threads) { + if (thread->mutex_wait_address != mutex_addr) + continue; + + ASSERT(thread->lock_owner == current_thread); + current_thread->RemoveMutexWaiter(thread); + if (new_owner != thread) + new_owner->AddMutexWaiter(thread); + } } -void Mutex::Acquire(Thread* thread) { - ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); +ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, + Handle requesting_thread_handle) { + // The mutex address must be 4-byte aligned + if ((address % sizeof(u32)) != 0) { + return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress); + } - priority = thread->current_priority; - thread->held_mutexes.insert(this); - SetHoldingThread(thread); - thread->UpdatePriority(); - Core::System::GetInstance().PrepareReschedule(); -} + SharedPtr<Thread> holding_thread = g_handle_table.Get<Thread>(holding_thread_handle); + SharedPtr<Thread> requesting_thread = g_handle_table.Get<Thread>(requesting_thread_handle); -ResultCode Mutex::Release(Thread* thread) { - auto holding_thread = GetHoldingThread(); - ASSERT(holding_thread); + // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another + // thread. + ASSERT(requesting_thread == GetCurrentThread()); - // We can only release the mutex if it's held by the calling thread. - ASSERT(thread == holding_thread); + u32 addr_value = Memory::Read32(address); + + // If the mutex isn't being held, just return success. + if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) { + return RESULT_SUCCESS; + } + + if (holding_thread == nullptr) + return ERR_INVALID_HANDLE; + + // Wait until the mutex is released + GetCurrentThread()->mutex_wait_address = address; + GetCurrentThread()->wait_handle = requesting_thread_handle; + + GetCurrentThread()->status = THREADSTATUS_WAIT_MUTEX; + GetCurrentThread()->wakeup_callback = nullptr; + + // Update the lock holder thread's priority to prevent priority inversion. + holding_thread->AddMutexWaiter(GetCurrentThread()); - holding_thread->held_mutexes.erase(this); - holding_thread->UpdatePriority(); - SetHoldingThread(nullptr); - SetHasWaiters(!GetWaitingThreads().empty()); - WakeupAllWaitingThreads(); Core::System::GetInstance().PrepareReschedule(); return RESULT_SUCCESS; } -void Mutex::AddWaitingThread(SharedPtr<Thread> thread) { - WaitObject::AddWaitingThread(thread); - thread->pending_mutexes.insert(this); - SetHasWaiters(true); - UpdatePriority(); -} - -void Mutex::RemoveWaitingThread(Thread* thread) { - WaitObject::RemoveWaitingThread(thread); - thread->pending_mutexes.erase(this); - if (!GetHasWaiters()) - SetHasWaiters(!GetWaitingThreads().empty()); - UpdatePriority(); -} +ResultCode Mutex::Release(VAddr address) { + // The mutex address must be 4-byte aligned + if ((address % sizeof(u32)) != 0) { + return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress); + } -void Mutex::UpdatePriority() { - if (!GetHoldingThread()) - return; + auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address); - u32 best_priority = THREADPRIO_LOWEST; - for (auto& waiter : GetWaitingThreads()) { - if (waiter->current_priority < best_priority) - best_priority = waiter->current_priority; + // There are no more threads waiting for the mutex, release it completely. + if (thread == nullptr) { + Memory::Write32(address, 0); + return RESULT_SUCCESS; } - if (best_priority != priority) { - priority = best_priority; - GetHoldingThread()->UpdatePriority(); - } -} + // Transfer the ownership of the mutex from the previous owner to the new one. + TransferMutexOwnership(address, GetCurrentThread(), thread); -Handle Mutex::GetOwnerHandle() const { - GuestState guest_state{Memory::Read32(guest_addr)}; - return guest_state.holding_thread_handle; -} + u32 mutex_value = thread->wait_handle; -SharedPtr<Thread> Mutex::GetHoldingThread() const { - GuestState guest_state{Memory::Read32(guest_addr)}; - return g_handle_table.Get<Thread>(guest_state.holding_thread_handle); -} + if (num_waiters >= 2) { + // Notify the guest that there are still some threads waiting for the mutex + mutex_value |= Mutex::MutexHasWaitersFlag; + } -void Mutex::SetHoldingThread(SharedPtr<Thread> thread) { - GuestState guest_state{Memory::Read32(guest_addr)}; - guest_state.holding_thread_handle.Assign(thread ? thread->guest_handle : 0); - Memory::Write32(guest_addr, guest_state.raw); -} + // Grant the mutex to the next waiting thread and resume it. + Memory::Write32(address, mutex_value); -bool Mutex::GetHasWaiters() const { - GuestState guest_state{Memory::Read32(guest_addr)}; - return guest_state.has_waiters != 0; -} + ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX); + thread->ResumeFromWait(); -void Mutex::SetHasWaiters(bool has_waiters) { - GuestState guest_state{Memory::Read32(guest_addr)}; - guest_state.has_waiters.Assign(has_waiters ? 1 : 0); - Memory::Write32(guest_addr, guest_state.raw); -} + thread->lock_owner = nullptr; + thread->condvar_wait_address = 0; + thread->mutex_wait_address = 0; + thread->wait_handle = 0; + return RESULT_SUCCESS; +} } // namespace Kernel diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index 38db21005..3117e7c70 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -15,87 +15,23 @@ namespace Kernel { class Thread; -class Mutex final : public WaitObject { +class Mutex final { public: - /** - * Creates a mutex. - * @param holding_thread Specifies a thread already holding the mutex. If not nullptr, this - * thread will acquire the mutex. - * @param guest_addr Address of the object tracking the mutex in guest memory. If specified, - * this mutex will update the guest object when its state changes. - * @param name Optional name of mutex - * @return Pointer to new Mutex object - */ - static SharedPtr<Mutex> Create(SharedPtr<Kernel::Thread> holding_thread, VAddr guest_addr = 0, - std::string name = "Unknown"); + /// Flag that indicates that a mutex still has threads waiting for it. + static constexpr u32 MutexHasWaitersFlag = 0x40000000; + /// Mask of the bits in a mutex address value that contain the mutex owner. + static constexpr u32 MutexOwnerMask = 0xBFFFFFFF; - std::string GetTypeName() const override { - return "Mutex"; - } - std::string GetName() const override { - return name; - } + /// Attempts to acquire a mutex at the specified address. + static ResultCode TryAcquire(VAddr address, Handle holding_thread_handle, + Handle requesting_thread_handle); - static const HandleType HANDLE_TYPE = HandleType::Mutex; - HandleType GetHandleType() const override { - return HANDLE_TYPE; - } - - u32 priority; ///< The priority of the mutex, used for priority inheritance. - std::string name; ///< Name of mutex (optional) - VAddr guest_addr; ///< Address of the guest mutex value - - /** - * Elevate the mutex priority to the best priority - * among the priorities of all its waiting threads. - */ - void UpdatePriority(); - - bool ShouldWait(Thread* thread) const override; - void Acquire(Thread* thread) override; - - void AddWaitingThread(SharedPtr<Thread> thread) override; - void RemoveWaitingThread(Thread* thread) override; - - /** - * Attempts to release the mutex from the specified thread. - * @param thread Thread that wants to release the mutex. - * @returns The result code of the operation. - */ - ResultCode Release(Thread* thread); - - /// Gets the handle to the holding process stored in the guest state. - Handle GetOwnerHandle() const; - - /// Gets the Thread pointed to by the owner handle - SharedPtr<Thread> GetHoldingThread() const; - /// Sets the holding process handle in the guest state. - void SetHoldingThread(SharedPtr<Thread> thread); - - /// Returns the has_waiters bit in the guest state. - bool GetHasWaiters() const; - /// Sets the has_waiters bit in the guest state. - void SetHasWaiters(bool has_waiters); + /// Releases the mutex at the specified address. + static ResultCode Release(VAddr address); private: - Mutex(); - ~Mutex() override; - - /// Object in guest memory used to track the mutex state - union GuestState { - u32_le raw; - /// Handle of the thread that currently holds the mutex, 0 if available - BitField<0, 30, u32_le> holding_thread_handle; - /// 1 when there are threads waiting for this mutex, otherwise 0 - BitField<30, 1, u32_le> has_waiters; - }; - static_assert(sizeof(GuestState) == 4, "GuestState size is incorrect"); + Mutex() = default; + ~Mutex() = default; }; -/** - * Releases all the mutexes held by the specified thread - * @param thread Thread that is holding the mutexes - */ -void ReleaseThreadMutexes(Thread* thread); - } // namespace Kernel diff --git a/src/core/hle/kernel/object_address_table.cpp b/src/core/hle/kernel/object_address_table.cpp index 434c16add..ec97f6f8e 100644 --- a/src/core/hle/kernel/object_address_table.cpp +++ b/src/core/hle/kernel/object_address_table.cpp @@ -10,12 +10,12 @@ namespace Kernel { ObjectAddressTable g_object_address_table; void ObjectAddressTable::Insert(VAddr addr, SharedPtr<Object> obj) { - ASSERT_MSG(objects.find(addr) == objects.end(), "Object already exists with addr=0x%llx", addr); + ASSERT_MSG(objects.find(addr) == objects.end(), "Object already exists with addr=0x{:X}", addr); objects[addr] = obj; } void ObjectAddressTable::Close(VAddr addr) { - ASSERT_MSG(objects.find(addr) != objects.end(), "Object does not exist with addr=0x%llx", addr); + ASSERT_MSG(objects.find(addr) != objects.end(), "Object does not exist with addr=0x{:X}", addr); objects.erase(addr); } diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 8e74059ea..651d932d3 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -20,12 +20,9 @@ namespace Kernel { // Lists all processes that exist in the current session. static std::vector<SharedPtr<Process>> process_list; -SharedPtr<CodeSet> CodeSet::Create(std::string name, u64 program_id) { +SharedPtr<CodeSet> CodeSet::Create(std::string name) { SharedPtr<CodeSet> codeset(new CodeSet); - codeset->name = std::move(name); - codeset->program_id = program_id; - return codeset; } @@ -41,6 +38,7 @@ SharedPtr<Process> Process::Create(std::string&& name) { process->flags.raw = 0; process->flags.memory_region.Assign(MemoryRegion::APPLICATION); process->status = ProcessStatus::Created; + process->program_id = 0; process_list.push_back(process); return process; @@ -56,7 +54,7 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { continue; } else if ((type & 0xF00) == 0xE00) { // 0x0FFF // Allowed interrupts list - LOG_WARNING(Loader, "ExHeader allowed interrupts list ignored"); + NGLOG_WARNING(Loader, "ExHeader allowed interrupts list ignored"); } else if ((type & 0xF80) == 0xF00) { // 0x07FF // Allowed syscalls mask unsigned int index = ((descriptor >> 24) & 7) * 24; @@ -76,7 +74,7 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { } else if ((type & 0xFFE) == 0xFF8) { // 0x001F // Mapped memory range if (i + 1 >= len || ((kernel_caps[i + 1] >> 20) & 0xFFE) != 0xFF8) { - LOG_WARNING(Loader, "Incomplete exheader memory range descriptor ignored."); + NGLOG_WARNING(Loader, "Incomplete exheader memory range descriptor ignored."); continue; } u32 end_desc = kernel_caps[i + 1]; @@ -111,19 +109,21 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { int minor = kernel_version & 0xFF; int major = (kernel_version >> 8) & 0xFF; - LOG_INFO(Loader, "ExHeader kernel version: %d.%d", major, minor); + NGLOG_INFO(Loader, "ExHeader kernel version: {}.{}", major, minor); } else { - LOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x%08X", descriptor); + NGLOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x{:08X}", descriptor); } } } void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { - // Allocate and map stack + // Allocate and map the main thread stack + // TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part + // of the user address space. vm_manager - .MapMemoryBlock(Memory::HEAP_VADDR_END - stack_size, + .MapMemoryBlock(Memory::STACK_AREA_VADDR_END - stack_size, std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, - MemoryState::Heap) + MemoryState::Mapped) .Unwrap(); misc_memory_used += stack_size; memory_region->used += stack_size; @@ -134,7 +134,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { HandleSpecialMapping(vm_manager, mapping); } - vm_manager.LogLayout(Log::Level::Debug); + vm_manager.LogLayout(); status = ProcessStatus::Running; Kernel::SetupMainThread(entry_point, main_thread_priority, this); @@ -155,9 +155,9 @@ void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { }; // Map CodeSet segments - MapSegment(module_->code, VMAPermission::ReadExecute, MemoryState::Code); - MapSegment(module_->rodata, VMAPermission::Read, MemoryState::Static); - MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::Static); + MapSegment(module_->code, VMAPermission::ReadExecute, MemoryState::CodeStatic); + MapSegment(module_->rodata, VMAPermission::Read, MemoryState::CodeMutable); + MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::CodeMutable); } VAddr Process::GetLinearHeapAreaAddress() const { @@ -184,6 +184,8 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per // Initialize heap heap_memory = std::make_shared<std::vector<u8>>(); heap_start = heap_end = target; + } else { + vm_manager.UnmapRange(heap_start, heap_end - heap_start); } // If necessary, expand backing vector to cover new heap extents. @@ -203,7 +205,7 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per size, MemoryState::Heap)); vm_manager.Reprotect(vma, perms); - heap_used += size; + heap_used = size; memory_region->used += size; return MakeResult<VAddr>(heap_end - size); @@ -290,7 +292,7 @@ ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { CASCADE_RESULT(auto new_vma, vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, - vma->second.meminfo_state)); + MemoryState::Mapped)); // Protect mirror with permissions from old region vm_manager.Reprotect(new_vma, vma->second.permissions); // Remove permissions from old region @@ -321,5 +323,4 @@ SharedPtr<Process> GetProcessById(u32 process_id) { return *itr; } -SharedPtr<Process> g_current_process; } // namespace Kernel diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index add98472f..68e77a4d1 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -56,7 +56,7 @@ class ResourceLimit; struct MemoryRegionInfo; struct CodeSet final : public Object { - static SharedPtr<CodeSet> Create(std::string name, u64 program_id); + static SharedPtr<CodeSet> Create(std::string name); std::string GetTypeName() const override { return "CodeSet"; @@ -72,8 +72,6 @@ struct CodeSet final : public Object { /// Name of the process std::string name; - /// Title ID corresponding to the process - u64 program_id; std::shared_ptr<std::vector<u8>> memory; @@ -113,6 +111,9 @@ public: static u32 next_process_id; + /// Title ID corresponding to the process + u64 program_id; + /// Resource limit descriptor for this process SharedPtr<ResourceLimit> resource_limit; @@ -202,5 +203,4 @@ void ClearProcessList(); /// Retrieves a process from the current list of processes. SharedPtr<Process> GetProcessById(u32 process_id); -extern SharedPtr<Process> g_current_process; } // namespace Kernel diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp index 0149a3ed6..0ef5fc57d 100644 --- a/src/core/hle/kernel/resource_limit.cpp +++ b/src/core/hle/kernel/resource_limit.cpp @@ -29,62 +29,62 @@ SharedPtr<ResourceLimit> ResourceLimit::GetForCategory(ResourceLimitCategory cat case ResourceLimitCategory::OTHER: return resource_limits[static_cast<u8>(category)]; default: - LOG_CRITICAL(Kernel, "Unknown resource limit category"); + NGLOG_CRITICAL(Kernel, "Unknown resource limit category"); UNREACHABLE(); } } -s32 ResourceLimit::GetCurrentResourceValue(u32 resource) const { +s32 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const { switch (resource) { - case COMMIT: + case ResourceType::Commit: return current_commit; - case THREAD: + case ResourceType::Thread: return current_threads; - case EVENT: + case ResourceType::Event: return current_events; - case MUTEX: + case ResourceType::Mutex: return current_mutexes; - case SEMAPHORE: + case ResourceType::Semaphore: return current_semaphores; - case TIMER: + case ResourceType::Timer: return current_timers; - case SHARED_MEMORY: + case ResourceType::SharedMemory: return current_shared_mems; - case ADDRESS_ARBITER: + case ResourceType::AddressArbiter: return current_address_arbiters; - case CPU_TIME: + case ResourceType::CPUTime: return current_cpu_time; default: - LOG_ERROR(Kernel, "Unknown resource type=%08X", resource); + NGLOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource)); UNIMPLEMENTED(); return 0; } } -u32 ResourceLimit::GetMaxResourceValue(u32 resource) const { +u32 ResourceLimit::GetMaxResourceValue(ResourceType resource) const { switch (resource) { - case PRIORITY: + case ResourceType::Priority: return max_priority; - case COMMIT: + case ResourceType::Commit: return max_commit; - case THREAD: + case ResourceType::Thread: return max_threads; - case EVENT: + case ResourceType::Event: return max_events; - case MUTEX: + case ResourceType::Mutex: return max_mutexes; - case SEMAPHORE: + case ResourceType::Semaphore: return max_semaphores; - case TIMER: + case ResourceType::Timer: return max_timers; - case SHARED_MEMORY: + case ResourceType::SharedMemory: return max_shared_mems; - case ADDRESS_ARBITER: + case ResourceType::AddressArbiter: return max_address_arbiters; - case CPU_TIME: + case ResourceType::CPUTime: return max_cpu_time; default: - LOG_ERROR(Kernel, "Unknown resource type=%08X", resource); + NGLOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource)); UNIMPLEMENTED(); return 0; } diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h index 1a0ca11f1..cc689a27a 100644 --- a/src/core/hle/kernel/resource_limit.h +++ b/src/core/hle/kernel/resource_limit.h @@ -16,17 +16,17 @@ enum class ResourceLimitCategory : u8 { OTHER = 3 }; -enum ResourceTypes { - PRIORITY = 0, - COMMIT = 1, - THREAD = 2, - EVENT = 3, - MUTEX = 4, - SEMAPHORE = 5, - TIMER = 6, - SHARED_MEMORY = 7, - ADDRESS_ARBITER = 8, - CPU_TIME = 9, +enum class ResourceType { + Priority = 0, + Commit = 1, + Thread = 2, + Event = 3, + Mutex = 4, + Semaphore = 5, + Timer = 6, + SharedMemory = 7, + AddressArbiter = 8, + CPUTime = 9, }; class ResourceLimit final : public Object { @@ -60,14 +60,14 @@ public: * @param resource Requested resource type * @returns The current value of the resource type */ - s32 GetCurrentResourceValue(u32 resource) const; + s32 GetCurrentResourceValue(ResourceType resource) const; /** * Gets the max value for the specified resource. * @param resource Requested resource type * @returns The max value of the resource type */ - u32 GetMaxResourceValue(u32 resource) const; + u32 GetMaxResourceValue(ResourceType resource) const; /// Name of resource limit object. std::string name; diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 235068b22..9cb9e0e5c 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -2,12 +2,15 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/core.h" #include "core/core_timing.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/scheduler.h" namespace Kernel { +std::mutex Scheduler::scheduler_mutex; + Scheduler::Scheduler(ARM_Interface* cpu_core) : cpu_core(cpu_core) {} Scheduler::~Scheduler() { @@ -17,6 +20,7 @@ Scheduler::~Scheduler() { } bool Scheduler::HaveReadyThreads() { + std::lock_guard<std::mutex> lock(scheduler_mutex); return ready_queue.get_first() != nullptr; } @@ -67,7 +71,7 @@ void Scheduler::SwitchContext(Thread* new_thread) { // Cancel any outstanding wakeup events for this thread new_thread->CancelWakeupTimer(); - auto previous_process = Kernel::g_current_process; + auto previous_process = Core::CurrentProcess(); current_thread = new_thread; @@ -75,8 +79,8 @@ void Scheduler::SwitchContext(Thread* new_thread) { new_thread->status = THREADSTATUS_RUNNING; if (previous_process != current_thread->owner_process) { - Kernel::g_current_process = current_thread->owner_process; - SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); + Core::CurrentProcess() = current_thread->owner_process; + SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table); } cpu_core->LoadContext(new_thread->context); @@ -89,41 +93,53 @@ void Scheduler::SwitchContext(Thread* new_thread) { } void Scheduler::Reschedule() { + std::lock_guard<std::mutex> lock(scheduler_mutex); + Thread* cur = GetCurrentThread(); Thread* next = PopNextReadyThread(); if (cur && next) { - LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId()); + NGLOG_TRACE(Kernel, "context switch {} -> {}", cur->GetObjectId(), next->GetObjectId()); } else if (cur) { - LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId()); + NGLOG_TRACE(Kernel, "context switch {} -> idle", cur->GetObjectId()); } else if (next) { - LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId()); + NGLOG_TRACE(Kernel, "context switch idle -> {}", next->GetObjectId()); } SwitchContext(next); } void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) { + std::lock_guard<std::mutex> lock(scheduler_mutex); + thread_list.push_back(thread); ready_queue.prepare(priority); } void Scheduler::RemoveThread(Thread* thread) { + std::lock_guard<std::mutex> lock(scheduler_mutex); + thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), thread_list.end()); } void Scheduler::ScheduleThread(Thread* thread, u32 priority) { + std::lock_guard<std::mutex> lock(scheduler_mutex); + ASSERT(thread->status == THREADSTATUS_READY); ready_queue.push_back(priority, thread); } void Scheduler::UnscheduleThread(Thread* thread, u32 priority) { + std::lock_guard<std::mutex> lock(scheduler_mutex); + ASSERT(thread->status == THREADSTATUS_READY); ready_queue.remove(priority, thread); } void Scheduler::SetThreadPriority(Thread* thread, u32 priority) { + std::lock_guard<std::mutex> lock(scheduler_mutex); + // If thread was ready, adjust queues if (thread->status == THREADSTATUS_READY) ready_queue.move(thread, thread->current_priority, priority); diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 27d0247d6..a3b5fb8ca 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -4,6 +4,7 @@ #pragma once +#include <mutex> #include <vector> #include "common/common_types.h" #include "common/thread_queue_list.h" @@ -68,6 +69,8 @@ private: SharedPtr<Thread> current_thread = nullptr; ARM_Interface* cpu_core; + + static std::mutex scheduler_mutex; }; } // namespace Kernel diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 5f31cf19b..bf812c543 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -4,6 +4,7 @@ #include <tuple> +#include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" @@ -60,6 +61,9 @@ void ServerSession::Acquire(Thread* thread) { ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) { auto& domain_message_header = context.GetDomainMessageHeader(); if (domain_message_header) { + // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs + context.SetDomainRequestHandlers(domain_request_handlers); + // If there is a DomainMessageHeader, then this is CommandType "Request" const u32 object_id{context.GetDomainMessageHeader()->object_id}; switch (domain_message_header->command) { @@ -67,7 +71,7 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con return domain_request_handlers[object_id - 1]->HandleSyncRequest(context); case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { - LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x%08X", object_id); + NGLOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id); domain_request_handlers[object_id - 1] = nullptr; @@ -77,7 +81,8 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con } } - LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value()); + NGLOG_CRITICAL(IPC, "Unknown domain command={}", + static_cast<int>(domain_message_header->command.Value())); ASSERT(false); } @@ -91,7 +96,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) { Kernel::HLERequestContext context(this); u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress()); - context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process, + context.PopulateFromIncomingCommandBuffer(cmd_buf, *Core::CurrentProcess(), Kernel::g_handle_table); ResultCode result = RESULT_SUCCESS; diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index d4505061e..ac4921298 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -4,6 +4,7 @@ #include <cstring> #include "common/logging/log.h" +#include "core/core.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/shared_memory.h" @@ -51,8 +52,8 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u } // Refresh the address mappings for the current process. - if (Kernel::g_current_process != nullptr) { - Kernel::g_current_process->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); + if (Core::CurrentProcess() != nullptr) { + Core::CurrentProcess()->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); } } else { auto& vm_manager = shared_memory->owner_process->vm_manager; @@ -106,31 +107,19 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi // Error out if the requested permissions don't match what the creator process allows. if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) { - LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, permissions don't match", - GetObjectId(), address, name.c_str()); + NGLOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", + GetObjectId(), address, name); return ERR_INVALID_COMBINATION; } // Error out if the provided permissions are not compatible with what the creator process needs. if (other_permissions != MemoryPermission::DontCare && static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) { - LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, permissions don't match", - GetObjectId(), address, name.c_str()); + NGLOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", + GetObjectId(), address, name); return ERR_WRONG_PERMISSION; } - // TODO(Subv): The same process that created a SharedMemory object - // can not map it in its own address space unless it was created with addr=0, result 0xD900182C. - - if (address != 0) { - // TODO(shinyquagsire23): Check for virtual/mappable memory here too? - if (address >= Memory::HEAP_VADDR && address < Memory::HEAP_VADDR_END) { - LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, invalid address", - GetObjectId(), address, name.c_str()); - return ERR_INVALID_ADDRESS; - } - } - VAddr target_address = address; if (base_address == 0 && target_address == 0) { @@ -142,10 +131,10 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi auto result = target_process->vm_manager.MapMemoryBlock( target_address, backing_block, backing_block_offset, size, MemoryState::Shared); if (result.Failed()) { - LOG_ERROR( + NGLOG_ERROR( Kernel, - "cannot map id=%u, target_address=0x%llx name=%s, error mapping to virtual memory", - GetObjectId(), target_address, name.c_str()); + "cannot map id={}, target_address=0x{:X} name={}, error mapping to virtual memory", + GetObjectId(), target_address, name); return result.Code(); } @@ -163,7 +152,7 @@ VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) { u32 masked_permissions = static_cast<u32>(permission) & static_cast<u32>(MemoryPermission::ReadWriteExecute); return static_cast<VMAPermission>(masked_permissions); -}; +} u8* SharedMemory::GetPointer(u32 offset) { return backing_block->data() + backing_block_offset + offset; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index b2e8b5ce3..097416e9e 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -4,14 +4,15 @@ #include <algorithm> #include <cinttypes> +#include <iterator> #include "common/logging/log.h" #include "common/microprofile.h" #include "common/string_util.h" +#include "core/core.h" #include "core/core_timing.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" -#include "core/hle/kernel/condition_variable.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/mutex.h" @@ -30,30 +31,30 @@ namespace Kernel { /// Set the process heap to a given Size. It can both extend and shrink the heap. static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { - LOG_TRACE(Kernel_SVC, "called, heap_size=0x%llx", heap_size); - auto& process = *g_current_process; + NGLOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size); + auto& process = *Core::CurrentProcess(); CASCADE_RESULT(*heap_addr, process.HeapAllocate(Memory::HEAP_VADDR, heap_size, VMAPermission::ReadWrite)); return RESULT_SUCCESS; } static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) { - LOG_WARNING(Kernel_SVC, "(STUBBED) called, addr=0x%llx", addr); + NGLOG_WARNING(Kernel_SVC, "(STUBBED) called, addr=0x{:X}", addr); return RESULT_SUCCESS; } /// Maps a memory range into a different range. static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { - LOG_TRACE(Kernel_SVC, "called, dst_addr=0x%llx, src_addr=0x%llx, size=0x%llx", dst_addr, - src_addr, size); - return g_current_process->MirrorMemory(dst_addr, src_addr, size); + NGLOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, + src_addr, size); + return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size); } /// Unmaps a region that was previously mapped with svcMapMemory static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { - LOG_TRACE(Kernel_SVC, "called, dst_addr=0x%llx, src_addr=0x%llx, size=0x%llx", dst_addr, - src_addr, size); - return g_current_process->UnmapMemory(dst_addr, src_addr, size); + NGLOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, + src_addr, size); + return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size); } /// Connect to an OS service given the port name, returns the handle to the port to out @@ -67,11 +68,11 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address if (port_name.size() > PortNameMaxLength) return ERR_PORT_NAME_TOO_LONG; - LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name.c_str()); + NGLOG_TRACE(Kernel_SVC, "called port_name={}", port_name); auto it = Service::g_kernel_named_ports.find(port_name); if (it == Service::g_kernel_named_ports.end()) { - LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: %s", port_name.c_str()); + NGLOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name); return ERR_NOT_FOUND; } @@ -89,11 +90,11 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address static ResultCode SendSyncRequest(Handle handle) { SharedPtr<ClientSession> session = g_handle_table.Get<ClientSession>(handle); if (!session) { - LOG_ERROR(Kernel_SVC, "called with invalid handle=0x%08X", handle); + NGLOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle); return ERR_INVALID_HANDLE; } - LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str()); + NGLOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); Core::System::GetInstance().PrepareReschedule(); @@ -104,7 +105,7 @@ static ResultCode SendSyncRequest(Handle handle) { /// Get the ID for the specified thread. static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) { - LOG_TRACE(Kernel_SVC, "called thread=0x%08X", thread_handle); + NGLOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); if (!thread) { @@ -117,7 +118,7 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) { /// Get the ID of the specified process static ResultCode GetProcessId(u32* process_id, Handle process_handle) { - LOG_TRACE(Kernel_SVC, "called process=0x%08X", process_handle); + NGLOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle); const SharedPtr<Process> process = g_handle_table.Get<Process>(process_handle); if (!process) { @@ -144,41 +145,11 @@ static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thr return true; }; -/// Wait for a kernel object to synchronize, timeout after the specified nanoseconds -static ResultCode WaitSynchronization1( - SharedPtr<WaitObject> object, Thread* thread, s64 nano_seconds = -1, - std::function<Thread::WakeupCallback> wakeup_callback = DefaultThreadWakeupCallback) { - - if (!object) { - return ERR_INVALID_HANDLE; - } - - if (object->ShouldWait(thread)) { - if (nano_seconds == 0) { - return RESULT_TIMEOUT; - } - - thread->wait_objects = {object}; - object->AddWaitingThread(thread); - thread->status = THREADSTATUS_WAIT_SYNCH_ANY; - - // Create an event to wake the thread up after the specified nanosecond delay has passed - thread->WakeAfterDelay(nano_seconds); - thread->wakeup_callback = wakeup_callback; - - Core::System::GetInstance().PrepareReschedule(); - } else { - object->Acquire(thread); - } - - return RESULT_SUCCESS; -} - /// Wait for the given handles to synchronize, timeout after the specified nanoseconds static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 handle_count, s64 nano_seconds) { - LOG_TRACE(Kernel_SVC, "called handles_address=0x%llx, handle_count=%d, nano_seconds=%d", - handles_address, handle_count, nano_seconds); + NGLOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}", + handles_address, handle_count, nano_seconds); if (!Memory::IsValidVirtualAddress(handles_address)) return ERR_INVALID_POINTER; @@ -231,14 +202,14 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 thread->WakeAfterDelay(nano_seconds); thread->wakeup_callback = DefaultThreadWakeupCallback; - Core::System::GetInstance().PrepareReschedule(); + Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule(); return RESULT_TIMEOUT; } /// Resumes a thread waiting on WaitSynchronization static ResultCode CancelSynchronization(Handle thread_handle) { - LOG_TRACE(Kernel_SVC, "called thread=0x%08X", thread_handle); + NGLOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); if (!thread) { @@ -255,74 +226,56 @@ static ResultCode CancelSynchronization(Handle thread_handle) { /// Attempts to locks a mutex, creating it if it does not already exist static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, Handle requesting_thread_handle) { - LOG_TRACE(Kernel_SVC, - "called holding_thread_handle=0x%08X, mutex_addr=0x%llx, " - "requesting_current_thread_handle=0x%08X", - holding_thread_handle, mutex_addr, requesting_thread_handle); - - SharedPtr<Thread> holding_thread = g_handle_table.Get<Thread>(holding_thread_handle); - SharedPtr<Thread> requesting_thread = g_handle_table.Get<Thread>(requesting_thread_handle); - - ASSERT(requesting_thread); - ASSERT(requesting_thread == GetCurrentThread()); - - SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr); - if (!mutex) { - // Create a new mutex for the specified address if one does not already exist - mutex = Mutex::Create(holding_thread, mutex_addr); - mutex->name = Common::StringFromFormat("mutex-%llx", mutex_addr); - } - - ASSERT(holding_thread == mutex->GetHoldingThread()); + NGLOG_TRACE(Kernel_SVC, + "called holding_thread_handle=0x{:08X}, mutex_addr=0x{:X}, " + "requesting_current_thread_handle=0x{:08X}", + holding_thread_handle, mutex_addr, requesting_thread_handle); - return WaitSynchronization1(mutex, requesting_thread.get()); + return Mutex::TryAcquire(mutex_addr, holding_thread_handle, requesting_thread_handle); } /// Unlock a mutex static ResultCode ArbitrateUnlock(VAddr mutex_addr) { - LOG_TRACE(Kernel_SVC, "called mutex_addr=0x%llx", mutex_addr); - - SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr); - ASSERT(mutex); + NGLOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); - return mutex->Release(GetCurrentThread()); + return Mutex::Release(mutex_addr); } /// Break program execution static void Break(u64 unk_0, u64 unk_1, u64 unk_2) { - LOG_CRITICAL(Debug_Emulated, "Emulated program broke execution!"); + NGLOG_CRITICAL(Debug_Emulated, "Emulated program broke execution!"); ASSERT(false); } /// Used to output a message on a debug hardware unit - does nothing on a retail unit static void OutputDebugString(VAddr address, s32 len) { - std::vector<char> string(len); - Memory::ReadBlock(address, string.data(), len); - LOG_DEBUG(Debug_Emulated, "%.*s", len, string.data()); + std::string str(len, '\0'); + Memory::ReadBlock(address, str.data(), str.size()); + NGLOG_DEBUG(Debug_Emulated, "{}", str); } /// Gets system/memory information for the current process static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) { - LOG_TRACE(Kernel_SVC, "called info_id=0x%X, info_sub_id=0x%X, handle=0x%08X", info_id, - info_sub_id, handle); + NGLOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, + info_sub_id, handle); - auto& vm_manager = g_current_process->vm_manager; + auto& vm_manager = Core::CurrentProcess()->vm_manager; switch (static_cast<GetInfoType>(info_id)) { case GetInfoType::AllowedCpuIdBitmask: - *result = g_current_process->allowed_processor_mask; + *result = Core::CurrentProcess()->allowed_processor_mask; break; case GetInfoType::AllowedThreadPrioBitmask: - *result = g_current_process->allowed_thread_priority_mask; + *result = Core::CurrentProcess()->allowed_thread_priority_mask; break; case GetInfoType::MapRegionBaseAddr: - *result = vm_manager.GetMapRegionBaseAddr(); + *result = Memory::MAP_REGION_VADDR; break; case GetInfoType::MapRegionSize: - *result = vm_manager.GetAddressSpaceSize(); + *result = Memory::MAP_REGION_SIZE; break; case GetInfoType::HeapRegionBaseAddr: - *result = vm_manager.GetNewMapRegionBaseAddr() + vm_manager.GetNewMapRegionSize(); + *result = Memory::HEAP_VADDR; break; case GetInfoType::HeapRegionSize: *result = Memory::HEAP_SIZE; @@ -346,21 +299,21 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) *result = vm_manager.GetAddressSpaceSize(); break; case GetInfoType::NewMapRegionBaseAddr: - *result = vm_manager.GetNewMapRegionBaseAddr(); + *result = Memory::NEW_MAP_REGION_VADDR; break; case GetInfoType::NewMapRegionSize: - *result = vm_manager.GetNewMapRegionSize(); + *result = Memory::NEW_MAP_REGION_SIZE; break; case GetInfoType::IsVirtualAddressMemoryEnabled: - *result = g_current_process->is_virtual_address_memory_enabled; + *result = Core::CurrentProcess()->is_virtual_address_memory_enabled; break; case GetInfoType::TitleId: - LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query titleid, returned 0"); + NGLOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query titleid, returned 0"); *result = 0; break; case GetInfoType::PrivilegedProcessId: - LOG_WARNING(Kernel_SVC, - "(STUBBED) Attempted to query priviledged process id bounds, returned 0"); + NGLOG_WARNING(Kernel_SVC, + "(STUBBED) Attempted to query privileged process id bounds, returned 0"); *result = 0; break; case GetInfoType::UserExceptionContextAddr: @@ -375,6 +328,19 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) return RESULT_SUCCESS; } +/// Sets the thread activity +static ResultCode SetThreadActivity(Handle handle, u32 unknown) { + NGLOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, unknown=0x{:08X}", handle, + unknown); + return RESULT_SUCCESS; +} + +/// Gets the thread context +static ResultCode GetThreadContext(Handle handle, VAddr addr) { + NGLOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, addr=0x{:X}", handle, addr); + return RESULT_SUCCESS; +} + /// Gets the priority for the specified thread static ResultCode GetThreadPriority(u32* priority, Handle handle) { const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(handle); @@ -397,33 +363,29 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { // Note: The kernel uses the current process's resource limit instead of // the one from the thread owner's resource limit. - SharedPtr<ResourceLimit>& resource_limit = g_current_process->resource_limit; - if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) { + SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit; + if (resource_limit->GetMaxResourceValue(ResourceType::Priority) > priority) { return ERR_NOT_AUTHORIZED; } thread->SetPriority(priority); - thread->UpdatePriority(); - // Update the mutexes that this thread is waiting for - for (auto& mutex : thread->pending_mutexes) - mutex->UpdatePriority(); - - Core::System::GetInstance().PrepareReschedule(); + Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule(); return RESULT_SUCCESS; } /// Get which CPU core is executing the current thread static u32 GetCurrentProcessorNumber() { - LOG_WARNING(Kernel_SVC, "(STUBBED) called, defaulting to processor 0"); - return 0; + NGLOG_TRACE(Kernel_SVC, "called"); + return GetCurrentThread()->processor_id; } static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size, u32 permissions) { - LOG_TRACE(Kernel_SVC, - "called, shared_memory_handle=0x%08X, addr=0x%llx, size=0x%llx, permissions=0x%08X", - shared_memory_handle, addr, size, permissions); + NGLOG_TRACE( + Kernel_SVC, + "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", + shared_memory_handle, addr, size, permissions); SharedPtr<SharedMemory> shared_memory = g_handle_table.Get<SharedMemory>(shared_memory_handle); if (!shared_memory) { @@ -440,23 +402,22 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s case MemoryPermission::WriteExecute: case MemoryPermission::ReadWriteExecute: case MemoryPermission::DontCare: - return shared_memory->Map(g_current_process.get(), addr, permissions_type, + return shared_memory->Map(Core::CurrentProcess().get(), addr, permissions_type, MemoryPermission::DontCare); default: - LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions); + NGLOG_ERROR(Kernel_SVC, "unknown permissions=0x{:08X}", permissions); } return RESULT_SUCCESS; } static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { - LOG_WARNING(Kernel_SVC, - "called, shared_memory_handle=0x%08X, addr=0x%" PRIx64 ", size=0x%" PRIx64 "", - shared_memory_handle, addr, size); + NGLOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", + shared_memory_handle, addr, size); SharedPtr<SharedMemory> shared_memory = g_handle_table.Get<SharedMemory>(shared_memory_handle); - return shared_memory->Unmap(g_current_process.get(), addr); + return shared_memory->Unmap(Core::CurrentProcess().get(), addr); } /// Query process memory @@ -468,11 +429,11 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_i } auto vma = process->vm_manager.FindVMA(addr); memory_info->attributes = 0; - if (vma == g_current_process->vm_manager.vma_map.end()) { + if (vma == Core::CurrentProcess()->vm_manager.vma_map.end()) { memory_info->base_address = 0; memory_info->permission = static_cast<u32>(VMAPermission::None); memory_info->size = 0; - memory_info->type = static_cast<u32>(MemoryState::Free); + memory_info->type = static_cast<u32>(MemoryState::Unmapped); } else { memory_info->base_address = vma->second.base; memory_info->permission = static_cast<u32>(vma->second.permissions); @@ -480,40 +441,47 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_i memory_info->type = static_cast<u32>(vma->second.meminfo_state); } - LOG_TRACE(Kernel_SVC, "called process=0x%08X addr=%llx", process_handle, addr); + NGLOG_TRACE(Kernel_SVC, "called process=0x{:08X} addr={:X}", process_handle, addr); return RESULT_SUCCESS; } /// Query memory static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAddr addr) { - LOG_TRACE(Kernel_SVC, "called, addr=%llx", addr); + NGLOG_TRACE(Kernel_SVC, "called, addr={:X}", addr); return QueryProcessMemory(memory_info, page_info, CurrentProcess, addr); } /// Exits the current process static void ExitProcess() { - LOG_INFO(Kernel_SVC, "Process %u exiting", g_current_process->process_id); + NGLOG_INFO(Kernel_SVC, "Process {} exiting", Core::CurrentProcess()->process_id); - ASSERT_MSG(g_current_process->status == ProcessStatus::Running, "Process has already exited"); + ASSERT_MSG(Core::CurrentProcess()->status == ProcessStatus::Running, + "Process has already exited"); - g_current_process->status = ProcessStatus::Exited; + Core::CurrentProcess()->status = ProcessStatus::Exited; - // Stop all the process threads that are currently waiting for objects. - auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList(); - for (auto& thread : thread_list) { - if (thread->owner_process != g_current_process) - continue; + auto stop_threads = [](const std::vector<SharedPtr<Thread>>& thread_list) { + for (auto& thread : thread_list) { + if (thread->owner_process != Core::CurrentProcess()) + continue; - if (thread == GetCurrentThread()) - continue; + if (thread == GetCurrentThread()) + continue; - // TODO(Subv): When are the other running/ready threads terminated? - ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY || - thread->status == THREADSTATUS_WAIT_SYNCH_ALL, - "Exiting processes with non-waiting threads is currently unimplemented"); + // TODO(Subv): When are the other running/ready threads terminated? + ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY || + thread->status == THREADSTATUS_WAIT_SYNCH_ALL, + "Exiting processes with non-waiting threads is currently unimplemented"); - thread->Stop(); - } + thread->Stop(); + } + }; + + auto& system = Core::System::GetInstance(); + stop_threads(system.Scheduler(0)->GetThreadList()); + stop_threads(system.Scheduler(1)->GetThreadList()); + stop_threads(system.Scheduler(2)->GetThreadList()); + stop_threads(system.Scheduler(3)->GetThreadList()); // Kill the current thread GetCurrentThread()->Stop(); @@ -524,72 +492,71 @@ static void ExitProcess() { /// Creates a new thread static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, u32 priority, s32 processor_id) { - std::string name = Common::StringFromFormat("unknown-%llx", entry_point); + std::string name = fmt::format("unknown-{:X}", entry_point); if (priority > THREADPRIO_LOWEST) { return ERR_OUT_OF_RANGE; } - SharedPtr<ResourceLimit>& resource_limit = g_current_process->resource_limit; - if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) { + SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit; + if (resource_limit->GetMaxResourceValue(ResourceType::Priority) > priority) { return ERR_NOT_AUTHORIZED; } if (processor_id == THREADPROCESSORID_DEFAULT) { // Set the target CPU to the one specified in the process' exheader. - processor_id = g_current_process->ideal_processor; + processor_id = Core::CurrentProcess()->ideal_processor; ASSERT(processor_id != THREADPROCESSORID_DEFAULT); } switch (processor_id) { case THREADPROCESSORID_0: - break; case THREADPROCESSORID_1: case THREADPROCESSORID_2: case THREADPROCESSORID_3: - // TODO(bunnei): Implement support for other processor IDs - LOG_ERROR(Kernel_SVC, - "Newly created thread must run in another thread (%u), unimplemented.", - processor_id); break; default: - ASSERT_MSG(false, "Unsupported thread processor ID: %d", processor_id); + ASSERT_MSG(false, "Unsupported thread processor ID: {}", processor_id); break; } CASCADE_RESULT(SharedPtr<Thread> thread, Thread::Create(name, entry_point, priority, arg, processor_id, stack_top, - g_current_process)); + Core::CurrentProcess())); CASCADE_RESULT(thread->guest_handle, g_handle_table.Create(thread)); *out_handle = thread->guest_handle; Core::System::GetInstance().PrepareReschedule(); + Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule(); - LOG_TRACE(Kernel_SVC, - "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " - "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", - entry_point, name.c_str(), arg, stack_top, priority, processor_id, *out_handle); + NGLOG_TRACE(Kernel_SVC, + "called entrypoint=0x{:08X} ({}), arg=0x{:08X}, stacktop=0x{:08X}, " + "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}", + entry_point, name, arg, stack_top, priority, processor_id, *out_handle); return RESULT_SUCCESS; } /// Starts the thread for the provided handle static ResultCode StartThread(Handle thread_handle) { - LOG_TRACE(Kernel_SVC, "called thread=0x%08X", thread_handle); + NGLOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); if (!thread) { return ERR_INVALID_HANDLE; } + ASSERT(thread->status == THREADSTATUS_DORMANT); + thread->ResumeFromWait(); + Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule(); return RESULT_SUCCESS; } /// Called when a thread exits static void ExitThread() { - LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::CPU().GetPC()); + NGLOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", Core::CurrentArmInterface().GetPC()); ExitCurrentThread(); Core::System::GetInstance().PrepareReschedule(); @@ -597,11 +564,11 @@ static void ExitThread() { /// Sleep the current thread static void SleepThread(s64 nanoseconds) { - LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); + NGLOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); // Don't attempt to yield execution if there are no available threads to run, // this way we avoid a useless reschedule to the idle thread. - if (nanoseconds == 0 && !Core::System::GetInstance().Scheduler().HaveReadyThreads()) + if (nanoseconds == 0 && !Core::System::GetInstance().CurrentScheduler().HaveReadyThreads()) return; // Sleep current thread and check for next thread to schedule @@ -616,111 +583,107 @@ static void SleepThread(s64 nanoseconds) { /// Signal process wide key atomic static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_variable_addr, Handle thread_handle, s64 nano_seconds) { - LOG_TRACE( + NGLOG_TRACE( Kernel_SVC, - "called mutex_addr=%llx, condition_variable_addr=%llx, thread_handle=0x%08X, timeout=%d", + "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", mutex_addr, condition_variable_addr, thread_handle, nano_seconds); SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); ASSERT(thread); - SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr); - if (!mutex) { - // Create a new mutex for the specified address if one does not already exist - mutex = Mutex::Create(thread, mutex_addr); - mutex->name = Common::StringFromFormat("mutex-%llx", mutex_addr); - } + CASCADE_CODE(Mutex::Release(mutex_addr)); - SharedPtr<ConditionVariable> condition_variable = - g_object_address_table.Get<ConditionVariable>(condition_variable_addr); - if (!condition_variable) { - // Create a new condition_variable for the specified address if one does not already exist - condition_variable = ConditionVariable::Create(condition_variable_addr).Unwrap(); - condition_variable->name = - Common::StringFromFormat("condition-variable-%llx", condition_variable_addr); - } + SharedPtr<Thread> current_thread = GetCurrentThread(); + current_thread->condvar_wait_address = condition_variable_addr; + current_thread->mutex_wait_address = mutex_addr; + current_thread->wait_handle = thread_handle; + current_thread->status = THREADSTATUS_WAIT_MUTEX; + current_thread->wakeup_callback = nullptr; - if (condition_variable->mutex_addr) { - // Previously created the ConditionVariable using WaitProcessWideKeyAtomic, verify - // everything is correct - ASSERT(condition_variable->mutex_addr == mutex_addr); - } else { - // Previously created the ConditionVariable using SignalProcessWideKey, set the mutex - // associated with it - condition_variable->mutex_addr = mutex_addr; - } + current_thread->WakeAfterDelay(nano_seconds); - if (mutex->GetOwnerHandle()) { - // Release the mutex if the current thread is holding it - mutex->Release(thread.get()); - } + // Note: Deliberately don't attempt to inherit the lock owner's priority. - auto wakeup_callback = [mutex, nano_seconds](ThreadWakeupReason reason, - SharedPtr<Thread> thread, - SharedPtr<WaitObject> object, size_t index) { - ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); - - if (reason == ThreadWakeupReason::Timeout) { - thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); - return true; - } - - ASSERT(reason == ThreadWakeupReason::Signal); + Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule(); + return RESULT_SUCCESS; +} - // Now try to acquire the mutex and don't resume if it's not available. - if (!mutex->ShouldWait(thread.get())) { - mutex->Acquire(thread.get()); - thread->SetWaitSynchronizationResult(RESULT_SUCCESS); - return true; - } +/// Signal process wide key +static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target) { + NGLOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", + condition_variable_addr, target); + + auto RetrieveWaitingThreads = + [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr condvar_addr) { + const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); + auto& thread_list = scheduler->GetThreadList(); + + for (auto& thread : thread_list) { + if (thread->condvar_wait_address == condvar_addr) + waiting_threads.push_back(thread); + } + }; + + // Retrieve a list of all threads that are waiting for this condition variable. + std::vector<SharedPtr<Thread>> waiting_threads; + RetrieveWaitingThreads(0, waiting_threads, condition_variable_addr); + RetrieveWaitingThreads(1, waiting_threads, condition_variable_addr); + RetrieveWaitingThreads(2, waiting_threads, condition_variable_addr); + RetrieveWaitingThreads(3, waiting_threads, condition_variable_addr); + // Sort them by priority, such that the highest priority ones come first. + std::sort(waiting_threads.begin(), waiting_threads.end(), + [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) { + return lhs->current_priority < rhs->current_priority; + }); + + // Only process up to 'target' threads, unless 'target' is -1, in which case process + // them all. + size_t last = waiting_threads.size(); + if (target != -1) + last = target; + + // If there are no threads waiting on this condition variable, just exit + if (last > waiting_threads.size()) + return RESULT_SUCCESS; - if (nano_seconds == 0) { - thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); - return true; - } + for (size_t index = 0; index < last; ++index) { + auto& thread = waiting_threads[index]; - thread->wait_objects = {mutex}; - mutex->AddWaitingThread(thread); - thread->status = THREADSTATUS_WAIT_SYNCH_ANY; + ASSERT(thread->condvar_wait_address == condition_variable_addr); - // Create an event to wake the thread up after the - // specified nanosecond delay has passed - thread->WakeAfterDelay(nano_seconds); - thread->wakeup_callback = DefaultThreadWakeupCallback; + // If the mutex is not yet acquired, acquire it. + u32 mutex_val = Memory::Read32(thread->mutex_wait_address); - Core::System::GetInstance().PrepareReschedule(); + if (mutex_val == 0) { + // We were able to acquire the mutex, resume this thread. + Memory::Write32(thread->mutex_wait_address, thread->wait_handle); + ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX); + thread->ResumeFromWait(); - return false; - }; - CASCADE_CODE( - WaitSynchronization1(condition_variable, thread.get(), nano_seconds, wakeup_callback)); + auto lock_owner = thread->lock_owner; + if (lock_owner) + lock_owner->RemoveMutexWaiter(thread); - return RESULT_SUCCESS; -} + thread->lock_owner = nullptr; + thread->mutex_wait_address = 0; + thread->condvar_wait_address = 0; + thread->wait_handle = 0; + } else { + // Couldn't acquire the mutex, block the thread. + Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); + auto owner = g_handle_table.Get<Thread>(owner_handle); + ASSERT(owner); + ASSERT(thread->status != THREADSTATUS_RUNNING); + thread->status = THREADSTATUS_WAIT_MUTEX; + thread->wakeup_callback = nullptr; -/// Signal process wide key -static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target) { - LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x%llx, target=0x%08x", - condition_variable_addr, target); - - // Wakeup all or one thread - Any other value is unimplemented - ASSERT(target == -1 || target == 1); - - SharedPtr<ConditionVariable> condition_variable = - g_object_address_table.Get<ConditionVariable>(condition_variable_addr); - if (!condition_variable) { - // Create a new condition_variable for the specified address if one does not already exist - condition_variable = ConditionVariable::Create(condition_variable_addr).Unwrap(); - condition_variable->name = - Common::StringFromFormat("condition-variable-%llx", condition_variable_addr); - } + // Signal that the mutex now has a waiting thread. + Memory::Write32(thread->mutex_wait_address, mutex_val | Mutex::MutexHasWaitersFlag); - CASCADE_CODE(condition_variable->Release(target)); + owner->AddMutexWaiter(thread); - if (condition_variable->mutex_addr) { - // If a mutex was created for this condition_variable, wait the current thread on it - SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(condition_variable->mutex_addr); - return WaitSynchronization1(mutex, GetCurrentThread()); + Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule(); + } } return RESULT_SUCCESS; @@ -738,13 +701,13 @@ static u64 GetSystemTick() { /// Close a handle static ResultCode CloseHandle(Handle handle) { - LOG_TRACE(Kernel_SVC, "Closing handle 0x%08X", handle); + NGLOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); return g_handle_table.Close(handle); } /// Reset an event static ResultCode ResetSignal(Handle handle) { - LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x%08X", handle); + NGLOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); auto event = g_handle_table.Get<Event>(handle); ASSERT(event != nullptr); event->Clear(); @@ -753,21 +716,69 @@ static ResultCode ResetSignal(Handle handle) { /// Creates a TransferMemory object static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) { - LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%llx, size=0x%llx, perms=%08X", addr, size, - permissions); + NGLOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, + size, permissions); *handle = 0; return RESULT_SUCCESS; } -static ResultCode SetThreadCoreMask(u64, u64, u64) { - LOG_WARNING(Kernel_SVC, "(STUBBED) called"); +static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) { + NGLOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); + + const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); + if (!thread) { + return ERR_INVALID_HANDLE; + } + + *core = thread->ideal_core; + *mask = thread->affinity_mask; + + return RESULT_SUCCESS; +} + +static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { + NGLOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle, + mask, core); + + const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); + if (!thread) { + return ERR_INVALID_HANDLE; + } + + if (core == THREADPROCESSORID_DEFAULT) { + ASSERT(thread->owner_process->ideal_processor != THREADPROCESSORID_DEFAULT); + // Set the target CPU to the one specified in the process' exheader. + core = thread->owner_process->ideal_processor; + mask = 1 << core; + } + + if (mask == 0) { + return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination); + } + + /// This value is used to only change the affinity mask without changing the current ideal core. + static constexpr u32 OnlyChangeMask = static_cast<u32>(-3); + + if (core == OnlyChangeMask) { + core = thread->ideal_core; + } else if (core >= Core::NUM_CPU_CORES && core != -1) { + return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); + } + + // Error out if the input core isn't enabled in the input mask. + if (core < Core::NUM_CPU_CORES && (mask & (1 << core)) == 0) { + return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination); + } + + thread->ChangeCore(core, mask); + return RESULT_SUCCESS; } static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permissions, u32 remote_permissions) { - LOG_TRACE(Kernel_SVC, "called, size=0x%llx, localPerms=0x%08x, remotePerms=0x%08x", size, - local_permissions, remote_permissions); + NGLOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size, + local_permissions, remote_permissions); auto sharedMemHandle = SharedMemory::Create(g_handle_table.Get<Process>(KernelHandle::CurrentProcess), size, static_cast<MemoryPermission>(local_permissions), @@ -778,7 +789,7 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss } static ResultCode ClearEvent(Handle handle) { - LOG_TRACE(Kernel_SVC, "called, event=0xX", handle); + NGLOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); SharedPtr<Event> evt = g_handle_table.Get<Event>(handle); if (evt == nullptr) @@ -812,7 +823,7 @@ static const FunctionDef SVC_Table[] = { {0x0B, SvcWrap<SleepThread>, "SleepThread"}, {0x0C, SvcWrap<GetThreadPriority>, "GetThreadPriority"}, {0x0D, SvcWrap<SetThreadPriority>, "SetThreadPriority"}, - {0x0E, nullptr, "GetThreadCoreMask"}, + {0x0E, SvcWrap<GetThreadCoreMask>, "GetThreadCoreMask"}, {0x0F, SvcWrap<SetThreadCoreMask>, "SetThreadCoreMask"}, {0x10, SvcWrap<GetCurrentProcessorNumber>, "GetCurrentProcessorNumber"}, {0x11, nullptr, "SignalEvent"}, @@ -844,14 +855,14 @@ static const FunctionDef SVC_Table[] = { {0x2B, nullptr, "FlushDataCache"}, {0x2C, nullptr, "MapPhysicalMemory"}, {0x2D, nullptr, "UnmapPhysicalMemory"}, - {0x2E, nullptr, "Unknown"}, + {0x2E, nullptr, "GetNextThreadInfo"}, {0x2F, nullptr, "GetLastThreadInfo"}, {0x30, nullptr, "GetResourceLimitLimitValue"}, {0x31, nullptr, "GetResourceLimitCurrentValue"}, - {0x32, nullptr, "SetThreadActivity"}, - {0x33, nullptr, "GetThreadContext"}, - {0x34, nullptr, "Unknown"}, - {0x35, nullptr, "Unknown"}, + {0x32, SvcWrap<SetThreadActivity>, "SetThreadActivity"}, + {0x33, SvcWrap<GetThreadContext>, "GetThreadContext"}, + {0x34, nullptr, "WaitForAddress"}, + {0x35, nullptr, "SignalToAddress"}, {0x36, nullptr, "Unknown"}, {0x37, nullptr, "Unknown"}, {0x38, nullptr, "Unknown"}, @@ -859,7 +870,7 @@ static const FunctionDef SVC_Table[] = { {0x3A, nullptr, "Unknown"}, {0x3B, nullptr, "Unknown"}, {0x3C, nullptr, "DumpInfo"}, - {0x3D, nullptr, "Unknown"}, + {0x3D, nullptr, "DumpInfoNew"}, {0x3E, nullptr, "Unknown"}, {0x3F, nullptr, "Unknown"}, {0x40, nullptr, "CreateSession"}, @@ -870,9 +881,9 @@ static const FunctionDef SVC_Table[] = { {0x45, nullptr, "CreateEvent"}, {0x46, nullptr, "Unknown"}, {0x47, nullptr, "Unknown"}, - {0x48, nullptr, "Unknown"}, - {0x49, nullptr, "Unknown"}, - {0x4A, nullptr, "Unknown"}, + {0x48, nullptr, "AllocateUnsafeMemory"}, + {0x49, nullptr, "FreeUnsafeMemory"}, + {0x4A, nullptr, "SetUnsafeAllocationLimit"}, {0x4B, nullptr, "CreateJitMemory"}, {0x4C, nullptr, "MapJitMemory"}, {0x4D, nullptr, "SleepSystem"}, @@ -909,7 +920,7 @@ static const FunctionDef SVC_Table[] = { {0x6C, nullptr, "SetHardwareBreakPoint"}, {0x6D, nullptr, "GetDebugThreadParam"}, {0x6E, nullptr, "Unknown"}, - {0x6F, nullptr, "Unknown"}, + {0x6F, nullptr, "GetMemoryInfo"}, {0x70, nullptr, "CreatePort"}, {0x71, nullptr, "ManageNamedPort"}, {0x72, nullptr, "ConnectToPort"}, @@ -929,8 +940,8 @@ static const FunctionDef SVC_Table[] = { }; static const FunctionDef* GetSVCInfo(u32 func_num) { - if (func_num >= ARRAY_SIZE(SVC_Table)) { - LOG_ERROR(Kernel_SVC, "unknown svc=0x%02X", func_num); + if (func_num >= std::size(SVC_Table)) { + NGLOG_ERROR(Kernel_SVC, "Unknown svc=0x{:02X}", func_num); return nullptr; } return &SVC_Table[func_num]; @@ -949,10 +960,10 @@ void CallSVC(u32 immediate) { if (info->func) { info->func(); } else { - LOG_CRITICAL(Kernel_SVC, "unimplemented SVC function %s(..)", info->name); + NGLOG_CRITICAL(Kernel_SVC, "Unimplemented SVC function {}(..)", info->name); } } else { - LOG_CRITICAL(Kernel_SVC, "unknown SVC function 0x%x", immediate); + NGLOG_CRITICAL(Kernel_SVC, "Unknown SVC function 0x{:X}", immediate); } } diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h index bc471d01e..70148c4fe 100644 --- a/src/core/hle/kernel/svc.h +++ b/src/core/hle/kernel/svc.h @@ -47,9 +47,12 @@ enum class GetInfoType : u64 { NewMapRegionSize = 15, // 3.0.0+ IsVirtualAddressMemoryEnabled = 16, + PersonalMmHeapUsage = 17, TitleId = 18, // 4.0.0+ PrivilegedProcessId = 19, + // 5.0.0+ + UserExceptionContextAddr = 20, }; void CallSVC(u32 immediate); diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index b224f5e67..40aa88cc1 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -13,14 +13,14 @@ namespace Kernel { -#define PARAM(n) Core::CPU().GetReg(n) +#define PARAM(n) Core::CurrentArmInterface().GetReg(n) /** * HLE a function return from the current ARM userland process * @param res Result to return */ static inline void FuncReturn(u64 res) { - Core::CPU().SetReg(0, res); + Core::CurrentArmInterface().SetReg(0, res); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -45,7 +45,7 @@ template <ResultCode func(u32*, u32)> void SvcWrap() { u32 param_1 = 0; u32 retval = func(¶m_1, (u32)PARAM(1)).raw; - Core::CPU().SetReg(1, param_1); + Core::CurrentArmInterface().SetReg(1, param_1); FuncReturn(retval); } @@ -53,7 +53,7 @@ template <ResultCode func(u32*, u64)> void SvcWrap() { u32 param_1 = 0; u32 retval = func(¶m_1, PARAM(1)).raw; - Core::CPU().SetReg(1, param_1); + Core::CurrentArmInterface().SetReg(1, param_1); FuncReturn(retval); } @@ -66,10 +66,30 @@ template <ResultCode func(u64*, u64)> void SvcWrap() { u64 param_1 = 0; u32 retval = func(¶m_1, PARAM(1)).raw; - Core::CPU().SetReg(1, param_1); + Core::CurrentArmInterface().SetReg(1, param_1); FuncReturn(retval); } +template <ResultCode func(u32, u64)> +void SvcWrap() { + FuncReturn(func((u32)(PARAM(0) & 0xFFFFFFFF), PARAM(1)).raw); +} + +template <ResultCode func(u32, u32, u64)> +void SvcWrap() { + FuncReturn(func((u32)(PARAM(0) & 0xFFFFFFFF), (u32)(PARAM(1) & 0xFFFFFFFF), PARAM(2)).raw); +} + +template <ResultCode func(u32, u32*, u64*)> +void SvcWrap() { + u32 param_1 = 0; + u64 param_2 = 0; + ResultCode retval = func((u32)(PARAM(2) & 0xFFFFFFFF), ¶m_1, ¶m_2); + Core::CurrentArmInterface().SetReg(1, param_1); + Core::CurrentArmInterface().SetReg(2, param_2); + FuncReturn(retval.raw); +} + template <ResultCode func(u64, u64, u32, u32)> void SvcWrap() { FuncReturn( @@ -100,7 +120,7 @@ template <ResultCode func(u32*, u64, u64, s64)> void SvcWrap() { u32 param_1 = 0; ResultCode retval = func(¶m_1, PARAM(1), (u32)(PARAM(2) & 0xFFFFFFFF), (s64)PARAM(3)); - Core::CPU().SetReg(1, param_1); + Core::CurrentArmInterface().SetReg(1, param_1); FuncReturn(retval.raw); } @@ -113,7 +133,7 @@ template <ResultCode func(u64*, u64, u64, u64)> void SvcWrap() { u64 param_1 = 0; u32 retval = func(¶m_1, PARAM(1), PARAM(2), PARAM(3)).raw; - Core::CPU().SetReg(1, param_1); + Core::CurrentArmInterface().SetReg(1, param_1); FuncReturn(retval); } @@ -123,7 +143,7 @@ void SvcWrap() { u32 retval = func(¶m_1, PARAM(1), PARAM(2), PARAM(3), (u32)PARAM(4), (s32)(PARAM(5) & 0xFFFFFFFF)) .raw; - Core::CPU().SetReg(1, param_1); + Core::CurrentArmInterface().SetReg(1, param_1); FuncReturn(retval); } @@ -146,7 +166,7 @@ template <ResultCode func(u32*, u64, u64, u32)> void SvcWrap() { u32 param_1 = 0; u32 retval = func(¶m_1, PARAM(1), PARAM(2), (u32)(PARAM(3) & 0xFFFFFFFF)).raw; - Core::CPU().SetReg(1, param_1); + Core::CurrentArmInterface().SetReg(1, param_1); FuncReturn(retval); } @@ -155,7 +175,7 @@ void SvcWrap() { u32 param_1 = 0; u32 retval = func(¶m_1, PARAM(1), (u32)(PARAM(2) & 0xFFFFFFFF), (u32)(PARAM(3) & 0xFFFFFFFF)).raw; - Core::CPU().SetReg(1, param_1); + Core::CurrentArmInterface().SetReg(1, param_1); FuncReturn(retval); } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index dd0a8ae48..cffa7ca83 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -55,16 +55,6 @@ inline static u32 const NewThreadId() { Thread::Thread() {} Thread::~Thread() {} -/** - * Check if the specified thread is waiting on the specified address to be arbitrated - * @param thread The thread to test - * @param wait_address The address to test against - * @return True if the thread is waiting, false otherwise - */ -static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) { - return thread->status == THREADSTATUS_WAIT_ARB && wait_address == thread->wait_address; -} - void Thread::Stop() { // Cancel any outstanding wakeup events for this thread CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); @@ -74,7 +64,7 @@ void Thread::Stop() { // Clean up thread from ready queue // This is only needed when the thread is termintated forcefully (SVC TerminateProcess) if (status == THREADSTATUS_READY) { - Core::System::GetInstance().Scheduler().UnscheduleThread(this, current_priority); + scheduler->UnscheduleThread(this, current_priority); } status = THREADSTATUS_DEAD; @@ -87,14 +77,11 @@ void Thread::Stop() { } wait_objects.clear(); - // Release all the mutexes that this thread holds - ReleaseThreadMutexes(this); - // Mark the TLS slot in the thread's page as free. u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; u64 tls_slot = ((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE; - Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot); + Core::CurrentProcess()->tls_slots[tls_page].reset(tls_slot); } void WaitCurrentThread_Sleep() { @@ -102,16 +89,10 @@ void WaitCurrentThread_Sleep() { thread->status = THREADSTATUS_WAIT_SLEEP; } -void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { - Thread* thread = GetCurrentThread(); - thread->wait_address = wait_address; - thread->status = THREADSTATUS_WAIT_ARB; -} - void ExitCurrentThread() { Thread* thread = GetCurrentThread(); thread->Stop(); - Core::System::GetInstance().Scheduler().RemoveThread(thread); + Core::System::GetInstance().CurrentScheduler().RemoveThread(thread); } /** @@ -120,16 +101,18 @@ void ExitCurrentThread() { * @param cycles_late The number of CPU cycles that have passed since the desired wakeup time */ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { - SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>((Handle)thread_handle); + const auto proper_handle = static_cast<Handle>(thread_handle); + SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>(proper_handle); if (thread == nullptr) { - LOG_CRITICAL(Kernel, "Callback fired for invalid thread %08X", (Handle)thread_handle); + NGLOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle); return; } bool resume = true; if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY || - thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) { + thread->status == THREADSTATUS_WAIT_SYNCH_ALL || + thread->status == THREADSTATUS_WAIT_HLE_EVENT) { // Remove the thread from each of its waiting objects' waitlists for (auto& object : thread->wait_objects) @@ -141,6 +124,22 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { resume = thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr, 0); } + if (thread->mutex_wait_address != 0 || thread->condvar_wait_address != 0 || + thread->wait_handle) { + ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX); + thread->mutex_wait_address = 0; + thread->condvar_wait_address = 0; + thread->wait_handle = 0; + + auto lock_owner = thread->lock_owner; + // Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance + // and don't have a lock owner unless SignalProcessWideKey was called first and the thread + // wasn't awakened due to the mutex already being acquired. + if (lock_owner) { + lock_owner->RemoveMutexWaiter(thread); + } + } + if (resume) thread->ResumeFromWait(); } @@ -150,22 +149,36 @@ void Thread::WakeAfterDelay(s64 nanoseconds) { if (nanoseconds == -1) return; - CoreTiming::ScheduleEvent(nsToCycles(nanoseconds), ThreadWakeupEventType, callback_handle); + CoreTiming::ScheduleEvent(CoreTiming::nsToCycles(nanoseconds), ThreadWakeupEventType, + callback_handle); } void Thread::CancelWakeupTimer() { CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); } +static boost::optional<s32> GetNextProcessorId(u64 mask) { + for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) { + if (mask & (1ULL << index)) { + if (!Core::System().GetInstance().Scheduler(index)->GetCurrentThread()) { + // Core is enabled and not running any threads, use this one + return index; + } + } + } + return {}; +} + void Thread::ResumeFromWait() { ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects"); switch (status) { case THREADSTATUS_WAIT_SYNCH_ALL: case THREADSTATUS_WAIT_SYNCH_ANY: - case THREADSTATUS_WAIT_ARB: + case THREADSTATUS_WAIT_HLE_EVENT: case THREADSTATUS_WAIT_SLEEP: case THREADSTATUS_WAIT_IPC: + case THREADSTATUS_WAIT_MUTEX: break; case THREADSTATUS_READY: @@ -178,11 +191,11 @@ void Thread::ResumeFromWait() { return; case THREADSTATUS_RUNNING: - DEBUG_ASSERT_MSG(false, "Thread with object id %u has already resumed.", GetObjectId()); + DEBUG_ASSERT_MSG(false, "Thread with object id {} has already resumed.", GetObjectId()); return; case THREADSTATUS_DEAD: // This should never happen, as threads must complete before being stopped. - DEBUG_ASSERT_MSG(false, "Thread with object id %u cannot be resumed because it's DEAD.", + DEBUG_ASSERT_MSG(false, "Thread with object id {} cannot be resumed because it's DEAD.", GetObjectId()); return; } @@ -190,8 +203,37 @@ void Thread::ResumeFromWait() { wakeup_callback = nullptr; status = THREADSTATUS_READY; - Core::System::GetInstance().Scheduler().ScheduleThread(this, current_priority); - Core::System::GetInstance().PrepareReschedule(); + + boost::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask); + if (!new_processor_id) { + new_processor_id = processor_id; + } + if (ideal_core != -1 && + Core::System().GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) { + new_processor_id = ideal_core; + } + + ASSERT(*new_processor_id < 4); + + // Add thread to new core's scheduler + auto& next_scheduler = Core::System().GetInstance().Scheduler(*new_processor_id); + + if (*new_processor_id != processor_id) { + // Remove thread from previous core's scheduler + scheduler->RemoveThread(this); + next_scheduler->AddThread(this, current_priority); + } + + processor_id = *new_processor_id; + + // If the thread was ready, unschedule from the previous core and schedule on the new core + scheduler->UnscheduleThread(this, current_priority); + next_scheduler->ScheduleThread(this, current_priority); + + // Change thread's scheduler + scheduler = next_scheduler; + + Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); } /** @@ -242,27 +284,25 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, SharedPtr<Process> owner_process) { // Check if priority is in ranged. Lowest priority -> highest priority id. if (priority > THREADPRIO_LOWEST) { - LOG_ERROR(Kernel_SVC, "Invalid thread priority: %u", priority); + NGLOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); return ERR_OUT_OF_RANGE; } if (processor_id > THREADPROCESSORID_MAX) { - LOG_ERROR(Kernel_SVC, "Invalid processor id: %d", processor_id); + NGLOG_ERROR(Kernel_SVC, "Invalid processor id: {}", processor_id); return ERR_OUT_OF_RANGE_KERNEL; } // TODO(yuriks): Other checks, returning 0xD9001BEA if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) { - LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %016" PRIx64, name.c_str(), entry_point); + NGLOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); // TODO (bunnei): Find the correct error code to use here return ResultCode(-1); } SharedPtr<Thread> thread(new Thread); - Core::System::GetInstance().Scheduler().AddThread(thread, priority); - thread->thread_id = NewThreadId(); thread->status = THREADSTATUS_DORMANT; thread->entry_point = entry_point; @@ -270,11 +310,17 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, thread->nominal_priority = thread->current_priority = priority; thread->last_running_ticks = CoreTiming::GetTicks(); thread->processor_id = processor_id; + thread->ideal_core = processor_id; + thread->affinity_mask = 1ULL << processor_id; thread->wait_objects.clear(); - thread->wait_address = 0; + thread->mutex_wait_address = 0; + thread->condvar_wait_address = 0; + thread->wait_handle = 0; thread->name = std::move(name); thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap(); thread->owner_process = owner_process; + thread->scheduler = Core::System().GetInstance().Scheduler(processor_id); + thread->scheduler->AddThread(thread, priority); // Find the next available TLS index, and mark it as used auto& tls_slots = owner_process->tls_slots; @@ -291,8 +337,8 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, auto& linheap_memory = memory_region->linear_heap_memory; if (linheap_memory->size() + Memory::PAGE_SIZE > memory_region->size) { - LOG_ERROR(Kernel_SVC, - "Not enough space in region to allocate a new TLS page for thread"); + NGLOG_ERROR(Kernel_SVC, + "Not enough space in region to allocate a new TLS page for thread"); return ERR_OUT_OF_MEMORY; } @@ -314,7 +360,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, // TODO(Subv): Find the correct MemoryState for this region. vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE, linheap_memory, offset, Memory::PAGE_SIZE, - MemoryState::ThreadLocalStorage); + MemoryState::ThreadLocal); } // Mark the slot as used @@ -332,32 +378,23 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, void Thread::SetPriority(u32 priority) { ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, "Invalid priority value."); - Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority); - nominal_priority = current_priority = priority; -} - -void Thread::UpdatePriority() { - u32 best_priority = nominal_priority; - for (auto& mutex : held_mutexes) { - if (mutex->priority < best_priority) - best_priority = mutex->priority; - } - BoostPriority(best_priority); + nominal_priority = priority; + UpdatePriority(); } void Thread::BoostPriority(u32 priority) { - Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority); + scheduler->SetThreadPriority(this, priority); current_priority = priority; } SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority, SharedPtr<Process> owner_process) { // Setup page table so we can write to memory - SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); + SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table); // Initialize new "main" thread auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0, - Memory::HEAP_VADDR_END, owner_process); + Memory::STACK_AREA_VADDR_END, owner_process); SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); @@ -392,13 +429,86 @@ VAddr Thread::GetCommandBufferAddress() const { return GetTLSAddress() + CommandHeaderOffset; } +void Thread::AddMutexWaiter(SharedPtr<Thread> thread) { + thread->lock_owner = this; + wait_mutex_threads.emplace_back(std::move(thread)); + UpdatePriority(); +} + +void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) { + boost::remove_erase(wait_mutex_threads, thread); + thread->lock_owner = nullptr; + UpdatePriority(); +} + +void Thread::UpdatePriority() { + // Find the highest priority among all the threads that are waiting for this thread's lock + u32 new_priority = nominal_priority; + for (const auto& thread : wait_mutex_threads) { + if (thread->nominal_priority < new_priority) + new_priority = thread->nominal_priority; + } + + if (new_priority == current_priority) + return; + + scheduler->SetThreadPriority(this, new_priority); + + current_priority = new_priority; + + // Recursively update the priority of the thread that depends on the priority of this one. + if (lock_owner) + lock_owner->UpdatePriority(); +} + +void Thread::ChangeCore(u32 core, u64 mask) { + ideal_core = core; + affinity_mask = mask; + + if (status != THREADSTATUS_READY) { + return; + } + + boost::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)}; + + if (!new_processor_id) { + new_processor_id = processor_id; + } + if (ideal_core != -1 && + Core::System().GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) { + new_processor_id = ideal_core; + } + + ASSERT(*new_processor_id < 4); + + // Add thread to new core's scheduler + auto& next_scheduler = Core::System().GetInstance().Scheduler(*new_processor_id); + + if (*new_processor_id != processor_id) { + // Remove thread from previous core's scheduler + scheduler->RemoveThread(this); + next_scheduler->AddThread(this, current_priority); + } + + processor_id = *new_processor_id; + + // If the thread was ready, unschedule from the previous core and schedule on the new core + scheduler->UnscheduleThread(this, current_priority); + next_scheduler->ScheduleThread(this, current_priority); + + // Change thread's scheduler + scheduler = next_scheduler; + + Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// /** * Gets the current thread */ Thread* GetCurrentThread() { - return Core::System::GetInstance().Scheduler().GetCurrentThread(); + return Core::System::GetInstance().CurrentScheduler().GetCurrentThread(); } void ThreadingInit() { @@ -406,6 +516,8 @@ void ThreadingInit() { next_thread_id = 1; } -void ThreadingShutdown() {} +void ThreadingShutdown() { + Kernel::ClearProcessList(); +} } // namespace Kernel diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 4fd2fc2f8..1d2da6d50 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -4,6 +4,7 @@ #pragma once +#include <memory> #include <string> #include <unordered_map> #include <vector> @@ -18,7 +19,7 @@ enum ThreadPriority : u32 { THREADPRIO_HIGHEST = 0, ///< Highest thread priority THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps - THREADPRIO_DEFAULT = 48, ///< Default thread priority for userland apps + THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps THREADPRIO_LOWEST = 63, ///< Lowest thread priority }; @@ -38,11 +39,12 @@ enum ThreadProcessorId : s32 { enum ThreadStatus { THREADSTATUS_RUNNING, ///< Currently running THREADSTATUS_READY, ///< Ready to run - THREADSTATUS_WAIT_ARB, ///< Waiting on an address arbiter + THREADSTATUS_WAIT_HLE_EVENT, ///< Waiting for hle event to finish THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC THREADSTATUS_WAIT_IPC, ///< Waiting for the reply from an IPC request THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true + THREADSTATUS_WAIT_MUTEX, ///< Waiting due to an ArbitrateLock/WaitProcessWideKey svc THREADSTATUS_DORMANT, ///< Created but not yet made ready THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated }; @@ -54,8 +56,8 @@ enum class ThreadWakeupReason { namespace Kernel { -class Mutex; class Process; +class Scheduler; class Thread final : public WaitObject { public: @@ -104,17 +106,23 @@ public: void SetPriority(u32 priority); /** - * Boost's a thread's priority to the best priority among the thread's held mutexes. - * This prevents priority inversion via priority inheritance. - */ - void UpdatePriority(); - - /** * Temporarily boosts the thread's priority until the next time it is scheduled * @param priority The new priority */ void BoostPriority(u32 priority); + /// Adds a thread to the list of threads that are waiting for a lock held by this thread. + void AddMutexWaiter(SharedPtr<Thread> thread); + + /// Removes a thread from the list of threads that are waiting for a lock held by this thread. + void RemoveMutexWaiter(SharedPtr<Thread> thread); + + /// Recalculates the current priority taking into account priority inheritance. + void UpdatePriority(); + + /// Changes the core that the thread is running or scheduled to run on. + void ChangeCore(u32 core, u64 mask); + /** * Gets the thread's thread ID * @return The thread's ID @@ -205,19 +213,22 @@ public: VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread - /// Mutexes currently held by this thread, which will be released when it exits. - boost::container::flat_set<SharedPtr<Mutex>> held_mutexes; - - /// Mutexes that this thread is currently waiting for. - boost::container::flat_set<SharedPtr<Mutex>> pending_mutexes; - SharedPtr<Process> owner_process; ///< Process that owns this thread /// Objects that the thread is waiting on, in the same order as they were // passed to WaitSynchronization1/N. std::vector<SharedPtr<WaitObject>> wait_objects; - VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address + /// List of threads that are waiting for a mutex that is held by this thread. + std::vector<SharedPtr<Thread>> wait_mutex_threads; + + /// Thread that owns the lock that this thread is waiting for. + SharedPtr<Thread> lock_owner; + + // If waiting on a ConditionVariable, this is the ConditionVariable address + VAddr condvar_wait_address; + VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address + Handle wait_handle; ///< The handle used to wait for the mutex. std::string name; @@ -234,6 +245,11 @@ public: // available. In case of a timeout, the object will be nullptr. std::function<WakeupCallback> wakeup_callback; + std::shared_ptr<Scheduler> scheduler; + + u32 ideal_core{0xFFFFFFFF}; + u64 affinity_mask{0x1}; + private: Thread(); ~Thread() override; diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 8da745634..661356a97 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -57,7 +57,8 @@ void Timer::Set(s64 initial, s64 interval) { // Immediately invoke the callback Signal(0); } else { - CoreTiming::ScheduleEvent(nsToCycles(initial), timer_callback_event_type, callback_handle); + CoreTiming::ScheduleEvent(CoreTiming::nsToCycles(initial), timer_callback_event_type, + callback_handle); } } @@ -77,7 +78,7 @@ void Timer::WakeupAllWaitingThreads() { } void Timer::Signal(int cycles_late) { - LOG_TRACE(Kernel, "Timer %u fired", GetObjectId()); + NGLOG_TRACE(Kernel, "Timer {} fired", GetObjectId()); signaled = true; @@ -86,7 +87,7 @@ void Timer::Signal(int cycles_late) { if (interval_delay != 0) { // Reschedule the timer with the interval delay - CoreTiming::ScheduleEvent(nsToCycles(interval_delay) - cycles_late, + CoreTiming::ScheduleEvent(CoreTiming::nsToCycles(interval_delay) - cycles_late, timer_callback_event_type, callback_handle); } } @@ -97,7 +98,7 @@ static void TimerCallback(u64 timer_handle, int cycles_late) { timer_callback_handle_table.Get<Timer>(static_cast<Handle>(timer_handle)); if (timer == nullptr) { - LOG_CRITICAL(Kernel, "Callback fired for invalid timer %08" PRIx64, timer_handle); + NGLOG_CRITICAL(Kernel, "Callback fired for invalid timer {:016X}", timer_handle); return; } diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index d5b36d71a..676e5b282 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <cinttypes> #include <iterator> #include "common/assert.h" #include "common/logging/log.h" @@ -18,8 +17,26 @@ namespace Kernel { static const char* GetMemoryStateName(MemoryState state) { static const char* names[] = { - "Free", "Reserved", "IO", "Static", "Code", "Private", - "Shared", "Continuous", "Aliased", "Alias", "AliasCode", "Locked", + "Unmapped", + "Io", + "Normal", + "CodeStatic", + "CodeMutable", + "Heap", + "Shared", + "Unknown1" + "ModuleCodeStatic", + "ModuleCodeMutable", + "IpcBuffer0", + "Mapped", + "ThreadLocal", + "TransferMemoryIsolated", + "TransferMemory", + "ProcessMemory", + "Unknown2" + "IpcBuffer1", + "IpcBuffer3", + "KernelStack", }; return names[(int)state]; @@ -87,8 +104,15 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, VirtualMemoryArea& final_vma = vma_handle->second; ASSERT(final_vma.size == size); - Core::CPU().MapBackingMemory(target, size, block->data() + offset, - VMAPermission::ReadWriteExecute); + auto& system = Core::System::GetInstance(); + system.ArmInterface(0).MapBackingMemory(target, size, block->data() + offset, + VMAPermission::ReadWriteExecute); + system.ArmInterface(1).MapBackingMemory(target, size, block->data() + offset, + VMAPermission::ReadWriteExecute); + system.ArmInterface(2).MapBackingMemory(target, size, block->data() + offset, + VMAPermission::ReadWriteExecute); + system.ArmInterface(3).MapBackingMemory(target, size, block->data() + offset, + VMAPermission::ReadWriteExecute); final_vma.type = VMAType::AllocatedMemoryBlock; final_vma.permissions = VMAPermission::ReadWrite; @@ -109,7 +133,11 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me VirtualMemoryArea& final_vma = vma_handle->second; ASSERT(final_vma.size == size); - Core::CPU().MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); + auto& system = Core::System::GetInstance(); + system.ArmInterface(0).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); + system.ArmInterface(1).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); + system.ArmInterface(2).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); + system.ArmInterface(3).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); final_vma.type = VMAType::BackingMemory; final_vma.permissions = VMAPermission::ReadWrite; @@ -142,7 +170,7 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) { VirtualMemoryArea& vma = vma_handle->second; vma.type = VMAType::Free; vma.permissions = VMAPermission::None; - vma.meminfo_state = MemoryState::Free; + vma.meminfo_state = MemoryState::Unmapped; vma.backing_block = nullptr; vma.offset = 0; @@ -166,6 +194,13 @@ ResultCode VMManager::UnmapRange(VAddr target, u64 size) { } ASSERT(FindVMA(target)->second.size >= size); + + auto& system = Core::System::GetInstance(); + system.ArmInterface(0).UnmapMemory(target, size); + system.ArmInterface(1).UnmapMemory(target, size); + system.ArmInterface(2).UnmapMemory(target, size); + system.ArmInterface(3).UnmapMemory(target, size); + return RESULT_SUCCESS; } @@ -204,11 +239,10 @@ void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) { } } -void VMManager::LogLayout(Log::Level log_level) const { +void VMManager::LogLayout() const { for (const auto& p : vma_map) { const VirtualMemoryArea& vma = p.second; - LOG_GENERIC(Log::Class::Kernel, log_level, - "%016" PRIx64 " - %016" PRIx64 " size: %16" PRIx64 " %c%c%c %s", vma.base, + NGLOG_DEBUG(Kernel, "{:016X} - {:016X} size: {:016X} {}{}{} {}", vma.base, vma.base + vma.size, vma.size, (u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-', (u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-', @@ -224,8 +258,8 @@ VMManager::VMAIter VMManager::StripIterConstness(const VMAHandle& iter) { } ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u64 size) { - ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%16" PRIx64, size); - ASSERT_MSG((base & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%016" PRIx64, base); + ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x{:016X}", size); + ASSERT_MSG((base & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x{:016X}", base); VMAIter vma_handle = StripIterConstness(FindVMA(base)); if (vma_handle == vma_map.end()) { @@ -260,8 +294,8 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u64 size) { } ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u64 size) { - ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%16" PRIx64, size); - ASSERT_MSG((target & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%016" PRIx64, target); + ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x{:016X}", size); + ASSERT_MSG((target & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x{:016X}", target); VAddr target_end = target + size; ASSERT(target_end >= target); @@ -358,38 +392,23 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { } u64 VMManager::GetTotalMemoryUsage() { - LOG_WARNING(Kernel, "(STUBBED) called"); - return 0xBE000000; + NGLOG_WARNING(Kernel, "(STUBBED) called"); + return 0xF8000000; } u64 VMManager::GetTotalHeapUsage() { - LOG_WARNING(Kernel, "(STUBBED) called"); + NGLOG_WARNING(Kernel, "(STUBBED) called"); return 0x0; } VAddr VMManager::GetAddressSpaceBaseAddr() { - LOG_WARNING(Kernel, "(STUBBED) called"); + NGLOG_WARNING(Kernel, "(STUBBED) called"); return 0x8000000; } u64 VMManager::GetAddressSpaceSize() { - LOG_WARNING(Kernel, "(STUBBED) called"); + NGLOG_WARNING(Kernel, "(STUBBED) called"); return MAX_ADDRESS; } -VAddr VMManager::GetMapRegionBaseAddr() { - LOG_WARNING(Kernel, "(STUBBED) called"); - return Memory::HEAP_VADDR; -} - -VAddr VMManager::GetNewMapRegionBaseAddr() { - LOG_WARNING(Kernel, "(STUBBED) called"); - return 0x8000000; -} - -u64 VMManager::GetNewMapRegionSize() { - LOG_WARNING(Kernel, "(STUBBED) called"); - return 0x8000000; -} - } // namespace Kernel diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 8de704a60..38e4ebcd3 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -41,15 +41,24 @@ enum class VMAPermission : u8 { /// Set of values returned in MemoryInfo.state by svcQueryMemory. enum class MemoryState : u32 { - Free = 0, - IO = 1, - Normal = 2, - Code = 3, - Static = 4, - Heap = 5, - Shared = 6, - Mapped = 6, - ThreadLocalStorage = 12, + Unmapped = 0x0, + Io = 0x1, + Normal = 0x2, + CodeStatic = 0x3, + CodeMutable = 0x4, + Heap = 0x5, + Shared = 0x6, + ModuleCodeStatic = 0x8, + ModuleCodeMutable = 0x9, + IpcBuffer0 = 0xA, + Mapped = 0xB, + ThreadLocal = 0xC, + TransferMemoryIsolated = 0xD, + TransferMemory = 0xE, + ProcessMemory = 0xF, + IpcBuffer1 = 0x11, + IpcBuffer3 = 0x12, + KernelStack = 0x13, }; /** @@ -66,7 +75,7 @@ struct VirtualMemoryArea { VMAType type = VMAType::Free; VMAPermission permissions = VMAPermission::None; /// Tag returned by svcQueryMemory. Not otherwise used. - MemoryState meminfo_state = MemoryState::Free; + MemoryState meminfo_state = MemoryState::Unmapped; // Settings for type = AllocatedMemoryBlock /// Memory block backing this VMA. @@ -178,7 +187,7 @@ public: void RefreshMemoryBlockMappings(const std::vector<u8>* block); /// Dumps the address space layout to the log, for debugging - void LogLayout(Log::Level log_level) const; + void LogLayout() const; /// Gets the total memory usage, used by svcGetInfo u64 GetTotalMemoryUsage(); @@ -192,15 +201,6 @@ public: /// Gets the total address space address size, used by svcGetInfo u64 GetAddressSpaceSize(); - /// Gets the map region base address, used by svcGetInfo - VAddr GetMapRegionBaseAddr(); - - /// Gets the base address for a new memory region, used by svcGetInfo - VAddr GetNewMapRegionBaseAddr(); - - /// Gets the size for a new memory region, used by svcGetInfo - u64 GetNewMapRegionSize(); - /// Each VMManager has its own page table, which is set as the main one when the owning process /// is scheduled. Memory::PageTable page_table; diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp index ec147b84c..b08ac72c1 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/wait_object.cpp @@ -39,7 +39,8 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { for (const auto& thread : waiting_threads) { // The list of waiting threads must not contain threads that are not waiting to be awakened. ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY || - thread->status == THREADSTATUS_WAIT_SYNCH_ALL, + thread->status == THREADSTATUS_WAIT_SYNCH_ALL || + thread->status == THREADSTATUS_WAIT_HLE_EVENT, "Inconsistent thread statuses in waiting_threads"); if (thread->current_priority >= candidate_priority) |