diff options
Diffstat (limited to 'src/core/hle')
179 files changed, 7583 insertions, 1972 deletions
diff --git a/src/core/hle/config_mem.cpp b/src/core/hle/config_mem.cpp index e386ccdc6..038af7909 100644 --- a/src/core/hle/config_mem.cpp +++ b/src/core/hle/config_mem.cpp @@ -28,4 +28,4 @@ void Init() { config_mem.firm_ctr_sdk_ver = 0x0000F297; } -} // namespace +} // namespace ConfigMem diff --git a/src/core/hle/config_mem.h b/src/core/hle/config_mem.h index 42fa6d789..1840d1760 100644 --- a/src/core/hle/config_mem.h +++ b/src/core/hle/config_mem.h @@ -53,4 +53,4 @@ extern ConfigMemDef config_mem; void Init(); -} // namespace +} // namespace ConfigMem diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index 88ba105e5..a6602e12c 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h @@ -91,6 +91,10 @@ struct BufferDescriptorX { address |= static_cast<VAddr>(address_bits_36_38) << 36; return address; } + + u64 Size() const { + return static_cast<u64>(size); + } }; static_assert(sizeof(BufferDescriptorX) == 8, "BufferDescriptorX size is incorrect"); @@ -133,6 +137,10 @@ struct BufferDescriptorC { address |= static_cast<VAddr>(address_bits_32_47) << 32; return address; } + + u64 Size() const { + return static_cast<u64>(size); + } }; static_assert(sizeof(BufferDescriptorC) == 8, "BufferDescriptorC size is incorrect"); @@ -143,6 +151,11 @@ struct DataPayloadHeader { static_assert(sizeof(DataPayloadHeader) == 8, "DataPayloadRequest size is incorrect"); struct DomainMessageHeader { + enum class CommandType : u32_le { + SendMessage = 1, + CloseVirtualHandle = 2, + }; + union { // Used when responding to an IPC request, Server -> Client. struct { @@ -153,7 +166,7 @@ struct DomainMessageHeader { // Used when performing an IPC request, Client -> Server. struct { union { - BitField<0, 8, u32_le> command; + BitField<0, 8, CommandType> command; BitField<16, 16, u32_le> size; }; u32_le object_id; diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 25530a3c8..3f87c4297 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -11,7 +11,6 @@ #include "core/hle/ipc.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.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/kernel.h" @@ -31,11 +30,6 @@ public: RequestHelperBase(Kernel::HLERequestContext& context) : context(&context), cmdbuf(context.CommandBuffer()) {} - void ValidateHeader() { - // DEBUG_ASSERT_MSG(index == TotalSize(), "Operations do not match the header (cmd 0x%x)", - // header.raw); - } - void Skip(unsigned size_in_words, bool set_to_null) { if (set_to_null) memset(cmdbuf + index, 0, size_in_words * sizeof(u32)); @@ -54,16 +48,36 @@ public: unsigned GetCurrentOffset() const { return static_cast<unsigned>(index); } + + void SetCurrentOffset(unsigned offset) { + index = static_cast<ptrdiff_t>(offset); + } }; -class RequestBuilder : public RequestHelperBase { +class ResponseBuilder : public RequestHelperBase { public: - RequestBuilder(u32* command_buffer) : RequestHelperBase(command_buffer) {} + ResponseBuilder(u32* command_buffer) : RequestHelperBase(command_buffer) {} + + u32 normal_params_size{}; + u32 num_handles_to_copy{}; + u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent + std::ptrdiff_t datapayload_index{}; + + /// Flags used for customizing the behavior of ResponseBuilder + enum class Flags : u32 { + None = 0, + /// Uses move handles to move objects in the response, even when in a domain. This is + /// required when PushMoveObjects is used. + AlwaysMoveHandles = 1, + }; + + ResponseBuilder(Kernel::HLERequestContext& context, u32 normal_params_size, + u32 num_handles_to_copy = 0, u32 num_objects_to_move = 0, + Flags flags = Flags::None) + + : RequestHelperBase(context), normal_params_size(normal_params_size), + num_handles_to_copy(num_handles_to_copy), num_objects_to_move(num_objects_to_move) { - RequestBuilder(Kernel::HLERequestContext& context, unsigned normal_params_size, - u32 num_handles_to_copy = 0, u32 num_handles_to_move = 0, - u32 num_domain_objects = 0) - : RequestHelperBase(context) { memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH); context.ClearIncomingObjects(); @@ -73,12 +87,19 @@ public: // The entire size of the raw data section in u32 units, including the 16 bytes of mandatory // padding. u32 raw_data_size = sizeof(IPC::DataPayloadHeader) / 4 + 4 + normal_params_size; - if (context.IsDomain()) { - raw_data_size += sizeof(DomainMessageHeader) / 4 + num_domain_objects; + + u32 num_handles_to_move{}; + u32 num_domain_objects{}; + const bool always_move_handles{ + (static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0}; + if (!context.Session()->IsDomain() || always_move_handles) { + num_handles_to_move = num_objects_to_move; } else { - // If we're not in a domain, turn the domain object parameters into move handles. - num_handles_to_move += num_domain_objects; - num_domain_objects = 0; + num_domain_objects = num_objects_to_move; + } + + if (context.Session()->IsDomain()) { + raw_data_size += sizeof(DomainMessageHeader) / 4 + num_domain_objects; } header.data_size.Assign(raw_data_size); @@ -97,7 +118,8 @@ public: AlignWithPadding(); - if (context.IsDomain()) { + const bool request_has_domain_header{context.GetDomainMessageHeader() != nullptr}; + if (context.Session()->IsDomain() && request_has_domain_header) { IPC::DomainMessageHeader domain_header{}; domain_header.num_objects = num_domain_objects; PushRaw(domain_header); @@ -106,23 +128,43 @@ public: IPC::DataPayloadHeader data_payload_header{}; data_payload_header.magic = Common::MakeMagic('S', 'F', 'C', 'O'); PushRaw(data_payload_header); + + datapayload_index = index; } - template <class T, class... Args> - void PushIpcInterface(Args&&... args) { - auto iface = std::make_shared<T>(std::forward<Args>(args)...); - if (context->IsDomain()) { + template <class T> + void PushIpcInterface(std::shared_ptr<T> iface) { + if (context->Session()->IsDomain()) { context->AddDomainObject(std::move(iface)); } else { - auto port = iface->CreatePort(); - auto session = port->Connect(); - ASSERT(session.Succeeded()); - context->AddMoveObject(std::move(session).Unwrap()); + auto sessions = Kernel::ServerSession::CreateSessionPair(iface->GetServiceName()); + auto server = std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions); + auto client = std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions); + iface->ClientConnected(server); + context->AddMoveObject(std::move(client)); } } + template <class T, class... Args> + void PushIpcInterface(Args&&... args) { + PushIpcInterface<T>(std::make_shared<T>(std::forward<Args>(args)...)); + } + + void ValidateHeader() { + const size_t num_domain_objects = context->NumDomainObjects(); + const size_t num_move_objects = context->NumMoveObjects(); + ASSERT_MSG(!num_domain_objects || !num_move_objects, + "cannot move normal handles and domain objects"); + ASSERT_MSG((index - datapayload_index) == normal_params_size, + "normal_params_size value is incorrect"); + ASSERT_MSG((num_domain_objects + num_move_objects) == num_objects_to_move, + "num_objects_to_move value is incorrect"); + ASSERT_MSG(context->NumCopyObjects() == num_handles_to_copy, + "num_handles_to_copy value is incorrect"); + } + // Validate on destruction, as there shouldn't be any case where we don't want it - ~RequestBuilder() { + ~ResponseBuilder() { ValidateHeader(); } @@ -150,52 +192,52 @@ public: /// Push /// template <> -inline void RequestBuilder::Push(u32 value) { +inline void ResponseBuilder::Push(u32 value) { cmdbuf[index++] = value; } template <typename T> -void RequestBuilder::PushRaw(const T& value) { +void ResponseBuilder::PushRaw(const T& value) { std::memcpy(cmdbuf + index, &value, sizeof(T)); index += (sizeof(T) + 3) / 4; // round up to word length } template <> -inline void RequestBuilder::Push(ResultCode value) { +inline void ResponseBuilder::Push(ResultCode value) { // Result codes are actually 64-bit in the IPC buffer, but only the high part is discarded. Push(value.raw); Push<u32>(0); } template <> -inline void RequestBuilder::Push(u8 value) { +inline void ResponseBuilder::Push(u8 value) { PushRaw(value); } template <> -inline void RequestBuilder::Push(u16 value) { +inline void ResponseBuilder::Push(u16 value) { PushRaw(value); } template <> -inline void RequestBuilder::Push(u64 value) { +inline void ResponseBuilder::Push(u64 value) { Push(static_cast<u32>(value)); Push(static_cast<u32>(value >> 32)); } template <> -inline void RequestBuilder::Push(bool value) { +inline void ResponseBuilder::Push(bool value) { Push(static_cast<u8>(value)); } template <typename First, typename... Other> -void RequestBuilder::Push(const First& first_value, const Other&... other_values) { +void ResponseBuilder::Push(const First& first_value, const Other&... other_values) { Push(first_value); Push(other_values...); } template <typename... O> -inline void RequestBuilder::PushCopyObjects(Kernel::SharedPtr<O>... pointers) { +inline void ResponseBuilder::PushCopyObjects(Kernel::SharedPtr<O>... pointers) { auto objects = {pointers...}; for (auto& object : objects) { context->AddCopyObject(std::move(object)); @@ -203,7 +245,7 @@ inline void RequestBuilder::PushCopyObjects(Kernel::SharedPtr<O>... pointers) { } template <typename... O> -inline void RequestBuilder::PushMoveObjects(Kernel::SharedPtr<O>... pointers) { +inline void ResponseBuilder::PushMoveObjects(Kernel::SharedPtr<O>... pointers) { auto objects = {pointers...}; for (auto& object : objects) { context->AddMoveObject(std::move(object)); @@ -222,15 +264,10 @@ public: Skip(CommandIdSize, false); } - RequestBuilder MakeBuilder(u32 normal_params_size, u32 num_handles_to_copy, - u32 num_handles_to_move, u32 num_domain_objects, - bool validate_header = true) { - if (validate_header) { - ValidateHeader(); - } - - return {*context, normal_params_size, num_handles_to_copy, num_handles_to_move, - num_domain_objects}; + ResponseBuilder MakeBuilder(u32 normal_params_size, u32 num_handles_to_copy, + u32 num_handles_to_move, + ResponseBuilder::Flags flags = ResponseBuilder::Flags::None) { + return {*context, normal_params_size, num_handles_to_copy, num_handles_to_move, flags}; } template <typename T> @@ -301,6 +338,11 @@ inline u64 RequestParser::Pop() { } template <> +inline s64 RequestParser::Pop() { + return static_cast<s64>(Pop<u64>()); +} + +template <> inline bool RequestParser::Pop() { return Pop<u8>() != 0; } 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 7ebb4b9c7..000000000 --- a/src/core/hle/kernel/domain.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#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}; - return request_handlers[object_id - 1]->HandleSyncRequest(context); - } - 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 afa09b404..bef4f15f5 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,26 +104,34 @@ 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) { + // CloseVirtualHandle command does not have SFC* or any data + return; + } if (incoming) { ASSERT(data_payload_header->magic == Common::MakeMagic('S', 'F', 'C', 'I')); @@ -108,7 +139,31 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { ASSERT(data_payload_header->magic == Common::MakeMagic('S', 'F', 'C', 'O')); } - data_payload_offset = rp.GetCurrentOffset(); + 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. } @@ -131,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); @@ -143,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(), @@ -173,19 +231,121 @@ 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()}; + const size_t buffer_size{GetWriteBufferSize()}; + if (size > buffer_size) { + LOG_CRITICAL(Core, "size (%016zx) is greater than buffer_size (%016zx)", size, buffer_size); + size = buffer_size; // TODO(bunnei): This needs to be HW tested + } + + if (is_buffer_b) { + Memory::WriteBlock(BufferDescriptorB()[0].Address(), buffer, size); + } 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..2cffec198 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,13 @@ 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, + .MapMemoryBlock(Memory::STACK_AREA_VADDR_END - stack_size, std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, - MemoryState::Heap) + MemoryState::Mapped) .Unwrap(); misc_memory_used += stack_size; memory_region->used += stack_size; @@ -155,9 +155,9 @@ void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { }; // Map CodeSet segments - MapSegment(module_->code, VMAPermission::ReadExecute, MemoryState::Code); - MapSegment(module_->rodata, VMAPermission::Read, MemoryState::Static); - MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::Static); + MapSegment(module_->code, VMAPermission::ReadExecute, MemoryState::CodeStatic); + MapSegment(module_->rodata, VMAPermission::Read, MemoryState::CodeMutable); + MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::CodeMutable); } VAddr Process::GetLinearHeapAreaAddress() const { @@ -184,6 +184,8 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per // Initialize heap heap_memory = std::make_shared<std::vector<u8>>(); heap_start = heap_end = target; + } else { + vm_manager.UnmapRange(heap_start, heap_end - heap_start); } // If necessary, expand backing vector to cover new heap extents. @@ -203,7 +205,7 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per size, MemoryState::Heap)); vm_manager.Reprotect(vma, perms); - heap_used += size; + heap_used = size; memory_region->used += size; return MakeResult<VAddr>(heap_end - size); @@ -290,7 +292,7 @@ ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { CASCADE_RESULT(auto new_vma, vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, - vma->second.meminfo_state)); + MemoryState::Mapped)); // Protect mirror with permissions from old region vm_manager.Reprotect(new_vma, vma->second.permissions); // Remove permissions from old region @@ -321,5 +323,4 @@ SharedPtr<Process> GetProcessById(u32 process_id) { return *itr; } -SharedPtr<Process> g_current_process; } // namespace Kernel diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index add98472f..68e77a4d1 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -56,7 +56,7 @@ class ResourceLimit; struct MemoryRegionInfo; struct CodeSet final : public Object { - static SharedPtr<CodeSet> Create(std::string name, u64 program_id); + static SharedPtr<CodeSet> Create(std::string name); std::string GetTypeName() const override { return "CodeSet"; @@ -72,8 +72,6 @@ struct CodeSet final : public Object { /// Name of the process std::string name; - /// Title ID corresponding to the process - u64 program_id; std::shared_ptr<std::vector<u8>> memory; @@ -113,6 +111,9 @@ public: static u32 next_process_id; + /// Title ID corresponding to the process + u64 program_id; + /// Resource limit descriptor for this process SharedPtr<ResourceLimit> resource_limit; @@ -202,5 +203,4 @@ void ClearProcessList(); /// Retrieves a process from the current list of processes. SharedPtr<Process> GetProcessById(u32 process_id); -extern SharedPtr<Process> g_current_process; } // namespace Kernel diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp index 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 088058ebc..171bbd956 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,18 +47,18 @@ 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 -static ResultCode ConnectToPort(Handle* out_handle, VAddr port_name_address) { +static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address) { if (!Memory::IsValidVirtualAddress(port_name_address)) return ERR_NOT_FOUND; @@ -87,7 +88,7 @@ static ResultCode ConnectToPort(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; @@ -253,8 +254,8 @@ static ResultCode CancelSynchronization(Handle thread_handle) { } /// Attempts to locks a mutex, creating it if it does not already exist -static ResultCode LockMutex(Handle holding_thread_handle, VAddr mutex_addr, - Handle requesting_thread_handle) { +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", @@ -264,6 +265,7 @@ static ResultCode LockMutex(Handle holding_thread_handle, VAddr mutex_addr, 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) { @@ -278,7 +280,7 @@ static ResultCode LockMutex(Handle holding_thread_handle, VAddr mutex_addr, } /// Unlock a mutex -static ResultCode UnlockMutex(VAddr mutex_addr) { +static ResultCode ArbitrateUnlock(VAddr mutex_addr) { LOG_TRACE(Kernel_SVC, "called mutex_addr=0x%llx", mutex_addr); SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr); @@ -305,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; @@ -332,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; @@ -342,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"); @@ -388,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; } @@ -416,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; } @@ -432,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); @@ -441,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) { @@ -450,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); @@ -474,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()) @@ -512,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); } @@ -541,7 +556,7 @@ 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; @@ -583,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 @@ -613,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, @@ -668,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; } @@ -728,14 +750,45 @@ 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; } -static ResultCode SetThreadCoreMask(u64, u64, u64) { - LOG_WARNING(Kernel_SVC, "(STUBBED) called"); +static ResultCode GetThreadCoreMask(Handle handle, u32* mask, u64* unknown) { + LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x%08X", handle); + *mask = 0x0; + *unknown = 0xf; + return RESULT_SUCCESS; +} + +static ResultCode SetThreadCoreMask(Handle handle, u32 mask, u64 unknown) { + LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x%08X, mask=0x%08X, unknown=0x%lx", handle, + mask, unknown); + 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; } @@ -764,24 +817,24 @@ static const FunctionDef SVC_Table[] = { {0x0B, SvcWrap<SleepThread>, "SleepThread"}, {0x0C, SvcWrap<GetThreadPriority>, "GetThreadPriority"}, {0x0D, SvcWrap<SetThreadPriority>, "SetThreadPriority"}, - {0x0E, nullptr, "GetThreadCoreMask"}, + {0x0E, SvcWrap<GetThreadCoreMask>, "GetThreadCoreMask"}, {0x0F, SvcWrap<SetThreadCoreMask>, "SetThreadCoreMask"}, {0x10, SvcWrap<GetCurrentProcessorNumber>, "GetCurrentProcessorNumber"}, {0x11, nullptr, "SignalEvent"}, - {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"}, {0x18, SvcWrap<WaitSynchronization>, "WaitSynchronization"}, {0x19, SvcWrap<CancelSynchronization>, "CancelSynchronization"}, - {0x1A, SvcWrap<LockMutex>, "LockMutex"}, - {0x1B, SvcWrap<UnlockMutex>, "UnlockMutex"}, + {0x1A, SvcWrap<ArbitrateLock>, "ArbitrateLock"}, + {0x1B, SvcWrap<ArbitrateUnlock>, "ArbitrateUnlock"}, {0x1C, SvcWrap<WaitProcessWideKeyAtomic>, "WaitProcessWideKeyAtomic"}, {0x1D, SvcWrap<SignalProcessWideKey>, "SignalProcessWideKey"}, {0x1E, SvcWrap<GetSystemTick>, "GetSystemTick"}, - {0x1F, SvcWrap<ConnectToPort>, "ConnectToPort"}, + {0x1F, SvcWrap<ConnectToNamedPort>, "ConnectToNamedPort"}, {0x20, nullptr, "SendSyncRequestLight"}, {0x21, SvcWrap<SendSyncRequest>, "SendSyncRequest"}, {0x22, nullptr, "SendSyncRequestWithUserBuffer"}, @@ -825,12 +878,12 @@ static const FunctionDef SVC_Table[] = { {0x48, nullptr, "Unknown"}, {0x49, nullptr, "Unknown"}, {0x4A, nullptr, "Unknown"}, - {0x4B, nullptr, "Unknown"}, - {0x4C, nullptr, "Unknown"}, + {0x4B, nullptr, "CreateJitMemory"}, + {0x4C, nullptr, "MapJitMemory"}, {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..5da4f5269 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -70,6 +70,21 @@ void SvcWrap() { FuncReturn(retval); } +template <ResultCode func(u32, u32, u64)> +void SvcWrap() { + FuncReturn(func((u32)(PARAM(0) & 0xFFFFFFFF), (u32)(PARAM(1) & 0xFFFFFFFF), PARAM(2)).raw); +} + +template <ResultCode func(u32, u32*, u64*)> +void SvcWrap() { + u32 param_1 = 0; + u64 param_2 = 0; + ResultCode retval = func((u32)(PARAM(2) & 0xFFFFFFFF), ¶m_1, ¶m_2); + Core::CPU().SetReg(1, param_1); + Core::CPU().SetReg(2, param_2); + FuncReturn(retval.raw); +} + template <ResultCode func(u64, u64, u32, u32)> void SvcWrap() { FuncReturn( @@ -91,6 +106,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 +165,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..f3a8aa4aa 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_AREA_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) diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 10ddc4feb..052f49979 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -19,6 +19,8 @@ enum class ErrorDescription : u32 { Success = 0, RemoteProcessDead = 301, + InvalidOffset = 6061, + InvalidLength = 6062, }; /** @@ -106,11 +108,11 @@ union ResultCode { } constexpr bool IsSuccess() const { - return is_error.ExtractValue(raw) == 0; + return raw == 0; } constexpr bool IsError() const { - return is_error.ExtractValue(raw) == 1; + return raw != 0; } }; @@ -198,6 +200,9 @@ public: } ResultVal& operator=(const ResultVal& o) { + if (this == &o) { + return *this; + } if (!empty()) { if (!o.empty()) { object = o.object; diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index 147f4e62e..52c3491d5 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp @@ -9,15 +9,113 @@ namespace Service { namespace Account { +using Uid = std::array<u64, 2>; +static constexpr Uid DEFAULT_USER_ID{0x10ull, 0x20ull}; + +class IProfile final : public ServiceFramework<IProfile> { +public: + IProfile() : ServiceFramework("IProfile") { + static const FunctionInfo functions[] = { + {1, &IProfile::GetBase, "GetBase"}, + }; + RegisterHandlers(functions); + } + +private: + void GetBase(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + ProfileBase profile_base{}; + IPC::ResponseBuilder rb{ctx, 16}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(profile_base); + } +}; + +class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { +public: + IManagerForApplication() : ServiceFramework("IManagerForApplication") { + static const FunctionInfo functions[] = { + {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"}, + {1, &IManagerForApplication::GetAccountId, "GetAccountId"}, + }; + RegisterHandlers(functions); + } + +private: + void CheckAvailability(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(true); // TODO: Check when this is supposed to return true and when not + } + + void GetAccountId(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(0x12345678ABCDEF); + } +}; + +void ACC_U0::GetUserExistence(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(true); // TODO: Check when this is supposed to return true and when not +} + +void ACC_U0::ListAllUsers(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + constexpr std::array<u128, 10> user_ids{DEFAULT_USER_ID}; + ctx.WriteBuffer(user_ids.data(), user_ids.size()); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void ACC_U0::ListOpenUsers(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + constexpr std::array<u128, 10> user_ids{DEFAULT_USER_ID}; + ctx.WriteBuffer(user_ids.data(), user_ids.size()); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void ACC_U0::GetProfile(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IProfile>(); + LOG_DEBUG(Service_ACC, "called"); +} + void ACC_U0::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestBuilder rb{ctx, 2}; + LOG_WARNING(Service_ACC, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void ACC_U0::GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IManagerForApplication>(); + LOG_DEBUG(Service_ACC, "called"); +} + +void ACC_U0::GetLastOpenedUser(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 6}; rb.Push(RESULT_SUCCESS); + rb.PushRaw(DEFAULT_USER_ID); } ACC_U0::ACC_U0() : ServiceFramework("acc:u0") { static const FunctionInfo functions[] = { + {1, &ACC_U0::GetUserExistence, "GetUserExistence"}, + {2, &ACC_U0::ListAllUsers, "ListAllUsers"}, + {3, &ACC_U0::ListOpenUsers, "ListOpenUsers"}, + {4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"}, + {5, &ACC_U0::GetProfile, "GetProfile"}, {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, + {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/acc/acc_u0.h b/src/core/hle/service/acc/acc_u0.h index ac243d5b8..222f37282 100644 --- a/src/core/hle/service/acc/acc_u0.h +++ b/src/core/hle/service/acc/acc_u0.h @@ -9,13 +9,31 @@ namespace Service { namespace Account { +// TODO: RE this structure +struct UserData { + INSERT_PADDING_BYTES(0x80); +}; +static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); + +// TODO: RE this structure +struct ProfileBase { + INSERT_PADDING_BYTES(0x38); +}; +static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase structure has incorrect size"); + class ACC_U0 final : public ServiceFramework<ACC_U0> { public: ACC_U0(); ~ACC_U0() = default; private: + void GetUserExistence(Kernel::HLERequestContext& ctx); + void ListAllUsers(Kernel::HLERequestContext& ctx); + void ListOpenUsers(Kernel::HLERequestContext& ctx); + void GetLastOpenedUser(Kernel::HLERequestContext& ctx); + void GetProfile(Kernel::HLERequestContext& ctx); void InitializeApplicationInfo(Kernel::HLERequestContext& ctx); + void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx); }; } // namespace Account diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index a761bea65..bab338205 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -2,14 +2,493 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cinttypes> +#include "core/file_sys/filesystem.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/event.h" #include "core/hle/service/am/am.h" +#include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" +#include "core/hle/service/apm/apm.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/nvflinger/nvflinger.h" +#include "core/settings.h" namespace Service { namespace AM { -void InstallInterfaces(SM::ServiceManager& service_manager) { - std::make_shared<AppletOE>()->InstallAsService(service_manager); +IWindowController::IWindowController() : ServiceFramework("IWindowController") { + static const FunctionInfo functions[] = { + {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, + {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, + }; + RegisterHandlers(functions); +} + +void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(0); +} + +void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +IAudioController::IAudioController() : ServiceFramework("IAudioController") { + static const FunctionInfo functions[] = { + {0, &IAudioController::SetExpectedMasterVolume, "SetExpectedMasterVolume"}, + {1, &IAudioController::GetMainAppletExpectedMasterVolume, + "GetMainAppletExpectedMasterVolume"}, + {2, &IAudioController::GetLibraryAppletExpectedMasterVolume, + "GetLibraryAppletExpectedMasterVolume"}, + {3, nullptr, "ChangeMainAppletMasterVolume"}, + {4, nullptr, "SetTransparentVolumeRate"}, + }; + RegisterHandlers(functions); +} + +void IAudioController::SetExpectedMasterVolume(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void IAudioController::GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(volume); +} + +void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(volume); +} + +IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {} + +IDebugFunctions::IDebugFunctions() : ServiceFramework("IDebugFunctions") {} + +ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) + : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) { + static const FunctionInfo functions[] = { + {1, &ISelfController::LockExit, "LockExit"}, + {2, &ISelfController::UnlockExit, "UnlockExit"}, + {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"}, + {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"}, + {11, &ISelfController::SetOperationModeChangedNotification, + "SetOperationModeChangedNotification"}, + {12, &ISelfController::SetPerformanceModeChangedNotification, + "SetPerformanceModeChangedNotification"}, + {13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"}, + {14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"}, + {16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"}, + {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, + }; + RegisterHandlers(functions); + + launchable_event = + Kernel::Event::Create(Kernel::ResetType::OneShot, "ISelfController:LaunchableEvent"); +} + +void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { + // Takes 3 input u8s with each field located immediately after the previous u8, these are + // bool flags. No output. + + IPC::RequestParser rp{ctx}; + + struct FocusHandlingModeParams { + u8 unknown0; + u8 unknown1; + u8 unknown2; + }; + auto flags = rp.PopRaw<FocusHandlingModeParams>(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ISelfController::SetRestartMessageEnabled(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + bool flag = rp.Pop<bool>(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called flag=%u", static_cast<u32>(flag)); +} + +void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + bool flag = rp.Pop<bool>(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called flag=%u", static_cast<u32>(flag)); +} + +void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) { + // Takes 3 input u8s with each field located immediately after the previous u8, these are + // bool flags. No output. + IPC::RequestParser rp{ctx}; + + bool enabled = rp.Pop<bool>(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called enabled=%u", static_cast<u32>(enabled)); +} + +void ISelfController::LockExit(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) { + launchable_event->Signal(); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(launchable_event); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) { + // TODO(Subv): Find out how AM determines the display to use, for now just create the layer + // in the Default display. + u64 display_id = nvflinger->OpenDisplay("Default"); + u64 layer_id = nvflinger->CreateLayer(display_id); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(layer_id); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") { + static const FunctionInfo functions[] = { + {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, + {1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"}, + {5, &ICommonStateGetter::GetOperationMode, "GetOperationMode"}, + {6, &ICommonStateGetter::GetPerformanceMode, "GetPerformanceMode"}, + {9, &ICommonStateGetter::GetCurrentFocusState, "GetCurrentFocusState"}, + }; + RegisterHandlers(functions); + + event = Kernel::Event::Create(Kernel::ResetType::OneShot, "ICommonStateGetter:Event"); +} + +void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) { + event->Signal(); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(event); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(15); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast<u8>(FocusState::InFocus)); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { + const bool use_docked_mode{Settings::values.use_docked_mode}; + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast<u8>(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld)); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) { + const bool use_docked_mode{Settings::values.use_docked_mode}; + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast<u32>(use_docked_mode ? APM::PerformanceMode::Docked + : APM::PerformanceMode::Handheld)); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { +public: + explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") { + static const FunctionInfo functions[] = { + {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, + {1, nullptr, "IsCompleted"}, + {10, nullptr, "Start"}, + {20, nullptr, "RequestExit"}, + {25, nullptr, "Terminate"}, + {30, nullptr, "GetResult"}, + {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"}, + {100, nullptr, "PushInData"}, + {101, nullptr, "PopOutData"}, + {102, nullptr, "PushExtraStorage"}, + {103, nullptr, "PushInteractiveInData"}, + {104, nullptr, "PopInteractiveOutData"}, + {105, nullptr, "GetPopOutDataEvent"}, + {106, nullptr, "GetPopInteractiveOutDataEvent"}, + {120, nullptr, "NeedsToExitProcess"}, + {120, nullptr, "GetLibraryAppletInfo"}, + {150, nullptr, "RequestForAppletToGetForeground"}, + {160, nullptr, "GetIndirectLayerConsumerHandle"}, + }; + RegisterHandlers(functions); + + state_changed_event = Kernel::Event::Create(Kernel::ResetType::OneShot, + "ILibraryAppletAccessor:StateChangedEvent"); + } + +private: + void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) { + state_changed_event->Signal(); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(state_changed_event); + + LOG_WARNING(Service_AM, "(STUBBED) called"); + } + + Kernel::SharedPtr<Kernel::Event> state_changed_event; +}; + +ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") { + static const FunctionInfo functions[] = { + {0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"}, + {1, nullptr, "TerminateAllLibraryApplets"}, + {2, nullptr, "AreAnyLibraryAppletsLeft"}, + {10, nullptr, "CreateStorage"}, + {11, nullptr, "CreateTransferMemoryStorage"}, + {12, nullptr, "CreateHandleStorage"}, + }; + RegisterHandlers(functions); +} + +void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<AM::ILibraryAppletAccessor>(); + + LOG_DEBUG(Service_AM, "called"); +} + +class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { +public: + explicit IStorageAccessor(std::vector<u8> buffer) + : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) { + static const FunctionInfo functions[] = { + {0, &IStorageAccessor::GetSize, "GetSize"}, + {11, &IStorageAccessor::Read, "Read"}, + }; + RegisterHandlers(functions); + } + +private: + std::vector<u8> buffer; + + void GetSize(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast<u64>(buffer.size())); + + LOG_DEBUG(Service_AM, "called"); + } + + void Read(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + u64 offset = rp.Pop<u64>(); + + const size_t size{ctx.GetWriteBufferSize()}; + + ASSERT(offset + size <= buffer.size()); + + ctx.WriteBuffer(buffer.data() + offset, size); + + IPC::ResponseBuilder rb{ctx, 2}; + + rb.Push(RESULT_SUCCESS); + + LOG_DEBUG(Service_AM, "called"); + } +}; + +class IStorage final : public ServiceFramework<IStorage> { +public: + explicit IStorage(std::vector<u8> buffer) + : ServiceFramework("IStorage"), buffer(std::move(buffer)) { + static const FunctionInfo functions[] = { + {0, &IStorage::Open, "Open"}, + }; + RegisterHandlers(functions); + } + +private: + std::vector<u8> buffer; + + void Open(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<AM::IStorageAccessor>(buffer); + + LOG_DEBUG(Service_AM, "called"); + } +}; + +IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { + static const FunctionInfo functions[] = { + {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"}, + {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"}, + {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"}, + {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, + {66, &IApplicationFunctions::InitializeGamePlayRecording, "InitializeGamePlayRecording"}, + {67, &IApplicationFunctions::SetGamePlayRecordingState, "SetGamePlayRecordingState"}, + {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, + }; + RegisterHandlers(functions); +} + +void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { + constexpr u8 data[0x88] = { + 0xca, 0x97, 0x94, 0xc7, // Magic + 1, 0, 0, 0, // IsAccountSelected (bool) + 1, 0, 0, 0, // User Id (word 0) + 0, 0, 0, 0, // User Id (word 1) + 0, 0, 0, 0, // User Id (word 2) + 0, 0, 0, 0 // User Id (word 3) + }; + + std::vector<u8> buffer(data, data + sizeof(data)); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<AM::IStorage>(buffer); + + LOG_DEBUG(Service_AM, "called"); +} + +void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + u128 uid = rp.PopRaw<u128>(); + + LOG_WARNING(Service, "(STUBBED) called uid = %016" PRIX64 "%016" PRIX64, uid[1], uid[0]); + + IPC::ResponseBuilder rb{ctx, 4}; + + FileSys::Path unused; + auto savedata = FileSystem::OpenFileSystem(FileSystem::Type::SaveData, unused); + if (savedata.Failed()) { + // Create the save data and return an error indicating that the operation was performed. + FileSystem::FormatFileSystem(FileSystem::Type::SaveData); + // TODO(Subv): Find out the correct error code for this. + rb.Push(ResultCode(ErrorModule::FS, 40)); + } else { + rb.Push(RESULT_SUCCESS); + } + + rb.Push<u64>(0); +} + +void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) { + // Takes an input u32 Result, no output. + // For example, in some cases official apps use this with error 0x2A2 then uses svcBreak. + + IPC::RequestParser rp{ctx}; + u32 result = rp.Pop<u32>(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called, result=0x%08X", result); +} + +void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(SystemLanguage::English); + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void IApplicationFunctions::InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void IApplicationFunctions::SetGamePlayRecordingState(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void IApplicationFunctions::NotifyRunning(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u8>(0); // Unknown, seems to be ignored by official processes + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void InstallInterfaces(SM::ServiceManager& service_manager, + std::shared_ptr<NVFlinger::NVFlinger> nvflinger) { + std::make_shared<AppletAE>(nvflinger)->InstallAsService(service_manager); + std::make_shared<AppletOE>(nvflinger)->InstallAsService(service_manager); } } // namespace AM diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 63d86cd41..27dbd8c95 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -4,13 +4,126 @@ #pragma once +#include <memory> #include "core/hle/service/service.h" +namespace Kernel { +class Event; +} + namespace Service { +namespace NVFlinger { +class NVFlinger; +} + namespace AM { +// TODO: Add more languages +enum SystemLanguage { + Japanese = 0, + English = 1, +}; + +class IWindowController final : public ServiceFramework<IWindowController> { +public: + IWindowController(); + +private: + void GetAppletResourceUserId(Kernel::HLERequestContext& ctx); + void AcquireForegroundRights(Kernel::HLERequestContext& ctx); +}; + +class IAudioController final : public ServiceFramework<IAudioController> { +public: + IAudioController(); + +private: + void SetExpectedMasterVolume(Kernel::HLERequestContext& ctx); + void GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx); + void GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx); + + u32 volume{100}; +}; + +class IDisplayController final : public ServiceFramework<IDisplayController> { +public: + IDisplayController(); +}; + +class IDebugFunctions final : public ServiceFramework<IDebugFunctions> { +public: + IDebugFunctions(); +}; + +class ISelfController final : public ServiceFramework<ISelfController> { +public: + ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); + +private: + void SetFocusHandlingMode(Kernel::HLERequestContext& ctx); + void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx); + void SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx); + void SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx); + void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx); + void LockExit(Kernel::HLERequestContext& ctx); + void UnlockExit(Kernel::HLERequestContext& ctx); + void GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx); + void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx); + void SetScreenShotPermission(Kernel::HLERequestContext& ctx); + + std::shared_ptr<NVFlinger::NVFlinger> nvflinger; + Kernel::SharedPtr<Kernel::Event> launchable_event; +}; + +class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { +public: + ICommonStateGetter(); + +private: + enum class FocusState : u8 { + InFocus = 1, + NotInFocus = 2, + }; + + enum class OperationMode : u8 { + Handheld = 0, + Docked = 1, + }; + + void GetEventHandle(Kernel::HLERequestContext& ctx); + void ReceiveMessage(Kernel::HLERequestContext& ctx); + void GetCurrentFocusState(Kernel::HLERequestContext& ctx); + void GetOperationMode(Kernel::HLERequestContext& ctx); + void GetPerformanceMode(Kernel::HLERequestContext& ctx); + + Kernel::SharedPtr<Kernel::Event> event; +}; + +class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { +public: + ILibraryAppletCreator(); + +private: + void CreateLibraryApplet(Kernel::HLERequestContext& ctx); +}; + +class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { +public: + IApplicationFunctions(); + +private: + void PopLaunchParameter(Kernel::HLERequestContext& ctx); + void EnsureSaveData(Kernel::HLERequestContext& ctx); + void SetTerminateResult(Kernel::HLERequestContext& ctx); + void GetDesiredLanguage(Kernel::HLERequestContext& ctx); + void InitializeGamePlayRecording(Kernel::HLERequestContext& ctx); + void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx); + void NotifyRunning(Kernel::HLERequestContext& ctx); +}; + /// Registers all AM services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, + std::shared_ptr<NVFlinger::NVFlinger> nvflinger); } // namespace AM } // namespace Service diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp new file mode 100644 index 000000000..0e51caa70 --- /dev/null +++ b/src/core/hle/service/am/applet_ae.cpp @@ -0,0 +1,112 @@ +// 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/service/am/am.h" +#include "core/hle/service/am/applet_ae.h" +#include "core/hle/service/nvflinger/nvflinger.h" + +namespace Service { +namespace AM { + +class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> { +public: + ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) + : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)) { + static const FunctionInfo functions[] = { + {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, + {1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"}, + {2, &ILibraryAppletProxy::GetWindowController, "GetWindowController"}, + {3, &ILibraryAppletProxy::GetAudioController, "GetAudioController"}, + {4, &ILibraryAppletProxy::GetDisplayController, "GetDisplayController"}, + {11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"}, + {20, &ILibraryAppletProxy::GetApplicationFunctions, "GetApplicationFunctions"}, + {1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, + }; + RegisterHandlers(functions); + } + +private: + void GetCommonStateGetter(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ICommonStateGetter>(); + LOG_DEBUG(Service_AM, "called"); + } + + void GetSelfController(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISelfController>(nvflinger); + LOG_DEBUG(Service_AM, "called"); + } + + void GetWindowController(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IWindowController>(); + LOG_DEBUG(Service_AM, "called"); + } + + void GetAudioController(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IAudioController>(); + LOG_DEBUG(Service_AM, "called"); + } + + void GetDisplayController(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IDisplayController>(); + LOG_DEBUG(Service_AM, "called"); + } + + void GetDebugFunctions(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IDebugFunctions>(); + LOG_DEBUG(Service_AM, "called"); + } + + void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ILibraryAppletCreator>(); + LOG_DEBUG(Service_AM, "called"); + } + + void GetApplicationFunctions(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IApplicationFunctions>(); + LOG_DEBUG(Service_AM, "called"); + } + + std::shared_ptr<NVFlinger::NVFlinger> nvflinger; +}; + +void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger); + LOG_DEBUG(Service_AM, "called"); +} + +AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) + : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) { + static const FunctionInfo functions[] = { + {100, nullptr, "OpenSystemAppletProxy"}, + {200, &AppletAE::OpenLibraryAppletProxyOld, "OpenLibraryAppletProxyOld"}, + {201, nullptr, "OpenLibraryAppletProxy"}, + {300, nullptr, "OpenOverlayAppletProxy"}, + {350, nullptr, "OpenSystemApplicationProxy"}, + {400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"}, + }; + RegisterHandlers(functions); +} + +} // namespace AM +} // namespace Service diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h new file mode 100644 index 000000000..38fc428fb --- /dev/null +++ b/src/core/hle/service/am/applet_ae.h @@ -0,0 +1,30 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace NVFlinger { +class NVFlinger; +} + +namespace AM { + +class AppletAE final : public ServiceFramework<AppletAE> { +public: + AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); + ~AppletAE() = default; + +private: + void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx); + + std::shared_ptr<NVFlinger::NVFlinger> nvflinger; +}; + +} // namespace AM +} // namespace Service diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp index b360e7e5f..bdcebe689 100644 --- a/src/core/hle/service/am/applet_oe.cpp +++ b/src/core/hle/service/am/applet_oe.cpp @@ -4,260 +4,17 @@ #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" +#include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_oe.h" -#include "core/hle/service/apm/apm.h" +#include "core/hle/service/nvflinger/nvflinger.h" namespace Service { namespace AM { -class IWindowController final : public ServiceFramework<IWindowController> { -public: - IWindowController() : ServiceFramework("IWindowController") { - static const FunctionInfo functions[] = { - {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, - {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, - }; - RegisterHandlers(functions); - } - -private: - void GetAppletResourceUserId(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestBuilder rb{ctx, 4}; - rb.Push(RESULT_SUCCESS); - rb.Push<u64>(0); - } - - void AcquireForegroundRights(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - } -}; - -class IAudioController final : public ServiceFramework<IAudioController> { -public: - IAudioController() : ServiceFramework("IAudioController") {} -}; - -class IDisplayController final : public ServiceFramework<IDisplayController> { -public: - IDisplayController() : ServiceFramework("IDisplayController") {} -}; - -class IDebugFunctions final : public ServiceFramework<IDebugFunctions> { -public: - IDebugFunctions() : ServiceFramework("IDebugFunctions") {} -}; - -class ISelfController final : public ServiceFramework<ISelfController> { -public: - ISelfController() : ServiceFramework("ISelfController") { - static const FunctionInfo functions[] = { - {11, &ISelfController::SetOperationModeChangedNotification, - "SetOperationModeChangedNotification"}, - {12, &ISelfController::SetPerformanceModeChangedNotification, - "SetPerformanceModeChangedNotification"}, - {13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"}, - {14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"}, - {16, &ISelfController::SetOutOfFocusSuspendingEnabled, - "SetOutOfFocusSuspendingEnabled"}, - }; - RegisterHandlers(functions); - } - -private: - void SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { - // Takes 3 input u8s with each field located immediately after the previous u8, these are - // bool flags. No output. - - IPC::RequestParser rp{ctx}; - - struct FocusHandlingModeParams { - u8 unknown0; - u8 unknown1; - u8 unknown2; - }; - auto flags = rp.PopRaw<FocusHandlingModeParams>(); - - IPC::RequestBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service, "(STUBBED) called"); - } - - void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service, "(STUBBED) called"); - } - - void SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - bool flag = rp.Pop<bool>(); - - IPC::RequestBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service, "(STUBBED) called flag=%u", static_cast<u32>(flag)); - } - - void SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - bool flag = rp.Pop<bool>(); - - IPC::RequestBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service, "(STUBBED) called flag=%u", static_cast<u32>(flag)); - } - - void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) { - // Takes 3 input u8s with each field located immediately after the previous u8, these are - // bool flags. No output. - IPC::RequestParser rp{ctx}; - - bool enabled = rp.Pop<bool>(); - - IPC::RequestBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service, "(STUBBED) called enabled=%u", static_cast<u32>(enabled)); - } -}; - -class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { -public: - ICommonStateGetter() : ServiceFramework("ICommonStateGetter") { - static const FunctionInfo functions[] = { - {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, - {1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"}, - {5, &ICommonStateGetter::GetOperationMode, "GetOperationMode"}, - {6, &ICommonStateGetter::GetPerformanceMode, "GetPerformanceMode"}, - {9, &ICommonStateGetter::GetCurrentFocusState, "GetCurrentFocusState"}, - }; - RegisterHandlers(functions); - - event = Kernel::Event::Create(Kernel::ResetType::OneShot, "ICommonStateGetter:Event"); - } - -private: - enum class FocusState : u8 { - InFocus = 1, - NotInFocus = 2, - }; - - enum class OperationMode : u8 { - Handheld = 0, - Docked = 1, - }; - - void GetEventHandle(Kernel::HLERequestContext& ctx) { - event->Signal(); - - IPC::RequestBuilder rb{ctx, 2, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(event); - - LOG_WARNING(Service, "(STUBBED) called"); - } - - void ReceiveMessage(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 3}; - rb.Push(RESULT_SUCCESS); - rb.Push<u32>(15); - - LOG_WARNING(Service, "(STUBBED) called"); - } - - void GetCurrentFocusState(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 3}; - rb.Push(RESULT_SUCCESS); - rb.Push(static_cast<u8>(FocusState::InFocus)); - - LOG_WARNING(Service, "(STUBBED) called"); - } - - void GetOperationMode(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 3}; - rb.Push(RESULT_SUCCESS); - rb.Push(static_cast<u8>(OperationMode::Handheld)); - - LOG_WARNING(Service, "(STUBBED) called"); - } - - void GetPerformanceMode(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 3}; - rb.Push(RESULT_SUCCESS); - rb.Push(static_cast<u32>(APM::PerformanceMode::Handheld)); - - LOG_WARNING(Service, "(STUBBED) called"); - } - - Kernel::SharedPtr<Kernel::Event> event; -}; - -class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { -public: - IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { - static const FunctionInfo functions[] = { - {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, - {66, &IApplicationFunctions::InitializeGamePlayRecording, - "InitializeGamePlayRecording"}, - {67, &IApplicationFunctions::SetGamePlayRecordingState, "SetGamePlayRecordingState"}, - {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, - }; - RegisterHandlers(functions); - } - -private: - void SetTerminateResult(Kernel::HLERequestContext& ctx) { - // Takes an input u32 Result, no output. - // For example, in some cases official apps use this with error 0x2A2 then uses svcBreak. - - IPC::RequestParser rp{ctx}; - u32 result = rp.Pop<u32>(); - - IPC::RequestBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service, "(STUBBED) called, result=0x%08X", result); - } - - void InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service, "(STUBBED) called"); - } - - void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service, "(STUBBED) called"); - } - - void NotifyRunning(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 3}; - rb.Push(RESULT_SUCCESS); - rb.Push<u8>(0); // Unknown, seems to be ignored by official processes - - LOG_WARNING(Service, "(STUBBED) called"); - } -}; - -class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { -public: - ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") {} -}; - class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { public: - IApplicationProxy() : ServiceFramework("IApplicationProxy") { + IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) + : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) { static const FunctionInfo functions[] = { {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"}, {1, &IApplicationProxy::GetSelfController, "GetSelfController"}, @@ -273,70 +30,73 @@ public: private: void GetAudioController(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IAudioController>(); - LOG_DEBUG(Service, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetDisplayController(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IDisplayController>(); - LOG_DEBUG(Service, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetDebugFunctions(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IDebugFunctions>(); - LOG_DEBUG(Service, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetWindowController(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IWindowController>(); - LOG_DEBUG(Service, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetSelfController(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISelfController>(); - LOG_DEBUG(Service, "called"); + rb.PushIpcInterface<ISelfController>(nvflinger); + LOG_DEBUG(Service_AM, "called"); } void GetCommonStateGetter(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ICommonStateGetter>(); - LOG_DEBUG(Service, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ILibraryAppletCreator>(); - LOG_DEBUG(Service, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetApplicationFunctions(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IApplicationFunctions>(); - LOG_DEBUG(Service, "called"); + LOG_DEBUG(Service_AM, "called"); } + + std::shared_ptr<NVFlinger::NVFlinger> nvflinger; }; void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IApplicationProxy>(); - LOG_DEBUG(Service, "called"); + rb.PushIpcInterface<IApplicationProxy>(nvflinger); + LOG_DEBUG(Service_AM, "called"); } -AppletOE::AppletOE() : ServiceFramework("appletOE") { +AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) + : ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)) { static const FunctionInfo functions[] = { {0x00000000, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"}, }; diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h index beb75bf2a..d2ab44c67 100644 --- a/src/core/hle/service/am/applet_oe.h +++ b/src/core/hle/service/am/applet_oe.h @@ -4,19 +4,26 @@ #pragma once +#include <memory> #include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/service.h" namespace Service { +namespace NVFlinger { +class NVFlinger; +} + namespace AM { class AppletOE final : public ServiceFramework<AppletOE> { public: - AppletOE(); + AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); ~AppletOE() = default; private: void OpenApplicationProxy(Kernel::HLERequestContext& ctx); + + std::shared_ptr<NVFlinger::NVFlinger> nvflinger; }; } // namespace AM diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 260683201..8b55d2fcb 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -2,16 +2,44 @@ // 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/service/aoc/aoc_u.h" namespace Service { namespace AOC { +AOC_U::AOC_U() : ServiceFramework("aoc:u") { + static const FunctionInfo functions[] = { + {0, nullptr, "CountAddOnContentByApplicationId"}, + {1, nullptr, "ListAddOnContentByApplicationId"}, + {2, &AOC_U::CountAddOnContent, "CountAddOnContent"}, + {3, &AOC_U::ListAddOnContent, "ListAddOnContent"}, + {4, nullptr, "GetAddOnContentBaseIdByApplicationId"}, + {5, nullptr, "GetAddOnContentBaseId"}, + {6, nullptr, "PrepareAddOnContentByApplicationId"}, + {7, nullptr, "PrepareAddOnContent"}, + }; + RegisterHandlers(functions); +} + +void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(0); + LOG_WARNING(Service_AOC, "(STUBBED) called"); +} + +void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(0); + LOG_WARNING(Service_AOC, "(STUBBED) called"); +} + void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<AOC_U>()->InstallAsService(service_manager); } -AOC_U::AOC_U() : ServiceFramework("aoc:u") {} - } // namespace AOC } // namespace Service diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h index 0cbbf1e5d..6e0ba15a5 100644 --- a/src/core/hle/service/aoc/aoc_u.h +++ b/src/core/hle/service/aoc/aoc_u.h @@ -13,6 +13,10 @@ class AOC_U final : public ServiceFramework<AOC_U> { public: AOC_U(); ~AOC_U() = default; + +private: + void CountAddOnContent(Kernel::HLERequestContext& ctx); + void ListAddOnContent(Kernel::HLERequestContext& ctx); }; /// Registers all AOC services with the specified service manager. diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp index 66d94ff52..c4b09b435 100644 --- a/src/core/hle/service/apm/apm.cpp +++ b/src/core/hle/service/apm/apm.cpp @@ -5,61 +5,15 @@ #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/apm/apm.h" +#include "core/hle/service/apm/interface.h" namespace Service { namespace APM { void InstallInterfaces(SM::ServiceManager& service_manager) { - std::make_shared<APM>()->InstallAsService(service_manager); -} - -class ISession final : public ServiceFramework<ISession> { -public: - ISession() : ServiceFramework("ISession") { - static const FunctionInfo functions[] = { - {0, &ISession::SetPerformanceConfiguration, "SetPerformanceConfiguration"}, - {1, &ISession::GetPerformanceConfiguration, "GetPerformanceConfiguration"}, - }; - RegisterHandlers(functions); - } - -private: - void SetPerformanceConfiguration(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - auto mode = static_cast<PerformanceMode>(rp.Pop<u32>()); - u32 config = rp.Pop<u32>(); - - IPC::RequestBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service, "(STUBBED) called mode=%u config=%u", static_cast<u32>(mode), config); - } - - void GetPerformanceConfiguration(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - auto mode = static_cast<PerformanceMode>(rp.Pop<u32>()); - - IPC::RequestBuilder rb{ctx, 3}; - rb.Push(RESULT_SUCCESS); - rb.Push<u32>(0); // Performance configuration - - LOG_WARNING(Service, "(STUBBED) called mode=%u", static_cast<u32>(mode)); - } -}; - -APM::APM() : ServiceFramework("apm") { - static const FunctionInfo functions[] = { - {0x00000000, &APM::OpenSession, "OpenSession"}, {0x00000001, nullptr, "GetPerformanceMode"}, - }; - RegisterHandlers(functions); -} - -void APM::OpenSession(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISession>(); + auto module_ = std::make_shared<Module>(); + std::make_shared<APM>(module_, "apm")->InstallAsService(service_manager); + std::make_shared<APM>(module_, "apm:p")->InstallAsService(service_manager); } } // namespace APM diff --git a/src/core/hle/service/apm/apm.h b/src/core/hle/service/apm/apm.h index 90a1afbbc..070ab21f8 100644 --- a/src/core/hle/service/apm/apm.h +++ b/src/core/hle/service/apm/apm.h @@ -14,13 +14,10 @@ enum class PerformanceMode : u8 { Docked = 1, }; -class APM final : public ServiceFramework<APM> { +class Module final { public: - APM(); - ~APM() = default; - -private: - void OpenSession(Kernel::HLERequestContext& ctx); + Module() = default; + ~Module() = default; }; /// Registers all AM services with the specified service manager. diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp new file mode 100644 index 000000000..0179351ba --- /dev/null +++ b/src/core/hle/service/apm/interface.cpp @@ -0,0 +1,66 @@ +// 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/service/apm/apm.h" +#include "core/hle/service/apm/interface.h" + +namespace Service { +namespace APM { + +class ISession final : public ServiceFramework<ISession> { +public: + ISession() : ServiceFramework("ISession") { + static const FunctionInfo functions[] = { + {0, &ISession::SetPerformanceConfiguration, "SetPerformanceConfiguration"}, + {1, &ISession::GetPerformanceConfiguration, "GetPerformanceConfiguration"}, + }; + RegisterHandlers(functions); + } + +private: + void SetPerformanceConfiguration(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto mode = static_cast<PerformanceMode>(rp.Pop<u32>()); + u32 config = rp.Pop<u32>(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_APM, "(STUBBED) called mode=%u config=%u", static_cast<u32>(mode), + config); + } + + void GetPerformanceConfiguration(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto mode = static_cast<PerformanceMode>(rp.Pop<u32>()); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); // Performance configuration + + LOG_WARNING(Service_APM, "(STUBBED) called mode=%u", static_cast<u32>(mode)); + } +}; + +APM::APM(std::shared_ptr<Module> apm, const char* name) + : ServiceFramework(name), apm(std::move(apm)) { + static const FunctionInfo functions[] = { + {0, &APM::OpenSession, "OpenSession"}, + {1, nullptr, "GetPerformanceMode"}, + }; + RegisterHandlers(functions); +} + +void APM::OpenSession(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISession>(); +} + +} // namespace APM +} // namespace Service diff --git a/src/core/hle/service/apm/interface.h b/src/core/hle/service/apm/interface.h new file mode 100644 index 000000000..7d53721de --- /dev/null +++ b/src/core/hle/service/apm/interface.h @@ -0,0 +1,27 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace APM { + +class APM final : public ServiceFramework<APM> { +public: + APM(std::shared_ptr<Module> apm, const char* name); + ~APM() = default; + +private: + void OpenSession(Kernel::HLERequestContext& ctx); + + std::shared_ptr<Module> apm; +}; + +/// Registers all AM services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace APM +} // namespace Service diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp new file mode 100644 index 000000000..ee749fddd --- /dev/null +++ b/src/core/hle/service/audio/audin_u.cpp @@ -0,0 +1,41 @@ +// 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/hle_ipc.h" +#include "core/hle/service/audio/audin_u.h" + +namespace Service { +namespace Audio { + +class IAudioIn final : public ServiceFramework<IAudioIn> { +public: + IAudioIn() : ServiceFramework("IAudioIn") { + static const FunctionInfo functions[] = { + {0x0, nullptr, "GetAudioInState"}, + {0x1, nullptr, "StartAudioIn"}, + {0x2, nullptr, "StopAudioIn"}, + {0x3, nullptr, "AppendAudioInBuffer_1"}, + {0x4, nullptr, "RegisterBufferEvent"}, + {0x5, nullptr, "GetReleasedAudioInBuffer_1"}, + {0x6, nullptr, "ContainsAudioInBuffer"}, + {0x7, nullptr, "AppendAudioInBuffer_2"}, + {0x8, nullptr, "GetReleasedAudioInBuffer_2"}, + }; + RegisterHandlers(functions); + } + ~IAudioIn() = default; +}; + +AudInU::AudInU() : ServiceFramework("audin:u") { + static const FunctionInfo functions[] = { + {0x00000000, nullptr, "ListAudioIns"}, + {0x00000001, nullptr, "OpenAudioIn"}, + }; + RegisterHandlers(functions); +} + +} // namespace Audio +} // namespace Service diff --git a/src/core/hle/service/audio/audin_u.h b/src/core/hle/service/audio/audin_u.h new file mode 100644 index 000000000..2b8576756 --- /dev/null +++ b/src/core/hle/service/audio/audin_u.h @@ -0,0 +1,23 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Kernel { +class HLERequestContext; +} + +namespace Service { +namespace Audio { + +class AudInU final : public ServiceFramework<AudInU> { +public: + explicit AudInU(); + ~AudInU() = default; +}; + +} // namespace Audio +} // namespace Service diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp index 2b4c6c5d0..3f7fb44eb 100644 --- a/src/core/hle/service/audio/audio.cpp +++ b/src/core/hle/service/audio/audio.cpp @@ -2,14 +2,22 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/service/audio/audin_u.h" #include "core/hle/service/audio/audio.h" #include "core/hle/service/audio/audout_u.h" +#include "core/hle/service/audio/audrec_u.h" +#include "core/hle/service/audio/audren_u.h" +#include "core/hle/service/audio/codecctl.h" namespace Service { namespace Audio { void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<AudOutU>()->InstallAsService(service_manager); + std::make_shared<AudInU>()->InstallAsService(service_manager); + std::make_shared<AudRecU>()->InstallAsService(service_manager); + std::make_shared<AudRenU>()->InstallAsService(service_manager); + std::make_shared<CodecCtl>()->InstallAsService(service_manager); } } // namespace Audio diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index c028262c6..e873d768f 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -2,23 +2,195 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <vector> #include "common/logging/log.h" +#include "core/core_timing.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/event.h" +#include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/audio/audout_u.h" namespace Service { namespace Audio { +/// Switch sample rate frequency +constexpr u32 sample_rate{48000}; +/// TODO(st4rk): dynamic number of channels, as I think Switch has support +/// to more audio channels (probably when Docked I guess) +constexpr u32 audio_channels{2}; +/// TODO(st4rk): find a proper value for the audio_ticks +constexpr u64 audio_ticks{static_cast<u64>(BASE_CLOCK_RATE / 500)}; + +class IAudioOut final : public ServiceFramework<IAudioOut> { +public: + IAudioOut() : ServiceFramework("IAudioOut"), audio_out_state(AudioState::Stopped) { + static const FunctionInfo functions[] = { + {0x0, nullptr, "GetAudioOutState"}, + {0x1, &IAudioOut::StartAudioOut, "StartAudioOut"}, + {0x2, &IAudioOut::StopAudioOut, "StopAudioOut"}, + {0x3, &IAudioOut::AppendAudioOutBuffer_1, "AppendAudioOutBuffer_1"}, + {0x4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"}, + {0x5, &IAudioOut::GetReleasedAudioOutBuffer_1, "GetReleasedAudioOutBuffer_1"}, + {0x6, nullptr, "ContainsAudioOutBuffer"}, + {0x7, nullptr, "AppendAudioOutBuffer_2"}, + {0x8, nullptr, "GetReleasedAudioOutBuffer_2"}, + }; + RegisterHandlers(functions); + + // This is the event handle used to check if the audio buffer was released + buffer_event = + Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioOutBufferReleasedEvent"); + + // Register event callback to update the Audio Buffer + audio_event = CoreTiming::RegisterEvent( + "IAudioOut::UpdateAudioBuffersCallback", [this](u64 userdata, int cycles_late) { + UpdateAudioBuffersCallback(); + CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event); + }); + + // Start the audio event + CoreTiming::ScheduleEvent(audio_ticks, audio_event); + } + + ~IAudioOut() { + CoreTiming::UnscheduleEvent(audio_event, 0); + } + +private: + void StartAudioOut(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + + // Start audio + audio_out_state = AudioState::Started; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void StopAudioOut(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + + // Stop audio + audio_out_state = AudioState::Stopped; + + queue_keys.clear(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(buffer_event); + } + + void AppendAudioOutBuffer_1(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + + const u64 key{rp.Pop<u64>()}; + queue_keys.insert(queue_keys.begin(), key); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetReleasedAudioOutBuffer_1(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + + // TODO(st4rk): This is how libtransistor currently implements the + // GetReleasedAudioOutBuffer, it should return the key (a VAddr) to the app and this address + // is used to know which buffer should be filled with data and send again to the service + // through AppendAudioOutBuffer. Check if this is the proper way to do it. + u64 key{0}; + + if (queue_keys.size()) { + key = queue_keys.back(); + queue_keys.pop_back(); + } + + ctx.WriteBuffer(&key, sizeof(u64)); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + // TODO(st4rk): This might be the total of released buffers, needs to be verified on + // hardware + rb.Push<u32>(static_cast<u32>(queue_keys.size())); + } + + void UpdateAudioBuffersCallback() { + if (audio_out_state != AudioState::Started) { + return; + } + + if (queue_keys.empty()) { + return; + } + + buffer_event->Signal(); + } + + enum class AudioState : u32 { + Started, + Stopped, + }; + + /// This is used to trigger the audio event callback that is going to read the samples from the + /// audio_buffer list and enqueue the samples using the sink (audio_core). + CoreTiming::EventType* audio_event; + + /// This is the evend handle used to check if the audio buffer was released + Kernel::SharedPtr<Kernel::Event> buffer_event; + + /// (st4rk): This is just a temporary workaround for the future implementation. Libtransistor + /// uses the key as an address in the App, so we need to return when the + /// GetReleasedAudioOutBuffer_1 is called, otherwise we'll run in problems, because + /// libtransistor uses the key returned as an pointer. + std::vector<u64> queue_keys; + + AudioState audio_out_state; +}; + void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestBuilder rb{ctx, 2}; + LOG_WARNING(Service_Audio, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + + const std::string audio_interface = "AudioInterface"; + ctx.WriteBuffer(audio_interface.c_str(), audio_interface.size()); + + IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0); + + rb.Push(RESULT_SUCCESS); + // TODO(st4rk): We're currently returning only one audio interface (stringlist size). However, + // it's highly possible to have more than one interface (despite that libtransistor requires + // only one). + rb.Push<u32>(1); +} + +void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + + if (!audio_out_interface) { + audio_out_interface = std::make_shared<IAudioOut>(); + } + + IPC::ResponseBuilder rb{ctx, 6, 0, 1}; rb.Push(RESULT_SUCCESS); + rb.Push<u32>(sample_rate); + rb.Push<u32>(audio_channels); + rb.Push<u32>(static_cast<u32>(PcmFormat::Int16)); + rb.Push<u32>(0); // This field is unknown + rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface); } AudOutU::AudOutU() : ServiceFramework("audout:u") { - static const FunctionInfo functions[] = { - {0x00000000, &AudOutU::ListAudioOuts, "ListAudioOuts"}, - }; + static const FunctionInfo functions[] = {{0x00000000, &AudOutU::ListAudioOuts, "ListAudioOuts"}, + {0x00000001, &AudOutU::OpenAudioOut, "OpenAudioOut"}, + {0x00000002, nullptr, "Unknown2"}, + {0x00000003, nullptr, "Unknown3"}}; RegisterHandlers(functions); } diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h index 42680af94..7fbce2225 100644 --- a/src/core/hle/service/audio/audout_u.h +++ b/src/core/hle/service/audio/audout_u.h @@ -4,19 +4,37 @@ #pragma once -#include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/service.h" +namespace Kernel { +class HLERequestContext; +} + namespace Service { namespace Audio { +class IAudioOut; + class AudOutU final : public ServiceFramework<AudOutU> { public: AudOutU(); ~AudOutU() = default; private: + std::shared_ptr<IAudioOut> audio_out_interface; + void ListAudioOuts(Kernel::HLERequestContext& ctx); + void OpenAudioOut(Kernel::HLERequestContext& ctx); + + enum class PcmFormat : u32 { + Invalid = 0, + Int8 = 1, + Int16 = 2, + Int24 = 3, + Int32 = 4, + PcmFloat = 5, + Adpcm = 6, + }; }; } // namespace Audio diff --git a/src/core/hle/service/audio/audrec_u.cpp b/src/core/hle/service/audio/audrec_u.cpp new file mode 100644 index 000000000..f2626ec70 --- /dev/null +++ b/src/core/hle/service/audio/audrec_u.cpp @@ -0,0 +1,38 @@ +// 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/hle_ipc.h" +#include "core/hle/service/audio/audrec_u.h" + +namespace Service { +namespace Audio { + +class IFinalOutputRecorder final : public ServiceFramework<IFinalOutputRecorder> { +public: + IFinalOutputRecorder() : ServiceFramework("IFinalOutputRecorder") { + static const FunctionInfo functions[] = { + {0x0, nullptr, "GetFinalOutputRecorderState"}, + {0x1, nullptr, "StartFinalOutputRecorder"}, + {0x2, nullptr, "StopFinalOutputRecorder"}, + {0x3, nullptr, "AppendFinalOutputRecorderBuffer"}, + {0x4, nullptr, "RegisterBufferEvent"}, + {0x5, nullptr, "GetReleasedFinalOutputRecorderBuffer"}, + {0x6, nullptr, "ContainsFinalOutputRecorderBuffer"}, + }; + RegisterHandlers(functions); + } + ~IFinalOutputRecorder() = default; +}; + +AudRecU::AudRecU() : ServiceFramework("audrec:u") { + static const FunctionInfo functions[] = { + {0x00000000, nullptr, "OpenFinalOutputRecorder"}, + }; + RegisterHandlers(functions); +} + +} // namespace Audio +} // namespace Service diff --git a/src/core/hle/service/audio/audrec_u.h b/src/core/hle/service/audio/audrec_u.h new file mode 100644 index 000000000..c31e412c1 --- /dev/null +++ b/src/core/hle/service/audio/audrec_u.h @@ -0,0 +1,23 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Kernel { +class HLERequestContext; +} + +namespace Service { +namespace Audio { + +class AudRecU final : public ServiceFramework<AudRecU> { +public: + explicit AudRecU(); + ~AudRecU() = default; +}; + +} // namespace Audio +} // namespace Service diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp new file mode 100644 index 000000000..6d0461bbc --- /dev/null +++ b/src/core/hle/service/audio/audren_u.cpp @@ -0,0 +1,260 @@ +// 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/core_timing.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/event.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/audio/audren_u.h" + +namespace Service { +namespace Audio { + +/// TODO(bunnei): Find a proper value for the audio_ticks +constexpr u64 audio_ticks{static_cast<u64>(BASE_CLOCK_RATE / 200)}; + +class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { +public: + IAudioRenderer() : ServiceFramework("IAudioRenderer") { + static const FunctionInfo functions[] = { + {0x0, nullptr, "GetAudioRendererSampleRate"}, + {0x1, nullptr, "GetAudioRendererSampleCount"}, + {0x2, nullptr, "GetAudioRendererMixBufferCount"}, + {0x3, nullptr, "GetAudioRendererState"}, + {0x4, &IAudioRenderer::RequestUpdateAudioRenderer, "RequestUpdateAudioRenderer"}, + {0x5, &IAudioRenderer::StartAudioRenderer, "StartAudioRenderer"}, + {0x6, &IAudioRenderer::StopAudioRenderer, "StopAudioRenderer"}, + {0x7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, + {0x8, nullptr, "SetAudioRendererRenderingTimeLimit"}, + {0x9, nullptr, "GetAudioRendererRenderingTimeLimit"}, + }; + RegisterHandlers(functions); + + system_event = + Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioRenderer:SystemEvent"); + + // Register event callback to update the Audio Buffer + audio_event = CoreTiming::RegisterEvent( + "IAudioRenderer::UpdateAudioCallback", [this](u64 userdata, int cycles_late) { + UpdateAudioCallback(); + CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event); + }); + + // Start the audio event + CoreTiming::ScheduleEvent(audio_ticks, audio_event); + } + ~IAudioRenderer() { + CoreTiming::UnscheduleEvent(audio_event, 0); + } + +private: + void UpdateAudioCallback() { + system_event->Signal(); + } + + void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "%s", ctx.Description().c_str()); + AudioRendererResponseData response_data{}; + + response_data.section_0_size = + static_cast<u32>(response_data.state_entries.size() * sizeof(AudioRendererStateEntry)); + response_data.section_1_size = static_cast<u32>(response_data.section_1.size()); + response_data.section_2_size = static_cast<u32>(response_data.section_2.size()); + response_data.section_3_size = static_cast<u32>(response_data.section_3.size()); + response_data.section_4_size = static_cast<u32>(response_data.section_4.size()); + response_data.section_5_size = static_cast<u32>(response_data.section_5.size()); + response_data.total_size = sizeof(AudioRendererResponseData); + + for (unsigned i = 0; i < response_data.state_entries.size(); i++) { + // 4 = Busy and 5 = Ready? + response_data.state_entries[i].state = 5; + } + + ctx.WriteBuffer(&response_data, response_data.total_size); + + IPC::ResponseBuilder rb{ctx, 2}; + + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_Audio, "(STUBBED) called"); + } + + void StartAudioRenderer(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_Audio, "(STUBBED) called"); + } + + void StopAudioRenderer(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_Audio, "(STUBBED) called"); + } + + void QuerySystemEvent(Kernel::HLERequestContext& ctx) { + // system_event->Signal(); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(system_event); + + LOG_WARNING(Service_Audio, "(STUBBED) called"); + } + + struct AudioRendererStateEntry { + u32_le state; + u32_le unknown_4; + u32_le unknown_8; + u32_le unknown_c; + }; + static_assert(sizeof(AudioRendererStateEntry) == 0x10, + "AudioRendererStateEntry has wrong size"); + + struct AudioRendererResponseData { + u32_le unknown_0; + u32_le section_5_size; + u32_le section_0_size; + u32_le section_1_size; + u32_le unknown_10; + u32_le section_2_size; + u32_le unknown_18; + u32_le section_3_size; + u32_le section_4_size; + u32_le unknown_24; + u32_le unknown_28; + u32_le unknown_2c; + u32_le unknown_30; + u32_le unknown_34; + u32_le unknown_38; + u32_le total_size; + + std::array<AudioRendererStateEntry, 0x18e> state_entries; + + std::array<u8, 0x600> section_1; + std::array<u8, 0xe0> section_2; + std::array<u8, 0x20> section_3; + std::array<u8, 0x10> section_4; + std::array<u8, 0xb0> section_5; + }; + static_assert(sizeof(AudioRendererResponseData) == 0x20e0, + "AudioRendererResponseData has wrong size"); + + /// This is used to trigger the audio event callback. + CoreTiming::EventType* audio_event; + + Kernel::SharedPtr<Kernel::Event> system_event; +}; + +class IAudioDevice final : public ServiceFramework<IAudioDevice> { +public: + IAudioDevice() : ServiceFramework("IAudioDevice") { + static const FunctionInfo functions[] = { + {0x0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"}, + {0x1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"}, + {0x2, nullptr, "GetAudioDeviceOutputVolume"}, + {0x3, nullptr, "GetActiveAudioDeviceName"}, + {0x4, &IAudioDevice::QueryAudioDeviceSystemEvent, "QueryAudioDeviceSystemEvent"}, + {0x5, &IAudioDevice::GetActiveChannelCount, "GetActiveChannelCount"}, + {0x6, nullptr, "ListAudioDeviceNameAuto"}, + {0x7, nullptr, "SetAudioDeviceOutputVolumeAuto"}, + {0x8, nullptr, "GetAudioDeviceOutputVolumeAuto"}, + {0x10, nullptr, "GetActiveAudioDeviceNameAuto"}, + {0x11, nullptr, "QueryAudioDeviceInputEvent"}, + {0x12, nullptr, "QueryAudioDeviceOutputEvent"}}; + RegisterHandlers(functions); + + buffer_event = + Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioOutBufferReleasedEvent"); + } + +private: + void ListAudioDeviceName(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + + const std::string audio_interface = "AudioInterface"; + ctx.WriteBuffer(audio_interface.c_str(), audio_interface.size()); + + IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0); + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(1); + } + + void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + + IPC::RequestParser rp{ctx}; + f32 volume = static_cast<f32>(rp.Pop<u32>()); + + auto file_buffer = ctx.ReadBuffer(); + auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); + + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); + rb.Push(RESULT_SUCCESS); + } + + void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + + buffer_event->Signal(); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(buffer_event); + } + + void GetActiveChannelCount(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(1); + } + + Kernel::SharedPtr<Kernel::Event> buffer_event; + +}; // namespace Audio + +AudRenU::AudRenU() : ServiceFramework("audren:u") { + static const FunctionInfo functions[] = { + {0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"}, + {1, &AudRenU::GetAudioRendererWorkBufferSize, "GetAudioRendererWorkBufferSize"}, + {2, &AudRenU::GetAudioDevice, "GetAudioDevice"}, + }; + RegisterHandlers(functions); +} + +void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<Audio::IAudioRenderer>(); + + LOG_DEBUG(Service_Audio, "called"); +} + +void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(0x400); + + LOG_WARNING(Service_Audio, "(STUBBED) called"); +} + +void AudRenU::GetAudioDevice(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<Audio::IAudioDevice>(); + + LOG_DEBUG(Service_Audio, "called"); +} + +} // namespace Audio +} // namespace Service diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h new file mode 100644 index 000000000..f59d1627d --- /dev/null +++ b/src/core/hle/service/audio/audren_u.h @@ -0,0 +1,28 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Kernel { +class HLERequestContext; +} + +namespace Service { +namespace Audio { + +class AudRenU final : public ServiceFramework<AudRenU> { +public: + explicit AudRenU(); + ~AudRenU() = default; + +private: + void OpenAudioRenderer(Kernel::HLERequestContext& ctx); + void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx); + void GetAudioDevice(Kernel::HLERequestContext& ctx); +}; + +} // namespace Audio +} // namespace Service diff --git a/src/core/hle/service/audio/codecctl.cpp b/src/core/hle/service/audio/codecctl.cpp new file mode 100644 index 000000000..d2a7f4cd0 --- /dev/null +++ b/src/core/hle/service/audio/codecctl.cpp @@ -0,0 +1,33 @@ +// 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/hle_ipc.h" +#include "core/hle/service/audio/codecctl.h" + +namespace Service { +namespace Audio { + +CodecCtl::CodecCtl() : ServiceFramework("codecctl") { + static const FunctionInfo functions[] = { + {0x00000000, nullptr, "InitializeCodecController"}, + {0x00000001, nullptr, "FinalizeCodecController"}, + {0x00000002, nullptr, "SleepCodecController"}, + {0x00000003, nullptr, "WakeCodecController"}, + {0x00000004, nullptr, "SetCodecVolume"}, + {0x00000005, nullptr, "GetCodecVolumeMax"}, + {0x00000006, nullptr, "GetCodecVolumeMin"}, + {0x00000007, nullptr, "SetCodecActiveTarget"}, + {0x00000008, nullptr, "Unknown"}, + {0x00000009, nullptr, "BindCodecHeadphoneMicJackInterrupt"}, + {0x0000000A, nullptr, "IsCodecHeadphoneMicJackInserted"}, + {0x0000000B, nullptr, "ClearCodecHeadphoneMicJackInterrupt"}, + {0x0000000C, nullptr, "IsCodecDeviceRequested"}, + }; + RegisterHandlers(functions); +} + +} // namespace Audio +} // namespace Service diff --git a/src/core/hle/service/audio/codecctl.h b/src/core/hle/service/audio/codecctl.h new file mode 100644 index 000000000..1121ab0b1 --- /dev/null +++ b/src/core/hle/service/audio/codecctl.h @@ -0,0 +1,23 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Kernel { +class HLERequestContext; +} + +namespace Service { +namespace Audio { + +class CodecCtl final : public ServiceFramework<CodecCtl> { +public: + explicit CodecCtl(); + ~CodecCtl() = default; +}; + +} // namespace Audio +} // namespace Service diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp new file mode 100644 index 000000000..1a18e0051 --- /dev/null +++ b/src/core/hle/service/fatal/fatal.cpp @@ -0,0 +1,38 @@ +// 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/service/fatal/fatal.h" +#include "core/hle/service/fatal/fatal_p.h" +#include "core/hle/service/fatal/fatal_u.h" + +namespace Service { +namespace Fatal { + +Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) + : ServiceFramework(name), module(std::move(module)) {} + +void Module::Interface::FatalSimple(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + u32 error_code = rp.Pop<u32>(); + LOG_WARNING(Service_Fatal, "(STUBBED) called, error_code=0x%X", error_code); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void Module::Interface::TransitionToFatalError(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Fatal, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void InstallInterfaces(SM::ServiceManager& service_manager) { + auto module = std::make_shared<Module>(); + std::make_shared<Fatal_P>(module)->InstallAsService(service_manager); + std::make_shared<Fatal_U>(module)->InstallAsService(service_manager); +} + +} // namespace Fatal +} // namespace Service diff --git a/src/core/hle/service/fatal/fatal.h b/src/core/hle/service/fatal/fatal.h new file mode 100644 index 000000000..85272b4be --- /dev/null +++ b/src/core/hle/service/fatal/fatal.h @@ -0,0 +1,29 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Fatal { + +class Module final { +public: + class Interface : public ServiceFramework<Interface> { + public: + Interface(std::shared_ptr<Module> module, const char* name); + + void FatalSimple(Kernel::HLERequestContext& ctx); + void TransitionToFatalError(Kernel::HLERequestContext& ctx); + + protected: + std::shared_ptr<Module> module; + }; +}; + +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace Fatal +} // namespace Service diff --git a/src/core/hle/service/fatal/fatal_p.cpp b/src/core/hle/service/fatal/fatal_p.cpp new file mode 100644 index 000000000..ba194e340 --- /dev/null +++ b/src/core/hle/service/fatal/fatal_p.cpp @@ -0,0 +1,14 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/fatal/fatal_p.h" + +namespace Service { +namespace Fatal { + +Fatal_P::Fatal_P(std::shared_ptr<Module> module) + : Module::Interface(std::move(module), "fatal:p") {} + +} // namespace Fatal +} // namespace Service diff --git a/src/core/hle/service/fatal/fatal_p.h b/src/core/hle/service/fatal/fatal_p.h new file mode 100644 index 000000000..d77b24bc4 --- /dev/null +++ b/src/core/hle/service/fatal/fatal_p.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/fatal/fatal.h" + +namespace Service { +namespace Fatal { + +class Fatal_P final : public Module::Interface { +public: + explicit Fatal_P(std::shared_ptr<Module> module); +}; + +} // namespace Fatal +} // namespace Service diff --git a/src/core/hle/service/fatal/fatal_u.cpp b/src/core/hle/service/fatal/fatal_u.cpp new file mode 100644 index 000000000..065cc868d --- /dev/null +++ b/src/core/hle/service/fatal/fatal_u.cpp @@ -0,0 +1,19 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/fatal/fatal_u.h" + +namespace Service { +namespace Fatal { + +Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:u") { + static const FunctionInfo functions[] = { + {1, &Fatal_U::FatalSimple, "FatalSimple"}, + {2, &Fatal_U::TransitionToFatalError, "TransitionToFatalError"}, + }; + RegisterHandlers(functions); +} + +} // namespace Fatal +} // namespace Service diff --git a/src/core/hle/service/fatal/fatal_u.h b/src/core/hle/service/fatal/fatal_u.h new file mode 100644 index 000000000..22374755e --- /dev/null +++ b/src/core/hle/service/fatal/fatal_u.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/fatal/fatal.h" + +namespace Service { +namespace Fatal { + +class Fatal_U final : public Module::Interface { +public: + explicit Fatal_U(std::shared_ptr<Module> module); +}; + +} // namespace Fatal +} // namespace Service diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp new file mode 100644 index 000000000..945832e98 --- /dev/null +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -0,0 +1,79 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <boost/container/flat_map.hpp> +#include "common/file_util.h" +#include "core/file_sys/filesystem.h" +#include "core/file_sys/savedata_factory.h" +#include "core/file_sys/sdmc_factory.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/filesystem/fsp_srv.h" + +namespace Service { +namespace FileSystem { + +/** + * Map of registered file systems, identified by type. Once an file system is registered here, it + * is never removed until UnregisterFileSystems is called. + */ +static boost::container::flat_map<Type, std::unique_ptr<FileSys::FileSystemFactory>> filesystem_map; + +ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type) { + auto result = filesystem_map.emplace(type, std::move(factory)); + + bool inserted = result.second; + ASSERT_MSG(inserted, "Tried to register more than one system with same id code"); + + auto& filesystem = result.first->second; + LOG_DEBUG(Service_FS, "Registered file system %s with id code 0x%08X", + filesystem->GetName().c_str(), static_cast<u32>(type)); + return RESULT_SUCCESS; +} + +ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, + FileSys::Path& path) { + LOG_TRACE(Service_FS, "Opening FileSystem with type=%d", type); + + auto itr = filesystem_map.find(type); + if (itr == filesystem_map.end()) { + // TODO(bunnei): Find a better error code for this + return ResultCode(-1); + } + + return itr->second->Open(path); +} + +ResultCode FormatFileSystem(Type type) { + LOG_TRACE(Service_FS, "Formatting FileSystem with type=%d", type); + + auto itr = filesystem_map.find(type); + if (itr == filesystem_map.end()) { + // TODO(bunnei): Find a better error code for this + return ResultCode(-1); + } + + FileSys::Path unused; + return itr->second->Format(unused); +} + +void RegisterFileSystems() { + filesystem_map.clear(); + + std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX); + std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX); + + auto savedata = std::make_unique<FileSys::SaveData_Factory>(std::move(nand_directory)); + RegisterFileSystem(std::move(savedata), Type::SaveData); + + auto sdcard = std::make_unique<FileSys::SDMC_Factory>(std::move(sd_directory)); + RegisterFileSystem(std::move(sdcard), Type::SDMC); +} + +void InstallInterfaces(SM::ServiceManager& service_manager) { + RegisterFileSystems(); + std::make_shared<FSP_SRV>()->InstallAsService(service_manager); +} + +} // namespace FileSystem +} // namespace Service diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h new file mode 100644 index 000000000..56d26146e --- /dev/null +++ b/src/core/hle/service/filesystem/filesystem.h @@ -0,0 +1,59 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include "common/common_types.h" +#include "core/hle/result.h" + +namespace FileSys { +class FileSystemBackend; +class FileSystemFactory; +class Path; +} // namespace FileSys + +namespace Service { + +namespace SM { +class ServiceManager; +} // namespace SM + +namespace FileSystem { + +/// Supported FileSystem types +enum class Type { + RomFS = 1, + SaveData = 2, + SDMC = 3, +}; + +/** + * Registers a FileSystem, instances of which can later be opened using its IdCode. + * @param factory FileSystem backend interface to use + * @param type Type used to access this type of FileSystem + */ +ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type); + +/** + * Opens a file system + * @param type Type of the file system to open + * @param path Path to the file system, used with Binary paths + * @return FileSys::FileSystemBackend interface to the file system + */ +ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, + FileSys::Path& path); + +/** + * Formats a file system + * @param type Type of the file system to format + * @return ResultCode of the operation + */ +ResultCode FormatFileSystem(Type type); + +/// Registers all Filesystem services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace FileSystem +} // namespace Service diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp new file mode 100644 index 000000000..89fa70ae6 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -0,0 +1,464 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cinttypes> +#include "common/logging/log.h" +#include "core/core.h" +#include "core/file_sys/directory.h" +#include "core/file_sys/filesystem.h" +#include "core/file_sys/storage.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/client_port.h" +#include "core/hle/kernel/client_session.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/filesystem/fsp_srv.h" + +namespace Service { +namespace FileSystem { + +class IStorage final : public ServiceFramework<IStorage> { +public: + IStorage(std::unique_ptr<FileSys::StorageBackend>&& backend) + : ServiceFramework("IStorage"), backend(std::move(backend)) { + static const FunctionInfo functions[] = { + {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"}, + {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, + }; + RegisterHandlers(functions); + } + +private: + std::unique_ptr<FileSys::StorageBackend> backend; + + void Read(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const s64 offset = rp.Pop<s64>(); + const s64 length = rp.Pop<s64>(); + + LOG_DEBUG(Service_FS, "called, offset=0x%ld, length=0x%ld", offset, length); + + // Error checking + if (length < 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); + return; + } + if (offset < 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); + return; + } + + // Read the data from the Storage backend + std::vector<u8> output(length); + ResultVal<size_t> res = backend->Read(offset, length, output.data()); + if (res.Failed()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res.Code()); + return; + } + + // Write the data to memory + ctx.WriteBuffer(output); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } +}; + +class IFile final : public ServiceFramework<IFile> { +public: + explicit IFile(std::unique_ptr<FileSys::StorageBackend>&& backend) + : ServiceFramework("IFile"), backend(std::move(backend)) { + static const FunctionInfo functions[] = { + {0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, {2, nullptr, "Flush"}, + {3, &IFile::SetSize, "SetSize"}, {4, &IFile::GetSize, "GetSize"}, + }; + RegisterHandlers(functions); + } + +private: + std::unique_ptr<FileSys::StorageBackend> backend; + + void Read(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 unk = rp.Pop<u64>(); + const s64 offset = rp.Pop<s64>(); + const s64 length = rp.Pop<s64>(); + + LOG_DEBUG(Service_FS, "called, offset=0x%ld, length=0x%ld", offset, length); + + // Error checking + if (length < 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); + return; + } + if (offset < 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); + return; + } + + // Read the data from the Storage backend + std::vector<u8> output(length); + ResultVal<size_t> res = backend->Read(offset, length, output.data()); + if (res.Failed()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res.Code()); + return; + } + + // Write the data to memory + ctx.WriteBuffer(output); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast<u64>(*res)); + } + + void Write(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 unk = rp.Pop<u64>(); + const s64 offset = rp.Pop<s64>(); + const s64 length = rp.Pop<s64>(); + + LOG_DEBUG(Service_FS, "called, offset=0x%ld, length=0x%ld", offset, length); + + // Error checking + if (length < 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); + return; + } + if (offset < 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); + return; + } + + // Write the data to the Storage backend + std::vector<u8> data = ctx.ReadBuffer(); + ResultVal<size_t> res = backend->Write(offset, length, true, data.data()); + if (res.Failed()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res.Code()); + return; + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void SetSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 size = rp.Pop<u64>(); + backend->SetSize(size); + LOG_DEBUG(Service_FS, "called, size=%" PRIu64, size); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetSize(Kernel::HLERequestContext& ctx) { + const u64 size = backend->GetSize(); + LOG_DEBUG(Service_FS, "called, size=%" PRIu64, size); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(size); + } +}; + +class IDirectory final : public ServiceFramework<IDirectory> { +public: + explicit IDirectory(std::unique_ptr<FileSys::DirectoryBackend>&& backend) + : ServiceFramework("IDirectory"), backend(std::move(backend)) { + static const FunctionInfo functions[] = { + {0, &IDirectory::Read, "Read"}, + {1, &IDirectory::GetEntryCount, "GetEntryCount"}, + }; + RegisterHandlers(functions); + } + +private: + std::unique_ptr<FileSys::DirectoryBackend> backend; + + void Read(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 unk = rp.Pop<u64>(); + + LOG_DEBUG(Service_FS, "called, unk=0x%llx", unk); + + // Calculate how many entries we can fit in the output buffer + u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); + + // Read the data from the Directory backend + std::vector<FileSys::Entry> entries(count_entries); + u64 read_entries = backend->Read(count_entries, entries.data()); + + // Convert the data into a byte array + std::vector<u8> output(entries.size() * sizeof(FileSys::Entry)); + std::memcpy(output.data(), entries.data(), output.size()); + + // Write the data to memory + ctx.WriteBuffer(output); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(read_entries); + } + + void GetEntryCount(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + u64 count = backend->GetEntryCount(); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(count); + } +}; + +class IFileSystem final : public ServiceFramework<IFileSystem> { +public: + explicit IFileSystem(std::unique_ptr<FileSys::FileSystemBackend>&& backend) + : ServiceFramework("IFileSystem"), backend(std::move(backend)) { + static const FunctionInfo functions[] = { + {0, &IFileSystem::CreateFile, "CreateFile"}, + {2, &IFileSystem::CreateDirectory, "CreateDirectory"}, + {7, &IFileSystem::GetEntryType, "GetEntryType"}, + {8, &IFileSystem::OpenFile, "OpenFile"}, + {9, &IFileSystem::OpenDirectory, "OpenDirectory"}, + {10, &IFileSystem::Commit, "Commit"}, + }; + RegisterHandlers(functions); + } + + void CreateFile(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto file_buffer = ctx.ReadBuffer(); + auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); + + std::string name(file_buffer.begin(), end); + + u64 mode = rp.Pop<u64>(); + u32 size = rp.Pop<u32>(); + + LOG_DEBUG(Service_FS, "called file %s mode 0x%" PRIX64 " size 0x%08X", name.c_str(), mode, + size); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend->CreateFile(name, size)); + } + + void CreateDirectory(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto file_buffer = ctx.ReadBuffer(); + auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); + + std::string name(file_buffer.begin(), end); + + LOG_DEBUG(Service_FS, "called directory %s", name.c_str()); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend->CreateDirectory(name)); + } + + void OpenFile(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto file_buffer = ctx.ReadBuffer(); + auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); + + std::string name(file_buffer.begin(), end); + + auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>()); + + LOG_DEBUG(Service_FS, "called file %s mode %u", name.c_str(), static_cast<u32>(mode)); + + auto result = backend->OpenFile(name, mode); + if (result.Failed()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result.Code()); + return; + } + + auto file = std::move(result.Unwrap()); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IFile>(std::move(file)); + } + + void OpenDirectory(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto file_buffer = ctx.ReadBuffer(); + auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); + + std::string name(file_buffer.begin(), end); + + // TODO(Subv): Implement this filter. + u32 filter_flags = rp.Pop<u32>(); + + LOG_DEBUG(Service_FS, "called directory %s filter %u", name.c_str(), filter_flags); + + auto result = backend->OpenDirectory(name); + if (result.Failed()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result.Code()); + return; + } + + auto directory = std::move(result.Unwrap()); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IDirectory>(std::move(directory)); + } + + void GetEntryType(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto file_buffer = ctx.ReadBuffer(); + auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); + + std::string name(file_buffer.begin(), end); + + LOG_DEBUG(Service_FS, "called file %s", name.c_str()); + + auto result = backend->GetEntryType(name); + if (result.Failed()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result.Code()); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(static_cast<u32>(*result)); + } + + void Commit(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + +private: + std::unique_ptr<FileSys::FileSystemBackend> backend; +}; + +FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { + static const FunctionInfo functions[] = { + {1, &FSP_SRV::Initalize, "Initalize"}, + {18, &FSP_SRV::MountSdCard, "MountSdCard"}, + {22, &FSP_SRV::CreateSaveData, "CreateSaveData"}, + {51, &FSP_SRV::MountSaveData, "MountSaveData"}, + {200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"}, + {202, nullptr, "OpenDataStorageByDataId"}, + {203, &FSP_SRV::OpenRomStorage, "OpenRomStorage"}, + {1005, &FSP_SRV::GetGlobalAccessLogMode, "GetGlobalAccessLogMode"}, + }; + RegisterHandlers(functions); +} + +void FSP_SRV::TryLoadRomFS() { + if (romfs) { + return; + } + FileSys::Path unused; + auto res = OpenFileSystem(Type::RomFS, unused); + if (res.Succeeded()) { + romfs = std::move(res.Unwrap()); + } +} + +void FSP_SRV::Initalize(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + FileSys::Path unused; + auto filesystem = OpenFileSystem(Type::SDMC, unused).Unwrap(); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IFileSystem>(std::move(filesystem)); +} + +void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto save_struct = rp.PopRaw<std::array<u8, 0x40>>(); + auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); + u128 uid = rp.PopRaw<u128>(); + + LOG_WARNING(Service_FS, "(STUBBED) called uid = %016" PRIX64 "%016" PRIX64, uid[1], uid[0]); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + FileSys::Path unused; + auto filesystem = OpenFileSystem(Type::SaveData, unused).Unwrap(); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IFileSystem>(std::move(filesystem)); +} + +void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(5); +} + +void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + TryLoadRomFS(); + if (!romfs) { + // TODO (bunnei): Find the right error code to use here + LOG_CRITICAL(Service_FS, "no file system interface available!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(-1)); + return; + } + + // Attempt to open a StorageBackend interface to the RomFS + auto storage = romfs->OpenFile({}, {}); + if (storage.Failed()) { + LOG_CRITICAL(Service_FS, "no storage interface available!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(storage.Code()); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IStorage>(std::move(storage.Unwrap())); +} + +void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called, using OpenDataStorageByCurrentProcess"); + OpenDataStorageByCurrentProcess(ctx); +} + +} // namespace FileSystem +} // namespace Service diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h new file mode 100644 index 000000000..e15ba4375 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp_srv.h @@ -0,0 +1,37 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include "core/hle/service/service.h" + +namespace FileSys { +class FileSystemBackend; +} + +namespace Service { +namespace FileSystem { + +class FSP_SRV final : public ServiceFramework<FSP_SRV> { +public: + explicit FSP_SRV(); + ~FSP_SRV() = default; + +private: + void TryLoadRomFS(); + + void Initalize(Kernel::HLERequestContext& ctx); + void MountSdCard(Kernel::HLERequestContext& ctx); + void CreateSaveData(Kernel::HLERequestContext& ctx); + void MountSaveData(Kernel::HLERequestContext& ctx); + void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); + void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); + void OpenRomStorage(Kernel::HLERequestContext& ctx); + + std::unique_ptr<FileSys::FileSystemBackend> romfs; +}; + +} // namespace FileSystem +} // namespace Service diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp new file mode 100644 index 000000000..26593bb0c --- /dev/null +++ b/src/core/hle/service/friend/friend.cpp @@ -0,0 +1,28 @@ +// 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/service/friend/friend.h" +#include "core/hle/service/friend/friend_a.h" + +namespace Service { +namespace Friend { + +void Module::Interface::Unknown(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_Friend, "(STUBBED) called"); +} + +Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) + : ServiceFramework(name), module(std::move(module)) {} + +void InstallInterfaces(SM::ServiceManager& service_manager) { + auto module = std::make_shared<Module>(); + std::make_shared<Friend_A>(module)->InstallAsService(service_manager); +} + +} // namespace Friend +} // namespace Service diff --git a/src/core/hle/service/friend/friend.h b/src/core/hle/service/friend/friend.h new file mode 100644 index 000000000..ffa498397 --- /dev/null +++ b/src/core/hle/service/friend/friend.h @@ -0,0 +1,29 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Friend { + +class Module final { +public: + class Interface : public ServiceFramework<Interface> { + public: + Interface(std::shared_ptr<Module> module, const char* name); + + void Unknown(Kernel::HLERequestContext& ctx); + + protected: + std::shared_ptr<Module> module; + }; +}; + +/// Registers all Friend services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace Friend +} // namespace Service diff --git a/src/core/hle/service/friend/friend_a.cpp b/src/core/hle/service/friend/friend_a.cpp new file mode 100644 index 000000000..e1f2397c2 --- /dev/null +++ b/src/core/hle/service/friend/friend_a.cpp @@ -0,0 +1,19 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/friend/friend_a.h" + +namespace Service { +namespace Friend { + +Friend_A::Friend_A(std::shared_ptr<Module> module) + : Module::Interface(std::move(module), "friend:a") { + static const FunctionInfo functions[] = { + {0, &Friend_A::Unknown, "Unknown"}, + }; + RegisterHandlers(functions); +} + +} // namespace Friend +} // namespace Service diff --git a/src/core/hle/service/friend/friend_a.h b/src/core/hle/service/friend/friend_a.h new file mode 100644 index 000000000..68fa58297 --- /dev/null +++ b/src/core/hle/service/friend/friend_a.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/friend/friend.h" + +namespace Service { +namespace Friend { + +class Friend_A final : public Module::Interface { +public: + explicit Friend_A(std::shared_ptr<Module> module); +}; + +} // namespace Friend +} // namespace Service diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 6254237fa..019a09444 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -9,6 +9,7 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/service.h" @@ -18,9 +19,9 @@ namespace HID { // Updating period for each HID device. // TODO(shinyquagsire23): These need better values. -constexpr u64 pad_update_ticks = BASE_CLOCK_RATE / 234; -constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE / 104; -constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE / 101; +constexpr u64 pad_update_ticks = BASE_CLOCK_RATE / 10000; +constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE / 10000; +constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE / 10000; class IAppletResource final : public ServiceFramework<IAppletResource> { public: @@ -44,12 +45,16 @@ public: CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event); } + ~IAppletResource() { + CoreTiming::UnscheduleEvent(pad_update_event, 0); + } + private: void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 1}; + IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(shared_mem); - LOG_DEBUG(Service, "called"); + LOG_DEBUG(Service_HID, "called"); } void LoadInputDevices() { @@ -60,13 +65,14 @@ private: } void UpdatePadCallback(u64 userdata, int cycles_late) { - SharedMemory* mem = reinterpret_cast<SharedMemory*>(shared_mem->GetPointer()); + SharedMemory mem{}; + std::memcpy(&mem, shared_mem->GetPointer(), sizeof(SharedMemory)); if (is_device_reload_pending.exchange(false)) LoadInputDevices(); // Set up controllers as neon red+blue Joy-Con attached to console - ControllerHeader& controller_header = mem->controllers[Controller_Handheld].header; + ControllerHeader& controller_header = mem.controllers[Controller_Handheld].header; controller_header.type = ControllerType_Handheld | ControllerType_JoyconPair; controller_header.single_colors_descriptor = ColorDesc_ColorsNonexistent; controller_header.right_color_body = JOYCON_BODY_NEON_RED; @@ -74,8 +80,8 @@ private: controller_header.left_color_body = JOYCON_BODY_NEON_BLUE; controller_header.left_color_buttons = JOYCON_BUTTONS_NEON_BLUE; - for (int layoutIdx = 0; layoutIdx < HID_NUM_LAYOUTS; layoutIdx++) { - ControllerLayout& layout = mem->controllers[Controller_Handheld].layouts[layoutIdx]; + for (int index = 0; index < HID_NUM_LAYOUTS; index++) { + ControllerLayout& layout = mem.controllers[Controller_Handheld].layouts[index]; layout.header.num_entries = HID_NUM_ENTRIES; layout.header.max_entry_index = HID_NUM_ENTRIES - 1; @@ -131,10 +137,25 @@ private: // layouts) } - // TODO(shinyquagsire23): Update touch info + // TODO(bunnei): Properly implement the touch screen, the below will just write empty data + + TouchScreen& touchscreen = mem.touchscreen; + const u64 last_entry = touchscreen.header.latest_entry; + const u64 curr_entry = (last_entry + 1) % touchscreen.entries.size(); + const u64 timestamp = CoreTiming::GetTicks(); + const u64 sample_counter = touchscreen.entries[last_entry].header.timestamp + 1; + touchscreen.header.timestamp_ticks = timestamp; + touchscreen.header.num_entries = touchscreen.entries.size(); + touchscreen.header.latest_entry = curr_entry; + touchscreen.header.max_entry_index = touchscreen.entries.size(); + touchscreen.header.timestamp = timestamp; + touchscreen.entries[curr_entry].header.timestamp = sample_counter; + touchscreen.entries[curr_entry].header.num_touches = 0; // TODO(shinyquagsire23): Signal events + std::memcpy(shared_mem->GetPointer(), &mem, sizeof(SharedMemory)); + // Reschedule recurrent event CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); } @@ -151,29 +172,203 @@ private: buttons; }; +class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { +public: + IActiveVibrationDeviceList() : ServiceFramework("IActiveVibrationDeviceList") { + static const FunctionInfo functions[] = { + {0, &IActiveVibrationDeviceList::ActivateVibrationDevice, "ActivateVibrationDevice"}, + }; + RegisterHandlers(functions); + } + +private: + void ActivateVibrationDevice(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } +}; + class Hid final : public ServiceFramework<Hid> { public: Hid() : ServiceFramework("hid") { static const FunctionInfo functions[] = { - {0x00000000, &Hid::CreateAppletResource, "CreateAppletResource"}, + {0, &Hid::CreateAppletResource, "CreateAppletResource"}, + {1, &Hid::ActivateDebugPad, "ActivateDebugPad"}, + {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"}, + {21, &Hid::ActivateMouse, "ActivateMouse"}, + {31, &Hid::ActivateKeyboard, "ActivateKeyboard"}, + {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, + {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"}, + {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, + {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, + {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"}, + {103, &Hid::ActivateNpad, "ActivateNpad"}, + {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, + "AcquireNpadStyleSetUpdateEventHandle"}, + {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"}, + {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, + {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, + "SetNpadJoyAssignmentModeSingleByDefault"}, + {124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"}, + {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"}, + {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"}, + {201, &Hid::SendVibrationValue, "SendVibrationValue"}, + {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"}, + {203, &Hid::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"}, + {206, &Hid::SendVibrationValues, "SendVibrationValues"}, }; RegisterHandlers(functions); + + event = Kernel::Event::Create(Kernel::ResetType::OneShot, "hid:EventHandle"); } ~Hid() = default; private: + std::shared_ptr<IAppletResource> applet_resource; + u32 joy_hold_type{0}; + Kernel::SharedPtr<Kernel::Event> event; + void CreateAppletResource(Kernel::HLERequestContext& ctx) { - auto client_port = std::make_shared<IAppletResource>()->CreatePort(); - auto session = client_port->Connect(); - if (session.Succeeded()) { - LOG_DEBUG(Service, "called, initialized IAppletResource -> session=%u", - (*session)->GetObjectId()); - IPC::RequestBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushMoveObjects(std::move(session).Unwrap()); - } else { - UNIMPLEMENTED(); + if (applet_resource == nullptr) { + applet_resource = std::make_shared<IAppletResource>(); } + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IAppletResource>(applet_resource); + LOG_DEBUG(Service_HID, "called"); + } + + void ActivateDebugPad(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void ActivateTouchScreen(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void ActivateMouse(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void ActivateKeyboard(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void StartSixAxisSensor(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void ActivateNpad(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(event); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(joy_hold_type); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void SendVibrationValue(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void GetActualVibrationValue(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(0); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IActiveVibrationDeviceList>(); + LOG_DEBUG(Service_HID, "called"); + } + + void SendVibrationValues(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); } }; diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index 2d0d2fb65..b8e53d2c7 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <sstream> #include <string> #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" @@ -28,25 +29,35 @@ private: IsHead = 1, IsTail = 2, }; + enum Severity : u32_le { + Trace, + Info, + Warning, + Error, + Critical, + }; u64_le pid; u64_le threadContext; union { BitField<0, 16, Flags> flags; - BitField<16, 8, u32_le> severity; + BitField<16, 8, Severity> severity; BitField<24, 8, u32_le> verbosity; }; u32_le payload_size; - /// Returns true if this is part of a single log message - bool IsSingleMessage() const { - return (flags & Flags::IsHead) && (flags & Flags::IsTail); + bool IsHeadLog() const { + return flags & Flags::IsHead; + } + bool IsTailLog() const { + return flags & Flags::IsTail; } }; static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size"); /// Log field type enum class Field : u8 { + Skip = 1, Message = 2, Line = 3, Filename = 4, @@ -56,7 +67,7 @@ private: }; /** - * LM::Initialize service function + * LM::Log service function * Inputs: * 0: 0x00000000 * Outputs: @@ -64,7 +75,7 @@ private: */ void Log(Kernel::HLERequestContext& ctx) { // This function only succeeds - Get that out of the way - IPC::RequestBuilder rb{ctx, 1}; + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); // Read MessageHeader, despite not doing anything with it right now @@ -74,9 +85,9 @@ private: Memory::ReadBlock(addr, &header, sizeof(MessageHeader)); addr += sizeof(MessageHeader); - if (!header.IsSingleMessage()) { - LOG_WARNING(Service, "Multi message logs are unimplemeneted"); - return; + if (header.IsHeadLog()) { + log_stream.str(""); + log_stream.clear(); } // Parse out log metadata @@ -84,7 +95,12 @@ private: std::string message, filename, function; while (addr < end_addr) { const Field field{static_cast<Field>(Memory::Read8(addr++))}; - size_t length{Memory::Read8(addr++)}; + const size_t length{Memory::Read8(addr++)}; + + if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) { + ++addr; + } + switch (field) { case Field::Message: message = Memory::ReadCString(addr, length); @@ -99,32 +115,52 @@ private: function = Memory::ReadCString(addr, length); break; } + addr += length; } // Empty log - nothing to do here - if (message.empty()) { + if (log_stream.str().empty() && message.empty()) { return; } // Format a nicely printable string out of the log metadata - std::string output; - if (filename.size()) { - output += filename + ':'; + if (!filename.empty()) { + log_stream << filename << ':'; } - if (function.size()) { - output += function + ':'; + if (!function.empty()) { + log_stream << function << ':'; } if (line) { - output += std::to_string(line) + ':'; + log_stream << std::to_string(line) << ':'; } - if (output.back() == ':') { - output += ' '; + if (log_stream.str().length() > 0 && log_stream.str().back() == ':') { + log_stream << ' '; } - output += message; + log_stream << message; - LOG_DEBUG(Debug_Emulated, "%s", output.c_str()); + if (header.IsTailLog()) { + switch (header.severity) { + case MessageHeader::Severity::Trace: + LOG_TRACE(Debug_Emulated, "%s", log_stream.str().c_str()); + break; + case MessageHeader::Severity::Info: + LOG_INFO(Debug_Emulated, "%s", log_stream.str().c_str()); + break; + case MessageHeader::Severity::Warning: + LOG_WARNING(Debug_Emulated, "%s", log_stream.str().c_str()); + break; + case MessageHeader::Severity::Error: + LOG_ERROR(Debug_Emulated, "%s", log_stream.str().c_str()); + break; + case MessageHeader::Severity::Critical: + LOG_CRITICAL(Debug_Emulated, "%s", log_stream.str().c_str()); + break; + } + } } + + std::ostringstream log_stream; }; void InstallInterfaces(SM::ServiceManager& service_manager) { @@ -139,20 +175,11 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { * 0: ResultCode */ void LM::Initialize(Kernel::HLERequestContext& ctx) { - auto client_port = std::make_shared<Logger>()->CreatePort(); - auto session = client_port->Connect(); - if (session.Succeeded()) { - LOG_DEBUG(Service_SM, "called, initialized logger -> session=%u", - (*session)->GetObjectId()); - IPC::RequestBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushMoveObjects(std::move(session).Unwrap()); - registered_loggers.emplace_back(std::move(client_port)); - } else { - UNIMPLEMENTED(); - } + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<Logger>(); - LOG_INFO(Service_SM, "called"); + LOG_DEBUG(Service_LM, "called"); } LM::LM() : ServiceFramework("lm") { diff --git a/src/core/hle/service/lm/lm.h b/src/core/hle/service/lm/lm.h index 4b954bdb2..371135057 100644 --- a/src/core/hle/service/lm/lm.h +++ b/src/core/hle/service/lm/lm.h @@ -5,7 +5,6 @@ #pragma once #include <vector> -#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/service.h" @@ -19,8 +18,6 @@ public: private: void Initialize(Kernel::HLERequestContext& ctx); - - std::vector<Kernel::SharedPtr<Kernel::ClientPort>> registered_loggers; }; /// Registers all LM services with the specified service manager. diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp new file mode 100644 index 000000000..49870841c --- /dev/null +++ b/src/core/hle/service/nfp/nfp.cpp @@ -0,0 +1,28 @@ +// 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/service/nfp/nfp.h" +#include "core/hle/service/nfp/nfp_user.h" + +namespace Service { +namespace NFP { + +Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) + : ServiceFramework(name), module(std::move(module)) {} + +void Module::Interface::Unknown(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NFP, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void InstallInterfaces(SM::ServiceManager& service_manager) { + auto module = std::make_shared<Module>(); + std::make_shared<NFP_User>(module)->InstallAsService(service_manager); +} + +} // namespace NFP +} // namespace Service diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h new file mode 100644 index 000000000..1163e9954 --- /dev/null +++ b/src/core/hle/service/nfp/nfp.h @@ -0,0 +1,28 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace NFP { + +class Module final { +public: + class Interface : public ServiceFramework<Interface> { + public: + Interface(std::shared_ptr<Module> module, const char* name); + + void Unknown(Kernel::HLERequestContext& ctx); + + protected: + std::shared_ptr<Module> module; + }; +}; + +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace NFP +} // namespace Service diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp new file mode 100644 index 000000000..14e5647c4 --- /dev/null +++ b/src/core/hle/service/nfp/nfp_user.cpp @@ -0,0 +1,19 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/nfp/nfp_user.h" + +namespace Service { +namespace NFP { + +NFP_User::NFP_User(std::shared_ptr<Module> module) + : Module::Interface(std::move(module), "nfp:user") { + static const FunctionInfo functions[] = { + {0, &NFP_User::Unknown, "Unknown"}, + }; + RegisterHandlers(functions); +} + +} // namespace NFP +} // namespace Service diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h new file mode 100644 index 000000000..1606444ca --- /dev/null +++ b/src/core/hle/service/nfp/nfp_user.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/nfp/nfp.h" + +namespace Service { +namespace NFP { + +class NFP_User final : public Module::Interface { +public: + explicit NFP_User(std::shared_ptr<Module> module); +}; + +} // namespace NFP +} // namespace Service diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp new file mode 100644 index 000000000..dd2d5fe63 --- /dev/null +++ b/src/core/hle/service/nifm/nifm.cpp @@ -0,0 +1,213 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/event.h" +#include "core/hle/service/nifm/nifm.h" +#include "core/hle/service/nifm/nifm_a.h" +#include "core/hle/service/nifm/nifm_s.h" +#include "core/hle/service/nifm/nifm_u.h" + +namespace Service { +namespace NIFM { + +class IScanRequest final : public ServiceFramework<IScanRequest> { +public: + explicit IScanRequest() : ServiceFramework("IScanRequest") { + static const FunctionInfo functions[] = { + {0, nullptr, "Submit"}, + {1, nullptr, "IsProcessing"}, + {2, nullptr, "GetResult"}, + {3, nullptr, "GetSystemEventReadableHandle"}, + }; + RegisterHandlers(functions); + } +}; + +class IRequest final : public ServiceFramework<IRequest> { +public: + explicit IRequest() : ServiceFramework("IRequest") { + static const FunctionInfo functions[] = { + {0, &IRequest::GetRequestState, "GetRequestState"}, + {1, &IRequest::GetResult, "GetResult"}, + {2, &IRequest::GetSystemEventReadableHandles, "GetSystemEventReadableHandles"}, + {3, &IRequest::Cancel, "Cancel"}, + {4, nullptr, "Submit"}, + {5, nullptr, "SetRequirement"}, + {6, nullptr, "SetRequirementPreset"}, + {8, nullptr, "SetPriority"}, + {9, nullptr, "SetNetworkProfileId"}, + {10, nullptr, "SetRejectable"}, + {11, nullptr, "SetConnectionConfirmationOption"}, + {12, nullptr, "SetPersistent"}, + {13, nullptr, "SetInstant"}, + {14, nullptr, "SetSustainable"}, + {15, nullptr, "SetRawPriority"}, + {16, nullptr, "SetGreedy"}, + {17, nullptr, "SetSharable"}, + {18, nullptr, "SetRequirementByRevision"}, + {19, nullptr, "GetRequirement"}, + {20, nullptr, "GetRevision"}, + {21, nullptr, "GetAppletInfo"}, + {22, nullptr, "GetAdditionalInfo"}, + {23, nullptr, "SetKeptInSleep"}, + {24, nullptr, "RegisterSocketDescriptor"}, + {25, nullptr, "UnregisterSocketDescriptor"}, + }; + RegisterHandlers(functions); + + event1 = Kernel::Event::Create(Kernel::ResetType::OneShot, "IRequest:Event1"); + event2 = Kernel::Event::Create(Kernel::ResetType::OneShot, "IRequest:Event2"); + } + +private: + void GetRequestState(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); + } + void GetResult(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); + } + void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2, 2}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(event1, event2); + } + void Cancel(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + Kernel::SharedPtr<Kernel::Event> event1, event2; +}; + +class INetworkProfile final : public ServiceFramework<INetworkProfile> { +public: + explicit INetworkProfile() : ServiceFramework("INetworkProfile") { + static const FunctionInfo functions[] = { + {0, nullptr, "Update"}, + {1, nullptr, "PersistOld"}, + {2, nullptr, "Persist"}, + }; + RegisterHandlers(functions); + } +}; + +class IGeneralService final : public ServiceFramework<IGeneralService> { +public: + IGeneralService(); + +private: + void GetClientId(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(0); + } + void CreateScanRequest(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IScanRequest>(); + + LOG_DEBUG(Service_NIFM, "called"); + } + void CreateRequest(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IRequest>(); + + LOG_DEBUG(Service_NIFM, "called"); + } + void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<INetworkProfile>(); + + LOG_DEBUG(Service_NIFM, "called"); + } +}; + +IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") { + static const FunctionInfo functions[] = { + {1, &IGeneralService::GetClientId, "GetClientId"}, + {2, &IGeneralService::CreateScanRequest, "CreateScanRequest"}, + {4, &IGeneralService::CreateRequest, "CreateRequest"}, + {5, nullptr, "GetCurrentNetworkProfile"}, + {6, nullptr, "EnumerateNetworkInterfaces"}, + {7, nullptr, "EnumerateNetworkProfiles"}, + {8, nullptr, "GetNetworkProfile"}, + {9, nullptr, "SetNetworkProfile"}, + {10, &IGeneralService::RemoveNetworkProfile, "RemoveNetworkProfile"}, + {11, nullptr, "GetScanDataOld"}, + {12, nullptr, "GetCurrentIpAddress"}, + {13, nullptr, "GetCurrentAccessPointOld"}, + {14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"}, + {15, nullptr, "GetCurrentIpConfigInfo"}, + {16, nullptr, "SetWirelessCommunicationEnabled"}, + {17, nullptr, "IsWirelessCommunicationEnabled"}, + {18, nullptr, "GetInternetConnectionStatus"}, + {19, nullptr, "SetEthernetCommunicationEnabled"}, + {20, nullptr, "IsEthernetCommunicationEnabled"}, + {21, nullptr, "IsAnyInternetRequestAccepted"}, + {22, nullptr, "IsAnyForegroundRequestAccepted"}, + {23, nullptr, "PutToSleep"}, + {24, nullptr, "WakeUp"}, + {25, nullptr, "GetSsidListVersion"}, + {26, nullptr, "SetExclusiveClient"}, + {27, nullptr, "GetDefaultIpSetting"}, + {28, nullptr, "SetDefaultIpSetting"}, + {29, nullptr, "SetWirelessCommunicationEnabledForTest"}, + {30, nullptr, "SetEthernetCommunicationEnabledForTest"}, + {31, nullptr, "GetTelemetorySystemEventReadableHandle"}, + {32, nullptr, "GetTelemetryInfo"}, + {33, nullptr, "ConfirmSystemAvailability"}, + {34, nullptr, "SetBackgroundRequestEnabled"}, + {35, nullptr, "GetScanData"}, + {36, nullptr, "GetCurrentAccessPoint"}, + {37, nullptr, "Shutdown"}, + }; + RegisterHandlers(functions); +} + +void Module::Interface::CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IGeneralService>(); + LOG_DEBUG(Service_NIFM, "called"); +} + +void Module::Interface::CreateGeneralService(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IGeneralService>(); + LOG_DEBUG(Service_NIFM, "called"); +} + +Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) + : ServiceFramework(name), module(std::move(module)) {} + +void InstallInterfaces(SM::ServiceManager& service_manager) { + auto module = std::make_shared<Module>(); + std::make_shared<NIFM_A>(module)->InstallAsService(service_manager); + std::make_shared<NIFM_S>(module)->InstallAsService(service_manager); + std::make_shared<NIFM_U>(module)->InstallAsService(service_manager); +} + +} // namespace NIFM +} // namespace Service diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h new file mode 100644 index 000000000..11d263b12 --- /dev/null +++ b/src/core/hle/service/nifm/nifm.h @@ -0,0 +1,29 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace NIFM { + +class Module final { +public: + class Interface : public ServiceFramework<Interface> { + public: + Interface(std::shared_ptr<Module> module, const char* name); + + void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx); + void CreateGeneralService(Kernel::HLERequestContext& ctx); + + protected: + std::shared_ptr<Module> module; + }; +}; + +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace NIFM +} // namespace Service diff --git a/src/core/hle/service/nifm/nifm_a.cpp b/src/core/hle/service/nifm/nifm_a.cpp new file mode 100644 index 000000000..f75df8c04 --- /dev/null +++ b/src/core/hle/service/nifm/nifm_a.cpp @@ -0,0 +1,19 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/nifm/nifm_a.h" + +namespace Service { +namespace NIFM { + +NIFM_A::NIFM_A(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:a") { + static const FunctionInfo functions[] = { + {4, &NIFM_A::CreateGeneralServiceOld, "CreateGeneralServiceOld"}, + {5, &NIFM_A::CreateGeneralService, "CreateGeneralService"}, + }; + RegisterHandlers(functions); +} + +} // namespace NIFM +} // namespace Service diff --git a/src/core/hle/service/nifm/nifm_a.h b/src/core/hle/service/nifm/nifm_a.h new file mode 100644 index 000000000..eaea14e29 --- /dev/null +++ b/src/core/hle/service/nifm/nifm_a.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/nifm/nifm.h" + +namespace Service { +namespace NIFM { + +class NIFM_A final : public Module::Interface { +public: + explicit NIFM_A(std::shared_ptr<Module> module); +}; + +} // namespace NIFM +} // namespace Service diff --git a/src/core/hle/service/nifm/nifm_s.cpp b/src/core/hle/service/nifm/nifm_s.cpp new file mode 100644 index 000000000..9c0b300e4 --- /dev/null +++ b/src/core/hle/service/nifm/nifm_s.cpp @@ -0,0 +1,19 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/nifm/nifm_s.h" + +namespace Service { +namespace NIFM { + +NIFM_S::NIFM_S(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:s") { + static const FunctionInfo functions[] = { + {4, &NIFM_S::CreateGeneralServiceOld, "CreateGeneralServiceOld"}, + {5, &NIFM_S::CreateGeneralService, "CreateGeneralService"}, + }; + RegisterHandlers(functions); +} + +} // namespace NIFM +} // namespace Service diff --git a/src/core/hle/service/nifm/nifm_s.h b/src/core/hle/service/nifm/nifm_s.h new file mode 100644 index 000000000..f9e2d8039 --- /dev/null +++ b/src/core/hle/service/nifm/nifm_s.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/nifm/nifm.h" + +namespace Service { +namespace NIFM { + +class NIFM_S final : public Module::Interface { +public: + explicit NIFM_S(std::shared_ptr<Module> module); +}; + +} // namespace NIFM +} // namespace Service diff --git a/src/core/hle/service/nifm/nifm_u.cpp b/src/core/hle/service/nifm/nifm_u.cpp new file mode 100644 index 000000000..44e6f483d --- /dev/null +++ b/src/core/hle/service/nifm/nifm_u.cpp @@ -0,0 +1,19 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/nifm/nifm_u.h" + +namespace Service { +namespace NIFM { + +NIFM_U::NIFM_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:u") { + static const FunctionInfo functions[] = { + {4, &NIFM_U::CreateGeneralServiceOld, "CreateGeneralServiceOld"}, + {5, &NIFM_U::CreateGeneralService, "CreateGeneralService"}, + }; + RegisterHandlers(functions); +} + +} // namespace NIFM +} // namespace Service diff --git a/src/core/hle/service/nifm/nifm_u.h b/src/core/hle/service/nifm/nifm_u.h new file mode 100644 index 000000000..912006775 --- /dev/null +++ b/src/core/hle/service/nifm/nifm_u.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/nifm/nifm.h" + +namespace Service { +namespace NIFM { + +class NIFM_U final : public Module::Interface { +public: + explicit NIFM_U(std::shared_ptr<Module> module); +}; + +} // namespace NIFM +} // namespace Service diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp new file mode 100644 index 000000000..45681c50f --- /dev/null +++ b/src/core/hle/service/ns/ns.cpp @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/ns/ns.h" +#include "core/hle/service/ns/pl_u.h" + +namespace Service { +namespace NS { + +void InstallInterfaces(SM::ServiceManager& service_manager) { + std::make_shared<PL_U>()->InstallAsService(service_manager); +} + +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h new file mode 100644 index 000000000..a4b7e3ded --- /dev/null +++ b/src/core/hle/service/ns/ns.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace NS { + +/// Registers all NS services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp new file mode 100644 index 000000000..ef3c7799a --- /dev/null +++ b/src/core/hle/service/ns/pl_u.cpp @@ -0,0 +1,122 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/common_paths.h" +#include "common/file_util.h" +#include "core/core.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/ns/pl_u.h" + +namespace Service { +namespace NS { + +struct FontRegion { + u32 offset; + u32 size; +}; + +// The below data is specific to shared font data dumped from Switch on f/w 2.2 +// Virtual address and offsets/sizes likely will vary by dump +static constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL}; +static constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000}; +static constexpr std::array<FontRegion, 6> SHARED_FONT_REGIONS{ + FontRegion{0x00000008, 0x001fe764}, FontRegion{0x001fe774, 0x00773e58}, + FontRegion{0x009725d4, 0x0001aca8}, FontRegion{0x0098d284, 0x00369cec}, + FontRegion{0x00cf6f78, 0x0039b858}, FontRegion{0x010927d8, 0x00019e80}, +}; + +enum class LoadState : u32 { + Loading = 0, + Done = 1, +}; + +PL_U::PL_U() : ServiceFramework("pl:u") { + static const FunctionInfo functions[] = { + {0, &PL_U::RequestLoad, "RequestLoad"}, + {1, &PL_U::GetLoadState, "GetLoadState"}, + {2, &PL_U::GetSize, "GetSize"}, + {3, &PL_U::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"}, + {4, &PL_U::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}}; + RegisterHandlers(functions); + + // Attempt to load shared font data from disk + const std::string filepath{FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT}; + FileUtil::CreateFullPath(filepath); // Create path if not already created + FileUtil::IOFile file(filepath, "rb"); + + if (file.IsOpen()) { + // Read shared font data + ASSERT(file.GetSize() == SHARED_FONT_MEM_SIZE); + shared_font = std::make_shared<std::vector<u8>>(static_cast<size_t>(file.GetSize())); + file.ReadBytes(shared_font->data(), shared_font->size()); + } else { + LOG_WARNING(Service_NS, "Unable to load shared font: %s", filepath.c_str()); + } +} + +void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 shared_font_type{rp.Pop<u32>()}; + + LOG_DEBUG(Service_NS, "called, shared_font_type=%d", shared_font_type); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 font_id{rp.Pop<u32>()}; + + LOG_DEBUG(Service_NS, "called, font_id=%d", font_id); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(static_cast<u32>(LoadState::Done)); +} + +void PL_U::GetSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 font_id{rp.Pop<u32>()}; + + LOG_DEBUG(Service_NS, "called, font_id=%d", font_id); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(SHARED_FONT_REGIONS[font_id].size); +} + +void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 font_id{rp.Pop<u32>()}; + + LOG_DEBUG(Service_NS, "called, font_id=%d", font_id); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(SHARED_FONT_REGIONS[font_id].offset); +} + +void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { + if (shared_font != nullptr) { + // TODO(bunnei): This is a less-than-ideal solution to load a RAM dump of the Switch shared + // font data. This (likely) relies on exact address, size, and offsets from the original + // dump. In the future, we need to replace this with a more robust solution. + + // Map backing memory for the font data + Core::CurrentProcess()->vm_manager.MapMemoryBlock(SHARED_FONT_MEM_VADDR, shared_font, 0, + SHARED_FONT_MEM_SIZE, + Kernel::MemoryState::Shared); + + // Create shared font memory object + shared_font_mem = Kernel::SharedMemory::Create( + Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite, + Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, + "PL_U:shared_font_mem"); + } + + LOG_DEBUG(Service_NS, "called"); + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(shared_font_mem); +} + +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/ns/pl_u.h b/src/core/hle/service/ns/pl_u.h new file mode 100644 index 000000000..360482d13 --- /dev/null +++ b/src/core/hle/service/ns/pl_u.h @@ -0,0 +1,34 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include "core/hle/kernel/shared_memory.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace NS { + +class PL_U final : public ServiceFramework<PL_U> { +public: + PL_U(); + ~PL_U() = default; + +private: + void RequestLoad(Kernel::HLERequestContext& ctx); + void GetLoadState(Kernel::HLERequestContext& ctx); + void GetSize(Kernel::HLERequestContext& ctx); + void GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx); + void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx); + + /// Handle to shared memory region designated for a shared font + Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem; + + /// Backing memory for the shared font data + std::shared_ptr<std::vector<u8>> shared_font; +}; + +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index 5ee33b3d6..cdc25b059 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h @@ -5,7 +5,9 @@ #pragma once #include <vector> +#include "common/bit_field.h" #include "common/common_types.h" +#include "common/swap.h" namespace Service { namespace Nvidia { @@ -17,6 +19,14 @@ class nvdevice { public: nvdevice() = default; virtual ~nvdevice() = default; + union Ioctl { + u32_le raw; + BitField<0, 8, u32_le> cmd; + BitField<8, 8, u32_le> group; + BitField<16, 14, u32_le> length; + BitField<30, 1, u32_le> is_in; + BitField<31, 1, u32_le> is_out; + }; /** * Handles an ioctl request. @@ -25,7 +35,7 @@ public: * @param output A buffer where the output data will be written to. * @returns The result code of the ioctl. */ - virtual u32 ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) = 0; + virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) = 0; }; } // namespace Devices diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index b65d79f11..87b3a2d74 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -14,24 +14,25 @@ namespace Service { namespace Nvidia { namespace Devices { -u32 nvdisp_disp0::ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { UNIMPLEMENTED(); return 0; } void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, - u32 stride) { + u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform) { VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); LOG_WARNING(Service, - "Drawing from address %llx offset %08X Width %u Height %u Stride %u Format %u", - addr, offset, width, height, stride, format); + "Drawing from address %lx offset %08X Width %u Height %u Stride %u Format %u", addr, + offset, width, height, stride, format); - using PixelFormat = RendererBase::FramebufferInfo::PixelFormat; - const RendererBase::FramebufferInfo framebuffer_info{ - addr, offset, width, height, stride, static_cast<PixelFormat>(format)}; + using PixelFormat = Tegra::FramebufferConfig::PixelFormat; + const Tegra::FramebufferConfig framebuffer{ + addr, offset, width, height, stride, static_cast<PixelFormat>(format), transform}; Core::System::GetInstance().perf_stats.EndGameFrame(); - VideoCore::g_renderer->SwapBuffers(framebuffer_info); + + VideoCore::g_renderer->SwapBuffers(framebuffer); } } // namespace Devices diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index f5f9de3f4..66f56f23d 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -8,6 +8,7 @@ #include <vector> #include "common/common_types.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" +#include "core/hle/service/nvflinger/buffer_queue.h" namespace Service { namespace Nvidia { @@ -20,10 +21,11 @@ public: nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev) : nvdevice(), nvmap_dev(std::move(nvmap_dev)) {} ~nvdisp_disp0() = default; - u32 ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; /// Performs a screen flip, drawing the buffer pointed to by the handle. - void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride); + void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, + NVFlinger::BufferQueue::BufferTransformFlags transform); private: std::shared_ptr<nvmap> nvmap_dev; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 9db08339a..9892402fa 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -2,16 +2,116 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cinttypes> #include "common/assert.h" #include "common/logging/log.h" +#include "core/core.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" +#include "core/hle/service/nvdrv/devices/nvmap.h" namespace Service { namespace Nvidia { namespace Devices { -u32 nvhost_as_gpu::ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) { - UNIMPLEMENTED(); +u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%zx, output_size=0x%zx", + command.raw, input.size(), output.size()); + + switch (static_cast<IoctlCommand>(command.raw)) { + case IoctlCommand::IocInitalizeExCommand: + return InitalizeEx(input, output); + case IoctlCommand::IocAllocateSpaceCommand: + return AllocateSpace(input, output); + case IoctlCommand::IocMapBufferExCommand: + return MapBufferEx(input, output); + case IoctlCommand::IocBindChannelCommand: + return BindChannel(input, output); + case IoctlCommand::IocGetVaRegionsCommand: + return GetVARegions(input, output); + } + return 0; +} + +u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlInitalizeEx params{}; + std::memcpy(¶ms, input.data(), input.size()); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x%x", params.big_page_size); + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlAllocSpace params{}; + std::memcpy(¶ms, input.data(), input.size()); + LOG_DEBUG(Service_NVDRV, "called, pages=%x, page_size=%x, flags=%x", params.pages, + params.page_size, params.flags); + + auto& gpu = Core::System::GetInstance().GPU(); + const u64 size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)}; + if (params.flags & 1) { + params.offset = gpu.memory_manager->AllocateSpace(params.offset, size, 1); + } else { + params.offset = gpu.memory_manager->AllocateSpace(size, params.align); + } + + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlMapBufferEx params{}; + std::memcpy(¶ms, input.data(), input.size()); + + LOG_DEBUG(Service_NVDRV, + "called, flags=%x, nvmap_handle=%x, buffer_offset=%" PRIu64 ", mapping_size=%" PRIu64 + ", offset=%" PRIu64, + params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size, + params.offset); + + if (!params.nvmap_handle) { + return 0; + } + + auto object = nvmap_dev->GetObject(params.nvmap_handle); + ASSERT(object); + + auto& gpu = Core::System::GetInstance().GPU(); + + if (params.flags & 1) { + params.offset = gpu.memory_manager->MapBufferEx(object->addr, params.offset, object->size); + } else { + params.offset = gpu.memory_manager->MapBufferEx(object->addr, object->size); + } + + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlBindChannel params{}; + std::memcpy(¶ms, input.data(), input.size()); + LOG_DEBUG(Service_NVDRV, "called, fd=%x", params.fd); + channel = params.fd; + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlGetVaRegions params{}; + std::memcpy(¶ms, input.data(), input.size()); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr=%" PRIu64 ", buf_size=%x", + params.buf_addr, params.buf_size); + + params.buf_size = 0x30; + params.regions[0].offset = 0x04000000; + params.regions[0].page_size = 0x1000; + params.regions[0].pages = 0x3fbfff; + + params.regions[1].offset = 0x04000000; + params.regions[1].page_size = 0x10000; + params.regions[1].pages = 0x1bffff; + // TODO(ogniK): This probably can stay stubbed but should add support way way later + std::memcpy(output.data(), ¶ms, output.size()); return 0; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 01f8861c8..f8a60cce7 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -4,20 +4,100 @@ #pragma once +#include <memory> +#include <utility> #include <vector> #include "common/common_types.h" +#include "common/swap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" namespace Service { namespace Nvidia { namespace Devices { +class nvmap; + class nvhost_as_gpu final : public nvdevice { public: - nvhost_as_gpu() = default; + nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} ~nvhost_as_gpu() override = default; - u32 ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + +private: + enum class IoctlCommand : u32_le { + IocInitalizeExCommand = 0x40284109, + IocAllocateSpaceCommand = 0xC0184102, + IocMapBufferExCommand = 0xC0284106, + IocBindChannelCommand = 0x40044101, + IocGetVaRegionsCommand = 0xC0404108, + }; + + struct IoctlInitalizeEx { + u32_le big_page_size; // depends on GPU's available_big_page_sizes; 0=default + s32_le as_fd; // ignored; passes 0 + u32_le flags; // passes 0 + u32_le reserved; // ignored; passes 0 + u64_le unk0; + u64_le unk1; + u64_le unk2; + }; + static_assert(sizeof(IoctlInitalizeEx) == 40, "IoctlInitalizeEx is incorrect size"); + + struct IoctlAllocSpace { + u32_le pages; + u32_le page_size; + u32_le flags; + INSERT_PADDING_WORDS(1); + union { + u64_le offset; + u64_le align; + }; + }; + static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitalizeEx is incorrect size"); + + struct IoctlMapBufferEx { + u32_le flags; // bit0: fixed_offset, bit2: cacheable + u32_le kind; // -1 is default + u32_le nvmap_handle; + u32_le page_size; // 0 means don't care + u64_le buffer_offset; + u64_le mapping_size; + u64_le offset; + }; + static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size"); + + struct IoctlBindChannel { + u32_le fd; + }; + static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size"); + + struct IoctlVaRegion { + u64_le offset; + u32_le page_size; + INSERT_PADDING_WORDS(1); + u64_le pages; + }; + static_assert(sizeof(IoctlVaRegion) == 24, "IoctlVaRegion is incorrect size"); + + struct IoctlGetVaRegions { + u64_le buf_addr; // (contained output user ptr on linux, ignored) + u32_le buf_size; // forced to 2*sizeof(struct va_region) + u32_le reserved; + IoctlVaRegion regions[2]; + }; + static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2, + "IoctlGetVaRegions is incorrect size"); + + u32 channel{}; + + u32 InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output); + u32 AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output); + u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output); + u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output); + u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output); + + std::shared_ptr<nvmap> nvmap_dev; }; } // namespace Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp new file mode 100644 index 000000000..45711d686 --- /dev/null +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -0,0 +1,64 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" + +namespace Service { +namespace Nvidia { +namespace Devices { + +u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%zx, output_size=0x%zx", + command.raw, input.size(), output.size()); + + switch (static_cast<IoctlCommand>(command.raw)) { + case IoctlCommand::IocGetConfigCommand: + return NvOsGetConfigU32(input, output); + case IoctlCommand::IocCtrlEventWaitCommand: + return IocCtrlEventWait(input, output); + } + UNIMPLEMENTED(); + return 0; +} + +u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { + IocGetConfigParams params{}; + std::memcpy(¶ms, input.data(), sizeof(params)); + LOG_DEBUG(Service_NVDRV, "called, setting=%s!%s", params.domain_str.data(), + params.param_str.data()); + + if (!strcmp(params.domain_str.data(), "nv")) { + if (!strcmp(params.param_str.data(), "NV_MEMORY_PROFILER")) { + params.config_str[0] = '0'; + } else if (!strcmp(params.param_str.data(), "NVN_THROUGH_OPENGL")) { + params.config_str[0] = '0'; + } else if (!strcmp(params.param_str.data(), "NVRM_GPU_PREVENT_USE")) { + params.config_str[0] = '0'; + } else { + params.config_str[0] = '0'; + } + } else { + UNIMPLEMENTED(); // unknown domain? Only nv has been seen so far on hardware + } + std::memcpy(output.data(), ¶ms, sizeof(params)); + return 0; +} + +u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output) { + IocCtrlEventWaitParams params{}; + std::memcpy(¶ms, input.data(), sizeof(params)); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, syncpt_id=%u threshold=%u timeout=%d", + params.syncpt_id, params.threshold, params.timeout); + + // TODO(Subv): Implement actual syncpt waiting. + params.value = 0; + std::memcpy(output.data(), ¶ms, sizeof(params)); + return 0; +} + +} // namespace Devices +} // namespace Nvidia +} // namespace Service diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h new file mode 100644 index 000000000..0ca01aa6d --- /dev/null +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -0,0 +1,60 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <cstdlib> +#include <cstring> +#include <vector> +#include "common/common_types.h" +#include "core/hle/service/nvdrv/devices/nvdevice.h" + +namespace Service { +namespace Nvidia { +namespace Devices { + +class nvhost_ctrl final : public nvdevice { +public: + nvhost_ctrl() = default; + ~nvhost_ctrl() override = default; + + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + +private: + enum class IoctlCommand : u32_le { + IocSyncptReadCommand = 0xC0080014, + IocSyncptIncrCommand = 0x40040015, + IocSyncptWaitCommand = 0xC00C0016, + IocModuleMutexCommand = 0x40080017, + IocModuleRegRDWRCommand = 0xC008010E, + IocSyncptWaitexCommand = 0xC0100019, + IocSyncptReadMaxCommand = 0xC008001A, + IocCtrlEventWaitCommand = 0xC010001D, + IocGetConfigCommand = 0xC183001B, + }; + + struct IocGetConfigParams { + std::array<char, 0x41> domain_str; + std::array<char, 0x41> param_str; + std::array<char, 0x101> config_str; + }; + static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size"); + + struct IocCtrlEventWaitParams { + u32_le syncpt_id; + u32_le threshold; + s32_le timeout; + u32_le value; + }; + static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size"); + + u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); + + u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output); +}; + +} // namespace Devices +} // namespace Nvidia +} // namespace Service diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp new file mode 100644 index 000000000..3b353d742 --- /dev/null +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -0,0 +1,127 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cinttypes> +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" + +namespace Service { +namespace Nvidia { +namespace Devices { + +u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%zx, output_size=0x%zx", + command.raw, input.size(), output.size()); + + switch (static_cast<IoctlCommand>(command.raw)) { + case IoctlCommand::IocGetCharacteristicsCommand: + return GetCharacteristics(input, output); + case IoctlCommand::IocGetTPCMasksCommand: + return GetTPCMasks(input, output); + case IoctlCommand::IocGetActiveSlotMaskCommand: + return GetActiveSlotMask(input, output); + case IoctlCommand::IocZcullGetCtxSizeCommand: + return ZCullGetCtxSize(input, output); + case IoctlCommand::IocZcullGetInfo: + return ZCullGetInfo(input, output); + } + UNIMPLEMENTED(); + return 0; +} + +u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called"); + IoctlCharacteristics params{}; + std::memcpy(¶ms, input.data(), input.size()); + params.gc.arch = 0x120; + params.gc.impl = 0xb; + params.gc.rev = 0xa1; + params.gc.num_gpc = 0x1; + params.gc.l2_cache_size = 0x40000; + params.gc.on_board_video_memory_size = 0x0; + params.gc.num_tpc_per_gpc = 0x2; + params.gc.bus_type = 0x20; + params.gc.big_page_size = 0x20000; + params.gc.compression_page_size = 0x20000; + params.gc.pde_coverage_bit_count = 0x1B; + params.gc.available_big_page_sizes = 0x30000; + params.gc.gpc_mask = 0x1; + params.gc.sm_arch_sm_version = 0x503; + params.gc.sm_arch_spa_version = 0x503; + params.gc.sm_arch_warp_count = 0x80; + params.gc.gpu_va_bit_count = 0x28; + params.gc.reserved = 0x0; + params.gc.flags = 0x55; + params.gc.twod_class = 0x902D; + params.gc.threed_class = 0xB197; + params.gc.compute_class = 0xB1C0; + params.gc.gpfifo_class = 0xB06F; + params.gc.inline_to_memory_class = 0xA140; + params.gc.dma_copy_class = 0xB0B5; + params.gc.max_fbps_count = 0x1; + params.gc.fbp_en_mask = 0x0; + params.gc.max_ltc_per_fbp = 0x2; + params.gc.max_lts_per_ltc = 0x1; + params.gc.max_tex_per_tpc = 0x0; + params.gc.max_gpc_count = 0x1; + params.gc.rop_l2_en_mask_0 = 0x21D70; + params.gc.rop_l2_en_mask_1 = 0x0; + params.gc.chipname = 0x6230326D67; + params.gc.gr_compbit_store_base_hw = 0x0; + params.gpu_characteristics_buf_size = 0xA0; + params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED) + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlGpuGetTpcMasksArgs params{}; + std::memcpy(¶ms, input.data(), input.size()); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, mask=0x%x, mask_buf_addr=0x%" PRIx64, + params.mask_buf_size, params.mask_buf_addr); + std::memcpy(output.data(), ¶ms, sizeof(params)); + return 0; +} + +u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called"); + IoctlActiveSlotMask params{}; + std::memcpy(¶ms, input.data(), input.size()); + params.slot = 0x07; + params.mask = 0x01; + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called"); + IoctlZcullGetCtxSize params{}; + std::memcpy(¶ms, input.data(), input.size()); + params.size = 0x1; + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called"); + IoctlNvgpuGpuZcullGetInfoArgs params{}; + std::memcpy(¶ms, input.data(), input.size()); + params.width_align_pixels = 0x20; + params.height_align_pixels = 0x20; + params.pixel_squares_by_aliquots = 0x400; + params.aliquot_total = 0x800; + params.region_byte_multiplier = 0x20; + params.region_header_size = 0x20; + params.subregion_header_size = 0xc0; + params.subregion_width_align_pixels = 0x20; + params.subregion_height_align_pixels = 0x40; + params.subregion_count = 0x10; + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +} // namespace Devices +} // namespace Nvidia +} // namespace Service diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h new file mode 100644 index 000000000..dc0476993 --- /dev/null +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -0,0 +1,130 @@ +// 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/swap.h" +#include "core/hle/service/nvdrv/devices/nvdevice.h" + +namespace Service { +namespace Nvidia { +namespace Devices { + +class nvhost_ctrl_gpu final : public nvdevice { +public: + nvhost_ctrl_gpu() = default; + ~nvhost_ctrl_gpu() override = default; + + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + +private: + enum class IoctlCommand : u32_le { + IocGetCharacteristicsCommand = 0xC0B04705, + IocGetTPCMasksCommand = 0xC0184706, + IocGetActiveSlotMaskCommand = 0x80084714, + IocZcullGetCtxSizeCommand = 0x80044701, + IocZcullGetInfo = 0x80284702, + }; + + struct IoctlGpuCharacteristics { + u32_le arch; // 0x120 (NVGPU_GPU_ARCH_GM200) + u32_le impl; // 0xB (NVGPU_GPU_IMPL_GM20B) + u32_le rev; // 0xA1 (Revision A1) + u32_le num_gpc; // 0x1 + u64_le l2_cache_size; // 0x40000 + u64_le on_board_video_memory_size; // 0x0 (not used) + u32_le num_tpc_per_gpc; // 0x2 + u32_le bus_type; // 0x20 (NVGPU_GPU_BUS_TYPE_AXI) + u32_le big_page_size; // 0x20000 + u32_le compression_page_size; // 0x20000 + u32_le pde_coverage_bit_count; // 0x1B + u32_le available_big_page_sizes; // 0x30000 + u32_le gpc_mask; // 0x1 + u32_le sm_arch_sm_version; // 0x503 (Maxwell Generation 5.0.3?) + u32_le sm_arch_spa_version; // 0x503 (Maxwell Generation 5.0.3?) + u32_le sm_arch_warp_count; // 0x80 + u32_le gpu_va_bit_count; // 0x28 + u32_le reserved; // NULL + u64_le flags; // 0x55 + u32_le twod_class; // 0x902D (FERMI_TWOD_A) + u32_le threed_class; // 0xB197 (MAXWELL_B) + u32_le compute_class; // 0xB1C0 (MAXWELL_COMPUTE_B) + u32_le gpfifo_class; // 0xB06F (MAXWELL_CHANNEL_GPFIFO_A) + u32_le inline_to_memory_class; // 0xA140 (KEPLER_INLINE_TO_MEMORY_B) + u32_le dma_copy_class; // 0xB0B5 (MAXWELL_DMA_COPY_A) + u32_le max_fbps_count; // 0x1 + u32_le fbp_en_mask; // 0x0 (disabled) + u32_le max_ltc_per_fbp; // 0x2 + u32_le max_lts_per_ltc; // 0x1 + u32_le max_tex_per_tpc; // 0x0 (not supported) + u32_le max_gpc_count; // 0x1 + u32_le rop_l2_en_mask_0; // 0x21D70 (fuse_status_opt_rop_l2_fbp_r) + u32_le rop_l2_en_mask_1; // 0x0 + u64_le chipname; // 0x6230326D67 ("gm20b") + u64_le gr_compbit_store_base_hw; // 0x0 (not supported) + }; + static_assert(sizeof(IoctlGpuCharacteristics) == 160, + "IoctlGpuCharacteristics is incorrect size"); + + struct IoctlCharacteristics { + u64_le gpu_characteristics_buf_size; // must not be NULL, but gets overwritten with + // 0xA0=max_size + u64_le gpu_characteristics_buf_addr; // ignored, but must not be NULL + IoctlGpuCharacteristics gc; + }; + static_assert(sizeof(IoctlCharacteristics) == 16 + sizeof(IoctlGpuCharacteristics), + "IoctlCharacteristics is incorrect size"); + + struct IoctlGpuGetTpcMasksArgs { + /// [in] TPC mask buffer size reserved by userspace. Should be at least + /// sizeof(__u32) * fls(gpc_mask) to receive TPC mask for each GPC. + /// [out] full kernel buffer size + u32_le mask_buf_size; + u32_le reserved; + + /// [in] pointer to TPC mask buffer. It will receive one 32-bit TPC mask per GPC or 0 if + /// GPC is not enabled or not present. This parameter is ignored if mask_buf_size is 0. + u64_le mask_buf_addr; + u64_le unk; // Nintendo add this? + }; + static_assert(sizeof(IoctlGpuGetTpcMasksArgs) == 24, + "IoctlGpuGetTpcMasksArgs is incorrect size"); + + struct IoctlActiveSlotMask { + u32_le slot; // always 0x07 + u32_le mask; + }; + static_assert(sizeof(IoctlActiveSlotMask) == 8, "IoctlActiveSlotMask is incorrect size"); + + struct IoctlZcullGetCtxSize { + u32_le size; + }; + static_assert(sizeof(IoctlZcullGetCtxSize) == 4, "IoctlZcullGetCtxSize is incorrect size"); + + struct IoctlNvgpuGpuZcullGetInfoArgs { + u32_le width_align_pixels; + u32_le height_align_pixels; + u32_le pixel_squares_by_aliquots; + u32_le aliquot_total; + u32_le region_byte_multiplier; + u32_le region_header_size; + u32_le subregion_header_size; + u32_le subregion_width_align_pixels; + u32_le subregion_height_align_pixels; + u32_le subregion_count; + }; + static_assert(sizeof(IoctlNvgpuGpuZcullGetInfoArgs) == 40, + "IoctlNvgpuGpuZcullGetInfoArgs is incorrect size"); + + u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output); + u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output); + u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); + u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output); + u32 ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output); +}; +} // namespace Devices +} // namespace Nvidia +} // namespace Service diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp new file mode 100644 index 000000000..da44c65f3 --- /dev/null +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -0,0 +1,147 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cinttypes> +#include <map> +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/core.h" +#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" + +namespace Service { +namespace Nvidia { +namespace Devices { + +u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%zx, output_size=0x%zx", + command.raw, input.size(), output.size()); + + switch (static_cast<IoctlCommand>(command.raw)) { + case IoctlCommand::IocSetNVMAPfdCommand: + return SetNVMAPfd(input, output); + case IoctlCommand::IocSetClientDataCommand: + return SetClientData(input, output); + case IoctlCommand::IocGetClientDataCommand: + return GetClientData(input, output); + case IoctlCommand::IocZCullBind: + return ZCullBind(input, output); + case IoctlCommand::IocSetErrorNotifierCommand: + return SetErrorNotifier(input, output); + case IoctlCommand::IocChannelSetPriorityCommand: + return SetChannelPriority(input, output); + case IoctlCommand::IocAllocGPFIFOEx2Command: + return AllocGPFIFOEx2(input, output); + case IoctlCommand::IocAllocObjCtxCommand: + return AllocateObjectContext(input, output); + } + + if (command.group == NVGPU_IOCTL_MAGIC) { + if (command.cmd == NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO) { + return SubmitGPFIFO(input, output); + } + } + + UNIMPLEMENTED(); + return 0; +}; + +u32 nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlSetNvmapFD params{}; + std::memcpy(¶ms, input.data(), input.size()); + LOG_DEBUG(Service_NVDRV, "called, fd=%x", params.nvmap_fd); + nvmap_fd = params.nvmap_fd; + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called"); + IoctlClientData params{}; + std::memcpy(¶ms, input.data(), input.size()); + user_data = params.data; + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called"); + IoctlClientData params{}; + std::memcpy(¶ms, input.data(), input.size()); + params.data = user_data; + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) { + std::memcpy(&zcull_params, input.data(), input.size()); + LOG_DEBUG(Service_NVDRV, "called, gpu_va=%" PRIx64 ", mode=%x", zcull_params.gpu_va, + zcull_params.mode); + std::memcpy(output.data(), &zcull_params, output.size()); + return 0; +} + +u32 nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlSetErrorNotifier params{}; + std::memcpy(¶ms, input.data(), input.size()); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset=%" PRIx64 ", size=%" PRIx64 ", mem=%x", + params.offset, params.size, params.mem); + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) { + std::memcpy(&channel_priority, input.data(), input.size()); + LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority=%x", channel_priority); + std::memcpy(output.data(), &channel_priority, output.size()); + return 0; +} + +u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlAllocGpfifoEx2 params{}; + std::memcpy(¶ms, input.data(), input.size()); + LOG_WARNING(Service_NVDRV, + "(STUBBED) called, num_entries=%x, flags=%x, unk0=%x, unk1=%x, unk2=%x, unk3=%x", + params.num_entries, params.flags, params.unk0, params.unk1, params.unk2, + params.unk3); + params.fence_out.id = 0; + params.fence_out.value = 0; + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlAllocObjCtx params{}; + std::memcpy(¶ms, input.data(), input.size()); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num=%x, flags=%x", params.class_num, + params.flags); + params.obj_id = 0x0; + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) { + if (input.size() < sizeof(IoctlSubmitGpfifo)) + UNIMPLEMENTED(); + IoctlSubmitGpfifo params{}; + std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo=%" PRIx64 ", num_entries=%x, flags=%x", + params.gpfifo, params.num_entries, params.flags); + + auto entries = std::vector<IoctlGpfifoEntry>(); + entries.resize(params.num_entries); + std::memcpy(&entries[0], &input.data()[sizeof(IoctlSubmitGpfifo)], + params.num_entries * sizeof(IoctlGpfifoEntry)); + for (auto entry : entries) { + VAddr va_addr = entry.Address(); + Core::System::GetInstance().GPU().ProcessCommandList(va_addr, entry.sz); + } + params.fence_out.id = 0; + params.fence_out.value = 0; + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +} // namespace Devices +} // namespace Nvidia +} // namespace Service diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h new file mode 100644 index 000000000..e7e9a0088 --- /dev/null +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -0,0 +1,144 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <vector> +#include "common/common_types.h" +#include "common/swap.h" +#include "core/hle/service/nvdrv/devices/nvdevice.h" + +namespace Service { +namespace Nvidia { +namespace Devices { + +class nvmap; +constexpr u32 NVGPU_IOCTL_MAGIC('H'); +constexpr u32 NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO(0x8); + +class nvhost_gpu final : public nvdevice { +public: + nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} + ~nvhost_gpu() override = default; + + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + +private: + enum class IoctlCommand : u32_le { + IocSetNVMAPfdCommand = 0x40044801, + IocSetClientDataCommand = 0x40084714, + IocGetClientDataCommand = 0x80084715, + IocZCullBind = 0xc010480b, + IocSetErrorNotifierCommand = 0xC018480C, + IocChannelSetPriorityCommand = 0x4004480D, + IocAllocGPFIFOEx2Command = 0xC020481A, + IocAllocObjCtxCommand = 0xC0104809, + }; + + enum class CtxObjects : u32_le { + Ctx2D = 0x902D, + Ctx3D = 0xB197, + CtxCompute = 0xB1C0, + CtxKepler = 0xA140, + CtxDMA = 0xB0B5, + CtxChannelGPFIFO = 0xB06F, + }; + + struct IoctlSetNvmapFD { + u32_le nvmap_fd; + }; + static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size"); + + struct IoctlClientData { + u64_le data; + }; + static_assert(sizeof(IoctlClientData) == 8, "IoctlClientData is incorrect size"); + + struct IoctlZCullBind { + u64_le gpu_va; + u32_le mode; // 0=global, 1=no_ctxsw, 2=separate_buffer, 3=part_of_regular_buf + INSERT_PADDING_WORDS(1); + }; + static_assert(sizeof(IoctlZCullBind) == 16, "IoctlZCullBind is incorrect size"); + + struct IoctlSetErrorNotifier { + u64_le offset; + u64_le size; + u32_le mem; // nvmap object handle + INSERT_PADDING_WORDS(1); + }; + static_assert(sizeof(IoctlSetErrorNotifier) == 24, "IoctlSetErrorNotifier is incorrect size"); + + struct IoctlFence { + u32_le id; + u32_le value; + }; + static_assert(sizeof(IoctlFence) == 8, "IoctlFence is incorrect size"); + + struct IoctlAllocGpfifoEx2 { + u32_le num_entries; // in + u32_le flags; // in + u32_le unk0; // in (1 works) + IoctlFence fence_out; // out + u32_le unk1; // in + u32_le unk2; // in + u32_le unk3; // in + }; + static_assert(sizeof(IoctlAllocGpfifoEx2) == 32, "IoctlAllocGpfifoEx2 is incorrect size"); + + struct IoctlAllocObjCtx { + u32_le class_num; // 0x902D=2d, 0xB197=3d, 0xB1C0=compute, 0xA140=kepler, 0xB0B5=DMA, + // 0xB06F=channel_gpfifo + u32_le flags; + u64_le obj_id; // (ignored) used for FREE_OBJ_CTX ioctl, which is not supported + }; + static_assert(sizeof(IoctlAllocObjCtx) == 16, "IoctlAllocObjCtx is incorrect size"); + + struct IoctlGpfifoEntry { + u32_le entry0; // gpu_va_lo + union { + u32_le entry1; // gpu_va_hi | (unk_0x02 << 0x08) | (size << 0x0A) | (unk_0x01 << 0x1F) + BitField<0, 8, u32_le> gpu_va_hi; + BitField<8, 2, u32_le> unk1; + BitField<10, 21, u32_le> sz; + BitField<31, 1, u32_le> unk2; + }; + + VAddr Address() const { + return (static_cast<VAddr>(gpu_va_hi) << 32) | entry0; + } + }; + static_assert(sizeof(IoctlGpfifoEntry) == 8, "IoctlGpfifoEntry is incorrect size"); + + struct IoctlSubmitGpfifo { + u64_le gpfifo; // (ignored) pointer to gpfifo fence structs + u32_le num_entries; // number of fence objects being submitted + u32_le flags; + IoctlFence fence_out; // returned new fence object for others to wait on + }; + static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(IoctlFence), + "submit_gpfifo is incorrect size"); + + u32_le nvmap_fd{}; + u64_le user_data{}; + IoctlZCullBind zcull_params{}; + u32_le channel_priority{}; + + u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); + u32 SetClientData(const std::vector<u8>& input, std::vector<u8>& output); + u32 GetClientData(const std::vector<u8>& input, std::vector<u8>& output); + u32 ZCullBind(const std::vector<u8>& input, std::vector<u8>& output); + u32 SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output); + u32 SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output); + u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output); + u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output); + u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output); + + std::shared_ptr<nvmap> nvmap_dev; +}; + +} // namespace Devices +} // namespace Nvidia +} // namespace Service diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index d37b5b159..b3842eb4c 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <algorithm> +#include <cinttypes> #include "common/assert.h" #include "common/logging/log.h" @@ -13,29 +14,28 @@ namespace Nvidia { namespace Devices { VAddr nvmap::GetObjectAddress(u32 handle) const { - auto itr = handles.find(handle); - ASSERT(itr != handles.end()); - - auto object = itr->second; + auto object = GetObject(handle); + ASSERT(object); ASSERT(object->status == Object::Status::Allocated); return object->addr; } -u32 nvmap::ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) { - switch (command) { - case IocCreateCommand: +u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { + switch (static_cast<IoctlCommand>(command.raw)) { + case IoctlCommand::Create: return IocCreate(input, output); - case IocAllocCommand: + case IoctlCommand::Alloc: return IocAlloc(input, output); - case IocGetIdCommand: + case IoctlCommand::GetId: return IocGetId(input, output); - case IocFromIdCommand: + case IoctlCommand::FromId: return IocFromId(input, output); - case IocParamCommand: + case IoctlCommand::Param: return IocParam(input, output); } UNIMPLEMENTED(); + return 0; } u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { @@ -51,7 +51,7 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { u32 handle = next_handle++; handles[handle] = std::move(object); - LOG_WARNING(Service, "(STUBBED) size 0x%08X", params.size); + LOG_DEBUG(Service_NVDRV, "size=0x%08X", params.size); params.handle = handle; @@ -63,17 +63,16 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { IocAllocParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - auto itr = handles.find(params.handle); - ASSERT(itr != handles.end()); + auto object = GetObject(params.handle); + ASSERT(object); - auto object = itr->second; object->flags = params.flags; object->align = params.align; object->kind = params.kind; object->addr = params.addr; object->status = Object::Status::Allocated; - LOG_WARNING(Service, "(STUBBED) Allocated address 0x%llx", params.addr); + LOG_DEBUG(Service_NVDRV, "called, addr=0x%" PRIx64, params.addr); std::memcpy(output.data(), ¶ms, sizeof(params)); return 0; @@ -83,12 +82,12 @@ u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) { IocGetIdParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service, "called"); + LOG_WARNING(Service_NVDRV, "called"); - auto itr = handles.find(params.handle); - ASSERT(itr != handles.end()); + auto object = GetObject(params.handle); + ASSERT(object); - params.id = itr->second->id; + params.id = object->id; std::memcpy(output.data(), ¶ms, sizeof(params)); return 0; @@ -98,17 +97,14 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) { IocFromIdParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service, "(STUBBED) called"); + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); auto itr = std::find_if(handles.begin(), handles.end(), [&](const auto& entry) { return entry.second->id == params.id; }); ASSERT(itr != handles.end()); - // Make a new handle for the object - u32 handle = next_handle++; - handles[handle] = itr->second; - - params.handle = handle; + // Return the existing handle instead of creating a new one. + params.handle = itr->first; std::memcpy(output.data(), ¶ms, sizeof(params)); return 0; @@ -120,12 +116,10 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) { IocParamParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service, "(STUBBED) called type=%u", params.type); - - auto itr = handles.find(params.handle); - ASSERT(itr != handles.end()); + LOG_WARNING(Service_NVDRV, "(STUBBED) called type=%u", params.type); - auto object = itr->second; + auto object = GetObject(params.handle); + ASSERT(object); ASSERT(object->status == Object::Status::Allocated); switch (static_cast<ParamTypes>(params.type)) { diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index 6954c0324..4681e438b 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h @@ -24,10 +24,9 @@ public: /// Returns the allocated address of an nvmap object given its handle. VAddr GetObjectAddress(u32 handle) const; - u32 ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; -private: - // Represents an nvmap object. + /// Represents an nvmap object. struct Object { enum class Status { Created, Allocated }; u32 id; @@ -39,21 +38,30 @@ private: Status status; }; + std::shared_ptr<Object> GetObject(u32 handle) const { + auto itr = handles.find(handle); + if (itr != handles.end()) { + return itr->second; + } + return {}; + } + +private: /// Id to use for the next handle that is created. u32 next_handle = 1; - // Id to use for the next object that is created. + /// Id to use for the next object that is created. u32 next_id = 1; /// Mapping of currently allocated handles to the objects they represent. std::unordered_map<u32, std::shared_ptr<Object>> handles; - enum IoctlCommands { - IocCreateCommand = 0xC0080101, - IocFromIdCommand = 0xC0080103, - IocAllocCommand = 0xC0200104, - IocParamCommand = 0xC00C0109, - IocGetIdCommand = 0xC008010E + enum class IoctlCommand : u32 { + Create = 0xC0080101, + FromId = 0xC0080103, + Alloc = 0xC0200104, + Param = 0xC00C0109, + GetId = 0xC008010E }; struct IocCreateParams { diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp index 0670ca155..c70370f1f 100644 --- a/src/core/hle/service/nvdrv/interface.cpp +++ b/src/core/hle/service/nvdrv/interface.cpp @@ -2,8 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cinttypes> #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/event.h" #include "core/hle/service/nvdrv/interface.h" #include "core/hle/service/nvdrv/nvdrv.h" @@ -11,58 +13,95 @@ namespace Service { namespace Nvidia { void NVDRV::Open(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + LOG_DEBUG(Service_NVDRV, "called"); - auto buffer = ctx.BufferDescriptorA()[0]; - - std::string device_name = Memory::ReadCString(buffer.Address(), buffer.Size()); + const auto& buffer = ctx.ReadBuffer(); + std::string device_name(buffer.begin(), buffer.end()); u32 fd = nvdrv->Open(device_name); - IPC::RequestBuilder rb{ctx, 4}; + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(fd); rb.Push<u32>(0); } void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + LOG_DEBUG(Service_NVDRV, "called"); IPC::RequestParser rp{ctx}; u32 fd = rp.Pop<u32>(); u32 command = rp.Pop<u32>(); - auto input_buffer = ctx.BufferDescriptorA()[0]; - auto output_buffer = ctx.BufferDescriptorB()[0]; + std::vector<u8> output(ctx.GetWriteBufferSize()); - std::vector<u8> input(input_buffer.Size()); - std::vector<u8> output(output_buffer.Size()); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output)); - Memory::ReadBlock(input_buffer.Address(), input.data(), input_buffer.Size()); + ctx.WriteBuffer(output); +} - u32 nv_result = nvdrv->Ioctl(fd, command, input, output); +void NVDRV::Close(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NVDRV, "called"); - Memory::WriteBlock(output_buffer.Address(), output.data(), output_buffer.Size()); + IPC::RequestParser rp{ctx}; + u32 fd = rp.Pop<u32>(); - IPC::RequestBuilder rb{ctx, 3}; - rb.Push(RESULT_SUCCESS); - rb.Push(nv_result); + auto result = nvdrv->Close(fd); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); } void NVDRV::Initialize(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestBuilder rb{ctx, 3}; + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); +} + +void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + u32 fd = rp.Pop<u32>(); + u32 event_id = rp.Pop<u32>(); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd=%x, event_id=%x", fd, event_id); + + IPC::ResponseBuilder rb{ctx, 3, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(query_event); + rb.Push<u32>(0); +} + +void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + pid = rp.Pop<u64>(); + + LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid=0x%" PRIx64, pid); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(0); } +void NVDRV::FinishInitialize(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name) : ServiceFramework(name), nvdrv(std::move(nvdrv)) { static const FunctionInfo functions[] = { {0, &NVDRV::Open, "Open"}, {1, &NVDRV::Ioctl, "Ioctl"}, + {2, &NVDRV::Close, "Close"}, {3, &NVDRV::Initialize, "Initialize"}, + {4, &NVDRV::QueryEvent, "QueryEvent"}, + {8, &NVDRV::SetClientPID, "SetClientPID"}, + {13, &NVDRV::FinishInitialize, "FinishInitialize"}, }; RegisterHandlers(functions); + + query_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "NVDRV::query_event"); } } // namespace Nvidia diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h index 8c95b7217..daf2302af 100644 --- a/src/core/hle/service/nvdrv/interface.h +++ b/src/core/hle/service/nvdrv/interface.h @@ -6,6 +6,7 @@ #include <memory> #include <string> +#include "core/hle/kernel/event.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/service.h" @@ -20,9 +21,17 @@ public: private: void Open(Kernel::HLERequestContext& ctx); void Ioctl(Kernel::HLERequestContext& ctx); + void Close(Kernel::HLERequestContext& ctx); void Initialize(Kernel::HLERequestContext& ctx); + void QueryEvent(Kernel::HLERequestContext& ctx); + void SetClientPID(Kernel::HLERequestContext& ctx); + void FinishInitialize(Kernel::HLERequestContext& ctx); std::shared_ptr<Module> nvdrv; + + u64 pid{}; + + Kernel::SharedPtr<Kernel::Event> query_event; }; } // namespace Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index cf525a875..ea00240e6 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -6,9 +6,13 @@ #include "core/hle/service/nvdrv/devices/nvdevice.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" +#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" +#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" +#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" #include "core/hle/service/nvdrv/devices/nvmap.h" -#include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvdrv/interface.h" +#include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvdrv/nvmemp.h" namespace Service { namespace Nvidia { @@ -19,14 +23,20 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { auto module_ = std::make_shared<Module>(); std::make_shared<NVDRV>(module_, "nvdrv")->InstallAsService(service_manager); std::make_shared<NVDRV>(module_, "nvdrv:a")->InstallAsService(service_manager); + std::make_shared<NVDRV>(module_, "nvdrv:s")->InstallAsService(service_manager); + std::make_shared<NVDRV>(module_, "nvdrv:t")->InstallAsService(service_manager); + std::make_shared<NVMEMP>()->InstallAsService(service_manager); nvdrv = module_; } Module::Module() { auto nvmap_dev = std::make_shared<Devices::nvmap>(); - devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(); + devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(nvmap_dev); + devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(nvmap_dev); + devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(); devices["/dev/nvmap"] = nvmap_dev; devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(nvmap_dev); + devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(); } u32 Module::Open(std::string device_name) { @@ -41,12 +51,22 @@ u32 Module::Open(std::string device_name) { return fd; } -u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 Module::Ioctl(u32 fd, u32_le command, const std::vector<u8>& input, std::vector<u8>& output) { auto itr = open_files.find(fd); ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); auto device = itr->second; - return device->ioctl(command, input, output); + return device->ioctl({command}, input, output); +} + +ResultCode Module::Close(u32 fd) { + auto itr = open_files.find(fd); + ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); + + open_files.erase(itr); + + // TODO(flerovium): return correct result code if operation failed. + return RESULT_SUCCESS; } } // namespace Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index 1940ced99..6a55ff96d 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -17,6 +17,13 @@ namespace Devices { class nvdevice; } +struct IoctlFence { + u32 id; + u32 value; +}; + +static_assert(sizeof(IoctlFence) == 8, "IoctlFence has wrong size"); + class Module final { public: Module(); @@ -35,6 +42,8 @@ public: u32 Open(std::string device_name); /// Sends an ioctl command to the specified file descriptor. u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output); + /// Closes a device file descriptor and returns operation success. + ResultCode Close(u32 fd); private: /// Id to use for the next open file descriptor. diff --git a/src/core/hle/service/nvdrv/nvmemp.cpp b/src/core/hle/service/nvdrv/nvmemp.cpp new file mode 100644 index 000000000..5a13732c7 --- /dev/null +++ b/src/core/hle/service/nvdrv/nvmemp.cpp @@ -0,0 +1,31 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvdrv/nvmemp.h" + +namespace Service { +namespace Nvidia { + +NVMEMP::NVMEMP() : ServiceFramework("nvmemp") { + static const FunctionInfo functions[] = { + {0, &NVMEMP::Unknown0, "Unknown0"}, + {1, &NVMEMP::Unknown1, "Unknown1"}, + }; + RegisterHandlers(functions); +} + +void NVMEMP::Unknown0(Kernel::HLERequestContext& ctx) { + UNIMPLEMENTED(); +} + +void NVMEMP::Unknown1(Kernel::HLERequestContext& ctx) { + UNIMPLEMENTED(); +} + +} // namespace Nvidia +} // namespace Service diff --git a/src/core/hle/service/nvdrv/nvmemp.h b/src/core/hle/service/nvdrv/nvmemp.h new file mode 100644 index 000000000..a6b5fbb82 --- /dev/null +++ b/src/core/hle/service/nvdrv/nvmemp.h @@ -0,0 +1,23 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Nvidia { + +class NVMEMP final : public ServiceFramework<NVMEMP> { +public: + NVMEMP(); + ~NVMEMP() = default; + +private: + void Unknown0(Kernel::HLERequestContext& ctx); + void Unknown1(Kernel::HLERequestContext& ctx); +}; + +} // namespace Nvidia +} // namespace Service diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp new file mode 100644 index 000000000..e4ff2e267 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -0,0 +1,115 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> + +#include "common/alignment.h" +#include "common/scope_exit.h" +#include "core/core_timing.h" +#include "core/hle/service/nvflinger/buffer_queue.h" + +namespace Service { +namespace NVFlinger { + +BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) { + native_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "BufferQueue NativeHandle"); + native_handle->Signal(); +} + +void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) { + Buffer buffer{}; + buffer.slot = slot; + buffer.igbp_buffer = igbp_buffer; + buffer.status = Buffer::Status::Free; + + LOG_WARNING(Service, "Adding graphics buffer %u", slot); + + queue.emplace_back(buffer); + + if (buffer_wait_event) { + buffer_wait_event->Signal(); + } +} + +boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) { + auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { + // Only consider free buffers. Buffers become free once again after they've been Acquired + // and Released by the compositor, see the NVFlinger::Compose method. + if (buffer.status != Buffer::Status::Free) { + return false; + } + + // Make sure that the parameters match. + return buffer.igbp_buffer.width == width && buffer.igbp_buffer.height == height; + }); + + if (itr == queue.end()) { + return boost::none; + } + + buffer_wait_event = nullptr; + + itr->status = Buffer::Status::Dequeued; + return itr->slot; +} + +const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const { + auto itr = std::find_if(queue.begin(), queue.end(), + [&](const Buffer& buffer) { return buffer.slot == slot; }); + ASSERT(itr != queue.end()); + ASSERT(itr->status == Buffer::Status::Dequeued); + return itr->igbp_buffer; +} + +void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform) { + auto itr = std::find_if(queue.begin(), queue.end(), + [&](const Buffer& buffer) { return buffer.slot == slot; }); + ASSERT(itr != queue.end()); + ASSERT(itr->status == Buffer::Status::Dequeued); + itr->status = Buffer::Status::Queued; + itr->transform = transform; +} + +boost::optional<const BufferQueue::Buffer&> BufferQueue::AcquireBuffer() { + auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) { + return buffer.status == Buffer::Status::Queued; + }); + if (itr == queue.end()) + return boost::none; + itr->status = Buffer::Status::Acquired; + return *itr; +} + +void BufferQueue::ReleaseBuffer(u32 slot) { + auto itr = std::find_if(queue.begin(), queue.end(), + [&](const Buffer& buffer) { return buffer.slot == slot; }); + ASSERT(itr != queue.end()); + ASSERT(itr->status == Buffer::Status::Acquired); + itr->status = Buffer::Status::Free; + + if (buffer_wait_event) { + buffer_wait_event->Signal(); + } +} + +u32 BufferQueue::Query(QueryType type) { + LOG_WARNING(Service, "(STUBBED) called type=%u", static_cast<u32>(type)); + switch (type) { + case QueryType::NativeWindowFormat: + // TODO(Subv): Use an enum for this + static constexpr u32 FormatABGR8 = 1; + return FormatABGR8; + } + + UNIMPLEMENTED(); + return 0; +} + +void BufferQueue::SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event) { + ASSERT_MSG(!buffer_wait_event, "buffer_wait_event only supports a single waiting thread!"); + buffer_wait_event = std::move(wait_event); +} + +} // namespace NVFlinger +} // namespace Service diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h new file mode 100644 index 000000000..1de5767cb --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue.h @@ -0,0 +1,102 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <vector> +#include <boost/optional.hpp> +#include "common/swap.h" +#include "core/hle/kernel/event.h" + +namespace CoreTiming { +struct EventType; +} + +namespace Service { +namespace NVFlinger { + +struct IGBPBuffer { + u32_le magic; + u32_le width; + u32_le height; + u32_le stride; + u32_le format; + u32_le usage; + INSERT_PADDING_WORDS(1); + u32_le index; + INSERT_PADDING_WORDS(3); + u32_le gpu_buffer_id; + INSERT_PADDING_WORDS(17); + u32_le nvmap_handle; + u32_le offset; + INSERT_PADDING_WORDS(60); +}; + +static_assert(sizeof(IGBPBuffer) == 0x16C, "IGBPBuffer has wrong size"); + +class BufferQueue final { +public: + enum class QueryType { + NativeWindowWidth = 0, + NativeWindowHeight = 1, + NativeWindowFormat = 2, + }; + + BufferQueue(u32 id, u64 layer_id); + ~BufferQueue() = default; + + enum class BufferTransformFlags : u32 { + /// No transform flags are set + Unset = 0x00, + /// Flip source image horizontally (around the vertical axis) + FlipH = 0x01, + /// Flip source image vertically (around the horizontal axis) + FlipV = 0x02, + /// Rotate source image 90 degrees clockwise + Rotate90 = 0x04, + /// Rotate source image 180 degrees + Roate180 = 0x03, + /// Rotate source image 270 degrees clockwise + Roate270 = 0x07, + }; + + struct Buffer { + enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 }; + + u32 slot; + Status status = Status::Free; + IGBPBuffer igbp_buffer; + BufferTransformFlags transform; + }; + + void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer); + boost::optional<u32> DequeueBuffer(u32 width, u32 height); + const IGBPBuffer& RequestBuffer(u32 slot) const; + void QueueBuffer(u32 slot, BufferTransformFlags transform); + boost::optional<const Buffer&> AcquireBuffer(); + void ReleaseBuffer(u32 slot); + u32 Query(QueryType type); + void SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event); + + u32 GetId() const { + return id; + } + + Kernel::SharedPtr<Kernel::Event> GetNativeHandle() const { + return native_handle; + } + +private: + u32 id; + u64 layer_id; + + std::vector<Buffer> queue; + Kernel::SharedPtr<Kernel::Event> native_handle; + + /// Used to signal waiting thread when no buffers are available + Kernel::SharedPtr<Kernel::Event> buffer_wait_event; +}; + +} // namespace NVFlinger +} // namespace Service diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp new file mode 100644 index 000000000..0d30f54dc --- /dev/null +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -0,0 +1,166 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> + +#include "common/alignment.h" +#include "common/scope_exit.h" +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" +#include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvflinger/buffer_queue.h" +#include "core/hle/service/nvflinger/nvflinger.h" +#include "video_core/renderer_base.h" +#include "video_core/video_core.h" + +namespace Service { +namespace NVFlinger { + +constexpr size_t SCREEN_REFRESH_RATE = 60; +constexpr u64 frame_ticks = static_cast<u64>(BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); + +NVFlinger::NVFlinger() { + // Add the different displays to the list of displays. + Display default_{0, "Default"}; + Display external{1, "External"}; + Display edid{2, "Edid"}; + Display internal{3, "Internal"}; + + displays.emplace_back(default_); + displays.emplace_back(external); + displays.emplace_back(edid); + displays.emplace_back(internal); + + // Schedule the screen composition events + composition_event = + CoreTiming::RegisterEvent("ScreenCompositioin", [this](u64 userdata, int cycles_late) { + Compose(); + CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event); + }); + + CoreTiming::ScheduleEvent(frame_ticks, composition_event); +} + +NVFlinger::~NVFlinger() { + CoreTiming::UnscheduleEvent(composition_event, 0); +} + +u64 NVFlinger::OpenDisplay(const std::string& name) { + LOG_WARNING(Service, "Opening display %s", name.c_str()); + + // TODO(Subv): Currently we only support the Default display. + ASSERT(name == "Default"); + + auto itr = std::find_if(displays.begin(), displays.end(), + [&](const Display& display) { return display.name == name; }); + + ASSERT(itr != displays.end()); + + return itr->id; +} + +u64 NVFlinger::CreateLayer(u64 display_id) { + auto& display = GetDisplay(display_id); + + ASSERT_MSG(display.layers.empty(), "Only one layer is supported per display at the moment"); + + u64 layer_id = next_layer_id++; + u32 buffer_queue_id = next_buffer_queue_id++; + auto buffer_queue = std::make_shared<BufferQueue>(buffer_queue_id, layer_id); + display.layers.emplace_back(layer_id, buffer_queue); + buffer_queues.emplace_back(std::move(buffer_queue)); + return layer_id; +} + +u32 NVFlinger::GetBufferQueueId(u64 display_id, u64 layer_id) { + const auto& layer = GetLayer(display_id, layer_id); + return layer.buffer_queue->GetId(); +} + +Kernel::SharedPtr<Kernel::Event> NVFlinger::GetVsyncEvent(u64 display_id) { + const auto& display = GetDisplay(display_id); + return display.vsync_event; +} + +std::shared_ptr<BufferQueue> NVFlinger::GetBufferQueue(u32 id) const { + auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(), + [&](const auto& queue) { return queue->GetId() == id; }); + + ASSERT(itr != buffer_queues.end()); + return *itr; +} + +Display& NVFlinger::GetDisplay(u64 display_id) { + auto itr = std::find_if(displays.begin(), displays.end(), + [&](const Display& display) { return display.id == display_id; }); + + ASSERT(itr != displays.end()); + return *itr; +} + +Layer& NVFlinger::GetLayer(u64 display_id, u64 layer_id) { + auto& display = GetDisplay(display_id); + + auto itr = std::find_if(display.layers.begin(), display.layers.end(), + [&](const Layer& layer) { return layer.id == layer_id; }); + + ASSERT(itr != display.layers.end()); + return *itr; +} + +void NVFlinger::Compose() { + for (auto& display : displays) { + // Trigger vsync for this display at the end of drawing + SCOPE_EXIT({ display.vsync_event->Signal(); }); + + // Don't do anything for displays without layers. + if (display.layers.empty()) + continue; + + // TODO(Subv): Support more than 1 layer. + ASSERT_MSG(display.layers.size() == 1, "Max 1 layer per display is supported"); + + Layer& layer = display.layers[0]; + auto& buffer_queue = layer.buffer_queue; + + // Search for a queued buffer and acquire it + auto buffer = buffer_queue->AcquireBuffer(); + + if (buffer == boost::none) { + // There was no queued buffer to draw, render previous frame + Core::System::GetInstance().perf_stats.EndGameFrame(); + VideoCore::g_renderer->SwapBuffers({}); + continue; + } + + auto& igbp_buffer = buffer->igbp_buffer; + + // Now send the buffer to the GPU for drawing. + auto nvdrv = Nvidia::nvdrv.lock(); + ASSERT(nvdrv); + + // TODO(Subv): Support more than just disp0. The display device selection is probably based + // on which display we're drawing (Default, Internal, External, etc) + auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0"); + ASSERT(nvdisp); + + nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format, + igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->transform); + + buffer_queue->ReleaseBuffer(buffer->slot); + + // TODO(Subv): Figure out when we should actually signal this event. + buffer_queue->GetNativeHandle()->Signal(); + } +} + +Layer::Layer(u64 id, std::shared_ptr<BufferQueue> queue) : id(id), buffer_queue(std::move(queue)) {} + +Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) { + vsync_event = Kernel::Event::Create(Kernel::ResetType::Pulse, "Display VSync Event"); +} + +} // namespace NVFlinger +} // namespace Service diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h new file mode 100644 index 000000000..3126018ad --- /dev/null +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -0,0 +1,84 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <boost/optional.hpp> +#include "core/hle/kernel/event.h" + +namespace CoreTiming { +struct EventType; +} + +namespace Service { +namespace NVFlinger { + +class BufferQueue; + +struct Layer { + Layer(u64 id, std::shared_ptr<BufferQueue> queue); + ~Layer() = default; + + u64 id; + std::shared_ptr<BufferQueue> buffer_queue; +}; + +struct Display { + Display(u64 id, std::string name); + ~Display() = default; + + u64 id; + std::string name; + + std::vector<Layer> layers; + Kernel::SharedPtr<Kernel::Event> vsync_event; +}; + +class NVFlinger final { +public: + NVFlinger(); + ~NVFlinger(); + + /// Opens the specified display and returns the id. + u64 OpenDisplay(const std::string& name); + + /// Creates a layer on the specified display and returns the layer id. + u64 CreateLayer(u64 display_id); + + /// Gets the buffer queue id of the specified layer in the specified display. + u32 GetBufferQueueId(u64 display_id, u64 layer_id); + + /// Gets the vsync event for the specified display. + Kernel::SharedPtr<Kernel::Event> GetVsyncEvent(u64 display_id); + + /// Obtains a buffer queue identified by the id. + std::shared_ptr<BufferQueue> GetBufferQueue(u32 id) const; + + /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when + /// finished. + void Compose(); + +private: + /// Returns the display identified by the specified id. + Display& GetDisplay(u64 display_id); + + /// Returns the layer identified by the specified id in the desired display. + Layer& GetLayer(u64 display_id, u64 layer_id); + + std::vector<Display> displays; + std::vector<std::shared_ptr<BufferQueue>> buffer_queues; + + /// Id to use for the next layer that is created, this counter is shared among all displays. + u64 next_layer_id = 1; + /// Id to use for the next buffer queue that is created, this counter is shared among all + /// layers. + u32 next_buffer_queue_id = 1; + + /// CoreTiming event that handles screen composition. + CoreTiming::EventType* composition_event; +}; + +} // namespace NVFlinger +} // namespace Service diff --git a/src/core/hle/service/pctl/pctl_a.cpp b/src/core/hle/service/pctl/pctl_a.cpp index 7978aecb8..c65fffa07 100644 --- a/src/core/hle/service/pctl/pctl_a.cpp +++ b/src/core/hle/service/pctl/pctl_a.cpp @@ -15,10 +15,10 @@ public: }; void PCTL_A::GetService(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IParentalControlService>(); - LOG_DEBUG(Service, "called"); + LOG_DEBUG(Service_PCTL, "called"); } PCTL_A::PCTL_A() : ServiceFramework("pctl:a") { diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index fe76b381c..8011d0d71 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -7,6 +7,7 @@ #include "common/assert.h" #include "common/logging/log.h" #include "common/string_util.h" +#include "core/core.h" #include "core/hle/ipc.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" @@ -19,13 +20,23 @@ #include "core/hle/service/aoc/aoc_u.h" #include "core/hle/service/apm/apm.h" #include "core/hle/service/audio/audio.h" +#include "core/hle/service/fatal/fatal.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/friend/friend.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/lm/lm.h" +#include "core/hle/service/nfp/nfp.h" +#include "core/hle/service/nifm/nifm.h" +#include "core/hle/service/ns/ns.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/pctl/pctl.h" #include "core/hle/service/service.h" +#include "core/hle/service/set/settings.h" #include "core/hle/service/sm/controller.h" #include "core/hle/service/sm/sm.h" +#include "core/hle/service/sockets/sockets.h" +#include "core/hle/service/spl/module.h" +#include "core/hle/service/ssl/ssl.h" #include "core/hle/service/time/time.h" #include "core/hle/service/vi/vi.h" @@ -129,7 +140,7 @@ void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) { ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& context) { switch (context.GetCommandType()) { case IPC::CommandType::Close: { - IPC::RequestBuilder rb{context, 1}; + IPC::ResponseBuilder rb{context, 2}; rb.Push(RESULT_SUCCESS); return ResultCode(ErrorModule::HIPC, ErrorDescription::RemoteProcessDead); } @@ -142,12 +153,10 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co break; } default: - UNIMPLEMENTED_MSG("command_type=%d", context.GetCommandType()); + UNIMPLEMENTED_MSG("command_type=%d", static_cast<int>(context.GetCommandType())); } - u32* cmd_buf = (u32*)Memory::GetPointer(Kernel::GetCurrentThread()->GetTLSAddress()); - context.WriteToOutgoingCommandBuffer(cmd_buf, *Kernel::g_current_process, - Kernel::g_handle_table); + context.WriteToOutgoingCommandBuffer(*Kernel::GetCurrentThread()); return RESULT_SUCCESS; } @@ -162,20 +171,34 @@ void AddNamedPort(std::string name, SharedPtr<ClientPort> port) { /// Initialize ServiceManager void Init() { + // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it + // here and pass it into the respective InstallInterfaces functions. + auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(); + SM::g_service_manager = std::make_shared<SM::ServiceManager>(); SM::ServiceManager::InstallInterfaces(SM::g_service_manager); Account::InstallInterfaces(*SM::g_service_manager); - AM::InstallInterfaces(*SM::g_service_manager); + AM::InstallInterfaces(*SM::g_service_manager, nv_flinger); AOC::InstallInterfaces(*SM::g_service_manager); APM::InstallInterfaces(*SM::g_service_manager); Audio::InstallInterfaces(*SM::g_service_manager); + Fatal::InstallInterfaces(*SM::g_service_manager); + FileSystem::InstallInterfaces(*SM::g_service_manager); + Friend::InstallInterfaces(*SM::g_service_manager); HID::InstallInterfaces(*SM::g_service_manager); LM::InstallInterfaces(*SM::g_service_manager); + NFP::InstallInterfaces(*SM::g_service_manager); + NIFM::InstallInterfaces(*SM::g_service_manager); + NS::InstallInterfaces(*SM::g_service_manager); Nvidia::InstallInterfaces(*SM::g_service_manager); PCTL::InstallInterfaces(*SM::g_service_manager); + Sockets::InstallInterfaces(*SM::g_service_manager); + SPL::InstallInterfaces(*SM::g_service_manager); + SSL::InstallInterfaces(*SM::g_service_manager); Time::InstallInterfaces(*SM::g_service_manager); - VI::InstallInterfaces(*SM::g_service_manager); + VI::InstallInterfaces(*SM::g_service_manager, nv_flinger); + Set::InstallInterfaces(*SM::g_service_manager); LOG_DEBUG(Service, "initialized OK"); } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 8e1c5b399..9c2e826da 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -21,7 +21,7 @@ class ClientPort; class ServerPort; class ServerSession; class HLERequestContext; -} +} // namespace Kernel namespace Service { @@ -189,4 +189,4 @@ extern std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_ /// Adds a port to the named port table void AddNamedPort(std::string name, Kernel::SharedPtr<Kernel::ClientPort> port); -} // namespace +} // namespace Service diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp new file mode 100644 index 000000000..aa7c924e7 --- /dev/null +++ b/src/core/hle/service/set/set.cpp @@ -0,0 +1,44 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <chrono> +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/client_port.h" +#include "core/hle/kernel/client_session.h" +#include "core/hle/service/set/set.h" + +namespace Service { +namespace Set { + +void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + u32 id = rp.Pop<u32>(); + constexpr std::array<u8, 13> lang_codes{}; + + ctx.WriteBuffer(lang_codes.data(), lang_codes.size()); + + IPC::ResponseBuilder rb{ctx, 2}; + + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_SET, "(STUBBED) called"); +} + +SET::SET() : ServiceFramework("set") { + static const FunctionInfo functions[] = { + {0, nullptr, "GetLanguageCode"}, + {1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"}, + {2, nullptr, "MakeLanguageCode"}, + {3, nullptr, "GetAvailableLanguageCodeCount"}, + {4, nullptr, "GetRegionCode"}, + {5, nullptr, "GetAvailableLanguageCodes2"}, + {6, nullptr, "GetAvailableLanguageCodeCount2"}, + {7, nullptr, "GetKeyCodeMap"}, + }; + RegisterHandlers(functions); +} + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h new file mode 100644 index 000000000..7b7814ed1 --- /dev/null +++ b/src/core/hle/service/set/set.h @@ -0,0 +1,22 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Set { + +class SET final : public ServiceFramework<SET> { +public: + explicit SET(); + ~SET() = default; + +private: + void GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx); +}; + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp new file mode 100644 index 000000000..6231acd96 --- /dev/null +++ b/src/core/hle/service/set/set_cal.cpp @@ -0,0 +1,40 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/set/set_cal.h" + +namespace Service { +namespace Set { + +SET_CAL::SET_CAL() : ServiceFramework("set:cal") { + static const FunctionInfo functions[] = { + {0, nullptr, "GetBluetoothBdAddress"}, + {1, nullptr, "GetConfigurationId1"}, + {2, nullptr, "GetAccelerometerOffset"}, + {3, nullptr, "GetAccelerometerScale"}, + {4, nullptr, "GetGyroscopeOffset"}, + {5, nullptr, "GetGyroscopeScale"}, + {6, nullptr, "GetWirelessLanMacAddress"}, + {7, nullptr, "GetWirelessLanCountryCodeCount"}, + {8, nullptr, "GetWirelessLanCountryCodes"}, + {9, nullptr, "GetSerialNumber"}, + {10, nullptr, "SetInitialSystemAppletProgramId"}, + {11, nullptr, "SetOverlayDispProgramId"}, + {12, nullptr, "GetBatteryLot"}, + {14, nullptr, "GetEciDeviceCertificate"}, + {15, nullptr, "GetEticketDeviceCertificate"}, + {16, nullptr, "GetSslKey"}, + {17, nullptr, "GetSslCertificate"}, + {18, nullptr, "GetGameCardKey"}, + {19, nullptr, "GetGameCardCertificate"}, + {20, nullptr, "GetEciDeviceKey"}, + {21, nullptr, "GetEticketDeviceKey"}, + {22, nullptr, "GetSpeakerParameter"}, + {23, nullptr, "GetLcdVendorId"}, + }; + RegisterHandlers(functions); +} + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/set_cal.h b/src/core/hle/service/set/set_cal.h new file mode 100644 index 000000000..9c0b851d0 --- /dev/null +++ b/src/core/hle/service/set/set_cal.h @@ -0,0 +1,19 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Set { + +class SET_CAL final : public ServiceFramework<SET_CAL> { +public: + explicit SET_CAL(); + ~SET_CAL() = default; +}; + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/set_fd.cpp b/src/core/hle/service/set/set_fd.cpp new file mode 100644 index 000000000..8320d4250 --- /dev/null +++ b/src/core/hle/service/set/set_fd.cpp @@ -0,0 +1,25 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/set/set_fd.h" + +namespace Service { +namespace Set { + +SET_FD::SET_FD() : ServiceFramework("set:fd") { + static const FunctionInfo functions[] = { + {2, nullptr, "SetSettingsItemValue"}, + {3, nullptr, "ResetSettingsItemValue"}, + {4, nullptr, "CreateSettingsItemKeyIterator"}, + {10, nullptr, "ReadSettings"}, + {11, nullptr, "ResetSettings"}, + {20, nullptr, "SetWebInspectorFlag"}, + {21, nullptr, "SetAllowedSslHosts"}, + {22, nullptr, "SetHostFsMountPoint"}, + }; + RegisterHandlers(functions); +} + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/set_fd.h b/src/core/hle/service/set/set_fd.h new file mode 100644 index 000000000..65b36bcb3 --- /dev/null +++ b/src/core/hle/service/set/set_fd.h @@ -0,0 +1,19 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Set { + +class SET_FD final : public ServiceFramework<SET_FD> { +public: + explicit SET_FD(); + ~SET_FD() = default; +}; + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp new file mode 100644 index 000000000..363abd10a --- /dev/null +++ b/src/core/hle/service/set/set_sys.cpp @@ -0,0 +1,167 @@ +// 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/service/set/set_sys.h" + +namespace Service { +namespace Set { + +void SET_SYS::GetColorSetId(Kernel::HLERequestContext& ctx) { + + IPC::ResponseBuilder rb{ctx, 3}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); + + LOG_WARNING(Service_SET, "(STUBBED) called"); +} + +SET_SYS::SET_SYS() : ServiceFramework("set:sys") { + static const FunctionInfo functions[] = { + {0, nullptr, "SetLanguageCode"}, + {1, nullptr, "SetNetworkSettings"}, + {2, nullptr, "GetNetworkSettings"}, + {3, nullptr, "GetFirmwareVersion"}, + {4, nullptr, "GetFirmwareVersion2"}, + {7, nullptr, "GetLockScreenFlag"}, + {8, nullptr, "SetLockScreenFlag"}, + {9, nullptr, "GetBacklightSettings"}, + {10, nullptr, "SetBacklightSettings"}, + {11, nullptr, "SetBluetoothDevicesSettings"}, + {12, nullptr, "GetBluetoothDevicesSettings"}, + {13, nullptr, "GetExternalSteadyClockSourceId"}, + {14, nullptr, "SetExternalSteadyClockSourceId"}, + {15, nullptr, "GetUserSystemClockContext"}, + {16, nullptr, "SetUserSystemClockContext"}, + {17, nullptr, "GetAccountSettings"}, + {18, nullptr, "SetAccountSettings"}, + {19, nullptr, "GetAudioVolume"}, + {20, nullptr, "SetAudioVolume"}, + {21, nullptr, "GetEulaVersions"}, + {22, nullptr, "SetEulaVersions"}, + {23, &SET_SYS::GetColorSetId, "GetColorSetId"}, + {24, nullptr, "SetColorSetId"}, + {25, nullptr, "GetConsoleInformationUploadFlag"}, + {26, nullptr, "SetConsoleInformationUploadFlag"}, + {27, nullptr, "GetAutomaticApplicationDownloadFlag"}, + {28, nullptr, "SetAutomaticApplicationDownloadFlag"}, + {29, nullptr, "GetNotificationSettings"}, + {30, nullptr, "SetNotificationSettings"}, + {31, nullptr, "GetAccountNotificationSettings"}, + {32, nullptr, "SetAccountNotificationSettings"}, + {35, nullptr, "GetVibrationMasterVolume"}, + {36, nullptr, "SetVibrationMasterVolume"}, + {37, nullptr, "GetSettingsItemValueSize"}, + {38, nullptr, "GetSettingsItemValue"}, + {39, nullptr, "GetTvSettings"}, + {40, nullptr, "SetTvSettings"}, + {41, nullptr, "GetEdid"}, + {42, nullptr, "SetEdid"}, + {43, nullptr, "GetAudioOutputMode"}, + {44, nullptr, "SetAudioOutputMode"}, + {45, nullptr, "IsForceMuteOnHeadphoneRemoved"}, + {46, nullptr, "SetForceMuteOnHeadphoneRemoved"}, + {47, nullptr, "GetQuestFlag"}, + {48, nullptr, "SetQuestFlag"}, + {49, nullptr, "GetDataDeletionSettings"}, + {50, nullptr, "SetDataDeletionSettings"}, + {51, nullptr, "GetInitialSystemAppletProgramId"}, + {52, nullptr, "GetOverlayDispProgramId"}, + {53, nullptr, "GetDeviceTimeZoneLocationName"}, + {54, nullptr, "SetDeviceTimeZoneLocationName"}, + {55, nullptr, "GetWirelessCertificationFileSize"}, + {56, nullptr, "GetWirelessCertificationFile"}, + {57, nullptr, "SetRegionCode"}, + {58, nullptr, "GetNetworkSystemClockContext"}, + {59, nullptr, "SetNetworkSystemClockContext"}, + {60, nullptr, "IsUserSystemClockAutomaticCorrectionEnabled"}, + {61, nullptr, "SetUserSystemClockAutomaticCorrectionEnabled"}, + {62, nullptr, "GetDebugModeFlag"}, + {63, nullptr, "GetPrimaryAlbumStorage"}, + {64, nullptr, "SetPrimaryAlbumStorage"}, + {65, nullptr, "GetUsb30EnableFlag"}, + {66, nullptr, "SetUsb30EnableFlag"}, + {67, nullptr, "GetBatteryLot"}, + {68, nullptr, "GetSerialNumber"}, + {69, nullptr, "GetNfcEnableFlag"}, + {70, nullptr, "SetNfcEnableFlag"}, + {71, nullptr, "GetSleepSettings"}, + {72, nullptr, "SetSleepSettings"}, + {73, nullptr, "GetWirelessLanEnableFlag"}, + {74, nullptr, "SetWirelessLanEnableFlag"}, + {75, nullptr, "GetInitialLaunchSettings"}, + {76, nullptr, "SetInitialLaunchSettings"}, + {77, nullptr, "GetDeviceNickName"}, + {78, nullptr, "SetDeviceNickName"}, + {79, nullptr, "GetProductModel"}, + {80, nullptr, "GetLdnChannel"}, + {81, nullptr, "SetLdnChannel"}, + {82, nullptr, "AcquireTelemetryDirtyFlagEventHandle"}, + {83, nullptr, "GetTelemetryDirtyFlags"}, + {84, nullptr, "GetPtmBatteryLot"}, + {85, nullptr, "SetPtmBatteryLot"}, + {86, nullptr, "GetPtmFuelGaugeParameter"}, + {87, nullptr, "SetPtmFuelGaugeParameter"}, + {88, nullptr, "GetBluetoothEnableFlag"}, + {89, nullptr, "SetBluetoothEnableFlag"}, + {90, nullptr, "GetMiiAuthorId"}, + {91, nullptr, "SetShutdownRtcValue"}, + {92, nullptr, "GetShutdownRtcValue"}, + {93, nullptr, "AcquireFatalDirtyFlagEventHandle"}, + {94, nullptr, "GetFatalDirtyFlags"}, + {95, nullptr, "GetAutoUpdateEnableFlag"}, + {96, nullptr, "SetAutoUpdateEnableFlag"}, + {97, nullptr, "GetNxControllerSettings"}, + {98, nullptr, "SetNxControllerSettings"}, + {99, nullptr, "GetBatteryPercentageFlag"}, + {100, nullptr, "SetBatteryPercentageFlag"}, + {101, nullptr, "GetExternalRtcResetFlag"}, + {102, nullptr, "SetExternalRtcResetFlag"}, + {103, nullptr, "GetUsbFullKeyEnableFlag"}, + {104, nullptr, "SetUsbFullKeyEnableFlag"}, + {105, nullptr, "SetExternalSteadyClockInternalOffset"}, + {106, nullptr, "GetExternalSteadyClockInternalOffset"}, + {107, nullptr, "GetBacklightSettingsEx"}, + {108, nullptr, "SetBacklightSettingsEx"}, + {109, nullptr, "GetHeadphoneVolumeWarningCount"}, + {110, nullptr, "SetHeadphoneVolumeWarningCount"}, + {111, nullptr, "GetBluetoothAfhEnableFlag"}, + {112, nullptr, "SetBluetoothAfhEnableFlag"}, + {113, nullptr, "GetBluetoothBoostEnableFlag"}, + {114, nullptr, "SetBluetoothBoostEnableFlag"}, + {115, nullptr, "GetInRepairProcessEnableFlag"}, + {116, nullptr, "SetInRepairProcessEnableFlag"}, + {117, nullptr, "GetHeadphoneVolumeUpdateFlag"}, + {118, nullptr, "SetHeadphoneVolumeUpdateFlag"}, + {119, nullptr, "NeedsToUpdateHeadphoneVolume"}, + {120, nullptr, "GetPushNotificationActivityModeOnSleep"}, + {121, nullptr, "SetPushNotificationActivityModeOnSleep"}, + {122, nullptr, "GetServiceDiscoveryControlSettings"}, + {123, nullptr, "SetServiceDiscoveryControlSettings"}, + {124, nullptr, "GetErrorReportSharePermission"}, + {125, nullptr, "SetErrorReportSharePermission"}, + {126, nullptr, "GetAppletLaunchFlags"}, + {127, nullptr, "SetAppletLaunchFlags"}, + {128, nullptr, "GetConsoleSixAxisSensorAccelerationBias"}, + {129, nullptr, "SetConsoleSixAxisSensorAccelerationBias"}, + {130, nullptr, "GetConsoleSixAxisSensorAngularVelocityBias"}, + {131, nullptr, "SetConsoleSixAxisSensorAngularVelocityBias"}, + {132, nullptr, "GetConsoleSixAxisSensorAccelerationGain"}, + {133, nullptr, "SetConsoleSixAxisSensorAccelerationGain"}, + {134, nullptr, "GetConsoleSixAxisSensorAngularVelocityGain"}, + {135, nullptr, "SetConsoleSixAxisSensorAngularVelocityGain"}, + {136, nullptr, "GetKeyboardLayout"}, + {137, nullptr, "SetKeyboardLayout"}, + {138, nullptr, "GetWebInspectorFlag"}, + {139, nullptr, "GetAllowedSslHosts"}, + {140, nullptr, "GetHostFsMountPoint"}, + }; + RegisterHandlers(functions); +} + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h new file mode 100644 index 000000000..105f1a3c7 --- /dev/null +++ b/src/core/hle/service/set/set_sys.h @@ -0,0 +1,22 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Set { + +class SET_SYS final : public ServiceFramework<SET_SYS> { +public: + explicit SET_SYS(); + ~SET_SYS() = default; + +private: + void GetColorSetId(Kernel::HLERequestContext& ctx); +}; + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/settings.cpp b/src/core/hle/service/set/settings.cpp new file mode 100644 index 000000000..c6bc9e240 --- /dev/null +++ b/src/core/hle/service/set/settings.cpp @@ -0,0 +1,22 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/set/set.h" +#include "core/hle/service/set/set_cal.h" +#include "core/hle/service/set/set_fd.h" +#include "core/hle/service/set/set_sys.h" +#include "core/hle/service/set/settings.h" + +namespace Service { +namespace Set { + +void InstallInterfaces(SM::ServiceManager& service_manager) { + std::make_shared<SET>()->InstallAsService(service_manager); + std::make_shared<SET_CAL>()->InstallAsService(service_manager); + std::make_shared<SET_FD>()->InstallAsService(service_manager); + std::make_shared<SET_SYS>()->InstallAsService(service_manager); +} + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/settings.h b/src/core/hle/service/set/settings.h new file mode 100644 index 000000000..6c8d5a58c --- /dev/null +++ b/src/core/hle/service/set/settings.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Set { + +/// Registers all Settings services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp index e8f5d0e4a..e12c53442 100644 --- a/src/core/hle/service/sm/controller.cpp +++ b/src/core/hle/service/sm/controller.cpp @@ -4,36 +4,43 @@ #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/domain.h" +#include "core/hle/kernel/session.h" #include "core/hle/service/sm/controller.h" namespace Service { namespace SM { void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) { - auto domain = Kernel::Domain::CreateFromSession(*ctx.ServerSession()->parent).Unwrap(); + ASSERT_MSG(!ctx.Session()->IsDomain(), "session is alread a domain"); + ctx.Session()->ConvertToDomain(); - IPC::RequestBuilder rb{ctx, 3}; + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(static_cast<u32>(domain->request_handlers.size())); + rb.Push<u32>(1); // Converted sessions start with 1 request handler - LOG_DEBUG(Service, "called, domain=%d", domain->GetObjectId()); + LOG_DEBUG(Service, "called, server_session=%d", ctx.Session()->GetObjectId()); } void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 1}; + // TODO(bunnei): This is just creating a new handle to the same Session. I assume this is wrong + // and that we probably want to actually make an entirely new Session, but we still need to + // verify this on hardware. + IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; rb.Push(RESULT_SUCCESS); - // TODO(Subv): Check if this is correct - if (ctx.IsDomain()) - rb.PushMoveObjects(ctx.Domain()); - else - rb.PushMoveObjects(ctx.ServerSession()); + Kernel::SharedPtr<Kernel::ClientSession> session{ctx.Session()->parent->client}; + rb.PushMoveObjects(session); - LOG_DEBUG(Service, "called"); + LOG_DEBUG(Service, "called, session=%u", session->GetObjectId()); +} + +void Controller::DuplicateSessionEx(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called, using DuplicateSession"); + + DuplicateSession(ctx); } void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 3}; + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(0x500); @@ -46,7 +53,7 @@ Controller::Controller() : ServiceFramework("IpcController") { {0x00000001, nullptr, "ConvertDomainToSession"}, {0x00000002, &Controller::DuplicateSession, "DuplicateSession"}, {0x00000003, &Controller::QueryPointerBufferSize, "QueryPointerBufferSize"}, - {0x00000004, nullptr, "DuplicateSessionEx"}, + {0x00000004, &Controller::DuplicateSessionEx, "DuplicateSessionEx"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/sm/controller.h b/src/core/hle/service/sm/controller.h index 0b873b492..7b4bc4b75 100644 --- a/src/core/hle/service/sm/controller.h +++ b/src/core/hle/service/sm/controller.h @@ -17,6 +17,7 @@ public: private: void ConvertSessionToDomain(Kernel::HLERequestContext& ctx); void DuplicateSession(Kernel::HLERequestContext& ctx); + void DuplicateSessionEx(Kernel::HLERequestContext& ctx); void QueryPointerBufferSize(Kernel::HLERequestContext& ctx); }; diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index f3bffac54..bc72512a0 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -83,7 +83,7 @@ std::shared_ptr<ServiceManager> g_service_manager; * 0: ResultCode */ void SM::Initialize(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 1}; + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); LOG_DEBUG(Service_SM, "called"); } @@ -99,10 +99,13 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { auto client_port = service_manager->GetServicePort(name); if (client_port.Failed()) { - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0, 0, 0); + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); rb.Push(client_port.Code()); LOG_ERROR(Service_SM, "called service=%s -> error 0x%08X", name.c_str(), client_port.Code().raw); + if (name.length() == 0) + return; // LibNX Fix + UNIMPLEMENTED(); return; } @@ -111,7 +114,8 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { if (session.Succeeded()) { LOG_DEBUG(Service_SM, "called service=%s -> session=%u", name.c_str(), (*session)->GetObjectId()); - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0, 1, 0); + IPC::ResponseBuilder rb = + rp.MakeBuilder(2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles); rb.Push(session.Code()); rb.PushMoveObjects(std::move(session).Unwrap()); } diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp new file mode 100644 index 000000000..790ff82b3 --- /dev/null +++ b/src/core/hle/service/sockets/bsd.cpp @@ -0,0 +1,90 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/sockets/bsd.h" + +namespace Service { +namespace Sockets { + +void BSD::RegisterClient(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); // bsd errno +} + +void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); // bsd errno +} + +void BSD::Socket(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + u32 domain = rp.Pop<u32>(); + u32 type = rp.Pop<u32>(); + u32 protocol = rp.Pop<u32>(); + + LOG_WARNING(Service, "(STUBBED) called domain=%u type=%u protocol=%u", domain, type, protocol); + + u32 fd = next_fd++; + + IPC::ResponseBuilder rb{ctx, 4}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(fd); + rb.Push<u32>(0); // bsd errno +} + +void BSD::Connect(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 4}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); // ret + rb.Push<u32>(0); // bsd errno +} + +void BSD::SendTo(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 4}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); // ret + rb.Push<u32>(0); // bsd errno +} + +void BSD::Close(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 4}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); // ret + rb.Push<u32>(0); // bsd errno +} + +BSD::BSD(const char* name) : ServiceFramework(name) { + static const FunctionInfo functions[] = { + {0, &BSD::RegisterClient, "RegisterClient"}, + {1, &BSD::StartMonitoring, "StartMonitoring"}, + {2, &BSD::Socket, "Socket"}, + {11, &BSD::SendTo, "SendTo"}, + {14, &BSD::Connect, "Connect"}, + {26, &BSD::Close, "Close"}, + }; + RegisterHandlers(functions); +} + +} // namespace Sockets +} // namespace Service diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h new file mode 100644 index 000000000..32d949e95 --- /dev/null +++ b/src/core/hle/service/sockets/bsd.h @@ -0,0 +1,31 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace Sockets { + +class BSD final : public ServiceFramework<BSD> { +public: + explicit BSD(const char* name); + ~BSD() = default; + +private: + void RegisterClient(Kernel::HLERequestContext& ctx); + void StartMonitoring(Kernel::HLERequestContext& ctx); + void Socket(Kernel::HLERequestContext& ctx); + void Connect(Kernel::HLERequestContext& ctx); + void SendTo(Kernel::HLERequestContext& ctx); + void Close(Kernel::HLERequestContext& ctx); + + /// Id to use for the next open file descriptor. + u32 next_fd = 1; +}; + +} // namespace Sockets +} // namespace Service diff --git a/src/core/hle/service/sockets/nsd.cpp b/src/core/hle/service/sockets/nsd.cpp new file mode 100644 index 000000000..e3542d325 --- /dev/null +++ b/src/core/hle/service/sockets/nsd.cpp @@ -0,0 +1,34 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/sockets/nsd.h" + +namespace Service { +namespace Sockets { + +NSD::NSD(const char* name) : ServiceFramework(name) { + static const FunctionInfo functions[] = { + {10, nullptr, "GetSettingName"}, + {11, nullptr, "GetEnvironmentIdentifier"}, + {12, nullptr, "GetDeviceId"}, + {13, nullptr, "DeleteSettings"}, + {14, nullptr, "ImportSettings"}, + {20, nullptr, "Resolve"}, + {21, nullptr, "ResolveEx"}, + {30, nullptr, "GetNasServiceSetting"}, + {31, nullptr, "GetNasServiceSettingEx"}, + {40, nullptr, "GetNasRequestFqdn"}, + {41, nullptr, "GetNasRequestFqdnEx"}, + {42, nullptr, "GetNasApiFqdn"}, + {43, nullptr, "GetNasApiFqdnEx"}, + {50, nullptr, "GetCurrentSetting"}, + {60, nullptr, "ReadSaveDataFromFsForTest"}, + {61, nullptr, "WriteSaveDataToFsForTest"}, + {62, nullptr, "DeleteSaveDataOfFsForTest"}, + }; + RegisterHandlers(functions); +} + +} // namespace Sockets +} // namespace Service diff --git a/src/core/hle/service/sockets/nsd.h b/src/core/hle/service/sockets/nsd.h new file mode 100644 index 000000000..a7c15a860 --- /dev/null +++ b/src/core/hle/service/sockets/nsd.h @@ -0,0 +1,20 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace Sockets { + +class NSD final : public ServiceFramework<NSD> { +public: + explicit NSD(const char* name); + ~NSD() = default; +}; + +} // namespace Sockets +} // namespace Service diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp new file mode 100644 index 000000000..eb4b5fa57 --- /dev/null +++ b/src/core/hle/service/sockets/sfdnsres.cpp @@ -0,0 +1,38 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/sockets/sfdnsres.h" + +namespace Service { +namespace Sockets { + +void SFDNSRES::GetAddrInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + LOG_WARNING(Service, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + + rb.Push(RESULT_SUCCESS); +} + +SFDNSRES::SFDNSRES() : ServiceFramework("sfdnsres") { + static const FunctionInfo functions[] = { + {0, nullptr, "SetDnsAddressesPrivate"}, + {1, nullptr, "GetDnsAddressPrivate"}, + {2, nullptr, "GetHostByName"}, + {3, nullptr, "GetHostByAddr"}, + {4, nullptr, "GetHostStringError"}, + {5, nullptr, "GetGaiStringError"}, + {6, &SFDNSRES::GetAddrInfo, "GetAddrInfo"}, + {7, nullptr, "GetNameInfo"}, + {8, nullptr, "RequestCancelHandle"}, + {9, nullptr, "CancelSocketCall"}, + }; + RegisterHandlers(functions); +} + +} // namespace Sockets +} // namespace Service diff --git a/src/core/hle/service/sockets/sfdnsres.h b/src/core/hle/service/sockets/sfdnsres.h new file mode 100644 index 000000000..c07cc1594 --- /dev/null +++ b/src/core/hle/service/sockets/sfdnsres.h @@ -0,0 +1,23 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace Sockets { + +class SFDNSRES final : public ServiceFramework<SFDNSRES> { +public: + explicit SFDNSRES(); + ~SFDNSRES() = default; + +private: + void GetAddrInfo(Kernel::HLERequestContext& ctx); +}; + +} // namespace Sockets +} // namespace Service diff --git a/src/core/hle/service/sockets/sockets.cpp b/src/core/hle/service/sockets/sockets.cpp new file mode 100644 index 000000000..cedc276d9 --- /dev/null +++ b/src/core/hle/service/sockets/sockets.cpp @@ -0,0 +1,22 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/sockets/bsd.h" +#include "core/hle/service/sockets/nsd.h" +#include "core/hle/service/sockets/sfdnsres.h" +#include "core/hle/service/sockets/sockets.h" + +namespace Service { +namespace Sockets { + +void InstallInterfaces(SM::ServiceManager& service_manager) { + std::make_shared<BSD>("bsd:s")->InstallAsService(service_manager); + std::make_shared<BSD>("bsd:u")->InstallAsService(service_manager); + std::make_shared<NSD>("nsd:a")->InstallAsService(service_manager); + std::make_shared<NSD>("nsd:u")->InstallAsService(service_manager); + std::make_shared<SFDNSRES>()->InstallAsService(service_manager); +} + +} // namespace Sockets +} // namespace Service diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h new file mode 100644 index 000000000..7e89c8d2c --- /dev/null +++ b/src/core/hle/service/sockets/sockets.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Sockets { + +/// Registers all Sockets services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace Sockets +} // namespace Service diff --git a/src/core/hle/service/spl/csrng.cpp b/src/core/hle/service/spl/csrng.cpp new file mode 100644 index 000000000..cde05717a --- /dev/null +++ b/src/core/hle/service/spl/csrng.cpp @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/spl/csrng.h" + +namespace Service { +namespace SPL { + +CSRNG::CSRNG(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "csrng") { + static const FunctionInfo functions[] = { + {0, &CSRNG::GetRandomBytes, "GetRandomBytes"}, + }; + RegisterHandlers(functions); +} + +} // namespace SPL +} // namespace Service diff --git a/src/core/hle/service/spl/csrng.h b/src/core/hle/service/spl/csrng.h new file mode 100644 index 000000000..59ca794dd --- /dev/null +++ b/src/core/hle/service/spl/csrng.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/spl/module.h" + +namespace Service { +namespace SPL { + +class CSRNG final : public Module::Interface { +public: + explicit CSRNG(std::shared_ptr<Module> module); +}; + +} // namespace SPL +} // namespace Service diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp new file mode 100644 index 000000000..fc1bcd94c --- /dev/null +++ b/src/core/hle/service/spl/module.cpp @@ -0,0 +1,42 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <cstdlib> +#include <vector> +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/spl/csrng.h" +#include "core/hle/service/spl/module.h" +#include "core/hle/service/spl/spl.h" + +namespace Service { +namespace SPL { + +Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) + : ServiceFramework(name), module(std::move(module)) {} + +void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + size_t size = ctx.GetWriteBufferSize(); + + std::vector<u8> data(size); + std::generate(data.begin(), data.end(), std::rand); + + ctx.WriteBuffer(data); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_DEBUG(Service_SPL, "called"); +} + +void InstallInterfaces(SM::ServiceManager& service_manager) { + auto module = std::make_shared<Module>(); + std::make_shared<CSRNG>(module)->InstallAsService(service_manager); + std::make_shared<SPL>(module)->InstallAsService(service_manager); +} + +} // namespace SPL +} // namespace Service diff --git a/src/core/hle/service/spl/module.h b/src/core/hle/service/spl/module.h new file mode 100644 index 000000000..12cdb2980 --- /dev/null +++ b/src/core/hle/service/spl/module.h @@ -0,0 +1,29 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace SPL { + +class Module final { +public: + class Interface : public ServiceFramework<Interface> { + public: + Interface(std::shared_ptr<Module> module, const char* name); + + void GetRandomBytes(Kernel::HLERequestContext& ctx); + + protected: + std::shared_ptr<Module> module; + }; +}; + +/// Registers all SPL services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace SPL +} // namespace Service diff --git a/src/core/hle/service/spl/spl.cpp b/src/core/hle/service/spl/spl.cpp new file mode 100644 index 000000000..deab29b91 --- /dev/null +++ b/src/core/hle/service/spl/spl.cpp @@ -0,0 +1,41 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/spl/spl.h" + +namespace Service { +namespace SPL { + +SPL::SPL(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "spl:") { + static const FunctionInfo functions[] = { + {0, nullptr, "GetConfig"}, + {1, nullptr, "UserExpMod"}, + {2, nullptr, "GenerateAesKek"}, + {3, nullptr, "LoadAesKey"}, + {4, nullptr, "GenerateAesKey"}, + {5, nullptr, "SetConfig"}, + {7, &SPL::GetRandomBytes, "GetRandomBytes"}, + {9, nullptr, "LoadSecureExpModKey"}, + {10, nullptr, "SecureExpMod"}, + {11, nullptr, "IsDevelopment"}, + {12, nullptr, "GenerateSpecificAesKey"}, + {13, nullptr, "DecryptPrivk"}, + {14, nullptr, "DecryptAesKey"}, + {15, nullptr, "DecryptAesCtr"}, + {16, nullptr, "ComputeCmac"}, + {17, nullptr, "LoadRsaOaepKey"}, + {18, nullptr, "UnwrapRsaOaepWrappedTitleKey"}, + {19, nullptr, "LoadTitleKey"}, + {20, nullptr, "UnwrapAesWrappedTitleKey"}, + {21, nullptr, "LockAesEngine"}, + {22, nullptr, "UnlockAesEngine"}, + {23, nullptr, "GetSplWaitEvent"}, + {24, nullptr, "SetSharedData"}, + {25, nullptr, "GetSharedData"}, + }; + RegisterHandlers(functions); +} + +} // namespace SPL +} // namespace Service diff --git a/src/core/hle/service/spl/spl.h b/src/core/hle/service/spl/spl.h new file mode 100644 index 000000000..9fd6059af --- /dev/null +++ b/src/core/hle/service/spl/spl.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/spl/module.h" + +namespace Service { +namespace SPL { + +class SPL final : public Module::Interface { +public: + explicit SPL(std::shared_ptr<Module> module); +}; + +} // namespace SPL +} // namespace Service diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp new file mode 100644 index 000000000..afa8d5d79 --- /dev/null +++ b/src/core/hle/service/ssl/ssl.cpp @@ -0,0 +1,17 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/ssl/ssl.h" + +namespace Service { +namespace SSL { + +SSL::SSL() : ServiceFramework("ssl") {} + +void InstallInterfaces(SM::ServiceManager& service_manager) { + std::make_shared<SSL>()->InstallAsService(service_manager); +} + +} // namespace SSL +} // namespace Service diff --git a/src/core/hle/service/ssl/ssl.h b/src/core/hle/service/ssl/ssl.h new file mode 100644 index 000000000..645dad003 --- /dev/null +++ b/src/core/hle/service/ssl/ssl.h @@ -0,0 +1,22 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace SSL { + +class SSL final : public ServiceFramework<SSL> { +public: + explicit SSL(); + ~SSL() = default; +}; + +/// Registers all SSL services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace SSL +} // namespace Service diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index e3d58aa60..c3e46f866 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -2,14 +2,164 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <chrono> +#include "common/logging/log.h" +#include "core/core_timing.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/client_port.h" +#include "core/hle/kernel/client_session.h" #include "core/hle/service/time/time.h" #include "core/hle/service/time/time_s.h" +#include "core/hle/service/time/time_u.h" namespace Service { namespace Time { +class ISystemClock final : public ServiceFramework<ISystemClock> { +public: + ISystemClock() : ServiceFramework("ISystemClock") { + static const FunctionInfo functions[] = { + {0, &ISystemClock::GetCurrentTime, "GetCurrentTime"}, + {2, &ISystemClock::GetSystemClockContext, "GetSystemClockContext"}}; + RegisterHandlers(functions); + } + +private: + void GetCurrentTime(Kernel::HLERequestContext& ctx) { + const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::seconds>( + std::chrono::system_clock::now().time_since_epoch()) + .count()}; + LOG_DEBUG(Service_Time, "called"); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(time_since_epoch); + } + + void GetSystemClockContext(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Time, "(STUBBED) called"); + SystemClockContext system_clock_ontext{}; + IPC::ResponseBuilder rb{ctx, (sizeof(SystemClockContext) / 4) + 2}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(system_clock_ontext); + } +}; + +class ISteadyClock final : public ServiceFramework<ISteadyClock> { +public: + ISteadyClock() : ServiceFramework("ISteadyClock") { + static const FunctionInfo functions[] = { + {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"}, + }; + RegisterHandlers(functions); + } + +private: + void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called"); + SteadyClockTimePoint steady_clock_time_point{cyclesToMs(CoreTiming::GetTicks()) / 1000}; + IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(steady_clock_time_point); + } +}; + +class ITimeZoneService final : public ServiceFramework<ITimeZoneService> { +public: + ITimeZoneService() : ServiceFramework("ITimeZoneService") { + static const FunctionInfo functions[] = { + {0, &ITimeZoneService::GetDeviceLocationName, "GetDeviceLocationName"}, + {1, nullptr, "SetDeviceLocationName"}, + {2, &ITimeZoneService::GetTotalLocationNameCount, "GetTotalLocationNameCount"}, + {3, nullptr, "LoadLocationNameList"}, + {4, &ITimeZoneService::LoadTimeZoneRule, "LoadTimeZoneRule"}, + {5, nullptr, "GetTimeZoneRuleVersion"}, + {100, nullptr, "ToCalendarTime"}, + {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, + {200, nullptr, "ToPosixTime"}, + {201, nullptr, "ToPosixTimeWithMyRule"}, + }; + RegisterHandlers(functions); + } + +private: + void GetDeviceLocationName(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Time, "(STUBBED) called"); + LocationName location_name{}; + IPC::ResponseBuilder rb{ctx, (sizeof(LocationName) / 4) + 2}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(location_name); + } + + void GetTotalLocationNameCount(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Time, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); + } + + void LoadTimeZoneRule(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Time, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + u64 posix_time = rp.Pop<u64>(); + + LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x%016lX", posix_time); + + CalendarTime calendar_time{2018, 1, 1, 0, 0, 0}; + CalendarAdditionalInfo additional_info{}; + IPC::ResponseBuilder rb{ctx, 10}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(calendar_time); + rb.PushRaw(additional_info); + } +}; + +void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISystemClock>(); + LOG_DEBUG(Service_Time, "called"); +} + +void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISystemClock>(); + LOG_DEBUG(Service_Time, "called"); +} + +void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISteadyClock>(); + LOG_DEBUG(Service_Time, "called"); +} + +void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ITimeZoneService>(); + LOG_DEBUG(Service_Time, "called"); +} + +void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISystemClock>(); + LOG_DEBUG(Service_Time, "called"); +} + +Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) + : ServiceFramework(name), time(std::move(time)) {} + void InstallInterfaces(SM::ServiceManager& service_manager) { - std::make_shared<TimeS>()->InstallAsService(service_manager); + auto time = std::make_shared<Module>(); + std::make_shared<TIME_S>(time)->InstallAsService(service_manager); + std::make_shared<TIME_U>(time)->InstallAsService(service_manager); } } // namespace Time diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h index 7d0803e24..197029e7a 100644 --- a/src/core/hle/service/time/time.h +++ b/src/core/hle/service/time/time.h @@ -9,6 +9,60 @@ namespace Service { namespace Time { +// TODO(Rozelette) RE this structure +struct LocationName { + INSERT_PADDING_BYTES(0x24); +}; +static_assert(sizeof(LocationName) == 0x24, "LocationName is incorrect size"); + +struct CalendarTime { + u16_le year; + u8 month; // Starts at 1 + u8 day; // Starts at 1 + u8 hour; + u8 minute; + u8 second; + INSERT_PADDING_BYTES(1); +}; +static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime structure has incorrect size"); + +// TODO(Rozelette) RE this structure +struct CalendarAdditionalInfo { + INSERT_PADDING_BYTES(0x18); +}; +static_assert(sizeof(CalendarAdditionalInfo) == 0x18, + "CalendarAdditionalInfo structure has incorrect size"); + +// TODO(bunnei) RE this structure +struct SystemClockContext { + INSERT_PADDING_BYTES(0x20); +}; +static_assert(sizeof(SystemClockContext) == 0x20, + "SystemClockContext structure has incorrect size"); + +struct SteadyClockTimePoint { + u64 value; + INSERT_PADDING_WORDS(4); +}; +static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size"); + +class Module final { +public: + class Interface : public ServiceFramework<Interface> { + public: + Interface(std::shared_ptr<Module> time, const char* name); + + void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx); + void GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx); + void GetStandardSteadyClock(Kernel::HLERequestContext& ctx); + void GetTimeZoneService(Kernel::HLERequestContext& ctx); + void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx); + + protected: + std::shared_ptr<Module> time; + }; +}; + /// Registers all Time services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager); diff --git a/src/core/hle/service/time/time_s.cpp b/src/core/hle/service/time/time_s.cpp index 6b0597d8e..b172b2bd6 100644 --- a/src/core/hle/service/time/time_s.cpp +++ b/src/core/hle/service/time/time_s.cpp @@ -2,54 +2,18 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <chrono> -#include "common/logging/log.h" -#include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/client_port.h" -#include "core/hle/kernel/client_session.h" #include "core/hle/service/time/time_s.h" namespace Service { namespace Time { -class ISystemClock final : public ServiceFramework<ISystemClock> { -public: - ISystemClock() : ServiceFramework("ISystemClock") { - static const FunctionInfo functions[] = { - {0, &ISystemClock::GetCurrentTime, "GetCurrentTime"}, - }; - RegisterHandlers(functions); - } - -private: - void GetCurrentTime(Kernel::HLERequestContext& ctx) { - const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::system_clock::now().time_since_epoch()) - .count()}; - IPC::RequestBuilder rb{ctx, 4}; - rb.Push(RESULT_SUCCESS); - rb.Push<u64>(time_since_epoch); - LOG_DEBUG(Service, "called"); - } -}; - -void TimeS::GetStandardUserSystemClock(Kernel::HLERequestContext& ctx) { - auto client_port = std::make_shared<ISystemClock>()->CreatePort(); - auto session = client_port->Connect(); - if (session.Succeeded()) { - LOG_DEBUG(Service, "called, initialized ISystemClock -> session=%u", - (*session)->GetObjectId()); - IPC::RequestBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushMoveObjects(std::move(session).Unwrap()); - } else { - UNIMPLEMENTED(); - } -} - -TimeS::TimeS() : ServiceFramework("time:s") { +TIME_S::TIME_S(std::shared_ptr<Module> time) : Module::Interface(std::move(time), "time:s") { static const FunctionInfo functions[] = { - {0x00000000, &TimeS::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, + {0, &TIME_S::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, + {1, &TIME_S::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, + {2, &TIME_S::GetStandardSteadyClock, "GetStandardSteadyClock"}, + {3, &TIME_S::GetTimeZoneService, "GetTimeZoneService"}, + {4, &TIME_S::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/time/time_s.h b/src/core/hle/service/time/time_s.h index 073227910..abc2a8c5a 100644 --- a/src/core/hle/service/time/time_s.h +++ b/src/core/hle/service/time/time_s.h @@ -4,19 +4,14 @@ #pragma once -#include "core/hle/kernel/hle_ipc.h" -#include "core/hle/service/service.h" +#include "core/hle/service/time/time.h" namespace Service { namespace Time { -class TimeS final : public ServiceFramework<TimeS> { +class TIME_S final : public Module::Interface { public: - TimeS(); - ~TimeS() = default; - -private: - void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx); + explicit TIME_S(std::shared_ptr<Module> time); }; } // namespace Time diff --git a/src/core/hle/service/time/time_u.cpp b/src/core/hle/service/time/time_u.cpp new file mode 100644 index 000000000..fc1ace325 --- /dev/null +++ b/src/core/hle/service/time/time_u.cpp @@ -0,0 +1,22 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/time/time_u.h" + +namespace Service { +namespace Time { + +TIME_U::TIME_U(std::shared_ptr<Module> time) : Module::Interface(std::move(time), "time:u") { + static const FunctionInfo functions[] = { + {0, &TIME_U::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, + {1, &TIME_U::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, + {2, &TIME_U::GetStandardSteadyClock, "GetStandardSteadyClock"}, + {3, &TIME_U::GetTimeZoneService, "GetTimeZoneService"}, + {4, &TIME_U::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, + }; + RegisterHandlers(functions); +} + +} // namespace Time +} // namespace Service diff --git a/src/core/hle/service/time/time_u.h b/src/core/hle/service/time/time_u.h new file mode 100644 index 000000000..f99d25057 --- /dev/null +++ b/src/core/hle/service/time/time_u.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/time/time.h" + +namespace Service { +namespace Time { + +class TIME_U final : public Module::Interface { +public: + explicit TIME_U(std::shared_ptr<Module> time); +}; + +} // namespace Time +} // namespace Service diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index cae2c4466..06c34e979 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -3,34 +3,46 @@ // Refer to the license.txt file included. #include <algorithm> - +#include <array> +#include <memory> +#include <boost/optional.hpp> #include "common/alignment.h" #include "common/scope_exit.h" #include "core/core_timing.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" +#include "core/hle/kernel/event.h" #include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvflinger/buffer_queue.h" #include "core/hle/service/vi/vi.h" #include "core/hle/service/vi/vi_m.h" +#include "core/hle/service/vi/vi_s.h" +#include "core/hle/service/vi/vi_u.h" #include "video_core/renderer_base.h" #include "video_core/video_core.h" namespace Service { namespace VI { -constexpr size_t SCREEN_REFRESH_RATE = 60; -constexpr u64 frame_ticks = static_cast<u64>(BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); +struct DisplayInfo { + char display_name[0x40]{"Default"}; + u64 unknown_1{1}; + u64 unknown_2{1}; + u64 width{1920}; + u64 height{1080}; +}; +static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size"); class Parcel { public: // This default size was chosen arbitrarily. static constexpr size_t DefaultBufferSize = 0x40; Parcel() : buffer(DefaultBufferSize) {} - Parcel(std::vector<u8> data) : buffer(std::move(data)) {} + explicit Parcel(std::vector<u8> data) : buffer(std::move(data)) {} virtual ~Parcel() = default; template <typename T> T Read() { + ASSERT(read_index + sizeof(T) <= buffer.size()); T val; std::memcpy(&val, buffer.data() + read_index, sizeof(T)); read_index += sizeof(T); @@ -40,6 +52,7 @@ public: template <typename T> T ReadUnaligned() { + ASSERT(read_index + sizeof(T) <= buffer.size()); T val; std::memcpy(&val, buffer.data() + read_index, sizeof(T)); read_index += sizeof(T); @@ -47,8 +60,10 @@ public: } std::vector<u8> ReadBlock(size_t length) { - std::vector<u8> data(length); - std::memcpy(data.data(), buffer.data() + read_index, length); + ASSERT(read_index + length <= buffer.size()); + const u8* const begin = buffer.data() + read_index; + const u8* const end = begin + length; + std::vector<u8> data(begin, end); read_index += length; read_index = Common::AlignUp(read_index, 4); return data; @@ -78,7 +93,18 @@ public: write_index = Common::AlignUp(write_index, 4); } + template <typename T> + void WriteObject(const T& val) { + u32_le size = static_cast<u32>(sizeof(val)); + Write(size); + // TODO(Subv): Support file descriptors. + Write<u32_le>(0); // Fd count. + Write(val); + } + void Deserialize() { + ASSERT(buffer.size() > sizeof(Header)); + Header header{}; std::memcpy(&header, buffer.data(), sizeof(Header)); @@ -93,17 +119,19 @@ public: SerializeData(); Header header{}; + header.data_size = static_cast<u32_le>(write_index - sizeof(Header)); header.data_offset = sizeof(Header); - header.data_size = write_index - sizeof(Header); + header.objects_size = 4; + header.objects_offset = sizeof(Header) + header.data_size; std::memcpy(buffer.data(), &header, sizeof(Header)); return buffer; } protected: - virtual void SerializeData(){}; + virtual void SerializeData() {} - virtual void DeserializeData(){}; + virtual void DeserializeData() {} private: struct Header { @@ -121,7 +149,7 @@ private: class NativeWindow : public Parcel { public: - NativeWindow(u32 id) : Parcel() { + explicit NativeWindow(u32 id) : Parcel() { data.id = id; } ~NativeWindow() override = default; @@ -134,11 +162,11 @@ protected: private: struct Data { u32_le magic = 2; - u32_le process_id; + u32_le process_id = 1; u32_le id; - INSERT_PADDING_BYTES(0xC); - std::array<u8, 8> dspdrv = {'d', 's', 'p', 'd', 'r', 'v'}; - INSERT_PADDING_BYTES(8); + INSERT_PADDING_WORDS(3); + std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'}; + INSERT_PADDING_WORDS(2); }; static_assert(sizeof(Data) == 0x28, "ParcelData has wrong size"); @@ -147,12 +175,12 @@ private: class IGBPConnectRequestParcel : public Parcel { public: - IGBPConnectRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) { + explicit IGBPConnectRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) { Deserialize(); } ~IGBPConnectRequestParcel() override = default; - void DeserializeData() { + void DeserializeData() override { std::u16string token = ReadInterfaceToken(); data = Read<Data>(); } @@ -168,7 +196,7 @@ public: class IGBPConnectResponseParcel : public Parcel { public: - IGBPConnectResponseParcel(u32 width, u32 height) : Parcel() { + explicit IGBPConnectResponseParcel(u32 width, u32 height) : Parcel() { data.width = width; data.height = height; } @@ -194,16 +222,16 @@ private: class IGBPSetPreallocatedBufferRequestParcel : public Parcel { public: - IGBPSetPreallocatedBufferRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) { + explicit IGBPSetPreallocatedBufferRequestParcel(const std::vector<u8>& buffer) + : Parcel(buffer) { Deserialize(); } ~IGBPSetPreallocatedBufferRequestParcel() override = default; - void DeserializeData() { + void DeserializeData() override { std::u16string token = ReadInterfaceToken(); data = Read<Data>(); - ASSERT(data.graphic_buffer_length == sizeof(IGBPBuffer)); - buffer = Read<IGBPBuffer>(); + buffer = Read<NVFlinger::IGBPBuffer>(); } struct Data { @@ -214,7 +242,7 @@ public: }; Data data; - IGBPBuffer buffer; + NVFlinger::IGBPBuffer buffer; }; class IGBPSetPreallocatedBufferResponseParcel : public Parcel { @@ -231,12 +259,12 @@ protected: class IGBPDequeueBufferRequestParcel : public Parcel { public: - IGBPDequeueBufferRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) { + explicit IGBPDequeueBufferRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) { Deserialize(); } ~IGBPDequeueBufferRequestParcel() override = default; - void DeserializeData() { + void DeserializeData() override { std::u16string token = ReadInterfaceToken(); data = Read<Data>(); } @@ -252,17 +280,28 @@ public: Data data; }; +struct BufferProducerFence { + u32 is_valid; + std::array<Nvidia::IoctlFence, 4> fences; +}; +static_assert(sizeof(BufferProducerFence) == 36, "BufferProducerFence has wrong size"); + class IGBPDequeueBufferResponseParcel : public Parcel { public: - IGBPDequeueBufferResponseParcel(u32 slot) : Parcel(), slot(slot) {} + explicit IGBPDequeueBufferResponseParcel(u32 slot) : Parcel(), slot(slot) {} ~IGBPDequeueBufferResponseParcel() override = default; protected: void SerializeData() override { - Write(slot); // TODO(Subv): Find out how this Fence is used. - std::array<u32_le, 11> fence = {}; - Write(fence); + BufferProducerFence fence = {}; + fence.is_valid = 1; + for (auto& fence_ : fence.fences) + fence_.id = -1; + + Write(slot); + Write<u32_le>(1); + WriteObject(fence); Write<u32_le>(0); } @@ -271,12 +310,12 @@ protected: class IGBPRequestBufferRequestParcel : public Parcel { public: - IGBPRequestBufferRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) { + explicit IGBPRequestBufferRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) { Deserialize(); } ~IGBPRequestBufferRequestParcel() override = default; - void DeserializeData() { + void DeserializeData() override { std::u16string token = ReadInterfaceToken(); slot = Read<u32_le>(); } @@ -286,51 +325,64 @@ public: class IGBPRequestBufferResponseParcel : public Parcel { public: - IGBPRequestBufferResponseParcel(IGBPBuffer buffer) : Parcel(), buffer(buffer) {} + explicit IGBPRequestBufferResponseParcel(NVFlinger::IGBPBuffer buffer) + : Parcel(), buffer(buffer) {} ~IGBPRequestBufferResponseParcel() override = default; protected: void SerializeData() override { - // TODO(Subv): Find out what this all means + // TODO(Subv): Figure out what this value means, writing non-zero here will make libnx try + // to read an IGBPBuffer object from the parcel. Write<u32_le>(1); - - Write<u32_le>(sizeof(IGBPBuffer)); - Write<u32_le>(0); // Unknown - - Write(buffer); - + WriteObject(buffer); Write<u32_le>(0); } - IGBPBuffer buffer; + NVFlinger::IGBPBuffer buffer; }; class IGBPQueueBufferRequestParcel : public Parcel { public: - IGBPQueueBufferRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) { + explicit IGBPQueueBufferRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) { Deserialize(); } ~IGBPQueueBufferRequestParcel() override = default; - void DeserializeData() { + void DeserializeData() override { std::u16string token = ReadInterfaceToken(); data = Read<Data>(); } + struct Fence { + u32_le id; + u32_le value; + }; + static_assert(sizeof(Fence) == 8, "Fence has wrong size"); + struct Data { u32_le slot; - INSERT_PADDING_WORDS(2); + INSERT_PADDING_WORDS(3); u32_le timestamp; - INSERT_PADDING_WORDS(20); + s32_le is_auto_timestamp; + s32_le crop_left; + s32_le crop_top; + s32_le crop_right; + s32_le crop_bottom; + s32_le scaling_mode; + NVFlinger::BufferQueue::BufferTransformFlags transform; + u32_le sticky_transform; + INSERT_PADDING_WORDS(2); + u32_le fence_is_valid; + std::array<Fence, 2> fences; }; - static_assert(sizeof(Data) == 96, "ParcelData has wrong size"); + static_assert(sizeof(Data) == 80, "ParcelData has wrong size"); Data data; }; class IGBPQueueBufferResponseParcel : public Parcel { public: - IGBPQueueBufferResponseParcel(u32 width, u32 height) : Parcel() { + explicit IGBPQueueBufferResponseParcel(u32 width, u32 height) : Parcel() { data.width = width; data.height = height; } @@ -354,15 +406,44 @@ private: Data data{}; }; +class IGBPQueryRequestParcel : public Parcel { +public: + explicit IGBPQueryRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) { + Deserialize(); + } + ~IGBPQueryRequestParcel() override = default; + + void DeserializeData() override { + std::u16string token = ReadInterfaceToken(); + type = Read<u32_le>(); + } + + u32 type; +}; + +class IGBPQueryResponseParcel : public Parcel { +public: + explicit IGBPQueryResponseParcel(u32 value) : Parcel(), value(value) {} + ~IGBPQueryResponseParcel() override = default; + +protected: + void SerializeData() override { + Write(value); + } + +private: + u32_le value; +}; + class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> { public: - IHOSBinderDriver(std::shared_ptr<NVFlinger> nv_flinger) + explicit IHOSBinderDriver(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) : ServiceFramework("IHOSBinderDriver"), nv_flinger(std::move(nv_flinger)) { static const FunctionInfo functions[] = { {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"}, {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"}, {2, &IHOSBinderDriver::GetNativeHandle, "GetNativeHandle"}, - {3, nullptr, "TransactParcelAuto"}, + {3, &IHOSBinderDriver::TransactParcel, "TransactParcelAuto"}, }; RegisterHandlers(functions); } @@ -391,64 +472,76 @@ private: u32 id = rp.Pop<u32>(); auto transaction = static_cast<TransactionId>(rp.Pop<u32>()); u32 flags = rp.Pop<u32>(); - - auto& input_buffer = ctx.BufferDescriptorA()[0]; - std::vector<u8> input_data(input_buffer.Size()); - Memory::ReadBlock(input_buffer.Address(), input_data.data(), input_buffer.Size()); - - auto& output_buffer = ctx.BufferDescriptorB()[0]; - auto buffer_queue = nv_flinger->GetBufferQueue(id); + LOG_DEBUG(Service_VI, "called, transaction=%x", static_cast<u32>(transaction)); + if (transaction == TransactionId::Connect) { - IGBPConnectRequestParcel request{input_data}; + IGBPConnectRequestParcel request{ctx.ReadBuffer()}; IGBPConnectResponseParcel response{1280, 720}; - auto response_buffer = response.Serialize(); - Memory::WriteBlock(output_buffer.Address(), response_buffer.data(), - output_buffer.Size()); + ctx.WriteBuffer(response.Serialize()); } else if (transaction == TransactionId::SetPreallocatedBuffer) { - IGBPSetPreallocatedBufferRequestParcel request{input_data}; + IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()}; buffer_queue->SetPreallocatedBuffer(request.data.slot, request.buffer); IGBPSetPreallocatedBufferResponseParcel response{}; - auto response_buffer = response.Serialize(); - Memory::WriteBlock(output_buffer.Address(), response_buffer.data(), - output_buffer.Size()); + ctx.WriteBuffer(response.Serialize()); } else if (transaction == TransactionId::DequeueBuffer) { - IGBPDequeueBufferRequestParcel request{input_data}; - - u32 slot = buffer_queue->DequeueBuffer(request.data.pixel_format, request.data.width, - request.data.height); - - IGBPDequeueBufferResponseParcel response{slot}; - auto response_buffer = response.Serialize(); - Memory::WriteBlock(output_buffer.Address(), response_buffer.data(), - output_buffer.Size()); + IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; + const u32 width{request.data.width}; + const u32 height{request.data.height}; + boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); + + if (slot != boost::none) { + // Buffer is available + IGBPDequeueBufferResponseParcel response{*slot}; + ctx.WriteBuffer(response.Serialize()); + } else { + // Wait the current thread until a buffer becomes available + auto wait_event = ctx.SleepClientThread( + Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1, + [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, + ThreadWakeupReason reason) { + // Repeat TransactParcel DequeueBuffer when a buffer is available + auto buffer_queue = nv_flinger->GetBufferQueue(id); + boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); + IGBPDequeueBufferResponseParcel response{*slot}; + ctx.WriteBuffer(response.Serialize()); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + }); + buffer_queue->SetBufferWaitEvent(std::move(wait_event)); + } } else if (transaction == TransactionId::RequestBuffer) { - IGBPRequestBufferRequestParcel request{input_data}; + IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()}; auto& buffer = buffer_queue->RequestBuffer(request.slot); IGBPRequestBufferResponseParcel response{buffer}; - auto response_buffer = response.Serialize(); - Memory::WriteBlock(output_buffer.Address(), response_buffer.data(), - output_buffer.Size()); + ctx.WriteBuffer(response.Serialize()); } else if (transaction == TransactionId::QueueBuffer) { - IGBPQueueBufferRequestParcel request{input_data}; + IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()}; - buffer_queue->QueueBuffer(request.data.slot); + buffer_queue->QueueBuffer(request.data.slot, request.data.transform); IGBPQueueBufferResponseParcel response{1280, 720}; - auto response_buffer = response.Serialize(); - Memory::WriteBlock(output_buffer.Address(), response_buffer.data(), - output_buffer.Size()); + ctx.WriteBuffer(response.Serialize()); + } else if (transaction == TransactionId::Query) { + IGBPQueryRequestParcel request{ctx.ReadBuffer()}; + + u32 value = + buffer_queue->Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type)); + + IGBPQueryResponseParcel response{value}; + ctx.WriteBuffer(response.Serialize()); + } else if (transaction == TransactionId::CancelBuffer) { + LOG_WARNING(Service_VI, "(STUBBED) called, transaction=CancelBuffer"); } else { ASSERT_MSG(false, "Unimplemented"); } - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestBuilder rb{ctx, 2}; + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -458,8 +551,8 @@ private: s32 addval = rp.PopRaw<s32>(); u32 type = rp.Pop<u32>(); - LOG_WARNING(Service, "(STUBBED) called id=%u, addval=%08X, type=%08X", id, addval, type); - IPC::RequestBuilder rb{ctx, 2}; + LOG_WARNING(Service_VI, "(STUBBED) called id=%u, addval=%08X, type=%08X", id, addval, type); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -472,14 +565,14 @@ private: // TODO(Subv): Find out what this actually is. - LOG_WARNING(Service, "(STUBBED) called id=%u, unknown=%08X", id, unknown); - IPC::RequestBuilder rb{ctx, 2, 1}; + LOG_WARNING(Service_VI, "(STUBBED) called id=%u, unknown=%08X", id, unknown); + IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(buffer_queue->GetNativeHandle()); } - std::shared_ptr<NVFlinger> nv_flinger; -}; + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; +}; // namespace VI class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> { public: @@ -494,19 +587,19 @@ public: private: void SetLayerZ(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; u64 layer_id = rp.Pop<u64>(); u64 z_value = rp.Pop<u64>(); - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0, 0, 0); + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); rb.Push(RESULT_SUCCESS); } }; class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> { public: - IManagerDisplayService(std::shared_ptr<NVFlinger> nv_flinger) + explicit IManagerDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) : ServiceFramework("IManagerDisplayService"), nv_flinger(std::move(nv_flinger)) { static const FunctionInfo functions[] = { {1020, &IManagerDisplayService::CloseDisplay, "CloseDisplay"}, @@ -520,16 +613,16 @@ public: private: void CloseDisplay(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; u64 display = rp.Pop<u64>(); - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0, 0, 0); + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); rb.Push(RESULT_SUCCESS); } void CreateManagedLayer(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; u32 unknown = rp.Pop<u32>(); rp.Skip(1, false); @@ -538,163 +631,173 @@ private: u64 layer_id = nv_flinger->CreateLayer(display); - IPC::RequestBuilder rb = rp.MakeBuilder(4, 0, 0, 0); + IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); rb.Push(RESULT_SUCCESS); rb.Push(layer_id); } void AddToLayerStack(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; u32 stack = rp.Pop<u32>(); u64 layer_id = rp.Pop<u64>(); - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0, 0, 0); + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); rb.Push(RESULT_SUCCESS); } - std::shared_ptr<NVFlinger> nv_flinger; + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; }; -void IApplicationDisplayService::GetRelayService(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger); -} +class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { +public: + IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); + ~IApplicationDisplayService() = default; -void IApplicationDisplayService::GetSystemDisplayService(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); +private: + void GetRelayService(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISystemDisplayService>(); -} + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger); + } -void IApplicationDisplayService::GetManagerDisplayService(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + void GetSystemDisplayService(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IManagerDisplayService>(nv_flinger); -} + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISystemDisplayService>(); + } -void IApplicationDisplayService::GetIndirectDisplayTransactionService( - Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + void GetManagerDisplayService(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger); -} + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IManagerDisplayService>(nv_flinger); + } -void IApplicationDisplayService::OpenDisplay(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestParser rp{ctx}; - auto name_buf = rp.PopRaw<std::array<u8, 0x40>>(); - auto end = std::find(name_buf.begin(), name_buf.end(), '\0'); + void GetIndirectDisplayTransactionService(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); - std::string name(name_buf.begin(), end); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger); + } - ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet"); + void OpenDisplay(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + auto name_buf = rp.PopRaw<std::array<u8, 0x40>>(); + auto end = std::find(name_buf.begin(), name_buf.end(), '\0'); - IPC::RequestBuilder rb = rp.MakeBuilder(4, 0, 0, 0); - rb.Push(RESULT_SUCCESS); - rb.Push<u64>(nv_flinger->OpenDisplay(name)); -} + std::string name(name_buf.begin(), end); -void IApplicationDisplayService::CloseDisplay(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestParser rp{ctx}; - u64 display_id = rp.Pop<u64>(); + ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet"); - IPC::RequestBuilder rb = rp.MakeBuilder(4, 0, 0, 0); - rb.Push(RESULT_SUCCESS); -} + IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(nv_flinger->OpenDisplay(name)); + } -void IApplicationDisplayService::OpenLayer(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestParser rp{ctx}; - auto name_buf = rp.PopRaw<std::array<u8, 0x40>>(); - auto end = std::find(name_buf.begin(), name_buf.end(), '\0'); + void CloseDisplay(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + u64 display_id = rp.Pop<u64>(); - std::string display_name(name_buf.begin(), end); + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); + rb.Push(RESULT_SUCCESS); + } - u64 layer_id = rp.Pop<u64>(); - u64 aruid = rp.Pop<u64>(); + void SetLayerScalingMode(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + u32 scaling_mode = rp.Pop<u32>(); + u64 unknown = rp.Pop<u64>(); - auto& buffer = ctx.BufferDescriptorB()[0]; + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); + rb.Push(RESULT_SUCCESS); + } - u64 display_id = nv_flinger->OpenDisplay(display_name); - u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); + void ListDisplays(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + DisplayInfo display_info; + ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); + IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(1); + LOG_WARNING(Service_VI, "(STUBBED) called"); + } - NativeWindow native_window{buffer_queue_id}; - auto data = native_window.Serialize(); - Memory::WriteBlock(buffer.Address(), data.data(), data.size()); + void OpenLayer(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_VI, "called"); + IPC::RequestParser rp{ctx}; + auto name_buf = rp.PopRaw<std::array<u8, 0x40>>(); + auto end = std::find(name_buf.begin(), name_buf.end(), '\0'); - IPC::RequestBuilder rb = rp.MakeBuilder(4, 0, 0, 0); - rb.Push(RESULT_SUCCESS); - rb.Push<u64>(data.size()); -} + std::string display_name(name_buf.begin(), end); -void IApplicationDisplayService::CreateStrayLayer(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + u64 layer_id = rp.Pop<u64>(); + u64 aruid = rp.Pop<u64>(); - IPC::RequestParser rp{ctx}; - u32 flags = rp.Pop<u32>(); - u64 display_id = rp.Pop<u64>(); + u64 display_id = nv_flinger->OpenDisplay(display_name); + u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); - auto& buffer = ctx.BufferDescriptorB()[0]; + NativeWindow native_window{buffer_queue_id}; + IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize())); + } - // TODO(Subv): What's the difference between a Stray and a Managed layer? + void CreateStrayLayer(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_VI, "called"); - u64 layer_id = nv_flinger->CreateLayer(display_id); - u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); + IPC::RequestParser rp{ctx}; + u32 flags = rp.Pop<u32>(); + rp.Pop<u32>(); // padding + u64 display_id = rp.Pop<u64>(); - NativeWindow native_window{buffer_queue_id}; - auto data = native_window.Serialize(); - Memory::WriteBlock(buffer.Address(), data.data(), data.size()); + // TODO(Subv): What's the difference between a Stray and a Managed layer? - IPC::RequestBuilder rb = rp.MakeBuilder(6, 0, 0, 0); - rb.Push(RESULT_SUCCESS); - rb.Push(layer_id); - rb.Push<u64>(data.size()); -} + u64 layer_id = nv_flinger->CreateLayer(display_id); + u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); -void IApplicationDisplayService::DestroyStrayLayer(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + NativeWindow native_window{buffer_queue_id}; + IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(layer_id); + rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize())); + } - IPC::RequestParser rp{ctx}; - u64 layer_id = rp.Pop<u64>(); + void DestroyStrayLayer(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0, 0, 0); - rb.Push(RESULT_SUCCESS); -} + IPC::RequestParser rp{ctx}; + u64 layer_id = rp.Pop<u64>(); -void IApplicationDisplayService::SetLayerScalingMode(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestParser rp{ctx}; - u32 scaling_mode = rp.Pop<u32>(); - u64 unknown = rp.Pop<u64>(); + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); + rb.Push(RESULT_SUCCESS); + } - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0, 0, 0); - rb.Push(RESULT_SUCCESS); -} + void GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + u64 display_id = rp.Pop<u64>(); -void IApplicationDisplayService::GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestParser rp{ctx}; - u64 display_id = rp.Pop<u64>(); + auto vsync_event = nv_flinger->GetVsyncEvent(display_id); - auto vsync_event = nv_flinger->GetVsyncEvent(display_id); + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 1, 0); + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(vsync_event); + } - IPC::RequestBuilder rb = rp.MakeBuilder(2, 1, 0, 0); - rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(vsync_event); -} + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; +}; -IApplicationDisplayService::IApplicationDisplayService(std::shared_ptr<NVFlinger> nv_flinger) +IApplicationDisplayService::IApplicationDisplayService( + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) : ServiceFramework("IApplicationDisplayService"), nv_flinger(std::move(nv_flinger)) { static const FunctionInfo functions[] = { {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"}, @@ -702,7 +805,7 @@ IApplicationDisplayService::IApplicationDisplayService(std::shared_ptr<NVFlinger {102, &IApplicationDisplayService::GetManagerDisplayService, "GetManagerDisplayService"}, {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService, "GetIndirectDisplayTransactionService"}, - {1000, nullptr, "ListDisplays"}, + {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"}, {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"}, {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"}, {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"}, @@ -714,212 +817,24 @@ IApplicationDisplayService::IApplicationDisplayService(std::shared_ptr<NVFlinger RegisterHandlers(functions); } -void InstallInterfaces(SM::ServiceManager& service_manager) { - std::make_shared<VI_M>()->InstallAsService(service_manager); -} - -NVFlinger::NVFlinger() { - // Add the different displays to the list of displays. - Display default_{0, "Default"}; - Display external{1, "External"}; - Display edid{2, "Edid"}; - Display internal{3, "Internal"}; - - displays.emplace_back(default_); - displays.emplace_back(external); - displays.emplace_back(edid); - displays.emplace_back(internal); - - // Schedule the screen composition events - composition_event = - CoreTiming::RegisterEvent("ScreenCompositioin", [this](u64 userdata, int cycles_late) { - Compose(); - CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event); - }); - - CoreTiming::ScheduleEvent(frame_ticks, composition_event); -} - -NVFlinger::~NVFlinger() { - CoreTiming::UnscheduleEvent(composition_event, 0); -} - -u64 NVFlinger::OpenDisplay(const std::string& name) { - LOG_WARNING(Service, "Opening display %s", name.c_str()); +Module::Interface::Interface(std::shared_ptr<Module> module, const char* name, + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) + : ServiceFramework(name), module(std::move(module)), nv_flinger(std::move(nv_flinger)) {} - // TODO(Subv): Currently we only support the Default display. - ASSERT(name == "Default"); - - auto itr = std::find_if(displays.begin(), displays.end(), - [&](const Display& display) { return display.name == name; }); - - ASSERT(itr != displays.end()); - - return itr->id; -} - -u64 NVFlinger::CreateLayer(u64 display_id) { - auto& display = GetDisplay(display_id); - - ASSERT_MSG(display.layers.empty(), "Only one layer is supported per display at the moment"); - - u64 layer_id = next_layer_id++; - u32 buffer_queue_id = next_buffer_queue_id++; - auto buffer_queue = std::make_shared<BufferQueue>(buffer_queue_id, layer_id); - display.layers.emplace_back(layer_id, buffer_queue); - buffer_queues.emplace_back(std::move(buffer_queue)); - return layer_id; -} - -u32 NVFlinger::GetBufferQueueId(u64 display_id, u64 layer_id) { - const auto& layer = GetLayer(display_id, layer_id); - return layer.buffer_queue->GetId(); -} +void Module::Interface::GetDisplayService(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); -Kernel::SharedPtr<Kernel::Event> NVFlinger::GetVsyncEvent(u64 display_id) { - const auto& display = GetDisplay(display_id); - return display.vsync_event; -} - -std::shared_ptr<BufferQueue> NVFlinger::GetBufferQueue(u32 id) const { - auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(), - [&](const auto& queue) { return queue->GetId() == id; }); - - ASSERT(itr != buffer_queues.end()); - return *itr; -} - -Display& NVFlinger::GetDisplay(u64 display_id) { - auto itr = std::find_if(displays.begin(), displays.end(), - [&](const Display& display) { return display.id == display_id; }); - - ASSERT(itr != displays.end()); - return *itr; -} - -Layer& NVFlinger::GetLayer(u64 display_id, u64 layer_id) { - auto& display = GetDisplay(display_id); - - auto itr = std::find_if(display.layers.begin(), display.layers.end(), - [&](const Layer& layer) { return layer.id == layer_id; }); - - ASSERT(itr != display.layers.end()); - return *itr; -} - -void NVFlinger::Compose() { - for (auto& display : displays) { - // Trigger vsync for this display at the end of drawing - SCOPE_EXIT({ display.vsync_event->Signal(); }); - - // Don't do anything for displays without layers. - if (display.layers.empty()) - continue; - - // TODO(Subv): Support more than 1 layer. - ASSERT_MSG(display.layers.size() == 1, "Max 1 layer per display is supported"); - - Layer& layer = display.layers[0]; - auto& buffer_queue = layer.buffer_queue; - - // Search for a queued buffer and acquire it - auto buffer = buffer_queue->AcquireBuffer(); - - if (buffer == boost::none) { - // There was no queued buffer to draw, render previous frame - VideoCore::g_renderer->SwapBuffers({}); - continue; - } - - auto& igbp_buffer = buffer->igbp_buffer; - - // Now send the buffer to the GPU for drawing. - auto nvdrv = Nvidia::nvdrv.lock(); - ASSERT(nvdrv); - - // TODO(Subv): Support more than just disp0. The display device selection is probably based - // on which display we're drawing (Default, Internal, External, etc) - auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0"); - ASSERT(nvdisp); - - nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format, - igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride); - - buffer_queue->ReleaseBuffer(buffer->slot); - } -} - -BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) { - native_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "BufferQueue NativeHandle"); -} - -void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) { - Buffer buffer{}; - buffer.slot = slot; - buffer.igbp_buffer = igbp_buffer; - buffer.status = Buffer::Status::Free; - - LOG_WARNING(Service, "Adding graphics buffer %u", slot); - - queue.emplace_back(buffer); -} - -u32 BufferQueue::DequeueBuffer(u32 pixel_format, u32 width, u32 height) { - auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { - // Only consider free buffers. Buffers become free once again after they've been Acquired - // and Released by the compositor, see the NVFlinger::Compose method. - if (buffer.status != Buffer::Status::Free) - return false; - - // Make sure that the parameters match. - auto& igbp_buffer = buffer.igbp_buffer; - return igbp_buffer.format == pixel_format && igbp_buffer.width == width && - igbp_buffer.height == height; - }); - ASSERT(itr != queue.end()); - - itr->status = Buffer::Status::Dequeued; - return itr->slot; -} - -const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const { - auto itr = std::find_if(queue.begin(), queue.end(), - [&](const Buffer& buffer) { return buffer.slot == slot; }); - ASSERT(itr != queue.end()); - ASSERT(itr->status == Buffer::Status::Dequeued); - return itr->igbp_buffer; -} - -void BufferQueue::QueueBuffer(u32 slot) { - auto itr = std::find_if(queue.begin(), queue.end(), - [&](const Buffer& buffer) { return buffer.slot == slot; }); - ASSERT(itr != queue.end()); - ASSERT(itr->status == Buffer::Status::Dequeued); - itr->status = Buffer::Status::Queued; -} - -boost::optional<const BufferQueue::Buffer&> BufferQueue::AcquireBuffer() { - auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) { - return buffer.status == Buffer::Status::Queued; - }); - if (itr == queue.end()) - return boost::none; - itr->status = Buffer::Status::Acquired; - return *itr; -} - -void BufferQueue::ReleaseBuffer(u32 slot) { - auto itr = std::find_if(queue.begin(), queue.end(), - [&](const Buffer& buffer) { return buffer.slot == slot; }); - ASSERT(itr != queue.end()); - ASSERT(itr->status == Buffer::Status::Acquired); - itr->status = Buffer::Status::Free; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger); } -Layer::Layer(u64 id, std::shared_ptr<BufferQueue> queue) : id(id), buffer_queue(std::move(queue)) {} - -Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) { - vsync_event = Kernel::Event::Create(Kernel::ResetType::Pulse, "Display VSync Event"); +void InstallInterfaces(SM::ServiceManager& service_manager, + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) { + auto module = std::make_shared<Module>(); + std::make_shared<VI_M>(module, nv_flinger)->InstallAsService(service_manager); + std::make_shared<VI_S>(module, nv_flinger)->InstallAsService(service_manager); + std::make_shared<VI_U>(module, nv_flinger)->InstallAsService(service_manager); } } // namespace VI diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h index 81d4f3daa..985c9d27c 100644 --- a/src/core/hle/service/vi/vi.h +++ b/src/core/hle/service/vi/vi.h @@ -4,9 +4,7 @@ #pragma once -#include <memory> -#include <boost/optional.hpp> -#include "core/hle/kernel/event.h" +#include "core/hle/service/nvflinger/nvflinger.h" #include "core/hle/service/service.h" namespace CoreTiming { @@ -16,147 +14,24 @@ struct EventType; namespace Service { namespace VI { -struct IGBPBuffer { - u32_le magic; - u32_le width; - u32_le height; - u32_le stride; - u32_le format; - u32_le usage; - INSERT_PADDING_WORDS(1); - u32_le index; - INSERT_PADDING_WORDS(3); - u32_le gpu_buffer_id; - INSERT_PADDING_WORDS(17); - u32_le nvmap_handle; - u32_le offset; - INSERT_PADDING_WORDS(60); -}; - -static_assert(sizeof(IGBPBuffer) == 0x16C, "IGBPBuffer has wrong size"); - -class BufferQueue { +class Module final { public: - BufferQueue(u32 id, u64 layer_id); - ~BufferQueue() = default; + class Interface : public ServiceFramework<Interface> { + public: + Interface(std::shared_ptr<Module> module, const char* name, + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); - struct Buffer { - enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 }; + void GetDisplayService(Kernel::HLERequestContext& ctx); - u32 slot; - Status status = Status::Free; - IGBPBuffer igbp_buffer; + protected: + std::shared_ptr<Module> module; + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; }; - - void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer); - u32 DequeueBuffer(u32 pixel_format, u32 width, u32 height); - const IGBPBuffer& RequestBuffer(u32 slot) const; - void QueueBuffer(u32 slot); - boost::optional<const Buffer&> AcquireBuffer(); - void ReleaseBuffer(u32 slot); - - u32 GetId() const { - return id; - } - - Kernel::SharedPtr<Kernel::Event> GetNativeHandle() const { - return native_handle; - } - -private: - u32 id; - u64 layer_id; - - std::vector<Buffer> queue; - Kernel::SharedPtr<Kernel::Event> native_handle; -}; - -struct Layer { - Layer(u64 id, std::shared_ptr<BufferQueue> queue); - ~Layer() = default; - - u64 id; - std::shared_ptr<BufferQueue> buffer_queue; -}; - -struct Display { - Display(u64 id, std::string name); - ~Display() = default; - - u64 id; - std::string name; - - std::vector<Layer> layers; - Kernel::SharedPtr<Kernel::Event> vsync_event; -}; - -class NVFlinger { -public: - NVFlinger(); - ~NVFlinger(); - - /// Opens the specified display and returns the id. - u64 OpenDisplay(const std::string& name); - - /// Creates a layer on the specified display and returns the layer id. - u64 CreateLayer(u64 display_id); - - /// Gets the buffer queue id of the specified layer in the specified display. - u32 GetBufferQueueId(u64 display_id, u64 layer_id); - - /// Gets the vsync event for the specified display. - Kernel::SharedPtr<Kernel::Event> GetVsyncEvent(u64 display_id); - - /// Obtains a buffer queue identified by the id. - std::shared_ptr<BufferQueue> GetBufferQueue(u32 id) const; - - /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when - /// finished. - void Compose(); - -private: - /// Returns the display identified by the specified id. - Display& GetDisplay(u64 display_id); - - /// Returns the layer identified by the specified id in the desired display. - Layer& GetLayer(u64 display_id, u64 layer_id); - - std::vector<Display> displays; - std::vector<std::shared_ptr<BufferQueue>> buffer_queues; - - /// Id to use for the next layer that is created, this counter is shared among all displays. - u64 next_layer_id = 1; - /// Id to use for the next buffer queue that is created, this counter is shared among all - /// layers. - u32 next_buffer_queue_id = 1; - - /// CoreTiming event that handles screen composition. - CoreTiming::EventType* composition_event; -}; - -class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { -public: - IApplicationDisplayService(std::shared_ptr<NVFlinger> nv_flinger); - ~IApplicationDisplayService() = default; - -private: - void GetRelayService(Kernel::HLERequestContext& ctx); - void GetSystemDisplayService(Kernel::HLERequestContext& ctx); - void GetManagerDisplayService(Kernel::HLERequestContext& ctx); - void GetIndirectDisplayTransactionService(Kernel::HLERequestContext& ctx); - void OpenDisplay(Kernel::HLERequestContext& ctx); - void CloseDisplay(Kernel::HLERequestContext& ctx); - void SetLayerScalingMode(Kernel::HLERequestContext& ctx); - void OpenLayer(Kernel::HLERequestContext& ctx); - void CreateStrayLayer(Kernel::HLERequestContext& ctx); - void DestroyStrayLayer(Kernel::HLERequestContext& ctx); - void GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx); - - std::shared_ptr<NVFlinger> nv_flinger; }; /// Registers all VI services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); } // namespace VI } // namespace Service diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp index 1a5a28b0d..5781fa9ec 100644 --- a/src/core/hle/service/vi/vi_m.cpp +++ b/src/core/hle/service/vi/vi_m.cpp @@ -2,28 +2,18 @@ // 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/service/vi/vi.h" #include "core/hle/service/vi/vi_m.h" namespace Service { namespace VI { -void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; - rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger); -} - -VI_M::VI_M() : ServiceFramework("vi:m") { +VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) + : Module::Interface(std::move(module), "vi:m", std::move(nv_flinger)) { static const FunctionInfo functions[] = { {2, &VI_M::GetDisplayService, "GetDisplayService"}, {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, }; RegisterHandlers(functions); - nv_flinger = std::make_shared<NVFlinger>(); } } // namespace VI diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h index 70ff7a2f3..0f7b799d6 100644 --- a/src/core/hle/service/vi/vi_m.h +++ b/src/core/hle/service/vi/vi_m.h @@ -4,21 +4,14 @@ #pragma once -#include <memory> -#include "core/hle/service/service.h" +#include "core/hle/service/vi/vi.h" namespace Service { namespace VI { -class VI_M final : public ServiceFramework<VI_M> { +class VI_M final : public Module::Interface { public: - VI_M(); - ~VI_M() = default; - -private: - void GetDisplayService(Kernel::HLERequestContext& ctx); - - std::shared_ptr<NVFlinger> nv_flinger; + explicit VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); }; } // namespace VI diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp new file mode 100644 index 000000000..1f937b2a8 --- /dev/null +++ b/src/core/hle/service/vi/vi_s.cpp @@ -0,0 +1,20 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/vi/vi_s.h" + +namespace Service { +namespace VI { + +VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) + : Module::Interface(std::move(module), "vi:s", std::move(nv_flinger)) { + static const FunctionInfo functions[] = { + {1, &VI_S::GetDisplayService, "GetDisplayService"}, + {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, + }; + RegisterHandlers(functions); +} + +} // namespace VI +} // namespace Service diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h new file mode 100644 index 000000000..7b32fdddc --- /dev/null +++ b/src/core/hle/service/vi/vi_s.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/vi/vi.h" + +namespace Service { +namespace VI { + +class VI_S final : public Module::Interface { +public: + explicit VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); +}; + +} // namespace VI +} // namespace Service diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp new file mode 100644 index 000000000..14e375b86 --- /dev/null +++ b/src/core/hle/service/vi/vi_u.cpp @@ -0,0 +1,20 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/vi/vi_u.h" + +namespace Service { +namespace VI { + +VI_U::VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) + : Module::Interface(std::move(module), "vi:u", std::move(nv_flinger)) { + static const FunctionInfo functions[] = { + {0, &VI_U::GetDisplayService, "GetDisplayService"}, + {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, + }; + RegisterHandlers(functions); +} + +} // namespace VI +} // namespace Service diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h new file mode 100644 index 000000000..c557a2235 --- /dev/null +++ b/src/core/hle/service/vi/vi_u.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/vi/vi.h" + +namespace Service { +namespace VI { + +class VI_U final : public Module::Interface { +public: + explicit VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); +}; + +} // namespace VI +} // namespace Service diff --git a/src/core/hle/shared_page.cpp b/src/core/hle/shared_page.cpp index 9ce8af961..bba4a0715 100644 --- a/src/core/hle/shared_page.cpp +++ b/src/core/hle/shared_page.cpp @@ -82,4 +82,4 @@ void Init() { CoreTiming::ScheduleEvent(0, update_time_event); } -} // namespace +} // namespace SharedPage diff --git a/src/core/hle/shared_page.h b/src/core/hle/shared_page.h index 864695ae1..a58259888 100644 --- a/src/core/hle/shared_page.h +++ b/src/core/hle/shared_page.h @@ -66,4 +66,4 @@ extern SharedPageDef shared_page; void Init(); -} // namespace +} // namespace SharedPage |
