diff options
Diffstat (limited to 'src/core/hle/kernel')
45 files changed, 805 insertions, 815 deletions
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp deleted file mode 100644 index 776d342f0..000000000 --- a/src/core/hle/kernel/address_arbiter.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/common_types.h" -#include "common/logging/log.h" -#include "core/hle/kernel/address_arbiter.h" -#include "core/hle/kernel/errors.h" -#include "core/hle/kernel/thread.h" -#include "core/memory.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Kernel namespace - -namespace Kernel { - -AddressArbiter::AddressArbiter() {} -AddressArbiter::~AddressArbiter() {} - -SharedPtr<AddressArbiter> AddressArbiter::Create(std::string name) { - SharedPtr<AddressArbiter> address_arbiter(new AddressArbiter); - - address_arbiter->name = std::move(name); - - return address_arbiter; -} - -ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, s32 value, - u64 nanoseconds) { - switch (type) { - - // Signal thread(s) waiting for arbitrate address... - case ArbitrationType::Signal: - // Negative value means resume all threads - if (value < 0) { - ArbitrateAllThreads(address); - } else { - // Resume first N threads - for (int i = 0; i < value; i++) - ArbitrateHighestPriorityThread(address); - } - break; - - // Wait current thread (acquire the arbiter)... - case ArbitrationType::WaitIfLessThan: - if ((s32)Memory::Read32(address) < value) { - Kernel::WaitCurrentThread_ArbitrateAddress(address); - } - break; - case ArbitrationType::WaitIfLessThanWithTimeout: - if ((s32)Memory::Read32(address) < value) { - Kernel::WaitCurrentThread_ArbitrateAddress(address); - GetCurrentThread()->WakeAfterDelay(nanoseconds); - } - break; - case ArbitrationType::DecrementAndWaitIfLessThan: { - s32 memory_value = Memory::Read32(address); - if (memory_value < value) { - // Only change the memory value if the thread should wait - Memory::Write32(address, (s32)memory_value - 1); - Kernel::WaitCurrentThread_ArbitrateAddress(address); - } - break; - } - case ArbitrationType::DecrementAndWaitIfLessThanWithTimeout: { - s32 memory_value = Memory::Read32(address); - if (memory_value < value) { - // Only change the memory value if the thread should wait - Memory::Write32(address, (s32)memory_value - 1); - Kernel::WaitCurrentThread_ArbitrateAddress(address); - GetCurrentThread()->WakeAfterDelay(nanoseconds); - } - break; - } - - default: - LOG_ERROR(Kernel, "unknown type=%d", type); - return ERR_INVALID_ENUM_VALUE_FND; - } - - // The calls that use a timeout seem to always return a Timeout error even if they did not put - // the thread to sleep - if (type == ArbitrationType::WaitIfLessThanWithTimeout || - type == ArbitrationType::DecrementAndWaitIfLessThanWithTimeout) { - - return RESULT_TIMEOUT; - } - return RESULT_SUCCESS; -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h deleted file mode 100644 index 1d24401b1..000000000 --- a/src/core/hle/kernel/address_arbiter.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "common/common_types.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/result.h" - -// Address arbiters are an underlying kernel synchronization object that can be created/used via -// supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR -// applications use them as an underlying mechanism to implement thread-safe barriers, events, and -// semphores. - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Kernel namespace - -namespace Kernel { - -enum class ArbitrationType : u32 { - Signal, - WaitIfLessThan, - DecrementAndWaitIfLessThan, - WaitIfLessThanWithTimeout, - DecrementAndWaitIfLessThanWithTimeout, -}; - -class AddressArbiter final : public Object { -public: - /** - * Creates an address arbiter. - * - * @param name Optional name used for debugging. - * @returns The created AddressArbiter. - */ - static SharedPtr<AddressArbiter> Create(std::string name = "Unknown"); - - std::string GetTypeName() const override { - return "Arbiter"; - } - std::string GetName() const override { - return name; - } - - static const HandleType HANDLE_TYPE = HandleType::AddressArbiter; - HandleType GetHandleType() const override { - return HANDLE_TYPE; - } - - std::string name; ///< Name of address arbiter object (optional) - - ResultCode ArbitrateAddress(ArbitrationType type, VAddr address, s32 value, u64 nanoseconds); - -private: - AddressArbiter(); - ~AddressArbiter() override; -}; - -} // namespace FileSys diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp index ce5d94e99..fb2b6f7a3 100644 --- a/src/core/hle/kernel/client_port.cpp +++ b/src/core/hle/kernel/client_port.cpp @@ -39,4 +39,4 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() { return MakeResult(std::get<SharedPtr<ClientSession>>(sessions)); } -} // namespace +} // namespace Kernel diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h index 8f7d6ac44..a829aeb6d 100644 --- a/src/core/hle/kernel/client_port.h +++ b/src/core/hle/kernel/client_port.h @@ -47,4 +47,4 @@ private: ~ClientPort() override; }; -} // namespace +} // namespace Kernel diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp index 646a5cc64..72773d8b1 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp @@ -48,4 +48,4 @@ ResultCode ClientSession::SendSyncRequest(SharedPtr<Thread> thread) { return server->HandleSyncRequest(std::move(thread)); } -} // namespace +} // namespace Kernel diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index 671174ec4..2258f95bc 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h @@ -7,7 +7,7 @@ #include <memory> #include <string> #include "common/common_types.h" -#include "core/hle/kernel/sync_object.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/result.h" namespace Kernel { @@ -16,7 +16,7 @@ class ServerSession; class Session; class Thread; -class ClientSession final : public SyncObject { +class ClientSession final : public Object { public: friend class ServerSession; @@ -33,7 +33,7 @@ public: return HANDLE_TYPE; } - ResultCode SendSyncRequest(SharedPtr<Thread> thread) override; + ResultCode SendSyncRequest(SharedPtr<Thread> thread); std::string name; ///< Name of client port (optional) @@ -45,4 +45,4 @@ private: ~ClientSession() override; }; -} // namespace +} // namespace Kernel diff --git a/src/core/hle/kernel/condition_variable.cpp b/src/core/hle/kernel/condition_variable.cpp index 5942eae61..a786d7f74 100644 --- a/src/core/hle/kernel/condition_variable.cpp +++ b/src/core/hle/kernel/condition_variable.cpp @@ -15,13 +15,12 @@ ConditionVariable::ConditionVariable() {} ConditionVariable::~ConditionVariable() {} ResultVal<SharedPtr<ConditionVariable>> ConditionVariable::Create(VAddr guest_addr, - VAddr mutex_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 = mutex_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); @@ -43,7 +42,7 @@ void ConditionVariable::Acquire(Thread* thread) { ResultCode ConditionVariable::Release(s32 target) { if (target == -1) { // When -1, wake up all waiting threads - SetAvailableCount(GetWaitingThreads().size()); + SetAvailableCount(static_cast<s32>(GetWaitingThreads().size())); WakeupAllWaitingThreads(); } else { // Otherwise, wake up just a single thread diff --git a/src/core/hle/kernel/condition_variable.h b/src/core/hle/kernel/condition_variable.h index 0610a284f..1c9f06769 100644 --- a/src/core/hle/kernel/condition_variable.h +++ b/src/core/hle/kernel/condition_variable.h @@ -4,8 +4,8 @@ #pragma once -#include <queue> #include <string> +#include <queue> #include "common/common_types.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/wait_object.h" @@ -19,12 +19,10 @@ 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 mutex_addr Optional address of a guest mutex associated with this condition variable, - * used by the OS for implementing events. * @param name Optional name of condition variable. * @return The created condition variable. */ - static ResultVal<SharedPtr<ConditionVariable>> Create(VAddr guest_addr, VAddr mutex_addr = 0, + static ResultVal<SharedPtr<ConditionVariable>> Create(VAddr guest_addr, std::string name = "Unknown"); std::string GetTypeName() const override { diff --git a/src/core/hle/kernel/domain.cpp b/src/core/hle/kernel/domain.cpp deleted file mode 100644 index 5035e9c08..000000000 --- a/src/core/hle/kernel/domain.cpp +++ /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. - -#include "common/logging/log.h" -#include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/client_port.h" -#include "core/hle/kernel/domain.h" -#include "core/hle/kernel/handle_table.h" -#include "core/hle/kernel/hle_ipc.h" -#include "core/hle/kernel/process.h" -#include "core/hle/kernel/session.h" -#include "core/hle/kernel/thread.h" - -namespace Kernel { - -ResultVal<SharedPtr<Domain>> Domain::Create(std::string name) { - SharedPtr<Domain> domain(new Domain); - domain->name = std::move(name); - return MakeResult(std::move(domain)); -} - -ResultVal<SharedPtr<Domain>> Domain::CreateFromSession(const Session& session) { - auto res = Create(session.port->GetName() + "_Domain"); - auto& domain = res.Unwrap(); - domain->request_handlers.push_back(std::move(session.server->hle_handler)); - Kernel::g_handle_table.ConvertSessionToDomain(session, domain); - return res; -} - -ResultCode Domain::SendSyncRequest(SharedPtr<Thread> thread) { - Kernel::HLERequestContext context(this); - u32* cmd_buf = (u32*)Memory::GetPointer(Kernel::GetCurrentThread()->GetTLSAddress()); - context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process, - Kernel::g_handle_table); - - auto& domain_message_header = context.GetDomainMessageHeader(); - if (domain_message_header) { - // If there is a DomainMessageHeader, then this is CommandType "Request" - const u32 object_id{context.GetDomainMessageHeader()->object_id}; - switch (domain_message_header->command) { - case IPC::DomainMessageHeader::CommandType::SendMessage: - return request_handlers[object_id - 1]->HandleSyncRequest(context); - - case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { - LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x%08X", object_id); - - request_handlers[object_id - 1] = nullptr; - - IPC::RequestBuilder rb{context, 2}; - rb.Push(RESULT_SUCCESS); - - return RESULT_SUCCESS; - } - } - - LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value()); - UNIMPLEMENTED(); - } - return request_handlers.front()->HandleSyncRequest(context); -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/domain.h b/src/core/hle/kernel/domain.h deleted file mode 100644 index 3fec3b0b2..000000000 --- a/src/core/hle/kernel/domain.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <memory> -#include <string> -#include <vector> -#include "core/hle/kernel/sync_object.h" -#include "core/hle/result.h" - -namespace Kernel { - -class Session; -class SessionRequestHandler; - -class Domain final : public SyncObject { -public: - std::string GetTypeName() const override { - return "Domain"; - } - - static const HandleType HANDLE_TYPE = HandleType::Domain; - HandleType GetHandleType() const override { - return HANDLE_TYPE; - } - - static ResultVal<SharedPtr<Domain>> CreateFromSession(const Session& server); - - ResultCode SendSyncRequest(SharedPtr<Thread> thread) override; - - /// The name of this domain (optional) - std::string name; - - std::vector<std::shared_ptr<SessionRequestHandler>> request_handlers; - -private: - Domain() = default; - ~Domain() override = default; - - static ResultVal<SharedPtr<Domain>> Create(std::string name = "Unknown"); -}; - -} // namespace Kernel diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 23f9df0d6..9cae2369f 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -52,4 +52,4 @@ void Event::WakeupAllWaitingThreads() { signaled = false; } -} // namespace +} // namespace Kernel diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index cc41abb85..e5c924a75 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h @@ -49,4 +49,4 @@ private: ~Event() override; }; -} // namespace +} // namespace Kernel diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp index 12506e64c..822449cd5 100644 --- a/src/core/hle/kernel/handle_table.cpp +++ b/src/core/hle/kernel/handle_table.cpp @@ -5,12 +5,11 @@ #include <utility> #include "common/assert.h" #include "common/logging/log.h" -#include "core/hle/kernel/client_session.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/process.h" -#include "core/hle/kernel/session.h" #include "core/hle/kernel/thread.h" namespace Kernel { @@ -55,14 +54,6 @@ ResultVal<Handle> HandleTable::Duplicate(Handle handle) { return Create(std::move(object)); } -void HandleTable::ConvertSessionToDomain(const Session& session, SharedPtr<Object> domain) { - for (auto& object : objects) { - if (DynamicObjectCast<ClientSession>(object) == session.client) { - object = domain; - } - } -} - ResultCode HandleTable::Close(Handle handle) { if (!IsValid(handle)) return ERR_INVALID_HANDLE; @@ -87,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)) { @@ -104,4 +95,4 @@ void HandleTable::Clear() { next_free_slot = 0; } -} // namespace +} // namespace Kernel diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h index dba5573a8..ba968c666 100644 --- a/src/core/hle/kernel/handle_table.h +++ b/src/core/hle/kernel/handle_table.h @@ -17,8 +17,6 @@ enum KernelHandle : Handle { CurrentProcess = 0xFFFF8001, }; -class Session; - /** * This class allows the creation of Handles, which are references to objects that can be tested * for validity and looked up. Here they are used to pass references to kernel objects to/from the @@ -62,11 +60,6 @@ public: ResultVal<Handle> Duplicate(Handle handle); /** - * Convert all handles of the specified Session to the specified Domain. - */ - void ConvertSessionToDomain(const Session& session, SharedPtr<Object> domain); - - /** * Closes a handle, removing it from the table and decreasing the object's ref-count. * @return `RESULT_SUCCESS` or one of the following errors: * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. @@ -130,4 +123,4 @@ private: extern HandleTable g_handle_table; -} // namespace +} // namespace Kernel diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index ac62a0d5a..ffcbfe64f 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -7,12 +7,13 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/domain.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" #include "core/hle/kernel/process.h" #include "core/hle/kernel/server_session.h" +#include "core/memory.h" namespace Kernel { @@ -26,8 +27,30 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s boost::range::remove_erase(connected_sessions, server_session); } -HLERequestContext::HLERequestContext(SharedPtr<Kernel::Domain> domain) : domain(std::move(domain)) { - cmd_buf[0] = 0; +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) @@ -39,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 @@ -49,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); } @@ -81,32 +104,31 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { for (unsigned i = 0; i < command_header->num_buf_w_descriptors; ++i) { buffer_w_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>()); } - if (command_header->buf_c_descriptor_flags != - IPC::CommandHeader::BufferDescriptorCFlag::Disabled) { - if (command_header->buf_c_descriptor_flags != - IPC::CommandHeader::BufferDescriptorCFlag::OneDescriptor) { - UNIMPLEMENTED(); - } - } + + buffer_c_offset = rp.GetCurrentOffset() + command_header->data_size; // Padding to align to 16 bytes rp.AlignWithPadding(); - if (IsDomain() && (command_header->type == IPC::CommandType::Request || !incoming)) { + if (Session()->IsDomain() && (command_header->type == IPC::CommandType::Request || !incoming)) { // If this is an incoming message, only CommandType "Request" has a domain header - // All outgoing domain messages have the domain header - domain_message_header = - std::make_unique<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>()); + // All outgoing domain messages have the domain header, if only incoming has it + if (incoming || domain_message_header) { + domain_message_header = + std::make_shared<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>()); + } else { + if (Session()->IsDomain()) + LOG_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(); - if (domain_message_header && - domain_message_header->command == - IPC::DomainMessageHeader::CommandType::CloseVirtualHandle) { + if (domain_message_header && domain_message_header->command == + IPC::DomainMessageHeader::CommandType::CloseVirtualHandle) { // CloseVirtualHandle command does not have SFC* or any data return; } @@ -117,6 +139,31 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { ASSERT(data_payload_header->magic == Common::MakeMagic('S', 'F', 'C', 'O')); } + rp.SetCurrentOffset(buffer_c_offset); + + // For Inline buffers, the response data is written directly to buffer_c_offset + // and in this case we don't have any BufferDescriptorC on the request. + if (command_header->buf_c_descriptor_flags > + IPC::CommandHeader::BufferDescriptorCFlag::InlineDescriptor) { + if (command_header->buf_c_descriptor_flags == + IPC::CommandHeader::BufferDescriptorCFlag::OneDescriptor) { + buffer_c_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorC>()); + } else { + unsigned num_buf_c_descriptors = + static_cast<unsigned>(command_header->buf_c_descriptor_flags.Value()) - 2; + + // This is used to detect possible underflows, in case something is broken + // with the two ifs above and the flags value is == 0 || == 1. + ASSERT(num_buf_c_descriptors < 14); + + for (unsigned i = 0; i < num_buf_c_descriptors; ++i) { + buffer_c_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorC>()); + } + } + } + + rp.SetCurrentOffset(data_payload_offset); + command = rp.Pop<u32_le>(); rp.Skip(1, false); // The command is actually an u64, but we don't use the high part. } @@ -139,8 +186,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); @@ -151,7 +201,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(), @@ -181,19 +231,118 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P // TODO(Subv): Translate the X/A/B/W buffers. - if (IsDomain()) { + if (Session()->IsDomain() && domain_message_header) { ASSERT(domain_message_header->num_objects == domain_objects.size()); // Write the domain objects to the command buffer, these go after the raw untranslated data. // TODO(Subv): This completely ignores C buffers. size_t domain_offset = size - domain_message_header->num_objects; - auto& request_handlers = domain->request_handlers; + auto& request_handlers = server_session->domain_request_handlers; for (auto& object : domain_objects) { request_handlers.emplace_back(object); - dst_cmdbuf[domain_offset++] = request_handlers.size(); + 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> buffer; + const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[0].Size()}; + + if (is_buffer_a) { + buffer.resize(BufferDescriptorA()[0].Size()); + Memory::ReadBlock(BufferDescriptorA()[0].Address(), buffer.data(), buffer.size()); + } else { + buffer.resize(BufferDescriptorX()[0].Size()); + Memory::ReadBlock(BufferDescriptorX()[0].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 %lx is too big", size); + + if (is_buffer_b) { + Memory::WriteBlock(BufferDescriptorB()[0].Address(), buffer, size); + } else { + Memory::WriteBlock(BufferDescriptorC()[0].Address(), buffer, size); + } + + return size; +} + +size_t HLERequestContext::WriteBuffer(const std::vector<u8>& buffer) 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::GetWriteBufferSize() const { + const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[0].Size()}; + return is_buffer_b ? BufferDescriptorB()[0].Size() : BufferDescriptorC()[0].Size(); +} + +std::string HLERequestContext::Description() const { + if (!command_header) { + return "No command header available"; + } + std::ostringstream s; + s << "IPC::CommandHeader: Type:" << static_cast<u32>(command_header->type.Value()); + s << ", X(Pointer):" << command_header->num_buf_x_descriptors; + if (command_header->num_buf_x_descriptors) { + s << '['; + for (u64 i = 0; i < command_header->num_buf_x_descriptors; ++i) { + s << "0x" << std::hex << BufferDescriptorX()[i].Size(); + if (i < command_header->num_buf_x_descriptors - 1) + s << ", "; + } + s << ']'; + } + s << ", A(Send):" << command_header->num_buf_a_descriptors; + if (command_header->num_buf_a_descriptors) { + s << '['; + for (u64 i = 0; i < command_header->num_buf_a_descriptors; ++i) { + s << "0x" << std::hex << BufferDescriptorA()[i].Size(); + if (i < command_header->num_buf_a_descriptors - 1) + s << ", "; + } + s << ']'; + } + s << ", B(Receive):" << command_header->num_buf_b_descriptors; + if (command_header->num_buf_b_descriptors) { + s << '['; + for (u64 i = 0; i < command_header->num_buf_b_descriptors; ++i) { + s << "0x" << std::hex << BufferDescriptorB()[i].Size(); + if (i < command_header->num_buf_b_descriptors - 1) + s << ", "; + } + s << ']'; + } + s << ", C(ReceiveList):" << BufferDescriptorC().size(); + if (!BufferDescriptorC().empty()) { + s << '['; + for (u64 i = 0; i < BufferDescriptorC().size(); ++i) { + s << "0x" << std::hex << BufferDescriptorC()[i].Size(); + if (i < BufferDescriptorC().size() - 1) + s << ", "; + } + s << ']'; + } + s << ", data_size:" << command_header->data_size.Value(); + + return s.str(); +} + } // namespace Kernel diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 6dceb766d..8b35da4c9 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. @@ -86,7 +89,6 @@ protected: */ class HLERequestContext { public: - HLERequestContext(SharedPtr<Kernel::Domain> domain); HLERequestContext(SharedPtr<Kernel::ServerSession> session); ~HLERequestContext(); @@ -96,28 +98,38 @@ public: } /** - * Returns the domain through which this request was made. - */ - const SharedPtr<Kernel::Domain>& Domain() const { - return domain; - } - - /** * Returns the session through which this request was made. This can be used as a map key to * access per-client data on services. */ - const SharedPtr<Kernel::ServerSession>& ServerSession() const { + const SharedPtr<Kernel::ServerSession>& Session() const { 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; @@ -143,14 +155,29 @@ public: return buffer_b_desciptors; } - const std::unique_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const { - return domain_message_header; + const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const { + return buffer_c_desciptors; } - bool IsDomain() const { - return domain != nullptr; + 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; + + /// Helper function to write a buffer using the appropriate buffer descriptor + size_t WriteBuffer(const void* buffer, size_t size) const; + + /// Helper function to write a buffer using the appropriate buffer descriptor + size_t WriteBuffer(const std::vector<u8>& buffer) const; + + /// Helper function to get the size of the input buffer + size_t GetReadBufferSize() const; + + /// Helper function to get the size of the output buffer + size_t GetWriteBufferSize() const; + template <typename T> SharedPtr<T> GetCopyObject(size_t index) { ASSERT(index < copy_objects.size()); @@ -183,25 +210,40 @@ public: domain_objects.clear(); } + size_t NumMoveObjects() const { + return move_objects.size(); + } + + size_t NumCopyObjects() const { + return copy_objects.size(); + } + + size_t NumDomainObjects() const { + return domain_objects.size(); + } + + std::string Description() const; + private: std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; - SharedPtr<Kernel::Domain> domain; SharedPtr<Kernel::ServerSession> server_session; // TODO(yuriks): Check common usage of this and optimize size accordingly boost::container::small_vector<SharedPtr<Object>, 8> move_objects; 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; std::vector<IPC::BufferDescriptorABW> buffer_w_desciptors; + std::vector<IPC::BufferDescriptorC> buffer_c_desciptors; unsigned data_payload_offset{}; + unsigned buffer_c_offset{}; u32_le command{}; }; 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 4d9549e45..053bf4e17 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -31,11 +31,6 @@ enum class HandleType : u32 { ServerPort, ClientSession, ServerSession, - Domain, -}; - -enum { - DEFAULT_STACK_SIZE = 0x10000, }; enum class ResetType { @@ -84,27 +79,12 @@ public: case HandleType::CodeSet: case HandleType::ClientPort: case HandleType::ClientSession: - case HandleType::Domain: return false; } UNREACHABLE(); } - /** - * Check if svcSendSyncRequest can be called on the object - * @return True svcSendSyncRequest can be called on the object, otherwise false - */ - bool IsSyncable() const { - switch (GetHandleType()) { - case HandleType::ClientSession: - case HandleType::Domain: - return true; - } - - UNREACHABLE(); - } - public: static unsigned int next_object_id; diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 4e86eb918..0b9dc700c 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -70,6 +70,7 @@ ResultCode Mutex::Release(Thread* thread) { holding_thread->held_mutexes.erase(this); holding_thread->UpdatePriority(); SetHoldingThread(nullptr); + SetHasWaiters(!GetWaitingThreads().empty()); WakeupAllWaitingThreads(); Core::System::GetInstance().PrepareReschedule(); diff --git a/src/core/hle/kernel/object_address_table.cpp b/src/core/hle/kernel/object_address_table.cpp index 434c16add..cd286f85d 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%lx", 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%lx", addr); objects.erase(addr); } diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 8e74059ea..3694afc60 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; @@ -119,11 +117,12 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { } 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, - std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, - MemoryState::Heap) + .MapMemoryBlock(Memory::STACK_VADDR, std::make_shared<std::vector<u8>>(stack_size, 0), 0, + stack_size, MemoryState::Mapped) .Unwrap(); misc_memory_used += stack_size; memory_region->used += stack_size; @@ -155,9 +154,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 +183,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 +204,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 +291,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 +322,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 517dc47a8..0149a3ed6 100644 --- a/src/core/hle/kernel/resource_limit.cpp +++ b/src/core/hle/kernel/resource_limit.cpp @@ -151,4 +151,4 @@ void ResourceLimitsInit() { void ResourceLimitsShutdown() {} -} // namespace +} // namespace Kernel diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h index 42874eb8d..1a0ca11f1 100644 --- a/src/core/hle/kernel/resource_limit.h +++ b/src/core/hle/kernel/resource_limit.h @@ -123,4 +123,4 @@ void ResourceLimitsInit(); // Destroys the resource limits void ResourceLimitsShutdown(); -} // namespace +} // namespace Kernel diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp new file mode 100644 index 000000000..921f27efb --- /dev/null +++ b/src/core/hle/kernel/scheduler.cpp @@ -0,0 +1,135 @@ +// Copyright 2018 yuzu emulator team +// 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 { + +Scheduler::Scheduler(ARM_Interface* cpu_core) : cpu_core(cpu_core) {} + +Scheduler::~Scheduler() { + for (auto& thread : thread_list) { + thread->Stop(); + } +} + +bool Scheduler::HaveReadyThreads() { + return ready_queue.get_first() != nullptr; +} + +Thread* Scheduler::GetCurrentThread() const { + return current_thread.get(); +} + +Thread* Scheduler::PopNextReadyThread() { + Thread* next = nullptr; + Thread* thread = GetCurrentThread(); + + if (thread && thread->status == THREADSTATUS_RUNNING) { + // We have to do better than the current thread. + // This call returns null when that's not possible. + next = ready_queue.pop_first_better(thread->current_priority); + if (!next) { + // Otherwise just keep going with the current thread + next = thread; + } + } else { + next = ready_queue.pop_first(); + } + + return next; +} + +void Scheduler::SwitchContext(Thread* new_thread) { + Thread* previous_thread = GetCurrentThread(); + + // Save context for previous thread + if (previous_thread) { + previous_thread->last_running_ticks = CoreTiming::GetTicks(); + cpu_core->SaveContext(previous_thread->context); + + if (previous_thread->status == THREADSTATUS_RUNNING) { + // This is only the case when a reschedule is triggered without the current thread + // yielding execution (i.e. an event triggered, system core time-sliced, etc) + ready_queue.push_front(previous_thread->current_priority, previous_thread); + previous_thread->status = THREADSTATUS_READY; + } + } + + // Load context of new thread + if (new_thread) { + ASSERT_MSG(new_thread->status == THREADSTATUS_READY, + "Thread must be ready to become running."); + + // Cancel any outstanding wakeup events for this thread + new_thread->CancelWakeupTimer(); + + auto previous_process = Core::CurrentProcess(); + + current_thread = new_thread; + + ready_queue.remove(new_thread->current_priority, new_thread); + new_thread->status = THREADSTATUS_RUNNING; + + if (previous_process != current_thread->owner_process) { + Core::CurrentProcess() = current_thread->owner_process; + SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table); + } + + cpu_core->LoadContext(new_thread->context); + cpu_core->SetTlsAddress(new_thread->GetTLSAddress()); + } else { + current_thread = nullptr; + // Note: We do not reset the current process and current page table when idling because + // technically we haven't changed processes, our threads are just paused. + } +} + +void Scheduler::Reschedule() { + Thread* cur = GetCurrentThread(); + Thread* next = PopNextReadyThread(); + + if (cur && next) { + LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId()); + } else if (cur) { + LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId()); + } else if (next) { + LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId()); + } + + SwitchContext(next); +} + +void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) { + thread_list.push_back(thread); + ready_queue.prepare(priority); +} + +void Scheduler::RemoveThread(Thread* thread) { + thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), + thread_list.end()); +} + +void Scheduler::ScheduleThread(Thread* thread, u32 priority) { + ASSERT(thread->status == THREADSTATUS_READY); + ready_queue.push_back(priority, thread); +} + +void Scheduler::UnscheduleThread(Thread* thread, u32 priority) { + ASSERT(thread->status == THREADSTATUS_READY); + ready_queue.remove(priority, thread); +} + +void Scheduler::SetThreadPriority(Thread* thread, u32 priority) { + // If thread was ready, adjust queues + if (thread->status == THREADSTATUS_READY) + ready_queue.move(thread, thread->current_priority, priority); + else + ready_queue.prepare(priority); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h new file mode 100644 index 000000000..27d0247d6 --- /dev/null +++ b/src/core/hle/kernel/scheduler.h @@ -0,0 +1,73 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <vector> +#include "common/common_types.h" +#include "common/thread_queue_list.h" +#include "core/arm/arm_interface.h" +#include "core/hle/kernel/thread.h" + +namespace Kernel { + +class Scheduler final { +public: + explicit Scheduler(ARM_Interface* cpu_core); + ~Scheduler(); + + /// Returns whether there are any threads that are ready to run. + bool HaveReadyThreads(); + + /// Reschedules to the next available thread (call after current thread is suspended) + void Reschedule(); + + /// Gets the current running thread + Thread* GetCurrentThread() const; + + /// Adds a new thread to the scheduler + void AddThread(SharedPtr<Thread> thread, u32 priority); + + /// Removes a thread from the scheduler + void RemoveThread(Thread* thread); + + /// Schedules a thread that has become "ready" + void ScheduleThread(Thread* thread, u32 priority); + + /// Unschedules a thread that was already scheduled + void UnscheduleThread(Thread* thread, u32 priority); + + /// Sets the priority of a thread in the scheduler + void SetThreadPriority(Thread* thread, u32 priority); + + /// Returns a list of all threads managed by the scheduler + const std::vector<SharedPtr<Thread>>& GetThreadList() const { + return thread_list; + } + +private: + /** + * Pops and returns the next thread from the thread queue + * @return A pointer to the next ready thread + */ + Thread* PopNextReadyThread(); + + /** + * Switches the CPU's active thread context to that of the specified thread + * @param new_thread The thread to switch to + */ + void SwitchContext(Thread* new_thread); + + /// Lists all thread ids that aren't deleted/etc. + std::vector<SharedPtr<Thread>> thread_list; + + /// Lists only ready thread ids. + Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue; + + SharedPtr<Thread> current_thread = nullptr; + + ARM_Interface* cpu_core; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index 49a9cdfa3..0b7061403 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp @@ -50,4 +50,4 @@ std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortP return std::make_tuple(std::move(server_port), std::move(client_port)); } -} // namespace +} // namespace Kernel diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index 6fe7c7f2f..9ef4ecc35 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h @@ -72,4 +72,4 @@ private: ~ServerPort() override; }; -} // namespace +} // namespace Kernel diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 09d02a691..33397d84f 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -4,6 +4,8 @@ #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" #include "core/hle/kernel/handle_table.h" @@ -56,34 +58,91 @@ void ServerSession::Acquire(Thread* thread) { pending_requesting_threads.pop_back(); } +ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) { + auto& domain_message_header = context.GetDomainMessageHeader(); + if (domain_message_header) { + // If there is a DomainMessageHeader, then this is CommandType "Request" + const u32 object_id{context.GetDomainMessageHeader()->object_id}; + switch (domain_message_header->command) { + case IPC::DomainMessageHeader::CommandType::SendMessage: + return domain_request_handlers[object_id - 1]->HandleSyncRequest(context); + + case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { + LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x%08X", object_id); + + domain_request_handlers[object_id - 1] = nullptr; + + IPC::ResponseBuilder rb{context, 2}; + rb.Push(RESULT_SUCCESS); + return RESULT_SUCCESS; + } + } + + LOG_CRITICAL(IPC, "Unknown domain command=%d", + static_cast<int>(domain_message_header->command.Value())); + ASSERT(false); + } + + return RESULT_SUCCESS; +} + ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) { // The ServerSession received a sync request, this means that there's new data available // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or // similar. - // If this ServerSession has an associated HLE handler, forward the request to it. - ResultCode result{RESULT_SUCCESS}; - if (hle_handler != nullptr) { - // Attempt to translate the incoming request's command buffer. - ResultCode translate_result = TranslateHLERequest(this); - if (translate_result.IsError()) - return translate_result; - - Kernel::HLERequestContext context(this); - u32* cmd_buf = (u32*)Memory::GetPointer(Kernel::GetCurrentThread()->GetTLSAddress()); - context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process, - Kernel::g_handle_table); - + Kernel::HLERequestContext context(this); + u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress()); + context.PopulateFromIncomingCommandBuffer(cmd_buf, *Core::CurrentProcess(), + Kernel::g_handle_table); + + ResultCode result = RESULT_SUCCESS; + // If the session has been converted to a domain, handle the domain request + if (IsDomain() && context.GetDomainMessageHeader()) { + result = HandleDomainSyncRequest(context); + // If there is no domain header, the regular session handler is used + } else if (hle_handler != nullptr) { + // If this ServerSession has an associated HLE handler, forward the request to it. result = hle_handler->HandleSyncRequest(context); - } else { - // Add the thread to the list of threads that have issued a sync request with this - // server. - pending_requesting_threads.push_back(std::move(thread)); + } + + if (thread->status == THREADSTATUS_RUNNING) { + // Put the thread to sleep until the server replies, it will be awoken in + // svcReplyAndReceive for LLE servers. + thread->status = THREADSTATUS_WAIT_IPC; + + if (hle_handler != nullptr) { + // For HLE services, we put the request threads to sleep for a short duration to + // simulate IPC overhead, but only if the HLE handler didn't put the thread to sleep for + // other reasons like an async callback. The IPC overhead is needed to prevent + // starvation when a thread only does sync requests to HLE services while a + // lower-priority thread is waiting to run. + + // This delay was approximated in a homebrew application by measuring the average time + // it takes for svcSendSyncRequest to return when performing the SetLcdForceBlack IPC + // request to the GSP:GPU service in a n3DS with firmware 11.6. The measured values have + // a high variance and vary between models. + static constexpr u64 IPCDelayNanoseconds = 39000; + thread->WakeAfterDelay(IPCDelayNanoseconds); + } else { + // Add the thread to the list of threads that have issued a sync request with this + // server. + pending_requesting_threads.push_back(std::move(thread)); + } } // If this ServerSession does not have an HLE implementation, just wake up the threads waiting // on it. WakeupAllWaitingThreads(); + + // Handle scenario when ConvertToDomain command was issued, as we must do the conversion at the + // end of the command such that only commands following this one are handled as domains + if (convert_to_domain) { + ASSERT_MSG(domain_request_handlers.empty(), "already a domain"); + domain_request_handlers = {hle_handler}; + convert_to_domain = false; + } + return result; } @@ -103,9 +162,4 @@ ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& n return std::make_tuple(std::move(server_session), std::move(client_session)); } - -ResultCode TranslateHLERequest(ServerSession* server_session) { - // TODO(Subv): Implement this function once multiple concurrent processes are supported. - return RESULT_SUCCESS; -} } // namespace Kernel diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index f4360ddf3..2da807042 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -21,6 +21,7 @@ class ServerSession; class Session; class SessionRequestHandler; class Thread; +class HLERequestContext; /** * Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS @@ -79,7 +80,10 @@ public: std::string name; ///< The name of this session (optional) std::shared_ptr<Session> parent; ///< The parent session, which links to the client endpoint. std::shared_ptr<SessionRequestHandler> - hle_handler; ///< This session's HLE request handler (optional) + hle_handler; ///< This session's HLE request handler (applicable when not a domain) + + /// This is the list of domain request handlers (after conversion to a domain) + std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers; /// List of threads that are pending a response after a sync request. This list is processed in /// a LIFO manner, thus, the last request will be dispatched first. @@ -91,6 +95,16 @@ public: /// TODO(Subv): Find a better name for this. SharedPtr<Thread> currently_handling; + /// Returns true if the session has been converted to a domain, otherwise False + bool IsDomain() const { + return !domain_request_handlers.empty(); + } + + /// Converts the session to a domain at the end of the current command + void ConvertToDomain() { + convert_to_domain = true; + } + private: ServerSession(); ~ServerSession() override; @@ -102,15 +116,13 @@ private: * @return The created server session */ static ResultVal<SharedPtr<ServerSession>> Create(std::string name = "Unknown"); + + /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an + /// object handle. + ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context); + + /// When set to True, converts the session to a domain at the end of the command + bool convert_to_domain{}; }; -/** - * Performs command buffer translation for an HLE IPC request. - * The command buffer from the ServerSession thread's TLS is copied into a - * buffer and all descriptors in the buffer are processed. - * TODO(Subv): Implement this function, currently we do not support multiple processes running at - * once, but once that is implemented we'll need to properly translate all descriptors - * in the command buffer. - */ -ResultCode TranslateHLERequest(ServerSession* server_session); -} +} // namespace Kernel diff --git a/src/core/hle/kernel/session.cpp b/src/core/hle/kernel/session.cpp index 8a2a7e3fd..642914744 100644 --- a/src/core/hle/kernel/session.cpp +++ b/src/core/hle/kernel/session.cpp @@ -9,4 +9,4 @@ namespace Kernel { Session::Session() {} Session::~Session() {} -} +} // namespace Kernel diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index 2cf319e99..e69b034a7 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -24,4 +24,4 @@ public: ServerSession* server = nullptr; ///< The server endpoint of the session. SharedPtr<ClientPort> port; ///< The port that this session is associated with (optional). }; -} +} // namespace Kernel diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 7279366ec..88230bdd9 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" @@ -14,7 +15,7 @@ namespace Kernel { SharedMemory::SharedMemory() {} SharedMemory::~SharedMemory() {} -SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u32 size, +SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u64 size, MemoryPermission permissions, MemoryPermission other_permissions, VAddr address, MemoryRegion region, std::string name) { @@ -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,14 +107,7 @@ 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()); - return ERR_INVALID_COMBINATION; - } - - // Heap-backed memory blocks can not be mapped with other_permissions = DontCare - if (base_address != 0 && other_permissions == MemoryPermission::DontCare) { - LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, permissions don't match", + LOG_ERROR(Kernel, "cannot map id=%u, address=0x%lx name=%s, permissions don't match", GetObjectId(), address, name.c_str()); return ERR_INVALID_COMBINATION; } @@ -121,23 +115,18 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi // 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", + LOG_ERROR(Kernel, "cannot map id=%u, address=0x%lx name=%s, permissions don't match", GetObjectId(), address, name.c_str()); return ERR_WRONG_PERMISSION; } - // TODO(Subv): Check for the Shared Device Mem flag in the creator process. - /*if (was_created_with_shared_device_mem && address != 0) { - return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); - }*/ - // 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) { - if (address < Memory::HEAP_VADDR) { - LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, invalid address", + // 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%lx name=%s, invalid address", GetObjectId(), address, name.c_str()); return ERR_INVALID_ADDRESS; } @@ -154,10 +143,9 @@ 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( - Kernel, - "cannot map id=%u, target_address=0x%llx name=%s, error mapping to virtual memory", - GetObjectId(), target_address, name.c_str()); + LOG_ERROR(Kernel, + "cannot map id=%u, target_address=0x%lx name=%s, error mapping to virtual memory", + GetObjectId(), target_address, name.c_str()); return result.Code(); } diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 93a6f2182..86f818e90 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -39,7 +39,7 @@ public: * linear heap. * @param name Optional object name, used for debugging purposes. */ - static SharedPtr<SharedMemory> Create(SharedPtr<Process> owner_process, u32 size, + static SharedPtr<SharedMemory> Create(SharedPtr<Process> owner_process, u64 size, MemoryPermission permissions, MemoryPermission other_permissions, VAddr address = 0, MemoryRegion region = MemoryRegion::BASE, @@ -98,10 +98,10 @@ public: ResultCode Unmap(Process* target_process, VAddr address); /** - * Gets a pointer to the shared memory block - * @param offset Offset from the start of the shared memory block to get pointer - * @return Pointer to the shared memory block from the specified offset - */ + * Gets a pointer to the shared memory block + * @param offset Offset from the start of the shared memory block to get pointer + * @return Pointer to the shared memory block from the specified offset + */ u8* GetPointer(u32 offset = 0); /// Process that created this shared memory block. @@ -116,7 +116,7 @@ public: /// Offset into the backing block for this shared memory. size_t backing_block_offset; /// Size of the memory block. Page-aligned. - u32 size; + u64 size; /// Permission restrictions applied to the process which created the block. MemoryPermission permissions; /// Permission restrictions applied to other processes mapping the block. @@ -129,4 +129,4 @@ private: ~SharedMemory() override; }; -} // namespace +} // namespace Kernel diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 45da842ef..311ab4187 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -3,10 +3,12 @@ // Refer to the license.txt file included. #include <algorithm> +#include <cinttypes> #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" @@ -20,7 +22,6 @@ #include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/svc.h" #include "core/hle/kernel/svc_wrap.h" -#include "core/hle/kernel/sync_object.h" #include "core/hle/kernel/thread.h" #include "core/hle/lock.h" #include "core/hle/result.h" @@ -31,14 +32,14 @@ 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; + 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); + LOG_WARNING(Kernel_SVC, "(STUBBED) called, addr=0x%lx", addr); return RESULT_SUCCESS; } @@ -46,14 +47,14 @@ static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state 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); + 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); + 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 @@ -87,7 +88,7 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address /// Makes a blocking IPC call to an OS service. static ResultCode SendSyncRequest(Handle handle) { - SharedPtr<SyncObject> session = g_handle_table.Get<SyncObject>(handle); + SharedPtr<ClientSession> session = g_handle_table.Get<ClientSession>(handle); if (!session) { LOG_ERROR(Kernel_SVC, "called with invalid handle=0x%08X", handle); return ERR_INVALID_HANDLE; @@ -255,14 +256,16 @@ 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", + 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) { @@ -304,23 +307,23 @@ 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); - 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.GetAddressSpaceBaseAddr(); + *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; @@ -331,6 +334,9 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) case GetInfoType::TotalHeapUsage: *result = vm_manager.GetTotalHeapUsage(); break; + case GetInfoType::IsCurrentProcessBeingDebugged: + *result = 0; + break; case GetInfoType::RandomEntropy: *result = 0; break; @@ -341,13 +347,13 @@ 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"); @@ -387,7 +393,7 @@ 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; + SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit; if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) { return ERR_NOT_AUTHORIZED; } @@ -415,8 +421,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s "called, shared_memory_handle=0x%08X, addr=0x%llx, size=0x%llx, permissions=0x%08X", shared_memory_handle, addr, size, permissions); - SharedPtr<SharedMemory> shared_memory = - Kernel::g_handle_table.Get<SharedMemory>(shared_memory_handle); + SharedPtr<SharedMemory> shared_memory = g_handle_table.Get<SharedMemory>(shared_memory_handle); if (!shared_memory) { return ERR_INVALID_HANDLE; } @@ -431,7 +436,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s case MemoryPermission::WriteExecute: case MemoryPermission::ReadWriteExecute: case MemoryPermission::DontCare: - return shared_memory->Map(Kernel::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); @@ -440,6 +445,16 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s 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); + + SharedPtr<SharedMemory> shared_memory = g_handle_table.Get<SharedMemory>(shared_memory_handle); + + return shared_memory->Unmap(Core::CurrentProcess().get(), addr); +} + /// Query process memory static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/, Handle process_handle, u64 addr) { @@ -449,11 +464,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); @@ -473,16 +488,17 @@ static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAdd /// Exits the current process static void ExitProcess() { - LOG_INFO(Kernel_SVC, "Process %u exiting", g_current_process->process_id); + LOG_INFO(Kernel_SVC, "Process %u 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 = GetThreadList(); + auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList(); for (auto& thread : thread_list) { - if (thread->owner_process != g_current_process) + if (thread->owner_process != Core::CurrentProcess()) continue; if (thread == GetCurrentThread()) @@ -511,14 +527,14 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V return ERR_OUT_OF_RANGE; } - SharedPtr<ResourceLimit>& resource_limit = g_current_process->resource_limit; + SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit; if (resource_limit->GetMaxResourceValue(ResourceTypes::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); } @@ -540,14 +556,15 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V 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(); - 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", + 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); return RESULT_SUCCESS; @@ -581,7 +598,7 @@ static void SleepThread(s64 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 && !HaveReadyThreads()) + if (nanoseconds == 0 && !Core::System::GetInstance().Scheduler().HaveReadyThreads()) return; // Sleep current thread and check for next thread to schedule @@ -611,20 +628,29 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var mutex->name = Common::StringFromFormat("mutex-%llx", mutex_addr); } - ASSERT(mutex->GetOwnerHandle() == thread_handle); - 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, mutex_addr).Unwrap(); + condition_variable = ConditionVariable::Create(condition_variable_addr).Unwrap(); condition_variable->name = Common::StringFromFormat("condition-variable-%llx", condition_variable_addr); } - ASSERT(condition_variable->GetAvailableCount() == 0); - ASSERT(condition_variable->mutex_addr == mutex_addr); + 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; + } + + if (mutex->GetOwnerHandle()) { + // Release the mutex if the current thread is holding it + mutex->Release(thread.get()); + } auto wakeup_callback = [mutex, nano_seconds](ThreadWakeupReason reason, SharedPtr<Thread> thread, @@ -666,8 +692,6 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var CASCADE_CODE( WaitSynchronization1(condition_variable, thread.get(), nano_seconds, wakeup_callback)); - mutex->Release(thread.get()); - return RESULT_SUCCESS; } @@ -726,7 +750,7 @@ 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, + LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%lx, size=0x%lx, perms=%08X", addr, size, permissions); *handle = 0; return RESULT_SUCCESS; @@ -737,6 +761,29 @@ static ResultCode SetThreadCoreMask(u64, u64, u64) { 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); + auto sharedMemHandle = + SharedMemory::Create(g_handle_table.Get<Process>(KernelHandle::CurrentProcess), size, + static_cast<MemoryPermission>(local_permissions), + static_cast<MemoryPermission>(remote_permissions)); + + CASCADE_RESULT(*handle, g_handle_table.Create(sharedMemHandle)); + return RESULT_SUCCESS; +} + +static ResultCode ClearEvent(Handle handle) { + LOG_TRACE(Kernel_SVC, "called, event=0xX", handle); + + SharedPtr<Event> evt = g_handle_table.Get<Event>(handle); + if (evt == nullptr) + return ERR_INVALID_HANDLE; + evt->Clear(); + return RESULT_SUCCESS; +} + namespace { struct FunctionDef { using Func = void(); @@ -766,9 +813,9 @@ static const FunctionDef SVC_Table[] = { {0x0F, SvcWrap<SetThreadCoreMask>, "SetThreadCoreMask"}, {0x10, SvcWrap<GetCurrentProcessorNumber>, "GetCurrentProcessorNumber"}, {0x11, nullptr, "SignalEvent"}, - {0x12, nullptr, "ClearEvent"}, + {0x12, SvcWrap<ClearEvent>, "ClearEvent"}, {0x13, SvcWrap<MapSharedMemory>, "MapSharedMemory"}, - {0x14, nullptr, "UnmapSharedMemory"}, + {0x14, SvcWrap<UnmapSharedMemory>, "UnmapSharedMemory"}, {0x15, SvcWrap<CreateTransferMemory>, "CreateTransferMemory"}, {0x16, SvcWrap<CloseHandle>, "CloseHandle"}, {0x17, SvcWrap<ResetSignal>, "ResetSignal"}, @@ -828,7 +875,7 @@ static const FunctionDef SVC_Table[] = { {0x4D, nullptr, "SleepSystem"}, {0x4E, nullptr, "ReadWriteRegister"}, {0x4F, nullptr, "SetProcessActivity"}, - {0x50, nullptr, "CreateSharedMemory"}, + {0x50, SvcWrap<CreateSharedMemory>, "CreateSharedMemory"}, {0x51, nullptr, "MapTransferMemory"}, {0x52, nullptr, "UnmapTransferMemory"}, {0x53, nullptr, "CreateInterruptEvent"}, diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h index 42cc41da3..bc471d01e 100644 --- a/src/core/hle/kernel/svc.h +++ b/src/core/hle/kernel/svc.h @@ -14,7 +14,11 @@ struct MemoryInfo { u32 type; u32 attributes; u32 permission; + u32 device_refcount; + u32 ipc_refcount; + INSERT_PADDING_WORDS(1); }; +static_assert(sizeof(MemoryInfo) == 0x28, "MemoryInfo has incorrect size."); struct PageInfo { u64 flags; diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index fd7054bbd..b224f5e67 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -91,6 +91,11 @@ void SvcWrap() { FuncReturn(func((u32)PARAM(0), PARAM(1), PARAM(2), (u32)PARAM(3)).raw); } +template <ResultCode func(u32, u64, u64)> +void SvcWrap() { + FuncReturn(func((u32)PARAM(0), PARAM(1), PARAM(2)).raw); +} + template <ResultCode func(u32*, u64, u64, s64)> void SvcWrap() { u32 param_1 = 0; @@ -145,6 +150,15 @@ void SvcWrap() { FuncReturn(retval); } +template <ResultCode func(Handle*, u64, u32, u32)> +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); + FuncReturn(retval); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// // Function wrappers that return type u32 diff --git a/src/core/hle/kernel/sync_object.h b/src/core/hle/kernel/sync_object.h deleted file mode 100644 index f2befa2ea..000000000 --- a/src/core/hle/kernel/sync_object.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <boost/smart_ptr/intrusive_ptr.hpp> -#include "core/hle/kernel/kernel.h" -#include "core/hle/result.h" - -namespace Kernel { - -class Thread; - -/// Class that represents a Kernel object that svcSendSyncRequest can be called on -class SyncObject : public Object { -public: - /** - * Handle a sync request from the emulated application. - * @param thread Thread that initiated the request. - * @returns ResultCode from the operation. - */ - virtual ResultCode SendSyncRequest(SharedPtr<Thread> thread) = 0; -}; - -// Specialization of DynamicObjectCast for SyncObjects -template <> -inline SharedPtr<SyncObject> DynamicObjectCast<SyncObject>(SharedPtr<Object> object) { - if (object != nullptr && object->IsSyncable()) { - return boost::static_pointer_cast<SyncObject>(std::move(object)); - } - return nullptr; -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 111c496b9..145f50887 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <algorithm> +#include <cinttypes> #include <list> #include <vector> #include "common/assert.h" @@ -40,14 +41,6 @@ void Thread::Acquire(Thread* thread) { // us to simply use a pool index or similar. static Kernel::HandleTable wakeup_callback_handle_table; -// Lists all thread ids that aren't deleted/etc. -static std::vector<SharedPtr<Thread>> thread_list; - -// Lists only ready thread ids. -static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue; - -static SharedPtr<Thread> current_thread; - // The first available thread id at startup static u32 next_thread_id; @@ -62,20 +55,6 @@ inline static u32 const NewThreadId() { Thread::Thread() {} Thread::~Thread() {} -Thread* GetCurrentThread() { - return current_thread.get(); -} - -/** - * 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); @@ -85,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) { - ready_queue.remove(current_priority, this); + Core::System::GetInstance().Scheduler().UnscheduleThread(this, current_priority); } status = THREADSTATUS_DEAD; @@ -105,113 +84,7 @@ void Thread::Stop() { 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); -} - -Thread* ArbitrateHighestPriorityThread(u32 address) { - Thread* highest_priority_thread = nullptr; - u32 priority = THREADPRIO_LOWEST; - - // Iterate through threads, find highest priority thread that is waiting to be arbitrated... - for (auto& thread : thread_list) { - if (!CheckWait_AddressArbiter(thread.get(), address)) - continue; - - if (thread == nullptr) - continue; - - if (thread->current_priority <= priority) { - highest_priority_thread = thread.get(); - priority = thread->current_priority; - } - } - - // If a thread was arbitrated, resume it - if (nullptr != highest_priority_thread) { - highest_priority_thread->ResumeFromWait(); - } - - return highest_priority_thread; -} - -void ArbitrateAllThreads(u32 address) { - // Resume all threads found to be waiting on the address - for (auto& thread : thread_list) { - if (CheckWait_AddressArbiter(thread.get(), address)) - thread->ResumeFromWait(); - } -} - -/** - * Switches the CPU's active thread context to that of the specified thread - * @param new_thread The thread to switch to - */ -static void SwitchContext(Thread* new_thread) { - Thread* previous_thread = GetCurrentThread(); - - // Save context for previous thread - if (previous_thread) { - previous_thread->last_running_ticks = CoreTiming::GetTicks(); - Core::CPU().SaveContext(previous_thread->context); - - if (previous_thread->status == THREADSTATUS_RUNNING) { - // This is only the case when a reschedule is triggered without the current thread - // yielding execution (i.e. an event triggered, system core time-sliced, etc) - ready_queue.push_front(previous_thread->current_priority, previous_thread); - previous_thread->status = THREADSTATUS_READY; - } - } - - // Load context of new thread - if (new_thread) { - ASSERT_MSG(new_thread->status == THREADSTATUS_READY, - "Thread must be ready to become running."); - - // Cancel any outstanding wakeup events for this thread - CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); - - auto previous_process = Kernel::g_current_process; - - current_thread = new_thread; - - ready_queue.remove(new_thread->current_priority, 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::CPU().LoadContext(new_thread->context); - Core::CPU().SetTlsAddress(new_thread->GetTLSAddress()); - } else { - current_thread = nullptr; - // Note: We do not reset the current process and current page table when idling because - // technically we haven't changed processes, our threads are just paused. - } -} - -/** - * Pops and returns the next thread from the thread queue - * @return A pointer to the next ready thread - */ -static Thread* PopNextReadyThread() { - Thread* next; - Thread* thread = GetCurrentThread(); - - if (thread && thread->status == THREADSTATUS_RUNNING) { - // We have to do better than the current thread. - // This call returns null when that's not possible. - next = ready_queue.pop_first_better(thread->current_priority); - if (!next) { - // Otherwise just keep going with the current thread - next = thread; - } - } else { - next = ready_queue.pop_first(); - } - - return next; + Core::CurrentProcess()->tls_slots[tls_page].reset(tls_slot); } void WaitCurrentThread_Sleep() { @@ -219,17 +92,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(); - thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), - thread_list.end()); + Core::System::GetInstance().Scheduler().RemoveThread(thread); } /** @@ -247,7 +113,8 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { 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) @@ -281,8 +148,9 @@ void Thread::ResumeFromWait() { 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: break; case THREADSTATUS_READY: @@ -306,32 +174,12 @@ void Thread::ResumeFromWait() { wakeup_callback = nullptr; - ready_queue.push_back(current_priority, this); status = THREADSTATUS_READY; + Core::System::GetInstance().Scheduler().ScheduleThread(this, current_priority); Core::System::GetInstance().PrepareReschedule(); } /** - * Prints the thread queue for debugging purposes - */ -static void DebugThreadQueue() { - Thread* thread = GetCurrentThread(); - if (!thread) { - LOG_DEBUG(Kernel, "Current: NO CURRENT THREAD"); - } else { - LOG_DEBUG(Kernel, "0x%02X %u (current)", thread->current_priority, - GetCurrentThread()->GetObjectId()); - } - - for (auto& t : thread_list) { - u32 priority = ready_queue.contains(t.get()); - if (priority != -1) { - LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId()); - } - } -} - -/** * Finds a free location for the TLS section of a thread. * @param tls_slots The TLS page array of the thread's owner process. * Returns a tuple of (page, slot, alloc_needed) where: @@ -379,7 +227,7 @@ 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: %d", priority); + LOG_ERROR(Kernel_SVC, "Invalid thread priority: %u", priority); return ERR_OUT_OF_RANGE; } @@ -391,15 +239,14 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, // TODO(yuriks): Other checks, returning 0xD9001BEA if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) { - LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point); + LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %016" PRIx64, name.c_str(), entry_point); // TODO (bunnei): Find the correct error code to use here return ResultCode(-1); } SharedPtr<Thread> thread(new Thread); - thread_list.push_back(thread); - ready_queue.prepare(priority); + Core::System::GetInstance().Scheduler().AddThread(thread, priority); thread->thread_id = NewThreadId(); thread->status = THREADSTATUS_DORMANT; @@ -452,7 +299,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 @@ -470,12 +317,7 @@ 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."); - // If thread was ready, adjust queues - if (status == THREADSTATUS_READY) - ready_queue.move(this, current_priority, priority); - else - ready_queue.prepare(priority); - + Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority); nominal_priority = current_priority = priority; } @@ -489,22 +331,18 @@ void Thread::UpdatePriority() { } void Thread::BoostPriority(u32 priority) { - // If thread was ready, adjust queues - if (status == THREADSTATUS_READY) - ready_queue.move(this, current_priority, priority); - else - ready_queue.prepare(priority); + Core::System::GetInstance().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_VADDR_END, owner_process); SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); @@ -519,25 +357,6 @@ SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority, return thread; } -bool HaveReadyThreads() { - return ready_queue.get_first() != nullptr; -} - -void Reschedule() { - Thread* cur = GetCurrentThread(); - Thread* next = PopNextReadyThread(); - - if (cur && next) { - LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId()); - } else if (cur) { - LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId()); - } else if (next) { - LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId()); - } - - SwitchContext(next); -} - void Thread::SetWaitSynchronizationResult(ResultCode result) { context.cpu_registers[0] = result.raw; } @@ -560,25 +379,20 @@ VAddr Thread::GetCommandBufferAddress() const { //////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Gets the current thread + */ +Thread* GetCurrentThread() { + return Core::System::GetInstance().Scheduler().GetCurrentThread(); +} + void ThreadingInit() { ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); - - current_thread = nullptr; next_thread_id = 1; } void ThreadingShutdown() { - current_thread = nullptr; - - for (auto& t : thread_list) { - t->Stop(); - } - thread_list.clear(); - ready_queue.clear(); -} - -const std::vector<SharedPtr<Thread>>& GetThreadList() { - return thread_list; + Kernel::ClearProcessList(); } } // namespace Kernel diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 0a1ada27d..dbf47e269 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -38,8 +38,9 @@ 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_DORMANT, ///< Created but not yet made ready @@ -249,28 +250,6 @@ SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority, SharedPtr<Process> owner_process); /** - * Returns whether there are any threads that are ready to run. - */ -bool HaveReadyThreads(); - -/** - * Reschedules to the next available thread (call after current thread is suspended) - */ -void Reschedule(); - -/** - * Arbitrate the highest priority thread that is waiting - * @param address The address for which waiting threads should be arbitrated - */ -Thread* ArbitrateHighestPriorityThread(VAddr address); - -/** - * Arbitrate all threads currently waiting. - * @param address The address for which waiting threads should be arbitrated - */ -void ArbitrateAllThreads(VAddr address); - -/** * Gets the current thread */ Thread* GetCurrentThread(); @@ -301,9 +280,4 @@ void ThreadingInit(); */ void ThreadingShutdown(); -/** - * Get a const reference to the thread list for debug use - */ -const std::vector<SharedPtr<Thread>>& GetThreadList(); - } // namespace Kernel diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index a93a6c87a..8da745634 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -111,4 +111,4 @@ void TimersInit() { void TimersShutdown() {} -} // namespace +} // namespace Kernel diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h index 82552372d..82d19cefc 100644 --- a/src/core/hle/kernel/timer.h +++ b/src/core/hle/kernel/timer.h @@ -76,4 +76,4 @@ void TimersInit(); /// Tears down the timer variables void TimersShutdown(); -} // namespace +} // namespace Kernel diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index bf261699e..1c2f873aa 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -2,6 +2,7 @@ // 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" @@ -10,15 +11,33 @@ #include "core/hle/kernel/errors.h" #include "core/hle/kernel/vm_manager.h" #include "core/memory.h" +#include "core/memory_hook.h" #include "core/memory_setup.h" -#include "core/mmio.h" 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]; @@ -60,8 +79,8 @@ void VMManager::Reset() { vma_map.emplace(initial_vma.base, initial_vma); page_table.pointers.fill(nullptr); + page_table.special_regions.clear(); page_table.attributes.fill(Memory::PageType::Unmapped); - page_table.cached_res_count.fill(0); UpdatePageTableForVMA(initial_vma); } @@ -121,7 +140,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state, - Memory::MMIORegionPointer mmio_handler) { + Memory::MemoryHookPointer mmio_handler) { // This is the appropriately sized VMA that will turn into our allocation. CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size)); VirtualMemoryArea& final_vma = vma_handle->second; @@ -141,7 +160,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; @@ -165,6 +184,9 @@ ResultCode VMManager::UnmapRange(VAddr target, u64 size) { } ASSERT(FindVMA(target)->second.size >= size); + + Core::CPU().UnmapMemory(target, size); + return RESULT_SUCCESS; } @@ -206,7 +228,8 @@ void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) { void VMManager::LogLayout(Log::Level log_level) const { for (const auto& p : vma_map) { const VirtualMemoryArea& vma = p.second; - LOG_GENERIC(Log::Class::Kernel, log_level, "%08X - %08X size: %8X %c%c%c %s", vma.base, + LOG_GENERIC(Log::Class::Kernel, log_level, + "%016" PRIx64 " - %016" PRIx64 " size: %16" PRIx64 " %c%c%c %s", vma.base, vma.base + vma.size, vma.size, (u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-', (u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-', @@ -222,8 +245,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%8X", size); - ASSERT_MSG((base & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%08X", base); + 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); VMAIter vma_handle = StripIterConstness(FindVMA(base)); if (vma_handle == vma_map.end()) { @@ -258,8 +281,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%8X", size); - ASSERT_MSG((target & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%08X", target); + 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); VAddr target_end = target + size; ASSERT(target_end >= target); @@ -375,14 +398,4 @@ u64 VMManager::GetAddressSpaceSize() { return MAX_ADDRESS; } -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 7a7fee54a..4d66146f6 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -10,7 +10,7 @@ #include "common/common_types.h" #include "core/hle/result.h" #include "core/memory.h" -#include "core/mmio.h" +#include "core/memory_hook.h" namespace Kernel { @@ -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. @@ -81,7 +90,7 @@ struct VirtualMemoryArea { // Settings for type = MMIO /// Physical address of the register area this VMA maps to. PAddr paddr = 0; - Memory::MMIORegionPointer mmio_handler = nullptr; + Memory::MemoryHookPointer mmio_handler = nullptr; /// Tests if this area can be merged to the right with `next`. bool CanBeMergedWith(const VirtualMemoryArea& next) const; @@ -160,7 +169,7 @@ public: * @param mmio_handler The handler that will implement read and write for this MMIO region. */ ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state, - Memory::MMIORegionPointer mmio_handler); + Memory::MemoryHookPointer mmio_handler); /// Unmaps a range of addresses, splitting VMAs as necessary. ResultCode UnmapRange(VAddr target, u64 size); @@ -192,12 +201,6 @@ public: /// Gets the total address space address size, used by svcGetInfo u64 GetAddressSpaceSize(); - /// 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) |
